# 材质管理操作 **本文引用的文件** - [routes.go](file://internal/handler/routes.go) - [auth.go](file://internal/middleware/auth.go) - [texture_handler.go](file://internal/handler/texture_handler.go) - [texture_service.go](file://internal/service/texture_service.go) - [texture_repository.go](file://internal/repository/texture_repository.go) - [texture.go](file://internal/model/texture.go) - [common.go](file://internal/types/common.go) - [response.go](file://internal/model/response.go) - [manager.go](file://pkg/database/manager.go) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖分析](#依赖分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件面向后端开发者与运维人员,系统化梳理材质管理模块的增删改查与收藏能力,重点覆盖以下内容: - UpdateTexture 接口的权限控制与字段级更新策略 - DeleteTexture 接口的软删除与权限校验 - ToggleFavorite 接口的收藏切换与 FavoriteCount 同步 - GetTexture、SearchTextures、GetUserTextures 的使用方式、分页与过滤规则 - 请求/响应结构、认证要求与常见错误码说明 ## 项目结构 材质管理相关代码采用典型的分层架构: - 路由层:注册 API 路由与鉴权中间件 - 处理层:HTTP 控制器,负责参数解析、鉴权、调用服务层并返回统一响应 - 服务层:业务逻辑编排,包含权限校验、字段级更新、软删除、收藏切换等 - 仓储层:数据库访问封装,提供查询、更新、计数等方法 - 模型层:实体定义与数据库映射 - 类型与响应:请求/响应结构体与统一响应模型 ```mermaid graph TB subgraph "路由层" R["routes.go
注册纹理相关路由"] end subgraph "中间件" M["auth.go
JWT鉴权中间件"] end subgraph "处理层" H["texture_handler.go
控制器:更新/删除/收藏/查询"] end subgraph "服务层" S["texture_service.go
业务逻辑:权限/字段更新/软删除/收藏切换"] end subgraph "仓储层" REPO["texture_repository.go
数据库访问:查询/更新/计数"] end subgraph "模型层" MOD["texture.go
实体:Texture/UserTextureFavorite"] end subgraph "类型与响应" T["common.go
请求/响应结构体"] RESP["response.go
统一响应模型"] end DB["manager.go
数据库初始化/迁移"] R --> M --> H --> S --> REPO --> DB H --> T H --> RESP S --> MOD REPO --> MOD ``` 图表来源 - [routes.go](file://internal/handler/routes.go#L42-L61) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L471) - [texture_service.go](file://internal/service/texture_service.go#L105-L225) - [texture_repository.go](file://internal/repository/texture_repository.go#L114-L180) - [texture.go](file://internal/model/texture.go#L15-L77) - [common.go](file://internal/types/common.go#L86-L152) - [response.go](file://internal/model/response.go#L1-L86) - [manager.go](file://pkg/database/manager.go#L52-L99) 章节来源 - [routes.go](file://internal/handler/routes.go#L42-L61) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L471) - [texture_service.go](file://internal/service/texture_service.go#L105-L225) - [texture_repository.go](file://internal/repository/texture_repository.go#L114-L180) - [texture.go](file://internal/model/texture.go#L15-L77) - [common.go](file://internal/types/common.go#L86-L152) - [response.go](file://internal/model/response.go#L1-L86) - [manager.go](file://pkg/database/manager.go#L52-L99) ## 核心组件 - 路由与鉴权 - 纹理路由组在 v1 下注册,其中部分接口需 JWT 鉴权;公开接口无需认证 - 鉴权中间件从 Authorization 头提取 Bearer Token 并校验,通过后将用户信息注入上下文 - 处理器 - 提供 UpdateTexture、DeleteTexture、ToggleFavorite、GetTexture、SearchTextures、GetUserTextures 等接口 - 参数绑定、分页默认值设置、统一响应封装 - 服务层 - UpdateTexture 字段级更新策略:仅当字段非空/非零时才更新 - DeleteTexture 软删除:将 Status 设为删除态,不影响数据完整性 - ToggleFavorite:根据收藏状态切换,同步更新 FavoriteCount - 查询接口:分页参数校验与默认值处理 - 仓储层 - 提供按条件查询、分页、计数、字段更新、软删除、收藏相关 CRUD 等 - 模型层 - Texture:包含上传者、名称、描述、类型、URL、哈希、大小、公开状态、下载/收藏计数、状态等 - UserTextureFavorite:收藏关联表 - 类型与响应 - UpdateTextureRequest、CreateTextureRequest、TextureInfo 等结构体 - 统一响应模型与常用状态码 章节来源 - [routes.go](file://internal/handler/routes.go#L42-L61) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L471) - [texture_service.go](file://internal/service/texture_service.go#L105-L225) - [texture_repository.go](file://internal/repository/texture_repository.go#L114-L180) - [texture.go](file://internal/model/texture.go#L15-L77) - [common.go](file://internal/types/common.go#L86-L152) - [response.go](file://internal/model/response.go#L1-L86) ## 架构总览 下图展示“更新材质”接口的端到端调用链路,体现鉴权、参数校验、权限检查、字段级更新与返回结果的完整流程。 ```mermaid sequenceDiagram participant C as "客户端" participant R as "路由(routes.go)" participant MW as "鉴权中间件(auth.go)" participant H as "控制器(texture_handler.go)" participant S as "服务层(texture_service.go)" participant REPO as "仓储层(texture_repository.go)" participant DB as "数据库" C->>R : "PUT /api/v1/texture/ : id" R->>MW : "进入AuthMiddleware()" MW-->>R : "校验通过,注入user_id" R->>H : "转发到UpdateTexture处理器" H->>H : "参数绑定/校验" H->>S : "service.UpdateTexture(id, user_id, name, description, is_public?)" S->>REPO : "FindTextureByID(id)" REPO-->>S : "返回Texture或nil" S->>S : "权限校验:UploaderID==user_id" S->>S : "构建字段更新map仅非空/非零字段" S->>REPO : "UpdateTextureFields(id, updates)" REPO->>DB : "执行UPDATE" DB-->>REPO : "OK" S->>REPO : "FindTextureByID(id)" REPO-->>S : "返回更新后的Texture" S-->>H : "返回Texture" H-->>C : "200 成功响应" ``` 图表来源 - [routes.go](file://internal/handler/routes.go#L50-L60) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L369) - [texture_service.go](file://internal/service/texture_service.go#L105-L141) - [texture_repository.go](file://internal/repository/texture_repository.go#L120-L124) ## 详细组件分析 ### UpdateTexture 接口:权限控制与字段级更新 - 权限控制 - 仅允许材质上传者修改;服务层通过比较 Texture 的 UploaderID 与请求用户ID进行校验 - 字段级更新策略 - 仅当请求中的 Name 非空、Description 非空、IsPublic 指针非空时,才将其加入更新集合 - 若无字段需要更新,则跳过数据库写入 - 最终重新查询并返回更新后的材质对象 - 认证要求 - 需携带 Bearer Token 的 Authorization 头 - 常见错误码 - 401 未授权(缺失/无效Token) - 403 权限不足(非上传者) - 400 请求参数错误(如ID非法) ```mermaid flowchart TD Start(["进入UpdateTexture"]) --> Parse["解析请求参数与用户ID"] Parse --> Load["加载材质记录"] Load --> Exists{"是否存在且未删除?"} Exists --> |否| Err404["返回404/不存在"] Exists --> |是| Perm["校验权限:UploaderID==user_id"] Perm --> |否| Err403["返回403/权限不足"] Perm --> |是| Build["构建字段更新map仅非空/非零字段"] Build --> HasAny{"是否有字段需要更新?"} HasAny --> |否| Reload["直接重新查询并返回"] HasAny --> |是| Save["执行字段更新"] Save --> Reload["重新查询并返回"] Reload --> End(["结束"]) Err404 --> End Err403 --> End ``` 图表来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L369) - [texture_service.go](file://internal/service/texture_service.go#L105-L141) - [texture_repository.go](file://internal/repository/texture_repository.go#L120-L124) 章节来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L369) - [texture_service.go](file://internal/service/texture_service.go#L105-L141) - [texture_repository.go](file://internal/repository/texture_repository.go#L120-L124) ### DeleteTexture 接口:软删除与权限验证 - 软删除实现 - 仓储层通过将 Status 更新为删除态(-1)实现软删除,保留数据以满足审计与历史追踪 - 权限验证 - 仅允许材质上传者删除;服务层在删除前进行权限校验 - 认证要求 - 需携带 Bearer Token 的 Authorization 头 - 常见错误码 - 401 未授权 - 403 权限不足 - 400 请求参数错误(如ID非法) ```mermaid sequenceDiagram participant C as "客户端" participant H as "控制器(texture_handler.go)" participant S as "服务层(texture_service.go)" participant REPO as "仓储层(texture_repository.go)" participant DB as "数据库" C->>H : "DELETE /api/v1/texture/ : id" H->>S : "service.DeleteTexture(id, user_id)" S->>REPO : "FindTextureByID(id)" REPO-->>S : "返回Texture或nil" S->>S : "权限校验:UploaderID==user_id" S->>REPO : "DeleteTexture(id) -> 更新status=-1" REPO->>DB : "执行UPDATE" DB-->>REPO : "OK" S-->>H : "返回nil成功" H-->>C : "200 成功响应" ``` 图表来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L371-L419) - [texture_service.go](file://internal/service/texture_service.go#L143-L160) - [texture_repository.go](file://internal/repository/texture_repository.go#L126-L130) 章节来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L371-L419) - [texture_service.go](file://internal/service/texture_service.go#L143-L160) - [texture_repository.go](file://internal/repository/texture_repository.go#L126-L130) ### ToggleFavorite 接口:收藏切换与计数同步 - 功能流程 - 检查材质是否存在且未删除 - 查询当前用户是否已收藏 - 已收藏:取消收藏并递减 FavoriteCount - 未收藏:添加收藏并递增 FavoriteCount - 返回新的收藏状态 - 认证要求 - 需携带 Bearer Token 的 Authorization 头 - 常见错误码 - 401 未授权 - 400 请求参数错误或业务异常 ```mermaid flowchart TD Start(["进入ToggleFavorite"]) --> Load["加载材质记录"] Load --> Exists{"是否存在且未删除?"} Exists --> |否| Err["返回错误"] Exists --> |是| Check["查询是否已收藏"] Check --> Favorited{"已收藏?"} Favorited --> |是| Unfav["删除收藏记录"] --> Dec["递减FavoriteCount"] --> RetFalse["返回false"] Favorited --> |否| Fav["新增收藏记录"] --> Inc["递增FavoriteCount"] --> RetTrue["返回true"] RetFalse --> End(["结束"]) RetTrue --> End Err --> End ``` 图表来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L421-L471) - [texture_service.go](file://internal/service/texture_service.go#L189-L225) - [texture_repository.go](file://internal/repository/texture_repository.go#L159-L187) 章节来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L421-L471) - [texture_service.go](file://internal/service/texture_service.go#L189-L225) - [texture_repository.go](file://internal/repository/texture_repository.go#L159-L187) ### 查询接口:GetTexture、SearchTextures、GetUserTextures - GetTexture - 公开接口,无需认证 - 根据ID查询材质,若不存在或已被软删除则返回404 - SearchTextures - 公开接口,无需认证 - 支持关键词、类型、公开筛选;分页参数默认值与范围校验 - GetUserTextures - 需 JWT 鉴权 - 仅返回当前用户上传且未删除的材质,支持分页 - 分页机制 - page 默认 1,pageSize 默认 20,最大 100 - 数据过滤规则 - SearchTextures:status=1;可按 is_public 过滤;按 name/description 模糊匹配 - GetUserTextures:uploader_id=user_id 且 status!=-1 - GetTexture:status!=deleted ```mermaid sequenceDiagram participant C as "客户端" participant H as "控制器(texture_handler.go)" participant S as "服务层(texture_service.go)" participant REPO as "仓储层(texture_repository.go)" C->>H : "GET /api/v1/texture/ : id" H->>S : "service.GetTextureByID(id)" S->>REPO : "FindTextureByID(id)" REPO-->>S : "Texture或nil" S-->>H : "返回Texture或错误" H-->>C : "200/404" C->>H : "GET /api/v1/texture?page&page_size&type=SKIN&public_only=true" H->>S : "service.SearchTextures(keyword,type,public_only,page,pageSize)" S->>REPO : "SearchTextures(...)" REPO-->>S : "[]Texture, total" S-->>H : "返回列表与总数" H-->>C : "200 分页响应" ``` 图表来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L174-L291) - [texture_service.go](file://internal/service/texture_service.go#L66-L104) - [texture_repository.go](file://internal/repository/texture_repository.go#L71-L112) 章节来源 - [texture_handler.go](file://internal/handler/texture_handler.go#L174-L291) - [texture_service.go](file://internal/service/texture_service.go#L66-L104) - [texture_repository.go](file://internal/repository/texture_repository.go#L71-L112) ## 依赖分析 - 组件耦合 - 路由层仅负责注册与中间件装配,低耦合 - 处理器依赖服务层,服务层依赖仓储层,仓储层依赖数据库 - 外部依赖 - JWT 鉴权中间件依赖认证服务 - 数据库初始化与自动迁移由数据库管理器负责 - 潜在循环依赖 - 代码组织清晰,未发现循环依赖迹象 ```mermaid graph LR Routes["routes.go"] --> Auth["auth.go"] Routes --> Handler["texture_handler.go"] Handler --> Service["texture_service.go"] Service --> Repo["texture_repository.go"] Repo --> DBMgr["manager.go"] Handler --> Types["common.go"] Handler --> Resp["response.go"] Service --> Model["texture.go"] Repo --> Model ``` 图表来源 - [routes.go](file://internal/handler/routes.go#L42-L61) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L471) - [texture_service.go](file://internal/service/texture_service.go#L105-L225) - [texture_repository.go](file://internal/repository/texture_repository.go#L114-L180) - [texture.go](file://internal/model/texture.go#L15-L77) - [common.go](file://internal/types/common.go#L86-L152) - [response.go](file://internal/model/response.go#L1-L86) - [manager.go](file://pkg/database/manager.go#L52-L99) 章节来源 - [routes.go](file://internal/handler/routes.go#L42-L61) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L471) - [texture_service.go](file://internal/service/texture_service.go#L105-L225) - [texture_repository.go](file://internal/repository/texture_repository.go#L114-L180) - [texture.go](file://internal/model/texture.go#L15-L77) - [common.go](file://internal/types/common.go#L86-L152) - [response.go](file://internal/model/response.go#L1-L86) - [manager.go](file://pkg/database/manager.go#L52-L99) ## 性能考虑 - 分页与索引 - 查询接口均使用分页与排序,仓储层对关键字段建立索引(如公开状态、下载/收藏计数、创建时间等),有助于提升检索性能 - 字段级更新 - UpdateTexture 仅更新提供字段,减少不必要的数据库写入 - 软删除 - 通过状态字段实现软删除,避免全量删除带来的性能与数据恢复成本 - 并发与事务 - 仓储层使用 GORM 执行单条 UPDATE/计数更新,建议在高并发场景下结合数据库层面的乐观锁或唯一约束保障一致性 [本节为通用指导,不涉及具体文件分析] ## 故障排查指南 - 401 未授权 - 检查 Authorization 头格式是否为 Bearer Token,Token 是否有效 - 403 权限不足 - 确认请求用户是否为材质上传者;检查 Update/Delete/ToggleFavorite 的权限校验逻辑 - 400 请求参数错误 - 检查 ID 是否为合法整数;请求体字段是否符合绑定规则 - 404 资源不存在 - 确认材质是否存在且未被软删除;查询接口会过滤 status=-1 的记录 - 分页异常 - page/page_size 默认值与范围校验:page<1 设为 1,pageSize 超过 100 或小于 1 设为 20 章节来源 - [auth.go](file://internal/middleware/auth.go#L12-L56) - [texture_handler.go](file://internal/handler/texture_handler.go#L293-L471) - [texture_service.go](file://internal/service/texture_service.go#L66-L104) - [texture_repository.go](file://internal/repository/texture_repository.go#L71-L112) ## 结论 本模块围绕“最小权限+字段级更新+软删除+计数同步”的设计原则,提供了完善的材质管理能力。通过鉴权中间件与服务层权限校验,确保只有上传者可修改/删除材质;UpdateTexture 的字段级更新策略降低写放大;ToggleFavorite 在变更收藏状态的同时同步 FavoriteCount,保证数据一致性;查询接口提供灵活的过滤与分页能力,兼顾可用性与性能。 [本节为总结性内容,不涉及具体文件分析] ## 附录 ### 接口一览与认证要求 - GET /api/v1/texture/:id - 认证:否 - 功能:获取材质详情 - GET /api/v1/texture - 认证:否 - 功能:搜索材质(关键词、类型、公开筛选) - PUT /api/v1/texture/:id - 认证:是(Bearer) - 功能:更新材质(仅上传者) - DELETE /api/v1/texture/:id - 认证:是(Bearer) - 功能:软删除材质(仅上传者) - POST /api/v1/texture/:id/favorite - 认证:是(Bearer) - 功能:切换收藏状态 - GET /api/v1/texture/my - 认证:是(Bearer) - 功能:获取当前用户上传的材质列表 - GET /api/v1/texture/favorites - 认证:是(Bearer) - 功能:获取当前用户收藏的材质列表 章节来源 - [routes.go](file://internal/handler/routes.go#L42-L61) - [auth.go](file://internal/middleware/auth.go#L12-L56) ### 请求/响应结构与字段说明 - UpdateTextureRequest - 字段:name、description、is_public(指针) - 说明:仅当字段非空/非零时参与更新 - CreateTextureRequest - 字段:name、description、type、url、hash、size、is_public、is_slim - TextureInfo - 字段:id、uploader_id、name、description、type、url、hash、size、is_public、download_count、favorite_count、is_slim、status、created_at、updated_at - 统一响应模型 - 成功:code=200,message=“操作成功”,data=业务数据 - 错误:code=400/401/403/404/500,message=错误描述,error=详细错误信息(开发环境) 章节来源 - [common.go](file://internal/types/common.go#L86-L152) - [response.go](file://internal/model/response.go#L1-L86)