feat: add local auth billing and usage management

This commit is contained in:
inman committed 2026-08-12 12:13:06 +08:00
1 parent f642b5e71f
commit 196fdde83f
119 files changed
+15695 -2650

No files matched your search

+289
View File
@@ -0,0 +1,289 @@
import {
createBillingPriceRule,
listBillingPriceRules,
updateBillingPriceRule,
type BillingPriceRuleInput
} from "@/lib/server/billing-store";
import type { 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-11";
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 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: "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: "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"]>;
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
}
},
{
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-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
}
}
];
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: candidate.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 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;
}