diff --git a/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisTmsSiteBatchController.java b/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisTmsSiteBatchController.java index b1f3e615b..6f329a29c 100644 --- a/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisTmsSiteBatchController.java +++ b/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisTmsSiteBatchController.java @@ -79,6 +79,20 @@ public class EmisTmsSiteBatchController extends EmisBaseController return getDataTable(list); } + + /** + * 根据批次号扫描组批次 + * + * @return + */ + @GetMapping("/scanSiteBatch") +// @PreAuthorize("@ss.hasPermi('emis:emisTmsSiteBatch:scanSiteBatch')") + public AjaxResult scanSiteBatch(String batchNo) { + emisTmsSiteBatchService.scanSiteBatch(batchNo); + return AjaxResult.success("发送成功"); + + } + /** * 批次明细 * @param emisTmsSiteBatch diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchDtlMapper.java b/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchDtlMapper.java index 28c8cef78..a5037a1cd 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchDtlMapper.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchDtlMapper.java @@ -13,6 +13,9 @@ package com.xdadan.erp.emis.mapper; import java.util.List; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xdadan.erp.emis.domain.EmisTmsSiteBatchDtl; +import com.xdadan.erp.emis.domain.EmisWaybill; +import org.apache.ibatis.annotations.Param; + /** * @ClassName EmisTmsSiteBatchDtlMapper * @Description

一级网点TMS组批次明细 Mapper 接口

