""" H5 补问 / 附件上传通道。 本文件职责:挂载 /inquiry-form;用 clarification token 绑定身份(URL 禁止 userid)。 禁止:从 URL 读 userid;禁止在本包改六态或直连 TMS;禁止同步 invoke Graph。 """ from __future__ import annotations import logging from typing import Any, Optional from fastapi import APIRouter, HTTPException from pydantic import BaseModel, Field from agent.channel.clarification import validate_token from agent.handlers.card_action import handle_card_action logger = logging.getLogger(__name__) router = APIRouter(prefix="/inquiry-form", tags=["h5"]) class H5SubmitBody(BaseModel): """H5 提交体:业务字段放 fields;身份只认服务端 token 绑定。""" action: str = Field(default="submit_clarify") fields: dict[str, Any] = Field(default_factory=dict) @router.get("/ping") def h5_ping() -> dict[str, str]: """存活探测。""" return {"status": "ok", "channel": "h5"} @router.get("/{token}") def h5_open(token: str) -> dict[str, Any]: """ 打开补问页:校验 token 未过期。 返回允许动作与 wait_version;不返回内部异常栈。 """ rec = validate_token(token) if rec is None: raise HTTPException(status_code=410, detail="表单已过期或无效,请回企微重新打开") return { "status": "ok", "thread_id": rec.thread_id, "wait_version": rec.wait_version, "allowed_actions": rec.allowed_actions, "note": "骨架:字段列表后续按合同生成", } @router.post("/{token}/submit") def h5_submit(token: str, body: H5SubmitBody) -> dict[str, Any]: """ 提交补问:消费 token,转入 card_action 骨架(不跑整图)。 副作用:token 一次性;入队/Handler 后续接 Worker。 """ rec = validate_token(token, action=body.action or "submit_clarify", consume=True) if rec is None: raise HTTPException(status_code=410, detail="表单已过期或无效,请回企微重新打开") handle_card_action( sender_id=rec.sender_id, thread_id=rec.thread_id, wait_version=rec.wait_version, action=body.action or "submit_clarify", payload=body.fields, ) logger.info( "h5.submit ok thread=%s wait=%s action=%s", rec.thread_id, rec.wait_version, body.action, ) return {"status": "accepted", "thread_id": rec.thread_id}