Files
tyapi-server/internal/domains/user/services/user_aggregate_service.go
2025-07-30 00:51:22 +08:00

569 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package services
import (
"context"
"fmt"
"go.uber.org/zap"
"tyapi-server/internal/domains/user/entities"
"tyapi-server/internal/domains/user/repositories"
"tyapi-server/internal/domains/user/repositories/queries"
"tyapi-server/internal/shared/interfaces"
)
// UserAggregateService 用户聚合服务接口
// 负责用户聚合根的生命周期管理和业务规则验证
type UserAggregateService interface {
// 聚合根管理
CreateUser(ctx context.Context, phone, password string) (*entities.User, error)
LoadUser(ctx context.Context, userID string) (*entities.User, error)
SaveUser(ctx context.Context, user *entities.User) error
LoadUserByPhone(ctx context.Context, phone string) (*entities.User, error)
// 业务规则验证
ValidateBusinessRules(ctx context.Context, user *entities.User) error
CheckInvariance(ctx context.Context, user *entities.User) error
// 查询方法
ExistsByPhone(ctx context.Context, phone string) (bool, error)
ExistsByID(ctx context.Context, userID string) (bool, error)
// 用户管理方法
GetUserByID(ctx context.Context, userID string) (*entities.User, error)
UpdateLoginStats(ctx context.Context, userID string) error
ListUsers(ctx context.Context, query *queries.ListUsersQuery) ([]*entities.User, int64, error)
GetUserStats(ctx context.Context) (*repositories.UserStats, error)
// 企业信息管理
CreateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error
UpdateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error
GetUserWithEnterpriseInfo(ctx context.Context, userID string) (*entities.User, error)
ValidateEnterpriseInfo(ctx context.Context, userID string) error
CheckUnifiedSocialCodeExists(ctx context.Context, unifiedSocialCode string, excludeUserID string) (bool, error)
// 认证域专用:写入/覆盖企业信息
CreateOrUpdateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error
CompleteCertification(ctx context.Context, userID string) error
}
// UserAggregateServiceImpl 用户聚合服务实现
type UserAggregateServiceImpl struct {
userRepo repositories.UserRepository
eventBus interfaces.EventBus
logger *zap.Logger
}
// NewUserAggregateService 创建用户聚合服务
func NewUserAggregateService(
userRepo repositories.UserRepository,
eventBus interfaces.EventBus,
logger *zap.Logger,
) UserAggregateService {
return &UserAggregateServiceImpl{
userRepo: userRepo,
eventBus: eventBus,
logger: logger,
}
}
// ================ 聚合根管理 ================
// CreateUser 创建用户
func (s *UserAggregateServiceImpl) CreateUser(ctx context.Context, phone, password string) (*entities.User, error) {
s.logger.Debug("创建用户聚合根", zap.String("phone", phone))
// 1. 检查手机号是否已注册
exists, err := s.ExistsByPhone(ctx, phone)
if err != nil {
return nil, fmt.Errorf("检查手机号失败: %w", err)
}
if exists {
return nil, fmt.Errorf("手机号已注册")
}
// 2. 创建用户聚合根
user, err := entities.NewUser(phone, password)
if err != nil {
return nil, fmt.Errorf("创建用户失败: %w", err)
}
// 3. 调用聚合根方法进行注册
if err := user.Register(); err != nil {
return nil, fmt.Errorf("用户注册失败: %w", err)
}
// 4. 验证业务规则
if err := s.ValidateBusinessRules(ctx, user); err != nil {
return nil, fmt.Errorf("业务规则验证失败: %w", err)
}
// 5. 保存到仓储
if err := s.SaveUser(ctx, user); err != nil {
return nil, fmt.Errorf("保存用户失败: %w", err)
}
s.logger.Info("用户创建成功",
zap.String("user_id", user.ID),
zap.String("phone", phone),
)
return user, nil
}
// LoadUser 根据ID加载用户聚合根
func (s *UserAggregateServiceImpl) LoadUser(ctx context.Context, userID string) (*entities.User, error) {
s.logger.Debug("加载用户聚合根", zap.String("user_id", userID))
user, err := s.userRepo.GetByID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("用户不存在: %w", err)
}
// 验证业务规则
if err := s.ValidateBusinessRules(ctx, &user); err != nil {
s.logger.Warn("用户业务规则验证失败",
zap.String("user_id", userID),
zap.Error(err),
)
}
return &user, nil
}
// SaveUser 保存用户聚合根
func (s *UserAggregateServiceImpl) SaveUser(ctx context.Context, user *entities.User) error {
s.logger.Debug("保存用户聚合根", zap.String("user_id", user.ID))
// 1. 验证业务规则
if err := s.ValidateBusinessRules(ctx, user); err != nil {
return fmt.Errorf("业务规则验证失败: %w", err)
}
// 2. 检查聚合根是否存在
exists, err := s.userRepo.Exists(ctx, user.ID)
if err != nil {
return fmt.Errorf("检查用户存在性失败: %w", err)
}
// 3. 保存到仓储
if exists {
err = s.userRepo.Update(ctx, *user)
if err != nil {
s.logger.Error("更新用户聚合根失败", zap.Error(err))
return fmt.Errorf("更新用户失败: %w", err)
}
} else {
createdUser, err := s.userRepo.Create(ctx, *user)
if err != nil {
s.logger.Error("创建用户聚合根失败", zap.Error(err))
return fmt.Errorf("创建用户失败: %w", err)
}
// 更新用户ID如果仓储生成了新的ID
if createdUser.ID != "" {
user.ID = createdUser.ID
}
}
// 4. 发布领域事件
if err := s.publishDomainEvents(ctx, user); err != nil {
s.logger.Error("发布领域事件失败", zap.Error(err))
// 不返回错误,因为数据已保存成功
}
s.logger.Debug("用户聚合根保存成功", zap.String("user_id", user.ID))
return nil
}
// LoadUserByPhone 根据手机号加载用户聚合根
func (s *UserAggregateServiceImpl) LoadUserByPhone(ctx context.Context, phone string) (*entities.User, error) {
s.logger.Debug("根据手机号加载用户聚合根", zap.String("phone", phone))
user, err := s.userRepo.GetByPhone(ctx, phone)
if err != nil {
return nil, fmt.Errorf("用户不存在: %w", err)
}
// 验证业务规则
if err := s.ValidateBusinessRules(ctx, user); err != nil {
s.logger.Warn("用户业务规则验证失败",
zap.String("phone", phone),
zap.Error(err),
)
}
return user, nil
}
// ================ 业务规则验证 ================
// ValidateBusinessRules 验证业务规则
func (s *UserAggregateServiceImpl) ValidateBusinessRules(ctx context.Context, user *entities.User) error {
s.logger.Debug("验证用户业务规则", zap.String("user_id", user.ID))
// 1. 实体内部业务规则验证
if err := user.ValidateBusinessRules(); err != nil {
return fmt.Errorf("实体业务规则验证失败: %w", err)
}
// 2. 跨聚合根业务规则验证
if err := s.validateCrossAggregateRules(ctx, user); err != nil {
return fmt.Errorf("跨聚合根业务规则验证失败: %w", err)
}
// 3. 领域级业务规则验证
if err := s.validateDomainRules(ctx, user); err != nil {
return fmt.Errorf("领域业务规则验证失败: %w", err)
}
return nil
}
// CheckInvariance 检查聚合根不变量
func (s *UserAggregateServiceImpl) CheckInvariance(ctx context.Context, user *entities.User) error {
s.logger.Debug("检查用户聚合根不变量", zap.String("user_id", user.ID))
// 1. 检查手机号唯一性
exists, err := s.ExistsByPhone(ctx, user.Phone)
if err != nil {
return fmt.Errorf("检查手机号唯一性失败: %w", err)
}
if exists {
// 检查是否是同一个用户
existingUser, err := s.LoadUserByPhone(ctx, user.Phone)
if err != nil {
return fmt.Errorf("获取现有用户失败: %w", err)
}
if existingUser.ID != user.ID {
return fmt.Errorf("手机号已被其他用户使用")
}
}
return nil
}
// validateCrossAggregateRules 验证跨聚合根业务规则
func (s *UserAggregateServiceImpl) validateCrossAggregateRules(ctx context.Context, user *entities.User) error {
// 1. 检查手机号唯一性(排除自己)
existingUser, err := s.userRepo.GetByPhone(ctx, user.Phone)
if err == nil && existingUser.ID != user.ID {
return fmt.Errorf("手机号已被其他用户使用")
}
return nil
}
// validateDomainRules 验证领域级业务规则
func (s *UserAggregateServiceImpl) validateDomainRules(ctx context.Context, user *entities.User) error {
// 这里可以添加领域级的业务规则验证
// 比如:检查手机号是否在黑名单中、检查用户权限等
return nil
}
// ================ 查询方法 ================
// ExistsByPhone 检查手机号是否存在
func (s *UserAggregateServiceImpl) ExistsByPhone(ctx context.Context, phone string) (bool, error) {
_, err := s.userRepo.GetByPhone(ctx, phone)
if err != nil {
return false, nil // 用户不存在返回false
}
return true, nil
}
// ExistsByID 检查用户ID是否存在
func (s *UserAggregateServiceImpl) ExistsByID(ctx context.Context, userID string) (bool, error) {
return s.userRepo.Exists(ctx, userID)
}
// GetUserByID 根据ID获取用户聚合根
func (s *UserAggregateServiceImpl) GetUserByID(ctx context.Context, userID string) (*entities.User, error) {
return s.LoadUser(ctx, userID)
}
// UpdateLoginStats 更新用户登录统计
func (s *UserAggregateServiceImpl) UpdateLoginStats(ctx context.Context, userID string) error {
user, err := s.LoadUser(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
user.IncrementLoginCount()
if err := s.SaveUser(ctx, user); err != nil {
s.logger.Error("更新用户登录统计失败", zap.Error(err))
return fmt.Errorf("更新用户登录统计失败: %w", err)
}
s.logger.Info("用户登录统计更新成功", zap.String("user_id", userID))
return nil
}
// ================ 企业信息管理 ================
// CreateEnterpriseInfo 创建企业信息
func (s *UserAggregateServiceImpl) CreateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
s.logger.Debug("创建企业信息", zap.String("user_id", userID))
// 1. 加载用户聚合根
user, err := s.LoadUser(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
// 2. 检查是否已有企业信息
if user.HasEnterpriseInfo() {
return fmt.Errorf("用户已有企业信息")
}
// 3. 检查统一社会信用代码唯一性
exists, err := s.CheckUnifiedSocialCodeExists(ctx, unifiedSocialCode, userID)
if err != nil {
return fmt.Errorf("检查统一社会信用代码失败: %w", err)
}
if exists {
return fmt.Errorf("统一社会信用代码已被使用")
}
// 4. 使用聚合根方法创建企业信息
err = user.CreateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
if err != nil {
return fmt.Errorf("创建企业信息失败: %w", err)
}
// 5. 验证业务规则
if err := s.ValidateBusinessRules(ctx, user); err != nil {
return fmt.Errorf("业务规则验证失败: %w", err)
}
// 6. 保存聚合根
err = s.SaveUser(ctx, user)
if err != nil {
s.logger.Error("保存用户聚合根失败", zap.Error(err))
return fmt.Errorf("保存企业信息失败: %w", err)
}
s.logger.Info("企业信息创建成功", zap.String("user_id", userID))
return nil
}
// UpdateEnterpriseInfo 更新企业信息
func (s *UserAggregateServiceImpl) UpdateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
s.logger.Debug("更新企业信息", zap.String("user_id", userID))
// 1. 加载用户聚合根
user, err := s.LoadUser(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
// 2. 检查是否有企业信息
if !user.HasEnterpriseInfo() {
return fmt.Errorf("用户暂无企业信息")
}
// 3. 检查统一社会信用代码唯一性(排除自己)
exists, err := s.CheckUnifiedSocialCodeExists(ctx, unifiedSocialCode, userID)
if err != nil {
return fmt.Errorf("检查统一社会信用代码失败: %w", err)
}
if exists {
return fmt.Errorf("统一社会信用代码已被其他用户使用")
}
// 4. 使用聚合根方法更新企业信息
err = user.UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
if err != nil {
return fmt.Errorf("更新企业信息失败: %w", err)
}
// 5. 验证业务规则
if err := s.ValidateBusinessRules(ctx, user); err != nil {
return fmt.Errorf("业务规则验证失败: %w", err)
}
// 6. 保存聚合根
err = s.SaveUser(ctx, user)
if err != nil {
s.logger.Error("保存用户聚合根失败", zap.Error(err))
return fmt.Errorf("保存企业信息失败: %w", err)
}
s.logger.Info("企业信息更新成功", zap.String("user_id", userID))
return nil
}
// GetUserWithEnterpriseInfo 获取用户信息(包含企业信息)
func (s *UserAggregateServiceImpl) GetUserWithEnterpriseInfo(ctx context.Context, userID string) (*entities.User, error) {
s.logger.Debug("获取用户信息(包含企业信息)", zap.String("user_id", userID))
// 加载用户聚合根(包含企业信息)
user, err := s.userRepo.GetByIDWithEnterpriseInfo(ctx, userID)
if err != nil {
return nil, fmt.Errorf("用户不存在: %w", err)
}
// 验证业务规则
if err := s.ValidateBusinessRules(ctx, &user); err != nil {
s.logger.Warn("用户业务规则验证失败",
zap.String("user_id", userID),
zap.Error(err),
)
}
return &user, nil
}
// ValidateEnterpriseInfo 验证企业信息
func (s *UserAggregateServiceImpl) ValidateEnterpriseInfo(ctx context.Context, userID string) error {
s.logger.Debug("验证企业信息", zap.String("user_id", userID))
// 1. 加载用户聚合根
user, err := s.LoadUser(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
// 2. 使用聚合根方法验证企业信息
err = user.ValidateEnterpriseInfo()
if err != nil {
return fmt.Errorf("企业信息验证失败: %w", err)
}
return nil
}
// CheckUnifiedSocialCodeExists 检查统一社会信用代码是否存在
func (s *UserAggregateServiceImpl) CheckUnifiedSocialCodeExists(ctx context.Context, unifiedSocialCode string, excludeUserID string) (bool, error) {
s.logger.Debug("检查统一社会信用代码是否存在",
zap.String("unified_social_code", unifiedSocialCode),
zap.String("exclude_user_id", excludeUserID),
)
// 参数验证
if unifiedSocialCode == "" {
return false, fmt.Errorf("统一社会信用代码不能为空")
}
// 通过用户仓库查询统一社会信用代码是否存在
exists, err := s.userRepo.ExistsByUnifiedSocialCode(ctx, unifiedSocialCode, excludeUserID)
if err != nil {
s.logger.Error("查询统一社会信用代码失败", zap.Error(err))
return false, fmt.Errorf("查询企业信息失败: %w", err)
}
if exists {
s.logger.Info("统一社会信用代码已存在",
zap.String("unified_social_code", unifiedSocialCode),
zap.String("exclude_user_id", excludeUserID),
)
} else {
s.logger.Debug("统一社会信用代码不存在",
zap.String("unified_social_code", unifiedSocialCode),
)
}
return exists, nil
}
// CreateOrUpdateEnterpriseInfo 认证域专用:写入/覆盖企业信息
func (s *UserAggregateServiceImpl) CreateOrUpdateEnterpriseInfo(
ctx context.Context,
userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string,
) error {
user, err := s.LoadUser(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
if user.EnterpriseInfo == nil {
enterpriseInfo, err := entities.NewEnterpriseInfo(userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
if err != nil {
return err
}
user.EnterpriseInfo = enterpriseInfo
} else {
err := user.EnterpriseInfo.UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
if err != nil {
return err
}
}
return s.SaveUser(ctx, user)
}
// CompleteCertification 完成认证
func (s *UserAggregateServiceImpl) CompleteCertification(ctx context.Context, userID string) error {
user, err := s.LoadUser(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
user.CompleteCertification()
return s.SaveUser(ctx, user)
}
// ListUsers 获取用户列表
func (s *UserAggregateServiceImpl) ListUsers(ctx context.Context, query *queries.ListUsersQuery) ([]*entities.User, int64, error) {
s.logger.Debug("获取用户列表",
zap.Int("page", query.Page),
zap.Int("page_size", query.PageSize),
)
// 直接调用仓储层查询用户列表
users, total, err := s.userRepo.ListUsers(ctx, query)
if err != nil {
s.logger.Error("查询用户列表失败", zap.Error(err))
return nil, 0, fmt.Errorf("查询用户列表失败: %w", err)
}
s.logger.Info("用户列表查询成功",
zap.Int("count", len(users)),
zap.Int64("total", total),
)
return users, total, nil
}
// GetUserStats 获取用户统计信息
func (s *UserAggregateServiceImpl) GetUserStats(ctx context.Context) (*repositories.UserStats, error) {
s.logger.Debug("获取用户统计信息")
// 直接调用仓储层查询用户统计信息
stats, err := s.userRepo.GetStats(ctx)
if err != nil {
s.logger.Error("查询用户统计信息失败", zap.Error(err))
return nil, fmt.Errorf("查询用户统计信息失败: %w", err)
}
s.logger.Info("用户统计信息查询成功",
zap.Int64("total_users", stats.TotalUsers),
zap.Int64("active_users", stats.ActiveUsers),
zap.Int64("certified_users", stats.CertifiedUsers),
)
return stats, nil
}
// ================ 私有方法 ================
// publishDomainEvents 发布领域事件
func (s *UserAggregateServiceImpl) publishDomainEvents(ctx context.Context, user *entities.User) error {
events := user.GetDomainEvents()
if len(events) == 0 {
return nil
}
for _, event := range events {
// 这里需要将领域事件转换为标准事件格式
// 暂时跳过,后续可以完善事件转换逻辑
s.logger.Debug("发布领域事件",
zap.String("user_id", user.ID),
zap.Any("event", event),
)
}
// 清除已发布的事件
user.ClearDomainEvents()
return nil
}