AsyncMaterialSummaryService.java 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535
  1. package com.hotent.project.service;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.hotent.base.util.StringUtil;
  4. import com.hotent.config.EipConfig;
  5. import com.hotent.project.manager.*;
  6. import com.hotent.project.model.*;
  7. import com.hotent.surveyinfo.manager.CostSurveyTemplateUploadManager;
  8. import com.hotent.surveyinfo.model.CostSurveyTemplateUpload;
  9. import com.hotent.uc.manager.OrgManager;
  10. import com.hotent.uc.manager.UserManager;
  11. import com.hotent.uc.model.Org;
  12. import com.hotent.uc.model.User;
  13. import com.hotent.uc.util.ContextUtil;
  14. import com.hotent.util.FileUploadUtil;
  15. import com.hotent.util.wordexcelutils.CompleteTemplateProcessor;
  16. import com.hotent.util.wordexcelutils.SmartTemplateWriter;
  17. import org.apache.poi.xwpf.usermodel.*;
  18. import org.slf4j.Logger;
  19. import org.slf4j.LoggerFactory;
  20. import org.springframework.beans.factory.annotation.Autowired;
  21. import org.springframework.scheduling.annotation.Async;
  22. import org.springframework.stereotype.Service;
  23. import java.io.FileOutputStream;
  24. import java.time.LocalDateTime;
  25. import java.util.Comparator;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.stream.Collectors;
  29. /**
  30. * 异步资料归纳服务
  31. *
  32. * @company 山西清众科技股份有限公司
  33. * @author 超级管理员
  34. * @since 2025-12-02
  35. */
  36. @Service
  37. public class AsyncMaterialSummaryService {
  38. private static final Logger logger = LoggerFactory.getLogger(AsyncMaterialSummaryService.class);
  39. @Autowired
  40. private CostProjectTaskMaterialSummaryManager costProjectTaskMaterialSummaryManager;
  41. @Autowired
  42. private CostProjectTaskMaterialSummaryDetailManager costProjectTaskMaterialSummaryDetailManager;
  43. @Autowired
  44. private CostProjectDocumentManager costProjectDocumentManager;
  45. @Autowired
  46. private CostProjectTaskManager costProjectTaskManager;
  47. @Autowired
  48. private CostSurveyTemplateUploadManager costSurveyTemplateUploadManager;
  49. @Autowired
  50. private CostProjectTaskMaterialManager costProjectTaskMaterialManager;
  51. @Autowired
  52. private CostProjectDeliberateManager costProjectDeliberateManager;
  53. @Autowired
  54. private CostProjectTaskAdjustmentRecordManager adjustmentRecordManager;
  55. @Autowired
  56. private CostProjectTaskEvidenceManager costProjectTaskEvidenceManager;
  57. @Autowired
  58. private CostProjectTaskPreliminaryOpinionManager preliminaryOpinionManager;
  59. @Autowired
  60. private CostProjectApprovalManager costProjectApprovalManager;
  61. @Autowired
  62. private OrgManager orgManager;
  63. @Autowired
  64. private UserManager userManager;
  65. // 从配置文件读取模板路径
  66. @org.springframework.beans.factory.annotation.Value("${archive.template.coverPath:}")
  67. private String coverTemplatePath;
  68. @org.springframework.beans.factory.annotation.Value("${archive.template.catalogPath:}")
  69. private String catalogTemplatePath;
  70. @org.springframework.beans.factory.annotation.Value("${archive.template.backCoverPath:}")
  71. private String backCoverTemplatePath;
  72. /**
  73. * 生成资料归纳主表(14个基础资料类别)- 核心逻辑
  74. *
  75. * @param mainTask 主任务
  76. * @param childTasks 子任务列表
  77. */
  78. public void generateMaterialSummary(CostProjectTask mainTask, List<CostProjectTask> childTasks) {
  79. try {
  80. // 定义14个基础资料类别
  81. String[] materialNames = {
  82. "成本监审报告(含成本监审报告签发稿、送达回证)",
  83. "被监审单位申请定(调)价报告(复印件)",
  84. "成本监审通知书(含送达回证)",
  85. "经营者需提供成本资料清单",
  86. "《政府定价成本监审调查表》",
  87. "成本监审补充资料通知书(含送达回证)",
  88. "成本审核初步意见告知书(含送达回证)",
  89. "经营者书面反馈的材料",
  90. "成本审核初步意见表(集体审议用)",
  91. "成本监审集体审议记录",
  92. "成本监审工作底稿",
  93. "成本监审提取资料登记表",
  94. "提取的成本资料和会计凭证等复印件",
  95. "中止定价成本监审料通知书(含送达回证)"
  96. };
  97. // 为主任务生成14个资料归纳主表记录
  98. for (int i = 0; i < materialNames.length; i++) {
  99. CostProjectTaskMaterialSummary summary = new CostProjectTaskMaterialSummary();
  100. summary.setTaskId(mainTask.getId());
  101. summary.setMaterialName(materialNames[i]);
  102. summary.setMaterialOrderNum(i + 1);
  103. summary.setIsDeleted("0");
  104. costProjectTaskMaterialSummaryManager.createOrUpdate(summary);
  105. // 根据不同的资料类别生成对应的明细记录
  106. generateDetailsByMaterialType(summary, mainTask, childTasks, i + 1);
  107. }
  108. } catch (Exception e) {
  109. // 记录异常日志,但不影响主流程
  110. logger.error("生成资料归纳失败", e);
  111. }
  112. }
  113. /**
  114. * 根据资料类别生成对应的明细记录
  115. *
  116. * @param summary 资料归纳主表
  117. * @param mainTask 主任务
  118. * @param childTasks 子任务列表
  119. * @param materialType 资料类别序号(1-13)
  120. */
  121. private void generateDetailsByMaterialType(CostProjectTaskMaterialSummary summary,
  122. CostProjectTask mainTask,
  123. List<CostProjectTask> childTasks,
  124. int materialType) {
  125. try {
  126. // 查询项目下的所有文书
  127. List<CostProjectDocument> documents = costProjectDocumentManager.getListByProjectId(mainTask.getProjectId());
  128. if (documents == null) {
  129. documents = new java.util.ArrayList<>();
  130. }
  131. switch (materialType) {
  132. case 1:
  133. // 成本监审报告(含成本监审报告签发稿、送达回证)
  134. generateType1Details(summary, mainTask, documents);
  135. break;
  136. case 2:
  137. // 被监审单位申请定(调)价报告(复印件)
  138. generateType2Details(summary, mainTask, childTasks, documents);
  139. break;
  140. case 3:
  141. // 成本监审通知书(含送达回证)
  142. generateType3Details(summary, mainTask, childTasks, documents);
  143. break;
  144. case 4:
  145. // 经营者需提供成本资料清单
  146. generateType4Details(summary, mainTask, childTasks, documents);
  147. break;
  148. case 5:
  149. // 《政府定价成本监审调查表》
  150. generateType5Details(summary, mainTask, childTasks, documents);
  151. break;
  152. case 6:
  153. // 成本监审补充资料通知书(含送达回证)
  154. generateType6Details(summary, mainTask, childTasks, documents);
  155. break;
  156. case 7:
  157. // 成本审核初步意见告知书(含送达回证)
  158. generateType7Details(summary, mainTask, childTasks, documents);
  159. break;
  160. case 8:
  161. // 经营者书面反馈的材料
  162. generateType8Details(summary, mainTask, childTasks, documents);
  163. break;
  164. case 9:
  165. // 成本审核初步意见表(集体审议用)
  166. generateType9Details(summary, mainTask, childTasks, documents);
  167. break;
  168. case 10:
  169. // 成本监审集体审议记录
  170. generateType10Details(summary, mainTask, documents);
  171. break;
  172. case 11:
  173. // 成本监审工作底稿
  174. generateType11Details(summary, mainTask, childTasks, documents);
  175. break;
  176. case 12:
  177. // 成本监审提取资料登记表
  178. generateType12Details(summary, mainTask, childTasks, documents);
  179. break;
  180. case 13:
  181. // 提取的成本资料和会计凭证等复印件
  182. generateType13Details(summary, mainTask, childTasks, documents);
  183. break;
  184. case 14:
  185. // 中止定价成本监审料通知书(含送达回证)
  186. generateType14Details(summary, mainTask, childTasks, documents);
  187. break;
  188. default:
  189. throw new RuntimeException("未知的资料类型");
  190. }
  191. } catch (Exception e) {
  192. logger.error("生成资料类型 {} 的明细时出错:{}", materialType, e.getMessage(), e);
  193. // 继续执行,不中断整个流程
  194. }
  195. }
  196. //1 卷宗封面
  197. //2 卷内目录
  198. //3 政府定价成本监审结论报告
  199. //4 成本监审通知书
  200. //5 成本审核初步意见告知书
  201. //6 成本审核初步意见表
  202. //7 成本监审集体审议记录
  203. //8 成本监审工作底稿
  204. //9 成本监审提取资料登记表
  205. //10 成本监审补充资料通知书
  206. //11 中止定价成本监审通知书
  207. //12 送达回证
  208. //13 成本监审卷宗备考表
  209. // ==================== 类型1:成本监审报告(含成本监审报告签发稿、送达回证) ====================
  210. // A.成本监审报告(含成本监审报告签发稿)
  211. // 包括成本监审报告、报告签发稿等文书。
  212. private void generateType1Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectDocument> documents) {
  213. int orderNum = 1;
  214. // 获取"成本监审报告"、"成本监审报告签发稿"、"送达回证"
  215. List<CostProjectDocument> matchedDocuments = documents.stream()
  216. .filter(doc -> "3".equals(doc.getDocumentType())
  217. || "3-12".equals(doc.getDocumentType()+"-"+doc.getDocumentTypeName())
  218. )
  219. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  220. .collect(Collectors.toList());
  221. for (CostProjectDocument document : matchedDocuments) {
  222. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  223. detail.setMasterId(summary.getId());
  224. detail.setTaskId(mainTask.getId());
  225. detail.setDocumentName(document.getDocumentName());
  226. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  227. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  228. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  229. detail.setFileSource("系统生成电子文书");
  230. detail.setPageCount(0);
  231. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  232. detail.setOrderNum(orderNum++);
  233. detail.setIsDeleted("0");
  234. costProjectTaskMaterialSummaryDetailManager.save(detail);
  235. }
  236. }
  237. // ==================== 类型2:被监审单位申请定(调)价报告(复印件) ====================
  238. // 被监审单位申请定(调)价报告等。
  239. //来源:从“监审立项”附件获取。
  240. private void generateType2Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  241. int orderNum = 1;
  242. // 获取"申请定(调)价报告"
  243. List<CostProjectDocument> matchedDocuments = documents.stream()
  244. .filter(doc -> "申请定(调)价报告".equals(doc.getDocumentName()))
  245. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  246. .collect(Collectors.toList());
  247. for (CostProjectDocument document : matchedDocuments) {
  248. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  249. detail.setMasterId(summary.getId());
  250. detail.setTaskId(mainTask.getId());
  251. detail.setDocumentName(document.getDocumentName());
  252. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  253. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  254. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  255. detail.setFileSource("监审主体上传文件");
  256. detail.setPageCount(0);
  257. detail.setAttachmentUrl(document.getScanDocumentUrl() != null ? document.getScanDocumentUrl() : "");
  258. detail.setOrderNum(orderNum++);
  259. detail.setIsDeleted("0");
  260. costProjectTaskMaterialSummaryDetailManager.save(detail);
  261. }
  262. CostProjectApproval approval = costProjectApprovalManager.getById(mainTask.getProjectId());
  263. if (approval != null && StringUtil.isNotEmpty(approval.getAccordingFileUrl())) {
  264. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  265. detail.setMasterId(summary.getId());
  266. detail.setTaskId(mainTask.getId());
  267. detail.setDocumentName("成本项目定(调)价依据");
  268. detail.setDocumentNumber("");
  269. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  270. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  271. detail.setFileSource("系统生成电子文书");
  272. detail.setPageCount(0);
  273. detail.setAttachmentUrl(approval.getAccordingFileUrl());
  274. detail.setOrderNum(orderNum++);
  275. detail.setIsDeleted("0");
  276. costProjectTaskMaterialSummaryDetailManager.save(detail);
  277. }
  278. }
  279. // ==================== 类型3:成本监审通知书(含送达回证) ====================
  280. // 包括成本监审通知书、送达回证等文书。
  281. //来源:从“监审文书”中获取。
  282. private void generateType3Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  283. int orderNum = 1;
  284. // "成本监审通知书"和"送达回证"
  285. List<CostProjectDocument> matchedDocuments = documents.stream()
  286. .filter(doc -> "4".equals(doc.getDocumentType())
  287. || "4-12".equals(doc.getDocumentType()+"-"+doc.getDocumentTypeName()))
  288. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  289. .collect(Collectors.toList());
  290. for (CostProjectDocument document : matchedDocuments) {
  291. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  292. detail.setMasterId(summary.getId());
  293. detail.setTaskId(mainTask.getId());
  294. detail.setDocumentName(document.getDocumentName());
  295. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  296. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  297. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  298. detail.setFileSource("系统生成电子文书");
  299. detail.setPageCount(0);
  300. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  301. detail.setOrderNum(orderNum++);
  302. detail.setIsDeleted("0");
  303. costProjectTaskMaterialSummaryDetailManager.save(detail);
  304. }
  305. }
  306. // ==================== 类型4:经营者需提供成本资料清单(提取资料) ====================
  307. // 成本监审任务中的资料报送要求。
  308. //来源:从“监审任务部署”中获取。
  309. private void generateType4Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  310. int orderNum = 1;
  311. // 获取"成本资料清单"
  312. List<CostProjectDocument> matchedDocuments = documents.stream()
  313. .filter(doc -> "9".equals(doc.getDocumentType()))
  314. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  315. .collect(Collectors.toList());
  316. for (CostProjectDocument document : matchedDocuments) {
  317. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  318. detail.setMasterId(summary.getId());
  319. detail.setTaskId(mainTask.getId());
  320. detail.setDocumentName(document.getDocumentName());
  321. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  322. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  323. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  324. detail.setFileSource("系统生成电子文书");
  325. detail.setPageCount(0);
  326. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  327. detail.setOrderNum(orderNum++);
  328. detail.setIsDeleted("0");
  329. costProjectTaskMaterialSummaryDetailManager.save(detail);
  330. }
  331. for (CostProjectTask childTask : childTasks) {
  332. List<CostProjectTaskEvidence> list = costProjectTaskEvidenceManager.list(
  333. new LambdaQueryWrapper<CostProjectTaskEvidence>().eq(CostProjectTaskEvidence::getTaskId, childTask.getId())
  334. );
  335. for (CostProjectTaskEvidence evidence : list) {
  336. // 只处理有附件的资料登记
  337. if (StringUtil.isNotEmpty(evidence.getAttachmentUrl())) {
  338. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  339. detail.setMasterId(summary.getId());
  340. detail.setTaskId(mainTask.getId());
  341. detail.setDocumentName(evidence.getMaterialName() != null ? evidence.getMaterialName() : "提取资料登记");
  342. detail.setDocumentNumber("");
  343. detail.setAuditedUnitId(childTask.getAuditedUnitId());
  344. detail.setAuditedUnitName(childTask.getAuditedUnitName() != null ? childTask.getAuditedUnitName() : "");
  345. detail.setFileSource("系统生成电子文书");
  346. detail.setPageCount(evidence.getPageCount() != null ? evidence.getPageCount() : 0);
  347. detail.setAttachmentUrl(evidence.getAttachmentUrl());
  348. detail.setOrderNum(orderNum++);
  349. detail.setIsDeleted("0");
  350. costProjectTaskMaterialSummaryDetailManager.save(detail);
  351. }
  352. }
  353. }
  354. }
  355. // ==================== 类型5:《政府定价成本监审调查表》 ====================
  356. // 各被监审单位的“成本监审调查表”文档。
  357. //来源:从“监审任务”中获取各被监审单位填报的成本监审调查表。
  358. private void generateType5Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  359. int orderNum = 1;
  360. for (CostProjectTask childTask : childTasks) {
  361. List<CostSurveyTemplateUpload> costSurveyTemplateUploads = costSurveyTemplateUploadManager.listByTaskId(childTask.getId());
  362. for (CostSurveyTemplateUpload upload : costSurveyTemplateUploads) {
  363. if ("1".equals(upload.getIsUpload()) && StringUtil.isNotEmpty(upload.getFileUrl())) {
  364. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  365. detail.setMasterId(summary.getId());
  366. detail.setTaskId(mainTask.getId());
  367. detail.setDocumentName(upload.getSurveyTemplateName() != null ? upload.getSurveyTemplateName() : "政府定价成本监审调查表");
  368. detail.setDocumentNumber("");
  369. detail.setAuditedUnitId(childTask.getAuditedUnitId());
  370. detail.setAuditedUnitName(childTask.getAuditedUnitName() != null ? childTask.getAuditedUnitName() : "");
  371. detail.setFileSource("监审单位反馈文件");
  372. detail.setPageCount(0);
  373. detail.setAttachmentUrl(upload.getFileUrl());
  374. detail.setOrderNum(orderNum++);
  375. detail.setIsDeleted("0");
  376. costProjectTaskMaterialSummaryDetailManager.save(detail);
  377. }
  378. }
  379. }
  380. // 成本调查表(kkkk)
  381. }
  382. // ==================== 类型6:成本监审补充资料通知书(含送达回证) ====================
  383. // 监审主体发送给各被监审单位的补充资料通知书、送达回证等文书。
  384. //来源:从“监审文书”中获取。
  385. private void generateType6Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  386. int orderNum = 1;
  387. List<CostProjectDocument> matchedDocuments = documents.stream()
  388. .filter(doc -> "10".equals(doc.getDocumentType())
  389. || "10-12".equals(doc.getDocumentType()+"-"+doc.getDocumentTypeName()))
  390. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  391. .collect(Collectors.toList());
  392. for (CostProjectDocument document : matchedDocuments) {
  393. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  394. detail.setMasterId(summary.getId());
  395. detail.setTaskId(mainTask.getId());
  396. detail.setDocumentName(document.getDocumentName());
  397. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  398. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  399. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  400. detail.setFileSource("系统生成电子文书");
  401. detail.setPageCount(0);
  402. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  403. detail.setOrderNum(orderNum++);
  404. detail.setIsDeleted("0");
  405. costProjectTaskMaterialSummaryDetailManager.save(detail);
  406. }
  407. }
  408. // ==================== 类型7:成本审核初步意见告知书(含送达回证) ====================
  409. // 监审主体发送给各被监审单位的初步意见告知书、送达回证等文书。
  410. // 来源:从“监审文书”中获取。
  411. private void generateType7Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  412. int orderNum = 1;
  413. // 获取"成本审核初步意见告知书"和"送达回证"
  414. List<CostProjectDocument> matchedDocuments = documents.stream()
  415. .filter(doc -> "5".equals(doc.getDocumentType())
  416. || "5-12".equals(doc.getDocumentType()+"-"+doc.getDocumentTypeName()))
  417. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  418. .collect(Collectors.toList());
  419. for (CostProjectDocument document : matchedDocuments) {
  420. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  421. detail.setMasterId(summary.getId());
  422. detail.setTaskId(mainTask.getId());
  423. detail.setDocumentName(document.getDocumentName());
  424. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  425. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  426. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  427. detail.setFileSource("系统生成电子文书");
  428. detail.setPageCount(0);
  429. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  430. detail.setOrderNum(orderNum++);
  431. detail.setIsDeleted("0");
  432. costProjectTaskMaterialSummaryDetailManager.save(detail);
  433. }
  434. }
  435. // ==================== 类型8:经营者书面反馈的材料 ====================
  436. // 各被监审单位对初步意见告知书的意见反馈资料。
  437. //来源:从“监审任务”中获取反馈意见。
  438. private void generateType8Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  439. int orderNum = 1;
  440. // 获取"书面反馈材料"(来源:监审单位反馈文件)
  441. List<CostProjectDocument> matchedDocuments = documents.stream()
  442. .filter(doc -> "书面反馈材料".equals(doc.getDocumentType()))
  443. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  444. .collect(Collectors.toList());
  445. for (CostProjectDocument document : matchedDocuments) {
  446. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  447. detail.setMasterId(summary.getId());
  448. detail.setTaskId(mainTask.getId());
  449. detail.setDocumentName(document.getDocumentName());
  450. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  451. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  452. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  453. detail.setFileSource("监审单位反馈文件");
  454. detail.setPageCount(0);
  455. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  456. detail.setOrderNum(orderNum++);
  457. detail.setIsDeleted("0");
  458. costProjectTaskMaterialSummaryDetailManager.save(detail);
  459. }
  460. // 获取被监审单位的反馈意见和反馈资料
  461. for (CostProjectTask childTask : childTasks) {
  462. List<CostProjectTaskPreliminaryOpinion> list = preliminaryOpinionManager.list(
  463. new LambdaQueryWrapper<>(CostProjectTaskPreliminaryOpinion.class)
  464. .eq(CostProjectTaskPreliminaryOpinion::getTaskId, childTask.getId())
  465. );
  466. for (CostProjectTaskPreliminaryOpinion opinion : list) {
  467. // 只处理有反馈资料的记录
  468. if (StringUtil.isNotEmpty(opinion.getFeedbackMaterials())) {
  469. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  470. detail.setMasterId(summary.getId());
  471. detail.setTaskId(mainTask.getId());
  472. detail.setDocumentName("书面反馈材料");
  473. detail.setDocumentNumber("");
  474. detail.setAuditedUnitId(childTask.getAuditedUnitId());
  475. detail.setAuditedUnitName(childTask.getAuditedUnitName() != null ? childTask.getAuditedUnitName() : "");
  476. detail.setFileSource("监审单位反馈文件");
  477. detail.setPageCount(0);
  478. detail.setAttachmentUrl(opinion.getFeedbackMaterials());
  479. detail.setOrderNum(orderNum++);
  480. detail.setIsDeleted("0");
  481. costProjectTaskMaterialSummaryDetailManager.save(detail);
  482. }
  483. }
  484. }
  485. }
  486. // ==================== 类型9:成本审核初步意见表(集体审议用) ====================
  487. // 来源:从“监审文书”中获取
  488. private void generateType9Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  489. int orderNum = 1;
  490. // 获取"成本审核初步意见表"
  491. List<CostProjectDocument> matchedDocuments = documents.stream()
  492. .filter(doc -> "6".equals(doc.getDocumentType()))
  493. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  494. .collect(Collectors.toList());
  495. for (CostProjectDocument document : matchedDocuments) {
  496. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  497. detail.setMasterId(summary.getId());
  498. detail.setTaskId(mainTask.getId());
  499. detail.setDocumentName(document.getDocumentName());
  500. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  501. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  502. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  503. detail.setFileSource("系统生成电子文书");
  504. detail.setPageCount(0);
  505. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  506. detail.setOrderNum(orderNum++);
  507. detail.setIsDeleted("0");
  508. costProjectTaskMaterialSummaryDetailManager.save(detail);
  509. }
  510. }
  511. // ==================== 类型10:成本监审集体审议记录 ====================
  512. // 来源:从“监审文书”中获取。
  513. private void generateType10Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectDocument> documents) {
  514. int orderNum = 1;
  515. // 获取"成本监审集体审议记录"
  516. List<CostProjectDocument> matchedDocuments = documents.stream()
  517. .filter(doc -> "7".equals(doc.getDocumentType()))
  518. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  519. .collect(Collectors.toList());
  520. for (CostProjectDocument document : matchedDocuments) {
  521. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  522. detail.setMasterId(summary.getId());
  523. detail.setTaskId(mainTask.getId());
  524. detail.setDocumentName(document.getDocumentName());
  525. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  526. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  527. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  528. detail.setFileSource("系统生成电子文书");
  529. detail.setPageCount(0);
  530. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  531. detail.setOrderNum(orderNum++);
  532. detail.setIsDeleted("0");
  533. costProjectTaskMaterialSummaryDetailManager.save(detail);
  534. }
  535. // 获取集体审议记录
  536. List<CostProjectDeliberate> list = costProjectDeliberateManager.list(
  537. new LambdaQueryWrapper<CostProjectDeliberate>().eq(CostProjectDeliberate::getTaskId, mainTask.getId())
  538. );
  539. for (CostProjectDeliberate deliberate : list) {
  540. // 只处理有附件的审议记录
  541. if (StringUtil.isNotEmpty(deliberate.getAttachmentUrl())) {
  542. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  543. detail.setMasterId(summary.getId());
  544. detail.setTaskId(mainTask.getId());
  545. detail.setDocumentName("成本监审集体审议记录");
  546. detail.setDocumentNumber("");
  547. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  548. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  549. detail.setFileSource("系统生成电子文书");
  550. detail.setPageCount(0);
  551. detail.setAttachmentUrl(deliberate.getAttachmentUrl());
  552. detail.setOrderNum(orderNum++);
  553. detail.setIsDeleted("0");
  554. costProjectTaskMaterialSummaryDetailManager.save(detail);
  555. }
  556. }
  557. }
  558. // ==================== 类型11:成本监审工作底稿 ====================
  559. // 来源:从“监审文书”中获取。
  560. private void generateType11Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  561. int orderNum = 1;
  562. // 获取"成本监审工作底稿"
  563. List<CostProjectDocument> matchedDocuments = documents.stream()
  564. .filter(doc -> "8".equals(doc.getDocumentType()))
  565. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  566. .collect(Collectors.toList());
  567. for (CostProjectDocument document : matchedDocuments) {
  568. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  569. detail.setMasterId(summary.getId());
  570. detail.setTaskId(mainTask.getId());
  571. detail.setDocumentName(document.getDocumentName());
  572. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  573. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  574. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  575. detail.setFileSource("系统生成电子文书");
  576. detail.setPageCount(0);
  577. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  578. detail.setOrderNum(orderNum++);
  579. detail.setIsDeleted("0");
  580. costProjectTaskMaterialSummaryDetailManager.save(detail);
  581. }
  582. // 获取核增核减记录(工作底稿的一部分)
  583. for (CostProjectTask childTask : childTasks) {
  584. List<CostProjectTaskAdjustmentRecord> costProjectTaskAdjustmentRecords = adjustmentRecordManager.listByTaskId(childTask.getId());
  585. for (CostProjectTaskAdjustmentRecord record : costProjectTaskAdjustmentRecords) {
  586. // 只处理有附件的记录
  587. if (StringUtil.isNotEmpty(record.getAttachmentUrl())) {
  588. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  589. detail.setMasterId(summary.getId());
  590. detail.setTaskId(mainTask.getId());
  591. detail.setDocumentName(record.getSubject() != null ? record.getSubject() + "-工作底稿" : "成本监审工作底稿");
  592. detail.setDocumentNumber("");
  593. detail.setAuditedUnitId(childTask.getAuditedUnitId());
  594. detail.setAuditedUnitName(childTask.getAuditedUnitName() != null ? childTask.getAuditedUnitName() : "");
  595. detail.setFileSource("系统生成电子文书");
  596. detail.setPageCount(0);
  597. detail.setAttachmentUrl(record.getAttachmentUrl());
  598. detail.setOrderNum(orderNum++);
  599. detail.setIsDeleted("0");
  600. costProjectTaskMaterialSummaryDetailManager.save(detail);
  601. }
  602. }
  603. }
  604. }
  605. // ==================== 类型12:成本监审提取资料登记表 ====================
  606. // 来源:从“监审文书”中获取。
  607. private void generateType12Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  608. int orderNum = 1;
  609. // 获取"成本监审提取资料登记表"
  610. List<CostProjectDocument> matchedDocuments = documents.stream()
  611. .filter(doc -> "9".equals(doc.getDocumentType()))
  612. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  613. .collect(Collectors.toList());
  614. for (CostProjectDocument document : matchedDocuments) {
  615. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  616. detail.setMasterId(summary.getId());
  617. detail.setTaskId(mainTask.getId());
  618. detail.setDocumentName(document.getDocumentName());
  619. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  620. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  621. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  622. detail.setFileSource("系统生成电子文书");
  623. detail.setPageCount(0);
  624. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  625. detail.setOrderNum(orderNum++);
  626. detail.setIsDeleted("0");
  627. costProjectTaskMaterialSummaryDetailManager.save(detail);
  628. }
  629. // 获取子任务的资料登记表
  630. for (CostProjectTask childTask : childTasks) {
  631. List<CostProjectTaskEvidence> list = costProjectTaskEvidenceManager.list(
  632. new LambdaQueryWrapper<CostProjectTaskEvidence>().eq(CostProjectTaskEvidence::getTaskId, childTask.getId())
  633. );
  634. for (CostProjectTaskEvidence evidence : list) {
  635. // 只处理有附件的资料登记
  636. if (StringUtil.isNotEmpty(evidence.getAttachmentUrl())) {
  637. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  638. detail.setMasterId(summary.getId());
  639. detail.setTaskId(mainTask.getId());
  640. detail.setDocumentName(evidence.getMaterialName() != null ? evidence.getMaterialName() : "提取资料登记");
  641. detail.setDocumentNumber("");
  642. detail.setAuditedUnitId(childTask.getAuditedUnitId());
  643. detail.setAuditedUnitName(childTask.getAuditedUnitName() != null ? childTask.getAuditedUnitName() : "");
  644. detail.setFileSource("系统生成电子文书");
  645. detail.setPageCount(evidence.getPageCount() != null ? evidence.getPageCount() : 0);
  646. detail.setAttachmentUrl(evidence.getAttachmentUrl());
  647. detail.setOrderNum(orderNum++);
  648. detail.setIsDeleted("0");
  649. costProjectTaskMaterialSummaryDetailManager.save(detail);
  650. }
  651. }
  652. }
  653. }
  654. // ==================== 类型13:提取的成本资料和会计凭证等复印件 ====================
  655. // 来源:各被监审单位首次及补充材料时提交的资料,包括综合性材料、财会资料等。
  656. private void generateType13Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  657. int orderNum = 1;
  658. // 获取"成本资料和会计凭证"(来源:被监审单位提交的资料)
  659. List<CostProjectDocument> matchedDocuments = documents.stream()
  660. .filter(doc -> "成本资料和会计凭证".equals(doc.getDocumentType()))
  661. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  662. .collect(Collectors.toList());
  663. for (CostProjectDocument document : matchedDocuments) {
  664. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  665. detail.setMasterId(summary.getId());
  666. detail.setTaskId(mainTask.getId());
  667. detail.setDocumentName(document.getDocumentName());
  668. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  669. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  670. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  671. detail.setFileSource("监审单位反馈文件");
  672. detail.setPageCount(0);
  673. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  674. detail.setOrderNum(orderNum++);
  675. detail.setIsDeleted("0");
  676. costProjectTaskMaterialSummaryDetailManager.save(detail);
  677. }
  678. // 获取子任务的资料登记表
  679. for (CostProjectTask childTask : childTasks) {
  680. // 获取任务的报送资料要求(被监审单位提交的补充资料)
  681. List<CostProjectTaskMaterial> list = costProjectTaskMaterialManager.list(
  682. new LambdaQueryWrapper<>(CostProjectTaskMaterial.class)
  683. .eq(CostProjectTaskMaterial::getTaskId, childTask.getId())
  684. );
  685. for (CostProjectTaskMaterial material : list) {
  686. // 只处理已上传的资料
  687. if ("1".equals(material.getIsUpload()) && StringUtil.isNotEmpty(material.getFileUrl())) {
  688. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  689. detail.setMasterId(summary.getId());
  690. detail.setTaskId(mainTask.getId());
  691. detail.setDocumentName(material.getInformationName() != null ? material.getInformationName() : "补充资料");
  692. detail.setDocumentNumber("");
  693. detail.setAuditedUnitId(childTask.getAuditedUnitId());
  694. detail.setAuditedUnitName(childTask.getAuditedUnitName() != null ? childTask.getAuditedUnitName() : "");
  695. detail.setFileSource("监审单位反馈文件");
  696. detail.setPageCount(0);
  697. detail.setAttachmentUrl(material.getFileUrl());
  698. detail.setOrderNum(orderNum++);
  699. detail.setIsDeleted("0");
  700. costProjectTaskMaterialSummaryDetailManager.save(detail);
  701. }
  702. }
  703. }
  704. }
  705. // ==================== 类型14:中止定价成本监审料通知书(含送达回证) ====================
  706. // 来源:从“监审文书”中获取。
  707. private void generateType14Details(CostProjectTaskMaterialSummary summary, CostProjectTask mainTask, List<CostProjectTask> childTasks, List<CostProjectDocument> documents) {
  708. int orderNum = 1;
  709. // 获取"中止定价成本监审通知书"(来源:被监审单位提交的资料)
  710. List<CostProjectDocument> matchedDocuments = documents.stream()
  711. .filter(doc -> "11".equals(doc.getDocumentType())
  712. || "11-12".equals(doc.getDocumentType()+"-"+doc.getDocumentTypeName()))
  713. .sorted(Comparator.comparing(doc -> doc.getOrderNum() != null ? doc.getOrderNum() : 0))
  714. .collect(Collectors.toList());
  715. for (CostProjectDocument document : matchedDocuments) {
  716. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  717. detail.setMasterId(summary.getId());
  718. detail.setTaskId(mainTask.getId());
  719. detail.setDocumentName(document.getDocumentName());
  720. detail.setDocumentNumber(document.getDocumentNumber() != null ? document.getDocumentNumber() : "");
  721. detail.setAuditedUnitId(mainTask.getAuditedUnitId());
  722. detail.setAuditedUnitName(mainTask.getAuditedUnitName() != null ? mainTask.getAuditedUnitName() : "");
  723. detail.setFileSource("监审单位反馈文件");
  724. detail.setPageCount(0);
  725. detail.setAttachmentUrl(document.getActUrl() != null ? document.getActUrl() : "");
  726. detail.setOrderNum(orderNum++);
  727. detail.setIsDeleted("0");
  728. costProjectTaskMaterialSummaryDetailManager.save(detail);
  729. }
  730. }
  731. /**
  732. * 档案校对通过后,异步生成Word卷宗
  733. * 顺序:封面(-1) -> 目录(-2) -> 14个资料(1-13) -> 封底(-3)
  734. *
  735. * @param taskId 任务ID
  736. */
  737. @Async
  738. public void generateWordArchiveAsync(String taskId) {
  739. CostProjectTask task = costProjectTaskManager.getById(taskId);
  740. try {
  741. logger.info("开始生成Word卷宗,任务ID:{}", taskId);
  742. // 查询任务的所有资料归纳主表(包含封面、目录、封底)
  743. List<CostProjectTaskMaterialSummary> allSummaryList =
  744. costProjectTaskMaterialSummaryManager.listAllByTaskId(taskId);
  745. // 创建Word文档
  746. XWPFDocument document = new XWPFDocument();
  747. boolean isFirstDocument = true;
  748. // 按顺序合并:封面(-1) -> 目录(-2) -> 14个资料(1-13) -> 封底(-3)
  749. // 排序:-1, -2, 1, 2, ..., 13, -3
  750. allSummaryList.sort((a, b) -> {
  751. int orderA = a.getMaterialOrderNum() != null ? a.getMaterialOrderNum() : 0;
  752. int orderB = b.getMaterialOrderNum() != null ? b.getMaterialOrderNum() : 0;
  753. // -1(封面) < -2(目录) < 1-13(资料) < -3(封底)
  754. int sortA = orderA == -1 ? -100 : (orderA == -2 ? -99 : (orderA == -3 ? 100 : orderA));
  755. int sortB = orderB == -1 ? -100 : (orderB == -2 ? -99 : (orderB == -3 ? 100 : orderB));
  756. return Integer.compare(sortA, sortB);
  757. });
  758. // 遍历所有主表,从明细表获取附件URL进行合并
  759. for (CostProjectTaskMaterialSummary summary : allSummaryList) {
  760. logger.info("合并资料:{}", summary.getMaterialName());
  761. List<CostProjectTaskMaterialSummaryDetail> detailList = summary.getDetailList();
  762. if (detailList != null && !detailList.isEmpty()) {
  763. detailList.sort(Comparator.comparing(d -> d.getOrderNum() != null ? d.getOrderNum() : 0));
  764. for (CostProjectTaskMaterialSummaryDetail detail : detailList) {
  765. if (StringUtil.isNotEmpty(detail.getAttachmentUrl())) {
  766. isFirstDocument = mergeDocumentFile(document, detail.getAttachmentUrl(), isFirstDocument);
  767. }
  768. }
  769. }
  770. }
  771. // 生成输出文件路径
  772. String fileName = FileUploadUtil.generateFileName("卷宗_" + taskId + ".docx");
  773. String outputPath = FileUploadUtil.generateSavePath(
  774. EipConfig.getUploadPath(), fileName, "docx");
  775. // 确保目录存在
  776. java.io.File outputFile = new java.io.File(outputPath);
  777. if (!outputFile.getParentFile().exists()) {
  778. outputFile.getParentFile().mkdirs();
  779. }
  780. // 保存Word文档
  781. FileOutputStream out = new FileOutputStream(outputPath);
  782. document.write(out);
  783. out.close();
  784. document.close();
  785. // 生成访问URL
  786. String archiveUrl = FileUploadUtil.getPathFileName(outputPath, fileName);
  787. logger.info("Word卷宗生成完成,任务ID:{},URL:{}", taskId, archiveUrl);
  788. // 更新任务的卷宗URL
  789. if (task != null) {
  790. task.setArchiveUrl(archiveUrl);
  791. task.setArchiveStatus("2");
  792. task.setArchiveTime(LocalDateTime.now());
  793. costProjectTaskManager.updateById(task);
  794. logger.info("卷宗URL已更新,任务ID:{}", taskId);
  795. }
  796. } catch (Exception e) {
  797. if (task != null) {
  798. task.setArchiveStatus("3");
  799. costProjectTaskManager.updateById(task);
  800. }
  801. logger.error("生成Word卷宗失败,任务ID:,错误:{}", taskId, e.getMessage(), e);
  802. }
  803. }
  804. /**
  805. * 合并单个文档文件到主文档(保持原始样式)
  806. *
  807. * @param document 主文档
  808. * @param fileUrl 文件URL(如:/profile/upload/20251116/xxx.docx)
  809. * @param isFirstDocument 是否是第一个文档
  810. * @return 是否是第一个文档(用于下次调用)
  811. */
  812. private boolean mergeDocumentFile(XWPFDocument document, String fileUrl, boolean isFirstDocument) {
  813. try {
  814. String filePath = fileUrl;
  815. // 处理路径:/profile 开头的路径需要替换为实际路径
  816. if (filePath.startsWith("/profile")) {
  817. filePath = EipConfig.getProfile() + filePath.substring(8);
  818. }
  819. java.io.File file = new java.io.File(filePath);
  820. if (!file.exists()) {
  821. logger.warn("文件不存在:{}(原始路径:{})", filePath, fileUrl);
  822. return isFirstDocument;
  823. }
  824. // 添加分页符(非第一个文档)
  825. if (!isFirstDocument) {
  826. XWPFParagraph pageBreakPara = document.createParagraph();
  827. XWPFRun pageBreakRun = pageBreakPara.createRun();
  828. pageBreakRun.addBreak(BreakType.PAGE);
  829. }
  830. // 读取Word文件
  831. java.io.FileInputStream fis = new java.io.FileInputStream(filePath);
  832. XWPFDocument sourceDoc = new XWPFDocument(fis);
  833. // 使用XML级别复制,保持原始样式
  834. org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody srcBody = sourceDoc.getDocument().getBody();
  835. org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody destBody = document.getDocument().getBody();
  836. // 复制所有元素(段落、表格等)
  837. for (org.apache.xmlbeans.XmlObject obj : srcBody.selectPath("./*")) {
  838. if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP) {
  839. // 复制段落
  840. org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP newP = destBody.addNewP();
  841. newP.set(obj.copy());
  842. } else if (obj instanceof org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl) {
  843. // 复制表格
  844. org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl newTbl = destBody.addNewTbl();
  845. newTbl.set(obj.copy());
  846. }
  847. }
  848. sourceDoc.close();
  849. fis.close();
  850. return false;
  851. } catch (Exception e) {
  852. logger.error("合并文件失败:{},错误:{}", fileUrl, e.getMessage(), e);
  853. return isFirstDocument;
  854. }
  855. }
  856. /**
  857. * 更新单个资料归纳主表的总页数
  858. *
  859. * @param masterId 主表ID
  860. */
  861. public void updateMasterTotalPageCount(String masterId) {
  862. try {
  863. CostProjectTaskMaterialSummary summary = costProjectTaskMaterialSummaryManager.getById(masterId);
  864. if (summary == null) {
  865. logger.warn("资料归纳主表不存在,ID:{}", masterId);
  866. return;
  867. }
  868. int totalPageCount = 0;
  869. // 查询该主表的所有明细
  870. List<CostProjectTaskMaterialSummaryDetail> detailList = costProjectTaskMaterialSummaryDetailManager.list(
  871. new LambdaQueryWrapper<CostProjectTaskMaterialSummaryDetail>()
  872. .eq(CostProjectTaskMaterialSummaryDetail::getMasterId, masterId)
  873. .eq(CostProjectTaskMaterialSummaryDetail::getIsDeleted, "0")
  874. );
  875. // 累加所有明细的页数
  876. for (CostProjectTaskMaterialSummaryDetail detail : detailList) {
  877. if (detail.getPageCount() != null) {
  878. totalPageCount += detail.getPageCount();
  879. }
  880. }
  881. // 更新主表的总页数
  882. summary.setTotalPageCount(String.valueOf(totalPageCount));
  883. costProjectTaskMaterialSummaryManager.updateById(summary);
  884. logger.info("资料归纳【{}】总页数已更新:{}", summary.getMaterialName(), totalPageCount);
  885. } catch (Exception e) {
  886. logger.error("更新主表总页数失败,主表ID:{},错误:{}", masterId, e.getMessage(), e);
  887. }
  888. }
  889. /**
  890. * 统计每个资料归纳主表的总页数(所有明细的页数之和)
  891. *
  892. * @param taskId 任务ID
  893. */
  894. public void calculatePageCountAsync(String taskId) {
  895. try {
  896. logger.info("开始异步统计页数,任务ID:{}", taskId);
  897. // 查询任务的所有资料归纳主表
  898. List<CostProjectTaskMaterialSummary> summaryList = costProjectTaskMaterialSummaryManager.listByTaskId(taskId);
  899. for (CostProjectTaskMaterialSummary summary : summaryList) {
  900. int totalPageCount = 0;
  901. // 获取该主表的所有明细
  902. if (summary.getDetailList() != null && !summary.getDetailList().isEmpty()) {
  903. for (CostProjectTaskMaterialSummaryDetail detail : summary.getDetailList()) {
  904. if (StringUtil.isNotEmpty(detail.getAttachmentUrl())) {
  905. // 统计该明细的页数
  906. int pageCount = calculateFilePageCount(detail.getAttachmentUrl());
  907. detail.setPageCount(pageCount);
  908. costProjectTaskMaterialSummaryDetailManager.updateById(detail);
  909. // 累加到总页数
  910. totalPageCount += pageCount;
  911. }
  912. }
  913. }
  914. // 更新主表的总页数
  915. summary.setTotalPageCount(String.valueOf(totalPageCount));
  916. costProjectTaskMaterialSummaryManager.updateById(summary);
  917. logger.info("资料归纳【{}】统计完成,总页数:{}", summary.getMaterialName(), totalPageCount);
  918. }
  919. logger.info("页数统计完成,任务ID:{}", taskId);
  920. } catch (Exception e) {
  921. logger.error("统计页数失败,任务ID:{},错误:{}", taskId, e.getMessage(), e);
  922. }
  923. }
  924. /**
  925. * 计算文件页数(公共方法,可被外部调用)
  926. *
  927. * @param fileUrl 文件URL或路径
  928. * @return 页数
  929. */
  930. public int calculateFilePageCount(String fileUrl) {
  931. try {
  932. if (StringUtil.isEmpty(fileUrl)) {
  933. return 0;
  934. }
  935. // 判断文件类型
  936. String lowerCaseUrl = fileUrl.toLowerCase();
  937. if (lowerCaseUrl.endsWith(".xls") || lowerCaseUrl.endsWith(".xlsx")) {
  938. // Excel文件算1页
  939. return 1;
  940. } else if (lowerCaseUrl.endsWith(".doc") || lowerCaseUrl.endsWith(".docx")) {
  941. // Word文件需要读取实际页数
  942. return getWordPageCount(fileUrl);
  943. } else {
  944. // 其他格式默认1页
  945. return 1;
  946. }
  947. } catch (Exception e) {
  948. logger.error("计算文件页数失败,文件:{},错误:{}", fileUrl, e.getMessage(), e);
  949. return 0;
  950. }
  951. }
  952. /**
  953. * 获取Word文档的页数
  954. *
  955. * @param fileUrl 文件URL或路径(如:/profile/upload/20251116/xxx.docx)
  956. * @return 页数
  957. */
  958. private int getWordPageCount(String fileUrl) {
  959. java.io.FileInputStream fis = null;
  960. XWPFDocument document = null;
  961. try {
  962. String filePath = fileUrl;
  963. if (filePath.startsWith("/profile")) {
  964. filePath = EipConfig.getProfile() + filePath.substring(8);
  965. }
  966. fis = new java.io.FileInputStream(filePath);
  967. document = new XWPFDocument(fis);
  968. // 获取页数
  969. int pageCount = document.getProperties().getExtendedProperties().getUnderlyingProperties().getPages();
  970. return pageCount > 0 ? pageCount : 1;
  971. } catch (Exception e) {
  972. logger.error("读取Word页数失败,文件:{},错误:{}", fileUrl, e.getMessage(), e);
  973. // 读取失败默认返回1页
  974. return 1;
  975. } finally {
  976. // 关闭资源
  977. try {
  978. if (document != null) {
  979. document.close();
  980. }
  981. if (fis != null) {
  982. fis.close();
  983. }
  984. } catch (Exception e) {
  985. logger.warn("关闭文件流失败:{}", e.getMessage());
  986. }
  987. }
  988. }
  989. // ==================== 卷宗校对文书生成功能 ====================
  990. /**
  991. * 生成卷宗文书(封面/目录/封底)
  992. * id为空是新增,id不为空是编辑
  993. *
  994. * @param req 生成请求
  995. * @return 生成的文书URL
  996. */
  997. public String generateArchiveDocument(com.hotent.project.req.ArchiveProofreadReq req) throws Exception {
  998. Integer documentType = req.getDocumentType();
  999. String existingId = req.getRelatedId();
  1000. // 如果id不为空,从数据库获取已有记录的documentType(id是主表ID)
  1001. if (StringUtil.isNotEmpty(existingId)) {
  1002. CostProjectTaskMaterialSummary existing = costProjectTaskMaterialSummaryManager.getById(existingId);
  1003. if (existing == null) {
  1004. throw new RuntimeException("主表记录不存在:" + existingId);
  1005. }
  1006. Integer orderNum = existing.getMaterialOrderNum();
  1007. if (orderNum != null && orderNum == -1) {
  1008. documentType = 1;
  1009. } else if (orderNum != null && orderNum == -2) {
  1010. documentType = 2;
  1011. } else if (orderNum != null && orderNum == -3) {
  1012. documentType = 3;
  1013. } else {
  1014. throw new RuntimeException("无效的文书记录");
  1015. }
  1016. }
  1017. String templatePath;
  1018. String documentName;
  1019. switch (documentType) {
  1020. case 1:
  1021. templatePath = coverTemplatePath;
  1022. documentName = "案卷封面";
  1023. if (StringUtil.isEmpty(templatePath)) {
  1024. throw new RuntimeException("案卷封面模板路径未配置");
  1025. }
  1026. break;
  1027. case 2:
  1028. templatePath = catalogTemplatePath;
  1029. documentName = "卷内目录";
  1030. if (StringUtil.isEmpty(templatePath)) {
  1031. throw new RuntimeException("卷内目录模板路径未配置");
  1032. }
  1033. break;
  1034. case 3:
  1035. templatePath = backCoverTemplatePath;
  1036. documentName = "案卷封底";
  1037. if (StringUtil.isEmpty(templatePath)) {
  1038. throw new RuntimeException("案卷封底模板路径未配置");
  1039. }
  1040. break;
  1041. default:
  1042. throw new RuntimeException("不支持的文书类型:" + documentType);
  1043. }
  1044. // 检查模板文件是否存在
  1045. java.io.File templateFile = new java.io.File(templatePath);
  1046. if (!templateFile.exists()) {
  1047. throw new RuntimeException("模板文件不存在:" + templatePath);
  1048. }
  1049. // 生成文书
  1050. String outputUrl = generateDocumentFromTemplate(templatePath, documentName, req);
  1051. // 保存到数据库(主表+明细表)
  1052. int orderNum = -documentType; // -1封面、-2目录、-3封底
  1053. String masterId;
  1054. if (StringUtil.isNotEmpty(existingId)) {
  1055. // 编辑:existingId是主表ID,查找对应的明细记录
  1056. masterId = existingId;
  1057. CostProjectTaskMaterialSummary existingSummary = costProjectTaskMaterialSummaryManager.getById(masterId);
  1058. if (existingSummary != null) {
  1059. // 更新task的卷宗号和归档人
  1060. CostProjectTask task = costProjectTaskManager.getById(existingSummary.getTaskId());
  1061. if (task != null) {
  1062. task.setArchiveNo(req.getArchiveNo());
  1063. task.setArchiveUser(ContextUtil.getCurrentUser().getFullname());
  1064. costProjectTaskManager.updateById(task);
  1065. }
  1066. }
  1067. CostProjectTaskMaterialSummaryDetail existingDetail = costProjectTaskMaterialSummaryDetailManager.getOne(
  1068. new LambdaQueryWrapper<CostProjectTaskMaterialSummaryDetail>()
  1069. .eq(CostProjectTaskMaterialSummaryDetail::getMasterId, masterId)
  1070. .eq(CostProjectTaskMaterialSummaryDetail::getIsDeleted, "0")
  1071. );
  1072. if (existingDetail != null) {
  1073. // 更新已有明细
  1074. existingDetail.setAttachmentUrl(outputUrl);
  1075. existingDetail.setGenerateTime(java.time.LocalDateTime.now());
  1076. costProjectTaskMaterialSummaryDetailManager.updateById(existingDetail);
  1077. } else {
  1078. // 创建新明细
  1079. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  1080. detail.setMasterId(masterId);
  1081. detail.setTaskId(req.getTaskId());
  1082. detail.setDocumentName(documentName);
  1083. detail.setFileSource("系统生成电子文书");
  1084. detail.setAttachmentUrl(outputUrl);
  1085. detail.setOrderNum(1);
  1086. detail.setGenerateTime(java.time.LocalDateTime.now());
  1087. detail.setIsDeleted("0");
  1088. costProjectTaskMaterialSummaryDetailManager.save(detail);
  1089. }
  1090. } else {
  1091. // 新增:创建主表记录
  1092. CostProjectTaskMaterialSummary summary = new CostProjectTaskMaterialSummary();
  1093. summary.setTaskId(req.getTaskId());
  1094. summary.setMaterialOrderNum(orderNum);
  1095. summary.setMaterialName(documentName);
  1096. summary.setIsDeleted("0");
  1097. costProjectTaskMaterialSummaryManager.save(summary);
  1098. masterId = summary.getId();
  1099. // 更新task的卷宗号和归档人
  1100. CostProjectTask task = costProjectTaskManager.getById(req.getTaskId());
  1101. if (task != null) {
  1102. task.setArchiveNo(req.getArchiveNo());
  1103. task.setArchiveUser(ContextUtil.getCurrentUser().getAccount());
  1104. costProjectTaskManager.updateById(task);
  1105. }
  1106. // 创建明细记录
  1107. CostProjectTaskMaterialSummaryDetail detail = new CostProjectTaskMaterialSummaryDetail();
  1108. detail.setMasterId(masterId);
  1109. detail.setTaskId(req.getTaskId());
  1110. detail.setDocumentName(documentName);
  1111. detail.setFileSource("系统生成电子文书");
  1112. detail.setAttachmentUrl(outputUrl);
  1113. detail.setOrderNum(1);
  1114. detail.setGenerateTime(java.time.LocalDateTime.now());
  1115. detail.setIsDeleted("0");
  1116. costProjectTaskMaterialSummaryDetailManager.save(detail);
  1117. }
  1118. return outputUrl;
  1119. }
  1120. /**
  1121. * 根据模板生成文书
  1122. *
  1123. * @param templatePath 模板路径
  1124. * @param documentName 文书名称
  1125. * @param req 请求参数
  1126. * @return 生成的文书URL
  1127. */
  1128. private String generateDocumentFromTemplate(String templatePath, String documentName,
  1129. com.hotent.project.req.ArchiveProofreadReq req) throws Exception {
  1130. java.io.FileInputStream fis = null;
  1131. XWPFDocument document = null;
  1132. java.io.FileOutputStream fos = null;
  1133. try {
  1134. // 读取模板
  1135. fis = new java.io.FileInputStream(templatePath);
  1136. document = new XWPFDocument(fis);
  1137. // 构建替换映射
  1138. Map<String, String> replacements = buildReplacementMap(req);
  1139. // 根据文书类型进行不同处理
  1140. switch (req.getDocumentType()) {
  1141. case 1:
  1142. // 案卷封面
  1143. generateCoverDocument(document, replacements);
  1144. break;
  1145. case 2:
  1146. // 卷内目录
  1147. generateCatalogDocument(document, replacements, req.getTaskId());
  1148. break;
  1149. case 3:
  1150. // 案卷封底
  1151. generateBackCoverDocument(document, replacements);
  1152. break;
  1153. }
  1154. // 生成输出文件路径
  1155. String fileName = FileUploadUtil.generateFileName(documentName + ".docx");
  1156. String outputPath = FileUploadUtil.generateSavePath(
  1157. EipConfig.getUploadPath(), fileName, "docx");
  1158. // 确保目录存在
  1159. java.io.File outputFile = new java.io.File(outputPath);
  1160. if (!outputFile.getParentFile().exists()) {
  1161. outputFile.getParentFile().mkdirs();
  1162. }
  1163. // 写入文件
  1164. fos = new java.io.FileOutputStream(outputPath);
  1165. document.write(fos);
  1166. // 返回访问URL
  1167. return FileUploadUtil.getPathFileName(outputPath, fileName);
  1168. } finally {
  1169. // 关闭资源
  1170. try {
  1171. if (fos != null) fos.close();
  1172. if (document != null) document.close();
  1173. if (fis != null) fis.close();
  1174. } catch (Exception e) {
  1175. logger.warn("关闭文件流失败:{}", e.getMessage());
  1176. }
  1177. }
  1178. }
  1179. /**
  1180. * 构建替换映射
  1181. * 自动从数据库获取:年份、监审项目名称、被监审单位、监审期间(开始-结束年度)、监审人员、办结时间
  1182. * 需要用户传递:价格主管部门、卷宗号、保管期间周期、立卷人、立卷日期、检查人、检查日期、备注
  1183. */
  1184. private Map<String, String> buildReplacementMap(com.hotent.project.req.ArchiveProofreadReq req) {
  1185. Map<String, String> map = new java.util.HashMap<>();
  1186. // 从数据库获取任务信息
  1187. CostProjectTask task = costProjectTaskManager.getById(req.getTaskId());
  1188. // 从数据库获取立项审批信息(包含监审组人员)
  1189. CostProjectApproval approval = costProjectApprovalManager.getOne(
  1190. new LambdaQueryWrapper<CostProjectApproval>()
  1191. .eq(CostProjectApproval::getProjectId, task.getProjectId())
  1192. );
  1193. // ========== 自动获取的字段 ==========
  1194. Org org = orgManager.getById(approval.getOrgId());
  1195. map.put("{价格主管部门或成本监审机构}", org.getName());
  1196. map.put("{年份}", task.getYear());
  1197. map.put("{监审项目名称}", task.getProjectName());
  1198. map.put("{被监审单位}", task.getAuditedUnitName());
  1199. // 监审期间(解析开始-结束年度,格式如:2024,2025,2023 逗号分隔)
  1200. String startYear = "";
  1201. String endYear = "";
  1202. String auditPeriod = task.getAuditPeriod();
  1203. if (StringUtil.isNotEmpty(auditPeriod)) {
  1204. String[] years = auditPeriod.split(",");
  1205. int minYear = Integer.MAX_VALUE;
  1206. int maxYear = Integer.MIN_VALUE;
  1207. for (String year : years) {
  1208. try {
  1209. int y = Integer.parseInt(year.trim());
  1210. if (y < minYear) minYear = y;
  1211. if (y > maxYear) maxYear = y;
  1212. } catch (NumberFormatException e) {
  1213. }
  1214. }
  1215. if (minYear != Integer.MAX_VALUE) {
  1216. startYear = String.valueOf(minYear);
  1217. endYear = String.valueOf(maxYear);
  1218. }
  1219. }
  1220. map.put("{监审开始年度}", startYear);
  1221. map.put("{监审结束年度}", endYear);
  1222. // 监审组负责人及人员(将ID转换为姓名)
  1223. String auditGroupNames = "";
  1224. if (approval != null && StringUtil.isNotEmpty(approval.getAuditGroup())) {
  1225. String[] userIds = approval.getAuditGroup().split(",");
  1226. StringBuilder names = new StringBuilder();
  1227. for (String userId : userIds) {
  1228. try {
  1229. User user = userManager.get(userId.trim());
  1230. if (user != null) {
  1231. if (names.length() > 0) {
  1232. names.append(",");
  1233. }
  1234. names.append(user.getFullname());
  1235. }
  1236. } catch (Exception e) {
  1237. }
  1238. }
  1239. auditGroupNames = names.toString();
  1240. }
  1241. map.put("{监审组负责人及人员}", auditGroupNames);
  1242. // 监审办结时间(任务状态为办结时,取更新时间,格式:xxxx年xx月)
  1243. String auditEndTime = "";
  1244. if ("400".equals(task.getStatus()) && task.getUpdateTime() != null) {
  1245. auditEndTime = task.getUpdateTime().getYear() + "年" + task.getUpdateTime().getMonthValue() + "月";
  1246. }
  1247. map.put("{监审办结时间}", auditEndTime);
  1248. // ========== 需要用户传递的字段 ==========
  1249. map.put("{卷宗号}", req.getArchiveNo() != null ? req.getArchiveNo() : "");
  1250. map.put("{保管期间周期}", req.getRetentionPeriod() != null ? req.getRetentionPeriod() : "");
  1251. // ========== 卷宗统计信息 ==========
  1252. // 获取14个资料归纳的总页数
  1253. List<CostProjectTaskMaterialSummary> summaryList = costProjectTaskMaterialSummaryManager.listByTaskId(req.getTaskId());
  1254. summaryList = summaryList.stream()
  1255. .filter(s -> s.getMaterialOrderNum() != null && s.getMaterialOrderNum() > 0)
  1256. .collect(Collectors.toList());
  1257. int totalPageCount = 0;
  1258. for (CostProjectTaskMaterialSummary summary : summaryList) {
  1259. if (summary.getTotalPageCount() != null) {
  1260. try {
  1261. totalPageCount += Integer.parseInt(summary.getTotalPageCount());
  1262. } catch (NumberFormatException e) {
  1263. }
  1264. }
  1265. }
  1266. map.put("{卷宗件数}", "1"); // 默认1件
  1267. map.put("{卷宗页数}", String.valueOf(totalPageCount));
  1268. map.put("{第几件}", "1"); // 默认第1件
  1269. map.put("{备注}", req.getRemark());
  1270. return map;
  1271. }
  1272. /**
  1273. * 生成案卷封面
  1274. */
  1275. private void generateCoverDocument(XWPFDocument document, Map<String, String> replacements) {
  1276. SmartTemplateWriter.writeToTemplate(document, replacements);
  1277. }
  1278. /**
  1279. * 生成卷内目录(只填充表格,不替换占位符)
  1280. */
  1281. private void generateCatalogDocument(XWPFDocument document, Map<String, String> replacements, String taskId) {
  1282. // 获取资料归纳列表,生成目录表格
  1283. List<CostProjectTaskMaterialSummary> summaryList = costProjectTaskMaterialSummaryManager.listByTaskId(taskId);
  1284. // 过滤掉封面、目录、封底(负数序号)
  1285. summaryList = summaryList.stream()
  1286. .filter(s -> s.getMaterialOrderNum() != null && s.getMaterialOrderNum() > 0)
  1287. .sorted(Comparator.comparing(CostProjectTaskMaterialSummary::getMaterialOrderNum))
  1288. .collect(Collectors.toList());
  1289. // 构建目录数据
  1290. List<CompleteTemplateProcessor.TableRowData> tableDataList = new java.util.ArrayList<>();
  1291. int currentPage = 1;
  1292. for (CostProjectTaskMaterialSummary summary : summaryList) {
  1293. CompleteTemplateProcessor.TableRowData rowData =
  1294. new CompleteTemplateProcessor.TableRowData();
  1295. rowData.setDocumentName(summary.getMaterialName());
  1296. int pageCount = 0;
  1297. if (summary.getTotalPageCount() != null) {
  1298. try {
  1299. pageCount = Integer.parseInt(summary.getTotalPageCount());
  1300. } catch (NumberFormatException e) {
  1301. pageCount = 0;
  1302. }
  1303. }
  1304. rowData.setPageCount(pageCount);
  1305. // 计算页码范围作为备注
  1306. if (pageCount > 0) {
  1307. rowData.setRemark(currentPage + "-" + (currentPage + pageCount - 1));
  1308. currentPage += pageCount;
  1309. } else {
  1310. rowData.setRemark("—");
  1311. }
  1312. tableDataList.add(rowData);
  1313. }
  1314. // 只填充表格数据,
  1315. if (!tableDataList.isEmpty()) {
  1316. processTableData(document, tableDataList);
  1317. }
  1318. }
  1319. /**
  1320. * 生成案卷封底
  1321. */
  1322. private void generateBackCoverDocument(XWPFDocument document, Map<String, String> replacements) {
  1323. SmartTemplateWriter.writeToTemplate(document, replacements);
  1324. }
  1325. /**
  1326. * 处理目录表格数据
  1327. */
  1328. private void processTableData(XWPFDocument document, List<CompleteTemplateProcessor.TableRowData> dataList) {
  1329. List<XWPFTable> tables = document.getTables();
  1330. if (tables.isEmpty()) {
  1331. logger.warn("未找到表格");
  1332. return;
  1333. }
  1334. XWPFTable table = tables.get(0);
  1335. // 找到模板行(第二行,第一行是表头)
  1336. int templateRowIndex = 0;
  1337. if (table.getRows().size() <= templateRowIndex) {
  1338. logger.warn("表格行数不足");
  1339. return;
  1340. }
  1341. // 获取模板行的单元格数量
  1342. int cellCount = table.getRow(templateRowIndex).getTableCells().size();
  1343. // 删除模板行
  1344. table.removeRow(templateRowIndex);
  1345. // 从模板行位置开始插入数据行
  1346. for (int i = 0; i < dataList.size(); i++) {
  1347. CompleteTemplateProcessor.TableRowData data = dataList.get(i);
  1348. XWPFTableRow newRow = table.insertNewTableRow(templateRowIndex + i);
  1349. // 创建单元格
  1350. for (int j = 0; j < cellCount; j++) {
  1351. newRow.addNewTableCell();
  1352. }
  1353. fillTableRow(newRow, i + 1, data);
  1354. }
  1355. }
  1356. /**
  1357. * 填充表格行数据
  1358. */
  1359. private void fillTableRow(XWPFTableRow row, int sequence,
  1360. CompleteTemplateProcessor.TableRowData data) {
  1361. List<XWPFTableCell> cells = row.getTableCells();
  1362. if (cells.size() >= 4) {
  1363. setCellText(cells.get(0), String.valueOf(sequence));
  1364. setCellText(cells.get(1), data.getDocumentName());
  1365. setCellText(cells.get(2), data.getPageCount() > 0 ? String.valueOf(data.getPageCount()) : "");
  1366. setCellText(cells.get(3), data.getRemark() != null ? data.getRemark() : "");
  1367. }
  1368. }
  1369. /**
  1370. * 设置单元格文本
  1371. */
  1372. private void setCellText(XWPFTableCell cell, String text) {
  1373. // 清除现有内容
  1374. for (int i = cell.getParagraphs().size() - 1; i >= 0; i--) {
  1375. cell.removeParagraph(i);
  1376. }
  1377. // 添加新内容
  1378. XWPFParagraph paragraph = cell.addParagraph();
  1379. XWPFRun run = paragraph.createRun();
  1380. run.setText(text);
  1381. paragraph.setAlignment(ParagraphAlignment.CENTER);
  1382. }
  1383. }