prepareReportTemplates.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /**
  2. * 将健康/异常报告模板写入 templates/
  3. * 用法:node scripts/prepareReportTemplates.mjs
  4. */
  5. import path from "path";
  6. import fs from "fs";
  7. import { fileURLToPath } from "url";
  8. import PizZip from "pizzip";
  9. import {
  10. HEALTH_IMAGE_MARKERS,
  11. applyHealthTemplateXmlPatches,
  12. patchAnomalyTemplateXml,
  13. patchTemplateXml,
  14. removeAllFooters,
  15. } from "../src/server/reportService/docxReportBuilder.js";
  16. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  17. const root = path.join(__dirname, "..");
  18. const templatesDir = path.join(root, "templates");
  19. const publicHealthCandidates = [
  20. path.join(
  21. root,
  22. "src",
  23. "public",
  24. "file",
  25. "风电机组健康评估_风场级自动报告模板_V3.0(2).docx",
  26. ),
  27. path.join(
  28. root,
  29. "src",
  30. "public",
  31. "file",
  32. "风电机组健康评估_风场级自动报告模板_V2.0.docx",
  33. ),
  34. path.join(
  35. root,
  36. "..",
  37. "public",
  38. "files",
  39. "风电机组健康评估_风场级自动报告模板_V1.0.docx",
  40. ),
  41. ];
  42. const publicHealth = publicHealthCandidates.find((item) => fs.existsSync(item));
  43. const publicAnomalyCandidates = [
  44. path.join(
  45. root,
  46. "src",
  47. "public",
  48. "file",
  49. "异常检测数据分析报告模板(大唐版)_修订版_人工对齐版_业务化.docx",
  50. ),
  51. path.join(
  52. root,
  53. "src",
  54. "public",
  55. "file",
  56. "异常检测数据分析报告模板(大唐版)_修订版_人工对齐版.docx",
  57. ),
  58. path.join(
  59. root,
  60. "src",
  61. "public",
  62. "file",
  63. "异常检测数据分析报告模板(大唐版).docx",
  64. ),
  65. ];
  66. const publicAnomaly = publicAnomalyCandidates.find((item) =>
  67. fs.existsSync(item),
  68. );
  69. function writeTemplate(zip, targetName) {
  70. fs.mkdirSync(templatesDir, { recursive: true });
  71. const outPath = path.join(templatesDir, targetName);
  72. if (fs.existsSync(outPath)) {
  73. try {
  74. fs.chmodSync(outPath, 0o644);
  75. } catch (_e) {
  76. // ignore
  77. }
  78. fs.unlinkSync(outPath);
  79. }
  80. fs.writeFileSync(outPath, zip.generate({ type: "nodebuffer" }), {
  81. mode: 0o644,
  82. });
  83. return outPath;
  84. }
  85. function patchAndWrite(sourcePath, targetName, patchFn) {
  86. if (!fs.existsSync(sourcePath)) {
  87. throw new Error(`源模板不存在: ${sourcePath}`);
  88. }
  89. const content = fs.readFileSync(sourcePath, "binary");
  90. const zip = new PizZip(content);
  91. const documentFile = zip.file("word/document.xml");
  92. if (!documentFile) throw new Error("模板缺少 word/document.xml");
  93. if (targetName === "health-report-template.docx") {
  94. applyHealthTemplateXmlPatches(zip);
  95. } else {
  96. zip.file("word/document.xml", patchFn(documentFile.asText()));
  97. }
  98. removeAllFooters(zip);
  99. return writeTemplate(zip, targetName);
  100. }
  101. fs.mkdirSync(templatesDir, { recursive: true });
  102. if (fs.existsSync(publicHealth)) {
  103. const healthOut = patchAndWrite(
  104. publicHealth,
  105. "health-report-template.docx",
  106. (xml) => patchTemplateXml(xml, HEALTH_IMAGE_MARKERS),
  107. );
  108. console.log("健康模板已写入并插入图片占位:", healthOut, "源:", publicHealth);
  109. } else {
  110. console.warn("未找到健康模板,跳过:", publicHealthCandidates.join(" | "));
  111. }
  112. if (publicAnomaly) {
  113. const anomalyOut = patchAndWrite(
  114. publicAnomaly,
  115. "异常检测数据分析报告模板(大唐版)_修订版_人工对齐版.docx",
  116. (xml) => patchAnomalyTemplateXml(xml),
  117. );
  118. patchAndWrite(publicAnomaly, "anomaly-report-template.docx", (xml) =>
  119. patchAnomalyTemplateXml(xml),
  120. );
  121. console.log("异常模板已写入并补齐 image 占位:", anomalyOut);
  122. } else {
  123. console.warn("未找到异常模板,跳过:", publicAnomalyCandidates.join(" | "));
  124. }
  125. console.log("完成。重启 downLoadServer 后即可生成报告。");