xyc
2026-09-21 abf468c9946c21512ddf2093895b30910e09e536
fix(汇总表): 中口径排名累计同比基数改用母版2025静态列
5个文件已修改
1个文件已添加
246 ■■■■■ 已修改文件
HANDOFF.md 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/交接文档_2026-09-21.md 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/功能测试报告/功能测试报告_中口径排名同比基数方案A_2026-09-21.md 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/工作日结/工作日结_2026-09-21.md 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/问题汇总/汇总大表问题交接_2026-09-21.md 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HANDOFF.md
@@ -4,6 +4,20 @@
> **每天结束时**:把当天进展、踩过的坑、新需求、未完成事项更新到本文件,然后可以放心开新对话。
> **协作约定(2026-08-31)**:说「提交」= æœ¬åœ° commit + æŽ¨é€åˆ°è¿œç«¯ git(origin main,Gitblit),不再只做本地 commit。
## æœ€æ–°ï¼ˆ2026-09-21)
- **最新总入口**:`docs/交接文档_2026-09-21.md`(系统现状、P0/P1/P2 é—留、操作手册);汇总大表专项见 `docs/问题汇总/汇总大表问题交接_2026-09-21.md`。
- **本日完成**:
  1. æ‹‰å–并合并 origin/main æœ€æ–°æäº¤ï¼ˆå«æŠ•资系统新模板、十堰源文件清理、汇总大表异步导出等)。
  2. å¤„理“中口径排名 vs ä¸­å£å¾„明细”历史同比基数双轨制:用户采用**方案 A**,排名页累计同比基数改读母版《中口径明细》的 2025 é™æ€åˆ—。
     - é»„冈 2026 å¹´ 1-8 æœˆå®¢è¿é‡ç´¯è®¡åŒæ¯”ç”± `7.744917%` ä¿®æ­£ä¸º `7.641733%`;
     - 17 å¸‚å·ž Ã— å®¢è¿é‡/周转量累计值与同比 68 ä¸ªæ•°å€¼å¯¹å…¨éƒ¨ä¸€è‡´ï¼ˆæœ€å¤§å·® `2.91e-11`);
     - 8 æœˆå½“月同比 34 ä¸ªæ•°å€¼å¯¹æ— å›žå½’(最大差 `5.48e-15`);
     - è§ã€ŠåŠŸèƒ½æµ‹è¯•æŠ¥å‘Š_中口径排名同比基数方案A_2026-09-21.md》。
