const redis = require('../models/redis'); const logger = require('../utils/logger'); const { v4: uuidv4 } = require('uuid'); /** * Token 刷新锁服务 * 提供分布式锁机制,避免并发刷新问题 */ class TokenRefreshService { constructor() { this.lockTTL = 60; // 锁的TTL: 60秒(token刷新通常在30秒内完成) this.lockValue = new Map(); // 存储每个锁的唯一值 } /** * 获取分布式锁 * 使用唯一标识符作为值,避免误释放其他进程的锁 */ async acquireLock(lockKey) { try { const client = redis.getClientSafe(); const lockId = uuidv4(); const result = await client.set(lockKey, lockId, 'NX', 'EX', this.lockTTL); if (result === 'OK') { this.lockValue.set(lockKey, lockId); logger.debug(`🔒 Acquired lock ${lockKey} with ID ${lockId}, TTL: ${this.lockTTL}s`); return true; } return false; } catch (error) { logger.error(`Failed to acquire lock ${lockKey}:`, error); return false; } } /** * 释放分布式锁 * 使用 Lua 脚本确保只释放自己持有的锁 */ async releaseLock(lockKey) { try { const client = redis.getClientSafe(); const lockId = this.lockValue.get(lockKey); if (!lockId) { logger.warn(`⚠️ No lock ID found for ${lockKey}, skipping release`); return; } // Lua 脚本:只有当值匹配时才删除 const luaScript = ` if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end `; const result = await client.eval(luaScript, 1, lockKey, lockId); if (result === 1) { this.lockValue.delete(lockKey); logger.debug(`🔓 Released lock ${lockKey} with ID ${lockId}`); } else { logger.warn(`⚠️ Lock ${lockKey} was not released - value mismatch or already expired`); } } catch (error) { logger.error(`Failed to release lock ${lockKey}:`, error); } } /** * 获取刷新锁 * @param {string} accountId - 账户ID * @param {string} platform - 平台类型 (claude/gemini) * @returns {Promise} 是否成功获取锁 */ async acquireRefreshLock(accountId, platform = 'claude') { const lockKey = `token_refresh_lock:${platform}:${accountId}`; return await this.acquireLock(lockKey); } /** * 释放刷新锁 * @param {string} accountId - 账户ID * @param {string} platform - 平台类型 (claude/gemini) */ async releaseRefreshLock(accountId, platform = 'claude') { const lockKey = `token_refresh_lock:${platform}:${accountId}`; await this.releaseLock(lockKey); } /** * 检查刷新锁状态 * @param {string} accountId - 账户ID * @param {string} platform - 平台类型 (claude/gemini) * @returns {Promise} 锁是否存在 */ async isRefreshLocked(accountId, platform = 'claude') { const lockKey = `token_refresh_lock:${platform}:${accountId}`; try { const client = redis.getClientSafe(); const exists = await client.exists(lockKey); return exists === 1; } catch (error) { logger.error(`Failed to check lock status ${lockKey}:`, error); return false; } } /** * 获取锁的剩余TTL * @param {string} accountId - 账户ID * @param {string} platform - 平台类型 (claude/gemini) * @returns {Promise} 剩余秒数,-1表示锁不存在 */ async getLockTTL(accountId, platform = 'claude') { const lockKey = `token_refresh_lock:${platform}:${accountId}`; try { const client = redis.getClientSafe(); const ttl = await client.ttl(lockKey); return ttl; } catch (error) { logger.error(`Failed to get lock TTL ${lockKey}:`, error); return -1; } } /** * 清理本地锁记录 * 在进程退出时调用,避免内存泄漏 */ cleanup() { this.lockValue.clear(); logger.info('🧹 Cleaned up local lock records'); } } // 创建单例实例 const tokenRefreshService = new TokenRefreshService(); module.exports = tokenRefreshService;