Browse Source

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

yangyj 10 months ago
parent
commit
722408c49f
20 changed files with 652 additions and 166 deletions
  1. 8 4
      blade-service-api/blade-e-visa-api/src/main/java/org/springblade/evisa/feign/EVisaClient.java
  2. 6 4
      blade-service-api/blade-e-visa-api/src/main/java/org/springblade/evisa/feign/EVisaClientFallBack.java
  3. 26 0
      blade-service-api/blade-e-visa-api/src/main/java/org/springblade/evisa/vo/SigInfoVO.java
  4. 14 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveExpertConclusionController.java
  5. 9 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveExpertConclusionMapper.java
  6. 15 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveExpertConclusionMapper.xml
  7. 3 3
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.java
  8. 7 6
      blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml
  9. 2 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/IArchiveExpertConclusionService.java
  10. 77 17
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveExpertConclusionServiceImpl.java
  11. 34 101
      blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java
  12. 78 2
      blade-service/blade-archive/src/main/java/org/springblade/archive/utils/ItextPdfUtils.java
  13. 153 0
      blade-service/blade-archive/src/main/java/org/springblade/archive/utils/MyPdfPageHelper2.java
  14. 1 0
      blade-service/blade-business/src/main/java/org/springblade/business/controller/TaskController.java
  15. 7 4
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/feign/EVisaClientImpl.java
  16. 7 0
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/EVisaService.java
  17. 172 2
      blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVisaServiceImpl.java
  18. 1 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ArchiveTreeContractController.java
  19. 13 8
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractSyncImpl.java
  20. 19 14
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractInfoServiceImpl.java

+ 8 - 4
blade-service-api/blade-e-visa-api/src/main/java/org/springblade/evisa/feign/EVisaClient.java

@@ -1,10 +1,8 @@
 package org.springblade.evisa.feign;
 
 import org.springblade.common.constant.EVisaConstant;
-import org.springblade.evisa.vo.CertBeanVO;
-import org.springblade.evisa.vo.EVisaMakeSealVO;
-import org.springblade.evisa.vo.EVisaTaskApprovalVO;
-import org.springblade.evisa.vo.TaskArchiveDTO;
+import org.springblade.core.tool.api.R;
+import org.springblade.evisa.vo.*;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -56,4 +54,10 @@ public interface EVisaClient {
     @PostMapping(API_PREFIX + "/onlineCheckSeal")
     CertBeanVO onlineCheckSeal(@RequestParam String pdfUrl);
 
+    /**
+     * 单张PDF批量电签,返回电签后的PDF
+     */
+    @PostMapping(API_PREFIX + "/batchEVisa")
+    R<String> batchEVisa(@RequestBody SigInfoVO vo);
+
 }

+ 6 - 4
blade-service-api/blade-e-visa-api/src/main/java/org/springblade/evisa/feign/EVisaClientFallBack.java

@@ -3,11 +3,10 @@ package org.springblade.evisa.feign;
 import feign.hystrix.FallbackFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springblade.evisa.vo.CertBeanVO;
-import org.springblade.evisa.vo.EVisaMakeSealVO;
-import org.springblade.evisa.vo.EVisaTaskApprovalVO;
-import org.springblade.evisa.vo.TaskArchiveDTO;
+import org.springblade.core.tool.api.R;
+import org.springblade.evisa.vo.*;
 import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestBody;
 
 @Component
 public class EVisaClientFallBack implements FallbackFactory<EVisaClient> {
@@ -47,6 +46,9 @@ public class EVisaClientFallBack implements FallbackFactory<EVisaClient> {
             public CertBeanVO onlineCheckSeal(String pdfUrl) {
                 return null;
             }
+
+            @Override
+            public R<String> batchEVisa(SigInfoVO vo){return R.fail("feign调用失败");}
         };
     }
 }

+ 26 - 0
blade-service-api/blade-e-visa-api/src/main/java/org/springblade/evisa/vo/SigInfoVO.java

@@ -0,0 +1,26 @@
+package org.springblade.evisa.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @Param   批量电签传参
+ * @Author wangwl
+ * @Date 2024/1/18 17:21
+ **/
+@Data
+public class SigInfoVO {
+
+    private String pdfUrl;
+    private List<SigInfo> list;
+
+    @Data
+    public static class SigInfo{
+        private String userName;
+        private String userSig;
+        private String sigPic;
+        private String sealCode;
+        private String sealPassword;
+    }
+}

+ 14 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/controller/ArchiveExpertConclusionController.java

@@ -171,4 +171,18 @@ public class ArchiveExpertConclusionController extends BladeController {
         conclusionService.submitTable(conclusion);
         return R.data("结论生成成功");
     }
+
+    /**
+     * 编写结论-测回提交
+     */
+    @GetMapping("/repealTable")
+    @ApiOperationSupport(order = 12)
+    @ApiOperation(value = "编写结论-测回提交", notes = "传入结论id")
+    @ApiImplicitParams(value = {
+            @ApiImplicitParam(name = "id", value = "结论id", required = true)
+    })
+    public R repealTable(@RequestParam Long id) {
+        conclusionService.repealTable(id);
+        return R.data("测回成功");
+    }
 }

+ 9 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchiveExpertConclusionMapper.java

@@ -2,8 +2,12 @@ package org.springblade.archive.mapper;
 
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
 import org.springblade.archive.entity.ArchiveExpertConclusion;
 import org.springblade.archive.entity.ArchiveExpertScore;
