修复附件询价:公网打开上传页不再出骨架 JSON,未上传时打字能正常回话。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 18:54:41 +08:00
co-authored by Cursor
parent 49faa35f47
commit 8e73af0d03
6 changed files with 150 additions and 15 deletions
+53 -9
View File
@@ -2,6 +2,8 @@
H5 补问 / 附件上传 / 复制工单号通道。
本文件职责:挂载 /inquiry-form;用 token 绑定身份或工单号(URL 禁止 userid)。
公网 Nginx 会把 /inquiry-form/upload/{token} 改写成 /inquiry-form/{token},
附件票必须在这两条路径上出同一张上传页、收同一套文件。
禁止:从 URL 读 userid;禁止在本包改六态或直连 TMS;禁止同步 invoke Graph。
"""
@@ -73,11 +75,19 @@ def h5_jsapi_signature(
raise HTTPException(status_code=503, detail="jsapi_unavailable") from exc
@router.get("/upload/{token}", response_class=HTMLResponse)
def h5_upload_open(token: str) -> HTMLResponse:
def _is_upload_token(token: str) -> bool:
"""
打开附件上传页。身份只认 token;过期给 410,不吐内部栈。
附件上传票:allowed_actions 含 upload_attachment。
公网 Nginx 会把 /inquiry-form/upload/{token} 改写成 /inquiry-form/{token},
不能只认 /upload/ 这一条路径。
"""
rec = validate_token(token)
return rec is not None and "upload_attachment" in (rec.allowed_actions or [])
def _upload_page(token: str) -> HTMLResponse:
"""打开附件上传页。身份只认 token;过期给 410,不吐内部栈。"""
from agent.handlers.attachment_inquiry import lookup_upload_sender
if lookup_upload_sender(token) is None:
@@ -85,10 +95,9 @@ def h5_upload_open(token: str) -> HTMLResponse:
return HTMLResponse(render_upload_page())
@router.post("/upload/{token}", response_class=HTMLResponse)
async def h5_upload_submit(
async def _accept_upload_files(
token: str,
files: Optional[list[UploadFile]] = File(default=None),
files: Optional[list[UploadFile]],
) -> HTMLResponse:
"""
接收多个 Excel/PDF。请求线程只验 token、读字节、入队,不等千问。
@@ -112,6 +121,21 @@ async def h5_upload_submit(
return HTMLResponse(render_upload_message("上传成功", copy.ATTACH_H5_OK))
@router.get("/upload/{token}", response_class=HTMLResponse)
def h5_upload_open(token: str) -> HTMLResponse:
"""显式上传路径。公网可能被 Nginx 改写到 /{token},两路必须同一页。"""
return _upload_page(token)
@router.post("/upload/{token}", response_class=HTMLResponse)
async def h5_upload_submit(
token: str,
files: Optional[list[UploadFile]] = File(default=None),
) -> HTMLResponse:
"""显式上传提交。与 POST /{token} 同一套入队逻辑。"""
return await _accept_upload_files(token, files)
@router.get("/air-routes/{token}", response_class=HTMLResponse)
def h5_air_route_open(token: str) -> HTMLResponse:
"""打开空运选线路页。身份只认 token。"""
@@ -205,15 +229,19 @@ def h5_land_route_pick(token: str, index: int = Form(default=-1)) -> HTMLRespons
@router.get("/{token}")
def h5_open(token: str) -> dict[str, Any]:
def h5_open(token: str):
"""
打开补问页:校验 token 未过期。
合同入口:/inquiry-form/{token}。
返回允许动作与 wait_version;不返回内部异常栈。
附件票出上传页(公网 Nginx 也会把 /upload/{token} 改写到这里)。
其它票仍回补问骨架,不吐内部栈。
"""
rec = validate_token(token)
if rec is None:
raise HTTPException(status_code=410, detail="表单已过期或无效,请回企微重新打开")
if "upload_attachment" in (rec.allowed_actions or []):
logger.info("h5.open upload thread=%s", rec.thread_id)
return _upload_page(token)
return {
"status": "ok",
"thread_id": rec.thread_id,
@@ -223,6 +251,22 @@ def h5_open(token: str) -> dict[str, Any]:
}
@router.post("/{token}", response_class=HTMLResponse)
async def h5_post_by_token(
token: str,
files: Optional[list[UploadFile]] = File(default=None),
) -> HTMLResponse:
"""
附件票在 /{token} 上收文件。
浏览器地址仍是 /upload/{token},表单相对提交后被 Nginx 改写到这里。
非附件票不收文件,避免冒充补问提交。
"""
if not _is_upload_token(token):
raise HTTPException(status_code=405, detail="该页不接收文件上传")
return await _accept_upload_files(token, files)
@router.post("/{token}/submit")
def h5_submit(token: str, body: H5SubmitBody) -> dict[str, Any]:
"""