Files
backend/.qoder/repowiki/zh/content/API参考/用户API/个人资料管理.md

357 lines
15 KiB
Markdown
Raw Normal View History

# 个人资料管理
<cite>
**本文引用的文件**
- [user_handler.go](file://internal/handler/user_handler.go)
- [routes.go](file://internal/handler/routes.go)
- [auth.go](file://internal/middleware/auth.go)
- [user_service.go](file://internal/service/user_service.go)
- [user_repository.go](file://internal/repository/user_repository.go)
- [common.go](file://internal/types/common.go)
- [response.go](file://internal/model/response.go)
- [user.go](file://internal/model/user.go)
- [jwt.go](file://pkg/auth/jwt.go)
- [password.go](file://pkg/auth/password.go)
- [user_handler_test.go](file://internal/handler/user_handler_test.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向开发者与测试人员系统性梳理“个人资料管理”相关API重点覆盖
- GET /api/v1/user/profile获取当前登录用户的详细信息
- PUT /api/v1/user/profile更新头像与密码邮箱修改请使用独立接口
文档将解释 UserInfo 数据结构各字段及 JSON 序列化规则;详述密码更新的安全机制(旧密码校验与新密码加密);说明头像 URL 的更新流程(预签名上传 URL 生成与最终更新)。同时,结合 user_handler.go 中的 GetUserProfile 与 UpdateUserProfile 函数,说明 JWT 中间件如何注入用户上下文,服务层如何调用仓库层执行持久化操作,并提供完整请求/响应示例(含成功与常见错误场景)。
## 项目结构
围绕个人资料管理的代码组织遵循“控制器-中间件-服务-仓库-模型”的分层设计,路由在 v1 组下统一挂载,用户相关接口均受 JWT 认证保护。
```mermaid
graph TB
Client["客户端"] --> Router["Gin 路由器"]
Router --> GroupV1["/api/v1 用户组<br/>启用 AuthMiddleware()"]
GroupV1 --> GetUserProfile["GET /api/v1/user/profile"]
GroupV1 --> UpdateUserProfile["PUT /api/v1/user/profile"]
GroupV1 --> AvatarUploadURL["POST /api/v1/user/avatar/upload-url"]
GroupV1 --> UpdateAvatar["PUT /api/v1/user/avatar"]
GetUserProfile --> Handler["user_handler.GetUserProfile"]
UpdateUserProfile --> Handler
AvatarUploadURL --> Handler
UpdateAvatar --> Handler
Handler --> Middleware["AuthMiddleware()<br/>注入 user_id/username/role"]
Handler --> Service["user_service.*"]
Service --> Repo["user_repository.*"]
Service --> Model["model.User"]
Handler --> Types["types.UserInfo / UpdateUserRequest"]
Handler --> Resp["model.Response / model.ErrorResponse"]
```
图表来源
- [routes.go](file://internal/handler/routes.go#L16-L41)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
- [user_handler.go](file://internal/handler/user_handler.go#L17-L193)
- [user_service.go](file://internal/service/user_service.go#L124-L164)
- [user_repository.go](file://internal/repository/user_repository.go#L17-L69)
- [common.go](file://internal/types/common.go#L42-L47)
- [response.go](file://internal/model/response.go#L1-L86)
章节来源
- [routes.go](file://internal/handler/routes.go#L16-L41)
## 核心组件
- 控制器层Handler
- GetUserProfile从上下文提取 user_id查询用户并返回 UserInfo
- UpdateUserProfile接收 UpdateUserRequest按需更新密码与头像返回最新 UserInfo
- 中间件层AuthMiddleware
- 解析 Authorization: Bearer <token>,校验 JWT 并将用户信息写入上下文
- 服务层Service
- GetUserByID封装仓库查询
- UpdateUserInfo / UpdateUserAvatar封装仓库更新
- ChangeUserPassword校验旧密码并加密新密码后更新
- 仓库层Repository
- FindUserByID / UpdateUser / UpdateUserFields数据库读写
- 类型与模型
- types.UpdateUserRequest请求体定义
- types.UserInfo响应体定义
- model.User数据库映射模型
- model.Response / model.ErrorResponse统一响应结构
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L17-L193)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
- [user_service.go](file://internal/service/user_service.go#L124-L164)
- [user_repository.go](file://internal/repository/user_repository.go#L17-L69)
- [common.go](file://internal/types/common.go#L42-L47)
- [user.go](file://internal/model/user.go#L7-L21)
- [response.go](file://internal/model/response.go#L1-L86)
## 架构总览
以下序列图展示“获取/更新用户资料”的端到端流程,包含 JWT 中间件、控制器、服务与仓库的交互。
```mermaid
sequenceDiagram
participant C as "客户端"
participant R as "Gin 路由"
participant M as "AuthMiddleware"
participant H as "user_handler"
participant S as "user_service"
participant RP as "user_repository"
participant DB as "数据库"
C->>R : "GET /api/v1/user/profile"
R->>M : "鉴权中间件"
M-->>R : "注入 user_id/username/role"
R->>H : "GetUserProfile"
H->>S : "GetUserByID(user_id)"
S->>RP : "FindUserByID(user_id)"
RP->>DB : "SELECT ..."
DB-->>RP : "User"
RP-->>S : "User"
S-->>H : "User"
H-->>C : "200 + UserInfo"
C->>R : "PUT /api/v1/user/profile"
R->>M : "鉴权中间件"
M-->>R : "注入 user_id/username/role"
R->>H : "UpdateUserProfile"
H->>H : "解析请求体 UpdateUserRequest"
alt "更新密码"
H->>S : "ChangeUserPassword(user_id, old, new)"
S->>RP : "FindUserByID(user_id)"
RP->>DB : "SELECT ..."
DB-->>RP : "User"
RP-->>S : "User"
S->>S : "CheckPassword(旧密码)"
S->>S : "HashPassword(新密码)"
S->>RP : "UpdateUserFields(user_id, {password})"
RP->>DB : "UPDATE ..."
DB-->>RP : "OK"
end
alt "更新头像"
H->>S : "UpdateUserInfo(User)"
S->>RP : "UpdateUser(User)"
RP->>DB : "SAVE ..."
DB-->>RP : "OK"
end
H->>S : "GetUserByID(user_id)"
S->>RP : "FindUserByID(user_id)"
RP->>DB : "SELECT ..."
DB-->>RP : "User"
RP-->>S : "User"
S-->>H : "User"
H-->>C : "200 + 最新 UserInfo"
```
图表来源
- [routes.go](file://internal/handler/routes.go#L27-L41)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
- [user_handler.go](file://internal/handler/user_handler.go#L17-L193)
- [user_service.go](file://internal/service/user_service.go#L124-L164)
- [user_repository.go](file://internal/repository/user_repository.go#L17-L69)
## 详细组件分析
### GET /api/v1/user/profile获取当前用户资料
- 功能概述
- 仅限已登录用户访问,通过 Authorization: Bearer <token> 进行鉴权
- 中间件将 user_id 写入上下文,控制器据此查询用户并返回 UserInfo
- 请求与响应
- 请求Authorization: Bearer <token>
- 成功响应200 + model.Response{ data: types.UserInfo }
- 常见错误401 未授权、404 用户不存在
- 数据结构与序列化
- types.UserInfo 字段与 JSON 映射规则详见“附录-数据结构”
- 错误处理
- 未携带或无效的 Authorization 头:返回 401
- 查询不到用户:返回 404
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L17-L68)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
- [routes.go](file://internal/handler/routes.go#L27-L41)
- [common.go](file://internal/types/common.go#L113-L125)
- [response.go](file://internal/model/response.go#L1-L86)
### PUT /api/v1/user/profile更新头像与密码
- 功能概述
- 支持同时更新头像 URL 与密码;若仅更新头像,可不提供密码字段
- 密码更新安全机制:必须同时提供旧密码与新密码,服务层校验旧密码并通过 bcrypt 加密新密码后更新
- 头像更新流程:先生成预签名上传 URL上传完成后调用更新头像接口最终返回最新 UserInfo
- 请求体 UpdateUserRequest
- avatar可选头像 URL
- old_password可选修改密码时必填
- new_password可选修改密码时必填
- 安全机制与流程
- 旧密码校验:服务层通过 bcrypt 校验
- 新密码加密:服务层使用 bcrypt 生成哈希
- 头像更新:控制器直接更新用户记录;若仅更新头像,服务层保存用户对象
- 错误处理
- 未授权401
- 参数错误400如仅提供新密码或仅提供旧密码
- 用户不存在404
- 服务器错误500
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L70-L193)
- [user_service.go](file://internal/service/user_service.go#L141-L164)
- [password.go](file://pkg/auth/password.go#L1-L21)
- [common.go](file://internal/types/common.go#L42-L47)
- [response.go](file://internal/model/response.go#L27-L53)
### 头像上传与更新流程(补充说明)
- 生成预签名上传 URL
- POST /api/v1/user/avatar/upload-url
- 输入file_name
- 输出post_url、form_data、avatar_url、expires_in
- 更新头像 URL
- PUT /api/v1/user/avatar
- 输入avatar_url查询参数
- 输出:最新 UserInfo
- 注意事项
- 该流程与“更新资料”接口不同,前者用于生成上传凭证,后者用于将最终 URL 写入数据库
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L195-L326)
- [routes.go](file://internal/handler/routes.go#L34-L39)
- [common.go](file://internal/types/common.go#L68-L80)
### UserInfo 数据结构与 JSON 序列化规则
- 字段说明
- id用户唯一标识
- username用户名
- email邮箱
- avatar头像 URL
- points积分
- role角色如 user
- status账户状态1 正常0 禁用,-1 删除)
- last_login_at最近登录时间可空
- created_at / updated_at创建与更新时间
- JSON 映射
- model.User 中的 Password 字段在 JSON 中不返回(避免泄露)
- 其他字段按 gorm 标签映射到 JSON
章节来源
- [user.go](file://internal/model/user.go#L7-L21)
- [common.go](file://internal/types/common.go#L113-L125)
### 请求/响应示例(含错误场景)
- 获取资料(成功)
- 请求Authorization: Bearer <token>
- 响应200 + { code: 200, message: "操作成功", data: { id, username, email, avatar, points, role, status, last_login_at, created_at, updated_at } }
- 更新资料(仅更新头像)
- 请求体:{ avatar: "https://example.com/new-avatar.png" }
- 响应200 + 最新 UserInfo
- 更新资料(仅更新密码)
- 请求体:{ old_password: "...", new_password: "..." }
- 响应200 + 最新 UserInfo
- 更新资料(参数错误)
- 请求体:{ new_password: "..." }(缺少 old_password
- 响应400 + { code: 400, message: "请求参数错误", error: "修改密码需要提供原密码" }
- 未授权
- 请求:未携带或无效 Authorization
- 响应401 + { code: 401, message: "未授权,请先登录" }
- 用户不存在
- 响应404 + { code: 404, message: "资源不存在" }
- 服务器错误
- 响应500 + { code: 500, message: "服务器内部错误" }
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L17-L193)
- [response.go](file://internal/model/response.go#L27-L53)
- [user_handler_test.go](file://internal/handler/user_handler_test.go#L52-L106)
## 依赖分析
- 控制器依赖
- user_handler 依赖 gin 上下文中的 user_id来自 AuthMiddleware
- 控制器调用 user_service 的 GetUserByID、UpdateUserInfo、ChangeUserPassword、UpdateUserAvatar
- 服务层依赖
- user_service 依赖 user_repository 的 FindUserByID、UpdateUser、UpdateUserFields
- 使用 pkg/auth 的 HashPassword 与 CheckPassword
- 中间件依赖
- AuthMiddleware 依赖 pkg/auth/jwt 的 ValidateToken将 claims 写入上下文
- 模型与类型
- model.User 作为 ORM 映射
- types.UserInfo / UpdateUserRequest 作为 API 层数据契约
```mermaid
graph LR
Handler["user_handler"] --> Service["user_service"]
Handler --> Middleware["AuthMiddleware"]
Handler --> Types["types.*"]
Handler --> Resp["model.Response / ErrorResponse"]
Service --> Repo["user_repository"]
Service --> Auth["pkg/auth/*"]
Middleware --> JWT["pkg/auth/jwt"]
Repo --> Model["model.User"]
```
图表来源
- [user_handler.go](file://internal/handler/user_handler.go#L17-L193)
- [user_service.go](file://internal/service/user_service.go#L124-L164)
- [user_repository.go](file://internal/repository/user_repository.go#L17-L69)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
- [jwt.go](file://pkg/auth/jwt.go#L1-L71)
- [password.go](file://pkg/auth/password.go#L1-L21)
- [common.go](file://internal/types/common.go#L42-L47)
- [response.go](file://internal/model/response.go#L1-L86)
- [user.go](file://internal/model/user.go#L7-L21)
## 性能考虑
- 数据库查询
- GetUserByID 使用精确主键查询,复杂度 O(1),建议保持索引完善
- 密码处理
- bcrypt 默认成本较高,建议在高并发场景关注 CPU 开销;可通过配置调整成本值
- 缓存策略
- 对频繁读取的用户资料可在应用层引入缓存(如 Redis减少数据库压力
- 日志与可观测性
- 控制器与服务层已记录关键错误日志,建议配合链路追踪与指标监控
## 故障排查指南
- 401 未授权
- 检查 Authorization 头是否为 Bearer <token> 格式
- 确认 token 未过期且签名正确
- 400 参数错误
- 密码更新时必须同时提供 old_password 与 new_password
- 头像 URL 必须为合法 URL
- 404 用户不存在
- 确认 user_id 是否有效;检查用户状态是否被禁用或删除
- 500 服务器错误
- 检查数据库连接与事务执行情况;查看服务层日志定位具体异常
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L27-L193)
- [user_handler_test.go](file://internal/handler/user_handler_test.go#L108-L152)
- [response.go](file://internal/model/response.go#L27-L53)
## 结论
个人资料管理 API 采用清晰的分层架构JWT 中间件负责鉴权并将用户上下文注入控制器,控制器协调服务层完成业务逻辑,服务层通过仓库层与数据库交互。密码更新具备严格的旧密码校验与新密码加密流程,头像更新支持预签名上传与最终 URL 写入。通过统一的响应结构与完善的错误处理,系统在安全性与易用性之间取得平衡。
## 附录
### 数据结构与序列化规则
- types.UserInfo
- 字段id、username、email、avatar、points、role、status、last_login_at、created_at、updated_at
- JSON 映射:与 model.User 字段一致,除 password 不返回
- types.UpdateUserRequest
- 字段avatar、old_password、new_password
- 校验:密码更新时 old_password 与 new_password 必须同时提供
- model.User数据库映射
- 字段id、username、password、email、avatar、points、role、status、properties、last_login_at、created_at、updated_at
- JSONpassword 不返回;其他字段按标签映射
- model.Response / model.ErrorResponse
- 统一响应结构,包含 code、message、data/error
章节来源
- [common.go](file://internal/types/common.go#L42-L47)
- [common.go](file://internal/types/common.go#L113-L125)
- [user.go](file://internal/model/user.go#L7-L21)
- [response.go](file://internal/model/response.go#L1-L86)