Files
th-hotel-simple/docs/import/20260706/查询接口后端开发契约_给开发_副本.md
2026-07-09 11:59:34 +08:00

16 KiB
Raw Blame History

查询接口后端开发契约:给开发

本文档给后端开发和开发 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=truematched_order_records=[]。因为 Skill 还需要继续判断是否有 pending task、workflow 或终止记录。

4. 接口一query_case_context

4.1 用途

通过 group_codeconfirmation_numberreservation_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_codeconfirmation_numberreservation_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 taskSkill 会倾向 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 明确不存在时可返回 404 NOT_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=falseFILE_NOT_ACCESSIBLE,或 success=truedownload_status=failed;项目第一版建议后者,方便 Skill 输出 structured manual_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_indexlinked_task_group_idsource_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_no
  • object_id
  • object_type
  • block_id
  • block_name
  • rate_code_price

这些字段虽属于查询上下文但后续任务卡展示、Opera API 定位、底表展示和硬校验都会用到,开发应在接口层明确返回。

10. 后端验收清单

后端完成后,至少应满足:

  • 四个接口都可通过 POST JSON 调用。
  • 所有接口都使用统一响应包。
  • 查询不到对象时,query_case_context 返回空数组而不是直接报错。
  • 每次查询都能带上 source_message_idsource_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 能返回父任务状态和执行顺序。
  • 所有接口不发生写入动作。