mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
chore: 删除临时测试脚本和评审文档
- 删除 scripts/create-test-accounts.js - 删除 scripts/add-claude-accounts.js - 删除 scripts/list-test-accounts.js - 删除 account_expire_bugfix.md 测试已完成,清理临时文件
This commit is contained in:
@@ -1,684 +0,0 @@
|
|||||||
# 账号过期功能代码评审报告
|
|
||||||
|
|
||||||
**评审日期**: 2025-10-12
|
|
||||||
**评审范围**: 账号订阅过期时间管理功能
|
|
||||||
**代码状态**: 部分完成,存在严重缺陷
|
|
||||||
**评审结论**: ❌ 不建议合并,需要修复核心缺陷
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 执行摘要
|
|
||||||
|
|
||||||
本次功能开发实现了为所有 9 个账户平台添加订阅过期时间管理功能。在数据存储和前端展示层面,实现**完整且准确**。但在**核心调度逻辑层存在严重缺陷**:
|
|
||||||
|
|
||||||
**✅ 已完成**:
|
|
||||||
- 前端路由层:所有平台支持过期时间编辑
|
|
||||||
- 后端数据层:所有服务完整存储 `subscriptionExpiresAt` 字段
|
|
||||||
- 字段映射层:路由层正确处理 `expiresAt` → `subscriptionExpiresAt` 映射
|
|
||||||
|
|
||||||
**❌ 严重缺陷**:
|
|
||||||
- **调度逻辑缺失**:除 Claude 外的所有平台(Gemini、OpenAI、Droid 等)未在账号选择时检查订阅过期时间,导致过期账号仍会被正常调度使用
|
|
||||||
|
|
||||||
**影响评估**: 该缺陷导致功能对大部分平台实际无效,用户设置的过期时间不会生效。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 已完成部分(质量优秀)
|
|
||||||
|
|
||||||
### 1. 前端路由修复 ⭐⭐⭐⭐⭐
|
|
||||||
|
|
||||||
**文件**: `web/admin-spa/src/views/AccountsView.vue`
|
|
||||||
**位置**: 第 3730-3790 行
|
|
||||||
|
|
||||||
**实现内容**:
|
|
||||||
```javascript
|
|
||||||
const handleSaveAccountExpiry = async ({ accountId, expiresAt }) => {
|
|
||||||
const account = accounts.value.find((acc) => acc.id === accountId)
|
|
||||||
|
|
||||||
// 根据平台类型动态选择正确的 API 端点
|
|
||||||
let endpoint = ''
|
|
||||||
switch (account.platform) {
|
|
||||||
case 'claude':
|
|
||||||
case 'claude-oauth':
|
|
||||||
endpoint = `/admin/claude-accounts/${accountId}`
|
|
||||||
break
|
|
||||||
case 'gemini':
|
|
||||||
endpoint = `/admin/gemini-accounts/${accountId}`
|
|
||||||
break
|
|
||||||
// ... 其他 7 个平台
|
|
||||||
}
|
|
||||||
|
|
||||||
await apiClient.put(endpoint, { expiresAt: expiresAt || null })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**覆盖平台**: 所有 9 个平台(claude, gemini, claude-console, bedrock, ccr, openai, droid, azure_openai, openai-responses)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 后端路由层字段映射 ⭐⭐⭐⭐⭐
|
|
||||||
|
|
||||||
**文件**: `src/routes/admin.js`
|
|
||||||
**覆盖路由**: 8 个 PUT 端点
|
|
||||||
|
|
||||||
**统一实现**:
|
|
||||||
```javascript
|
|
||||||
// 所有路由统一添加字段映射逻辑
|
|
||||||
const mappedUpdates = { ...updates }
|
|
||||||
if ('expiresAt' in mappedUpdates) {
|
|
||||||
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
|
|
||||||
delete mappedUpdates.expiresAt
|
|
||||||
logger.info(`Mapping expiresAt to subscriptionExpiresAt...`)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**覆盖位置**:
|
|
||||||
- ✅ Claude Console: admin.js:2748
|
|
||||||
- ✅ CCR: admin.js:3174
|
|
||||||
- ✅ Bedrock: admin.js:3577
|
|
||||||
- ✅ Gemini: admin.js:4047
|
|
||||||
- ✅ OpenAI: admin.js:7429
|
|
||||||
- ✅ Azure OpenAI: admin.js:7987
|
|
||||||
- ✅ OpenAI-Responses: admin.js:8357
|
|
||||||
- ✅ Droid: admin.js:8837
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 后端数据层字段存储 ⭐⭐⭐⭐⭐
|
|
||||||
|
|
||||||
**涉及服务**: 全部 9 个 AccountService
|
|
||||||
|
|
||||||
**三层完整实现**:
|
|
||||||
|
|
||||||
#### 存储层 (createAccount)
|
|
||||||
```javascript
|
|
||||||
subscriptionExpiresAt: accountData.subscriptionExpiresAt || '',
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 查询层 (getAllAccounts)
|
|
||||||
```javascript
|
|
||||||
// 映射给前端
|
|
||||||
expiresAt: accountData.subscriptionExpiresAt || null,
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 更新层 (updateAccount)
|
|
||||||
```javascript
|
|
||||||
if (updates.subscriptionExpiresAt !== undefined) {
|
|
||||||
// 直接保存,不做调整
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**字段独立性**:
|
|
||||||
- `expiresAt` - OAuth Token 过期时间(技术字段,自动刷新)
|
|
||||||
- `subscriptionExpiresAt` - 账户订阅到期时间(业务字段,手动管理)
|
|
||||||
- 两字段完全独立,Token 刷新不会覆盖订阅过期时间 ✅
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ❌ 严重缺陷(阻塞发布)
|
|
||||||
|
|
||||||
### 核心问题:调度逻辑缺失订阅过期时间检查
|
|
||||||
|
|
||||||
**严重性**: 🔴 **P0 - 阻塞发布**
|
|
||||||
**影响范围**: Gemini、OpenAI、Droid、及其他 6 个平台(除 Claude 外所有平台)
|
|
||||||
**影响**: 过期账号仍会被正常调度,导致功能实际无效
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 缺陷 1: Gemini 账号 ❌
|
|
||||||
|
|
||||||
**文件**: `src/services/geminiAccountService.js`
|
|
||||||
**位置**: 第 285-290 行
|
|
||||||
**方法**: `selectAvailableAccount()`
|
|
||||||
|
|
||||||
**问题代码**:
|
|
||||||
```javascript
|
|
||||||
for (const accountId of sharedAccountIds) {
|
|
||||||
const account = await getAccount(accountId)
|
|
||||||
if (account && account.isActive === 'true' && !isRateLimited(account)) {
|
|
||||||
availableAccounts.push(account) // ❌ 未检查 subscriptionExpiresAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修复方案**:
|
|
||||||
```javascript
|
|
||||||
function isSubscriptionExpired(account) {
|
|
||||||
if (!account.subscriptionExpiresAt) return false
|
|
||||||
return new Date(account.subscriptionExpiresAt) <= new Date()
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const accountId of sharedAccountIds) {
|
|
||||||
const account = await getAccount(accountId)
|
|
||||||
if (
|
|
||||||
account &&
|
|
||||||
account.isActive === 'true' &&
|
|
||||||
!isRateLimited(account) &&
|
|
||||||
!isSubscriptionExpired(account) // ✅ 添加过期检查
|
|
||||||
) {
|
|
||||||
availableAccounts.push(account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 缺陷 2: OpenAI 账号 ❌
|
|
||||||
|
|
||||||
**文件**: `src/services/openaiAccountService.js`
|
|
||||||
**位置**: 第 917-922 行
|
|
||||||
**方法**: `selectAvailableAccount()`
|
|
||||||
|
|
||||||
**问题代码**:
|
|
||||||
```javascript
|
|
||||||
for (const accountId of sharedAccountIds) {
|
|
||||||
const account = await getAccount(accountId)
|
|
||||||
if (account && account.isActive === 'true' && !isRateLimited(account)) {
|
|
||||||
availableAccounts.push(account) // ❌ 未检查 subscriptionExpiresAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修复方案**: 与 Gemini 相同,添加过期检查
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 缺陷 3: Droid 账号 ❌
|
|
||||||
|
|
||||||
**文件**: `src/services/droidAccountService.js`
|
|
||||||
**位置**: 第 914-939 行
|
|
||||||
**方法**: `getSchedulableAccounts()`
|
|
||||||
|
|
||||||
**问题代码**:
|
|
||||||
```javascript
|
|
||||||
return allAccounts.filter((account) => {
|
|
||||||
const isActive = this._isTruthy(account.isActive)
|
|
||||||
const isSchedulable = this._isTruthy(account.schedulable)
|
|
||||||
const status = typeof account.status === 'string' ? account.status.toLowerCase() : ''
|
|
||||||
|
|
||||||
if (!isActive || !isSchedulable || status !== 'active') {
|
|
||||||
return false // ❌ 只检查了这些条件,未检查 subscriptionExpiresAt
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**修复方案**:
|
|
||||||
```javascript
|
|
||||||
_isSubscriptionExpired(account) {
|
|
||||||
if (!account.subscriptionExpiresAt) return false
|
|
||||||
return new Date(account.subscriptionExpiresAt) <= new Date()
|
|
||||||
}
|
|
||||||
|
|
||||||
return allAccounts.filter((account) => {
|
|
||||||
const isActive = this._isTruthy(account.isActive)
|
|
||||||
const isSchedulable = this._isTruthy(account.schedulable)
|
|
||||||
const status = typeof account.status === 'string' ? account.status.toLowerCase() : ''
|
|
||||||
const expired = this._isSubscriptionExpired(account) // ✅ 添加过期检查
|
|
||||||
|
|
||||||
if (!isActive || !isSchedulable || status !== 'active' || expired) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 缺陷 4-9: 其他平台 ⚠️
|
|
||||||
|
|
||||||
**平台**: CCR、Claude Console、Bedrock、Azure OpenAI、OpenAI-Responses
|
|
||||||
|
|
||||||
**状态**: 未发现独立的账号选择方法
|
|
||||||
|
|
||||||
**分析**:
|
|
||||||
- 这些平台可能通过 Claude 的统一调度逻辑
|
|
||||||
- 或采用简单的轮询/随机选择
|
|
||||||
- **需要全面测试确认**
|
|
||||||
|
|
||||||
**建议**: 修复前 3 个平台后,进行全量测试
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 参考实现: Claude 账号 ✅
|
|
||||||
|
|
||||||
**文件**: `src/services/claudeAccountService.js`
|
|
||||||
**位置**: 第 786-814 行
|
|
||||||
|
|
||||||
**正确实现**:
|
|
||||||
```javascript
|
|
||||||
isAccountNotExpired(account) {
|
|
||||||
if (!account.subscriptionExpiresAt) {
|
|
||||||
return true // 未设置视为永不过期
|
|
||||||
}
|
|
||||||
|
|
||||||
const expiryDate = new Date(account.subscriptionExpiresAt)
|
|
||||||
const now = new Date()
|
|
||||||
|
|
||||||
if (expiryDate <= now) {
|
|
||||||
logger.debug(
|
|
||||||
`⏰ Account ${account.name} expired at ${account.subscriptionExpiresAt}`
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在账号筛选时使用
|
|
||||||
activeAccounts = activeAccounts.filter(account =>
|
|
||||||
account.isActive === 'true' &&
|
|
||||||
account.status !== 'error' &&
|
|
||||||
account.schedulable !== 'false' &&
|
|
||||||
this.isAccountNotExpired(account) // ✅ 正确检查
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**质量**: ⭐⭐⭐⭐⭐ 可作为其他平台的参考
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 修复方案
|
|
||||||
|
|
||||||
### 推荐方案:各服务添加独立过期检查函数
|
|
||||||
|
|
||||||
**优点**:
|
|
||||||
- 保持服务独立性
|
|
||||||
- 可定制日志格式
|
|
||||||
- 不引入额外依赖
|
|
||||||
|
|
||||||
### 修复步骤
|
|
||||||
|
|
||||||
#### 步骤 1: 添加辅助函数
|
|
||||||
|
|
||||||
在各服务中添加:
|
|
||||||
```javascript
|
|
||||||
/**
|
|
||||||
* 检查账户订阅是否过期
|
|
||||||
* @param {Object} account - 账户对象
|
|
||||||
* @returns {boolean} - true: 已过期, false: 未过期
|
|
||||||
*/
|
|
||||||
function isSubscriptionExpired(account) {
|
|
||||||
if (!account.subscriptionExpiresAt || account.subscriptionExpiresAt === '') {
|
|
||||||
return false // 未设置视为永不过期
|
|
||||||
}
|
|
||||||
const expiryDate = new Date(account.subscriptionExpiresAt)
|
|
||||||
return expiryDate <= new Date()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 步骤 2: 在筛选逻辑中调用
|
|
||||||
|
|
||||||
**Gemini**:
|
|
||||||
```javascript
|
|
||||||
if (
|
|
||||||
account &&
|
|
||||||
account.isActive === 'true' &&
|
|
||||||
!isRateLimited(account) &&
|
|
||||||
!isSubscriptionExpired(account) // ✅
|
|
||||||
) {
|
|
||||||
availableAccounts.push(account)
|
|
||||||
} else if (account && isSubscriptionExpired(account)) {
|
|
||||||
logger.debug(
|
|
||||||
`⏰ Skipping expired Gemini account: ${account.name}, expired at ${account.subscriptionExpiresAt}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**OpenAI**: 同上
|
|
||||||
|
|
||||||
**Droid**:
|
|
||||||
```javascript
|
|
||||||
const expired = this._isSubscriptionExpired(account)
|
|
||||||
|
|
||||||
if (!isActive || !isSchedulable || status !== 'active' || expired) {
|
|
||||||
if (expired) {
|
|
||||||
logger.debug(`⏰ Skipping expired Droid account: ${account.name}`)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 测试方案
|
|
||||||
|
|
||||||
### 1. 功能测试
|
|
||||||
|
|
||||||
#### TC-1.1: Gemini 账号过期检查
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- 2 个 Gemini 共享账号
|
|
||||||
- Account A: subscriptionExpiresAt = "2025-01-01" (未过期)
|
|
||||||
- Account B: subscriptionExpiresAt = "2024-01-01" (已过期)
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 调用 /api/v1/messages 发送 Gemini 请求
|
|
||||||
2. 检查日志查看选中的账号
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 只有 Account A 被选中
|
|
||||||
- ✅ 日志显示 Account B 因过期被跳过
|
|
||||||
- ✅ 请求正常完成
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-1.2: OpenAI 账号过期检查
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- 2 个 OpenAI 共享账号
|
|
||||||
- Account C: subscriptionExpiresAt = "2025-01-01" (未过期)
|
|
||||||
- Account D: subscriptionExpiresAt = "2024-01-01" (已过期)
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 调用 OpenAI API
|
|
||||||
2. 检查日志
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 只有 Account C 被选中
|
|
||||||
- ✅ 请求正常完成
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-1.3: Droid 账号过期检查
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- 2 个 Droid 共享账号
|
|
||||||
- Account E: subscriptionExpiresAt = "2025-01-01" (未过期)
|
|
||||||
- Account F: subscriptionExpiresAt = "2024-01-01" (已过期)
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 调用 Droid API
|
|
||||||
2. 检查日志
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 只有 Account E 被选中
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-1.4: 全部过期无可用账号
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- 某平台所有账号都已过期
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 调用该平台 API
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 返回错误: "No available {platform} accounts"
|
|
||||||
- ✅ 日志显示所有账号因过期被跳过
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 边界测试
|
|
||||||
|
|
||||||
#### TC-2.1: 未设置过期时间
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- subscriptionExpiresAt = null 或 ""
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 调用 API
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 账号正常被选中(永不过期)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-2.2: 过期时间边界
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- 当前时间: 2025-10-12 10:00:00
|
|
||||||
- subscriptionExpiresAt = "2025-10-12T10:00:00Z"
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 在 10:00:00 时调用 API
|
|
||||||
2. 在 10:00:01 时调用 API
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 两次调用账号都因过期被跳过(<= 判断)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-2.3: 无效日期格式
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- subscriptionExpiresAt = "invalid-date"
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 调用 API
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ new Date() 返回 Invalid Date
|
|
||||||
- ✅ 比较结果为 false,账号视为未过期(容错)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 回归测试
|
|
||||||
|
|
||||||
#### TC-3.1: 现有条件不受影响
|
|
||||||
```yaml
|
|
||||||
验证点:
|
|
||||||
- isActive = 'false' 仍被跳过
|
|
||||||
- schedulable = 'false' 仍被跳过
|
|
||||||
- status = 'error' 仍被跳过
|
|
||||||
- 限流账号仍被跳过
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 所有原有过滤条件正常工作
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-3.2: Token 刷新不影响订阅时间
|
|
||||||
```yaml
|
|
||||||
前提条件:
|
|
||||||
- subscriptionExpiresAt = "2026-01-01"
|
|
||||||
- OAuth token 即将过期
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 触发 token 自动刷新
|
|
||||||
2. 检查 subscriptionExpiresAt
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ expiresAt (OAuth) 被更新
|
|
||||||
- ✅ subscriptionExpiresAt 保持不变
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-3.3: 字段独立性
|
|
||||||
```yaml
|
|
||||||
步骤:
|
|
||||||
1. 通过 Web 界面更新订阅过期时间
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ subscriptionExpiresAt 更新
|
|
||||||
- ✅ expiresAt (OAuth) 不变
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. 集成测试
|
|
||||||
|
|
||||||
#### TC-4.1: 多平台混合场景
|
|
||||||
```yaml
|
|
||||||
场景:
|
|
||||||
- 3 个 Claude: 1 过期, 2 正常
|
|
||||||
- 2 个 Gemini: 1 过期, 1 正常
|
|
||||||
- 2 个 OpenAI: 全部过期
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 调用 10 次 Claude API
|
|
||||||
2. 调用 10 次 Gemini API
|
|
||||||
3. 调用 1 次 OpenAI API
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ Claude 分配到 2 个正常账号
|
|
||||||
- ✅ Gemini 分配到 1 个正常账号
|
|
||||||
- ✅ OpenAI 返回错误
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TC-4.2: Web 界面端到端
|
|
||||||
```yaml
|
|
||||||
步骤:
|
|
||||||
1. 登录 Web 管理界面
|
|
||||||
2. 编辑账号过期时间为昨天
|
|
||||||
3. 保存并刷新
|
|
||||||
4. 调用 API 验证
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 过期时间成功保存
|
|
||||||
- ✅ 界面正确显示
|
|
||||||
- ✅ API 调用时账号被跳过
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. 性能测试
|
|
||||||
|
|
||||||
#### TC-5.1: 大量过期账号性能
|
|
||||||
```yaml
|
|
||||||
场景:
|
|
||||||
- 100 个账号,95 个过期,5 个正常
|
|
||||||
|
|
||||||
步骤:
|
|
||||||
1. 并发 100 次 API 调用
|
|
||||||
2. 测量响应时间
|
|
||||||
|
|
||||||
预期:
|
|
||||||
- ✅ 过期检查耗时 <5ms
|
|
||||||
- ✅ 无性能告警
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 测试覆盖率目标
|
|
||||||
|
|
||||||
| 测试类型 | 目标覆盖率 | 优先级 |
|
|
||||||
|---------|-----------|--------|
|
|
||||||
| 功能测试 | 100% | P0 |
|
|
||||||
| 边界测试 | 100% | P0 |
|
|
||||||
| 回归测试 | 100% | P0 |
|
|
||||||
| 集成测试 | 80% | P1 |
|
|
||||||
| 性能测试 | - | P1 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 修复优先级
|
|
||||||
|
|
||||||
### P0 - 必须修复(阻塞发布)
|
|
||||||
|
|
||||||
1. ❌ **Gemini** - geminiAccountService.js:285
|
|
||||||
2. ❌ **OpenAI** - openaiAccountService.js:917
|
|
||||||
3. ❌ **Droid** - droidAccountService.js:914
|
|
||||||
|
|
||||||
### P1 - 高优先级(建议修复)
|
|
||||||
|
|
||||||
4. ⚠️ **确认其他 5 平台** - CCR, Claude Console, Bedrock, Azure OpenAI, OpenAI-Responses
|
|
||||||
5. 📝 **统一日志格式**
|
|
||||||
|
|
||||||
### P2 - 中优先级(可选)
|
|
||||||
|
|
||||||
6. 🔔 **WebHook 通知** - 账号即将过期提醒
|
|
||||||
7. 🎨 **前端视觉提示** - 高亮即将过期账号
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 修复检查清单
|
|
||||||
|
|
||||||
完成修复后,请逐项确认:
|
|
||||||
|
|
||||||
### 代码修改
|
|
||||||
- [ ] Gemini 添加 `isSubscriptionExpired()` 函数
|
|
||||||
- [ ] Gemini 在 `selectAvailableAccount()` 中调用检查
|
|
||||||
- [ ] OpenAI 添加 `isSubscriptionExpired()` 函数
|
|
||||||
- [ ] OpenAI 在 `selectAvailableAccount()` 中调用检查
|
|
||||||
- [ ] Droid 添加 `_isSubscriptionExpired()` 函数
|
|
||||||
- [ ] Droid 在 `getSchedulableAccounts()` 中调用检查
|
|
||||||
- [ ] 所有检查都记录调试日志
|
|
||||||
|
|
||||||
### 代码质量
|
|
||||||
- [ ] ESLint 检查通过
|
|
||||||
- [ ] Prettier 格式化完成
|
|
||||||
- [ ] 添加必要注释
|
|
||||||
- [ ] 函数命名符合规范
|
|
||||||
|
|
||||||
### 测试验证
|
|
||||||
- [ ] 通过所有功能测试(TC-1.1 ~ 1.4)
|
|
||||||
- [ ] 通过所有边界测试(TC-2.1 ~ 2.3)
|
|
||||||
- [ ] 通过所有回归测试(TC-3.1 ~ 3.3)
|
|
||||||
- [ ] 通过集成测试(TC-4.1 ~ 4.2)
|
|
||||||
- [ ] 性能测试无退化(TC-5.1)
|
|
||||||
|
|
||||||
### 文档更新
|
|
||||||
- [ ] 更新 CLAUDE.md
|
|
||||||
- [ ] 记录 commit 信息
|
|
||||||
- [ ] 更新本报告状态
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 发布建议
|
|
||||||
|
|
||||||
### ⛔ 不建议当前版本发布
|
|
||||||
|
|
||||||
**原因**:
|
|
||||||
1. **功能不完整**: 核心调度逻辑缺失,大部分平台功能无效
|
|
||||||
2. **用户体验差**: 用户设置过期时间但实际不生效
|
|
||||||
3. **潜在风险**: 过期账号继续使用可能导致 API 失败
|
|
||||||
|
|
||||||
### ✅ 发布前必须完成
|
|
||||||
|
|
||||||
1. 修复 P0 级别的 3 个缺陷
|
|
||||||
2. 通过所有 P0 测试用例
|
|
||||||
3. 进行充分回归测试
|
|
||||||
|
|
||||||
### 📅 推荐发布流程
|
|
||||||
|
|
||||||
| 阶段 | 任务 | 工期 |
|
|
||||||
|-----|------|------|
|
|
||||||
| 阶段 1 | 修复代码 + 单元测试 | 1-2 天 |
|
|
||||||
| 阶段 2 | 集成测试 + 回归测试 | 1 天 |
|
|
||||||
| 阶段 3 | 测试环境验证 | 1-2 天 |
|
|
||||||
| 阶段 4 | 生产环境部署 | 0.5 天 |
|
|
||||||
|
|
||||||
**预计完成**: 3-5 个工作日
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 评审信息
|
|
||||||
|
|
||||||
- **评审人员**: Claude Code
|
|
||||||
- **评审日期**: 2025-10-12
|
|
||||||
- **项目名称**: Claude Relay Service
|
|
||||||
- **功能名称**: 账号订阅过期时间管理
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 附录 A: 相关文件清单
|
|
||||||
|
|
||||||
### 前端
|
|
||||||
- `web/admin-spa/src/views/AccountsView.vue` ✅
|
|
||||||
|
|
||||||
### 后端路由
|
|
||||||
- `src/routes/admin.js` ✅
|
|
||||||
|
|
||||||
### 后端服务
|
|
||||||
- `src/services/claudeAccountService.js` ✅ (参考实现)
|
|
||||||
- `src/services/geminiAccountService.js` ❌ 需修复
|
|
||||||
- `src/services/openaiAccountService.js` ❌ 需修复
|
|
||||||
- `src/services/droidAccountService.js` ❌ 需修复
|
|
||||||
- `src/services/claudeConsoleAccountService.js` ✅
|
|
||||||
- `src/services/bedrockAccountService.js` ✅
|
|
||||||
- `src/services/ccrAccountService.js` ✅
|
|
||||||
- `src/services/azureOpenaiAccountService.js` ✅
|
|
||||||
- `src/services/openaiResponsesAccountService.js` ✅
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 附录 B: 技术债务
|
|
||||||
|
|
||||||
发现以下技术债务(不影响本次功能):
|
|
||||||
|
|
||||||
1. **缺少单元测试**: 所有 AccountService 缺少测试
|
|
||||||
2. **代码重复**: 9 个服务逻辑高度相似
|
|
||||||
3. **日志不统一**: 不同服务日志格式差异大
|
|
||||||
4. **错误处理简单**: 部分服务错误处理不完善
|
|
||||||
|
|
||||||
**建议**: 后续迭代逐步改进
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**报告结束**
|
|
||||||
Reference in New Issue
Block a user