Files
backend/.qoder/repowiki/zh/content/服务架构/用户服务.md
lan a4b6c5011e
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
chore(git): 更新.gitignore以忽略新的本地文件
2025-11-30 08:33:17 +08:00

372 lines
16 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>
**本文引用的文件列表**
- [internal/service/user_service.go](file://internal/service/user_service.go)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go)
- [internal/model/user.go](file://internal/model/user.go)
- [internal/handler/auth_handler.go](file://internal/handler/auth_handler.go)
- [pkg/auth/password.go](file://pkg/auth/password.go)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go)
- [internal/types/common.go](file://internal/types/common.go)
- [internal/service/common.go](file://internal/service/common.go)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go)
- [internal/model/system_config.go](file://internal/model/system_config.go)
- [internal/service/user_service_test.go](file://internal/service/user_service_test.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件聚焦于用户服务UserService的职责、方法与内部逻辑覆盖用户注册、登录、信息更新等核心功能并对默认角色、状态与积分的初始化进行说明同时解释头像选择策略优先自定义头像否则使用默认头像以及数据验证规则用户名、邮箱、密码非空且邮箱格式正确。文档还提供服务层与仓储层的调用关系示例帮助初学者理解业务流程并为有经验的开发者提供扩展点建议如修改默认角色、添加新的用户属性
## 项目结构
用户服务位于 internal/service 层,围绕 internal/model 定义的数据模型工作,通过 internal/repository 与数据库交互,对外由 internal/handler 提供 HTTP 接口。认证相关的密码处理与 JWT 令牌生成分别由 pkg/auth 下的 password 与 jwt 组件完成。
```mermaid
graph TB
subgraph "接口层"
H["auth_handler<br/>HTTP接口"]
end
subgraph "服务层"
S["user_service<br/>用户业务逻辑"]
end
subgraph "模型层"
M["user.go<br/>User/UserLoginLog/UserPointLog"]
SCM["system_config.go<br/>SystemConfig"]
end
subgraph "仓储层"
R["user_repository<br/>CRUD/事务"]
SCR["system_config_repository<br/>系统配置查询"]
end
subgraph "认证与工具"
P["password.go<br/>密码加解密"]
J["jwt.go<br/>JWT签发/校验"]
T["types/common.go<br/>请求/响应结构"]
end
H --> S
S --> R
S --> SCR
S --> P
S --> J
S --> M
S --> SCM
T --> H
```
图表来源
- [internal/handler/auth_handler.go](file://internal/handler/auth_handler.go#L1-L250)
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
- [internal/model/system_config.go](file://internal/model/system_config.go#L1-L42)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go#L1-L71)
- [internal/types/common.go](file://internal/types/common.go#L1-L215)
章节来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
- [internal/handler/auth_handler.go](file://internal/handler/auth_handler.go#L1-L250)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go#L1-L71)
- [internal/types/common.go](file://internal/types/common.go#L1-L215)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
- [internal/model/system_config.go](file://internal/model/system_config.go#L1-L42)
## 核心组件
- 用户模型User包含用户名、邮箱、头像、积分、角色、状态、最后登录时间等字段以及唯一索引约束与默认值。
- 用户登录日志UserLoginLog记录登录尝试的IP、UA、是否成功及失败原因。
- 用户积分日志UserPointLog记录积分变动明细类型、金额、前后余额、原因等
- 系统配置SystemConfig键值型配置用于存储默认头像等全局设置。
- 服务层UserService封装注册、登录、信息更新、头像更新、密码修改/重置、邮箱变更等业务逻辑。
- 仓储层UserRepository提供用户与登录/积分日志的数据库操作,包含事务性积分更新。
- 认证工具Password/JWT密码加密与校验、JWT签发与校验。
- 接口层AuthHandler接收HTTP请求绑定参数调用服务层并返回统一响应。
章节来源
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
- [internal/model/system_config.go](file://internal/model/system_config.go#L1-L42)
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go#L1-L71)
- [internal/handler/auth_handler.go](file://internal/handler/auth_handler.go#L1-L250)
## 架构总览
下图展示从HTTP请求到服务层再到仓储层的整体调用链路以及关键对象之间的关系。
```mermaid
sequenceDiagram
participant C as "客户端"
participant H as "AuthHandler"
participant S as "UserService"
participant P as "Password"
participant J as "JWTService"
participant R as "UserRepository"
participant SCR as "SystemConfigRepository"
C->>H : "POST /api/v1/auth/register"
H->>H : "绑定RegisterRequest并校验"
H->>S : "RegisterUser(jwtService, username, password, email, avatar)"
S->>R : "FindUserByUsername(username)"
S->>R : "FindUserByEmail(email)"
S->>P : "HashPassword(password)"
S->>SCR : "GetSystemConfigByKey('default_avatar')"
S->>R : "CreateUser(user)"
S->>J : "GenerateToken(user.id, user.username, user.role)"
S-->>H : "返回user, token"
H-->>C : "返回LoginResponse"
C->>H : "POST /api/v1/auth/login"
H->>S : "LoginUser(jwtService, usernameOrEmail, password, ip, ua)"
S->>R : "按用户名或邮箱查找用户"
S->>S : "检查用户状态"
S->>P : "CheckPassword(user.password, password)"
S->>R : "UpdateUserFields(last_login_at)"
S->>J : "GenerateToken(...)"
S-->>H : "返回user, token"
H-->>C : "返回LoginResponse"
```
图表来源
- [internal/handler/auth_handler.go](file://internal/handler/auth_handler.go#L1-L250)
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go#L1-L71)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
## 详细组件分析
### 用户服务UserService职责与方法
- 注册RegisterUser
- 校验用户名与邮箱唯一性
- 密码加密
- 头像选择:若传入自定义头像则使用,否则从系统配置读取默认头像
- 初始化角色为“user”状态为1正常积分初始为0
- 创建用户并生成JWT
- 登录LoginUser
- 支持用户名或邮箱登录(通过是否包含“@”判断)
- 校验用户状态仅允许状态为1的用户登录
- 验证密码
- 生成JWT并更新最后登录时间
- 记录登录日志(成功/失败)
- 查询与更新
- 按ID获取用户
- 更新用户信息(完整保存)
- 更新头像(字段更新)
- 修改密码(校验旧密码后加密新密码并更新)
- 重置密码(通过邮箱查找用户并更新密码)
- 更换邮箱(校验新邮箱唯一性后更新)
章节来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
### 数据模型与默认值
- 用户模型字段与默认值
- 角色默认“user”
- 状态默认1正常
- 积分默认0
- 头像:默认空字符串
- 时间戳createdAt/updatedAt默认当前时间
- 登录日志与积分日志
- 登录日志包含IP、UA、登录方式、是否成功、失败原因
- 积分日志包含变更类型、金额、前后余额、原因等
章节来源
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
### 头像逻辑(默认头像策略)
- 优先使用用户提供的头像URL
- 若未提供则从系统配置中读取键为“default_avatar”的值作为默认头像
- 若系统配置不存在,则返回提示信息字符串
```mermaid
flowchart TD
Start(["开始"]) --> CheckProvided["是否提供了头像URL"]
CheckProvided --> |是| UseProvided["使用提供的头像URL"]
CheckProvided --> |否| LoadConfig["从系统配置读取default_avatar"]
LoadConfig --> Found{"配置是否存在?"}
Found --> |是| UseDefault["使用系统配置中的默认头像URL"]
Found --> |否| UseError["返回默认头像配置缺失提示"]
UseProvided --> End(["结束"])
UseDefault --> End
UseError --> End
```
图表来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L228-L240)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L23)
- [internal/model/system_config.go](file://internal/model/system_config.go#L1-L42)
章节来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L228-L240)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L23)
- [internal/model/system_config.go](file://internal/model/system_config.go#L1-L42)
### 数据验证规则
- 请求体绑定规则(由接口层负责)
- 注册用户名必填且长度在3-50之间邮箱必填且符合邮箱格式密码必填且长度6-128验证码必填且长度6头像可选且需为合法URL
- 登录用户名必填支持用户名或邮箱密码必填且长度6-128
- 更新用户头像可选且为合法URL修改密码时旧密码与新密码长度要求
- 服务层内部验证
- 注册时对用户名、邮箱、密码进行基本非空与邮箱格式校验
- 登录时对状态进行检查,密码进行校验
- 更换邮箱时确保新邮箱未被他人使用
章节来源
- [internal/types/common.go](file://internal/types/common.go#L1-L215)
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/service/user_service_test.go](file://internal/service/user_service_test.go#L1-L200)
### 服务层与仓储层调用关系示例
- 注册流程
- Handler -> UserService.RegisterUser -> UserRepository.FindUserByUsername/ByEmail -> Password.HashPassword -> SystemConfigRepository.GetSystemConfigByKey -> UserRepository.CreateUser -> JWTService.GenerateToken
- 登录流程
- Handler -> UserService.LoginUser -> UserRepository.FindUserByUsername/ByEmail -> Password.CheckPassword -> UserRepository.UpdateUserFields -> JWTService.GenerateToken
```mermaid
classDiagram
class AuthHandler {
+Register()
+Login()
}
class UserService {
+RegisterUser(...)
+LoginUser(...)
+GetUserByID(...)
+UpdateUserInfo(...)
+UpdateUserAvatar(...)
+ChangeUserPassword(...)
+ResetUserPassword(...)
+ChangeUserEmail(...)
}
class UserRepository {
+CreateUser(...)
+FindUserByID(...)
+FindUserByUsername(...)
+FindUserByEmail(...)
+UpdateUser(...)
+UpdateUserFields(...)
+CreateLoginLog(...)
+UpdateUserPoints(...)
}
class SystemConfigRepository {
+GetSystemConfigByKey(...)
}
class Password {
+HashPassword(...)
+CheckPassword(...)
}
class JWTService {
+GenerateToken(...)
+ValidateToken(...)
}
AuthHandler --> UserService : "调用"
UserService --> UserRepository : "CRUD/事务"
UserService --> SystemConfigRepository : "读取默认头像"
UserService --> Password : "密码处理"
UserService --> JWTService : "签发Token"
```
图表来源
- [internal/handler/auth_handler.go](file://internal/handler/auth_handler.go#L1-L250)
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go#L1-L71)
## 依赖分析
- 内部依赖
- UserService 依赖 UserRepository、SystemConfigRepository、Password、JWTService、User模型、SystemConfig模型
- AuthHandler 依赖 UserService、Types请求/响应、Logger、Redis验证码、Email发送验证码
- 外部依赖
- bcrypt 用于密码加密与校验
- golang-jwt/jwt/v5 用于JWT签发与校验
- GORM 用于数据库访问与事务
```mermaid
graph LR
H["AuthHandler"] --> S["UserService"]
S --> R["UserRepository"]
S --> SCR["SystemConfigRepository"]
S --> P["Password(bcrypt)"]
S --> J["JWTService(gojwt)"]
S --> M["User/SystemConfig Models"]
R --> DB["GORM/DB"]
SCR --> DB
```
图表来源
- [internal/handler/auth_handler.go](file://internal/handler/auth_handler.go#L1-L250)
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [internal/repository/system_config_repository.go](file://internal/repository/system_config_repository.go#L1-L58)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go#L1-L71)
章节来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [pkg/auth/jwt.go](file://pkg/auth/jwt.go#L1-L71)
## 性能考虑
- 密码加密成本bcrypt 默认成本较高,建议在高并发场景下关注注册/登录的延迟,必要时评估成本参数与异步化策略
- 数据库索引:用户名与邮箱字段具备唯一索引,有助于快速去重;登录日志与积分日志按时间建立索引,有利于查询与统计
- 事务一致性:积分变更采用事务,避免并发写入导致的余额不一致
- 缓存策略:默认头像配置可考虑缓存以减少数据库查询次数
## 故障排查指南
- 注册失败
- 检查用户名/邮箱是否重复
- 确认密码加密是否成功
- 检查系统配置中是否存在“default_avatar”
- 登录失败
- 用户不存在或状态非1
- 密码校验失败
- 检查IP与User-Agent是否正确传入
- 头像显示异常
- 自定义头像URL是否有效
- 系统默认头像配置是否正确
- 集成测试参考
- 单元测试覆盖了默认头像逻辑、邮箱检测、常量与验证逻辑,可据此定位问题
章节来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/service/user_service_test.go](file://internal/service/user_service_test.go#L1-L200)
## 结论
UserService 以清晰的职责划分实现了用户生命周期管理:从注册、登录到信息维护与安全控制。通过明确的默认值与验证规则,结合仓储层的事务与模型层的结构化设计,系统在保证安全性的同时具备良好的可扩展性。对于扩展需求,可在服务层增加新的默认值或属性映射,并在仓储层补充相应的查询/更新逻辑。
## 附录
### 扩展点指导
- 修改默认角色
- 在注册流程中调整角色初始化值
- 若需动态分配角色,可引入系统配置或权限策略模块
- 添加新的用户属性
- 在 User 模型中新增字段并设置默认值
- 在注册流程中初始化该字段
- 在接口层的请求/响应结构中同步新增字段
- 默认积分初始化
- 当前初始积分为0可通过系统配置读取或硬编码调整
- 注册奖励积分可作为后续增强点服务层已有TODO标记
章节来源
- [internal/model/user.go](file://internal/model/user.go#L1-L71)
- [internal/service/user_service.go](file://internal/service/user_service.go#L1-L249)
- [internal/types/common.go](file://internal/types/common.go#L1-L215)
- [internal/service/common.go](file://internal/service/common.go#L1-L14)