feat(mes): 新增生产线扫码入库功能
- 添加扫码入库菜单权限和数据库表结构
- 实现袋码扫码入库的完整业务流程
- 集成WIP虚拟仓库存和库存事务处理
- 提供扫码事件查询和工作台看板功能
- 实现幂等性检查和重复入库防护机制
- 完善错误处理和状态追踪功能
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ç产线æ«ç å
¥åº - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - ç产线æ«ç å
¥åºèªå¨å·¥ä½å° |
| | | - æ«ç äºä»¶å®¡è®¡ |
| | | - çäº§æ¹æ¬¡åè¢ç 详æ
|
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 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 | æ¥è¯¢å·¥ä½å°çæ¿ | |
| | | |
| | | |
| | | - è¾¹ç¼ç¨åºä¸å¾ä¸ä¼ å¾çæè§é¢ï¼æ¬æ¥å£åªæ¥æ¶è§£ç åçäºç»´ç ç»æã |
| | | - å端工ä½å°åºå¨é¡µé¢å¸è½½æ¶åæ¢å·æ°ä»»å¡ã |
| | | - å
¥åºå¤±è´¥æ¶å±ç¤ºå端è¿åçä¸å¡é误ï¼ä¸èªå¨è°ç¨éç¨åºåè°æ´æ¥å£éè¯ã |
| | | - WIP åºåºååºä½éæåé
ç½®ï¼æ¥å£ä¸æ¥å客æ·ç«¯æå®ä»åºç¼å·ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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è¢ç æ«ç å
¥åºäºä»¶'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ç产线æ«ç å
¥åºèå䏿鮿é |
| | | -- èåæè½½å°ä¸è¢ä¸ç 管çï¼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); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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()); } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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")); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.enums; |
| | | |
| | | public interface MesProScanEventStatusEnum { |
| | | String SUCCESS = "SUCCESS"; |
| | | String FAILED = "FAILED"; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |