Commit 1b0d0773a3430448634fe91fdcf5fb42261431ed

Authored by YunaiV
1 parent b26ff0d7

feat(mes): 更新质检缺陷记录权限标识,重构相关校验逻辑,优化缺陷统计计算

yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/defect/MesQcDefectRecordController.java
@@ -28,17 +28,17 @@ public class MesQcDefectRecordController { @@ -28,17 +28,17 @@ public class MesQcDefectRecordController {
28 @Resource 28 @Resource
29 private MesQcDefectRecordService defectRecordService; 29 private MesQcDefectRecordService defectRecordService;
30 30
31 - // TODO @AI:使用 mes:qc-record:create 权限标识先;不要搞太复杂;当然,也写个 todo @芋艿:说下这个情况 31 + // TODO @芋艿:说下 mes:qc-record 这个权限标识的情况
32 @PostMapping("/create") 32 @PostMapping("/create")
33 @Operation(summary = "创建质检缺陷记录") 33 @Operation(summary = "创建质检缺陷记录")
34 - @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:create')") 34 + @PreAuthorize("@ss.hasPermission('mes:qc-record:create')")
35 public CommonResult<Long> createDefectRecord(@Valid @RequestBody MesQcDefectRecordSaveReqVO createReqVO) { 35 public CommonResult<Long> createDefectRecord(@Valid @RequestBody MesQcDefectRecordSaveReqVO createReqVO) {
36 return success(defectRecordService.createDefectRecord(createReqVO)); 36 return success(defectRecordService.createDefectRecord(createReqVO));
37 } 37 }
38 38
39 @PutMapping("/update") 39 @PutMapping("/update")
40 @Operation(summary = "更新质检缺陷记录") 40 @Operation(summary = "更新质检缺陷记录")
41 - @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:update')") 41 + @PreAuthorize("@ss.hasPermission('mes:qc-record:update')")
42 public CommonResult<Boolean> updateDefectRecord(@Valid @RequestBody MesQcDefectRecordSaveReqVO updateReqVO) { 42 public CommonResult<Boolean> updateDefectRecord(@Valid @RequestBody MesQcDefectRecordSaveReqVO updateReqVO) {
43 defectRecordService.updateDefectRecord(updateReqVO); 43 defectRecordService.updateDefectRecord(updateReqVO);
44 return success(true); 44 return success(true);
@@ -47,7 +47,7 @@ public class MesQcDefectRecordController { @@ -47,7 +47,7 @@ public class MesQcDefectRecordController {
47 @DeleteMapping("/delete") 47 @DeleteMapping("/delete")
48 @Operation(summary = "删除质检缺陷记录") 48 @Operation(summary = "删除质检缺陷记录")
49 @Parameter(name = "id", description = "编号", required = true) 49 @Parameter(name = "id", description = "编号", required = true)
50 - @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:update')") 50 + @PreAuthorize("@ss.hasPermission('mes:qc-record:update')")
51 public CommonResult<Boolean> deleteDefectRecord(@RequestParam("id") Long id) { 51 public CommonResult<Boolean> deleteDefectRecord(@RequestParam("id") Long id) {
52 defectRecordService.deleteDefectRecord(id); 52 defectRecordService.deleteDefectRecord(id);
53 return success(true); 53 return success(true);
@@ -55,7 +55,7 @@ public class MesQcDefectRecordController { @@ -55,7 +55,7 @@ public class MesQcDefectRecordController {
55 55
56 @GetMapping("/page") 56 @GetMapping("/page")
57 @Operation(summary = "获得质检缺陷记录分页") 57 @Operation(summary = "获得质检缺陷记录分页")
58 - @PreAuthorize("@ss.hasPermission('mes:qc-defect-record:query')") 58 + @PreAuthorize("@ss.hasPermission('mes:qc-record:query')")
59 public CommonResult<PageResult<MesQcDefectRecordRespVO>> getDefectRecordPage(@Valid MesQcDefectRecordPageReqVO pageReqVO) { 59 public CommonResult<PageResult<MesQcDefectRecordRespVO>> getDefectRecordPage(@Valid MesQcDefectRecordPageReqVO pageReqVO) {
60 PageResult<MesQcDefectRecordDO> pageResult = defectRecordService.getDefectRecordPage(pageReqVO); 60 PageResult<MesQcDefectRecordDO> pageResult = defectRecordService.getDefectRecordPage(pageReqVO);
61 return success(BeanUtils.toBean(pageResult, MesQcDefectRecordRespVO.class)); 61 return success(BeanUtils.toBean(pageResult, MesQcDefectRecordRespVO.class));
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/defect/MesQcDefectRecordServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.qc.defect; 1 package cn.iocoder.yudao.module.mes.service.qc.defect;
2 2
3 -import cn.hutool.core.util.ObjUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordPageReqVO; 5 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; 6 import cn.iocoder.yudao.module.mes.controller.admin.qc.defect.vo.MesQcDefectRecordSaveReqVO;
8 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO; 7 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; 8 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; 9 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
15 import cn.iocoder.yudao.module.mes.service.qc.lqc.MesQcIqcLineService; 10 import cn.iocoder.yudao.module.mes.service.qc.lqc.MesQcIqcLineService;
  11 +import cn.iocoder.yudao.module.mes.service.qc.lqc.MesQcIqcService;
16 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
17 import org.springframework.context.annotation.Lazy; 13 import org.springframework.context.annotation.Lazy;
18 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
19 import org.springframework.transaction.annotation.Transactional; 15 import org.springframework.transaction.annotation.Transactional;
20 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
21 17
22 -import java.math.BigDecimal;  
23 -import java.math.RoundingMode;  
24 -import java.util.ArrayList;  
25 import java.util.List; 18 import java.util.List;
26 import java.util.Objects; 19 import java.util.Objects;
27 20
@@ -40,9 +33,9 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService { @@ -40,9 +33,9 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService {
40 @Resource 33 @Resource
41 private MesQcDefectRecordMapper defectRecordMapper; 34 private MesQcDefectRecordMapper defectRecordMapper;
42 35
43 - // TODO @AI:不要调用 mapper,调用对方的 service;  
44 @Resource 36 @Resource
45 - private MesQcIqcMapper iqcMapper; 37 + @Lazy
  38 + private MesQcIqcService iqcService;
46 @Resource 39 @Resource
47 @Lazy 40 @Lazy
48 private MesQcIqcLineService iqcLineService; 41 private MesQcIqcLineService iqcLineService;
@@ -50,9 +43,8 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService { @@ -50,9 +43,8 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService {
50 @Override 43 @Override
51 @Transactional(rollbackFor = Exception.class) 44 @Transactional(rollbackFor = Exception.class)
52 public Long createDefectRecord(MesQcDefectRecordSaveReqVO createReqVO) { 45 public Long createDefectRecord(MesQcDefectRecordSaveReqVO createReqVO) {
53 - // 1. 校验主表和行表存在  
54 - validateQcExists(createReqVO.getQcType(), createReqVO.getQcId());  
55 - validateQcLineExists(createReqVO.getQcType(), createReqVO.getLineId()); 46 + // 1. 校验检验单和检验行存在
  47 + validateQcAndLineExists(createReqVO.getQcType(), createReqVO.getQcId(), createReqVO.getLineId());
56 48
57 // 2. 插入 49 // 2. 插入
58 MesQcDefectRecordDO record = BeanUtils.toBean(createReqVO, MesQcDefectRecordDO.class); 50 MesQcDefectRecordDO record = BeanUtils.toBean(createReqVO, MesQcDefectRecordDO.class);
@@ -102,25 +94,10 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService { @@ -102,25 +94,10 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService {
102 94
103 // ==================== 校验方法 ==================== 95 // ==================== 校验方法 ====================
104 96
105 - // TODO @AI:validateQcExists、validateQcLineExists 合并成一个方法;然后调用对应的 service 方法;  
106 - private void validateQcExists(Integer qcType, Long qcId) { 97 + private void validateQcAndLineExists(Integer qcType, Long qcId, Long lineId) {
107 if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) { 98 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 - } 99 + iqcService.validateIqcExists(qcId);
  100 + iqcLineService.validateIqcLineExists(lineId);
124 } else { 101 } else {
125 // 后续扩展:IPQC、OQC、RQC 102 // 后续扩展:IPQC、OQC、RQC
126 throw exception(QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED); 103 throw exception(QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED);
@@ -138,91 +115,19 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService { @@ -138,91 +115,19 @@ public class MesQcDefectRecordServiceImpl implements MesQcDefectRecordService {
138 // ==================== 缺陷统计 ==================== 115 // ==================== 缺陷统计 ====================
139 116
140 /** 117 /**
141 - * 重新计算缺陷统计(行级 + 主表级) 118 + * 重新计算缺陷统计(行级 + 主表级),委托给对应的检验 Service
142 * 119 *
143 * @param qcType 检验类型 120 * @param qcType 检验类型
144 * @param qcId 检验单 ID 121 * @param qcId 检验单 ID
145 */ 122 */
146 - @SuppressWarnings("DuplicatedCode")  
147 private void recalculateDefectStats(Integer qcType, Long qcId) { 123 private void recalculateDefectStats(Integer qcType, Long qcId) {
148 - // 1. 查询所有缺陷记录  
149 List<MesQcDefectRecordDO> records = defectRecordMapper.selectListByQcTypeAndQcId(qcType, qcId); 124 List<MesQcDefectRecordDO> records = defectRecordMapper.selectListByQcTypeAndQcId(qcType, qcId);
150 -  
151 if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) { 125 if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) {
152 - // TODO @AI:直接调用 iqcservice 方法;qcId、records  
153 - recalculateIqcDefectStats(qcId, records); 126 + iqcService.recalculateDefectStats(qcId, records);
154 } else { 127 } else {
155 // 后续扩展:IPQC、OQC、RQC 128 // 后续扩展:IPQC、OQC、RQC
156 throw exception(QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED); 129 throw exception(QC_DEFECT_RECORD_QC_TYPE_UNSUPPORTED);
157 } 130 }
158 } 131 }
159 132
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 } 133 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcLineService.java
@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLinePageReqVO; 4 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLinePageReqVO;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcLineDO; 5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcLineDO;
6 6
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO;
  8 +
7 import java.util.List; 9 import java.util.List;
8 10
9 /** 11 /**
@@ -14,6 +16,14 @@ import java.util.List; @@ -14,6 +16,14 @@ import java.util.List;
14 public interface MesQcIqcLineService { 16 public interface MesQcIqcLineService {
15 17
16 /** 18 /**
  19 + * 校验来料检验行存在
  20 + *
  21 + * @param id 编号
  22 + * @return 来料检验行
  23 + */
  24 + MesQcIqcLineDO validateIqcLineExists(Long id);
  25 +
  26 + /**
17 * 获得来料检验行 27 * 获得来料检验行
18 * 28 *
19 * @param id 编号 29 * @param id 编号
@@ -30,14 +40,6 @@ public interface MesQcIqcLineService { @@ -30,14 +40,6 @@ public interface MesQcIqcLineService {
30 PageResult<MesQcIqcLineDO> getIqcLinePage(MesQcIqcLinePageReqVO pageReqVO); 40 PageResult<MesQcIqcLineDO> getIqcLinePage(MesQcIqcLinePageReqVO pageReqVO);
31 41
32 /** 42 /**
33 - * 根据来料检验单 ID 查询所有行  
34 - *  
35 - * @param iqcId 来料检验单 ID  
36 - * @return 行列表  
37 - */  
38 - List<MesQcIqcLineDO> selectListByIqcId(Long iqcId);  
39 -  
40 - /**  
41 * 从模板指标自动生成检验行 43 * 从模板指标自动生成检验行
42 * 44 *
43 * @param iqcId 来料检验单 ID 45 * @param iqcId 来料检验单 ID
@@ -46,11 +48,12 @@ public interface MesQcIqcLineService { @@ -46,11 +48,12 @@ public interface MesQcIqcLineService {
46 void createLinesFromTemplate(Long iqcId, Long templateId); 48 void createLinesFromTemplate(Long iqcId, Long templateId);
47 49
48 /** 50 /**
49 - * 批量更新行的缺陷统计数量 51 + * 根据缺陷记录重新计算各行的缺陷统计数量
50 * 52 *
51 - * @param lines 待更新的行列表(只需设置 id、criticalQuantity、majorQuantity、minorQuantity) 53 + * @param iqcId 来料检验单 ID
  54 + * @param records 缺陷记录列表
52 */ 55 */
53 - void batchUpdateDefectStats(List<MesQcIqcLineDO> lines); 56 + void recalculateLineDefectStats(Long iqcId, List<MesQcDefectRecordDO> records);
54 57
55 /** 58 /**
56 * 根据来料检验单 ID 级联删除所有行 59 * 根据来料检验单 ID 级联删除所有行
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcLineServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.qc.lqc; 1 package cn.iocoder.yudao.module.mes.service.qc.lqc;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLinePageReqVO; 6 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLinePageReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcLineDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcLineDO;
6 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO;
7 import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcLineMapper; 10 import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcLineMapper;
8 import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateIndicatorMapper; 11 import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateIndicatorMapper;
  12 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
9 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
10 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
11 import org.springframework.validation.annotation.Validated; 15 import org.springframework.validation.annotation.Validated;
12 16
  17 +import java.util.ArrayList;
13 import java.util.List; 18 import java.util.List;
  19 +import java.util.Objects;
14 20
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
15 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 22 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  23 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
16 24
17 /** 25 /**
18 * MES 来料检验单行 Service 实现类 26 * MES 来料检验单行 Service 实现类
@@ -29,6 +37,15 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService { @@ -29,6 +37,15 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService {
29 private MesQcTemplateIndicatorMapper templateIndicatorMapper; 37 private MesQcTemplateIndicatorMapper templateIndicatorMapper;
30 38
31 @Override 39 @Override
  40 + public MesQcIqcLineDO validateIqcLineExists(Long id) {
  41 + MesQcIqcLineDO line = iqcLineMapper.selectById(id);
  42 + if (line == null) {
  43 + throw exception(QC_IQC_LINE_NOT_EXISTS);
  44 + }
  45 + return line;
  46 + }
  47 +
  48 + @Override
32 public MesQcIqcLineDO getIqcLine(Long id) { 49 public MesQcIqcLineDO getIqcLine(Long id) {
33 return iqcLineMapper.selectById(id); 50 return iqcLineMapper.selectById(id);
34 } 51 }
@@ -39,11 +56,6 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService { @@ -39,11 +56,6 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService {
39 } 56 }
40 57
41 @Override 58 @Override
42 - public List<MesQcIqcLineDO> selectListByIqcId(Long iqcId) {  
43 - return iqcLineMapper.selectListByIqcId(iqcId);  
44 - }  
45 -  
46 - @Override  
47 public void createLinesFromTemplate(Long iqcId, Long templateId) { 59 public void createLinesFromTemplate(Long iqcId, Long templateId) {
48 List<MesQcTemplateIndicatorDO> indicators = templateIndicatorMapper.selectListByTemplateId(templateId); 60 List<MesQcTemplateIndicatorDO> indicators = templateIndicatorMapper.selectListByTemplateId(templateId);
49 List<MesQcIqcLineDO> lines = convertList(indicators, indicator -> new MesQcIqcLineDO() 61 List<MesQcIqcLineDO> lines = convertList(indicators, indicator -> new MesQcIqcLineDO()
@@ -57,9 +69,34 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService { @@ -57,9 +69,34 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService {
57 } 69 }
58 70
59 @Override 71 @Override
60 - public void batchUpdateDefectStats(List<MesQcIqcLineDO> lines) { 72 + public void recalculateLineDefectStats(Long iqcId, List<MesQcDefectRecordDO> records) {
  73 + List<MesQcIqcLineDO> lines = iqcLineMapper.selectListByIqcId(iqcId);
  74 + if (CollUtil.isEmpty(lines)) {
  75 + return;
  76 + }
  77 + List<MesQcIqcLineDO> updateLines = new ArrayList<>(lines.size());
61 for (MesQcIqcLineDO line : lines) { 78 for (MesQcIqcLineDO line : lines) {
62 - iqcLineMapper.updateById(line); 79 + int critical = 0, major = 0, minor = 0;
  80 + for (MesQcDefectRecordDO record : records) {
  81 + if (ObjUtil.notEqual(record.getLineId(), line.getId())) {
  82 + continue;
  83 + }
  84 + int quantity = ObjUtil.defaultIfNull(record.getQuantity(), 1);
  85 + if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) {
  86 + critical += quantity;
  87 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MAJOR.getType())) {
  88 + major += quantity;
  89 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MINOR.getType())) {
  90 + minor += quantity;
  91 + } else {
  92 + throw exception(QC_DEFECT_RECORD_LEVEL_UNKNOWN);
  93 + }
  94 + }
  95 + updateLines.add(new MesQcIqcLineDO().setId(line.getId())
  96 + .setCriticalQuantity(critical).setMajorQuantity(major).setMinorQuantity(minor));
  97 + }
  98 + for (MesQcIqcLineDO updateLine : updateLines) {
  99 + iqcLineMapper.updateById(updateLine);
63 } 100 }
64 } 101 }
65 102
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcService.java
@@ -3,9 +3,12 @@ package cn.iocoder.yudao.module.mes.service.qc.lqc; @@ -3,9 +3,12 @@ package cn.iocoder.yudao.module.mes.service.qc.lqc;
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.MesQcIqcPageReqVO; 4 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
5 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO; 5 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defect.MesQcDefectRecordDO;
6 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDO; 7 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDO;
7 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
8 9
  10 +import java.util.List;
  11 +
9 /** 12 /**
10 * MES 来料检验单(IQC) Service 接口 13 * MES 来料检验单(IQC) Service 接口
11 * 14 *
@@ -43,6 +46,14 @@ public interface MesQcIqcService { @@ -43,6 +46,14 @@ public interface MesQcIqcService {
43 void deleteIqc(Long id); 46 void deleteIqc(Long id);
44 47
45 /** 48 /**
  49 + * 校验来料检验单存在
  50 + *
  51 + * @param id 编号
  52 + * @return 来料检验单
  53 + */
  54 + MesQcIqcDO validateIqcExists(Long id);
  55 +
  56 + /**
46 * 获得来料检验单 57 * 获得来料检验单
47 * 58 *
48 * @param id 编号 59 * @param id 编号
@@ -58,4 +69,12 @@ public interface MesQcIqcService { @@ -58,4 +69,12 @@ public interface MesQcIqcService {
58 */ 69 */
59 PageResult<MesQcIqcDO> getIqcPage(MesQcIqcPageReqVO pageReqVO); 70 PageResult<MesQcIqcDO> getIqcPage(MesQcIqcPageReqVO pageReqVO);
60 71
  72 + /**
  73 + * 根据缺陷记录重新计算主表的缺陷统计(含行级下沉)
  74 + *
  75 + * @param iqcId 来料检验单 ID
  76 + * @param records 缺陷记录列表
  77 + */
  78 + void recalculateDefectStats(Long iqcId, List<MesQcDefectRecordDO> records);
  79 +
61 } 80 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcServiceImpl.java
@@ -6,10 +6,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,10 +6,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO; 7 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
8 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO; 8 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
  9 +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.MesQcIqcDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO; 11 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO; 12 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
12 import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcMapper; 13 import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcMapper;
  14 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
13 import cn.iocoder.yudao.module.mes.enums.qc.MesQcIqcStatusEnum; 15 import cn.iocoder.yudao.module.mes.enums.qc.MesQcIqcStatusEnum;
14 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; 16 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
15 import cn.iocoder.yudao.module.mes.service.qc.defect.MesQcDefectRecordService; 17 import cn.iocoder.yudao.module.mes.service.qc.defect.MesQcDefectRecordService;
@@ -19,6 +21,11 @@ import org.springframework.stereotype.Service; @@ -19,6 +21,11 @@ import org.springframework.stereotype.Service;
19 import org.springframework.transaction.annotation.Transactional; 21 import org.springframework.transaction.annotation.Transactional;
20 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
21 23
  24 +import java.math.BigDecimal;
  25 +import java.math.RoundingMode;
  26 +import java.util.List;
  27 +import java.util.Objects;
  28 +
22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
23 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 30 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
24 31
@@ -121,6 +128,15 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -121,6 +128,15 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
121 defectRecordService.deleteByQcTypeAndQcId(MesQcTypeEnum.IQC.getType(), id); 128 defectRecordService.deleteByQcTypeAndQcId(MesQcTypeEnum.IQC.getType(), id);
122 } 129 }
123 130
  131 + @Override
  132 + public MesQcIqcDO validateIqcExists(Long id) {
  133 + MesQcIqcDO iqc = iqcMapper.selectById(id);
  134 + if (iqc == null) {
  135 + throw exception(QC_IQC_NOT_EXISTS);
  136 + }
  137 + return iqc;
  138 + }
  139 +
124 /** 140 /**
125 * 校验来料检验单存在且为草稿状态 141 * 校验来料检验单存在且为草稿状态
126 * 142 *
@@ -128,10 +144,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -128,10 +144,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
128 * @return 来料检验单 144 * @return 来料检验单
129 */ 145 */
130 private MesQcIqcDO validateIqcStatusPrepare(Long id) { 146 private MesQcIqcDO validateIqcStatusPrepare(Long id) {
131 - MesQcIqcDO iqc = iqcMapper.selectById(id);  
132 - if (iqc == null) {  
133 - throw exception(QC_IQC_NOT_EXISTS);  
134 - } 147 + MesQcIqcDO iqc = validateIqcExists(id);
135 if (ObjUtil.notEqual(iqc.getStatus(), MesQcIqcStatusEnum.PREPARE.getType())) { 148 if (ObjUtil.notEqual(iqc.getStatus(), MesQcIqcStatusEnum.PREPARE.getType())) {
136 throw exception(QC_IQC_NOT_PREPARE); 149 throw exception(QC_IQC_NOT_PREPARE);
137 } 150 }
@@ -158,4 +171,45 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -158,4 +171,45 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
158 return iqcMapper.selectPage(pageReqVO); 171 return iqcMapper.selectPage(pageReqVO);
159 } 172 }
160 173
  174 + @Override
  175 + public void recalculateDefectStats(Long iqcId, List<MesQcDefectRecordDO> records) {
  176 + // 1. 行级缺陷统计
  177 + iqcLineService.recalculateLineDefectStats(iqcId, records);
  178 +
  179 + // 2.1 汇总主表的缺陷数量
  180 + int totalCritical = 0, totalMajor = 0, totalMinor = 0;
  181 + for (MesQcDefectRecordDO record : records) {
  182 + int quantity = ObjUtil.defaultIfNull(record.getQuantity(), 1);
  183 + if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) {
  184 + totalCritical += quantity;
  185 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MAJOR.getType())) {
  186 + totalMajor += quantity;
  187 + } else if (Objects.equals(record.getLevel(), MesQcDefectLevelEnum.MINOR.getType())) {
  188 + totalMinor += quantity;
  189 + } else {
  190 + throw exception(QC_DEFECT_RECORD_LEVEL_UNKNOWN);
  191 + }
  192 + }
  193 + // 2.2 计算缺陷率
  194 + MesQcIqcDO iqc = validateIqcExists(iqcId);
  195 + BigDecimal criticalRate = BigDecimal.ZERO;
  196 + BigDecimal majorRate = BigDecimal.ZERO;
  197 + BigDecimal minorRate = BigDecimal.ZERO;
  198 + if (iqc.getCheckQuantity() != null && iqc.getCheckQuantity() > 0) {
  199 + BigDecimal checkQty = BigDecimal.valueOf(iqc.getCheckQuantity());
  200 + criticalRate = BigDecimal.valueOf(totalCritical).multiply(BigDecimal.valueOf(100))
  201 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  202 + majorRate = BigDecimal.valueOf(totalMajor).multiply(BigDecimal.valueOf(100))
  203 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  204 + minorRate = BigDecimal.valueOf(totalMinor).multiply(BigDecimal.valueOf(100))
  205 + .divide(checkQty, 2, RoundingMode.HALF_UP);
  206 + }
  207 +
  208 + // 3. 更新主表
  209 + MesQcIqcDO updateIqc = new MesQcIqcDO().setId(iqcId)
  210 + .setCriticalQuantity(totalCritical).setMajorQuantity(totalMajor).setMinorQuantity(totalMinor)
  211 + .setCriticalRate(criticalRate).setMajorRate(majorRate).setMinorRate(minorRate);
  212 + iqcMapper.updateById(updateIqc);
  213 + }
  214 +
161 } 215 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateServiceImpl.java
@@ -25,8 +25,11 @@ import org.springframework.validation.annotation.Validated; @@ -25,8 +25,11 @@ import org.springframework.validation.annotation.Validated;
25 import java.util.Collection; 25 import java.util.Collection;
26 import java.util.Collections; 26 import java.util.Collections;
27 import java.util.List; 27 import java.util.List;
  28 +import java.util.Set;
28 29
29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;
30 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 33 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
31 34
32 /** 35 /**
@@ -116,15 +119,11 @@ public class MesQcTemplateServiceImpl implements MesQcTemplateService { @@ -116,15 +119,11 @@ public class MesQcTemplateServiceImpl implements MesQcTemplateService {
116 return null; 119 return null;
117 } 120 }
118 // 1.2 批量查模板,筛选 types 包含给定 qcType 的第一个 121 // 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); 122 + List<MesQcTemplateDO> templates = templateMapper.selectByIds(
  123 + convertSet(templateItems, MesQcTemplateItemDO::getTemplateId));
  124 +
  125 + // 2. 筛选 types 包含给定 qcType 的第一个
  126 + return findFirst(templates, template -> CollUtil.contains(template.getTypes(), qcType));
128 } 127 }
129 128
130 @Override 129 @Override