546 lines
21 KiB
Markdown
546 lines
21 KiB
Markdown
|
|
# 数据模型
|
|||
|
|
|
|||
|
|
<cite>
|
|||
|
|
**本文引用的文件**
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go)
|
|||
|
|
- [internal/model/system_config.go](file://internal/model/system_config.go)
|
|||
|
|
- [internal/model/audit_log.go](file://internal/model/audit_log.go)
|
|||
|
|
- [internal/model/token.go](file://internal/model/token.go)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go)
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go)
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go)
|
|||
|
|
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go)
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go)
|
|||
|
|
- [pkg/database/postgres.go](file://pkg/database/postgres.go)
|
|||
|
|
</cite>
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
1. [简介](#简介)
|
|||
|
|
2. [项目结构](#项目结构)
|
|||
|
|
3. [核心组件](#核心组件)
|
|||
|
|
4. [架构总览](#架构总览)
|
|||
|
|
5. [详细组件分析](#详细组件分析)
|
|||
|
|
6. [依赖分析](#依赖分析)
|
|||
|
|
7. [性能考量](#性能考量)
|
|||
|
|
8. [故障排查指南](#故障排查指南)
|
|||
|
|
9. [结论](#结论)
|
|||
|
|
10. [附录](#附录)
|
|||
|
|
|
|||
|
|
## 简介
|
|||
|
|
本文件面向 CarrotSkin 项目的数据库层,聚焦于核心实体 User、Texture、Profile 和 SystemConfig 的数据模型设计。内容涵盖字段定义、数据类型、主键/外键关系、索引与约束、业务语义与验证规则,并提供实体关系图(ERD)、示例数据与数据访问模式说明,帮助初学者快速理解,同时为有经验的开发者提供性能优化与迁移策略建议。
|
|||
|
|
|
|||
|
|
## 项目结构
|
|||
|
|
围绕数据模型的关键目录与文件:
|
|||
|
|
- 模型层:internal/model 下的各实体模型文件
|
|||
|
|
- 仓储层:internal/repository 下的 CRUD 与聚合查询实现
|
|||
|
|
- 数据库层:pkg/database 提供连接、迁移与连接池管理
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
subgraph "模型层"
|
|||
|
|
MUser["User<br/>internal/model/user.go"]
|
|||
|
|
MTexture["Texture<br/>internal/model/texture.go"]
|
|||
|
|
MProfile["Profile<br/>internal/model/profile.go"]
|
|||
|
|
MSystemConfig["SystemConfig<br/>internal/model/system_config.go"]
|
|||
|
|
MAudit["AuditLog<br/>internal/model/audit_log.go"]
|
|||
|
|
MToken["Token<br/>internal/model/token.go"]
|
|||
|
|
MYgg["Yggdrasil<br/>internal/model/yggdrasil.go"]
|
|||
|
|
end
|
|||
|
|
subgraph "仓储层"
|
|||
|
|
RUser["UserRepository<br/>internal/repository/user_repository.go"]
|
|||
|
|
RTexture["TextureRepository<br/>internal/repository/texture_repository.go"]
|
|||
|
|
RProfile["ProfileRepository<br/>internal/repository/profile_repository.go"]
|
|||
|
|
RSystem["SystemConfigRepository<br/>internal/repository/system_config_repository.go"]
|
|||
|
|
end
|
|||
|
|
subgraph "数据库层"
|
|||
|
|
DManager["Database Manager<br/>pkg/database/manager.go"]
|
|||
|
|
DPostgres["Postgres Driver<br/>pkg/database/postgres.go"]
|
|||
|
|
end
|
|||
|
|
MUser --> RUser
|
|||
|
|
MTexture --> RTexture
|
|||
|
|
MProfile --> RProfile
|
|||
|
|
MSystemConfig --> RSystem
|
|||
|
|
RUser --> DManager
|
|||
|
|
RTexture --> DManager
|
|||
|
|
RProfile --> DManager
|
|||
|
|
RSystem --> DManager
|
|||
|
|
DManager --> DPostgres
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [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/system_config.go](file://internal/model/system_config.go#L1-L42)
|
|||
|
|
- [internal/model/audit_log.go](file://internal/model/audit_log.go#L1-L46)
|
|||
|
|
- [internal/model/token.go](file://internal/model/token.go#L1-L15)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go#L1-L48)
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go#L1-L114)
|
|||
|
|
- [pkg/database/postgres.go](file://pkg/database/postgres.go#L1-L74)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go#L52-L99)
|
|||
|
|
- [pkg/database/postgres.go](file://pkg/database/postgres.go#L13-L60)
|
|||
|
|
|
|||
|
|
## 核心组件
|
|||
|
|
本节对四个核心实体进行字段级说明,包括数据类型、约束、索引与业务含义。
|
|||
|
|
|
|||
|
|
- User(用户)
|
|||
|
|
- 主键:id(自增整数)
|
|||
|
|
- 唯一索引:username、email
|
|||
|
|
- 字段要点:
|
|||
|
|
- username:字符串,唯一,用于登录与标识
|
|||
|
|
- email:字符串,唯一,用于找回密码与通知
|
|||
|
|
- avatar:字符串,头像 URL
|
|||
|
|
- points:整数,积分余额,支持增减与日志追踪
|
|||
|
|
- role:字符串,默认“user”,用于权限控制
|
|||
|
|
- status:小整数,1 正常、0 禁用、-1 删除(软删除)
|
|||
|
|
- properties:JSONB,存储扩展属性
|
|||
|
|
- last_login_at:时间戳,最近登录时间
|
|||
|
|
- created_at/updated_at:时间戳,默认 CURRENT_TIMESTAMP
|
|||
|
|
- 业务规则:
|
|||
|
|
- 软删除通过 status 字段实现
|
|||
|
|
- 登录日志与积分日志分别记录在 user_login_logs 与 user_point_logs
|
|||
|
|
|
|||
|
|
- Texture(材质)
|
|||
|
|
- 主键:id(自增整数)
|
|||
|
|
- 外键:uploader_id → User.id
|
|||
|
|
- 唯一索引:hash(SHA-256)
|
|||
|
|
- 字段要点:
|
|||
|
|
- uploader_id:整数,上传者
|
|||
|
|
- name/description:名称与描述
|
|||
|
|
- type:枚举,SKIN 或 CAPE
|
|||
|
|
- url:字符串,材质资源地址
|
|||
|
|
- hash:字符串(64),唯一,用于去重
|
|||
|
|
- size:整数,字节数
|
|||
|
|
- is_public:布尔,是否公开
|
|||
|
|
- download_count/favorite_count:整数,统计指标,带索引
|
|||
|
|
- is_slim:布尔,是否 Alex(细)模型
|
|||
|
|
- status:小整数,1 正常、0 审核中、-1 删除(软删除)
|
|||
|
|
- created_at/updated_at:时间戳
|
|||
|
|
- 业务规则:
|
|||
|
|
- is_public + type + status 组合索引用于检索
|
|||
|
|
- 下载与收藏计数采用表达式更新,避免并发竞争导致的丢失更新
|
|||
|
|
|
|||
|
|
- Profile(档案)
|
|||
|
|
- 主键:uuid(字符串,36 位)
|
|||
|
|
- 外键:user_id → User.id
|
|||
|
|
- 唯一索引:name(角色名)
|
|||
|
|
- 字段要点:
|
|||
|
|
- uuid:档案 UUID
|
|||
|
|
- user_id:整数,所属用户
|
|||
|
|
- name:字符串,Minecraft 角色名,唯一
|
|||
|
|
- skin_id/cape_id:整数,关联 Texture.id
|
|||
|
|
- rsa_private_key:文本,RSA 私钥(不对外返回)
|
|||
|
|
- is_active:布尔,是否为当前激活档案
|
|||
|
|
- last_used_at:时间戳,最近使用时间
|
|||
|
|
- created_at/updated_at:时间戳
|
|||
|
|
- 业务规则:
|
|||
|
|
- 激活档案切换时,同一用户下其他档案会被置为非激活
|
|||
|
|
- 支持预加载 Skin/Cape 关联实体
|
|||
|
|
|
|||
|
|
- SystemConfig(系统配置)
|
|||
|
|
- 主键:id(自增整数)
|
|||
|
|
- 唯一索引:key
|
|||
|
|
- 字段要点:
|
|||
|
|
- key:字符串,配置键,唯一
|
|||
|
|
- value:文本,配置值
|
|||
|
|
- description:字符串,描述
|
|||
|
|
- type:枚举,STRING/INTEGER/BOOLEAN/JSON
|
|||
|
|
- is_public:布尔,是否允许前端读取
|
|||
|
|
- created_at/updated_at:时间戳
|
|||
|
|
- 业务规则:
|
|||
|
|
- is_public 为 true 的配置可作为公开响应的一部分返回
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L7-L21)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L28-L71)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L15-L40)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L42-L77)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L7-L29)
|
|||
|
|
- [internal/model/system_config.go](file://internal/model/system_config.go#L17-L32)
|
|||
|
|
|
|||
|
|
## 架构总览
|
|||
|
|
数据库层通过连接管理器统一初始化与迁移,模型定义通过 GORM 注解映射到 PostgreSQL 表结构。迁移顺序遵循“先被引用表,后引用表”的原则,确保外键约束可用。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant App as "应用启动"
|
|||
|
|
participant DBMgr as "Database Manager"
|
|||
|
|
participant PG as "Postgres Driver"
|
|||
|
|
participant ORM as "GORM"
|
|||
|
|
participant Repo as "Repositories"
|
|||
|
|
App->>DBMgr : Init(cfg, logger)
|
|||
|
|
DBMgr->>PG : New(cfg)
|
|||
|
|
PG-->>DBMgr : *gorm.DB
|
|||
|
|
DBMgr->>ORM : AutoMigrate(models...)
|
|||
|
|
ORM-->>DBMgr : 迁移完成
|
|||
|
|
App->>Repo : 使用仓储层进行CRUD
|
|||
|
|
Repo->>ORM : 执行查询/更新
|
|||
|
|
ORM-->>Repo : 结果
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go#L22-L99)
|
|||
|
|
- [pkg/database/postgres.go](file://pkg/database/postgres.go#L13-L60)
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
|
|||
|
|
|
|||
|
|
## 详细组件分析
|
|||
|
|
|
|||
|
|
### 实体关系图(ERD)
|
|||
|
|
以下 ERD 展示了 User、Texture、Profile、SystemConfig 的主键、外键与关键索引。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
erDiagram
|
|||
|
|
USER {
|
|||
|
|
bigint id PK
|
|||
|
|
varchar username UK
|
|||
|
|
varchar email UK
|
|||
|
|
varchar avatar
|
|||
|
|
integer points
|
|||
|
|
varchar role
|
|||
|
|
smallint status
|
|||
|
|
jsonb properties
|
|||
|
|
timestamp last_login_at
|
|||
|
|
timestamp created_at
|
|||
|
|
timestamp updated_at
|
|||
|
|
}
|
|||
|
|
TEXTURE {
|
|||
|
|
bigint id PK
|
|||
|
|
bigint uploader_id FK
|
|||
|
|
varchar name
|
|||
|
|
text description
|
|||
|
|
varchar type
|
|||
|
|
varchar url
|
|||
|
|
varchar hash UK
|
|||
|
|
integer size
|
|||
|
|
boolean is_public
|
|||
|
|
integer download_count
|
|||
|
|
integer favorite_count
|
|||
|
|
boolean is_slim
|
|||
|
|
smallint status
|
|||
|
|
timestamp created_at
|
|||
|
|
timestamp updated_at
|
|||
|
|
}
|
|||
|
|
PROFILE {
|
|||
|
|
varchar uuid PK
|
|||
|
|
bigint user_id FK
|
|||
|
|
varchar name UK
|
|||
|
|
bigint skin_id
|
|||
|
|
bigint cape_id
|
|||
|
|
text rsa_private_key
|
|||
|
|
boolean is_active
|
|||
|
|
timestamp last_used_at
|
|||
|
|
timestamp created_at
|
|||
|
|
timestamp updated_at
|
|||
|
|
}
|
|||
|
|
SYSTEM_CONFIG {
|
|||
|
|
bigint id PK
|
|||
|
|
varchar key UK
|
|||
|
|
text value
|
|||
|
|
varchar description
|
|||
|
|
varchar type
|
|||
|
|
boolean is_public
|
|||
|
|
timestamp created_at
|
|||
|
|
timestamp updated_at
|
|||
|
|
}
|
|||
|
|
USER ||--o{ TEXTURE : "uploader_id -> id"
|
|||
|
|
USER ||--o{ PROFILE : "user_id -> id"
|
|||
|
|
TEXTURE ||--o{ PROFILE : "skin_id/cape_id -> id"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L7-L21)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L15-L40)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L7-L29)
|
|||
|
|
- [internal/model/system_config.go](file://internal/model/system_config.go#L17-L32)
|
|||
|
|
|
|||
|
|
### User 模型与访问模式
|
|||
|
|
- 字段与约束
|
|||
|
|
- 主键:id
|
|||
|
|
- 唯一索引:username、email
|
|||
|
|
- 状态软删除:status=-1 表示删除
|
|||
|
|
- JSONB 扩展属性:properties
|
|||
|
|
- 关联与日志
|
|||
|
|
- UserPointLog:记录积分变动,含 operator_id 关联操作人
|
|||
|
|
- UserLoginLog:记录登录来源与结果
|
|||
|
|
- 仓储能力
|
|||
|
|
- 基础 CRUD、按用户名/邮箱查询、软删除
|
|||
|
|
- 事务内更新积分并写入日志
|
|||
|
|
- 更新头像、邮箱等字段
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Svc as "服务层"
|
|||
|
|
participant Repo as "UserRepository"
|
|||
|
|
participant DB as "GORM"
|
|||
|
|
participant Log as "UserPointLog"
|
|||
|
|
Svc->>Repo : UpdateUserPoints(userID, amount, type, reason)
|
|||
|
|
Repo->>DB : 事务开始
|
|||
|
|
Repo->>DB : 查询用户当前积分
|
|||
|
|
Repo->>DB : 校验余额防止负值
|
|||
|
|
Repo->>DB : 更新用户积分
|
|||
|
|
Repo->>DB : 创建积分日志(Log)
|
|||
|
|
DB-->>Repo : 提交事务
|
|||
|
|
Repo-->>Svc : 返回结果
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L89-L124)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L28-L71)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L7-L71)
|
|||
|
|
|
|||
|
|
### Texture 模型与访问模式
|
|||
|
|
- 字段与约束
|
|||
|
|
- 主键:id;唯一索引:hash
|
|||
|
|
- 外键:uploader_id → User.id
|
|||
|
|
- 组合索引:is_public + type + status;download_count/favorite_count 带索引
|
|||
|
|
- 关联与日志
|
|||
|
|
- UserTextureFavorite:用户收藏材质(联合唯一索引 uk_user_texture)
|
|||
|
|
- TextureDownloadLog:下载记录
|
|||
|
|
- 仓储能力
|
|||
|
|
- 创建、按 ID/Hash 查询、分页与搜索(关键词、类型、公开度)
|
|||
|
|
- 软删除(status=-1)
|
|||
|
|
- 表达式更新下载/收藏计数,避免竞态
|
|||
|
|
- 收藏/取消收藏与查询用户收藏列表
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
Start(["搜索入口"]) --> BuildQuery["构建基础查询<br/>status=1"]
|
|||
|
|
BuildQuery --> Public{"仅公开?"}
|
|||
|
|
Public --> |是| ApplyPublic["追加 is_public=true"]
|
|||
|
|
Public --> |否| SkipPublic["跳过公开过滤"]
|
|||
|
|
ApplyPublic --> TypeFilter{"指定类型?"}
|
|||
|
|
SkipPublic --> TypeFilter
|
|||
|
|
TypeFilter --> |是| ApplyType["追加 type=?"]
|
|||
|
|
TypeFilter --> |否| KeywordFilter
|
|||
|
|
ApplyType --> KeywordFilter{"有关键词?"}
|
|||
|
|
KeywordFilter --> |是| ApplyKeyword["name/description LIKE %keyword%"]
|
|||
|
|
KeywordFilter --> |否| Paginate
|
|||
|
|
ApplyKeyword --> Paginate["COUNT + ORDER BY created_at DESC + 分页"]
|
|||
|
|
Paginate --> End(["返回结果"])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L71-L112)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L15-L40)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
|
|||
|
|
|
|||
|
|
### Profile 模型与访问模式
|
|||
|
|
- 字段与约束
|
|||
|
|
- 主键:uuid;唯一索引:name
|
|||
|
|
- 外键:user_id → User.id;skin_id/cape_id → Texture.id
|
|||
|
|
- is_active 控制当前生效档案
|
|||
|
|
- 仓储能力
|
|||
|
|
- 创建、按 uuid/name 查询、按用户查询全部档案
|
|||
|
|
- 设置激活档案(事务内将用户其他档案置为非激活)
|
|||
|
|
- 更新最后使用时间
|
|||
|
|
- KeyPair 的读写(JSONB 字段)
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Svc as "服务层"
|
|||
|
|
participant Repo as "ProfileRepository"
|
|||
|
|
participant DB as "GORM"
|
|||
|
|
Svc->>Repo : SetActiveProfile(uuid, userID)
|
|||
|
|
Repo->>DB : 事务开始
|
|||
|
|
Repo->>DB : 将用户所有档案 is_active=false
|
|||
|
|
Repo->>DB : 将指定档案 is_active=true
|
|||
|
|
DB-->>Repo : 提交事务
|
|||
|
|
Repo-->>Svc : 返回结果
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L89-L109)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L7-L29)
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
|
|||
|
|
|
|||
|
|
### SystemConfig 模型与访问模式
|
|||
|
|
- 字段与约束
|
|||
|
|
- 主键:id;唯一索引:key
|
|||
|
|
- 类型枚举:STRING/INTEGER/BOOLEAN/JSON
|
|||
|
|
- is_public 控制前端可见性
|
|||
|
|
- 仓储能力
|
|||
|
|
- 按 key 查询、获取公开配置、获取全部配置、更新值
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
|
|||
|
|
- [internal/model/system_config.go](file://internal/model/system_config.go#L1-L42)
|
|||
|
|
|
|||
|
|
### 其他相关模型与迁移顺序
|
|||
|
|
- AuditLog:审计日志,记录用户行为与资源变更,含 JSONB 字段与多维索引
|
|||
|
|
- Token:认证令牌模型(表名为 token),用于 Yggdrasil 等流程
|
|||
|
|
- Yggdrasil:与 User 一对一关联,User 创建后自动同步生成随机密码记录
|
|||
|
|
|
|||
|
|
迁移顺序(AutoMigrate):
|
|||
|
|
- 先创建被引用表:User、UserPointLog、UserLoginLog
|
|||
|
|
- 再创建引用表:Profile、Texture、UserTextureFavorite、TextureDownloadLog、Token、Yggdrasil
|
|||
|
|
- 最后创建 SystemConfig、AuditLog、CasbinRule
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/audit_log.go](file://internal/model/audit_log.go#L1-L46)
|
|||
|
|
- [internal/model/token.go](file://internal/model/token.go#L1-L15)
|
|||
|
|
- [internal/model/yggdrasil.go](file://internal/model/yggdrasil.go#L1-L48)
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go#L52-L99)
|
|||
|
|
|
|||
|
|
## 依赖分析
|
|||
|
|
- 模型到仓储:各模型通过 GORM 注解与仓储层交互,仓储层负责具体查询、更新与事务控制
|
|||
|
|
- 仓储到数据库:统一通过 MustGetDB 获取连接,避免重复初始化
|
|||
|
|
- 迁移顺序:AutoMigrate 明确列出迁移顺序,确保外键约束可用
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph LR
|
|||
|
|
MUser["User Model"] --> RUser["UserRepository"]
|
|||
|
|
MTexture["Texture Model"] --> RTexture["TextureRepository"]
|
|||
|
|
MProfile["Profile Model"] --> RProfile["ProfileRepository"]
|
|||
|
|
MSystem["SystemConfig Model"] --> RSystem["SystemConfigRepository"]
|
|||
|
|
RUser --> DMgr["Database Manager"]
|
|||
|
|
RTexture --> DMgr
|
|||
|
|
RProfile --> DMgr
|
|||
|
|
RSystem --> DMgr
|
|||
|
|
DMgr --> DPostgres["Postgres Driver"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
图表来源
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go#L35-L99)
|
|||
|
|
- [pkg/database/postgres.go](file://pkg/database/postgres.go#L13-L60)
|
|||
|
|
|
|||
|
|
## 性能考量
|
|||
|
|
- 索引策略
|
|||
|
|
- User:username、email 唯一索引;登录/积分日志按 created_at 倒序索引
|
|||
|
|
- Texture:hash 唯一索引;is_public + type + status 组合索引;download_count/favorite_count 带索引
|
|||
|
|
- Profile:name 唯一索引;user_id 索引
|
|||
|
|
- SystemConfig:key 唯一索引;is_public 索引
|
|||
|
|
- AuditLog:多维索引(action、resource_type+resource_id、created_at)
|
|||
|
|
- 并发与一致性
|
|||
|
|
- 使用表达式更新计数字段(download_count、favorite_count),避免竞态
|
|||
|
|
- 事务内更新用户积分并写入日志,保证原子性
|
|||
|
|
- 连接池与日志
|
|||
|
|
- 连接池参数由配置注入,建议结合负载压测调整 MaxOpenConns、MaxIdleConns、ConnMaxLifetime
|
|||
|
|
- 日志级别在 Postgres 驱动中按驱动类型配置
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/model/user.go](file://internal/model/user.go#L28-L71)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L15-L40)
|
|||
|
|
- [internal/model/texture.go](file://internal/model/texture.go#L42-L77)
|
|||
|
|
- [internal/model/profile.go](file://internal/model/profile.go#L7-L29)
|
|||
|
|
- [internal/model/system_config.go](file://internal/model/system_config.go#L17-L32)
|
|||
|
|
- [internal/model/audit_log.go](file://internal/model/audit_log.go#L7-L27)
|
|||
|
|
- [pkg/database/postgres.go](file://pkg/database/postgres.go#L13-L60)
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L89-L124)
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L132-L151)
|
|||
|
|
|
|||
|
|
## 故障排查指南
|
|||
|
|
- 数据库未初始化
|
|||
|
|
- 现象:调用 MustGetDB 抛错或 AutoMigrate 返回错误
|
|||
|
|
- 排查:确认已调用 Init(cfg, logger),检查配置项与连接可达性
|
|||
|
|
- 迁移失败
|
|||
|
|
- 现象:AutoMigrate 报错
|
|||
|
|
- 排查:检查迁移顺序是否正确(先被引用表,后引用表);确认数据库版本与驱动兼容
|
|||
|
|
- 查询不到记录
|
|||
|
|
- 现象:按 username/email/uuid 查询返回空
|
|||
|
|
- 排查:确认 status 非 -1;检查唯一索引是否冲突;确认大小写与格式
|
|||
|
|
- 并发计数不一致
|
|||
|
|
- 现象:download_count/favorite_count 不准确
|
|||
|
|
- 排查:确认使用表达式更新;避免直接赋值覆盖
|
|||
|
|
- 事务回滚
|
|||
|
|
- 现象:积分更新失败或日志未写入
|
|||
|
|
- 排查:检查事务内错误处理与提交路径
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go#L22-L33)
|
|||
|
|
- [pkg/database/manager.go](file://pkg/database/manager.go#L52-L99)
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L89-L124)
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L132-L151)
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
本数据模型围绕 User、Texture、Profile、SystemConfig 四大核心实体,采用 PostgreSQL + GORM 的组合实现,具备完善的索引与约束策略、清晰的迁移顺序与事务保障。通过仓储层抽象,实现了稳定的 CRUD 与复杂查询能力。建议在生产环境中结合监控与压测持续优化连接池与索引策略。
|
|||
|
|
|
|||
|
|
## 附录
|
|||
|
|
|
|||
|
|
### 示例数据(概念性)
|
|||
|
|
- User
|
|||
|
|
- id: 1
|
|||
|
|
- username: "alice"
|
|||
|
|
- email: "alice@example.com"
|
|||
|
|
- points: 100
|
|||
|
|
- role: "user"
|
|||
|
|
- status: 1
|
|||
|
|
- properties: "{}"
|
|||
|
|
- last_login_at: 当前时间
|
|||
|
|
- created_at/updated_at: 当前时间
|
|||
|
|
- Texture
|
|||
|
|
- id: 101
|
|||
|
|
- uploader_id: 1
|
|||
|
|
- name: "Steve Classic"
|
|||
|
|
- type: "SKIN"
|
|||
|
|
- url: "/uploads/skin_101.png"
|
|||
|
|
- hash: "sha256..."
|
|||
|
|
- size: 102400
|
|||
|
|
- is_public: true
|
|||
|
|
- download_count: 120
|
|||
|
|
- favorite_count: 45
|
|||
|
|
- is_slim: false
|
|||
|
|
- status: 1
|
|||
|
|
- created_at/updated_at: 当前时间
|
|||
|
|
- Profile
|
|||
|
|
- uuid: "f47ac10b-62d6-4c6f-8b3c-1234567890ab"
|
|||
|
|
- user_id: 1
|
|||
|
|
- name: "Steve"
|
|||
|
|
- skin_id: 101
|
|||
|
|
- cape_id: null
|
|||
|
|
- rsa_private_key: "..."
|
|||
|
|
- is_active: true
|
|||
|
|
- last_used_at: 当前时间
|
|||
|
|
- created_at/updated_at: 当前时间
|
|||
|
|
- SystemConfig
|
|||
|
|
- id: 1
|
|||
|
|
- key: "site_name"
|
|||
|
|
- value: "CarrotSkin"
|
|||
|
|
- description: "站点名称"
|
|||
|
|
- type: "STRING"
|
|||
|
|
- is_public: true
|
|||
|
|
- created_at/updated_at: 当前时间
|
|||
|
|
|
|||
|
|
### 数据访问模式清单
|
|||
|
|
- User
|
|||
|
|
- 创建/查询/更新/软删除
|
|||
|
|
- 事务内更新积分并写入日志
|
|||
|
|
- Texture
|
|||
|
|
- 创建/查询/按 Hash 去重/分页搜索/软删除
|
|||
|
|
- 表达式更新下载/收藏计数
|
|||
|
|
- 收藏/取消收藏与查询收藏列表
|
|||
|
|
- Profile
|
|||
|
|
- 创建/查询/按用户查询/设置激活档案/更新最后使用时间
|
|||
|
|
- KeyPair 读写(JSONB)
|
|||
|
|
- SystemConfig
|
|||
|
|
- 按 key 查询/获取公开配置/获取全部配置/更新值
|
|||
|
|
|
|||
|
|
章节来源
|
|||
|
|
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
|
|||
|
|
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
|
|||
|
|
- [internal/repository/profile_repository.go](file://internal/repository/profile_repository.go#L1-L200)
|
|||
|
|
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
|