20 KiB
M002 Backend Data Model Design 后端数据模型设计
文档信息
| 项目 | 内容 |
|---|---|
| 文档版本 | 0.2 |
| 日期 | 2026-07-11 |
| 状态 | V2 后端数据模型与阶段实现记录;V3 新开发和当前实现状态以 M002-order-task-workflow-v3.md 为准 |
| 适用范围 | AI 过渡层、订单、任务、任务卡、审计和 OPERA 模拟结果 |
| 主要读者 | 后端、数据库、测试、后续协作 agent |
1. 文档定位
本文定义 M002 第一阶段后端数据模型草案,用于支撑 SuperAgent 任务结果入站、订单挂靠、任务卡确认、队列顺序、审计和 OPERA 模拟结果。
本文不是完整最终模型。当前后端已经按本模型落地第一阶段 Flyway migration、Entity、Mapper、Repository、Service 和测试;后续真实 OPERA、前端页面和 SuperAgent 查询上下文接口仍需继续补充。
2026-07-11 后,M002 V3 已确认采用 0711 P0 冻结基线;2026-07-12 后,Parent Group / Allotment 语义按 P0.1 修订。后续数据模型扩展必须支持结构化 S10/S99、message_events[]、40 路由、方案 C 中的 AI 原始三元组 / 系统处理分类分离、type-known manual review 同卡解阻和 adapter_contract_error,不能只沿用本文的 ai_task_results[] 阶段模型。
2. 设计原则
- 保留 AI 原始 JSON,不覆盖、不重写、不丢字段。
- 用户确认后的数据写入
confirmed_payload_json,OPERA 模拟只读取确认后的 payload。 - 高频查询、幂等、排序和状态机字段冗余为物理列。
- 任务卡字段第一版先写在代码里,但必须通过低耦合 Provider 封装,后续可迁移为数据库配置。
BLOCKED不作为任务持久状态,而是根据同订单前置任务是否完成实时计算。- 用户不允许强制完成任务。
- 用户不能跳过失败的 OPERA 模拟操作。
- 同订单队列可处理状态实时计算时,
FAILED和COMPLETED都视为前置任务已结束,不阻塞后续任务。 - OPERA 当前是模拟结构,后续真实系统接入时通过 adapter 映射,不污染核心任务模型。
3. 枚举
3.1 订单状态
| 状态 | 中文说明 |
|---|---|
TEMPORARY |
临时订单,用于 New Booking 未取得业务号、Fallback、Message Notification 或暂无法匹配订单的任务容器 |
ACTIVE |
有效订单,可承载可处理业务任务 |
ENDED |
已结束订单,例如已完成、已取消或业务生命周期结束;仍可查看历史 |
LOGIC_DELETED |
逻辑删除订单,主要用于临时订单迁移后废弃,不再承载新任务 |
3.2 任务状态
| 状态 | 中文说明 |
|---|---|
PENDING_CONFIRM |
待用户确认订单归属和任务字段 |
READY |
已确认,等待执行 OPERA 模拟 |
EXECUTING |
正在执行 OPERA 模拟 |
FAILED |
任务级失败结束态;第一版 OPERA 单条模拟操作失败时不直接把任务改为该状态,避免按队列规则误放行 |
COMPLETED |
任务已完成 |
说明:
Message Notification不参与执行队列,可在创建后直接进入COMPLETED或保持只读完成态。manual_review/Fallback默认进入PENDING_CONFIRM,等待人工转换或处理。- 阻塞态通过查询同订单前置任务实时计算,不落
BLOCKED状态。
3.3 系统主任务类型
| 类型 | 中文说明 |
|---|---|
NEW_BOOKING |
新建订单任务 |
UPDATE_BOOKING |
更新订单任务,下面包含多种任务卡 |
CANCEL_BOOKING |
取消订单任务 |
MANUAL_REVIEW |
人工复核 / Fallback 任务 |
INFORMATIONAL_MESSAGE |
只读信息提醒任务,不参与执行队列 |
3.4 任务卡类型
第一版建议包含:
NEW_BOOKINGUPDATE_BOOKINGCANCEL_BOOKINGVOUCHER_RECEIVEDROOMING_LISTAMEND_GROUP_CODETRACE_RESERVATION_NOTESTA_RECORDERMESSAGE_NOTIFICATIONFALLBACK_REVIEW
4. 表设计总览
| 表名 | 中文说明 |
|---|---|
workflow_reservation_ai_batch |
AI 任务结果接收批次表 |
workflow_reservation_ai_transition |
AI 任务结果过渡表,一条 AI item 一行 |
workflow_reservation_order |
订单表 |
workflow_reservation_task |
任务表 |
workflow_reservation_task_card |
任务卡确认数据表 |
workflow_reservation_audit_log |
订单任务审计表 |
workflow_reservation_opera_operation |
OPERA 模拟逻辑操作表 |
workflow_reservation_opera_operation_attempt |
OPERA 模拟操作尝试记录表 |
表名前缀使用 workflow_reservation,表示当前属于 reservation 工作流,不放入平台中立 platform 模块。
5. AI 接收批次表
表名:workflow_reservation_ai_batch
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
批次 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
source_message_id |
BIGINT |
关联 SourceMessage |
request_payload_sha256 |
CHAR(64) |
原始请求体 SHA-256 |
batch_idempotency_key |
CHAR(64) |
系统生成的批次幂等键 |
client_id |
VARCHAR(128) |
SuperAgent 调用方 ID |
request_id |
VARCHAR(128) |
调用方请求 ID,可为空 |
received_at |
DATETIME(6) |
AI 结果请求接收时间,按 UTC 理解 |
item_count |
INT |
AI item 数量 |
extraction_warnings_json |
LONGTEXT |
抽取警告 JSON |
created_at / updated_at |
DATETIME(6) |
创建和更新时间 |
索引建议:
- 唯一索引:
hotel_id + batch_idempotency_key - 普通索引:
hotel_id + source_message_id - 普通索引:
hotel_id + received_at
6. AI 过渡表
表名:workflow_reservation_ai_transition
V2 一条 ai_task_results[] item 对应一行。M002 V3 后,message_events[]、S10/S99 入口通知、unhandled_current_intents[] 和 adapter 契约错误也统一以 transition 方式追溯保存。
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
AI 过渡记录 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
batch_id |
BIGINT |
接收批次 ID |
source_message_id |
BIGINT |
来源消息 ID |
source_event_index |
INT |
AI current 事件序号 |
array_index |
INT |
AI 返回列表中的顺序,建议从 1 开始 |
execution_order |
INT |
映射到订单任务队列的初始顺序 |
catalog_code |
VARCHAR(32) |
Skill 目录代码 |
skill_id |
VARCHAR(128) |
Skill 标识 |
result_type |
VARCHAR(64) |
AI 结果类型,例如 normal_task、manual_review、source_message_review_notification、adapter_contract_error |
ai_task_type |
VARCHAR(64) |
AI 原始任务类型 |
route_code |
VARCHAR(64) |
M002 V3 派生路由码;S10/S99 使用外部 route_code,业务事件使用系统稳定路由码 |
system_process_category |
VARCHAR(64) |
系统处理分类:BUSINESS_TASK、SOURCE_MESSAGE_NOTIFICATION、UNHANDLED_CURRENT_INTENT、ADAPTER_CONTRACT_ERROR |
system_task_type |
VARCHAR(64) |
系统主任务类型 |
task_card_type |
VARCHAR(64) |
任务卡类型 |
task_subtype |
VARCHAR(128) |
业务动作 subtype |
current_or_history |
VARCHAR(32) |
当前或历史标识 |
group_code |
VARCHAR(128) |
Group Code 候选 |
confirmation_number |
VARCHAR(128) |
Confirmation Number 候选 |
item_payload_sha256 |
CHAR(64) |
单个 item JSON 哈希 |
item_idempotency_key |
CHAR(64) |
系统生成 item 幂等键 |
manual_reason_code |
VARCHAR(128) |
人工复核原因码 |
parent_source_event_index |
INT |
父任务事件序号 |
linked_task_group_id |
VARCHAR(128) |
联动任务组 ID |
blocked_until_parent_completed |
TINYINT(1) |
是否等待父任务完成 |
ai_payload_json |
LONGTEXT |
AI 原始 item JSON |
case_keys_json |
LONGTEXT |
订单候选键 JSON |
extracted_fields_json |
LONGTEXT |
AI 业务字段 JSON |
manual_review_json |
LONGTEXT |
人工复核 JSON |
informational_message_json |
LONGTEXT |
信息提醒 JSON |
attachments_json |
LONGTEXT |
附件 JSON |
context_used_json |
LONGTEXT |
上下文 JSON |
adapter_error_code |
VARCHAR(128) |
Adapter 契约错误代码,仅在当前 event 不建业务任务时保存 |
adapter_error_message |
VARCHAR(512) |
Adapter 契约错误说明,仅保存安全摘要 |
created_at / updated_at |
DATETIME(6) |
创建和更新时间 |
索引建议:
- 唯一索引:
hotel_id + item_idempotency_key - 普通索引:
hotel_id + source_message_id + source_event_index - 普通索引:
hotel_id + result_type + ai_task_type - 普通索引:
hotel_id + route_code - 普通索引:
hotel_id + system_process_category - 普通索引:
hotel_id + group_code - 普通索引:
hotel_id + confirmation_number
7. 订单表
表名:workflow_reservation_order
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
系统内部订单 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
order_key_type |
VARCHAR(32) |
业务号类型:GROUP_CODE、CONFIRMATION_NUMBER、TEMPORARY |
order_business_key |
VARCHAR(128) |
真实业务号 |
active_business_key |
VARCHAR(128) |
ACTIVE 订单唯一约束辅助键,ACTIVE 业务订单时等于真实业务号,其他状态为空 |
temporary_order_code |
VARCHAR(128) |
临时订单展示编号 |
order_status |
VARCHAR(32) |
订单状态 |
business_key_source |
VARCHAR(64) |
业务号来源,例如 AI_CANDIDATE、USER_CONFIRMED、OPERA_SIMULATION_RESULT |
business_key_backfilled_at |
DATETIME(6) |
New Booking 成功后回填真实业务号的 UTC 时间 |
display_name |
VARCHAR(256) |
前端展示名称 |
source_message_id |
BIGINT |
首次创建该订单的来源消息 |
created_from_task_id |
BIGINT |
首次创建该订单的任务 ID,可为空 |
ended_at |
DATETIME(6) |
订单进入 ENDED 的时间 |
logic_deleted_at |
DATETIME(6) |
逻辑删除时间 |
logic_deleted_reason |
VARCHAR(512) |
逻辑删除原因 |
version |
BIGINT |
乐观锁版本 |
created_at / updated_at |
DATETIME(6) |
创建和更新时间 |
索引建议:
- 普通索引:
hotel_id + order_status + updated_at - 普通索引:
hotel_id + order_key_type + order_business_key - 唯一索引:
hotel_id + order_key_type + active_business_key,用于保证同一hotel_id + GROUP_CODE/hotel_id + CONFIRMATION_NUMBER只能有一个 ACTIVE 订单 - 唯一索引:
hotel_id + temporary_order_code
说明:
- MySQL 第一版不依赖部分唯一索引;通过
active_business_key在 ACTIVE 业务订单时写入真实业务号、非 ACTIVE 或临时订单为空,配合唯一索引保证同一业务号只能有一个 ACTIVE 订单。 LOGIC_DELETED订单不得再挂新任务。
8. 任务表
表名:workflow_reservation_task
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
系统任务 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
order_id |
BIGINT |
当前挂靠订单 ID |
source_message_id |
BIGINT |
来源消息 ID |
ai_transition_id |
BIGINT |
AI 过渡记录 ID |
result_type |
VARCHAR(32) |
AI 结果类型 |
ai_task_type |
VARCHAR(64) |
AI 原始任务类型 |
system_task_type |
VARCHAR(64) |
系统主任务类型 |
task_card_type |
VARCHAR(64) |
任务卡类型 |
task_subtype |
VARCHAR(128) |
业务动作 subtype |
task_status |
VARCHAR(32) |
任务状态 |
queue_participation |
TINYINT(1) |
是否参与订单执行队列 |
execution_order |
INT |
同订单执行顺序 |
parent_task_id |
BIGINT |
父任务 ID,可为空 |
parent_source_event_index |
INT |
父任务 source event index |
linked_task_group_id |
VARCHAR(128) |
联动任务组 ID |
blocked_until_parent_completed |
TINYINT(1) |
是否等待父任务完成 |
last_failure_reason |
VARCHAR(512) |
最近失败原因摘要 |
confirmed_at |
DATETIME(6) |
用户确认时间 |
completed_at |
DATETIME(6) |
完成时间 |
version |
BIGINT |
乐观锁版本 |
created_at / updated_at |
DATETIME(6) |
创建和更新时间 |
索引建议:
- 普通索引:
hotel_id + order_id + queue_participation + execution_order - 唯一索引:
hotel_id + order_id + queue_participation + execution_order,用于防止并发创建任务时出现相同队列序号 - 普通索引:
hotel_id + task_status + updated_at - 普通索引:
hotel_id + source_message_id - 普通索引:
hotel_id + ai_transition_id
队列规则:
queue_participation=false的任务不阻塞队列,适用于Message Notification。- 是否可编辑、可确认、可执行由服务层实时计算。
- 当前任务前面存在未完成且参与队列的任务时,本任务只能查看。
9. 任务卡确认数据表
表名:workflow_reservation_task_card
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
任务卡 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
task_id |
BIGINT |
所属任务 |
task_card_type |
VARCHAR(64) |
任务卡类型 |
field_contract_version |
VARCHAR(64) |
字段契约版本,当前 P0 字段矩阵写 20260711-p0 |
ai_payload_json |
LONGTEXT |
任务卡使用的 AI 原始 JSON 快照 |
draft_payload_json |
LONGTEXT |
用户编辑草稿,可为空 |
confirmed_payload_json |
LONGTEXT |
用户确认后的最终 payload |
confirmed_by |
VARCHAR(128) |
确认人 |
confirmed_at |
DATETIME(6) |
确认时间 |
created_at / updated_at |
DATETIME(6) |
创建和更新时间 |
索引建议:
- 唯一索引:
hotel_id + task_id - 普通索引:
hotel_id + task_card_type
说明:
- 第一版任务卡字段配置写在代码中,但通过
TaskCardFieldDefinitionProvider之类的接口提供,避免 Controller / Service 直接依赖硬编码数组。 - 后续如果改为数据库配置,只替换 Provider 实现,不改任务核心流程。
- 字段契约从
code-v1迁移到20260711-p0时,不能盲目改写已经存在用户草稿或确认结果的历史任务卡。当前 V18 仅更新draft_payload_json IS NULL且confirmed_payload_json IS NULL的旧任务卡;已存在 payload 的历史数据继续保留原field_contract_version,待重新保存、最终确认或后续专项 backfill 时再迁移。
10. 审计表
表名:workflow_reservation_audit_log
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
审计 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
order_id |
BIGINT |
订单 ID,可为空 |
task_id |
BIGINT |
任务 ID,可为空 |
operation_id |
BIGINT |
OPERA 模拟操作 ID,可为空 |
actor_type |
VARCHAR(32) |
操作人类型,例如 SYSTEM、USER、SUPERAGENT |
actor_id |
VARCHAR(128) |
操作人标识 |
action |
VARCHAR(128) |
操作类型 |
reason |
VARCHAR(512) |
操作原因 |
before_snapshot_json |
LONGTEXT |
变更前摘要 JSON |
after_snapshot_json |
LONGTEXT |
变更后摘要 JSON |
occurred_at |
DATETIME(6) |
发生时间 |
created_at |
DATETIME(6) |
记录创建时间 |
索引建议:
- 普通索引:
hotel_id + order_id + occurred_at - 普通索引:
hotel_id + task_id + occurred_at - 普通索引:
hotel_id + action + occurred_at
审计不得保存 Secret、Token、完整邮件正文、真实附件 URL 或不必要的个人敏感信息。
11. OPERA 模拟逻辑操作表
表名:workflow_reservation_opera_operation
一条任务可能生成多条 OPERA 模拟逻辑操作。
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
OPERA 模拟操作 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
order_id |
BIGINT |
订单 ID |
task_id |
BIGINT |
任务 ID |
operation_sequence |
INT |
同任务下操作顺序,从 1 开始 |
operation_code |
VARCHAR(64) |
模拟操作代码,第一版固定 SIMULATE_PRECHECK 和 SIMULATE_WRITE |
operation_name |
VARCHAR(128) |
模拟操作展示名称 |
operation_status |
VARCHAR(32) |
操作状态:PENDING、SUCCEEDED、FAILED |
request_payload_json |
LONGTEXT |
生成该操作时使用的模拟请求摘要,不保存完整邮件原文 |
attempt_count |
INT |
已执行 attempt 次数 |
last_attempt_id |
BIGINT |
最近一次 attempt ID |
last_error_message |
VARCHAR(512) |
最近一次失败原因摘要 |
created_at / updated_at |
DATETIME(6) |
创建和更新时间 |
索引建议:
- 唯一索引:
hotel_id + task_id + operation_sequence - 普通索引:
hotel_id + task_id + operation_status - 普通索引:
hotel_id + order_id + operation_sequence
12. OPERA 模拟尝试记录表
表名:workflow_reservation_opera_operation_attempt
每次执行或重试都新增一条 attempt,不能覆盖历史。
| 字段 | 类型建议 | 中文说明 |
|---|---|---|
id |
BIGINT |
尝试记录 ID |
hotel_id |
VARCHAR(64) |
酒店或业务上下文 |
order_id |
BIGINT |
订单 ID |
task_id |
BIGINT |
所属任务 |
operation_id |
BIGINT |
所属逻辑操作 |
attempt_number |
INT |
第几次尝试,从 1 开始 |
attempt_status |
VARCHAR(32) |
尝试状态:SUCCEEDED、FAILED |
request_payload_json |
LONGTEXT |
本次模拟请求 payload |
response_payload_json |
LONGTEXT |
本次模拟响应 payload |
error_message |
VARCHAR(512) |
失败原因摘要 |
started_at |
DATETIME(6) |
开始时间 |
finished_at |
DATETIME(6) |
结束时间 |
created_at |
DATETIME(6) |
记录创建时间 |
索引建议:
- 唯一索引:
hotel_id + operation_id + attempt_number - 普通索引:
hotel_id + task_id + created_at - 普通索引:
hotel_id + operation_id + attempt_number
规则:
- 用户不能跳过失败的 OPERA 模拟操作。
- 失败后操作进入
FAILED,任务保持未完成;因为队列规则里任务FAILED视为已结束,第一版不能把 OPERA 操作失败直接落成任务FAILED,避免后续任务被错误放行。 - 不允许用户强制把失败任务改成
COMPLETED。 - 后续真实 OPERA 接入时,应通过 adapter 把真实响应映射到
response_payload_json,业务号候选字段需在拿到真实结构后再补稳定字段或 JSON 结构。
13. 订单业务号回填
只有 NEW_BOOKING 且 confirmed_payload_json 没有可用业务号时,才需要从 OPERA 成功结果回填订单业务号。
当前第一版后端不做业务号回填,也没有落地 business_key_candidates_json 字段。后续拿到真实 OPERA 返回结构后,再通过低耦合 adapter 解析候选业务号并补充稳定字段或 JSON 结构。候选结构可以参考:
{
"key_type": "CONFIRMATION_NUMBER",
"business_key": "CNF123456",
"source": "OPERA_SIMULATION_RESULT",
"raw_path": "reserved.for.future.real.opera.path"
}
说明:
- 当前没有真实 OPERA 返回样例,
raw_path只能预留。 - FIT Reservation 回填
CONFIRMATION_NUMBER。 - Group / Block / Allotment 回填
GROUP_CODE或后续确认的对应业务号类型。
14. 第一版不落库的内容
以下内容第一版不建议单独建表:
- 任务卡字段矩阵配置:先写在代码 Provider 中。
- 任务阻塞状态:实时计算,不落
BLOCKED。 - OPERA 真实接口字段映射:后续真实系统接入后在 adapter 层补充。
- 全量 Excel 字段路径:后端引用
docs/import/20260706/任务卡展示编辑矩阵.xlsx作为完整规则来源,不手抄成数据库。 - 前端展示 / 编辑白名单:V2 阶段前端引用
docs/import/20260708/任务卡前端展示字段表 3.0.xlsx;V3 起以 2026-07-11 P0 冻结基线中的前端 Excel 和路由说明为准。前端白名单不作为后端校验和 OPERA 映射的替代来源。
15. 待确认问题
ENDED是否仅表示取消完成,还是也包含正常完成后的历史订单。- 临时订单逻辑删除前是否需要保留空订单一段时间。
- OPERA 真实响应字段出现后,是否需要新增稳定字段而不是只放 JSON。
- 任务卡字段代码 Provider 的具体类名和包路径,后续实现时按项目规范确认。