fix(opencode): lite 列表 DTO 补 opencode_go_usage,修复列表页显示余额

This commit is contained in:
alfadb
2026-09-23 15:26:44 +08:00
parent a2e1618700
commit d0731be2f0
3 changed files with 60 additions and 2 deletions
@@ -0,0 +1,57 @@
package dto
import (
"encoding/json"
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/stretchr/testify/require"
)
// 账号列表页固定传 lite=1,lite 投影(AccountListItemFromAccount)必须与完整
// Account DTO 一样携带 opencode_go_usage,否则前端用量单元格会退回「余额 --」。
func TestAccountListItemFromAccount_CarriesOpenCodeGoUsage(t *testing.T) {
now := time.Now().UTC()
// 生产账号 164 的形态:deepseek 平台 + OpenCode Go 官方基址 + apikey
eligible := &service.Account{
ID: 164, Name: "DeepSeek OpenCode", Platform: service.PlatformDeepseek, Type: service.AccountTypeAPIKey,
Credentials: map[string]any{"base_url": "https://opencode.ai/zen/go/v1", "api_key": "test-key"},
Extra: map[string]any{
service.OpenCodeGoUsageAutoRefreshExtraKey: true,
service.OpenCodeGoUsageSnapshotExtraKey: &service.OpenCodeGoUsageSnapshot{
Status: service.OpenCodeGoUsageStatusOK,
Data: &service.OpenCodeGoUsageData{Rolling: service.OpenCodeGoUsageWindow{Status: "ok", Percent: 6}},
LastAttemptAt: now, NextRefreshAt: now.Add(time.Hour),
},
},
Status: service.StatusActive,
}
item := AccountListItemFromAccount(AccountFromServiceShallow(eligible))
require.NotNil(t, item.OpenCodeGoUsage)
require.True(t, item.OpenCodeGoUsage.Eligible)
require.True(t, item.OpenCodeGoUsage.AutoRefreshEnabled)
require.NotNil(t, item.OpenCodeGoUsage.Snapshot)
require.NotNil(t, item.OpenCodeGoUsage.Snapshot.Data)
require.Equal(t, 6.0, item.OpenCodeGoUsage.Snapshot.Data.Rolling.Percent)
raw, err := json.Marshal(item)
require.NoError(t, err)
require.Contains(t, string(raw), `"opencode_go_usage"`)
require.Contains(t, string(raw), `"eligible":true`)
// 对照:base_url 指向 api.deepseek.com 的普通 deepseek 账号不参与用量窗口
ineligible := &service.Account{
ID: 165, Name: "DeepSeek Direct", Platform: service.PlatformDeepseek, Type: service.AccountTypeAPIKey,
Credentials: map[string]any{"base_url": "https://api.deepseek.com", "api_key": "test-key-2"},
Status: service.StatusActive,
}
plainItem := AccountListItemFromAccount(AccountFromServiceShallow(ineligible))
require.Nil(t, plainItem.OpenCodeGoUsage)
plainRaw, err := json.Marshal(plainItem)
require.NoError(t, err)
require.NotContains(t, string(plainRaw), `"opencode_go_usage"`)
}
+2 -2
View File
@@ -465,8 +465,8 @@ func AccountListItemFromAccount(a *Account) *AccountListItem {
return &AccountListItem{
ID: a.ID, Name: a.Name, Notes: a.Notes, Platform: a.Platform, Type: a.Type,
Credentials: a.Credentials, CredentialsStatus: a.CredentialsStatus, Extra: a.Extra,
OllamaCloudUsage: a.OllamaCloudUsage,
ProxyID: a.ProxyID, ProxyFallbackOriginID: a.ProxyFallbackOriginID, ProxyFallbackOriginName: a.ProxyFallbackOriginName,
OllamaCloudUsage: a.OllamaCloudUsage, OpenCodeGoUsage: a.OpenCodeGoUsage,
ProxyID: a.ProxyID, ProxyFallbackOriginID: a.ProxyFallbackOriginID, ProxyFallbackOriginName: a.ProxyFallbackOriginName,
Concurrency: a.Concurrency, LoadFactor: a.LoadFactor, Priority: a.Priority, RateMultiplier: a.RateMultiplier,
Status: a.Status, ErrorMessage: a.ErrorMessage, LastUsedAt: a.LastUsedAt, ExpiresAt: a.ExpiresAt,
AutoPauseOnExpired: a.AutoPauseOnExpired, CreatedAt: a.CreatedAt, UpdatedAt: a.UpdatedAt,
+1
View File
@@ -341,6 +341,7 @@ type AccountListItem struct {
CredentialsStatus map[string]bool `json:"credentials_status,omitempty"`
Extra map[string]any `json:"extra,omitempty"`
OllamaCloudUsage *service.OllamaCloudUsageState `json:"ollama_cloud_usage,omitempty"`
OpenCodeGoUsage *service.OpenCodeGoUsageState `json:"opencode_go_usage,omitempty"`
ProxyID *int64 `json:"proxy_id"`
ProxyFallbackOriginID *int64 `json:"proxy_fallback_origin_id"`