Преглед изворни кода

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

LHB пре 2 недеља
родитељ
комит
b2163a2920
57 измењених фајлова са 1806 додато и 224 уклоњено
  1. 14 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/dto/ScanFileMoveDTO.java
  2. 52 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/entity/ScanFile.java
  3. 35 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/entity/ScanFolder.java
  4. 18 0
      blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ScanFolderVO.java
  5. 3 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ArchiveFileClient.java
  6. 12 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ArchiveTreeContract.java
  7. 3 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ContractInfo.java
  8. 2 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/NodeBaseInfoVO.java
  9. 6 0
      blade-service-api/blade-meter-api/src/main/java/org/springblade/meter/feign/InterimPayCertificateItemClient.java
  10. 49 20
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchivesAutoController.java
  11. 178 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ScanFileController.java
  12. 48 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveTreeService.java
  13. 2 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml
  14. 26 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFileMapper.java
  15. 30 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFileMapper.xml
  16. 15 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFolderMapper.java
  17. 12 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFolderMapper.xml
  18. 10 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchivesAutoService.java
  19. 31 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/ScanFileService.java
  20. 5 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/ScanFolderService.java
  21. 9 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveNameServiceImpl.java
  22. 176 6
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  23. 520 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ScanFileServiceImpl.java
  24. 16 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ScanFolderServiceImpl.java
  25. 18 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/utils/FileUtils.java
  26. 2 2
      blade-service/blade-business/src/main/java/org/springblade/business/controller/InformationWriteQueryController.java
  27. 1 1
      blade-service/blade-business/src/main/java/org/springblade/business/controller/NeiWaiYeProgressController.java
  28. 16 3
      blade-service/blade-business/src/main/java/org/springblade/business/controller/TaskController.java
  29. 0 2
      blade-service/blade-business/src/main/java/org/springblade/business/controller/TrialSummaryController.java
  30. 5 0
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ArchiveFileClientImpl.java
  31. 1 1
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/TaskClientImpl.java
  32. 8 2
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml
  33. 1 1
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/InformationQueryMapper.java
  34. 17 3
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/InformationQueryMapper.xml
  35. 15 3
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ArchiveFileServiceImpl.java
  36. 2 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/InformationQueryServiceImpl.java
  37. 2 1
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/TaskServiceImpl.java
  38. 2 15
      blade-service/blade-dingding/src/main/java/org/springblade/dingding/service/impl/MeetingServiceImpl.java
  39. 6 3
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/ChekSignData.java
  40. 1 1
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/EVController.java
  41. 17 25
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVDataServiceImpl.java
  42. 192 56
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/ScrDataServiceImpl.java
  43. 10 2
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/utils/PdfAddimgUtil.java
  44. 11 8
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java
  45. 20 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/NodeBaseInfoController.java
  46. 1 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TrialSummaryClassificationConfigurationController.java
  47. 4 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java
  48. 49 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ArchiveTreeContractImpl.java
  49. 3 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.xml
  50. 3 5
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.xml
  51. 59 51
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java
  52. 29 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java
  53. 3 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeSynchronousRecordServiceImpl.java
  54. 1 1
      blade-service/blade-meter/src/main/java/org/springblade/meter/controller/TaskController.java
  55. 4 0
      blade-service/blade-meter/src/main/java/org/springblade/meter/service/IInterimPayCertificateItemService.java
  56. 10 0
      blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/InterimPayCertificateItemClientImpl.java
  57. 21 0
      blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/InterimPayCertificateItemServiceImpl.java

+ 14 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/dto/ScanFileMoveDTO.java

@@ -0,0 +1,14 @@
+package org.springblade.archive.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ScanFileMoveDTO {
+   private List<Long> ids;
+   private Long nodeId;
+}

+ 52 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/entity/ScanFile.java

@@ -0,0 +1,52 @@
+package org.springblade.archive.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.omg.CORBA.PRIVATE_MEMBER;
+
+@TableName(value = "scan_file")
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ScanFile {
+    @TableId(
+            value = "id",
+            type = IdType.ASSIGN_ID
+    )
+    private Long id;
+    @ApiModelProperty(value = "项目id")
+    private Long projectId;
+    @ApiModelProperty(value = "合同id")
+    private Long contractId;
+    @ApiModelProperty(value = "文件夹id")
+    private Long folderId;
+    @ApiModelProperty(value = "序号")
+    private Integer sort;
+    @ApiModelProperty(value = "数字编号")
+    private Integer digitalNum;
+    @ApiModelProperty(value = "文件编号")
+    private String fileNum;
+    @ApiModelProperty(value = "文件题名")
+    private String fileName;
+    @ApiModelProperty(value = "文件页数")
+    private String fileSize;
+    @ApiModelProperty(value = "文件日期")
+    private String fileDate;
+    @ApiModelProperty(value = "创建时间")
+    private String creatTime;
+    @ApiModelProperty(value = "文件路径")
+    private String filePath;
+    @ApiModelProperty(value = "OSS路径")
+    private String ossUrl;
+    @ApiModelProperty(value = "负责人")
+    private String responsible;
+    @ApiModelProperty(value = "是否删除")
+    private Integer isDeleted;
+    @ApiModelProperty(value = "是否移动")
+    private Integer isMove;
+}

+ 35 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/entity/ScanFolder.java

@@ -0,0 +1,35 @@
+package org.springblade.archive.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@TableName("scan_folder")
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ScanFolder {
+    @TableId(
+            value = "id",
+            type = IdType.ASSIGN_ID
+    )
+    @ApiModelProperty(value = "id")
+    private Long id;
+    @ApiModelProperty(value = "父级id")
+    private Long parentId;
+    @ApiModelProperty(value = "项目id")
+    private Long projectId;
+    @ApiModelProperty(value = "合同id")
+    private Long contractId;
+    @ApiModelProperty(value = "文件夹名称")
+    private String folderName;
+    @ApiModelProperty(value = "文件夹路径")
+    private String folderPath;
+    @ApiModelProperty(value = "是否删除")
+    private Integer isDeleted;
+}

+ 18 - 0
blade-service-api/blade-archive-api/src/main/java/org/springblade/archive/vo/ScanFolderVO.java

@@ -0,0 +1,18 @@
+package org.springblade.archive.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springblade.archive.entity.ScanFolder;
+
+import java.util.List;
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ScanFolderVO extends ScanFolder {
+    @ApiModelProperty(value = "是否有子级")
+    private Boolean hasChildren;
+    @ApiModelProperty(value = "子级节点")
+    private List<ScanFolderVO> childs;
+}

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

@@ -178,4 +178,7 @@ public interface ArchiveFileClient {
 
     @PostMapping(API_PREFIX + "/selectMaxSortByContractId")
     Integer selectMaxSortByContractId(@RequestParam Long contractId);
+
+    @PostMapping(API_PREFIX + "/saveBatchArchiveFile")
+    void saveBatchArchiveFile(@RequestBody List<ArchiveFile> list);
 }

+ 12 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ArchiveTreeContract.java

