From 8f1cf5c7557f35fd4d8c5e6db4bd49efba83648e Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Wed, 21 Oct 2026 11:01:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=AF=94=E5=AF=B9=E8=A7=84?= =?UTF-8?q?=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/price/BillingController.java | 3 +- .../vo/OrderBillProductCheckResult.java | 230 +++++++++++++++++- .../service/OrderBillProductCheckService.java | 202 +++++++++------ .../impl/TmsPriceQuoteServiceImpl.java | 1 + .../OrderBillProductCheckDrawer.vue | 53 ++-- 5 files changed, 384 insertions(+), 105 deletions(-) diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java index db955640..2a345e71 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/BillingController.java @@ -180,7 +180,8 @@ public class BillingController extends BaseController } /** - * Detect package sales products (as of billing end time) missing from product catalog. + * Compare local order-bill products vs customer bound package SALES products + * (includes product_code_mapping). */ @PreAuthorize("@ss.hasPermi('price:orderBill:list')") @PostMapping("/orderBill/checkProducts") diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/OrderBillProductCheckResult.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/OrderBillProductCheckResult.java index c2c6f903..f6706237 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/OrderBillProductCheckResult.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/OrderBillProductCheckResult.java @@ -3,35 +3,243 @@ package com.bms.price.domain.vo; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import lombok.Data; /** - * Order-bill product check: customer package (sales) products vs product catalog. + * Local order-bill products vs customer bound package (SALES) comparison. + * Matching also accepts product_code_mapping (upstream -> BMS). */ -@Data public class OrderBillProductCheckResult implements Serializable { private static final long serialVersionUID = 1L; - /** Orders matched by search filters (customer + billing time). */ private long orderTotal; + private int distinctOrderProducts; private int packageProductCount; private int matchedCount; private int missingCount; private String asOfTime; - private List packageProducts = new ArrayList(); - private List missingProducts = new ArrayList(); + private List packageProducts = new ArrayList(); + private List orderProducts = new ArrayList(); + private List missingProducts = new ArrayList(); - @Data - public static class ProductRow implements Serializable + public long getOrderTotal() + { + return orderTotal; + } + + public void setOrderTotal(long orderTotal) + { + this.orderTotal = orderTotal; + } + + public int getDistinctOrderProducts() + { + return distinctOrderProducts; + } + + public void setDistinctOrderProducts(int distinctOrderProducts) + { + this.distinctOrderProducts = distinctOrderProducts; + } + + public int getPackageProductCount() + { + return packageProductCount; + } + + public void setPackageProductCount(int packageProductCount) + { + this.packageProductCount = packageProductCount; + } + + public int getMatchedCount() + { + return matchedCount; + } + + public void setMatchedCount(int matchedCount) + { + this.matchedCount = matchedCount; + } + + public int getMissingCount() + { + return missingCount; + } + + public void setMissingCount(int missingCount) + { + this.missingCount = missingCount; + } + + public String getAsOfTime() + { + return asOfTime; + } + + public void setAsOfTime(String asOfTime) + { + this.asOfTime = asOfTime; + } + + public List getPackageProducts() + { + return packageProducts; + } + + public void setPackageProducts(List packageProducts) + { + this.packageProducts = packageProducts; + } + + public List getOrderProducts() + { + return orderProducts; + } + + public void setOrderProducts(List orderProducts) + { + this.orderProducts = orderProducts; + } + + public List getMissingProducts() + { + return missingProducts; + } + + public void setMissingProducts(List missingProducts) + { + this.missingProducts = missingProducts; + } + + public static class PackageProductRow implements Serializable { private static final long serialVersionUID = 1L; private String customerCode; private String productCode; private String productName; - /** Whether product exists in catalog (effective at asOf). */ - private boolean inLibrary; - /** Order count in search billing window for this product. */ + + public String getCustomerCode() + { + return customerCode; + } + + public void setCustomerCode(String customerCode) + { + this.customerCode = customerCode; + } + + public String getProductCode() + { + return productCode; + } + + public void setProductCode(String productCode) + { + this.productCode = productCode; + } + + public String getProductName() + { + return productName; + } + + public void setProductName(String productName) + { + this.productName = productName; + } + } + + public static class OrderProductRow implements Serializable + { + private static final long serialVersionUID = 1L; + private String customerCode; + private String upstreamCode; + private String productName; + private String mappedCode; + private boolean inPackage; + private boolean mappedInPackage; + private boolean covered; private int orderCount; + + public String getCustomerCode() + { + return customerCode; + } + + public void setCustomerCode(String customerCode) + { + this.customerCode = customerCode; + } + + public String getUpstreamCode() + { + return upstreamCode; + } + + public void setUpstreamCode(String upstreamCode) + { + this.upstreamCode = upstreamCode; + } + + public String getProductName() + { + return productName; + } + + public void setProductName(String productName) + { + this.productName = productName; + } + + public String getMappedCode() + { + return mappedCode; + } + + public void setMappedCode(String mappedCode) + { + this.mappedCode = mappedCode; + } + + public boolean isInPackage() + { + return inPackage; + } + + public void setInPackage(boolean inPackage) + { + this.inPackage = inPackage; + } + + public boolean isMappedInPackage() + { + return mappedInPackage; + } + + public void setMappedInPackage(boolean mappedInPackage) + { + this.mappedInPackage = mappedInPackage; + } + + public boolean isCovered() + { + return covered; + } + + public void setCovered(boolean covered) + { + this.covered = covered; + } + + public int getOrderCount() + { + return orderCount; + } + + public void setOrderCount(int orderCount) + { + this.orderCount = orderCount; + } } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/OrderBillProductCheckService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/OrderBillProductCheckService.java index ae007701..fc6c6894 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/OrderBillProductCheckService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/OrderBillProductCheckService.java @@ -3,6 +3,7 @@ package com.bms.price.service; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; @@ -17,16 +18,17 @@ import org.springframework.stereotype.Service; import com.bms.common.annotation.DataScope; import com.bms.common.exception.ServiceException; import com.bms.common.utils.StringUtils; -import com.bms.price.domain.BmsProduct; import com.bms.price.domain.PrcOrderBill; import com.bms.price.domain.PrcPriceSetProduct; import com.bms.price.domain.vo.OrderBillProductCheckResult; -import com.bms.price.domain.vo.OrderBillProductCheckResult.ProductRow; -import com.bms.price.mapper.BmsProductMapper; +import com.bms.price.domain.vo.OrderBillProductCheckResult.OrderProductRow; +import com.bms.price.domain.vo.OrderBillProductCheckResult.PackageProductRow; import com.bms.price.mapper.PrcOrderBillMapper; +import com.bms.system.service.IBmsProductCodeMappingService; /** - * Detect package sales products (as of billing end time) that are missing from product catalog. + * Compare local order-bill product codes against customer bound package SALES products. + * Also accepts product_code_mapping (upstream -> BMS). */ @Service public class OrderBillProductCheckService @@ -37,7 +39,7 @@ public class OrderBillProductCheckService private ITmsPriceQuoteService tmsPriceQuoteService; @Autowired - private BmsProductMapper productMapper; + private IBmsProductCodeMappingService productCodeMappingService; @Autowired private PrcOrderBillMapper orderBillMapper; @@ -51,74 +53,125 @@ public class OrderBillProductCheckService OrderBillProductCheckResult result = new OrderBillProductCheckResult(); result.setAsOfTime(formatAsOf(asOf)); - List customerCodes = normalizeCodes(query.getCustomerCodes()); - Map orderCountByKey = loadOrderCounts(query); - long orderTotal = 0L; - for (Integer c : orderCountByKey.values()) + List> groups = orderBillMapper.selectDistinctProductGroups(query); + if (groups == null || groups.isEmpty()) { - if (c != null) - { - orderTotal += c.intValue(); - } + result.setOrderTotal(0L); + return result; } - result.setOrderTotal(orderTotal); - List packageRows = new ArrayList(); - List missingRows = new ArrayList(); - int matched = 0; - - for (String customerCode : customerCodes) + long orderTotal = 0L; + Set customerCodes = new HashSet(); + for (Map g : groups) { - List products = tmsPriceQuoteService.listAllBoundPackageProducts(customerCode, asOf); - Set seen = new HashSet(); - if (products == null) + String customerCode = trim(str(g, "customer_code")); + String productCode = trim(str(g, "product_code")); + if (StringUtils.isEmpty(customerCode) || StringUtils.isEmpty(productCode)) { continue; } - for (PrcPriceSetProduct p : products) + customerCodes.add(customerCode); + orderTotal += longValue(g, "order_count"); + } + + Map> packageCodeByCustomer = new HashMap>(); + List packageRows = new ArrayList(); + for (String customerCode : customerCodes) + { + List products = tmsPriceQuoteService.listAllBoundPackageProducts(customerCode, asOf); + Set codes = new HashSet(); + if (products != null) { - if (p == null || StringUtils.isEmpty(p.getProductCode())) + for (PrcPriceSetProduct p : products) { - continue; - } - String code = p.getProductCode().trim(); - String upper = code.toUpperCase(Locale.ROOT); - if (!seen.add(upper)) - { - continue; - } - boolean inLibrary = existsInLibrary(code, asOf); - ProductRow row = new ProductRow(); - row.setCustomerCode(customerCode); - row.setProductCode(code); - row.setProductName(trim(p.getProductName())); - row.setInLibrary(inLibrary); - Integer oc = orderCountByKey.get(key(customerCode, upper)); - row.setOrderCount(oc == null ? 0 : oc.intValue()); - packageRows.add(row); - if (inLibrary) - { - matched++; - } - else - { - missingRows.add(row); + if (p == null || StringUtils.isEmpty(p.getProductCode())) + { + continue; + } + if (!isSalesProduct(p)) + { + continue; + } + String code = p.getProductCode().trim(); + String upper = code.toUpperCase(Locale.ROOT); + if (!codes.add(upper)) + { + continue; + } + PackageProductRow pr = new PackageProductRow(); + pr.setCustomerCode(customerCode); + pr.setProductCode(code); + pr.setProductName(trim(p.getProductName())); + packageRows.add(pr); } } + packageCodeByCustomer.put(customerCode, codes); + } + packageRows.sort(Comparator + .comparing(PackageProductRow::getCustomerCode, nullSafe()) + .thenComparing(PackageProductRow::getProductCode, nullSafe())); + + List orderProducts = new ArrayList(); + List missingProducts = new ArrayList(); + int matched = 0; + for (Map g : groups) + { + String customerCode = trim(str(g, "customer_code")); + String productCode = trim(str(g, "product_code")); + if (StringUtils.isEmpty(customerCode) || StringUtils.isEmpty(productCode)) + { + continue; + } + Set pkg = packageCodeByCustomer.get(customerCode); + if (pkg == null) + { + pkg = Collections.emptySet(); + } + boolean inPackage = pkg.contains(productCode.toUpperCase(Locale.ROOT)); + String mappedCode = resolveMappedIfDifferent(productCode); + boolean mappedInPackage = false; + if (StringUtils.isNotEmpty(mappedCode)) + { + mappedInPackage = pkg.contains(mappedCode.toUpperCase(Locale.ROOT)); + } + boolean covered = inPackage || mappedInPackage; + + OrderProductRow row = new OrderProductRow(); + row.setCustomerCode(customerCode); + row.setUpstreamCode(productCode); + row.setProductName(trim(str(g, "product_name"))); + row.setMappedCode(mappedCode); + row.setInPackage(inPackage); + row.setMappedInPackage(mappedInPackage); + row.setCovered(covered); + row.setOrderCount((int) Math.min(Integer.MAX_VALUE, longValue(g, "order_count"))); + orderProducts.add(row); + if (covered) + { + matched++; + } + else + { + missingProducts.add(row); + } } - packageRows.sort(Comparator - .comparing(ProductRow::getCustomerCode, nullSafe()) - .thenComparing(ProductRow::getProductCode, nullSafe())); - missingRows.sort(Comparator - .comparing(ProductRow::getCustomerCode, nullSafe()) - .thenComparing(ProductRow::getProductCode, nullSafe())); + orderProducts.sort(Comparator + .comparing(OrderProductRow::isCovered) + .thenComparing(OrderProductRow::getCustomerCode, nullSafe()) + .thenComparing(OrderProductRow::getUpstreamCode, nullSafe())); + missingProducts.sort(Comparator + .comparing(OrderProductRow::getCustomerCode, nullSafe()) + .thenComparing(OrderProductRow::getUpstreamCode, nullSafe())); + result.setOrderTotal(orderTotal); + result.setDistinctOrderProducts(orderProducts.size()); result.setPackageProductCount(packageRows.size()); result.setMatchedCount(matched); - result.setMissingCount(missingRows.size()); + result.setMissingCount(missingProducts.size()); result.setPackageProducts(packageRows); - result.setMissingProducts(missingRows); + result.setOrderProducts(orderProducts); + result.setMissingProducts(missingProducts); return result; } @@ -136,32 +189,28 @@ public class OrderBillProductCheckService return rows == null ? new ArrayList() : rows; } - private Map loadOrderCounts(PrcOrderBill query) + private String resolveMappedIfDifferent(String upstreamCode) { - List> groups = orderBillMapper.selectDistinctProductGroups(query); - Map map = new HashMap(); - if (groups == null) + if (productCodeMappingService == null || StringUtils.isEmpty(upstreamCode)) { - return map; + return null; } - for (Map g : groups) + String mapped = productCodeMappingService.resolveBmsProductCode(upstreamCode); + if (StringUtils.isEmpty(mapped) || mapped.equalsIgnoreCase(upstreamCode.trim())) { - String customerCode = trim(str(g, "customer_code")); - String productCode = trim(str(g, "product_code")); - if (StringUtils.isEmpty(customerCode) || StringUtils.isEmpty(productCode)) - { - continue; - } - map.put(key(customerCode, productCode.toUpperCase(Locale.ROOT)), - Integer.valueOf((int) Math.min(Integer.MAX_VALUE, longValue(g, "order_count")))); + return null; } - return map; + return mapped.trim(); } - private boolean existsInLibrary(String productCode, Date asOf) + private static boolean isSalesProduct(PrcPriceSetProduct p) { - BmsProduct hit = productMapper.selectEffectiveBmsProductByCode(productCode, asOf); - return hit != null && StringUtils.isNotEmpty(hit.getProductCode()); + if (p == null) + { + return false; + } + String type = p.getPriceType(); + return StringUtils.isEmpty(type) || "SALES".equalsIgnoreCase(type.trim()); } private void validate(PrcOrderBill query) @@ -248,11 +297,6 @@ public class OrderBillProductCheckService return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(asOf); } - private static String key(String customerCode, String productUpper) - { - return customerCode + "|" + productUpper; - } - private static String trim(String s) { return StringUtils.isEmpty(s) ? null : s.trim(); 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 776bf0e7..36531f73 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 @@ -359,6 +359,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService } PrcPriceSetProduct q = new PrcPriceSetProduct(); q.setPsvId(psvId); + q.setPriceType("SALES"); List rows = setProductMapper.selectPrcPriceSetProductList(q); if (rows == null || rows.isEmpty()) { diff --git a/bms-front/src/views/price/orderBill/components/OrderBillProductCheckDrawer.vue b/bms-front/src/views/price/orderBill/components/OrderBillProductCheckDrawer.vue index b0be9873..69bd2824 100644 --- a/bms-front/src/views/price/orderBill/components/OrderBillProductCheckDrawer.vue +++ b/bms-front/src/views/price/orderBill/components/OrderBillProductCheckDrawer.vue @@ -1,5 +1,5 @@