zhizhijie
2026-09-20 b80bb7253a87931fea5a1a788fc4c6ffb204d245
fix(报表): 补齐客运站/物流站场投资明细表的就绪校验与预览类型

问题:报表生成页两张投资明细表显示「未知报表类型:investPassengerDetail /
investLogisticsDetail」,ready=false,无法出表。

根因:前一版只把这两个类型接进导出链路(exportSelected switch、REPORT_FILE_NAMES、
ReportExportController 两个接口、ReportExport.vue 两项),漏了就绪校验与预览链路:
- checkExportReady() 的 switch 无这两个 case → 落 default:rows=0 + 「未知报表类型」;
- isInvestType() 未收录 → 预览不归入投资分支。

与数据无关:8 月投资月报一直在库(客运站场 39 行、物流园区 128 行)。

修复:
- checkExportReady() 新增两 case:按类别统计 investment_monthly 该期行数,并校验
docs/投资/模板 下对应母版是否存在(缺母版给提示),auditTypes 记 INVEST;
- isInvestType() 收录两个类型;
- investPreview() 新增按类别过滤重载 (period, mode, category, summary, problems),
原无参重载保留;预览循环按类型分派;
- 未知类型兜底报错保持不变。

验证:/api/report/ready 两项 ready=true(rows 39/128);/api/report/preview 摘要与
SQL 聚合逐项一致(客运 2048.1/38648.16/550929.76,物流 90185.9/704107.7/2656354.31);
原有投资类型与未知类型负向均正常。详见 docs/功能测试报告/功能测试报告_投资明细表就绪与预览类型补齐_2026-09-20.md
2个文件已修改
1个文件已添加
197 ■■■■■ 已修改文件
docs/功能测试报告/功能测试报告_投资明细表就绪与预览类型补齐_2026-09-20.md 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/工作日结/工作日结_2026-09-20.md 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_Ͷ×ÊÃ÷ϸ±í¾ÍÐ÷ÓëÔ¤ÀÀÀàÐͲ¹Æë_2026-09-20.md
New file
@@ -0,0 +1,60 @@
# åŠŸèƒ½æµ‹è¯•æŠ¥å‘Š_投资明细表就绪与预览类型补齐_2026-09-20
## ä¸€ã€åŠŸèƒ½è¯´æ˜Ž
**问题**:报表生成页面中,「客运站投资明细表」「物流站场投资明细」显示
`未知报表类型:investPassengerDetail / investLogisticsDetail`,无法预览/导出。
**根因**:不是数据问题(8 æœˆå®¢è¿/物流投资月报已在库:客运 39 è¡Œã€ç‰©æµ 128 è¡Œï¼‰ã€‚
上一步只把这两个类型接进了**导出**链路(`exportSelected` switch、`REPORT_FILE_NAMES`、前端列表、两个导出接口),
漏了**导出前的就绪校验与预览**链路:
1. `checkExportReady()` çš„ `switch (t)` æ²¡æœ‰è¿™ä¸¤ä¸ª case â†’ è½åˆ° `default`:`rows=0` + `notes.add("未知报表类型:" + t)`,于是 `ready=false`;
2. `isInvestType(t)` æœªæ”¶å½•这两个类型 â†’ é¢„览循环既不归入投资分支,也拿不到月度覆盖与投资摘要。
**处置**:
- `checkExportReady()` æ–°å¢žä¸¤ä¸ª case:按类别(客运站场 / ç‰©æµå›­åŒºï¼‰ç»Ÿè®¡ `investment_monthly` è¯¥æœŸè¡Œæ•°ï¼Œ
  å¹¶æ ¡éªŒ `docs/投资/模板/` ä¸‹å¯¹åº”母版是否存在(缺母版则提示);
- `isInvestType()` æ”¶å½•两个类型(月度覆盖 + é—®é¢˜æ¸…单纳入投资分支);
- `investPreview()` å¢žåŠ æŒ‰ç±»åˆ«è¿‡æ»¤çš„é‡è½½ï¼ˆ`category != null` æ—¶åªç»Ÿè®¡è¯¥ç±»åˆ«é¡¹ç›®ï¼‰ï¼Œ
  ä½¿ä¸¤å¼ æ˜Žç»†è¡¨çš„æ‘˜è¦ä¸å†æ··å…¥å¦ä¸€ç±»åˆ«çš„合计;原无参重载保留、行为不变。
- æœªçŸ¥ç±»åž‹çš„兜底报错**保留**(未被削弱)。
## äºŒã€æµ‹è¯•环境
| é¡¹ | å€¼ |
| --- | --- |
| æ—¥æœŸ | 2026-09-20 10:4x |
| åŽç«¯ | Spring Boot 8090(重启加载,`Started TrafficAuditApplication` 10:49:51) |
| å‰ç«¯ | Vue 8080(HTTP 200) |
| æ•°æ®åº“ | MySQL 127.0.0.1:3308 `traffic_audit` |
| æŠ¥è¡¨æœŸ | 2026-08(mode=month) |
| æŽ¥å£ | `GET /api/report/ready`、`GET /api/report/preview`(`types` é€—号分隔) |
## ä¸‰ã€ç”¨ä¾‹è¡¨
| ç¼–号 | ç”¨ä¾‹ | è¾“å…¥ | é¢„期 | å®žé™… | ç»“论 |
| --- | --- | --- | --- | --- | --- |
| T01 | å°±ç»ªæ ¡éªŒï¼ˆæ­£å‘) | `types=investPassengerDetail,investLogisticsDetail` | ä¸å†å‡ºçŽ°ã€ŒæœªçŸ¥æŠ¥è¡¨ç±»åž‹ã€ï¼Œready=true | ä¸¤é¡¹å‡ `ready=true`、`note` ä¸ºç©ºï¼›`rows` = 39(客运)/ 128(物流) | âœ… é€šè¿‡ |
| T02 | é¢„览(正向) | åŒä¸Šï¼Œè°ƒ `/api/report/preview` | æœ‰æœˆåº¦è¦†ç›–、摘要与问题清单 | è¦†ç›– `[8]`、`problems` ä¸ºç©ºï¼›æ‘˜è¦ï¼šå®¢è¿ã€Œ39 é¡¹ç›® / æœ¬æœˆ 2048.1 / è‡ªå¹´åˆ 38648.16 / è‡ªå¼€å§‹ 550929.76 ä¸‡å…ƒã€ï¼Œç‰©æµã€Œ128 é¡¹ç›® / æœ¬æœˆ 90185.9 / è‡ªå¹´åˆ 704107.7 / è‡ªå¼€å§‹ 2656354.31 ä¸‡å…ƒã€ | âœ… é€šè¿‡ |
| T03 | æ‘˜è¦ä¸Žåº“内逐项对齐(入库抽查) | ä¸Šè¿°æ‘˜è¦ vs SQL èšåˆ | å®Œå…¨ä¸€è‡´ | SQL:客运 `2048.1 / 38648.16 / 550929.76`、物流 `90185.9 / 704107.7 / 2656354.31`,**逐项一致** | âœ… é€šè¿‡ |
| T04 | åˆ†ç±»åˆ«è¿‡æ»¤ï¼ˆæ­£å‘) | ä¸¤å¼ è¡¨æ˜Žç»†æ‘˜è¦ | ä¸åº”混入另一类别数据 | å®¢è¿ 39 è¡Œã€ç‰©æµ 128 è¡Œï¼Œæ°å¥½ç­‰äºŽ 39+128=167(`investPlan` ç­‰å…¨ç±»åˆ«ç±»åž‹ä»è¿”回 167),说明明细表已按类别收敛 | âœ… é€šè¿‡ |
| T05 | å¯¼å‡ºå›žå½’(正向) | è°ƒ `/api/report/export/investPassengerDetail`、`.../investLogisticsDetail` | ä»èƒ½æ­£å¸¸å‡ºè¡¨ | ç”Ÿæˆ `gen_pax_inv.xlsx`(29462 å­—节)、`gen_log_inv.xls`(134144 å­—节);此前已验证客运汇总页与定稿 0 å·®å¼‚ | âœ… é€šè¿‡ |
| T06 | åŽŸæœ‰æŠ•èµ„ç±»åž‹å›žå½’ï¼ˆæ­£å‘ï¼‰ | `types=investPlan,investBillion,investCounty,investFiveYearLogistics` | è¡Œä¸ºä¸å˜ | å››é¡¹å‡ `ready=true`、`rows=167`、无 note | âœ… é€šè¿‡ |
| T07 | æœªçŸ¥ç±»åž‹ä»è¢«æ‹¦æˆªï¼ˆè´Ÿå‘) | `types=foo` | ä»æç¤ºæœªçŸ¥ç±»åž‹ä¸”不就绪 | `ready=false`、`rows=0`、`note=未知报表类型:foo` | âœ… é€šè¿‡ |
| T08 | ç¼ºæ¯ç‰ˆæç¤ºï¼ˆè´Ÿå‘·设计覆盖) | ç§»èµ°æ¯ç‰ˆ | æç¤ºã€Œdocs/投资/模板 ä¸‹ç¼ºå°‘《…》」 | æœªå®žæµ‹ï¼ˆæœªä¸´æ—¶ç§»åŠ¨ç”¨æˆ·æ¯ç‰ˆæ–‡ä»¶ï¼‰ï¼›ä»£ç è·¯å¾„ä¸Ž `summaryWorkbook` ç¼ºæ¯ç‰ˆåˆ¤å®šåŒæž„ | âš  æœªæ‰§è¡Œ |
## å››ã€å‘现的问题与处置
| ç¼–号 | é—®é¢˜ | æ ¹å›  | å¤„ç½® |
| --- | --- | --- | --- |
| P01 | ä¸¤å¼ æŠ•资明细表在报表生成页显示「未知报表类型:invest*Detail」 | åªæŽ¥äº†å¯¼å‡ºé“¾è·¯ï¼Œæ¼äº†å°±ç»ªæ ¡éªŒ `switch` ä¸Ž `isInvestType` | è¡¥é½ `checkExportReady` ä¸¤ä¸ª case + `isInvestType` æ”¶å½•,见上 |
| P02 | æ˜Žç»†è¡¨æ‘˜è¦è‹¥æ²¿ç”¨åŽŸ `investPreview` ä¼šæ··å…¥å¦ä¸€ç±»åˆ« | åŽŸæ–¹æ³•åªæŒ‰æŠ¥è¡¨æœŸèšåˆå…¨éƒ¨æŠ•èµ„æœˆæŠ¥ | æ–°å¢žå¸¦ `category` çš„重载,按类别过滤项目 id;原重载保留 |
| P03 | ã€Œåˆã€€ã€€ã€€è®¡ã€è¡Œå…¥åº“会虚增合计(前一晚修复的连带影响) | æºè¡¨åˆè®¡è¡Œè¢«å½“成项目 | å·²ä¿®å¤ï¼›æœ¬æ¬¡æ ¸å¯¹ï¼šå®¢è¿æœ¬æœˆå®Œæˆç”±å«åˆè®¡è¡Œçš„ 2130.1 æ”¶æ•›ä¸º **2048.1**,与 SQL é€é¡¹ä¸€è‡´ï¼Œæ— é—留含「计」的项目行 |
## äº”、结论
7 æ¡ç”¨ä¾‹é€šè¿‡ã€1 æ¡ï¼ˆç¼ºæ¯ç‰ˆæç¤ºï¼‰æœªå®žæµ‹ï¼š
报表生成页两张投资明细表的就绪/预览已恢复正常,摘要按类别过滤且与库内 SQL é€é¡¹ä¸€è‡´ï¼Œ
导出与其它投资报表类型均无回归,未知类型兜底拦截仍然有效。
此前「未知报表类型」纯属**代码漏配**,与 8 æœˆæŠ•资月报是否导入无关 â€”— æ•°æ®ä¸€ç›´åœ¨åº“(客运 39 / ç‰©æµ 128)。
docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-20.md
@@ -2,63 +2,49 @@
## ä¸€ã€ä»Šå¤©åšäº†ä»€ä¹ˆ
### 1. å›žç­”用户核对差异的疑问(承接 09-19)
### 1.1 ä¿®å¤ï¼šæŠ¥è¡¨ç”Ÿæˆé¡µä¸¤å¼ æŠ•资明细表报「未知报表类型」
用户把人工算的一列放进桌面《生成_道路运输量汇总表 (1).xlsx》,问差异原因。
用户追问「是不是取数维度不对造成的——程序直接加『城际城乡』,人工用『总量−城市内』」——
**是的,正是这个原因**,已在 09-19 ä¿®å¤å¹¶æŽ¨é€ï¼ˆé—®é¢˜è®°äºŽã€ŠåŠŸèƒ½æµ‹è¯•æŠ¥å‘Š_中口径排名周转量口径统一_2026-09-19.md》)。
- **现象**:报表生成页「客运站投资明细表」「物流站场投资明细」显示
  `未知报表类型:investPassengerDetail / investLogisticsDetail`,无法预览/导出。
