Pārlūkot izejas kodu

Merge branch 'dev' of http://219.151.181.73:3000/zhuwei/bladex into dev

laibulaizheli 1 mēnesi atpakaļ
vecāks
revīzija
881c2cbb96
30 mainītis faili ar 1240 papildinājumiem un 99 dzēšanām
  1. 14 0
      blade-common/pom.xml
  2. 238 0
      blade-common/src/main/java/org/springblade/common/utils/DeepSeekClient.java
  3. 34 0
      blade-common/src/main/java/org/springblade/common/vo/DeepSeekResponse.java
  4. 5 4
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/CommonFileClient.java
  5. 24 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/FeignConfig.java
  6. 10 0
      blade-ops/blade-resource/pom.xml
  7. 5 4
      blade-ops/blade-resource/src/main/java/org/springblade/resource/feign/CommonFileClientImpl.java
  8. 64 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/entity/ArchiveAiName.java
  9. 11 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchiveAiNameVO.java
  10. 11 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ArchiveAiNameVO1.java
  11. 16 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/dto/AddBussFileSortDTO.java
  12. 2 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/TableFile.java
  13. 10 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/CurrentNode.java
  14. 155 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveAiNameController.java
  15. 9 14
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchivesAutoController.java
  16. 44 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveAiNameMapper.java
  17. 32 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveAiNameMapper.xml
  18. 45 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchiveAiNameService.java
  19. 1 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchivesAutoService.java
  20. 148 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveAiNameServiceImpl.java
  21. 4 4
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveAutoPdfServiceImpl.java
  22. 32 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  23. 159 38
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java
  24. 12 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TableFileController.java
  25. 4 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/TableFileMapper.xml
  26. 27 25
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ExcelTabServiceImpl.java
  27. 40 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java
  28. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java
  29. 76 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/utils/ComplexStringComparator.java
  30. 7 2
      blade-service/blade-meter/src/main/java/org/springblade/meter/controller/TaskController.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>

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

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

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

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

@@ -0,0 +1,24 @@
+package org.springblade.resource.feign;
+
+import feign.codec.Encoder;
+import feign.form.spring.SpringFormEncoder;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.SpringEncoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class FeignConfig {
+    private final ObjectFactory<HttpMessageConverters> messageConverters;
+
+    public FeignConfig(ObjectFactory<HttpMessageConverters> messageConverters) {
+        this.messageConverters = messageConverters;
+    }
+
+    @Bean
+    public Encoder feignEncoder() {
+        // 默认使用 SpringEncoder(支持 JSON),但如果遇到 MultipartFile 则用 SpringFormEncoder
+        return new SpringFormEncoder(new SpringEncoder(messageConverters));
+    }
+}

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

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

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

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

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

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

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

+ 9 - 14
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;
 
@@ -41,13 +38,10 @@ 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.business.feign.ArchiveFileClient;
-import org.springblade.common.constant.CommonConstant;
-import org.springblade.common.utils.CommonUtil;
+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;
@@ -56,15 +50,10 @@ 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.*;
@@ -76,7 +65,6 @@ 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.*;
 import java.util.concurrent.ExecutorService;
@@ -107,11 +95,11 @@ public class ArchivesAutoController extends BladeController {
 	private ExecutorService executorService;
 	@Autowired
 	private ITraceLogService iTraceLogService;
-
     private final JdbcTemplate jdbcTemplate;
     @Autowired
     private ArchiveFileClient archiveFileClient;
 
+
 	/**
 	 * 详情
 	 */
@@ -551,6 +539,13 @@ public class ArchivesAutoController extends BladeController {
 		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));
+	}
+
 	/**
 	 * 预览案卷文件
 	 */

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

+ 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 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchivesAutoService.java

@@ -168,4 +168,5 @@ public interface IArchivesAutoService extends BaseService<ArchivesAuto> {
 	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);
+	}
+}

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

