Commit a347943b68595902809f67578fdf5cca84b7f8af
1 parent
015eab68
feat(mes): 优化缺陷统计计算逻辑,重构模板校验方法,增强代码可读性
Showing
4 changed files
with
76 additions
and
52 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcDefectServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.mes.service.qc.lqc; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 5 | 6 | import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.defect.MesQcIqcDefectPageReqVO; |
| ... | ... | @@ -124,6 +125,7 @@ public class MesQcIqcDefectServiceImpl implements MesQcIqcDefectService { |
| 124 | 125 | * |
| 125 | 126 | * @param iqcId 来料检验单 ID |
| 126 | 127 | */ |
| 128 | + @SuppressWarnings("DuplicatedCode") | |
| 127 | 129 | private void recalculateDefectStats(Long iqcId) { |
| 128 | 130 | // 1. 查询所有缺陷记录 |
| 129 | 131 | List<MesQcIqcDefectDO> defects = iqcDefectMapper.selectListByIqcId(iqcId); |
| ... | ... | @@ -131,44 +133,38 @@ public class MesQcIqcDefectServiceImpl implements MesQcIqcDefectService { |
| 131 | 133 | // 2. 更新每行的缺陷数量 |
| 132 | 134 | List<MesQcIqcLineDO> lines = iqcLineService.selectListByIqcId(iqcId); |
| 133 | 135 | for (MesQcIqcLineDO line : lines) { |
| 134 | - int critical = 0; | |
| 135 | - int major = 0; | |
| 136 | - int minor = 0; | |
| 136 | + int critical = 0, major = 0, minor = 0; | |
| 137 | 137 | for (MesQcIqcDefectDO defect : defects) { |
| 138 | - // TODO @AI:notEquals continue;减少括号层级; | |
| 139 | - if (Objects.equals(defect.getLineId(), line.getId())) { | |
| 140 | - // TODO @AI:defaultIfNull;变量改成 quantity | |
| 141 | - int qty = defect.getDefectQuantity() != null ? defect.getDefectQuantity() : 1; | |
| 142 | - if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) { | |
| 143 | - critical += qty; | |
| 144 | - } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MAJOR.getType())) { | |
| 145 | - major += qty; | |
| 146 | - } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MINOR.getType())) { | |
| 147 | - minor += qty; | |
| 148 | - } | |
| 149 | - // TODO @AI:抛出异常;未知的缺陷等级 | |
| 138 | + if (ObjUtil.notEqual(defect.getLineId(), line.getId())) { | |
| 139 | + continue; | |
| 140 | + } | |
| 141 | + int quantity = ObjUtil.defaultIfNull(defect.getDefectQuantity(), 1); | |
| 142 | + if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) { | |
| 143 | + critical += quantity; | |
| 144 | + } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MAJOR.getType())) { | |
| 145 | + major += quantity; | |
| 146 | + } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MINOR.getType())) { | |
| 147 | + minor += quantity; | |
| 148 | + } else { | |
| 149 | + throw exception(QC_IQC_DEFECT_LEVEL_UNKNOWN); | |
| 150 | 150 | } |
| 151 | 151 | } |
| 152 | - MesQcIqcLineDO updateLine = new MesQcIqcLineDO().setId(line.getId()) | |
| 153 | - .setCriticalQuantity(critical).setMajorQuantity(major).setMinorQuantity(minor); | |
| 154 | - // 直接通过 lineService 内部更新(避免循环依赖,使用 mapper) | |
| 155 | - // TODO @AI:单独搞个 updateLineDefectStats 方法;减少 updateLine 变量的作用域; | |
| 156 | - iqcLineMapper.updateById(updateLine); | |
| 152 | + updateLineDefectStats(line.getId(), critical, major, minor); | |
| 157 | 153 | } |
| 158 | 154 | |
| 159 | 155 | // 3.1 汇总主表的缺陷数量 |
| 160 | 156 | int totalCritical = 0, totalMajor = 0, totalMinor = 0; |
| 161 | 157 | for (MesQcIqcDefectDO defect : defects) { |
| 162 | - // TODO @AI:defaultIfNull;变量改成 quantity | |
| 163 | - int qty = defect.getDefectQuantity() != null ? defect.getDefectQuantity() : 1; | |
| 158 | + int quantity = ObjUtil.defaultIfNull(defect.getDefectQuantity(), 1); | |
| 164 | 159 | if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.CRITICAL.getType())) { |
| 165 | - totalCritical += qty; | |
| 160 | + totalCritical += quantity; | |
| 166 | 161 | } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MAJOR.getType())) { |
| 167 | - totalMajor += qty; | |
| 162 | + totalMajor += quantity; | |
| 168 | 163 | } else if (Objects.equals(defect.getDefectLevel(), MesQcDefectLevelEnum.MINOR.getType())) { |
| 169 | - totalMinor += qty; | |
| 164 | + totalMinor += quantity; | |
| 165 | + } else { | |
| 166 | + throw exception(QC_IQC_DEFECT_LEVEL_UNKNOWN); | |
| 170 | 167 | } |
| 171 | - // TODO @AI:抛出异常;未知的缺陷等级 | |
| 172 | 168 | } |
| 173 | 169 | // 3.2 计算缺陷率 |
| 174 | 170 | MesQcIqcDO iqc = iqcMapper.selectById(iqcId); |
| ... | ... | @@ -191,4 +187,14 @@ public class MesQcIqcDefectServiceImpl implements MesQcIqcDefectService { |
| 191 | 187 | iqcMapper.updateById(updateIqc); |
| 192 | 188 | } |
| 193 | 189 | |
| 190 | + // TODO @AI:在 iqclineservice 里,提供一个方法,直接根据行 ID 列表,批量更新缺陷统计数量。然后这里直接调用即可,避免循环更新数据库。 | |
| 191 | + /** | |
| 192 | + * 更新检验行的缺陷统计数量 | |
| 193 | + */ | |
| 194 | + private void updateLineDefectStats(Long lineId, int critical, int major, int minor) { | |
| 195 | + MesQcIqcLineDO updateLine = new MesQcIqcLineDO().setId(lineId) | |
| 196 | + .setCriticalQuantity(critical).setMajorQuantity(major).setMinorQuantity(minor); | |
| 197 | + iqcLineMapper.updateById(updateLine); | |
| 198 | + } | |
| 199 | + | |
| 194 | 200 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/lqc/MesQcIqcServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.mes.service.qc.lqc; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.hutool.core.util.ObjUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.exception.ErrorCode; | |
| 4 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | 8 | import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO; |
| ... | ... | @@ -9,17 +11,14 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.qc.lqc.MesQcIqcDO; |
| 9 | 11 | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO; |
| 10 | 12 | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO; |
| 11 | 13 | import cn.iocoder.yudao.module.mes.dal.mysql.qc.lqc.MesQcIqcMapper; |
| 12 | -import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateItemMapper; | |
| 13 | -import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateMapper; | |
| 14 | 14 | import cn.iocoder.yudao.module.mes.enums.qc.MesQcIqcStatusEnum; |
| 15 | 15 | import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; |
| 16 | +import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService; | |
| 16 | 17 | import jakarta.annotation.Resource; |
| 17 | 18 | import org.springframework.stereotype.Service; |
| 18 | 19 | import org.springframework.transaction.annotation.Transactional; |
| 19 | 20 | import org.springframework.validation.annotation.Validated; |
| 20 | 21 | |
| 21 | -import java.util.Objects; | |
| 22 | - | |
| 23 | 22 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 24 | 23 | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| 25 | 24 | |
| ... | ... | @@ -35,9 +34,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { |
| 35 | 34 | @Resource |
| 36 | 35 | private MesQcIqcMapper iqcMapper; |
| 37 | 36 | @Resource |
| 38 | - private MesQcTemplateMapper templateMapper; | |
| 39 | - @Resource | |
| 40 | - private MesQcTemplateItemMapper templateItemMapper; | |
| 37 | + private MesQcTemplateService templateService; | |
| 41 | 38 | |
| 42 | 39 | @Resource |
| 43 | 40 | private MesQcIqcLineService iqcLineService; |
| ... | ... | @@ -51,16 +48,13 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { |
| 51 | 48 | validateIqcCodeUnique(null, createReqVO.getCode()); |
| 52 | 49 | // 1.2 查找物料关联的 IQC 检测模板 |
| 53 | 50 | Long templateId = createReqVO.getTemplateId(); |
| 54 | - // TODO @AI:直接使用 templateService 的 validateTemplateExists() 方法,后续如果有更多模板相关的校验,也可以放在该方法中 | |
| 55 | - MesQcTemplateDO template = templateMapper.selectById(templateId); | |
| 56 | - // TODO @AI:CollUtil.contains 这样更合适; | |
| 57 | - if (template == null || template.getTypes() == null || !template.getTypes().contains(MesQcTypeEnum.IQC.getType())) { | |
| 51 | + MesQcTemplateDO template = templateService.validateTemplateExists(templateId); | |
| 52 | + if (!CollUtil.contains(template.getTypes(), MesQcTypeEnum.IQC.getType())) { | |
| 58 | 53 | throw exception(QC_IQC_NO_TEMPLATE); |
| 59 | 54 | } |
| 60 | 55 | |
| 61 | 56 | // 3.1 从模板的产品关联中获取检测参数(min_check_quantity、max_unqualified_quantity) |
| 62 | - // TODO @AI:使用 templateItemService 方法,不要直接使用对方的 mapper; | |
| 63 | - MesQcTemplateItemDO templateItem = templateItemMapper.selectByTemplateIdAndItemId( | |
| 57 | + MesQcTemplateItemDO templateItem = templateService.getTemplateItemByTemplateIdAndItemId( | |
| 64 | 58 | templateId, createReqVO.getItemId()); |
| 65 | 59 | if (templateItem != null) { |
| 66 | 60 | if (createReqVO.getMinCheckQuantity() == null) { |
| ... | ... | @@ -85,11 +79,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { |
| 85 | 79 | // 1.1 校验存在 |
| 86 | 80 | MesQcIqcDO iqc = validateIqcExists(updateReqVO.getId()); |
| 87 | 81 | // 1.2 校验状态为草稿 |
| 88 | - // TODO @AI:notEquals; | |
| 89 | - // TODO @AI:抽成一个方法,这样 completeIqc 和 deleteIqc 也可以复用,避免重复代码; | |
| 90 | - if (!Objects.equals(iqc.getStatus(), MesQcIqcStatusEnum.PREPARE.getType())) { | |
| 91 | - throw exception(QC_IQC_ONLY_PREPARE_CAN_COMPLETE); | |
| 92 | - } | |
| 82 | + validatePrepareStatus(iqc, QC_IQC_ONLY_PREPARE_CAN_COMPLETE); | |
| 93 | 83 | // 1.3 校验编号唯一 |
| 94 | 84 | validateIqcCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); |
| 95 | 85 | |
| ... | ... | @@ -103,9 +93,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { |
| 103 | 93 | // 1.1 校验存在 |
| 104 | 94 | MesQcIqcDO iqc = validateIqcExists(id); |
| 105 | 95 | // 1.2 校验状态为草稿 |
| 106 | - if (!Objects.equals(iqc.getStatus(), MesQcIqcStatusEnum.PREPARE.getType())) { | |
| 107 | - throw exception(QC_IQC_ONLY_PREPARE_CAN_COMPLETE); | |
| 108 | - } | |
| 96 | + validatePrepareStatus(iqc, QC_IQC_ONLY_PREPARE_CAN_COMPLETE); | |
| 109 | 97 | // 1.3 校验合格品 + 不合格品 = 检测数量 |
| 110 | 98 | if (iqc.getCheckQuantity() != null && iqc.getCheckQuantity() > 0) { |
| 111 | 99 | int total = (iqc.getQualifiedQuantity() != null ? iqc.getQualifiedQuantity() : 0) |
| ... | ... | @@ -129,9 +117,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { |
| 129 | 117 | // 1.1 校验存在 |
| 130 | 118 | MesQcIqcDO iqc = validateIqcExists(id); |
| 131 | 119 | // 1.2 仅草稿可删 |
| 132 | - if (!Objects.equals(iqc.getStatus(), MesQcIqcStatusEnum.PREPARE.getType())) { | |
| 133 | - throw exception(QC_IQC_ONLY_PREPARE_CAN_DELETE); | |
| 134 | - } | |
| 120 | + validatePrepareStatus(iqc, QC_IQC_ONLY_PREPARE_CAN_DELETE); | |
| 135 | 121 | |
| 136 | 122 | // 2.1 删除主表 |
| 137 | 123 | iqcMapper.deleteById(id); |
| ... | ... | @@ -149,6 +135,13 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { |
| 149 | 135 | return iqc; |
| 150 | 136 | } |
| 151 | 137 | |
| 138 | + // TODO @AI:就搞个方法,传递 id 进去;抛出不为 草稿状态的异常;然后在 update/delete/complete 方法中调用这个方法(不用专门 errorCode) | |
| 139 | + private void validatePrepareStatus(MesQcIqcDO iqc, ErrorCode errorCode) { | |
| 140 | + if (ObjUtil.notEqual(iqc.getStatus(), MesQcIqcStatusEnum.PREPARE.getType())) { | |
| 141 | + throw exception(errorCode); | |
| 142 | + } | |
| 143 | + } | |
| 144 | + | |
| 152 | 145 | private void validateIqcCodeUnique(Long id, String code) { |
| 153 | 146 | MesQcIqcDO iqc = iqcMapper.selectByCode(code); |
| 154 | 147 | if (iqc == null) { | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateService.java
| ... | ... | @@ -90,6 +90,23 @@ public interface MesQcTemplateService { |
| 90 | 90 | return convertMap(getTemplateList(ids), MesQcTemplateDO::getId); |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | + /** | |
| 94 | + * 校验质检方案存在 | |
| 95 | + * | |
| 96 | + * @param id 编号 | |
| 97 | + * @return 质检方案 | |
| 98 | + */ | |
| 99 | + MesQcTemplateDO validateTemplateExists(Long id); | |
| 100 | + | |
| 101 | + /** | |
| 102 | + * 根据方案 ID + 产品 ID 获取产品关联 | |
| 103 | + * | |
| 104 | + * @param templateId 方案 ID | |
| 105 | + * @param itemId 产品 ID | |
| 106 | + * @return 产品关联(可能为 null) | |
| 107 | + */ | |
| 108 | + MesQcTemplateItemDO getTemplateItemByTemplateIdAndItemId(Long templateId, Long itemId); | |
| 109 | + | |
| 93 | 110 | // ========== 质检方案-检测指标项 ========== |
| 94 | 111 | |
| 95 | 112 | /** | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateServiceImpl.java
| ... | ... | @@ -84,10 +84,13 @@ public class MesQcTemplateServiceImpl implements MesQcTemplateService { |
| 84 | 84 | templateItemMapper.deleteByTemplateId(id); |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | - private void validateTemplateExists(Long id) { | |
| 88 | - if (templateMapper.selectById(id) == null) { | |
| 87 | + @Override | |
| 88 | + public MesQcTemplateDO validateTemplateExists(Long id) { | |
| 89 | + MesQcTemplateDO template = templateMapper.selectById(id); | |
| 90 | + if (template == null) { | |
| 89 | 91 | throw exception(QC_TEMPLATE_NOT_EXISTS); |
| 90 | 92 | } |
| 93 | + return template; | |
| 91 | 94 | } |
| 92 | 95 | |
| 93 | 96 | private void validateTemplateCodeUnique(Long id, String code) { |
| ... | ... | @@ -101,6 +104,11 @@ public class MesQcTemplateServiceImpl implements MesQcTemplateService { |
| 101 | 104 | } |
| 102 | 105 | |
| 103 | 106 | @Override |
| 107 | + public MesQcTemplateItemDO getTemplateItemByTemplateIdAndItemId(Long templateId, Long itemId) { | |
| 108 | + return templateItemMapper.selectByTemplateIdAndItemId(templateId, itemId); | |
| 109 | + } | |
| 110 | + | |
| 111 | + @Override | |
| 104 | 112 | public MesQcTemplateDO getTemplate(Long id) { |
| 105 | 113 | return templateMapper.selectById(id); |
| 106 | 114 | } | ... | ... |