diff --git a/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java b/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java
index 322aa191..547f56a2 100644
--- a/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java
+++ b/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/InspectionCompleteNotification.java
@@ -40,6 +40,14 @@ public class InspectionCompleteNotification implements Serializable {
*/
private String instockCode;
+ /**
+ * 批次号
+ *
+ * 必填,用于 WMS 精确匹配批次记录
+ * 同一入库单可能包含多个批次,必须通过批次号区分
+ */
+ private String batchCode;
+
/**
* 质检结果
*
diff --git a/ruoyi-api/hwmom-api-wms/pom.xml b/ruoyi-api/hwmom-api-wms/pom.xml
index 8a551c02..03ea15cf 100644
--- a/ruoyi-api/hwmom-api-wms/pom.xml
+++ b/ruoyi-api/hwmom-api-wms/pom.xml
@@ -23,6 +23,13 @@
ruoyi-common-core
+
+
+ org.dromara
+ hwmom-api-qms
+ ${revision}
+
+
org.dromara
ruoyi-common-excel
diff --git a/ruoyi-api/hwmom-api-wms/src/main/java/org/dromara/wms/api/RemoteWmsInstockService.java b/ruoyi-api/hwmom-api-wms/src/main/java/org/dromara/wms/api/RemoteWmsInstockService.java
new file mode 100644
index 00000000..eecc4e41
--- /dev/null
+++ b/ruoyi-api/hwmom-api-wms/src/main/java/org/dromara/wms/api/RemoteWmsInstockService.java
@@ -0,0 +1,36 @@
+package org.dromara.wms.api;
+
+import org.dromara.common.core.domain.R;
+import org.dromara.qms.api.dto.InspectionCompleteNotification;
+
+/**
+ * WMS 入库服务 Dubbo 接口
+ *
+ * 提供 QMS 与 WMS 集成的入库相关服务
+ *
+ * @author zch
+ * @date 2026-1-14
+ */
+public interface RemoteWmsInstockService {
+
+ /**
+ * 质检完成通知入库(备用接口,暂不实现)
+ *
+ * 业务场景:
+ * 1. QMS 质检完成后调用此接口通知 WMS
+ * 2. WMS 根据质检结果决定是否完成入库
+ * 3. 合格:完成入库,更新库存
+ * 4. 不合格:标记拒收,生成不合格品记录
+ *
+ * 说明:
+ * - 主要方式采用 REST API(POST /wsmApi/notifyInspectionComplete)
+ * - 此 Dubbo 接口作为备用方案,未来如需服务间直接调用可启用
+ *
+ * 说明:租户ID和用户ID从当前调用上下文自动获取
+ *
+ * @param notification 质检完成通知参数
+ * @return 处理结果
+ */
+ R completeInstockAfterInspection(InspectionCompleteNotification notification);
+
+}
diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/dubbo/RemoteQmsInspectionServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/dubbo/RemoteQmsInspectionServiceImpl.java
new file mode 100644
index 00000000..b696a983
--- /dev/null
+++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/dubbo/RemoteQmsInspectionServiceImpl.java
@@ -0,0 +1,84 @@
+package org.dromara.qms.dubbo;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.qms.api.RemoteQmsInspectionService;
+import org.dromara.qms.api.dto.InspectionCompleteNotification;
+import org.dromara.qms.api.dto.WmsInspectionTaskRequest;
+import org.dromara.qms.service.IQcWmsService;
+import org.springframework.stereotype.Service;
+
+/**
+ * QMS 质检服务 Dubbo 实现
+ *
+ * 为 WMS 等外部服务提供质检相关功能的 RPC 调用接口
+ *
+ * 说明:此 Dubbo 服务层仅负责处理跨服务调用的上下文传递(租户、用户),
+ * 具体业务逻辑委托给 {@link IQcWmsService} 实现
+ *
+ * @author zch
+ * @date 2026-1-14
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+@DubboService
+public class RemoteQmsInspectionServiceImpl implements RemoteQmsInspectionService {
+
+ private final IQcWmsService qcWmsService;
+
+ /**
+ * 为 WMS 入库单创建质检任务
+ *
+ * 业务说明:
+ * 1. 从 Dubbo 调用上下文获取租户ID和用户ID
+ * 2. 使用 TenantHelper.dynamic 确保租户隔离
+ * 3. 委托给 IQcWmsService 处理具体业务逻辑
+ *
+ * @param request WMS 入库质检请求参数
+ * @return 质检单号
+ */
+ @Override
+ public String createInspectionTaskForWMS(WmsInspectionTaskRequest request) {
+ // 从当前上下文获取租户ID和用户ID(由 Dubbo 调用链传递)
+ String tenantId = TenantHelper.getTenantId();
+ Long userId = LoginHelper.getUserId();
+
+ log.info("WMS调用QMS创建质检任务(Dubbo),入库单号: {}, 物料编码: {}, 租户ID: {}, 用户ID: {}",
+ request.getInstockCode(), request.getMaterialCode(), tenantId, userId);
+
+ // 使用 TenantHelper.dynamic 确保租户隔离
+ return TenantHelper.dynamic(tenantId, () -> {
+ try {
+ return qcWmsService.createInspectionTask(request);
+ } catch (ServiceException e) {
+ log.error("WMS调用QMS创建质检任务失败: {}", e.getMessage());
+ throw e;
+ } catch (Exception e) {
+ log.error("WMS调用QMS创建质检任务异常", e);
+ throw new ServiceException("创建质检任务异常: " + e.getMessage());
+ }
+ });
+ }
+
+ /**
+ * 通知 WMS 质检完成(预留接口,暂不实现)
+ *
+ * 说明:质检完成回调采用 REST API 方式实现(POST /wsmApi/notifyInspectionComplete)
+ * 此 Dubbo 接口作为备用方案,未来如需服务间直接调用可启用
+ *
+ * @param notification 质检完成通知参数
+ * @return 处理结果
+ */
+ @Override
+ public R notifyInspectionComplete(InspectionCompleteNotification notification) {
+ log.info("收到质检完成通知(Dubbo方式),质检单号: {}, 质检结果: {}", notification.getInspectionNo(), notification.getResult());
+ log.warn("当前采用 REST API 方式回调,此 Dubbo 接口暂不实现");
+ return R.ok("此接口暂未启用,请使用 REST API 方式回调");
+ }
+}
diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsServiceImpl.java
new file mode 100644
index 00000000..95995f74
--- /dev/null
+++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcWmsServiceImpl.java
@@ -0,0 +1,200 @@
+package org.dromara.qms.service.impl;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.system.api.RemoteCodeRuleService;
+import org.dromara.qms.api.dto.WmsInspectionTaskRequest;
+import org.dromara.qms.domain.bo.QcInspectionMainBo;
+import org.dromara.qms.domain.bo.QcInspectionResultBo;
+import org.dromara.qms.domain.bo.QcTemplateItemBo;
+import org.dromara.qms.domain.vo.QcInspectionTemplateVo;
+import org.dromara.qms.domain.vo.QcTemplateItemVo;
+import org.dromara.qms.service.IQcInspectionMainService;
+import org.dromara.qms.service.IQcInspectionResultService;
+import org.dromara.qms.service.IQcInspectionTemplateService;
+import org.dromara.qms.service.IQcTemplateItemService;
+import org.dromara.qms.service.IQcWmsService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * WMS仓储质检Service业务层处理
+ *
+ * 为 WMS 入库提供质检任务创建和管理功能
+ *
+ * @author zch
+ * @date 2026-01-14
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class QcWmsServiceImpl implements IQcWmsService {
+
+ private final IQcInspectionMainService qcInspectionMainService;
+ private final IQcInspectionTemplateService qcInspectionTemplateService;
+ private final IQcInspectionResultService qcInspectionResultService;
+ private final IQcTemplateItemService qcTemplateItemService;
+
+ /**
+ * 编码规则服务(Dubbo 引用)
+ * 用于生成质检单号
+ */
+ @DubboReference(timeout = 300000)
+ private final RemoteCodeRuleService remoteCodeRuleService;
+
+ /**
+ * 为 WMS 入库单创建质检任务
+ *
+ * 业务流程:
+ * 1. 参数校验(入库单号、物料编码、质检数量、检验类型必填)
+ * 2. 匹配质检模板(8级降级匹配策略)
+ * 3. 生成质检单号(调用编码规则服务)
+ * 4. 构建并保存质检主表
+ * 5. 根据模板生成质检结果子表
+ * 6. 返回质检单号
+ *
+ * @param request WMS 入库质检请求参数
+ * @return 质检单号
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String createInspectionTask(WmsInspectionTaskRequest request) {
+ log.info("WMS调用创建质检任务,入库单号: {}, 物料编码: {}, 质检数量: {}, 检验类型: {}",
+ request.getInstockCode(), request.getMaterialCode(), request.getInspectionQty(), request.getInspectionType());
+
+ // 1. 参数校验
+ validateRequest(request);
+
+ // 2. 匹配质检模板(调用已实现的8级降级匹配策略)
+ QcInspectionTemplateVo templateVo = qcInspectionTemplateService.getMatchedTemplate(
+ request.getMaterialCode(),
+ request.getStationName(),
+ request.getProcessCode(),
+ request.getInspectionType()
+ );
+
+ if (templateVo == null) {
+ throw new ServiceException("未找到匹配的质检模板,物料编码: " + request.getMaterialCode());
+ }
+
+ log.info("质检模板匹配成功,模板ID: {}, 模板名称: {}", templateVo.getTemplateId(), templateVo.getTemplateName());
+
+ // 3. 生成质检单号(调用编码规则服务)
+ String inspectionNo = remoteCodeRuleService.selectCodeRuleCode("3");
+ if (StringUtils.isBlank(inspectionNo)) {
+ throw new ServiceException("获取质检单号失败");
+ }
+
+ // 4. 构建质检主表BO
+ QcInspectionMainBo mainBo = buildInspectionMainBo(request, templateVo, inspectionNo);
+
+ // 5. 插入质检主表
+ Boolean insertResult = qcInspectionMainService.insertByBo(mainBo);
+ if (!insertResult) {
+ throw new ServiceException("质检主表保存失败");
+ }
+
+ log.info("质检主表创建成功,质检单号: {}, 质检ID: {}", inspectionNo, mainBo.getInspectionId());
+
+ // 6. 根据模板生成质检结果子表
+ generateInspectionResults(mainBo.getInspectionId(), templateVo.getTemplateId());
+
+ log.info("质检结果子表生成成功,共{}个检测项", getTemplateItemCount(templateVo.getTemplateId()));
+
+ // 7. 返回质检单号
+ return inspectionNo;
+ }
+
+ /**
+ * 参数校验
+ */
+ private void validateRequest(WmsInspectionTaskRequest request) {
+ if (StringUtils.isBlank(request.getInstockCode())) {
+ throw new ServiceException("入库单号不能为空");
+ }
+ if (StringUtils.isBlank(request.getMaterialCode())) {
+ throw new ServiceException("物料编码不能为空");
+ }
+ if (request.getInspectionQty() == null || request.getInspectionQty().compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("质检数量必须大于0");
+ }
+ if (StringUtils.isBlank(request.getInspectionType())) {
+ throw new ServiceException("检验类型不能为空");
+ }
+ }
+
+ /**
+ * 构建质检主表 BO
+ */
+ private QcInspectionMainBo buildInspectionMainBo(WmsInspectionTaskRequest request,
+ QcInspectionTemplateVo templateVo,
+ String inspectionNo) {
+ QcInspectionMainBo mainBo = new QcInspectionMainBo();
+ mainBo.setInspectionNo(inspectionNo);
+ mainBo.setTemplateId(templateVo.getTemplateId());
+ mainBo.setTemplateName(templateVo.getTemplateName());
+ mainBo.setInspectionType(templateVo.getTypeId());
+ mainBo.setMaterialCode(request.getMaterialCode());
+ mainBo.setMaterialName(request.getMaterialName());
+ mainBo.setInspectionQty(request.getInspectionQty());
+ mainBo.setProductionOrder(request.getInstockCode()); // 入库单号作为业务来源单号
+ mainBo.setRemark(request.getInstockCode()); // 备注中存储入库单号,便于后续回调
+ mainBo.setBatchNo(request.getBatchCode());
+ mainBo.setStationName(request.getStationName());
+ mainBo.setWorkshop(request.getWorkshop());
+ mainBo.setSupplierName(request.getSupplierName());
+ mainBo.setStatus("0"); // 未处理
+ mainBo.setResult("2"); // 待判定
+ mainBo.setQualifiedQty(BigDecimal.ZERO);
+ mainBo.setUnqualifiedQty(BigDecimal.ZERO);
+ return mainBo;
+ }
+
+ /**
+ * 生成质检结果子表
+ *
+ * 根据模板中的检测项生成对应的质检结果记录
+ */
+ private void generateInspectionResults(Long inspectionId, Long templateId) {
+ QcTemplateItemBo itemBo = new QcTemplateItemBo();
+ itemBo.setTemplateId(templateId);
+ List itemList = qcTemplateItemService.queryList(itemBo);
+
+ for (QcTemplateItemVo item : itemList) {
+ QcInspectionResultBo resultBo = new QcInspectionResultBo();
+ resultBo.setInspectionId(inspectionId);
+ resultBo.setItemId(item.getItemId());
+ resultBo.setDetectResult("2"); // 未判定
+ resultBo.setItemCode(item.getItemCode());
+ resultBo.setItemName(item.getItemName());
+ resultBo.setInspectionPosition(item.getInspectionPosition());
+ resultBo.setCategoryName(item.getCategoryName());
+ resultBo.setDetectType(item.getDetectType());
+ resultBo.setControlType(item.getControlType());
+ resultBo.setStandardValue(item.getStandardValue());
+ resultBo.setUpperLimit(item.getUpperLimit());
+ resultBo.setLowerLimit(item.getLowerLimit());
+ resultBo.setSpecName(item.getSpecName());
+ resultBo.setSpecUpper(item.getSpecUpper());
+ resultBo.setSpecLower(item.getSpecLower());
+ resultBo.setDescription(item.getDescription());
+ resultBo.setTypeId(item.getInspectionType());
+ qcInspectionResultService.insertByBo(resultBo);
+ }
+ }
+
+ /**
+ * 获取模板检测项数量(用于日志)
+ */
+ private int getTemplateItemCount(Long templateId) {
+ QcTemplateItemBo itemBo = new QcTemplateItemBo();
+ itemBo.setTemplateId(templateId);
+ return qcTemplateItemService.queryList(itemBo).size();
+ }
+}
diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsInstockPrintController.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsInstockPrintController.java
index f9f43c30..08315d02 100644
--- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsInstockPrintController.java
+++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsInstockPrintController.java
@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
+import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
@@ -17,11 +18,13 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.wms.domain.bo.WmsInstockPrintBo;
import org.dromara.wms.domain.vo.WmsInstockPrintVo;
+import org.dromara.wms.service.IWmsInspectionInitiationService;
import org.dromara.wms.service.IWmsInstockPrintService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
+import java.util.Map;
/**
* 入库单-物料打印条码
@@ -38,6 +41,8 @@ public class WmsInstockPrintController extends BaseController {
private final IWmsInstockPrintService wmsInstockPrintService;
+ private final IWmsInspectionInitiationService wmsInspectionInitiationService;
+
/**
* 查询入库单-物料打印条码列表
*/
@@ -119,4 +124,29 @@ public class WmsInstockPrintController extends BaseController {
return wmsInstockPrintService.printCOde(vos);
}
+ /**
+ * 批量发起质检任务
+ *
+ * 业务场景:
+ * 前端选择批次后,调用此接口创建质检任务
+ * 服务层调用 QMS Dubbo 接口创建质检任务,并更新打印记录状态为"质检中"
+ *
+ * 说明:
+ * 1. 只处理 inspectionRequest='0'(必检)且 inspectionType='0'(未发起)的批次
+ * 2. 使用 @GlobalTransactional 保证分布式事务一致性
+ * 3. 调用独立的 WmsInspectionInitiationService 处理业务逻辑
+ *
+ * @param prints 选中的打印记录列表
+ * @return Map<批次号, 质检单号或错误信息>
+ */
+ @SaCheckPermission("wms:instockPrint:createInspection")
+ @Log(title = "发起质检", businessType = BusinessType.INSERT)
+ @RepeatSubmit()
+ @PostMapping("/createInspection")
+ @GlobalTransactional(rollbackFor = Exception.class)
+ public R