feat: 网约车拆分表与道路运输量汇总整本生成、全表两小数显示 + 09-03 测试文档与空母版
6个文件已修改
13个文件已添加
861 ■■■■■ 已修改文件
AGENTS.md 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/公路旅客/模板_个体客运(2026.7).xlsx 补丁 | 查看 | 原始文档 | blame | 历史
docs/功能测试报告/功能测试报告_公路旅客报表并入个体客运_2026-09-03.md 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/功能测试报告/功能测试报告_城市客运新增网约车拆分表与汇总工作簿及两位小数显示_2026-09-03.md 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/功能测试报告/功能测试报告_汇总工作簿骨架生成_2026-09-03.md 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/城市客运/网约车订单及全省总量.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
docs/工作日结/工作日结_2026-09-03.md 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/待业务用户确认清单_2026-09-02.md 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/报表生成_关系逻辑与每日进度总表_2026-09-03.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
docs/生成汇总大表/2026年7月道路运输量汇总表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
docs/生成汇总大表/2026年7月道路运输量汇总表_7月清空测试版.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
docs/生成汇总大表/2026年7月道路运输量汇总表_7月清空测试版v2.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
docs/生成汇总大表/自动生成汇总工作簿落地方案.md 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/生成汇总大表/输入输出与逻辑关系总表_2026-09-03.md 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/生成汇总大表/页签逻辑关系说明.md 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/controller/ReportExportController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java 465 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-web/src/views/DataImport.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-web/src/views/ReportExport.vue 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
AGENTS.md
@@ -43,3 +43,7 @@
   - å†™å…¥ `docs/工作日结/`,命名:`工作日结_<YYYY-MM-DD>.md`。
   - å†…容至少包含:今天做了什么(功能/修复)、测试结果摘要、遗留问题与待办、明天计划。
   - å½“天没有代码/测试工作时,也要写(一句话说明“今日无代码改动”即可)。
3. **git å·¥ä½œæµï¼ˆ2026-09-03 ç”¨æˆ·æŽˆæƒï¼Œæ— éœ€å†è¯¢é—®æ˜¯å¦æŽ¨é€ï¼‰**
   - æ¯æ¬¡ä¿®æ”¹ä»£ç /文档后:先测试并写《功能测试报告》/更新《工作日结》,再 `git fetch gitea` ä¸Žè¿œç«¯ `main` å¯¹æ¯”合并(必要时解决冲突),确认无误后 commit å¹¶ push åˆ°å…¬å¸ gitea(`http://61.183.254.94:3000/r/trafficAudit.git`,账号 zyj)。
   - è‹¥è¿œç«¯å·²æœ‰åŒäº‹æ–°æäº¤ï¼šå¿…须先合并/基于最新再推,禁止强推覆盖同事代码。
   - å…¬å¸ git è´¦å·å¯†ç åªé€šè¿‡æœ¬æœº git å‡­æ®/临时 askpass ä½¿ç”¨ï¼Œç¦æ­¢å†™è¿›ä»“库内任何文档或提交内容。
