fix: 恢复迁移文件到原始状态修复校验和错误
Some checks failed
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled

Migration files should never be modified after being applied.
Reverted comment-only changes to fix checksum mismatch.

修复错误: migration 001_init.sql checksum mismatch
解决方案: 将迁移文件恢复到修改前的状态
This commit is contained in:
huangzhenpc
2026-01-04 18:11:45 +08:00
parent d274c8cb14
commit 13b95049c3
3 changed files with 270 additions and 270 deletions

View File

@@ -1,172 +1,172 @@
-- TianShuAPI 初始化数据库迁移脚本 -- Sub2API 初始化数据库迁移脚本
-- PostgreSQL 15+ -- PostgreSQL 15+
-- 1. proxies 代理IP表无外键依赖 -- 1. proxies 代理IP表无外键依赖
CREATE TABLE IF NOT EXISTS proxies ( CREATE TABLE IF NOT EXISTS proxies (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL, name VARCHAR(100) NOT NULL,
protocol VARCHAR(20) NOT NULL, -- http/https/socks5 protocol VARCHAR(20) NOT NULL, -- http/https/socks5
host VARCHAR(255) NOT NULL, host VARCHAR(255) NOT NULL,
port INT NOT NULL, port INT NOT NULL,
username VARCHAR(100), username VARCHAR(100),
password VARCHAR(100), password VARCHAR(100),
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ deleted_at TIMESTAMPTZ
); );
CREATE INDEX IF NOT EXISTS idx_proxies_status ON proxies(status); CREATE INDEX IF NOT EXISTS idx_proxies_status ON proxies(status);
CREATE INDEX IF NOT EXISTS idx_proxies_deleted_at ON proxies(deleted_at); CREATE INDEX IF NOT EXISTS idx_proxies_deleted_at ON proxies(deleted_at);
-- 2. groups 分组表(无外键依赖) -- 2. groups 分组表(无外键依赖)
CREATE TABLE IF NOT EXISTS groups ( CREATE TABLE IF NOT EXISTS groups (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE, name VARCHAR(100) NOT NULL UNIQUE,
description TEXT, description TEXT,
rate_multiplier DECIMAL(10, 4) NOT NULL DEFAULT 1.0, -- 费率倍率 rate_multiplier DECIMAL(10, 4) NOT NULL DEFAULT 1.0, -- 费率倍率
is_exclusive BOOLEAN NOT NULL DEFAULT FALSE, -- 是否专属分组 is_exclusive BOOLEAN NOT NULL DEFAULT FALSE, -- 是否专属分组
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ deleted_at TIMESTAMPTZ
); );
CREATE INDEX IF NOT EXISTS idx_groups_name ON groups(name); CREATE INDEX IF NOT EXISTS idx_groups_name ON groups(name);
CREATE INDEX IF NOT EXISTS idx_groups_status ON groups(status); CREATE INDEX IF NOT EXISTS idx_groups_status ON groups(status);
CREATE INDEX IF NOT EXISTS idx_groups_is_exclusive ON groups(is_exclusive); CREATE INDEX IF NOT EXISTS idx_groups_is_exclusive ON groups(is_exclusive);
CREATE INDEX IF NOT EXISTS idx_groups_deleted_at ON groups(deleted_at); CREATE INDEX IF NOT EXISTS idx_groups_deleted_at ON groups(deleted_at);
-- 3. users 用户表(无外键依赖) -- 3. users 用户表(无外键依赖)
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL, password_hash VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL DEFAULT 'user', -- admin/user role VARCHAR(20) NOT NULL DEFAULT 'user', -- admin/user
balance DECIMAL(20, 8) NOT NULL DEFAULT 0, -- 余额(可为负数) balance DECIMAL(20, 8) NOT NULL DEFAULT 0, -- 余额(可为负数)
concurrency INT NOT NULL DEFAULT 5, -- 并发数限制 concurrency INT NOT NULL DEFAULT 5, -- 并发数限制
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
allowed_groups BIGINT[] DEFAULT NULL, -- 允许绑定的分组ID列表 allowed_groups BIGINT[] DEFAULT NULL, -- 允许绑定的分组ID列表
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ deleted_at TIMESTAMPTZ
); );
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_status ON users(status); CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users(deleted_at); CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users(deleted_at);
-- 4. accounts 上游账号表依赖proxies -- 4. accounts 上游账号表依赖proxies
CREATE TABLE IF NOT EXISTS accounts ( CREATE TABLE IF NOT EXISTS accounts (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL, name VARCHAR(100) NOT NULL,
platform VARCHAR(50) NOT NULL, -- anthropic/openai/gemini platform VARCHAR(50) NOT NULL, -- anthropic/openai/gemini
type VARCHAR(20) NOT NULL, -- oauth/apikey type VARCHAR(20) NOT NULL, -- oauth/apikey
credentials JSONB NOT NULL DEFAULT '{}', -- 凭证信息(加密存储) credentials JSONB NOT NULL DEFAULT '{}', -- 凭证信息(加密存储)
extra JSONB NOT NULL DEFAULT '{}', -- 扩展信息 extra JSONB NOT NULL DEFAULT '{}', -- 扩展信息
proxy_id BIGINT REFERENCES proxies(id) ON DELETE SET NULL, proxy_id BIGINT REFERENCES proxies(id) ON DELETE SET NULL,
concurrency INT NOT NULL DEFAULT 3, -- 账号并发限制 concurrency INT NOT NULL DEFAULT 3, -- 账号并发限制
priority INT NOT NULL DEFAULT 50, -- 调度优先级(1-100越小越高) priority INT NOT NULL DEFAULT 50, -- 调度优先级(1-100越小越高)
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled/error status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled/error
error_message TEXT, error_message TEXT,
last_used_at TIMESTAMPTZ, last_used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ deleted_at TIMESTAMPTZ
); );
CREATE INDEX IF NOT EXISTS idx_accounts_platform ON accounts(platform); CREATE INDEX IF NOT EXISTS idx_accounts_platform ON accounts(platform);
CREATE INDEX IF NOT EXISTS idx_accounts_type ON accounts(type); CREATE INDEX IF NOT EXISTS idx_accounts_type ON accounts(type);
CREATE INDEX IF NOT EXISTS idx_accounts_status ON accounts(status); CREATE INDEX IF NOT EXISTS idx_accounts_status ON accounts(status);
CREATE INDEX IF NOT EXISTS idx_accounts_proxy_id ON accounts(proxy_id); CREATE INDEX IF NOT EXISTS idx_accounts_proxy_id ON accounts(proxy_id);
CREATE INDEX IF NOT EXISTS idx_accounts_priority ON accounts(priority); CREATE INDEX IF NOT EXISTS idx_accounts_priority ON accounts(priority);
CREATE INDEX IF NOT EXISTS idx_accounts_last_used_at ON accounts(last_used_at); CREATE INDEX IF NOT EXISTS idx_accounts_last_used_at ON accounts(last_used_at);
CREATE INDEX IF NOT EXISTS idx_accounts_deleted_at ON accounts(deleted_at); CREATE INDEX IF NOT EXISTS idx_accounts_deleted_at ON accounts(deleted_at);
-- 5. api_keys API密钥表依赖users, groups -- 5. api_keys API密钥表依赖users, groups
CREATE TABLE IF NOT EXISTS api_keys ( CREATE TABLE IF NOT EXISTS api_keys (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
key VARCHAR(64) NOT NULL UNIQUE, -- sk-xxx格式 key VARCHAR(64) NOT NULL UNIQUE, -- sk-xxx格式
name VARCHAR(100) NOT NULL, name VARCHAR(100) NOT NULL,
group_id BIGINT REFERENCES groups(id) ON DELETE SET NULL, group_id BIGINT REFERENCES groups(id) ON DELETE SET NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ deleted_at TIMESTAMPTZ
); );
CREATE INDEX IF NOT EXISTS idx_api_keys_key ON api_keys(key); CREATE INDEX IF NOT EXISTS idx_api_keys_key ON api_keys(key);
CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id); CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id);
CREATE INDEX IF NOT EXISTS idx_api_keys_group_id ON api_keys(group_id); CREATE INDEX IF NOT EXISTS idx_api_keys_group_id ON api_keys(group_id);
CREATE INDEX IF NOT EXISTS idx_api_keys_status ON api_keys(status); CREATE INDEX IF NOT EXISTS idx_api_keys_status ON api_keys(status);
CREATE INDEX IF NOT EXISTS idx_api_keys_deleted_at ON api_keys(deleted_at); CREATE INDEX IF NOT EXISTS idx_api_keys_deleted_at ON api_keys(deleted_at);
-- 6. account_groups 账号-分组关联表依赖accounts, groups -- 6. account_groups 账号-分组关联表依赖accounts, groups
CREATE TABLE IF NOT EXISTS account_groups ( CREATE TABLE IF NOT EXISTS account_groups (
account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
group_id BIGINT NOT NULL REFERENCES groups(id) ON DELETE CASCADE, group_id BIGINT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
priority INT NOT NULL DEFAULT 50, -- 分组内优先级 priority INT NOT NULL DEFAULT 50, -- 分组内优先级
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (account_id, group_id) PRIMARY KEY (account_id, group_id)
); );
CREATE INDEX IF NOT EXISTS idx_account_groups_group_id ON account_groups(group_id); CREATE INDEX IF NOT EXISTS idx_account_groups_group_id ON account_groups(group_id);
CREATE INDEX IF NOT EXISTS idx_account_groups_priority ON account_groups(priority); CREATE INDEX IF NOT EXISTS idx_account_groups_priority ON account_groups(priority);
-- 7. redeem_codes 卡密表依赖users -- 7. redeem_codes 卡密表依赖users
CREATE TABLE IF NOT EXISTS redeem_codes ( CREATE TABLE IF NOT EXISTS redeem_codes (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
code VARCHAR(32) NOT NULL UNIQUE, -- 兑换码 code VARCHAR(32) NOT NULL UNIQUE, -- 兑换码
type VARCHAR(20) NOT NULL DEFAULT 'balance', -- balance type VARCHAR(20) NOT NULL DEFAULT 'balance', -- balance
value DECIMAL(20, 8) NOT NULL, -- 面值USD value DECIMAL(20, 8) NOT NULL, -- 面值USD
status VARCHAR(20) NOT NULL DEFAULT 'unused', -- unused/used status VARCHAR(20) NOT NULL DEFAULT 'unused', -- unused/used
used_by BIGINT REFERENCES users(id) ON DELETE SET NULL, used_by BIGINT REFERENCES users(id) ON DELETE SET NULL,
used_at TIMESTAMPTZ, used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
); );
CREATE INDEX IF NOT EXISTS idx_redeem_codes_code ON redeem_codes(code); CREATE INDEX IF NOT EXISTS idx_redeem_codes_code ON redeem_codes(code);
CREATE INDEX IF NOT EXISTS idx_redeem_codes_status ON redeem_codes(status); CREATE INDEX IF NOT EXISTS idx_redeem_codes_status ON redeem_codes(status);
CREATE INDEX IF NOT EXISTS idx_redeem_codes_used_by ON redeem_codes(used_by); CREATE INDEX IF NOT EXISTS idx_redeem_codes_used_by ON redeem_codes(used_by);
-- 8. usage_logs 使用记录表依赖users, api_keys, accounts -- 8. usage_logs 使用记录表依赖users, api_keys, accounts
CREATE TABLE IF NOT EXISTS usage_logs ( CREATE TABLE IF NOT EXISTS usage_logs (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
api_key_id BIGINT NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE, api_key_id BIGINT NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE,
account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
request_id VARCHAR(64), request_id VARCHAR(64),
model VARCHAR(100) NOT NULL, model VARCHAR(100) NOT NULL,
-- Token使用量4类 -- Token使用量4类
input_tokens INT NOT NULL DEFAULT 0, input_tokens INT NOT NULL DEFAULT 0,
output_tokens INT NOT NULL DEFAULT 0, output_tokens INT NOT NULL DEFAULT 0,
cache_creation_tokens INT NOT NULL DEFAULT 0, cache_creation_tokens INT NOT NULL DEFAULT 0,
cache_read_tokens INT NOT NULL DEFAULT 0, cache_read_tokens INT NOT NULL DEFAULT 0,
-- 详细的缓存创建分类 -- 详细的缓存创建分类
cache_creation_5m_tokens INT NOT NULL DEFAULT 0, cache_creation_5m_tokens INT NOT NULL DEFAULT 0,
cache_creation_1h_tokens INT NOT NULL DEFAULT 0, cache_creation_1h_tokens INT NOT NULL DEFAULT 0,
-- 费用USD -- 费用USD
input_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, input_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
output_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, output_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
cache_creation_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, cache_creation_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
cache_read_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, cache_read_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
total_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, -- 原始总费用 total_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, -- 原始总费用
actual_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, -- 实际扣除费用 actual_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, -- 实际扣除费用
-- 元数据 -- 元数据
stream BOOLEAN NOT NULL DEFAULT FALSE, stream BOOLEAN NOT NULL DEFAULT FALSE,
duration_ms INT, duration_ms INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
); );
CREATE INDEX IF NOT EXISTS idx_usage_logs_user_id ON usage_logs(user_id); CREATE INDEX IF NOT EXISTS idx_usage_logs_user_id ON usage_logs(user_id);
CREATE INDEX IF NOT EXISTS idx_usage_logs_api_key_id ON usage_logs(api_key_id); CREATE INDEX IF NOT EXISTS idx_usage_logs_api_key_id ON usage_logs(api_key_id);
CREATE INDEX IF NOT EXISTS idx_usage_logs_account_id ON usage_logs(account_id); CREATE INDEX IF NOT EXISTS idx_usage_logs_account_id ON usage_logs(account_id);
CREATE INDEX IF NOT EXISTS idx_usage_logs_model ON usage_logs(model); CREATE INDEX IF NOT EXISTS idx_usage_logs_model ON usage_logs(model);
CREATE INDEX IF NOT EXISTS idx_usage_logs_created_at ON usage_logs(created_at); CREATE INDEX IF NOT EXISTS idx_usage_logs_created_at ON usage_logs(created_at);
CREATE INDEX IF NOT EXISTS idx_usage_logs_user_created ON usage_logs(user_id, created_at); CREATE INDEX IF NOT EXISTS idx_usage_logs_user_created ON usage_logs(user_id, created_at);

