Files
backend/.qoder/repowiki/zh/content/服务架构/材质服务.md
lan a4b6c5011e
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
chore(git): 更新.gitignore以忽略新的本地文件
2025-11-30 08:33:17 +08:00

462 lines
19 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>
**本文引用的文件**
- [internal/service/texture_service.go](file://internal/service/texture_service.go)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go)
- [internal/model/texture.go](file://internal/model/texture.go)
- [pkg/database/manager.go](file://pkg/database/manager.go)
- [internal/service/texture_service_test.go](file://internal/service/texture_service_test.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向开发者系统性梳理材质服务TextureService的职责、方法与内部逻辑重点覆盖
- 材质类型SKIN/CAPE校验与默认值设定状态、下载数、收藏数
- 材质状态管理(正常、禁用、删除)
- 上传、搜索、收藏等业务规则及服务层与仓储层的协作
- 状态验证逻辑(状态为-1表示已删除无效
- 常见问题与错误处理策略
- 性能优化建议(如高效查询热门材质)
## 项目结构
围绕材质服务的关键文件组织如下:
- 服务层:负责业务规则编排与跨仓储调用
- 仓储层:封装数据库访问与聚合查询
- 模型层:定义材质实体、收藏关联与下载日志
- 数据库管理:统一数据库连接与自动迁移
```mermaid
graph TB
subgraph "服务层"
S1["TextureService<br/>internal/service/texture_service.go"]
end
subgraph "仓储层"
R1["TextureRepository<br/>internal/repository/texture_repository.go"]
end
subgraph "模型层"
M1["Texture<br/>internal/model/texture.go"]
M2["UserTextureFavorite<br/>internal/model/texture.go"]
M3["TextureDownloadLog<br/>internal/model/texture.go"]
end
subgraph "基础设施"
D1["数据库管理器<br/>pkg/database/manager.go"]
end
S1 --> R1
R1 --> D1
S1 --> M1
S1 --> M2
S1 --> M3
R1 --> M1
R1 --> M2
R1 --> M3
```
图表来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
- [pkg/database/manager.go](file://pkg/database/manager.go#L1-L114)
章节来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
- [pkg/database/manager.go](file://pkg/database/manager.go#L1-L114)
## 核心组件
- TextureService提供材质上传、查询、更新、删除、下载统计、收藏切换、收藏列表、上传数量限制检查等能力
- TextureRepository封装材质的增删改查、分页、统计、收藏与下载日志写入等仓储操作
- Model.Texture材质实体包含类型、状态、公开度、下载/收藏计数、Slim标记等
- Model.UserTextureFavorite用户-材质收藏关联
- Model.TextureDownloadLog材质下载日志
- Database Manager数据库连接与自动迁移
章节来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
- [pkg/database/manager.go](file://pkg/database/manager.go#L1-L114)
## 架构总览
服务层通过统一的 GORM 数据库实例访问仓储层,仓储层对数据库执行具体操作;模型层定义实体与索引约束,确保查询与统计效率。
```mermaid
sequenceDiagram
participant C as "调用方"
participant S as "TextureService"
participant R as "TextureRepository"
participant DB as "GORM DB"
participant M as "Model"
C->>S : 调用上传/搜索/收藏等方法
S->>R : 调用仓储方法查询/更新/写入
R->>DB : 执行数据库操作
DB-->>R : 返回结果/影响行数
R-->>S : 返回实体/列表/统计
S-->>C : 返回业务结果
```
图表来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [pkg/database/manager.go](file://pkg/database/manager.go#L1-L114)
## 详细组件分析
### TextureService 方法与职责
- 创建材质
- 校验上传者存在性
- 基于哈希去重
- 类型转换与校验(仅支持 SKIN/CAPE
- 默认状态为“正常”,默认下载数与收藏数为 0
- 写入仓储并返回实体
- 获取材质详情
- 查询材质并校验状态(状态为-1视为已删除
- 用户上传列表
- 分页查询,排除已删除状态
- 搜索材质
- 支持公开筛选、类型筛选、关键词模糊匹配
- 分页查询,按创建时间倒序
- 更新材质
- 权限校验(仅上传者可更新)
- 动态更新名称/描述/公开状态
- 删除材质
- 权限校验(仅上传者可删除)
- 采用软删除(状态置为-1
- 记录下载
- 校验材质存在性
- 原子递增下载计数
- 写入下载日志
- 切换收藏
- 校验材质存在性
- 已收藏则取消并递减收藏计数;未收藏则添加并递增收藏计数
- 用户收藏列表
- 分页查询收藏的材质(排除已删除)
- 上传数量限制检查
- 统计用户未删除材质数量并与上限比较
章节来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
### 材质类型与默认值
- 类型枚举
- SKIN、CAPE 两种类型
- 默认值
- 状态1正常
- 下载数0
- 收藏数0
- Slim 标记:用于区分 Alex/Steve 角色皮肤样式
章节来源
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
### 状态管理与验证
- 状态含义
- 1正常
- 0审核中/禁用(仍存在,但不可用)
- -1已删除软删除
- 验证规则
- 获取详情时若状态为-1判定为“已删除”
- 搜索与收藏列表均排除状态为-1的材质
- 删除操作采用软删除,不物理删除
章节来源
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
### 上传、搜索与收藏业务规则
- 上传
- 哈希唯一性约束,避免重复
- 类型严格校验
- 默认状态与计数初始化
- 搜索
- 公开筛选、类型筛选、关键词模糊匹配
- 分页与排序(按创建时间倒序)
- 收藏
- 基于用户-材质关联表维护收藏
- 切换收藏时原子更新计数
章节来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
### 服务层与仓储层协作
- 服务层负责:
- 参数校验与业务规则
- 权限校验(上传者)
- 组合仓储调用以完成复杂流程(如收藏切换)
- 仓储层负责:
- 数据库 CRUD、分页、统计、索引使用
- 原子计数更新(下载/收藏)
```mermaid
classDiagram
class TextureService {
+CreateTexture(...)
+GetTextureByID(...)
+GetUserTextures(...)
+SearchTextures(...)
+UpdateTexture(...)
+DeleteTexture(...)
+RecordTextureDownload(...)
+ToggleTextureFavorite(...)
+GetUserTextureFavorites(...)
+CheckTextureUploadLimit(...)
}
class TextureRepository {
+CreateTexture(...)
+FindTextureByID(...)
+FindTextureByHash(...)
+FindTexturesByUploaderID(...)
+SearchTextures(...)
+UpdateTexture(...)
+UpdateTextureFields(...)
+DeleteTexture(...)
+IncrementTextureDownloadCount(...)
+IncrementTextureFavoriteCount(...)
+DecrementTextureFavoriteCount(...)
+CreateTextureDownloadLog(...)
+IsTextureFavorited(...)
+AddTextureFavorite(...)
+RemoveTextureFavorite(...)
+GetUserTextureFavorites(...)
+CountTexturesByUploaderID(...)
}
class Texture
class UserTextureFavorite
class TextureDownloadLog
TextureService --> TextureRepository : "调用"
TextureRepository --> Texture : "读写"
TextureRepository --> UserTextureFavorite : "读写"
TextureRepository --> TextureDownloadLog : "读写"
```
图表来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
### 关键流程图示
#### 获取材质详情流程
```mermaid
flowchart TD
Start(["进入 GetTextureByID"]) --> Load["仓储查询材质"]
Load --> Found{"找到记录?"}
Found --> |否| NotFound["返回不存在错误"]
Found --> |是| CheckStatus["检查状态"]
CheckStatus --> Deleted{"状态为-1"}
Deleted --> |是| DelErr["返回已删除错误"]
Deleted --> |否| Return["返回材质对象"]
NotFound --> End(["结束"])
DelErr --> End
Return --> End
```
图表来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L66-L79)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L16-L27)
#### 切换收藏流程
```mermaid
flowchart TD
Start(["进入 ToggleTextureFavorite"]) --> Load["仓储查询材质"]
Load --> Found{"找到记录?"}
Found --> |否| Err["返回不存在错误"]
Found --> |是| CheckFav["检查是否已收藏"]
CheckFav --> IsFav{"已收藏?"}
IsFav --> |是| Unfav["删除收藏记录"]
Unfav --> Dec["递减收藏计数"]
IsFav --> |否| Fav["新增收藏记录"]
Fav --> Inc["递增收藏计数"]
Dec --> Done["返回false"]
Inc --> Done2["返回true"]
Err --> End(["结束"])
Done --> End
Done2 --> End
```
图表来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L189-L225)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L159-L187)
#### 上传流程(含类型与去重校验)
```mermaid
flowchart TD
Start(["进入 CreateTexture"]) --> CheckUser["校验上传者存在"]
CheckUser --> Exists{"存在?"}
Exists --> |否| UErr["返回用户不存在错误"]
Exists --> |是| CheckHash["按哈希查询材质"]
CheckHash --> Dup{"已存在?"}
Dup --> |是| DupErr["返回材质已存在错误"]
Dup --> |否| ParseType["解析类型(SKIN/CAPE)"]
ParseType --> Valid{"类型有效?"}
Valid --> |否| TErr["返回无效类型错误"]
Valid --> |是| Build["构建材质对象(默认状态/计数)"]
Build --> Save["仓储保存材质"]
Save --> Ok["返回材质对象"]
UErr --> End(["结束"])
DupErr --> End
TErr --> End
Ok --> End
```
图表来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L12-L64)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L29-L41)
## 依赖关系分析
- 服务层依赖仓储层接口,仓储层依赖数据库管理器提供的全局 GORM 实例
- 模型层定义了材质、收藏、下载日志三类实体,并带有索引与排序提示
- 数据库管理器负责初始化与迁移,确保表结构与模型一致
```mermaid
graph LR
TS["TextureService"] --> TR["TextureRepository"]
TR --> DM["Database Manager"]
TR --> M["Models(Texture/UserTextureFavorite/TextureDownloadLog)"]
```
图表来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [pkg/database/manager.go](file://pkg/database/manager.go#L1-L114)
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
章节来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [pkg/database/manager.go](file://pkg/database/manager.go#L1-L114)
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
## 性能考虑
- 索引与排序
- 材质表对公开状态、类型、状态组合建立索引,有利于搜索过滤
- 下载数与收藏数列带索引且按降序排序,便于热门查询
- 原子计数更新
- 下载与收藏计数采用原生表达式递增/递减,减少锁竞争与往返
- 分页与预加载
- 分页查询时使用偏移与限制,结合预加载提升列表渲染效率
- 热门查询建议
- 使用下载数/收藏数列的降序索引进行 Top-N 查询
- 对搜索场景,优先利用公开状态与类型过滤,缩小扫描范围
- 缓存策略(建议)
- 对热点材质详情与热门榜单可引入缓存层,降低数据库压力
- 结合 TTL 与失效策略,保证数据一致性
章节来源
- [internal/model/texture.go](file://internal/model/texture.go#L1-L77)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L132-L151)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L93-L103)
## 故障排查指南
- 无效类型
- 现象:上传时返回“无效的材质类型”
- 排查:确认传入类型为 SKIN 或 CAPE
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L32-L41)
- 材质已存在
- 现象:上传返回“该材质已存在”
- 排查:确认哈希是否重复;检查去重逻辑
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L23-L31)
- 用户不存在
- 现象:上传返回“用户不存在”
- 排查确认上传者ID正确检查用户是否存在
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L14-L21)
- 材质不存在或已删除
- 现象:获取详情/收藏/下载时返回“材质不存在”或“材质已删除”
- 排查确认ID正确检查状态是否为-1
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L66-L79)
- 权限不足
- 现象:更新/删除返回“无权修改/无权删除”
- 排查:确认请求者为上传者
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L106-L141)
- 上传数量超限
- 现象:检查上传限制返回已达上限
- 排查:确认当前未删除材质数量与上限配置
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L239-L251)
章节来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L12-L64)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L66-L79)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L106-L141)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L239-L251)
## 结论
TextureService 将业务规则与仓储操作解耦,通过严格的类型与状态校验、完善的权限控制与原子计数更新,保障了材质上传、搜索、收藏与统计的可靠性。配合模型层的索引设计与服务层的分页策略,可在高并发场景下保持良好性能。建议在热点场景引入缓存与更精细的索引策略,持续优化查询与写入性能。
## 附录
### API 一览(方法与关键行为)
- CreateTexture
- 输入上传者ID、名称、描述、类型、URL、哈希、大小、公开/Slim、默认状态与计数
- 行为:校验用户与哈希唯一性,类型转换,创建并返回
- 错误:用户不存在、哈希重复、无效类型
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L12-L64)
- GetTextureByID
- 输入材质ID
- 行为:查询并校验状态(-1视为已删除
- 错误:不存在、已删除
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L66-L79)
- GetUserTextures
- 输入上传者ID、分页参数
- 行为:分页查询用户上传材质(排除已删除)
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L81-L91)
- SearchTextures
- 输入:关键词、类型、公开筛选、分页参数
- 行为:按条件过滤并分页,按创建时间倒序
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L93-L103)
- UpdateTexture
- 输入材质ID、上传者ID、名称/描述/公开状态
- 行为:权限校验后动态更新字段
- 错误:不存在、权限不足
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L106-L141)
- DeleteTexture
- 输入材质ID、上传者ID
- 行为:权限校验后软删除(状态=-1
- 错误:不存在、权限不足
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L144-L160)
- RecordTextureDownload
- 输入材质ID、用户ID/IP/UA
- 行为:原子递增下载计数并写入日志
- 错误:不存在
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L162-L187)
- ToggleTextureFavorite
- 输入用户ID、材质ID
- 行为:切换收藏并原子更新计数
- 错误:不存在
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L189-L225)
- GetUserTextureFavorites
- 输入用户ID、分页参数
- 行为:分页查询收藏的材质(排除已删除)
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L227-L237)
- CheckTextureUploadLimit
- 输入上传者ID、最大数量
- 行为:统计未删除材质数量并与上限比较
- 错误:达到上限
- 参考路径:[internal/service/texture_service.go](file://internal/service/texture_service.go#L239-L251)
### 状态与类型验证(测试参考)
- 类型验证:仅接受 SKIN/CAPE
- 参考路径:[internal/service/texture_service_test.go](file://internal/service/texture_service_test.go#L7-L44)
- 默认值验证:状态=1下载数=0收藏数=0
- 参考路径:[internal/service/texture_service_test.go](file://internal/service/texture_service_test.go#L46-L65)
- 状态验证:状态=-1 无效
- 参考路径:[internal/service/texture_service_test.go](file://internal/service/texture_service_test.go#L67-L99)
- 分页参数校验page≥11≤pageSize≤100
- 参考路径:[internal/service/texture_service_test.go](file://internal/service/texture_service_test.go#L102-L161)
- 参考路径:[internal/service/texture_service_test.go](file://internal/service/texture_service_test.go#L163-L215)
- 参考路径:[internal/service/texture_service_test.go](file://internal/service/texture_service_test.go#L376-L428)