Răsfoiți Sursa

同步声像文件

xqj 10 luni în urmă
părinte
comite
ac9dffa036
19 a modificat fișierele cu 1140 adăugiri și 9 ștergeri
  1. 8 1
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/ImageClassificationFileDTO.java
  2. 32 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/ArchiveFile.java
  3. 6 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/ImageClassificationFile.java
  4. 5 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ArchiveFileClient.java
  5. 28 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ImageClassificationFileClient.java
  6. 29 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TreeVoTwo.java
  7. 7 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ArchiveTreeContract.java
  8. 48 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/enums/AssociationTypeEnum.java
  9. 27 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/enums/TreeStructureEnum.java
  10. 10 0
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ArchiveFileClientImpl.java
  11. 22 0
      blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ImageClassificationFileClientImpl.java
  12. 2 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ArchiveFileMapper.xml
  13. 6 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ImageClassificationFileMapper.java
  14. 43 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/ImageClassificationFileMapper.xml
  15. 16 2
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ArchiveTreeContractController.java
  16. 15 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.java
  17. 46 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ArchiveTreeContractMapper.xml
  18. 707 6
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractSyncImpl.java
  19. 83 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/utils/YearTreeUtils.java

+ 8 - 1
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/ImageClassificationFileDTO.java

@@ -16,9 +16,9 @@
  */
  */
 package org.springblade.business.dto;
 package org.springblade.business.dto;
 
 
-import org.springblade.business.entity.ImageClassificationFile;
 import lombok.Data;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.EqualsAndHashCode;
+import org.springblade.business.entity.ImageClassificationFile;
 
 
 /**
 /**
  * 数据传输对象实体类
  * 数据传输对象实体类
@@ -31,4 +31,11 @@ import lombok.EqualsAndHashCode;
 public class ImageClassificationFileDTO extends ImageClassificationFile {
 public class ImageClassificationFileDTO extends ImageClassificationFile {
     private static final long serialVersionUID = 1L;
     private static final long serialVersionUID = 1L;
 
 
+    /**
+     * 存储目录格式
+     * 1-目录存储,2-时间存储
+     * (通过classifyId 关联 m_image_classification_config 这张表主键可得到)
+     */
+    private Integer storageDirectoryFormat;
+
 }
 }

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

@@ -283,4 +283,36 @@ public class ArchiveFile extends BaseEntity {
      */
      */
     @ApiModelProperty("类型,1施工 2监理")
     @ApiModelProperty("类型,1施工 2监理")
     private Integer classify;
     private Integer classify;
+
+    /**
+     * m_wbs_tree_contract表中的主键,p_key_id
+     */
+    @ApiModelProperty("m_wbs_tree_contract表中的主键,p_key_id")
+    private Long mWbsTreeContractPKeyId;
+
+    /**
+     * u_image_classification_file_id表中的主键id
+     */
+    @ApiModelProperty("u_image_classification_file_id表中的主键id")
+    private Long uImageClassificationFileId;
+
+    /**
+     * 归档文件储存类型,可看代码枚举
+     * @StorageTypeEnum
+     */
+    @ApiModelProperty("归档文件储存类型")
+    private Integer archiveFileStorageType;
+
+    /**
+     * 节点树结构,1-普通树,2-时间树
+     * 正常都是1, 2是为了区分声像文件的两个树形结构下的文件
+     */
+    @ApiModelProperty("节点树结构,1-普通树,2-时间树")
+    private Integer nodeTreeStructure;
+
+    /**
+     * 文件时间名称,可用于确定时间树节点的id
+     */
+    @ApiModelProperty("文件时间名称,可用于确定时间树节点的id")
+    private String dateName;
 }
 }

+ 6 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/ImageClassificationFile.java

@@ -127,4 +127,10 @@ public class ImageClassificationFile extends BaseEntity {
     @ApiModelProperty("合并后的PDF")
     @ApiModelProperty("合并后的PDF")
     private String margePdfUrl;
     private String margePdfUrl;
 
 
+    @ApiModelProperty("图片或视频的水平分辨率")
+    private Integer fileWidth;
+
+    @ApiModelProperty("图片或视频的垂直分辨率")
+    private Integer fileHeight;
+
 }
 }

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

@@ -150,4 +150,9 @@ public interface ArchiveFileClient {
     @PostMapping(API_PREFIX + "/updateById2")
     @PostMapping(API_PREFIX + "/updateById2")
     void updateById2(@RequestBody ArchiveFile archiveFile);
     void updateById2(@RequestBody ArchiveFile archiveFile);
 
 
+    /**
+     * 根据合同Id和储存类型查询集合
+     */
+    @PostMapping(API_PREFIX + "/getListByContractIdAndArchiveFileStorageType")
+    List<ArchiveFile> getListByContractIdAndArchiveFileStorageType(@RequestParam Long contractId,@RequestParam Integer getListByContractIdAndArchiveFileStorageType);
 }
 }

+ 28 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/feign/ImageClassificationFileClient.java

@@ -0,0 +1,28 @@
+package org.springblade.business.feign;
+
+import org.springblade.business.dto.ImageClassificationFileDTO;
+import org.springblade.common.constant.BusinessConstant;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.List;
+
+/**
+ * @author xingqianji
+ */
+@FeignClient(value = BusinessConstant.APPLICATION_WEATHER_NAME)
+public interface ImageClassificationFileClient {
+
+    /**
+     * 接口前缀
+     */
+    String API_PREFIX = "/api/business/imageClassificationFile";
+
+    /**
+     * 根据合同段id查询声像文件
+     */
+    @PostMapping(API_PREFIX + "/getImageClassificationFileListByContractId")
+    List<ImageClassificationFileDTO> getImageClassificationFileListByContractId(@RequestBody Long contractId);
+
+}

+ 29 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TreeVoTwo.java

@@ -0,0 +1,29 @@
+package org.springblade.business.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class TreeVoTwo {
+
+    private String name;
+
+    private String hierarchy;
+
+    private Long parentId;
+
+    private List<TreeVoTwo> children = new ArrayList<>();
+
+    public TreeVoTwo(String name, List<TreeVoTwo> children, String hierarchy, Long parentId) {
+        this.name = name;
+        this.children = children;
+        this.parentId = parentId;
+        this.hierarchy = hierarchy;
+    }
+
+    public TreeVoTwo() {
+    }
+
+}

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

@@ -233,6 +233,13 @@ public class ArchiveTreeContract extends BaseEntity {
     @ApiModelProperty(value = "专家名称,逗号分隔")
     @ApiModelProperty(value = "专家名称,逗号分隔")
     private String expertName;
     private String expertName;
 
 
+    /**
+     * 树结构
+     * 正常都是1,数据库默认保存也是1
+     * 2是因为,声像文件那里,有根据时间树形结构来展示的树
+     */
+    @ApiModelProperty(value = "树结构,1-正常树,2-时间树")
+    private Integer treeStructure;
 
 
     public ArchiveTreeContract() {
     public ArchiveTreeContract() {
     }
     }

+ 48 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/enums/AssociationTypeEnum.java

@@ -0,0 +1,48 @@
+package org.springblade.manager.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springblade.core.tool.utils.ObjectUtil;
+
+/**
+ * 关联类型枚举
+ * @author xingqianji
+ */
+@Getter
+@AllArgsConstructor
+public enum AssociationTypeEnum {
+
+    UNKNOWN(-1, ""),
+    QUALITY_INSPECTION_DATA(1, "质检资料"),
+    TRIAL_DATA(2, "试验资料"),
+    SOUND_IMAGE_DATA(3, "影像资料"),
+    LEDGER_DATA(4, "台账资料"),
+    FIRST_ARTICLE_DATA(5, "首件资料"),
+    LOG_FILE(6, "日志文件");
+
+    /**
+     * 编码
+     */
+    private Integer code;
+
+    /**
+     * 描述
+     */
+    private String desc;
+
+    /**
+     * 根据编码获取枚举描述
+     */
+    public static String getByCode(int code) {
+        // 判断code
+        if(ObjectUtil.isNotEmpty(code)){
+            for (AssociationTypeEnum storageTypeEnum : AssociationTypeEnum.values()) {
+                if (storageTypeEnum.getCode() == code) {
+                    return storageTypeEnum.getDesc();
+                }
+            }
+        }
+        return AssociationTypeEnum.UNKNOWN.getDesc();
+    }
+
+}

+ 27 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/enums/TreeStructureEnum.java

@@ -0,0 +1,27 @@
+package org.springblade.manager.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 树形结构枚举
+ * @author xingqianji
+ */
+@Getter
+@AllArgsConstructor
+public enum TreeStructureEnum {
+
+    ORDINARY_TREE(1, "普通树形"),
+    DATE_TREE(2, "时间树形");
+
+    /**
+     * 编码
+     */
+    private Integer code;
+
+    /**
+     * 描述
+     */
+    private String desc;
+
+}

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

@@ -17,6 +17,7 @@ import org.springblade.business.service.ITaskService;
 import org.springblade.business.vo.ArchiveFileVO;
 import org.springblade.business.vo.ArchiveFileVO;
 import org.springblade.common.utils.FileUtils;
 import org.springblade.common.utils.FileUtils;
 import org.springblade.manager.entity.ContractInfo;
 import org.springblade.manager.entity.ContractInfo;
+import org.springblade.manager.enums.StorageTypeEnum;
 import org.springblade.manager.feign.ContractClient;
 import org.springblade.manager.feign.ContractClient;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
@@ -322,4 +323,13 @@ public class ArchiveFileClientImpl implements ArchiveFileClient {
         iArchiveFileService.updateById(archiveFile);
         iArchiveFileService.updateById(archiveFile);
     }
     }
 
 
