Files
aiagent/backend/app/api/knowledge_dashboard.py
renjianbo 876789fac1 feat: multi-tenant workspace isolation, RBAC, sidebar nav, billing, and Android enhancements
- Backend: workspace_id isolation for 14 model tables + safe migration/backfill
- Backend: RBAC system with 4 roles and 23 permissions, seeded on startup
- Backend: workspace admin endpoints (list/manage all workspaces)
- Backend: admin user management API (CRUD, reset password)
- Backend: billing API with subscription plans, usage tracking, rate limiting
- Backend: fix system_logs.py UNION query and wrong column references
- Backend: WebSocket JWT auth and workspace enforcement
- Frontend: sidebar navigation replacing top dropdown menu
- Frontend: user management page (Users.vue) for admins
- Frontend: enhanced Workspaces.vue with admin table view
- Frontend: workspace RBAC computed properties in user store
- Android: agent marketplace, billing/subscription UI, onboarding wizard
- Android: phone login, analytics tracker, crash handler, network diagnostics
- Android: splash screen, encrypted token storage, app update enhancements
- Docs: multi-tenant RBAC guide with 8 sections and role-permission matrix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-04 01:00:22 +08:00

109 lines
3.7 KiB
Python

"""
知识仪表盘 API — 瓶颈分析、知识条目查询、趋势统计
"""
from __future__ import annotations
import logging
from datetime import datetime, timedelta
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy import func
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.api.auth import get_current_user
from app.api.deps import get_current_workspace_id
from app.models.user import User
from app.models.knowledge_entry import KnowledgeEntry
from app.services.bottleneck_detector import bottleneck_detector
from app.services.optimization_engine import optimization_engine
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1/knowledge-dashboard", tags=["knowledge-dashboard"])
@router.get("/bottlenecks")
def get_bottlenecks(
hours: int = Query(168, ge=1, le=2160, description="分析时长(小时)"),
current_user: User = Depends(get_current_user),
):
"""瓶颈分析 — 检测工作流性能瓶颈并生成优化建议。"""
analysis = bottleneck_detector.run_full_analysis(hours=hours)
optimizations = optimization_engine.generate_optimizations(analysis.get("bottlenecks", []))
# Build recommendations dict keyed by node_type for frontend lookup
recommendations_map = {}
for opt in optimizations:
recommendations_map[opt["node_type"]] = {
"node_type": opt["node_type"],
"severity": opt["severity"],
"current_state": opt.get("current_metrics", {}),
"changes": opt.get("changes", []),
}
return {
**analysis,
"recommendations": list(optimizations),
"optimizations": recommendations_map,
}
@router.get("/entries")
def get_knowledge_entries(
days: int = Query(7, ge=1, le=365, description="统计天数"),
limit: int = Query(50, ge=1, le=200, description="返回条数"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
workspace_id: str = Depends(get_current_workspace_id),
):
"""获取当前工作区知识条目列表,按创建时间倒序。"""
since = datetime.now() - timedelta(days=days)
q = db.query(KnowledgeEntry).filter(
KnowledgeEntry.created_at >= since,
KnowledgeEntry.is_active == True,
)
if workspace_id:
q = q.filter(KnowledgeEntry.workspace_id == workspace_id)
entries = q.order_by(KnowledgeEntry.created_at.desc()).limit(limit).all()
return [e.to_dict() for e in entries]
@router.get("/trend")
def get_knowledge_trend(
days: int = Query(7, ge=1, le=365, description="统计天数"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
workspace_id: str = Depends(get_current_workspace_id),
):
"""知识条目增长趋势(当前工作区)— 按天统计新增数量。"""
since = datetime.now() - timedelta(days=days)
q = db.query(
func.date(KnowledgeEntry.created_at).label("date"),
func.count(KnowledgeEntry.id).label("count"),
).filter(
KnowledgeEntry.created_at >= since,
KnowledgeEntry.is_active == True,
)
if workspace_id:
q = q.filter(KnowledgeEntry.workspace_id == workspace_id)
rows = q.group_by(func.date(KnowledgeEntry.created_at)).order_by(
func.date(KnowledgeEntry.created_at).asc()
).all()
# Fill in missing dates with 0 count
trend = []
current_date = since.date()
end_date = datetime.now().date()
date_counts = {row.date: row.count for row in rows}
while current_date <= end_date:
trend.append({
"date": current_date.isoformat(),
"count": date_counts.get(current_date, 0),
})
current_date += timedelta(days=1)
return trend