Browse Source

Merge remote-tracking branch 'origin/master' into master

yangyj 1 year ago
parent
commit
01061b8812
49 changed files with 1831 additions and 242 deletions
  1. 2 2
      blade-common/src/main/java/org/springblade/common/utils/AsyncConfigurer.java
  2. 111 0
      blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java
  3. 82 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/LargeFile.java
  4. 18 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/MultipartFileParam.java
  5. 6 0
      blade-ops/blade-resource/pom.xml
  6. 454 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/endpoint/LargeFileEndpoint.java
  7. 33 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/mapper/LargeFileMapper.java
  8. 33 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/mapper/LargeFileMapper.xml
  9. 30 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/service/ILargeFileService.java
  10. 49 0
      blade-ops/blade-resource/src/main/java/org/springblade/resource/service/impl/LargeFileServiceImpl.java
  11. 3 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ArchiveFileClient.java
  12. 14 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ArchiveTreeContract.java
  13. 12 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/feign/ArchiveTreeContractClient.java
  14. 1 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/config/sqliteConfig.java
  15. 3 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveFileAutoController.java
  16. 3 3
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveOfflineVersionInfoController.java
  17. 1 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveOfflineVersionInfoMapper.xml
  18. 1 1
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchiveOfflineVersionInfoService.java
  19. 155 7
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveOfflineVersionInfoServiceImpl.java
  20. 183 36
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  21. 9 1
      blade-service/blade-business/src/main/java/org/springblade/business/controller/EVisaTaskCheckController.java
  22. 7 0
      blade-service/blade-business/src/main/java/org/springblade/business/controller/ImageClassificationFileController.java
  23. 8 0
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ArchiveFileClientImpl.java
  24. 2 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.java
  25. 5 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml
  26. 4 4
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/InformationQueryMapper.xml
  27. 3 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/IImageClassificationFileService.java
  28. 136 86
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ImageClassificationFileServiceImpl.java
  29. 37 32
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java
  30. 2 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/FormulaController.java
  31. 11 9
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TextdictInfoController.java
  32. 16 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ArchiveTreeContractImpl.java
  33. 4 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.java
  34. 21 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.xml
  35. 2 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractInfoMapper.xml
  36. 2 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.xml
  37. 2 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.xml
  38. 3 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IExcelTabService.java
  39. 15 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArTreeContractInitServiceImpl.java
  40. 94 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveAutoRuleSyncImpl.java
  41. 5 6
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractServiceImpl.java
  42. 5 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractSyncImpl.java
  43. 8 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeServiceImpl.java
  44. 101 4
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ExcelTabServiceImpl.java
  45. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java
  46. 25 7
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreePrivateServiceImpl.java
  47. 27 11
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeServiceImpl.java
  48. 3 22
      blade-service/blade-manager/src/main/java/org/springblade/manager/utils/FileUtils.java
  49. 79 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/utils/ForestNodeMergerEx.java

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

