Files
backend/.qoder/repowiki/zh/content/API参考/档案API/更新与删除.md
lan a4b6c5011e
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
chore(git): 更新.gitignore以忽略新的本地文件
2025-11-30 08:33:17 +08:00

16 KiB
Raw Blame History

更新与删除

**本文档引用的文件** - [routes.go](file://internal/handler/routes.go) - [profile_handler.go](file://internal/handler/profile_handler.go) - [auth.go](file://internal/middleware/auth.go) - [profile_service.go](file://internal/service/profile_service.go) - [profile_repository.go](file://internal/repository/profile_repository.go) - [profile.go](file://internal/model/profile.go) - [common.go](file://internal/types/common.go) - [jwt.go](file://pkg/auth/jwt.go) - [manager.go](file://pkg/database/manager.go)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排查指南
  9. 结论

简介

本章节面向需要对接“档案更新与删除”接口的开发者,重点说明以下内容:

  • PUT /api/v1/profile/:uuid 的更新流程:支持修改档案名称、更换关联的皮肤或披风材质;强调“只能修改自己名下的档案”的权限校验。
  • DELETE /api/v1/profile/:uuid 的删除流程:删除前进行权限检查与档案存在性验证。
  • 请求体格式与错误处理策略如403无权操作、404资源不存在
  • 结合 service 层代码解释更新操作的事务性保证与数据一致性维护机制。

项目结构

围绕档案更新与删除功能,涉及如下关键模块:

  • 路由层:在路由中注册了 /api/v1/profile/{uuid} 的 PUT 与 DELETE 接口,并统一使用鉴权中间件。
  • 中间件层JWT 鉴权中间件负责解析 Authorization 头并校验令牌有效性,将用户标识注入上下文。
  • 处理器层profile_handler 负责接收请求、绑定参数、调用 service 并输出响应。
  • 服务层profile_service 执行业务逻辑,包含权限校验、唯一性检查、更新与删除等。
  • 仓储层profile_repository 封装数据库访问,提供查询、更新、删除与事务性操作。
  • 模型与类型profile 模型定义档案字段及关联关系types 定义请求与响应结构。
  • 数据库与鉴权GORM 管理数据库连接JWT 用于用户身份验证。
graph TB
Client["客户端"] --> Routes["路由: /api/v1/profile/{uuid}"]
Routes --> AuthMW["鉴权中间件: JWT"]
AuthMW --> Handler["处理器: profile_handler"]
Handler --> Service["服务: profile_service"]
Service --> Repo["仓储: profile_repository"]
Repo --> DB["数据库: GORM"]
Handler --> Model["模型: Profile"]
Handler --> Types["类型: UpdateProfileRequest"]

图表来源

章节来源

核心组件

  • 路由注册:在路由组 /api/v1/profile 下注册了 GET/:uuid、POST/、GET/、PUT/:uuid、DELETE/:uuid、POST/:uuid/activate 等接口,其中 PUT 与 DELETE 对应本节主题。
  • 鉴权中间件:要求 Authorization 头为 Bearer 令牌,校验通过后将 user_id 等信息写入上下文。
  • 处理器UpdateProfile 与 DeleteProfile 分别调用 service 层执行业务逻辑,并根据 service 返回的错误映射为合适的 HTTP 状态码。
  • 服务层UpdateProfile 与 DeleteProfile 在执行前均进行“档案存在性验证”和“权限校验”,并在必要时使用数据库事务保证一致性。
  • 仓储层FindProfileByUUID、UpdateProfile、DeleteProfile 提供基础 CRUDSetActiveProfile 使用事务确保“同一用户仅有一个活跃档案”。

章节来源

架构总览

下图展示了从客户端到数据库的调用链路,以及权限校验与事务控制的关键节点。

sequenceDiagram
participant C as "客户端"
participant R as "路由"
participant M as "鉴权中间件"
participant H as "处理器 : UpdateProfile/DeleteProfile"
participant S as "服务 : profile_service"
participant P as "仓储 : profile_repository"
participant D as "数据库 : GORM"
C->>R : "PUT /api/v1/profile/{uuid}"
R->>M : "进入鉴权中间件"
M-->>R : "校验通过,注入 user_id"
R->>H : "转发请求"
H->>S : "调用 UpdateProfile/ DeleteProfile"
S->>P : "FindProfileByUUID"
P->>D : "查询档案"
D-->>P : "返回档案或记录不存在"
P-->>S : "返回结果"
alt "更新场景"
S->>S : "权限校验 : profile.UserID == user_id"
S->>P : "可选 : 名称唯一性检查"
S->>P : "UpdateProfile(更新字段)"
P->>D : "保存更新"
D-->>P : "成功"
P-->>S : "返回更新后的档案"
else "删除场景"
S->>S : "权限校验 : profile.UserID == user_id"
S->>P : "DeleteProfile"
P->>D : "删除记录"
D-->>P : "成功"
end
S-->>H : "返回结果或错误"
H-->>C : "HTTP 响应(200/403/404/500)"

图表来源

详细组件分析

PUT /api/v1/profile/:uuid 更新档案

  • 功能概述
    • 支持修改档案名称(可选)、更换关联的皮肤或披风材质(可选)。
    • 严格权限控制:仅允许修改“自己名下的档案”。
  • 请求路径与方法
    • 方法: PUT
    • 路径: /api/v1/profile/:uuid
    • 鉴权: 需要 Bearer 令牌
  • 请求体结构
    • 字段:
      • name: 字符串,长度范围 1-16可选
      • skin_id: 整数指向材质记录的ID可选
      • cape_id: 整数指向材质记录的ID可选
    • 示例: 仅更新名称
    • 示例: 仅更换皮肤
    • 示例: 同时更换皮肤与披风
  • 成功响应
    • 返回更新后的档案信息(包含 uuid、user_id、name、skin_id、cape_id、is_active、last_used_at、created_at、updated_at
  • 错误处理
    • 400: 请求参数错误(如字段校验失败)
    • 401: 未授权(缺少或无效的 Authorization 头)
    • 403: 无权操作(目标档案不属于当前用户)
    • 404: 资源不存在档案UUID不存在
    • 500: 服务器内部错误(数据库异常、唯一性冲突等)
  • 权限与存在性校验
    • 处理器从上下文取出 user_id若缺失则直接返回 401。
    • 服务层先查询档案,若不存在返回 404随后校验 profile.UserID 是否等于 user_id否则返回 403。
  • 名称唯一性与字段更新
    • 当 name 发生变化时,服务层会检查同名是否已存在,若存在则返回 400。
    • skin_id 与 cape_id 为可选字段,仅当传入非空值时才更新对应字段。
  • 事务性与一致性
    • 更新操作本身通过单条 save/update 完成,不涉及跨表事务。
    • 若未来扩展为多步更新(例如同时更新多个关联字段),建议在服务层使用 GORM 事务包裹,确保原子性与一致性。
flowchart TD
Start(["进入 UpdateProfile"]) --> Parse["解析请求体<br/>name/skin_id/cape_id"]
Parse --> GetCtx["从上下文获取 user_id"]
GetCtx --> HasUser{"user_id 存在?"}
HasUser --> |否| Resp401["返回 401 未授权"]
HasUser --> |是| Load["查询档案 FindProfileByUUID"]
Load --> Found{"档案存在?"}
Found --> |否| Resp404["返回 404 资源不存在"]
Found --> |是| Perm{"档案归属校验<br/>profile.UserID == user_id"}
Perm --> |否| Resp403["返回 403 无权操作"]
Perm --> |是| NameChk{"是否更新 name"}
NameChk --> |是| Dup{"检查同名是否存在"}
Dup --> |是| Resp400["返回 400 参数错误"]
Dup --> |否| Apply["应用字段更新<br/>name/skin_id/cape_id"]
NameChk --> |否| Apply
Apply --> Save["保存更新 UpdateProfile"]
Save --> Reload["重新加载档案 FindProfileByUUID"]
Reload --> Resp200["返回 200 成功"]

图表来源

章节来源

DELETE /api/v1/profile/:uuid 删除档案

  • 功能概述
    • 删除指定 UUID 的档案。
    • 删除前进行权限检查与档案存在性验证。
  • 请求路径与方法
    • 方法: DELETE
    • 路径: /api/v1/profile/:uuid
    • 鉴权: 需要 Bearer 令牌
  • 成功响应
    • 返回成功消息message: "删除成功")。
  • 错误处理
    • 401: 未授权(缺少或无效的 Authorization 头)
    • 403: 无权操作(目标档案不属于当前用户)
    • 404: 资源不存在档案UUID不存在
    • 500: 服务器内部错误(数据库异常)
  • 删除流程
    • 处理器从上下文取出 user_id若缺失则直接返回 401。
    • 服务层先查询档案,若不存在返回 404随后校验权限不匹配返回 403。
    • 通过权限校验后执行删除,成功返回 200。
sequenceDiagram
participant C as "客户端"
participant H as "处理器 : DeleteProfile"
participant S as "服务 : profile_service"
participant P as "仓储 : profile_repository"
participant D as "数据库 : GORM"
C->>H : "DELETE /api/v1/profile/{uuid}"
H->>S : "调用 DeleteProfile(uuid, user_id)"
S->>P : "FindProfileByUUID"
P->>D : "查询"
D-->>P : "返回档案或不存在"
P-->>S : "结果"
alt "档案不存在"
S-->>H : "返回 404"
H-->>C : "404"
else "权限校验失败"
S-->>H : "返回 403"
H-->>C : "403"
else "权限通过"
S->>P : "DeleteProfile"
P->>D : "删除记录"
D-->>P : "成功"
S-->>H : "返回 nil"
H-->>C : "200 成功"
end

图表来源

章节来源

权限验证与鉴权中间件

  • 中间件职责
    • 校验 Authorization 头格式Bearer token
    • 使用 JWT 服务验证令牌有效性,并将 user_id、username、role 写入上下文。
  • 处理器侧使用
    • 处理器从上下文读取 user_id若不存在则返回 401。
    • 服务层进一步校验档案归属,不匹配返回 403。
  • JWT 服务
    • 生成与验证使用 HS256 签名算法,过期时间可配置。

章节来源

事务性保证与数据一致性

  • 更新操作
    • 当前 UpdateProfile 通过单次 save/update 完成,不涉及跨表事务。
    • 若未来扩展为多步更新(例如同时更新多个字段或关联表),建议在服务层使用 GORM 事务包裹,确保原子性与一致性。
  • 删除操作
    • DeleteProfile 为单条删除,不涉及跨表事务。
  • 活跃档案设置
    • SetActiveProfile 使用 GORM 事务,先将用户所有档案设为非活跃,再将目标档案设为活跃,保证“同一用户仅有一个活跃档案”的约束。

章节来源

依赖关系分析

  • 路由依赖
    • profile 路由组使用鉴权中间件,确保后续接口均需有效 JWT。
  • 处理器依赖
    • profile_handler 依赖鉴权中间件提供的 user_id依赖 service 层执行业务逻辑,依赖 model/types 定义的数据结构。
  • 服务层依赖
    • profile_service 依赖 repository 层进行数据访问,依赖数据库连接管理器。
  • 仓储层依赖
    • profile_repository 依赖 GORM 与数据库连接。
  • 鉴权与数据库
    • jwt.go 提供令牌签发与校验manager.go 提供数据库连接获取与迁移。
graph LR
Routes["routes.go"] --> AuthMW["auth.go"]
AuthMW --> Handler["profile_handler.go"]
Handler --> Service["profile_service.go"]
Service --> Repo["profile_repository.go"]
Repo --> DBMgr["manager.go"]
Handler --> Types["common.go"]
Handler --> Model["profile.go"]
Handler --> JWT["jwt.go"]

图表来源

章节来源

性能考量

  • 查询与预加载
    • 仓储层在查询档案时使用预加载关联的皮肤与披风,有助于减少 N+1 查询问题,提升响应速度。
  • 事务范围
    • 当前更新与删除均为单条操作,事务开销较小;若扩展为多步更新,建议将相关操作合并到事务中,避免部分成功导致的不一致。
  • 唯一性检查
    • 更新名称时进行同名检查,避免并发场景下的重复;建议在数据库层面增加唯一索引以降低竞争条件风险。
  • 日志与可观测性
    • 处理器在发生错误时记录日志,便于定位问题;建议在服务层也增加关键步骤的日志埋点。

章节来源

故障排查指南

  • 401 未授权
    • 检查请求头 Authorization 是否为 Bearer 令牌格式;确认令牌未过期且签名正确。
  • 403 无权操作
    • 确认当前用户是否拥有目标档案;检查档案所属 user_id 与当前用户是否一致。
  • 404 资源不存在
    • 确认档案 UUID 是否正确;检查数据库中是否存在该记录。
  • 400 参数错误
    • 检查请求体字段是否符合长度与类型要求;例如 name 长度应在 1-16 之间。
  • 500 服务器错误
    • 查看服务端日志,关注数据库连接、唯一性冲突、事务回滚等问题。

章节来源

结论

  • PUT /api/v1/profile/:uuid 与 DELETE /api/v1/profile/:uuid 已具备完善的权限校验与存在性验证机制。
  • 更新操作支持名称与材质字段的灵活更新,删除操作简洁可靠。
  • 服务层与仓储层清晰分离职责,当前更新与删除为单步操作;若未来扩展为多步更新,建议引入事务以保障一致性。
  • 建议在数据库层面完善唯一性约束,并在服务层增加关键步骤的日志埋点,以便于问题定位与性能优化。