#
cl
18 小时以前 c6938bcb89091596edab2740f7bf0b218956b4b0
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CusBarcodeSyncMatnrService.java
@@ -12,6 +12,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -118,37 +119,48 @@
            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));
        List<Map<String, Object>> viewItems = new ArrayList<>(matnrCodes.size());
        for (String code : matnrCodes) {
            boolean hit = CusBarcodeSyncViewQueryService.orderMatnrHitsBarcodeView(code, viewItems);
            List<Map<String, Object>> one = cusBarcodeSyncViewQueryService.listMapsForBarcode(code);
            boolean hit = CusBarcodeSyncViewQueryService.orderMatnrHitsBarcodeView(code, one);
            log.info("[cus_barcode_sync] 强制校验 code={} viewHit={}", code, hit);
            if (!hit) {
                throw new CoolException("物料未在视图 cus_barcode_sync_view 中:" + code);
            }
            viewItems.addAll(one);
        }
        log.info("[cus_barcode_sync] FORCE_VIEW 分支 CUS_ITEM_SYNC_MODE.val={} viewRows={} viewBarcodes=[{}]",
                formatCfgVal(cfg.rawVal),
                viewItems.size(),
                summarizeViewBarcodes(viewItems));
        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;
        Map<String, List<Map<String, Object>>> viewByCode = null;
        try {
            viewItems = cusBarcodeSyncViewQueryService.listByItemNos(matnrCodes);
            viewByCode = new LinkedHashMap<>();
            for (String code : matnrCodes) {
                viewByCode.put(code, cusBarcodeSyncViewQueryService.listMapsForBarcode(code));
            }
        } catch (Exception ex) {
            log.warn("[cus_barcode_sync] 查询 cus_barcode_sync_view 异常,将仅按物料表校验", ex);
        }
        int viewRowTotal = viewByCode == null ? 0 : viewByCode.values().stream().mapToInt(l -> l == null ? 0 : l.size()).sum();
        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()) {
                viewRowTotal,
                summarizeBarcodesWithHits(viewByCode));
        if (viewByCode != null) {
            try {
                cusBarcodeSyncMatnrApplyService.applyFromViewRows(viewItems, orderItemByCode, loginUserId);
                for (String code : matnrCodes) {
                    List<Map<String, Object>> one = viewByCode.get(code);
                    if (one != null && !one.isEmpty()) {
                        cusBarcodeSyncMatnrApplyService.applyFromViewRows(one, orderItemByCode, loginUserId);
                    }
                }
            } catch (Exception ex) {
                log.warn("[cus_barcode_sync] 批量 applyFromViewRows 失败", ex);
                log.warn("[cus_barcode_sync] 按条码 applyFromViewRows 失败", ex);
            }
        }
        // 视图有条码但本地仍无:按行补建档
@@ -158,53 +170,49 @@
                log.info("[cus_barcode_sync] 校验通过 code={} localMatnrId={}", code, m.getId());
                continue;
            }
            boolean viewHit = viewItems != null && CusBarcodeSyncViewQueryService.orderMatnrHitsBarcodeView(code, viewItems);
            int viewRowCount = viewItems == null ? 0 : viewItems.size();
            List<Map<String, Object>> one = viewByCode == null
                    ? Collections.emptyList()
                    : viewByCode.getOrDefault(code, Collections.emptyList());
            boolean viewHit = CusBarcodeSyncViewQueryService.orderMatnrHitsBarcodeView(code, one);
            int viewRowCount = one.size();
            log.info("[cus_barcode_sync] 本地无记录 code={} viewHit={} viewRowCount={}", code, viewHit, viewRowCount);
            Exception applyEx = null;
            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) {
                        applyEx = ex;
                        log.warn("[cus_barcode_sync] 按视图补全物料失败 code={}", code, ex);
                    }
                    m = findLocalMatnrForOrderCode(code);
            if (viewHit && !one.isEmpty()) {
                try {
                    log.info("[cus_barcode_sync] 按条码补档 apply rows={} code={}", one.size(), code);
                    cusBarcodeSyncMatnrApplyService.applyFromViewRows(one, orderItemByCode, loginUserId);
                } catch (Exception ex) {
                    applyEx = ex;
                    log.warn("[cus_barcode_sync] 按视图补全物料失败 code={}", code, ex);
                }
                m = findLocalMatnrForOrderCode(code);
            }
            if (m == null) {
                ManMatnrFailReason fr = resolveManMatnrFailReason(viewItems, viewHit, applyEx);
                ManMatnrFailReason fr = resolveManMatnrFailReason(viewByCode, viewHit, applyEx);
                log.warn("[cus_barcode_sync] 无法落地 man_matnr code={} reason={} viewRowCount={} viewBarcodes=[{}]",
                        code, fr.name(), viewRowCount, summarizeViewBarcodes(viewItems));
                throw new CoolException("物料不存在:" + code + "," + fr.userMessage);
                        code, fr.name(), viewRowCount, summarizeViewBarcodes(one));
                throw new CoolException("物料不存在:" + code);
            }
        }
    }
    private enum ManMatnrFailReason {
        VIEW_QUERY_ERROR("副库视图查询异常,未拿到结果集"),
        VIEW_ZERO_ROWS("副库视图本次查询 0 行,请核对 dj-cloud-wms 与视图数据"),
        VIEW_CODE_NOT_IN_RESULT("副库视图有返回行但无与本单 matnr 相等的 barcode"),
        APPLY_ERROR("视图已命中但写入 man_matnr 失败"),
        APPLY_STILL_EMPTY("视图已命中且已 apply 仍无主库物料记录");
        final String userMessage;
        ManMatnrFailReason(String userMessage) {
            this.userMessage = userMessage;
        }
        VIEW_QUERY_ERROR,
        VIEW_ZERO_ROWS,
        VIEW_CODE_NOT_IN_RESULT,
        APPLY_ERROR,
        APPLY_STILL_EMPTY
    }
    private static ManMatnrFailReason resolveManMatnrFailReason(List<Map<String, Object>> viewItems, boolean viewHit, Exception applyEx) {
        if (viewItems == null) {
    private static ManMatnrFailReason resolveManMatnrFailReason(
            Map<String, List<Map<String, Object>>> viewByCode,
            boolean viewHit,
            Exception applyEx) {
        if (viewByCode == null) {
            return ManMatnrFailReason.VIEW_QUERY_ERROR;
        }
        if (viewItems.isEmpty()) {
        boolean anyHit = viewByCode.values().stream().anyMatch(list -> list != null && !list.isEmpty());
        if (!anyHit) {
            return ManMatnrFailReason.VIEW_ZERO_ROWS;
        }
        if (!viewHit) {
@@ -236,6 +244,18 @@
        return s.length() > 1200 ? s.substring(0, 1200) + "..." : s;
    }
    /** 仅汇总本次查询有命中的单据物料号 */
    private static String summarizeBarcodesWithHits(Map<String, List<Map<String, Object>>> viewByCode) {
        if (viewByCode == null || viewByCode.isEmpty()) {
            return "";
        }
        String s = viewByCode.entrySet().stream()
                .filter(e -> e.getValue() != null && !e.getValue().isEmpty())
                .map(Map.Entry::getKey)
                .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) {