| const { v4: uuidv4 } = require('uuid') |
| const logger = require('../utils/logger') |
| const redis = require('../models/redis') |
|
|
| class AccountGroupService { |
| constructor() { |
| this.GROUPS_KEY = 'account_groups' |
| this.GROUP_PREFIX = 'account_group:' |
| this.GROUP_MEMBERS_PREFIX = 'account_group_members:' |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| async createGroup(groupData) { |
| try { |
| const { name, platform, description = '' } = groupData |
|
|
| |
| if (!name || !platform) { |
| throw new Error('分组名称和平台类型为必填项') |
| } |
|
|
| |
| if (!['claude', 'gemini', 'openai', 'droid'].includes(platform)) { |
| throw new Error('平台类型必须是 claude、gemini、openai 或 droid') |
| } |
|
|
| const client = redis.getClientSafe() |
| const groupId = uuidv4() |
| const now = new Date().toISOString() |
|
|
| const group = { |
| id: groupId, |
| name, |
| platform, |
| description, |
| createdAt: now, |
| updatedAt: now |
| } |
|
|
| |
| await client.hmset(`${this.GROUP_PREFIX}${groupId}`, group) |
|
|
| |
| await client.sadd(this.GROUPS_KEY, groupId) |
|
|
| logger.success(`✅ 创建账户分组成功: ${name} (${platform})`) |
|
|
| return group |
| } catch (error) { |
| logger.error('❌ 创建账户分组失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async updateGroup(groupId, updates) { |
| try { |
| const client = redis.getClientSafe() |
| const groupKey = `${this.GROUP_PREFIX}${groupId}` |
|
|
| |
| const exists = await client.exists(groupKey) |
| if (!exists) { |
| throw new Error('分组不存在') |
| } |
|
|
| |
| const existingGroup = await client.hgetall(groupKey) |
|
|
| |
| if (updates.platform && updates.platform !== existingGroup.platform) { |
| throw new Error('不能修改分组的平台类型') |
| } |
|
|
| |
| const updateData = { |
| ...updates, |
| updatedAt: new Date().toISOString() |
| } |
|
|
| |
| delete updateData.id |
| delete updateData.platform |
| delete updateData.createdAt |
|
|
| |
| await client.hmset(groupKey, updateData) |
|
|
| |
| const updatedGroup = await client.hgetall(groupKey) |
|
|
| logger.success(`✅ 更新账户分组成功: ${updatedGroup.name}`) |
|
|
| return updatedGroup |
| } catch (error) { |
| logger.error('❌ 更新账户分组失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| async deleteGroup(groupId) { |
| try { |
| const client = redis.getClientSafe() |
|
|
| |
| const group = await this.getGroup(groupId) |
| if (!group) { |
| throw new Error('分组不存在') |
| } |
|
|
| |
| const members = await this.getGroupMembers(groupId) |
| if (members.length > 0) { |
| throw new Error('分组内还有账户,无法删除') |
| } |
|
|
| |
| const boundApiKeys = await this.getApiKeysUsingGroup(groupId) |
| if (boundApiKeys.length > 0) { |
| throw new Error('还有API Key使用此分组,无法删除') |
| } |
|
|
| |
| await client.del(`${this.GROUP_PREFIX}${groupId}`) |
| await client.del(`${this.GROUP_MEMBERS_PREFIX}${groupId}`) |
|
|
| |
| await client.srem(this.GROUPS_KEY, groupId) |
|
|
| logger.success(`✅ 删除账户分组成功: ${group.name}`) |
| } catch (error) { |
| logger.error('❌ 删除账户分组失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async getGroup(groupId) { |
| try { |
| const client = redis.getClientSafe() |
| const groupData = await client.hgetall(`${this.GROUP_PREFIX}${groupId}`) |
|
|
| if (!groupData || Object.keys(groupData).length === 0) { |
| return null |
| } |
|
|
| |
| const memberCount = await client.scard(`${this.GROUP_MEMBERS_PREFIX}${groupId}`) |
|
|
| return { |
| ...groupData, |
| memberCount: memberCount || 0 |
| } |
| } catch (error) { |
| logger.error('❌ 获取分组详情失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async getAllGroups(platform = null) { |
| try { |
| const client = redis.getClientSafe() |
| const groupIds = await client.smembers(this.GROUPS_KEY) |
|
|
| const groups = [] |
| for (const groupId of groupIds) { |
| const group = await this.getGroup(groupId) |
| if (group) { |
| |
| if (!platform || group.platform === platform) { |
| groups.push(group) |
| } |
| } |
| } |
|
|
| |
| groups.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)) |
|
|
| return groups |
| } catch (error) { |
| logger.error('❌ 获取分组列表失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async addAccountToGroup(accountId, groupId, accountPlatform) { |
| try { |
| const client = redis.getClientSafe() |
|
|
| |
| const group = await this.getGroup(groupId) |
| if (!group) { |
| throw new Error('分组不存在') |
| } |
|
|
| |
| const normalizedAccountPlatform = |
| accountPlatform === 'claude-console' ? 'claude' : accountPlatform |
| if (normalizedAccountPlatform !== group.platform) { |
| throw new Error('账户平台与分组平台不匹配') |
| } |
|
|
| |
| await client.sadd(`${this.GROUP_MEMBERS_PREFIX}${groupId}`, accountId) |
|
|
| logger.success(`✅ 添加账户到分组成功: ${accountId} -> ${group.name}`) |
| } catch (error) { |
| logger.error('❌ 添加账户到分组失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async removeAccountFromGroup(accountId, groupId) { |
| try { |
| const client = redis.getClientSafe() |
|
|
| |
| await client.srem(`${this.GROUP_MEMBERS_PREFIX}${groupId}`, accountId) |
|
|
| logger.success(`✅ 从分组移除账户成功: ${accountId}`) |
| } catch (error) { |
| logger.error('❌ 从分组移除账户失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async getGroupMembers(groupId) { |
| try { |
| const client = redis.getClientSafe() |
| const members = await client.smembers(`${this.GROUP_MEMBERS_PREFIX}${groupId}`) |
| return members || [] |
| } catch (error) { |
| logger.error('❌ 获取分组成员失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async isGroupEmpty(groupId) { |
| try { |
| const members = await this.getGroupMembers(groupId) |
| return members.length === 0 |
| } catch (error) { |
| logger.error('❌ 检查分组是否为空失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async getApiKeysUsingGroup(groupId) { |
| try { |
| const client = redis.getClientSafe() |
| const groupKey = `group:${groupId}` |
|
|
| |
| const apiKeyIds = await client.smembers('api_keys') |
| const boundApiKeys = [] |
|
|
| for (const keyId of apiKeyIds) { |
| const keyData = await client.hgetall(`api_key:${keyId}`) |
| if ( |
| keyData && |
| (keyData.claudeAccountId === groupKey || |
| keyData.geminiAccountId === groupKey || |
| keyData.openaiAccountId === groupKey || |
| keyData.droidAccountId === groupKey) |
| ) { |
| boundApiKeys.push({ |
| id: keyId, |
| name: keyData.name |
| }) |
| } |
| } |
|
|
| return boundApiKeys |
| } catch (error) { |
| logger.error('❌ 获取使用分组的API Key失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async getAccountGroup(accountId) { |
| try { |
| const client = redis.getClientSafe() |
| const allGroupIds = await client.smembers(this.GROUPS_KEY) |
|
|
| for (const groupId of allGroupIds) { |
| const isMember = await client.sismember(`${this.GROUP_MEMBERS_PREFIX}${groupId}`, accountId) |
| if (isMember) { |
| return await this.getGroup(groupId) |
| } |
| } |
|
|
| return null |
| } catch (error) { |
| logger.error('❌ 获取账户所属分组失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async getAccountGroups(accountId) { |
| try { |
| const client = redis.getClientSafe() |
| const allGroupIds = await client.smembers(this.GROUPS_KEY) |
| const memberGroups = [] |
|
|
| for (const groupId of allGroupIds) { |
| const isMember = await client.sismember(`${this.GROUP_MEMBERS_PREFIX}${groupId}`, accountId) |
| if (isMember) { |
| const group = await this.getGroup(groupId) |
| if (group) { |
| memberGroups.push(group) |
| } |
| } |
| } |
|
|
| |
| memberGroups.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)) |
|
|
| return memberGroups |
| } catch (error) { |
| logger.error('❌ 获取账户所属分组列表失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async setAccountGroups(accountId, groupIds, accountPlatform) { |
| try { |
| |
| await this.removeAccountFromAllGroups(accountId) |
|
|
| |
| for (const groupId of groupIds) { |
| await this.addAccountToGroup(accountId, groupId, accountPlatform) |
| } |
|
|
| logger.success(`✅ 批量设置账户分组成功: ${accountId} -> [${groupIds.join(', ')}]`) |
| } catch (error) { |
| logger.error('❌ 批量设置账户分组失败:', error) |
| throw error |
| } |
| } |
|
|
| |
| |
| |
| |
| async removeAccountFromAllGroups(accountId) { |
| try { |
| const client = redis.getClientSafe() |
| const allGroupIds = await client.smembers(this.GROUPS_KEY) |
|
|
| for (const groupId of allGroupIds) { |
| await client.srem(`${this.GROUP_MEMBERS_PREFIX}${groupId}`, accountId) |
| } |
|
|
| logger.success(`✅ 从所有分组移除账户成功: ${accountId}`) |
| } catch (error) { |
| logger.error('❌ 从所有分组移除账户失败:', error) |
| throw error |
| } |
| } |
| } |
|
|
| module.exports = new AccountGroupService() |
|
|