落地私聊附件询价:发卡走 H5 收 Excel/PDF,认成口播再复用三线文字流程,避免附件卡在识别或退成纯文本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-18 17:25:38 +08:00
co-authored by Cursor
parent 8219dce84b
commit 65ef75f1ef
19 changed files with 2420 additions and 8 deletions
+46 -1
View File
@@ -10,14 +10,20 @@ from __future__ import annotations
import logging
from typing import Any, Optional
from fastapi import APIRouter, HTTPException, Query
from fastapi import APIRouter, File, HTTPException, Query, UploadFile
from fastapi.responses import HTMLResponse
from pydantic import BaseModel, Field
from agent.channel.clarification import validate_token
from agent.channel.h5.copy_page import expired_copy_page, render_copy_page
from agent.channel.h5.copy_store import lookup_copy_token
from agent.channel.h5.upload_page import (
expired_upload_page,
render_upload_message,
render_upload_page,
)
from agent.handlers.card_action import handle_card_action
from agent.policy import inquiry_copy as copy
logger = logging.getLogger(__name__)
@@ -67,6 +73,45 @@ 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:
"""
打开附件上传页。身份只认 token;过期给 410,不吐内部栈。
"""
from agent.handlers.attachment_inquiry import lookup_upload_sender
if lookup_upload_sender(token) is None:
return HTMLResponse(expired_upload_page(), status_code=410)
return HTMLResponse(render_upload_page())
@router.post("/upload/{token}", response_class=HTMLResponse)
async def h5_upload_submit(
token: str,
files: Optional[list[UploadFile]] = File(default=None),
) -> HTMLResponse:
"""
接收多个 Excel/PDF。请求线程只验 token、读字节、入队,不等千问。
"""
from agent.handlers.attachment_inquiry import accept_h5_uploads, lookup_upload_sender
sender_id = lookup_upload_sender(token)
if sender_id is None:
return HTMLResponse(expired_upload_page(), status_code=410)
incoming: list[tuple[str, bytes]] = []
for item in files or []:
name = item.filename or ""
raw = await item.read()
incoming.append((name, raw))
phase, hint = accept_h5_uploads(sender_id=sender_id, files=incoming, process_now=False)
logger.info("h5.upload phase=%s sender=%s n=%s", phase, sender_id, len(incoming))
if phase == "expired":
return HTMLResponse(expired_upload_page(), status_code=410)
if phase in {"reject_type", "need_file"}:
return HTMLResponse(render_upload_message("无法上传", hint), status_code=400)
return HTMLResponse(render_upload_message("上传成功", copy.ATTACH_H5_OK))
@router.get("/{token}")
def h5_open(token: str) -> dict[str, Any]:
"""