# 功能测试报告 — 投资月报源文件解析引擎(Step1 地基) - 日期:2026-09-17 - 被测对象:`docs\投资\工具\市州月报解析.py`(新增,**只读**,不修改任何源文件/目标表) - 目的:把 17 市州的月报源文件(27 个)解析成统一的「项目行」记录,为 Step1(月报 → 两张明细表)提供唯一数据来源 - 数据:2026 年 7 月源文件(客运 10 个 + 物流 17 个) --- ## 一、功能说明 | 能力 | 实现方式 | | --- | --- | | 选工作表 | 表头区(前 14 行 × 前 32 列)出现本期年月标记(文本日期或 Excel 日期序列号)→ 命中即 +20 分;再按关键字段命中数打分。**不看「隐藏/可见」** | | 定位列 | 按表头关键字匹配(复用 `格式探测.py` 的 `FIELD_PAT`);同一字段多次命中时取「行号最小 + 命中词最短」 | | 切市州段 | 「市州行」= 首 5 列里恰好只有 1 个单元格是市州名且同行其余为空 → 只取本文件对应市州那一段 | | 认项目行 | 名称列非空 + 名称长度 ≥4 + 不是小计/合计/分类行 + 有数值锚点;兼容「无序号列」(随州)与「缺序号」(林区/黄石) | | 归属分类 | `一、二、三、四、五、` 分类行作为其后项目的 `分类` 字段 | | 一致性校验 | 项目行按字段求和 ⇄ 源文件的「合计」行(优先)/「自开始建设累计最大」的小计行 / 「市州行」终值 | | 特例登记 | `CITY_OVERRIDE`:`2026年7月站场固投(南山、汉口站).xlsx` 无文件名城市、无市州行 → 登记为**武汉**(业务事实:汉口客运中心属武汉) | 输出:`docs\投资\输出\解析结果_2026-07.tsv`(一行一个项目,19 列)+ `解析报告_2026-07.md`(每文件对账表 + 按市州汇总表)。 --- ## 二、测试环境 - 本机 Windows,Python 3.14;`_pylibs` 提供 `xlrd 2.0.2` + `openpyxl` - 源文件:`docs\投资\输入\客运(客运站)\7月\`(10 个)、`docs\投资\输入\物流(物流园区)\7月\`(17 个) - 全程只读;运行后源文件哈希不变 --- ## 三、用例与结果 | 编号 | 用例 | 预期 | 实际 | 结论 | | --- | --- | --- | --- | --- | | P01 | 全量解析 27 个源文件 | 全部产出项目行,无异常 | 27/27 成功,共 **151 个项目行**(客运 48 + 物流 103) | ✅ 通过 | | P02 | 每文件「项目行求和」与源文件「合计/小计/市州行」对账 | 有合计(或市州行)的文件必须一致 | **13 个文件 ✅ 一致,0 个不一致**;4 个源文件本身无小计行(十堰客运、襄阳客运 `.et`、随州物流、仙桃物流)→ 记「—」 | ✅ 通过 | | P03 | 十堰物流(53 行隐藏行 + 武汉历史区块) | 只取十堰段 7 个项目 | 市州=十堰@R64,项目行 **7**,求和 79511/27811/3799 = 源文件 R80 合计 **完全相等** | ✅ 通过 | | P04 | 黄石物流(隐藏行 R10 是真实项目) | 必须取到,共 8 个项目行 | 8 个项目行,求和 59500.2/34978.15/7088.9 = 市州行 R5 **完全相等** | ✅ 通过 | | P05 | 潜江 / 林区(26 张历史表被隐藏、可见表名与实际月份不符) | 按表头日期选中本期表 | 潜江选中 `潜江传化2022.3`(表头 2026.07.22)、林区选中 `2026.7`(表头 2026.07.24),均 1 个项目行且与各自小计行一致 | ✅ 通过 | | P06 | 襄阳客运 `.et`(WPS 专有格式 + 列位右移 1 列) | 能读,能定位列 | `xlrd` 直读 `2026年7月 ` 表;建设性质=E…自年初累计=K、本月完成=L(右移 1 列被表头匹配自动吸收);6 个项目行 | ✅ 通过 | | P07 | 无序号列 / 缺序号行(随州物流、林区客运、黄石物流) | 都要算项目行 | 随州 2 行(名称列=A)、林区 1 行、黄石 8 行(含无序号行 R10) | ✅ 通过 | | P08 | 分类行与签字栏不得被当成项目行 | 只作 `分类` 或不计入 | `一、使用交通部车购税资金建设项目` 等进入 `分类` 字段;`统计负责人:`「填报要求:」等均排除 | ✅ 通过 | | P09 | 无市州行/无文件名城市的文件 | 需人工登记,不能猜错 | `站场固投(南山、汉口站).xlsx` → 武汉(CHECKED:与源文件 R8 合计 184285.4/1500 一致) | ✅ 通过 | | P10 | 重复运行幂等(只读) | 源文件不变 | 源文件未变化 | ✅ 通过 | --- ## 四、发现的问题与处置 | # | 问题 | 根因 | 处置 | | --- | --- | --- | --- | | 1 | 黄冈客运、林区客运报「不一致」 | 校验行的挑选规则不对:黄冈的「合 计」在 **B 列**(R10),林区有 3 行小计(R11 空 / R14 有值 / R17 只有文字),原代码一律取**最后一行** | 新增 `pick_total()`:优先「合计/总计」行;无合计行时取「自开始建设累计最大」的小计行 → 两文件均转为 ✅ | | 2 | 8 个文件没有可比对的合计行 | 这些文件的合计值写在**市州行**上(黄石 R5、孝感 R7、咸宁 R6…) | 解析时单独留存「市州行值」,作为无小计行时的兜底校验 → 只剩 4 个文件无对账行 | | 3 | 武汉(南山、汉口)客运解析出 `市州=空` | 该文件是简表布局,既无市州行、文件名也无城市 | 新增 `CITY_OVERRIDE` 人工登记表(带业务依据注释),登记为武汉 | --- ## 五、结论 - 解析引擎 **P01–P10 全部通过**;151 个项目行与源文件合计值**逐项对得上**(13 个文件有对账行且全部一致,4 个源文件本身无小计行)。 - 隐藏行/隐藏表/工作表名错乱/`.et` 格式/右移 1 列/无序号列 —— 这些坑全部被吸收,且**没有用「隐藏/可见」当过滤条件**。 - 下一步(Step1 写入侧):按用户答复把 6 列(H/I/J/K/L/M)按项目名称匹配写入两张明细表;新增项目整行插入;校验「当月自年初累计 = 上月自年初累计 + 本月完成」,不符弹窗 + 记日志 + 按等式纠正。 - 未改动任何源文件,未写入任何目标表,未改业务程序代码。