feat(channel): 渠道管理系统 — 多模式定价 + 统一计费解析
Cherry-picked from release/custom-0.1.106: a9117600
This commit is contained in:
56
backend/migrations/081_create_channels.sql
Normal file
56
backend/migrations/081_create_channels.sql
Normal file
@@ -0,0 +1,56 @@
|
||||
-- Create channels table for managing pricing channels.
|
||||
-- A channel groups multiple groups together and provides custom model pricing.
|
||||
|
||||
SET LOCAL lock_timeout = '5s';
|
||||
SET LOCAL statement_timeout = '10min';
|
||||
|
||||
-- 渠道表
|
||||
CREATE TABLE IF NOT EXISTS channels (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 渠道名称唯一索引
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_channels_name ON channels (name);
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_status ON channels (status);
|
||||
|
||||
-- 渠道-分组关联表(每个分组只能属于一个渠道)
|
||||
CREATE TABLE IF NOT EXISTS channel_groups (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
channel_id BIGINT NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
||||
group_id BIGINT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_groups_group_id ON channel_groups (group_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_groups_channel_id ON channel_groups (channel_id);
|
||||
|
||||
-- 渠道模型定价表(一条定价可绑定多个模型)
|
||||
CREATE TABLE IF NOT EXISTS channel_model_pricing (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
channel_id BIGINT NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
||||
models JSONB NOT NULL DEFAULT '[]',
|
||||
input_price NUMERIC(20,12),
|
||||
output_price NUMERIC(20,12),
|
||||
cache_write_price NUMERIC(20,12),
|
||||
cache_read_price NUMERIC(20,12),
|
||||
image_output_price NUMERIC(20,8),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_model_pricing_channel_id ON channel_model_pricing (channel_id);
|
||||
|
||||
COMMENT ON TABLE channels IS '渠道管理:关联多个分组,提供自定义模型定价';
|
||||
COMMENT ON TABLE channel_groups IS '渠道-分组关联表:每个分组最多属于一个渠道';
|
||||
COMMENT ON TABLE channel_model_pricing IS '渠道模型定价:一条定价可绑定多个模型,价格一致';
|
||||
COMMENT ON COLUMN channel_model_pricing.models IS '绑定的模型列表,JSON 数组,如 ["claude-opus-4-6","claude-opus-4-6-thinking"]';
|
||||
COMMENT ON COLUMN channel_model_pricing.input_price IS '每 token 输入价格(USD),NULL 表示使用默认';
|
||||
COMMENT ON COLUMN channel_model_pricing.output_price IS '每 token 输出价格(USD),NULL 表示使用默认';
|
||||
COMMENT ON COLUMN channel_model_pricing.cache_write_price IS '缓存写入每 token 价格,NULL 表示使用默认';
|
||||
COMMENT ON COLUMN channel_model_pricing.cache_read_price IS '缓存读取每 token 价格,NULL 表示使用默认';
|
||||
COMMENT ON COLUMN channel_model_pricing.image_output_price IS '图片输出价格(Gemini Image 等),NULL 表示使用默认';
|
||||
67
backend/migrations/082_refactor_channel_pricing.sql
Normal file
67
backend/migrations/082_refactor_channel_pricing.sql
Normal file
@@ -0,0 +1,67 @@
|
||||
-- Extend channel_model_pricing with billing_mode and add context-interval child table.
|
||||
-- Supports three billing modes: token (per-token with context intervals),
|
||||
-- per_request (per-request with context-size tiers), and image (per-image).
|
||||
|
||||
SET LOCAL lock_timeout = '5s';
|
||||
SET LOCAL statement_timeout = '10min';
|
||||
|
||||
-- 1. 为 channel_model_pricing 添加 billing_mode 列
|
||||
ALTER TABLE channel_model_pricing
|
||||
ADD COLUMN IF NOT EXISTS billing_mode VARCHAR(20) NOT NULL DEFAULT 'token';
|
||||
|
||||
COMMENT ON COLUMN channel_model_pricing.billing_mode IS '计费模式:token(按 token 区间计费)、per_request(按次计费)、image(图片计费)';
|
||||
|
||||
-- 2. 创建区间定价子表
|
||||
CREATE TABLE IF NOT EXISTS channel_pricing_intervals (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
pricing_id BIGINT NOT NULL REFERENCES channel_model_pricing(id) ON DELETE CASCADE,
|
||||
min_tokens INT NOT NULL DEFAULT 0,
|
||||
max_tokens INT,
|
||||
tier_label VARCHAR(50),
|
||||
input_price NUMERIC(20,12),
|
||||
output_price NUMERIC(20,12),
|
||||
cache_write_price NUMERIC(20,12),
|
||||
cache_read_price NUMERIC(20,12),
|
||||
per_request_price NUMERIC(20,12),
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_pricing_intervals_pricing_id
|
||||
ON channel_pricing_intervals (pricing_id);
|
||||
|
||||
COMMENT ON TABLE channel_pricing_intervals IS '渠道定价区间:支持按 token 区间、按次分层、图片分辨率分层';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.min_tokens IS '区间下界(含),token 模式使用';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.max_tokens IS '区间上界(不含),NULL 表示无上限';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.tier_label IS '层级标签,按次/图片模式使用(如 1K、2K、4K、HD)';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.input_price IS 'token 模式:每 token 输入价';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.output_price IS 'token 模式:每 token 输出价';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.cache_write_price IS 'token 模式:缓存写入价';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.cache_read_price IS 'token 模式:缓存读取价';
|
||||
COMMENT ON COLUMN channel_pricing_intervals.per_request_price IS '按次/图片模式:每次请求价格';
|
||||
|
||||
-- 3. 迁移现有 flat 定价为单区间 [0, +inf)
|
||||
-- 仅迁移有明确定价(至少一个价格字段非 NULL)的条目
|
||||
INSERT INTO channel_pricing_intervals (pricing_id, min_tokens, max_tokens, input_price, output_price, cache_write_price, cache_read_price, sort_order)
|
||||
SELECT
|
||||
cmp.id,
|
||||
0,
|
||||
NULL,
|
||||
cmp.input_price,
|
||||
cmp.output_price,
|
||||
cmp.cache_write_price,
|
||||
cmp.cache_read_price,
|
||||
0
|
||||
FROM channel_model_pricing cmp
|
||||
WHERE cmp.billing_mode = 'token'
|
||||
AND (cmp.input_price IS NOT NULL OR cmp.output_price IS NOT NULL
|
||||
OR cmp.cache_write_price IS NOT NULL OR cmp.cache_read_price IS NOT NULL)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM channel_pricing_intervals cpi WHERE cpi.pricing_id = cmp.id
|
||||
);
|
||||
|
||||
-- 4. 迁移 image_output_price 为 image 模式的区间条目
|
||||
-- 将有 image_output_price 的现有条目复制为 billing_mode='image' 的独立条目
|
||||
-- 注意:这里不改变原条目的 billing_mode,而是将 image_output_price 作为向后兼容字段保留
|
||||
-- 实际的 image 计费在未来由独立的 billing_mode='image' 条目处理
|
||||
Reference in New Issue
Block a user