+    @Override
+    public List<ArchiveFile> getListByContractIdAndArchiveFileStorageType(Long contractId, Integer getListByContractIdAndArchiveFileStorageType) {
+        LambdaQueryWrapper<ArchiveFile> wrapper = Wrappers.lambdaQuery();
+        wrapper.eq(ArchiveFile::getContractId, contractId);
+        wrapper.eq(ArchiveFile::getIsDeleted, 0);
+        wrapper.eq(ArchiveFile::getArchiveFileStorageType, getListByContractIdAndArchiveFileStorageType);
+        return this.iArchiveFileService.list(wrapper);
+    }
+
 }
 }

+ 22 - 0
blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ImageClassificationFileClientImpl.java

@@ -0,0 +1,22 @@
+package org.springblade.business.feignClient;
+
+import lombok.AllArgsConstructor;
+import org.springblade.business.dto.ImageClassificationFileDTO;
+import org.springblade.business.feign.ImageClassificationFileClient;
+import org.springblade.business.mapper.ImageClassificationFileMapper;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@AllArgsConstructor
+public class ImageClassificationFileClientImpl implements ImageClassificationFileClient {
+
+    private final ImageClassificationFileMapper imageClassificationFileMapper;
+
+    @Override
+    public List<ImageClassificationFileDTO> getImageClassificationFileListByContractId(Long contractId) {
+        return imageClassificationFileMapper.getImageClassificationFileListByContractId(contractId);
+    }
+
+}

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

@@ -58,6 +58,8 @@
         <result column="pdf_page_url" property="pdfPageUrl"/>
         <result column="pdf_page_url" property="pdfPageUrl"/>
         <result column="fid" property="fid"/>
         <result column="fid" property="fid"/>
         <result column="rectification" property="rectification"/>
         <result column="rectification" property="rectification"/>
+        <result column="m_wbs_tree_contract_p_key_id" property="mWbsTreeContractPKeyId"/>
+        <result column="u_image_classification_file_id" property="uImageClassificationFileId"/>
     </resultMap>
     </resultMap>
     <update id="recoveryByIds">
     <update id="recoveryByIds">
         update u_archive_file set is_deleted = 0 where
         update u_archive_file set is_deleted = 0 where

+ 6 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/ImageClassificationFileMapper.java

@@ -16,6 +16,7 @@
  */
  */
 package org.springblade.business.mapper;
 package org.springblade.business.mapper;
 
 
+import org.springblade.business.dto.ImageClassificationFileDTO;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.entity.ImageClassificationFile;
 import org.springblade.business.entity.ImageClassificationFile;
 import org.springblade.business.vo.ImageClassificationFileVO;
 import org.springblade.business.vo.ImageClassificationFileVO;
@@ -75,4 +76,9 @@ public interface ImageClassificationFileMapper extends BaseMapper<ImageClassific
      */
      */
     public List<ImageClassificationFile> getDeleteDataByIds(@Param("ids") List<String> ids);
     public List<ImageClassificationFile> getDeleteDataByIds(@Param("ids") List<String> ids);
 
 
+    /**
+     * 根据合同段id查询声像文件
+     */
+    List<ImageClassificationFileDTO> getImageClassificationFileListByContractId(@Param("contractId") Long contractId);
+
 }
 }

+ 43 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/ImageClassificationFileMapper.xml

@@ -30,6 +30,8 @@
         <result column="text_content" property="textContent"/>
         <result column="text_content" property="textContent"/>
         <result column="pdf_url" property="pdfUrl"/>
         <result column="pdf_url" property="pdfUrl"/>
         <result column="marge_pdf_url" property="margePdfUrl"/>
         <result column="marge_pdf_url" property="margePdfUrl"/>
+        <result column="file_width" property="fileWidth"/>
+        <result column="file_height" property="fileHeight"/>
     </resultMap>
     </resultMap>
     <update id="recoveryByIds">
     <update id="recoveryByIds">
         update u_image_classification_file set is_deleted = 0 where
         update u_image_classification_file set is_deleted = 0 where
@@ -131,4 +133,45 @@
         </foreach>
         </foreach>
     </select>
     </select>
 
 
+    <!-- 根据合同段id查询声像文件 -->
+    <select id="getImageClassificationFileListByContractId" resultType="org.springblade.business.dto.ImageClassificationFileDTO">
+        select
+            u_image_classification_file.id,
+            u_image_classification_file.classify_id,
+            u_image_classification_file.project_id,
+            u_image_classification_file.contract_id,
+            u_image_classification_file.wbs_id,
+            u_image_classification_file.title,
+            u_image_classification_file.type,
+            u_image_classification_file.image_url,
+            u_image_classification_file.pdf_url,
+            u_image_classification_file.file_size,
+            u_image_classification_file.file_name,
+            u_image_classification_file.shooting_user,
+            u_image_classification_file.shooting_time,
+            u_image_classification_file.photo_code,
+            u_image_classification_file.film_code,
+            u_image_classification_file.see_also_code,
+            u_image_classification_file.text_content,
+            u_image_classification_file.create_user,
+            u_image_classification_file.create_dept,
+            u_image_classification_file.create_time,
+            u_image_classification_file.update_user,
+            u_image_classification_file.update_time,
+            u_image_classification_file.upload_time,
+            u_image_classification_file.marge_pdf_url,
+            u_image_classification_file.file_width,
+            u_image_classification_file.file_height,
+            m_image_classification_config.storage_directory_format
+        from
+            u_image_classification_file u_image_classification_file
+        left join
+            m_image_classification_config m_image_classification_config
+        on
+            u_image_classification_file.classify_id = m_image_classification_config.id
+        where
+            u_image_classification_file.contract_id = #{contractId}
+        and u_image_classification_file.is_deleted = 0
+    </select>
+
 </mapper>
 </mapper>

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

@@ -506,6 +506,20 @@ public class ArchiveTreeContractController extends BladeController {
         return R.data(archiveTreeContractService.startInspect(projectId,type));
         return R.data(archiveTreeContractService.startInspect(projectId,type));
     }
     }
 
 
-
-
+    @PostMapping("syncSoundImageData")
+    @ApiOperation(value = "同步合同段声像文件", notes = "传入当前节点id")
+    @ApiImplicitParams(value = {
+            @ApiImplicitParam(name = "projectId", value = "项目id", required = true, dataType = "Long"),
+            @ApiImplicitParam(name = "contractId", value = "合同段id", required = true, dataType = "Long"),
+            @ApiImplicitParam(name = "nodeId", value = "当前节点id", required = true, dataType = "Long"),
+    })
+    public R syncSoundImageData(Long projectId, Long contractId, Long nodeId) {
+        R returnObject = R.success("同步合同段声像文件完成" );
+        try {
+            archiveTreeContractSync.syncSoundImageData(projectId,contractId, nodeId);
+        }catch (Exception e) {
+            returnObject = R.fail(e.getMessage());
+        }
+        return returnObject;
+    }
 }
 }

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

