From 9bad721754fe8bbe2e5f459d0706e0fefac569f3 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期日, 20 九月 2026 09:05:41 +0800
Subject: [PATCH] feat(mes): 添加智能质检报告功能与AI导入能力

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicator/MesQcIndicatorServiceImpl.java |  118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 115 insertions(+), 3 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicator/MesQcIndicatorServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicator/MesQcIndicatorServiceImpl.java
index c3c6819..562b5bf 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicator/MesQcIndicatorServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicator/MesQcIndicatorServiceImpl.java
@@ -3,6 +3,7 @@
 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;
@@ -10,6 +11,7 @@
 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;
@@ -19,15 +21,17 @@
 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 鑺嬮亾婧愮爜
+ * @author 瓒呯骇绠$悊鍛�
  */
 @Service
 @Validated
@@ -43,6 +47,7 @@
 
         // 鎻掑叆
         MesQcIndicatorDO indicator = BeanUtils.toBean(createReqVO, MesQcIndicatorDO.class);
+        normalizeHierarchy(indicator);
         indicatorMapper.insert(indicator);
         return indicator.getId();
     }
@@ -56,6 +61,7 @@
 
         // 鏇存柊
         MesQcIndicatorDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIndicatorDO.class);
+        normalizeHierarchy(updateObj);
         indicatorMapper.updateById(updateObj);
     }
 
@@ -63,6 +69,11 @@
     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);
     }
@@ -72,8 +83,100 @@
         validateIndicatorCodeUnique(id, saveReqVO.getCode());
         // 鏍¢獙鍚嶇О鍞竴
         validateIndicatorNameUnique(id, saveReqVO.getName());
+        // 鏍¢獙灞傜骇褰掑睘锛堝彧鍏佽涓ゅ眰锛�
+        validateHierarchy(id, saveReqVO);
         // 鏍¢獙缁撴灉鍊煎睘鎬�
-        validateResultSpecification(saveReqVO.getResultType(), saveReqVO.getResultSpecification());
+        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) {
@@ -102,7 +205,11 @@
         }
     }
 
-    private void validateResultSpecification(Integer resultType, String resultSpecification) {
+    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)) {
@@ -127,6 +234,11 @@
     }
 
     @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();

--
Gitblit v1.9.3