瀏覽代碼

Merge branch 'refs/heads/dev' into feature-lihb-bug

# Conflicts:
#	blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/EVController.java
LHB 1 月之前
父節點
當前提交
c612db80b3
共有 49 個文件被更改,包括 1441 次插入187 次删除
  1. 1 1
      blade-common/src/main/java/org/springblade/common/utils/AsyncConfigurer.java
  2. 6 3
      blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java
  3. 26 0
      blade-common/src/main/java/org/springblade/common/utils/ForestNodeMergerEx.java
  4. 31 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/dto/FindAndReplaceDto.java
  5. 12 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/trans/ArchiveTreeVo.java
  6. 1 1
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchivesAutoVO.java
  7. 19 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchivesAutoVO4.java
  8. 10 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/ArchiveFile.java
  9. 4 1
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/MessageWarningVO.java
  10. 5 4
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ArchiveTreeContract.java
  11. 3 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ArchiveTreeContractClient.java
  12. 5 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileAutoController.java
  13. 9 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileController.java
  14. 9 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveInspectionInfoController.java
  15. 40 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchivesAutoController.java
  16. 3 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.java
  17. 26 9
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml
  18. 8 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchivesAutoService.java
  19. 5 5
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveExpertConclusionServiceImpl.java
  20. 148 4
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveNameServiceImpl.java
  21. 564 28
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  22. 1 1
      blade-service/blade-business/pom.xml
  23. 8 4
      blade-service/blade-business/src/main/java/org/springblade/business/controller/MessageWarningController.java
  24. 4 1
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml
  25. 8 2
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/MetadataClassificationServiceImpl.java
  26. 2 1
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/TaskServiceImpl.java
  27. 10 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/WeatherInfoServiceImpl.java
  28. 137 51
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/ArchiveController.java
  29. 9 4
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/EVController.java
  30. 1 1
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/EVisaService.java
  31. 11 6
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVDataServiceImpl.java
  32. 32 5
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVisaServiceImpl.java
  33. 1 1
      blade-service/blade-manager/pom.xml
  34. 5 4
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ArchiveTreeContractController.java
  35. 17 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java
  36. 11 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeController.java
  37. 7 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ArchiveTreeContractImpl.java
  38. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.java
  39. 38 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.xml
  40. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IArchiveTreeContractService.java
  41. 2 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IWbsTreeService.java
  42. 2 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArTreeContractInitServiceImpl.java
  43. 80 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractServiceImpl.java
  44. 13 11
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractSyncImpl.java
  45. 2 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ExcelTabServiceImpl.java
  46. 11 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java
  47. 30 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeServiceImpl.java
  48. 10 6
      blade-service/blade-manager/src/main/java/org/springblade/manager/utils/PdfAddimgUtil.java
  49. 50 12
      blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

+ 1 - 1
blade-common/src/main/java/org/springblade/common/utils/AsyncConfigurer.java

@@ -15,7 +15,7 @@ public class AsyncConfigurer {
     /**
      * cpu 核心数量
      */
-    public static final int cpuNum = 3 ;//Runtime.getRuntime().availableProcessors();
+    public static final int cpuNum = 1 ;//Runtime.getRuntime().availableProcessors();
 
     /**
      * 线程池配置

+ 6 - 3
blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java

@@ -857,11 +857,15 @@ public class CommonUtil {
     public static byte[] compressImage3(String url) throws IOException, ImageProcessingException, MetadataException {
         try(InputStream file = CommonUtil.getOSSInputStream(url)) {
             byte[] imageData = InputStreamToBytes(file);
-            return compressImage3(imageData);
+            String extension = url.substring(url.lastIndexOf(".") + 1).toUpperCase();
+            if ("JPG".equals(extension) || "JPEG".equals(extension)) {
+                extension = "JPEG";
+            }
+            return compressImage3(imageData, extension);
         }
     }
 
-    public static byte[] compressImage3(byte[] imageData) throws ImageProcessingException, IOException, MetadataException {
+    public static byte[] compressImage3(byte[] imageData, String formatName) throws ImageProcessingException, IOException, MetadataException {
         // 读取原始图像(处理旋转问题)
         int orientation = 1;
         Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(imageData));
@@ -873,7 +877,6 @@ public class CommonUtil {
             }
         }
         // 缩放图像
-        String formatName = "JPEG";
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         long sizeLimit = 1024*1024*5; //5M 1920 ×1080
         int width = 1080;

+ 26 - 0
blade-common/src/main/java/org/springblade/common/utils/ForestNodeMergerEx.java

@@ -331,4 +331,30 @@ public class ForestNodeMergerEx {
     }
 
 
+    //芯的更新
+    public static <T extends INodeEx<T>> void InitTreeSortEx(T node, String parentTreeSort, int siblingIndex) {
+        // 计算当前节点排序值:100 + 兄弟索引
+        int currentSort = 100 + siblingIndex;
+
+        // 格式化为3位数字(100-999)
+        String sortStr = String.format("%03d", currentSort);
+
+        // 拼接树路径(无分隔符)
+        String currentTreeSort = parentTreeSort + sortStr;
+
+        // 检查是否需要更新
+        if (node.getTreeSort() == null || !node.getTreeSort().equals(currentTreeSort)) {
+            node.setFlag(1);
+            node.setTreeSort(currentTreeSort);
+        }
+
+        // 递归处理子节点
+        List<T> children = node.getChildren();
+        if (children != null && !children.isEmpty()) {
+            for (int i = 0; i < children.size(); i++) {
+                InitTreeSortEx(children.get(i), currentTreeSort, i);
+            }
+        }
+    }
+
 }

+ 31 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/dto/FindAndReplaceDto.java

@@ -0,0 +1,31 @@
+package org.springblade.archive.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode
+public class FindAndReplaceDto {
+    private String ids;
+    /**
+     * 1新增 2替换 3删除
+     */
+    private Integer type;
+    /**
+     * 查找内容
+     */
+    private String query;
+    /**
+     * 新增内容
+     */
+    private String replace;
+
+    /**
+     * 1 前   2 后
+     */
+    private Integer position;
+}

+ 12 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/trans/ArchiveTreeVo.java

@@ -43,6 +43,7 @@ public class ArchiveTreeVo {
     private String code;
     private String storageTime;
     private String secretLevel;
+    private Integer extNodeType;
 
     public String getId() {
         return id;
@@ -328,7 +329,18 @@ public class ArchiveTreeVo {
         return secretLevel;
     }
 
+
+
+
     public void setSecretLevel(String secretLevel) {
         this.secretLevel = secretLevel;
     }
+
+    public Integer getExtNodeType() {
+        return extNodeType;
+    }
+
+    public void setExtNodeType(Integer extNodeType) {
+        this.extNodeType = extNodeType;
+    }
 }

+ 1 - 1
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchivesAutoVO.java

@@ -236,7 +236,7 @@ public class ArchivesAutoVO extends ArchivesAuto {
 
 		private  String archiveId;
 
-		private Long id;
+		private String id;
 		private Long fileSize;
 		private Integer isElement;
 	}

+ 19 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchivesAutoVO4.java

@@ -0,0 +1,19 @@
+package org.springblade.archive.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springblade.archive.entity.ArchivesAuto;
+
+@Data
+@EqualsAndHashCode
+@NoArgsConstructor
+@AllArgsConstructor
+public class ArchivesAutoVO4{
+    private Long id;
+    private String fileNumber;
+    private Long nodeId;
+    private Long parentId;
+    private String fileNumberPrefix;
+}

+ 10 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/ArchiveFile.java

@@ -328,6 +328,8 @@ public class ArchiveFile extends BaseEntity {
     private String outId;
 
     private String sortNum;
+    @ApiModelProperty("是否锁定 1已锁定")
+    private Integer isLock;
 
     public void fromExternal(ArchiveFileVo vo) {
         if (vo == null) {
@@ -420,4 +422,12 @@ public class ArchiveFile extends BaseEntity {
         this.setIsDeleted(0);
 
     }
+
+    public void setFileTime(String fileTime) {
+        if (fileTime == null || fileTime.equals("null") ) {
+            this.fileTime = "";
+            return;
+        }
+        this.fileTime = fileTime.replaceAll("[^0-9]", "");
+    }
 }

+ 4 - 1
blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/MessageWarningVO.java

@@ -45,7 +45,10 @@ public class MessageWarningVO extends MessageWarning {
     @ApiModelProperty("结束时间")
     private String endTime;
 
-    @ApiModelProperty("类型")
+    @ApiModelProperty("1废除, 2驳回")
+    private String repealType;
+
+    @ApiModelProperty("1废除, 2驳回")
     private String typeValue;
 
     @ApiModelProperty("任务催办未读数量")

+ 5 - 4
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ArchiveTreeContract.java

@@ -372,10 +372,11 @@ public class ArchiveTreeContract extends BaseEntity {
         this.projectType = archiveTree.getProjectType();
         this.storageType = archiveTree.getStorageType();
         this.expDataType = archiveTree.getExpDataType();
-        this.archiveAutoType = archiveTree.getArchiveAutoType();
-        this.archiveAutoNodeId = archiveTree.getArchiveAutoNodeId();
-        this.archiveAutoGroupId = archiveTree.getArchiveAutoGroupId();
-        this.archiveAutoGroupSelect = archiveTree.getArchiveAutoGroupSelect();
+        //TODO 0625
+//        this.archiveAutoType = archiveTree.getArchiveAutoType();
+//        this.archiveAutoNodeId = archiveTree.getArchiveAutoNodeId();
+//        this.archiveAutoGroupId = archiveTree.getArchiveAutoGroupId();
+//        this.archiveAutoGroupSelect = archiveTree.getArchiveAutoGroupSelect();
         this.isUploadFileDisplayConfigurationTree = archiveTree.getIsDisplayTree();
         this.classify = archiveTree.getClassify();
         this.treeSort =  archiveTree.getTreeSort();

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

@@ -48,6 +48,9 @@ public interface ArchiveTreeContractClient {
     @PostMapping(API_PREFIX + "/getArchiveTreeContractListByIds")
     List<ArchiveTreeContract> getArchiveTreeContractListByIds(@RequestParam String ids);
 
+    @PostMapping(API_PREFIX + "/getArchiveTreeContractListByList")
+    List<ArchiveTreeContract> getArchiveTreeContractListByList(@RequestBody List<Long> ids);
+
     @PostMapping(API_PREFIX + "/getFirstNodeByTreeCode")
     ArchiveTreeContract getFirstNodeByTreeCode(@RequestParam Long projectId,@RequestParam Integer type);
 

+ 5 - 1
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileAutoController.java

@@ -339,12 +339,16 @@ ArchiveFileAutoController extends BladeController {
     @ApiOperation(value = "预览")
     public R<Object> mergePdf(@RequestParam String ids) {
         List<ArchiveFile> result = this.archiveFileClient.getArchiveFileByArchivesId(ids, "");
+        // 由于文件分解后,pdf文件会生成多个,所以需要合并
+        if(result!=null && result.size()>0){
+            result.sort(Comparator.comparing(ArchiveFile::getSort));
+        }
         List<String> pdfUrls = result.stream().map(archiveFile -> StringUtils.isNotEmpty(archiveFile.getPdfFileUrl()) ? archiveFile.getPdfFileUrl() : archiveFile.getFileUrl()).distinct().collect(Collectors.toList());
 
         //FileUtils.mergePdfPublicMethods(pdfUrls,"");
         /**
          * 案卷的只有一个文件,不合并先,需要的时候在把合并方法放出来
          * **/
-        return R.data(pdfUrls);
+        return R.data(pdfUrls.get(0));
     }
 }

+ 9 - 2
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileController.java

@@ -417,8 +417,15 @@ public class ArchiveFileController extends BladeController {
     @GetMapping("/getMetadataFile")
     @ApiOperationSupport(order = 16)
     @ApiOperation(value = "文件收集-上传文件责任者")
-    public R getMetadataFile(Long fileId) {
-        return R.data(metadataClassificationClient.getMetadataFile(fileId));
+    public R getMetadataFile(String fileId) {
+        long id;
+        try  {
+            fileId = fileId.split("_")[0];
+            id = Long.parseLong(fileId);
+        } catch (Exception e) {
+            return R.data(null);
+        }
+        return R.data(metadataClassificationClient.getMetadataFile(id));
     }
 
     /**

+ 9 - 2
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveInspectionInfoController.java

@@ -97,9 +97,16 @@ public class ArchiveInspectionInfoController {
     @GetMapping("/opinion")
     @ApiOperationSupport(order = 6)
     @ApiOperation(value = "获取抽检意见", notes = "传入fileId")
-    public R<ArchiveInspectionDTO> getOpinion(@RequestParam Long fileId) {
+    public R<ArchiveInspectionDTO> getOpinion(@RequestParam String fileId) {
         Long userId = AuthUtil.getUserId();
-        ArchiveInspectionDTO archiveInspectionDTO = archiveInspectionService.getbyFileId(fileId, userId);
+        long id;
+        try  {
+            fileId = fileId.split("_")[0];
+            id = Long.parseLong(fileId);
+        } catch (Exception e) {
+            return R.data(null);
+        }
+        ArchiveInspectionDTO archiveInspectionDTO = archiveInspectionService.getbyFileId(id, userId);
         return R.data(archiveInspectionDTO);
     }
 

+ 40 - 2
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchivesAutoController.java

@@ -34,6 +34,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 import org.apache.http.message.BasicNameValuePair;
 import org.springblade.archive.dto.ArchiveWarningDTO;
+import org.springblade.archive.dto.FindAndReplaceDto;
 import org.springblade.archive.dto.SaveApplyDTO;
 import org.springblade.archive.entity.ArchiveConclusion;
 import org.springblade.archive.entity.ExpertInspection;
@@ -63,6 +64,7 @@ import org.springblade.manager.vo.MyInspectTreeVO;
 import org.springblade.system.cache.ParamCache;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.RequestParam;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -106,6 +108,8 @@ public class ArchivesAutoController extends BladeController {
 	private ExecutorService executorService;
 	@Autowired
 	private ITraceLogService iTraceLogService;
+
+    private final JdbcTemplate jdbcTemplate;
 	/**
 	 * 详情
 	 */
@@ -152,6 +156,22 @@ public class ArchivesAutoController extends BladeController {
 		return R.data(pages);
 	}
 
+    @GetMapping("/fileNumberFlush")
+    @ApiOperationSupport(order = 4)
+    @ApiOperation(value = "档号整理——按档号排序")
+    public R fileNumberFlush(Long projectId,Long contractId,String nodeId,Integer isArchive){
+        List<ArchiveTreeContract> archiveTreeContracts = this.archiveTreeContractClient.queryAllChildByAncestors(nodeId,contractId);
+        List<String> ids = new ArrayList<>();
+        if(archiveTreeContracts != null && archiveTreeContracts.size() > 0){
+            ids=JSONArray.parseArray(JSONObject.toJSONString(archiveTreeContracts.stream().map(ArchiveTreeContract::getId).distinct().collect(Collectors.toList())), String.class);
+        }
+        ids.add(nodeId);
+        archivesAutoService.fileNumberFlush(projectId,contractId,ids,isArchive);
+        return R.success("正在重置题名中,请稍后刷新");
+    }
+
+
+
 
 
 	/**
@@ -391,13 +411,15 @@ public class ArchivesAutoController extends BladeController {
 	@ApiOperationSupport(order = 10)
 	@ApiOperation(value = "锁定", notes = "传入ids")
 	public R lock(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
-
 		List<ArchivesAuto> archivesAutos = archivesAutoService.listByIds(Func.toLongList(ids));
 		for (ArchivesAuto ar  :archivesAutos) {
 			ar.setIsLock(1);
+            String sql="update u_archive_file set is_lock=1 where archive_id="+ar.getId()+" and is_deleted=0";
+            jdbcTemplate.update(sql);
 		}
+        archivesAutoService.updateBatchById(archivesAutos);
 
-		return R.status(archivesAutoService.updateBatchById(archivesAutos));
+		return R.status(true);
 	}
 
 	/**
@@ -410,9 +432,18 @@ public class ArchivesAutoController extends BladeController {
 		List<ArchivesAuto> archivesAutos = archivesAutoService.listByIds(Func.toLongList(ids));
 		for (ArchivesAuto ar  :archivesAutos) {
 			ar.setIsLock(0);
+            String sql="update u_archive_file set is_lock=0 where archive_id="+ar.getId()+" and is_deleted=0";
+            jdbcTemplate.update(sql);
 		}
 		return R.status(archivesAutoService.updateBatchById(archivesAutos));
 	}
+    @PostMapping("/findAndReplace")
+    @ApiOperationSupport(order = 12)
+    @ApiOperation(value = "查找并替换", notes = "传入ids")
+    public R findAndReplace(@RequestBody FindAndReplaceDto dto){
+        List<ArchivesAuto> archivesAutos = archivesAutoService.listByIds(Func.toLongList(dto.getIds()));
+        return R.status(archivesAutoService.findAndReplace(archivesAutos,dto));
+    }
 
 
 	@PostMapping("/archiveAutoPercentComplete")
@@ -961,5 +992,12 @@ public class ArchivesAutoController extends BladeController {
 		return R.data(iPage);
 	}
 
+    @GetMapping("/reBuildArchiveFrontPdfs")
+    @ApiOperationSupport(order = 41)
+    @ApiModelProperty(value = "重新生成档案四要素")
+    public R reBuildArchiveFrontPdfs(String archiveIds,Long projectId){
+        return R.status(archivesAutoService.reBuildArchiveFrontPdfs(archiveIds,projectId));
+    }
+
 
 }

+ 3 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.java

@@ -226,4 +226,7 @@ public interface ArchivesAutoMapper extends BaseMapper<ArchivesAuto> {
 	Map<String, BigDecimal> getAllArchiveByContractTypeSummary(@Param("projectId") Long projectId,@Param("type") String typ);
 
 	String getArchiveFileByParentId(@Param("nodeId") String nodeId,@Param("contractId") String contractId);
+
+    List<ArchivesAutoVO4> selectAllArchiveAuto(@Param("projectId")Long projectId, @Param("contractId") Long contractId, @Param("nodeIds") List<String> nodeIds,@Param("isArchive")Integer isArchive);
+
 }

+ 26 - 9
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml

@@ -218,7 +218,7 @@
 
     <select id="approvalFile" resultType="org.springblade.archive.vo.ArchivesAutoVO$ApprovalFile">
         <if test="archiveId!=null">
-            select * from u_archive_file where archive_id = #{archiveId} and is_element = 0 order by sort
+            select * from u_archive_file where archive_id = #{archiveId} order by sort
         </if>
     </select>
 
@@ -586,6 +586,13 @@
         <if test="vo.searchType == 2 and vo.queryValue != null and vo.queryValue != ''">
             and uaf.file_name like concat('%',#{vo.queryValue},'%')
         </if>
+        <if test="vo.searchType == 2 and vo.queryList != null and vo.queryList.size() > 0">
+            and (
+            <foreach collection="vo.queryList" item="queryItem" separator=" OR ">
+                uaf.file_name like concat('%',#{queryItem},'%')
+            </foreach>
+            )
+        </if>
         <if test="vo.storageTimes != null and vo.storageTimes != ''">
             and uaa.storage_time in
             <foreach collection="vo.storageTimes" item="storageTime" open="(" separator="," close=")">
@@ -1165,7 +1172,7 @@
                uaa.id,uaa.name,uaa.page_n ,uaa.remark ,uaa.file_number,uaa.unit,uaa.start_date,uaa.end_date,
                 (select bdb.dict_value  from blade_dict_biz bdb WHERE bdb.is_sealed = 0 and bdb.code = 'storage_period' and bdb.dict_key = uaa.storage_time limit 1 ) as storageTimeValue
         from m_archive_tree_contract matc right join u_archives_auto uaa on matc.id = uaa.node_id
-        WHERE uaa.is_deleted = 0 and matc.is_deleted = 0 and uaa.is_apply = #{isApply} and matc.ancestors like concat("%",#{firstNode},"%")
+        WHERE uaa.is_deleted = 0 and matc.is_deleted = 0 and uaa.is_apply = #{isApply} and matc.ancestors like concat("%",#{firstNode},"%") and uaa.is_archive = 1
         order by uaa.tree_sort
     </select>
     <select id="getNodeArchives" resultType="org.springblade.archive.vo.ArchiveInspectVO">
@@ -1266,7 +1273,7 @@
         )
     </select>
     <select id="getRoutingInspection" resultType="org.springblade.archive.vo.ArchiveWarningVO">
-        select uaf.file_name ,uaf.file_url,0 as sourceType,uaf.node_id,
+        select uaf.file_name ,IF(uaf.file_url is null or uaf.file_url = '', uaf.pdf_file_url, uaf.file_url) as file_url,0 as sourceType,uaf.node_id,
                (select uaa.name from u_archives_auto uaa WHERE uaa.id = uaf.archive_id) as archive_name,
                (select GROUP_CONCAT(uai.opinion) from u_archive_inspection uai WHERE uai.file_id = uaf.id) as allopinion
         from u_archive_file uaf join m_archive_tree_contract atc on uaf.node_id  = atc.id
@@ -1277,7 +1284,7 @@
     <select id="getSpotCheck" resultType="org.springblade.archive.vo.ArchiveWarningVO">
         select GROUP_CONCAT(aei.opinion) as allopinion,aei.archive_name ,aei.node_id,1 as sourceType,
                (select uaf.file_name from u_archive_file uaf WHERE uaf.id = aei.file_id) as fileName,
-               (select uaf.file_url from u_archive_file uaf WHERE uaf.id = aei.file_id) as fileUrl
+               (select IF(uaf.file_url is null or uaf.file_url = '', uaf.pdf_file_url, uaf.file_url) as file_url from u_archive_file uaf WHERE uaf.id = aei.file_id) as fileUrl
         from m_archive_tree_contract atc join u_archive_expert_inspection aei on aei.node_id  = atc.id
         where atc.project_id  = #{projectId} and atc.is_deleted = 0 and aei.is_deleted = 0 and aei.is_pass = 0
           and atc.is_deleted = 0
@@ -1411,10 +1418,20 @@
            AND uaa.is_destroy = #{type}
         )a
     </select>
-
-    <select id="getArchiveFileByParentId" resultType="java.lang.String">
-        SELECT b.id from m_archive_tree a ,m_archive_tree_contract b where a.id=b.from_id and a.id=#{nodeId} and contract_id=#{contractId} and a.is_deleted=0 and b.is_deleted=0
+    <select id="selectAllArchiveAuto" resultType="org.springblade.archive.vo.ArchivesAutoVO4">
+        select uaa.id ,uaa.file_number,uaa.node_id,uatc.parent_Id,uatc.file_number_prefix from u_archives_auto uaa left join m_archive_tree_contract uatc on uaa.node_id=uatc.id where uaa.is_deleted = 0 and uaa.project_id=#{projectId}
+        <choose>
+            <when test="isArchive != null and isArchive != ''">
+                and uaa.is_archive = #{isArchive}
+            </when>
+            <otherwise>
+                and (uaa.is_archive is null or uaa.is_archive != 1)
+            </otherwise>
+        </choose>
+        and uaa.node_id in
+        <foreach collection="nodeIds" item="nodeId" open="(" separator="," close=")">
+            #{nodeId}
+        </foreach>
+        ORDER BY uaa.auto_file_sort Asc
     </select>
-
-
 </mapper>

+ 8 - 1
blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchivesAutoService.java

@@ -18,6 +18,7 @@ package org.springblade.archive.service;
 
 
 import org.springblade.archive.dto.ArchiveWarningDTO;
+import org.springblade.archive.dto.FindAndReplaceDto;
 import org.springblade.archive.dto.JiLinQueryDto;
 import org.springblade.archive.dto.SaveApplyDTO;
 import org.springblade.archive.entity.ArchiveConclusion;
@@ -88,7 +89,7 @@ public interface IArchivesAutoService extends BaseService<ArchivesAuto> {
 	//刷新某个项目的档号
 	void refreshFileNumberNoSlipt(Long projectId,Long contractId,Long nodeId,boolean bforce, Long traceId);
 
-	void test();
+	void test666();
 
     List<DictBiz> getCarrierTypeByDict();
 
@@ -156,4 +157,10 @@ public interface IArchivesAutoService extends BaseService<ArchivesAuto> {
     List<JiLinArchiveAutoDto> getArchiveAutoDtoList(Long projId, List<JiLinQueryDto>dtos);
 
 	R sendFileToEArchives(ArchiveDataVo dataInfo);
+
+    void fileNumberFlush(Long projectId, Long contractId, List<String> nodeIds,Integer isArchive);
+
+    boolean reBuildArchiveFrontPdfs(String archiveIds, Long projectId);
+
+    boolean findAndReplace(List<ArchivesAuto> archivesAutos, FindAndReplaceDto dto);
 }

+ 5 - 5
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveExpertConclusionServiceImpl.java

@@ -243,11 +243,11 @@ public class ArchiveExpertConclusionServiceImpl extends BaseServiceImpl<ArchiveE
             throw new ServiceException("专家账号信息错误,请联系管理员");
         }
         //获取所有专家个人证书
-        List<SignPfxFile> list = baseMapper.getAllSign(userIds);
-        if (list.size() == 0 || list.size() != userIds.size()){
-            throw new ServiceException("有专家没有配置电签,请联系管理员");
-        }
-        Map<Long, SignPfxFile> map = list.stream().collect(Collectors.toMap(l -> l.getCertificateUserId(), l -> l));
+//        List<SignPfxFile> list = baseMapper.getAllSign(userIds);
+//        if (list.size() == 0 || list.size() != userIds.size()){
+//            throw new ServiceException("有专家没有配置电签,请联系管理员");
+//        }
+//        Map<Long, SignPfxFile> map = list.stream().collect(Collectors.toMap(l -> l.getCertificateUserId(), l -> l));
 
         String pdfUrl = "";
         try {

+ 148 - 4
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveNameServiceImpl.java

@@ -61,12 +61,12 @@ public class ArchiveNameServiceImpl implements IArchiveNameService {
         // 4. 批量获取缺失的祖先节点
         List<ArchiveTreeContract> missingAncestors = Collections.emptyList();
         if (!missingAncestorIds.isEmpty()) {
-            String ancestorIdsStr = missingAncestorIds.stream()
-                    .map(String::valueOf)
-                    .collect(Collectors.joining(","));
+            // 直接使用Set<Long>,无需转换为字符串
+            List<Long> ancestorIdsList = new ArrayList<>(missingAncestorIds);
 
+            // 使用新方法调用
             missingAncestors = archiveTreeContractClient
-                    .getArchiveTreeContractListByIds(ancestorIdsStr);
+                    .getArchiveTreeContractListByList(ancestorIdsList);
 
             for (ArchiveTreeContract ancestor : missingAncestors) {
                 Long ancestorId = ancestor.getId();
@@ -208,6 +208,150 @@ public class ArchiveNameServiceImpl implements IArchiveNameService {
         // 1. 获取输入节点
         Map<Long, ArchiveTreeContract> allNodeMap = hierarchy.getAllNodeMap();
         List<ArchiveTreeContract> nodes = new ArrayList<>();
+        for (Long nodeId : nodeIds) {
+            ArchiveTreeContract node = allNodeMap.get(nodeId);
+            if (node != null) {
+                nodes.add(node);
+            }
+        }
+        if (nodes.isEmpty()) {
+            return "";
+        }
+
+        // 2. 按父节点分组 (一级分组)
+        Map<Long, List<ArchiveTreeContract>> parentGroups = new LinkedHashMap<>();
+        for (ArchiveTreeContract node : nodes) {
+            Long parentId = node.getParentId();
+            if (parentId == null) {
+                parentGroups.put(node.getId(), Collections.singletonList(node));
+            } else {
+                parentGroups.computeIfAbsent(parentId, k -> new ArrayList<>()).add(node);
+            }
+        }
+
+        // 3. 按祖父节点分组 (二级分组)
+        Map<Long, List<Map.Entry<Long, List<ArchiveTreeContract>>>> grandParentGroups = new LinkedHashMap<>();
+        for (Map.Entry<Long, List<ArchiveTreeContract>> entry : parentGroups.entrySet()) {
+            Long parentId = entry.getKey();
+            ArchiveTreeContract parentNode = allNodeMap.get(parentId);
+            Long grandParentId = (parentNode != null) ? parentNode.getParentId() : null;
+            grandParentGroups.computeIfAbsent(grandParentId, k -> new ArrayList<>()).add(entry);
+        }
+
+        // 4. 处理分组
+        List<String> nameParts = new ArrayList<>();
+        for (Map.Entry<Long, List<Map.Entry<Long, List<ArchiveTreeContract>>>> grandEntry : grandParentGroups.entrySet()) {
+            Long grandParentId = grandEntry.getKey();
+            List<Map.Entry<Long, List<ArchiveTreeContract>>> directParentGroups = grandEntry.getValue();
+
+            // 尝试在祖父节点级别合并
+            boolean canUseGrandParentName = false;
+            if (grandParentId != null) {
+                ArchiveTreeContract grandParentNode = allNodeMap.get(grandParentId);
+                if (grandParentNode != null) {
+                    // 获取祖父节点的所有直接子节点
+                    List<ArchiveTreeContract> grandChildren = hierarchy.getChildren(grandParentId);
+                    if (grandChildren != null && !grandChildren.isEmpty()) {
+                        // 检查是否覆盖所有直接子节点
+                        boolean allChildrenCovered = true;
+                        Set<Long> coveredParentIds = new HashSet<>();
+                        for (Map.Entry<Long, List<ArchiveTreeContract>> directEntry : directParentGroups) {
+                            coveredParentIds.add(directEntry.getKey());
+                        }
+
+                        // 验证所有子节点都被覆盖
+                        for (ArchiveTreeContract child : grandChildren) {
+                            if (!coveredParentIds.contains(child.getId())) {
+                                allChildrenCovered = false;
+                                break;
+                            }
+                        }
+
+                        // 验证每个被覆盖的子节点组是否都是全覆盖状态
+                        if (allChildrenCovered) {
+                            boolean allGroupsFullyCovered = true;
+                            for (Map.Entry<Long, List<ArchiveTreeContract>> directEntry : directParentGroups) {
+                                List<ArchiveTreeContract> groupNodes = directEntry.getValue();
+                                // 获取该父节点的所有子节点
+                                List<ArchiveTreeContract> childrenOfParent = hierarchy.getChildren(directEntry.getKey());
+                                int childrenCount = (childrenOfParent != null) ? childrenOfParent.size() : 0;
+
+                                // 检查是否全覆盖
+                                if (childrenCount == 0 || groupNodes.size() != childrenCount) {
+                                    allGroupsFullyCovered = false;
+                                    break;
+                                }
+                            }
+
+                            if (allGroupsFullyCovered) {
+                                nameParts.add(getLevelName(hierarchy, grandParentId));
+                                canUseGrandParentName = true;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (canUseGrandParentName) {
+                continue; // 已使用祖父节点名称,跳过直接父节点处理
+            }
+
+            // 处理直接父节点组
+            boolean firstParentInGroup = true;
+            for (Map.Entry<Long, List<ArchiveTreeContract>> directEntry : directParentGroups) {
+                Long groupId = directEntry.getKey();
+                List<ArchiveTreeContract> groupNodes = directEntry.getValue();
+                ArchiveTreeContract parent = allNodeMap.get(groupId);
+
+                if (parent == null) {
+                    // 无父节点的情况:添加所有节点名称(用顿号分隔)
+                    String nodeNames = groupNodes.stream()
+                            .map(ArchiveTreeContract::getNodeName)
+                            .collect(Collectors.joining("、"));
+                    nameParts.add(nodeNames);
+                    continue;
+                }
+
+                // 获取直接子节点
+                List<ArchiveTreeContract> allChildren = hierarchy.getChildren(groupId);
+                int childrenCount = (allChildren != null) ? allChildren.size() : 0;
+
+                // 检查是否全覆盖所有子节点
+                if (childrenCount > 0 && groupNodes.size() == childrenCount) {
+                    // 只有组内第一个父节点使用完整层级名称
+                    String parentName = firstParentInGroup ?
+                            getLevelName(hierarchy, groupId) :
+                            parent.getNodeName();
+
+                    nameParts.add(parentName);
+                } else {
+                    // 获取父节点名称
+                    String parentName = firstParentInGroup ?
+                            getLevelName(hierarchy, groupId) :
+                            parent.getNodeName();
+
+                    // 部分覆盖:添加父节点名称+子节点名称(用顿号分隔)
+                    String childNames = groupNodes.stream()
+                            .map(ArchiveTreeContract::getNodeName)
+                            .collect(Collectors.joining("、"));
+                    nameParts.add(parentName + childNames);
+                }
+
+                // 后续节点不再是第一个
+                firstParentInGroup = false;
+            }
+        }
+
+        // 5. 拼接最终结果
+        return String.join("、", nameParts);
+    }
+    public String generateFullLevelName1(
+            List<Long> nodeIds,
+            NodeHierarchy hierarchy
+    ) {
+        // 1. 获取输入节点
+        Map<Long, ArchiveTreeContract> allNodeMap = hierarchy.getAllNodeMap();
+        List<ArchiveTreeContract> nodes = new ArrayList<>();
 
         for (Long nodeId : nodeIds) {
             ArchiveTreeContract node = allNodeMap.get(nodeId);

+ 564 - 28
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -24,6 +24,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.itextpdf.text.*;
@@ -35,6 +36,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 
 import org.springblade.archive.dto.ArchiveWarningDTO;
+import org.springblade.archive.dto.FindAndReplaceDto;
 import org.springblade.archive.dto.JiLinQueryDto;
 import org.springblade.archive.dto.SaveApplyDTO;
 import org.springblade.archive.entity.*;
@@ -73,6 +75,9 @@ import org.springblade.system.entity.DictBiz;
 import org.springblade.system.feign.IDictBizClient;
 import org.springblade.system.user.entity.User;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springframework.transaction.annotation.Transactional;
@@ -135,6 +140,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	private final IArchiveExpertConclusionService expertConclusionService;
 	private final ITraceLogService iTraceLogService;
 
+    private final JdbcTemplate jdbcTemplate;
+
 	//表格高度
 	private static int high = 20;
 	//表格宽度
@@ -142,6 +149,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	private final CommonFileClient commonFileClient;
 
+	private final IArchiveNameService archiveNameService;
+
 
 	@Override
 	public IPage<ArchivesAutoVO> selectArchivesAutoPage(IPage<ArchivesAutoVO> page, ArchivesAutoVO archivesAuto) {
@@ -373,11 +382,147 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		if (StringUtils.isNotBlank(vo.getSecretLevel())) {
 			vo.setSecretLevelValue("1".equals(vo.getSecretLevel()) ? "机密" : ("2".equals(vo.getSecretLevel()) ? "绝密" : "秘密"));
 		}
-		if (vo.getApprovalFileList() != null && vo.getApprovalFileList().size() > 0) {
-			vo.setPageNumber(vo.getApprovalFileList().size());
+		List<ArchivesAutoVO.ApprovalFile> approvalFiles = vo.getApprovalFileList();
+		ArchivesAutoVO.ApprovalFile front = null;
+		ArchivesAutoVO.ApprovalFile cataLog = null;
+		ArchivesAutoVO.ApprovalFile spare = null;
+		ArchivesAutoVO.ApprovalFile back = null;
+		if (approvalFiles != null && !approvalFiles.isEmpty()) {
+			// todo 四要素 使用名称判断
+			Map<String, List<ArchivesAutoVO.ApprovalFile>> collect = approvalFiles.stream().collect(Collectors.groupingBy(approvalFile -> {
+				if (approvalFile != null &&
+						(approvalFile.getFileName().equals("封面") || approvalFile.getFileName().equals("卷内目录") || approvalFile.getFileName().equals("卷内备考表") || approvalFile.getFileName().equals("备考表") || approvalFile.getFileName().equals("背脊"))) {
+					if (approvalFile.getFileName().equals("卷内备考表")) {
+						return "备考表";
+					}
+					return approvalFile.getFileName();
+				}
+				return "0";
+			}, Collectors.toList()));
+			List<ArchivesAutoVO.ApprovalFile> approvalFiles1 = collect.get("0");
+			if (approvalFiles1 == null) {
+				approvalFiles1 = new ArrayList<>();
+			}
+			vo.setPageNumber(approvalFiles1.size());
+			vo.setApprovalFileList(approvalFiles1);
+			if (collect.containsKey("封面")) {
+				front = collect.get("封面").stream().max(Comparator.comparing(ArchivesAutoVO.ApprovalFile::getId)).orElse(null);
+			}
+			if (collect.containsKey("卷内目录")) {
+				cataLog = collect.get("卷内目录").stream().max(Comparator.comparing(ArchivesAutoVO.ApprovalFile::getId)).orElse(null);
+			}
+			if (collect.containsKey("备考表")) {
+				spare = collect.get("备考表").stream().max(Comparator.comparing(ArchivesAutoVO.ApprovalFile::getId)).orElse(null);
+			}
+			if (collect.containsKey("背脊")) {
+				back = collect.get("背脊").stream().max(Comparator.comparing(ArchivesAutoVO.ApprovalFile::getId)).orElse(null);
+			}
 		} else {
 			vo.setPageNumber(0);
 		}
+		String outUrl = vo.getOutUrl();
+		if (StringUtils.isNotBlank(outUrl)) {
+			// 根据 factorType 字符串生成档案号码字符串链表
+			ArchiveProjectConfig config = archiveProjectConfigService.getByProjectIdOrNew(vo.getProjectId());
+			String[] frontUrls = outUrl.split(",");
+			for (String frontUrl : frontUrls) {
+				// 封面(原r_Archives_front)增加中文"封面"匹配
+				if (front == null && (frontUrl.contains(ArchiveAutoPdfServiceImpl.ARCHIVE_NUMBER[0])
+						|| frontUrl.contains("封面"))
+						&& config.getFactorType().contains("1")) {
+					front = new ArchivesAutoVO.ApprovalFile();
+					front.setFileName("封面");
+					if(frontUrl.contains("@@@")){
+						front.setFileUrl(frontUrl.substring(0,frontUrl.indexOf("@@@")));
+					}else {
+						front.setFileUrl(frontUrl);
+					}
+				}
+				// 卷内目录(原r_Archives_catalog)增加中文匹配
+				else if (cataLog == null && (frontUrl.contains(ArchiveAutoPdfServiceImpl.ARCHIVE_NUMBER[1])
+						|| frontUrl.contains("卷内目录"))
+						&& config.getFactorType().contains("2")) {
+					cataLog = new ArchivesAutoVO.ApprovalFile();
+					cataLog.setFileName("卷内目录");
+					if(frontUrl.contains("@@@")){
+						cataLog.setFileUrl(frontUrl.substring(0,frontUrl.indexOf("@@@")));
+					}else {
+						cataLog.setFileUrl(frontUrl);
+					}
+				}
+				// 备考表(原r_Archives_spare)增加中文匹配
+				else if (spare == null && (frontUrl.contains(ArchiveAutoPdfServiceImpl.ARCHIVE_NUMBER[2])
+						|| frontUrl.contains("备考表"))
+						&& config.getFactorType().contains("3")) {
+					spare = new ArchivesAutoVO.ApprovalFile();
+					spare.setFileName("备考表");
+					if(frontUrl.contains("@@@")){
+						spare.setFileUrl(frontUrl.substring(0,frontUrl.indexOf("@@@")));
+					}else {
+						spare.setFileUrl(frontUrl);
+					}
+				}
+				// 背脊(原r_Archives_back)增加中文匹配
+				else if (back == null && (frontUrl.contains(ArchiveAutoPdfServiceImpl.ARCHIVE_NUMBER[3])
+						|| frontUrl.contains("背脊"))
+						&& config.getFactorType().contains("4")) {
+					back = new ArchivesAutoVO.ApprovalFile();
+					back.setFileName("背脊");
+					if(frontUrl.contains("@@@")){
+						back.setFileUrl(frontUrl.substring(0,frontUrl.indexOf("@@@")));
+					}else {
+						back.setFileUrl(frontUrl);
+					}
+				}
+			}
+		}
+		List<ArchivesAutoVO.ApprovalFile> files = vo.getApprovalFileList();;
+		if (files != null) {
+			String tempId = null;
+			if (!files.isEmpty()) {
+				tempId = files.get(0).getId();
+			}
+			if (front != null && front.getFileUrl() != null) {
+				if (front.getId() == null && tempId != null) {
+					front.setId(tempId + "_1");
+				}
+				if (front.getId() != null) {
+					front.setPdfFileUrl(front.getFileUrl());
+					files.add(0,front);
+				}
+			}
+			if (cataLog != null && cataLog.getFileUrl() != null) {
+				if (cataLog.getId() == null && tempId != null) {
+					cataLog.setId(tempId  + "_2");
+				}
+				if (cataLog.getId() != null) {
+					cataLog.setPdfFileUrl(cataLog.getFileUrl());
+					if (front != null && front.getFileUrl() != null) {
+						files.add(1,cataLog);
+					} else {
+						files.add(0,cataLog);
+					}
+				}
+			}
+			if (spare != null && spare.getFileUrl() != null) {
+				if (spare.getId() == null && tempId != null) {
+					spare.setId(tempId  + "_3");
+				}
+				if (spare.getId() != null) {
+					spare.setPdfFileUrl(spare.getFileUrl());
+					files.add(spare);
+				}
+			}
+			if (back != null && back.getFileUrl() != null) {
+				if (back.getId() == null && tempId != null) {
+					back.setId(tempId  + "_4");
+				}
+				if (back.getId() != null) {
+					back.setPdfFileUrl(back.getFileUrl());
+					files.add(back);
+				}
+			}
+		}
 		return vo;
 	}
 
@@ -1150,6 +1295,83 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 	}
 
+
+	private String builtArchiveName_new(List<ArchiveFile> waitArchiveFiles, ArchiveTreeContract node, boolean isCrossNode
+	,IArchiveNameService.NodeHierarchy nameInfo) {
+
+		String archiveName = "";
+
+		Long projectId = node.getProjectId();
+		ProjectInfo projectInfo = projectClient.getById(String.valueOf(projectId));
+		String projectName = projectInfo.getProjectName();
+		String contractName = "";
+		Long contractId = node.getContractId();
+		if (contractId != null && contractId != -1) {
+			ContractInfo contract = contractClient.getContractById(contractId);
+			contractName = contract.getContractName();
+		}
+		//获取案卷题名
+		archiveName = projectName;
+		if (StringUtils.isNotEmpty(contractName)) {
+			archiveName = archiveName + contractName;
+		}
+
+
+		//--正常节点
+		Set<String> uniqueNodeIds = waitArchiveFiles.stream()
+				.map(ArchiveFile::getNodeId)
+				.filter(nodeId -> nodeId != null && !nodeId.isEmpty())
+				.collect(Collectors.toCollection(LinkedHashSet::new));
+
+		// 2. 将节点ID从String转换为Long(保持原始顺序)
+		List<Long> nodeIds = uniqueNodeIds.stream()
+				.map(this::safeParseLong)
+				.filter(Objects::nonNull)
+				.collect(Collectors.toList());
+
+
+		String fullPath =  archiveNameService.generateFullLevelName(nodeIds, nameInfo);
+
+		archiveName+=fullPath;
+
+//		if (archiveName.length() > 200) {
+//			// 从150位置开始查找第一个顿号
+//			int index = archiveName.indexOf('、', 200);
+//			if (index != -1) {
+//				// 找到顿号,截取到顿号位置(去掉顿号)并拼接“等文件”
+//				archiveName = archiveName.substring(0, index) + "等文件";
+//			} else {
+//				// 没有找到顿号,直接截取150个字符并拼接“等文件”
+//				archiveName = archiveName.substring(0, 200) + "等文件";
+//			}
+//		}
+
+		if (archiveName.length() > 1100) {
+			// 直接截取前1100个字符
+			archiveName = archiveName.substring(0, 1100);
+		}
+
+
+		//TODO wbs节点
+		//不存在跨节点 项目名称+案卷题名规则(在后台归档目录树设置的)+后缀
+		//存在跨节点  获取当前所有节点的父级节点题名规则+所有同层级跨节点并卷的节点名称拼接+后缀
+		String archiveNameSuffix = node.getArchiveNameSuffix();
+		if (archiveNameSuffix == null || archiveNameSuffix.equals("null")) {
+			archiveNameSuffix = "";
+		}
+		return archiveName + archiveNameSuffix;
+	}
+
+	// 安全解析Long的方法(处理格式异常)
+	private Long safeParseLong(String value) {
+		try {
+			return Long.parseLong(value);
+		} catch (NumberFormatException e) {
+			log.warn("无法转换的节点ID格式: {}", value);
+			return null;
+		}
+	}
+
 	private String builtArchiveName(List<ArchiveFile> waitArchiveFiles, ArchiveTreeContract node, boolean isCrossNode) {
 
 		String archiveName = "";
@@ -1269,20 +1491,68 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 	}
 
+	/**
+	 * 单独组卷规则组卷
+	 *
+	 * @param waitArchiveFiles
+	 * @param node             规格参数所在节点
+	 * @param pageN
+	 */
+	private void createArchive3_new(List<ArchiveFile> waitArchiveFiles, ArchiveTreeContract node, int pageN,IArchiveNameService.NodeHierarchy nameInfo) {
+
+		String archiveStartDateAndEndDate = getArchiveStartDateAndEndDate(waitArchiveFiles);
+		String[] split = archiveStartDateAndEndDate.split(",");
+		String startDate = split.length >= 1 ? split[0] : "";
+		String endDate = split.length >= 2 ? split[1] : "";
+		int fileN = waitArchiveFiles.size();
+		if (fileN == 0) {
+			return;
+		}
+
+		String archiveName = builtArchiveName_new(waitArchiveFiles, node, false,nameInfo);//获取案卷题名
+		//1.创建新案卷
+		ArchivesAuto archivesAuto = builtArchives(node, pageN, fileN, startDate, endDate, archiveName);
+		//2.设置文件所属案卷,组卷状态
+		Long archivesAutoId = archivesAuto.getId();
+
+		//封面和生成文件页码
+		archiveAutoPdfService.buildArchiveFrontPdfs(archivesAuto.getProjectId(), archivesAuto, waitArchiveFiles, false);
+
+		builtFilePageNo(archivesAuto, waitArchiveFiles);//生成文件页码
+
+
+		for (ArchiveFile file : waitArchiveFiles) {
+			file.setArchiveId(archivesAutoId);//设置文件所属案卷
+			file.setIsArchive(1);
+
+		}
+		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
+		try {
+//			for (ArchiveFile saveVo : waitArchiveFiles) {
+//				metadataClassificationClient.createMetadataFile(saveVo.getId(), 0);
+//			}
+			batchCreateMetadataFiles(waitArchiveFiles);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+
+
 	/**
 	 * 分类并卷组卷
 	 *
 	 * @param waitArchiveFiles
 	 * @param archiveAutoGroupId 分类并卷分组ID
 	 */
-	private void createArchive2(List<ArchiveFile> waitArchiveFiles, Long archiveAutoGroupId, Long projectId) {
+	private void createArchive2(List<ArchiveFile> waitArchiveFiles, Long archiveAutoGroupId, Long projectId,IArchiveNameService.NodeHierarchy nameInfo) {
 
 		//获取同一分类archiveAutoGroupId下设置的(设置规则时选中的)节点,排好序
-		List<ArchiveTreeContract> selectList = archiveTreeContractClient.getStorageNodeByGroupId(projectId, archiveAutoGroupId);
-		//分类并卷节点默认采用同类型下第一个存储节点为归属节点
-		if (selectList== null || selectList.size() == 0) {
-			return;
-		}
+//		List<ArchiveTreeContract> selectList = archiveTreeContractClient.getStorageNodeByGroupId(projectId, archiveAutoGroupId);
+//		//分类并卷节点默认采用同类型下第一个存储节点为归属节点
+//		if (selectList== null || selectList.size() == 0) {
+//			return;
+//		}
 
 		if (waitArchiveFiles==null || waitArchiveFiles.size()== 0) {
 			return;
@@ -1290,7 +1560,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 		String strfirstNodeId = waitArchiveFiles.get(0).getNodeId();
 
-		ArchiveTreeContract node = selectList.get(0);
+		ArchiveTreeContract node = null;
 		if (StringUtils.isNotEmpty(strfirstNodeId)) {
 			Long firstNodeId = Long.parseLong(strfirstNodeId);
 			ArchiveTreeContract firstNode = archiveTreeContractClient.getArchiveTreeContractById(firstNodeId);
@@ -1317,7 +1587,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 		//默认组卷存在跨节点组卷  注意案卷归属节点,案卷命名方式
 		//获取案卷题名
-		String archiveName = builtArchiveName(waitArchiveFiles, node, true);//获取案卷题名
+		String archiveName = builtArchiveName_new(waitArchiveFiles, node, true,nameInfo);//获取案卷题名
 
 		//1.创建新案卷
 		ArchivesAuto archivesAuto = builtArchives(node, pageN, fileN, startDate, endDate, archiveName);
@@ -1546,6 +1816,10 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	private void archiveAutoMethod3(List<ArchiveTreeContract> list, Map<String, List<ArchiveFile>> boxMap,
 									Map<Long, String> boxFileMap, Long traceId) {
+
+		// 4. 构建节点层级关系
+		IArchiveNameService.NodeHierarchy nameInfo =
+				archiveNameService.buildNodeHierarchy(list);
 		// 步骤1:遍历节点集合
 		for (ArchiveTreeContract node : list) {
 			// 步骤2:获取当前节点的案卷规格
@@ -1581,7 +1855,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 					// 如果是最后一个文件且有待组卷文件,则组卷
 					if (archiveFilesSize == archiveFiles.size() && !waitArchiveFiles.isEmpty()) {
-						createArchive3(waitArchiveFiles, node, archivesSize);
+						createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo);
 						waitArchiveFiles.clear();
 						archivesSize = 0;
 					}
@@ -1600,7 +1874,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 						// 最后一个文件直接组卷
 						if (archiveFilesSize == archiveFiles.size()) {
-							createArchive3(waitArchiveFiles, node, archivesSize);
+							createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo);
 							waitArchiveFiles.clear();
 							archivesSize = 0;
 						}
@@ -1609,7 +1883,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 					else if (checkStatus == 1) {
 						waitArchiveFiles.add(file);
 						archivesSize = tempTotalSize;
-						createArchive3(waitArchiveFiles, node, archivesSize);
+						createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo);
 
 						// 重置待组卷集合
 						waitArchiveFiles.clear();
@@ -1619,7 +1893,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 					else if (checkStatus == -1) {
 						if (!waitArchiveFiles.isEmpty()) {
 							// 先将现有集合组卷(不含当前文件)
-							createArchive3(waitArchiveFiles, node, archivesSize);
+							createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo);
 
 							// 新建集合存放当前文件
 							waitArchiveFiles.clear();
@@ -1628,7 +1902,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 							// 最后一个文件直接组卷
 							if (archiveFilesSize == archiveFiles.size()) {
-								createArchive3(waitArchiveFiles, node, archivesSize);
+								createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo);
 								waitArchiveFiles.clear();
 								archivesSize = 0;
 							}
@@ -1636,7 +1910,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 							// 当前文件单独成卷
 							waitArchiveFiles.add(file);
 							archivesSize = filePage;
-							createArchive3(waitArchiveFiles, node, archivesSize);
+							createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo);
 
 							// 重置集合
 							waitArchiveFiles.clear();
@@ -1750,15 +2024,19 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 			//一个archiveAutoGroupId组成一个案卷  案卷归属同个key的归档树节点select=1的第一个groupId2NodeIdMap
 			//createArchive2(archiveFiles, archiveAutoGroupId, projectId);
 
-			archiveAutoMethodGroup(archiveFiles, archiveAutoGroupId, projectId);
+			archiveAutoMethodGroup(archiveFiles, archiveAutoGroupId, projectId,null);
 		}
 
 	}
 
-
 	private void archiveAutoMethod2_new(List<ArchiveTreeContract> list, List<ArchiveTreeContract> topList, Long projectId,
 										Map<String, List<ArchiveFile>> boxMap, Map<Long, String> boxFileMap, Long traceId) {
 
+		// 构建TOP节点ID集合(提前计算提高效率)
+		Set<Long> topIdSet = topList.stream()
+				.map(ArchiveTreeContract::getId)
+				.collect(Collectors.toSet());
+
 		// 分类并卷集合<groupId, List<文件>>
 		Map<Long, List<ArchiveFile>> archiveMap = new LinkedHashMap<>();
 		// 记录同个分组id对应的第一个节点ID(案卷归属节点)
@@ -1769,6 +2047,114 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		// 当前顶层节点对应的分组ID
 		Long currArchiveAutoGroupId = null;
 
+		// 4. 构建节点层级关系
+		IArchiveNameService.NodeHierarchy nameInfo =
+				archiveNameService.buildNodeHierarchy(list);
+
+		// 步骤1:遍历节点集合
+		for (ArchiveTreeContract node : list) {
+			// 初始化当前节点的分组ID和顶层节点ID
+			Long archiveAutoGroupId = node.getArchiveAutoGroupId();
+			Long nodeTopId = 0L;
+
+			String ancestors = node.getAncestors();
+			if (ancestors != null && !ancestors.trim().isEmpty()) {
+				// 分割祖先字符串并转换为Long列表
+				List<Long> ancestorIds = Arrays.stream(ancestors.split(","))
+						.map(String::trim)
+						.filter(s -> !s.isEmpty())
+						.map(s -> {
+							try {
+								return Long.parseLong(s);
+							} catch (NumberFormatException e) {
+								return null;
+							}
+						})
+						.filter(Objects::nonNull)
+						.collect(Collectors.toList());
+
+				// 从上到下遍历:根节点(开始) → 最近祖先(末尾)
+				// 查找最后一个匹配的TOP节点(离当前节点最近的)
+				for (int i = ancestorIds.size() - 1; i >= 0; i--) {
+					Long ancestorId = ancestorIds.get(i);
+					if (topIdSet.contains(ancestorId)) {
+						nodeTopId = ancestorId;
+						break;
+					}
+				}
+			}
+			// =========== 关键修改结束 ===========
+
+			// 步骤3:确定当前节点的分组ID
+			if (curTopId != null && curTopId.equals(nodeTopId) && nodeTopId != 0) {
+				// 如果属于同一个非零顶层节点,使用之前的分组ID
+				archiveAutoGroupId = currArchiveAutoGroupId;
+			} else {
+				// 新顶层节点或没有顶层节点,更新当前分组信息
+				curTopId = nodeTopId;
+				currArchiveAutoGroupId = archiveAutoGroupId;
+			}
+
+			// 步骤4:查询节点下的未归档文件(保持原逻辑)
+			List<ArchiveFile> archiveFiles = archiveFileClient.getListByNodeID(node.getId().toString());
+
+			if (archiveFiles != null && !archiveFiles.isEmpty()) {
+				// 记录日志(每个节点只记录一次)
+				String completeMsg = "[自动组卷] 分类组卷:" + "-traceId:" + traceId + "节点:" + node.getNodeName() + " 文件数量:" + archiveFiles.size();
+				iTraceLogService.saveLog(traceId, completeMsg);
+
+				// 步骤5:遍历未归档文件
+				for (ArchiveFile file : archiveFiles) {
+					// 步骤6:判断文件是否存在分盒设置
+					if (file.getBoxNumber() != null && file.getBoxNumber() != -1) {
+						// 添加到分盒文件集合
+						addBoxMap(file, boxMap, boxFileMap);
+					} else {
+						// 分类并卷流程
+						// 步骤7:将文件按照<groupId,List<文件>>放入集合
+						//改用top分组了
+						archiveAutoGroupId = curTopId;
+						if (archiveMap.containsKey(archiveAutoGroupId)) {
+							List<ArchiveFile> groupList = archiveMap.get(archiveAutoGroupId);
+							groupList.add(file);
+						} else {
+							List<ArchiveFile> groupList = new ArrayList<>();
+							groupList.add(file);
+							archiveMap.put(archiveAutoGroupId, groupList);
+							groupId2NodeIdMap.put(archiveAutoGroupId, node.getId());
+						}
+					}
+				}
+			}
+		}
+
+		// 步骤8:按集合创建案卷(保持原逻辑)
+		for (Map.Entry<Long, List<ArchiveFile>> entry : archiveMap.entrySet()) {
+			Long archiveAutoGroupId = entry.getKey();
+			List<ArchiveFile> archiveFiles = entry.getValue();
+
+			// 同一个分组ID下的文件分组组卷
+			archiveAutoMethodGroup(archiveFiles, archiveAutoGroupId, projectId,nameInfo);
+		}
+	}
+
+	private void archiveAutoMethod2_new1(List<ArchiveTreeContract> list, List<ArchiveTreeContract> topList, Long projectId,
+										Map<String, List<ArchiveFile>> boxMap, Map<Long, String> boxFileMap, Long traceId) {
+
+		// 分类并卷集合<groupId, List<文件>>
+		Map<Long, List<ArchiveFile>> archiveMap = new LinkedHashMap<>();
+		// 记录同个分组id对应的第一个节点ID(案卷归属节点)
+		Map<Long, Long> groupId2NodeIdMap = new HashMap<>();
+
+		// 当前处理的顶层节点ID
+		Long curTopId = null;
+		// 当前顶层节点对应的分组ID
+		Long currArchiveAutoGroupId = null;
+
+		// 4. 构建节点层级关系
+		IArchiveNameService.NodeHierarchy nameInfo =
+				archiveNameService.buildNodeHierarchy(list);
+
 		// 步骤1:遍历节点集合
 		for (ArchiveTreeContract node : list) {
 			// 初始化当前节点的分组ID和顶层节点ID
@@ -1833,11 +2219,15 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 			List<ArchiveFile> archiveFiles = entry.getValue();
 
 			// 同一个分组ID下的文件分组组卷
-			archiveAutoMethodGroup(archiveFiles, archiveAutoGroupId, projectId);
+			archiveAutoMethodGroup(archiveFiles, archiveAutoGroupId, projectId,nameInfo);
 		}
 	}
 
-	private void archiveAutoMethodGroup(List<ArchiveFile> archiveFiles, Long archiveAutoGroupId, Long projectId) {
+
+
+
+
+	private void archiveAutoMethodGroup(List<ArchiveFile> archiveFiles, Long archiveAutoGroupId, Long projectId,IArchiveNameService.NodeHierarchy nameInfo) {
 		if (archiveFiles.size()==0) {
 			return;
 		}
@@ -1885,13 +2275,13 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 					waitArchiveFiles.add(file);
 					archivesSize = tempTotalSize;
 					if (fileIndex == totalFiles) { // 是最后一个文件
-						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo);
 					}
 					break;
 
 				case 1: // 达到规格
 					waitArchiveFiles.add(file);
-					createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+					createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo);
 					waitArchiveFiles = new ArrayList<>();
 					archivesSize = 0;
 					break;
@@ -1899,10 +2289,10 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 				case -1: // 超出规格
 					if (waitArchiveFiles.isEmpty()) {
 						// 当前文件单独成卷
-						createArchive2(Collections.singletonList(file), archiveAutoGroupId, projectId);
+						createArchive2(Collections.singletonList(file), archiveAutoGroupId, projectId,nameInfo);
 					} else {
 						// 先将现有文件组卷
-						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo);
 
 						// 创建新的待组卷集合
 						waitArchiveFiles = new ArrayList<>();
@@ -1910,7 +2300,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 						archivesSize = filePage;
 
 						if (fileIndex == totalFiles) { // 是最后一个文件
-							createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+							createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo);
 						}
 					}
 					break;
@@ -2775,7 +3165,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	}
 
 
-	public void test() {
+	public void test666() {
 		Long projectId = 0L;
 		List<ArchiveTreeContract> archiveTreeContracts = archiveTreeContractClient.getListByProjectId(projectId);
 
@@ -4024,8 +4414,154 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		return list;
 	}
 
-
-	public void deleteFile(String defaultDir,Long id){
+    @Override
+    @Async
+    public void fileNumberFlush(Long projectId, Long contractId, List<String> nodeIds,Integer isArchive) {
+        ArchiveProjectConfig config = archiveProjectConfigService.getByProjectIdOrNew(projectId);
+        List<ArchivesAutoVO4>list=baseMapper.selectAllArchiveAuto(projectId,contractId,nodeIds,isArchive);
+        if(list!=null && list.size()>0){
+            if(config.getDirType()==null||config.getDirType()==0){
+                List<ArchivesAuto> archivesAutos = setFileNumberByConfig(config, list);
+                this.updateBatchById(archivesAutos);
+            }else {
+                Map<Long, List<ArchivesAutoVO4>> map = list.stream().collect(Collectors.groupingBy(ArchivesAutoVO4::getParentId));
+                for (Map.Entry<Long, List<ArchivesAutoVO4>> entry : map.entrySet()) {
+                    List<ArchivesAutoVO4> value = entry.getValue();
+                    List<ArchivesAuto> archivesAutos = setFileNumberByConfig(config, value);
+                   this.updateBatchById(archivesAutos);
+                }
+            }
+        }
+
+    }
+
+    @Override
+    public boolean reBuildArchiveFrontPdfs(String archiveIds, Long projectId) {
+        if(StringUtils.isEmpty(archiveIds)){
+            return false;
+        }
+        archiveAutoPdfService.assignArchiveTableUrl();
+        String[] ids = archiveIds.split(",");
+        for (String archiveId : ids) {
+            ArchivesAuto auto = baseMapper.selectById(archiveId);
+            String sql="select * from u_archive_file where archive_id = "+archiveId+" and is_deleted = 0 order by sort,sort_num,create_time";
+            //List<ArchiveFile> archiveFiles = archiveFileClient.getArchiveFileByArchiveID(Long.valueOf(archiveId));
+            List<ArchiveFile> archiveFiles= jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(ArchiveFile.class));
+            archiveAutoPdfService.buildArchiveFrontPdfs(projectId,auto,archiveFiles,true);
+            baseMapper.updateById(auto);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean findAndReplace(List<ArchivesAuto> archivesAutos, FindAndReplaceDto dto) {
+        List<ArchivesAuto>updates = new ArrayList<>();
+        for (ArchivesAuto archivesAuto : archivesAutos) {
+            ArchivesAuto auto = new ArchivesAuto();
+            if(dto.getType()==1){
+               if(StringUtils.isNotEmpty(dto.getQuery())&&StringUtils.isNotEmpty(dto.getReplace())){
+                   String name = archivesAuto.getName();
+                   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));
+                        // 更新名称
+                       archivesAuto.setName(sb.toString());
+                       auto.setId(archivesAuto.getId());
+                       auto.setName(archivesAuto.getName());
+                       updates.add(auto);
+                   }
+               }
+            } else if (dto.getType()==2) {
+                if(StringUtils.isNotEmpty(dto.getQuery())&&StringUtils.isNotEmpty(dto.getReplace())){
+                    String name = archivesAuto.getName();
+                    if (StringUtils.isNotEmpty(name)&&name.contains(dto.getQuery())) {
+                        String newName = name.replaceAll(dto.getQuery(), dto.getReplace());
+                        archivesAuto.setName(newName);
+                        auto.setId(archivesAuto.getId());
+                        auto.setName(archivesAuto.getName());
+                        updates.add(auto);
+                    }
+                }
+            }else {
+                if(StringUtils.isNotEmpty(dto.getQuery())){
+                    String name = archivesAuto.getName();
+                    if (StringUtils.isNotEmpty(name)&&name.contains(dto.getQuery())) {
+                        String newName = name.replaceAll(dto.getQuery(), "");
+                        archivesAuto.setName(newName);
+                        auto.setId(archivesAuto.getId());
+                        auto.setName(archivesAuto.getName());
+                        updates.add(auto);
+                    }
+                }
+            }
+        }
+        if(!updates.isEmpty()){
+            return this.updateBatchById(archivesAutos);
+        }
+        return true;
+    }
+
+    public List<ArchivesAuto> setFileNumberByConfig(ArchiveProjectConfig config,List<ArchivesAutoVO4> value){
+        int i=1;
+        List<ArchivesAuto>list=new ArrayList<>();
+        for (ArchivesAutoVO4 v:value) {
+            ArchivesAuto auto = new ArchivesAuto();
+            if(config.getIndexType()==null||config.getIndexType()==0){
+               v.setFileNumber(v.getFileNumberPrefix()+"_"+i);
+           }else {
+               String prefix = v.getFileNumberPrefix();
+               int index = i; // 编号从 1 开始
+               // 获取配置中的编号长度(最多多少位)
+               int numLength = config.getIndexNum();
+               // 默认最多4位,防止过长或无效值
+               if (numLength > 10) { // 限制最大为10位
+                   numLength = 4;
+               }
+               // 使用 %0xd 格式化数字,x 表示总长度
+               String formattedIndex = String.format("%0" + numLength + "d", index);
+               // 设置最终档号
+               v.setFileNumber(prefix + "_" + formattedIndex);
+           }
+            auto.setId(v.getId());
+            auto.setFileNumber(v.getFileNumber());
+            list.add(auto);
+            i++;
+        }
+        return list;
+    }
+
+
+    public void deleteFile(String defaultDir,Long id){
 		String dir = defaultDir+"/"+id;
 		String file = defaultDir+"/"+id+".zip";
 		// 多条命令执行

+ 1 - 1
blade-service/blade-business/pom.xml

@@ -194,7 +194,7 @@
                     <target>${java.version}</target>
                     <encoding>${project.build.sourceEncoding}</encoding>
                     <compilerArguments>
-                        <bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar;${java.home}/lib/jsse.jar
+                        <bootclasspath>${java.home}/lib/rt.jar:${java.home}/lib/jce.jar:${java.home}/lib/jsse.jar
                         </bootclasspath>
                     </compilerArguments>
                 </configuration>

+ 8 - 4
blade-service/blade-business/src/main/java/org/springblade/business/controller/MessageWarningController.java

@@ -155,7 +155,8 @@ public class MessageWarningController extends BladeController {
     @ApiOperationSupport(order = 4)
     @ApiOperation(value = "分页", notes = "传入operationWarning")
     public R<IPage<MessageWarningVO>> list(MessageWarningVO vo, Query query) {
-
+        String content = vo.getContent();
+        vo.setContent(null);
         QueryWrapper<MessageWarning> wrapper = Condition.getQueryWrapper(vo);
         //获取当前人的数据
         wrapper.lambda().eq(MessageWarning::getPushUser, AuthUtil.getUserId().toString());
@@ -166,13 +167,16 @@ public class MessageWarningController extends BladeController {
         if (vo.getSmsType() != null && vo.getSmsType() > -1) {
             wrapper.lambda().eq(MessageWarning::getIsRead, new Integer("2").equals(vo.getSmsType()) ? 0 : 1);
         }
-        if (vo.getTypeValue() != null && !vo.getTypeValue().isEmpty()) {
-            if (vo.getTypeValue().equals("1")) {
+        if (vo.getRepealType() != null && !vo.getRepealType().isEmpty()) {
+            if (vo.getRepealType().equals("1")) {
                 wrapper.lambda().like(MessageWarning::getContent, "废除了");
             } else {
                 wrapper.lambda().like(MessageWarning::getContent, "驳回了");
             }
         }
+        if (content != null && !content.isEmpty()) {
+            wrapper.lambda().like(MessageWarning::getContent, content);
+        }
         //设置合同段ID
         wrapper.lambda().eq(MessageWarning::getProjectId, vo.getProjectId()).eq(MessageWarning::getContractId, vo.getContractId());
 
@@ -197,7 +201,7 @@ public class MessageWarningController extends BladeController {
                 if (dictBizs != null && dictBizs.size() > 0) {
                     for (DictBiz biz : dictBizs) {
                         if (biz.getDictKey().equals(reVO.getType().toString())) {
-                            reVO.setTypeValue(biz.getDictValue());
+                            reVO.setRepealType(biz.getDictValue());
                             break;
                         }
                     }

+ 4 - 1
blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml

@@ -90,6 +90,9 @@
         <if test="vo.rectification != null and vo.rectification != ''">
             and u.rectification = #{vo.rectification}
         </if>
+        <if test="vo.sourceType != null and vo.sourceType != ''">
+            and u.source_type = #{vo.sourceType}
+        </if>
         <if test="vo.rectification == null and vo.archiveId == null">
             and (u.is_auto_file is null or u.is_auto_file != 1)
         </if>
@@ -161,7 +164,7 @@
             and u.rectification = #{vo.rectification}
         </if>
         <if test="vo.sourceType != null and vo.sourceType != ''">
-            and u.source_type = #{sourceType}
+            and u.source_type = #{vo.sourceType}
         </if>
 
         <if test="vo.rectification == null and vo.archiveId == null">

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

@@ -711,8 +711,14 @@ public class MetadataClassificationServiceImpl
             if (!uniqueNodeIds.isEmpty()) {
                 try {
                     // 1. 批量获取所有节点
-                    String nodeIdsStr = String.join(",", uniqueNodeIds);
-                    List<ArchiveTreeContract> nodeList = archiveTreeContractClient.getArchiveTreeContractListByIds(nodeIdsStr);
+                    // 1. 将字符串ID转换为Long列表
+                    List<Long> nodeIdList = uniqueNodeIds.stream()
+                            .map(Long::valueOf) // 将字符串转换为Long
+                            .collect(Collectors.toList());
+
+                    // 2. 使用新方法批量获取节点
+                    List<ArchiveTreeContract> nodeList = archiveTreeContractClient
+                            .getArchiveTreeContractListByList(nodeIdList);
 
                     // 2. 直接使用nodeList中的节点(避免重复添加当前节点ID)
                     Map<Long, ArchiveTreeContract> allAncestorsMap = new HashMap<>();

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

@@ -1788,7 +1788,8 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
                 } else //电签完成
                 {
                     try {
-                        archiveTreeContractClient.writeBusinessData(query.getWbsId(), query.getContractId(), formDataId, query.getClassify());
+                        //组卷前同步即可
+                        //archiveTreeContractClient.writeBusinessData(query.getWbsId(), query.getContractId(), formDataId, query.getClassify());
                     } catch (Exception e) {
                     }
                 }

+ 10 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/WeatherInfoServiceImpl.java

@@ -16,6 +16,7 @@ import org.springblade.business.entity.WeatherInfo;
 import org.springblade.business.mapper.WeatherInfoMapper;
 import org.springblade.business.service.WeatherInfoService;
 import org.springblade.common.utils.BaiduApiUtil;
+import org.springblade.common.utils.SystemUtils;
 import org.springblade.common.utils.YiKeYunApiUtils;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.tool.utils.DateUtil;
@@ -121,6 +122,9 @@ public class WeatherInfoServiceImpl extends ServiceImpl<WeatherInfoMapper, Weath
      */
     @Scheduled(cron = "0 18 10 * * ?")
     public void syncWeatherInfo() {
+        if (!SystemUtils.isLinux()) {
+            return;
+        }
         //获取所有合同段的定位信息
         List<ProjectContractArea> areaList = this.projectContractAreaClient.queryAllContractArea();
         Map<String, Map<String, String>> cachedWeatherMap = new HashMap<>();
@@ -176,6 +180,9 @@ public class WeatherInfoServiceImpl extends ServiceImpl<WeatherInfoMapper, Weath
      */
     @Scheduled(cron = "0 0 8 * * ?")
     public void syncHistoryWeatherInfo() {
+        if (!SystemUtils.isLinux()) {
+            return;
+        }
         //获取所有合同段的定位信息
         List<ProjectContractArea> areaList = this.projectContractAreaClient.queryAllContractArea();
 
@@ -290,6 +297,9 @@ public class WeatherInfoServiceImpl extends ServiceImpl<WeatherInfoMapper, Weath
     //去除重复天气,并填充每个合同段缺失的天气
     @Scheduled(cron = "0 0 9 * * ?")
     public void scanMissWeather(){
+        if (!SystemUtils.isLinux()) {
+            return;
+        }
         //获取所有项目的合同段
         List<ContractInfoVO> contractInfos = baseMapper.getAllContract();
         for (ContractInfoVO contract : contractInfos) {

+ 137 - 51
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/ArchiveController.java

@@ -22,10 +22,12 @@ import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 import java.io.*;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
@@ -76,7 +78,7 @@ public class ArchiveController {
                     if (!aBoolean) {
 
                         if (!aBoolean) {
-                            RedisTemplate.opsForValue().set("splitpng-" + dataInfo.getArchiveId(), "1", 7200, TimeUnit.SECONDS);
+                            RedisTemplate.opsForValue().set("splitpng-" + dataInfo.getArchiveId(), "1", 600, TimeUnit.SECONDS);
                             CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
                                 try {
                                     /*===============执行批量任务===============*/
@@ -103,35 +105,55 @@ public class ArchiveController {
             String archiveId = taskSign.getArchiveId();
             String id = taskSign.getId();
             String taskId = taskSign.getTaskId();
+            List<String> listPdf = new ArrayList<>();
+            int startPage = 0;
+            for (int i = 2; i <= 10; i++) {
+                // 获取pdf第二页的数据
+                String firstUrl = FileUtils.getSysLocalFileUrl() + "archiveSplit/" + archiveId + "first__"+i+"__.pdf";
+                File file = new File(firstUrl);
+                if (!file.exists()) {
+                    getPdfByPage(i, i, fileUrl, firstUrl);
+                }
 
-            // 获取pdf第二页的数据
-            String firstUrl = FileUtils.getSysLocalFileUrl() + "archiveSplit/" + archiveId + "_001.pdf";
-            File file = new File(firstUrl);
-            if (!file.exists()) {
-                getPdfByPage(2, 2, fileUrl, firstUrl);
-            }
-
-            // 保存第一页为300DPI图片
-            String imagePath = FileUtils.getSysLocalFileUrl() + "archiveSplit/" + archiveId + "_001.png";
-            File imgfile = new File(imagePath);
-            if (!imgfile.exists()) {
-                savePdfAsImage(1, firstUrl, imagePath);
+                // 保存第一页为300DPI图片
+                String imagePath = FileUtils.getSysLocalFileUrl() + "archiveSplit/" + archiveId + "first__"+i+"__.png";
+                File imgfile = new File(imagePath);
+                if (!imgfile.exists()) {
+                    savePdfAsImage(1, firstUrl, imagePath);
+                }
+                // 删除pdf
+                file.delete();
+                String state = OcrTitle(imagePath,"1");
+                if(state.equals("1")){
+                    if(startPage<2){
+                        startPage = i ;
+                    }
+                    listPdf.add(imagePath);
+                }else{
+                    imgfile.delete();
+                    break;
+                }
             }
-
+            System.out.println(listPdf.size());
+            String filePath = startPage+"--"+(listPdf.size()+1);
             //判断
             List<Map<String, Object>> mapList = jdbcTemplate.queryForList("select * from u_archives_split_info where id=" + id + "");
             if (mapList != null && Func.isNotEmpty(mapList) && mapList.size() >= 1) {
                 String status = mapList.get(0).get("status") + "";
                 if (status.equals("3")) {
-                    String updateSql = "update u_archives_auto set split_status=1 where id=" + id;
+                    String updateSql = "update u_archives_auto set split_status=1 where id=" + archiveId;
                     jdbcTemplate.execute(updateSql);
                 }
             } else {
-                String sql22 = "insert into u_archives_split_info(id,status,file_url,first_file_url,task_id,archive_id,create_time) VALUES(" + id + ",2,'" + fileUrl + "','" + imagePath + "'," + taskId + "," + archiveId + ",SYSDATE())";
+                String sql22 = "insert into u_archives_split_info(id,status,file_url,first_file_url,task_id,archive_id,create_time) VALUES(" + id + ",2,'" + fileUrl + "','" + filePath + "'," + taskId + "," + archiveId + ",SYSDATE())";
                 jdbcTemplate.execute(sql22);
-                String updateSql = "update u_archives_auto set split_status=2 where id=" + id;
+                String updateSql = "update u_archives_auto set split_status=2 where id=" + archiveId;
                 jdbcTemplate.execute(updateSql);
             }
+
+            String sql = "delete from u_archive_file where id<>'"+id+"' and archive_id='"+archiveId+"'";
+            jdbcTemplate.execute(sql);
+
             RedisTemplate.delete("splitpng-" + archiveId);
         } catch (Exception e) {
             e.printStackTrace();
@@ -154,7 +176,7 @@ public class ArchiveController {
                     if (!aBoolean) {
 
                         if (!aBoolean) {
-                            RedisTemplate.opsForValue().set("splithtml-" + dataInfo.getArchiveId(), "1", 600, TimeUnit.SECONDS);
+                            RedisTemplate.opsForValue().set("splithtml-" + dataInfo.getArchiveId(), "1", 1200, TimeUnit.SECONDS);
                             CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
                                 try {
                                     /*===============执行批量任务===============*/
@@ -179,72 +201,103 @@ public class ArchiveController {
             String archiveId = taskSign.getArchiveId();
             String fileUlr = taskSign.getFileUrl();
             String firstPage = FileUtils.getSysLocalFileUrl() + "archiveSplit/";
+            String firstFileUrl = taskSign.getFirstFileUrl();
+            String firstUrl[] = firstFileUrl.split("--");
+            int basePage = Integer.parseInt(firstUrl[1]);
+            int baseStart = Integer.parseInt(firstUrl[0]);
+            String dutyUser = "";
             int bkb = 0 ;
             //将imagePath 的数据转成一个可解析的html
-            String htmlUrl = pngToHtml(firstPage, archiveId);
-            /*String htmlUrl2 = pngToHtml(firstPage, archiveId);
-            String htmlUrl = "";
-            if (htmlUrl1.equals(htmlUrl2) && htmlUrl1.indexOf("_001.html") >= 0 && htmlUrl1.indexOf("archiveSplit") >= 0) {
-                htmlUrl = htmlUrl2;
-            }*/
+            String htmlUrl = pngToHtml(firstPage, archiveId,taskSign.getFirstFileUrl());
+
 
             if (htmlUrl.indexOf("_001.html") >= 0 && htmlUrl.indexOf("archiveSplit") >= 0) {
                 String htmlString = IoUtil.readToString(new FileInputStream(htmlUrl));
                 Document doc = Jsoup.parse(htmlString);
                 Element table = doc.select("table").first();
                 Elements trs = table.select("tr");
+                //由于解析已经成功,可能数据已经分解过,需要删除
+                if(trs!=null && trs.size()>=1){
+                    String sql = "delete from u_archive_file where id<>'"+taskSign.getId()+"' and archive_id='"+archiveId+"'";
+                    jdbcTemplate.execute(sql);
+                }
 
-                for (int i = 1; i <= trs.size() - 1; i++) {
+                for (int i = 0; i <= trs.size() - 1; i++) {
                     Element tr = trs.get(i);
                     String zrz = tr.select("td").get(0).text();
                     String wjtm = tr.select("td").get(1).text();
                     String rq = tr.select("td").get(2).text();
                     String ym = tr.select("td").get(3).text();
+                   if(zrz.equals("责任者") && wjtm.equals("文件题名") && rq.equals("日期")){
+                        continue;
+                   }
                     int startYm = 0;
                     int endYm = 0;
                     if(i<trs.size()-1){
                         startYm = Func.toInt(ym);
                         String enData = trs.get(i+1).select("td").get(3).text();
-                        if(enData.indexOf("-")>=0){
-                            endYm = Func.toInt(enData.split("-")[0])-1;
-                        }else{
-                            endYm = Func.toInt(enData)-1;
+                        if(enData.indexOf("页")>=0){
+                            enData = trs.get(i+2).select("td").get(3).text();
+                        }
+
+                        String[] parts = enData.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
+                        if(parts!=null && parts.length>=1){
+                            endYm = Func.toInt(parts[0]);
                         }
                     }else{
-                        if(ym.indexOf("-")>=0){
-                            String[] split = ym.split("-");
+                        String[] split = ym.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
+                        if(split!=null && split.length>=3){
                             startYm = Func.toInt(split[0]);
-                            endYm = Func.toInt(split[1]);
+                            endYm = Func.toInt(split[2]);
+                        }else{
+                            startYm = Func.toInt(split[0]);
+                            endYm = Func.toInt(split[0]);
                         }
                     }
-                    startYm = startYm + 2 ;
-                    endYm = endYm + 2 ;
-                    System.out.println(zrz + " " + wjtm + " " + rq + " " + ym);
+                    startYm = basePage+startYm ;
+                    endYm = basePage+endYm ;
+                    dutyUser = zrz;
+                    System.out.println("序号="+i+"--文件提名:"+wjtm +"--开始("+startYm+"-"+endYm+")---页数"+(endYm-startYm+1));
                     // 分解文件
                     String fmlUrl = FileUtils.getSysLocalFileUrl() + "archiveSplit/"+archiveId+"_cf_00"+i+".pdf";
                     getPdfByPage(startYm,endYm,fileUlr,fmlUrl);
-                    saveDataToMysql(fmlUrl,wjtm,taskSign.getId(),endYm-startYm+1);
+                    saveDataToMysql(fmlUrl,wjtm,taskSign.getId(),endYm-startYm+1,i,zrz);
                     bkb =  endYm ;
                 }
-
             } else {
-
+                return;
             }
 
             // 添加封面信息
             String fmlUrl = FileUtils.getSysLocalFileUrl() + "archiveSplit/"+archiveId+"_fm_001.pdf";
             getPdfByPage(1,1,fileUlr,fmlUrl);
-            saveDataToMysql(fmlUrl,"封面",taskSign.getId(),1);
+            saveDataToMysql(fmlUrl,"封面",taskSign.getId(),1,-4,dutyUser);
 
             // 卷内目录
             String jnmuUrl = FileUtils.getSysLocalFileUrl() + "archiveSplit/"+archiveId+"_jnml_001.pdf";
-            getPdfByPage(2,2,fileUlr,jnmuUrl);
-            saveDataToMysql(jnmuUrl,"卷内目录",taskSign.getId(),1);
+            getPdfByPage(baseStart,basePage,fileUlr,jnmuUrl);
+            saveDataToMysql(jnmuUrl,"卷内目录",taskSign.getId(),1,-3,dutyUser);
 
             // 卷内备考表
             String jnbkbUrl = FileUtils.getSysLocalFileUrl() + "archiveSplit/"+archiveId+"_jnbkb_001.pdf";
             getPdfByPage(bkb+1,bkb+1,fileUlr,jnbkbUrl);
-            saveDataToMysql(jnbkbUrl,"卷内备考表",taskSign.getId(),1);
+            saveDataToMysql(jnbkbUrl,"卷内备考表",taskSign.getId(),1,100,dutyUser);
+
+            // 背脊表
+            String bjbUrl = FileUtils.getSysLocalFileUrl() + "archiveSplit/"+archiveId+"_beiji_001.pdf";
+            String bjbUrlPng = FileUtils.getSysLocalFileUrl() + "archiveSplit/"+archiveId+"_beiji_001.png";
+            getPdfByPage(bkb+2,bkb+2,fileUlr,bjbUrl);
+
+            File bgImgFile = new File(bjbUrl);
+            if (!bgImgFile.exists()) {
+                savePdfAsImage(1, bjbUrl, bjbUrlPng);
+            }
+            String state = OcrTitle(bjbUrl,"3");
+            if(state.equals("1")){
+                saveDataToMysql(jnbkbUrl,"背脊表",taskSign.getId(),1,101,dutyUser);
+            }
+            bgImgFile.delete();
+
 
             // 修改任务状态
             String updateSql = "update u_archives_split_info set status=3 where id=" + taskSign.getId();
@@ -260,20 +313,19 @@ public class ArchiveController {
             jdbcTemplate.execute(taxkSql2);
 
             // 修改完成情况
-
             RedisTemplate.delete("splithtml-" + archiveId);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
-    public static String pngToHtml(String fileUrl, String pKeyId) {
+    public static String pngToHtml(String fileUrl, String pKeyId,String pageNum) {
         String lasHhtmlUrl = "";
         try {
             // 定义Python解释器路径和脚本路径
             String pythonScript = "/Users/hongchuangyanfa/Desktop/PycharmProjects/splitPngToHtml.py";
             // 构建命令
-            ProcessBuilder pb = new ProcessBuilder("python3", pythonScript, fileUrl, pKeyId);
+            ProcessBuilder pb = new ProcessBuilder("python3", pythonScript, fileUrl, pKeyId, pageNum);
             Process process = pb.start();
 
             // 读取Python脚本输出
@@ -281,7 +333,6 @@ public class ArchiveController {
                     new InputStreamReader(process.getInputStream()));
             String htmlUrl;
             while ((htmlUrl = reader.readLine()) != null) {
-                System.out.println("222" + htmlUrl);
                 if (htmlUrl.indexOf("html文件路径") >= 0 && htmlUrl.indexOf("_001.html") >= 0 && htmlUrl.indexOf("archiveSplit") >= 0) {
                     lasHhtmlUrl = htmlUrl.replace("html文件路径", "");
                 }
@@ -299,6 +350,38 @@ public class ArchiveController {
         }
     }
 
+    public String OcrTitle(String fileUrl, String type) {
+        String lasHhtmlUrl = "";
+        try {
+            // 定义Python解释器路径和脚本路径
+            String pythonScript = "/Users/hongchuangyanfa/Desktop/PycharmProjects/splitPngByTitle.py";
+            // 构建命令
+            ProcessBuilder pb = new ProcessBuilder("python3", pythonScript, fileUrl, type);
+            Process process = pb.start();
+
+            // 读取Python脚本输出
+            BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(process.getInputStream()));
+            String htmlUrl;
+            while ((htmlUrl = reader.readLine()) != null) {
+                System.out.println("222" + htmlUrl);
+                if (htmlUrl.indexOf("图片中是否有卷内目录") >= 0 && htmlUrl.indexOf("True") >=0) {
+                    return "1";
+                }
+            }
+            // 等待进程结束
+            int exitCode = process.waitFor();
+            if (exitCode == 0) {
+                return lasHhtmlUrl;
+            } else {
+                return "1";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "1";
+        }
+    }
+
 
     public static void getPdfByPage(int startPage, int endPage, String filePath, String savePath) {
         try {
@@ -329,7 +412,7 @@ public class ArchiveController {
         }
     }
 
-    public void savePdfAsImage(int pageNum, String filePath, String outputPath) {
+    public static void savePdfAsImage(int pageNum, String filePath, String outputPath) {
         try (InputStream inputStream = FileUtils.getInputStreamByUrl(filePath);
              PDDocument document = PDDocument.load(inputStream)) {
 
@@ -362,7 +445,8 @@ public class ArchiveController {
         }
     }
 
-    public int saveDataToMysql(String upFileUrl,String fileName,String fileId,int filePage) {
+    public int saveDataToMysql(String upFileUrl,String fileName,String fileId,int filePage,int sort,String dutyUser) {
+
         // 获取封面信息
         long newPkId = SnowFlakeUtil.getId(); //主键Id
         File fmfile = new File(upFileUrl);
@@ -375,13 +459,13 @@ public class ArchiveController {
                         " drawing_no,cite_change_number,certification_time,e_visa_file,node_ext_id,file_type,archive_id,origin_id,filming_time,filmingor_time,tag_id,pic_code,refer_code,film_code,width,height,ftime,utime,del_time,sort,box_name,box_number,is_auto_file,is_archive,page_num, " +
                         " file_size,source_type,is_element,pdf_page_url,fid,rectification,classify,m_wbs_tree_contract_p_key_id,u_image_classification_file_id,archive_file_storage_type,node_tree_structure,date_name,archive_file_stroage_type,out_id,sort_num " +
                         "   ) " +
-                        " SELECT "+newPkId+",project_id,contract_id,node_id,file_number,'" + fileName + "',file_time,'" + FmPdfUrl + "','" + FmPdfUrl + "',"+filePage+",is_approval,is_certification,is_need_certification,duty_user,create_user,create_dept,create_time,update_user,update_time,status,is_deleted,sheet_type,sheet_source, " +
-                        "        drawing_no,cite_change_number,certification_time,e_visa_file,node_ext_id,file_type,archive_id,origin_id,filming_time,filmingor_time,tag_id,pic_code,refer_code,film_code,width,height,ftime,utime,del_time,sort,box_name,box_number,is_auto_file,is_archive,page_num, " +
+                        " SELECT "+newPkId+",project_id,contract_id,node_id,file_number,'" + fileName + "',file_time,'" + FmPdfUrl + "','" + FmPdfUrl + "',"+filePage+",is_approval,is_certification,is_need_certification,'"+dutyUser+"',create_user,create_dept,create_time,update_user,update_time,status,is_deleted,sheet_type,sheet_source, " +
+                        "        drawing_no,cite_change_number,certification_time,e_visa_file,node_ext_id,file_type,archive_id,origin_id,filming_time,filmingor_time,tag_id,pic_code,refer_code,film_code,width,height,ftime,utime,del_time,"+sort+",box_name,box_number,is_auto_file,is_archive,page_num, " +
                         "        file_size,source_type,is_element,pdf_page_url,fid,rectification,classify,m_wbs_tree_contract_p_key_id,u_image_classification_file_id,archive_file_storage_type,node_tree_structure,date_name,archive_file_stroage_type,out_id,sort_num " +
                         " from u_archive_file where id=" + fileId;
                 System.out.println(fileName + "----" + sql);
                 jdbcTemplate.execute(sql);
-                return 200;
+
             } else {
                 // 检查一下oss是否启动
                 System.out.println("oss服务未启动,无法上传文件到oss");
@@ -390,5 +474,7 @@ public class ArchiveController {
         }else{
             return 404;
         }
+        fmfile.delete();
+        return 200;
     }
 }

+ 9 - 4
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/EVController.java

@@ -58,7 +58,7 @@ public class EVController {
     @Resource(name = "taskExecutor1")
     private ThreadPoolExecutor executor;
 
-   // @Scheduled(cron = "0/10 * * * * ?")
+    @Scheduled(cron = "0/10 * * * * ?")
     public void SignInfo() {
         //执行代码
 
@@ -71,8 +71,9 @@ public class EVController {
                 "JSON_UNQUOTE(JSON_EXTRACT(json_data, '$.flag')) as flag," +
                 "GROUP_CONCAT(create_user) as userId," +
                 "GROUP_CONCAT(nick_name) as nickName," +
-                "sign_type as sigType," +
-                "(select count(0) from u_task_parallel b where a.task_parallel_id = b.process_instance_id and b.is_deleted = 0 and (b.status = 1 or b.initiative = 1)) isSignature" +
+                "sign_type as sigType ," +
+                "(select count(0) from u_task_parallel b INNER JOIN u_task c on b.process_instance_id = c.process_instance_id " +
+                "where c.id = JSON_UNQUOTE(JSON_EXTRACT(a.json_data, '$.taskId')) and b.is_deleted = 0 and (b.status = 1 or b.initiative = 1)) isSignature" +
                 " from u_task_batch a where is_deleted=0 GROUP BY JSON_EXTRACT(json_data, '$.formDataId'),sign_type ORDER BY sign_type DESC ";
                 //and JSON_UNQUOTE(JSON_EXTRACT(json_data,'$.taskId')) in(SELECT id from u_task where project_id =1792760669353865218 and is_deleted =0 and approval_type=1 )
         List<TaskSignInfoVO> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TaskSignInfoVO.class));
@@ -82,7 +83,7 @@ public class EVController {
                 if(dataInfo.getSigType() == 2 && dataInfo.getIsSignature() > 0){
                     continue;
                 }
-                if (executor.getQueue().size() <= 10) {
+                if (executor.getQueue().size() <= 20) {
                     Boolean aBoolean = RedisTemplate.hasKey("sign-" + dataInfo.getFormDataId());
                     if (!aBoolean) {
                         if (dataInfo.getSigType() == 2) {
@@ -102,6 +103,10 @@ public class EVController {
                                 try {
                                     /*===============执行批量任务===============*/
                                     evDataService.signTaskBatch(dataInfo);
+                                    System.out.println("队列数量" + executor.getQueue().size());
+                                    System.out.println("活跃数量" + executor.getActiveCount());
+                                    System.out.println("总共数量" + executor.getTaskCount());
+                                    System.out.println("完成数量" + executor.getCompletedTaskCount());
                                 } catch (Exception e) {
                                     e.printStackTrace();
                                 }

+ 1 - 1
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/EVisaService.java

@@ -50,5 +50,5 @@ public interface EVisaService {
 
     Object[] signPdfByAXQZ(SealPdfVO pdfVO, String loPdfurl,String outPdfUrl);
 
-    String signPdfByDFZX(HashMap<String, Object> daMa);
+    String signPdfByDFZX(HashMap<String, Object> daMa,String signPdfByDFZX,String contractId);
 }

+ 11 - 6
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVDataServiceImpl.java

@@ -129,11 +129,11 @@ public class EVDataServiceImpl implements EVDataService {
                 if (taskApp.getSigState() != 1) {
                     return;
                 }
-            } else if (taskApp.getRemarkType().equals("2")) { //东方中讯
+            } else if (taskApp.getRemarkType().equals("2") || taskApp.getRemarkType().equals("3")) { //东方中讯
                 //添加电签策略
                 List<Map<String, Object>> strategyListByDFZX = getStrategyListByDFZX(taskApp, ids);
                 //调用签字逻辑
-                String s = signTaskBatchByDFZX(strategyListByDFZX, fileUrl, taskApp.getSigType());
+                String s = signTaskBatchByDFZX(strategyListByDFZX, fileUrl, taskApp.getSigType(),taskApp.getRemarkType(),taskApp.getContractId());
                 if (s.contains("sucess")) {
                     taskApp.setLastFilePdfUrl(s.split("@@@@")[1]);
                     taskApp.setSigState(1);
@@ -492,7 +492,9 @@ public class EVDataServiceImpl implements EVDataService {
                 Integer remarkType = projectInfo.getRemarkType();
                 if (remarkType != null && Func.isNotEmpty(remarkType) && remarkType == 2) {
                     taskApp.setRemarkType("2");
-                } else {
+                }else if (remarkType != null && Func.isNotEmpty(remarkType) && remarkType == 3) {
+                    taskApp.setRemarkType("3");
+                }else {
                     taskApp.setRemarkType("1");
                 }
             }
@@ -663,7 +665,7 @@ public class EVDataServiceImpl implements EVDataService {
     }
 
     // 添加电签策略 -- 东方中讯
-    public String signTaskBatchByDFZX(List<Map<String, Object>> maps, String pdfUrl, int type) {
+    public String signTaskBatchByDFZX(List<Map<String, Object>> maps, String pdfUrl, int type,String remarkType,String contractId) {
         if (maps != null && maps.size() > 0) {
             String fileUrl = pdfUrl;
             for (Map<String, Object> dataMap : maps) {
@@ -671,7 +673,7 @@ public class EVDataServiceImpl implements EVDataService {
                 daMa.put("keyWord", dataMap.get("keyWord"));
                 daMa.put("sealId", dataMap.get("sealId"));
                 // 设置图片显示大小
-                if (type != 2) { //章
+                if (type != 2 && !remarkType.equals("3")) { //章
                     daMa.put("showHeight", 30);
                     daMa.put("showWidth", 60);
                 }
@@ -702,7 +704,10 @@ public class EVDataServiceImpl implements EVDataService {
                 } catch (Exception e) {
                     throw new RuntimeException(e);
                 }
-                String reData = eVisaService.signPdfByDFZX(daMa);
+
+
+                String reData = eVisaService.signPdfByDFZX(daMa,remarkType,contractId);
+
                 if (reData.indexOf("success@") >= 0) {
                     fileUrl = reData.split("@@@@")[1];
                 } else {

+ 32 - 5
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVisaServiceImpl.java

@@ -68,6 +68,9 @@ import org.springblade.system.cache.ParamCache;
 import org.springblade.system.user.entity.User;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.StringHttpMessageConverter;
@@ -313,7 +316,7 @@ public class EVisaServiceImpl implements EVisaService {
                             String originalFileB64 = Base64.toBase64String(fileByte);
                             daMa.put("fileB64", originalFileB64);
                             daMa.put("lastSignFlag", false);
-                            String reData = signPdfByDFZX(daMa);
+                            String reData = signPdfByDFZX(daMa,reType,contractId);
                             if (reData.indexOf("success@") >= 0) {
                                 fileUrl = reData.split("@@@@")[1];
                             } else {
@@ -517,7 +520,7 @@ public class EVisaServiceImpl implements EVisaService {
                             String originalFileB64 = Base64.toBase64String(fileByte);
                             daMa.put("fileB64", originalFileB64);
                             daMa.put("lastSignFlag", false);
-                            String reData = signPdfByDFZX(daMa);
+                            String reData = signPdfByDFZX(daMa,"2",contractId);
                             if (reData.indexOf("success@") >= 0) {
                                 fileUrl = reData.split("@@@@")[1];
                             } else {
@@ -788,12 +791,17 @@ public class EVisaServiceImpl implements EVisaService {
      * 东方 中讯
      * @throws Exception
      */
-    public String signPdfByDFZX(HashMap<String, Object> request) {
+    public String signPdfByDFZX(HashMap<String, Object> paramsMap,String signPdfByDFZX,String contractId) {
         String url = "http://localhost:9125/FrontSys/SealServicezx/FileSignByKeyWord";
         String sys_isonline = ParamCache.getValue(CommonConstant.SYS_ISONLINE);
         if ("20".equals(sys_isonline) || SystemUtils.isWindows() || SystemUtils.isMacOs()) {
             url = "http://219.151.181.73:9125/FrontSys/SealServicezx/FileSignByKeyWord";
         }
+        if(signPdfByDFZX.equals("3")){
+            url = "http://113.250.191.72:9125/FrontSys/SealServicezx/FileSignByKeyWord";
+        }
+
+
         String sysLocalFileUrl = FileUtils.getSysLocalFileUrl();
         String filecode = SnowFlakeUtil.getId() + "";
         String dataFileUrl = sysLocalFileUrl + "/pdf/" + filecode + ".pdf";
@@ -802,7 +810,9 @@ public class EVisaServiceImpl implements EVisaService {
             httpRequestFactory.setConnectionRequestTimeout(30000);
             httpRequestFactory.setConnectTimeout(30000);
             httpRequestFactory.setReadTimeout(30000);
+
             RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
+
             // 设置编码格式为UTF-8
             List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
             HttpMessageConverter<?> converterTarget = null;
@@ -818,16 +828,33 @@ public class EVisaServiceImpl implements EVisaService {
             }
             HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
             converterList.add(1, converter);
+            HashMap<String, Object> retData;
+            if(signPdfByDFZX.equals("3")){
+                Map<String, Object> stringObj = jdbcTemplate.queryForMap("SELECT seal_project_id,seal_account_id,seal_customer_id,seal_comm_key from m_contract_info  where id = " + contractId);
+                System.out.println(contractId);
+                HttpHeaders headers = new HttpHeaders();
+                // 添加自定义头(示例:设置Authorization和Content-Type)
+                headers.add("project_id", stringObj.get("seal_project_id")+"");  // 替换为实际
+                headers.add("account_id", stringObj.get("seal_account_id")+"");  // 替换为实际
+                headers.add("customer_id", stringObj.get("seal_customer_id")+"");  // 替换为实际
+                headers.add("commKey", stringObj.get("seal_comm_key")+"");  // 替换为实际
+                headers.setContentType(MediaType.APPLICATION_JSON);  // 指定请求体为JSON格式
+
+                HttpEntity<HashMap<String, Object>> httpEntity = new HttpEntity<>(paramsMap, headers);
+
+                retData = restTemplate.postForObject(url, httpEntity, HashMap.class);
+            }else{
+                retData = restTemplate.postForObject(url, paramsMap, HashMap.class);
+            }
 
-            HashMap<String, Object> retData = restTemplate.postForObject(url, request, HashMap.class);
             System.out.println("东方中讯uRL" + url);
             String code = retData.get("code").toString();
             String msg = retData.get("msg").toString();
 
             if (!"0".equals(code)) {
+                System.out.println(retData);
                 return ERROR + "@@@@" + msg;
             }
-
             String fileB642 = retData.get("fileB64").toString();
 
             FileOutputStream fout = new FileOutputStream(dataFileUrl);

+ 1 - 1
blade-service/blade-manager/pom.xml

@@ -234,7 +234,7 @@
                     <target>${java.version}</target>
                     <encoding>${project.build.sourceEncoding}</encoding>
                     <compilerArguments>
-                        <bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar;${java.home}/lib/jsse.jar
+                        <bootclasspath>${java.home}/lib/rt.jar:${java.home}/lib/jce.jar:${java.home}/lib/jsse.jar
                         </bootclasspath>
                     </compilerArguments>
                 </configuration>

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

@@ -101,13 +101,13 @@ public class ArchiveTreeContractController extends BladeController {
         }
         String sql="select * from m_contract_info where id="+contractId1;
         ContractInfo info = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(ContractInfo.class));
-        if(detail.getRollor()==null&&info!=null&&info.getFiler()!=null){
+        if(StringUtils.isEmpty(detail.getRollor())&&info!=null&&info.getFiler()!=null){
             detail.setRollor(info.getFiler());
         }
-        if(detail.getReviewer()==null&&info!=null&&info.getReviewer()!=null){
+        if(StringUtils.isEmpty(detail.getReviewer())&&info!=null&&info.getReviewer()!=null){
             detail.setReviewer(info.getReviewer());
         }
-        if(detail.getFileNumberPrefix()==null&&info!=null&&info.getPrefix()!=null){
+        if(StringUtils.isEmpty(detail.getFileNumberPrefix())&&info!=null&&info.getPrefix()!=null){
             detail.setFileNumberPrefix(info.getPrefix());
         }
         return R.data(detail);
@@ -398,7 +398,8 @@ public class ArchiveTreeContractController extends BladeController {
 
         //TODO 待节点都同步完了以后,再去设置wbs节点的组卷规则。(方式:客户级wbs节点-》项目级wbs节点-》wbs项目级组卷规则-》设置规则到客户级wbs节点以及子节点)
         if (b) {
-            archiveTreeContractService.updateWbsRuleNodes(archiveTree.getProjectId());
+            //todo 0625 暂时去掉项目级组卷规则同步客户级
+            //archiveTreeContractService.updateWbsRuleNodes(archiveTree.getProjectId());
         }
 
         if (b) {

+ 17 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java

@@ -790,6 +790,23 @@ public class ExcelTabController extends BladeController {
                 this.excelTabService.gsColor(pkeyId, process.getPKeyId().toString(), wbsTreeContract.getProjectId(), doc);
             }
             doc.select("Col").remove();
+            org.springblade.manager.entity.TableInfo tableInfo = this.tableInfoService.getOne(Wrappers.<org.springblade.manager.entity.TableInfo>lambdaQuery().select(org.springblade.manager.entity.TableInfo::getId)
+                    .eq(org.springblade.manager.entity.TableInfo::getTabEnName, wbsTreeContract.getInitTableName()).eq(org.springblade.manager.entity.TableInfo::getIsDeleted, 0).last("limit 1"));
+            if (tableInfo != null ) {
+                List<WbsFormElement> list = this.wbsFormElementService.list(Wrappers.<WbsFormElement>lambdaQuery().select(WbsFormElement::getEKey, WbsFormElement::getELength)
+                        .eq(WbsFormElement::getFId, tableInfo.getId()).eq(WbsFormElement::getIsDeleted, 0));
+                if (list != null && !list.isEmpty()) {
+                    Map<String, Integer> keyNameMap = list.stream().filter(wbsFormElement -> wbsFormElement.getELength() != null).collect(Collectors.toMap(WbsFormElement::getEKey, WbsFormElement::getELength, (v1, v2) -> v1 > v2 ? v1 : v2));
+                    Elements keyNames = table.getElementsByAttribute("keyname");
+                    keyNames.forEach(element -> {
+                        String key = element.attr("keyname");
+                        Integer length = keyNameMap.get(key.split("__")[0]);
+                        if (length != null) {
+                            element.attr("maxlength", length.toString());
+                        }
+                    });
+                }
+            }
             fileInputStream.close();
             return R.data(table + "");
         } catch (Exception e) {

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

@@ -532,6 +532,17 @@ public class WbsTreeController extends BladeController {
         return R.data(result);
     }
 
+    /**
+     * 根据节点类型,内业资料类型查询节点数据
+     */
+    @GetMapping("/getQueryValueByNodeType")
+    @ApiOperationSupport(order = 21)
+    @ApiOperation(value = "根据节点类型,内业资料类型查询节点数据", notes = "传入wbsId、projectId、nodeType、majorDataType")
+    public Object getQueryValueByNodeType(@RequestParam String wbsId, @RequestParam String projectId, @RequestParam String queryValue, @RequestParam(required = false,defaultValue = "6") Integer nodeType, @RequestParam(required = false, defaultValue = "-1") Integer majorDataType) {
+        Object result = wbsTreeService.getQueryValueByNodeType(wbsId, projectId, queryValue, nodeType, majorDataType);
+        return R.data(result);
+    }
+
     @GetMapping("setContractIsPid")
     public void setContractIsPid(){
         //为合同段节点没有设置is_pid的字段设置isPid

+ 7 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ArchiveTreeContractImpl.java

@@ -48,7 +48,7 @@ public class ArchiveTreeContractImpl implements ArchiveTreeContractClient {
      */
     @Override
     public List<ArchiveTreeContract> getHavedFileNodeByProjectID(Long projectId) {
-        List<ArchiveTreeContract> list = archiveTreeContractMapper.getHavedFileNodeByProjectID(projectId);
+        List<ArchiveTreeContract> list = archiveTreeContractMapper.getHavedFileNodeByProjectID1(projectId);
         Map<Long,String> map = new HashMap<>();
         for (ArchiveTreeContract box: list) {
             map.put(box.getId(),"1");
@@ -103,6 +103,12 @@ public class ArchiveTreeContractImpl implements ArchiveTreeContractClient {
         return archiveTreeContractService.getArchiveTreeContractListByIds(ids);
     }
 
+    @Override
+    public List<ArchiveTreeContract> getArchiveTreeContractListByList(List<Long> ids) {
+
+        return archiveTreeContractService.getArchiveTreeContractListByList(ids);
+    }
+
     @Override
     public ArchiveTreeContract getFirstNodeByTreeCode(Long projectId,Integer type) {
         ArchiveTreeContract contract = new ArchiveTreeContract();

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

@@ -80,6 +80,8 @@ public interface ArchiveTreeContractMapper extends BaseMapper<ArchiveTreeContrac
 
     List<ArchiveTreeContract> getHavedFileNodeByProjectID(@Param("projectId") Long projectId);
 
+    List<ArchiveTreeContract> getHavedFileNodeByProjectID1(@Param("projectId") Long projectId);
+
     List<ArchiveTreeContract> getHavedBoxFileNodeByProjectID(@Param("projectId") Long projectId);
 
     List<ArchiveTreeContract> getListByProjectId(@Param("projectId") Long projectId);

+ 38 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.xml

@@ -358,6 +358,43 @@
         order by ancestors asc,tree_sort asc
     </select>
 
+    <select id="getHavedFileNodeByProjectID1" resultMap="archiveTreeContractResultMap">
+        SELECT
+            *
+        FROM
+            m_archive_tree_contract
+        WHERE
+            1 = 1
+          AND project_id = #{projectId}
+          AND is_deleted = 0
+          AND archive_auto_type IS NOT NULL
+          AND id IN (
+            SELECT
+                node_id
+            FROM
+                u_archive_file uaf
+            WHERE
+                1 = 1
+              AND uaf.project_id = #{projectId}
+              AND uaf.is_deleted = 0
+              AND uaf.node_id IS NOT NULL
+              AND (
+                -- 条件1:标记为未归档的文件
+
+                    -- 条件2:未关联有效案卷的文件
+                        NOT EXISTS (
+                                SELECT 1
+                                FROM u_archives_auto uaa
+                                WHERE
+                                    uaa.id = uaf.archive_id
+                                  AND uaa.project_id = uaf.project_id
+                                  AND uaa.is_deleted = 0
+                            )
+                )
+        )
+        ORDER BY tree_sort,sort
+    </select>
+
 
     <select id="getTopAutoTypeNodeByProjectID" resultMap="archiveTreeContractResultMap">
         SELECT
@@ -394,7 +431,7 @@
               AND node_id IS NOT NULL
               AND (box_number IS NOT NULL and box_number != -1 )
         )
-        order by ancestors asc,tree_sort asc
+        order by tree_sort,sort
     </select>
 
 

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

@@ -113,6 +113,8 @@ public interface IArchiveTreeContractService extends BaseService<ArchiveTreeCont
 
     List<ArchiveTreeContract> getArchiveTreeContractListByIds(String ids);
 
+    List<ArchiveTreeContract> getArchiveTreeContractListByList(List<Long> ids);
+
     List<ArchiveTreeContract> getWbsAssociatedNodes(String contractId);
 
     void updateAllSonNodeIdsForArchiveAutoRule(ArchiveTreeContract archiveTreeContract);

+ 2 - 3
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IWbsTreeService.java

@@ -6,12 +6,9 @@ import org.springblade.core.tool.api.R;
 import org.springblade.manager.dto.FormElementDTO;
 import org.springblade.manager.dto.WbsTreeContractDTO;
 import org.springblade.manager.dto.WbsTreeDTO2;
-import org.springblade.manager.entity.TableInfo;
-import org.springblade.manager.entity.WbsFormElement;
 import org.springblade.manager.entity.WbsTree;
 import org.springblade.manager.entity.WbsTreePrivate;
 import org.springblade.manager.vo.*;
-import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
@@ -82,4 +79,6 @@ public interface IWbsTreeService extends BaseService<WbsTree> {
     Object getQueryValueByType(String queryValue, String wbsId, String projectId, String type);
 
     List<WbsTreePrivate> getTitleRange(String projectId);
+
+    Object getQueryValueByNodeType(String wbsId, String projectId, String queryValue, Integer nodeType, Integer majorDataType);
 }

+ 2 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArTreeContractInitServiceImpl.java

@@ -235,7 +235,8 @@ public class ArTreeContractInitServiceImpl {
 
         InitTreeSort(trees.get(0), "", 0);
 
-        archiveAutoRuleSync.syncArchiveTreeContractList(vo2Map, proVo2Trees);
+        //todo 0625 暂时去掉项目级组卷规则同步客户级
+        //archiveAutoRuleSync.syncArchiveTreeContractList(vo2Map, proVo2Trees);
 
         List<ArchiveTreeContract> upList = new ArrayList<>();
 

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

@@ -405,6 +405,13 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 		return baseMapper.getArchiveTreeContractListByIds(idsList);
 	}
 
+	@Override
+	public List<ArchiveTreeContract> getArchiveTreeContractListByList(List<Long> ids) {
+		// 将List<Long>转换成List<String>
+		List<String> stringIds = ids.stream().map(Object::toString).collect(Collectors.toList());
+		return baseMapper.getArchiveTreeContractListByIds(stringIds);
+	}
+
 	/**
 	 *  同步项目级
 	 * @param dstNode
@@ -497,6 +504,9 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 		this.saveBatch(saveList);
 		this.updateBatchById(upList);
 
+		//再刷新一次treesort
+		sortChildren(dstNode);
+
 		return true;
 	}
 
@@ -1710,4 +1720,74 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 	}
 
 
+	/**
+	 * 刷新某个节点下的treesort
+	 * @param archiveTreeContract
+	 */
+	void sortChildren(ArchiveTreeContract archiveTreeContract) {
+		if (archiveTreeContract == null) {
+			return;
+		}
+
+		// 获取整个项目的树
+		List<ArchiveTreeContractVO2> trees = this.tree2Root(
+				AuthUtil.getTenantId(),
+				null,
+				null,
+				archiveTreeContract.getProjectId(),
+				null
+		);
+
+		if (trees.isEmpty()) {
+			return;
+		}
+		ArchiveTreeContractVO2 tree = trees.get(0);
+
+		// 初始化整棵树的排序 (只设置内存中的 treeSort 和 flag)
+		ForestNodeMergerEx.InitTreeSortEx(tree, "", 0);
+
+		// 获取当前节点对应的子树
+		ArchiveTreeContractVO2 subTree = ForestNodeMergerEx.getSubTree(tree, archiveTreeContract.getId());
+		if (subTree == null) {
+			return; // 没找到子树
+		}
+
+		// 收集子树中所有节点 (用于后续筛选)
+		List<ArchiveTreeContractVO2> allNodes = new ArrayList<>();
+		ForestNodeMergerEx.getTreeList(subTree, allNodes);
+
+		// 筛选需要更新的节点 (flag == 1)
+		List<ArchiveTreeContractVO2> changeList = new ArrayList<>();
+		for (ArchiveTreeContractVO2 node : allNodes) {
+			if (node.getFlag() == 1) {
+				changeList.add(node);
+			}
+		}
+
+		if (changeList.isEmpty()) {
+			return; // 没有需要更新的节点
+		}
+
+		// 构建 ID 映射
+		Map<Long, ArchiveTreeContractVO2> map = new HashMap<>();
+		List<Long> ids = new ArrayList<>();
+		for (ArchiveTreeContractVO2 ar : changeList) {
+			ids.add(ar.getId());
+			map.put(ar.getId(), ar);
+		}
+
+		// 从数据库查询需要更新的实体
+		List<ArchiveTreeContract> changeArchiveList = baseMapper.selectBatchIds(ids);
+		for (ArchiveTreeContract changeNode : changeArchiveList) {
+			ArchiveTreeContractVO2 ar = map.get(changeNode.getId());
+			if (ar != null) {
+				changeNode.setTreeSort(ar.getTreeSort());
+			}
+		}
+
+		// 批量更新数据库
+		this.saveOrUpdateBatch(changeArchiveList);
+	}
+
+
 }

+ 13 - 11
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractSyncImpl.java

@@ -200,10 +200,10 @@ public class ArchiveTreeContractSyncImpl {
         //排序
         ForestNodeMergerEx.InitAncestors(tree, "0");
 
-        ForestNodeMergerEx.InitTreeSort(tree, "", 0);
+        ForestNodeMergerEx.InitTreeSortEx(tree, "", 0);
 
-        //更新自动组卷节点
-        archiveAutoRuleSync.syncArchiveTreeContractList(vo2Map, proVo2Trees);
+        //更新自动组卷节点 todo 0625 暂时去掉项目级组卷规则同步客户级
+        //archiveAutoRuleSync.syncArchiveTreeContractList(vo2Map, proVo2Trees);
 
         //根据vo的排序和自动组卷信息,刷新新增节点,根据修改标识生成更新节点
         arTreeContractInitService.handleAddAndUpList(vo2Map, allAddList, upList);
@@ -597,7 +597,7 @@ public class ArchiveTreeContractSyncImpl {
                     else  if ( (info.getNodePdfUrl() != null && !info.getNodePdfUrl().equals(archiveFile.getPdfFileUrl()))
                                 || (sort != null && !sort.equals(archiveFile.getSort()))
                                 || (nodeId != null && !nodeId.toString().equals(archiveFile.getNodeId()))
-                                || (StringUtils.isNotEmpty(info.getBusinessTime() ) && !info.getBusinessTime().equals(archiveFile.getFileTime()))
+                                || (StringUtils.isNotEmpty(info.getBusinessTime() ) && !info.getBusinessTime().replaceAll("[^0-9]", "").equals(archiveFile.getFileTime()))
                                 || (info.getEVisaPdfSize()!= null && info.getEVisaPdfSize() > 0L && !info.getEVisaPdfSize().equals(archiveFile.getFileSize()))
                     ) {
 
@@ -619,8 +619,10 @@ public class ArchiveTreeContractSyncImpl {
                         archiveFile.setFilePage(info.getEVisaPdfPage());
                         archiveFile.setFileSize(info.getEVisaPdfSize());
                         archiveFile.setIsCertification(1);
-                        archiveFile.setCertificationTime(info.getBusinessTime());
-                        archiveFile.setFileTime(info.getBusinessTime());
+                        if (StringUtils.isNotEmpty(info.getBusinessTime())) {
+                            archiveFile.setCertificationTime(info.getBusinessTime());
+                            archiveFile.setFileTime(info.getBusinessTime());
+                        }
                         archiveFile.setSort(sort);
                         if (nodeId != null && !nodeId.toString().equals(archiveFile.getNodeId())) {
                             archiveFile.setNodeId(nodeId.toString());
@@ -688,14 +690,14 @@ public class ArchiveTreeContractSyncImpl {
             // 特殊类型处理:根据 nodeType 匹配对应的数字
             if (treeContractVO6.getNodeType() != null) {
                 switch (treeContractVO6.getNodeType()) {
-                    case 1: // 单位工程 → 匹配 2
-                        return majarDataType.contains("2");
+                    case 1: // 单位工程 → 匹配 9
+                        return majarDataType.contains("9");
                     case 2: // 分部工程 → 匹配 8
                     case 3: // 子分部工程 → 匹配 8
                         return majarDataType.contains("8");
-                    case 4: // 分项工程 → 匹配 9
-                    case 5: // 子分项工程 → 匹配 9
-                        return majarDataType.contains("9");
+                    case 4: // 分项工程 → 匹配 2
+                    case 5: // 子分项工程 → 匹配 2
+                        return majarDataType.contains("2");
                     default:
                         return majarDataType.contains(currentMajor);
                 }

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

@@ -1248,7 +1248,7 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
                     if (wbsTreeContractByP != null) {
                         //处理文件提名
                         String fileName = this.wbsParamService.createFileTitle(wbsTreeContractByP);
-                        if(wbsTreeContract.getMajorDataType()!=null&&wbsTreeContract.getMajorDataType()==6){
+                        if(wbsTreeContractByP.getMajorDataType()!=null&&wbsTreeContractByP.getMajorDataType()==4){
                             String sql1="Select sg_suffix,jl_suffix from m_project_info where id="+wbsTreeContractByP.getProjectId()+" and is_deleted=0";
                             List<ProjectInfo> query = jdbcTemplate.query(sql1, new BeanPropertyRowMapper<>(ProjectInfo.class));
                             if(query.size()>0){
@@ -1295,7 +1295,7 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
                         .eq(WbsTreeContract::getId, wbsTreeContract.getParentId()).eq(WbsTreeContract::getContractId, wbsTreeContract.getContractId()));
                 //处理文件提名
                 fileName1= this.wbsParamService.createFileTitle(wbsTreeContractByP);
-                if(wbsTreeContract.getMajorDataType()!=null&&wbsTreeContract.getMajorDataType()==6){
+                if(wbsTreeContractByP.getMajorDataType()!=null&&wbsTreeContractByP.getMajorDataType()==4){
                     String sql1="Select sg_suffix,jl_suffix from m_project_info where id="+wbsTreeContractByP.getProjectId()+" and is_deleted=0";
                     List<ProjectInfo> query = jdbcTemplate.query(sql1, new BeanPropertyRowMapper<>(ProjectInfo.class));
                     if(query.size()>0){

+ 11 - 2
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java

@@ -3191,7 +3191,11 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
 
             }
             Cell c5 = getCellByAddress(sheet, "C5");
-            c5.setCellValue(lastEndChangeMoney == null ? "0" : lastEndChangeMoney.toString());
+            if (periodId==1890367903054798850L){
+                c5.setCellValue("933212");
+            }else {
+                c5.setCellValue(lastEndChangeMoney == null ? "0" : lastEndChangeMoney.toString());
+            }
             //------------------------------------------------------------本期变更------------------------------------------------------------
             //获取当前合同段的所有已审批并且已下达的变更令
             /*String sql4 = "SELECT id,change_money,change_approval_date from s_change_token_form where contract_id = '"+contractId +"' and approve_status = 2 and command_status = 1 and is_deleted = 0";
@@ -3210,7 +3214,12 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             changeMoneySum = changeMoneySum.setScale(0, RoundingMode.HALF_UP);*/
             BigDecimal changemoneySum = changeMoneyNew.subtract(totalMoney).subtract(lastEndChangeMoney).setScale(0, RoundingMode.HALF_UP);
             Cell e5 = getCellByAddress(sheet, "E5");
-            e5.setCellValue(changemoneySum.toString());
+            if(periodId==1890367903054798850L){
+                e5.setCellValue("515977");
+            }else {
+                e5.setCellValue(changemoneySum.toString());
+            }
+
             //------------------------------------------------------------本期末累计变更------------------------------------------------------------
             //上期末累计变更+本期变更
             BigDecimal lastEndChangeMoneySum = lastEndChangeMoney.add(changemoneySum);

+ 30 - 3
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeServiceImpl.java

@@ -1,13 +1,12 @@
 package org.springblade.manager.service.impl;
 
 import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.google.common.collect.Lists;
-import jodd.util.ArraysUtil;
 import lombok.AllArgsConstructor;
-import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.StringUtils;
 import org.springblade.common.utils.FileUtils;
 import org.springblade.common.utils.SnowFlakeUtil;
@@ -41,7 +40,6 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.File;
 import java.io.IOException;
 import java.util.*;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 @Service
@@ -558,6 +556,35 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
         return this.baseMapper.getTitleRange(Long.valueOf(projectId));
     }
 
+    @Override
+    public Object getQueryValueByNodeType(String wbsId, String projectId, String queryValue, Integer nodeType, Integer majorDataType) {
+        Set<WbsTreePrivate> resultNodes = new HashSet<>();
+        LambdaQueryWrapper<WbsTreePrivate> wrapper = Wrappers.<WbsTreePrivate>lambdaQuery()
+                .eq(WbsTreePrivate::getType, 1).eq(WbsTreePrivate::getWbsId, wbsId).eq(WbsTreePrivate::getProjectId, projectId).eq(WbsTreePrivate::getNodeType, nodeType);
+        if (majorDataType != null) {
+            if (majorDataType > 0) {
+                wrapper.eq(WbsTreePrivate::getMajorDataType, majorDataType);
+            } else if (majorDataType == -1) {
+                wrapper.and(wrapper1 -> wrapper1.isNull(WbsTreePrivate::getMajorDataType).or().eq(WbsTreePrivate::getMajorDataType, 0));
+            }
+        }
+        if (queryValue != null && !queryValue.trim().isEmpty()) {
+            wrapper.like(WbsTreePrivate::getNodeName, queryValue);
+        }
+        List<WbsTreePrivate> wbsTreePrivates = wbsTreePrivateMapper.selectList(wrapper);
+        this.getParentNodesPrivateWbs(wbsTreePrivates, resultNodes, projectId, wbsId);
+        resultNodes.addAll(wbsTreePrivates);
+        List<WbsTreePrivateQueryValueVO> wbsTreePrivateQueryValueVOS = BeanUtil.copyProperties(resultNodes, WbsTreePrivateQueryValueVO.class);
+        if (wbsTreePrivateQueryValueVOS.size() > 0) {
+            List<WbsTreePrivateQueryValueVO> listRoot = wbsTreePrivateQueryValueVOS.stream().filter(f -> f.getParentId() == 0L).collect(Collectors.toList());
+            Map<Long, List<WbsTreePrivateQueryValueVO>> mapAll = wbsTreePrivateQueryValueVOS.stream().collect(Collectors.groupingBy(WbsTreePrivateQueryValueVO::getParentId));
+            this.buildWbsTreePrivateByGetQueryValueByType(listRoot, mapAll);
+            this.sortListPrivate(listRoot);
+            return listRoot;
+        }
+        return null;
+    }
+
     /**
      * 公有排序
      */

+ 10 - 6
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/PdfAddimgUtil.java

@@ -136,9 +136,11 @@ public class PdfAddimgUtil {
         // 添加图片
 
         // 设置图片的新宽度和高度
-        float newWidth = 75f; // 新的宽度
-        float newHeight = image.getScaledHeight() * (newWidth / image.getScaledWidth()); // 根据宽度计算高度
-        image.scaleAbsolute(newWidth, newHeight); // 设置图片的新尺寸
+        if (!type.equals("6")) {
+            float newWidth = 75f; // 新的宽度
+            float newHeight = image.getScaledHeight() * (newWidth / image.getScaledWidth()); // 根据宽度计算高度
+            image.scaleAbsolute(newWidth, newHeight); // 设置图片的新尺寸
+        }
         //调整图片尺寸
         image.setAbsolutePosition(x, y);
         under.addImage(image);
@@ -216,9 +218,11 @@ public class PdfAddimgUtil {
         // 添加图片
 
         // 设置图片的新宽度和高度
-        int newWidth = cmToPx(wide);
-        int newHeight = cmToPx(hign);
-        image.scaleAbsolute(newWidth, newHeight); // 设置图片的新尺寸
+        if (!type.equals("6")) {
+            int newWidth = cmToPx(wide);
+            int newHeight = cmToPx(hign);
+            image.scaleAbsolute(newWidth, newHeight); // 设置图片的新尺寸
+        }
         //调整图片尺寸
        // image.scaleAbsolute(newWidth, newHeight);
         image.setAbsolutePosition(x, y);

+ 50 - 12
blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

@@ -191,28 +191,28 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
         if (userCount > 0L && Func.isEmpty(user.getId())) {
             throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", user.getAccount()));
         }
-        //获取Role信息
+            //获取Role信息
         if (user.getProjectAndUserList().size() <= 0) {
             throw new ServiceException("请关联项目合同段用户类型信息");
         }
         StringBuffer stringBuffer = new StringBuffer();
         Set<String> set = new TreeSet<>();
         List<SaveUserInfoByProjectDTO> projectAndUserList = user.getProjectAndUserList();
-        projectAndUserList.forEach(i -> {
-            String roleId = i.getRoleId();
-            set.add(roleId);
-        });
-        for (String s : set) {
-            stringBuffer.append(s).append(",");
-        }
-        user.setRoleId(String.valueOf(stringBuffer.deleteCharAt(stringBuffer.length() - 1)));
-        //user.setUserType(1); //用户平台-WEB
+            projectAndUserList.forEach(i -> {
+                String roleId = i.getRoleId();
+                set.add(roleId);
+            });
+            for (String s : set) {
+                stringBuffer.append(s).append(",");
+            }
+            user.setRoleId(String.valueOf(stringBuffer.deleteCharAt(stringBuffer.length() - 1)));
+
+       //user.setUserType(1); //用户平台-WEB
         user.setName(user.getRealName());
         boolean b1 = save(user);
-
         if (b1) {
             //绑定用户与部门关系
-            boolean b = submitUserDept(user);
+             boolean b = submitUserDept(user);
             if (b) {
                 //比较
                 if (checkContractId(user.getProjectAndUserList())) {
@@ -221,6 +221,44 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                 //新增用户绑定项目合同段
                 user.getProjectAndUserList().forEach((i) -> {
                     i.setUserId(String.valueOf(user.getId()));
+                    ContractInfo contractInfo = contractClient.getContractById(Long.valueOf(i.getContractId()));
+                    //监理、业主、中西试验室合同段新增
+                    if (contractInfo!=null&&(contractInfo.getContractType() == 2 || contractInfo.getContractType() == 3 || contractInfo.getContractType() == 4)) {
+                        //同步到对应关联的施工合同段中
+                        String sql = "select * from m_contract_relation_jlyz where contract_id_jlyz = " + contractInfo.getId();
+                        List<String> record_SG = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(ContractRelationJlyz.class)).stream().map(ContractRelationJlyz::getContractIdSg).collect(Collectors.toList()).stream().map(String::valueOf).collect(Collectors.toList());
+                        if (record_SG.size() > 0) {
+                           a: for (String sgId : record_SG) {
+                                //当前施工合同段所有用户角色信息
+                                String sql2 = "select * from m_project_assignment_user where contract_id = " + sgId + " and post_id is null and status = 1 and is_deleted = 0";
+                                List<SaveUserInfoByProjectDTO> userRoleInfoSG = jdbcTemplate.query(sql2, new BeanPropertyRowMapper<>(SaveUserInfoByProjectDTO.class));
+                                for (SaveUserInfoByProjectDTO sg : userRoleInfoSG) {
+                                    if (sg.getUserId().equals(i.getUserId())) {
+                                        //如果监理合同段与施工合同段中有相同的用户信息,就移除,一个用户只能存在一个合同段中
+                                        continue a;
+                                    }
+                                }
+                                //重构入参
+                                List<SaveUserInfoByProjectDTO> addList = new ArrayList<>();
+                                    SaveUserInfoByProjectDTO newObj = BeanUtil.copyProperties(i, SaveUserInfoByProjectDTO.class);
+                                    assert newObj != null;
+                                    newObj.setId(SnowFlakeUtil.getId());
+                                    newObj.setContractId(sgId);
+                                    newObj.setStatus(1);
+                                    if (ObjectUtil.isNotEmpty(SecureUtil.getUser())) {
+                                        newObj.setCreateUser(SecureUtil.getUser().getUserId());
+                                        newObj.setCreateDept(Long.parseLong(SecureUtil.getUser().getDeptId().split(",")[0]));
+                                    }
+                                    newObj.setCreateTime(new Date());
+                                    newObj.setIsRecordJlId(contractInfo.getId()); //从监理合同段关联的数据,用于删除关联时同步删除用户信息
+                                    addList.add(newObj);
+                                //保存监理数据时,同步到对应关联的施工合同段中去
+                                if (addList.size() > 0) {
+                                    contractClient.saveUserInfoByProjectThree(addList);
+                                }
+                            }
+                        }
+                    }
                 });
                 contractClient.saveUserInfoByProjectThree(user.getProjectAndUserList());
                 return true;