# 材质服务 **本文引用的文件** - [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) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件面向开发者,系统性梳理材质服务(TextureService)的职责、方法与内部逻辑,重点覆盖: - 材质类型(SKIN/CAPE)校验与默认值设定(状态、下载数、收藏数) - 材质状态管理(正常、禁用、删除) - 上传、搜索、收藏等业务规则及服务层与仓储层的协作 - 状态验证逻辑(状态为-1表示已删除,无效) - 常见问题与错误处理策略 - 性能优化建议(如高效查询热门材质) ## 项目结构 围绕材质服务的关键文件组织如下: - 服务层:负责业务规则编排与跨仓储调用 - 仓储层:封装数据库访问与聚合查询 - 模型层:定义材质实体、收藏关联与下载日志 - 数据库管理:统一数据库连接与自动迁移 ```mermaid graph TB subgraph "服务层" S1["TextureService
internal/service/texture_service.go"] end subgraph "仓储层" R1["TextureRepository
internal/repository/texture_repository.go"] end subgraph "模型层" M1["Texture
internal/model/texture.go"] M2["UserTextureFavorite
internal/model/texture.go"] M3["TextureDownloadLog
internal/model/texture.go"] end subgraph "基础设施" D1["数据库管理器
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≥1,1≤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)