Explorar el Código

fix:核定表生成

zzw hace 2 semanas
padre
commit
27ac86c684

+ 6 - 3
assistMg/src/main/java/com/hotent/enterpriseDeclare/controller/material/CostProjectTaskMaterialSummaryController.java

@@ -360,7 +360,7 @@ public class CostProjectTaskMaterialSummaryController extends BaseController<Cos
         }
         resultList.add(catalogResp);
 
-        // 3. 14个资料归纳(每个类别独立显示页码:1-N
+        // 3. 14个资料归纳(页码连续累加
         for (CostProjectTaskMaterialSummary summary : materialList) {
             ArchiveProofreadResp resp = new ArchiveProofreadResp();
             resp.setOrderNum(displayOrderNum++);
@@ -380,9 +380,12 @@ public class CostProjectTaskMaterialSummaryController extends BaseController<Cos
                 }
             }
             resp.setPageCount(pageCount);
-            // 每个资料类别独立显示页码:1-N(不累加)
+            // 页码连续累加:计算在整个卷宗中的起止页码
             if (pageCount > 0) {
-                resp.setPageRange("1-" + pageCount);
+                int startPage = currentPage;
+                int endPage = currentPage + pageCount - 1;
+                resp.setPageRange(startPage + "-" + endPage);
+                currentPage += pageCount;  // 更新页码累加器
             } else {
                 resp.setPageRange("—");
             }

+ 116 - 36
assistMg/src/main/java/com/hotent/project/service/AsposeArchiveService.java

