Parcourir la source

Merge branch 'test-merge' of http://219.151.181.73:3000/zhuwei/bladex into test-merge

lvy il y a 1 mois
Parent
commit
9502aa9a1f
32 fichiers modifiés avec 1120 ajouts et 109 suppressions
  1. 37 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/FormulaReferenceDTO.java
  2. 3 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/TrialSummaryReflectionSaveDTO.java
  3. 5 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreeContractExtend.java
  4. 3 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ArchiveTreeContractClient.java
  5. 10 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/TrialTableDataInfo.java
  6. 9 0
      blade-service/blade-business/src/main/java/org/springblade/business/controller/ArchiveFileController.java
  7. 2 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.java
  8. 3 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml
  9. 2 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/IArchiveFileService.java
  10. 39 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ArchiveFileServiceImpl.java
  11. 20 0
      blade-service/blade-manager/pom.xml
  12. 43 1
      blade-service/blade-manager/src/main/java/com/mixsmart/utils/CustomFunction.java
  13. 9 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java
  14. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/FormulaController.java
  15. 61 42
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TrialSummaryClassificationConfigurationController.java
  16. 563 20
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java
  17. 3 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreePrivateController.java
  18. 5 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ArchiveTreeContractImpl.java
  19. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.java
  20. 13 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.xml
  21. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.java
  22. 8 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.xml
  23. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IArchiveTreeContractService.java
  24. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IFormulaService.java
  25. 5 4
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IWbsTreeContractService.java
  26. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IWbsTreePrivateService.java
  27. 5 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractServiceImpl.java
  28. 27 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ExcelTabServiceImpl.java
  29. 92 17
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java
  30. 95 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java
  31. 44 13
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreePrivateServiceImpl.java
  32. 5 1
      pom.xml

+ 37 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/FormulaReferenceDTO.java

@@ -0,0 +1,37 @@
+package org.springblade.manager.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 公式应用设置
+ * @author LHB
+ */
+@Data
+public class FormulaReferenceDTO {
+    @ApiModelProperty("表单pKeyId")
+    private Long pKeyId;
+
+    @ApiModelProperty(value = "节点pKeyId",required = true)
+    @NotNull(message = "节点pKeyId不能为空")
+    private Long pId;
+
+    @ApiModelProperty(value = "选择表格的元素key",required = true)
+    @NotBlank(message = "选择表格的元素key不能为空")
+    private String key;
+
+    @ApiModelProperty(value = "表单元素表名称",required = true)
+    @NotBlank(message = "表单元素表名称不能为空")
+    private String initTableName;
+
+    @ApiModelProperty(value = "状态 0禁用 1启用",required = true)
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+        @ApiModelProperty(value = "类型 1-质检 2-试验",required = true)
+    @NotNull(message = "类型")
+    private Integer type;
+}

+ 3 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/TrialSummaryReflectionSaveDTO.java

