Commit b26ff0d722445a4fdfbb747ba7675f102b6518f3

Authored by YunaiV
1 parent c093da8c

feat(mes): 重构 IQC 缺陷记录为通用质检缺陷记录表,支持多模块复用

将 mes_qc_iqc_defect 重构为通用 mes_qc_defect_record 表,通过 qcType 区分 IQC/IPQC/OQC/RQC,实现多质检模块缺陷记录复用。

主要变更:
1. 新增 MesQcDefectRecordDO、Mapper、Service、Controller 及 VO
2. 删除旧 IQC 独享的缺陷表相关代码(DO/Mapper/Service/Controller/VO 共 8 个文件)
3. 更新 MesQcIqcServiceImpl,替换为通用 MesQcDefectRecordService
4. 新增错误码 QC_DEFECT_RECORD_NOT_EXISTS/LEVEL_UNKNOWN/QC_TYPE_UNSUPPORTED
5. 新增 SQL 建表脚本 mes_qc_defect_record_migration.sql 及测试数据
Showing 17 changed files with 506 additions and 384 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/defect/MesQcDefectRecordController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.qc.defect;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordRespVO;
  8 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordSaveReqVO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO;
  10 +import cn.iocoder.yudao.module.mes.service.qc.defect.MesQcDefectRecordService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +@Tag(name = "管理后台 - MES 质检缺陷记录")
  23 +@RestController
  24 +@RequestMapping("/mes/qc/defect-record")
  25 +@Validated
  26 +public class MesQcDefectRecordController {
  27 +
  28 + @Resource
  29 + private MesQcDefectRecordService defectRecordService;
  30 +
  31 + // TODO @AI:使用 mes:qc-record:create 权限标识先;不要搞太复杂;当然,也写个 todo @芋艿:说下这个情况
  32 + @PostMapping("/create")
  33 + @Operation(summary = "创建质检缺陷记录")
  34 + @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:create')")
  35 + public CommonResult<Long> createDefectRecord(@Valid @RequestBody MesQcDefectRecordSaveReqVO createReqVO) {
  36 + return success(defectRecordService.createDefectRecord(createReqVO));
  37 + }
  38 +
  39 + @PutMapping("/update")
  40 + @Operation(summary = "更新质检缺陷记录")
  41 + @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:update')")
  42 + public CommonResult<Boolean> updateDefectRecord(@Valid @RequestBody MesQcDefectRecordSaveReqVO updateReqVO) {
  43 + defectRecordService.updateDefectRecord(updateReqVO);
  44 + return success(true);
  45 + }
  46 +
  47 + @DeleteMapping("/delete")
  48 + @Operation(summary = "删除质检缺陷记录")
  49 + @Parameter(name = "id", description = "编号", required = true)
  50 + @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:update')")
  51 + public CommonResult<Boolean> deleteDefectRecord(@RequestParam("id") Long id) {
  52 + defectRecordService.deleteDefectRecord(id);
  53 + return success(true);
  54 + }
  55 +
  56 + @GetMapping("/page")
  57 + @Operation(summary = "获得质检缺陷记录分页")
  58 + @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:query')")
  59 + public CommonResult<PageResult<MesQcDefectRecordRespVO>> getDefectRecordPage(@Valid MesQcDefectRecordPageReqVO pageReqVO) {
  60 + PageResult<MesQcDefectRecordDO> pageResult = defectRecordService.getDefectRecordPage(pageReqVO);
  61 + return success(BeanUtils.toBean(pageResult, MesQcDefectRecordRespVO.class));
  62 + }
  63 +
  64 +}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/defect/MesQcIqcDefectPageReqVO.java renamed to yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/defect/vo/MesQcDefectRecordPageReqVO.java
