Стать исполнителем новая версия
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
SET search_path TO identity;
|
||||||
|
SELECT "WorkingDays" FROM "WorkSchedules" LIMIT 3;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
SET search_path TO "identity";
|
||||||
|
SELECT * FROM "WorkSchedules";
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Nashel.Modules.Identity.Domain.Repositories;
|
||||||
|
using Nashel.Modules.Identity.Domain.Services;
|
||||||
|
|
||||||
|
namespace Nashel.Modules.Identity.Application.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Команда смены пароля.
|
||||||
|
/// </summary>
|
||||||
|
public record ChangePasswordCommand : IRequest<bool>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID аккаунта.
|
||||||
|
/// </summary>
|
||||||
|
public Guid AccountId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Текущий пароль.
|
||||||
|
/// </summary>
|
||||||
|
public string OldPassword { get; init; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Новый пароль.
|
||||||
|
/// </summary>
|
||||||
|
public string NewPassword { get; init; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangePasswordCommandHandler : IRequestHandler<ChangePasswordCommand, bool>
|
||||||
|
{
|
||||||
|
private readonly IAccountRepository _accountRepository;
|
||||||
|
private readonly IPasswordHasher _passwordHasher;
|
||||||
|
|
||||||
|
public ChangePasswordCommandHandler(IAccountRepository accountRepository, IPasswordHasher passwordHasher)
|
||||||
|
{
|
||||||
|
_accountRepository = accountRepository;
|
||||||
|
_passwordHasher = passwordHasher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Handle(ChangePasswordCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var account = await _accountRepository.GetByIdAsync(request.AccountId, cancellationToken);
|
||||||
|
|
||||||
|
if (account == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Аккаунт не найден");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем старый пароль
|
||||||
|
if (!_passwordHasher.VerifyPassword(request.OldPassword, account.PasswordHash))
|
||||||
|
{
|
||||||
|
throw new Exception("Неверный текущий пароль");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что новый пароль отличается от старого
|
||||||
|
if (_passwordHasher.VerifyPassword(request.NewPassword, account.PasswordHash))
|
||||||
|
{
|
||||||
|
throw new Exception("Новый пароль должен отличаться от текущего");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Хешируем и устанавливаем новый пароль
|
||||||
|
var newPasswordHash = _passwordHasher.HashPassword(request.NewPassword);
|
||||||
|
account.ChangePassword(newPasswordHash);
|
||||||
|
|
||||||
|
await _accountRepository.UpdateAsync(account, cancellationToken);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,23 +70,59 @@ public class RegisterUserCommandHandler : IRequestHandler<RegisterUserCommand, G
|
|||||||
|
|
||||||
public async Task<Guid> Handle(RegisterUserCommand request, CancellationToken cancellationToken)
|
public async Task<Guid> Handle(RegisterUserCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Проверка существования пользователя
|
// Проверяем, есть ли аккаунт с таким телефоном (включая удалённые)
|
||||||
if (await _accountRepository.GetByPhoneAsync(request.Phone, cancellationToken) != null)
|
var existingAccount = await _accountRepository.GetByPhoneIncludingDeletedAsync(request.Phone, cancellationToken);
|
||||||
|
|
||||||
|
if (existingAccount != null)
|
||||||
{
|
{
|
||||||
throw new Exception("Пользователь уже существует");
|
if (!existingAccount.IsDeleted)
|
||||||
|
{
|
||||||
|
// Аккаунт существует и активен - ошибка
|
||||||
|
throw new Exception("Пользователь с таким номером телефона уже существует");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Аккаунт удалён - восстанавливаем его с новыми данными
|
||||||
|
var passwordHash = _passwordHasher.HashPassword(request.Password);
|
||||||
|
|
||||||
|
// Очищаем старые данные профиля
|
||||||
|
var profile = UserProfile.Create(
|
||||||
|
existingAccount.Id,
|
||||||
|
request.FirstName,
|
||||||
|
request.LastName,
|
||||||
|
request.Patronymic,
|
||||||
|
request.CompanyName,
|
||||||
|
request.Inn,
|
||||||
|
request.Description);
|
||||||
|
|
||||||
|
existingAccount.Roles.Clear();
|
||||||
|
existingAccount.Roles.Add(Role.User);
|
||||||
|
|
||||||
|
// Добавляем роль только для Company
|
||||||
|
if (request.Role == Role.Company)
|
||||||
|
{
|
||||||
|
existingAccount.Roles.Add(request.Role);
|
||||||
|
}
|
||||||
|
|
||||||
|
existingAccount.SetProfile(profile);
|
||||||
|
existingAccount.ChangePassword(passwordHash);
|
||||||
|
existingAccount.Restore();
|
||||||
|
|
||||||
|
await _accountRepository.UpdateAsync(existingAccount, cancellationToken);
|
||||||
|
|
||||||
|
return existingAccount.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
var passwordHash = _passwordHasher.HashPassword(request.Password);
|
// Создаём новый аккаунт
|
||||||
var account = Account.Create(request.Phone, passwordHash);
|
var passwordHashNew = _passwordHasher.HashPassword(request.Password);
|
||||||
|
var account = Account.Create(request.Phone, passwordHashNew);
|
||||||
|
|
||||||
// Добавляем роль только для Company. Newbie не добавляем - она добавляется через BecomePerformerCommand
|
// Добавляем роль только для Company. Newbie не добавляем - она добавляется через BecomePerformerCommand
|
||||||
if (request.Role == Role.Company)
|
if (request.Role == Role.Company)
|
||||||
{
|
{
|
||||||
account.Roles.Add(request.Role);
|
account.Roles.Add(request.Role);
|
||||||
}
|
}
|
||||||
// Для Newbie роль не добавляем - пользователь остаётся User до заполнения формы становления исполнителем
|
|
||||||
|
|
||||||
var profile = UserProfile.Create(
|
var newProfile = UserProfile.Create(
|
||||||
account.Id,
|
account.Id,
|
||||||
request.FirstName,
|
request.FirstName,
|
||||||
request.LastName,
|
request.LastName,
|
||||||
@@ -95,10 +131,9 @@ public class RegisterUserCommandHandler : IRequestHandler<RegisterUserCommand, G
|
|||||||
request.Inn,
|
request.Inn,
|
||||||
request.Description);
|
request.Description);
|
||||||
|
|
||||||
account.SetProfile(profile);
|
account.SetProfile(newProfile);
|
||||||
|
|
||||||
await _accountRepository.AddAsync(account, cancellationToken);
|
await _accountRepository.AddAsync(account, cancellationToken);
|
||||||
// Предполагаем, что AccountRepository.AddAsync сохранит всё дерево сущностей
|
|
||||||
|
|
||||||
return account.Id;
|
return account.Id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Nashel.Modules.Identity.Domain.Repositories;
|
||||||
|
|
||||||
|
namespace Nashel.Modules.Identity.Application.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Команда обновления локации пользователя.
|
||||||
|
/// </summary>
|
||||||
|
public record UpdateLocationCommand : IRequest<bool>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID аккаунта.
|
||||||
|
/// </summary>
|
||||||
|
public Guid AccountId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Локация (город, район).
|
||||||
|
/// </summary>
|
||||||
|
public string? Location { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Текущая геолокация.
|
||||||
|
/// </summary>
|
||||||
|
public string? CurrentLocation { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateLocationCommandHandler : IRequestHandler<UpdateLocationCommand, bool>
|
||||||
|
{
|
||||||
|
private readonly IAccountRepository _accountRepository;
|
||||||
|
|
||||||
|
public UpdateLocationCommandHandler(IAccountRepository accountRepository)
|
||||||
|
{
|
||||||
|
_accountRepository = accountRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Handle(UpdateLocationCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var account = await _accountRepository.GetByIdAsync(request.AccountId, cancellationToken);
|
||||||
|
|
||||||
|
if (account == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Аккаунт не найден");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.Profile == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Профиль не найден");
|
||||||
|
}
|
||||||
|
|
||||||
|
account.UpdateLocation(request.Location, request.CurrentLocation);
|
||||||
|
await _accountRepository.UpdateAsync(account, cancellationToken);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Nashel.Modules.Identity.Domain.Aggregates;
|
||||||
|
using Nashel.Modules.Identity.Domain.Entities;
|
||||||
|
using Nashel.Modules.Identity.Domain.Repositories;
|
||||||
|
|
||||||
|
namespace Nashel.Modules.Identity.Application.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Команда обновления расписания работы.
|
||||||
|
/// </summary>
|
||||||
|
public record UpdateScheduleCommand : IRequest<bool>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID аккаунта.
|
||||||
|
/// </summary>
|
||||||
|
public Guid AccountId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Всегда на связи (24/7).
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAlwaysReady { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Рабочие дни в формате JSON.
|
||||||
|
/// Пример: [{"day":1,"startTime":"09:00","endTime":"18:00"},{"day":2,"startTime":"09:00","endTime":"18:00"}]
|
||||||
|
/// </summary>
|
||||||
|
public string WorkingDaysJson { get; init; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateScheduleCommandHandler : IRequestHandler<UpdateScheduleCommand, bool>
|
||||||
|
{
|
||||||
|
private readonly IAccountRepository _accountRepository;
|
||||||
|
|
||||||
|
public UpdateScheduleCommandHandler(IAccountRepository accountRepository)
|
||||||
|
{
|
||||||
|
_accountRepository = accountRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Handle(UpdateScheduleCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var account = await _accountRepository.GetByIdAsync(request.AccountId, cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
|
if (account == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Аккаунт не найден");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.Profile == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Профиль не найден");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рабочие дни передаем как строку JSON или null
|
||||||
|
var workingDays = request.IsAlwaysReady ? null : request.WorkingDaysJson;
|
||||||
|
|
||||||
|
var workSchedule = WorkSchedule.Create(request.IsAlwaysReady, workingDays);
|
||||||
|
account.UpdateSchedule(workSchedule);
|
||||||
|
|
||||||
|
await _accountRepository.UpdateAsync(account, cancellationToken);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Nashel.BuildingBlocks.Application.Abstractions;
|
using Nashel.BuildingBlocks.Application.Abstractions;
|
||||||
using Nashel.Modules.Identity.Domain.Repositories;
|
using Nashel.Modules.Identity.Domain.Repositories;
|
||||||
@@ -11,7 +13,10 @@ public record WorkScheduleResponse(
|
|||||||
Dictionary<string, List<TimePeriod>>? SchedulePerDay
|
Dictionary<string, List<TimePeriod>>? SchedulePerDay
|
||||||
);
|
);
|
||||||
|
|
||||||
public record TimePeriod(string Start, string End);
|
public record TimePeriod(
|
||||||
|
[property: JsonPropertyName("start")] string Start,
|
||||||
|
[property: JsonPropertyName("end")] string End
|
||||||
|
);
|
||||||
|
|
||||||
public record ProfileResponse(
|
public record ProfileResponse(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
@@ -68,7 +73,11 @@ public class GetProfileQueryHandler : IRequestHandler<GetProfileQuery, ProfileRe
|
|||||||
Dictionary<string, List<TimePeriod>>? schedulePerDay = null;
|
Dictionary<string, List<TimePeriod>>? schedulePerDay = null;
|
||||||
if (account.Profile.WorkSchedule.WorkingDays != null)
|
if (account.Profile.WorkSchedule.WorkingDays != null)
|
||||||
{
|
{
|
||||||
schedulePerDay = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, List<TimePeriod>>>(account.Profile.WorkSchedule.WorkingDays);
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
schedulePerDay = JsonSerializer.Deserialize<Dictionary<string, List<TimePeriod>>>(account.Profile.WorkSchedule.WorkingDays, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
workScheduleResponse = new WorkScheduleResponse(
|
workScheduleResponse = new WorkScheduleResponse(
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ public class Account : AggregateRoot<Guid>
|
|||||||
public List<Role> Roles { get; private set; } = new();
|
public List<Role> Roles { get; private set; } = new();
|
||||||
public UserProfile Profile { get; private set; } = default!;
|
public UserProfile Profile { get; private set; } = default!;
|
||||||
|
|
||||||
|
// Soft Delete
|
||||||
|
public bool IsDeleted { get; private set; }
|
||||||
|
public DateTime? DeletedAt { get; private set; }
|
||||||
|
|
||||||
// Конструктор для EF Core
|
// Конструктор для EF Core
|
||||||
private Account() { }
|
private Account() { }
|
||||||
|
|
||||||
@@ -21,6 +25,7 @@ public class Account : AggregateRoot<Guid>
|
|||||||
Phone = phone;
|
Phone = phone;
|
||||||
PasswordHash = passwordHash;
|
PasswordHash = passwordHash;
|
||||||
Roles.Add(Role.User);
|
Roles.Add(Role.User);
|
||||||
|
IsDeleted = false;
|
||||||
AddDomainEvent(new AccountCreatedEvent(Id));
|
AddDomainEvent(new AccountCreatedEvent(Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +34,41 @@ public class Account : AggregateRoot<Guid>
|
|||||||
return new Account(Guid.NewGuid(), phone, passwordHash);
|
return new Account(Guid.NewGuid(), phone, passwordHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SoftDelete()
|
||||||
|
{
|
||||||
|
IsDeleted = true;
|
||||||
|
DeletedAt = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Restore()
|
||||||
|
{
|
||||||
|
IsDeleted = false;
|
||||||
|
DeletedAt = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChangePassword(string newPasswordHash)
|
||||||
|
{
|
||||||
|
PasswordHash = newPasswordHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLocation(string? location, string? currentLocation)
|
||||||
|
{
|
||||||
|
if (Profile == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Profile not found");
|
||||||
|
}
|
||||||
|
Profile.UpdateLocation(location, currentLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateSchedule(WorkSchedule workSchedule)
|
||||||
|
{
|
||||||
|
if (Profile == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Profile not found");
|
||||||
|
}
|
||||||
|
Profile.SetWorkSchedule(workSchedule);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetProfile(UserProfile profile)
|
public void SetProfile(UserProfile profile)
|
||||||
{
|
{
|
||||||
Profile = profile;
|
Profile = profile;
|
||||||
|
|||||||
@@ -105,4 +105,10 @@ public class UserProfile : Entity<Guid>
|
|||||||
{
|
{
|
||||||
WorkSchedule = workSchedule;
|
WorkSchedule = workSchedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateLocation(string? location, string? currentLocation)
|
||||||
|
{
|
||||||
|
Location = location;
|
||||||
|
CurrentLocation = currentLocation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public enum Role
|
|||||||
{
|
{
|
||||||
User = 0,
|
User = 0,
|
||||||
Newbie,
|
Newbie,
|
||||||
|
Candidate,
|
||||||
Master,
|
Master,
|
||||||
Company,
|
Company,
|
||||||
Admin
|
Admin
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ public interface IAccountRepository
|
|||||||
Task<Account?> GetByIdAsync(Guid id, CancellationToken cancellationToken);
|
Task<Account?> GetByIdAsync(Guid id, CancellationToken cancellationToken);
|
||||||
Task AddAsync(Account account, CancellationToken cancellationToken);
|
Task AddAsync(Account account, CancellationToken cancellationToken);
|
||||||
Task UpdateAsync(Account account, CancellationToken cancellationToken);
|
Task UpdateAsync(Account account, CancellationToken cancellationToken);
|
||||||
|
Task<Account?> GetByPhoneIncludingDeletedAsync(string phone, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -37,5 +37,16 @@ public class AccountConfiguration : IEntityTypeConfiguration<Account>
|
|||||||
.WithOne()
|
.WithOne()
|
||||||
.HasForeignKey<UserProfile>(p => p.Id)
|
.HasForeignKey<UserProfile>(p => p.Id)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
// Soft Delete
|
||||||
|
builder.Property(x => x.IsDeleted)
|
||||||
|
.IsRequired()
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
|
builder.Property(x => x.DeletedAt)
|
||||||
|
.IsRequired(false);
|
||||||
|
|
||||||
|
// Фильтр для soft delete
|
||||||
|
builder.HasQueryFilter(x => !x.IsDeleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ public class AccountRepository : IAccountRepository
|
|||||||
.FirstOrDefaultAsync(a => a.Phone == phone, cancellationToken);
|
.FirstOrDefaultAsync(a => a.Phone == phone, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Account?> GetByPhoneIncludingDeletedAsync(string phone, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await _context.Accounts
|
||||||
|
.IgnoreQueryFilters()
|
||||||
|
.Include(a => a.Profile)
|
||||||
|
.FirstOrDefaultAsync(a => a.Phone == phone, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync(Account account, CancellationToken cancellationToken)
|
public async Task UpdateAsync(Account account, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[AccountRepository.UpdateAsync] Starting update for account: {account.Id}");
|
Console.WriteLine($"[AccountRepository.UpdateAsync] Starting update for account: {account.Id}");
|
||||||
|
|||||||
@@ -106,6 +106,48 @@ public static class IdentityEndpoints
|
|||||||
})
|
})
|
||||||
.WithName("SearchCompetencies")
|
.WithName("SearchCompetencies")
|
||||||
.WithOpenApi(operation => new(operation) { Summary = "Поиск компетенций" });
|
.WithOpenApi(operation => new(operation) { Summary = "Поиск компетенций" });
|
||||||
|
|
||||||
|
// Эндпоинт для смены пароля
|
||||||
|
profileGroup.MapPost("/change-password", async (ChangePasswordRequest request, ISender sender) =>
|
||||||
|
{
|
||||||
|
await sender.Send(new ChangePasswordCommand
|
||||||
|
{
|
||||||
|
AccountId = request.AccountId,
|
||||||
|
OldPassword = request.OldPassword,
|
||||||
|
NewPassword = request.NewPassword
|
||||||
|
});
|
||||||
|
return Results.Ok(new { success = true, message = "Пароль успешно изменён" });
|
||||||
|
})
|
||||||
|
.WithName("ChangePassword")
|
||||||
|
.WithOpenApi(operation => new(operation) { Summary = "Сменить пароль" });
|
||||||
|
|
||||||
|
// Эндпоинт для обновления локации
|
||||||
|
profileGroup.MapPut("/location", async (UpdateLocationRequest request, ISender sender) =>
|
||||||
|
{
|
||||||
|
await sender.Send(new UpdateLocationCommand
|
||||||
|
{
|
||||||
|
AccountId = request.AccountId,
|
||||||
|
Location = request.Location,
|
||||||
|
CurrentLocation = request.CurrentLocation
|
||||||
|
});
|
||||||
|
return Results.NoContent();
|
||||||
|
})
|
||||||
|
.WithName("UpdateLocation")
|
||||||
|
.WithOpenApi(operation => new(operation) { Summary = "Обновить локацию" });
|
||||||
|
|
||||||
|
// Эндпоинт для обновления расписания
|
||||||
|
profileGroup.MapPut("/schedule", async (UpdateScheduleRequest request, ISender sender) =>
|
||||||
|
{
|
||||||
|
await sender.Send(new UpdateScheduleCommand
|
||||||
|
{
|
||||||
|
AccountId = request.AccountId,
|
||||||
|
IsAlwaysReady = request.IsAlwaysReady,
|
||||||
|
WorkingDaysJson = request.WorkingDaysJson ?? "[]"
|
||||||
|
});
|
||||||
|
return Results.NoContent();
|
||||||
|
})
|
||||||
|
.WithName("UpdateSchedule")
|
||||||
|
.WithOpenApi(operation => new(operation) { Summary = "Обновить расписание" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,3 +161,21 @@ public record BecomePerformerRequest(
|
|||||||
bool IsAlwaysReady,
|
bool IsAlwaysReady,
|
||||||
string? WorkingDays
|
string? WorkingDays
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public record ChangePasswordRequest(
|
||||||
|
Guid AccountId,
|
||||||
|
string OldPassword,
|
||||||
|
string NewPassword
|
||||||
|
);
|
||||||
|
|
||||||
|
public record UpdateLocationRequest(
|
||||||
|
Guid AccountId,
|
||||||
|
string? Location,
|
||||||
|
string? CurrentLocation
|
||||||
|
);
|
||||||
|
|
||||||
|
public record UpdateScheduleRequest(
|
||||||
|
Guid AccountId,
|
||||||
|
bool IsAlwaysReady,
|
||||||
|
string? WorkingDaysJson
|
||||||
|
);
|
||||||
|
|||||||
@@ -14,13 +14,18 @@ public class BecomePerformerCommandHandlerTests
|
|||||||
{
|
{
|
||||||
private readonly Mock<IAccountRepository> _mockRepo;
|
private readonly Mock<IAccountRepository> _mockRepo;
|
||||||
private readonly Mock<ICurrentUserService> _mockUserService;
|
private readonly Mock<ICurrentUserService> _mockUserService;
|
||||||
|
private readonly Mock<ICompetencyRepository> _mockCompetencyRepo;
|
||||||
private readonly BecomePerformerCommandHandler _handler;
|
private readonly BecomePerformerCommandHandler _handler;
|
||||||
|
|
||||||
public BecomePerformerCommandHandlerTests()
|
public BecomePerformerCommandHandlerTests()
|
||||||
{
|
{
|
||||||
_mockRepo = new Mock<IAccountRepository>();
|
_mockRepo = new Mock<IAccountRepository>();
|
||||||
_mockUserService = new Mock<ICurrentUserService>();
|
_mockUserService = new Mock<ICurrentUserService>();
|
||||||
_handler = new BecomePerformerCommandHandler(_mockRepo.Object, _mockUserService.Object);
|
_mockCompetencyRepo = new Mock<ICompetencyRepository>();
|
||||||
|
_handler = new BecomePerformerCommandHandler(
|
||||||
|
_mockRepo.Object,
|
||||||
|
_mockUserService.Object,
|
||||||
|
_mockCompetencyRepo.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
Reference in New Issue
Block a user