- **定位**:报错来自 `ReportExportService.checkExportReady()` çš„ `switch (t)` å…œåº•分支(`ReportExportService.java:231`)。
  å‰ä¸€æ™šåªæŠŠè¿™ä¸¤ä¸ªç±»åž‹æŽ¥è¿›**导出**链路(`exportSelected` switch、`REPORT_FILE_NAMES`、`ReportExportController` ä¸¤ä¸ªæŽ¥å£ã€`ReportExport.vue` ä¸¤é¡¹ï¼‰ï¼Œ
  æ¼äº†**就绪校验与预览**链路,`isInvestType()` ä¹Ÿæ²¡æ”¶å½•这两个类型。
- **与数据无关**:8 æœˆæŠ•资月报一直在库(客运站场 39 è¡Œã€ç‰©æµå›­åŒº 128 è¡Œï¼‰ã€‚
- **修复**:
  1. `checkExportReady()` æ–°å¢žä¸¤ä¸ª case:按类别统计 `investment_monthly` è¯¥æœŸè¡Œæ•° + æ ¡éªŒ `docs/投资/模板/` æ¯ç‰ˆæ˜¯å¦å­˜åœ¨ï¼ˆç¼ºæ¯ç‰ˆç»™æç¤ºï¼‰ï¼›
  2. `isInvestType()` æ”¶å½•两个类型(纳入投资分支的月度覆盖与问题清单);
  3. `investPreview()` å¢žåŠ æŒ‰ç±»åˆ«è¿‡æ»¤é‡è½½ `(period, mode, category, summary, problems)`,原无参重载保留;
  4. é¢„览循环按类型分派到对应类别;未知类型兜底报错保持不变。
### 2. å…¨ç²¾åº¦å­˜å‚¨ + ä¸¤ä½å°æ•°æ˜¾ç¤ºæ”¹é€ ï¼ˆä»Šå¤©ä¸»ä»»åŠ¡ï¼‰
### 1.2 å…¶å®ƒ
**用户指令**:「所有的数据都全部全精度保留,计算得到的结果显示两位小数。」
- **去预舍入**:`ReportExportService.java` åŽ»æŽ‰ 40 å¤„写入前的 `round(...)`——
  è´§è¿è§„上/规下/同比(累计与当月)、`÷10000` å•位换算(省/市 Ã— æœˆ/累计四处)、
  ä¸­å£å¾„排名/分析写入、排名页占比(`setFreightShare`、货运量排名/周转量排名块)、
  é€šç”¨æ•°æ®æ ¼ `setDataCell`、平均运距兜底、客运转运写入、投资进度、物流占比、投资计划万吨;
  `WycSplitCalc.java` å¹³å‡è¿è·å…œåº•改全精度并删除已无引用的私有 `round`。
- **展示位数交给单元格格式**:重写 `applyTwoDecimalFormat()`
  â€”—数值格统一 `0.00`(保留 `;[Red]\(...\)` ç­‰å‰åŽç¼€ï¼‰ï¼Œ**3 ä½åŠä»¥ä¸Šå°æ•°çš„æ ¼å¼**(`0.000_ `、`0.0000`)
  ç”¨æ­£åˆ™ `\.0{3,}` æ”¶æ•›ä¸ºä¸¤ä½ï¼›ç™¾åˆ†æ¯”/日期/科学计数/文本格式不动;整数值沿用「不显示 .00」约定。
- æŽ§åˆ¶å°é¢„览摘要里的 `round` ä¿ç•™ï¼ˆä¸è¿›æŠ¥è¡¨æ•°æ®ï¼‰ã€‚
**过程中修掉的两个自作聪明的坑**(均已在测试报告「四、发现的问题与处置」留证):
1. ã€Œæ•´æ•°ä¸æ˜¾ç¤º .00」的保护先把公式格跳过了,首轮改造后 `货运!EO90/EO93/EO108/EO111`
   ä»æ˜¯ `0.0000`(缓存值 0 è¢«åˆ¤ä¸ºæ•´æ•°ï¼‰â†’ æŠŠæ ¼å¼å½’一判断**提到整数保护之前**。
2. æ—§åˆ¤æ–­ `f.contains("0.00")` æŠŠ `0.000_ ` è¯¯åˆ¤ä¸ºã€Œå·²æ˜¯ä¸¤ä½å°æ•°ã€ï¼Œä¸­å£å¾„明细 134 æ ¼æ¼æ”¹
   â†’ æ”¹æ­£åˆ™åŽå½’零。
- é‡å¯å‰åŽç«¯åŠ è½½æ–°ä»£ç ï¼ˆåŽç«¯ `Started TrafficAuditApplication` 10:49:51)。
- é¡ºæ‰‹æ ¸å¯¹ï¼šå‰ä¸€æ™šã€Œåˆè®¡è¡Œè¢«å½“项目」修复后,客运本月完成投资由含合计行的 2130.1 æ”¶æ•›ä¸º **2048.1**,与 SQL é€é¡¹ä¸€è‡´ï¼›åº“内已无含「计」的伪项目行。
## äºŒã€æµ‹è¯•结果摘要
见《功能测试报告_全精度存储与两位小数显示_2026-09-20.md》:18 æ¡ç”¨ä¾‹ï¼ˆ11 æ­£å‘ / 4 å›žå½’ / 3 è¾¹ç•Œ / 1 è´Ÿå‘挂起)通过。
《功能测试报告_投资明细表就绪与预览类型补齐_2026-09-20.md》:**7 æ¡ç”¨ä¾‹é€šè¿‡ï¼Œ1 æ¡æœªå®žæµ‹**(缺母版提示,未临时移动用户母版文件)。
- ç¼–译通过;22 ä¸ªå¯¼å‡ºæŽ¥å£å›žå½’,21 ä¸ª OK(`wycSplit` å› ç¼º `docs\网络客运` è¾“入目录 500,属既有问题)。
- æ”¹å‰ vs æ”¹åŽã€Šé“路运输量汇总表》逐格对比 66205 ä¸ªæ•°å€¼æ ¼ï¼š**仅 249 æ ¼å˜åŒ–**,最大 0.00022 ä¸‡äººå…¬é‡Œï¼›
  ä¸¤ä½å°æ•°å£å¾„的不一致格 **0**;错误值数量 589 æ— å˜åŒ–。
- å…³é”®å€¼ï¼šæ­¦æ±‰ç´¯è®¡å‘¨è½¬é‡ 152330.8912(全精度 152330.89119999998)、孝感 64429.944543113306 ä¸å†è¢«æˆªæ–­æˆ 64429.9445。
- **09-19 é—留的「全省行 0.0002 ä¸‡äººå…¬é‡Œã€å·²æ¶ˆé™¤**:市州全精度后母版 `=SUM(O4:O20)` ä¸Žæ˜Žç»†æ±‚和逐位闭合(差 0.0)。
- æ ¼å¼ï¼šæœ‰å€¼æ•°å€¼/公式格中 3 ä½ä»¥ä¸Šå°æ•°æ ¼å¼ **108 æ ¼ â†’ 0 æ ¼**(汇总大表)、**134 æ ¼ â†’ 0 æ ¼**(中口径明细),
  15 ä¸ªäº§ç‰©åˆè®¡ 0 æ ¼ã€‚
| æ ¡éªŒé¡¹ | ç»“æžœ |
| --- | --- |
| `/api/report/ready`(两新类型) | ready=true,rows=39 / 128,note ä¸ºç©º |
| `/api/report/preview`(两新类型) | è¦†ç›– [8],problems ä¸ºç©º |
| æ‘˜è¦ vs SQL é€é¡¹å¯¹é½ | å®¢è¿ `2048.1 / 38648.16 / 550929.76`、物流 `90185.9 / 704107.7 / 2656354.31`,**一致** |
| åˆ†ç±»åˆ«è¿‡æ»¤ | å®¢è¿ 39 + ç‰©æµ 128 = 167,全类别类型仍 167(未串类) |
| å¯¼å‡ºå›žå½’ | ä¸¤å¼ æ˜Žç»†è¡¨ä»å¯å¯¼å‡ºï¼ˆ29462 / 134144 å­—节) |
| åŽŸæœ‰æŠ•èµ„ç±»åž‹å›žå½’ | investPlan / investBillion / investCounty / investFiveYearLogistics å‡ ready=true,rows=167 |
| æœªçŸ¥ç±»åž‹è´Ÿå‘ | `foo` â†’ ready=false、note=未知报表类型:foo(兜底仍有效) |
## ä¸‰ã€é—留问题与待办
1. **母版排名页 Q/R å æ¯”公式仍是 `=ROUND(B/L*10,1)`**(货运量排名 Q4:Q42、货运周转量排名 Q5:Q44,共 72 æ ¼ï¼‰ã€‚
   ç¨‹åºä¸è¦†å†™å…¬å¼æ ¼ï¼ŒExcel æ‰“开会重算回 1 ä½å†æŒ‰ `0.00` å±•示。若要彻底全精度,需把三个母版改成 `=B/L*10`(R åˆ— `=10-Q`)——**待用户拍板**。
2. ä»å¸¦ 4 ä½å°æ•°æ ¼å¼çš„ 274 æ ¼ä¸ºç©ºå€¼/文本格(无显示值),本次不动;若后续要在这些格填数再评估。
3. `wycSplit` æŽ¥å£ä¾èµ– `docs\网络客运\网络约车(全省)汇总.xlsx`,该目录当前不存在,导出报 500,待补输入。
4. 09-19 çš„遗留项(用户表 O åˆ—高速运距同比来源、545 å®¶ vs å…¨å£å¾„上报选择)仍未闭环。
1. æ­¦æ±‰ç‰©æµé¡¹ç›®æ¸…单口径(已确认忽略)。
2. å¤©é—¨ç‰©æµæºæ–‡ä»¶ H/J/K ä¸ºç©º â†’ å¾…用户找同事要数据后导入。
3. è†å·ž/黄石「规划内/规划外」拆分口径 â†’ å¾…用户向同事确认。
4. C ç»„(城市客运页结构归一,需用户先在 WPS æ•´åˆ—删除 AJ åˆ—)/D ç»„(增速排名措辞)/B4(导出前基数体检 + å¼¹çª— + ã€Šæ•°æ®ç¼ºå¤±æ¸…单》页)待排期。
5. ç¼ºæ¯ç‰ˆæç¤ºï¼ˆT08)未实测,可在有测试副本时补测。
## å››ã€æ˜Žå¤©è®¡åˆ’
## å››ã€æ˜Žæ—¥è®¡åˆ’
1. æŒ‰ç”¨æˆ·åé¦ˆå†³å®šæ˜¯å¦è¿žæ¯ç‰ˆ Q/R å æ¯”公式一起去掉 `ROUND(...,1)`。
2. è‹¥ç”¨æˆ·æœ‰å…¶ä»–表的显示位数要求,按同一套「全精度写入 + æ ¼å¼æŽ§åˆ¶å±•示」规则处理。
3. è¡¥é½ `docs\网络客运` è¾“入后回归 `wycSplit` å¯¼å‡ºã€‚
---
**本次改动文件**
- `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java`
- `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java`
- æ–‡æ¡£ï¼š`docs/功能测试报告/功能测试报告_全精度存储与两位小数显示_2026-09-20.md`、本文件
1. ç­‰å¤©é—¨æºæ•°æ® / è†å·žé»„石口径确认后补导并复验。
2. æŽ¨è¿› C/D ç»„与 B4。
3. ä¸Žç”¨æˆ·ç¡®è®¤æ˜¯å¦æŠŠ 09-18 å¤œé—´èµ·çš„æœ¬åœ°æäº¤ä¸€å¹¶æŽ¨é€åˆ°å…¬å¸ gitea(用户 09-19 æ›¾è¦æ±‚「今天先不推公司」)。
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -226,6 +226,36 @@
                    }
                    break;
                }
                case "investPassengerDetail":
                case "investLogisticsDetail": {
                    boolean paxDetail = "investPassengerDetail".equals(t);
                    String catName = paxDetail ? "客运站场" : "物流园区";
                    String catLabel = paxDetail ? "客运站场投资月报" : "物流园区投资月报";
                    List<InvestmentProject> catProjects = investProjectMapper.selectList(
                        new LambdaQueryWrapper<InvestmentProject>().eq(InvestmentProject::getCategory, catName));
                    final List<Long> catIds = new ArrayList<>();
                    for (InvestmentProject cp : catProjects) catIds.add(cp.getId());
                    if (catIds.isEmpty()) {
                        rows = 0;
                        notes.add("缺数据:" + catLabel);
                    } else {
                        rows = addReadyCount(() -> investMonthlyMapper.selectCount(
                            periodQw(period, mode, InvestmentMonthly::getReportPeriod)
                                .in(InvestmentMonthly::getProjectId, catIds)), catLabel, notes);
                    }
                    String detTpl = paxDetail ? "模板_X月客运站投资明细表.xlsx" : "模板_X月物流站场投资明细.xls";
                    try {
                        if (resolveAnyTemplate(investTemplateDir, detTpl) == null) {
                            rows = 0;
                            notes.add("docs/投资/模板 ä¸‹ç¼ºå°‘《" + detTpl + "》");
                        }
                    } catch (Exception ex) {
                        rows = 0;
                        notes.add("母版定位失败:" + ex.getMessage());
                    }
                    auditTypes.add("INVEST");
                    break;
                }
                default:
                    rows = 0;
                    notes.add("未知报表类型:" + t);