View File

@@ -1,33 +1,33 @@
-- TianShuAPI 账号类型迁移脚本 -- Sub2API 账号类型迁移脚本
-- 将 'official' 类型账号迁移为 'oauth' 或 'setup-token' -- 将 'official' 类型账号迁移为 'oauth' 或 'setup-token'
-- 根据 credentials->>'scope' 字段判断: -- 根据 credentials->>'scope' 字段判断:
-- - 包含 'user:profile' 的是 'oauth' 类型 -- - 包含 'user:profile' 的是 'oauth' 类型
-- - 只有 'user:inference' 的是 'setup-token' 类型 -- - 只有 'user:inference' 的是 'setup-token' 类型
-- 1. 将包含 profile scope 的 official 账号迁移为 oauth -- 1. 将包含 profile scope 的 official 账号迁移为 oauth
UPDATE accounts UPDATE accounts
SET type = 'oauth', SET type = 'oauth',
updated_at = NOW() updated_at = NOW()
WHERE type = 'official' WHERE type = 'official'
AND credentials->>'scope' LIKE '%user:profile%'; AND credentials->>'scope' LIKE '%user:profile%';
-- 2. 将只有 inference scope 的 official 账号迁移为 setup-token -- 2. 将只有 inference scope 的 official 账号迁移为 setup-token
UPDATE accounts UPDATE accounts
SET type = 'setup-token', SET type = 'setup-token',
updated_at = NOW() updated_at = NOW()
WHERE type = 'official' WHERE type = 'official'
AND ( AND (
credentials->>'scope' = 'user:inference' credentials->>'scope' = 'user:inference'
OR credentials->>'scope' NOT LIKE '%user:profile%' OR credentials->>'scope' NOT LIKE '%user:profile%'
); );
-- 3. 处理没有 scope 字段的旧账号(默认为 oauth -- 3. 处理没有 scope 字段的旧账号(默认为 oauth
UPDATE accounts UPDATE accounts
SET type = 'oauth', SET type = 'oauth',
updated_at = NOW() updated_at = NOW()
WHERE type = 'official' WHERE type = 'official'
AND (credentials->>'scope' IS NULL OR credentials->>'scope' = ''); AND (credentials->>'scope' IS NULL OR credentials->>'scope' = '');
-- 4. 验证迁移结果(查询是否还有 official 类型账号) -- 4. 验证迁移结果(查询是否还有 official 类型账号)
-- SELECT COUNT(*) FROM accounts WHERE type = 'official'; -- SELECT COUNT(*) FROM accounts WHERE type = 'official';
-- 如果结果为 0说明迁移成功 -- 如果结果为 0说明迁移成功

View File

@@ -1,65 +1,65 @@
-- TianShuAPI 订阅功能迁移脚本 -- Sub2API 订阅功能迁移脚本
-- 添加订阅分组和用户订阅功能 -- 添加订阅分组和用户订阅功能
-- 1. 扩展 groups 表添加订阅相关字段 -- 1. 扩展 groups 表添加订阅相关字段
ALTER TABLE groups ADD COLUMN IF NOT EXISTS platform VARCHAR(50) NOT NULL DEFAULT 'anthropic'; ALTER TABLE groups ADD COLUMN IF NOT EXISTS platform VARCHAR(50) NOT NULL DEFAULT 'anthropic';
ALTER TABLE groups ADD COLUMN IF NOT EXISTS subscription_type VARCHAR(20) NOT NULL DEFAULT 'standard'; ALTER TABLE groups ADD COLUMN IF NOT EXISTS subscription_type VARCHAR(20) NOT NULL DEFAULT 'standard';
ALTER TABLE groups ADD COLUMN IF NOT EXISTS daily_limit_usd DECIMAL(20, 8) DEFAULT NULL; ALTER TABLE groups ADD COLUMN IF NOT EXISTS daily_limit_usd DECIMAL(20, 8) DEFAULT NULL;
ALTER TABLE groups ADD COLUMN IF NOT EXISTS weekly_limit_usd DECIMAL(20, 8) DEFAULT NULL; ALTER TABLE groups ADD COLUMN IF NOT EXISTS weekly_limit_usd DECIMAL(20, 8) DEFAULT NULL;
ALTER TABLE groups ADD COLUMN IF NOT EXISTS monthly_limit_usd DECIMAL(20, 8) DEFAULT NULL; ALTER TABLE groups ADD COLUMN IF NOT EXISTS monthly_limit_usd DECIMAL(20, 8) DEFAULT NULL;
ALTER TABLE groups ADD COLUMN IF NOT EXISTS default_validity_days INT NOT NULL DEFAULT 30; ALTER TABLE groups ADD COLUMN IF NOT EXISTS default_validity_days INT NOT NULL DEFAULT 30;
-- 添加索引 -- 添加索引
CREATE INDEX IF NOT EXISTS idx_groups_platform ON groups(platform); CREATE INDEX IF NOT EXISTS idx_groups_platform ON groups(platform);
CREATE INDEX IF NOT EXISTS idx_groups_subscription_type ON groups(subscription_type); CREATE INDEX IF NOT EXISTS idx_groups_subscription_type ON groups(subscription_type);
-- 2. 创建 user_subscriptions 用户订阅表 -- 2. 创建 user_subscriptions 用户订阅表
CREATE TABLE IF NOT EXISTS user_subscriptions ( CREATE TABLE IF NOT EXISTS user_subscriptions (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
group_id BIGINT NOT NULL REFERENCES groups(id) ON DELETE CASCADE, group_id BIGINT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
-- 订阅有效期 -- 订阅有效期
starts_at TIMESTAMPTZ NOT NULL, starts_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ NOT NULL, expires_at TIMESTAMPTZ NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/expired/suspended status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/expired/suspended
-- 滑动窗口起始时间NULL=未激活) -- 滑动窗口起始时间NULL=未激活)
daily_window_start TIMESTAMPTZ, daily_window_start TIMESTAMPTZ,
weekly_window_start TIMESTAMPTZ, weekly_window_start TIMESTAMPTZ,
monthly_window_start TIMESTAMPTZ, monthly_window_start TIMESTAMPTZ,
-- 当前窗口已用额度USD基于 total_cost 计算) -- 当前窗口已用额度USD基于 total_cost 计算)
daily_usage_usd DECIMAL(20, 10) NOT NULL DEFAULT 0, daily_usage_usd DECIMAL(20, 10) NOT NULL DEFAULT 0,
weekly_usage_usd DECIMAL(20, 10) NOT NULL DEFAULT 0, weekly_usage_usd DECIMAL(20, 10) NOT NULL DEFAULT 0,
monthly_usage_usd DECIMAL(20, 10) NOT NULL DEFAULT 0, monthly_usage_usd DECIMAL(20, 10) NOT NULL DEFAULT 0,
-- 管理员分配信息 -- 管理员分配信息
assigned_by BIGINT REFERENCES users(id) ON DELETE SET NULL, assigned_by BIGINT REFERENCES users(id) ON DELETE SET NULL,
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
notes TEXT, notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 唯一约束:每个用户对每个分组只能有一个订阅 -- 唯一约束:每个用户对每个分组只能有一个订阅
UNIQUE(user_id, group_id) UNIQUE(user_id, group_id)
); );
-- user_subscriptions 索引 -- user_subscriptions 索引
CREATE INDEX IF NOT EXISTS idx_user_subscriptions_user_id ON user_subscriptions(user_id); CREATE INDEX IF NOT EXISTS idx_user_subscriptions_user_id ON user_subscriptions(user_id);
CREATE INDEX IF NOT EXISTS idx_user_subscriptions_group_id ON user_subscriptions(group_id); CREATE INDEX IF NOT EXISTS idx_user_subscriptions_group_id ON user_subscriptions(group_id);
CREATE INDEX IF NOT EXISTS idx_user_subscriptions_status ON user_subscriptions(status); CREATE INDEX IF NOT EXISTS idx_user_subscriptions_status ON user_subscriptions(status);
CREATE INDEX IF NOT EXISTS idx_user_subscriptions_expires_at ON user_subscriptions(expires_at); CREATE INDEX IF NOT EXISTS idx_user_subscriptions_expires_at ON user_subscriptions(expires_at);
CREATE INDEX IF NOT EXISTS idx_user_subscriptions_assigned_by ON user_subscriptions(assigned_by); CREATE INDEX IF NOT EXISTS idx_user_subscriptions_assigned_by ON user_subscriptions(assigned_by);
-- 3. 扩展 usage_logs 表添加分组和订阅关联 -- 3. 扩展 usage_logs 表添加分组和订阅关联
ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS group_id BIGINT REFERENCES groups(id) ON DELETE SET NULL; ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS group_id BIGINT REFERENCES groups(id) ON DELETE SET NULL;
ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS subscription_id BIGINT REFERENCES user_subscriptions(id) ON DELETE SET NULL; ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS subscription_id BIGINT REFERENCES user_subscriptions(id) ON DELETE SET NULL;
ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS rate_multiplier DECIMAL(10, 4) NOT NULL DEFAULT 1; ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS rate_multiplier DECIMAL(10, 4) NOT NULL DEFAULT 1;
ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS first_token_ms INT; ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS first_token_ms INT;
-- usage_logs 新索引 -- usage_logs 新索引
CREATE INDEX IF NOT EXISTS idx_usage_logs_group_id ON usage_logs(group_id); CREATE INDEX IF NOT EXISTS idx_usage_logs_group_id ON usage_logs(group_id);
CREATE INDEX IF NOT EXISTS idx_usage_logs_subscription_id ON usage_logs(subscription_id); CREATE INDEX IF NOT EXISTS idx_usage_logs_subscription_id ON usage_logs(subscription_id);
CREATE INDEX IF NOT EXISTS idx_usage_logs_sub_created ON usage_logs(subscription_id, created_at); CREATE INDEX IF NOT EXISTS idx_usage_logs_sub_created ON usage_logs(subscription_id, created_at);