package service import ( "context" "encoding/json" "fmt" "time" "bugulma/backend/internal/domain" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) type UserService struct { userRepo domain.UserRepository } func NewUserService(userRepo domain.UserRepository) *UserService { return &UserService{ userRepo: userRepo, } } type CreateUserRequest struct { Email string Name string Password string Role domain.UserRole Permissions []string } type UpdateUserRequest struct { Name *string Email *string Role *domain.UserRole Permissions *[]string IsActive *bool } // ListUsers retrieves users with filters and pagination func (s *UserService) ListUsers(ctx context.Context, filters domain.UserListFilters, pagination domain.PaginationParams) (*domain.PaginatedResult[domain.User], error) { return s.userRepo.List(ctx, filters, pagination) } // GetUser retrieves a user by ID func (s *UserService) GetUser(ctx context.Context, userID string) (*domain.User, error) { return s.userRepo.GetByID(ctx, userID) } // CreateUser creates a new user func (s *UserService) CreateUser(ctx context.Context, req CreateUserRequest) (*domain.User, error) { // Check if email already exists existing, err := s.userRepo.GetByEmail(ctx, req.Email) if err == nil && existing != nil { return nil, fmt.Errorf("user with email %s already exists", req.Email) } // Hash password hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } // Marshal permissions permissionsJSON, err := json.Marshal(req.Permissions) if err != nil { return nil, fmt.Errorf("failed to marshal permissions: %w", err) } user := &domain.User{ ID: uuid.New().String(), Email: req.Email, Name: req.Name, Password: string(hashedPassword), Role: req.Role, Permissions: string(permissionsJSON), IsActive: true, } if err := s.userRepo.Create(ctx, user); err != nil { return nil, fmt.Errorf("failed to create user: %w", err) } return user, nil } // UpdateUser updates a user func (s *UserService) UpdateUser(ctx context.Context, userID string, req UpdateUserRequest) (*domain.User, error) { user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return nil, err } if req.Name != nil { user.Name = *req.Name } if req.Email != nil { // Check if email is already taken by another user existing, err := s.userRepo.GetByEmail(ctx, *req.Email) if err == nil && existing != nil && existing.ID != userID { return nil, fmt.Errorf("email %s is already taken", *req.Email) } user.Email = *req.Email } if req.IsActive != nil { user.IsActive = *req.IsActive } // Update role if provided if req.Role != nil { if err := s.userRepo.UpdateRole(ctx, userID, *req.Role); err != nil { return nil, err } user.Role = *req.Role } // Update permissions if provided if req.Permissions != nil { if err := s.userRepo.UpdatePermissions(ctx, userID, *req.Permissions); err != nil { return nil, err } permissionsJSON, err := json.Marshal(*req.Permissions) if err != nil { return nil, fmt.Errorf("failed to marshal permissions: %w", err) } user.Permissions = string(permissionsJSON) } // Save updates (if any non-repository fields were updated) if req.Name != nil || req.Email != nil || req.IsActive != nil { if err := s.userRepo.Update(ctx, user); err != nil { return nil, fmt.Errorf("failed to update user: %w", err) } } return user, nil } // UpdateRole updates a user's role func (s *UserService) UpdateRole(ctx context.Context, userID string, role domain.UserRole) error { return s.userRepo.UpdateRole(ctx, userID, role) } // UpdatePermissions updates a user's permissions func (s *UserService) UpdatePermissions(ctx context.Context, userID string, permissions []string) error { return s.userRepo.UpdatePermissions(ctx, userID, permissions) } // DeactivateUser deactivates a user func (s *UserService) DeactivateUser(ctx context.Context, userID string) error { return s.userRepo.Deactivate(ctx, userID) } // ActivateUser activates a user func (s *UserService) ActivateUser(ctx context.Context, userID string) error { return s.userRepo.Activate(ctx, userID) } // GetUserActivity retrieves activity log for a user func (s *UserService) GetUserActivity(ctx context.Context, userID string, limit, offset int) ([]interface{}, int64, error) { // TODO: Implement when activity logging is ready return []interface{}{}, 0, nil } // GetUserStats retrieves user statistics func (s *UserService) GetUserStats(ctx context.Context) (map[string]interface{}, error) { // Get all users allUsers, err := s.userRepo.List(ctx, domain.UserListFilters{}, domain.PaginationParams{Limit: 10000, Offset: 0}) if err != nil { return nil, err } stats := map[string]interface{}{ "total": allUsers.Total, "active": 0, "inactive": 0, "by_role": make(map[string]int64), "new_this_month": 0, } now := time.Now() monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) for _, user := range allUsers.Items { if user.IsActive { stats["active"] = stats["active"].(int) + 1 } else { stats["inactive"] = stats["inactive"].(int) + 1 } roleCount := stats["by_role"].(map[string]int64) roleCount[string(user.Role)] = roleCount[string(user.Role)] + 1 if user.CreatedAt.After(monthStart) { stats["new_this_month"] = stats["new_this_month"].(int) + 1 } } return stats, nil }