# 材质API
**本文引用的文件**
- [routes.go](file://internal/handler/routes.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)
- [upload_service.go](file://internal/service/upload_service.go)
- [minio.go](file://pkg/storage/minio.go)
- [auth.go](file://internal/middleware/auth.go)
- [texture_service_test.go](file://internal/service/texture_service_test.go)
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向“材质管理API”的使用者与维护者,系统性梳理材质的搜索、上传、创建、更新、删除、收藏及分页查询能力。基于路由组 /api/v1/texture 的公开与认证两类访问模式,详细说明各端点的行为、参数、响应与错误处理,并深入解析生成上传URL的流程、材质元数据创建、分页查询策略以及收藏功能的实现细节。同时解释材质与用户的关系及权限控制机制,帮助读者快速上手并正确集成。
## 项目结构
- 路由注册集中在路由处理器中,按组划分公开与认证两类接口。
- 处理器负责参数校验、鉴权、调用服务层并返回统一响应。
- 服务层封装业务规则(如权限校验、分页边界、收藏增删计数)。
- 仓储层负责数据库查询与写入(含软删除、计数更新、收藏关联)。
- 模型层定义材质、收藏、下载日志等实体及其索引。
- 上传服务与存储客户端负责生成预签名上传URL与对象命名策略。
- 中间件提供JWT认证与可选认证能力。
```mermaid
graph TB
subgraph "HTTP层"
R["路由注册
routes.go"]
H["处理器
texture_handler.go"]
end
subgraph "服务层"
S["服务
texture_service.go"]
end
subgraph "仓储层"
REPO["仓储
texture_repository.go"]
end
subgraph "模型层"
M["模型
texture.go"]
end
subgraph "上传与存储"
U["上传服务
upload_service.go"]
ST["存储客户端
minio.go"]
end
subgraph "鉴权"
A["认证中间件
auth.go"]
end
R --> H
H --> A
H --> S
S --> REPO
REPO --> M
H --> U
U --> ST
```
图表来源
- [routes.go](file://internal/handler/routes.go#L42-L61)
- [texture_handler.go](file://internal/handler/texture_handler.go#L1-L600)
- [texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [texture.go](file://internal/model/texture.go#L1-L77)
- [upload_service.go](file://internal/service/upload_service.go#L1-L161)
- [minio.go](file://pkg/storage/minio.go#L1-L121)
- [auth.go](file://internal/middleware/auth.go#L1-L79)
章节来源
- [routes.go](file://internal/handler/routes.go#L42-L61)
## 核心组件
- 路由与鉴权
- 公开端点:GET /api/v1/texture、GET /api/v1/texture/{id}
- 认证端点:POST /api/v1/texture/upload-url、POST /api/v1/texture、PUT /api/v1/texture/{id}、DELETE /api/v1/texture/{id}、POST /api/v1/texture/{id}/favorite、GET /api/v1/texture/my、GET /api/v1/texture/favorites
- 认证中间件要求 Authorization: Bearer ,通过后将用户信息注入上下文
- 数据模型
- 材质实体包含上传者ID、名称、描述、类型、URL、哈希、大小、公开状态、下载/收藏计数、是否细臂、状态、时间戳等;并关联上传者
- 收藏关联表 user_texture_favorites 记录用户与材质的收藏关系
- 下载日志表 texture_download_logs 记录下载行为
- 上传与存储
- 生成预签名POST URL,限定文件类型、大小范围与过期时间,对象路径按用户与材质类型组织
- 服务与仓储
- 提供搜索、分页、权限校验、收藏切换、软删除、计数更新等业务逻辑
- 仓储实现分页查询、总数统计、软删除、收藏增删与计数更新
章节来源
- [routes.go](file://internal/handler/routes.go#L42-L61)
- [texture.go](file://internal/model/texture.go#L1-L77)
- [upload_service.go](file://internal/service/upload_service.go#L1-L161)
- [texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [auth.go](file://internal/middleware/auth.go#L1-L79)
## 架构总览
下图展示从HTTP请求到数据库与存储的关键交互路径,涵盖公开搜索、认证上传与创建、权限校验、收藏与分页等。
```mermaid
sequenceDiagram
participant C as "客户端"
participant R as "路由
routes.go"
participant H as "处理器
texture_handler.go"
participant A as "认证中间件
auth.go"
participant S as "服务
texture_service.go"
participant REPO as "仓储
texture_repository.go"
participant M as "模型
texture.go"
participant U as "上传服务
upload_service.go"
participant ST as "存储客户端
minio.go"
C->>R : 请求 /api/v1/texture/search
R->>H : 调用 SearchTextures
H->>S : 调用 SearchTextures(db, keyword, type, publicOnly, page, pageSize)
S->>REPO : 查询并统计总数
REPO-->>S : 返回列表与总数
S-->>H : 返回结果
H-->>C : 200 + 分页数据
C->>R : 请求 /api/v1/texture/upload-url (认证)
R->>A : 鉴权
A-->>R : 注入用户ID
R->>H : 调用 GenerateTextureUploadURL
H->>U : 生成预签名POST URL
U->>ST : 生成POST策略与URL
ST-->>U : 返回PostURL、FormData、FileURL
U-->>H : 返回结果
H-->>C : 200 + 上传URL与过期时间
C->>R : 请求 /api/v1/texture (认证)
R->>A : 鉴权
A-->>R : 注入用户ID
R->>H : 调用 CreateTexture
H->>S : 检查上传配额与创建材质
S->>REPO : 写入材质记录
REPO-->>S : 返回新记录
S-->>H : 返回材质信息
H-->>C : 200 + 材质详情
```
图表来源
- [routes.go](file://internal/handler/routes.go#L42-L61)
- [texture_handler.go](file://internal/handler/texture_handler.go#L1-L600)
- [auth.go](file://internal/middleware/auth.go#L1-L79)
- [upload_service.go](file://internal/service/upload_service.go#L117-L160)
- [minio.go](file://pkg/storage/minio.go#L82-L121)
- [texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [texture.go](file://internal/model/texture.go#L1-L77)
## 详细组件分析
### 路由与访问模式
- 公开访问
- GET /api/v1/texture:搜索材质(关键词、类型、公开筛选、分页)
- GET /api/v1/texture/{id}:获取材质详情
- 认证访问
- POST /api/v1/texture/upload-url:生成材质上传URL(预签名POST)
- POST /api/v1/texture:创建材质记录(文件已上传至存储)
- PUT /api/v1/texture/{id}:更新材质(仅上传者可操作)
- DELETE /api/v1/texture/{id}:删除材质(软删除,仅上传者可操作)
- POST /api/v1/texture/{id}/favorite:切换收藏
- GET /api/v1/texture/my:获取当前用户上传的材质列表(分页)
- GET /api/v1/texture/favorites:获取当前用户收藏的材质列表(分页)
章节来源
- [routes.go](file://internal/handler/routes.go#L42-L61)
### 生成上传URL流程
- 客户端向 POST /api/v1/texture/upload-url 发起请求,携带文件名与材质类型(SKIN/CAPE)
- 处理器校验请求体、获取用户ID并调用上传服务
- 上传服务:
- 校验文件名扩展名与类型
- 选择对应上传配置(大小范围、过期时间)
- 解析存储桶名称(textures)
- 生成对象名:user_{userID}/{textureTypeFolder}/{timestamp}_{originalFileName}
- 生成预签名POST策略(含内容长度范围、过期时间),返回PostURL、FormData与最终访问URL
- 客户端使用返回的PostURL与FormData直传到对象存储,成功后调用 POST /api/v1/texture 创建材质记录
```mermaid
flowchart TD
Start(["开始"]) --> Bind["绑定请求体
文件名/类型"]
Bind --> Validate["校验文件名与类型"]
Validate --> Config["加载上传配置"]
Config --> Bucket["解析存储桶名称"]
Bucket --> ObjName["生成对象名
user_{id}/{type}/{ts}_{fileName}"]
ObjName --> Policy["生成预签名POST策略"]
Policy --> Result["返回PostURL/FormData/FileURL"]
Result --> End(["结束"])
```
图表来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L18-L83)
- [upload_service.go](file://internal/service/upload_service.go#L117-L160)
- [minio.go](file://pkg/storage/minio.go#L82-L121)
章节来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L18-L83)
- [upload_service.go](file://internal/service/upload_service.go#L117-L160)
- [minio.go](file://pkg/storage/minio.go#L82-L121)
### 材质元数据创建
- 客户端上传完成后,向 POST /api/v1/texture 提交材质元数据(名称、描述、类型、URL、哈希、大小、公开状态、是否细臂)
- 处理器:
- 校验请求体
- 检查用户上传配额(默认最大100条)
- 调用服务层创建材质记录(校验用户存在、去重哈希、转换类型、初始化默认值)
- 写入数据库并返回材质信息
- 服务层:
- 校验用户存在
- 哈希去重检查
- 类型转换(SKIN/CAPE)
- 初始化状态、下载/收藏计数为0
- 仓储层:
- 插入记录
章节来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L85-L172)
- [texture_service.go](file://internal/service/texture_service.go#L12-L64)
- [texture_repository.go](file://internal/repository/texture_repository.go#L9-L13)
- [texture.go](file://internal/model/texture.go#L16-L35)
### 搜索与分页查询
- GET /api/v1/texture 支持:
- keyword:关键词(名称/描述模糊匹配)
- type:SKIN/CAPE
- public_only:仅公开材质
- page/page_size:分页(最小1,最大100,默认20)
- 服务层对分页参数进行边界修正,仓储层:
- 若 public_only 为真,则过滤 is_public=true
- 按 type 进行精确过滤
- 按 name/description 模糊匹配
- 统计总数并按创建时间倒序分页查询
- 返回统一分页响应(list、total、page、page_size、total_pages)
章节来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L225-L291)
- [texture_service.go](file://internal/service/texture_service.go#L93-L103)
- [texture_repository.go](file://internal/repository/texture_repository.go#L71-L112)
- [texture_service_test.go](file://internal/service/texture_service_test.go#L163-L215)
### 更新与删除
- PUT /api/v1/texture/{id}:
- 仅材质上传者可更新
- 支持更新名称、描述、公开状态(可选字段)
- 服务层按非空字段构建更新集合并执行
- DELETE /api/v1/texture/{id}:
- 仅材质上传者可删除
- 采用软删除(status=-1),不影响历史下载/收藏计数
章节来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L293-L419)
- [texture_service.go](file://internal/service/texture_service.go#L105-L160)
- [texture_repository.go](file://internal/repository/texture_repository.go#L126-L131)
### 收藏功能
- POST /api/v1/texture/{id}/favorite:
- 切换收藏状态(已收藏则取消,未收藏则添加)
- 服务层先检查材质是否存在,再判断是否已收藏
- 成功后分别更新 user_texture_favorites 与 textures.favorite_count
- 返回 is_favorited 结果
- GET /api/v1/texture/favorites:
- 获取当前用户收藏的材质列表(分页,最小1,最大100,默认20)
- 仓储层通过子查询定位收藏的材质ID,再查询并统计总数
章节来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L421-L471)
- [texture_handler.go](file://internal/handler/texture_handler.go#L537-L599)
- [texture_service.go](file://internal/service/texture_service.go#L189-L238)
- [texture_repository.go](file://internal/repository/texture_repository.go#L159-L221)
- [texture.go](file://internal/model/texture.go#L42-L57)
### 我的材质
- GET /api/v1/texture/my:
- 获取当前用户上传的材质列表(分页,最小1,最大100,默认20)
- 仓储层按 uploader_id 且 status!=-1 过滤,统计总数并分页查询
章节来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L473-L535)
- [texture_service.go](file://internal/service/texture_service.go#L81-L91)
- [texture_repository.go](file://internal/repository/texture_repository.go#L43-L69)
### 权限控制与用户关系
- 认证中间件要求 Authorization: Bearer ,并将用户ID、用户名、角色注入上下文
- 权限规则:
- 仅上传者可更新/删除材质
- 仅登录用户可收藏/取消收藏
- 搜索公开材质时可匿名访问
- 上传URL生成与创建材质需登录
- 材质与用户:
- 材质实体包含 uploader_id 字段,指向上传者
- 模型中定义了 Uploader 关联,便于返回上传者信息
章节来源
- [auth.go](file://internal/middleware/auth.go#L1-L79)
- [texture_handler.go](file://internal/handler/texture_handler.go#L293-L419)
- [texture.go](file://internal/model/texture.go#L16-L35)
## 依赖分析
- 处理器依赖:
- 类型定义:请求/响应结构体(来自 internal/types/common.go)
- 服务层:业务逻辑封装
- 存储服务:生成预签名URL
- 服务层依赖:
- 仓储层:数据库操作
- 模型层:实体定义
- 仓储层依赖:
- 数据库连接与GORM
- 模型层:实体与索引
- 上传服务依赖:
- 存储客户端:生成POST策略与URL
- 配置:上传大小范围、过期时间、SSL与Endpoint
```mermaid
graph LR
H["texture_handler.go"] --> T["types/common.go"]
H --> S["texture_service.go"]
S --> REPO["texture_repository.go"]
REPO --> M["texture.go"]
H --> U["upload_service.go"]
U --> ST["minio.go"]
H --> A["auth.go"]
```
图表来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L1-L600)
- [common.go](file://internal/types/common.go#L86-L191)
- [texture_service.go](file://internal/service/texture_service.go#L1-L252)
- [texture_repository.go](file://internal/repository/texture_repository.go#L1-L232)
- [texture.go](file://internal/model/texture.go#L1-L77)
- [upload_service.go](file://internal/service/upload_service.go#L1-L161)
- [minio.go](file://pkg/storage/minio.go#L1-L121)
- [auth.go](file://internal/middleware/auth.go#L1-L79)
## 性能考虑
- 分页参数边界
- 所有分页接口均对 page/page_size 进行边界约束(最小1,最大100,默认20),避免过大请求导致数据库压力
- 查询优化
- 搜索接口按 status=1 与可选 is_public、type、关键词进行过滤,建议在相关列建立索引以提升查询效率
- 分页查询按 created_at 倒序,结合索引可减少排序成本
- 计数更新
- 收藏/下载计数采用原子更新(UpdateColumn),降低并发冲突概率
- 上传URL
- 预签名POST策略限制文件大小范围与过期时间,减少无效请求与存储压力
章节来源
- [texture_service.go](file://internal/service/texture_service.go#L81-L103)
- [texture_repository.go](file://internal/repository/texture_repository.go#L71-L112)
- [texture_repository.go](file://internal/repository/texture_repository.go#L132-L151)
## 故障排查指南
- 认证失败
- 缺少Authorization头或格式不正确:返回401
- Token无效:返回401
- 参数错误
- 请求体绑定失败:返回400
- 无效的材质ID:返回400
- 权限不足
- 非上传者尝试更新/删除:返回403
- 业务异常
- 材质不存在:返回404
- 已达到上传数量上限:返回400
- 哈希重复:返回400
- 上传URL生成失败
- 文件名不合法、类型不支持、存储桶不存在、生成策略失败:返回400
- 搜索/分页异常
- 数据库查询失败:返回500
章节来源
- [texture_handler.go](file://internal/handler/texture_handler.go#L18-L83)
- [texture_handler.go](file://internal/handler/texture_handler.go#L85-L172)
- [texture_handler.go](file://internal/handler/texture_handler.go#L293-L419)
- [texture_handler.go](file://internal/handler/texture_handler.go#L421-L471)
- [texture_handler.go](file://internal/handler/texture_handler.go#L473-L599)
- [texture_service.go](file://internal/service/texture_service.go#L12-L64)
- [texture_service.go](file://internal/service/texture_service.go#L105-L160)
- [texture_service.go](file://internal/service/texture_service.go#L189-L238)
## 结论
材质API围绕公开搜索与认证上传两条主线设计,通过严格的参数校验、权限控制与分页策略保障可用性与性能。上传流程采用预签名POST策略,既简化客户端实现又保证安全性。收藏与分页查询进一步完善了用户体验。建议在生产环境中为常用查询列建立索引,并结合监控与日志持续优化性能与稳定性。
## 附录
### API端点一览(公开与认证)
- 公开
- GET /api/v1/texture:搜索材质(关键词、类型、公开筛选、分页)
- GET /api/v1/texture/{id}:获取材质详情
- 认证
- POST /api/v1/texture/upload-url:生成材质上传URL(预签名POST)
- POST /api/v1/texture:创建材质记录
- PUT /api/v1/texture/{id}:更新材质
- DELETE /api/v1/texture/{id}:删除材质
- POST /api/v1/texture/{id}/favorite:切换收藏
- GET /api/v1/texture/my:我的材质(分页)
- GET /api/v1/texture/favorites:我的收藏(分页)
章节来源
- [routes.go](file://internal/handler/routes.go#L42-L61)
### 请求/响应要点
- 生成上传URL
- 请求体:file_name、texture_type(SKIN/CAPE)
- 响应体:post_url、form_data、texture_url、expires_in
- 创建材质
- 请求体:name、description、type、url、hash、size、is_public、is_slim
- 响应体:完整材质信息(含计数与时间戳)
- 搜索
- 查询参数:keyword、type、public_only、page、page_size
- 响应体:分页列表与总数
- 收藏
- 请求体:无
- 响应体:is_favorited
- 我的材质/收藏
- 查询参数:page、page_size
- 响应体:分页列表与总数
章节来源
- [common.go](file://internal/types/common.go#L86-L191)
- [texture_handler.go](file://internal/handler/texture_handler.go#L18-L83)
- [texture_handler.go](file://internal/handler/texture_handler.go#L85-L172)
- [texture_handler.go](file://internal/handler/texture_handler.go#L225-L291)
- [texture_handler.go](file://internal/handler/texture_handler.go#L421-L471)
- [texture_handler.go](file://internal/handler/texture_handler.go#L473-L599)