# 材质删除
**本文引用的文件**
- [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)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go)
- [internal/handler/routes.go](file://internal/handler/routes.go)
- [internal/service/texture_service_test.go](file://internal/service/texture_service_test.go)
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
## 简介
本专项文档聚焦“材质删除”API,围绕删除操作的权限验证机制展开,结合服务层与仓储层的实现,说明删除流程、成功响应、错误场景,以及删除对数据库记录的影响(软删除)与关联数据处理(收藏关系)。同时基于测试用例,确认删除权限校验逻辑与行为一致性。
## 项目结构
与材质删除相关的代码分布在以下模块:
- 路由注册:定义DELETE /api/v1/texture/:id接口
- 处理器:解析参数、鉴权、调用服务层并返回响应
- 服务层:执行业务规则(权限校验、存在性校验)、调用仓储层
- 仓储层:执行数据库操作(软删除)
- 数据模型:定义材质实体、收藏关系、下载日志等
```mermaid
graph TB
Routes["路由注册
routes.go"] --> Handler["处理器
texture_handler.go"]
Handler --> Service["服务层
texture_service.go"]
Service --> Repo["仓储层
texture_repository.go"]
Repo --> Model["数据模型
texture.go"]
```
图表来源
- [internal/handler/routes.go](file://internal/handler/routes.go#L42-L61)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
- [internal/model/texture.go](file://internal/model/texture.go#L16-L35)
章节来源
- [internal/handler/routes.go](file://internal/handler/routes.go#L42-L61)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
- [internal/model/texture.go](file://internal/model/texture.go#L16-L35)
## 核心组件
- 路由与鉴权
- DELETE /api/v1/texture/:id 由处理器绑定,使用鉴权中间件,要求携带有效令牌。
- 处理器
- 解析路径参数(材质ID),从上下文提取当前用户ID,调用服务层执行删除,并按错误码返回相应HTTP状态与响应体。
- 服务层
- 校验材质存在性;校验删除权限(仅上传者可删);调用仓储层执行软删除。
- 仓储层
- 将材质记录的status字段置为-1,实现软删除。
- 数据模型
- 材质实体包含UploaderID、Status等字段;收藏关系通过user_texture_favorites表维护。
章节来源
- [internal/handler/routes.go](file://internal/handler/routes.go#L42-L61)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
- [internal/model/texture.go](file://internal/model/texture.go#L16-L35)
## 架构总览
下图展示从客户端到数据库的完整调用链路,包括鉴权、参数解析、权限校验、软删除与响应返回。
```mermaid
sequenceDiagram
participant C as "客户端"
participant R as "路由(routes.go)"
participant H as "处理器(texture_handler.go)"
participant S as "服务层(texture_service.go)"
participant P as "仓储层(texture_repository.go)"
participant D as "数据库"
C->>R : "DELETE /api/v1/texture/ : id"
R->>H : "进入处理器"
H->>H : "解析路径参数/校验JWT"
H->>S : "service.DeleteTexture(db, textureID, uploaderID)"
S->>P : "FindTextureByID(textureID)"
P-->>S : "返回材质或nil"
alt "材质不存在"
S-->>H : "返回错误:材质不存在"
H-->>C : "403 错误响应"
else "材质存在"
S->>S : "校验 uploaderID == 请求者ID"
alt "非上传者"
S-->>H : "返回错误:无权删除此材质"
H-->>C : "403 错误响应"
else "上传者本人"
S->>P : "DeleteTexture(textureID)"
P->>D : "UPDATE textures SET status=-1 WHERE id=..."
D-->>P : "OK"
P-->>S : "OK"
S-->>H : "OK"
H-->>C : "200 成功响应"
end
end
```
图表来源
- [internal/handler/routes.go](file://internal/handler/routes.go#L42-L61)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
## 详细组件分析
### 删除API请求流程与权限验证
- 路由与鉴权
- DELETE /api/v1/texture/:id 由处理器绑定,使用鉴权中间件,要求携带有效令牌。
- 参数解析与鉴权
- 处理器从路径参数解析材质ID,从上下文提取当前用户ID;若缺失则返回未授权。
- 权限验证
- 服务层先查询材质是否存在且未被软删除;
- 再校验请求者ID与材质的UploaderID一致,否则返回无权删除。
- 执行删除
- 通过仓储层将材质记录的status字段置为-1,完成软删除。
- 响应
- 成功返回200与通用成功响应体;错误返回403及错误信息。
```mermaid
flowchart TD
Start(["开始"]) --> Parse["解析路径参数
获取材质ID"]
Parse --> Auth{"JWT鉴权通过?"}
Auth --> |否| Resp401["返回401 未授权"]
Auth --> |是| Load["查询材质记录"]
Load --> Exists{"是否存在且未软删除?"}
Exists --> |否| Resp403a["返回403 材质不存在/已删除"]
Exists --> |是| Perm{"请求者ID==上传者ID?"}
Perm --> |否| Resp403b["返回403 无权删除此材质"]
Perm --> |是| SoftDel["软删除:设置status=-1"]
SoftDel --> Resp200["返回200 成功"]
Resp401 --> End(["结束"])
Resp403a --> End
Resp403b --> End
Resp200 --> End
```
图表来源
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
章节来源
- [internal/handler/routes.go](file://internal/handler/routes.go#L42-L61)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
### 权限验证机制
- 上传者身份判定
- 服务层比较请求者的用户ID与材质记录中的UploaderID,仅当两者相等时才允许删除。
- 存在性与状态校验
- 查询材质时会忽略status=-1的记录(软删除),因此若返回nil,即视为“不存在”。
- 测试覆盖
- 单元测试包含“删除权限检查”的用例,验证相同ID允许删除、不同ID拒绝删除的行为。
章节来源
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/service/texture_service_test.go](file://internal/service/texture_service_test.go#L315-L345)
### 删除API的成功响应与错误情况
- 成功响应
- HTTP 200,返回通用成功响应体(无额外数据)。
- 常见错误
- 401 未授权:缺少或无效的JWT令牌。
- 400 参数错误:材质ID格式非法。
- 403 无权删除:请求者非材质上传者。
- 403 材质不存在:目标材质不存在或已被软删除。
- 处理器侧错误分支
- 参数解析失败返回400;
- 服务层返回错误时统一转换为403(无权删除/不存在)。
章节来源
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
### 数据库记录影响与关联数据处理
- 删除策略
- 采用软删除:将textures表的status字段置为-1,不物理移除记录。
- 影响范围
- 材质记录仍保留,便于审计与历史追踪;
- 查询接口(如获取详情、搜索、我的材质)均会忽略status=-1的记录。
- 关联数据
- 收藏关系:删除操作不直接清理user_texture_favorites表中的收藏记录;
- 若需清理收藏,应在业务层面另行设计或在仓储层扩展软删除时级联处理(当前实现未体现)。
- 下载日志
- 删除操作不涉及texture_download_logs表的清理。
章节来源
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
- [internal/model/texture.go](file://internal/model/texture.go#L16-L35)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L66-L79)
### 类图(代码级)
```mermaid
classDiagram
class Texture {
+int64 id
+int64 uploader_id
+string name
+string description
+TextureType type
+string url
+string hash
+int size
+bool is_public
+int download_count
+int favorite_count
+bool is_slim
+int16 status
+time created_at
+time updated_at
}
class UserTextureFavorite {
+int64 id
+int64 user_id
+int64 texture_id
+time created_at
}
class TextureDownloadLog {
+int64 id
+int64 texture_id
+*int64 user_id
+string ip_address
+string user_agent
+time created_at
}
Texture --> UserTextureFavorite : "收藏关系"
Texture --> TextureDownloadLog : "下载日志"
```
图表来源
- [internal/model/texture.go](file://internal/model/texture.go#L16-L35)
- [internal/model/texture.go](file://internal/model/texture.go#L42-L57)
- [internal/model/texture.go](file://internal/model/texture.go#L60-L71)
## 依赖分析
- 组件耦合
- 处理器依赖服务层;服务层依赖仓储层;仓储层依赖数据库访问工具。
- 关键依赖链
- 路由 -> 处理器 -> 服务层 -> 仓储层 -> 数据库
- 可能的循环依赖
- 当前文件间未发现循环导入;各层职责清晰,符合分层架构。
```mermaid
graph LR
Routes["routes.go"] --> Handler["texture_handler.go"]
Handler --> Service["texture_service.go"]
Service --> Repo["texture_repository.go"]
Repo --> DB["数据库"]
```
图表来源
- [internal/handler/routes.go](file://internal/handler/routes.go#L42-L61)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
章节来源
- [internal/handler/routes.go](file://internal/handler/routes.go#L42-L61)
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
- [internal/repository/texture_repository.go](file://internal/repository/texture_repository.go#L126-L130)
## 性能考虑
- 查询与软删除
- 删除前的查询与软删除均为单记录操作,复杂度低。
- 索引与过滤
- 材质查询通常按UploaderID、状态、公开性等条件过滤,建议确保相关列具备索引以提升查询效率。
- 批量与事务
- 当前删除为单条记录操作,无需事务;若未来扩展批量删除,需评估事务边界与回滚策略。
## 故障排查指南
- 401 未授权
- 检查请求头Authorization是否携带有效JWT;确认中间件已正确注入user_id。
- 400 参数错误
- 检查路径参数id是否为合法整数。
- 403 无权删除
- 确认当前用户ID与材质记录的UploaderID一致;核对服务层权限校验逻辑。
- 403 材质不存在
- 确认材质ID有效且未被软删除;检查仓储层查询是否正确忽略status=-1。
- 日志定位
- 处理器与服务层均记录错误日志,可据此快速定位问题。
章节来源
- [internal/handler/texture_handler.go](file://internal/handler/texture_handler.go#L371-L419)
- [internal/service/texture_service.go](file://internal/service/texture_service.go#L143-L160)
## 结论
- 权限验证严格:仅上传者可删除材质,服务层明确校验请求者ID与UploaderID一致性。
- 删除策略为软删除:通过status字段标记删除,不破坏历史数据与关联完整性。
- 错误处理清晰:处理器将服务层错误映射为403,配合日志便于排障。
- 关联清理:当前实现未清理收藏关系,如需可在业务层补充或扩展仓储层软删除逻辑。