This commit is contained in:
2025-07-11 21:05:58 +08:00
parent 5b4392894f
commit e3d64e7485
74 changed files with 14379 additions and 697 deletions

View 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"实践,在保持架构简单的同时,显著提升了代码质量和可维护性!