From f51c040165f21d362f6b721f4a5a275523eb0f8c Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Fri, 16 Jan 2026 09:24:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(wms):=20=E6=B7=BB=E5=8A=A0=E8=B4=A8?= =?UTF-8?q?=E6=A3=80=E5=AE=8C=E6=88=90=E5=9B=9E=E8=B0=83=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=B9=B6=E9=87=8D=E6=9E=84QMS=E5=9B=9E=E8=B0=83=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增notifyInspectionComplete接口处理质检完成通知 - 实现质检合格自动完成入库和不合格标记拒收功能 - 添加入库打印记录质检状态更新逻辑 - 重构QMS回调WMS逻辑支持让步接收场景 - 更新检测方法字段映射和文件上传空指针处理 - 完善让步接收评审后的状态流转规则 - 添加批次号过滤确保精确更新对应记录 --- .../qms/controller/QcPDAController.java | 2 +- .../qms/domain/vo/QcInspectionResultVo.java | 15 +- .../qms/service/impl/QcPDAServiceImpl.java | 18 +- .../impl/QcUnqualifiedReviewServiceImpl.java | 77 +- .../impl/QcWmsCallbackServiceImpl.java | 6 +- .../wms/controller/api/apiController.java | 124 +++ ruoyi-modules/hwmom-wms/wms-qms.md | 896 ++++++++++++++++-- 7 files changed, 1062 insertions(+), 76 deletions(-) diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcPDAController.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcPDAController.java index 93005b73..84fe6879 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcPDAController.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/controller/QcPDAController.java @@ -163,7 +163,7 @@ public class QcPDAController { public R submitInspection(String json, List checkItemFiles) { QcInspectionMainBo bo= JSONObject.parseObject(json, QcInspectionMainBo.class); // 处理文件上传 - if(!checkItemFiles.isEmpty()){ + if(checkItemFiles != null &&!checkItemFiles.isEmpty()){ List ossIds = handleFileUploads(checkItemFiles); bo.setOssIds(ossIds); } diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionResultVo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionResultVo.java index ec2d49a3..2cf15d90 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionResultVo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionResultVo.java @@ -110,18 +110,17 @@ public class QcInspectionResultVo implements Serializable { private Long typeId; /** - * 检测方法(0目视,1千分尺) + * 检测方法(字典键值,关联QcInspectionItem.method) */ @ExcelProperty(value = "检测方法", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "qc_methond") - @Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "qc_methond") - private String methodName; + private String method; -// /** -// * 检测方法名称 -// */ -// @ExcelProperty(value = "检测方法名称") -// private String methodName; + /** + * 检测方法名称(字典翻译值) + */ + @Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "method", other = "qc_methond") + private String methodName; /** * 检测方式(0定性,1定量)(快照) diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcPDAServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcPDAServiceImpl.java index 31d94cb5..9444cf79 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcPDAServiceImpl.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcPDAServiceImpl.java @@ -74,6 +74,9 @@ public class QcPDAServiceImpl implements IQcPDAService { private final IProdBaseStationInfoService prodBaseStationInfoService; private final IProdBaseProcessInfoService prodBaseProcessInfoService; + // WMS 回调服务(质检完成后通知 WMS) + private final IQcWmsCallbackService qcWmsCallbackService; + private static final String UNQUALIFIED_REVIEW_FLOW_CODE = "unqualified_review"; /** @@ -115,9 +118,11 @@ public class QcPDAServiceImpl implements IQcPDAService { /** * 通过planDetailId和processId生成质检任务 + *

+ * 使用分布式事务:调用了 remoteCodeRuleService 生成质检编号 */ @Override - @Transactional(rollbackFor = Exception.class) + @GlobalTransactional(rollbackFor = Exception.class) public Boolean generateInspectionTask(GenerateInspectionTaskBo bo) { ProdQmsPlanDetail planDetail = remotePdaMesApiService.remoteGetProductionPlanDetail(bo); if (StringUtils.isNull(planDetail)) { @@ -363,6 +368,17 @@ public class QcPDAServiceImpl implements IQcPDAService { } } } + + // FIXME:hwmom-wms 回调:质检完成后通知 WMS 更新质检状态 + // 说明:此方法根据质检结果自动判断更新 WMS 的 inspectionType: + // - 全部合格 -> inspectionType='2' + // - 全部不合格 -> inspectionType='3' + // - 部分合格部分不合格 -> 保持 inspectionType='1'(等待评审) + QcInspectionMainVo updatedMain = qcInspectionMainService.queryById(inspectionId); + if (qcWmsCallbackService.isFromWmsInspection(updatedMain)) { + qcWmsCallbackService.notifyWmsInspectionComplete(updatedMain); + } + // 方法执行成功返回true return true; } diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java index c95c3c1b..eefff968 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java @@ -21,10 +21,12 @@ import org.dromara.qms.domain.vo.QcUnqualifiedReviewVo; import org.dromara.qms.mapper.QcUnqualifiedReviewMapper; import org.dromara.qms.service.IQcInspectionTypeService; import org.dromara.qms.service.IQcUnqualifiedReviewService; +import org.dromara.qms.service.IQcWmsCallbackService; import org.dromara.workflow.api.RemoteWorkflowService; import org.dromara.workflow.api.domain.RemoteCompleteTask; import org.springframework.stereotype.Service; +import java.math.BigDecimal; import java.util.*; /** @@ -41,6 +43,14 @@ public class QcUnqualifiedReviewServiceImpl implements IQcUnqualifiedReviewServi private final IQcInspectionTypeService inspectionTypeService; + /** + * WMS 回调服务 + *

+ * 说明:评审完成后回调 WMS 更新质检状态 + * 可替换为其他服务或注释掉不使用 + */ + private final IQcWmsCallbackService qcWmsCallbackService; + //WARM-FLOW工作流 @DubboReference(timeout = 30000) private final RemoteWorkflowService remoteWorkflowService; @@ -211,6 +221,71 @@ public class QcUnqualifiedReviewServiceImpl implements IQcUnqualifiedReviewServi completeTask.setMessage("审批通过 " ); remoteWorkflowService.completeTask(completeTask); - return baseMapper.updateById(review) > 0; + boolean result = baseMapper.updateById(review) > 0; + + // FIXME:【可配置】WMS 回调:根据评审结果更新 WMS 质检状态 + // 说明:可直接注释掉此方法调用来禁用 WMS 回调 + if (result) { + callbackWmsAfterReview(review); + } + + return result; + } + + /** + * 【可配置】评审完成后回调 WMS + *

+ * 根据评审结果(reviewResult)更新 WMS 的质检状态: + * - 4(让步接收)-> inspectionType='2'(合格),全部入库 + * - 0(报废)/2(退货)-> inspectionType='2'(合格),更新数量为合格数量 + * - 1(返工)/3(流转)-> 不回调,等待后续处理 + *

+ * 说明:此方法已独立抽离,便于: + * 1. 替换为其他 Dubbo 服务或 REST API + * 2. 直接注释掉不使用 + * 3. 根据业务需求修改回调逻辑 + * + * @param review 不合格品评审记录 + */ + private void callbackWmsAfterReview(QcUnqualifiedReview review) { + String reviewResult = review.getReviewResult(); + if (StringUtils.isBlank(reviewResult)) { + return; + } + + // 转换为 VO 用于回调 + QcUnqualifiedReviewVo reviewVo = new QcUnqualifiedReviewVo(); + reviewVo.setReviewId(review.getReviewId()); + reviewVo.setInspectionNo(review.getInspectionNo()); + reviewVo.setInspectionQty(review.getInspectionQty()); + reviewVo.setBatchNo(review.getBatchNo()); + reviewVo.setMaterialCode(review.getMaterialCode()); + reviewVo.setReviewResult(reviewResult); + + switch (reviewResult) { + case "4": + // 让步接收:更新为合格,全部入库 + // TODO:【可替换】调用 WMS 回调服务 + qcWmsCallbackService.notifyWmsForConcessionAccepted(reviewVo); + break; + + case "0": + case "2": + // 报废(0)/退货(2):更新为合格,但只入库合格部分(数量为0) + // TODO:【可替换】调用 WMS 回调服务,更新数量为0 + qcWmsCallbackService.notifyWmsForConcessionRejected(reviewVo, BigDecimal.ZERO); + break; + + case "1": + case "3": + // 返工(1)/流转(3):暂不回调 WMS,等待后续处理 + // 如需回调,取消下面注释即可 + // qcWmsCallbackService.notifyWmsForConcessionRejected(reviewVo, BigDecimal.ZERO); + break; + + default: + // 未知评审结果,不处理 + break; + } } } diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsCallbackServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsCallbackServiceImpl.java index 9c53c7fb..f1d9e96e 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsCallbackServiceImpl.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsCallbackServiceImpl.java @@ -79,7 +79,7 @@ public class QcWmsCallbackServiceImpl implements IQcWmsCallbackService { // 2. 获取质检类型(用于判断是否回调) String qcInspectionType = getQcInspectionType(main); - + // 3. 判断质检结果并调用对应的回调方法 BigDecimal qualifiedQty = main.getQualifiedQty() != null ? main.getQualifiedQty() : BigDecimal.ZERO; BigDecimal unqualifiedQty = main.getUnqualifiedQty() != null ? main.getUnqualifiedQty() : BigDecimal.ZERO; @@ -189,7 +189,7 @@ public class QcWmsCallbackServiceImpl implements IQcWmsCallbackService { */ private boolean callbackWmsUpdateStatus(QcInspectionMainVo main, String targetInspectionType, BigDecimal updateQty, String qcInspectionType) { - // 【配置项】只有原材料检(4)或入库检(7)需要回调 WMS + // TODO:【配置项】只有原材料检(4)或入库检(7)需要回调 WMS // 可根据业务需求修改此判断条件,或直接注释掉整个方法体 if (!"4".equals(qcInspectionType) && !"7".equals(qcInspectionType)) { log.info("检测类型 {} 不需要回调 WMS,跳过", qcInspectionType); @@ -210,7 +210,7 @@ public class QcWmsCallbackServiceImpl implements IQcWmsCallbackService { notification.setQualifiedQty(updateQty); } - // 【可替换】调用 WMS Dubbo 接口 + // TODO:【可替换】调用 WMS Dubbo 接口 // 可替换为:REST API 调用、其他 Dubbo 服务、或直接注释掉 R result = remoteWmsInstockService.completeInstockAfterInspection(notification); diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/api/apiController.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/api/apiController.java index 6f9b5395..3dabd2a2 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/api/apiController.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/api/apiController.java @@ -1,16 +1,29 @@ package org.dromara.wms.controller.api; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.qms.api.dto.InspectionCompleteNotification; import org.dromara.wms.domain.bo.WmsInstockOrderBo; +import org.dromara.wms.domain.bo.WmsInstockPrintBo; import org.dromara.wms.domain.vo.WmsInstockOrderVo; +import org.dromara.wms.domain.vo.WmsInstockPrintVo; import org.dromara.wms.service.IBaseSupplierInfoService; import org.dromara.wms.service.IWmsInstockOrderService; +import org.dromara.wms.service.IWmsInstockPrintService; import org.dromara.wms.service.IWmsInstockRecordService; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.math.BigDecimal; +import java.util.List; + +@Slf4j @RequiredArgsConstructor @RestController @RequestMapping("/wsmApi") @@ -22,6 +35,8 @@ public class apiController { private final IBaseSupplierInfoService baseSupplierInfoService;//供应商信息服务 + private final IWmsInstockPrintService wmsInstockPrintService;//入库打印服务 + //质检入库 @PostMapping("/qcInstore") @@ -42,5 +57,114 @@ public class apiController { return R.ok(wmsInstockOrderService.insertByBo(bo)); } + /** + * 质检完成回调接口 + *

+ * 业务场景: + * 1. QMS 质检完成后调用此接口通知 WMS + * 2. WMS 根据质检结果决定是否完成入库 + * 3. 合格:完成入库,更新库存 + * 4. 不合格:标记拒收,生成不合格品记录 + * + * @param notification 质检完成通知参数 + * @return 处理结果 + */ + @PostMapping("/notifyInspectionComplete") + @Transactional(rollbackFor = Exception.class) + public R notifyInspectionComplete(@RequestBody InspectionCompleteNotification notification) { + log.info("收到质检完成通知,质检单号: {}, 入库单号: {}, 质检结果: {}", + notification.getInspectionNo(), notification.getInstockCode(), notification.getResult()); + + // 参数校验 + if (StringUtils.isBlank(notification.getInstockCode())) { + return R.fail("入库单号不能为空"); + } + if (StringUtils.isBlank(notification.getResult())) { + return R.fail("质检结果不能为空"); + } + if (StringUtils.isBlank(notification.getStatus())) { + return R.fail("质检状态不能为空"); + } + if (StringUtils.isBlank(notification.getBatchCode())) { + return R.fail("批次号不能为空"); + } + + try { + // 只有质检状态为"已完成"(status="1")时才处理 + if (!"1".equals(notification.getStatus())) { + log.warn("质检未完成,跳过处理,质检单号: {}", notification.getInspectionNo()); + return R.ok("质检未完成,跳过处理"); + } + + // 更新入库打印记录的质检状态 + updateInspectionPrintStatus(notification); + + // 根据质检结果决定是否完成入库 + if ("0".equals(notification.getResult())) { + // 质检合格 - 完成入库 + log.info("质检合格,开始完成入库,入库单号: {}", notification.getInstockCode()); + Boolean result = wmsInstockRecordService.completeQualityCheck(notification.getInstockCode()); + return result ? R.ok("质检合格,入库完成") : R.fail("入库完成失败"); + } else { + // 质检不合格 - 标记拒收 + log.warn("质检不合格,入库单号: {}", notification.getInstockCode()); + markAsRejected(notification); + return R.ok("质检不合格,已标记拒收"); + } + + } catch (ServiceException e) { + log.error("质检完成处理失败: {}", e.getMessage()); + return R.fail(e.getMessage()); + } catch (Exception e) { + log.error("质检完成处理异常", e); + return R.fail("质检完成处理异常: " + e.getMessage()); + } + } + + /** + * 更新入库打印记录的质检状态 + *

+ * 说明:必须按 instockCode + batchCode 精确更新,避免影响其他批次 + * inspectionType: 0=未发起, 1=质检中, 2=合格, 3=不合格 + */ + private void updateInspectionPrintStatus(InspectionCompleteNotification notification) { + WmsInstockPrintBo printBo = new WmsInstockPrintBo(); + printBo.setInstockCode(notification.getInstockCode()); + printBo.setBatchCode(notification.getBatchCode()); // 按批次号精确过滤 + List prints = wmsInstockPrintService.queryList(printBo); + + for (WmsInstockPrintVo print : prints) { + WmsInstockPrintBo updateBo = new WmsInstockPrintBo(); + updateBo.setInstockPrintId(print.getInstockPrintId()); + // 状态映射:result(0=合格,1=不合格) -> inspectionType(2=合格,3=不合格) + String inspectionType = "0".equals(notification.getResult()) ? "2" : "3"; + updateBo.setInspectionType(inspectionType); + wmsInstockPrintService.updateByBo(updateBo); + } + } + + /** + * 标记为拒收 + *

+ * 说明:必须按 instockCode + batchCode 精确更新,避免影响其他批次 + */ + private void markAsRejected(InspectionCompleteNotification notification) { + WmsInstockPrintBo printBo = new WmsInstockPrintBo(); + printBo.setInstockCode(notification.getInstockCode()); + printBo.setBatchCode(notification.getBatchCode()); // 按批次号精确过滤 + List prints = wmsInstockPrintService.queryList(printBo); + + for (WmsInstockPrintVo print : prints) { + // 更新入库状态为拒收(可自定义状态码) + WmsInstockPrintBo updateBo = new WmsInstockPrintBo(); + updateBo.setInstockPrintId(print.getInstockPrintId()); + updateBo.setInboundStatus("3"); // 3=拒收 + wmsInstockPrintService.updateByBo(updateBo); + } + + // TODO: 生成不合格品记录(可选) + log.warn("入库单 {} 已标记为拒收,待人工处理不合格品", notification.getInstockCode()); + } + } diff --git a/ruoyi-modules/hwmom-wms/wms-qms.md b/ruoyi-modules/hwmom-wms/wms-qms.md index 5225f267..6f4d3451 100644 --- a/ruoyi-modules/hwmom-wms/wms-qms.md +++ b/ruoyi-modules/hwmom-wms/wms-qms.md @@ -7,6 +7,7 @@ | 1.0 | 2026-01-15 | 初始版本,完整需求与方案设计 | zch | | 1.1 | 2026-01-15 | 新增独立文件实现,添加代码实现章节 | zch | | 1.2 | 2026-01-15 | 修复Code Review问题:创建独立 Dubbo 实现类、修复状态映射、添加批次号过滤 | zch | +| **1.3** | **2026-01-15** | **重构QMS回调WMS逻辑**:1.入库改由PDA扫码完成 2.回调只更新质检状态 3.抽离回调方法便于替换/注释 4.支持检测类型4(原材料检)和7(入库检) 5.新增让步接收回调 | zch | --- @@ -768,83 +769,840 @@ seata: --- -## 9. 让步接收方案(待讨论) +## 9. 让步接收方案 -### 9.1 业务场景 +### 9.1 业务需求概述 + +**业务定义**:让步接收是指产品质量不完全符合标准,但经过评审后认为可以使用,允许按合格品入库的特殊场景。 + +**核心原则**: +1. **PDA驱动入库**:QMS回调只更新状态,不自动入库,入库由PDA扫码完成 +2. **状态驱动**:PDA入库条件基于 `inspectionRequest`(质检要求)和 `inspectionType`(质检状态)组合判断 + - **允许入库**: + - `inspectionRequest='1'`(免检)→ 直接允许入库 + - `inspectionRequest='0'`(必检)且 `inspectionType='2'`(合格)→ 允许入库 + - **不允许入库**: + - `inspectionRequest='0'`(必检)且 `inspectionType='0'`(未发起)→ 提示"需要先发起质检" + - `inspectionRequest='0'`(必检)且 `inspectionType='1'`(质检中)→ 提示"物料质检中,无法入库" + - `inspectionRequest='0'`(必检)且 `inspectionType='3'`(不合格)→ 提示"物料质检不合格,请联系质量部门进行让步接收评审" +3. **批次号一致**:全流程使用同一批次号,确保可追溯 +4. **数量可调整**:让步接收不通过时,可调整入库数量 + +### 9.2 业务场景分类 + +#### 场景A:全部合格(无需让步接收) ``` -批次 BATCH001,总数100 -├─ 质检完成:80合格 + 20不合格 -│ ├─ 合格80 → QMS 回调 WMS → inspection_type='2' -│ └─ 不合格20 → 创建不合格品评审 +批次 BATCH001,总数100件 +├─ PDA质检完成:100合格 + 0不合格 +│ ├─ qualified_qty=100, unqualified_qty=0 +│ └─ result='0'(合格) │ -└─ 不合格品评审 → 让步接收(reviewResult='4') - └─ QMS 回调 WMS → inspection_type='2'(让步接收视为合格) - └─ PDA 扫码入库 20 件 +└─ QMS回调WMS + └─ inspection_type='2'(合格) + └─ PDA扫码入库100件 ``` -### 9.2 技术方案(待讨论) +#### 场景B:部分不合格(需要让步接收评审) -#### 方案A:让步接收时单独回调(推荐) +``` +批次 BATCH002,总数100件 +├─ PDA质检完成:80合格 + 20不合格 +│ ├─ qualified_qty=80, unqualified_qty=20 +│ └─ result='1'(不合格) +│ +├─ QMS回调WMS +│ └─ inspection_type='1'(质检中) ⭐保持等待评审 +│ └─ 创建不合格品评审 +│ +└─ 不合格品评审 + ├─ 让步接收通过(review_result='4') + │ └─ QMS回调WMS: inspection_type='2' + │ └─ PDA扫码入库100件(80合格+20让步) + │ + └─ 让步接收不通过 + └─ QMS回调WMS: inspection_type='2', apportion_qty=80 + └─ PDA扫码入库80件(仅合格部分) +``` +#### 场景C:全部不合格(需要让步接收评审) + +``` +批次 BATCH003,总数100件 +├─ PDA质检完成:0合格 + 100不合格 +│ ├─ qualified_qty=0, unqualified_qty=100 +│ └─ result='1'(不合格) +│ +├─ QMS回调WMS +│ └─ inspection_type='3'(不合格) ⭐保持等待评审 +│ └─ 创建不合格品评审 +│ +└─ 不合格品评审 + ├─ 让步接收通过(review_result='4') + │ └─ QMS回调WMS: inspection_type='2' + │ └─ PDA扫码入库100件 + │ + └─ 让步接收不通过 + └─ QMS回调WMS: inspection_type='2', apportion_qty=0 + └─ 无法入库(数量为0) +``` + +### 9.3 状态流转规则 + +#### 9.3.1 inspection_type 状态流转 + +``` +【正常流程】 +'0'(未发起) → '1'(质检中) → '2'(合格) → PDA入库 + +【部分不合格流程】 +'0' → '1' → '1'(保持质检中) → [让步接收评审] → '2' → PDA入库 + ↑ ↓ + └─────── 等待评审 ──────┘ + +【全部不合格流程】 +'0' → '1' → '3'(不合格) → [让步接收评审] → '2' → PDA入库 + ↑ ↓ + └────── 等待评审 ─────┘ +``` + +#### 9.3.2 状态判断规则 + +| PDA质检结果 | qualified_qty | unqualified_qty | WMS更新为 | 说明 | +|-------------|--------------|----------------|----------|------| +| 全部合格 | =总数 | =0 | `'2'` | 直接入库 | +| 部分不合格 | >0 | >0 | `'1'` | 等待让步接收评审 | +| 全部不合格 | =0 | =总数 | `'3'` | 等待让步接收评审 | + +#### 9.3.3 让步接收评审后的更新规则 + +| 评审结果 | review_result | WMS更新为 | apportion_qty | 入库数量 | +|---------|--------------|----------|--------------|---------| +| 让步接收通过 | `'4'` | `'2'` | 不变 | 全部数量 | +| 让步接收不通过 | 其他 | `'2'` | 更新为qualified_qty | 只有合格部分 | + +### 9.4 技术实现方案 + +#### 9.4.1 需要修改的文件清单 + +| 序号 | 模块 | 文件 | 修改内容 | 优先级 | +|-----|-----|------|---------|--------| +| 1 | QMS | `QcPDAServiceImpl.java` | 修改质检完成回调逻辑 | **高** | +| 2 | QMS | `QcWmsCallbackServiceImpl.java` | 新增3个回调方法 | **高** | +| 3 | QMS | `QcUnqualifiedReviewServiceImpl.java` | 评审完成处理逻辑 | **高** | +| 4 | QMS API | `InspectionCompleteNotification.java` | 新增updateQty字段 | 中 | +| 5 | WMS | `WmsInspectionCallbackServiceImpl.java` | 支持更新apportion_qty | 中 | +| 6 | PDA | `RawInActivity.java` | 允许inspection_type='2'入库 | 高 | + +#### 9.4.2 修改1:QcPDAServiceImpl.java - 质检完成回调 + +**文件位置**:`hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcPDAServiceImpl.java` + +**修改位置**:第370-374行 + +**修改前**: ```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()); - } +// WMS 回调:质检完成后通知 WMS 更新状态 +QcInspectionMainVo updatedMain = qcInspectionMainService.queryById(inspectionId); +if (qcWmsCallbackService.isFromWmsInspection(updatedMain)) { + qcWmsCallbackService.notifyWmsForQualified(updatedMain); } ``` -#### 方案B:与合格数量回调合并 +**修改后**: +```java +// WMS 回调:质检完成后通知 WMS 更新状态 +QcInspectionMainVo updatedMain = qcInspectionMainService.queryById(inspectionId); +if (qcWmsCallbackService.isFromWmsInspection(updatedMain)) { + // 调用新的回调方法:根据质检结果决定状态更新 + qcWmsCallbackService.notifyWmsInspectionComplete(updatedMain); +} +``` -将让步接收也调用 `notifyWmsForQualified()` 方法,统一处理。 +#### 9.4.3 修改2:QcWmsCallbackServiceImpl.java - 新增回调方法 -### 9.3 数据一致性保证 +**文件位置**:`hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsCallbackServiceImpl.java` -| 场景 | 合格数量 | 让步接收数量 | 累计入库 | 最终状态 | -|-----|---------|-------------|---------|---------| -| 场景1 | 80 | 20 | 100 | 已入库 | -| 场景2 | 100 | 0 | 100 | 已入库 | -| 场景3 | 0 | 100 | 100 | 已入库 | +**方法抽离说明**: +以下方法已独立抽离,便于: +1. 替换为其他Dubbo服务或REST API +2. 注释掉不使用(如业务变更) +3. 根据检测类型(inspectionType)判断是否需要回调 + +**检测类型字典**(用于判断是否回调WMS): + +| inspectionType | 名称 | 说明 | +|---------------|------|------| +| 0 | 首检 | 生产首件检验 | +| 1 | 专检 | 专职检验 | +| 2 | 自检 | 操作工自检 | +| 3 | 互检 | 互检 | +| **4** | **原材料检** | **WMS入库默认使用,需回调WMS** | +| 5 | 抽检 | 抽样检验 | +| 6 | 成品检 | 成品出厂检验 | +| 7 | 入库检 | 入库时检验 | +| 8 | 出库检 | 出库时检验 | + +**关键**:当前WMS发起质检时默认传 `inspectionType=4`(原材料检),因此回调时也只对原材料检进行WMS状态更新。 + +**新增方法1:质检完成回调** + +```java +/** + * 【可配置】质检完成回调WMS + * + * 说明:此方法已独立抽离,便于: + * 1. 替换为其他Dubbo服务或REST API + * 2. 注释掉不使用(如业务变更) + * 3. 根据inspectionType判断是否需要回调 + * + * 根据质检结果更新WMS状态: + * - 全部合格 → inspection_type='2' + * - 部分不合格 → inspection_type='1'(等待评审) + * - 全部不合格 → inspection_type='3'(等待评审) + * + * @param main 质检主表 + * @param inspectionType 检测类型(4=原材料检,7=入库检等) + * @return 是否成功 + */ +private boolean callbackWmsAfterInspection(QcInspectionMainVo main, String inspectionType) { + // 当前配置:只有原材料检(4)需要回调WMS + // 可根据业务需求修改此判断条件 + if (!"4".equals(inspectionType)) { + log.info("检测类型 {} 不需要回调WMS,跳过", inspectionType); + return false; + } + + try { + InspectionCompleteNotification notification = buildNotification(main, null, false); + + // 【可替换】调用WMS Dubbo接口 + // 可替换为:REST API调用、其他Dubbo服务、或直接注释掉 + R result = remoteWmsInspectionService.completeInstockAfterInspection(notification); + + if (result == null || result.getCode() != R.SUCCESS) { + log.error("质检单 {} 回调WMS失败: {}", main.getInspectionNo(), + result != null ? result.getMsg() : "null"); + return false; + } + + log.info("质检单 {} 回调WMS成功,结果: {}, 合格数量: {}", + main.getInspectionNo(), main.getResult(), main.getQualifiedQty()); + return true; + + } catch (Exception e) { + log.error("质检完成回调WMS失败: {}", e.getMessage(), e); + return false; + } +} + +/** + * 质检完成后回调WMS(公共入口) + * + * @param main 质检主表 + * @return 是否成功 + */ +@Override +public boolean notifyWmsInspectionComplete(QcInspectionMainVo main) { + if (!isFromWmsInspection(main)) { + log.info("质检单 {} 不是来自WMS入库,跳过回调", main.getInspectionNo()); + return false; + } + + // 获取检测类型(从remark字段或其他方式获取) + String inspectionType = main.getRemark(); // 或从其他字段获取 + + // 调用抽离的回调方法 + return callbackWmsAfterInspection(main, inspectionType); +} +``` + +**新增方法2:让步接收通过后回调** + +```java +/** + * 【可配置】让步接收评审通过后回调WMS + * + * 说明:此方法已独立抽离,便于: + * 1. 替换为其他Dubbo服务或REST API + * 2. 注释掉不使用(如业务变更) + * + * 业务逻辑: + * 1. 更新质检主表:累加让步接收数量到合格数量 + * 2. 回调WMS:更新状态为'2'(合格) + * 3. apportion_qty保持不变 + * + * @param review 不合格品评审记录 + * @return 是否成功 + */ +private boolean callbackWmsForConcessionAccepted(QcUnqualifiedReviewVo review) { + // 可在此添加业务开关 + // if (!ENABLE_CONCESSION_CALLBACK) { return false; } + + try { + // 1. 获取质检主表 + QcInspectionMainVo main = queryByInspectionNo(review.getInspectionNo()); + if (main == null || !isFromWmsInspection(main)) { + log.warn("质检单 {} 不是来自WMS入库,跳过回调", review.getInspectionNo()); + return false; + } + + // 2. 更新质检主表:累加让步接收数量 + BigDecimal concessionQty = review.getInspectionQty(); + main.setQualifiedQty(main.getQualifiedQty().add(concessionQty)); + main.setUnqualifiedQty(main.getUnqualifiedQty().subtract(concessionQty)); + main.setResult("0"); // 更新为合格 + qcInspectionMainService.updateById(main); + + log.info("质检单 {} 让步接收数量已更新: {} + {} = {}", + review.getInspectionNo(), + main.getQualifiedQty().subtract(concessionQty), + concessionQty, + main.getQualifiedQty()); + + // 3. 【可替换】回调WMS:更新状态为'2' + InspectionCompleteNotification notification = buildNotification(main, null, true); + // 可替换为:REST API调用、其他Dubbo服务、或直接注释掉 + R result = remoteWmsInstockService.completeInstockAfterInspection(notification); + + if (result == null || result.getCode() != R.SUCCESS) { + log.error("让步接收回调WMS失败: {}", result != null ? result.getMsg() : "null"); + return false; + } + + log.info("让步接收回调WMS成功,质检单号: {}, 批次号: {}, 数量: {}", + review.getInspectionNo(), review.getBatchNo(), concessionQty); + return true; + + } catch (Exception e) { + log.error("让步接收回调WMS失败: {}", e.getMessage(), e); + return false; + } +} + +/** + * 让步接收评审通过后回调WMS(公共入口) + * + * @param review 不合格品评审记录 + * @return 是否成功 + */ +@Override +public boolean notifyWmsForConcessionAccepted(QcUnqualifiedReviewVo review) { + return callbackWmsForConcessionAccepted(review); +} +``` + +**新增方法3:让步接收不通过后回调** + +```java +/** + * 【可配置】让步接收评审不通过后回调WMS + * + * 说明:此方法已独立抽离,便于: + * 1. 替换为其他Dubbo服务或REST API + * 2. 注释掉不使用(如业务变更) + * + * 业务逻辑: + * 1. 更新质检结果为合格(允许合格部分入库) + * 2. 回调WMS:更新状态为'2',同时更新apportion_qty为合格数量 + * + * @param review 不合格品评审记录 + * @param qualifiedQty 合格数量 + * @return 是否成功 + */ +private boolean callbackWmsForConcessionRejected(QcUnqualifiedReviewVo review, BigDecimal qualifiedQty) { + // 可在此添加业务开关 + // if (!ENABLE_CONCESSION_CALLBACK) { return false; } + + try { + // 1. 获取质检主表 + QcInspectionMainVo main = queryByInspectionNo(review.getInspectionNo()); + if (main == null || !isFromWmsInspection(main)) { + log.warn("质检单 {} 不是来自WMS入库,跳过回调", review.getInspectionNo()); + return false; + } + + // 2. 【可替换】回调WMS:更新状态为'2',同时更新数量 + InspectionCompleteNotification notification = buildNotification(main, qualifiedQty, false); + notification.setUpdateQty("1"); // 标记需要更新数量 + + // 可替换为:REST API调用、其他Dubbo服务、或直接注释掉 + R result = remoteWmsInstockService.completeInstockAfterInspection(notification); + + if (result == null || result.getCode() != R.SUCCESS) { + log.error("让步接收不通过回调WMS失败: {}", result != null ? result.getMsg() : "null"); + return false; + } + + log.info("让步接收不通过回调WMS成功,质检单号: {}, 批次号: {}, 合格数量: {}", + review.getInspectionNo(), review.getBatchNo(), qualifiedQty); + return true; + + } catch (Exception e) { + log.error("让步接收不通过回调WMS失败: {}", e.getMessage(), e); + return false; + } +} + +/** + * 让步接收评审不通过后回调WMS(公共入口) + * + * @param review 不合格品评审记录 + * @param qualifiedQty 合格数量 + * @return 是否成功 + */ +@Override +public boolean notifyWmsForConcessionRejected(QcUnqualifiedReviewVo review, BigDecimal qualifiedQty) { + return callbackWmsForConcessionRejected(review, qualifiedQty); +} +``` + +**修改方法:buildNotification() - 支持数量传递** + +```java +/** + * 构建质检完成通知参数 + * + * @param main 质检主表 + * @param qty 数量(null=全部合格数量,非null=指定数量) + * @param isConcession 是否让步接收 + * @return 通知参数 + */ +@Override +public Object buildNotification(QcInspectionMainVo main, BigDecimal qty, boolean isConcession) { + InspectionCompleteNotification notification = new InspectionCompleteNotification(); + + // 基本信息 + notification.setInspectionNo(main.getInspectionNo()); + notification.setMaterialCode(main.getMaterialCode()); + notification.setInstockCode(main.getRemark()); // remark存储入库单号 + notification.setBatchCode(main.getBatchNo()); // 批次号 + + // 质检结果 + if (isConcession) { + // 让步接收视为合格 + notification.setResult("0"); + notification.setQualifiedQty(qty != null ? qty : main.getQualifiedQty()); + notification.setUnqualifiedQty(BigDecimal.ZERO); + } else { + // 正常质检结果 + notification.setResult(main.getResult()); + notification.setQualifiedQty(main.getQualifiedQty()); + notification.setUnqualifiedQty(main.getUnqualifiedQty()); + } + + // 质检状态 + notification.setStatus("1"); // 已完成 + + // 质检完成时间 + notification.setInspectionEndTime(main.getInspectionEndTime() != null + ? DateUtils.formatDateTime(main.getInspectionEndTime()) + : DateUtils.getTime()); + + return notification; +} +``` + +#### 9.4.4 修改3:QcUnqualifiedReviewServiceImpl.java - 评审完成处理 + +**文件位置**:`hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java` + +**修改completeTask()方法** + +```java +@Override +@GlobalTransactional(rollbackFor = Exception.class) +public Boolean completeTask(QcUnqualifiedReviewBo bo) { + QcUnqualifiedReview review = baseMapper.selectById(bo.getReviewId()); + + if (review == null) { + throw new ServiceException("不合格品待评审不存在"); + } + if (ObjectUtils.isNotEmpty(review.getReviewEndTime())) { + throw new ServiceException("该任务已处理完成"); + } + + // 1. 完成工作流审批(现有逻辑) + Long currentTaskId = remoteWorkflowService.getCurrentTaskIdByInstanceId( + review.getProcessInstanceId() + ); + if (currentTaskId == null) { + throw new ServiceException("任务不存在"); + } + + review.setReviewer(LoginHelper.getUsername()); + review.setReviewerId(LoginHelper.getUserId()); + review.setReviewEndTime(new Date()); + + RemoteCompleteTask completeTask = new RemoteCompleteTask(); + completeTask.setTaskId(currentTaskId); + List messageTypes = new ArrayList<>(); + messageTypes.add("1"); + completeTask.setMessageType(messageTypes); + completeTask.setMessage("审批通过"); + remoteWorkflowService.completeTask(completeTask); + + // 2. 根据评审结果处理WMS回调 + if ("4".equals(review.getReviewResult())) { + // 让步接收通过 + qcWmsCallbackService.notifyWmsForConcessionAccepted(review); + } else { + // 让步接收不通过或其他结果 + // 获取质检主表的合格数量 + QcInspectionMainVo main = qcInspectionMainService.queryByInspectionNo( + review.getInspectionNo() + ); + if (main != null) { + qcWmsCallbackService.notifyWmsForConcessionRejected(review, main.getQualifiedQty()); + } + } + + return baseMapper.updateById(review) > 0; +} +``` + +#### 9.4.5 修改4:InspectionCompleteNotification.java - 新增字段 + +**文件位置**:`hwmom/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java` + +**新增字段**: + +```java +/** + * 是否更新数量标记 + *

+ * 说明: + * 0=不更新,1=更新为qualifiedQty + *

+ * 使用场景:让步接收评审不通过时,需要更新入库数量为实际合格数量 + */ +private String updateQty; + +public String getUpdateQty() { + return updateQty; +} + +public void setUpdateQty(String updateQty) { + this.updateQty = updateQty; +} +``` + +#### 9.4.6 修改5:WmsInspectionCallbackServiceImpl.java - 支持更新数量 + +**文件位置**:`hwmom/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsInspectionCallbackServiceImpl.java` + +**修改handleInspectionComplete()方法** + +```java +@Override +public boolean handleInspectionComplete(InspectionCompleteNotification notification) { + // 1. 参数校验 + if (StringUtils.isBlank(notification.getInstockCode()) || + StringUtils.isBlank(notification.getBatchCode())) { + log.warn("入库单号或批次号为空"); + return false; + } + + // 2. 查询打印记录 + WmsInstockPrintVo printVo = queryByInstockAndBatch( + notification.getInstockCode(), + notification.getBatchCode() + ); + + if (printVo == null) { + log.warn("未找到入库打印记录: instockCode={}, batchCode={}", + notification.getInstockCode(), notification.getBatchCode()); + return false; + } + + // 3. 根据质检结果更新状态 + if ("0".equals(notification.getResult())) { + // 合格 + return updateToQualified(notification); + } else { + // 不合格:判断是部分不合格还是全部不合格 + if (notification.getQualifiedQty().compareTo(BigDecimal.ZERO) > 0) { + // 部分不合格 → 保持'1'(质检中),等待评审 + return updateToInProgress(notification); + } else { + // 全部不合格 → 更新为'3'(不合格),等待评审 + return updateToUnqualified(notification); + } + } +} + +/** + * 更新为质检中状态(部分不合格,等待评审) + */ +private boolean updateToInProgress(InspectionCompleteNotification notification) { + WmsInstockPrintBo updateBo = new WmsInstockPrintBo(); + updateBo.setInstockCode(notification.getInstockCode()); + updateBo.setBatchCode(notification.getBatchCode()); + updateBo.setInspectionType("1"); // 质检中 + + boolean success = instockPrintService.updateByBo(updateBo) > 0; + if (success) { + log.info("批次 {} 状态已更新为'质检中',等待让步接收评审", + notification.getBatchCode()); + } + return success; +} + +/** + * 更新为合格状态(支持更新数量) + */ +private boolean updateToQualified(InspectionCompleteNotification notification) { + WmsInstockPrintBo updateBo = new WmsInstockPrintBo(); + updateBo.setInstockCode(notification.getInstockCode()); + updateBo.setBatchCode(notification.getBatchCode()); + updateBo.setInspectionType("2"); // 合格 + + // 如果标记需要更新数量(让步接收不通过场景) + if ("1".equals(notification.getUpdateQty())) { + updateBo.setApportionQty(notification.getQualifiedQty()); + log.info("批次 {} 数量已更新为: {}", + notification.getBatchCode(), notification.getQualifiedQty()); + } + + boolean success = instockPrintService.updateByBo(updateBo) > 0; + if (success) { + log.info("批次 {} 状态已更新为'合格'", notification.getBatchCode()); + } + return success; +} +``` + +#### 9.4.7 修改6:RawInActivity.java (PDA) - PDA入库条件判断 + +**文件位置**:`mom-pad/app/src/main/java/com/example/haiwei_mom/wms/raw/RawInActivity.java` + +**字段说明**: +- `inspectionRequest`(质检要求):0=必检,1=免检 +- `inspectionType`(质检状态):0=未发起,1=质检中,2=合格,3=不合格 + +**修改质检提交方法** + +```java +// 质检提交 +public void rawinSubmit2(View view) { + if (!inCheck()) { + return; + } + + String inspectionRequest = rawInstock.getInspectionRequest(); // 质检要求 + String inspectionType = rawInstock.getInspectionType(); // 质检状态 + + // 【修改】基于 inspectionRequest 和 inspectionType 组合判断 + // 允许入库:inspectionRequest='1'(免检) 或 (inspectionRequest='0' 且 inspectionType='2') + // 不允许入库:其他情况 + boolean canInstock = "1".equals(inspectionRequest); // 免检直接允许 + if (!canInstock && "0".equals(inspectionRequest)) { + // 必检时,只有合格(2)才能入库 + canInstock = "2".equals(inspectionType); + } + + if (!canInstock) { + String message = ""; + if ("0".equals(inspectionType)) { + message = "物料需要先发起质检"; + } else if ("1".equals(inspectionType)) { + message = "物料质检中,无法入库"; + } else if ("3".equals(inspectionType)) { + message = "物料质检不合格,请联系质量部门进行让步接收评审"; + } else { + message = "物料状态异常,无法入库"; + } + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + return; + } + + rawinSubmit(); +} +``` + +### 9.5 数据一致性保证 + +#### 9.5.1 数据流转示例 + +**示例1:部分不合格,让步接收通过** + +``` +批次:BATCH001,总数:100件 + +【步骤1】PDA质检完成 +qc_inspection_main: + - qualified_qty = 80 + - unqualified_qty = 20 + - result = '1' (不合格) + +wms_instock_print: + - inspection_type = '1' (质检中) ← 保持等待 + +【步骤2】让步接收评审通过 +qc_unqualified_review: + - review_result = '4' (让步接收) + - inspection_qty = 20 + +qc_inspection_main: + - qualified_qty = 80 + 20 = 100 + - unqualified_qty = 0 + - result = '0' (合格) + +wms_instock_print: + - inspection_type = '2' (合格) + - apportion_qty = 100 (不变) + +【步骤3】PDA扫码入库 +wms_instock_record: + - instock_qty = 100 + +wms_inventory: + - inventory_qty = 100 +``` + +**示例2:部分不合格,让步接收不通过** + +``` +批次:BATCH002,总数:100件 + +【步骤1】PDA质检完成 +qc_inspection_main: + - qualified_qty = 80 + - unqualified_qty = 20 + +wms_instock_print: + - inspection_type = '1' (质检中) + +【步骤2】让步接收评审不通过 +qc_unqualified_review: + - review_result = '0' (报废) + +wms_instock_print: + - inspection_type = '2' (合格) + - apportion_qty = 80 ← 更新为合格数量 + +【步骤3】PDA扫码入库 +wms_instock_record: + - instock_qty = 80 (只能入库80件) + +wms_inventory: + - inventory_qty = 80 +``` + +#### 9.5.2 数据一致性验证表 + +| 场景 | 质检结果 | PDA质检后WMS状态 | 评审结果 | 评审后WMS状态 | apportion_qty | 可入库数量 | +|-----|---------|---------------|---------|--------------|--------------|-----------| +| 全部合格 | 100合格+0不合格 | '2' | - | - | 100 | 100 | +| 部分不合格+通过 | 80合格+20不合格 | '1' | 通过 | '2' | 100 | 100 | +| 部分不合格+不通过 | 80合格+20不合格 | '1' | 不通过 | '2' | 80 | 80 | +| 全部不合格+通过 | 0合格+100不合格 | '3' | 通过 | '2' | 100 | 100 | +| 全部不合格+不通过 | 0合格+100不合格 | '3' | 不通过 | '2' | 0 | 0 | + +### 9.6 接口时序图 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 让步接收完整时序图 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ 【角色】 │ +│ PDA → PDA设备 │ +│ QMS → 质量管理系统 │ +│ WMS → 仓储管理系统 │ +│ │ +│ 【场景:部分不合格(80+20),让步接收通过】 │ +│ │ +│ PDA QMS WMS │ +│ │ │ │ │ +│ │────质检完成──>│ │ │ +│ │ │ │ │ +│ │ ├─80合格+20不合格 │ +│ │ │ │ │ +│ │ └─────Dubbo回调───────>│ │ +│ │ │ │ │ +│ │ │ ├─ inspection_type='1' │ +│ │ │ │ (保持质检中) │ +│ │ │ │ │ +│ │ ├─创建不合格评审───────>│ │ +│ │ │ │ │ +│ │ │ │ │ +│ 用户 │ │ │ +│ │ │ │ │ +│ │────评审通过──>│ │ │ +│ │ │ │ │ +│ │ ├─累加合格数量(80+20=100) │ +│ │ │ │ │ +│ │ └─────Dubbo回调───────>│ │ +│ │ │ │ │ +│ │ │ ├─ inspection_type='2' │ +│ │ │ │ (合格) │ +│ │ │ │ │ +│ │ │ │ │ +│ │────扫码入库──>│ │ │ +│ │ │ │ │ +│ │ │ ├─ inspection_type='2' ✓ │ +│ │ │ ├─ 创建入库记录 │ +│ │ │ └─ 更新库存(100) │ +│ │ │ │ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 9.7 接口定义更新 + +#### 9.7.1 IQcWmsCallbackService 接口新增方法 + +```java +/** + * QMS回调WMS服务接口 + */ +public interface IQcWmsCallbackService { + + /** + * 质检完成后回调WMS + * + * @param main 质检主表 + * @return 是否成功 + */ + boolean notifyWmsInspectionComplete(QcInspectionMainVo main); + + /** + * 让步接收评审通过后回调WMS + * + * @param review 不合格品评审记录 + * @return 是否成功 + */ + boolean notifyWmsForConcessionAccepted(QcUnqualifiedReviewVo review); + + /** + * 让步接收评审不通过后回调WMS + * + * @param review 不合格品评审记录 + * @param qualifiedQty 合格数量 + * @return 是否成功 + */ + boolean notifyWmsForConcessionRejected(QcUnqualifiedReviewVo review, BigDecimal qualifiedQty); + + // ... 其他现有方法 +} +``` + +### 9.8 关键注意事项 + +1. **状态保持原则**:部分不合格或全部不合格时,WMS状态保持为'1'或'3',等待让步接收评审 +2. **数量更新时机**:只在让步接收不通过时更新apportion_qty +3. **PDA入库前提**:基于 `inspectionRequest`(质检要求)和 `inspectionType`(质检状态)组合判断 + - `inspectionRequest='1'`(免检)→ 允许入库 + - `inspectionRequest='0'`(必检)且 `inspectionType='2'`(合格)→ 允许入库 + - 其他情况不允许入库 +4. **分布式事务**:让步接收处理使用@GlobalTransactional保证一致性 +5. **批次号一致性**:全流程使用同一批次号,确保可追溯 +6. **方法抽离与可配置性**: + - 质检完成回调方法已抽离为 `callbackWmsAfterInspection()`,便于替换或禁用 + - 让步接收回调方法已抽离为 `callbackWmsForConcessionAccepted()` 和 `callbackWmsForConcessionRejected()` + - 可通过修改检测类型判断逻辑(如 `if (!"4".equals(inspectionType))`)来控制回调行为 + - 可通过添加业务开关(如 `ENABLE_CONCESSION_CALLBACK`)来启用/禁用让步接收回调 + - Dubbo调用可替换为REST API或其他服务调用方式 --- @@ -868,6 +1626,20 @@ public class QcUnqualifiedReviewServiceImpl { | 0 | 必检 | 需要质检 | | 1 | 免检 | 不需要质检 | +#### qc_inspection_type(检测类型) + +| 字典值 | 字典标签 | 说明 | +|-------|---------|------| +| 0 | 首检 | 生产首件检验 | +| 1 | 专检 | 专职检验 | +| 2 | 自检 | 操作工自检 | +| 3 | 互检 | 互检 | +| **4** | **原材料检** | **WMS入库默认使用,需回调WMS** | +| 5 | 抽检 | 抽样检验 | +| 6 | 成品检 | 成品出厂检验 | +| 7 | 入库检 | 入库时检验 | +| 8 | 出库检 | 出库时检验 | + #### wms_inbound_status(入库状态) | 字典值 | 字典标签 | 说明 |