- **当前环境**:dev åŽç«¯ `8090`、前端 `8080` è¿è¡Œä¸­ï¼›æ•°æ®åº“ `127.0.0.1:3305/traffic_audit`。
- **未完成重点**:2026-09 æ¯ç‰ˆï¼ˆäººå·¥æ¯ç‰ˆæˆ–扩列器)、《亿元投资项目》按 Q7 è¿½åˆ—、导出前基数体检 B4、P1/P2 å…¶ä»–事项(详见总交接文档第 4 ç« ï¼‰ã€‚
- **测试产物**:`_tmp/qa_plan_a_20260921/`(按项目约定保留,未经同意不删)。
## ä»Šæ—¥ï¼ˆ2026-09-04)
### 13. å…³æœºäº¤æŽ¥å¿«ç…§ï¼ˆ09-04 æ™šï¼‰
docs/½»½ÓÎĵµ_2026-09-21.md
@@ -194,7 +194,7 @@
| # | äº‹é¡¹ | è¯´æ˜Ž | çŠ¶æ€ |
| --- | --- | --- | --- |
| P1-1 | ä¸­å£å¾„排名 vs ä¸­å£å¾„明细「历史同期基数双轨制」 | é™¤æ­¦æ±‰å¤– 16 ä¸ªå¸‚州累计同比都有差(黄冈 7.74% vs 7.64%);根因是母版 2025 å¹´ 3、4 æœˆç½‘约车拆分用了一套与出租列自相矛盾的旧输入。有方案 A/B,**建议 A**(排名页改用母版 2025 é™æ€åˆ—) | âŒï¼ˆè¯¦è§æ±‡æ€»å¤§è¡¨äº¤æŽ¥ç¬¬ 4 ç« ï¼‰ |
| P1-1 | ä¸­å£å¾„排名 vs ä¸­å£å¾„明细「历史同期基数双轨制」 | é™¤æ­¦æ±‰å¤– 16 ä¸ªå¸‚州累计同比都有差(黄冈 7.74% vs 7.64%);根因是母版 2025 å¹´ 3、4 æœˆç½‘约车拆分用了一套与出租列自相矛盾的旧输入。**方案 A å·²å®žæ–½å¹¶å›žå½’:排名页改用母版 2025 é™æ€åˆ—**;黄冈 1-8 æœˆç´¯è®¡åŒæ¯”修正为 `7.641733%`,17 å¸‚å·ž Ã— ä¸¤æŒ‡æ ‡å…± 68 ä¸ªåŒæ¯”/累计格全部一致(最大差 `2.91e-11`),当月同比无回归 | âœ…(2026-09-21,详见汇总大表交接第 4 ç« ï¼‰ |
| P1-2 | ä¸­å£å¾„排名全省行差 0.0002 ä¸‡äººå…¬é‡Œ | æ¯ç‰ˆå…¬å¼ `=SUM(O4:O20)` å…ˆå››èˆäº”入再求和的结构性差异,是否改全精度写值需拍板 | â¸ |
| P1-3 | ä¸­å£å¾„明细 4 æ ¼ `#DIV/0!` | 2025 å¹´ 8 æœˆåŸºæ•°ä¸º 0(不可比),是否改空白需业务确认 | â¸ |
| P1-4 | å…¨çœ h2031 1-8 æœˆ 27524.49 vs Î£17 å¸‚å·ž 24481.74 | å·® **3042.75**(企业上报的「全省」含市州明细外的数据,或某市州漏报)。程序一律按母版口径(Σ市州)走 â†’ å»ºè®®ä¸šåŠ¡ä¾§æ ¸ä¸€æ¬¡ | â¸ |
@@ -231,7 +231,7 @@
1. **P0-1**:8 æœˆåå °ç‰©æµæºæ–‡ä»¶çš„「非十堰段」由谁清、什么时候清?(建议:接手人删段并备份)
2. **P0-2**:《亿元投资项目》按月追加列的做法确认(用户 Q7 å·²ç»™è§„则,实施前再确认一次列位)。
3. **P0-4**:2026-09 æ¯ç‰ˆæ€Žä¹ˆåŠž â€”— ç›´æŽ¥æ”¾ä¸€ä»½äººå·¥æ¯ç‰ˆï¼Œè¿˜æ˜¯å…ˆåšæ‰©åˆ—器?(用户正在问同事)
4. **P1-1**:中口径排名历史基数走方案 A è¿˜æ˜¯ B?(建议 A)
4. **P1-1**:中口径排名历史基数——用户已拍板并完成方案 A(排名页改用母版 2025 é™æ€åˆ—),无需再确认。
5. **P1-2 / P1-3**:0.0002 ä¸‡äººå…¬é‡Œæ˜¯å¦æ”¹å…¨ç²¾åº¦å†™å€¼ï¼›`#DIV/0!` æ˜¯å¦æ”¹ç©ºç™½ã€‚
6. **P1-4**:h2031 å…¨çœè¡Œä¸Ž Î£17 å¸‚å·žå·® 3042.75 çš„业务解释。
7. **P1-6**:荆州 / é»„石「规划内 / è§„划外」口径。
@@ -321,3 +321,4 @@
| --- | --- |
| 2026-09-21 | é¦–次成文:整合汇总大表交接(`docs/问题汇总/汇总大表问题交接_2026-09-21.md`)与投资/货运/客运各线遗留 |
| 2026-09-21(下午) | P0-1 é—­çŽ¯ï¼ˆåå °æºæ–‡ä»¶å·²åˆ æ®µ + é‡å¯¼éªŒè¯ï¼‰ï¼›æ–°å¢ž P1-13(投资系统新模板支持);数据快照补 `investment_system` è¡Œ |
| 2026-09-21(下午) | P1-1 é—­çŽ¯ï¼šç”¨æˆ·é‡‡ç”¨æ–¹æ¡ˆ A,中口径排名的 2025 ç´¯è®¡åŸºæ•°æ”¹è¯»æ¯ç‰ˆã€Šä¸­å£å¾„明细》静态列;黄冈累计同比由 7.74% ä¿®æ­£ä¸º 7.641733%,双页一致性回归通过 |
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_Öпھ¶ÅÅÃûͬ±È»ùÊý·½°¸A_2026-09-21.md
New file
@@ -0,0 +1,66 @@
# åŠŸèƒ½æµ‹è¯•æŠ¥å‘Šï¼šä¸­å£å¾„æŽ’ååŒæ¯”åŸºæ•°æ–¹æ¡ˆ A(2026-09-21)
## 1. éœ€æ±‚与方案
用户针对 2026 å¹´ 1-8 æœˆæ±‡æ€»å¤§è¡¨æå‡ºé—®é¢˜ï¼š
- ã€Œä¸­å£å¾„排名」黄冈累计同比系统生成 `7.744917%`
- ã€Œä¸­å£å¾„明细」同一指标为 `7.641733%`
- è¦æ±‚查明并修正。
按用户拍板采用**方案 A**:
- ã€Œä¸­å£å¾„排名」计算去年同期累计时,不再使用库内现算的 2025 ç½‘约车拆分结果;
- æ”¹ç”¨æ¯ç‰ˆã€Šä¸­å£å¾„明细》页已有的 2025 å¹´é€æœˆé™æ€å®šç¨¿å€¼ï¼›
- 2026 å¹´å½“年累计值、排名公式、全省 SUM å…¬å¼å’Œå½“月同比逻辑均不变;
- æ‰¾ä¸åˆ°æ¯ç‰ˆé™æ€åˆ—时保留原库内现算逻辑,并写 WARN æ—¥å¿—。
## 2. å®žçŽ°èŒƒå›´
文件:`traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java`
- `exportPassengerMidRank()` å¢žåŠ å¯é€‰å·¥ä½œç°¿ä¸Šä¸‹æ–‡é‡è½½ã€‚
- æ±‡æ€»å¤§è¡¨å¯¼å‡ºæ—¶ç›´æŽ¥å¤ç”¨å½“前内存工作簿,读取《中口径明细》2025 å¹´é™æ€åˆ—。
- ç‹¬ç«‹å¯¼å‡ºã€Šç”Ÿæˆ_中口径排名.xlsx》时,按报表期打开对应汇总母版读取同一静态列。
- é™æ€åˆ—按表头文字识别,不写死列字母。
- è¯»å– 17 å¸‚州「总客运量」「总旅客周转量」1..N æœˆå€¼å¹¶ç´¯è®¡ï¼›å…¨çœåŸºæ•°ç”± 17 å¸‚州求和。
- ä»»ä¸€å¸‚州、任一指标缺少完整月份时放弃静态基数并回退原逻辑,避免静默使用残缺基数。
## 3. éªŒè¯çŽ¯å¢ƒ
- åŽç«¯ï¼šSpring Boot `8090`
- æ•°æ®åº“:`127.0.0.1:3305/traffic_audit`
- æŠ¥è¡¨æœŸï¼š`2026-08`
- æ¯ç‰ˆï¼š`docs/生成汇总大表/2026å¹´8月道路运输量汇总表.xlsx`
- æµ‹è¯•产物:
  - `_tmp/qa_plan_a_20260921/生成_道路运输量汇总表_202608_方案A.xlsx`
  - `_tmp/qa_plan_a_20260921/生成_中口径排名_202608_方案A.xlsx`
## 4. æµ‹è¯•结果
| ç¼–号 | ç”¨ä¾‹ | é¢„期 | å®žæµ‹ | ç»“æžœ |
| --- | --- | --- | --- | --- |
| A01 | åŽç«¯ç¼–译 | ç¼–译成功 | `mvn -DskipTests compile` æˆåŠŸ | é€šè¿‡ |
| A02 | 2026-08 æ±‡æ€»å¤§è¡¨ç”Ÿæˆ | æˆåŠŸäº§å‡ºå¯è¯» xlsx | ç”ŸæˆæˆåŠŸï¼Œ1325107 bytes | é€šè¿‡ |
| A03 | é»„冈客运量累计同比 | æŽ’名=明细=`7.641733%` | æŽ’名 `0.07641733462732472`,明细 `0.07641733462732603` | é€šè¿‡ |
| A04 | é»„冈周转量累计同比 | æŽ’名=明细 | æŽ’名 `0.01593727813242681`,明细 `0.015937278132423405` | é€šè¿‡ |
| A05 | 17 å¸‚å·ž Ã— å®¢è¿é‡/周转量累计同比 | 34 ç»„逐格一致 | å«ç´¯è®¡å€¼åœ¨å†…å…± 68 ä¸ªæ•°å€¼å¯¹å…¨éƒ¨ä¸€è‡´ï¼Œæœ€å¤§ç»å¯¹å·® `2.91e-11` | é€šè¿‡ |
| A06 | å…¨çœç´¯è®¡åŒæ¯” | æŽ’名=明细 | å®¢è¿é‡ï¼šæŽ’名 `0.0581055773685379`,明细 `0.058105577368537276`;周转量同样一致 | é€šè¿‡ |
| A07 | å…¨çœç´¯è®¡å€¼=Σ17 å¸‚å·ž | å®Œå…¨ç›¸ç­‰ | å®¢è¿é‡ `31528.060699999998`;周转量 `1227147.4890200002`,均相等 | é€šè¿‡ |
| A08 | 2026-08 å½“月同比回归 | æŽ’名=明细 | 34 ä¸ªå¸‚州指标全部一致,最大绝对差 `5.48e-15` | é€šè¿‡ |
| A09 | ç‹¬ç«‹ã€Šç”Ÿæˆ_中口径排名.xlsx》 | ä½¿ç”¨åŒä¸€é™æ€åŸºæ•° | é»„冈客运量 `7.641733%`、周转量 `1.593728%`,与明细一致 | é€šè¿‡ |
| A10 | é™æ€åŸºæ•°ç¼ºå¤±å…œåº• | ä¸ä¸­æ–­å¯¼å‡ºï¼Œå›žé€€çŽ°ç®—å¹¶è®°æ—¥å¿— | ä»£ç çº§æ£€æŸ¥ï¼šæ¯ç‰ˆ/静态列缺失返回 `null`,沿用原 `midCumYoy()` | é€šè¿‡ |
## 5. ç»“论
方案 A å·²å®žæ–½å¹¶å®Œæˆå›žå½’。
- åŽŸæ¥çš„ `7.74%` ä¸Ž `7.64%` åŒè½¨å·®å¼‚已消除;
- 2026-08 æ±‡æ€»å¤§è¡¨ä¸­ï¼Œé»„冈累计同比为 `7.641733%`;
- æŽ’名页与中口径明细页的累计同比、累计值、全省汇总均一致;
- å½“月同比未发生回归;
- æœªä¿®æ”¹ä»»ä½•数据库表结构或业务数据。
## 6. å¤‡æ³¨
项目根目录 `_tmp/qa_plan_a_20260921/` ä¸‹çš„æµ‹è¯•产物按项目约定保留,未经用户同意不删除。
docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-21.md
@@ -80,8 +80,7 @@
## ä¸‰ã€é—留问题与待办
1. **P0-1**:历史同比基数双轨制 â€”— æ–¹æ¡ˆ A(排名页改用母版 2025 é™æ€åˆ—,推荐)/ æ–¹æ¡ˆ B(明细页改现算)
   å¾…用户拍板,**未实施**。
1. **P0-1 å·²è§£å†³**:用户选择方案 A(排名页改用母版 2025 é™æ€åˆ—),已于 2026-09-21 å®žæ–½å¹¶å›žå½’通过,见本文件第六节。
2. **P0-2**:母版依赖 â€”— 9 æœˆå‡ºè¡¨éœ€å…ˆæœ‰æ–°æ¯ç‰ˆï¼›æ–¹æ¡ˆ A(程序自动扩列,2-3 å¤©ï¼‰/ æ–¹æ¡ˆ B(Excel å°å·¥å…·ï¼Œ1 å¤©ï¼‰å¾…拍板。
3. P2:B4 å¯¼å‡ºå‰åŸºæ•°ä½“检 + ã€Šæ•°æ®ç¼ºå¤±æ¸…单》页、D ç»„增速排名措辞、中口径明细 4 æ ¼ `#DIV/0!`、
   ç¼ºæ¯ç‰ˆæç¤ºå®žæµ‹ã€éƒ¨ç½²åŒ…旧母版(用户明确暂不处理)。
@@ -130,3 +129,15 @@
- é‡æ–°éªŒè¯ï¼ˆä¸‰å±‚,全过):结构 `CREATE TABLE` 29/29;逐表 `COUNT(*)` 29/29 ä¸€è‡´ï¼›`CHECKSUM TABLE` 29/29 ä¸€è‡´ï¼›`mysqldump --order-by-primary` æœ‰åºå¯¼å‡ºä¸¤ä»½æ•°æ®æ–‡ä»¶ **SHA256 å®Œå…¨ç›¸åŒ**(逐行内容一致)。
- **经验**:备份只是某一时刻的快照,导完之后只要还在导数据/跑审核就旧了;交付前必须最后再导一次。
- æ–°å¢žæ–‡æ¡£ï¼š`docs/工具/数据库备份与还原操作步骤.md`(备份参数说明 / ä¸‰æ­¥éªŒè¯ / è¿˜åŽŸ / å¸¸è§é—®é¢˜ï¼Œå‘½ä»¤å¯ç›´æŽ¥å¤åˆ¶ï¼‰ã€‚
## å…­ã€è¿½åŠ ï¼šP0-1 åŽ†å²åŒæ¯”åŸºæ•°åŒè½¨åˆ¶æŒ‰æ–¹æ¡ˆ A ä¿®å¤
用户选择 **方案 A**(排名页改用母版《中口径明细》2025 é™æ€åˆ—),已完成代码、回归与文档更新。
- ä»£ç ï¼š`ReportExportService.exportPassengerMidRank()` å¢žåŠ æ¯ç‰ˆå·¥ä½œç°¿ä¸Šä¸‹æ–‡é‡è½½ï¼›æ±‡æ€»å¯¼å‡ºå¤ç”¨å†…å­˜æ¯ç‰ˆï¼Œç‹¬ç«‹æŽ’åè¡¨å¯¼å‡ºæ‰“å¼€å¯¹åº”æ¯ç‰ˆï¼›æŒ‰è¡¨å¤´è¯†åˆ« 2025 å¹´é€æœˆåˆ—,读取「总客运量 / æ€»æ—…客周转量」并按 1..N æœˆç´¯è®¡ï¼›å…¨çœåŸºæ•°ç”± 17 å¸‚州求和;静态基数不完整时回退库内现算并写 WARN。
- ç¼–译:`mvn -DskipTests compile` é€šè¿‡ã€‚
- ç”Ÿæˆå›žå½’:2026-08 æ±‡æ€»å¤§è¡¨ï¼›é»„冈客运量累计同比排名/明细均为 `7.641733%`(原 7.744917%)。
- åŒé¡µä¸€è‡´æ€§ï¼š17 å¸‚å·ž Ã— å®¢è¿é‡/周转量的累计值与同比共 68 ä¸ªæ•°å€¼å¯¹å…¨éƒ¨ä¸€è‡´ï¼Œæœ€å¤§ç»å¯¹å·® `2.91e-11`;全省累计值 = Î£17 å¸‚å·žï¼›8 æœˆå½“月同比 34 ä¸ªæ•°å€¼å¯¹æœ€å¤§å·® `5.48e-15`。
- ç‹¬ç«‹ã€Šç”Ÿæˆ_中口径排名.xlsx》也按同一静态口径导出,结果与明细页一致。
- æµ‹è¯•产物:`_tmp/qa_plan_a_20260921/生成_道路运输量汇总表_202608_方案A.xlsx`、`_tmp/qa_plan_a_20260921/生成_中口径排名_202608_方案A.xlsx`(按约定保留)。
- æµ‹è¯•报告:`docs/功能测试报告/功能测试报告_中口径排名同比基数方案A_2026-09-21.md`。
docs/ÎÊÌâ»ã×Ü/»ã×Ü´ó±íÎÊÌâ½»½Ó_2026-09-21.md
@@ -8,7 +8,7 @@
8 æœˆæ±‡æ€»å¤§è¡¨å·²èƒ½åšåˆ°ã€Œå¯¼å…¥åŸºç¡€æ•°æ® â†’ ä¸€é”®ç”Ÿæˆã€ï¼Œä¸Žäººå·¥æ¯ç‰ˆé€æ ¼æ¯”对基本一致。但有两个高危遗留:
1. **历史同期(2025 å¹´ï¼‰åˆ—在两套口径之间没有统一** â€”— ã€Œä¸­å£å¾„排名」页与「中口径明细」页的累计同比对不上(黄冈 7.74% vs 7.64%),**这是第一优先问题**。
1. ~~**历史同期(2025 å¹´ï¼‰åˆ—在两套口径之间没有统一** â€”— ã€Œä¸­å£å¾„排名」页与「中口径明细」页的累计同比对不上(黄冈 7.74% vs 7.64%),**这是第一优先问题**。~~ **已于 2026-09-21 æŒ‰æ–¹æ¡ˆ A è§£å†³ï¼Œè§ç¬¬ 4 ç« ã€‚**
2. **每月要人工准备一份新母版**,程序不会自己扩列,9 æœˆåŠä»¥åŽä¸è§£å†³å°±ä¼šå¡ä½å‡ºè¡¨ã€‚
## 1. å‰ç½®é˜…读(先看这 5 ä»½ï¼Œçº¦ 30 åˆ†é’Ÿå»ºç«‹æ¦‚念)
@@ -40,7 +40,7 @@
| ç¼–号 | é—®é¢˜ | çŠ¶æ€ | ä¼˜å…ˆçº§ |
| --- | --- | --- | --- |
| P0-1 | åŽ†å²åŒæ¯”åŸºæ•°ã€ŒåŒè½¨åˆ¶ã€ï¼šä¸­å£å¾„æŽ’å vs ä¸­å£å¾„明细 ç´¯è®¡åŒæ¯”全市州不一致 | **未解决** | é«˜ |
| P0-1 | åŽ†å²åŒæ¯”åŸºæ•°ã€ŒåŒè½¨åˆ¶ã€ï¼šä¸­å£å¾„æŽ’å vs ä¸­å£å¾„明细 ç´¯è®¡åŒæ¯”全市州不一致 | **已解决(2026-09-21,方案 A)** | é«˜ |
| P0-2 | æ¯ç‰ˆä¾èµ–:每月要人工准备新母版,程序不会自己扩列 | **未解决** | é«˜ |
| P1-1 | åŸŽå¸‚客运页 AJ åˆ—(2025å¹´1-7月累计)残留在生成表 | å·²ä¿®å¤ 09-20 | - |
| P1-2 | ä¸­å£å¾„排名周转量口径与人工列不一致 | å·²ä¿®å¤ 09-19 | - |
@@ -52,6 +52,8 @@
| P2-4 | ç¼ºæ¯ç‰ˆæç¤ºæœªå®žæµ‹ï¼›éƒ¨ç½²åŒ…内是带 AJ åˆ—的旧母版 | å¾…处理 | ä½Ž |
## 4. P0-1 åŽ†å²åŒæ¯”åŸºæ•°ã€ŒåŒè½¨åˆ¶ã€ï¼ˆæœ¬æ¬¡äº¤æŽ¥é‡ç‚¹ï¼‰
> **状态更新(2026-09-21):用户采用方案 A,已实施并回归通过。** æŽ’名页的 2025 ç´¯è®¡åŸºæ•°æ”¹è¯»æ¯ç‰ˆã€Šä¸­å£å¾„明细》静态列;黄冈 1-8 æœˆç´¯è®¡åŒæ¯”ç”± 7.744917% ä¿®æ­£ä¸º 7.641733%,与明细页一致。
### 4.1 çŽ°è±¡
@@ -118,7 +120,11 @@
- **全省行两页完全一致**(31528.0607 / 5.810558%),因为这是**市州之间的分摊差异**,全省加总相互抵消。
### 4.6 å¤„置方案(待拍板,尚未实施)
### 4.6 å¤„置方案(2026-09-21:方案 A å·²å®žæ–½ï¼‰
**实施结果**:`exportPassengerMidRank()` åœ¨æ±‡æ€»å¯¼å‡ºä¸­å¤ç”¨å†…存母版,在独立排名表导出中打开对应母版,按表头读取《中口径明细》2025 å¹´ã€Œæ€»å®¢è¿é‡ã€ã€Œæ€»æ—…客周转量」1..N æœˆé™æ€å€¼ï¼›å…¨çœåŸºæ•°ç”± 17 å¸‚州求和。静态列不完整时自动回退原库内现算并写 WARN æ—¥å¿—。
回归证据:2026-08 æ±‡æ€»å¤§è¡¨é»„冈客运量累计同比排名/明细均为 `7.641733%`;17 å¸‚å·ž Ã— å®¢è¿é‡/周转量的累计值与累计同比共 68 ä¸ªæ•°å€¼å¯¹å…¨éƒ¨ä¸€è‡´ï¼Œæœ€å¤§ç»å¯¹å·® `2.91e-11`;当月同比 34 ä¸ªæ•°å€¼å¯¹æœ€å¤§å·® `5.48e-15`。详见《功能测试报告_中口径排名同比基数方案A_2026-09-21.md》。
- **方案 A(推荐,改动小)**:让 `exportPassengerMidRank()` å–「去年同期累计」时,改用母版那份 2025 é™æ€åˆ—(与中口径明细同源)。
  - ç†ç”±ï¼šå¯¹å¤–发布的同比,基数本该用去年定稿值;只动排名页 `midCumYoy/midCumClass` çš„取数来源,2026 å½“月值不受影响。
@@ -237,7 +243,7 @@
## 11. å¾…用户/业务确认清单(接手人先别自己拍)
1. P0-1 èµ°æ–¹æ¡ˆ A è¿˜æ˜¯ B?(建议 A)
1. ~~P0-1 èµ°æ–¹æ¡ˆ A è¿˜æ˜¯ B?(建议 A)~~ ç”¨æˆ·å·²é€‰æ‹©æ–¹æ¡ˆ A,2026-09-21 å·²å®žæ–½å¹¶å›žå½’通过。
2. P0-2 èµ°æ–¹æ¡ˆ A(程序自动扩列)还是 B(Excel å°å·¥å…·ï¼‰ï¼Ÿ9 æœˆæ˜¯å¦å…ˆç”¨ B åº”急?
3. ä¸­å£å¾„明细 4 æ ¼ `#DIV/0!` æ˜¯å¦æ”¹ç©ºç™½ï¼Ÿ
4. B4 åŸºæ•°ä½“检的触发时机与弹窗文案。
@@ -245,6 +251,6 @@
## 12. å»ºè®®çš„æŽ¥æ‰‹é¡ºåº
1. è·‘通环境 + ç”Ÿæˆä¸€æ¬¡ 2026-08 æ±‡æ€»å¤§è¡¨ï¼Œç¡®è®¤èƒ½å¤çŽ°æœ¬æ–‡æ‰€æœ‰çŽ°è±¡ï¼ˆåŠå¤©ï¼‰ã€‚
2. å¤„理 P0-1 æ–¹æ¡ˆ A(1 å¤©ï¼Œå«å›žå½’)。
2. ~~处理 P0-1 æ–¹æ¡ˆ A(1 å¤©ï¼Œå«å›žå½’)。~~ å·²äºŽ 2026-09-21 å®Œæˆã€‚
3. å¤„理 P0-2 æ–¹æ¡ˆ B å‡º 9 æœˆæ¯ç‰ˆï¼ˆ1 å¤©ï¼‰ï¼ŒéšåŽæŽ’ A。
4. è¡¥ B4 åŸºæ•°ä½“检(1-2 å¤©ï¼‰ã€‚
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -2681,9 +2681,19 @@
     * ç”Ÿæˆ_中口径排名.xlsx:单块累计(选 1 ä¸ªæœˆ=当月,选 1-6 æœˆ=累计),RANK/SUM å…¬å¼ä¿ç•™ã€‚
     */
    public byte[] exportPassengerMidRank(String period, String mode) throws Exception {
        return exportPassengerMidRank(period, mode, null);
    }
    /**
     * æ–¹æ¡ˆ A(2026-09-21):中口径排名的累计同比基数与“中口径明细”保持同源。
     * æ˜Žç»†é¡µçš„ 2025 å¹´åˆ—是母版静态定稿值,排名页原先查库现算,两者在 2025-03/04 ç½‘约车拆分上不同。
     * summaryWb éžç©ºæ—¶ç›´æŽ¥å¤ç”¨å½“前正在生成的汇总工作簿;独立导出时再打开对应母版。
     */
    private byte[] exportPassengerMidRank(String period, String mode, XSSFWorkbook summaryWb) throws Exception {
        int maxMonth = monthOf(period, mode);
        int currentYear = Integer.parseInt(period.substring(0, 4));
        File template = resolvePassengerTemplate("生成_中口径排名.xlsx");
        Map<String, double[]> lastYearCumBase = loadMidRankStaticBase(period, mode, summaryWb, currentYear, maxMonth);
        try (InputStream in = new FileInputStream(template);
             XSSFWorkbook wb = new XSSFWorkbook(in)) {
            Sheet sheet = wb.getSheetAt(0);
@@ -2703,8 +2713,8 @@
                title.getCell(10).setCellValue(currentYear + "å¹´" + maxMonth + "月全省分市州完成道路客运生产情况");
            }
            // å…¨çœè¡Œ r3:B3/F3 ä¸º SUM å…¬å¼ä¿ç•™ï¼›D3/H3 åŒæ¯”填值(0-based åˆ— 3/7)
            setValOrBlank(sheet, 2, 3, midCumYoy(mid, currentYear, "湖北省", maxMonth, 0, true));
            setValOrBlank(sheet, 2, 7, midCumYoy(mid, currentYear, "湖北省", maxMonth, 0, false));
            setValOrBlank(sheet, 2, 3, midRankCumYoy(mid, currentYear, "湖北省", maxMonth, true, lastYearCumBase));
            setValOrBlank(sheet, 2, 7, midRankCumYoy(mid, currentYear, "湖北省", maxMonth, false, lastYearCumBase));
            // å³å—全省行 N3/R3:当月同比(L3/P3 ä¸º SUM å…¬å¼ä¿ç•™ï¼‰
            setValOrBlank(sheet, 2, 13, midMonthYoy(mid, currentYear, maxMonth, "湖北省", 0, true));
            setValOrBlank(sheet, 2, 17, midMonthYoy(mid, currentYear, maxMonth, "湖北省", 0, false));
@@ -2715,17 +2725,13 @@
                int r0 = 3 + i;
                double pass = midCumClass(mid, currentYear, city, maxMonth, 0, true);
                double turn = midCumClass(mid, currentYear, city, maxMonth, 0, false);
                double lastPass = midCumClass(mid, currentYear - 1, city, maxMonth, 0, true);
                double lastTurn = midCumClass(mid, currentYear - 1, city, maxMonth, 0, false);
                setValOrBlank(sheet, r0, 1, pass == 0 ? null : pass);
                setValOrBlank(sheet, r0, 3, midCumYoy(mid, currentYear, city, maxMonth, 0, true));
                setValOrBlank(sheet, r0, 3, midRankCumYoy(mid, currentYear, city, maxMonth, true, lastYearCumBase));
                setValOrBlank(sheet, r0, 5, turn == 0 ? null : turn);
                setValOrBlank(sheet, r0, 7, midCumYoy(mid, currentYear, city, maxMonth, 0, false));
                setValOrBlank(sheet, r0, 7, midRankCumYoy(mid, currentYear, city, maxMonth, false, lastYearCumBase));
                // å³å—当月:L/P å€¼ã€N/R å½“月同比(M/O/Q/S æŽ’名公式保留)
                double mPass = midClassVal(mid, currentYear, maxMonth, city, 0, true);
                double mTurn = midClassVal(mid, currentYear, maxMonth, city, 0, false);
                double mLastPass = midClassVal(mid, currentYear - 1, maxMonth, city, 0, true);
                double mLastTurn = midClassVal(mid, currentYear - 1, maxMonth, city, 0, false);
                setValOrBlank(sheet, r0, 11, mPass == 0 ? null : mPass);
                setValOrBlank(sheet, r0, 13, mPass == 0 ? null : midMonthYoy(mid, currentYear, maxMonth, city, 0, true));
                setValOrBlank(sheet, r0, 15, mTurn == 0 ? null : mTurn);
@@ -2734,6 +2740,112 @@
            recalc(wb);
            return toBytes(wb);
        }
    }
    /** è¯»å–母版《中口径明细》的 2025 å¹´é€æœˆé™æ€å€¼ï¼Œæ±‡æ€»æˆæŽ’名页所需的去年同期累计基数。 */
    private Map<String, double[]> readMidDetailLastYearCumulative(XSSFWorkbook sourceWb, int currentYear, int maxMonth) {
        Map<String, double[]> out = new LinkedHashMap<>();
        if (sourceWb == null || maxMonth <= 0) return out;
        Sheet sheet = sourceWb.getSheet("中口径明细");
        if (sheet == null) return out;
        Map<Integer, Integer> monthCols = new LinkedHashMap<>();
        java.util.regex.Pattern monthHeader = java.util.regex.Pattern.compile("^(\\d{4})å¹´(\\d{1,2})月$");
        int headerEnd = Math.min(sheet.getLastRowNum(), sheet.getFirstRowNum() + 5);
        for (int r = sheet.getFirstRowNum(); r <= headerEnd; r++) {
            Row row = sheet.getRow(r);
            if (row == null) continue;
            for (int c = row.getFirstCellNum(); c >= 0 && c < row.getLastCellNum(); c++) {
                String text = cellText(row.getCell(c));
                if (text == null) continue;
                java.util.regex.Matcher matcher = monthHeader.matcher(text.trim());
                if (!matcher.matches()) continue;
                int y = Integer.parseInt(matcher.group(1));
                int m = Integer.parseInt(matcher.group(2));
                if (y == currentYear - 1 && m >= 1 && m <= maxMonth) monthCols.putIfAbsent(m, c);
            }
        }
        if (monthCols.size() < maxMonth) return out;
        for (int r = sheet.getFirstRowNum(); r <= sheet.getLastRowNum(); r++) {
            Row row = sheet.getRow(r);
            if (row == null) continue;
            String city = RegionUtil.normalizeCityName(cellText(row.getCell(0)));
            if (city == null || !RegionUtil.CITY_LIST.contains(city)) continue;
            String metric = cellText(row.getCell(1));
            if (metric == null) continue;
            int idx = metric.contains("总旅客周转量") ? 1 : (metric.contains("总客运量") ? 0 : -1);
            if (idx < 0) continue;
            double sum = 0.0;
            boolean complete = true;
            for (int m = 1; m <= maxMonth; m++) {
                Cell c = row.getCell(monthCols.get(m));
                if (c == null) { complete = false; break; }
                if (c.getCellType() == CellType.NUMERIC) {
                    sum += c.getNumericCellValue();
                } else if (c.getCellType() == CellType.FORMULA && c.getCachedFormulaResultType() == CellType.NUMERIC) {
                    sum += c.getNumericCellValue();
                } else {
                    complete = false;
                    break;
                }
            }
            double[] values = out.computeIfAbsent(city, k -> new double[]{Double.NaN, Double.NaN});
            values[idx] = complete ? sum : Double.NaN;
        }
        double[] province = new double[2];
        for (String city : RegionUtil.CITY_LIST) {
            double[] values = out.get(city);
            if (values == null || Double.isNaN(values[0]) || Double.isNaN(values[1])) return new LinkedHashMap<>();
            province[0] += values[0];
            province[1] += values[1];
        }
        out.put("湖北省", province);
        return out;
    }
    /** ç‹¬ç«‹å¯¼å‡ºæ—¶ä»Žå¯¹åº”汇总母版读取静态基数;汇总导出时直接复用内存中的工作簿,避免重复 I/O。 */
    private Map<String, double[]> loadMidRankStaticBase(String period, String mode, XSSFWorkbook summaryWb,
                                                       int currentYear, int maxMonth) {
        if (summaryWb != null) {
            Map<String, double[]> base = readMidDetailLastYearCumulative(summaryWb, currentYear, maxMonth);
            if (!base.isEmpty()) return base;
            log.warn("中口径排名:当前汇总工作簿的中口径明细未读到完整 2025 é™æ€ç´¯è®¡åŸºæ•°ï¼Œå›žé€€åº“内现算");
            return null;
        }
        double savedZipRatio = ZipSecureFile.getMinInflateRatio();
        ZipSecureFile.setMinInflateRatio(0.0001);
        try {
            File mother = resolveSummaryMother(period, mode);
            if (mother == null || !mother.isFile()) {
                log.warn("中口径排名:未找到 {} çš„æ±‡æ€»æ¯ç‰ˆï¼Œ2025 ç´¯è®¡åŸºæ•°å›žé€€åº“内现算", period);
                return null;
            }
            try (InputStream in = new FileInputStream(mother); XSSFWorkbook wb = new XSSFWorkbook(in)) {
                Map<String, double[]> base = readMidDetailLastYearCumulative(wb, currentYear, maxMonth);
                if (base.isEmpty()) {
                    log.warn("中口径排名:母版 {} çš„中口径明细未读到完整 2025 é™æ€ç´¯è®¡åŸºæ•°ï¼Œå›žé€€åº“内现算", mother.getName());
                    return null;
                }
                log.info("中口径排名:2025 ç´¯è®¡åŸºæ•°æ”¹ç”¨æ¯ç‰ˆé™æ€åˆ—({})", mother.getName());
                return base;
            }
        } catch (Exception e) {
            log.warn("中口径排名:读取母版 2025 é™æ€ç´¯è®¡åŸºæ•°å¤±è´¥ï¼Œå›žé€€åº“内现算:{}", e.getMessage());
            return null;
        } finally {
            ZipSecureFile.setMinInflateRatio(savedZipRatio);
        }
    }
    /** ä¸­å£å¾„排名累计同比:方案 A æœ‰é™æ€åŸºæ•°æ—¶ä¸Žå…¶å¯¹é½ï¼Œå¦åˆ™ä¿ç•™åŽŸåº“å†…çŽ°ç®—é€»è¾‘ã€‚ */
    private Double midRankCumYoy(Map<Integer, Map<String, double[][]>> mid, int year, String area,
                                 int monthCount, boolean volume, Map<String, double[]> staticBase) {
        if (staticBase == null) return midCumYoy(mid, year, area, monthCount, 0, volume);
        double[] values = staticBase.get(area);
        if (values == null) return null;
        double current = midCumClass(mid, year, area, monthCount, 0, volume);
        if (current == 0.0) return null;
        double base = volume ? values[0] : values[1];
        return base == 0.0 ? null : yoyOf(current, base);
    }
    /** å†™æ•°å€¼æˆ–清空(公式单元格保留不动) */
@@ -3754,7 +3866,7 @@
        int n = 0;
        n += copyRankValues(wb, "货运量排名", exportFreightRank(period, mode), new int[][]{{0, 0}, {22, 0}});
        n += copyRankValues(wb, "货运周转量排名", exportTurnoverRank(period, mode), new int[][]{{0, 0}, {22, 0}});
        n += copyRankValues(wb, "中口径排名", exportPassengerMidRank(period, mode), new int[][]{{0, 0}, {0, 10}});
        n += copyRankValues(wb, "中口径排名", exportPassengerMidRank(period, mode, wb), new int[][]{{0, 0}, {0, 10}});
        return n;
    }