From 63bc74eca9bfbc947b0233059a5a7ebf9f00b034 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Tue, 1 Sep 2026 14:38:49 +0800 Subject: [PATCH] =?UTF-8?q?refactor(pricing):=20modelRule=20=E5=8A=A0=20me?= =?UTF-8?q?diaPrices=20+=20match=20=E7=A7=BB=E9=99=A4=20mediaType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- consts/pricing/charge_mode.go | 5 +++++ service/pricing/charge_calc.go | 27 +++++---------------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/consts/pricing/charge_mode.go b/consts/pricing/charge_mode.go index 023a4d9..c89b9b9 100644 --- a/consts/pricing/charge_mode.go +++ b/consts/pricing/charge_mode.go @@ -22,6 +22,11 @@ var ModelUnitSet = map[ChargeMode]bool{ ChargeModePerSecond: true, ChargeModePerMinute: true, ChargeModePerHour: true, ChargeModePerChar: true, } +// ModelMediaSet 模型费率 mediaPrices 允许的媒体类型键(输入媒体白名单,前端下拉同源) +var ModelMediaSet = map[string]struct{}{ + "text": {}, "audio": {}, "video": {}, "image": {}, +} + // PeriodSet 周期允许集合(per_period,扩月/季只加值) var PeriodSet = map[string]bool{"year": true} diff --git a/service/pricing/charge_calc.go b/service/pricing/charge_calc.go index f03433b..8403516 100644 --- a/service/pricing/charge_calc.go +++ b/service/pricing/charge_calc.go @@ -12,11 +12,10 @@ import ( // ====================== 用量结构 ====================== // ChargeUsage 实际用量(结算入参,工作流/调用方上报)。 -// DurationSec 秒、ItemCount 条数、TokensByModel 各模型token数(per_token 用)。 +// DurationSec 秒、TokensByModel 各模型token数(per_token 用)。 // 模型计价字段见各计算器注释(spec §5.3)。 type ChargeUsage struct { DurationSec float64 `json:"durationSec"` // 时长(秒) - ItemCount int64 `json:"itemCount"` // 条数(按条计费可选) TokensByModel map[string]int64 `json:"tokensByModel"` // 各模型消耗token // —— 模型计价扩展(spec §5.3)—— PromptTokens int64 `json:"promptTokens"` // token 计费用:输入 @@ -55,7 +54,6 @@ type perSecondRules struct { // calculator 计费方式计算器(新增方式:实现该接口并按 subject_type 注册进对应分表)。 // rules 为 rulesJSON 解析后的强类型费率结构;所有金额单位均为元(保留2位小数)。 type calculator interface { - mode() pricingConsts.ChargeMode // validate 解析并校验 rulesJSON(SaveConfig 与结算时调用) validate(rulesJSON string) (interface{}, error) // charge 结算实际金额(元),不足1分向上取整 @@ -130,8 +128,6 @@ func calcCharge(subjectType pricingConsts.SubjectType, mode pricingConsts.Charge type perItemCalculator struct{} -func (perItemCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerItem } - func (perItemCalculator) validate(rulesJSON string) (interface{}, error) { var rules perItemRules if err := json.Unmarshal([]byte(rulesJSON), &rules); err != nil { @@ -160,8 +156,6 @@ func (perItemCalculator) charge(rules interface{}, usage *ChargeUsage) (float64, type perSecondCalculator struct{} -func (perSecondCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerSecond } - func (perSecondCalculator) validate(rulesJSON string) (interface{}, error) { var rules perSecondRules if err := json.Unmarshal([]byte(rulesJSON), &rules); err != nil { @@ -183,8 +177,6 @@ func (perSecondCalculator) charge(rules interface{}, usage *ChargeUsage) (float6 type perTokenCalculator struct{} -func (perTokenCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerToken } - func (perTokenCalculator) validate(rulesJSON string) (interface{}, error) { // per_token 为 {} 仅启用标记;价格取自各模型 subject 实时配置(spec §5.4) var container map[string]interface{} @@ -210,14 +202,14 @@ type modelRules struct { } type modelRule struct { - Name string `json:"name"` - Match *modelMatch `json:"match,omitempty"` // 空=任意调用 - Price *modelPrice `json:"price"` + Name string `json:"name"` + Match *modelMatch `json:"match,omitempty"` // 空=任意调用 + Price *modelPrice `json:"price"` // 默认价:输入不含以下媒体 + MediaPrices map[string]*modelPrice `json:"mediaPrices,omitempty"` // 媒体类型→该媒体价(输入含该媒体时) } // modelMatch 命中条件(全可选,命中=全部满足) type modelMatch struct { - MediaType string `json:"mediaType,omitempty"` // text/audio/video/image Thinking *bool `json:"thinking,omitempty"` // 思考/非思考 OutputAudio *bool `json:"outputAudio,omitempty"` // 输出有声/无声 OutputResolution string `json:"outputResolution,omitempty"` // 输出分辨率 @@ -244,9 +236,6 @@ func modelRuleMatch(m *modelMatch, u *ChargeUsage) bool { if m == nil { return true } - if m.MediaType != "" && m.MediaType != u.MediaType { - return false - } if m.Thinking != nil && (u.Thinking == nil || *m.Thinking != *u.Thinking) { return false } @@ -281,8 +270,6 @@ func matchModelRule(r *modelRules, u *ChargeUsage) *modelRule { // cost = (prompt-cached)/base×input + cached/base×cacheHit + completion/base×output type modelTokenCalculator struct{ base float64 } -func (modelTokenCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePer1M } - func (c modelTokenCalculator) validate(rulesJSON string) (interface{}, error) { var r modelRules if err := json.Unmarshal([]byte(rulesJSON), &r); err != nil { @@ -355,8 +342,6 @@ type modelUnitCalculator struct { usage func(*ChargeUsage) float64 } -func (modelUnitCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerSecond } - func (c modelUnitCalculator) validate(rulesJSON string) (interface{}, error) { var r modelRules if err := json.Unmarshal([]byte(rulesJSON), &r); err != nil { @@ -394,8 +379,6 @@ func usageImageCount(u *ChargeUsage) float64 { return float64(u.ImageCount) } type perPeriodCalculator struct{} -func (perPeriodCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerPeriod } - func (perPeriodCalculator) validate(rulesJSON string) (interface{}, error) { var r perPeriodRules if err := json.Unmarshal([]byte(rulesJSON), &r); err != nil {