Files
NianAIGC/lib/server/billing-catalog.ts
2026-09-23 15:10:13 +08:00

480 lines
19 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
createBillingPriceRule,
listBillingPriceRules,
updateBillingPriceRule,
type BillingPriceRuleInput
} from "@/lib/server/billing-store";
import type { BillingParameterDimension, BillingPriceRule } from "@/lib/types";
export const BILLING_USD_CNY_RATE = 7.2;
export const DEFAULT_BILLING_MARKUP_MULTIPLIER = 1.2;
export const BILLING_CATALOG_OBSERVED_AT = "2026-08-21";
const BAILIAN_IMAGE_URL = "https://help.aliyun.com/zh/model-studio/wan2-7-image-pro";
const BAILIAN_VIDEO_URL = "https://help.aliyun.com/zh/model-studio/wan2-7-i2v";
const EVOLINK_IMAGE_URL = "https://evolink.ai/zh/gpt-image-2";
const VOLC_ARK_PRICING_URL = "https://www.volcengine.com/docs/82379/1544106?lang=zh";
const JIMENG_PRICING_URL = "https://www.volcengine.com/activity/jimeng";
const SEEDREAM_PRICING_URL = "https://docs.volcengine.com/docs/82379/1544106?lang=zh";
const MINIMAX_PRICING_URL = "https://platform.minimaxi.com/docs/guides/pricing-paygo#视频";
const SEEDREAM_50_PRO_DIMENSIONS = [
{
key: "size",
label: "分辨率",
baselineValue: "1.5K",
defaultValue: "1.5K",
tiers: [
{ value: "1K", label: "1K", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "1.5K", label: "1.5K", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "2K", label: "2K", standardFactor: 2, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true }
]
}
] satisfies NonNullable<BillingPriceRuleInput["parameterDimensions"]>;
const EVOLINK_GPT_IMAGE_DIMENSIONS = [
{
key: "quality",
label: "生成质量",
baselineValue: "medium",
defaultValue: "medium",
tiers: [
{ value: "low", label: "快速", standardFactor: 0.11, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "官方口径约为 medium 的 0.11 倍成本。" },
{ value: "medium", label: "标准", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "high", label: "精细", standardFactor: 4, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "官方口径约为 medium 的 4 倍成本。" }
]
},
{
key: "resolution",
label: "分辨率",
baselineValue: "1K",
defaultValue: "1K",
tiers: [
{ value: "1K", label: "1K", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "2K", label: "2K", standardFactor: 4, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "按像素预算相对 1K 的估算倍率。" },
{ value: "4K", label: "4K", standardFactor: 8, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "按平台支持的最大像素预算估算。" }
]
},
{
key: "aspectRatio",
label: "画面比例",
baselineValue: "1:1",
defaultValue: "1:1",
tiers: [
{ value: "1:1", label: "1:1", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "4:3", label: "4:3", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "3:4", label: "3:4", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "16:9", label: "16:9", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "9:16", label: "9:16", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "70:99", label: "A4 竖版", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "99:70", label: "A4 横版", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "other", label: "其他比例", match: {}, standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "同一分辨率档位下按相同比例预算估算。" }
]
},
{
key: "referenceImageCount",
label: "参考图数量",
baselineValue: 0,
defaultValue: 0,
tiers: [
{ value: 0, label: "无参考图", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "1–4", label: "1–4 张", match: { min: 1, max: 4 }, standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "EvoLink 上游按 image input token 计费,当前平台按基础生成成本估算。" },
{ value: "5–8", label: "5–8 张", match: { min: 5, max: 8 }, standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "EvoLink 上游按 image input token 计费,当前平台按基础生成成本估算。" },
{ value: "9–16", label: "9–16 张", match: { min: 9, max: 16 }, standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true, note: "EvoLink 上游按 image input token 计费,当前平台按基础生成成本估算。" }
]
}
] satisfies NonNullable<BillingPriceRuleInput["parameterDimensions"]>;
function evolink25PriceRule(model: string, sourceUrl: string): BillingPriceRuleInput {
const parameterDimensions = structuredClone(EVOLINK_GPT_IMAGE_DIMENSIONS);
parameterDimensions[0].tiers = [
{ value: "low", label: "快速", standardFactor: 4 / 9, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "medium", label: "标准", standardFactor: 1, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true },
{ value: "high", label: "精细", standardFactor: 35 / 9, markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER, enabled: true }
];
parameterDimensions[1].tiers = parameterDimensions[1].tiers.slice(0, 1);
return {
id: `base-evolink-${model}`,
provider: "evolink",
capability: "image.generate",
reqKey: model,
unit: "image",
standardUnitPriceFen: 9,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "1K 基础输出估算:low/medium/high 成本为 ¥0.04/¥0.09/¥0.35;输入费用未单独加收,非上游实际 token 账单。",
parameterDimensions,
source: {
url: sourceUrl,
currency: "USD",
unitPrice: 0.012,
fxRate: BILLING_USD_CNY_RATE,
basis: "1024×1024、无参考图输出费用估算:low 约 $0.0053,medium 约 $0.012,high 约 $0.0474;人民币标准成本向上取整到分。参考图和提示词会影响上游实扣。",
observedAt: "2026-09-23"
}
};
}
export const DEFAULT_BILLING_PRICE_RULES: BillingPriceRuleInput[] = [
{
id: "base-volcengine-jimeng-seedream46",
provider: "volcengine-visual",
capability: "image.generate",
reqKey: "jimeng_seedream46_cvtob",
unit: "image",
standardUnitPriceFen: 20,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "即梦4.6参考基准:公开活动页按 ¥200/1000 张折算;官方实时价格以供应商控制台为准。",
source: {
url: JIMENG_PRICING_URL,
currency: "CNY",
unitPrice: 0.2,
basis: "公开资源包 ¥200/1000 张折算;非 API 实时刊例价",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-evolink-gpt-image-2",
provider: "evolink",
capability: "image.generate",
reqKey: "gpt-image-2",
unit: "image",
standardUnitPriceFen: 34,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "EvoLink 按 token 计费;当前以 medium / 1K / 1:1 / 无参考图作为平台标准基准,参数档位另列。",
parameterDimensions: EVOLINK_GPT_IMAGE_DIMENSIONS,
source: {
url: EVOLINK_IMAGE_URL,
currency: "USD",
unitPrice: 0.047,
fxRate: BILLING_USD_CNY_RATE,
basis: "页面估算:medium、1K、1:1、无参考图,约 $0.047/张;折算 ¥0.3384,账本按 ¥0.34",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
evolink25PriceRule("gpt-image-2.5-flare", "https://evolink.ai/gpt-image-2-5"),
evolink25PriceRule("gpt-image-2.5-sunburst", "https://evolink.ai/zh/gpt-image-2-5-sunburst"),
{
id: "base-bailian-wan27-image-pro",
provider: "bailian",
capability: "image.generate",
reqKey: "wan2.7-image-pro",
unit: "image",
standardUnitPriceFen: 50,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "北京地域官方基础价:¥0.50/张。",
source: {
url: BAILIAN_IMAGE_URL,
currency: "CNY",
unitPrice: 0.5,
basis: "北京地域,按成功生成图片计费",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedream-5-0-pro",
provider: "seedream",
capability: "image.generate",
reqKey: "doubao-seedream-5-0-pro-260628",
unit: "image",
standardUnitPriceFen: 30,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "基础生图:1K/1.5K ¥0.30,2K ¥0.60;首张输入图免费,额外输入图 ¥0.02/张。",
parameterDimensions: SEEDREAM_50_PRO_DIMENSIONS,
source: {
url: SEEDREAM_PRICING_URL,
currency: "CNY",
unitPrice: 0.3,
basis: "Seedream 5.0 Pro 单图基础生图;2K 按 ¥0.60/张,额外参考图按 ¥0.02/张",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-bailian-wan27-i2v-720p",
provider: "bailian",
capability: "video.generate",
reqKey: "wan2.7-i2v-2026-04-25",
variantKey: "resolution=720p",
unit: "video_second",
standardUnitPriceFen: 60,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "北京地域官方基础价:720P ¥0.60/秒。",
source: {
url: BAILIAN_VIDEO_URL,
currency: "CNY",
unitPrice: 0.6,
basis: "北京地域,720P,按成功视频秒数计费",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-bailian-wan27-i2v-1080p",
provider: "bailian",
capability: "video.generate",
reqKey: "wan2.7-i2v-2026-04-25",
variantKey: "resolution=1080p",
unit: "video_second",
standardUnitPriceFen: 100,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "北京地域官方基础价:1080P ¥1.00/秒。",
source: {
url: BAILIAN_VIDEO_URL,
currency: "CNY",
unitPrice: 1,
basis: "北京地域,1080P,按成功视频秒数计费",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedance-2-0-480p",
provider: "seedance",
capability: "video.generate",
reqKey: "doubao-seedance-2-0-260128",
variantKey: "resolution=480p",
unit: "video_second",
standardUnitPriceFen: 46,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "官方 5 秒、16:9、无输入视频样例折算:¥2.31/5秒≈¥0.46/秒;实际按方舟 token 用量结算。",
source: {
url: VOLC_ARK_PRICING_URL,
currency: "CNY",
unitPrice: 0.46,
basis: "doubao-seedance-2.0,480P,无输入视频、16:9、5 秒示例折算;实际 token 计费可能随输入变化",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedance-2-0-720p",
provider: "seedance",
capability: "video.generate",
reqKey: "doubao-seedance-2-0-260128",
variantKey: "resolution=720p",
unit: "video_second",
standardUnitPriceFen: 99,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "官方 5 秒、16:9、无输入视频样例折算:¥4.97/5秒≈¥0.99/秒;实际按方舟 token 用量结算。",
source: {
url: VOLC_ARK_PRICING_URL,
currency: "CNY",
unitPrice: 0.99,
basis: "doubao-seedance-2.0,720P,无输入视频、16:9、5 秒示例折算;实际 token 计费可能随输入变化",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedance-2-0-1080p",
provider: "seedance",
capability: "video.generate",
reqKey: "doubao-seedance-2-0-260128",
variantKey: "resolution=1080p",
unit: "video_second",
standardUnitPriceFen: 248,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "官方 5 秒、16:9、无输入视频样例折算:¥12.39/5秒≈¥2.48/秒;实际按方舟 token 用量结算。",
source: {
url: VOLC_ARK_PRICING_URL,
currency: "CNY",
unitPrice: 2.48,
basis: "doubao-seedance-2.0,1080P,无输入视频、16:9、5 秒示例折算;实际 token 计费可能随输入变化",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedance-2-0-4k",
provider: "seedance",
capability: "video.generate",
reqKey: "doubao-seedance-2-0-260128",
variantKey: "resolution=4k",
unit: "video_second",
standardUnitPriceFen: 505,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "官方 5 秒、16:9、无输入视频样例折算:¥25.27/5秒≈¥5.05/秒;实际按方舟 token 用量结算。",
source: {
url: VOLC_ARK_PRICING_URL,
currency: "CNY",
unitPrice: 5.05,
basis: "doubao-seedance-2.0,4K,无输入视频、16:9、5 秒示例折算;实际 token 计费可能随输入变化",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedance-2-5-480p",
provider: "seedance",
capability: "video.generate",
reqKey: "doubao-seedance-2-5-260628",
variantKey: "resolution=480p",
unit: "video_second",
standardUnitPriceFen: 67,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "官方 5 秒、16:9、无输入视频样例 ¥3.36,实际按方舟 token 用量结算。",
source: {
url: `${VOLC_ARK_PRICING_URL}#sd25_price`,
currency: "CNY",
unitPrice: 0.67,
basis: "doubao-seedance-2.5,480P,无输入视频、16:9、5 秒示例折算",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedance-2-5-720p",
provider: "seedance",
capability: "video.generate",
reqKey: "doubao-seedance-2-5-260628",
variantKey: "resolution=720p",
unit: "video_second",
standardUnitPriceFen: 151,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "官方 5 秒、16:9、无输入视频样例 ¥7.56,实际按方舟 token 用量结算。",
source: {
url: `${VOLC_ARK_PRICING_URL}#sd25_price`,
currency: "CNY",
unitPrice: 1.51,
basis: "doubao-seedance-2.5,720P,无输入视频、16:9、5 秒示例折算",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-seedance-2-5-1080p",
provider: "seedance",
capability: "video.generate",
reqKey: "doubao-seedance-2-5-260628",
variantKey: "resolution=1080p",
unit: "video_second",
standardUnitPriceFen: 374,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "官方 5 秒、16:9、无输入视频样例 ¥18.71,实际按方舟 token 用量结算。",
source: {
url: `${VOLC_ARK_PRICING_URL}#sd25_price`,
currency: "CNY",
unitPrice: 3.74,
basis: "doubao-seedance-2.5,1080P,无输入视频、16:9、5 秒示例折算",
observedAt: BILLING_CATALOG_OBSERVED_AT
}
},
{
id: "base-minimax-h3-768p",
provider: "minimax",
capability: "video.generate",
reqKey: "MiniMax-H3",
variantKey: "resolution=768p",
unit: "video_second",
standardUnitPriceFen: 50,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "MiniMax H3 768P 输出 ¥0.50/秒;首五张输入图免费。",
source: {
url: MINIMAX_PRICING_URL,
currency: "CNY",
unitPrice: 0.5,
basis: "MiniMax H3 768P 输出视频,按成功视频秒数计费",
observedAt: "2026-09-04"
}
},
{
id: "base-minimax-h3-2k",
provider: "minimax",
capability: "video.generate",
reqKey: "MiniMax-H3",
variantKey: "resolution=2k",
unit: "video_second",
standardUnitPriceFen: 80,
markupMultiplier: DEFAULT_BILLING_MARKUP_MULTIPLIER,
enabled: true,
note: "MiniMax H3 2K 输出 ¥0.80/秒;首五张输入图免费。",
source: {
url: MINIMAX_PRICING_URL,
currency: "CNY",
unitPrice: 0.8,
basis: "MiniMax H3 2K 输出视频,按成功视频秒数计费",
observedAt: "2026-09-04"
}
}
];
export async function ensureDefaultBillingPriceRules(): Promise<BillingPriceRule[]> {
const existing = await listBillingPriceRules({ includeDisabled: true });
const created: BillingPriceRule[] = [];
for (const candidate of DEFAULT_BILLING_PRICE_RULES) {
const candidateKey = priceRuleMatchKey(candidate);
const existingRule = existing.find((rule) => priceRuleMatchKey(rule) === candidateKey);
if (existingRule) {
const updated = await updateBillingPriceRule(existingRule.id, {
reqKey: candidate.reqKey,
variantKey: candidate.variantKey,
unit: candidate.unit,
standardUnitPriceFen: candidate.standardUnitPriceFen,
enabled: candidate.enabled,
conditions: candidate.conditions,
quantitySource: candidate.quantitySource,
priority: candidate.priority,
note: candidate.note,
source: candidate.source,
parameterDimensions: mergeParameterDimensions(candidate.parameterDimensions, existingRule.parameterDimensions)
});
if (updated) {
const index = existing.findIndex((rule) => rule.id === updated.id);
if (index >= 0) existing[index] = updated;
}
continue;
}
if (existing.some((rule) => rule.provider === candidate.provider
&& rule.capability === candidate.capability
&& !rule.reqKey
&& !rule.variantKey)) continue;
try {
const rule = await createBillingPriceRule(candidate);
created.push(rule);
} catch (error) {
if (!isConflict(error)) throw error;
}
}
return [...existing, ...created];
}
function mergeParameterDimensions(
defaults: BillingParameterDimension[] | undefined,
saved: BillingParameterDimension[] | undefined
): BillingParameterDimension[] | undefined {
return defaults?.map((dimension) => {
const savedDimension = saved?.find((item) => item.key === dimension.key);
return {
...dimension,
tiers: dimension.tiers.map((tier) => {
const savedTier = savedDimension?.tiers.find((item) => String(item.value) === String(tier.value));
const savedMarkup = savedTier?.markupMultiplier;
return {
...tier,
markupMultiplier: typeof savedMarkup === "number"
&& Number.isFinite(savedMarkup)
&& savedMarkup >= 1
&& savedMarkup <= 1000
? savedMarkup
: tier.markupMultiplier
};
})
};
});
}
function priceRuleMatchKey(rule: Pick<BillingPriceRule, "provider" | "capability" | "reqKey" | "variantKey" | "conditions">): string {
const conditions = Object.fromEntries(Object.entries(rule.conditions || {}).sort(([left], [right]) => left.localeCompare(right)));
return [rule.provider, rule.capability, rule.reqKey || "", rule.variantKey || "", JSON.stringify(conditions)].join("\u0000");
}
function isConflict(error: unknown): boolean {
return typeof error === "object" && error !== null && "status" in error && (error as { status?: unknown }).status === 409;
}