# 材质上传流程 **本文档引用文件** - [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) ## 目录 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)