Commit 7d45f862fcf531b85fbce7f8717dea3d58b8d309

Authored by YunaiV
1 parent 26507997

refactor(mes): 优化 RQC 模块,添加主表缺陷统计

  主要变更:
  - RQC 主表添加缺陷统计字段(致命/严重/轻微缺陷数量和缺陷率)
  - 实现主表缺陷统计逻辑,对齐 IQC 实现方式
  - 删除 sourceDocCode 字段,rqcType 重命名为 type
  - RQC Line 表 tool 字段改为 String 类型(冗余存储)
  - 删除 MesQcTemplateDetailService 中未使用的方法
  - 修复 OQC Mapper 查询条件错误
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcLineController.java
@@ -85,13 +85,6 @@ public class MesQcRqcLineController { @@ -85,13 +85,6 @@ public class MesQcRqcLineController {
85 .setIndicatorType(indicator.getType())); 85 .setIndicatorType(indicator.getType()));
86 findAndThen(unitMeasureMap, vo.getUnitMeasureId(), 86 findAndThen(unitMeasureMap, vo.getUnitMeasureId(),
87 unit -> vo.setUnitMeasureName(unit.getName())); 87 unit -> vo.setUnitMeasureName(unit.getName()));
88 - // 逐个查询工具名称(工具数量通常较少)  
89 - if (vo.getToolId() != null) {  
90 - MesTmToolDO tool = toolService.getTool(vo.getToolId());  
91 - if (tool != null) {  
92 - vo.setToolName(tool.getName());  
93 - }  
94 - }  
95 }); 88 });
96 } 89 }
97 90
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcPageReqVO.java
@@ -23,14 +23,11 @@ public class MesQcRqcPageReqVO extends PageParam { @@ -23,14 +23,11 @@ public class MesQcRqcPageReqVO extends PageParam {
23 @Schema(description = "来源单据类型", example = "RTISSUE") 23 @Schema(description = "来源单据类型", example = "RTISSUE")
24 private String sourceDocType; 24 private String sourceDocType;
25 25
26 - @Schema(description = "来源单据编号", example = "RT2025")  
27 - private String sourceDocCode;  
28 -  
29 @Schema(description = "产品物料 ID", example = "20") 26 @Schema(description = "产品物料 ID", example = "20")
30 private Long itemId; 27 private Long itemId;
31 28
32 - @Schema(description = "检验类型", example = "1")  
33 - private Integer rqcType; 29 + @Schema(description = "批次号", example = "BATCH001")
  30 + private String batchCode;
34 31
35 @Schema(description = "检测结果", example = "1") 32 @Schema(description = "检测结果", example = "1")
36 private Integer checkResult; 33 private Integer checkResult;
@@ -41,8 +38,4 @@ public class MesQcRqcPageReqVO extends PageParam { @@ -41,8 +38,4 @@ public class MesQcRqcPageReqVO extends PageParam {
41 @Schema(description = "检测人员用户 ID", example = "1") 38 @Schema(description = "检测人员用户 ID", example = "1")
42 private Long inspectorUserId; 39 private Long inspectorUserId;
43 40
44 - @Schema(description = "检测日期")  
45 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
46 - private LocalDateTime[] inspectDate;  
47 -  
48 } 41 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcRespVO.java
@@ -36,9 +36,6 @@ public class MesQcRqcRespVO { @@ -36,9 +36,6 @@ public class MesQcRqcRespVO {
36 @Schema(description = "来源单据类型", example = "RTISSUE") 36 @Schema(description = "来源单据类型", example = "RTISSUE")
37 private String sourceDocType; 37 private String sourceDocType;
38 38
39 - @Schema(description = "来源单据编号", example = "RT20250101001")  
40 - private String sourceDocCode;  
41 -  
42 @Schema(description = "来源单据行 ID", example = "300") 39 @Schema(description = "来源单据行 ID", example = "300")
43 private Long sourceLineId; 40 private Long sourceLineId;
44 41
@@ -46,7 +43,7 @@ public class MesQcRqcRespVO { @@ -46,7 +43,7 @@ public class MesQcRqcRespVO {
46 43
47 @Schema(description = "检验类型", example = "1") 44 @Schema(description = "检验类型", example = "1")
48 @ExcelProperty("检验类型") 45 @ExcelProperty("检验类型")
49 - private Integer rqcType; 46 + private Integer type;
50 47
51 // ========== 物料 ========== 48 // ========== 物料 ==========
52 49
@@ -110,6 +107,26 @@ public class MesQcRqcRespVO { @@ -110,6 +107,26 @@ public class MesQcRqcRespVO {
110 @ExcelProperty("备注") 107 @ExcelProperty("备注")
111 private String remark; 108 private String remark;
112 109
  110 + // ========== 缺陷统计 ==========
  111 +
  112 + @Schema(description = "致命缺陷数量", example = "5")
  113 + private Integer criticalQuantity;
  114 +
  115 + @Schema(description = "严重缺陷数量", example = "10")
  116 + private Integer majorQuantity;
  117 +
  118 + @Schema(description = "轻微缺陷数量", example = "15")
  119 + private Integer minorQuantity;
  120 +
  121 + @Schema(description = "致命缺陷率(%)", example = "5.00")
  122 + private BigDecimal criticalRate;
  123 +
  124 + @Schema(description = "严重缺陷率(%)", example = "10.00")
  125 + private BigDecimal majorRate;
  126 +
  127 + @Schema(description = "轻微缺陷率(%)", example = "15.00")
  128 + private BigDecimal minorRate;
  129 +
113 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 130 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
114 @ExcelProperty("创建时间") 131 @ExcelProperty("创建时间")
115 private LocalDateTime createTime; 132 private LocalDateTime createTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java
1 package cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo; 1 package cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import com.fasterxml.jackson.annotation.JsonIgnore;
3 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.AssertTrue;
  7 +import jakarta.validation.constraints.DecimalMin;
4 import jakarta.validation.constraints.NotEmpty; 8 import jakarta.validation.constraints.NotEmpty;
5 import jakarta.validation.constraints.NotNull; 9 import jakarta.validation.constraints.NotNull;
6 import lombok.Data; 10 import lombok.Data;
@@ -23,8 +27,7 @@ public class MesQcRqcSaveReqVO { @@ -23,8 +27,7 @@ public class MesQcRqcSaveReqVO {
23 @NotEmpty(message = "检验单名称不能为空") 27 @NotEmpty(message = "检验单名称不能为空")
24 private String name; 28 private String name;
25 29
26 - @Schema(description = "检验模板 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")  
27 - @NotNull(message = "检验模板不能为空") 30 + @Schema(description = "检验模板 ID", example = "100")
28 private Long templateId; 31 private Long templateId;
29 32
30 // ========== 来源单据 ========== 33 // ========== 来源单据 ==========
@@ -35,16 +38,14 @@ public class MesQcRqcSaveReqVO { @@ -35,16 +38,14 @@ public class MesQcRqcSaveReqVO {
35 @Schema(description = "来源单据类型", example = "RTISSUE") 38 @Schema(description = "来源单据类型", example = "RTISSUE")
36 private String sourceDocType; 39 private String sourceDocType;
37 40
38 - @Schema(description = "来源单据编号", example = "RT20250101001")  
39 - private String sourceDocCode;  
40 -  
41 @Schema(description = "来源单据行 ID", example = "300") 41 @Schema(description = "来源单据行 ID", example = "300")
42 private Long sourceLineId; 42 private Long sourceLineId;
43 43
44 // ========== 检验类型 ========== 44 // ========== 检验类型 ==========
45 45
46 - @Schema(description = "检验类型", example = "1")  
47 - private Integer rqcType; 46 + @Schema(description = "检验类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  47 + @NotNull(message = "检验类型不能为空")
  48 + private Integer type;
48 49
49 // ========== 物料 ========== 50 // ========== 物料 ==========
50 51
@@ -57,15 +58,19 @@ public class MesQcRqcSaveReqVO { @@ -57,15 +58,19 @@ public class MesQcRqcSaveReqVO {
57 58
58 // ========== 数量 ========== 59 // ========== 数量 ==========
59 60
60 - // TODO @AI:参考 iqc 的参数校验;  
61 -  
62 - @Schema(description = "检测数量", example = "100") 61 + @Schema(description = "检测数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  62 + @NotNull(message = "检测数量不能为空")
  63 + @DecimalMin(value = "0", message = "检测数量不能小于 0")
63 private BigDecimal checkQuantity; 64 private BigDecimal checkQuantity;
64 65
65 - @Schema(description = "合格品数量", example = "90") 66 + @Schema(description = "合格品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "90")
  67 + @NotNull(message = "合格品数量不能为空")
  68 + @DecimalMin(value = "0", message = "合格品数量不能小于 0")
66 private BigDecimal qualifiedQuantity; 69 private BigDecimal qualifiedQuantity;
67 70
68 - @Schema(description = "不合格数量", example = "10") 71 + @Schema(description = "不合格数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  72 + @NotNull(message = "不合格数量不能为空")
  73 + @DecimalMin(value = "0", message = "不合格数量不能小于 0")
69 private BigDecimal unqualifiedQuantity; 74 private BigDecimal unqualifiedQuantity;
70 75
71 // ========== 检验 ========== 76 // ========== 检验 ==========
@@ -73,13 +78,24 @@ public class MesQcRqcSaveReqVO { @@ -73,13 +78,24 @@ public class MesQcRqcSaveReqVO {
73 @Schema(description = "检测结果", example = "1") 78 @Schema(description = "检测结果", example = "1")
74 private Integer checkResult; 79 private Integer checkResult;
75 80
76 - @Schema(description = "检测日期") 81 + @Schema(description = "检测日期", requiredMode = Schema.RequiredMode.REQUIRED)
  82 + @NotNull(message = "检测日期不能为空")
77 private LocalDateTime inspectDate; 83 private LocalDateTime inspectDate;
78 84
79 - @Schema(description = "检测人员用户 ID", example = "1") 85 + @Schema(description = "检测人员用户 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  86 + @NotNull(message = "检测人员不能为空")
80 private Long inspectorUserId; 87 private Long inspectorUserId;
81 88
82 @Schema(description = "备注", example = "备注") 89 @Schema(description = "备注", example = "备注")
83 private String remark; 90 private String remark;
84 91
  92 + @AssertTrue(message = "检测数量必须等于合格品数量与不合格品数量之和")
  93 + @JsonIgnore
  94 + public boolean isQuantityValid() {
  95 + if (ObjUtil.hasNull(checkQuantity, qualifiedQuantity, unqualifiedQuantity)) {
  96 + return true; // @NotNull 会处理 null
  97 + }
  98 + return checkQuantity.compareTo(qualifiedQuantity.add(unqualifiedQuantity)) == 0;
  99 + }
  100 +
85 } 101 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/line/MesQcRqcLineRespVO.java
@@ -30,11 +30,8 @@ public class MesQcRqcLineRespVO { @@ -30,11 +30,8 @@ public class MesQcRqcLineRespVO {
30 @Schema(description = "检测指标类型", example = "QUANTITATIVE") 30 @Schema(description = "检测指标类型", example = "QUANTITATIVE")
31 private String indicatorType; 31 private String indicatorType;
32 32
33 - @Schema(description = "检测工具 ID", example = "5")  
34 - private Long toolId;  
35 -  
36 - @Schema(description = "检测工具名称", example = "卡尺")  
37 - private String toolName; 33 + @Schema(description = "检测工具", example = "卡尺")
  34 + private String tool;
38 35
39 @Schema(description = "检测方法", example = "目测") 36 @Schema(description = "检测方法", example = "目测")
40 private String checkMethod; 37 private String checkMethod;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/rqc/MesQcRqcDO.java
@@ -58,10 +58,6 @@ public class MesQcRqcDO extends BaseDO { @@ -58,10 +58,6 @@ public class MesQcRqcDO extends BaseDO {
58 */ 58 */
59 private String sourceDocType; 59 private String sourceDocType;
60 /** 60 /**
61 - * 来源单据编号  
62 - */  
63 - private String sourceDocCode;  
64 - /**  
65 * 来源单据行 ID 61 * 来源单据行 ID
66 */ 62 */
67 private Long sourceLineId; 63 private Long sourceLineId;
@@ -73,7 +69,7 @@ public class MesQcRqcDO extends BaseDO { @@ -73,7 +69,7 @@ public class MesQcRqcDO extends BaseDO {
73 * 69 *
74 * 字典 mes_rqc_type 70 * 字典 mes_rqc_type
75 */ 71 */
76 - private Integer rqcType; 72 + private Integer type;
77 73
78 // ========== 物料 ========== 74 // ========== 物料 ==========
79 75
@@ -133,6 +129,33 @@ public class MesQcRqcDO extends BaseDO { @@ -133,6 +129,33 @@ public class MesQcRqcDO extends BaseDO {
133 */ 129 */
134 private String remark; 130 private String remark;
135 131
  132 + // ========== 缺陷统计 ==========
  133 +
  134 + /**
  135 + * 致命缺陷数量
  136 + */
  137 + private Integer criticalQuantity;
  138 + /**
  139 + * 严重缺陷数量
  140 + */
  141 + private Integer majorQuantity;
  142 + /**
  143 + * 轻微缺陷数量
  144 + */
  145 + private Integer minorQuantity;
  146 + /**
  147 + * 致命缺陷率(%)
  148 + */
  149 + private BigDecimal criticalRate;
  150 + /**
  151 + * 严重缺陷率(%)
  152 + */
  153 + private BigDecimal majorRate;
  154 + /**
  155 + * 轻微缺陷率(%)
  156 + */
  157 + private BigDecimal minorRate;
  158 +
136 // ========== 预留字段 ========== 159 // ========== 预留字段 ==========
137 160
138 /** 161 /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/rqc/MesQcRqcLineDO.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; 5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
6 -import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolDO;  
7 import com.baomidou.mybatisplus.annotation.KeySequence; 6 import com.baomidou.mybatisplus.annotation.KeySequence;
8 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
9 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
@@ -44,11 +43,11 @@ public class MesQcRqcLineDO extends BaseDO { @@ -44,11 +43,11 @@ public class MesQcRqcLineDO extends BaseDO {
44 */ 43 */
45 private Long indicatorId; 44 private Long indicatorId;
46 /** 45 /**
47 - * 检测工具 ID 46 + * 检测工具
48 * 47 *
49 - * 关联 {@link MesTmToolDO#getId()} 48 + * 冗余 {@link MesQcIndicatorDO#getTool()}
50 */ 49 */
51 - private Long toolId; 50 + private String tool;
52 /** 51 /**
53 * 检测方法 52 * 检测方法
54 */ 53 */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java
@@ -23,8 +23,6 @@ public interface MesQcOqcMapper extends BaseMapperX<MesQcOqcDO> { @@ -23,8 +23,6 @@ public interface MesQcOqcMapper extends BaseMapperX<MesQcOqcDO> {
23 .eqIfPresent(MesQcOqcDO::getItemId, reqVO.getItemId()) 23 .eqIfPresent(MesQcOqcDO::getItemId, reqVO.getItemId())
24 .eqIfPresent(MesQcOqcDO::getCheckResult, reqVO.getCheckResult()) 24 .eqIfPresent(MesQcOqcDO::getCheckResult, reqVO.getCheckResult())
25 .eqIfPresent(MesQcOqcDO::getStatus, reqVO.getStatus()) 25 .eqIfPresent(MesQcOqcDO::getStatus, reqVO.getStatus())
26 - .betweenIfPresent(MesQcOqcDO::getOutDate, reqVO.getOutDate())  
27 - .betweenIfPresent(MesQcOqcDO::getInspectDate, reqVO.getInspectDate())  
28 .eqIfPresent(MesQcOqcDO::getInspectorUserId, reqVO.getInspectorUserId()) 26 .eqIfPresent(MesQcOqcDO::getInspectorUserId, reqVO.getInspectorUserId())
29 .orderByDesc(MesQcOqcDO::getId)); 27 .orderByDesc(MesQcOqcDO::getId));
30 } 28 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/rqc/MesQcRqcMapper.java
@@ -19,12 +19,10 @@ public interface MesQcRqcMapper extends BaseMapperX<MesQcRqcDO> { @@ -19,12 +19,10 @@ public interface MesQcRqcMapper extends BaseMapperX<MesQcRqcDO> {
19 return selectPage(reqVO, new LambdaQueryWrapperX<MesQcRqcDO>() 19 return selectPage(reqVO, new LambdaQueryWrapperX<MesQcRqcDO>()
20 .likeIfPresent(MesQcRqcDO::getCode, reqVO.getCode()) 20 .likeIfPresent(MesQcRqcDO::getCode, reqVO.getCode())
21 .eqIfPresent(MesQcRqcDO::getSourceDocType, reqVO.getSourceDocType()) 21 .eqIfPresent(MesQcRqcDO::getSourceDocType, reqVO.getSourceDocType())
22 - .likeIfPresent(MesQcRqcDO::getSourceDocCode, reqVO.getSourceDocCode())  
23 .eqIfPresent(MesQcRqcDO::getItemId, reqVO.getItemId()) 22 .eqIfPresent(MesQcRqcDO::getItemId, reqVO.getItemId())
24 - .eqIfPresent(MesQcRqcDO::getRqcType, reqVO.getRqcType()) 23 + .likeIfPresent(MesQcRqcDO::getBatchCode, reqVO.getBatchCode())
25 .eqIfPresent(MesQcRqcDO::getCheckResult, reqVO.getCheckResult()) 24 .eqIfPresent(MesQcRqcDO::getCheckResult, reqVO.getCheckResult())
26 .eqIfPresent(MesQcRqcDO::getStatus, reqVO.getStatus()) 25 .eqIfPresent(MesQcRqcDO::getStatus, reqVO.getStatus())
27 - .betweenIfPresent(MesQcRqcDO::getInspectDate, reqVO.getInspectDate())  
28 .eqIfPresent(MesQcRqcDO::getInspectorUserId, reqVO.getInspectorUserId()) 26 .eqIfPresent(MesQcRqcDO::getInspectorUserId, reqVO.getInspectorUserId())
29 .orderByDesc(MesQcRqcDO::getId)); 27 .orderByDesc(MesQcRqcDO::getId));
30 } 28 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcLineServiceImpl.java
@@ -5,21 +5,25 @@ import cn.hutool.core.util.ObjUtil; @@ -5,21 +5,25 @@ import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.line.MesQcRqcLinePageReqVO; 6 import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.line.MesQcRqcLinePageReqVO;
7 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO; 7 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
8 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO;
10 import cn.iocoder.yudao.module.mes.dal.mysql.qc.rqc.MesQcRqcLineMapper; 11 import cn.iocoder.yudao.module.mes.dal.mysql.qc.rqc.MesQcRqcLineMapper;
11 import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateIndicatorMapper; 12 import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateIndicatorMapper;
12 import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum; 13 import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
  14 +import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
13 import jakarta.annotation.Resource; 15 import jakarta.annotation.Resource;
14 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
15 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
16 18
17 import java.util.ArrayList; 19 import java.util.ArrayList;
18 import java.util.List; 20 import java.util.List;
  21 +import java.util.Map;
19 import java.util.Objects; 22 import java.util.Objects;
20 23
21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
23 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 27 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
24 28
25 /** 29 /**
@@ -36,6 +40,9 @@ public class MesQcRqcLineServiceImpl implements MesQcRqcLineService { @@ -36,6 +40,9 @@ public class MesQcRqcLineServiceImpl implements MesQcRqcLineService {
36 @Resource 40 @Resource
37 private MesQcTemplateIndicatorMapper templateIndicatorMapper; 41 private MesQcTemplateIndicatorMapper templateIndicatorMapper;
38 42
  43 + @Resource
  44 + private MesQcIndicatorService indicatorService;
  45 +
39 @Override 46 @Override
40 public MesQcRqcLineDO validateRqcLineExists(Long id) { 47 public MesQcRqcLineDO validateRqcLineExists(Long id) {
41 MesQcRqcLineDO line = rqcLineMapper.selectById(id); 48 MesQcRqcLineDO line = rqcLineMapper.selectById(id);
@@ -57,14 +64,22 @@ public class MesQcRqcLineServiceImpl implements MesQcRqcLineService { @@ -57,14 +64,22 @@ public class MesQcRqcLineServiceImpl implements MesQcRqcLineService {
57 64
58 @Override 65 @Override
59 public void createLinesFromTemplate(Long rqcId, Long templateId) { 66 public void createLinesFromTemplate(Long rqcId, Long templateId) {
60 - List<MesQcTemplateIndicatorDO> indicators = templateIndicatorMapper.selectListByTemplateId(templateId);  
61 - List<MesQcRqcLineDO> lines = convertList(indicators, indicator -> new MesQcRqcLineDO()  
62 - .setRqcId(rqcId).setIndicatorId(indicator.getIndicatorId())  
63 - .setToolId(null) // TODO @芋艿:模板指标暂无 toolId,后续可扩展;todo @AI:改成 tool;参考 iqc  
64 - .setCheckMethod(indicator.getCheckMethod())  
65 - .setStandardValue(indicator.getStandardValue()).setUnitMeasureId(indicator.getUnitMeasureId())  
66 - .setMaxThreshold(indicator.getThresholdMax()).setMinThreshold(indicator.getThresholdMin())  
67 - .setCriticalQuantity(0).setMajorQuantity(0).setMinorQuantity(0)); 67 + List<MesQcTemplateIndicatorDO> templateIndicators = templateIndicatorMapper.selectListByTemplateId(templateId);
  68 + if (CollUtil.isEmpty(templateIndicators)) {
  69 + return;
  70 + }
  71 + Map<Long, MesQcIndicatorDO> indicatorMap = indicatorService.getIndicatorMap(
  72 + convertSet(templateIndicators, MesQcTemplateIndicatorDO::getIndicatorId));
  73 + List<MesQcRqcLineDO> lines = convertList(templateIndicators, templateIndicator -> {
  74 + MesQcIndicatorDO indicator = indicatorMap.get(templateIndicator.getIndicatorId());
  75 + return new MesQcRqcLineDO()
  76 + .setRqcId(rqcId).setIndicatorId(templateIndicator.getIndicatorId())
  77 + .setTool(indicator != null ? indicator.getTool() : null)
  78 + .setCheckMethod(templateIndicator.getCheckMethod())
  79 + .setStandardValue(templateIndicator.getStandardValue()).setUnitMeasureId(templateIndicator.getUnitMeasureId())
  80 + .setMaxThreshold(templateIndicator.getThresholdMax()).setMinThreshold(templateIndicator.getThresholdMin())
  81 + .setCriticalQuantity(0).setMajorQuantity(0).setMinorQuantity(0);
  82 + });
68 rqcLineMapper.insertBatch(lines); 83 rqcLineMapper.insertBatch(lines);
69 } 84 }
70 85
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.qc.rqc; 1 package cn.iocoder.yudao.module.mes.service.qc.rqc;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -8,12 +7,15 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcPageReqVO; @@ -8,12 +7,15 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcPageReqVO;
8 import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcSaveReqVO; 7 import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcSaveReqVO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO;
11 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO; 10 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
12 import cn.iocoder.yudao.module.mes.dal.mysql.qc.rqc.MesQcRqcMapper; 11 import cn.iocoder.yudao.module.mes.dal.mysql.qc.rqc.MesQcRqcMapper;
13 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusEnum; 12 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusEnum;
  13 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
14 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; 14 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
  15 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
15 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; 16 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
16 -import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService; 17 +import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateDetailService;
  18 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
17 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
18 import org.springframework.context.annotation.Lazy; 20 import org.springframework.context.annotation.Lazy;
19 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
@@ -21,7 +23,9 @@ import org.springframework.transaction.annotation.Transactional; @@ -21,7 +23,9 @@ import org.springframework.transaction.annotation.Transactional;
21 import org.springframework.validation.annotation.Validated; 23 import org.springframework.validation.annotation.Validated;
22 24
23 import java.math.BigDecimal; 25 import java.math.BigDecimal;
  26 +import java.math.RoundingMode;
24 import java.util.List; 27 import java.util.List;
  28 +import java.util.Objects;
25 29
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 31 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -38,29 +42,35 @@ public class MesQcRqcServiceImpl implements MesQcRqcService { @@ -38,29 +42,35 @@ public class MesQcRqcServiceImpl implements MesQcRqcService {
38 @Resource 42 @Resource
39 private MesQcRqcMapper rqcMapper; 43 private MesQcRqcMapper rqcMapper;
40 @Resource 44 @Resource
41 - private MesQcTemplateService templateService; 45 + private MesQcTemplateDetailService templateDetailService;
42 @Resource 46 @Resource
43 private MesQcRqcLineService rqcLineService; 47 private MesQcRqcLineService rqcLineService;
44 @Resource 48 @Resource
45 @Lazy 49 @Lazy
  50 + private MesMdItemService itemService;
  51 + @Resource
  52 + @Lazy
46 private MesQcDefectRecordService defectRecordService; 53 private MesQcDefectRecordService defectRecordService;
47 54
  55 + @Resource
  56 + private AdminUserApi adminUserApi;
  57 +
48 @Override 58 @Override
49 @Transactional(rollbackFor = Exception.class) 59 @Transactional(rollbackFor = Exception.class)
50 public Long createRqc(MesQcRqcSaveReqVO createReqVO) { 60 public Long createRqc(MesQcRqcSaveReqVO createReqVO) {
51 // 1.1 校验编号唯一 61 // 1.1 校验编号唯一
52 validateRqcCodeUnique(null, createReqVO.getCode()); 62 validateRqcCodeUnique(null, createReqVO.getCode());
53 - // TODO @AI:关联的物资等存在的校验;  
54 - // 1.2 校验检验模板存在且支持 RQC 类型  
55 - Long templateId = createReqVO.getTemplateId();  
56 - // TODO @AI:参考 iqc 获取 templateId;不在依赖前端传递;  
57 - MesQcTemplateDO template = templateService.validateTemplateExists(templateId);  
58 - if (!CollUtil.contains(template.getTypes(), MesQcTypeEnum.RQC.getType())) {  
59 - throw exception(QC_RQC_NO_TEMPLATE);  
60 - } 63 + // 1.2 校验物料、检测人员存在
  64 + itemService.validateItemExists(createReqVO.getItemId());
  65 + adminUserApi.validateUser(createReqVO.getInspectorUserId());
  66 + // 1.3 根据物料 + 检验类型自动匹配模板
  67 + MesQcTemplateItemDO templateItem = templateDetailService.getRequiredTemplateByItemIdAndType(
  68 + createReqVO.getItemId(), createReqVO.getType());
  69 + Long templateId = templateItem.getTemplateId();
61 70
62 // 2. 插入主表 71 // 2. 插入主表
63 MesQcRqcDO rqc = BeanUtils.toBean(createReqVO, MesQcRqcDO.class) 72 MesQcRqcDO rqc = BeanUtils.toBean(createReqVO, MesQcRqcDO.class)
  73 + .setTemplateId(templateId)
64 .setStatus(MesOrderStatusEnum.DRAFT.getType()); 74 .setStatus(MesOrderStatusEnum.DRAFT.getType());
65 rqcMapper.insert(rqc); 75 rqcMapper.insert(rqc);
66 76
@@ -75,6 +85,9 @@ public class MesQcRqcServiceImpl implements MesQcRqcService { @@ -75,6 +85,9 @@ public class MesQcRqcServiceImpl implements MesQcRqcService {
75 validateRqcStatusPrepare(updateReqVO.getId()); 85 validateRqcStatusPrepare(updateReqVO.getId());
76 // 1.2 校验编号唯一 86 // 1.2 校验编号唯一
77 validateRqcCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); 87 validateRqcCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
  88 + // 1.3 校验物料、检测人员存在
  89 + itemService.validateItemExists(updateReqVO.getItemId());
  90 + adminUserApi.validateUser(updateReqVO.getInspectorUserId());
78 91
79 // 2. 更新 92 // 2. 更新
80 MesQcRqcDO updateObj = BeanUtils.toBean(updateReqVO, MesQcRqcDO.class); 93 MesQcRqcDO updateObj = BeanUtils.toBean(updateReqVO, MesQcRqcDO.class);
@@ -160,10 +173,44 @@ public class MesQcRqcServiceImpl implements MesQcRqcService { @@ -160,10 +173,44 @@ public class MesQcRqcServiceImpl implements MesQcRqcService {
160 173
161 @Override 174 @Override
162 public void recalculateDefectStats(Long rqcId, List<MesQcDefectRecordDO> records) { 175 public void recalculateDefectStats(Long rqcId, List<MesQcDefectRecordDO> records) {
163 - // RQC 只更新行级缺陷统计,主表不存储缺陷统计字段 176 + MesQcRqcDO rqc = validateRqcExists(rqcId);
  177 + // 1. 行级缺陷统计
164 rqcLineService.recalculateLineDefectStats(rqcId, records); 178 rqcLineService.recalculateLineDefectStats(rqcId, records);
165 179
166 - // TODO @AI:还是对齐其他的 180 + // 2.1 汇总主表的缺陷数量
  181 + int totalCritical = 0;
  182 + int totalMajor = 0;
  183 + int totalMinor = 0;
  184 + for (MesQcDefectRecordDO record : records) {
  185 + int quantity = ObjUtil.defaultIfNull(record.getQuantity(), 1);
  186 + if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) {
  187 + totalCritical += quantity;
  188 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MAJOR.getType())) {
  189 + totalMajor += quantity;
  190 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MINOR.getType())) {
  191 + totalMinor += quantity;
  192 + } else {
  193 + throw exception(QC_DEFECT_RECORD_LEVEL_UNKNOWN);
  194 + }
  195 + }
  196 + // 2.2 计算缺陷率
  197 + BigDecimal criticalRate = BigDecimal.ZERO;
  198 + BigDecimal majorRate = BigDecimal.ZERO;
  199 + BigDecimal minorRate = BigDecimal.ZERO;
  200 + if (rqc.getCheckQuantity() != null && rqc.getCheckQuantity().compareTo(BigDecimal.ZERO) > 0) {
  201 + BigDecimal checkQty = rqc.getCheckQuantity();
  202 + criticalRate = BigDecimal.valueOf(totalCritical).multiply(BigDecimal.valueOf(100))
  203 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  204 + majorRate = BigDecimal.valueOf(totalMajor).multiply(BigDecimal.valueOf(100))
  205 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  206 + minorRate = BigDecimal.valueOf(totalMinor).multiply(BigDecimal.valueOf(100))
  207 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  208 + }
  209 + // 2.3 更新主表
  210 + MesQcRqcDO updateRqc = new MesQcRqcDO().setId(rqcId)
  211 + .setCriticalQuantity(totalCritical).setMajorQuantity(totalMajor).setMinorQuantity(totalMinor)
  212 + .setCriticalRate(criticalRate).setMajorRate(majorRate).setMinorRate(minorRate);
  213 + rqcMapper.updateById(updateRqc);
167 } 214 }
168 215
169 } 216 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateDetailService.java
@@ -5,13 +5,12 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.indicator.Mes @@ -5,13 +5,12 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.indicator.Mes
5 import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.indicator.MesQcTemplateIndicatorSaveReqVO; 5 import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.indicator.MesQcTemplateIndicatorSaveReqVO;
6 import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.item.MesQcTemplateItemPageReqVO; 6 import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.item.MesQcTemplateItemPageReqVO;
7 import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.item.MesQcTemplateItemSaveReqVO; 7 import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.item.MesQcTemplateItemSaveReqVO;
8 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;  
9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
11 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; 10 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
12 import jakarta.validation.Valid; 11 import jakarta.validation.Valid;
13 12
14 -// TODO @AI:还是按照表,拆分。而不是 detail; 13 +// TODO @AI:还是按照表,拆分。一个 MesQcTemplateIndicatorDO、MesQcTemplateItemDO;
15 /** 14 /**
16 * MES 质检方案明细 Service 接口 15 * MES 质检方案明细 Service 接口
17 * 16 *
@@ -99,29 +98,6 @@ public interface MesQcTemplateDetailService { @@ -99,29 +98,6 @@ public interface MesQcTemplateDetailService {
99 */ 98 */
100 PageResult<MesQcTemplateItemDO> getTemplateItemPage(MesQcTemplateItemPageReqVO pageReqVO); 99 PageResult<MesQcTemplateItemDO> getTemplateItemPage(MesQcTemplateItemPageReqVO pageReqVO);
101 100
102 - // ========== 复杂查询 ==========  
103 -  
104 - /**  
105 - * 根据方案 ID + 产品 ID 获取产品关联  
106 - *  
107 - * @param templateId 方案 ID  
108 - * @param itemId 产品 ID  
109 - * @return 产品关联  
110 - */  
111 - MesQcTemplateItemDO getTemplateItemByTemplateIdAndItemId(Long templateId, Long itemId);  
112 -  
113 - // TODO @芋艿:【不要删除】后续这个方法,会慢慢被替代掉;  
114 - /**  
115 - * 根据产品物料 ID 和 QC 类型查找检验模板  
116 - *  
117 - * 查找逻辑:在 template_item 中找到匹配 itemId 的模板,再校验该模板的 types 包含给定的 qcType  
118 - *  
119 - * @param itemId 产品物料 ID  
120 - * @param qcType 检测种类(枚举 {@link MesQcTypeEnum})  
121 - * @return 匹配的检验模板(可能为 null)  
122 - */  
123 - MesQcTemplateDO getTemplateByItemIdAndType(Long itemId, Integer qcType);  
124 -  
125 /** 101 /**
126 * 根据产品物料 ID 和 QC 类型查找检验模板的产品关联(必须存在,否则抛异常) 102 * 根据产品物料 ID 和 QC 类型查找检验模板的产品关联(必须存在,否则抛异常)
127 * 103 *
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateDetailServiceImpl.java
@@ -159,25 +159,6 @@ public class MesQcTemplateDetailServiceImpl implements MesQcTemplateDetailServic @@ -159,25 +159,6 @@ public class MesQcTemplateDetailServiceImpl implements MesQcTemplateDetailServic
159 // ========== 复杂查询 ========== 159 // ========== 复杂查询 ==========
160 160
161 @Override 161 @Override
162 - public MesQcTemplateItemDO getTemplateItemByTemplateIdAndItemId(Long templateId, Long itemId) {  
163 - return templateItemMapper.selectByTemplateIdAndItemId(templateId, itemId);  
164 - }  
165 -  
166 - @Override  
167 - public MesQcTemplateDO getTemplateByItemIdAndType(Long itemId, Integer qcType) {  
168 - // 1.1 查出 itemId 关联的所有 templateId  
169 - List<MesQcTemplateItemDO> templateItems = templateItemMapper.selectListByItemId(itemId);  
170 - if (CollUtil.isEmpty(templateItems)) {  
171 - return null;  
172 - }  
173 - // 1.2 批量查模板,筛选 types 包含给定 qcType 的第一个  
174 - List<MesQcTemplateDO> templates = templateMapper.selectByIds(  
175 - convertSet(templateItems, MesQcTemplateItemDO::getTemplateId));  
176 - // 2. 筛选 types 包含给定 qcType 的第一个  
177 - return findFirst(templates, template -> CollUtil.contains(template.getTypes(), qcType));  
178 - }  
179 -  
180 - @Override  
181 public MesQcTemplateItemDO getRequiredTemplateByItemIdAndType(Long itemId, Integer qcType) { 162 public MesQcTemplateItemDO getRequiredTemplateByItemIdAndType(Long itemId, Integer qcType) {
182 // 1. 查出 itemId 关联的所有 templateId 163 // 1. 查出 itemId 关联的所有 templateId
183 List<MesQcTemplateItemDO> templateItems = templateItemMapper.selectListByItemId(itemId); 164 List<MesQcTemplateItemDO> templateItems = templateItemMapper.selectListByItemId(itemId);