mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
OpenCode Go 上游(https://opencode.ai/zen/go/v1)官方提供用量端点 GET /zen/go/v1/usage(Bearer 复用消费 key),返回 rolling(5h)/weekly/monthly 三窗口的 percent 与 resetsAt。本提交为 openai apikey 账号(base_url 为 opencode.ai/zen/go/v1)接入该用量窗口: - 后端:OpenCodeGoUsageService——手动刷新(每账号 10s 节流)、 周期自动刷新(全局开关 + 账号开关双开启,interval 5-1440 分钟, leader lock + 每轮 20 账号 + 并发 4)、失败指数退避(上限 24h)、 Retry-After 尊重;401→unauthorized、403→failed;snapshot 持久化于 accounts.extra(单账号写入,无分组 CAS、无会话加密) - admin API:settings GET/PUT、/:id/opencode-go-usage GET、 auto-refresh PUT、refresh POST(与 Ollama Cloud usage 同构) - 前端:账号列表行内三窗口进度条(复用 UsageProgressBar)、 编辑弹窗内配置面板(详情+手动刷新+auto-refresh)、全局设置卡片、 en/zh i18n 验证: - 上游端点已用生产真实 Go key 实测 200,响应 {"usage":{"rolling":{"status":"ok","percent":6,...},...}}(2026-08-13) - go build ./... OK;service/handler/repo/dto/cmd 全量回归 OK - 新增单测 20 例(解析 401/403/畸形 JSON、退避、节流等)全绿 - frontend vitest 新增 7 例 + Ollama 回归 4 例全绿;vue-tsc 零错误
122 lines
2.9 KiB
Go
122 lines
2.9 KiB
Go
package admin
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type openCodeGoUsageAutoRefreshRequest struct {
|
|
Enabled *bool `json:"enabled" binding:"required"`
|
|
}
|
|
|
|
func (h *AccountHandler) GetOpenCodeGoUsageSettings(c *gin.Context) {
|
|
if h.opencodeGoUsage == nil {
|
|
response.ErrorFrom(c, service.ErrOpenCodeGoUsageUnavailable)
|
|
return
|
|
}
|
|
settings, err := h.opencodeGoUsage.GetSettings(c.Request.Context())
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
response.Success(c, settings)
|
|
}
|
|
|
|
func (h *AccountHandler) UpdateOpenCodeGoUsageSettings(c *gin.Context) {
|
|
if h.opencodeGoUsage == nil {
|
|
response.ErrorFrom(c, service.ErrOpenCodeGoUsageUnavailable)
|
|
return
|
|
}
|
|
var req service.OpenCodeGoUsageSettings
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
if err := h.opencodeGoUsage.UpdateSettings(c.Request.Context(), &req); err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
settings, err := h.opencodeGoUsage.GetSettings(c.Request.Context())
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
response.Success(c, settings)
|
|
}
|
|
|
|
func (h *AccountHandler) GetOpenCodeGoUsage(c *gin.Context) {
|
|
if !h.requireOpenCodeGoUsage(c) {
|
|
return
|
|
}
|
|
accountID, ok := openCodeGoUsageAccountID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
state, err := h.opencodeGoUsage.GetState(c.Request.Context(), accountID)
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
response.Success(c, state)
|
|
}
|
|
|
|
func (h *AccountHandler) SetOpenCodeGoUsageAutoRefresh(c *gin.Context) {
|
|
if !h.requireOpenCodeGoUsage(c) {
|
|
return
|
|
}
|
|
accountID, ok := openCodeGoUsageAccountID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req openCodeGoUsageAutoRefreshRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "Invalid request: "+err.Error())
|
|
return
|
|
}
|
|
state, err := h.opencodeGoUsage.SetAutoRefresh(c.Request.Context(), accountID, *req.Enabled)
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
response.Success(c, state)
|
|
}
|
|
|
|
func (h *AccountHandler) RefreshOpenCodeGoUsage(c *gin.Context) {
|
|
if !h.requireOpenCodeGoUsage(c) {
|
|
return
|
|
}
|
|
accountID, ok := openCodeGoUsageAccountID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
state, err := h.opencodeGoUsage.Refresh(c.Request.Context(), accountID)
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
response.Success(c, state)
|
|
}
|
|
|
|
func (h *AccountHandler) requireOpenCodeGoUsage(c *gin.Context) bool {
|
|
if h != nil && h.opencodeGoUsage != nil {
|
|
return true
|
|
}
|
|
response.ErrorFrom(c, service.ErrOpenCodeGoUsageUnavailable)
|
|
return false
|
|
}
|
|
|
|
func openCodeGoUsageAccountID(c *gin.Context) (int64, bool) {
|
|
if c == nil {
|
|
return 0, false
|
|
}
|
|
accountID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil || accountID <= 0 {
|
|
response.BadRequest(c, "Invalid account ID")
|
|
return 0, false
|
|
}
|
|
return accountID, true
|
|
}
|