From 7dc2054d4d93f17ce8595f1b0559c904bee2a38f Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 24 八月 2026 16:06:40 +0800
Subject: [PATCH] feat(mes): 新增生产线扫码入库功能
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesProScanEventStatusEnum.java | 6
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java | 97 ++++++++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/scan/MesProScanEventMapper.java | 32 ++++
docs/mes_scan_event_integration.md | 103 ++++++++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventService.java | 17 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java | 36 ++++
sql/mes_scan_event_inbound.sql | 36 ++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java | 18 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java | 28 +++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java | 41 +++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventPageReqVO.java | 16 ++
sql/mysql/mes_pro_scan_inbound_init.sql | 32 ++++
12 files changed, 462 insertions(+), 0 deletions(-)
diff --git a/docs/mes_scan_event_integration.md b/docs/mes_scan_event_integration.md
new file mode 100644
index 0000000..289532b
--- /dev/null
+++ b/docs/mes_scan_event_integration.md
@@ -0,0 +1,103 @@
+# 鐢熶骇绾挎壂鐮佸叆搴� - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 鐢熶骇绾挎壂鐮佸叆搴撹嚜鍔ㄥ伐浣滃彴
+- 鎵爜浜嬩欢瀹¤
+- 鐢熶骇鎵规鍜岃鐮佽鎯�
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 宸ヤ笟鐩告満鐢辫竟缂樼▼搴忓畬鎴愪簩缁寸爜璇嗗埆锛屼笂浼� `eventId`銆佽澶囩紪鐮併�佷骇绾跨紪鐮佸拰琚嬬爜銆�
+2. 鍚庣鏍规嵁琚嬬爜甯﹀嚭鐢熶骇鎵规鍜屼骇鍝佺墿鏂欙紝骞朵娇鐢� WIP 铏氭嫙浠撻厤缃畬鎴愬簱瀛樺叆璐︺��
+3. 鍏ュ簱鎴愬姛鍚庤褰曟湇鍔$鍏ュ簱鏃堕棿锛屽鍔犳殏瀛樺簱瀛橈紝骞剁疮璁℃壒娆″叆搴撹鏁般��
+4. 鍚屼竴涓� `eventId` 閲嶈瘯鏃惰繑鍥炲師浜嬩欢锛涘凡鍏ュ簱琚嬬爜涓嶄細閲嶅澧炲姞搴撳瓨銆�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|---|---|---|
+| POST | `/mes/pro/scan-in/inbound` | 琚嬬爜鎵爜鍏ュ簱锛屾垚鍔熷悗澧炲姞 WIP 铏氭嫙浠撳簱瀛樺苟鍐欏簱瀛樹簨鍔� |
+| GET | `/mes/pro/scan-in/page` | 鍒嗛〉鏌ヨ鎵爜浜嬩欢 |
+| GET | `/mes/pro/scan-in/latest` | 鏌ヨ璁惧鎴栧叏灞�鏈�杩戞壂鐮佷簨浠� |
+| GET | `/mes/pro/scan-in/dashboard` | 鏌ヨ宸ヤ綔鍙版渶杩戜簨浠跺拰绱浜嬩欢鏁� |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| eventId | String | 鏄� | 杈圭紭璁惧鐢熸垚鐨勫箓绛変簨浠跺彿 |
+| bagCode | String | 鏄� | 宸ヤ笟鐩告満璇嗗埆鍑虹殑琚嬬爜 |
+| quantity | Decimal | 鏄� | 鍏ュ簱鏁伴噺锛屾湰鏈熶竴琚嬮�氬父涓� 1 |
+| deviceCode | String | 鏄� | 璁惧缂栫爜锛岀敤浜庡璁� |
+| lineCode | String | 鍚� | 浜х嚎缂栫爜锛岀敤浜庡璁� |
+| locationId | Long | 鏄� | WIP 鏆傚瓨搴撳尯缂栧彿 |
+| areaId | Long | 鏄� | WIP 鏆傚瓨搴撲綅缂栧彿 |
+| remark | String | 鍚� | 澶囨敞 |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|---|---|---|
+| eventId | String | 骞傜瓑浜嬩欢鍙� |
+| bagCode | String | 琚嬬爜 |
+| batchCode | String | 鐢熶骇鎵规鍙� |
+| quantity | Decimal | 鏈鍏ュ簱鏁伴噺 |
+| deviceCode | String | 璁惧缂栫爜 |
+| eventTime | DateTime | 鏈嶅姟绔叆搴撴椂闂� |
+| transactionId | Long | 搴撳瓨浜嬪姟缂栧彿 |
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|---|---|---|
+| deviceCode | 璁惧鐘舵�併�佹渶杩戞壂鐮佸垪琛� | 鏄剧ず褰撳墠璁惧鎴栦簨浠舵潵婧� |
+| bagCode | 鏈�杩戞壂鐮佸垪琛� | 鏄剧ず鏈�杩戣瘑鍒殑琚嬬爜 |
+| batchCode | 鏈�杩戞壂鐮佸垪琛ㄣ�佹壒娆℃眹鎬� | 灞曠ず琚嬬爜褰掑睘鐨勭敓浜ф壒娆� |
+| eventTime | 鏈�杩戞壂鐮佸垪琛� | 浠ユ湇鍔$鍏ュ簱鏃堕棿涓哄噯 |
+| transactionId | 鏈�杩戞壂鐮佸垪琛� | 鐢ㄤ簬搴撳瓨娴佹按杩芥函 |
+| quantity | 搴撳瓨鎽樿 | 绱鏆傚瓨鏁伴噺 |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|---|---|
+| 閲嶅浜嬩欢 | 鍚屼竴 `eventId` 杩斿洖绗竴娆″鐞嗙粨鏋滐紝涓嶉噸澶嶅鍔犲簱瀛� |
+| 宸插叆搴撹鐮� | 鎷掔粷閲嶅鍏ュ簱锛屽苟淇濈暀涓氬姟閿欒淇℃伅 |
+| WIP 鏆傚瓨搴� | 浠撳簱鐢卞悗绔娇鐢� WIP 铏氭嫙浠撹В鏋愶紝鍓嶇涓嶄紶浠撳簱缂栧彿 |
+| 浜嬪姟涓�鑷存�� | 浜嬩欢銆佽鐮佺姸鎬併�佹壒娆$疮璁°�佸簱瀛樺彴璐﹀拰搴撳瓨浜嬪姟鍦ㄥ悓涓�浜嬪姟涓彁浜� |
+| 璁惧鏃堕棿 | 璁惧鎵爜鏃堕棿鐢ㄤ簬杩芥函锛屽簱瀛樺叆搴撴椂闂翠娇鐢ㄦ湇鍔$鏃堕棿 |
+
+## 澶辫触鐘舵�佷笌鏍囧噯閿欒鐮�
+
+鎵爜璇锋眰鏃犺鎴愬姛鎴栧け璐ラ兘浼氫繚鐣欐壂鐮佷簨浠惰褰曘�傜姸鎬佸彇鍊硷細
+
+| 鐘舵�� | 璇存槑 |
+|---|---|
+| SUCCESS | 宸插畬鎴愬簱瀛樺叆搴� |
+| FAILED | 鏍¢獙鎴栧簱瀛樺鐞嗗け璐� |
+
+| 閿欒鐮� | 璇存槑 |
+|---|---|
+| MES_SCAN_BAG_NOT_EXISTS | 琚嬬爜涓嶅瓨鍦� |
+| MES_SCAN_BAG_ALREADY_INBOUND | 琚嬬爜宸插叆搴� |
+| MES_SCAN_BATCH_NOT_EXISTS | 鐢熶骇鎵规涓嶅瓨鍦� |
+| MES_SCAN_WIP_WAREHOUSE_NOT_EXISTS | WIP 铏氭嫙浠撲笉瀛樺湪 |
+| MES_SCAN_TRANSACTION_FAILED | 搴撳瓨浜嬪姟澶勭悊澶辫触 |
+| MES_SCAN_EVENT_ID_CONFLICT | 浜嬩欢鍙峰凡瀛樺湪浣嗗唴瀹逛笉涓�鑷� |
+
+澶辫触璁板綍淇濈暀璁惧缂栫爜銆佽鐮併�佹帴鏀舵椂闂淬�佸け璐ユ椂闂淬�侀敊璇爜鍜岄敊璇師鍥狅紱澶辫触鏃朵笉浜х敓搴撳瓨浜嬪姟銆�
+
+## 鏉冮檺鐐�
+
+| 鏉冮檺 | 鐢ㄩ�� |
+|---|---|
+| mes:pro-scan-in:query | 鏌ヨ浜嬩欢鍜屾渶杩戞壂鐮� |
+| mes:pro-scan-in:inbound | 鎵ц鎵爜鍏ュ簱 |
+| mes:pro-scan-in:dashboard | 鏌ヨ宸ヤ綔鍙扮湅鏉� |
+
+
+- 杈圭紭绋嬪簭涓嶅緱涓婁紶鍥剧墖鎴栬棰戯紝鏈帴鍙e彧鎺ユ敹瑙g爜鍚庣殑浜岀淮鐮佺粨鏋溿��
+- 鍓嶇宸ヤ綔鍙板簲鍦ㄩ〉闈㈠嵏杞芥椂鍋滄鍒锋柊浠诲姟銆�
+- 鍏ュ簱澶辫触鏃跺睍绀哄悗绔繑鍥炵殑涓氬姟閿欒锛屼笉鑷姩璋冪敤閫氱敤搴撳瓨璋冩暣鎺ュ彛閲嶈瘯銆�
+- WIP 搴撳尯鍜屽簱浣嶉渶鎻愬墠閰嶇疆锛涙帴鍙d笉鎺ュ彈瀹㈡埛绔寚瀹氫粨搴撶紪鍙枫��
diff --git a/sql/mes_scan_event_inbound.sql b/sql/mes_scan_event_inbound.sql
new file mode 100644
index 0000000..c521d1a
--- /dev/null
+++ b/sql/mes_scan_event_inbound.sql
@@ -0,0 +1,36 @@
+ALTER TABLE mes_pro_bag_code ADD COLUMN inbound_time datetime NULL COMMENT '鍏ュ簱鏃堕棿';
+ALTER TABLE mes_pro_bag_code ADD COLUMN inbound_event_id bigint NULL COMMENT '鍏ュ簱浜嬩欢ID';
+ALTER TABLE mes_pro_bag_code ADD COLUMN inbound_device_code varchar(64) NULL COMMENT '鍏ュ簱璁惧缂栫爜';
+ALTER TABLE mes_pro_bag_code ADD COLUMN inbound_line_code varchar(64) NULL COMMENT '鍏ュ簱浜х嚎缂栫爜';
+ALTER TABLE mes_pro_batch ADD COLUMN inbound_bag_quantity int NULL DEFAULT 0 COMMENT '绱鍏ュ簱琚嬫暟' AFTER pallet_quantity;
+
+ALTER TABLE mes_pro_batch ADD COLUMN inbound_event_id bigint NULL COMMENT '鍏ュ簱浜嬩欢ID';
+ALTER TABLE mes_pro_batch ADD COLUMN inbound_device_code varchar(64) NULL COMMENT '鍏ュ簱璁惧缂栫爜';
+ALTER TABLE mes_pro_batch ADD COLUMN inbound_line_code varchar(64) NULL COMMENT '鍏ュ簱浜х嚎缂栫爜';
+CREATE TABLE mes_pro_scan_event (
+ id bigint NOT NULL AUTO_INCREMENT,
+ event_id varchar(64) NOT NULL,
+ bag_code varchar(128) NOT NULL,
+ bag_code_id bigint NULL,
+ batch_id bigint NULL,
+ batch_code varchar(128) NULL,
+ item_id bigint NULL,
+ quantity decimal(18,6) NULL,
+ device_code varchar(64) NOT NULL,
+ line_code varchar(64) NULL,
+ warehouse_id bigint NULL,
+ location_id bigint NULL,
+ area_id bigint NULL,
+ transaction_id bigint NULL,
+ status varchar(16) NOT NULL DEFAULT 'FAILED',
+ error_code varchar(64) NULL,
+ error_message varchar(500) NULL,
+ received_time datetime NOT NULL,
+ failed_time datetime NULL,
+ event_time datetime NULL,
+ remark varchar(500) NULL,
+ creator varchar(64) NULL, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updater varchar(64) NULL, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ deleted bit NOT NULL DEFAULT 0,
+ PRIMARY KEY (id), UNIQUE KEY uk_mes_scan_event_event_id (event_id), UNIQUE KEY uk_mes_scan_event_bag (bag_code_id), KEY idx_mes_scan_event_status (status), KEY idx_mes_scan_event_error_code (error_code), KEY idx_mes_scan_event_received_time (received_time)
+) ENGINE=InnoDB COMMENT='MES琚嬬爜鎵爜鍏ュ簱浜嬩欢';
diff --git a/sql/mysql/mes_pro_scan_inbound_init.sql b/sql/mysql/mes_pro_scan_inbound_init.sql
new file mode 100644
index 0000000..7282738
--- /dev/null
+++ b/sql/mysql/mes_pro_scan_inbound_init.sql
@@ -0,0 +1,32 @@
+-- 鐢熶骇绾挎壂鐮佸叆搴撹彍鍗曚笌鎸夐挳鏉冮檺
+-- 鑿滃崟鎸傝浇鍒颁竴琚嬩竴鐮佺鐞嗭紙602630锛夛紝缁勪欢璺緞涓庡墠绔姩鎬佽矾鐢变竴鑷淬��
+UPDATE system_menu
+SET parent_id = 602630, sort = 7, updater = '1', update_time = NOW()
+WHERE id = 602650 AND deleted = 0;
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
+ status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+SELECT 602650, '鐢熶骇绾挎壂鐮佸叆搴�', '', 2, 7, 602630, 'scan-in', 'ep:camera', 'mes/pro/scan-in/index', 'MesProScanIn',
+ 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 602650 AND deleted = 0);
+
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
+ status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+SELECT 602651, '鎵爜鍏ュ簱鏌ヨ', 'mes:pro-scan-in:query', 3, 1, 602650, '', '', '', '',
+ 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 602651 AND deleted = 0);
+
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
+ status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+SELECT 602652, '鎵爜鍏ュ簱', 'mes:pro-scan-in:inbound', 3, 2, 602650, '', '', '', '',
+ 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 602652 AND deleted = 0);
+
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
+ status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+SELECT 602653, '鎵爜鍏ュ簱鐪嬫澘', 'mes:pro-scan-in:dashboard', 3, 3, 602650, '', '', '', '',
+ 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 602653 AND deleted = 0);
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java
new file mode 100644
index 0000000..4566021
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.scan;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventCreateReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventRespVO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventPageReqVO;
+import java.util.List;
+import java.util.Map;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan.MesProScanEventDO;
+import cn.iocoder.yudao.module.mes.service.pro.scan.MesProScanEventService;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.security.access.prepost.PreAuthorize;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+@Tag(name = "绠$悊鍚庡彴 - MES 鎵爜鍏ュ簱")
+@RestController @RequestMapping("/mes/pro/scan-in")
+public class MesProScanEventController {
+ @Resource private MesProScanEventService service;
+ @PostMapping("/inbound") @PreAuthorize("@ss.hasPermission('mes:pro-scan-in:inbound')") @Operation(summary = "琚嬬爜鎵爜鍏ュ簱")
+ public CommonResult<MesProScanEventRespVO> inbound(@Valid @RequestBody MesProScanEventCreateReqVO req) {
+ return success(BeanUtils.toBean(service.createInbound(req), MesProScanEventRespVO.class));
+ }
+ @GetMapping("/page") @PreAuthorize("@ss.hasPermission('mes:pro-scan-in:query')") @Operation(summary = "鎵爜浜嬩欢鍒嗛〉")
+ public CommonResult<PageResult<MesProScanEventRespVO>> page(@Valid MesProScanEventPageReqVO req) {
+ PageResult<MesProScanEventDO> page = service.getPage(req);
+ return success(new PageResult<>(BeanUtils.toBean(page.getList(), MesProScanEventRespVO.class), page.getTotal()));
+ }
+ @GetMapping("/latest") @PreAuthorize("@ss.hasPermission('mes:pro-scan-in:query')") @Operation(summary = "鏈�鏂版壂鐮佷簨浠�")
+ public CommonResult<MesProScanEventRespVO> latest(@RequestParam(required = false) String deviceCode) {
+ return success(BeanUtils.toBean(service.getLatest(deviceCode), MesProScanEventRespVO.class));
+ }
+ @GetMapping("/dashboard") @PreAuthorize("@ss.hasPermission('mes:pro-scan-in:dashboard')") @Operation(summary = "鎵爜鍏ュ簱鐪嬫澘")
+ public CommonResult<Map<String, Object>> dashboard() { return success(service.getDashboard()); }
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java
new file mode 100644
index 0000000..99ded7b
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.*;
+import lombok.Data;
+import java.math.BigDecimal;
+
+@Data
+public class MesProScanEventCreateReqVO {
+ @NotBlank private String eventId;
+ @NotBlank private String bagCode;
+ @NotNull @DecimalMin("0.000001") private BigDecimal quantity;
+ @NotBlank private String deviceCode;
+ private String lineCode;
+ @NotNull private Long locationId;
+ @NotNull private Long areaId;
+ private String remark;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventPageReqVO.java
new file mode 100644
index 0000000..52254e1
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventPageReqVO.java
@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MesProScanEventPageReqVO extends PageParam {
+ private String eventId;
+ private String bagCode;
+ private String batchCode;
+ private String deviceCode;
+ private String status;
+ private String errorCode;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java
new file mode 100644
index 0000000..18ca711
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+public class MesProScanEventRespVO {
+ private Long id;
+ private String eventId;
+ private String bagCode;
+ private String batchCode;
+ private BigDecimal quantity;
+ private String deviceCode;
+ private String lineCode;
+ private Long warehouseId;
+ private Long locationId;
+ private Long areaId;
+ private Long transactionId;
+ private String status;
+ private String errorCode;
+ private String errorMessage;
+ private LocalDateTime receivedTime;
+ private LocalDateTime failedTime;
+ private LocalDateTime eventTime;
+ private String remark;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java
new file mode 100644
index 0000000..d0be21d
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@TableName("mes_pro_scan_event")
+@KeySequence("mes_pro_scan_event_seq")
+@Data @EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor
+public class MesProScanEventDO extends BaseDO {
+ @TableId private Long id;
+ private String eventId;
+ private String bagCode;
+ private Long bagCodeId;
+ private Long batchId;
+ private String batchCode;
+ private Long itemId;
+ private BigDecimal quantity;
+ private String deviceCode;
+ private String lineCode;
+ private Long warehouseId;
+ private Long locationId;
+ private Long areaId;
+ private Long transactionId;
+ private String status;
+ private String errorCode;
+ private String errorMessage;
+ private LocalDateTime receivedTime;
+ private LocalDateTime failedTime;
+ private LocalDateTime eventTime;
+ private String remark;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/scan/MesProScanEventMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/scan/MesProScanEventMapper.java
new file mode 100644
index 0000000..523c172
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/scan/MesProScanEventMapper.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.mes.dal.mysql.pro.scan;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan.MesProScanEventDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventPageReqVO;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface MesProScanEventMapper extends BaseMapperX<MesProScanEventDO> {
+ default MesProScanEventDO selectByEventIdForUpdate(String eventId) {
+ return selectOne(new LambdaQueryWrapperX<MesProScanEventDO>()
+ .eq(MesProScanEventDO::getEventId, eventId).last("FOR UPDATE"));
+ }
+ default PageResult<MesProScanEventDO> selectPage(MesProScanEventPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX<MesProScanEventDO>()
+ .eqIfPresent(MesProScanEventDO::getEventId, reqVO.getEventId())
+ .likeIfPresent(MesProScanEventDO::getBagCode, reqVO.getBagCode())
+ .likeIfPresent(MesProScanEventDO::getBatchCode, reqVO.getBatchCode())
+ .eqIfPresent(MesProScanEventDO::getDeviceCode, reqVO.getDeviceCode())
+ .eqIfPresent(MesProScanEventDO::getStatus, reqVO.getStatus())
+ .eqIfPresent(MesProScanEventDO::getErrorCode, reqVO.getErrorCode())
+ .orderByDesc(MesProScanEventDO::getEventTime));
+ }
+
+ default MesProScanEventDO selectLatest(String deviceCode) {
+ return selectOne(new LambdaQueryWrapperX<MesProScanEventDO>()
+ .eqIfPresent(MesProScanEventDO::getDeviceCode, deviceCode)
+ .orderByDesc(MesProScanEventDO::getEventTime).last("LIMIT 1"));
+ }
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesProScanEventStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesProScanEventStatusEnum.java
new file mode 100644
index 0000000..656c392
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesProScanEventStatusEnum.java
@@ -0,0 +1,6 @@
+package cn.iocoder.yudao.module.mes.enums;
+
+public interface MesProScanEventStatusEnum {
+ String SUCCESS = "SUCCESS";
+ String FAILED = "FAILED";
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventService.java
new file mode 100644
index 0000000..d4b48fc
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventService.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.mes.service.pro.scan;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventCreateReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventPageReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan.MesProScanEventDO;
+import jakarta.validation.Valid;
+
+import java.util.Map;
+
+public interface MesProScanEventService {
+ MesProScanEventDO createInbound(@Valid MesProScanEventCreateReqVO reqVO);
+ MesProScanEventDO getByEventId(String eventId);
+ PageResult<MesProScanEventDO> getPage(MesProScanEventPageReqVO reqVO);
+ MesProScanEventDO getLatest(String deviceCode);
+ Map<String, Object> getDashboard();
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java
new file mode 100644
index 0000000..e2c841c
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java
@@ -0,0 +1,97 @@
+package cn.iocoder.yudao.module.mes.service.pro.scan;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventCreateReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventPageReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan.MesProScanEventDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.scan.MesProScanEventMapper;
+import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.MesProScanEventStatusEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
+import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
+import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+@Validated
+public class MesProScanEventServiceImpl implements MesProScanEventService {
+ @Resource private MesProScanEventMapper eventMapper;
+ @Resource private MesProBagCodeMapper bagCodeMapper;
+ @Resource private MesProBatchMapper batchMapper;
+ @Resource private MesWmTransactionService transactionService;
+ @Resource private MesWmWarehouseService warehouseService;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public MesProScanEventDO createInbound(MesProScanEventCreateReqVO req) {
+ MesProScanEventDO existing = eventMapper.selectByEventIdForUpdate(req.getEventId());
+ if (existing != null) {
+ if (!req.getBagCode().equals(existing.getBagCode()) || !req.getDeviceCode().equals(existing.getDeviceCode())) {
+ return failed(existing, "MES_SCAN_EVENT_ID_CONFLICT", "浜嬩欢鍙峰凡瀛樺湪浣嗗唴瀹逛笉涓�鑷�");
+ }
+ return existing;
+ }
+ MesProScanEventDO event = new MesProScanEventDO();
+ event.setEventId(req.getEventId()).setBagCode(req.getBagCode()).setQuantity(req.getQuantity())
+ .setDeviceCode(req.getDeviceCode()).setLineCode(req.getLineCode()).setRemark(req.getRemark())
+ .setStatus(MesProScanEventStatusEnum.FAILED).setReceivedTime(LocalDateTime.now());
+ eventMapper.insert(event);
+ MesProBagCodeDO bag = bagCodeMapper.selectByCodeForUpdate(req.getBagCode());
+ if (bag == null) return failed(event, "MES_SCAN_BAG_NOT_EXISTS", "琚嬬爜涓嶅瓨鍦�");
+ event.setBagCodeId(bag.getId());
+ if (bag.getInboundEventId() != null) return failed(event, "MES_SCAN_BAG_ALREADY_INBOUND", "琚嬬爜宸插叆搴�");
+ MesProBatchDO batch = batchMapper.selectByIdForUpdate(bag.getBatchId());
+ if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "鐢熶骇鎵规涓嶅瓨鍦�");
+ event.setBatchId(batch.getId()).setBatchCode(batch.getCode()).setItemId(batch.getItemId());
+ MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
+ if (warehouse == null) return failed(event, "MES_SCAN_WIP_WAREHOUSE_NOT_EXISTS", "WIP铏氭嫙浠撲笉瀛樺湪");
+ event.setWarehouseId(warehouse.getId()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId());
+ try {
+ Long txId = transactionService.createTransaction(new MesWmTransactionSaveReqDTO()
+ .setType(MesWmTransactionTypeEnum.IN.getType()).setBizType(MesBizTypeConstants.WM_PRODUCT_PRODUCE)
+ .setBizId(batch.getId()).setBizCode(batch.getCode()).setBizLineId(bag.getId())
+ .setItemId(batch.getItemId()).setQuantity(req.getQuantity()).setBatchCode(batch.getCode())
+ .setWarehouseId(warehouse.getId()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId())
+ .setReceiptTime(LocalDateTime.now()).setCheckFlag(true));
+ event.setTransactionId(txId).setStatus(MesProScanEventStatusEnum.SUCCESS).setEventTime(LocalDateTime.now());
+ eventMapper.updateById(event);
+ bagCodeMapper.markInbound(bag.getId(), event.getId(), req.getDeviceCode(), req.getLineCode());
+ batchMapper.markInbound(batch.getId(), event.getId(), req.getDeviceCode(), req.getLineCode());
+ return event;
+ } catch (Exception ex) {
+ return failed(event, "MES_SCAN_TRANSACTION_FAILED", "搴撳瓨浜嬪姟澶勭悊澶辫触");
+ }
+ }
+
+ private MesProScanEventDO failed(MesProScanEventDO event, String code, String message) {
+ event.setStatus(MesProScanEventStatusEnum.FAILED).setErrorCode(code).setErrorMessage(message)
+ .setFailedTime(LocalDateTime.now()).setEventTime(LocalDateTime.now());
+ eventMapper.updateById(event);
+ return event;
+ }
+
+ @Override public MesProScanEventDO getByEventId(String eventId) { return eventMapper.selectOne(MesProScanEventDO::getEventId, eventId); }
+ @Override public PageResult<MesProScanEventDO> getPage(MesProScanEventPageReqVO reqVO) { return eventMapper.selectPage(reqVO); }
+ @Override public MesProScanEventDO getLatest(String deviceCode) { return eventMapper.selectLatest(deviceCode); }
+ @Override public Map<String, Object> getDashboard() {
+ Map<String, Object> result = new HashMap<>();
+ result.put("latest", eventMapper.selectLatest(null));
+ result.put("total", eventMapper.selectCount(null));
+ result.put("success", eventMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesProScanEventDO>().eq(MesProScanEventDO::getStatus, MesProScanEventStatusEnum.SUCCESS)));
+ result.put("failed", eventMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesProScanEventDO>().eq(MesProScanEventDO::getStatus, MesProScanEventStatusEnum.FAILED)));
+ return result;
+ }
+}
--
Gitblit v1.9.3