@@ -406,6 +406,18 @@ public class ArchiveTreeContract extends BaseEntity {
         this.associationType = 1;
         this.setExtType(1);
 
+
+        if (archiveTree.getExtNodeType() != null) {
+            Integer externalExtNodeType = archiveTree.getExtNodeType();
+            if (externalExtNodeType == 1) {
+                this.extNodeType = 1; // 如果为1,则this同名属性也取1
+            } else if (externalExtNodeType == 2) {
+                this.extNodeType = 2; // 如果为2,则this同名属性取2
+            } else if (externalExtNodeType == 3) {
+                this.extNodeType = 4; // 如果为3,则同名属性取值为4
+            }
+        }
+
 //        this.majorDataType = archiveTree.getMajorDataType();
 //        this.displayHierarchy = archiveTree.getDisplayHierarchy();
         this.isStorageNode = archiveTree.getIsStorage();

+ 3 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ContractInfo.java

@@ -181,6 +181,9 @@ public class ContractInfo extends BaseEntity {
     @ApiModelProperty(value = "卷盒规格")
     private String specification;
 
+    @ApiModelProperty(value = "1正在扫描中 ,2没有扫描")
+    private Integer isScan;
+
     /**
      * 排序
      */

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

@@ -31,4 +31,6 @@ import lombok.EqualsAndHashCode;
 public class NodeBaseInfoVO extends NodeBaseInfo {
 	private static final long serialVersionUID = 1L;
 
+	private Integer isDisable;
+
 }

+ 6 - 0
blade-service-api/blade-meter-api/src/main/java/org/springblade/meter/feign/InterimPayCertificateItemClient.java

@@ -20,4 +20,10 @@ public interface InterimPayCertificateItemClient {
 
     @PostMapping("/updateInterimPayCertificateItem")
     void updateInterimPayCertificateItem(@RequestBody List<InterimPayCertificateItem> list4);
+
+    @PostMapping("/addInterimPayCertificateItem1")
+    void addInterimPayCertificateItem1(@RequestParam Long reportId, @RequestBody MonthlyReportVo vo);
+
+    @PostMapping("/updateInterimPayCertificateItem1")
+    void updateInterimPayCertificateItem1(@RequestBody InterimPayCertificateItem item);
 }

+ 49 - 20
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchivesAutoController.java

@@ -592,7 +592,33 @@ public class ArchivesAutoController extends BladeController {
 	 * @return
 	 */
 	@PostMapping("/archiveAutoMethod")
-	public R archiveAutoMethod(Long projectId,Long contractId,Long nodeId) {
+	public R archiveAutoMethod(Long projectId, Long contractId, Long nodeId,
+							   @RequestParam(required = false) String childNodeIds) {
+		List<Long> childNodes = null;
+		if (StringUtils.isNotBlank(childNodeIds)) {
+			childNodes = Arrays.stream(childNodeIds.split(","))
+					.map(String::trim)
+					.filter(s -> !s.isEmpty())
+					.map(Long::valueOf)
+					.collect(Collectors.toList());
+		}
+		return archiveAutoMethod(projectId, contractId, nodeId, childNodes);
+	}
+
+	/**
+	 * 自动组卷入口(带childNodes参数)
+	 * projectId 为当前项目
+	 * contractId 为客户端当前选定的合同段
+	 * nodeId 为左侧树选定的节点id
+	 * childNodes 为指定的子节点列表,用于过滤
+	 * @param projectId
+	 * @param contractId
+	 * @param nodeId
+	 * @param childNodes
+	 * @return
+	 */
+	@PostMapping("/archiveAutoMethodWithChildNodes")
+	public R archiveAutoMethod(Long projectId,Long contractId,Long nodeId, @RequestParam(required = false) List<Long> childNodes) {
 		try{
 			//先验证当前项目是否在自动组卷中,组卷中直接返回
 			//ProjectInfo projectInfo = projectClient.getById(String.valueOf(projectId));
@@ -610,7 +636,7 @@ public class ArchivesAutoController extends BladeController {
 			//设置自动组卷中
 			contractClient.updateIsArchivesAutoById(contractId,1);
 
-			archivesAutoService.archiveAutoMethodThread(projectId,contractId,nodeId,traceId);
+			archivesAutoService.archiveAutoMethodThread(projectId,contractId,nodeId,traceId, childNodes);
 			return R.data("开始自动组卷中,请耐心等待");
 		}catch (Exception e){
 			e.printStackTrace();
@@ -625,21 +651,15 @@ public class ArchivesAutoController extends BladeController {
 	@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("请选择多个案卷进行合并");
-//		}
-		//查出所有案卷文件
-		List<ArchiveFile>archiveFileList=new ArrayList<>();
-		List<Long> longList = Func.toLongList(ids);
-		for (Long id : longList) {
-			archiveFileList.addAll(archiveFileClient.getArchiveFileByArchiveIds(id+""));
-		}
-		//根据档号后缀排序 拿到第一个
-		ArchivesAuto auto = archivesAutoList.get(0);
-		if(longList.size()>1){
+	public R reCreateArchiveAuto(@ApiParam(value = "主键集合", required = true) @RequestParam String ids,@ApiParam(value = "合并后的文件题目", required = true)String name,Integer type){
+		if(type==1){
+			//先查出勾选的案卷
+			List<ArchivesAuto> archivesAutoList=archivesAutoService.listByIds(Func.toLongList(ids));
+			if(archivesAutoList.size()<=1){
+				return R.fail("请选择多个案卷进行合并");
+			}
+			//根据档号后缀排序 拿到第一个
+			ArchivesAuto auto = archivesAutoList.get(0);
 			archivesAutoList.sort(Comparator.comparingInt(a -> {
 				String fileNumber = a.getFileNumber();
 				if (fileNumber == null || fileNumber.isEmpty()) {
@@ -664,6 +684,12 @@ public class ArchivesAutoController extends BladeController {
 			}));
 			//将除第一个以外的案卷文件archiveId 设置成第一个的id
 			List<ArchiveFile>updateArchiveFileList=new ArrayList<>();
+			//查出所有案卷文件
+			List<ArchiveFile>archiveFileList=new ArrayList<>();
+			List<Long> longList = Func.toLongList(ids);
+			for (Long id : longList) {
+				archiveFileList.addAll(archiveFileClient.getArchiveFileByArchiveIds(id+""));
+			}
 			int i=1;
 			for (ArchiveFile file : archiveFileList) {
 				if (!file.getArchiveId().equals(auto.getId())) {
@@ -677,10 +703,13 @@ public class ArchivesAutoController extends BladeController {
 			//删除其他案卷
 			archivesAutoList.remove(auto);
 			archivesAutoService.deleteLogic(archivesAutoList.stream().map(o->o.getId()).collect(Collectors.toList()));
+			//设置案卷页码和四要素
+			archivesAutoService.reCreateArchiveAuto(auto, archiveFileList);
+			return R.status(true);
+		}else {
+			archivesAutoService.reCreateArchiveAuto1(ids);
+			return R.success("正在重组,请稍后查看");
 		}
-		//设置案卷页码和四要素
-		archivesAutoService.reCreateArchiveAuto(auto, archiveFileList);
-		return R.status(true);
 	}
 
 	@PostMapping("/creatFileNameFormAI")

+ 178 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ScanFileController.java

@@ -0,0 +1,178 @@
+package org.springblade.archive.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springblade.archive.dto.ScanFileMoveDTO;
+import org.springblade.archive.entity.ScanFile;
+import org.springblade.archive.entity.ScanFolder;
+import org.springblade.archive.service.ScanFileService;
+import org.springblade.archive.service.ScanFolderService;
+import org.springblade.archive.vo.ScanFolderVO;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.tool.api.R;
+import org.springblade.manager.entity.ContractInfo;
+import org.springblade.meter.vo.InterimPayCertificateVO;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+@RestController
+@AllArgsConstructor
+@RequestMapping("/scanFile")
+@Api(value = "扫描文件接口", tags = "扫描文件接口")
+public class ScanFileController {
+    private final ScanFileService scanFileService;
+    private final JdbcTemplate jdbcTemplate;
+
+    // 线程池
+    @Resource(name = "taskExecutor1")
+    private ThreadPoolExecutor executor;
+
+
+    @GetMapping("/startOrEndScan")
+    @ApiOperation("开始或结束扫描")
+    @ApiImplicitParams({@ApiImplicitParam(name = "contractId", value = "合同ID"),@ApiImplicitParam(name = "type", value = "1:开始扫描 0:结束扫描")})
+    public R startOrEndScan(Long contractId,Integer type){
+        String sql="update m_contract_info set is_scan="+type+" where id="+contractId;
+        jdbcTemplate.update(sql);
+        return R.success("操作成功"+ (type==1?",正在扫描中":",已结束扫描"));
+    }
+
+    @Scheduled(fixedDelay = 120000)
+    public void scan (){
+        String sql = "select * from m_contract_info where is_scan=1 and is_deleted=0";
+        List<ContractInfo> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(ContractInfo.class));
+        if (!list.isEmpty()) {
+            // 创建线程池
+            int threadCount = Math.min(list.size(), Runtime.getRuntime().availableProcessors() * 2);
+            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
+            try {
+                // 存储Future对象以获取执行结果
+                List<Future<?>> futures = new ArrayList<>();
+                // 提交任务到线程池
+                for (ContractInfo contractInfo : list) {
+                    Future<?> future = executorService.submit(() -> {
+                        try {
+                            scanFileService.scanAndSave(contractInfo.getId(), Long.parseLong(contractInfo.getPId()));
+                            System.out.println("合同ID " + contractInfo.getId() + " 扫描完成");
+                        } catch (Exception e) {
+                            System.err.println("处理合同ID " + contractInfo.getId() + " 时出错: " + e.getMessage());
+                            throw e; // 重新抛出异常以便Future能捕获
+                        }
+                    });
+                    futures.add(future);
+                }
+                // 关闭线程池
+                executorService.shutdown();
+                // 等待所有任务完成并检查结果
+                for (int i = 0; i < futures.size(); i++) {
+                    try {
+                        futures.get(i).get(); // 这会阻塞直到任务完成
+                        System.out.println("任务 " + i + " 已成功完成");
+                    } catch (Exception e) {
+                        System.err.println("任务 " + i + " 执行失败: " + e.getMessage());
+                    }
+                }
+                // 等待线程池完全终止
+                if (executorService.awaitTermination(30, TimeUnit.MINUTES)) {
+                    System.out.println("所有扫描任务已完成");
+                } else {
+                    System.err.println("部分扫描任务超时未完成");
+                    executorService.shutdownNow();
+                }
+
+            } catch (InterruptedException e) {
+                System.err.println("线程执行被中断: " + e.getMessage());
+                executorService.shutdownNow();
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    @GetMapping("/getScanFolder")
+    @ApiOperation("获取扫描文件夹")
+    @ApiImplicitParams({@ApiImplicitParam(name = "contractId", value = "合同ID"),@ApiImplicitParam(name = "projectId", value = "项目ID")})
+    public R<List<ScanFolderVO>> getScanFolder(Long contractId, Long projectId){
+        List<ScanFolderVO> list = scanFileService.getScanFolder(contractId,projectId);
+        return R.data( list);
+    }
+
+    @GetMapping("/getScanFile")
+    @ApiOperation("获取扫描文件")
+    @ApiImplicitParams({@ApiImplicitParam(name = "contractId", value = "合同ID"),@ApiImplicitParam(name = "projectId", value = "项目ID"),@ApiImplicitParam(name = "folderId", value = "文件夹ID"),@ApiImplicitParam(name = "query", value = "查询参数")})
+    public R<IPage<ScanFile>> getScanFile(Long contractId, Long projectId, Long folderId, Query query,Integer move){
+        IPage<ScanFile> page=scanFileService.getScanFile(contractId,projectId,folderId,query,move);
+        return R.data(page);
+    }
+    @GetMapping("/getDetil")
+    @ApiOperation("获取扫描文件详情")
+    @ApiImplicitParam(name = "id", value = "文件ID")
+    public R<ScanFile> getDetil(Long id){
+        return R.data(scanFileService.getById(id));
+    }
+    @PostMapping("/updateScanFile")
+    @ApiOperation("更新扫描文件")
+    @ApiImplicitParam(name = "scanFiles", value = "扫描文件")
+    public R updateScanFile(@RequestBody List<ScanFile> scanFiles){
+        return R.data(scanFileService.updateBatchById(scanFiles));
+    }
+
+    @GetMapping("/deleteScanFile")
+    @ApiOperation("删除扫描文件")
+    @ApiImplicitParam(name = "fileNames", value = "文件名称")
+    public R deleteScanFile(String ids){
+        return R.success(scanFileService.deleteScanFile(ids));
+    }
+
+    @GetMapping("/getContractStatus")
+    @ApiOperation("获取合同状态")
+    @ApiImplicitParam(name = "contractId", value = "合同ID")
+    public R<Integer> getContractStatus(Long contractId){
+        if (contractId == null) {
+            return R.data(2);
+        }
+        String sql = "SELECT is_scan FROM m_contract_info WHERE id = ?";
+        try {
+            Integer result = jdbcTemplate.queryForObject(sql, Integer.class, contractId);
+            return R.data(result != null ? result : 2);
+        } catch (Exception e) {
+            // 记录异常日志(可选)
+            System.err.println("查询合同扫描状态出错: " + e.getMessage());
+            return R.data(2);
+        }
+    }
+
+    @PostMapping("/autoRecognize")
+    @ApiOperation("自动识别")
+    @ApiImplicitParam(name = "ids", value = "文件IDs")
+    public R autoRecognize(String ids){
+       return R.status(scanFileService.autoRecognize(ids));
+    }
+
+    @PostMapping("/moveScanFile")
+    @ApiOperation("移动文件")
+    @ApiImplicitParams({@ApiImplicitParam(name = "ids", value = "文件IDs"),@ApiImplicitParam(name = "nodeId", value = "目标文件夹ID") })
+    public R moveScanFile(@RequestBody ScanFileMoveDTO dto){
+        return R.status(scanFileService.moveScanFile(dto.getIds(),dto.getNodeId()));
+    }
+
+
+
+}

+ 48 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/external/impl/ExternalDataArchiveTreeService.java

@@ -220,6 +220,7 @@ public class ExternalDataArchiveTreeService {
         }
     }
 
+
     private void getAddAndUpdateContracts(
             ExternalDataInfo externalDataInfo,
             List<ArchiveTreeContract> externalContracts,
@@ -227,6 +228,53 @@ public class ExternalDataArchiveTreeService {
             List<ArchiveTreeContract> addContracts,
             List<ArchiveTreeContract> upContracts) {
 
+        // 1. 创建本地合同的 Map:key 为 outId,value 为合同对象
+        Map<String, ArchiveTreeContract> localContractMap = new HashMap<>();
+        for (ArchiveTreeContract localContract : localContracts) {
+            String outId = localContract.getOutId();
+            if (outId != null && !outId.isEmpty()) {
+                localContractMap.put(outId, localContract);
+            }
+        }
+
+        // 2. 遍历外部合同,判断新增或更新
+        for (ArchiveTreeContract externalContract : externalContracts) {
+            String externalOutId = externalContract.getOutId();
+
+            // 处理空 outId(根据业务需求决定是否跳过或新增)
+            if (externalOutId == null || externalOutId.isEmpty()) {
+                // addContracts.add(externalContract); // 如果需要处理空 outId 则取消注释
+                continue;
+            }
+
+            // 通过 Map 查找对应的本地合同
+            ArchiveTreeContract localContract = localContractMap.get(externalOutId);
+
+            if (localContract == null) {
+                // 本地不存在该合同,需要新增
+                addContracts.add(externalContract);
+            } else {
+                // 新增的判断逻辑:如果 localContract 的 extNodeType 为 null 或 -1,
+                // 且 externalContract 的 extNodeType 不为 null,则添加到更新列表
+                Integer localExtNodeType = localContract.getExtNodeType();
+                Integer externalExtNodeType = externalContract.getExtNodeType();
+
+                if ((localExtNodeType == null || localExtNodeType == -1)
+                        && externalExtNodeType != null) {
+                    localContract.setExtNodeType(externalExtNodeType);
+                    upContracts.add(localContract);
+                }
+                // 这里可以添加其他更新条件的判断,例如时间戳比较
+            }
+        }
+    }
+    private void getAddAndUpdateContracts1(
+            ExternalDataInfo externalDataInfo,
+            List<ArchiveTreeContract> externalContracts,
+            List<ArchiveTreeContract> localContracts,
+            List<ArchiveTreeContract> addContracts,
+            List<ArchiveTreeContract> upContracts) {
+
         // 1. 提取本地合同的 outId 集合(用于快速判断外部合同是否已存在)
         Set<String> localOutIdSet = localContracts.stream()
                 .map(ArchiveTreeContract::getOutId)  // 直接使用本地合同的 outId 字段

+ 2 - 1
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml

@@ -153,6 +153,7 @@
         <result column="archive_name_suffix" property="archiveNameSuffix"/>
         <result column="ext_key_id" property="extKeyId"/>
         <result column="out_id" property="outId"/>
+        <result column="ext_node_type" property="extNodeType"/>
     </resultMap>
 
     <!-- 通用查询映射结果 -->
@@ -1566,7 +1567,7 @@
         where is_deleted = 0
     </select>
     <select id="getOutNodesByOutIds" resultMap="archiveTreeContractResultMap">
-        select id,node_name,parent_id,out_id
+        select id,node_name,parent_id,out_id,ext_node_type
         from m_archive_tree_contract
         where project_id = #{projectId} and is_deleted = 0 and
         out_id in

+ 26 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFileMapper.java

@@ -0,0 +1,26 @@
+package org.springblade.archive.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import feign.Param;
+import org.springblade.archive.entity.ScanFile;
+import org.springblade.archive.entity.ScanFolder;
+
+import java.util.List;
+
+public interface ScanFileMapper extends BaseMapper<ScanFile> {
+
+    Integer exists(@Param("projectId") Long projectId, @Param("contractId") Long contractId, @Param("fileName") String fileName);
+
+    int insert(ScanFile scanFile);
+
+    Integer selectMaxDigitalNum(@Param("contractId") Long contractId, @Param("projectId") Long projectId);
+
+    Integer selectMaxSort(@Param("contractId") Long contractId, @Param("projectId") Long projectId, @Param("folderId") Long folderId);
+
+    List<ScanFolder> getScanFolder(@Param("contractId") Long contractId, @Param("projectId") Long projectId);
+
+    IPage<ScanFile> getScanFile(IPage<ScanFile> page, @Param("contractId") Long contractId, @Param("projectId") Long projectId, @Param("folderId") Long folderId,@Param("move")Integer  move);
+
+    void removeScan(@Param("longList") List<Long> longList);
+}

+ 30 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFileMapper.xml

@@ -0,0 +1,30 @@
+<?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.ScanFileMapper">
+    <update id="removeScan">
+        update scan_file set is_deleted = 1 where project_id = #{projectId} and contract_id = #{contractId}
+        and id in
+        <foreach collection="longList" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+    <select id="exists" resultType="java.lang.Integer">
+        select COUNT(*) FROM scan_file WHERE project_id = #{projectId} AND contract_id = #{contractId}  AND file_name = #{fileName}
+    </select>
+    <select id="selectMaxDigitalNum" resultType="java.lang.Integer">
+        select MAX(digital_num) FROM scan_file WHERE project_id = #{projectId} AND contract_id = #{contractId}
+    </select>
+    <select id="selectMaxSort" resultType="java.lang.Integer">
+        select MAX(sort) FROM scan_file WHERE project_id = #{projectId} AND contract_id = #{contractId} And folder_id = #{folderId}
+    </select>
+    <select id="getScanFolder" resultType="org.springblade.archive.entity.ScanFolder">
+        select * from scan_folder where project_id = #{projectId} and contract_id = #{contractId} and is_deleted = 0
+    </select>
+    <select id="getScanFile" resultType="org.springblade.archive.entity.ScanFile">
+        select * from scan_file where project_id = #{projectId} and contract_id = #{contractId} and folder_id = #{folderId} and is_deleted = 0
+        <if test="move!= null">
+            and is_move = #{move}
+        </if>
+        order by digital_num
+    </select>
+</mapper>

+ 15 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFolderMapper.java

@@ -0,0 +1,15 @@
+package org.springblade.archive.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+import org.springblade.archive.entity.ScanFolder;
+
+public interface ScanFolderMapper extends BaseMapper<ScanFolder> {
+
+    int exists(@Param("projectId") Long projectId, @Param("contractId") Long contractId, @Param("folderName") String folderName);
+
+    int insert(ScanFolder scanFolder);
+
+
+    Long getId(@Param("folderName") String folderName, @Param("contractId") Long contractId, @Param("projectId") Long projectId);
+}

+ 12 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ScanFolderMapper.xml

@@ -0,0 +1,12 @@
+<?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.ScanFolderMapper">
+
+
+    <select id="exists" resultType="java.lang.Integer">
+        select COUNT(*) FROM scan_folder WHERE project_id = #{projectId} AND contract_id = #{contractId} AND folder_name = #{folderName}
+    </select>
+    <select id="getId" resultType="java.lang.Long">
+        select id from scan_folder where project_id = #{projectId} AND contract_id = #{contractId} AND folder_name = #{folderName}
+    </select>
+</mapper>

+ 10 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchivesAutoService.java

@@ -76,8 +76,14 @@ public interface IArchivesAutoService extends BaseService<ArchivesAuto> {
 
 	void archiveAutoMethod(Long project,Long contractId,Long nodeId,Long traceId);
 
+	void archiveAutoMethod(Long project,Long contractId,Long nodeId,Long traceId,List<Long> childNodes);
+
+	void archiveAutoMethodThread(Long projectId, Long contractId, Long nodeId, Long traceId, List<Long> childNodes);
+
 	void splitArchvies(Long project,Long contractId,Long nodeId);
 
+	void splitArchvies(Long project,Long contractId,Long nodeId,List<Long> childNodes);
+
 	public String getMergeArchivesFile(Long archiveId);
 
 	//拆卷
@@ -90,6 +96,8 @@ public interface IArchivesAutoService extends BaseService<ArchivesAuto> {
 	//刷新某个项目的档号
 	void refreshFileNumberNoSlipt(Long projectId,Long contractId,Long nodeId,boolean bforce, Long traceId);
 
+	void refreshFileNumberNoSlipt(Long projectId,Long contractId,Long nodeId,boolean bforce, Long traceId,List<Long> childNodes);
+
 	void test666();
 
     List<DictBiz> getCarrierTypeByDict();
@@ -177,4 +185,6 @@ public interface IArchivesAutoService extends BaseService<ArchivesAuto> {
 	List<Long> getArchiveIdsByNodes(List<Long> ids);
 
 	void reomoveArchiveAndFile(List<Long> archiveIds);
+
+    void reCreateArchiveAuto1(String ids);
 }

+ 31 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/ScanFileService.java

@@ -0,0 +1,31 @@
+package org.springblade.archive.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springblade.archive.entity.ScanFile;
+import org.springblade.archive.vo.ScanFolderVO;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.tool.api.R;
+
+import java.util.List;
+
+
+public interface ScanFileService extends IService<ScanFile> {
+    R scanAndSaveFolder(Long contractId, Long projectId);
+
+    R scanAndSaveFiles(Long contractId, Long projectId);
+
+    void sortNumber(Long contractId, Long projectId);
+
+    void scanAndSave(Long contractId,Long projectId);
+
+    List<ScanFolderVO> getScanFolder(Long contractId, Long projectId);
+
+    IPage<ScanFile> getScanFile(Long contractId, Long projectId, Long folderId, Query query,Integer move);
+
+    String deleteScanFile(String ids);
+
+    Boolean autoRecognize(String ids);
+
+    boolean moveScanFile(List<Long> ids, Long nodeId);
+}

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

@@ -0,0 +1,5 @@
+package org.springblade.archive.service;
+
+public interface ScanFolderService {
+
+}

+ 9 - 2
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveNameServiceImpl.java

@@ -283,7 +283,11 @@ public class ArchiveNameServiceImpl implements IArchiveNameService {
                                 }
                             }
 
-                            if (allGroupsFullyCovered && useCovering) {
+                            // 检查祖父节点是否为单位工程,如果是则跳过此处理
+                            boolean isUnitProject = grandParentNode.getExtNodeType() != null && 
+                                                  grandParentNode.getExtNodeType() == 1;
+                            
+                            if (allGroupsFullyCovered && useCovering && !isUnitProject) {
                                 nameParts.add(getLevelName(hierarchy, grandParentId));
                                 canUseGrandParentName = true;
                             }
@@ -316,8 +320,11 @@ public class ArchiveNameServiceImpl implements IArchiveNameService {
                 List<ArchiveTreeContract> allChildren = hierarchy.getChildren(groupId);
                 int childrenCount = (allChildren != null) ? allChildren.size() : 0;
 
+                boolean isUnitProject = parent.getExtNodeType() != null &&
+                        parent.getExtNodeType() == 1;
+
                 // 检查是否全覆盖所有子节点
-                if (childrenCount > 0 && groupNodes.size() == childrenCount ) {
+                if (childrenCount > 0 && groupNodes.size() == childrenCount && !isUnitProject) {
                     // 只有组内第一个父节点使用完整层级名称
                     String parentName = firstParentInGroup ?
                             getLevelName(hierarchy, groupId) :

+ 176 - 6
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -771,7 +771,69 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	@Override
 	public void splitArchvies(Long projectId, Long contractId, Long nodeId) {
+		splitArchvies(projectId, contractId, nodeId, null);
+	}
+
+	public void splitArchvies(Long projectId, Long contractId, Long nodeId, List<Long> childNodes) {
 		List<String> removeFiles = new ArrayList<>();
+		
+		// 如果 childNodes 不为空,使用 childNodes 进行拆卷
+		if (childNodes != null && !childNodes.isEmpty()) {
+			log.info("[自动组卷-拆卷]{}", "对指定childNodes进行拆卷,childNodes: " + childNodes);
+			
+			// 从 childNodes 分别获取节点信息并合并处理
+			for (Long childNodeId : childNodes) {
+				ArchiveTreeContract childNode = archiveTreeContractClient.getArchiveTreeContractById(childNodeId);
+				if (childNode != null) {
+					// 如果指定了 contractId,检查节点是否属于该合同段
+					if (contractId != null && !contractId.equals(childNode.getContractId())) {
+						continue; // 跳过不属于指定合同段的节点
+					}
+					
+					String ancestors = childNode.getAncestors() + "," + childNodeId;
+					String strChildNodeId = childNodeId.toString();
+					
+					// 清除案卷封面等pdf
+					List<String> archivesOutUrlList = baseMapper.getArchivesOutUrlListByNode(projectId, ancestors, strChildNodeId);
+					if (archivesOutUrlList != null && archivesOutUrlList.size() > 0) {
+						for (String outUrl : archivesOutUrlList) {
+							String[] splits = outUrl.split(",");
+							for (String url : splits) {
+								if (StringUtils.isNotEmpty(url) && StringUtils.isNotEmpty(url.trim())) {
+									removeFiles.add(FileUtils.getAliYunSubUrl(url));
+								}
+							}
+						}
+						// 删除四要素文件记录
+						baseMapper.deleteIsElementFileByNode(projectId, ancestors, strChildNodeId);
+					}
+					
+					// 清除文件页面pdf
+					List<String> filePageUrlList = baseMapper.getFilePageUrlListByNode(projectId, ancestors, strChildNodeId);
+					if (filePageUrlList != null && filePageUrlList.size() > 0) {
+						for (String url : filePageUrlList) {
+							if (StringUtils.isNotEmpty(url) && StringUtils.isNotEmpty(url.trim())) {
+								removeFiles.add(FileUtils.getAliYunSubUrl(url));
+							}
+						}
+					}
+					
+					// 执行拆卷操作
+					baseMapper.splitArchviesByNode(projectId, ancestors, strChildNodeId);
+				}
+			}
+			
+			// 异步删除文件
+			if (!removeFiles.isEmpty()) {
+				executorService.execute(() -> {
+					log.info("[自动组卷-拆卷]{}", "删除旧封面,页码文件开始" + removeFiles);
+					iossClient.removeFiles(removeFiles);
+					log.info("[自动组卷-拆卷]{}", "删除旧封面,页码文件结束" + removeFiles);
+				});
+			}
+			return;
+		}
+		
 		//没合同段的默认整个项目
 		if (contractId == null) {
 			log.info("[自动组卷-拆卷]{}", "对全项目未锁定案卷拆卷");
@@ -901,6 +963,10 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	@Override
 	public void archiveAutoMethod(Long projectId, Long contractId, Long nodeId, Long traceId) {
+		archiveAutoMethod(projectId, contractId, nodeId, traceId, null);
+	}
+
+	public void archiveAutoMethod(Long projectId, Long contractId, Long nodeId, Long traceId, List<Long> childNodes) {
 		//步骤一:把档号集合初始化
 		indexMap = new HashMap<>();
 		//步list = {ArrayList@18238}  size = 19骤二:查询归档树节点。存在未归档文件的节点。
@@ -919,6 +985,40 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		//按合同段过滤
 		List<ArchiveTreeContract> authlist = authFilter(list, contractId, nodeId);
 
+		// 新增:处理 childNodes 参数
+		if (childNodes != null && !childNodes.isEmpty()) {
+			// 创建合并列表(包含原始 authlist)
+			List<ArchiveTreeContract> mergedAuthlist = new ArrayList<>();
+
+			// 遍历每个子节点ID,应用过滤并合并结果
+			for (Long childNodeId : childNodes) {
+				List<ArchiveTreeContract> childAuthlist = authFilter(list, contractId, childNodeId);
+				mergedAuthlist.addAll(childAuthlist);
+			}
+
+			// 去重处理(根据节点ID)
+			authlist = mergedAuthlist;
+		}
+		
+		// 如果 childNodes 不为空,进行祖先节点过滤
+//		if (childNodes != null && !childNodes.isEmpty()) {
+//			authlist = authlist.stream()
+//				.filter(node -> {
+//					// 检查节点的祖先是否包含 childNodes 中的任一元素
+//					if (StringUtils.isNotEmpty(node.getAncestors())) {
+//						String[] ancestors = node.getAncestors().split(",");
+//						for (String ancestor : ancestors) {
+//							if (StringUtils.isNotEmpty(ancestor.trim()) && childNodes.contains(Long.valueOf(ancestor.trim()))) {
+//								return true;
+//							}
+//						}
+//					}
+//					// 检查节点本身是否在 childNodes 中
+//					return childNodes.contains(node.getId());
+//				})
+//				.collect(Collectors.toList());
+//		}
+
 		//步骤三:遍历归档树节点整理出 默认规则节点,分类并卷节点,单独组卷节点 三个集合。
 		List<ArchiveTreeContract> list0 = new ArrayList<>();
 		List<ArchiveTreeContract> list1 = new ArrayList<>();
@@ -954,7 +1054,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		archiveAutoMethod2_new(list2, listTop, projectId, boxMap, boxFileMap, traceId);//分类组卷
 		//设置完成度50%
 		projectClient.updateIsArchivesAutoById(projectId, 50);
-		archiveAutoMethod1(list1, boxMap, boxFileMap, traceId);//默认组卷
+		archiveAutoMethod3(list1, boxMap, boxFileMap, traceId);//默认组卷
 
 		//分盒组卷的且节点没配置组卷类型的
 		archiveAutoMethod0(list0, boxMap, boxFileMap);//默认组卷
@@ -963,6 +1063,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		archiveAutoMethodBox(boxMap, traceId);//分盒组卷
 	}
 
+
+
 	private void addBoxMap(ArchiveFile file, Map<String, List<ArchiveFile>> boxMap, Map<Long, String> boxFileMap) {
 		String boxName = file.getBoxName();
 		if (boxFileMap.containsKey(file.getId())) {
@@ -2618,6 +2720,10 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	 * @param nodeId
 	 */
 	public void refreshFileNumberNoSlipt(Long projectId, Long contractId, Long nodeId, boolean bforce, Long traceId) {
+		refreshFileNumberNoSlipt(projectId, contractId, nodeId, bforce, traceId, null);
+	}
+
+	public void refreshFileNumberNoSlipt(Long projectId, Long contractId, Long nodeId, boolean bforce, Long traceId, List<Long> childNodes) {
 		List<ArchiveTreeContract> list = archiveTreeContractClient.getListByProjectId(projectId);
 		if (nodeId != null) {
 			ArchiveTreeContract node = archiveTreeContractClient.getArchiveTreeContractById(nodeId);
@@ -2629,7 +2735,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		Integer indexType = 0;
 		ArchiveProjectConfig config = archiveProjectConfigService.getByProjectIdOrNew(projectId);
 
-		this.refreshFileNumberNoSlipt(list, contractId, nodeId, bforce, config.getIndexType(), config.getDirType(), config.getIndexNum(), traceId);
+		this.refreshFileNumberNoSlipt(list, contractId, nodeId, bforce, config.getIndexType(), config.getDirType(), config.getIndexNum(), traceId, childNodes);
 	}
 
 	/**
@@ -2639,6 +2745,11 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	 */
 	public void refreshFileNumberNoSlipt(List<ArchiveTreeContract> archiveTreeContracts, Long contractId, Long nodeId,
 										 boolean bforce, Integer indexType, Integer dirType, Integer indexNum, Long traceId) {
+		refreshFileNumberNoSlipt(archiveTreeContracts, contractId, nodeId, bforce, indexType, dirType, indexNum, traceId, null);
+	}
+
+	public void refreshFileNumberNoSlipt(List<ArchiveTreeContract> archiveTreeContracts, Long contractId, Long nodeId,
+										 boolean bforce, Integer indexType, Integer dirType, Integer indexNum, Long traceId, List<Long> childNodes) {
 
 		List<ArchiveTreeContractVO2> subTreeList = new ArrayList<>();
 		List<List<ArchiveTreeContract>> subGroupedList = new ArrayList<>();
@@ -2659,6 +2770,26 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 			//根据 authFilter函数过滤
 			subList = authFilterNum(subList, contractId, nodeId);
+			
+			// 如果 childNodes 不为空,进行祖先节点过滤
+			if (childNodes != null && !childNodes.isEmpty()) {
+				subList = subList.stream()
+					.filter(node -> {
+						// 检查节点的祖先是否包含 childNodes 中的任一元素
+						if (StringUtils.isNotEmpty(node.getAncestors())) {
+							String[] ancestors = node.getAncestors().split(",");
+							for (String ancestor : ancestors) {
+								if (StringUtils.isNotEmpty(ancestor.trim()) && childNodes.contains(Long.valueOf(ancestor.trim()))) {
+									return true;
+								}
+							}
+						}
+						// 检查节点本身是否在 childNodes 中
+						return childNodes.contains(node.getId());
+					})
+					.collect(Collectors.toList());
+			}
+			
 			List<Long> ids = subList.stream()
 					.map(ArchiveTreeContract::getId)
 					.collect(Collectors.toList());
@@ -3284,10 +3415,17 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	@Override
 	public void archiveAutoMethodThread(Long projectId, Long contractId, Long nodeId, Long traceId) {
+		archiveAutoMethodThread(projectId, contractId, nodeId, traceId, null);
+	}
+
+	public void archiveAutoMethodThread(Long projectId, Long contractId, Long nodeId, Long traceId, List<Long> childNodes) {
 		executorService.execute(() -> {
 			try {
 				// 将项目未锁定案卷拆卷
 				String startSplitMsg = "[自动组卷] 开始对未锁定案卷拆卷。projectId:" + projectId + "-contractId:" + contractId + "-nodeId:" + nodeId + "-traceId:" + traceId;
+				if (childNodes != null && !childNodes.isEmpty()) {
+					startSplitMsg += "-childNodes:" + childNodes;
+				}
 				//log.info(startSplitMsg);
 				iTraceLogService.saveLog(traceId, startSplitMsg);
 
@@ -3296,31 +3434,49 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 				//     return;
 				// }
 
-				splitArchvies(projectId, contractId, nodeId);
+				splitArchvies(projectId, contractId, nodeId, childNodes);
 				// 设置完成度10%
 				contractClient.updateIsArchivesAutoById(contractId, 10);
 
 				// 项目自动组卷入口
 				String startAutoArchiveMsg = "[自动组卷] 开始自动组卷。projectId:" + projectId + "-contractId:" + contractId + "-nodeId:" + nodeId + "-traceId:" + traceId;
+				if (childNodes != null && !childNodes.isEmpty()) {
+					startAutoArchiveMsg += "-childNodes:" + childNodes;
+				}
 				//log.info(startAutoArchiveMsg);
 				iTraceLogService.saveLog(traceId, startAutoArchiveMsg);
 
 				archiveAutoPdfService.assignArchiveTableUrl();
 
-				archiveAutoMethod(projectId, contractId, nodeId, traceId);
+				if (childNodes != null && !childNodes.isEmpty()) {
+					archiveAutoMethod(projectId, contractId, nodeId, traceId, childNodes);
+				} else {
+					archiveAutoMethod(projectId, contractId, nodeId, traceId);
+				}
 				// 设置完成度80%
 				contractClient.updateIsArchivesAutoById(contractId, 80);
 
 				// 刷新项目档号
 				String startRefreshMsg = "[自动组卷] 开始刷新组卷档号。projectId:" + projectId + "-contractId:" + contractId + "-nodeId:" + nodeId + "-traceId:" + traceId;
+				if (childNodes != null && !childNodes.isEmpty()) {
+					startRefreshMsg += "-childNodes:" + childNodes;
+				}
 				//log.info(startRefreshMsg);
 				iTraceLogService.saveLog(traceId, startRefreshMsg);
 
 				refreshFileNumberNoSlipt(projectId, contractId, nodeId, true, traceId);
+//				if (childNodes != null && !childNodes.isEmpty()) {
+//					refreshFileNumberNoSlipt(projectId, contractId, nodeId, true, traceId, childNodes);
+//				} else {
+//					refreshFileNumberNoSlipt(projectId, contractId, nodeId, true, traceId);
+//				}
 
 				// 设置自动组卷结束
 				contractClient.updateIsArchivesAutoById(contractId, 0);
 				String completeMsg = "[自动组卷] 自动组卷完成。projectId:" + projectId + "-contractId:" + contractId + "-nodeId:" + nodeId + "-traceId:" + traceId;
+				if (childNodes != null && !childNodes.isEmpty()) {
+					completeMsg += "-childNodes:" + childNodes;
+				}
 				//log.info(completeMsg);
 				iTraceLogService.saveLog(traceId, completeMsg);
 
@@ -4678,7 +4834,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		for (ArchivesAutoVO4 v : value) {
 			ArchivesAuto auto = new ArchivesAuto();
 			if (config.getIndexType() == null || config.getIndexType() == 0) {
-				v.setFileNumber(v.getFileNumberPrefix() + "_" + i);
+				v.setFileNumber(v.getFileNumberPrefix() + "—" + i);
+
 			} else {
 				String prefix = v.getFileNumberPrefix();
 				int index = i; // 编号从 startNumber 开始
@@ -4691,7 +4848,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 				// 使用 %0xd 格式化数字,x 表示总长度
 				String formattedIndex = String.format("%0" + numLength + "d", index);
 				// 设置最终档号
-				v.setFileNumber(prefix + "_" + formattedIndex);
+				v.setFileNumber(prefix + "" + formattedIndex);
 			}
 			auto.setId(v.getId());
 			auto.setFileNumber(v.getFileNumber());
@@ -5001,6 +5158,19 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		autoMapper.removeFilesByArchiveIds(archiveIds);
 	}
 
+	@Override
+	@Async
+	public void reCreateArchiveAuto1(String ids) {
+		for (Long id : Func.toLongList(ids)) {
+			//先查出勾选的案卷
+			ArchivesAuto archivesAuto=this.getById(id);
+			//查出所有案卷文件
+			List<ArchiveFile>archiveFileList=archiveFileClient.getArchiveFileByArchiveIds(id+"");
+			//设置案卷页码和四要素
+			this.reCreateArchiveAuto(archivesAuto, archiveFileList);
+		}
+	}
+
 	/**
 	 * 分组适配方法:按nodeId分组文件并进行页数合并处理
 	 *

+ 520 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ScanFileServiceImpl.java

@@ -0,0 +1,520 @@
+package org.springblade.archive.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import org.springblade.archive.entity.ArchivesAuto;
+import org.springblade.archive.entity.ScanFile;
+import org.springblade.archive.entity.ScanFolder;
+import org.springblade.archive.mapper.ScanFileMapper;
+import org.springblade.archive.mapper.ScanFolderMapper;
+import org.springblade.archive.service.ScanFileService;
+import org.springblade.archive.utils.FileUtils;
+import org.springblade.archive.vo.ScanFolderVO;
+import org.springblade.business.entity.ArchiveFile;
+import org.springblade.business.feign.ArchiveFileClient;
+import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.oss.model.BladeFile;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.manager.entity.ArchiveTreeContract;
+import org.springblade.manager.feign.ArchiveTreeContractClient;
+import org.springblade.resource.feign.NewIOSSClient;
+import org.springframework.beans.BeanUtils;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+@Service
+@AllArgsConstructor
+public class ScanFileServiceImpl  extends ServiceImpl<ScanFileMapper, ScanFile> implements ScanFileService {
+//    //文件根目录
+//    private static final String ROOT_PREFIX = "D:" + File.separator + "ScanPDF";
+//    //备份文件根目录
+//    private static final String ROOT_PREFIX_back = "D:" + File.separator + "ScanBackPDF";
+    //文件根目录
+    private static final String ROOT_PREFIX = "/mnt/sdc/ScanPDF";
+    //备份文件根目录
+    private static final String ROOT_PREFIX_back = "/mnt/sdc/ScanBackPDF";
+
+    private final ScanFolderMapper scanFolderMapper;
+    private final ScanFileMapper scanFileMapper;
+    private final NewIOSSClient newIOSSClient;
+    private final ArchiveTreeContractClient archiveTreeContractClient;
+    private final JdbcTemplate jdbcTemplate;
+    private final ArchiveFileClient archiveFileClient;
+
+
+
+
+    @Override
+    public void scanAndSave(Long contractId, Long projectId) {
+        //扫描文件夹
+        scanAndSaveFolder(contractId, projectId);
+        //扫描文件
+        scanAndSaveFiles(contractId,projectId);
+        //整理编号,序号
+        sortNumber(contractId, projectId);
+    }
+
+    @Override
+    public List<ScanFolderVO> getScanFolder(Long contractId, Long projectId) {
+        List<ScanFolder> list = scanFileMapper.getScanFolder(contractId,projectId);
+        // 将ScanFolder转换为ScanFolderVO
+        List<ScanFolderVO> voList = list.stream().map(scanFolder -> {
+            ScanFolderVO vo = new ScanFolderVO();
+            // 复制属性
+            BeanUtils.copyProperties(scanFolder, vo);
+            // 初始化子节点列表
+            vo.setChilds(new ArrayList<>());
+            // 默认为没有子节点,后续会重新判断
+            vo.setHasChildren(false);
+            return vo;
+        }).collect(Collectors.toList());
+
+        // 构建父子关系
+        Map<Long, ScanFolderVO> voMap = voList.stream()
+                .collect(Collectors.toMap(ScanFolderVO::getId, v -> v));
+
+        List<ScanFolderVO> result = new ArrayList<>();
+
+        for (ScanFolderVO vo : voList) {
+            Long parentId = vo.getParentId();
+            if (parentId == null || parentId == 0) {
+                // 没有父节点的作为根节点
+                result.add(vo);
+            } else {
+                // 有父节点的添加到对应父节点的子列表中
+                ScanFolderVO parent = voMap.get(parentId);
+                if (parent != null) {
+                    parent.getChilds().add(vo);
+                    parent.setHasChildren(true); // 标记父节点有子节点
+                }
+            }
+        }
+
+        return result;
+
+    }
+
+    @Override
+    public IPage<ScanFile> getScanFile(Long contractId, Long projectId, Long folderId, Query query,Integer move) {
+        IPage<ScanFile> page = new Page<>(query.getCurrent(), query.getSize());
+        page=baseMapper.getScanFile(page,contractId,projectId,folderId,move);
+        return page;
+    }
+
+    @Override
+    public String deleteScanFile(String ids) {
+        List<Long> longList = Func.toLongList(ids);
+        List<ScanFile> scanFiles = baseMapper.selectList(new LambdaQueryWrapper<>(ScanFile.class).in(ScanFile::getId, longList));
+        List<String> fileNames = scanFiles.stream().filter(o-> !StringUtil.isBlank(o.getFileName())).map(o -> o.getFileName()).collect(Collectors.toList());
+        baseMapper.removeScan(longList);
+        newIOSSClient.removeFiles(fileNames);
+        return "";
+    }
+
+    @Override
+    public Boolean autoRecognize(String ids) {
+        List<Long> longList = Func.toLongList(ids);
+        List<ScanFile> scanFiles = baseMapper.selectList(new LambdaQueryWrapper<>(ScanFile.class).in(ScanFile::getId, longList));
+        return true;
+    }
+
+
+    @Override
+    public boolean moveScanFile(List<Long> ids, Long nodeId) {
+        try {
+            List<ScanFile> scanFiles = baseMapper.selectList(new LambdaQueryWrapper<>(ScanFile.class).in(ScanFile::getId, ids));
+            ArchiveTreeContract contract = archiveTreeContractClient.getArchiveTreeContractById(nodeId);
+            String sql="select IFNULL(max(sort),0) from u_archive_file where project_id="+contract.getProjectId()+" and contract_id="+contract.getContractId()+" and is_deleted=0";
+            Integer sort = jdbcTemplate.queryForObject(sql, Integer.class);
+            List<ArchiveFile>list=new ArrayList<>();
+            if(contract!=null){
+                for (ScanFile file : scanFiles) {
+                    ArchiveFile archiveFile = new ArchiveFile();
+                    archiveFile.setId(SnowFlakeUtil.getId());
+                    archiveFile.setProjectId(contract.getProjectId()+"");
+                    archiveFile.setContractId(contract.getContractId()+"");
+                    archiveFile.setNodeId(nodeId+"");
+                    archiveFile.setFileNumber(file.getFileNum());
+                    archiveFile.setFileName(file.getFileName());
+                    archiveFile.setFileTime(file.getFileDate());
+                    archiveFile.setFileUrl(file.getOssUrl());
+                    archiveFile.setPdfFileUrl(file.getOssUrl());
+                    archiveFile.setFilePage(file.getFileSize()!=null?Integer.parseInt(file.getFileSize()):0);
+                    archiveFile.setIsApproval(0);
+                    archiveFile.setIsNeedCertification(0);
+                    archiveFile.setIsCertification(0);
+                    archiveFile.setDutyUser(file.getResponsible());
+                    archiveFile.setIsArchive(0);
+                    archiveFile.setSourceType(2);
+                    archiveFile.setIsElement(0);
+                    archiveFile.setClassify(contract.getClassify());
+                    archiveFile.setNodeTreeStructure(contract.getTreeStructure());
+                    archiveFile.setSort(sort++);
+                    list.add(archiveFile);
+                }
+                archiveFileClient.saveBatchArchiveFile(list);
+                for (ScanFile file : scanFiles) {
+                    file.setIsMove(1);
+                }
+                this.updateBatchById(scanFiles);
+            }
+            return true;
+        }catch (Exception e){
+            return false;
+        }
+    }
+
+    /**
+     * 入口方法:扫描并入库指定contractId的所有文件夹
+     * @param contractId 传入的合同ID(对应D:\PDF下的文件夹名)
+     * @param projectId 传入的项目ID
+     */
+    public R scanAndSaveFolder(Long contractId, Long projectId) {
+        // 1. 构建合同对应的根文件夹路径(D:\PDF\${contractId})
+        String contractFolderPath = ROOT_PREFIX + File.separator + contractId;
+        System.out.println("contractFolderPath=" + contractFolderPath);
+        File contractFolder = new File(contractFolderPath);
+
+        // 校验根文件夹是否存在且为目录
+        if (!contractFolder.exists() || !contractFolder.isDirectory()) {
+            return R.fail("错误:根文件夹不存在或非法,路径=" + contractFolderPath);
+        }
+
+        // 2. 扫描一级文件夹(parent_id=0)并入库,同时递归子文件夹
+        File[] firstLevelFolders = contractFolder.listFiles(File::isDirectory);
+        if (firstLevelFolders == null || firstLevelFolders.length == 0) {
+            return R.fail("提示:合同ID=" + contractId + "的根文件夹下无任何子文件夹");
+        }
+
+        // 遍历一级文件夹,逐个入库并递归子目录
+        for (File firstLevelFolder : firstLevelFolders) {
+            String folderName = firstLevelFolder.getName();
+            Long folderId = scanFolderMapper.getId(folderName,contractId,projectId);
+            if(folderId==null){
+                folderId=SnowFlakeUtil.getId(); // 生成随机ID
+            }
+            String filePath = firstLevelFolder.getAbsolutePath();
+            // 创建一级目录的ScanFolder对象(parent_id=0)
+            ScanFolder firstLevelScanFolder = new ScanFolder(
+                    folderId,
+                    0L,          // 一级目录父ID固定为0
+                    projectId,
+                    contractId,
+                    folderName,
+                    filePath,
+                    0
+            );
+                 // 入库一级目录
+                 checkInsertScanFolder(firstLevelScanFolder);
+                // 递归扫描当前一级目录下的所有子文件夹(子目录的父ID=当前一级目录ID)
+                recursiveScanSubFolders(firstLevelFolder, folderId, projectId, contractId);
+
+        }
+        return R.success("扫描并入库成功");
+    }
+
+    /**
+     * 递归扫描子文件夹并入库
+     * @param parentFolder 父文件夹对象(当前要扫描子目录的文件夹)
+     * @param parentFolderId 父文件夹在数据库中的ID(子目录的parent_id)
+     * @param projectId 项目ID(透传)
+     * @param contractId 合同ID(透传)
+     */
+    private void recursiveScanSubFolders(File parentFolder, Long parentFolderId, Long projectId, Long contractId) {
+        // 获取父文件夹下的所有子文件夹
+        File[] subFolders = parentFolder.listFiles(File::isDirectory);
+        if (subFolders == null || subFolders.length == 0) {
+            System.out.println("递归终止:父ID=" + parentFolderId + "(名称=" + parentFolder.getName() + ")下无子文件夹");
+            return;
+        }
+
+        // 遍历每个子文件夹,入库并继续递归其下的子目录
+        for (File subFolder : subFolders) {
+            String subFolderName = subFolder.getName();
+            Long subFolderId = scanFolderMapper.getId(subFolderName,contractId,projectId);
+            if(subFolderId==null){
+                subFolderId=SnowFlakeUtil.getId(); // 生成随机ID
+            }
+            String filePath = subFolder.getAbsolutePath();
+
+            // 创建子目录的ScanFolder对象(parent_id=父文件夹ID)
+            ScanFolder subScanFolder = new ScanFolder(
+                    subFolderId,
+                    parentFolderId, // 关联父目录ID
+                    projectId,
+                    contractId,
+                    subFolderName,
+                    filePath,
+                    0
+            );
+                // 入库子目录
+                 checkInsertScanFolder(subScanFolder);
+                // 递归扫描当前子目录下的子文件夹
+                recursiveScanSubFolders(subFolder, subFolderId, projectId, contractId);
+        }
+    }
+
+    /**
+     * 检查文件夹是否存在,不存在则新增
+     * @param scanFolder
+     * @return
+     */
+    public boolean checkInsertScanFolder(ScanFolder scanFolder) {
+        int result = scanFolderMapper.exists(scanFolder.getProjectId(), scanFolder.getContractId(), scanFolder.getFolderName());
+        if(result>0){
+            return false;
+        }else {
+            scanFolderMapper.insert(scanFolder);
+        }
+        return true;
+    }
+
+
+    /**
+     * 扫描文件并入库
+     * @param contractId
+     * @param projectId
+     */
+    public R scanAndSaveFiles(Long contractId, Long projectId) {
+        // 1. 构建合同对应的根文件夹路径(D:\PDF\${contractId})
+        String contractFolderPath = ROOT_PREFIX + File.separator + contractId;
+        System.out.println("contractFolderPath=" + contractFolderPath);
+        File contractFolder = new File(contractFolderPath);
+
+        // 校验根文件夹是否存在且为目录
+        if (!contractFolder.exists() || !contractFolder.isDirectory()) {
+            return R.fail("错误:根文件夹不存在或非法,路径=" + contractFolderPath);
+        }
+
+        System.out.println("开始扫描合同ID=" + contractId + "下的所有文件...");
+        // 递归扫描所有文件
+        recursiveScanFiles(contractFolder, projectId, contractId);
+        System.out.println("合同ID=" + contractId + "的文件扫描入库完成");
+        return R.success("扫描并入库完成");
+    }
+
+    @Override
+    public void sortNumber(Long contractId, Long projectId) {
+        List<ScanFile> scanFiles = scanFileMapper.selectList(new LambdaQueryWrapper<ScanFile>().eq(ScanFile::getContractId, contractId).eq(ScanFile::getProjectId, projectId).isNull(ScanFile::getSort).isNull(ScanFile::getDigitalNum).orderByAsc(ScanFile::getCreatTime));
+        Integer maxDigitalNum=scanFileMapper.selectMaxDigitalNum(contractId,projectId);
+        if(maxDigitalNum==null||maxDigitalNum<1){
+            maxDigitalNum=0;
+        }
+        Map<Long, List<ScanFile>> scanFilesMap = scanFiles.stream()
+                .collect(Collectors.groupingBy(ScanFile::getFolderId));
+        for (Map.Entry<Long, List<ScanFile>> entry : scanFilesMap.entrySet()) {
+            Integer MaxSort=scanFileMapper.selectMaxSort(contractId,projectId,entry.getKey());
+            if(MaxSort==null||MaxSort<1){
+                MaxSort=0;
+            }
+            List<ScanFile> list = entry.getValue();
+            for (ScanFile scanFile : list) {
+                scanFile.setSort(++MaxSort);
+                scanFile.setDigitalNum(++maxDigitalNum);
+                scanFileMapper.updateById(scanFile);
+            }
+        }
+    }
+
+
+    /**
+     * 递归扫描文件夹中的所有文件并入库
+     * @param currentFolder 当前扫描的文件夹
+     * @param projectId 项目ID
+     * @param contractId 合同ID
+     */
+    private void recursiveScanFiles(File currentFolder, Long projectId, Long contractId) {
+        // 1. 先处理当前文件夹中的文件
+        File[] files = currentFolder.listFiles(File::isFile);
+        if (files != null && files.length > 0) {
+            for (File file : files) {
+                processFile(file, currentFolder, projectId, contractId);
+            }
+        }
+
+        // 2. 递归处理子文件夹
+        File[] subFolders = currentFolder.listFiles(File::isDirectory);
+        if (subFolders != null && subFolders.length > 0) {
+            for (File subFolder : subFolders) {
+                recursiveScanFiles(subFolder, projectId, contractId);
+            }
+        }
+    }
+
+    /**
+     * 处理单个文件:获取信息并入库
+     */
+    private void processFile(File file, File parentFolder, Long projectId, Long contractId) {
+        String fileMD5 = calculateFileMD5(file);
+        try {
+            // 获取文件创建时间
+            BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
+            Date date = new Date(attr.creationTime().toMillis());
+            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            String createTime = sdf1.format(date); // 2023-12-25 14:30:45
+            // 获取文件信息
+            String fileName = file.getName();
+            String filePath = file.getAbsolutePath();
+            String folderName = parentFolder.getName();
+            // 查询对应的folder_id
+            Long folderId = scanFolderMapper.getId(folderName,contractId,projectId);
+            if (folderId == null) {
+                System.err.println("警告:未找到对应的文件夹记录,跳过文件:" + filePath +
+                        " (folderName=" + folderName + ")");
+                return;
+            }
+            // 检查文件是否已存在(根据project_id, contract_id, file_path判断)
+            if (fileExists(projectId, contractId, folderName)) {
+                System.out.println("文件已存在,跳过:" + filePath);
+                return;
+            }
+            BladeFile bladeFile = newIOSSClient.uploadFile(fileName, filePath);
+            if(bladeFile==null){
+                return;
+            }
+            String oSSFileMD5 = calculateOSSFileMD5(bladeFile.getLink());
+            if (fileMD5 != null && !fileMD5.equals(oSSFileMD5)) {
+                return;
+            }
+            String pdfNum = FileUtils.getPdfNum(bladeFile.getLink());
+            // 生成随机ID
+            Long fileId = SnowFlakeUtil.getId();
+            // 创建文件实体
+            ScanFile scanFile = new ScanFile(
+                    fileId,
+                    projectId,
+                    contractId,
+                    folderId,
+                    null,//序号
+                    null,//数字编号
+                    null,//文件编号
+                    fileName,
+                    pdfNum,//文件页数
+                    null,//文件日期
+                    createTime,
+                    filePath,
+                    bladeFile.getLink(),//OSS路径
+                    null,//负责人
+                    0,
+                    0
+            );
+            // 入库
+            if (scanFileMapper.insert(scanFile)>0) {
+                System.out.println("文件入库成功:" + filePath);
+                try {
+                    // 构建备份文件路径,保持原有的文件夹结构
+                    if (filePath.startsWith(ROOT_PREFIX)) {
+                        // 截取原始路径中除根目录外的部分
+                        String relativePath = filePath.substring(ROOT_PREFIX.length());
+                        System.out.println("relativePath: " + relativePath);
+                        // 构建备份文件完整路径
+                        String backupFilePath = ROOT_PREFIX_back + relativePath;
+                        System.out.println("backupFilePath: " + backupFilePath);
+                        File backupFile = new File(backupFilePath);
+                        // 创建备份文件所在的目录
+                        File backupDir = backupFile.getParentFile();
+                        if (!backupDir.exists()) {
+                            boolean dirCreated = backupDir.mkdirs();
+                            if (!dirCreated) {
+                                throw new IOException("无法创建备份目录: " + backupDir.getAbsolutePath());
+                            }
+                        }
+                        // 复制文件到备份目录
+                        Files.copy(file.toPath(), backupFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+                        String fileMD5New = calculateFileMD5(backupFile);
+                        if (fileMD5New != null && fileMD5New.equals(fileMD5)) {
+                            // 备份成功后删除源文件
+                            file.delete();
+                        }
+                    }
+                } catch (IOException e) {
+                    System.err.println("文件备份失败:" + filePath + ",错误信息:" + e.getMessage());
+                }
+            }
+        } catch (IOException e) {
+            System.err.println("处理文件时出错:" + file.getAbsolutePath() + ",错误信息:" + e.getMessage());
+        }
+    }
+    private Boolean fileExists(Long projectId, Long contractId, String folderName){
+        Integer i = scanFileMapper.exists(projectId, contractId, folderName);
+        if(i==1){
+            return true;
+        }else {
+            return false;
+        }
+    }
+
+    private String calculateFileMD5(File file) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            try (InputStream is = Files.newInputStream(file.toPath());
+                 DigestInputStream dis = new DigestInputStream(is, md)) {
+                byte[] buffer = new byte[8192];
+                while (dis.read(buffer) != -1) {
+                    // 读取文件内容以计算MD5
+                }
+            }
+            byte[] digest = md.digest();
+            StringBuilder sb = new StringBuilder();
+            for (byte b : digest) {
+                sb.append(String.format("%02x", b));
+            }
+            return sb.toString();
+        } catch (Exception e) {
+            System.err.println("计算本地文件MD5失败:" + file.getAbsolutePath() + ",错误信息:" + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 计算OSS文件的MD5值
+     * @param ossUrl OSS文件路径
+     * @return MD5值,如果出错返回null
+     */
+    private String calculateOSSFileMD5(String ossUrl) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+
+            // 使用URL下载OSS文件
+            URL url = new URL(ossUrl);
+            try (InputStream is = url.openStream();
+                 DigestInputStream dis = new DigestInputStream(is, md)) {
+                byte[] buffer = new byte[8192];
+                while (dis.read(buffer) != -1) {
+                    // 读取文件内容以计算MD5
+                }
+            }
+
+            byte[] digest = md.digest();
+            StringBuilder sb = new StringBuilder();
+            for (byte b : digest) {
+                sb.append(String.format("%02x", b));
+            }
+            return sb.toString();
+        } catch (Exception e) {
+            System.err.println("计算OSS文件MD5失败:" + ossUrl + ",错误信息:" + e.getMessage());
+            return null;
+        }
+    }
+}

+ 16 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ScanFolderServiceImpl.java

@@ -0,0 +1,16 @@
+package org.springblade.archive.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.AllArgsConstructor;
+import org.springblade.archive.entity.ArchivesAuto;
+import org.springblade.archive.entity.ScanFolder;
+import org.springblade.archive.mapper.ArchivesAutoMapper;
+import org.springblade.archive.service.ScanFolderService;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.manager.entity.WbsTreeSynchronousRecord;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class ScanFolderServiceImpl  implements ScanFolderService {
+}

+ 18 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/utils/FileUtils.java

@@ -9,6 +9,7 @@ import com.sun.image.codec.jpeg.JPEGImageEncoder;
 import lombok.extern.slf4j.Slf4j;
 
 import org.apache.commons.lang.StringUtils;
+import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
 import org.apache.poi.ss.usermodel.ClientAnchor;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -879,4 +880,21 @@ public class FileUtils {
         return String.valueOf(pageNumber);
     }
 
+    public static String getPdfNum(String url) {
+        try {
+            if (url.isEmpty() || url.equals("")) {
+                return "0";
+            }
+            InputStream pdfInputStream = CommonUtil.getOSSInputStream(url);
+            //获取这份文件的页数并设置签章策略
+            //获取PDF文件
+            PDDocument document = PDDocument.load(pdfInputStream);
+            int page = document.getPages().getCount();
+            return page + "";
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
 }

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

@@ -1738,7 +1738,7 @@ public R<String> batchDownloadFileToZip(String ids, HttpServletResponse response
                 String sql = "";
                 String sql1 = "";
                 if (primaryKeyId != null && primaryKeyId.indexOf("*") > 0) {
-                    primaryKeyId = primaryKeyId.substring(0, primaryKeyId.length() - 1);
+                    primaryKeyId=primaryKeyId.replaceAll("\\*","");
                     sql = "update u_trial_self_inspection_record set task_status = '未上报' where id=" + primaryKeyId;
                     sql1 = "SELECT e.* from u_entrust_info e left join u_trial_self_inspection_record t on t.entrust_id=e.id where t.id=" + primaryKeyId;
                 } else {
@@ -4046,7 +4046,7 @@ public R<Boolean> saveContractTreeNode(@RequestBody AddContractTreeNodeVO vo) {
 
                 //处理重复的数据
                 selectedNodeList = selectedNodeList.stream().collect(Collectors.collectingAndThen(
-                    Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(WbsTreePrivate::getId))),
+                    Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(WbsTreePrivate::getPKeyId))),
                     ArrayList::new
                 ));
 

+ 1 - 1
blade-service/blade-business/src/main/java/org/springblade/business/controller/NeiWaiYeProgressController.java

@@ -76,7 +76,7 @@ public class NeiWaiYeProgressController {
                     queryProcessDataVOList.addAll(this.informationQueryService.queryProcessDataByParentIdAndContractId2(contract.getId().toString(), classFy, contractId));
                 }
 
-            } else if (!new Integer("6").equals(node.getNodeType()) && !Arrays.asList("1,2,3,4".split(",")).contains(node.getMajorDataType().toString())) {
+            } else if (!new Integer("6").equals(node.getNodeType()) &&(node.getMajorDataType() == null || !Arrays.asList("1,2,3,4".split(",")).contains(node.getMajorDataType().toString()))) {
                 //查询出节点下所有填报节点
 //                List<WbsTreeContract> list = this.wbsTreeContractClient.getAllChildren(node.getId(), Long.parseLong(node.getContractId()));
 //                List<Long> keys = list.stream().map(l -> l.getPKeyId()).collect(Collectors.toList());

+ 16 - 3
blade-service/blade-business/src/main/java/org/springblade/business/controller/TaskController.java

@@ -1884,7 +1884,7 @@ public class TaskController extends BladeController {
             for (TaskParallel taskParallel : taskApproveUserNamesList) {
                 String eVisaContent = taskParallel.getEVisaContent();
                 if(ObjectUtil.isNotEmpty(eVisaContent) && eVisaContent.contains("当前等待电签的批次较多")){
-                    String sql = "INSERT INTO  `u_task_batch` (id,json_data,create_user,update_user,nick_name)VALUES("+i+",'{\"approvalFileList\":[],\"approvalType\":"+businessTaskPageVO.getApprovalType()+",\"comment\":\"OK\",\"flag\":\"OK\",\"formDataId\":\""+businessTaskPageVO.getFormDataId()+"\",\"parallelProcessInstanceId\":\""+taskParallel.getParallelProcessInstanceId()+"\",\"pass\":true,\"taskId\":\""+businessTaskPageVO.getTaskId()+"\"}',"+taskParallel.getTaskUser()+","+taskParallel.getTaskUser()+",'"+taskParallel.getTaskUserName()+"')";
+                    String sql = "INSERT INTO  `u_task_batch` (id,json_data,create_user,update_user,nick_name,update_time)VALUES("+i+",'{\"approvalFileList\":[],\"approvalType\":"+businessTaskPageVO.getApprovalType()+",\"comment\":\"OK\",\"flag\":\"OK\",\"formDataId\":\""+businessTaskPageVO.getFormDataId()+"\",\"parallelProcessInstanceId\":\""+taskParallel.getParallelProcessInstanceId()+"\",\"pass\":true,\"taskId\":\""+businessTaskPageVO.getTaskId()+"\"}',"+taskParallel.getTaskUser()+","+taskParallel.getTaskUser()+",'"+taskParallel.getTaskUserName()+"',SYSDATE())";
                     jdbcTemplate.execute(sql);
                     i++;
                 }
@@ -1923,11 +1923,21 @@ public class TaskController extends BladeController {
         int current = dto.getCurrent();
         int size = dto.getSize();
 
+        String sqlContractType="select contract_type from m_contract_info where id="+dto.getContractId()+" and is_deleted=0";
+        Integer contractType = jdbcTemplate.queryForObject(sqlContractType, Integer.class);
         //封装入参SQL
         List<Object> params = new ArrayList<>();
         Integer eVisaStatus = dto.getVisaStatus();
-        StringBuilder sqlString = new StringBuilder("SELECT * FROM u_task WHERE approval_type not in (4, 5, 6, 7, 11) AND status != 3 AND is_deleted=0 AND contract_id = " + dto.getContractId()); //approval_type != 4 非档案的任务就是1填报资料,2工程文件,3日志资料
-
+        StringBuilder sqlString = new StringBuilder("SELECT * FROM u_task WHERE approval_type not in (4, 5, 6, 7, 11) AND status != 3 AND is_deleted=0 " ); //approval_type != 4 非档案的任务就是1填报资料,2工程文件,3日志资料
+        if(contractType==2){
+            String sql="select contract_id_sg from m_contract_relation_jlyz where contract_id_jlyz="+dto.getContractId();
+            List<String> list = jdbcTemplate.query(sql, new SingleColumnRowMapper<>(String.class));
+            list.add(dto.getContractId());
+            String join = String.join(",", list);
+            sqlString.append(" AND contract_id in ("+join+")");
+        }else {
+            sqlString.append(" AND contract_id ="+ dto.getContractId());
+        }
         if (StringUtils.isNotBlank(dto.getStartTimeValue()) && StringUtils.isNotBlank(dto.getEndTimeValue())) {
             if (dto.getStartTimeValue().equals(dto.getEndTimeValue())) {
                 sqlString.append(" AND start_time = ?");
@@ -1964,6 +1974,9 @@ public class TaskController extends BladeController {
         }
         //是否填报过
         sqlString.append(" AND (SELECT COUNT(1)  FROM u_information_query WHERE u_task.form_data_id = id and is_deleted=0) > 0");
+        if(contractType==2){
+            sqlString.append(" AND (SELECT classify FROM u_information_query where  u_task.form_data_id = id and is_deleted=0)=2");
+        }
 
         //总数量
         String sqlCount = sqlString.toString().replace("*", "count(1)");

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

@@ -335,8 +335,6 @@ public class TrialSummaryController {
                  }
              }
 
-
-
             String htmlString = this.html(classC.getHtmlUrl());
             Map<String, String> indexMap = this.indexMap(htmlString, map2);
 

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

@@ -459,4 +459,9 @@ public class ArchiveFileClientImpl implements ArchiveFileClient {
     public Integer selectMaxSortByContractId(Long contractId) {
         return iArchiveFileService.selectMaxSortByContractId(contractId);
     }
+    @Override
+    public void saveBatchArchiveFile(List<ArchiveFile> list) {
+        iArchiveFileService.saveBatch(list);
+    }
+
 }

+ 1 - 1
blade-service/blade-business/src/main/java/org/springblade/business/feignClient/TaskClientImpl.java

@@ -305,8 +305,8 @@ public class TaskClientImpl implements TaskClient {
                     map.put("isUse", ids.contains(fixedFlow.getId()) ? 1 : 0);
                     map.put("fixedFlowName", fixedFlow.getFixedFlowName());
                     map.put("linkUserJoinString", StringUtils.join(names, ","));
+                    result.add(map);
                 }
-                result.add(map);
             }
             return result;
         }

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

@@ -156,7 +156,7 @@
     </select>
 
     <select id="selectArchiveFilePage" resultMap="archiveFileResultMap">
-        select u.id,u.project_id,u.contract_id,u.node_id,u.file_number,u.file_name,u.file_time,u.file_url,u.pdf_file_url,u.file_page,u.is_approval,u.is_certification,u.is_need_certification,u.duty_user,u.create_user,u.create_dept,u.create_time,u.update_user,u.update_time,u.status,u.is_deleted,u.sheet_type,u.sheet_source,u.drawing_no,u.cite_change_number,u.certification_time,u.e_visa_file,u.node_ext_id,u.file_type,u.archive_id,u.origin_id,u.filming_time,u.filmingor_time,u.tag_id,u.pic_code,u.refer_code,u.film_code,u.width,u.height,u.ftime,u.del_time,u.sort,u.box_name,u.box_number,u.is_auto_file,u.is_archive,u.page_num,u.file_size,u.source_type,u.is_element,u.pdf_page_url,u.fid,u.rectification,u.classify,u.m_wbs_tree_contract_p_key_id,u.u_image_classification_file_id,u.archive_file_storage_type,u.node_tree_structure,u.date_name,u.archive_file_stroage_type,u.out_id,u.sort_num,u.is_lock,u.archive_sort,u.remark,u.m_album_id
+        select u.id,u.project_id,u.contract_id,u.node_id,u.file_number,u.file_name,u.file_time,u.file_url,u.pdf_file_url,u.file_page,u.is_approval,u.is_certification,u.is_need_certification,u.duty_user,u.create_user,u.create_dept,u.create_time,u.update_user,u.update_time,u.status,u.is_deleted,u.sheet_type,u.sheet_source,u.drawing_no,u.cite_change_number,u.certification_time,u.e_visa_file,u.node_ext_id,u.file_type,u.archive_id,u.origin_id,u.tag_id,u.pic_code,u.refer_code,u.film_code,u.width,u.height,u.sort,u.box_name,u.box_number,u.is_auto_file,u.is_archive,u.page_num,u.file_size,u.source_type,u.is_element,u.pdf_page_url,u.fid,u.rectification,u.classify,u.m_wbs_tree_contract_p_key_id,u.u_image_classification_file_id,u.archive_file_storage_type,u.node_tree_structure,u.date_name,u.archive_file_stroage_type,u.out_id,u.sort_num,u.is_lock,u.archive_sort,u.remark,u.m_album_id
         from u_archive_file u
         <if test="vo.nodeIds != null and vo.nodeIds != ''">
             left join m_archive_tree_contract t on t.id = u.node_id and t.is_deleted= 0
@@ -240,10 +240,16 @@
             )
         </if>
         order by
+        u.sort,
         <if test="vo.nodeIds != null and vo.nodeIds != ''">
+            case
+            when t.tree_sort regexp '^[a-zA-Z]' then 0  -- 字母开头的排在前面
+            when t.tree_sort regexp '^[0-9]' then 1    -- 数字开头的排在后面
+            else 2                                   -- 其他情况
+            end,
             t.tree_sort,
         </if>
-        u.sort,u.sort_num,u.create_time
+        u.sort_num,u.create_time
         limit #{current}, #{size}
     </select>
 

+ 1 - 1
blade-service/blade-business/src/main/java/org/springblade/business/mapper/InformationQueryMapper.java

@@ -151,5 +151,5 @@ public interface InformationQueryMapper extends BaseMapper<InformationQuery> {
 
     int addCheckPdfInfoByIds(@Param("ids") List<String> ids,@Param("classify") String classify);
 
-    ChekPdfPaceVo getCheckPdfPaceInfo(@Param("contractId") String contractI, @Param("classify") String classify);
+    ChekPdfPaceVo getCheckPdfPaceInfo(@Param("contractId") String contractId, @Param("classify") String classify);
 }

+ 17 - 3
blade-service/blade-business/src/main/java/org/springblade/business/mapper/InformationQueryMapper.xml

@@ -1024,10 +1024,24 @@
     </update>
 
 
+<!--    <select id="getCheckPdfPaceInfo" resultType="org.springblade.business.vo.ChekPdfPaceVo">-->
+<!--          SELECT c.TotalCount,c.finishCount, IF(FLOOR(finishCount/TotalCount),null,0) as pace from (-->
+<!--             SELECT COUNT(1) as TotalCount,IF(SUM(IF(status = 'chek_status', 1, 0)),null,0) AS finishCount from u_information_query where contract_id=#{contractId} and classify=#{classify} and chek_status in(1,2,3)-->
+<!--          ) c-->
+<!--    </select>-->
+
     <select id="getCheckPdfPaceInfo" resultType="org.springblade.business.vo.ChekPdfPaceVo">
-        SELECT c.TotalCount,c.finishCount, IF(FLOOR(finishCount/TotalCount),null,0) as pace from (
-           SELECT COUNT(1) as TotalCount,IF(SUM(IF(status = 'chek_status', 1, 0)),null,0) AS finishCount from u_information_query where contract_id=#{contractId} and classify=#{classify} and chek_status in(1,2,3)
-        ) c
+        SELECT
+            COUNT(1) as totalCount,
+            SUM(CASE WHEN chek_status IN (1,2,3) THEN 1 ELSE 0 END) as finishCount,
+            CASE
+                WHEN COUNT(1) = 0 THEN 0
+                ELSE ROUND(SUM(CASE WHEN chek_status IN (1,2,3) THEN 1 ELSE 0 END) * 100.0 / COUNT(1), 2)
+                END as pace
+        FROM u_information_query
+        WHERE contract_id = #{contractId,jdbcType=VARCHAR}
+          AND classify = #{classify,jdbcType=VARCHAR}
+          AND chek_status IN (0,1,2,3)
     </select>
 
 </mapper>

+ 15 - 3
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ArchiveFileServiceImpl.java

@@ -328,13 +328,20 @@ public class ArchiveFileServiceImpl extends BaseServiceImpl<ArchiveFileMapper, A
 
     @Override
     public boolean sortByFileTime(Long nodeId) {
-        String sql = "SELECT id,file_time,node_id FROM u_archive_file WHERE node_id IN (SELECT id FROM m_archive_tree_contract WHERE  FIND_IN_SET(" + nodeId + ",ancestors)) AND is_deleted=0";
+        String sql = "SELECT id,file_time,node_id,sort FROM u_archive_file WHERE node_id IN (SELECT id FROM m_archive_tree_contract WHERE  FIND_IN_SET(" + nodeId + ",ancestors)) AND is_deleted=0";
         List<ArchiveFile> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(ArchiveFile.class));
         if (list.size() > 0) {
             Map<String, List<ArchiveFile>> group = list.stream()
                     .collect(Collectors.groupingBy(ArchiveFile::getNodeId));
             for (Map.Entry<String, List<ArchiveFile>> entry : group.entrySet()) {
                 List<ArchiveFile> archiveFiles = entry.getValue();
+                // 从archiveFiles列表中提取sort字段,处理null值
+                List<Integer> sortValues = archiveFiles.stream()
+                        .map(file -> {
+                            // 如果sort字段为null,返回0;否则返回实际值
+                            return file.getSort() != null ? file.getSort() : 0;
+                        })
+                        .collect(Collectors.toList());
                 archiveFiles.sort((a, b) -> {
                     if (a.getFileTime() == null && b.getFileTime() == null) return 0;
                     if (a.getFileTime() == null) return -1;
@@ -342,8 +349,13 @@ public class ArchiveFileServiceImpl extends BaseServiceImpl<ArchiveFileMapper, A
                     return a.getFileTime().compareTo(b.getFileTime());
                 });
                 int sort = 0;
-                for (ArchiveFile file : archiveFiles) {
-                    file.setSort(sort++);
+                for (int i = 0; i < archiveFiles.size(); i++ ) {
+                    Integer sortValue = sortValues.get(i);
+                    if(sortValue!=0){
+                        archiveFiles.get(i).setSort(sortValue);
+                    }else {
+                        archiveFiles.get(i).setSort(sort++);
+                    }
                 }
                 this.updateBatchById(archiveFiles);
             }

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

@@ -44,6 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 import java.io.File;
@@ -1116,6 +1117,7 @@ public class InformationQueryServiceImpl extends BaseServiceImpl<InformationQuer
     }
 
     @Override
+//    @Async
     public R updateCheckPdfInfo(String type, String ids,String classify) {
         if(type==null || Func.isNull(type)){
             return R.fail("type不能weinull");

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

@@ -656,6 +656,7 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
                     }
                     taskBatch.setNickName(nickName);
                     taskBatch.setCreateTime(new Date());
+                    taskBatch.setUpdateTime(new Date());
                     taskList.add(taskBatch);
                     //设置委托单的状态
                     try {
@@ -2357,7 +2358,7 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
         String sqlForTaskPall = "UPDATE u_task_parallel a  set a.`status`=2 , a.e_visa_status=1 ,a.e_visa_content='电签成功' where a.process_instance_id in(select process_instance_id  from u_task b  where b.form_data_id in( " + ids2 + ") and b.status in(0,1,2) and b.is_deleted = 0)  and a.`status` in(0,1)";
 
         String taskBtech = "insert into u_task_batch(id,task_parallel_id,json_data,create_user,create_dept,create_time,update_user,update_time,status,is_deleted,nick_name,sign_format,sign_type)  " +
-                " SELECT a.id,a.process_instance_id,json_object('approvalFileList',json_array(),'approvalType',b.approval_type,'comment','','flag','OK','formDataId',b.form_data_id,'parallelProcessInstanceId',a.parallel_process_instance_id,'pass',true,'taskId',b.id) as  json_data,a.task_user,a.create_dept,a.create_time,a.update_user,a.update_time,1 as status,0 as is_deleted,a.task_user_name as nick_name ,1 as sign_format,1 as sign_type from u_task_parallel a,u_task b where b.`status` in(1,2) and a.`status` in(2)  and   a.process_instance_id=b.process_instance_id " +
+                " SELECT a.id,a.process_instance_id,json_object('approvalFileList',json_array(),'approvalType',b.approval_type,'comment','','flag','OK','formDataId',b.form_data_id,'parallelProcessInstanceId',a.parallel_process_instance_id,'pass',true,'taskId',b.id) as  json_data,a.task_user,a.create_dept,a.create_time,a.update_user,SYSDATE(),1 as status,0 as is_deleted,a.task_user_name as nick_name ,1 as sign_format,1 as sign_type from u_task_parallel a,u_task b where b.`status` in(1,2) and a.`status` in(2)  and   a.process_instance_id=b.process_instance_id " +
                 " and b.form_data_id in( " + ids2 + ") and a.parallel_process_instance_id not in(SELECT JSON_EXTRACT(c.json_data, '$.parallelProcessInstanceId') from u_task_batch c)";
 
         if(userIds!=null && userIds.length()>=1){

+ 2 - 15
blade-service/blade-dingding/src/main/java/org/springblade/dingding/service/impl/MeetingServiceImpl.java

@@ -113,19 +113,6 @@ public class MeetingServiceImpl implements MeetingService {
 
                     return compareChineseNumbers(number1, number2);
                 });
-                for (MeetingVo vo : vos) {
-                    if("一会议室".equals(vo.getFixedData())){
-                        vo.setFloor("6楼");
-                    } else if ("二会议室".equals(vo.getFixedData())) {
-                        vo.setFloor("6楼");
-                    }else if ("三会议室".equals(vo.getFixedData())) {
-                        vo.setFloor("4楼");
-                    }else if ("四会议室".equals(vo.getFixedData())) {
-                        vo.setFloor("3楼");
-                    }else if ("五会议室".equals(vo.getFixedData())) {
-                        vo.setFloor("2楼");
-                    }
-                }
                 UserInfoVO vo = new UserInfoVO();
                 vo.setSystem("dingding");
                 vo.setContractId(1L);
@@ -147,8 +134,8 @@ public class MeetingServiceImpl implements MeetingService {
     // 提取会议室名称中的中文数字部分
     private String extractChineseNumber(String roomName) {
         // 假设格式为 "一会议室"、"二会议室" 等,提取前面的中文数字
-        if (roomName != null && roomName.endsWith("会议室")) {
-            return roomName.substring(0, roomName.length() - 3); // 去掉"会议室"三个字
+        if (roomName != null) {
+            return roomName.substring(0, 1); // 去掉"会议室"三个字
         }
         return roomName;
     }

+ 6 - 3
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/controller/ChekSignData.java

@@ -14,9 +14,11 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * 清表基础数据表 控制器
@@ -55,16 +57,17 @@ public class ChekSignData {
             for (ScrSignInfoVO dataInfo : query) {
                 if (executor.getQueue().size()<=30 ) {
                     Long nodeId = dataInfo.getId();
-                    Boolean aBoolean = RedisTemplate.hasKey("chek-" + nodeId);
+                    // 兼容以前 u_information_query 与 u_task 一对多的关系
+                    Boolean aBoolean = RedisTemplate.hasKey("chek-" + nodeId + dataInfo.getProcessInstanceId());
 
                     if (!aBoolean) {
-                        RedisTemplate.opsForValue().set("chek-" + nodeId, "1",3600, TimeUnit.SECONDS);
+                        RedisTemplate.opsForValue().set("chek-" + nodeId + dataInfo.getProcessInstanceId(), "1",3600, TimeUnit.SECONDS);
                         CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
                             try {
                                 /*===============执行批量任务===============*/
                                 scrDataService.sctTaskBatch(dataInfo);
                             } catch (Exception e) {
-                                RedisTemplate.delete("chek-" + nodeId);
+                                RedisTemplate.delete("chek-" + nodeId + dataInfo.getProcessInstanceId());
                                 e.printStackTrace();
                             }
                         }, executor);

+ 1 - 1
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() {
         //执行代码
 

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

@@ -210,7 +210,7 @@ public class EVDataServiceImpl implements EVDataService {
                 }
 
                 String sql2 = "INSERT into u_task_batch(id,task_parallel_id,json_data,create_user,create_dept,create_time,update_user,update_time,status,is_deleted,nick_name,sign_format,sign_type)" +
-                        "SELECT " + id + " as id,task_parallel_id,json_data,create_user,create_dept,create_time,update_user,update_time,status,is_deleted,nick_name,sign_format,2 as sign_type from u_task_batch where id=" + batchId + "";
+                        "SELECT " + id + " as id,task_parallel_id,json_data,create_user,create_dept,create_time,update_user,SYSDATE(),status,is_deleted,nick_name,sign_format,2 as sign_type from u_task_batch where id=" + batchId + "";
                 jdbcTemplate.execute(sql2);
             }
         }
@@ -342,11 +342,13 @@ public class EVDataServiceImpl implements EVDataService {
                             }
                         }
                     }
+                    //电签成功后需要将check_status改为1,进行电签检测
+                    String updateCheckStatus="update  u_information_query set check_status=1 where id='" + taskApp.getFormDataId() + "' and check_status!=0 ";
+                    jdbcTemplate.execute(updateCheckStatus);
                     updateSql = "update u_information_query set pdf_trial_url_position='" + pdfTrialUrlPosition + "',business_time='" + taskApp.getPdfDate() + "',node_pdf_url='" + nodePdfUrl + "',e_visa_pdf_page=" + pdfPage + ",e_visa_pdf_size=" + pdfSize + ",e_visa_pdf_url='" + taskApp.getLastFilePdfUrl() + "',status='" + taskApp.getSigType() + "',update_time=SYSDATE() where id='" + taskApp.getFormDataId() + "' ";
                     //修改 计量 需要引用的 附件信息
                     String updataFile = "update s_attachment_form set file_url='"+nodePdfUrl+"' ,file_pdf_url='"+nodePdfUrl+"' where select_id='"+taskApp.getFormDataId()+"' ";
                     jdbcTemplate.execute(updataFile);
-
                     String upTsk = "update s_attachment_form_task set file_url='"+nodePdfUrl+"' ,file_pdf_url='"+nodePdfUrl+"' where select_id='"+taskApp.getFormDataId()+"' ";
                     jdbcTemplate.execute(upTsk);
                 } else if (taskApp.getApprovalType() == 2) {
@@ -465,24 +467,14 @@ public class EVDataServiceImpl implements EVDataService {
 
             } else if (taskApp.getApprovalType() == 5) {
                 map = this.jdbcTemplate.queryForMap("select * from s_interim_pay_certificate where  is_deleted=0 and contract_period_id = " + taskApp.getFormDataId());
-                String pdfUrl=map.get("raw_url") + "";
+                String pdfUrl = "";
                 //中间计量用逗号拼接pagePdfUrl
-               /* String pageUrl = map.get("page_pdf_url")+"";
-                if(StringUtils.isNotEmpty(pageUrl) && pageUrl.length()>=20 ){
-                    String jsonStr=map.get("page_pdf_url")+"";
-                    ObjectMapper mapper = new ObjectMapper();
-                    JsonNode jsonNode = mapper.readTree(jsonStr);
-                    StringBuilder result = new StringBuilder();
-                    Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
-                    while (fields.hasNext()) {
-                        Map.Entry<String, JsonNode> entry = fields.next();
-                        result.append(entry.getValue().asText());
-                        if (fields.hasNext()) {
-                            result.append(",");
-                        }
-                    }
-                    pdfUrl=result.toString();
-                }*/
+                String pageUrl = map.get("page_pdf_url")+"";
+                if(StringUtils.isNotEmpty(pageUrl) && pageUrl.length()>=20 ) {
+                    pageUrl = pageUrl.replaceAll(" ", "");
+                }else{
+                    pdfUrl=map.get("raw_url") + "";
+                }
                 taskApp.setSignPdfUrl(pdfUrl);
             } else if (taskApp.getApprovalType() == 6 || taskApp.getApprovalType() == 7) {
                 map = this.jdbcTemplate.queryForMap("select * from  s_material_start_statement where is_deleted=0 and meter_period_id = " + taskApp.getFormDataId());
@@ -630,7 +622,7 @@ public class EVDataServiceImpl implements EVDataService {
     public void addSignatureDataByAXQZ(List<SealStrategyVO> strategyListByAXQ,String formDataId,String markType) {
         System.out.println("www");
         String deleteSql = "delete from m_sign_data where query_id = '" + formDataId + "' and user_id in( ";
-        String insertsql = "INSERT INTO m_sign_data (id, query_id, text_id, user_id, sign_time, type, img_url, sign_type) VALUES (?, ?, ?, ?, SYSDATE(), ?, ?, ?)";
+        String insertsql = "INSERT INTO m_sign_data (id, query_id, text_id, user_id, sign_time, type, img_url, sign_type, pyzbx, pyzby) VALUES (?, ?, ?, ?, SYSDATE(), ?, ?, ?, ?, ?)";
         List<Object[]> batchArgs = new ArrayList<>();
         for (SealStrategyVO sealStrategyVO : strategyListByAXQ) {
             long newPkId = SnowFlakeUtil.getId(); //主键Id
@@ -638,7 +630,7 @@ public class EVDataServiceImpl implements EVDataService {
             if(sealStrategyVO.isCompanySeal()){
                 type = "2";
             }
-            batchArgs.add(new Object[]{newPkId, formDataId, sealStrategyVO.getKeyword(), sealStrategyVO.getUserId(), type, sealStrategyVO.getImageUrl(), markType});
+            batchArgs.add(new Object[]{newPkId, formDataId, sealStrategyVO.getKeyword(), sealStrategyVO.getUserId(), type, sealStrategyVO.getImageUrl(), markType, sealStrategyVO.getOffSetX(), sealStrategyVO.getOffSetY()});
             deleteSql+= sealStrategyVO.getUserId() +",";
         }
         // 添加更多数据...
@@ -651,13 +643,13 @@ public class EVDataServiceImpl implements EVDataService {
     public void addSignatureDataByDFZX(List<Map<String, Object>> strategyListByAXQ,String formDataId,String markType) {
         System.out.println("www");
         String deleteSql = "delete from m_sign_data where query_id = '" + formDataId + "' and user_id in( ";
-        String insertsql = "INSERT INTO m_sign_data (id, query_id, text_id, user_id, sign_time, type, img_url, sign_type) VALUES (?, ?, ?, ?, SYSDATE(), ?, ?, ?)";
+        String insertsql = "INSERT INTO m_sign_data (id, query_id, text_id, user_id, sign_time, type, img_url, sign_type, pyzbx, pyzby) VALUES (?, ?, ?, ?, SYSDATE(), ?, ?, ?, ?, ?)";
         List<Object[]> batchArgs = new ArrayList<>();
         for (Map<String, Object> map : strategyListByAXQ) {
             long newPkId = SnowFlakeUtil.getId(); //主键Id
             String userId = map.get("userId")+"";
             String type = map.get("type")+"";
-            batchArgs.add(new Object[]{newPkId, formDataId, map.get("keyWord"), userId, type, map.get("sealId"), markType});
+            batchArgs.add(new Object[]{newPkId, formDataId, map.get("keyWord"), userId, type, map.get("sealId"), markType, map.get("pyzbx"), map.get("pyzby")});
             deleteSql+=userId +",";
          }
         // 添加更多数据...
@@ -946,7 +938,7 @@ public class EVDataServiceImpl implements EVDataService {
             int i = SignFtpUtil.uploadFile(locPdfUrl, inputStream);
             if (i == 1) {
                  result = eVisaService.signPdfByAXQZ(pdfVO, locPdfUrl, OutPdfUrl);
-                if(result!=null){
+                if(result!=null && result[0]!=null){
                     int i1 = SignFtpUtil.downloadFile(dataFileUrl, OutPdfUrl);
                     fileUrl = dataFileUrl;
                     SignFtpUtil.FTPDeleteDir(locPdfUrl);
@@ -961,7 +953,7 @@ public class EVDataServiceImpl implements EVDataService {
         } else {
             result = eVisaService.signPdfByAXQZ(pdfVO, fileByte);
             //执行电签
-            if (result != null) {
+            if(result!=null && result[0]!=null){
                 try {
                     //对文件流做判断
                     byte[] bytes = (byte[]) result[0];

+ 192 - 56
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/ScrDataServiceImpl.java

@@ -7,11 +7,10 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
 import org.springblade.business.vo.ScrSignInfoVO;
-import org.springblade.business.vo.TaskSignInfoVO;
 import org.springblade.common.utils.CommonUtil;
-import org.springblade.core.tool.utils.BeanUtil;
 import org.springblade.core.tool.utils.CollectionUtil;
 import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.evisa.service.ScrDataService;
 import org.springblade.evisa.utils.PdfAddimgUtil;
 import org.springblade.evisa.vo.SignKeyVO;
@@ -70,10 +69,10 @@ public class ScrDataServiceImpl implements ScrDataService {
 
         if(strategyListByDFZX==null || strategyListByDFZX.size()==0){
 
-            String sql2 = "UPDATE u_information_query set chek_status=10 where id='"+taskApp.getId()+"'";
+            String sql2 = "UPDATE u_information_query set chek_status=3 where id='"+taskApp.getId()+"'";
             jdbcTemplate.execute(sql2);
 
-            RedisTemplate.delete("chek-" + taskApp.getId());
+            RedisTemplate.delete("chek-" + taskApp.getId() + taskApp.getProcessInstanceId());
             System.out.println(taskApp.getProcessInstanceId()+"-"+"总共:" );
             return;
         }
@@ -221,7 +220,7 @@ public class ScrDataServiceImpl implements ScrDataService {
             String sql2 = "UPDATE u_information_query set chek_status=3 where id='"+taskApp.getId()+"'";
             jdbcTemplate.execute(sql2);
         }
-        RedisTemplate.delete("chek-" + taskApp.getId());
+        RedisTemplate.delete("chek-" + taskApp.getId() + taskApp.getProcessInstanceId());
       //  System.out.println(taskApp.getProcessInstanceId()+"-"+"总共:" );
       //  System.out.println(taskApp.getId() +"-"+"总共:" + positions.size() + "-剩下-" + differentElements.size());
     }
@@ -230,19 +229,17 @@ public class ScrDataServiceImpl implements ScrDataService {
 
     // 添加电签策略 -- 安心签
     public List<Map<String, Object>> getStrategyListByAXQ(ScrSignInfoVO task, String ids) {
-        String sql = "select task_user,count(1) as total,initiative,status,(select  count(1) from u_task_parallel where process_instance_id = '" + task.getProcessInstanceId() + "' and initiative=2 and status=2) as pCount from u_task_parallel where process_instance_id = '" + task.getProcessInstanceId() + "'";
+        String sql = "select task_user,initiative,status,(select  count(1) from u_task_parallel where process_instance_id = '" + task.getProcessInstanceId() + "' and initiative=2 and status=2) as pCount from u_task_parallel where process_instance_id = '" + task.getProcessInstanceId() + "'";
         List<Map<String, Object>> mapList = jdbcTemplate.queryForList(sql);
         ArrayList<String> strArray = new ArrayList();
-        int total = 0;
         int pCount = 0;
         if (mapList != null && mapList.size() >= 1) {
             for (int i = 0; i < mapList.size(); i++) {
                 Map<String, Object> map = mapList.get(i);
                 if(i==0){
-                    total = Func.toInt(map.get("total"));
                     pCount = Func.toInt(map.get("pCount"));
                 }
-                if(map.get("initiative").equals("2") && map.get("status").equals("2")){
+                if((map.get("initiative")+"").equals("2") && (map.get("status")+"").equals("2")){
                     strArray.add(map.get("task_user").toString());
                 }
             }
@@ -270,14 +267,8 @@ public class ScrDataServiceImpl implements ScrDataService {
             for (int i = 0; i < strArray.size(); i++) {
                 List<Map<String, Object>> maps2 = null;
                 if (dqIds.length() > 0) {
-                    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.get(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.get(i) + " and c.is_deleted=0 ) ) x where x.sealId is not null ";
-                    if (total==pCount) {
-                        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);
-                    }
-                    maps2 = jdbcTemplate.queryForList(sqlinfo);
+                    String sqlinfo = " SELECT * from ( SELECT a.id as keyWord,a.project_id,a.pyzbx ,a.pyzby,(SELECT signature_file_url from m_sign_pfx_file where is_register=1 and certificate_user_id='" + strArray.get(i) + "' and is_deleted=0  ) as signature_file_url, '1' as type 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.get(i) + " and c.is_deleted=0 ) ) x where x.signature_file_url is not null ";
+                    System.out.println("安心签--签字--" + sqlinfo);
                     maps2 = jdbcTemplate.queryForList(sqlinfo);
                 }
                 if(CollectionUtil.isNotEmpty(maps2)) {
@@ -301,26 +292,43 @@ public class ScrDataServiceImpl implements ScrDataService {
                             maps.add(keyList.get(0));
                         }
                     }
+                } else {
+                    // 查询 sign_data 表
+                    List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * from m_sign_data where query_id = ? and text_id in (?) and user_id = ? and type = 1", task.getId(), dqIds, strArray.get(i));
+                    if (!list.isEmpty()) {
+                        System.out.println("1111111111111111111");
+                    }
                 }
-             /*   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);
+            }
+
+
+
+            String sqlinfo = "SELECT a.id as keyWord,a.pyzbx,a.pyzby,b.certificate_number as sealId, '2' as type 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);
+            List<Map<String, Object>> maps2 = jdbcTemplate.queryForList(sqlinfo);
+            if(CollectionUtil.isNotEmpty(maps2)) {
+                Map<String, List<Map<String, Object>>> peopleByAge = maps2.stream()
+                        .collect(Collectors.groupingBy(hada -> (Func.toStr(hada.get("keyWord")))));
+                for (String keyId : peopleByAge.keySet()) {
+                    int exId = 0;
+                    List<Map<String, Object>> keyList = peopleByAge.get(keyId);
+                    if (keyList != null && keyList.size() == 1) {
+                        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())) {
+                                maps.add(datax);
+                                exId = 1;
                             }
                         }
                     }
-                }*/
+                    if (exId == 0) {
+                        maps.add(keyList.get(0));
+                    }
+                }
+            } else {
+                System.out.println("22222222222222222222");
             }
         }
         return maps;
@@ -406,6 +414,8 @@ public class ScrDataServiceImpl implements ScrDataService {
             List<String> positions = pdfSignIds.getEVisaConfigList();
             Map<String, String> dataMap = pdfSignIds.getDataMap();
 
+            Map<String, String> dataMap1 = checkSignByPdf(taskApp, pdfSignIds, pdfData);
+
             List<String> sucessUser = new ArrayList<>();
             List<String> sucessCompan = new ArrayList<>();
             String ids = String.join(",", positions);
@@ -420,10 +430,10 @@ public class ScrDataServiceImpl implements ScrDataService {
 
             if (strategyListByDFZX == null || strategyListByDFZX.size() == 0) {
 
-                String sql2 = "UPDATE u_information_query set chek_status=10 where id='" + taskApp.getId() + "'";
+                String sql2 = "UPDATE u_information_query set chek_status=3 where chek_status != 2 and id='" + taskApp.getId() + "'";
                 jdbcTemplate.execute(sql2);
 
-                RedisTemplate.delete("chek-" + taskApp.getId());
+                RedisTemplate.delete("chek-" + taskApp.getId() + taskApp.getProcessInstanceId());
                 System.out.println(taskApp.getProcessInstanceId() + "-" + "总共:");
                 return;
             }
@@ -566,8 +576,8 @@ public class ScrDataServiceImpl implements ScrDataService {
                                 if (pkeyid.length() < 19) {
                                     keyw = keyw - (float) (10 - pkeyid.length() / 2.0);
                                 }
-//                                System.out.println("page = " + (i + 1) + ", type = " + type + ", keyid = " + pkeyid + ", imgX : " + imgX + ", keyw : " + keyw + ", imgY : "
-//                                        + imgY + ", keyh : " + keyh + ", keyword =" + keyData.replace("\r", "\\r") + ", remarkType = " + taskApp.getRemarkType());
+                                System.out.println("page = " + (i + 1) + ", type = " + type + ", keyid = " + pkeyid + ", imgX : " + imgX + ", keyw : " + keyw + ", imgY : "
+                                        + imgY + ", keyh : " + keyh + ", keyword =" + keyData.replace("\r", "\\r") + ", remarkType = " + taskApp.getRemarkType());
                                 if (Math.abs(imgX - keyw) <= threshold && Math.abs(imgY - keyh) <= threshold) {
                                     if (type.equals("1")) { //个人
                                         sucessUser.add(pkeyid);
@@ -600,22 +610,6 @@ public class ScrDataServiceImpl implements ScrDataService {
                 }
             }
 
-//            boolean isSign1 = true;
-//            //判断个人是否签完
-//            List<String> userList = strategyListByDFZX.stream().filter(item -> item.get("type").equals("1")).map(map -> map.get("keyWord").toString()).collect(Collectors.toList());
-//            if (!userList.isEmpty()) {
-//                Set<String> differentElements = new HashSet<>(userList);
-//                sucessUser.forEach(differentElements::remove);
-//                if (!differentElements.isEmpty()) {
-//                    isSign1 = false;
-//                    StringBuilder sb = new StringBuilder();
-//                    sb.append("id = ").append(taskApp.getId()).append(", 人总共:").append(userList.size()).append(", 剩下:").append(differentElements.size());
-//                    for (String element : differentElements) {
-//                        sb.append(", ").append(element);
-//                    }
-//                    System.err.println(sb);
-//                }
-//            }
             if(!sucessUser.isEmpty() && isSign){
                 for(String user:sucessUser){
                     for(String mapkey:dataMap.keySet()){
@@ -634,7 +628,42 @@ public class ScrDataServiceImpl implements ScrDataService {
                     }
                 }
                 if(!dataUserMap.isEmpty()){
-                    isSign = false ;
+                    Map<String, float[]> allKeyPositions = getKeyPositions(pdfSignIds, pdfData);
+                    Set<String> removeKeys = new HashSet<>();
+                    dataUserMap.forEach((id, value) -> {
+                        float[] floats = allKeyPositions.get(value);
+                        if (floats == null || floats.length < 6) {
+                            return;
+                        }
+                        dataMap.forEach((key, value1) -> {
+                            float[] floats1 = allKeyPositions.get(value);
+                            if (floats1.length < 2) {
+                                return;
+                            }
+                            if (floats[0] != floats1[0]) {
+                                return;
+                            }
+                            PDPage page = document.getPage((int) floats[0] - 1);
+                            float pageHeight = page.getMediaBox().getHeight();
+                            float pageWidth = page.getMediaBox().getWidth();
+                            if (Math.abs(floats[5] * pageHeight - floats1[3] * pageHeight) < 1 && Math.abs(floats[4] * pageWidth - floats1[1] * pageWidth) < 1 && !dataUserMap.containsKey(key)) {
+                                removeKeys.add(id);
+                            }
+                        });
+                    });
+                    if (!removeKeys.isEmpty()) {
+                        dataUserMap.entrySet().removeIf(entry -> removeKeys.contains(entry.getKey()));
+                    }
+                    if (!dataUserMap.isEmpty()) {
+                        if (dataMap1 == null) {
+                            dataUserMap.entrySet().removeIf(item -> true);
+                        } else {
+                            dataUserMap.entrySet().removeIf(entry -> !dataMap1.containsKey(entry.getKey()));
+                        }
+                    }
+                    if (!dataUserMap.isEmpty()) {
+                        isSign = false ;
+                    }
                 }
                 System.out.println(taskApp.getId() +"-"+"个人总共:" + sucessUser.size() + "-剩下-" +dataUserMap.keySet().size());
             }
@@ -642,10 +671,117 @@ public class ScrDataServiceImpl implements ScrDataService {
                 String sql2 = "UPDATE u_information_query set chek_status=2 where id='" + taskApp.getId() + "'";
                 jdbcTemplate.execute(sql2);
             } else {
-                String sql2 = "UPDATE u_information_query set chek_status=3 where id='" + taskApp.getId() + "'";
+                String sql2 = "UPDATE u_information_query set chek_status=3 where chek_status != 2 and id='" + taskApp.getId() + "'";
                 jdbcTemplate.execute(sql2);
             }
-            RedisTemplate.delete("chek-" + taskApp.getId());
+            RedisTemplate.delete("chek-" + taskApp.getId() + taskApp.getProcessInstanceId());
+        }
+    }
+
+    /**
+     * 获取所有电签关键字的位置
+     */
+    public Map<String, float[]> getKeyPositions(SignKeyVO pdfSignIds,byte[] pdfData) throws Exception {
+        if (pdfSignIds == null) {
+            return  null;
+        }
+        Map<String, String> dataMap = pdfSignIds.getDataMap();
+        Collection<String> keys = dataMap.values();
+        if (keys.isEmpty()) {
+            return null;
+        }
+        List<PDFIndexInfo> pdfIndexInfo = PdfAddimgUtil.findKeywordPostions(pdfData,  String.join(",", keys));
+        if (pdfIndexInfo.isEmpty()) {
+            return  null;
+        }
+        Map<String, float[]> map = new HashMap<>();
+        for (PDFIndexInfo info : pdfIndexInfo) {
+            map.put(info.getPkeyid(), info.getDataInfo());
+        }
+        return map;
+    }
+
+    public Map<String, String> checkSignByPdf(ScrSignInfoVO taskApp,SignKeyVO pdfSignIds,byte[] pdfData) throws Exception {
+        int threshold = 5;
+        //转换
+        try (PDDocument document = PDDocument.load(pdfData);) {
+            List<String> positions = pdfSignIds.getEVisaConfigList();
+            Map<String, String> dataMap = pdfSignIds.getDataMap();
+            String keyWord = String.join(",", positions);
+            List<String> sucessList = new ArrayList<>();
+            List<PDFIndexInfo> pdfIndexInfo = PdfAddimgUtil.findKeywordPostions(pdfData, keyWord);
+            Map<String, List<PDFIndexInfo>> groupBy = pdfIndexInfo.stream().collect(Collectors.groupingBy(da -> Func.toStr(da.getDataInfo()[0])));
+            for (int i = 0; i < document.getPages().getCount(); i++) {
+                PDPage page = document.getPage(i);
+                List<PDAnnotation> annotations = page.getAnnotations();
+                for (PDAnnotation annotation : annotations) {
+                    if (annotation instanceof PDAnnotationWidget) {
+                        PDRectangle rect = annotation.getRectangle();
+                        float imgW = rect.getWidth();
+                        float imgH = rect.getHeight();
+                        float imgX = rect.getLowerLeftX() + imgW / 2;
+                        float imgY = rect.getLowerLeftY() + imgH / 2;
+                        List<PDFIndexInfo> pdfIndexInfos = groupBy.get((i + 1) + ".0");
+                        if (pdfIndexInfos != null && !pdfIndexInfos.isEmpty()) {
+                            for (PDFIndexInfo pdfInfo : pdfIndexInfos) {
+                                String pkeyid = pdfInfo.getPkeyid();
+                                float[] dataInfo = pdfInfo.getDataInfo();
+                                float keyX = dataInfo[1];
+                                float keyY = dataInfo[2];
+                                float pageHeight = page.getMediaBox().getHeight();
+                                float pageWidth = page.getMediaBox().getWidth();
+
+                                float keyw = keyX * pageWidth;
+                                float keyh = pageHeight - keyY * pageHeight;
+                                float pyzbx = 0.0f;
+                                float pyzby = 0.0f;
+                                if (taskApp.getRemarkType().equals("3")) { //东方中讯
+                                    if (imgH >= 100) {
+                                        // 签章
+                                        keyw = keyw + pyzbx - 21;
+                                        keyh = keyh + pyzby - 16;
+                                    } else {
+                                        keyw = keyw + pyzbx + 10;
+                                        keyh = keyh + pyzby;
+                                    }
+                                } else if (taskApp.getRemarkType().equals("2")) { //东方中讯
+                                    if (imgH >= 100) {
+                                        // 签章
+                                        keyw = keyw + pyzbx - 21;
+                                        keyh = keyh + pyzby - 15;
+                                    } else {
+                                        keyw = keyw + pyzbx + 10;
+                                        keyh = keyh + pyzby;
+                                    }
+                                } else {
+                                    keyw = keyw + pyzbx + 5;
+                                    keyh = keyh + pyzby;
+                                }
+                                if (pkeyid.length() < 19) {
+                                    keyw = keyw - (float) (10 - pkeyid.length() / 2.0);
+                                }
+                                if (Math.abs(imgX - keyw) <= threshold && Math.abs(imgY - keyh) <= threshold) {
+                                    sucessList.add(pkeyid);
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            Map<String, String> dataUserMap = new HashMap<>(dataMap);
+            //判断章是否签完
+            if (!sucessList.isEmpty()) {
+                for (String user : sucessList) {
+                    for (String mapkey : dataMap.keySet()) {
+                        String mapval = dataMap.get(mapkey);
+                        if (mapval.contains(user)) {
+                            dataUserMap.remove(mapkey);
+                        }
+                    }
+                }
+            }
+            return dataUserMap;
         }
     }
 }

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

@@ -265,6 +265,16 @@ public class PdfAddimgUtil {
                 }
 
                 float[] postions = charPositions.get(positionIndex);
+                try {
+                    // 获取text的结束位置
+                    int endPos = positionIndex + text.length();
+                    if (endPos < content.length()) {
+                        float[] floats = charPositions.get(endPos);
+                        postions = new float[]{postions[0], postions[1], postions[2], floats[0], floats[1], floats[2]};
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
                 data.setDataInfo(postions);
                 data.setPkeyid(text);
                 result.add(data);
@@ -406,8 +416,6 @@ public class PdfAddimgUtil {
             String text = stripper.getText(document);
             String[] lines = text.split("[ \\n]+");
 
-            Pattern pattern = Pattern.compile("(\\d{4}[年-]\\d{2}[月-]\\d{2}日?)");
-
             for(int k=0;k<lines.length;k++){
                 String textStr = lines[k];
                 int index = textStr.indexOf("*");

+ 11 - 8
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java

@@ -4347,17 +4347,20 @@ public class ExcelTabController extends BladeController {
         }else {
             WbsTreeContract wbsInfo = wbsTreeContractService.getBaseMapper().selectOne(Wrappers.<WbsTreeContract>query().lambda()
                     .eq(WbsTreeContract::getPKeyId, pkeyId));
-            String sql="select * from m_wbs_tree_contract where p_id="+wbsInfo.getPId()+" and is_type_private_pid="+wbsInfo.getIsTypePrivatePid();
-            List<WbsTreeContract> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContract.class));
-            if(!list.isEmpty()){
-                for (WbsTreeContract wbsTreeContract : list) {
-                    String tabName = wbsTreeContract.getInitTableName();
-                    if (StringUtils.isNotEmpty(tabName)) {
-                        String delSql = "delete from " + tabName + " where p_key_id = " + wbsTreeContract.getPKeyId();
-                        jdbcTemplate.execute(delSql);
+            if(wbsInfo.getIsTypePrivatePid()!=null){
+                String sql="select * from m_wbs_tree_contract where p_id="+wbsInfo.getPId()+" and is_type_private_pid="+wbsInfo.getIsTypePrivatePid()+" and project_id="+wbsInfo.getProjectId();
+                List<WbsTreeContract> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContract.class));
+                if(!list.isEmpty()){
+                    for (WbsTreeContract wbsTreeContract : list) {
+                        String tabName = wbsTreeContract.getInitTableName();
+                        if (StringUtils.isNotEmpty(tabName)) {
+                            String delSql = "delete from " + tabName + " where p_key_id = " + wbsTreeContract.getPKeyId();
+                            jdbcTemplate.execute(delSql);
+                        }
                     }
                 }
             }
+
         }
         return R.data("成功!");
     }

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

@@ -27,13 +27,16 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.models.auth.In;
 import lombok.AllArgsConstructor;
 import javax.validation.Valid;
 
 import org.apache.commons.lang.StringUtils;
+import org.springblade.business.entity.InformationQuery;
 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.BeanUtil;
 import org.springblade.core.tool.utils.Func;
 import org.springblade.manager.entity.WbsTreeContract;
 import org.springblade.manager.service.IExcelTabService;
@@ -287,12 +290,26 @@ public class NodeBaseInfoController extends BladeController {
     @GetMapping("/getNodeData")
     @ApiOperationSupport(order = 9)
     @ApiOperation(value = "获取节点基础数据", notes = "获取节点基础数据")
-    public R getNodeData(@RequestParam Long pKeyId){
+    public R getNodeData(@RequestParam Long pKeyId, Integer classify){
         NodeBaseInfo nodeBaseInfo = nodeBaseInfoService.getBaseMapper().selectOne(new QueryWrapper<NodeBaseInfo>().eq("node_id", pKeyId));
+        String sql="select * from u_information_query where wbs_id="+pKeyId+" and classify="+classify+" and is_deleted=0 order by  create_time DESC";
+        List<InformationQuery> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(InformationQuery.class));
+        NodeBaseInfoVO baseInfoVO=new NodeBaseInfoVO();
+        BeanUtil.copy(nodeBaseInfo, baseInfoVO);
+        if(!query.isEmpty()){
+            InformationQuery informationQuery = query.get(0);
+            if(informationQuery!=null&&(informationQuery.getStatus()==1||informationQuery.getStatus()==2)){
+                baseInfoVO.setIsDisable(1);
+            }
+        }else {
+            baseInfoVO.setIsDisable(0);
+        }
         if(nodeBaseInfo!=null){
-            return R.data(nodeBaseInfo);
+            return R.data(baseInfoVO);
         }else {
-            return R.data(nodeBaseInfoService.getOrSaveNodeBaseInfo(pKeyId));
+            NodeBaseInfo info = nodeBaseInfoService.getOrSaveNodeBaseInfo(pKeyId);
+            BeanUtil.copy(info, NodeBaseInfoVO.class);
+            return R.data(baseInfoVO);
         }
     }
 

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

@@ -175,6 +175,7 @@ public class TrialSummaryClassificationConfigurationController extends BladeCont
                         .in(WbsTreePrivate::getParentId, ids).eq(WbsTreePrivate::getProjectId, projectId)
                         .eq(WbsTreePrivate::getWbsType, wbsType)
                         .eq(WbsTreePrivate::getType, 2)
+                        .isNull(WbsTreePrivate::getTrialTabContractId)
                         .eq(WbsTreePrivate::getWbsId, wbsId));
                 for (WbsTreePrivate wbsTreePrivate : wbsTreePrivates) {
                     SelectedTabVO vo = new SelectedTabVO();

+ 4 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java

@@ -565,6 +565,10 @@ public class WbsTreeContractController extends BladeController {
                 Elements tdElements2 = tr2.select("td");
                 for (int j = 0; j < tdElements1.size(); j++) {
                     Element td1 = tdElements1.get(j);
+                    // 检查td1是否包含dqid属性,如果包含则跳过
+                    if (td1.attr("dqid").length() > 0) {
+                        continue;
+                    }
                     Element td2 = tdElements2.get(j);
                     String keyName = getKeyNameFromChildElement(td1);
                     if (StringUtils.isNotEmpty(keyName)) {

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

@@ -1,5 +1,7 @@
 package org.springblade.manager.feign;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
 import org.apache.ibatis.annotations.Param;
 import org.springblade.archive.dto.JiLinQueryDto;
@@ -13,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 @RestController
 @AllArgsConstructor
@@ -166,10 +170,54 @@ public class ArchiveTreeContractImpl implements ArchiveTreeContractClient {
         return archiveTreeContractMapper.getOutNodesByOutIds(projectId,outIds);
     }
 
+//    @Override
+//    public void updateArchiveTreeContract(List<ArchiveTreeContract> archiveTreeContracts) {
+//        archiveTreeContractService.updateBatchById(archiveTreeContracts);
+//    }
+
     @Override
     public void updateArchiveTreeContract(List<ArchiveTreeContract> archiveTreeContracts) {
-        archiveTreeContractService.updateBatchById(archiveTreeContracts);
+        // 1. 提取传入合同列表中的所有ID(过滤空值)
+        List<Long> ids = archiveTreeContracts.stream()
+                .map(ArchiveTreeContract::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (ids.isEmpty()) {
+            return;
+        }
+
+        // 2. 根据ID批量查询现有记录
+        LambdaQueryWrapper<ArchiveTreeContract> wrapper = Wrappers.lambdaQuery();
+        wrapper.in(ArchiveTreeContract::getId, ids);
+        List<ArchiveTreeContract> existContracts = archiveTreeContractService.list(wrapper);
+
+        // 3. 将传入合同列表转换为Map<ID, 合同对象>便于查找
+        Map<Long, ArchiveTreeContract> externalContractMap = archiveTreeContracts.stream()
+                .collect(Collectors.toMap(
+                        ArchiveTreeContract::getId,
+                        Function.identity(),
+                        (existing, replacement) -> existing // 处理重复ID,保留第一个
+                ));
+
+        // 4. 准备更新列表 - 只更新ext_node_type字段
+        List<ArchiveTreeContract> updateList = new ArrayList<>();
+
+        for (ArchiveTreeContract local : existContracts) {
+            ArchiveTreeContract external = externalContractMap.get(local.getId());
+            if (external != null) {
+                // 只更新ext_node_type字段
+                local.setExtNodeType(external.getExtNodeType());
+                updateList.add(local);
+            }
+        }
+
+        // 5. 批量更新(建议分批处理,如每500条一次)
+        if (!updateList.isEmpty()) {
+            archiveTreeContractService.updateBatchById(updateList);
+        }
     }
+
     @Override
     public void addArchiveTreeContract(@RequestBody List<ArchiveTreeContract> archiveTreeContracts, @RequestParam Long rootId){
 //        archiveTreeContractService.saveBatch(archiveTreeContracts);

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

@@ -1057,17 +1057,17 @@
 
     </select>
     <select id="getSiblingWbsContract" resultType="org.springblade.manager.entity.WbsTreeContract">
-        select * from m_wbs_tree_contract where p_id= (select p_id from m_wbs_tree_contract where p_key_id=#{pKeyId}) and is_deleted=0;
+        select * from m_wbs_tree_contract where p_id= (select p_id from m_wbs_tree_contract where p_key_id=#{pKeyId}) and is_deleted=0 order by sort;
     </select>
     <select id="getWbsTreeContractsByPKeyIds" resultType="org.springblade.manager.entity.WbsTreeContract">
-        select p_key_id,parent_id,p_id,ancestors,ancestors_p_id from m_wbs_tree_contract where p_key_id in (
+        select p_key_id,parent_id,p_id,ancestors,ancestors_p_id,node_type from m_wbs_tree_contract where p_key_id in (
         <foreach collection="pKeyIds" item="pkeyId" separator=",">
             #{pkeyId}
         </foreach>
         ) and is_deleted=0
     </select>
     <select id="getChildWbsTreeContracts" resultType="org.springblade.manager.entity.WbsTreeContract">
-        select p_key_id,ancestors,ancestors_p_id from m_wbs_tree_contract where ancestors_p_id like #{pKeyId}  and is_deleted=0
+        select p_key_id,ancestors,ancestors_p_id from m_wbs_tree_contract where FIND_IN_SET(#{pKeyId}, ancestors_p_id)   and is_deleted=0
     </select>
 
 </mapper>

+ 3 - 5
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.xml

@@ -891,11 +891,9 @@
           and project_id = #{projectId} AND type in(2, 10)
     </delete>
 
-    <delete id="delTableById">
-        delete
-        from m_table_info
-        WHERE id = #{pKeyId}
-    </delete>
+    <update id="delTableById">
+        update m_table_info set is_deleted = 1 WHERE id = #{pKeyId}
+    </update>
     <update id="cancelLinkNodeTreeInfoByJL">
         UPDATE m_wbs_tree_private
         SET jler_tree_id = NULL

+ 59 - 51
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java

@@ -1115,7 +1115,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                     FormData ft = tec.formDataMap.get(k.getCode());
                     if (ft.getCoordsList().size() > 5 || (k.getEName().contains("实测值") && !k.getEName().contains("设计值") && !k.getEName().contains("合格率"))) {
                         tec.checkItems.add(ft);
-                    } else if (k.getEName().contains("检验日期")) {
+                    } else if (k.getEName().contains("检验日期")||k.getEName().contains("检验时间")) {
                         tec.checkDate.add(ft);
                     }
                 });
@@ -2604,7 +2604,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         String sql = "Select * from s_contract_meter_period where contract_id=" + contractId + " and is_deleted=0" + " order by start_date";
         List<ContractMeterPeriod> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(ContractMeterPeriod.class));
         //获取所有excel报表源数据
-        String sqlForExcel = "SELECT p.p_key_id,e.name,e.id,p.html_url,e.file_url,p.init_table_name as tabName FROM m_wbs_tree_private as p LEFT JOIN m_excel_tab as e on p.excel_id=e.id where p.wbs_type=3 AND p.is_deleted=0 AND p.type=2" + " and p.project_id=" + projectId;
+        String sqlForExcel = "SELECT p.p_key_id,e.name,e.id,p.html_url,e.file_url FROM m_wbs_tree_private as p LEFT JOIN m_excel_tab as e on p.excel_id=e.id where p.wbs_type=3 AND p.is_deleted=0 AND p.type=2" + " and p.project_id=" + projectId;
         List<ExcelTabVo1> excelTabs = jdbcTemplate.query(sqlForExcel, new BeanPropertyRowMapper<>(ExcelTabVo1.class));
         ArrayList<ReportResult> reportResults = new ArrayList<>();
         //中期支付报表封面
@@ -2650,25 +2650,25 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         String htmlUrl6 = excel6.get().getHtmlUrl();
         Long pKeyId6 = excel6.get().getPKeyId();
         // 获取中期支付报表封面的pdfurl
-        ReportResult CoverOfMidtermPaymentReportPDF = getCoverOfMidtermPaymentReportPDF(url, contractId, periodId, htmlUrl, projectName, list, pKeyId, excel.get().getTabName());
+        ReportResult CoverOfMidtermPaymentReportPDF = getCoverOfMidtermPaymentReportPDF(url, contractId, periodId, htmlUrl, projectName, list, pKeyId);
 
         //获取中间支付审核表的pdfurl
-        ReportResult intermediatePaymentPDF = getIntermediatePaymentPDF(url1, contractId, periodId, list, redisId, htmlUrl1, blReserveFundsRatioNew, projectName, changeMoneyNew, pKeyId1, excel1.get().getTabName());
+        ReportResult intermediatePaymentPDF = getIntermediatePaymentPDF(url1, contractId, periodId, list, redisId, htmlUrl1, blReserveFundsRatioNew, projectName, changeMoneyNew, pKeyId1,reportId);
 
         //获取补助款申请支付审核表pdfUrl
-        ReportResult subsidyApplicationPaymentReviewPDF = getSubsidyApplicationPaymentReviewPDF(url2, contractId, periodId, list, htmlUrl2, blReserveFundsRatioNew, projectName, projectId, reportId, pKeyId2,excel2.get().getTabName());
+        ReportResult subsidyApplicationPaymentReviewPDF = getSubsidyApplicationPaymentReviewPDF(url2, contractId, periodId, list, htmlUrl2, blReserveFundsRatioNew, projectName, projectId, reportId, pKeyId2);
 
         //获取中间计量支付证书pdfUrl
-        ReportResult intermediateMeasurementPaymentCertificatePDF = getIntermediateMeasurementPaymentCertificatePDF(url3, contractId, periodId, list, htmlUrl3, blReserveFundsRatioNew, projectName, pKeyId3,excel3.get().getTabName());
+        ReportResult intermediateMeasurementPaymentCertificatePDF = getIntermediateMeasurementPaymentCertificatePDF(url3, contractId, periodId, list, htmlUrl3, blReserveFundsRatioNew, projectName, pKeyId3);
 
         //获取清单支付报表PDF
-        ReportResult inventoryPayReportPDF = getInventoryPayReportPDF(url6, contractId, periodId, projectId, list, redisId, htmlUrl6, pKeyId6, excel6.get().getTabName());
+        ReportResult inventoryPayReportPDF = getInventoryPayReportPDF(url6, contractId, periodId, projectId, list, redisId, htmlUrl6, pKeyId6);
 
         //获取工程支付月报pdfUrl
-        ReportResult monthlyReportPDF = getMonthlyReportPDF(url5, reportId, contractId, periodId, projectId, list, redisId, htmlUrl5, pKeyId5, excel5.get().getTabName());
+        ReportResult monthlyReportPDF = getMonthlyReportPDF(url5, reportId, contractId, periodId, projectId, list, redisId, htmlUrl5, pKeyId5);
 
         //获取中间支付申请表pdfUrl
-        ReportResult intermediateApplyPDF = getIntermediateApplyPDF(url4, periodId, projectId, htmlUrl4, projectName, contractId, pKeyId4, excel4.get().getTabName());
+        ReportResult intermediateApplyPDF = getIntermediateApplyPDF(url4, periodId, projectId, htmlUrl4, projectName, contractId, pKeyId4);
 
         reportResults.add(CoverOfMidtermPaymentReportPDF);
         reportResults.add(intermediatePaymentPDF);
@@ -2686,7 +2686,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     /**
      * 中期支付报表封面
      */
-    private ReportResult getCoverOfMidtermPaymentReportPDF(String url, Long contractId, Long periodId, String htmlUrl, String projectName, List<ContractMeterPeriod> list, Long pkeyId, String tabName) {
+    private ReportResult getCoverOfMidtermPaymentReportPDF(String url, Long contractId, Long periodId, String htmlUrl, String projectName, List<ContractMeterPeriod> list, Long pkeyId) {
 
         //获取本期计量期
         String sqlForMeterPeriodById = "SELECT id,period_number,start_date,end_date FROM s_contract_meter_period WHERE id=" + periodId + " and is_deleted = 0 ";
@@ -2737,7 +2737,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                 Cell c10 = getCellByAddress(sheet, "C10");
                 c10.setCellValue(contractInfo.getSupervisionUnitName());
             }
-            dianqian(htmlUrl, sheet, workbook, tabName);
+            dianqian(htmlUrl, sheet, workbook);
             if (!periodId.equals(1867838908899852290L)) {
                 dianqianTime(htmlUrl, sheet, workbook, pkeyId, periodId, contractId);
             }
@@ -2773,7 +2773,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     }
 
     /*设置电签ID*/
-    public void dianqian(String htmlUrl, Sheet sheet, Workbook workbook, String tabName) {
+    public void dianqian(String htmlUrl, Sheet sheet, Workbook workbook) {
         try {
             InputStream inputStreamByUrl = FileUtils.getInputStreamByUrl(htmlUrl);
             String htmlString = IoUtil.readToString(inputStreamByUrl);
@@ -2781,30 +2781,8 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             Element table = doc.select("table").first();
             // 组装电签设置
             Elements dqids = table.getElementsByAttribute("dqid");
-            // 电签组装2
-            String dqSql = "select e_key,GROUP_CONCAT(DISTINCT concat('*✹',id)) ids from u_sign_key_role_info where tab_en_name='" + tabName + "' GROUP BY e_key";
-            List<Map<String, Object>> mapList = jdbcTemplate.queryForList(dqSql);
-            if(mapList!=null && mapList.size()>0){
-                for(Map<String, Object> map : mapList) {
-                    Elements elementsBy = table.getElementsByAttributeValueStarting("keyname", map.get("e_key") + "_");
-                    if(elementsBy!=null && elementsBy.size()>0){
-                        for(Element element : elementsBy){
-                            String dqIds = (String) map.get("ids");
-                            dqIds = dqIds.replace(",","");
-                            dqIds = dqIds.substring(1);
-                            element.attr("sign_type", dqIds);
-                            dqids.add(element);
-                        }
-                    }
-                }
-            }
             for (Element element : dqids) {
-                String dqid="";
-                if(element.hasAttr("sign_type")){
-                    dqid = element.attr("sign_type");
-                }else{
-                    dqid = element.attr("dqid");
-                }
+                String dqid = element.attr("dqid");
 
                 int x1 = 0;
                 int y1 = 0;
@@ -2850,6 +2828,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
         }
     }
 
+
     /*设置电签时间*/
     private void dianqianTime(String htmlUrl, Sheet sheet, Workbook workbook, Long pkeyId, Long perId, Long contractId) {
         String timeSql = "SELECT DISTINCT a.time_col_key,DATE_FORMAT(c.create_time, '%Y年%m月%d日') as create_time from m_textdict_info a ,m_project_assignment_user b,u_task_parallel c ,u_task d  where a.sig_role_id=b.role_id and b.user_id=c.task_user and c.process_instance_id=d.process_instance_id and  b.contract_id=" + contractId + " and LENGTH(a.time_col_key)>=2 " +
@@ -2898,7 +2877,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     /**
      * 中间计量支付证书获取PDFurl
      */
-    private ReportResult getIntermediateMeasurementPaymentCertificatePDF(String url3, Long contractId, Long periodId, List<ContractMeterPeriod> list, String htmlUrl, BigDecimal blReserveFundsRatioNew, String projectName, Long pkeyId, String tabName) {
+    private ReportResult getIntermediateMeasurementPaymentCertificatePDF(String url3, Long contractId, Long periodId, List<ContractMeterPeriod> list, String htmlUrl, BigDecimal blReserveFundsRatioNew, String projectName, Long pkeyId) {
         //判断当前是否是第一期
         Boolean isOnePeriod = false;
         ContractMeterPeriod contractMeterPeriod = list.get(0);
@@ -2979,7 +2958,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             Cell c3 = getCellByAddress(sheet, "C3");
             c3.setCellValue(projectName);
 
-            dianqian(htmlUrl, sheet, workbook, tabName);
+            dianqian(htmlUrl, sheet, workbook);
             if (!periodId.equals(1867838908899852290L)) {
                 dianqianTime(htmlUrl, sheet, workbook, pkeyId, periodId, contractId);
             }
@@ -3025,7 +3004,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     /**
      * 补助款申请支付审核表获取PDFurl
      */
-    private ReportResult getSubsidyApplicationPaymentReviewPDF(String url2, Long contractId, Long periodId, List<ContractMeterPeriod> list, String htmlUrl, BigDecimal blReserveFundsRatioNew, String projectName, Long projectId, Long reportId, Long pkeyId, String tabName) {
+    private ReportResult getSubsidyApplicationPaymentReviewPDF(String url2, Long contractId, Long periodId, List<ContractMeterPeriod> list, String htmlUrl, BigDecimal blReserveFundsRatioNew, String projectName, Long projectId, Long reportId, Long pkeyId) {
         //判断当前是否是只有1期计量
         Boolean isOnePeriod = false;
         ContractMeterPeriod contractMeterPeriod = list.get(0);
@@ -3299,7 +3278,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             Cell a2 = getCellByAddress(sheet, "A2");
             a2.setCellValue(projectName);
             // 电签
-            dianqian(htmlUrl, sheet, workbook, tabName);
+            dianqian(htmlUrl, sheet, workbook);
             if (!periodId.equals(1867838908899852290L)) {
                 dianqianTime(htmlUrl, sheet, workbook, pkeyId, periodId, contractId);
             }
@@ -3339,7 +3318,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     /**
      * 中间支付审核表获取PDFurl
      */
-    public ReportResult getIntermediatePaymentPDF(String url, Long contractId, Long periodId, List<ContractMeterPeriod> list, Long redisId, String htmlUrl, BigDecimal blReserveFundsRatioNew, String projectName, BigDecimal changeMoneyNew, Long pkeyId, String tabName) {
+    public ReportResult getIntermediatePaymentPDF(String url, Long contractId, Long periodId, List<ContractMeterPeriod> list, Long redisId, String htmlUrl, BigDecimal blReserveFundsRatioNew, String projectName, BigDecimal changeMoneyNew, Long pkeyId,Long reportId) {
         //判断当前是否是只有1期计量
         Boolean isOnePeriod = false;
         ContractMeterPeriod contractMeterPeriod1 = list.get(0);
@@ -3437,9 +3416,30 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             changeMoneySum = changeMoneySum.setScale(0, RoundingMode.HALF_UP);*/
             BigDecimal changemoneySum = changeMoneyNew.subtract(totalMoney).subtract(lastEndChangeMoney).setScale(0, RoundingMode.HALF_UP);
             Cell e5 = getCellByAddress(sheet, "E5");
+            String sql4 = "Select * From s_interim_pay_certificate_item where certificate_id=" + reportId + " and chapter_seq='本期变更' and is_deleted=0 ";
+            List<InterimPayCertificateItem> lists = jdbcTemplate.query(sql4, new BeanPropertyRowMapper<>(InterimPayCertificateItem.class));
+            if (lists.isEmpty()) {
+                MonthlyReportVo vo=new MonthlyReportVo();
+                vo.setChapterSeq("本期变更");
+                vo.setBeforeEndMoney(lastEndChangeMoney);
+                vo.setNowMoney(changemoneySum);
+                vo.setNowEndMoney(lastEndChangeMoney.add(changemoneySum));
+                //第一次需要新增支付项
+                interimPayCertificateItemClient.addInterimPayCertificateItem1(reportId, vo);
+            } else {
+                InterimPayCertificateItem item = lists.get(0);
+                //第二次后就是更新
+                changemoneySum=stringToBigDecimal(item.getCurrentPeriodPay());
+                item.setCurrentPeriodPay(changemoneySum.toString());
+                item.setPreviousPeriodEndPay(lastEndChangeMoney.toString());
+                item.setCurrentPeriodEndPay(lastEndChangeMoney.add(changemoneySum).toString());
+                interimPayCertificateItemClient.updateInterimPayCertificateItem1(item);
+            }
             if(periodId==1890367903054798850L){
                 e5.setCellValue("515977");
-            }else {
+            } else if (periodId==1963802568671502338L) {
+                e5.setCellValue("1496852");
+            } else {
                 e5.setCellValue(changemoneySum.toString());
             }
 
@@ -3611,7 +3611,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             //顶部项目名称
             Cell a1 = getCellByAddress(sheet, "A1");
             a1.setCellValue(projectName);
-            dianqian(htmlUrl, sheet, workbook, tabName);
+            dianqian(htmlUrl, sheet, workbook);
             if (!periodId.equals(1867838908899852290L)) {
                 dianqianTime(htmlUrl, sheet, workbook, pkeyId, periodId, contractId);
             }
@@ -3661,6 +3661,17 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
     }
 
 
+        public  BigDecimal stringToBigDecimal(String value) {
+            if (StringUtils.isEmpty(value)) {
+                return BigDecimal.ZERO;
+            }
+            try {
+                return new BigDecimal(value.trim());
+            } catch (NumberFormatException e) {
+                return BigDecimal.ZERO;
+            }
+        }
+
 
     /**
      * 获取上期末的数据
@@ -3760,10 +3771,9 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
      * 中间支付申请表获取pdfUrl
      *
      * @param url
-     * @param tabName
      * @return
      */
-    public ReportResult getIntermediateApplyPDF(String url, Long periodId, Long projectId, String htmlUrl, String projectName, Long contractId, Long pkeyId, String tabName) {
+    public ReportResult getIntermediateApplyPDF(String url, Long periodId, Long projectId, String htmlUrl, String projectName, Long contractId, Long pkeyId) {
         InputStream modInput = null;
         FileInputStream excelFileInput = null;
         FileOutputStream outputStream = null;
@@ -3773,7 +3783,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
             modInput = CommonUtil.getOSSInputStream(url);
             workbook = WorkbookFactory.create(modInput);
             Sheet sheet = workbook.getSheetAt(0);
-            dianqian(htmlUrl, sheet, workbook, tabName);
+            dianqian(htmlUrl, sheet, workbook);
             if (!periodId.equals(1867838908899852290L)) {
                 dianqianTime(htmlUrl, sheet, workbook, pkeyId, periodId, contractId);
             }
@@ -3840,10 +3850,9 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
      * 工程支付月报获取pdfUrl
      *
      * @param url
-     * @param tabName
      * @return
      */
-    public ReportResult getMonthlyReportPDF(String url, Long reportId, Long contractId, Long periodId, Long projectId, List<ContractMeterPeriod> list, Long redisId, String htmlUrl, Long pkeyId, String tabName) throws IllegalAccessException {
+    public ReportResult getMonthlyReportPDF(String url, Long reportId, Long contractId, Long periodId, Long projectId, List<ContractMeterPeriod> list, Long redisId, String htmlUrl, Long pkeyId) throws IllegalAccessException {
         //获取本期计量期
         String sqlForMeterPeriodById = "SELECT id,period_number,start_date,end_date FROM s_contract_meter_period WHERE id=" + periodId + " and is_deleted = 0 ";
         ContractMeterPeriod contractMeterPeriodNow = jdbcTemplate.queryForObject(sqlForMeterPeriodById, new BeanPropertyRowMapper<>(ContractMeterPeriod.class));
@@ -4301,7 +4310,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                     m_3.setCellValue(formattedDate);
                 }
             }
-            dianqian(htmlUrl, sheet, workbook, tabName);
+            dianqian(htmlUrl, sheet, workbook);
             if (!periodId.equals(1867838908899852290L)) {
                 dianqianTime(htmlUrl, sheet, workbook, pkeyId, periodId, contractId);
             }
@@ -4337,11 +4346,10 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
      * 清单支付报表获取PDF
      *
      * @param url
-     * @param tabName
      * @return
      */
 
-    public ReportResult getInventoryPayReportPDF(String url, Long contractId, Long periodId, Long projectId, List<ContractMeterPeriod> list, Long redisId, String htmlUrl, Long pkeyId, String tabName) throws FileNotFoundException, IllegalAccessException {
+    public ReportResult getInventoryPayReportPDF(String url, Long contractId, Long periodId, Long projectId, List<ContractMeterPeriod> list, Long redisId, String htmlUrl, Long pkeyId) throws FileNotFoundException, IllegalAccessException {
         MiddleMeterApply middleMeterApply = new MiddleMeterApply();
         middleMeterApply.setContractId(contractId);
         middleMeterApply.setContractPeriodId(periodId);
@@ -4729,7 +4737,7 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
                             l4.setCellStyle(cellStyle);
                             l4.setCellValue(contractInfo.getSupervisionUnitName());
                         }
-                        dianqian(htmlUrl, sheet, workbook, tabName);
+                        dianqian(htmlUrl, sheet, workbook);
                         if (!periodId.equals(1867838908899852290L)) {
                             dianqianTime(htmlUrl, sheet, workbook, pkeyId, periodId, contractId);
                         }

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

@@ -4696,7 +4696,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         List<WbsTreeContract> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContract.class));
 
         InputStream templateStream = new FileInputStream(new File(templatePath));
-        org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(templateStream);
+        Workbook workbook = WorkbookFactory.create(templateStream);
         Sheet templateSheet = workbook.getSheetAt(0);
 
         // 移除默认的Sheet1
@@ -4918,6 +4918,33 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
         List<WbsTreeContract> list= wbsTreeContractMapper.getWbsTreeContractsByPKeyIds(dto.getLeftPkeyIds());
         WbsTreeContract moveFatherNode = this.getById(list.get(0).getPId());
         WbsTreeContract fatherContract = this.getById(dto.getRightPkeyId());
+        Integer leftNodeType = 0;
+        Integer rightNodeType =0;
+        if(list.get(0).getNodeType()!=null){
+            if(list.get(0).getNodeType()==1){
+                leftNodeType=1;
+            } else if (list.get(0).getNodeType()==18) {
+                leftNodeType=2;
+            }else {
+                leftNodeType=list.get(0).getNodeType()+1;
+            }
+        }
+        if(fatherContract.getNodeType()!=null){
+            if(fatherContract.getNodeType()==1){
+                rightNodeType=1;
+            } else if (fatherContract.getNodeType()==18) {
+                rightNodeType=2;
+            }else {
+                rightNodeType=fatherContract.getNodeType()+1;
+            }
+        }
+        if(leftNodeType==rightNodeType){
+            throw new ServiceException("请勿同级节点相互移动");
+        }
+        if(leftNodeType<rightNodeType){
+            throw new ServiceException("请勿向比自己层级低的节点移动");
+        }
+
 
         String sql="SELECT * from m_formula_data_block WHERE sw_id = "+moveFatherNode.getId()+" and contract_id ="+moveFatherNode.getContractId()+" and  type =0";
         List<FormulaDataBlock> formulaDataBlocks1 = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(FormulaDataBlock.class));
@@ -5660,7 +5687,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
     }
 
      //保存Workbook到本地文件(本地测试放开)
-    private void saveWorkbookToFile(org.apache.poi.ss.usermodel.Workbook workbook, String filePath) {
+    private void saveWorkbookToFile(Workbook workbook, String filePath) {
         try {
             File file = new File(filePath);
             File parentDir = file.getParentFile();

+ 3 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeSynchronousRecordServiceImpl.java

@@ -259,6 +259,9 @@ public class WbsTreeSynchronousRecordServiceImpl extends ServiceImpl<WbsTreeSync
     public void syncInit() {
 //         本地环境跳过执行(可添加日志输出)
         if (!schedulerEnabled) return;
+        if(!SystemUtils.isLinux()){
+            return;
+        }
 
         List<WbsTreeSynchronousRecord> wbsTreeSynchronousRecords = baseMapper.selectList(new QueryWrapper<WbsTreeSynchronousRecord>().lambda()
                 .in(WbsTreeSynchronousRecord::getStatus, 0, 1)

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

@@ -4769,7 +4769,7 @@ public class TaskController extends BladeController {
                 String UPSqlJL = " update u_task_parallel a set a.e_visa_status=1,e_visa_content='电签成功',initiative=2 where sort in( SELECT a.fixed_flow_branch_sort from u_task b,u_fixed_flow_link a where b.`status` in(1,2) and b.form_data_id='" + report.getPeriodId() + "' and a.fixed_flow_id=b.fixed_flow_id and a.flow_task_type=2 ) and a.process_instance_id in(SELECT process_instance_id from u_task b where b.`status` in(1,2) and b.form_data_id='" + report.getPeriodId() + "')";
                 String DeSql = "delete from u_task_batch where task_parallel_id in(select b.process_instance_id from u_task b where b.`status` in(1,2) and b.form_data_id=" + report.getPeriodId() + ")";
                 //删除以前存在的电签
-                String sql = "insert into u_task_batch(id,task_parallel_id,json_data,create_user,create_dept,create_time,update_user,update_time,status,is_deleted,nick_name,sign_format,sign_type) " + " SELECT a.id,a.process_instance_id,json_object('approvalFileList',json_array(),'approvalType',b.approval_type,'comment','','flag','OK','formDataId',b.form_data_id,'parallelProcessInstanceId',a.parallel_process_instance_id,'pass',true,'taskId',b.id) as  json_data,a.task_user,a.create_dept,a.create_time,a.update_user,a.update_time,1 as status,0 as is_deleted,a.task_user_name as nick_name ,1 as sign_format,1 as sign_type from u_task_parallel a,u_task b,u_fixed_flow_link c where b.`status` in(1,2) and  a.process_instance_id=b.process_instance_id and c.flow_task_type!=2 and a.sort = c.fixed_flow_branch_sort and c.fixed_flow_id=b.fixed_flow_id and b.form_data_id= '" + report.getPeriodId() + "'";
+                String sql = "insert into u_task_batch(id,task_parallel_id,json_data,create_user,create_dept,create_time,update_user,update_time,status,is_deleted,nick_name,sign_format,sign_type) " + " SELECT a.id,a.process_instance_id,json_object('approvalFileList',json_array(),'approvalType',b.approval_type,'comment','','flag','OK','formDataId',b.form_data_id,'parallelProcessInstanceId',a.parallel_process_instance_id,'pass',true,'taskId',b.id) as  json_data,a.task_user,a.create_dept,a.create_time,a.update_user,SYSDATE(),1 as status,0 as is_deleted,a.task_user_name as nick_name ,1 as sign_format,1 as sign_type from u_task_parallel a,u_task b,u_fixed_flow_link c where b.`status` in(1,2) and  a.process_instance_id=b.process_instance_id and c.flow_task_type!=2 and a.sort = c.fixed_flow_branch_sort and c.fixed_flow_id=b.fixed_flow_id and b.form_data_id= '" + report.getPeriodId() + "'";
 
                 jdbcTemplate.execute(DeSql);
                 jdbcTemplate.execute(UPSql);

+ 4 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/service/IInterimPayCertificateItemService.java

@@ -19,4 +19,8 @@ public interface IInterimPayCertificateItemService extends BaseService<InterimPa
     void sendJLSmg(Long taskId);
 
     void saveOrUpdateBatchList(List<InterimPayCertificateItem> items,Long reportId);
+
+    void addInterimPayCertificateItem1(Long reportId, MonthlyReportVo vo);
+
+    void updateInterimPayCertificateItem1(InterimPayCertificateItem item);
 }

+ 10 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/InterimPayCertificateItemClientImpl.java

@@ -21,4 +21,14 @@ public class InterimPayCertificateItemClientImpl implements InterimPayCertificat
     public void updateInterimPayCertificateItem(List<InterimPayCertificateItem> list4) {
         iInterimPayCertificateItemService.updateInterimPayCertificateItem(list4);
     }
+
+    @Override
+    public void addInterimPayCertificateItem1(Long reportId, MonthlyReportVo vo) {
+        iInterimPayCertificateItemService.addInterimPayCertificateItem1(reportId,vo);
+    }
+
+    @Override
+    public void updateInterimPayCertificateItem1(InterimPayCertificateItem item) {
+        iInterimPayCertificateItemService.updateInterimPayCertificateItem1(item);
+    }
 }

+ 21 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/InterimPayCertificateItemServiceImpl.java

@@ -136,4 +136,25 @@ public class InterimPayCertificateItemServiceImpl extends BaseServiceImpl<Interi
             saveBatch(items);
         }
     }
+
+    @Override
+    public void addInterimPayCertificateItem1(Long reportId, MonthlyReportVo vo) {
+                InterimPayCertificateItem item = new InterimPayCertificateItem();
+                item.setCertificateId(reportId);
+                item.setSort(100);
+                item.setFormName(vo.getChapterSeq());
+                item.setChapterSeq(vo.getFormName());
+                item.setContractAmount(vo.getCurrentMeterMoney() == null ? null : vo.getCurrentMeterMoney().toString());
+                item.setRevisedTotal(vo.getChangeMeterMoney() == null ? null : vo.getChangeMeterMoney().toString());
+                item.setRevisedAmount(vo.getAfterCurrentMeterMoney() == null ? null : vo.getAfterCurrentMeterMoney().toString());
+                item.setCurrentPeriodEndPay(vo.getNowEndMoney() == null ? null : vo.getNowEndMoney().toString());
+                item.setPreviousPeriodEndPay(vo.getBeforeEndMoney() == null ? null : vo.getBeforeEndMoney().toString());
+                item.setCurrentPeriodPay(vo.getNowMoney() == null ? null : vo.getNowMoney().toString());
+                save(item);
+    }
+
+    @Override
+    public void updateInterimPayCertificateItem1(InterimPayCertificateItem item) {
+        updateById(item);
+    }
 }