Стать исполнителем
This commit is contained in:
@@ -1,4 +1,20 @@
|
|||||||
services:
|
services:
|
||||||
|
migrations:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: nashel-migrations
|
||||||
|
environment:
|
||||||
|
- ASPNETCORE_ENVIRONMENT=Development
|
||||||
|
- ConnectionStrings__DefaultConnection=Host=db;Port=5432;Database=nashel;Username=postgres;Password=postgres
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
command: >
|
||||||
|
sh -c "cd /app/src/Host && dotnet ef database update --project ../Modules/Identity/Infrastructure --startup-project . --context IdentityDbContext"
|
||||||
|
networks:
|
||||||
|
- nashel-network
|
||||||
|
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -14,6 +30,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
migrations:
|
||||||
|
condition: service_completed_successfully
|
||||||
networks:
|
networks:
|
||||||
- nashel-network
|
- nashel-network
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,31 @@ builder.Services.AddCors(options =>
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Middleware для обработки исключений
|
||||||
|
app.UseExceptionHandler(errorApp =>
|
||||||
|
{
|
||||||
|
errorApp.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 500;
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
var exception = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>();
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
var error = new
|
||||||
|
{
|
||||||
|
Message = exception.Error.Message,
|
||||||
|
StackTrace = exception.Error.StackTrace
|
||||||
|
};
|
||||||
|
|
||||||
|
Console.WriteLine($"[ERROR] {exception.Error.Message}");
|
||||||
|
Console.WriteLine(exception.Error.StackTrace);
|
||||||
|
|
||||||
|
await context.Response.WriteAsJsonAsync(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Настройка конвейера HTTP-запросов.
|
// Настройка конвейера HTTP-запросов.
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": false,
|
||||||
"applicationUrl": "http://localhost:5232",
|
"applicationUrl": "http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,80 +9,87 @@ public record BecomePerformerCommand : IRequest<Unit>
|
|||||||
{
|
{
|
||||||
public string Description { get; init; } = default!;
|
public string Description { get; init; } = default!;
|
||||||
public List<string> CompetencyNames { get; init; } = default!;
|
public List<string> CompetencyNames { get; init; } = default!;
|
||||||
public bool Is24_7 { get; init; }
|
public string? Location { get; init; }
|
||||||
|
public string? CurrentLocation { get; init; }
|
||||||
public bool IsAlwaysReady { get; init; }
|
public bool IsAlwaysReady { get; init; }
|
||||||
public List<string>? WorkingDays { get; init; }
|
public string? WorkingDays { get; init; }
|
||||||
public string? WorkingHoursStart { get; init; }
|
|
||||||
public string? WorkingHoursEnd { get; init; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BecomePerformerCommandHandler : IRequestHandler<BecomePerformerCommand, Unit>
|
public class BecomePerformerCommandHandler : IRequestHandler<BecomePerformerCommand, Unit>
|
||||||
{
|
{
|
||||||
private readonly IAccountRepository _accountRepository;
|
private readonly IAccountRepository _accountRepository;
|
||||||
private readonly ICurrentUserService _currentUserService;
|
private readonly ICurrentUserService _currentUserService;
|
||||||
|
private readonly ICompetencyRepository _competencyRepository;
|
||||||
|
|
||||||
public BecomePerformerCommandHandler(IAccountRepository accountRepository, ICurrentUserService currentUserService)
|
public BecomePerformerCommandHandler(
|
||||||
|
IAccountRepository accountRepository,
|
||||||
|
ICurrentUserService currentUserService,
|
||||||
|
ICompetencyRepository competencyRepository)
|
||||||
{
|
{
|
||||||
_accountRepository = accountRepository;
|
_accountRepository = accountRepository;
|
||||||
_currentUserService = currentUserService;
|
_currentUserService = currentUserService;
|
||||||
|
_competencyRepository = competencyRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Unit> Handle(BecomePerformerCommand request, CancellationToken cancellationToken)
|
public async Task<Unit> Handle(BecomePerformerCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine($"[BecomePerformerCommand] Starting to process request for user");
|
||||||
|
|
||||||
var userId = _currentUserService.UserId;
|
var userId = _currentUserService.UserId;
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("[BecomePerformerCommand] User is not authorized");
|
||||||
throw new Exception("Неавторизован");
|
throw new Exception("Неавторизован");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"[BecomePerformerCommand] UserId: {userId.Value}");
|
||||||
|
|
||||||
var account = await _accountRepository.GetByIdAsync(userId.Value, cancellationToken);
|
var account = await _accountRepository.GetByIdAsync(userId.Value, cancellationToken);
|
||||||
if (account == null)
|
if (account == null)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine($"[BecomePerformerCommand] Account not found for userId: {userId.Value}");
|
||||||
throw new Exception("Аккаунт не найден");
|
throw new Exception("Аккаунт не найден");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем компетенции
|
Console.WriteLine($"[BecomePerformerCommand] Account found: {account.Id}");
|
||||||
var competencies = request.CompetencyNames
|
|
||||||
.Select(name => Competency.Create(name))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// Создаем график работы
|
// Получаем или создаем компетенции
|
||||||
WorkSchedule? workSchedule = null;
|
var competencies = new List<Competency>();
|
||||||
if (request.Is24_7 || request.IsAlwaysReady || (request.WorkingDays != null && request.WorkingDays.Any()))
|
if (request.CompetencyNames != null)
|
||||||
{
|
{
|
||||||
string? workingDaysJson = null;
|
Console.WriteLine($"[BecomePerformerCommand] Processing {request.CompetencyNames.Count} competencies");
|
||||||
string? workingHoursJson = null;
|
foreach (var name in request.CompetencyNames)
|
||||||
|
|
||||||
if (request.WorkingDays != null && request.WorkingDays.Any())
|
|
||||||
{
|
{
|
||||||
workingDaysJson = System.Text.Json.JsonSerializer.Serialize(request.WorkingDays);
|
var competency = await _competencyRepository.GetByNameAsync(name, cancellationToken);
|
||||||
}
|
if (competency == null)
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.WorkingHoursStart) && !string.IsNullOrEmpty(request.WorkingHoursEnd))
|
|
||||||
{
|
|
||||||
workingHoursJson = System.Text.Json.JsonSerializer.Serialize(new
|
|
||||||
{
|
{
|
||||||
start = request.WorkingHoursStart,
|
Console.WriteLine($"[BecomePerformerCommand] Creating new competency: {name}");
|
||||||
end = request.WorkingHoursEnd
|
competency = Competency.Create(name);
|
||||||
});
|
await _competencyRepository.AddAsync(competency, cancellationToken);
|
||||||
|
}
|
||||||
|
competencies.Add(competency);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkSchedule? workSchedule = null;
|
||||||
|
if (!request.IsAlwaysReady && !string.IsNullOrEmpty(request.WorkingDays))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[BecomePerformerCommand] Creating work schedule");
|
||||||
workSchedule = WorkSchedule.Create(
|
workSchedule = WorkSchedule.Create(
|
||||||
request.Is24_7,
|
|
||||||
request.IsAlwaysReady,
|
request.IsAlwaysReady,
|
||||||
workingDaysJson,
|
request.WorkingDays
|
||||||
workingHoursJson
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем данные исполнителя
|
Console.WriteLine($"[BecomePerformerCommand] Updating performer data");
|
||||||
account.UpdatePerformerData(request.Description, competencies, workSchedule);
|
account.UpdatePerformerData(request.Description, competencies, request.Location, request.CurrentLocation, workSchedule);
|
||||||
|
|
||||||
// Добавляем роль исполнителя
|
|
||||||
account.BecomePerformer();
|
account.BecomePerformer();
|
||||||
|
|
||||||
|
Console.WriteLine($"[BecomePerformerCommand] Saving to database");
|
||||||
await _accountRepository.UpdateAsync(account, cancellationToken);
|
await _accountRepository.UpdateAsync(account, cancellationToken);
|
||||||
|
|
||||||
|
Console.WriteLine($"[BecomePerformerCommand] Successfully completed");
|
||||||
|
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,12 +79,12 @@ public class RegisterUserCommandHandler : IRequestHandler<RegisterUserCommand, G
|
|||||||
var passwordHash = _passwordHasher.HashPassword(request.Password);
|
var passwordHash = _passwordHasher.HashPassword(request.Password);
|
||||||
var account = Account.Create(request.Phone, passwordHash);
|
var account = Account.Create(request.Phone, passwordHash);
|
||||||
|
|
||||||
// Добавляем роль, если она отличается от User
|
// Добавляем роль только для Company. Newbie не добавляем - она добавляется через BecomePerformerCommand
|
||||||
if (request.Role != Role.User && request.Role != Role.Admin)
|
if (request.Role == Role.Company)
|
||||||
{
|
{
|
||||||
// Здесь можно добавить логику проверки прав, но для MVP разрешим
|
|
||||||
account.Roles.Add(request.Role);
|
account.Roles.Add(request.Role);
|
||||||
}
|
}
|
||||||
|
// Для Newbie роль не добавляем - пользователь остаётся User до заполнения формы становления исполнителем
|
||||||
|
|
||||||
var profile = UserProfile.Create(
|
var profile = UserProfile.Create(
|
||||||
account.Id,
|
account.Id,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ public record UpdateProfileCommand : IRequest
|
|||||||
public string? CompanyName { get; init; }
|
public string? CompanyName { get; init; }
|
||||||
public string? Inn { get; init; }
|
public string? Inn { get; init; }
|
||||||
public string? Description { get; init; }
|
public string? Description { get; init; }
|
||||||
|
public string? Location { get; init; }
|
||||||
|
public string? CurrentLocation { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateProfileCommandHandler : IRequestHandler<UpdateProfileCommand>
|
public class UpdateProfileCommandHandler : IRequestHandler<UpdateProfileCommand>
|
||||||
@@ -50,7 +52,9 @@ public class UpdateProfileCommandHandler : IRequestHandler<UpdateProfileCommand>
|
|||||||
request.Patronymic,
|
request.Patronymic,
|
||||||
request.CompanyName,
|
request.CompanyName,
|
||||||
request.Inn,
|
request.Inn,
|
||||||
request.Description);
|
request.Description,
|
||||||
|
request.Location,
|
||||||
|
request.CurrentLocation);
|
||||||
|
|
||||||
await _accountRepository.UpdateAsync(account, cancellationToken);
|
await _accountRepository.UpdateAsync(account, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ namespace Nashel.Modules.Identity.Application.Queries.GetProfile;
|
|||||||
public record GetProfileQuery : IRequest<ProfileResponse>;
|
public record GetProfileQuery : IRequest<ProfileResponse>;
|
||||||
|
|
||||||
public record WorkScheduleResponse(
|
public record WorkScheduleResponse(
|
||||||
bool Is24_7,
|
|
||||||
bool IsAlwaysReady,
|
bool IsAlwaysReady,
|
||||||
List<string>? WorkingDays,
|
Dictionary<string, List<TimePeriod>>? SchedulePerDay
|
||||||
string? WorkingHoursStart,
|
|
||||||
string? WorkingHoursEnd
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public record TimePeriod(string Start, string End);
|
||||||
|
|
||||||
public record ProfileResponse(
|
public record ProfileResponse(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
string Phone,
|
string Phone,
|
||||||
@@ -24,6 +23,8 @@ public record ProfileResponse(
|
|||||||
string? CompanyName,
|
string? CompanyName,
|
||||||
string? Inn,
|
string? Inn,
|
||||||
string? Description,
|
string? Description,
|
||||||
|
string? Location,
|
||||||
|
string? CurrentLocation,
|
||||||
string FullName,
|
string FullName,
|
||||||
string? AvatarUrl,
|
string? AvatarUrl,
|
||||||
List<string> Competencies,
|
List<string> Competencies,
|
||||||
@@ -64,31 +65,15 @@ public class GetProfileQueryHandler : IRequestHandler<GetProfileQuery, ProfileRe
|
|||||||
WorkScheduleResponse? workScheduleResponse = null;
|
WorkScheduleResponse? workScheduleResponse = null;
|
||||||
if (account.Profile.WorkSchedule != null)
|
if (account.Profile.WorkSchedule != null)
|
||||||
{
|
{
|
||||||
var workingDays = account.Profile.WorkSchedule.WorkingDays != null
|
Dictionary<string, List<TimePeriod>>? schedulePerDay = null;
|
||||||
? System.Text.Json.JsonSerializer.Deserialize<List<string>>(account.Profile.WorkSchedule.WorkingDays)
|
if (account.Profile.WorkSchedule.WorkingDays != null)
|
||||||
: null;
|
|
||||||
|
|
||||||
string? workingHoursStart = null;
|
|
||||||
string? workingHoursEnd = null;
|
|
||||||
if (account.Profile.WorkSchedule.WorkingHours != null)
|
|
||||||
{
|
{
|
||||||
var workingHours = System.Text.Json.JsonSerializer.Deserialize<System.Text.Json.JsonElement>(account.Profile.WorkSchedule.WorkingHours);
|
schedulePerDay = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, List<TimePeriod>>>(account.Profile.WorkSchedule.WorkingDays);
|
||||||
if (workingHours.TryGetProperty("start", out var start))
|
|
||||||
{
|
|
||||||
workingHoursStart = start.GetString();
|
|
||||||
}
|
|
||||||
if (workingHours.TryGetProperty("end", out var end))
|
|
||||||
{
|
|
||||||
workingHoursEnd = end.GetString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
workScheduleResponse = new WorkScheduleResponse(
|
workScheduleResponse = new WorkScheduleResponse(
|
||||||
account.Profile.WorkSchedule.Is24_7,
|
|
||||||
account.Profile.WorkSchedule.IsAlwaysReady,
|
account.Profile.WorkSchedule.IsAlwaysReady,
|
||||||
workingDays,
|
schedulePerDay
|
||||||
workingHoursStart,
|
|
||||||
workingHoursEnd
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +87,8 @@ public class GetProfileQueryHandler : IRequestHandler<GetProfileQuery, ProfileRe
|
|||||||
account.Profile.CompanyName,
|
account.Profile.CompanyName,
|
||||||
account.Profile.Inn,
|
account.Profile.Inn,
|
||||||
account.Profile.Description,
|
account.Profile.Description,
|
||||||
|
account.Profile.Location,
|
||||||
|
account.Profile.CurrentLocation,
|
||||||
fullName,
|
fullName,
|
||||||
account.Profile.AvatarUrl,
|
account.Profile.AvatarUrl,
|
||||||
account.Profile.Competencies.Select(c => c.Name).ToList(),
|
account.Profile.Competencies.Select(c => c.Name).ToList(),
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ public class Account : AggregateRoot<Guid>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdatePerformerData(string description, IEnumerable<Competency> competencies, WorkSchedule? workSchedule)
|
public void UpdatePerformerData(string description, IEnumerable<Competency> competencies, string? location, string? currentLocation, WorkSchedule? workSchedule)
|
||||||
{
|
{
|
||||||
Profile.UpdatePerformerData(description, competencies, workSchedule);
|
Profile.UpdatePerformerData(description, competencies, location, currentLocation, workSchedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateWorkSchedule(WorkSchedule workSchedule)
|
public void UpdateWorkSchedule(WorkSchedule workSchedule)
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ public class UserProfile : Entity<Guid>
|
|||||||
public string? Inn { get; private set; }
|
public string? Inn { get; private set; }
|
||||||
public string? Description { get; private set; }
|
public string? Description { get; private set; }
|
||||||
public string? AvatarUrl { get; private set; }
|
public string? AvatarUrl { get; private set; }
|
||||||
|
public string? Location { get; private set; }
|
||||||
|
public string? CurrentLocation { get; private set; }
|
||||||
public IReadOnlyCollection<Competency> Competencies => _competencies.AsReadOnly();
|
public IReadOnlyCollection<Competency> Competencies => _competencies.AsReadOnly();
|
||||||
public WorkSchedule? WorkSchedule { get; private set; }
|
public WorkSchedule? WorkSchedule { get; private set; }
|
||||||
|
|
||||||
@@ -41,7 +43,7 @@ public class UserProfile : Entity<Guid>
|
|||||||
return new UserProfile(id, firstName, lastName, patronymic, companyName, inn, description);
|
return new UserProfile(id, firstName, lastName, patronymic, companyName, inn, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(string firstName, string lastName, string? patronymic, string? companyName, string? inn, string? description)
|
public void Update(string firstName, string lastName, string? patronymic, string? companyName, string? inn, string? description, string? location = null, string? currentLocation = null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(description) && description.Length > MaxDescriptionLength)
|
if (!string.IsNullOrEmpty(description) && description.Length > MaxDescriptionLength)
|
||||||
throw new ArgumentException($"Описание не может превышать {MaxDescriptionLength} символов", nameof(description));
|
throw new ArgumentException($"Описание не может превышать {MaxDescriptionLength} символов", nameof(description));
|
||||||
@@ -52,6 +54,8 @@ public class UserProfile : Entity<Guid>
|
|||||||
CompanyName = companyName;
|
CompanyName = companyName;
|
||||||
Inn = inn;
|
Inn = inn;
|
||||||
Description = description;
|
Description = description;
|
||||||
|
Location = location;
|
||||||
|
CurrentLocation = currentLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateAvatar(string? avatarUrl)
|
public void UpdateAvatar(string? avatarUrl)
|
||||||
@@ -70,7 +74,7 @@ public class UserProfile : Entity<Guid>
|
|||||||
_competencies.AddRange(competencyList);
|
_competencies.AddRange(competencyList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdatePerformerData(string description, IEnumerable<Competency> competencies, WorkSchedule? workSchedule)
|
public void UpdatePerformerData(string description, IEnumerable<Competency> competencies, string? location, string? currentLocation, WorkSchedule? workSchedule)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(description) || description.Length < 50)
|
if (string.IsNullOrWhiteSpace(description) || description.Length < 50)
|
||||||
throw new ArgumentException($"Описание должно содержать минимум 50 символов", nameof(description));
|
throw new ArgumentException($"Описание должно содержать минимум 50 символов", nameof(description));
|
||||||
@@ -79,6 +83,8 @@ public class UserProfile : Entity<Guid>
|
|||||||
throw new ArgumentException($"Описание не может превышать {MaxDescriptionLength} символов", nameof(description));
|
throw new ArgumentException($"Описание не может превышать {MaxDescriptionLength} символов", nameof(description));
|
||||||
|
|
||||||
Description = description;
|
Description = description;
|
||||||
|
Location = location;
|
||||||
|
CurrentLocation = currentLocation;
|
||||||
|
|
||||||
var competencyList = competencies.ToList();
|
var competencyList = competencies.ToList();
|
||||||
if (competencyList.Count > MaxCompetencies)
|
if (competencyList.Count > MaxCompetencies)
|
||||||
@@ -90,6 +96,11 @@ public class UserProfile : Entity<Guid>
|
|||||||
WorkSchedule = workSchedule;
|
WorkSchedule = workSchedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateCurrentLocation(string? currentLocation)
|
||||||
|
{
|
||||||
|
CurrentLocation = currentLocation;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetWorkSchedule(WorkSchedule workSchedule)
|
public void SetWorkSchedule(WorkSchedule workSchedule)
|
||||||
{
|
{
|
||||||
WorkSchedule = workSchedule;
|
WorkSchedule = workSchedule;
|
||||||
|
|||||||
@@ -3,47 +3,39 @@ namespace Nashel.Modules.Identity.Domain.Entities;
|
|||||||
public class WorkSchedule
|
public class WorkSchedule
|
||||||
{
|
{
|
||||||
public Guid Id { get; private set; }
|
public Guid Id { get; private set; }
|
||||||
public bool Is24_7 { get; private set; }
|
|
||||||
public bool IsAlwaysReady { get; private set; }
|
public bool IsAlwaysReady { get; private set; }
|
||||||
public string? WorkingDays { get; private set; } // JSON-массив дней недели: ["Mon", "Tue", ...]
|
public string? WorkingDays { get; private set; } // JSON: {"Понедельник": [{"start": "09:00", "end": "18:00"}], ...}
|
||||||
public string? WorkingHours { get; private set; } // JSON: {"start": "09:00", "end": "18:00"}
|
|
||||||
|
|
||||||
// EF Core constructor
|
// EF Core constructor
|
||||||
private WorkSchedule() { }
|
private WorkSchedule() { }
|
||||||
|
|
||||||
private WorkSchedule(bool is24_7, bool isAlwaysReady, string? workingDays, string? workingHours)
|
private WorkSchedule(bool isAlwaysReady, string? workingDays)
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid();
|
Id = Guid.NewGuid();
|
||||||
Is24_7 = is24_7;
|
|
||||||
IsAlwaysReady = isAlwaysReady;
|
IsAlwaysReady = isAlwaysReady;
|
||||||
WorkingDays = workingDays;
|
WorkingDays = workingDays;
|
||||||
WorkingHours = workingHours;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WorkSchedule Create(bool is24_7, bool isAlwaysReady, string? workingDays, string? workingHours)
|
public static WorkSchedule Create(bool isAlwaysReady, string? workingDays)
|
||||||
{
|
{
|
||||||
// Если 24/7 или Always Ready, то дни и часы не нужны
|
// Если Always Ready, то расписание не нужно
|
||||||
if (is24_7 || isAlwaysReady)
|
if (isAlwaysReady)
|
||||||
{
|
{
|
||||||
workingDays = null;
|
workingDays = null;
|
||||||
workingHours = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WorkSchedule(is24_7, isAlwaysReady, workingDays, workingHours);
|
return new WorkSchedule(isAlwaysReady, workingDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(bool is24_7, bool isAlwaysReady, string? workingDays, string? workingHours)
|
public void Update(bool isAlwaysReady, string? workingDays)
|
||||||
{
|
{
|
||||||
// Если 24/7 или Always Ready, то дни и часы не нужны
|
// Если Always Ready, то расписание не нужно
|
||||||
if (is24_7 || isAlwaysReady)
|
if (isAlwaysReady)
|
||||||
{
|
{
|
||||||
workingDays = null;
|
workingDays = null;
|
||||||
workingHours = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Is24_7 = is24_7;
|
|
||||||
IsAlwaysReady = isAlwaysReady;
|
IsAlwaysReady = isAlwaysReady;
|
||||||
WorkingDays = workingDays;
|
WorkingDays = workingDays;
|
||||||
WorkingHours = workingHours;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public static class DependencyInjection
|
|||||||
public static IServiceCollection AddIdentityModule(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddIdentityModule(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddScoped<IAccountRepository, AccountRepository>();
|
services.AddScoped<IAccountRepository, AccountRepository>();
|
||||||
|
services.AddScoped<ICompetencyRepository, CompetencyRepository>();
|
||||||
services.AddScoped<IJwtTokenGenerator, JwtTokenGenerator>();
|
services.AddScoped<IJwtTokenGenerator, JwtTokenGenerator>();
|
||||||
services.AddScoped<ICurrentUserService, CurrentUserService>();
|
services.AddScoped<ICurrentUserService, CurrentUserService>();
|
||||||
services.AddScoped<IPasswordHasher, PasswordHasher>();
|
services.AddScoped<IPasswordHasher, PasswordHasher>();
|
||||||
|
|||||||
+1
-7
@@ -12,16 +12,10 @@ public class WorkScheduleConfiguration : IEntityTypeConfiguration<WorkSchedule>
|
|||||||
|
|
||||||
builder.HasKey(x => x.Id);
|
builder.HasKey(x => x.Id);
|
||||||
|
|
||||||
builder.Property(x => x.Is24_7)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
builder.Property(x => x.IsAlwaysReady)
|
builder.Property(x => x.IsAlwaysReady)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
builder.Property(x => x.WorkingDays)
|
builder.Property(x => x.WorkingDays)
|
||||||
.HasMaxLength(100); // JSON-массив дней недели
|
.HasMaxLength(2000); // JSON с расписанием для каждого дня
|
||||||
|
|
||||||
builder.Property(x => x.WorkingHours)
|
|
||||||
.HasMaxLength(100); // JSON с часами работы
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+199
@@ -0,0 +1,199 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Nashel.Modules.Identity.Infrastructure.Persistence;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(IdentityDbContext))]
|
||||||
|
[Migration("20260216120252_AddCurrentLocationAndRemoveIs24_7")]
|
||||||
|
partial class AddCurrentLocationAndRemoveIs24_7
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.2")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("Roles")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Phone")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Accounts", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("AvatarUrl")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<string>("CompanyName")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("CurrentLocation")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Inn")
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Patronymic")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("UserProfiles", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Entities.Competency", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("IX_Competencies_Name");
|
||||||
|
|
||||||
|
b.ToTable("Competencies", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Entities.WorkSchedule", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAlwaysReady")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("WorkingDays")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("character varying(2000)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("WorkSchedules", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("UserProfileCompetencies", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserProfileId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("CompetencyId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("UserProfileId", "CompetencyId");
|
||||||
|
|
||||||
|
b.HasIndex("CompetencyId");
|
||||||
|
|
||||||
|
b.ToTable("UserProfileCompetencies", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.Account", null)
|
||||||
|
.WithOne("Profile")
|
||||||
|
.HasForeignKey("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Entities.WorkSchedule", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", null)
|
||||||
|
.WithOne("WorkSchedule")
|
||||||
|
.HasForeignKey("Nashel.Modules.Identity.Domain.Entities.WorkSchedule", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("UserProfileCompetencies", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Nashel.Modules.Identity.Domain.Entities.Competency", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CompetencyId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserProfileId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Profile")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("WorkSchedule");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddCurrentLocationAndRemoveIs24_7 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Is24_7",
|
||||||
|
schema: "identity",
|
||||||
|
table: "WorkSchedules");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "WorkingHours",
|
||||||
|
schema: "identity",
|
||||||
|
table: "WorkSchedules");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "WorkingDays",
|
||||||
|
schema: "identity",
|
||||||
|
table: "WorkSchedules",
|
||||||
|
type: "character varying(2000)",
|
||||||
|
maxLength: 2000,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(100)",
|
||||||
|
oldMaxLength: 100,
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "CurrentLocation",
|
||||||
|
schema: "identity",
|
||||||
|
table: "UserProfiles",
|
||||||
|
type: "text",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Location",
|
||||||
|
schema: "identity",
|
||||||
|
table: "UserProfiles",
|
||||||
|
type: "text",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CurrentLocation",
|
||||||
|
schema: "identity",
|
||||||
|
table: "UserProfiles");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Location",
|
||||||
|
schema: "identity",
|
||||||
|
table: "UserProfiles");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "WorkingDays",
|
||||||
|
schema: "identity",
|
||||||
|
table: "WorkSchedules",
|
||||||
|
type: "character varying(100)",
|
||||||
|
maxLength: 100,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(2000)",
|
||||||
|
oldMaxLength: 2000,
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "Is24_7",
|
||||||
|
schema: "identity",
|
||||||
|
table: "WorkSchedules",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "WorkingHours",
|
||||||
|
schema: "identity",
|
||||||
|
table: "WorkSchedules",
|
||||||
|
type: "character varying(100)",
|
||||||
|
maxLength: 100,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-9
@@ -62,6 +62,9 @@ namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations
|
|||||||
.HasMaxLength(200)
|
.HasMaxLength(200)
|
||||||
.HasColumnType("character varying(200)");
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("CurrentLocation")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.HasMaxLength(2048)
|
.HasMaxLength(2048)
|
||||||
.HasColumnType("character varying(2048)");
|
.HasColumnType("character varying(2048)");
|
||||||
@@ -80,6 +83,9 @@ namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations
|
|||||||
.HasMaxLength(100)
|
.HasMaxLength(100)
|
||||||
.HasColumnType("character varying(100)");
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Patronymic")
|
b.Property<string>("Patronymic")
|
||||||
.HasMaxLength(100)
|
.HasMaxLength(100)
|
||||||
.HasColumnType("character varying(100)");
|
.HasColumnType("character varying(100)");
|
||||||
@@ -114,19 +120,12 @@ namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<bool>("Is24_7")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("IsAlwaysReady")
|
b.Property<bool>("IsAlwaysReady")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
b.Property<string>("WorkingDays")
|
b.Property<string>("WorkingDays")
|
||||||
.HasMaxLength(100)
|
.HasMaxLength(2000)
|
||||||
.HasColumnType("character varying(100)");
|
.HasColumnType("character varying(2000)");
|
||||||
|
|
||||||
b.Property<string>("WorkingHours")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("character varying(100)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Nashel.Modules.Identity.Domain.Aggregates;
|
using Nashel.Modules.Identity.Domain.Aggregates;
|
||||||
|
using Nashel.Modules.Identity.Domain.Entities;
|
||||||
using Nashel.Modules.Identity.Domain.Repositories;
|
using Nashel.Modules.Identity.Domain.Repositories;
|
||||||
using Nashel.Modules.Identity.Infrastructure.Persistence;
|
using Nashel.Modules.Identity.Infrastructure.Persistence;
|
||||||
|
|
||||||
@@ -20,6 +21,12 @@ public class AccountRepository : IAccountRepository
|
|||||||
if (account.Profile != null)
|
if (account.Profile != null)
|
||||||
{
|
{
|
||||||
await _context.UserProfiles.AddAsync(account.Profile, cancellationToken);
|
await _context.UserProfiles.AddAsync(account.Profile, cancellationToken);
|
||||||
|
|
||||||
|
// Добавляем WorkSchedule если есть
|
||||||
|
if (account.Profile.WorkSchedule != null)
|
||||||
|
{
|
||||||
|
await _context.WorkSchedules.AddAsync(account.Profile.WorkSchedule, cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await _context.Accounts.AddAsync(account, cancellationToken);
|
await _context.Accounts.AddAsync(account, cancellationToken);
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
@@ -30,6 +37,8 @@ public class AccountRepository : IAccountRepository
|
|||||||
return await _context.Accounts
|
return await _context.Accounts
|
||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ThenInclude(p => p.Competencies)
|
.ThenInclude(p => p.Competencies)
|
||||||
|
.Include(a => a.Profile)
|
||||||
|
.ThenInclude(p => p.WorkSchedule)
|
||||||
.FirstOrDefaultAsync(a => a.Id == id, cancellationToken);
|
.FirstOrDefaultAsync(a => a.Id == id, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +51,88 @@ public class AccountRepository : IAccountRepository
|
|||||||
|
|
||||||
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}");
|
||||||
|
|
||||||
// Явно обновляем профиль, чтобы EF Core сохранил изменения в таблицу UserProfiles
|
// Явно обновляем профиль, чтобы EF Core сохранил изменения в таблицу UserProfiles
|
||||||
if (account.Profile != null)
|
if (account.Profile != null)
|
||||||
{
|
{
|
||||||
_context.UserProfiles.Update(account.Profile);
|
Console.WriteLine($"[AccountRepository.UpdateAsync] Profile found: {account.Profile.Id}");
|
||||||
|
|
||||||
|
// Загружаем текущий профиль с компетенциями
|
||||||
|
var existingProfile = await _context.UserProfiles
|
||||||
|
.Include(p => p.Competencies)
|
||||||
|
.Include(p => p.WorkSchedule)
|
||||||
|
.FirstOrDefaultAsync(p => p.Id == account.Profile.Id, cancellationToken);
|
||||||
|
|
||||||
|
if (existingProfile != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[AccountRepository.UpdateAsync] Existing profile loaded");
|
||||||
|
|
||||||
|
// Обновляем свойства профиля
|
||||||
|
existingProfile.Update(
|
||||||
|
account.Profile.FirstName,
|
||||||
|
account.Profile.LastName,
|
||||||
|
account.Profile.Patronymic,
|
||||||
|
account.Profile.CompanyName,
|
||||||
|
account.Profile.Inn,
|
||||||
|
account.Profile.Description,
|
||||||
|
account.Profile.Location,
|
||||||
|
account.Profile.CurrentLocation
|
||||||
|
);
|
||||||
|
|
||||||
|
// Обновляем компетенции через доменный метод
|
||||||
|
// Поскольку компетенции уже сохранены через CompetencyRepository,
|
||||||
|
// просто загружаем их из БД по Id для правильной работы EF Core
|
||||||
|
var competencies = new List<Competency>();
|
||||||
|
if (account.Profile.Competencies != null && account.Profile.Competencies.Any())
|
||||||
|
{
|
||||||
|
var competencyIds = account.Profile.Competencies
|
||||||
|
.Select(c => c.Id)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Console.WriteLine($"[AccountRepository.UpdateAsync] Loading {competencyIds.Count} competencies from DB");
|
||||||
|
competencies = await _context.Competencies
|
||||||
|
.Where(c => competencyIds.Contains(c.Id))
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
existingProfile.UpdateCompetencies(competencies);
|
||||||
|
|
||||||
|
// Обновляем WorkSchedule
|
||||||
|
if (account.Profile.WorkSchedule != null)
|
||||||
|
{
|
||||||
|
if (existingProfile.WorkSchedule != null)
|
||||||
|
{
|
||||||
|
// Обновляем существующий WorkSchedule
|
||||||
|
existingProfile.WorkSchedule.Update(
|
||||||
|
account.Profile.WorkSchedule.IsAlwaysReady,
|
||||||
|
account.Profile.WorkSchedule.WorkingDays
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Создаём новый WorkSchedule
|
||||||
|
var newSchedule = WorkSchedule.Create(
|
||||||
|
account.Profile.WorkSchedule.IsAlwaysReady,
|
||||||
|
account.Profile.WorkSchedule.WorkingDays
|
||||||
|
);
|
||||||
|
// EF Core сам установит правильную связь через Id профиля
|
||||||
|
existingProfile.SetWorkSchedule(newSchedule);
|
||||||
|
_context.WorkSchedules.Add(newSchedule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.UserProfiles.Update(existingProfile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[AccountRepository.UpdateAsync] Existing profile not found, using account profile");
|
||||||
|
_context.UserProfiles.Update(account.Profile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_context.Accounts.Update(account);
|
_context.Accounts.Update(account);
|
||||||
|
Console.WriteLine($"[AccountRepository.UpdateAsync] Saving changes to database");
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
Console.WriteLine($"[AccountRepository.UpdateAsync] Successfully completed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Nashel.Modules.Identity.Domain.Entities;
|
||||||
|
using Nashel.Modules.Identity.Domain.Repositories;
|
||||||
|
using Nashel.Modules.Identity.Infrastructure.Persistence;
|
||||||
|
|
||||||
|
namespace Nashel.Modules.Identity.Infrastructure.Repositories;
|
||||||
|
|
||||||
|
public class CompetencyRepository : ICompetencyRepository
|
||||||
|
{
|
||||||
|
private readonly IdentityDbContext _context;
|
||||||
|
|
||||||
|
public CompetencyRepository(IdentityDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Competency?> GetByNameAsync(string name, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await _context.Competencies
|
||||||
|
.FirstOrDefaultAsync(c => c.Name == name, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddAsync(Competency competency, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _context.Competencies.AddAsync(competency, cancellationToken);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Competency>> GetAllAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await _context.Competencies
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,16 +58,23 @@ public static class IdentityEndpoints
|
|||||||
|
|
||||||
profileGroup.MapPost("/become-performer", async (BecomePerformerRequest request, ISender sender) =>
|
profileGroup.MapPost("/become-performer", async (BecomePerformerRequest request, ISender sender) =>
|
||||||
{
|
{
|
||||||
|
Console.WriteLine($"[BecomePerformerEndpoint] Request received");
|
||||||
|
Console.WriteLine($"[BecomePerformerEndpoint] Description: {request.Description?.Substring(0, Math.Min(50, request.Description.Length))}...");
|
||||||
|
Console.WriteLine($"[BecomePerformerEndpoint] Competencies count: {request.Competencies?.Count ?? 0}");
|
||||||
|
Console.WriteLine($"[BecomePerformerEndpoint] Location: {request.Location}");
|
||||||
|
Console.WriteLine($"[BecomePerformerEndpoint] IsAlwaysReady: {request.IsAlwaysReady}");
|
||||||
|
|
||||||
await sender.Send(new BecomePerformerCommand
|
await sender.Send(new BecomePerformerCommand
|
||||||
{
|
{
|
||||||
Description = request.Description,
|
Description = request.Description,
|
||||||
CompetencyNames = request.Competencies,
|
CompetencyNames = request.Competencies,
|
||||||
Is24_7 = request.Is24_7,
|
Location = request.Location,
|
||||||
|
CurrentLocation = request.CurrentLocation,
|
||||||
IsAlwaysReady = request.IsAlwaysReady,
|
IsAlwaysReady = request.IsAlwaysReady,
|
||||||
WorkingDays = request.WorkingDays,
|
WorkingDays = request.WorkingDays
|
||||||
WorkingHoursStart = request.WorkingHoursStart,
|
|
||||||
WorkingHoursEnd = request.WorkingHoursEnd
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Console.WriteLine($"[BecomePerformerEndpoint] Successfully completed");
|
||||||
return Results.Ok();
|
return Results.Ok();
|
||||||
})
|
})
|
||||||
.WithName("BecomePerformer")
|
.WithName("BecomePerformer")
|
||||||
@@ -107,9 +114,8 @@ public record UpdateCompetenciesRequest(List<string> Competencies);
|
|||||||
public record BecomePerformerRequest(
|
public record BecomePerformerRequest(
|
||||||
string Description,
|
string Description,
|
||||||
List<string> Competencies,
|
List<string> Competencies,
|
||||||
bool Is24_7,
|
string? Location,
|
||||||
|
string? CurrentLocation,
|
||||||
bool IsAlwaysReady,
|
bool IsAlwaysReady,
|
||||||
List<string>? WorkingDays,
|
string? WorkingDays
|
||||||
string? WorkingHoursStart,
|
|
||||||
string? WorkingHoursEnd
|
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user