5 小时以前 9bad721754fe8bbe2e5f459d0706e0fefac569f3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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);
    }
 
}