# 汇总表三条主线修复方案 + 投资「按项目名称更新」现状(2026-09-18) > 结论先行:三条主线不是三个独立 bug,而是 **一个统一根因(公式不重算)+ 一处页范围漏项 + 一处口径缺口**。 > 证据文件:`_tmp\_qa_out\生成_道路运输量汇总表_2026-08.xlsx`、`_tmp\_qa_out\gencols.txt`、`_tmp\_qa_out\genrows.txt`。 ## 一、三条主线的根因 ### 证据(刚复核) | 页 | 8月当月值列(Q) | 8月同比列(R) | 说明 | |---|---|---|---| | 公交 / 出租 / 网约车 / 班线包车 / 轨道、轮渡 | **有公式**(`=Q9+Q13+…`),**无缓存值**(cached=None) | 有公式 | 公式写了但没算,读值工具只看到空/旧值 | | 城市客运 / 中口径明细 | **连公式都没有**(Q5=None、Q9=None) | **有公式**(`=Q5/AJ5-1`) | 同比写在、当月值没写 → 典型"页范围不一致" | | 中口径排名 | `K3='=SUM(K4:K20)'`、`O3='=SUM(O4:O20)'`(无缓存值) | `M3=0.7424`、`Q3=0.2317` **是写死的数** | SUM 没重算 + 全省同比写死成库内全省口径 | ### 关键事实 1. **母版本身就是公式驱动的**(不需要人工再做模板): - 全省行 = Σ17 市州:`公交!C5 = =C9+C13+…+C73`、`中口径明细!C5 = =C6+C7+C8+C9`; - 城市客运 / 中口径明细的市州行 = 跨表引用:`城市客运!C9 = =公交!C16+出租!C16+网约车!C16+'轨道、轮渡'!C9`、`中口径明细!C11 = =班线包车!C8`。 - 所以这两页**根本不需要单独写数据**,只要"按月右移公式"补齐 + 重算,整页自动就对。 2. **两个"补公式"函数的页范围不一致**(`SummaryWorkbookFiller.java`): - `ensureMonthYoyFormulas()` 用 `isMonthLedgerSheet()` → 含 城市客运 / 中口径明细 / 公路总客运; - `ensureMonthFormulas()` 用 `isFilledLedgerSheet()` → 只有 6 个台账页(货运/班线包车/公交/出租/网约车/轨道轮渡)← **漏项就在这里**。 3. **生成件里的公式没有缓存值**:POI 写文件时不重算,母版里 7 月的缓存值被原样留下,所以"全省行/累计/排名停在上一期"。Excel 打开靠 `fullCalcOnLoad` 会算,但**系统预览、二次处理、部分 WPS 都看不到新值**。 ### 归纳 | 主线 | 现象 | 根因 | |---|---|---| | 1 | 城市客运 / 中口径明细 8月整列空白、累计=1~7月 | `ensureMonthFormulas()` 页范围漏了这两页 | | 2 | 公交/出租/网约车/城市分市州的**全省行**停在 7 月 | 公式无缓存值(未重算) | | 3 | 中口径排名数据不对 | 同主线 2;另加"全省同比"写死成库内全省口径(1-8月 27524.49)与 Σ17市州(24481.74)打架 | ## 二、修复方案(4 步) 1. **补公式页范围**:`SummaryWorkbookFiller.ensureMonthFormulas()` 的页判断改用 `isMonthLedgerSheet`(含 城市客运 / 中口径明细 / 公路总客运)。 另:中口径明细里有 4 个**数值行**(市州"城际城乡巡游出租/网约车"是硬编码数,不是公式),需要按库里源数据回填,不能只靠公式右移。 2. **生成收尾做公式重算**:在写文件前用 `XSSFFormulaEvaluator.evaluateAll()` 把整本算一遍并写入缓存值(逐页 try-catch;失败则保留 `fullCalcOnLoad=true` 降级)。 好处:全省行、累计列、排名 SUM、城市客运/中口径明细跨表引用**一次性全部自动出值**,不用再讨论"全省按哪个口径"——母版口径就是 Σ17 市州。 3. **统一中口径口径**: - `exportPassengerMidRank()` 全省行同比不再写死库内全省值,改成与 Σ市州 同源; - `loadMidClassMap()` 补 dim3 巡游出租 + dim4 网约车(现在只装了班线 + 公交两维,所以独立《生成_公路旅客中口径明细》1月全省少 535.83+333.39)。 4. **建回归比对脚本**:每次生成后逐页逐格与"母版公式 + 库源数据重算"比对,作为验收基线(可复用 `_tmp\_qa_out` 里已有的对比脚本)。 ### 需要业务确认(不阻塞修复) - 库里 h2031「全省」行 1-8 月 = 27524.49,比 Σ17 市州多 **3042.75**。修复一律按母版口径(Σ市州)走;这个差异意味着企业上报的"全省"含市州明细之外的数据(或某市州漏报),建议业务侧核一次。 ## 三、投资:按项目名称更新 / 新增项目归到对应模块 ### 现状(已核实,程序确实是按项目名称走的) - **导入侧**:`DataImportService.saveInvestProjectRow()` / 全省汇总大表路径,以「**项目名称 + 类别**」为业务主键 upsert —— 命中则更新字段 + 写当期月度,未命中则**新增**并带上该文件的市州与类别(客运站场 / 物流园区)。 库内抽查:客运站场 49 条 / 49 个名称,物流园区 135 条 / 135 个名称,**无重复行**。 - **生成侧**:`ReportExportService.matchInvest()` 是按名称(归一化 + 市州 + 类别过滤)去匹配模板行,不是按列位/行号取数。 - **同名保护**:已实现"市州一致性校验"(库中归属市州与本次解析不一致 → 跳过该行并在页面弹窗提示)。 - **不模糊匹配**:`normInvestName()` 只做括号/连接符/省市县前缀归一,不做近似合并 → 符合"名称有差异就是两个项目"。 ### 缺口(就是你说的"新增项目要归到对应模块下面") 1. 模板里**没有的新项目目前不会自动插行**:`investPlan`(全省预安排)等生成器只用 `matchInvest` 匹配已有模板行,**匹配不到的新项目会被丢掉**,不会按市州/板块插到对应位置。 → 需加"插行"逻辑:按 市州 + 类别(+资金渠道分组)定位块区间,在块末尾插行、复制相邻行格式与公式、写项目名与新数据,并更新合计行范围。 2. **"模块"最细一层现在入不了库**:两张明细表的"一、使用交通部车购税资金 / 二、省燃油税 / 三、农村综合运输服务站 / 四、候车亭"分组行,在导入时被 `^[一二三四五六七八九十]+、.*` 规则**主动跳过**,所以项目落不到具体渠道块。 → 需给 `investment_project` 加 `fund_channel` 字段,导入时记录"当前分组"写到其后项目上,再重导 7/8 月。 3. 同名不同项目目前**不弹窗**(只有市州错位弹窗)。