- Remove old review reports (keep latest only) - Move docs/ to deploy/docs-backup/ - Move performance-testing/ to deploy/ - Clean up test output files - Organize root directory
453 lines
12 KiB
Markdown
453 lines
12 KiB
Markdown
# Sub2API 模块分析报告:账户管理模块
|
||
|
||
## 1. 模块概述
|
||
|
||
### 1.1 模块定位
|
||
账户管理模块是 Sub2API 的上游资源管理核心,负责管理连接到 AI 服务提供商的账号(Account)。这些账号是系统转发请求的"上游凭证",包括 OAuth 授权账号、API Key 账号等。
|
||
|
||
### 1.2 核心职责
|
||
- **账号 CRUD**:创建、读取、更新、删除上游账号
|
||
- **账号状态管理**:监控账号健康、处理限流和过期
|
||
- **分组管理**:将账号分组以实现资源隔离和配额控制
|
||
- **账号测试**:验证账号有效性(TestConnection)
|
||
|
||
## 2. 代码结构分析
|
||
|
||
### 2.1 核心文件
|
||
|
||
| 文件路径 | 职责 | 代码行数 |
|
||
|---------|------|----------|
|
||
| `service/account.go` | 账号实体定义和服务基础 | ~800 行 |
|
||
| `service/account_service.go` | 账号管理核心逻辑 | ~1200 行 |
|
||
| `service/account_group.go` | 分组管理服务 | ~500 行 |
|
||
| `service/account_usage_service.go` | 账号用量监控 | ~400 行 |
|
||
| `service/account_expiry_service.go` | 账号过期管理 | ~300 行 |
|
||
| `handler/admin/account_handler.go` | 账号管理 API | ~1900 行 |
|
||
| `repository/account_repo.go` | 账号数据访问层 | ~1800 行 |
|
||
|
||
### 2.2 数据模型
|
||
|
||
```go
|
||
// ent/schema/account.go
|
||
type Account struct {
|
||
ID int64 // 主键
|
||
Platform string // 平台:anthropic/openai/gemini/antigravity/bedrock
|
||
Type string // 类型:oauth/apikey
|
||
Name string // 账号名称(显示用)
|
||
Credentials string // 加密凭证(JSON)
|
||
Extra string // 额外配置(JSON)
|
||
Status string // 状态:active/error/disabled/expired
|
||
GroupID int64 // 所属分组
|
||
RateMultiplier float64 // 计费倍率
|
||
MaxConcurrency int // 最大并发
|
||
BaseURL string // 自定义上游地址
|
||
CreatedAt time.Time
|
||
UpdatedAt time.Time
|
||
}
|
||
```
|
||
|
||
## 3. 功能详细分析
|
||
|
||
### 3.1 账号类型支持
|
||
|
||
| 平台 | 类型 | 认证方式 | 特性 |
|
||
|------|------|----------|------|
|
||
| **Anthropic** | OAuth / API Key | Bearer Token | Claude 模型支持 |
|
||
| **OpenAI** | API Key | Bearer Token | ChatGPT、GPT 模型 |
|
||
| **Google Gemini** | API Key | Bearer Token | 多模态支持 |
|
||
| **Antigravity** | OAuth | 独立认证系统 | 独立配额 |
|
||
| **AWS Bedrock** | API Key | AWS 签名 v4 | Claude on Bedrock |
|
||
|
||
### 3.2 账号创建流程
|
||
|
||
```go
|
||
// service/account_service.go - CreateAccount
|
||
func (s *AccountService) CreateAccount(ctx context.Context, req CreateAccountRequest) (*Account, error) {
|
||
// 1. 验证请求参数
|
||
if err := validateAccountRequest(req); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 2. 加密凭证
|
||
encryptedCreds, err := s.encryptCredentials(req.Credentials)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 3. 创建账号
|
||
account := &Account{
|
||
Platform: req.Platform,
|
||
Type: req.Type,
|
||
Name: req.Name,
|
||
Credentials: encryptedCreds,
|
||
GroupID: req.GroupID,
|
||
Status: StatusActive,
|
||
}
|
||
|
||
// 4. 保存到数据库
|
||
return s.accountRepo.Create(ctx, account)
|
||
}
|
||
```
|
||
|
||
### 3.3 账号验证 (TestConnection)
|
||
|
||
```go
|
||
// service/account_test_service.go - TestAccount
|
||
func (s *AccountTestService) TestAccount(ctx context.Context, account *Account) (*TestResult, error) {
|
||
// 1. 构建测试请求
|
||
testReq := buildTestRequest(account)
|
||
|
||
// 2. 发送请求
|
||
resp, err := s.sendRequest(ctx, account, testReq)
|
||
if err != nil {
|
||
return &TestResult{
|
||
Success: false,
|
||
Error: err.Error(),
|
||
}, nil
|
||
}
|
||
|
||
// 3. 检查响应
|
||
if !isSuccessResponse(resp) {
|
||
return &TestResult{
|
||
Success: false,
|
||
StatusCode: resp.StatusCode,
|
||
Error: parseError(resp.Body),
|
||
}, nil
|
||
}
|
||
|
||
// 4. 解析响应(获取配额信息)
|
||
quota := parseQuotaInfo(resp.Body)
|
||
return &TestResult{
|
||
Success: true,
|
||
QuotaInfo: quota,
|
||
StatusCode: 200,
|
||
}, nil
|
||
}
|
||
```
|
||
|
||
**验证端点:**
|
||
| 平台 | 测试端点 | 验证内容 |
|
||
|------|----------|----------|
|
||
| Anthropic | `/v1/messages` | 基本连通性 + 配额 |
|
||
| OpenAI | `/v1/models` | 模型列表 |
|
||
| Gemini | `/v1/models` | 模型列表 |
|
||
| Antigravity | `/v1/models` | 模型列表 |
|
||
| Bedrock | `/invocations` | 基本连通性 |
|
||
|
||
### 3.4 账号状态管理
|
||
|
||
```go
|
||
// 账号状态转换
|
||
const (
|
||
StatusActive = "active" // 正常可用
|
||
StatusError = "error" // 出错(临时)
|
||
StatusDisabled = "disabled" // 管理员禁用
|
||
StatusExpired = "expired" // 已过期
|
||
StatusRateLimited = "rate_limited" // 被限流
|
||
)
|
||
|
||
// 状态检查
|
||
func (a *Account) CanUse() bool {
|
||
return a.Status == StatusActive || a.Status == StatusRateLimited
|
||
}
|
||
```
|
||
|
||
**状态变更触发:**
|
||
- **Active → Error**:TestConnection 失败、请求返回 5xx
|
||
- **Active → RateLimited**:上游返回 429
|
||
- **Active → Expired**:检测到过期时间
|
||
- **Any → Disabled**:管理员手动操作
|
||
|
||
### 3.5 账号分组管理
|
||
|
||
```go
|
||
// service/account_group.go
|
||
type Group struct {
|
||
ID int64
|
||
Name string
|
||
Platform string // 平台类型
|
||
Status string
|
||
RateMultiplier float64 // 计费倍率
|
||
MaxConcurrency int // 分组最大并发
|
||
Models []string // 允许的模型列表
|
||
IsExclusive bool // 独占模式(只能被单个用户使用)
|
||
}
|
||
```
|
||
|
||
**分组特性:**
|
||
- 按平台隔离:不同平台使用不同分组
|
||
- 模型限制:分组可限定可用模型
|
||
- 独占模式:某些分组只允许单个用户访问
|
||
- 计费倍率:不同分组可有不同计费策略
|
||
|
||
### 3.6 账号用量追踪
|
||
|
||
```go
|
||
// service/account_usage_service.go
|
||
func (s *AccountUsageService) RecordUsage(ctx context.Context, accountID int64, tokens int, cost float64) error {
|
||
// 1. 更新内存统计
|
||
s.localStats.Add(accountID, tokens, cost)
|
||
|
||
// 2. 更新 Redis 统计
|
||
s.redisStats.Incr(accountID, tokens, cost)
|
||
|
||
// 3. 定期同步到数据库
|
||
if s.shouldSync(accountID) {
|
||
s.syncToDB(accountID)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 获取当前负载
|
||
func (s *AccountUsageService) GetLoadFactor(accountID int64) float64 {
|
||
activeConns := s.GetActiveConnections(accountID)
|
||
maxConns := s.GetMaxConcurrency(accountID)
|
||
return float64(activeConns) / float64(maxConns)
|
||
}
|
||
```
|
||
|
||
## 4. 高级功能
|
||
|
||
### 4.1 账号预热
|
||
|
||
```go
|
||
// service/account_intercept_warmup.go
|
||
// 新账号首次使用前进行预热请求
|
||
func WarmupAccount(account *Account) error {
|
||
// 发送轻量级请求建立连接
|
||
req := &Request{
|
||
Model: "claude-3-haiku-20240307", // 最小的模型
|
||
MaxTokens: 1,
|
||
}
|
||
|
||
resp, err := sendRequest(account, req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 预热后更新状态
|
||
account.Status = StatusActive
|
||
return nil
|
||
}
|
||
```
|
||
|
||
### 4.2 账号健康检查
|
||
|
||
```go
|
||
// service/account_health_check.go
|
||
func (s *HealthCheckService) CheckAccount(accountID int64) {
|
||
account := s.getAccount(accountID)
|
||
|
||
// 检查最后活跃时间
|
||
if time.Since(account.LastUsedAt) > 24*time.Hour {
|
||
// 超过 24 小时未使用,发送探测请求
|
||
s.testAccount(account)
|
||
}
|
||
|
||
// 检查过期时间
|
||
if account.ExpiresAt != nil && time.Now().After(*account.ExpiresAt) {
|
||
s.updateStatus(accountID, StatusExpired)
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.3 账号配额重置
|
||
|
||
```go
|
||
// service/account_quota_reset.go
|
||
func (s *QuotaResetService) ResetDailyQuota() {
|
||
// 每天 UTC 0 点重置
|
||
for _, account := range s.getAllAccounts() {
|
||
account.UsageToday = 0
|
||
account.UpdatedAt = time.Now()
|
||
s.accountRepo.Update(account)
|
||
}
|
||
}
|
||
```
|
||
|
||
## 5. 数据访问层
|
||
|
||
### 5.1 Repository 接口
|
||
|
||
```go
|
||
// repository/account_repo.go
|
||
type AccountRepository interface {
|
||
Create(ctx context.Context, account *Account) (*Account, error)
|
||
GetByID(ctx context.Context, id int64) (*Account, error)
|
||
GetByGroupID(ctx context.Context, groupID int64) ([]*Account, error)
|
||
Update(ctx context.Context, account *Account) error
|
||
Delete(ctx context.Context, id int64) error
|
||
|
||
// 高级查询
|
||
GetAvailableAccounts(ctx context.Context, groupID int64) ([]*Account, error)
|
||
Search(ctx context.Context, filters AccountFilters) ([]*Account, error)
|
||
}
|
||
```
|
||
|
||
### 5.2 查询优化
|
||
|
||
```go
|
||
// 常用查询优化
|
||
func (r *accountRepository) GetAvailableAccounts(ctx context.Context, groupID int64) ([]*Account, error) {
|
||
return r.client.Account.Query().
|
||
Where(
|
||
account.GroupID(groupID),
|
||
account.StatusEQ("active"),
|
||
account.DeletedAtIsNil(),
|
||
).
|
||
All(ctx)
|
||
}
|
||
```
|
||
|
||
## 6. 配置参数
|
||
|
||
### 6.1 账号配置(config.yaml)
|
||
|
||
```yaml
|
||
account:
|
||
# 账号健康检查
|
||
health_check:
|
||
enabled: true
|
||
interval: 5m
|
||
timeout: 30s
|
||
|
||
# 账号预热
|
||
warmup:
|
||
enabled: true
|
||
model: "claude-3-haiku-20240307"
|
||
|
||
# 配额重置
|
||
quota_reset:
|
||
timezone: "UTC"
|
||
hour: 0
|
||
|
||
# 限流配置
|
||
rate_limit:
|
||
window: 60s
|
||
max_errors: 3
|
||
reset_after: 300s
|
||
```
|
||
|
||
### 6.2 环境变量
|
||
|
||
| 变量 | 说明 | 默认值 |
|
||
|------|------|--------|
|
||
| `ACCOUNT_HEALTH_CHECK_ENABLED` | 启用健康检查 | true |
|
||
| `ACCOUNT_WARMUP_ENABLED` | 启用预热 | true |
|
||
| `ACCOUNT_MAX_CONCURRENCY` | 默认最大并发 | 10 |
|
||
|
||
## 7. 修改和扩展指南
|
||
|
||
### 7.1 常见修改场景
|
||
|
||
**场景 1:添加新的账号类型**
|
||
|
||
```go
|
||
// 1. 在 domain/constants.go 添加类型
|
||
const AccountTypeCustom = "custom"
|
||
|
||
// 2. 在 service/account.go 添加验证
|
||
func (a *Account) ValidateCustom() error {
|
||
// 自定义验证逻辑
|
||
}
|
||
|
||
// 3. 在 handler 中添加创建接口
|
||
router.POST("/accounts/custom", createCustomAccount)
|
||
```
|
||
|
||
**场景 2:调整账号选择策略**
|
||
|
||
```go
|
||
// service/account_service.go - SelectAccount
|
||
func (s *AccountService) SelectAccount(ctx context.Context, groupID int64, model string) (*Account, error) {
|
||
// 调整选择逻辑
|
||
accounts := s.getAvailableAccounts(groupID)
|
||
|
||
// 按负载排序
|
||
sort.Slice(accounts, func(i, j int) bool {
|
||
return accounts[i].LoadFactor < accounts[j].LoadFactor
|
||
})
|
||
|
||
return accounts[0], nil
|
||
}
|
||
```
|
||
|
||
**场景 3:修改限流行为**
|
||
|
||
```go
|
||
// service/account_service.go - HandleRateLimit
|
||
func (s *AccountService) HandleRateLimit(accountID int64, resetAfter time.Duration) error {
|
||
// 修改限流持续时间
|
||
account, _ := s.GetByID(accountID)
|
||
account.Status = StatusRateLimited
|
||
account.RateLimitedAt = time.Now()
|
||
account.RateLimitResetAt = time.Now().Add(resetAfter * 2) // 倍增
|
||
|
||
return s.Update(account)
|
||
}
|
||
```
|
||
|
||
### 7.2 注意事项
|
||
|
||
1. **凭证安全**:账号凭证必须加密存储,密钥独立管理
|
||
2. **并发安全**:账号选择需要考虑并发场景
|
||
3. **状态一致性**:账号状态变更需要同步到缓存
|
||
|
||
## 8. 测试覆盖
|
||
|
||
### 8.1 单元测试
|
||
|
||
| 测试文件 | 覆盖范围 |
|
||
|----------|----------|
|
||
| `account_service_test.go` | 账号 CRUD |
|
||
| `account_load_factor_test.go` | 负载计算 |
|
||
| `account_expiry_service_test.go` | 过期处理 |
|
||
| `account_test_service_openai_test.go` | OpenAI 测试 |
|
||
|
||
### 8.2 集成测试
|
||
|
||
| 测试文件 | 场景 |
|
||
|----------|------|
|
||
| `e2e_gateway_test.go` | 账号选择和请求转发 |
|
||
|
||
## 9. 监控与运维
|
||
|
||
### 9.1 关键指标
|
||
|
||
| 指标 | 告警阈值 | 说明 |
|
||
|------|----------|------|
|
||
| `account_error_count` | > 10 | 账号错误数 |
|
||
| `account_rate_limited` | > 20% | 账号限流比例 |
|
||
| `account_expired` | > 5% | 账号过期比例 |
|
||
| `account_test_success_rate` | < 90% | 测试成功率 |
|
||
|
||
### 9.2 运维任务
|
||
|
||
| 任务 | 频率 | 说明 |
|
||
|------|------|------|
|
||
| 健康检查 | 5 分钟 | 检查账号可用性 |
|
||
| 配额重置 | 每天 | 重置每日用量 |
|
||
| 过期检查 | 每天 | 检查账号过期时间 |
|
||
| 统计同步 | 每小时 | 同步用量到数据库 |
|
||
|
||
## 10. 总结
|
||
|
||
账户管理模块特点:
|
||
|
||
- **多平台支持**:统一接口管理多种 AI 服务账号
|
||
- **状态自动化**:自动处理限流、过期等状态
|
||
- **分组隔离**:通过分组实现资源隔离和配额控制
|
||
- **健康监控**:持续的账号健康检查和预热
|
||
|
||
**潜在改进点:**
|
||
1. 账号测试可以更全面(模拟实际请求)
|
||
2. 支持更多账号配置选项(代理、超时等)
|
||
|
||
**修改建议:**
|
||
- 账号类型扩展相对简单,风险较低
|
||
- 状态管理逻辑修改需充分测试
|
||
|
||
---
|
||
*文档版本:1.0*
|
||
*最后更新:2025-01*
|
||
*分析基于:Sub2API v0.1.104* |