docs/¹«Â·ÂÿÍ/Ä£°å_¸öÌå¿ÍÔË£¨2026.7£©.xlsx
Binary files differ
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_¹«Â·Âÿͱ¨±í²¢Èë¸öÌå¿ÍÔË_2026-09-03.md
New file
@@ -0,0 +1,33 @@
# åŠŸèƒ½æµ‹è¯•æŠ¥å‘Šï¼šå…¬è·¯æ—…å®¢æŠ¥è¡¨å¹¶å…¥ä¸ªä½“å®¢è¿ï¼ˆ2026-09-03)
## åŠŸèƒ½è¯´æ˜Ž
按 2026-09-03 ä¸šåŠ¡å£å¾„ç¡®è®¤ï¼ˆã€Šå¾…ä¸šåŠ¡ç”¨æˆ·ç¡®è®¤æ¸…å•ã€‹#1/#2/#3):
- åˆ†å¸‚州表《生成_公路旅客分市州》客运量/周转量 æ€»è¡Œ = ä¼ä¸š(H2031) + ä¸ªä½“,其中个体行保留、只显示个体;
- ä¸­å£å¾„ æ˜Žç»†/排名/分析 ä¸‰è¡¨çš„ æ€»é‡è¡Œã€å…¬è·¯ç­çº¿è¡Œ å®¢è¿é‡/周转量 åŒæ ·å¹¶å…¥ä¸ªä½“(同比同口径,无去年数据留空);
- å­æ„Ÿä¸ªä½“库内 2 è¡Œï¼ŒæŠ¥è¡¨å±‚按市州合并为 1 è¡Œï¼ˆä¸æ”¹å¯¼å…¥åŽ»é‡ï¼‰ã€‚
代码改动:traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
- passengerMonthVal:metricIdx 0/1(客运量/周转量总行)改为 ä¼ä¸š+个体;
- loadMidClassMap:个体客运数据并入 dim1(公路班线)/dim0(总量)。
备注:本次代码未提交、未推送。
## æµ‹è¯•环境
- åŽç«¯ 8090(spring-boot:run);数据库 MySQL 127.0.0.1:3308/traffic_audit。
- æ•°æ®ï¼š2026-07 H2031 ä¼ä¸šæœˆæŠ¥ 771 è¡Œ + ä¸ªä½“客运 7 è¡Œï¼ˆ6 å¸‚州,其中孝感 2 è¡Œï¼‰ï¼›2026-03 ä¼ä¸š 778 è¡Œï¼›åŸŽå¸‚公交城乡 2026-06/07 å„ 142 è¡Œã€‚
- æ¨¡æ¿ï¼šdocs/公路旅客/ ä¸‹ ç”Ÿæˆ_公路旅客分市州.xlsx、生成_中口径明细.xlsx ç­‰ã€‚
- æ–¹æ³•:调用 /api/report/export/*?period=2026-07 ç”Ÿæˆ 4 å¼ è¡¨ï¼Œä¸Žåº“内按市州 SQL èšåˆå€¼é€æ ¼æ¯”对(容差 0.002,格值按 4 ä½å°æ•°ï¼‰ã€‚
## ç”¨ä¾‹ä¸Žç»“æžœ
1. åˆ†å¸‚州表 2026-07(正向):18 åŒºåŸŸï¼ˆå…¨çœ+17 å¸‚州)x å®¢è¿é‡/周转量/其中个体 ä¸¤ä¸¤æ ¸å¯¹ï¼Œå…¨éƒ¨ä¸€è‡´ã€‚示例:黄石总客运量 71.2988=70.8(企业)+0.4988(个体);孝感总客运量 56.9534=54.7732+2.1802(两行合一);全省 1631.2013/113731.3141。结论 PASS。
2. ä¸­å£å¾„明细 2026-07 å®¢è¿é‡å—(正向):班线行=企业+个体、公交行=公交城乡、总量行=班线+公交,1-6 æœˆåˆ—逐格一致(武汉 3 æœˆç­çº¿ 326.996 ç­‰ï¼‰ï¼›7 æœˆåˆ—为既有扩列空白问题,见问题 1。结论 PASS(<=6 æœˆèŒƒå›´ï¼‰ã€‚
3. ä¸­å£å¾„排名 2026-07(正向):全省+17 å¸‚å·ž ç´¯è®¡(1-7月)/当月 å®¢è¿é‡ã€å‘¨è½¬é‡å…± 4 åˆ— x 18 è¡Œæ ¸å¯¹ä¸€è‡´ï¼ˆå¦‚全省累计 6170.906/286093.3504、当月 3097.8613/141613.7241,均含个体)。结论 PASS。
4. ä¸­å£å¾„分析 2026-07(正向):全省 ç´¯è®¡/当月 æ€»/班线/公交 å®¢è¿é‡ã€å‘¨è½¬é‡åŠå æ¯”公式核对一致(班线累计 3186.726/228737.9404、当月 1631.2013/113731.3141 å«ä¸ªä½“)。结论 PASS。
5. å›žå½’:passengerSeries 2026-07 æ‰“包接口 200;passengerCityDetail 2026-06(无个体月份)正常生成无报错。结论 PASS。
## å‘现的问题与处置
1. ä¸­å£å¾„明细 2026-07 è‡³ 12 æœˆå•元格与累计列不含 >6 æœˆæ•°æ®ï¼šæ¨¡æ¿æ‰©åˆ—后新列无公式,导出逻辑只填充已有公式单元格。属既有问题(与本次个体并入改动无关),已在清单落盘,另立任务修复 extendMonthlyColumns ä¸Žæ˜Žç»†å¡«å……。
2. åˆ†å¸‚州模板神农架区域 A åˆ—简写为 æž—区,比对脚本按别名映射后一致,非数据问题。
## ç»“论
个体并入口径按业务确认实现:分市州、中口径明细/排名/分析 4 å¼ æŠ¥è¡¨æ•°å€¼ä¸Žæ•°æ®åº“聚合一致,无静默错位,功能通过。代码未提交未推送,待用户确认后提交。
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_³ÇÊпÍÔËÐÂÔöÍøÔ¼³µ²ð·Ö±íÓë»ã×ܹ¤×÷²¾¼°Á½Î»Ð¡ÊýÏÔʾ_2026-09-03.md
New file
@@ -0,0 +1,35 @@
# åŠŸèƒ½æµ‹è¯•æŠ¥å‘Šï¼šåŸŽå¸‚å®¢è¿æŠ¥è¡¨æ–°å¢žã€Šç½‘çº¦è½¦å®¢è¿é‡æ‹†åˆ†è¡¨ã€‹ã€Šé“è·¯è¿è¾“é‡æ±‡æ€»è¡¨ã€‹ä¸Žæ•°å€¼ä¸¤ä½å°æ•°æ˜¾ç¤ºï¼ˆ2026-09-03)
## åŠŸèƒ½è¯´æ˜Ž
按 2026-09-03 ç¡®è®¤çš„下一步需求落地三件事:
1. æŠ¥è¡¨ç”Ÿæˆ-城市客运报表 åˆ—表新增两项:`网约车客运量拆分表`(key=wycSplit)、`道路运输量汇总表(整本)`(key=summaryWorkbook);单选导出走 `/api/report/export/{key}`,多选打包走 `/api/report/export/selected`,列表“数据就绪”状态由 `/api/report/ready` ç»Ÿä¸€è®¡ç®—(已接入)。
2. ç½‘约车拆分算法与《网约车拆分.xlsx》月页人工口径一致:客运量=订单占比×全省网约车客运量 pin;城市内=巡游出租城市内占比代理,武汉保持自身、其余按剩余占比分摊至全省城市内 pin;城际城乡=总量−城市内;周转量=客运量×巡游出租平均运距占比分摊全省周转量 pin(城市内同法)。输入=《网约车订单及全省总量.xlsx》2026-07 è¡Œï¼ˆB-E å…¨çœ pin、G-W 17 å¸‚州订单)+ ç³»ç»Ÿå·¡æ¸¸å‡ºç§ŸæœˆæŠ¥ï¼ˆcity_taxi_monthly,含平均运距)。
3. æ‰€æœ‰ç”Ÿæˆè¡¨æ•°å€¼â€œæ˜¾ç¤ºä¸¤ä½å°æ•°ã€ä¿ç•™å…¨ç²¾åº¦â€ï¼š`toBytes(XSSFWorkbook)`/`toBytesHssf(HSSFWorkbook)` å‡ºå£ç»Ÿä¸€æ‰§è¡Œ `applyTwoDecimalFormat`——非整数的 General/0/0.0/#,##0 ç­‰çº¯æ•°å€¼æ ¼å¼æ”¹ä¸º `0.00` æ˜¾ç¤ºï¼Œå­˜å‚¨å€¼ä¸åŠ¨ï¼›æ•´æ•°ã€ç™¾åˆ†æ¯”ã€æ—¥æœŸã€ç§‘å­¦è®¡æ•°ã€å·²å« `0.00` çš„æ ·å¼ä¸€å¾‹ä¸æ”¹å˜ã€‚
代码改动(未提交、未推送):ReportExportService.java(新增 exportSummaryWorkbook/exportWycSplit/resolveSummaryMother/loadWycMonthlyInput/applyTwoDecimalFormat åŠ selected/ready åˆ†æ”¯ï¼‰ã€ReportExportController.java(新增两个 GET ç«¯ç‚¹ï¼‰ã€ReportExport.vue(城市客运报表列表加两项)。本期另修复母版缺月静默回退问题(见“发现的问题”第 1 æ¡ï¼‰ã€‚
## æµ‹è¯•环境
- åŽç«¯ 8090(mvn spring-boot:run,Started OK 16:32);前端 8080 å°±ç»ªï¼›MySQL 127.0.0.1:3308。
- äººå·¥å£å¾„参考:docs/城市客运/网约车拆分_2026å¹´7月.xlsx(“与人工大表核对”页);输入文件 docs/城市客运/网约车订单及全省总量.xlsx(2026-07 è¡Œï¼‰ã€‚
- æ¯ç‰ˆï¼šdocs/生成汇总大表/2026å¹´7月道路运输量汇总表.xlsx(19 é¡µç­¾ï¼Œ4 éšè—é¡µï¼‰ã€‚
- æ–¹æ³•:HTTP è°ƒç”¨æŽ¥å£ä¸‹è½½äº§ç‰©ï¼Œopenpyxl é€æ ¼æ¯”对。
## ç”¨ä¾‹ä¸Žç»“果(输入 / é¢„期 / å®žé™… / ç»“论)
1. æ­£å‘-数据就绪:wycSplit、summaryWorkbook æŸ¥ 2026-07 ready â†’ å‡ ready=true(rows 17/159,note ç©ºï¼‰ã€‚PASS。
2. æ­£å‘-wycSplit å¯¼å‡ºï¼šGET /export/wycSplit?period=2026-07 â†’ 200,产物 7371B,单页 `2026-07拆分`,客运量块(全省+17 å¸‚州)+ å‘¨è½¬é‡å—结构完整。PASS。
3. æ­£å‘-内部勾稽:全省行订单/客运量/巡游量/城市内/城际城乡与 17 å¸‚州求和一致 13 é¡¹ã€å„市州 åŸŽé™…城乡=总量−城市内、输入 pin(4852.3/4430.84/28348.02/22705.61/订单 29730791)逐项命中。PASS。
4. æ­£å‘-与人工口径一致:17 å¸‚州四指标(客运量、城市内客运量、周转量、城市内周转量)逐市州与“与人工大表核对”比较,最大绝对偏差 0.00000000(阈值 0.02)。PASS。
5. æ­£å‘-两位小数/全精度:产物 185 ä¸ªéžæ•´æ•°æ•°å€¼æ ¼ number_format å…¨éƒ¨ä¸º `0.00`;存储值保留全精度(如 1825.1975355818822、0.9131422212146817);订单等整数值保持 General ä¸å‡ºçް `.00`。PASS。
6. æ­£å‘-summaryWorkbook å¯¼å‡ºï¼šGET /export/summaryWorkbook?period=2026-07 â†’ 200,产物 1,269,456B;19 é¡µç­¾åç§°/顺序/隐藏状态与母版一致;全簿单元格值 0 å·®å¼‚;仅 10,588 å¤„显示格式由 General è°ƒæ•´ä¸º `0.00`(母版缓存小数,值不变)。PASS。
7. æ­£å‘-批量打包:/export/selected?types=wycSplit,summaryWorkbook â†’ zip 1,206,867B å«ä¸¤ä¸ªæ–‡ä»¶ã€‚PASS。
8. è´Ÿå‘-wycSplit ç¼ºè¾“入:2026-08(输入表该行未维护)→ HTTP500,message“网约车输入 2026-08 è¡Œä¸å®Œæ•´ï¼šå…¨çœ pin / è®¢å•缺失,请核对《网约车订单及全省总量.xlsx》”,ready=false å¹¶é™„缺数据说明。PASS(预期失败、提示明确)。
9. è´Ÿå‘-summaryWorkbook ç¼ºæ¯ç‰ˆï¼š2026-08(docs/生成汇总大表 æ—  8 æœˆæ¯ç‰ˆï¼‰â†’ HTTP500“未找到模板文件: 2026å¹´8月道路运输量汇总表.xlsx”,ready=false。PASS(预期失败、提示明确,修复后验证)。
10. ç¼–译回归:mvn compile EXIT=0;后端重启无异常。PASS。
## å‘现的问题与处置
1. ä¿®å¤ï¼šsummaryWorkbook æ¯ç‰ˆå®šä½åŽŸå¸¦â€œâ‰¤ç›®æ ‡æœŸæœ€æ–°æ¯ç‰ˆå›žé€€â€ï¼Œå¯¼è‡´ 2026-08 è¯·æ±‚静默使用 7 æœˆæ¯ç‰ˆç”Ÿæˆã€æ–‡ä»¶åä¸º 2026-08 è€Œå†…容仍为 2026 å¹´ 7 æœˆï¼ˆè·¨å¹´åœºæ™¯è¿˜ä¼šåªæ¢å¹´ä»½ä¸æ¢æœˆä»½ï¼‰ï¼Œå±žè¯¯å¯¼æ€§è¾“出。已删除回退逻辑,改为仅精确匹配 `{å¹´}å¹´{月}月道路运输量汇总表.xlsx`,缺失即明确报错;月份扩展规则留待 P0 æ¯ç‰ˆå®šç¨¿ã€‚
2. è§‚察项(与本次改动无关):cityTaxiDetail å¯¼å‡ºä» 500“未找到模板文件: ç”Ÿæˆ_巡游出租客运量分市州明细.xlsx”,docs/城市客运 ä¸‹è¯¥è¾“出模板文件当前缺失,另立任务跟进补齐新版式模板。
3. ä½¿ç”¨è¯´æ˜Žï¼šç½‘约车拆分每月需在《网约车订单及全省总量.xlsx》补一行当月数据(B-E å…¨çœ pin、G-W 17 å¸‚州订单、F åˆè®¡ï¼‰ï¼Œready çŠ¶æ€ä¼šæç¤ºç¼ºå¤±ï¼Œä¸ä¼šé™é»˜ç©ºè·‘ã€‚
## ç»“论
两个新列表选项与“两位小数显示、全精度存储”按口径实现并通过正向/负向回归;网约车拆分 2026-07 ä¸Žäººå·¥å£å¾„大表完全一致(最大偏差 0);汇总工作簿骨架与母版零值差异;缺月/缺输入均明确报错不静默。代码未提交未推送。
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_»ã×ܹ¤×÷²¾¹Ç¼ÜÉú³É_2026-09-03.md
New file
@@ -0,0 +1,34 @@
# åŠŸèƒ½æµ‹è¯•æŠ¥å‘Šï¼šæ±‡æ€»å·¥ä½œç°¿éª¨æž¶ç”Ÿæˆï¼ˆP0-P1)(2026-09-03)
## åŠŸèƒ½è¯´æ˜Ž
按 2026-09-03 å£å¾„确认(《自动生成汇总工作簿落地方案》v0.1:`#REF!` ä»¥åŽå¤„理;2025 å¹´åˆ—暂时保留母版缓存值;先试 P0-P1 éª¨æž¶ï¼‰ï¼Œæ–°å¢žæ±‡æ€»å·¥ä½œç°¿ç”ŸæˆæŽ¥å£ï¼š
- åŽç«¯ä»Ž `docs/生成汇总大表/` å®šä½æ¯ç‰ˆã€ŠYYYYå¹´M月道路运输量汇总表.xlsx》并整本复制,保留全部 19 ä¸ªé¡µç­¾ã€ç‰ˆå¼ã€å…¬å¼ä¸Ž 2025/2024 å‚考列缓存值;
- ä»…当目标年 â‰  2026 æ—¶ï¼Œå°†å•元格文本中的 `2026å¹´` å‰ç¼€æ›¿æ¢ä¸ºç›®æ ‡å¹´ï¼ˆå¦‚ 2027年),2025/2024 å­—样与页签名不动;目标年为 2026 æ—¶ä¸åšä»»ä½•替换(与母版一致);
- æ•°æ®å¡«å……(台账→口径合成→成品)按方案 P2 èµ·é€æ­¥æŽ¥å…¥ï¼Œæœ¬æœŸä»…验证骨架可生成、无损坏。
代码改动(未提交、未推送):
- traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
  - æ–°å¢ž `summary.template-dir` é…ç½®é¡¹ï¼ˆé»˜è®¤ `docs/生成汇总大表`);
  - æ–°å¢ž `exportSummaryWorkbook(period, mode)` ä¸Ž `dynamicSummaryYear(wb, year)`;
  - `selectedReportData` å¢žåŠ  `summaryWorkbook` åˆ†æ”¯ï¼›`REPORT_FILE_NAMES` å¢žåŠ  `summaryWorkbook`。
- traffic-audit-server/src/main/java/com/trafficaudit/reportexport/controller/ReportExportController.java
  - æ–°å¢ž `GET /api/report/export/summaryWorkbook?period=…&mode=month`。
## æµ‹è¯•环境
- åŽç«¯ 8090(start-dev.ps1 å¯åŠ¨ï¼Œå°±ç»ªæ—¥å¿— OK);MySQL 127.0.0.1:3308。
- æ¯ç‰ˆï¼šdocs/生成汇总大表/2026å¹´7月道路运输量汇总表.xlsx(19 é¡µç­¾ï¼Œéšè— 4 é¡µï¼šå…¬è·¯æ€»å®¢è¿ã€2025年城市客运量、中口径客运量、中口径计算过程)。
- æ–¹æ³•:调用接口下载生成文件,openpyxl ä¸Žæ¯ç‰ˆé€é¡¹æ¯”对(页签清单/顺序/隐藏状态/年份文本计数/前三行版式)。
## ç”¨ä¾‹ä¸Žç»“æžœ
1. 2026-07 éª¨æž¶ç”Ÿæˆï¼ˆæ­£å‘):接口 200,产物 1.23MB;页签 19 ä¸ªä¸”顺序与母版一致;4 ä¸ªéšè—é¡µä¿æŒ hidden。结论 PASS。
2. ç‰ˆå¼ä¿ç•™ï¼ˆæ­£å‘):19 é¡µå‰ä¸‰è¡Œå•元格字符串与母版 0 å·®å¼‚(含表头年份 2026年);无页签被改名/增删。结论 PASS。
3. å¹´ä»½æ–‡æœ¬æœªè¢«è¯¯æ›¿æ¢ï¼ˆæ­£å‘):全表统计 `2025å¹´`146 å¤„、`2024å¹´`160 å¤„、`2026å¹´`59 å¤„,生成文件与母版完全一致(目标年=2026 æ—¶ä¸åšæ›¿æ¢ï¼‰ã€‚结论 PASS。
4. ç¼ºå¤±æœˆä»½è´Ÿå‘:period=2026-08 è¯·æ±‚返回 500,后端日志报 `未找到模板文件: 2026å¹´8月道路运输量汇总表.xlsx`,符合预期(母版仅 7 æœˆï¼‰ã€‚结论 PASS(预期失败)。
5. ç¼–译回归:改动后 mvn ç¼–译 EXIT=0(compile2.log);后端启动无异常。结论 PASS。
## å‘现的问题与处置
1. åŠ¨æ€å¹´ä»½æ›¿æ¢ï¼ˆå¦‚ 2027 å¹´ï¼‰é€»è¾‘仅做了代码走查 + 2026 æ’等验证,未端到端实测(母版只有 2026 å¹´ 7 æœˆã€ç›®å½•中无其它年份模板,属预期);P2 é˜¶æ®µè¡¥ä¸€ä¸ªä¸´æ—¶ 2027 æ¨¡æ¿åšæ›¿æ¢å›žå½’。
2. æ–¹æ¡ˆé—留待拍板项(行列边界、月份扩展、2024 åˆ—、轨道轮渡入库、输出目录)本期不处理,推进 P2(台账页数据填充)前需确认。
## ç»“论
汇总工作簿 P0-P1 éª¨æž¶æŒ‰å£å¾„实现并通过验证:整本复制母版生成成功,页签/隐藏/版式/2025-2024 ç¼“存值零损坏;目标年 2026 æ—¶ä¸Žæ¯ç‰ˆä¸€è‡´ï¼›ç¼ºå¤±æœˆä»½æŒ‰â€œæœªæ‰¾åˆ°æ¨¡æ¿æ–‡ä»¶â€æ˜Žç¡®æŠ¥é”™ï¼Œæ— é™é»˜é”™è¯¯ã€‚代码未提交未推送。
docs/³ÇÊпÍÔË/ÍøÔ¼³µ¶©µ¥¼°È«Ê¡×ÜÁ¿.xlsx
Binary files differ
docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-03.md
New file
@@ -0,0 +1,39 @@
# å·¥ä½œæ—¥ç»“_2026-09-03
## ä»Šå¤©åšäº†ä»€ä¹ˆ
1. å…¬è·¯æ—…客“个体并入”口径落地(分市州总行=企业+个体;中口径明细/排名/分析的班线与总量并入个体;孝感库内 2 è¡ŒæŠ¥è¡¨åˆå¹¶ 1 è¡Œï¼‰ï¼Œå·²æµ‹è¯•并出报告:docs/功能测试报告/功能测试报告_公路旅客报表并入个体客运_2026-09-03.md。
2. ã€Š2026å¹´7月道路运输量汇总表》口径梳理完成:产出 docs/生成汇总大表/页签逻辑关系说明.md ä¸Ž è‡ªåŠ¨ç”Ÿæˆæ±‡æ€»å·¥ä½œç°¿è½åœ°æ–¹æ¡ˆ.md(v0.1,P0-P7 è®¡åˆ’)。
3. æ±‡æ€»å·¥ä½œç°¿ P0-P1 éª¨æž¶å®žçŽ°ï¼ˆæŒ‰ç”¨æˆ·ç­”å¤ï¼š`#REF!` ä»¥åŽå¤„理、2025 ä¿ç•™æ¯ç‰ˆç¼“存值、先试一下):新增 GET /api/report/export/summaryWorkbook,整本复制母版、目标年 2026 æ—¶é›¶æ”¹åŠ¨ï¼›å·²ç¼–è¯‘ã€å¯åŠ¨å¹¶éªŒè¯é€šè¿‡ï¼ŒæŠ¥å‘Šï¼šdocs/功能测试报告/功能测试报告_汇总工作簿骨架生成_2026-09-03.md。
4. æ•°æ®å¯¼å…¥ç•Œé¢â€œå¹´æŠ¥â€æ•°æ®ç±»åž‹å…¥å£æ”¹ä¸ºéšè—ï¼ˆDataImport.vue,待办清单已同步)。
## æµ‹è¯•结果摘要
- å…¬è·¯æ—…客并入个体:4 å¼ æŠ¥è¡¨æ•°å€¼ä¸Žåº“内 SQL èšåˆé€æ ¼ä¸€è‡´ï¼ˆå®¹å·® 0.002),PASS;中口径明细 7-12 æœˆæ‰©åˆ—空白为既有问题,另立任务。
- æ±‡æ€»å·¥ä½œç°¿éª¨æž¶ï¼š19 é¡µç­¾/隐藏页/版式/2025-2024 ç¼“存值与母版零差异;2026-08(无母版)报“未找到模板文件”,符合预期;动态年替换待 P2 è¡¥æµ‹ã€‚
## é—留问题与待办
- æŽ¨è¿› P2(台账页数据填充)前需拍板:行列边界、月份扩展、2024 åˆ—处理、轨道轮渡数据是否入库、输出目录。
- ç³»ç»Ÿå¾…办:#9 å®¢è¿é‡åŒæ¯”环比 0% å®¡æ ¸è§„则、#16 å¯¼å…¥å®žæ—¶è¿›åº¦â€œå·²å¤„理 N/M è¡Œâ€ã€#5 ç½‘约车拆分填值(等数据源);中口径明细 7-12 æœˆåˆ—填充。
## æ˜Žå¤©è®¡åˆ’
- æŒ‰ç”¨æˆ·ç¡®è®¤æŽ¨è¿›æ±‡æ€»å·¥ä½œç°¿ P2(先出台账页数据来源与填充方案),同步每日功能测试报告。
- 09-03 ä»£ç ä¸Žæ–‡æ¡£å‡æœª commit/push(用户未要求,绝不推送)。
## è¿½åŠ ï¼ˆå½“æ—¥åŽç»­ä¼šè¯ï¼‰
5. æŠ¥è¡¨ç”Ÿæˆ-城市客运报表列表新增两项:`网约车客运量拆分表`(wycSplit)、`道路运输量汇总表(整本)`(summaryWorkbook),单/多选导出与 ready å‰ç½®å‡æŽ¥å…¥ã€‚
6. â€œä¸¤ä½å°æ•°æ˜¾ç¤ºã€ä¿ç•™å…¨ç²¾åº¦â€è½åœ°ï¼šxlsx/xls å‡ºå£ç»Ÿä¸€ applyTwoDecimalFormat(非整数纯数值显示 0.00,整数/%/日期不动)。
7. ä¿®å¤ï¼šsummaryWorkbook æ¯ç‰ˆå®šä½åŽŸå­˜åœ¨â€œç¼ºæœˆé™é»˜å›žé€€â€ï¼ˆ2026-08 ä¼šç”¨ 7 æœˆæ¯ç‰ˆç”Ÿæˆè¯¯å¯¼æ–‡ä»¶ï¼‰ï¼Œæ”¹ä¸ºä»…精确匹配、缺母版明确报错。
8. äº§å‡ºã€Šè¾“入输出与逻辑关系总表_2026-09-03.md》(报表生成输入/页签/运算汇总)与《功能测试报告_城市客运新增网约车拆分表与汇总工作簿及两位小数显示_2026-09-03.md》。
### è¿½åŠ æµ‹è¯•æ‘˜è¦
- wycSplit 2026-07:内部勾稽 13 é¡¹ PASS;与人工口径大表四指标 17 å¸‚州最大偏差 0.00000000;185 ä¸ªéžæ•´æ•°æ•°å€¼æ ¼å…¨éƒ¨ 0.00 æ˜¾ç¤ºä¸”存储全精度。
- summaryWorkbook 2026-07:19 é¡µç­¾/值与母版零差异(仅 10,588 å¤„显示格式 General→0.00);2026-08 ç¼ºæ¯ç‰ˆæŠ¥â€œæœªæ‰¾åˆ°æ¨¡æ¿æ–‡ä»¶â€ä¸é™é»˜ã€‚
- mvn compile EXIT=0;后端已重启并在 8090 è¿è¡Œï¼›è§‚察项 cityTaxiDetail æ¨¡æ¿ç¼ºå¤± 500 ä¸Žæœ¬æ¬¡æ”¹åŠ¨æ— å…³ï¼Œå¦ç«‹ä»»åŠ¡ã€‚
### è¿½åŠ 2(16:40-17:10 ä¼šè¯ï¼‰
9. æ¢å¤å·¥ä½œåŒºè¢«è¯¯åˆ çš„ 13 ä¸ªæŠ¥è¡¨è¾“出模板(docs/公路旅客、docs/城市客运、docs/货运 ä¸‹çš„生成_*.xlsx,从版本库恢复):cityTaxiDetail ç­‰åŸŽå¸‚客运导出恢复 200,不再 500;cityTaxiDetail 132 ä¸ªå°æ•°æ ¼å…¨éƒ¨ 0.00 æ˜¾ç¤ºã€‚
10. å£å¾„核实并更正:出租/网约车“全量≠城市内”(武汉除外)。2026-07 å‡ºç§Ÿå…¨çœ 5860.24/城市内 5351.24(城乡差 509.00);网约车拆分 4852.30/4430.84(城乡差 421.46)。母版出租/网约车页本身含总量行+其中城市内行;城市客运口径用各页“其中城市内”行。已同步修订《页签逻辑关系说明.md》与《输入输出与逻辑关系总表_2026-09-03.md》。
11. è½åœ°â€œå”¯ä¸€æ€»è¡¨â€ï¼šdocs/报表生成_关系逻辑与每日进度总表.xlsx(5 é¡µç­¾ï¼šè¯´æ˜Ž/报表关系总表/汇总表页签与口径/疑问与待确认清单/每日待办与完成进度)。
### è¿½åŠ 3(下班前,17:10-17:40)
12. æ€»è¡¨â€œæ¯æ—¥å¾…办”与“疑问清单”增加优先级列(P0 æ¬¡æ—¥æœ€å…ˆ / P1 æœ¬å‘¨ / P2 æœ‰ç©ºï¼‰ï¼Œå¹¶é¢„排 09-04 å¾…办 6 æ¡ï¼›å› æ—§ç‰ˆæ€»è¡¨è¢« WPS å ç”¨æ— æ³•覆盖,新版另存为 docs/报表生成_关系逻辑与每日进度总表_2026-09-03.xlsx(关闭旧文件后可替换回正式名)。
13. ç”¨æˆ·æŽˆæƒï¼ˆè®°å…¥ AGENTS.md å·¥ä½œè§„范):以后每次改完代码先拉公司 gitea æœ€æ–°å¯¹æ¯”合并,再 commit+push,不再逐次征求“是否推送”。本次已执行该流程。
14. æ¨¡æ¿ç»“论:巡游出租明细等生成用“空数据母版 + å¯¼å‡ºå¡«å€¼â€æ–¹æ¡ˆï¼Œä¸æ”¹ç”¨å¸¦æ•°æ®ç‰ˆæ¨¡æ¿ã€‚
docs/´ýÒµÎñÓû§È·ÈÏÇåµ¥_2026-09-02.md
@@ -49,3 +49,42 @@
- æ¨¡æ¿æ–‡ä»¶æ­£è¢«å¦ä¸€ä¼šè¯ä¿®æ”¹ï¼Œä»¥ä¸Š â˜…排版 é¡¹ä¸­æ¶‰åŠæ¨¡æ¿æ ·å¼çš„(6/8/10/12/14)建议以“另一会话定稿后的版本”再当面核对一次,避免重复改两遍。
## ç”¨æˆ· 2026-09-03 é€æ¡ç­”复与执行状态(最新,以此为准)
| # | æ¨¡å— | ç”¨æˆ·ç­”复(09-03) | æ‰§è¡ŒçŠ¶æ€ / è®¡åˆ’ |
|---|---|---|---|
| 1 | å…¬è·¯æ—…客 | å­æ„Ÿä¸¤è¡Œåˆå¹¶ä¸ºä¸€è¡Œï¼ˆè·¨åŽ¿+县内班线),口径统一为班线 | æŠ¥è¡¨å±‚已按市州合计(库 2 è¡Œâ†’表 1 è¡Œï¼‰ï¼›å¾…确认是否入库也合并为一行(改导入去重) |
| 2 | å…¬è·¯æ—…客 | åˆ†å¸‚å·ž/中口径明细/排名/分析表的“班线客运量、周转量”都应涵盖个体数据(反问“你指的哪个表”) | çŽ°çŠ¶ï¼šåˆ†å¸‚å·žè¡¨æœ‰â€œå…¶ä¸­ä¸ªä½“â€è¡Œï¼ˆå·²å¡«ï¼‰ï¼›â€œå®¢è¿é‡/周转量”行与中口径班线行未并入个体 â†’ å¾…确认口径后并入(见下方待确认A) |
| 3 | å…¬è·¯æ—…客 | ç”¨æˆ·ç›´æŽ¥å¯¼å…¥ä¸ªä½“客运数据,直接把个体数据汇总到《生成_公路旅客分市州》 | å¾…按 A å£å¾„落实:并入总量行(保留“其中个体”行拆分)并扩展到中口径三表 |
| 4 | å…¬è·¯æ—…客 | å¾…办 | â—‹ æ”¹æœŸï¼šH2031 ç¼ºæœˆ/2025 åŸºæœŸåŽç»­è¡¥ |
| 5 | åŸŽå¸‚客运 | ç½‘约车列按拆分公式填值,数据来源后续补充说明 | ç»´æŒç•™ç©ºï¼›ç­‰â€œéƒ¨çº§æ€»é‡/订单数”来源说明后再实现拆分 |
| 6 | åŸŽå¸‚客运 | æ¨¡æ¿æš‚时留空,去年数据后续补充 | âœ” ç»´æŒçŽ°çŠ¶ï¼ˆåŽ»å¹´åˆ—/同比留空) |
| 7 | åŸŽå¸‚客运 | ç•™ç©ºï¼ŒåŽç»­è¡¥å…… | âœ” ç»´æŒçŽ°çŠ¶ï¼ˆç¼ºæœˆåˆ—ç•™ç©ºï¼‰ |
| 8 | åŸŽå¸‚客运 | ä¸Žå®˜æ–¹æ ·å¼ ä¸€è‡´å³å¯ | âœ” ç»´æŒçŽ°çŠ¶ï¼ˆè½¨é“åªæŽ’å…¨çœ/武汉/黄石等) |
| 9 | åŸŽå¸‚客运 | åŒæ¯”/环比 0% éœ€æ ¸å®ž â†’ åŠ åˆ° åŸŽå¸‚公交、货运、公路旅客 çš„审核规则里 | è®¡åˆ’:调研审核规则框架后新增规则(见待办队列 R1) |
| 10 | è´§è¿ | å·²åœ¨è¡¨æ ¼é‡Œé¢ä¿®è®¢ | âœ” å·²å®Œæˆï¼ˆ9/2 ç¬¬äºŒé¡µå¡«å½“月 + åˆ—头“当月完成”) |
| 11 | è´§è¿ | å¯¹ç…§å®˜æ–¹æ ·å¼ ç¡®è®¤ | å¾…办(需官方样张对照分市州明细版式) |
| 12 | è´§è¿ | å…ˆä¸ä¿®æ”¹ï¼Œæ•°æ®åŽç»­è¡¥å½•(中口径去年明细页) | â—‹ æ”¹æœŸ |
| 13 | èƒ½è€— | å¾…办 | â—‹ æ”¹æœŸï¼ˆåŒæ¯”留空维持现状) |
| 14 | æŠ•资 | æš‚时按模板原样,后续改动再交流 | âœ” ç»´æŒçŽ°çŠ¶ï¼ˆ.xls è€æ ¼å¼ï¼‰ |
| 15 | åŠŸèƒ½ | æš‚时不做年报导入,可以隐藏一下 | âœ” å·²å®žçŽ°ï¼ˆ09-03 å‰ç«¯éšè—å¹´æŠ¥å…¥å£ï¼Œæ•°æ®ä¿ç•™ï¼‰ |
| 16 | ä½“验 | æœŸæœ›å¯¼å…¥ä¸­æ˜¾ç¤ºâ€œå·²å¤„理 N/M è¡Œâ€å®žæ—¶è¿›åº¦ | è®¡åˆ’:调研现有导入批次机制后实现(见待办队列 R2) |
### å¾…办队列(我方 09-03 èµ·ï¼‰
- [x] 15 å¹´æŠ¥å…¥å£éšè—ï¼ˆå·²æ”¹ DataImport.vue:annual ç»„ hidden + visibleGroups è¿‡æ»¤ï¼‰
- [ ] 1/2/3 ä¸ªä½“客运并入公路旅客报表(待确认 A:并入口径)
- [ ] 1 å­æ„Ÿä¸¤è¡Œå…¥åº“合并 or æŠ¥è¡¨å±‚合计即可(待确认 B)
- [ ] R1(9) å®¢è¿é‡åŒæ¯”/环比 0% å®¡æ ¸è§„则(城市公交/货运/公路旅客)
- [ ] R2(16) å¯¼å…¥â€œå·²å¤„理 N/M è¡Œâ€å®žæ—¶è¿›åº¦ï¼ˆåŽç«¯è¿›åº¦æŸ¥è¯¢ + å‰ç«¯è½®è¯¢ï¼‰
- [ ] 5 ç½‘约车拆分填值(等业务提供拆分数据来源)
- [ ] 11 è´§è¿åˆ†å¸‚州明细对照官方样张
## 09-03 ä¸Šåˆæ‰§è¡Œæ›´æ–°ï¼ˆä¸ªä½“并入公路旅客报表:已实现并通过功能测试)
- [x] #1 å­æ„Ÿï¼šç»´æŒæŠ¥è¡¨å±‚按市州合并为一行(库内两行合计 21802/436433 -> è¡¨å†…一行 2.1802/43.6433),不改导入去重。
- [x] #2/#3 åˆ†å¸‚州表 å®¢è¿é‡/周转量 æ€»è¡Œ = ä¼ä¸š(H2031)+个体;中口径 æ˜Žç»†/排名/分析 ä¸‰è¡¨ æ€»é‡/公路班线 è¡ŒåŒæ ·å¹¶å…¥ä¸ªä½“(同比同口径,无去年数据留空)。
  - æ”¹åŠ¨ï¼štraffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
    - passengerMonthVal:metricIdx 0/1 æ”¹ä¸º ä¼ä¸š+个体;
    - loadMidClassMap:个体并入 dim1(公路班线)/dim0(总量);
  - éªŒè¯ï¼š2026-07(H2031 771 è¡Œ + ä¸ªä½“ 7 è¡Œï¼‰4 å¼ è¡¨æ•°å€¼ä¸Žåº“内聚合逐格一致(详见 docs/功能测试报告/功能测试报告_公路旅客报表并入个体客运_2026-09-03.md)。
  - é—留(与本次改动无关):中口径明细模板扩列后 2026-07~12 æœˆå•元格与累计不含 >6 æœˆæ•°æ®ï¼Œå¦ç«‹ä»»åŠ¡å¤„ç†ã€‚
docs/±¨±íÉú³É_¹ØÏµÂß¼­ÓëÿÈÕ½ø¶È×ܱí_2026-09-03.xlsx
Binary files differ
docs/Éú³É»ã×Ü´ó±í/2026Äê7ÔµÀ·ÔËÊäÁ¿»ã×ܱí.xlsx
Binary files differ
docs/Éú³É»ã×Ü´ó±í/2026Äê7ÔµÀ·ÔËÊäÁ¿»ã×ܱí_7ÔÂÇå¿Õ²âÊÔ°æ.xlsx
Binary files differ
docs/Éú³É»ã×Ü´ó±í/2026Äê7ÔµÀ·ÔËÊäÁ¿»ã×ܱí_7ÔÂÇå¿Õ²âÊÔ°æv2.xlsx
Binary files differ
docs/Éú³É»ã×Ü´ó±í/×Ô¶¯Éú³É»ã×ܹ¤×÷²¾Â䵨·½°¸.md
New file
@@ -0,0 +1,69 @@
# è‡ªåŠ¨ç”Ÿæˆã€Šé“è·¯è¿è¾“é‡æ±‡æ€»è¡¨ã€‹è½åœ°æ–¹æ¡ˆï¼ˆv0.1,2026-09-03)
## 1 ç›®æ ‡ä¸Žäº¤ä»˜ç‰©
- ç³»ç»ŸæŒ‰æŠ¥è¡¨æœŸè‡ªåŠ¨ç”Ÿæˆä¸€æœ¬æ±‡æ€»å·¥ä½œç°¿ï¼ˆ2026-07 æ ·ä¾‹å·²ç”±ç”¨æˆ·æ‰‹å·¥åˆ¶ä½œï¼Œä½äºŽ docs/生成汇总大表/2026å¹´7月道路运输量汇总表.xlsx)。
- äº¤ä»˜ç‰©å½¢æ€ï¼šä¸€ä¸ª .xlsx å·¥ä½œç°¿ï¼Œå†…含 15 ä¸ªå¯è§é¡µç­¾ï¼ˆç‰ˆå¼ã€å…¬å¼ã€æ ·å¼ä¸Žäººå·¥æ¯ç‰ˆä¸€è‡´ï¼‰ï¼Œéšè—é¡µï¼ˆå…¬è·¯æ€»å®¢è¿ã€2025年城市客运量、中口径客运量、中口径计算过程)保留但隐藏,本次不纳入数据加工。
- ä¸å†å•独生成各小表文件;原各小表导出功能保留不变(供单表场景使用)。
## 2 æ€»ä½“策略:模板驱动 + ä¸‰å±‚流水线
- ä»¥ç”¨æˆ·äººå·¥å·¥ä½œç°¿ä¸ºæ¯ç‰ˆï¼šå¤åˆ¶å…¶ç‰ˆå¼/样式/合并/公式,系统只清空数据区并回填,保留页内累计、同比、排名、占比公式并重算。与现有 ReportExportService çš„æ¨¡æ¿æ›¿æ¢æ¨¡å¼ä¸€è‡´ï¼Œé£Žé™©æœ€å°ã€ä¸Žäººå·¥æœ€æŽ¥è¿‘。
- æµæ°´çº¿åˆ†ä¸‰å±‚,与《页签逻辑关系说明》一致:
  1)台账页填充:班线包车、公交、出租、网约车、轨道轮渡(分方式、分市州、逐月)。
  2)口径合成页:城市客运(城市内口径)、中口径明细(城际城乡口径)。
  3)成品页:城市汇总、城市分市州、货运、货运量排名、货运周转量排名、中口径排名、中口径分析、道路运输周转量。
- æ ¡éªŒå±‚:页内公式随 recalc é‡ç®—;生成后用库内 SQL èšåˆä¸Žæˆå“é€æ ¼å¯¹ç…§ï¼ˆé˜ˆå€¼ï¼šæœˆå€¼ 0.005 ä»¥å†…)。
## 3 é¡µç­¾æ¸…单与数据源映射
| é¡µç­¾ | å±‚ | å£å¾„与内容 | æ•°æ®æºï¼ˆåº“表/导入类型) | å¤ç”¨çŽ°æœ‰é€»è¾‘ |
|---|---|---|---|---|
| ç­çº¿åŒ…车 | 1 | å…¬è·¯ç­çº¿+包车,客运量行含个体,其中个体单独行 | h2031_enterprise_monthly、passenger_individual_monthly | ç”Ÿæˆ_公路旅客分市州 æ”¹é€ ï¼ˆä¸ªä½“并入总行已实现,2026-09-03) |
| å…¬äº¤ | 1 | å…¨çœ+市州 æ€»é‡ä¸Žå…¶ä¸­åŸŽå¸‚内 | city_bus_monthly | åŸŽå¸‚客运导出模板逻辑扩展 |
| å‡ºç§Ÿ | 1 | å·¡æ¸¸å‡ºç§Ÿï¼Œæ€»é‡=城市内 | å‡ºç§Ÿå¯¼å…¥è¡¨ï¼ˆcity_taxi ç›¸å…³ï¼‰ | åŒä¸Š |
| ç½‘约车 | 1 | æ€»é‡=城市内(拆分来源待定) | ç½‘约车导入表 | åŒä¸Š |
| è½¨é“轮渡 | 1 | è½¨é“:武汉、黄石;轮渡:仅武汉 | è½¨é“/轮渡导入数据 | åŸŽå¸‚客运导出扩展 |
| åŸŽå¸‚客运 | 2 | å…¬äº¤åŸŽå¸‚内+出租+网约车+轨道轮渡 | ç”±ç¬¬ 1 å±‚合成 | æ–°åˆæˆé€»è¾‘ |
| ä¸­å£å¾„明细 | 2 | ç­çº¿+公交城乡差+出租城乡差+网约车城乡差 | ç”±ç¬¬ 1 å±‚合成 | çŽ°ä¸­å£å¾„å¯¼å‡ºæ”¹ç‰ˆå¼ä¸ºé¡µå†…å¡«å…… |
| ä¸­å£å¾„排名 | 3 | ä¸­å£å¾„口径市州排名 | ç¬¬ 2 å±‚ | çŽ°ä¸­å£å¾„æŽ’åå¯¼å‡º |
| ä¸­å£å¾„分析 | 3 | ä¸­å£å¾„口径全省结构占比 | ç¬¬ 2 å±‚ | çŽ°ä¸­å£å¾„åˆ†æžå¯¼å‡º |
| åŸŽå¸‚汇总 | 3 | åˆ†æ–¹å¼åŸŽå¸‚客运累计/当月 | ç¬¬ 2 å±‚ | çŽ°åŸŽå¸‚å®¢è¿æ±‡æ€»å¯¼å‡º |
| åŸŽå¸‚分市州 | 3 | å„市州城市客运明细与排名 | ç¬¬ 2 å±‚ | çŽ°åŸŽå¸‚å®¢è¿åˆ†å¸‚å·žå¯¼å‡º |
| è´§è¿ | 3 | è´§è¿é‡/周转量 è§„上+规下 | æ¨¡æ¿_货运量周转量、拆分表 | çŽ°è´§è¿å¯¼å‡º |
| è´§è¿é‡æŽ’名 | 3 | è´§è¿é‡å£å¾„排名 | åŒä¸Š | çŽ°è´§è¿é‡æŽ’åå¯¼å‡º |
| è´§è¿å‘¨è½¬é‡æŽ’名 | 3 | å‘¨è½¬é‡å£å¾„排名 | åŒä¸Š | çŽ°å‘¨è½¬é‡æŽ’åå¯¼å‡º |
| é“路运输周转量 | 3 | B=G+L/2+Q/10 | è´§è¿é¡µ+城市客运页+中口径明细页 | æ–°åˆæˆé¡µ |
## 4 å…³é”®è§„则清单(与说明文档第 2、4 èŠ‚ä¸€è‡´ï¼‰
- æœˆä»½åˆ—:2026 å¹´ 1-7 æœˆå€¼åˆ— C/E/G/I/K/M/O,偶数列为同比;Q åˆ—=1-7 æœˆç´¯è®¡ï¼ˆå·²ç¡®è®¤æ­£ç¡®åŠ è‡³ 7 æœˆï¼‰ã€‚
- åŒæ¯”:本期/2025 å¹´åŒæœˆ-1;2025 åŒæœŸå—在右侧 T èµ·ï¼›æ— åŽ»å¹´æ•°æ®ç•™ç©ºã€‚
- åŸŽä¹¡å·®=全量-城市内(公交);出租、网约车 å…¨é‡=城市内(城乡差 0)。
- ç­çº¿åŒ…车客运量行含个体(其中个体为子集)。
- è½¨é“轮渡:轨道 æ­¦æ±‰+黄石;轮渡 ä»…武汉。
- å…¨çœè¡Œ=17 å¸‚州求和(公式保留)。
- é“路运输周转量:B=G(货运规上+规下周转量,万吨公里)+L(城市客运量,万人次)/2+Q(中口径总旅客周转量,万人公里)/10;系数 2 ä¸Ž 10 æŒ‰ç”¨æˆ·å…¬å¼åŽŸæ ·å®žçŽ°ï¼Œä¸šåŠ¡æŠ˜ç®—ä¾æ®å¦è¡Œå¤‡æ³¨ã€‚
- æŽ’名用 RANK、占比用 åˆ†é¡¹/总行 å…¬å¼ï¼Œéš recalc é‡ç®—。
## 5 å®žæ–½æ­¥éª¤ä¸ŽéªŒæ”¶
- P0 æ¯ç‰ˆå®šç¨¿ï¼šç¡®è®¤ #REF! æ¸…除方式;固定每页数据区边界(行、列、月份上限 12 æœˆæ‰©å±•规则);确认 2025 åˆ—与 2024 åˆ—的填充策略。验收:母版无 #REF!、行列映射清单评审通过。
- P1 éª¨æž¶ç”Ÿæˆï¼šæ–°å¢ž workbook çº§æ¨¡æ¿å¤åˆ¶ä¸Žé¡µç­¾ç®¡ç†å·¥å…·ç±»ï¼Œç”Ÿæˆå™¨å…¥å£ exportSummaryWorkbook(period, mode),输出整本工作簿(隐藏页隐藏)。验收:空表可导出、样式保留、页签齐全。
- P2 å°è´¦é¡µå¡«å……:班线包车、公交、出租、网约车、轨道轮渡。验收:以 2026-07 æ•°æ®ä¸Žäººå·¥æ¯ç‰ˆåŒé¡µé€æ ¼æ¯”对通过。
- P3 åˆæˆé¡µå¡«å……:城市客运、中口径明细(城乡差公式)。验收:中口径明细校验行 Q1 çº¦ç­‰äºŽ 0;城市客运=分方式城市内求和。
- P4 æˆå“é¡µå¡«å……:城市汇总、城市分市州、中口径排名、中口径分析、货运及两张排名。验收:各页与人工母版逐格比对。
- P5 æ€»å‘¨è½¬é‡åˆæˆï¼šé“路运输周转量 B=G+L/2+Q/10 ä¸ŽåŽ»å¹´å¯¹ç…§åˆ—ã€‚éªŒæ”¶ï¼šB åˆ—全省=各市州求和、与人工母版一致。
- P6 å‰ç«¯æŽ¥å…¥ï¼šæŠ¥è¡¨ç”Ÿæˆé¡µæ–°å¢ž æ±‡æ€»å·¥ä½œç°¿ ç±»åž‹ï¼ˆå« ready æ•°æ®å°±ç»ªæ£€æŸ¥ã€ç”Ÿæˆã€ä¸‹è½½ã€å¯¼å…¥è®°å½•/操作日志)。验收:页面上可一键生成 2026-07 å¹¶ä¸‹è½½ã€‚
- P7 å›žå½’与报告:全量对照 2026-07 äººå·¥æ¯ç‰ˆï¼›è¾“出《功能测试报告_汇总工作簿生成_<日期>.md》(正向逐格对照、负向缺数据提示、入库抽查)。
## 6 æ•°æ®ç¼ºå£ä¸Žé£Žé™©
- 2025 åŸºæœŸï¼šåº“内 H2031 ä»… 2026-03/07、城市客运仅 2026-06/07,无 2025 æ•°æ®ï¼›2025 åŒæœŸåˆ—只能 ç•™ç©º æˆ– ä¿ç•™æ¯ç‰ˆç¼“存值,需拍板。
- ç½‘约车拆分数据源未定:按母版现状先实现 æ€»é‡=城市内(城乡差 0),等拆分说明后再改。
- è½¨é“轮渡月度数据是否已入库、字段口径(武汉/黄石)待核对。
- åŸŽå¸‚分市州、城市汇总 ç‰ˆå¼ç»†èŠ‚éœ€å¯¹ç…§å®˜æ–¹æ ·å¼ é€æ ¼ç¡®è®¤ã€‚
- è´§è¿è§„上规下拆分表与周转量模板每月是否有对应月份数据待确认(缺月时该页留空并提示)。
- æ¯ç‰ˆæ–‡ä»¶å¯èƒ½è¢« Excel å ç”¨ï¼šç”Ÿæˆæ—¶ä¸€å¾‹å¦å­˜æ–°æ–‡ä»¶ï¼Œä¸æ”¹æ¯ç‰ˆã€‚
- æœˆä»½>7 æ—¶ï¼ˆ8-12 æœˆï¼‰éœ€å…ˆè§£å†³æ¨¡æ¿æ‰©åˆ—问题(现中口径明细 7 æœˆåŽç©ºç™½é—®é¢˜ï¼Œå¦ç«‹ä»»åŠ¡ï¼‰ã€‚
## 7 å¾…用户拍板清单
1. #REF! æ¸…除:由我程序清除这 9 å¤„单元格(置空),还是保留人工处理?
2. 2025 åŒæœŸåˆ—:留空 æˆ– ä¿ç•™æ¯ç‰ˆç¼“存值?(涉及同比列是否显示)
3. è½¨é“轮渡数据是否已导入系统、是否本月先人工补导测试。
4. æ±‡æ€»å·¥ä½œç°¿è¾“出目录与命名(建议 docs/生成汇总大表/输出/生成_道路运输量汇总表_YYYY-MM.xlsx)。
5. æ˜¯å¦æœ¬æœˆå°±ä»¥ 2026-07 æ¯ç‰ˆä¸ºåº•稿,先做 P0-P1 è¯•跑第一版骨架。
docs/Éú³É»ã×Ü´ó±í/ÊäÈëÊä³öÓëÂß¼­¹ØÏµ×ܱí_2026-09-03.md
New file
@@ -0,0 +1,34 @@
# æŠ¥è¡¨ç”Ÿæˆè¾“入输出与逻辑关系总表(2026-09-03 ç‰ˆï¼‰
> å£å¾„汇总入口:把“报表生成(城市客运/汇总工作簿)”相关功能的输入表、页签关系、逻辑运算汇总如下。
> è¯¦ç»†å±•开见两篇专题:`docs/生成汇总大表/页签逻辑关系说明.md`(19 é¡µä¸‰å±‚结构与勾稽)、`docs/生成汇总大表/自动生成汇总工作簿落地方案.md`(P0-P7 è½åœ°æ–¹æ¡ˆï¼‰ã€‚
## 1 å…³ç³»æ€»è¡¨
| åŠŸèƒ½/报表项 | è¾“入(文件/库表) | å‚与页签 | å…³é”®è¿ç®— | è¾“出 | å£å¾„备注 |
|---|---|---|---|---|---|
| ç½‘约车客运量拆分表(wycSplit,月度) | ã€Šç½‘约车订单及全省总量.xlsx》当月行:B-E=网约车全省客运量/城市内客运量/周转量/城市内周转量(源自《网约车总数.xlsx》),F=订单合计,G-W=17 å¸‚州订单(源自《湖北网络预约出租汽车…分析报告》附件1);city_taxi_monthly å·¡æ¸¸å‡ºç§ŸæœˆæŠ¥ï¼ˆå®¢è¿é‡/城市内/平均运距/城市内平均运距) | æ— ï¼ˆç‹¬ç«‹é¡µ `yyyy-MM拆分`,客运量块+周转量块) | å®¢è¿é‡=订单占比×全省 pin;城市内 H:武汉=自身、其余按巡游城市内占比剩余分摊至 pinCK;城际城乡=总量−城市内;周转量 G=客运量×巡游平均运距占比分摊 pinZ,城市内同法(用城市内平均运距) | ç”Ÿæˆ_网约车客运量拆分表_{期}.xlsx | ä¾›ã€Šé“路运输量汇总表》网约车台账页引用;2026-07 ä¸Žäººå·¥å£å¾„四指标最大偏差 0 |
| é“路运输量汇总表(summaryWorkbook,整本) | æ¯ç‰ˆ docs/生成汇总大表/{å¹´}å¹´{月}月道路运输量汇总表.xlsx(P0-P1 æ•´æœ¬å¤åˆ¶ï¼›P2 èµ·é€æ­¥æ”¹ä¸º å°è´¦/口径合成/成品 ä¸‰å±‚回填) | 19 é¡µï¼šç­çº¿åŒ…车、公交、出租、网约车、轨道轮渡(台账);城市客运、中口径明细(口径合成);城市汇总、城市分市州、货运、两张排名、中口径排名/分析、道路运输周转量(成品)等 | ç›®æ ‡å¹´=2026 é›¶æ”¹åŠ¨ï¼›è·¨å¹´åªæ›¿æ¢æ–‡æœ¬ `2026å¹´`→目标年;数据填充/月份扩展 P2 å¾…定 | ç”Ÿæˆ_道路运输量汇总表_{期}.xlsx | æ¯ç‰ˆå¿…须逐月存在(缺月明确报错,不跨月回退);2025/2024 åˆ—保留母版缓存值;#REF! å¾…处理 |
| åŸŽå¸‚客运既有 9 å¼ ï¼ˆå…¬äº¤/出租/轨道轮渡/汇总/累计等) | åŸŽå¸‚公交/巡游出租等月报库表 + docs/城市客运 æ–°ç‰ˆå¼æ¨¡æ¿ | å„自单页/双页 | æŒ‰å„表口径聚合、同比、累计 | ç”Ÿæˆ_xxx.xlsx | ä¸Žæœ¬æ¬¡æ–°å¢žé¡¹åŒåˆ—表,统一走两位小数显示规则 |
## 2 ç½‘约车拆分算法(与《网约车拆分.xlsx》月页一致)
设 17 å¸‚å·ž i(0=武汉),全省 pin:客运量 K=4852.3、城市内 CK=4430.84、周转量 Z=28348.02、城市内 CZ=22705.61(2026-07)。
- C_i = è®¢å•_i Ã· Î£è®¢å• Ã— K(网约车客运量)。
- å·¡æ¸¸å æ¯” F_i = å‡ºç§ŸåŸŽå¸‚内客运量_i Ã· å‡ºç§Ÿå®¢è¿é‡_i。
- åŸŽå¸‚内客运量 H_i:武汉 H_0 = C_0×F_0;其余 H_i = (C_i×F_i) Ã· (ΣC_jF_j âˆ’ C_0F_0) Ã— (CK âˆ’ C_0F_0)。城际城乡 I_i = C_i âˆ’ H_i。
- å‘¨è½¬é‡ G_i = (C_i×平均运距_i) Ã· Î£(C_j×平均运距_j) Ã— Z;城市内周转量 Hl_i:武汉 = G_0;其余 = (H_i×城市内平均运距_i) Ã· (ΣH_j×城市内平均运距_j âˆ’ H_0×城市内平均运距_0) Ã— (CZ âˆ’ G_0);城际城乡周转量 Il_i = G_i âˆ’ Hl_i。
- å¹³å‡è¿è·ç¼ºçœå…œåº• = å‘¨è½¬é‡ Ã· å®¢è¿é‡ï¼ˆåŸŽå¸‚内同理);武汉无城际城乡(H=K、I=0)。
## 3 æ±‡æ€»å·¥ä½œç°¿ä¸‰å±‚口径(摘要,详见页签逻辑关系说明.md)
- ç¬¬ 1 å±‚台账页(分方式明细):班线包车(含个体)、公交、出租、网约车、轨道轮渡;全省+17 å¸‚州逐月,行结构 4 è¡Œ/市州(客运量/周转量/其中客运量/其中周转量)。
- ç¬¬ 2 å±‚口径合成:城市客运(=公交城市内+出租城市内+网约车城市内+轨道轮渡);中口径明细(=班线全量+公交城乡差+出租城乡差+网约车城乡差)。城乡差=台账页“总量行−其中城市内行”(2026-07:出租全省 509.00、网约车 421.46;武汉城乡差=0)。
- ç¬¬ 3 å±‚成品:城市汇总、城市分市州、货运/排名、中口径排名/分析、道路运输周转量(B=G+L/2+Q/10:货运周转量+城市客运量/2+中口径总旅客周转量/10)。
- è½¨é“轮渡:轨道=武汉+黄石;轮渡仅武汉;黄石无轮渡。
## 4 å…±æ€§è§„则
1. æ•°å€¼â€œæ˜¾ç¤ºä¸¤ä½å°æ•°ã€ä¿ç•™å…¨ç²¾åº¦â€ï¼šæ‰€æœ‰ç”Ÿæˆå·¥ä½œç°¿å‡ºå£ç»Ÿä¸€æ‰§è¡Œ applyTwoDecimalFormat(`0.00` æ˜¾ç¤ºï¼›æ•´æ•°/%/日期/科学计数/已含 0.00 æ ·å¼ä¸åŠ¨ï¼‰ã€‚
2. ç¼ºæ•°æ®ä¸é™é»˜ï¼šwycSplit ç¼ºè¾“入行、summaryWorkbook ç¼ºå½“月母版、ready å‰ç½®æ ¡éªŒç¼ºåº“表数据时,列表置“缺数据”并给出原因,导出明确报错。
3. çŠ¶æ€æŸ¥è¯¢ï¼š/api/report/ready(数据就绪)、/api/report/preview、/api/report/export/{key}(单个)、/api/report/export/selected(多选 zip)均已覆盖新类型。
docs/Éú³É»ã×Ü´ó±í/ҳǩÂß¼­¹ØÏµËµÃ÷.md
New file
@@ -0,0 +1,79 @@
# é¡µç­¾é€»è¾‘关系说明(2026å¹´7月道路运输量汇总表)
## 0 è¯´æ˜Žä¸Žä¾æ®
- ä¾æ®æ–‡ä»¶ï¼šdocs/生成汇总大表/2026å¹´7月道路运输量汇总表.xlsx(含公式原稿,2026-09-03 åˆ†æžï¼‰ã€‚
- ç”¨é€”:作为系统自动生成该汇总工作簿的取数与口径依据。最终交付物为这一本汇总工作簿,不再单独生成小表文件。
- éšè—é¡µæš‚不纳入本次范围:公路总客运、2025年城市客运量、中口径客运量、中口径计算过程(工作簿内仍保留,生成时可同步隐藏)。
- ç”¨æˆ·å£å¾„确认(2026-09-03):
  1)班线包车等页 Q åˆ—(2026年累计)为 1-7 æœˆåˆè®¡ï¼Œæ­¦æ±‰ç´¯è®¡æ­£ç¡®åŠ åˆ° 7 æœˆï¼›
  2)断链公式 #REF! æ®‹ä½™ä»…作清理项,不影响主数据区;
  3)轨道、轮渡只有武汉和黄石有数据;道路运输周转量 =(货运页:规上+规下周转量)+(城市客运页:客运量/2)+(中口径明细页:总旅客周转量/10);
  4)班线包车页的客运量行包含个体(其中个体为其子集行)。
  5)补充确认(2026-09-03 ç¬¬äºŒè½®ï¼‰ï¼šé»„石只有轨道、没有轮渡(轮渡仅武汉行);道路运输周转量合成公式 G+L/2+Q/10 ç¡®è®¤æ— è¯¯ï¼›#REF! å¤„理意见见第 6 èŠ‚ã€‚
  6)口径修订(2026-09-03 ç¬¬ä¸‰è½®ï¼‰ï¼šå‡ºç§Ÿä¸Žç½‘约车均存在城际城乡部分,全量 â‰  åŸŽå¸‚内(仅武汉全量=城市内);城市客运口径使用各台账页“其中城市内”行;本文第 3、4 èŠ‚å·²æŒ‰æ¯ç‰ˆå…¬å¼ä¸Ž 2026-07 å®žé™…数据核对更正(出租全省城乡差 509.00、网约车 421.46)。
## 1 æ€»ä½“结构
工作簿共 19 é¡µï¼ŒæŒ‰æ•°æ®åŠ å·¥ç¨‹åº¦åˆ†ä¸‰å±‚ï¼ˆæœ¬è¯´æ˜Žåªè¦†ç›–å¯è§ 15 é¡µï¼›ä¸‰å±‚命名已按业务提问校正):
- æœ¯è¯­å›žç­”:台账页就是分方式的月度明细底稿(按市州逐月);口径合成页是跨方式加总页,中口径明细是代表,城市客运页同属此类;成品页是所有最终报送版式页,排名只是其中一类,还包括汇总、分析、总周转量等。
【第 1 å±‚:分方式明细台账页(明细底稿)】班线包车、公交、出租、网约车、轨道轮渡
  - æŒ‰ å…¨çœ+17 å¸‚å·ž é€æœˆå­˜ å®¢è¿é‡ã€å‘¨è½¬é‡ï¼Œå¹¶å¸¦å­é›†è¡Œï¼ˆå…¶ä¸­ä¸ªä½“ / å…¶ä¸­åŸŽå¸‚内)。
【第 2 å±‚:跨方式口径合成页】城市客运、中口径明细
  - åŸŽå¸‚客运:城市内口径汇总(公交城市内 + å‡ºç§ŸåŸŽå¸‚内 + ç½‘约车城市内 + è½¨é“轮渡,公式见第 3 èŠ‚é€é¡¹å¼•ç”¨å„é¡µâ€œå…¶ä¸­åŸŽå¸‚å†…â€è¡Œï¼‰ã€‚
  - ä¸­å£å¾„明细:城际城乡口径汇总(公路班线 + å…¬äº¤åŸŽä¹¡ + å‡ºç§ŸåŸŽä¹¡ + ç½‘约车城乡),是中口径排名、中口径分析的数据来源。
【第 3 å±‚:成品报表页】城市汇总、城市分市州、货运、货运量排名、货运周转量排名、中口径排名、中口径分析、道路运输周转量
  - ç”±ç¬¬ 1、2 å±‚数据汇总计算形成最终报送版式。
## 2 é€šç”¨ç‰ˆå¼ï¼ˆå°è´¦é¡µä¸Žæ˜Žç»†é¡µä¸€è‡´ï¼‰
- 2026 å¹´ 1-7 æœˆå€¼åˆ—:C、E、G、I、K、M、O(偶数列为当月同比)。
- Q åˆ—:2026 å¹´ 1-7 æœˆç´¯è®¡ï¼ˆC+E+G+I+K+M+O,加到 7 æœˆï¼Œå·²ç¡®è®¤ï¼‰ã€‚
- å³ä¾§ T èµ·ä¸º 2025 å¹´ 1-12 æœˆåŒæœŸå—(值列 T、V、X、Z、AB、AD、AF、AH、AJ、AL、AN、AP),同比公式形如 D=C/T-1。
- å†å³ä¾§å­˜åœ¨ 2024 å¹´åŠæ›´æ—©å‚考列(部分页有 #REF! æ®‹ä½™ï¼Œè§ç¬¬ 6 èŠ‚ï¼‰ã€‚
- è¡Œç»“构:全省块在前(含 å…¶ä¸­ è¡Œï¼‰ï¼ŒéšåŽæ¯ä¸ªå¸‚州占 4 è¡Œï¼ˆå®¢è¿é‡ã€å‘¨è½¬é‡ã€å…¶ä¸­å®¢è¿é‡ã€å…¶ä¸­å‘¨è½¬é‡ï¼‰ã€‚
## 3 å„页职责与主要取数
- ç­çº¿åŒ…车:公路班线(含包车)。全省行 r4/r5 ä¸ºå®¢è¿é‡/周转量(客运量含个体,已确认);r6/r7 ä¸ºå…¶ä¸­ä¸ªä½“客运量/周转量。市州行自 r8 èµ·æ¯ 4 è¡Œä¸€å—。数据源:H203-1 ä¼ä¸šæœˆæŠ¥ + ä¸ªä½“户月报。
- å…¬äº¤ï¼šæ€»é‡è¡Œ r5/r6,其中城市内 r7/r8;公交城乡 = æ€»é‡ - åŸŽå¸‚内。数据源:城市公交月报。
- å‡ºç§Ÿï¼šå·¡æ¸¸å‡ºç§Ÿï¼Œç»“构同公交:全省块 r5-r8(客运量/周转量/其中城市内客运量/其中城市内周转量),每市州 4 è¡Œä¸€å—(武汉 r9-r12、黄石 r13-r16,依此类推)。总量行与“其中城市内”行并存,总量 â‰¥ åŸŽå¸‚内;仅武汉两行相等(城乡差 0),其余市州存在城际城乡差。2026-07:全省出租客运量 5860.24 ä¸‡äººæ¬¡ã€å…¶ä¸­åŸŽå¸‚内 5351.24、城际城乡差 509.00(源=city_taxi_monthly çš„ passenger_volume/passenger_city,如黄石 283.02/258.57)。
- ç½‘约车:结构同公交(总量行+其中城市内行,每市州 4 è¡Œï¼‰ã€‚全量与城市内来自《网约车客运量拆分表》:客运量 C→总量行、城市内 H→其中城市内行、城际城乡 I→城乡差行。2026-07:全省 4852.30 / 4430.84 / 421.46,武汉无城乡差(I=0);母版网约车页当月值即拆分结果(如黄石城市内 140.344963641575)。
- è½¨é“轮渡:上半块轨道(全省 r4/r5,武汉 r6/r7,黄石 r8/r9,全省=武汉+黄石);下半块轮渡(全省 r13/r14,武汉 r15/r16,全省=武汉)。黄石只有轨道、没有轮渡(已二次确认);轮渡页仅武汉行(全省=武汉),无需为黄石扩行。
- åŸŽå¸‚客运:城市内口径。市州行公式示例(武汉,C åˆ—=1 æœˆï¼‰ï¼šC6=公交!C11+出租!C11+网约车!C11+轨道轮渡!C6+轨道轮渡!C15——公交/出租/网约车引用的 C11 å‡ä¸ºè¯¥é¡µâ€œå…¶ä¸­åŸŽå¸‚内客运量”行,轨道轮渡引轨道块 C6 ä¸Žè½®æ¸¡å— C15;周转量同理引用各页城市内周转量行(如出租!C12)。全省行=各市州求和。数据源=公交城市内+出租城市内+网约车城市内+轨道轮渡。
- ä¸­å£å¾„明细:城际城乡口径。页内注记:中口径=公路班线+城际城乡公交+城际城乡出租+城际城乡网约车。公交/出租/网约车城乡部分取 å°è´¦é¡µæ€»é‡-城市内 å·®é¢ï¼ˆæ ¡éªŒå…¬å¼ Q1=Q5-(网约车!Q5-网约车!Q7)-(出租!Q5-出租!Q7)-(公交!Q5-公交!Q7)-班线包车!Q4)。
- ä¸­å£å¾„排名:在中口径总量(明细页 Q ç´¯è®¡å£å¾„)上按市州排名,含占全省比例、同比与增速排名。
- ä¸­å£å¾„分析:全省口径结构占比(如 D åˆ—=各分项/总行占比公式)。
- åŸŽå¸‚汇总:全省城市客运分方式累计/当月两段(总客运量、公交、出租、其中巡游、网约车、轨道、轮渡),左半 2026,右半 2025 åŸºæœŸåŠåŒæ¯”。
- åŸŽå¸‚分市州:各市州城市客运明细与排名(B=G+L+Q+V+X ä¹‹ç±»ç»„合列求和结构)。
- è´§è¿ã€è´§è¿é‡æŽ’名、货运周转量排名:货运口径小表(货运量=规上+规下;周转量=规上+规下)。
- é“路运输周转量:最终 GDP æ ¸ç®—口径页。B åˆ—公式 B=G+L/2+Q/10,其中:
  - G = è´§è¿é¡µè§„上+规下周转量(万吨公里);
  - L = åŸŽå¸‚客运量(万人次,取城市客运口径累计);
  - Q = å…¬è·¯å®¢è¿å‘¨è½¬é‡ï¼ˆä¸‡äººå…¬é‡Œï¼Œå–中口径明细总旅客周转量)。
  å³ä¾§å¦å«åŽ»å¹´ç´¯è®¡å¯¹ç…§ï¼ˆV åˆ—=W+X/2+Y/10)与排名、占全省比例、同比。
## 4 ä¸‰ä¸ªå£å¾„的勾稽关系
设某分方式(公交/出租/网约车):
- å…¨é‡ = å°è´¦é¡µå®¢è¿é‡è¡Œï¼›
- åŸŽå¸‚内 = å°è´¦é¡µå…¶ä¸­åŸŽå¸‚内行;
- åŸŽä¹¡å·® = å…¨é‡ - åŸŽå¸‚内(公路中口径使用)。
则:
- åŸŽå¸‚客运口径 = å…¬äº¤åŸŽå¸‚内 + å‡ºç§ŸåŸŽå¸‚内 + ç½‘约车城市内 + è½¨é“轮渡(2026-09-03 ä¿®è®¢ï¼šå‡ºç§Ÿ/网约车全量 â‰  åŸŽå¸‚内,城市客运页公式实际引用各页“其中城市内”行,不存在“全量等价”写法)。
- ä¸­å£å¾„ = ç­çº¿åŒ…车全量(含个体) + å…¬äº¤åŸŽä¹¡å·® + å‡ºç§ŸåŸŽä¹¡å·® + ç½‘约车城乡差。
- ç­çº¿åŒ…车全量 = å…¶ä¸­ä¸ªä½“ + å…¶ä½™ä¼ä¸šéƒ¨åˆ†ï¼ˆä¸ªä½“子集关系,已确认)。
## 5 æ•°æ®æ¥æºä¸Žç³»ç»Ÿæ˜ å°„(建议)
- ç­çº¿åŒ…车:h2031_enterprise_monthly + passenger_individual_monthly(客运量=企业+个体,其中个体单独行)。
- å…¬äº¤ï¼šcity_bus_monthly(注意区分城市内/城乡字段口径,当前系统按 passenger_chengxiang å–城乡)。
- å‡ºç§Ÿï¼šcity_taxi_monthly(总量=passenger_volume å®¢è¿é‡è¡Œï¼ŒåŸŽå¸‚内=passenger_city“其中城市内”行,与 U402 ä¸ŠæŠ¥åŒæºï¼‰ã€‚
- ç½‘约车:拆分表输出(总量行=拆分客运量 C、其中城市内行=拆分 H、城乡差行=拆分 I;输入=部级订单附件1 17 å¸‚州订单 + å…¨çœ pin + å·¡æ¸¸å‡ºç§ŸæœˆæŠ¥å¹³å‡è¿è·ï¼‰ï¼Œ2026-07 æ‹†åˆ†ç»“果已按此口径填列母版网约车页。
- è½¨é“轮渡:武汉/黄石轨道、武汉轮渡专用导入表。
- è´§è¿ï¼šh2032 ç­‰è§„上规下拆分表(模板_货运量周转量 + æ‹†åˆ†è¡¨ï¼‰ã€‚
## 6 é—留问题与待办
1. #REF! æ®‹ä½™ï¼šå¯è§é¡µ ä¸­å£å¾„明细 AU4/AU97、货运 BQ108、班线包车 IJ4/JW4/IJ5/JW5/JF28/JF29,均在远离 1-7 æœˆä¸»æ•°æ®åŒºçš„年份/辅助列。业务答复为 å·²åˆ /清除(2026-09-03 11:01 å¤æŸ¥æ–‡ä»¶ä»å­˜åœ¨ï¼‰ï¼Œæ¸…除方式待最终确认:程序清除置空,或人工处理。
2. ç½‘约车拆分公式数据源(部级总量/订单数)待业务补充后再接入(原清单第 5 æ¡ï¼‰ã€‚
3. å·²äºŒæ¬¡ç¡®è®¤é¡¹ï¼šé»„石有轨道无轮渡;道路运输周转量合成公式无误;三层结构命名见第 1 èŠ‚ã€‚
## 7 ç»“论
按以上页签职责与三个口径(全量/城市内/城乡差)即可复现该汇总工作簿的合成逻辑;隐藏页不参与本次交付。
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/controller/ReportExportController.java
@@ -84,6 +84,22 @@
        return buildResponse(reportService.exportPassengerMidAnalysis(period, mode), "生成_公路旅客中口径分析.xlsx");
    }
    /** ç”Ÿæˆ_道路运输量汇总表.xlsx(汇总工作簿 P0-P1 éª¨æž¶ï¼šæ•´æœ¬å¤åˆ¶æ¯ç‰ˆï¼Œé¡µç­¾/公式/2025 ç¼“存保留) */
    @GetMapping("/export/summaryWorkbook")
    public ResponseEntity<byte[]> exportSummaryWorkbook(@RequestParam("period") String period,
                                                        @RequestParam(value = "mode", defaultValue = "month") String mode) throws Exception {
        logExport("道路运输量汇总表.xlsx", period);
        return buildResponse(reportService.exportSummaryWorkbook(period, mode), String.format("生成_道路运输量汇总表_%s.xlsx", period));
    }
    /** ç”Ÿæˆ_网约车客运量拆分表.xlsx(月度:订单附件1 + å…¨çœ pin + å·¡æ¸¸å‡ºç§ŸæœˆæŠ¥ï¼‰ */
    @GetMapping("/export/wycSplit")
    public ResponseEntity<byte[]> exportWycSplit(@RequestParam("period") String period,
                                                 @RequestParam(value = "mode", defaultValue = "month") String mode) throws Exception {
        logExport("生成_网约车客运量拆分表.xlsx", period);
        return buildResponse(reportService.exportWycSplit(period, mode), String.format("生成_网约车客运量拆分表_%s.xlsx", period));
    }
    /** ç”Ÿæˆ_能运汇总表.xlsx(H204,全省 + å„市州 sheet) */
    @GetMapping("/export/energySummary")
    public ResponseEntity<byte[]> exportEnergySummary(@RequestParam("period") String period) throws Exception {
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -107,6 +107,10 @@
    @Value("${passenger.template-dir:docs/公路旅客+能耗/输出}")
    private String passengerTemplateDir;
    /** æ±‡æ€»å·¥ä½œç°¿æ¨¡æ¿ç›®å½•(application.yml summary.template-dir) */
    @Value("${summary.template-dir:docs/生成汇总大表}")
    private String summaryTemplateDir;
    /** è´§è¿æŠ¥è¡¨æ¨¡æ¿ç›®å½•(application.yml freight.template-dir) */
    @Value("${freight.template-dir:docs/货运}")
    private String freightTemplateDir;
@@ -171,6 +175,43 @@
                    rows = addReadyCount(() -> energyMapper.selectCount(periodQw(period, mode, EnergyVehicleQuarterly::getReportPeriod)), "能耗车辆季度数据", notes);
                    auditTypes.add("H204");
                    break;
                case "wycSplit": {
                    rows = addReadyCount(() -> cityTaxiMapper.selectCount(periodQw(period, mode, CityTaxiMonthly::getReportPeriod)), "巡游出租月报", notes);
                    try {
                        Map<String, Object> inp = loadWycMonthlyInput(period, mode);
                        if (inp == null) {
                            rows = 0;
                            notes.add("《网约车订单及全省总量.xlsx》缺 " + period + " è¡Œï¼ˆéœ€å…ˆç»´æŠ¤å½“月订单与全省 pin)");
                        } else {
                            double pinK = (Double) inp.get("pinK"), pinCK = (Double) inp.get("pinCK");
                            double pinZ = (Double) inp.get("pinZ"), pinCZ = (Double) inp.get("pinCZ");
                            double orderSum = (Double) inp.get("orderSum");
                            if (pinK <= 0 || pinCK <= 0 || pinZ <= 0 || pinCZ <= 0 || orderSum <= 0) {
                                rows = 0;
                                notes.add("网约车输入 " + period + " è¡Œä¸å®Œæ•´ï¼ˆè®¢å•/全省 pin ç¼ºå¤±ï¼‰");
                            }
                        }
                    } catch (Exception ex) {
                        rows = 0;
                        notes.add("网约车输入读取失败:" + ex.getMessage());
                    }
                    break;
                }
                case "summaryWorkbook": {
                    rows = addReadyCount(() -> cityTaxiMapper.selectCount(periodQw(period, mode, CityTaxiMonthly::getReportPeriod)), "巡游出租月报", notes)
                         + addReadyCount(() -> cityBusMapper.selectCount(periodQw(period, mode, CityBusMonthly::getReportPeriod)), "城市公交月报", notes);
                    if (rows <= 0) notes.add("汇总表依赖的公交/出租月报数据缺失");
                    try {
                        if (resolveSummaryMother(period, mode) == null) {
                            rows = 0;
                            notes.add("docs/生成汇总大表 ä¸‹ç¼ºå°‘可复制的母版汇总表");
                        }
                    } catch (Exception ex) {
                        rows = 0;
                        notes.add("母版定位失败:" + ex.getMessage());
                    }
                    break;
                }
                default:
                    rows = 0;
                    notes.add("未知报表类型:" + t);
@@ -181,6 +222,7 @@
                auditRows += auditRowsOf(period, mode, at);
                audited = audited || auditedMarked(period, mode, at);
            }
            if (auditTypes.isEmpty()) audited = true;
            it.put("rows", rows);
            it.put("ready", rows > 0);
            it.put("auditRows", auditRows);