@@ -67,4 +70,12 @@ public interface EmisTmsSiteBatchDtlMapper extends BaseMapper selectEmisWayBillByBatchNo(@Param("batchNo") String batchNo); } diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchMissMapper.java b/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchMissMapper.java index 35572edb2..d5c03bcb1 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchMissMapper.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTmsSiteBatchMissMapper.java @@ -2,6 +2,7 @@ package com.xdadan.erp.emis.mapper; import java.util.List; import com.xdadan.erp.emis.domain.EmisTmsSiteBatchMiss; +import org.apache.ibatis.annotations.Param; /** * TMS漏组批次信息Mapper接口 @@ -65,6 +66,10 @@ public interface EmisTmsSiteBatchMissMapper { */ public int batchInsertEmisTmsSiteBatchMiss(List list); + /** + * + * @param lineCode + */ void deleteAll(String lineCode); /** @@ -74,4 +79,10 @@ public interface EmisTmsSiteBatchMissMapper { * @return TMS漏组批次信息列表 */ public List selectByBillCodeCount(Integer count); + + /** + * + * @param billCodes + */ + void deleteByBillCodes(@Param("billCodes") List billCodes); } \ No newline at end of file diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTransPlanRuleMapper.java b/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTransPlanRuleMapper.java index 64c8cc1ef..5adeb04bb 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTransPlanRuleMapper.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/mapper/EmisTransPlanRuleMapper.java @@ -132,4 +132,11 @@ public interface EmisTransPlanRuleMapper { * @return 线路信息列表 */ public List> selectLineInfoByNames(List lineNames); + + /** + * + * @param lineCodes + * @return + */ + List selectEmisTransPlanRuleByLineCodes(@Param("lineCodes") List lineCodes); } \ No newline at end of file diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java index ad2b71ce5..2720c7901 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java @@ -13,6 +13,7 @@ package com.xdadan.erp.emis.service; import cn.hutool.core.collection.CollectionUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; + import com.beust.jcommander.internal.Lists; import com.xdadan.erp.common.core.domain.entity.SysUser; import com.xdadan.erp.common.core.redis.RedissonService; import com.xdadan.erp.common.utils.*; @@ -171,7 +172,22 @@ public class EmisBaseService { @Autowired private EmisCommissionDtlMapper emisCommissionDtlMapper; - private static SysUser emptySysUser = new SysUser(); + @Autowired + private EmisTmsSiteBatchMissMapper emisTmsSiteBatchMissMapper; + + @Autowired + private EmisTransPlanRuleMapper emisTransPlanRuleMapper; + + @Autowired + private EmisWaybillBatchStatusMapper emisWaybillBatchStatusMapper; + + @Autowired + private EmisTmsSiteBatchMissRecordMapper emisTmsSiteBatchMissRecordMapper; + + private int totalBatchStatusRecords = 0; + private int totalMissRecords = 0; + + private static SysUser emptySysUser = new SysUser(); static { emptySysUser.setUserId(0L); @@ -4105,6 +4121,568 @@ public class EmisBaseService { } } + public void scanSiteBatchByWaybills(List emisWaybills) { + try { + if (CollectionUtil.isEmpty(emisWaybills)) { + return; + } + List billCodes = emisWaybills.stream().map(EmisWaybill::getBillCode).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(billCodes)) { + return; + } + List lineCodes = emisWaybills.stream().map(EmisWaybill::getTransLineType).distinct().collect(Collectors.toList()); + if (CollectionUtil.isEmpty(lineCodes)) { + return; + } + emisTmsSiteBatchMissMapper.deleteByBillCodes(billCodes); + long startTime = System.currentTimeMillis(); + int totalProcessed = 0; + int successCount = 0; + int failCount = 0; + List failedBillCodes = new ArrayList<>(); + + // 获取所有线路规划 + List treeRules = this.selectEmisTransPlanRuleTree(lineCodes); + if (treeRules.isEmpty()) { + log.info("No trans plan rules found, skip auto scan"); + return; + } + + // 按线路编码分组 + Map> transPlanRulesByLineCode = treeRules.stream() + .collect(Collectors.groupingBy(EmisTransPlanRule::getLineCode)); + + // 直接处理运单批次 + EmisBaseService.ProcessResult result = processWaybillBatch(emisWaybills, transPlanRulesByLineCode); + successCount += result.getSuccessCount(); + failCount += result.getFailCount(); + failedBillCodes.addAll(result.getFailedBillCodes()); + + // 批量插入运单组批次状态记录 + if (!result.getBatchStatusList().isEmpty()) { +// emisWaybillBatchStatusMapper.batchInsertEmisWaybillBatchStatus(result.getBatchStatusList()); + totalBatchStatusRecords += result.getBatchStatusList().size(); + } + + // 批量保存漏组记录 + if (!result.getMissRecords().isEmpty()) { + List filteredRecords = result.getMissRecords().stream() + .collect(Collectors.collectingAndThen( + Collectors.toMap( + record -> record.getBillCode() + "_" + record.getMissReason(), + record -> record, + (existing, replacement) -> existing), + map -> new ArrayList<>(map.values()))); + emisTmsSiteBatchMissMapper.batchInsertEmisTmsSiteBatchMiss(filteredRecords); + totalMissRecords += filteredRecords.size(); + } + + long endTime = System.currentTimeMillis(); + long processTime = endTime - startTime; + + // 保存扫描记录 + saveRecord(totalProcessed, successCount, failCount, processTime, failedBillCodes); + + log.info( + "Auto scan completed. Total processed: {}, Success: {}, Failed: {}, Time taken: {}ms, Batch status records: {}, Miss records: {}", + totalProcessed, successCount, failCount, processTime, + totalBatchStatusRecords, totalMissRecords); + if (CollectionUtil.isNotEmpty(failedBillCodes)) { + log.warn("Failed bill codes: {}", failedBillCodes); + } + } catch (Exception e) { + log.error("Error in autoScanSiteBatch", e); + } + } + + private void saveRecord(int totalProcessed, int successCount, int failCount, long processTime, + List failedBillCodes) { + EmisTmsSiteBatchMissRecord record = new EmisTmsSiteBatchMissRecord(); + record.setTotalProcessed(totalProcessed); + record.setSuccessCount(successCount); + record.setFailCount(failCount); + record.setProcessTime(processTime); + record.setTotalBatchStatusRecords(totalBatchStatusRecords); + record.setTotalMissRecords(totalMissRecords); + record.setFailedBillCodes( + CollectionUtil.isNotEmpty(failedBillCodes) ? String.join(",", failedBillCodes) : ""); + record.setCreateBy("system"); + record.setCreateTime(new Date()); + record.setCreateSite("88888"); + + emisTmsSiteBatchMissRecordMapper.insertEmisTmsSiteBatchMissRecord(record); + } + + private static class ProcessResult { + private final int successCount; + private final int failCount; + private final List failedBillCodes; + private final List batchStatusList; + private final List missRecords; + + public ProcessResult(int successCount, int failCount, List failedBillCodes, + List batchStatusList, List missRecords) { + this.successCount = successCount; + this.failCount = failCount; + this.failedBillCodes = failedBillCodes; + this.batchStatusList = batchStatusList; + this.missRecords = missRecords; + } + + public int getSuccessCount() { + return successCount; + } + + public int getFailCount() { + return failCount; + } + + public List getFailedBillCodes() { + return failedBillCodes; + } + + public List getBatchStatusList() { + return batchStatusList; + } + + public List getMissRecords() { + return missRecords; + } + } + + private EmisBaseService.ProcessResult processWaybillBatch(List waybills, + Map> transPlanRulesByLineCode) { + int successCount = 0; + int failCount = 0; + List failedBillCodes = new ArrayList<>(); + List missRecords = new ArrayList<>(); + List batchStatusList = new ArrayList<>(); + + // 获取所有运单号 + List billCodes = waybills.stream() + .map(EmisWaybill::getBillCode) + .collect(Collectors.toList()); + + // 一次性查询所有运单对应的组批次信息 + List siteBatches = emisTmsSiteBatchMapper + .selectEmisTmsSiteBatchListByBillCodes(billCodes); + + // 按运单号分组 + Map> siteBatchesByBillCode = new HashMap<>(); + for (EmisTmsSiteBatch batch : siteBatches) { + if (batch.getBatchDtlList() != null) { + for (EmisTmsSiteBatchDtl dtl : batch.getBatchDtlList()) { + siteBatchesByBillCode.computeIfAbsent(dtl.getBillCode(), k -> new ArrayList<>()).add(batch); + } + } + } + + for (EmisWaybill waybill : waybills) { + try { + // 获取运单对应的线路规划 + List ruleListByTransLineType = transPlanRulesByLineCode + .get(waybill.getTransLineType()); + if (CollectionUtil.isEmpty(ruleListByTransLineType)) { + // 创建漏组记录 + EmisTmsSiteBatchMiss miss = createMissRecord(waybill); + miss.setMissReason("当前线路没有创建对应的线路规划"); + miss.setMissType("1"); + miss.setLineCode(waybill.getTransLineType()); + miss.setProductType(waybill.getProductType()); + missRecords.add(miss); + successCount++; + continue; + } + + List ruleListByProductType = ruleListByTransLineType.stream() + .filter(i -> StringUtils.isNotBlank(i.getProductType()) + && i.getProductType().contains(waybill.getProductType())) + .collect(Collectors.toList()); + + if (CollectionUtil.isEmpty(ruleListByProductType)) { + // 创建漏组记录 + EmisTmsSiteBatchMiss miss = createMissRecord(waybill); + miss.setMissReason("当前线路没有创建对应的产品类型"); + miss.setMissType("1"); + miss.setLineCode(waybill.getTransLineType()); + miss.setProductType(waybill.getProductType()); + missRecords.add(miss); + successCount++; + continue; + } + + // 获取运单对应的组批次信息 + List waybillBatches = siteBatchesByBillCode.get(waybill.getBillCode()); + if (CollectionUtil.isEmpty(waybillBatches)) { + // 创建漏组记录 + EmisTmsSiteBatchMiss miss = createMissRecord(waybill); + miss.setMissReason("当前运单没有组批次"); + miss.setMissType("2"); + miss.setLineCode(waybill.getTransLineType()); + miss.setProductType(waybill.getProductType()); + missRecords.add(miss); + successCount++; + continue; + } + + // 合并相同起始网点和下一网点的批次 + Map mergedBatches = new HashMap<>(); + for (EmisTmsSiteBatch batch : waybillBatches) { + String key = batch.getStartSiteCode() + "_" + batch.getNextSiteCode(); + if (mergedBatches.containsKey(key)) { + EmisTmsSiteBatch existingBatch = mergedBatches.get(key); + String existingSupplier = existingBatch.getSupplierCode(); + String newSupplier = batch.getSupplierCode(); + if (!existingSupplier.equals(newSupplier)) { + existingBatch.setSupplierCode(existingSupplier + "," + newSupplier); + } + } else { + mergedBatches.put(key, batch); + } + } + waybillBatches = new ArrayList<>(mergedBatches.values()); + + // 检查运单是否匹配线路规划树 + boolean isAnyBranchMatched = checkBranchMatch(ruleListByProductType, waybillBatches, waybill, + missRecords); + + if (isAnyBranchMatched) { + // 创建批次状态记录 + EmisWaybillBatchStatus batchStatus = new EmisWaybillBatchStatus(); + batchStatus.setBillCode(waybill.getBillCode()); + batchStatus.setSiteBatchStatus("1"); // 1表示匹配成功 + Date now = new Date(); + batchStatus.setCreateBy("system"); + batchStatus.setCreateTime(now); + batchStatus.setCreateSite("88888"); + batchStatusList.add(batchStatus); + } + successCount++; + } catch (Exception e) { + log.error("Failed to process waybill: {}", waybill.getBillCode(), e); + failCount++; + failedBillCodes.add(waybill.getBillCode()); + } + } + + return new EmisBaseService.ProcessResult(successCount, failCount, failedBillCodes, batchStatusList, + missRecords); + } + + public List selectEmisTransPlanRuleTree(List lineCodes) { + log.info("Start querying supplier route plan tree in EmisBaseService"); + List list = emisTransPlanRuleMapper.selectEmisTransPlanRuleByLineCodes(lineCodes); + List tree = buildTree(list); + log.info("Queried supplier route plan tree in EmisBaseService, node count={}", tree != null ? tree.size() : 0); + return tree; + } + + private EmisTmsSiteBatchMiss createMissRecord(EmisWaybill waybill) { + EmisTmsSiteBatchMiss miss = new EmisTmsSiteBatchMiss(); + miss.setBillCode(waybill.getBillCode()); + Date now = new Date(); + miss.setCreateBy("system"); + miss.setCreateTime(now); + miss.setCreateSite("88888"); + return miss; + } + + /** + * 检查一个分支是否完全匹配 + * + * @param waybillBatches 运单批次列表 + * @param waybill + * @param missRecords + * @return 是否匹配 + */ + private boolean checkBranchMatch(List ruleList, List waybillBatches, + EmisWaybill waybill, List missRecords) { + // 检查当前节点是否匹配 + boolean allNodeMatched = false; + List filterRules = Lists.newArrayList(); + for (EmisTransPlanRule rule : ruleList) { + List list = Lists.newArrayList(); + if (checkRule(rule, waybillBatches, list)) { + allNodeMatched = true; + break; + } + if (list.size() > filterRules.size()) { + filterRules = list; + } + } + if (allNodeMatched) { + return allNodeMatched; + } + if (CollectionUtil.isEmpty(filterRules)) { + // 创建漏组记录 + EmisTmsSiteBatchMiss miss = createMissRecord(waybill); + miss.setMissReason("该运单所有组批次信息都不匹配,请仔细核对"); + miss.setMissType("3"); + miss.setLineCode(waybill.getTransLineType()); + miss.setProductType(waybill.getProductType()); + missRecords.add(miss); + return false; + } + EmisTransPlanRule filterRule = filterRules.get(0); + Long ancestor; + if (StringUtils.isEmpty(filterRule.getAncestors())) { + ancestor = filterRule.getId(); + } else { + String[] ancestors = filterRule.getAncestors().split(","); + ancestor = Long.valueOf(ancestors[0]); + } + + List rules = ruleList.stream().filter(i -> i.getId().equals(ancestor)) + .collect(Collectors.toList()); + EmisTransPlanRule rule = rules.get(0); + setMissRecords(missRecords, rule, filterRules, waybill); + return false; + } + + /** + * 比较两个供应商代码字符串是否包含相同的供应商(忽略顺序) + * + * @param supplierCode1 第一个供应商代码字符串 + * @param supplierCode2 第二个供应商代码字符串 + * @return 如果包含相同的供应商返回true,否则返回false + */ + private boolean compareSupplierCodes(String supplierCode1, String supplierCode2) { + if (supplierCode1 == null || supplierCode2 == null) { + return supplierCode1 == supplierCode2; + } + Set suppliers1 = Arrays.stream(supplierCode1.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + Set suppliers2 = Arrays.stream(supplierCode2.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + return suppliers1.equals(suppliers2); + } + + private boolean checkRule(EmisTransPlanRule rule, List waybillBatches, + List list) { + // 检查当前节点是否匹配 + boolean currentMatch = false; + for (EmisTmsSiteBatch batch : waybillBatches) { + if (rule.getStartSiteCode().equals(batch.getStartSiteCode()) && + rule.getNextSiteCode().equals(batch.getNextSiteCode()) && + compareSupplierCodes(rule.getSupplierCode(), batch.getSupplierCode()) + && batch.getEtd().before(rule.getEndDate()) + && !batch.getEtd().before(rule.getStartDate())) { + currentMatch = true; + list.add(rule); + break; + } + } + + // 如果是叶子节点,返回当前节点的匹配结果 + if (CollectionUtil.isEmpty(rule.getChildren())) { + return currentMatch; + } + + // 获取当前节点的所有直接子节点(第一级) + Set firstLevelRules = new HashSet<>(rule.getChildren()); + + // 检查第一级是否至少有一个节点匹配 + boolean hasFirstLevelMatch = false; + // 收集所有第一级节点的子节点(第二级) + Set secondLevelRules = new HashSet<>(); + + for (EmisTransPlanRule firstLevelRule : firstLevelRules) { + // 检查第一级节点是否匹配 + boolean firstLevelMatch = false; + for (EmisTmsSiteBatch batch : waybillBatches) { + if (firstLevelRule.getStartSiteCode().equals(batch.getStartSiteCode()) && + firstLevelRule.getNextSiteCode().equals(batch.getNextSiteCode()) && + compareSupplierCodes(firstLevelRule.getSupplierCode(), batch.getSupplierCode()) + && batch.getEtd().before(firstLevelRule.getEndDate()) + && !batch.getEtd().before(firstLevelRule.getStartDate())) { + firstLevelMatch = true; + list.add(firstLevelRule); + break; + } + } + + if (firstLevelMatch) { + hasFirstLevelMatch = true; + } + + // 收集当前第一级节点的所有子节点到第二级集合中 + if (!CollectionUtil.isEmpty(firstLevelRule.getChildren())) { + secondLevelRules.addAll(firstLevelRule.getChildren()); + } + } + + // 如果第二级为空,说明已经到底了,返回当前节点匹配且第一级至少有一个匹配的结果 + if (secondLevelRules.isEmpty()) { + return currentMatch && hasFirstLevelMatch; + } + + // 检查第二级是否至少有一个节点匹配 + boolean hasSecondLevelMatch = false; + // 收集所有第二级节点的子节点(第三级) + Set thirdLevelRules = new HashSet<>(); + + for (EmisTransPlanRule secondLevelRule : secondLevelRules) { + // 检查第二级节点是否匹配 + boolean secondLevelMatch = false; + for (EmisTmsSiteBatch batch : waybillBatches) { + if (secondLevelRule.getStartSiteCode().equals(batch.getStartSiteCode()) && + secondLevelRule.getNextSiteCode().equals(batch.getNextSiteCode()) && + compareSupplierCodes(secondLevelRule.getSupplierCode(), batch.getSupplierCode()) + && batch.getEtd().before(secondLevelRule.getEndDate()) + && !batch.getEtd().before(secondLevelRule.getStartDate())) { + secondLevelMatch = true; + list.add(secondLevelRule); + break; + } + } + + if (secondLevelMatch) { + hasSecondLevelMatch = true; + } + + // 收集当前第二级节点的所有子节点到第三级集合中 + if (!CollectionUtil.isEmpty(secondLevelRule.getChildren())) { + thirdLevelRules.addAll(secondLevelRule.getChildren()); + } + } + + // 递归处理下一级,并收集结果 + boolean hasNextLevelMatch = checkNextLevel(thirdLevelRules, waybillBatches, list); + + // 返回当前节点匹配且每一级都至少有一个匹配的结果 + return currentMatch && hasFirstLevelMatch && hasSecondLevelMatch && hasNextLevelMatch; + } + + /** + * 递归检查下一级节点 + */ + private boolean checkNextLevel(Set currentLevelRules, + List waybillBatches, List list) { + // 如果当前级为空,说明已经到底了,返回true + if (currentLevelRules.isEmpty()) { + return true; + } + + // 检查当前级是否至少有一个节点匹配 + boolean hasCurrentLevelMatch = false; + // 收集所有当前级节点的子节点(下一级) + Set nextLevelRules = new HashSet<>(); + + for (EmisTransPlanRule currentLevelRule : currentLevelRules) { + // 检查当前级节点是否匹配 + boolean currentLevelMatch = false; + for (EmisTmsSiteBatch batch : waybillBatches) { + if (currentLevelRule.getStartSiteCode().equals(batch.getStartSiteCode()) && + currentLevelRule.getNextSiteCode().equals(batch.getNextSiteCode()) && + compareSupplierCodes(currentLevelRule.getSupplierCode(), batch.getSupplierCode()) + && batch.getEtd().before(currentLevelRule.getEndDate()) + && !batch.getEtd().before(currentLevelRule.getStartDate())) { + currentLevelMatch = true; + list.add(currentLevelRule); + break; + } + } + + if (currentLevelMatch) { + hasCurrentLevelMatch = true; + } + + // 收集当前级节点的所有子节点到下一级集合中 + if (!CollectionUtil.isEmpty(currentLevelRule.getChildren())) { + nextLevelRules.addAll(currentLevelRule.getChildren()); + } + } + + // 递归处理下一级,并收集结果 + boolean hasNextLevelMatch = checkNextLevel(nextLevelRules, waybillBatches, list); + + // 返回当前级至少有一个匹配且下一级匹配的结果 + return hasCurrentLevelMatch && hasNextLevelMatch; + } + + private void setMissRecords(List missRecords, EmisTransPlanRule rule, + List filterRules, EmisWaybill waybill) { + // 检查当前节点是否在filterRules中 + boolean isMatched = filterRules.stream() + .anyMatch(filterRule -> filterRule.getStartSiteCode().equals(rule.getStartSiteCode()) && + filterRule.getNextSiteCode().equals(rule.getNextSiteCode()) && + compareSupplierCodes(filterRule.getSupplierCode(), rule.getSupplierCode())); + + if (!isMatched) { + // 创建漏组记录 + EmisTmsSiteBatchMiss miss = createMissRecord(waybill); + miss.setMissReason(String.format("组批次有遗漏/错误,请根据推荐路线排查: %s-%s-%s", + rule.getStartSiteName(), + rule.getNextSiteName(), + rule.getSupplierName())); + setRule(rule, miss); + missRecords.add(miss); + } + + List childrens = rule.getChildren(); + if (CollectionUtil.isEmpty(childrens)) { + return; + } + // 递归处理子节点 + checkChildrens(missRecords, childrens, filterRules, waybill); + } + + private static void setRule(EmisTransPlanRule rule, EmisTmsSiteBatchMiss miss) { + miss.setTransPlanRuleId(rule.getId()); + miss.setLineCode(rule.getLineCode()); + miss.setProductType(rule.getProductType()); + miss.setStartSiteCode(rule.getStartSiteCode()); + miss.setNextSiteCode(rule.getNextSiteCode()); + miss.setManager(rule.getManager()); + miss.setSupplierCode(rule.getSupplierCode()); + miss.setStartDate(rule.getStartDate()); + miss.setEndDate(rule.getEndDate()); + miss.setMissType("0"); + } + + private void checkChildrens(List missRecords, List childrens, + List filterRules, EmisWaybill waybill) { + // 检查子节点是否至少有一个匹配 + boolean hasMatchedChild = childrens.stream() + .anyMatch(childRule -> filterRules.stream() + .anyMatch( + filterRule -> filterRule.getStartSiteCode().equals(childRule.getStartSiteCode()) && + filterRule.getNextSiteCode().equals(childRule.getNextSiteCode()) && + compareSupplierCodes(filterRule.getSupplierCode(), + childRule.getSupplierCode()))); + + if (!hasMatchedChild) { + // 如果所有子节点都不匹配,为每个子节点创建漏组记录 + StringBuilder missReason = new StringBuilder("组批次有遗漏/错误,请根据推荐路线排查: "); + for (int i = 0; i < childrens.size(); i++) { + EmisTransPlanRule childRule = childrens.get(i); + if (i > 0) { + missReason.append(" 或 "); + } + missReason.append(String.format("%s-%s-%s", + childRule.getStartSiteName(), + childRule.getNextSiteName(), + childRule.getSupplierName())); + } + + // 创建漏组记录 + EmisTmsSiteBatchMiss miss = createMissRecord(waybill); + miss.setMissReason(missReason.toString()); + setRule(childrens.get(0), miss); + missRecords.add(miss); + } + + // 递归处理所有子节点 + for (EmisTransPlanRule child : childrens) { + List childList = child.getChildren(); + if (!CollectionUtil.isEmpty(childList)) { + checkChildrens(missRecords, childList, filterRules, waybill); + } + } + } + public static void main(String[] args) throws ParseException { // Date startDate = new Date("202"); Date startDate= new SimpleDateFormat("yyyy-MM-dd").parse("2025-05-11"); diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/IEmisTmsSiteBatchService.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/IEmisTmsSiteBatchService.java index 178db8f0d..9ef466b3c 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/service/IEmisTmsSiteBatchService.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/IEmisTmsSiteBatchService.java @@ -73,4 +73,11 @@ public interface IEmisTmsSiteBatchService * @return 结果 */ public int deleteEmisTmsSiteBatchById(Long id); + + /** + * 根据批次号扫描组批次 + * + * @param batchNo + */ + void scanSiteBatch(String batchNo); } diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisQuotePriceServiceImpl.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisQuotePriceServiceImpl.java index a155f97e5..d65ca1bca 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisQuotePriceServiceImpl.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisQuotePriceServiceImpl.java @@ -10,6 +10,7 @@ package com.xdadan.erp.emis.service.impl; +import java.util.Date; import java.util.List; import cn.hutool.core.collection.CollectionUtil; @@ -17,6 +18,7 @@ import com.xdadan.erp.emis.domain.EmisQuoteExpression; import com.xdadan.erp.emis.domain.enumtype.EmisBizErrorType; import com.xdadan.erp.emis.domain.exception.EmisBizError; import com.xdadan.erp.emis.mapper.EmisQuoteExpressionMapper; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.xdadan.erp.emis.domain.EmisQuotePrice; @@ -168,6 +170,24 @@ public class EmisQuotePriceServiceImpl implements IEmisQuotePriceService int successCount = 0; for (EmisQuotePrice emisQuotePrice : emisQuotePriceList) { try { + if (emisQuotePrice.getQuoteId() == null) { + continue; + } + EmisQuotePrice dbQuotePrice = emisQuotePriceMapper + .selectEmisQuotePriceByQuoteId(emisQuotePrice.getQuoteId()); + if (dbQuotePrice == null) { + continue; + } + Date startDate = emisQuotePrice.getStartDate(); + Date endDate = emisQuotePrice.getEndDate(); + BeanUtils.copyProperties(dbQuotePrice, emisQuotePrice); + if (startDate != null) { + emisQuotePrice.setStartDate(startDate); + } + if (endDate != null) { + emisQuotePrice.setEndDate(endDate); + } + // 检查是否重复 List listOld = emisQuotePriceMapper.checkUnique(emisQuotePrice); if (!CollectionUtil.isEmpty(listOld)) { diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchMissServiceImpl.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchMissServiceImpl.java index 4fb126b8e..71b8cfc2c 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchMissServiceImpl.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchMissServiceImpl.java @@ -538,17 +538,126 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements return currentMatch; } - // 检查所有子节点,记录所有匹配的节点 - boolean allChildrenMatch = true; - for (EmisTransPlanRule childRule : rule.getChildren()) { - boolean childMatch = checkRule(childRule, waybillBatches, list); - if (!childMatch) { - allChildrenMatch = false; + // 获取当前节点的所有直接子节点(第一级) + Set firstLevelRules = new HashSet<>(rule.getChildren()); + + // 检查第一级是否至少有一个节点匹配 + boolean hasFirstLevelMatch = false; + // 收集所有第一级节点的子节点(第二级) + Set secondLevelRules = new HashSet<>(); + + for (EmisTransPlanRule firstLevelRule : firstLevelRules) { + // 检查第一级节点是否匹配 + boolean firstLevelMatch = false; + for (EmisTmsSiteBatch batch : waybillBatches) { + if (firstLevelRule.getStartSiteCode().equals(batch.getStartSiteCode()) && + firstLevelRule.getNextSiteCode().equals(batch.getNextSiteCode()) && + compareSupplierCodes(firstLevelRule.getSupplierCode(), batch.getSupplierCode()) + && batch.getEtd().before(firstLevelRule.getEndDate()) + && !batch.getEtd().before(firstLevelRule.getStartDate())) { + firstLevelMatch = true; + list.add(firstLevelRule); + break; + } + } + + if (firstLevelMatch) { + hasFirstLevelMatch = true; + } + + // 收集当前第一级节点的所有子节点到第二级集合中 + if (!CollectionUtil.isEmpty(firstLevelRule.getChildren())) { + secondLevelRules.addAll(firstLevelRule.getChildren()); } } - // 返回当前节点和所有子节点的匹配结果 - return currentMatch && allChildrenMatch; + // 如果第二级为空,说明已经到底了,返回当前节点匹配且第一级至少有一个匹配的结果 + if (secondLevelRules.isEmpty()) { + return currentMatch && hasFirstLevelMatch; + } + + // 检查第二级是否至少有一个节点匹配 + boolean hasSecondLevelMatch = false; + // 收集所有第二级节点的子节点(第三级) + Set thirdLevelRules = new HashSet<>(); + + for (EmisTransPlanRule secondLevelRule : secondLevelRules) { + // 检查第二级节点是否匹配 + boolean secondLevelMatch = false; + for (EmisTmsSiteBatch batch : waybillBatches) { + if (secondLevelRule.getStartSiteCode().equals(batch.getStartSiteCode()) && + secondLevelRule.getNextSiteCode().equals(batch.getNextSiteCode()) && + compareSupplierCodes(secondLevelRule.getSupplierCode(), batch.getSupplierCode()) + && batch.getEtd().before(secondLevelRule.getEndDate()) + && !batch.getEtd().before(secondLevelRule.getStartDate())) { + secondLevelMatch = true; + list.add(secondLevelRule); + break; + } + } + + if (secondLevelMatch) { + hasSecondLevelMatch = true; + } + + // 收集当前第二级节点的所有子节点到第三级集合中 + if (!CollectionUtil.isEmpty(secondLevelRule.getChildren())) { + thirdLevelRules.addAll(secondLevelRule.getChildren()); + } + } + + // 递归处理下一级,并收集结果 + boolean hasNextLevelMatch = checkNextLevel(thirdLevelRules, waybillBatches, list); + + // 返回当前节点匹配且每一级都至少有一个匹配的结果 + return currentMatch && hasFirstLevelMatch && hasSecondLevelMatch && hasNextLevelMatch; + } + + /** + * 递归检查下一级节点 + */ + private boolean checkNextLevel(Set currentLevelRules, + List waybillBatches, List list) { + // 如果当前级为空,说明已经到底了,返回true + if (currentLevelRules.isEmpty()) { + return true; + } + + // 检查当前级是否至少有一个节点匹配 + boolean hasCurrentLevelMatch = false; + // 收集所有当前级节点的子节点(下一级) + Set nextLevelRules = new HashSet<>(); + + for (EmisTransPlanRule currentLevelRule : currentLevelRules) { + // 检查当前级节点是否匹配 + boolean currentLevelMatch = false; + for (EmisTmsSiteBatch batch : waybillBatches) { + if (currentLevelRule.getStartSiteCode().equals(batch.getStartSiteCode()) && + currentLevelRule.getNextSiteCode().equals(batch.getNextSiteCode()) && + compareSupplierCodes(currentLevelRule.getSupplierCode(), batch.getSupplierCode()) + && batch.getEtd().before(currentLevelRule.getEndDate()) + && !batch.getEtd().before(currentLevelRule.getStartDate())) { + currentLevelMatch = true; + list.add(currentLevelRule); + break; + } + } + + if (currentLevelMatch) { + hasCurrentLevelMatch = true; + } + + // 收集当前级节点的所有子节点到下一级集合中 + if (!CollectionUtil.isEmpty(currentLevelRule.getChildren())) { + nextLevelRules.addAll(currentLevelRule.getChildren()); + } + } + + // 递归处理下一级,并收集结果 + boolean hasNextLevelMatch = checkNextLevel(nextLevelRules, waybillBatches, list); + + // 返回当前级至少有一个匹配且下一级匹配的结果 + return hasCurrentLevelMatch && hasNextLevelMatch; } private void setMissRecords(List missRecords, EmisTransPlanRule rule, diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchServiceImpl.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchServiceImpl.java index 3327e8ce9..c36028c9f 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchServiceImpl.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/impl/EmisTmsSiteBatchServiceImpl.java @@ -13,15 +13,20 @@ package com.xdadan.erp.emis.service.impl; import java.util.Date; import java.util.List; +import cn.hutool.core.collection.CollectionUtil; import com.xdadan.erp.common.core.redis.RedissonService; import com.xdadan.erp.common.utils.DateUtils; import com.xdadan.erp.common.utils.SecurityUtils; import com.xdadan.erp.emis.domain.EmisSite; import com.xdadan.erp.emis.domain.EmisTmsSiteBatchDtl; +import com.xdadan.erp.emis.domain.EmisWaybill; import com.xdadan.erp.emis.mapper.EmisTmsSiteBatchDtlMapper; import com.xdadan.erp.emis.service.EmisBaseService; +import jodd.util.StringUtil; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import com.xdadan.erp.emis.domain.EmisTmsSiteBatch; import com.xdadan.erp.emis.mapper.EmisTmsSiteBatchMapper; @@ -36,6 +41,7 @@ import org.springframework.util.CollectionUtils; * @date 2024-03-01 08:54:56 */ @Service +@Slf4j public class EmisTmsSiteBatchServiceImpl extends EmisBaseService implements IEmisTmsSiteBatchService { @Autowired @@ -143,4 +149,18 @@ public class EmisTmsSiteBatchServiceImpl extends EmisBaseService implements IEmi return emisTmsSiteBatchMapper.deleteEmisTmsSiteBatchById(id); } + @Override + @Async + public void scanSiteBatch(String batchNo) { + log.info("start scan site batch ,batchNo:{}", batchNo); + if (StringUtil.isBlank(batchNo)) { + return; + } + List waybills = emisTmsSiteBatchDtlMapper.selectEmisWayBillByBatchNo(batchNo); + if (CollectionUtil.isEmpty(waybills)) { + return; + } + scanSiteBatchByWaybills(waybills); + } + } diff --git a/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchDtlMapper.xml b/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchDtlMapper.xml index 502c78dbb..c0cc5735f 100644 --- a/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchDtlMapper.xml +++ b/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchDtlMapper.xml @@ -184,4 +184,12 @@ where batch_no = #{batchNo} and del_flag = '0' + + \ No newline at end of file diff --git a/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchMissMapper.xml b/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchMissMapper.xml index 2361a3b73..d9a609293 100644 --- a/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchMissMapper.xml +++ b/emis-biz/src/main/resources/mapper/EmisTmsSiteBatchMissMapper.xml @@ -256,6 +256,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + update emis_tms_site_batch_miss set del_flag = '1' + where bill_code in + + #{billCode} + + + insert into emis_tms_site_batch_miss (trans_plan_rule_id, bill_code, miss_reason, line_code,manager, product_type, diff --git a/emis-biz/src/main/resources/mapper/EmisTransPlanRuleMapper.xml b/emis-biz/src/main/resources/mapper/EmisTransPlanRuleMapper.xml index 88d4ad29f..8535ab645 100644 --- a/emis-biz/src/main/resources/mapper/EmisTransPlanRuleMapper.xml +++ b/emis-biz/src/main/resources/mapper/EmisTransPlanRuleMapper.xml @@ -61,6 +61,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" order by id desc + +