@@ -131,4 +131,19 @@ public interface ArchiveTreeContractMapper extends BaseMapper<ArchiveTreeContrac
     List<MyInspectTreeVO> MyLazyTree(@Param("parentId") Long parentId,@Param("projectId") Long projectId,@Param("userId") Long userId);
     List<MyInspectTreeVO> MyLazyTree(@Param("parentId") Long parentId,@Param("projectId") Long projectId,@Param("userId") Long userId);
 
 
     ArchiveExpertConclusion getNewTable(@Param("projectId") Long projectId);
     ArchiveExpertConclusion getNewTable(@Param("projectId") Long projectId);
+
+    /**
+     * 批量添加归档合同树
+     */
+    int batchInsertArchiveTreeContract(@Param("list") List<ArchiveTreeContract> list);
+
+    /**
+     * 批量删除归档合同树
+     */
+    int batchDeleteArchiveTreeContractByIdList(@Param("ids") List<Long> ids, @Param("isDeleted") Integer isDeleted);
+
+    /**
+     * 批量修改归档树
+     */
+    int batchUpdateArchiveTreeContract(@Param("list") List<Map> list);
 }
 }

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

@@ -699,4 +699,50 @@
 <!--          and contract_id = #{contractId} and is_deleted = 0;-->
 <!--          and contract_id = #{contractId} and is_deleted = 0;-->
 <!--    </select>-->
 <!--    </select>-->
 
 
