2026-08-06 22:40:18 +08:00
2026-08-03 20:59:59 +08:00
2026-08-03 20:59:59 +08:00

ARR 2.0:程序化 XML 入库

ARR 2.0 是独立于原 ARR 工程的程序化版本。用户只上传一次 Opera RES_DETAIL XML后续处理、校验、工件保存和 Finance 入库全部由 ARR 自己完成;生产路径不调用 Agent不暴露 MCP也不需要 prompt、fetch_oss_file、submission grant 或公网源文件地址。

处理链路

浏览器上传 XML
  -> ARR 将源 XML 作为 private 不可变对象写入 OSS
  -> ARR 登记 processing run / attempt并切换为 running
  -> 固定版本 process_daily.py 在隔离临时目录生成 v4 JSON以及正式 XLSX 或纯 PRICE_UNMATCHED 复核结果
  -> ARR 将结果工件作为 private 不可变对象写入 OSS
  -> DeliveryValidator 校验 Schema、哈希、行数、业务恒等式和独立日报复验
  -> 纯 PRICE_UNMATCHED只登记审计化复核 case/items/events返回 needs_review无 Finance/outbox/日报下载)
  -> 员工填写并冻结全部缺价键后,以原 XML + 固定价表 + 清单重新处理并独立复验
  -> 最终成功才在一个事务中写入/激活 Finance 版本与 arr.daily_version_committed返回 succeeded
  -> 其他业务错误返回 failed基础设施重试保留冻结清单
  -> 独立 monthly worker 领取事件
  -> 从数据库内 ARRIVAL 派生月份与“更新至”日期
  -> 生成并校验月报,原子登记 reporting 元数据和可下载工件

上传接口仍返回 HTTP 202 以兼容现有页面,但请求会等到初始校验和状态登记结束,因此响应体里的任务状态已经是 succeededneeds_reviewfailed,不是“远端已接收”。

保留的安全边界

  • 源 XML、日报、两份 JSON、异常清单和最终人工复核时冻结清单都按 SHA-256、字节数、MIME 和对象身份校验。
  • PRICE_UNMATCHED 只会进入待复核不会生成日报、Finance 版本或失败 outbox混合错误仍走失败链路。
  • 最终人工价格清单绑定 job、原 XML SHA、业务日期、处理器/规则身份、case 和完整问题键集合,独立 validate_daily.py 会用同一清单重放后才允许入库。
  • 同一 delivery 的重放保持幂等;用户重新上传会创建新的 job并按现有业务日期版本规则在成功后安全替换当前版本。
  • 数据库只对可识别的瞬时并发错误做最多四次事务重试。这不是模型或工具重试。
  • 当前 bucket 可以是 privatepublic-read,但 ARR 2.0 写出的每个对象 ACL 都是 privatepublic-read-write 和已启用/暂停 versioning 会被拒绝。

本地启动

建议使用 Python 3.12

python -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
cp .env.example .env.local

.env.local 中的数据库、OSS 和凭据注入当前 shell 后启动。Web 还必须从运行时 Secret 注入 ARR_WEB_USERNAME 和至少 12 位的 ARR_WEB_PASSWORD;任一缺失都会拒绝启动,真实值不得提交到仓库:

.venv/bin/python -m arr_web.run \
  --host 127.0.0.1 \
  --port 8765 \
  --enable-processing \
  --enable-monthly-generation \
  --enable-company-reports

另开一个进程启动月报消费者:

.venv/bin/python -m monthly_reports.worker \
  --db-config /absolute/path/to/booking-test-db.env \
  --output-root /app/outputs/monthly_reports

浏览器访问 http://127.0.0.1:8765 后会先进入 ARR 登录页。登录后,GET /api/health 中相关 readiness 均为 true 才表示页面处理和下载能力可用;未登录的容器只使用无详情的 GET /healthz readiness。 worker 是独立无端口进程,应由进程管理器单独保活。

当前测试库权威结构为 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.xml014/015 增加 Booking 当前整表指针以及可编辑的 Excel 提取草稿。ARR 2.0 使用原有 artifact_callback 通用工件交付表;不会读写 009/010 的 grant/MCP submission 表。

Booking Excel 房型提取

“公司渠道明细”页可以上传原始 .xlsx,程序从同一工作表的 Tour Code 与精确泰文表头 โรงแรม 提取房型记录。提取结果先进入草稿,不会直接覆盖当前 Booking 数据:

  • Tour Code 会删除制表符、换行和空格;同一 Tour Code 以物理位置最后一行为准,最后一行明确写明取消时移除,单元格底色不代表取消。
  • 只分析酒店文本圆括号内的房型信息。【房型】 后面的整数是数量;未单独写数量时按 1。
  • U-TWN/U-DBL 后带任意数字或小数仍分别归一为 U-TWN/U-DBL高级房TWN高级房DBL 分别归一为 TWNDBL
  • 无法确认的中括号内容保留原始标识、保留已识别数量并标为“待人工确认”;没有中括号的房型文本也进入人工确认。
  • 多个房型拆成多条记录,重复房型不在提取阶段合并。附加费、儿童早餐和导游房等非客房项目忽略。
  • 自动识别与人工记录都可修改或删除;待人工项目确认前不计入有效房量。全部待人工项确认或删除后,才可一次性启用整份工作簿;重新提取原文件会按源文件重建草稿。

对应持久化迁移是 database/014_booking_current_source_batch.sqldatabase/015_booking_excel_review_drafts.sql

测试

.venv/bin/python -m unittest discover -s tests -p 'test_*.py' -v

tests/test_arr_programmatic.py 是关键竖切:它使用真实固定处理器、不可变对象存储、独立验证器和入库 repository 覆盖自动成功、纯缺价待复核、整数 0 输入并冻结为 0.00 重放、可重试基础设施错误和确定性最终失败。

部署

生产配置见 deploy/README.md。当前 Compose 模板仍只打包 webcaddyCaddy 负责 HTTPSWeb 负责应用登录和会话;没有 MCP 端口、MCP 域名或 Agent Secret。月报和公司渠道明细都由 Python/openpyxl 生成XLSX 与 result.json 上传现有 OSS月报 worker 必须作为独立进程部署, .web-jobs 队列状态仍使用 /app/outputs 持久卷。

当前月报行为

  • 用户只上传 XML页面不再要求月份、截止日或单独点击“生成月报”。
  • worker 只消费最终成功提交的日报事件,使用纳入月报数据的最大 ARRIVAL 作为“更新至”;待人工处理和失败任务都不会创建该事件。
  • 月报版本、lineage、渠道行数和两个工件身份持久化到 reporting schema列表和下载由这些元数据驱动。
  • 月报页可见时自动同步发布记录,新版本直接加入列表,不需要点击“刷新”。
  • 每条数据行的 TOTAL PRICE 都是 REAL PRICE × NIGHTS × NO_OF_ROOMS 的 Excel 公式(当前列布局为 =R[row]*C[row]*G[row])。

原工程 /Users/chillishark/ARR项目0727 未被修改,可继续作为 ARR 1.x 回滚基线。

Description
No description provided
Readme 2.6 MiB
Languages
Python 78.1%
JavaScript 10%
PLpgSQL 7%
CSS 3.4%
HTML 1.5%