seedance price#4560
Conversation
WalkthroughThe changes implement token-aware billing logs and establish quota adjustment mechanisms for asynchronous refund flows. They extend Doubao pricing from a single-dimension video-input discount to a two-dimensional model (resolution × video input) with validation. The task billing system is refactored to propagate token counts through quota operations and logging. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@service/task_billing.go`:
- Around line 150-165: taskAdjustQuotaData currently logs adjustments with
common.GetTimestamp(), causing adjustments to land in the current hour instead
of the original task submission hour; change it to derive the bucket timestamp
from the task's original submission time (use task.SubmittedAt or task.CreatedAt
as provided by the Task model), truncate/round that time to the hour used by
LogTaskConsumption, and pass that derived timestamp into
model.LogQuotaDataAdjust (instead of common.GetTimestamp()) so quota_data
adjustments are recorded in the same hourly bucket as the original
LogTaskConsumption.
🪄 Autofix (Beta)
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
Run ID: eeac2584-2df3-4d28-be4f-b3c3e2eb1ccb
📒 Files selected for processing (8)
model/log.gomodel/usedata.gomodel/user.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/doubao/constants.goservice/task_billing.goservice/task_billing_test.goservice/task_polling.go
| // taskAdjustQuotaData 同步调整 quota_data 统计(/api/data/ 数据来源)。 | ||
| // quotaDelta / tokenDelta 均为有符号增量: | ||
| // - 失败退款:quotaDelta=-quota, tokenDelta=0(原始记录 token 就是 0,对称回退即可) | ||
| // - 补扣 / 部分退款 + 真实 token:quotaDelta=±delta, tokenDelta=+totalTokens | ||
| // (token 之所以 *永远是 +*:原始 LogTaskConsumption 写入的 token 都是 0, | ||
| // 现在要补到 totalTokens,所以是单向增量,不随 quota 取号) | ||
| func taskAdjustQuotaData(task *model.Task, quotaDelta, tokenDelta int) { | ||
| if !common.DataExportEnabled { | ||
| return | ||
| } | ||
| if quotaDelta == 0 && tokenDelta == 0 { | ||
| return | ||
| } | ||
| username, _ := model.GetUsernameById(task.UserId, false) | ||
| model.LogQuotaDataAdjust(task.UserId, username, taskModelName(task), quotaDelta, common.GetTimestamp(), tokenDelta) | ||
| } |
There was a problem hiding this comment.
Bucket quota_data adjustments into the original task hour.
LogTaskConsumption records the pre-consume stats in the submission bucket, but this helper always uses common.GetTimestamp(). If a task is refunded or reconciled after the hour rolls over, the original bucket stays inflated and the adjustment lands in a different hour, so /api/data is still wrong for time-scoped queries.
Suggested fix
func taskAdjustQuotaData(task *model.Task, quotaDelta, tokenDelta int) {
if !common.DataExportEnabled {
return
}
if quotaDelta == 0 && tokenDelta == 0 {
return
}
username, _ := model.GetUsernameById(task.UserId, false)
- model.LogQuotaDataAdjust(task.UserId, username, taskModelName(task), quotaDelta, common.GetTimestamp(), tokenDelta)
+ createdAt := task.CreatedAt
+ if createdAt == 0 {
+ createdAt = common.GetTimestamp()
+ }
+ model.LogQuotaDataAdjust(task.UserId, username, taskModelName(task), quotaDelta, createdAt, tokenDelta)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // taskAdjustQuotaData 同步调整 quota_data 统计(/api/data/ 数据来源)。 | |
| // quotaDelta / tokenDelta 均为有符号增量: | |
| // - 失败退款:quotaDelta=-quota, tokenDelta=0(原始记录 token 就是 0,对称回退即可) | |
| // - 补扣 / 部分退款 + 真实 token:quotaDelta=±delta, tokenDelta=+totalTokens | |
| // (token 之所以 *永远是 +*:原始 LogTaskConsumption 写入的 token 都是 0, | |
| // 现在要补到 totalTokens,所以是单向增量,不随 quota 取号) | |
| func taskAdjustQuotaData(task *model.Task, quotaDelta, tokenDelta int) { | |
| if !common.DataExportEnabled { | |
| return | |
| } | |
| if quotaDelta == 0 && tokenDelta == 0 { | |
| return | |
| } | |
| username, _ := model.GetUsernameById(task.UserId, false) | |
| model.LogQuotaDataAdjust(task.UserId, username, taskModelName(task), quotaDelta, common.GetTimestamp(), tokenDelta) | |
| } | |
| // taskAdjustQuotaData 同步调整 quota_data 统计(/api/data/ 数据来源)。 | |
| // quotaDelta / tokenDelta 均为有符号增量: | |
| // - 失败退款:quotaDelta=-quota, tokenDelta=0(原始记录 token 就是 0,对称回退即可) | |
| // - 补扣 / 部分退款 + 真实 token:quotaDelta=±delta, tokenDelta=+totalTokens | |
| // (token 之所以 *永远是 +*:原始 LogTaskConsumption 写入的 token 都是 0, | |
| // 现在要补到 totalTokens,所以是单向增量,不随 quota 取号) | |
| func taskAdjustQuotaData(task *model.Task, quotaDelta, tokenDelta int) { | |
| if !common.DataExportEnabled { | |
| return | |
| } | |
| if quotaDelta == 0 && tokenDelta == 0 { | |
| return | |
| } | |
| username, _ := model.GetUsernameById(task.UserId, false) | |
| createdAt := task.CreatedAt | |
| if createdAt == 0 { | |
| createdAt = common.GetTimestamp() | |
| } | |
| model.LogQuotaDataAdjust(task.UserId, username, taskModelName(task), quotaDelta, createdAt, tokenDelta) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@service/task_billing.go` around lines 150 - 165, taskAdjustQuotaData
currently logs adjustments with common.GetTimestamp(), causing adjustments to
land in the current hour instead of the original task submission hour; change it
to derive the bucket timestamp from the task's original submission time (use
task.SubmittedAt or task.CreatedAt as provided by the Task model),
truncate/round that time to the hour used by LogTaskConsumption, and pass that
derived timestamp into model.LogQuotaDataAdjust (instead of
common.GetTimestamp()) so quota_data adjustments are recorded in the same hourly
bucket as the original LogTaskConsumption.

Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
本次提交包含两组互相独立、但都围绕 Doubao Seedance 视频任务计费链路 的修复/功能增强:
Seedance 2.0 新增 1080p 档位定价(relay/channel/task/doubao/)
在原先只考虑「是否含视频输入」一个维度的基础上,把 seedance 2.0 系列的实际定价升级为 (模型, 输出分辨率, 是否含视频输入) 三元组,原因是上游 2026-01-28 价格表对 1080p 单独给出了一档不同的单价。
异步任务额度的"守恒"修复(service/task_billing.go 等)
RefundTaskQuota / RecalculateTaskQuota 只动了 资金来源(钱包/订阅) 与 令牌额度,但没有同步回退 User.UsedQuota / Channel.UsedQuota / quota_data 看板统计,导致异步任务(seedance 等视频任务)一旦失败退款或事后差额结算,会出现:
用户「总额度 = Quota + UsedQuota」不再守恒,UsedQuota 永远偏高;实际消耗token未记录;
渠道用量(channels.used_quota)偏高;
/api/data/ 数据看板显示的 quota / token_used 与实际收费不一致;实际消耗token未记录。
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
核验了用户扣费逻辑。




Summary by CodeRabbit
Release Notes
New Features
Tests