Bladeren bron

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

LHB 1 maand geleden
bovenliggende
commit
e12259061d
100 gewijzigde bestanden met toevoegingen van 3513 en 268 verwijderingen
  1. 14 0
      blade-common/pom.xml
  2. 1 1
      blade-common/src/main/java/org/springblade/common/utils/AsyncConfigurer.java
  3. 6 3
      blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java
  4. 238 0
      blade-common/src/main/java/org/springblade/common/utils/DeepSeekClient.java
  5. 26 0
      blade-common/src/main/java/org/springblade/common/utils/ForestNodeMergerEx.java
  6. 34 0
      blade-common/src/main/java/org/springblade/common/vo/DeepSeekResponse.java
  7. 7 1
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Oss.java
  8. 5 4
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/CommonFileClient.java
  9. 14 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/FeignConfig.java
  10. 5 5
      blade-ops/blade-admin/pom.xml
  11. 10 0
      blade-ops/blade-resource/pom.xml
  12. 1 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/builder/oss/OssBuilder.java
  13. 1 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/builder/ossre/MinioTemplateRe.java
  14. 1 1
      blade-ops/blade-resource/src/main/java/org/springblade/resource/endpoint/LargeFileEndpoint.java
  15. 5 4
      blade-ops/blade-resource/src/main/java/org/springblade/resource/feign/CommonFileClientImpl.java
  16. 1 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/mapper/OssMapper.xml
  17. 31 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/dto/FindAndReplaceDto.java
  18. 64 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/entity/ArchiveAiName.java
  19. 9 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/trans/ArchiveFileVo.java
  20. 47 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/trans/ArchiveReq.java
  21. 12 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/trans/ArchiveTreeVo.java
  22. 11 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchiveAiNameVO.java
  23. 11 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchiveAiNameVO1.java
  24. 1 1
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchivesAutoVO.java
  25. 19 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchivesAutoVO4.java
  26. 17 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/ArchiveFile.java
  27. 3 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ArchiveFileClient.java
  28. 1 1
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/InformationQueryClient.java
  29. 5 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/ArchiveFileVO.java
  30. 4 1
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/MessageWarningVO.java
  31. 3 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TaskSignInfoVO.java
  32. 16 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/AddBussFileSortDTO.java
  33. 39 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/ServicePlanDTO.java
  34. 34 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/ServicePlanTaskDTO.java
  35. 13 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/ServiceUserDto.java
  36. 5 4
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ArchiveTreeContract.java
  37. 86 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ServicePlan.java
  38. 50 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ServicePlanTask.java
  39. 2 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/TableFile.java
  40. 10 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreeContract.java
  41. 7 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreePrivate.java
  42. 142 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreeSynchronousRecord.java
  43. 55 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/enums/WbsSyncTypeEnum.java
  44. 9 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ArchiveTreeContractClient.java
  45. 9 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ExcelTabClient.java
  46. 5 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ExcelTabClientFallBack.java
  47. 10 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/CurrentNode.java
  48. 11 4
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/EvaSummary.java
  49. 5 1
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/FB02.java
  50. 34 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/ServicePlanTaskVO.java
  51. 50 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/ServicePlanVO.java
  52. 8 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeContractLazyVO.java
  53. 2 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreePrivateQueryValueVO.java
  54. 27 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeSynchronousRecordVo.java
  55. 1 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WtcEva.java
  56. 1 0
      blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/vo/InformationQueryBIMVO.java
  57. 155 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveAiNameController.java
  58. 5 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileAutoController.java
  59. 9 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileController.java
  60. 9 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveInspectionInfoController.java
  61. 89 19
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchivesAutoController.java
  62. 37 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveBuildService.java
  63. 33 13
      blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveFileService.java
  64. 21 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveTreeService.java
  65. 44 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveAiNameMapper.java
  66. 32 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveAiNameMapper.xml
  67. 5 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.java
  68. 27 9
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml
  69. 2 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/TaskSplitMapper.java
  70. 9 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/TaskSplitMapper.xml
  71. 45 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchiveAiNameService.java
  72. 1 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchiveNameService.java
  73. 14 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchivesAutoService.java
  74. 148 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveAiNameServiceImpl.java
  75. 5 5
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveExpertConclusionServiceImpl.java
  76. 148 4
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveNameServiceImpl.java
  77. 772 46
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  78. 13 7
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/TaskSplitServiceImpl.java
  79. 1 1
      blade-service/blade-business/pom.xml
  80. 30 3
      blade-service/blade-business/src/main/java/org/springblade/business/controller/EVisaTaskCheckController.java
  81. 84 8
      blade-service/blade-business/src/main/java/org/springblade/business/controller/InformationWriteQueryController.java
  82. 8 4
      blade-service/blade-business/src/main/java/org/springblade/business/controller/MessageWarningController.java
  83. 5 0
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ArchiveFileClientImpl.java
  84. 3 2
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/InformationQueryClientImpl.java
  85. 28 5
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml
  86. 7 2
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ArchiveFileServiceImpl.java
  87. 8 2
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/MetadataClassificationServiceImpl.java
  88. 10 2
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/TaskServiceImpl.java
  89. 19 2
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/TrialSelfInspectionRecordServiceImpl.java
  90. 10 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/WeatherInfoServiceImpl.java
  91. 47 1
      blade-service/blade-business/src/main/java/org/springblade/business/utils/PDFUtil.java
  92. 144 53
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/ArchiveController.java
  93. 9 4
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/EVController.java
  94. 1 1
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/EVisaService.java
  95. 90 16
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVDataServiceImpl.java
  96. 32 5
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVisaServiceImpl.java
  97. 7 1
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/utils/FileUtils.java
  98. 100 10
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/utils/PDFUtils.java
  99. 6 0
      blade-service/blade-manager/pom.xml
  100. 3 2
      blade-service/blade-manager/src/main/java/com/mixsmart/utils/CustomFunction.java

+ 14 - 0
blade-common/pom.xml

@@ -82,6 +82,20 @@
             <version>0.1.6</version>
         </dependency>
 
+        <!-- 使用OkHttp -->
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>4.9.3</version>
+        </dependency>
+
+        <!-- 使用Gson进行JSON处理 -->
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.9</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 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;

+ 238 - 0
blade-common/src/main/java/org/springblade/common/utils/DeepSeekClient.java

@@ -0,0 +1,238 @@
+package org.springblade.common.utils;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class DeepSeekClient {
+    private static final Logger logger = LoggerFactory.getLogger(DeepSeekClient.class);
+
+    private static final String API_KEY = "sk-16ebce9ef2eb40a68f29d9c2a70fe6b6";
+
+    private static final String API_URL = "https://api.deepseek.com/v1/chat/completions";
+
+    private final OkHttpClient client;
+    private final Gson gson = new Gson();
+
+    public DeepSeekClient() {
+        this.client = new OkHttpClient.Builder()
+                .connectTimeout(30, TimeUnit.SECONDS)
+                .readTimeout(60, TimeUnit.SECONDS)
+                .writeTimeout(30, TimeUnit.SECONDS)
+                .build();
+    }
+
+    /**
+     * 请求 DeepSeek API
+     */
+    public String callDeepSeek(String prompt) throws IOException {
+        DeepSeekRequest request = new DeepSeekRequest(prompt, "deepseek-chat");
+        String requestJson = gson.toJson(request);
+
+        logger.debug("Sending request to DeepSeek: {}", requestJson);
+
+        Request httpRequest = new Request.Builder()
+                .url(API_URL)
+                .post(RequestBody.create(requestJson, MediaType.parse("application/json")))
+                .addHeader("Authorization", "Bearer " + API_KEY)
+                .addHeader("Content-Type", "application/json")
+                .addHeader("Accept", "application/json")
+                .build();
+
+        try (Response response = client.newCall(httpRequest).execute()) {
+            if (!response.isSuccessful()) {
+                String errorBody = response.body() != null ? response.body().string() : "No error body";
+                logger.error("API request failed with code: {}, Body: {}", response.code(), errorBody);
+                throw new IOException("API request failed with code: " + response.code());
+            }
+            return response.body().string();
+        }
+    }
+
+    /**
+     * 解析响应获取结果
+     */
+    public String analysisResponse(String responseJson) {
+        try {
+            JsonObject jsonObject = JsonParser.parseString(responseJson).getAsJsonObject();
+
+            if (jsonObject.has("error")) {
+                JsonObject error = jsonObject.getAsJsonObject("error");
+                String errorMsg = error.get("message").getAsString();
+                logger.error("API returned error: {}", errorMsg);
+                return "Error: " + errorMsg;
+            }
+
+            DeepSeekResponse response = gson.fromJson(jsonObject, DeepSeekResponse.class);
+
+            if (response == null || response.getChoices() == null || response.getChoices().isEmpty()) {
+                logger.error("Invalid response structure");
+                return "Error: Invalid response structure";
+            }
+
+            return response.getChoices().get(0).getMessage().getContent();
+        } catch (Exception e) {
+            logger.error("Error parsing response: {}", e.getMessage());
+            return "Error: " + e.getMessage();
+        }
+    }
+
+    /**
+     * 直接获取精简后的内容
+     */
+    public String getSimplifiedContent(String prompt) {
+        try {
+            String response = callDeepSeek(prompt);
+            return analysisResponse(response);
+        } catch (IOException e) {
+            logger.error("API call failed: {}", e.getMessage());
+            return "Error: " + e.getMessage();
+        }
+    }
+
+    static class DeepSeekRequest {
+        private String model;
+        private Message[] messages;
+        private double temperature = 0.7;
+        private int max_tokens = 2000;
+        private boolean stream = false;
+
+        // 构造函数
+        public DeepSeekRequest(String prompt, String model) {
+            this.model = model;
+
+            // 构造专家级对话上下文
+            this.messages = new Message[]{
+                    // 系统角色设定
+                    new Message("system", "你是有20年经验的建筑施工资料专家,熟悉GB/T50328、GB50202等规范。请用专业术语回答。"),
+
+                    // 用户问题
+                    new Message("user", "作为资料管理专家,请处理以下案卷题名:" + prompt)
+            };
+
+            this.temperature = 0.3;  // 降低随机性,提高专业性
+            this.max_tokens = 1000;
+        }
+
+        // Getters and Setters
+        public String getModel() {
+            return model;
+        }
+
+        public void setModel(String model) {
+            this.model = model;
+        }
+
+        public Message[] getMessages() {
+            return messages;
+        }
+
+        public void setMessages(Message[] messages) {
+            this.messages = messages;
+        }
+
+        public double getTemperature() {
+            return temperature;
+        }
+
+        public void setTemperature(double temperature) {
+            this.temperature = temperature;
+        }
+
+        public int getMax_tokens() {
+            return max_tokens;
+        }
+
+        public void setMax_tokens(int max_tokens) {
+            this.max_tokens = max_tokens;
+        }
+
+        public boolean isStream() {
+            return stream;
+        }
+
+        public void setStream(boolean stream) {
+            this.stream = stream;
+        }
+    }
+
+    static class Message {
+        private String role;
+        private String content;
+
+        // 构造函数
+        public Message(String role, String content) {
+            this.role = role;
+            this.content = content;
+        }
+
+        // Getters and Setters
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+
+        public String getContent() {
+            return content;
+        }
+
+        public void setContent(String content) {
+            this.content = content;
+        }
+    }
+
+    static class DeepSeekResponse {
+        private List<Choice> choices;
+
+        public List<Choice> getChoices() {
+            return choices;
+        }
+
+        public void setChoices(List<Choice> choices) {
+            this.choices = choices;
+        }
+
+        static class Choice {
+            private Message message;
+            private int index;
+            private String finish_reason;
+
+            public Message getMessage() {
+                return message;
+            }
+
+            public void setMessage(Message message) {
+                this.message = message;
+            }
+
+            public int getIndex() {
+                return index;
+            }
+
+            public void setIndex(int index) {
+                this.index = index;
+            }
+
+            public String getFinish_reason() {
+                return finish_reason;
+            }
+
+            public void setFinish_reason(String finish_reason) {
+                this.finish_reason = finish_reason;
+            }
+        }
+    }
+}

+ 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);
+            }
+        }
+    }
+
 }

+ 34 - 0
blade-common/src/main/java/org/springblade/common/vo/DeepSeekResponse.java

@@ -0,0 +1,34 @@
+package org.springblade.common.vo;
+
+import lombok.Data;
+
+import java.util.List;
+@Data
+public class DeepSeekResponse {
+    private String id;
+    private String object;
+    private long created;
+    private String model;
+    private List<Choice> choices;
+    private Usage usage;
+
+    @Data
+    public static class Choice {
+        private int index;
+        private Message message;
+        private String finish_reason;
+    }
+
+    @Data
+    public static class Message {
+        private String role;
+        private String content;
+    }
+
+    @Data
+    public static class Usage {
+        private int prompt_tokens;
+        private int completion_tokens;
+        private int total_tokens;
+    }
+}

+ 7 - 1
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Oss.java

