fix(中口径): 库内缺数据不再覆盖母版定稿值 + 跨簿重算容错 + 报表期后月份清空

问题(用户 09-18 反馈):
1) 独立《生成_中口径明细》1~6 月被清空、总行/累计不可用;
2) 汇总大表「中口径明细」页虽有值,但导出的独立件对不上;
3) 8 月报表里 9~12 月被克隆公式算出「全 0」假数据。

根因:库内该月该维无源(2026-01~06 网约车订单源头即为空)时,
代码把模板公式/缓存值 setBlank 清掉;且跨簿公式([1]出租!C13)
在外部工作簿缺失时抛异常,导致引用它的总行/累计连锁失败。

改动(ReportExportService.java):
- exportPassengerMidDetail() 总行值列一律保留模板公式(1~6 月原公式 +
扩列克隆公式),由收尾重算填值;删除「扩列月份手写库内合计」与
keepCached 清公式两条旁路;
- fillMidCell():库内取值 0(该月该维无源)时直接 return,保留母版/
定稿原值,绝不写空、绝不写 0;
- recalcWorkbookFormulas():新增 ev.setIgnoreMissingWorkbooks(true),
跨簿公式缺失时改用该格自身缓存值参与计算;
- 新增 blankFutureMonthColumns():报表期之后的月份列清数据格、留表头。

验证:独立《生成_中口径明细》18 地区 x 12 个月,总行 = Σ4 维、
累计 = Σ1~8 月,不一致数 0;与汇总大表「中口径明细」页 1~8 月
704 对格 0 差异;9~12 月数据格全空、表头保留。
回归:汇总大表 17 页 0 格差异、《生成_中口径排名》0 格差异。

纠正:早前「母版城市级公式引错页」为控制台中文乱码导致的误判,
母版公式正确(=[1]出租!C13-[1]出租!C15),无需修改。

文档:更新《功能测试报告_中口径四维口径统一_2026-09-18.md》
与《工作日结_2026-09-18.md》。
3个文件已修改
139 ■■■■■ 已修改文件
docs/功能测试报告/功能测试报告_中口径四维口径统一_2026-09-18.md 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/工作日结/工作日结_2026-09-18.md 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_Öпھ¶ËÄά¿Ú¾¶Í³Ò»_2026-09-18.md
@@ -16,19 +16,27 @@
- æ€»å®¢è¿é‡/总周转量 = ç»´åº¦ 1+2+3+4(`loadMidClassMap` çš„维度 0);
- **全省行一律 = Î£17 å¸‚å·ž**(不采用库内 `region_code='420000'` çš„全省行,该口径 1-8 æœˆæ¯” Î£å¸‚州多 3042.75,属业务待核项);
- åŒæ¯” = (本期 âˆ’ åŽ»å¹´åŒæœŸ) / åŽ»å¹´åŒæœŸï¼›æ— åŽ»å¹´æ•°æ®ç•™ç©ºã€‚
- **缺数据处理规则(本次新增,重要)**:库内该月该维没有源数据时,**保留母版/模板原值**,绝不写空、也不写 0。
  ä¸­å£å¾„明细的城市级「城际城乡巡游出租/网约车」行本来就是「台账页 æ€»é‡ âˆ’ åŸŽå¸‚内」的引用并已业务定稿;
  ç”¨åº“内缺失值覆盖,会把定稿数清掉(2026-01~06 ç½‘约车订单缺失时,全省 333.39 ä¸Žå„市州值被清空过)。
