feat(r1-touch): 建议分级+决策类推送+应用前预览 (alert不推送/同title防轰炸/preview对比)
This commit is contained in:
@@ -27,6 +27,8 @@ def _sug_dict(s: AISuggestion) -> dict:
|
||||
"source": s.source,
|
||||
"suggestion_type": s.suggestion_type,
|
||||
"target_type": s.target_type,
|
||||
"category": s.category or "decision",
|
||||
"pushed": s.pushed or 0,
|
||||
"target_id": s.target_id,
|
||||
"title": s.title,
|
||||
"content": s.content,
|
||||
@@ -62,6 +64,7 @@ def create_suggestion(
|
||||
source=data.get("source", "manual"),
|
||||
suggestion_type=suggestion_type,
|
||||
target_type=data.get("target_type", "kpi"),
|
||||
category="alert" if data.get("target_type") == "alert" else data.get("category", "decision"),
|
||||
target_id=data.get("target_id"),
|
||||
title=title,
|
||||
content=data.get("content"),
|
||||
@@ -78,6 +81,7 @@ def create_suggestion(
|
||||
def list_suggestions(
|
||||
status: Optional[str] = Query(None, description="unapplied/applied/dismissed"),
|
||||
suggestion_type: Optional[str] = Query(None),
|
||||
category: Optional[str] = Query(None, description="decision/alert 建议分类过滤"),
|
||||
entity_id: int = Depends(get_entity_id),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -87,6 +91,8 @@ def list_suggestions(
|
||||
query = query.filter(AISuggestion.status == status)
|
||||
if suggestion_type:
|
||||
query = query.filter(AISuggestion.suggestion_type == suggestion_type)
|
||||
if category:
|
||||
query = query.filter(AISuggestion.category == category)
|
||||
items = query.order_by(AISuggestion.created_at.desc()).limit(200).all()
|
||||
return {"data": [_sug_dict(s) for s in items], "total": len(items)}
|
||||
|
||||
@@ -275,6 +281,60 @@ _APPLYERS = {
|
||||
}
|
||||
|
||||
|
||||
@router.get("/{suggestion_id}/preview")
|
||||
def preview_suggestion(suggestion_id: int, db: Session = Depends(get_db),
|
||||
entity_id: int = Depends(get_entity_id)):
|
||||
"""应用前预览:将变更什么(当前值 → 新值),建立信任 (R1触达修复 2026-08-31)
|
||||
|
||||
- kpi_target: {kpi_name, current_target, new_target}
|
||||
- budget_adjust:{kpi_name, period, current_budget, new_budget}
|
||||
- action_plan: {kpi_name, plan_title, assignee, priority, due_date}
|
||||
"""
|
||||
sug = db.query(AISuggestion).filter(AISuggestion.id == suggestion_id).first()
|
||||
if not sug:
|
||||
raise HTTPException(404, "建议不存在")
|
||||
sd = sug.suggestion_data or {}
|
||||
kpi = None
|
||||
kpi_id = sd.get("kpi_id") or sug.target_id
|
||||
if kpi_id:
|
||||
kpi = db.query(KPIDefinition).filter(KPIDefinition.id == kpi_id).first()
|
||||
|
||||
if sug.suggestion_type == "kpi_target":
|
||||
return {"data": {
|
||||
"type": "kpi_target",
|
||||
"kpi_name": kpi.kpi_name if kpi else "KPI#" + str(kpi_id),
|
||||
"current_target": kpi.target_value if kpi else None,
|
||||
"new_target": sd.get("target_value"),
|
||||
}}
|
||||
if sug.suggestion_type == "budget_adjust":
|
||||
period = sd.get("period") or sug.target_type
|
||||
current_budget = None
|
||||
if kpi and period:
|
||||
bp = db.query(BudgetPlan).filter(
|
||||
BudgetPlan.entity_id == sug.entity_id,
|
||||
BudgetPlan.kpi_id == kpi.id,
|
||||
BudgetPlan.period == period,
|
||||
BudgetPlan.status == "active",
|
||||
).order_by(BudgetPlan.id.desc()).first()
|
||||
current_budget = bp.budget_value if bp else None
|
||||
return {"data": {
|
||||
"type": "budget_adjust",
|
||||
"kpi_name": kpi.kpi_name if kpi else "KPI#" + str(kpi_id),
|
||||
"period": period,
|
||||
"current_budget": current_budget,
|
||||
"new_budget": sd.get("budget_value"),
|
||||
}}
|
||||
# action_plan
|
||||
return {"data": {
|
||||
"type": "action_plan",
|
||||
"kpi_name": kpi.kpi_name if kpi else "KPI#" + str(kpi_id),
|
||||
"plan_title": sd.get("title") or sug.title,
|
||||
"assignee": sd.get("assignee") or "",
|
||||
"priority": sd.get("priority") or "medium",
|
||||
"due_date": sd.get("due_date") or "",
|
||||
}}
|
||||
|
||||
|
||||
@router.post("/{suggestion_id}/apply")
|
||||
def apply_suggestion(
|
||||
suggestion_id: int,
|
||||
|
||||
Reference in New Issue
Block a user