订单数据源增加订单对比功能

This commit is contained in:
wuweihua
2026-10-20 13:55:55 +08:00
parent 8093cb52e7
commit 45969a4d29
20 changed files with 1865 additions and 96 deletions
@@ -0,0 +1,317 @@
package com.bms.price.service;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
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.mapper.PrcOrderBillMapper;
/**
* Detect package sales products (as of billing end time) that are missing from product catalog.
*/
@Service
public class OrderBillProductCheckService
{
private static final int ORDER_DRILL_LIMIT = 500;
@Autowired
private ITmsPriceQuoteService tmsPriceQuoteService;
@Autowired
private BmsProductMapper productMapper;
@Autowired
private PrcOrderBillMapper orderBillMapper;
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id",
permission = "price:orderBill:list")
public OrderBillProductCheckResult check(PrcOrderBill query)
{
validate(query);
Date asOf = resolveAsOf(query);
OrderBillProductCheckResult result = new OrderBillProductCheckResult();
result.setAsOfTime(formatAsOf(asOf));
List<String> customerCodes = normalizeCodes(query.getCustomerCodes());
Map<String, Integer> orderCountByKey = loadOrderCounts(query);
long orderTotal = 0L;
for (Integer c : orderCountByKey.values())
{
if (c != null)
{
orderTotal += c.intValue();
}
}
result.setOrderTotal(orderTotal);
List<ProductRow> packageRows = new ArrayList<ProductRow>();
List<ProductRow> missingRows = new ArrayList<ProductRow>();
int matched = 0;
for (String customerCode : customerCodes)
{
List<PrcPriceSetProduct> products = tmsPriceQuoteService.listAllBoundPackageProducts(customerCode, asOf);
Set<String> seen = new HashSet<String>();
if (products == null)
{
continue;
}
for (PrcPriceSetProduct p : products)
{
if (p == null || StringUtils.isEmpty(p.getProductCode()))
{
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);
}
}
}
packageRows.sort(Comparator
.comparing(ProductRow::getCustomerCode, nullSafe())
.thenComparing(ProductRow::getProductCode, nullSafe()));
missingRows.sort(Comparator
.comparing(ProductRow::getCustomerCode, nullSafe())
.thenComparing(ProductRow::getProductCode, nullSafe()));
result.setPackageProductCount(packageRows.size());
result.setMatchedCount(matched);
result.setMissingCount(missingRows.size());
result.setPackageProducts(packageRows);
result.setMissingProducts(missingRows);
return result;
}
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id",
permission = "price:orderBill:list")
public List<PrcOrderBill> listOrdersByProduct(PrcOrderBill query)
{
validate(query);
if (StringUtils.isEmpty(query.getProductCode()))
{
throw new ServiceException("productCode required");
}
query.setProductCode(query.getProductCode().trim());
List<PrcOrderBill> rows = orderBillMapper.selectOrderBillsByProductForCheck(query, ORDER_DRILL_LIMIT);
return rows == null ? new ArrayList<PrcOrderBill>() : rows;
}
private Map<String, Integer> loadOrderCounts(PrcOrderBill query)
{
List<Map<String, Object>> groups = orderBillMapper.selectDistinctProductGroups(query);
Map<String, Integer> map = new HashMap<String, Integer>();
if (groups == null)
{
return map;
}
for (Map<String, Object> g : groups)
{
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 map;
}
private boolean existsInLibrary(String productCode, Date asOf)
{
BmsProduct hit = productMapper.selectEffectiveBmsProductByCode(productCode, asOf);
return hit != null && StringUtils.isNotEmpty(hit.getProductCode());
}
private void validate(PrcOrderBill query)
{
if (query == null)
{
throw new ServiceException("customer and billing time required");
}
List<String> codes = normalizeCodes(query.getCustomerCodes());
if (codes.isEmpty())
{
throw new ServiceException("please select customer");
}
query.setCustomerCodes(codes);
Map<String, Object> params = query.getParams();
String begin = params == null ? null : strObj(params.get("beginBizDate"));
String end = params == null ? null : strObj(params.get("endBizDate"));
if (StringUtils.isEmpty(begin) || StringUtils.isEmpty(end))
{
throw new ServiceException("please select billing time range");
}
}
private static List<String> normalizeCodes(List<String> codes)
{
if (codes == null || codes.isEmpty())
{
return new ArrayList<String>();
}
LinkedHashSet<String> set = new LinkedHashSet<String>();
for (String c : codes)
{
if (StringUtils.isNotEmpty(c))
{
set.add(c.trim());
}
}
return new ArrayList<String>(set);
}
private static Date resolveAsOf(PrcOrderBill query)
{
Map<String, Object> params = query.getParams();
String raw = params == null ? null : strObj(params.get("endBizDate"));
if (StringUtils.isEmpty(raw) && params != null)
{
raw = strObj(params.get("beginBizDate"));
}
Date parsed = parseDateTime(raw);
return parsed != null ? parsed : new Date();
}
private static Date parseDateTime(String raw)
{
if (StringUtils.isEmpty(raw))
{
return null;
}
String s = raw.trim().replace('T', ' ');
try
{
if (s.length() >= 19)
{
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(s.substring(0, 19));
}
if (s.length() >= 10)
{
return new SimpleDateFormat("yyyy-MM-dd").parse(s.substring(0, 10));
}
}
catch (ParseException ignore)
{
return null;
}
return null;
}
private static String formatAsOf(Date asOf)
{
if (asOf == null)
{
return null;
}
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();
}
private static String strObj(Object v)
{
return v == null ? null : String.valueOf(v).trim();
}
private static Comparator<String> nullSafe()
{
return Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER);
}
private static String str(Map<String, Object> row, String key)
{
Object v = value(row, key);
return v == null ? null : String.valueOf(v).trim();
}
private static long longValue(Map<String, Object> row, String key)
{
Object v = value(row, key);
if (v == null)
{
return 0L;
}
if (v instanceof Number)
{
return ((Number) v).longValue();
}
try
{
return Long.parseLong(String.valueOf(v).trim());
}
catch (Exception e)
{
return 0L;
}
}
private static Object value(Map<String, Object> row, String key)
{
if (row == null || key == null)
{
return null;
}
if (row.containsKey(key))
{
return row.get(key);
}
for (Map.Entry<String, Object> e : row.entrySet())
{
if (e.getKey() != null && e.getKey().equalsIgnoreCase(key))
{
return e.getValue();
}
}
return null;
}
}