+import org.springblade.manager.entity.SignPfxFile;
+
+import java.util.List;
 
 /**
  * <p>
@@ -15,4 +19,9 @@ import org.springblade.archive.entity.ArchiveExpertScore;
 public interface ArchiveExpertConclusionMapper extends BaseMapper<ArchiveExpertConclusion> {
 
 
+    List<SignPfxFile> getAllSign(@Param("ids") List<Long> userIds);
+
+    void updateAllArchiveByProject(@Param("projectId") Long projectId);
+
+    void updateAllNodeByProject(@Param("projectId") Long projectId);
 }

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

@@ -1,6 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.springblade.archive.mapper.ArchiveExpertConclusionMapper">
+    <update id="updateAllArchiveByProject">
+        update u_archives_auto set is_apply = 0,is_reviewed = 0,update_status=0,expert_id = null,is_inspect = 0
+        where project_id = #{projectId} and is_deleted = 0
+    </update>
+    <update id="updateAllNodeByProject">
+        update m_archive_tree_contract set expert_id = null,expert_name = null
+        where project_id = #{projectId} and is_deleted = 0
+    </update>
 
 
+    <select id="getAllSign" resultType="org.springblade.manager.entity.SignPfxFile">
+        select *
+        from m_sign_pfx_file where is_deleted = 0 and certificate_user_id in
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
 </mapper>

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

@@ -176,16 +176,16 @@ public interface ArchivesAutoMapper extends BaseMapper<ArchivesAuto> {
 	List<ArchivesAutoVO2> getUnitAllArchive2(@Param("firstNode") Long firstNode,@Param("grade") Integer grade,@Param("isApply") Integer isApply);
 
 	//查询所有的,树不加id,档案状态加
-	IPage<ArchiveInspectVO> getNodeArchives(IPage<ArchiveInspectVO> page,@Param("id") Long nodeId,@Param("projectId") Long projectId,@Param("searchType") Integer searchType,@Param("searchValue") String searchValue,@Param("userId") Long userId);
+	IPage<ArchiveInspectVO> getNodeArchives(IPage<ArchiveInspectVO> page,@Param("id") Long nodeId,@Param("projectId") Long projectId,@Param("searchType") Integer searchType,@Param("searchValue") String searchValue,@Param("userId") Long userId,@Param("conclusionId") Long conclusionId);
 
 	//查询专家的,树加id,档案状态加
-	IPage<ArchiveInspectVO> getNodeArchives2(IPage<ArchiveInspectVO> page,@Param("id") Long nodeId,@Param("projectId") Long projectId,@Param("searchType") Integer searchType,@Param("searchValue") String searchValue,@Param("userId") Long userId);
+	IPage<ArchiveInspectVO> getNodeArchives2(IPage<ArchiveInspectVO> page,@Param("id") Long nodeId,@Param("projectId") Long projectId,@Param("searchType") Integer searchType,@Param("searchValue") String searchValue,@Param("userId") Long userId,@Param("conclusionId") Long conclusionId);
 
     List<ArchivesAuto> getNodeAllArchive(@Param("ids") List<Long> ids);
 
     Integer getUserArchiveTotal(@Param("projectId") Long projectId,@Param("userId") Long userId);
 
-	Integer getUserReviewedTotal(@Param("projectId") Long projectId,@Param("userId") Long userId);
+	Integer getUserReviewedTotal(@Param("projectId") Long projectId,@Param("userId") Long userId,@Param("conclusionId") Long conclusionId);
 
 	IPage<ExpertInspectionVO> getUserInspectInfo(IPage<ExpertInspectionVO> page,@Param("projectId") Long projectId,@Param("userId") Long userId,@Param("conclusionId") Long conclusionId);
 

+ 7 - 6
blade-service/blade-archive/src/main/java/org/springblade/archive/mapper/ArchivesAutoMapper.xml

@@ -1045,11 +1045,11 @@
     </select>
     <select id="getNodeArchives" resultType="org.springblade.archive.vo.ArchiveInspectVO">
         select uaa.id ,uaa.file_number,uaa.name ,uaa.unit,
-               if((select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id
+               if((select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id and aei.conclusion_id = #{conclusionId}
                    and aei.expert_id = #{userId}) > 0,'已抽检','未抽检') as inspectStatusName,
-               (CASE when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id
+               (CASE when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id and aei.conclusion_id = #{conclusionId}
                     and aei.expert_id = #{userId} and is_pass = 1) = 1 then '合格'
-                   when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id
+                   when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id and aei.conclusion_id = #{conclusionId}
                     and aei.expert_id = #{userId} and is_pass = 0) > 0 then '整改' else null end) as updateStatusName
         from m_archive_tree_contract atc right join u_archives_auto uaa on atc.id = uaa.node_id
         WHERE uaa.is_apply = 1 and uaa.project_id = #{projectId} and atc.project_id = #{projectId} and atc.is_deleted = 0 and uaa.is_deleted =0
@@ -1065,11 +1065,11 @@
     </select>
     <select id="getNodeArchives2" resultType="org.springblade.archive.vo.ArchiveInspectVO">
         select uaa.id ,uaa.file_number,uaa.name ,uaa.unit,
-        if((select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id
+        if((select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id and aei.conclusion_id = #{conclusionId}
             and aei.expert_id = #{userId}) > 0,'已抽检','未抽检') as inspectStatusName,
-        (CASE when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id
+        (CASE when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id and aei.conclusion_id = #{conclusionId}
             and aei.expert_id = #{userId} and is_pass = 1) = 1 then '合格'
-            when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id
+            when (select count(1) from u_archive_expert_inspection aei where aei.is_deleted =0 and aei.archive_id = uaa.id and aei.conclusion_id = #{conclusionId}
             and aei.expert_id = #{userId} and is_pass = 0) > 0 then '整改' else null end) as updateStatusName
         from m_archive_tree_contract atc right join u_archives_auto uaa on atc.id = uaa.node_id
         WHERE uaa.is_apply = 1 and uaa.project_id = #{projectId} and atc.project_id = #{projectId} and atc.is_deleted = 0 and uaa.is_deleted =0
@@ -1104,6 +1104,7 @@
               WHERE project_id = #{projectId}
                 and is_deleted = 0
                 and expert_id = #{userId}
+                and conclusion_id = #{conclusionId}
              ) a
     </select>
     <select id="getUserInspectInfo" resultType="org.springblade.archive.vo.ExpertInspectionVO">

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

@@ -33,4 +33,6 @@ public interface IArchiveExpertConclusionService extends BaseService<ArchiveExpe
     Boolean checkSubmit(Long projectId);
 
     Long getCurrentId(Long projectId);
+
+    void repealTable(Long id);
 }

+ 77 - 17
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchiveExpertConclusionServiceImpl.java

@@ -27,7 +27,10 @@ import org.springblade.core.oss.model.BladeFile;
 import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.Func;
+import org.springblade.evisa.feign.EVisaClient;
+import org.springblade.evisa.vo.SigInfoVO;
 import org.springblade.manager.entity.ProjectInfo;
+import org.springblade.manager.entity.SignPfxFile;
 import org.springblade.manager.feign.ProjectClient;
 import org.springblade.resource.feign.NewIOSSClient;
 import org.springblade.system.user.entity.User;
@@ -37,6 +40,8 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.io.FileOutputStream;
 import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -53,6 +58,8 @@ public class ArchiveExpertConclusionServiceImpl extends BaseServiceImpl<ArchiveE
     private final IUserClient userClient;
 
     private final NewIOSSClient newIOSSClient;
+
+    private final EVisaClient eVisaClient;
     //表格高度
     private static int high = 20;
     //表格宽度
@@ -78,6 +85,7 @@ public class ArchiveExpertConclusionServiceImpl extends BaseServiceImpl<ArchiveE
         conclusion.setIsBuildScore(0);
         conclusion.setStatus(1);
         conclusion.setTaskId(taskId);
+        conclusion.setExpertIds(expertIds);
         ProjectInfo info = projectClient.getById(projectId + "");
         if (info == null){
             return R.fail("获取项目信息失败");
@@ -219,41 +227,79 @@ public class ArchiveExpertConclusionServiceImpl extends BaseServiceImpl<ArchiveE
     @Override
     @Transactional
     public void submitTable(ArchiveExpertConclusion conclusion) {
+        //根据专家信息获取到具体的user
+        String expertIds = conclusion.getExpertIds();
+        if (StringUtils.isBlank(expertIds)){
+            throw new ServiceException("获取专家账号失败");
+        }
+        List<Long> userIds = Func.toLongList(expertIds);
+        List<User> users = userClient.userInfoByIds(userIds);
+        if (users.size() == 0 || users.size() != userIds.size()){
+            throw new ServiceException("专家账号信息错误,请联系管理员");
+        }
+        //获取所有专家个人证书
+        List<SignPfxFile> list = baseMapper.getAllSign(userIds);
+        if (list.size() == 0 || list.size() != userIds.size()){
+            throw new ServiceException("有专家没有配置电签,请联系管理员");
+        }
+        Map<Long, SignPfxFile> map = list.stream().collect(Collectors.toMap(l -> l.getCertificateUserId(), l -> l));
+
         String pdfUrl = "";
         try {
             //生成结论pdf
-            pdfUrl = buildPdf(conclusion);
+            pdfUrl = buildPdf(conclusion,users);
             if (StringUtils.isBlank(pdfUrl)){
                 throw new ServiceException("");
             }
         }catch (Exception e){
-            throw new ServiceException("生成PDF失败");
+            throw new ServiceException("生成PDF失败:"+e.getMessage());
+        }
+        //构建专家电签信息
+        SigInfoVO vo = new SigInfoVO();
+        List<SigInfoVO.SigInfo> infos = new ArrayList<>();
+        for (User user : users) {
+            SignPfxFile file = map.get(user.getId());
+            if (file == null){
+                throw new ServiceException("未获取到专家:"+user.getName()+"的电签信息");
+            }
+            SigInfoVO.SigInfo info = new SigInfoVO.SigInfo();
+            info.setUserName(user.getName());
+            info.setUserSig(user.getName()+user.getPhone().substring(0,3));
+            info.setSealCode("S_NEW_"+file.getCertificatePassword());
+            info.setSealPassword(file.getCertificatePassword());
+            if (StringUtils.isBlank(file.getSignatureFileUrl())){
+                throw new ServiceException("未获取到专家:"+user.getName()+"的签字图片");
+            }
+            info.setSigPic(file.getSignatureFileUrl());
+            infos.add(info);
         }
+        vo.setPdfUrl(pdfUrl);
+        vo.setList(infos);
         //PDF电签
-        try {
-//            pdfUrl = ;
-        }catch (Exception e){
-            throw new ServiceException("PDF电签失败");
+        R<String> visa = eVisaClient.batchEVisa(vo);
+        if (visa == null){
+            throw new ServiceException("调用电签服务失败");
+        }
+        if (visa.getCode() == 400){
+            throw new ServiceException(visa.getMsg());
         }
         //修改状态,插入结论PDF
-        conclusion.setTableUrl(pdfUrl);
+        conclusion.setTableUrl(visa.getData());
         conclusion.setStatus(2);
+        conclusion.setApproveDate(LocalDate.now());
+        //还原当前项目所有档案的:申请验收状态,查阅状态,抽检意见,专家id,是否抽检
+        baseMapper.updateAllArchiveByProject(conclusion.getProjectId());
+        //还原当前项目所有分配的树节点
+        baseMapper.updateAllNodeByProject(conclusion.getProjectId());
         this.updateById(conclusion);
     }
 
-    private String buildPdf(ArchiveExpertConclusion conclusion) throws Exception {
+    private String buildPdf(ArchiveExpertConclusion conclusion,List<User> users) throws Exception {
         //获取评分基础数据
         List<ArchiveExpertScore> list1 = scoreService.list(new LambdaQueryWrapper<ArchiveExpertScore>()
                 .eq(ArchiveExpertScore::getProjectId, conclusion.getProjectId())
                 .eq(ArchiveExpertScore::getConclusionId, conclusion.getId()));
         Map<Integer, List<ArchiveExpertScore>> map = list1.stream().collect(Collectors.groupingBy(ArchiveExpertScore::getUnitType));
-        //根据专家信息获取到具体的user
-        String expertIds = conclusion.getExpertIds();
-        if (StringUtils.isBlank(expertIds)){
-            throw new ServiceException("获取专家账号失败");
-        }
-        List<Long> userIds = Func.toLongList(expertIds);
-        List<User> users = userClient.userInfoByIds(userIds);
         //获取专家组长
         User us = null;
         for (User user : users) {
@@ -267,7 +313,8 @@ public class ArchiveExpertConclusionServiceImpl extends BaseServiceImpl<ArchiveE
         //新建一个pdf文档对象,前一个参数是纸张大小,后四个为边距
         Document document = new Document(PageSize.A4, 10, 10, 80, 85);
         //文件地址
-        String localFile = "C:\\Users\\泓创研发01\\Desktop\\"+conclusion.getId()+".pdf";
+//        String localFile = "C:\\Users\\泓创研发01\\Desktop\\"+conclusion.getId()+".pdf";
+        String localFile = "/www/wwwroot/Users/hongchuangyanfa/Desktop/archiveCheck/"+conclusion.getId()+".pdf";
         //建立一个书写器
         PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(localFile));
         //页尾
@@ -275,7 +322,8 @@ public class ArchiveExpertConclusionServiceImpl extends BaseServiceImpl<ArchiveE
         writer.setPageEvent(helper);
         document.open();
         //创建字体
-        BaseFont baseFont2 =BaseFont.createFont("C:/WINDOWS/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
+//        BaseFont baseFont2 =BaseFont.createFont("C:/WINDOWS/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
+        BaseFont baseFont2 =BaseFont.createFont("/usr/share/fonts/chinese/simsun.ttc,0", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
         //字体对象,这里可以创建一个方法
         Font sigFont = new Font(baseFont2, 0.1F, Font.NORMAL); //大小为0.1的正常字体
         Font size13font = new Font(baseFont2, 13, Font.NORMAL); //大小为13的正常字体
@@ -468,5 +516,17 @@ public class ArchiveExpertConclusionServiceImpl extends BaseServiceImpl<ArchiveE
         }
         return one.getId();
     }
+
+    /**
+     * 编写结论-测回提交
+     */
+    @Override
+    public void repealTable(Long id) {
+       this.update(new LambdaUpdateWrapper<ArchiveExpertConclusion>()
+            .eq(ArchiveExpertConclusion::getId,id)
+            .set(ArchiveExpertConclusion::getApproveDate,null)
+            .set(ArchiveExpertConclusion::getStatus,1)
+            .set(ArchiveExpertConclusion::getTableUrl,null));
+    }
 }
 

