go-chi-oapi-codegen-todolist/backend/internal/service/user_service.go
2025-04-20 15:58:52 +07:00

84 lines
2.5 KiB
Go

package service
import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/Sosokker/todolist-backend/internal/domain" // Adjust path
"github.com/Sosokker/todolist-backend/internal/repository" // Adjust path
"github.com/google/uuid"
)
type userService struct {
userRepo repository.UserRepository
logger *slog.Logger
}
func NewUserService(repo repository.UserRepository) UserService {
return &userService{
userRepo: repo,
logger: slog.Default().With("service", "user"),
}
}
func (s *userService) GetUserByID(ctx context.Context, id uuid.UUID) (*domain.User, error) {
user, err := s.userRepo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, domain.ErrNotFound) {
s.logger.WarnContext(ctx, "User not found by ID", "userId", id)
return nil, domain.ErrNotFound
}
s.logger.ErrorContext(ctx, "Failed to get user by ID from repo", "error", err, "userId", id)
return nil, domain.ErrInternalServer
}
return user, nil
}
func (s *userService) UpdateUser(ctx context.Context, userID uuid.UUID, input UpdateUserInput) (*domain.User, error) {
// GetUserByID handles NotFound/Forbidden error
existingUser, err := s.GetUserByID(ctx, userID)
if err != nil {
return nil, err
}
// Prepare update data DTO for the repository
updateData := &domain.User{
// Copy non-updatable fields or handle defaults
}
needsUpdate := false
if input.Username != nil {
if err := ValidateUsername(*input.Username); err != nil {
return nil, err
}
updateData.Username = *input.Username
needsUpdate = true
} else {
updateData.Username = existingUser.Username
}
// TODO: Add logic for other updatable fields (e.g., email)
// Password updates should involve hashing and likely be a separate endpoint/service method.
// Email updates might require a verification flow.
if !needsUpdate {
s.logger.InfoContext(ctx, "No fields provided for user update", "userId", userID)
return existingUser, nil
}
updatedUser, err := s.userRepo.Update(ctx, userID, updateData)
if err != nil {
if errors.Is(err, domain.ErrConflict) {
s.logger.WarnContext(ctx, "User update conflict", "error", err, "userId", userID, "conflictingUsername", updateData.Username)
return nil, fmt.Errorf("username '%s' is already taken: %w", updateData.Username, domain.ErrConflict)
}
s.logger.ErrorContext(ctx, "Failed to update user in repo", "error", err, "userId", userID)
return nil, domain.ErrInternalServer
}
s.logger.InfoContext(ctx, "User updated successfully", "userId", userID)
return updatedUser, nil
}