# 更新与删除 **本文档引用的文件** - [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 用于用户身份验证。 ```mermaid 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"] ``` 图表来源 - [routes.go](file://internal/handler/routes.go#L63-L79) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339) - [profile_service.go](file://internal/service/profile_service.go#L92-L159) - [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77) - [profile.go](file://internal/model/profile.go#L7-L29) - [common.go](file://internal/types/common.go#L201-L206) 章节来源 - [routes.go](file://internal/handler/routes.go#L63-L79) - [auth.go](file://internal/middleware/auth.go#L12-L56) ## 核心组件 - 路由注册:在路由组 /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 提供基础 CRUD;SetActiveProfile 使用事务确保“同一用户仅有一个活跃档案”。 章节来源 - [routes.go](file://internal/handler/routes.go#L63-L79) - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339) - [profile_service.go](file://internal/service/profile_service.go#L92-L159) - [profile_repository.go](file://internal/repository/profile_repository.go#L89-L109) ## 架构总览 下图展示了从客户端到数据库的调用链路,以及权限校验与事务控制的关键节点。 ```mermaid 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)" ``` 图表来源 - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339) - [profile_service.go](file://internal/service/profile_service.go#L92-L159) - [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77) ## 详细组件分析 ### 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 事务包裹,确保原子性与一致性。 ```mermaid flowchart TD Start(["进入 UpdateProfile"]) --> Parse["解析请求体
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{"档案归属校验
profile.UserID == user_id"} Perm --> |否| Resp403["返回 403 无权操作"] Perm --> |是| NameChk{"是否更新 name?"} NameChk --> |是| Dup{"检查同名是否存在"} Dup --> |是| Resp400["返回 400 参数错误"] Dup --> |否| Apply["应用字段更新
name/skin_id/cape_id"] NameChk --> |否| Apply Apply --> Save["保存更新 UpdateProfile"] Save --> Reload["重新加载档案 FindProfileByUUID"] Reload --> Resp200["返回 200 成功"] ``` 图表来源 - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L280) - [profile_service.go](file://internal/service/profile_service.go#L92-L135) - [profile_repository.go](file://internal/repository/profile_repository.go#L19-L71) 章节来源 - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L280) - [profile_service.go](file://internal/service/profile_service.go#L92-L135) - [common.go](file://internal/types/common.go#L201-L206) ### 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。 ```mermaid 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 ``` 图表来源 - [profile_handler.go](file://internal/handler/profile_handler.go#L282-L339) - [profile_service.go](file://internal/service/profile_service.go#L137-L159) - [profile_repository.go](file://internal/repository/profile_repository.go#L73-L77) 章节来源 - [profile_handler.go](file://internal/handler/profile_handler.go#L282-L339) - [profile_service.go](file://internal/service/profile_service.go#L137-L159) ### 权限验证与鉴权中间件 - 中间件职责 - 校验 Authorization 头格式(Bearer token)。 - 使用 JWT 服务验证令牌有效性,并将 user_id、username、role 写入上下文。 - 处理器侧使用 - 处理器从上下文读取 user_id,若不存在则返回 401。 - 服务层进一步校验档案归属,不匹配返回 403。 - JWT 服务 - 生成与验证使用 HS256 签名算法,过期时间可配置。 章节来源 - [auth.go](file://internal/middleware/auth.go#L12-L56) - [jwt.go](file://pkg/auth/jwt.go#L10-L71) - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339) ### 事务性保证与数据一致性 - 更新操作 - 当前 UpdateProfile 通过单次 save/update 完成,不涉及跨表事务。 - 若未来扩展为多步更新(例如同时更新多个字段或关联表),建议在服务层使用 GORM 事务包裹,确保原子性与一致性。 - 删除操作 - DeleteProfile 为单条删除,不涉及跨表事务。 - 活跃档案设置 - SetActiveProfile 使用 GORM 事务,先将用户所有档案设为非活跃,再将目标档案设为活跃,保证“同一用户仅有一个活跃档案”的约束。 章节来源 - [profile_service.go](file://internal/service/profile_service.go#L92-L135) - [profile_service.go](file://internal/service/profile_service.go#L137-L188) - [profile_repository.go](file://internal/repository/profile_repository.go#L89-L109) ## 依赖关系分析 - 路由依赖 - profile 路由组使用鉴权中间件,确保后续接口均需有效 JWT。 - 处理器依赖 - profile_handler 依赖鉴权中间件提供的 user_id,依赖 service 层执行业务逻辑,依赖 model/types 定义的数据结构。 - 服务层依赖 - profile_service 依赖 repository 层进行数据访问,依赖数据库连接管理器。 - 仓储层依赖 - profile_repository 依赖 GORM 与数据库连接。 - 鉴权与数据库 - jwt.go 提供令牌签发与校验;manager.go 提供数据库连接获取与迁移。 ```mermaid 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"] ``` 图表来源 - [routes.go](file://internal/handler/routes.go#L63-L79) - [auth.go](file://internal/middleware/auth.go#L12-L56) - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339) - [profile_service.go](file://internal/service/profile_service.go#L92-L159) - [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77) - [manager.go](file://pkg/database/manager.go#L35-L50) - [common.go](file://internal/types/common.go#L201-L206) - [profile.go](file://internal/model/profile.go#L7-L29) - [jwt.go](file://pkg/auth/jwt.go#L10-L71) 章节来源 - [routes.go](file://internal/handler/routes.go#L63-L79) - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339) - [profile_service.go](file://internal/service/profile_service.go#L92-L159) - [profile_repository.go](file://internal/repository/profile_repository.go#L19-L77) - [manager.go](file://pkg/database/manager.go#L35-L50) ## 性能考量 - 查询与预加载 - 仓储层在查询档案时使用预加载关联的皮肤与披风,有助于减少 N+1 查询问题,提升响应速度。 - 事务范围 - 当前更新与删除均为单条操作,事务开销较小;若扩展为多步更新,建议将相关操作合并到事务中,避免部分成功导致的不一致。 - 唯一性检查 - 更新名称时进行同名检查,避免并发场景下的重复;建议在数据库层面增加唯一索引以降低竞争条件风险。 - 日志与可观测性 - 处理器在发生错误时记录日志,便于定位问题;建议在服务层也增加关键步骤的日志埋点。 章节来源 - [profile_repository.go](file://internal/repository/profile_repository.go#L19-L31) - [profile_service.go](file://internal/service/profile_service.go#L92-L135) ## 故障排查指南 - 401 未授权 - 检查请求头 Authorization 是否为 Bearer 令牌格式;确认令牌未过期且签名正确。 - 403 无权操作 - 确认当前用户是否拥有目标档案;检查档案所属 user_id 与当前用户是否一致。 - 404 资源不存在 - 确认档案 UUID 是否正确;检查数据库中是否存在该记录。 - 400 参数错误 - 检查请求体字段是否符合长度与类型要求;例如 name 长度应在 1-16 之间。 - 500 服务器错误 - 查看服务端日志,关注数据库连接、唯一性冲突、事务回滚等问题。 章节来源 - [profile_handler.go](file://internal/handler/profile_handler.go#L197-L339) - [profile_service.go](file://internal/service/profile_service.go#L92-L159) ## 结论 - PUT /api/v1/profile/:uuid 与 DELETE /api/v1/profile/:uuid 已具备完善的权限校验与存在性验证机制。 - 更新操作支持名称与材质字段的灵活更新,删除操作简洁可靠。 - 服务层与仓储层清晰分离职责,当前更新与删除为单步操作;若未来扩展为多步更新,建议引入事务以保障一致性。 - 建议在数据库层面完善唯一性约束,并在服务层增加关键步骤的日志埋点,以便于问题定位与性能优化。