**本次改动文件**:`traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java`
- `loadMidClassMap()`:补维度 3(巡游出租城乡)、维度 4(网约车城乡),新增 `midPeriodKey()`;
- `fillMidCell()`:去掉「出租/网约车写死 0」旁路,四维统一取库内值;
- `fillMidYoyCell()` / `fillMidCumYoyColumn()`:同比由「只算前两维」改为四维齐全;
- `exportPassengerMidDetail()`:由「只处理公式格」改为「按列遍历」,扩列出来的 7~12 æœˆç©ºæ ¼æ‰å¡«å¾—上;扩列月份的总行值列由库内四维合计直接写入;重算改用逐格容错的 `recalcWorkbookFormulas()`(原 `evaluateAll()` ä¼šåœ¨è·¨ç°¿å…¬å¼å¤„整本中断,累计列拿不到新缓存值)。
- `exportPassengerMidDetail()`:由「只处理公式格」改为「按列遍历」,扩列出来的 7~12 æœˆç©ºæ ¼æ‰å¡«å¾—上;
- æ€»è¡Œå€¼åˆ—**一律保留模板公式**(1~6 æœˆåŽŸå…¬å¼ + æ‰©åˆ—克隆出来的公式),由收尾重算填值(不再 `keepCached` æ¸…公式、也不再手写合计);
- `fillMidCell()`:库内取值为 0(该月该维无源)时直接返回,保留母版/定稿原值;
- é‡ç®—改用逐格容错的 `recalcWorkbookFormulas()`,并 `setIgnoreMissingWorkbooks(true)`:跨簿公式(`[1]出租!C13`)在外部工作簿缺失时改用该格自身缓存值参与计算,否则引用它的总行/累计会连锁失败(表现为"公式在、但没有缓存值");
- æ–°å¢ž `blankFutureMonthColumns()`:报表期之后的月份列(如 8 æœˆæŠ¥è¡¨é‡Œçš„ 9~12 æœˆï¼‰æ¸…空数据格、保留表头,避免出现"9~12 æœˆå…¨æ˜¯ 0"的假数据。
## äºŒã€æµ‹è¯•环境
- åŽç«¯ 8090(`start-dev.ps1` é‡å¯ï¼ŒPID 11772)、前端 8080(复用已在运行实例);数据库 `traffic_audit`(127.0.0.1:3308);报表期 2026-08,mode=month。
- åŽç«¯ 8090(`start-dev.ps1` é‡å¯ï¼Œç¬¬äºŒè½® PID 18516)、前端 8080(复用已在运行实例);数据库 `traffic_audit`(127.0.0.1:3308);报表期 2026-08,mode=month。
- å¯¼å‡ºæŽ¥å£ï¼š`/api/report/export/summaryWorkbook`、`/api/report/export/passengerMidDetail`、`/api/report/export/passengerMidRank`。
- è¯æ®æ–‡ä»¶ï¼š`_tmp/_qa_out/step3c_summary.xlsx`、`step3c_midDetail.xlsx`、`step3c_midRank.xlsx`、`chk_step3c2.txt`、`chk_step3c3.txt`、`cmp_step3bc.txt`(本轮);`step3b_*.xlsx`(改动前基线)。
- è¯æ®æ–‡ä»¶ï¼ˆç¬¬ä¸€è½®ï¼‰ï¼š`_tmp/_qa_out/step3c_summary.xlsx`、`step3c_midDetail.xlsx`、`step3c_midRank.xlsx`、`chk_step3c2.txt`、`chk_step3c3.txt`、`cmp_step3bc.txt`;`step3b_*.xlsx`(改动前基线)。
- è¯æ®æ–‡ä»¶ï¼ˆç¬¬äºŒè½®/最终):`_tmp/_qa_out/step3g_summary.xlsx`、`step3g_midDetail.xlsx`、`step3g_midRank.xlsx`、`export_step3g.txt`、`chk_step3g.txt`、`cmp_md_vs_summary.txt`;导出脚本 `export_step3g.py`。
## ä¸‰ã€ç”¨ä¾‹è¡¨
@@ -46,6 +54,11 @@
| 10 | è´Ÿå‘/边界 | `wyc_order_monthly` 2026-01~06 è®¢å•列全为 NULL、模板《网约车订单及全省总量.xlsx》只有 2026-07 ä¸€è¡Œæœ‰æ•°æ® | ç»´åº¦ 4 è¯¥æœˆæ— æº â†’ è¡Œç•™ç©ºï¼ˆä¸ç¼–造) | ç‹¬ç«‹ä»¶ã€ŒåŸŽé™…城乡网约车」1-6 æœˆä¸ºç©ºï¼›æ±‡æ€»è¡¨è¯¥å£å¾„由母版定稿承载 | é€šè¿‡ï¼ˆæŒ‰é¢„期留空并记录) |
| 11 | è´Ÿå‘/边界 | åŽ»å¹´åŒæœŸæ— æ•°æ®ï¼ˆå¦‚ 2025-01 ç½‘约车订单缺失) | åŒæ¯”留空 | ç•™ç©ºï¼Œæœªå†™ 0 / #DIV/0! | é€šè¿‡ |
| 12 | è´Ÿå‘/边界 | ç¼ºæ¨¡æ¿æ–‡ä»¶ | æŠ›ã€Œæœªæ‰¾åˆ°æ¨¡æ¿æ–‡ä»¶ã€å¹¶å†™å…¥åŽç«¯æ—¥å¿— | æœªè§¦å‘(模板齐全) | ä¸é€‚用 |
| 13 | æ­£å‘ | ç‹¬ç«‹ä»¶åŸŽå¸‚级「城际城乡网约车」(R14 æ­¦æ±‰ / R19 é»„石 â€¦ï¼‰1~6 æœˆ | æœ‰å€¼ï¼ˆæ²¿ç”¨å®šç¨¿ï¼š0 / 13.5998 / 10.668 / â€¦ï¼‰ï¼Œä¸è¢«æ¸…空 | 0 / 13.599765541547697 / 10.668042925780014 â€¦ | é€šè¿‡ |
| 14 | æ­£å‘ | ç‹¬ç«‹ä»¶ 18 åœ°åŒº Ã— 12 ä¸ªæœˆï¼šæ€»è¡Œ = Î£ è¯¥å— 4 ç»´è¡Œã€ç´¯è®¡ = Î£1-8 æœˆ | å…¨éƒ¨ç›¸ç­‰ | ä¸ä¸€è‡´æ•° 0 | é€šè¿‡ |
| 15 | æ­£å‘ | ç‹¬ç«‹ä»¶ä¸Žæ±‡æ€»å¤§è¡¨ã€Œä¸­å£å¾„明细」页 1~8 æœˆé€æ ¼å¯¹ç…§ï¼ˆ18 åœ°åŒº Ã— 5 è¡Œ Ã— 8 æœˆï¼‰ | å®Œå…¨ä¸€è‡´ | 704 å¯¹æ ¼ 0 å·®å¼‚ | é€šè¿‡ |
| 16 | è´Ÿå‘/边界 | æŠ¥è¡¨æœŸä¹‹åŽçš„æœˆä»½åˆ—(9~12 æœˆï¼‰ | æ•°æ®æ ¼ä¸ºç©ºã€è¡¨å¤´ä¿ç•™ | æ•°æ®æ ¼å…¨ç©ºã€è¡¨å¤´ `2026å¹´9月…12月` ä¿ç•™ | é€šè¿‡ |
| 17 | å›žå½’ | æœ¬è½®æ”¹åŠ¨å‰åŽï¼šæ±‡æ€»å¤§è¡¨ 17 é¡µã€ç‹¬ç«‹ã€Šç”Ÿæˆ_中口径排名》 | 0 å·®å¼‚ | å‡ 0 å·®å¼‚;独立《生成_中口径明细》差异仅落在 æ€»è¡Œ/出租城乡/网约车城乡 ä¸‰ç±»è¡Œçš„æœˆä»½åˆ—与累计列 | é€šè¿‡ |
**入库抽查**(报表期 2026-08,与源表逐项对齐)
@@ -64,9 +77,14 @@
   - çŽ°è±¡ï¼šæŽ’åä»¶å…¨çœç´¯è®¡ = 29464.6108、累计同比 = 0.638;中口径明细全省累计 = 31528.0607、累计同比 = 0.0581,差值 2063.45 = 2026 å¹´ 1-6 æœˆç»´åº¦ 4(333.39+294.49+347.23+335.39+369.27+383.68)。
   - æ ¹å› ï¼šæŽ’名件由 Java ç”¨ `loadMidClassMap()` çŽ°ç®—ï¼ˆ1-6 æœˆç½‘约车无源 â†’ å°‘一块,且 2025 å¹´åŸºçº¿ä¹Ÿåªæœ‰åº“内口径);中口径明细页走的是母版公式链 + å®šç¨¿ç¼“存值。
   - å»ºè®®ï¼šè§ã€Šæ±‡æ€»è¡¨ä¸‰æ¡ä¸»çº¿ä¿®å¤æ–¹æ¡ˆ_2026-09-18》第 3 æ­¥â€”—统一由「母版公式链 + å°è´¦é¡µã€å‡ºæ•°ï¼ŒJava ä¸å†å¦ç®—一套(详见本轮答复)。
3. **(属母版缺陷)中口径明细城市级「城际城乡巡游出租/网约车」行公式引错页**
   - çŽ°è±¡ï¼šæ­¦æ±‰ R13/R14 å…¬å¼ä¸º `=公交!C13-公交!C15`(应分别指向 `出租!`、`网约车!`),城市级恒为 0;而全省行 R8/R9 é æ¯ç‰ˆç¼“存值给出真实数(535.83 / 333.39),二者不自洽。
   - å¤„置:需修母版这两行的公式引用(一次性),之后由重算自动出值。本次未改母版。
3. **(已纠正的误判)"母版公式引错页"不成立**
   - ä¸Šä¸€è½®ç»“论误判的经过:控制台里中文乱码(`����!`),把 `=出租!C13-出租!C15` å½“成 `=公交!C13-公交!C15`。
   - å¤æ ¸ç»“论(写入文件后按列读取,避开乱码):母版《中口径明细》与独立模板都是**正确的公式链**——
     åŸŽå¸‚级城乡行 = `=[1]出租!C13-[1]出租!C15` / `=[1]网约车!C13-[1]网约车!C15`(台账页 æ€»é‡ âˆ’ åŸŽå¸‚内),
     å…¨çœè¡Œ = `=C13+C18+…`(Σ17 å¸‚州),总行 = `=C6+C7+C8+C9`。
   - åå‘验证:台账页「总量 âˆ’ åŸŽå¸‚内」按 17 å¸‚州求和 = **535.83 / 333.39**,与母版全省行定稿值完全一致;
     æ­¦æ±‰ å‡ºç§Ÿ/网约车城乡 = 0(该市全部为城市内),黄石 = 26.01 / 13.5998,与母版逐项一致。
   - çœŸæ­£çš„æ ¹å› åœ¨æˆ‘们自己的代码:库内无源时把模板公式/缓存值清空(见问题 1 ä¸Ž"缺数据处理规则")。
4. **(数据缺口)2026 å¹´ 1-6 æœˆç½‘约车订单未入库**
   - `wyc_order_monthly` 2026-01~06 ä»…有 `pin_*`,`order_*` å…¨ NULL;模板文件也只有 2026-07 ä¸€è¡Œæœ‰æ•°æ® â†’ ç»´åº¦ 4 çš„ 1-6 æœˆæ— æºã€‚
@@ -74,4 +92,8 @@
本次改动把「中口径」从 2 ç»´ï¼ˆç­çº¿+公交)统一为 4 ç»´ï¼Œå¹¶ä¿®æŽ‰äº†ç‹¬ç«‹ã€Šç”Ÿæˆ_中口径明细》扩列月份总行为空、累计停在上一期的问题;已验证 12 æ¡ç”¨ä¾‹ï¼ˆ6 æ­£å‘ / 3 å›žå½’ / 3 è´Ÿå‘边界)全部通过,汇总大表 17 é¡µä¸Žã€Šç”Ÿæˆ_中口径排名》零回归。
仍待处理:排名件与明细件的口径归一(问题 2,需业务/方案确认)、母版中口径明细城市级公式引错页(问题 3)、网约车 1-6 æœˆè®¢å•数据缺口(问题 4)。
仍待处理:排名件与明细件的口径归一(问题 2,需业务/方案确认)、网约车 2026-01~06 ä¸Ž 2025-01/09/10 è®¢å•数据缺口(问题 4)。
**补记(同日第二轮)**:本轮把"缺数据保留定稿值 + æ€»è¡Œä¿ç•™å…¬å¼ + è·¨ç°¿å®¹é”™é‡ç®— + æŠ¥è¡¨æœŸä¹‹åŽæœˆä»½æ¸…空"四条并进,
独立《生成_中口径明细》现在**整表自洽**(18 åœ°åŒº Ã— 12 ä¸ªæœˆï¼šæ€»è¡Œ = Î£4 ç»´ã€ç´¯è®¡ = Î£1-8 æœˆï¼Œä¸ä¸€è‡´æ•° 0),
且与汇总大表「中口径明细」页 1~8 æœˆé€æ ¼ä¸€è‡´ï¼ˆ704 æ ¼ 0 å·®å¼‚);汇总大表 17 é¡µä¸ŽæŽ’名件零回归。
docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-18.md
@@ -38,6 +38,28 @@
- ã€Šç”Ÿæˆ_中口径排名》全省累计 29464.6108 / åŒæ¯” 0.638,比中口径明细少 2063.45,差值正好是 2026 å¹´ 1-6 æœˆç»´åº¦ 4(网约车)→ æŽ’名件是 Java çŽ°ç®—ã€æ˜Žç»†é¡µèµ°æ¯ç‰ˆå…¬å¼é“¾ï¼Œä¸¤å¥—å£å¾„æ‰“æž¶ï¼ˆç¬¬ 3 æ­¥çš„真正收口点)。
- æ¯ç‰ˆã€Œä¸­å£å¾„明细」城市级「城际城乡巡游出租/网约车」行公式引错页(`=公交!C13-公交!C15`,应指向 `出租!`/`网约车!`),城市级恒 0、全省行靠缓存值 â†’ éœ€ä¿®æ¯ç‰ˆå…¬å¼ä¸€æ¬¡ã€‚
### 4. ç¬¬ 3 æ­¥æ”¶å°¾ï¼šç¼ºæ•°æ®ä¸å†è¦†ç›–定稿值(13:30)
用户反馈「独立《生成_中口径明细》8 æœˆæ²¡é—®é¢˜äº†ï¼Œä½† 1~6 æœˆåè€Œè¢«æ¸…空」后,追出**真正根因**:
- `exportPassengerMidDetail()` å¯¹æ‰©åˆ—月份(>6 æœˆï¼‰çš„æ€»è¡Œå€¼åˆ—是「直接写库内四维合计、写 0 å°± setBlank」;
  è€Œ 1~6 æœˆèµ° `keepCached()` æŠŠå…¬å¼æ¸…成空格 â†’ è¡¨é¢ä¸Š 7/8 æœˆä¿®å¥½äº†ï¼Œå®žé™…上把母版的定稿链路掐断了。
- `fillMidCell()` åŒæ ·ã€Œåº“内取值为 0 â†’ setBlank」,于是 2026-01~06 ç½‘约车城乡(库内该 6 ä¸ªæœˆæ— è®¢å•、维度 4 æ— æºï¼‰è¢«æ•´ç‰‡æ¸…空,
  è¿žå¸¦æ€»è¡Œã€ç´¯è®¡ã€åŒæ¯”全错。
**改动(`ReportExportService.java`,4 å¤„)**:
1. `exportPassengerMidDetail()` æ€»è¡Œå€¼åˆ—:删掉「扩列月份手写库内合计」与 `keepCached` æ¸…公式两条旁路,**一律保留模板公式**,由收尾重算写回缓存值。
2. `fillMidCell()`:库内取值为 0(该月该维无源)时**直接 return,保留母版/定稿原值**——绝不写空、也绝不写 0。
3. `recalcWorkbookFormulas()`:新增 `ev.setIgnoreMissingWorkbooks(true)`。跨簿公式(`[1]出租!C13`)在外部工作簿缺失时默认抛异常,
   ä¼šè®©å¼•用它的总行/累计连锁失败(表现为「公式在、没有缓存值」),置 true åŽæ”¹ç”¨è¯¥æ ¼è‡ªèº«ç¼“存值参与计算。
4. æ–°å¢ž `blankFutureMonthColumns()`:报表期之后的月份列(8 æœˆæŠ¥è¡¨é‡Œçš„ 9~12 æœˆï¼‰**清数据格、留表头**,避免克隆公式算出「9~12 æœˆå…¨æ˜¯ 0」的假数据。
**重要纠正(写进测试报告,也在此留痕)**:早前判断「母版中口径明细城市级公式引错页(`=公交!C13-公交!C15`)」是**误判**——
那是控制台中文乱码造成的误读;母版/模板真实公式是正确的 `=[1]出租!C13-[1]出租!C15` / `=[1]网约车!C13-[1]网约车!C15`,
台账页「总量−城市内」Σ17 å¸‚å·ž = 535.83 / 333.39,与母版定稿全省行完全一致。**母版公式不需要修,之前"待修母版"的结论作废。**
## äºŒã€æµ‹è¯•结果摘要
见 `docs/功能测试报告/功能测试报告_汇总表公式扩页与重算_2026-09-18.md`:8 æ¡æ­£å‘ + 4 æ¡è´Ÿå‘/边界 + 4 é¡¹å…¥åº“抽查,全部通过。
@@ -55,12 +77,26 @@
- 18 ä¸ªåœ°åŒº Ã—(7、8 æœˆï¼‰æ€»è¡Œ = Î£ è¯¥å— 4 ç»´è¡Œï¼›ç´¯è®¡åˆ—缓存值 = Î£1-8 æœˆï¼ˆå…¨çœ 31528.0607);
- å›žå½’:汇总大表 17 é¡µ 0 æ ¼å˜åŒ–、《生成_中口径排名》0 æ ¼å˜åŒ–、《生成_中口径明细》仅 36 æ ¼å˜åŒ–(全部是目标格)。
### ç¬¬ 3 æ­¥æ”¶å°¾ï¼ˆ13:30)验证
- ç‹¬ç«‹ã€Šç”Ÿæˆ_中口径明细》:18 åœ°åŒº Ã— 12 ä¸ªæœˆï¼Œ**总行 = Î£4 ç»´ã€ç´¯è®¡ = Î£1-8 æœˆï¼Œä¸ä¸€è‡´æ•° 0**;9~12 æœˆæ•°æ®æ ¼å…¨ç©ºã€è¡¨å¤´ä¿ç•™ã€‚
- ä¸Žæ±‡æ€»å¤§è¡¨ã€Œä¸­å£å¾„明细」页 1~8 æœˆ **704 å¯¹æ ¼ 0 å·®å¼‚**。
- å…¨çœ 1-8 æœˆæ€»å®¢è¿é‡ï¼š3733.79 / 3777.83 / 3874.44 / 3959.25 / 4115.87 / 4034.99 / 4028.32 / 4003.57;
  ç½‘约车城乡:333.39 / 294.49 / 347.23 / 335.39 / 369.27 / 383.68 / 421.46 / 426.56;累计 AA5 = 31528.0607、AA9 = 2911.47。
- åŸŽå¸‚级网约车城乡:武汉 0、黄石 13.599765541547697(沿用定稿,未被清空)。
- å›žå½’:汇总大表 17 é¡µ **0 æ ¼å·®å¼‚**、《生成_中口径排名》**0 æ ¼å·®å¼‚**;
  æ˜Žç»†ä»¶å·®å¼‚仅落在 æ€»è¡Œ / å‡ºç§ŸåŸŽä¹¡ / ç½‘约车城乡 è¡Œçš„æœˆä»½åˆ—与 AA ç´¯è®¡åˆ—(属预期)。
## ä¸‰ã€é—留问题与待办
1. ~~中口径排名同比仍是写死值~~ â†’ **已按新口径取值**(M3=0.638 / Q3=0.2247),但仍与中口径明细的 0.0581 ä¸è‡ªæ´½ï¼Œå·®å€¼ 2063.45 = ç½‘约车 1-6 æœˆæ— æºï¼›**收口办法见下方"第 3 æ­¥æ”¶å£å»ºè®®"**。
2. ~~`loadMidClassMap()` ç¼ºå·¡æ¸¸å‡ºç§Ÿ + ç½‘约车两维~~ â†’ **已补**(维度 3、4),独立中口径明细四维齐全。
3. **母版缺陷待修**:中口径明细城市级「城际城乡巡游出租/网约车」行公式引错页(指向 `公交!`),城市级恒 0、全省行靠缓存值。
4. **数据缺口**:2026-01~06 ç½‘约车订单未入库(`order_*` å…¨ NULL),模板《网约车订单及全省总量.xlsx》只有 2026-07 ä¸€è¡Œ â†’ ç»´åº¦ 4 è¯¥ 6 ä¸ªæœˆæ— æºï¼›éœ€ä¸šåŠ¡è¡¥æ•°æˆ–ç¡®è®¤ä¸é€‚ç”¨ã€‚
3. ~~**母版缺陷待修**:中口径明细城市级公式引错页~~ â†’ **误判,已作废**:真实公式是正确的跨簿引用,母版无需修改;当时的"引错页"是控制台中文乱码误读。
4. **数据缺口(已核实,属源头就没有)**:2026-01~06 ç½‘约车订单在**源文件**《模板_网约车订单及全省总量.xlsx》里就是空的(只有全省 pin、无订单列),
   ä¸æ˜¯å¯¼å…¥å¤±è´¥â€”—`wyc_order_monthly` è¯¥ 6 æœŸ `order_*` å…¨ NULL ä¸Žä¹‹å»åˆï¼ˆå¯¼å…¥æ‰¹æ¬¡ `WYC_ORDER`,09-04)。
   å½±å“é¢åªæœ‰ä¸€ä¸ªï¼š`WycSplitCalc.calc()` å›  `orderSum<=0` ç®—不出网约车拆分 â†’ ç»´åº¦ 4 è¯¥ 6 ä¸ªæœˆæ— æºï¼ˆçŽ°å·²ç”±ç¬¬ 4 æ¡æ”¹åŠ¨ä¿ç•™æ¯ç‰ˆå®šç¨¿å€¼å…œä½ï¼‰ã€‚
   **建议**:给网约车订单导入加一条「订单列全空」的显式提示(现在会静默成功),并请业务确认这 6 ä¸ªæœˆè®¢å•是否可补。
5. åº“内 h2031「全省」行 â‰  Î£17 å¸‚州(1-8 æœˆ 27524.49 vs 24481.74,差 3042.75):业务口径问题,建议业务侧核对。
6. æŠ•资:模板里没有的新项目不会自动插行;明细表资金渠道分组行未入库(需加 `fund_channel`);同名不同项目未弹窗。
7. æŠ•资两张明细表(`X月客运站投资明细表` / `X月物流站场投资明细`)生成器未做。
@@ -73,12 +109,13 @@
2. æ´¾ç”Ÿé¡µï¼ˆåŸŽå¸‚客运 / ä¸­å£å¾„明细 / å…¬è·¯æ€»å®¢è¿ / ä¸­å£å¾„客运量 / æŽ’名)**只补公式 + é‡ç®—,不单独写数**;
3. åªæœ‰æ¯ç‰ˆé‡Œæœ¬å°±æ˜¯ç¡¬ç¼–码数值的行(中口径明细的城市级"城际城乡出租/网约车"、全省行)按四维口径回填;
4. ç‹¬ç«‹å¯¼å‡ºä»¶ï¼ˆç”Ÿæˆ_中口径明细 / æŽ’名 / å…¬è·¯æ—…客分市州)改为直接取汇总表对应页的值,或共用同一口径函数;
5. æ¯ç‰ˆé‡Œå¼•错页的城市级公式修一次(第 3 æ¡ï¼‰ï¼›
6. ç”ŸæˆåŽè·‘"生成件 vs æ¯ç‰ˆé‡ç®—"逐格回归比对,差异清单落日志,作为验收基线。
5. ~~母版里引错页的城市级公式修一次~~ â†’ **无需**(误判,已作废);
6. ç”ŸæˆåŽè·‘"生成件 vs æ¯ç‰ˆé‡ç®—"逐格回归比对,差异清单落日志,作为验收基线;
7. åº“内缺失(该月该维无源)时**一律保留母版定稿值、不覆盖**——已在第 4 æ¡æ”¹åŠ¨ä¸­è½åœ°ã€‚
## å››ã€æ˜Žå¤©è®¡åˆ’
1. ç¬¬ 3 æ­¥æ”¶å£ï¼šæŒ‰ä¸Šé¢ 6 æ¡è½åœ°ï¼ˆå…ˆä¿®æ¯ç‰ˆå¼•错页的城市级公式 â†’ æŽ’名件改取同源 â†’ å»ºå›žå½’比对脚本)。
2. ç½‘约车 2026-01~06 è®¢å•数据缺口:向用户确认是补数还是留空。
3. æŠ•资"新增项目插行 + åˆ†ç»„行入库"方案落地(等用户对"模块"口径的最后确认)。
4. æŠ•资两张明细表生成器。
1. ç¬¬ 3 æ­¥æ”¶å£ï¼šæŽ’名件与明细件口径归一(差值 2063.45 = 2026 å¹´ 1-6 æœˆç½‘约车城乡)→ æŽ’名件改取汇总表同源值 â†’ å›ºåŒ–"生成件 vs æ¯ç‰ˆé‡ç®—"逐格回归脚本。
2. ç½‘约车订单导入空列提示(页面明确提示"该期订单列为空,未写入订单数")+ å‘用户确认 2026-01~06 æ˜¯å¦è¡¥æ•°ã€‚
3. æŠ•资"新增项目插行 + åˆ†ç»„行入库(`fund_channel`)"方案落地(等用户对"模块"口径的最后确认)。
4. æŠ•资两张明细表生成器(`X月客运站投资明细表` / `X月物流站场投资明细`)。
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -2223,15 +2223,10 @@
                    if (blockOffset % 5 == 0) {
                        if (isYoy) {
                            fillMidYoyCell(c, mid, currentYear, monthOfCol(col), area, -1, volume);
                        } else if (monthOfCol(col) > 6) {
                            // æ‰©åˆ—出来的 7~12 æœˆæ€»è¡Œå€¼åˆ—:模板里本无此列,克隆后既没有公式也没有缓存值,
                            // ç›´æŽ¥å†™åº“内四维合计(维度0)。1~6 æœˆä¿ç•™æ¨¡æ¿ç¼“存值,避免丢掉模板里的定稿数。
                            double total = midClassVal(mid, currentYear, monthOfCol(col), area, 0, volume);
                            c.setBlank();
                            if (total != 0.0) c.setCellValue(round(total, 4));
                        } else if (c.getCellType() == CellType.FORMULA) {
                            keepCached(c); // 1~6 æœˆæ€»è¡Œå€¼åˆ— = æ¨¡æ¿ç¼“存值,保留
                        }
                        // æ€»è¡Œå€¼åˆ—:一律保留模板公式(1~6 æœˆåŽŸå…¬å¼ + æ‰©åˆ—克隆出来的公式),
                        // ç”±ç”Ÿæˆæ”¶å°¾çš„ recalcWorkbookFormulas æ±‚值写回缓存。早期版本这里 keepCached
                        // ä¼šæŠŠåˆšå…‹éš†å‡ºæ¥çš„公式清成空格,导致 7 æœˆåŠä»¥åŽæ€»è¡Œæ°¸è¿œç©ºç™½ã€‚
                        continue;
                    }
                    int k = blockOffset % 5; // 1=班线 2=公交 3=出租城乡 4=网约车城乡