1 -package cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect; 1 +package cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
@@ -7,17 +7,21 @@ import lombok.Data; @@ -7,17 +7,21 @@ import lombok.Data;
7 import lombok.EqualsAndHashCode; 7 import lombok.EqualsAndHashCode;
8 import lombok.ToString; 8 import lombok.ToString;
9 9
10 -@Schema(description = "管理后台 - MES 来料检验缺陷记录分页 Request VO") 10 +@Schema(description = "管理后台 - MES 质检缺陷记录分页 Request VO")
11 @Data 11 @Data
12 @EqualsAndHashCode(callSuper = true) 12 @EqualsAndHashCode(callSuper = true)
13 @ToString(callSuper = true) 13 @ToString(callSuper = true)
14 -public class MesQcIqcDefectPageReqVO extends PageParam { 14 +public class MesQcDefectRecordPageReqVO extends PageParam {
15 15
16 - @Schema(description = "来料检验单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")  
17 - @NotNull(message = "来料检验单 ID 不能为空")  
18 - private Long iqcId; 16 + @Schema(description = "检验类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + @NotNull(message = "检验类型不能为空")
  18 + private Integer qcType;
19 19
20 - @Schema(description = "来料检验行 ID", example = "200") 20 + @Schema(description = "检验单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  21 + @NotNull(message = "检验单 ID 不能为空")
  22 + private Long qcId;
  23 +
  24 + @Schema(description = "检验行 ID", example = "200")
21 private Long lineId; 25 private Long lineId;
22 26
23 } 27 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/defect/MesQcIqcDefectRespVO.java renamed to yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/defect/vo/MesQcDefectRecordRespVO.java
1 -package cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect; 1 +package cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
7 7
8 -@Schema(description = "管理后台 - MES 来料检验缺陷记录 Response VO") 8 +@Schema(description = "管理后台 - MES 质检缺陷记录 Response VO")
9 @Data 9 @Data
10 -public class MesQcIqcDefectRespVO { 10 +public class MesQcDefectRecordRespVO {
11 11
12 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 12 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
13 private Long id; 13 private Long id;
14 14
15 - @Schema(description = "来料检验单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")  
16 - private Long iqcId; 15 + @Schema(description = "检验类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  16 + private Integer qcType;
17 17
18 - @Schema(description = "来料检验行 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") 18 + @Schema(description = "检验单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  19 + private Long qcId;
  20 +
  21 + @Schema(description = "检验行 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
19 private Long lineId; 22 private Long lineId;
20 23
21 @Schema(description = "缺陷描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "表面划伤") 24 @Schema(description = "缺陷描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "表面划伤")
22 - private String defectName; 25 + private String name;
23 26
24 @Schema(description = "缺陷等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 27 @Schema(description = "缺陷等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
25 - private Integer defectLevel; 28 + private Integer level;
26 29
27 @Schema(description = "缺陷数量", example = "1") 30 @Schema(description = "缺陷数量", example = "1")
28 - private Integer defectQuantity; 31 + private Integer quantity;
29 32
30 @Schema(description = "备注", example = "备注") 33 @Schema(description = "备注", example = "备注")
31 private String remark; 34 private String remark;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/defect/vo/MesQcDefectRecordSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
  5 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +import jakarta.validation.constraints.NotNull;
  9 +import lombok.Data;
  10 +
  11 +@Schema(description = "管理后台 - MES 质检缺陷记录新增/修改 Request VO")
  12 +@Data
  13 +public class MesQcDefectRecordSaveReqVO {
  14 +
  15 + @Schema(description = "编号", example = "1024")
  16 + private Long id;
  17 +
  18 + @Schema(description = "检验类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  19 + @NotNull(message = "检验类型不能为空")
  20 + @InEnum(MesQcTypeEnum.class)
  21 + private Integer qcType;
  22 +
  23 + @Schema(description = "检验单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  24 + @NotNull(message = "检验单 ID 不能为空")
  25 + private Long qcId;
  26 +
  27 + @Schema(description = "检验行 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
  28 + @NotNull(message = "检验行 ID 不能为空")
  29 + private Long lineId;
  30 +
  31 + @Schema(description = "缺陷描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "表面划伤")
  32 + @NotEmpty(message = "缺陷描述不能为空")
  33 + private String name;
  34 +
  35 + @Schema(description = "缺陷等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  36 + @NotNull(message = "缺陷等级不能为空")
  37 + @InEnum(MesQcDefectLevelEnum.class)
  38 + private Integer level;
  39 +
  40 + @Schema(description = "缺陷数量", example = "1")
  41 + private Integer quantity;
  42 +
  43 + @Schema(description = "备注", example = "备注")
  44 + private String remark;
  45 +
  46 +}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcDefectController.java deleted
1 -package cn.iocoder.yudao.module.mes.controller.admin.qc.iqc;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectPageReqVO;  
7 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectRespVO;  
8 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectSaveReqVO;  
9 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDefectDO;  
10 -import cn.iocoder.yudao.module.mes.service.qc.lqc.MesQcIqcDefectService;  
11 -import io.swagger.v3.oas.annotations.Operation;  
12 -import io.swagger.v3.oas.annotations.Parameter;  
13 -import io.swagger.v3.oas.annotations.tags.Tag;  
14 -import jakarta.annotation.Resource;  
15 -import jakarta.validation.Valid;  
16 -import org.springframework.security.access.prepost.PreAuthorize;  
17 -import org.springframework.validation.annotation.Validated;  
18 -import org.springframework.web.bind.annotation.*;  
19 -  
20 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
21 -  
22 -@Tag(name = "管理后台 - MES 来料检验缺陷记录")  
23 -@RestController  
24 -@RequestMapping("/mes/qc/iqc/defect")  
25 -@Validated  
26 -public class MesQcIqcDefectController {  
27 -  
28 - @Resource  
29 - private MesQcIqcDefectService iqcDefectService;  
30 -  
31 - @PostMapping("/create")  
32 - @Operation(summary = "创建来料检验缺陷记录")  
33 - @PreAuthorize("@ss.hasPermission('mes:qc-iqc:create')")  
34 - public CommonResult<Long> createIqcDefect(@Valid @RequestBody MesQcIqcDefectSaveReqVO createReqVO) {  
35 - return success(iqcDefectService.createIqcDefect(createReqVO));  
36 - }  
37 -  
38 - @PutMapping("/update")  
39 - @Operation(summary = "更新来料检验缺陷记录")  
40 - @PreAuthorize("@ss.hasPermission('mes:qc-iqc:update')")  
41 - public CommonResult<Boolean> updateIqcDefect(@Valid @RequestBody MesQcIqcDefectSaveReqVO updateReqVO) {  
42 - iqcDefectService.updateIqcDefect(updateReqVO);  
43 - return success(true);  
44 - }  
45 -  
46 - @DeleteMapping("/delete")  
47 - @Operation(summary = "删除来料检验缺陷记录")  
48 - @Parameter(name = "id", description = "编号", required = true)  
49 - @PreAuthorize("@ss.hasPermission('mes:qc-iqc:update')")  
50 - public CommonResult<Boolean> deleteIqcDefect(@RequestParam("id") Long id) {  
51 - iqcDefectService.deleteIqcDefect(id);  
52 - return success(true);  
53 - }  
54 -  
55 - @GetMapping("/page")  
56 - @Operation(summary = "获得来料检验缺陷记录分页")  
57 - @PreAuthorize("@ss.hasPermission('mes:qc-iqc:query')")  
58 - public CommonResult<PageResult<MesQcIqcDefectRespVO>> getIqcDefectPage(@Valid MesQcIqcDefectPageReqVO pageReqVO) {  
59 - PageResult<MesQcIqcDefectDO> pageResult = iqcDefectService.getIqcDefectPage(pageReqVO);  
60 - return success(BeanUtils.toBean(pageResult, MesQcIqcDefectRespVO.class));  
61 - }  
62 -  
63 -}  
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/defect/MesQcIqcDefectSaveReqVO.java deleted
1 -package cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import jakarta.validation.constraints.NotEmpty;  
5 -import jakarta.validation.constraints.NotNull;  
6 -import lombok.Data;  
7 -  
8 -@Schema(description = "管理后台 - MES 来料检验缺陷记录新增/修改 Request VO")  
9 -@Data  
10 -public class MesQcIqcDefectSaveReqVO {  
11 -  
12 - @Schema(description = "编号", example = "1024")  
13 - private Long id;  
14 -  
15 - @Schema(description = "来料检验单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")  
16 - @NotNull(message = "来料检验单 ID 不能为空")  
17 - private Long iqcId;  
18 -  
19 - @Schema(description = "来料检验行 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")  
20 - @NotNull(message = "来料检验行 ID 不能为空")  
21 - private Long lineId;  
22 -  
23 - @Schema(description = "缺陷描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "表面划伤")  
24 - @NotEmpty(message = "缺陷描述不能为空")  
25 - private String defectName;  
26 -  
27 - @Schema(description = "缺陷等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
28 - @NotNull(message = "缺陷等级不能为空")  
29 - private Integer defectLevel;  
30 -  
31 - @Schema(description = "缺陷数量", example = "1")  
32 - private Integer defectQuantity;  
33 -  
34 - @Schema(description = "备注", example = "备注")  
35 - private String remark;  
36 -  
37 -}  
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/defect/MesQcDefectDO.java
@@ -34,12 +34,16 @@ public class MesQcDefectDO extends BaseDO { @@ -34,12 +34,16 @@ public class MesQcDefectDO extends BaseDO {
34 * 缺陷描述 34 * 缺陷描述
35 */ 35 */
36 private String name; 36 private String name;
  37 + // TODO @AI:改成 mes_defect_type;字典 sql 变更;另外,dict constants 里也写下
  38 + // TODO @AI:枚举类;
37 /** 39 /**
38 * 检测项类型 40 * 检测项类型
39 * 41 *
40 * 字典类型 mes_index_type 42 * 字典类型 mes_index_type
41 */ 43 */
42 private String type; 44 private String type;
  45 + // TODO @AI:改成 mes_defect_level;另外,dict constants 里也写下
  46 + // TODO @AI:枚举类;
43 /** 47 /**
44 * 缺陷等级 48 * 缺陷等级
45 * 49 *
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/lqc/MesQcIqcDefectDO.java renamed to yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/defect/MesQcDefectRecordDO.java
1 -package cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc; 1 +package cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect;
2 2
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.qc.ipqc.MesQcIpqcDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcLineDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcLineDO;
  8 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
  9 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
4 import com.baomidou.mybatisplus.annotation.KeySequence; 10 import com.baomidou.mybatisplus.annotation.KeySequence;
5 import com.baomidou.mybatisplus.annotation.TableId; 11 import com.baomidou.mybatisplus.annotation.TableId;
6 import com.baomidou.mybatisplus.annotation.TableName; 12 import com.baomidou.mybatisplus.annotation.TableName;
7 import lombok.*; 13 import lombok.*;
8 14
9 /** 15 /**
10 - * MES 来料检验缺陷记录 DO 16 + * MES 质检缺陷记录 DO
  17 + *
  18 + * 通用缺陷记录表,通过 {@link #qcType} 区分检验类型(IQC、IPQC、OQC、RQC),多模块复用
11 * 19 *
12 * @author 芋道源码 20 * @author 芋道源码
13 */ 21 */
14 -@TableName("mes_qc_iqc_defect")  
15 -@KeySequence("mes_qc_iqc_defect_seq") 22 +@TableName("mes_qc_defect_record")
  23 +@KeySequence("mes_qc_defect_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
16 @Data 24 @Data
17 @EqualsAndHashCode(callSuper = true) 25 @EqualsAndHashCode(callSuper = true)
18 @ToString(callSuper = true) 26 @ToString(callSuper = true)
19 @Builder 27 @Builder
20 @NoArgsConstructor 28 @NoArgsConstructor
21 @AllArgsConstructor 29 @AllArgsConstructor
22 -public class MesQcIqcDefectDO extends BaseDO { 30 +public class MesQcDefectRecordDO extends BaseDO {
23 31
24 /** 32 /**
25 * 编号 33 * 编号
@@ -27,31 +35,39 @@ public class MesQcIqcDefectDO extends BaseDO { @@ -27,31 +35,39 @@ public class MesQcIqcDefectDO extends BaseDO {
27 @TableId 35 @TableId
28 private Long id; 36 private Long id;
29 /** 37 /**
30 - * 来料检验单 ID 38 + * 检验类型
  39 + *
  40 + * 枚举 {@link MesQcTypeEnum}
  41 + */
  42 + private Integer qcType;
  43 + /**
  44 + * 检验单 ID
31 * 45 *
32 * 关联 {@link MesQcIqcDO#getId()} 46 * 关联 {@link MesQcIqcDO#getId()}
  47 + * 关联 {@link MesQcIpqcDO#getId()}
33 */ 48 */
34 - private Long iqcId; 49 + private Long qcId;
35 /** 50 /**
36 - * 来料检验行 ID 51 + * 检验行 ID
37 * 52 *
38 * 关联 {@link MesQcIqcLineDO#getId()} 53 * 关联 {@link MesQcIqcLineDO#getId()}
  54 + * 关联 {@link MesQcIpqcLineDO#getId()}
39 */ 55 */
40 private Long lineId; 56 private Long lineId;
41 /** 57 /**
42 * 缺陷描述 58 * 缺陷描述
43 */ 59 */
44 - private String defectName; 60 + private String name;
45 /** 61 /**
46 * 缺陷等级 62 * 缺陷等级
47 * 63 *
48 - * 枚举 {@link cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum} 64 + * 枚举 {@link MesQcDefectLevelEnum}
49 */ 65 */
50 - private Integer defectLevel; 66 + private Integer level;
51 /** 67 /**
52 * 缺陷数量 68 * 缺陷数量
53 */ 69 */
54 - private Integer defectQuantity; 70 + private Integer quantity;
55 /** 71 /**
56 * 备注 72 * 备注
57 */ 73 */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/defect/MesQcDefectRecordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.qc.defect;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * MES 质检缺陷记录 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface MesQcDefectRecordMapper extends BaseMapperX<MesQcDefectRecordDO> {
  19 +
  20 + default PageResult<MesQcDefectRecordDO> selectPage(MesQcDefectRecordPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<MesQcDefectRecordDO>()
  22 + .eqIfPresent(MesQcDefectRecordDO::getQcType, reqVO.getQcType())
  23 + .eqIfPresent(MesQcDefectRecordDO::getQcId, reqVO.getQcId())
  24 + .eqIfPresent(MesQcDefectRecordDO::getLineId, reqVO.getLineId())
  25 + .orderByDesc(MesQcDefectRecordDO::getId));
  26 + }
  27 +
  28 + default List<MesQcDefectRecordDO> selectListByQcTypeAndQcId(Integer qcType, Long qcId) {
  29 + return selectList(new LambdaQueryWrapperX<MesQcDefectRecordDO>()
  30 + .eq(MesQcDefectRecordDO::getQcType, qcType)
  31 + .eq(MesQcDefectRecordDO::getQcId, qcId));
  32 + }
  33 +
  34 + default List<MesQcDefectRecordDO> selectListByQcTypeAndQcIdAndLineId(Integer qcType, Long qcId, Long lineId) {
  35 + return selectList(new LambdaQueryWrapperX<MesQcDefectRecordDO>()
  36 + .eq(MesQcDefectRecordDO::getQcType, qcType)
  37 + .eq(MesQcDefectRecordDO::getQcId, qcId)
  38 + .eq(MesQcDefectRecordDO::getLineId, lineId));
  39 + }
  40 +
  41 + default void deleteByQcTypeAndQcId(Integer qcType, Long qcId) {
  42 + delete(new LambdaQueryWrapperX<MesQcDefectRecordDO>()
  43 + .eq(MesQcDefectRecordDO::getQcType, qcType)
  44 + .eq(MesQcDefectRecordDO::getQcId, qcId));
  45 + }
  46 +
  47 +}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/lqc/MesQcIqcDefectMapper.java deleted
1 -package cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;  
5 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
6 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectPageReqVO;  
7 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDefectDO;  
8 -import org.apache.ibatis.annotations.Mapper;  
9 -  
10 -import java.util.List;  
11 -  
12 -/**  
13 - * MES 来料检验缺陷记录 Mapper  
14 - *  
15 - * @author 芋道源码  
16 - */  
17 -@Mapper  
18 -public interface MesQcIqcDefectMapper extends BaseMapperX<MesQcIqcDefectDO> {  
19 -  
20 - default PageResult<MesQcIqcDefectDO> selectPage(MesQcIqcDefectPageReqVO reqVO) {  
21 - return selectPage(reqVO, new LambdaQueryWrapperX<MesQcIqcDefectDO>()  
22 - .eqIfPresent(MesQcIqcDefectDO::getIqcId, reqVO.getIqcId())  
23 - .eqIfPresent(MesQcIqcDefectDO::getLineId, reqVO.getLineId())  
24 - .orderByDesc(MesQcIqcDefectDO::getId));  
25 - }  
26 -  
27 - default List<MesQcIqcDefectDO> selectListByIqcId(Long iqcId) {  
28 - return selectList(MesQcIqcDefectDO::getIqcId, iqcId);  
29 - }  
30 -  
31 - default List<MesQcIqcDefectDO> selectListByIqcIdAndLineId(Long iqcId, Long lineId) {  
32 - return selectList(new LambdaQueryWrapperX<MesQcIqcDefectDO>()  
33 - .eq(MesQcIqcDefectDO::getIqcId, iqcId)  
34 - .eq(MesQcIqcDefectDO::getLineId, lineId));  
35 - }  
36 -  
37 - default void deleteByIqcId(Long iqcId) {  
38 - delete(new LambdaQueryWrapperX<MesQcIqcDefectDO>()  
39 - .eq(MesQcIqcDefectDO::getIqcId, iqcId));  
40 - }  
41 -  
42 -}  
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -252,9 +252,20 @@ public interface ErrorCodeConstants { @@ -252,9 +252,20 @@ public interface ErrorCodeConstants {
252 ErrorCode QC_IQC_NO_TEMPLATE = new ErrorCode(1_040_603_005, "当前产品未配置 IQC 检测模板"); 252 ErrorCode QC_IQC_NO_TEMPLATE = new ErrorCode(1_040_603_005, "当前产品未配置 IQC 检测模板");
253 // ========== MES 质量管理-来料检验行(1-040-603-100) ========== 253 // ========== MES 质量管理-来料检验行(1-040-603-100) ==========
254 ErrorCode QC_IQC_LINE_NOT_EXISTS = new ErrorCode(1_040_603_100, "来料检验行不存在"); 254 ErrorCode QC_IQC_LINE_NOT_EXISTS = new ErrorCode(1_040_603_100, "来料检验行不存在");
255 - // ========== MES 质量管理-来料检验缺陷(1-040-603-200) ==========  
256 - ErrorCode QC_IQC_DEFECT_NOT_EXISTS = new ErrorCode(1_040_603_200, "来料检验缺陷记录不存在");  
257 - ErrorCode QC_IQC_DEFECT_LEVEL_UNKNOWN = new ErrorCode(1_040_603_201, "未知的缺陷等级"); 255 +
  256 + // ========== MES 质量管理-过程检验 IPQC(1-040-604-000) ==========
  257 + ErrorCode QC_IPQC_NOT_EXISTS = new ErrorCode(1_040_604_000, "过程检验单不存在");
  258 + ErrorCode QC_IPQC_CODE_DUPLICATE = new ErrorCode(1_040_604_001, "过程检验单编号已存在");
  259 + ErrorCode QC_IPQC_NOT_PREPARE = new ErrorCode(1_040_604_002, "只有草稿状态的检验单才可操作");
  260 + ErrorCode QC_IPQC_QUANTITY_MISMATCH = new ErrorCode(1_040_604_004, "合格品与不合格品数量之和须等于检测数量");
  261 + ErrorCode QC_IPQC_NO_TEMPLATE = new ErrorCode(1_040_604_005, "当前产品未配置 IPQC 检测模板");
  262 + // ========== MES 质量管理-过程检验行(1-040-604-100) ==========
  263 + ErrorCode QC_IPQC_LINE_NOT_EXISTS = new ErrorCode(1_040_604_100, "过程检验行不存在");
  264 +
  265 + // ========== MES 质量管理-质检缺陷记录(通用)(1-040-605-000) ==========
  266 + ErrorCode QC_DEFECT_RECORD_NOT_EXISTS = new ErrorCode(1_040_605_000, "缺陷记录不存在");
  267 + ErrorCode QC_DEFECT_RECORD_LEVEL_UNKNOWN = new ErrorCode(1_040_605_001, "未知的缺陷等级");
  268 + ErrorCode QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED = new ErrorCode(1_040_605_002, "不支持的检验类型");
258 269
259 // ========== MES 仓库管理-仓库(1-040-700-000) ========== 270 // ========== MES 仓库管理-仓库(1-040-700-000) ==========
260 ErrorCode WM_WAREHOUSE_NOT_EXISTS = new ErrorCode(1_040_700_000, "仓库不存在"); 271 ErrorCode WM_WAREHOUSE_NOT_EXISTS = new ErrorCode(1_040_700_000, "仓库不存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcDefectService.java renamed to yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/defect/MesQcDefectRecordService.java
1 -package cn.iocoder.yudao.module.mes.service.qc.lqc; 1 +package cn.iocoder.yudao.module.mes.service.qc.defect;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectPageReqVO;  
5 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectSaveReqVO;  
6 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDefectDO; 4 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordPageReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO;
7 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
8 8
9 /** 9 /**
10 - * MES 来料检验缺陷记录 Service 接口 10 + * MES 质检缺陷记录 Service 接口
11 * 11 *
12 * @author 芋道源码 12 * @author 芋道源码
13 */ 13 */
14 -public interface MesQcIqcDefectService { 14 +public interface MesQcDefectRecordService {
15 15
16 /** 16 /**
17 - * 创建来料检验缺陷记录 17 + * 创建质检缺陷记录
18 * 18 *
19 * @param createReqVO 创建信息 19 * @param createReqVO 创建信息
20 * @return 编号 20 * @return 编号
21 */ 21 */
22 - Long createIqcDefect(@Valid MesQcIqcDefectSaveReqVO createReqVO); 22 + Long createDefectRecord(@Valid MesQcDefectRecordSaveReqVO createReqVO);
23 23
24 /** 24 /**
25 - * 更新来料检验缺陷记录 25 + * 更新质检缺陷记录
26 * 26 *
27 * @param updateReqVO 更新信息 27 * @param updateReqVO 更新信息
28 */ 28 */
29 - void updateIqcDefect(@Valid MesQcIqcDefectSaveReqVO updateReqVO); 29 + void updateDefectRecord(@Valid MesQcDefectRecordSaveReqVO updateReqVO);
30 30
31 /** 31 /**
32 - * 删除来料检验缺陷记录 32 + * 删除质检缺陷记录
33 * 33 *
34 * @param id 编号 34 * @param id 编号
35 */ 35 */
36 - void deleteIqcDefect(Long id); 36 + void deleteDefectRecord(Long id);
37 37
38 /** 38 /**
39 - * 获得来料检验缺陷记录分页 39 + * 获得质检缺陷记录分页
40 * 40 *
41 * @param pageReqVO 分页查询 41 * @param pageReqVO 分页查询
42 - * @return 来料检验缺陷记录分页 42 + * @return 质检缺陷记录分页
43 */ 43 */
44 - PageResult<MesQcIqcDefectDO> getIqcDefectPage(MesQcIqcDefectPageReqVO pageReqVO); 44 + PageResult<MesQcDefectRecordDO> getDefectRecordPage(MesQcDefectRecordPageReqVO pageReqVO);
45 45
46 /** 46 /**
47 - * 根据来料检验单 ID 级联删除所有缺陷记录 47 + * 根据检验类型和检验单 ID 级联删除所有缺陷记录
48 * 48 *
49 - * @param iqcId 来料检验单 ID 49 + * @param qcType 检验类型
  50 + * @param qcId 检验单 ID
50 */ 51 */
51 - void deleteByIqcId(Long iqcId); 52 + void deleteByQcTypeAndQcId(Integer qcType, Long qcId);
52 53
53 } 54 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/defect/MesQcDefectRecordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.qc.defect;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordSaveReqVO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDO;
  10 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcLineDO;
  11 +import cn.iocoder.yudao.module.mes.dal.mysql.qc.defect.MesQcDefectRecordMapper;
  12 +import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcMapper;
  13 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
  14 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
  15 +import cn.iocoder.yudao.module.mes.service.qc.lqc.MesQcIqcLineService;
  16 +import jakarta.annotation.Resource;
  17 +import org.springframework.context.annotation.Lazy;
  18 +import org.springframework.stereotype.Service;
  19 +import org.springframework.transaction.annotation.Transactional;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import java.math.BigDecimal;
  23 +import java.math.RoundingMode;
  24 +import java.util.ArrayList;
  25 +import java.util.List;
  26 +import java.util.Objects;
  27 +
  28 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  30 +
  31 +/**
  32 + * MES 质检缺陷记录 Service 实现类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Service
  37 +@Validated
  38 +public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService {
  39 +
  40 + @Resource
  41 + private MesQcDefectRecordMapper defectRecordMapper;
  42 +
  43 + // TODO @AI:不要调用 mapper,调用对方的 service;
  44 + @Resource
  45 + private MesQcIqcMapper iqcMapper;
  46 + @Resource
  47 + @Lazy
  48 + private MesQcIqcLineService iqcLineService;
  49 +
  50 + @Override
  51 + @Transactional(rollbackFor = Exception.class)
  52 + public Long createDefectRecord(MesQcDefectRecordSaveReqVO createReqVO) {
  53 + // 1. 校验主表和行表存在
  54 + validateQcExists(createReqVO.getQcType(), createReqVO.getQcId());
  55 + validateQcLineExists(createReqVO.getQcType(), createReqVO.getLineId());
  56 +
  57 + // 2. 插入
  58 + MesQcDefectRecordDO record = BeanUtils.toBean(createReqVO, MesQcDefectRecordDO.class);
  59 + defectRecordMapper.insert(record);
  60 +
  61 + // 3. 重新计算缺陷统计
  62 + recalculateDefectStats(createReqVO.getQcType(), createReqVO.getQcId());
  63 + return record.getId();
  64 + }
  65 +
  66 + @Override
  67 + @Transactional(rollbackFor = Exception.class)
  68 + public void updateDefectRecord(MesQcDefectRecordSaveReqVO updateReqVO) {
  69 + // 1. 校验存在
  70 + validateDefectRecordExists(updateReqVO.getId());
  71 +
  72 + // 2. 更新
  73 + MesQcDefectRecordDO updateObj = BeanUtils.toBean(updateReqVO, MesQcDefectRecordDO.class);
  74 + defectRecordMapper.updateById(updateObj);
  75 +
  76 + // 3. 重新计算缺陷统计
  77 + recalculateDefectStats(updateReqVO.getQcType(), updateReqVO.getQcId());
  78 + }
  79 +
  80 + @Override
  81 + @Transactional(rollbackFor = Exception.class)
  82 + public void deleteDefectRecord(Long id) {
  83 + // 1. 校验存在
  84 + MesQcDefectRecordDO record = validateDefectRecordExists(id);
  85 +
  86 + // 2. 删除
  87 + defectRecordMapper.deleteById(id);
  88 +
  89 + // 3. 重新计算缺陷统计
  90 + recalculateDefectStats(record.getQcType(), record.getQcId());
  91 + }
  92 +
  93 + @Override
  94 + public PageResult<MesQcDefectRecordDO> getDefectRecordPage(MesQcDefectRecordPageReqVO pageReqVO) {
  95 + return defectRecordMapper.selectPage(pageReqVO);
  96 + }
  97 +
  98 + @Override
  99 + public void deleteByQcTypeAndQcId(Integer qcType, Long qcId) {
  100 + defectRecordMapper.deleteByQcTypeAndQcId(qcType, qcId);
  101 + }
  102 +
  103 + // ==================== 校验方法 ====================
  104 +
  105 + // TODO @AI:validateQcExists、validateQcLineExists 合并成一个方法;然后调用对应的 service 方法;
  106 + private void validateQcExists(Integer qcType, Long qcId) {
  107 + if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) {
  108 + if (iqcMapper.selectById(qcId) == null) {
  109 + throw exception(QC_IQC_NOT_EXISTS);
  110 + }
  111 + } else {
  112 + // 后续扩展:IPQC、OQC、RQC
  113 + throw exception(QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED);
  114 + }
  115 + }
  116 +
  117 + // TODO @AI:去掉这个,相信调用方
  118 + private void validateQcLineExists(Integer qcType, Long lineId) {
  119 + if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) {
  120 + MesQcIqcLineDO line = iqcLineService.getIqcLine(lineId);
  121 + if (line == null) {
  122 + throw exception(QC_IQC_LINE_NOT_EXISTS);
  123 + }
  124 + } else {
  125 + // 后续扩展:IPQC、OQC、RQC
  126 + throw exception(QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED);
  127 + }
  128 + }
  129 +
  130 + private MesQcDefectRecordDO validateDefectRecordExists(Long id) {
  131 + MesQcDefectRecordDO record = defectRecordMapper.selectById(id);
  132 + if (record == null) {
  133 + throw exception(QC_DEFECT_RECORD_NOT_EXISTS);
  134 + }
  135 + return record;
  136 + }
  137 +
  138 + // ==================== 缺陷统计 ====================
  139 +
  140 + /**
  141 + * 重新计算缺陷统计(行级 + 主表级)
  142 + *
  143 + * @param qcType 检验类型
  144 + * @param qcId 检验单 ID
  145 + */
  146 + @SuppressWarnings("DuplicatedCode")
  147 + private void recalculateDefectStats(Integer qcType, Long qcId) {
  148 + // 1. 查询所有缺陷记录
  149 + List<MesQcDefectRecordDO> records = defectRecordMapper.selectListByQcTypeAndQcId(qcType, qcId);
  150 +
  151 + if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) {
  152 + // TODO @AI:直接调用 iqcservice 方法;qcId、records
  153 + recalculateIqcDefectStats(qcId, records);
  154 + } else {
  155 + // 后续扩展:IPQC、OQC、RQC
  156 + throw exception(QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED);
  157 + }
  158 + }
  159 +
  160 + /**
  161 + * IQC 缺陷统计重算
  162 + */
  163 + private void recalculateIqcDefectStats(Long qcId, List<MesQcDefectRecordDO> records) {
  164 + // 1. 按行汇总缺陷数量,批量更新
  165 + // TODO @AI:1 这块的逻辑,放到 iqcLineService 里,使用 qcId、records;
  166 + List<MesQcIqcLineDO> lines = iqcLineService.selectListByIqcId(qcId);
  167 + List<MesQcIqcLineDO> updateLines = new ArrayList<>();
  168 + for (MesQcIqcLineDO line : lines) {
  169 + int critical = 0, major = 0, minor = 0;
  170 + for (MesQcDefectRecordDO record : records) {
  171 + if (ObjUtil.notEqual(record.getLineId(), line.getId())) {
  172 + continue;
  173 + }
  174 + int quantity = ObjUtil.defaultIfNull(record.getQuantity(), 1);
  175 + if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) {
  176 + critical += quantity;
  177 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MAJOR.getType())) {
  178 + major += quantity;
  179 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MINOR.getType())) {
  180 + minor += quantity;
  181 + } else {
  182 + throw exception(QC_DEFECT_RECORD_LEVEL_UNKNOWN);
  183 + }
  184 + }
  185 + updateLines.add(new MesQcIqcLineDO().setId(line.getId())
  186 + .setCriticalQuantity(critical).setMajorQuantity(major).setMinorQuantity(minor));
  187 + }
  188 + iqcLineService.batchUpdateDefectStats(updateLines);
  189 +
  190 + // 2. 汇总主表的缺陷数量
  191 + // TODO @AI:2-4 这块的逻辑,放到 iqcService 里,使用 qcId、records;
  192 + int totalCritical = 0, totalMajor = 0, totalMinor = 0;
  193 + for (MesQcDefectRecordDO record : records) {
  194 + int quantity = ObjUtil.defaultIfNull(record.getQuantity(), 1);
  195 + if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) {
  196 + totalCritical += quantity;
  197 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MAJOR.getType())) {
  198 + totalMajor += quantity;
  199 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MINOR.getType())) {
  200 + totalMinor += quantity;
  201 + } else {
  202 + throw exception(QC_DEFECT_RECORD_LEVEL_UNKNOWN);
  203 + }
  204 + }
  205 +
  206 + // 3. 计算缺陷率
  207 + MesQcIqcDO iqc = iqcMapper.selectById(qcId);
  208 + BigDecimal criticalRate = BigDecimal.ZERO;
  209 + BigDecimal majorRate = BigDecimal.ZERO;
  210 + BigDecimal minorRate = BigDecimal.ZERO;
  211 + if (iqc != null && iqc.getCheckQuantity() != null && iqc.getCheckQuantity() > 0) {
  212 + BigDecimal checkQty = BigDecimal.valueOf(iqc.getCheckQuantity());
  213 + criticalRate = BigDecimal.valueOf(totalCritical).multiply(BigDecimal.valueOf(100))
  214 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  215 + majorRate = BigDecimal.valueOf(totalMajor).multiply(BigDecimal.valueOf(100))
  216 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  217 + minorRate = BigDecimal.valueOf(totalMinor).multiply(BigDecimal.valueOf(100))
  218 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  219 + }
  220 +
  221 + // 4. 更新主表
  222 + MesQcIqcDO updateIqc = new MesQcIqcDO().setId(qcId)
  223 + .setCriticalQuantity(totalCritical).setMajorQuantity(totalMajor).setMinorQuantity(totalMinor)
  224 + .setCriticalRate(criticalRate).setMajorRate(majorRate).setMinorRate(minorRate);
  225 + iqcMapper.updateById(updateIqc);
  226 + }
  227 +
  228 +}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcDefectServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.mes.service.qc.lqc;  
2 -  
3 -import cn.hutool.core.util.ObjUtil;  
4 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectPageReqVO;  
7 -import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectSaveReqVO;  
8 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDO;  
9 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDefectDO;  
10 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcLineDO;  
11 -import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcDefectMapper;  
12 -import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcMapper;  
13 -import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;  
14 -import jakarta.annotation.Resource;  
15 -import org.springframework.context.annotation.Lazy;  
16 -import org.springframework.stereotype.Service;  
17 -import org.springframework.transaction.annotation.Transactional;  
18 -import org.springframework.validation.annotation.Validated;  
19 -  
20 -import java.math.BigDecimal;  
21 -import java.math.RoundingMode;  
22 -import java.util.ArrayList;  
23 -import java.util.List;  
24 -import java.util.Objects;  
25 -  
26 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
27 -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;  
28 -  
29 -/**  
30 - * MES 来料检验缺陷记录 Service 实现类  
31 - *  
32 - * @author 芋道源码  
33 - */  
34 -@Service  
35 -@Validated  
36 -public class MesQcIqcDefectServiceImpl implements MesQcIqcDefectService {  
37 -  
38 - @Resource  
39 - private MesQcIqcDefectMapper iqcDefectMapper;  
40 - @Resource  
41 - private MesQcIqcMapper iqcMapper;  
42 - @Resource  
43 - @Lazy  
44 - private MesQcIqcLineService iqcLineService;  
45 -  
46 - @Override  
47 - @Transactional(rollbackFor = Exception.class)  
48 - public Long createIqcDefect(MesQcIqcDefectSaveReqVO createReqVO) {  
49 - // 1.1 校验 IQC 存在  
50 - validateIqcExists(createReqVO.getIqcId());  
51 - // 1.2 校验行存在  
52 - validateIqcLineExists(createReqVO.getLineId());  
53 -  
54 - // 2. 插入  
55 - MesQcIqcDefectDO defect = BeanUtils.toBean(createReqVO, MesQcIqcDefectDO.class);  
56 - iqcDefectMapper.insert(defect);  
57 -  
58 - // 3. 重新计算缺陷统计  
59 - recalculateDefectStats(createReqVO.getIqcId());  
60 - return defect.getId();  
61 - }  
62 -  
63 - @Override  
64 - @Transactional(rollbackFor = Exception.class)  
65 - public void updateIqcDefect(MesQcIqcDefectSaveReqVO updateReqVO) {  
66 - // 1. 校验存在  
67 - validateIqcDefectExists(updateReqVO.getId());  
68 -  
69 - // 2. 更新  
70 - MesQcIqcDefectDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIqcDefectDO.class);  
71 - iqcDefectMapper.updateById(updateObj);  
72 -  
73 - // 3. 重新计算缺陷统计  
74 - recalculateDefectStats(updateReqVO.getIqcId());  
75 - }  
76 -  
77 - @Override  
78 - @Transactional(rollbackFor = Exception.class)  
79 - public void deleteIqcDefect(Long id) {  
80 - // 1. 校验存在  
81 - MesQcIqcDefectDO defect = validateIqcDefectExists(id);  
82 -  
83 - // 2. 删除  
84 - iqcDefectMapper.deleteById(id);  
85 -  
86 - // 3. 重新计算缺陷统计  
87 - recalculateDefectStats(defect.getIqcId());  
88 - }  
89 -  
90 - @Override  
91 - public PageResult<MesQcIqcDefectDO> getIqcDefectPage(MesQcIqcDefectPageReqVO pageReqVO) {  
92 - return iqcDefectMapper.selectPage(pageReqVO);  
93 - }  
94 -  
95 - @Override  
96 - public void deleteByIqcId(Long iqcId) {  
97 - iqcDefectMapper.deleteByIqcId(iqcId);  
98 - }  
99 -  
100 - private void validateIqcExists(Long iqcId) {  
101 - if (iqcMapper.selectById(iqcId) == null) {  
102 - throw exception(QC_IQC_NOT_EXISTS);  
103 - }  
104 - }  
105 -  
106 - private void validateIqcLineExists(Long lineId) {  
107 - MesQcIqcLineDO line = iqcLineService.getIqcLine(lineId);  
108 - if (line == null) {  
109 - throw exception(QC_IQC_LINE_NOT_EXISTS);  
110 - }  
111 - }  
112 -  
113 - private MesQcIqcDefectDO validateIqcDefectExists(Long id) {  
114 - MesQcIqcDefectDO defect = iqcDefectMapper.selectById(id);  
115 - if (defect == null) {  
116 - throw exception(QC_IQC_DEFECT_NOT_EXISTS);  
117 - }  
118 - return defect;  
119 - }  
120 -  
121 - /**  
122 - * 重新计算缺陷统计(行级 + 主表级)  
123 - *  
124 - * @param iqcId 来料检验单 ID  
125 - */  
126 - @SuppressWarnings("DuplicatedCode")  
127 - private void recalculateDefectStats(Long iqcId) {  
128 - // 1. 查询所有缺陷记录  
129 - List<MesQcIqcDefectDO> defects = iqcDefectMapper.selectListByIqcId(iqcId);  
130 -  
131 - // 2. 按行汇总缺陷数量,批量更新  
132 - List<MesQcIqcLineDO> lines = iqcLineService.selectListByIqcId(iqcId);  
133 - List<MesQcIqcLineDO> updateLines = new ArrayList<>();  
134 - for (MesQcIqcLineDO line : lines) {  
135 - int critical = 0, major = 0, minor = 0;  
136 - for (MesQcIqcDefectDO defect : defects) {  
137 - if (ObjUtil.notEqual(defect.getLineId(), line.getId())) {  
138 - continue;  
139 - }  
140 - int quantity = ObjUtil.defaultIfNull(defect.getDefectQuantity(), 1);  
141 - if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) {  
142 - critical += quantity;  
143 - } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MAJOR.getType())) {  
144 - major += quantity;  
145 - } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MINOR.getType())) {  
146 - minor += quantity;  
147 - } else {  
148 - throw exception(QC_IQC_DEFECT_LEVEL_UNKNOWN);  
149 - }  
150 - }  
151 - updateLines.add(new MesQcIqcLineDO().setId(line.getId())  
152 - .setCriticalQuantity(critical).setMajorQuantity(major).setMinorQuantity(minor));  
153 - }  
154 - iqcLineService.batchUpdateDefectStats(updateLines);  
155 -  
156 - // 3.1 汇总主表的缺陷数量  
157 - int totalCritical = 0, totalMajor = 0, totalMinor = 0;  
158 - for (MesQcIqcDefectDO defect : defects) {  
159 - int quantity = ObjUtil.defaultIfNull(defect.getDefectQuantity(), 1);  
160 - if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) {  
161 - totalCritical += quantity;  
162 - } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MAJOR.getType())) {  
163 - totalMajor += quantity;  
164 - } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MINOR.getType())) {  
165 - totalMinor += quantity;  
166 - } else {  
167 - throw exception(QC_IQC_DEFECT_LEVEL_UNKNOWN);  
168 - }  
169 - }  
170 - // 3.2 计算缺陷率  
171 - MesQcIqcDO iqc = iqcMapper.selectById(iqcId);  
172 - BigDecimal criticalRate = BigDecimal.ZERO;  
173 - BigDecimal majorRate = BigDecimal.ZERO;  
174 - BigDecimal minorRate = BigDecimal.ZERO;  
175 - if (iqc != null && iqc.getCheckQuantity() != null && iqc.getCheckQuantity() > 0) {  
176 - BigDecimal checkQty = BigDecimal.valueOf(iqc.getCheckQuantity());  
177 - criticalRate = BigDecimal.valueOf(totalCritical).multiply(BigDecimal.valueOf(100))  
178 - .divide(checkQty, 2, RoundingMode.HALF_UP);  
179 - majorRate = BigDecimal.valueOf(totalMajor).multiply(BigDecimal.valueOf(100))  
180 - .divide(checkQty, 2, RoundingMode.HALF_UP);  
181 - minorRate = BigDecimal.valueOf(totalMinor).multiply(BigDecimal.valueOf(100))  
182 - .divide(checkQty, 2, RoundingMode.HALF_UP);  
183 - }  
184 - // 3.3 更新主表  
185 - MesQcIqcDO updateIqc = new MesQcIqcDO().setId(iqcId)  
186 - .setCriticalQuantity(totalCritical).setMajorQuantity(totalMajor).setMinorQuantity(totalMinor)  
187 - .setCriticalRate(criticalRate).setMajorRate(majorRate).setMinorRate(minorRate);  
188 - iqcMapper.updateById(updateIqc);  
189 - }  
190 -  
191 -}  
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcServiceImpl.java
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemD @@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemD
12 import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcMapper; 12 import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcMapper;
13 import cn.iocoder.yudao.module.mes.enums.qc.MesQcIqcStatusEnum; 13 import cn.iocoder.yudao.module.mes.enums.qc.MesQcIqcStatusEnum;
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.qc.defect.MesQcDefectRecordService;
15 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService; 16 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService;
16 import jakarta.annotation.Resource; 17 import jakarta.annotation.Resource;
17 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
@@ -38,7 +39,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -38,7 +39,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
38 @Resource 39 @Resource
39 private MesQcIqcLineService iqcLineService; 40 private MesQcIqcLineService iqcLineService;
40 @Resource 41 @Resource
41 - private MesQcIqcDefectService iqcDefectService; 42 + private MesQcDefectRecordService defectRecordService;
42 43
43 @Override 44 @Override
44 @Transactional(rollbackFor = Exception.class) 45 @Transactional(rollbackFor = Exception.class)
@@ -117,7 +118,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -117,7 +118,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
117 // 2.2 级联删除行 118 // 2.2 级联删除行
118 iqcLineService.deleteByIqcId(id); 119 iqcLineService.deleteByIqcId(id);
119 // 2.3 级联删除缺陷记录 120 // 2.3 级联删除缺陷记录
120 - iqcDefectService.deleteByIqcId(id); 121 + defectRecordService.deleteByQcTypeAndQcId(MesQcTypeEnum.IQC.getType(), id);
121 } 122 }
122 123
123 /** 124 /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateService.java
@@ -107,6 +107,17 @@ public interface MesQcTemplateService { @@ -107,6 +107,17 @@ public interface MesQcTemplateService {
107 */ 107 */
108 MesQcTemplateItemDO getTemplateItemByTemplateIdAndItemId(Long templateId, Long itemId); 108 MesQcTemplateItemDO getTemplateItemByTemplateIdAndItemId(Long templateId, Long itemId);
109 109
  110 + /**
  111 + * 根据产品物料 ID 和 QC 类型查找检验模板
  112 + *
  113 + * 查找逻辑:在 template_item 中找到匹配 itemId 的模板,再校验该模板的 types 包含给定的 qcType
  114 + *
  115 + * @param itemId 产品物料 ID
  116 + * @param qcType 检测种类(枚举 {@link cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum})
  117 + * @return 匹配的检验模板(可能为 null)
  118 + */
  119 + MesQcTemplateDO getTemplateByItemIdAndType(Long itemId, Integer qcType);
  120 +
110 // ========== 质检方案-检测指标项 ========== 121 // ========== 质检方案-检测指标项 ==========
111 122
112 /** 123 /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateServiceImpl.java
@@ -109,6 +109,25 @@ public class MesQcTemplateServiceImpl implements MesQcTemplateService { @@ -109,6 +109,25 @@ public class MesQcTemplateServiceImpl implements MesQcTemplateService {
109 } 109 }
110 110
111 @Override 111 @Override
  112 + public MesQcTemplateDO getTemplateByItemIdAndType(Long itemId, Integer qcType) {
  113 + // 1.1 查出 itemId 关联的所有 templateId
  114 + List<MesQcTemplateItemDO> templateItems = templateItemMapper.selectListByItemId(itemId);
  115 + if (CollUtil.isEmpty(templateItems)) {
  116 + return null;
  117 + }
  118 + // 1.2 批量查模板,筛选 types 包含给定 qcType 的第一个
  119 + // TODO @AI:convertSet;
  120 + List<Long> templateIds = templateItems.stream()
  121 + .map(MesQcTemplateItemDO::getTemplateId).distinct().toList();
  122 + List<MesQcTemplateDO> templates = templateMapper.selectByIds(templateIds);
  123 +
  124 + // 2. 筛选 types 包含给定 qcType 的第一个 TODO @AI:findOne;
  125 + return templates.stream()
  126 + .filter(t -> CollUtil.contains(t.getTypes(), qcType))
  127 + .findFirst().orElse(null);
  128 + }
  129 +
  130 + @Override
112 public MesQcTemplateDO getTemplate(Long id) { 131 public MesQcTemplateDO getTemplate(Long id) {
113 return templateMapper.selectById(id); 132 return templateMapper.selectById(id);
114 } 133 }