16 KiB
查询接口后端开发契约:给开发
本文档给后端开发和开发 AI 使用,用于把 Skill查询接口需求梳理_给开发.md 落成可开发的只读查询接口。
本契约只定义 Skill script 可调用的只读查询接口。
这些接口用于给 Skill 提供判断上下文,不负责创建任务卡、不写 Opera、不改业务数据库、不确认付款、不导入名单。
字段级细节以 查询接口返回字段字典.xlsx 为准;本文档负责定义接口路径、请求方式、统一响应、错误码和各接口的 JSON 结构。
1. 接口总原则
1.1 只读边界
所有接口必须是只读查询:
- 不创建任务卡。
- 不写 Opera。
- 不更新预订。
- 不确认 payment。
- 不生成 receipt。
- 不导入 rooming list。
- 不维护 TA Recorder。
- 不写 Trace / Reservation Notes。
- 不把
manual_review自动升级为normal_task。
接口只能返回事实、候选、状态和证据。
最终业务判断仍由 Skill 根据项目规则输出 ai_task_result。
1.2 Current / History 边界
业务动作必须来自 body_current。
body_thread 只能作为 target binding evidence。
如果查询 key 来自历史邮件,调用方应传:
{
"target_key_source": "body_thread_evidence",
"body_thread_used_only_as_evidence": true
}
后端接口可以使用该 key 查询对象,但不得因为历史邮件里出现旧动作而创建或建议创建任务。
1.3 统一路径建议
第一版建议路径如下:
| 逻辑接口名 | HTTP 方法 | 路径 |
|---|---|---|
query_case_context |
POST |
/api/ai-query/v1/case-context |
query_object_detail |
POST |
/api/ai-query/v1/object-detail |
query_file_parse_context |
POST |
/api/ai-query/v1/file-parse-context |
query_parent_task_context |
POST |
/api/ai-query/v1/parent-task-context |
使用 POST 是为了传复杂查询条件,不代表有写入动作。
2. 通用请求头
建议支持:
| Header | 必填 | 说明 |
|---|---|---|
Content-Type: application/json |
是 | JSON 请求 |
X-Request-Id |
是 | 调用方生成的请求 ID,用于排查 |
X-Source-Message-Id |
建议 | 邮件 ID,方便日志串联 |
X-AI-Trace-Id |
建议 | AI 运行链路 ID |
鉴权方式由信息系统统一确定,本文不限定。
3. 通用响应包
所有接口统一返回:
{
"success": true,
"request_id": "",
"trace_id": "",
"data": {},
"warnings": [],
"error": null
}
失败时:
{
"success": false,
"request_id": "",
"trace_id": "",
"data": null,
"warnings": [],
"error": {
"code": "",
"message": "",
"details": {}
}
}
3.1 通用错误码
| code | HTTP 状态 | 说明 |
|---|---|---|
BAD_REQUEST |
400 | 请求 JSON 格式错误或字段类型错误 |
MISSING_REQUIRED_FIELD |
400 | 缺少必填字段 |
UNAUTHORIZED |
401 | 未鉴权 |
FORBIDDEN |
403 | 无权限访问该数据 |
NOT_FOUND |
404 | 查询对象不存在;仅用于按明确 ID 查询的接口 |
FILE_NOT_ACCESSIBLE |
422 | 文件无权限、下载失败或不可访问 |
PARSE_FAILED |
422 | 文件解析失败 |
UPSTREAM_TIMEOUT |
504 | 上游系统超时 |
INTERNAL_ERROR |
500 | 未知后端错误 |
注意:query_case_context 中查不到对象时,通常不应返回 404,而应返回 success=true 且 matched_order_records=[]。因为 Skill 还需要继续判断是否有 pending task、workflow 或终止记录。
4. 接口一:query_case_context
4.1 用途
通过 group_code、confirmation_number、reservation_no 查询业务对象、待处理任务、工作流、终止记录和 key 关系。
适用 Skill:
S01/S02/S03/S04/S05/S06/S07/S08
4.2 Request
{
"source_message_id": "MSG-001",
"source_event_index": 1,
"group_code": "LT260417VIPA",
"confirmation_number": null,
"reservation_no": null,
"object_type_hint": "group_block",
"target_key_source": "body_current",
"body_thread_used_only_as_evidence": false
}
字段要求:
| 字段 | 必填 | 说明 |
|---|---|---|
source_message_id |
是 | 当前邮件 ID |
source_event_index |
是 | 当前事件序号 |
group_code |
条件 | Group / Allotment 优先 key |
confirmation_number |
条件 | FIT 优先 key |
reservation_no |
否 | Opera reservation no,辅助查询 |
object_type_hint |
否 | fit_reservation / group_block / allotment_control_block / booking_record / unknown |
target_key_source |
建议 | body_current / body_thread_evidence / upstream_task_context / system_context |
body_thread_used_only_as_evidence |
建议 | key 来自历史证据时传 true |
group_code、confirmation_number、reservation_no 至少应有一个非空。
如果三个 key 都为空,后端可返回 MISSING_REQUIRED_FIELD;调用方也可选择不调用该接口。
4.3 Response data
{
"matched_order_records": [
{
"object_id": "OBJ-001",
"object_type": "group_block",
"group_code": "LT260417VIPA",
"confirmation_number": null,
"reservation_no": null,
"block_id": "BLK-10001",
"block_name": "LT260417VIPA GROUP",
"guest_name": null,
"group_name": "LT260417VIPA GROUP",
"arrival_date": "2026-04-17",
"departure_date": "2026-04-20",
"room_items": [],
"rate_code": null,
"rate_code_price": null,
"reservation_type": null,
"status": "active",
"cancel_status": "not_cancelled",
"source_table": "opera_group_block_sync",
"last_updated_at": "2026-07-07T10:00:00+08:00"
}
],
"pending_or_open_tasks": [],
"active_workflows": [],
"terminated_records": [],
"target_object_validation": {
"status": "single",
"matched_object_id": "OBJ-001",
"matched_object_type": "group_block",
"can_create_new_booking_task": false,
"can_create_update_task": true,
"can_create_cancel_task": true,
"can_attach_voucher": true,
"can_attach_rooming_list": true,
"needs_manual_review_reason": null
},
"key_relationships": {
"group_code_and_confirmation_same_object": null,
"relationship_evidence": ""
}
}
4.4 后端处理规则
- 查不到对象时返回空数组,不要直接报错。
- 必须同时查:已写入对象、pending/open task、active workflow、terminated record。
- 如果查到多个可能对象,
target_object_validation.status = "multiple"。 - 接口不要返回“应该 normal_task / manual_review”的结论,只返回事实和校验摘要。
- 对 S01,若同 key 已有有效对象或 pending/open task,Skill 会倾向
manual_review。 - 对 S04/S05,如果无已写入对象但有可承接的 pending New Booking / 上游任务,仍可作为目标候选返回。
5. 接口二:query_object_detail
5.1 用途
按 object_id 查询更完整的对象快照,供 S02 建 before/after、S03 判断取消状态、S07 extra bed 取 rate_code_price、S04/S05/S08 做目标展示和硬校验。
适用 Skill:
S02/S03/S04/S05/S07/S08
5.2 Request
{
"object_id": "OBJ-001",
"object_type": "group_block"
}
字段要求:
| 字段 | 必填 | 说明 |
|---|---|---|
object_id |
是 | query_case_context 返回的对象 ID |
object_type |
建议 | 对象类型,可帮助后端选择查询表 |
5.3 Response data
{
"object_id": "OBJ-001",
"object_type": "group_block",
"group_code": "LT260417VIPA",
"confirmation_number": null,
"reservation_no": null,
"block_id": "BLK-10001",
"block_name": "LT260417VIPA GROUP",
"status": "active",
"arrival_date": "2026-04-17",
"departure_date": "2026-04-20",
"nights": 3,
"guest_count": null,
"room_items": [
{
"room_type": "TWN",
"pms_room_type_code": "RM2",
"room_quantity": 2,
"rate_code": "WHO2",
"rate_code_price": 2000
}
],
"rate_code": "WHO2",
"rate_code_price": 2000,
"reservation_type": "TA",
"can_update": true,
"can_cancel": true,
"hard_validation_warnings": []
}
5.4 后端处理规则
object_id明确不存在时可返回 404NOT_FOUND。rate_code_price如果后端当前拿不到,应返回null,并在hard_validation_warnings说明原因。- S07 extra bed 不会自己计算最终房价;后端需提供能否取得
rate_code_price的事实。 room_items[]可先返回摘要,第一版不要求完整 Opera 明细字段。
6. 接口三:query_file_parse_context
6.1 用途
查询附件、图片、PDF、OCR、Excel / workbook、QBD / LianTai 表格、voucher、rooming list 文件解析结果。
适用 Skill:
S01/S02/S03/S04/S05/S06/S07/S08
6.2 Request
{
"source_message_id": "MSG-001",
"source_event_index": 1,
"file_reference": "file://attachment-001",
"parse_need": "voucher"
}
parse_need 枚举建议:
| 值 | 用途 |
|---|---|
voucher |
S04 voucher / payment slip |
rooming_list |
S05 / S08 名单文件 |
qbd_liantai_table |
S01/S02/S03/S06/S07 表格行证据 |
ocr |
图片/PDF OCR |
generic |
通用解析 |
6.3 Response data
{
"file_reference": "file://attachment-001",
"filename": "voucher.pdf",
"download_status": "ok",
"display_available": true,
"file_type": "pdf",
"ocr_text": "",
"workbook_summary": {},
"sheet_summaries": [],
"highlighted_rows": [],
"row_level_evidence": [],
"voucher_evidence": {
"voucher_subtype": "lian_tai_credit_voucher",
"lian_tai_code_candidate": "LT260417VIPA",
"code_field_read_status": "clear",
"bank_slip_detected": false
},
"list_file_evidence": {},
"parse_warnings": []
}
QBD / LianTai 表格场景可返回:
{
"row_level_evidence": [
{
"sheet_name": "BOOKING 06-2026",
"row_number": 12,
"row_label": "12",
"highlight_status": "yellow",
"action_status_cell_value": "NEW BOOKING",
"raw_date_range": "17/04/26-20/04/26",
"raw_room_type": "TWN",
"raw_room_quantity": "2",
"raw_rate": "2000THB+500",
"before_after_evidence": null,
"strikethrough_state": null,
"cell_reference": "H12",
"ocr_evidence": null
}
]
}
Rooming List 场景可返回:
{
"list_file_evidence": {
"list_evidence_type": "rooming_list",
"file_type_validation": {
"is_guest_level_list": true,
"positive_headers": ["English Name", "Passport No", "Nationality"],
"negative_booking_table_evidence": []
},
"target_key_candidates": [
{
"key_type": "group_code",
"key_value": "HD260510A-1",
"source": "sheet_name"
}
],
"shared_evidence_sheets": ["总名单"]
}
}
6.4 后端处理规则
- 文件不可访问时返回
success=false、FILE_NOT_ACCESSIBLE,或success=true且download_status=failed;项目第一版建议后者,方便 Skill 输出 structuredmanual_review。 - S04 不应从 bank slip OCR 的金额、memo、银行账号、付款人等字段自动生成 group_code。
- S05 不能因为文件是 Excel 就判断为 rooming list,必须返回正向名单证据或负向 booking table 证据。
- QBD / LianTai 多个高亮行应作为多个
row_level_evidence[]返回,Skill 再按行拆分任务。 - 接口只返回证据,不负责决定路由到 S01/S02/S03/S05。
7. 接口四:query_parent_task_context
7.1 用途
查询 linked task 的父任务上下文。
主要用于:
- S07 从 S01 / S02 / S06 派生 Trace / Extra Bed linked task。
- S08 从 S05 Rooming List 派生 TA Recorder linked task。
7.2 Request
{
"source_message_id": "MSG-001",
"parent_source_event_index": 1,
"linked_task_group_id": "LTK-001",
"source_rooming_list_task_id": null
}
字段要求:
| 字段 | 必填 | 说明 |
|---|---|---|
source_message_id |
建议 | 当前邮件 ID |
parent_source_event_index |
条件 | 父事件序号 |
linked_task_group_id |
条件 | 联动任务组 ID |
source_rooming_list_task_id |
S08条件 | S05 Rooming List 父任务 ID |
至少应提供 parent_source_event_index、linked_task_group_id、source_rooming_list_task_id 中一个。
7.3 Response data
{
"parent_task": {
"task_id": "TASK-001",
"task_type": "Rooming List",
"task_status": "confirmed",
"case_keys": {
"group_code": "HD260510A-1"
},
"source_event_index": 1
},
"linked_tasks": [],
"execution_order": [
{
"task_id": "TASK-001",
"execution_order": 1
},
{
"task_id": "TASK-002",
"execution_order": 2
}
],
"parent_confirmed_payload": {
"target_object_id": "OBJ-001",
"file_reference": "file://rooming-list.xlsx"
},
"parent_status": "confirmed",
"attachment_binding": {
"file_reference": "file://rooming-list.xlsx",
"workbook_name": "HD260510A.xlsx",
"sheet_name": "HD260510A-1"
},
"file_reference": "file://rooming-list.xlsx",
"workbook_name": "HD260510A.xlsx",
"sheet_name": "HD260510A-1"
}
7.4 后端处理规则
- 这个接口不能创建 linked task,只能返回父任务和已存在 linked task 上下文。
- S07 linked task 必须等父 S01/S02/S06 完成并确认目标对象后才能真正执行。
- S08 linked task 必须跟随 S05 已确认的目标
group_code,不得从 sheet 名自行猜group_code。 parent_status未完成时,Skill 可输出blocked_until_parent_completed=true。
8. 推荐调用时序
8.1 普通路由级查询
Main Agent 从 body_current 提取 group_code / confirmation_number
-> POST /api/ai-query/v1/case-context
-> routing_context 传给 Skill
-> Skill 判断 normal_task / manual_review
8.2 current 无 key,但 history 有唯一目标 key
body_current 有明确业务动作
-> body_thread 仅提取唯一 target key 作为 evidence
-> POST /api/ai-query/v1/case-context
target_key_source=body_thread_evidence
body_thread_used_only_as_evidence=true
-> Skill 输出中保留 target_key_source 和 hard validation 标记
8.3 文件类任务
当前邮件有附件 / 图片 / PDF / Excel / link
-> POST /api/ai-query/v1/file-parse-context
-> 如解析出 key,再 POST /api/ai-query/v1/case-context
-> Skill 判断任务
8.4 linked task
S01/S02/S06 主任务 + S07 linked task
或 S05 Rooming List + S08 TA Recorder
-> POST /api/ai-query/v1/parent-task-context
-> 返回父任务状态、目标对象、附件绑定和执行顺序
9. 与两张字典表的关系
9.1 与 查询接口返回字段字典.xlsx
本文定义接口级契约。
字段名、字段路径、数据类型、是否数组、是否必传/必返,以 查询接口返回字段字典.xlsx 为准。
9.2 与 AI输出参数并集字典.xlsx
AI输出参数并集字典.xlsx 只定义 Skill 最终输出到过渡表的字段。
查询接口返回字段不要求全部进入 AI 输出 JSON。
如果某个查询字段只用于 Skill 判断,可只进入 context_used 摘要,不必作为 AI 输出字段展开。
当前建议重点关注这些查询字段:
reservation_noobject_idobject_typeblock_idblock_namerate_code_price
这些字段虽属于查询上下文,但后续任务卡展示、Opera API 定位、底表展示和硬校验都会用到,开发应在接口层明确返回。
10. 后端验收清单
后端完成后,至少应满足:
- 四个接口都可通过 POST JSON 调用。
- 所有接口都使用统一响应包。
- 查询不到对象时,
query_case_context返回空数组而不是直接报错。 - 每次查询都能带上
source_message_id、source_event_index或请求追踪信息。 - 支持 target key 来自
body_thread_evidence,但不从 history 触发任务。 query_case_context同时返回已写入对象、pending/open task、active workflow、terminated record。query_file_parse_context能区分 voucher、rooming list、QBD/LianTai table 证据。query_parent_task_context能返回父任务状态和执行顺序。- 所有接口不发生写入动作。