From 0c5c540e0f75c3c591dd36928dfb1ca404f2d124 Mon Sep 17 00:00:00 2001 From: agentuser Date: Mon, 25 May 2026 15:00:21 +0800 Subject: [PATCH] feat: fill annual report table cells with generated investment data --- docs/openapi.yaml | 6 +- .../annualReport/AnnualReportProvider.java | 119 +++++++++++++++--- src/main/resources/application.yml | 2 +- 3 files changed, 103 insertions(+), 24 deletions(-) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 08b59e3..f1f1b66 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -5,7 +5,7 @@ info: contact: name: 吕冬 servers: - - url: https://closein.site + - url: https://closein.cn description: 生产服务器(HTTPS) - url: http://localhost:10086 description: 本地开发服务器(HTTP) @@ -42,7 +42,7 @@ paths: code: 0 msg: "success" data: - url: "https://closein.site/api/docx/download?id=d8d4122719c8465ea75ab62b37f6ce10" + url: "https://closein.cn/api/docx/download?id=d8d4122719c8465ea75ab62b37f6ce10" fileName: "annualReport.docx" "400": description: 参数校验失败(exportId 为空 / businessData 为空) @@ -177,7 +177,7 @@ components: type: string format: uri description: 文件下载 URL,由 origin + 路径组成 - example: "https://closein.site/api/docx/download?id=d8d4122719c8465ea75ab62b37f6ce10" + example: "https://closein.cn/api/docx/download?id=d8d4122719c8465ea75ab62b37f6ce10" fileName: type: string description: 文件名 diff --git a/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java b/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java index c58e35c..c3d67e8 100644 --- a/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java +++ b/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java @@ -3,8 +3,10 @@ package com.gmp.mock.template.annualReport; import java.io.InputStream; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTable; @@ -16,24 +18,38 @@ import com.deepoove.poi.XWPFTemplate; import com.gmp.mock.template.TemplateProvider; /** - * poi-tl based template provider. + * 年度报告模板填充器。 * - * Simple text placeholders ({{title}}, {{companyName}}, etc.) are handled - * by poi-tl directly. Table rows are built programmatically with POI API - * after poi-tl rendering, because poi-tl's table loop syntax can be - * finicky with raw XML templates. + * 模板含一个固定表格,行结构如下: + * Row 0: 表头(序号 | 专业类别 | 合计 | yearA | yearB | yearC) + * Row 1: 总计(汇总行) + * Row 2~11: 各投资分类(电网基建/小型基建/技改/科技/信息化) * - * To edit the template: open the .docx in Word, write {{placeholders}}. + * postProcess() 遍历现有行,按行名填充生成的投资数据到单元格。 */ @Component public class AnnualReportProvider implements TemplateProvider { - private static final List> METRICS = List.of( - Map.of("name", "营业收入", "value", "¥ 1,234,567", "change", "+15.3%"), - Map.of("name", "净利润", "value", "¥ 321,098", "change", "+8.7%"), - Map.of("name", "毛利率", "value", "45.2%", "change", "+2.1%") + /** 行索引 → 生成策略 */ + private static final List ROW_CONFIGS = List.of( + new RowConfig(1, "总计", true), // 汇总行:自动求和 + new RowConfig(2, "电网基建", true), // 分类标题行 + new RowConfig(3, "主网基建", false), + new RowConfig(4, "配网基建", false), + new RowConfig(5, "小型基建", true), // 分类标题行(只有一项) + new RowConfig(6, "技改项目", true), // 分类标题行 + new RowConfig(7, "生产技改", false), + new RowConfig(8, "营销技改", false), + new RowConfig(9, "其他技改", false), + new RowConfig(10, "科技项目", true), // 分类标题行(只有一项) + new RowConfig(11, "信息化项目", true) // 分类标题行(只有一项) ); + /** 非标题行的行索引列表(需要实际填充数据) */ + private static final List DATA_ROW_INDEXES = List.of(3, 4, 7, 8, 9, 10, 11); + + private record RowConfig(int index, String name, boolean isHeader) {} + @Override public String getTemplateId() { return "annualReport"; @@ -59,27 +75,90 @@ public class AnnualReportProvider implements TemplateProvider { @Override public Map getDataModel() { + int yearA = 2024; + int yearB = 2025; + int yearC = 2026; return Map.of( "title", "2025 年度工作报告", "companyName", "示例科技有限公司", "reportPeriod", "2025年1月 - 2025年12月", - "generatedDate", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")) + "generatedDate", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")), + "yearA", String.valueOf(yearA), + "yearB", String.valueOf(yearB), + "yearC", String.valueOf(yearC), + "snhj", "300", + "dwjjtz", "200" ); } @Override public void postProcess(XWPFDocument document) { List tables = document.getTables(); - if (tables.isEmpty()) { - return; // 模板中无表格,跳过 - } - // Add data rows to the first table in the document + if (tables.isEmpty()) return; XWPFTable table = tables.get(0); - for (Map metric : METRICS) { - XWPFTableRow row = table.createRow(); - row.getCell(0).setText(metric.get("name")); - row.getCell(1).setText(metric.get("value")); - row.getCell(2).setText(metric.get("change")); + + // 1. 为每个数据行生成随机投资数据 + Map rowData = new LinkedHashMap<>(); + long grandTotal = 0; + long grandYearA = 0; + long grandYearB = 0; + long grandYearC = 0; + + // 分类与子项分配比例 + Map> categoryChildren = Map.of( + 2, List.of(3, 4), // 电网基建 → 主网基建 + 配网基建 + 5, List.of(5), // 小型基建(自身就是数据行) + 6, List.of(7, 8, 9), // 技改项目 → 生产技改 + 营销技改 + 其他技改 + 10, List.of(10), // 科技项目 + 11, List.of(11) // 信息化项目 + ); + + for (var entry : categoryChildren.entrySet()) { + int catRowIdx = entry.getKey(); + List childIndexes = entry.getValue(); + + // 为子项生成随机数据 + long catTotal = 0, catA = 0, catB = 0, catC = 0; + for (int childIdx : childIndexes) { + long a = randomInvest(); + long b = randomInvest(); + long c = randomInvest(); + long total = a + b + c; + rowData.put(childIdx, new long[]{total, a, b, c}); + catTotal += total; catA += a; catB += b; catC += c; + } + // 分类标题行 = 子项之和 + rowData.put(catRowIdx, new long[]{catTotal, catA, catB, catC}); + grandTotal += catTotal; grandYearA += catA; grandYearB += catB; grandYearC += catC; + } + // 总计行 + rowData.put(1, new long[]{grandTotal, grandYearA, grandYearB, grandYearC}); + + // 2. 填充表格 + for (var entry : rowData.entrySet()) { + int rowIdx = entry.getKey(); + long[] vals = entry.getValue(); + XWPFTableRow row = table.getRow(rowIdx); + if (row == null) continue; + setCellText(row, 2, fmtLong(vals[0])); // 合计 + setCellText(row, 3, fmtLong(vals[1])); // yearA + setCellText(row, 4, fmtLong(vals[2])); // yearB + setCellText(row, 5, fmtLong(vals[3])); // yearC } } + + private void setCellText(XWPFTableRow row, int col, String text) { + if (row.getCell(col) != null) { + row.getCell(col).setText(text); + } + } + + private static long randomInvest() { + // 生成 500~50000 万元之间的随机投资额 + return ThreadLocalRandom.current().nextLong(500, 50001); + } + + private static String fmtLong(long v) { + return String.format("%,d", v); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5ce28d6..65ea3e6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,4 +6,4 @@ spring: name: gmp-mock-service gmp: - download-origin: https://closein.site + download-origin: https://closein.cn