@@ -15,7 +15,7 @@ public class AsyncConfigurer {
     /**
      * cpu 核心数量
      */
-    public static final int cpuNum = Runtime.getRuntime().availableProcessors();
+    public static final int cpuNum = 1;//Runtime.getRuntime().availableProcessors();
 
     /**
      * 线程池配置
@@ -25,7 +25,7 @@ public class AsyncConfigurer {
     @Bean("taskExecutor1")
     public ThreadPoolExecutor getAsyncExecutor() {
         return new ThreadPoolMonitor(cpuNum
-                , cpuNum * 2
+                , 3
                 , 60
                 , TimeUnit.SECONDS
                 , new LinkedBlockingQueue<>(2000)

+ 111 - 0
blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java

@@ -8,11 +8,14 @@ import java.io.*;
 import java.math.BigDecimal;
 import java.net.HttpURLConnection;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 /**
  * 通用工具类
@@ -206,5 +209,113 @@ public class CommonUtil {
                 .collect(Collectors.toList());
     }
 
+    /**
+     * 流写入文件
+     *
+     * @param inputStream 文件输入流
+     * @param file        输出文件
+     */
+    public static void inputStreamToFile(InputStream inputStream, File file) {
+        try {
+            OutputStream os = new FileOutputStream(file);
+            int bytesRead = 0;
+            byte[] buffer = new byte[8192];
+            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
+                os.write(buffer, 0, bytesRead);
+            }
+            os.close();
+            inputStream.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 删除文件夹下所有文件
+     * @param path
+     * @return
+     */
+    public static boolean deleteDir(String path) {
+        File file = new File(path);
+        if (!file.exists()) {//判断是否待删除目录是否存在
+            System.err.println("The dir are not exists!");
+            return false;
+        }
+
+        String[] content = file.list();//取得当前目录下所有文件和文件夹
+        for (String name : content) {
+            File temp = new File(path, name);
+            if (temp.isDirectory()) {//判断是否是目录
+                deleteDir(temp.getAbsolutePath());//递归调用,删除目录里的内容
+                temp.delete();//删除空目录
+            } else {
+                if (!temp.delete()) {//直接删除文件
+                    System.err.println("Failed to delete " + name);
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 压缩指定路径下的文件夹
+     * @param filesPath
+     * @throws Exception
+     */
+    public static void packageZip(String filesPath) throws Exception {
+        // 要被压缩的文件夹
+        File file = new File(filesPath);   //需要压缩的文件夹
+        File zipFile = new File(filesPath + ".zip");  //放于和需要压缩的文件夹同级目录
+        ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
+        isDirectory(file, zipOut, "", true);   //判断是否为文件夹
+        zipOut.close();
+    }
+
+    public static void isDirectory(File file, ZipOutputStream zipOutputStream, String filePath, boolean flag) throws IOException {
+        //判断是否为问加减
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();  //获取该文件夹下所有文件(包含文件夹)
+            filePath = flag == true ? file.getName() : filePath + File.separator + file.getName();   //首次为选中的文件夹,即根目录,之后递归实现拼接目录
+            for (int i = 0; i < files.length; ++i) {
+                //判断子文件是否为文件夹
+                if (files[i].isDirectory()) {
+                    //进入递归,flag置false 即当前文件夹下仍包含文件夹
+                    isDirectory(files[i], zipOutputStream, filePath, false);
+                } else {
+                    //不为文件夹则进行压缩
+                    InputStream input = new FileInputStream(files[i]);
+                    zipOutputStream.putNextEntry(new ZipEntry(filePath + File.separator + files[i].getName()));
+                    int temp = 0;
+                    while ((temp = input.read()) != -1) {
+                        zipOutputStream.write(temp);
+                    }
+                    input.close();
+                }
+            }
+        } else {
+            //将子文件夹下的文件进行压缩
+            InputStream input = new FileInputStream(file);
+            zipOutputStream.putNextEntry(new ZipEntry(file.getPath()));
+            int temp = 0;
+            while ((temp = input.read()) != -1) {
+                zipOutputStream.write(temp);
+            }
+            input.close();
+        }
+    }
+
+    /**
+     * @param urlStr
+     * @return 返回Url资源大小
+     * @throws IOException
+     */
+    public static long getResourceLength(String urlStr) throws IOException {
+        URL url = new URL(urlStr);
+        URLConnection urlConnection = url.openConnection();
+        urlConnection.connect();
+        //返回响应报文头字段Content-Length的值
+        return urlConnection.getContentLength();
+    }
+
 
 }

+ 82 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/LargeFile.java

@@ -0,0 +1,82 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 附件表实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_large_file")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "LargeFile对象", description = "分片表")
+public class LargeFile extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 相对路径
+	 */
+	private String path;
+
+	/**
+	 * 文件名
+	 */
+	private String name;
+
+	/**
+	 * 后缀
+	 */
+	private String suffix;
+
+	/**
+	 * 实际分片 大小|字节B
+	 */
+	private Integer size;
+
+
+
+	/**
+	 * 已上传分片
+	 */
+	private Integer shardIndex;
+
+	/**
+	 * 理论 分片大小|B
+	 */
+	private Integer shardSize;
+
+	/**
+	 * 分片总数
+	 */
+	private Integer shardTotal;
+
+	/**
+	 * 文件标识(md5算法)
+	 */
+	private String fileKey;
+
+
+}

+ 18 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/MultipartFileParam.java

@@ -0,0 +1,18 @@
+package org.springblade.resource.vo;
+
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+@Data
+public class MultipartFileParam {
+    private String identifier;//文件md5值
+    private Integer chunkNumber;//当前分片数量
+    private Integer chunkSize;//分片大小
+    private String currentChunkSize;//当前分片大小
+    private String filename;//文件名称
+    private String relativePath;//相对路径
+    private Integer totalChunks;//总分片数
+    private String totalSize;//文件大小
+    private String objectType;//类型
+    private MultipartFile file;
+}

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

@@ -15,6 +15,12 @@
     <packaging>jar</packaging>
 
     <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-system-api</artifactId>
+            <version>2.9.1.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.springblade</groupId>
             <artifactId>blade-common</artifactId>

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

@@ -0,0 +1,454 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.endpoint;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.apache.commons.io.FileUtils;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.springblade.common.constant.CommonConstant;
+import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.oss.model.BladeFile;
+import org.springblade.core.oss.model.OssFile;
+import org.springblade.core.secure.annotation.PreAuth;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.constant.RoleConstant;
+import org.springblade.core.tool.utils.FileUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.resource.builder.oss.OssBuilder;
+import org.springblade.resource.entity.Attach;
+import org.springblade.resource.entity.LargeFile;
+import org.springblade.resource.feign.CommonFileClient;
+import org.springblade.resource.service.IAttachService;
+import org.springblade.resource.service.ILargeFileService;
+import org.springblade.resource.service.IOssService;
+import org.springblade.resource.vo.MultipartFileParam;
+import org.springblade.resource.vo.NewBladeFile;
+import org.springblade.system.cache.ParamCache;
+import org.springframework.beans.BeanUtils;
+import org.springframework.util.DigestUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.lang.reflect.Method;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 大文件存储端点
+ *
+ * @author Chill
+ */
+@NonDS
+@RestController
+@AllArgsConstructor
+@RequestMapping("/largeFile/endpoint")
+@Api(value = "大文件存储端点", tags = "大文件存储端点")
+public class LargeFileEndpoint {
+
+	/**
+	 * 对象存储构建类
+	 */
+	private final OssBuilder ossBuilder;
+
+	private final ILargeFileService iLargeFileService;
+
+	private final Lock lock = new ReentrantLock();
+	/**
+	 * 附件表服务
+	 */
+	private final IAttachService attachService;
+
+	private final CommonFileClient commonFileClient;
+
+	/**
+	 * 创建存储桶
+	 *
+	 * @param bucketName 存储桶名称
+	 * @return Bucket
+	 */
+	@SneakyThrows
+	@PostMapping("/make-bucket")
+	@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+	public R makeBucket(@RequestParam String bucketName) {
+		ossBuilder.template().makeBucket(bucketName);
+		return R.success("创建成功");
+	}
+
+	/**
+	 * 创建存储桶
+	 *
+	 * @param bucketName 存储桶名称
+	 * @return R
+	 */
+	@SneakyThrows
+	@PostMapping("/remove-bucket")
+	@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
+	public R removeBucket(@RequestParam String bucketName) {
+		ossBuilder.template().removeBucket(bucketName);
+		return R.success("删除成功");
+	}
+	/**
+	 * @return
+	 * @throws Exception
+	 * **/
+	@SneakyThrows
+	@PostMapping("/upload-file")
+	public  R uploadByfile(@RequestParam(value = "file",required=false) MultipartFile file,
+						   @RequestParam(value = "identifier",required=false) String identifier,
+						   @RequestParam(value = "chunkNumber",required=false) Integer chunkNumber,
+						   @RequestParam(value = "chunkSize",required=false) Integer chunkSize,
+						   @RequestParam(value = "currentChunkSize",required=false) String currentChunkSize,
+						   @RequestParam(value = "filename",required=false) String filename,
+						   @RequestParam(value = "relativePath",required=false) String relativePath,
+						   @RequestParam(value = "totalChunks",required=false) Integer totalChunks,
+						   @RequestParam(value = "totalSize",required=false) String totalSize,
+						   @RequestParam(value = "objectType",required=false) String objectType) throws Exception {
+		R result = new R();
+		// 判断是否上传
+		if (file == null) {
+			result.setSuccess(false);
+			result.setMsg("没有文件!");
+			return result;
+		}
+		MultipartFileParam param = new MultipartFileParam();
+		param.setFile(file);
+		param.setIdentifier(identifier);
+		param.setChunkNumber(chunkNumber);
+		param.setChunkSize(chunkSize);
+		param.setCurrentChunkSize(currentChunkSize);
+		param.setFilename(filename);
+		param.setRelativePath(relativePath);
+		param.setTotalChunks(totalChunks);
+		param.setTotalSize(totalSize);
+		param.setObjectType(objectType);
+		return uploadByMappedByteBuffer(param);
+	}
+	/**
+	 * 分块上传
+	 * 第一步:获取RandomAccessFile,随机访问文件类的对象
+	 * 第二步:调用RandomAccessFile的getChannel()方法,打开文件通道 FileChannel
+	 * 第三步:获取当前是第几个分块,计算文件的最后偏移量
+	 * 第四步:获取当前文件分块的字节数组,用于获取文件字节长度
+	 * 第五步:使用文件通道FileChannel类的 map()方法创建直接字节缓冲器  MappedByteBuffer
+	 * 第六步:将分块的字节数组放入到当前位置的缓冲区内  mappedByteBuffer.put(byte[] b);
+	 * 第七步:释放缓冲区
+	 * 第八步:检查文件是否全部完成上传
+	 */
+
+	public  R uploadByMappedByteBuffer(MultipartFileParam param) throws Exception {
+		R result = new R();
+		if (param.getIdentifier() == null || "".equals(param.getIdentifier())) {
+			return result;
+		}
+		// 判断是否上传
+		if (param.getFile() == null) {
+			result.setSuccess(false);
+			result.setMsg("没有文件!");
+			return result;
+		}
+		if(checkMd5(param.getFile().getInputStream(), param.getIdentifier())){
+			result.setSuccess(false);
+			result.setMsg("文件的Identifier对不上!");
+			return result;
+		}
+		/**
+		 * 查询是否已经上传该分片
+		 * **/
+		QueryWrapper<LargeFile> wrapper = new QueryWrapper<LargeFile>()
+				.eq("file_key", param.getIdentifier()).eq("is_deleted",0).eq("shard_index",param.getChunkNumber()).orderByDesc("shard_index");
+		LargeFile list = iLargeFileService.getOne(wrapper);
+		if(list != null ){
+			result.setSuccess(true);
+			result.setMsg("该分片已上传!");
+			result.setData(list.getShardIndex());
+			result.setCode(200);
+			return result;
+		}
+		// 文件名称
+		String fileName = getFileName(param);
+		// 临时文件名称
+		String tempFileName = param.getIdentifier() + fileName.substring(fileName.lastIndexOf(".")) + "."+param.getChunkNumber();
+		// 获取文件路径
+		/**Windows文件路径要加在哪个盘**/
+//		String filePath = "D:/www/wwwroot/Users/hongchuangyanfa/Desktop/Desktop/ceshi";
+		String filePath = ParamCache.getValue(CommonConstant.SYS_LOCAL_URL)+"largeFile/";
+		// 创建文件夹
+//		getAbsoluteFile(filePath, fileName);
+//		new File(filePath, fileName);
+		// 创建临时文件
+//		File tempFile = new File(filePath, tempFileName);
+		File tempFile = buildUploadFile(tempFileName);
+		param.getFile().transferTo(tempFile);
+
+		/**
+		 * 以上意思是把每个分片都保存成本地一个文件,如 测试.mp4.1 最后合并
+		 * ===================================================
+		 * 以下注释是把分片存到一个文件,持续写入,感觉不太保险**/
+//		//第一步 获取RandomAccessFile,随机访问文件类的对象
+//		RandomAccessFile raf = new RandomAccessFile(tempFile,"rw");
+//		//第二步 调用RandomAccessFile的getChannel()方法,打开文件通道 FileChannel
+//		FileChannel fileChannel = raf.getChannel();
+//		//第三步 获取当前是第几个分块,计算文件的最后偏移量
+//		long offset = (param.getChunkNumber() - 1) * param.getChunkSize();
+//		//第四步 获取当前文件分块的字节数组,用于获取文件字节长度
+//		byte[] fileData = param.getFile().getBytes();
+//		//第五步 使用文件通道FileChannel类的 map()方法创建直接字节缓冲器  MappedByteBuffer
+//		MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, offset, fileData.length);
+//		//第六步 将分块的字节数组放入到当前位置的缓冲区内  mappedByteBuffer.put(byte[] b)
+//		mappedByteBuffer.put(fileData);
+//		//第七步 释放缓冲区
+//		freeMappedByteBuffer(mappedByteBuffer);
+//		fileChannel.close();
+//		raf.close();
+		//第八步,保存分片信息
+		LargeFile largeFile = new LargeFile();
+
+		largeFile.setFileKey(param.getIdentifier());
+		largeFile.setName(tempFileName);
+		largeFile.setPath(filePath+fileName);
+		largeFile.setShardIndex(param.getChunkNumber());
+		largeFile.setShardSize(param.getChunkSize());
+		largeFile.setSize(Integer.valueOf(param.getCurrentChunkSize()));
+		largeFile.setShardTotal(param.getTotalChunks());
+		largeFile.setSuffix(param.getObjectType());
+		iLargeFileService.save(largeFile);
+
+		//第八步 检查文件是否全部完成上传
+		lock.lock();
+		try {
+			// 检测是否为最后一块分片
+			QueryWrapper<LargeFile> wrapper1 = new QueryWrapper<LargeFile>()
+					.eq("file_key", param.getIdentifier()).eq("is_deleted",0);
+
+			Integer count = Math.toIntExact(iLargeFileService.count(wrapper1));
+			if (count.equals(param.getTotalChunks())) {
+				/**每个文件保存到本地所使用的合并各个文件**/
+				merge(largeFile,filePath);
+				String path = largeFile.getPath(); //获取到的路径 没有.1 .2 这样的东西
+
+				//截取视频所在的路径
+				path = path.replace(filePath,"");
+				File file = new File(filePath + path);
+				//修改成原来的文件名
+				renameFile(file,param.getFilename());
+				FileInputStream inputStream = new FileInputStream(filePath + param.getFilename());
+//				上传oss
+				BladeFile bladeFile = ossBuilder.template().putFile(param.getFilename(),inputStream);
+
+
+				NewBladeFile newBladeFile = new NewBladeFile();
+//				if(param.getFilename().contains("pdf")){
+//					PDDocument document = PDDocument.load(inputStream);
+//					//获取文件页数
+//					newBladeFile.setPage(document.getPages().getCount());
+//					//pdf的路径就是文件上传的路径
+//					newBladeFile.setPdfUrl(bladeFile.getLink());
+//				}
+				BeanUtils.copyProperties(bladeFile, newBladeFile);
+				File file1 = new File(filePath + param.getFilename());
+				//删除本地文件
+				file1.delete();
+				iLargeFileService.updateLargeFileDeleted(param.getIdentifier());
+				result.setSuccess(true);
+				result.setMsg("上传成功!");
+				result.setData(newBladeFile);
+//				result.setData(new BladeFile());
+				result.setCode(200);
+				return result;
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} finally {
+			lock.unlock();
+		}
+		result.setCode(200);
+		result.setSuccess(true);
+		result.setMsg("上传成功!");
+		result.setData(largeFile.getShardIndex());
+		return result;
+	}
+
+	/**
+	 * 构建上传目录和文件
+	 */
+	private File buildUploadFile(String name) {
+		String fileName = name;
+		String fullDir = buildUploadDir();
+		return new File(fullDir, fileName);
+	}
+
+	/**
+	 * 构建上传完整目录
+	 */
+	private String buildUploadDir() {
+		String fullDir =ParamCache.getValue(CommonConstant.SYS_LOCAL_URL)+"largeFile/";
+		File dir = new File(fullDir);
+		if (!dir.exists()) {
+			dir.mkdirs();
+		}
+		return fullDir;
+	}
+
+	/**
+	 * md5校验
+	 */
+	private boolean checkMd5(InputStream is, String md5) {
+		String check = "";
+		try {
+			check = DigestUtils.md5DigestAsHex(is);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		if (!check.equalsIgnoreCase(md5)) {
+			return false;
+		}
+		return true;
+	}
+	/**
+	 * @author fengxinglie
+	 * 合并分页
+	 */
+	private void merge(LargeFile largeFile,String basePath) throws FileNotFoundException, InterruptedException {
+		//合并分片开始
+		String path = largeFile.getPath(); //获取到的路径 没有.1 .2 这样的东西
+		//截取视频所在的路径
+		path = path.replace(basePath,"");
+		Integer shardTotal= largeFile.getShardTotal();
+		File newFile = new File(basePath + path);
+		FileOutputStream outputStream = new FileOutputStream(newFile,true); // 文件追加写入
+		FileInputStream fileInputStream = null; //分片文件
+		byte[] byt = new byte[10 * 1024 * 1024];
+		int len;
+		try {
+			for (int i = 0; i < shardTotal; i++) {
+				// 读取第i个分片
+				fileInputStream = new FileInputStream(new File(basePath + path + "." + (i + 1))); // course\6sfSqfOwzmik4A4icMYuUe.mp4.1
+				while ((len = fileInputStream.read(byt)) != -1) {
+					outputStream.write(byt, 0, len);
+				}
+			}
+		} catch (IOException e) {
+		} finally {
+			try {
+				if (fileInputStream != null) {
+					fileInputStream.close();
+				}
+				outputStream.close();
+			} catch (Exception e) {
+//				log.error("IO流关闭", e);
+			}
+		}
+		//告诉java虚拟机去回收垃圾 至于什么时候回收 这个取决于 虚拟机的决定
+		System.gc();
+		//等待100毫秒 等待垃圾回收去 回收完垃圾
+		Thread.sleep(100);
+		for (int i = 0; i < shardTotal; i++) {
+			String filePath = basePath + path + "." + (i + 1);
+			File file = new File(filePath);
+			boolean result = file.delete();
+//			log.info("删除{},{}", filePath, result ? "成功" : "失败");
+		}
+//		log.info("删除分片结束");
+	}
+
+	/**
+	 * 文件重命名
+	 *
+	 * @param toBeRenamed   将要修改名字的文件
+	 * @param toFileNewName 新的名字
+	 * @return
+	 */
+	private static boolean renameFile(File toBeRenamed, String toFileNewName) {
+		//检查要重命名的文件是否存在,是否是文件
+		if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
+			return false;
+		}
+		String p = toBeRenamed.getParent();
+		File newFile = new File(p + File.separatorChar + toFileNewName);
+		//修改文件名
+		return toBeRenamed.renameTo(newFile);
+	}
+
+
+
+	/**
+	 * 在MappedByteBuffer释放后再对它进行读操作的话就会引发jvm crash,在并发情况下很容易发生
+	 * 正在释放时另一个线程正开始读取,于是crash就发生了。所以为了系统稳定性释放前一般需要检 查是否还有线程在读或写
+	 *
+	 * @param mappedByteBuffer
+	 */
+	private static void freeMappedByteBuffer(final MappedByteBuffer mappedByteBuffer) {
+		try {
+			if (mappedByteBuffer == null) {
+				return;
+			}
+			mappedByteBuffer.force();
+			AccessController.doPrivileged(new PrivilegedAction<Object>() {
+				@Override
+				public Object run() {
+					try {
+						Method getCleanerMethod = mappedByteBuffer.getClass().getMethod("cleaner", new Class[0]);
+						//可以访问private的权限
+						getCleanerMethod.setAccessible(true);
+						//在具有指定参数的 方法对象上调用此 方法对象表示的底层方法
+						sun.misc.Cleaner cleaner = (sun.misc.Cleaner) getCleanerMethod.invoke(mappedByteBuffer,
+								new Object[0]);
+						cleaner.clean();
+					} catch (Exception e) {
+//						log.error("clean MappedByteBuffer error!!!", e);
+					}
+					return null;
+				}
+			});
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	private static String getFileName(MultipartFileParam param) {
+		String extension;
+		if (ObjectUtil.isNotEmpty(param.getFile())) {
+			String filename = param.getFile().getOriginalFilename();
+			extension = filename.substring(filename.lastIndexOf("."));
+		} else {
+			extension = param.getFilename().substring(param.getFilename().lastIndexOf("."));
+		}
+		return param.getIdentifier() + extension;
+	}
+
+}

+ 33 - 0
blade-ops/blade-resource/src/main/java/org/springblade/resource/mapper/LargeFileMapper.java

@@ -0,0 +1,33 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.resource.entity.LargeFile;
+
+import java.util.List;
+
+/**
+ *  Mapper 接口
+ *
+ * @author BladeX
+ */
+public interface LargeFileMapper extends BaseMapper<LargeFile> {
+
+
+    Integer updateLargeFileDeleted(String identifier);
+}

+ 33 - 0
blade-ops/blade-resource/src/main/java/org/springblade/resource/mapper/LargeFileMapper.xml

@@ -0,0 +1,33 @@
+<?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.resource.mapper.LargeFileMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="largeFileResultMap" type="org.springblade.resource.entity.LargeFile">
+        <result column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="path" property="path"/>
+        <result column="name" property="name"/>
+        <result column="suffix" property="suffix"/>
+        <result column="size" property="size"/>
+        <result column="shard_index" property="shardIndex"/>
+        <result column="shard_size" property="shardSize"/>
+        <result column="shard_total" property="shardTotal"/>
+        <result column="file_key" property="fileKey"/>
+    </resultMap>
+    <update id="updateLargeFileDeleted">
+        update blade_large_file set is_deleted = 1 where
+        file_key=#{identifier}
+    </update>
+
+
+    <select id="selectOssPage" resultMap="largeFileResultMap">
+        select * from blade_oss where is_deleted = 0
+    </select>
+
+</mapper>

+ 30 - 0
blade-ops/blade-resource/src/main/java/org/springblade/resource/service/ILargeFileService.java

@@ -0,0 +1,30 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.service;
+
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.resource.entity.LargeFile;
+
+/**
+ * 服务类
+ *
+ * @author BladeX
+ */
+public interface ILargeFileService extends BaseService<LargeFile> {
+
+    boolean updateLargeFileDeleted(String identifier);
+}

+ 49 - 0
blade-ops/blade-resource/src/main/java/org/springblade/resource/service/impl/LargeFileServiceImpl.java

@@ -0,0 +1,49 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.resource.entity.LargeFile;
+import org.springblade.resource.entity.Oss;
+import org.springblade.resource.mapper.LargeFileMapper;
+import org.springblade.resource.mapper.OssMapper;
+import org.springblade.resource.service.ILargeFileService;
+import org.springblade.resource.service.IOssService;
+import org.springblade.resource.vo.OssVO;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 服务实现类
+ *
+ * @author BladeX
+ */
+@Service
+public class LargeFileServiceImpl extends BaseServiceImpl<LargeFileMapper, LargeFile> implements ILargeFileService {
+
+    @Override
+    public boolean updateLargeFileDeleted(String identifier) {
+        baseMapper.updateLargeFileDeleted(identifier);
+        return false;
+    }
+}

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

@@ -64,4 +64,7 @@ public interface ArchiveFileClient {
 
     @PostMapping(API_PREFIX + "/updateArchiveFileForCreateArchive")
     void updateArchiveFileForCreateArchive(@RequestBody List<ArchiveFile> waitArchiveFiles);
+
+    @PostMapping(API_PREFIX + "/getListByProjectId")
+    List<ArchiveFile> getListByProjectId(@RequestBody Long projectId);
 }

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

@@ -181,6 +181,20 @@ public class ArchiveTreeContract extends BaseEntity {
 	//树形排序
 	private String treeSort;
 
+	//---节点基础信息,自动组卷需要这些参数来确定
+	//档号前缀
+	private String fileNumberPrefix;
+	//保管期限
+	private String storageTime;
+	//立卷人
+	private String rollor;
+	//审核人
+	private String reviewer;
+	//卷盒规格
+	private String specification;
+	//案卷后缀
+	private String archiveNameSuffix;
+
 
 
 	public ArchiveTreeContract() {

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

@@ -24,4 +24,16 @@ public interface ArchiveTreeContractClient {
     @PostMapping(API_PREFIX + "/getHavedFileNodeByProjectID")
     List<ArchiveTreeContract> getHavedFileNodeByProjectID(@RequestParam Long projectId);
 
+    /**
+     * 根据项目id获取所有数据
+     */
+    @PostMapping(API_PREFIX + "/getListByProjectId")
+    List<ArchiveTreeContract> getListByProjectId(@RequestParam Long projectId);
+
+    @PostMapping(API_PREFIX + "/getSelectNodeByGroupId")
+    List<ArchiveTreeContract> getSelectNodeByGroupId(@RequestParam Long projectId,@RequestParam Long archiveAutoGroupId);
+
+    @PostMapping(API_PREFIX + "/getArchiveTreeContractById")
+    ArchiveTreeContract getArchiveTreeContractById(@RequestParam Long id);
+
 }

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

@@ -14,7 +14,7 @@ import javax.sql.DataSource;
 public class sqliteConfig {
     public DataSource dataSource(){
         DruidDataSource ds = new DruidDataSource();
-        ds.setUrl("jdbc:sqlite:D:\\develop\\test\\test330.db");
+        ds.setUrl("jdbc:sqlite:/www/wwwroot/localClient/data");
         ds.setDriverClassName("org.sqlite.JDBC");
         return ds;
     }

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

@@ -80,7 +80,9 @@ public class ArchiveFileAutoController extends BladeController {
                             saveVo.setArchiveId(archive.getId());
                             saveVo.setOriginId(archive.getId());
                             list.add(saveVo);
-                            pageN = pageN + saveVo.getFilePage();
+                            if(saveVo.getFilePage() != null && !saveVo.getFilePage().equals("")){
+                                pageN = pageN + saveVo.getFilePage();
+                            }
                             i++;
                         }
                         saveVos.setList(list);

+ 3 - 3
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveOfflineVersionInfoController.java

@@ -36,9 +36,9 @@ public class ArchiveOfflineVersionInfoController {
      */
     @ApiOperation(value = "打包数据")
     @GetMapping("/packData")
-    public R<String> packData(Long projectId){
+    public R<String> packData(Long projectId) throws Exception {
         //异步调用自动打包上传,完成后修改数据库信息
-        offlineVersionInfoService.packData(projectId);
+        offlineVersionInfoService.packData(1578599210897772545L);
         return R.data("最新数据后台自动打包中,打包完成后会更新打包日期");
     }
     /**
@@ -47,7 +47,7 @@ public class ArchiveOfflineVersionInfoController {
     @ApiOperation(value = "脱机载体工具")
     @GetMapping("/getVersionInfo")
     public R<ArchiveOfflineVersionInfo> getVersionInfo(Long projectId){
-        ArchiveOfflineVersionInfo versionInfo = mapper.selectVersionInfo(111L);
+        ArchiveOfflineVersionInfo versionInfo = mapper.selectVersionInfo(1578599210897772545L);
         String size = this.formatSize(Long.parseLong(versionInfo.getFileSize()));
         versionInfo.setFileSize(size);
         return R.data(versionInfo);

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

@@ -12,7 +12,7 @@
     </resultMap>
     <select id="selectVersionInfo" resultMap="ResultMap">
         SELECT id, project_id, upload_date, file_url, file_name, file_size
-        FROM bladex.u_archive_offline_version_info
+        FROM u_archive_offline_version_info
         where project_id = #{projectId}
         order by upload_date desc
         limit 1;

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

@@ -9,5 +9,5 @@ import org.springblade.core.mp.base.BaseService;
  * @Date 2023/3/31 10:09
  **/
 public interface IArchiveOfflineVersionInfoService extends BaseService<ArchiveOfflineVersionInfo> {
-    void packData(Long projectId);
+    void packData(Long projectId) throws Exception;
 }

+ 155 - 7
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveOfflineVersionInfoServiceImpl.java

@@ -1,27 +1,35 @@
 package org.springblade.archive.service.impl;
 
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.AllArgsConstructor;
 import org.springblade.archive.config.sqliteConfig;
 import org.springblade.archive.dto.ArchivesAutoDTO;
 import org.springblade.archive.entity.ArchiveOfflineVersionInfo;
-import org.springblade.archive.entity.ArchivesAuto;
 import org.springblade.archive.mapper.ArchiveOfflineVersionInfoMapper;
 import org.springblade.archive.mapper.ArchivesAutoMapper;
 import org.springblade.archive.service.IArchiveOfflineVersionInfoService;
-import org.springblade.archive.service.IArchivesAutoService;
 import org.springblade.archive.utils.FileUtils;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.feign.ArchiveFileClient;
+import org.springblade.common.utils.CommonUtil;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.oss.model.BladeFile;
+import org.springblade.core.tool.utils.ResourceUtil;
 import org.springblade.core.tool.utils.StringUtil;
-import org.springframework.beans.BeanUtils;
+import org.springblade.manager.entity.ArchiveTreeContract;
+import org.springblade.manager.feign.ArchiveTreeContractClient;
+import org.springblade.resource.feign.NewIOSSClient;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
+import java.sql.Statement;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -33,14 +41,149 @@ import java.util.List;
 @Service
 @AllArgsConstructor
 public class ArchiveOfflineVersionInfoServiceImpl extends BaseServiceImpl<ArchiveOfflineVersionInfoMapper, ArchiveOfflineVersionInfo> implements IArchiveOfflineVersionInfoService {
+    private final ArchiveOfflineVersionInfoMapper infoMapper;
     private final sqliteConfig data;
     private final ArchivesAutoMapper autoMapper;
     private final ArchiveFileClient fileClient;
+    private final ArchiveTreeContractClient contractClient;
+    private final NewIOSSClient newIOSSClient;
+
 
     @Override
-    public void packData(Long projectId) {
+    @Async
+    public void packData(Long projectId) throws Exception {
+        String localUrl = "/www/wwwroot/localClient/local_archives/alilib";
+        String packUrl = "/www/wwwroot/localClient";
+        String zipUrl = "/www/wwwroot/localClient.zip";
+        //清空url的文件夹
+        CommonUtil.deleteDir(localUrl);
+        //导入档案到sqlite
+        this.autoToSqlite(projectId);
+        //导入归档树到sqlite
+        this.contractToSqlite(projectId);
+        //导入档案文件
+        this.fileToSqlite(projectId);
+        //打包文件
+        CommonUtil.packageZip(packUrl);
+        //上传文件
+        File zipFile = ResourceUtil.getFile(zipUrl);
+        //BladeFile bladeFile = ossBuilder.template().putFile("localClient.zip",new FileInputStream(zipFile));
+        BladeFile bladeFile =newIOSSClient.uploadFile("localClient.zip",zipUrl);
+        ArchiveOfflineVersionInfo info = new ArchiveOfflineVersionInfo();
+        info.setId(SnowFlakeUtil.getId());
+        info.setFileUrl(bladeFile.getLink());
+        info.setFileName(bladeFile.getOriginalName());
+        info.setUploadDate(LocalDateTime.now());
+        info.setProjectId(projectId);
+        info.setFileSize(CommonUtil.getResourceLength(bladeFile.getLink())+"");
+        infoMapper.insert(info);
+
+    }
+    public void fileToSqlite(Long projectId) throws Exception {
+        List<ArchiveFile> list = fileClient.getListByProjectId(projectId);
+        String localUrl = "/www/wwwroot/localClient/local_archives/alilib/";
+        for (ArchiveFile file : list) {
+            if (StringUtil.isNotBlank(file.getFileUrl())) {
+                String fileUrl = file.getFileUrl();
+                String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
+                InputStream file_out = CommonUtil.getOSSInputStream(fileUrl);
+                CommonUtil.inputStreamToFile(file_out,new File(localUrl+fileName));
+                file.setFileUrl(fileName);
+            }
+            if (StringUtil.isNotBlank(file.getPdfFileUrl())){
+                String pdfFileUrl = file.getPdfFileUrl();
+                String fileName = pdfFileUrl.substring(pdfFileUrl.lastIndexOf('/') + 1);
+                InputStream file_out = CommonUtil.getOSSInputStream(pdfFileUrl);
+                CommonUtil.inputStreamToFile(file_out,new File(localUrl+fileName));
+                file.setPdfFileUrl(fileName);
+            }
+        }
+        try  {
+            Connection conn = data.dataSource().getConnection();
+            //清空原有数据
+            Statement statement = conn.createStatement();
+            statement.execute("DELETE FROM u_archive_file");
+            statement.close();
+            //导入最新数据
+            String sql = "INSERT INTO u_archive_file (id, project_id, node_id, file_number, file_name, file_time, file_url, pdf_file_url, status, is_deleted, archive_id,create_time)" +
+                    " VALUES(?,?,?,?,?,?,?,?,?,?,?,?)";
+            PreparedStatement pstm = conn.prepareStatement(sql);
+            //pstm 绑定数据
+            int i=0;
+            for (ArchiveFile file : list) {
+                i++;
+                pstm.setLong(1,file.getId());
+                pstm.setLong(2, Long.parseLong(file.getProjectId()));
+                pstm.setString(3,file.getNodeId()==null?"":file.getNodeId());
+                pstm.setString(4,file.getFileNumber()==null?"":file.getFileNumber());
+                pstm.setString(5,file.getFileName());
+                pstm.setString(6,file.getFileTime());
+                pstm.setString(7,file.getFileUrl()==null?"":file.getFileUrl());
+                pstm.setString(8,file.getPdfFileUrl()==null?"":file.getPdfFileUrl());
+                pstm.setInt(9,file.getStatus());
+                pstm.setInt(10,file.getIsDeleted());
+                pstm.setLong(11,file.getArchiveId()==null?-1:file.getArchiveId());
+                pstm.setString(12,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file.getCreateTime()));
+                //添加批处理
+                pstm.addBatch();
+                if (i % 1000 == 0) {
+                    pstm.executeBatch();
+                    pstm.clearBatch();
+                }
+            }
+            //将批处理余下的语句执行完毕
+            pstm.executeBatch();
+            //释放资源
+            pstm.close();
+            conn.close();
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    public void contractToSqlite(Long projectId){
+        List<ArchiveTreeContract> list = contractClient.getListByProjectId(projectId);
+        try  {
+            Connection conn = data.dataSource().getConnection();
+            Statement statement = conn.createStatement();
+            statement.execute("DELETE FROM m_archive_tree_contract");
+            statement.close();
+            //获取执行对象
+            String sql = "INSERT INTO m_archive_tree_contract(id, project_id, parent_id, ancestors, node_name,status,is_deleted,create_time)" +
+                    "VALUES(?, ?, ?, ?, ?,?,?,?)";
+            PreparedStatement pstm = conn.prepareStatement(sql);
+            //pstm 绑定数据
+            int i=0;
+            for (ArchiveTreeContract contract : list) {
+                i++;
+                pstm.setLong(1,contract.getId());
+                pstm.setLong(2,contract.getProjectId());
+                pstm.setLong(3,contract.getParentId());
+                pstm.setString(4,contract.getAncestors());
+                pstm.setString(5,contract.getNodeName());
+                pstm.setInt(6,contract.getStatus());
+                pstm.setInt(7,contract.getIsDeleted());
+                pstm.setString(8,  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(contract.getCreateTime()));
+                //添加批处理
+                pstm.addBatch();
+                if (i % 1000 == 0) {
+                    pstm.executeBatch();
+                    pstm.clearBatch();
+                }
+            }
+            //将批处理余下的语句执行完毕
+            pstm.executeBatch();
+            //释放资源
+            pstm.close();
+            conn.close();
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    public void autoToSqlite(Long projectId){
         List<ArchivesAutoDTO> list = autoMapper.getListByProjectId(projectId);
-        String localUrl = "D:\\develop\\aliyun\\";
+        String localUrl = "/www/wwwroot/localClient/local_archives/alilib/";
         //拼接档案里文件的pdf设置allPdf
         for (ArchivesAutoDTO dto : list) {
             List<ArchiveFile> files = fileClient.getArchiveFileByArchivesId(dto.getId()+"","");
@@ -61,7 +204,11 @@ public class ArchiveOfflineVersionInfoServiceImpl extends BaseServiceImpl<Archiv
         }
         try  {
             Connection conn = data.dataSource().getConnection();
-            //获取执行对象
+            //清空数据
+            Statement statement = conn.createStatement();
+            statement.execute("DELETE FROM u_archives_auto");
+            statement.close();
+            //导入数据
             String sql = "INSERT INTO u_archives_auto (id, project_id, name, file_number, unit,storage_time, is_archive,node_id, status, is_deleted,create_time,all_file_pdf)" +
                     "VALUES(?,?,?,?,?,?,?,?,?,?,?,?)";
             PreparedStatement pstm = conn.prepareStatement(sql);
@@ -98,4 +245,5 @@ public class ArchiveOfflineVersionInfoServiceImpl extends BaseServiceImpl<Archiv
             e.printStackTrace();
         }
     }
+
 }

+ 183 - 36
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -55,6 +55,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	private ArchiveFileClient archiveFileClient;
 	private ArchiveTreeContractClient archiveTreeContractClient;
 
+	private Map<String,Integer> indexMap; //按立卷位区分和生成流水号
 
 	@Override
 	public IPage<ArchivesAutoVO> selectArchivesAutoPage(IPage<ArchivesAutoVO> page, ArchivesAutoVO archivesAuto) {
@@ -143,7 +144,7 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 		//步骤四:按照单独,分类,默认的顺序执行组卷流程。
 		archiveAutoMethod3(list3);//单独组卷
-		archiveAutoMethod2(list2);//分类组卷
+		archiveAutoMethod2(list2,projectId);//分类组卷
 		archiveAutoMethod1(list1);//默认组卷
 	}
 
@@ -184,27 +185,45 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		return null;
 	}
 
-	private ArchivesAuto builtArchives(ArchiveTreeContract node,int pageN,int fileN){
+	private ArchivesAuto builtArchives(ArchiveTreeContract node,int pageN,int fileN,String startDate,String endDate,String archiveName){
 		ArchivesAuto archivesAuto = new ArchivesAuto();
 		archivesAuto.setProjectId(node.getProjectId());
 		archivesAuto.setContractId(node.getContractId());
-		archivesAuto.setName("");//TODO 案卷题名
-		archivesAuto.setFileNumber("");//TODO 档号
+		archivesAuto.setName(archiveName);//案卷题名
+		if(indexMap==null){
+			indexMap=new HashMap<>();
+		}
+		//TODO 获取立卷单位
+		String unit="";
+		//获取档号
+		String fileNumberPrefix=node.getFileNumberPrefix(); //档号前缀在节点设置
+		Integer fileNumberSuffix = null;//档号后缀按立卷单位生成流水号
+		if(indexMap.containsKey(unit)){
+			fileNumberSuffix = indexMap.get(unit);
+			indexMap.put(unit,fileNumberSuffix+1);
+		}else{
+			fileNumberSuffix=1;
+			indexMap.put(unit,fileNumberSuffix+1);
+		}
+
+		archivesAuto.setFileNumber(fileNumberPrefix+fileNumberSuffix);//档号
 		//archivesAuto.setMicron();//微缩号
-		archivesAuto.setUnit("");//TODO 单位
+		archivesAuto.setUnit(unit);//立卷单位
 		//archivesAuto.setQuantity();//数量/单位
-		archivesAuto.setSpecification("");//TODO 规格 从节点规格获取
-		archivesAuto.setStartDate(null);//TODO 文件开始时间
-		archivesAuto.setEndDate(null);//TODO 文件结束时间
-		archivesAuto.setStorageTime("");//TODO 保管时间  从节点规格获取
-		archivesAuto.setSecretLevel("");//TODO 保密级别 从节点规格获取
+
+		archivesAuto.setSpecification(node.getSpecification());//案卷规格 从节点规格获取
+		archivesAuto.setStartDate(LocalDateTime.parse(startDate));//文件开始时间
+		archivesAuto.setEndDate(LocalDateTime.parse(endDate));//文件结束时间
+		archivesAuto.setStorageTime(node.getStorageTime());//保管期限  从节点规格获取
+		archivesAuto.setSecretLevel("");//保密级别 没地方设置 暂留
 		//archivesAuto.setCarrierType();//载体类型
 		//archivesAuto.setKeywords();//主题词
 		//archivesAuto.setStorageLocation();//存放位置
 		archivesAuto.setIsArchive(1);//已归档
 		//archivesAuto.setRemark();//备注
-		archivesAuto.setRollDate(null);// TODO 立卷日期
-		archivesAuto.setRollor("自动");//立卷人
+		archivesAuto.setRollDate(LocalDateTime.now());//立卷日期
+		archivesAuto.setRollor(node.getRollor());//立卷人 从节点规格获取
+		archivesAuto.setReviewer(node.getReviewer());//审核人 从节点规格获取
 		archivesAuto.setNodeId(node.getId());//归档树节点
 		archivesAuto.setOutUrl("");//TODO 生成案卷的封面 目录 备考表,背级
 		archivesAuto.setFileN(fileN);//文件数量
@@ -214,28 +233,143 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		//archivesAuto.setSize();
 		archivesAuto.setTreeSort(node.getTreeSort());
 		//archivesAuto.setIsOpen();
-		archivesAuto.setIscheck(0);
+		//archivesAuto.setIscheck(0);
 		archivesAuto.setIsAutoFile(0);//是否案卷收集上传的案卷
 		archivesAuto.setIsLock(0);//案卷锁定状态
 		baseMapper.insert(archivesAuto);
 		return archivesAuto;
 	}
 
+	private String getArchiveStartDateAndEndDate(List<ArchiveFile> waitArchiveFiles){
+		//找出文件集合中的最大最小时间
+		List<String> fileTimeList = new ArrayList<>();
+		for(ArchiveFile file:waitArchiveFiles){
+			fileTimeList.add(file.getFileTime());
+		}
+		Collections.sort(fileTimeList);
+		String startDate=fileTimeList.get(0);
+		String endDate=fileTimeList.get(fileTimeList.size()-1);
+		return startDate+","+endDate;
+	}
+
+	private String builtArchiveName(List<ArchiveFile> waitArchiveFiles, ArchiveTreeContract node,boolean isCrossNode){
+		//TODO 获取案卷题名
+		return "";
+	}
+	private String builtFilePageNo(List<ArchiveFile> waitArchiveFiles){
+		//TODO 生成文件对应的页码,返回url
+		return "";
+	}
+
+
 	/**
 	 * 单独组卷规则组卷
 	 * @param waitArchiveFiles
-	 * @param node
+	 * @param node 规格参数所在节点
+	 * @param pageN
 	 */
 	private void createArchive3(List<ArchiveFile> waitArchiveFiles, ArchiveTreeContract node,int pageN){
 
+		String archiveStartDateAndEndDate = getArchiveStartDateAndEndDate(waitArchiveFiles);
+		String[] split = archiveStartDateAndEndDate.split(",");
+		String startDate=split[0];
+		String endDate=split[1];
+		int fileN =waitArchiveFiles.size();
+
+		String archiveName=builtArchiveName(waitArchiveFiles,node,false);//获取案卷题名
 		//1.创建新案卷
-		ArchivesAuto archivesAuto = builtArchives(node, pageN, waitArchiveFiles.size());
+		ArchivesAuto archivesAuto = builtArchives(node, pageN, fileN,startDate,endDate,archiveName);
 		//2.设置文件所属案卷,组卷状态
 		Long archivesAutoId = archivesAuto.getId();
+
+		builtFilePageNo(waitArchiveFiles);//生成文件页码
+
+		for(ArchiveFile file:waitArchiveFiles){
+			file.setArchiveId(archivesAutoId);//设置文件所属案卷
+			file.setIsArchive(1);
+
+		}
+		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
+	}
+
+	/**
+	 * 分类并卷组卷
+	 * @param waitArchiveFiles
+	 * @param archiveAutoGroupId 分类并卷分组ID
+	 */
+	private void createArchive2(List<ArchiveFile> waitArchiveFiles, Long archiveAutoGroupId,Long projectId){
+
+		//获取同一分类archiveAutoGroupId下设置的(设置规则时选中的)节点,排好序
+		List<ArchiveTreeContract> selectList=archiveTreeContractClient.getSelectNodeByGroupId(projectId,archiveAutoGroupId);
+		//分类并卷节点默认采用设置节点的第一个节点为案卷归属节点。
+		ArchiveTreeContract node = selectList.get(0);
+		//获取案卷文件起止时间
+		String archiveStartDateAndEndDate = getArchiveStartDateAndEndDate(waitArchiveFiles);
+		String[] split = archiveStartDateAndEndDate.split(",");
+		String startDate=split[0];
+		String endDate=split[1];
+		int fileN =waitArchiveFiles.size();
+		//获取案卷内文件总页数
+		int pageN=0;
+		for(ArchiveFile file:waitArchiveFiles){
+			pageN=pageN+file.getFilePage();
+		}
+
+
+		//默认组卷存在跨节点组卷  注意案卷归属节点,案卷命名方式
+		//获取案卷题名
+		String archiveName=builtArchiveName(waitArchiveFiles,node,true);//获取案卷题名
+
+		//1.创建新案卷
+		ArchivesAuto archivesAuto = builtArchives(node,pageN,fileN,startDate,endDate,archiveName);
+
+		//2.生成文件页码
+		builtFilePageNo(waitArchiveFiles);
+
+		//3.设置文件所属案卷,组卷状态
+		Long archivesAutoId = archivesAuto.getId();
+		for(ArchiveFile file:waitArchiveFiles){
+			file.setArchiveId(archivesAutoId);//设置文件所属案卷
+			file.setIsArchive(1);
+		}
+		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
+	}
+
+	/**
+	 * 默认规则组卷
+	 * @param waitArchiveFiles
+	 * @param pageN
+	 */
+	private void createArchive1(List<ArchiveFile> waitArchiveFiles,int pageN){
+
+		//获取案卷文件起止时间
+		String archiveStartDateAndEndDate = getArchiveStartDateAndEndDate(waitArchiveFiles);
+		String[] split = archiveStartDateAndEndDate.split(",");
+		String startDate=split[0];
+		String endDate=split[1];
+		int fileN =waitArchiveFiles.size();
+
+
+		//默认组卷存在跨节点组卷  注意案卷归属节点,案卷命名方式
+		//案卷归属节点为 排第一的文件所属节点
+		ArchiveFile firstFile = waitArchiveFiles.get(0);
+		Long nodeId = Long.parseLong(firstFile.getNodeId());
+		ArchiveTreeContract node = archiveTreeContractClient.getArchiveTreeContractById(nodeId);
+
+		//获取案卷题名
+		String archiveName=builtArchiveName(waitArchiveFiles,node,true);//获取案卷题名
+
+		//1.创建新案卷
+		ArchivesAuto archivesAuto = builtArchives(node,pageN,fileN,startDate,endDate,archiveName);
+
+		//2.生成文件页码
+		builtFilePageNo(waitArchiveFiles);
+
+		//3.设置文件所属案卷,组卷状态
+		Long archivesAutoId = archivesAuto.getId();
 		for(ArchiveFile file:waitArchiveFiles){
 			file.setArchiveId(archivesAutoId);//设置文件所属案卷
 			file.setIsArchive(1);
-			//TODO 重新打页码文件
 		}
 		archiveFileClient.updateArchiveFileForCreateArchive(waitArchiveFiles);
 	}
@@ -248,7 +382,9 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		//步骤1:遍历节点集合
 		for(ArchiveTreeContract node:list){
 			//步骤2:获取当前节点的案卷规格
-			int specificationSize=300;//TODO
+			String specificationStr = node.getSpecification();
+			int specification = Integer.parseInt(specificationStr);
+			int specificationSize=specification*10;
 			//步骤3:查询节点下的未组卷文件
 			List<ArchiveFile> archiveFiles = archiveFileClient.listWrappers(Wrappers.<ArchiveFile>lambdaQuery()
 					.eq(ArchiveFile::getNodeId, node)
@@ -331,16 +467,12 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 		}
 
-
-
-
-
 	}
 
 	/**
 	 * 分类并卷组卷  设置分类的节点下只有一个案卷,节点下的所有文件都组成这个案卷。如果设置分类节点(select=1的)多个,案卷归属排序第一个节点。
 	 */
-	private void archiveAutoMethod2(List<ArchiveTreeContract> list){
+	private void archiveAutoMethod2(List<ArchiveTreeContract> list,Long projectId){
 
 		//分类并卷集合<groupId,List<文件>>
 		Map<Long,List<ArchiveFile>> archiveMap = new HashMap<>();
@@ -383,10 +515,10 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		//步骤6:按集合创建案卷,每个group类的案卷归属顺序排第一个节点
 		Set<Map.Entry<Long, List<ArchiveFile>>> entries = archiveMap.entrySet();
 		for(Map.Entry<Long, List<ArchiveFile>> entry:entries){
-			Long key = entry.getKey();
+			Long archiveAutoGroupId = entry.getKey();
 			List<ArchiveFile> archiveFiles = entry.getValue();
-			//TODO 一个key 组成一个案卷  案卷归属同个key的归档树节点select=1的第一个groupId2NodeIdMap
-
+			//一个archiveAutoGroupId组成一个案卷  案卷归属同个key的归档树节点select=1的第一个groupId2NodeIdMap
+			createArchive2(archiveFiles,archiveAutoGroupId,projectId);
 		}
 
 	}
@@ -420,7 +552,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 			if(!archiveAutoNodeId.equals(archiveAutoNodeId_current)) {
 				//超过最高并卷节点(节点是排序好的,只要不相等就超过了)
 				if(waitArchiveFiles.size()>0){
-					//TODO 将待组卷集合里的文件组卷
+					//将待组卷集合里的文件组卷
+					createArchive1(waitArchiveFiles,archivesSize);
 				}
 				waitArchiveFiles.clear();
 				archivesSize=0;
@@ -430,7 +563,9 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 			//步骤3.1:获取节点规格
 			if(waitArchiveFiles.size()==0){
 				//waitArchiveFiles待组卷文件为空时,按当前节点的规格
-				specificationSize=300;//TODO
+				String specificationStr = node.getSpecification();
+				int specification = Integer.parseInt(specificationStr);
+				specificationSize=specification*10;
 			}
 
 			//步骤3.2:查询出当前节点未组卷的文件
@@ -447,7 +582,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 				if(file.getBoxNumber()!=null) {
 					//TODO 走分盒组卷流程
 					if(nodeSize==list.size() && archiveFilesSize==archiveFiles.size()){
-						//TODO 如果当前是最后一个文件直接将waitArchiveFiles组卷
+						//如果当前是最后一个文件直接将waitArchiveFiles组卷
+						createArchive1(waitArchiveFiles,archivesSize);
 					}
 				}else{
 					//步骤3.5 计算和判断待组卷文件集合是否达到组卷要求,达到要求创建案卷,案卷归属当前节点,案卷下文件改为已组卷
@@ -459,7 +595,8 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 						waitArchiveFiles.add(file);
 						//判断是否最后一个节点,最后一个文件
 						if(nodeSize==list.size() && archiveFilesSize==archiveFiles.size()){
-							//TODO 直接将waitArchiveFiles待组卷集合组卷,归属集合第一个文件所在节点
+							//直接将waitArchiveFiles待组卷集合组卷,归属集合第一个文件所在节点
+							createArchive1(waitArchiveFiles,archivesSize);
 							//将待组卷文件集合,总页数还原初始值,
 							waitArchiveFiles.clear();
 							archivesSize=0;
@@ -470,35 +607,45 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 					//步骤3.7达到规格 可以组一卷
 					if(checkStatus==1){
 						waitArchiveFiles.add(file);
-						//TODO 将waitArchiveFiles待组卷集合组卷,归属集合第一个文件所在节点
+						//将waitArchiveFiles待组卷集合组卷,归属集合第一个文件所在节点
+						createArchive1(waitArchiveFiles,archivesSize);
 						//将待组卷文件集合,总页数还原初始值,
 						waitArchiveFiles.clear();
 						archivesSize=0;
-						specificationSize=300;//TODO 更新specificationSize待组卷规格为 当前节点的组卷规格
+
+						String specificationStr = node.getSpecification();
+						int specification = Integer.parseInt(specificationStr);
+						specificationSize=specification*10;//更新specificationSize待组卷规格为 当前节点的组卷规格
 					}
 					//步骤3.8超出规格
 					if(checkStatus==-1){
 						//步骤3.9如果waitArchiveFiles集合size>0,先将集合中文件组卷。再将当前文件加入集合
 						if(waitArchiveFiles.size()>0){
-							//3.9.1 TODO 将waitArchiveFiles组卷,归属集合第一个文件所在节点
+							//3.9.1 将waitArchiveFiles组卷,归属集合第一个文件所在节点
+							createArchive1(waitArchiveFiles,archivesSize);
 							//将待组卷文件集合,总页数还原初始值,
 							waitArchiveFiles.clear();
 							//3.9.2保存当前文件进入待组卷集合,
 							waitArchiveFiles.add(file);
 							//3.9.3待组卷页数=当前文件页数
 							archivesSize=filePage;
-							specificationSize=300;//TODO 更新specificationSize待组卷规格为 当前节点的组卷规格
+
+							String specificationStr = node.getSpecification();
+							int specification = Integer.parseInt(specificationStr);
+							specificationSize=specification*10;//更新specificationSize待组卷规格为 当前节点的组卷规格
+
 							//3.9.4 判断当前文件是不是最后一个节点,最后一个文件
 							if(nodeSize==list.size() && archiveFilesSize==archiveFiles.size()){
-								//TODO 最后一个文件再将waitArchiveFiles组卷
+								//最后一个文件再将waitArchiveFiles组卷
+								createArchive1(waitArchiveFiles,archivesSize);
 							}
 						}else{
 							//步骤3.10 如果waitArchiveFiles集合size=0,说明当前文件已经超过规格,直接将当前文件组卷
 							waitArchiveFiles.add(file);
-							//TODO 直接将waitArchiveFiles组卷
+							//直接将waitArchiveFiles组卷
+							createArchive1(waitArchiveFiles,archivesSize);
 							waitArchiveFiles.clear();
 							archivesSize=0;
-							specificationSize=300;//TODO 更新specificationSize待组卷规格为 当前节点的组卷规格
 						}
 					}
 

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

@@ -91,14 +91,22 @@ public class EVisaTaskCheckController {
 
             //汇总电签配置的审批角色
             List<String> eVisaRoleList = jsonList.stream().map(jsonObject -> jsonObject.getString("sigRoleId")).distinct().collect(Collectors.toList());
+
             //检查
             //循环审批人的角色集合,并判断电签配置中是否含有这个角色
+            List<String> userNameFail = new ArrayList<>();
             for (JSONObject userRole : userRoleList) {
                 if (!eVisaRoleList.contains(userRole.getString("roleId"))) {
                     User user = this.userClient.userInfoById(userRole.getLong("userId")).getData();
-                    return R.data(300, false, "所选中的用户【" + user.getRealName() + "】不具备当前表格所需要的签字岗位,请联系维护人员处理或更换审批人员");
+                    userNameFail.add(user.getRealName());
                 }
             }
+
+            //批量提示
+            if (userNameFail.size() > 0) {
+                return R.data(300, false, "所选中的用户【" + StringUtils.join(userNameFail, ",") + "】不具备当前表格所需要的签字岗位,请联系维护人员处理或更换审批人员");
+            }
+
             //均满足
             return R.data(true);
         }

+ 7 - 0
blade-service/blade-business/src/main/java/org/springblade/business/controller/ImageClassificationFileController.java

@@ -729,4 +729,11 @@ public class ImageClassificationFileController extends BladeController {
         return fileSizeString;
     }
 
+    @GetMapping("/getFileTitleName")
+    @ApiOperationSupport(order = 2)
+    @ApiOperation(value = "获取节点文件题名", notes = "传入节点PkeyId")
+    public R<Object> getFileTitleName(@Valid @RequestParam String pKeyId) {
+        return R.data(this.imageClassificationFileService.getFileTitleName(pKeyId));
+    }
+
 }

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

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import lombok.AllArgsConstructor;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.feign.ArchiveFileClient;
+import org.springblade.business.mapper.ArchiveFileMapper;
 import org.springblade.business.service.IArchiveFileService;
 import org.springblade.business.vo.ArchiveFileVO;
 import org.springframework.web.bind.annotation.RestController;
@@ -22,6 +23,8 @@ public class ArchiveFileClientImpl implements ArchiveFileClient {
 
     private final IArchiveFileService iArchiveFileService;
 
+    private final ArchiveFileMapper fileMapper;
+
 
     @Override
     public void saveArchiveFile(ArchiveFileVO vo) {
@@ -94,4 +97,9 @@ public class ArchiveFileClientImpl implements ArchiveFileClient {
     public void updateArchiveFileForCreateArchive(List<ArchiveFile> waitArchiveFiles) {
         iArchiveFileService.updateBatchById(waitArchiveFiles);
     }
+
+    @Override
+    public List<ArchiveFile> getListByProjectId(Long projectId) {
+        return fileMapper.getListByProjectId(projectId);
+    }
 }

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

@@ -66,4 +66,6 @@ public interface ArchiveFileMapper extends BaseMapper<ArchiveFile> {
 	List<ArchiveFile> getArchiveFileByArchivesId(@Param("ids") List<Long> ids);
 
 	List<ArchiveFile> getArchiveFileByFileIds(@Param("ids") List<Long> ids);
+
+	public List<ArchiveFile> getListByProjectId(@Param("projectId") Long projectId);
 }

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

@@ -268,4 +268,9 @@
         </if>
         order by sort,create_time DESC
     </select>
+    <select id="getListByProjectId" resultType="org.springblade.business.entity.ArchiveFile">
+        select id,project_id as projectId,node_id as nodeId,file_number as fileNumber,file_name as fileName,file_time as fileTime,file_url as fileUrl,pdf_file_url as pdfFileUrl,archive_id as archiveId,status,is_deleted as isDeleted,create_time as createTime
+        from u_archive_file
+        where project_id=#{projectId};
+    </select>
 </mapper>

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

@@ -142,11 +142,11 @@
                CASE
                    WHEN c.allCount = c.savaCont and notExsitChild='false'  THEN 2
                    WHEN (c.allCount+1) = c.savaCont and notExsitChild='true'  THEN 2
-                   WHEN c.allCount = c.submitCount and notExsitChild='false' THEN 3
-                   WHEN (c.allCount+1) = c.submitCount and notExsitChild='true' THEN 3
+                   WHEN c.allCount = c.submitCounts and notExsitChild='false' THEN 3
+                   WHEN (c.allCount+1) = c.submitCounts and notExsitChild='true' THEN 3
                    WHEN c.allCount = c.appCount and notExsitChild='false' THEN 4
                    WHEN (c.allCount+1) = c.appCount and notExsitChild='true' THEN 4
-                   WHEN c.savaCont>=1 or c.submitCount>=1 or c.appCount>=1 THEN 2
+                   WHEN c.savaCont>=1 or c.submitCounts>=1 or c.appCount>=1 THEN 2
                    ELSE 1 END AS colorStatus
         from (
                  SELECT
@@ -172,7 +172,7 @@
 			 ( SELECT count(1) from m_wbs_tree_contract where id in(SELECT parent_id from m_wbs_tree_contract a where a.is_deleted=0 and a.type=2 and table_owner in(1,2,3)) and contract_id=wtc.contract_id and type =1 and is_deleted=0 and (FIND_IN_SET(wtc.id,ancestors) or wtc.p_key_id=p_key_id)
 				) as allCount,
 			 (SELECT count(1) from u_information_query b where b.wbs_id in(SELECT p_key_id from m_wbs_tree_contract a where (FIND_IN_SET(wtc.id,a.ancestors) or wtc.p_key_id=a.p_key_id) and a.is_deleted=0 and a.`status`='0' ) and b.is_deleted=0 and b.classify=1) as savaCont,
-			 (SELECT count(1) from u_information_query b where b.wbs_id in(SELECT p_key_id from m_wbs_tree_contract a where (FIND_IN_SET(wtc.id,a.ancestors) or wtc.p_key_id=a.p_key_id) and a.is_deleted=0 and a.`status`='1' ) and b.is_deleted=0 and b.classify=1) as submitCount,
+			 (SELECT count(1) from u_information_query b where b.wbs_id in(SELECT p_key_id from m_wbs_tree_contract a where (FIND_IN_SET(wtc.id,a.ancestors) or wtc.p_key_id=a.p_key_id) and a.is_deleted=0 and a.`status`='1' ) and b.is_deleted=0 and b.classify=1) as submitCounts,
 			 (SELECT count(1) from u_information_query b where b.wbs_id in(SELECT p_key_id from m_wbs_tree_contract a where (FIND_IN_SET(wtc.id,a.ancestors) or wtc.p_key_id=a.p_key_id) and a.is_deleted=0 and a.`status`='2' ) and b.is_deleted=0 and b.classify=1) as appCount
 				FROM
         m_wbs_tree_contract AS wtc

+ 3 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/IImageClassificationFileService.java

@@ -53,4 +53,7 @@ public interface IImageClassificationFileService extends BaseService<ImageClassi
 	 * 根据ids查找已经删除的数据
 	 */
 	List<ImageClassificationFile> getDeleteDataByIds(List<String> ids);
+
+    String getFileTitleName(String pKeyId);
+
 }

+ 136 - 86
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/ImageClassificationFileServiceImpl.java

@@ -30,8 +30,11 @@ import org.springblade.business.service.IImageClassificationFileService;
 import org.springblade.business.vo.TreeVo;
 import org.springblade.common.utils.CommonUtil;
 import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.manager.entity.WbsParam;
 import org.springblade.manager.entity.WbsTreeContract;
 import org.springblade.manager.feign.WbsTreeContractClient;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 
@@ -39,7 +42,7 @@ import java.util.*;
 import java.util.stream.Collectors;
 
 /**
- *  服务实现类
+ * 服务实现类
  *
  * @author BladeX
  * @since 2022-05-24
@@ -47,91 +50,138 @@ import java.util.stream.Collectors;
 @Service
 @AllArgsConstructor
 public class ImageClassificationFileServiceImpl extends BaseServiceImpl<ImageClassificationFileMapper, ImageClassificationFile> implements IImageClassificationFileService {
-	private final WbsTreeContractClient wbsTreeContractClient;
-	@Override
-	public List<Integer> queryCurrentContractImageFileType(String contractId) {
-		return this.baseMapper.queryCurrentContractImageFileType(contractId);
-	}
-
-	@Override
-	public Integer queryCurrentClassifyAllFileCount(String projectId, String contractId, Long classifyId) {
-		return this.baseMapper.queryCurrentClassifyAllFileCount(projectId, contractId, classifyId);
-	}
-
-	@Override
-	public List<TreeVo> getYearDateTree(String classifyId, String projectId, String contractId) {
-		//获取时间
-		List<Date> shootingTimes = this.baseMapper.selectShootingTimeByClassifyAndProjectId(classifyId, projectId, contractId);
-		//转换时间
-		List<String> yearMonthDayList = new ArrayList<>();
-		shootingTimes.forEach(date -> yearMonthDayList.add(DateFormatUtils.format(date, "yyyy-MM-dd")));
-		//转成结构树
-		return YearTreeUtils.yearMonthDayTree(yearMonthDayList, "DESC");
-	}
-
-	@Override
-	public IPage<ImageClassificationFileVO> selectImageClassificationFilePage(IPage<ImageClassificationFileVO> page, ImageClassificationFileVO imageClassificationFile) {
-		long current = (page.getCurrent() - 1L) * page.getSize();
-		if(StringUtils.isNotEmpty(imageClassificationFile.getWbsIdsStr())){
-			//查询下级节点信息
+
+    private final WbsTreeContractClient wbsTreeContractClient;
+    private final JdbcTemplate jdbcTemplate;
+
+    @Override
+    public List<Integer> queryCurrentContractImageFileType(String contractId) {
+        return this.baseMapper.queryCurrentContractImageFileType(contractId);
+    }
+
+    @Override
+    public Integer queryCurrentClassifyAllFileCount(String projectId, String contractId, Long classifyId) {
+        return this.baseMapper.queryCurrentClassifyAllFileCount(projectId, contractId, classifyId);
+    }
+
+    @Override
+    public List<TreeVo> getYearDateTree(String classifyId, String projectId, String contractId) {
+        //获取时间
+        List<Date> shootingTimes = this.baseMapper.selectShootingTimeByClassifyAndProjectId(classifyId, projectId, contractId);
+        //转换时间
+        List<String> yearMonthDayList = new ArrayList<>();
+        shootingTimes.forEach(date -> yearMonthDayList.add(DateFormatUtils.format(date, "yyyy-MM-dd")));
+        //转成结构树
+        return YearTreeUtils.yearMonthDayTree(yearMonthDayList, "DESC");
+    }
+
+    @Override
+    public IPage<ImageClassificationFileVO> selectImageClassificationFilePage(IPage<ImageClassificationFileVO> page, ImageClassificationFileVO imageClassificationFile) {
+        long current = (page.getCurrent() - 1L) * page.getSize();
+        if (StringUtils.isNotEmpty(imageClassificationFile.getWbsIdsStr())) {
+            //查询下级节点信息
 //			imageClassificationFile.setWbsIds(JSONArray.parseArray(JSONObject.toJSONString(imageClassificationFile.getWbsIdsStr().split(",")), String.class));
-			WbsTreeContract node = wbsTreeContractClient.getContractWbsTreeByPrimaryKeyId(Long.valueOf(imageClassificationFile.getWbsIdsStr()));
-			List<WbsTreeContract> wbsTreeContracts = new ArrayList<>();
-			if (node.getParentId()==0){
-				wbsTreeContracts = wbsTreeContractClient.queryAllChild(imageClassificationFile.getContractId());
-			}else {
-				wbsTreeContracts = wbsTreeContractClient.queryCurrentNodeAllChild(imageClassificationFile.getContractId(), node.getId());
-			}
-			wbsTreeContracts.add(node);
-			imageClassificationFile.setWbsIds(wbsTreeContracts.stream().map(wtc->wtc.getPKeyId()+"").collect(Collectors.toList()));
-		}
-		//获取数据
-		List<ImageClassificationFile> fileVOS = this.baseMapper.selectImageClassificationFilePage(current, page.getSize(), imageClassificationFile);
-		if(fileVOS != null && fileVOS.size() > 0){
-			//分组
-			List<List<ImageClassificationFile>> group = CommonUtil.getBatchSize(fileVOS, new Integer(String.valueOf(page.getSize())));
-
-			//获取数据并设置分页信息
-			List<ImageClassificationFile> result = group.get(new Integer(String.valueOf(page.getCurrent())) - 1);
-			//转换类型
-			List<ImageClassificationFileVO> resultVo = JSONArray.parseArray(JSONObject.toJSONString(result), ImageClassificationFileVO.class);
-
-			//处理文件大小单位
-			resultVo.forEach(vo -> {
-				//获取文件大小
-				String fileSize = vo.getFileSize();
-				if(StringUtils.isNotEmpty(fileSize) && CommonUtil.checkIsBigDecimal(fileSize)){
-					long size = new Long(fileSize);
-					if(size < 1024L){
-						vo.setFileSize(vo.getFileSize() + "B");
-					} else {
-						vo.setFileSize((size / 1024) + "K");
-					}
-				}
-			});
-
-			page.setTotal(fileVOS.size());
-			page.setRecords(resultVo);
-		}
-
-		return page;
-	}
-
-	/**
-	 * 恢复删除
-	 * @param ids
-	 * @return
-	 */
-	@Override
-	public Boolean recoveryByIds(List<String> ids) {
-		return baseMapper.recoveryByIds(ids);
-	}
-	/**
-	 * 根据ids查找已经删除的数据
-	 */
-	@Override
-	public List<ImageClassificationFile> getDeleteDataByIds(List<String> ids) {
-		return baseMapper.getDeleteDataByIds(ids);
-	}
+            WbsTreeContract node = wbsTreeContractClient.getContractWbsTreeByPrimaryKeyId(Long.valueOf(imageClassificationFile.getWbsIdsStr()));
+            List<WbsTreeContract> wbsTreeContracts = new ArrayList<>();
+            if (node.getParentId() == 0) {
+                wbsTreeContracts = wbsTreeContractClient.queryAllChild(imageClassificationFile.getContractId());
+            } else {
+                wbsTreeContracts = wbsTreeContractClient.queryCurrentNodeAllChild(imageClassificationFile.getContractId(), node.getId());
+            }
+            wbsTreeContracts.add(node);
+            imageClassificationFile.setWbsIds(wbsTreeContracts.stream().map(wtc -> wtc.getPKeyId() + "").collect(Collectors.toList()));
+        }
+        //获取数据
+        List<ImageClassificationFile> fileVOS = this.baseMapper.selectImageClassificationFilePage(current, page.getSize(), imageClassificationFile);
+        if (fileVOS != null && fileVOS.size() > 0) {
+            //分组
+            List<List<ImageClassificationFile>> group = CommonUtil.getBatchSize(fileVOS, new Integer(String.valueOf(page.getSize())));
+
+            //获取数据并设置分页信息
+            List<ImageClassificationFile> result = group.get(new Integer(String.valueOf(page.getCurrent())) - 1);
+            //转换类型
+            List<ImageClassificationFileVO> resultVo = JSONArray.parseArray(JSONObject.toJSONString(result), ImageClassificationFileVO.class);
+
+            //处理文件大小单位
+            resultVo.forEach(vo -> {
+                //获取文件大小
+                String fileSize = vo.getFileSize();
+                if (StringUtils.isNotEmpty(fileSize) && CommonUtil.checkIsBigDecimal(fileSize)) {
+                    long size = new Long(fileSize);
+                    if (size < 1024L) {
+                        vo.setFileSize(vo.getFileSize() + "B");
+                    } else {
+                        vo.setFileSize((size / 1024) + "K");
+                    }
+                }
+            });
+
+            page.setTotal(fileVOS.size());
+            page.setRecords(resultVo);
+        }
+
+        return page;
+    }
+
+    /**
+     * 恢复删除
+     *
+     * @param ids
+     * @return
+     */
+    @Override
+    public Boolean recoveryByIds(List<String> ids) {
+        return baseMapper.recoveryByIds(ids);
+    }
+
+    /**
+     * 根据ids查找已经删除的数据
+     */
+    @Override
+    public List<ImageClassificationFile> getDeleteDataByIds(List<String> ids) {
+        return baseMapper.getDeleteDataByIds(ids);
+    }
+
+    @Override
+    public String getFileTitleName(String pKeyId) {
+        WbsTreeContract wbsTreeContract = jdbcTemplate.query("select id,ancestors,contract_id,project_id,wbs_id from m_wbs_tree_contract where is_deleted = 0 and status = 1 and p_key_id = " + pKeyId, new BeanPropertyRowMapper<>(WbsTreeContract.class)).stream().findAny().orElse(null);
+        if (wbsTreeContract != null) {
+            WbsParam wbsParam = jdbcTemplate.query("select v from m_wbs_param where is_deleted = 0 and status = 1 and v is not null and k = 'FILE_TITLE' and name = '文件题名' and node_id = " + wbsTreeContract.getId(), new BeanPropertyRowMapper<>(WbsParam.class)).stream().findAny().orElse(null);
+            if (wbsParam != null) {
+                String[] titles = wbsParam.getV().split("-");
+                List<String> nodeNumber = new ArrayList<>();
+                for (String title : titles) {
+                    if (title.contains("c") || title.contains("C")) {
+                        String lastStr = title.substring(title.length() - 1);
+                        nodeNumber.add(lastStr);
+                    }
+                }
+
+                List<String> ancestors = Arrays.asList(wbsTreeContract.getAncestors().split(","));
+                /*ancestors = new ArrayList<>(ancestors); // 将固定大小的列表复制到新的可变列表中
+                ancestors.removeIf(("0")::equals); //删除根节点*/
+
+                List<String> ids = new ArrayList<>();
+                for (String index : nodeNumber) {
+                    if ("0".equals(index)) {
+                        index = "1";
+                    }
+                    if (Integer.parseInt(index) <= ancestors.size()) {
+                        String id = ancestors.get(Integer.parseInt(index));
+                        if (StringUtils.isNotEmpty(id)) {
+                            ids.add(id);
+                        }
+                    }
+                }
+
+                List<WbsTreeContract> wbsTreeContractList = jdbcTemplate.query("select node_name from m_wbs_tree_contract where is_deleted = 0 and status = 1 and project_id = " + wbsTreeContract.getProjectId() + " and contract_id = " + wbsTreeContract.getContractId() + " and wbs_id = " + wbsTreeContract.getWbsId() + " and id in(" + StringUtils.join(ids, ",") + ")", new BeanPropertyRowMapper<>(WbsTreeContract.class));
+                List<String> nameList = wbsTreeContractList.stream().map(WbsTreeContract::getNodeName).collect(Collectors.toList());
+                if (nameList.size() > 0) {
+                    return StringUtils.join(nameList, "");
+                }
+            }
+        }
+        return null;
+    }
 
 }

+ 37 - 32
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java

@@ -551,7 +551,7 @@ public class ExcelTabController extends BladeController {
                                 .attr("maxlength", filedLength);
                         if(element.html().indexOf("el-input") >= 0){
                             String mousedown = "$event" + "," + "'" + attrInfo + "'";
-                            element.children().get(0).attr("@mousedown.left", "inputLeftClick("+mousedown+")");
+                            element.children().get(0).attr("@mouseup.left", "inputLeftClick("+mousedown+")");
                         }
                     } else {
                         element.children().get(0).attr("placeholder", lastName)
@@ -1075,7 +1075,7 @@ public class ExcelTabController extends BladeController {
                         ExctabCell exctabCell = new ExctabCell();
                         if ((textInfo.indexOf("年") >= 0 && textInfo.indexOf("月") >= 0 && textInfo.indexOf("日") >= 0) || inputText.indexOf("日期") >= 0) {
                             if (inputText.indexOf("日期") >= 0) {
-                                data.empty().append("<el-date-picker type='date' @keyDowns='dateKeydown' format='YYYY年MM月DD日' value-format='YYYY年MM月DD日' @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder='" + inputText + "'> </el-date-picker>");
+                                data.empty().append("<el-date-picker type='date' @keyDowns='dateKeydown' format='YYYY年MM月DD日' value-format='YYYY年MM月DD日' @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder='" + inputText + "'> </el-date-picker>");
                             } else if (textInfo.indexOf("年") >= 0 && textInfo.indexOf("月") >= 0 && textInfo.indexOf("日") >= 0) {
                                 if (inputText.indexOf("专业监理工程师") >= 0) {
                                     inputText = "专业监理工程师_年月日";
@@ -1085,7 +1085,7 @@ public class ExcelTabController extends BladeController {
                                     inputText = "年月日";
                                 }
                             }
-                            data.empty().append("<el-date-picker @keyDowns='dateKeydown'  type='date' format='YYYY年MM月DD日' value-format='YYYY年MM月DD日' @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder='年月日'> </el-date-picker>");
+                            data.empty().append("<el-date-picker @keyDowns='dateKeydown'  type='date' format='YYYY年MM月DD日' value-format='YYYY年MM月DD日' @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder='年月日'> </el-date-picker>");
                             exctabCell.setTextInfo(inputText);
                             exctabCell.setExctabId(excelId);
                             exctabCell.setIsDeleted(0);
@@ -1122,32 +1122,32 @@ public class ExcelTabController extends BladeController {
                                 objs.add(jsonObject);
                             }
 
-                            String checkbox = "<hc-form-checkbox-group @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' :objs='" + objs + "'  @change='checkboxGroupChange' @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " placeholder=''> </hc-form-checkbox-group>";
+                            String checkbox = "<hc-form-checkbox-group @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' :objs='" + objs + "'  @change='checkboxGroupChange' @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " placeholder=''> </hc-form-checkbox-group>";
                             data.empty().append(checkbox);
 
                         } else {
                             if (index_state) { // 区域内
                                 if (rowspan >= 1) {
-                                    data.empty().append("<el-input type='textarea' @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft'  @keydown.shift.right='keyupShiftRight'  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;'   :rows=" + rowspan * 2 + " placeholder=''> </el-input>");
+                                    data.empty().append("<el-input type='textarea' @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft'  @keydown.shift.right='keyupShiftRight'  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;'   :rows=" + rowspan * 2 + " placeholder=''> </el-input>");
                                 } else {
-                                    data.empty().append("<el-input type='text' @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft'  @keydown.shift.right='keyupShiftRight'  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder=''> </el-input>");
+                                    data.empty().append("<el-input type='text' @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft'  @keydown.shift.right='keyupShiftRight'  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder=''> </el-input>");
                                 }
                             } else { // 区域外
                                 if (j == 0) {
                                     if (colspan == maxCol && i >= 1) {
                                         if (rowspan >= 1) {
-                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='textarea'  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;'   :rows=" + rowspan * 2 + " placeholder=''> </el-input>");
+                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='textarea'  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;'   :rows=" + rowspan * 2 + " placeholder=''> </el-input>");
                                         } else {
-                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='text' @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder=''> </el-input>");
+                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='text' @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder=''> </el-input>");
                                         }
                                     }
                                 } else {
                                     Element bforData = tds.get(j - 1);
                                     if (!bforData.text().isEmpty() || bforData.html().indexOf("hc-form-checkbox-group") >= 0) {
                                         if (rowspan >= 1) {
-                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='textarea' @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;'   :rows=" + rowspan * 2 + " placeholder=''> </el-input>");
+                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='textarea' @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;'   :rows=" + rowspan * 2 + " placeholder=''> </el-input>");
                                         } else {
-                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft'  @keydown.shift.right='keyupShiftRight' type='text' @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder=''> </el-input>");
+                                            data.empty().append("<el-input @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft'  @keydown.shift.right='keyupShiftRight' type='text' @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + i + " tdIndex=" + j + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%;' placeholder=''> </el-input>");
                                         }
                                     }
                                 }
@@ -1605,7 +1605,7 @@ public class ExcelTabController extends BladeController {
     @ApiOperation(value = "填报页面数据保存", notes = "填报页面数据保存")
     public R<String> saveBussData(@Valid @RequestBody JSONObject dataInfo, BladeUser bladeUser) throws Exception {
         JSONArray dataArray = new JSONArray();
-
+        System.out.println("1=" + DateUtil.formatDateTime(DateUtil.now()));
         if (dataInfo.containsKey("dataInfo")) { // 节点保存
             JSONObject jsonObject = dataInfo.getJSONObject("dataInfo");
             dataArray = jsonObject.getJSONArray("orderList");
@@ -1631,23 +1631,7 @@ public class ExcelTabController extends BladeController {
             groupIds += "," + jsonObject.getString("tabGroupId");
             ;
         }
-        /*
-        if (StringUtils.isNotEmpty(groupIds)) {
-            List<WbsTreeContract> wbsTreeContractList = this.wbsTreeContractService.getBaseMapper().selectList(
-                    Wrappers.<WbsTreeContract>lambdaQuery()
-                            .in(WbsTreeContract::getTabGroupId, groupIds)
-                            .notIn(WbsTreeContract::getPKeyId, pkeyIds)
-                            .eq(WbsTreeContract::getIsDeleted, BladeConstant.DB_NOT_DELETED)
-            );
-            if (wbsTreeContractList != null && wbsTreeContractList.size() >= 1) {
-                for (WbsTreeContract data : wbsTreeContractList) {
-                    R bussDataInfo = this.excelTabService.getBussDataInfo(data.getPKeyId(),1);
-                    Object data1 = bussDataInfo.getData();
-                    dataArray.add(data1);
-                }
-            }
-        }
-       */
+        System.out.println("2=" + DateUtil.formatDateTime(DateUtil.now()));
         /*全加载,或者可以优化成依赖加载*/
         List<TableInfo> tableInfoList = this.excelTabService.getTableInfoList(dataArray);
         if (tableInfoList != null) {
@@ -1692,15 +1676,18 @@ public class ExcelTabController extends BladeController {
         if (!info.isSuccess()) {
             return info;
         }
-
+        System.out.println("3=" + DateUtil.formatDateTime(DateUtil.now()));
         //单个 pdf加载
         for (TableInfo tableInfo : tableInfoList) {
-            excelTabService.getBussPdfInfo(Long.parseLong(tableInfo.getPkeyId()));
-        }
+            if(!tableInfo.isToBeUpdated()){
+                excelTabService.getBussPdfInfo(Long.parseLong(tableInfo.getPkeyId()));
+            }
 
+        }
+        System.out.println("4=" + DateUtil.formatDateTime(DateUtil.now()));
         // 合并pdf加载
         excelTabService.getBussPdfs(nodeid, classify, contractId, projectId);
-
+        System.out.println("5=" + DateUtil.formatDateTime(DateUtil.now()));
         return R.data("操作成功");
     }
 
@@ -2957,4 +2944,22 @@ public class ExcelTabController extends BladeController {
         }
         return R.data(res);
     }
+
+
+    /**
+     * 手动修改合同tree 节点错乱问题
+     */
+    @GetMapping("/asyContrantById")
+    @ApiOperationSupport(order = 37)
+    @ApiOperation(value = "修改合同tree", notes = "修改合同tree")
+    @ApiImplicitParams(value = {
+            @ApiImplicitParam(name = "contractId", value = "contractId", required = true)
+    })
+    public R getHtmlBussCols(String contractId){
+        excelTabService.updateContractById(contractId);
+
+
+        return R.data("成功");
+    }
+
 }

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

@@ -298,9 +298,9 @@ public class FormulaController {
                         break;
                     }
                 }
-                if(data.size()>dw.length){
+                if((data.size()+start)>dw.length){
                    // String moreData = data.stream().skip(dw.length).map(StringUtils::handleNull).collect(Collectors.joining(StringPool.SEMICOLON));
-                    List<Object> moreData = data.stream().skip(dw.length).map(StringUtils::handleNull).collect(Collectors.toList());
+                    List<Object> moreData = data.stream().skip(result.size()).map(StringUtils::handleNull).collect(Collectors.toList());
                     // 频率添加表单
                     this.wbsTreeContractService.addTabInfoByRan(info,moreData,dw);
                 }

+ 11 - 9
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/TextdictInfoController.java

@@ -256,12 +256,14 @@ public class TextdictInfoController extends BladeController {
         String oncklickText = "'" + placeholder + "'," + trindex + "," + tdindex;
 
         String vmode = "formData." + keyname;
+        String leftCli = "inputLeftClick($event,'" + keyname + "')";
         if (textdictInfo.getTextId().equals("input")) { // 文本框
-            element.empty().append("<el-input  @mousedown.left='inputLeftClick($event, '"+keyname+"')' type='text' id=" + keyname + " @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' v-model=" + vmode + " placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%' > </el-input>");
+            element.empty().append("<el-input  @mouseup.left="+leftCli+" type='text' id=" + keyname + " @keydown.shift.up='keyupShiftUp'  @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' v-model=" + vmode + " placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%' > </el-input>");
 
         } else if (textdictInfo.getTextId().equals("textarea")) { // 文本域
             int rowspan = element.attr("ROWSPAN").equals("") ? 0 : Integer.parseInt(element.attr("ROWSPAN"));
-            element.empty().append("<el-input  :rows=" + rowspan * 2 + " id=" + keyname + " @mousedown.left='inputLeftClick($event, '"+keyname+"')' @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight'  type='textarea' placeholder=" + placeholder + " v-model=" + vmode + "    keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'  > </el-input>");
+
+            element.empty().append("<el-input  :rows=" + rowspan * 2 + "  id=" + keyname + "  @mouseup.left="+leftCli+"  @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight'  type='textarea' placeholder=" + placeholder + " v-model=" + vmode + "    keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'  > </el-input>");
 
         } else if (textdictInfo.getTextId().equals("select")) { // 下拉框
             String selectText = " <el-select id=" + keyname + " v-model=" + vmode + " @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' keyname=" + keyname + " weighing=" + weighing + " placeholder=" + placeholder + " trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + ">"; //v-model="+keyname+"
@@ -297,17 +299,17 @@ public class TextdictInfoController extends BladeController {
                 element.empty().append(checkbox);
             }
         } else if (textdictInfo.getTextId().equals("date")) { // 日期--年月日时分秒
-            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='datetime' format='YYYY年MM月DD日 HH:mm:ss' value-format='YYYY年MM月DD日 hh:mm:ss' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-date-picker>");
+            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='datetime' format='YYYY年MM月DD日 HH:mm:ss' value-format='YYYY年MM月DD日 hh:mm:ss' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-date-picker>");
         } else if (textdictInfo.getTextId().equals("dateYMD")) { // 日期--年月日
-            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='date' format='YYYY年MM月DD日' value-format='YYYY年MM月DD日' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-date-picker>");
+            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='date' format='YYYY年MM月DD日' value-format='YYYY年MM月DD日' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-date-picker>");
         } else if (textdictInfo.getTextId().equals("dateHMS")) { // 日期--时分秒
-            element.empty().append("<el-time-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='date' format='HH:mm:ss' value-format='HH:mm:ss' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
+            element.empty().append("<el-time-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='date' format='HH:mm:ss' value-format='HH:mm:ss' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
         } else if (textdictInfo.getTextId().equals("dateSM")) { // 日期--时分
-            element.empty().append("<el-time-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='date' format='HH:mm' value-format='HH:mm' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
+            element.empty().append("<el-time-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='date' format='HH:mm' value-format='HH:mm' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
         } else if (textdictInfo.getTextId().equals("dateMDHM")) { // 日期--月日时分
-            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='datetime' format='MM月DD日 HH:mm' value-format='MM月DD日 HH:mm' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
+            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='datetime' format='MM月DD日 HH:mm' value-format='MM月DD日 HH:mm' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
         } else if (textdictInfo.getTextId().equals("dateDHM")) { // 日期--日时分
-            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='datetime' format='DD日 HH:mm' value-format='DD日 HH:mm' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
+            element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='datetime' format='DD日 HH:mm' value-format='DD日 HH:mm' placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%'   placeholder='" + placeholder + "'> </el-time-picker>");
         } else if (textdictInfo.getTextId().equals("daterange")) { // 时间段
             element.empty().append("<el-date-picker @keyDowns='dateKeydown' v-model=" + vmode + " type='datetimerange' placeholder=" + placeholder + "  start-placeholder='开始日期'  end-placeholder='结束日期' format='YYYY年MM月DD日' trIndex=" + trindex + " keyname=" + keyname + " weighing=" + weighing + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + ">");
             element.children().get(0).attr("@change", "datePickerChange($event,'" + keyname + "')");
@@ -318,7 +320,7 @@ public class TextdictInfoController extends BladeController {
         } else if (textdictInfo.getTextId().equals("img")) {
             element.empty().append("<hc-table-form-upload @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight'  :src='" + vmode + "' placeholder=" + placeholder + " v-model=" + vmode + "  keyName=" + keyname + " weighing=" + weighing + "  @success='formUploadSuccess' @del='delTableFormFile' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + "></hc-table-form-upload> ");
         } else if (textdictInfo.getTextId().equals("searchSelect")) { //搜索框
-            element.empty().append("<hc-form-select-search id=" + keyname + " @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='dap_site_data' :val=" + vmode + " contractId=''  pkeyId='' @change='formRemoteChange' v-model=" + vmode + " placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%' > </hc-form-select-search>");
+            element.empty().append("<hc-form-select-search id=" + keyname + " @keydown.shift.up='keyupShiftUp' @keydown.shift.down='keyupShiftDown' @keydown.shift.left='keyupShiftLeft' @keydown.shift.right='keyupShiftRight' type='dap_site_data' :val=" + vmode + " contractId=''  pkeyId='' @change='formRemoteChange' v-model=" + vmode + " placeholder=" + placeholder + " keyname=" + keyname + " weighing=" + weighing + "  @contextmenu.prevent.native='contextmenuClick("+parm+")'  @mouseup.right='RightClick(" + parm + ")' trIndex=" + trindex + " tdIndex=" + tdindex + "  x1=" + x1 + " x2=" + x2 + " y1=" + y1 + " y2=" + y2 + " style='width:100%;height:100%' > </hc-form-select-search>");
         }
         element.attr("@click", "getInformation(" + oncklickText + ")");
         File writefile = new File(wbsTreePrivate.getHtmlUrl());

+ 16 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/feign/ArchiveTreeContractImpl.java

@@ -44,5 +44,21 @@ public class ArchiveTreeContractImpl implements ArchiveTreeContractClient {
         return list;
     }
 
+    @Override
+    public List<ArchiveTreeContract> getListByProjectId(Long projectId) {
+        return archiveTreeContractMapper.getListByProjectId(projectId);
+    }
+
+    @Override
+    public List<ArchiveTreeContract> getSelectNodeByGroupId(Long projectId,Long archiveAutoGroupId) {
+        List<ArchiveTreeContract> list = archiveTreeContractMapper.getSelectNodeByGroupId(projectId,archiveAutoGroupId);
+        return list;
+    }
+
+    @Override
+    public ArchiveTreeContract getArchiveTreeContractById(Long id) {
+        return null;
+    }
+
 
 }

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

@@ -68,4 +68,8 @@ public interface ArchiveTreeContractMapper extends BaseMapper<ArchiveTreeContrac
 
 
 	List<ArchiveTreeContract> getHavedFileNodeByProjectID(@Param("projectId") Long projectId);
+
+	List<ArchiveTreeContract> getListByProjectId(@Param("projectId")Long projectId);
+
+	List<ArchiveTreeContract> getSelectNodeByGroupId(@Param("projectId") Long projectId,@Param("archiveAutoGroupId") Long archiveAutoGroupId);
 }

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

@@ -310,5 +310,26 @@
         )
         order by tree_sort asc
     </select>
+    <select id="getListByProjectId" resultType="org.springblade.manager.entity.ArchiveTreeContract">
+        select id,project_id as projectId,parent_id as parentId,ancestors,node_name as nodeName,status,is_deleted as isDeleted,create_time as createTime
+        from m_archive_tree_contract
+        where project_id=#{projectId};
+    </select>
+
+
+    <select id="getSelectNodeByGroupId" resultMap="archiveTreeContractResultMap">
+        SELECT
+            *
+        FROM
+            m_archive_tree_contract
+        WHERE
+            1 = 1
+          AND project_id = #{projectId}
+          AND is_deleted = 0
+          AND archive_auto_type =2
+          AND archive_auto_group_select =1
+          AND archive_auto_group_id =#{archiveAutoGroupId}
+        order by tree_sort asc
+    </select>
 
 </mapper>

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

@@ -73,6 +73,7 @@
         <result column="key" property="key"/>
         <result column="has_children" property="hasChildren"/>
         <result column="node_type" property="nodeType"/>
+        <result column="old_id" property="oldId"/>
         <result column="sort" property="sort"/>
     </resultMap>
 
@@ -448,6 +449,7 @@
         d.node_type AS "nodeType",
         d.id AS "value",
         d.id AS "key",
+        old_id,
         sort
         FROM m_wbs_tree_contract d
         WHERE

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

@@ -576,7 +576,8 @@
                 major_data_type = #{item.majorDataType},
                 table_type = #{item.tableType},
                 table_owner = #{item.tableOwner},
-                html_url = #{item.htmlUrl}
+                html_url = #{item.htmlUrl},
+                sort = #{item.sort}
             </set>
             WHERE id = #{item.id}
             AND project_id = #{item.projectId}

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

@@ -295,7 +295,8 @@
                 mix_ratio_test_ids = #{item.mixRatioTestIds},
                 init_table_name = #{item.initTableName},
                 init_table_id = #{item.initTableId},
-                html_url = #{item.htmlUrl}
+                html_url = #{item.htmlUrl},
+                sort = #{item.sort}
             </set>
             WHERE id = #{item.id}
             AND project_id = #{item.projectId}

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

@@ -162,4 +162,7 @@ public interface IExcelTabService extends BaseService<ExcelTab> {
      * 在线excel 保存回调
      */
     ExcelEditCallback callbackSave(ExcelEditCallback callback);
+
+    // 校验合同段数据问题
+    R updateContractById(String contractId);
 }

+ 15 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArTreeContractInitServiceImpl.java

@@ -236,6 +236,11 @@ public class ArTreeContractInitServiceImpl {
             treeContractVO2.setParentId(archiveTreeContract.getParentId());
             treeContractVO2.setTitle(archiveTreeContract.getNodeName());
             treeContractVO2.setSort(archiveTreeContract.getSort());
+
+            treeContractVO2.setArchiveAutoNodeId(archiveTreeContract.getArchiveAutoNodeId());
+            treeContractVO2.setArchiveAutoType(archiveTreeContract.getArchiveAutoType());
+            treeContractVO2.setArchiveAutoGroupId(archiveTreeContract.getArchiveAutoGroupId());
+
             archiveTreeContractVO2List.add(treeContractVO2);
             vo2Map.put(treeContractVO2.getId(),treeContractVO2);
         }
@@ -448,6 +453,16 @@ public class ArTreeContractInitServiceImpl {
                 archiveTree.setParentId(oldNewMap.get(wbsTreeVO2.getParentId()));
             }
 
+//            archiveTree.setId(wbsTreeVO2.getId());
+//            if (wbsTreeVO2.getParentId() == 0) {
+//                archiveTree.setParentId(subTree.getId());
+//            }else {
+//                archiveTree.setParentId(wbsTreeVO2.getParentId());
+//            }
+            if (StringUtils.isNotEmpty(wbsTreeVO2.getOldId())) {
+                archiveTree.setFromId(Long.parseLong(wbsTreeVO2.getOldId()));
+            }
+
             if (archiveTree.getParentId() == null) {
                 continue;
             }

+ 94 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveAutoRuleSyncImpl.java

@@ -0,0 +1,94 @@
+package org.springblade.manager.service.impl;
+
+
+import lombok.AllArgsConstructor;
+import org.springblade.manager.entity.ArchiveTree;
+import org.springblade.manager.entity.ArchiveTreeContract;
+import org.springblade.manager.utils.ForestNodeMergerEx;
+import org.springblade.manager.vo.ArchiveTreeContractVO2;
+import org.springblade.manager.vo.ArchiveTreeVO2;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+@Service
+@AllArgsConstructor
+public class ArchiveAutoRuleSyncImpl {
+
+    private static Map<String, String> syncPropertyMap;
+    static {
+        syncPropertyMap = new HashMap<>();
+        syncPropertyMap.put("archiveAutoNodeId", "");
+
+    }
+
+    public void syncArchiveTreeList(List<ArchiveTreeVO2> archiveTreeVO2s){
+
+        List<ArchiveTreeVO2> normalList = new ArrayList<>();
+        List<ArchiveTreeVO2> wbsList = new ArrayList<>();
+
+        ForestNodeMergerEx.setPropertyfromId(archiveTreeVO2s,syncPropertyMap);
+        //todo group
+
+    }
+
+    public void syncArchiveTreeContractList(List<ArchiveTreeContractVO2> archiveTreeContractVO2s){
+
+        List<ArchiveTreeContractVO2> normalList = new ArrayList<>();
+        List<ArchiveTreeContractVO2> wbsList = new ArrayList<>();
+        List<ArchiveTreeContractVO2> allWbsList = new ArrayList<>();
+        Map<Long,Long> wbsIdMap = new HashMap<>();
+
+        for (ArchiveTreeContractVO2 archiveTreeContractVO2: archiveTreeContractVO2s) {
+            if (archiveTreeContractVO2.getExtType()!= null && archiveTreeContractVO2.getExtType() == 1) {
+                wbsList.add(archiveTreeContractVO2);
+            }else {
+                normalList.add(archiveTreeContractVO2);
+            }
+        }
+
+        //根据formId
+        ForestNodeMergerEx.setPropertyfromId(normalList,syncPropertyMap);
+
+        //todo,这里补充上获取对应的wbsid
+        List<Long> wbsIds = new ArrayList<>();
+        for (Long wbsId : wbsIds){
+            wbsIdMap.put(wbsId,0l);
+        }
+
+        //找出所有相关的ID
+        for (ArchiveTreeContractVO2 wbsNode : wbsList) {
+            if (wbsNode.getFromId() == null) {
+                if (wbsIdMap.get(wbsNode.getExtId())!= null ) {
+                    allWbsList.add(wbsNode);
+                }
+
+            }else {
+                if (wbsIdMap.get(wbsNode.getFromId())!= null ) {
+                    allWbsList.add(wbsNode);
+                }
+            }
+        }
+
+        if (allWbsList != null && allWbsList.size() > 0) {
+            //按照树的顺序从上王下排序
+            allWbsList.sort(Comparator.comparing(ArchiveTreeContractVO2::getTreeSort));
+            for (ArchiveTreeContractVO2 wbsNode : wbsList){
+                List<ArchiveTreeContractVO2> tempList = new ArrayList<>();
+                ForestNodeMergerEx.getTreeList(wbsNode,tempList);
+                for (ArchiveTreeContractVO2 node: tempList) {
+                    node.setArchiveAutoNodeId(wbsNode.getId());
+                }
+            }
+        }
+
+
+
+        //todo group
+        for (ArchiveTreeContractVO2 archiveTreeContractVO2 : archiveTreeContractVO2s) {
+            if (archiveTreeContractVO2.getArchiveAutoGroupId() != null) {
+
+            }
+        }
+    }
+}

+ 5 - 6
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractServiceImpl.java

@@ -494,7 +494,7 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 			for (ArchiveTreeContractVO2 ar: dstTree.getChildren()) {
 				if (StringUtils.isEmpty(ar.getTreeCode())) {
 					//同步
-					if (dstNode.getParentId() == 0 || dstScopeTree.getId() == ar.getId()) {
+					if (dstNode.getParentId() == 0 || dstScopeTree.getId().equals(ar.getId()) ) {
 						//同步
 						List<ArchiveTreeContract> tmpSaveList = null;
 						if (dstNode.getParentId() == 0) {
@@ -516,11 +516,12 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 					for (ArchiveTreeContractVO2 contractNode : ar.getChildren()){
 
 						List<ArchiveTreeContract> tmpSaveList = null;
-						if (dstNode.getParentId() == 0 || dstScopeTree.getId() == contractNode.getId()) {
+						if (dstNode.getParentId() == 0 || dstScopeTree.getId().equals(contractNode.getId())
+								|| dstScopeTree.getId().equals(contractNode.getParentId())) {
 
 							ArchiveTreeVO2 tmpSubTree = srcTree;
 							Long dstNodeId = dstNode.getId();
-							if (dstNode.getParentId() == 0) {
+							if (dstNode.getParentId() == 0 ||  dstScopeTree.getId().equals(contractNode.getParentId()) ) {
 								tmpSubTree = ForestNodeMergerEx.getSubTree(srcTree,ar.getFromId());
 								dstNodeId = contractNode.getId();
 							}
@@ -652,7 +653,7 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 			}
 
 		}
-		this.saveOrUpdateBatch(archiveTreeContracts);
+		this.saveOrUpdateBatch(upList);
 		return true;
 	}
 
@@ -666,8 +667,6 @@ public class ArchiveTreeContractServiceImpl extends BaseServiceImpl<ArchiveTreeC
 			return;
 		}
 
-
-
 		List<ArchiveTreeContractVO2> trees = this.tree2Root(AuthUtil.getTenantId(),null,null,archiveTreeContracts.get(0).getProjectId(),null);
 		if (trees.size() == 0) {
 			return;

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

@@ -104,6 +104,10 @@ public class ArchiveTreeContractSyncImpl {
             if (tmp != null ) {
                 archiveTreeContract.setAncestors(tmp.getAncestors());
                 archiveTreeContract.setTreeSort(tmp.getTreeSort());
+                archiveTreeContract.setAncestors(tmp.getAncestors());
+                archiveTreeContract.setArchiveAutoNodeId(tmp.getArchiveAutoNodeId());
+                archiveTreeContract.setArchiveAutoGroupId(tmp.getArchiveAutoGroupId());
+                archiveTreeContract.setArchiveAutoType(tmp.getArchiveAutoType());
 
             }
         }
@@ -121,7 +125,7 @@ public class ArchiveTreeContractSyncImpl {
 
         List<ArchiveTreeContract> saveList = new ArrayList<>();
         //todo 等测试OK再打开
-        //saveList =arTreeContractInitService.getContractExtNodes(AuthUtil.getTenantId(),projectId,dstTree);
+        saveList =arTreeContractInitService.getContractExtNodes(AuthUtil.getTenantId(),projectId,dstTree);
 
         return saveList;
     }

+ 8 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeServiceImpl.java

@@ -965,6 +965,11 @@ public class ArchiveTreeServiceImpl extends BaseServiceImpl<ArchiveTreeMapper, A
             archiveTreeVO2.setParentId(archiveTree.getParentId());
             archiveTreeVO2.setTitle(archiveTree.getNodeName());
             archiveTreeVO2.setSort(archiveTree.getSort());
+
+            archiveTreeVO2.setArchiveAutoNodeId(archiveTree.getArchiveAutoNodeId());
+            archiveTreeVO2.setArchiveAutoType(archiveTree.getArchiveAutoType());
+            archiveTreeVO2.setArchiveAutoGroupId(archiveTree.getArchiveAutoGroupId());
+
             archiveTreeVO2s.add(archiveTreeVO2);
             vo2Map.put(archiveTreeVO2.getId(),archiveTreeVO2);
         }
@@ -978,6 +983,9 @@ public class ArchiveTreeServiceImpl extends BaseServiceImpl<ArchiveTreeMapper, A
             ArchiveTreeVO2 tmp = vo2Map.get(archiveTree.getId());
             if (tmp != null ) {
                 archiveTree.setAncestors(tmp.getAncestors());
+                archiveTree.setArchiveAutoNodeId(tmp.getArchiveAutoNodeId());
+                archiveTree.setArchiveAutoGroupId(tmp.getArchiveAutoGroupId());
+                archiveTree.setArchiveAutoType(tmp.getArchiveAutoType());
             }
         }
     }

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

@@ -31,6 +31,7 @@ import com.spire.xls.CellRange;
 import com.spire.xls.Workbook;
 import com.spire.xls.*;
 import lombok.AllArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.poi.ss.usermodel.CellStyle;
@@ -92,6 +93,7 @@ import java.util.*;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * 清表基础数据表 服务实现类
@@ -556,6 +558,101 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
         return editCallback;
     }
 
+    @Override
+    public R updateContractById(String contractId) {
+        // 删除被删除的数据
+        wbsTreeContractService.getBaseMapper().delete(Wrappers.<WbsTreeContract>query().lambda()
+                .eq(WbsTreeContract::getContractId, contractId)
+                .eq(WbsTreeContract::getIsDeleted, 1));
+
+        // 查询所有合同段的数据
+        List<WbsTreeContract> wbsTreeContractList = wbsTreeContractService.getBaseMapper().selectList(Wrappers.<WbsTreeContract>query().lambda()
+                .eq(WbsTreeContract::getContractId, contractId)
+                .eq(WbsTreeContract::getIsDeleted, 0));
+
+        // 数据维数据
+        Map<String , List<WbsTreeContract> > Data = new HashMap<>();
+        if(wbsTreeContractList!=null && wbsTreeContractList.size()>=1){
+            for(WbsTreeContract dataInfo : wbsTreeContractList){
+                String dataId = dataInfo.getId()+"";
+                String parentId = dataInfo.getParentId()+"";
+
+                if(!Data.containsKey(dataId)){
+                    List<WbsTreeContract> newData = new ArrayList<>();
+                    Data.put(dataId,newData);
+                }
+                // 添加数据
+                if(Data.containsKey(parentId)){
+                    List<WbsTreeContract> newData = Data.get(parentId);
+                    newData.add(dataInfo);
+                    Data.put(parentId,newData);
+                }
+            }
+        }
+
+        //
+        List<WbsTreeContract> lastList = new ArrayList<>();
+        List<WbsTreeContract> collect = wbsTreeContractList.stream().filter(wbsTreeContract -> "0".equals(wbsTreeContract.getParentId().toString())).collect(Collectors.toList());
+
+        this.dataInfo(collect,lastList,Data);
+
+        if(lastList!=null && lastList.size()>=1){
+            for(WbsTreeContract data : lastList){
+                UpdateWrapper<WbsTreeContract> updateWrapper = new UpdateWrapper<>();
+                updateWrapper.eq("p_key_id", data.getPKeyId() + "");
+                updateWrapper.set("ancestors", data.getAncestors());
+                wbsTreeContractService.getBaseMapper().update(data,updateWrapper);
+            }
+        }
+        return R.data("成功");
+    }
+
+
+    public void dataInfo(List<WbsTreeContract> collect,List<WbsTreeContract> lastList,Map<String , List<WbsTreeContract> > Data){
+
+        /*List<WbsTreeContract> redata = new ArrayList<>();
+        if (collect!=null && collect.size()==1){
+            // 主节点
+            for(WbsTreeContract fu:collect){
+                String dataId = fu.getId()+"";
+                List<WbsTreeContract> er = Data.get(dataId);
+                redata.add(fu);
+                if(er!=null && er.size()>=1){
+                    for(WbsTreeContract erData : er){
+                        erData.setAncestors(fu.getAncestors()+","+erData.getParentId());
+                        lastList.add(erData);
+                    }
+                }
+            }
+        }
+        if(redata!=null && redata.size()>=1){
+            this.dataInfo(redata,lastList,Data);
+        }*/
+
+
+        Queue<WbsTreeContract> queue = new LinkedList<>();
+        //将第一层级的目录全部入列
+        collect.forEach(queue::offer);
+        while (!queue.isEmpty()) {
+            //每次循环出列一个对象 一直到队列为空
+            WbsTreeContract tree= queue.poll();
+            String dataId = tree.getId()+"";
+            List<WbsTreeContract> er = Data.get(dataId);
+            if(er!=null && er.size()>=1){
+                for(WbsTreeContract erData : er){
+                    erData.setAncestors(tree.getAncestors()+","+erData.getParentId());
+                    lastList.add(erData);
+                }
+            }
+
+            if (CollectionUtils.isNotEmpty(er)) {
+                //如果有children集合 将children继续入列
+                er.forEach(queue::offer);
+            }
+        }
+    }
+
+
 
     /**
      * 保存首件
@@ -1079,8 +1176,8 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
 
     @Override
     public R getBussPdfInfo(Long pkeyId) throws Exception {
-        String file_path = ParamCache.getValue(CommonConstant.SYS_LOCAL_URL);
-        //String file_path = "/Users/hongchuangyanfa/Desktop/";
+        //String file_path = ParamCache.getValue(CommonConstant.SYS_LOCAL_URL);
+        String file_path = "/Users/hongchuangyanfa/Desktop/";
         WbsTreeContract wbsTreeContract = wbsTreeContractService.getBaseMapper().selectOne(Wrappers.<WbsTreeContract>query().lambda()
                 .eq(WbsTreeContract::getPKeyId, pkeyId));
 
@@ -1384,8 +1481,8 @@ public class ExcelTabServiceImpl extends BaseServiceImpl<ExcelTabMapper, ExcelTa
 
     @Override
     public void getBussPdfs(String nodeId, String classify, String contractId, String projectId) throws Exception {
-        String file_path = ParamCache.getValue(CommonConstant.SYS_LOCAL_URL);
-        //String file_path = "/Users/hongchuangyanfa/Desktop/";
+        //String file_path = ParamCache.getValue(CommonConstant.SYS_LOCAL_URL);
+        String file_path = "/Users/hongchuangyanfa/Desktop/";
         // 获取有权限的节点信息
         List<AppWbsTreeContractVO> wbsTreeContractList = wbsTreeContractService.searchNodeAllTable(nodeId, classify, contractId, projectId);
         List<String> data = new ArrayList<>();

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

@@ -426,7 +426,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
 
         //计算张数
         double tabCount = moreData.size() / Double.parseDouble(excLenght.length+"") ;
-        double tabsCount  = Math.ceil(tabCount)- wbsTreeContractList2.size()+1;
+        double tabsCount  = Math.ceil(tabCount);//- wbsTreeContractList2.size()+indexId+1;
         int maxIndex=0;
         if(tabsCount>=1){
             for(int i =0 ;i<tabsCount;i++){

+ 25 - 7
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreePrivateServiceImpl.java

@@ -13,6 +13,7 @@ import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;
+import org.springblade.business.feign.InformationQueryClient;
 import org.springblade.common.constant.CommonConstant;
 import org.springblade.common.utils.CommonUtil;
 import org.springblade.common.utils.SnowFlakeUtil;
@@ -66,6 +67,7 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
     private final WbsTreePrivateMapper wbsTreePrivateMapper;
     private final JdbcTemplate jdbcTemplate;
     private final TextdictInfoServiceImpl textDictInfoService;
+    private final InformationQueryClient informationQueryClient;
 
     @Resource(name = "taskExecutor1")
     private final ThreadPoolExecutor executor;
@@ -231,16 +233,20 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
     @Override
     public boolean wbsTreePrivateSort(List<WbsTreePrivateDTO2> wbsTreeDTO) {
         int number = 1;
+        String projectId = "";
+        String parentId = "";
         for (WbsTreePrivateDTO2 wbsTree : wbsTreeDTO) {
             wbsTree.setSort(number);
             baseMapper.updateSortById(wbsTree.getPKeyId(), wbsTree.getSort());
 
-            WbsTreePrivate objPrivate = baseMapper.selectOne(Wrappers.<WbsTreePrivate>query().lambda().eq(WbsTreePrivate::getPKeyId, wbsTree.getPKeyId()));
+            WbsTreePrivate objPrivate = baseMapper.selectOne(Wrappers.<WbsTreePrivate>lambdaQuery()
+                    .select(WbsTreePrivate::getWbsId, WbsTreePrivate::getProjectId, WbsTreePrivate::getId, WbsTreePrivate::getParentId)
+                    .eq(WbsTreePrivate::getPKeyId, wbsTree.getPKeyId()));
             if (objPrivate != null) {
-                String projectId = objPrivate.getProjectId();
+                projectId = objPrivate.getProjectId();
+                parentId = objPrivate.getParentId() + "";
                 String wbsId = objPrivate.getWbsId();
                 Long id = objPrivate.getId();
-                //合同段wbs树同步排序
                 LambdaUpdateWrapper<WbsTreeContract> updateWrapper = new LambdaUpdateWrapper<>();
                 updateWrapper.eq(WbsTreeContract::getWbsId, wbsId);
                 updateWrapper.eq(WbsTreeContract::getProjectId, projectId);
@@ -250,6 +256,15 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
             }
             number++;
         }
+
+        //更新Redis
+        List<ContractInfo> contractInfos = contractInfoMapper.selectList(Wrappers.<ContractInfo>lambdaQuery().select(ContractInfo::getId).eq(ContractInfo::getContractType, 1).eq(ContractInfo::getPId, Long.parseLong(projectId)));
+        if (contractInfos.size() > 0) {
+            for (ContractInfo contractInfo : contractInfos) {
+                informationQueryClient.AsyncWbsTree("", parentId, contractInfo.getId() + "", "", "1");
+            }
+        }
+
         return true;
     }
 
@@ -393,6 +408,7 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
                                 || (ObjectUtils.isNotEmpty(wbsTree.getMixRatioTestIds()) && !wbsTree.getMixRatioTestIds().equals(wbsTreePrivate.getMixRatioTestIds()))
                                 || (ObjectUtils.isNotEmpty(wbsTree.getInitTableId()) && !wbsTree.getInitTableId().toString().equals(wbsTreePrivate.getInitTableId()))
                                 || (ObjectUtils.isNotEmpty(wbsTree.getInitTableName()) && !wbsTree.getInitTableName().equals(wbsTreePrivate.getInitTableName()))
+                                || ((new Integer(1)).equals(wbsTreePrivate.getType()) && ObjectUtils.isNotEmpty(wbsTree.getSort()) && !wbsTree.getSort().equals(wbsTreePrivate.getSort()))
                         )) {
                     //修改项目wbs信息
                     WbsTreePrivate wbsPrivate = BeanUtil.copyProperties(wbsTree, WbsTreePrivate.class);
@@ -456,10 +472,12 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
         List<WbsTreePrivate> listPrivate = new ArrayList<>();
         List<WbsTreeContract> listContract = new ArrayList<>();
         //获取当前项目下所有合同段信息
-        List<ContractInfo> contractInfos = contractInfoMapper.selectList(Wrappers.<ContractInfo>query().lambda().select(ContractInfo::getId, ContractInfo::getContractType).eq(ContractInfo::getPId, projectId));
+        List<ContractInfo> contractInfos = contractInfoMapper.selectList(Wrappers.<ContractInfo>query().lambda().select(ContractInfo::getId, ContractInfo::getContractType)
+                .eq(ContractInfo::getContractType, 1)
+                .eq(ContractInfo::getPId, projectId));
         List<Long> contractInfosIds = contractInfos.stream().filter(f -> 1 == f.getContractType()).map(ContractInfo::getId).collect(Collectors.toList());
 
-        Map<Long, WbsTreePrivate> maps = wbsTreePrivatesAllNow.stream().collect(Collectors.toMap(WbsTreePrivate::getId, Function.identity()));
+        Map<Long, WbsTreePrivate> maps = wbsTreePrivatesAllNow.stream().collect(Collectors.toMap(WbsTreePrivate::getId, wbsTreePrivate -> wbsTreePrivate, (obj1, obj2) -> obj1));
 
         for (WbsTreePrivate wbsTreePrivate : wbsTreePrivates) {
             WbsTreePrivate treePrivateNow = maps.get(wbsTreePrivate.getId());
@@ -473,7 +491,8 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
                         || (ObjectUtils.isNotEmpty(wbsTreePrivate.getMixRatioTestIds()) && !wbsTreePrivate.getMixRatioTestIds().equals(treePrivateNow.getMixRatioTestIds()))
                         || (ObjectUtils.isNotEmpty(wbsTreePrivate.getInitTableId()) && !wbsTreePrivate.getInitTableId().equals(treePrivateNow.getInitTableId()))
                         || (ObjectUtils.isNotEmpty(wbsTreePrivate.getInitTableName()) && !wbsTreePrivate.getInitTableName().equals(treePrivateNow.getInitTableName())
-                        || (ObjectUtils.isNotEmpty(wbsTreePrivate.getHtmlUrl()) && !wbsTreePrivate.getHtmlUrl().equals(treePrivateNow.getHtmlUrl())))) {
+                        || (ObjectUtils.isNotEmpty(wbsTreePrivate.getHtmlUrl()) && !wbsTreePrivate.getHtmlUrl().equals(treePrivateNow.getHtmlUrl())))
+                        || ((new Integer(1)).equals(wbsTreePrivate.getType()) && ObjectUtils.isNotEmpty(wbsTreePrivate.getSort()) && !wbsTreePrivate.getSort().equals(treePrivateNow.getSort()))) {
                     //修改项目wbs信息
                     WbsTreePrivate wbsPrivate = BeanUtil.copyProperties(wbsTreePrivate, WbsTreePrivate.class);
                     if (wbsPrivate != null) {
@@ -492,7 +511,6 @@ public class WbsTreePrivateServiceImpl extends BaseServiceImpl<WbsTreePrivateMap
                     }
                 }
             }
-
         }
 
         //修改到项目

+ 27 - 11
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeServiceImpl.java

@@ -278,7 +278,7 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
                 IOException | ClassNotFoundException e) {
             throw new ServiceException("操作失败,请上传正确模板内容");
         } finally {
-            if (canonicalPath != null){
+            if (canonicalPath != null) {
                 File file2 = new File(canonicalPath);
                 if (file2.isFile() && file2.exists()) {
                     if (file2.delete()) {
@@ -331,9 +331,19 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
                 //质检公有
                 if (ObjectUtil.isNotEmpty(projectInfo.getReferenceWbsTemplateId()) && (ObjectUtil.isNotEmpty(projectInfo.getReferenceWbsTemplateType()) && ("public").equals(projectInfo.getReferenceWbsTemplateType()))) {
                     //获取公有树
-                    List<WbsTree> wbsTreeListAll = wbsTreeMapper.selectList(Wrappers.<WbsTree>lambdaQuery().eq(WbsTree::getWbsId, projectInfo.getReferenceWbsTemplateId()).eq(WbsTree::getType, 1).eq(WbsTree::getStatus, 1));
+                    List<WbsTree> wbsTreeListAll = wbsTreeMapper.selectList(Wrappers.<WbsTree>lambdaQuery()
+                            .select(WbsTree::getId, WbsTree::getNodeName, WbsTree::getNodeType, WbsTree::getMajorDataType,
+                                    WbsTree::getTableType, WbsTree::getTableOwner, WbsTree::getImportMatchingInfo,
+                                    WbsTree::getMixRatioTestIds, WbsTree::getInitTableId,
+                                    WbsTree::getInitTableName, WbsTree::getSort)
+                            .eq(WbsTree::getWbsId, projectInfo.getReferenceWbsTemplateId()).eq(WbsTree::getType, 1).eq(WbsTree::getStatus, 1));
                     //获取项目私节点、元素表
-                    List<WbsTreePrivate> wbsTreePrivatesAll = wbsTreePrivateMapper.selectList(Wrappers.<WbsTreePrivate>query().lambda().eq(WbsTreePrivate::getWbsId, projectInfo.getReferenceWbsTemplateId()).eq(WbsTreePrivate::getProjectId, wbsTreePrivate.getProjectId()).eq(WbsTreePrivate::getStatus, 1));
+                    List<WbsTreePrivate> wbsTreePrivatesAll = wbsTreePrivateMapper.selectList(Wrappers.<WbsTreePrivate>lambdaQuery()
+                            .select(WbsTreePrivate::getId, WbsTreePrivate::getNodeName, WbsTreePrivate::getNodeType, WbsTreePrivate::getMajorDataType,
+                                    WbsTreePrivate::getTableType, WbsTreePrivate::getTableOwner, WbsTreePrivate::getImportMatchingInfo,
+                                    WbsTreePrivate::getMixRatioTestIds, WbsTreePrivate::getInitTableId,
+                                    WbsTreePrivate::getInitTableName, WbsTreePrivate::getHtmlUrl, WbsTreePrivate::getSort)
+                            .eq(WbsTreePrivate::getWbsId, projectInfo.getReferenceWbsTemplateId()).eq(WbsTreePrivate::getProjectId, wbsTreePrivate.getProjectId()).eq(WbsTreePrivate::getStatus, 1));
                     //同步修改
                     this.updateWbsInfoPrivateAsync(wbsTreeListAll, wbsTreePrivatesAll, wbsTreePrivate.getProjectId(), Integer.parseInt(wbsTreePrivate.getWbsType()));
 
@@ -345,14 +355,20 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
                             .eq(WbsTreePrivate::getPKeyId, projectInfo.getReferenceWbsTemplateId()));
                     //获取私有引用项目树
                     List<WbsTreePrivate> wbsTreePrivateAllOld = wbsTreePrivateMapper.selectList(Wrappers.<WbsTreePrivate>query().lambda()
-                            .select(WbsTreePrivate::getId, WbsTreePrivate::getNodeName, WbsTreePrivate::getNodeType, WbsTreePrivate::getMajorDataType, WbsTreePrivate::getTableType, WbsTreePrivate::getTableOwner, WbsTreePrivate::getImportMatchingInfo, WbsTreePrivate::getMixRatioTestIds, WbsTreePrivate::getInitTableId, WbsTreePrivate::getInitTableName, WbsTreePrivate::getHtmlUrl)
+                            .select(WbsTreePrivate::getId, WbsTreePrivate::getNodeName, WbsTreePrivate::getNodeType, WbsTreePrivate::getMajorDataType,
+                                    WbsTreePrivate::getTableType, WbsTreePrivate::getTableOwner, WbsTreePrivate::getImportMatchingInfo,
+                                    WbsTreePrivate::getMixRatioTestIds, WbsTreePrivate::getInitTableId,
+                                    WbsTreePrivate::getInitTableName, WbsTreePrivate::getHtmlUrl, WbsTreePrivate::getSort)
                             .eq(WbsTreePrivate::getStatus, 1)
                             .eq(WbsTreePrivate::getProjectId, Long.parseLong(wbsTreePrivateRecord.getProjectId()))
                             .and(obj -> obj.eq(WbsTreePrivate::getWbsId, Long.parseLong(wbsTreePrivateRecord.getWbsId())).or().isNull(WbsTreePrivate::getWbsId))
                     );
                     //获取当前项目私有树、元素表
                     List<WbsTreePrivate> wbsTreePrivatesAllNow = wbsTreePrivateMapper.selectList(Wrappers.<WbsTreePrivate>query().lambda()
-                            .select(WbsTreePrivate::getId, WbsTreePrivate::getNodeName, WbsTreePrivate::getNodeType, WbsTreePrivate::getMajorDataType, WbsTreePrivate::getTableType, WbsTreePrivate::getTableOwner, WbsTreePrivate::getImportMatchingInfo, WbsTreePrivate::getMixRatioTestIds, WbsTreePrivate::getInitTableId, WbsTreePrivate::getInitTableName, WbsTreePrivate::getHtmlUrl)
+                            .select(WbsTreePrivate::getId, WbsTreePrivate::getNodeName, WbsTreePrivate::getNodeType, WbsTreePrivate::getMajorDataType,
+                                    WbsTreePrivate::getTableType, WbsTreePrivate::getTableOwner, WbsTreePrivate::getImportMatchingInfo,
+                                    WbsTreePrivate::getMixRatioTestIds, WbsTreePrivate::getInitTableId,
+                                    WbsTreePrivate::getInitTableName, WbsTreePrivate::getHtmlUrl, WbsTreePrivate::getSort)
                             .eq(WbsTreePrivate::getStatus, 1)
                             .eq(WbsTreePrivate::getProjectId, Long.parseLong(wbsTreePrivate.getProjectId()))
                             .and(obj -> obj.eq(WbsTreePrivate::getWbsId, projectInfo.getReferenceWbsTemplateId()).or().isNull(WbsTreePrivate::getWbsId))
@@ -417,7 +433,7 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
     }
 
     @Override
-    public List<WbsFormElementVO> selectFormElements(String id, String nodeId ,Integer type) {
+    public List<WbsFormElementVO> selectFormElements(String id, String nodeId, Integer type) {
         List<WbsFormElementVO> wbsFormElements = wbsTreeMapper.selectFormElements(id);
         if (Func.isEmpty(wbsFormElements)) {
             wbsFormElements = wbsTreeMapper.selectFormElements4TableId(id);
@@ -442,13 +458,13 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
             String sql;
             if (com.mixsmart.utils.StringUtils.isEquals(type, 1)) {
                 sql = "select c.element_id from m_wbs_tree a INNER JOIN m_wbs_tree b on a.parent_id=b.id INNER JOIN m_element_formula_mapping c on b.id=c.node_id where  a.id=" + id + " and c.scope=" + scopeB + " and c.element_id in(" + elementIds + ")";
-            } else if (com.mixsmart.utils.StringUtils.isEquals(type, 0)){
+            } else if (com.mixsmart.utils.StringUtils.isEquals(type, 0)) {
                 WbsTreePrivate treePrivate = this.wbsTreePrivateService.getOne(Wrappers.<WbsTreePrivate>lambdaQuery().eq(WbsTreePrivate::getPKeyId, id));
                 sql = "select c.element_id from m_wbs_tree_private a INNER JOIN m_wbs_tree b on a.parent_id=b.id INNER JOIN m_element_formula_mapping c on b.id=c.node_id where  a.p_key_id=" + id + " and c.scope=" + scopeB + " and c.element_id in(" + elementIds + ")  and c.project_id=" + treePrivate.getProjectId();
-            }else  if (com.mixsmart.utils.StringUtils.isEquals(type, 3)){
+            } else if (com.mixsmart.utils.StringUtils.isEquals(type, 3)) {
                 WbsTreePrivate treePrivate = this.wbsTreePrivateService.getOne(Wrappers.<WbsTreePrivate>lambdaQuery().eq(WbsTreePrivate::getPKeyId, nodeId));
                 sql = "select c.element_id from m_wbs_tree_private a INNER JOIN m_wbs_tree b on a.parent_id=b.id INNER JOIN m_element_formula_mapping c on b.id=c.node_id where  a.p_key_id=" + id + " and c.scope=35 and c.element_id in(" + elementIds + ")  and c.project_id=" + treePrivate.getProjectId();
-            }else{
+            } else {
                 sql = "select c.element_id from m_wbs_tree a INNER JOIN m_wbs_tree b on a.parent_id=b.id INNER JOIN m_element_formula_mapping c on b.id=c.node_id where  a.id=" + id + " and c.scope=35 and c.element_id in(" + elementIds + ")";
             }
             List<Long> longs2 = this.jdbcTemplate.queryForList(sql, Long.class);
@@ -466,8 +482,8 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
     }
 
     @Override
-    public List<WbsFormElementVO> selectPrivateFormElements(String id,String eName) {
-        return wbsTreeMapper.selectPrivateFormElements(id,eName);
+    public List<WbsFormElementVO> selectPrivateFormElements(String id, String eName) {
+        return wbsTreeMapper.selectPrivateFormElements(id, eName);
     }
 
     @Override

+ 3 - 22
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/FileUtils.java

@@ -379,31 +379,12 @@ public class FileUtils {
 
         String excelPath="/Users/hongchuangyanfa/Desktop/pdf/1625671101419880450.xlsx";
         File file_out = ResourceUtil.getFile("/Users/hongchuangyanfa/Desktop/pdf/12345.xlsx");
-        InputStream exceInp = new FileInputStream(file_out);//CommonUtil.getOSSInputStream("https://bladex-test-info.oss-cn-chengdu.aliyuncs.com//upload/20230308/aede5ee70ecc095c8e1dd76aa2f49d1a.xlsx");
-        final org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(exceInp);
-        Sheet sheet = workbook.getSheetAt(0);
-        sheet.setForceFormulaRecalculation(true);
-        // fileName为图片完整路径,例:C:\images\EDG.jpg
+        InputStream exceInp =CommonUtil.getOSSInputStream("https://bladex-test-info.oss-cn-chengdu.aliyuncs.com//upload/20230308/aede5ee70ecc095c8e1dd76aa2f49d1a.xlsx");
+        String htmlString = IoUtil.readToString(exceInp);
+        FileUtil.writeToFile(file_out, htmlString);
 
 
-        FileOutputStream outputStream = new FileOutputStream(excelPath);
-        //生成一份新的excel
 
-        Cell cell = sheet.getRow(10).getCell(10);
-        cell.setCellValue("2023年02-08-2023年02-09");
-
-
-
-        workbook.write(outputStream);
-        if (outputStream != null) {
-            IoUtil.closeQuietly(outputStream);
-        }
-        if (exceInp != null) {
-            IoUtil.closeQuietly(exceInp);
-        }
-        if (workbook != null) {
-            IoUtil.closeQuietly(workbook);
-        }
 
     }
 

+ 79 - 2
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/ForestNodeMergerEx.java

@@ -6,6 +6,8 @@ import org.springblade.common.utils.INodeEx;
 import org.springblade.manager.vo.ArchiveTreeContractVO2;
 import org.springframework.beans.BeanUtils;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -143,7 +145,10 @@ public class ForestNodeMergerEx {
         getTreeList(dstTree,dstList);
 
         boolean bRootMatch = true;
-        if (dstTree.getFromId() != srcTree.getFromId())  {
+
+        if (dstTree.getFromId() == null ) {
+            bRootMatch = false;
+        }else if (!dstTree.getFromId().equals(srcTree.getId()))  {
             bRootMatch = false;
         }
 
@@ -161,7 +166,7 @@ public class ForestNodeMergerEx {
             if (srcDstMap.get(src.getId())!= null) {
                 continue;
             }
-            if (src.getId() == srcTree.getId() && bRootMatch == false) {
+            if (src.getId().equals(srcTree.getId()) && bRootMatch == false) {
                 continue;
             }
 
@@ -232,4 +237,76 @@ public class ForestNodeMergerEx {
     }
 
 
+    public static void assignPropertyValue(Object obj, String propertyName, Object value)
+    {
+        try {
+            // 获取对象的Class类型
+            Class<?> clazz = obj.getClass();
+            // 通过属性名获取类的成员变量
+            Field field = clazz.getDeclaredField(propertyName);
+            // 设置私有成员变量可访问
+            field.setAccessible(true);
+            // 给对象的属性赋值
+            field.set(obj, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static Object getPropertyValue(Object obj, String propertyName) {
+        try {
+            Class<?> clazz = obj.getClass();
+            Method getterMethod = clazz.getMethod(getGetterName(propertyName));
+            return getterMethod.invoke(obj);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    // 根据属性名生成getter方法名
+    private static String getGetterName(String propertyName) {
+        return "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+    }
+
+
+
+    public static <T extends INodeEx<T>> List<T> setPropertyfromId(List<T> NodeList, Map<String,String> propertyMap)
+    {
+        List<T> changeList = new ArrayList<>();
+        Map<Long,Long> oldNewMap = new LinkedHashMap<>();
+
+        for (T node: NodeList) {
+            if (node.getFromId() != null) {
+                oldNewMap.put(node.getFromId(),node.getId());
+            }
+        }
+
+        for (T node: NodeList) {
+            if (node.getFromId() == null) {
+                continue;
+            }
+
+            boolean bSet = false;
+
+            for (String fieldName: propertyMap.keySet()) {
+                Object obj = getPropertyValue(node,fieldName);
+                if (obj != null ) {
+                    Long newId = oldNewMap.get(obj);
+                    if (newId != null ) {
+                        assignPropertyValue(node,fieldName,newId);
+                        bSet = true;
+                    }
+                }
+            }
+
+            if (bSet){
+                changeList.add(node);
+            }
+        }
+
+        return changeList;
+    }
+
+
 }