451 lines
18 KiB
Markdown
451 lines
18 KiB
Markdown
# 用户模型
|
||
|
||
<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) |