package services import ( "context" "fmt" "time" "github.com/google/uuid" "go.uber.org/zap" "golang.org/x/crypto/bcrypt" "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 eventBus interfaces.EventBus logger *zap.Logger } // NewUserService 创建用户服务 func NewUserService( repo *repositories.UserRepository, eventBus interfaces.EventBus, logger *zap.Logger, ) *UserService { return &UserService{ repo: repo, 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("User service initialized") return nil } // HealthCheck 健康检查 func (s *UserService) HealthCheck(ctx context.Context) error { // 简单检查:尝试查询用户数量 _, err := s.repo.Count(ctx, interfaces.CountOptions{}) return err } // Shutdown 关闭服务 func (s *UserService) Shutdown(ctx context.Context) error { s.logger.Info("User service shutdown") return nil } // Create 创建用户 func (s *UserService) Create(ctx context.Context, createDTO interface{}) (*entities.User, error) { req, ok := createDTO.(*dto.CreateUserRequest) if !ok { return nil, fmt.Errorf("invalid DTO type for user creation") } // 验证业务规则 if err := s.ValidateCreate(ctx, req); err != nil { return nil, err } // 检查用户名和邮箱是否已存在 if err := s.checkDuplicates(ctx, req.Username, req.Email); err != nil { return nil, err } // 创建用户实体 user := req.ToEntity() user.ID = uuid.New().String() // 加密密码 hashedPassword, err := s.hashPassword(req.Password) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } user.Password = hashedPassword // 保存用户 if err := s.repo.Create(ctx, user); err != nil { s.logger.Error("Failed to create user", zap.Error(err)) return nil, fmt.Errorf("failed to create user: %w", err) } // 发布用户创建事件 event := events.NewUserCreatedEvent(user, s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("Failed to publish user created event", zap.Error(err)) } s.logger.Info("User created successfully", zap.String("user_id", user.ID), zap.String("username", user.Username)) return user, nil } // GetByID 根据ID获取用户 func (s *UserService) GetByID(ctx context.Context, id string) (*entities.User, error) { if id == "" { return nil, fmt.Errorf("user ID is required") } user, err := s.repo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("user not found: %w", err) } return user, nil } // Update 更新用户 func (s *UserService) Update(ctx context.Context, id string, updateDTO interface{}) (*entities.User, error) { req, ok := updateDTO.(*dto.UpdateUserRequest) if !ok { return nil, fmt.Errorf("invalid DTO type for user update") } // 验证业务规则 if err := s.ValidateUpdate(ctx, id, req); err != nil { return nil, err } // 获取现有用户 user, err := s.repo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("user not found: %w", err) } // 记录变更前的值 oldValues := s.captureUserValues(user) // 应用更新 s.applyUserUpdates(user, req) // 保存更新 if err := s.repo.Update(ctx, user); err != nil { s.logger.Error("Failed to update user", zap.Error(err)) return nil, fmt.Errorf("failed to update user: %w", err) } // 发布用户更新事件 newValues := s.captureUserValues(user) changes := s.findChanges(oldValues, newValues) if len(changes) > 0 { event := events.NewUserUpdatedEvent(user.ID, changes, oldValues, newValues, s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("Failed to publish user updated event", zap.Error(err)) } } s.logger.Info("User updated successfully", zap.String("user_id", user.ID), zap.Int("changes", len(changes))) return user, nil } // Delete 删除用户 func (s *UserService) Delete(ctx context.Context, id string) error { if id == "" { return fmt.Errorf("user ID is required") } // 获取用户信息用于事件 user, err := s.repo.GetByID(ctx, id) if err != nil { return fmt.Errorf("user not found: %w", err) } // 软删除用户 if err := s.repo.SoftDelete(ctx, id); err != nil { s.logger.Error("Failed to delete user", zap.Error(err)) return fmt.Errorf("failed to delete user: %w", err) } // 发布用户删除事件 event := events.NewUserDeletedEvent(user.ID, user.Username, user.Email, true, s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("Failed to publish user deleted event", zap.Error(err)) } s.logger.Info("User deleted successfully", zap.String("user_id", id)) return nil } // List 获取用户列表 func (s *UserService) List(ctx context.Context, options interfaces.ListOptions) ([]*entities.User, error) { return s.repo.List(ctx, options) } // Search 搜索用户 func (s *UserService) Search(ctx context.Context, query string, options interfaces.ListOptions) ([]*entities.User, error) { // 设置搜索关键字 searchOptions := options searchOptions.Search = query return s.repo.List(ctx, searchOptions) } // Count 统计用户数量 func (s *UserService) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) { return s.repo.Count(ctx, options) } // Validate 验证用户实体 func (s *UserService) Validate(ctx context.Context, entity *entities.User) error { return entity.Validate() } // ValidateCreate 验证创建请求 func (s *UserService) ValidateCreate(ctx context.Context, createDTO interface{}) error { req, ok := createDTO.(*dto.CreateUserRequest) if !ok { return fmt.Errorf("invalid DTO type") } // 基础验证已经由binding标签处理,这里添加业务规则验证 if req.Username == "admin" || req.Username == "root" { return fmt.Errorf("username '%s' is reserved", req.Username) } return nil } // ValidateUpdate 验证更新请求 func (s *UserService) ValidateUpdate(ctx context.Context, id string, updateDTO interface{}) error { _, ok := updateDTO.(*dto.UpdateUserRequest) if !ok { return fmt.Errorf("invalid DTO type") } if id == "" { return fmt.Errorf("user ID is required") } return nil } // 业务方法 // Login 用户登录 func (s *UserService) Login(ctx context.Context, loginReq *dto.LoginRequest) (*entities.User, error) { // 根据用户名或邮箱查找用户 var user *entities.User var err error if s.isEmail(loginReq.Login) { user, err = s.repo.FindByEmail(ctx, loginReq.Login) } else { user, err = s.repo.FindByUsername(ctx, loginReq.Login) } if err != nil { return nil, fmt.Errorf("invalid credentials") } // 验证密码 if !s.checkPassword(loginReq.Password, user.Password) { return nil, fmt.Errorf("invalid credentials") } // 检查用户状态 if !user.CanLogin() { return nil, fmt.Errorf("account is disabled or suspended") } // 更新最后登录时间 user.UpdateLastLogin() if err := s.repo.Update(ctx, user); err != nil { s.logger.Warn("Failed to update last login time", zap.Error(err)) } // 发布登录事件 event := events.NewUserLoggedInEvent( user.ID, user.Username, s.getClientIP(ctx), s.getUserAgent(ctx), s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("Failed to publish user logged in event", zap.Error(err)) } s.logger.Info("User logged in successfully", zap.String("user_id", user.ID), zap.String("username", user.Username)) return user, nil } // ChangePassword 修改密码 func (s *UserService) ChangePassword(ctx context.Context, userID string, req *dto.ChangePasswordRequest) error { // 获取用户 user, err := s.repo.GetByID(ctx, userID) if err != nil { return fmt.Errorf("user not found: %w", err) } // 验证旧密码 if !s.checkPassword(req.OldPassword, user.Password) { return fmt.Errorf("current password is incorrect") } // 加密新密码 hashedPassword, err := s.hashPassword(req.NewPassword) if err != nil { return fmt.Errorf("failed to hash new password: %w", err) } // 更新密码 user.Password = hashedPassword if err := s.repo.Update(ctx, user); err != nil { return fmt.Errorf("failed to update password: %w", err) } // 发布密码修改事件 event := events.NewUserPasswordChangedEvent(user.ID, user.Username, s.getCorrelationID(ctx)) if err := s.eventBus.Publish(ctx, event); err != nil { s.logger.Warn("Failed to publish password changed event", zap.Error(err)) } s.logger.Info("Password changed successfully", zap.String("user_id", userID)) return nil } // GetStats 获取用户统计 func (s *UserService) GetStats(ctx context.Context) (*dto.UserStatsResponse, error) { total, err := s.repo.Count(ctx, interfaces.CountOptions{}) if err != nil { return nil, err } // 这里可以并行查询不同状态的用户数量 // 简化实现,返回基础统计 return &dto.UserStatsResponse{ TotalUsers: total, ActiveUsers: total, // 简化 InactiveUsers: 0, SuspendedUsers: 0, NewUsersToday: 0, NewUsersWeek: 0, NewUsersMonth: 0, }, nil } // 私有方法 // checkDuplicates 检查重复的用户名和邮箱 func (s *UserService) checkDuplicates(ctx context.Context, username, email string) error { // 检查用户名 if existingUser, err := s.repo.FindByUsername(ctx, username); err == nil && existingUser != nil { return fmt.Errorf("username already exists") } // 检查邮箱 if existingUser, err := s.repo.FindByEmail(ctx, email); err == nil && existingUser != nil { return fmt.Errorf("email already exists") } return nil } // hashPassword 加密密码 func (s *UserService) hashPassword(password string) (string, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", err } return string(hash), nil } // checkPassword 验证密码 func (s *UserService) checkPassword(password, hash string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil } // isEmail 检查是否为邮箱格式 func (s *UserService) isEmail(str string) bool { return len(str) > 0 && len(str) < 255 && len(str) > 5 && str[len(str)-4:] != ".." && (len(str) > 6 && str[len(str)-4:] == ".com") || (len(str) > 5 && str[len(str)-3:] == ".cn") || (len(str) > 6 && str[len(str)-4:] == ".org") || (len(str) > 6 && str[len(str)-4:] == ".net") // 简化的邮箱检查,实际应该使用正则表达式 } // applyUserUpdates 应用用户更新 func (s *UserService) applyUserUpdates(user *entities.User, req *dto.UpdateUserRequest) { if req.FirstName != nil { user.FirstName = *req.FirstName } if req.LastName != nil { user.LastName = *req.LastName } if req.Phone != nil { user.Phone = *req.Phone } if req.Avatar != nil { user.Avatar = *req.Avatar } user.UpdatedAt = time.Now() } // captureUserValues 捕获用户值用于变更比较 func (s *UserService) captureUserValues(user *entities.User) map[string]interface{} { return map[string]interface{}{ "first_name": user.FirstName, "last_name": user.LastName, "phone": user.Phone, "avatar": user.Avatar, } } // findChanges 找出变更的字段 func (s *UserService) findChanges(oldValues, newValues map[string]interface{}) map[string]interface{} { changes := make(map[string]interface{}) for key, newValue := range newValues { if oldValue, exists := oldValues[key]; !exists || oldValue != newValue { changes[key] = newValue } } return changes } // getCorrelationID 获取关联ID func (s *UserService) getCorrelationID(ctx context.Context) string { if id := ctx.Value("correlation_id"); id != nil { if correlationID, ok := id.(string); ok { return correlationID } } return uuid.New().String() } // getClientIP 获取客户端IP func (s *UserService) getClientIP(ctx context.Context) string { if ip := ctx.Value("client_ip"); ip != nil { if clientIP, ok := ip.(string); ok { return clientIP } } return "unknown" } // getUserAgent 获取用户代理 func (s *UserService) getUserAgent(ctx context.Context) string { if ua := ctx.Value("user_agent"); ua != nil { if userAgent, ok := ua.(string); ok { return userAgent } } return "unknown" }