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<MesQcIndicatorDO> 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());
|
}
|
|
/**
|
* 校验层级归属。只允许两层:分组项必须顶级,录入项可挂到分组项下。
|
* <p>
|
* 「父必须是分组项」这条本身就掐死了第三层,所以不再单独写一条「父必须顶级」的校验。
|
* 按业务优先级只抛第一处不满足,避免一次抛多条让用户抓不住重点。
|
*/
|
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<MesQcIndicatorDO> getIndicatorPage(MesQcIndicatorPageReqVO pageReqVO) {
|
return indicatorMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
public List<MesQcIndicatorDO> getIndicatorList() {
|
return indicatorMapper.selectList();
|
}
|
|
@Override
|
public List<MesQcIndicatorDO> getIndicatorGroupList() {
|
return indicatorMapper.selectListByItemType(MesQcIndicatorItemTypeEnum.GROUP.getType());
|
}
|
|
@Override
|
public List<MesQcIndicatorDO> getIndicatorList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyList();
|
}
|
return indicatorMapper.selectByIds(ids);
|
}
|
|
@Override
|
public Map<Long, MesQcIndicatorDO> validateIndicatorListExists(Collection<Long> ids) {
|
List<MesQcIndicatorDO> indicators = getIndicatorList(ids);
|
if (indicators.size() != ids.size()) {
|
throw exception(QC_INDICATOR_NOT_EXISTS);
|
}
|
return convertMap(indicators, MesQcIndicatorDO::getId);
|
}
|
|
}
|