You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

67 KiB

WMS-QMS 质检集成需求与方案文档

文档版本历史

版本 日期 修订内容 作者
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

目录


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_mainstatus='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 发起质检接口

接口定义

/**
 * 批量发起质检任务
 * 前端选择批次后调用此接口创建质检任务
 *
 * @param prints 选中的打印记录列表
 * @return Map<批次号, 质检单号或错误信息>
 */
@PostMapping("/createInspection")
public R<Map<String, String>> createInspection(@RequestBody List<WmsInstockPrintBo> prints)

请求参数

[
  {
    "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"
  }
]

响应结果

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "BATCH001": "QC202501150001",
    "BATCH002": "QC202501150002"
  }
}

4.2 QMS 质检完成回调接口WMS 暴露给 QMS

Dubbo 接口定义

/**
 * WMS 暴露给 QMS 的质检完成回调接口
 * QMS 质检完成后调用此接口通知 WMS
 */
public interface RemoteWmsInspectionService {
    /**
     * 质检完成回调
     * @param notification 质检完成通知
     */
    void notifyInspectionComplete(InspectionCompleteNotification notification);
}

回调参数

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发起质检服务实现

// WmsInspectionInitiationServiceImpl.java
@Service
public class WmsInspectionInitiationServiceImpl implements IWmsInspectionInitiationService {

    @DubboReference(timeout = 300000)
    private RemoteQmsInspectionService remoteQmsInspectionService;

    private final IWmsInstockPrintService instockPrintService;

    @Override
    public Map<String, String> createInspection(List<WmsInstockPrintBo> prints) {
        Map<String, String> 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服务实现

// 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<Void> result = remoteWmsInstockService.completeInstockAfterInspection(notification);
        return result != null && result.getCode() == R.SUCCESS;
    }
}

WMS接收QMS回调服务实现本地服务

// 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服务实现独立类

// RemoteWmsInspectionCallbackServiceImpl.java
@Service
@DubboService
public class RemoteWmsInspectionCallbackServiceImpl implements RemoteWmsInstockService {

    private final IWmsInspectionCallbackService wmsInspectionCallbackService;

    @Override
    public R<Void> 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 新增按钮

<!-- 在工具栏添加"发起质检"按钮 -->
<el-row :gutter="10" class="mb8">
  <el-col :span="1.5">
    <el-button
      type="primary"
      plain
      icon="VideoPlay"
      :disabled="multiple"
      @click="handleCreateInspection"
      v-hasPermi="['wms:instockPrint:createInspection']">
      发起质检
    </el-button>
  </el-col>
</el-row>

7.3 按钮点击处理

/** 发起质检按钮操作 */
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 定义

// 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 注解

场景1WMS 调用 QMS 创建质检任务

// WMS 模块
@Service
public class WmsInstockPrintServiceImpl {

    @DubboReference
    private RemoteQmsInspectionService remoteQmsInspectionService;

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)  // ← 调用方加注解
    public Map<String, String> createInspection(List<WmsInstockPrintBo> prints) {
        Map<String, String> 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;
    }
}

场景2QMS 调用 WMS 质检完成回调

// 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 配置

# 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 业务需求概述

业务定义:让步接收是指产品质量不完全符合标准,但经过评审后认为可以使用,允许按合格品入库的特殊场景。

核心原则

  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件
├─ PDA质检完成100合格 + 0不合格
│   ├─ qualified_qty=100, unqualified_qty=0
│   └─ result='0'(合格)
│
└─ QMS回调WMS
    └─ inspection_type='2'(合格)
        └─ PDA扫码入库100件

场景B部分不合格需要让步接收评审

批次 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 修改1QcPDAServiceImpl.java - 质检完成回调

文件位置hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcPDAServiceImpl.java

修改位置第370-374行

修改前

// WMS 回调:质检完成后通知 WMS 更新状态
QcInspectionMainVo updatedMain = qcInspectionMainService.queryById(inspectionId);
if (qcWmsCallbackService.isFromWmsInspection(updatedMain)) {
    qcWmsCallbackService.notifyWmsForQualified(updatedMain);
}

修改后

// WMS 回调:质检完成后通知 WMS 更新状态
QcInspectionMainVo updatedMain = qcInspectionMainService.queryById(inspectionId);
if (qcWmsCallbackService.isFromWmsInspection(updatedMain)) {
    // 调用新的回调方法:根据质检结果决定状态更新
    qcWmsCallbackService.notifyWmsInspectionComplete(updatedMain);
}

9.4.3 修改2QcWmsCallbackServiceImpl.java - 新增回调方法

文件位置hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsCallbackServiceImpl.java

方法抽离说明 以下方法已独立抽离,便于:

  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质检完成回调

/**
 * 【可配置】质检完成回调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<Void> 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让步接收通过后回调

/**
 * 【可配置】让步接收评审通过后回调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<Void> 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让步接收不通过后回调

/**
 * 【可配置】让步接收评审不通过后回调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<Void> 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() - 支持数量传递

/**
 * 构建质检完成通知参数
 *
 * @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 修改3QcUnqualifiedReviewServiceImpl.java - 评审完成处理

文件位置hwmom/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcUnqualifiedReviewServiceImpl.java

修改completeTask()方法

@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<String> 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 修改4InspectionCompleteNotification.java - 新增字段

文件位置hwmom/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java

新增字段

/**
 * 是否更新数量标记
 * <p>
 * 说明:
 * 0=不更新1=更新为qualifiedQty
 * <p>
 * 使用场景:让步接收评审不通过时,需要更新入库数量为实际合格数量
 */
private String updateQty;

public String getUpdateQty() {
    return updateQty;
}

public void setUpdateQty(String updateQty) {
    this.updateQty = updateQty;
}

9.4.6 修改5WmsInspectionCallbackServiceImpl.java - 支持更新数量

文件位置hwmom/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsInspectionCallbackServiceImpl.java

修改handleInspectionComplete()方法

@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 修改6RawInActivity.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=不合格

修改质检提交方法

// 质检提交
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 接口新增方法

/**
 * 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或其他服务调用方式

10. 附录

10.1 字典值定义

wms_inspection_type质检状态

字典值 字典标签 说明
0 未发起 打印后默认状态
1 质检中 已创建质检任务,等待执行
2 合格 质检合格
3 不合格 质检不合格

inspection_request质检要求

字典值 字典标签 说明
0 必检 需要质检
1 免检 不需要质检

qc_inspection_type检测类型

字典值 字典标签 说明
0 首检 生产首件检验
1 专检 专职检验
2 自检 操作工自检
3 互检 互检
4 原材料检 WMS入库默认使用需回调WMS
5 抽检 抽样检验
6 成品检 成品出厂检验
7 入库检 入库时检验
8 出库检 出库时检验

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 接口(待确认)

文档结束