@@ -212,7 +254,8 @@
        for (String t : types) {
            if (isFreightType(t)) freight = true;
            else if (isPassengerType(t)) passenger = true;
            else if ("cityTaxiDetail".equals(t) || isCityPassengerType(t)) { cityTaxi = true; cityBus = true; }
            else if ("cityTaxiDetail".equals(t) || "wycSplit".equals(t)) { cityTaxi = true; }
            else if (isCityPassengerType(t) || "summaryWorkbook".equals(t)) { cityTaxi = true; cityBus = true; }
            else if (isInvestType(t)) invest = true;
            else if ("energySummary".equals(t)) energy = true;
        }
@@ -256,7 +299,51 @@
            List<Integer> missing = new ArrayList<>();
            List<Integer> coverage = new ArrayList<>();
            if (Boolean.FALSE.equals(it.get("audited"))) problems.add("该报表期尚未审核通过");
            if (isFreightType(t)) {
            if ("wycSplit".equals(t)) {
                coverage.addAll(taxiCover);
                missing = missingMonths(taxiCover, month);
                try {
                    Map<String, Object> inp = loadWycMonthlyInput(period, mode);
                    if (inp == null) {
                        problems.add("缺网约车输入:请在《网约车订单及全省总量.xlsx》补 " + period + " è¡Œ");
                    } else {
                        double pinK = (Double) inp.get("pinK"), pinCK = (Double) inp.get("pinCK");
                        double pinZ = (Double) inp.get("pinZ"), pinCZ = (Double) inp.get("pinCZ");
                        double orderSum = (Double) inp.get("orderSum");
                        if (pinK <= 0 || pinCK <= 0 || pinZ <= 0 || pinCZ <= 0 || orderSum <= 0) {
                            problems.add("网约车输入 " + period + " è¡Œä¸å®Œæ•´ï¼ˆè®¢å•/全省 pin ç¼ºå¤±ï¼‰");
                        } else {
                            addSum(summary, "全省网约车客运量", pinK, "万人");
                            addSum(summary, "其中城市内客运量", pinCK, "万人");
                            addSum(summary, "全省网约车周转量", pinZ, "万人公里");
                            addSum(summary, "其中城市内周转量", pinCZ, "万人公里");
                            addSum(summary, "部级订单合计", orderSum, "单");
                        }
                    }
                } catch (Exception ex) {
                    problems.add("网约车输入读取失败:" + ex.getMessage());
                }
            } else if ("summaryWorkbook".equals(t)) {
                java.util.Set<Integer> cov = new java.util.TreeSet<>();
                cov.addAll(busCover);
                cov.addAll(taxiCover);
                coverage.addAll(cov);
                missing = missingMonths(cov, month);
                try {
                    if (resolveSummaryMother(period, mode) == null) {
                        problems.add("docs/生成汇总大表 ä¸‹ç¼ºå°‘可复制的母版汇总表");
                    } else {
                        Map<Integer, Map<String, double[]>> busM = loadCityBusByMonth(yearPrefix, month);
                        Map<Integer, Map<String, double[]>> taxiM = loadCityTaxiByMonth(yearPrefix, month);
                        Map<String, double[]> bc = busM.get(month);
                        Map<String, double[]> tc = taxiM.get(month);
                        if (bc != null && bc.get("全省") != null) addSum(summary, "城市公交客运量(全省当月)", bc.get("全省")[0], "万人");
                        if (tc != null && tc.get("全省") != null) addSum(summary, "巡游出租客运量(全省当月)", tc.get("全省")[0], "万人");
                    }
                } catch (Exception ex) {
                    problems.add(ex.getMessage());
                }
            } else if (isFreightType(t)) {
                coverage.addAll(freightCover);
                missing = missingMonths(freightCover, month);
                freightPreview(period, month, summary, problems);
@@ -954,14 +1041,15 @@
        return org.apache.poi.ss.util.CellReference.convertNumToColString(colIdx);
    }
    /** æ—…客分市州月值(0=客运量/1=周转量/2=个体客运量/3=个体周转量) */
    /** æ—…客分市州月值(0=客运量/1=周转量/2=个体客运量/3=个体周转量;0/1=企业H2031+个体,2026-09-03 å£å¾„确认) */
    private double passengerMonthVal(Map<Integer, Map<String, PassengerAgg>> data, Map<Integer, Map<String, double[]>> indi,
                                     int year, int month, String region, int metricIdx) {
        if (metricIdx <= 1) {
        if (metricIdx == 0 || metricIdx == 1) { // å®¢è¿é‡/周转量总行:企业+个体
            PassengerAgg agg = aggOf(data, year, month, region);
            Double val = agg == null ? null
            double ent = agg == null ? 0.0
                : (metricIdx == 0 ? agg.passengerTotal / 10000.0 : agg.turnoverTotal / 10000.0);
            return val == null ? 0.0 : val;
            double ind = individualVal(indi, year, month, region, metricIdx == 0 ? 0 : 1);
            return ent + ind;
        }
        return individualVal(indi, year, month, region, metricIdx - 2);
    }
@@ -2106,7 +2194,7 @@
    /**
     * ä¸­å£å¾„分类月度数据:年*100+月 -> (市州/全省 -> double[5][2])
     * ç»´åº¦ 0=总量 1=公路班线(h2031) 2=城际城乡公交(cityBus) 3=巡游出租 4=网约车;[客运量(万人), å‘¨è½¬é‡(万人公里)]
     * ç»´åº¦ 0=总量 1=公路班线(h2031+个体) 2=城际城乡公交(cityBus) 3=巡游出租 4=网约车;[客运量(万人), å‘¨è½¬é‡(万人公里)]
     */
    private Map<Integer, Map<String, double[][]>> loadMidClassMap() {
        Map<Integer, Map<String, double[][]>> mid = new HashMap<>();
@@ -2128,6 +2216,25 @@
                + nz(e.getPassengerClass4()) + nz(e.getPassengerCharter())) / 10000.0;
            double turn = (nz(e.getTurnoverClass1()) + nz(e.getTurnoverClass2()) + nz(e.getTurnoverClass3())
                + nz(e.getTurnoverClass4()) + nz(e.getTurnoverCharter())) / 10000.0;
            addMidClass(mid, key, city, 1, pass, turn);
            addMidClass(mid, key, "湖北省", 1, pass, turn);
        }
        for (PassengerIndividualMonthly e : passengerIndividualMapper.selectList(null)) { // ä¸ªä½“并入公路班线/总量(09-03 å£å¾„)
            if (e.getReportPeriod() == null) continue;
            String[] parts = e.getReportPeriod().split("-");
            if (parts.length != 2) continue;
            int y, m;
            try {
                y = Integer.parseInt(parts[0]);
                m = Integer.parseInt(parts[1]);
            } catch (NumberFormatException ex) {
                continue;
            }
            String city = RegionUtil.cityByCode(e.getRegionCode());
            if (city == null) continue;
            int key = y * 100 + m;
            double pass = nz(e.getPassengerCount()) / 10000.0;
            double turn = nz(e.getTurnover()) / 10000.0;
            addMidClass(mid, key, city, 1, pass, turn);
            addMidClass(mid, key, "湖北省", 1, pass, turn);
        }
@@ -3087,6 +3194,307 @@
        return bos.toByteArray();
    }
    /**
     * ç”Ÿæˆ_道路运输量汇总表.xlsx(P0-P1 éª¨æž¶ï¼‰ï¼šæ•´æœ¬å¤åˆ¶æ¯ç‰ˆã€ŠYYYYå¹´M月道路运输量汇总表.xlsx》,
     * ä¿ç•™å…¨éƒ¨é¡µç­¾/版式/公式/2025 å¹´ç¼“存值,仅把表头年份动态化为目标年;数据填充按 P2 èµ·é€æ­¥æŽ¥å…¥ã€‚
     */
    public byte[] exportSummaryWorkbook(String period, String mode) throws Exception {
        int year = Integer.parseInt(period.contains("-") ? period.split("-")[0] : period);
        int month = period.contains("-") ? monthOf(period, mode) : 12;
        File template = resolveSummaryMother(period, mode);
        if (template == null) {
            throw new RuntimeException("未找到汇总表母版《" + year + "å¹´" + month + "月道路运输量汇总表.xlsx》(docs/生成汇总大表)");
        }
        try (InputStream in = new FileInputStream(template);
             XSSFWorkbook wb = new XSSFWorkbook(in)) {
            dynamicSummaryYear(wb, year);
            return toBytes(wb);
        }
    }
    /** æ±‡æ€»è¡¨æ¯ç‰ˆå®šä½ï¼šä»…精确匹配 {year}å¹´{month}月…(P0-P1 éª¨æž¶æœŸä¸å…è®¸è·¨æœˆ/跨年静默回退,
     *  ç›®æ ‡æœŸæ— å¯¹åº”母版时明确报错,月份扩展规则待 P0 æ¯ç‰ˆå®šç¨¿åŽå†æ”¾å¼€ï¼‰ */
    private File resolveSummaryMother(String period, String mode) throws Exception {
        String year = period != null && period.contains("-") ? period.split("-")[0] : (period == null ? "" : period);
        int month = period != null && period.contains("-") ? monthOf(period, mode) : 12;
        String name = year + "å¹´" + month + "月道路运输量汇总表.xlsx";
        return resolveAnyTemplate(summaryTemplateDir, name);
    }
    // ==================== ç½‘约车客运量拆分表(月度) ====================
    /** ç½‘约车客运量拆分表:输入=部级订单(网约车订单及全省总量.xlsx G-W)+ å…¨çœ pin(B-E)+ å·¡æ¸¸å‡ºç§ŸæœˆæŠ¥ï¼ˆDB);
     *  ç®—法与《网约车拆分.xlsx》月页一致:客运量=订单占比×全省总量;城市内=巡游出租城市内占比代理、武汉保持自身其余按剩余分摊;
     *  å‘¨è½¬é‡=客运量×巡游出租平均运距占比分摊全省周转量 pin,城市内周转量同法。 */
    public byte[] exportWycSplit(String period, String mode) throws Exception {
        if (period == null || !period.contains("-")) {
            throw new RuntimeException("网约车客运量拆分表仅支持按月生成,请选择具体月份");
        }
        int year = Integer.parseInt(period.split("-")[0]);
        int month = parseMonth(period);
        Map<String, Object> inp = loadWycMonthlyInput(period, mode);
        if (inp == null) {
            throw new RuntimeException("《网约车订单及全省总量.xlsx》缺少报表期 " + period + " çš„æ•°æ®è¡Œï¼Œè¯·å…ˆç»´æŠ¤å½“月订单与全省 pin");
        }
        double pinK = (Double) inp.get("pinK");
        double pinCK = (Double) inp.get("pinCK");
        double pinZ = (Double) inp.get("pinZ");
        double pinCZ = (Double) inp.get("pinCZ");
        double orderSum = (Double) inp.get("orderSum");
        double[] orderArr = (double[]) inp.get("orders");
        if (pinK <= 0 || pinCK <= 0 || pinZ <= 0 || pinCZ <= 0 || orderSum <= 0) {
            throw new RuntimeException("网约车输入 " + period + " è¡Œä¸å®Œæ•´ï¼šå…¨çœ pin / è®¢å•缺失,请核对《网约车订单及全省总量.xlsx》");
        }
        List<String> cities = RegionUtil.cityList();
        int n = cities.size();
        if (orderArr == null || orderArr.length < n) {
            throw new RuntimeException("网约车订单列数不足(应为 17 å¸‚州订单列)");
        }
        Map<String, double[]> taxi = taxiMonthlyOf(period);
        java.util.List<String> missingTaxi = new java.util.ArrayList<>();
        double[] Dtax = new double[n];
        double[] Etax = new double[n];
        double[] avgD = new double[n];
        double[] avgDc = new double[n];
        for (int i = 0; i < n; i++) {
            double[] a = taxi.get(cities.get(i));
            if (a == null || a[0] <= 0) {
                missingTaxi.add(cities.get(i));
                continue;
            }
            Dtax[i] = a[0];
            Etax[i] = a[2];
            avgD[i] = a[4];
            avgDc[i] = a[5];
        }
        if (!missingTaxi.isEmpty()) {
            throw new RuntimeException("巡游出租月报缺少以下市州数据:" + String.join("、", missingTaxi));
        }
        double[] C = new double[n];
        double[] H = new double[n];
        double[] I = new double[n];
        double[] Gl = new double[n];
        double[] Hl = new double[n];
        double[] Il = new double[n];
        double sumG = 0;
        double sumDl = 0;
        double sumEl = 0;
        for (int i = 0; i < n; i++) {
            C[i] = orderArr[i] / orderSum * pinK;
            double F = Etax[i] / Dtax[i];
            double G = C[i] * F;
            sumG += G;
            double Dl = C[i] * avgD[i];
            double El = H[i] * avgDc[i];
            sumDl += Dl;
            sumEl += El;
        }
        // åŸŽå¸‚内:武汉保持自身,其余按 G å‰©ä½™å æ¯”分摊至全省城市内 pin
        for (int i = 0; i < n; i++) {
            if (i == 0) {
                H[i] = C[i] * (Etax[i] / Dtax[i]);
            } else {
                H[i] = (C[i] * (Etax[i] / Dtax[i])) / (sumG - C[0] * (Etax[0] / Dtax[0])) * (pinCK - C[0] * (Etax[0] / Dtax[0]));
            }
            I[i] = C[i] - H[i];
        }
        // å‘¨è½¬é‡ï¼šæŒ‰ å®¢è¿é‡Ã—巡游出租平均运距 å æ¯”分摊全省周转量 pin
        double whG = C[0] * (Etax[0] / Dtax[0]);
        double whDl = C[0] * avgD[0];
        double[] El2 = new double[n];
        double sumEl2 = 0;
        for (int i = 0; i < n; i++) {
            double Dl = C[i] * avgD[i];
            double El2v = H[i] * avgDc[i];
            El2[i] = El2v;
            sumEl2 += El2v;
            Gl[i] = Dl / sumDl * pinZ;
        }
        for (int i = 0; i < n; i++) {
            if (i == 0) {
                Hl[i] = Gl[i];
            } else {
                Hl[i] = El2[i] / (sumEl2 - El2[0]) * (pinCZ - Gl[0]);
            }
            Il[i] = Gl[i] - Hl[i];
        }
        return writeWycSplitWorkbook(period, cities, orderArr, C, Dtax, Etax, H, I, avgD, avgDc, Gl, Hl, Il, pinK, pinCK, pinZ, pinCZ, orderSum);
    }
    private byte[] writeWycSplitWorkbook(String period, List<String> cities, double[] orders, double[] C, double[] Dtax,
                                         double[] Etax, double[] H, double[] I, double[] avgD, double[] avgDc,
                                         double[] Gl, double[] Hl, double[] Il,
                                         double pinK, double pinCK, double pinZ, double pinCZ, double orderSum) throws Exception {
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sh = wb.createSheet(period + "拆分");
        int n = cities.size();
        // å®¢è¿é‡å—
        Row t = sh.createRow(0);
        t.createCell(0).setCellValue("网约车客运量拆分(万人)  æŠ¥è¡¨æœŸï¼š" + period);
        String[] head1 = {"市州", "订单总数(单)", "网约车客运量(万人)", "巡游出租客运量(万人)", "巡游出租城市内客运量(万人)", "城市内占比", "网约车城市内客运量(万人)", "网约车城际城乡客运量(万人)"};
        Row h1 = sh.createRow(1);
        for (int j = 0; j < head1.length; j++) h1.createCell(j).setCellValue(head1[j]);
        int r = 2;
        writeWycRow(sh, r++, "全省", orderSum, pinK, sum(Dtax), sum(Etax), pinK > 0 ? pinCK / pinK : 0, pinCK, pinK - pinCK);
        for (int i = 0; i < n; i++) {
            writeWycRow(sh, r++, cities.get(i), orders[i], C[i], Dtax[i], Etax[i], Dtax[i] > 0 ? Etax[i] / Dtax[i] : 0, H[i], I[i]);
        }
        r += 1;
        Row t2 = sh.createRow(r++);
        t2.createCell(0).setCellValue("旅客周转量拆分(万人公里)");
        String[] head2 = {"市州", "出租车平均运距(公里)", "出租车城市内平均运距(公里)", "网约车旅客周转量(万人公里)", "网约车城市内旅客周转量(万人公里)", "网约车城际城乡旅客周转量(万人公里)"};
        Row h2 = sh.createRow(r++);
        for (int j = 0; j < head2.length; j++) h2.createCell(j).setCellValue(head2[j]);
        writeWycRow2(sh, r++, "全省", 0, 0, pinZ, pinCZ, pinZ - pinCZ);
        for (int i = 0; i < n; i++) {
            writeWycRow2(sh, r++, cities.get(i), avgD[i], avgDc[i], Gl[i], Hl[i], Il[i]);
        }
        r += 1;
        String[] notes = {
            "注:1) å®¢è¿é‡ C = è®¢å•占比 Ã— å…¨çœç½‘约车客运量;城市内 H:武汉=自身,其余按 G å‰©ä½™å æ¯”分摊至全省城市内 pin;城际城乡 = æ€»é‡ - åŸŽå¸‚内。",
            "    2) å‘¨è½¬é‡ G = å®¢è¿é‡ Ã— å·¡æ¸¸å‡ºç§Ÿå¹³å‡è¿è· çš„占比 Ã— å…¨çœç½‘约车周转量 pin;城市内周转量同法(用城市内平均运距)。",
            "    3) è¾“入:订单 = å½“月分析报告附件1(17 å¸‚州订单数);全省 pin = ç½‘约车总数.xlsx;巡游出租 = å·¡æ¸¸å‡ºç§ŸæœˆæŠ¥ï¼ˆç³»ç»Ÿå¯¼å…¥ï¼Œå«å¹³å‡è¿è·ï¼‰ã€‚",
            "    4) æ­¦æ±‰æ— åŸŽé™…城乡;数值保留全精度,界面/文件显示两位小数。"
        };
        for (String nt : notes) {
            Row nr = sh.createRow(r++);
            nr.createCell(0).setCellValue(nt);
        }
        for (int j = 0; j < 8; j++) sh.setColumnWidth(j, 18 * 256);
        return toBytes(wb);
    }
    private void writeWycRow(Sheet sh, int r, String name, double ord, double k, double dt, double et, double f, double h, double i) {
        Row row = sh.createRow(r);
        row.createCell(0).setCellValue(name);
        row.createCell(1).setCellValue(ord);
        row.createCell(2).setCellValue(k);
        row.createCell(3).setCellValue(dt);
        row.createCell(4).setCellValue(et);
        row.createCell(5).setCellValue(f);
        row.createCell(6).setCellValue(h);
        row.createCell(7).setCellValue(i);
    }
    private void writeWycRow2(Sheet sh, int r, String name, double ad, double adc, double g, double h, double i) {
        Row row = sh.createRow(r);
        row.createCell(0).setCellValue(name);
        row.createCell(1).setCellValue(ad);
        row.createCell(2).setCellValue(adc);
        row.createCell(3).setCellValue(g);
        row.createCell(4).setCellValue(h);
        row.createCell(5).setCellValue(i);
    }
    private double sum(double[] a) {
        double s = 0;
        for (double v : a) s += v;
        return s;
    }
    /** è¯»å– docs/城市客运/网约车订单及全省总量.xlsx:按 period(yyyy-MM) è¡Œå– pin(B-E) ä¸Ž 17 å¸‚州订单(G-W) */
    private Map<String, Object> loadWycMonthlyInput(String period, String mode) throws Exception {
        String target = period;
        File f;
        try {
            f = resolveAnyTemplate(templateDir, "网约车订单及全省总量.xlsx");
        } catch (Exception e) {
            throw new RuntimeException("未找到《网约车订单及全省总量.xlsx》,请放到 docs/城市客运 ç›®å½•");
        }
        try (InputStream in = new FileInputStream(f);
             org.apache.poi.ss.usermodel.Workbook wb = org.apache.poi.ss.usermodel.WorkbookFactory.create(in)) {
            org.apache.poi.ss.usermodel.Sheet sh = wb.getSheetAt(0);
            org.apache.poi.ss.usermodel.DataFormatter df = new org.apache.poi.ss.usermodel.DataFormatter();
            List<String> cities = RegionUtil.cityList();
            int n = cities.size();
            for (int i = 1; i <= sh.getLastRowNum(); i++) {
                Row row = sh.getRow(i);
                if (row == null) continue;
                Cell c0 = row.getCell(0);
                if (c0 == null) continue;
                String m = df.formatCellValue(c0).trim();
                if (m == null || m.isEmpty()) continue;
                if (m.length() > 7) m = m.substring(0, 7);
                if (!target.equals(m)) continue;
                Map<String, Object> map = new HashMap<>();
                map.put("pinK", cellNum(row, 1, df, 0.0));
                map.put("pinCK", cellNum(row, 2, df, 0.0));
                map.put("pinZ", cellNum(row, 3, df, 0.0));
                map.put("pinCZ", cellNum(row, 4, df, 0.0));
                double[] orders = new double[n];
                double orderSum = 0;
                for (int k = 0; k < n; k++) {
                    orders[k] = cellNum(row, 6 + k, df, 0.0);
                    orderSum += orders[k];
                }
                if (orderSum <= 0) {
                    orderSum = cellNum(row, 5, df, 0.0); // F åˆ—合计兜底
                }
                map.put("orders", orders);
                map.put("orderSum", orderSum);
                return map;
            }
            return null;
        }
    }
    private double cellNum(Row row, int idx, org.apache.poi.ss.usermodel.DataFormatter df, double def) {
        Cell c = row.getCell(idx);
        if (c == null) return def;
        if (c.getCellType() == CellType.NUMERIC) return c.getNumericCellValue();
        String s = df.formatCellValue(c).replace(",", "").trim();
        if (s == null || s.isEmpty()) return def;
        try {
            return Double.parseDouble(s);
        } catch (Exception e) {
            return def;
        }
    }
    /** å·¡æ¸¸å‡ºç§ŸæœˆæŠ¥ï¼ˆDB)按月加载:市州 -> [客运量, å‘¨è½¬é‡, åŸŽå¸‚内客运量, åŸŽå¸‚内周转量, å¹³å‡è¿è·, åŸŽå¸‚内平均运距] */
    private Map<String, double[]> taxiMonthlyOf(String period) {
        Map<String, double[]> map = new java.util.LinkedHashMap<>();
        List<CityTaxiMonthly> rows = cityTaxiMapper.selectList(
            new LambdaQueryWrapper<CityTaxiMonthly>().eq(CityTaxiMonthly::getReportPeriod, period));
        for (CityTaxiMonthly r : rows) {
            String city = RegionUtil.normalizeCityName(r.getCity());
            if (city == null || city.isEmpty()) city = "未知";
            double[] a = map.computeIfAbsent(city, k -> new double[6]);
            a[0] += nz(r.getPassengerVolume());
            a[1] += nz(r.getTurnover());
            a[2] += nz(r.getPassengerCity());
            a[3] += nz(r.getTurnoverCity());
            if (r.getAvgDistance() != null && r.getAvgDistance() > 0) a[4] = r.getAvgDistance();
            if (r.getAvgDistanceCity() != null && r.getAvgDistanceCity() > 0) a[5] = r.getAvgDistanceCity();
        }
        for (double[] a : map.values()) {
            if (a[4] <= 0 && a[0] > 0) a[4] = round(a[1] / a[0], 2);
            if (a[5] <= 0 && a[2] > 0) a[5] = round(a[3] / a[2], 2);
        }
        return map;
    }
    /** æ±‡æ€»å·¥ä½œç°¿å¹´ä»½åŠ¨æ€åŒ–ï¼šä»…æŠŠ 2026å¹´ å‰ç¼€æ›¿æ¢ä¸ºç›®æ ‡å¹´ï¼ˆ2025/2024 å‚考列不动) */
    private void dynamicSummaryYear(XSSFWorkbook wb, int year) {
        String oldPrefix = "2026å¹´";
        String newPrefix = year + "å¹´";
        if (oldPrefix.equals(newPrefix)) return;
        for (int i = 0; i < wb.getNumberOfSheets(); i++) {
            Sheet sh = wb.getSheetAt(i);
            if (sh == null) continue;
            for (Row row : sh) {
                if (row == null) continue;
                for (Cell c : row) {
                    if (c == null || c.getCellType() != CellType.STRING) continue;
                    String v = c.getStringCellValue();
                    if (v != null && v.contains(oldPrefix)) {
                        c.setCellValue(v.replace(oldPrefix, newPrefix));
                    }
                }
            }
        }
    }
    /** ç”Ÿæˆ_投资报表系列.zip:预安排 + åäº”五 + äº¿å…ƒ + ç»æµŽå¼ºåŽ¿ */
    public byte[] exportInvestSeries(String period, String mode) throws Exception {
        java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
@@ -3127,6 +3535,7 @@
            case "passengerMidDetail": return exportPassengerMidDetail(period, mode);
            case "passengerMidRank": return exportPassengerMidRank(period, mode);
            case "passengerMidAnalysis": return exportPassengerMidAnalysis(period, mode);
            case "summaryWorkbook": return exportSummaryWorkbook(period, mode);
            case "energySummary": return exportEnergySummary(period);
            case "investPlan": return investPlan(period, mode);
            case "investFiveYearLogistics": return investFiveYearLogistics(period, mode);
@@ -3138,6 +3547,7 @@
            case "cityPassengerCitySum": return exportCityPassengerCitySum("生成_城市客运客运量分市州累计.xlsx", period, mode);
            case "cityPassengerDetailSum": return exportCityPassengerCitySum("生成_城市客运各市州明细表.xlsx", period, mode);
            case "cityPassengerSummary": return exportCityPassengerSummary(period, mode);
            case "wycSplit": return exportWycSplit(period, mode);
            default: return null;
        }
    }
