# WMS-QMS 质检集成需求与方案文档 ## 文档版本历史 | 版本 | 日期 | 修订内容 | 作者 | |-----|------|---------|------| | 1.0 | 2026-01-15 | 初始版本,完整需求与方案设计 | zch | | 1.1 | 2026-01-15 | 新增独立文件实现,添加代码实现章节 | zch | | 1.2 | 2026-01-15 | 修复Code Review问题:创建独立 Dubbo 实现类、修复状态映射、添加批次号过滤 | zch | --- ## 目录 - [1. 需求概述](#1-需求概述) - [2. 业务流程](#2-业务流程) - [3. 数据模型](#3-数据模型) - [4. 接口设计](#4-接口设计) - [5. 新增文件清单](#5-新增文件清单) - [6. 技术实现方案](#6-技术实现方案) - [7. 前端改造](#7-前端改造) - [8. 分布式事务处理](#8-分布式事务处理) - [9. 让步接收方案(待讨论)](#9-让步接收方案待讨论) --- ## 1. 需求概述 ### 1.1 背景 WMS(仓储管理系统)与 QMS(质量管理系统)需要实现质检业务闭环,支持以下场景: 1. **WMS 主动发起质检**:通过前端界面手动选择批次,调用 QMS 创建质检任务 2. **QMS 质检完成回调**:质检完成后通知 WMS,更新批次质检状态 3. **PDA 扫码入库**:根据质检状态,PDA 扫码完成实际入库操作 4. **让步接收闭环**:不合格品让步接收后,支持 PDA 扫码入库 ### 1.2 核心需求 | 需求点 | 说明 | 优先级 | |-------|------|--------| | 手动发起质检 | 打印标签后不自动创建质检任务,通过前端界面手动选择批次发起 | 高 | | 质检完成通知 | QMS 质检完成后回调 WMS,更新批次质检状态 | 高 | | 合格数量入库 | 质检合格的批次,PDA 扫码完成入库 | 高 | | 让步接收入库 | 不合格品让步接收后,PDA 扫码完成入库 | 中 | | 批次追溯 | 全流程保持批次号一致性,确保可追溯 | 高 | ### 1.3 核心数据表 - 质检qms模块通用模块版本只关注qc_inspection_item、qc_inspection_item_category、qc_inspection_main、qc_inspection_main_file_relation、qc_inspection_result、qc_inspection_template、qc_template_item、qc_unqualified_record、qc_unqualified_review! - hwmom-wms核心关注base_measurement_unit_info、base_supplier_info1、base_material_category、base_material_type、base_material_info_copy1 / base_material_info、wms_base_customer、wms_base_area、wms_base_location、wms_base_warehouse、wms_allocate_order、wms_allocate_order_detail、wms_allocate_task、wms_check_task、wms_purchase_order、wms_purchase_order_detail、wms_instock_detail、wms_instock_order、wms_instock_print、wms_instock_record、wms_inventory、wms_inventory_check、wms_inventory_check_record、wms_outstock_detail、wms_outstock_order、wms_outstock_record! --- ## 2. 业务流程 ### 2.1 整体业务流程图 ``` ┌─────────────────────────────────────────────────────────────────┐ │ WMS-QMS 质检闭环完整流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 【步骤1】打印标签 │ │ ├─ 生成 wms_instock_print 记录 │ │ ├─ inspection_type='0'(未发起) │ │ └─ inspection_request='0/1'(必检/免检) │ │ │ │ 【步骤2】前端发起质检(新功能) │ │ ├─ qc.vue 界面选择批次 │ │ ├─ 过滤:inspection_request='0'且 inspection_type='0' │ │ ├─ 点击"发起质检"按钮 │ │ └─ POST /wms/instockPrint/createInspection │ │ │ │ 【步骤3】WMS 调用 QMS 创建质检任务 │ │ ├─ @GlobalTransactional(调用方加注解) │ │ ├─ remoteQmsInspectionService.createInspectionTaskForWMS() │ │ └─ 更新打印记录:inspection_type='1'(质检中) │ │ │ │ 【步骤4】PDA 质检执行 │ │ ├─ 质检员填写检测结果 │ │ ├─ 保存到 qc_inspection_result │ │ └─ 更新 qc_inspection_main:status='1', result='0/1' │ │ qualifiedQty=?, unqualifiedQty=? │ │ │ │ 【步骤5】QMS 回调 WMS(质检完成) │ │ ├─ @GlobalTransactional(调用方加注解) │ │ ├─ remoteWmsInspectionService.notifyInspectionComplete() │ │ ├─ 传递批次号和合格/不合格数量 │ │ └─ 只更新质检状态,❌ 不创建入库记录 │ │ ├─ 合格:inspection_type='2' │ │ └─ 不合格:inspection_type='3' │ │ │ │ 【步骤6】PDA 扫码入库(合格批次) │ │ ├─ POST /pda/instock/submit │ │ ├─ 过滤:inspection_type='2'(合格) │ │ ├─ 创建 wms_instock_record 记录 │ │ ├─ 更新库存 wms_inventory │ │ └─ 更新打印记录:inbound_status='1'(已入库) │ │ │ │ 【步骤7】让步接收(待讨论) │ │ ├─ 不合格品评审 → 让步接收 │ │ ├─ QMS 回调 WMS(与步骤5类似) │ │ ├─ 更新质检状态:inspection_type='2'(合格) │ │ └─ PDA 扫码入库(与步骤6类似) │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.2 状态流转说明 #### inspection_type(质检状态)字段 | 值 | 说明 | 触发条件 | 后续操作 | |---|------|---------|---------| | 0 | 未发起 | 打印标签后默认 | 前端发起质检 | | 1 | 质检中 | WMS 调用 QMS 创建质检任务后 | PDA 质检执行 | | 2 | 合格 | QMS 回调 WMS 更新状态后 | PDA 扫码入库 | | 3 | 不合格 | QMS 回调 WMS 更新状态后 | 不合格品评审 | #### inbound_status(入库状态)字段 | 值 | 说明 | 触发条件 | |---|------|---------| | 0 | 待入库 | 打印标签后默认 | | 1 | 已入库 | PDA 扫码入库成功 | | 2 | 入库中 | 预留,当前未使用 | | 3 | 拒收 | 预留,当前未使用 | --- ## 3. 数据模型 ### 3.1 核心数据表 #### wms_instock_print(入库打印记录表) | 字段 | 类型 | 说明 | 示例 | |-----|------|------|------| | instock_print_id | bigint | 主键 | 123 | | instock_code | varchar(64) | 入库单号 | RK202501150001 | | batch_code | varchar(64) | 批次号 | BATCH001 | | material_code | varchar(64) | 物料编码 | MAT001 | | material_name | varchar(128) | 物料名称 | 橡胶 | | apportion_qty | decimal(16,2) | 条码数量(分包数量) | 100.00 | | inspection_request | char(1) | 质检要求(0=必检,1=免检) | 0 | | inspection_type | char(1) | 质检状态(0未发起,1质检中,2合格,3不合格) | 1 | | inbound_status | char(1) | 入库状态(0待入库,1已入库) | 0 | | actual_inbound_time | datetime | 实际入库时间 | 2026-01-15 10:00:00 | #### wms_instock_record(入库记录表) | 字段 | 类型 | 说明 | 示例 | |-----|------|------|------| | instock_record_id | bigint | 主键 | 456 | | instock_code | varchar(64) | 入库单号 | RK202501150001 | | batch_code | varchar(64) | 批次号 | BATCH001 | | material_code | varchar(64) | 物料编码 | MAT001 | | instock_qty | decimal(16,2) | 入库数量 | 100.00 | | instock_time | datetime | 入库时间 | 2026-01-15 10:00:00 | #### wms_inventory(库存表) | 字段 | 类型 | 说明 | |-----|------|------| | inventory_id | bigint | 主键 | | batch_code | varchar(64) | 批次号 | | material_id | bigint | 物料ID | | store_id | bigint | 仓库ID | | location_code | varchar(64) | 库位编码 | | inventory_qty | decimal(16,2) | 库存数量 | | lock_state | char(1) | 锁定状态(0未锁定,1锁定) | | inventory_status | char(1) | 库存状态(0归零,1正常) | #### qc_inspection_main(质检主表) | 字段 | 类型 | 说明 | 关联关系 | |-----|------|------|---------| | inspection_id | bigint | 质检主键 | - | | inspection_no | varchar(64) | 质检单号 | - | | template_id | bigint | 模板ID | - | | material_code | varchar(64) | 物料编码 | 关联 wms_instock_print | | inspection_qty | decimal(16,2) | 质检数量 | - | | qualified_qty | decimal(16,2) | 合格数量 | - | | unqualified_qty | decimal(16,2) | 不合格数量 | - | | result | char(1) | 质检结果(0合格,1不合格) | - | | status | char(1) | 单据状态(0未处理,1完成) | - | | batch_no | varchar(64) | 批次号 | 关联 wms_instock_print.batch_code | | production_order | varchar(64) | 生产订单号/业务来源单号 | 可存储入库单号 | | remark | varchar(512) | 备注 | 可存储入库单号 | --- ## 4. 接口设计 ### 4.1 WMS 发起质检接口 #### 接口定义 ```java /** * 批量发起质检任务 * 前端选择批次后调用此接口创建质检任务 * * @param prints 选中的打印记录列表 * @return Map<批次号, 质检单号或错误信息> */ @PostMapping("/createInspection") public R> createInspection(@RequestBody List prints) ``` #### 请求参数 ```json [ { "instockPrintId": 123, "instockCode": "RK202501150001", "batchCode": "BATCH001", "materialCode": "MAT001", "materialName": "橡胶", "apportionQty": 100.00, "inspectionRequest": "0" }, { "instockPrintId": 124, "instockCode": "RK202501150001", "batchCode": "BATCH002", "materialCode": "MAT001", "materialName": "橡胶", "apportionQty": 50.00, "inspectionRequest": "0" } ] ``` #### 响应结果 ```json { "code": 200, "msg": "操作成功", "data": { "BATCH001": "QC202501150001", "BATCH002": "QC202501150002" } } ``` ### 4.2 QMS 质检完成回调接口(WMS 暴露给 QMS) #### Dubbo 接口定义 ```java /** * WMS 暴露给 QMS 的质检完成回调接口 * QMS 质检完成后调用此接口通知 WMS */ public interface RemoteWmsInspectionService { /** * 质检完成回调 * @param notification 质检完成通知 */ void notifyInspectionComplete(InspectionCompleteNotification notification); } ``` #### 回调参数 ```java public class InspectionCompleteNotification implements Serializable { private String inspectionNo; // 质检单号(必填) private String instockCode; // 入库单号(必填) private String batchCode; // 批次号(必填,精确匹配) private String result; // 质检结果:0=合格,1=不合格(必填) private String status; // 质检状态:0=未处理,1=已完成(必填) private BigDecimal qualifiedQty; // 合格数量(必填) private BigDecimal unqualifiedQty; // 不合格数量(必填) private String materialCode; // 物料编码(可选) private String inspectionEndTime; // 质检完成时间(可选) } ``` --- ## 5. 新增文件清单 ### 5.1 设计原则 **核心原则**:独立新建Java文件,不修改现有文件,便于查看和调试 1. **模块化设计**:将WMS-QMS集成相关的业务逻辑抽离到独立的服务类 2. **复用现有服务**:新文件可调用现有服务方法,避免重复代码 3. **便于维护**:独立文件便于定位问题、调试和后续扩展 4. **职责清晰**:每个文件职责单一,符合SOLID原则 ### 5.2 WMS模块新增文件 | 文件路径 | 类型 | 说明 | |---------|------|------| | `service/IWmsInspectionInitiationService.java` | 接口 | WMS发起质检服务接口 | | `service/impl/WmsInspectionInitiationServiceImpl.java` | 实现 | WMS发起质检服务实现 | | `service/IWmsInspectionCallbackService.java` | 接口 | WMS接收QMS回调服务接口 | | `service/impl/WmsInspectionCallbackServiceImpl.java` | 实现 | WMS接收QMS回调服务实现(本地服务) | | `dubbo/RemoteWmsInspectionCallbackServiceImpl.java` | 实现 | WMS Dubbo服务实现(实现RemoteWmsInstockService) | | `controller/api/apiController.java` | 修改 | REST API回调接口(添加@Transactional、批次号过滤、状态映射) | | `controller/WmsInstockPrintController.java` | 修改 | 添加`createInspection`端点 | ### 5.3 QMS模块新增文件 | 文件路径 | 类型 | 说明 | |---------|------|------| | `service/IQcWmsCallbackService.java` | 接口 | QMS回调WMS服务接口 | | `service/impl/QcWmsCallbackServiceImpl.java` | 实现 | QMS回调WMS服务实现 | ### 5.4 API模块修改文件 | 文件路径 | 类型 | 说明 | |---------|------|------| | `hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java` | 修改 | 添加`batchCode`字段 | ### 5.5 文件依赖关系图 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 新增文件依赖关系 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 【WMS模块】 │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ WmsInstockPrintController │ │ │ │ └── @PostMapping("/createInspection") │ │ │ │ └── IWmsInspectionInitiationService │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ WmsInspectionInitiationServiceImpl │ │ │ │ ├── @DubboReference RemoteQmsInspectionService │ │ │ │ └── 复用 IWmsInstockPrintService │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ │ │ 【调用QMS Dubbo】 │ │ │ │ 【QMS模块】 │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ QcPDAServiceImpl (现有文件,复用) │ │ │ │ └── submitInspection() │ │ │ │ └── 调用 IQcWmsCallbackService.notifyWmsForQualified│ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ QcWmsCallbackServiceImpl (新增) │ │ │ │ ├── @DubboReference RemoteWmsInspectionService │ │ │ │ └── 复用 IQcInspectionMainService │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ │ │ 【回调WMS Dubbo】 │ │ │ │ 【WMS模块】 │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ WmsInspectionCallbackServiceImpl (@DubboService) │ │ │ │ └── handleInspectionComplete() │ │ │ │ └── 复用 IWmsInstockPrintService.updateByBo() │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 5.6 关键代码示例 #### WMS发起质检服务实现 ```java // WmsInspectionInitiationServiceImpl.java @Service public class WmsInspectionInitiationServiceImpl implements IWmsInspectionInitiationService { @DubboReference(timeout = 300000) private RemoteQmsInspectionService remoteQmsInspectionService; private final IWmsInstockPrintService instockPrintService; @Override public Map createInspection(List prints) { Map result = new LinkedHashMap<>(); for (WmsInstockPrintBo print : prints) { // 1. 校验是否可以发起质检 String reason = getCannotInitiateReason(print); if (reason != null) { result.put(print.getBatchCode(), "跳过: " + reason); continue; } // 2. 调用 QMS Dubbo 接口 String inspectionNo = remoteQmsInspectionService.createInspectionTaskForWMS(request); // 3. 更新本地状态 updateInspectionType(print.getInstockPrintId(), "1"); result.put(print.getBatchCode(), inspectionNo); } return result; } } ``` #### QMS回调WMS服务实现 ```java // QcWmsCallbackServiceImpl.java @Service public class QcWmsCallbackServiceImpl implements IQcWmsCallbackService { @DubboReference(timeout = 300000) private RemoteWmsInstockService remoteWmsInstockService; private final IQcInspectionMainService qcInspectionMainService; @Override public boolean notifyWmsForQualified(QcInspectionMainVo main) { // 1. 检查是否来自WMS入库 if (!isFromWmsInspection(main)) { return false; } // 2. 构建通知参数 InspectionCompleteNotification notification = buildNotification(main, null, false); // 3. 调用 WMS Dubbo 接口 R result = remoteWmsInstockService.completeInstockAfterInspection(notification); return result != null && result.getCode() == R.SUCCESS; } } ``` #### WMS接收QMS回调服务实现(本地服务) ```java // WmsInspectionCallbackServiceImpl.java @Service public class WmsInspectionCallbackServiceImpl implements IWmsInspectionCallbackService { private final IWmsInstockPrintService instockPrintService; @Override public boolean handleInspectionComplete(InspectionCompleteNotification notification) { // 1. 精确查询打印记录(instockCode + batchCode) WmsInstockPrintVo printVo = queryByInstockAndBatch( notification.getInstockCode(), notification.getBatchCode() ); // 2. 根据质检结果更新状态(inspectionType: 2=合格, 3=不合格) if ("0".equals(notification.getResult())) { return updateToQualified(notification.getInstockCode(), notification.getBatchCode()); } else { return updateToUnqualified(notification.getInstockCode(), notification.getBatchCode()); } } } ``` #### WMS Dubbo服务实现(独立类) ```java // RemoteWmsInspectionCallbackServiceImpl.java @Service @DubboService public class RemoteWmsInspectionCallbackServiceImpl implements RemoteWmsInstockService { private final IWmsInspectionCallbackService wmsInspectionCallbackService; @Override public R completeInstockAfterInspection(InspectionCompleteNotification notification) { // 委托给本地服务处理 boolean success = wmsInspectionCallbackService.handleInspectionComplete(notification); return success ? R.ok("质检回调处理成功") : R.fail("质检回调处理失败"); } } ``` --- ## 6. 技术实现方案 ### 6.1 模块依赖关系 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 模块依赖关系 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ WMS │────────>│ QMS │<────────│ PDA │ │ │ │ 模块 │ Dubbo │ 模块 │ Dubbo │ 扫码入库 │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ ▲ ▲ │ │ │ HTTP │ │ │ ┌────┴────┐ ┌────┴────┐ │ │ │前端 qc.vue│ │ PDA设备 │ │ │ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 6.2 技术栈 | 技术点 | 使用方式 | 说明 | |-------|---------|------| | 微服务框架 | Spring Cloud Alibaba + Dubbo 3.x | 服务间 RPC 调用 | | 分布式事务 | Seata @GlobalTransactional | 跨服务数据一致性 | | 认证授权 | Sa-Token | 用户身份验证 | | 多租户 | TenantHelper | 租户隔离 | ### 6.3 核心代码结构 #### WMS 模块 ``` hwmom/ruoyi-modules/hwmom-wms/ ├── controller/ │ └── api/ │ ├── WmsPdaApiController.java # PDA 入库接口 │ └── WmsInstockPrintController.java # 打印记录管理接口 ├── service/ │ ├── IWmsInstockPrintService.java # 打印记录服务接口 │ └── impl/ │ └── WmsInstockPrintServiceImpl.java # 打印记录服务实现 └── dubbo/ └── RemoteQmsInspectionService.java # QMS Dubbo 接口引用 ``` #### QMS 模块 ``` hwmom/ruoyi-modules/hwmom-qms/ ├── controller/ │ └── QcPDAController.java # PDA 质检接口 ├── service/ │ ├── IQcPDAService.java # PDA 质检服务接口 │ ├── IQcWmsService.java # WMS 仓储质检服务接口 │ └── impl/ │ ├── QcPDAServiceImpl.java # PDA 质检服务实现 │ └── QcWmsServiceImpl.java # WMS 仓储质检服务实现 └── dubbo/ └── RemoteQmsInspectionServiceImpl.java # QMS Dubbo 服务实现 ``` --- ## 7. 前端改造 ### 7.1 界面文件 **文件路径**:`hwmom-ui/src/views/wms/instockPrint/qc.vue` ### 7.2 新增按钮 ```vue 发起质检 ``` ### 7.3 按钮点击处理 ```typescript /** 发起质检按钮操作 */ const handleCreateInspection = async () => { // 1. 过滤:只处理必检(inspectionRequest='0')且未发起(inspectionType='0'或null)的批次 const validRecords = vos.value.filter(item => item.inspectionRequest === '0' && (!item.inspectionType || item.inspectionType === '0') ); if (validRecords.length === 0) { proxy?.$modal.msgWarning("请选择需要发起质检的批次(必检且未发起)"); return; } // 2. 确认提示 await proxy?.$modal.confirm( `确认为选中的 ${validRecords.length} 个批次发起质检吗?` ); // 3. 调用接口 loading.value = true; try { const res = await createInspection(validRecords); proxy?.$modal.msgSuccess("质检任务创建成功"); await getList(); } catch (error) { console.error("发起质检失败", error); } finally { loading.value = false; } }; ``` ### 7.4 API 定义 ```typescript // api/wms/instockPrint/index.ts /** * 批量发起质检任务 * @param prints 选中的打印记录列表 */ export const createInspection = (prints: InstockPrintVO[]) => { return request({ url: '/wms/instockPrint/createInspection', method: 'post', data: prints }); }; ``` --- ## 8. 分布式事务处理 ### 8.1 Dubbo 调用事务规范 **重要原则**:调用方(发起方)需要添加 `@GlobalTransactional` 注解 #### 场景1:WMS 调用 QMS 创建质检任务 ```java // WMS 模块 @Service public class WmsInstockPrintServiceImpl { @DubboReference private RemoteQmsInspectionService remoteQmsInspectionService; @Override @GlobalTransactional(rollbackFor = Exception.class) // ← 调用方加注解 public Map createInspection(List prints) { Map result = new LinkedHashMap<>(); for (WmsInstockPrintBo print : prints) { try { // 1. 构建请求参数 WmsInspectionTaskRequest request = new WmsInspectionTaskRequest(); request.setInstockCode(print.getInstockCode()); request.setMaterialCode(print.getMaterialCode()); request.setInspectionQty(print.getApportionQty()); request.setBatchCode(print.getBatchCode()); request.setInspectionType("7"); // 入库检 // 2. 调用 QMS Dubbo 接口(远程事务) String inspectionNo = remoteQmsInspectionService.createInspectionTaskForWMS(request); log.info("批次 {} 质检任务创建成功,质检单号: {}", print.getBatchCode(), inspectionNo); // 3. 更新本地状态(只有远程调用成功才更新) WmsInstockPrintBo updateBo = new WmsInstockPrintBo(); updateBo.setInstockPrintId(print.getInstockPrintId()); updateBo.setInspectionType("1"); // 质检中 instockPrintService.updateByBo(updateBo); result.put(print.getBatchCode(), inspectionNo); } catch (Exception e) { log.error("批次 {} 创建质检任务失败: {}", print.getBatchCode(), e.getMessage(), e); result.put(print.getBatchCode(), "失败: " + e.getMessage()); // 全局事务会自动回滚 throw new ServiceException("创建质检任务失败: " + e.getMessage()); } } return result; } } ``` #### 场景2:QMS 调用 WMS 质检完成回调 ```java // QMS 模块 @Service public class QcPDAServiceImpl { @DubboReference private RemoteWmsInspectionService remoteWmsInspectionService; /** * 质检完成后的回调方法(抽离独立方法) */ private void notifyWmsForQualified(QcInspectionMainVo main) { // 如果没有合格数量,不回调 if (main.getQualifiedQty().compareTo(BigDecimal.ZERO) <= 0) { log.info("质检单 {} 无合格数量,跳过回调", main.getInspectionNo()); return; } try { // 1. 构建回调参数 InspectionCompleteNotification notification = buildNotification(main); // 2. 调用 WMS Dubbo 接口(远程事务) remoteWmsInspectionService.notifyInspectionComplete(notification); log.info("质检单 {} 回调 WMS 成功", main.getInspectionNo()); } catch (Exception e) { log.error("质检单 {} 回调 WMS 失败: {}", main.getInspectionNo(), e.getMessage(), e); // 根据业务需求决定是否抛出异常 // 如果不影响主流程,可以只记录日志 } } /** * 构建回调通知参数 */ private InspectionCompleteNotification buildNotification(QcInspectionMainVo main) { InspectionCompleteNotification notification = new InspectionCompleteNotification(); notification.setInspectionNo(main.getInspectionNo()); notification.setInstockCode(main.getRemark()); // remark 字段存储入库单号 notification.setBatchCode(main.getBatchNo()); // 批次号 notification.setQualifiedQty(main.getQualifiedQty()); notification.setUnqualifiedQty(main.getUnqualifiedQty()); notification.setMaterialCode(main.getMaterialCode()); notification.setResult("0"); // 合格 notification.setStatus("1"); // 已完成 notification.setInspectionEndTime(new Date()); return notification; } } ``` ### 7.2 事务回滚场景 | 场景 | 回滚范围 | 说明 | |-----|---------|------| | QMS 创建质检任务失败 | WMS 本地状态不更新 | `@GlobalTransactional` 保证 | | WMS 接收回调失败 | 不影响 QMS 主流程 | 捕获异常,记录日志 | | 网络超时 | Seata 自动重试 | 配置超时时间和重试次数 | ### 7.3 Seata 配置 ```yaml # application.yml seata: enabled: true application-id: hwmom-wms tx-service-group: default_tx_group registry: type: nacos nacos: server-addr: 127.0.0.1:8848 namespace: public group: SEATA_GROUP username: nacos password: nacos config: type: nacos nacos: server-addr: 127.0.0.1:8848 namespace: public group: SEATA_GROUP username: nacos password: nacos ``` --- ## 9. 让步接收方案(待讨论) ### 9.1 业务场景 ``` 批次 BATCH001,总数100 ├─ 质检完成:80合格 + 20不合格 │ ├─ 合格80 → QMS 回调 WMS → inspection_type='2' │ └─ 不合格20 → 创建不合格品评审 │ └─ 不合格品评审 → 让步接收(reviewResult='4') └─ QMS 回调 WMS → inspection_type='2'(让步接收视为合格) └─ PDA 扫码入库 20 件 ``` ### 9.2 技术方案(待讨论) #### 方案A:让步接收时单独回调(推荐) ```java // QMS 模块 - 不合格品评审服务 @Service public class QcUnqualifiedReviewServiceImpl { @Override @GlobalTransactional(rollbackFor = Exception.class) public Boolean completeTask(QcUnqualifiedReviewBo bo) { QcUnqualifiedReview review = baseMapper.selectById(bo.getReviewId()); // 1. 完成工作流审批 // ... 现有逻辑 ... // 2. 【新增】让步接收回调 WMS if ("4".equals(review.getReviewResult())) { notifyWmsForConcession(review); } return baseMapper.updateById(review) > 0; } /** * 让步接收完成后回调 WMS(单独抽离的方法) * 后续可改为其他实现方式(REST API / 消息队列) */ private void notifyWmsForConcession(QcUnqualifiedReview review) { // 从质检单号获取质检主表信息 QcInspectionMainVo inspectionMain = qcInspectionMainService.queryByInspectionNo(review.getInspectionNo()); // 构建回调参数 InspectionCompleteNotification notification = new InspectionCompleteNotification(); notification.setInspectionNo(review.getInspectionNo()); notification.setInstockCode(inspectionMain.getRemark()); // remark 存储入库单号 notification.setBatchCode(review.getBatchNo()); // 批次号 notification.setQualifiedQty(review.getInspectionQty()); // 让步接收数量 notification.setUnqualifiedQty(BigDecimal.ZERO); notification.setResult("0"); // 让步接收视为合格 notification.setStatus("1"); // 已完成 // 调用 WMS Dubbo 接口 remoteWmsInspectionService.notifyInspectionComplete(notification); log.info("让步接收回调 WMS 成功,批次号: {}, 数量: {}", review.getBatchNo(), review.getInspectionQty()); } } ``` #### 方案B:与合格数量回调合并 将让步接收也调用 `notifyWmsForQualified()` 方法,统一处理。 ### 9.3 数据一致性保证 | 场景 | 合格数量 | 让步接收数量 | 累计入库 | 最终状态 | |-----|---------|-------------|---------|---------| | 场景1 | 80 | 20 | 100 | 已入库 | | 场景2 | 100 | 0 | 100 | 已入库 | | 场景3 | 0 | 100 | 100 | 已入库 | --- ## 10. 附录 ### 10.1 字典值定义 #### wms_inspection_type(质检状态) | 字典值 | 字典标签 | 说明 | |-------|---------|------| | 0 | 未发起 | 打印后默认状态 | | 1 | 质检中 | 已创建质检任务,等待执行 | | 2 | 合格 | 质检合格 | | 3 | 不合格 | 质检不合格 | #### inspection_request(质检要求) | 字典值 | 字典标签 | 说明 | |-------|---------|------| | 0 | 必检 | 需要质检 | | 1 | 免检 | 不需要质检 | #### wms_inbound_status(入库状态) | 字典值 | 字典标签 | 说明 | |-------|---------|------| | 0 | 待入库 | 初始状态 | | 1 | 已入库 | PDA 扫码入库成功 | | 2 | 入库中 | 预留,当前未使用 | | 3 | 拒收 | 预留,当前未使用 | ### 10.2 关键文件路径清单 | 模块 | 文件路径 | 说明 | |-----|---------|------| | **WMS** | `hwmom/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/api/WmsInstockPrintController.java` | 打印记录控制器 | | **WMS** | `hwmom/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/IWmsInstockPrintService.java` | 打印记录服务接口 | | **WMS** | `hwmom/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsInstockPrintServiceImpl.java` | 打印记录服务实现 | | **WMS** | `hwmom/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/dubbo/RemoteWmsInspectionService.java` | WMS 暴露给 QMS 的接口(新增) | | **QMS** | `hwmom/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/RemoteQmsInspectionService.java` | QMS Dubbo 接口定义 | | **QMS** | `hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/dubbo/RemoteQmsInspectionServiceImpl.java` | QMS Dubbo 服务实现 | | **QMS** | `hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcWmsService.java` | WMS 仓储质检服务接口 | | **QMS** | `hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsServiceImpl.java` | WMS 仓储质检服务实现 | | **QMS** | `hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcPDAServiceImpl.java` | PDA 质检服务实现 | | **API** | `hwmom/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java` | 质检完成通知 DTO | | **API** | `hwmom/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/WmsInspectionTaskRequest.java` | WMS 入库质检请求 DTO | | **前端** | `hwmom-ui/src/views/wms/instockPrint/qc.vue` | 质检入库任务界面 | | **前端** | `hwmom-ui/src/api/wms/instockPrint/index.ts` | 打印记录前端 API | --- ## 11. 待确认事项 1. ~~批次号传递方式~~ ✓ 已确认:必须传递 `batchCode` 2. ~~入库时机~~ ✓ 已确认:由 PDA 扫码入库,回调只更新状态 3. ~~分布式事务~~ ✓ 已确认:调用方加 `@GlobalTransactional` 4. 让步接收具体实现方式(待讨论) 5. 是否需要新增 WMS 暴露给 QMS 的 Dubbo 接口(待确认) --- **文档结束**