10 KiB
ARR 数据落库 MVP v1 数据字典
更新时间:2026-07-28
权威 DDL:database/008_arr_mvp_v1_rebuild.sql
已部署目标:<ARR_DB_HOST>:5432/booking_test(远程测试库)
1. 模型边界
数据库只保存三类内容:
ingestion:私有 OSS/本地 fixture 工件身份、Agent 处理运行、交付验收与 outbox;booking:预订附件来源行、行级 Agent 解析版本和房型明细;finance:日报不可变版本、每条 XML 记录的全部 outcome、current 指针和渠道计数。
月报、渠道明细和每 10 日公司用房表均从只读视图/普通程序生成,不建 report_versions 或报表行表。第一版不包含多酒店、账户权限、RSVN 任务卡、邮件元数据和前端追溯页面。
统一类型:主键为 bigint identity,业务日期为 date,时间为 timestamptz,金额为 numeric(18,2),完整 JSON 为 jsonb。
2. ingestion schema
ingestion.artifacts
只保存不可变私有对象的元数据,不保存文件字节、签名 URL 或凭据。
| 字段 | 类型 | 含义 |
|---|---|---|
id |
bigint | 工件 ID |
artifact_kind |
text | booking_source_md、booking_excel、opera_xml、daily_xlsx、monthly_xlsx、channel_detail_xlsx、company_ten_day_xlsx、exception_xlsx、result_json、structured_result_json |
storage_provider |
text | oss、s3 或 local_fixture |
bucket_alias |
text | 内部 bucket 别名,不含路径符 |
object_key |
text | 私有对象键;禁止绝对路径、..、查询串和 fragment |
object_version_id |
text? | 对象存储版本号 |
etag |
text? | provider ETag;内容身份仍以 SHA-256 为准 |
original_filename |
text | 安全原文件名 |
sha256 |
char(64) | 内容哈希 |
byte_size |
bigint | 文件大小,允许 0 |
mime_type |
text? | MIME |
created_at |
timestamptz | 登记时间 |
唯一性:artifact_kind + sha256;同时约束 provider/bucket/object/version 身份唯一。
ingestion.processing_runs
ARR 拥有的一次逻辑处理运行,不是 RSVN 任务卡。
关键字段:run_key、pipeline_type(opera_daily/booking_source_import)、源/结果工件 ID、run_status、请求及交付 processor/rule/schema 版本、delivery_sha256、delivery_json、business_date、安全失败码/消息和完整时间线。
终态规则:accepted 的 Opera 日报必须具备结果工件、营业日、交付版本、规则哈希和交付哈希;rejected/failed 必须有失败码。
ingestion.processing_attempts
一次运行的可重试尝试。
关键字段:processing_run_id、attempt_no、attempt_status、64 位 idempotency_key、可选 remote_run_id、安全失败字段和时间线。运行内 attempt 序号唯一,idempotency key 全局唯一。
ingestion.processing_deliveries
ARR 收到并验收的 DeliveryEnvelope。
关键字段:delivery_key、run/attempt、envelope_sha256、完整 envelope_json、delivery_status、result_status、processor/rule/schema、营业日、五类输入/输出工件 FK、daily_version_id、失败字段和接收/验收/提交时间。
同一个 delivery key 只能代表同一份交付;相同 attempt + envelope 哈希不得重复。
ingestion.outbox_events
与业务事务一起提交的隐私最小化可靠事件。
关键字段:event_key、aggregate_type、aggregate_id、event_type、payload、发布状态/次数/可用时间/发布时间和最后失败码。payload 只放 opaque ID、日期、计数和安全状态,不放住客字段。
3. booking schema
booking.source_batches
一份预订 Excel 或当前 MD fixture 的导入批次。
字段:source_artifact_id、source_kind(expected_fixture/booking_excel)、source_format_version、可选参考年月、batch_status、source/accepted/failed 计数、失败字段和时间线。一个工件只能形成一个批次。
booking.source_rows
一行对应原文件中的一行,重复 Group Code 也必须分行保存。
| 字段 | 类型 | 含义 |
|---|---|---|
id |
bigint | 来源行 ID |
source_batch_id |
bigint | 所属导入批次 |
source_worksheet |
text | 原工作表 |
source_row_no |
integer | 原行号 |
group_code_raw |
text | 原始 Group Code/Tour Code |
group_code_key |
text generated | upper(btrim(group_code_raw)),精确关联键 |
type_of_room_raw |
text | 原始 TYPE OF ROOM/โรงแรม 文本 |
no_of_rooms |
integer | 该来源行总房数,必须大于 0 |
source_row_sha256 |
char(64) | 来源坐标和业务值的确定性哈希 |
created_at |
timestamptz | 落库时间 |
唯一性只针对“批次 + worksheet + 行号”和“批次 + 行哈希”,不把 Group Code 设为唯一。
booking.parse_versions
一条来源行的不可变 Agent 解析版本。
字段:source_row_id、version_no、parse_status(accepted/needs_review/failed)、结果 Schema、processor 名称/版本、规则哈希、输入行哈希、结果哈希、完整已验收 result_json、失败字段和验收时间。版本唯一键为 source_row_id + version_no。
精确 JSON 合同:database/contracts/booking-row-parse-result.schema.json。
booking.room_items
解析后可按 Group Code 查询的房型项目;一条来源行可拆出多项。
| 字段 | 类型 | 含义 |
|---|---|---|
parse_version_id |
bigint | 所属解析版本 |
item_no |
integer | Agent 输出顺序 |
room_type_raw |
text | 原始房型显示 |
room_type_code |
text? | 可选标准房型码 |
quantity |
integer | 房数,必须大于 0 |
unit_price |
numeric? | 解析单价;未知可为空 |
currency_code |
char(3)? | ISO 三位大写币种 |
price_token_raw |
text? | 例如 12,用于解释 1200 |
source_fragment |
text? | 对应原文片段 |
created_at |
timestamptz | 落库时间 |
同一解析版本内 item_no 唯一。
booking.current_row_parses
每条来源行唯一指向一个当前 accepted parse。触发器同时验证:批次已 accepted、parse 已 accepted、所有 room item 数量合计等于来源行 no_of_rooms。
Booking 视图
booking.v_current_room_items:逐来源行返回当前 accepted 的全部 room item 和来源坐标。booking.v_group_room_item_summary:查询时按group_code_key + 房型汇总数量;不修改原始行。booking.v_group_booking_rooms:生成booking_room展示串、总预订房数和命中来源行数。
4. finance schema
finance.daily_versions
一次 ARR 验收结果形成一个不可变日报版本。
字段:营业日/版本号、processing_run_id、源 XML/日报/result/structured-result/异常工件 FK、version_status、processor/rule/schema、result_sha256、五类 outcome 计数、失败字段和创建/验收/激活/替换时间。
五类计数必须严格满足:
source_rows = retained_rows
+ excluded_rate_code_rows
+ duplicate_rows
+ validation_failed_rows
+ price_unmatched_rows
验收成功版本状态为 validated/active/superseded;失败交付可保存为 rejected,但绝不能成为 current。
finance.daily_records
保存 structured-result 中每一条 XML reservation,包括未进入正式日报的记录。
来源与处理字段:
| 字段 | 含义 |
|---|---|
daily_version_id |
所属不可变版本 |
source_sequence |
Agent 连续源序号 |
source_location |
如 reservation[1] |
source_worksheet/source_row_no |
Excel 型输入坐标;直接 XML 必须同时为空 |
outcome |
retained、excluded_rate_code、duplicate、validation_failed、price_unmatched |
decision_codes |
处理决定码数组 |
duplicate_of_record_id |
duplicate 指向同版本原记录 |
日报业务字段:
| 字段 | 类型 |
|---|---|
block_code |
text |
adults / children |
integer |
company_name / company_key |
text |
confirmation_no |
text |
disp_room_no |
text |
effective_rate_amount |
numeric(18,2) |
full_name |
text |
res_comment |
text |
group_code_key |
generated:upper(btrim(res_comment));空值为 null |
trace_text |
text |
no_of_rooms |
integer |
products |
text |
rate_code |
text |
normalized_rate_code |
generated:规范化 rate code |
room_category_label |
text |
arrival / departure |
date |
nights |
integer |
real_price / total_price / kb_amount |
numeric(18,2) |
channel_key / pricing_method |
text |
预订关联字段:booking_source_match_status(matched/unmatched/missing_group_code/not_checked)与 booking_source_match_count。重复 Group Code 命中多条来源行仍是 matched,并记录实际命中数。
retained 记录强制约束:日期/晚数一致、金额非负、去重键完整、预订查询已检查,并且:
total_price = real_price × no_of_rooms × nights
finance.daily_channel_metrics
每个日报版本的渠道名称、确定性顺序和 retained 行数。主键为 daily_version_id + channel_key;同版本 channel_order 唯一。
finance.current_daily_versions
每个营业日唯一指向当前 active 版本。触发器阻止指向未激活或日期不一致的版本。同日修正先完整落新版本,最后原子切换该指针。
5. 业务只读视图
finance.v_active_daily_facts:仅 current daily version +outcome='retained';所有普通业务查询的权威源。finance.v_monthly_report_rows:月报 20 列投影;RATE_AMOUNT、ROOM_CATEGORY为字段别名,Booking Room来自预订 Group Code 查询,TOTAL PRICE直接使用日报值。finance.v_channel_details:ARRIVAL、DEPARTURE、NIGHTS、BLOCK_CODE、RES_COMMENT、Booking Room、Total Booking Price 等;Total Booking Price 等于该日报行total_price,不按 Group Code 二次汇总。finance.v_company_report_source:公司 10 日报表程序使用的 Finance 日期/价格事实和预订房型补充。finance.v_daily_processing_audit:当前与历史版本的全部 outcome、决定码、duplicate lineage、源文件哈希、processor/rule/schema 和验收时间;用于以后追溯页面。
6. 精确关联规则
upper(trim(finance.daily_records.RES_COMMENT))
= booking.source_rows.group_code_key
禁止使用 BLOCK_CODE 代替,禁止模糊匹配。预订数据只提供 Booking Room;Finance 自己提供到离日期、晚数、实际房数、价格和 total_price。