package model import ( "math" "sort" "strings" "github.com/QuantumNous/new-api/setting/ratio_setting" ) // ImagePerImageTierRow 单档图片按张标价(已乘渠道展示折扣,未乘用户分组倍率)。 type ImagePerImageTierRow struct { UsdAfterChannelDiscount float64 `json:"usd_after_channel_discount"` Resolution string `json:"resolution,omitempty"` Lane string `json:"lane,omitempty"` } // ImagePerImagePricingHint 多档图片分辨率价在定价卡片上的摘要。 type ImagePerImagePricingHint struct { MinUsdAfterChannelDiscount float64 `json:"min_usd_after_channel_discount"` Resolution string `json:"resolution,omitempty"` Lane string `json:"lane,omitempty"` TierCount int `json:"tier_count"` Tiers []ImagePerImageTierRow `json:"tiers,omitempty"` } type imagePerImageTier struct { RawUSD float64 Res string Lane string } func laneOrderImagePerImage(l string) int { switch l { case "text_to_image": return 0 case "image_to_image": return 1 default: return 99 } } func tierLessImagePerImage(a, b imagePerImageTier) bool { ar := strings.TrimSpace(strings.ToLower(a.Res)) br := strings.TrimSpace(strings.ToLower(b.Res)) if ar != br { return ar < br } return laneOrderImagePerImage(a.Lane) < laneOrderImagePerImage(b.Lane) } func collectImagePerImageTiers(rules ratio_setting.ImagePricingRules) []imagePerImageTier { out := make([]imagePerImageTier, 0, 16) for _, r := range rules.TextToImagePerImage { if r.ImagePrice <= 0 { continue } out = append(out, imagePerImageTier{ RawUSD: r.ImagePrice, Res: r.Resolution, Lane: "text_to_image", }) } for _, r := range rules.ImageToImagePerImage { if r.ImagePrice <= 0 { continue } out = append(out, imagePerImageTier{ RawUSD: r.ImagePrice, Res: r.Resolution, Lane: "image_to_image", }) } return out } func pickMinImagePerImageTier(tiers []imagePerImageTier) (imagePerImageTier, bool) { if len(tiers) == 0 { return imagePerImageTier{}, false } best := 0 for i := 1; i < len(tiers); i++ { a, b := tiers[best], tiers[i] if b.RawUSD < a.RawUSD-1e-12 { best = i continue } if math.Abs(b.RawUSD-a.RawUSD) < 1e-9 && tierLessImagePerImage(b, a) { best = i } } return tiers[best], true } func imageTierRowLess(a, b ImagePerImageTierRow) bool { ar := strings.TrimSpace(strings.ToLower(a.Resolution)) br := strings.TrimSpace(strings.ToLower(b.Resolution)) if ar != br { return ar < br } return laneOrderImagePerImage(a.Lane) < laneOrderImagePerImage(b.Lane) } func buildSortedImagePerImageTierRows(tiers []imagePerImageTier, globalRules ratio_setting.ImagePricingRules, costDiscPercent, markupDiscPercent float64) []ImagePerImageTierRow { rows := make([]ImagePerImageTierRow, 0, len(tiers)) for _, ti := range tiers { globalRaw := lookupImageTierRawUSD(globalRules, ti) usd := EffectiveRuleUnitPrice(ti.RawUSD, globalRaw, costDiscPercent, markupDiscPercent) if usd <= 0 { continue } rows = append(rows, ImagePerImageTierRow{ UsdAfterChannelDiscount: usd, Resolution: strings.TrimSpace(ti.Res), Lane: ti.Lane, }) } sort.Slice(rows, func(i, j int) bool { a, b := rows[i], rows[j] if math.Abs(a.UsdAfterChannelDiscount-b.UsdAfterChannelDiscount) > 1e-9 { return a.UsdAfterChannelDiscount < b.UsdAfterChannelDiscount } return imageTierRowLess(a, b) }) return rows } func resolveChannelImageRulesForPricingCardHint(channelID int, modelName string) (ratio_setting.ImagePricingRules, bool) { if channelID > 0 { if rules, ok := ratio_setting.GetChannelImagePricingRules(channelID, modelName); ok && ratio_setting.HasUsableImagePerImageRules(rules) { return rules, true } } return ratio_setting.ImagePricingRules{}, false } func resolveGlobalImageRulesForPricingCardHint(modelName string) (ratio_setting.ImagePricingRules, bool) { if rules, ok := ratio_setting.GetImagePricingRules(modelName); ok && ratio_setting.HasUsableImagePerImageRules(rules) { return rules, true } return ratio_setting.ImagePricingRules{}, false } func lookupImageTierRawUSD(rules ratio_setting.ImagePricingRules, target imagePerImageTier) float64 { for _, c := range collectImagePerImageTiers(rules) { if c.Lane != target.Lane { continue } if !strings.EqualFold(strings.TrimSpace(c.Res), strings.TrimSpace(target.Res)) { continue } return c.RawUSD } return 0 } // BuildImagePerImageHint 汇总当前模型×渠道下图片按张档位,返回最低价档(含成本折扣与加价折扣)及全部档位。 func BuildImagePerImageHint(channelID int, modelName string, costDiscPercent, markupDiscPercent float64) *ImagePerImagePricingHint { channelRules, chOK := resolveChannelImageRulesForPricingCardHint(channelID, modelName) globalRules, glOK := resolveGlobalImageRulesForPricingCardHint(modelName) if !chOK && !glOK { return nil } rulesForTiers := channelRules if !chOK { rulesForTiers = globalRules } tiers := collectImagePerImageTiers(rulesForTiers) if len(tiers) == 0 { return nil } rows := buildSortedImagePerImageTierRows(tiers, globalRules, costDiscPercent, markupDiscPercent) if len(rows) == 0 { return nil } bestRow := rows[0] return &ImagePerImagePricingHint{ MinUsdAfterChannelDiscount: bestRow.UsdAfterChannelDiscount, Resolution: bestRow.Resolution, Lane: bestRow.Lane, TierCount: len(tiers), Tiers: rows, } }