@@ -3162,6 +3572,8 @@
        REPORT_FILE_NAMES.put("cityPassengerCitySum", "生成_城市客运客运量分市州累计.xlsx");
        REPORT_FILE_NAMES.put("cityPassengerDetailSum", "生成_城市客运各市州明细表.xlsx");
        REPORT_FILE_NAMES.put("cityPassengerSummary", "生成_城市客运汇总.xlsx");
        REPORT_FILE_NAMES.put("summaryWorkbook", "生成_道路运输量汇总表.xlsx");
        REPORT_FILE_NAMES.put("wycSplit", "生成_网约车客运量拆分表.xlsx");
    }
    private void addZipEntry(java.util.zip.ZipOutputStream zos, String name, byte[] data) throws Exception {
@@ -3412,6 +3824,7 @@
    }
    private byte[] toBytesHssf(HSSFWorkbook wb) throws Exception {
        applyTwoDecimalFormat(wb);
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            wb.write(out);
            wb.close();
@@ -4145,7 +4558,45 @@
    private double nz(Double v) {
        return v == null ? 0.0 : v;
    }
    /** ç”Ÿæˆè¡¨æ•°å€¼åˆ—统一样式:保留全精度数值,显示两位小数;整数值与 %/日期/科学计数等既有样式不改变 */
    private void applyTwoDecimalFormat(org.apache.poi.ss.usermodel.Workbook wb) {
        if (wb == null) return;
        org.apache.poi.ss.usermodel.DataFormat df = wb.createDataFormat();
        short fmtTwo = df.getFormat("0.00");
        for (int s = 0; s < wb.getNumberOfSheets(); s++) {
            org.apache.poi.ss.usermodel.Sheet sh = wb.getSheetAt(s);
            if (sh == null) continue;
            for (org.apache.poi.ss.usermodel.Row row : sh) {
                if (row == null) continue;
                for (org.apache.poi.ss.usermodel.Cell cell : row) {
                    if (cell == null) continue;
                    try {
                        CellType ct = cell.getCellType();
                        if (ct != CellType.NUMERIC && ct != CellType.FORMULA) continue;
                        if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) continue;
                        if (ct == CellType.NUMERIC && Math.rint(cell.getNumericCellValue()) == cell.getNumericCellValue()) continue;
                        org.apache.poi.ss.usermodel.CellStyle cs = cell.getCellStyle();
                        if (cs == null) continue;
                        String f = cs.getDataFormatString();
                        if (f == null) f = "";
                        if (f.contains("%") || f.contains("E") || f.contains("0.00")) continue;
                        boolean plain = f.isEmpty() || "General".equals(f) || "general".equals(f)
                            || "0".equals(f) || "#,##0".equals(f) || "0.0".equals(f) || "#,##0.0".equals(f);
                        if (!plain) continue;
                        org.apache.poi.ss.usermodel.CellStyle ns = wb.createCellStyle();
                        ns.cloneStyleFrom(cs);
                        ns.setDataFormat(fmtTwo);
                        cell.setCellStyle(ns);
                    } catch (Exception ignore) {
                        // ä¸ªåˆ«å•元格格式异常不阻塞导出
                    }
                }
            }
        }
    }
    private byte[] toBytes(XSSFWorkbook wb) throws Exception {
        applyTwoDecimalFormat(wb);
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            wb.write(out);
            wb.close();
traffic-audit-web/src/views/DataImport.vue
@@ -29,10 +29,10 @@
            <div class="card-head">选择数据类型</div>
            <div class="module-tabs-head">
              <el-radio-group v-model="activeModule" size="small" @change="onModuleSwitch">
                <el-radio-button v-for="g in groups" :key="g.key" :label="g.key">{{ g.name }}</el-radio-button>
                <el-radio-button v-for="g in visibleGroups" :key="g.key" :label="g.key">{{ g.name }}</el-radio-button>
              </el-radio-group>
            </div>
            <div v-for="g in groups" :key="g.key" v-show="activeModule === g.key" class="module-tab-pane">
            <div v-for="g in visibleGroups" :key="g.key" v-show="activeModule === g.key" class="module-tab-pane">
              <div class="module-sel">
                                <div class="module-sel-row">
                  <el-select v-model="moduleSel[g.key]" placeholder="选择数据类型" style="width:260px"
@@ -107,7 +107,7 @@
                </div>
                <div v-if="!period" class="tip">请先在上方选择报表期</div>
                <div v-else class="overview-grid">
                  <div class="ov-row" v-for="g in groups" :key="g.key">
                  <div class="ov-row" v-for="g in visibleGroups" :key="g.key">
                    <div class="ov-group">{{ g.name }}</div>
                    <div class="ov-item" v-for="t in g.types" :key="t.value" :class="overviewState(t.value).cls">
                      <div class="ov-name">{{ t.label }}</div>
@@ -206,7 +206,7 @@
          ]
        },
        {
          key: 'annual', name: '年报', types: [
          key: 'annual', hidden: true, name: '年报', types: [
            { value: 'annualCompanyBasic', label: '单位基本情况' },
            { value: 'annualEnterpriseFinance', label: '法人企业财务状况' },
            { value: 'annualPassengerEnergy', label: '公路旅客运输能源消耗情况' },
@@ -224,6 +224,8 @@
    }
  },
  computed: {
    // å¹´æŠ¥æš‚不开放:隐藏入口(数据保留,后端实现后可移除 hidden)
    visibleGroups() { return this.groups.filter(g => !g.hidden) },
    failBatches() { return this.batches.filter(b => b.failRows > 0) },
    failRowsFlat() {
      const out = []
traffic-audit-web/src/views/ReportExport.vue
@@ -190,7 +190,9 @@
            { key: 'cityRailFerryDetail', label: '轨道轮渡客运量分市州明细', fileName: '生成_轨道轮渡客运量分市州明细.xlsx', status: null, statusNote: '' },
            { key: 'cityPassengerCitySum', label: '城市客运客运量分市州累计', fileName: '生成_城市客运客运量分市州累计.xlsx', status: null, statusNote: '' },
            { key: 'cityPassengerDetailSum', label: '城市客运各市州明细表', fileName: '生成_城市客运各市州明细表.xlsx', status: null, statusNote: '' },
            { key: 'cityPassengerSummary', label: '城市客运汇总', fileName: '生成_城市客运汇总.xlsx', status: null, statusNote: '' }
            { key: 'cityPassengerSummary', label: '城市客运汇总', fileName: '生成_城市客运汇总.xlsx', status: null, statusNote: '' },
            { key: 'wycSplit', label: '网约车客运量拆分表', fileName: '生成_网约车客运量拆分表.xlsx', status: null, statusNote: '' },
            { key: 'summaryWorkbook', label: '道路运输量汇总表(整本)', fileName: '生成_道路运输量汇总表.xlsx', status: null, statusNote: '' }
          ], checked: []
        },
        {