feat(gateway): 补齐桌面端高级媒体直连接口
ci / verify (pull_request) Successful in 15m34s
ci / verify (pull_request) Successful in 15m34s
新增图片矢量化、视频超分、每日用量、计价与任务隔离能力,并通过环境变量解析平台凭据。 已通过 Go 全量门禁、迁移检查、镜像构建以及 Vectorizer 五格式和 Topaz 3 秒视频真实 DEV 验收。
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
||||
)
|
||||
|
||||
type dailyTokenUsageSummary struct {
|
||||
CumulativeTokens int64 `json:"cumulativeTokens"`
|
||||
PeakDailyTokens int64 `json:"peakDailyTokens"`
|
||||
CurrentStreakDays int `json:"currentStreakDays"`
|
||||
LongestStreakDays int `json:"longestStreakDays"`
|
||||
}
|
||||
|
||||
// dailyTokenUsage godoc
|
||||
// @Summary 查询每日 Token 与资源点用量
|
||||
// @Description 按 IANA 时区返回连续自然日用量;API Key 仅统计当前 Key,JWT 统计当前用户。
|
||||
// @Tags workspace
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param from query string true "开始日期 YYYY-MM-DD"
|
||||
// @Param to query string true "结束日期 YYYY-MM-DD"
|
||||
// @Param timezone query string false "IANA 时区" default(UTC)
|
||||
// @Success 200 {object} DailyTokenUsageResponse
|
||||
// @Failure 400 {object} ErrorEnvelope
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/workspace/token-usage/daily [get]
|
||||
func (s *Server) dailyTokenUsage(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := auth.UserFromContext(r.Context())
|
||||
if !ok {
|
||||
writeError(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
timezone := strings.TrimSpace(r.URL.Query().Get("timezone"))
|
||||
if timezone == "" {
|
||||
timezone = "UTC"
|
||||
}
|
||||
location, err := time.LoadLocation(timezone)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid IANA timezone")
|
||||
return
|
||||
}
|
||||
from, err := time.ParseInLocation("2006-01-02", strings.TrimSpace(r.URL.Query().Get("from")), location)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid from date")
|
||||
return
|
||||
}
|
||||
to, err := time.ParseInLocation("2006-01-02", strings.TrimSpace(r.URL.Query().Get("to")), location)
|
||||
if err != nil || to.Before(from) || to.Sub(from) > 399*24*time.Hour {
|
||||
writeError(w, http.StatusBadRequest, "invalid to date or date range exceeds 400 days")
|
||||
return
|
||||
}
|
||||
stored, err := s.store.ListDailyTokenUsage(r.Context(), user, from, to, timezone)
|
||||
if err != nil {
|
||||
s.logger.Error("list daily token usage failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "list daily token usage failed")
|
||||
return
|
||||
}
|
||||
byDate := make(map[string]store.DailyTokenUsage, len(stored))
|
||||
for _, item := range stored {
|
||||
byDate[item.Date] = item
|
||||
}
|
||||
items, summary := fillDailyTokenUsageDays(byDate, from, to)
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"items": items, "tokenDays": items, "summary": summary,
|
||||
"range": map[string]string{"from": from.Format("2006-01-02"), "to": to.Format("2006-01-02"), "timezone": timezone},
|
||||
})
|
||||
}
|
||||
|
||||
func fillDailyTokenUsageDays(byDate map[string]store.DailyTokenUsage, from, to time.Time) ([]store.DailyTokenUsage, dailyTokenUsageSummary) {
|
||||
items := make([]store.DailyTokenUsage, 0, 32)
|
||||
summary := dailyTokenUsageSummary{}
|
||||
currentRun := 0
|
||||
for day := from; !day.After(to); day = day.AddDate(0, 0, 1) {
|
||||
key := day.Format("2006-01-02")
|
||||
item, found := byDate[key]
|
||||
if !found {
|
||||
item.Date = key
|
||||
}
|
||||
items = append(items, item)
|
||||
summary.CumulativeTokens += item.TotalTokens
|
||||
if item.TotalTokens > summary.PeakDailyTokens {
|
||||
summary.PeakDailyTokens = item.TotalTokens
|
||||
}
|
||||
if item.TaskCount > 0 {
|
||||
currentRun++
|
||||
if currentRun > summary.LongestStreakDays {
|
||||
summary.LongestStreakDays = currentRun
|
||||
}
|
||||
} else {
|
||||
currentRun = 0
|
||||
}
|
||||
}
|
||||
summary.CurrentStreakDays = currentRun
|
||||
return items, summary
|
||||
}
|
||||
Reference in New Issue
Block a user