chore(git): 更新.gitignore以忽略新的本地文件
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled

This commit is contained in:
lan
2025-11-30 08:33:17 +08:00
parent 4b4980820f
commit a4b6c5011e
58 changed files with 19353 additions and 0 deletions

View 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)

View 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 提供基础 CRUDSetActiveProfile 使用事务确保“同一用户仅有一个活跃档案”。
章节来源
- [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 已具备完善的权限校验与存在性验证机制。
- 更新操作支持名称与材质字段的灵活更新,删除操作简洁可靠。
- 服务层与仓储层清晰分离职责,当前更新与删除为单步操作;若未来扩展为多步更新,建议引入事务以保障一致性。
- 建议在数据库层面完善唯一性约束,并在服务层增加关键步骤的日志埋点,以便于问题定位与性能优化。

View 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如模型类型
- 请求/响应类型
- CreateProfileRequestname必填1-16字符
- UpdateProfileRequestname可选、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)

View 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。该设计既满足业务需求又通过数据库层的事务保障了原子性与可靠性。建议在生产环境中关注索引与连接池配置以进一步优化性能与稳定性。

View 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无需认证。
- 请求参数:路径参数 uuidMinecraft档案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
- nameMinecraft角色名
- 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协议下的集成需求。通过合理的数据库索引、关联预加载与连接池配置可在高并发场景下保持稳定与高效。建议结合缓存与监控体系持续优化查询性能与用户体验。