@@ -41,10 +41,7 @@ import java.io.OutputStream;
 import java.io.*;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -865,6 +862,9 @@ public class ArchiveAutoPdfServiceImpl implements IArchiveAutoPdfService {
         try {
             if (file != null) {
                 url = file.getPdfFileUrl();
+                if(StringUtils.isNotEmpty(file.getEVisaFile())&& !Objects.equals(file.getPdfFileUrl(),file.getEVisaFile())){
+                    url=file.getEVisaFile();
+                }
                 if (StringUtil.isEmpty(url)) {
                     String fileUrl = file.getFileUrl();
                     if (fileUrl != null && StringUtils.isNotEmpty(fileUrl) && fileUrl.endsWith(".pdf")) {

+ 32 - 1
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -52,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;
@@ -152,6 +153,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	private final IArchiveNameService archiveNameService;
 
+	private final IArchiveAiNameService aiNameService;
+
 
 	@Override
 	public IPage<ArchivesAutoVO> selectArchivesAutoPage(IPage<ArchivesAutoVO> page, ArchivesAutoVO archivesAuto) {
@@ -1536,7 +1539,35 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		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);
+	}
 
 
 	/**

+ 159 - 38
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java

@@ -19,6 +19,8 @@ import com.spire.xls.collections.PicturesCollection;
 import com.spire.xls.core.spreadsheet.HTMLOptions;
 import io.swagger.annotations.*;
 import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 import lombok.SneakyThrows;
 import org.apache.commons.codec.Charsets;
 import org.apache.commons.io.IOUtils;
@@ -52,6 +54,7 @@ import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.constant.BladeConstant;
 import org.springblade.core.tool.utils.*;
 import org.springblade.manager.bean.TableInfo;
+import org.springblade.manager.dto.AddBussFileSortDTO;
 import org.springblade.manager.entity.*;
 import org.springblade.manager.enums.ExecuteType;
 import org.springblade.manager.mapper.ExcelTabMapper;
@@ -1710,7 +1713,7 @@ public class ExcelTabController extends BladeController {
     /**
      * 上传文件
      *
-     * @param file 文件
+     * @param files 文件
      * @return ObjectStat
      */
     @SneakyThrows
@@ -1725,40 +1728,43 @@ public class ExcelTabController extends BladeController {
             @ApiImplicitParam(name = "classify", value = "classify", required = true),
             @ApiImplicitParam(name = "projectId", value = "projectId", required = true)
     })
-    public R addBussFile(@RequestParam("file") MultipartFile file, Long pkeyId, String nodeId, String contractId, String projectId, String classify) {
-
+    public R addBussFile(@RequestParam("file") MultipartFile[] file, Long pkeyId, String nodeId, String contractId, String projectId, String classify) {
+        List<TableFile>list=new ArrayList<>();
+        for (int i = 0; i < file.length; i++) {
+            R<BladeFile> bladeFile = iossClient.addFileInfo(file[i]);
+            BladeFile bladeFile1 = bladeFile.getData();
+            TableFile tableFile = new TableFile();
+            String fileExtension = FileUtil.getFileExtension(bladeFile1.getName()).toLowerCase();
+            tableFile.setTabId(pkeyId + "");
+            tableFile.setName(file[i].getOriginalFilename());
+            tableFile.setType(2);
+            tableFile.setDomainUrl(bladeFile1.getLink());
+            tableFile.setIsDeleted(0);
+            tableFile.setExtension(fileExtension);
+
+            NewBladeFile newBladeFile = new NewBladeFile();
+            if (fileExtension.contains("xlsx")) {
+                newBladeFile = this.commonFileClient.excelToPdf(file[i]);
+                tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("xls")) {
+                newBladeFile = this.commonFileClient.excelToPdf(file[i]);
+                tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("docx")) {
+                newBladeFile = this.commonFileClient.wordToPdf(file[i]);
+                tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("png") || fileExtension.contains("jpg") || fileExtension.contains("webp") || fileExtension.contains("apng") ||
+                    fileExtension.contains("bmp") || fileExtension.contains("jepg") || fileExtension.contains("tif") || fileExtension.contains("gif")) {
+                newBladeFile = this.commonFileClient.pngOrJpgToPdf(file[i]);
+                tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("pdf")) {
+                tableFile.setDomainPdfUrl(bladeFile1.getLink());
+            }
 
-        R<BladeFile> bladeFile = iossClient.addFileInfo(file);
-        BladeFile bladeFile1 = bladeFile.getData();
-        TableFile tableFile = new TableFile();
-        String fileExtension = FileUtil.getFileExtension(bladeFile1.getName()).toLowerCase();
-        tableFile.setTabId(pkeyId + "");
-        tableFile.setName(file.getOriginalFilename());
-        tableFile.setType(2);
-        tableFile.setDomainUrl(bladeFile1.getLink());
-        tableFile.setIsDeleted(0);
-        tableFile.setExtension(fileExtension);
-
-        NewBladeFile newBladeFile = new NewBladeFile();
-        if (fileExtension.contains("xlsx")) {
-            newBladeFile = this.commonFileClient.excelToPdf(file);
-            tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
-        } else if (fileExtension.contains("xls")) {
-            newBladeFile = this.commonFileClient.excelToPdf(file);
-            tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
-        } else if (fileExtension.contains("docx")) {
-            newBladeFile = this.commonFileClient.wordToPdf(file);
-            tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
-        } else if (fileExtension.contains("png") || fileExtension.contains("jpg") || fileExtension.contains("webp") || fileExtension.contains("apng") ||
-                fileExtension.contains("bmp") || fileExtension.contains("jepg") || fileExtension.contains("tif") || fileExtension.contains("gif")) {
-            newBladeFile = this.commonFileClient.pngOrJpgToPdf(file);
-            tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
-        } else if (fileExtension.contains("pdf")) {
-            tableFile.setDomainPdfUrl(bladeFile1.getLink());
+            tableFile.setStatus("finished");
+            list.add(tableFile);
         }
 
-        tableFile.setStatus("finished");
-        tableFileService.save(tableFile);
+        tableFileService.saveBatch(list);
 
         // 生成单个pdf
         excelTabService.getBussPdfInfo(pkeyId);
@@ -1789,7 +1795,7 @@ public class ExcelTabController extends BladeController {
             informationQueryClient.saveInfo(query);
         }
         excelTabService.getBussPdfs(nodeId, classify, contractId, projectId);
-        return R.data(tableFile.getId());
+        return R.status(true);
     }
 
     @GetMapping("/show-buss-tab")
@@ -1810,6 +1816,47 @@ public class ExcelTabController extends BladeController {
         excelTabService.getBussPdfInfo(pkeyId);
         //重新生成PDF修改queryInfo
         excelTabService.getBussPdfs(nodeId, classify, wbsTreeContract.getContractId(), wbsTreeContract.getProjectId());
+
+        //如果操作的的是没填写日期的表单
+        if(wbsTreeContract.getDateIsComplete()!=null&&wbsTreeContract.getDateIsComplete()==2){
+            List<WbsTreeContract> wbsTreeContracts = wbsTreeContractService.getBaseMapper().selectList(new LambdaQueryWrapper<>(WbsTreeContract.class).eq(WbsTreeContract::getPId, wbsTreeContract.getPId()).ne(WbsTreeContract::getPKeyId,wbsTreeContract.getPKeyId()));
+            if(!wbsTreeContracts.isEmpty()){
+                List<WbsTreeContract> list = wbsTreeContracts.stream().filter(w -> Objects.equals(2, w.getDateIsComplete())).collect(Collectors.toList());
+                //除了操作的表单外 不存在没填的日期
+                if(list.isEmpty()){
+                    //放开隐藏 设置祖级节点为2
+                    if(status==1){
+                        String ancestorsPId = wbsTreeContract.getAncestorsPId();
+                        if (ancestorsPId.startsWith("0,")) {
+                            ancestorsPId = ancestorsPId.substring(2);
+                        }
+                        ancestorsPId=ancestorsPId+","+wbsTreeContract.getPKeyId();
+                        List<Long> longList = Arrays.stream(ancestorsPId.split(","))
+                                .map(Long::valueOf)
+                                .collect(Collectors.toList());
+                        UpdateWrapper<WbsTreeContract> updateWrapper1 = new UpdateWrapper<>();
+                        updateWrapper1.in("p_key_id", longList);
+                        updateWrapper1.set("date_is_complete",2);
+                        wbsTreeContractService.update(updateWrapper1);
+                    //隐藏表单  设置祖级节点为1
+                    }if(status==2){
+                        String ancestorsPId = wbsTreeContract.getAncestorsPId();
+                        if (ancestorsPId.startsWith("0,")) {
+                            ancestorsPId = ancestorsPId.substring(2);
+                        }
+                        ancestorsPId=ancestorsPId+","+wbsTreeContract.getPKeyId();
+                        List<Long> longList = Arrays.stream(ancestorsPId.split(","))
+                                .map(Long::valueOf)
+                                .collect(Collectors.toList());
+                        UpdateWrapper<WbsTreeContract> updateWrapper1 = new UpdateWrapper<>();
+                        updateWrapper1.in("p_key_id", longList);
+                        updateWrapper1.set("date_is_complete",1);
+                        wbsTreeContractService.update(updateWrapper1);
+                    }
+                }
+            }
+
+        }
         return R.data("成功");
     }
 
@@ -4402,6 +4449,43 @@ public class ExcelTabController extends BladeController {
 
 
     }
+    @Data
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class BussfileDTO{
+        private String name;
+        private String url;
+    }
+    @PostMapping("/previewBussfile")
+    public R<List<BussfileDTO>> previewBussfile(@RequestPart("file")MultipartFile[] file){
+        List<BussfileDTO>list=new ArrayList<>();
+        for (int i = 0; i < file.length; i++){
+            BussfileDTO dto = new BussfileDTO();
+            R<BladeFile> bladeFile = iossClient.addFileInfo(file[i]);
+            BladeFile bladeFile1 = bladeFile.getData();
+            String fileExtension = FileUtil.getFileExtension(bladeFile1.getName()).toLowerCase();
+            NewBladeFile newBladeFile = new NewBladeFile();
+            if (fileExtension.contains("xlsx")) {
+                newBladeFile = this.commonFileClient.excelToPdf(file[i]);
+                dto.setUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("xls")) {
+                newBladeFile = this.commonFileClient.excelToPdf(file[i]);
+                dto.setUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("docx")) {
+                newBladeFile = this.commonFileClient.wordToPdf(file[i]);
+                dto.setUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("png") || fileExtension.contains("jpg") || fileExtension.contains("webp") || fileExtension.contains("apng") ||
+                    fileExtension.contains("bmp") || fileExtension.contains("jepg") || fileExtension.contains("tif") || fileExtension.contains("gif")) {
+                newBladeFile = this.commonFileClient.pngOrJpgToPdf(file[i]);
+                dto.setUrl(newBladeFile.getPdfUrl());
+            } else if (fileExtension.contains("pdf")) {
+                dto.setUrl(bladeFile1.getLink());
+            }
+            dto.setName(file[i].getOriginalFilename());
+            list.add(dto);
+        }
+        return R.data(list);
+    }
 
     /**
      * 质检附件追加
@@ -4417,11 +4501,12 @@ public class ExcelTabController extends BladeController {
             @ApiImplicitParam(name = "nodeId", value = "nodeId", required = true),
 
     })
-    public R addBussFile(@RequestParam("file") MultipartFile[] file, String nodeId, Integer type, Long contractId, Integer classify, BladeUser b) {
+    public R addBussFile(Integer classify,Long contractId,@RequestParam("file") MultipartFile[] file, String nodeId, Integer type,BladeUser b) {
         List<TableFile> fileList = new ArrayList<>();
         if (file != null && file.length >= 1) {
-            for (MultipartFile multipartFile : file) {
-                R<BladeFile> bladeFile = iossClient.addFileInfo(multipartFile);
+            for (int i = 0; i < file.length; i++) {
+
+                R<BladeFile> bladeFile = iossClient.addFileInfo(file[i]);
                 BladeFile bladeFile1 = bladeFile.getData();
 
                 TableFile tableFile = new TableFile();
@@ -4429,7 +4514,7 @@ public class ExcelTabController extends BladeController {
                 String fileExtension = FileUtil.getFileExtension(bladeFile1.getName()).toLowerCase();
                 tableFile.setTabId(nodeId + "");
                 tableFile.setContractId(contractId);
-                tableFile.setName(multipartFile.getOriginalFilename());
+                tableFile.setName(file[i].getOriginalFilename());
                 tableFile.setType(type); //10 代表附件
                 if (type == 10 || type == 11 || type == 12 || type == 20 || type == 21 || type == 22) {
                     tableFile.setDomainPdfUrl(bladeFile1.getLink());
@@ -4439,6 +4524,23 @@ public class ExcelTabController extends BladeController {
                 tableFile.setExtension(fileExtension);
                 tableFile.setClassify(classify);
                 fileList.add(tableFile);
+                NewBladeFile newBladeFile = new NewBladeFile();
+                if (fileExtension.contains("xlsx")) {
+                    newBladeFile = this.commonFileClient.excelToPdf(file[i]);
+                    tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+                } else if (fileExtension.contains("xls")) {
+                    newBladeFile = this.commonFileClient.excelToPdf(file[i]);
+                    tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+                } else if (fileExtension.contains("docx")) {
+                    newBladeFile = this.commonFileClient.wordToPdf(file[i]);
+                    tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+                } else if (fileExtension.contains("png") || fileExtension.contains("jpg") || fileExtension.contains("webp") || fileExtension.contains("apng") ||
+                        fileExtension.contains("bmp") || fileExtension.contains("jepg") || fileExtension.contains("tif") || fileExtension.contains("gif")) {
+                    newBladeFile = this.commonFileClient.pngOrJpgToPdf(file[i]);
+                    tableFile.setDomainPdfUrl(newBladeFile.getPdfUrl());
+                } else if (fileExtension.contains("pdf")) {
+                    tableFile.setDomainPdfUrl(bladeFile1.getLink());
+                }
             }
             tableFileService.saveOrUpdateBatch(fileList);
             String file_path = FileUtils.getSysLocalFileUrl();
@@ -4453,7 +4555,9 @@ public class ExcelTabController extends BladeController {
             if (tabpdf2.exists()) {
                 tabpdf2.delete();
             }
-            FileUtils.mergePdfPublicMethods(datainfo, listPdf);
+            if(datainfo.size()>=2){
+                FileUtils.mergePdfPublicMethods(datainfo, listPdf);
+            }
             String netUrl = "";
             BladeFile bladeFile = this.newIOSSClient.uploadFile(nodeId + ".pdf", listPdf);
             if (bladeFile != null && ObjectUtils.isNotEmpty(bladeFile.getLink())) {
@@ -4509,6 +4613,23 @@ public class ExcelTabController extends BladeController {
         }
     }
 
+    @PostMapping("/add-bussfile-node-sort")
+    public R addBussFileNodeSort(@RequestBody AddBussFileSortDTO dto){
+        List<TableFile> tableFileList = tableFileService.list(new LambdaQueryWrapper<>(TableFile.class).eq(TableFile::getClassify, dto.getClassify()).eq(TableFile::getContractId, dto.getContractId()).eq(TableFile::getTabId, dto.getId()).eq(TableFile::getType, dto.getType()));
+        if(dto.getList().length>0&&!tableFileList.isEmpty()){
+            for (TableFile file : tableFileList) {
+                for (int i = 0; i < dto.getList().length; i++) {
+                    if(dto.getList()[i].equals(file.getName())){
+                        file.setSort(i);
+                        break;
+                    }
+                }
+            }
+            return R.status(tableFileService.updateBatchById(tableFileList));
+       }
+       return R.status(true);
+    }
+
 
     @PostMapping("/save_nodeId")
     @ApiOperationSupport(order = 72)

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

@@ -55,6 +55,7 @@ import org.springblade.core.boot.ctrl.BladeController;
 import java.rmi.ServerException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
@@ -137,7 +138,7 @@ public class TableFileController extends BladeController {
                 updateWrapper.set("tab_file_type", 1);
                 wbsTreeContractService.update(updateWrapper);
                 WbsTreeContract wbsTreeContract = wbsTreeContractService.getBaseMapper().selectOne(new QueryWrapper<WbsTreeContract>().eq("p_key_id", tableFile.getTabId()));
-                if(wbsTreeContract.getNodeClass().equals(Integer.valueOf(2))){
+                if(Objects.equals(wbsTreeContract.getNodeClass(), 2)){
                     Integer classify =
                             Arrays.asList("1", "2", "3").contains(wbsTreeContract.getTableOwner()) ? 1 :
                                     Arrays.asList("4", "5", "6").contains(wbsTreeContract.getTableOwner()) ? 2 : 1;
@@ -279,4 +280,14 @@ public class TableFileController extends BladeController {
         }
         return R.data("成功");
     }
+    @PostMapping("/sort")
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "附件排序", notes = "传入listids")
+    public R sort(@RequestBody List<TableFile> list){
+        int i=1;
+        for (TableFile file : list) {
+            file.setSort(i++);
+        }
+      return R.status(tableFileService.updateBatchById(list));
+    }
 }

+ 4 - 3
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/TableFileMapper.xml

@@ -34,13 +34,14 @@
     </select>
 
     <select id="selectTableFileList" resultMap="TableFileVO">
-        select *,domain_url as url from m_table_file where is_deleted = 0 and type=2 and tab_id = #{pKid}
+        select *,domain_url as url from m_table_file where is_deleted = 0 and type=2 and tab_id = #{pKid} order by sort asc
     </select>
     <select id="getAllFileByIds" resultType="org.springblade.manager.entity.TableFile">
         SELECT mtf.* from m_table_file mtf WHERE is_deleted = 0 and classify = #{classify} and type = 2 and tab_id in
         <foreach collection="ids" item="id" open="(" close=")" separator=",">
             #{id}
         </foreach>
+        ORDER BY sort ASC
     </select>
 
     <delete id="delDataById">
@@ -51,10 +52,10 @@
     </delete>
 
     <select id="selectTableFileListByTen" resultMap="TableFileVO">
-        select *,domain_url as url from m_table_file where is_deleted = 0  and classify = #{classify} and type in (10,11,12,20,21,22) and tab_id = #{pKid}
+        select *,domain_pdf_url as url from m_table_file where is_deleted = 0  and classify = #{classify} and type in (10,11,12,20,21,22) and tab_id = #{pKid} Order by sort ASC
     </select>
 
     <select id="selectTableFileList1" resultMap="TableFileVO">
-        select *,domain_url as url from m_table_file where is_deleted = 0 and (type=20 or type=21 or type=22) and tab_id = #{pKid}
+        select *,domain_pdf_url as url from m_table_file where is_deleted = 0 and (type=20 or type=21 or type=22) and tab_id = #{pKid} Order by sort ASC
     </select>
 </mapper>

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

@@ -2511,25 +2511,27 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
         List<WbsTreeContract> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContract.class));
         if(!query.isEmpty()){
             WbsTreeContract contract = query.get(0);
-            contract.setDateIsComplete(dateFlag?1:2);
-            String ancestorsPId = contract.getAncestorsPId();
-            if (ancestorsPId.startsWith("0,")) {
-                ancestorsPId = ancestorsPId.substring(2);
-            }
-            ancestorsPId=ancestorsPId+","+contract.getPKeyId();
-            List<Long> longList = Arrays.stream(ancestorsPId.split(","))
-                    .map(Long::valueOf)
-                    .collect(Collectors.toList());
-            UpdateWrapper<WbsTreeContract> updateWrapper1 = new UpdateWrapper<>();
-            updateWrapper1.in("p_key_id", longList);
-            updateWrapper1.set("date_is_complete",contract.getDateIsComplete());
-            wbsTreeContractService.update(updateWrapper1);
-            if(StringUtils.isNotEmpty(firstDate)){
-                Integer classify =
-                        Arrays.asList("1", "2", "3").contains(contract.getTableOwner()) ? 1 :
-                                Arrays.asList("4", "5", "6").contains(contract.getTableOwner()) ? 2 : 1;
-                String updateQuery="update u_information_query set business_time= '"+firstDate+"' where wbs_id="+contract.getPKeyId()+" and classify="+classify;
-                jdbcTemplate.update(updateQuery);
+            if(!Objects.equals(contract.getIsBussShow(), 2)&&contract.getIsDeleted()==0){
+                contract.setDateIsComplete(dateFlag?1:2);
+                String ancestorsPId = contract.getAncestorsPId();
+                if (ancestorsPId.startsWith("0,")) {
+                    ancestorsPId = ancestorsPId.substring(2);
+                }
+                ancestorsPId=ancestorsPId+","+contract.getPKeyId();
+                List<Long> longList = Arrays.stream(ancestorsPId.split(","))
+                        .map(Long::valueOf)
+                        .collect(Collectors.toList());
+                UpdateWrapper<WbsTreeContract> updateWrapper1 = new UpdateWrapper<>();
+                updateWrapper1.in("p_key_id", longList);
+                updateWrapper1.set("date_is_complete",contract.getDateIsComplete());
+                wbsTreeContractService.update(updateWrapper1);
+                if(StringUtils.isNotEmpty(firstDate)){
+                    Integer classify =
+                            Arrays.asList("1", "2", "3").contains(contract.getTableOwner()) ? 1 :
+                                    Arrays.asList("4", "5", "6").contains(contract.getTableOwner()) ? 2 : 1;
+                    String updateQuery="update u_information_query set business_time= '"+firstDate+"' where wbs_id="+contract.getPKeyId()+" and classify="+classify;
+                    jdbcTemplate.update(updateQuery);
+                }
             }
         }
 
@@ -4976,12 +4978,12 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
         }
         chart(reData, wbsTreePrivate);
         if(ObjectUtil.isNotEmpty(jsonObject)){
-//            if(isCancel){
-//                if(isCancelList.size()>0){
-//                    List<String> finalIsCancelList = isCancelList;
-//                    jsonObject.entrySet().removeIf(entry -> finalIsCancelList.contains(entry.getKey()));
-//                }
-//            }
+            if(isCancel){
+                if(isCancelList.size()>0){
+                    List<String> finalIsCancelList = isCancelList;
+                    jsonObject.entrySet().removeIf(entry -> finalIsCancelList.contains(entry.getKey()));
+                }
+            }
             for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                 if(entry.getKey().contains("key")){
                     if(!reData.containsKey(entry.getKey())){

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

@@ -462,7 +462,37 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
 
                                 List<ItemBlock> itemBlockList = g.getList();
                                 int originSize = itemBlockList.size();
-                                List<Long> ids = getNodeType46(tec).stream().map(CurrentNode::getPkId).collect(Collectors.toList());
+                                List<CurrentNode> nodeType46 = getNodeType46(tec);
+                                List<Long> ids = nodeType46.stream().map(CurrentNode::getPkId).collect(Collectors.toList());
+
+                                Map<Long, CurrentNode> sortInfoMap  = nodeType46.stream().collect(Collectors.toMap(CurrentNode::getPkId, info -> info,
+                                        (oldInfo, newInfo) -> newInfo));
+                                //排序规则
+                                Comparator<ItemBlock> itemBlockComparator = Comparator
+                                        // 第一级:按sort排序(升序),缺失的排最后
+                                        .comparingInt((ItemBlock obj) ->
+                                                sortInfoMap.containsKey(obj.getPkeyId()) ?
+                                                        sortInfoMap.get(obj.getPkeyId()).getSort() :
+                                                        Integer.MAX_VALUE)
+
+                                        // 第二级:按title排序(自然顺序),缺失的排最后
+                                        .thenComparing(obj ->
+                                                        sortInfoMap.containsKey(obj.getPkeyId()) ?
+                                                                sortInfoMap.get(obj.getPkeyId()).getTitle() :
+                                                                null,
+                                                Comparator.nullsLast(Comparator.naturalOrder()))
+
+                                        // 第三级:按create_time排序(时间升序),缺失的排最后
+                                        .thenComparing(obj ->
+                                                        sortInfoMap.containsKey(obj.getPkeyId()) ?
+                                                                sortInfoMap.get(obj.getPkeyId()).getCreateTime() :
+                                                                null,
+                                                Comparator.nullsLast(Comparator.naturalOrder()));
+
+                                // 2. 对对象集合进行多级排序
+                                itemBlockList.sort(itemBlockComparator);
+
+
                                 /*清除那些已经不存在的工序*/
                                 itemBlockList.removeIf(ik -> !ids.contains(ik.getPkeyId()));
                                 if (itemBlockList.size() > 0) {
@@ -631,7 +661,15 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     }
 
     public void descendantType46(Object id, List<Map<String, Object>> listMaps) {
-        String sql = "select p_Key_id pkId,id,node_type nodeType,table_type tableType  from m_wbs_tree_contract where parent_id =? and is_deleted=0";
+        String sql = "select " +
+                        "p_Key_id pkId," +
+                        "id," +
+                        "node_type nodeType," +
+                        "table_type tableType," +
+                        "sort," +
+                        "IFNULL(if(length(trim(full_name))>0,full_name,node_name),node_name) AS title," +
+                        "create_time createTime " +
+                        "from m_wbs_tree_contract where parent_id =? and is_deleted=0";
         List<Map<String, Object>> tmp = this.jdbcTemplate.queryForList(sql, id);
         if (tmp.size() > 0) {
             for (Map<String, Object> map : tmp) {

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

@@ -1176,7 +1176,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         List<AppWbsTreeContractVO> voList = new ArrayList<>();
         List<TableFile> files2 = tableFileService.list(new LambdaQueryWrapper<TableFile>()
                 .eq(TableFile::getTabId,primaryKeyId)
-                .eq(TableFile::getClassify,classify));
+                .eq(TableFile::getClassify,classify).orderByAsc(TableFile::getSort));
         if (vos != null && vos.size() > 0) {
             List<Long> list = vos.stream().map(AppWbsTreeContractVO::getPKeyId).collect(Collectors.toList());
             List<TableFile> files = tableFileService.getAllFileByIds(list,classify);

+ 76 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/ComplexStringComparator.java

@@ -0,0 +1,76 @@
+package org.springblade.manager.utils;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Function;
+
+public class ComplexStringComparator<T> implements Comparator<T> {
+    private final Function<T, String> fieldExtractor;
+
+    public ComplexStringComparator(Function<T, String> fieldExtractor) {
+        this.fieldExtractor = fieldExtractor;
+    }
+
+    @Override
+    public int compare(T o1, T o2) {
+        String s1 = fieldExtractor.apply(o1);
+        String s2 = fieldExtractor.apply(o2);
+        return compareComplexStrings(s1, s2);
+    }
+
+    // 复杂字符串比较核心算法
+    private int compareComplexStrings(String s1, String s2) {
+        List<Object> parts1 = splitString(s1);
+        List<Object> parts2 = splitString(s2);
+
+        int minLength = Math.min(parts1.size(), parts2.size());
+        for (int i = 0; i < minLength; i++) {
+            Object p1 = parts1.get(i);
+            Object p2 = parts2.get(i);
+
+            if (p1 instanceof Integer && p2 instanceof Integer) {
+                int cmp = ((Integer) p1).compareTo((Integer) p2);
+                if (cmp != 0) return cmp;
+            } else {
+                int cmp = p1.toString().compareTo(p2.toString());
+                if (cmp != 0) return cmp;
+            }
+        }
+        return Integer.compare(parts1.size(), parts2.size());
+    }
+
+    // 字符串拆分工具方法(字母与数字分离)
+    private List<Object> splitString(String s) {
+        List<Object> parts = new ArrayList<>();
+        StringBuilder buffer = new StringBuilder();
+        int numFlag = -1; // 0:字母 1:数字
+
+        for (char c : s.toCharArray()) {
+            if (Character.isDigit(c)) {
+                if (numFlag == 0) {
+                    parts.add(buffer.toString());
+                    buffer.setLength(0);
+                }
+                numFlag = 1;
+                buffer.append(c);
+            } else {
+                if (numFlag == 1) {
+                    parts.add(Integer.parseInt(buffer.toString()));
+                    buffer.setLength(0);
+                }
+                numFlag = 0;
+                buffer.append(c);
+            }
+        }
+
+        if (buffer.length() > 0) {
+            if (numFlag == 1) {
+                parts.add(Integer.parseInt(buffer.toString()));
+            } else {
+                parts.add(buffer.toString());
+            }
+        }
+        return parts;
+    }
+}

+ 7 - 2
blade-service/blade-meter/src/main/java/org/springblade/meter/controller/TaskController.java

@@ -898,8 +898,13 @@ public class TaskController extends BladeController {
                         currentParallelId = parallel.getId();
                         break;
                     }
-                    if (parallel.getStatus() != 2) {
-                        name.add(parallel.getTaskUserName());
+                    //判断是否是流程审批
+                    String sql="select COALESCE(fixed_flow_link_type, 0) from u_fixed_flow_link where fixed_flow_id="+task.getFixedFlowId()+" and fixed_flow_link_user="+parallel.getTaskUser()+" limit 1";
+                    Integer fixedFlowLinkType =   jdbcTemplate.queryForObject(sql,Integer.class);
+                    if(fixedFlowLinkType==1){
+                        if (parallel.getStatus() != 2) {
+                            name.add(parallel.getTaskUserName());
+                        }
                     }
                 }
                 if (name.size() > 0) {