chore(git): 更新.gitignore以忽略新的本地文件
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled

This commit is contained in:
lan
2025-11-30 08:33:17 +08:00
parent 4b4980820f
commit a4b6c5011e
58 changed files with 19353 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
# 材质上传与管理
<cite>
**本文档引用的文件**
- [texture_service.go](file://internal/service/texture_service.go)
- [upload_service.go](file://internal/service/upload_service.go)
- [texture_repository.go](file://internal/repository/texture_repository.go)
- [texture.go](file://internal/model/texture.go)
- [routes.go](file://internal/handler/routes.go)
</cite>
## 目录
1. [简介](#简介)
2. [上传流程与元数据创建](#上传流程与元数据创建)
3. [上传限制检查机制](#上传限制检查机制)
4. [材质更新API权限控制](#材质更新api权限控制)
5. [字段更新策略](#字段更新策略)
6. [错误处理场景](#错误处理场景)
## 简介
本系统提供完整的材质上传与管理功能支持用户上传皮肤SKIN和披风CAPE材质。系统通过预签名URL实现安全的文件上传确保只有经过身份验证的用户才能上传和管理自己的材质。材质元数据存储在数据库中包含名称、描述、类型、哈希值等信息。系统实现了严格的权限控制确保只有上传者才能修改或删除自己的材质。同时系统还提供了收藏、下载统计等功能增强了用户体验。
## 上传流程与元数据创建
材质上传流程分为两个阶段生成上传URL和创建材质元数据。首先用户通过调用`/api/v1/texture/upload-url`接口获取预签名的上传URL。该接口验证用户身份、文件名和材质类型后生成一个临时的上传链接允许用户直接上传文件到存储服务。上传完成后用户调用`/api/v1/texture`接口创建材质元数据。系统会验证用户是否存在、材质哈希是否重复并创建相应的数据库记录。材质元数据包括上传者ID、名称、描述、类型、URL、哈希值、大小、公开状态等信息。
```mermaid
sequenceDiagram
participant 用户 as 用户
participant 处理器 as texture_handler
participant 服务 as upload_service
participant 存储 as 存储服务
用户->>处理器 : POST /api/v1/texture/upload-url
处理器->>服务 : GenerateTextureUploadURL()
服务->>服务 : 验证文件名和类型
服务->>存储 : 生成预签名POST URL
存储-->>服务 : 返回预签名URL
服务-->>处理器 : 返回URL
处理器-->>用户 : 返回上传URL
用户->>存储 : 使用预签名URL上传文件
存储-->>用户 : 上传成功
用户->>处理器 : POST /api/v1/texture
处理器->>服务 : CreateTexture()
服务->>服务 : 验证用户和哈希
服务->>repository : CreateTexture()
repository-->>服务 : 创建成功
服务-->>处理器 : 返回材质信息
处理器-->>用户 : 返回创建结果
```
**图示来源**
- [upload_service.go](file://internal/service/upload_service.go#L117-L160)
- [texture_service.go](file://internal/service/texture_service.go#L12-L63)
- [routes.go](file://internal/handler/routes.go#L53-L54)
**本节来源**
- [upload_service.go](file://internal/service/upload_service.go#L117-L160)
- [texture_service.go](file://internal/service/texture_service.go#L12-L63)
## 上传限制检查机制
系统通过`CheckTextureUploadLimit`函数实现用户上传数量限制。该函数接收数据库连接、上传者ID和最大允许上传数量作为参数。首先函数调用`CountTexturesByUploaderID`从数据库中统计指定用户已上传的材质数量。然后将统计结果与最大限制进行比较。如果当前上传数量大于或等于最大限制函数返回错误信息提示用户已达到最大上传数量限制。否则函数返回nil表示可以继续上传。系统配置中定义了每个用户的最大材质数量限制默认值为100。
```mermaid
flowchart TD
Start([开始]) --> Count["调用CountTexturesByUploaderID<br/>统计用户上传数量"]
Count --> Compare{"当前数量 >= 最大限制?"}
Compare --> |是| ReturnError["返回错误信息:<br/>已达到最大上传数量限制"]
Compare --> |否| ReturnSuccess["返回nil允许上传"]
ReturnError --> End([结束])
ReturnSuccess --> End
```
**图示来源**
- [texture_service.go](file://internal/service/texture_service.go#L239-L251)
- [texture_repository.go](file://internal/repository/texture_repository.go#L223-L231)
**本节来源**
- [texture_service.go](file://internal/service/texture_service.go#L239-L251)
## 材质更新API权限控制
材质更新API实现了严格的权限控制确保只有上传者才能修改自己的材质。当用户尝试更新材质时系统首先通过`FindTextureByID`从数据库中获取材质信息。然后比较请求中的上传者ID与材质记录中的`UploaderID`。如果两者不匹配,系统返回"无权修改此材质"的错误信息。这种基于上传者ID的权限检查机制有效防止了未经授权的修改操作。权限检查在`UpdateTexture`服务函数中实现,是更新操作的第一步,确保在进行任何数据修改之前完成身份验证。
```mermaid
flowchart TD
Start([开始]) --> GetTexture["调用FindTextureByID<br/>获取材质信息"]
GetTexture --> CheckExist{"材质存在?"}
CheckExist --> |否| ReturnError1["返回错误:<br/>材质不存在"]
CheckExist --> |是| CheckPermission{"UploaderID匹配?"}
CheckPermission --> |否| ReturnError2["返回错误:<br/>无权修改此材质"]
CheckPermission --> |是| UpdateFields["更新指定字段"]
UpdateFields --> ReturnSuccess["返回更新后的材质"]
ReturnError1 --> End([结束])
ReturnError2 --> End
ReturnSuccess --> End
```
**图示来源**
- [texture_service.go](file://internal/service/texture_service.go#L106-L141)
- [texture_repository.go](file://internal/repository/texture_repository.go#L16-L27)
**本节来源**
- [texture_service.go](file://internal/service/texture_service.go#L106-L141)
## 字段更新策略
系统采用灵活的字段更新策略,仅更新客户端提供的字段。当更新材质时,系统创建一个`updates`映射来存储需要更新的字段。如果提供了名称且不为空,将其添加到`updates`映射中;如果提供了描述且不为空,也将其添加到映射中;如果提供了公开状态(`isPublic`),同样将其添加到映射中。只有当`updates`映射不为空时,系统才会调用`UpdateTextureFields`执行数据库更新操作。这种策略避免了不必要的数据库写入,提高了性能,并确保未提供的字段保持原值不变。
```mermaid
flowchart TD
Start([开始]) --> InitMap["初始化updates映射"]
InitMap --> CheckName{"名称提供且不为空?"}
CheckName --> |是| AddName["添加name到updates映射"]
CheckName --> |否| CheckDesc
AddName --> CheckDesc
CheckDesc --> {"描述提供且不为空?"}
CheckDesc --> |是| AddDesc["添加description到updates映射"]
CheckDesc --> |否| CheckPublic
AddDesc --> CheckPublic
CheckPublic --> {"公开状态提供?"}
CheckPublic --> |是| AddPublic["添加is_public到updates映射"]
CheckPublic --> |否| CheckUpdates
AddPublic --> CheckUpdates
CheckUpdates --> {"updates映射为空?"}
CheckUpdates --> |是| ReturnOriginal["返回原材质"]
CheckUpdates --> |否| UpdateDB["调用UpdateTextureFields<br/>更新数据库"]
UpdateDB --> ReturnUpdated["返回更新后的材质"]
ReturnOriginal --> End([结束])
ReturnUpdated --> End
```
**图示来源**
- [texture_service.go](file://internal/service/texture_service.go#L106-L141)
- [texture_repository.go](file://internal/repository/texture_repository.go#L120-L124)
**本节来源**
- [texture_service.go](file://internal/service/texture_service.go#L106-L141)
## 错误处理场景
系统在材质上传与管理过程中实现了全面的错误处理机制。当用户达到上传上限时,`CheckTextureUploadLimit`函数返回"已达到最大上传数量限制"的错误信息。当用户尝试修改不属于自己的材质时,`UpdateTexture`函数返回"无权修改此材质"的错误信息。其他常见错误包括:用户不存在、材质已存在、无效的材质类型、文件名为空、不支持的文件格式等。所有错误都通过标准的错误响应格式返回给客户端,包含错误代码和描述信息,便于前端进行相应的错误处理和用户提示。
**本节来源**
- [texture_service.go](file://internal/service/texture_service.go#L239-L251)
- [texture_service.go](file://internal/service/texture_service.go#L106-L141)
- [upload_service.go](file://internal/service/upload_service.go#L120-L127)
- [texture_service.go](file://internal/service/texture_service.go#L19-L21)
- [texture_service.go](file://internal/service/texture_service.go#L28-L30)
- [texture_service.go](file://internal/service/texture_service.go#L40-L41)

View File

@@ -0,0 +1,327 @@
# 材质上传流程
<cite>
**本文档引用文件**
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go)
- [internal/service/upload_service.go](file://internal/service/upload_service.go)
- [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/storage/minio.go](file://pkg/storage/minio.go)
- [pkg/config/config.go](file://pkg/config/config.go)
</cite>
## 目录
1. [流程概述](#流程概述)
2. [API接口说明](#api接口说明)
3. [生成预签名上传URL](#生成预签名上传url)
4. [创建材质记录](#创建材质记录)
5. [错误处理机制](#错误处理机制)
6. [流程时序图](#流程时序图)
## 流程概述
材质上传流程采用分步式设计包含两个核心API接口`GenerateTextureUploadURL``CreateTexture`。该流程遵循安全最佳实践通过预签名URL机制实现文件上传确保文件在上传到存储系统后才在数据库中创建相应记录。
整个流程分为两个阶段:
1. **准备阶段**:客户端调用`GenerateTextureUploadURL`接口服务器验证权限后返回预签名上传URL和表单数据
2. **完成阶段**:客户端使用返回的凭证上传文件后,调用`CreateTexture`接口创建材质元数据记录
这种设计确保了只有成功上传的文件才会在系统中创建记录,避免了数据库中出现孤立的记录。
## API接口说明
材质上传相关的API接口定义在路由配置中主要包含以下两个核心接口
```mermaid
flowchart TD
A[客户端] --> B[GenerateTextureUploadURL]
A --> C[CreateTexture]
B --> D[返回预签名URL和表单数据]
C --> E[创建材质记录]
```
**接口来源**
- [internal/handler/routes.go](file://internal/handler/routes.go#L53-L54)
## 生成预签名上传URL
`GenerateTextureUploadURL`接口负责生成临时的文件上传凭证,使客户端能够直接上传文件到对象存储系统。
### 接口实现
该接口的处理流程如下:
```mermaid
flowchart TD
Start([开始]) --> AuthCheck["验证用户身份"]
AuthCheck --> ValidateInput["验证请求参数"]
ValidateInput --> CheckFileName["验证文件名"]
CheckFileName --> CheckTextureType["验证材质类型"]
CheckTextureType --> GetStorageConfig["获取存储配置"]
GetStorageConfig --> GenerateObjectName["生成对象名称"]
GenerateObjectName --> GeneratePresignedURL["生成预签名POST URL"]
GeneratePresignedURL --> ReturnResult["返回PostURL和FormData"]
ReturnResult --> End([结束])
```
**代码来源**
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L28-L83)
- [internal/service/upload_service.go](file://internal/service/upload_service.go#L117-L160)
### 请求参数
请求体包含以下字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| fileName | string | 上传的文件名 |
| textureType | string | 材质类型SKIN或CAPE |
### 响应格式
成功响应包含预签名上传所需的所有信息:
```json
{
"code": 200,
"message": "success",
"data": {
"postURL": "https://storage.example.com/textures",
"formData": {
"key": "user_1/skin/20231201120000_texture.png",
"policy": "base64-encoded-policy",
"signature": "request-signature",
"AWSAccessKeyId": "access-key-id"
},
"textureURL": "https://storage.example.com/textures/user_1/skin/20231201120000_texture.png",
"expiresIn": 900
}
}
```
### 实现细节
1. **文件名验证**:检查文件扩展名是否为`.png`确保只允许上传PNG格式的材质文件
2. **类型验证**:确认材质类型为`SKIN``CAPE`之一
3. **对象名称生成**:采用`user_{userId}/{textureType}/timestamp_{originalFileName}`的格式,确保文件路径的唯一性
4. **预签名URL生成**:调用存储模块的`GeneratePresignedPostURL`方法创建临时上传凭证
**存储实现来源**
- [pkg/storage/minio.go](file://pkg/storage/minio.go#L82-L120)
## 创建材质记录
`CreateTexture`接口在文件上传完成后创建材质的元数据记录,将文件与用户关联起来。
### 接口实现
该接口的处理流程包含多个验证步骤:
```mermaid
flowchart TD
Start([开始]) --> AuthCheck["验证用户身份"]
AuthCheck --> ValidateInput["验证请求参数"]
ValidateInput --> CheckLimit["检查上传数量限制"]
CheckLimit --> CheckHash["检查文件Hash是否重复"]
CheckHash --> ValidateUser["验证用户存在"]
ValidateUser --> ConvertType["转换材质类型"]
ConvertType --> CreateRecord["创建材质记录"]
CreateRecord --> ReturnResult["返回材质信息"]
ReturnResult --> End([结束])
style CheckLimit fill:#f9f,stroke:#333
style CheckHash fill:#f9f,stroke:#333
```
**代码来源**
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L95-L172)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L12-L64)
### 请求参数
请求体包含材质的元数据信息:
| 字段 | 类型 | 说明 |
|------|------|------|
| name | string | 材质名称 |
| description | string | 描述信息 |
| type | string | 材质类型SKIN或CAPE |
| url | string | 文件访问URL |
| hash | string | 文件SHA-256哈希值 |
| size | int | 文件大小(字节) |
| isPublic | boolean | 是否公开 |
| isSlim | boolean | 是否为细身模型 |
### 响应格式
成功创建材质后返回材质的详细信息:
```json
{
"code": 200,
"message": "success",
"data": {
"id": 123,
"uploaderID": 1,
"name": "My Skin",
"description": "A custom skin",
"type": "SKIN",
"url": "https://storage.example.com/textures/user_1/skin/20231201120000_texture.png",
"hash": "sha256-hash-value",
"size": 10240,
"isPublic": true,
"downloadCount": 0,
"favoriteCount": 0,
"isSlim": false,
"status": 1,
"createdAt": "2023-12-01T12:00:00Z",
"updatedAt": "2023-12-01T12:00:00Z"
}
}
```
### 核心验证逻辑
#### 上传数量限制检查
系统通过`CheckTextureUploadLimit`函数检查用户是否达到上传上限:
```go
func CheckTextureUploadLimit(db *gorm.DB, uploaderID int64, maxTextures int) error {
count, err := repository.CountTexturesByUploaderID(uploaderID)
if err != nil {
return err
}
if count >= int64(maxTextures) {
return fmt.Errorf("已达到最大上传数量限制(%d)", maxTextures)
}
return nil
}
```
**代码来源**
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L239-L251)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L223-L231)
#### 重复上传防止
通过文件的SHA-256哈希值防止重复上传相同内容的材质
```go
// 检查Hash是否已存在
existingTexture, err := repository.FindTextureByHash(hash)
if err != nil {
return nil, err
}
if existingTexture != nil {
return nil, errors.New("该材质已存在")
}
```
**代码来源**
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L23-L30)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L29-L41)
## 错误处理机制
系统针对不同场景提供了详细的错误响应,帮助客户端正确处理各种异常情况。
### 错误码定义
| 错误码 | HTTP状态码 | 说明 |
|--------|------------|------|
| 400 | 400 | 请求参数错误 |
| 401 | 401 | 未授权访问 |
| 403 | 403 | 无权操作 |
| 404 | 404 | 资源不存在 |
### 常见错误场景
#### 上传数量达到上限
当用户上传的材质数量达到系统限制时,返回以下错误:
```json
{
"code": 400,
"message": "已达到最大上传数量限制(100)",
"data": null
}
```
#### 权限不足
未登录用户或非上传者尝试操作时返回:
```json
{
"code": 401,
"message": "Unauthorized",
"data": null
}
```
#### 文件Hash冲突
上传已存在的材质文件时返回:
```json
{
"code": 400,
"message": "该材质已存在",
"data": null
}
```
#### 无效的材质类型
指定不支持的材质类型时返回:
```json
{
"code": 400,
"message": "无效的材质类型: INVALID",
"data": null
}
```
## 流程时序图
以下是完整的材质上传流程时序图:
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Handler as "处理器"
participant Service as "服务层"
participant Storage as "存储模块"
participant DB as "数据库"
Client->>Handler : 调用GenerateTextureUploadURL
Handler->>Service : 验证参数并调用GenerateTextureUploadURL
Service->>Service : 验证文件名和类型
Service->>Storage : 获取存储桶名称
Storage-->>Service : 返回存储桶名称
Service->>Storage : 生成预签名POST URL
Storage-->>Service : 返回PostURL和FormData
Service-->>Handler : 返回结果
Handler-->>Client : 返回预签名上传信息
Client->>Storage : 使用PostURL上传文件
Storage-->>Client : 上传成功响应
Client->>Handler : 调用CreateTexture
Handler->>Service : 验证参数并调用CreateTexture
Service->>DB : 检查用户上传数量限制
DB-->>Service : 返回数量统计
Service->>DB : 检查文件Hash是否重复
DB-->>Service : 返回查询结果
Service->>DB : 创建材质记录
DB-->>Service : 创建成功
Service-->>Handler : 返回材质信息
Handler-->>Client : 返回创建结果
```
**时序图来源**
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go)
- [internal/service/upload_service.go](file://internal/service/upload_service.go)
- [internal/service/texture_service.go](file://internal/service/texture_service.go)

View File

@@ -0,0 +1,460 @@
# 材质管理操作
<cite>
**本文引用的文件**
- [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)
</cite>
## 目录
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<br/>注册纹理相关路由"]
end
subgraph "中间件"
M["auth.go<br/>JWT鉴权中间件"]
end
subgraph "处理层"
H["texture_handler.go<br/>控制器:更新/删除/收藏/查询"]
end
subgraph "服务层"
S["texture_service.go<br/>业务逻辑:权限/字段更新/软删除/收藏切换"]
end
subgraph "仓储层"
REPO["texture_repository.go<br/>数据库访问:查询/更新/计数"]
end
subgraph "模型层"
MOD["texture.go<br/>实体Texture/UserTextureFavorite"]
end
subgraph "类型与响应"
T["common.go<br/>请求/响应结构体"]
RESP["response.go<br/>统一响应模型"]
end
DB["manager.go<br/>数据库初始化/迁移"]
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 默认 1pageSize 默认 20最大 100
- 数据过滤规则
- SearchTexturesstatus=1可按 is_public 过滤;按 name/description 模糊匹配
- GetUserTexturesuploader_id=user_id 且 status!=-1
- GetTexturestatus!=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 TokenToken 是否有效
- 403 权限不足
- 确认请求用户是否为材质上传者;检查 Update/Delete/ToggleFavorite 的权限校验逻辑
- 400 请求参数错误
- 检查 ID 是否为合法整数;请求体字段是否符合绑定规则
- 404 资源不存在
- 确认材质是否存在且未被软删除;查询接口会过滤 status=-1 的记录
- 分页异常
- page/page_size 默认值与范围校验page<1 设为 1pageSize 超过 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
- 字段namedescriptionis_public指针
- 说明仅当字段非空/非零时参与更新
- CreateTextureRequest
- 字段namedescriptiontypeurlhashsizeis_publicis_slim
- TextureInfo
- 字段iduploader_idnamedescriptiontypeurlhashsizeis_publicdownload_countfavorite_countis_slimstatuscreated_atupdated_at
- 统一响应模型
- 成功code=200message=“操作成功”data=业务数据
- 错误code=400/401/403/404/500message=错误描述error=详细错误信息(开发环境)
章节来源
- [common.go](file://internal/types/common.go#L86-L152)
- [response.go](file://internal/model/response.go#L1-L86)