557 lines
16 KiB
Markdown
557 lines
16 KiB
Markdown
|
|
# Sub2API 模块分析报告:用户与API Key管理模块
|
|||
|
|
|
|||
|
|
## 1. 模块概述
|
|||
|
|
|
|||
|
|
### 1.1 模块定位
|
|||
|
|
用户与API Key管理模块是Sub2API系统的用户资源管理核心,负责管理系统中所有用户账户、API Key的创建、分配、权限控制以及用户分组等操作。该模块与认证模块紧密配合,共同构成系统的访问控制体系。
|
|||
|
|
|
|||
|
|
### 1.2 核心职责
|
|||
|
|
- **用户生命周期管理**:用户注册、登录、信息修改、注销
|
|||
|
|
- **API Key管理**:创建、分配、吊销、权限控制
|
|||
|
|
- **用户分组管理**:用户分组、分组权限、组内资源分配
|
|||
|
|
- **配额与限制**:用户级别的配额、并发限制、速率限制
|
|||
|
|
- **用户属性管理**:自定义属性、标签、扩展信息
|
|||
|
|
|
|||
|
|
## 2. 代码结构分析
|
|||
|
|
|
|||
|
|
### 2.1 核心文件
|
|||
|
|
|
|||
|
|
| 文件路径 | 职责 | 代码行数 |
|
|||
|
|
|---------|------|----------|
|
|||
|
|
| `service/user.go` | 用户服务核心逻辑 | ~500行 |
|
|||
|
|
| `service/api_key_service.go` | API Key服务 | ~900行 |
|
|||
|
|
| `service/admin_service.go` | 管理后台用户操作 | ~2000行 |
|
|||
|
|
| `handler/user_handler.go` | 用户相关API处理器 | ~400行 |
|
|||
|
|
| `handler/admin/user_handler.go` | 管理后台用户处理器 | ~400行 |
|
|||
|
|
| `handler/api_key_handler.go` | API Key处理器 | ~300行 |
|
|||
|
|
| `handler/admin/apikey_handler.go` | 管理后台API Key处理器 | ~200行 |
|
|||
|
|
| `repository/user_repo.go` | 用户数据访问层 | ~600行 |
|
|||
|
|
| `repository/api_key_repo.go` | API Key数据访问层 | ~400行 |
|
|||
|
|
|
|||
|
|
### 2.2 数据模型
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 用户实体 - ent/schema/user.go
|
|||
|
|
type User struct {
|
|||
|
|
ID int64
|
|||
|
|
Email string // 邮箱(唯一)
|
|||
|
|
PasswordHash string // 密码哈希
|
|||
|
|
Name string // 显示名称
|
|||
|
|
Avatar string // 头像URL
|
|||
|
|
Status string // 用户状态:active/disabled
|
|||
|
|
Balance float64 // 账户余额
|
|||
|
|
Concurrency int // 并发数限制
|
|||
|
|
RateMultiplier float64 // 计费倍率
|
|||
|
|
TOTPEnabled bool // 是否启用双因素认证
|
|||
|
|
TOTPSecret string // TOTP密钥(加密存储)
|
|||
|
|
LastLoginAt *time.Time // 最后登录时间
|
|||
|
|
CreatedAt time.Time
|
|||
|
|
UpdatedAt time.Time
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// API Key实体 - ent/schema/apikey.go
|
|||
|
|
type APIKey struct {
|
|||
|
|
ID int64
|
|||
|
|
Key string // Key值(sk-开头)
|
|||
|
|
Name string // 名称
|
|||
|
|
UserID int64 // 所属用户
|
|||
|
|
GroupID *int64 // 绑定分组
|
|||
|
|
Quota float64 // 配额(0为无限制)
|
|||
|
|
QuotaUsed float64 // 已使用配额
|
|||
|
|
Status string // 状态:active/disabled/quota_exhausted/expired
|
|||
|
|
RateLimit5h float64 // 5小时速率限制
|
|||
|
|
RateLimit1d float64 // 1天速率限制
|
|||
|
|
RateLimit7d float64 // 7天速率限制
|
|||
|
|
ExpiresAt *time.Time // 过期时间
|
|||
|
|
IPWhitelist string // IP白名单(JSON数组)
|
|||
|
|
LastUsedAt *time.Time // 最后使用时间
|
|||
|
|
CreatedAt time.Time
|
|||
|
|
UpdatedAt time.Time
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. 功能详细分析
|
|||
|
|
|
|||
|
|
### 3.1 用户注册与登录
|
|||
|
|
|
|||
|
|
#### 3.1.1 用户注册流程
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// service/auth_service.go - Register
|
|||
|
|
func (s *AuthService) Register(ctx context.Context, req RegisterRequest) (*User, error) {
|
|||
|
|
// 1. 验证邮箱格式
|
|||
|
|
if !isValidEmail(req.Email) {
|
|||
|
|
return nil, ErrInvalidEmail
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 检查邮箱是否已存在
|
|||
|
|
if exists, _ := s.userRepo.ExistsByEmail(ctx, req.Email); exists {
|
|||
|
|
return nil, ErrEmailExists
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 密码强度验证
|
|||
|
|
if !isStrongPassword(req.Password) {
|
|||
|
|
return nil, ErrWeakPassword
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4. 密码哈希
|
|||
|
|
passwordHash, _ := bcrypt.GenerateFromPassword([]byte(req.Password), 12)
|
|||
|
|
|
|||
|
|
// 5. 创建用户
|
|||
|
|
user := &User{
|
|||
|
|
Email: req.Email,
|
|||
|
|
PasswordHash: string(passwordHash),
|
|||
|
|
Name: req.Name,
|
|||
|
|
Status: StatusActive,
|
|||
|
|
Balance: 0,
|
|||
|
|
Concurrency: 5, // 默认并发限制
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.userRepo.Create(ctx, user)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.1.2 用户登录流程
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// service/auth_service.go - Login
|
|||
|
|
func (s *AuthService) Login(ctx context.Context, email, password string) (*LoginResponse, error) {
|
|||
|
|
// 1. 获取用户
|
|||
|
|
user, err := s.userRepo.GetByEmail(ctx, email)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, ErrInvalidCredentials
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 验证密码
|
|||
|
|
if !bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)) {
|
|||
|
|
// 记录登录失败
|
|||
|
|
s.recordLoginFailure(ctx, user.ID)
|
|||
|
|
return nil, ErrInvalidCredentials
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 检查用户状态
|
|||
|
|
if user.Status != StatusActive {
|
|||
|
|
return nil, ErrUserDisabled
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4. 如果启用了TOTP,验证TOTP码
|
|||
|
|
if user.TOTPEnabled {
|
|||
|
|
// 返回需要TOTP验证的标记
|
|||
|
|
return &LoginResponse{
|
|||
|
|
RequireTOTP: true,
|
|||
|
|
UserID: user.ID,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 5. 生成JWT Token
|
|||
|
|
token, err := s.generateJWT(user)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 6. 更新最后登录时间
|
|||
|
|
s.userRepo.UpdateLastLogin(ctx, user.ID)
|
|||
|
|
|
|||
|
|
return &LoginResponse{
|
|||
|
|
Token: token,
|
|||
|
|
User: user,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 API Key管理
|
|||
|
|
|
|||
|
|
#### 3.2.1 API Key创建
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// service/api_key_service.go - Create
|
|||
|
|
func (s *APIKeyService) Create(ctx context.Context, userID int64, req CreateAPIKeyRequest) (*APIKey, error) {
|
|||
|
|
// 1. 生成随机Key
|
|||
|
|
key := "sk-" + generateRandomKey(32)
|
|||
|
|
|
|||
|
|
// 2. 检查用户API Key数量限制
|
|||
|
|
count, _ := s.apiKeyRepo.CountByUser(ctx, userID)
|
|||
|
|
if count >= maxAPIKeysPerUser {
|
|||
|
|
return nil, ErrTooManyAPIKeys
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 创建API Key
|
|||
|
|
apiKey := &APIKey{
|
|||
|
|
Key: key,
|
|||
|
|
Name: req.Name,
|
|||
|
|
UserID: userID,
|
|||
|
|
GroupID: req.GroupID,
|
|||
|
|
Quota: req.Quota,
|
|||
|
|
Status: StatusActive,
|
|||
|
|
RateLimit5h: req.RateLimit5h,
|
|||
|
|
RateLimit1d: req.RateLimit1d,
|
|||
|
|
RateLimit7d: req.RateLimit7d,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4. 处理IP白名单
|
|||
|
|
if len(req.IPWhitelist) > 0 {
|
|||
|
|
apiKey.IPWhitelist = json.Marshal(req.IPWhitelist)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 5. 保存到数据库
|
|||
|
|
created, err := s.apiKeyRepo.Create(ctx, apiKey)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 6. 返回时只显示一次Key
|
|||
|
|
return created, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.2 API Key验证
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// service/api_key_service.go - ValidateKey
|
|||
|
|
func (s *APIKeyService) ValidateKey(ctx context.Context, key string) (*APIKey, *User, error) {
|
|||
|
|
// 1. 缓存查询
|
|||
|
|
if cached := s.getCache(key); cached != nil {
|
|||
|
|
return cached.APIKey, cached.User, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 数据库查询
|
|||
|
|
apiKey, err := s.apiKeyRepo.GetByKey(ctx, key)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, nil, ErrInvalidKey
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 验证状态
|
|||
|
|
if apiKey.Status == StatusDisabled {
|
|||
|
|
return nil, nil, ErrKeyDisabled
|
|||
|
|
}
|
|||
|
|
if apiKey.Status == StatusQuotaExhausted {
|
|||
|
|
return nil, nil, ErrQuotaExhausted
|
|||
|
|
}
|
|||
|
|
if apiKey.Status == StatusExpired || (apiKey.ExpiresAt != nil && time.Now().After(*apiKey.ExpiresAt)) {
|
|||
|
|
return nil, nil, ErrKeyExpired
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4. 验证配额
|
|||
|
|
if apiKey.Quota > 0 && apiKey.QuotaUsed >= apiKey.Quota {
|
|||
|
|
return nil, nil, ErrQuotaExhausted
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 5. 获取用户信息
|
|||
|
|
user, err := s.userRepo.GetByID(ctx, apiKey.UserID)
|
|||
|
|
if err != nil || user.Status != StatusActive {
|
|||
|
|
return nil, nil, ErrUserDisabled
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 6. 缓存结果
|
|||
|
|
s.setCache(key, apiKey, user)
|
|||
|
|
|
|||
|
|
return apiKey, user, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 用户分组管理
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 用户分组 - 用于资源隔离和配额控制
|
|||
|
|
type Group struct {
|
|||
|
|
ID int64
|
|||
|
|
Name string
|
|||
|
|
Platform string // 所属平台:anthropic/openai/gemini等
|
|||
|
|
Status string // active/disabled
|
|||
|
|
RateMultiplier float64 // 计费倍率
|
|||
|
|
MaxConcurrency int // 最大并发数
|
|||
|
|
MaxSessions int // 最大会话数
|
|||
|
|
MaxRPM int // 每分钟最大请求数
|
|||
|
|
Models []string // 允许的模型列表
|
|||
|
|
IsExclusive bool // 是否独占(只能被一个用户使用)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.4 用户配额与限制
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 用户级别限制配置
|
|||
|
|
type UserQuota struct {
|
|||
|
|
Balance float64 // 账户余额
|
|||
|
|
Concurrency int // 最大并发数
|
|||
|
|
RateMultiplier float64 // 计费倍率(默认1.0)
|
|||
|
|
MonthlyQuota float64 // 月度配额
|
|||
|
|
DailyLimit float64 // 每日限制
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用量检查
|
|||
|
|
func (s *UserService) CheckQuota(ctx context.Context, userID int64, cost float64) error {
|
|||
|
|
user, err := s.userRepo.GetByID(ctx, userID)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查余额
|
|||
|
|
if user.Balance > 0 && user.Balance < cost {
|
|||
|
|
return ErrInsufficientBalance
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查并发限制
|
|||
|
|
activeConns := s.getActiveConnections(userID)
|
|||
|
|
if activeConns >= user.Concurrency {
|
|||
|
|
return ErrConcurrencyExceeded
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.5 用户属性管理
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 自定义用户属性 - 支持扩展字段
|
|||
|
|
type UserAttribute struct {
|
|||
|
|
ID int64
|
|||
|
|
Name string // 属性名
|
|||
|
|
Type string // 类型:string/number/boolean
|
|||
|
|
Required bool // 是否必填
|
|||
|
|
Default string // 默认值
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 用户属性值
|
|||
|
|
type UserAttributeValue struct {
|
|||
|
|
UserID int64
|
|||
|
|
AttributeID int64
|
|||
|
|
Value string
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. 权限控制
|
|||
|
|
|
|||
|
|
### 4.1 角色权限
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
const (
|
|||
|
|
RoleUser = "user" // 普通用户
|
|||
|
|
RoleAdmin = "admin" // 管理员
|
|||
|
|
RoleSuperAdmin = "super_admin" // 超级管理员
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 权限检查
|
|||
|
|
func (s *UserService) CheckPermission(userID int64, action string) bool {
|
|||
|
|
user, _ := s.userRepo.GetByID(ctx, userID)
|
|||
|
|
|
|||
|
|
switch action {
|
|||
|
|
case "user:read":
|
|||
|
|
return true
|
|||
|
|
case "user:write":
|
|||
|
|
return user.Role == RoleAdmin || user.Role == RoleSuperAdmin
|
|||
|
|
case "admin:*":
|
|||
|
|
return user.Role == RoleSuperAdmin
|
|||
|
|
default:
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.2 API Key权限继承
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// API Key继承用户的权限和配额
|
|||
|
|
type APIKeyPermission struct {
|
|||
|
|
UserID int64
|
|||
|
|
GroupID *int64
|
|||
|
|
Quota float64 // 继承用户的配额
|
|||
|
|
RateLimit float64 // 继承用户的速率限制
|
|||
|
|
Models []string // 继承分组的模型限制
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 5. 数据访问层
|
|||
|
|
|
|||
|
|
### 5.1 用户Repository
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// repository/user_repo.go
|
|||
|
|
type UserRepository interface {
|
|||
|
|
Create(ctx context.Context, user *User) (*User, error)
|
|||
|
|
GetByID(ctx context.Context, id int64) (*User, error)
|
|||
|
|
GetByEmail(ctx context.Context, email string) (*User, error)
|
|||
|
|
Update(ctx context.Context, user *User) error
|
|||
|
|
Delete(ctx context.Context, id int64) error
|
|||
|
|
ExistsByEmail(ctx context.Context, email string) (bool, error)
|
|||
|
|
List(ctx context.Context, params PaginationParams, filters UserFilters) ([]User, int64, error)
|
|||
|
|
UpdateBalance(ctx context.Context, userID int64, amount float64) error
|
|||
|
|
UpdateConcurrency(ctx context.Context, userID int64, concurrency int) error
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 API Key Repository
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// repository/api_key_repo.go
|
|||
|
|
type APIKeyRepository interface {
|
|||
|
|
Create(ctx context.Context, key *APIKey) (*APIKey, error)
|
|||
|
|
GetByID(ctx context.Context, id int64) (*APIKey, error)
|
|||
|
|
GetByKey(ctx context.Context, key string) (*APIKey, error)
|
|||
|
|
GetByUserID(ctx context.Context, userID int64) ([]APIKey, error)
|
|||
|
|
Update(ctx context.Context, key *APIKey) error
|
|||
|
|
Delete(ctx context.Context, id int64) error
|
|||
|
|
CountByUser(ctx context.Context, userID int64) (int, error)
|
|||
|
|
List(ctx context.Context, params PaginationParams, filters APIKeyFilters) ([]APIKey, int64, error)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6. 配置参数
|
|||
|
|
|
|||
|
|
### 6.1 用户配置(config.yaml)
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
user:
|
|||
|
|
# 注册配置
|
|||
|
|
registration:
|
|||
|
|
enabled: true # 允许注册
|
|||
|
|
email_verification: false # 邮箱验证
|
|||
|
|
password_min_length: 8
|
|||
|
|
password_require_complexity: true
|
|||
|
|
|
|||
|
|
# 登录配置
|
|||
|
|
login:
|
|||
|
|
max_attempts: 5 # 最大登录尝试
|
|||
|
|
lockout_duration: 15m # 锁定时长
|
|||
|
|
session_timeout: 24h # 会话超时
|
|||
|
|
|
|||
|
|
# 用户限制
|
|||
|
|
limits:
|
|||
|
|
max_api_keys_per_user: 50 # 每用户最大API Key数
|
|||
|
|
default_concurrency: 5 # 默认并发数
|
|||
|
|
default_balance: 0 # 默认余额
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6.2 API Key配置
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
api_key:
|
|||
|
|
# 默认速率限制
|
|||
|
|
default_rate_limits:
|
|||
|
|
rate_limit_5h: 100000
|
|||
|
|
rate_limit_1d: 500000
|
|||
|
|
rate_limit_7d: 3500000
|
|||
|
|
|
|||
|
|
# 缓存配置
|
|||
|
|
cache:
|
|||
|
|
enabled: true
|
|||
|
|
l1_size: 10000
|
|||
|
|
l1_ttl: 1m
|
|||
|
|
l2_ttl: 5m
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 7. 修改和扩展指南
|
|||
|
|
|
|||
|
|
### 7.1 常见修改场景
|
|||
|
|
|
|||
|
|
**场景1:调整用户并发限制**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// service/user.go - UpdateConcurrency
|
|||
|
|
func (s *UserService) UpdateConcurrency(ctx context.Context, userID int64, concurrency int) error {
|
|||
|
|
// 验证限制范围
|
|||
|
|
if concurrency < 1 || concurrency > 100 {
|
|||
|
|
return ErrInvalidConcurrency
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
user, _ := s.userRepo.GetByID(ctx, userID)
|
|||
|
|
user.Concurrency = concurrency
|
|||
|
|
|
|||
|
|
return s.userRepo.Update(ctx, user)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**场景2:添加用户属性**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 1. 在 ent/schema/user.go 添加字段
|
|||
|
|
func (User) Fields() []ent.Field {
|
|||
|
|
return []ent.Field{
|
|||
|
|
field.String("custom_field").Optional(),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 在 handler 中添加访问接口
|
|||
|
|
router.PUT("/users/:id/custom-field", updateCustomField)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**场景3:修改API Key配额逻辑**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// service/api_key_service.go - CheckQuota
|
|||
|
|
func (s *APIKeyService) CheckQuota(ctx context.Context, key *APIKey, cost float64) error {
|
|||
|
|
// 修改配额检查逻辑
|
|||
|
|
if key.Quota > 0 {
|
|||
|
|
// 改为允许一定比例的超支
|
|||
|
|
allowedOverdraft := key.Quota * 0.1 // 10%超支额度
|
|||
|
|
if key.QuotaUsed + cost > key.Quota + allowedOverdraft {
|
|||
|
|
return ErrQuotaExhausted
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 7.2 注意事项
|
|||
|
|
|
|||
|
|
1. **安全性**:用户密码必须使用强哈希存储
|
|||
|
|
2. **数据一致性**:API Key与用户关系需要级联处理
|
|||
|
|
3. **性能**:用户列表查询需要分页和索引优化
|
|||
|
|
|
|||
|
|
## 8. 测试覆盖
|
|||
|
|
|
|||
|
|
### 8.1 单元测试
|
|||
|
|
|
|||
|
|
| 测试文件 | 覆盖范围 |
|
|||
|
|
|----------|----------|
|
|||
|
|
| `auth_service_register_test.go` | 用户注册逻辑 |
|
|||
|
|
| `api_key_service_test.go` | API Key CRUD |
|
|||
|
|
| `user_service_test.go` | 用户管理逻辑 |
|
|||
|
|
|
|||
|
|
### 8.2 集成测试
|
|||
|
|
|
|||
|
|
| 测试文件 | 场景 |
|
|||
|
|
|----------|------|
|
|||
|
|
| `e2e_user_flow_test.go` | 完整用户使用流程 |
|
|||
|
|
|
|||
|
|
## 9. 监控与运维
|
|||
|
|
|
|||
|
|
### 9.1 关键指标
|
|||
|
|
|
|||
|
|
| 指标 | 告警阈值 | 说明 |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| `user_register_count` | - | 用户注册数 |
|
|||
|
|
| `user_login_failures` | > 10/min | 登录失败数 |
|
|||
|
|
| `api_key_count` | - | API Key总数 |
|
|||
|
|
| `api_key_quota_exhausted` | > 20% | 配额耗尽比例 |
|
|||
|
|
|
|||
|
|
### 9.2 运维任务
|
|||
|
|
|
|||
|
|
| 任务 | 频率 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 清理无效用户 | 每月 | 清理长期未登录用户 |
|
|||
|
|
| 检查API Key | 每周 | 检查过期Key并通知 |
|
|||
|
|
| 用户数据分析 | 每周 | 用户活跃度分析 |
|
|||
|
|
|
|||
|
|
## 10. 总结
|
|||
|
|
|
|||
|
|
用户与API Key管理模块特点:
|
|||
|
|
|
|||
|
|
- **完整的用户生命周期**:从注册到注销的完整管理
|
|||
|
|
- **灵活的权限控制**:基于角色和分组的权限体系
|
|||
|
|
- **API Key安全**:支持IP白名单、速率限制、配额控制
|
|||
|
|
- **双因素认证**:支持TOTP增强安全性
|
|||
|
|
|
|||
|
|
**潜在改进点:**
|
|||
|
|
1. 激活码和API Key目前没有包含系统标识,存在跨实例使用风险
|
|||
|
|
2. 用户分组权限控制可以更细粒度
|
|||
|
|
|
|||
|
|
**修改建议:**
|
|||
|
|
- 如需解决跨实例使用问题,可在Key生成时嵌入实例ID
|
|||
|
|
- 权限修改需要谨慎测试,避免影响现有功能
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
*文档版本:1.0*
|
|||
|
|
*最后更新:2025-01*
|
|||
|
|
*分析基于:Sub2API v0.1.104*
|