@@ -52,6 +52,13 @@ public class Oss extends TenantEntity {
      */
     @ApiModelProperty(value = "资源地址")
     private String endpoint;
+
+    /**
+     * oss地址
+     */
+    @ApiModelProperty(value = "资源地址")
+    private String transformEndpoint;
+
     /**
      * accessKey
      */
@@ -83,5 +90,4 @@ public class Oss extends TenantEntity {
     @ApiModelProperty(value = "备注")
     private String remark;
 
-
 }

+ 5 - 4
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/CommonFileClient.java

@@ -6,23 +6,24 @@ import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
 import org.springframework.web.multipart.MultipartFile;
 
 @FeignClient(
-        value = AppConstant.APPLICATION_RESOURCE_NAME
+        value = AppConstant.APPLICATION_RESOURCE_NAME,configuration = FeignConfig.class
 )
 public interface CommonFileClient {
 
     String API_PREFIX = "/CommonFileApi";
 
     @PostMapping(value = API_PREFIX + "/pngOrJpgToPdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-    NewBladeFile pngOrJpgToPdf(MultipartFile file);
+    NewBladeFile pngOrJpgToPdf(@RequestPart("file")MultipartFile file);
 
     @PostMapping(value = API_PREFIX + "/wordToPdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-    NewBladeFile wordToPdf(MultipartFile file);
+    NewBladeFile wordToPdf(@RequestPart("file")MultipartFile file);
 
     @PostMapping(value = API_PREFIX + "/excelToPdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-    NewBladeFile excelToPdf(MultipartFile file);
+    NewBladeFile excelToPdf(@RequestPart("file")MultipartFile file);
 
     @PostMapping(value = API_PREFIX + "/getPdfNum")
     String getPdfNum(@RequestParam("url") String url);

+ 14 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/FeignConfig.java

@@ -0,0 +1,14 @@
+package org.springblade.resource.feign;
+
+import feign.codec.Encoder;
+import feign.form.spring.SpringFormEncoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class FeignConfig {
+    @Bean
+    public Encoder feignFormEncoder() {
+        return new SpringFormEncoder();
+    }
+}

+ 5 - 5
blade-ops/blade-admin/pom.xml

@@ -91,11 +91,11 @@
             <artifactId>spring-cloud-starter-security</artifactId>
         </dependency>
         <!--Taobao-Sdk-->
-        <dependency>
-            <groupId>com.taobao</groupId>
-            <artifactId>taobao-sdk</artifactId>
-            <version>20201116</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.taobao</groupId>-->
+<!--            <artifactId>taobao-sdk</artifactId>-->
+<!--            <version>20201116</version>-->
+<!--        </dependency>-->
     </dependencies>
 
     <build>

+ 10 - 0
blade-ops/blade-resource/pom.xml

@@ -149,6 +149,16 @@
             <groupId>org.springblade</groupId>
             <artifactId>blade-starter-oss</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.github.openfeign.form</groupId>
+            <artifactId>feign-form</artifactId>
+            <version>3.8.0</version>
+        </dependency>
+        <dependency>
+            <groupId>io.github.openfeign.form</groupId>
+            <artifactId>feign-form-spring</artifactId>
+            <version>3.8.0</version>
+        </dependency>
     </dependencies>
 
 

+ 1 - 0
blade-ops/blade-resource/src/main/java/org/springblade/resource/builder/oss/OssBuilder.java

@@ -98,6 +98,7 @@ public class OssBuilder {
                 oss.setEndpoint("https://xinan1.zos.ctyun.cn");
             }
         }
+		oss.setEndpoint("https://xinan1.zos.ctyun.cn");
 		System.out.println("oss111="+oss.getEndpoint());
 		Oss ossCached = ossPool.get(tenantId);
 		OssTemplate template = templatePool.get(tenantId);

+ 1 - 0
blade-ops/blade-resource/src/main/java/org/springblade/resource/builder/ossre/MinioTemplateRe.java

@@ -483,6 +483,7 @@ public class MinioTemplateRe implements OssTemplateRe {
 	//	ossProperties.setEndpoint("http://183.247.216.148:9000");
 		//return ossProperties.getEndpoint();
 		return "http://183.247.216.148:9000";
+		//return "http://8.130.108.204:9000";
 	}
 
 

+ 1 - 1
blade-ops/blade-resource/src/main/java/org/springblade/resource/endpoint/LargeFileEndpoint.java

@@ -286,7 +286,7 @@ public class LargeFileEndpoint {
 
             if (param.getFilename().contains("pdf") || param.getFilename().contains("PDF")) {
                 try {
-                    PdfReader pdfReader = new PdfReader(inputStream);
+                    PdfReader pdfReader = new PdfReader(new FileInputStream(file1));
                     int pages = pdfReader.getNumberOfPages();
                     //获取文件页数
                     newBladeFile.setPage(pages);

+ 5 - 4
blade-ops/blade-resource/src/main/java/org/springblade/resource/feign/CommonFileClientImpl.java

@@ -4,7 +4,6 @@ import com.aspose.cells.SaveFormat;
 import com.aspose.words.DocumentBuilder;
 import com.itextpdf.text.Image;
 import com.itextpdf.text.Rectangle;
-import com.itextpdf.text.Utilities;
 import com.itextpdf.text.pdf.PdfWriter;
 import lombok.AllArgsConstructor;
 import org.apache.pdfbox.pdmodel.PDDocument;
@@ -16,6 +15,7 @@ import org.springblade.core.tool.utils.FileUtil;
 import org.springblade.core.tool.utils.IoUtil;
 import org.springblade.resource.builder.oss.OssBuilder;
 import org.springblade.resource.vo.NewBladeFile;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -33,11 +33,12 @@ public class CommonFileClientImpl implements CommonFileClient {
      */
     private final OssBuilder ossBuilder;
 
+
     /**
      * png 和 jpg 转 pdf
      */
     @Override
-    public NewBladeFile pngOrJpgToPdf(MultipartFile file) {
+    public NewBladeFile pngOrJpgToPdf(@RequestParam("file")MultipartFile file) {
         String pdfFileUrl = "";
         InputStream pdfInput = null;
         try {
@@ -103,7 +104,7 @@ public class CommonFileClientImpl implements CommonFileClient {
      * word 转 pdf
      */
     @Override
-    public NewBladeFile wordToPdf(MultipartFile file) {
+    public NewBladeFile wordToPdf(@RequestParam("file")MultipartFile file) {
         String pdfFileUrl = "";
         int page = 0;
         InputStream pdfInput = null;
@@ -149,7 +150,7 @@ public class CommonFileClientImpl implements CommonFileClient {
      * excel 转 pdf
      */
     @Override
-    public NewBladeFile excelToPdf(MultipartFile file) {
+    public NewBladeFile excelToPdf(@RequestParam("file") MultipartFile file) {
         String pdfFileUrl = "";
         int page = 0;
         org.apache.poi.ss.usermodel.Workbook ss = null;

+ 1 - 0
blade-ops/blade-resource/src/main/java/org/springblade/resource/mapper/OssMapper.xml

@@ -14,6 +14,7 @@
         <result column="oss_code" property="ossCode"/>
         <result column="category" property="category"/>
         <result column="endpoint" property="endpoint"/>
+        <result column="transform_endpoint" property="transformEndpoint"/>
         <result column="access_key" property="accessKey"/>
         <result column="secret_key" property="secretKey"/>
         <result column="bucket_name" property="bucketName"/>

+ 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;
+}

+ 64 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/entity/ArchiveAiName.java

@@ -0,0 +1,64 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.archive.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import org.springblade.core.mp.base.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 实体类
+ *
+ * @author BladeX
+ * @since 2025-07-03
+ */
+@Data
+@TableName("u_archive_ai_name")
+@EqualsAndHashCode(callSuper = true)
+public class ArchiveAiName extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	* 项目ID
+	*/
+		private Long projectId;
+	/**
+	* 合同段ID
+	*/
+		private Long contractId;
+	/**
+	* 任务ID
+	*/
+		private Long taskId;
+	/**
+	* 档案案卷ID
+	*/
+		private Long archiveAutoId;
+	/**
+	* 档案案卷题目
+	*/
+		private String archiveName;
+	/**
+	* 档案案卷题目AI
+	*/
+		private String archiveNameAi;
+
+
+}

+ 9 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/trans/ArchiveFileVo.java

@@ -48,6 +48,7 @@ public class ArchiveFileVo {
     private Integer width;
     private Integer height;
     private String isArchive;
+    private Integer sort;
 
     public String getId() {
         return id;
@@ -400,4 +401,12 @@ public class ArchiveFileVo {
     public void setIsArchive(String isArchive) {
         this.isArchive = isArchive;
     }
+
+    public Integer getSort() {
+        return sort;
+    }
+
+    public void setSort(Integer sort) {
+        this.sort = sort;
+    }
 }

+ 47 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/trans/ArchiveReq.java

@@ -22,6 +22,21 @@ public class ArchiveReq {
 
     String remoteId;
 
+    /**
+     * 默认0,用nodeId去找
+     * 为1时,用contractName,nodeName找
+     */
+    Integer transType= 0;
+
+    //合同段名称
+    String contractName;
+
+    //节点名称
+    String nodeName;
+
+    //0 施工,1 监理
+    Integer nodeType;
+
     //归档树
     List<ArchiveTreeVo> trees=new ArrayList<>();
     //案卷
@@ -102,4 +117,36 @@ public class ArchiveReq {
     public void setProjectName(String projectName) {
         this.projectName = projectName;
     }
+
+    public Integer getTransType() {
+        return transType;
+    }
+
+    public void setTransType(Integer transType) {
+        this.transType = transType;
+    }
+
+    public String getContractName() {
+        return contractName;
+    }
+
+    public void setContractName(String contractName) {
+        this.contractName = contractName;
+    }
+
+    public String getNodeName() {
+        return nodeName;
+    }
+
+    public void setNodeName(String nodeName) {
+        this.nodeName = nodeName;
+    }
+
+    public Integer getNodeType() {
+        return nodeType;
+    }
+
+    public void setNodeType(Integer nodeType) {
+        this.nodeType = nodeType;
+    }
 }

+ 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;
+    }
 }

+ 11 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchiveAiNameVO.java

@@ -0,0 +1,11 @@
+package org.springblade.archive.vo;
+
+import lombok.Data;
+
+@Data
+public class ArchiveAiNameVO {
+    private Long taskId;
+    private String taskTime;
+    private Integer status;
+    private Integer num;
+}

+ 11 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchiveAiNameVO1.java

@@ -0,0 +1,11 @@
+package org.springblade.archive.vo;
+
+import lombok.Data;
+
+@Data
+public class ArchiveAiNameVO1 {
+    private Long id;
+    private Long archiveAutoId;
+    private String archiveNameAi;
+    private Integer status;
+}

+ 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;
+}

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

@@ -280,6 +280,9 @@ public class ArchiveFile extends BaseEntity {
     @ApiModelProperty("卷内索引")
     private Integer fid;
 
+    @ApiModelProperty("卷内排序")
+    private Integer archiveSort;
+
     //整改情况,0,无整改,1 待整改,2 已整改
     @ApiModelProperty("整改情况")
     private Integer rectification;
@@ -328,6 +331,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) {
@@ -389,6 +394,10 @@ public class ArchiveFile extends BaseEntity {
         }
 
         this.setSort(vo.getOrderNum());
+        //设置了专门排序的就设置
+        if (vo.getSort()!= null) {
+            this.setSort(vo.getSort());
+        }
 
 
         String type = vo.getType();
@@ -420,4 +429,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]", "");
+    }
 }

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

@@ -164,4 +164,7 @@ public interface ArchiveFileClient {
 
     @PostMapping(API_PREFIX + "/saveArchiveFileByBIM")
     void saveArchiveFileByBIM(@RequestBody ArchiveFile archiveFile);
+
+    @PostMapping(API_PREFIX + "/getArchiveFileByArchiveIds")
+    List<ArchiveFile> getArchiveFileByArchiveIds(@RequestParam String archiveIds);
 }

+ 1 - 1
blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/InformationQueryClient.java

@@ -97,7 +97,7 @@ public interface InformationQueryClient {
     void insert(@RequestBody InformationQuery iq);
 
     @PostMapping(API_PREFIX+"/getInfoByWbsId")
-    InformationQuery getInfoByWbsId(@RequestParam Long wbsId);
+    InformationQuery getInfoByWbsId(@RequestParam Long wbsId,@RequestParam Integer classify);
 
     @PostMapping(API_PREFIX+"/updateInformationQuery1")
     void update(@RequestBody  InformationQuery iq);

+ 5 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/ArchiveFileVO.java

@@ -97,4 +97,9 @@ public class ArchiveFileVO extends ArchiveFile {
     private String archiveName;
 
     private Integer extType;
+
+    /**
+     * 是否已组卷
+     */
+    private Integer isArchive;
 }

+ 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("任务催办未读数量")

+ 3 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TaskSignInfoVO.java

@@ -89,4 +89,7 @@ public class TaskSignInfoVO {
     @ApiModelProperty("电签内容")
     private String signSmg;
 
+    @ApiModelProperty("是否可以签章:0-表示可以,否则就不行")
+    private Integer isSignature;
+
 }

+ 16 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/AddBussFileSortDTO.java

@@ -0,0 +1,16 @@
+package org.springblade.manager.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class AddBussFileSortDTO {
+    private String[] list;
+    private Integer classify;
+    private Long contractId;
+    private String id;
+    private Integer type;
+}

+ 39 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/ServicePlanDTO.java

@@ -0,0 +1,39 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.manager.dto;
+
+import org.springblade.manager.entity.ServicePlan;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 数据传输对象实体类
+ *
+ * @author BladeX
+ * @since 2025-06-25
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ServicePlanDTO extends ServicePlan {
+	private static final long serialVersionUID = 1L;
+
+    private String userId;
+
+    private String planStartTime1;
+
+    private String planEndTime1;
+}

+ 34 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/ServicePlanTaskDTO.java

@@ -0,0 +1,34 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.manager.dto;
+
+import org.springblade.manager.entity.ServicePlanTask;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 数据传输对象实体类
+ *
+ * @author BladeX
+ * @since 2025-06-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ServicePlanTaskDTO extends ServicePlanTask {
+	private static final long serialVersionUID = 1L;
+
+}

+ 13 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/ServiceUserDto.java

@@ -0,0 +1,13 @@
+package org.springblade.manager.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ServiceUserDto {
+    private String userId;
+    private String userName;
+}

+ 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();

+ 86 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ServicePlan.java

@@ -0,0 +1,86 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.manager.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDate;
+
+import io.swagger.annotations.ApiModelProperty;
+import org.springblade.core.mp.base.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 实体类
+ *
+ * @author BladeX
+ * @since 2025-06-25
+ */
+@Data
+@TableName("m_service_plan")
+@EqualsAndHashCode(callSuper = true)
+public class ServicePlan extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	* 填写类型 1月度服务计划 2服务完成确认单
+	*/
+    @ApiModelProperty(value = "填写类型 1月度服务计划 2服务完成确认单")
+		private Integer fileInType;
+    /**
+     * 项目Id
+     */
+    @ApiModelProperty(value = "项目Id")
+        private Long projectId;
+    /**
+     * 合同段Id
+     */
+    @ApiModelProperty(value = "合同段Id")
+        private Long contractId;
+	/**
+	* 计划开始时间
+	*/
+    @ApiModelProperty(value = "计划开始时间")
+		private LocalDate planStartTime;
+	/**
+	* 计划结束时间
+	*/
+    @ApiModelProperty(value = "计划结束时间")
+		private LocalDate planEndTime;
+    /**
+     * 编写人
+     */
+    @ApiModelProperty(value = "编写人ID 逗号拼接")
+        private String writeUser;
+	/**
+	* 发送人员
+	*/
+    @ApiModelProperty(value = "发送人员ID 逗号拼接")
+		private String sendUser;
+    /**
+     * pdf路径
+      */
+    @ApiModelProperty(value = "pdf路径")
+        private String pdfUrl;
+
+
+
+
+
+}

+ 50 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ServicePlanTask.java

@@ -0,0 +1,50 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.manager.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import org.springblade.core.mp.base.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 实体类
+ *
+ * @author BladeX
+ * @since 2025-06-27
+ */
+@Data
+@TableName("m_service_plan_task")
+@EqualsAndHashCode(callSuper = true)
+public class ServicePlanTask extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	* 服务计划主键ID
+	*/
+		private Long servicePlanId;
+	/**
+	* 发送人ID
+	*/
+		private Long userId;
+
+		private Integer isCancel;
+
+
+}

+ 2 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/TableFile.java

@@ -96,4 +96,6 @@ public class TableFile implements Serializable {
     @ApiModelProperty(value = "1施工2监理")
     private Integer classify;
 
+    private Integer sort;
+
 }

+ 10 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreeContract.java

@@ -1,5 +1,8 @@
 package org.springblade.manager.entity;
 
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.annotations.ApiModelProperty;
@@ -23,8 +26,12 @@ public class WbsTreeContract extends BaseEntity {
      * 主键id
      */
     @ApiModelProperty(value = "主键id")
+    @TableId(value = "p_key_id", type = IdType.INPUT)
     private Long pKeyId;
 
+    @TableField
+    private Long id;
+
 
     @ApiModelProperty(value = "新节点Id")
     private Long pId;
@@ -318,6 +325,9 @@ public class WbsTreeContract extends BaseEntity {
     @JsonProperty(value = "treePId")
     private Long treePId;
 
+    @ApiModelProperty(value = "是否完成日期填写 1是 2否")
+    private Integer dateIsComplete;
+
 
 
 }

+ 7 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreePrivate.java

@@ -1,5 +1,8 @@
 package org.springblade.manager.entity;
 
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.annotations.ApiModelProperty;
@@ -20,9 +23,13 @@ public class WbsTreePrivate extends BaseEntity {
     /**
      * 主键
      */
+    @TableId(value = "p_key_id", type = IdType.INPUT)
     @JsonProperty(value = "pKeyId")
     private Long pKeyId;
 
+    @TableField
+    private Long id;
+
     @ApiModelProperty(value = "pid")
     private Long pId;
 

+ 142 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreeSynchronousRecord.java

@@ -0,0 +1,142 @@
+package org.springblade.manager.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * WBS同步记录表
+ *
+ * @author LHB
+ * @TableName m_wbs_tree_synchronous_record
+ */
+@TableName(value = "m_wbs_tree_synchronous_record")
+@Data
+public class WbsTreeSynchronousRecord {
+    /**
+     * id111
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 项目id
+     */
+    private Long projectId;
+
+    /**
+     * 项目名称
+     */
+    private String projectName;
+
+
+    @TableField("`range`")
+    private Integer range;
+    /**
+     * 同步范围名称
+     */
+    private String rangeName;
+    /**
+     * 合同段范围 逗号拼接的编号 101.未填报 102.已填报-未上报 103.未上报 104.待审批 105.已审批
+     */
+    private String contractRange;
+    /**
+     * 合同段范围名称
+     */
+    private String contractRangeName;
+
+    /**
+     * 同步源Id
+     */
+    private Long templateId;
+
+    /**
+     * 同步源名称
+     */
+    private String templateName;
+
+    /**
+     * 同步类型 逗号拼接的编号 1.新增表单 2.清表配置 3.元素配置 4.电签配置 5.公式配置 6.默认值配置 7.表单排序
+     */
+    private String type;
+
+    /**
+     * 同步类型名称
+     */
+    private String typeName;
+
+    /**
+     * 同步节点id 多个节点
+     */
+    private String nodeId;
+
+    /**
+     * 同步节点名称
+     */
+    private String nodeName;
+    /**
+     * 表单Ids     range = 4 强制同步时  当前数据为同步源
+     */
+    private String formIds;
+    /**
+     * 同步节点数量
+     */
+    private Integer nodeNum;
+    /**
+     * 已同步数量
+     */
+    private Integer nodeNumEnd;
+
+    /**
+     * 是否删除(0-未删除,1-删除)
+     */
+    private Integer isDeleted;
+
+    /**
+     * 状态(0-未同步,1-正在同步,2-已同步,3-同步失败)
+     */
+    private Integer status;
+    /**
+     * 状态(0-未同步,1-正在同步,2-已同步,3-同步失败)
+     */
+    @TableField(exist = false)
+    private String statusName;
+    /**
+     * 错误信息
+     */
+    @TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+    private String errorMsg;
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 创建人
+     */
+    private String createUser;
+
+    /**
+     * 创建人id
+     */
+    private Long createUserId;
+
+    /**
+     * 修改时间
+     */
+    private Date updateTime;
+
+    /**
+     * 修改人
+     */
+    private String updateUser;
+
+    /**
+     * 修改人id
+     */
+    private Long updateUserId;
+}

+ 55 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/enums/WbsSyncTypeEnum.java

@@ -0,0 +1,55 @@
+package org.springblade.manager.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springblade.core.tool.utils.ObjectUtil;
+
+/**
+ * @author LHB
+ */
+@Getter
+@AllArgsConstructor
+public enum WbsSyncTypeEnum {
+    UNKNOWN(-1, ""),
+    INSERT_FORM(1, "新增表单"),
+    CLEAR_TABLE_CONFIG(2, "清表配置"),
+    ELEMENT_CONFIG(3, "元素配置"),
+    E_VISA_CONFIG(4, "电签配置"),
+    FORMULA_CONFIG(5, "公式配置"),
+    DEFAULT_VALUE_CONFIG(6, "默认值配置"),
+    FORM_SORT(7, "表单排序"),
+    /**
+     * 合同段同步范围
+     */
+    NOT_FILLED_IN(101, "未填报"),
+    ALREADY_FILLED_IN_NOT_REPORTED(102, "已填报-未上报"),
+    NOT_REPORTED(103, "未上报"),
+    PENDING_APPROVAL(104, "待审批"),
+    APPROVED(105, "已审批"),
+    ;
+
+    /**
+     * 编码
+     */
+    public Integer code;
+
+    /**
+     * 描述
+     */
+    private String desc;
+
+    /**
+     * 根据编码获取枚举描述
+     */
+    public static String getByCode(int code) {
+        // 判断code
+        if(ObjectUtil.isNotEmpty(code)){
+            for (StorageTypeEnum storageTypeEnum : StorageTypeEnum.values()) {
+                if (storageTypeEnum.getCode() == code) {
+                    return storageTypeEnum.getDesc();
+                }
+            }
+        }
+        return WbsSyncTypeEnum.UNKNOWN.getDesc();
+    }
+}

+ 9 - 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);
 
@@ -79,4 +82,10 @@ public interface ArchiveTreeContractClient {
     @PostMapping(API_PREFIX+"/adsArchiveTreeContract")
     public void addArchiveTreeContract(@RequestBody List<ArchiveTreeContract> archiveTreeContracts,@RequestParam Long rootId);
 
+
+    @PostMapping(API_PREFIX + "/getNodeIdByName")
+    Long getNodeIdByName(@RequestParam("projectName") String projectName,
+                         @RequestParam("contractName") String contractName,
+                         @RequestParam("nodeName") String nodeName);
+
 }

+ 9 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ExcelTabClient.java

@@ -58,6 +58,15 @@ public interface ExcelTabClient {
                  @RequestParam String projectId,
                  @RequestHeader("Blade-Auth") String header) throws Exception;
 
+
+
+    @PostMapping(API_PREFIX + "/synPdfKeyInfo")
+    R synPdfKeyInfo(@RequestParam String contractId,
+                 @RequestParam String nodeIds,
+                 @RequestParam String classify,
+                 @RequestParam String projectId,
+                 @RequestHeader("Blade-Auth") String header) throws Exception;
+
     @PostMapping(API_PREFIX + "/getArchiveTabList")
     List<ExcelTab> getArchiveTabList();
 

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

@@ -51,6 +51,11 @@ public class ExcelTabClientFallBack implements ExcelTabClient {
         return null;
     }
 
+    @Override
+    public R synPdfKeyInfo(String contractId, String nodeIds, String classify, String projectId, String header) throws Exception {
+        return null;
+    }
+
     @Override
     public List<ExcelTab> getArchiveTabList(){
         return null;

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

@@ -1,7 +1,9 @@
 package org.springblade.manager.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import org.springblade.manager.entity.WbsTreePrivate;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
@@ -37,6 +39,14 @@ public class CurrentNode {
     private Long wbsNodeId;
     /**节点类型*/
     private Integer nodeType;
+    /**节点排序*/
+    private Integer sort;
+    /**节点名称*/
+    private String title;
+    /**创建时间*/
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime  createTime;
     /**treeCode*/
     private List<String> treeCode =new ArrayList<>();
     /**

+ 11 - 4
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/EvaSummary.java

@@ -60,17 +60,24 @@ public abstract class EvaSummary<T> {
                     ));
 
             int index=1;
+            int pageSizex=0;
             for (Map.Entry<Integer, List<T>> entry : pages.entrySet()) {
                 /*每一页就是一条插入记录*/
                 int k = entry.getKey();
+
                 if (wtcList.size() <= k) {
                     break;
                 }
                 List<T> v = entry.getValue();
                 /*是否是最后一页,fb02最后一页汇总用到*/
                 boolean isLast=index==pages.size();
+                // 算出 每页大小
+                if(pageSizex<=0){
+                    pageSizex = v.size();
+                }
                 index++;
-                List<Object> l=this.fds.stream().map(fd->putEd(k,v,fd,datas,isLast)).collect(Collectors.toList());
+                int finalPageSizex = pageSizex;
+                List<Object> l=this.fds.stream().map(fd->putEd(finalPageSizex,k,v,fd,datas,isLast)).collect(Collectors.toList());
                 l.add(wtcList.get(k).getPKeyId());
                 params.add(l.toArray());
             }
@@ -79,8 +86,8 @@ public abstract class EvaSummary<T> {
         return false;
     }
 
-    /**把数据放到对应元素*/
-    private String putEd(int pn, List<T> items, FormData fd,List<T>datas,boolean isLastPage){
+    /**把数据放到对应元素(一页一行的添加)*/
+    private String putEd(int pageSize,int pn, List<T> items, FormData fd,List<T>datas,boolean isLastPage){
         BiFunction<List<T>,Integer,List<Object>> fc = this.fm.get(fd.getCode());
         if(fc!=null) {
             List<Object> data ;
@@ -93,7 +100,7 @@ public abstract class EvaSummary<T> {
                     data=Collections.singletonList("");
                 }
             }else {
-                data  = fc.apply(items,pn);
+                data  = fc.apply(items,pn*pageSize);
             }
             for (int i = 0; i < data.size(); i++) {
                 Coords c = fd.getCoordsList().get(i);

+ 5 - 1
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/FB02.java

@@ -6,9 +6,13 @@ import lombok.EqualsAndHashCode;
 import org.apache.commons.lang.StringUtils;
 import org.springblade.common.utils.BaseUtils;
 import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.IoUtil;
 import org.springblade.manager.dto.FormData;
 import org.springblade.manager.entity.FormulaDataBlock;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.*;
@@ -56,7 +60,7 @@ public class FB02 extends EvaSummary<Item02>{
                   this.fm.put(fd.getCode(),(List<Item02> l,Integer pn)->l.stream().map(Item02::getSubItem).collect(Collectors.toList()));
               }else if(fd.getEName().contains("序号")){
                   this.sn=fd;
-                  this.fm.put(fd.getCode(),(List<Item02> l,Integer pn)->IntStream.range(0, l.size()).boxed().map(i->i+pn*l.size()+1).collect(Collectors.toList()));
+                  this.fm.put(fd.getCode(),(List<Item02> l,Integer pn)->IntStream.range(0, l.size()).boxed().map(i->i+pn+1).collect(Collectors.toList()));
               }else if(fd.getEName().contains("实测项目")){
                   this.name=fd;
                   this.fm.put(fd.getCode(),(List<Item02> l,Integer pn)->l.stream().map(Item02::getName).collect(Collectors.toList()));

+ 34 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/ServicePlanTaskVO.java

@@ -0,0 +1,34 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.manager.vo;
+
+import org.springblade.manager.entity.ServicePlanTask;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 视图实体类
+ *
+ * @author BladeX
+ * @since 2025-06-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ServicePlanTaskVO extends ServicePlanTask {
+	private static final long serialVersionUID = 1L;
+
+}

+ 50 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/ServicePlanVO.java

@@ -0,0 +1,50 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.manager.vo;
+
+import org.springblade.manager.entity.ServicePlan;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 视图实体类
+ *
+ * @author BladeX
+ * @since 2025-06-25
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ServicePlanVO extends ServicePlan {
+	private static final long serialVersionUID = 1L;
+   //1计划中  2协同中-甲方  3协同中-系统  4已计划
+    private String statusValue;
+    //是否可编辑
+   private Boolean isEdit;
+    //计划开始时间
+   private String startTime;
+   //计划结束时间
+   private String endTime;
+   //发送人
+   private String sendUserName;
+
+   private String writeUserName;
+
+   private String planTime;
+
+
+
+}

+ 8 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeContractLazyVO.java

@@ -19,6 +19,9 @@ public class WbsTreeContractLazyVO implements Serializable {
     @JsonProperty(value = "pKeyId")
     private Long pKeyId;
 
+    @ApiModelProperty("pkeyId的节点父级id")
+    private Long pId;
+
     @ApiModelProperty("节点主键pKeyId别名,其他接口要用")
     private Long primaryKeyId;
 
@@ -133,4 +136,9 @@ public class WbsTreeContractLazyVO implements Serializable {
     @ApiModelProperty(value = "关联后管节点ID")
     private Long isTypePrivatePid;
 
+    @ApiModelProperty(value = "是否完成日期填写1是2否")
+    private Integer dateIsComplete;
+
+
+
 }

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

@@ -14,4 +14,6 @@ public class WbsTreePrivateQueryValueVO extends WbsTreePrivate {
 
     private Boolean hasChildren;
 
+    private Long primaryKeyId;
+
 }

+ 27 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeSynchronousRecordVo.java

@@ -0,0 +1,27 @@
+package org.springblade.manager.vo;
+
+import lombok.Data;
+
+/**
+ *
+ * @author LHB
+ */
+@Data
+public class WbsTreeSynchronousRecordVo {
+    /**
+     * id
+     */
+    private Long id;
+    /**
+     * 项目名称
+     */
+    private String name;
+    /**
+     * wbsId
+     */
+    private String wbsId;
+    /**
+     * type = 1 公共  type = 2 私有
+     */
+    private Integer type;
+}

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

@@ -19,6 +19,7 @@ public class WtcEva {
     private String treeCode;
     private Long parentId;
     private Long pKeyId;
+    private Long pId;//pkeyid 的父级 id
     public boolean isEva(){
       return Objects.requireNonNull(this.nodeName, "nodeName不能为空").contains("评定");
   }

+ 1 - 0
blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/vo/InformationQueryBIMVO.java

@@ -11,6 +11,7 @@ public class InformationQueryBIMVO {
 
     @TableField("id")
     private String id;
+
     @TableField("父级id")
     private String parentId;
 

+ 155 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveAiNameController.java

@@ -0,0 +1,155 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.archive.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import lombok.AllArgsConstructor;
+import javax.validation.Valid;
+
+import org.springblade.archive.vo.ArchiveAiNameVO;
+import org.springblade.archive.vo.ArchiveAiNameVO1;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.RequestParam;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springblade.archive.entity.ArchiveAiName;
+import org.springblade.archive.service.IArchiveAiNameService;
+import org.springblade.core.boot.ctrl.BladeController;
+
+import java.util.List;
+
+/**
+ *  控制器
+ *
+ * @author BladeX
+ * @since 2025-07-03
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/archiveainame")
+@Api(value = "", tags = "接口")
+public class ArchiveAiNameController extends BladeController {
+
+	private final IArchiveAiNameService archiveAiNameService;
+
+	/**
+	 * 详情
+	 */
+	@GetMapping("/detail")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "详情", notes = "传入archiveAiName")
+	public R<ArchiveAiName> detail(ArchiveAiName archiveAiName) {
+		ArchiveAiName detail = archiveAiNameService.getOne(Condition.getQueryWrapper(archiveAiName));
+		return R.data(detail);
+	}
+
+	@GetMapping("/getArchiveAiTask")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "获取AI任务", notes = "获取AI任务")
+	public R<List<ArchiveAiNameVO>>getArchiveAiTask(Long projectId, Long contractId){
+		return R.data(archiveAiNameService.getArchiveAiTask(projectId,contractId));
+	}
+
+	@GetMapping("/deletedArchiveAiTask")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "删除AI任务", notes = "删除AI任务")
+	public R deletedArchiveAiTask(Long taskId){
+		return R.status(archiveAiNameService.deletedArchiveAiTask(taskId));
+	}
+	@GetMapping("/page")
+	@ApiOperationSupport(order = 2)
+	@ApiOperation(value = "分页")
+	public R<IPage<ArchiveAiName>>page(Long taskId,Long projectId,Long contractId,Query query){
+		if(taskId!=null){
+			IPage<ArchiveAiName> pages = archiveAiNameService.page(Condition.getPage(query), new LambdaQueryWrapper<>(ArchiveAiName.class).eq(ArchiveAiName::getTaskId,taskId).eq(ArchiveAiName::getStatus,2));
+			return R.data(pages);
+		}else {
+			IPage<ArchiveAiName> pages = archiveAiNameService.page(Condition.getPage(query), new LambdaQueryWrapper<>(ArchiveAiName.class).eq(ArchiveAiName::getProjectId,projectId).eq(ArchiveAiName::getContractId,contractId).eq(ArchiveAiName::getStatus,2));
+			return R.data(pages);
+		}
+	}
+
+	@PostMapping("/confirmAiName")
+	@ApiOperationSupport(order = 3)
+	@ApiOperation(value = "确认AI名称", notes = "确认AI名称")
+	public R confirmAiName(@RequestBody List<ArchiveAiNameVO1>list){
+		return R.status(archiveAiNameService.confirmAiName(list));
+	}
+
+//	/**
+//	 * 分页
+//	 */
+//	@GetMapping("/list")
+//	@ApiOperationSupport(order = 2)
+//	@ApiOperation(value = "分页", notes = "传入archiveAiName")
+//	public R<IPage<ArchiveAiName>> list(ArchiveAiName archiveAiName, Query query) {
+//		IPage<ArchiveAiName> pages = archiveAiNameService.page(Condition.getPage(query), Condition.getQueryWrapper(archiveAiName));
+//		return R.data(pages);
+//	}
+
+//
+//
+//	/**
+//	 * 新增
+//	 */
+//	@PostMapping("/save")
+//	@ApiOperationSupport(order = 4)
+//	@ApiOperation(value = "新增", notes = "传入archiveAiName")
+//	public R save(@Valid @RequestBody ArchiveAiName archiveAiName) {
+//		return R.status(archiveAiNameService.save(archiveAiName));
+//	}
+//
+//	/**
+//	 * 修改
+//	 */
+//	@PostMapping("/update")
+//	@ApiOperationSupport(order = 5)
+//	@ApiOperation(value = "修改", notes = "传入archiveAiName")
+//	public R update(@Valid @RequestBody ArchiveAiName archiveAiName) {
+//		return R.status(archiveAiNameService.updateById(archiveAiName));
+//	}
+//
+//	/**
+//	 * 新增或修改
+//	 */
+//	@PostMapping("/submit")
+//	@ApiOperationSupport(order = 6)
+//	@ApiOperation(value = "新增或修改", notes = "传入archiveAiName")
+//	public R submit(@Valid @RequestBody ArchiveAiName archiveAiName) {
+//		return R.status(archiveAiNameService.saveOrUpdate(archiveAiName));
+//	}
+//
+//
+//	/**
+//	 * 删除
+//	 */
+//	@PostMapping("/remove")
+//	@ApiOperationSupport(order = 7)
+//	@ApiOperation(value = "逻辑删除", notes = "传入ids")
+//	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
+//		return R.status(archiveAiNameService.deleteLogic(Func.toLongList(ids)));
+//	}
+
+	
+}

+ 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);
     }
 

+ 89 - 19
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchivesAutoController.java

@@ -16,16 +16,13 @@
  */
 package org.springblade.archive.controller;
 
-import cn.hutool.core.text.split.SplitIter;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import io.swagger.annotations.*;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import lombok.AllArgsConstructor;
 
 import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 
@@ -34,18 +31,17 @@ 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;
 import org.springblade.archive.service.IArchiveAutoPdfService;
 import org.springblade.archive.service.ITraceLogService;
 import org.springblade.archive.utils.CallBgrsjk;
-import org.springblade.archive.utils.FileUtils;
 import org.springblade.archive.vo.*;
 import org.springblade.business.entity.ArchiveFile;
-import org.springblade.business.entity.InformationQuery;
-import org.springblade.common.constant.CommonConstant;
-import org.springblade.common.utils.CommonUtil;
+import org.springblade.business.feign.ArchiveFileClient;
+import org.springblade.common.utils.DeepSeekClient;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
@@ -54,15 +50,12 @@ import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.Func;
 import org.springblade.manager.entity.ArchiveTreeContract;
 import org.springblade.manager.entity.ContractInfo;
-import org.springblade.manager.entity.ExcelEditCallback;
-import org.springblade.manager.entity.ProjectInfo;
 import org.springblade.manager.feign.ArchiveTreeContractClient;
 import org.springblade.manager.feign.ContractClient;
 import org.springblade.manager.feign.ProjectClient;
-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.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.RequestParam;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -72,12 +65,8 @@ import org.springblade.archive.service.IArchivesAutoService;
 import org.springblade.core.boot.ctrl.BladeController;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
@@ -106,6 +95,11 @@ public class ArchivesAutoController extends BladeController {
 	private ExecutorService executorService;
 	@Autowired
 	private ITraceLogService iTraceLogService;
+    private final JdbcTemplate jdbcTemplate;
+    @Autowired
+    private ArchiveFileClient archiveFileClient;
+
+
 	/**
 	 * 详情
 	 */
@@ -152,6 +146,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 +401,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 +422,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")
@@ -482,6 +503,48 @@ public class ArchivesAutoController extends BladeController {
 		}
 
 	}
+	@PostMapping("/reCreateArchiveAuto")
+	@ApiOperationSupport(order = 13)
+	@ApiOperation(value = "重新生成案卷", notes = "传入ids")
+	@Transactional
+	public R reCreateArchiveAuto(@ApiParam(value = "主键集合", required = true) @RequestParam String ids,@ApiParam(value = "合并后的文件题目", required = true)String name){
+		//先查出勾选的案卷
+		List<ArchivesAuto> archivesAutoList=archivesAutoService.listByIds(Func.toLongList(ids));
+		if(archivesAutoList.size()<=1){
+			return R.fail("请选择多个案卷进行合并");
+		}
+		archivesAutoList.sort(Comparator.comparingInt(a -> {
+			String[] parts = a.getFileNumber().split("_");
+			return Integer.parseInt(parts[parts.length - 1]); // 取最后一个部分作为数字
+		}));
+		//根据档号后缀排序 拿到第一个
+		ArchivesAuto auto = archivesAutoList.get(0);
+		//查出所有案卷文件
+		List<ArchiveFile>archiveFileList=archiveFileClient.getArchiveFileByArchiveIds(ids);
+		//将除第一个以外的案卷文件archiveId 设置成第一个的id
+		List<ArchiveFile>updateArchiveFileList=new ArrayList<>();
+		for (ArchiveFile file : archiveFileList) {
+			if (!file.getArchiveId().equals(auto.getId())) {
+				file.setArchiveId(auto.getId());
+				updateArchiveFileList.add(file);
+			}
+		}
+		archiveFileClient.updateArchiveFile(updateArchiveFileList);
+		auto.setName(name);
+		//删除其他案卷
+		archivesAutoList.remove(auto);
+		archivesAutoService.deleteLogic(archivesAutoList.stream().map(o->o.getId()).collect(Collectors.toList()));
+		//设置案卷页码和四要素
+		archivesAutoService.reCreateArchiveAuto(auto, archiveFileList);
+		return R.status(true);
+	}
+
+	@PostMapping("/creatFileNameFormAI")
+	@ApiOperationSupport(order = 10)
+	@ApiOperation(value = "案卷ai题名", notes = "传入ids")
+	public R creatFileNameFormAI(String ids,Long projectId,Long contractId) throws IOException {
+		return R.status(archivesAutoService.creatFileNameFormAI(ids,projectId,contractId));
+	}
 
 	/**
 	 * 预览案卷文件
@@ -961,5 +1024,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));
+    }
+
 
 }

+ 37 - 1
blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveBuildService.java

@@ -4,6 +4,7 @@ import lombok.AllArgsConstructor;
 import org.springblade.archive.external.bean.ExternalDataInfo;
 import org.springblade.archive.external.utils.TransUtil;
 import org.springblade.archive.trans.ArchiveReq;
+import org.springblade.manager.feign.ArchiveTreeContractClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -18,8 +19,11 @@ public class ExternalDataArchiveBuildService {
     @Autowired
     private  ExternalDataArchiveTreeService externalTreeService;
 
+
     private ExternalDataArchiveMetaService externalMetaService;
 
+    private final ArchiveTreeContractClient archiveTreeContractClient;
+
 
     /**
      *
@@ -31,7 +35,39 @@ public class ExternalDataArchiveBuildService {
             throw new IllegalArgumentException("请求参数不能为空");
         }
 
-        ExternalDataInfo externalDataInfo = externalTreeService.getDataByIds(req.getNodeId(),req.getRemoteId());
+        // 当 transType 为 1 时,使用新方式获取 remoteId, 并且在传输开始时设置
+        if (req.getTransType() != null && req.getTransType() == 1 && req.getType()== 0) {
+            // 获取节点ID
+            Long remoteIdLong = archiveTreeContractClient.getNodeIdByName(
+                    req.getProjectName(),
+                    req.getContractName(),
+                    req.getNodeName()
+            );
+
+            if (remoteIdLong == null) {
+                throw new RuntimeException("无法找到匹配的节点: " +
+                        req.getProjectName() + "/" +
+                        req.getContractName() + "/" +
+                        req.getNodeName());
+            }
+
+            // 将 Long 转换为 String
+            req.setRemoteId(String.valueOf(remoteIdLong));
+        }
+
+        // 调用获取数据的方法
+        ExternalDataInfo externalDataInfo = externalTreeService.getDataByIds(
+                req.getNodeId(),
+                req.getRemoteId(),
+                req.getProjectName(),
+                req.getContractName(),
+                req.getNodeName(),
+                req.getTransType()
+        );
+
+        if (externalDataInfo == null) {
+            return;
+        }
 
         switch (req.getType()) {
             case 0:

+ 33 - 13
blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveFileService.java

@@ -40,7 +40,7 @@ public class ExternalDataArchiveFileService {
         List<ArchiveFile> upFiles = new ArrayList<>();
 
         //外部文件
-        List<ArchiveFile> externalFiles = listConvert(req.getNodeId(), req.getFiles(), externalDataInfo);
+        List<ArchiveFile> externalFiles = listConvert(req.getNodeId(), req.getFiles(), externalDataInfo,req.getTransType());
 
         List<String> outIds = Optional.ofNullable(externalFiles)
                 .orElseGet(Collections::emptyList)
@@ -55,13 +55,13 @@ public class ExternalDataArchiveFileService {
         //把本地的也加进去,一般用不到作为冗余手段
         updateFileMapping(externalDataInfo,localFiles);
         //比较获取
-        getAddAndUpdateFiles(externalDataInfo,externalFiles, localFiles, addFiles, upFiles);
+        getAddAndUpdateFiles(externalDataInfo,externalFiles, localFiles, addFiles, upFiles,req);
 
         //保存
         addAndUpdateFiles(addFiles,upFiles);
     }
 
-    public List<ArchiveFile> listConvert(String nodeId, List<ArchiveFileVo> archiveFileVos, ExternalDataInfo externalDataInfo) {
+    public List<ArchiveFile> listConvert(String nodeId, List<ArchiveFileVo> archiveFileVos, ExternalDataInfo externalDataInfo,Integer transType) {
 
         List<ArchiveFile> archiveFiles = new ArrayList<>();
         Map<String, Long> archiveFileOutIdMapping = new LinkedHashMap<>();
@@ -84,9 +84,14 @@ public class ExternalDataArchiveFileService {
 
             ArchiveFile archiveFile = new ArchiveFile();
 
-            // 拷贝属性
+            //
             archiveFile.fromExternal(archiveFileVo);
 
+            //模式1,只传递未组卷得案卷
+            if (transType != null && transType == 1) {
+                archiveFile.setIsArchive(0);
+            }
+
             // 设置新ID(从映射表中获取)
             Long newNodeId = archiveFileOutIdMapping.get(archiveFileVo.getId());
             archiveFile.setId(newNodeId);
@@ -127,7 +132,7 @@ public class ExternalDataArchiveFileService {
             List<ArchiveFile> externalFiles,
             List<ArchiveFile> localFiles,
             List<ArchiveFile> addFiles,
-            List<ArchiveFile> upFiles) {
+            List<ArchiveFile> upFiles,ArchiveReq req) {
 
         // 1. 提取本地文件的 outId 集合(用于快速判断外部文件是否已存在)
         Set<String> localOutIdSet = localFiles.stream()
@@ -155,7 +160,7 @@ public class ExternalDataArchiveFileService {
             } else {
                 // 存在则查找本地对应文件,判断是否需要更新
                 ArchiveFile localFile = localFileMap.get(externalOutId);
-                if (localFile != null && needUpdate(externalFile, localFile)) {
+                if (localFile != null && needUpdate(externalFile, localFile,req)) {
                     updateLocalFile(localFile, externalFile); // 更新字段
                     upFiles.add(localFile);
                 }
@@ -164,15 +169,30 @@ public class ExternalDataArchiveFileService {
     }
 
     // 辅助方法:判断是否需要更新
-    private boolean needUpdate(ArchiveFile external, ArchiveFile local) {
-        String extUtimeStr = formatTime(external.getUtime());
-        String localUtimeStr = formatTime(local.getUtime());
+// 辅助方法:判断是否需要更新
+    private boolean needUpdate(ArchiveFile external, ArchiveFile local, ArchiveReq req) {
+        // 处理 transType == 1 的情况
+        if (req.getTransType() != null && req.getTransType() == 1) {
+            // 比较 fileTime(避免空指针)和 sort 字段
+            String extFileTime = external.getFileTime();
+            String localFileTime = local.getFileTime();
+            boolean fileTimeDifferent = !Objects.equals(extFileTime, localFileTime);
+
+            boolean sortDifferent = !Objects.equals(external.getSort(), local.getSort());
+
+            return fileTimeDifferent || sortDifferent;
+        }
+        // 处理 transType == 0 和其他情况(默认逻辑)
+        else {
+            String extUtimeStr = formatTime(external.getUtime());
+            String localUtimeStr = formatTime(local.getUtime());
 
-        // 字符串严格比较:格式或内容不同即视为不一致
-        return !Objects.equals(extUtimeStr, localUtimeStr)
-                || !Objects.equals(external.getPdfPageUrl(), local.getPdfPageUrl());
-                //|| !Objects.equals(external.getArchiveId(), local.getArchiveId());
+            // 确保 external.getPdfPageUrl() 非空才比较(external有值)
+            boolean pdfUrlDifferent = external.getPdfPageUrl() != null &&
+                    !Objects.equals(external.getPdfPageUrl(), local.getPdfPageUrl());
 
+            return !Objects.equals(extUtimeStr, localUtimeStr) || pdfUrlDifferent;
+        }
     }
 
     /**

+ 21 - 1
blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveTreeService.java

@@ -26,7 +26,27 @@ public class ExternalDataArchiveTreeService {
     private ArchivesAutoMapper autoMapper;
     private final MetadataClassificationClient metadataClassificationClient;
 
-    public  ExternalDataInfo getDataByIds(String nodeId, String remoteId){
+    public ExternalDataInfo getDataByIds(String nodeId, String remoteId,
+                                         String projectName, String contractName,
+                                         String nodeName, Integer transType) {
+
+        // 当 transType 为 1 时,重新获取 remoteId
+        if (transType != null && transType == 1) {
+            // 获取节点ID
+            Long remoteIdLong = archiveTreeContractClient.getNodeIdByName(
+                    projectName,
+                    contractName,
+                    nodeName
+            );
+
+            if (remoteIdLong == null) {
+                throw new RuntimeException("无法找到匹配的节点: " +
+                        projectName + "/" + contractName + "/" + nodeName);
+            }
+
+            // 将 Long 转换为 String
+            remoteId = String.valueOf(remoteIdLong);
+        }
         ExternalDataInfo externalDataInfo = TransUtil.getDataByIds(nodeId, remoteId);
 
         if (externalDataInfo.getParent() == null) {

+ 44 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveAiNameMapper.java

@@ -0,0 +1,44 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.archive.mapper;
+
+import io.lettuce.core.dynamic.annotation.Param;
+import org.springblade.archive.entity.ArchiveAiName;
+import org.springblade.archive.vo.ArchiveAiNameVO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import java.util.List;
+
+/**
+ *  Mapper 接口
+ *
+ * @author BladeX
+ * @since 2025-07-03
+ */
+public interface ArchiveAiNameMapper extends BaseMapper<ArchiveAiName> {
+
+	/**
+	 * 自定义分页
+	 *
+	 * @param page
+	 * @param archiveAiName
+	 * @return
+	 */
+	List<ArchiveAiNameVO> selectArchiveAiNamePage(IPage page, ArchiveAiNameVO archiveAiName);
+
+    boolean deletedArchiveAiTask(@Param("taskId") Long taskId);
+}

+ 32 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveAiNameMapper.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.springblade.archive.mapper.ArchiveAiNameMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="archiveAiNameResultMap" type="org.springblade.archive.entity.ArchiveAiName">
+        <result column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_dept" property="createDept"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="project_id" property="projectId"/>
+        <result column="contract_id" property="contractId"/>
+        <result column="task_id" property="taskId"/>
+        <result column="archive_auto_id" property="archiveAutoId"/>
+        <result column="archive_name" property="archiveName"/>
+        <result column="archive_name_ai" property="archiveNameAi"/>
+    </resultMap>
+    <update id="deletedArchiveAiTask">
+        update  u_archive_ai_name set is_deleted=1 where task_id=#{taskId}
+
+    </update>
+
+
+    <select id="selectArchiveAiNamePage" resultMap="archiveAiNameResultMap">
+        select * from u_archive_ai_name where is_deleted = 0
+    </select>
+
+</mapper>

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

@@ -226,4 +226,9 @@ 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);
+
+
+
 }

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

@@ -216,9 +216,10 @@
         <result column="sort_num" property="sortNum"/>
     </resultMap>
 
+
     <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 +587,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 +1173,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 +1274,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 +1285,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 +1419,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>

+ 2 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/TaskSplitMapper.java

@@ -45,4 +45,6 @@ public interface TaskSplitMapper extends BaseMapper<TaskSplit> {
 
 	// 获取分解任务是否存在
 	Integer getSpliteTaskCount(String contractId);
+
+	//Integer updateArchiveByIds(String contractId,);
 }

+ 9 - 1
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/TaskSplitMapper.xml

@@ -27,11 +27,19 @@
     </select>
 
     <select id="getArchiveCount" resultType="java.lang.Integer">
-        select count(1) from u_archives_auto where contract_id = #{contractId}
+        select count(1) from u_archives_auto where contract_id = #{contractId} and is_deleted = 0
     </select>
 
     <select id="getSpliteTaskCount" resultType="java.lang.Integer">
         select count(1) from u_task_split where contract_id = #{contractId} and type=2
     </select>
 
+<!--    <update id="updateArchiveByIds">
+        update u_archives_auto set status = 1 where contract_id = #{contractId} and id in
+        <foreach item="item" collection="ids" separator="," close=")" open="(" index="index">
+            #{item}
+        </foreach>
+    </update>-->
+
+
 </mapper>

+ 45 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchiveAiNameService.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.archive.service;
+
+import org.springblade.archive.entity.ArchiveAiName;
+import org.springblade.archive.vo.ArchiveAiNameVO;
+import org.springblade.archive.vo.ArchiveAiNameVO1;
+import org.springblade.core.mp.base.BaseService;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ *  服务类
+ *
+ * @author BladeX
+ * @since 2025-07-03
+ */
+public interface IArchiveAiNameService extends BaseService<ArchiveAiName> {
+
+
+
+    void syncCreatAiName(List<ArchiveAiName> aiNames) throws IOException;
+
+    List<ArchiveAiNameVO> getArchiveAiTask(Long projectId, Long contractId);
+
+    boolean confirmAiName(List<ArchiveAiNameVO1>vos);
+
+    Boolean deletedArchiveAiTask(Long taskId);
+}

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

@@ -60,6 +60,6 @@ public interface IArchiveNameService {
      */
     String generateFullLevelName(
             List<Long> nodeList,
-            NodeHierarchy hierarchy
+            NodeHierarchy hierarchy,boolean useCovering
     );
 }

+ 14 - 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;
@@ -25,6 +26,7 @@ import org.springblade.archive.entity.ArchivesAuto;
 import org.springblade.archive.entity.ExpertInspection;
 import org.springblade.archive.entity.JiLinArchiveAutoDto;
 import org.springblade.archive.vo.*;
+import org.springblade.business.entity.ArchiveFile;
 import org.springblade.core.mp.base.BaseService;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springblade.core.mp.support.Query;
@@ -88,7 +90,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 +158,15 @@ 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);
+
+	void reCreateArchiveAuto(ArchivesAuto archivesAuto, List<ArchiveFile> waitArchiveFiles);
+
+
+	boolean creatFileNameFormAI(String ids, Long projectId, Long contractId) throws IOException;
 }

+ 148 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveAiNameServiceImpl.java

@@ -0,0 +1,148 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.archive.service.impl;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.apache.commons.lang.StringUtils;
+import org.springblade.archive.entity.ArchiveAiName;
+import org.springblade.archive.mapper.ArchiveAiNameMapper;
+import org.springblade.archive.service.IArchiveAiNameService;
+import org.springblade.archive.vo.ArchiveAiNameVO;
+import org.springblade.archive.vo.ArchiveAiNameVO1;
+import org.springblade.common.utils.DeepSeekClient;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.groupingBy;
+
+/**
+ *  服务实现类
+ *
+ * @author BladeX
+ * @since 2025-07-03
+ */
+@Service
+public class ArchiveAiNameServiceImpl extends BaseServiceImpl<ArchiveAiNameMapper, ArchiveAiName> implements IArchiveAiNameService {
+
+	 private static String DEEPSEEK_ARCHIVE_NAME = ".这是一段案卷题名,精简案卷题名重复啰嗦的内容,不要加以上内容没有的词语 返回值不要有任何多余得废话,只要结果";
+	 private static final int MAX_CONCURRENT_REQUESTS = 5;
+
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+
+	@Autowired
+	private DeepSeekClient deepSeekClient;
+
+	@Resource(name = "taskExecutor1")
+	private ThreadPoolExecutor executor;
+
+	private final Semaphore apiSemaphore = new Semaphore(MAX_CONCURRENT_REQUESTS);
+
+	@Override
+	@Async("taskExecutor1")
+	@Transactional
+	public void syncCreatAiName(List<ArchiveAiName> aiNames) {
+		aiNames.parallelStream().forEach(name -> {
+			try {
+				apiSemaphore.acquire();
+				try {
+					String content = deepSeekClient.getSimplifiedContent(name.getArchiveName() + DEEPSEEK_ARCHIVE_NAME);
+					if (content.startsWith("API_ERROR:") ||
+							content.startsWith("PARSE_ERROR:") ||
+							content.startsWith("IO_ERROR:")||content.startsWith("Error:")) {
+						name.setStatus(4);
+						name.setArchiveNameAi("获取失败");
+					} else {
+						name.setArchiveNameAi(content);
+						name.setStatus(2); // 标记为成功
+					}
+				} finally {
+					apiSemaphore.release();
+				}
+			} catch (InterruptedException e) {
+				Thread.currentThread().interrupt();
+				name.setStatus(4); // 设置失败状态
+				name.setArchiveNameAi("获取失败");
+			} catch (Exception e) {
+				name.setStatus(4); // 设置失败状态
+				name.setArchiveNameAi("获取失败");
+			}
+		});
+
+		// 分批更新数据库
+		int batchSize = 100;
+		for (int i = 0; i < aiNames.size(); i += batchSize) {
+			int end = Math.min(i + batchSize, aiNames.size());
+			List<ArchiveAiName> batch = aiNames.subList(i, end);
+			this.updateBatchById(batch);
+		}
+	}
+
+	@Override
+	public List<ArchiveAiNameVO> getArchiveAiTask(Long projectId, Long contractId) {
+		List<ArchiveAiNameVO>list=new ArrayList<>();
+		List<ArchiveAiName> archiveAiNameList = this.baseMapper.selectList(new LambdaQueryWrapper<>(ArchiveAiName.class).eq(ArchiveAiName::getProjectId, projectId).eq(ArchiveAiName::getContractId, contractId));
+		if(!archiveAiNameList.isEmpty()){
+			Map<Long, List<ArchiveAiName>> map = archiveAiNameList.stream().collect(groupingBy(ArchiveAiName::getTaskId));
+			for (Map.Entry<Long, List<ArchiveAiName>> entry : map.entrySet()) {
+				ArchiveAiNameVO archiveAiNameVO = new ArchiveAiNameVO();
+				List<ArchiveAiName> archiveAiNames = entry.getValue();
+				archiveAiNameVO.setTaskId(entry.getKey());
+				Date createTime = archiveAiNames.get(0).getCreateTime();
+				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+				String formattedDate = sdf.format(createTime);
+				archiveAiNameVO.setTaskTime(formattedDate);
+				archiveAiNameVO.setNum(archiveAiNames.size());
+				archiveAiNameVO.setStatus(archiveAiNames.stream().allMatch(ai -> ai.getStatus() == 2) ? 2 : archiveAiNames.stream().allMatch(ai -> ai.getStatus() == 3)?3:1);
+				list.add(archiveAiNameVO);
+			}
+		}
+		return list;
+	}
+
+
+	@Transactional
+	public boolean confirmAiName(List<ArchiveAiNameVO1>vos) {
+		for (ArchiveAiNameVO1 archiveAiName : vos) {
+			if(archiveAiName.getStatus()==2&& StringUtils.isNotEmpty(archiveAiName.getArchiveNameAi())){
+				archiveAiName.setStatus(3);
+				String sql=" update u_archives_auto set name='"+archiveAiName.getArchiveNameAi()+"' where id="+archiveAiName.getArchiveAutoId();
+				jdbcTemplate.update(sql);
+				String sql2="update u_archive_ai_name set status=3 where id="+archiveAiName.getId();
+				jdbcTemplate.update(sql2);
+			}
+		}
+		return true;
+	}
+
+	@Override
+	public Boolean deletedArchiveAiTask(Long taskId) {
+		return baseMapper.deletedArchiveAiTask(taskId);
+	}
+}

+ 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();
@@ -202,6 +202,150 @@ public class ArchiveNameServiceImpl implements IArchiveNameService {
 
     @Override
     public String generateFullLevelName(
+            List<Long> nodeIds,
+            NodeHierarchy hierarchy,boolean useCovering
+    ) {
+        // 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 && useCovering) {
+                                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
     ) {

+ 772 - 46
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.*;
@@ -50,6 +52,7 @@ import org.springblade.business.feign.ArchiveFileClient;
 import org.springblade.business.feign.MetadataClassificationClient;
 import org.springblade.business.feign.TaskClient;
 import org.springblade.common.utils.CommonUtil;
+import org.springblade.common.utils.DeepSeekClient;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.mp.base.BaseServiceImpl;
@@ -73,6 +76,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;
@@ -91,6 +97,7 @@ import java.util.*;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 /**
@@ -135,6 +142,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 +151,10 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	private final CommonFileClient commonFileClient;
 
+	private final IArchiveNameService archiveNameService;
+
+	private final IArchiveAiNameService aiNameService;
+
 
 	@Override
 	public IPage<ArchivesAutoVO> selectArchivesAutoPage(IPage<ArchivesAutoVO> page, ArchivesAutoVO archivesAuto) {
@@ -373,11 +386,138 @@ 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[] elementFiles = new ArchivesAutoVO.ApprovalFile[4];
+		AtomicInteger pageSize = new AtomicInteger(0);
+		if (approvalFiles != null && !approvalFiles.isEmpty()) {
+			// 四要素 使用名称判断
+			approvalFiles.removeIf(approvalFile -> {
+				if ("封面".equals(approvalFile.getFileName()) || "封面表".equals(approvalFile.getFileName())) {
+					elementFiles[0] = approvalFile;
+					return true;
+				} else if ("卷内目录".equals(approvalFile.getFileName()) || "卷内目录表".equals(approvalFile.getFileName()) || "目录表".equals(approvalFile.getFileName()) || "目录".equals(approvalFile.getFileName())) {
+					elementFiles[1] = approvalFile;
+					return true;
+				} else if ("备考表".equals(approvalFile.getFileName()) || "卷内备考表".equals(approvalFile.getFileName())) {
+					elementFiles[2] = approvalFile;
+					return true;
+				} else if ("背脊".equals(approvalFile.getFileName()) || "背脊表".equals(approvalFile.getFileName())) {
+					elementFiles[3] = approvalFile;
+					return true;
+				}
+				pageSize.addAndGet(approvalFile.getFilePage() == null ? 1 : approvalFile.getFilePage());
+				return false;
+			});
 		} else {
 			vo.setPageNumber(0);
 		}
+		ArchivesAutoVO.ApprovalFile front = elementFiles[0];
+		ArchivesAutoVO.ApprovalFile cataLog = elementFiles[1];
+		ArchivesAutoVO.ApprovalFile spare = elementFiles[2];
+		ArchivesAutoVO.ApprovalFile back = elementFiles[3];
+		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 +1290,129 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 	}
 
+
+	private String builtArchiveName_new(List<ArchiveFile> waitArchiveFiles, ArchiveTreeContract node, boolean isCrossNode
+	,IArchiveNameService.NodeHierarchy nameInfo,Set<Long> multiVolumeNodes,Integer archiveAutoType) {
+
+		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.getContractNumber();
+		}
+		//获取案卷题名
+		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));
+
+		String timeRange = "";
+
+		boolean extFlag = false;
+		if (node.getExtType()!= null && node.getExtType()== 1) {
+			extFlag = true;
+		}
+
+
+		if (uniqueNodeIds.size() == 1 && extFlag == false) {
+			try {
+				Long nodeId = Long.parseLong(uniqueNodeIds.iterator().next());
+
+				// 检查该节点是否需要添加时间区间(生成多个案卷)
+				if (multiVolumeNodes != null && multiVolumeNodes.contains(nodeId)) {
+					// 获取所有有效的时间字符串
+					List<String> validTimes = waitArchiveFiles.stream()
+							.map(ArchiveFile::getFileTime)
+							.filter(time -> time != null && !time.trim().isEmpty())
+							.sorted() // 按字符串自然顺序排序
+							.collect(Collectors.toList());
+
+					if (!validTimes.isEmpty()) {
+						String minTime = validTimes.get(0);
+						String maxTime = validTimes.get(validTimes.size() - 1);
+
+						// 生成时间区间字符串
+						timeRange = minTime + "~" + maxTime;
+					} else {
+						log.warn("组卷文件缺少有效时间戳:nodeId={}", nodeId);
+					}
+				}
+			} catch (NumberFormatException e) {
+				log.warn("无效的节点ID格式", e);
+			}
+		}
+
+		// 2. 将节点ID从String转换为Long(保持原始顺序)
+		List<Long> nodeIds = uniqueNodeIds.stream()
+				.map(this::safeParseLong)
+				.filter(Objects::nonNull)
+				.collect(Collectors.toList());
+
+		String fullPath = "";
+		if (extFlag == true) {
+			fullPath =  archiveNameService.generateFullLevelName(nodeIds, nameInfo,true);
+		}else {
+			//独立组卷,且非工序节点,取节点名曾即可
+			if (archiveAutoType == 3) {
+				fullPath =  node.getNodeName();
+			}else {
+				fullPath =  archiveNameService.generateFullLevelName(nodeIds, nameInfo,false);
+			}
+		}
+
+
+		archiveName += timeRange + 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 = "";
@@ -1218,7 +1481,6 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		//生成文件对应的页码,返回url
 		archiveAutoPdfService.builtFilePageNo(archivesAuto, waitArchiveFiles);
 		this.updateById(archivesAuto);
-
 		return "";
 	}
 
@@ -1245,7 +1507,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		//1.创建新案卷
 		ArchivesAuto archivesAuto = builtArchives(node, pageN, fileN, startDate, endDate, archiveName);
 		//2.设置文件所属案卷,组卷状态
-		Long archivesAutoId = archivesAuto.getId();
+		//Long archivesAutoId = archivesAuto.getId();
 
 		//封面和生成文件页码
 		archiveAutoPdfService.buildArchiveFrontPdfs(archivesAuto.getProjectId(), archivesAuto, waitArchiveFiles, false);
@@ -1253,11 +1515,94 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		builtFilePageNo(archivesAuto, waitArchiveFiles);//生成文件页码
 
 
-		for (ArchiveFile file : waitArchiveFiles) {
-			file.setArchiveId(archivesAutoId);//设置文件所属案卷
-			file.setIsArchive(1);
+		//设置文件所属案卷,组卷状态,排序
+		Long archivesAutoId = archivesAuto.getId();
+		setArchiveFiles(archivesAutoId,waitArchiveFiles);
+		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
+		try {
+//			for (ArchiveFile saveVo : waitArchiveFiles) {
+//				metadataClassificationClient.createMetadataFile(saveVo.getId(), 0);
+//			}
+			batchCreateMetadataFiles(waitArchiveFiles);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+
+	public void reCreateArchiveAuto(ArchivesAuto archivesAuto, List<ArchiveFile> waitArchiveFiles){
+		archiveAutoPdfService.assignArchiveTableUrl();
+		//生成四要素
+		archiveAutoPdfService.buildArchiveFrontPdfs(archivesAuto.getProjectId(), archivesAuto, waitArchiveFiles, true);
+		//生成页码
+		builtFilePageNo(archivesAuto, waitArchiveFiles);
+		archiveFileClient.updateArchiveFile(waitArchiveFiles);
+	}
+
+	@Override
+	public boolean creatFileNameFormAI(String ids, Long projectId, Long contractId) throws IOException {
+		List<ArchivesAuto> archivesAutoList = this.listByIds(Func.toLongList(ids));
+		List<ArchiveAiName>aiNames=new ArrayList<>();
+		Long taskId=SnowFlakeUtil.getId();
+		List<ArchiveAiName> archiveAiNameList = aiNameService.getBaseMapper().selectList(new LambdaQueryWrapper<>(ArchiveAiName.class).eq(ArchiveAiName::getProjectId, projectId).eq(ArchiveAiName::getContractId, contractId).in(ArchiveAiName::getArchiveAutoId, Func.toLongList(ids)));
+		List<Long> existid=new ArrayList<>();
+		if(!archiveAiNameList.isEmpty()){
+			existid= archiveAiNameList.stream().filter(ai -> ai.getStatus() == 1).map(ai -> ai.getArchiveAutoId()).collect(Collectors.toList());
+		}
+		for (ArchivesAuto auto : archivesAutoList) {
+			if(!existid.isEmpty()){
+				if(existid.contains(auto.getId())){
+					continue;
+				}
+			}
+			ArchiveAiName aiName = new ArchiveAiName();
+			aiName.setProjectId(projectId);
+			aiName.setContractId(contractId);
+			aiName.setTaskId(taskId);
+			aiName.setArchiveAutoId(auto.getId());
+			aiName.setArchiveName(auto.getName());
+			aiName.setStatus(1);
+			aiNames.add(aiName);
+		}
+		//异步调用AI接口
+		aiNameService.syncCreatAiName(aiNames);
+		return aiNameService.saveBatch(aiNames);
+	}
+
 
+	/**
+	 * 单独组卷规则组卷
+	 *
+	 * @param waitArchiveFiles
+	 * @param node             规格参数所在节点
+	 * @param pageN
+	 */
+	private void createArchive3_new(List<ArchiveFile> waitArchiveFiles, ArchiveTreeContract node, int pageN,IArchiveNameService.NodeHierarchy nameInfo, Set<Long> multiVolumeNodes) {
+
+		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,multiVolumeNodes,3);//获取案卷题名
+		//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);//生成文件页码
+
+
+		//设置文件所属案卷,组卷状态,排序
+		Long archivesAutoId = archivesAuto.getId();
+		setArchiveFiles(archivesAutoId,waitArchiveFiles);
 		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
 		try {
 //			for (ArchiveFile saveVo : waitArchiveFiles) {
@@ -1269,20 +1614,22 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 	}
 
+
+
 	/**
 	 * 分类并卷组卷
 	 *
 	 * @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,Set<Long> multiVolumeNodes) {
 
 		//获取同一分类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 +1637,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 +1664,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 		//默认组卷存在跨节点组卷  注意案卷归属节点,案卷命名方式
 		//获取案卷题名
-		String archiveName = builtArchiveName(waitArchiveFiles, node, true);//获取案卷题名
+		String archiveName = builtArchiveName_new(waitArchiveFiles, node, true,nameInfo,multiVolumeNodes,2);//获取案卷题名
 
 		//1.创建新案卷
 		ArchivesAuto archivesAuto = builtArchives(node, pageN, fileN, startDate, endDate, archiveName);
@@ -1327,12 +1674,16 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 		builtFilePageNo(archivesAuto, waitArchiveFiles);
 
-		//3.设置文件所属案卷,组卷状态
+
+//		Long archivesAutoId = archivesAuto.getId();
+//		for (ArchiveFile file : waitArchiveFiles) {
+//			file.setArchiveId(archivesAutoId);//设置文件所属案卷
+//			file.setIsArchive(1);
+//		}
+		//设置文件所属案卷,组卷状态,排序
 		Long archivesAutoId = archivesAuto.getId();
-		for (ArchiveFile file : waitArchiveFiles) {
-			file.setArchiveId(archivesAutoId);//设置文件所属案卷
-			file.setIsArchive(1);
-		}
+		setArchiveFiles(archivesAutoId,waitArchiveFiles);
+
 		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
 		try {
 			batchCreateMetadataFiles(waitArchiveFiles);
@@ -1375,11 +1726,9 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		builtFilePageNo(archivesAuto, waitArchiveFiles);
 
 		//3.设置文件所属案卷,组卷状态
+		//设置文件所属案卷,组卷状态,排序
 		Long archivesAutoId = archivesAuto.getId();
-		for (ArchiveFile file : waitArchiveFiles) {
-			file.setArchiveId(archivesAutoId);//设置文件所属案卷
-			file.setIsArchive(1);
-		}
+		setArchiveFiles(archivesAutoId,waitArchiveFiles);
 		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
 		try {
 //			for (ArchiveFile saveVo : waitArchiveFiles) {
@@ -1431,11 +1780,9 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		builtFilePageNo(archivesAuto, waitArchiveFiles);
 
 		//3.设置文件所属案卷,组卷状态
+		//设置文件所属案卷,组卷状态,排序
 		Long archivesAutoId = archivesAuto.getId();
-		for (ArchiveFile file : waitArchiveFiles) {
-			file.setArchiveId(archivesAutoId);//设置文件所属案卷
-			file.setIsArchive(1);
-		}
+		setArchiveFiles(archivesAutoId,waitArchiveFiles);
 		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
 		try {
 //			for (ArchiveFile saveVo : waitArchiveFiles) {
@@ -1546,6 +1893,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:获取当前节点的案卷规格
@@ -1566,6 +1917,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 			String completeMsg = "[自动组卷] 单独组卷:" + "-traceId:" + traceId + "节点:" + node.getNodeName() + " 文件数量:" + archiveFiles.size();
 			iTraceLogService.saveLog(traceId, completeMsg);
 
+			Set<Long> multiVolumeNodes = precalculateMultiVolumeNodes(archiveFiles, specificationSize);
+
 			// 步骤4:遍历未归档文件
 			List<ArchiveFile> waitArchiveFiles = new ArrayList<>();  // 待组卷文件集合
 			int archivesSize = 0;  // 待组卷文件总页数
@@ -1581,7 +1934,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 					// 如果是最后一个文件且有待组卷文件,则组卷
 					if (archiveFilesSize == archiveFiles.size() && !waitArchiveFiles.isEmpty()) {
-						createArchive3(waitArchiveFiles, node, archivesSize);
+						createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo,null);
 						waitArchiveFiles.clear();
 						archivesSize = 0;
 					}
@@ -1600,7 +1953,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 						// 最后一个文件直接组卷
 						if (archiveFilesSize == archiveFiles.size()) {
-							createArchive3(waitArchiveFiles, node, archivesSize);
+							createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo,multiVolumeNodes);
 							waitArchiveFiles.clear();
 							archivesSize = 0;
 						}
@@ -1609,7 +1962,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,multiVolumeNodes);
 
 						// 重置待组卷集合
 						waitArchiveFiles.clear();
@@ -1619,7 +1972,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 					else if (checkStatus == -1) {
 						if (!waitArchiveFiles.isEmpty()) {
 							// 先将现有集合组卷(不含当前文件)
-							createArchive3(waitArchiveFiles, node, archivesSize);
+							createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo,multiVolumeNodes);
 
 							// 新建集合存放当前文件
 							waitArchiveFiles.clear();
@@ -1628,7 +1981,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 							// 最后一个文件直接组卷
 							if (archiveFilesSize == archiveFiles.size()) {
-								createArchive3(waitArchiveFiles, node, archivesSize);
+								createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo,multiVolumeNodes);
 								waitArchiveFiles.clear();
 								archivesSize = 0;
 							}
@@ -1636,7 +1989,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 							// 当前文件单独成卷
 							waitArchiveFiles.add(file);
 							archivesSize = filePage;
-							createArchive3(waitArchiveFiles, node, archivesSize);
+							createArchive3_new(waitArchiveFiles, node, archivesSize,nameInfo,multiVolumeNodes);
 
 							// 重置集合
 							waitArchiveFiles.clear();
@@ -1750,15 +2103,123 @@ 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(案卷归属节点)
+		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
+			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(案卷归属节点)
@@ -1769,6 +2230,10 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		// 当前顶层节点对应的分组ID
 		Long currArchiveAutoGroupId = null;
 
+		// 4. 构建节点层级关系
+		IArchiveNameService.NodeHierarchy nameInfo =
+				archiveNameService.buildNodeHierarchy(list);
+
 		// 步骤1:遍历节点集合
 		for (ArchiveTreeContract node : list) {
 			// 初始化当前节点的分组ID和顶层节点ID
@@ -1833,11 +2298,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;
 		}
@@ -1868,6 +2337,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		int fileIndex = 0;
 		int totalFiles = archiveFiles.size();
 
+		Set<Long> multiVolumeNodes = precalculateMultiVolumeNodes(archiveFiles, specificationSize);
+
 		for (ArchiveFile file : archiveFiles) {
 			fileIndex++;
 			// 获取文件页数,处理null值
@@ -1885,13 +2356,13 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 					waitArchiveFiles.add(file);
 					archivesSize = tempTotalSize;
 					if (fileIndex == totalFiles) { // 是最后一个文件
-						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo,multiVolumeNodes);
 					}
 					break;
 
 				case 1: // 达到规格
 					waitArchiveFiles.add(file);
-					createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+					createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo,multiVolumeNodes);
 					waitArchiveFiles = new ArrayList<>();
 					archivesSize = 0;
 					break;
@@ -1899,10 +2370,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,multiVolumeNodes);
 					} else {
 						// 先将现有文件组卷
-						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+						createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo,multiVolumeNodes);
 
 						// 创建新的待组卷集合
 						waitArchiveFiles = new ArrayList<>();
@@ -1910,7 +2381,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 						archivesSize = filePage;
 
 						if (fileIndex == totalFiles) { // 是最后一个文件
-							createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId);
+							createArchive2(waitArchiveFiles, archiveAutoGroupId, projectId,nameInfo,multiVolumeNodes);
 						}
 					}
 					break;
@@ -2775,7 +3246,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	}
 
 
-	public void test() {
+	public void test666() {
 		Long projectId = 0L;
 		List<ArchiveTreeContract> archiveTreeContracts = archiveTreeContractClient.getListByProjectId(projectId);
 
@@ -4024,8 +4495,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";
 		// 多条命令执行
@@ -4159,4 +4776,113 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		return R.success("保存成功");
 	}
 
+	// 归档文件设置函数
+	public  void setArchiveFiles(Long archivesAutoId, List<ArchiveFile> waitArchiveFiles) {
+		if (waitArchiveFiles == null || archivesAutoId == null) {
+			return; // 处理空指针情况
+		}
+
+		int archiveSortCounter = 1; // 从1开始递增的archiveSort计数器
+
+		for (ArchiveFile file : waitArchiveFiles) {
+			file.setArchiveId(archivesAutoId);   // 设置文件所属案卷ID
+			file.setIsArchive(1);                // 标记文件已归档
+			file.setArchiveSort(archiveSortCounter++); // 设置自增的archiveSort
+		}
+	}
+
+
+	private Set<Long> precalculateMultiVolumeNodes(List<ArchiveFile> files, int specificationSize) {
+		if (files == null || files.isEmpty()) {
+			return Collections.emptySet();
+		}
+
+		Map<Long, List<List<ArchiveFile>>> nodeGroupingMap = new HashMap<>();
+		Set<Long> multiVolumeNodes = new HashSet<>();
+
+		// 待组卷文件集合
+		List<ArchiveFile> waitArchiveFiles = new ArrayList<>();
+		// 待组卷文件总页数
+		int archivesSize = 0;
+		// 文件索引(用于处理最后文件)
+		int fileIndex = 0;
+		int totalFiles = files.size();
+
+		for (ArchiveFile file : files) {
+			fileIndex++;
+			int filePage = file.getFilePage() != null ? file.getFilePage() : 0;
+			int tempTotalSize = archivesSize + filePage;
+			int checkStatus = checkSpecificationSize(specificationSize, tempTotalSize);
+
+			switch (checkStatus) {
+				case 0: // 未到规格
+					waitArchiveFiles.add(file);
+					archivesSize = tempTotalSize;
+					if (fileIndex == totalFiles) { // 是最后一个文件
+						recordGroup(waitArchiveFiles, nodeGroupingMap);
+					}
+					break;
+
+				case 1: // 达到规格
+					waitArchiveFiles.add(file);
+					recordGroup(waitArchiveFiles, nodeGroupingMap);
+					waitArchiveFiles = new ArrayList<>();
+					archivesSize = 0;
+					break;
+
+				case -1: // 超出规格
+					if (!waitArchiveFiles.isEmpty()) {
+						recordGroup(waitArchiveFiles, nodeGroupingMap);
+						waitArchiveFiles = new ArrayList<>();
+						waitArchiveFiles.add(file);
+						archivesSize = filePage;
+						if (fileIndex == totalFiles) { // 是最后一个文件
+							recordGroup(waitArchiveFiles, nodeGroupingMap);
+						}
+					} else {
+						waitArchiveFiles.add(file);
+						archivesSize = filePage;
+						recordGroup(waitArchiveFiles, nodeGroupingMap);
+						waitArchiveFiles = new ArrayList<>();
+						archivesSize = 0;
+					}
+					break;
+			}
+		}
+
+		// 确定哪些节点有多个卷
+		nodeGroupingMap.forEach((nodeId, groups) -> {
+			if (groups.size() > 1) {
+				multiVolumeNodes.add(nodeId);
+			}
+		});
+
+		return multiVolumeNodes;
+	}
+
+
+	private void recordGroup(List<ArchiveFile> groupFiles, Map<Long, List<List<ArchiveFile>>> nodeGroupingMap) {
+		if (groupFiles == null || groupFiles.isEmpty()) {
+			return;
+		}
+
+		// 提取节点ID(确保单一节点)
+		Set<Long> groupNodeIds = new HashSet<>();
+		for (ArchiveFile file : groupFiles) {
+			try {
+				Long nodeId = Long.parseLong(file.getNodeId());
+				groupNodeIds.add(nodeId);
+			} catch (Exception e) {
+				// 忽略无效节点ID
+			}
+		}
+
+		// 仅记录单节点分组
+		if (groupNodeIds.size() == 1) {
+			Long nodeId = groupNodeIds.iterator().next();
+			nodeGroupingMap.computeIfAbsent(nodeId, k -> new ArrayList<>())
+					.add(new ArrayList<>(groupFiles));
+		}
+	}
+
 }

+ 13 - 7
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/TaskSplitServiceImpl.java

@@ -33,6 +33,7 @@ import javax.annotation.Resource;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Random;
 
@@ -73,7 +74,7 @@ public class TaskSplitServiceImpl extends BaseServiceImpl<TaskSplitMapper, TaskS
 			}
 
 			Random random = new Random();
-			int randomNumber = random.nextInt(11) + 10; // 生成10到20之间的随机数
+			int randomNumber = random.nextInt(30) + 150; // 生成10到20之间的随机数
 			int i = (int) Math.ceil(randomNumber * spliteTaskCount/60.0 );
 
 			Integer archiveCount = baseMapper.getArchiveCount(contractId);
@@ -85,18 +86,23 @@ public class TaskSplitServiceImpl extends BaseServiceImpl<TaskSplitMapper, TaskS
 			data.put("taskTime",i);
 		}else{ // 指定文件解析
 			taskSplit.setType(3);
-			String[] split = splitIds.split(",");
-			taskSplit.setToolCount(split.length);
+			//String[] split = splitIds.split(",");
+			List<String> split = Func.toStrList(splitIds);
+			taskSplit.setToolCount(split.size());
 			taskSplit.setFinished(0);
 
+			// 修改当前文件的状体
+			// baseMapper.updateArchiveByIds(contractId,splitIds);
+
+
 			Random random = new Random();
-			int randomNumber = random.nextInt(11) + 10; // 生成10到20之间的随机数
-			int i = (int) Math.ceil(randomNumber * split.length/60.0 );
-			data.put("fileCount",split.length);
+			int randomNumber = random.nextInt(30) + 150;// 生成10到20之间的随机数
+			int i = (int) Math.ceil(randomNumber * split.size()/60.0 );
+			data.put("fileCount",split.size());
 			data.put("taskTime",i);
 			LocalDateTime now = LocalDateTime.now();
 			DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
-			String formattedDateTime = now.format(formatter)+"_"+split.length+"条分解任务";
+			String formattedDateTime = now.format(formatter)+"_"+split.size()+"条分解任务";
 			taskSplit.setTaskName(formattedDateTime);
 		}
 

+ 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>

+ 30 - 3
blade-service/blade-business/src/main/java/org/springblade/business/controller/EVisaTaskCheckController.java

@@ -844,9 +844,36 @@ public class EVisaTaskCheckController {
             if (ids == null || ids.size() == 0) {
                 return null;
             }
-            String sql = "select * from m_textdict_info where id in(" + StringUtils.join(ids, ",") + ") ";
-            List<TextdictInfo> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TextdictInfo.class));
-            jsonList = JSONArray.parseArray(JSONObject.toJSONString(query), JSONObject.class);
+        Map<String, List<String>> groupMap = ids.stream().collect(Collectors.groupingBy(str -> {
+            if (str.contains("✹")) {
+                return "sign";
+            }
+            return "dqid";
+        }));
+        List<String> dqIds = groupMap.get("dqid");
+        List<TextdictInfo> query = null;
+        if (dqIds != null && !dqIds.isEmpty()) {
+            String sql = "select * from m_textdict_info where id in(" + StringUtils.join(dqIds, ",") + ") ";
+            query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TextdictInfo.class));
+        }
+        List<String> signList = groupMap.get("sign");
+        if (signList != null && !signList.isEmpty()) {
+            String signIds = signList.stream().map(sign -> sign.replace("✹", "")).collect(Collectors.joining(","));
+            String sql = "SELECT distinct conf_id,relation_id from m_sign_config_relation WHERE conf_id in ( " + signIds + ") and type = 1 and is_deleted = 0";
+            List<SignConfigRelation> signConfigRelationList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(SignConfigRelation.class));
+            if (!signConfigRelationList.isEmpty()) {
+                if (query == null) {
+                    query = new ArrayList<>();
+                }
+                for (SignConfigRelation relation : signConfigRelationList) {
+                    TextdictInfo textdictInfo = new TextdictInfo();
+                    textdictInfo.setId(relation.getConfId());
+                    textdictInfo.setSigRoleId(relation.getRelationId() + "");
+                    query.add(textdictInfo);
+                }
+            }
+        }
+        jsonList = JSONArray.parseArray(JSONObject.toJSONString(query), JSONObject.class);
 //        }
         return jsonList;
     }

+ 84 - 8
blade-service/blade-business/src/main/java/org/springblade/business/controller/InformationWriteQueryController.java

@@ -1456,7 +1456,7 @@ public R<String> batchDownloadFileToZip(String ids, HttpServletResponse response
                     String[] idss = query.stream()
                             .map(item -> String.valueOf(item.getId()))
                             .toArray(String[]::new);
-                    String update = "update u_entrust_info set status=3 where id in(" + String.join(",", idss) + ")";
+                    String update = "update u_entrust_info set status=4 where id in(" + String.join(",", idss) + ")";
                     jdbcTemplate.execute(update);
                 }
                 return R.success("操作成功");
@@ -1927,7 +1927,7 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
                     List<String> keySet = new LinkedList<>();
                     for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
                         keySet.add(entry.getKey());
-                        Object value = reviseValue(eMap, entry.getKey(), entry.getValue());
+                        Object value = reviseValue(eMap, entry.getKey(), entry.getValue(), nodeOld.getPKeyId());
                         if (value != null) {
                             if (value.toString().contains("\n")) {
                                 value = value.toString().replace("\n", "\\n");
@@ -2369,10 +2369,49 @@ private Object reviseValue(Map<String, String> p2, String key, Object value) {
     }
     return value;
 }
+    private Object reviseValue(Map<String, String> p2, String key, Object value, Long pKeyId) {
+        try {
+            if (p2.containsKey(key)) {
+                String setting = p2.get(key);
+                if (StringPool.NULL.equals(setting)) {
+                    /*擦除原来的内容*/
+                    return setting;
+                } else {
+                    /*重做随机值*/
+                    List<RangeJson> rjs = JSON.parseArray(setting, RangeJson.class);
+                    if (value != null && !value.toString().isEmpty() && Func.isNotEmpty(rjs)) {
+                        List<RangeJson> rangeJsons = rjs.stream().filter(rj -> rj.getPkeyId().equals(pKeyId)).collect(Collectors.toList());
+                        if (!rangeJsons.isEmpty()) {
+                            List<String[]> la = Arrays.stream(value.toString().split("☆")).map(s -> s.split("_\\^_")).collect(Collectors.toList());
+                            /*全部为一个数的时候不用修改*/
+                            if (la.stream().map(a -> a[0]).collect(Collectors.toSet()).size() > 1 || la.size() == 1) {
+                                List<String> result = new ArrayList<>();
+                                for (String[] a : la) {
+                                    String v = a[0];
+                                    String sv;
+                                    if (v.contains("、")) {
+                                        sv = Arrays.stream(v.split("[、]")).map(e -> imitate(e, rangeJsons)).collect(Collectors.joining("、"));
+                                    } else {
+                                        sv = imitate(v, rangeJsons);
+                                    }
+                                    result.add(sv + "_^_" + a[1]);
+                                }
+                                return String.join("☆", result);
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return value;
+    }
 
 public String imitate(String v, List<RangeJson> rjs) {
     try {
-        RangeJson best = rjs.stream().min(Comparator.comparingDouble(j -> Double.parseDouble(v) - Double.parseDouble(j.getDesign()))).orElse(rjs.get(0));
+        RangeJson best = rjs.stream().filter(j -> BaseUtils.isNotEmpty(j.getDesign()))
+                .peek(j -> j.setDesign(j.getDesign().split("/")[0])).min(Comparator.comparingDouble(j -> Double.parseDouble(v) - Double.parseDouble(j.getDesign()))).orElse(rjs.get(0));
         // int scale = Math.max(new StringBuilder(v).reverse().indexOf("."), 0);
         int scale = BaseUtils.getScaleZero(v, best.getDev(), best.getDesign());
         return BaseUtils.rangeList(1, best.getDesign(), best.getDev(), 1, scale, 1).get(0).toString();
@@ -2394,7 +2433,8 @@ private String reviseCols(Map<String, String> p2, String cols, Long pkeyId, Stri
                 List<String> colsList = target.stream().filter(k -> k.startsWith("key")).collect(Collectors.toList());
                 for (String k : colsList) {
                     if (p2.containsKey(k)) {
-                        Object value = reviseValue(p2, k, origin.get(k));
+//                        Object value = reviseValue(p2, k, origin.get(k));
+                        Object value = reviseValue(p2, k, origin.get(k), pkeyId);
                         if (value != null) {
                             if (value.toString().contains("_^_")) {
                                 value = "'" + value + "'";
@@ -2542,7 +2582,16 @@ private void addCopyTabData(WbsTreeContract needCopyNode, WbsTreeContract toCopy
                             String tableName = needTab.getInitTableName();
                             String col = nodeTabColsMap.get(tableName);
                             String colVal = nodeTabColsMap.get(tableName);
-                            colVal = colVal.replaceAll("id,p_key_id,", "'" + SnowFlakeUtil.getId() + "' as id,'" + toCopyNodeTab.getPKeyId() + "' as p_key_id,");
+                            String[] split = colVal.split(",");
+                            for (int i = 0; i < split.length; i++) {
+                                String key = split[i];
+                                if (key.equals("id")) {
+                                    split[i] = "'" + SnowFlakeUtil.getId() + "' as id";
+                                } else if (key.equals("p_key_id")) {
+                                    split[i] = "'" + toCopyNodeTab.getPKeyId() + "' as p_key_id";
+                                }
+                            }
+                            colVal = String.join(",", split);
                             //delete SQL (先删除旧数据,再新增)
                             String delSql = "delete from " + tableName + " where p_key_id = " + toCopyNodeTab.getPKeyId() + " ; ";
                             //insert into SQL
@@ -2591,7 +2640,16 @@ private void addCopyTabData(WbsTreeContract needCopyNode, WbsTreeContract toCopy
                                 String tableName = objTab.getInitTableName();
                                 String col = nodeTabColsMap.get(tableName);
                                 String colVal = nodeTabColsMap.get(tableName);
-                                colVal = colVal.replaceAll("id,p_key_id,", "'" + SnowFlakeUtil.getId() + "' as id,'" + objTab.getPKeyId() + "' as p_key_id,");
+                                String[] split = colVal.split(",");
+                                for (int i = 0; i < split.length; i++) {
+                                    String key = split[i];
+                                    if (key.equals("id")) {
+                                        split[i] = "'" + SnowFlakeUtil.getId() + "' as id";
+                                    } else if (key.equals("p_key_id")) {
+                                        split[i] = "'" + objTab.getPKeyId() + "' as p_key_id";
+                                    }
+                                }
+                                colVal = String.join(",", split);
                                 //delete SQL (先删除旧数据,再新增)
                                 String delSql = "delete from " + tableName + " where p_key_id = " + objTab.getPKeyId() + " ; ";
                                 //insert into SQL
@@ -2707,7 +2765,16 @@ private void addCopyNodesAndTabsBuildData(List<WbsTreeContract> addNodeList, Lis
                         String tableName = obj.getInitTableName();
                         String col = nodeTabColsMap.get(tableName);
                         String colVal = nodeTabColsMap.get(tableName);
-                        colVal = colVal.replaceAll("id,p_key_id,", "'" + SnowFlakeUtil.getId() + "' as id,'" + obj.getPKeyId() + "' as p_key_id,");
+                        String[] split = colVal.split(",");
+                        for (int i = 0; i < split.length; i++) {
+                            String key = split[i];
+                            if (key.equals("id")) {
+                                split[i] = "'" + SnowFlakeUtil.getId() + "' as id";
+                            } else if (key.equals("p_key_id")) {
+                                split[i] = "'" + obj.getPKeyId() + "' as p_key_id";
+                            }
+                        }
+                        colVal = String.join(",", split);
                         //delete SQL (先删除旧数据,再新增)
                         String delSql = "delete from " + tableName + " where p_key_id = " + obj.getPKeyId() + " ; ";
                         //insert into SQL
@@ -2840,7 +2907,16 @@ private void addCopyNodesAndTabsBuildData(List<WbsTreeContract> addNodeList, Lis
                                     StringBuilder copyDataSql = new StringBuilder();
                                     String col = queryProcessDataVO.getAncestors();
                                     String colVal = queryProcessDataVO.getAncestors();
-                                    colVal = colVal.replaceAll("id,p_key_id,", "'" + SnowFlakeUtil.getId() + "' as id,'" + objTab.getPKeyId() + "' as p_key_id,");
+                                    String[] split = colVal.split(",");
+                                    for (int i = 0; i < split.length; i++) {
+                                        String key = split[i];
+                                        if (key.equals("id")) {
+                                            split[i] = "'" + SnowFlakeUtil.getId() + "' as id";
+                                        } else if (key.equals("p_key_id")) {
+                                            split[i] = "'" + objTab.getPKeyId() + "' as p_key_id";
+                                        }
+                                    }
+                                    colVal = String.join(",", split);
                                     //delete SQL (先删除旧数据,再新增)
                                     String delSql = "delete from " + tableName + " where p_key_id = " + objTab.getPKeyId() + " ; ";
                                     //insert into SQL

+ 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;
                         }
                     }

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

@@ -346,4 +346,9 @@ public class ArchiveFileClientImpl implements ArchiveFileClient {
         iArchiveFileService.save(archiveFile);
     }
 
+    @Override
+    public List<ArchiveFile> getArchiveFileByArchiveIds(String archiveIds) {
+        return iArchiveFileService.getArchiveFileByArchivesId(archiveIds,null);
+    }
+
 }

+ 3 - 2
blade-service/blade-business/src/main/java/org/springblade/business/feignClient/InformationQueryClientImpl.java

@@ -127,10 +127,11 @@ public class InformationQueryClientImpl implements InformationQueryClient {
     }
 
     @Override
-    public InformationQuery getInfoByWbsId(Long wbsId) {
-        return informationQueryMapper.selectOne(new QueryWrapper<InformationQuery>().eq("wbs_id",wbsId));
+    public InformationQuery getInfoByWbsId(Long wbsId,Integer classify) {
+        return informationQueryMapper.selectOne(new QueryWrapper<InformationQuery>().eq("wbs_id",wbsId).eq("classify",classify));
     }
 
+
     @Override
     public void update(InformationQuery iq) {
         informationQueryMapper.updateById(iq);

+ 28 - 5
blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml

@@ -57,6 +57,7 @@
         <result column="is_element" property="isElement"/>
         <result column="pdf_page_url" property="pdfPageUrl"/>
         <result column="fid" property="fid"/>
+        <result column="archive_sort" property="archiveSort"/>
         <result column="rectification" property="rectification"/>
         <result column="m_wbs_tree_contract_p_key_id" property="mWbsTreeContractPKeyId"/>
         <result column="u_image_classification_file_id" property="uImageClassificationFileId"/>
@@ -90,6 +91,19 @@
         <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.isArchive != null and vo.isArchive != ''">
+            <choose>
+                <when test="vo.isArchive == 0">
+                    AND (u.is_archive = #{vo.isArchive} OR u.is_archive IS NULL)
+                </when>
+                <otherwise>
+                    AND u.is_archive = #{vo.isArchive}
+                </otherwise>
+            </choose>
+        </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,9 +175,18 @@
             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.isArchive != null and vo.isArchive != ''">
+            <choose>
+                <when test="vo.isArchive == 0">
+                    AND (u.is_archive = #{vo.isArchive} OR u.is_archive IS NULL)
+                </when>
+                <otherwise>
+                    AND u.is_archive = #{vo.isArchive}
+                </otherwise>
+            </choose>
         </if>
-
         <if test="vo.rectification == null and vo.archiveId == null">
             and (u.is_auto_file is null or u.is_auto_file != 1)
         </if>
@@ -354,7 +377,7 @@
                 #{id}
             </foreach>
         </if>
-        order by sort,sort_num,create_time
+        order by archive_sort,sort,sort_num,create_time
     </select>
     <select id="getArchiveFileByFileIds" resultMap="archiveFileResultMap">
         select * from u_archive_file where is_deleted = 0
@@ -364,7 +387,7 @@
                 #{id}
             </foreach>
         </if>
-        order by sort,sort_num,create_time
+        order by archive_sort,sort,sort_num,create_time
     </select>
     <select id="getListByProjectId" resultType="org.springblade.business.entity.ArchiveFile">
         select *
@@ -418,7 +441,7 @@
         from u_archive_file
         where archive_id = #{archiveId}
           and is_deleted = 0
-        order by sort,sort_num,create_time
+        order by archive_sort,sort,sort_num,create_time
     </select>
 
 

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

@@ -72,6 +72,7 @@ public class ArchiveFileServiceImpl extends BaseServiceImpl<ArchiveFileMapper, A
         pageVoList.forEach(vos -> {
             vos.setIsApprovalValue(new Integer("0").equals(vos.getStatus()) ? "未上报" : new Integer("1").equals(vos.getStatus()) ? "待审批" : new Integer("2").equals(vos.getStatus()) ? "已审批" : "已废除");
             vos.setIsCertificationValue(new Integer("1").equals(vos.getIsCertification()) ? "已认证" : "未认证");
+            vos.setIsArchive(vos.getIsArchive()==null?0: vos.getIsArchive());
             if (StringUtils.isNotEmpty(vos.getSheetSource())) {
                 sheetSourceList.forEach(source -> {
                     if (source.getDictKey().equals(vos.getSheetSource())) {
@@ -165,8 +166,12 @@ public class ArchiveFileServiceImpl extends BaseServiceImpl<ArchiveFileMapper, A
         }
         for (ArchiveFile vo : list) {
             // 修改所有的认证状态,如果是不需要认证就设置为已认证,如果是需要认证就设置为未认证
-            if (Func.isNotEmpty(vo.getIsNeedCertification()) && vo.getIsNeedCertification() == 0){
-                vo.setIsCertification(1);
+            if (Func.isNotEmpty(vo.getIsNeedCertification())){
+                if (vo.getIsNeedCertification() == 0) {
+                    vo.setIsCertification(1);
+                } else if (vo.getIsNeedCertification() == 1) {
+                    vo.setIsCertification(0);
+                }
             }
             //循环查看是否把需要审批改为不需要审批,  不可能把不需要审批改为需要审批 ,0不需要,1需要
             if (vo.getStatus() == 0 && vo.getIsApproval() == 0) {

+ 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<>();

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

@@ -713,7 +713,7 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
                                 String[] idss = query.stream()
                                         .map(item -> String.valueOf(item.getId()))
                                         .toArray(String[]::new);
-                                String update = "update u_entrust_info set status=3 where id in(" + String.join(",", idss) + ")";
+                                String update = "update u_entrust_info set status=4 where id in(" + String.join(",", idss) + ")";
                                 jdbcTemplate.execute(update);
                             }
                         }
@@ -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) {
                     }
                 }
@@ -2047,6 +2048,11 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
 
                     }
 
+                    /*if(type == 3 && task.getApprovalType()== 9){
+
+                        result = excelTabClient.synPdfKeyInfo(contractId, task.getFormDataId(), classify, projectId, header);
+
+                    }else*/
                     if (type == 1) {
                         //重新保存
                         long startTime_1 = System.currentTimeMillis();
@@ -2149,6 +2155,8 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
         return excelTabClient.synPDFInfo(contractId, nodePKeyIds, classify, projectId, header);
     }
 
+
+
     /**
      * 返回true代表满足条件 是超级管理员并且部门是在泓创智诚下面的部门
      */

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

@@ -107,7 +107,24 @@ public class TrialSelfInspectionRecordServiceImpl extends BaseServiceImpl<TrialS
                 queryWrapper.lambda().in(TrialSelfInspectionRecord::getNodeId, pIds);
             }
             if (StringUtils.isNotEmpty(dto.getQueryValue())) {
-                queryWrapper.lambda().like(TrialSelfInspectionRecord::getTrialProjectName, dto.getQueryValue());
+                //查询委托单编码
+                List<EntrustInfo> entrustInfos = entrustInfoMapper.selectList(Wrappers.<EntrustInfo>lambdaQuery()
+                        .like(EntrustInfo::getEntrustNo, dto.getQueryValue()));
+                List<Long> entrustIds;
+                if(CollectionUtil.isNotEmpty(entrustInfos)){
+                    entrustIds = entrustInfos.stream().map(EntrustInfo::getId).collect(Collectors.toList());
+                } else {
+                    entrustIds = null;
+                }
+
+                queryWrapper.lambda().and(
+                        wq ->
+                            wq.like(TrialSelfInspectionRecord::getTrialProjectName, dto.getQueryValue()).or()
+                                    .like(TrialSelfInspectionRecord::getRecordNo, dto.getQueryValue()).or()
+                                    .like(TrialSelfInspectionRecord::getReportNo, dto.getQueryValue()).or()
+                                    .like(TrialSelfInspectionRecord::getSpecificationNumber, dto.getQueryValue()).or()
+                                    .in(CollectionUtil.isNotEmpty(entrustIds),TrialSelfInspectionRecord::getEntrustId, entrustIds)
+                );
             }
             if (org.apache.commons.lang.StringUtils.isNotEmpty(dto.getStartTime()) && org.apache.commons.lang.StringUtils.isNotEmpty(dto.getEndTime())) {
                 String endTime = dto.getEndTime();
@@ -268,7 +285,7 @@ public class TrialSelfInspectionRecordServiceImpl extends BaseServiceImpl<TrialS
      * @throws FileNotFoundException
      */
     private String getMergePdfToTrialNew(Long contractId, Long nodeId, Integer type) throws FileNotFoundException {
-        String sql = "select pdf_url,e_visa_pdf_url from u_information_query where wbs_id='" + nodeId + "' and contract_id ='" + contractId + "' and classify = '" + type + "'" ;
+        String sql = "select pdf_url,e_visa_pdf_url from u_information_query where wbs_id='" + nodeId + "' and status in(0,1,2) and contract_id ='" + contractId + "' and classify = '" + type + "'" ;
         List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
         if (maps.size() >= 1) {
             Map<String, Object> stringObjectMap = maps.get(0);

+ 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) {

+ 47 - 1
blade-service/blade-business/src/main/java/org/springblade/business/utils/PDFUtil.java

@@ -104,7 +104,8 @@ public class PDFUtil {
             String[] lines = text.split("[ \\n]+");
             for(int k=0;k<lines.length;k++){
                 String textStr = lines[k];
-                if(textStr.indexOf("*")>=0){
+                int index = textStr.indexOf("*");
+                if(index>=0 && textStr.charAt(index+1) != '✹'){
                     textStr = textStr.substring(textStr.lastIndexOf("*")+1,textStr.length());
                 }
 
@@ -119,6 +120,17 @@ public class PDFUtil {
                         eVisaConfigList.add(txt);
                     }
                 }
+                textS = Func.toStrArray("\\*",textStr);
+                for(String txt : textS){
+                    for (int i = 0; i < txt.length(); i++) {
+                        if (!Character.isDigit(txt.charAt(i))) {
+                            txt=txt.substring(0,i);
+                        }
+                    }
+                    if (txt.length() >= 15 && Func.isNumeric(txt)) {
+                        eVisaConfigList.add(txt);
+                    }
+                }
 
                 // 特殊处理
                 if(textStr.indexOf("1")>=0){
@@ -128,6 +140,40 @@ public class PDFUtil {
                         eVisaConfigList.add(txt);
                     }
                 }
+                if (textStr.contains("✹")) {
+                    String textString = textStr.substring(textStr.indexOf("✹") + 1);
+                    if (textString.contains("||✹")) {
+                        String[] textS1 = Func.toStrArray("\\|\\|✹",textString);
+                        for(String txt : textS1){
+                            for (int i = 0; i < txt.length(); i++) {
+                                if (!Character.isDigit(txt.charAt(i))) {
+                                    txt=txt.substring(0,i);
+                                }
+                            }
+                            if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                                eVisaConfigList.add("✹" + txt);
+                            }
+                        }
+                    }
+                    if (textString.contains("*✹")) {
+                        String[] textS1 = Func.toStrArray("\\*✹",textString);
+                        for(String txt : textS1){
+                            for (int i = 0; i < txt.length(); i++) {
+                                if (!Character.isDigit(txt.charAt(i))) {
+                                    txt=txt.substring(0,i);
+                                }
+                            }
+                            if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                                eVisaConfigList.add("✹" + txt);
+                            }
+                        }
+                    }
+                    if(textString.startsWith("1")){
+                        if (textString.length() >= 15 && Func.isNumeric(textString)) {
+                            eVisaConfigList.add("✹" + textString);
+                        }
+                    }
+                }
             }
 
             List<String> unique = eVisaConfigList.stream().distinct().collect(Collectors.toList());

+ 144 - 53
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,42 +105,61 @@ 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();
         }
     }
 
-
     @Scheduled(cron = "0/30 * * * * ?")
     public void SplitPdfInfo() {
         //执行代码
@@ -146,7 +167,7 @@ public class ArchiveController {
         log.info("分解html开始");
         String sql = "select  * from u_archives_split_info where status =2 "; // and TIMESTAMPDIFF(MINUTE, create_time, NOW()) >=3";
         List<ArchivesSplitInfoVO> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(ArchivesSplitInfoVO.class));
-
+        log.info("分解html开始==="+query.size());
         if (query != null && query.size() >= 1) {
             for (ArchivesSplitInfoVO dataInfo : query) {
                 if (executor.getQueue().size() <= 10) {
@@ -154,7 +175,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 {
                                     /*===============执行批量任务===============*/
@@ -176,75 +197,108 @@ public class ArchiveController {
 
     public void signTaskBatchpng(ArchivesSplitInfoVO taskSign) {
         try {
+            System.out.println("分解001");
             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());
+            System.out.println("分解002="+htmlUrl);
 
             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[2]);
+                        }else{
                             startYm = Func.toInt(split[0]);
-                            endYm = Func.toInt(split[1]);
+                            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,rq);
                     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(bjbUrlPng);
+            if (!bgImgFile.exists()) {
+                savePdfAsImage(1, bjbUrl, bjbUrlPng);
+            }
+            String state = OcrTitle(bjbUrlPng,"3");
+            if(state.equals("1")){
+                saveDataToMysql(bjbUrl,"背脊表",taskSign.getId(),1,101,dutyUser,"");
+            }
+            bgImgFile.delete();
+
 
             // 修改任务状态
             String updateSql = "update u_archives_split_info set status=3 where id=" + taskSign.getId();
@@ -259,21 +313,24 @@ public class ArchiveController {
             jdbcTemplate.execute(taxkSql);
             jdbcTemplate.execute(taxkSql2);
 
-            // 修改完成情况
+            // 删除html
+            File fileHtml = new File(fmlUrl);
+            fileHtml.delete();
 
+            // 修改完成情况
             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 +338,7 @@ public class ArchiveController {
                     new InputStreamReader(process.getInputStream()));
             String htmlUrl;
             while ((htmlUrl = reader.readLine()) != null) {
-                System.out.println("222" + htmlUrl);
+                System.out.println(htmlUrl);
                 if (htmlUrl.indexOf("html文件路径") >= 0 && htmlUrl.indexOf("_001.html") >= 0 && htmlUrl.indexOf("archiveSplit") >= 0) {
                     lasHhtmlUrl = htmlUrl.replace("html文件路径", "");
                 }
@@ -299,6 +356,38 @@ public class ArchiveController {
         }
     }
 
+    public static 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 +418,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 +451,7 @@ 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,String fileTime) {
         // 获取封面信息
         long newPkId = SnowFlakeUtil.getId(); //主键Id
         File fmfile = new File(upFileUrl);
@@ -375,13 +464,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 + "','"+fileTime+"','" + 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 +479,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,14 +71,19 @@ 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" +
-                " from u_task_batch where is_deleted=0 GROUP BY JSON_EXTRACT(json_data, '$.formDataId'),sign_type ORDER BY sign_type ASC ";
+                "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));
 
         if (query != null && query.size() >= 1) {
             for (TaskSignInfoVO dataInfo : query) {
-                if (executor.getQueue().size() <= 10) {
+                if(dataInfo.getSigType() == 2 && dataInfo.getIsSignature() > 0){
+                    continue;
+                }
+                if (executor.getQueue().size() <= 20) {
                     Boolean aBoolean = RedisTemplate.hasKey("sign-" + dataInfo.getFormDataId());
                     if (!aBoolean) {
                         if (dataInfo.getSigType() == 2) {

+ 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);
 }

+ 90 - 16
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");
                 }
             }
@@ -509,11 +511,27 @@ public class EVDataServiceImpl implements EVDataService {
         String[] strArray = Func.toStrArray(task.getUserId());
         String[] userNames = Func.toStrArray(task.getNickName());
         List<Map<String, Object>> maps = new ArrayList<>();
+        String[] split = ids.split(",");
+        StringBuilder dqIds = new StringBuilder();
+        StringBuilder signIds = new StringBuilder();
+        for (String id : split) {
+            if (id != null && id.contains("✹")) {
+                signIds.append(id.substring(1)).append( ",");
+            } else {
+                dqIds.append(id).append( ",");
+            }
+        }
+        if (dqIds.length()>0) {
+            dqIds.deleteCharAt(dqIds.length()-1);
+        }
+        if (signIds.length()>0) {
+            signIds.deleteCharAt(signIds.length()-1);
+        }
         if(strArray!=null && strArray.length>0) {
             for (int i = 0; i < strArray.length; i++) {
-                String sqlinfo = " SELECT * from ( SELECT a.id as keyWord,a.project_id,a.pyzbx ,a.pyzby,(SELECT acc_code from blade_user where id='" + strArray[i] + "' and is_deleted=0  ) as sealId from m_textdict_info a where  a.type =2 and a.id in (" + ids + ")  and sig_role_id in (SELECT DISTINCT c.role_id from m_project_assignment_user c  where c.contract_id=" + task.getContractId() + " and user_id=" + strArray[i] + " and c.is_deleted=0 ) ) x where x.sealId is not null ";
+                String sqlinfo = " SELECT * from ( SELECT a.id as keyWord,a.project_id,a.pyzbx ,a.pyzby,(SELECT acc_code from blade_user where id='" + strArray[i] + "' and is_deleted=0  ) as sealId from m_textdict_info a where  a.type =2 and a.id in (" + dqIds + ")  and sig_role_id in (SELECT DISTINCT c.role_id from m_project_assignment_user c  where c.contract_id=" + task.getContractId() + " and user_id=" + strArray[i] + " and c.is_deleted=0 ) ) x where x.sealId is not null ";
                 if (task.getSigType() == 2) {
-                    sqlinfo = "SELECT a.id as keyWord,a.pyzbx,a.pyzby,b.certificate_number as sealId from m_textdict_info a ,m_sign_pfx_file b where a.sig_role_id = b.pfx_type and b.project_contract_role like '%" + task.getContractId() + "%' and a.is_deleted=0 and b.is_deleted=0 and a.type=6 and a.id in(" + ids + ")";
+                    sqlinfo = "SELECT a.id as keyWord,a.pyzbx,a.pyzby,b.certificate_number as sealId from m_textdict_info a ,m_sign_pfx_file b where a.sig_role_id = b.pfx_type and b.project_contract_role like '%" + task.getContractId() + "%' and a.is_deleted=0 and b.is_deleted=0 and a.type=6 and a.id in(" + dqIds + ")";
                     System.out.println("东方中讯--签章--" + sqlinfo);
                 } else {
                     System.out.println("东方中讯--签字--" + sqlinfo);
@@ -541,6 +559,25 @@ public class EVDataServiceImpl implements EVDataService {
                         }
                     }
                 }
+                if (signIds.length() > 0 && task.getSigType() != 2) {
+                    String sql = "SELECT * from ( SELECT a.conf_id as keyWord,0.0 as pyzbx ,0.0 as pyzby,(SELECT acc_code from blade_user where id = " + strArray[i] + " and is_deleted=0  ) as sealId " +
+                            "from m_sign_config_relation a where a.type = 1 and a.is_deleted = 0 and a.conf_id in (" + signIds + ") " +
+                            "and a.relation_id in (SELECT DISTINCT c.role_id from m_project_assignment_user c  where c.contract_id= " + task.getContractId() + " and user_id= " + strArray[i] + " and c.is_deleted=0 ) ) x where x.sealId is not null ";
+                    List<Map<String, Object>> maps3 = jdbcTemplate.queryForList(sql);
+                    System.out.println("东方中讯--签字--key =" + sql);
+                    if (!maps3.isEmpty()) {
+                        Map<String, List<Map<String, Object>>> peopleByAge = maps2.stream()
+                                .collect(Collectors.groupingBy(hada -> (Func.toStr(hada.get("keyWord")))));
+                        for (String keyId : peopleByAge.keySet()) {
+                            List<Map<String, Object>> keyList = peopleByAge.get(keyId);
+                            if (keyList != null && !keyList.isEmpty()) {
+                                Map<String, Object> map = keyList.get(0);
+                                map.put("keyWord", "✹" + map.get("keyWord"));
+                                maps.add(map);
+                            }
+                        }
+                    }
+                }
             }
         }
         return maps;
@@ -553,19 +590,33 @@ public class EVDataServiceImpl implements EVDataService {
         String[] userNames = Func.toStrArray(task.getNickName());
         List<SealStrategyVO> sealStrategyVOS = new ArrayList<>();
 
-
+        String[] split = ids.split(",");
+        StringBuilder dqIds = new StringBuilder();
+        StringBuilder signIds = new StringBuilder();
+        for (String id : split) {
+            if (id != null && id.contains("✹")) {
+                signIds.append(id.substring(1)).append( ",");
+            } else {
+                dqIds.append(id).append( ",");
+            }
+        }
+        if (dqIds.length()>0) {
+            dqIds.deleteCharAt(dqIds.length()-1);
+        }
+        if (signIds.length()>0) {
+            signIds.deleteCharAt(signIds.length()-1);
+        }
         if(strArray!=null && strArray.length>0){
             for (int i =0 ;i < strArray.length;i++) {
                 String userId =strArray[i];
-                String sqlinfo = "SELECT * from ( SELECT DISTINCT a.id,a.pyzbx ,a.pyzby,a.project_id,(SELECT signature_file_url from m_sign_pfx_file where is_register=1 and certificate_user_id='" + userId + "' and is_deleted=0  ) as signature_file_url, (SELECT wide from m_sign_pfx_file where is_register=1 and certificate_user_id='" + userId + "' and is_deleted=0  ) as wide ,(SELECT high from m_sign_pfx_file where is_register=1 and certificate_user_id='" + userId + "' and is_deleted=0  ) as high from m_textdict_info a where  a.type =2 and a.id in (" + ids + ") and sig_role_id in (SELECT DISTINCT c.role_id from m_project_assignment_user c  where c.contract_id=" + task.getContractId() + " and user_id=" + userId + " and c.is_deleted=0 ) ) x where x.signature_file_url is not null ";
+                String sqlinfo = "SELECT * from ( SELECT DISTINCT a.id,a.pyzbx ,a.pyzby,a.project_id,(SELECT signature_file_url from m_sign_pfx_file where is_register=1 and certificate_user_id='" + userId + "' and is_deleted=0  ) as signature_file_url, (SELECT wide from m_sign_pfx_file where is_register=1 and certificate_user_id='" + userId + "' and is_deleted=0  ) as wide ,(SELECT high from m_sign_pfx_file where is_register=1 and certificate_user_id='" + userId + "' and is_deleted=0  ) as high from m_textdict_info a where  a.type =2 and a.id in (" + dqIds + ") and sig_role_id in (SELECT DISTINCT c.role_id from m_project_assignment_user c  where c.contract_id=" + task.getContractId() + " and user_id=" + userId + " and c.is_deleted=0 ) ) x where x.signature_file_url is not null ";
                 if (task.getSigType() == 2) {
-                    sqlinfo = "SELECT a.id,a.pyzbx,a.pyzby,b.signature_file_url,b.id as sfId,a.project_id,b.certificate_password,b.certificate_user_name,b.certificate_number ,b.wide,b.high from m_textdict_info a ,m_sign_pfx_file b where a.sig_role_id = b.pfx_type and b.project_contract_role like '%" + task.getContractId() + "%' and a.is_deleted=0 and b.is_deleted=0 and a.type=6 and a.id in(" + ids + ") ";
+                    sqlinfo = "SELECT a.id,a.pyzbx,a.pyzby,b.signature_file_url,b.id as sfId,a.project_id,b.certificate_password,b.certificate_user_name,b.certificate_number ,b.wide,b.high from m_textdict_info a ,m_sign_pfx_file b where a.sig_role_id = b.pfx_type and b.project_contract_role like '%" + task.getContractId() + "%' and a.is_deleted=0 and b.is_deleted=0 and a.type=6 and a.id in(" + dqIds + ") ";
                     System.out.println("安心签--签章--=" + sqlinfo);
                 } else {
                     System.out.println("安心签--签字--=" + sqlinfo);
                 }
                 List<Map<String, Object>> maps2 = jdbcTemplate.queryForList(sqlinfo);
-
                 if (maps2 != null && maps2.size() > 0) {
                     List<Map<String, Object>> maps = new ArrayList<>();
                     Map<String, List<Map<String, Object>>> peopleByAge = maps2.stream()
@@ -574,22 +625,42 @@ public class EVDataServiceImpl implements EVDataService {
                     for (String keyId : peopleByAge.keySet()) {
                         int exId = 0;
                         List<Map<String, Object>> keyList = peopleByAge.get(keyId);
-                        if (keyList != null && keyList.size() == 1) {
+                        if (keyList != null && keyList.size() == 1 && keyList.get(0).get("signature_file_url") != null) {
                             maps.addAll(keyList);
                             exId = 1;
                         } else if (keyList != null && keyList.size() >= 2) {
                             for (Map<String, Object> datax : keyList) {
-                                if ((datax.get("project_id") + "").equals(task.getProjectId())) {
+                                if ((datax.get("project_id") + "").equals(task.getProjectId()) && datax.get("signature_file_url") != null) {
                                     maps.add(datax);
                                     exId = 1;
                                 }
                             }
                         }
-                        if (exId == 0) {
+                        if (exId == 0 && keyList != null) {
                             maps.add(keyList.get(0));
                         }
                     }
-
+                    if (signIds.length() > 0 && task.getSigType() != 2) {
+                        String sql = "SELECT * from ( SELECT DISTINCT a.conf_id as id,0.0 as pyzbx ,0.0 as pyzby,(SELECT signature_file_url from m_sign_pfx_file where is_register=1 and certificate_user_id='"
+                                + userId + "' and is_deleted=0  ) as signature_file_url, (SELECT wide from m_sign_pfx_file where is_register=1 and certificate_user_id='"
+                                + userId + "' and is_deleted=0  ) as wide ,(SELECT high from m_sign_pfx_file where is_register=1 and certificate_user_id='"
+                                + userId + "' and is_deleted=0  ) as high from m_sign_config_relation a where a.type = 1 and a.is_deleted = 0 and a.conf_id in (" + signIds + ") and a.relation_id in (SELECT DISTINCT c.role_id from m_project_assignment_user c  where c.contract_id="
+                                + task.getContractId() + " and user_id=" + userId + " and c.is_deleted=0 ) ) x where x.signature_file_url is not null ";
+                        List<Map<String, Object>> maps3 = jdbcTemplate.queryForList(sql);
+                        System.out.println("安心签--签章--key =" + sql);
+                        if (!maps3.isEmpty()) {
+                            Map<String, List<Map<String, Object>>> peopleByAges = maps3.stream()
+                                    .collect(Collectors.groupingBy(hada -> (Func.toStr(hada.get("id")))));
+                            for (String keyId : peopleByAges.keySet()) {
+                                List<Map<String, Object>> keyList = peopleByAges.get(keyId);
+                                if (keyList != null && !keyList.isEmpty()) {
+                                    Map<String, Object> map = keyList.get(0);
+                                    map.put("id", "✹" + map.get("id"));
+                                    maps.add(map);
+                                }
+                            }
+                        }
+                    }
                     if (maps == null || maps.size() <= 0) {
                         break;
                     }
@@ -663,7 +734,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 +742,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 +773,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);

+ 7 - 1
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/utils/FileUtils.java

@@ -108,7 +108,13 @@ public class FileUtils {
             if (url.isEmpty() || url.equals("")) {
                 return "0";
             }
-            InputStream pdfInputStream = CommonUtil.getOSSInputStream(url);
+            File file1 = new File(url);
+            InputStream pdfInputStream;
+            if(file1.exists()){
+                pdfInputStream = new FileInputStream(file1);
+            }else{
+                pdfInputStream = CommonUtil.getOSSInputStream(url);
+            }
             //获取这份文件的页数并设置签章策略
             //获取PDF文件
             PDDocument document = PDDocument.load(pdfInputStream);

+ 100 - 10
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/utils/PDFUtils.java

@@ -30,7 +30,8 @@ public class PDFUtils {
 
             for(int k=0;k<lines.length;k++){
                 String textStr = lines[k];
-                if(textStr.indexOf("*")>=0){
+                int index = textStr.indexOf("*");
+                if(index>=0 && textStr.charAt(index+1) != '✹'){
                     textStr = textStr.substring(textStr.lastIndexOf("*")+1,textStr.length());
                 }
 
@@ -41,25 +42,70 @@ public class PDFUtils {
                             txt=txt.substring(0,i);
                         }
                     }
-                    if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                    if (txt.length() >= 15 && Func.isNumeric(txt)) {
                         eVisaConfigList.add(txt);
                     }
-                    if (taskApp.getPdfDate() == null || taskApp.getPdfDate().isEmpty()) {
-                        Matcher matcher = pattern.matcher(txt);
-                        if(matcher.matches()){
-                            taskApp.setPdfDate(matcher.group(1));
+                }
+                if (taskApp.getPdfDate() == null || taskApp.getPdfDate().isEmpty()) {
+                    Matcher matcher = pattern.matcher(textStr);
+                    if(matcher.matches()){
+                        taskApp.setPdfDate(matcher.group(1));
+                    }
+                }
+                textS = Func.toStrArray("\\*",textStr);
+                for(String txt : textS){
+                    for (int i = 0; i < txt.length(); i++) {
+                        if (!Character.isDigit(txt.charAt(i))) {
+                            txt=txt.substring(0,i);
                         }
                     }
+                    if (txt.length() >= 15 && Func.isNumeric(txt)) {
+                        eVisaConfigList.add(txt);
+                    }
                 }
 
                 // 特殊处理
                 if(textStr.indexOf("1")>=0){
                     String txt = textStr.substring(textStr.indexOf("1"));
-                    if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                    if (txt.length() >= 15 && Func.isNumeric(txt)) {
                         System.out.println(txt);
                         eVisaConfigList.add(txt);
                     }
                 }
+                if (textStr.contains("✹")) {
+                    String textString = textStr.substring(textStr.indexOf("✹") + 1);
+                    if (textString.contains("||✹")) {
+                        String[] textS1 = Func.toStrArray("\\|\\|✹",textString);
+                        for(String txt : textS1){
+                            for (int i = 0; i < txt.length(); i++) {
+                                if (!Character.isDigit(txt.charAt(i))) {
+                                    txt=txt.substring(0,i);
+                                }
+                            }
+                            if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                                eVisaConfigList.add("✹" + txt);
+                            }
+                        }
+                    }
+                    if (textString.contains("*✹")) {
+                        String[] textS1 = Func.toStrArray("\\*✹",textString);
+                        for(String txt : textS1){
+                            for (int i = 0; i < txt.length(); i++) {
+                                if (!Character.isDigit(txt.charAt(i))) {
+                                    txt=txt.substring(0,i);
+                                }
+                            }
+                            if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                                eVisaConfigList.add("✹" + txt);
+                            }
+                        }
+                    }
+                    if(textString.startsWith("1")){
+                        if (textString.length() >= 15 && Func.isNumeric(textString)) {
+                            eVisaConfigList.add("✹" + textString);
+                        }
+                    }
+                }
             }
 
 
@@ -86,11 +132,10 @@ public class PDFUtils {
             PDFTextStripper stripper = new PDFTextStripper();
             String text = stripper.getText(document);
             String[] lines = text.split("[ \\n]+");
-            String regex = "^\\d{4}年\\d{2}月\\d{2}日$";
-
             for(int k=0;k<lines.length;k++){
                 String textStr = lines[k];
-                if(textStr.indexOf("*")>=0){
+                int index = textStr.indexOf("*");
+                if(index>=0 && textStr.charAt(index+1) != '✹'){
                     textStr = textStr.substring(textStr.lastIndexOf("*")+1,textStr.length());
                 }
 
@@ -105,6 +150,17 @@ public class PDFUtils {
                         eVisaConfigList.add(txt);
                     }
                 }
+                textS = Func.toStrArray("\\*",textStr);
+                for(String txt : textS){
+                    for (int i = 0; i < txt.length(); i++) {
+                        if (!Character.isDigit(txt.charAt(i))) {
+                            txt=txt.substring(0,i);
+                        }
+                    }
+                    if (txt.length() >= 15 && Func.isNumeric(txt)) {
+                        eVisaConfigList.add(txt);
+                    }
+                }
 
                 // 特殊处理
                 if(textStr.indexOf("1")>=0){
@@ -114,6 +170,40 @@ public class PDFUtils {
                         eVisaConfigList.add(txt);
                     }
                 }
+                if (textStr.contains("✹")) {
+                    String textString = textStr.substring(textStr.indexOf("✹") + 1);
+                    if (textString.contains("||✹")) {
+                        String[] textS1 = Func.toStrArray("\\|\\|✹",textString);
+                        for(String txt : textS1){
+                            for (int i = 0; i < txt.length(); i++) {
+                                if (!Character.isDigit(txt.charAt(i))) {
+                                    txt=txt.substring(0,i);
+                                }
+                            }
+                            if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                                eVisaConfigList.add("✹" + txt);
+                            }
+                        }
+                    }
+                    if (textString.contains("*✹")) {
+                        String[] textS1 = Func.toStrArray("\\*✹",textString);
+                        for(String txt : textS1){
+                            for (int i = 0; i < txt.length(); i++) {
+                                if (!Character.isDigit(txt.charAt(i))) {
+                                    txt=txt.substring(0,i);
+                                }
+                            }
+                            if (txt.length() >= 15 && Func.isNumeric(txt)||(Func.isNumeric(txt)&&txt.length()==8&&txt.startsWith("123"))) {
+                                eVisaConfigList.add("✹" + txt);
+                            }
+                        }
+                    }
+                    if(textString.startsWith("1")){
+                        if (textString.length() >= 15 && Func.isNumeric(textString)) {
+                            eVisaConfigList.add("✹" + textString);
+                        }
+                    }
+                }
             }
 
 

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

@@ -208,6 +208,12 @@
             <version>7.2.4</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>2.10.3</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 3 - 2
blade-service/blade-manager/src/main/java/com/mixsmart/utils/CustomFunction.java

@@ -37,7 +37,7 @@ import static java.math.BigDecimal.ROUND_HALF_UP;
 
 
 /**
- * @author yangyj
+ * @author yangyjT(com.mixsmart.utils.CustomFunction).dateAfter(T(com.mixsmart.utils.CustomFunction).dateMax(E['m_20220928150650_1575019183173795840:key_9']),28)
  */
 public class CustomFunction {
 
@@ -450,13 +450,14 @@ public class CustomFunction {
             List<String> result = new ArrayList<>();
             if(ListUtils.isNotEmpty(dateList)&&ListUtils.isNotEmpty(offSet)){
                 for(int i=0;i<dateList.size();i++){
-                    String d= dateList.get(i);
+                    String d= dateList.get(i).replaceAll("[\"']", "");;
                     if(StringUtils.isNotEmpty(d)) {
                         if(d.contains(",")){
                             String[] arr=d.replaceAll("[\\[\\]\\s]+","").split(",");
                             d=arr[arr.length-1];
                         }
                         Integer n = StringUtils.handObj2Integer(offSet.size() > i ? offSet.get(i) : offSet.getLast());
+                        // 日期需要正确的格式,需要判断
                         DateTime dt = new DateTime(d);
                         dt.offset(DateField.HOUR_OF_DAY, 24 * n);
                         result.add(dt.toString(DatePattern.CHINESE_DATE_PATTERN));

Some files were not shown because too many files changed in this diff