+ 34 - 101
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -41,9 +41,7 @@ import org.springblade.archive.dto.SaveApplyDTO;
 import org.springblade.archive.entity.*;
 import org.springblade.archive.mapper.ArchiveConclusionMapper;
 import org.springblade.archive.service.*;
-import org.springblade.archive.utils.ArchiveTreeUtil;
-import org.springblade.archive.utils.FileTransJavaDemo;
-import org.springblade.archive.utils.FileUtils;
+import org.springblade.archive.utils.*;
 import org.springblade.archive.vo.*;
 import org.springblade.archive.mapper.ArchivesAutoMapper;
 import org.springblade.business.entity.ArchiveFile;
@@ -2941,48 +2939,42 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 
 	//生成验收申请PDF
 	private String createAppPdf(String projectName, List<ArchivesAutoVO2> vo2s) throws Exception {
-		String fileUrl = "C:\\Users\\泓创研发01\\Desktop\\appPDF.pdf";
+		String fileUrl = "/www/wwwroot/Users/hongchuangyanfa/Desktop/archiveCheck/appPDF.pdf";
 		//新建一个pdf文档对象,前一个参数是纸张大小,后四个为边距
 		Document document = new Document(PageSize.A4, 5, 5, 30, 30);
+		//创建字体
+//		BaseFont baseFont =BaseFont.createFont("C:/WINDOWS/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
+		BaseFont baseFont =BaseFont.createFont("/usr/share/fonts/chinese/simsun.ttc,0", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
+		//字体对象,这里可以创建一个方法
+		Font size10font = new Font(baseFont, 13, Font.NORMAL); //大小为10的正常字体
+		Font size17font = new Font(baseFont, 17, Font.BOLD);  //大小为17的正常字体
 		//建立一个书写器
 		PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(fileUrl));
+		MyPdfPageHelper2 helper = new MyPdfPageHelper2(baseFont,size10font,size17font,projectName);
+		writer.setPageEvent(helper);
 		document.open();
-		//创建字体
-		BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
-		//字体对象,这里可以创建一个方法
-		Font size10font = new Font(baseFont, 10, Font.NORMAL); //大小为10的正常字体
-		Font size17font = new Font(baseFont, 17, Font.NORMAL);  //大小为17的正常字体
 		//该数组是每个表格的宽度
 		float[] floats = new float[6];
-		floats[0] = 0.1f;
-		floats[1] = 0.1f;
-		floats[2] = 0.5f;
-		floats[3] = 0.1f;
-		floats[4] = 0.1f;
-		floats[5] = 0.1f;
-		tableHeader(projectName,document,size10font,size17font,floats);
+		floats[0] = 0.09f;
+		floats[1] = 0.14f;
+		floats[2] = 0.48f;
+		floats[3] = 0.09f;
+		floats[4] = 0.11f;
+		floats[5] = 0.09f;
 
 		//将数据放入表格中
-		int total = 0;
 		for (int i = 0; i < vo2s.size(); i++) {
 			ArchivesAutoVO2 vo2 = vo2s.get(i);
 			PdfPTable dataTable = new PdfPTable(6);
 			dataTable.setWidthPercentage(widthPercentage);
 			dataTable.setTotalWidth(floats);
-			dataTable.addCell(pdfTableStyle(i+1+"", size10font, high, true, true));
-			dataTable.addCell(pdfTableStyle(vo2.getFileNumber(), size10font, high, true, true));
-			dataTable.addCell(pdfTableStyle("		"+vo2.getName(), size10font, high, false, false));
-			dataTable.addCell(pdfTableStyle(vo2.getPageN()+"", size10font, high, true, true));
-			dataTable.addCell(pdfTableStyle(vo2.getStorageTimeValue(), size10font, high, true, true));
-			dataTable.addCell(pdfTableStyle(vo2.getRemark(), size10font, high, true, true));
-			total += (int)Math.ceil(new Double(vo2.getName().length()+4) / 50);
-			//如果出现不相同,则合并之前的
+			dataTable.addCell(ItextPdfUtils.pdfTableStyle11(i+1+"", size10font, 30, true, true));
+			dataTable.addCell(ItextPdfUtils.pdfTableStyle11(vo2.getFileNumber(), size10font, 30, true, true));
+			dataTable.addCell(ItextPdfUtils.pdfTableStyle12(vo2.getName(), size10font, 30, false, true));
+			dataTable.addCell(ItextPdfUtils.pdfTableStyle11(vo2.getPageN()+"", size10font, 30, true, true));
+			dataTable.addCell(ItextPdfUtils.pdfTableStyle11(vo2.getStorageTimeValue(), size10font, 30, true, true));
+			dataTable.addCell(ItextPdfUtils.pdfTableStyle11(vo2.getRemark(), size10font, 30, true, true));
 			document.add(dataTable);
-			if (total >= 40 && i !=  (vo2s.size()-1)) {
-				total = 0;
-				document.newPage();
-				tableHeader(projectName,document,size10font,size17font,floats);
-			}
 		}
 		document.close();
 		writer.close();
@@ -2993,46 +2985,6 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		return bladeFile.getLink();
 	}
 
