Przeglądaj źródła

fix:卷宗合并

zzw 1 tydzień temu
rodzic
commit
40ead5bdfd

+ 2 - 1
assistMg/src/main/java/com/hotent/constant/TaskStatusConstant.java

@@ -15,7 +15,8 @@ public enum TaskStatusConstant {
     SUSPENDED("300", "中止", 3),
     COMPLETED("400", "办结", 4),
     NOT_PASSED("500", "不通过", 5),
-    BCCL("600", "补充材料", 6);
+    BCCL("600", "补充材料", 6),
+    BCCLSH("666", "补充材料审核", 6);
 
 
     private String statusCode;

+ 31 - 0
assistMg/src/main/java/com/hotent/enterpriseDeclare/controller/material/CostProjectTaskSurveyGenericController.java

@@ -329,6 +329,28 @@ public class CostProjectTaskSurveyGenericController {
     }
 
     /**
+     * 检查核定表是否包含核定值字段
+     */
+    @GetMapping(value = "/checkVerifyValueColumn")
+    @ApiOperation(value = "检查核定表是否包含核定值字段", httpMethod = "GET")
+    public CommonResult<Boolean> checkVerifyValueColumn(
+            @ApiParam(name = "taskId", value = "任务ID", required = true)
+            @RequestParam String taskId) {
+        // 通过taskId查询核定表模板
+        CostVerifyTemplate template = costVerifyTemplateManager.getOne(
+                new QueryWrapper<CostVerifyTemplate>().eq("task_id", taskId));
+        if (template == null) {
+            return CommonResult.<Boolean>ok().value(false);
+        }
+        // 查询表头
+        List<CostVerifyTemplateHeaders> headersList = costVerifyTemplateHeadersDao.selectList(
+                new QueryWrapper<CostVerifyTemplateHeaders>().eq("survey_template_id", template.getSurveyTemplateId()));
+        boolean hasVerifyValueColumn = headersList.stream()
+                .anyMatch(h -> h.getFieldName() != null && h.getFieldName().contains("核定值"));
+        return CommonResult.<Boolean>ok().value(hasVerifyValueColumn);
+    }
+
+    /**
      * 企业报送-调查表-在线填报
      *
      * @param dataList 数据列表
@@ -1097,6 +1119,15 @@ public class CostProjectTaskSurveyGenericController {
                     return;
                 }
 
+                // 校验表头中是否有"核定值"列
+                boolean hasVerifyValueColumn = headersList.stream()
+                        .anyMatch(h -> h.getFieldName() != null && h.getFieldName().contains("核定值"));
+                if (!hasVerifyValueColumn) {
+                    response.setContentType("application/json;charset=UTF-8");
+                    response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("请先保存模板")));
+                    return;
+                }
+
                 List<CostVerifyTemplateItems> itemsList =
                         costVerifyTemplateItemsDao.selectByVerifyTemplateId(surveyTemplateId, null);
 

+ 1 - 0
assistMg/src/main/java/com/hotent/enterpriseDeclare/req/CostTaskReviewPageReq.java

@@ -42,6 +42,7 @@ public class CostTaskReviewPageReq extends PageReq {
 
     private String startYear;
 
+    // type=1: 按用户数据范围过滤(市级看本市、县级看本县); type!=1: 按项目负责人/成员过滤
     private Integer type=2;
 }
 

+ 32 - 12
assistMg/src/main/java/com/hotent/project/component/TaskWarningStatusComponent.java

@@ -1,13 +1,15 @@
 package com.hotent.project.component;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.hotent.project.manager.CostProjectTaskNodeManager;
+import com.hotent.project.manager.CostProjectProccessManager;
+import com.hotent.project.manager.CostProjectProccessNodeManager;
+import com.hotent.project.model.CostProjectProccess;
+import com.hotent.project.model.CostProjectProccessNode;
 import com.hotent.project.model.CostProjectTask;
-import com.hotent.project.model.CostProjectTaskNode;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.time.LocalDateTime;
+import java.time.LocalDate;
 
 /**
  * 任务预警状态计算组件
@@ -16,29 +18,47 @@ import java.time.LocalDateTime;
 public class TaskWarningStatusComponent {
 
     @Autowired
-    private CostProjectTaskNodeManager costProjectTaskNodeManager;
+    private CostProjectProccessManager costProjectProccessManager;
+
+    @Autowired
+    private CostProjectProccessNodeManager costProjectProccessNodeManager;
 
     /**
      * 计算任务预警状态
      * @param task 任务对象
-     * @return 预警状态:green-绿色预警(在办理期限内) red-红色预警(超过环节期限)
+     * @return 预警状态:green-绿色预警(在当前环节期限内) yellow-黄色预警(超过当前环节期限) red-红色预警(超过整个流程期限)
      */
     public String calculateWarningStatus(CostProjectTask task) {
-        CostProjectTaskNode currentNode = costProjectTaskNodeManager.getOne(
-                new LambdaQueryWrapper<CostProjectTaskNode>()
-                        .eq(CostProjectTaskNode::getTaskId, task.getId())
-                        .eq(CostProjectTaskNode::getProcessNodeKey, task.getCurrentNode())
+        // 获取流程信息
+        CostProjectProccess process = costProjectProccessManager.getOne(
+                new LambdaQueryWrapper<CostProjectProccess>()
+                        .eq(CostProjectProccess::getProjectId, task.getProjectId())
         );
 
-        if (currentNode == null || currentNode.getEndTime() == null) {
+        if (process == null) {
             return "green";
         }
 
-        LocalDateTime now = LocalDateTime.now();
-        if (now.isAfter(currentNode.getEndTime())) {
+        LocalDate now = LocalDate.now();
+
+        // 1. 检查是否超过整个流程期限(红色预警)
+        if (process.getPlannedAuditEndDate() != null && now.isAfter(process.getPlannedAuditEndDate())) {
             return "red";
         }
 
+        // 获取当前环节的节点信息
+        CostProjectProccessNode currentNode = costProjectProccessNodeManager.getOne(
+                new LambdaQueryWrapper<CostProjectProccessNode>()
+                        .eq(CostProjectProccessNode::getProcessId, process.getProcessId())
+                        .eq(CostProjectProccessNode::getProcessNodeKey, task.getCurrentNode())
+        );
+
+        // 2. 检查是否超过当前环节期限(黄色预警)
+        if (currentNode != null && currentNode.getEndTime() != null && now.isAfter(currentNode.getEndTime())) {
+            return "yellow";
+        }
+
+        // 3. 在当前环节期限内(绿色预警)
         return "green";
     }
 }

+ 1 - 0
assistMg/src/main/java/com/hotent/project/controller/CostProjectTaskController.java

@@ -100,6 +100,7 @@ public class CostProjectTaskController extends BaseController<CostProjectTaskMan
     @PostMapping("/taskList")
     @ApiOperation(value = "任务进度管理列表", notes = "任务进度管理列表")
     public  CommonResult<IPage<CostProjectTask>> getCatalogList(@RequestBody CostTaskReviewPageReq req) throws Exception {
+        req.setType(1);
         req.setNStatus(TaskStatusConstant.COMPLETED.getStatusCode());
         IPage<CostProjectTask> pageResult = baseService.pageListForReviewTask(req);
         for (CostProjectTask task : pageResult.getRecords()) {

+ 18 - 3
assistMg/src/main/java/com/hotent/project/manager/impl/CostNoticeManagerImpl.java

@@ -84,7 +84,25 @@ public class CostNoticeManagerImpl extends BaseManagerImpl<CostNoticeDao, CostNo
 
 	@Override
 	public IPage<CostNotice> pageList(CostProjectNoticePageReq req) {
+		User currentUser = ContextUtil.getCurrentUser();
+		String currentUserId = currentUser.getId();
+
 		LambdaQueryWrapper<CostNotice> qw = new LambdaQueryWrapper<>();
+		qw.eq(CostNotice::getIsDeleted, "0");
+
+		// 用户权限过滤:只能看到发给自己或自己企业的消息
+		com.hotent.baseInfo.model.AuditedUnit unit = auditedUnitManager.getOne(
+			new LambdaQueryWrapper<com.hotent.baseInfo.model.AuditedUnit>()
+				.eq(com.hotent.baseInfo.model.AuditedUnit::getAccount, currentUserId)
+				.last("limit 1")
+		);
+		if (unit != null) {
+			qw.and(q -> q.eq(CostNotice::getEnterpriseId, unit.getUnitId())
+					.or()
+					.eq(CostNotice::getSendTarget, currentUserId));
+		} else {
+			qw.eq(CostNotice::getSendTarget, currentUserId);
+		}
 
 		if(ObjectUtil.isNotEmpty(req.getTitle())){
 			qw.like(CostNotice::getNoticeTitle, req.getTitle());
@@ -98,9 +116,6 @@ public class CostNoticeManagerImpl extends BaseManagerImpl<CostNoticeDao, CostNo
 		if(ObjectUtil.isNotEmpty(req.getTaskId())){
 			qw.eq(CostNotice::getTaskId, req.getTaskId());
 		}
-//		if(ObjectUtil.isNotEmpty(req.getEnterpriseId())){
-//			qw.eq(CostNotice::getEnterpriseId, req.getEnterpriseId());
-//		}
 		qw.orderByDesc(CostNotice::getCreateTime);
 		IPage<CostNotice> page=new Page<>(req.getPageNum(), req.getPageSize());
 		IPage<CostNotice> costNoticeIPage = this.page(page, qw);

+ 6 - 2
assistMg/src/main/java/com/hotent/project/manager/impl/CostProjectApprovalManagerImpl.java

@@ -419,7 +419,9 @@ public class CostProjectApprovalManagerImpl extends BaseManagerImpl<CostProjectA
         //文书集合
         List<CostProjectDocument> documentList = this.costProjectDocumentManager.getListByProjectId(costProjectApproval.getProjectId());
         //资料集合
-        List<CostProjectMaterial> materialList = this.costProjectMaterialManager.getListByProjectId(costProjectApproval.getProjectId());
+//        List<CostProjectMaterial> materialList = this.costProjectMaterialManager.getListByProjectId(costProjectApproval.getProjectId());
+        List<CostCatalogUnit> catalogUnitList = costCatalogUnitManager.getListByCatalogId(costProjectApproval.getCatalogId());
+
         //流程对象
         CostProjectProccess projectProccess = costProjectProccessManager.getProcessByProjectId(costProjectApproval.getProjectId());
         //节点集合
@@ -561,9 +563,11 @@ public class CostProjectApprovalManagerImpl extends BaseManagerImpl<CostProjectA
             } );
             taskDocumentList.addAll(documents);
 
+
             // 关联资料
-            List<CostProjectTaskMaterial> materials = CopyUtil.copyList(materialList, CostProjectTaskMaterial.class);
+            List<CostProjectTaskMaterial> materials = CopyUtil.copyList(catalogUnitList, CostProjectTaskMaterial.class);
             materials.forEach(mat ->{
+                mat.setProjectId(costProjectApproval.getProjectId());
                 mat.setTaskId(childTask.getId());
                 mat.setId(null);
             } );

+ 2 - 2
assistMg/src/main/java/com/hotent/project/manager/impl/CostProjectDocumentManagerImpl.java

@@ -651,11 +651,11 @@ public class CostProjectDocumentManagerImpl extends BaseManagerImpl<CostProjectD
             } catch (IOException e) {
                 throw new RuntimeException("处理Word文档时出错", e);
             }
-            costProjectDocument.setActUrl(EipConfig.getImgUrl() + FileUploadUtil.getPathFileName(outputPath, fileName));
+            costProjectDocument.setActUrl(FileUploadUtil.getPathFileName(outputPath, fileName));
             this.updateById(costProjectDocument);
         }
 
-        return costProjectDocument.getActUrl() ;
+        return EipConfig.getImgUrl() + costProjectDocument.getActUrl() ;
     }
 
 

+ 2 - 2
assistMg/src/main/java/com/hotent/project/manager/impl/CostProjectMaterialManagerImpl.java

@@ -114,13 +114,13 @@ public class CostProjectMaterialManagerImpl extends BaseManagerImpl<CostProjectM
         List<String> list = records.stream().map(CostProjectMaterial::getInformationType).distinct().collect(Collectors.toList());
         QueryWrapper<DataDict> wrapper = new QueryWrapper<>();
         wrapper.eq("TYPE_ID_","1976557733440159744");
-        wrapper.in("KEY_",list);
+        wrapper.in(!list.isEmpty(),"KEY_",list);
         List<DataDict> costDictDatas= dataDictManager.list(wrapper);
 
         List<String> listRequire = records.stream().map(CostProjectMaterial::getFormatRequired).distinct().collect(Collectors.toList());
         QueryWrapper<DataDict> wrapperRequire = new QueryWrapper<>();
         wrapperRequire.eq("TYPE_ID_","1976558645202157568");
-        wrapperRequire.in("KEY_",listRequire);
+        wrapperRequire.in(!list.isEmpty(),"KEY_",listRequire);
         List<DataDict> listRequireDatas= dataDictManager.list(wrapperRequire);
 
         records.forEach(costCatalogUnit -> {

+ 18 - 15
assistMg/src/main/java/com/hotent/project/manager/impl/CostProjectTaskManagerImpl.java

@@ -419,7 +419,7 @@ public class CostProjectTaskManagerImpl extends BaseManagerImpl<CostProjectTaskD
 
         // 如果当前节点不是tjcl,则保持当前节点不变,只修改状态
         if (!"tjcl".equals(currentNode)) {
-            task.setStatus(TaskStatusConstant.AUDITING.getStatusCode());
+            task.setStatus(TaskStatusConstant.BCCLSH.getStatusCode());
         } else {
             task.setCurrentNode(NodeConstant.clcs.getNodeKey());
             task.setStatus(TaskStatusConstant.AUDITING.getStatusCode());
@@ -1285,21 +1285,18 @@ public class CostProjectTaskManagerImpl extends BaseManagerImpl<CostProjectTaskD
             throw new RuntimeException("未指定催办人员");
         }
         String title = NodeConstant.getNodeValueByKey(task.getCurrentNode()) + "催办";
+        String orgId = getCurrentUserMainOrgId();
+        Org org = orgManager.getById(orgId);
+        String noticeSource = (org.getName()) + " " + AuthenticationUtil.getCurrentUserFullname();
+        String enterpriseId = task.getAuditedUnitId() == null ? "" : task.getAuditedUnitId();
         // 分割用户ID并逐个发送通知
         for (String userId : req.getUserIds().split(",")) {
-
-            // 发送催办通知
             String content = "[" + NodeConstant.getNodeValueByKey(task.getCurrentNode()) + "]" + AuthenticationUtil.getCurrentUserFullname() + "催办" + task.getAuditedUnitName() + "的项目(" + task.getProjectName() + ")";
             if (StringUtil.isNotEmpty(req.getContent())) {
                 content += ",原因:" + req.getContent();
             }
-            String enterpriseId = task.getAuditedUnitId() == null ? "" : task.getAuditedUnitId();
-            String orgId = getCurrentUserMainOrgId();
-            Org org = orgManager.getById(orgId);
-            String noticeSource = (org.getName()) + " " + AuthenticationUtil.getCurrentUserFullname();
-            String sendTarget = task.getCreateBy() == null ? "" : task.getCreateBy();
             // 使用传入的用户ID作为发送目标
-            costNoticeManager.sendNotice(task.getProjectId(), task.getId(), "1", title, content, enterpriseId, noticeSource, sendTarget);
+            costNoticeManager.sendNotice(task.getProjectId(), task.getId(), "1", title, content, enterpriseId, noticeSource, userId);
         }
         return title;
     }
@@ -1312,10 +1309,7 @@ public class CostProjectTaskManagerImpl extends BaseManagerImpl<CostProjectTaskD
      * @return 催报结果消息
      */
     private String remindUnitTask(CostProjectTask task, CostTaskPageReq req) {
-
         String title = NodeConstant.getNodeValueByKey(task.getCurrentNode()) + "催报";
-        // 分割用户ID并逐个发送通知
-        // 发送催报通知
         String content = "[" + NodeConstant.getNodeValueByKey(task.getCurrentNode()) + "]" + AuthenticationUtil.getCurrentUserFullname() + "催报" + task.getAuditedUnitName() + "的项目(" + task.getProjectName() + ")";
         if (StringUtil.isNotEmpty(req.getContent())) {
             content += ",原因:" + req.getContent();
@@ -1324,10 +1318,19 @@ public class CostProjectTaskManagerImpl extends BaseManagerImpl<CostProjectTaskD
         String orgId = getCurrentUserMainOrgId();
         Org org = orgManager.getById(orgId);
         String noticeSource = (org.getName()) + " " + AuthenticationUtil.getCurrentUserFullname();
-        String sendTarget = task.getCreateBy() == null ? "" : task.getCreateBy();
-        // 使用传入的用户ID作为发送目标
+        // 获取企业关联账号作为发送目标
+        String sendTarget = "";
+        if (StringUtil.isNotEmpty(enterpriseId)) {
+            AuditedUnit auditedUnit = auditedUnitManager.getOne(
+                    new LambdaQueryWrapper<AuditedUnit>()
+                            .eq(AuditedUnit::getUnitId, enterpriseId)
+                            .eq(AuditedUnit::getIsDeleted, "0")
+            );
+            if (auditedUnit != null && StringUtil.isNotEmpty(auditedUnit.getAccount())) {
+                sendTarget = auditedUnit.getAccount();
+            }
+        }
         costNoticeManager.sendNotice(task.getProjectId(), task.getId(), "1", title, content, enterpriseId, noticeSource, sendTarget);
-
         return title;
     }
 

+ 109 - 50
assistMg/src/main/java/com/hotent/project/service/AsyncMaterialSummaryService.java

@@ -822,7 +822,8 @@ public class AsyncMaterialSummaryService {
      */
     @Async
     public void generateWordArchiveAsync(String taskId) {
-        CostProjectTask task = costProjectTaskManager.getById(taskId);
+        boolean success = false;
+        String archiveUrl = null;
         try {
             logger.info("开始生成Word卷宗,任务ID:{}", taskId);
 
@@ -835,11 +836,9 @@ public class AsyncMaterialSummaryService {
             boolean isFirstDocument = true;
 
             // 按顺序合并:封面(-1) -> 目录(-2) -> 14个资料(1-13) -> 封底(-3)
-            // 排序:-1, -2, 1, 2, ..., 13, -3
             allSummaryList.sort((a, b) -> {
                 int orderA = a.getMaterialOrderNum() != null ? a.getMaterialOrderNum() : 0;
                 int orderB = b.getMaterialOrderNum() != null ? b.getMaterialOrderNum() : 0;
-                // -1(封面) < -2(目录) < 1-13(资料) < -3(封底)
                 int sortA = orderA == -1 ? -100 : (orderA == -2 ? -99 : (orderA == -3 ? 100 : orderA));
                 int sortB = orderB == -1 ? -100 : (orderB == -2 ? -99 : (orderB == -3 ? 100 : orderB));
                 return Integer.compare(sortA, sortB);
@@ -877,26 +876,62 @@ public class AsyncMaterialSummaryService {
             document.close();
 
             // 生成访问URL
-            String archiveUrl = FileUploadUtil.getPathFileName(outputPath, fileName);
-
+            archiveUrl = FileUploadUtil.getPathFileName(outputPath, fileName);
+            success = true;
             logger.info("Word卷宗生成完成,任务ID:{},URL:{}", taskId, archiveUrl);
 
-            // 更新任务的卷宗URL
-            if (task != null) {
-                task.setArchiveUrl(archiveUrl);
-                task.setArchiveStatus("2");
-                task.setArchiveTime(LocalDateTime.now());
-                costProjectTaskManager.updateById(task);
-                logger.info("卷宗URL已更新,任务ID:{}", taskId);
+        } catch (Exception e) {
+            logger.error("生成Word卷宗失败,任务ID:{},错误:{}", taskId, e.getMessage(), e);
+        } finally {
+            // 无论成功失败,都更新状态
+            try {
+                CostProjectTask task = costProjectTaskManager.getById(taskId);
+                if (task != null) {
+                    if (success) {
+                        task.setArchiveUrl(archiveUrl);
+                        task.setArchiveStatus("2");
+                        task.setArchiveTime(LocalDateTime.now());
+                    } else {
+                        task.setArchiveStatus("3");
+                    }
+                    costProjectTaskManager.updateById(task);
+                    logger.info("卷宗状态已更新,任务ID:{},状态:{}", taskId, success ? "2-成功" : "3-失败");
+                }
+            } catch (Exception e) {
+                logger.error("更新卷宗状态失败,任务ID:{},错误:{}", taskId, e.getMessage(), e);
             }
+        }
+    }
 
-        } catch (Exception e) {
-            if (task != null) {
-                task.setArchiveStatus("3");
-                costProjectTaskManager.updateById(task);
+    /**
+     * 将URL转换为本地文件路径
+     * 支持格式:
+     * 1. /profile/upload/xxx.docx
+     * 2. http://xxx/profile/upload/xxx.docx
+     * 3. 本地绝对路径
+     */
+    private String convertToLocalPath(String fileUrl) {
+        if (StringUtil.isEmpty(fileUrl)) {
+            return null;
+        }
+
+        // http或https开头,提取/profile部分
+        if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
+            int profileIndex = fileUrl.indexOf("/profile/");
+            if (profileIndex > 0) {
+                String relativePath = fileUrl.substring(profileIndex);
+                return EipConfig.getProfile() + relativePath.substring(8);
             }
-            logger.error("生成Word卷宗失败,任务ID:,错误:{}", taskId, e.getMessage(), e);
+            return null;
         }
+
+        // /profile开头
+        if (fileUrl.startsWith("/profile")) {
+            return EipConfig.getProfile() + fileUrl.substring(8);
+        }
+
+        // 本地绝对路径
+        return fileUrl;
     }
 
     /**
@@ -909,10 +944,10 @@ public class AsyncMaterialSummaryService {
      */
     private boolean mergeDocumentFile(XWPFDocument document, String fileUrl, boolean isFirstDocument) {
         try {
-            String filePath = fileUrl;
-            // 处理路径:/profile 开头的路径需要替换为实际路径
-            if (filePath.startsWith("/profile")) {
-                filePath = EipConfig.getProfile() + filePath.substring(8);
+            String filePath = convertToLocalPath(fileUrl);
+            if (filePath == null) {
+                logger.warn("无法解析文件路径:{}", fileUrl);
+                return isFirstDocument;
             }
 
             java.io.File file = new java.io.File(filePath);
@@ -953,36 +988,51 @@ public class AsyncMaterialSummaryService {
     /**
      * 合并Word文档
      */
-    private void mergeWordDocument(XWPFDocument document, String filePath) throws Exception {
-        java.io.FileInputStream fis = new java.io.FileInputStream(filePath);
-        XWPFDocument sourceDoc = new XWPFDocument(fis);
-
-        org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody srcBody = sourceDoc.getDocument().getBody();
-        org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody destBody = document.getDocument().getBody();
-
-        for (org.apache.xmlbeans.XmlObject obj : srcBody.selectPath("./*")) {
-            if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP) {
-                org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP newP = destBody.addNewP();
-                newP.set(obj.copy());
-            } else if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl) {
-                org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl newTbl = destBody.addNewTbl();
-                newTbl.set(obj.copy());
+    private void mergeWordDocument(XWPFDocument document, String filePath) {
+        java.io.FileInputStream fis = null;
+        XWPFDocument sourceDoc = null;
+        try {
+            fis = new java.io.FileInputStream(filePath);
+            sourceDoc = new XWPFDocument(fis);
+
+            org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody srcBody = sourceDoc.getDocument().getBody();
+            org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody destBody = document.getDocument().getBody();
+
+            for (org.apache.xmlbeans.XmlObject obj : srcBody.selectPath("./*")) {
+                if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP) {
+                    org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP newP = destBody.addNewP();
+                    newP.set(obj.copy());
+                } else if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl) {
+                    org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl newTbl = destBody.addNewTbl();
+                    newTbl.set(obj.copy());
+                }
+            }
+        } catch (Exception e) {
+            logger.error("合并Word失败:{},错误:{}", filePath, e.getMessage());
+            XWPFParagraph para = document.createParagraph();
+            XWPFRun run = para.createRun();
+            run.setText("[Word文件:" + new java.io.File(filePath).getName() + " - 无法嵌入]");
+        } finally {
+            try {
+                if (sourceDoc != null) sourceDoc.close();
+                if (fis != null) fis.close();
+            } catch (Exception e) {
+                logger.warn("关闭文件流失败:{}", e.getMessage());
             }
         }
-
-        sourceDoc.close();
-        fis.close();
     }
 
     /**
      * 合并PDF文档(转换为图片后插入)
      */
-    private void mergePdfDocument(XWPFDocument document, String filePath) throws Exception {
+    private void mergePdfDocument(XWPFDocument document, String filePath) {
+        PDDocument pdfDoc = null;
         try {
-            PDDocument pdfDoc = PDDocument.load(new java.io.File(filePath));
+            pdfDoc = PDDocument.load(new java.io.File(filePath));
             org.apache.pdfbox.rendering.PDFRenderer renderer = new org.apache.pdfbox.rendering.PDFRenderer(pdfDoc);
+            int pageCount = pdfDoc.getNumberOfPages();
 
-            for (int i = 0; i < pdfDoc.getNumberOfPages(); i++) {
+            for (int i = 0; i < pageCount; i++) {
                 java.awt.image.BufferedImage image = renderer.renderImageWithDPI(i, 200);
 
                 java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
@@ -999,29 +1049,34 @@ public class AsyncMaterialSummaryService {
 
                 para.createRun().addBreak();
 
-                if (i < pdfDoc.getNumberOfPages() - 1) {
+                if (i < pageCount - 1) {
                     XWPFParagraph pageBreak = document.createParagraph();
                     XWPFRun pageBreakRun = pageBreak.createRun();
                     pageBreakRun.addBreak(BreakType.PAGE);
                 }
             }
-
-            pdfDoc.close();
             logger.info("PDF文件已合并:{}", filePath);
         } catch (Exception e) {
             logger.error("合并PDF失败:{},错误:{}", filePath, e.getMessage());
             XWPFParagraph para = document.createParagraph();
             XWPFRun run = para.createRun();
             run.setText("[PDF文件:" + new java.io.File(filePath).getName() + " - 无法嵌入]");
+        } finally {
+            try {
+                if (pdfDoc != null) pdfDoc.close();
+            } catch (Exception e) {
+                logger.warn("关闭PDDocument失败:{}", e.getMessage());
+            }
         }
     }
 
     /**
      * 合并Excel文档(转换为表格后插入)
      */
-    private void mergeExcelDocument(XWPFDocument document, String filePath) throws Exception {
+    private void mergeExcelDocument(XWPFDocument document, String filePath) {
+        org.apache.poi.ss.usermodel.Workbook workbook = null;
         try {
-            org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(new java.io.File(filePath));
+            workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(new java.io.File(filePath));
 
             for (int sheetIndex = 0; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) {
                 org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(sheetIndex);
@@ -1063,14 +1118,18 @@ public class AsyncMaterialSummaryService {
                     pageBreakRun.addBreak(BreakType.PAGE);
                 }
             }
-
-            workbook.close();
             logger.info("Excel文件已合并:{}", filePath);
         } catch (Exception e) {
             logger.error("合并Excel失败:{},错误:{}", filePath, e.getMessage());
             XWPFParagraph para = document.createParagraph();
             XWPFRun run = para.createRun();
-            run.setText("[Excel文件:" + new java.io.File(filePath).getName() + " - 无法嵌入,请查看原文件]");
+            run.setText("[Excel文件:" + new java.io.File(filePath).getName() + " - 无法嵌入]");
+        } finally {
+            try {
+                if (workbook != null) workbook.close();
+            } catch (Exception e) {
+                logger.warn("关闭Workbook失败:{}", e.getMessage());
+            }
         }
     }
 
@@ -1359,7 +1418,7 @@ public class AsyncMaterialSummaryService {
             CostProjectTask task = costProjectTaskManager.getById(req.getTaskId());
             if (task != null) {
                 task.setArchiveNo(req.getArchiveNo());
-                task.setArchiveUser(ContextUtil.getCurrentUser().getAccount());
+                task.setArchiveUser(ContextUtil.getCurrentUser().getFullname());
                 costProjectTaskManager.updateById(task);
             }
 

+ 30 - 61
assistMg/src/main/java/com/hotent/surveyinfo/controller/CostVerifyTemplateHeadersController.java

@@ -79,23 +79,6 @@ public class CostVerifyTemplateHeadersController extends BaseController<CostVeri
         return CommonResult.<List<CostVerifyTemplateHeaders>>ok().value(headersList);
     }
 
-    /**
-     * 根据成本核定表id获取现行版的表头数据列表
-     * @param surveyTemplateId 成本核定表id
-     * @return 表头数据列表
-     */
-//	@ApiOperation(value="根据成本核定表id获取现行版的表头数据列表", httpMethod = "GET", notes = "根据成本核定表id和版本号获取表头数据列表")
-//	@GetMapping("/getlistBySurveyTemplateIdcurrentversion")
-//	public CommonResult<List<CostVerifyTemplateHeaders>> getlistBySurveyTemplateIdcurrentversion(
-//			@ApiParam(name="surveyTemplateId",value="成本核定表id", required = true) @RequestParam String surveyTemplateId) {
-//
-//		//获取现行版本号
-//		CostSurveyTemplateVersion version = costSurveyTemplateVersionManager.selectCurrentVersion(surveyTemplateId);
-//		List<CostSurveyTemplateHeaders> headersList =
-//				costSurveyTemplateHeadersDao.selectBySurveyTemplateIdAndVersion(surveyTemplateId, version.getId());
-//
-//		return CommonResult.<List<CostSurveyTemplateHeaders>>ok().value(headersList);
-//	}
 
 
     /**
@@ -142,77 +125,63 @@ public class CostVerifyTemplateHeadersController extends BaseController<CostVeri
     public CommonResult<String> batchSaveOrUpdate(@ApiParam(name = "request", value = "包含表头列表和指标项列表的请求对象", required = true) @RequestBody BatchVerifyHeadersItemsRequest request) throws Exception {
 
         String costVerifyTemplateId = request.getCostVerifyTemplateId();
-
-
         List<CostVerifyTemplateHeaders> headersList = request.getHeadersList();
         List<CostVerifyTemplateItems> itemsList = request.getItemsList();
-        List<CostVerifyTemplateHeaders> deleteHeadersList = request.getDeleteheadersList();
 
         // 检查输入列表是否为空
-        if ((headersList == null || headersList.isEmpty()) && (itemsList == null || itemsList.isEmpty()) && (deleteHeadersList == null || deleteHeadersList.isEmpty())) {
+        if ((headersList == null || headersList.isEmpty()) && (itemsList == null || itemsList.isEmpty())) {
             return CommonResult.<String>error().message("批量操作失败,数据列表为空");
         }
         IUser user = ContextUtil.getCurrentUser();
 
-
-        // 先处理表头数据,确保所有表头都有ID
+        // 处理表头数据:先删除再重建,并建立旧ID到新ID的映射
+        java.util.Map<String, String> oldIdToNewIdMap = new java.util.HashMap<>();
         if (headersList != null && !headersList.isEmpty()) {
-            for (CostVerifyTemplateHeaders header : headersList) {
-                boolean isHeaderCreate = StringUtil.isEmpty(header.getId());
-                header.setFieldEname(PinyinUtil.getPinyin(header.getFieldName()));
-
-                if (StringUtil.isEmpty(header.getId())) {
-
-                    header.setId(UUID.randomUUID().toString());
-                    header.setCreateBy(user.getAccount());
-                    header.setCreateTime(LocalDateTime.now());
-                    baseService.create(header);
-                } else {
-                    header.setUpdateBy(user.getAccount());
-                    header.setUpdateTime(LocalDateTime.now());
-                    baseService.update(header);
-                }
-
-
-            }
-        }
-
-        // 处理需要删除的表头
-        if (deleteHeadersList != null && !deleteHeadersList.isEmpty()) {
-            for (CostVerifyTemplateHeaders header : deleteHeadersList) {
-                if (StringUtil.isNotEmpty(header.getId())) {
-                    baseService.remove(header.getId());
+            // 删除该模板下所有表头
+            costSurveyTemplateHeadersDao.deleteByCostVerifyTemplateId(costVerifyTemplateId);
 
+            // 批量插入新表头
+            for (CostVerifyTemplateHeaders header : headersList) {
+                String oldId = header.getId();
+                String newId = UUID.randomUUID().toString();
+                // 记录旧ID到新ID的映射
+                if (StringUtil.isNotEmpty(oldId)) {
+                    oldIdToNewIdMap.put(oldId, newId);
                 }
+                header.setId(newId);
+                header.setFieldEname(PinyinUtil.getPinyin(header.getFieldName()));
+                header.setCreateBy(user.getAccount());
+                header.setCreateTime(LocalDateTime.now());
             }
+            baseService.saveBatch(headersList);
         }
 
-
-        // 处理指标项数据,动态设置 headersId
+        // 处理指标项数据:先删除再重建
         if (itemsList != null && !itemsList.isEmpty()) {
-            if (itemsList.size() > 0) {
-                costSurveyTemplateItemsManager.deleteBySurveyTemplateId(costVerifyTemplateId);
-            }
-
+            costSurveyTemplateItemsManager.deleteBySurveyTemplateId(costVerifyTemplateId);
 
             // 批量处理指标项数据
             List<CostVerifyTemplateItems> itemsToSave = new ArrayList<>();
             for (CostVerifyTemplateItems item : itemsList) {
-                // 检查是否已设置 headersId
-                if (StringUtil.isEmpty(item.getHeadersId())) {
-                    item.setHeadersId(headersList.stream().filter(h -> h.getFieldName().equals(item.getRkey())).findFirst().map(CostVerifyTemplateHeaders::getId).orElse(null));
+                // 将旧的headersId映射到新的headersId
+                String oldHeadersId = item.getHeadersId();
+                if (StringUtil.isNotEmpty(oldHeadersId) && oldIdToNewIdMap.containsKey(oldHeadersId)) {
+                    item.setHeadersId(oldIdToNewIdMap.get(oldHeadersId));
+                } else if (StringUtil.isEmpty(oldHeadersId) && headersList != null) {
+                    // 如果没有headersId,尝试通过字段名匹配
+                    item.setHeadersId(headersList.stream()
+                            .filter(h -> h.getFieldName().equals(item.getRkey()))
+                            .findFirst()
+                            .map(CostVerifyTemplateHeaders::getId)
+                            .orElse(null));
                 }
                 item.setId(UUID.randomUUID().toString());
                 item.setCreateBy(user.getAccount());
                 item.setCreateTime(LocalDateTime.now());
                 item.setUpdateBy(user.getAccount());
                 item.setUpdateTime(LocalDateTime.now());
-                costSurveyTemplateItemsManager.create(item);
-
                 itemsToSave.add(item);
             }
-
-            // 批量保存
             costSurveyTemplateItemsManager.saveBatch(itemsToSave);
         }
 

+ 1 - 1
pom.xml

@@ -45,7 +45,7 @@
 		<commons-io.version>2.6</commons-io.version>
 		<jsr311-api.version>1.1.1</jsr311-api.version>
 		<poi-ooxml.version>4.1.2</poi-ooxml.version>
-		<fontbox.version>1.8.4</fontbox.version>
+		<fontbox.version>2.0.28</fontbox.version>
 		<poi-scratchpad.version>4.1.2</poi-scratchpad.version>
 		<poi-ooxml-schemas.version>4.1.2</poi-ooxml-schemas.version>
 		<xdocreport.version>2.0.1</xdocreport.version>