From f12621f180d55ea4b991b019baf970fc2308f3ce Mon Sep 17 00:00:00 2001 From: Nguyen Huynh Anh Tai <144357738+NguyenHuynhAnhTai@users.noreply.github.com> Date: Thu, 1 May 2025 18:02:38 +0700 Subject: [PATCH] fix: enhance ApproveInspectionSchedule logic and improve CreateStaff user existence checks --- .../Commands/ApproveInspectionSchedule.cs | 17 +++++ src/UseCases/UC_User/Commands/CreateStaff.cs | 37 ++++++--- .../UC_User/Commands/CreateStaffTest.cs | 75 +++++++++++++++++-- 3 files changed, 112 insertions(+), 17 deletions(-) diff --git a/src/UseCases/UC_InspectionSchedule/Commands/ApproveInspectionSchedule.cs b/src/UseCases/UC_InspectionSchedule/Commands/ApproveInspectionSchedule.cs index e5d595bf..4108d34c 100644 --- a/src/UseCases/UC_InspectionSchedule/Commands/ApproveInspectionSchedule.cs +++ b/src/UseCases/UC_InspectionSchedule/Commands/ApproveInspectionSchedule.cs @@ -50,6 +50,8 @@ CancellationToken cancellationToken .ThenInclude(s => s.Model) .Include(s => s.Car) .ThenInclude(s => s.GPS) + .Include(s => s.Car) + .ThenInclude(s => s.Contract) .Include(s => s.CarReport) .Include(s => s.Technician) .Include(s => s.Photos) @@ -162,6 +164,21 @@ CancellationToken cancellationToken ? InspectionScheduleStatusEnum.Approved : InspectionScheduleStatusEnum.Rejected; schedule.UpdatedAt = DateTimeOffset.UtcNow; + + // reset contract signature if schedule is not approved + if (!request.IsApproved && schedule.Type == InspectionScheduleType.NewCar) + { + var contract = schedule.Car?.Contract; + if (contract != null) + { + contract.OwnerSignature = null; + contract.OwnerSignatureDate = null; + contract.TechnicianSignature = null; + contract.TechnicianSignatureDate = null; + contract.Status = CarContractStatusEnum.Pending; + contract.UpdatedAt = DateTimeOffset.UtcNow; + } + } // Set Car Status into available if (request.IsApproved) { diff --git a/src/UseCases/UC_User/Commands/CreateStaff.cs b/src/UseCases/UC_User/Commands/CreateStaff.cs index e55b966e..c8a3713e 100644 --- a/src/UseCases/UC_User/Commands/CreateStaff.cs +++ b/src/UseCases/UC_User/Commands/CreateStaff.cs @@ -46,19 +46,34 @@ CancellationToken cancellationToken if (!new[] { "consultant", "technician" }.Contains(request.RoleName.ToLower())) return Result.Error(ResponseMessages.MustBeConsultantOrTechnician); - // Check if user already exists - User? existingUser = await context.Users.FirstOrDefaultAsync( - x => - EF.Functions.ILike(x.Email, request.Email) - || EF.Functions.ILike(x.Phone, request.Phone), - cancellationToken - ); + // Check if email or phone already exists + List users = await context + .Users.AsNoTracking() + .Include(u => u.EncryptionKey) + .ToListAsync(cancellationToken); - if (existingUser is not null) + foreach (var u in users) { - return existingUser.Email.ToLower() == request.Email.ToLower() - ? Result.Error(ResponseMessages.EmailAddressIsExisted) - : Result.Error(ResponseMessages.PhoneNumberIsExisted); + string decryptedKey = keyManagementService.DecryptKey( + u.EncryptionKey.EncryptedKey, + encryptionSettings.Key + ); + + string decryptedPhone = await aesEncryptionService.Decrypt( + u.Phone, + decryptedKey, + u.EncryptionKey.IV + ); + + if (u.Email == request.Email) + { + return Result.Error(ResponseMessages.EmailAddressIsExisted); + } + + if (decryptedPhone == request.Phone) + { + return Result.Error(ResponseMessages.PhoneNumberIsExisted); + } } // Get role diff --git a/test/UseCases.UnitTests/UC_User/Commands/CreateStaffTest.cs b/test/UseCases.UnitTests/UC_User/Commands/CreateStaffTest.cs index a810b04f..4427c9ef 100644 --- a/test/UseCases.UnitTests/UC_User/Commands/CreateStaffTest.cs +++ b/test/UseCases.UnitTests/UC_User/Commands/CreateStaffTest.cs @@ -1,5 +1,6 @@ using Ardalis.Result; using Domain.Constants; +using Domain.Entities; using Domain.Shared; using Infrastructure.Encryption; using Microsoft.EntityFrameworkCore; @@ -9,6 +10,8 @@ using UseCases.UC_User.Commands; using UseCases.UnitTests.TestBases; using UseCases.UnitTests.TestBases.TestData; +using UseCases.Utils; +using UUIDNext; namespace UseCases.UnitTests.UC_User.Commands; @@ -41,7 +44,12 @@ public async Task Handle_UserNotAdmin_ReturnsForbidden() { // Arrange var driverRole = await TestDataCreateUserRole.CreateTestUserRole(_dbContext, "Driver"); - var user = await TestDataCreateUser.CreateTestUser(_dbContext, driverRole); + var user = await CreateUserWithEncryptedPhone( + driverRole, + "driver@test.com", + "Test Driver", + "1234567890" + ); _currentUser.SetUser(user); var handler = new CreateStaff.Handler( @@ -82,7 +90,12 @@ string name // Arrange var adminRole = await TestDataCreateUserRole.CreateTestUserRole(_dbContext, "Admin"); var staffRole = await TestDataCreateUserRole.CreateTestUserRole(_dbContext, roleName); - var admin = await TestDataCreateUser.CreateTestUser(_dbContext, adminRole); + var admin = await CreateUserWithEncryptedPhone( + adminRole, + "admin@test.com", + "Admin User", + "9876543210" + ); _currentUser.SetUser(admin); var handler = new CreateStaff.Handler( @@ -128,11 +141,17 @@ public async Task Handle_ExistingEmail_ReturnsError() _dbContext, "Consultant" ); - var admin = await TestDataCreateUser.CreateTestUser(_dbContext, adminRole); - var existingUser = await TestDataCreateUser.CreateTestUser( - _dbContext, + var admin = await CreateUserWithEncryptedPhone( + adminRole, + "admin@test.com", + "Admin User", + "9876543211" + ); + var existingUser = await CreateUserWithEncryptedPhone( consultantRole, - "existing@test.com" + "existing@test.com", + "Existing User", + "5555555555" ); _currentUser.SetUser(admin); @@ -196,4 +215,48 @@ string roleName // Assert Assert.False(result.IsValid); } + + private async Task CreateUserWithEncryptedPhone( + UserRole userRole, + string email = "test@example.com", + string name = "Test User", + string phoneNumber = "1234567890", + string avatarUrl = "http://example.com/avatar.jpg" + ) + { + // Generate encryption key and encrypt phone number + (string key, string iv) = await _keyService.GenerateKeyAsync(); + string encryptedPhone = await _aesService.Encrypt(phoneNumber, key, iv); + string encryptedKey = _keyService.EncryptKey(key, _encryptionSettings.Key); + + // Create encryption key record + var encryptionKey = new EncryptionKey { EncryptedKey = encryptedKey, IV = iv }; + await _dbContext.EncryptionKeys.AddAsync(encryptionKey); + await _dbContext.SaveChangesAsync(); + + // Create user with encrypted phone + var user = new User + { + Id = Uuid.NewDatabaseFriendly(Database.PostgreSql), + EncryptionKeyId = encryptionKey.Id, + Name = name, + Email = email, + AvatarUrl = avatarUrl, + Password = "password".HashString(), + RoleId = userRole.Id, + Address = "Test Address", + DateOfBirth = DateTime.UtcNow.AddYears(-30), + Phone = encryptedPhone, + }; + + // Create user statistics + var userStatistic = new UserStatistic { UserId = user.Id }; + + // Save to database + await _dbContext.Users.AddAsync(user); + await _dbContext.UserStatistics.AddAsync(userStatistic); + await _dbContext.SaveChangesAsync(); + + return user; + } }