@@ -2249,7 +2244,27 @@
            int[] recalc = recalcWorkbookFormulas(wb);
            log.info("中口径明细公式重算完成:成功 {} æ ¼ï¼Œå¤±è´¥ {} æ ¼", recalc[0], recalc[1]);
            clearFormulaErrorsAll(wb); // åŽ»å¹´åŒæœŸæ˜Žç»†é¡µæ®‹ç•™ #REF! åŒæ¯”公式 â†’ æ¸…空
            blankFutureMonthColumns(sheet, fillMonths); // æŠ¥è¡¨æœŸä¹‹åŽçš„æœˆä»½åˆ—不保留克隆公式/0 å€¼
            return toBytes(wb);
        }
    }
    /**
     * æ¸…空「报表期之后」的月份列(扩列克隆出来的 7~12 æœˆé‡Œã€å°šæœªåˆ°æœŸçš„æœˆä»½ï¼‰ã€‚
     * ä¸æ¸…的话,克隆出来的总行公式会算出 0,报表上会出现"9~12 æœˆå…¨æ˜¯ 0"的假数据;
     * ç´¯è®¡åˆ—(AA)的公式/缓存值已在重算阶段算好,清空格子不影响它(空 = 0)。
     */
    private void blankFutureMonthColumns(Sheet sheet, int filledMonths) {
        for (int m = filledMonths + 1; m <= 12; m++) {
            int col = 2 * m;            // 0 åŸºï¼š1 æœˆ=2(C)、7 æœˆ=14(O)、12 æœˆ=24(Y)
            for (int r = 4; r <= sheet.getLastRowNum(); r++) { // 0 åŸºç¬¬ 4 è¡Œ = ç¬¬ 5 è¡Œï¼ˆé¦–个数据行),表头不动
                Row row = sheet.getRow(r);
                if (row == null) continue;
                Cell val = row.getCell(col);
                if (val != null) val.setBlank();
                Cell yoy = row.getCell(col + 1);
                if (yoy != null) yoy.setBlank();
            }
        }
    }
