From 1ba0dbfde5634c8bf2ec20891d6b226b996f6b59 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 02 九月 2026 14:24:38 +0800
Subject: [PATCH] refactor(mes): 优化MES生产扫码入库功能支持多类型扫码
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java | 124 +++++++++++++++++++++++++++++++++++++++--
1 files changed, 118 insertions(+), 6 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
index 744bccc..04ff12f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
@@ -22,7 +22,12 @@
import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService;
import cn.iocoder.yudao.module.mes.service.pro.pallet.MesProPalletService;
import cn.iocoder.yudao.module.mes.service.pro.scan.MesProScanEventService;
+import cn.iocoder.yudao.module.mes.dal.dataobject.trace.consumer.MesTraceConsumerScanEventDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.trace.consumer.MesTraceConsumerScanEventMapper;
import cn.iocoder.yudao.module.mes.service.qc.seedquality.MesQcSeedQualityService;
+import cn.iocoder.yudao.module.mes.service.trace.consumer.MesTraceConsumerScanEventService;
+import cn.iocoder.yudao.module.mes.service.trace.crossregion.MesTraceCrossRegionService;
+import cn.iocoder.yudao.module.mes.service.trace.integrity.MesTraceIntegrityService;
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -33,6 +38,9 @@
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -73,21 +81,62 @@
private AdminUserApi adminUserApi;
@Resource
private MesWmBatchService mesWmBatchService;
+ @Resource
+ private MesTraceConsumerScanEventService consumerScanEventService;
+ @Resource
+ private MesTraceCrossRegionService crossRegionService;
+ @Resource
+ private MesTraceIntegrityService integrityService;
+ @Resource
+ private MesTraceConsumerScanEventMapper consumerScanEventMapper;
@Override
public MesProTracePublicRespVO queryTrace(String code) {
String rawCode = StrUtil.trim(StrUtil.nullToEmpty(code));
String traceCode = normalize(rawCode);
if (StrUtil.isEmpty(traceCode)) {
+ recordConsumerScan(rawCode, traceCode, null, null, false);
throw exception(MES_TRACE_CODE_NOT_FOUND);
}
- // 1. 瑙f瀽杩芥函鍏ュ彛锛屽畾浣嶆壒娆�
- TraceEntry entry = resolveEntry(traceCode);
- if (entry.batchId == null) {
- throw exception(MES_TRACE_CODE_NOT_FOUND);
+ TraceEntry entry = null;
+ try {
+ // 1. 瑙f瀽杩芥函鍏ュ彛锛屽畾浣嶆壒娆�
+ entry = resolveEntry(traceCode);
+ if (entry.batchId == null) {
+ throw exception(MES_TRACE_CODE_NOT_FOUND);
+ }
+ // 2. 鑱氬悎鍏ㄧ▼淇℃伅
+ MesProTracePublicRespVO result = buildRespVO(rawCode, traceCode, entry.sourceType, entry.batchId);
+ recordConsumerScan(rawCode, traceCode, entry.sourceType, entry.batchId, true);
+ return result;
+ } catch (RuntimeException ex) {
+ recordConsumerScan(rawCode, traceCode, entry == null ? null : entry.sourceType,
+ entry == null ? null : entry.batchId, false);
+ throw ex;
}
- // 2. 鑱氬悎鍏ㄧ▼淇℃伅
- return buildRespVO(rawCode, traceCode, entry.sourceType, entry.batchId);
+ }
+
+ /**
+ * 璁板綍娑堣垂鑰呮壂鐮佷簨浠讹紝骞惰Е鍙戦槻浼槻绐滆揣鎵╁睍锛堢獪璐у垽瀹� + 鎵爜鎽樿锛夛紝鍧囧閿欎笉褰卞搷婧簮涓绘祦绋�
+ */
+ private MesTraceConsumerScanEventDO recordConsumerScan(String rawCode, String traceCode, String sourceType,
+ Long batchId, boolean success) {
+ MesTraceConsumerScanEventDO event = consumerScanEventService.recordScan(rawCode, traceCode, sourceType,
+ batchId, success, success ? null : MES_TRACE_CODE_NOT_FOUND.getCode().toString());
+ if (event == null) {
+ return null;
+ }
+ try {
+ crossRegionService.checkAndCreateWarning(event);
+ } catch (Exception ex) {
+ log.warn("[recordConsumerScan][绐滆揣鍒ゅ畾澶辫触] eventId={}", event.getId(), ex);
+ }
+ try {
+ integrityService.recordScanIntegrity(event);
+ } catch (Exception ex) {
+ log.warn("[recordConsumerScan][鎵爜鎽樿鐢熸垚澶辫触] eventId={}", event.getId(), ex);
+ }
+ return event;
}
// ==================== 杩芥函鐮佽В鏋� ====================
@@ -162,8 +211,11 @@
resp.setInbound(buildInbound(batchId));
// 璐ㄦ鍖哄潡
resp.setQuality(buildQuality(batch));
+ // 闃蹭吉楠岃瘉鍖哄潡
+ fillAntiFake(resp, traceCode);
// 鍙�夋墿灞曞尯鍧楋紙鏈夋暟鎹墠杩斿洖锛�
fillOptionalModules(resp, batch);
+ fillTimelineAndDecision(resp);
return resp;
}
@@ -254,6 +306,25 @@
return vo;
}
+ /**
+ * 濉厖闃蹭吉楠岃瘉鍖哄潡锛氳杩芥函鐮佺疮璁℃壂鐮佹鏁般�侀娆�/鏈�杩戞壂鐮佹椂闂达紙姝e搧楠岃瘉澧炲己锛�
+ */
+ private void fillAntiFake(MesProTracePublicRespVO resp, String traceCode) {
+ try {
+ List<MesTraceConsumerScanEventDO> events = consumerScanEventMapper.selectListByNormalizedCode(traceCode);
+ if (CollUtil.isEmpty(events)) {
+ return;
+ }
+ MesProTracePublicRespVO.AntiFakeVO vo = new MesProTracePublicRespVO.AntiFakeVO();
+ vo.setScanCount((long) events.size());
+ vo.setFirstScanTime(events.get(0).getEventTime());
+ vo.setLastScanTime(events.get(events.size() - 1).getEventTime());
+ resp.setAntiFake(vo);
+ } catch (Exception ex) {
+ log.warn("[fillAntiFake][杩芥函鐮� {} 闃蹭吉楠岃瘉缁熻澶辫触: {}]", traceCode, ex.getMessage());
+ }
+ }
+
private void fillOptionalModules(MesProTracePublicRespVO resp, MesProBatchDO batch) {
try {
List<MesWmBatchPurchaseSourceRespVO> purchaseSources = mesWmBatchService.getPurchaseSourceList(batch.getCode());
@@ -271,6 +342,47 @@
}
}
+ private void fillTimelineAndDecision(MesProTracePublicRespVO resp) {
+ List<MesProTracePublicRespVO.TimelineVO> timeline = new ArrayList<>();
+ if (resp.getProduceDate() != null) {
+ timeline.add(timeline("鐢熶骇", "BATCH_CREATED", "鐢熶骇鎵规寤虹珛", resp.getProduceDate(), null,
+ resp.getBatchStatusName()));
+ }
+ if (resp.getProcessTraces() != null) {
+ resp.getProcessTraces().forEach(item -> timeline.add(timeline("鍔犲伐", item.getEventType(),
+ item.getEventType() == null ? "宸ュ簭娴佽浆" : ("IN".equals(item.getEventType()) ? "宸ュ簭鎶曞叆" : "宸ュ簭浜у嚭"),
+ item.getEventTime(), item.getQuantity(), item.getQualityStatus() == null ? null : String.valueOf(item.getQualityStatus()))));
+ }
+ if (resp.getInbound() != null) {
+ timeline.add(timeline("浠撳偍", "INBOUND", "浠撳偍鍏ュ簱", resp.getInbound().getEventTime(),
+ resp.getInbound().getQuantity(), resp.getInbound().getStatus()));
+ }
+ if (resp.getSalesTargets() != null) {
+ resp.getSalesTargets().forEach(item -> timeline.add(timeline("閿�鍞�", "OUTBOUND", "閿�鍞嚭搴�",
+ item.getSalesDate(), item.getQuantity(), item.getQualityStatus() == null ? null : String.valueOf(item.getQualityStatus()))));
+ }
+ timeline.sort(Comparator.comparing(MesProTracePublicRespVO.TimelineVO::getEventTime,
+ Comparator.nullsLast(Comparator.naturalOrder())));
+ resp.setTimeline(timeline);
+
+ MesProTracePublicRespVO.DecisionVO decision = new MesProTracePublicRespVO.DecisionVO();
+ decision.setQualityRisk(resp.getQuality() != null && Integer.valueOf(2).equals(resp.getQuality().getResult()));
+ decision.setInboundQuantity(resp.getInbound() == null ? null : resp.getInbound().getQuantity());
+ decision.setSalesTargetCount(resp.getSalesTargets() == null ? 0 : resp.getSalesTargets().size());
+ decision.setConsumerScanCount(resp.getAntiFake() == null ? 0L : resp.getAntiFake().getScanCount());
+ decision.setRiskLevel(Boolean.TRUE.equals(decision.getQualityRisk()) ? "HIGH" : "NORMAL");
+ resp.setDecision(decision);
+ }
+
+ private MesProTracePublicRespVO.TimelineVO timeline(String stage, String eventType, String eventName,
+ java.time.LocalDateTime eventTime, BigDecimal quantity,
+ String status) {
+ MesProTracePublicRespVO.TimelineVO vo = new MesProTracePublicRespVO.TimelineVO();
+ vo.setStage(stage).setEventType(eventType).setEventName(eventName).setEventTime(eventTime)
+ .setQuantity(quantity).setStatus(status);
+ return vo;
+ }
+
// ==================== 鍚嶇О鏄犲皠 ====================
private String userName(Long userId) {
--
Gitblit v1.9.3