-- 蜂鸟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;