diff --git a/internal/logic/billing/level.go b/internal/logic/billing/level.go index 16cd823..cf56d35 100644 --- a/internal/logic/billing/level.go +++ b/internal/logic/billing/level.go @@ -25,6 +25,31 @@ func GetTenantLevelConfig(ctx context.Context, level int) (*entity.TntTenantLeve return &config, nil } +// GetLevelPriceMultiplier 获取租户级别的价格乘数 +// 查询失败时静默返回 1.0,不阻断计费 +func GetLevelPriceMultiplier(ctx context.Context, tenantID int64) float64 { + var tenant entity.TntTenants + if err := dao.TntTenants.Ctx(ctx).Where("id", tenantID).Scan(&tenant); err != nil || tenant.Id == 0 { + g.Log().Warningf(ctx, "[Billing] failed to read tenant level for multiplier: tenantID=%d, err=%v", tenantID, err) + return 1.0 + } + + if tenant.Level <= 1 { + return 1.0 + } + + var config entity.TntTenantLevelConfigs + if err := dao.TntTenantLevelConfigs.Ctx(ctx).Where("level", tenant.Level).Scan(&config); err != nil || config.Id == 0 { + g.Log().Warningf(ctx, "[Billing] failed to read level config for multiplier: level=%d, err=%v", tenant.Level, err) + return 1.0 + } + + if config.PriceMultiplier > 0 && config.PriceMultiplier != 1.0 { + return config.PriceMultiplier + } + return 1.0 +} + // CheckAndUpgradeLevel 检查并升级租户等级(充值后调用) // 升级策略:仅升不降 — max_members/max_concurrency 仅在新值 > 当前值时才更新 func CheckAndUpgradeLevel(ctx context.Context, tenantID int64) error { @@ -97,6 +122,9 @@ func CheckAndUpgradeLevel(ctx context.Context, tenantID int64) error { // 7. 清除并发限制缓存 _, _ = g.Redis().Do(ctx, "DEL", fmt.Sprintf("tenant:conc_limit:%d", tenantID)) + // 8. 清除该租户的价格缓存(级别变化可能影响折扣) + modelPriceCache.DeleteByPattern(ctx, fmt.Sprintf("%d:*", tenantID)) + return nil } diff --git a/internal/logic/billing/pricing.go b/internal/logic/billing/pricing.go index 13af17a..49bb8f9 100644 --- a/internal/logic/billing/pricing.go +++ b/internal/logic/billing/pricing.go @@ -212,6 +212,15 @@ func GetModelPrice(ctx context.Context, tenantID int64, modelName string) (*Pric } } + // 3.5 级别折扣 fallback:当租户×模型维度未设置倍率时,使用租户级别的 price_multiplier + if tenantMultiplier == 1.0 { + levelMultiplier := GetLevelPriceMultiplier(ctx, tenantID) + if levelMultiplier > 0 && levelMultiplier < 1.0 { + tenantMultiplier = levelMultiplier + discountRatio = levelMultiplier + } + } + // 4. 套餐价(待实现) // 前置条件:需新增 pln_plan_model_pricing 表存储每个套餐的每个模型定价, // 或为 pln_plans 增加 billing_discount_ratio 全局折扣字段。 diff --git a/web/admin/src/views/tenant/TenantLevelsPage.vue b/web/admin/src/views/tenant/TenantLevelsPage.vue index 0f2be34..1f4ae28 100644 --- a/web/admin/src/views/tenant/TenantLevelsPage.vue +++ b/web/admin/src/views/tenant/TenantLevelsPage.vue @@ -100,14 +100,12 @@ async function handleSubmit() { try { if (editingId.value) { await request.put(`/admin/tenant-level-configs/${editingId.value}`, { - update: { - name: formData.name, - cumulative_recharge_threshold: formData.cumulative_recharge_threshold, - max_members: formData.max_members, - max_concurrency: formData.max_concurrency, - price_multiplier: formData.price_multiplier, - sort_order: formData.sort_order, - }, + name: formData.name, + cumulative_recharge_threshold: formData.cumulative_recharge_threshold, + max_members: formData.max_members, + max_concurrency: formData.max_concurrency, + price_multiplier: formData.price_multiplier, + sort_order: formData.sort_order, }) Message.success('更新成功') } else {