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

451 lines
18 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/model/user.go](file://internal/model/user.go)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go)
- [internal/service/user_service.go](file://internal/service/user_service.go)
- [pkg/auth/password.go](file://pkg/auth/password.go)
- [pkg/database/postgres.go](file://pkg/database/postgres.go)
- [internal/model/profile.go](file://internal/model/profile.go)
- [internal/model/system_config.go](file://internal/model/system_config.go)
- [internal/service/serialize_service.go](file://internal/service/serialize_service.go)
- [internal/service/user_service_test.go](file://internal/service/user_service_test.go)
- [internal/repository/user_repository_test.go](file://internal/repository/user_repository_test.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件聚焦于用户模型 User 的字段定义、数据类型、GORM 标签与业务含义,以及与 UserPointLog、UserLoginLog 的关联关系。同时提供用户状态流转图、积分系统使用模式、属性扩展Properties JSONB的实践建议并给出数据验证规则、唯一性约束与索引优化建议最后结合代码路径说明如何安全地处理密码存储与敏感信息脱敏。
## 项目结构
围绕用户模型的关键文件分布如下:
- 模型层:用户、积分日志、登录日志、档案等
- 仓储层:用户 CRUD、积分更新、登录日志创建等
- 服务层:注册、登录、修改密码、更换邮箱等业务流程
- 安全层:密码哈希与校验
- 数据库层PostgreSQL 连接与配置
```mermaid
graph TB
subgraph "模型层"
M_User["User<br/>internal/model/user.go"]
M_PointLog["UserPointLog<br/>internal/model/user.go"]
M_LoginLog["UserLoginLog<br/>internal/model/user.go"]
M_Profile["Profile<br/>internal/model/profile.go"]
end
subgraph "仓储层"
R_UserRepo["UserRepository<br/>internal/repository/user_repository.go"]
end
subgraph "服务层"
S_UserSvc["UserService<br/>internal/service/user_service.go"]
S_Serialize["SerializeService<br/>internal/service/serialize_service.go"]
end
subgraph "安全层"
A_Password["Password Hash/Check<br/>pkg/auth/password.go"]
end
subgraph "数据库层"
D_Postgres["PostgreSQL连接<br/>pkg/database/postgres.go"]
end
S_UserSvc --> R_UserRepo
R_UserRepo --> D_Postgres
S_UserSvc --> A_Password
S_Serialize --> M_User
M_User --> M_PointLog
M_User --> M_LoginLog
M_Profile --> M_User
```
图表来源
- [internal/model/user.go](file://internal/model/user.go#L1-L70)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [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/database/postgres.go](file://pkg/database/postgres.go#L1-L74)
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
- [internal/service/serialize_service.go](file://internal/service/serialize_service.go#L86-L97)
章节来源
- [internal/model/user.go](file://internal/model/user.go#L1-L70)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L1-L137)
- [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/database/postgres.go](file://pkg/database/postgres.go#L1-L74)
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
- [internal/service/serialize_service.go](file://internal/service/serialize_service.go#L86-L97)
## 核心组件
本节对 User 结构体及其关键字段进行逐项解析涵盖字段类型、GORM 标签、默认值、业务语义与约束。
- 字段与类型
- ID整型自增主键用于唯一标识用户
- Username字符串长度限制与唯一索引约束
- Password字符串存储哈希后的密码JSON 中不返回
- Email字符串长度限制与唯一索引约束
- Avatar字符串头像 URL默认空串
- Points整型积分余额默认 0
- Role字符串角色默认 "user"
- Status短整型状态枚举默认 1正常0禁用-1删除
- Properties字符串映射 PostgreSQL 的 JSONB 类型,用于扩展属性
- LastLoginAt时间戳最近登录时间
- CreatedAt/UpdatedAt时间戳默认 CURRENT_TIMESTAMP
- 约束与索引
- Username、Email 唯一索引
- UserPointLog、UserLoginLog 的 UserID、IP 地址、is_success 等字段建立索引
- Properties 以 JSONB 存储,便于灵活扩展
- 业务含义
- 注册时默认 Role 为 "user"Status 为 1正常Points 为 0
- 登录成功后更新 LastLoginAt
- 删除采用软删除,将 Status 设为 -1
章节来源
- [internal/model/user.go](file://internal/model/user.go#L1-L26)
- [internal/model/user.go](file://internal/model/user.go#L28-L70)
- [internal/service/user_service.go](file://internal/service/user_service.go#L12-L67)
- [internal/service/user_service.go](file://internal/service/user_service.go#L111-L121)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L71-L75)
## 架构总览
用户模型在系统中的交互流程如下:
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Handler as "处理器/控制器"
participant Service as "UserService"
participant Repo as "UserRepository"
participant DB as "数据库(PostgreSQL)"
participant Auth as "Password(哈希/校验)"
Client->>Handler : "注册/登录请求"
Handler->>Service : "调用业务方法"
Service->>Auth : "注册时哈希密码"
Auth-->>Service : "返回哈希值"
Service->>Repo : "创建用户/保存用户"
Repo->>DB : "执行SQL"
DB-->>Repo : "返回结果"
Repo-->>Service : "返回用户对象"
Service-->>Handler : "返回用户与令牌"
Handler-->>Client : "响应(不含敏感字段)"
```
图表来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L12-L67)
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L11-L15)
- [pkg/database/postgres.go](file://pkg/database/postgres.go#L1-L74)
## 详细组件分析
### User 结构体与字段详解
- ID
- 类型:整型自增主键
- 用途:全局唯一标识
- GORM 标签:主键、自增
- Username
- 类型:字符串,长度限制,唯一索引
- 用途:登录凭据之一
- 约束:唯一性
- Password
- 类型:字符串,存储 bcrypt 哈希
- 用途:登录凭据
- 安全JSON 中不返回
- Email
- 类型:字符串,长度限制,唯一索引
- 用途:找回密码、通知等
- 约束:唯一性
- Avatar
- 类型字符串URL
- 默认:空串
- Points
- 类型:整型
- 默认0
- 用途:积分系统余额
- Role
- 类型:字符串
- 默认:"user"
- Status
- 类型:短整型
- 默认1正常
- 取值1 正常0 禁用;-1 删除(软删)
- Properties
- 类型:字符串,映射 PostgreSQL 的 JSONB
- 用途:扩展属性,如权限、配置等
- LastLoginAt
- 类型:时间戳
- 用途:记录最近登录时间
- CreatedAt/UpdatedAt
- 类型:时间戳,默认 CURRENT_TIMESTAMP
章节来源
- [internal/model/user.go](file://internal/model/user.go#L1-L26)
### User 与 UserPointLog 的关联
- 外键关系
- UserPointLog.UserID 引用 User.ID
- UserPointLog.OperatorID 可选,指向操作者用户
- 级联行为
- 代码中未显式声明级联删除/更新,遵循 GORM 默认行为
- 由于 User 采用软删除Status=-1建议在业务层避免直接删除用户防止破坏积分日志的完整性
- 日志字段
- ChangeType变更类型如 EARN、SPEND、ADMIN_ADJUST
- Amount/BalanceBefore/BalanceAfter金额与前后余额
- Reason/ReferenceType/ReferenceID原因与关联对象类型/ID
- OperatorID可选记录管理员操作者
```mermaid
classDiagram
class User {
+int64 ID
+string Username
+string Email
+string Avatar
+int Points
+string Role
+int16 Status
+string Properties
+time.Time LastLoginAt
+time.Time CreatedAt
+time.Time UpdatedAt
}
class UserPointLog {
+int64 ID
+int64 UserID
+string ChangeType
+int Amount
+int BalanceBefore
+int BalanceAfter
+string Reason
+string ReferenceType
+*int64 ReferenceID
+*int64 OperatorID
+time.Time CreatedAt
}
class UserLoginLog {
+int64 ID
+int64 UserID
+string IPAddress
+string UserAgent
+string LoginMethod
+bool IsSuccess
+string FailureReason
+time.Time CreatedAt
}
User "1" <-- "many" UserPointLog : "外键UserID"
User "1" <-- "many" UserLoginLog : "外键UserID"
User "1" <-- "many" UserPointLog : "外键OperatorID(可选)"
```
图表来源
- [internal/model/user.go](file://internal/model/user.go#L28-L70)
章节来源
- [internal/model/user.go](file://internal/model/user.go#L28-L70)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L89-L124)
### User 与 UserLoginLog 的关联
- 外键关系
- UserLoginLog.UserID 引用 User.ID
- 日志字段
- IPAddressinet 类型,记录登录 IP
- UserAgent文本
- LoginMethod默认 PASSWORD
- IsSuccess/FailureReason登录成功与否及原因
- 索引
- UserID、IPAddress、IsSuccess 建有索引CreatedAt 建有复合索引并按降序排序
章节来源
- [internal/model/user.go](file://internal/model/user.go#L52-L70)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L77-L87)
### 用户状态流转图
- 正常1可登录、可消费积分
- 禁用0禁止登录不影响积分
- 删除(-1软删除查询时默认过滤该状态
```mermaid
stateDiagram-v2
[*] --> 正常
正常 --> 禁用 : "管理员操作"
正常 --> 删除 : "软删除"
禁用 --> 正常 : "恢复"
禁用 --> 删除 : "软删除"
删除 --> 正常 : "不可恢复"
删除 --> 禁用 : "不可恢复"
```
图表来源
- [internal/model/user.go](file://internal/model/user.go#L14-L16)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L71-L75)
### 积分系统Points使用模式
- 初始化:注册时 Points 默认 0
- 更新:通过 UpdateUserPoints 在事务中完成,先读取当前余额,计算新余额,再写入并创建日志
- 校验:若新余额小于 0则回滚并返回错误
- 日志:记录变更类型、金额、前后余额、原因、关联对象与操作者
```mermaid
flowchart TD
Start(["开始"]) --> ReadUser["读取用户当前积分"]
ReadUser --> Calc["计算新余额 = 当前 + 变更金额"]
Calc --> Check{"新余额 >= 0 ?"}
Check --> |否| Rollback["回滚事务并返回错误"]
Check --> |是| Update["更新用户积分"]
Update --> Log["创建积分日志"]
Log --> End(["结束"])
Rollback --> End
```
图表来源
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L89-L124)
章节来源
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L89-L124)
- [internal/service/user_service.go](file://internal/service/user_service.go#L12-L67)
### 属性扩展Properties JSONB使用模式
- 存储Properties 为字符串,映射 PostgreSQL 的 JSONB 类型
- 用途:存放扩展字段,如权限、配置等
- 序列化:在序列化时可直接返回 Properties但需注意敏感信息脱敏
- 示例路径:序列化用户时将 Properties 原样返回,供上层处理
章节来源
- [internal/model/user.go](file://internal/model/user.go#L16-L17)
- [internal/service/serialize_service.go](file://internal/service/serialize_service.go#L86-L97)
### 数据验证规则、唯一性约束与索引优化建议
- 验证规则
- 用户名非空,长度范围(建议在服务层补充)
- 邮箱非空且包含 @ 符号(建议在服务层补充)
- 密码非空(建议在服务层补充)
- 唯一性约束
- Username、Email 唯一索引
- 索引优化建议
- UserUsername、Email 唯一索引(已具备)
- UserPointLogUserID、CreatedAt(降序)、ReferenceType/ReferenceID可选
- UserLoginLogUserID、IPAddress、IsSuccess、CreatedAt(降序)
- 状态查询
- 查询用户时默认排除 Status=-1软删除
章节来源
- [internal/model/user.go](file://internal/model/user.go#L9-L21)
- [internal/model/user.go](file://internal/model/user.go#L31-L41)
- [internal/model/user.go](file://internal/model/user.go#L55-L61)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L18-L29)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L31-L43)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L45-L57)
- [internal/service/user_service_test.go](file://internal/service/user_service_test.go#L109-L164)
- [internal/repository/user_repository_test.go](file://internal/repository/user_repository_test.go#L1-L69)
### 密码存储与敏感信息脱敏
- 密码存储
- 注册时使用 bcrypt 哈希存储
- 登录时使用 bcrypt 校验
- User.Password 在 JSON 响应中不返回
- 敏感信息脱敏
- Password 字段在 JSON 中标记为不输出
- Properties 作为 JSONB 返回,需在上层进行脱敏处理(例如移除敏感键或替换为占位符)
- 头像 URL、邮箱等字段在返回前可根据需要进行脱敏策略
章节来源
- [pkg/auth/password.go](file://pkg/auth/password.go#L1-L21)
- [internal/service/user_service.go](file://internal/service/user_service.go#L12-L67)
- [internal/service/user_service.go](file://internal/service/user_service.go#L141-L164)
- [internal/model/user.go](file://internal/model/user.go#L10-L12)
- [internal/service/serialize_service.go](file://internal/service/serialize_service.go#L86-L97)
## 依赖分析
- 组件耦合
- UserService 依赖 UserRepository、JWT 服务与 Password 工具
- UserRepository 依赖数据库连接
- User 模型与 UserPointLog、UserLoginLog 通过外键关联
- 外键与级联
- 未显式声明级联,遵循 GORM 默认行为
- 建议在业务层避免直接物理删除用户,以保持积分与登录日志的完整性
- 外部依赖
- PostgreSQL 驱动与连接池配置
- bcrypt 密码哈希
```mermaid
graph LR
S_UserSvc["UserService"] --> R_UserRepo["UserRepository"]
S_UserSvc --> A_Password["Password"]
R_UserRepo --> D_Postgres["PostgreSQL"]
M_User["User"] --> M_PointLog["UserPointLog"]
M_User --> M_LoginLog["UserLoginLog"]
```
图表来源
- [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/database/postgres.go](file://pkg/database/postgres.go#L1-L74)
- [internal/model/user.go](file://internal/model/user.go#L1-L70)
章节来源
- [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/database/postgres.go](file://pkg/database/postgres.go#L1-L74)
- [internal/model/user.go](file://internal/model/user.go#L1-L70)
## 性能考虑
- 索引设计
- UserUsername、Email 唯一索引
- UserPointLogUserID、CreatedAt(降序)、ReferenceType/ReferenceID可选
- UserLoginLogUserID、IPAddress、IsSuccess、CreatedAt(降序)
- 查询过滤
- 查询用户时默认排除软删除Status!=-1减少扫描
- 事务与并发
- 积分更新使用事务,保证一致性
- 缓存与限流
- 登录失败与成功日志可配合缓存与限流策略,降低暴力破解风险
[本节为通用指导,无需列出具体文件来源]
## 故障排查指南
- 登录失败
- 检查用户是否存在与状态是否为 1
- 校验密码哈希是否正确
- 记录失败日志以便审计
- 积分不足
- 确认余额计算逻辑与事务一致性
- 检查日志是否正确记录
- 软删除影响
- 查询用户时默认排除 Status=-1
- 若出现“用户不存在”,确认是否被软删除
章节来源
- [internal/service/user_service.go](file://internal/service/user_service.go#L70-L121)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L71-L75)
- [internal/repository/user_repository.go](file://internal/repository/user_repository.go#L89-L124)
- [internal/repository/user_repository_test.go](file://internal/repository/user_repository_test.go#L1-L69)
## 结论
User 模型通过明确的字段定义、GORM 标签与业务约束,支撑了注册、登录、积分与日志等核心功能。通过软删除与事务化的积分更新,确保了数据一致性与可审计性。建议在服务层补充输入验证与脱敏策略,并根据业务增长持续优化索引与查询路径。
[本节为总结性内容,无需列出具体文件来源]
## 附录
- 相关模型与配置
- Profile 模型与 User 的关联
- SystemConfig 模型(系统配置)
- 序列化与脱敏
- SerializeUser 返回 Properties需在上层进行脱敏处理
章节来源
- [internal/model/profile.go](file://internal/model/profile.go#L1-L64)
- [internal/model/system_config.go](file://internal/model/system_config.go#L1-L42)
- [internal/service/serialize_service.go](file://internal/service/serialize_service.go#L86-L97)