+    <!-- 批量添加归档合同树 -->
+    <insert id="batchInsertArchiveTreeContract" parameterType="java.util.List">
+        INSERT INTO m_archive_tree_contract (id, tenant_id, project_id, contract_id, parent_id, ancestors, node_name,
+        full_name, tree_code, ext_type, ext_id, ext_key_id, ext_attach_id, sort, status, is_deleted, from_id, node_type,
+        is_storage_node, post_type, major_data_type, project_type, storage_type, create_user, create_dept, create_time, tree_structure)
+        VALUES
+        <foreach collection="list" item="item" separator=",">
+            (#{item.id}, #{item.tenantId}, #{item.projectId}, #{item.contractId}, #{item.parentId},#{item.ancestors},
+            #{item.nodeName},#{item.fullName},#{item.treeCode},#{item.extType},#{item.extId},#{item.extKeyId},#{item.extAttachId},
+            #{item.sort},#{item.status},#{item.isDeleted},#{item.fromId},#{item.nodeType},#{item.isStorageNode},#{item.postType},#{item.majorDataType}
+            ,#{item.projectType},#{item.storageType},#{item.createUser},#{item.createDept},#{item.createTime}, #{item.treeStructure})
+        </foreach>
+    </insert>
+
+    <!-- 批量删除归档合同树 -->
+    <update id="batchDeleteArchiveTreeContractByIdList">
+        update
+            m_archive_tree_contract
+        set
+            is_deleted = #{isDeleted},
+            update_time = NOW()
+        where
+            id in
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+    <!-- 批量修改归档树 -->
+    <update id="batchUpdateArchiveTreeContract">
+        <foreach collection="list" item="item" index="index" separator=";">
+            update
+                m_archive_tree_contract
+            <set>
+                parent_id = #{item.parentId},
+                ancestors = #{item.ancestors},
+                node_name = #{item.nodeName},
+                node_type = #{item.nodeType},
+                full_name = #{item.fullName},
+                tree_structure = #{item.treeStructure},
+                update_time = NOW()
+            </set>
+            where id = #{item.id}
+        </foreach>
+    </update>
+
 </mapper>
 </mapper>

+ 707 - 6
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractSyncImpl.java

@@ -1,35 +1,48 @@
 package org.springblade.manager.service.impl;
 package org.springblade.manager.service.impl;
 
 
+import com.alibaba.nacos.common.utils.DateFormatUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.google.protobuf.ServiceException;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.StringUtils;
 import org.springblade.archive.entity.ArchiveProjectConfig;
 import org.springblade.archive.entity.ArchiveProjectConfig;
 import org.springblade.archive.feign.ArchiveAutoClient;
 import org.springblade.archive.feign.ArchiveAutoClient;
+import org.springblade.business.dto.ImageClassificationFileDTO;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.entity.InformationQuery;
 import org.springblade.business.entity.InformationQuery;
 import org.springblade.business.feign.ArchiveFileClient;
 import org.springblade.business.feign.ArchiveFileClient;
+import org.springblade.business.feign.ImageClassificationFileClient;
 import org.springblade.business.feign.InformationQueryClient;
 import org.springblade.business.feign.InformationQueryClient;
+import org.springblade.business.vo.TreeVoTwo;
+import org.springblade.common.utils.ForestNodeMergerEx;
+import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.CollectionUtil;
+import org.springblade.core.tool.utils.ObjectUtil;
 import org.springblade.manager.entity.ArchiveTreeContract;
 import org.springblade.manager.entity.ArchiveTreeContract;
 import org.springblade.manager.entity.ContractInfo;
 import org.springblade.manager.entity.ContractInfo;
 import org.springblade.manager.entity.ContractRelationJlyz;
 import org.springblade.manager.entity.ContractRelationJlyz;
 import org.springblade.manager.entity.ProjectInfo;
 import org.springblade.manager.entity.ProjectInfo;
-import org.springblade.manager.feign.ContractClient;
+import org.springblade.manager.enums.StorageTypeEnum;
+import org.springblade.manager.enums.TreeStructureEnum;
 import org.springblade.manager.mapper.ArchiveTreeContractMapper;
 import org.springblade.manager.mapper.ArchiveTreeContractMapper;
 import org.springblade.manager.service.IContractInfoService;
 import org.springblade.manager.service.IContractInfoService;
 import org.springblade.manager.service.IProjectInfoService;
 import org.springblade.manager.service.IProjectInfoService;
-import org.springblade.common.utils.ForestNodeMergerEx;
-import org.springblade.manager.service.IWbsTreeContractService;
 import org.springblade.manager.utils.ForestNodeMerger;
 import org.springblade.manager.utils.ForestNodeMerger;
+import org.springblade.manager.utils.YearTreeUtils;
+import org.springblade.manager.vo.ArchiveTreeContractVO;
 import org.springblade.manager.vo.ArchiveTreeContractVO2;
 import org.springblade.manager.vo.ArchiveTreeContractVO2;
 import org.springblade.manager.vo.ArchiveTreeVO2;
 import org.springblade.manager.vo.ArchiveTreeVO2;
 import org.springblade.manager.vo.WbsTreeContractVO6;
 import org.springblade.manager.vo.WbsTreeContractVO6;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.*;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
 
 
 @Slf4j
 @Slf4j
 @Service
 @Service
@@ -55,6 +68,7 @@ public class ArchiveTreeContractSyncImpl {
 
 
     private final ArchiveAutoClient archiveAutoClient;
     private final ArchiveAutoClient archiveAutoClient;
 
 
+    private final ImageClassificationFileClient imageClassificationFileClient;
 
 
     /**
     /**
      * 普通同步
      * 普通同步
@@ -620,10 +634,10 @@ public class ArchiveTreeContractSyncImpl {
 
 
     public List<ArchiveTreeContract>  getArchiveTreeContractsWbs(Long contractId,Long attachId,Integer classify) {
     public List<ArchiveTreeContract>  getArchiveTreeContractsWbs(Long contractId,Long attachId,Integer classify) {
 
 
-        List<ArchiveTreeContract> archiveTreeContracts = 	archiveTreeContractMapper.selectList(Wrappers.<ArchiveTreeContract>query().lambda()
+        List<ArchiveTreeContract> archiveTreeContracts = archiveTreeContractMapper.selectList(Wrappers.<ArchiveTreeContract>query().lambda()
                 .eq(ArchiveTreeContract::getContractId, contractId)
                 .eq(ArchiveTreeContract::getContractId, contractId)
                 .eq(ArchiveTreeContract::getExtAttachId, attachId)
                 .eq(ArchiveTreeContract::getExtAttachId, attachId)
-                .eq(ArchiveTreeContract::getClassify, classify)
+                .eq(ObjectUtil.isNotEmpty(classify), ArchiveTreeContract::getClassify, classify)
                 .eq(ArchiveTreeContract::getExtType, 1));
                 .eq(ArchiveTreeContract::getExtType, 1));
         return archiveTreeContracts;
         return archiveTreeContracts;
 
 
@@ -738,5 +752,692 @@ public class ArchiveTreeContractSyncImpl {
         return groupedFiles;
         return groupedFiles;
     }
     }
 
 
+    /**
+     * 同步合同段声像文件
+     */
+    public void syncSoundImageData(Long projectId, Long contractId, Long nodeId) throws ServiceException {
+        // 合同信息
+        ContractInfo contractInfo = contractInfoService.getById(contractId);
+        // 判断是否正在同步
+        Integer isArchivesAuto = contractInfo.getIsArchivesAuto();
+        if(ObjectUtil.isNotEmpty(isArchivesAuto) && isArchivesAuto != 0) {
+            throw new ServiceException("当前合同段已经在同步中,请耐心等待");
+        }
+        // 判断节点存储类型是否为声像
+        ArchiveTreeContract archiveTreeContract = archiveTreeContractMapper.selectById(nodeId);
+        if(archiveTreeContract != null) {
+            if(!archiveTreeContract.getStorageType().equals(StorageTypeEnum.SOUND_IMAGE.getCode())){
+                throw new ServiceException("当前节点存储类型不是声像,无法同步声像文件");
+            }
+        }else{
+            throw new ServiceException("当前节点不存在,同步失败");
+        }
+        log.info("开始自动组卷...." + contractId);
+        //设置自动组卷中
+        contractInfoService.updateIsArchivesAutoById(contractId,1);
+        try {
+            // 同步文件
+            syncSoundImageData(projectId,contractInfo,archiveTreeContract);
+            //完成自动组卷
+            contractInfoService.updateIsArchivesAutoById(contractId, 0);
+        }catch (Exception e){
+            e.printStackTrace();
+            //设置自动组卷结束
+            contractInfoService.updateIsArchivesAutoById(contractId, 0);
+            log.error("同步声像文件报错,projectId:"+projectId+"、contractId:"+contractId+"nodeId:"+nodeId+",原因:"+e.getMessage());
+            throw new ServiceException("同步声像文件报错");
+        }
+    }
+
+    /**
+     * 同步某个合同段的声像资料到归档
+     */
+    public void syncSoundImageData(Long projectId, ContractInfo contractInfo, ArchiveTreeContract archiveTreeContract) {
+        Long contractId = contractInfo.getId();
+
+        log.info("同步声像文件,projectId:"+projectId+",contractId:"+contractId+",nodeId:"+archiveTreeContract.getId());
+
+        //项目信息
+        ProjectInfo projectInfo = projectInfoService.getOne(projectId);
+
+        //项目使用的wbs模板
+        Long lWbsId = projectInfo.getReferenceWbsTemplateId();
+
+        //获取合同段wbs划分树,用来组织树
+        List<WbsTreeContractVO6> wbsTrees = contractInfoService.tree8(lWbsId.toString(), projectId.toString(), contractId.toString());
+        if (CollectionUtil.isEmpty(wbsTrees)) {
+            return;
+        }
+
+        // wbs转换archive树形集合
+        List<ArchiveTreeContract> wbsCovertArchiveTreeContractList = new ArrayList<>();
+
+        // wbs转换archive,并初始化值
+        getTreeList(AuthUtil.getTenantId(), wbsTrees.get(0), wbsCovertArchiveTreeContractList, archiveTreeContract, archiveTreeContract.getId());
+
+        log.info("同步声像文件树  imageClassificationFileList :"+wbsCovertArchiveTreeContractList.size());
+
+        //同步质检关联节点
+        syncNodes(wbsCovertArchiveTreeContractList, contractInfo, archiveTreeContract);
+    }
+
+    /**
+     * 将树形节点对象转换为普通的list集合
+     */
+    public void getTreeList(String tenantId, WbsTreeContractVO6 tree, List<ArchiveTreeContract> wbsCovertArchiveTreeContractList, ArchiveTreeContract archiveTreeContract, Long archiveTreeContractId) {
+        if (ObjectUtil.isEmpty(tree)) {
+            return;
+        }
+        // wbs树转archive树
+        ArchiveTreeContract archiveTreeContractTemp = wbsTreeContractCovertArchiveTreeContract(tenantId, tree, archiveTreeContract, archiveTreeContractId);
+        wbsCovertArchiveTreeContractList.add(archiveTreeContractTemp);
+
+        // 循环遍历,转换树对象
+        List<WbsTreeContractVO6> childrenList = tree.getChildren();
+        if (childrenList != null) {
+            for (WbsTreeContractVO6 child : childrenList) {
+                getTreeList(tenantId, child, wbsCovertArchiveTreeContractList, archiveTreeContractTemp, archiveTreeContractId);
+            }
+        }
+    }
+
+    /**
+     * 将wbs树初始化为归档树
+     */
+    public ArchiveTreeContract wbsTreeContractCovertArchiveTreeContract(String tenantId, WbsTreeContractVO6 tree, ArchiveTreeContract archiveTreeContract, Long archiveTreeContractId) {
+        ArchiveTreeContract archiveTreeContractTemp = new ArchiveTreeContract();
+        //设置基本信息
+        archiveTreeContractTemp.setId(SnowFlakeUtil.getId());
+        archiveTreeContractTemp.setTenantId(tenantId);
+        archiveTreeContractTemp.setProjectId(archiveTreeContract.getProjectId());
+        archiveTreeContractTemp.setContractId(archiveTreeContract.getContractId());
+        archiveTreeContractTemp.setParentId(archiveTreeContract.getId());
+        archiveTreeContractTemp.setAncestors(archiveTreeContract.getAncestors()+","+archiveTreeContract.getId());
+        archiveTreeContractTemp.setNodeName(tree.getNodeName());
+        archiveTreeContractTemp.setNodeType(1);
+        archiveTreeContractTemp.setFullName(tree.getFullName());
+        archiveTreeContractTemp.setTreeCode(archiveTreeContract.getContractId().toString());
+        //设置关联
+        archiveTreeContractTemp.setExtType(1);
+        archiveTreeContractTemp.setExtId(tree.getId());
+        archiveTreeContractTemp.setExtKeyId(tree.getPKeyId());
+        archiveTreeContractTemp.setExtAttachId(archiveTreeContractId);
+        archiveTreeContractTemp.setSort(tree.getSort());
+        archiveTreeContractTemp.setStatus(1);
+        archiveTreeContractTemp.setIsDeleted(0);
+        archiveTreeContractTemp.setFromId(tree.getId());
+        if (StringUtils.isNotEmpty(tree.getOldId())) {
+            archiveTreeContractTemp.setFromId(Long.parseLong(tree.getOldId()));
+        }
+        archiveTreeContractTemp.setIsStorageNode(1);
+        //wbs_tree_contract与archive_tree_contract字段等存在差异,不清楚的字段值,我沿用同步节点的字段值进行初始化
+        archiveTreeContractTemp.setPostType(archiveTreeContract.getPostType());
+        archiveTreeContractTemp.setMajorDataType(archiveTreeContract.getMajorDataType());
+        archiveTreeContractTemp.setProjectType(archiveTreeContract.getProjectType());
+        archiveTreeContractTemp.setStorageType(archiveTreeContract.getStorageType());
+        archiveTreeContractTemp.setCreateUser(tree.getCreateUser());
+        archiveTreeContractTemp.setCreateDept(tree.getCreateDept());
+        archiveTreeContractTemp.setCreateTime(tree.getCreateTime());
+        archiveTreeContractTemp.setTreeStructure(TreeStructureEnum.ORDINARY_TREE.getCode());
+        return archiveTreeContractTemp;
+    }
+
+    private void syncNodes(List<ArchiveTreeContract> wbsCovertArchiveTreeContractList,
+                           ContractInfo contractInfo, ArchiveTreeContract archiveTreeContract) {
+        Long contractId = contractInfo.getId();
+
+        // 获取关联节点
+        List<ArchiveTreeContract> associatedNodes = Arrays.asList(archiveTreeContract);
+
+        log.info("同步声像文件树节点  syncNodes:"+associatedNodes.size());
+
+        // 新增的归档树聚合
+        List<ArchiveTreeContract> saveArchiveTreeContractList = new ArrayList<>();
+        // 新增的归档文件集合
+        List<ArchiveFile> saveArchiveFileList = new ArrayList<>();
+        // 修改的归档树聚合
+        List<Map> updateArchiveTreeContractList = new ArrayList<>();
+        // 修改的归档文件集合
+        List<ArchiveFile> updateArchiveFileList = new ArrayList<>();
+        // 删除的归档树聚合
+        List<ArchiveTreeContract> deleteArchiveTreeContractList = new ArrayList<>();
+        // 删除的归档文件集合
+        List<ArchiveFile> deleteArchiveFileList = new ArrayList<>();
+
+        for (ArchiveTreeContract node : associatedNodes) {
+            log.info("同步声像文件树节点  syncNode:" + node.getId() + "-" +  node.getNodeName());
+
+            /***同步树-开始*/
+            // 获取该合同段下的wbs节点
+            List<ArchiveTreeContract> originalArchiveTreeContractList = getArchiveTreeContractsWbs(contractId, node.getId(),null);
+
+            // 树形map
+            Map<Long, ArchiveTreeContract> originalArchiveTreeContractMap = buildNodeMap(originalArchiveTreeContractList);
+
+            // 转换类型
+            List<ArchiveTreeContractVO> archiveTreeContractVOList = wbsCovertArchiveTreeContractList.stream().map(tree -> {
+                ArchiveTreeContractVO vo = new ArchiveTreeContractVO();
+                BeanUtils.copyProperties(tree, vo);
+                return vo;
+            }).collect(Collectors.toList());
+
+            // list转换为树形
+            List<ArchiveTreeContractVO> mergeList = org.springblade.core.tool.node.ForestNodeMerger.merge(archiveTreeContractVOList);
+
+            // 与已有数据做合并(目的是将已有的树信息复制到对应的树)
+            archiveAndOldTreeMergeData(mergeList, originalArchiveTreeContractMap, TreeStructureEnum.ORDINARY_TREE);
+
+            // 重新刷新整个树结构(刷新整个树的父子节点id)
+            for (ArchiveTreeContractVO archiveTreeContractVO : mergeList) {
+                refreshTreeStructure(archiveTreeContractVO, node.getId(), node.getAncestors());
+            }
+
+            // 将树转换为list
+            List<ArchiveTreeContractVO> archiveTreeContractVOTempList = new ArrayList<>();
+            ForestNodeMerger.getTreeList(mergeList.get(0), archiveTreeContractVOTempList);
+
+            List<ArchiveTreeContract> newArchiveTreeContractList = archiveTreeContractVOTempList.stream().map(vo -> {
+                ArchiveTreeContract archiveTreeContractTemp = new ArchiveTreeContract();
+                BeanUtils.copyProperties(vo, archiveTreeContractTemp);
+                return vo;
+            }).collect(Collectors.toList());
+
+            // 树形map
+            Map<Long, ArchiveTreeContract> newArchiveTreeContractMap = buildNodeMap(newArchiveTreeContractList);
+
+            // 取两个树形集合中的差异,并输出新增、修改、删除三个集合
+            diffTrees(originalArchiveTreeContractList, newArchiveTreeContractList, originalArchiveTreeContractMap,
+                    newArchiveTreeContractMap, saveArchiveTreeContractList, deleteArchiveTreeContractList, updateArchiveTreeContractList);
+            /***同步树-结束*/
+
+            /***同步树型文件数据准备-开始*/
+            // 根据合同段id查询声像文件
+            List<ImageClassificationFileDTO> imageClassificationFileList = getImageClassificationFileListByContractId(contractId);
+
+            // 声像文件转换
+            List<ArchiveFile> newArchiveFileList = imageClassificationFileList.stream().map(file -> {
+                ArchiveFile archiveFile = imageClassificationFileCovertArchiveFile(file, node, newArchiveTreeContractMap);
+                return archiveFile;
+            }).collect(Collectors.toList());
+
+            // 查询合同段下的声像文件
+            List<ArchiveFile> originalArchiveFileList = archiveFileClient.getListByContractIdAndArchiveFileStorageType(contractId, StorageTypeEnum.SOUND_IMAGE.getCode());
+
+            // 构件原始文件TreeMap
+            Map<Long, ArchiveFile> originalArchiveFileMap = buildArchiveFileMap(originalArchiveFileList);
+
+            // 与已有数据做合并(目的是将已有的树信息复制到对应的树)
+            originalAndNewArchiveFileMergeData(newArchiveFileList, originalArchiveFileMap);
+
+            // 构件新文件TreeMap
+            Map<Long, ArchiveFile> newArchiveFileMap = buildArchiveFileMap(newArchiveFileList);
+
+            // 将声像文件集合遍历,分为时间储存和树形储存两个集合
+            List<ArchiveFile> newArchiveFileTreeList = new ArrayList<>();
+            List<ArchiveFile> newArchiveFileDateList = new ArrayList<>();
+            for (ArchiveFile archiveFile : newArchiveFileList) {
+                if(archiveFile.getNodeTreeStructure() == 1) {
+                    newArchiveFileTreeList.add(archiveFile);
+                }else if(archiveFile.getNodeTreeStructure() == 2) {
+                    newArchiveFileDateList.add(archiveFile);
+                }
+            }
+
+            // 将声像文件集合遍历,分为时间储存和树形储存两个集合
+            List<ArchiveFile> originalArchiveFileTreeList = new ArrayList<>();
+            List<ArchiveFile> originalArchiveFileDateList = new ArrayList<>();
+            for (ArchiveFile archiveFile : originalArchiveFileList) {
+                if(archiveFile.getNodeTreeStructure() == 1) {
+                    originalArchiveFileTreeList.add(archiveFile);
+                }else if(archiveFile.getNodeTreeStructure() == 2) {
+                    originalArchiveFileDateList.add(archiveFile);
+                }
+            }
+            /***同步树型文件数据准备-结束*/
+
+            /**同步树形文件-开始**/
+            // 取两个树形集合中的差异,并输出新增、修改、删除三个集合
+            diffFiles(originalArchiveFileTreeList, newArchiveFileTreeList, originalArchiveFileMap, newArchiveFileMap,
+                    saveArchiveFileList, deleteArchiveFileList, updateArchiveFileList, newArchiveTreeContractMap, TreeStructureEnum.ORDINARY_TREE);
+            /**同步树形文件-结束**/
+
+            /**同步时间树-开始**/
+            // 获取该合同段下的时间型节点
+            List<ArchiveTreeContract> originalDateArchiveTreeContractList = getArchiveTreeContractsByTreeStructure(contractId, node.getId(), TreeStructureEnum.DATE_TREE);
+
+            // 树形map
+            Map<Long, ArchiveTreeContract> originalDateArchiveTreeContractMap = buildDateMap(originalDateArchiveTreeContractList);
+
+            // 将最新文件的时间取出,创建时间树
+            List<String> dateList = newArchiveFileDateList.stream().map(archiveFile -> {
+                String format = DateFormatUtils.format(archiveFile.getCreateTime(), "yyyy-MM-dd");
+                return format;
+            }).collect(Collectors.toList());
+
+            // 转时间树
+            List<TreeVoTwo> dateTreeList = YearTreeUtils.yearMonthDayTree(dateList, "DESC");
+
+            // date转换archive树形集合
+            List<ArchiveTreeContractVO> dateCovertArchiveTreeContractVoList = new ArrayList<>();
+
+            // date转换archive,并初始化值
+            getTreeList(AuthUtil.getTenantId(), dateTreeList, dateCovertArchiveTreeContractVoList, archiveTreeContract, archiveTreeContract.getId());
+
+            // list转换为树形
+            List<ArchiveTreeContractVO> mergeDateList = org.springblade.core.tool.node.ForestNodeMerger.merge(dateCovertArchiveTreeContractVoList);
+
+            // 与已有数据做合并(目的是将已有的树信息复制到对应的树)
+            archiveAndOldTreeMergeData(mergeDateList, originalDateArchiveTreeContractMap, TreeStructureEnum.DATE_TREE);
+
+            // 重新刷新整个树结构(刷新整个树的父子节点id)
+            for (ArchiveTreeContractVO dateArchiveTreeContractVO : mergeDateList) {
+                refreshTreeStructure(dateArchiveTreeContractVO, node.getId(), node.getAncestors());
+            }
+
+            // 将树转换为list
+            List<ArchiveTreeContractVO> dateArchiveTreeContractVOTempList = new ArrayList<>();
+            for (ArchiveTreeContractVO archiveTreeContractVO : mergeDateList) {
+                ForestNodeMerger.getTreeList(archiveTreeContractVO, dateArchiveTreeContractVOTempList);
+            }
+            // 转实体类
+            List<ArchiveTreeContract> newDateArchiveTreeContractList = dateArchiveTreeContractVOTempList.stream().map(vo -> {
+                ArchiveTreeContract archiveTreeContractTemp = new ArchiveTreeContract();
+                BeanUtils.copyProperties(vo, archiveTreeContractTemp);
+                return vo;
+            }).collect(Collectors.toList());
+
+            // 树形map
+            Map<Long, ArchiveTreeContract> newDateArchiveTreeContractMap = buildDateMap(newDateArchiveTreeContractList);
+
+            // 取两个树形集合中的差异,并输出新增、修改、删除三个集合
+            diffDateTrees(originalDateArchiveTreeContractList, newDateArchiveTreeContractList, originalDateArchiveTreeContractMap,
+                    newDateArchiveTreeContractMap, saveArchiveTreeContractList, deleteArchiveTreeContractList, updateArchiveTreeContractList);
+            /**同步时间树-结束**/
+
+            /**同步时间树形文件-开始**/
+            // 取两个树形集合中的差异,并输出新增、修改、删除三个集合
+            diffFiles(originalArchiveFileDateList, newArchiveFileDateList, originalArchiveFileMap, newArchiveFileMap,
+                    saveArchiveFileList, deleteArchiveFileList, updateArchiveFileList, newDateArchiveTreeContractMap, TreeStructureEnum.DATE_TREE);
+            /**同步时间树形文件-结束**/
+
+            /**统一刷新树与文件**/
+            // 刷新树
+            refreshArchiveTreeContract(saveArchiveTreeContractList, updateArchiveTreeContractList, deleteArchiveTreeContractList);
+
+            // 刷新文件
+            refreshArchiveFile(saveArchiveFileList, updateArchiveFileList, deleteArchiveFileList);
+        }
+    }
+    /**
+     * 时间树中的差异
+     * 遍历循环,获取差异,并输出三个集合(归档树的fullName = 老树中的fullName)
+     */
+    private static void diffDateTrees(List<ArchiveTreeContract> originalDateArchiveTreeContractList, List<ArchiveTreeContract> newDateArchiveTreeContractList, Map<Long, ArchiveTreeContract> originalDateArchiveTreeContractMap,
+                                  Map<Long, ArchiveTreeContract> newDateArchiveTreeContractMap, List<ArchiveTreeContract> insertions,
+                                  List<ArchiveTreeContract> deletions, List<Map> modifications) {
+        for (ArchiveTreeContract originalDateArchiveTreeContract : originalDateArchiveTreeContractList) {
+            if (!newDateArchiveTreeContractMap.containsKey(originalDateArchiveTreeContract.getFullName())) {
+                deletions.add(originalDateArchiveTreeContract);
+            } else {
+                ArchiveTreeContract node2 = newDateArchiveTreeContractMap.get(originalDateArchiveTreeContract.getFullName());
+                if (originalDateArchiveTreeContract.getFullName().equals(node2.getFullName())) {
+                    Map map = new HashMap();
+                    map.put("id", originalDateArchiveTreeContract.getId());
+                    map.put("parentId", node2.getParentId());
+                    map.put("ancestors", node2.getAncestors());
+                    map.put("nodeName", node2.getNodeName());
+                    map.put("nodeType", node2.getNodeType());
+                    map.put("fullName", node2.getFullName());
+                    map.put("treeStructure", node2.getTreeStructure());
+                    modifications.add(map);
+                }
+            }
+        }
+        for (ArchiveTreeContract newArchiveTreeContract : newDateArchiveTreeContractList) {
+            if (!originalDateArchiveTreeContractMap.containsKey(newArchiveTreeContract.getFullName())) {
+                insertions.add(newArchiveTreeContract);
+            }
+        }
+    }
+
+    /**
+     * 根据tree结构获取归档树
+     */
+    public List<ArchiveTreeContract>  getArchiveTreeContractsByTreeStructure(Long contractId,Long attachId, TreeStructureEnum treeStructure) {
+        List<ArchiveTreeContract> archiveTreeContracts = archiveTreeContractMapper.selectList(Wrappers.<ArchiveTreeContract>query().lambda()
+                .eq(ArchiveTreeContract::getContractId, contractId)
+                .eq(ArchiveTreeContract::getExtAttachId, attachId)
+                .eq(ArchiveTreeContract::getTreeStructure, treeStructure.getCode()));
+        return archiveTreeContracts;
+    }
+
+    /**
+     * 将时间树形节点对象转换为普通的list集合
+     */
+    public void getTreeList(String tenantId, List<TreeVoTwo> dateTreeList, List<ArchiveTreeContractVO> dateCovertArchiveTreeContractList, ArchiveTreeContract archiveTreeContract, Long archiveTreeContractId) {
+        if (CollectionUtil.isEmpty(dateTreeList)) {
+            return;
+        }
+        for (TreeVoTwo tree : dateTreeList) {
+            // wbs树转archive树
+            ArchiveTreeContractVO archiveTreeContractVoTemp = dateTreeCovertArchiveTreeContract(tenantId, tree, archiveTreeContract, archiveTreeContractId);
+            dateCovertArchiveTreeContractList.add(archiveTreeContractVoTemp);
+
+            // 循环遍历,转换树对象
+            List<TreeVoTwo> childrenList = tree.getChildren();
+            if (childrenList != null) {
+                getTreeList(tenantId, childrenList, dateCovertArchiveTreeContractList, archiveTreeContractVoTemp, archiveTreeContractId);
+            }
+        }
+    }
+
+    /**
+     * 将时间树初始化为归档树
+     */
+    public ArchiveTreeContractVO dateTreeCovertArchiveTreeContract(String tenantId, TreeVoTwo tree, ArchiveTreeContract archiveTreeContract, Long archiveTreeContractId) {
+        ArchiveTreeContractVO archiveTreeContractVoTemp = new ArchiveTreeContractVO();
+        //设置基本信息
+        archiveTreeContractVoTemp.setId(SnowFlakeUtil.getId());
+        archiveTreeContractVoTemp.setTenantId(tenantId);
+        archiveTreeContractVoTemp.setProjectId(archiveTreeContract.getProjectId());
+        archiveTreeContractVoTemp.setContractId(archiveTreeContract.getContractId());
+        archiveTreeContractVoTemp.setParentId(archiveTreeContract.getId());
+        archiveTreeContractVoTemp.setAncestors(archiveTreeContract.getAncestors()+","+archiveTreeContract.getId());
+        archiveTreeContractVoTemp.setNodeName(tree.getName());
+        archiveTreeContractVoTemp.setNodeType(1);
+        archiveTreeContractVoTemp.setFullName(tree.getHierarchy());
+        archiveTreeContractVoTemp.setTreeCode(archiveTreeContract.getContractId().toString());
+        //设置关联
+        archiveTreeContractVoTemp.setExtAttachId(archiveTreeContractId);
+        archiveTreeContractVoTemp.setSort(archiveTreeContract.getSort());
+        archiveTreeContractVoTemp.setStatus(1);
+        archiveTreeContractVoTemp.setIsDeleted(0);
+        archiveTreeContractVoTemp.setIsStorageNode(1);
+        //时间树不同于wbs树,不清楚的字段值,我沿用同步节点的字段值进行初始化
+        archiveTreeContractVoTemp.setPostType(archiveTreeContract.getPostType());
+        archiveTreeContractVoTemp.setMajorDataType(archiveTreeContract.getMajorDataType());
+        archiveTreeContractVoTemp.setProjectType(archiveTreeContract.getProjectType());
+        archiveTreeContractVoTemp.setStorageType(archiveTreeContract.getStorageType());
+        archiveTreeContractVoTemp.setCreateUser(archiveTreeContract.getCreateUser());
+        archiveTreeContractVoTemp.setCreateDept(archiveTreeContract.getCreateDept());
+        archiveTreeContractVoTemp.setCreateTime(archiveTreeContract.getCreateTime());
+        archiveTreeContractVoTemp.setTreeStructure(TreeStructureEnum.DATE_TREE.getCode());
+        return archiveTreeContractVoTemp;
+    }
+
+    /**
+     * 刷新归档文件
+     */
+    void refreshArchiveFile(List<ArchiveFile> saveArchiveFileList, List<ArchiveFile> updateArchiveFileList,
+                            List<ArchiveFile> deleteArchiveFileList) {
+        if (CollectionUtil.isNotEmpty(saveArchiveFileList)) {
+            archiveFileClient.addArchiveFile(saveArchiveFileList);
+        }
+        if (CollectionUtil.isNotEmpty(updateArchiveFileList)) {
+            archiveFileClient.updateArchiveFile(updateArchiveFileList);
+        }
+        if (CollectionUtil.isNotEmpty(deleteArchiveFileList)) {
+            List<Long> idList = deleteArchiveFileList.stream().map(ArchiveFile::getId).collect(Collectors.toList());
+            archiveFileClient.removeFile(idList);
+        }
+    }
+
+    /**
+     * 现有的树与wbs的树做合并
+     */
+    public void originalAndNewArchiveFileMergeData(List<ArchiveFile> mergeList, Map<Long, ArchiveFile> archiveFileMap) {
+        if (CollectionUtil.isEmpty(mergeList)) {
+            return;
+        }
+        // 循环遍历,将已有的赋值
+        for (ArchiveFile archiveFile : mergeList) {
+            ArchiveFile archiveFileTemp = archiveFileMap.get(archiveFile.getUImageClassificationFileId());
+            if(ObjectUtil.isNotEmpty(archiveFileTemp)){
+                archiveFile.setId(archiveFileTemp.getId());
+                archiveFile.setNodeId(archiveFileTemp.getNodeId());
+            }
+        }
+    }
+
+    /**
+     * 遍历循环,获取差异,并输出三个集合(archiveFile表的uImageClassificationFileId = ImageClassificationFile 的 id)
+     */
+    private static void diffFiles(List<ArchiveFile> originalArchiveFileList, List<ArchiveFile> newArchiveFileList, Map<Long, ArchiveFile> originalArchiveFileTreeMap,
+                                  Map<Long, ArchiveFile> newArchiveFileTreeMap, List<ArchiveFile> insertions, List<ArchiveFile> deletions, List<ArchiveFile> modifications,
+                                  Map<Long, ArchiveTreeContract> newArchiveTreeContractMap, TreeStructureEnum treeStructureEnum) {
+        for (ArchiveFile originalArchiveFile : originalArchiveFileList) {
+            if (!newArchiveFileTreeMap.containsKey(originalArchiveFile.getUImageClassificationFileId())) {
+                deletions.add(originalArchiveFile);
+            } else {
+                ArchiveFile node2 = newArchiveFileTreeMap.get(originalArchiveFile.getUImageClassificationFileId());
+                if (originalArchiveFile.getUImageClassificationFileId().equals(node2.getUImageClassificationFileId())) {
+                    modifications.add(node2);
+                }
+            }
+        }
+        for (ArchiveFile newArchiveFile : newArchiveFileList) {
+            if (!originalArchiveFileTreeMap.containsKey(newArchiveFile.getUImageClassificationFileId())) {
+                ArchiveTreeContract archiveTreeContract = null;
+                if(treeStructureEnum.getCode() == TreeStructureEnum.ORDINARY_TREE.getCode()){
+                    archiveTreeContract = newArchiveTreeContractMap.get(newArchiveFile.getMWbsTreeContractPKeyId());
+                } else if(treeStructureEnum.getCode() == TreeStructureEnum.DATE_TREE.getCode()) {
+                    archiveTreeContract = newArchiveTreeContractMap.get(Long.valueOf(newArchiveFile.getDateName()));
+                }
+                if(ObjectUtil.isNotEmpty(archiveTreeContract)){
+                    newArchiveFile.setNodeId(String.valueOf(archiveTreeContract.getId()));
+                }
+                insertions.add(newArchiveFile);
+            }
+        }
+    }
+
+    /**
+     * 构建节点map
+     */
+    private static Map<Long, ArchiveFile> buildArchiveFileMap(List<ArchiveFile> archiveFileList) {
+        Map<Long, ArchiveFile> map = new HashMap<>();
+        for (ArchiveFile archiveFile : archiveFileList) {
+            map.put(archiveFile.getUImageClassificationFileId(), archiveFile);
+        }
+        return map;
+    }
+
+    /**
+     * imageClassificationFile 转换为 archiveFile
+     */
+    private ArchiveFile imageClassificationFileCovertArchiveFile(ImageClassificationFileDTO file,
+                                                                 ArchiveTreeContract node,
+                                                                 Map<Long, ArchiveTreeContract> wbsCovertArchiveTreeContractMap)
+    {
+        ArchiveFile archiveFile = new ArchiveFile();
+        archiveFile.setId(SnowFlakeUtil.getId());
+        archiveFile.setProjectId(String.valueOf(node.getProjectId()));
+        archiveFile.setContractId(String.valueOf(node.getContractId()));
+        ArchiveTreeContract archiveTreeContractTemp = wbsCovertArchiveTreeContractMap.get(Long.valueOf(file.getWbsId()));
+        if(archiveTreeContractTemp != null){
+            archiveFile.setNodeId(String.valueOf(archiveTreeContractTemp.getId()));
+        }
+        archiveFile.setFileNumber(String.valueOf(file.getId()));
+        archiveFile.setFileName(file.getTitle());
+        archiveFile.setFileTime(file.getUploadTime());
+        archiveFile.setFileUrl(file.getImageUrl());
+        archiveFile.setPdfFileUrl(file.getPdfUrl());
+        archiveFile.setFilePage(1);
+        archiveFile.setIsApproval(0);
+        archiveFile.setIsCertification(1);
+        archiveFile.setIsNeedCertification(0);
+        archiveFile.setDutyUser(file.getShootingUser());
+        archiveFile.setCreateUser(file.getCreateUser());
+        archiveFile.setCreateDept(file.getCreateDept());
+        archiveFile.setCreateTime(file.getCreateTime());
+        archiveFile.setUpdateUser(file.getUpdateUser());
+        archiveFile.setUpdateTime(file.getUpdateTime());
+        archiveFile.setStatus(0);
+        archiveFile.setIsDeleted(0);
+        archiveFile.setFileType(Long.valueOf(file.getType()));
+        archiveFile.setFilmingTime(file.getShootingTime());
+        archiveFile.setFilmingorTime(file.getShootingTime());
+        archiveFile.setPicCode(file.getPhotoCode());
+        archiveFile.setFilmCode(file.getFilmCode());
+        archiveFile.setReferCode(file.getSeeAlsoCode());
+        archiveFile.setWidth(file.getFileWidth());
+        archiveFile.setHeight(file.getFileHeight());
+        // 创建 LocalDateTime 时间爱你
+        LocalDateTime localDateTime = new Date().toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalDateTime();
+        archiveFile.setFtime(localDateTime);
+        archiveFile.setUtime(localDateTime);
+        archiveFile.setSort(0);
+        archiveFile.setPageNum("1");
+        archiveFile.setFileSize(convertSizeToBytes(file.getFileSize()));
+        archiveFile.setSourceType(1);
+        archiveFile.setIsElement(0);
+        archiveFile.setRectification(0);
+        archiveFile.setMWbsTreeContractPKeyId(file.getWbsId());
+        archiveFile.setUImageClassificationFileId(file.getId());
+        archiveFile.setArchiveFileStorageType(StorageTypeEnum.SOUND_IMAGE.getCode());
+        archiveFile.setNodeTreeStructure(file.getStorageDirectoryFormat());
+        archiveFile.setDateName(DateFormatUtils.format(file.getCreateTime(), "yyyyMMdd"));
+        return archiveFile;
+    }
+
+    /**
+     * imageClassificationFile里面的fileSize,是已经转换过的(230.6MB),是字符串
+     * 而archiveFile里面的fileSize是纯数字大小,现在要转换回来
+     */
+    private static Long convertSizeToBytes(String size) {
+        Long value = Double.valueOf(size.replaceAll("[^\\d.]", "")).longValue();
+        String unit = size.replaceAll("[\\d.]", "").trim();
+        switch (unit) {
+            case "B":
+                return value;
+            case "KB":
+                return value * 1024;
+            case "MB":
+                return value * 1024 * 1024;
+            case "GB":
+                return value * 1024 * 1024 * 1024;
+            default:
+                throw new IllegalArgumentException("Invalid size unit: " + unit);
+        }
+    }
+
+    /**
+     * 重新刷新整个树结构
+     */
+    public void refreshTreeStructure(ArchiveTreeContractVO archiveTreeContractVO, Long id, String ancestors) {
+        archiveTreeContractVO.setParentId(id);
+        archiveTreeContractVO.setAncestors(ancestors+","+id);
+        List<ArchiveTreeContractVO> children = archiveTreeContractVO.getChildren();
+        if (CollectionUtil.isNotEmpty(children)) {
+            for (ArchiveTreeContractVO child : children) {
+                refreshTreeStructure(child, archiveTreeContractVO.getId(), archiveTreeContractVO.getAncestors());
+            }
+        }
+    }
+
+    /**
+     * 现有的树与老的树做合并
+     */
+    public void archiveAndOldTreeMergeData(List<ArchiveTreeContractVO> mergeList, Map<Long, ArchiveTreeContract> archiveTreeContractMap, TreeStructureEnum treeStructureEnum) {
+        if (CollectionUtil.isEmpty(mergeList)) {
+            return;
+        }
+        // 循环遍历,将已有的赋值,随后再次将整个树的上下节点,重新刷新
+        for (ArchiveTreeContractVO archiveTreeContractVO : mergeList) {
+            ArchiveTreeContract archiveTreeContract = null;
+            if(treeStructureEnum.getCode() == TreeStructureEnum.ORDINARY_TREE.getCode()){
+                archiveTreeContract = archiveTreeContractMap.get(archiveTreeContractVO.getExtKeyId());
+            } else if(treeStructureEnum.getCode() == TreeStructureEnum.DATE_TREE.getCode()) {
+                archiveTreeContract = archiveTreeContractMap.get(archiveTreeContractVO.getFullName());
+            }
+            if(ObjectUtil.isNotEmpty(archiveTreeContract)){
+                archiveTreeContractVO.setId(archiveTreeContract.getId());
+                archiveTreeContractVO.setParentId(archiveTreeContract.getParentId());
+                archiveTreeContractVO.setAncestors(archiveTreeContract.getAncestors());
+            }
+            archiveAndOldTreeMergeData(archiveTreeContractVO.getChildren(), archiveTreeContractMap, treeStructureEnum);
+        }
+    }
+
+    /**
+     * 刷新归档树
+     */
+    void refreshArchiveTreeContract(List<ArchiveTreeContract> saveArchiveTreeContractList, List<Map> updateArchiveTreeContractList,
+                                    List<ArchiveTreeContract> deleteArchiveTreeContractList) {
+        if (CollectionUtil.isNotEmpty(saveArchiveTreeContractList)) {
+            archiveTreeContractMapper.batchInsertArchiveTreeContract(saveArchiveTreeContractList);
+        }
+        if (CollectionUtil.isNotEmpty(updateArchiveTreeContractList)) {
+            archiveTreeContractMapper.batchUpdateArchiveTreeContract(updateArchiveTreeContractList);
+        }
+        if (CollectionUtil.isNotEmpty(deleteArchiveTreeContractList)) {
+            List<Long> idList = deleteArchiveTreeContractList.stream().map(ArchiveTreeContract::getId).collect(Collectors.toList());
+            archiveTreeContractMapper.batchDeleteArchiveTreeContractByIdList(idList, 1);
+        }
+    }
+
+    /**
+     * 根据合同段id查询声像文件
+     */
+    public List<ImageClassificationFileDTO> getImageClassificationFileListByContractId(Long contractId) {
+        return imageClassificationFileClient.getImageClassificationFileListByContractId(contractId);
+    }
+
+    /**
+     * 遍历循环,获取差异,并输出三个集合(归档树的ExtKeyId = Wbs树的pKeyId)
+     */
+    private static void diffTrees(List<ArchiveTreeContract> originalArchiveTreeContractList, List<ArchiveTreeContract> newArchiveTreeContractList, Map<Long, ArchiveTreeContract> originalArchiveTreeContractMap,
+                                  Map<Long, ArchiveTreeContract> newArchiveTreeContractMap, List<ArchiveTreeContract> insertions,
+                                  List<ArchiveTreeContract> deletions, List<Map> modifications) {
+        for (ArchiveTreeContract originalArchiveTreeContract : originalArchiveTreeContractList) {
+            if (!newArchiveTreeContractMap.containsKey(originalArchiveTreeContract.getExtKeyId())) {
+                deletions.add(originalArchiveTreeContract);
+            } else {
+                ArchiveTreeContract node2 = newArchiveTreeContractMap.get(originalArchiveTreeContract.getExtKeyId());
+                if (originalArchiveTreeContract.getExtKeyId().equals(node2.getExtKeyId())) {
+                    // 更新修改数据
+                    Map map = new HashMap();
+                    map.put("id", originalArchiveTreeContract.getId());
+                    map.put("parentId", node2.getParentId());
+                    map.put("ancestors", node2.getAncestors());
+                    map.put("nodeName", node2.getNodeName());
+                    map.put("nodeType", node2.getNodeType());
+                    map.put("fullName", node2.getFullName());
+                    map.put("treeStructure", node2.getTreeStructure());
+                    modifications.add(map);
+                }
+            }
+        }
+        for (ArchiveTreeContract newArchiveTreeContract : newArchiveTreeContractList) {
+            if (!originalArchiveTreeContractMap.containsKey(newArchiveTreeContract.getExtKeyId())) {
+                insertions.add(newArchiveTreeContract);
+            }
+        }
+    }
+
+    /**
+     * 构建节点map
+     */
+    private static Map<Long, ArchiveTreeContract> buildNodeMap(List<ArchiveTreeContract> tree) {
+        Map<Long, ArchiveTreeContract> map = new HashMap<>();
+        for (ArchiveTreeContract node : tree) {
+            map.put(node.getExtKeyId(), node);
+        }
+        return map;
+    }
+
+    /**
+     * 构建时间map
+     */
+    private static Map<Long, ArchiveTreeContract> buildDateMap(List<ArchiveTreeContract> tree) {
+        Map<Long, ArchiveTreeContract> map = new HashMap<>();
+        for (ArchiveTreeContract node : tree) {
+            // 时间树中,fullName表示该节点的详细时间,如20240109,唯一性
+            map.put(Long.valueOf(node.getFullName()), node);
+        }
+        return map;
+    }
 
 
 }
 }

