落地空运单聊询价字段与线路选择。

必填核对后再建单;多条线路走 H5 点选;费用只展示该线路 TMS 回包,不套用整单空运费。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 15:02:32 +08:00
co-authored by Cursor
parent 8afe909230
commit cfbce2fd69
53 changed files with 3470 additions and 372 deletions
+47 -1
View File
@@ -10,7 +10,7 @@ from __future__ import annotations
import logging
from typing import Any, Optional
from fastapi import APIRouter, File, HTTPException, Query, UploadFile
from fastapi import APIRouter, File, Form, HTTPException, Query, UploadFile
from fastapi.responses import HTMLResponse
from pydantic import BaseModel, Field
@@ -112,6 +112,52 @@ async def h5_upload_submit(
return HTMLResponse(render_upload_message("上传成功", copy.ATTACH_H5_OK))
@router.get("/air-routes/{token}", response_class=HTMLResponse)
def h5_air_route_open(token: str) -> HTMLResponse:
"""打开空运选线路页。身份只认 token。"""
from agent.channel.h5.air_route_page import expired_air_route_page, render_air_route_page
from agent.channel.h5.air_route_store import lookup_air_route_token
rec = lookup_air_route_token(token)
if rec is None:
return HTMLResponse(expired_air_route_page(), status_code=410)
return HTMLResponse(render_air_route_page(rec))
@router.post("/air-routes/{token}", response_class=HTMLResponse)
def h5_air_route_pick(token: str, index: int = Form(default=-1)) -> HTMLResponse:
"""
选定线路。请求线程只验 token、入出站队列,不查 TMS、不跑整图。
"""
from agent.channel.h5.air_route_page import (
expired_air_route_page,
render_air_route_ok,
render_air_route_page,
)
from agent.channel.h5.air_route_store import lookup_air_route_token
from agent.channel.queue import get_message_store
from agent.policy.air_text_flow import get_air_text_flow
rec = lookup_air_route_token(token)
if rec is None:
return HTMLResponse(expired_air_route_page(), status_code=410)
sender_id = str(rec.get("sender_id") or "").strip()
if not sender_id:
return HTMLResponse(expired_air_route_page(), status_code=410)
def reply(text: str, extra=None) -> None:
store = get_message_store()
store.enqueue_outbound(touser=sender_id, content=text, payload=dict(extra or {}))
phase = get_air_text_flow().select_air_route(
sender_id=sender_id, index=int(index), reply=reply
)
logger.info("h5.air_route pick sender=%s phase=%s idx=%s", sender_id, phase, index)
if phase in {"route_expired", "route_invalid"}:
return HTMLResponse(render_air_route_page(rec), status_code=400)
return HTMLResponse(render_air_route_ok())
@router.get("/{token}")
def h5_open(token: str) -> dict[str, Any]:
"""