feat: 添加线索引擎、NLQ、场景检测、前端界面等核心功能模块
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"""线索看板与处置 API(R7/R17/R18/R20)。
|
||||
|
||||
注意:不提供删除线索的端点(R19 线索不可删,独立性硬约束)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.schemas import (
|
||||
AdjudicateRequest,
|
||||
AssignRequest,
|
||||
ClueOut,
|
||||
DashboardSummary,
|
||||
)
|
||||
from app.clues import service as clue_svc
|
||||
from app.clues.models import Clue, ClueStatus, ConfidenceTier
|
||||
from app.db import get_session
|
||||
|
||||
router = APIRouter(prefix="/clues", tags=["clues"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[ClueOut])
|
||||
def list_clues(
|
||||
status: ClueStatus | None = Query(default=None),
|
||||
scenario_code: str | None = Query(default=None),
|
||||
confidence: ConfidenceTier | None = Query(default=None),
|
||||
session: Session = Depends(get_session),
|
||||
) -> list[Clue]:
|
||||
return clue_svc.list_clues(
|
||||
session, status=status, scenario_code=scenario_code, confidence=confidence
|
||||
)
|
||||
|
||||
|
||||
@router.get("/summary", response_model=DashboardSummary)
|
||||
def summary(session: Session = Depends(get_session)) -> DashboardSummary:
|
||||
"""运营看板汇总(R18/R21 的基础指标)。"""
|
||||
clues = session.query(Clue).all()
|
||||
by_status: dict[str, int] = {}
|
||||
by_conf: dict[str, int] = {}
|
||||
by_scenario: dict[str, int] = {}
|
||||
total_amount = 0.0
|
||||
for c in clues:
|
||||
by_status[c.status.value] = by_status.get(c.status.value, 0) + 1
|
||||
by_conf[c.confidence.value] = by_conf.get(c.confidence.value, 0) + 1
|
||||
by_scenario[c.scenario_code] = by_scenario.get(c.scenario_code, 0) + 1
|
||||
total_amount += c.amount_involved or 0.0
|
||||
return DashboardSummary(
|
||||
total=len(clues),
|
||||
by_status=by_status,
|
||||
by_confidence=by_conf,
|
||||
by_scenario=by_scenario,
|
||||
total_amount_involved=total_amount,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{clue_id}", response_model=ClueOut)
|
||||
def get_clue(clue_id: uuid.UUID, session: Session = Depends(get_session)) -> Clue:
|
||||
clue = session.get(Clue, clue_id)
|
||||
if clue is None:
|
||||
raise HTTPException(status_code=404, detail="线索不存在")
|
||||
return clue
|
||||
|
||||
|
||||
@router.post("/{clue_id}/assign", response_model=ClueOut)
|
||||
def assign_clue(
|
||||
clue_id: uuid.UUID, req: AssignRequest, session: Session = Depends(get_session)
|
||||
) -> Clue:
|
||||
clue = session.get(Clue, clue_id)
|
||||
if clue is None:
|
||||
raise HTTPException(status_code=404, detail="线索不存在")
|
||||
return clue_svc.assign(session, clue, assignee=req.assignee, actor=req.actor)
|
||||
|
||||
|
||||
@router.post("/{clue_id}/adjudicate", response_model=ClueOut)
|
||||
def adjudicate_clue(
|
||||
clue_id: uuid.UUID, req: AdjudicateRequest, session: Session = Depends(get_session)
|
||||
) -> Clue:
|
||||
clue = session.get(Clue, clue_id)
|
||||
if clue is None:
|
||||
raise HTTPException(status_code=404, detail="线索不存在")
|
||||
clue_svc.adjudicate(session, clue, confirmed=req.confirmed, actor=req.actor, note=req.note)
|
||||
return clue
|
||||
Reference in New Issue
Block a user