feat(channel): add balance query for New API channels - #6737
feat(channel): add balance query for New API channels#6737lelinhomafx4021-creator wants to merge 3 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughThis PR adds New API channel balance retrieval, validation, normalization, persistence, and display. It supports native units, unlimited quotas, currencies, balance refreshes, exhausted-channel handling, and structured frontend responses. ChangesNew API balance support
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Admin
participant ChannelBilling
participant NewAPI
participant ChannelDatabase
participant ChannelUI
Admin->>ChannelBilling: request New API balance refresh
ChannelBilling->>NewAPI: fetch usage and status
NewAPI-->>ChannelBilling: return quota and status data
ChannelBilling->>ChannelDatabase: persist balance_info
ChannelDatabase-->>ChannelBilling: return updated balance data
ChannelBilling-->>ChannelUI: return structured balance response
ChannelUI->>Admin: render native balance and timestamp
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@controller/channel-billing-newapi.go`:
- Around line 80-106: Update normalizeNewAPIBalance to clamp negative remaining
quotas to zero before populating info.Remaining or calculating converted USD
values, while preserving unlimited handling and allowing newAPIBalanceExhausted
to disable depleted channels during bulk refresh.
- Around line 70-77: Update GetResponseBody to close res.Body on every execution
path, including non-200 upstream responses returned as errors. Ensure the body
is closed immediately after a successful HTTP response is obtained while
preserving the existing response parsing and error behavior used by
getNewAPIChannelResponse.
In `@controller/channel.go`:
- Line 630: Update AddChannel before BatchInsertChannels to reset all
server-managed balance fields on addChannelRequest.Channel: Balance,
BalanceUpdatedTime, UsedQuota, and the existing BalanceInfo. Ensure
client-provided values for these fields cannot be persisted during channel
creation.
- Around line 1436-1440: Update the clone balance handling around BalanceInfo so
structured and legacy balance state follow the same reset policy: when
resetBalance is false, preserve the source BalanceInfo, Balance,
BalanceUpdatedTime, and UsedQuota; when true, clear all corresponding balance
fields. Add tests covering both reset modes and their resulting balance state.
In `@web/src/features/channels/lib/channel-actions.ts`:
- Around line 376-403: Gate all balance query state updates behind
response.success && hasPayload: in balance-query-dialog.tsx, update currentRow
and balanceUpdatedAt only for successful payloads, and in channel-actions.ts,
invalidate channel-list queries only under the same condition. Preserve the
existing payload formatting and success callback behavior, and do not process
response.data or response.balance from failed responses.
In `@web/src/features/channels/lib/new-api-balance.ts`:
- Around line 30-43: Remove the default value from the unlimitedLabel parameter
in formatNewAPIBalance so callers must explicitly provide a localized label.
Preserve the existing unlimited formatting behavior and require all call sites
to pass their localized label.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ea815216-ac8b-4462-8786-0404aa7684a4
📒 Files selected for processing (18)
controller/channel-billing-newapi.gocontroller/channel-billing.gocontroller/channel.gocontroller/channel_authz.gocontroller/channel_authz_test.gocontroller/channel_billing_balance_test.godocs/channel/new-api-balance.mdmodel/channel.gomodel/channel_balance.gomodel/channel_balance_test.goweb/src/features/channels/components/channels-columns.tsxweb/src/features/channels/components/dialogs/balance-query-dialog.tsxweb/src/features/channels/lib/__tests__/new-api-balance.test.tsweb/src/features/channels/lib/channel-actions.tsweb/src/features/channels/lib/channel-utils.tsweb/src/features/channels/lib/index.tsweb/src/features/channels/lib/new-api-balance.tsweb/src/features/channels/types.ts
| func getNewAPIChannelResponse(channel *model.Channel, path string) ([]byte, error) { | ||
| baseURL, err := url.Parse(strings.TrimSpace(channel.GetBaseURL())) | ||
| if err != nil || (baseURL.Scheme != "http" && baseURL.Scheme != "https") || baseURL.Host == "" || baseURL.User != nil || baseURL.RawQuery != "" || baseURL.Fragment != "" { | ||
| return nil, errors.New("invalid New API channel base URL") | ||
| } | ||
| baseURL.Path = strings.TrimRight(baseURL.Path, "/") + path | ||
| baseURL.RawPath = "" | ||
| return GetResponseBody(http.MethodGet, baseURL.String(), channel, GetAuthHeader(channel.Key)) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Close failed upstream response bodies.
GetResponseBody returns before it closes res.Body when the upstream returns a non-200 status. This new flow calls it for /api/status, then intentionally ignores that error. Repeated status failures can retain connections and file descriptors.
Close the body on every path in GetResponseBody.
Proposed fix
res, err := client.Do(req)
if err != nil {
return nil, err
}
+defer func() {
+ _ = res.Body.Close()
+}()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("status code: %d", res.StatusCode)
}
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
-err = res.Body.Close()
-if err != nil {
- return nil, err
-}
return body, nil🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@controller/channel-billing-newapi.go` around lines 70 - 77, Update
GetResponseBody to close res.Body on every execution path, including non-200
upstream responses returned as errors. Ensure the body is closed immediately
after a successful HTTP response is obtained while preserving the existing
response parsing and error behavior used by getNewAPIChannelResponse.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
controller/channel_billing_balance_test.go (1)
37-37: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winMove fatal handling out of the HTTP handler goroutine.
httptest.NewServerruns the handler in a server goroutine, andt.FatalfcallsFailNowwithruntime.Goexit, which can exit only that goroutine. An unexpected upstream path may then leave the client without a response.Record the failure with
t.Errorf, write an HTTP error, and let the test goroutine assert the client error.Proposed fix
- t.Fatalf("unexpected path %s", r.URL.Path) + t.Errorf("unexpected path %s", r.URL.Path) + http.Error(w, "unexpected path", http.StatusInternalServerError)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@controller/channel_billing_balance_test.go` at line 37, In the httptest handler, replace t.Fatalf in the unexpected-path branch with t.Errorf, write an HTTP error response, and return from the handler. In the test goroutine, assert that the client request reports the resulting error instead of relying on handler-side fatal termination.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@controller/channel_authz_test.go`:
- Around line 145-149: Deep-copy or snapshot the fields of original.BalanceInfo
before invoking applyChannelBalanceReset in the no-reset test, rather than
relying on the shallow preserved := original copy. Update the assertions to
compare against that independent snapshot while retaining the existing Balance
and BalanceUpdatedTime checks.
---
Outside diff comments:
In `@controller/channel_billing_balance_test.go`:
- Line 37: In the httptest handler, replace t.Fatalf in the unexpected-path
branch with t.Errorf, write an HTTP error response, and return from the handler.
In the test goroutine, assert that the client request reports the resulting
error instead of relying on handler-side fatal termination.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 403afb5c-2407-4fe2-9213-feaa9780d346
📒 Files selected for processing (9)
controller/channel-billing-newapi.gocontroller/channel-billing.gocontroller/channel.gocontroller/channel_authz_test.gocontroller/channel_billing_balance_test.goweb/src/features/channels/components/dialogs/balance-query-dialog.tsxweb/src/features/channels/lib/__tests__/new-api-balance.test.tsweb/src/features/channels/lib/channel-actions.tsweb/src/features/channels/lib/new-api-balance.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- web/src/features/channels/lib/tests/new-api-balance.test.ts
- web/src/features/channels/lib/channel-actions.ts
- controller/channel-billing-newapi.go
- web/src/features/channels/lib/new-api-balance.ts
- web/src/features/channels/components/dialogs/balance-query-dialog.tsx
|
@seefs001 Could you please review this PR? Thank you! |
Important
📝 变更描述 / Description
为
New API类型渠道增加上游余额查询。管理员刷新余额时,系统使用渠道中配置的 Bearer Token 请求上游/api/usage/token/,并结合/api/status识别余额单位及无限额度状态。查询结果保存到
balance_info,前端按上游原始单位展示 USD、CNY、credits、tokens 或自定义单位,避免把人民币或积分误显示为美元。本次变更仅处理New API渠道;多密钥渠道仍不支持余额汇总,也不增加自动切换逻辑。🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
/api/status返回 HTTP 200。Summary by CodeRabbit
New Features
Bug Fixes
Documentation