-	/**
-	 * 	表头
-	 * @param document	整个文档
-	 * @param size10font
-	 * @param size17font
-	 * @param floats	表头格式
-	 * @throws DocumentException
-	 */
-	void tableHeader(String projectName,Document document,Font size10font,Font size17font,float[] floats) throws DocumentException {
-		//添加标题
-		//创建第一行表格
-		PdfPTable tableName = new PdfPTable(1);
-		tableName.setWidthPercentage(widthPercentage);  //设置标题长度占纸张比例
-		PdfPCell cell = pdfTableStyle("单位科学技术档案归档目录", size17font, 50, true, true);
-		cell.disableBorderSide(15);
-		//给表格赋值
-		tableName.addCell(cell);
-		//将表格添加到文档对象中
-		document.add(tableName);
-		//项目名称
-		PdfPTable two = new PdfPTable(1);
-		two.setWidthPercentage(widthPercentage);
-		PdfPCell cell2 = pdfTableStyle("项目名称:"+projectName, size10font, 20, false, false);
-		cell2.disableBorderSide(15);
-		two.addCell(cell2);
-		document.add(two);
-		//表头文字
-		String[] name = new String[]{"序号", "档号", "案卷提名", "总页数","保管期限","备注"};
-		//创建第二行,并设置第二行中的表格数
-		PdfPTable twoTable = new PdfPTable(name.length);
-		twoTable.setWidthPercentage(widthPercentage);
-		//循环将表头数据添加到第二行表格中
-		for (int i = 0; i < name.length; i++) {
-			PdfPCell pdfPCell = pdfTableStyle(name[i], size10font, high, true, true);
-			twoTable.addCell(pdfPCell);
-		}
-		//设置表格的宽度
-		twoTable.setTotalWidth(floats);
-		document.add(twoTable);
-	}
 
 	@Override
 	public R<Boolean> batchUpdateIsApply(Integer isApply, List<Long> ids) {
@@ -3094,12 +3046,16 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	@Override
 	public IPage<ArchiveInspectVO> getNodeArchives(Query query,Long nodeId,Long projectId,Integer searchType,String searchValue,Integer type) {
 		IPage<ArchiveInspectVO> page = new Page<>(query.getCurrent(),query.getSize());
+		ArchiveExpertConclusion one = expertConclusionService.getOne(new LambdaUpdateWrapper<ArchiveExpertConclusion>()
+				.eq(ArchiveExpertConclusion::getProjectId, projectId)
+				.orderByDesc(ArchiveExpertConclusion::getCreateTime)
+				.last("limit 1"));
 		if (type == 1){
 			//查询所有的,树不加id,档案状态加
-			return baseMapper.getNodeArchives(page,nodeId,projectId,searchType,searchValue,AuthUtil.getUserId());
+			return baseMapper.getNodeArchives(page,nodeId,projectId,searchType,searchValue,AuthUtil.getUserId(),one.getId());
 		}else {
 			//查询专家的,树加id,档案状态加
-			return baseMapper.getNodeArchives2(page,nodeId,projectId,searchType,searchValue,AuthUtil.getUserId());
+			return baseMapper.getNodeArchives2(page,nodeId,projectId,searchType,searchValue,AuthUtil.getUserId(),one.getId());
 		}
 	}
 
@@ -3117,7 +3073,12 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		}
 		map.put("total",t1+"");
 		//获取用户归属档案所有已阅总数
-		Integer t2 = baseMapper.getUserReviewedTotal(projectId, userId);
+		//获取最新一期抽检
+		ArchiveExpertConclusion one = expertConclusionService.getOne(new LambdaUpdateWrapper<ArchiveExpertConclusion>()
+				.eq(ArchiveExpertConclusion::getProjectId, projectId)
+				.orderByDesc(ArchiveExpertConclusion::getCreateTime)
+				.last("limit 1"));
+		Integer t2 = baseMapper.getUserReviewedTotal(projectId, userId,one.getId());
 		if (t2 == null){
 			t2 = 0;
 		}
@@ -3602,32 +3563,4 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 		process.waitFor();
 	}
 