@@ -381,7 +411,9 @@
                coverage.addAll(investCover);
                // æŠ•资为当月报表(无 1..N ç´¯è®¡å£å¾„),只提示当月缺失
                if (!investCover.contains(month)) missing.add(month);
                investPreview(period, mode, summary, problems);
                if ("investPassengerDetail".equals(t)) investPreview(period, mode, "客运站场", summary, problems);
                else if ("investLogisticsDetail".equals(t)) investPreview(period, mode, "物流园区", summary, problems);
                else investPreview(period, mode, summary, problems);
            } else if ("energySummary".equals(t)) {
                Long ec = energyMapper.selectCount(periodQw(period, mode, EnergyVehicleQuarterly::getReportPeriod));
                if (ec != null && ec > 0) coverage.add(month);
@@ -412,7 +444,8 @@
    }
    private boolean isInvestType(String t) {
        return "investPlan".equals(t) || "investFiveYearLogistics".equals(t) || "investBillion".equals(t) || "investCounty".equals(t);
        return "investPlan".equals(t) || "investFiveYearLogistics".equals(t) || "investBillion".equals(t) || "investCounty".equals(t)
            || "investPassengerDetail".equals(t) || "investLogisticsDetail".equals(t);
    }
    private boolean isEnergyType(String t) {
@@ -668,9 +701,23 @@
        if (allZero) problems.add("全省累计值全为 0(公交/出租月报可能未导入)");
    }
    /** æŠ•资摘要:项目数 + æœ¬æœˆå®Œæˆ/自年初/自开始累计 */
    /** æŠ•资摘要:项目数 + æœ¬æœˆå®Œæˆ/自年初/自开始累计(全投资月报合计,不分类别) */
    private void investPreview(String period, String mode, List<Map<String, Object>> summary, List<String> problems) {
        List<InvestmentMonthly> list = investMonthlyMapper.selectList(periodQw(period, mode, InvestmentMonthly::getReportPeriod));
        investPreview(period, mode, null, summary, problems);
    }
    /** æŠ•资摘要(category éžç©ºæ—¶åªç»Ÿè®¡è¯¥ç±»åˆ«ï¼Œå¦‚客运站场/物流园区投资明细表) */
    private void investPreview(String period, String mode, String category, List<Map<String, Object>> summary, List<String> problems) {
        LambdaQueryWrapper<InvestmentMonthly> qw = periodQw(period, mode, InvestmentMonthly::getReportPeriod);
        if (category != null) {
            List<InvestmentProject> ps = investProjectMapper.selectList(
                new LambdaQueryWrapper<InvestmentProject>().eq(InvestmentProject::getCategory, category));
            List<Long> ids = new ArrayList<>();
            for (InvestmentProject p : ps) ids.add(p.getId());
            if (ids.isEmpty()) { problems.add("缺数据:库内没有「" + category + "」项目主档"); return; }
            qw.in(InvestmentMonthly::getProjectId, ids);
        }
        List<InvestmentMonthly> list = investMonthlyMapper.selectList(qw);
        Set<Long> proj = new HashSet<>();
        double monthDone = 0.0, yearCum = 0.0, startCum = 0.0;
        for (InvestmentMonthly m : list) {