- Introduced new endpoints `/api/analyst/query/{trace_id}/sample` and `/api/analyst/escalate` for sampling query results and escalating issues to human analysts, respectively.
- Enhanced `AnalystAgent` to support sampling of SQL results based on trace ID and to handle escalation requests, improving user experience in error scenarios.
- Updated `analyst_schemas.py` to include `EscalateRequest` for structured escalation requests.
- Added corresponding frontend API calls and UI components to facilitate user interactions with the new features.
- Implemented unit tests to ensure the reliability of the new functionalities.
This update significantly enhances the analytical capabilities of the application, allowing users to retrieve detailed query samples and escalate issues effectively.
117 lines
3.8 KiB
Python
117 lines
3.8 KiB
Python
"""代销平台 · 客户 L0 与资产读 API(canonical · 不要求 X-Agent-Type)。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from fastapi import APIRouter, Depends, Query, Request
|
||
|
||
from app.api.deps import (
|
||
AuthContext,
|
||
assert_platform_customer_access,
|
||
get_platform_auth_context,
|
||
)
|
||
from app.repository.core_ro import CoreReadOnlyRepository
|
||
from app.repository.risk_repository import RiskRepository
|
||
from app.service.platform import customer_service
|
||
from app.utils.exceptions import ApiError
|
||
from app.utils.response import ok
|
||
|
||
router = APIRouter(prefix="/api/customers", tags=["platform-customers"])
|
||
|
||
|
||
def _core_ro() -> CoreReadOnlyRepository:
|
||
return CoreReadOnlyRepository()
|
||
|
||
|
||
def _risk_repo() -> RiskRepository:
|
||
return RiskRepository()
|
||
|
||
|
||
def _trace_id(request: Request) -> str:
|
||
return getattr(request.state, "trace_id", "unknown")
|
||
|
||
|
||
@router.get("/{customer_id}")
|
||
def get_customer_api(
|
||
customer_id: str,
|
||
request: Request,
|
||
auth: AuthContext = Depends(get_platform_auth_context),
|
||
) -> dict:
|
||
core = _core_ro()
|
||
repo = _risk_repo()
|
||
assert_platform_customer_access(auth, customer_id, core_ro=core, risk_repo=repo)
|
||
row = customer_service.get_customer_l0(customer_id, core_ro=core)
|
||
if row is None:
|
||
raise ApiError(404, "NOT_FOUND", f"customer not found: {customer_id}")
|
||
return ok(row, _trace_id(request)).model_dump()
|
||
|
||
|
||
@router.get("/{customer_id}/holdings")
|
||
def list_holdings_api(
|
||
customer_id: str,
|
||
request: Request,
|
||
limit: int = Query(500, ge=1, le=500),
|
||
auth: AuthContext = Depends(get_platform_auth_context),
|
||
) -> dict:
|
||
core = _core_ro()
|
||
repo = _risk_repo()
|
||
assert_platform_customer_access(auth, customer_id, core_ro=core, risk_repo=repo)
|
||
data = customer_service.list_holdings(customer_id, limit=limit, core_ro=core)
|
||
return ok(data, _trace_id(request)).model_dump()
|
||
|
||
|
||
@router.post("/{customer_id}/threshold-check")
|
||
def threshold_check_api(
|
||
customer_id: str,
|
||
request: Request,
|
||
push: bool = Query(False, description="演示:命中时 Redis pub 主动推送"),
|
||
auth: AuthContext = Depends(get_platform_auth_context),
|
||
) -> dict:
|
||
"""C-04:主动扫描亏损阈值(可选 push)。"""
|
||
from app.service.threshold_service import run_threshold_check
|
||
|
||
core = _core_ro()
|
||
repo = _risk_repo()
|
||
assert_platform_customer_access(auth, customer_id, core_ro=core, risk_repo=repo)
|
||
holdings = customer_service.list_holdings(customer_id, limit=500, core_ro=core)
|
||
rows = holdings.get("items") if isinstance(holdings, dict) else []
|
||
if not isinstance(rows, list):
|
||
rows = []
|
||
result = run_threshold_check(
|
||
customer_id,
|
||
rows,
|
||
trace_id=_trace_id(request),
|
||
push=push,
|
||
)
|
||
return ok(result, _trace_id(request)).model_dump()
|
||
|
||
|
||
@router.get("/{customer_id}/trades")
|
||
def list_trades_api(
|
||
customer_id: str,
|
||
request: Request,
|
||
limit: int = Query(50, ge=1, le=200),
|
||
offset: int = Query(0, ge=0),
|
||
auth: AuthContext = Depends(get_platform_auth_context),
|
||
) -> dict:
|
||
core = _core_ro()
|
||
repo = _risk_repo()
|
||
assert_platform_customer_access(auth, customer_id, core_ro=core, risk_repo=repo)
|
||
data = customer_service.list_trades(
|
||
customer_id, limit=limit, offset=offset, core_ro=core
|
||
)
|
||
return ok(data, _trace_id(request)).model_dump()
|
||
|
||
|
||
@router.get("/{customer_id}/products")
|
||
def list_customer_products_api(
|
||
customer_id: str,
|
||
request: Request,
|
||
limit: int = Query(50, ge=1, le=200),
|
||
auth: AuthContext = Depends(get_platform_auth_context),
|
||
) -> dict:
|
||
core = _core_ro()
|
||
repo = _risk_repo()
|
||
assert_platform_customer_access(auth, customer_id, core_ro=core, risk_repo=repo)
|
||
data = customer_service.list_customer_products(customer_id, limit=limit, core_ro=core)
|
||
return ok(data, _trace_id(request)).model_dump()
|