Files
backend/.qoder/repowiki/zh/content/API参考/档案API/激活管理.md
lan a4b6c5011e
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
chore(git): 更新.gitignore以忽略新的本地文件
2025-11-30 08:33:17 +08:00

288 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 激活管理
<cite>
**本文引用的文件**
- [routes.go](file://internal/handler/routes.go)
- [profile_handler.go](file://internal/handler/profile_handler.go)
- [profile_service.go](file://internal/service/profile_service.go)
- [profile_repository.go](file://internal/repository/profile_repository.go)
- [profile.go](file://internal/model/profile.go)
- [response.go](file://internal/model/response.go)
- [manager.go](file://pkg/database/manager.go)
- [postgres.go](file://pkg/database/postgres.go)
- [config.go](file://pkg/config/config.go)
- [main.go](file://cmd/server/main.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
## 简介
本文件聚焦于“档案激活管理API”的设计与实现围绕 POST /api/v1/profile/:uuid/activate 端点展开,解释“活跃档案”的概念及其在系统中的作用:代表用户当前在游戏中使用的角色外观。调用该接口后,系统通过数据库事务确保原子性:先将用户所有其他档案的 is_active 字段设为 false再将指定 UUID 的档案设为 active 状态;同时,该操作会更新档案的 last_used_at 时间戳,用于追踪最近使用情况。本文还提供调用示例、成功/失败响应说明,并结合 repository 层的 SetActiveProfile 事务实现,说明数据一致性保障机制。
## 项目结构
该模块位于典型的分层架构中:
- 路由层:定义 API 路由与鉴权中间件绑定
- 处理器层:接收请求、解析参数、调用服务层并输出响应
- 服务层:编排业务流程,进行权限校验与调用仓库层
- 仓库层:封装数据库操作,提供事务与字段更新能力
- 模型层:定义数据结构与表映射
- 数据库层GORM 初始化、连接池与迁移
```mermaid
graph TB
subgraph "路由层"
R["internal/handler/routes.go"]
end
subgraph "处理器层"
H["internal/handler/profile_handler.go"]
end
subgraph "服务层"
S["internal/service/profile_service.go"]
end
subgraph "仓库层"
RP["internal/repository/profile_repository.go"]
end
subgraph "模型层"
M["internal/model/profile.go"]
end
subgraph "数据库层"
DM["pkg/database/manager.go"]
DP["pkg/database/postgres.go"]
end
subgraph "应用入口"
MAIN["cmd/server/main.go"]
end
R --> H
H --> S
S --> RP
RP --> DM
DM --> DP
S --> M
```
图表来源
- [routes.go](file://internal/handler/routes.go#L63-L79)
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
- [profile.go](file://internal/model/profile.go#L7-L24)
- [manager.go](file://pkg/database/manager.go#L13-L50)
- [postgres.go](file://pkg/database/postgres.go#L13-L60)
- [main.go](file://cmd/server/main.go#L41-L51)
章节来源
- [routes.go](file://internal/handler/routes.go#L63-L79)
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
- [profile.go](file://internal/model/profile.go#L7-L24)
- [manager.go](file://pkg/database/manager.go#L13-L50)
- [postgres.go](file://pkg/database/postgres.go#L13-L60)
- [main.go](file://cmd/server/main.go#L41-L51)
## 核心组件
- 路由注册:在路由组 /api/v1/profile 下注册 POST /:uuid/activate绑定鉴权中间件
- 处理器从上下文提取用户ID与UUID调用服务层设置活跃档案
- 服务层:校验档案归属、调用仓库层执行事务设置活跃状态,并更新 last_used_at
- 仓库层:使用 GORM 事务,先批量将用户其他档案置为非活跃,再将目标档案置为活跃;随后更新 last_used_at
- 模型层Profile 结构体包含 uuid、user_id、name、is_active、last_used_at 等字段
章节来源
- [routes.go](file://internal/handler/routes.go#L63-L79)
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
- [profile.go](file://internal/model/profile.go#L7-L24)
## 架构总览
下图展示了从客户端到数据库的完整调用链路,以及事务原子性保障。
```mermaid
sequenceDiagram
participant C as "客户端"
participant R as "路由层<br/>routes.go"
participant H as "处理器层<br/>profile_handler.go"
participant S as "服务层<br/>profile_service.go"
participant RP as "仓库层<br/>profile_repository.go"
participant DB as "数据库(GORM)"
C->>R : "POST /api/v1/profile/ : uuid/activate"
R->>H : "绑定鉴权中间件后进入处理器"
H->>H : "从上下文获取 user_id 并校验"
H->>S : "调用 SetActiveProfile(db, uuid, user_id)"
S->>RP : "FindProfileByUUID(uuid)"
RP-->>S : "返回档案或错误"
S->>S : "校验档案归属(user_id)"
S->>RP : "SetActiveProfile(uuid, user_id) 事务"
RP->>DB : "事务开始"
DB-->>RP : "事务上下文"
RP->>DB : "批量更新其他档案为非活跃"
RP->>DB : "更新目标档案为活跃"
RP-->>S : "提交事务"
S->>RP : "UpdateProfileLastUsedAt(uuid)"
RP->>DB : "更新 last_used_at"
RP-->>S : "返回成功"
S-->>H : "返回成功"
H-->>C : "200 OK {message : 设置成功}"
```
图表来源
- [routes.go](file://internal/handler/routes.go#L63-L79)
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
## 详细组件分析
### 活跃档案的概念与作用
- 概念:活跃档案是用户当前在游戏中使用的角色外观,系统通过 is_active 字段标识
- 作用:确保同一用户在同一时刻仅有一个活跃档案,避免多角色外观冲突;同时通过 last_used_at 记录最近使用时间,便于审计与统计
章节来源
- [profile.go](file://internal/model/profile.go#L7-L24)
### 接口定义与调用流程
- 方法与路径POST /api/v1/profile/:uuid/activate
- 鉴权:需携带有效 JWT处理器从上下文提取 user_id
- 参数:
- 路径参数 uuid目标档案的唯一标识
- 成功响应200 OK返回统一成功响应结构
- 失败响应:
- 401 未授权:缺少或无效的 JWT
- 403 禁止访问:档案不属于当前用户
- 404 资源不存在:档案不存在
- 500 服务器错误:其他数据库或业务异常
章节来源
- [routes.go](file://internal/handler/routes.go#L63-L79)
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [response.go](file://internal/model/response.go#L20-L38)
### 处理器层实现要点
- 从上下文获取 user_id若缺失返回 401
- 调用服务层 SetActiveProfile根据错误类型映射为 404 或 403 或 500
- 成功返回 200 OK
章节来源
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [response.go](file://internal/model/response.go#L20-L38)
### 服务层业务逻辑
- 校验档案是否存在与归属关系
- 调用仓库层 SetActiveProfile 执行事务
- 事务完成后调用 UpdateProfileLastUsedAt 更新 last_used_at
章节来源
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
### 仓库层事务实现与数据一致性
- 使用 GORM 事务,确保以下两个更新在同一事务中:
1) 将用户所有档案的 is_active 设为 false
2) 将指定 uuid 的档案 is_active 设为 true
- 提交事务后,调用 UpdateProfileLastUsedAt 将 last_used_at 更新为当前时间
- 该实现保证了“同一时刻仅有一个活跃档案”的强一致性
```mermaid
flowchart TD
Start(["进入 SetActiveProfile 事务"]) --> BatchDeactivate["批量更新其他档案为非活跃"]
BatchDeactivate --> TargetActivate["更新目标档案为活跃"]
TargetActivate --> Commit{"事务提交成功?"}
Commit --> |是| UpdateTimestamp["更新 last_used_at"]
Commit --> |否| Rollback["回滚事务"]
UpdateTimestamp --> End(["返回成功"])
Rollback --> End
```
图表来源
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
章节来源
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
### 数据模型与字段说明
- Profile 模型包含:
- uuid档案唯一标识
- user_id所属用户ID
- name角色名
- is_active是否为活跃档案
- last_used_at最后使用时间
- created_at/updated_at创建与更新时间
章节来源
- [profile.go](file://internal/model/profile.go#L7-L24)
### 数据库连接与迁移
- 应用启动时初始化数据库连接与 GORM 实例,执行 AutoMigrate 自动迁移
- 连接池参数可配置PostgreSQL 驱动通过 DSN 构建
- Profile 表在迁移中创建,包含上述字段及索引
章节来源
- [main.go](file://cmd/server/main.go#L41-L51)
- [manager.go](file://pkg/database/manager.go#L13-L50)
- [postgres.go](file://pkg/database/postgres.go#L13-L60)
- [config.go](file://pkg/config/config.go#L34-L47)
## 依赖关系分析
- 路由层依赖处理器层
- 处理器层依赖服务层
- 服务层依赖仓库层与模型层
- 仓库层依赖数据库管理器与 GORM
- 应用入口负责初始化数据库并执行迁移
```mermaid
graph LR
Routes["routes.go"] --> Handler["profile_handler.go"]
Handler --> Service["profile_service.go"]
Service --> Repo["profile_repository.go"]
Repo --> DBMgr["database/manager.go"]
DBMgr --> PG["database/postgres.go"]
Main["cmd/server/main.go"] --> DBMgr
```
图表来源
- [routes.go](file://internal/handler/routes.go#L63-L79)
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
- [manager.go](file://pkg/database/manager.go#L13-L50)
- [postgres.go](file://pkg/database/postgres.go#L13-L60)
- [main.go](file://cmd/server/main.go#L41-L51)
章节来源
- [routes.go](file://internal/handler/routes.go#L63-L79)
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
- [manager.go](file://pkg/database/manager.go#L13-L50)
- [postgres.go](file://pkg/database/postgres.go#L13-L60)
- [main.go](file://cmd/server/main.go#L41-L51)
## 性能考量
- 事务内两次 UPDATE 操作,均基于 user_id 与 uuid 的过滤条件,建议在 user_id 上建立索引以提升批量更新效率
- last_used_at 更新为单行更新,影响范围小
- 数据库连接池参数可通过配置调整,以平衡并发与资源占用
[本节为通用性能建议,不直接分析具体文件]
## 故障排查指南
- 401 未授权:确认请求头携带有效 JWT且令牌未过期
- 403 禁止访问:目标档案不属于当前用户,检查 uuid 对应的 user_id
- 404 资源不存在uuid 无效或档案已被删除
- 500 服务器错误:数据库异常或服务层内部错误,查看日志定位具体原因
- 事务一致性问题:若出现“多个活跃档案”,检查数据库索引与事务提交逻辑
章节来源
- [profile_handler.go](file://internal/handler/profile_handler.go#L354-L399)
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
## 结论
POST /api/v1/profile/:uuid/activate 端点通过严格的鉴权与事务控制,确保“同一用户仅有一个活跃档案”的数据一致性,并在成功后更新 last_used_at。该设计既满足业务需求又通过数据库层的事务保障了原子性与可靠性。建议在生产环境中关注索引与连接池配置以进一步优化性能与稳定性。