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

336 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 邮箱变更
<cite>
**本文引用的文件**
- [user_handler.go](file://internal/handler/user_handler.go)
- [routes.go](file://internal/handler/routes.go)
- [auth.go](file://internal/middleware/auth.go)
- [verification_service.go](file://internal/service/verification_service.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)
- [redis.go](file://pkg/redis/redis.go)
- [email.go](file://pkg/email/email.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能与可靠性](#性能与可靠性)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
## 简介
本文档围绕“POST /api/v1/user/change-email”端点系统化阐述邮箱变更的完整流程与技术实现细节。该接口要求用户提供新邮箱地址与验证码后端通过验证服务核验Redis中存储的验证码随后由用户服务层执行数据库更新确保邮箱唯一性与安全性。文档还解释了请求体结构、验证码生成与存储机制、错误处理策略并给出成功与失败场景的响应示例强调该操作需要有效的JWT认证。
## 项目结构
- 路由与鉴权
- 路由在 v1 组下用户相关接口均受JWT中间件保护。
- 邮箱变更接口位于 /api/v1/user/change-email采用POST方法。
- 控制器层
- 用户控制器负责接收请求、参数校验、调用服务层、组装响应。
- 服务层
- 验证服务生成、发送、校验验证码验证码以特定键规则存入Redis。
- 用户服务:执行邮箱唯一性检查与数据库更新。
- 数据访问层
- 用户仓库封装GORM操作提供按邮箱查询与字段更新能力。
- 基础设施
- Redis客户端提供键值存取、过期控制、删除等操作。
- 邮件服务:根据类型发送不同主题的验证码邮件。
```mermaid
graph TB
Client["客户端"] --> Routes["路由: /api/v1/user/change-email"]
Routes --> AuthMW["JWT中间件"]
AuthMW --> Handler["用户处理器: ChangeEmail"]
Handler --> VerifySvc["验证服务: VerifyCode"]
Handler --> UserSvc["用户服务: ChangeUserEmail"]
VerifySvc --> Redis["Redis: 验证码存储"]
UserSvc --> Repo["用户仓库: UpdateUserFields"]
Repo --> DB["数据库: user 表"]
VerifySvc --> Email["邮件服务: SendChangeEmail"]
```
图表来源
- [routes.go](file://internal/handler/routes.go#L1-L120)
- [auth.go](file://internal/middleware/auth.go#L1-L79)
- [user_handler.go](file://internal/handler/user_handler.go#L328-L416)
- [verification_service.go](file://internal/service/verification_service.go#L1-L119)
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [user_repository.go](file://internal/repository/user_repository.go#L65-L69)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
- [email.go](file://pkg/email/email.go#L47-L55)
章节来源
- [routes.go](file://internal/handler/routes.go#L1-L120)
- [auth.go](file://internal/middleware/auth.go#L1-L79)
## 核心组件
- 请求体结构
- ChangeEmailRequest.new_email新邮箱地址必填且符合邮箱格式。
- ChangeEmailRequest.verification_code验证码必填且长度为6。
- 鉴权要求
- 所有用户相关接口均需携带有效的Bearer JWT。
- 验证码机制
- 验证码类型常量包含“change_email”用于区分不同用途。
- 验证码长度为6有效期10分钟发送频率限制1分钟。
- 验证通过后Redis中的验证码键会被删除。
- 邮箱唯一性校验
- 在更新邮箱前,查询数据库确认新邮箱未被其他用户占用;若被占用则拒绝。
- 错误处理
- 参数错误、未授权、验证码错误、邮箱已被使用、服务器内部错误等均有明确响应码与消息。
章节来源
- [common.go](file://internal/types/common.go#L62-L66)
- [verification_service.go](file://internal/service/verification_service.go#L14-L24)
- [verification_service.go](file://internal/service/verification_service.go#L79-L98)
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [response.go](file://internal/model/response.go#L27-L53)
## 架构总览
POST /api/v1/user/change-email 的端到端流程如下:
1. 客户端携带JWT向 /api/v1/user/change-email 发起POST请求请求体包含 new_email 与 verification_code。
2. 路由匹配到用户组JWT中间件校验通过后进入用户处理器。
3. 处理器解析请求体并调用验证服务验证Redis中对应类型的验证码是否匹配。
4. 验证通过后,处理器调用用户服务更新数据库中的邮箱字段。
5. 更新完成后,重新查询用户信息并返回成功响应。
```mermaid
sequenceDiagram
participant C as "客户端"
participant R as "路由"
participant M as "JWT中间件"
participant H as "用户处理器"
participant VS as "验证服务"
participant RS as "Redis"
participant US as "用户服务"
participant UR as "用户仓库"
participant DB as "数据库"
C->>R : "POST /api/v1/user/change-email"
R->>M : "鉴权"
M-->>R : "通过"
R->>H : "进入ChangeEmail"
H->>VS : "VerifyCode(email, code, type=change_email)"
VS->>RS : "Get(verification : code : change_email : new_email)"
RS-->>VS : "验证码"
VS-->>H : "验证通过"
H->>US : "ChangeUserEmail(userID, new_email)"
US->>UR : "FindUserByEmail(new_email)"
UR-->>US : "查询结果"
US->>UR : "UpdateUserFields(userID, {email : new_email})"
UR->>DB : "更新"
DB-->>UR : "成功"
UR-->>US : "成功"
US-->>H : "成功"
H-->>C : "返回用户信息"
```
图表来源
- [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#L328-L416)
- [verification_service.go](file://internal/service/verification_service.go#L79-L98)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [user_repository.go](file://internal/repository/user_repository.go#L65-L69)
## 详细组件分析
### 控制器ChangeEmail 端点
- 功能职责
- 从上下文提取用户IDJWT中间件注入
- 解析请求体为 ChangeEmailRequest。
- 调用验证服务核验验证码(类型为 change_email
- 调用用户服务更新邮箱。
- 重新查询用户信息并返回成功响应。
- 错误处理
- 缺少Authorization头或无效token返回401。
- 请求体绑定失败返回400。
- 验证码错误或过期返回400。
- 邮箱已被占用返回400。
- 用户不存在返回404。
- 其他异常返回500。
```mermaid
flowchart TD
Start(["进入ChangeEmail"]) --> GetCtx["获取user_id"]
GetCtx --> CheckAuth{"user_id存在?"}
CheckAuth -- 否 --> Resp401["返回401未授权"]
CheckAuth -- 是 --> BindReq["绑定ChangeEmailRequest"]
BindReq --> BindOK{"绑定成功?"}
BindOK -- 否 --> Resp400["返回400参数错误"]
BindOK -- 是 --> Verify["调用VerifyCode(type=change_email)"]
Verify --> VerifyOK{"验证通过?"}
VerifyOK -- 否 --> Resp400V["返回400验证码错误/过期"]
VerifyOK -- 是 --> Update["调用ChangeUserEmail"]
Update --> UpdateOK{"更新成功?"}
UpdateOK -- 否 --> Resp400E["返回400邮箱已被使用/其他错误"]
UpdateOK -- 是 --> Reload["重新查询用户信息"]
Reload --> Resp200["返回200成功"]
```
图表来源
- [user_handler.go](file://internal/handler/user_handler.go#L328-L416)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
- [response.go](file://internal/model/response.go#L27-L53)
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L328-L416)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
- [response.go](file://internal/model/response.go#L27-L53)
### 验证服务:验证码生成、发送与校验
- 生成与存储
- 生成6位数字验证码。
- 以键模式 verification:code:{type}:{email} 写入Redis有效期10分钟。
- 发送频率限制键 verification:rate_limit:{type}:{email} 设置1分钟。
- 校验逻辑
- 读取Redis中的验证码并与请求一致进行比对。
- 校验通过后删除该验证码键。
- 邮件发送
- 根据类型选择不同主题与正文,发送更换邮箱验证码邮件。
```mermaid
flowchart TD
Gen["生成6位验证码"] --> Store["写入Redis: code键(10分钟)"]
Store --> Rate["设置rate_limit键(1分钟)"]
Rate --> Mail["发送邮件(ChangeEmail)"]
Mail --> Verify["VerifyCode: 读取code键"]
Verify --> Match{"是否匹配?"}
Match -- 否 --> Err["返回错误(验证码错误/过期)"]
Match -- 是 --> Del["删除code键"]
Del --> Ok["返回成功"]
```
图表来源
- [verification_service.go](file://internal/service/verification_service.go#L26-L77)
- [verification_service.go](file://internal/service/verification_service.go#L79-L98)
- [email.go](file://pkg/email/email.go#L47-L55)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
章节来源
- [verification_service.go](file://internal/service/verification_service.go#L14-L24)
- [verification_service.go](file://internal/service/verification_service.go#L26-L77)
- [verification_service.go](file://internal/service/verification_service.go#L79-L98)
- [email.go](file://pkg/email/email.go#L47-L55)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
### 用户服务:邮箱唯一性与更新
- 唯一性检查
- 查询新邮箱是否已被其他用户占用若存在且ID不同则报错。
- 更新逻辑
- 使用UpdateUserFields更新邮箱字段。
```mermaid
flowchart TD
Check["FindUserByEmail(new_email)"] --> Found{"是否找到用户?"}
Found -- 是且ID!=userID --> Err["返回错误: 邮箱已被使用"]
Found -- 否或ID==userID --> Update["UpdateUserFields(userID, {email: new_email})"]
Update --> Done["返回成功"]
```
图表来源
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [user_repository.go](file://internal/repository/user_repository.go#L45-L57)
- [user_repository.go](file://internal/repository/user_repository.go#L65-L69)
章节来源
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [user_repository.go](file://internal/repository/user_repository.go#L45-L57)
- [user_repository.go](file://internal/repository/user_repository.go#L65-L69)
### 数据模型与响应
- 请求体
- ChangeEmailRequest.new_email新邮箱地址必填且符合邮箱格式。
- ChangeEmailRequest.verification_code验证码必填且长度为6。
- 成功响应
- 返回通用响应结构,包含用户信息(含更新后的邮箱)。
- 错误响应
- 400请求参数错误、验证码错误/过期、邮箱已被使用。
- 401未授权。
- 404用户不存在。
- 500服务器内部错误。
章节来源
- [common.go](file://internal/types/common.go#L62-L66)
- [response.go](file://internal/model/response.go#L27-L53)
- [user_handler.go](file://internal/handler/user_handler.go#L328-L416)
## 依赖关系分析
- 控制器依赖
- 路由与JWT中间件保证接口受保护。
- 验证服务:负责验证码核验。
- 用户服务:负责邮箱更新与唯一性检查。
- 服务层依赖
- Redis客户端验证码存取与删除。
- 邮件服务:发送验证码邮件。
- 用户仓库:数据库读写。
- 数据库约束
- 用户表的邮箱字段具备唯一索引,配合服务层检查可避免并发冲突。
```mermaid
graph LR
Handler["用户处理器"] --> VerifySvc["验证服务"]
Handler --> UserSvc["用户服务"]
VerifySvc --> Redis["Redis客户端"]
VerifySvc --> Email["邮件服务"]
UserSvc --> Repo["用户仓库"]
Repo --> DB["数据库"]
```
图表来源
- [user_handler.go](file://internal/handler/user_handler.go#L328-L416)
- [verification_service.go](file://internal/service/verification_service.go#L79-L98)
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [user_repository.go](file://internal/repository/user_repository.go#L65-L69)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
- [email.go](file://pkg/email/email.go#L47-L55)
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L328-L416)
- [verification_service.go](file://internal/service/verification_service.go#L79-L98)
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [user_repository.go](file://internal/repository/user_repository.go#L65-L69)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
- [email.go](file://pkg/email/email.go#L47-L55)
## 性能与可靠性
- 验证码缓存
- Redis键过期时间10分钟避免长期占用内存rate_limit键1分钟限制发送频率降低滥用风险。
- 并发一致性
- 服务层在更新前进行邮箱唯一性检查,结合数据库唯一索引,有效防止并发写入导致的重复。
- 日志与可观测性
- 处理器与服务层在关键路径记录日志,便于定位问题。
- 错误传播
- 明确的错误码与消息,便于前端统一处理。
[本节为通用指导,不直接分析具体文件]
## 故障排查指南
- 401 未授权
- 检查请求头 Authorization 是否为 Bearer 令牌,且令牌有效。
- 400 请求参数错误
- 确认请求体包含 new_email 与 verification_code且格式正确。
- 400 验证码错误/过期
- 检查Redis中 verification:code:change_email:{email} 是否存在且未过期;确认邮件是否送达。
- 400 邮箱已被使用
- 确认新邮箱未被其他用户占用;若被占用请更换邮箱。
- 404 用户不存在
- 检查用户是否被软删除或账户状态异常。
- 500 服务器内部错误
- 查看服务日志关注数据库更新与Redis存取异常。
章节来源
- [user_handler.go](file://internal/handler/user_handler.go#L328-L416)
- [verification_service.go](file://internal/service/verification_service.go#L79-L98)
- [user_service.go](file://internal/service/user_service.go#L186-L201)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
- [response.go](file://internal/model/response.go#L27-L53)
## 结论
POST /api/v1/user/change-email 通过严格的JWT鉴权、Redis验证码校验与数据库唯一性检查实现了安全可靠的邮箱变更流程。请求体简洁明确错误处理清晰适合在生产环境中稳定运行。建议在部署时确保Redis与邮件服务可用并合理配置验证码有效期与发送频率限制以兼顾用户体验与安全。