feat: 普通用户用量统计 + 管理后台用量明细
后端 - /api/usage/stats:当前用户按日聚合统计(请求数/输入/输出/缓存 tokens/费用) - /api/usage/logs:当前用户用量明细分页 - /api/admin/usage/logs:全量明细(分页 + 协议/状态/模型/用户筛选 + 用户名关联) - /api/admin/usage/summary:全量汇总(按用户分组) - dao:UsageFilter 支持筛选分页;DailyUsageDAO.ListAll - 路由加固:新增 middleware.AdminOnly,既有 /api/admin/* 全部迁移到 带管理员角色校验的分组(此前登录即可访问,属安全隐患) 前端 - 普通用户「用量统计」页:统计卡片 + 纯 CSS 每日请求量条形图 + 明细表格分页 - 管理后台「用量明细」页:汇总卡片 + 协议/状态/模型/用户筛选 + 明细表格 + 原始请求/响应查看弹窗(记录开关开启时) - stores/usage.ts 与类型定义;控制台/管理菜单挂载
This commit is contained in:
@@ -55,6 +55,50 @@ func (d *UsageDAO) CountByUserID(ctx context.Context, userID uint64) (int64, err
|
||||
return count, err
|
||||
}
|
||||
|
||||
// UsageFilter 用量明细筛选条件(管理后台)。
|
||||
type UsageFilter struct {
|
||||
UserID *uint64 // 指定用户(nil=全部)
|
||||
Protocol string // 协议 chat/messages/responses(空=全部)
|
||||
Status string // success/error/canceled(空=全部)
|
||||
ModelName string // 模型名模糊(空=全部)
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
// ListAll 管理后台全量用量明细(分页 + 筛选),并带用户名。
|
||||
func (d *UsageDAO) ListAll(ctx context.Context, f UsageFilter) ([]store.UsageLog, error) {
|
||||
q := d.db.WithContext(ctx).Model(&store.UsageLog{})
|
||||
q = applyUsageFilter(q, f)
|
||||
var logs []store.UsageLog
|
||||
err := q.Order("created_at DESC").Limit(f.Limit).Offset(f.Offset).Find(&logs).Error
|
||||
return logs, err
|
||||
}
|
||||
|
||||
// CountAll 统计符合筛选条件的明细总数。
|
||||
func (d *UsageDAO) CountAll(ctx context.Context, f UsageFilter) (int64, error) {
|
||||
q := d.db.WithContext(ctx).Model(&store.UsageLog{})
|
||||
q = applyUsageFilter(q, f)
|
||||
var count int64
|
||||
err := q.Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
func applyUsageFilter(q *gorm.DB, f UsageFilter) *gorm.DB {
|
||||
if f.UserID != nil {
|
||||
q = q.Where("user_id = ?", *f.UserID)
|
||||
}
|
||||
if f.Protocol != "" {
|
||||
q = q.Where("protocol = ?", f.Protocol)
|
||||
}
|
||||
if f.Status != "" {
|
||||
q = q.Where("status = ?", f.Status)
|
||||
}
|
||||
if f.ModelName != "" {
|
||||
q = q.Where("model_name LIKE ?", "%"+f.ModelName+"%")
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// UsageDaily DAO
|
||||
func (d *DailyUsageDAO) Create(ctx context.Context, log *store.UsageDaily) error {
|
||||
return d.db.WithContext(ctx).Create(log).Error
|
||||
@@ -106,3 +150,20 @@ func (d *DailyUsageDAO) ListByDateRange(ctx context.Context, userID uint64, star
|
||||
Find(&logs).Error
|
||||
return logs, err
|
||||
}
|
||||
|
||||
// ListAll 管理后台:全部用户的日聚合(可选按用户/日期范围筛选),按日期倒序。
|
||||
func (d *DailyUsageDAO) ListAll(ctx context.Context, userID *uint64, start, end string) ([]store.UsageDaily, error) {
|
||||
q := d.db.WithContext(ctx).Model(&store.UsageDaily{})
|
||||
if userID != nil {
|
||||
q = q.Where("user_id = ?", *userID)
|
||||
}
|
||||
if start != "" {
|
||||
q = q.Where("date >= ?", start)
|
||||
}
|
||||
if end != "" {
|
||||
q = q.Where("date <= ?", end)
|
||||
}
|
||||
var logs []store.UsageDaily
|
||||
err := q.Order("date DESC, user_id ASC").Find(&logs).Error
|
||||
return logs, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user