256 lines
7.0 KiB
Go
256 lines
7.0 KiB
Go
package processor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"apskel-pos-be/internal/entities"
|
|
"apskel-pos-be/internal/mappers"
|
|
"apskel-pos-be/internal/models"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type UserProcessorImpl struct {
|
|
userRepo UserRepository
|
|
organizationRepo OrganizationRepository
|
|
outletRepo OutletRepository
|
|
}
|
|
|
|
func NewUserProcessor(
|
|
userRepo UserRepository,
|
|
organizationRepo OrganizationRepository,
|
|
outletRepo OutletRepository,
|
|
) *UserProcessorImpl {
|
|
return &UserProcessorImpl{
|
|
userRepo: userRepo,
|
|
organizationRepo: organizationRepo,
|
|
outletRepo: outletRepo,
|
|
}
|
|
}
|
|
|
|
func (p *UserProcessorImpl) CreateUser(ctx context.Context, req *models.CreateUserRequest) (*models.UserResponse, error) {
|
|
_, err := p.organizationRepo.GetByID(ctx, req.OrganizationID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("organization not found: %w", err)
|
|
}
|
|
|
|
if req.OutletID != nil {
|
|
_, err := p.outletRepo.GetByID(ctx, *req.OutletID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("outlet not found: %w", err)
|
|
}
|
|
}
|
|
|
|
existingUser, err := p.userRepo.GetByEmail(ctx, req.Email)
|
|
if err == nil && existingUser != nil {
|
|
return nil, fmt.Errorf("user with email %s already exists", req.Email)
|
|
}
|
|
|
|
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to hash password: %w", err)
|
|
}
|
|
|
|
userEntity := mappers.UserCreateRequestToEntity(req, string(passwordHash))
|
|
|
|
err = p.userRepo.Create(ctx, userEntity)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create user: %w", err)
|
|
}
|
|
|
|
return mappers.UserEntityToResponse(userEntity), nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) UpdateUser(ctx context.Context, id uuid.UUID, req *models.UpdateUserRequest) (*models.UserResponse, error) {
|
|
existingUser, err := p.userRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
if req.Email != nil && *req.Email != existingUser.Email {
|
|
existingUserByEmail, err := p.userRepo.GetByEmail(ctx, *req.Email)
|
|
if err == nil && existingUserByEmail != nil && existingUserByEmail.ID != id {
|
|
return nil, fmt.Errorf("user with email %s already exists", *req.Email)
|
|
}
|
|
}
|
|
|
|
if req.Name != nil {
|
|
existingUser.Name = *req.Name
|
|
}
|
|
if req.Email != nil {
|
|
existingUser.Email = *req.Email
|
|
}
|
|
if req.Role != nil {
|
|
existingUser.Role = entities.UserRole(*req.Role)
|
|
}
|
|
if req.OutletID != nil {
|
|
existingUser.OutletID = req.OutletID
|
|
}
|
|
if req.IsActive != nil {
|
|
existingUser.IsActive = *req.IsActive
|
|
}
|
|
if req.Permissions != nil {
|
|
existingUser.Permissions = entities.Permissions(*req.Permissions)
|
|
}
|
|
|
|
err = p.userRepo.Update(ctx, existingUser)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to update user: %w", err)
|
|
}
|
|
|
|
return mappers.UserEntityToResponse(existingUser), nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) DeleteUser(ctx context.Context, id uuid.UUID) error {
|
|
_, err := p.userRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
err = p.userRepo.Delete(ctx, id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete user: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) GetUserByID(ctx context.Context, id uuid.UUID) (*models.UserResponse, error) {
|
|
user, err := p.userRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
return mappers.UserEntityToResponse(user), nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) GetUserByEmail(ctx context.Context, email string) (*models.UserResponse, error) {
|
|
user, err := p.userRepo.GetByEmail(ctx, email)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
return mappers.UserEntityToResponse(user), nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) ListUsers(ctx context.Context, organizationID uuid.UUID, page, limit int) ([]models.UserResponse, int, error) {
|
|
_, err := p.organizationRepo.GetByID(ctx, organizationID)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("organization not found: %w", err)
|
|
}
|
|
|
|
offset := (page - 1) * limit
|
|
|
|
filters := map[string]interface{}{
|
|
"organization_id": organizationID,
|
|
}
|
|
|
|
users, totalCount, err := p.userRepo.List(ctx, filters, limit, offset)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("failed to get users: %w", err)
|
|
}
|
|
|
|
responses := make([]models.UserResponse, len(users))
|
|
for i, user := range users {
|
|
response := mappers.UserEntityToResponse(user)
|
|
if response != nil {
|
|
responses[i] = *response
|
|
}
|
|
}
|
|
|
|
return responses, int(totalCount), nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) GetUserEntityByEmail(ctx context.Context, email string) (*entities.User, error) {
|
|
user, err := p.userRepo.GetByEmail(ctx, email)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) ChangePassword(ctx context.Context, userID uuid.UUID, req *models.ChangePasswordRequest) error {
|
|
user, err := p.userRepo.GetByID(ctx, userID)
|
|
if err != nil {
|
|
return fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.CurrentPassword))
|
|
if err != nil {
|
|
return fmt.Errorf("current password is incorrect")
|
|
}
|
|
|
|
newPasswordHash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to hash new password: %w", err)
|
|
}
|
|
|
|
err = p.userRepo.UpdatePassword(ctx, userID, string(newPasswordHash))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update password: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) ActivateUser(ctx context.Context, userID uuid.UUID) error {
|
|
_, err := p.userRepo.GetByID(ctx, userID)
|
|
if err != nil {
|
|
return fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
err = p.userRepo.UpdateActiveStatus(ctx, userID, true)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to activate user: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) DeactivateUser(ctx context.Context, userID uuid.UUID) error {
|
|
_, err := p.userRepo.GetByID(ctx, userID)
|
|
if err != nil {
|
|
return fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
err = p.userRepo.UpdateActiveStatus(ctx, userID, false)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to deactivate user: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *UserProcessorImpl) UpdateUserOutlet(ctx context.Context, userID uuid.UUID, req *models.UpdateUserOutletRequest) (*models.UserResponse, error) {
|
|
// Get user first to validate existence and get organization_id
|
|
existingUser, err := p.userRepo.GetByID(ctx, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user not found: %w", err)
|
|
}
|
|
|
|
// Validate outlet exists
|
|
outlet, err := p.outletRepo.GetByID(ctx, req.OutletID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("outlet not found: %w", err)
|
|
}
|
|
|
|
// Validate outlet belongs to user's organization
|
|
if outlet.OrganizationID != existingUser.OrganizationID {
|
|
return nil, fmt.Errorf("outlet does not belong to user's organization")
|
|
}
|
|
|
|
// Update user's outlet_id
|
|
existingUser.OutletID = &req.OutletID
|
|
|
|
err = p.userRepo.Update(ctx, existingUser)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to update user outlet: %w", err)
|
|
}
|
|
|
|
return mappers.UserEntityToResponse(existingUser), nil
|
|
}
|