@@ -215,20 +215,26 @@ public class AsposeArchiveService {
      */
     private boolean appendDocument(Document mainDoc, String filePath, boolean isFirst, int sectionStart) throws Exception {
         String lowerPath = filePath.toLowerCase();
-        Document docToAppend;
-
-        if (lowerPath.endsWith(".docx") || lowerPath.endsWith(".doc")) {
-            // Word文档直接加载
-            docToAppend = new Document(filePath);
-        } else if (lowerPath.endsWith(".xlsx") || lowerPath.endsWith(".xls")) {
-            // Excel转Word
-            docToAppend = convertExcelToWord(filePath);
-        } else if (lowerPath.endsWith(".pdf")) {
-            // PDF转Word
-            docToAppend = convertPdfToWord(filePath);
-        } else {
-            logger.warn("   [警告] 不支持的文件格式:{}", filePath);
-            return isFirst;
+        Document docToAppend = null;
+
+        try {
+            if (lowerPath.endsWith(".docx") || lowerPath.endsWith(".doc")) {
+                // Word文档直接加载
+                docToAppend = new Document(filePath);
+            } else if (lowerPath.endsWith(".xlsx") || lowerPath.endsWith(".xls")) {
+                // Excel转Word(内部已有异常处理,会返回错误提示文档)
+                docToAppend = convertExcelToWord(filePath);
+            } else if (lowerPath.endsWith(".pdf")) {
+                // PDF转Word
+                docToAppend = convertPdfToWord(filePath);
+            } else {
+                logger.warn("   [警告] 不支持的文件格式:{}", filePath);
+                return isFirst;
+            }
+        } catch (Exception e) {
+            logger.error("   [错误] 文件转换失败:{},错误:{}", filePath, e.getMessage());
+            // 创建错误提示文档,而不是中断整个流程
+            docToAppend = createErrorDocument("文件转换失败:" + extractFileName(filePath) + "\n错误:" + e.getMessage());
         }
 
         if (docToAppend != null && docToAppend.getSections().getCount() > 0) {
@@ -257,8 +263,31 @@ public class AsposeArchiveService {
      * Excel转Word文档
      */
     private Document convertExcelToWord(String excelPath) throws Exception {
-        // 加载Excel
-        Workbook workbook = new Workbook(excelPath);
+        // 验证文件
+        File excelFile = new File(excelPath);
+        if (!excelFile.exists()) {
+            logger.error("   [错误] Excel文件不存在:{}", excelPath);
+            return createErrorDocument("文件不存在:" + extractFileName(excelPath));
+        }
+
+        if (excelFile.length() == 0) {
+            logger.error("   [错误] Excel文件大小为0:{}", excelPath);
+            return createErrorDocument("文件为空:" + extractFileName(excelPath));
+        }
+
+        logger.info("   Excel文件大小:{} bytes", excelFile.length());
+
+        // 加载Excel(添加异常处理)
+        Workbook workbook;
+        try {
+            workbook = new Workbook(excelPath);
+        } catch (com.aspose.cells.CellsException e) {
+            logger.error("   [错误] Excel文件损坏或格式不支持:{},错误:{}", excelPath, e.getMessage());
+            return createErrorDocument("文件损坏或格式不支持:" + extractFileName(excelPath));
+        } catch (Exception e) {
+            logger.error("   [错误] 加载Excel文件失败:{},错误:{}", excelPath, e.getMessage());
+            return createErrorDocument("文件加载失败:" + extractFileName(excelPath));
+        }
 
         // 获取第一个工作表
         com.aspose.cells.Worksheet worksheet = workbook.getWorksheets().get(0);
@@ -446,16 +475,33 @@ public class AsposeArchiveService {
      * PDF转Word文档
      */
     private Document convertPdfToWord(String pdfPath) throws Exception {
-        // 加载PDF
-        com.aspose.pdf.Document pdfDoc = new com.aspose.pdf.Document(pdfPath);
+        // 验证文件
+        File pdfFile = new File(pdfPath);
+        if (!pdfFile.exists()) {
+            logger.error("   [错误] PDF文件不存在:{}", pdfPath);
+            return createErrorDocument("文件不存在:" + extractFileName(pdfPath));
+        }
 
-        // 转为Word
-        ByteArrayOutputStream wordStream = new ByteArrayOutputStream();
-        pdfDoc.save(wordStream, com.aspose.pdf.SaveFormat.DocX);
+        if (pdfFile.length() == 0) {
+            logger.error("   [错误] PDF文件大小为0:{}", pdfPath);
+            return createErrorDocument("文件为空:" + extractFileName(pdfPath));
+        }
 
-        // 加载Word文档
-        ByteArrayInputStream wordInput = new ByteArrayInputStream(wordStream.toByteArray());
-        return new Document(wordInput);
+        try {
+            // 加载PDF
+            com.aspose.pdf.Document pdfDoc = new com.aspose.pdf.Document(pdfPath);
+
+            // 转为Word
+            ByteArrayOutputStream wordStream = new ByteArrayOutputStream();
+            pdfDoc.save(wordStream, com.aspose.pdf.SaveFormat.DocX);
+
+            // 加载Word文档
+            ByteArrayInputStream wordInput = new ByteArrayInputStream(wordStream.toByteArray());
+            return new Document(wordInput);
+        } catch (Exception e) {
+            logger.error("   [错误] PDF转换失败:{},错误:{}", pdfPath, e.getMessage());
+            return createErrorDocument("PDF转换失败:" + extractFileName(pdfPath));
+        }
     }
 
     /**
@@ -540,7 +586,7 @@ public class AsposeArchiveService {
                     existingHeader.remove();
                 }
 
-                // 计算当前节在全局中的起始页码
+                // 计算当前节在全局中的起始页码(用于计算偏移量)
                 Node currentSectionFirstNode = section.getBody().getFirstChild();
                 int currentGlobalPage = 1;
                 if (currentSectionFirstNode != null) {
@@ -550,12 +596,13 @@ public class AsposeArchiveService {
                 // 计算正文内的相对页码(从1开始)
                 int globalPageNum = currentGlobalPage - contentGlobalStartPage + 1;
 
-                // 关键:只在每个文件的第一个节重置页码为1
-                // 同一文件的其他节不重置,继续递增
-                if (i == fileRange.startSectionIndex) {
+                // 关键:只在正文的第一个节重置页码为1,其他节不重置,保持全局连续
+                if (i == contentFileRanges.get(0).startSectionIndex) {
+                    // 只在正文第一个节重置页码为1
                     section.getPageSetup().setRestartPageNumbering(true);
                     section.getPageSetup().setPageStartingNumber(1);
                 } else {
+                    // 其他所有节都不重置,保持全局连续递增
                     section.getPageSetup().setRestartPageNumbering(false);
                 }
 
@@ -580,14 +627,13 @@ public class AsposeArchiveService {
                 builder.getFont().setSize(12);
                 builder.getFont().setBold(true);
 
-                // 插入全局页码(使用静态文本)
-                // 因为PAGE字段现在是每文件独立的,无法用于全局连续编号
-                String pageNumStr = String.format("%03d", globalPageNum);
-                builder.write(pageNumStr);
+                // 修复:使用动态PAGE字段,配合格式化显示为001、002、003...
+                // 插入PAGE字段,并使用\# "000"格式化为三位数
+                builder.insertField("PAGE \\# \"000\"", "001");
 
                 section.getPageSetup().setHeaderDistance(20);
 
-                // ========== 创建页脚(第 X 页 / 共 Y 页)- 使用动态字段 ==========
+                // ========== 创建页脚(第 X 页 / 共 Y 页)- 每个文件独立计算 ==========
                 HeaderFooter footer = new HeaderFooter(doc, HeaderFooterType.FOOTER_PRIMARY);
                 headerFooters.add(footer);
 
@@ -606,12 +652,15 @@ public class AsposeArchiveService {
                 builder.getFont().setSize(10.5);
                 builder.getFont().setBold(false);
 
+                // 计算当前文件内的相对页码(用于页脚显示)
+                // 获取当前节在当前文件中的相对页码
+                int fileRelativePageNum = currentGlobalPage - globalStartPage + 1;
+
                 // 添加"第 "
                 builder.write("第 ");
 
-                // 页脚使用 PAGE 字段(动态递增)
-                // 因为每个文件第一个节重置为1,所以PAGE字段会自动显示正确的相对页码
-                builder.insertField("PAGE", "1");
+                // 页脚显示当前文件内的相对页码(静态文本)
+                builder.write(String.valueOf(fileRelativePageNum));
 
                 // 添加" 页 / 共 "
                 builder.write(" 页 / 共 ");
@@ -752,4 +801,35 @@ public class AsposeArchiveService {
         int lastSlash = Math.max(url.lastIndexOf('/'), url.lastIndexOf('\\'));
         return lastSlash >= 0 ? url.substring(lastSlash + 1) : url;
     }
+
+    /**
+     * 创建错误提示文档(当文件损坏或无法处理时)
+     */
+    private Document createErrorDocument(String errorMessage) throws Exception {
+        Document doc = new Document();
+        DocumentBuilder builder = new DocumentBuilder(doc);
+
+        // 设置段落格式
+        builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
+        builder.getParagraphFormat().setSpaceAfter(12);
+
+        // 设置字体
+        builder.getFont().setName("宋体");
+        builder.getFont().setSize(14);
+        builder.getFont().setBold(true);
+        builder.getFont().setColor(java.awt.Color.RED);
+
+        // 写入错误信息
+        builder.writeln("【文件处理失败】");
+        builder.getFont().setSize(12);
+        builder.getFont().setBold(false);
+        builder.getFont().setColor(java.awt.Color.BLACK);
+        builder.writeln();
+        builder.writeln(errorMessage);
+        builder.writeln();
+        builder.writeln("请检查原始文件是否完整或格式是否正确。");
+
+        logger.warn("   创建错误提示文档:{}", errorMessage);
+        return doc;
+    }
 }

+ 8 - 2
assistMg/src/main/java/com/hotent/project/service/AsyncMaterialSummaryService.java

@@ -1440,6 +1440,9 @@ public class AsyncMaterialSummaryService {
         // 构建目录数据
         List<CompleteTemplateProcessor.TableRowData> tableDataList = new java.util.ArrayList<>();
 
+        // 页码累加器:用于生成连续的起止页码
+        int currentPage = 1;
+
         for (CostProjectTaskMaterialSummary summary : summaryList) {
             // 获取该资料类别下的所有明细文件
             List<CostProjectTaskMaterialSummaryDetail> detailList = summary.getDetailList();
@@ -1460,9 +1463,12 @@ public class AsyncMaterialSummaryService {
                     int pageCount = detail.getPageCount() != null ? detail.getPageCount() : 0;
                     rowData.setPageCount(pageCount);
 
-                    // 每个文件独立编页码:从1开始
+                    // 连续累加页码:计算在整个卷宗中的起止页码
                     if (pageCount > 0) {
-                        rowData.setRemark("1-" + pageCount);
+                        int startPage = currentPage;
+                        int endPage = currentPage + pageCount - 1;
+                        rowData.setRemark(startPage + "-" + endPage);
+                        currentPage += pageCount;  // 更新页码累加器
                     } else {
                         rowData.setRemark("—");
                     }

+ 210 - 32
assistMg/src/main/java/com/hotent/surveyinfo/controller/CostVerifyTemplateController.java

@@ -76,6 +76,15 @@ public class CostVerifyTemplateController extends BaseController<CostVerifyTempl
 	@Autowired
 	private UserManager userService;
 
+	@Autowired
+	private com.hotent.project.dao.CostProjectTaskDao costProjectTaskDao;
+
+	@Autowired
+	private com.hotent.surveyinfo.dao.CostSurveyTemplateUploadDao costSurveyTemplateUploadDao;
+
+	@Autowired
+	private com.hotent.enterpriseDeclare.dao.CostSurveyTemplateUploadDataDao costSurveyTemplateUploadDataDao;
+
 
 	// 创建通用服务实例
 	private final CrudService crudService = new CrudService();
@@ -173,28 +182,72 @@ public class CostVerifyTemplateController extends BaseController<CostVerifyTempl
 			template.setAreaCode(currentUser.getCountyCode());
 		}
 
+		// 修复:设置 taskId,将新生成的模板绑定到任务(与调查表生成方式保持一致)
+		template.setTaskId(taskId);
+
 		//新增修改后的模板数据
 		costSurveyTemplateDao.insert(template);
 
 		// 查询源模板的数据项和表头
-		List<CostVerifyTemplateItems> itemsList = costVerifyTemplateItemsDao.selectByVerifyTemplateId(templateId,taskId);
+		// 修复:查询源模板数据时不使用 taskId 过滤,确保能查询到历史核定模板的所有项目数据
+		List<CostVerifyTemplateItems> itemsList = costVerifyTemplateItemsDao.selectByVerifyTemplateId(templateId, null);
 		List<CostVerifyTemplateHeaders> headersList = costVerifyTemplateHeadersDao.selectBySurveyTemplateId(templateId);
 
-		// 关键修复:如果在任务中(有 taskId),过滤掉审计期间字段,避免重复生成
+		// 关键修复:如果在任务中(有 taskId),过滤掉审计期间字段,然后根据任务的审计期间重新生成
 		if (taskId != null && headersList != null && !headersList.isEmpty()) {
+			System.out.println("========== 开始过滤年限字段 ==========");
+			System.out.println("原始 headersList 数量: " + headersList.size());
+			System.out.println("原始 itemsList 数量: " + (itemsList != null ? itemsList.size() : 0));
+
+			// 1. 先过滤掉源模板的年限字段headers
+			int originalHeadersCount = headersList.size();
 			headersList = headersList.stream()
 					.filter(header -> {
 						String fieldName = header.getFieldName();
 						if (fieldName == null) {
 							return true; // 保留字段名为空的
 						}
-						// 过滤掉包含审计期间关键词的字段(前端会根据任务的审计期间重新生成)
-						return !fieldName.contains("年账面值")
+						// 过滤掉包含审计期间关键词的字段
+						boolean keep = !fieldName.contains("年账面值")
 								&& !fieldName.contains("年调整值")
 								&& !fieldName.contains("年审核调整值")
 								&& !fieldName.contains("年核定值");
+						if (!keep) {
+							System.out.println("过滤掉年限字段 header: fieldName=" + fieldName);
+						}
+						return keep;
 					})
 					.collect(Collectors.toList());
+			System.out.println("headers 过滤结果: 原始=" + originalHeadersCount + ", 保留=" + headersList.size());
+
+			// 2. 直接根据item的rkey字段过滤items(简单直接)
+			if (itemsList != null && !itemsList.isEmpty()) {
+				int originalItemsCount = itemsList.size();
+				itemsList = itemsList.stream()
+						.filter(item -> {
+							String rkey = item.getRkey();
+							if (rkey == null) {
+								return true; // 保留rkey为空的
+							}
+							// 过滤掉包含年限关键词的items
+							return !rkey.contains("年账面值")
+									&& !rkey.contains("年调整值")
+									&& !rkey.contains("年审核调整值")
+									&& !rkey.contains("年核定值");
+						})
+						.collect(Collectors.toList());
+				System.out.println("items 过滤结果: 原始=" + originalItemsCount + ", 保留=" + itemsList.size());
+			}
+
+			// 3. 根据任务的审计期间生成新的年限字段
+			List<CostVerifyTemplateHeaders> auditPeriodHeaders = generateAuditPeriodHeaders(taskId, tid, headersList);
+			if (auditPeriodHeaders != null && !auditPeriodHeaders.isEmpty()) {
+				headersList.addAll(auditPeriodHeaders);
+				System.out.println("新生成的年限字段数量: " + auditPeriodHeaders.size());
+			}
+			System.out.println("最终 headersList 数量: " + headersList.size());
+			System.out.println("最终 itemsList 数量: " + (itemsList != null ? itemsList.size() : 0));
+			System.out.println("========== 过滤年限字段完成 ==========");
 		}
 
 		// 创建旧ID到新ID的映射表(关键修复:使用Map保存映射关系)
@@ -223,49 +276,147 @@ public class CostVerifyTemplateController extends BaseController<CostVerifyTempl
 
 		// 将itemsList数据依次写入 CostVerifyTemplateItems 表中
 		if (itemsList != null && !itemsList.isEmpty()) {
-			List<CostVerifyTemplateItems> updateitemsList = new ArrayList<>();
-
 			for (CostVerifyTemplateItems items : itemsList) {
 				// 获取旧的 headersId
 				String oldHeadersId = items.getHeadersId();
 
-				// 跳过 headersId 为 null 的情况
-				if (oldHeadersId == null) {
-					System.err.println("警告:item (rowid=" + items.getRowid() + ") 的 headersId 为 null,跳过该 item");
-					continue;
-				}
-
-				// 关键修复:通过Map查找新的 headersId
+				// 通过Map查找新的 headersId
 				String newHeadersId = headerIdMap.get(oldHeadersId);
 
-				// 如果找不到映射,记录警告并跳过该 item
-				if (newHeadersId == null) {
-					System.err.println("警告:item (rowid=" + items.getRowid() +
-							") 的 headersId=" + oldHeadersId + " 在 headersList 中不存在,跳过该 item");
-					continue;
-				}
-
 				// 设置新的 headersId
 				items.setHeadersId(newHeadersId);
-				updateitemsList.add(items);
-			}
 
-			// 批量保存数据项
-			if (!updateitemsList.isEmpty()) {
-				for (CostVerifyTemplateItems items : updateitemsList) {
-					String itemId = UUID.randomUUID().toString();
-					items.setId(itemId);
-					items.setSurveyTemplateId(tid);
-					items.setCreateBy(ContextUtil.getCurrentUser().getAccount());
-					items.setCreateTime( LocalDateTime.now());
-					costVerifyTemplateItemsDao.insert(items);
-				}
+				// 保存数据项
+				String itemId = UUID.randomUUID().toString();
+				items.setId(itemId);
+				items.setSurveyTemplateId(tid);
+				items.setCreateBy(ContextUtil.getCurrentUser().getAccount());
+				items.setCreateTime(LocalDateTime.now());
+				costVerifyTemplateItemsDao.insert(items);
 			}
 		}
 
 		return CommonResult.<CostVerifyTemplate>ok().value(template);
 	}
 
+	/**
+	 * 根据任务的审计期间生成年限字段
+	 * @param taskId 任务ID
+	 * @param surveyTemplateId 模板ID
+	 * @param existingHeaders 已有的表头列表(用于获取基础信息和计算orderNum)
+	 * @return 生成的年限字段列表
+	 */
+	private List<CostVerifyTemplateHeaders> generateAuditPeriodHeaders(String taskId, String surveyTemplateId, List<CostVerifyTemplateHeaders> existingHeaders) {
+		List<CostVerifyTemplateHeaders> auditPeriodHeaders = new ArrayList<>();
+
+		try {
+			// 1. 根据taskId查询任务信息,获取审计期间
+			com.hotent.project.model.CostProjectTask task = costProjectTaskDao.selectById(taskId);
+			if (task == null || task.getAuditPeriod() == null || task.getAuditPeriod().isEmpty()) {
+				System.out.println("任务 " + taskId + " 没有审计期间信息,跳过生成年限字段");
+				return auditPeriodHeaders;
+			}
+
+			String auditPeriod = task.getAuditPeriod(); // 如 "2022,2023,2024"
+			String[] years = auditPeriod.split(",");
+
+			// 2. 获取基础信息(从已有表头中获取)
+			if (existingHeaders == null || existingHeaders.isEmpty()) {
+				System.out.println("没有已有表头,无法生成年限字段");
+				return auditPeriodHeaders;
+			}
+
+			CostVerifyTemplateHeaders baseHeader = existingHeaders.get(0);
+			String tabtype = baseHeader.getTabtype();
+			String versionId = baseHeader.getVersionId();
+
+			// 3. 计算起始orderNum(orderNum是String类型,需要转换)
+			int maxOrderNum = existingHeaders.stream()
+					.mapToInt(h -> {
+						if (h.getOrderNum() == null) return 0;
+						try {
+							return Integer.parseInt(h.getOrderNum());
+						} catch (NumberFormatException e) {
+							return 0;
+						}
+					})
+					.max()
+					.orElse(0);
+
+			int currentOrderNum = maxOrderNum + 1;
+
+			// 4. 为每个年份生成3个字段:账面值、审核调整值、核定值
+			for (String year : years) {
+				year = year.trim();
+
+				// 4.1 账面值字段
+				CostVerifyTemplateHeaders bookValueHeader = new CostVerifyTemplateHeaders();
+				bookValueHeader.setId(UUID.randomUUID().toString());
+				bookValueHeader.setSurveyTemplateId(surveyTemplateId);
+				bookValueHeader.setVersionId(versionId);
+				bookValueHeader.setTabtype(tabtype);
+				bookValueHeader.setFieldEname("year" + year + "BookValue");
+				bookValueHeader.setFieldName(year + "年账面值");
+				bookValueHeader.setFieldType("double");
+				bookValueHeader.setFieldTypelen(255);  // Integer 类型
+				bookValueHeader.setFieldTypenointlen(5);  // Integer 类型
+				bookValueHeader.setIsRequired("true");
+				bookValueHeader.setShowVisible("1");
+				bookValueHeader.setIsDict("false");
+				bookValueHeader.setOrderNum(String.valueOf(currentOrderNum++));  // String 类型
+				bookValueHeader.setCreateBy(ContextUtil.getCurrentUser().getAccount());
+				bookValueHeader.setCreateTime(LocalDateTime.now());
+				auditPeriodHeaders.add(bookValueHeader);
+
+				// 4.2 审核调整值字段
+				CostVerifyTemplateHeaders auditHeader = new CostVerifyTemplateHeaders();
+				auditHeader.setId(UUID.randomUUID().toString());
+				auditHeader.setSurveyTemplateId(surveyTemplateId);
+				auditHeader.setVersionId(versionId);
+				auditHeader.setTabtype(tabtype);
+				auditHeader.setFieldEname("year" + year + "Audit");
+				auditHeader.setFieldName(year + "年审核调整值");
+				auditHeader.setFieldType("double");
+				auditHeader.setFieldTypelen(9);  // Integer 类型
+				auditHeader.setFieldTypenointlen(5);  // Integer 类型
+				auditHeader.setIsRequired("true");
+				auditHeader.setShowVisible("1");
+				auditHeader.setIsDict("false");
+				auditHeader.setOrderNum(String.valueOf(currentOrderNum++));  // String 类型
+				auditHeader.setCreateBy(ContextUtil.getCurrentUser().getAccount());
+				auditHeader.setCreateTime(LocalDateTime.now());
+				auditPeriodHeaders.add(auditHeader);
+
+				// 4.3 核定值字段
+				CostVerifyTemplateHeaders approvedHeader = new CostVerifyTemplateHeaders();
+				approvedHeader.setId(UUID.randomUUID().toString());
+				approvedHeader.setSurveyTemplateId(surveyTemplateId);
+				approvedHeader.setVersionId(versionId);
+				approvedHeader.setTabtype(tabtype);
+				approvedHeader.setFieldEname("year" + year + "ApprovedValue");
+				approvedHeader.setFieldName(year + "年核定值");
+				approvedHeader.setFieldType("double");
+				approvedHeader.setFieldTypelen(255);  // Integer 类型
+				approvedHeader.setFieldTypenointlen(5);  // Integer 类型
+				approvedHeader.setIsRequired("true");
+				approvedHeader.setShowVisible("1");
+				approvedHeader.setIsDict("false");
+				approvedHeader.setOrderNum(String.valueOf(currentOrderNum++));  // String 类型
+				approvedHeader.setCreateBy(ContextUtil.getCurrentUser().getAccount());
+				approvedHeader.setCreateTime(LocalDateTime.now());
+				auditPeriodHeaders.add(approvedHeader);
+			}
+
+			System.out.println("成功生成 " + auditPeriodHeaders.size() + " 个年限字段");
+
+		} catch (Exception e) {
+			System.err.println("生成年限字段失败: " + e.getMessage());
+			e.printStackTrace();
+		}
+
+		return auditPeriodHeaders;
+	}
+
 
 
 	/**
@@ -295,6 +446,33 @@ public class CostVerifyTemplateController extends BaseController<CostVerifyTempl
 				new QueryWrapper<CostVerifyTemplate>().eq("task_id", id).orderByDesc("create_time").last("limit 1")
 		));
 	}
+
+	/**
+	 * 清空核定表数据(保留模板结构)
+	 * @param surveyTemplateId 模板ID
+	 * @param taskId 任务ID
+	 * @return
+	 * @throws Exception
+	 */
+	@PostMapping(value="/clearData")
+	@ApiOperation(value="清空核定表数据", httpMethod = "POST", notes = "清空核定表数据,保留模板结构")
+	public CommonResult<String> clearData(
+			@ApiParam(name="surveyTemplateId", value="模板ID", required = true)
+			@RequestParam String surveyTemplateId,
+			@ApiParam(name="taskId", value="任务ID", required = true)
+			@RequestParam String taskId) throws Exception {
+
+            QueryWrapper<com.hotent.enterpriseDeclare.model.CostSurveyTemplateUploadData> dataQuery =
+                new QueryWrapper<>();
+            dataQuery.eq("task_id", taskId)
+                    .eq("type", "3");
+            costSurveyTemplateUploadDataDao.delete(dataQuery);
+
+			System.out.println("成功清空模板 " + surveyTemplateId + " 在任务 " + taskId + " 下的数据");
+			return CommonResult.<String>ok().message("数据已清空");
+
+	}
+
     /**
 	 * 新增,更新成本核定表模板表
 	 * @param costSurveyTemplate