@@ -18,6 +18,9 @@ public class TrialSummaryReflectionSaveDTO implements Serializable {
 
     @Data
     public static class ReflectionBean implements Serializable {
+        @ApiModelProperty(value = "试验表的p_key_id")
+        private Long id;
+
         @ApiModelProperty(value = "试验表的p_key_id")
         private Long trialTabId;
 

+ 5 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreeContractExtend.java

@@ -64,4 +64,9 @@ public class WbsTreeContractExtend {
      *  修改人
      */
     private Long updateUser;
+
+    /**
+     * 哪些元素执行公式
+     */
+    private String formulaConfig;
 }

+ 3 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ArchiveTreeContractClient.java

@@ -90,4 +90,7 @@ public interface ArchiveTreeContractClient {
 
     @PostMapping(API_PREFIX + "/getAuthCode")
     public String getAuthCode(@RequestParam Long contractId);
+
+    @PostMapping(API_PREFIX + "/getArchiveTreeContractListByListOrderByTreeSort")
+    List<ArchiveTreeContract> getArchiveTreeContractListByListOrderByTreeSort(@RequestBody List<Long> nodeIds);
 }

+ 10 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/TrialTableDataInfo.java

@@ -46,6 +46,16 @@ public class TrialTableDataInfo implements java.io.Serializable {
         return UNKNOWN;
     }
 
+    public static Long getElementId(String key) {
+        TrialTableElementEnum[] values = TrialTableElementEnum.values();
+        for (TrialTableElementEnum value : values) {
+            if (value.key.equals(key)) {
+                return value.id;
+            }
+        }
+        return 0L;
+    }
+
     @Getter
     public static enum TrialTableElementEnum {
         RECORD_NO(203700000001001L, "key_301","recordNo", "记录编号"),

+ 9 - 0
blade-service/blade-business/src/main/java/org/springblade/business/controller/ArchiveFileController.java

@@ -42,6 +42,7 @@ import org.springblade.manager.vo.ArchiveTreeVO2;
 import org.springblade.system.entity.Role;
 import org.springblade.system.feign.ISysClient;
 import org.springframework.beans.BeanUtils;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.RequestParam;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -532,4 +533,12 @@ public class ArchiveFileController extends BladeController {
     }
 
 
+    //@Scheduled(cron = "0 04 10 * * ?")
+    @GetMapping("/flushArchiveFileSort")
+    public void flushArchiveFileSort(){
+        Long projectId=1935614356128206849L;
+        archiveFileService.flushArchiveFileSort(projectId);
+    }
+
+
 }

+ 2 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.java

@@ -124,4 +124,6 @@ public interface ArchiveFileMapper extends BaseMapper<ArchiveFile> {
     List<ArchiveFile> getAllArchiveFileByIds(@Param("strList") List<String> strList);
 
     Integer selectMaxSortByContractId(@Param("contractId") Long contractId);
+
+    List<ArchiveFile> selectArchiveFileByProjectId(@Param("projectId") Long projectId);
 }

+ 3 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml

@@ -575,4 +575,7 @@
     <select id="selectMaxSortByContractId" resultType="java.lang.Integer">
         select max(sort) from u_archive_file where contract_id = #{contractId}
     </select>
+    <select id="selectArchiveFileByProjectId" resultType="org.springblade.business.entity.ArchiveFile">
+        select id,node_id,sort from u_archive_file where project_id = #{projectId} and is_deleted = 0
+    </select>
 </mapper>

+ 2 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/IArchiveFileService.java

@@ -79,4 +79,6 @@ public interface IArchiveFileService extends BaseService<ArchiveFile> {
     boolean sortByFileTime(Long nodeId);
 
     Integer selectMaxSortByContractId(Long contractId);
+
+    void flushArchiveFileSort(Long projectId);
 }

+ 39 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ArchiveFileServiceImpl.java

@@ -2,6 +2,7 @@ package org.springblade.business.service.impl;
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
@@ -17,6 +18,8 @@ import org.springblade.core.mp.base.BaseServiceImpl;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
 import org.springblade.core.tool.utils.Func;
+import org.springblade.manager.entity.ArchiveTreeContract;
+import org.springblade.manager.feign.ArchiveTreeContractClient;
 import org.springblade.resource.feign.NewIOSSClient;
 import org.springblade.system.entity.DictBiz;
 import org.springblade.system.feign.IDictBizClient;
@@ -48,6 +51,7 @@ public class ArchiveFileServiceImpl extends BaseServiceImpl<ArchiveFileMapper, A
 
     private final ArchiveInspectionInfoClient archiveInspectionInfoClient;
     private final JdbcTemplate jdbcTemplate;
+    private final ArchiveTreeContractClient archiveTreeContractClient;
 
 
     @Override
@@ -371,4 +375,39 @@ public class ArchiveFileServiceImpl extends BaseServiceImpl<ArchiveFileMapper, A
     public Integer selectMaxSortByContractId(Long contractId) {
         return baseMapper.selectMaxSortByContractId(contractId);
     }
+
+    @Override
+    public void flushArchiveFileSort(Long projectId) {
+        List<ArchiveFile>archileFileList=baseMapper.selectArchiveFileByProjectId(projectId);
+        Map<String, List<ArchiveFile>> archiveMap = archileFileList.stream().collect(Collectors.groupingBy(ArchiveFile::getNodeId));
+        List<Long> NodeIds = archileFileList.stream()
+                .map(ArchiveFile::getNodeId)
+                .map(Long::parseLong)
+                .distinct()
+                .collect(Collectors.toList());
+        List<ArchiveTreeContract> sortIds = archiveTreeContractClient.getArchiveTreeContractListByListOrderByTreeSort(NodeIds);
+        int sort=10100001;
+        for (ArchiveTreeContract nodeId : sortIds) {
+            if(archiveMap.containsKey(nodeId.getId().toString())){
+                List<ArchiveFile> archiveFiles = archiveMap.get(nodeId.getId().toString());
+                if (archiveFiles != null) {
+                    archiveFiles = archiveFiles.stream()
+                            .sorted(Comparator.comparing(ArchiveFile::getSort, Comparator.nullsLast(Integer::compareTo)))
+                            .collect(Collectors.toList());
+                    for (ArchiveFile archiveFile : archiveFiles) {
+                        archiveFile.setSort(sort++);
+                    }
+                }
+            }
+        }
+        int i=1;
+        for (ArchiveFile file : archileFileList) {
+            this.update(Wrappers.<ArchiveFile>lambdaUpdate()
+                    .set(ArchiveFile::getSort, file.getSort())
+                    .eq(ArchiveFile::getId, file.getId()));
+            System.out.println("归档文件排序进度:" + i + "/" + archileFileList.size());
+            i++;
+        }
+        System.out.println("归档文件排序成功");
+    }
 }

+ 20 - 0
blade-service/blade-manager/pom.xml

@@ -214,6 +214,26 @@
             <version>2.10.3</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>e-iceblue</groupId>
+            <artifactId>spire.xls</artifactId>
+            <version>15.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>xalan</groupId>
+            <artifactId>xalan</artifactId>
+            <version>2.7.2</version>
+        </dependency>
+        <dependency>
+            <groupId>xerces</groupId>
+            <artifactId>xercesImpl</artifactId>
+            <version>2.12.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.16.1</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 43 - 1
blade-service/blade-manager/src/main/java/com/mixsmart/utils/CustomFunction.java

@@ -469,7 +469,7 @@ public class CustomFunction {
             return result;
     }
 
-
+    // 获取天气
     public static Object weather( Object dateObj,Map<String,Object> map){
            if(dateObj!=null&&map!=null){
                List<Object> date=obj2ListObj(dateObj);
@@ -478,8 +478,50 @@ public class CustomFunction {
            return "";
     }
 
+    //天气 - 气温
+    public static Object weathertem( Object dateObj,Map<String,Object> map){
+        if(dateObj!=null&&map!=null){
+            List<Object> date=obj2ListObj(dateObj);
+            return  date.stream().map(StringUtils::handleNull).filter(StringUtils::isNotEmpty).map(e->map.get(new DateTime(FormulaUtils.range2end(e)).toString(DatePattern.NORM_DATE_PATTERN))).collect(Collectors.toList());
+        }
+        return "";
+    }
 
+    // 获取最大温度
+    public static Object maxtembydate( Object dateObj,Map<String,Object> map){
+        if(dateObj!=null&&map!=null){
+            List<Object> date=obj2ListObj(dateObj);
+            return  date.stream().map(StringUtils::handleNull).filter(StringUtils::isNotEmpty).map(e->map.get(new DateTime(FormulaUtils.range2end(e)).toString(DatePattern.NORM_DATE_PATTERN))).collect(Collectors.toList());
+        }
+        return "";
+    }
 
+    // 获取最小温度
+    public static Object mintembydate( Object dateObj,Map<String,Object> map){
+        if(dateObj!=null&&map!=null){
+            List<Object> date=obj2ListObj(dateObj);
+            return  date.stream().map(StringUtils::handleNull).filter(StringUtils::isNotEmpty).map(e->map.get(new DateTime(FormulaUtils.range2end(e)).toString(DatePattern.NORM_DATE_PATTERN))).collect(Collectors.toList());
+        }
+        return "";
+    }
+
+    // 获取星期几
+    public static Object weekbydate( Object dateObj,Map<String,Object> map){
+        if(dateObj!=null&&map!=null){
+            List<Object> date=obj2ListObj(dateObj);
+            return  date.stream().map(StringUtils::handleNull).filter(StringUtils::isNotEmpty).map(e->map.get(new DateTime(FormulaUtils.range2end(e)).toString(DatePattern.NORM_DATE_PATTERN))).collect(Collectors.toList());
+        }
+        return "";
+    }
+
+    // 获取最大温度
+    public static Object maxminbydate( Object dateObj,Map<String,Object> map){
+        if(dateObj!=null&&map!=null){
+            List<Object> date=obj2ListObj(dateObj);
+            return  date.stream().map(StringUtils::handleNull).filter(StringUtils::isNotEmpty).map(e->map.get(new DateTime(FormulaUtils.range2end(e)).toString(DatePattern.NORM_DATE_PATTERN))).collect(Collectors.toList());
+        }
+        return "";
+    }
 
     public static List<Object> obj2ListObj(Object input) {
         if (input == null) {

+ 9 - 2
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java

@@ -4309,12 +4309,19 @@ public class ExcelTabController extends BladeController {
     @PostMapping("/add-buss-imginfo")
     @ApiOperationSupport(order = 32)
     @ApiOperation(value = "表单填写图片上传", notes = "表单填写图片上传")
-    public R addBussFile(@RequestParam MultipartFile file) {
-
+    public R addBussFile(@RequestParam MultipartFile file, String oldUrl) {
         BladeFile bladeFile = this.newIOSSClient.uploadFileByInputStream(file);
+        if(bladeFile!=null&&StringUtils.isNotEmpty(oldUrl)){
+            String fileName=oldUrl.substring(oldUrl.lastIndexOf("/")+1);
+            newIOSSClient.removeFile(fileName);
+        }
         return R.data(bladeFile);
     }
 
+    private String isExistOssUrl(Long pkeyId,String key) {
+        return wbsTreeContractService.isExistOssUrl(pkeyId,key);
+    }
+
     /**
      * 项目元素 关联表
      */

+ 1 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/FormulaController.java

@@ -585,7 +585,7 @@ public class FormulaController {
             ContractInfo contract = this.contractInfoService.getById(fo.getContractId());
             WbsTreeContract parent = this.wbsTreeContractService.getOne(Wrappers.<WbsTreeContract>lambdaQuery().eq(WbsTreeContract::getId,fo.getParentId()).eq(WbsTreeContract::getContractId,fo.getContractId()).last("limit 1"));
 
-            List<KeyMapper> kms = this.service.getKeyMapperList(Collections.singletonList(fo.getPkeyId()), contract.getPId(), parent.getPKeyId().toString(), ExecuteType.INSPECTION);
+            List<KeyMapper> kms = this.service.getKeyMapperList(Collections.singletonList(fo.getPkeyId()), contract.getPId(), parent.getPKeyId().toString(), ExecuteType.INSPECTION, null);
             KeyMapper keyMapper = null;
             if (Func.isNotEmpty(kms)) {
                 Optional<KeyMapper> optionalKeyMapper = kms.stream().filter(e -> StringUtils.isEquals(e.getField(), key)).findFirst();

+ 61 - 42
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TrialSummaryClassificationConfigurationController.java

@@ -42,6 +42,7 @@ import java.io.*;
 import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 
 @RestController
@@ -211,29 +212,37 @@ public class TrialSummaryClassificationConfigurationController extends BladeCont
     @RequestMapping(value = "/input/detail", method = RequestMethod.GET)
     public R<Object> inputDetail(@RequestParam String classId, @RequestParam String keyName) {
         TrialSummaryClassificationConfiguration classificationConfiguration = iTrialSummaryClassificationConfigurationService.getById(classId);
+        List<Map<String, String>> r=new ArrayList<>();
         if (classificationConfiguration != null && ObjectUtil.isNotEmpty(classificationConfiguration.getExcelId())) {
             String sql = "SELECT * FROM m_trial_summary_excel_tab_reflection WHERE excel_id = ? AND html_key_name = ?";
-            TrialSummaryExcelTabReflection obj = jdbcTemplate.query(sql, new Object[]{classificationConfiguration.getExcelId(), keyName}, new BeanPropertyRowMapper<>(TrialSummaryExcelTabReflection.class)).stream().findAny().orElse(null);
-            if (obj != null) {
-                if (TrialTableDataInfo.LONG_ID.equals(obj.getTrialTabId())) {
-                    Map<String, String> map = new HashMap<>();
-                    map.put("id", obj.getId().toString());
-                    map.put("tabName", TrialTableDataInfo.TBN_CH);
-                    map.put("elementName", TrialTableDataInfo.getElementName(obj.getElementKey()));
-                    return R.data(map);
-                }
-                WbsTreePrivate trialTab = wbsTreePrivateServiceImpl.getBaseMapper().getByPKeyId(obj.getTrialTabId());
-                WbsFormElement element = wbsFormElementService.getById(obj.getElementId());
-                if (trialTab != null && element != null) {
-                    Map<String, String> map = new HashMap<>();
-                    map.put("id", obj.getId().toString());
-                    map.put("tabName", ObjectUtil.isNotEmpty(trialTab.getFullName()) ? trialTab.getFullName() : trialTab.getNodeName());
-                    map.put("elementName", element.getEName());
-                    return R.data(map);
+            List<TrialSummaryExcelTabReflection> list = jdbcTemplate.query(sql, new Object[]{classificationConfiguration.getExcelId(), keyName}, new BeanPropertyRowMapper<>(TrialSummaryExcelTabReflection.class));
+            if (list != null) {
+                for (TrialSummaryExcelTabReflection obj : list) {
+                    if (TrialTableDataInfo.LONG_ID.equals(obj.getTrialTabId())) {
+                        Map<String, String> map = new HashMap<>();
+                        map.put("id", obj.getId().toString());
+                        map.put("tabName", TrialTableDataInfo.TBN_CH);
+                        map.put("elementName", TrialTableDataInfo.getElementName(obj.getElementKey()));
+                        map.put("trialTabId",TrialTableDataInfo.LONG_ID+"");
+                        map.put("elementId",TrialTableDataInfo.getElementId(obj.getElementKey())+"");
+                        r.add( map);
+                    }else {
+                        WbsTreePrivate trialTab = wbsTreePrivateServiceImpl.getBaseMapper().getByPKeyId(obj.getTrialTabId());
+                        WbsFormElement element = wbsFormElementService.getById(obj.getElementId());
+                        if (trialTab != null && element != null) {
+                            Map<String, String> map = new HashMap<>();
+                            map.put("id", obj.getId().toString());
+                            map.put("tabName", ObjectUtil.isNotEmpty(trialTab.getFullName()) ? trialTab.getFullName() : trialTab.getNodeName());
+                            map.put("elementName", element.getEName());
+                            map.put("trialTabId",obj.getTrialTabId()+"");
+                            map.put("elementId",obj.getElementId()+"");
+                            r.add(map);
+                        }
+                    }
                 }
             }
         }
-        return R.data(null);
+        return R.data(r);
     }
 
     @ApiOperationSupport(order = 13)
@@ -248,37 +257,47 @@ public class TrialSummaryClassificationConfigurationController extends BladeCont
                 if (classificationConfiguration != null && ObjectUtil.isNotEmpty(classificationConfiguration.getExcelId())) {
                     List<TrialSummaryReflectionSaveDTO.ReflectionBean> reflectionBeanList = dto.getReflectionBeanList();
                     Set<TrialSummaryReflectionSaveDTO.ReflectionBean> collect = new HashSet<>(reflectionBeanList);
+                    Set<TrialSummaryReflectionSaveDTO.ReflectionBean> updateList = collect.stream().filter(bean -> ObjectUtil.isNotEmpty(bean.getId())).collect(Collectors.toSet());
+                    Set<TrialSummaryReflectionSaveDTO.ReflectionBean> insertList = collect.stream().filter(bean -> ObjectUtil.isEmpty(bean.getId())).collect(Collectors.toSet());
+
                     List<String> keyNameList = collect.stream()
                             .map(bean -> "'" + bean.getHtmlKeyName() + "'")
                             .collect(Collectors.toList());
                     if (keyNameList.size() <= 0) {
                         throw new ServiceException("入参异常,未获取到htmlKeyName");
                     }
-
-                    String sqlDel = "DELETE FROM m_trial_summary_excel_tab_reflection WHERE excel_id = ? AND class_id = ? AND html_key_name IN (" +
-                            StringUtils.join(keyNameList, ",") + ")";
-                    List<Object> paramsDel = new ArrayList<>();
-                    paramsDel.add(classificationConfiguration.getExcelId());
-                    paramsDel.add(classificationConfiguration.getId());
-                    jdbcTemplate.update(sqlDel, paramsDel.toArray());
-
-                    String sqlInsert = "INSERT INTO m_trial_summary_excel_tab_reflection(id,class_id,excel_id,trial_tab_id,element_id,html_key_name,trial_tab_name,element_key) VALUES (?,?,?,?,?,?,?,?)";
-                    List<Object[]> batchArgs = new ArrayList<>();
-                    for (TrialSummaryReflectionSaveDTO.ReflectionBean reflectionBean : collect) {
-                        Object[] paramsInsert = {
-                                SnowFlakeUtil.getId(),
-                                classificationConfiguration.getId(),
-                                classificationConfiguration.getExcelId(),
-                                reflectionBean.getTrialTabId(),
-                                reflectionBean.getElementId(),
-                                reflectionBean.getHtmlKeyName(),
-                                reflectionBean.getTrialTabName(),
-                                reflectionBean.getElementKey()
-                        };
-                        batchArgs.add(paramsInsert);
+                    // 批量更新已有记录
+                    if (!updateList.isEmpty()) {
+                        String sqlUpdate = "UPDATE m_trial_summary_excel_tab_reflection SET trial_tab_id = ?, element_id = ? WHERE id = ?";
+                        List<Object[]> updateBatchArgs = new ArrayList<>();
+                        for (TrialSummaryReflectionSaveDTO.ReflectionBean reflectionBean : updateList) {
+                            Object[] paramsUpdate = {
+                                    reflectionBean.getTrialTabId(),
+                                    reflectionBean.getElementId(),
+                                    reflectionBean.getId()
+                            };
+                            updateBatchArgs.add(paramsUpdate);
+                        }
+                        jdbcTemplate.batchUpdate(sqlUpdate, updateBatchArgs);
+                    }
+                    if (!insertList.isEmpty()){
+                        String sqlInsert = "INSERT INTO m_trial_summary_excel_tab_reflection(id,class_id,excel_id,trial_tab_id,element_id,html_key_name,trial_tab_name,element_key) VALUES (?,?,?,?,?,?,?,?)";
+                        List<Object[]> batchArgs = new ArrayList<>();
+                        for (TrialSummaryReflectionSaveDTO.ReflectionBean reflectionBean : insertList) {
+                            Object[] paramsInsert = {
+                                    SnowFlakeUtil.getId(),
+                                    classificationConfiguration.getId(),
+                                    classificationConfiguration.getExcelId(),
+                                    reflectionBean.getTrialTabId(),
+                                    reflectionBean.getElementId(),
+                                    reflectionBean.getHtmlKeyName(),
+                                    reflectionBean.getTrialTabName(),
+                                    reflectionBean.getElementKey()
+                            };
+                            batchArgs.add(paramsInsert);
+                        }
+                        jdbcTemplate.batchUpdate(sqlInsert, batchArgs);
                     }
-                    jdbcTemplate.batchUpdate(sqlInsert, batchArgs);
-
                     return R.success("操作成功");
                 }
             }

+ 563 - 20
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java

@@ -1,12 +1,5 @@
 package org.springblade.manager.controller;
-
-import com.alibaba.fastjson.JSONObject;
-import com.aspose.cells.LoadFormat;
-import com.aspose.cells.LoadOptions;
-import com.aspose.cells.SaveFormat;
 import com.aspose.cells.Workbook;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import com.google.common.collect.Lists;
@@ -20,10 +13,8 @@ import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.SneakyThrows;
 import org.apache.commons.lang.StringUtils;
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.ss.util.WorkbookUtil;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
@@ -31,8 +22,6 @@ import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springblade.business.vo.NeiYeLedgerVO1;
-import org.springblade.common.utils.CommonUtil;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.boot.ctrl.BladeController;
 import org.springblade.core.log.exception.ServiceException;
@@ -41,26 +30,21 @@ import org.springblade.core.tool.utils.CollectionUtil;
 import org.springblade.core.tool.utils.IoUtil;
 import org.springblade.core.tool.utils.ObjectUtil;
 import org.springblade.core.tool.utils.ResourceUtil;
+import org.springblade.manager.dto.FormulaReferenceDTO;
 import org.springblade.manager.dto.MoveNodeDTO;
 import org.springblade.manager.dto.TableSortDTO;
 import org.springblade.manager.dto.WbsTreeContractDTO2;
 import org.springblade.manager.entity.*;
 import org.springblade.manager.feign.ContractClient;
-import org.springblade.manager.service.INodeBaseInfoService;
 import org.springblade.manager.service.IWbsParamService;
 import org.springblade.manager.service.IWbsTreeContractService;
-import org.springblade.manager.service.IWbsTreePrivateService;
-import org.springblade.manager.service.impl.NodeBaseInfoServiceImpl;
 import org.springblade.manager.service.impl.WbsTreeContractServiceImpl;
+import org.springblade.manager.util.DataStructureFormatUtils;
 import org.springblade.manager.utils.FileUtils;
 import org.springblade.manager.utils.RandomNumberHolder;
 import org.springblade.manager.vo.*;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.ByteArrayResource;
 import org.springframework.core.io.Resource;
 import org.springframework.dao.DataAccessException;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -83,7 +67,21 @@ import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import org.springframework.http.ContentDisposition;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import static com.aspose.cells.HtmlLinkTargetType.BLANK;
+import static com.aspose.cells.LoadDataFilterOptions.FORMULA;
+import static com.aspose.cells.PropertyType.BOOLEAN;
+import static com.aspose.cells.PropertyType.STRING;
+import static java.sql.Types.NUMERIC;
+import static java.util.stream.Collectors.toMap;
+
 
 @RestController
 @AllArgsConstructor
@@ -428,8 +426,187 @@ public class WbsTreeContractController extends BladeController {
             }
         }
     }
+    @SneakyThrows
+    @GetMapping("/download-node-excel")
+    @ApiOperationSupport(order = 14)
+    @ApiOperation(value = "客户端-下载节点下所有表单为多sheet excel", notes = "传入节点ID和分类")
+    public void downloadNodeExcel(@RequestParam Long nodeId, @RequestParam Integer classify, HttpServletResponse response) {
+        // 构建Excel文件名
+        StringBuilder excelName = new StringBuilder();
+        // 获取节点下所有表单
+        List<WbsTreeContract> formList = wbsTreeContractServiceImpl.selectAllPkeyIdByNodeId(nodeId, classify);
+        if (ObjectUtil.isEmpty(formList)) {
+            throw new ServiceException("该节点下没有找到对应的表单数据");
+        }
+
+        // 获取节点及祖先节点信息用于构建文件名
+        WbsTreeContract node = wbsTreeContractServiceImpl.getById(nodeId);
+        List<WbsTreeContract> ancestorsList = wbsTreeContractServiceImpl.getAncestorsList(node.getAncestorsPId());
+        for (WbsTreeContract ancestor : ancestorsList) {
+            if (2 == ancestor.getNodeType()) {
+                excelName.append(ancestor.getNodeName());
+            } else if (4 == ancestor.getNodeType()) {
+                excelName.append("-" + ancestor.getNodeName());
+            }
+        }
+        excelName.append("-" + node.getNodeName());
+
+        // 创建主工作簿(用于合并多sheet)
+        XSSFWorkbook mainWorkbook = new XSSFWorkbook();
+
+        try {
+            // 遍历所有表单,生成对应的sheet
+            for (WbsTreeContract form : formList) {
+                String pKeyId = form.getPKeyId()+"";
+                String sheetName = form.getNodeName();
+                // 处理sheet名称中的特殊字符(Excel不允许的字符)
+                sheetName = sheetName.replaceAll("[\\\\/:*?\"<>|]", "_");
+
+                // 1. 获取当前表单的htmlUrl(复用downloadExcel的逻辑)
+                String htmlUrl = "";
+                WbsTreeContract contractTab = iWbsTreeContractService.getBaseMapper()
+                        .selectOne(Wrappers.<WbsTreeContract>lambdaQuery().eq(WbsTreeContract::getPKeyId, pKeyId));
+
+                if (ObjectUtil.isEmpty(contractTab)) {
+                    // 尝试从试验表获取
+                    WbsTreePrivate privateTab = jdbcTemplate.query(
+                                    "select * from m_wbs_tree_private where p_key_id = ?",
+                                    new BeanPropertyRowMapper<>(WbsTreePrivate.class),
+                                    pKeyId)
+                            .stream().findAny().orElse(null);
+                    if (privateTab != null && privateTab.getHtmlUrl() != null) {
+                        htmlUrl = privateTab.getHtmlUrl();
+                    }
+                } else {
+                    htmlUrl = contractTab.getHtmlUrl();
+                }
+
+                // 跳过无html信息的表单
+                if (ObjectUtil.isEmpty(htmlUrl)) {
+                    logger.warn("表单pKeyId:{} 未获取到html信息,已跳过", pKeyId);
+                    continue;
+                }
+
+                // 2. 转换html为单个sheet的工作簿
+                InputStream htmlStream = FileUtils.getInputStreamByUrl(htmlUrl);
+                String htmlContent = IoUtil.readToString(htmlStream);
+                org.apache.poi.ss.usermodel.Workbook singleSheetWorkbook = HtmlTableToExcelConverter.convertHtmlTableToExcel(htmlContent);
+
+                // 3. 将单个sheet复制到主工作簿
+                if (singleSheetWorkbook.getNumberOfSheets() > 0) {
+                    Sheet sourceSheet = singleSheetWorkbook.getSheetAt(0);
+                    Sheet targetSheet = mainWorkbook.createSheet(sheetName);
+                    copySheetContent(mainWorkbook, sourceSheet, targetSheet);
+                }
+
+                // 关闭临时工作簿释放资源
+                singleSheetWorkbook.close();
+            }
+
+            // 4. 输出主工作簿到响应
+            if (mainWorkbook.getNumberOfSheets() == 0) {
+                throw new ServiceException("所有表单均无法生成有效Excel内容");
+            }
+
+            // 设置响应头
+            String encodedFileName = URLEncoder.encode(excelName + ".xlsx", "UTF-8")
+                    .replaceAll("\\+", " ")
+                    .replaceAll("%2B", "+");
+            response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+
+            // 写入输出流
+            try (ServletOutputStream outputStream = response.getOutputStream()) {
+                mainWorkbook.write(outputStream);
+                outputStream.flush(); // 强制刷出所有数据
+            }
+
+        } catch (Exception e) {
+            logger.error("下载节点多sheet Excel异常", e);
+            throw new ServiceException("下载失败:" + e.getMessage());
+        } finally {
+            if (mainWorkbook != null) {
+                mainWorkbook.close();
+            }
+        }
+    }
 
+    /**
+     * 复制sheet内容(包括样式、合并区域、行高列宽)
+     */
+    private void copySheetContent(XSSFWorkbook targetWorkbook, Sheet sourceSheet, Sheet targetSheet) {
+        // 复制合并区域
+        for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) {
+            CellRangeAddress mergedRegion = sourceSheet.getMergedRegion(i);
+            targetSheet.addMergedRegion(mergedRegion);
+        }
 
+        // 复制列宽
+        for (int col = 0; col <= sourceSheet.getLastRowNum(); col++) {
+            targetSheet.setColumnWidth(col, sourceSheet.getColumnWidth(col));
+        }
+
+        // 复制行数据及样式
+        for (int rowIdx = 0; rowIdx <= sourceSheet.getLastRowNum(); rowIdx++) {
+            Row sourceRow = sourceSheet.getRow(rowIdx);
+            if (sourceRow == null) continue;
+
+            Row targetRow = targetSheet.createRow(rowIdx);
+            targetRow.setHeight(sourceRow.getHeight());
+
+            // 复制单元格
+            for (int cellIdx = 0; cellIdx < sourceRow.getLastCellNum(); cellIdx++) {
+                Cell sourceCell = sourceRow.getCell(cellIdx);
+                if (sourceCell == null) continue;
+
+                Cell targetCell = targetRow.createCell(cellIdx);
+                copyCellContent(targetWorkbook, sourceCell, targetCell);
+            }
+        }
+    }
+
+    private void copyCellContent(XSSFWorkbook targetWorkbook, Cell sourceCell, Cell targetCell) {
+        // 复制样式
+        CellStyle targetStyle = targetWorkbook.createCellStyle();
+        targetStyle.cloneStyleFrom(sourceCell.getCellStyle());
+        targetCell.setCellStyle(targetStyle);
+
+        // 复制单元格值(完善类型处理)
+        CellType cellType = CellType.forInt(sourceCell.getCellType());
+        // 公式单元格需要先获取计算后的值
+        if (cellType == CellType.FORMULA) {
+            cellType = CellType.forInt(sourceCell.getCachedFormulaResultType());
+        }
+
+        switch (cellType) {
+            case STRING:
+                targetCell.setCellValue(sourceCell.getStringCellValue());
+                break;
+            case NUMERIC:
+                if (DateUtil.isCellDateFormatted(sourceCell)) {
+                    targetCell.setCellValue(sourceCell.getDateCellValue());
+                } else {
+                    targetCell.setCellValue(sourceCell.getNumericCellValue());
+                }
+                break;
+            case BOOLEAN:
+                targetCell.setCellValue(sourceCell.getBooleanCellValue());
+                break;
+            case FORMULA:
+                // 公式单元格同时复制公式和缓存结果
+                targetCell.setCellFormula(sourceCell.getCellFormula());
+                targetCell.setCellValue(sourceCell.getNumericCellValue()); // 补充缓存值
+                break;
+            case BLANK:
+                break;
+            case ERROR:
+                targetCell.setCellErrorValue(sourceCell.getErrorCellValue());
+                break;
+            default:
+                // 兜底处理,避免遗漏
+                targetCell.setCellValue(sourceCell.toString());
+        }
+    }
 
 
     // 计算最大列数以对齐所有行
@@ -507,7 +684,365 @@ public class WbsTreeContractController extends BladeController {
         cellStyle.setBorderRight(BorderStyle.THIN);
         cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
     }
+    @PostMapping("/import-node-excel")
+    @ApiOperationSupport(order = 13)
+    @ApiOperation(value = "客户端-导入多sheet excel到对应节点下的表单", notes = "传入节点ID、分类和多sheet excel文件")
+    public R<Map<String, Object>> importNodeExcel(
+            @RequestPart MultipartFile file,
+            @RequestParam Long nodeId,
+            @RequestParam Integer classify) throws Exception {
+
+        // 1. 获取节点下所有表单,并建立 sheet名 -> WbsTreeContract 的映射(处理特殊字符)
+        List<WbsTreeContract> wbsTreeContracts = wbsTreeContractServiceImpl.selectAllPkeyIdByNodeId(nodeId, classify);
+        if (wbsTreeContracts.isEmpty()) {
+            return R.fail("该节点下没有找到对应的表单数据");
+        }
+
+        // 处理表单名称(与下载时的sheet名处理逻辑一致,确保匹配)
+        Map<String, WbsTreeContract> nodeNameToContractMap = new HashMap<>();
+        for (WbsTreeContract contract : wbsTreeContracts) {
+            String processedNodeName = contract.getNodeName().replaceAll("[\\\\/:*?\"<>|]", "_");
+            nodeNameToContractMap.put(processedNodeName, contract);
+        }
+
+        // 2. 加载上传的多sheet Excel文件
+        com.spire.xls.Workbook mainWorkbook = new com.spire.xls.Workbook();
+        try {
+            mainWorkbook.loadFromStream(file.getInputStream());
+        } catch (Exception e) {
+            logger.error("加载Excel文件失败", e);
+            return R.fail("Excel文件解析失败:" + e.getMessage());
+        }
+
+        // 3. 遍历所有sheet,逐个处理
+        Map<String, Object> allResults = new LinkedHashMap<>(); // 按sheet顺序保存结果
+        int sheetCount = mainWorkbook.getWorksheets().getCount();
+
+        for (int i = 0; i < sheetCount; i++) {
+            com.spire.xls.Worksheet sheet = mainWorkbook.getWorksheets().get(i);
+            String sheetName = sheet.getName();
+            String processedSheetName = sheetName.replaceAll("[\\\\/:*?\"<>|]", "_"); // 处理sheet名特殊字符
+
+            // 匹配对应的表单
+            WbsTreeContract matchedContract = nodeNameToContractMap.get(processedSheetName);
+            if (matchedContract == null) {
+                allResults.put(sheetName, "未找到匹配的表单(节点名:" + sheetName + ")");
+                logger.warn("sheet名[{}]未匹配到任何表单,已跳过", sheetName);
+                continue;
+            }
+
+            // 获取当前表单的pkeyId
+            String pkeyId = matchedContract.getPKeyId() + "";
+            logger.info("开始处理sheet[{}],对应表单pkeyId[{}]", sheetName, pkeyId);
+
+            // 4. 将当前sheet保存为临时Excel文件(模拟单个文件上传)
+            File tempFile = null;
+            InputStream tempInputStream = null;
+            try {
+                // 创建临时文件
+                tempFile = File.createTempFile("sheet_", ".xlsx");
+                // 创建仅包含当前sheet的新工作簿
+                com.spire.xls.Workbook singleSheetWorkbook = new com.spire.xls.Workbook();
+                singleSheetWorkbook.getWorksheets().clear();
+                singleSheetWorkbook.getWorksheets().addCopy(sheet); // 复制当前sheet到新工作簿
+                singleSheetWorkbook.saveToFile(tempFile.getAbsolutePath(), com.spire.xls.FileFormat.Version2016);
+                singleSheetWorkbook.dispose();
+
+                // 读取临时文件作为输入流,调用单表单导入逻辑
+                tempInputStream = new FileInputStream(tempFile);
+                Map<String, Object> sheetResult = processSingleSheetImport(pkeyId, tempInputStream);
+                Map<String, String> dataMap = getDataMap(sheetResult);
+                String delSql = "delete from " + matchedContract.getInitTableName() + " where p_key_id=" + matchedContract.getPKeyId();
+                dataMap.put("p_key_id", matchedContract.getPKeyId()+"");
+                String  sqlInfo = buildMTableInsertSql(matchedContract.getInitTableName(), dataMap, SnowFlakeUtil.getId(), null, null).toString();
+                jdbcTemplate.execute(delSql);
+                jdbcTemplate.execute(sqlInfo);
+            } catch (Exception e) {
+                String errorMsg = "处理sheet[" + sheetName + "]失败:" + e.getMessage();
+                allResults.put(sheetName, errorMsg);
+                logger.error(errorMsg, e);
+            } finally {
+                // 关闭流并删除临时文件
+                if (tempInputStream != null) {
+                    tempInputStream.close();
+                }
+                if (tempFile != null && !tempFile.delete()) {
+                    logger.warn("临时文件[{}]删除失败", tempFile.getAbsolutePath());
+                }
+            }
+        }
+        mainWorkbook.dispose();
+        return R.data(allResults);
+    }
 
+    public StringBuilder buildMTableInsertSql(String tabName, Map<String, String> dataMap2, Object id, Object groupId, Object pKeyId) {
+        if (dataMap2 == null || dataMap2.isEmpty() || tabName == null || tabName.isEmpty()) {
+            return new StringBuilder();
+        }
+        //拼接SQL
+        StringBuilder sql = new StringBuilder("INSERT INTO " + tabName),
+                keySql = new StringBuilder(),
+                valSql = new StringBuilder();
+        if (id == null) {
+            if (dataMap2.containsKey("id")) {
+                id = dataMap2.get("id");
+            }
+        }
+        keySql.append("id");
+        valSql.append(id == null ? SnowFlakeUtil.getId() : id);
+        if (groupId ==  null) {
+            groupId = dataMap2.get("group_id");
+        }
+        if (groupId != null) {
+            keySql.append(", group_id");
+            valSql.append(", ").append(groupId);
+        }
+        if (pKeyId == null) {
+            pKeyId = dataMap2.get("p_key_id");
+        }
+        if (pKeyId != null) {
+            keySql.append(", p_key_id");
+            valSql.append(", ").append(pKeyId);
+        }
+        //参数
+        Map<String, String> opsParamMap = new HashMap<>();
+        dataMap2.remove("id");
+        dataMap2.remove("group_id");
+        dataMap2.remove("p_key_id");
+        String key201 = dataMap2.remove("key_201");
+        String fields = dataMap2.keySet().stream().map(key -> "'" + key + "'").collect(Collectors.joining(","));
+        Map<String, Integer> map = new HashMap<>();
+        if (!fields.isEmpty()) {
+            try {
+                fields = fields + ", 'key_201'";
+                List<Map<String, Object>> fieldMap = jdbcTemplate.queryForList("select distinct COLUMN_NAME as fieldName, CHARACTER_MAXIMUM_LENGTH as fieldLength from information_schema.COLUMNS where  TABLE_NAME = '" + tabName +
+                        "' and COLUMN_NAME in (" + fields + ")");
+                map = fieldMap.stream().collect(toMap(k -> k.get("fieldName") + "", v -> {
+                    try {
+                        return Integer.parseInt(v.get("fieldLength") + "");
+                    } catch (Exception e) {
+                        return 0;
+                    }
+                }, Math::min));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        if (key201 != null) {
+            Map<String, String> map1 = DataStructureFormatUtils.parseDataByKey(key201);
+            if (!map1.isEmpty()) {
+                opsParamMap.putAll(map1);
+            }
+        }
+        for (String key : dataMap2.keySet()) {
+            String[] split = key.split("_");
+            if (split.length > 1 && Integer.parseInt(split[1]) > 80) {
+                // 大于80则保留在扩展字段中
+                opsParamMap.put(key, dataMap2.get(key));
+            } else {
+                String value = dataMap2.get(key);
+                if (value != null) {
+                    Integer i = map.get(key);
+                    // 长度超过数据库长度也保留在扩展字段中
+                    if (i != null &&  value.length() > i) {
+                        opsParamMap.put(key, dataMap2.get(key));
+                        continue;
+                    }
+                }
+                keySql.append(", ").append(key);
+                valSql.append(", '").append(value).append("'");
+            }
+        }
+        if (!opsParamMap.isEmpty()) {
+            keySql.append(", key_201");
+            String data = DataStructureFormatUtils.buildData(opsParamMap);
+            try {
+                if (!map.containsKey( "key_201")) {
+                    jdbcTemplate.execute("alter table " + tabName + " add column key_201 text");
+                } else  {
+                    Integer i = map.get("key_201");
+                    if (data.length() > i) {
+                        if (i < 10000) {
+                            // 65535 byte
+                            jdbcTemplate.execute("alter table " + tabName + " modify column key_201 text");
+                        }else {
+                            // 16777215 byte
+                            jdbcTemplate.execute("alter table " + tabName + " modify column key_201 mediumtext");
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            valSql.append(", '").append(data).append("'");
+        }
+        sql.append("(").append(keySql).append(")").append(" values(").append(valSql).append(")");
+        return sql;
+    }
+
+    public Map<String,String> getDataMap(Map<String, Object> originalMap){
+        // 用于存储合并后的结果
+        Map<String, String> mergedMap = new HashMap<>();
+
+        // 正则表达式:匹配"key_前缀__剩余坐标"的格式
+        Pattern pattern = Pattern.compile("key_(\\w+)__([\\w_]+)");
+
+        for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue()+"";
+            Matcher matcher = pattern.matcher(key);
+
+            if (matcher.matches()) {
+                String prefix = matcher.group(1); // 提取__前面的前缀(如7、17)
+                String suffix = matcher.group(2); // 提取__后面的剩余坐标(如11_9、12_5)
+
+                // 构建合并后的键(简化为prefix,如7、17)
+                String mergedKey = "key_" + prefix;
+                // 构建合并后的值(值_^_剩余坐标)
+                String mergedValue = value + "_^_" + suffix;
+
+                // 若该键已存在,用☆拼接新值;否则直接存入
+                if (mergedMap.containsKey(mergedKey)) {
+                    mergedMap.put(mergedKey, mergedMap.get(mergedKey) + "☆" + mergedValue);
+                } else {
+                    mergedMap.put(mergedKey, mergedValue);
+                }
+            }
+        }
+        return mergedMap;
+    }
+    /**
+     * 复用importExcel的核心逻辑,处理单个sheet的导入
+     * 抽取自原importExcel方法,参数改为pkeyId和输入流
+     */
+    private Map<String, Object> processSingleSheetImport(String pkeyId, InputStream inputStream) throws Exception {
+        // 获取当前表htmlString(模板)
+        String htmlString_1 = wbsTreeContractServiceImpl.getHtmlString(pkeyId);
+        if (StringUtils.isEmpty(htmlString_1)) {
+            throw new ServiceException("获取表单[" + pkeyId + "]的html模板失败");
+        }
+
+        // 结果集
+        Map<String, Object> resultMap = new HashMap<>();
+
+        // 日期格式正则(复用原逻辑)
+        String doubleSlashRegex_XG = ".*\\/[^\\/]*\\/.*";
+        String dateFormatRegex_yyyyMdd = "\\d{4}/\\d{1,2}/\\d{1,2}";
+        String dateFormatRegex_yyyyMMdd = "\\d{4}/\\d{2}/\\d{2}";
+        String dateFormatRegex_chinese = "(\\d{4}年\\d{1,2}月\\d{1,2}日|\\d{4}年\\d{2}月\\d{2}日)";
+        SimpleDateFormat inputDateFormat = new SimpleDateFormat("yyyy/M/dd");
+        SimpleDateFormat outputDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
+
+        // 临时文件路径(复用原逻辑)
+        Long id = SnowFlakeUtil.getId();
+        String importExcelFilePath = FileUtils.getSysLocalFileUrl();
+        String importExcelTOHtmlPath = importExcelFilePath + "/pdf//" + id + ".html";
+
+        com.spire.xls.Workbook workbook = null;
+        try {
+            // 导入的excel转换为html(复用原逻辑)
+            workbook = new com.spire.xls.Workbook();
+            workbook.loadFromHtml(inputStream); // 加载单个sheet的输入流
+            workbook.saveToFile(importExcelTOHtmlPath, com.spire.xls.FileFormat.HTML);
+            com.spire.xls.Worksheet sheet = workbook.getWorksheets().get(0);
+
+            // 获取转换后的html路径
+            String url_1 = importExcelTOHtmlPath.split("pdf//")[0];
+            String excelToHtmlFileUrl = url_1 + "/pdf/" + id + "_files/" + sheet.getName() + ".html";
+            String htmlString_2 = IoUtil.readToString(new FileInputStream(ResourceUtil.getFile(excelToHtmlFileUrl)));
+
+            // 解析两张html的tr、td(复用原逻辑)
+            Document doc_1 = Jsoup.parse(htmlString_1); // 模板html
+            Document doc_2 = Jsoup.parse(htmlString_2); // 导入的excel转换的html
+            Elements trElements1 = doc_1.select("table tbody tr");
+            Elements trElements2 = doc_2.select("table tbody tr");
+
+            for (int i = 0; i < trElements1.size(); i++) {
+                Element tr1 = trElements1.get(i);
+                Element tr2 = trElements2.size() > i ? trElements2.get(i) : null;
+                if (tr2 == null) break;
+
+                Elements tdElements1 = tr1.select("td");
+                Elements tdElements2 = tr2.select("td");
+
+                for (int j = 0; j < tdElements1.size(); j++) {
+                    Element td1 = tdElements1.get(j);
+                    if (td1.attr("dqid").length() > 0) { // 跳过包含dqid的td
+                        continue;
+                    }
+
+                    Element td2 = tdElements2.size() > j ? tdElements2.get(j) : null;
+                    if (td2 == null) break;
+
+                    String keyName = getKeyNameFromChildElement(td1); // 复用原方法获取key
+                    if (StringUtils.isNotEmpty(keyName)) {
+                        String divValue = td2.text();
+                        if (StringUtils.isNotEmpty(divValue)) {
+                            // 日期范围处理
+                            if (parseDateRange(divValue).size() == 2) {
+                                resultMap.put(keyName, parseDateRange(divValue));
+                                continue;
+                            }
+
+                            // 日期格式转换(复用原逻辑)
+                            Pattern pattern_XG = Pattern.compile(doubleSlashRegex_XG);
+                            Matcher matcher_XG = pattern_XG.matcher(divValue);
+                            if (matcher_XG.matches()) {
+                                Pattern pattern_yyyyMdd = Pattern.compile(dateFormatRegex_yyyyMdd);
+                                Pattern pattern_yyyyMMdd = Pattern.compile(dateFormatRegex_yyyyMMdd);
+                                Matcher matcher_yyyyMdd = pattern_yyyyMdd.matcher(divValue);
+                                Matcher matcher_yyyyMMdd = pattern_yyyyMMdd.matcher(divValue);
+
+                                if (matcher_yyyyMdd.matches() || matcher_yyyyMMdd.matches()) {
+                                    Date date = inputDateFormat.parse(divValue);
+                                    divValue = outputDateFormat.format(date);
+                                }
+                            } else if (divValue.contains("年") && divValue.contains("月") && divValue.contains("日")) {
+                                Pattern pattern_chinese = Pattern.compile(dateFormatRegex_chinese);
+                                Matcher matcher_chinese = pattern_chinese.matcher(divValue);
+                                if (matcher_chinese.matches()) {
+                                    Date date = outputDateFormat.parse(divValue);
+                                    divValue = outputDateFormat.format(date);
+                                }
+                            }
+
+                            resultMap.put(keyName, divValue);
+                        }
+                    }
+                }
+            }
+
+            // 排序结果(复用原逻辑)
+            if (!resultMap.isEmpty()) {
+                List<Map.Entry<String, Object>> entryList = new ArrayList<>(resultMap.entrySet());
+                entryList.sort(new KeyComparator());
+                LinkedHashMap<String, Object> sortedMap = new LinkedHashMap<>();
+                for (Map.Entry<String, Object> entry : entryList) {
+                    sortedMap.put(entry.getKey(), entry.getValue());
+                }
+                resultMap = sortedMap;
+            }
+
+        } finally {
+            if (workbook != null) {
+                workbook.dispose();
+            }
+            // 删除临时文件(复用原逻辑)
+            if (deleteFolder(Paths.get(importExcelTOHtmlPath))) {
+                logger.info("表单[{}]临时文件删除成功", pkeyId);
+            } else {
+                logger.warn("表单[{}]临时文件删除失败", pkeyId);
+            }
+            String url_1 = importExcelTOHtmlPath.split("pdf//")[0];
+            if (deleteFolderAndContents(Paths.get(url_1 + "/pdf/" + id + "_files"))) {
+                logger.info("表单[{}]临时文件夹删除成功", pkeyId);
+            } else {
+                logger.warn("表单[{}]临时文件夹删除失败", pkeyId);
+            }
+        }
+
+        return resultMap.isEmpty() ? Collections.singletonMap("message", "未获取到有效数据") : resultMap;
+    }
     /**
      * 客户端-导入excel数据到对应元素表中
      *
@@ -970,6 +1505,8 @@ public class WbsTreeContractController extends BladeController {
 
     }
 
+
+
     /**
      * 判断日期范围格式数据,以下12种格式
      * 2023-01-01-2023-01-30 或 2023-01-01~2023-01-30
@@ -1217,5 +1754,11 @@ public class WbsTreeContractController extends BladeController {
     }
 
 
-
+    @PostMapping("/setFormulaReference")
+    @ApiOperation(value = "公式引用")
+    @ApiOperationSupport(order = 33)
+    public R setFormulaReference(@RequestBody FormulaReferenceDTO dto) {
+        iWbsTreeContractService.setFormulaReference(dto);
+        return R.success("成功");
+    }
 }

+ 3 - 3
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreePrivateController.java

@@ -845,9 +845,9 @@ public class WbsTreePrivateController extends BladeController {
      */
     @GetMapping("/get-excel-html")
     @ApiOperationSupport(order = 23)
-    @ApiOperation(value = "试验-客户端获取表的HTML", notes = "传入节点primaryKeyId")
-    public Object getExcelHtml(@RequestParam String primaryKeyId) throws Exception {
-        return wbsTreePrivateService.getExcelHtml(primaryKeyId);
+    @ApiOperation(value = "试验-客户端获取表的HTML", notes = "传入节点primaryKeyId和试验id")
+    public Object getExcelHtml(@RequestParam String primaryKeyId, String id) throws Exception {
+        return wbsTreePrivateService.getExcelHtml(primaryKeyId, id);
     }
 
     /**

+ 5 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ArchiveTreeContractImpl.java

@@ -49,6 +49,11 @@ public class ArchiveTreeContractImpl implements ArchiveTreeContractClient {
         return this.archiveTreeContractService.getAuthCode(contractId);
     }
 
+    @Override
+    public List<ArchiveTreeContract> getArchiveTreeContractListByListOrderByTreeSort(List<Long> nodeIds) {
+        return this.archiveTreeContractService.getArchiveTreeContractListByListOrderByTreeSort(nodeIds);
+    }
+
     /**
      * 获取项目下存在未组卷文件的归档树节点
      *

+ 2 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.java

@@ -190,4 +190,6 @@ public interface ArchiveTreeContractMapper extends BaseMapper<ArchiveTreeContrac
 
     Integer selectExtTypeByParentId(@Param("projectId")Long projectId,
                                     @Param("parentId") Long parentId);
+
+    List<ArchiveTreeContract> getArchiveTreeContractListByListOrderByTreeSort(@Param("nodeIds") List<Long> nodeIds);
 }

+ 13 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.xml

@@ -1028,4 +1028,17 @@
     <select id="selectExtTypeByParentId" resultType="java.lang.Integer">
         select count(0) from m_archive_tree_contract where project_id = #{projectId} and ext_type = 2 and is_deleted = 0 and FIND_IN_SET(#{parentId},ancestors)
     </select>
+    <select id="getArchiveTreeContractListByListOrderByTreeSort"
+            resultType="org.springblade.manager.entity.ArchiveTreeContract">
+        SELECT
+        d.*
+        FROM
+        m_archive_tree_contract d
+        WHERE
+        d.is_deleted = 0 and d.id in
+        <foreach item="id" collection="nodeIds" open="(" close=")" separator=",">
+            #{id}
+        </foreach>
+        ORDER BY case when d.tree_sort regexp '^[a-zA-Z]' then 0 when d.tree_sort regexp '^[0-9]' then 1 else 2 end, d.tree_sort
+    </select>
 </mapper>

+ 2 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.java

@@ -187,4 +187,6 @@ public interface WbsTreeContractMapper extends EasyBaseMapper<WbsTreeContract> {
     void updateAncestorsPid(@Param("ancestorsPId") String ancestorsPId, @Param("ancestors") String ancestors,@Param("pKeyId")Long pKeyId);
 
     void updateWbsTreeAncestors(@Param("contract")WbsTreeContract contract);
+
+    List<WbsTreeContract> getAncestorsList(@Param("pKeyIds") List<Long> longList);
 }

+ 8 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.xml

@@ -1077,5 +1077,13 @@
     <select id="getChildWbsTreeContracts" resultType="org.springblade.manager.entity.WbsTreeContract">
         select p_key_id,ancestors,ancestors_p_id from m_wbs_tree_contract where FIND_IN_SET(#{pKeyId}, ancestors_p_id)   and is_deleted=0
     </select>
+    <select id="getAncestorsList" resultType="org.springblade.manager.entity.WbsTreeContract">
+        select * from m_wbs_tree_contract where p_key_id in (
+        <foreach collection="pKeyIds" item="pkeyId" separator=",">
+            #{pkeyId}
+        </foreach>
+        ) and is_deleted=0
+        order by node_type
+    </select>
 
 </mapper>

+ 2 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IArchiveTreeContractService.java

@@ -147,4 +147,6 @@ public interface IArchiveTreeContractService extends BaseService<ArchiveTreeCont
     Long getNodeIdByName(String projectName, String contractName, String nodeName);
 
     boolean deleteTreeEx(Long id);
+
+    List<ArchiveTreeContract> getArchiveTreeContractListByListOrderByTreeSort(List<Long> nodeIds);
 }

+ 1 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IFormulaService.java

@@ -63,7 +63,7 @@ public interface IFormulaService extends BaseService<Formula> {
     /**
      * ids 表流水号 projectId项目 nodeId工序节点的pkeyId ExecuteType执行模式 质检或者试验 首件
      */
-    List<KeyMapper> getKeyMapperList(List<Long> ids, String projectId, String nodeId, ExecuteType executeType);
+    List<KeyMapper> getKeyMapperList(List<Long> ids, String projectId, String nodeId, ExecuteType executeType, String testGroupId);
 
    void formulaInto(List<FormData> curFormDatas, String projectId, String nodeId, ExecuteType executeType);
 

+ 5 - 4
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IWbsTreeContractService.java

@@ -4,10 +4,7 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.springblade.business.dto.EKeyDto;
 import org.springblade.core.mp.base.BaseService;
 import org.springblade.core.tool.api.R;
-import org.springblade.manager.dto.MoveNodeDTO;
-import org.springblade.manager.dto.RangeInfo;
-import org.springblade.manager.dto.WbsTreeContractDTO;
-import org.springblade.manager.dto.WbsTreeContractDTO2;
+import org.springblade.manager.dto.*;
 import org.springblade.manager.entity.ContractRelationJlyz;
 import org.springblade.manager.entity.WbsTreeContract;
 import org.springblade.manager.entity.WbsTreePrivate;
@@ -118,4 +115,8 @@ public interface IWbsTreeContractService extends BaseService<WbsTreeContract> {
     Object getWbsContractSubdivisionMeasurable(String projectId, String sectionId, String pid, Boolean judgment);
 
     List<QualityData> qualityDataAcquisition(String projectId, String sectionId, String codesId);
+
+    String isExistOssUrl(Long pkeyId,String key);
+
+    void setFormulaReference(FormulaReferenceDTO dto);
 }

+ 1 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IWbsTreePrivateService.java

@@ -84,7 +84,7 @@ public interface IWbsTreePrivateService extends BaseService<WbsTreePrivate> {
 
     List<WbsTreePrivateVO4> searchNodeAllTable(String primaryKeyId, String type, String tableType, String contractId, String projectId, Integer isAdd, Long id);
 
-    Object getExcelHtml(String primaryKeyId) throws Exception;
+    Object getExcelHtml(String primaryKeyId, String id) throws Exception;
 
     Object getExcelHtmlEntrust(String nodeId,String contractId) throws Exception;
 

+ 5 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractServiceImpl.java

@@ -800,6 +800,11 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 		return true;
 	}
 
+	@Override
+	public List<ArchiveTreeContract> getArchiveTreeContractListByListOrderByTreeSort(List<Long> nodeIds) {
+		return baseMapper.getArchiveTreeContractListByListOrderByTreeSort(nodeIds);
+	}
+
 
 	/**
 	 * 批量更新

+ 27 - 3
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ExcelTabServiceImpl.java

@@ -122,6 +122,7 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
 
     private final InformationQueryClient informationQueryClient;
     private final IWbsTreeContractService wbsTreeContractService;
+    private final WbsTreeContractExtendService wbsTreeContractExtendService;
     private final IFormulaService formulaService;
     @Autowired
     private JdbcTemplate jdbcTemplate;
@@ -515,6 +516,7 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
                                 e.setClassify(example.getClassify());
                                 e.setProjectId(example.getProjectId());
                                 e.setGroupId(example.getGroupId());
+                                e.setTestGroupId(example.getTestGroupId());
                                 map.forEach((key, value) -> {
                                     if (key.startsWith("key_")) {
                                         e.getDataMap().put(key, Func.toStr(value));
@@ -528,7 +530,7 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
                     tableInfoList.sort(Comparator.comparingInt(a -> tableAllIds.indexOf(Long.parseLong(a.getPkeyId()))));
                 }
                 StopWatch stopWatch = new StopWatch();
-                List<KeyMapper> keyMappers = this.formulaService.getKeyMapperList(tableAll.stream().map(NodeTable::getPKeyId).filter(Func::isNotEmpty).collect(Collectors.toList()), tableInfoList.get(0).getProjectId(), String.valueOf(nodeId), type);
+                List<KeyMapper> keyMappers = this.formulaService.getKeyMapperList(tableAll.stream().map(NodeTable::getPKeyId).filter(Func::isNotEmpty).collect(Collectors.toList()), tableInfoList.get(0).getProjectId(), String.valueOf(nodeId), type, tableInfoList.get(0).getTestGroupId());
                 if (Func.isNotEmpty(keyMappers) && Func.isNotEmpty(tableAll)) {
                     Map<String, Map<String, String>> coordinateMap = createCoordinateMap(keyMappers, type);
                     stopWatch.start("公式处理");
@@ -1086,13 +1088,35 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
     @Override
     public void gsColor(Long pKeyId, String nodeId, String projectId, Document doc) {
         try {
-            List<KeyMapper> keyMappers = this.formulaService.getKeyMapperList(Collections.singletonList(pKeyId), projectId, nodeId, ExecuteType.INSPECTION);
+            List<KeyMapper> keyMappers = this.formulaService.getKeyMapperList(Collections.singletonList(pKeyId), projectId, nodeId, ExecuteType.INSPECTION, "1");
+            //扩展----根据父节点查询不允许执行的公式
+            WbsTreeContractExtend byId = wbsTreeContractExtendService.getById(nodeId);
+            List<String> list;
+
+            if(byId != null){
+                String formulaConfig = byId.getFormulaConfig();
+                if(StringUtils.isNotEmpty(formulaConfig)){
+                    list = Arrays.asList(formulaConfig.split(","));
+                } else {
+                    list = new ArrayList<>();
+                }
+            } else {
+                list = new ArrayList<>();
+            }
+
             if (!keyMappers.isEmpty()) {
                 keyMappers.stream()
                         .filter(e -> e.getFormulaId() != null)
                         .forEach(e -> {
                             String key = e.getField() + "__";
-                            processElements(doc.select("table").first().select("[keyname^=" + key + "]"));
+                            Elements select = doc.select("table").first().select("[keyname^=" + key + "]");
+                            if(list.contains(e.getTableName() + ":" + e.getField())){
+                                //当前元素不执行公式
+                                select.stream().filter(Objects::nonNull).forEach(element -> element.attr("clearFormula", "1"));
+                            }else{
+                                select.stream().filter(Objects::nonNull).forEach(element -> element.attr("clearFormula", "0"));
+                                processElements(select);
+                            }
                         });
             }
         } catch (Exception e) {

+ 92 - 17
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java

@@ -79,6 +79,7 @@ import java.io.*;
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -131,6 +132,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     private final IWbsParamService wpService;
     private final FormulaStrategyFactory formulaStrategyFactory;
     private final IWbsTreeContractService wbsTreeContractService;
+    private final WbsTreeContractExtendService wbsTreeContractExtendService;
     private final WbsTreePrivateMapper wbsTreePrivateMapper;
     private final IElementFormulaMappingService elementFormulaMappingService;
     private final JdbcTemplate jdbcTemplate;
@@ -1244,8 +1246,21 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         try {
             List<String> dateList = new ArrayList<>();
             tec.formDataList.forEach(e -> {
-                if (e.executable() && e.getFormula().getFormula().contains(".weather(")) {
-                    String code = RegexUtil.findResult("(?<=weather\\(E\\[')[^']+(?='\\],WEATHER\\))", e.getFormula().getFormula());
+                if (e.executable() && (e.getFormula().getFormula().contains(".weather(") //天气
+                        || e.getFormula().getFormula().contains(".maxtembydate(") // 最大气温
+                        || e.getFormula().getFormula().contains(".mintembydate(")  // 最小气温
+                        || e.getFormula().getFormula().contains("maxminbydate(")  //最大最小气温
+                        || e.getFormula().getFormula().contains("weathertem(")    // 气温/天气
+                        || e.getFormula().getFormula().contains("weekbydate(")  // 日期计算周几
+                )) {
+                    String tyepData[] = new String[]{"WEATHER","MAXTEMBYDATE","MINTEMBYDATE","MAXMINBYDATE","WEATHERTEM","WEEKBYDATE"};
+                    String code ="";
+                    for (int i=0;i<tyepData.length;i++){
+                        code = RegexUtil.findResult("(?<=\\(E\\[')[^']+(?='\\],"+tyepData[i]+"\\))", e.getFormula().getFormula());
+                        if (code != null) {
+                            break;
+                        }
+                    }
                     if (code != null) {
                         FormData formData = tec.getFormDataMap().get(code);
                         if (formData != null && !formData.empty()) {
@@ -1256,12 +1271,51 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                 }
             });
             if (dateList.size() > 0) {
-                List<Map<String, Object>> listMap = this.jdbcTemplate.queryForList(" select DATE(b.record_time) ds,b.weather from m_project_contract_area a join u_weather_info b on a.id=contract_area_id where a.contract_id=" + tec.getContractId() + " and DATE(b.record_time) in('" + dateList.stream().distinct().collect(Collectors.joining("','")) + "')");
+                List<Map<String, Object>> listMap = this.jdbcTemplate.queryForList(" select DATE(b.record_time) ds,b.weather,temp_high as maxtembydate,temp_low as mintembydate,CONCAT_WS('~',temp_low,temp_high) as maxminbydate,CONCAT(b.weather,'、',temp_low,'℃~',temp_high,'℃') as weathertem from m_project_contract_area a join u_weather_info b on a.id=contract_area_id where a.contract_id=" + tec.getContractId() + " and DATE(b.record_time) in('" + dateList.stream().distinct().collect(Collectors.joining("','")) + "')");
+
+                // 天气
                 Map<String, String> map = new HashMap<>();
+                //最高气温
+                Map<String, String> map2 = new HashMap<>();
+                //最低气温
+                Map<String, String> map3 = new HashMap<>();
+                //星期几
+                Map<String, String> map4 = new HashMap<>();
+                //最高最低气温
+                Map<String, String> map5 = new HashMap<>();
+                //气温 天气
+                Map<String, String> map6 = new HashMap<>();
                 listMap.forEach(m -> {
-                    map.put(Func.toStr(m.get("ds")), Func.toStr(m.get("weather")));
+                    String ds = Func.toStr(m.get("ds"));
+                    map.put(ds, Func.toStr(m.get("weather")));
+                    //将时间转化星期几
+                    if (StringUtils.isNotEmpty(ds)) {
+                        Date datetime = null;
+                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+                        try {
+                            datetime = dateFormat.parse(ds);
+                        }catch (Exception e){
+
+                        }
+                        String[] weekDays = {"日", "一", "二", "三", "四", "五", "六"};
+                        Calendar calendar = Calendar.getInstance();
+                        calendar.setTime(datetime);
+                        String week = weekDays[calendar.get(Calendar.DAY_OF_WEEK) - 1];
+                        map4.put(ds, week);
+                    }else {
+                        map4.put(ds, "");
+                    }
+                    map2.put(ds, Func.toStr(m.get("maxtembydate")));
+                    map3.put(ds, Func.toStr(m.get("mintembydate")));
+                    map5.put(ds, Func.toStr(m.get("maxminbydate")));
+                    map6.put(ds, Func.toStr(m.get("weathertem")));
                 });
                 tec.constantMap.put("WEATHER", map);
+                tec.constantMap.put("MAXTEMBYDATE", map2);
+                tec.constantMap.put("MINTEMBYDATE", map3);
+                tec.constantMap.put("WEEKBYDATE", map4);
+                tec.constantMap.put("MAXMINBYDATE", map5);
+                tec.constantMap.put("WEATHERTEM", map6);
             }
         } catch (Exception e) {
             e.printStackTrace();
@@ -1278,7 +1332,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             checkTable = op.get().getInitTableName();
         }
         for (FormData fd : tec.formDataList) {
-            if(fd.getCode().equals("m_20230423154304_1650042591250481152:key_42")){
+            if(fd.getCode().equals("_20240528110420_1795289980302524416:key_8")){
                 System.out.println("111");
             }
             if (fd.verify()) {
@@ -1548,6 +1602,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                                 }
                             } else {
                                 putEle(f, ele, currentMap, fd);
+                               //公式获取值
                                 Object data = Expression.parse(formula.getFormula()).calculate(currentMap);
                                 //如果有空串,也要加进data不然表格数据顺序会混乱
                                 try {
@@ -1622,7 +1677,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                                         }
                                     }
                                 }
-
+                                // 公式获取的数据 准备写入 准装 字段和值的关系
                                 write(tec, fd, data);
                             }
                             /*错位计算偏移量重置*/
@@ -2511,12 +2566,12 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     }
 
     @Override
-    public List<KeyMapper> getKeyMapperList(List<Long> ids, String projectId, String nodeId, ExecuteType executeType) {
+    public List<KeyMapper> getKeyMapperList(List<Long> ids, String projectId, String nodeId, ExecuteType executeType, String testGroupId) {
         /*ids 表流水号 projectId项目 nodeId工序节点的pkeyId ExecuteType执行模式 质检或者试验 首件*/
         if (executeType.equals(ExecuteType.INSPECTION)) {
-            return listForContract(ids, projectId, nodeId);
+            return listForContract(ids, projectId, nodeId, testGroupId);
         } else if (executeType.equals(ExecuteType.TESTING) ) {
-            return listForPrivate(ids, projectId, nodeId);
+            return listForPrivate(ids, projectId, nodeId, testGroupId);
         } else if (executeType.equals(ExecuteType.LOGINFO) ) {
             return listForPrivateByLog(ids, projectId, nodeId);
         }
@@ -5222,8 +5277,18 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     }
 
     /*表id,项目id,节点Id*/
-    public List<KeyMapper> listForContract(List<Long> ids, String projectId, String nodeId) {
+    public List<KeyMapper> listForContract(List<Long> ids, String projectId, String nodeId, String testGroupId) {
         List<Map<String, Object>> listMap = listMap(ids, ExecuteType.INSPECTION);
+        //质检这个参数为0,表示过滤公式,为1,表示html渲染表格
+        if(!"1".equals(testGroupId)){
+            //根据节点id查询 合同段扩展表
+            WbsTreeContractExtend byId = wbsTreeContractExtendService.getById(nodeId);
+            if (byId != null && StringUtils.isNotEmpty(byId.getFormulaConfig())) {
+                List<String> list = Arrays.asList(byId.getFormulaConfig().split(","));
+                listMap = listMap.stream().filter(m -> !list.contains(m.get("tableName").toString() + ":" + m.get("field").toString())).collect(Collectors.toList());
+            }
+        }
+
         WbsTreePrivate wtp = wtpId(Long.parseLong(nodeId));
         if (Func.isNotEmpty(listMap)) {
             /*当前节点的某个元素存在多种作用域的公式,作用域范围越小优先级越高*/
@@ -5308,9 +5373,17 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
 
     }
 
-    private List<KeyMapper> listForPrivate(List<Long> ids, String projectId, String nodeId) {
+    private List<KeyMapper> listForPrivate(List<Long> ids, String projectId, String nodeId, String testGroupId) {
         /**/
         List<Map<String, Object>> listMap = listMap(ids, ExecuteType.TESTING);
+        if(StringUtil.isNotBlank(testGroupId)){
+            //根据节点id查询 合同段扩展表
+            WbsTreeContractExtend byId = wbsTreeContractExtendService.getById(testGroupId);
+            if (byId != null && StringUtils.isNotEmpty(byId.getFormulaConfig())) {
+                List<String> list = Arrays.asList(byId.getFormulaConfig().split(","));
+                listMap = listMap.stream().filter(m -> !list.contains(m.get("tableName").toString() + ":" + m.get("field").toString())).collect(Collectors.toList());
+            }
+        }
         List<KeyMapper> list = listMap.stream().map(m -> BeanUtil.toBean(m, KeyMapper.class)).collect(Collectors.toList());
         if (Func.isNotEmpty(listMap)) {
             WbsTreePrivate wtp = this.wbsTreePrivateMapper.selectOne(Wrappers.<WbsTreePrivate>lambdaQuery().eq(WbsTreePrivate::getPKeyId, nodeId));
@@ -6140,6 +6213,9 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         if(Objects.equals("≠",s1)){
             s1 = "!=";
         }
+        if(Objects.equals("=",s1)){
+            s1 = "==";
+        }
         if(Objects.equals("<",s1)){
             s1 = "<";
         }
@@ -6273,8 +6349,6 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                 /*设计值可以是数值类型,或空(等效0)*/
                 if (dataFd != null) {
                     List<Object> values = dataFd.getRawValue();
-                    /*保留小数位,去除*/
-                    values = values.stream().map(e -> StringUtils.number2StringZero(e,8)).collect(Collectors.toList());
                     //boolean nonNumeric = values.stream().filter(StringUtils::isNotEmpty).anyMatch(e -> !StringUtils.isNumber(e));
                     boolean nonNumeric = values.stream()
                             .filter(StringUtils::isNotEmpty)
@@ -6661,7 +6735,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
 
 
     @Override
-    public R<Object> evaluate(Long pkeyId) {
+    public R evaluate(Long pkeyId) {
         if (StringUtils.isNumber(pkeyId)) {
             /*节点信息*/
             WtcEva wtcEva = this.getSqlOne(
@@ -6715,7 +6789,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                                     xm.setName(name);
                                     xm.setSubItem(info.getTitle());
 
-                                    if(name.contains("△")){
+                                    if(name.contains("△")||name.contains("Δ")){
                                         xm.setWeight(2);
                                     }else{
                                         xm.setWeight(1);
@@ -6925,7 +6999,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                 }
             } catch (Exception e) {
                 e.printStackTrace();
-                return R.data("暂无数据");
+                return R.fail("暂无数据");
             }
         }
         return R.fail("无数据");
@@ -6997,6 +7071,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         }
         return remap;
     }
+
     public Map<String, String> getWtpParent(String meterType, String projectId) {
         Map<String, Object> map = this.jdbcTemplate.queryForMap("select id,wbs_id wbsId,CONCAT(ancestors_p_id,',',p_key_id) path from  m_wbs_tree_private a  where a.node_name=? and a.project_id=?", meterType, projectId);
         Map<String, String> result = new HashMap<>();
@@ -7928,5 +8003,5 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         }
         return wbsTreeContract;
     }
-
+    
 }

+ 95 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java

@@ -69,6 +69,7 @@ import org.springblade.manager.mapper.*;
 import org.springblade.manager.service.INodeBaseInfoService;
 import org.springblade.manager.service.ITableFileService;
 import org.springblade.manager.service.IWbsTreeContractService;
+import org.springblade.manager.service.WbsTreeContractExtendService;
 import org.springblade.manager.util.DataStructureFormatUtils;
 import org.springblade.manager.utils.CompositeKey;
 import org.springblade.manager.vo.*;
@@ -139,6 +140,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
     private final TrialCyAccessoriesClient trialCyAccessoriesClient;
     private final WbsTreeContractStatisticsClient wbsTreeContractStatisticsClient;
     private final FormulaDataBlockMapper formulaDataBlockMapper;
+    private final WbsTreeContractExtendService wbsTreeContractExtendService;
 
 
 
@@ -722,6 +724,24 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         }
     }
 
+    public List<WbsTreeContract> selectAllPkeyIdByNodeId(Long nodeId,Integer classify) {
+        Object[] params;
+        if (classify == 1) {
+            params = new Object[]{nodeId, 1, 2, 3};
+        } else {
+            params = new Object[]{nodeId, 4, 5, 6};
+        }
+
+        return jdbcTemplate.query(
+                "select * from m_wbs_tree_contract where p_id = ? and is_deleted = 0 and status!=0 and type = 2 and table_owner in (?,?,?) order by sort",
+                new BeanPropertyRowMapper<>(WbsTreeContract.class),
+                params);
+    }
+
+    public List<WbsTreeContract> getAncestorsList(String ancestorsPId) {
+        return baseMapper.getAncestorsList(Func.toLongList(ancestorsPId));
+    }
+
 
     public static class WbsTreeContractComparator implements Comparator<AppWbsTreeContractVO> {
 
@@ -6035,6 +6055,25 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         return qualityDatas;
     }
 
+    @Override
+    public String isExistOssUrl(Long pkeyId,String key) {
+        String[] keys = key.split("__");
+        WbsTreeContract wbsTreeContract = this.getById(pkeyId);
+        String querySql = "select * from " + wbsTreeContract.getInitTableName() + " where p_key_id=" + pkeyId;
+        List<Map<String, Object>> dataIn = jdbcTemplate.queryForList(querySql);
+        if(dataIn.isEmpty()){
+            return "";
+        }
+        Map<String, Object> map = dataIn.get(0);
+        Set<Map.Entry<String, Object>> entrySet = map.entrySet();
+        for (Map.Entry<String, Object> entry : entrySet) {
+            if(entry.getKey().equals(keys[0])){
+                return entry.getValue()==null ? "" : entry.getValue().toString();
+            }
+        }
+        return "";
+    }
+
     /**
      * 比较两个祖级路径
      */
@@ -6090,4 +6129,60 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         // 如果共同部分都相同,长度短的排在前面(父级在子级前面)
         return Integer.compare(arr1.length, arr2.length);
     }
+
+    @Override
+    public void setFormulaReference(FormulaReferenceDTO dto) {
+        try {
+            dto.setKey(dto.getKey().split("__")[0]);
+            WbsTreeContract parentNode = null;
+            if(dto.getType() == 1){
+                //查询父节点
+                parentNode = wbsTreeContractMapper.selectById(dto.getPId());
+            }
+
+            //查询父节点的扩展信息
+            WbsTreeContractExtend byId = wbsTreeContractExtendService.getById(dto.getPId());
+
+            if(byId != null){
+                String formulaConfig = byId.getFormulaConfig();
+                if(StringUtils.isNotEmpty(formulaConfig)){
+                    List<String> list = Arrays.asList(formulaConfig.split(","));
+                    HashSet<String> strings = new HashSet<>(list);
+                    if(dto.getStatus() == 1){
+                        strings.remove(dto.getInitTableName() + ":" + dto.getKey());
+                    }else{
+                        strings.add(dto.getInitTableName() + ":" + dto.getKey());
+                    }
+                    byId.setFormulaConfig(StringUtils.join(strings, ","));
+                }else{
+                    if(dto.getStatus() == 0){
+                        byId.setFormulaConfig(dto.getInitTableName() + ":" + dto.getKey());
+                    }
+                }
+                wbsTreeContractExtendService.updateById(byId);
+            }else{
+                if(dto.getStatus() == 0){
+                    WbsTreeContractExtend wbsTreeContractExtend = new WbsTreeContractExtend();
+                    if(dto.getType() == 1){
+                        if(parentNode == null){
+                            throw new ServiceException("父节点不存在");
+                        }
+                        wbsTreeContractExtend.setPKeyId(parentNode.getPKeyId());
+                        wbsTreeContractExtend.setContractId(Long.valueOf(parentNode.getContractId()));
+                        wbsTreeContractExtend.setAncestors(parentNode.getAncestorsPId());
+                    }
+                    if(dto.getType() == 2){
+                        wbsTreeContractExtend.setPKeyId(dto.getPId());
+                    }
+                    wbsTreeContractExtend.setType(null);
+                    wbsTreeContractExtend.setIsSync(0);
+                    wbsTreeContractExtend.setFormulaConfig(dto.getInitTableName() + ":" + dto.getKey());
+                    wbsTreeContractExtendService.save(wbsTreeContractExtend);
+                }
+            }
+        } catch (NumberFormatException e) {
+            e.printStackTrace();
+            throw new RuntimeException("失败");
+        }
+    }
 }

+ 44 - 13
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreePrivateServiceImpl.java

@@ -39,10 +39,7 @@ import org.springblade.manager.dto.WbsTreePrivateDTO2;
 import org.springblade.manager.dto.WbsTreePrivateDTO3;
 import org.springblade.manager.entity.*;
 import org.springblade.manager.mapper.*;
-import org.springblade.manager.service.IElementFormulaMappingService;
-import org.springblade.manager.service.IFormulaService;
-import org.springblade.manager.service.ITableInfoService;
-import org.springblade.manager.service.IWbsTreePrivateService;
+import org.springblade.manager.service.*;
 import org.springblade.manager.utils.CompositeKey1;
 import org.springblade.manager.utils.FileUtils;
 import org.springblade.manager.vo.*;
@@ -76,6 +73,7 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
 
     private final WbsTreeContractMapper wbsTreeContractMapper;
     private final WbsTreeContractServiceImpl wbsTreeContractService;
+    private final WbsTreeContractExtendService wbsTreeContractExtendService;
     private final ITableInfoService tableInfoService;
     private final WbsTreeMapper wbsTreeMapper;
     private final ContractInfoMapper contractInfoMapper;
@@ -2901,7 +2899,7 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
     }
 
     @Override
-    public Object getExcelHtml(String primaryKeyId) throws Exception {
+    public Object getExcelHtml(String primaryKeyId, String id) throws Exception {
 
         WbsTreePrivate wbsTreePrivate = baseMapper.selectOne(Wrappers.<WbsTreePrivate>query().lambda()
                 .eq(WbsTreePrivate::getPKeyId, primaryKeyId));
@@ -2957,18 +2955,51 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
         //字段查询、获取公式字段
         String colKeys = "SELECT e_key from m_table_info a ,m_wbs_form_element b WHERE a.tab_en_name = '" + tabName + "' and a.id=b.f_id and b.id  in(SELECT element_id from m_element_formula_mapping c where c.is_deleted=0) ";
         List<Map<String, Object>> maps = jdbcTemplate.queryForList(colKeys);
+        //扩展----根据父节点查询不允许执行的公式
+        List<String>  list = new ArrayList<>();;
+        if(StringUtil.isNotBlank(id)){
+            WbsTreeContractExtend byId = wbsTreeContractExtendService.getById(id);
+            if(byId != null){
+                String formulaConfig = byId.getFormulaConfig();
+                if(StringUtils.isNotEmpty(formulaConfig)){
+                    list = Arrays.asList(formulaConfig.split(","));
+                }
+            }
+        }
+
         if (maps.size() > 0) {
             for (Map<String, Object> keys : maps) {
                 String key = keys.get("e_key") + "__";
-                Elements gsColor = doc.select("el-input[keyname~=^" + key + "]");
-                for (Element element : gsColor) {
-                    element.parent().attr("gscolor", "11");
+                //采用质检html渲染方式获取表单数据
+                Elements select = doc.select("table").first().select("[keyname^=" + key + "]");
+                if(list.contains(tabName + ":" + keys.get("e_key"))){
+                    //当前元素不执行公式
+                    select.stream().filter(Objects::nonNull).forEach(element -> element.attr("clearFormula", "1"));
+                }else{
+                    //当前元素不执行公式
+                    select.stream().filter(Objects::nonNull).forEach(element -> element.attr("clearFormula", "0"));
+                    for (Element element : select) {
+                        element.parent().attr("gscolor", "11");
+                    }
                 }
 
-                Elements dateColor = doc.select("el-date-picker[keyname~=^" + key + "]");
-                for (Element element : dateColor) {
-                    element.parent().attr("gscolor", "11");
-                }
+//                Elements gsColor = doc.select("el-input[keyname~=^" + key + "]");
+//                if(list.contains(tabName + ":" + keys.get("e_key"))){
+//                    gsColor.stream().filter(Objects::nonNull).forEach(element -> element.attr("clearFormula", "1"));
+//                }else{
+//                    for (Element element : gsColor) {
+//                        element.parent().attr("gscolor", "11");
+//                    }
+//                }
+//                Elements dateColor = doc.select("el-date-picker[keyname~=^" + key + "]");
+//                if(list.contains(tabName + ":" + keys.get("e_key"))){
+//                    dateColor.stream().filter(Objects::nonNull).forEach(element -> element.attr("clearFormula", "1"));
+//                }else{
+//                    for (Element element : dateColor) {
+//                        element.parent().attr("gscolor", "11");
+//                    }
+//                }
+
             }
         }
         doc.select("Col").remove();
@@ -3003,7 +3034,7 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
         } else {
             return R.fail("该合同段没有委托单权限业务");
         }
-        Object excelHtml = getExcelHtml(pkeyId);
+        Object excelHtml = getExcelHtml(pkeyId, null);
         return excelHtml;
     }
 

+ 5 - 1
pom.xml

@@ -226,7 +226,11 @@
             <name>Release Repository</name>
             <url>http://nexus.bladex.vip/repository/maven-releases/</url>
         </repository>
-
+        <repository>
+            <id>com.e-iceblue</id>
+            <name>e-iceblue</name>
+            <url>https://repo.e-iceblue.cn/repository/maven-public/</url>
+        </repository>
     </repositories>
 
     <pluginRepositories>