-	/**
-	 * 表格的样式
-	 *
-	 * @param content 内容
-	 * @param font    字体对象
-	 * @param high    表格高度
-	 * @return
-	 * @Param isAlignCenter 是否水平居中
-	 * @Param isAlignMidde  是否垂直居中
-	 */
-	static PdfPCell pdfTableStyle(String content, Font font, int high, boolean isAlignCenter, boolean isAlignMiddle) {
-		Paragraph phrase = new Paragraph(content, font);
-//		phrase.setAlignment(Paragraph.ALIGN_LEFT);
-		PdfPCell pdfPCell = new PdfPCell(phrase);
-		pdfPCell.setMinimumHeight(high);
-		pdfPCell.setUseAscender(true); // 设置可以居中
-		if (isAlignCenter) {
-			pdfPCell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); // 设置水平居中
-		}else {
-			pdfPCell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT); // 设置首行
-		}
-		if (isAlignMiddle) {
-			pdfPCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); // 设置垂直居中
-		}else {
-			pdfPCell.setVerticalAlignment(PdfPCell.ALIGN_LEFT); // 设置垂直居中
-		}
-		return pdfPCell;
-	}
 }

+ 78 - 2
blade-service/blade-archive/src/main/java/org/springblade/archive/utils/ItextPdfUtils.java

@@ -199,8 +199,8 @@ public class ItextPdfUtils {
     public static void rowHeader2(Document document,String context,Font font,String sigCode,Font sigFont) throws DocumentException {
         //做左右分隔
         float[] floats = new float[3];
-        floats[0] = 0.15f;
-        floats[1] = 0.6f;
+        floats[0] = 0.22f;
+        floats[1] = 0.58f;
         floats[2] = 0.2f;
         //创建小标题
         PdfPTable tableName = new PdfPTable(3);
@@ -227,4 +227,80 @@ public class ItextPdfUtils {
         document.add(tableName);
     }
 
+    //档案申请验收表头
+    static void tableHeader2(Document document,Font size10font,Font size17font,float[] floats,String projectName) throws DocumentException {
+        //添加标题
+        //创建第一行表格
+        PdfPTable tableName = new PdfPTable(1);
+        tableName.setWidthPercentage(100);  //设置标题长度占纸张比例
+        PdfPCell cell = pdfTableStyle("单位科学技术档案归档目录", size17font, 50);
+        cell.disableBorderSide(15);
+        //给表格赋值
+        tableName.addCell(cell);
+        //将表格添加到文档对象中
+        document.add(tableName);
+        //项目名称
+        PdfPTable two = new PdfPTable(1);
+        two.setWidthPercentage(100);
+        PdfPCell cell2 = pdfTableStyle3("项目名称:"+projectName, size10font, false,true);
+        cell2.disableBorderSide(15);
+        two.addCell(cell2);
+        document.add(two);
+        ItextPdfUtils.blankRow(document,size10font,5);
+        //表头文字
+        String[] name = new String[]{"序号", "档号", "案卷提名", "总页数","保管期限","备注"};
+        //创建第二行,并设置第二行中的表格数
+        PdfPTable twoTable = new PdfPTable(name.length);
+        twoTable.setWidthPercentage(100);
+        //循环将表头数据添加到第二行表格中
+        for (int i = 0; i < name.length; i++) {
+            PdfPCell pdfPCell = pdfTableStyle7(name[i], size10font, 20);
+            twoTable.addCell(pdfPCell);
+        }
+        //设置表格的宽度
+        twoTable.setTotalWidth(floats);
+        document.add(twoTable);
+    }
+
+    //档案申请验收其他字段
+    public static PdfPCell pdfTableStyle11(String content, Font font, int high, boolean isAlignCenter, boolean isAlignMidle) {
+        Paragraph phrase = new Paragraph(content, font);
+        phrase.setAlignment(Paragraph.ALIGN_LEFT);
+        PdfPCell pdfPCell = new PdfPCell(phrase);
+        pdfPCell.setMinimumHeight(high);
+        pdfPCell.setUseAscender(true); // 设置可以居中
+        if (isAlignCenter) {
+            pdfPCell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); // 设置水平居中
+        }else {
+            pdfPCell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT); // 设置水平居中
+        }
+        if (isAlignMidle) {
+            pdfPCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); // 设置垂直居中
+        }else {
+            pdfPCell.setVerticalAlignment(PdfPCell.ALIGN_LEFT); // 设置垂直居中
+        }
+
+        return pdfPCell;
+    }
+    //档案申请验收案卷提名
+    public static PdfPCell pdfTableStyle12(String content, Font font, int high, boolean isAlignCenter, boolean isAlignMidle) {
+        Paragraph phrase = new Paragraph("    "+content, font);
+        phrase.setAlignment(Paragraph.ALIGN_LEFT);
+        PdfPCell pdfPCell = new PdfPCell(phrase);
+        pdfPCell.setMinimumHeight(high);
+        pdfPCell.setUseAscender(true); // 设置可以居中
+        if (isAlignCenter) {
+            pdfPCell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); // 设置水平居中
+        }else {
+            pdfPCell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT); // 设置水平居中
+        }
+        if (isAlignMidle) {
+            pdfPCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); // 设置垂直居中
+        }else {
+            pdfPCell.setVerticalAlignment(PdfPCell.ALIGN_LEFT); // 设置垂直居中
+        }
+
+        return pdfPCell;
+    }
+
 }

+ 153 - 0
blade-service/blade-archive/src/main/java/org/springblade/archive/utils/MyPdfPageHelper2.java

