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
@@ -0,0 +1,43 @@
package store
import (
"context"
"gorm.io/gorm/clause"
)
// Setting 是平台级键值配置(全局,非租户作用域)——如 token→积分汇率 等可后台热调的计费规则。
// 表名 sundynix_setting。Key 唯一。
type Setting struct {
BaseModel
Key string `gorm:"size:64;uniqueIndex"`
Value string `gorm:"size:255"`
}
func (Setting) TableName() string { return "sundynix_setting" }
// 平台设置键。
const SettingTokensPerCredit = "tokens_per_credit"
// GetSetting 读一个平台设置;不存在返回空串(调用方回退默认)。
func (p *Postgres) GetSetting(ctx context.Context, key string) string {
if p.db == nil {
return ""
}
var s Setting
if err := p.db.WithContext(ctx).Select("value").Where("key = ?", key).First(&s).Error; err != nil {
return ""
}
return s.Value
}
// SetSetting 幂等写一个平台设置(key 唯一,重复即覆盖 value)。
func (p *Postgres) SetSetting(ctx context.Context, key, value string) error {
if p.db == nil {
return errStoreDisabled
}
return p.db.WithContext(ctx).Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "key"}},
DoUpdates: clause.AssignmentColumns([]string{"value", "updated_at"}),
}).Create(&Setting{Key: key, Value: value}).Error
}