001_init.sql 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. -- Sub2API 初始化数据库迁移脚本
  2. -- PostgreSQL 15+
  3. -- 1. proxies 代理IP表(无外键依赖)
  4. CREATE TABLE IF NOT EXISTS proxies (
  5. id BIGSERIAL PRIMARY KEY,
  6. name VARCHAR(100) NOT NULL,
  7. protocol VARCHAR(20) NOT NULL, -- http/https/socks5
  8. host VARCHAR(255) NOT NULL,
  9. port INT NOT NULL,
  10. username VARCHAR(100),
  11. password VARCHAR(100),
  12. status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
  13. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  14. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  15. deleted_at TIMESTAMPTZ
  16. );
  17. CREATE INDEX IF NOT EXISTS idx_proxies_status ON proxies(status);
  18. CREATE INDEX IF NOT EXISTS idx_proxies_deleted_at ON proxies(deleted_at);
  19. -- 2. groups 分组表(无外键依赖)
  20. CREATE TABLE IF NOT EXISTS groups (
  21. id BIGSERIAL PRIMARY KEY,
  22. name VARCHAR(100) NOT NULL UNIQUE,
  23. description TEXT,
  24. rate_multiplier DECIMAL(10, 4) NOT NULL DEFAULT 1.0, -- 费率倍率
  25. is_exclusive BOOLEAN NOT NULL DEFAULT FALSE, -- 是否专属分组
  26. status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
  27. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  28. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  29. deleted_at TIMESTAMPTZ
  30. );
  31. CREATE INDEX IF NOT EXISTS idx_groups_name ON groups(name);
  32. CREATE INDEX IF NOT EXISTS idx_groups_status ON groups(status);
  33. CREATE INDEX IF NOT EXISTS idx_groups_is_exclusive ON groups(is_exclusive);
  34. CREATE INDEX IF NOT EXISTS idx_groups_deleted_at ON groups(deleted_at);
  35. -- 3. users 用户表(无外键依赖)
  36. CREATE TABLE IF NOT EXISTS users (
  37. id BIGSERIAL PRIMARY KEY,
  38. email VARCHAR(255) NOT NULL UNIQUE,
  39. password_hash VARCHAR(255) NOT NULL,
  40. role VARCHAR(20) NOT NULL DEFAULT 'user', -- admin/user
  41. balance DECIMAL(20, 8) NOT NULL DEFAULT 0, -- 余额(可为负数)
  42. concurrency INT NOT NULL DEFAULT 5, -- 并发数限制
  43. status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
  44. allowed_groups BIGINT[] DEFAULT NULL, -- 允许绑定的分组ID列表
  45. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  46. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  47. deleted_at TIMESTAMPTZ
  48. );
  49. CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
  50. CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
  51. CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users(deleted_at);
  52. -- 4. accounts 上游账号表(依赖proxies)
  53. CREATE TABLE IF NOT EXISTS accounts (
  54. id BIGSERIAL PRIMARY KEY,
  55. name VARCHAR(100) NOT NULL,
  56. platform VARCHAR(50) NOT NULL, -- anthropic/openai/gemini
  57. type VARCHAR(20) NOT NULL, -- oauth/apikey
  58. credentials JSONB NOT NULL DEFAULT '{}', -- 凭证信息(加密存储)
  59. extra JSONB NOT NULL DEFAULT '{}', -- 扩展信息
  60. proxy_id BIGINT REFERENCES proxies(id) ON DELETE SET NULL,
  61. concurrency INT NOT NULL DEFAULT 3, -- 账号并发限制
  62. priority INT NOT NULL DEFAULT 50, -- 调度优先级(1-100,越小越高)
  63. status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled/error
  64. error_message TEXT,
  65. last_used_at TIMESTAMPTZ,
  66. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  67. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  68. deleted_at TIMESTAMPTZ
  69. );
  70. CREATE INDEX IF NOT EXISTS idx_accounts_platform ON accounts(platform);
  71. CREATE INDEX IF NOT EXISTS idx_accounts_type ON accounts(type);
  72. CREATE INDEX IF NOT EXISTS idx_accounts_status ON accounts(status);
  73. CREATE INDEX IF NOT EXISTS idx_accounts_proxy_id ON accounts(proxy_id);
  74. CREATE INDEX IF NOT EXISTS idx_accounts_priority ON accounts(priority);
  75. CREATE INDEX IF NOT EXISTS idx_accounts_last_used_at ON accounts(last_used_at);
  76. CREATE INDEX IF NOT EXISTS idx_accounts_deleted_at ON accounts(deleted_at);
  77. -- 5. api_keys API密钥表(依赖users, groups)
  78. CREATE TABLE IF NOT EXISTS api_keys (
  79. id BIGSERIAL PRIMARY KEY,
  80. user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  81. key VARCHAR(64) NOT NULL UNIQUE, -- sk-xxx格式
  82. name VARCHAR(100) NOT NULL,
  83. group_id BIGINT REFERENCES groups(id) ON DELETE SET NULL,
  84. status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled
  85. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  86. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  87. deleted_at TIMESTAMPTZ
  88. );
  89. CREATE INDEX IF NOT EXISTS idx_api_keys_key ON api_keys(key);
  90. CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id);
  91. CREATE INDEX IF NOT EXISTS idx_api_keys_group_id ON api_keys(group_id);
  92. CREATE INDEX IF NOT EXISTS idx_api_keys_status ON api_keys(status);
  93. CREATE INDEX IF NOT EXISTS idx_api_keys_deleted_at ON api_keys(deleted_at);
  94. -- 6. account_groups 账号-分组关联表(依赖accounts, groups)
  95. CREATE TABLE IF NOT EXISTS account_groups (
  96. account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
  97. group_id BIGINT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
  98. priority INT NOT NULL DEFAULT 50, -- 分组内优先级
  99. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  100. PRIMARY KEY (account_id, group_id)
  101. );
  102. CREATE INDEX IF NOT EXISTS idx_account_groups_group_id ON account_groups(group_id);
  103. CREATE INDEX IF NOT EXISTS idx_account_groups_priority ON account_groups(priority);
  104. -- 7. redeem_codes 卡密表(依赖users)
  105. CREATE TABLE IF NOT EXISTS redeem_codes (
  106. id BIGSERIAL PRIMARY KEY,
  107. code VARCHAR(32) NOT NULL UNIQUE, -- 兑换码
  108. type VARCHAR(20) NOT NULL DEFAULT 'balance', -- balance
  109. value DECIMAL(20, 8) NOT NULL, -- 面值(USD)
  110. status VARCHAR(20) NOT NULL DEFAULT 'unused', -- unused/used
  111. used_by BIGINT REFERENCES users(id) ON DELETE SET NULL,
  112. used_at TIMESTAMPTZ,
  113. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  114. );
  115. CREATE INDEX IF NOT EXISTS idx_redeem_codes_code ON redeem_codes(code);
  116. CREATE INDEX IF NOT EXISTS idx_redeem_codes_status ON redeem_codes(status);
  117. CREATE INDEX IF NOT EXISTS idx_redeem_codes_used_by ON redeem_codes(used_by);
  118. -- 8. usage_logs 使用记录表(依赖users, api_keys, accounts)
  119. CREATE TABLE IF NOT EXISTS usage_logs (
  120. id BIGSERIAL PRIMARY KEY,
  121. user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  122. api_key_id BIGINT NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE,
  123. account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
  124. request_id VARCHAR(64),
  125. model VARCHAR(100) NOT NULL,
  126. -- Token使用量(4类)
  127. input_tokens INT NOT NULL DEFAULT 0,
  128. output_tokens INT NOT NULL DEFAULT 0,
  129. cache_creation_tokens INT NOT NULL DEFAULT 0,
  130. cache_read_tokens INT NOT NULL DEFAULT 0,
  131. -- 详细的缓存创建分类
  132. cache_creation_5m_tokens INT NOT NULL DEFAULT 0,
  133. cache_creation_1h_tokens INT NOT NULL DEFAULT 0,
  134. -- 费用(USD)
  135. input_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
  136. output_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
  137. cache_creation_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
  138. cache_read_cost DECIMAL(20, 10) NOT NULL DEFAULT 0,
  139. total_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, -- 原始总费用
  140. actual_cost DECIMAL(20, 10) NOT NULL DEFAULT 0, -- 实际扣除费用
  141. -- 元数据
  142. stream BOOLEAN NOT NULL DEFAULT FALSE,
  143. duration_ms INT,
  144. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  145. );
  146. CREATE INDEX IF NOT EXISTS idx_usage_logs_user_id ON usage_logs(user_id);
  147. CREATE INDEX IF NOT EXISTS idx_usage_logs_api_key_id ON usage_logs(api_key_id);
  148. CREATE INDEX IF NOT EXISTS idx_usage_logs_account_id ON usage_logs(account_id);
  149. CREATE INDEX IF NOT EXISTS idx_usage_logs_model ON usage_logs(model);
  150. CREATE INDEX IF NOT EXISTS idx_usage_logs_created_at ON usage_logs(created_at);
  151. CREATE INDEX IF NOT EXISTS idx_usage_logs_user_created ON usage_logs(user_id, created_at);