turash/bugulma/backend/internal/service/user_service.go

202 lines
5.5 KiB
Go

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
}