From ab6216922a11cc5db6ae0b4bf50a814bf58c0fd6 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Mon, 26 Oct 2026 11:59:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=AF=94=E5=AF=B9=E5=8A=A0?= =?UTF-8?q?=E4=B8=8A=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitor/TabOrderMonitorController.java | 2 +- .../price/domain/vo/TabOrderCompareTask.java | 19 +- .../mapper/OracleUpstreamOrderMapper.java | 2 +- .../bms/price/mapper/PrcOrderBillMapper.java | 5 + .../price/oracle/TabOrderMonitorService.java | 40 +++- .../impl/TmsPriceQuoteServiceImpl.java | 17 +- .../task/TabOrderCompareAsyncRunner.java | 178 ++++++++++++++---- .../price/OracleUpstreamOrderMapper.xml | 10 +- .../mapper/price/PrcOrderBillMapper.xml | 17 ++ .../components/OrderSourceCompareDrawer.vue | 44 ++++- .../src/views/monitor/orderSource/index.vue | 2 +- bms-front/src/views/price/orderBill/index.vue | 62 ++++-- 12 files changed, 337 insertions(+), 61 deletions(-) diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/TabOrderMonitorController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/TabOrderMonitorController.java index 35a8c980..ca55c27a 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/TabOrderMonitorController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/TabOrderMonitorController.java @@ -157,7 +157,7 @@ public class TabOrderMonitorController extends BaseController customerKeywordResolver.resolve(query); String asyncTaskId = UUID.randomUUID().toString().replace("-", ""); tabOrderCompareAsyncRunner.put(asyncTaskId, TabOrderCompareTask.STATUS_PENDING, 0, "queued", - 0, 0, 0, null, null); + 0, 0, 0, 0, null, null, null); tabOrderCompareAsyncRunner.run(asyncTaskId, query); Map data = new HashMap(); data.put("asyncTaskId", asyncTaskId); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TabOrderCompareTask.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TabOrderCompareTask.java index 20c6f4ee..8e0cb419 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TabOrderCompareTask.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TabOrderCompareTask.java @@ -3,10 +3,12 @@ package com.bms.price.domain.vo; import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * 订单比对异步任务进度(Redis)。 - * ok=本地已有;fail/缺失=上游有而本地无(仅转单号)。 + * missing = 上游有转单号、本地无; + * statusMismatch = 本地有转单号但 order_status 与上游不一致。 */ public class TabOrderCompareTask implements Serializable { @@ -27,9 +29,16 @@ public class TabOrderCompareTask implements Serializable private int matched; /** 本地缺失 */ private int missing; + /** 本地有但订单状态不一致 */ + private int statusMismatch; private String error; /** 缺失转单号样本(有上限) */ private List missingTransferNos = new ArrayList(); + /** + * 状态不一致样本(有上限)。 + * 每项:transferNo / upstreamStatus / localStatus + */ + private List> statusMismatchSamples = new ArrayList>(); public String getTaskId() { return taskId; } public void setTaskId(String taskId) { this.taskId = taskId; } @@ -45,6 +54,8 @@ public class TabOrderCompareTask implements Serializable public void setMatched(int matched) { this.matched = matched; } public int getMissing() { return missing; } public void setMissing(int missing) { this.missing = missing; } + public int getStatusMismatch() { return statusMismatch; } + public void setStatusMismatch(int statusMismatch) { this.statusMismatch = statusMismatch; } public String getError() { return error; } public void setError(String error) { this.error = error; } public List getMissingTransferNos() { return missingTransferNos; } @@ -53,4 +64,10 @@ public class TabOrderCompareTask implements Serializable this.missingTransferNos = missingTransferNos == null ? new ArrayList() : missingTransferNos; } + public List> getStatusMismatchSamples() { return statusMismatchSamples; } + public void setStatusMismatchSamples(List> statusMismatchSamples) + { + this.statusMismatchSamples = statusMismatchSamples == null + ? new ArrayList>() : statusMismatchSamples; + } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/OracleUpstreamOrderMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/OracleUpstreamOrderMapper.java index a44aef51..fc4ea222 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/OracleUpstreamOrderMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/OracleUpstreamOrderMapper.java @@ -30,7 +30,7 @@ public interface OracleUpstreamOrderMapper @Param("endRow") long endRow); /** - * 订单比对专用(异步分页):仅返回 TRANSFER_ORDER_ID。 + * 订单比对专用(异步分页):返回 TRANSFER_ORDER_ID + ORDER_STATUS。 */ List> selectTabOrderCompareKeyPage( @Param("orderTable") String orderTable, diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java index d7ddc7b7..a2dc5094 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java @@ -43,6 +43,11 @@ public interface PrcOrderBillMapper List selectExistingTransferNos(@Param("transferNos") List transferNos, @Param("customerCodes") List customerCodes); + /** 订单比对:按转单号批量查本地 transfer_no + order_status。 */ + List> selectTransferOrderStatuses( + @Param("transferNos") List transferNos, + @Param("customerCodes") List customerCodes); + /** 订单号下拉搜索(带数据权限) */ public List selectOrderNoOptions(@Param("query") PrcOrderBill query, @Param("limit") int limit); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java b/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java index 4b501eb4..e3febb59 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/oracle/TabOrderMonitorService.java @@ -295,10 +295,38 @@ public class TabOrderMonitorService } /** - * 订单比对异步分页:专用 SQL 仅返回当前页转单号。 + * 订单比对异步分页:返回当前页转单号(兼容旧调用)。 */ @DataSource(DataSourceType.SLAVE) public List selectCompareTransferPage(TabOrderMonitorQuery query, int pageNum, int pageSize) + { + List> rows = selectCompareKeyPage(query, pageNum, pageSize); + if (rows == null || rows.isEmpty()) + { + return Collections.emptyList(); + } + List out = new ArrayList(rows.size()); + for (Map row : rows) + { + Object v = row == null ? null : row.get("transferNo"); + if (v == null) + { + continue; + } + String tn = String.valueOf(v).trim(); + if (!tn.isEmpty()) + { + out.add(tn); + } + } + return out; + } + + /** + * 订单比对异步分页:返回 transferNo + orderStatus(上游 ORDER_STATUS)。 + */ + @DataSource(DataSourceType.SLAVE) + public List> selectCompareKeyPage(TabOrderMonitorQuery query, int pageNum, int pageSize) { // 比对与回填列表完全隔离:先清单号再 validate,仅走客户+提货时间 if (query != null) @@ -334,7 +362,7 @@ public class TabOrderMonitorService { return Collections.emptyList(); } - List out = new ArrayList(rows.size()); + List> out = new ArrayList>(rows.size()); for (Map row : rows) { Object v = value(row, "TRANSFER_ORDER_ID"); @@ -343,10 +371,14 @@ public class TabOrderMonitorService continue; } String tn = String.valueOf(v).trim(); - if (!tn.isEmpty()) + if (tn.isEmpty()) { - out.add(tn); + continue; } + Map one = new LinkedHashMap(); + one.put("transferNo", tn); + one.put("orderStatus", integer(row, "ORDER_STATUS")); + out.add(one); } return out; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java index 39e2f3ce..b5c6d9fe 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java @@ -497,6 +497,13 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService productCode, productName, cc, cn, zone.zoneCode, weight, bizDate); if (hit != null) { + // Billing requires product catalog price (产品价); package hit alone is not enough. + if (request instanceof TmsPriceBillRequest && !hasProductCatalogPrice(catalog)) + { + missCode = Math.max(missCode, + diagnoseCatalogMiss(productCode, cc, cn, zone.zoneCode, bizDate)); + continue; + } stampRequestMeta(request, hit); return hit; } @@ -504,7 +511,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService } else if (TYPE_DISCOUNT.equals(setType)) { - if (catalog == null) + if (!hasProductCatalogPrice(catalog)) { missCode = Math.max(missCode, diagnoseCatalogMiss(productCode, cc, cn, zone.zoneCode, bizDate)); continue; @@ -747,7 +754,13 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService return out; } - /** Catalog match for product_amount / DISCOUNT base; null is allowed for FIXED sales. */ + /** True when product catalog row has a usable freight price (产品价). */ + private static boolean hasProductCatalogPrice(BmsProductCountry catalog) + { + return catalog != null && catalog.getFreightPrice() != null; + } + + /** Catalog match for product_amount / DISCOUNT base; quote FIXED may still hit without catalog. */ private BmsProductCountry softMatchCatalog(String productCode, String countryCode, String countryName, String zoneCode, BigDecimal weight, Date bizDate, PrcZone resolvedZone) { diff --git a/bms-back/bms-system/src/main/java/com/bms/price/task/TabOrderCompareAsyncRunner.java b/bms-back/bms-system/src/main/java/com/bms/price/task/TabOrderCompareAsyncRunner.java index da55bf78..3f28e259 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/task/TabOrderCompareAsyncRunner.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/task/TabOrderCompareAsyncRunner.java @@ -1,12 +1,11 @@ package com.bms.price.task; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +22,7 @@ import com.bms.price.search.IBillSearchStore; import com.bms.price.search.TabOrderSyncEsLog; /** - * 订单比对异步任务:分页拉上游转单号,与本地 transfer_no 比对差异。 + * 订单比对异步任务:分页拉上游转单号+状态,与本地 transfer_no / order_status 比对。 */ @Component public class TabOrderCompareAsyncRunner @@ -37,6 +36,8 @@ public class TabOrderCompareAsyncRunner public static final int SCAN_HARD_LIMIT = 50000; /** 进度/抽屉中保留的缺失转单号上限 */ public static final int MISSING_SAMPLE_LIMIT = 5000; + /** 状态不一致样本上限 */ + public static final int STATUS_MISMATCH_SAMPLE_LIMIT = 5000; @Autowired private TabOrderMonitorService tabOrderMonitorService; @@ -62,11 +63,14 @@ public class TabOrderCompareAsyncRunner query.setOrgOrderId(null); query.setTransferOrderId(null); } - put(asyncTaskId, TabOrderCompareTask.STATUS_RUNNING, 0, "starting", 0, 0, 0, null, null); + put(asyncTaskId, TabOrderCompareTask.STATUS_RUNNING, 0, "starting", + 0, 0, 0, 0, null, null, null); int scanned = 0; int matched = 0; int missing = 0; + int statusMismatch = 0; List missingSample = new ArrayList(); + List> statusMismatchSample = new ArrayList>(); int pageNum = 1; while (true) { @@ -77,43 +81,72 @@ public class TabOrderCompareAsyncRunner } int remain = SCAN_HARD_LIMIT - scanned; int pageSize = Math.min(PAGE_SIZE, remain); - List transferNos = tabOrderMonitorService.selectCompareTransferPage(query, pageNum, pageSize); - if (transferNos == null || transferNos.isEmpty()) + List> pageRows = + tabOrderMonitorService.selectCompareKeyPage(query, pageNum, pageSize); + if (pageRows == null || pageRows.isEmpty()) { break; } - Set existing = findExisting(transferNos, query == null ? null : query.getCustomerCodes()); - for (String tn : transferNos) + List transferNos = new ArrayList(pageRows.size()); + for (Map row : pageRows) { - if (StringUtils.isEmpty(tn)) + Object tn = row == null ? null : row.get("transferNo"); + if (tn != null && StringUtils.isNotEmpty(String.valueOf(tn).trim())) + { + transferNos.add(String.valueOf(tn).trim()); + } + } + Map localStatusByTn = + findLocalStatuses(transferNos, query == null ? null : query.getCustomerCodes()); + for (Map row : pageRows) + { + if (row == null) { continue; } - scanned++; - String key = tn.trim().toUpperCase(Locale.ROOT); - if (existing.contains(key)) + Object tnObj = row.get("transferNo"); + if (tnObj == null || StringUtils.isEmpty(String.valueOf(tnObj).trim())) { - matched++; + continue; } - else + String tn = String.valueOf(tnObj).trim(); + scanned++; + String key = tn.toUpperCase(Locale.ROOT); + Integer upstreamStatus = toInteger(row.get("orderStatus")); + if (!localStatusByTn.containsKey(key)) { missing++; if (missingSample.size() < MISSING_SAMPLE_LIMIT) { - missingSample.add(tn.trim()); + missingSample.add(tn); + } + continue; + } + matched++; + Integer localStatus = localStatusByTn.get(key); + if (!statusEquals(upstreamStatus, localStatus)) + { + statusMismatch++; + if (statusMismatchSample.size() < STATUS_MISMATCH_SAMPLE_LIMIT) + { + Map one = new LinkedHashMap(); + one.put("transferNo", tn); + one.put("upstreamStatus", upstreamStatus); + one.put("localStatus", localStatus); + statusMismatchSample.add(one); } } } int percent = Math.min(99, (int) ((scanned * 100L) / Math.max(scanned + pageSize, 1))); - // 最后一页不满页时近乎完成;否则按已扫描量给一个缓慢增长的百分比 - if (transferNos.size() < pageSize) + if (pageRows.size() < pageSize) { percent = 99; } put(asyncTaskId, TabOrderCompareTask.STATUS_RUNNING, percent, - "scanning page=" + pageNum + " scanned=" + scanned + " missing=" + missing, - scanned, matched, missing, null, missingSample); - if (transferNos.size() < pageSize) + "scanning page=" + pageNum + " scanned=" + scanned + + " missing=" + missing + " statusMismatch=" + statusMismatch, + scanned, matched, missing, statusMismatch, null, missingSample, statusMismatchSample); + if (pageRows.size() < pageSize) { break; } @@ -124,14 +157,19 @@ public class TabOrderCompareAsyncRunner resp.put("scanned", Integer.valueOf(scanned)); resp.put("matched", Integer.valueOf(matched)); resp.put("missing", Integer.valueOf(missing)); + resp.put("statusMismatch", Integer.valueOf(statusMismatch)); resp.put("missingTransferNos", missingSample); + resp.put("statusMismatchSamples", statusMismatchSample); resp.put("missingTruncated", Boolean.valueOf(missing > missingSample.size())); + resp.put("statusMismatchTruncated", + Boolean.valueOf(statusMismatch > statusMismatchSample.size())); put(asyncTaskId, TabOrderCompareTask.STATUS_SUCCESS, 100, - "done scanned=" + scanned + " missing=" + missing, - scanned, matched, missing, null, missingSample); + "done scanned=" + scanned + " missing=" + missing + " statusMismatch=" + statusMismatch, + scanned, matched, missing, statusMismatch, null, missingSample, statusMismatchSample); TabOrderSyncEsLog.save(billSearchStore, asyncTaskId, "COMPARE_DONE", query, resp, "SUCCESS", null); - log.info("orderSource compare done asyncTaskId={} scanned={} matched={} missing={}", - asyncTaskId, Integer.valueOf(scanned), Integer.valueOf(matched), Integer.valueOf(missing)); + log.info("orderSource compare done asyncTaskId={} scanned={} matched={} missing={} statusMismatch={}", + asyncTaskId, Integer.valueOf(scanned), Integer.valueOf(matched), + Integer.valueOf(missing), Integer.valueOf(statusMismatch)); } catch (Exception e) { @@ -141,33 +179,101 @@ public class TabOrderCompareAsyncRunner { msg = e.getClass().getSimpleName(); } - put(asyncTaskId, TabOrderCompareTask.STATUS_FAIL, 100, "failed", 0, 0, 0, msg, null); + put(asyncTaskId, TabOrderCompareTask.STATUS_FAIL, 100, "failed", + 0, 0, 0, 0, msg, null, null); TabOrderSyncEsLog.save(billSearchStore, asyncTaskId, "COMPARE_DONE", query, null, "FAIL", msg); } } - private Set findExisting(List transferNos, List customerCodes) + /** transferNo(upper) -> local order_status (null value means local row exists with null status). */ + private Map findLocalStatuses(List transferNos, List customerCodes) { - Set found = new HashSet(); + Map found = new HashMap(); if (transferNos == null || transferNos.isEmpty()) { return found; } - List hit = prcOrderBillMapper.selectExistingTransferNos(transferNos, cleanCodes(customerCodes)); - if (hit == null) + List> rows = + prcOrderBillMapper.selectTransferOrderStatuses(transferNos, cleanCodes(customerCodes)); + if (rows == null) { return found; } - for (String s : hit) + for (Map row : rows) { - if (StringUtils.isNotEmpty(s)) + if (row == null) { - found.add(s.trim().toUpperCase(Locale.ROOT)); + continue; + } + Object tn = first(row, "transferNo", "TRANSFERNO", "transfer_no"); + if (tn == null || StringUtils.isEmpty(String.valueOf(tn).trim())) + { + continue; + } + String key = String.valueOf(tn).trim().toUpperCase(Locale.ROOT); + Integer st = toInteger(first(row, "orderStatus", "ORDERSTATUS", "order_status")); + // Keep first hit; duplicate transfer_no rare. + if (!found.containsKey(key)) + { + found.put(key, st); } } return found; } + private static Object first(Map row, String... keys) + { + if (row == null || keys == null) + { + return null; + } + for (String k : keys) + { + if (row.containsKey(k) && row.get(k) != null) + { + return row.get(k); + } + } + return null; + } + + private static Integer toInteger(Object v) + { + if (v == null || "".equals(String.valueOf(v).trim())) + { + return null; + } + if (v instanceof Integer) + { + return (Integer) v; + } + if (v instanceof Number) + { + return Integer.valueOf(((Number) v).intValue()); + } + try + { + return Integer.valueOf(String.valueOf(v).trim()); + } + catch (Exception e) + { + return null; + } + } + + private static boolean statusEquals(Integer a, Integer b) + { + if (a == null && b == null) + { + return true; + } + if (a == null || b == null) + { + return false; + } + return a.intValue() == b.intValue(); + } + private static List cleanCodes(List codes) { if (codes == null || codes.isEmpty()) @@ -186,7 +292,8 @@ public class TabOrderCompareAsyncRunner } public void put(String taskId, String status, int percent, String message, - int scanned, int matched, int missing, String error, List missingSample) + int scanned, int matched, int missing, int statusMismatch, String error, + List missingSample, List> statusMismatchSample) { TabOrderCompareTask task = new TabOrderCompareTask(); task.setTaskId(taskId); @@ -196,11 +303,16 @@ public class TabOrderCompareAsyncRunner task.setScanned(scanned); task.setMatched(matched); task.setMissing(missing); + task.setStatusMismatch(statusMismatch); task.setError(error); if (missingSample != null) { task.setMissingTransferNos(new ArrayList(missingSample)); } + if (statusMismatchSample != null) + { + task.setStatusMismatchSamples(new ArrayList>(statusMismatchSample)); + } redisCache.setCacheObject(CACHE_KEY_PREFIX + taskId, task, TTL_MINUTES, TimeUnit.MINUTES); } diff --git a/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml index a3361914..d475a529 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/OracleUpstreamOrderMapper.xml @@ -163,14 +163,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + +