chore(git): 更新.gitignore以忽略新的本地文件
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
This commit is contained in:
451
.qoder/repowiki/zh/content/数据模型/用户模型.md
Normal file
451
.qoder/repowiki/zh/content/数据模型/用户模型.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# 用户模型
|
||||
|
||||
<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
|
||||
- 日志字段
|
||||
- IPAddress:inet 类型,记录登录 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 唯一索引
|
||||
- 索引优化建议
|
||||
- User:Username、Email 唯一索引(已具备)
|
||||
- UserPointLog:UserID、CreatedAt(降序)、ReferenceType/ReferenceID(可选)
|
||||
- UserLoginLog:UserID、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)
|
||||
|
||||
## 性能考虑
|
||||
- 索引设计
|
||||
- User:Username、Email 唯一索引
|
||||
- UserPointLog:UserID、CreatedAt(降序)、ReferenceType/ReferenceID(可选)
|
||||
- UserLoginLog:UserID、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)
|
||||
Reference in New Issue
Block a user