fix(compare-record): admin 放行 cancelled 筛选 + llm_calls 拉取改后台任务

- admin 列表 status pattern 加 cancelled(客户端会上报用户终止比价)
- 上报端点拉 pricebot llm_calls 改 BackgroundTasks 异步回填, 不阻塞上报响应(独立 session)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 23:32:36 +08:00
parent f790519765
commit 3cab3b9055
2 changed files with 35 additions and 13 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ def list_comparison_records(
db: AdminDb,
user_id: Annotated[int | None, Query()] = None,
phone: Annotated[str | None, Query(description="手机号前缀")] = None,
status: Annotated[str | None, Query(pattern="^(success|failed)$")] = None,
status: Annotated[str | None, Query(pattern="^(success|failed|cancelled)$")] = None,
business_type: Annotated[str | None, Query()] = None,
limit: Annotated[int, Query(ge=1, le=100)] = 20,
cursor: Annotated[int | None, Query()] = None,
+34 -12
View File
@@ -15,9 +15,11 @@ from __future__ import annotations
import logging
from fastapi import APIRouter, HTTPException, Query, status
from fastapi import APIRouter, BackgroundTasks, HTTPException, Query, status
from app.api.deps import CurrentUser, DbSession
from app.db.session import SessionLocal
from app.models.comparison import ComparisonRecord
from app.repositories import comparison as crud_compare
from app.services.pricebot_llm_calls import fetch_llm_calls
from app.schemas.compare_record import (
@@ -40,29 +42,49 @@ router = APIRouter(prefix="/api/v1/compare", tags=["compare-record"])
summary="上报一次比价结果(幂等)",
)
def report_record(
payload: ComparisonRecordIn, user: CurrentUser, db: DbSession
payload: ComparisonRecordIn,
user: CurrentUser,
db: DbSession,
background_tasks: BackgroundTasks,
) -> ComparisonRecordCreatedOut:
rec = crud_compare.upsert_record(db, user_id=user.id, payload=payload)
# 同机拉 pricebot 本次比价的 LLM 调用明细落库(best-effort,失败不阻断上报)。
# llm_call_count/retry_count 直接由明细派生:次数=条数,重试=带 error 的条数。
calls = fetch_llm_calls(rec.trace_id)
if calls:
rec.llm_calls = calls
rec.llm_call_count = len(calls)
rec.retry_count = sum(1 for c in calls if c.get("error"))
db.commit()
# LLM 调用明细异步回填:同机拉 pricebot llm_calls 最长 5s 且是 best-effort,放后台
# 任务做,不阻塞上报响应(顺带给 pricebot 落盘留足余量)。upsert 已 commit,后台用
# 独立 session 按 record id 回填 llm_calls + 派生 llm_call_count/retry_count。
background_tasks.add_task(_backfill_llm_calls, rec.id, rec.trace_id)
logger.info(
"compare record user=%s trace=%s biz=%s status=%s saved=%s llm_calls=%d",
"compare record user=%s trace=%s biz=%s status=%s saved=%s (llm_calls backfill queued)",
user.id,
rec.trace_id,
rec.business_type,
rec.status,
rec.saved_amount_cents,
len(calls),
)
return ComparisonRecordCreatedOut(id=rec.id)
def _backfill_llm_calls(record_id: int, trace_id: str) -> None:
"""后台回填本次比价的 LLM 调用明细 + 派生 llm_call_count/retry_count。
独立 DB session(请求 session 此时已关);拉取/写库失败只 log,绝不影响已落库的上报。"""
calls = fetch_llm_calls(trace_id)
if not calls:
return
db = SessionLocal()
try:
rec = db.get(ComparisonRecord, record_id)
if rec is None:
return
rec.llm_calls = calls
rec.llm_call_count = len(calls)
rec.retry_count = sum(1 for c in calls if c.get("error"))
db.commit()
logger.info("backfill llm_calls trace=%s n=%d", trace_id, len(calls))
except Exception as e: # noqa: BLE001 best-effort
logger.warning("backfill llm_calls failed trace=%s: %s", trace_id, e)
finally:
db.close()
@router.get(
"/stats",
response_model=CompareStatsOut,