Files
tyapi-server/internal/domains/user/services/user_aggregate_service.go

569 lines
18 KiB
Go
Raw Normal View History

2025-07-28 01:46:39 +08:00
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)
// 企业信息管理
2025-07-30 00:51:22 +08:00
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
2025-07-28 01:46:39 +08:00
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)
// 认证域专用:写入/覆盖企业信息
2025-07-30 00:51:22 +08:00
CreateOrUpdateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error
2025-07-28 01:46:39 +08:00
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 {
2025-07-30 00:51:22 +08:00
s.logger.Warn("用户业务规则验证失败",
2025-07-28 01:46:39 +08:00
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 {
2025-07-30 00:51:22 +08:00
s.logger.Warn("用户业务规则验证失败",
2025-07-28 01:46:39 +08:00
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 创建企业信息
2025-07-30 00:51:22 +08:00
func (s *UserAggregateServiceImpl) CreateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
2025-07-28 01:46:39 +08:00
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. 使用聚合根方法创建企业信息
2025-07-30 00:51:22 +08:00
err = user.CreateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
2025-07-28 01:46:39 +08:00
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 更新企业信息
2025-07-30 00:51:22 +08:00
func (s *UserAggregateServiceImpl) UpdateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
2025-07-28 01:46:39 +08:00
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. 使用聚合根方法更新企业信息
2025-07-30 00:51:22 +08:00
err = user.UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
2025-07-28 01:46:39 +08:00
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 {
2025-07-30 00:51:22 +08:00
s.logger.Warn("用户业务规则验证失败",
2025-07-28 01:46:39 +08:00
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) {
2025-07-30 00:51:22 +08:00
s.logger.Debug("检查统一社会信用代码是否存在",
2025-07-28 01:46:39 +08:00
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 {
2025-07-30 00:51:22 +08:00
s.logger.Info("统一社会信用代码已存在",
2025-07-28 01:46:39 +08:00
zap.String("unified_social_code", unifiedSocialCode),
zap.String("exclude_user_id", excludeUserID),
)
} else {
2025-07-30 00:51:22 +08:00
s.logger.Debug("统一社会信用代码不存在",
2025-07-28 01:46:39 +08:00
zap.String("unified_social_code", unifiedSocialCode),
)
}
return exists, nil
}
// CreateOrUpdateEnterpriseInfo 认证域专用:写入/覆盖企业信息
func (s *UserAggregateServiceImpl) CreateOrUpdateEnterpriseInfo(
ctx context.Context,
2025-07-30 00:51:22 +08:00
userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string,
2025-07-28 01:46:39 +08:00
) error {
user, err := s.LoadUser(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
if user.EnterpriseInfo == nil {
2025-07-30 00:51:22 +08:00
enterpriseInfo, err := entities.NewEnterpriseInfo(userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
2025-07-28 01:46:39 +08:00
if err != nil {
return err
}
user.EnterpriseInfo = enterpriseInfo
} else {
2025-07-30 00:51:22 +08:00
err := user.EnterpriseInfo.UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
2025-07-28 01:46:39 +08:00
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) {
2025-07-30 00:51:22 +08:00
s.logger.Debug("获取用户列表",
2025-07-28 01:46:39 +08:00
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)
}
2025-07-30 00:51:22 +08:00
s.logger.Info("用户列表查询成功",
2025-07-28 01:46:39 +08:00
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)
}
2025-07-30 00:51:22 +08:00
s.logger.Info("用户统计信息查询成功",
2025-07-28 01:46:39 +08:00
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 {
// 这里需要将领域事件转换为标准事件格式
// 暂时跳过,后续可以完善事件转换逻辑
2025-07-30 00:51:22 +08:00
s.logger.Debug("发布领域事件",
2025-07-28 01:46:39 +08:00
zap.String("user_id", user.ID),
zap.Any("event", event),
)
}
// 清除已发布的事件
user.ClearDomainEvents()
return nil
2025-07-30 00:51:22 +08:00
}