Skip to content

fix(subscription): preserve wallet overflow snapshots#5649

Open
mxyhi wants to merge 1 commit into
QuantumNous:mainfrom
mxyhi:pr/subscription-wallet-overflow
Open

fix(subscription): preserve wallet overflow snapshots#5649
mxyhi wants to merge 1 commit into
QuantumNous:mainfrom
mxyhi:pr/subscription-wallet-overflow

Conversation

@mxyhi

@mxyhi mxyhi commented Jun 21, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 本描述已人工整理后提交。
  • 本次代码由 Codex 辅助生成,并已人工复核和本地验证。

📝 变更描述 / Description

本 PR 修复订阅用量更新时误覆盖 user_subscriptions 快照字段的问题。

allow_wallet_overflow 是从订阅计划复制到用户订阅上的快照字段。当前订阅重置额度、预扣额度、退款回滚额度时使用了 Save,会把整个 UserSubscription 结构写回数据库。对于历史数据中 allow_wallet_overflow 为 SQL NULL 的记录,Go 会按 bool 零值读成 false,后续只想更新用量字段的操作就可能把该快照列误写成显式 false

修复方式:把相关路径从整行 Save 改为只更新自身负责的列:

  • amount_used
  • last_reset_time
  • next_reset_time
  • updated_at

这样订阅重置、预扣、退款回滚不会再改动 allow_wallet_overflow 等订阅快照字段。

同时新增回归测试:构造 allow_wallet_overflow = NULL 的用户订阅,依次执行 reset、pre-consume、refund delta,确认该列仍保持 NULL,不会被用量更新改写。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

暂无关联 Issue。

提交前已搜索现有 Issues 和 PRs,关键词包括:

  • allow_wallet_overflow
  • wallet overflow
  • no matched subscription for group

未发现重复提交。已有相关 PR #5616 只处理重复迁移列的问题,不覆盖本次整行 Save 覆盖快照字段的问题。

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

本地验证已通过:

go test ./model ./service
# ok github.com/QuantumNous/new-api/model
# ok github.com/QuantumNous/new-api/service

go test ./...
# all packages passed

git diff --check upstream/main..HEAD
# no output

同类修复已先在 fork 部署验证:

  • 运行镜像:ghcr.io/mxyhi/new-api:main-e26d7dca5f57
  • 公网响应头:x-new-api-version: main-e26d7dca5f57
  • 修复后 active 订阅中 allow_wallet_overflow=false 的异常行数:0
  • 部署后日志未再出现新的 no matched subscription for group: cc-maxpanicfatal

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Three subscription persistence functions (maybeResetUserSubscriptionWithPlanTx, PreConsumeUserSubscription, PostConsumeUserSubscriptionDelta) are changed from full-struct tx.Save calls to targeted GORM Updates calls that write only the specific columns being modified. A new test file verifies that allow_wallet_overflow remains NULL across those transitions.

Changes

Subscription Targeted Persistence and Snapshot Test

Layer / File(s) Summary
Targeted column updates in subscription persistence functions
model/subscription.go
Adjusts the AllowWalletOverflow field comment to note it must survive usage/reset writes. Replaces tx.Save calls in maybeResetUserSubscriptionWithPlanTx, PreConsumeUserSubscription, and PostConsumeUserSubscriptionDelta with tx.Model(&UserSubscription{}).Where("id = ?", ...).Updates(map[string]interface{}{...}) calls that write only last_reset_time/next_reset_time or amount_used and updated_at.
In-memory SQLite test verifying allow_wallet_overflow stays NULL
model/subscription_test.go
Adds setupSubscriptionTestDB (swaps global DB/flags to a temporary SQLite instance and returns a cleanup closure), TestSubscriptionUsageUpdatesDoNotOverwriteWalletOverflowSnapshot (migrates schema, creates a plan and subscription, forces allow_wallet_overflow to NULL, runs reset/pre-consume/post-consume transitions, and asserts AmountUsed advances through 0→10→5 while allow_wallet_overflow stays NULL), and requireWalletOverflowSnapshotNull (queries the column directly and asserts sql.NullBool.Valid is false).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • QuantumNous/new-api#5616: Modifies AllowWalletOverflow GORM tag (default:true) in model/subscription.go to prevent repeated schema migrations — directly touches the same field whose snapshot behavior is being protected in this PR.

Poem

🐇 Hippity-hop, I write just the right bits,
No more full saves that bulldoze my commits!
amount_used goes up, reset times align,
allow_wallet_overflow stays perfectly fine —
NULL where I left it, my snapshot secure,
Targeted columns, precise and pure! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(subscription): preserve wallet overflow snapshots' directly and clearly summarizes the main change: fixing a bug where wallet overflow snapshots are unintentionally overwritten during subscription operations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
model/subscription_test.go (1)

84-91: 💤 Low value

FOR UPDATE is a no-op on SQLite—consider a comment or conditional.

SQLite serializes writes at the database level and ignores row-level locking hints. The gorm:query_option "FOR UPDATE" clause here mimics production flow (which targets MySQL/PostgreSQL), but new contributors may be confused if they expect actual row locking in this test.

This isn't a bug—the test still validates the column update behavior—but a brief comment documenting the SQLite limitation would improve clarity.

🤖 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 `@model/subscription_test.go` around lines 84 - 91, The `FOR UPDATE` clause in
the query at the transaction setup is not enforced by SQLite since it serializes
writes at the database level and ignores row-level locking hints. Add a comment
above the `tx.Set("gorm:query_option", "FOR UPDATE")` line explaining that while
this `FOR UPDATE` clause is a no-op on SQLite, it is included to mimic the
production behavior that would be used with MySQL or PostgreSQL, ensuring the
test validates the same code path as production.
🤖 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.

Nitpick comments:
In `@model/subscription_test.go`:
- Around line 84-91: The `FOR UPDATE` clause in the query at the transaction
setup is not enforced by SQLite since it serializes writes at the database level
and ignores row-level locking hints. Add a comment above the
`tx.Set("gorm:query_option", "FOR UPDATE")` line explaining that while this `FOR
UPDATE` clause is a no-op on SQLite, it is included to mimic the production
behavior that would be used with MySQL or PostgreSQL, ensuring the test
validates the same code path as production.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3a574b08-67a4-410c-9920-13d68ab0bb7f

📥 Commits

Reviewing files that changed from the base of the PR and between 0b7ae4e and 8102e75.

📒 Files selected for processing (2)
  • model/subscription.go
  • model/subscription_test.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant