Files
cursornew2026/backend/migrations/upgrade_v2.sql
huangzhenpc ac19d029da backend v2.1: 公告管理功能 + 系统重构
- 新增 Announcement 数据模型,支持公告的增删改查
- 后台管理新增"公告管理"Tab(创建/编辑/删除/启用禁用)
- 客户端 /api/announcement 改为从数据库读取
- 账号服务重构,新增无感换号、自动分析等功能
- 新增后台任务调度器、数据库迁移脚本
- Schema/Service/Config 全面升级至 v2.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:58:05 +08:00

203 lines
13 KiB
SQL

-- 蜂鸟Pro v2.1 数据库迁移脚本
-- 从旧版本迁移到新的数据模型
-- ==================== cursor_accounts 表迁移 ====================
-- 1. 重命名 access_token 为 token (如果存在旧列名)
-- 注意: 如果已经是 token 列名则跳过
SET @exist_access_token := (
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'cursor_accounts'
AND COLUMN_NAME = 'access_token'
);
SET @sql = IF(@exist_access_token > 0,
'ALTER TABLE cursor_accounts CHANGE COLUMN access_token token TEXT NOT NULL COMMENT "认证Token (user_id::jwt)"',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2. 添加新列 (如果不存在)
-- account_type 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'account_type');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN account_type ENUM("free_trial", "pro", "free", "business", "unknown") DEFAULT "unknown" COMMENT "账号类型"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- membership_type 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'membership_type');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN membership_type VARCHAR(50) NULL COMMENT "会员类型原始值"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- billing_cycle_start 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'billing_cycle_start');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN billing_cycle_start DATETIME NULL COMMENT "计费周期开始"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- billing_cycle_end 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'billing_cycle_end');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN billing_cycle_end DATETIME NULL COMMENT "计费周期结束"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- trial_days_remaining 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'trial_days_remaining');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN trial_days_remaining INT DEFAULT 0 COMMENT "试用剩余天数"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- usage_limit 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'usage_limit');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN usage_limit INT DEFAULT 0 COMMENT "用量上限"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- usage_used 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'usage_used');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN usage_used INT DEFAULT 0 COMMENT "已用用量"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- usage_remaining 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'usage_remaining');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN usage_remaining INT DEFAULT 0 COMMENT "剩余用量"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- usage_percent 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'usage_percent');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN usage_percent DECIMAL(5,2) DEFAULT 0 COMMENT "用量百分比"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- total_requests 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'total_requests');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN total_requests INT DEFAULT 0 COMMENT "总请求次数"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- total_input_tokens 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'total_input_tokens');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN total_input_tokens BIGINT DEFAULT 0 COMMENT "总输入Token"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- total_output_tokens 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'total_output_tokens');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN total_output_tokens BIGINT DEFAULT 0 COMMENT "总输出Token"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- total_cost_cents 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'total_cost_cents');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN total_cost_cents DECIMAL(10,2) DEFAULT 0 COMMENT "总花费(美分)"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- locked_by_key_id 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'locked_by_key_id');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN locked_by_key_id INT NULL COMMENT "被哪个激活码锁定"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- locked_at 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'locked_at');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN locked_at DATETIME NULL COMMENT "锁定时间"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- last_analyzed_at 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'last_analyzed_at');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN last_analyzed_at DATETIME NULL COMMENT "最后分析时间"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- analyze_error 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cursor_accounts' AND COLUMN_NAME = 'analyze_error');
SET @sql = IF(@exist = 0, 'ALTER TABLE cursor_accounts ADD COLUMN analyze_error VARCHAR(500) NULL COMMENT "分析错误信息"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- 3. 更新状态枚举值 (旧的 active -> available, expired -> exhausted)
UPDATE cursor_accounts SET status = 'available' WHERE status = 'active';
UPDATE cursor_accounts SET status = 'exhausted' WHERE status = 'expired';
-- 4. 修改 status 列的枚举类型
ALTER TABLE cursor_accounts MODIFY COLUMN status ENUM('pending', 'analyzing', 'available', 'in_use', 'exhausted', 'invalid', 'disabled') DEFAULT 'pending' COMMENT '账号状态';
-- 5. 添加索引
CREATE INDEX IF NOT EXISTS idx_cursor_accounts_status ON cursor_accounts(status);
CREATE INDEX IF NOT EXISTS idx_cursor_accounts_account_type ON cursor_accounts(account_type);
CREATE INDEX IF NOT EXISTS idx_cursor_accounts_locked_by_key_id ON cursor_accounts(locked_by_key_id);
-- ==================== activation_keys 表迁移 ====================
-- 添加新列 (如果不存在)
-- master_key_id 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'master_key_id');
SET @sql = IF(@exist = 0, 'ALTER TABLE activation_keys ADD COLUMN master_key_id INT NULL COMMENT "主密钥ID"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- merged_count 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'merged_count');
SET @sql = IF(@exist = 0, 'ALTER TABLE activation_keys ADD COLUMN merged_count INT DEFAULT 0 COMMENT "已合并的子密钥数量"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- merged_at 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'merged_at');
SET @sql = IF(@exist = 0, 'ALTER TABLE activation_keys ADD COLUMN merged_at DATETIME NULL COMMENT "合并时间"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- duration_days 列 (旧版可能是 valid_days)
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'duration_days');
SET @exist_valid_days := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'valid_days');
SET @sql = IF(@exist = 0 AND @exist_valid_days > 0,
'ALTER TABLE activation_keys CHANGE COLUMN valid_days duration_days INT DEFAULT 30 COMMENT "该密钥贡献的天数"',
IF(@exist = 0, 'ALTER TABLE activation_keys ADD COLUMN duration_days INT DEFAULT 30 COMMENT "该密钥贡献的天数"', 'SELECT 1')
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- quota_contribution 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'quota_contribution');
SET @sql = IF(@exist = 0, 'ALTER TABLE activation_keys ADD COLUMN quota_contribution INT DEFAULT 500 COMMENT "该密钥贡献的积分"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- seamless_enabled 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'seamless_enabled');
SET @sql = IF(@exist = 0, 'ALTER TABLE activation_keys ADD COLUMN seamless_enabled BOOLEAN DEFAULT FALSE COMMENT "是否启用无感换号"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- last_active_at 列
SET @exist := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activation_keys' AND COLUMN_NAME = 'last_active_at');
SET @sql = IF(@exist = 0, 'ALTER TABLE activation_keys ADD COLUMN last_active_at DATETIME NULL COMMENT "最后活跃时间"', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- 更新密钥状态枚举
ALTER TABLE activation_keys MODIFY COLUMN status ENUM('unused', 'active', 'expired', 'disabled') DEFAULT 'unused' COMMENT '状态';
-- 更新套餐类型枚举 (free -> auto)
UPDATE activation_keys SET membership_type = 'auto' WHERE membership_type = 'free';
ALTER TABLE activation_keys MODIFY COLUMN membership_type ENUM('auto', 'pro') DEFAULT 'pro' COMMENT '套餐类型';
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_activation_keys_master_key_id ON activation_keys(master_key_id);
CREATE INDEX IF NOT EXISTS idx_activation_keys_device_id ON activation_keys(device_id);
-- ==================== global_settings 表 - 添加自动检测开关设置 ====================
-- 确保 global_settings 表存在
CREATE TABLE IF NOT EXISTS global_settings (
id INT PRIMARY KEY AUTO_INCREMENT,
`key` VARCHAR(100) UNIQUE NOT NULL COMMENT '设置键',
value VARCHAR(500) NOT NULL COMMENT '设置值',
value_type VARCHAR(20) DEFAULT 'string' COMMENT '值类型',
description VARCHAR(500) NULL COMMENT '描述',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 添加自动检测开关设置
INSERT INTO global_settings (`key`, value, value_type, description) VALUES
('auto_analyze_enabled', 'false', 'bool', '是否启用自动账号分析'),
('auto_switch_enabled', 'true', 'bool', '是否启用自动换号'),
('account_analyze_interval', '300', 'int', '账号分析间隔(秒)'),
('account_analyze_batch_size', '10', 'int', '每批分析账号数量'),
('auto_switch_threshold', '98', 'int', 'Auto池自动换号阈值(用量百分比)'),
('pro_switch_threshold', '98', 'int', 'Pro池自动换号阈值(用量百分比)'),
('max_switch_per_day', '50', 'int', '每日最大换号次数'),
('auto_daily_switches', '999', 'int', 'Auto密钥每日换号次数限制'),
('pro_quota_per_switch', '1', 'int', 'Pro密钥每次换号消耗积分')
ON DUPLICATE KEY UPDATE description = VALUES(description);
-- ==================== 完成 ====================
SELECT '数据库迁移完成!' AS message;