temp
This commit is contained in:
221
docs/用户域实体优化总结.md
Normal file
221
docs/用户域实体优化总结.md
Normal file
@@ -0,0 +1,221 @@
|
||||
# 用户域实体优化总结
|
||||
|
||||
## 🎯 **优化目标**
|
||||
|
||||
将 User 实体从"贫血模型"升级为"充血模型",将业务逻辑从 Service 层迁移到实体层,实现更好的封装和职责分离。
|
||||
|
||||
## ✅ **完成的优化**
|
||||
|
||||
### **1. 实体业务方法增强**
|
||||
|
||||
#### **密码管理方法**
|
||||
|
||||
```go
|
||||
// 修改密码(包含完整的业务验证)
|
||||
func (u *User) ChangePassword(oldPassword, newPassword, confirmPassword string) error
|
||||
|
||||
// 验证密码
|
||||
func (u *User) CheckPassword(password string) bool
|
||||
|
||||
// 设置密码(用于注册或重置)
|
||||
func (u *User) SetPassword(password string) error
|
||||
```
|
||||
|
||||
#### **手机号管理方法**
|
||||
|
||||
```go
|
||||
// 验证手机号格式
|
||||
func (u *User) IsValidPhone() bool
|
||||
|
||||
// 设置手机号(包含格式验证)
|
||||
func (u *User) SetPhone(phone string) error
|
||||
|
||||
// 获取脱敏手机号
|
||||
func (u *User) GetMaskedPhone() string
|
||||
```
|
||||
|
||||
#### **用户状态检查方法**
|
||||
|
||||
```go
|
||||
// 检查用户是否可以登录
|
||||
func (u *User) CanLogin() bool
|
||||
|
||||
// 检查用户是否活跃
|
||||
func (u *User) IsActive() bool
|
||||
|
||||
// 检查用户是否已删除
|
||||
func (u *User) IsDeleted() bool
|
||||
```
|
||||
|
||||
### **2. 业务规则验证**
|
||||
|
||||
#### **密码强度验证**
|
||||
|
||||
- 长度要求:8-128 位
|
||||
- 必须包含数字
|
||||
- 必须包含字母
|
||||
- 必须包含特殊字符
|
||||
|
||||
#### **手机号格式验证**
|
||||
|
||||
- 11 位数字
|
||||
- 以 1 开头
|
||||
- 第二位为 3-9
|
||||
|
||||
#### **业务不变性验证**
|
||||
|
||||
- 新密码不能与旧密码相同
|
||||
- 确认密码必须匹配
|
||||
- 用户状态检查
|
||||
|
||||
### **3. 工厂方法**
|
||||
|
||||
```go
|
||||
// 创建新用户的工厂方法
|
||||
func NewUser(phone, password string) (*User, error)
|
||||
```
|
||||
|
||||
### **4. 静态工具方法**
|
||||
|
||||
```go
|
||||
// 验证手机号格式(静态方法)
|
||||
func IsValidPhoneFormat(phone string) bool
|
||||
|
||||
// 检查是否为验证错误
|
||||
func IsValidationError(err error) bool
|
||||
```
|
||||
|
||||
## 🔄 **Service 层重构**
|
||||
|
||||
### **优化前的问题**
|
||||
|
||||
```go
|
||||
// 业务逻辑集中在Service中
|
||||
func (s *UserService) ChangePassword(ctx context.Context, userID string, req *dto.ChangePasswordRequest) error {
|
||||
// 验证新密码确认
|
||||
if req.NewPassword != req.ConfirmNewPassword { ... }
|
||||
|
||||
// 验证当前密码
|
||||
if !s.checkPassword(req.OldPassword, user.Password) { ... }
|
||||
|
||||
// 哈希新密码
|
||||
hashedPassword, err := s.hashPassword(req.NewPassword)
|
||||
|
||||
// 更新密码
|
||||
user.Password = hashedPassword
|
||||
return s.repo.Update(ctx, user)
|
||||
}
|
||||
```
|
||||
|
||||
### **优化后的改进**
|
||||
|
||||
```go
|
||||
// Service只负责协调,业务逻辑委托给实体
|
||||
func (s *UserService) ChangePassword(ctx context.Context, userID string, req *dto.ChangePasswordRequest) error {
|
||||
// 1. 获取用户信息
|
||||
user, err := s.repo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
|
||||
// 2. 执行业务逻辑(委托给实体)
|
||||
if err := user.ChangePassword(req.OldPassword, req.NewPassword, req.ConfirmNewPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 保存用户
|
||||
return s.repo.Update(ctx, user)
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 **优化效果对比**
|
||||
|
||||
| 方面 | 优化前 | 优化后 |
|
||||
| ---------------- | -------------- | -------------- |
|
||||
| **业务逻辑位置** | Service 层 | 实体层 |
|
||||
| **代码复用性** | 低 | 高 |
|
||||
| **测试难度** | 需要 Mock 仓储 | 可直接测试实体 |
|
||||
| **职责分离** | 不清晰 | 清晰 |
|
||||
| **维护性** | 一般 | 优秀 |
|
||||
|
||||
## 🧪 **测试覆盖**
|
||||
|
||||
### **单元测试**
|
||||
|
||||
- ✅ 密码修改功能测试
|
||||
- ✅ 密码验证功能测试
|
||||
- ✅ 手机号设置功能测试
|
||||
- ✅ 手机号脱敏功能测试
|
||||
- ✅ 手机号格式验证测试
|
||||
- ✅ 用户创建工厂方法测试
|
||||
|
||||
### **测试结果**
|
||||
|
||||
```
|
||||
=== RUN TestUser_ChangePassword
|
||||
--- PASS: TestUser_ChangePassword (0.57s)
|
||||
=== RUN TestUser_CheckPassword
|
||||
--- PASS: TestUser_CheckPassword (0.16s)
|
||||
=== RUN TestUser_SetPhone
|
||||
--- PASS: TestUser_SetPhone (0.00s)
|
||||
=== RUN TestUser_GetMaskedPhone
|
||||
--- PASS: TestUser_GetMaskedPhone (0.00s)
|
||||
=== RUN TestIsValidPhoneFormat
|
||||
--- PASS: TestIsValidPhoneFormat (0.00s)
|
||||
=== RUN TestNewUser
|
||||
--- PASS: TestNewUser (0.08s)
|
||||
PASS
|
||||
```
|
||||
|
||||
## 🚀 **新增功能**
|
||||
|
||||
### **1. 用户信息更新**
|
||||
|
||||
```go
|
||||
func (s *UserService) UpdateUserProfile(ctx context.Context, userID string, req *dto.UpdateProfileRequest) (*entities.User, error)
|
||||
```
|
||||
|
||||
### **2. 用户停用**
|
||||
|
||||
```go
|
||||
func (s *UserService) DeactivateUser(ctx context.Context, userID string) error
|
||||
```
|
||||
|
||||
### **3. 软删除支持**
|
||||
|
||||
```go
|
||||
func (r *UserRepository) SoftDelete(ctx context.Context, id string) error
|
||||
func (r *UserRepository) Restore(ctx context.Context, id string) error
|
||||
```
|
||||
|
||||
## 📈 **架构改进**
|
||||
|
||||
### **1. 更好的封装**
|
||||
|
||||
- 业务规则与数据在一起
|
||||
- 减少外部依赖
|
||||
- 提高内聚性
|
||||
|
||||
### **2. 更清晰的职责**
|
||||
|
||||
- 实体:业务逻辑和验证
|
||||
- Service:协调和事务管理
|
||||
- Repository:数据访问
|
||||
|
||||
### **3. 更容易测试**
|
||||
|
||||
- 实体方法可以独立测试
|
||||
- 不需要复杂的 Mock 设置
|
||||
- 测试覆盖更全面
|
||||
|
||||
## 🎉 **总结**
|
||||
|
||||
这次优化成功实现了:
|
||||
|
||||
1. **✅ 充血模型** - 实体包含丰富的业务方法
|
||||
2. **✅ 职责分离** - Service 专注于协调,实体专注于业务逻辑
|
||||
3. **✅ 更好的封装** - 业务规则与数据紧密耦合
|
||||
4. **✅ 更容易测试** - 实体方法可以独立测试
|
||||
5. **✅ 代码复用** - 业务逻辑可以在不同场景下复用
|
||||
|
||||
这是一个成功的"轻量级 DDD"实践,在保持架构简单的同时,显著提升了代码质量和可维护性!
|
||||
Reference in New Issue
Block a user