feat: add daily manual price review workflow

This commit is contained in:
Wyndham ARR
2026-08-06 22:40:18 +08:00
parent aae3d8e1db
commit ca3e8e18fa
77 changed files with 7750 additions and 602 deletions

View File

@@ -8,22 +8,25 @@ ARR 2.0 是独立于原 ARR 工程的程序化版本。用户只上传一次 Ope
浏览器上传 XML
-> ARR 将源 XML 作为 private 不可变对象写入 OSS
-> ARR 登记 processing run / attempt并切换为 running
-> 固定版本 process_daily.py 在隔离临时目录生成完整 JSON/XLSX
-> 固定版本 process_daily.py 在隔离临时目录生成 v4 JSON,以及正式 XLSX 或纯 PRICE_UNMATCHED 复核结果
-> ARR 将结果工件作为 private 不可变对象写入 OSS
-> DeliveryValidator 校验 Schema、哈希、行数、业务恒等式和独立日报复验
-> PostgresIngestionRepository 在一个事务中写入/激活 Finance 版本
-> 写入 arr.daily_version_committed outbox 事件并返回 succeeded / failed 的终态回执
-> 纯 PRICE_UNMATCHED只登记审计化复核 case/items/events返回 needs_review无 Finance/outbox/日报下载)
-> 员工填写并冻结全部缺价键后,以原 XML + 固定价表 + 清单重新处理并独立复验
-> 最终成功才在一个事务中写入/激活 Finance 版本与 arr.daily_version_committed返回 succeeded
-> 其他业务错误返回 failed基础设施重试保留冻结清单
-> 独立 monthly worker 领取事件
-> 从数据库内 ARRIVAL 派生月份与“更新至”日期
-> 生成并校验月报,原子登记 reporting 元数据和可下载工件
```
上传接口仍返回 HTTP `202` 以兼容现有页面,但请求会等到校验和数据库提交结束,因此响应体里的任务状态已经是 `succeeded``failed`,不是“远端已接收”。
上传接口仍返回 HTTP `202` 以兼容现有页面,但请求会等到初始校验和状态登记结束,因此响应体里的任务状态已经是 `succeeded``needs_review``failed`,不是“远端已接收”。
## 保留的安全边界
- 源 XML、日报、两份 JSON 和异常清单都按 SHA-256、字节数、MIME 和对象身份校验。
- 成功结果由独立 `validate_daily.py` 再验一次;验证失败不会写入半成品 Finance 版本
- 源 XML、日报、两份 JSON、异常清单和(最终人工复核时)冻结清单都按 SHA-256、字节数、MIME 和对象身份校验。
- `PRICE_UNMATCHED` 只会进入待复核不会生成日报、Finance 版本或失败 outbox混合错误仍走失败链路
- 最终人工价格清单绑定 job、原 XML SHA、业务日期、处理器/规则身份、case 和完整问题键集合,独立 `validate_daily.py` 会用同一清单重放后才允许入库。
- 同一 delivery 的重放保持幂等;用户重新上传会创建新的 job并按现有业务日期版本规则在成功后安全替换当前版本。
- 数据库只对可识别的瞬时并发错误做最多四次事务重试。这不是模型或工具重试。
- 当前 bucket 可以是 `private``public-read`,但 ARR 2.0 写出的每个对象 ACL 都是 `private``public-read-write` 和已启用/暂停 versioning 会被拒绝。
@@ -62,7 +65,7 @@ cp .env.example .env.local
均为 `true` 才表示页面处理和下载能力可用;未登录的容器只使用无详情的 `GET /healthz` readiness。
worker 是独立无端口进程,应由进程管理器单独保活。
当前测试库权威结构为 `database/008_arr_mvp_v1_rebuild.sql` 加 009016 增量迁移。012 只增加月报发布元数据、Finance 日版本 lineage 和受控本地工件身份016 允许新月报 OSS 工件并保留旧 local 记录013 将用户上传的 XML 文件名独立保存为任务来源信息,内部源工件仍统一命名为 `source.xml`014/015 增加 Booking 当前整表指针以及可编辑的 Excel 提取草稿。ARR 2.0 使用原有 `artifact_callback` 通用工件交付表;不会读写 009/010 的 grant/MCP submission 表。
当前测试库权威结构为 `database/008_arr_mvp_v1_rebuild.sql` 加 009018 增量迁移。017 必须在同一发布窗口先确认/应用 016 后才可应用;它增加价格复核 case/item/event 审计、`awaiting_review` 生命周期、最终人工价格 lineage 和受保护回滚。018 是前向约束修复,只为冻结人工清单增加 `manual_override_json` 工件类型。012 只增加月报发布元数据、Finance 日版本 lineage 和受控本地工件身份016 允许新月报 OSS 工件并保留旧 local 记录013 将用户上传的 XML 文件名独立保存为任务来源信息,内部源工件仍统一命名为 `source.xml`014/015 增加 Booking 当前整表指针以及可编辑的 Excel 提取草稿。ARR 2.0 使用原有 `artifact_callback` 通用工件交付表;不会读写 009/010 的 grant/MCP submission 表。
## Booking Excel 房型提取
@@ -83,7 +86,7 @@ worker 是独立无端口进程,应由进程管理器单独保活。
.venv/bin/python -m unittest discover -s tests -p 'test_*.py' -v
```
`tests/test_arr_programmatic.py` 是关键竖切:它使用真实固定处理器、不可变对象存储、独立验证器和入库 repository 覆盖成功与业务失败。
`tests/test_arr_programmatic.py` 是关键竖切:它使用真实固定处理器、不可变对象存储、独立验证器和入库 repository 覆盖自动成功、纯缺价待复核、整数 `0` 输入并冻结为 `0.00` 重放、可重试基础设施错误和确定性最终失败。
## 部署
@@ -95,7 +98,7 @@ Python/openpyxl 生成XLSX 与 `result.json` 上传现有 OSS月报 worker
## 当前月报行为
- 用户只上传 XML页面不再要求月份、截止日或单独点击“生成月报”。
- worker 只消费成功提交的日报事件,使用纳入月报数据的最大 `ARRIVAL` 作为“更新至”;XML 文件名和当前时间都不参与推导
- worker 只消费最终成功提交的日报事件,使用纳入月报数据的最大 `ARRIVAL` 作为“更新至”;待人工处理和失败任务都不会创建该事件
- 月报版本、lineage、渠道行数和两个工件身份持久化到 `reporting` schema列表和下载由这些元数据驱动。
- 月报页可见时自动同步发布记录,新版本直接加入列表,不需要点击“刷新”。
- 每条数据行的 `TOTAL PRICE` 都是 `REAL PRICE × NIGHTS × NO_OF_ROOMS` 的 Excel 公式(当前列布局为 `=R[row]*C[row]*G[row]`)。