feat(gateway): 计费规则可后台配置 —— token→积分汇率(DB) + 每模型积分权重

让「扣费按规则扣」的规则真正可后台热调(此前汇率是 env、积分权重无接口):
- sundynix_setting KV 表 + GetSetting/SetSetting;TokensPerCredit 改为
  DB 设置优先 → env 回退 → 1000,SaveUsageEvent 按 ctx 读,改完即对后续任务生效。
- Pricing.credit_weight 纳入 UpsertPricing + ListPricing/SavePricing API。
- GET/PUT /admin/billing-config(token→积分汇率)。

live 验证:UI 存汇率=500 → billing-config/DB=500 → 新任务 credits_micro=214000
=107tok/500×1e6,规则→扣费联动精确成立。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-07 13:26:20 +08:00
parent 54de95aa53
commit e2fc2d366c
6 changed files with 102 additions and 19 deletions
+5 -4
View File
@@ -268,15 +268,16 @@ func (p *Postgres) ListPricing(ctx context.Context) ([]Pricing, error) {
return rows, err
}
// UpsertPricing 写入/更新某模型的计价(model_id 唯一,重复即覆盖单价/币种)。
func (p *Postgres) UpsertPricing(ctx context.Context, modelID string, inPer1K, outPer1K float64, currency string) error {
// UpsertPricing 写入/更新某模型的计价(model_id 唯一,重复即覆盖单价/币种/积分权重)。
// creditWeight 为每模型积分权重(0=按 1.0 计,即不加权)。
func (p *Postgres) UpsertPricing(ctx context.Context, modelID string, inPer1K, outPer1K, creditWeight float64, currency string) error {
if p.db == nil {
return errStoreDisabled
}
return p.db.WithContext(ctx).Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "model_id"}},
DoUpdates: clause.Assignments(map[string]any{"input_per_1k": inPer1K, "output_per_1k": outPer1K, "currency": currency, "updated_at": time.Now()}),
}).Create(&Pricing{ModelID: modelID, InputPer1K: inPer1K, OutputPer1K: outPer1K, Currency: currency}).Error
DoUpdates: clause.Assignments(map[string]any{"input_per_1k": inPer1K, "output_per_1k": outPer1K, "credit_weight": creditWeight, "currency": currency, "updated_at": time.Now()}),
}).Create(&Pricing{ModelID: modelID, InputPer1K: inPer1K, OutputPer1K: outPer1K, CreditWeight: creditWeight, Currency: currency}).Error
}
// LLMModel 是一个模型后端配置(控制面:管理员在此登记可用模型)。