Просмотр исходного кода

Merge remote-tracking branch 'origin/test-merge' into test-merge

LHB 1 месяц назад
Родитель
Сommit
af56decc2a
24 измененных файлов с 574 добавлено и 103 удалено
  1. 7 7
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/WbsTreeContractStatistics.java
  2. 4 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ArchiveFileClient.java
  3. 3 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/WbsTreeContractStatisticsClient.java
  4. 17 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/WbsInfoDTO.java
  5. 13 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsInfo.java
  6. 1 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsNodeTableVO.java
  7. 0 2
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreePrivateTableVO.java
  8. 12 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileController.java
  9. 81 0
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ArchiveFileClientImpl.java
  10. 10 0
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/WbsTreeContractStatisticsClientImpl.java
  11. 2 2
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/WbsTreeContractStatisticsMapper.xml
  12. 1 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/IWbsTreeContractStatisticsService.java
  13. 2 2
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ContractLogServiceImpl.java
  14. 93 2
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/WbsTreeContractStatisticsServiceImpl.java
  15. 40 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsInfoController.java
  16. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeController.java
  17. 1 7
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreePrivateController.java
  18. 3 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsInfoMapper.java
  19. 2 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsInfoMapper.xml
  20. 1 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.xml
  21. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IWbsInfoService.java
  22. 54 10
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java
  23. 59 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsInfoServiceImpl.java
  24. 166 62
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java

+ 7 - 7
blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/WbsTreeContractStatistics.java

@@ -63,38 +63,38 @@ public class WbsTreeContractStatistics implements Serializable {
      * 叶子节点数量
      */
     @ApiModelProperty(value = "叶子节点数量")
-    private Integer leafNum = 0;
+    private Integer leafNum;
 
     /**
      * 已填报数量
      */
     @ApiModelProperty(value = "已填报数量")
-    private Integer fillNum = 0;
+    private Integer fillNum;
     /**
      * 待审批数量
      */
     @ApiModelProperty(value = "待审批数量")
-    private Integer approveNum = 0;
+    private Integer approveNum;
     /**
      * 已审批数量
      */
     @ApiModelProperty(value = "已审批数量")
-    private Integer completeNum = 0;
+    private Integer completeNum;
     /**
      * 已填报数量(监理)
      */
     @ApiModelProperty(value = "已填报数量(监理)")
-    private Integer jlFillNum = 0;
+    private Integer jlFillNum;
     /**
      * 待审批数量(监理)
      */
     @ApiModelProperty(value = "待审批数量(监理)")
-    private Integer jlApproveNum = 0;
+    private Integer jlApproveNum;
     /**
      * 已审批数量(监理)
      */
     @ApiModelProperty(value = "已审批数量(监理)")
-    private Integer jlCompleteNum = 0;
+    private Integer jlCompleteNum;
 
     @DateTimeFormat(
             pattern = "yyyy-MM-dd HH:mm:ss"

+ 4 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ArchiveFileClient.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import org.springblade.archive.dto.FindAndReplaceDto;
 import org.springblade.archive.dto.SendsWebSocketArchiveDTO;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.vo.ArchiveFileVO;
@@ -167,4 +168,7 @@ public interface ArchiveFileClient {
 
     @PostMapping(API_PREFIX + "/getArchiveFileByArchiveIds")
     List<ArchiveFile> getArchiveFileByArchiveIds(@RequestParam String archiveIds);
+
+    @PostMapping(API_PREFIX + "/findAndReplace")
+    boolean findAndReplace(@RequestBody List<ArchiveFile> archiveFiles, @RequestParam FindAndReplaceDto dto);
 }

+ 3 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/WbsTreeContractStatisticsClient.java

@@ -45,4 +45,7 @@ public interface WbsTreeContractStatisticsClient {
     @GetMapping(API_PREFIX + "/statisticsContract")
     void statisticsContract(@RequestParam Long contractId);
 
+    @GetMapping(API_PREFIX + "/updateAncestors")
+    void updateAncestors(@RequestParam String ids);
+
 }

+ 17 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/WbsInfoDTO.java

@@ -4,8 +4,11 @@ import io.swagger.annotations.ApiModelProperty;
 import io.swagger.models.auth.In;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.springblade.manager.entity.ProjectInfo;
 import org.springblade.manager.entity.WbsInfo;
 
+import java.util.List;
+
 @Data
 @EqualsAndHashCode(callSuper = true)
 public class WbsInfoDTO extends WbsInfo {
@@ -27,5 +30,19 @@ public class WbsInfoDTO extends WbsInfo {
      */
     private Integer status;
 
+    /**
+     * 项目名称
+     */
+    @ApiModelProperty(value = "项目名称")
+    private String projectName;
+
+    /**
+     * 项目名称
+     */
+    @ApiModelProperty(value = "项目名称")
+    private String projectId;
+
+    List<ProjectInfo> projectInfoList;
+
 
 }

+ 13 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsInfo.java

@@ -1,10 +1,13 @@
 package org.springblade.manager.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import org.springblade.core.mp.base.BaseEntity;
 
+import java.util.List;
+
 @Data
 @TableName("m_wbs_info")
 @EqualsAndHashCode(callSuper = true)
@@ -21,5 +24,15 @@ public class WbsInfo extends BaseEntity {
      */
     private Integer wbsType;
 
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+    /**
+     * 项目列表
+     */
+    @TableField(exist = false)
+    private List<ProjectInfo> projectInfoList;
 
 }

+ 1 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsNodeTableVO.java

@@ -92,5 +92,6 @@ public class WbsNodeTableVO implements Serializable {
     private String htmlUrl;
 
     private String nodeType;
+    private Integer wbsType;
 
 }

+ 0 - 2
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreePrivateTableVO.java

@@ -22,8 +22,6 @@ public class WbsTreePrivateTableVO  implements Serializable {
     @ApiModelProperty(value = "标题")
     private String title;
 
-    private Integer sort;
-
     @ApiModelProperty(value = "表单list")
     List<WbsNodeTableVO> list;
 

+ 12 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileController.java

@@ -10,6 +10,8 @@ import io.swagger.annotations.*;
 import lombok.AllArgsConstructor;
 import lombok.SneakyThrows;
 import org.apache.commons.lang.StringUtils;
+import org.springblade.archive.dto.FindAndReplaceDto;
+import org.springblade.archive.entity.ArchivesAuto;
 import org.springblade.archive.service.IArchivesAutoService;
 import org.springblade.archive.utils.FileUtils;
 import org.springblade.archive.vo.ArchiveDataVo;
@@ -458,4 +460,14 @@ public class ArchiveFileController extends BladeController {
         //数据入库
         return  autoService.sendFileToEArchives(vo);
     }
+
+
+
+    @PostMapping("/findAndReplace")
+    @ApiOperationSupport(order = 12)
+    @ApiOperation(value = "查找并替换", notes = "传入ids")
+    public R findAndReplace(@RequestBody FindAndReplaceDto dto){
+        List<ArchiveFile> archiveFiles = archiveFileClient.getAllArchiveFileByArchiveIds(Func.toStrList(dto.getIds()));
+        return R.status(archiveFileClient.findAndReplace(archiveFiles,dto));
+    }
 }

+ 81 - 0
blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ArchiveFileClientImpl.java

@@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
 import org.apache.commons.lang.StringUtils;
+import org.springblade.archive.dto.FindAndReplaceDto;
+import org.springblade.archive.entity.ArchivesAuto;
 import org.springblade.archive.vo.ArchivesAutoVO;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.feign.ArchiveFileClient;
@@ -16,6 +18,7 @@ import org.springblade.business.service.IArchiveFileService;
 import org.springblade.business.service.ITaskService;
 import org.springblade.business.vo.ArchiveFileVO;
 import org.springblade.common.utils.FileUtils;
+import org.springblade.core.log.exception.ServiceException;
 import org.springblade.manager.entity.ContractInfo;
 import org.springblade.manager.enums.StorageTypeEnum;
 import org.springblade.manager.feign.ContractClient;
@@ -352,4 +355,82 @@ public class ArchiveFileClientImpl implements ArchiveFileClient {
         return iArchiveFileService.getArchiveFileByArchivesId(archiveIds,null);
     }
 
+    @Override
+    public boolean findAndReplace(List<ArchiveFile> archiveFiles, FindAndReplaceDto dto) {
+        List<ArchiveFile> updates = new ArrayList<>();
+        for (ArchiveFile archiveFile : archiveFiles) {
+            ArchiveFile auto = new ArchiveFile();
+            if (dto.getType() == 1) {
+                if (StringUtils.isNotEmpty(dto.getQuery()) && StringUtils.isNotEmpty(dto.getReplace())) {
+                    String name = archiveFile.getFileName();
+                    if (StringUtils.isNotEmpty(name) && name.contains(dto.getQuery())) {
+                        StringBuilder sb = new StringBuilder();
+                        int lastIndex = 0;
+                        // 查找所有匹配的位置
+                        int index = name.indexOf(dto.getQuery());
+                        while (index != -1) {
+                            // 添加未处理部分(从上一个 lastIndex 到当前 index)
+                            sb.append(name, lastIndex, index);
+
+                            // 根据位置插入替换内容
+                            if (dto.getPosition() != null) {
+                                switch (dto.getPosition()) {
+                                    case 1: // 在查询内容前插入
+                                        sb.append(dto.getReplace());
+                                        sb.append(dto.getQuery());
+                                        break;
+                                    case 2: // 在查询内容后插入
+                                        sb.append(dto.getQuery());
+                                        sb.append(dto.getReplace());
+                                        break;
+                                    default:
+                                        throw new ServiceException("请选择正确的定位条件");
+                                }
+                            } else {
+                                sb.append(dto.getQuery()); // 没有指定位置时保留原内容
+                            }
+                            // 更新 lastIndex 到当前匹配结束位置
+                            lastIndex = index + dto.getQuery().length();
+                            // 继续查找下一个匹配项
+                            index = name.indexOf(dto.getQuery(), lastIndex);
+                        }
+                        // 添加剩余的部分
+                        sb.append(name.substring(lastIndex));
+                        // 更新名称
+                        archiveFile.setFileName(sb.toString());
+                        auto.setId(archiveFile.getId());
+                        auto.setFileName(archiveFile.getFileName());
+                        updates.add(auto);
+                    }
+                }
+            } else if (dto.getType() == 2) {
+                if (StringUtils.isNotEmpty(dto.getQuery()) && StringUtils.isNotEmpty(dto.getReplace())) {
+                    String name = archiveFile.getFileName();
+                    if (StringUtils.isNotEmpty(name) && name.contains(dto.getQuery())) {
+                        String newName = name.replaceAll(dto.getQuery(), dto.getReplace());
+                        archiveFile.setFileName(newName);
+                        auto.setId(archiveFile.getId());
+                        auto.setFileName(archiveFile.getFileName());
+                        updates.add(auto);
+                    }
+                }
+            } else {
+                if (StringUtils.isNotEmpty(dto.getQuery())) {
+                    String name = archiveFile.getFileName();
+                    if (StringUtils.isNotEmpty(name) && name.contains(dto.getQuery())) {
+                        String newName = name.replaceAll(dto.getQuery(), "");
+                        archiveFile.setFileName(newName);
+                        auto.setId(archiveFile.getId());
+                        auto.setFileName(archiveFile.getFileName());
+                        updates.add(auto);
+                    }
+                }
+            }
+        }
+        if (!updates.isEmpty()) {
+            return iArchiveFileService.updateBatchById(updates);
+        }
+        return true;
+    }
+
 }

+ 10 - 0
blade-service/blade-business/src/main/java/org/springblade/business/feignClient/WbsTreeContractStatisticsClientImpl.java

@@ -9,6 +9,7 @@ import org.springblade.business.feign.WbsTreeContractStatisticsClient;
 import org.springblade.business.mapper.InformationQueryMapper;
 import org.springblade.business.service.IWbsTreeContractStatisticsService;
 import org.springblade.business.service.impl.WbsTreeContractStatisticsServiceImpl;
+import org.springblade.common.utils.SystemUtils;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
@@ -36,6 +37,9 @@ public class WbsTreeContractStatisticsClientImpl implements WbsTreeContractStati
 
     @Scheduled(cron = "0/30 * * * * ?")
     public void updateInformationQueryStatus() {
+        if (!SystemUtils.isLinux()) {
+            return;
+        }
         Map<String, String> map = new HashMap<>();
         List<String> queryIds = redisTemplate.opsForList().range(INFORMATION_QUERY_STATUS_STATISTICS_KEY, 0, 100);
         if (queryIds != null && !queryIds.isEmpty()) {
@@ -160,4 +164,10 @@ public class WbsTreeContractStatisticsClientImpl implements WbsTreeContractStati
     public void statisticsContract(Long contractId) {
         wbsTreeContractStatisticsService.statisticsContract(contractId);
     }
+
+    @Override
+    @Async
+    public void updateAncestors(String ids) {
+        wbsTreeContractStatisticsService.updateAncestors(ids);
+    }
 }

+ 2 - 2
blade-service/blade-business/src/main/java/org/springblade/business/mapper/WbsTreeContractStatisticsMapper.xml

@@ -28,14 +28,14 @@
     <update id="updateLeafNum">
         UPDATE m_wbs_tree_contract_statistics a INNER JOIN (
                 select  b.id, COUNT(c.id) AS leaf_num, sum(c.fill_num) as fill_num, sum(c.approve_num) as approve_num, sum(c.complete_num) as complete_num, sum(c.jl_fill_num) as jl_fill_num, sum(c.jl_approve_num) as jl_approve_num, sum(c.jl_complete_num) as jl_complete_num
-                FROM m_wbs_tree_contract_statistics b INNER JOIN m_wbs_tree_contract_statistics c  ON c.contract_id = b.contract_id AND c.is_leaf = 1 AND c.is_deleted = 0  AND c.status = 1 AND c.ancestors LIKE CONCAT(b.ancestors, ',', b.id, '%')
+                FROM m_wbs_tree_contract_statistics b INNER JOIN m_wbs_tree_contract_statistics c  ON c.contract_id = b.contract_id AND c.leaf_num = 0 AND c.is_deleted = 0  AND c.status = 1 AND c.ancestors LIKE CONCAT(b.ancestors, ',', b.id, '%')
                 WHERE b.id IN (
                     <foreach collection="ids" item="item" separator=",">
                         #{item}
                     </foreach>
                     ) GROUP BY b.id
             ) AS t ON a.id = t.id
-        SET a.leaf_num = t.leaf_num, a.fill_num = t.fill_num,a.approve_num = t.approve_num, a.complete_num = t.complete_num, a.jl_fill_num =t.jl_fill_num, a.jl_approve_num = t.jl_approve_num, a.jl_complete_num = t.jl_complete_num;
+        SET a.leaf_num = t.leaf_num, a.fill_num = t.fill_num,a.approve_num = t.approve_num, a.complete_num = t.complete_num, a.jl_fill_num =t.jl_fill_num, a.jl_approve_num = t.jl_approve_num, a.jl_complete_num = t.jl_complete_num, a.is_leaf = if(t.leaf_num > 0, 0, 1);
     </update>
     <select id="getByIdOnLock" resultMap="ResultMap">
         select <include refid="includeSql"/> from m_wbs_tree_contract_statistics where id = #{id} for update

+ 1 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/IWbsTreeContractStatisticsService.java

@@ -12,5 +12,6 @@ public interface IWbsTreeContractStatisticsService extends IService<WbsTreeContr
     Boolean updateIsDeleted(Long id, Integer isDeleted);
     void statisticsContract(Long contractId);
 
+    void updateAncestors(String ids);
     void sync();
 }

+ 2 - 2
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ContractLogServiceImpl.java

@@ -254,7 +254,7 @@ public class ContractLogServiceImpl extends BaseServiceImpl<ContractLogMapper, C
                 .eq(ContractLog::getContractId, logVo.getContractId())
                 .eq(ContractLog::getWbsNodeId, logVo.getWbsNodeId())
                 .isNotNull(ContractLog::getPdfUrl)
-                .orderByAsc(ContractLog::getRecordTime)
+                .orderByDesc(ContractLog::getRecordTime)
         );
         if(CollectionUtil.isEmpty(contractLogs)){
             return false;
@@ -297,7 +297,7 @@ public class ContractLogServiceImpl extends BaseServiceImpl<ContractLogMapper, C
                     contractLogMonthPack.setCreateUser(userId);
 
                     //排序
-                    monthLogs.sort(Comparator.comparing(ContractLog::getRecordTime));
+                    monthLogs.sort(Comparator.comparing(ContractLog::getRecordTime).reversed());
                     List<String> pdfFiles = monthLogs.stream().map(m -> {
                         if(StringUtil.isNotBlank(m.getEVisaPdfUrl())){
                             return m.getEVisaPdfUrl();

+ 93 - 2
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/WbsTreeContractStatisticsServiceImpl.java

@@ -240,6 +240,92 @@ public class WbsTreeContractStatisticsServiceImpl extends ServiceImpl<WbsTreeCon
         return true;
     }
 
+    @Override
+    public void updateAncestors(String ids) {
+        if (!StringUtil.hasText(ids)) {
+            return;
+        }
+        String[] split = ids.split(",");
+        if (split.length == 0) {
+            return;
+        }
+        Map<Long, Long> map = new HashMap<>();
+        {
+            ids = Arrays.stream(split).filter(StringUtil::isNumeric).distinct().collect(Collectors.joining(","));
+            List<WbsTreeContract> query = jdbcTemplate.query("SELECT id,p_key_id,p_id,parent_id FROM m_wbs_tree_contract WHERE is_deleted = 0 and type = 1 and p_key_id in (" + String.join(",", ids) + ")", new BeanPropertyRowMapper<>(WbsTreeContract.class));
+            if (query.isEmpty()) {
+                return;
+            }
+            // 只更新父级节点
+            Map<Long, List<WbsTreeContract>> childrenMap = query.stream().collect(Collectors.groupingBy(WbsTreeContract::getPId));
+            for (WbsTreeContract wbsTreeContract : query) {
+                List<WbsTreeContract> list = childrenMap.get(wbsTreeContract.getPKeyId());
+                if (list == null) {
+                    // 更新当前节点 parent ancestors字段
+                    map.put(wbsTreeContract.getPKeyId(), wbsTreeContract.getPId());
+                }
+            }
+        }
+        if (map.isEmpty()) {
+            return;
+        }
+        List<WbsTreeContractStatistics> wbsTreeContractStatistics = this.listByIds(map.keySet());
+        List<WbsTreeContractStatistics> parentList = this.listByIds(map.values());
+        Map<Long, WbsTreeContractStatistics> parentMap = parentList.stream().collect(Collectors.toMap(WbsTreeContractStatistics::getId, item -> item));
+        List<WbsTreeContractStatistics> updateList = new ArrayList<>();
+        List<String> sqlList = new ArrayList<>();
+        for (WbsTreeContractStatistics statistics : wbsTreeContractStatistics) {
+            Long parentId = map.get(statistics.getId());
+            WbsTreeContractStatistics update = new WbsTreeContractStatistics();
+            update.setId(statistics.getId());
+            if (parentId == null) {
+                update.setParentId(0L);
+                update.setAncestors("0");
+                updateList.add(update);
+                continue;
+            }
+            WbsTreeContractStatistics parent = parentMap.get(parentId);
+            if (parent == null) {
+                update.setParentId(0L);
+                update.setAncestors("0");
+                updateList.add(update);
+                continue;
+            }
+            update.setParentId(parent.getId());
+            update.setAncestors(parent.getAncestors() + "," + parent.getId());
+            updateList.add(update);
+            WbsTreeContractStatistics temp = new WbsTreeContractStatistics();
+            temp.setId(statistics.getParentId());
+            temp.setAncestors(statistics.getAncestors());
+            parentMap.put(statistics.getParentId(), temp);
+            String oldAncestors = statistics.getAncestors() + "," + statistics.getId();
+            String newAncestors = update.getAncestors() + "," + statistics.getId();
+            String sql = "UPDATE m_wbs_tree_contract_statistics SET ancestors = replace(ancestors, '" + oldAncestors + "', '" + newAncestors + "') WHERE is_deleted = 0 and status = 1 and ancestors like '"
+                    + oldAncestors + "%' and contract_id = " + statistics.getContractId();
+            sqlList.add(sql);
+        }
+        Set<Long> parentIds = new HashSet<>();
+        parentMap.values().forEach(item -> {
+            parentIds.add(item.getId());
+            String ancestors = item.getAncestors();
+            if (ancestors != null && !ancestors.isEmpty()) {
+                String[] split1 = ancestors.split(",");
+                for (String s : split1) {
+                    if (StringUtil.isNumeric(s)) {
+                        parentIds.add(Long.parseLong(s));
+                    }
+                }
+            }
+        });
+        this.updateBatchById(updateList);
+        for (String s : sqlList) {
+            jdbcTemplate.update(s);
+        }
+        if (!parentIds.isEmpty()) {
+            this.baseMapper.updateLeafNum(parentIds);
+        }
+    }
+
     public void createWbsTreeContractStatisticsParent(WbsTreeContract wbsTreeContract, WbsTreeContractStatistics wbsTreeContractStatistics, List<WbsTreeContractStatistics> wbsTreeContractStatisticsList) {
         if (wbsTreeContract.getParentId() == null || wbsTreeContract.getParentId() <= 0) {
             wbsTreeContractStatistics.setParentId(0L);
@@ -253,6 +339,7 @@ public class WbsTreeContractStatisticsServiceImpl extends ServiceImpl<WbsTreeCon
                 return;
             }
             parent = createWbsTreeContractStatistics(wbsTreeContractParent);
+            parent.setIsLeaf(0);
             createWbsTreeContractStatisticsParent(wbsTreeContractParent, parent, wbsTreeContractStatisticsList);
             wbsTreeContractStatisticsList.add(parent);
         } else {
@@ -260,20 +347,24 @@ public class WbsTreeContractStatisticsServiceImpl extends ServiceImpl<WbsTreeCon
                 wbsTreeContractStatistics.setParentId(parent.getId());
                 wbsTreeContractStatistics.setAncestors(parent.getAncestors() + "," + parent.getId());
             }
+            if (parent.getIsLeaf() == 1) {
+                this.update(Wrappers.<WbsTreeContractStatistics>lambdaUpdate().eq(WbsTreeContractStatistics::getId, parent.getId()).set(WbsTreeContractStatistics::getIsLeaf, 0));
+            }
         }
     }
 
     public void createWbsTreeContractStatisticsChildren(WbsTreeContract wbsTreeContract, WbsTreeContractStatistics wbsTreeContractStatistics, List<WbsTreeContractStatistics> wbsTreeContractStatisticsList) {
         List<WbsTreeContract> wbsTreeContracts =  jdbcTemplate.query("select * from m_wbs_tree_contract where is_deleted = 0 and type = 1 and contract_id = ? and p_id = ?",
                 new Object[]{wbsTreeContract.getContractId(), wbsTreeContract.getPKeyId()}, new BeanPropertyRowMapper<>(WbsTreeContract.class));
-        if (wbsTreeContracts == null || wbsTreeContracts.isEmpty()) {
+        if (wbsTreeContracts.isEmpty()) {
             wbsTreeContractStatistics.setIsLeaf(1);
             wbsTreeContractStatistics.setLeafNum(0);
             return;
         }
+        wbsTreeContractStatistics.setIsLeaf(0);
         wbsTreeContracts.forEach(item -> {
             WbsTreeContractStatistics statistics = createWbsTreeContractStatistics(item);
-            createWbsTreeContractStatisticsChildren(item, wbsTreeContractStatistics, wbsTreeContractStatisticsList);
+            createWbsTreeContractStatisticsChildren(item, statistics, wbsTreeContractStatisticsList);
             statistics.setAncestors(wbsTreeContractStatistics.getAncestors() + "," + wbsTreeContractStatistics.getId());
             statistics.setParentId(wbsTreeContractStatistics.getId());
             wbsTreeContractStatisticsList.add(statistics);

+ 40 - 2
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsInfoController.java

@@ -1,5 +1,6 @@
 package org.springblade.manager.controller;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import io.swagger.annotations.*;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
@@ -9,9 +10,12 @@ import javax.validation.Valid;
 
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
+import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.manager.dto.WbsInfoDTO;
+import org.springblade.manager.entity.ContractInfo;
 import org.springblade.manager.entity.WbsInfo;
 import org.springblade.manager.entity.WbsTree;
 import org.springblade.manager.entity.WbsTreePrivate;
@@ -26,6 +30,8 @@ import org.springblade.manager.wrapper.WbsInfoWrapper;
 import org.springblade.manager.service.IWbsInfoService;
 import org.springblade.core.boot.ctrl.BladeController;
 
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 @RestController
@@ -67,7 +73,9 @@ public class WbsInfoController extends BladeController {
     @ApiOperationSupport(order = 2)
     @ApiOperation(value = "分页", notes = "传入wbsInfo")
     public R<IPage<WbsInfoVO>> list(WbsInfoDTO wbsInfo, Query query) {
-        IPage<WbsInfo> pages = wbsInfoService.page(Condition.getPage(query), Condition.getQueryWrapper(wbsInfo));
+        QueryWrapper<WbsInfo> wrapper = Condition.getQueryWrapper(wbsInfo);
+        wrapper.orderByAsc("sort");
+        IPage<WbsInfo> pages = wbsInfoService.page(Condition.getPage(query), wrapper);
         return R.data(WbsInfoWrapper.build().pageVO(pages));
     }
 
@@ -82,7 +90,7 @@ public class WbsInfoController extends BladeController {
             @ApiImplicitParam(name = "current", value = "当前页", required = true),
             @ApiImplicitParam(name = "size", value = "每页的数量", required = true)
     })
-    public R<IPage<WbsInfo>> page(WbsInfo wbsInfo, Query query) {
+    public R<IPage<WbsInfo>> page(WbsInfoDTO wbsInfo, Query query) {
         IPage<WbsInfo> pages = wbsInfoService.selectWbsInfoPage(Condition.getPage(query), wbsInfo);
         return R.data(pages);
     }
@@ -179,5 +187,35 @@ public class WbsInfoController extends BladeController {
         return R.status(wbsInfoService.tabNodeSort(Func.toStrList(primaryKeyIds)));
     }
 
+    /**
+     * 排序
+     */
+    @PostMapping("/sort")
+    @ApiOperationSupport(order = 9)
+    @ApiOperation(value = "排序", notes = "传入排序好的ids")
+    public R<Boolean> sort(@RequestBody List<Long> wbsInfoIds) {
+        int sort = 0;
+        if (wbsInfoIds == null || wbsInfoIds.isEmpty()) {
+            return R.data(false);
+        }
+        List<WbsInfo> updateList = new ArrayList<>(wbsInfoIds.size());
+        Long userId = AuthUtil.getUserId();
+        for (Long id : wbsInfoIds) {
+            WbsInfo temp = new WbsInfo();
+            temp.setSort(++sort);
+            temp.setUpdateUser(userId);
+            temp.setUpdateTime(new Date());
+            temp.setId(id);
+            updateList.add(temp);
+        }
+        try {
+            boolean result = wbsInfoService.updateBatchById(updateList);
+            return R.data(result);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.fail(200, "排序保存失败");
+        }
+    }
+
 
 }

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

@@ -538,7 +538,7 @@ public class WbsTreeController extends BladeController {
     @GetMapping("/getQueryValueByNodeType")
     @ApiOperationSupport(order = 21)
     @ApiOperation(value = "根据节点类型,内业资料类型查询节点数据", notes = "传入wbsId、projectId、nodeType、majorDataType")
-    public Object getQueryValueByNodeType(@RequestParam WbsTreePrivateQueryVO vo) {
+    public Object getQueryValueByNodeType(WbsTreePrivateQueryVO vo) {
         Object result = wbsTreeService.getQueryValueByNodeType(vo);
         return R.data(result);
     }

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

@@ -400,13 +400,7 @@ public class WbsTreePrivateController extends BladeController {
         List<WbsNodeTableVO> data = r.getData();
         List<WbsTreePrivateTableVO> list = new ArrayList<>();
         if (data != null && !data.isEmpty()) {
-            Integer wbsType = null;
-            if (StringUtil.isNumeric(wbsId)) {
-                List<WbsInfo> query = jdbcTemplate.query("select id, wbs_type from m_wbs_info where is_deleted = 0 and id = " + Long.parseLong(wbsId), new BeanPropertyRowMapper<>(WbsInfo.class));
-                if (!query.isEmpty()) {
-                    wbsType = query.get(0).getWbsType();
-                }
-            }
+            Integer wbsType = data.get(0).getWbsType();
             Map<Integer, List<WbsNodeTableVO>> groupMap;
             if (wbsType != null && wbsType == 1) {
                 groupMap = data.stream().collect(Collectors.groupingBy(vo -> {

+ 3 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsInfoMapper.java

@@ -2,13 +2,15 @@ package org.springblade.manager.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.lettuce.core.dynamic.annotation.Param;
+import org.springblade.manager.dto.WbsInfoDTO;
 import org.springblade.manager.entity.WbsInfo;
 
 import java.util.List;
 
 public interface WbsInfoMapper extends BaseMapper<WbsInfo> {
 
-    List<WbsInfo> selectWbsInfoPage(IPage page, WbsInfo wbsInfo);
+    List<WbsInfoDTO> selectWbsInfoPage(IPage page, @Param("wbsInfo") WbsInfo wbsInfo);
 
     List<WbsInfo> selectAll(Integer type);
 

+ 2 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsInfoMapper.xml

@@ -14,6 +14,7 @@
         <result column="is_deleted" property="isDeleted"/>
         <result column="wbs_name" property="wbsName"/>
         <result column="wbs_type" property="wbsType"/>
+        <result column="sort" property="sort"/>
     </resultMap>
     <resultMap id="resultMap2" type="org.springblade.manager.vo.WbsInfoVO">
         <result column="id" property="id"/>
@@ -42,7 +43,7 @@
         GROUP BY
         wbs_name
         ORDER BY
-        create_time
+        sort,create_time
     </select>
 
     <select id="selectById2" resultMap="wbsInfoResultMap">

+ 1 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.xml

@@ -519,6 +519,7 @@
                wt.sort,
                wt.status,
                wt.node_type                                                                         AS nodeType,
+               wt.wbs_type                                                                          AS wbsType,
                wt.fill_rate                                                                         AS "fillRate",
                wt.html_url                                                                          AS htmlUrl,
                (SELECT count(1)

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

@@ -11,7 +11,7 @@ import java.util.List;
 
 public interface IWbsInfoService extends BaseService<WbsInfo> {
 
-    IPage<WbsInfo> selectWbsInfoPage(IPage<WbsInfo> page, WbsInfo wbsInfo);
+    IPage<WbsInfo> selectWbsInfoPage(IPage<WbsInfo> page, WbsInfoDTO wbsInfo);
 
     List<WbsInfo> findByWbsType(String type);
 

+ 54 - 10
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java

@@ -1262,7 +1262,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             checkTable = op.get().getInitTableName();
         }
         for (FormData fd : tec.formDataList) {
-            if(fd.getCode().equals("m_20220928144828_1575014563634479104:key_24")){
+            if(fd.getCode().equals("m_20230423154304_1650042591250481152:key_42")){
                 System.out.println("111");
             }
             if (fd.verify()) {
@@ -5303,20 +5303,40 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                 }else{
                     int max = 0;
                     do {
-                        Matcher m = RegexUtils.matcher(FC_REG + "(ifelse)\\(([^)]+)\\)", f);
+                        // 修改正则表达式,使用非贪婪匹配来处理嵌套情况
+                        Matcher m = RegexUtils.matcher(FC_REG + "(ifelse)\\(((?:[^()]++|\\([^()]*\\))*?)\\)", f);
+                        boolean found = false;
+
                         while (m.find()) {
+                            found = true;
                             String el = m.group();
-                            String pstr = el.replaceAll("^" + FC_REG + "ifelse\\(", "").replaceAll("\\)$", "");
-                            String[] pa = pstr.split(",");
-                            if (pa.length == 3) {
-                                Object data = Expression.parse(pa[0] + "?" + pa[1] + ":" + pa[2]).calculate(createCurrentMap(el, tec));
-                                f = f.replace(el, putDataWithKey(data, tec));
-                            } else {
-                                f = f.replace(el, "参数格式错误");
-                            }
 
+                            try {
+                                // 提取括号内的内容(去掉外层的ifelse())
+                                String content = el.replaceFirst("^" + FC_REG + "ifelse\\(", "").replaceFirst("\\)$", "");
+
+                                // 解析参数,处理嵌套逗号(在括号内的逗号不应该作为分隔符)
+                                List<String> pa = parseParameters(content);
+
+                                if (pa.size() == 3) {
+                                    Map<String, Object> currentMap = createCurrentMap(el, tec);
+                                    String string = pa.get(0) + "?" + pa.get(1) + ":" + pa.get(2);
+                                    Expression parse = Expression.parse(string);
+                                    Object data = parse.calculate(currentMap);
+                                    f = f.replace(el, putDataWithKey(data, tec));
+                                } else {
+                                    f = f.replace(el, "参数格式错误");
+                                }
+                            } catch (Exception e) {
+                                f = f.replace(el, "解析错误");
+                            }
                         }
+
                         max++;
+                        // 如果没有找到匹配项,提前退出循环
+                        if (!found) {
+                            break;
+                        }
                     } while (f.contains("ifelse") && max < 20);
                 }
             }
@@ -5355,6 +5375,30 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         }
     }
 
+    // 添加参数解析方法,处理嵌套逗号
+    private List<String> parseParameters(String content) {
+        List<String> params = new ArrayList<>();
+        int depth = 0;
+        int start = 0;
+
+        for (int i = 0; i < content.length(); i++) {
+            char c = content.charAt(i);
+            if (c == '(') {
+                depth++;
+            } else if (c == ')') {
+                depth--;
+            } else if (c == ',' && depth == 0) {
+                // 只有在最外层才分割参数
+                params.add(content.substring(start, i).trim());
+                start = i + 1;
+            }
+        }
+        // 添加最后一个参数
+        params.add(content.substring(start).trim());
+
+        return params;
+    }
+
     /**
      * 符号替换
      * @param s1

+ 59 - 3
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsInfoServiceImpl.java

@@ -1,24 +1,28 @@
 package org.springblade.manager.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.manager.dto.WbsInfoDTO;
+import org.springblade.manager.entity.ProjectInfo;
 import org.springblade.manager.entity.WbsTree;
 import org.springblade.manager.mapper.WbsInfoMapper;
 import org.springblade.manager.service.IWbsInfoService;
 import org.springblade.core.mp.base.BaseServiceImpl;
 import org.springblade.manager.entity.WbsInfo;
 import org.springblade.manager.vo.WbsInfoVO;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.List;
+import java.util.*;
 
 @Service
 @AllArgsConstructor
@@ -28,8 +32,60 @@ public class WbsInfoServiceImpl extends BaseServiceImpl<WbsInfoMapper, WbsInfo>
     private final JdbcTemplate jdbcTemplate;
 
     @Override
-    public IPage<WbsInfo> selectWbsInfoPage(IPage<WbsInfo> page, WbsInfo wbsInfo) {
-        return page.setRecords(baseMapper.selectWbsInfoPage(page, wbsInfo));
+    public IPage<WbsInfo> selectWbsInfoPage(IPage<WbsInfo> page, WbsInfoDTO wbsInfo) {
+        Set<Long> wbsIds = new HashSet<>();
+        if (StringUtil.hasText(wbsInfo.getProjectName()) || wbsInfo.getProjectId() != null) {
+            List<ProjectInfo> projectInfoList;
+            if (wbsInfo.getProjectId() != null) {
+                projectInfoList = jdbcTemplate.query("select * from m_project_info where id = " + wbsInfo.getProjectId(), new BeanPropertyRowMapper<>(ProjectInfo.class));
+            } else {
+                projectInfoList = jdbcTemplate.query("select * from m_project_info where project_name like '%" + wbsInfo.getProjectName() + "%'", new BeanPropertyRowMapper<>(ProjectInfo.class));
+            }
+            if (projectInfoList.isEmpty()) {
+                return page;
+            }
+            for (ProjectInfo projectInfo : projectInfoList) {
+                if (projectInfo.getReferenceWbsTemplateId() != null) {
+                    wbsIds.add(projectInfo.getReferenceWbsTemplateId());
+                }
+                if (projectInfo.getReferenceWbsTemplateIdTrial() != null) {
+                    wbsIds.add(projectInfo.getReferenceWbsTemplateIdTrial());
+                }
+                if (projectInfo.getReferenceWbsTemplateIdLar() != null) {
+                    wbsIds.add(projectInfo.getReferenceWbsTemplateIdLar());
+                }
+                if (projectInfo.getReferenceWbsTemplateIdMeter() != null) {
+                    wbsIds.add(projectInfo.getReferenceWbsTemplateIdMeter());
+                }
+                if (projectInfo.getReferenceWbsTemplateIdLar() != null) {
+                    wbsIds.add(projectInfo.getReferenceWbsTemplateIdLar());
+                }
+            }
+        }
+        LambdaQueryWrapper<WbsInfo> query = Wrappers.lambdaQuery();
+        if (!wbsIds.isEmpty()) {
+            query.in(WbsInfo::getId, wbsIds);
+        }
+        if (StringUtil.hasText(wbsInfo.getWbsName())) {
+            query.like(WbsInfo::getWbsName, wbsInfo.getWbsName());
+        }
+        if (wbsInfo.getWbsType() != null) {
+            query.eq(WbsInfo::getWbsType, wbsInfo.getWbsType());
+        }
+        IPage<WbsInfo> infoIPage = baseMapper.selectPage(page, query);
+        List<WbsInfo> records = infoIPage.getRecords();
+        if (!records.isEmpty()) {
+            for (WbsInfo record : records) {
+                List<ProjectInfo> projectInfoList = jdbcTemplate.query("select id, project_name from m_project_info where reference_log_wbs_template_id = ? " +
+                                "or reference_wbs_template_id = ? or reference_wbs_template_id_trial = ? or reference_wbs_template_id_lar = ? or reference_wbs_template_id_meter = ?"
+                        ,new Object[]{record.getId(), record.getId(), record.getId(), record.getId(), record.getId()} ,new BeanPropertyRowMapper<>(ProjectInfo.class));
+                if (!projectInfoList.isEmpty()) {
+                    record.setProjectInfoList(projectInfoList);
+                }
+            }
+            records.sort(Comparator.comparing(WbsInfo::getSort));
+        }
+        return infoIPage;
     }
 
     @Override

+ 166 - 62
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java

@@ -69,6 +69,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.ByteArrayResource;
 import org.springframework.core.io.Resource;
 import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
@@ -92,12 +93,14 @@ import java.math.BigInteger;
 import java.math.MathContext;
 import java.math.RoundingMode;
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 
 @Service
@@ -124,6 +127,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
     private final WbsFormElementMapper wbsFormElementMapper;
     private final TrialCyAccessoriesClient trialCyAccessoriesClient;
     private final WbsTreeContractStatisticsClient wbsTreeContractStatisticsClient;
+    private final FormulaDataBlockMapper formulaDataBlockMapper;
 
 
 
@@ -4486,7 +4490,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         String templatePath = "/mnt/sdc/Users/hongchuangyanfa/Desktop/excel/gcdcTemplate.xlsx";
         //String templatePath = "C:\\upload\\excel\\gcdc.xlsx";
         // 查询数据
-        String sql = "select * from m_wbs_tree_contract where contract_id = " + contractId +
+        String sql = "select *,CONCAT(ancestors_p_id, ',', p_key_id) AS ancestors_p_id from m_wbs_tree_contract where contract_id = " + contractId +
                 " and is_deleted = 0 and type = 1 and node_type != 6 and p_id != 0";
         List<WbsTreeContract> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContract.class));
 
@@ -4510,8 +4514,8 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
                 .collect(Collectors.toMap(
                         WbsTreeContract::getPKeyId,
                         unit -> list.stream()
-                                .filter(item -> item.getAncestors() != null &&
-                                        item.getAncestors().contains(unit.getPKeyId().toString()))
+                                .filter(item -> item.getAncestorsPId() != null &&
+                                        item.getAncestorsPId().contains(unit.getPKeyId().toString()))
                                 .collect(Collectors.toList())
                 ));
 
@@ -4519,15 +4523,17 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         for (Map.Entry<Long, List<WbsTreeContract>> entry : unitProjectMap.entrySet()) {
             Long unitId = entry.getKey();
             List<WbsTreeContract> unitProjects = entry.getValue();
-
-            // 获取单位工程名称
-            String unitName = unitProjects.stream()
-                    .filter(item -> item.getPKeyId().equals(unitId))
-                    .findFirst()
-                    .map(WbsTreeContract::getNodeName)
-                    .orElse("未知单位工程");
+            String sql1="Select * from m_wbs_tree_contract where p_key_id="+unitId;
+            WbsTreeContract unitNode=jdbcTemplate.queryForObject(sql1,new BeanPropertyRowMapper<>(WbsTreeContract.class));
+            unitProjects.add(unitNode);
+//            // 获取单位工程名称
+//            String unitName = unitProjects.stream()
+//                    .filter(item -> item.getPKeyId().equals(unitId))
+//                    .findFirst()
+//                    .map(WbsTreeContract::getNodeName)
+//                    .orElse("未知单位工程");
             // 创建安全的sheet名称
-            String safeUnitName = unitName.replaceAll("[\\\\/*\\[\\]:?]", "_");
+            String safeUnitName = unitNode.getNodeName().replaceAll("[\\\\/*\\[\\]:?]", "_");
             if (safeUnitName.length() > 31) {
                 safeUnitName = safeUnitName.substring(0, 31);
             }
@@ -4553,8 +4559,8 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         }
 
          //保存文件到本地(本地测试放开,正式环境不需要)
-//         String outputPath = "C:\\upload\\excel\\111.xlsx";
-//        saveWorkbookToFile(workbook, outputPath);
+         //String outputPath = "C:\\upload\\excel\\111.xlsx";
+         //saveWorkbookToFile(workbook, outputPath);
 
         // 同时返回给浏览器下载
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -4564,13 +4570,12 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray());
 
 
-        // 对中文文件名进行URL编码
-        String filename = "划分导出(请勿修改隐藏列).xlsx";
-        String encodedFilename = URLEncoder.encode(filename, "UTF-8")
-                .replaceAll("\\+", "%20"); // 替换+号为%20,确保兼容性
-
+        // 设置响应头
+        String fileName = "工程划分(请勿删除或修隐藏列).xlsx";
+        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString())
+                .replaceAll("\\+", "%20");
         return ResponseEntity.ok()
-                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFilename + "\"; filename*=utf-8''" + encodedFilename)
+                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=" + encodedFileName)
                 .contentType(MediaType.APPLICATION_OCTET_STREAM)
                 .contentLength(resource.contentLength())
                 .body(resource);
@@ -4601,6 +4606,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         List<WbsTreeContract> wbsTreeContractList = new ArrayList<>();
         int totalCount = 0;
         int successCount = 0;
+        Map<Long, String>map=new HashMap<>();
         try (InputStream inputStream = file.getInputStream();
              XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
 
@@ -4624,13 +4630,16 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
                         if (isValidCell(pkeyCell) && isValidCell(codeCell)) {
                             String pkeyId = getCellValue(pkeyCell);
                             String partitionCode = getCellValue(codeCell);
-
+                            if(map.containsKey(Long.parseLong(pkeyId))){
+                                continue;
+                            }
                             if (StringUtils.isNotBlank(pkeyId) && StringUtils.isNotBlank(partitionCode)) {
                                 totalCount++;
                                 WbsTreeContract wbsTreeContract = new WbsTreeContract();
                                 wbsTreeContract.setPKeyId(Long.parseLong(pkeyId.trim()));
                                 wbsTreeContract.setPartitionCode(partitionCode.trim());
                                 wbsTreeContractList.add(wbsTreeContract);
+                                map.put(wbsTreeContract.getPKeyId(),wbsTreeContract.getPartitionCode());
                                 successCount++;
                             }
                         }
@@ -4655,6 +4664,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         return R.status(false);
     }
 
+
     @Override
     public Boolean getIsImport(Long projectId, Long contractId) {
         Object o = bladeRedis.get("import:projectId:" + projectId + "contractId:" + contractId);
@@ -4682,8 +4692,42 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
 
     @Override
     public R moveNode(MoveNodeDTO dto) {
-      List<WbsTreeContract> list= wbsTreeContractMapper.getWbsTreeContractsByPKeyIds(dto.getLeftPkeyIds());
+        if(dto.getLeftPkeyIds().isEmpty()){
+            throw new ServiceException("请选择需要移动的节点");
+        }
+        if(dto.getRightPkeyId()==null){
+            throw new ServiceException("请选择要移动到哪个节点");
+        }
+        List<WbsTreeContract> list= wbsTreeContractMapper.getWbsTreeContractsByPKeyIds(dto.getLeftPkeyIds());
+        WbsTreeContract moveFatherNode = this.getById(list.get(0).getPId());
         WbsTreeContract fatherContract = this.getById(dto.getRightPkeyId());
+
+        String sql="SELECT * from m_formula_data_block WHERE sw_id = "+moveFatherNode.getId()+" and contract_id ="+moveFatherNode.getContractId()+" and  type =0";
+        List<FormulaDataBlock> formulaDataBlocks1 = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(FormulaDataBlock.class));
+
+        String sql1="SELECT * from m_formula_data_block WHERE sw_id = "+fatherContract.getId()+" and contract_id ="+fatherContract.getContractId()+" and  type =0";
+        List<FormulaDataBlock> formulaDataBlocks2 = jdbcTemplate.query(sql1, new BeanPropertyRowMapper<>(FormulaDataBlock.class));
+        List<ElementBlock> elementBlocks1=new ArrayList<>();
+        List<ElementBlock> elementBlocks2=new ArrayList<>();
+        if(formulaDataBlocks2.size()>0){
+            if(formulaDataBlocks1.size()>0){
+                elementBlocks1= JSON.parseArray(formulaDataBlocks1.get(0).getVal(), ElementBlock.class);
+                elementBlocks2= JSON.parseArray(formulaDataBlocks2.get(0).getVal(), ElementBlock.class);
+                elementBlocks2.addAll(elementBlocks1);
+                formulaDataBlocks2.get(0).setVal(JSON.toJSONString(elementBlocks2));
+                formulaDataBlockMapper.updateById(formulaDataBlocks2.get(0));
+            }
+        }else {
+            if(formulaDataBlocks1.size()>0){
+                elementBlocks1= JSON.parseArray(formulaDataBlocks1.get(0).getVal(), ElementBlock.class);
+                FormulaDataBlock insert = new FormulaDataBlock();
+                insert.setContractId(Long.parseLong(fatherContract.getContractId()));
+                insert.setType(0);
+                insert.setSwId(fatherContract.getId());
+                insert.setVal(JSON.toJSONString(elementBlocks1));
+                formulaDataBlockMapper.insert(insert);
+            }
+        }
         for (WbsTreeContract contract : list) {
             String oldancestorsPId = contract.getAncestorsPId();
             String oldancestors = contract.getAncestors();
@@ -4704,6 +4748,8 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
             }
             this.wbsTreeContractMapper.updateWbsTreeAncestors(contract);
         }
+        String ids = dto.getLeftPkeyIds().stream().map(id -> id + "").collect(Collectors.joining(","));
+        this.wbsTreeContractStatisticsClient.updateAncestors(ids);
         return R.success("操作成功");
     }
 
@@ -4828,13 +4874,14 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
             Row row = sheet.createRow(rowNum);
 
             // 解析祖级节点
-            String[] ancestors = leafNode.getAncestors().split(",");
+            String[] ancestors = leafNode.getAncestorsPId().split(",");
 
             // 转换为 List<Long>
             List<Long> ancestorIds = Arrays.stream(ancestors)
                     .map(Long::parseLong)
                     .collect(Collectors.toList());
 
+            ancestorIds.add(leafNode.getPKeyId());
             // 根据叶子节点类型填充各级工程数据
             fillEngineeringDataByLeafType(row, unitProjects, ancestorIds, mergeMap, rowNum, centerStyle, leafNode.getNodeType());
 
@@ -4849,6 +4896,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
     private List<WbsTreeContract> findLeafNodesByPriority(List<WbsTreeContract> projects) {
         // 按照节点类型优先级:子分项(5) -> 分项(4) -> 子分部(3) -> 分部(2) -> 单位工程(18)
         List<WbsTreeContract> leafNodes = new ArrayList<>();
+        Set<Long> ancestorIds= new HashSet<>();
 
         // 1. 先找子分项工程 (节点类型5)
         leafNodes = projects.stream()
@@ -4856,39 +4904,90 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
                 .collect(Collectors.toList());
 
         if (!leafNodes.isEmpty()) {
-            return leafNodes;
+            sortLeafNodes(leafNodes);
         }
+        ancestorIds= leafNodes.stream()
+                .map(WbsTreeContract::getAncestorsPId)
+                .filter(Objects::nonNull)
+                .flatMap(ancestors -> Arrays.stream(ancestors.split(",")))
+                .map(String::trim)
+                .filter(id -> !id.isEmpty())
+                .map(Long::parseLong)
+                .collect(Collectors.toSet());
 
         // 2. 如果没有子分项,找分项工程 (节点类型4)
-        leafNodes = projects.stream()
-                .filter(item -> item.getNodeType() == 4)
+        Set<Long> finalAncestorIds = ancestorIds;
+        List<WbsTreeContract> items=projects.stream()
+                .filter(item -> item.getNodeType() == 4 && !finalAncestorIds.contains(item.getPKeyId()))
                 .collect(Collectors.toList());
-
-        if (!leafNodes.isEmpty()) {
-            return leafNodes;
-        }
+        if(!items.isEmpty()&&items.size()>0){
+            leafNodes.addAll(items);
+        }
+        ancestorIds = leafNodes.stream()
+                .map(WbsTreeContract::getAncestorsPId)
+                .filter(Objects::nonNull)
+                .flatMap(ancestors -> Arrays.stream(ancestors.split(",")))
+                .map(String::trim)
+                .filter(id -> !id.isEmpty())
+                .map(Long::parseLong)
+                .collect(Collectors.toSet());
 
         // 3. 如果没有分项,找子分部工程 (节点类型3)
-        leafNodes = projects.stream()
-                .filter(item -> item.getNodeType() == 3)
+        Set<Long> finalAncestorIds1 = ancestorIds;
+        List<WbsTreeContract> subDivisional = projects.stream()
+                .filter(item -> item.getNodeType() == 3&& !finalAncestorIds1.contains(item.getPKeyId()))
                 .collect(Collectors.toList());
-
-        if (!leafNodes.isEmpty()) {
-            return leafNodes;
-        }
-
+        if(!subDivisional.isEmpty()&&subDivisional.size()>0){
+            leafNodes.addAll(subDivisional);
+        }
+        ancestorIds = leafNodes.stream()
+                .map(WbsTreeContract::getAncestorsPId)
+                .filter(Objects::nonNull)
+                .flatMap(ancestors -> Arrays.stream(ancestors.split(",")))
+                .map(String::trim)
+                .filter(id -> !id.isEmpty())
+                .map(Long::parseLong)
+                .collect(Collectors.toSet());
+        Set<Long> finalAncestorIds2 = ancestorIds;
         // 4. 如果没有子分部,找分部工程 (节点类型2)
-        leafNodes = projects.stream()
-                .filter(item -> item.getNodeType() == 2)
+        List<WbsTreeContract> divisional = projects.stream()
+                .filter(item -> item.getNodeType() == 2&& !finalAncestorIds2.contains(item.getPKeyId()))
                 .collect(Collectors.toList());
+        if(!divisional.isEmpty()&&divisional.size()>0){
+            leafNodes.addAll(divisional);
+        }
+        ancestorIds = leafNodes.stream()
+                .map(WbsTreeContract::getAncestorsPId)
+                .filter(Objects::nonNull)
+                .flatMap(ancestors -> Arrays.stream(ancestors.split(",")))
+                .map(String::trim)
+                .filter(id -> !id.isEmpty())
+                .map(Long::parseLong)
+                .collect(Collectors.toSet());
+        Set<Long> finalAncestorIds3 = ancestorIds;
 
-        if (!leafNodes.isEmpty()) {
-            return leafNodes;
+        // 5. 最后找单位工程 (节点类型18)
+        List<WbsTreeContract> unit = projects.stream()
+                .filter(item -> item.getNodeType() == 18&&!finalAncestorIds3.contains(item.getPKeyId()))
+                .collect(Collectors.toList());
+        if(!unit.isEmpty()&&unit.size()>0){
+            leafNodes.addAll(unit);
         }
+        return sortLeafNodes(leafNodes);
+    }
 
-        // 5. 最后找单位工程 (节点类型18)
-        return projects.stream()
-                .filter(item -> item.getNodeType() == 18)
+    // 排序方法:先按pId排序,pId相同则按sort排序(null值排在最后)
+    private List<WbsTreeContract> sortLeafNodes(List<WbsTreeContract> nodes) {
+        return nodes.stream()
+                .sorted(Comparator
+                        .comparing(WbsTreeContract::getPId)
+                        .thenComparing(
+                                Comparator.comparing(
+                                        WbsTreeContract::getSort,
+                                        Comparator.nullsLast(Comparator.naturalOrder())
+                                )
+                        )
+                )
                 .collect(Collectors.toList());
     }
 
@@ -5176,29 +5275,34 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
 
     // 合并单列并设置样式(检查是否已存在)
     private void mergeSingleColumnIfNotExists(Sheet sheet, int col, int startRow, int endRow, CellStyle style, Set<String> mergedRegions) {
-        String regionKey = col + ":" + startRow + ":" + endRow;
-
-        if (!mergedRegions.contains(regionKey)) {
-            CellRangeAddress mergedRegion = new CellRangeAddress(startRow, endRow, col, col);
-
-            // 检查是否已经存在相同的合并区域
-            boolean alreadyExists = false;
-            for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
-                CellRangeAddress existingRegion = sheet.getMergedRegion(i);
-                if (existingRegion.getFirstColumn() == col &&
-                        existingRegion.getFirstRow() == startRow &&
-                        existingRegion.getLastRow() == endRow) {
-                    alreadyExists = true;
-                    break;
+        try {
+            String regionKey = col + ":" + startRow + ":" + endRow;
+
+            if (!mergedRegions.contains(regionKey)) {
+                CellRangeAddress mergedRegion = new CellRangeAddress(startRow, endRow, col, col);
+
+                // 检查是否已经存在相同的合并区域
+                boolean alreadyExists = false;
+                for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
+                    CellRangeAddress existingRegion = sheet.getMergedRegion(i);
+                    if (existingRegion.getFirstColumn() == col &&
+                            existingRegion.getFirstRow() == startRow &&
+                            existingRegion.getLastRow() == endRow) {
+                        alreadyExists = true;
+                        break;
+                    }
                 }
-            }
 
-            if (!alreadyExists) {
-                sheet.addMergedRegion(mergedRegion);
-                setMergedRegionStyle(sheet, mergedRegion, style);
-                mergedRegions.add(regionKey);
+                if (!alreadyExists) {
+                    sheet.addMergedRegion(mergedRegion);
+                    setMergedRegionStyle(sheet, mergedRegion, style);
+                    mergedRegions.add(regionKey);
+                }
             }
+        } catch (Exception e) {
+            e.printStackTrace();
         }
+
     }
 
     // 判断是否是名称列