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

398 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# 创建与列表
<cite>
**本文引用的文件**
- [routes.go](file://internal/handler/routes.go)
- [profile_handler.go](file://internal/handler/profile_handler.go)
- [profile_service.go](file://internal/service/profile_service.go)
- [profile_repository.go](file://internal/repository/profile_repository.go)
- [profile.go](file://internal/model/profile.go)
- [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)