package services import ( "context" "fmt" "github.com/google/uuid" "go.uber.org/zap" "tyapi-server/internal/domains/user/dto" "tyapi-server/internal/domains/user/entities" "tyapi-server/internal/domains/user/events" "tyapi-server/internal/domains/user/repositories" "tyapi-server/internal/shared/interfaces" ) // UserService 用户服务实现 type UserService struct { repo *repositories.UserRepository smsCodeService *SMSCodeService eventBus interfaces.EventBus logger *zap.Logger } // NewUserService 创建用户服务 func NewUserService( repo *repositories.UserRepository, smsCodeService *SMSCodeService, eventBus interfaces.EventBus, logger *zap.Logger, ) *UserService { return &UserService{ repo: repo, smsCodeService: smsCodeService, eventBus: eventBus, logger: logger, } } // Name 返回服务名称 func (s *UserService) Name() string { return "user-service" } // Initialize 初始化服务 func (s *UserService) Initialize(ctx context.Context) error { s.logger.Info("用户服务已初始化") return nil } // HealthCheck 健康检查 func (s *UserService) HealthCheck(ctx context.Context) error { // 简单的健康检查 return nil } // Shutdown 关闭服务 func (s *UserService) Shutdown(ctx context.Context) error { s.logger.Info("用户服务已关闭") return nil } // Register 用户注册 func (s *UserService) Register(ctx context.Context, registerReq *dto.RegisterRequest) (*entities.User, error) { // 1. 验证短信验证码 if err := s.smsCodeService.VerifyCode(ctx, registerReq.Phone, registerReq.Code, entities.SMSSceneRegister); err != nil { return nil, fmt.Errorf("验证码验证失败: %w", err) } // 2. 检查手机号是否已存在 if err := s.checkPhoneDuplicate(ctx, registerReq.Phone); err != nil { return nil, err } // 3. 使用工厂方法创建用户实体(业务规则验证在实体中完成) user, err := entities.NewUser(registerReq.Phone, registerReq.Password) if err != nil { return nil, fmt.Errorf("创建用户失败: %w", err) } // 4. 设置用户ID user.ID = uuid.New().String() // 5. 保存用户 if err := s.repo.Create(ctx, user); err != nil { s.logger.Error("创建用户失败", zap.Error(err)) return nil, fmt.Errorf("创建用户失败: %w", err) } // 6. 发布用户注册事件 event := events.NewUserRegisteredEvent(user, s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("发布用户注册事件失败", zap.Error(err)) } s.logger.Info("用户注册成功", zap.String("user_id", user.ID), zap.String("phone", user.Phone)) return user, nil } // LoginWithPassword 密码登录 func (s *UserService) LoginWithPassword(ctx context.Context, loginReq *dto.LoginWithPasswordRequest) (*entities.User, error) { // 1. 根据手机号查找用户 user, err := s.repo.FindByPhone(ctx, loginReq.Phone) if err != nil { return nil, fmt.Errorf("用户名或密码错误") } // 2. 检查用户是否可以登录(委托给实体) if !user.CanLogin() { return nil, fmt.Errorf("用户状态异常,无法登录") } // 3. 验证密码(委托给实体) if !user.CheckPassword(loginReq.Password) { return nil, fmt.Errorf("用户名或密码错误") } // 4. 发布用户登录事件 event := events.NewUserLoggedInEvent( user.ID, user.Phone, s.getClientIP(ctx), s.getUserAgent(ctx), s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("发布用户登录事件失败", zap.Error(err)) } s.logger.Info("用户密码登录成功", zap.String("user_id", user.ID), zap.String("phone", user.Phone)) return user, nil } // LoginWithSMS 短信验证码登录 func (s *UserService) LoginWithSMS(ctx context.Context, loginReq *dto.LoginWithSMSRequest) (*entities.User, error) { // 1. 验证短信验证码 if err := s.smsCodeService.VerifyCode(ctx, loginReq.Phone, loginReq.Code, entities.SMSSceneLogin); err != nil { return nil, fmt.Errorf("验证码验证失败: %w", err) } // 2. 根据手机号查找用户 user, err := s.repo.FindByPhone(ctx, loginReq.Phone) if err != nil { return nil, fmt.Errorf("用户不存在") } // 3. 检查用户是否可以登录(委托给实体) if !user.CanLogin() { return nil, fmt.Errorf("用户状态异常,无法登录") } // 4. 发布用户登录事件 event := events.NewUserLoggedInEvent( user.ID, user.Phone, s.getClientIP(ctx), s.getUserAgent(ctx), s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("发布用户登录事件失败", zap.Error(err)) } s.logger.Info("用户短信登录成功", zap.String("user_id", user.ID), zap.String("phone", user.Phone)) return user, nil } // ChangePassword 修改密码 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 := s.smsCodeService.VerifyCode(ctx, user.Phone, req.Code, entities.SMSSceneChangePassword); err != nil { return fmt.Errorf("验证码验证失败: %w", err) } // 3. 执行业务逻辑(委托给实体) if err := user.ChangePassword(req.OldPassword, req.NewPassword, req.ConfirmNewPassword); err != nil { return err } // 4. 保存用户 if err := s.repo.Update(ctx, user); err != nil { return fmt.Errorf("密码更新失败: %w", err) } // 5. 发布密码修改事件 event := events.NewUserPasswordChangedEvent(user.ID, user.Phone, s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("发布密码修改事件失败", zap.Error(err)) } s.logger.Info("密码修改成功", zap.String("user_id", userID)) return nil } // GetByID 根据ID获取用户 func (s *UserService) GetByID(ctx context.Context, id string) (*entities.User, error) { if id == "" { return nil, fmt.Errorf("用户ID不能为空") } user, err := s.repo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("用户不存在: %w", err) } return user, nil } // UpdateUserProfile 更新用户信息 func (s *UserService) UpdateUserProfile(ctx context.Context, userID string, req *dto.UpdateProfileRequest) (*entities.User, error) { // 1. 获取用户信息 user, err := s.repo.GetByID(ctx, userID) if err != nil { return nil, fmt.Errorf("用户不存在: %w", err) } // 2. 更新手机号(如果需要) if req.Phone != "" && req.Phone != user.Phone { // 检查新手机号是否已存在 if err := s.checkPhoneDuplicate(ctx, req.Phone); err != nil { return nil, err } // 使用实体的方法设置手机号 if err := user.SetPhone(req.Phone); err != nil { return nil, err } } // 3. 保存用户 if err := s.repo.Update(ctx, user); err != nil { return nil, fmt.Errorf("更新用户信息失败: %w", err) } s.logger.Info("用户信息更新成功", zap.String("user_id", userID)) return user, nil } // DeactivateUser 停用用户 func (s *UserService) DeactivateUser(ctx context.Context, userID string) error { // 1. 获取用户信息 user, err := s.repo.GetByID(ctx, userID) if err != nil { return fmt.Errorf("用户不存在: %w", err) } // 2. 检查用户状态 if user.IsDeleted() { return fmt.Errorf("用户已被停用") } // 3. 软删除用户(这里需要调用仓储的软删除方法) if err := s.repo.SoftDelete(ctx, userID); err != nil { return fmt.Errorf("停用用户失败: %w", err) } s.logger.Info("用户停用成功", zap.String("user_id", userID)) return nil } // ================ 工具方法 ================ // checkPhoneDuplicate 检查手机号重复 func (s *UserService) checkPhoneDuplicate(ctx context.Context, phone string) error { if _, err := s.repo.FindByPhone(ctx, phone); err == nil { return fmt.Errorf("手机号已存在") } return nil } // getCorrelationID 获取关联ID func (s *UserService) getCorrelationID(ctx context.Context) string { if id := ctx.Value("correlation_id"); id != nil { if strID, ok := id.(string); ok { return strID } } return uuid.New().String() } // getClientIP 获取客户端IP func (s *UserService) getClientIP(ctx context.Context) string { if ip := ctx.Value("client_ip"); ip != nil { if strIP, ok := ip.(string); ok { return strIP } } return "" } // getUserAgent 获取用户代理 func (s *UserService) getUserAgent(ctx context.Context) string { if ua := ctx.Value("user_agent"); ua != nil { if strUA, ok := ua.(string); ok { return strUA } } return "" }