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.

913 lines
38 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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_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 发起质检接口
#### 接口定义
```java
/**
* 批量发起质检任务
* 前端选择批次后调用此接口创建质检任务
*
* @param prints 选中的打印记录列表
* @return Map<批次号, 质检单号或错误信息>
*/
@PostMapping("/createInspection")
public R<Map<String, String>> createInspection(@RequestBody List<WmsInstockPrintBo> 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<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服务实现
```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<Void> 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<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 新增按钮
```vue
<!-- 在工具栏添加"发起质检"按钮 -->
<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 按钮点击处理
```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` 注解
#### 场景1WMS 调用 QMS 创建质检任务
```java
// 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 质检完成回调
```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 接口(待确认)
---
**文档结束**