feat: 看板优化——中文标题+Token消耗+时间段筛选+品牌名统一
- 学员端:LoginPanel/App.vue 描述文案优化,去掉生硬表述 - 后台品牌统一:index.html/App.vue 标题改为'大本营千问千答' - 看板tab:Dashboard -> 数据看板 - 看板新增:输入Token、输出Token、总Token 统计 - 看板支持按时间段筛选:后端API增加start/end参数,前端加日期选择器 - stats-grid布局从5列调整为4列,响应式改为2列
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
@@ -15,8 +17,16 @@ router = APIRouter()
|
||||
|
||||
@router.get("/dashboard")
|
||||
def dashboard(
|
||||
start: str = Query(default="", description="开始日期 YYYY-MM-DD"),
|
||||
end: str = Query(default="", description="结束日期 YYYY-MM-DD"),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: Admin = Depends(get_current_admin),
|
||||
) -> dict:
|
||||
stats = AdminDashboardService.stats(db)
|
||||
start_dt = datetime.strptime(start, "%Y-%m-%d") if start else None
|
||||
if end:
|
||||
end_dt = datetime.strptime(end, "%Y-%m-%d")
|
||||
end_dt = end_dt.replace(hour=23, minute=59, second=59)
|
||||
else:
|
||||
end_dt = None
|
||||
stats = AdminDashboardService.stats(db, start_dt, end_dt)
|
||||
return api_success(DashboardStats.model_validate(stats).model_dump())
|
||||
|
||||
@@ -31,6 +31,9 @@ class DashboardStats(BaseModel):
|
||||
messageCount: int
|
||||
aiRequestCount: int
|
||||
knowledgeCount: int
|
||||
inputToken: int
|
||||
outputToken: int
|
||||
totalToken: int
|
||||
|
||||
|
||||
class AdminUserUpdateRequest(BaseModel):
|
||||
|
||||
@@ -60,13 +60,35 @@ class AdminAuthService:
|
||||
|
||||
class AdminDashboardService:
|
||||
@staticmethod
|
||||
def stats(db: Session) -> dict:
|
||||
def stats(db: Session, start: datetime | None = None, end: datetime | None = None) -> dict:
|
||||
from sqlalchemy import and_
|
||||
|
||||
# 基础表查询条件(时间范围)
|
||||
user_filter = [User.is_deleted == 0]
|
||||
session_filter = [ChatSession.is_deleted == 0]
|
||||
msg_filter = []
|
||||
ai_filter = []
|
||||
|
||||
if start:
|
||||
user_filter.append(User.created_at >= start)
|
||||
session_filter.append(ChatSession.created_at >= start)
|
||||
msg_filter.append(ChatMessage.created_at >= start)
|
||||
ai_filter.append(AiRequestLog.created_at >= start)
|
||||
if end:
|
||||
user_filter.append(User.created_at <= end)
|
||||
session_filter.append(ChatSession.created_at <= end)
|
||||
msg_filter.append(ChatMessage.created_at <= end)
|
||||
ai_filter.append(AiRequestLog.created_at <= end)
|
||||
|
||||
return {
|
||||
"userCount": db.scalar(select(func.count(User.id)).where(User.is_deleted == 0)) or 0,
|
||||
"sessionCount": db.scalar(select(func.count(ChatSession.id)).where(ChatSession.is_deleted == 0)) or 0,
|
||||
"messageCount": db.scalar(select(func.count(ChatMessage.id))) or 0,
|
||||
"aiRequestCount": db.scalar(select(func.count(AiRequestLog.id))) or 0,
|
||||
"userCount": db.scalar(select(func.count(User.id)).where(and_(*user_filter))) or 0,
|
||||
"sessionCount": db.scalar(select(func.count(ChatSession.id)).where(and_(*session_filter))) or 0,
|
||||
"messageCount": db.scalar(select(func.count(ChatMessage.id)).where(and_(*msg_filter))) or 0,
|
||||
"aiRequestCount": db.scalar(select(func.count(AiRequestLog.id)).where(and_(*ai_filter))) or 0,
|
||||
"knowledgeCount": db.scalar(select(func.count(Knowledge.id))) or 0,
|
||||
"inputToken": db.scalar(select(func.coalesce(func.sum(AiRequestLog.input_token), 0)).where(and_(*ai_filter))) or 0,
|
||||
"outputToken": db.scalar(select(func.coalesce(func.sum(AiRequestLog.output_token), 0)).where(and_(*ai_filter))) or 0,
|
||||
"totalToken": db.scalar(select(func.coalesce(func.sum(AiRequestLog.total_token), 0)).where(and_(*ai_filter))) or 0,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user