From b29d86d9ed34fd890eb14cb7a75f6ae4f7148865 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 01 九月 2026 13:58:03 +0800
Subject: [PATCH] feat(mes): 新增防伪防窜货功能模块

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java |   79 ++++++++++++++++++++++++++++++++++++---
 1 files changed, 73 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..110c7b1 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;
@@ -73,21 +78,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,6 +208,8 @@
         resp.setInbound(buildInbound(batchId));
         // 璐ㄦ鍖哄潡
         resp.setQuality(buildQuality(batch));
+        // 闃蹭吉楠岃瘉鍖哄潡
+        fillAntiFake(resp, traceCode);
         // 鍙�夋墿灞曞尯鍧楋紙鏈夋暟鎹墠杩斿洖锛�
         fillOptionalModules(resp, batch);
         return resp;
@@ -254,6 +302,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());

--
Gitblit v1.9.3