package cn.iocoder.yudao.module.mes.service.qc.indicator; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.module.mes.controller.admin.qc.indicator.vo.MesQcIndicatorPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.indicator.vo.MesQcIndicatorSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicator.MesQcIndicatorMapper; import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum; import cn.iocoder.yudao.module.mes.enums.qc.MesQcResultValueTypeEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 质检指标 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesQcIndicatorServiceImpl implements MesQcIndicatorService { @Resource private MesQcIndicatorMapper indicatorMapper; @Override public Long createIndicator(MesQcIndicatorSaveReqVO createReqVO) { // 校验相关参数 validateIndicatorSaveData(null, createReqVO); // 插入 MesQcIndicatorDO indicator = BeanUtils.toBean(createReqVO, MesQcIndicatorDO.class); normalizeHierarchy(indicator); indicatorMapper.insert(indicator); return indicator.getId(); } @Override public void updateIndicator(MesQcIndicatorSaveReqVO updateReqVO) { // 校验存在 validateIndicatorExists(updateReqVO.getId()); // 校验相关参数 validateIndicatorSaveData(updateReqVO.getId(), updateReqVO); // 更新 MesQcIndicatorDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIndicatorDO.class); normalizeHierarchy(updateObj); indicatorMapper.updateById(updateObj); } @Override public void deleteIndicator(Long id) { // 校验存在 validateIndicatorExists(id); // 级联删除子项:指标只支持两层,挂在它下面的都是它的明细项,父删了子无处可挂 List children = indicatorMapper.selectListByParentId(id); if (CollUtil.isNotEmpty(children)) { indicatorMapper.deleteByIds(convertList(children, MesQcIndicatorDO::getId)); } // 删除 indicatorMapper.deleteById(id); } private void validateIndicatorSaveData(Long id, MesQcIndicatorSaveReqVO saveReqVO) { // 校验编码唯一 validateIndicatorCodeUnique(id, saveReqVO.getCode()); // 校验名称唯一 validateIndicatorNameUnique(id, saveReqVO.getName()); // 校验层级归属(只允许两层) validateHierarchy(id, saveReqVO); // 校验结果值属性 validateResultSpecification(saveReqVO.getItemType(), saveReqVO.getResultType(), saveReqVO.getResultSpecification()); } /** * 校验层级归属。只允许两层:分组项必须顶级,录入项可挂到分组项下。 *

* 「父必须是分组项」这条本身就掐死了第三层,所以不再单独写一条「父必须顶级」的校验。 * 按业务优先级只抛第一处不满足,避免一次抛多条让用户抓不住重点。 */ private void validateHierarchy(Long id, MesQcIndicatorSaveReqVO reqVO) { Long parentId = ObjectUtils.defaultIfNull(reqVO.getParentId(), MesQcIndicatorDO.PARENT_ID_ROOT); Integer itemType = ObjectUtils.defaultIfNull(reqVO.getItemType(), MesQcIndicatorItemTypeEnum.ENTRY.getType()); // 1. 分组项只能顶级 if (MesQcIndicatorItemTypeEnum.isGroup(itemType) && !MesQcIndicatorDO.PARENT_ID_ROOT.equals(parentId)) { throw new ServiceException(QC_INDICATOR_GROUP_CANNOT_NEST.getCode(), String.format("分组项[%s]必须作为顶级指标,不能挂到其它指标下(当前选择了父指标[%s])。" + "如需调整层级,请把「条目类型」改为录入项后再选择所属分组。", defaultStr(reqVO.getName()), nameOf(parentId))); } // 2. 父指标必须存在且是分组项 if (!MesQcIndicatorDO.PARENT_ID_ROOT.equals(parentId)) { if (Objects.equals(id, parentId)) { throw exception(QC_INDICATOR_PARENT_SELF); } MesQcIndicatorDO parent = indicatorMapper.selectById(parentId); if (parent == null) { throw new ServiceException(QC_INDICATOR_PARENT_NOT_EXISTS.getCode(), String.format("所属分组不存在(指标编号 %d 查无此指标,可能已被删除)。请重新选择所属分组,或清空该项作为顶级指标。", parentId)); } if (!MesQcIndicatorItemTypeEnum.isGroup(parent.getItemType())) { throw new ServiceException(QC_INDICATOR_PARENT_NOT_GROUP.getCode(), String.format("所选父指标[%s]不是分组项(当前条目类型为[%s]),不能作为所属分组。" + "请改选一个分组项,或先在指标列表把[%s]的条目类型改为分组项。", defaultStr(parent.getName()), itemTypeNameOf(parent.getItemType()), defaultStr(parent.getName()))); } } // 3. 录入项必须有结果值类型(分组项没有结果值,现网分组行 result_type 就是 NULL) if (!MesQcIndicatorItemTypeEnum.isGroup(itemType) && reqVO.getResultType() == null) { throw exception(QC_INDICATOR_RESULT_TYPE_REQUIRED); } // 4. 自己已经有子项时,不能再被别人收编、也不能反过来变成子项 if (id == null) { return; } Long childCount = indicatorMapper.selectCountByParentId(id); if (childCount <= 0) { return; } if (!MesQcIndicatorDO.PARENT_ID_ROOT.equals(parentId)) { throw new ServiceException(QC_INDICATOR_HAS_CHILDREN.getCode(), String.format("指标[%s]下已挂有 %d 个录入项,不能再挂到其它分组下(指标只支持两层)。" + "请先删除或移走这 %d 个子项,再修改它的所属分组。", defaultStr(reqVO.getName()), childCount, childCount)); } if (!MesQcIndicatorItemTypeEnum.isGroup(itemType)) { throw new ServiceException(QC_INDICATOR_HAS_CHILDREN.getCode(), String.format("指标[%s]下已挂有 %d 个录入项,不能再把它的条目类型改为录入项,否则这些子项会失去所属分组。" + "请先删除或移走这 %d 个子项,再修改条目类型。", defaultStr(reqVO.getName()), childCount, childCount)); } } /** * 落库前补齐层级字段:分组项清空结果值(现网分组行没有结果值),排序号留空则排到同级末尾 */ private void normalizeHierarchy(MesQcIndicatorDO indicator) { indicator.setParentId(ObjectUtils.defaultIfNull(indicator.getParentId(), MesQcIndicatorDO.PARENT_ID_ROOT)); indicator.setItemType(ObjectUtils.defaultIfNull(indicator.getItemType(), MesQcIndicatorItemTypeEnum.ENTRY.getType())); if (MesQcIndicatorItemTypeEnum.isGroup(indicator.getItemType())) { indicator.setResultType(null); indicator.setResultSpecification(null); } if (indicator.getSortOrder() == null) { Integer max = indicatorMapper.selectMaxSortOrder(indicator.getParentId()); indicator.setSortOrder((max == null ? 0 : max) + (MesQcIndicatorDO.PARENT_ID_ROOT.equals(indicator.getParentId()) ? 10 : 1)); } } private String nameOf(Long id) { MesQcIndicatorDO indicator = id == null ? null : indicatorMapper.selectById(id); return indicator == null ? String.valueOf(id) : defaultStr(indicator.getName()); } private static String itemTypeNameOf(Integer itemType) { return MesQcIndicatorItemTypeEnum.isGroup(itemType) ? MesQcIndicatorItemTypeEnum.GROUP.getName() : MesQcIndicatorItemTypeEnum.ENTRY.getName(); } private static String defaultStr(String value) { return StrUtil.blankToDefault(value, ""); } private void validateIndicatorExists(Long id) { if (indicatorMapper.selectById(id) == null) { throw exception(QC_INDICATOR_NOT_EXISTS); } } private void validateIndicatorCodeUnique(Long id, String code) { MesQcIndicatorDO indicator = indicatorMapper.selectByCode(code); if (indicator == null) { return; } if (ObjUtil.notEqual(indicator.getId(), id)) { throw exception(QC_INDICATOR_CODE_DUPLICATE); } } private void validateIndicatorNameUnique(Long id, String name) { MesQcIndicatorDO indicator = indicatorMapper.selectByName(name); if (indicator == null) { return; } if (ObjUtil.notEqual(indicator.getId(), id)) { throw exception(QC_INDICATOR_NAME_DUPLICATE); } } private void validateResultSpecification(Integer itemType, Integer resultType, String resultSpecification) { // 分组项没有结果值,落库前会被清空,这里不校验 if (MesQcIndicatorItemTypeEnum.isGroup(ObjectUtils.defaultIfNull(itemType, MesQcIndicatorItemTypeEnum.ENTRY.getType()))) { return; } if (ObjectUtils.equalsAny(resultType, MesQcResultValueTypeEnum.FILE.getType(), MesQcResultValueTypeEnum.DICT.getType())) { if (StrUtil.isBlank(resultSpecification)) { throw exception(QC_INDICATOR_RESULT_SPECIFICATION_REQUIRED); } } } @Override public MesQcIndicatorDO getIndicator(Long id) { return indicatorMapper.selectById(id); } @Override public PageResult getIndicatorPage(MesQcIndicatorPageReqVO pageReqVO) { return indicatorMapper.selectPage(pageReqVO); } @Override public List getIndicatorList() { return indicatorMapper.selectList(); } @Override public List getIndicatorGroupList() { return indicatorMapper.selectListByItemType(MesQcIndicatorItemTypeEnum.GROUP.getType()); } @Override public List getIndicatorList(Collection ids) { if (CollUtil.isEmpty(ids)) { return Collections.emptyList(); } return indicatorMapper.selectByIds(ids); } @Override public Map validateIndicatorListExists(Collection ids) { List indicators = getIndicatorList(ids); if (indicators.size() != ids.size()) { throw exception(QC_INDICATOR_NOT_EXISTS); } return convertMap(indicators, MesQcIndicatorDO::getId); } }