feat(gateway): SaaS P2 计量·增量3 —— GET /admin/usage 用量/积分/成本口径
系统级(store.WithoutTenant,跨租户)用量观测: - 无 tenant:全平台按天 SUM 趋势 + 各租户用量排行(含当前余额)。 - 有 tenant:该租户按天趋势 + 当前积分余额。 - from/to(YYYYMMDD)缺省近 30 天。UsageTrend/UsageByTenant/TenantBalance store 方法。 live 验证:两口径数值自洽(balance=seed−credits、credits=tok×汇率、totals=trend之和)。 至此 P2 计量后端(事实源)齐:明细/账本/余额/rollup/查询。前端页 + P4 硬拦截/充值待需求拉动。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -365,3 +365,39 @@ func mask(s string) string {
|
||||
}
|
||||
return "••••" + s[len(s)-4:]
|
||||
}
|
||||
|
||||
// AdminUsage: GET /api/v1/admin/usage?tenant=&from=&to= —— 系统级用量/积分/成本口径。
|
||||
// 无 tenant → 全平台按天 SUM + 各租户排行;有 tenant → 该租户按天趋势 + 当前余额。
|
||||
// from/to 为 YYYYMMDD,缺省近 30 天。走 WithoutTenant(跨租户,与 admin/overview 同口径)。
|
||||
func (h *Handler) AdminUsage(c *gin.Context) {
|
||||
ctx := store.WithoutTenant(c.Request.Context())
|
||||
now := time.Now()
|
||||
from := c.Query("from")
|
||||
if from == "" {
|
||||
from = now.AddDate(0, 0, -29).Format("20060102")
|
||||
}
|
||||
to := c.Query("to")
|
||||
if to == "" {
|
||||
to = now.Format("20060102")
|
||||
}
|
||||
tenant := c.Query("tenant")
|
||||
|
||||
trend := h.db.UsageTrend(ctx, tenant, from, to)
|
||||
var totTok, totCredits, totCost, totTasks int64
|
||||
for _, d := range trend {
|
||||
totTok += d.TotalTok
|
||||
totCredits += d.CreditsMicro
|
||||
totCost += d.CostMicros
|
||||
totTasks += d.TaskCount
|
||||
}
|
||||
resp := gin.H{
|
||||
"from": from, "to": to, "tenant": tenant, "trend": trend,
|
||||
"totals": gin.H{"total_tok": totTok, "credits_micro": totCredits, "cost_micros": totCost, "task_count": totTasks},
|
||||
}
|
||||
if tenant != "" {
|
||||
resp["balance_micro"] = h.db.TenantBalance(ctx, tenant)
|
||||
} else {
|
||||
resp["tenants"] = h.db.UsageByTenant(ctx, from, to, 20) // 全平台:各租户用量排行(含余额)
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user