473 lines
19 KiB
Markdown
473 lines
19 KiB
Markdown
|
|
# 档案模型
|
|||
|
|
|
|||
|
|
<cite>
|
|||
|
|
**本文引用的文件**
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go)
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go)
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go)
|
|||
|
|
- [internal/types/common.go](file://internal/types/common.go)
|
|||
|
|
- [internal/service/profile_service_test.go](file://internal/service/profile_service_test.go)
|
|||
|
|
</cite>
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
1. [简介](#简介)
|
|||
|
|
2. [项目结构](#项目结构)
|
|||
|
|
3. [核心组件](#核心组件)
|
|||
|
|
4. [架构总览](#架构总览)
|
|||
|
|
5. [详细组件分析](#详细组件分析)
|
|||
|
|
6. [依赖分析](#依赖分析)
|
|||
|
|
7. [性能考虑](#性能考虑)
|
|||
|
|
8. [故障排查指南](#故障排查指南)
|
|||
|
|
9. [结论](#结论)
|
|||
|
|
10. [附录](#附录)
|
|||
|
|
|
|||
|
|
## 简介
|
|||
|
|
本文件围绕Minecraft档案模型进行系统化技术文档整理,重点覆盖以下主题:
|
|||
|
|
- Profile结构体的核心字段:UUID、Name、SkinID、CapeID以及RSA密钥对的安全存储机制
|
|||
|
|
- 档案激活状态(IsActive)与最后使用时间(LastUsedAt)的业务意义
|
|||
|
|
- 与用户(User)、皮肤(Texture)的外键关联关系及其在Yggdrasil协议中的作用
|
|||
|
|
- ProfileResponse响应结构的设计原理,包括Textures数据的嵌套格式与元数据(metadata)中模型类型(slim/classic)的表示方式
|
|||
|
|
- UUID命名规范、角色名唯一性约束以及密钥轮换策略的技术说明,并结合实际API响应示例进行说明
|
|||
|
|
|
|||
|
|
## 项目结构
|
|||
|
|
本项目采用分层架构,档案模型位于内部模型层,服务层负责业务流程编排,仓储层负责数据持久化,类型定义用于请求/响应契约与校验。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
subgraph "模型层"
|
|||
|
|
M_Profile["Profile<br/>profiles 表"]
|
|||
|
|
M_Texture["Texture<br/>textures 表"]
|
|||
|
|
M_User["User<br/>user 表"]
|
|||
|
|
M_Ygg["Yggdrasil<br/>Yggdrasil 表"]
|
|||
|
|
end
|
|||
|
|
subgraph "服务层"
|
|||
|
|
S_Profile["ProfileService<br/>档案业务逻辑"]
|
|||
|
|
S_Ygg["YggdrasilService<br/>Yggdrasil协议集成"]
|
|||
|
|
end
|
|||
|
|
subgraph "仓储层"
|
|||
|
|
R_Profile["ProfileRepository<br/>档案数据访问"]
|
|||
|
|
end
|
|||
|
|
M_Profile --> M_User
|
|||
|
|
M_Profile --> M_Texture
|
|||
|
|
S_Profile --> R_Profile
|
|||
|
|
S_Ygg --> R_Profile
|
|||
|
|
S_Ygg --> M_Profile
|
|||
|
|
S_Ygg --> M_Ygg
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go#L1-L49)
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go#L1-L49)
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
|||
|
|
|
|||
|
|
## 核心组件
|
|||
|
|
- Profile:Minecraft档案实体,包含UUID、角色名、皮肤/披风ID、RSA私钥、激活状态、最后使用时间等字段,并与User、Texture建立关联。
|
|||
|
|
- ProfileResponse:对外响应结构,包含UUID、角色名、Textures(含皮肤/披风URL与metadata模型类型)、IsActive、LastUsedAt、CreatedAt。
|
|||
|
|
- KeyPair:密钥对结构,包含私钥、公钥、过期时间、刷新时间,用于安全存储与轮换。
|
|||
|
|
- Texture:材质实体,支持皮肤/披风类型、URL、哈希、是否公开、下载/收藏计数、是否slim等属性。
|
|||
|
|
- Yggdrasil:与用户绑定的Yggdrasil密码实体,用于协议认证与会话管理。
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go#L1-L49)
|
|||
|
|
|
|||
|
|
## 架构总览
|
|||
|
|
档案模型贯穿“模型-服务-仓储-外部协议”的完整链路。服务层负责创建/更新/删除档案、设置活跃档案、更新最后使用时间、生成RSA密钥对;仓储层负责数据库读写与事务控制;模型层定义表结构与关联;类型层定义请求/响应契约;Yggdrasil服务层负责与外部协议交互(如会话数据存储与校验)。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Client as "客户端"
|
|||
|
|
participant Handler as "处理器"
|
|||
|
|
participant Service as "ProfileService"
|
|||
|
|
participant Repo as "ProfileRepository"
|
|||
|
|
participant DB as "数据库"
|
|||
|
|
participant Ygg as "YggdrasilService"
|
|||
|
|
Client->>Handler : "创建档案/更新档案/设置活跃档案"
|
|||
|
|
Handler->>Service : "调用业务方法"
|
|||
|
|
Service->>Repo : "查询/更新/事务"
|
|||
|
|
Repo->>DB : "执行SQL/GORM操作"
|
|||
|
|
DB-->>Repo : "返回结果"
|
|||
|
|
Repo-->>Service : "返回实体/影响行数"
|
|||
|
|
Service-->>Handler : "返回业务结果"
|
|||
|
|
Handler-->>Client : "返回ProfileResponse/错误"
|
|||
|
|
Note over Service,DB : "设置活跃档案时,服务层会调用仓储层更新最后使用时间"
|
|||
|
|
Service->>Ygg : "JoinServer/HasJoinedServer与Yggdrasil协议交互"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
|||
|
|
|
|||
|
|
## 详细组件分析
|
|||
|
|
|
|||
|
|
### Profile结构体与外键关联
|
|||
|
|
- 字段说明
|
|||
|
|
- UUID:档案唯一标识,主键,长度为36(标准UUID格式)
|
|||
|
|
- Name:角色名,最大16字符,全局唯一索引
|
|||
|
|
- SkinID/CapeID:指向Texture表的外键,允许为空
|
|||
|
|
- RSAPrivateKey:RSA私钥(PEM格式),不返回给前端
|
|||
|
|
- IsActive:是否为当前活跃档案,默认true,带索引
|
|||
|
|
- LastUsedAt:最后使用时间,用于统计与排序
|
|||
|
|
- CreatedAt/UpdatedAt:记录创建与更新时间戳
|
|||
|
|
- 关联关系
|
|||
|
|
- Profile.UserID -> User.ID
|
|||
|
|
- Profile.SkinID -> Texture.ID
|
|||
|
|
- Profile.CapeID -> Texture.ID
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
classDiagram
|
|||
|
|
class Profile {
|
|||
|
|
+string UUID
|
|||
|
|
+int64 UserID
|
|||
|
|
+string Name
|
|||
|
|
+int64* SkinID
|
|||
|
|
+int64* CapeID
|
|||
|
|
+string RSAPrivateKey
|
|||
|
|
+bool IsActive
|
|||
|
|
+time.Time* LastUsedAt
|
|||
|
|
+time.Time CreatedAt
|
|||
|
|
+time.Time UpdatedAt
|
|||
|
|
}
|
|||
|
|
class User {
|
|||
|
|
+int64 ID
|
|||
|
|
+string Username
|
|||
|
|
+string Email
|
|||
|
|
+string Role
|
|||
|
|
+int16 Status
|
|||
|
|
+time.Time* LastLoginAt
|
|||
|
|
+time.Time CreatedAt
|
|||
|
|
+time.Time UpdatedAt
|
|||
|
|
}
|
|||
|
|
class Texture {
|
|||
|
|
+int64 ID
|
|||
|
|
+int64 UploaderID
|
|||
|
|
+string Name
|
|||
|
|
+string URL
|
|||
|
|
+string Hash
|
|||
|
|
+bool IsPublic
|
|||
|
|
+int DownloadCount
|
|||
|
|
+int FavoriteCount
|
|||
|
|
+bool IsSlim
|
|||
|
|
+int16 Status
|
|||
|
|
+time.Time CreatedAt
|
|||
|
|
+time.Time UpdatedAt
|
|||
|
|
}
|
|||
|
|
Profile --> User : "外键 UserID"
|
|||
|
|
Profile --> Texture : "外键 SkinID"
|
|||
|
|
Profile --> Texture : "外键 CapeID"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
|
|||
|
|
### ProfileResponse响应结构设计
|
|||
|
|
- 结构组成
|
|||
|
|
- uuid/name/is_active/last_used_at/created_at:基础档案信息
|
|||
|
|
- textures:包含皮肤与披风两个子项
|
|||
|
|
- SKIN/CAPE:每个项包含url与metadata
|
|||
|
|
- metadata.model:取值为"slim"或"classic",用于指示模型类型
|
|||
|
|
- 设计原则
|
|||
|
|
- 以Yggdrasil协议兼容为目标,textures字段直接映射皮肤/披风资源与元数据
|
|||
|
|
- 通过枚举化的model字段明确区分Alex(细臂)与Steve(粗臂)模型
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
classDiagram
|
|||
|
|
class ProfileResponse {
|
|||
|
|
+string uuid
|
|||
|
|
+string name
|
|||
|
|
+ProfileTexturesData textures
|
|||
|
|
+bool is_active
|
|||
|
|
+time.Time* last_used_at
|
|||
|
|
+time.Time created_at
|
|||
|
|
}
|
|||
|
|
class ProfileTexturesData {
|
|||
|
|
+ProfileTexture* SKIN
|
|||
|
|
+ProfileTexture* CAPE
|
|||
|
|
}
|
|||
|
|
class ProfileTexture {
|
|||
|
|
+string url
|
|||
|
|
+ProfileTextureMetadata* metadata
|
|||
|
|
}
|
|||
|
|
class ProfileTextureMetadata {
|
|||
|
|
+string model
|
|||
|
|
}
|
|||
|
|
ProfileResponse --> ProfileTexturesData
|
|||
|
|
ProfileTexturesData --> ProfileTexture
|
|||
|
|
ProfileTexture --> ProfileTextureMetadata
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L31-L64)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L31-L64)
|
|||
|
|
|
|||
|
|
### RSA密钥对的安全存储机制
|
|||
|
|
- 生成与存储
|
|||
|
|
- 服务层在创建档案时生成RSA-2048私钥(PEM格式),并保存至Profile.RSAPrivateKey字段
|
|||
|
|
- 私钥不返回给前端,避免泄露风险
|
|||
|
|
- 读取与轮换
|
|||
|
|
- 仓储层提供GetProfileKeyPair/UpdateProfileKeyPair接口,支持从数据库读取与更新密钥对
|
|||
|
|
- KeyPair结构体包含私钥、公钥、过期时间、刷新时间,便于后续密钥轮换策略落地
|
|||
|
|
- 安全建议
|
|||
|
|
- 建议在密钥过期前主动轮换,更新数据库中的密钥对并同步到缓存/内存
|
|||
|
|
- 对敏感字段进行最小暴露,仅在必要时解密或传输
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
Start(["开始"]) --> Gen["生成RSA-2048私钥PEM"]
|
|||
|
|
Gen --> Save["保存至Profile.RSAPrivateKey"]
|
|||
|
|
Save --> Use["对外响应不返回私钥"]
|
|||
|
|
Use --> Rotate{"是否需要轮换?"}
|
|||
|
|
Rotate --> |否| End(["结束"])
|
|||
|
|
Rotate --> |是| Update["更新数据库中的密钥对"]
|
|||
|
|
Update --> End
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L204-L220)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L139-L199)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L204-L220)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L139-L199)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
|
|||
|
|
### 档案激活状态与最后使用时间的业务意义
|
|||
|
|
- IsActive
|
|||
|
|
- 用于标记当前用户所选中的活跃档案,同一用户下仅有一个档案处于活跃状态
|
|||
|
|
- 设置活跃档案时,服务层会将该用户其他档案置为非活跃
|
|||
|
|
- LastUsedAt
|
|||
|
|
- 每当设置活跃档案时,服务层会更新该字段为当前时间
|
|||
|
|
- 用于统计与排序,帮助用户快速识别最近使用的档案
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Client as "客户端"
|
|||
|
|
participant Service as "ProfileService"
|
|||
|
|
participant Repo as "ProfileRepository"
|
|||
|
|
participant DB as "数据库"
|
|||
|
|
Client->>Service : "设置活跃档案"
|
|||
|
|
Service->>Repo : "将用户其他档案置为非活跃"
|
|||
|
|
Repo->>DB : "UPDATE profiles SET is_active=false WHERE user_id=?"
|
|||
|
|
Service->>Repo : "将目标档案置为活跃"
|
|||
|
|
Repo->>DB : "UPDATE profiles SET is_active=true WHERE uuid=? AND user_id=?"
|
|||
|
|
Service->>Repo : "更新最后使用时间"
|
|||
|
|
Repo->>DB : "UPDATE profiles SET last_used_at=CURRENT_TIMESTAMP WHERE uuid=?"
|
|||
|
|
DB-->>Repo : "返回影响行数"
|
|||
|
|
Repo-->>Service : "返回成功"
|
|||
|
|
Service-->>Client : "返回成功"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L161-L188)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L161-L188)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L89-L117)
|
|||
|
|
|
|||
|
|
### 与用户(User)与皮肤(Texture)的外键关联关系
|
|||
|
|
- Profile.UserID -> User.ID
|
|||
|
|
- 一对多:一个用户可拥有多个档案
|
|||
|
|
- Profile.SkinID/CapeID -> Texture.ID
|
|||
|
|
- 多对一:一个档案可关联到一张皮肤与一张披风(均可为空)
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
erDiagram
|
|||
|
|
USER {
|
|||
|
|
int64 id PK
|
|||
|
|
string username UK
|
|||
|
|
string email UK
|
|||
|
|
string role
|
|||
|
|
int16 status
|
|||
|
|
timestamp last_login_at
|
|||
|
|
timestamp created_at
|
|||
|
|
timestamp updated_at
|
|||
|
|
}
|
|||
|
|
TEXTURE {
|
|||
|
|
int64 id PK
|
|||
|
|
int64 uploader_id FK
|
|||
|
|
string name
|
|||
|
|
string url
|
|||
|
|
string hash UK
|
|||
|
|
bool is_public
|
|||
|
|
int download_count
|
|||
|
|
int favorite_count
|
|||
|
|
bool is_slim
|
|||
|
|
int16 status
|
|||
|
|
timestamp created_at
|
|||
|
|
timestamp updated_at
|
|||
|
|
}
|
|||
|
|
PROFILE {
|
|||
|
|
string uuid PK
|
|||
|
|
int64 user_id FK
|
|||
|
|
string name UK
|
|||
|
|
int64* skin_id FK
|
|||
|
|
int64* cape_id FK
|
|||
|
|
text rsa_private_key
|
|||
|
|
bool is_active
|
|||
|
|
timestamp last_used_at
|
|||
|
|
timestamp created_at
|
|||
|
|
timestamp updated_at
|
|||
|
|
}
|
|||
|
|
USER ||--o{ PROFILE : "拥有"
|
|||
|
|
TEXTURE ||--o{ PROFILE : "被使用"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
|
|||
|
|
### 在Yggdrasil协议中的作用
|
|||
|
|
- Profile与Yggdrasil的关系
|
|||
|
|
- ProfileResponse中的textures字段用于向客户端提供皮肤/披风资源与模型元数据,满足Yggdrasil协议对纹理与模型类型的要求
|
|||
|
|
- Yggdrasil实体与User存在一对一关联,用于协议认证与会话管理
|
|||
|
|
- 会话与验证
|
|||
|
|
- 服务层提供JoinServer/HasJoinedServer方法,将会话数据写入Redis并进行用户名/IP校验,确保玩家加入服务器的合法性
|
|||
|
|
- 会话数据包含accessToken、userName、selectedProfile、ip等关键字段
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Client as "客户端"
|
|||
|
|
participant YggSvc as "YggdrasilService"
|
|||
|
|
participant TokenRepo as "TokenRepository"
|
|||
|
|
participant ProfRepo as "ProfileRepository"
|
|||
|
|
participant Redis as "Redis"
|
|||
|
|
Client->>YggSvc : "JoinServer(serverId, accessToken, selectedProfile, ip)"
|
|||
|
|
YggSvc->>TokenRepo : "根据accessToken查询Token"
|
|||
|
|
TokenRepo-->>YggSvc : "返回Token"
|
|||
|
|
YggSvc->>ProfRepo : "根据ProfileId查询Profile"
|
|||
|
|
ProfRepo-->>YggSvc : "返回Profile"
|
|||
|
|
YggSvc->>Redis : "写入会话数据Join_前缀+serverId"
|
|||
|
|
Redis-->>YggSvc : "成功"
|
|||
|
|
YggSvc-->>Client : "返回成功"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L163)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go#L1-L49)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go#L1-L49)
|
|||
|
|
|
|||
|
|
### UUID命名规范、角色名唯一性约束与密钥轮换策略
|
|||
|
|
- UUID命名规范
|
|||
|
|
- Profile.UUID为主键,采用标准36字符格式(包含连字符)
|
|||
|
|
- 服务层在创建档案时使用标准库生成新UUID
|
|||
|
|
- 角色名唯一性约束
|
|||
|
|
- Profile.Name具有唯一索引,服务层在创建/更新时均进行冲突检测
|
|||
|
|
- 请求/响应契约中对名称长度有严格限制(1-16字符)
|
|||
|
|
- 密钥轮换策略
|
|||
|
|
- KeyPair结构体提供过期时间与刷新时间字段,便于实现周期性轮换
|
|||
|
|
- 建议在过期前主动生成新密钥对并更新数据库,同时同步到缓存/内存,确保服务可用性
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
- [internal/types/common.go](file://internal/types/common.go#L181-L206)
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L18-L69)
|
|||
|
|
- [internal/service/profile_service_test.go](file://internal/service/profile_service_test.go#L348-L406)
|
|||
|
|
|
|||
|
|
## 依赖分析
|
|||
|
|
- 组件耦合
|
|||
|
|
- ProfileService高度依赖ProfileRepository与数据库/GORM
|
|||
|
|
- ProfileRepository对数据库连接与事务有直接依赖
|
|||
|
|
- YggdrasilService依赖Redis与TokenRepository,间接依赖ProfileRepository
|
|||
|
|
- 关联关系
|
|||
|
|
- Profile与User/Texture通过GORM外键注解建立关联
|
|||
|
|
- ProfileResponse与Profile/Texture的嵌套结构映射清晰,便于序列化/反序列化
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph LR
|
|||
|
|
ProfileService --> ProfileRepository
|
|||
|
|
ProfileRepository --> Database["GORM/PostgreSQL"]
|
|||
|
|
YggdrasilService --> Redis["Redis"]
|
|||
|
|
YggdrasilService --> ProfileRepository
|
|||
|
|
Profile --> User
|
|||
|
|
Profile --> Texture
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L1-L253)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/service/yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
|||
|
|
|
|||
|
|
## 性能考虑
|
|||
|
|
- 索引优化
|
|||
|
|
- Profile.Name具备唯一索引,减少重复角色名查询成本
|
|||
|
|
- Profile.UserID与IsActive具备索引,提升活跃档案切换与查询效率
|
|||
|
|
- 预加载策略
|
|||
|
|
- 仓储层在查询档案时预加载Skin/Cape关联,避免N+1查询
|
|||
|
|
- 事务与一致性
|
|||
|
|
- 设置活跃档案采用事务,确保原子性与一致性
|
|||
|
|
- 缓存与会话
|
|||
|
|
- Yggdrasil会话数据写入Redis,降低频繁查询数据库的压力
|
|||
|
|
|
|||
|
|
## 故障排查指南
|
|||
|
|
- 常见错误与定位
|
|||
|
|
- 角色名冲突:创建/更新时若返回“角色名已被使用”,检查Profile.Name唯一性约束与服务层校验逻辑
|
|||
|
|
- 权限不足:操作他人档案会返回“无权操作此档案”,检查服务层对Profile.UserID与传入userID的比对
|
|||
|
|
- 档案不存在:查询/更新/删除时若返回“档案不存在”,检查UUID格式与仓储层查询条件
|
|||
|
|
- 密钥读取失败:GetProfileKeyPair返回“未找到”或错误,检查数据库字段映射与查询条件
|
|||
|
|
- 调试建议
|
|||
|
|
- 在服务层与仓储层增加日志输出,定位具体环节(查询、更新、事务)
|
|||
|
|
- 使用单元测试验证请求/响应契约与边界条件(名称长度、UUID格式、密钥PEM格式)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/service/profile_service.go](file://internal/service/profile_service.go#L71-L159)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L139-L199)
|
|||
|
|
- [internal/service/profile_service_test.go](file://internal/service/profile_service_test.go#L348-L406)
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
本档案模型围绕Profile为核心,结合ProfileResponse的纹理与元数据设计,满足Minecraft Yggdrasil协议对皮肤/披风与模型类型的要求。通过严格的唯一性约束、索引优化与事务保障,确保了数据一致性与性能。RSA密钥对的安全存储与KeyPair结构为后续密钥轮换提供了基础。与User/Texture的外键关联清晰地刻画了用户与材质的使用关系,配合Yggdrasil服务层的会话管理,形成完整的档案生命周期闭环。
|
|||
|
|
|
|||
|
|
## 附录
|
|||
|
|
- API响应示例(概念性说明)
|
|||
|
|
- ProfileResponse示例包含uuid、name、textures(含SKIN/CAPE)、is_active、last_used_at、created_at等字段
|
|||
|
|
- textures.metadata.model取值为"slim"或"classic",用于指示模型类型
|
|||
|
|
- 请求/响应契约要点
|
|||
|
|
- UpdateProfileRequest对name长度与skin_id/cape_id进行约束
|
|||
|
|
- CreateProfileRequest对角色名长度与类型进行约束
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L31-L64)
|
|||
|
|
- [internal/types/common.go](file://internal/types/common.go#L181-L206)
|