+ 83 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/YearTreeUtils.java

@@ -0,0 +1,83 @@
+package org.springblade.manager.utils;
+
+import org.springblade.business.vo.TreeVo;
+import org.springblade.business.vo.TreeVoTwo;
+import org.springblade.common.utils.SnowFlakeUtil;
+
+import java.util.*;
+
+public class YearTreeUtils {
+
+    public static List<TreeVoTwo> yearMonthDayTree(List<String> yearMonthDayList, String sort) {
+
+        //最终集合
+        List<TreeVoTwo> result = new ArrayList<>();
+        if (yearMonthDayList == null || yearMonthDayList.size() <= 0) {
+            return result;
+        }
+
+        //年
+        Map<String, List<String>> yearMap = new HashMap<>();
+        for (String dateStr : yearMonthDayList) {
+            //拆分获取年月日
+            String[] dateArray = dateStr.split("-");
+
+            List<String> monthList;
+            if (yearMap.containsKey(dateArray[0] + "年")) {
+                //存在,获取集合
+                monthList = yearMap.get(dateArray[0] + "年");
+            } else {
+                //不存在,创建
+                monthList = new ArrayList<>();
+            }
+            monthList.add(dateArray[1] + "-" + dateArray[2]);
+            yearMap.put(dateArray[0] + "年", monthList);
+        }
+
+        //循环年
+        for (Map.Entry<String, List<String>> yearMaps : yearMap.entrySet()) {
+            String year = yearMaps.getKey().replace("年", "");
+            List<String> monthList = yearMaps.getValue();
+
+            //月集合
+            List<TreeVoTwo> monthResult = new ArrayList<>();
+
+            //月
+            TreeVoTwo monthMap = new TreeVoTwo();
+
+            //循环月份
+            String month = monthList.get(0).split("-")[0];
+            int index = 0;
+            for (String monthDay : monthList) {
+                //拆分
+                String[] monthDays = monthDay.split("-");
+
+                if (!month.equals(monthDays[0])) {
+                    month = monthDays[0];
+                    monthResult.add(monthMap);
+                    monthMap = new TreeVoTwo();
+                }
+                monthMap.setName(month + "月");
+                monthMap.setHierarchy(year + month);
+                monthMap.setParentId(Long.valueOf(year));
+                monthMap.getChildren().add(new TreeVoTwo(monthDays[1] + "日", new ArrayList<>(), year + month + monthDays[1], Long.valueOf(year + month)));
+                index++;
+                if (index == monthList.size()) {
+                    monthResult.add(monthMap);
+                }
+            }
+            //年
+            TreeVoTwo yearResult = new TreeVoTwo(year, monthResult, year, 0L);
+            result.add(yearResult);
+        }
+
+        if ("DESC".equals(sort)) {
+            result.sort(Comparator.comparing(TreeVoTwo::getName).reversed());
+        } else {
+            result.sort(Comparator.comparing(TreeVoTwo::getName));
+        }
+
+        return result;
+    }
+
+}