From 566df42b505af34fe5ad1c95ec6edec36d7ce713 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 14 Jan 2026 18:10:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(qms):=20=E6=96=B0=E5=A2=9E=E8=B4=A8?= =?UTF-8?q?=E6=A3=80=E7=B1=BB=E5=9E=8B=E5=92=8C=E5=A4=9A=E7=BA=A7=E9=99=8D?= =?UTF-8?q?=E7=BA=A7=E5=8C=B9=E9=85=8D=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增出库检类型(编号8)并统一更新所有质检类型定义 - 实现质检模板多级降级匹配策略,支持物料/工位/工序精确匹配 - 添加通用模板功能和应用级缓存优化性能 - 增加检测方法字段和多值查询支持 - 完善PDA质检任务生成逻辑和异常提示信息 - 新增WMS入库质检任务接口DTO --- .../model/bo/GenerateInspectionTaskBo.java | 2 +- .../qms/api/dto/WmsInspectionTaskRequest.java | 99 +++++++++ .../qms/domain/QcInspectionItemCategory.java | 2 +- .../qms/domain/QcInspectionResult.java | 4 + .../qms/domain/QcInspectionTemplate.java | 8 +- .../dromara/qms/domain/QcInspectionType.java | 2 +- .../domain/bo/QcInspectionItemCategoryBo.java | 2 +- .../qms/domain/bo/QcInspectionMainBo.java | 7 +- .../qms/domain/bo/QcInspectionTemplateBo.java | 6 +- .../qms/domain/bo/QcInspectionTypeBo.java | 4 +- .../qms/domain/dto/QcInspectionMainTask.java | 2 +- .../domain/vo/QcInspectionItemCategoryVo.java | 2 +- .../qms/domain/vo/QcInspectionResultVo.java | 16 ++ .../qms/domain/vo/QcInspectionTemplateVo.java | 8 +- .../qms/domain/vo/QcInspectionTypeVo.java | 4 +- .../service/IQcInspectionTemplateService.java | 13 ++ .../impl/QcInspectionMainServiceImpl.java | 33 ++- .../impl/QcInspectionTemplateServiceImpl.java | 188 +++++++++++++++++- .../qms/service/impl/QcPDAServiceImpl.java | 105 ++++++++-- .../QcInspectionMainFileRelationMapper.xml | 31 +++ 20 files changed, 499 insertions(+), 39 deletions(-) create mode 100644 ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/WmsInspectionTaskRequest.java diff --git a/ruoyi-api/hwmom-api-pda/src/main/java/org/dromara/pda/api/model/bo/GenerateInspectionTaskBo.java b/ruoyi-api/hwmom-api-pda/src/main/java/org/dromara/pda/api/model/bo/GenerateInspectionTaskBo.java index 239b4e3e..db9f1e15 100644 --- a/ruoyi-api/hwmom-api-pda/src/main/java/org/dromara/pda/api/model/bo/GenerateInspectionTaskBo.java +++ b/ruoyi-api/hwmom-api-pda/src/main/java/org/dromara/pda/api/model/bo/GenerateInspectionTaskBo.java @@ -24,7 +24,7 @@ public class GenerateInspectionTaskBo implements Serializable { /** * qc_inspection_type表的qc_inspection_type - * 0首检 1专检 2自检 3互检 4原材料检 5抽检 6成品检 7入库检 + * 首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8 */ private String qcInspectionType; diff --git a/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/WmsInspectionTaskRequest.java b/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/WmsInspectionTaskRequest.java new file mode 100644 index 00000000..86cc16c6 --- /dev/null +++ b/ruoyi-api/hwmom-api-qms/src/main/java/org/dromara/qms/api/dto/WmsInspectionTaskRequest.java @@ -0,0 +1,99 @@ +package org.dromara.qms.api.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * WMS 入库质检任务请求 DTO + *

+ * 用于 WMS 调用 QMS 创建质检任务时传递参数 + * + * @author zch + * @date 2026-1-14 + */ +@Data +@NoArgsConstructor +public class WmsInspectionTaskRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 入库单号 + *

+ * 用于关联质检单与入库单,质检完成后回写入库状态 + */ + private String instockCode; + + /** + * 物料编码 + *

+ * 必填,用于质检模板匹配(8级降级策略中物料=200分) + */ + private String materialCode; + + /** + * 物料名称 + */ + private String materialName; + + /** + * 质检数量 + *

+ * 必填,从入库单物料数量获取 + */ + private BigDecimal inspectionQty; + + /** + * 批次号 + */ + private String batchCode; + + /** + * 工位名称/编码 + *

+ * 可选,用于质检模板精确匹配(工位=20分) + */ + private String stationName; + + /** + * 工序编码 + *

+ * 可选,用于质检模板精确匹配(工序=2分) + */ + private String processCode; + + /** + * 检验类型 + *

+ * 必填,WMS 入库固定传 "7"(入库检) + * 检验类型字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8 + */ + private String inspectionType; + + /** + * 供应商名称 + *

+ * 可选,用于记录来源 + */ + private String supplierName; + + /** + * 生产订单号 + *

+ * 可选,用于追溯 + */ + private String productionOrder; + + /** + * 车间 + *

+ * 可选,用于数据权限过滤 + */ + private String workshop; + +} diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionItemCategory.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionItemCategory.java index b03ed86c..6aee2c64 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionItemCategory.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionItemCategory.java @@ -70,7 +70,7 @@ public class QcInspectionItemCategory extends TenantEntity { private String typeName;//join /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ @TableField(exist = false) private String qcInspectionType;//join diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionResult.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionResult.java index 2a20a9be..02c7cf6e 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionResult.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionResult.java @@ -134,5 +134,9 @@ public class QcInspectionResult extends TenantEntity { @TableLogic private String delFlag; + @TableField(exist = false) + private String method; + @TableField(exist = false) + private String methodName; } diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionTemplate.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionTemplate.java index c5a7fac8..4c645450 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionTemplate.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionTemplate.java @@ -87,6 +87,12 @@ public class QcInspectionTemplate extends TenantEntity { */ private String description; + /** + * 是否通用模板(0否/1是) + * 通用模板不绑定具体物料/工序/工位,仅按检测类型适用 + */ + private String isDefault; + /** * 是否删除(0表示存在,2表示删除) */ @@ -106,7 +112,7 @@ public class QcInspectionTemplate extends TenantEntity { private String typeName;//JOIN /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ @TableField(exist = false) private String qcInspectionType;//JOIN diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionType.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionType.java index 3157d11b..9a470426 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionType.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/QcInspectionType.java @@ -38,7 +38,7 @@ public class QcInspectionType extends TenantEntity { private String typeName; /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ private String qcInspectionType; diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionItemCategoryBo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionItemCategoryBo.java index 05c68d5a..ad349f98 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionItemCategoryBo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionItemCategoryBo.java @@ -63,7 +63,7 @@ public class QcInspectionItemCategoryBo extends BaseEntity { private String typeName;//join /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ private String qcInspectionType;//join diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionMainBo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionMainBo.java index dea57ee5..92de85ad 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionMainBo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionMainBo.java @@ -158,10 +158,15 @@ public class QcInspectionMainBo extends BaseEntity { private String templateName;//join /** - * 检测类型字典 + * 检测类型字典(单值查询) */ private String qcInspectionType; + /** + * 检测类型字典列表(多值查询,用于产品检等需要查询多个类型的场景) + */ + private List qcInspectionTypes; + /** * 检测类型名称 */ diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTemplateBo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTemplateBo.java index 8e89b755..3bebcd24 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTemplateBo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTemplateBo.java @@ -88,6 +88,10 @@ public class QcInspectionTemplateBo extends BaseEntity { */ private String description; + /** + * 是否通用模板(0否/1是) + */ + private String isDefault; /** * 检测类型编码 @@ -100,7 +104,7 @@ public class QcInspectionTemplateBo extends BaseEntity { private String typeName;//JOIN /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ private String qcInspectionType;//JOIN diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTypeBo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTypeBo.java index c31e2f5b..4c20de29 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTypeBo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/bo/QcInspectionTypeBo.java @@ -39,9 +39,9 @@ public class QcInspectionTypeBo extends BaseEntity { private String typeName; /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ - // @NotNull(message = "检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检)不能为空", groups = { AddGroup.class, EditGroup.class }) + // @NotNull(message = "检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8)不能为空", groups = { AddGroup.class, EditGroup.class }) private String qcInspectionType; /** diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/dto/QcInspectionMainTask.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/dto/QcInspectionMainTask.java index 9ae96bb7..033b8a04 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/dto/QcInspectionMainTask.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/dto/QcInspectionMainTask.java @@ -33,7 +33,7 @@ public class QcInspectionMainTask { private String inspectionQty; /** - * 检验类型(0首检 1专检 2自检 3互检 4原材料检 5抽检 6成品检 7入库检) + * 检验类型(首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) * 必填,上位机写死传7入库检 */ @JsonProperty("InspectionType") diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemCategoryVo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemCategoryVo.java index fd35bae5..21a4fa07 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemCategoryVo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionItemCategoryVo.java @@ -78,7 +78,7 @@ public class QcInspectionItemCategoryVo implements Serializable { private String typeName;//join /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ private String qcInspectionType;//join 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 02778e60..ec2d49a3 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 @@ -1,5 +1,7 @@ package org.dromara.qms.domain.vo; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; import java.math.BigDecimal; import org.dromara.qms.domain.QcInspectionResult; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; @@ -107,6 +109,20 @@ public class QcInspectionResultVo implements Serializable { @ExcelDictFormat(readConverterExp = "快=照") private Long typeId; + /** + * 检测方法(0目视,1千分尺) + */ + @ExcelProperty(value = "检测方法", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "qc_methond") + @Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "qc_methond") + private String methodName; + +// /** +// * 检测方法名称 +// */ +// @ExcelProperty(value = "检测方法名称") +// private String methodName; + /** * 检测方式(0定性,1定量)(快照) */ diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTemplateVo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTemplateVo.java index cd6036db..d968a483 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTemplateVo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTemplateVo.java @@ -106,6 +106,12 @@ public class QcInspectionTemplateVo implements Serializable { @ExcelProperty(value = "模板说明") private String description; + /** + * 是否通用模板(0否/1是) + */ + @ExcelProperty(value = "是否通用模板") + private String isDefault; + /** * 检测类型编码 */ @@ -117,7 +123,7 @@ public class QcInspectionTemplateVo implements Serializable { private String typeName;//JOIN /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ private String qcInspectionType;//JOIN diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTypeVo.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTypeVo.java index 67fea006..ee0c2409 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTypeVo.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/domain/vo/QcInspectionTypeVo.java @@ -47,9 +47,9 @@ public class QcInspectionTypeVo implements Serializable { private String typeName; /** - * 检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检) + * 检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) */ - @ExcelProperty(value = "检测类型(字典:首检 专检 自检 互检 原材料检 抽检 成品检)", converter = ExcelDictConvert.class) + @ExcelProperty(value = "检测类型(字典:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8)", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "qc_inspection_type") private String qcInspectionType; diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcInspectionTemplateService.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcInspectionTemplateService.java index 200a7ea2..6fcb3e23 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcInspectionTemplateService.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/IQcInspectionTemplateService.java @@ -58,6 +58,19 @@ public interface IQcInspectionTemplateService { */ Boolean updateByBo(QcInspectionTemplateBo bo); + /** + * 多级降级匹配策略获取质检模板 + * 匹配优先级:物料相关(精确优先) > 工位相关(精确优先) > 通用模板 + * + * @param materialCode 物料编码 + * @param stationCode 工位编码 + * @param processCode 工序编码 + * @param qcInspectionType 检测类型(首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) + * @return 匹配的质检模板 + */ + QcInspectionTemplateVo getMatchedTemplate(String materialCode, String stationCode, + String processCode, String qcInspectionType); + /** * 校验并批量删除检测模板主信息 * diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionMainServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionMainServiceImpl.java index 9867f449..55e714bf 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionMainServiceImpl.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionMainServiceImpl.java @@ -1,31 +1,41 @@ package org.dromara.qms.service.impl; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.github.yulichang.toolkit.JoinWrappers; -import com.github.yulichang.wrapper.MPJLambdaWrapper; -import lombok.RequiredArgsConstructor; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.dubbo.config.annotation.DubboReference; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.api.RemoteCodeRuleService; import org.dromara.qms.domain.QcInspectionMain; import org.dromara.qms.domain.QcInspectionResult; import org.dromara.qms.domain.QcInspectionTemplate; import org.dromara.qms.domain.QcInspectionType; 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.QcInspectionMainVo; +import org.dromara.qms.domain.vo.QcTemplateItemVo; +import org.dromara.qms.domain.vo.QcInspectionTemplateVo; import org.dromara.qms.mapper.QcInspectionMainMapper; import org.dromara.qms.mapper.QcInspectionResultMapper; 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.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import lombok.RequiredArgsConstructor; /** * 质检任务Service业务层处理 @@ -38,9 +48,10 @@ import java.util.Map; public class QcInspectionMainServiceImpl implements IQcInspectionMainService { private final QcInspectionMainMapper baseMapper; - private final IQcInspectionResultService qcInspectionResultService; private final QcInspectionResultMapper qcInspectionResultMapper; + private final IQcInspectionTemplateService qcInspectionTemplateService; + private final IQcTemplateItemService qcTemplateItemService; /** @@ -107,7 +118,9 @@ public class QcInspectionMainServiceImpl implements IQcInspectionMainService { .eq(bo.getInspectionType() != null, QcInspectionMain::getInspectionType, bo.getInspectionType()) // .eq(bo.getStatus() != null, QcInspectionMain::getStatus, bo.getStatus()) - .eq(StringUtils.isNotBlank(bo.getQcInspectionType()), QcInspectionType::getQcInspectionType, bo.getQcInspectionType()) + // 支持单值和多值查询检测类型字典 + .eq(StringUtils.isNotBlank(bo.getQcInspectionType()) && (bo.getQcInspectionTypes() == null || bo.getQcInspectionTypes().isEmpty()), QcInspectionType::getQcInspectionType, bo.getQcInspectionType()) + .in(bo.getQcInspectionTypes() != null && !bo.getQcInspectionTypes().isEmpty(), QcInspectionType::getQcInspectionType, bo.getQcInspectionTypes()) .eq(StringUtils.isNotBlank(bo.getStatus()), QcInspectionMain::getStatus, bo.getStatus()) .eq(StringUtils.isNotBlank(bo.getInspector()), QcInspectionMain::getInspector, bo.getInspector()) .eq(StringUtils.isNotBlank(bo.getShift()), QcInspectionMain::getShift, bo.getShift()) diff --git a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionTemplateServiceImpl.java b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionTemplateServiceImpl.java index d2ac13bd..019587d5 100644 --- a/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionTemplateServiceImpl.java +++ b/ruoyi-modules/hwmom-qms/src/main/java/org/dromara/qms/service/impl/QcInspectionTemplateServiceImpl.java @@ -1,5 +1,8 @@ package org.dromara.qms.service.impl; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.TableDataInfo; @@ -20,6 +23,7 @@ import org.dromara.qms.service.IQcInspectionTemplateService; import java.util.List; import java.util.Map; import java.util.Collection; +import java.util.stream.Collectors; /** * 检测模板主Service业务层处理 @@ -27,12 +31,20 @@ import java.util.Collection; * @author zch * @date 2025-07-14 */ +@Slf4j @RequiredArgsConstructor @Service public class QcInspectionTemplateServiceImpl implements IQcInspectionTemplateService { private final QcInspectionTemplateMapper baseMapper; + // ========== 应用级缓存:所有质检模板 ========== + // 应用启动时一次性加载,避免运行时频繁查询数据库 + private volatile List templateCache; + + // 按检测类型分组的缓存(加速查找) + private volatile Map> templatesByTypeCache; + /** * 查询检测模板主 * @@ -93,6 +105,7 @@ public class QcInspectionTemplateServiceImpl implements IQcInspectionTemplateSer .like(StringUtils.isNotBlank(bo.getSupplierName()), QcInspectionTemplate::getSupplierName, bo.getSupplierName()) .eq(StringUtils.isNotBlank(bo.getDescription()), QcInspectionTemplate::getDescription, bo.getDescription()) .eq(StringUtils.isNotBlank(bo.getQcInspectionType()), QcInspectionType::getQcInspectionType, bo.getQcInspectionType()) + .eq(StringUtils.isNotBlank(bo.getIsDefault()), QcInspectionTemplate::getIsDefault, bo.getIsDefault()) .orderByDesc(QcInspectionTemplate::getCreateTime); return lqw; } @@ -110,6 +123,7 @@ public class QcInspectionTemplateServiceImpl implements IQcInspectionTemplateSer boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setTemplateId(add.getTemplateId()); + refreshCache(); // 刷新应用级缓存 } return flag; } @@ -124,14 +138,178 @@ public class QcInspectionTemplateServiceImpl implements IQcInspectionTemplateSer public Boolean updateByBo(QcInspectionTemplateBo bo) { QcInspectionTemplate update = MapstructUtils.convert(bo, QcInspectionTemplate.class); validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + refreshCache(); // 刷新应用级缓存 + } + return flag; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(QcInspectionTemplate entity){ - //TODO 做一些数据校验,如唯一约束 + // 校验1:通用模板不能绑定物料/工序/工位 + if ("1".equals(entity.getIsDefault())) { + boolean hasBinding = StringUtils.isNotBlank(entity.getMaterialCode()) + || StringUtils.isNotBlank(entity.getStationCode()) + || StringUtils.isNotBlank(entity.getProcessCode()); + if (hasBinding) { + throw new ServiceException("通用模板不能绑定具体物料、工序或工位!"); + } + } + + // 校验2:每种检测类型只能有一个通用模板 + if ("1".equals(entity.getIsDefault()) && entity.getTypeId() != null) { + QcInspectionTemplateBo checkBo = new QcInspectionTemplateBo(); + checkBo.setTypeId(entity.getTypeId()); + checkBo.setIsDefault("1"); + List existingDefaults = queryList(checkBo); + boolean hasOtherDefault = existingDefaults.stream() + .anyMatch(vo -> !vo.getTemplateId().equals(entity.getTemplateId())); + if (hasOtherDefault) { + throw new ServiceException("该检测类型已存在通用模板!"); + } + } + } + + /** + * 多级降级匹配策略获取质检模板(应用层缓存版,零数据库查询) + * 匹配优先级:物料+工位+工序 > 物料+工位 > 物料+工序 > 物料 > 工位+工序 > 工位 > 工序 > 通用模板 + * 赋分规则:物料=200, 工位=20, 工序=2, 通用模板=0(兜底) + * + * @param materialCode 物料编码 + * @param stationCode 工位编码 + * @param processCode 工序编码 + * @param qcInspectionType 检测类型(首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8) + * @return 匹配的质检模板 + */ + @Override + public QcInspectionTemplateVo getMatchedTemplate(String materialCode, String stationCode, + String processCode, String qcInspectionType) { + if (StringUtils.isBlank(qcInspectionType)) { + throw new ServiceException("检测类型不能为空!"); + } + + // 确保缓存已初始化 + if (templatesByTypeCache == null) { + synchronized (this) { + if (templatesByTypeCache == null) { + refreshCache(); + } + } + } + + // 从缓存获取该检测类型的所有模板 + List typeTemplates = templatesByTypeCache.get(qcInspectionType); + if (typeTemplates == null || typeTemplates.isEmpty()) { + throw new ServiceException("检测类型[" + qcInspectionType + "]无可用检测模板!"); + } + + // 【修复】阶段1:在非通用模板中找最佳匹配(分数>0) + QcInspectionTemplateVo bestMatch = typeTemplates.stream() + .filter(t -> !"1".equals(t.getIsDefault())) // 排除通用模板 + .max((t1, t2) -> { + int score1 = calculateMatchScore(t1, materialCode, stationCode, processCode); + int score2 = calculateMatchScore(t2, materialCode, stationCode, processCode); + return Integer.compare(score1, score2); + }) + .filter(t -> calculateMatchScore(t, materialCode, stationCode, processCode) > 0) + .orElse(null); + + // 【修复】阶段2:如果没有非通用模板匹配,使用通用模板兜底 + if (bestMatch == null) { + bestMatch = typeTemplates.stream() + .filter(t -> "1".equals(t.getIsDefault())) // 只查找通用模板 + .findFirst() + .orElse(null); + + if (bestMatch != null) { + log.info("使用通用模板兜底:{},检测类型:{}", bestMatch.getTemplateName(), qcInspectionType); + } + } + + if (bestMatch == null) { + throw new ServiceException("[" + + (StringUtils.isNotBlank(materialCode) ? "物料=" + materialCode + " " : "") + + (StringUtils.isNotBlank(stationCode) ? "工位=" + stationCode + " " : "") + + (StringUtils.isNotBlank(processCode) ? "工序=" + processCode + " " : "") + + "检测类型=" + qcInspectionType + "]无匹配模板!"); + } + + int score = calculateMatchScore(bestMatch, materialCode, stationCode, processCode); + log.info("匹配到模板:{},分数:{}", bestMatch.getTemplateName(), score); + return bestMatch; + } + + /** + * 计算非通用模板的匹配分数 + * 赋分规则:物料=200, 工位=20, 工序=2, 通用模板=0(不参与分数计算,单独处理) + * + * 分数计算示例: + * - 物料+工位+工序 = 200+20+2 = 222分(最高优先级) + * - 物料+工位 = 200+20 = 220分 + * - 物料+工序 = 200+2 = 202分 + * - 物料 = 200 = 200分 + * - 工位+工序 = 20+2 = 22分 + * - 工位 = 20 = 20分 + * - 工序 = 2 = 2分 + * - 通用模板 = 0 = 0分(兜底,不参与分数比较) + */ + private int calculateMatchScore(QcInspectionTemplateVo template, + String materialCode, String stationCode, String processCode) { + // 通用模板返回0分,不参与分数计算,单独作为兜底策略 + if ("1".equals(template.getIsDefault())) { + return 0; + } + + int score = 0; + + // 物料匹配(最高权重:200分) + if (StringUtils.isNotBlank(materialCode) && materialCode.equals(template.getMaterialCode())) { + score += 200; + } + + // 工位匹配(中等权重:20分) + if (StringUtils.isNotBlank(stationCode) && stationCode.equals(template.getStationCode())) { + score += 20; + } + + // 工序匹配(较低权重:2分) + if (StringUtils.isNotBlank(processCode) && processCode.equals(template.getProcessCode())) { + score += 2; + } + + return score; + } + + /** + * 初始化缓存(应用启动时自动调用) + */ + @PostConstruct + public synchronized void initCache() { + refreshCache(); + } + + /** + * 刷新缓存(模板增删改时调用) + */ + public synchronized void refreshCache() { + log.info("开始加载质检模板缓存..."); + + // 一次性查询所有模板 + QcInspectionTemplateBo bo = new QcInspectionTemplateBo(); + templateCache = queryList(bo); + + // 按检测类型分组,加速后续查找 + templatesByTypeCache = templateCache.stream() + .collect(Collectors.groupingBy(t -> { + String type = t.getQcInspectionType(); + return StringUtils.isNotBlank(type) ? type : ""; + })); + + log.info("质检模板缓存加载完成,共{}条,检测类型数:{}", + templateCache.size(), templatesByTypeCache.size()); } /** @@ -146,6 +324,10 @@ public class QcInspectionTemplateServiceImpl implements IQcInspectionTemplateSer if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } - return baseMapper.deleteByIds(ids) > 0; + boolean flag = baseMapper.deleteByIds(ids) > 0; + if (flag) { + refreshCache(); // 刷新应用级缓存 + } + return flag; } } 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 363ecc46..31d94cb5 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 @@ -60,17 +60,58 @@ public class QcPDAServiceImpl implements IQcPDAService { private final IQcInspectionMainFileRelationService qcInspectionMainFileRelationService; - @DubboReference + @DubboReference(timeout = 300000) private final RemotePdaMesApiService remotePdaMesApiService; - @DubboReference + @DubboReference(timeout = 300000) private final RemoteCodeRuleService remoteCodeRuleService; //WARM-FLOW工作流 - @DubboReference(timeout = 30000) + @DubboReference(timeout = 300000) private final RemoteWorkflowService remoteWorkflowService; + + // 工位信息和工序信息服务(用于通过名称查询编码) + private final IProdBaseStationInfoService prodBaseStationInfoService; + private final IProdBaseProcessInfoService prodBaseProcessInfoService; + private static final String UNQUALIFIED_REVIEW_FLOW_CODE = "unqualified_review"; + /** + * 根据工位名称(机台名称)查询工位编码 + * FIXME: 后期需要优化缓存策略,避免每次查询都访问数据库 + * TODO: 考虑在应用启动时缓存工位名称→编码的映射关系 + * + * @param stationName 工位名称(来自 ProdQmsPlanDetail.releaseName,对应机台名称) + * @return 工位编码,如果查询不到返回 null + */ + private String getStationCodeByName(String stationName) { + if (StringUtils.isBlank(stationName)) { + return null; + } + ProdBaseStationInfoBo bo = new ProdBaseStationInfoBo(); + bo.setStationName(stationName); + List list = prodBaseStationInfoService.queryList(bo); + return (list != null && !list.isEmpty()) ? list.get(0).getStationCode() : null; + } + + /** + * 根据工序名称查询工序编码 + * FIXME: 后期需要优化缓存策略,避免每次查询都访问数据库 + * TODO: 考虑在应用启动时缓存工序名称→编码的映射关系 + * + * @param processName 工序名称(来自 ProdQmsPlanDetail.processName) + * @return 工序编码,如果查询不到返回 null + */ + private String getProcessCodeByName(String processName) { + if (StringUtils.isBlank(processName)) { + return null; + } + ProdBaseProcessInfoBo bo = new ProdBaseProcessInfoBo(); + bo.setProcessName(processName); + List list = prodBaseProcessInfoService.queryList(bo); + return (list != null && !list.isEmpty()) ? list.get(0).getProcessCode() : null; + } + /** * 通过planDetailId和processId生成质检任务 @@ -88,20 +129,28 @@ public class QcPDAServiceImpl implements IQcPDAService { } QcInspectionMainBo selectMain = new QcInspectionMainBo(); selectMain.setPlanDetailId(planDetail.getPlanDetailId()); + selectMain.setQcInspectionType(bo.getQcInspectionType()); // 按质检类型筛选 selectMain.setStatus("0");//单据状态(0未处理/1完成) List mainVoList = qcInspectionMainService.queryList(selectMain); if (!mainVoList.isEmpty()){ - throw new ServiceException("此生产计划已生成质检任务且未处理!"); + // 获取已存在的未处理质检任务信息 + QcInspectionMainVo existingTask = mainVoList.get(0); + String existingInspectionNo = existingTask.getInspectionNo(); + String typeName = StringUtils.isNotBlank(existingTask.getTypeName()) + ? existingTask.getTypeName() + : getInspectionTypeName(bo.getQcInspectionType()); + throw new ServiceException("【" + existingInspectionNo + "】" + typeName + "任务未质检,当前状态为未判定,请先完成该质检任务!"); } + // 使用多级降级匹配策略:物料相关 > 工位相关 > 通用模板 + // FIXME: ProdQmsPlanDetail.releaseName 是机台名称(对应工位名称),需要查询工位编码 + // FIXME: ProdQmsPlanDetail.processName 是工序名称,需要查询工序编码 + // TODO: 后期建议 PDA API 直接返回编码,避免跨模块查询 String materialCode = planDetail.getMaterialCode(); - QcInspectionTemplateBo templateBo = new QcInspectionTemplateBo(); - templateBo.setMaterialCode(materialCode); - templateBo.setQcInspectionType(bo.getQcInspectionType()); - List templateVos = qcInspectionTemplateService.queryList(templateBo); - if (templateVos.size() != 1) { - throw new ServiceException("此物料无可用检测模板!"); - } - QcInspectionTemplateVo templateVo = templateVos.get(0); + String stationCode = getStationCodeByName(planDetail.getReleaseName()); // 根据机台名称查询工位编码 + String processCode = getProcessCodeByName(planDetail.getProcessName()); // 根据工序名称查询工序编码 + QcInspectionTemplateVo templateVo = qcInspectionTemplateService.getMatchedTemplate( + materialCode, stationCode, processCode, bo.getQcInspectionType() + ); QcInspectionMainBo inspectionBo = new QcInspectionMainBo(); inspectionBo.setInspectionNo(inspectionNo); inspectionBo.setPlanDetailId(planDetail.getPlanDetailId()); @@ -436,5 +485,37 @@ public class QcPDAServiceImpl implements IQcPDAService { return true; } + /** + * 根据质检类型代码获取质检类型名称 + * @param qcInspectionType 质检类型代码:首检=0, 专检=1, 自检=2, 互检=3, 原材料检=4, 抽检=5, 成品检=6, 入库检=7,出库检=8 + * @return 质检类型名称 + */ + private String getInspectionTypeName(String qcInspectionType) { + if (StringUtils.isBlank(qcInspectionType)) { + return "质检"; + } + switch (qcInspectionType) { + case "0": + return "首检"; + case "1": + return "专检"; + case "2": + return "自检"; + case "3": + return "互检"; + case "4": + return "原材料检"; + case "5": + return "抽检"; + case "6": + return "成品检"; + case "7": + return "入库检"; + case "8": + return "出库检"; + default: + return "质检"; + } + } } diff --git a/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcInspectionMainFileRelationMapper.xml b/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcInspectionMainFileRelationMapper.xml index 897c56bc..93caf65f 100644 --- a/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcInspectionMainFileRelationMapper.xml +++ b/ruoyi-modules/hwmom-qms/src/main/resources/mapper/qms/QcInspectionMainFileRelationMapper.xml @@ -4,4 +4,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + + + + + + + select relation_id, tenant_id, inspection_id, tenant_id, oss_id, create_dept, create_by, create_time, update_by, update_time, del_flag + from qc_inspection_main_file_relation + + + + +