package handler import ( "net/http" "strconv" "strings" "time" "cyberstrike-ai/internal/database" "cyberstrike-ai/internal/security" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // GetTokenUsageStats returns model token usage aggregates for dashboard views. func (h *ConversationHandler) GetTokenUsageStats(c *gin.Context) { filter := tokenUsageFilterFromQuery(c) if session, ok := security.CurrentSession(c); ok { filter.Access = database.RBACListAccess{UserID: session.UserID, Scope: session.Scope} } stats, err := h.db.GetModelTokenUsageStats(filter) if err != nil { h.logger.Error("获取Token用量统计失败", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, stats) } // GetConversationTokenUsageStats returns token usage scoped to one conversation. func (h *ConversationHandler) GetConversationTokenUsageStats(c *gin.Context) { filter := tokenUsageFilterFromQuery(c) filter.ConversationID = strings.TrimSpace(c.Param("id")) if session, ok := security.CurrentSession(c); ok { filter.Access = database.RBACListAccess{UserID: session.UserID, Scope: session.Scope} } stats, err := h.db.GetModelTokenUsageStats(filter) if err != nil { h.logger.Error("获取对话Token用量统计失败", zap.Error(err), zap.String("conversationId", filter.ConversationID)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, stats) } func tokenUsageFilterFromQuery(c *gin.Context) database.ModelTokenUsageFilter { days, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("days", "7"))) if days <= 0 { days = 7 } if days > 365 { days = 365 } limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "10"))) if limit <= 0 { limit = 10 } if limit > 500 { limit = 500 } filter := database.ModelTokenUsageFilter{ ConversationID: strings.TrimSpace(c.Query("conversation_id")), ProjectID: strings.TrimSpace(c.Query("project_id")), Days: days, Limit: limit, } if since := parseTokenUsageQueryTime(c.Query("since")); !since.IsZero() { filter.Since = since } else if days > 0 { now := time.Now() start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, -(days - 1)) filter.Since = start } if until := parseTokenUsageQueryTime(c.Query("until")); !until.IsZero() { filter.Until = until } return filter } func parseTokenUsageQueryTime(raw string) time.Time { raw = strings.TrimSpace(raw) if raw == "" { return time.Time{} } for _, layout := range []string{time.RFC3339Nano, time.RFC3339, "2006-01-02"} { if t, err := time.Parse(layout, raw); err == nil { return t } } return time.Time{} }