@@ -2274,12 +2289,19 @@
        return (col - 1) / 2;
    }
    /** å¡«ä¸­å£å¾„数据单元格:班线/公交/出租城乡/网约车城乡 å››ç»´ç»Ÿä¸€å–库内值(无值清空) */
    /**
     * å¡«ä¸­å£å¾„数据单元格:班线/公交/出租城乡/网约车城乡 å››ç»´ç»Ÿä¸€å–库内值。
     * åº“内该月该维无源(取值为 0)时**保留母版/模板原值**——绝不写空、也不写 0。
     * ä¾æ®ï¼šæ¨¡æ¿çš„城市级城乡行是「台账页 æ€»é‡ âˆ’ åŸŽå¸‚内」的引用、并已业务定稿;
     * ç”¨åº“内缺失值覆盖会把定稿数清掉(如 2026-01~06 ç½‘约车订单缺失时,
     * å…¨çœç½‘约车城乡 333.39 ä¸Žå„市州值会被清空)。
     */
    private void fillMidCell(Cell c, Map<Integer, Map<String, double[][]>> mid, int year, int month, String area, int k, boolean volume) {
        double v = midClassVal(mid, year, month, area, k, volume);
        if (v == 0.0) return; // åº“内无源:保留母版/定稿原值,缺数据不覆盖
        // POI setCellValue(double) å¯¹å…¬å¼å•元格只更新缓存不移除公式,必须先 setBlank å†å†™å€¼
        c.setBlank();
        if (v != 0.0) c.setCellValue(round(v, 4));
        c.setCellValue(round(v, 4));
    }
    /** æ˜¾å¼å†™å…¥ 0(POI setCellValue(0.0) ä¼šè½¬ blank,需操作底层 XML) */
@@ -3445,6 +3467,14 @@
            log.warn("汇总工作簿公式重算:创建求值器失败,跳过({})", e.getMessage());
            return new int[]{0, 0};
        }
        try {
            // è·¨ç°¿å…¬å¼ï¼ˆ[1]出租!C13 ä¹‹ç±»ï¼‰åœ¨å¤–部工作簿缺失时默认抛异常,会让引用它的
            // æ€»è¡Œ/累计/派生公式连锁失败(表现为「公式还在但没有缓存值」)。
            // ç½® true åŽæ”¹ç”¨å…¬å¼æ ¼è‡ªèº«çš„缓存值(即定稿值)参与计算。
            ev.setIgnoreMissingWorkbooks(true);
        } catch (Exception ignore) {
            // ä¸ªåˆ«å®žçŽ°ä¸æ”¯æŒè¯¥å¼€å…³ï¼Œå¿½ç•¥å³å¯
        }
        int ok = 0, fail = 0;
        java.util.List<String> samples = new java.util.ArrayList<>();
        for (int i = 0; i < wb.getNumberOfSheets(); i++) {