chore(git): 更新.gitignore以忽略新的本地文件
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
This commit is contained in:
398
.qoder/repowiki/zh/content/API参考/档案API/创建与列表.md
Normal file
398
.qoder/repowiki/zh/content/API参考/档案API/创建与列表.md
Normal file
@@ -0,0 +1,398 @@
|
||||
# 创建与列表
|
||||
|
||||
<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)
|
||||
- [common.go](file://internal/types/common.go)
|
||||
- [response.go](file://internal/model/response.go)
|
||||
- [texture.go](file://internal/model/texture.go)
|
||||
- [profile_handler_test.go](file://internal/handler/profile_handler_test.go)
|
||||
- [common_test.go](file://internal/types/common_test.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件面向开发者与集成方,系统性梳理“创建与列表”相关接口的实现与使用规范,重点覆盖:
|
||||
- 通过 POST /api/v1/profile/ 创建新档案,请求体中必须包含 1-16 字符的角色名;可选皮肤ID与披风ID。
|
||||
- 系统在创建时自动将该档案设为用户活跃档案,并将该用户其他档案置为非活跃。
|
||||
- 通过 GET /api/v1/profile/ 获取当前用户所有档案列表,响应包含档案UUID、名称、活跃状态、关联材质等信息。
|
||||
- 结合 profile_service.go 中的 CheckProfileLimit 逻辑说明用户档案数量上限(默认5个)的控制机制。
|
||||
|
||||
## 项目结构
|
||||
围绕档案模块的路由、处理器、服务层、仓储层与模型如下所示:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "路由层"
|
||||
R["routes.go<br/>注册 /api/v1/profile/* 路由"]
|
||||
end
|
||||
subgraph "处理器层"
|
||||
H["profile_handler.go<br/>CreateProfile / GetProfiles / SetActiveProfile 等"]
|
||||
end
|
||||
subgraph "服务层"
|
||||
S["profile_service.go<br/>CreateProfile / GetUserProfiles / CheckProfileLimit 等"]
|
||||
end
|
||||
subgraph "仓储层"
|
||||
RP["profile_repository.go<br/>CreateProfile / FindProfilesByUserID / SetActiveProfile 等"]
|
||||
end
|
||||
subgraph "模型与类型"
|
||||
M["profile.go<br/>Profile / ProfileResponse / ProfileTexturesData 等"]
|
||||
T["texture.go<br/>Texture 类型"]
|
||||
C["common.go<br/>CreateProfileRequest / ProfileInfo / UpdateProfileRequest 等"]
|
||||
RESP["response.go<br/>统一响应结构"]
|
||||
end
|
||||
R --> H
|
||||
H --> S
|
||||
S --> RP
|
||||
RP --> M
|
||||
S --> M
|
||||
H --> RESP
|
||||
H --> C
|
||||
M --> T
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L15-L399)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L17-L202)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L13-L118)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L57)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L31)
|
||||
- [common.go](file://internal/types/common.go#L81-L166)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
|
||||
## 核心组件
|
||||
- 路由注册:在路由层为档案模块注册了认证中间件保护的 POST / GET / PUT / DELETE / POST activate 等路径。
|
||||
- 处理器:负责解析请求、鉴权、调用服务层并返回统一响应。
|
||||
- 服务层:封装业务规则,如创建档案时的用户存在性校验、角色名唯一性校验、活跃状态设置、档案数量上限检查等。
|
||||
- 仓储层:封装数据库访问,如创建档案、查询用户档案列表、批量设置活跃状态等。
|
||||
- 模型与类型:定义档案实体、响应结构、请求体结构以及材质类型。
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L15-L399)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L17-L202)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L13-L118)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L57)
|
||||
- [common.go](file://internal/types/common.go#L81-L166)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
## 架构总览
|
||||
下图展示从客户端到数据库的调用链路与关键步骤。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Router as "路由层(routes.go)"
|
||||
participant Handler as "处理器(profile_handler.go)"
|
||||
participant Service as "服务层(profile_service.go)"
|
||||
participant Repo as "仓储层(profile_repository.go)"
|
||||
participant DB as "数据库"
|
||||
Client->>Router : "POST /api/v1/profile"
|
||||
Router->>Handler : "CreateProfile"
|
||||
Handler->>Handler : "解析请求体(CreateProfileRequest)"
|
||||
Handler->>Handler : "读取用户ID(鉴权)"
|
||||
Handler->>Service : "CheckProfileLimit(userID, 5)"
|
||||
Service->>Repo : "CountProfilesByUserID(userID)"
|
||||
Repo->>DB : "统计数量"
|
||||
DB-->>Repo : "count"
|
||||
Repo-->>Service : "返回count"
|
||||
Service-->>Handler : "通过/错误"
|
||||
alt "未达上限"
|
||||
Handler->>Service : "CreateProfile(userID, name)"
|
||||
Service->>Repo : "FindUserByID(userID)"
|
||||
Repo->>DB : "查询用户"
|
||||
DB-->>Repo : "用户"
|
||||
Repo-->>Service : "返回用户"
|
||||
Service->>Repo : "FindProfileByName(name)"
|
||||
Repo->>DB : "查询角色名"
|
||||
DB-->>Repo : "结果"
|
||||
Service->>Repo : "CreateProfile(Profile)"
|
||||
Repo->>DB : "插入档案"
|
||||
Service->>Repo : "SetActiveProfile(uuid, userID)"
|
||||
Repo->>DB : "事务 : 将其他档案置为非活跃<br/>并将当前档案置为活跃"
|
||||
DB-->>Repo : "提交"
|
||||
Repo-->>Service : "返回Profile"
|
||||
Service-->>Handler : "返回Profile"
|
||||
Handler-->>Client : "200 成功(统一响应)"
|
||||
else "已达上限"
|
||||
Handler-->>Client : "400 参数错误(已达上限)"
|
||||
end
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L28-L93)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L17-L69)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L13-L109)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### POST /api/v1/profile/ 创建档案
|
||||
- 功能概述:创建新的Minecraft角色档案,系统自动生成UUID并默认设为活跃状态,同时将该用户其他档案置为非活跃。
|
||||
- 请求体
|
||||
- 必填:name(字符串,1-16字符)
|
||||
- 可选:skin_id(整数,材质ID)、cape_id(整数,材质ID)
|
||||
- 响应
|
||||
- 成功:返回统一响应结构,data为 ProfileInfo 对象,包含 uuid、user_id、name、skin_id、cape_id、is_active、last_used_at、created_at、updated_at。
|
||||
- 失败:根据错误类型返回 400(参数错误/已达上限)、401(未授权)、500(服务器错误)。
|
||||
- 关键业务逻辑
|
||||
- 用户存在性与状态校验
|
||||
- 角色名唯一性校验
|
||||
- 档案数量上限检查(默认5个)
|
||||
- 创建成功后自动设置活跃状态,并将其他档案置为非活跃
|
||||
- 请求示例
|
||||
- 方法:POST
|
||||
- URL:/api/v1/profile
|
||||
- 请求头:Authorization: Bearer <token>
|
||||
- 请求体:
|
||||
- name: "PlayerName"
|
||||
- skin_id: 123(可选)
|
||||
- cape_id: 456(可选)
|
||||
- 响应示例
|
||||
- 成功:
|
||||
- code: 200
|
||||
- message: "操作成功"
|
||||
- data: {
|
||||
uuid: "550e8400-e29b-41d4-a716-446655440000"
|
||||
user_id: 1
|
||||
name: "PlayerName"
|
||||
skin_id: 123
|
||||
cape_id: 456
|
||||
is_active: true
|
||||
last_used_at: "2025-10-01T12:00:00Z"
|
||||
created_at: "2025-10-01T10:00:00Z"
|
||||
updated_at: "2025-10-01T10:00:00Z"
|
||||
}
|
||||
- 达到上限:
|
||||
- code: 400
|
||||
- message: "已达到档案数量上限(5个)"
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["进入 CreateProfile"]) --> Bind["绑定请求体(CreateProfileRequest)"]
|
||||
Bind --> CheckAuth{"鉴权通过?"}
|
||||
CheckAuth --> |否| Resp401["返回 401 未授权"]
|
||||
CheckAuth --> |是| Limit["CheckProfileLimit(userID, 5)"]
|
||||
Limit --> Over{"超过上限?"}
|
||||
Over --> |是| Resp400["返回 400 已达上限"]
|
||||
Over --> |否| Create["CreateProfile(userID, name)"]
|
||||
Create --> Exists{"用户存在且状态正常?"}
|
||||
Exists --> |否| Resp500["返回 500 用户异常"]
|
||||
Exists --> |是| Unique{"角色名唯一?"}
|
||||
Unique --> |否| Resp400["返回 400 角色名冲突"]
|
||||
Unique --> |是| Insert["插入档案记录"]
|
||||
Insert --> SetActive["SetActiveProfile(uuid, userID)<br/>将其他档案置为非活跃,当前置为活跃"]
|
||||
SetActive --> Done(["返回 200 成功"])
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L28-L93)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L17-L69)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L13-L109)
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L28-L93)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L17-L69)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L13-L109)
|
||||
- [common.go](file://internal/types/common.go#L81-L90)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
### GET /api/v1/profile/ 获取档案列表
|
||||
- 功能概述:返回当前用户的所有档案列表,包含每个档案的UUID、名称、活跃状态、关联材质等。
|
||||
- 请求
|
||||
- 方法:GET
|
||||
- URL:/api/v1/profile
|
||||
- 请求头:Authorization: Bearer <token>
|
||||
- 响应
|
||||
- 成功:返回统一响应结构,data为 ProfileInfo 数组。
|
||||
- 失败:401(未授权)、500(服务器错误)。
|
||||
- 关键逻辑
|
||||
- 服务层查询用户所有档案并预加载关联材质(Skin/Cape)
|
||||
- 处理器将模型转换为 ProfileInfo 并返回
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Router as "路由层(routes.go)"
|
||||
participant Handler as "处理器(profile_handler.go)"
|
||||
participant Service as "服务层(profile_service.go)"
|
||||
participant Repo as "仓储层(profile_repository.go)"
|
||||
participant DB as "数据库"
|
||||
Client->>Router : "GET /api/v1/profile"
|
||||
Router->>Handler : "GetProfiles"
|
||||
Handler->>Handler : "读取用户ID(鉴权)"
|
||||
Handler->>Service : "GetUserProfiles(userID)"
|
||||
Service->>Repo : "FindProfilesByUserID(userID)"
|
||||
Repo->>DB : "查询档案列表并预加载 Skin/Cape"
|
||||
DB-->>Repo : "返回 profiles"
|
||||
Repo-->>Service : "返回 profiles"
|
||||
Service-->>Handler : "返回 profiles"
|
||||
Handler->>Handler : "转换为 ProfileInfo 列表"
|
||||
Handler-->>Client : "200 成功(统一响应)"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L95-L151)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L83-L90)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L44-L57)
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L95-L151)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L83-L90)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L44-L57)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L24)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L31)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
### 档案数量上限与活跃状态控制
|
||||
- 档案数量上限
|
||||
- 默认上限为5个,来源于系统配置与处理器中的硬编码值。
|
||||
- 服务层提供 CheckProfileLimit(userID, maxProfiles) 进行检查。
|
||||
- 活跃状态控制
|
||||
- 创建新档案时,服务层会将该档案设为活跃,并通过仓储层的事务将该用户其他档案置为非活跃。
|
||||
- 提供 SetActiveProfile 接口用于手动切换活跃档案。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["创建/切换活跃"] --> B["CheckProfileLimit(userID, 5)"]
|
||||
B --> C{"未超限?"}
|
||||
C --> |是| D["CreateProfile 或 SetActiveProfile"]
|
||||
D --> E["SetActiveProfile(uuid, userID) 事务"]
|
||||
E --> F["将其他档案 is_active=false"]
|
||||
E --> G["将当前档案 is_active=true"]
|
||||
C --> |否| H["返回 400 达到上限"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L52-L63)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L190-L202)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L109)
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L52-L63)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L190-L202)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L109)
|
||||
|
||||
## 依赖分析
|
||||
- 路由层依赖处理器层,处理器层依赖服务层,服务层依赖仓储层,仓储层依赖数据库与模型。
|
||||
- 处理器层与服务层均依赖统一响应结构与请求/响应类型定义。
|
||||
- 档案模型与材质模型存在外键关联,查询时进行预加载以减少N+1问题。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Routes["routes.go"] --> Handler["profile_handler.go"]
|
||||
Handler --> Service["profile_service.go"]
|
||||
Service --> Repo["profile_repository.go"]
|
||||
Repo --> Model["profile.go"]
|
||||
Model --> Texture["texture.go"]
|
||||
Handler --> Types["common.go"]
|
||||
Handler --> Resp["response.go"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L15-L399)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L17-L202)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L13-L118)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L57)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L31)
|
||||
- [common.go](file://internal/types/common.go#L81-L166)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L15-L399)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L17-L202)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L13-L118)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L57)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L31)
|
||||
- [common.go](file://internal/types/common.go#L81-L166)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
## 性能考虑
|
||||
- 预加载关联材质:仓储层在查询用户档案列表时预加载 Skin 与 Cape,避免多次查询。
|
||||
- 事务一致性:设置活跃状态采用数据库事务,确保原子性与一致性。
|
||||
- 唯一性约束:角色名在模型层定义唯一索引,查询时可快速判定冲突。
|
||||
- 响应结构:统一响应结构便于前端处理与日志记录。
|
||||
|
||||
[本节为通用建议,不涉及具体文件分析]
|
||||
|
||||
## 故障排查指南
|
||||
- 400 参数错误
|
||||
- 角色名为空或长度不在1-16范围内
|
||||
- 已达到档案数量上限(默认5个)
|
||||
- 401 未授权
|
||||
- 缺少或无效的认证令牌
|
||||
- 403 权限不足
|
||||
- 操作他人档案(如更新/删除/设置活跃)
|
||||
- 404 资源不存在
|
||||
- 档案UUID不存在
|
||||
- 500 服务器错误
|
||||
- 数据库查询失败、事务提交失败、用户状态异常等
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L28-L93)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L95-L151)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L280)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L282-L339)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L341-L399)
|
||||
- [profile_handler_test.go](file://internal/handler/profile_handler_test.go#L40-L73)
|
||||
- [common_test.go](file://internal/types/common_test.go#L350-L383)
|
||||
|
||||
## 结论
|
||||
- 创建档案接口严格遵循请求体校验与业务规则,确保角色名唯一与数量上限控制。
|
||||
- 活跃状态切换通过事务保障一致性,避免并发场景下的状态不一致。
|
||||
- 档案列表接口提供完整档案信息与关联材质,满足前端展示需求。
|
||||
- 建议在生产环境中将上限值从硬编码迁移到系统配置中心,以便动态调整。
|
||||
|
||||
[本节为总结性内容,不涉及具体文件分析]
|
||||
|
||||
## 附录
|
||||
|
||||
### API 定义与数据结构
|
||||
|
||||
- POST /api/v1/profile
|
||||
- 请求体:CreateProfileRequest
|
||||
- name: string (必填,1-16字符)
|
||||
- skin_id: int64 (可选)
|
||||
- cape_id: int64 (可选)
|
||||
- 响应体:统一响应结构,data 为 ProfileInfo
|
||||
- uuid: string
|
||||
- user_id: int64
|
||||
- name: string
|
||||
- skin_id: int64 (可选)
|
||||
- cape_id: int64 (可选)
|
||||
- is_active: bool
|
||||
- last_used_at: datetime (可选)
|
||||
- created_at: datetime
|
||||
- updated_at: datetime
|
||||
|
||||
- GET /api/v1/profile
|
||||
- 响应体:统一响应结构,data 为 ProfileInfo 数组
|
||||
|
||||
章节来源
|
||||
- [common.go](file://internal/types/common.go#L81-L90)
|
||||
- [common.go](file://internal/types/common.go#L154-L166)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L28-L93)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L95-L151)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
354
.qoder/repowiki/zh/content/API参考/档案API/更新与删除.md
Normal file
354
.qoder/repowiki/zh/content/API参考/档案API/更新与删除.md
Normal file
@@ -0,0 +1,354 @@
|
||||
# 更新与删除
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [routes.go](file://internal/handler/routes.go)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go)
|
||||
- [auth.go](file://internal/middleware/auth.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)
|
||||
- [common.go](file://internal/types/common.go)
|
||||
- [jwt.go](file://pkg/auth/jwt.go)
|
||||
- [manager.go](file://pkg/database/manager.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本章节面向需要对接“档案更新与删除”接口的开发者,重点说明以下内容:
|
||||
- PUT /api/v1/profile/:uuid 的更新流程:支持修改档案名称、更换关联的皮肤或披风材质;强调“只能修改自己名下的档案”的权限校验。
|
||||
- DELETE /api/v1/profile/:uuid 的删除流程:删除前进行权限检查与档案存在性验证。
|
||||
- 请求体格式与错误处理策略(如403无权操作、404资源不存在)。
|
||||
- 结合 service 层代码解释更新操作的事务性保证与数据一致性维护机制。
|
||||
|
||||
## 项目结构
|
||||
围绕档案更新与删除功能,涉及如下关键模块:
|
||||
- 路由层:在路由中注册了 /api/v1/profile/{uuid} 的 PUT 与 DELETE 接口,并统一使用鉴权中间件。
|
||||
- 中间件层:JWT 鉴权中间件负责解析 Authorization 头并校验令牌有效性,将用户标识注入上下文。
|
||||
- 处理器层:profile_handler 负责接收请求、绑定参数、调用 service 并输出响应。
|
||||
- 服务层:profile_service 执行业务逻辑,包含权限校验、唯一性检查、更新与删除等。
|
||||
- 仓储层:profile_repository 封装数据库访问,提供查询、更新、删除与事务性操作。
|
||||
- 模型与类型:profile 模型定义档案字段及关联关系;types 定义请求与响应结构。
|
||||
- 数据库与鉴权:GORM 管理数据库连接;JWT 用于用户身份验证。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Client["客户端"] --> Routes["路由: /api/v1/profile/{uuid}"]
|
||||
Routes --> AuthMW["鉴权中间件: JWT"]
|
||||
AuthMW --> Handler["处理器: profile_handler"]
|
||||
Handler --> Service["服务: profile_service"]
|
||||
Service --> Repo["仓储: profile_repository"]
|
||||
Repo --> DB["数据库: GORM"]
|
||||
Handler --> Model["模型: Profile"]
|
||||
Handler --> Types["类型: UpdateProfileRequest"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [auth.go](file://internal/middleware/auth.go#L12-L56)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L159)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L29)
|
||||
- [common.go](file://internal/types/common.go#L201-L206)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [auth.go](file://internal/middleware/auth.go#L12-L56)
|
||||
|
||||
## 核心组件
|
||||
- 路由注册:在路由组 /api/v1/profile 下注册了 GET/:uuid、POST/、GET/、PUT/:uuid、DELETE/:uuid、POST/:uuid/activate 等接口,其中 PUT 与 DELETE 对应本节主题。
|
||||
- 鉴权中间件:要求 Authorization 头为 Bearer 令牌,校验通过后将 user_id 等信息写入上下文。
|
||||
- 处理器:UpdateProfile 与 DeleteProfile 分别调用 service 层执行业务逻辑,并根据 service 返回的错误映射为合适的 HTTP 状态码。
|
||||
- 服务层:UpdateProfile 与 DeleteProfile 在执行前均进行“档案存在性验证”和“权限校验”,并在必要时使用数据库事务保证一致性。
|
||||
- 仓储层:FindProfileByUUID、UpdateProfile、DeleteProfile 提供基础 CRUD;SetActiveProfile 使用事务确保“同一用户仅有一个活跃档案”。
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L159)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L109)
|
||||
|
||||
## 架构总览
|
||||
下图展示了从客户端到数据库的调用链路,以及权限校验与事务控制的关键节点。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant R as "路由"
|
||||
participant M as "鉴权中间件"
|
||||
participant H as "处理器 : UpdateProfile/DeleteProfile"
|
||||
participant S as "服务 : profile_service"
|
||||
participant P as "仓储 : profile_repository"
|
||||
participant D as "数据库 : GORM"
|
||||
C->>R : "PUT /api/v1/profile/{uuid}"
|
||||
R->>M : "进入鉴权中间件"
|
||||
M-->>R : "校验通过,注入 user_id"
|
||||
R->>H : "转发请求"
|
||||
H->>S : "调用 UpdateProfile/ DeleteProfile"
|
||||
S->>P : "FindProfileByUUID"
|
||||
P->>D : "查询档案"
|
||||
D-->>P : "返回档案或记录不存在"
|
||||
P-->>S : "返回结果"
|
||||
alt "更新场景"
|
||||
S->>S : "权限校验 : profile.UserID == user_id"
|
||||
S->>P : "可选 : 名称唯一性检查"
|
||||
S->>P : "UpdateProfile(更新字段)"
|
||||
P->>D : "保存更新"
|
||||
D-->>P : "成功"
|
||||
P-->>S : "返回更新后的档案"
|
||||
else "删除场景"
|
||||
S->>S : "权限校验 : profile.UserID == user_id"
|
||||
S->>P : "DeleteProfile"
|
||||
P->>D : "删除记录"
|
||||
D-->>P : "成功"
|
||||
end
|
||||
S-->>H : "返回结果或错误"
|
||||
H-->>C : "HTTP 响应(200/403/404/500)"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L159)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### PUT /api/v1/profile/:uuid 更新档案
|
||||
- 功能概述
|
||||
- 支持修改档案名称(可选)、更换关联的皮肤或披风材质(可选)。
|
||||
- 严格权限控制:仅允许修改“自己名下的档案”。
|
||||
- 请求路径与方法
|
||||
- 方法: PUT
|
||||
- 路径: /api/v1/profile/:uuid
|
||||
- 鉴权: 需要 Bearer 令牌
|
||||
- 请求体结构
|
||||
- 字段:
|
||||
- name: 字符串,长度范围 1-16(可选)
|
||||
- skin_id: 整数,指向材质记录的ID(可选)
|
||||
- cape_id: 整数,指向材质记录的ID(可选)
|
||||
- 示例: 仅更新名称
|
||||
- 示例: 仅更换皮肤
|
||||
- 示例: 同时更换皮肤与披风
|
||||
- 成功响应
|
||||
- 返回更新后的档案信息(包含 uuid、user_id、name、skin_id、cape_id、is_active、last_used_at、created_at、updated_at)。
|
||||
- 错误处理
|
||||
- 400: 请求参数错误(如字段校验失败)
|
||||
- 401: 未授权(缺少或无效的 Authorization 头)
|
||||
- 403: 无权操作(目标档案不属于当前用户)
|
||||
- 404: 资源不存在(档案UUID不存在)
|
||||
- 500: 服务器内部错误(数据库异常、唯一性冲突等)
|
||||
- 权限与存在性校验
|
||||
- 处理器从上下文取出 user_id,若缺失则直接返回 401。
|
||||
- 服务层先查询档案,若不存在返回 404;随后校验 profile.UserID 是否等于 user_id,否则返回 403。
|
||||
- 名称唯一性与字段更新
|
||||
- 当 name 发生变化时,服务层会检查同名是否已存在,若存在则返回 400。
|
||||
- skin_id 与 cape_id 为可选字段,仅当传入非空值时才更新对应字段。
|
||||
- 事务性与一致性
|
||||
- 更新操作本身通过单条 save/update 完成,不涉及跨表事务。
|
||||
- 若未来扩展为多步更新(例如同时更新多个关联字段),建议在服务层使用 GORM 事务包裹,确保原子性与一致性。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["进入 UpdateProfile"]) --> Parse["解析请求体<br/>name/skin_id/cape_id"]
|
||||
Parse --> GetCtx["从上下文获取 user_id"]
|
||||
GetCtx --> HasUser{"user_id 存在?"}
|
||||
HasUser --> |否| Resp401["返回 401 未授权"]
|
||||
HasUser --> |是| Load["查询档案 FindProfileByUUID"]
|
||||
Load --> Found{"档案存在?"}
|
||||
Found --> |否| Resp404["返回 404 资源不存在"]
|
||||
Found --> |是| Perm{"档案归属校验<br/>profile.UserID == user_id"}
|
||||
Perm --> |否| Resp403["返回 403 无权操作"]
|
||||
Perm --> |是| NameChk{"是否更新 name?"}
|
||||
NameChk --> |是| Dup{"检查同名是否存在"}
|
||||
Dup --> |是| Resp400["返回 400 参数错误"]
|
||||
Dup --> |否| Apply["应用字段更新<br/>name/skin_id/cape_id"]
|
||||
NameChk --> |否| Apply
|
||||
Apply --> Save["保存更新 UpdateProfile"]
|
||||
Save --> Reload["重新加载档案 FindProfileByUUID"]
|
||||
Reload --> Resp200["返回 200 成功"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L280)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L135)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L71)
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L280)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L135)
|
||||
- [common.go](file://internal/types/common.go#L201-L206)
|
||||
|
||||
### DELETE /api/v1/profile/:uuid 删除档案
|
||||
- 功能概述
|
||||
- 删除指定 UUID 的档案。
|
||||
- 删除前进行权限检查与档案存在性验证。
|
||||
- 请求路径与方法
|
||||
- 方法: DELETE
|
||||
- 路径: /api/v1/profile/:uuid
|
||||
- 鉴权: 需要 Bearer 令牌
|
||||
- 成功响应
|
||||
- 返回成功消息(message: "删除成功")。
|
||||
- 错误处理
|
||||
- 401: 未授权(缺少或无效的 Authorization 头)
|
||||
- 403: 无权操作(目标档案不属于当前用户)
|
||||
- 404: 资源不存在(档案UUID不存在)
|
||||
- 500: 服务器内部错误(数据库异常)
|
||||
- 删除流程
|
||||
- 处理器从上下文取出 user_id,若缺失则直接返回 401。
|
||||
- 服务层先查询档案,若不存在返回 404;随后校验权限,不匹配返回 403。
|
||||
- 通过权限校验后执行删除,成功返回 200。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "处理器 : DeleteProfile"
|
||||
participant S as "服务 : profile_service"
|
||||
participant P as "仓储 : profile_repository"
|
||||
participant D as "数据库 : GORM"
|
||||
C->>H : "DELETE /api/v1/profile/{uuid}"
|
||||
H->>S : "调用 DeleteProfile(uuid, user_id)"
|
||||
S->>P : "FindProfileByUUID"
|
||||
P->>D : "查询"
|
||||
D-->>P : "返回档案或不存在"
|
||||
P-->>S : "结果"
|
||||
alt "档案不存在"
|
||||
S-->>H : "返回 404"
|
||||
H-->>C : "404"
|
||||
else "权限校验失败"
|
||||
S-->>H : "返回 403"
|
||||
H-->>C : "403"
|
||||
else "权限通过"
|
||||
S->>P : "DeleteProfile"
|
||||
P->>D : "删除记录"
|
||||
D-->>P : "成功"
|
||||
S-->>H : "返回 nil"
|
||||
H-->>C : "200 成功"
|
||||
end
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L282-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L137-L159)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L73-L77)
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L282-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L137-L159)
|
||||
|
||||
### 权限验证与鉴权中间件
|
||||
- 中间件职责
|
||||
- 校验 Authorization 头格式(Bearer token)。
|
||||
- 使用 JWT 服务验证令牌有效性,并将 user_id、username、role 写入上下文。
|
||||
- 处理器侧使用
|
||||
- 处理器从上下文读取 user_id,若不存在则返回 401。
|
||||
- 服务层进一步校验档案归属,不匹配返回 403。
|
||||
- JWT 服务
|
||||
- 生成与验证使用 HS256 签名算法,过期时间可配置。
|
||||
|
||||
章节来源
|
||||
- [auth.go](file://internal/middleware/auth.go#L12-L56)
|
||||
- [jwt.go](file://pkg/auth/jwt.go#L10-L71)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339)
|
||||
|
||||
### 事务性保证与数据一致性
|
||||
- 更新操作
|
||||
- 当前 UpdateProfile 通过单次 save/update 完成,不涉及跨表事务。
|
||||
- 若未来扩展为多步更新(例如同时更新多个字段或关联表),建议在服务层使用 GORM 事务包裹,确保原子性与一致性。
|
||||
- 删除操作
|
||||
- DeleteProfile 为单条删除,不涉及跨表事务。
|
||||
- 活跃档案设置
|
||||
- SetActiveProfile 使用 GORM 事务,先将用户所有档案设为非活跃,再将目标档案设为活跃,保证“同一用户仅有一个活跃档案”的约束。
|
||||
|
||||
章节来源
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L135)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L137-L188)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L109)
|
||||
|
||||
## 依赖关系分析
|
||||
- 路由依赖
|
||||
- profile 路由组使用鉴权中间件,确保后续接口均需有效 JWT。
|
||||
- 处理器依赖
|
||||
- profile_handler 依赖鉴权中间件提供的 user_id,依赖 service 层执行业务逻辑,依赖 model/types 定义的数据结构。
|
||||
- 服务层依赖
|
||||
- profile_service 依赖 repository 层进行数据访问,依赖数据库连接管理器。
|
||||
- 仓储层依赖
|
||||
- profile_repository 依赖 GORM 与数据库连接。
|
||||
- 鉴权与数据库
|
||||
- jwt.go 提供令牌签发与校验;manager.go 提供数据库连接获取与迁移。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Routes["routes.go"] --> AuthMW["auth.go"]
|
||||
AuthMW --> Handler["profile_handler.go"]
|
||||
Handler --> Service["profile_service.go"]
|
||||
Service --> Repo["profile_repository.go"]
|
||||
Repo --> DBMgr["manager.go"]
|
||||
Handler --> Types["common.go"]
|
||||
Handler --> Model["profile.go"]
|
||||
Handler --> JWT["jwt.go"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [auth.go](file://internal/middleware/auth.go#L12-L56)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L159)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77)
|
||||
- [manager.go](file://pkg/database/manager.go#L35-L50)
|
||||
- [common.go](file://internal/types/common.go#L201-L206)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L29)
|
||||
- [jwt.go](file://pkg/auth/jwt.go#L10-L71)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L159)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77)
|
||||
- [manager.go](file://pkg/database/manager.go#L35-L50)
|
||||
|
||||
## 性能考量
|
||||
- 查询与预加载
|
||||
- 仓储层在查询档案时使用预加载关联的皮肤与披风,有助于减少 N+1 查询问题,提升响应速度。
|
||||
- 事务范围
|
||||
- 当前更新与删除均为单条操作,事务开销较小;若扩展为多步更新,建议将相关操作合并到事务中,避免部分成功导致的不一致。
|
||||
- 唯一性检查
|
||||
- 更新名称时进行同名检查,避免并发场景下的重复;建议在数据库层面增加唯一索引以降低竞争条件风险。
|
||||
- 日志与可观测性
|
||||
- 处理器在发生错误时记录日志,便于定位问题;建议在服务层也增加关键步骤的日志埋点。
|
||||
|
||||
章节来源
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L135)
|
||||
|
||||
## 故障排查指南
|
||||
- 401 未授权
|
||||
- 检查请求头 Authorization 是否为 Bearer 令牌格式;确认令牌未过期且签名正确。
|
||||
- 403 无权操作
|
||||
- 确认当前用户是否拥有目标档案;检查档案所属 user_id 与当前用户是否一致。
|
||||
- 404 资源不存在
|
||||
- 确认档案 UUID 是否正确;检查数据库中是否存在该记录。
|
||||
- 400 参数错误
|
||||
- 检查请求体字段是否符合长度与类型要求;例如 name 长度应在 1-16 之间。
|
||||
- 500 服务器错误
|
||||
- 查看服务端日志,关注数据库连接、唯一性冲突、事务回滚等问题。
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L92-L159)
|
||||
|
||||
## 结论
|
||||
- PUT /api/v1/profile/:uuid 与 DELETE /api/v1/profile/:uuid 已具备完善的权限校验与存在性验证机制。
|
||||
- 更新操作支持名称与材质字段的灵活更新,删除操作简洁可靠。
|
||||
- 服务层与仓储层清晰分离职责,当前更新与删除为单步操作;若未来扩展为多步更新,建议引入事务以保障一致性。
|
||||
- 建议在数据库层面完善唯一性约束,并在服务层增加关键步骤的日志埋点,以便于问题定位与性能优化。
|
||||
336
.qoder/repowiki/zh/content/API参考/档案API/档案API.md
Normal file
336
.qoder/repowiki/zh/content/API参考/档案API/档案API.md
Normal file
@@ -0,0 +1,336 @@
|
||||
# 档案API
|
||||
|
||||
<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)
|
||||
- [common.go](file://internal/types/common.go)
|
||||
- [response.go](file://internal/model/response.go)
|
||||
- [profile.go](file://internal/model/profile.go)
|
||||
- [texture.go](file://internal/model/texture.go)
|
||||
- [texture_service.go](file://internal/service/texture_service.go)
|
||||
- [texture_repository.go](file://internal/repository/texture_repository.go)
|
||||
- [postgres.go](file://pkg/database/postgres.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件面向开发者与集成方,系统化梳理“档案API”的设计与使用方法,覆盖以下能力:
|
||||
- 档案的创建、查询、更新、删除与“活跃档案”设置
|
||||
- 公开获取档案详情与需要认证的档案管理端点
|
||||
- 档案与Minecraft用户UUID的关系、活跃档案的概念
|
||||
- 档案列表的获取方式
|
||||
- 完整的API使用示例,包括创建档案的请求体结构与获取档案详情的响应格式
|
||||
- 档案与材质之间的关系
|
||||
|
||||
## 项目结构
|
||||
档案API位于路由组 `/api/v1/profile` 下,采用“公开路由 + 认证路由”的分层设计:
|
||||
- 公开路由:通过档案UUID获取档案详情
|
||||
- 认证路由:需要携带JWT令牌,支持创建、查询列表、更新、删除、设置活跃档案
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "路由组 /api/v1/profile"
|
||||
A["GET /:uuid<br/>公开:获取档案详情"]
|
||||
subgraph "认证组"
|
||||
B["POST /<br/>创建档案"]
|
||||
C["GET /<br/>获取我的档案列表"]
|
||||
D["PUT /:uuid<br/>更新档案"]
|
||||
E["DELETE /:uuid<br/>删除档案"]
|
||||
F["POST /:uuid/activate<br/>设置活跃档案"]
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
|
||||
## 核心组件
|
||||
- 路由注册:集中于路由文件,定义公开与认证端点
|
||||
- 处理器:profile_handler 负责鉴权、参数解析、调用服务层并返回统一响应
|
||||
- 服务层:profile_service 实现业务规则(如创建档案时生成UUID与RSA私钥、设置活跃状态、权限校验等)
|
||||
- 仓储层:profile_repository 封装数据库操作(增删改查、统计、事务)
|
||||
- 类型与模型:types 中定义请求/响应结构;model 中定义档案与材质模型及响应结构
|
||||
- 数据库:通过GORM连接PostgreSQL,统一日志与连接池配置
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L1-L399)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
||||
- [common.go](file://internal/types/common.go#L81-L207)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
- [profile.go](file://internal/model/profile.go#L1-L64)
|
||||
- [texture.go](file://internal/model/texture.go#L1-L77)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L1-L74)
|
||||
|
||||
## 架构总览
|
||||
档案API遵循经典的三层架构:HTTP路由 -> 处理器 -> 服务 -> 仓储 -> 数据库。认证中间件确保仅持有有效令牌的用户可访问受保护端点。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Client["客户端"] --> R["Gin 路由<br/>/api/v1/profile"]
|
||||
R --> M["认证中间件"]
|
||||
M --> H["处理器<br/>profile_handler"]
|
||||
H --> S["服务层<br/>profile_service"]
|
||||
S --> Repo["仓储层<br/>profile_repository"]
|
||||
Repo --> DB["数据库<br/>PostgreSQL(GORM)"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L1-L399)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L1-L74)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 路由与端点
|
||||
- 公开端点
|
||||
- GET /api/v1/profile/:uuid:根据UUID获取档案详情
|
||||
- 认证端点
|
||||
- POST /api/v1/profile:创建档案
|
||||
- GET /api/v1/profile:获取当前用户的所有档案列表
|
||||
- PUT /api/v1/profile/:uuid:更新档案(可更新角色名、皮肤ID、披风ID)
|
||||
- DELETE /api/v1/profile/:uuid:删除档案
|
||||
- POST /api/v1/profile/:uuid/activate:设置活跃档案(同时将该用户其他档案设为非活跃)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
|
||||
### 处理器与鉴权
|
||||
- 认证中间件:所有认证端点均使用认证中间件,从上下文提取user_id
|
||||
- 参数绑定:使用Gin的ShouldBindJSON进行请求体校验
|
||||
- 统一响应:使用统一响应结构,错误码与消息标准化
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L1-L399)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
### 服务层业务规则
|
||||
- 创建档案
|
||||
- 校验用户存在且状态正常
|
||||
- 校验角色名唯一
|
||||
- 生成UUID与RSA私钥(PEM格式)
|
||||
- 默认设置为活跃档案,并将该用户其他档案设为非活跃
|
||||
- 更新档案
|
||||
- 校验档案归属(仅档案所属用户可更新)
|
||||
- 校验新角色名唯一
|
||||
- 支持更新角色名、皮肤ID、披风ID
|
||||
- 删除档案
|
||||
- 校验档案归属
|
||||
- 设置活跃档案
|
||||
- 校验档案归属
|
||||
- 使用事务将该用户其他档案设为非活跃,再将目标档案设为活跃
|
||||
- 同步更新最后使用时间
|
||||
- 档案数量限制
|
||||
- 通过服务层检查当前用户档案数量是否超过上限
|
||||
|
||||
章节来源
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
||||
|
||||
### 仓储层与数据库
|
||||
- 查询
|
||||
- FindProfileByUUID:按UUID查询并预加载皮肤与披风
|
||||
- FindProfilesByUserID:按用户ID查询并预加载皮肤与披风,按创建时间倒序
|
||||
- FindProfileByName:按角色名查询
|
||||
- 更新
|
||||
- UpdateProfile、UpdateProfileFields
|
||||
- 删除
|
||||
- DeleteProfile(按UUID删除)
|
||||
- 统计
|
||||
- CountProfilesByUserID
|
||||
- 活跃状态
|
||||
- SetActiveProfile:事务内将该用户其他档案设为非活跃,再将目标档案设为活跃
|
||||
- 最后使用时间
|
||||
- UpdateProfileLastUsedAt
|
||||
|
||||
章节来源
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
||||
|
||||
### 模型与类型
|
||||
- 档案模型
|
||||
- 字段:UUID、用户ID、角色名、皮肤ID、披风ID、活跃状态、最后使用时间、创建/更新时间
|
||||
- 关联:User、Skin(Texture)、Cape(Texture)
|
||||
- 档案响应结构
|
||||
- ProfileResponse:包含UUID、角色名、textures(皮肤/披风)、活跃状态、最后使用时间、创建时间
|
||||
- ProfileTexturesData:包含SKIN与CAPE两个可选字段
|
||||
- ProfileTexture:包含URL与可选metadata(如模型类型)
|
||||
- 请求/响应类型
|
||||
- CreateProfileRequest:name(必填,1-16字符)
|
||||
- UpdateProfileRequest:name(可选)、skin_id(可选)、cape_id(可选)
|
||||
- ProfileInfo:用于列表与详情的统一返回字段
|
||||
|
||||
章节来源
|
||||
- [profile.go](file://internal/model/profile.go#L1-L64)
|
||||
- [common.go](file://internal/types/common.go#L81-L207)
|
||||
|
||||
### 档案与材质的关系
|
||||
- 档案可关联两种材质:皮肤(SkinID)与披风(CapeID)
|
||||
- 材质模型包含类型(SKIN/CAPE)、URL、哈希、大小、公开状态等
|
||||
- 服务层与仓储层均支持对材质的创建、查询、更新、删除、收藏等操作,但这些属于独立的材质API范畴
|
||||
|
||||
章节来源
|
||||
- [profile.go](file://internal/model/profile.go#L1-L64)
|
||||
- [texture.go](file://internal/model/texture.go#L1-L77)
|
||||
- [texture_service.go](file://internal/service/texture_service.go#L1-L252)
|
||||
- [texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
|
||||
|
||||
### API使用示例
|
||||
|
||||
- 创建档案
|
||||
- 方法与路径:POST /api/v1/profile
|
||||
- 认证:需要JWT
|
||||
- 请求体结构(CreateProfileRequest)
|
||||
- name:字符串,必填,长度1-16
|
||||
- 成功响应:返回ProfileInfo(包含UUID、用户ID、角色名、皮肤ID、披风ID、活跃状态、最后使用时间、创建/更新时间)
|
||||
- 常见错误:400(参数错误/达到档案数量上限)、401(未授权)、500(服务器错误)
|
||||
|
||||
- 获取我的档案列表
|
||||
- 方法与路径:GET /api/v1/profile
|
||||
- 认证:需要JWT
|
||||
- 成功响应:数组,元素为ProfileInfo
|
||||
|
||||
- 获取档案详情
|
||||
- 方法与路径:GET /api/v1/profile/{uuid}
|
||||
- 认证:公开端点,无需JWT
|
||||
- 成功响应:ProfileInfo
|
||||
|
||||
- 更新档案
|
||||
- 方法与路径:PUT /api/v1/profile/{uuid}
|
||||
- 认证:需要JWT
|
||||
- 请求体结构(UpdateProfileRequest)
|
||||
- name:字符串,可选,长度1-16
|
||||
- skin_id:整数,可选
|
||||
- cape_id:整数,可选
|
||||
- 成功响应:ProfileInfo
|
||||
|
||||
- 删除档案
|
||||
- 方法与路径:DELETE /api/v1/profile/{uuid}
|
||||
- 认证:需要JWT
|
||||
- 成功响应:{"message":"删除成功"}
|
||||
|
||||
- 设置活跃档案
|
||||
- 方法与路径:POST /api/v1/profile/{uuid}/activate
|
||||
- 认证:需要JWT
|
||||
- 成功响应:{"message":"设置成功"}
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L1-L399)
|
||||
- [common.go](file://internal/types/common.go#L81-L207)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
### 活跃档案概念
|
||||
- 每个用户在同一时刻只能有一个“活跃档案”
|
||||
- 当创建新档案或切换活跃档案时,系统会将该用户其他档案设为非活跃
|
||||
- 设置活跃档案会同步更新最后使用时间
|
||||
|
||||
章节来源
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
|
||||
|
||||
### 档案列表获取方式
|
||||
- 通过认证端点 GET /api/v1/profile 获取当前用户的所有档案
|
||||
- 返回顺序按创建时间倒序
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L95-L151)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L83-L90)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L44-L57)
|
||||
|
||||
### 档案与Minecraft用户UUID的关系
|
||||
- 档案模型包含UUID字段,用于标识Minecraft角色
|
||||
- 档案与用户通过user_id关联
|
||||
- 公开端点通过UUID获取档案详情,不涉及用户身份
|
||||
|
||||
章节来源
|
||||
- [profile.go](file://internal/model/profile.go#L1-L64)
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
|
||||
## 依赖分析
|
||||
- 路由到处理器:路由文件注册各端点,处理器负责鉴权与参数解析
|
||||
- 处理器到服务:处理器调用服务层实现业务逻辑
|
||||
- 服务到仓储:服务层封装业务规则并委托仓储层执行数据库操作
|
||||
- 仓储到数据库:仓储层通过GORM访问PostgreSQL
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Routes["routes.go"] --> Handler["profile_handler.go"]
|
||||
Handler --> Service["profile_service.go"]
|
||||
Service --> Repo["profile_repository.go"]
|
||||
Repo --> DB["postgres.go"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L1-L399)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L1-L74)
|
||||
|
||||
## 性能考虑
|
||||
- 预加载策略:查询档案时预加载皮肤与披风,减少N+1查询风险
|
||||
- 分页与排序:列表查询按创建时间倒序,避免全量扫描
|
||||
- 事务一致性:设置活跃档案使用事务,保证原子性
|
||||
- 连接池:数据库连接池配置合理,建议结合实际负载调整
|
||||
|
||||
章节来源
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L57)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L161-L188)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L1-L74)
|
||||
|
||||
## 故障排查指南
|
||||
- 未授权
|
||||
- 现象:返回401
|
||||
- 排查:确认JWT是否正确传递与有效
|
||||
- 参数错误
|
||||
- 现象:返回400
|
||||
- 排查:检查请求体字段是否符合长度与类型要求
|
||||
- 无权操作
|
||||
- 现象:返回403
|
||||
- 排查:确认操作的档案是否属于当前用户
|
||||
- 资源不存在
|
||||
- 现象:返回404
|
||||
- 排查:确认UUID是否正确、档案是否被删除
|
||||
- 达到上限
|
||||
- 现象:返回400
|
||||
- 排查:检查当前用户档案数量与系统限制
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L1-L399)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
## 结论
|
||||
档案API以清晰的公开/认证分层设计,结合严格的鉴权与参数校验,提供了完整的Minecraft角色档案生命周期管理能力。通过服务层的业务规则与仓储层的事务保障,系统在一致性与扩展性方面具备良好基础。建议在生产环境中配合限流、缓存与监控进一步优化性能与稳定性。
|
||||
|
||||
## 附录
|
||||
|
||||
### API端点一览
|
||||
- 公开
|
||||
- GET /api/v1/profile/:uuid
|
||||
- 认证
|
||||
- POST /api/v1/profile
|
||||
- GET /api/v1/profile
|
||||
- PUT /api/v1/profile/:uuid
|
||||
- DELETE /api/v1/profile/:uuid
|
||||
- POST /api/v1/profile/:uuid/activate
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
288
.qoder/repowiki/zh/content/API参考/档案API/激活管理.md
Normal file
288
.qoder/repowiki/zh/content/API参考/档案API/激活管理.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# 激活管理
|
||||
|
||||
<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。该设计既满足业务需求,又通过数据库层的事务保障了原子性与可靠性。建议在生产环境中关注索引与连接池配置,以进一步优化性能与稳定性。
|
||||
299
.qoder/repowiki/zh/content/API参考/档案API/详情查询.md
Normal file
299
.qoder/repowiki/zh/content/API参考/档案API/详情查询.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# 详情查询
|
||||
|
||||
<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)
|
||||
- [texture.go](file://internal/model/texture.go)
|
||||
- [response.go](file://internal/model/response.go)
|
||||
- [common.go](file://internal/types/common.go)
|
||||
- [postgres.go](file://pkg/database/postgres.go)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文件面向“详情查询”API,聚焦 GET /api/v1/profile/:uuid 端点。该接口为公开路由,无需认证即可访问,用于根据Minecraft用户UUID获取档案的公开信息。响应体包含档案UUID、用户名、创建时间、最后使用时间、活跃状态,以及关联的皮肤和披风信息(若存在)。该接口在Minecraft客户端通过Yggdrasil协议连接服务器时,可被用于查询玩家档案信息,以支持皮肤/披风展示等场景。
|
||||
|
||||
## 项目结构
|
||||
围绕“详情查询”API的关键文件组织如下:
|
||||
- 路由注册:在路由层定义公开的 GET /api/v1/profile/:uuid,并将其挂载到 v1 分组下。
|
||||
- 处理器:实现 GetProfile 处理函数,负责参数解析、调用服务层、构造统一响应。
|
||||
- 服务层:封装业务逻辑,调用仓库层进行数据查询。
|
||||
- 仓库层:基于GORM执行数据库查询,预加载皮肤/披风关联。
|
||||
- 模型与类型:定义档案模型、响应结构、纹理类型等。
|
||||
- 数据库:PostgreSQL驱动与连接池配置。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "路由层"
|
||||
R["routes.go<br/>注册 /api/v1/profile/:uuid"]
|
||||
end
|
||||
subgraph "处理器层"
|
||||
H["profile_handler.go<br/>GetProfile 处理函数"]
|
||||
end
|
||||
subgraph "服务层"
|
||||
S["profile_service.go<br/>GetProfileByUUID"]
|
||||
end
|
||||
subgraph "仓库层"
|
||||
REPO["profile_repository.go<br/>FindProfileByUUID"]
|
||||
end
|
||||
subgraph "模型与类型"
|
||||
M["profile.go<br/>Profile/ProfileResponse"]
|
||||
T["texture.go<br/>Texture"]
|
||||
RESP["response.go<br/>统一响应结构"]
|
||||
TYPES["common.go<br/>ProfileInfo"]
|
||||
end
|
||||
subgraph "数据库"
|
||||
DB["postgres.go<br/>PostgreSQL 连接与配置"]
|
||||
end
|
||||
R --> H
|
||||
H --> S
|
||||
S --> REPO
|
||||
REPO --> DB
|
||||
H --> RESP
|
||||
S --> M
|
||||
REPO --> M
|
||||
M --> T
|
||||
H --> TYPES
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L24)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L35)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
- [common.go](file://internal/types/common.go#L154-L165)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L13-L74)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
|
||||
## 核心组件
|
||||
- 路由层:在 v1 分组下注册 GET /api/v1/profile/:uuid,明确该端点为公开路由,无需JWT认证。
|
||||
- 处理器层:GetProfile 从路径参数读取 uuid,调用服务层查询档案,构造统一响应。
|
||||
- 服务层:GetProfileByUUID 调用仓库层查询档案;若未找到返回“档案不存在”错误。
|
||||
- 仓库层:FindProfileByUUID 使用 uuid 条件查询,并预加载 Skin 和 Cape 关联。
|
||||
- 模型与类型:Profile 定义档案字段及关联;ProfileResponse 为对外响应结构;ProfileInfo 为处理器侧返回结构;Texture 定义材质模型及其索引字段。
|
||||
- 统一响应:response.go 提供统一的 Response/Error 结构,便于前后端约定。
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L24)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L35)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
- [common.go](file://internal/types/common.go#L154-L165)
|
||||
|
||||
## 架构总览
|
||||
下面的序列图展示了客户端调用 GET /api/v1/profile/:uuid 的典型流程,以及与Yggdrasil协议的集成场景。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Router as "Gin 路由"
|
||||
participant Handler as "GetProfile 处理器"
|
||||
participant Service as "profile_service.GetProfileByUUID"
|
||||
participant Repo as "profile_repository.FindProfileByUUID"
|
||||
participant DB as "PostgreSQL"
|
||||
Client->>Router : "GET /api/v1/profile/ : uuid"
|
||||
Router->>Handler : "路由分发"
|
||||
Handler->>Service : "GetProfileByUUID(uuid)"
|
||||
Service->>Repo : "FindProfileByUUID(uuid)"
|
||||
Repo->>DB : "SELECT ... WHERE uuid=?<br/>并预加载 Skin/Cape"
|
||||
DB-->>Repo : "Profile 记录"
|
||||
Repo-->>Service : "Profile 对象"
|
||||
Service-->>Handler : "Profile 对象"
|
||||
Handler-->>Client : "200 + 统一响应体"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L13-L74)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 接口定义与行为
|
||||
- 路由:公开端点 GET /api/v1/profile/:uuid,无需认证。
|
||||
- 请求参数:路径参数 uuid(Minecraft档案UUID)。
|
||||
- 成功响应:返回统一响应结构,包含档案基本信息与关联皮肤/披风信息。
|
||||
- 错误响应:当档案不存在时返回404。
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
### 响应数据结构
|
||||
- 响应体字段(对外):
|
||||
- uuid:档案UUID
|
||||
- name:Minecraft角色名
|
||||
- textures:包含皮肤与披风信息的对象
|
||||
- SKIN:皮肤信息(可选)
|
||||
- url:皮肤图片地址
|
||||
- metadata:元数据(可选)
|
||||
- model:皮肤模型(slim 或 classic)
|
||||
- CAPE:披风信息(可选)
|
||||
- url:披风图片地址
|
||||
- metadata:元数据(可选)
|
||||
- is_active:是否为活跃档案
|
||||
- last_used_at:最后使用时间(可选)
|
||||
- created_at:创建时间
|
||||
- updated_at:更新时间
|
||||
|
||||
- 处理器侧返回结构(ProfileInfo):
|
||||
- uuid、user_id、name、skin_id、cape_id、is_active、last_used_at、created_at、updated_at
|
||||
|
||||
章节来源
|
||||
- [profile.go](file://internal/model/profile.go#L31-L57)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L35)
|
||||
- [common.go](file://internal/types/common.go#L154-L165)
|
||||
|
||||
### 查询流程与错误码
|
||||
- 正常流程:处理器读取 uuid -> 服务层查询 -> 仓库层查询并预加载关联 -> 返回统一响应。
|
||||
- 错误码:
|
||||
- 404:档案不存在
|
||||
- 500:服务器内部错误(数据库异常、服务层错误等)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["进入 GetProfile"]) --> Parse["解析路径参数 uuid"]
|
||||
Parse --> Query["调用服务层 GetProfileByUUID"]
|
||||
Query --> RepoCall["仓库层 FindProfileByUUID(uuid)"]
|
||||
RepoCall --> Found{"找到记录?"}
|
||||
Found --> |是| BuildResp["组装响应含 textures"]
|
||||
Found --> |否| NotFound["返回 404 档案不存在"]
|
||||
BuildResp --> Ok["返回 200 + 统一响应"]
|
||||
NotFound --> End(["结束"])
|
||||
Ok --> End
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
### 与Yggdrasil协议的集成场景
|
||||
- 在Minecraft客户端连接服务器时,可通过Yggdrasil的会话服务器端点获取玩家档案信息,从而展示皮肤/披风。
|
||||
- 该端点与Yggdrasil的会话服务器端点存在语义上的互补:前者为公开档案详情查询,后者为认证后的会话信息查询。
|
||||
- 典型调用链:客户端 -> 服务器(Yggdrasil)-> 本系统 GetProfileByUUID(公开详情查询)。
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L426-L440)
|
||||
|
||||
## 依赖分析
|
||||
- 路由层依赖 Gin 路由注册,将公开端点挂载至 v1 分组。
|
||||
- 处理器层依赖服务层与统一响应结构。
|
||||
- 服务层依赖仓库层与数据库连接。
|
||||
- 仓库层依赖 GORM 与 PostgreSQL 驱动。
|
||||
- 模型层定义档案与纹理的字段、索引与关联。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Routes["routes.go"] --> Handler["profile_handler.go"]
|
||||
Handler --> Service["profile_service.go"]
|
||||
Service --> Repo["profile_repository.go"]
|
||||
Repo --> DB["postgres.go"]
|
||||
Handler --> Resp["response.go"]
|
||||
Service --> Model["profile.go"]
|
||||
Repo --> Model
|
||||
Model --> Texture["texture.go"]
|
||||
Handler --> Types["common.go"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L13-L74)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L24)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L35)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
- [common.go](file://internal/types/common.go#L154-L165)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L63-L79)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L13-L74)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L24)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L35)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
- [common.go](file://internal/types/common.go#L154-L165)
|
||||
|
||||
## 性能考量
|
||||
- 数据库索引与查询优化
|
||||
- Profile 表的 uuid 字段为主键,查询命中主键索引,具备高效率。
|
||||
- Profile 表的 name 字段具有唯一索引,有助于去重与唯一性约束。
|
||||
- Profile 表的 user_id 字段具有普通索引,有利于按用户维度查询。
|
||||
- Profile 表的 is_active 字段具有索引,便于筛选活跃档案。
|
||||
- Texture 表的 hash 字段具有唯一索引,有利于快速定位材质。
|
||||
- Texture 表的 uploader_id、is_public、favorite_count、download_count 等字段具有索引,便于检索与排序。
|
||||
- 关联预加载
|
||||
- 仓库层在查询档案时使用预加载(Preload)加载 Skin 与 Cape,减少 N+1 查询风险,提升响应速度。
|
||||
- 数据库连接池
|
||||
- PostgreSQL 驱动初始化时配置了连接池参数(最大空闲连接、最大打开连接、连接最大生命周期),有助于并发场景下的稳定性与吞吐量。
|
||||
- 缓存建议
|
||||
- 对高频查询的档案详情可引入缓存(如Redis),以进一步降低数据库压力。
|
||||
- 缓存键可采用 profile:{uuid},并设置合理过期时间(如几分钟)。
|
||||
- 日志与监控
|
||||
- 处理器层记录错误日志,便于定位慢查询与异常。
|
||||
- 建议增加指标埋点(如QPS、P95/P99延迟、错误率)以便持续优化。
|
||||
|
||||
章节来源
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31)
|
||||
- [profile.go](file://internal/model/profile.go#L7-L24)
|
||||
- [texture.go](file://internal/model/texture.go#L16-L35)
|
||||
- [postgres.go](file://pkg/database/postgres.go#L13-L74)
|
||||
|
||||
## 故障排查指南
|
||||
- 404 档案不存在
|
||||
- 现象:请求返回 404,消息提示“档案不存在”。
|
||||
- 排查:确认 uuid 是否正确;检查数据库中是否存在该 uuid 的档案记录。
|
||||
- 500 服务器内部错误
|
||||
- 现象:请求返回 500,消息提示服务层或仓库层错误。
|
||||
- 排查:查看处理器层日志;检查数据库连接与连接池配置;确认 GORM 查询是否报错。
|
||||
- 响应缺少皮肤/披风信息
|
||||
- 现象:textures 字段为空或部分缺失。
|
||||
- 排查:确认档案是否绑定了皮肤/披风;检查关联表是否存在有效记录;确认仓库层预加载是否生效。
|
||||
- Yggdrasil集成问题
|
||||
- 现象:客户端无法显示皮肤/披风。
|
||||
- 排查:确认客户端调用的Yggdrasil端点与本系统公开详情查询端点是否一致;核对响应结构与字段命名是否匹配。
|
||||
|
||||
章节来源
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L153-L195)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
## 结论
|
||||
GET /api/v1/profile/:uuid 作为公开端点,提供了基于Minecraft档案UUID的详情查询能力。其架构清晰、职责分明:路由层公开、处理器层编排、服务层封装、仓库层持久化、模型层定义。响应体包含档案基础信息与皮肤/披风信息,满足客户端在Yggdrasil协议下的集成需求。通过合理的数据库索引、关联预加载与连接池配置,可在高并发场景下保持稳定与高效。建议结合缓存与监控体系,持续优化查询性能与用户体验。
|
||||
Reference in New Issue
Block a user