Files
tyapi-server/docs/用户域实体优化总结.md
2025-07-11 21:05:58 +08:00

5.4 KiB
Raw Blame History

用户域实体优化总结

🎯 优化目标

将 User 实体从"贫血模型"升级为"充血模型",将业务逻辑从 Service 层迁移到实体层,实现更好的封装和职责分离。

完成的优化

1. 实体业务方法增强

密码管理方法

// 修改密码(包含完整的业务验证)
func (u *User) ChangePassword(oldPassword, newPassword, confirmPassword string) error

// 验证密码
func (u *User) CheckPassword(password string) bool

// 设置密码(用于注册或重置)
func (u *User) SetPassword(password string) error

手机号管理方法

// 验证手机号格式
func (u *User) IsValidPhone() bool

// 设置手机号(包含格式验证)
func (u *User) SetPhone(phone string) error

// 获取脱敏手机号
func (u *User) GetMaskedPhone() string

用户状态检查方法

// 检查用户是否可以登录
func (u *User) CanLogin() bool

// 检查用户是否活跃
func (u *User) IsActive() bool

// 检查用户是否已删除
func (u *User) IsDeleted() bool

2. 业务规则验证

密码强度验证

  • 长度要求8-128 位
  • 必须包含数字
  • 必须包含字母
  • 必须包含特殊字符

手机号格式验证

  • 11 位数字
  • 以 1 开头
  • 第二位为 3-9

业务不变性验证

  • 新密码不能与旧密码相同
  • 确认密码必须匹配
  • 用户状态检查

3. 工厂方法

// 创建新用户的工厂方法
func NewUser(phone, password string) (*User, error)

4. 静态工具方法

// 验证手机号格式(静态方法)
func IsValidPhoneFormat(phone string) bool

// 检查是否为验证错误
func IsValidationError(err error) bool

🔄 Service 层重构

优化前的问题

// 业务逻辑集中在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)
}

优化后的改进

// 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. 用户信息更新

func (s *UserService) UpdateUserProfile(ctx context.Context, userID string, req *dto.UpdateProfileRequest) (*entities.User, error)

2. 用户停用

func (s *UserService) DeactivateUser(ctx context.Context, userID string) error

3. 软删除支持

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