feat: 完善账户多分组功能和Azure OpenAI支持

主要功能:
- 实现账户多分组调度功能完整支持
- 修复Azure OpenAI账户优先级显示问题(前端条件判断缺失)
- 修复未分组筛选功能失效(API参数处理)
- 修复Azure OpenAI账户创建错误调用Gemini API的问题
- 完善各平台分组信息支持和使用统计显示
- 统一删除账户时的分组清理逻辑
- 添加前端请求参数处理支持

技术改进:
- 前端支持多平台账户请求构造
- 后端统一groupInfos返回格式
- API客户端完善查询参数处理

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sczheng189
2025-09-02 20:16:20 +08:00
parent e69ab2161d
commit 37e6c14eac
10 changed files with 617 additions and 517 deletions

View File

@@ -1,379 +0,0 @@
/**
* 多分组功能测试脚本
* 测试一个账户可以属于多个分组的功能
*/
require('dotenv').config()
const redis = require('../src/models/redis')
const accountGroupService = require('../src/services/accountGroupService')
const claudeAccountService = require('../src/services/claudeAccountService')
// 测试配置
const TEST_PREFIX = 'multi_group_test_'
const CLEANUP_ON_FINISH = true
// 测试数据存储
const testData = {
groups: [],
accounts: []
}
// 颜色输出
const colors = {
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
reset: '\x1b[0m'
}
function log(message, type = 'info') {
const color =
{
success: colors.green,
error: colors.red,
warning: colors.yellow,
info: colors.blue
}[type] || colors.reset
console.log(`${color}${message}${colors.reset}`)
}
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
// 清理测试数据
async function cleanup() {
log('\n🧹 清理测试数据...', 'info')
// 删除测试账户
for (const account of testData.accounts) {
try {
await claudeAccountService.deleteAccount(account.id)
log(`✅ 删除测试账户: ${account.name}`, 'success')
} catch (error) {
log(`❌ 删除账户失败: ${error.message}`, 'error')
}
}
// 删除测试分组
for (const group of testData.groups) {
try {
// 先移除所有成员
const members = await accountGroupService.getGroupMembers(group.id)
for (const memberId of members) {
await accountGroupService.removeAccountFromGroup(memberId, group.id)
}
await accountGroupService.deleteGroup(group.id)
log(`✅ 删除测试分组: ${group.name}`, 'success')
} catch (error) {
log(`❌ 删除分组失败: ${error.message}`, 'error')
}
}
}
// 测试1: 创建测试数据
async function test1_createTestData() {
log('\n📝 测试1: 创建测试数据', 'info')
try {
// 创建3个测试分组
const group1 = await accountGroupService.createGroup({
name: `${TEST_PREFIX}高优先级组`,
platform: 'claude',
description: '高优先级账户分组'
})
testData.groups.push(group1)
log(`✅ 创建分组1: ${group1.name}`, 'success')
const group2 = await accountGroupService.createGroup({
name: `${TEST_PREFIX}备用组`,
platform: 'claude',
description: '备用账户分组'
})
testData.groups.push(group2)
log(`✅ 创建分组2: ${group2.name}`, 'success')
const group3 = await accountGroupService.createGroup({
name: `${TEST_PREFIX}专用组`,
platform: 'claude',
description: '专用账户分组'
})
testData.groups.push(group3)
log(`✅ 创建分组3: ${group3.name}`, 'success')
// 创建测试账户
const account1 = await claudeAccountService.createAccount({
name: `${TEST_PREFIX}测试账户1`,
email: 'test1@example.com',
refreshToken: 'test_refresh_token_1',
accountType: 'group'
})
testData.accounts.push(account1)
log(`✅ 创建测试账户1: ${account1.name}`, 'success')
const account2 = await claudeAccountService.createAccount({
name: `${TEST_PREFIX}测试账户2`,
email: 'test2@example.com',
refreshToken: 'test_refresh_token_2',
accountType: 'group'
})
testData.accounts.push(account2)
log(`✅ 创建测试账户2: ${account2.name}`, 'success')
log(`✅ 测试数据创建完成: 3个分组, 2个账户`, 'success')
} catch (error) {
log(`❌ 测试1失败: ${error.message}`, 'error')
throw error
}
}
// 测试2: 账户加入多个分组
async function test2_addAccountToMultipleGroups() {
log('\n📝 测试2: 账户加入多个分组', 'info')
try {
const [group1, group2, group3] = testData.groups
const [account1, account2] = testData.accounts
// 账户1加入分组1和分组2
await accountGroupService.addAccountToGroup(account1.id, group1.id, 'claude')
log(`✅ 账户1加入分组1: ${group1.name}`, 'success')
await accountGroupService.addAccountToGroup(account1.id, group2.id, 'claude')
log(`✅ 账户1加入分组2: ${group2.name}`, 'success')
// 账户2加入分组2和分组3
await accountGroupService.addAccountToGroup(account2.id, group2.id, 'claude')
log(`✅ 账户2加入分组2: ${group2.name}`, 'success')
await accountGroupService.addAccountToGroup(account2.id, group3.id, 'claude')
log(`✅ 账户2加入分组3: ${group3.name}`, 'success')
log(`✅ 多分组关系建立完成`, 'success')
} catch (error) {
log(`❌ 测试2失败: ${error.message}`, 'error')
throw error
}
}
// 测试3: 验证多分组关系
async function test3_verifyMultiGroupRelationships() {
log('\n📝 测试3: 验证多分组关系', 'info')
try {
const [group1, group2, group3] = testData.groups
const [account1, account2] = testData.accounts
// 验证账户1的分组关系
const account1Groups = await accountGroupService.getAccountGroup(account1.id)
log(`📊 账户1所属分组数量: ${account1Groups.length}`, 'info')
const account1GroupNames = account1Groups.map((g) => g.name).sort()
const expectedAccount1Groups = [group1.name, group2.name].sort()
if (JSON.stringify(account1GroupNames) === JSON.stringify(expectedAccount1Groups)) {
log(`✅ 账户1分组关系正确: [${account1GroupNames.join(', ')}]`, 'success')
} else {
throw new Error(
`账户1分组关系错误期望: [${expectedAccount1Groups.join(', ')}], 实际: [${account1GroupNames.join(', ')}]`
)
}
// 验证账户2的分组关系
const account2Groups = await accountGroupService.getAccountGroup(account2.id)
log(`📊 账户2所属分组数量: ${account2Groups.length}`, 'info')
const account2GroupNames = account2Groups.map((g) => g.name).sort()
const expectedAccount2Groups = [group2.name, group3.name].sort()
if (JSON.stringify(account2GroupNames) === JSON.stringify(expectedAccount2Groups)) {
log(`✅ 账户2分组关系正确: [${account2GroupNames.join(', ')}]`, 'success')
} else {
throw new Error(
`账户2分组关系错误期望: [${expectedAccount2Groups.join(', ')}], 实际: [${account2GroupNames.join(', ')}]`
)
}
log(`✅ 多分组关系验证通过`, 'success')
} catch (error) {
log(`❌ 测试3失败: ${error.message}`, 'error')
throw error
}
}
// 测试4: 验证分组成员关系
async function test4_verifyGroupMemberships() {
log('\n📝 测试4: 验证分组成员关系', 'info')
try {
const [group1, group2, group3] = testData.groups
const [account1, account2] = testData.accounts
// 验证分组1的成员
const group1Members = await accountGroupService.getGroupMembers(group1.id)
if (group1Members.includes(account1.id) && group1Members.length === 1) {
log(`✅ 分组1成员正确: [${account1.name}]`, 'success')
} else {
throw new Error(`分组1成员错误期望: [${account1.id}], 实际: [${group1Members.join(', ')}]`)
}
// 验证分组2的成员应该包含两个账户
const group2Members = await accountGroupService.getGroupMembers(group2.id)
const expectedGroup2Members = [account1.id, account2.id].sort()
const actualGroup2Members = group2Members.sort()
if (JSON.stringify(actualGroup2Members) === JSON.stringify(expectedGroup2Members)) {
log(`✅ 分组2成员正确: [${account1.name}, ${account2.name}]`, 'success')
} else {
throw new Error(
`分组2成员错误期望: [${expectedGroup2Members.join(', ')}], 实际: [${actualGroup2Members.join(', ')}]`
)
}
// 验证分组3的成员
const group3Members = await accountGroupService.getGroupMembers(group3.id)
if (group3Members.includes(account2.id) && group3Members.length === 1) {
log(`✅ 分组3成员正确: [${account2.name}]`, 'success')
} else {
throw new Error(`分组3成员错误期望: [${account2.id}], 实际: [${group3Members.join(', ')}]`)
}
log(`✅ 分组成员关系验证通过`, 'success')
} catch (error) {
log(`❌ 测试4失败: ${error.message}`, 'error')
throw error
}
}
// 测试5: 从部分分组中移除账户
async function test5_removeFromPartialGroups() {
log('\n📝 测试5: 从部分分组中移除账户', 'info')
try {
const [group1, group2] = testData.groups
const [account1] = testData.accounts
// 将账户1从分组1中移除但仍在分组2中
await accountGroupService.removeAccountFromGroup(account1.id, group1.id)
log(`✅ 从分组1中移除账户1`, 'success')
// 验证账户1现在只属于分组2
const account1Groups = await accountGroupService.getAccountGroup(account1.id)
if (account1Groups.length === 1 && account1Groups[0].id === group2.id) {
log(`✅ 账户1现在只属于分组2: ${account1Groups[0].name}`, 'success')
} else {
const groupNames = account1Groups.map((g) => g.name)
throw new Error(`账户1分组状态错误期望只在分组2中实际: [${groupNames.join(', ')}]`)
}
// 验证分组1现在为空
const group1Members = await accountGroupService.getGroupMembers(group1.id)
if (group1Members.length === 0) {
log(`✅ 分组1现在为空`, 'success')
} else {
throw new Error(`分组1应该为空但还有成员: [${group1Members.join(', ')}]`)
}
// 验证分组2仍有两个成员
const group2Members = await accountGroupService.getGroupMembers(group2.id)
if (group2Members.length === 2) {
log(`✅ 分组2仍有两个成员`, 'success')
} else {
throw new Error(`分组2应该有2个成员实际: ${group2Members.length}`)
}
log(`✅ 部分移除测试通过`, 'success')
} catch (error) {
log(`❌ 测试5失败: ${error.message}`, 'error')
throw error
}
}
// 测试6: 账户完全移除时的分组清理
async function test6_accountDeletionGroupCleanup() {
log('\n📝 测试6: 账户删除时的分组清理', 'info')
try {
const [, group2, group3] = testData.groups // 跳过第一个元素
const [account1, account2] = testData.accounts
// 记录删除前的状态
const beforeGroup2Members = await accountGroupService.getGroupMembers(group2.id)
const beforeGroup3Members = await accountGroupService.getGroupMembers(group3.id)
log(`📊 删除前分组2成员数: ${beforeGroup2Members.length}`, 'info')
log(`📊 删除前分组3成员数: ${beforeGroup3Members.length}`, 'info')
// 删除账户2这应该会触发从所有分组中移除的逻辑
await claudeAccountService.deleteAccount(account2.id)
log(`✅ 删除账户2: ${account2.name}`, 'success')
// 从测试数据中移除避免cleanup时重复删除
testData.accounts = testData.accounts.filter((acc) => acc.id !== account2.id)
// 等待一下确保删除操作完成
await sleep(500)
// 验证分组2现在只有账户1
const afterGroup2Members = await accountGroupService.getGroupMembers(group2.id)
if (afterGroup2Members.length === 1 && afterGroup2Members[0] === account1.id) {
log(`✅ 分组2现在只有账户1`, 'success')
} else {
throw new Error(`分组2成员状态错误期望只有账户1实际: [${afterGroup2Members.join(', ')}]`)
}
// 验证分组3现在为空
const afterGroup3Members = await accountGroupService.getGroupMembers(group3.id)
if (afterGroup3Members.length === 0) {
log(`✅ 分组3现在为空`, 'success')
} else {
throw new Error(`分组3应该为空但还有成员: [${afterGroup3Members.join(', ')}]`)
}
log(`✅ 账户删除的分组清理测试通过`, 'success')
} catch (error) {
log(`❌ 测试6失败: ${error.message}`, 'error')
throw error
}
}
// 主测试函数
async function runTests() {
log('\n🚀 开始多分组功能测试\n', 'info')
try {
// 连接Redis
await redis.connect()
log('✅ Redis连接成功', 'success')
// 执行测试
await test1_createTestData()
await test2_addAccountToMultipleGroups()
await test3_verifyMultiGroupRelationships()
await test4_verifyGroupMemberships()
await test5_removeFromPartialGroups()
await test6_accountDeletionGroupCleanup()
log('\n🎉 所有测试通过!多分组功能工作正常', 'success')
} catch (error) {
log(`\n❌ 测试失败: ${error.message}`, 'error')
console.error(error)
} finally {
// 清理测试数据
if (CLEANUP_ON_FINISH) {
await cleanup()
} else {
log('\n⚠ 测试数据未清理,请手动清理', 'warning')
}
// 关闭Redis连接
await redis.disconnect()
process.exit(0)
}
}
// 运行测试
runTests()