@@ -0,0 +1,153 @@
+package org.springblade.archive.utils;
+
+import com.itextpdf.text.*;
+import com.itextpdf.text.pdf.*;
+
+import java.io.IOException;
+
+/**
+ * @Param   itext生成页脚,标题,档案验收申请PDF使用
+ * @Author wangwl
+ * @Date 2024/1/17 16:31
+ **/
+public class MyPdfPageHelper2 extends PdfPageEventHelper {
+    /**
+     * 页眉
+     */
+    public String header = "";
+    /**
+     * 文档字体大小,页脚页眉最好和文本大小一致
+     */
+    public int presentFontSize = 12;
+    /**
+     * 文档页面大小,最好前面传入,否则默认为A4纸张
+     */
+    public Rectangle pageSize = PageSize.A4;
+    // 模板
+    public PdfTemplate total = null;
+    // 基础字体对象
+    public BaseFont bf = null;
+    // 利用基础字体生成的字体对象,一般用于生成中文文字
+    public Font fontDetail = null;
+    Font size10font = null;
+    Font size17font = null;
+    String projectName = "";
+
+    public MyPdfPageHelper2() {
+    }
+
+    public MyPdfPageHelper2(BaseFont bf, Font size10font, Font size17font, String projectName) {
+        this.bf = bf;
+        this.size10font = size10font;
+        this.size17font = size17font;
+        this.projectName = projectName;
+    }
+
+    /**
+     * @param yeMei
+     *            页眉字符串
+     * @param presentFontSize
+     *            数据体字体大小
+     * @param pageSize
+     *            页面文档大小,A4,A5,A6横转翻转等Rectangle对象
+     */
+    public MyPdfPageHelper2(String yeMei, int presentFontSize, Rectangle pageSize) {
+        this.header = yeMei;
+        this.presentFontSize = presentFontSize;
+        this.pageSize = pageSize;
+    }
+
+    public void setHeader(String header) {
+        this.header = header;
+    }
+
+    public void setPresentFontSize(int presentFontSize) {
+        this.presentFontSize = presentFontSize;
+    }
+
+    /**
+     * TODO 文档打开时创建模板
+     */
+    @Override
+    public void onOpenDocument(PdfWriter writer, Document document) {
+        total = writer.getDirectContent().createTemplate(50, 50);// 共页的矩形的长宽高
+    }
+
+    @Override
+    public void onStartPage(PdfWriter writer, Document document) {
+        float[] floats = new float[6];
+        floats[0] = 0.09f;
+        floats[1] = 0.14f;
+        floats[2] = 0.48f;
+        floats[3] = 0.09f;
+        floats[4] = 0.11f;
+        floats[5] = 0.09f;
+        try {
+            ItextPdfUtils.tableHeader2(document, size10font, size17font, floats,projectName);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * TODO 关闭每页的时候,写入页眉,写入'第几页共'这几个字。
+     */
+    @Override
+    public void onEndPage(PdfWriter writer, Document document) {
+        try {
+            if (bf == null) {
+                bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
+            }
+            if (fontDetail == null) {
+                fontDetail = new Font(bf, presentFontSize, Font.NORMAL);// 数据体字体
+            }
+        } catch (DocumentException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        // 拿到当前的PdfContentByte
+        PdfContentByte cb = writer.getDirectContent();
+
+        // 1.写入页眉     参数:要写入文本的页面对象,对齐,文字内容,X轴位置,Y轴位置,逆时针旋转的角度
+        ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, new Phrase(header, fontDetail), document.left(), document.top() + 20, 45);
+
+        // 2.写入页脚    前半部分的 第 X页/共
+        int pageS = writer.getPageNumber();
+        String foot1 = "第 " + pageS + " 页";
+        Phrase footer = new Phrase(foot1, fontDetail);
+
+        // 计算前半部分的foot1的长度,后面好定位最后一部分的'Y页'这俩字的x轴坐标,字体长度也要计算进去 = len
+        float len = bf.getWidthPoint(foot1, presentFontSize);
+
+        /**
+         *  写入页脚   前半部分的    第 X页/共
+         * x轴就是(右margin+左margin + right() -left()- len)/2.0F 再给偏移20F适合人类视觉感受,否则肉眼看上去就太偏左了
+         * y轴就是底边界-20,否则就贴边重叠到数据体里了就不是页脚了;注意Y轴是从下往上累加的,最上方的Top值是大于Bottom好几百开外的。
+         */
+        ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer, (document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F + 20F, document.bottom() - 20, 0);
+
+        /**
+         *  写入页脚  后半部分   ?页
+         *  注:因为共?页   中的?值是在doc.close() 后才能得到,所以这一先加入模板,最后结束的时候后边用值替换
+         * x=(右margin+左margin + right() -left())/2.0F
+         * y 轴和之前的保持一致,底边界-20
+         */
+//        cb.addTemplate(total, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F + 20F, document.bottom() - 20); // 调节模版显示的位置
+    }
+
+    /**
+     * TODO 关闭文档时,替换模板,完成整个页眉页脚组件
+     */
+    @Override
+    public void onCloseDocument(PdfWriter writer, Document document) {
+        // 7.最后一步了,就是关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
+        total.beginText();
+        total.setFontAndSize(bf, presentFontSize);// 生成的模版的字体、颜色
+        String foot2 = " " + writer.getPageNumber() + " 页";
+        total.showText(foot2);// 模版显示的内容
+        total.endText();
+        total.closePath();
+    }
+}

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

@@ -681,6 +681,7 @@ public class TaskController extends BladeController {
                                     dto.setUserType("3");
                                     dto.setRealName(name);
                                     dto.setPhone(phone);
+                                    dto.setIdNumber(expertInfo.getIdNumber());
                                     //设置专家部门
                                     dto.setDeptId(AuthUtil.getDeptId());
                                     dto.setStatus(1);

+ 7 - 4
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/feign/EVisaClientImpl.java

@@ -4,11 +4,9 @@ import cfca.paperless.dto.bean.CertBean;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import lombok.AllArgsConstructor;
+import org.springblade.core.tool.api.R;
 import org.springblade.evisa.service.EVisaService;
-import org.springblade.evisa.vo.CertBeanVO;
-import org.springblade.evisa.vo.EVisaMakeSealVO;
-import org.springblade.evisa.vo.EVisaTaskApprovalVO;
-import org.springblade.evisa.vo.TaskArchiveDTO;
+import org.springblade.evisa.vo.*;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
@@ -32,6 +30,11 @@ public class EVisaClientImpl implements EVisaClient {
         return null;
     }
 
+    @Override
+    public R<String> batchEVisa(SigInfoVO vo) {
+        return eVisaService.batchEVisa(vo);
+    }
+
     @Override
     public String certification(String pdfUrl, String fileName, String contractId) {
         return this.eVisaService.certification(pdfUrl, fileName, contractId);

+ 7 - 0
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/EVisaService.java

@@ -1,8 +1,10 @@
 package org.springblade.evisa.service;
 
 import cfca.paperless.dto.bean.CertBean;
+import org.springblade.core.tool.api.R;
 import org.springblade.evisa.vo.EVisaMakeSealVO;
 import org.springblade.evisa.vo.EVisaTaskApprovalVO;
+import org.springblade.evisa.vo.SigInfoVO;
 
 import java.util.List;
 
@@ -38,4 +40,9 @@ public interface EVisaService {
      */
     List<CertBean> onlineCheckSeal(String pdfUrl);
 
+    /**
+     * 单张PDF批量电签,返回电签后的PDF
+     */
+    R<String> batchEVisa(SigInfoVO vo);
+
 }

+ 172 - 2
blade-service/blade-e-visa/src/main/java/org/springblade/evisa/service/impl/EVisaServiceImpl.java

@@ -8,6 +8,7 @@ import cfca.paperless.base.BaseConstants;
 import cfca.paperless.base.util.Base64;
 import cfca.paperless.base.util.GUIDUtil;
 import cfca.paperless.base.util.ImageUtil;
+import cfca.paperless.base.util.IoUtil;
 import cfca.paperless.base.util.PwdEncryptUtil;
 import cfca.paperless.client.PaperlessClient;
 import cfca.paperless.dto.RequestHead;
@@ -16,15 +17,19 @@ import cfca.paperless.dto.ResponseHead;
 import cfca.paperless.dto.bean.*;
 import cfca.paperless.dto.request.requestbody.tx20.MakeSealRequestBody;
 import cfca.paperless.dto.request.requestbody.tx40.CompoundSealPdfListRequestBody;
+import cfca.paperless.dto.request.requestbody.tx40.SealPdfRequestBody;
 import cfca.paperless.dto.request.requestbody.tx40.VerifyPdfSealRequestBody;
 import cfca.paperless.dto.request.tx20.MakeSealRequest;
 import cfca.paperless.dto.request.tx40.CompoundSealPdfListDetachedRequest;
+import cfca.paperless.dto.request.tx40.SealPdfRequest;
 import cfca.paperless.dto.request.tx40.VerifyPdfSealRequest;
 import cfca.paperless.dto.response.responsebody.tx20.MakeSealResponseBody;
 import cfca.paperless.dto.response.responsebody.tx40.CompoundSealPdfListDetachedResponseBody;
+import cfca.paperless.dto.response.responsebody.tx40.SealPdfResponseBody;
 import cfca.paperless.dto.response.responsebody.tx40.VerifyPdfSealResponseBody;
 import cfca.paperless.dto.response.tx20.MakeSealResponse;
 import cfca.paperless.dto.response.tx40.CompoundSealPdfListDetachedResponse;
+import cfca.paperless.dto.response.tx40.SealPdfResponse;
 import cfca.paperless.dto.response.tx40.VerifyPdfSealResponse;
 import cn.hutool.core.io.file.FileReader;
 import com.alibaba.fastjson.JSONObject;
@@ -42,9 +47,11 @@ import org.springblade.common.constant.CommonConstant;
 import org.springblade.common.constant.EVisaConstant;
 import org.springblade.common.utils.CommonUtil;
 import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.oss.model.BladeFile;
 import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.secure.utils.SecureUtil;
+import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.*;
 import org.springblade.evisa.redissionUtil.DistributedRedisLock;
 import org.springblade.evisa.service.EVisaService;
@@ -84,8 +91,8 @@ import java.util.*;
 @AllArgsConstructor
 public class EVisaServiceImpl implements EVisaService {
 
-    private static final String SIGN_HOST = "172.30.224.79";
-    //private static final String SIGN_HOST = "47.115.117.246";
+//    private static final String SIGN_HOST = "172.30.224.79";
+    private static final String SIGN_HOST = "47.115.117.246";
 
     private static final String SIGN_PORT = "8183";
 
@@ -1363,4 +1370,167 @@ public class EVisaServiceImpl implements EVisaService {
             return null;
         }
     }
+
+    /**
+     *      单张PDF批量电签,返回电签后的PDF
+     * @param vo
+     * @return
+     */
+    @Override
+    public R<String> batchEVisa(SigInfoVO vo) {
+        try {
+            List<SigInfoVO.SigInfo> list = vo.getList();
+            String pdfUrl = vo.getPdfUrl();
+
+            for (SigInfoVO.SigInfo sigInfo : list) {
+                pdfUrl = myDoWork(sigInfo, pdfUrl);
+            }
+            return R.data(pdfUrl);
+        }catch (Exception e){
+            return R.fail(e.getMessage());
+        }
+    }
+    public String myDoWork(SigInfoVO.SigInfo sig,String pdfUrl) throws Exception {
+
+        PaperlessClient paperlessClient = new PaperlessClient(SIGN_HOST, SIGN_PORT, 300000, 1800000);
+
+        paperlessClient.setSSL(false);
+        /*******************************************************************************/
+        SealPdfRequest sealPdfRequest = new SealPdfRequest();
+        RequestHead requestHead = new RequestHead();
+        String transactionNo = GUIDUtil.generateId();
+        //机构编码非空
+        String organizationCode = EVisaConstant.organizationCode;
+        //操作员编码 可为空
+        String operatorCode = "";
+        //渠道编码 可为空
+        String channelCode = "";
+        //设置属性
+        requestHead.setBasicInfo(transactionNo, organizationCode,operatorCode,channelCode);
+
+        sealPdfRequest.setHead(requestHead);
+        /*******************************************************************************/
+        SealPdfRequestBody requestBody = new SealPdfRequestBody();
+        //待签章文件
+        byte[] pdfData = CommonUtil.InputStreamToBytes(CommonUtil.getOSSInputStream(pdfUrl));
+        // 数据源类型 1 pdf文件路径 2 pdf文件字节流
+        String inputType = BaseConstants.INPUT_TYPE_FILEDATA;
+        //设置数据源,数据源类型为2时,为空,数据源类型为1时,上送数据源路径(服务器可以访问到的文件路径)
+        requestBody.setInputSource("");
+        //数据源类型
+        requestBody.setInputType(inputType);
+        //数据源为2时,需上送 pdf文件数据
+        requestBody.setPdfData(pdfData);
+        //签章后文件保存地址,不为空时,直接将签章文件保存在此地址,不再返回签章后文档数据
+        requestBody.setOutputFilepath("");
+        //时间戳方式,默认为0;0:实时访问CFCA 时间戳服务;1:使用从CFCA购置并在本地部署的时间戳服务器产品;
+        requestBody.setTimestampChannel(BaseConstants.TIME_STAMP_CHANNEL_CFCA);
+
+        /************************ ②-2.构造机构章策略 *********************************************/
+        SealStrategy sealStrategy = myGenerateSealStrategy(sig);
+
+        requestBody.setSealStrategy(sealStrategy);
+
+        sealPdfRequest.setBody(requestBody);
+
+        /************************请求签章 ********************************************/
+        ResponseDto responseDto = paperlessClient.execute(sealPdfRequest);
+
+        /************************解析响应结果 *********************************************/
+
+        SealPdfResponse sealPdfResponse = (SealPdfResponse)responseDto;
+
+        ResponseHead responseHead = sealPdfResponse.getHead();
+        SealPdfResponseBody responseBody = sealPdfResponse.getBody();
+        String resultOutputFilepath = "";
+        if(ClientConstants.CODE_SUCCESS.equals(responseHead.getCode())){
+
+            resultOutputFilepath = responseBody.getOutputFilepath();
+
+            byte[] pdf = responseBody.getPdf();
+            System.out.println(pdf == null ? 0 : pdf.length);
+
+            String resultPdfFileDataHash = responseBody.getPdfFileDataHash();
+            System.out.println("resultPdfFileDataHash=" + resultPdfFileDataHash);
+
+            if(cfca.paperless.base.util.StringUtil.isNotEmpty(resultOutputFilepath)) {
+                System.out.println("文件已输出到:"+ resultOutputFilepath);
+            } else {
+                Random random = new Random();
+                int i = random.nextInt(100000);
+                String pdfFile = "/www/wwwroot/Users/hongchuangyanfa/Desktop/archiveCheck/" + i +".pdf";
+                resultOutputFilepath = pdfFile;
+                IoUtil.write(resultOutputFilepath, pdf);
+                System.out.println("文件已输出到:"+ resultOutputFilepath );
+                BladeFile bladeFile = newIOSSClient.uploadFile("aa.pdf",pdfFile);
+                if (bladeFile == null || StringUtils.isBlank(bladeFile.getLink())){
+                    throw new ServiceException("上传阿里云失败");
+                }
+                resultOutputFilepath = bladeFile.getLink();
+            }
+
+        } else {
+            throw new ServiceException(" NG,Code:"+responseHead.getCode()+",Message:"+responseHead.getMessage());
+        }
+
+        return resultOutputFilepath;
+    }
+    public SealStrategy  myGenerateSealStrategy(SigInfoVO.SigInfo sig) throws Exception{
+
+        String type = BaseConstants.SEAL_TYPE_IMAGE;
+        String sealCode = sig.getSealCode();
+        String sealPassword = PwdEncryptUtil.encrypto(sig.getSealPassword());
+        SealStrategy  sealStrategy = new SealStrategy();
+        sealStrategy.setSealInfo(type, sealCode, sealPassword);
+
+        String pngFilePath = sig.getSigPic();
+        //外传图片
+        byte[] imageData = CommonUtil.InputStreamToBytes(CommonUtil.getOSSInputStream(pngFilePath));
+        //图片进行Base64编码/
+        String imageDataString =  new String(Base64.encode(imageData), StandardCharsets.UTF_8);
+        sealStrategy.setSealImage(imageDataString);
+        //算法,非空
+        String hashAlg = BaseConstants.HASHALG_SM3;
+        sealStrategy.setHashAlg(hashAlg);
+        //透明度,0-1.0f,默认1.0f,不透明
+        String fillOpacity = "1";
+        sealStrategy.setFillOpacity(fillOpacity);
+        //是否显示,默认1 显示
+        String visible = "1";
+        sealStrategy.setVisible(visible);
+        //签章图片显示尺寸  单位毫米  默认为图片本身尺寸
+        //签章图片显示尺寸,单位毫米;如果是圆形,则为半径实际尺寸;如果是矩形,则为较长的一边的实际尺寸;如果是不规则图形,则为较长的一边的实际尺寸
+        String displaySize = "50";
+        sealStrategy.setDisplaySize(displaySize);
+        // 业务码 businessCode ,businessCode为空,则不添加业务码,业务码相关属性都不需要设置
+        //业务码内容
+        String businessCode = "";
+        // 业务码字体大小,默认12,可以为空
+        String businessFontSize = "20";
+        //以图片底部为起点,向上占图片百分之多少的位置开始显示业务码,默认0.5
+        String textRectHeightPercent = "0.5";
+        //颜色值,默认黑色 000000
+        String businessColor = "FF0000";
+        //字体,默认宋体,可以为空
+        String bussinessFamily = "";
+        // 业务码  end
+        sealStrategy.setBusinessInfo(businessCode, businessFontSize, textRectHeightPercent, businessColor, bussinessFamily);
+        // 签章杂项信息
+        //签章人姓名,必填
+        String sealPerson = sig.getUserName();
+        //签章地点,可以为空
+        String sealLocation = "";
+        //签章原因,可以为空
+        String sealReason = "";
+        sealStrategy.setSealMiscInfo(sealPerson, sealLocation, sealReason);
+        /************************关键字签章****************************************************/
+        //关键字
+        String keyword =sig.getUserSig();
+        //x轴偏移,默认0
+        String offSetX = "0";
+        //y轴偏移,默认0
+        String offSetY = "0";
+        sealStrategy.setSignWithKeywordInfo(keyword, offSetX, offSetY);
+        return sealStrategy;
+    }
 }

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

@@ -426,7 +426,7 @@ public class ArchiveTreeContractController extends BladeController {
     @ApiOperation(value = "同步业务数据", notes = "传入节点id")
     public R test1(Long projectId,Long contractId) {
 
-        archiveTreeContractSync.syncBusinessData(projectId,contractId,1);
+        archiveTreeContractSync.syncBusinessData(projectId,contractId,1,contractId);
 
         return R.success("开始同步合同段,请耐心等待" );
     }

+ 13 - 8
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ArchiveTreeContractSyncImpl.java

@@ -264,12 +264,12 @@ public class ArchiveTreeContractSyncImpl {
                 if (contractIndfo.getContractType() == 2) {
                     List<ContractRelationJlyz> list = contractInfoService.searchContractRelationInfo(contractId.toString());
                     for (ContractRelationJlyz contractRelationJlyz: list) {
-                        syncBusinessData(projectId,contractRelationJlyz.getId(),2);
+                        syncBusinessData(projectId,contractRelationJlyz.getId(),2,contractId);
                     }
 
                 }else {
                     //将项目未锁定案卷拆卷
-                    syncBusinessData(projectId,contractId,1);
+                    syncBusinessData(projectId,contractId,1,contractId);
                 }
                 contractInfoService.updateIsArchivesAutoById(contractId, 0);
 
@@ -289,7 +289,7 @@ public class ArchiveTreeContractSyncImpl {
      * @param projectId
      * @param contractId
      */
-    public void  syncBusinessData(Long projectId,Long contractId,Integer classify) {
+    public void  syncBusinessData(Long projectId,Long contractId,Integer classify,Long orgContractId) {
 
         log.info("同步文件  projectId:"+projectId+"-contractId:"+contractId);
 
@@ -345,7 +345,7 @@ public class ArchiveTreeContractSyncImpl {
 
         //同步质检关联节点
         syncNodes(contractId, informationQueryList, wbsTreeContractVO6Map, wbsTreeKeyToSortMap,
-                wbsTreeKeyToIdMap, archiveKeyIdMap, contractIndfo,classify);
+                wbsTreeKeyToIdMap, archiveKeyIdMap, contractIndfo,classify,orgContractId);
 
 
         //wbs的keyId到 归档树的映射
@@ -432,9 +432,14 @@ public class ArchiveTreeContractSyncImpl {
     private void syncNodes(Long contractId, List<InformationQuery> informationQueryList,
                            Map<Long, WbsTreeContractVO6> wbsTreeContractVO6Map, Map<Long, Integer> wbsTreeKeyToSortMap,
                            Map<Long, Long> wbsTreeKeyToIdMap, Map<Long, ArchiveFile> archiveKeyIdMap,
-                           ContractInfo contractIndfo,Integer classify) {
-        // 获取关联节点
-        List<ArchiveTreeContract> associatedNodes = getWbsAssociatedNodes(contractId.toString());
+                           ContractInfo contractIndfo,Integer classify,Long orgContractId) {
+        // 获取关联节点的合同段id
+        Long associatedContractId = contractId;
+        if (classify == 2) {
+            associatedContractId = orgContractId;
+        }
+
+        List<ArchiveTreeContract> associatedNodes = getWbsAssociatedNodes(associatedContractId.toString());
         if (associatedNodes == null || associatedNodes.isEmpty()) {
             return;
         }
@@ -455,7 +460,7 @@ public class ArchiveTreeContractSyncImpl {
             List<ArchiveTreeContract> archiveTreeContracts = getArchiveTreeContractsWbs(contractId, node.getId(),classify);
 
             // 更新archiveTreeContractIdMap映射
-            archiveTreeContractIdMap.clear();
+             archiveTreeContractIdMap.clear();
             for (ArchiveTreeContract ar : archiveTreeContracts) {
                 archiveTreeContractIdMap.put(ar.getExtKeyId(), ar.getId());
             }

+ 19 - 14
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractInfoServiceImpl.java

@@ -976,20 +976,25 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
                 projectContractArea.setProjectId(contractInfo.getPId());
                 projectContractArea.setIsDeleted(0);
                 projectContractArea.setContractId(String.valueOf(contractInfo.getId()));
-                QueryWrapper<ProjectContractArea> queryWrapper = new QueryWrapper<>();
-                queryWrapper.eq("project_id", contractInfo.getPId());
-                queryWrapper.eq("contract_id", contractInfo.getId());
-                queryWrapper.eq("province", position.get("province"));
-                queryWrapper.eq("city", position.get("city"));
-                queryWrapper.eq("county", position.get("district"));
-                queryWrapper.eq("city_code", position.get("adcode"));
-                queryWrapper.eq("is_deleted", contractInfo.getIsDeleted());
-                ProjectContractArea projectContractArea1 = projectContractAreaMapper.selectOne(queryWrapper);
-                if (projectContractArea1 == null) {
-                    int res = projectContractAreaMapper.insert(projectContractArea);
-                    return res > 0;
-                }
-                return true;
+//                QueryWrapper<ProjectContractArea> queryWrapper = new QueryWrapper<>();
+//                queryWrapper.eq("project_id", contractInfo.getPId());
+//                queryWrapper.eq("contract_id", contractInfo.getId());
+//                queryWrapper.eq("province", position.get("province"));
+//                queryWrapper.eq("city", position.get("city"));
+//                queryWrapper.eq("county", position.get("district"));
+//                queryWrapper.eq("city_code", position.get("adcode"));
+//                queryWrapper.eq("is_deleted", contractInfo.getIsDeleted());
+//                ProjectContractArea projectContractArea1 = projectContractAreaMapper.selectOne(queryWrapper);
+//                if (projectContractArea1 == null) {
+//                    int res = projectContractAreaMapper.insert(projectContractArea);
+//                    return res > 0;
+//                }
+                //删除之前所有的地址,再新增
+                projectContractAreaMapper.delete(new LambdaQueryWrapper<ProjectContractArea>()
+                    .eq(ProjectContractArea::getContractId,projectContractArea.getContractId()));
+                int res = projectContractAreaMapper.insert(projectContractArea);
+                return res > 0;
+//                return true;
             } catch (IOException e) {
                 throw new ServiceException("施工台账初始化失败");
             }