# 历史提案：月报渠道顺序清单

## 状态

本文件仅保留原始分析。正式实现已编号为 `database/006_report_channel_manifest.sql`；是否应用以及执行 SHA 只以 `database/APPLIED_MIGRATIONS.md` 为准。不要把本 proposal 本身当作迁移执行。

## 已证实的缺口

analytics 1.2 要求 `channels[]` 严格等于月报实际工作表名称和顺序，并保留实际存在的空工作表。当前：

- `finance.daily_channel_metrics` 只有 `daily_version_id / worksheet / row_count`；
- `finance.v_channel_booking_enriched` 没有渠道顺序；
- finance schema 中没有 `worksheet_order / ordinal / channel_order` 候选列；
- 物理行顺序、字母排序和硬编码都不能成为业务契约。

因此当前数据库可以重建数值聚合，但不能 DB-only 生成完整 analytics 1.2。

## 最小结构建议

建议新增 report-level 表 `finance.report_channel_manifest`，每个正式月报版本、每个实际工作表一行：

| 字段 | 建议类型 | 约束/含义 |
|---|---|---|
| `report_version_id` | `bigint` | 非空，外键指向 `finance.report_versions(id)` |
| `worksheet` | `text` | 非空且去除首尾空白后不为空；保存原工作表名称 |
| `worksheet_order` | `integer` | 非空，从 1 连续编号；保存工作簿 sheet 顺序 |
| `row_count` | `integer` | 非空且不小于 0；空工作表必须保存 0 |

建议约束：

- 主键：`(report_version_id, worksheet)`；
- 唯一键：`(report_version_id, worksheet_order)`；
- 同一 report version 的顺序必须完整连续为 `1..N`；
- active/current report 在激活前必须有完整 manifest；
- manifest 必须绑定 `report_versions.artifact_file_id` 对应的同一月报工件，不能从事实行排序推断。

## 写入与回填边界

- 新月报：由已验收的月报生成/采用流程读取工作簿 `sheetnames` 后，在同一版本发布事务中写入；不得在看板查询时补写。
- 既有 2026-07：必须由另行授权的受控回填读取月报工件本身并记录原 sheet 顺序。不能用数据库物理顺序、渠道字母顺序、当前 XLSX 7.22 快照或硬编码 6 个渠道代替 7.26 工件。
- 该表只保存工作表名称、顺序和聚合行数，不保存客人级字段。

## 切换验收

迁移与回填完成后，仍需同时满足：

1. report artifact SHA-256、month 和 `as_of_date` 与 XLSX baseline 相同；
2. 渠道名称、顺序、空渠道和 row_count 完全一致；
3. KPI、公司销售、房型明细、渠道明细和渠道×房型矩阵逐项一致；
4. repository 只查询 `finance.v_channel_booking_enriched`、版本表和本 manifest；
5. 所有测试通过后，才把本地 `OPERA_DASHBOARD_SOURCE` 切到 `database`。

现有服务端 repository 已按上述表名与字段进行能力探测；表不存在或清单不完整时会以 `DB_CHANNEL_ORDER_UNAVAILABLE` 明确回退 XLSX。
