package com.vincent.rsf.server.manager.service;
|
|
import com.vincent.rsf.framework.exception.CoolException;
|
import com.vincent.rsf.server.api.controller.erp.params.SyncOrdersItem;
|
import com.vincent.rsf.server.manager.entity.Matnr;
|
import com.vincent.rsf.server.manager.enums.CusItemSyncMode;
|
import com.vincent.rsf.server.system.constant.GlobalConfigCode;
|
import com.vincent.rsf.server.system.entity.Config;
|
import com.vincent.rsf.server.system.service.ConfigService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.stream.Collectors;
|
|
/**
|
* cus_barcode_sync_view 与 man_matnr 同步(云仓通知单、无订单组托等共用)。
|
* 分支仅由 {@link GlobalConfigCode#CUS_ITEM_SYNC_MODE}(sys_config.val)解析为 {@link CusItemSyncMode} 决定。
|
* 副库读无事务({@link CusBarcodeSyncViewQueryService} {@code NOT_SUPPORTED}),主库写 {@link CusBarcodeSyncMatnrApplyService} {@code REQUIRES_NEW}。
|
*/
|
@Service
|
@Slf4j
|
public class CusBarcodeSyncMatnrService {
|
|
@Autowired
|
private CusBarcodeSyncViewQueryService cusBarcodeSyncViewQueryService;
|
@Autowired
|
private MatnrService matnrService;
|
@Autowired
|
private ConfigService configService;
|
@Autowired
|
private CusBarcodeSyncMatnrApplyService cusBarcodeSyncMatnrApplyService;
|
|
/** 与云仓通知单同步:按明细 matnr 查视图并写入/更新物料 */
|
public void syncFromOrderItems(List<SyncOrdersItem> orderItems, Long loginUserId) {
|
if (orderItems == null || orderItems.isEmpty()) {
|
return;
|
}
|
List<String> matnrCodes = orderItems.stream()
|
.map(SyncOrdersItem::getMatnr)
|
.filter(StringUtils::isNotBlank)
|
.map(String::trim)
|
.distinct()
|
.collect(Collectors.toList());
|
if (matnrCodes.isEmpty()) {
|
return;
|
}
|
syncAlignedWithBarcodeView(matnrCodes, buildOrderItemByMatnrCode(orderItems), loginUserId);
|
}
|
|
/** 无订单组托等:仅按物料号(与视图 barcode 一致)走同一套视图策略 */
|
public void syncFromMatnrCodes(Collection<String> matnrCodes, Long loginUserId) {
|
if (matnrCodes == null || matnrCodes.isEmpty()) {
|
return;
|
}
|
List<String> codes = matnrCodes.stream()
|
.map(StringUtils::trimToNull)
|
.filter(Objects::nonNull)
|
.distinct()
|
.collect(Collectors.toList());
|
if (codes.isEmpty()) {
|
return;
|
}
|
syncAlignedWithBarcodeView(codes, Collections.emptyMap(), loginUserId);
|
}
|
|
private static Map<String, SyncOrdersItem> buildOrderItemByMatnrCode(List<SyncOrdersItem> orderItems) {
|
Map<String, SyncOrdersItem> map = new LinkedHashMap<>();
|
if (orderItems == null) {
|
return map;
|
}
|
for (SyncOrdersItem item : orderItems) {
|
if (StringUtils.isBlank(item.getMatnr())) {
|
continue;
|
}
|
String key = StringUtils.trimToNull(item.getMatnr().trim());
|
if (key == null) {
|
continue;
|
}
|
map.putIfAbsent(key, item);
|
}
|
return map;
|
}
|
|
/** 与分支解析共用同一份 Config(来自 ConfigService 全局缓存,避免每次同步打库) */
|
private CusItemSyncConfigSnapshot resolveCusItemSyncConfig() {
|
Config c = configService.getCachedOrLoad(GlobalConfigCode.CUS_ITEM_SYNC_MODE);
|
if (c == null) {
|
return new CusItemSyncConfigSnapshot(CusItemSyncMode.NONE, null);
|
}
|
return new CusItemSyncConfigSnapshot(CusItemSyncMode.fromConfig(c.getVal()), c.getVal());
|
}
|
|
private static String formatCfgVal(String rawVal) {
|
if (rawVal == null) {
|
return "(无配置)";
|
}
|
String t = rawVal.trim();
|
return t.isEmpty() ? "(空)" : t;
|
}
|
|
private void syncAlignedWithBarcodeView(List<String> matnrCodes, Map<String, SyncOrdersItem> orderItemByCode, Long loginUserId) {
|
CusItemSyncConfigSnapshot cfg = resolveCusItemSyncConfig();
|
log.info("[cus_barcode_sync] 同步入口 CUS_ITEM_SYNC_MODE.val={} resolved={} ds={} matnrCount={} matnrs=[{}]",
|
formatCfgVal(cfg.rawVal),
|
cfg.mode,
|
cusBarcodeSyncViewQueryService.effectiveDataSourceLabel(),
|
matnrCodes.size(),
|
joinCodesForLog(matnrCodes));
|
if (cfg.mode == CusItemSyncMode.NONE) {
|
syncMatnrNonForceFromView(cfg, matnrCodes, orderItemByCode, loginUserId);
|
return;
|
}
|
List<Map<String, Object>> viewItems = cusBarcodeSyncViewQueryService.listByItemNos(matnrCodes);
|
log.info("[cus_barcode_sync] FORCE_VIEW 分支 CUS_ITEM_SYNC_MODE.val={} viewRows={} viewBarcodes=[{}]",
|
formatCfgVal(cfg.rawVal),
|
viewItems == null ? 0 : viewItems.size(),
|
summarizeViewBarcodes(viewItems));
|
for (String code : matnrCodes) {
|
boolean hit = CusBarcodeSyncViewQueryService.orderMatnrHitsBarcodeView(code, viewItems);
|
log.info("[cus_barcode_sync] 强制校验 code={} viewHit={}", code, hit);
|
if (!hit) {
|
throw new CoolException("物料未在视图 cus_barcode_sync_view 中:" + code);
|
}
|
}
|
cusBarcodeSyncMatnrApplyService.applyFromViewRows(viewItems, orderItemByCode, loginUserId);
|
}
|
|
private void syncMatnrNonForceFromView(CusItemSyncConfigSnapshot cfg, List<String> matnrCodes, Map<String, SyncOrdersItem> orderItemByCode, Long loginUserId) {
|
List<Map<String, Object>> viewItems = null;
|
try {
|
viewItems = cusBarcodeSyncViewQueryService.listByItemNos(matnrCodes);
|
} catch (Exception ex) {
|
log.warn("[cus_barcode_sync] 查询 cus_barcode_sync_view 异常,将仅按物料表校验", ex);
|
}
|
log.info("[cus_barcode_sync] NONE 分支 CUS_ITEM_SYNC_MODE.val={} 副库视图 rows={} barcodesInView=[{}]",
|
formatCfgVal(cfg.rawVal),
|
viewItems == null ? 0 : viewItems.size(),
|
summarizeViewBarcodes(viewItems));
|
if (viewItems != null && !viewItems.isEmpty()) {
|
try {
|
cusBarcodeSyncMatnrApplyService.applyFromViewRows(viewItems, orderItemByCode, loginUserId);
|
} catch (Exception ex) {
|
log.warn("[cus_barcode_sync] 批量 applyFromViewRows 失败", ex);
|
}
|
}
|
// 视图有条码但本地仍无:按行补建档
|
for (String code : matnrCodes) {
|
Matnr m = findLocalMatnrForOrderCode(code);
|
if (m != null) {
|
log.info("[cus_barcode_sync] 校验通过 code={} localMatnrId={}", code, m.getId());
|
continue;
|
}
|
boolean viewHit = viewItems != null && CusBarcodeSyncViewQueryService.orderMatnrHitsBarcodeView(code, viewItems);
|
log.info("[cus_barcode_sync] 本地无记录 code={} viewHit={} viewRowCount={}", code, viewHit,
|
viewItems == null ? 0 : viewItems.size());
|
if (viewHit && viewItems != null) {
|
List<Map<String, Object>> rowsForCode = viewItems.stream()
|
.filter(r -> CusBarcodeSyncViewQueryService.rowMatchesOrderMatnr(code, Objects.toString(r.get("barcode"), null)))
|
.collect(Collectors.toList());
|
if (!rowsForCode.isEmpty()) {
|
try {
|
log.info("[cus_barcode_sync] 按条码补档 apply rows={} code={}", rowsForCode.size(), code);
|
cusBarcodeSyncMatnrApplyService.applyFromViewRows(rowsForCode, orderItemByCode, loginUserId);
|
} catch (Exception ex) {
|
log.warn("[cus_barcode_sync] 按视图补全物料失败 code={}", code, ex);
|
}
|
m = findLocalMatnrForOrderCode(code);
|
}
|
}
|
if (m == null) {
|
log.warn("[cus_barcode_sync] 仍无法落地 man_matnr code={} viewHit={} viewSample=[{}]",
|
code, viewHit, summarizeViewBarcodes(viewItems));
|
throw new CoolException("物料不存在:" + code);
|
}
|
}
|
}
|
|
private static String joinCodesForLog(List<String> matnrCodes) {
|
if (matnrCodes == null || matnrCodes.isEmpty()) {
|
return "";
|
}
|
String s = String.join(",", matnrCodes);
|
return s.length() > 1200 ? s.substring(0, 1200) + "..." : s;
|
}
|
|
private static String summarizeViewBarcodes(List<Map<String, Object>> viewItems) {
|
if (viewItems == null || viewItems.isEmpty()) {
|
return "";
|
}
|
String s = viewItems.stream()
|
.map(r -> Objects.toString(r.get("barcode"), ""))
|
.filter(StringUtils::isNotBlank)
|
.distinct()
|
.collect(Collectors.joining(","));
|
return s.length() > 1200 ? s.substring(0, 1200) + "..." : s;
|
}
|
|
private Matnr findLocalMatnrForOrderCode(String orderMatnr) {
|
String t = StringUtils.trimToNull(orderMatnr);
|
if (t == null) {
|
return null;
|
}
|
return matnrService.getOneByCodeAndBatch(t, "");
|
}
|
|
private static final class CusItemSyncConfigSnapshot {
|
final CusItemSyncMode mode;
|
/** sys_config.CUS_ITEM_SYNC_MODE 的 val,无配置行为 null */
|
final String rawVal;
|
|
CusItemSyncConfigSnapshot(CusItemSyncMode mode, String rawVal) {
|
this.mode = mode;
|
this.rawVal = rawVal;
|
}
|
}
|
|
}
|