diff --git a/Berizco.Api/Brizco.Api.csproj b/Berizco.Api/Brizco.Api.csproj index 5e1f5ca..656e5ec 100644 --- a/Berizco.Api/Brizco.Api.csproj +++ b/Berizco.Api/Brizco.Api.csproj @@ -59,6 +59,7 @@ + diff --git a/Berizco.Api/Controllers/AuthController.cs b/Berizco.Api/Controllers/AuthController.cs new file mode 100644 index 0000000..cfd2380 --- /dev/null +++ b/Berizco.Api/Controllers/AuthController.cs @@ -0,0 +1,61 @@ +using Brizco.Common.Models.Api; +using Brizco.Core.BaseServices; +using Brizco.Core.BaseServices.Abstracts; +using Microsoft.AspNetCore.Authorization.Infrastructure; + +namespace Brizco.Api.Controllers; + +public class AuthController : ICarterModule +{ + + public virtual void AddRoutes(IEndpointRouteBuilder app) + { + var group = app.NewVersionedApi("Auth") + .MapGroup($"api/auth"); + + group.MapPost("login/password", LoginWithPassword) + .WithDisplayName("LoginWithPassword") + .HasApiVersion(1.0); + + group.MapPost("login/code", LoginWithVerifyCode) + .WithDisplayName("LoginWithVerifyCode") + .HasApiVersion(1.0); + + group.MapGet("verifycode", GetVerifyCodeCode) + .WithDisplayName("GetVerifyCodeCode") + .HasApiVersion(1.0); + + group.MapPut("forgetpassword", ForgetPassword) + .WithDisplayName("ForgetPassword") + .HasApiVersion(1.0); + + group.MapPost("signup/complex", SignUpComplex) + .WithDisplayName("SignUp") + .RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser()) + .HasApiVersion(1.0); + } + + public async Task SignUpComplex([FromBody] SignUpRequestDto request, IAccountService accountService, CancellationToken cancellationToken) => + TypedResults.Ok(await accountService.CompleteComplexSignUpAsync(request,cancellationToken)); + + public async Task LoginWithPassword([FromBody] LoginRequestDto loginRequestDto,IAccountService accountService, CancellationToken cancellationToken) => + TypedResults.Ok(await accountService.LoginWithPasswordAsync(loginRequestDto.UserName, loginRequestDto.Password,cancellationToken)); + + + public async Task LoginWithVerifyCode([FromBody] LoginRequestDto loginRequestDto, IAccountService accountService, CancellationToken cancellationToken) => + TypedResults.Ok(await accountService.LoginWithVerifyCodeAsync(loginRequestDto.UserName, loginRequestDto.VerifyCode,cancellationToken)); + + + public async Task GetVerifyCodeCode([FromQuery] string phoneNumber, IAccountService accountService) => + TypedResults.Ok(await accountService.GetVerifyCodeAsync(phoneNumber)); + + + public async Task ForgetPassword([FromQuery] string phoneNumber, IAccountService accountService) => + TypedResults.Ok(await accountService.ForgetPasswordAsync(phoneNumber)); + + + public async Task LoginSwagger([FromForm] TokenRequest tokenRequest, IAccountService accountService, CancellationToken cancellationToken) + => TypedResults.Json(await accountService.LoginWithPasswordAsync(tokenRequest.username, tokenRequest.password,cancellationToken)); + + +} \ No newline at end of file diff --git a/Berizco.Api/Program.cs b/Berizco.Api/Program.cs index b93ec89..d03a0c6 100644 --- a/Berizco.Api/Program.cs +++ b/Berizco.Api/Program.cs @@ -96,11 +96,14 @@ var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.UseCustomSwagger(siteSetting.BaseUrl); + //app.UseSwagger(); + //app.UseSwaggerUI(); } app.UseAuthorization(); +app.UseAuthentication(); + app.UseCors(x => x .SetIsOriginAllowed(origin => true) .AllowAnyMethod() @@ -109,7 +112,6 @@ app.UseCors(x => x app.UseExceptionHandlerMiddleware(); -app.UseCustomSwagger(siteSetting.BaseUrl); app.MapCarter(); app.UseStaticFiles(); await app.InitialDb(); diff --git a/Berizco.Infrastructure/Brizco.Infrastructure.csproj b/Berizco.Infrastructure/Brizco.Infrastructure.csproj index 744594c..b3dba0d 100644 --- a/Berizco.Infrastructure/Brizco.Infrastructure.csproj +++ b/Berizco.Infrastructure/Brizco.Infrastructure.csproj @@ -10,8 +10,26 @@ + + + + - + + + + + + + + + + + + + + + diff --git a/Berizco.Infrastructure/Models/RestAddress.cs b/Berizco.Infrastructure/Models/RestAddress.cs new file mode 100644 index 0000000..2b76dc6 --- /dev/null +++ b/Berizco.Infrastructure/Models/RestAddress.cs @@ -0,0 +1,6 @@ +namespace Brizco.Infrastructure.Models; + +public static class RestAddress +{ + public static string BaseKaveNegar { get => "https://api.kavenegar.com/v1/"; } +} \ No newline at end of file diff --git a/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponse.cs b/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponse.cs new file mode 100644 index 0000000..a259136 --- /dev/null +++ b/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponse.cs @@ -0,0 +1,7 @@ +namespace Brizco.Infrastructure.Models.RestApi.KaveNegar; + +public class KaveNegarResponse +{ + public KaveNegarReturn Return { get; set; } + public KaveNegarResponseEntry[] entries { get; set; } +} \ No newline at end of file diff --git a/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponseEntry.cs b/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponseEntry.cs new file mode 100644 index 0000000..c6c8245 --- /dev/null +++ b/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponseEntry.cs @@ -0,0 +1,13 @@ +namespace Brizco.Infrastructure.Models.RestApi.KaveNegar; + +public class KaveNegarResponseEntry +{ + public int messageid { get; set; } + public string message { get; set; } + public int status { get; set; } + public string statustext { get; set; } + public string sender { get; set; } + public string receptor { get; set; } + public int date { get; set; } + public int cost { get; set; } +} \ No newline at end of file diff --git a/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarReturn.cs b/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarReturn.cs new file mode 100644 index 0000000..34a3c24 --- /dev/null +++ b/Berizco.Infrastructure/Models/RestApi/KaveNegar/KaveNegarReturn.cs @@ -0,0 +1,7 @@ +namespace Brizco.Infrastructure.Models.RestApi.KaveNegar; + +public class KaveNegarReturn +{ + public int status { get; set; } + public string message { get; set; } +} \ No newline at end of file diff --git a/Berizco.Infrastructure/RestServices/IKaveNegarRestApi.cs b/Berizco.Infrastructure/RestServices/IKaveNegarRestApi.cs new file mode 100644 index 0000000..ba260ed --- /dev/null +++ b/Berizco.Infrastructure/RestServices/IKaveNegarRestApi.cs @@ -0,0 +1,12 @@ +using Brizco.Infrastructure.Models.RestApi.KaveNegar; + +namespace Brizco.Infrastructure.RestServices; + +public interface IKaveNegarRestApi +{ + + [Post("/{apiKey}/verify/lookup.json")] + Task SendLookUp(string apiKey, [Query] string receptor, [Query] string token, [Query] string token2, [Query] string token10, [Query] string token20, [Query] string template); + [Post("/{apiKey}/sms/send.json")] + Task SendSms(string apiKey, [Query] string receptor, [Query] string message, [Query] string sender); +} \ No newline at end of file diff --git a/Berizco.Infrastructure/RestServices/IRestApiWrapper.cs b/Berizco.Infrastructure/RestServices/IRestApiWrapper.cs new file mode 100644 index 0000000..d30099b --- /dev/null +++ b/Berizco.Infrastructure/RestServices/IRestApiWrapper.cs @@ -0,0 +1,11 @@ +namespace Brizco.Infrastructure.RestServices; + +public interface IRestApiWrapper : IScopedDependency +{ + IKaveNegarRestApi KaveNegarRestApi { get; } +} + +public class RestApiWrapper : IRestApiWrapper +{ + public IKaveNegarRestApi KaveNegarRestApi => RestService.For(RestAddress.BaseKaveNegar); +} \ No newline at end of file diff --git a/Berizco.Infrastructure/Services/SmsService.cs b/Berizco.Infrastructure/Services/SmsService.cs new file mode 100644 index 0000000..5d43579 --- /dev/null +++ b/Berizco.Infrastructure/Services/SmsService.cs @@ -0,0 +1,33 @@ +namespace Brizco.Infrastructure.Services; + +public class SmsService : ISmsService +{ + private readonly IRestApiWrapper _restApiWrapper; + private readonly ILogger _logger; + private readonly SiteSettings _siteSettings; + public SmsService(IRestApiWrapper restApiWrapper, + IOptionsSnapshot optionsSnapshot, + ILogger logger) + { + _restApiWrapper = restApiWrapper; + _logger = logger; + _siteSettings = optionsSnapshot.Value; + } + public async Task SendForgerPasswordAsync(string phoneNumber, string newPassword) + { + var rest = await _restApiWrapper.KaveNegarRestApi.SendLookUp(_siteSettings.KaveNegarApiKey, phoneNumber, newPassword, null, null, null, "forgetPassword"); + + if (rest.Return.status != 200) + throw new BaseApiException(ApiResultStatusCode.SendSmsError, rest.Return.message); + } + + public async Task SendVerifyCodeAsync(string phoneNumber, string verifyCode) + { + + var rest = await _restApiWrapper.KaveNegarRestApi.SendLookUp(_siteSettings.KaveNegarApiKey, phoneNumber, verifyCode, null, null, null, "login"); + + if (rest.Return.status != 200) + throw new BaseApiException(ApiResultStatusCode.SendSmsError, rest.Return.message); + } + +} \ No newline at end of file diff --git a/Berizco.Repository/Brizco.Repository.csproj b/Berizco.Repository/Brizco.Repository.csproj index 7f2feeb..d728e30 100644 --- a/Berizco.Repository/Brizco.Repository.csproj +++ b/Berizco.Repository/Brizco.Repository.csproj @@ -29,8 +29,6 @@ - - diff --git a/Berizco.Repository/Handlers/Activity/CreateActivityCommandHandler.cs b/Berizco.Repository/Handlers/Activity/CreateActivityCommandHandler.cs index 41bb5b8..ee68e7f 100644 --- a/Berizco.Repository/Handlers/Activity/CreateActivityCommandHandler.cs +++ b/Berizco.Repository/Handlers/Activity/CreateActivityCommandHandler.cs @@ -17,48 +17,58 @@ public class CreateActivityCommandHandler : IRequestHandler().Add(task); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return task.AdaptToLDto(); + } + catch (Exception) { - if (request.Roles.Count == 0) - throw new AppException("اگر فعالیت برای یک گروه نقش انتخاب شده باشد باید لیست نقش ها را ارسال نمایید"); - task.AddShift(request.Roles.ToArray()); + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; } - - if (task.IsRelatedToShift) - { - if (request.Shifts.Count == 0) - throw new AppException("اگر فعالیت برای یک شیفت انتخاب شده باشد باید لیست شیفت ها را ارسال نمایید"); - task.AddRole(request.Shifts.ToArray()); - } - - _repositoryWrapper.SetRepository().Add(task); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); - return task.AdaptToLDto(); } } \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Activity/DeleteActivityCommandHandler.cs b/Berizco.Repository/Handlers/Activity/DeleteActivityCommandHandler.cs index 42684d4..0551448 100644 --- a/Berizco.Repository/Handlers/Activity/DeleteActivityCommandHandler.cs +++ b/Berizco.Repository/Handlers/Activity/DeleteActivityCommandHandler.cs @@ -10,15 +10,25 @@ public class DeleteActivityCommandHandler : IRequestHandler Handle(DeleteActivityCommand request, CancellationToken cancellationToken) { - var task = await _repositoryWrapper.SetRepository() - .TableNoTracking - .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); - if (task == null) - throw new AppException("Task not found", ApiResultStatusCode.NotFound); - _repositoryWrapper.SetRepository() - .Delete(task); + try + { + await _repositoryWrapper.BeginTransaction(cancellationToken); + var task = await _repositoryWrapper.SetRepository() + .TableNoTracking + .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); + if (task == null) + throw new AppException("Task not found", ApiResultStatusCode.NotFound); + _repositoryWrapper.SetRepository() + .Delete(task); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); - return true; + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return true; + } + catch (Exception ) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } } } \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Activity/GetActivitiesQueryHandler.cs b/Berizco.Repository/Handlers/Activity/GetActivitiesQueryHandler.cs index 284384f..0d6742a 100644 --- a/Berizco.Repository/Handlers/Activity/GetActivitiesQueryHandler.cs +++ b/Berizco.Repository/Handlers/Activity/GetActivitiesQueryHandler.cs @@ -12,7 +12,7 @@ public class GetActivitiesQueryHandler : IRequestHandler().TableNoTracking .OrderByDescending(s => s.CreatedAt) - .Skip(request.page * 15).Take(15) + .Skip(request.Page * 15).Take(15) .Select(ActivityMapper.ProjectToSDto) .ToListAsync(cancellationToken); diff --git a/Berizco.Repository/Handlers/Activity/GetActivityQueryHandler.cs b/Berizco.Repository/Handlers/Activity/GetActivityQueryHandler.cs index 9293b98..292d286 100644 --- a/Berizco.Repository/Handlers/Activity/GetActivityQueryHandler.cs +++ b/Berizco.Repository/Handlers/Activity/GetActivityQueryHandler.cs @@ -15,7 +15,7 @@ public class GetActivityQueryHandler : IRequestHandler() .TableNoTracking - .Where(s => s.Id == request.id) + .Where(s => s.Id == request.Id) .Select(ActivityMapper.ProjectToLDto) .FirstOrDefaultAsync(cancellationToken); if (task == null) diff --git a/Berizco.Repository/Handlers/Activity/UpdateActivityCommandHandler.cs b/Berizco.Repository/Handlers/Activity/UpdateActivityCommandHandler.cs index 9871f4f..e488d64 100644 --- a/Berizco.Repository/Handlers/Activity/UpdateActivityCommandHandler.cs +++ b/Berizco.Repository/Handlers/Activity/UpdateActivityCommandHandler.cs @@ -22,29 +22,39 @@ public class UpdateActivityCommandHandler : IRequestHandler() - .Update(newTask); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); + _repositoryWrapper.SetRepository() + .Update(newTask); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); - return true; + return true; + } + catch (Exception ) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } } } \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Complex/CreateComplexCommandHandler.cs b/Berizco.Repository/Handlers/Complex/CreateComplexCommandHandler.cs index 1d5072f..7158448 100644 --- a/Berizco.Repository/Handlers/Complex/CreateComplexCommandHandler.cs +++ b/Berizco.Repository/Handlers/Complex/CreateComplexCommandHandler.cs @@ -13,9 +13,19 @@ public class CreateComplexCommandHandler : IRequestHandler Handle(CreateComplexCommand request, CancellationToken cancellationToken) { - var complex = Domain.Entities.Complex.Complex.Create(request.Name, request.Address, request.SupportPhone); - _repositoryWrapper.SetRepository().Add(complex); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); - return complex.AdaptToSDto(); + try + { + await _repositoryWrapper.BeginTransaction(cancellationToken); + var complex = Domain.Entities.Complex.Complex.Create(request.Name, request.Address, request.SupportPhone); + _repositoryWrapper.SetRepository().Add(complex); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return complex.AdaptToSDto(); + } + catch (Exception) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } } } \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Complex/CreateComplexUserCommandHandler.cs b/Berizco.Repository/Handlers/Complex/CreateComplexUserCommandHandler.cs new file mode 100644 index 0000000..2612a36 --- /dev/null +++ b/Berizco.Repository/Handlers/Complex/CreateComplexUserCommandHandler.cs @@ -0,0 +1,51 @@ +using Brizco.Domain.Entities.User; +using Microsoft.AspNetCore.Identity; + +namespace Brizco.Repository.Handlers.Complex; + +public class CreateComplexUserCommandHandler : IRequestHandler +{ + private readonly IRepositoryWrapper _repositoryWrapper; + private readonly UserManager _userManager; + private readonly RoleManager _roleManager; + private readonly ILogger _logger; + + public CreateComplexUserCommandHandler(IRepositoryWrapper repositoryWrapper, + UserManager userManager, + RoleManager roleManager, + ILogger logger) + { + _repositoryWrapper = repositoryWrapper; + _userManager = userManager; + _roleManager = roleManager; + _logger = logger; + } + + public async Task Handle(CreateComplexUserCommand request, CancellationToken cancellationToken) + { + try + { + await _repositoryWrapper.BeginTransaction(cancellationToken); + var complex = await _repositoryWrapper.SetRepository().TableNoTracking + .FirstOrDefaultAsync(c => c.Id == request.ComplexId, cancellationToken); + if (complex == null) + throw new AppException("Complex not found", ApiResultStatusCode.NotFound); + + var complexUser = complex.AddComplexUser(request.UserId, request.RoleId); + _repositoryWrapper.SetRepository().Update(complex); + var role = await _roleManager.FindByIdAsync(request.RoleId.ToString()); + var user = await _userManager.FindByIdAsync(request.UserId.ToString()); + var result = await _userManager.AddToRoleAsync(user, role.Name); + if (!result.Succeeded) + throw new AppException(string.Join('|', result.Errors)); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return complexUser.AdaptToSDto(); + } + catch (Exception) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } + } +} \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Complex/DeleteComplexCommandHandler.cs b/Berizco.Repository/Handlers/Complex/DeleteComplexCommandHandler.cs index 2dec095..7ec7f06 100644 --- a/Berizco.Repository/Handlers/Complex/DeleteComplexCommandHandler.cs +++ b/Berizco.Repository/Handlers/Complex/DeleteComplexCommandHandler.cs @@ -10,15 +10,25 @@ public class DeleteComplexCommandHandler : IRequestHandler Handle(DeleteComplexCommand request, CancellationToken cancellationToken) { - var task = await _repositoryWrapper.SetRepository() - .TableNoTracking - .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); - if (task == null) - throw new AppException("Task not found", ApiResultStatusCode.NotFound); - _repositoryWrapper.SetRepository() - .Delete(task); + try + { + await _repositoryWrapper.BeginTransaction(cancellationToken); + var task = await _repositoryWrapper.SetRepository() + .TableNoTracking + .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); + if (task == null) + throw new AppException("Task not found", ApiResultStatusCode.NotFound); + _repositoryWrapper.SetRepository() + .Delete(task); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); - return true; + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return true; + } + catch (Exception) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } } } \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Complex/DeleteComplexUserCommandHandler.cs b/Berizco.Repository/Handlers/Complex/DeleteComplexUserCommandHandler.cs new file mode 100644 index 0000000..78d0d33 --- /dev/null +++ b/Berizco.Repository/Handlers/Complex/DeleteComplexUserCommandHandler.cs @@ -0,0 +1,36 @@ +using Brizco.Domain.Entities.Complex; + +namespace Brizco.Repository.Handlers.Complex; + +public class DeleteComplexUserCommandHandler : IRequestHandler +{ + private readonly IRepositoryWrapper _repositoryWrapper; + + public DeleteComplexUserCommandHandler(IRepositoryWrapper repositoryWrapper) + { + _repositoryWrapper = repositoryWrapper; + } + + public async Task Handle(DeleteComplexUserCommand request, CancellationToken cancellationToken) + { + try + { + await _repositoryWrapper.BeginTransaction(cancellationToken); + var complexUser = await _repositoryWrapper.SetRepository() + .TableNoTracking + .FirstOrDefaultAsync(c => c.Id == request.ComplexUserId, cancellationToken); + if (complexUser == null) + throw new AppException("ComplexUser not found", ApiResultStatusCode.NotFound); + + _repositoryWrapper.SetRepository().Delete(complexUser); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return true; + } + catch (Exception) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } + } +} \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Complex/GetComplexQueryHandler.cs b/Berizco.Repository/Handlers/Complex/GetComplexQueryHandler.cs index cd5b59b..fe8376a 100644 --- a/Berizco.Repository/Handlers/Complex/GetComplexQueryHandler.cs +++ b/Berizco.Repository/Handlers/Complex/GetComplexQueryHandler.cs @@ -14,7 +14,7 @@ public class GetComplexQueryHandler : IRequestHandler() .TableNoTracking .Where(s => s.Id == request.Id) - .Select(ComplexMapper.ProjectToLDto) + .Select(ComplexMapper.ProjectToSDto) .FirstOrDefaultAsync(cancellationToken); if (complex == null) throw new AppException("Complex not found", ApiResultStatusCode.NotFound); diff --git a/Berizco.Repository/Handlers/Complex/GetComplexesQueryHandler.cs b/Berizco.Repository/Handlers/Complex/GetComplexesQueryHandler.cs index 8958889..5ad15e5 100644 --- a/Berizco.Repository/Handlers/Complex/GetComplexesQueryHandler.cs +++ b/Berizco.Repository/Handlers/Complex/GetComplexesQueryHandler.cs @@ -8,7 +8,7 @@ public class GetComplexesQueryHandler : IRequestHandler> Handle(GetComplexesQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetComplexesQuery request, CancellationToken cancellationToken) { var tasks = await _repositoryWrapper.SetRepository().TableNoTracking .OrderByDescending(s => s.CreatedAt) diff --git a/Berizco.Repository/Handlers/Complex/UpdateActivityCommandHandler.cs b/Berizco.Repository/Handlers/Complex/UpdateActivityCommandHandler.cs deleted file mode 100644 index a17e9cb..0000000 --- a/Berizco.Repository/Handlers/Complex/UpdateActivityCommandHandler.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Brizco.Repository.Handlers.Complex; - -public class UpdateActivityCommandHandler : IRequestHandler -{ - private readonly IRepositoryWrapper _repositoryWrapper; - private readonly ICurrentUserService _currentUserService; - - public UpdateActivityCommandHandler(IRepositoryWrapper repositoryWrapper,ICurrentUserService currentUserService) - { - _repositoryWrapper = repositoryWrapper; - _currentUserService = currentUserService; - } - - public async Task Handle(UpdateActivityCommand request, CancellationToken cancellationToken) - { - var task = await _repositoryWrapper.SetRepository() - .TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.Id,cancellationToken); - if (task == null) - throw new AppException("Task not found", ApiResultStatusCode.NotFound); - if (_currentUserService.ComplexId == null) - throw new AppException("ComplexId is null", ApiResultStatusCode.NotFound); - if (!Guid.TryParse(_currentUserService.ComplexId,out Guid complexId)) - throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound); - - var newTask = Domain.Entities.Task.Activity.Create( - request.Status, - request.DoneAt, - request.PerformanceDescription, - request.Title, - request.Description, - request.Type, - request.IsRelatedToShift, - request.IsRelatedToRole, - request.IsRelatedToPerson, - request.IsDisposable, - request.SetFor, - request.HasDisposed, - request.Amount, - request.AmountType, - complexId); - - newTask.Id = request.Id; - - _repositoryWrapper.SetRepository() - .Update(newTask); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); - - return true; - } -} \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Shift/CreateShiftCommandHandler.cs b/Berizco.Repository/Handlers/Shift/CreateShiftCommandHandler.cs index f56b0c0..44f5a4a 100644 --- a/Berizco.Repository/Handlers/Shift/CreateShiftCommandHandler.cs +++ b/Berizco.Repository/Handlers/Shift/CreateShiftCommandHandler.cs @@ -18,17 +18,27 @@ public class CreateShiftCommandHandler : IRequestHandler shift.SetDay(d)); + request.DayOfWeeks.ForEach(d => shift.SetDay(d)); - _repositoryWrapper.SetRepository().Add(shift); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); - return shift; + _repositoryWrapper.SetRepository().Add(shift); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return shift; + } + catch (Exception ) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } } } \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Shift/DeleteShiftCommandHandler.cs b/Berizco.Repository/Handlers/Shift/DeleteShiftCommandHandler.cs index e4622cd..77fbc36 100644 --- a/Berizco.Repository/Handlers/Shift/DeleteShiftCommandHandler.cs +++ b/Berizco.Repository/Handlers/Shift/DeleteShiftCommandHandler.cs @@ -12,7 +12,7 @@ public class DeleteShiftCommandHandler : IRequestHandler() .TableNoTracking - .FirstOrDefaultAsync(s => s.Id == request.id, cancellationToken); + .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); if (shift == null) throw new AppException("Shift not found", ApiResultStatusCode.NotFound); _repositoryWrapper.SetRepository() diff --git a/Berizco.Repository/Handlers/Shift/UpdateShiftCommandHandler.cs b/Berizco.Repository/Handlers/Shift/UpdateShiftCommandHandler.cs index 5d8c6cd..43ff0b5 100644 --- a/Berizco.Repository/Handlers/Shift/UpdateShiftCommandHandler.cs +++ b/Berizco.Repository/Handlers/Shift/UpdateShiftCommandHandler.cs @@ -14,7 +14,7 @@ public class UpdateShiftCommandHandler : IRequestHandler Handle(UpdateShiftCommand request, CancellationToken cancellationToken) { var shift = await _repositoryWrapper.SetRepository() - .TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.id); + .TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.Id); if (shift == null) throw new AppException("Shift not found", ApiResultStatusCode.NotFound); @@ -28,7 +28,7 @@ public class UpdateShiftCommandHandler : IRequestHandler() .Update(newShift); await _repositoryWrapper.SaveChangesAsync(cancellationToken); diff --git a/Berizco.Repository/Handlers/ShiftPlan/CreateShiftPlanCommandHandler.cs b/Berizco.Repository/Handlers/ShiftPlan/CreateShiftPlanCommandHandler.cs index 23b80dc..a3fe947 100644 --- a/Berizco.Repository/Handlers/ShiftPlan/CreateShiftPlanCommandHandler.cs +++ b/Berizco.Repository/Handlers/ShiftPlan/CreateShiftPlanCommandHandler.cs @@ -16,15 +16,26 @@ public class CreateShiftPlanCommandHandler : IRequestHandlershiftPlan.AddUser(i)); + request.UserIds.ForEach(i => shiftPlan.AddUser(i)); - _repositoryWrapper.SetRepository().Add(shiftPlan); - await _repositoryWrapper.SaveChangesAsync(cancellationToken); - return shiftPlan.AdaptToLDto(); + _repositoryWrapper.SetRepository().Add(shiftPlan); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + await _repositoryWrapper.CommitAsync(cancellationToken); + return shiftPlan.AdaptToLDto(); + } + catch (Exception ) + { + await _repositoryWrapper.RollBackAsync(cancellationToken); + throw; + } } } \ No newline at end of file diff --git a/Berizco.Repository/Handlers/Task/DeleteTaskCommandHandler.cs b/Berizco.Repository/Handlers/Task/DeleteTaskCommandHandler.cs index fcaaa3e..2911a30 100644 --- a/Berizco.Repository/Handlers/Task/DeleteTaskCommandHandler.cs +++ b/Berizco.Repository/Handlers/Task/DeleteTaskCommandHandler.cs @@ -12,7 +12,7 @@ public class DeleteActivityCommandHandler : IRequestHandler() .TableNoTracking - .FirstOrDefaultAsync(s => s.Id == request.id, cancellationToken); + .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); if (task == null) throw new AppException("Task not found", ApiResultStatusCode.NotFound); _repositoryWrapper.SetRepository() diff --git a/Berizco.Repository/Handlers/Task/GetTaskQueryHandler.cs b/Berizco.Repository/Handlers/Task/GetTaskQueryHandler.cs index cd7e3af..75e87b6 100644 --- a/Berizco.Repository/Handlers/Task/GetTaskQueryHandler.cs +++ b/Berizco.Repository/Handlers/Task/GetTaskQueryHandler.cs @@ -15,7 +15,7 @@ public class GetActivityQueryHandler : IRequestHandler { var task = await _repositoryWrapper.SetRepository() .TableNoTracking - .Where(s => s.Id == request.id) + .Where(s => s.Id == request.Id) .Select(TaskMapper.ProjectToLDto) .FirstOrDefaultAsync(cancellationToken); if (task == null) diff --git a/Berizco.Repository/Handlers/Task/GetTasksQueryHandler.cs b/Berizco.Repository/Handlers/Task/GetTasksQueryHandler.cs index 9c59add..180d5ec 100644 --- a/Berizco.Repository/Handlers/Task/GetTasksQueryHandler.cs +++ b/Berizco.Repository/Handlers/Task/GetTasksQueryHandler.cs @@ -12,7 +12,7 @@ public class GetActivitiesQueryHandler : IRequestHandler().TableNoTracking .OrderByDescending(s => s.CreatedAt) - .Skip(request.page * 15).Take(15) + .Skip(request.Page * 15).Take(15) .Select(TaskMapper.ProjectToSDto) .ToListAsync(cancellationToken); diff --git a/Berizco.Repository/Handlers/Task/UpdateTaskCommandHandler.cs b/Berizco.Repository/Handlers/Task/UpdateTaskCommandHandler.cs index f16e55a..fc08f8d 100644 --- a/Berizco.Repository/Handlers/Task/UpdateTaskCommandHandler.cs +++ b/Berizco.Repository/Handlers/Task/UpdateTaskCommandHandler.cs @@ -14,7 +14,7 @@ public class UpdateActivityCommandHandler : IRequestHandler Handle(UpdateTaskCommand request, CancellationToken cancellationToken) { var task = await _repositoryWrapper.SetRepository() - .TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.id,cancellationToken); + .TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.Id,cancellationToken); if (task == null) throw new AppException("Task not found", ApiResultStatusCode.NotFound); @@ -35,7 +35,7 @@ public class UpdateActivityCommandHandler : IRequestHandler() .Update(newTask); diff --git a/Berizco.Repository/Migrations/20230918112118_Init.Designer.cs b/Berizco.Repository/Migrations/20230919130824_Init.Designer.cs similarity index 97% rename from Berizco.Repository/Migrations/20230918112118_Init.Designer.cs rename to Berizco.Repository/Migrations/20230919130824_Init.Designer.cs index 6ba96a8..539b19e 100644 --- a/Berizco.Repository/Migrations/20230918112118_Init.Designer.cs +++ b/Berizco.Repository/Migrations/20230919130824_Init.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Brizco.Repository.Migrations { [DbContext(typeof(ApplicationContext))] - [Migration("20230918112118_Init")] + [Migration("20230919130824_Init")] partial class Init { /// @@ -106,6 +106,9 @@ namespace Brizco.Repository.Migrations .IsRequired() .HasColumnType("text"); + b.Property("RoleId") + .HasColumnType("uuid"); + b.Property("UserId") .HasColumnType("uuid"); @@ -113,6 +116,8 @@ namespace Brizco.Repository.Migrations b.HasIndex("ComplexId"); + b.HasIndex("RoleId"); + b.HasIndex("UserId"); b.ToTable("ComplexUsers", "public"); @@ -541,6 +546,10 @@ namespace Brizco.Repository.Migrations .IsRequired() .HasColumnType("text"); + b.Property("EnglishName") + .IsRequired() + .HasColumnType("text"); + b.Property("Name") .HasMaxLength(256) .HasColumnType("character varying(256)"); @@ -549,6 +558,10 @@ namespace Brizco.Repository.Migrations .HasMaxLength(256) .HasColumnType("character varying(256)"); + b.Property("PersianName") + .IsRequired() + .HasColumnType("text"); + b.HasKey("Id"); b.HasIndex("ComplexId"); @@ -620,6 +633,9 @@ namespace Brizco.Repository.Migrations b.Property("SecurityStamp") .HasColumnType("text"); + b.Property("SignUpStatus") + .HasColumnType("integer"); + b.Property("TwoFactorEnabled") .HasColumnType("boolean"); @@ -770,6 +786,12 @@ namespace Brizco.Repository.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("Brizco.Domain.Entities.User.ApplicationRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("Brizco.Domain.Entities.User.ApplicationUser", "User") .WithMany() .HasForeignKey("UserId") @@ -778,6 +800,8 @@ namespace Brizco.Repository.Migrations b.Navigation("Complex"); + b.Navigation("Role"); + b.Navigation("User"); }); diff --git a/Berizco.Repository/Migrations/20230918112118_Init.cs b/Berizco.Repository/Migrations/20230919130824_Init.cs similarity index 97% rename from Berizco.Repository/Migrations/20230918112118_Init.cs rename to Berizco.Repository/Migrations/20230919130824_Init.cs index b702314..b4abe47 100644 --- a/Berizco.Repository/Migrations/20230918112118_Init.cs +++ b/Berizco.Repository/Migrations/20230919130824_Init.cs @@ -47,6 +47,7 @@ namespace Brizco.Repository.Migrations LastName = table.Column(type: "text", nullable: false), BirthDate = table.Column(type: "timestamp without time zone", nullable: false), Gender = table.Column(type: "integer", nullable: false), + SignUpStatus = table.Column(type: "integer", nullable: false), UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), @@ -74,6 +75,8 @@ namespace Brizco.Repository.Migrations { Id = table.Column(type: "uuid", nullable: false), Description = table.Column(type: "text", nullable: false), + EnglishName = table.Column(type: "text", nullable: false), + PersianName = table.Column(type: "text", nullable: false), ComplexId = table.Column(type: "uuid", nullable: true), Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), @@ -187,41 +190,6 @@ namespace Brizco.Repository.Migrations onDelete: ReferentialAction.Restrict); }); - migrationBuilder.CreateTable( - name: "ComplexUsers", - schema: "public", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - UserId = table.Column(type: "uuid", nullable: false), - ComplexId = table.Column(type: "uuid", nullable: false), - RemovedAt = table.Column(type: "timestamp without time zone", nullable: false), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), - CreatedBy = table.Column(type: "text", nullable: false), - IsRemoved = table.Column(type: "boolean", nullable: false), - RemovedBy = table.Column(type: "text", nullable: false), - ModifiedAt = table.Column(type: "timestamp without time zone", nullable: false), - ModifiedBy = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ComplexUsers", x => x.Id); - table.ForeignKey( - name: "FK_ComplexUsers_Complexes_ComplexId", - column: x => x.ComplexId, - principalSchema: "public", - principalTable: "Complexes", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_ComplexUsers_Users_UserId", - column: x => x.UserId, - principalSchema: "public", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - migrationBuilder.CreateTable( name: "Logins", schema: "public", @@ -266,6 +234,49 @@ namespace Brizco.Repository.Migrations onDelete: ReferentialAction.Restrict); }); + migrationBuilder.CreateTable( + name: "ComplexUsers", + schema: "public", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + ComplexId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false), + RemovedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedBy = table.Column(type: "text", nullable: false), + IsRemoved = table.Column(type: "boolean", nullable: false), + RemovedBy = table.Column(type: "text", nullable: false), + ModifiedAt = table.Column(type: "timestamp without time zone", nullable: false), + ModifiedBy = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ComplexUsers", x => x.Id); + table.ForeignKey( + name: "FK_ComplexUsers_Complexes_ComplexId", + column: x => x.ComplexId, + principalSchema: "public", + principalTable: "Complexes", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ComplexUsers_Roles_RoleId", + column: x => x.RoleId, + principalSchema: "public", + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ComplexUsers_Users_UserId", + column: x => x.UserId, + principalSchema: "public", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + migrationBuilder.CreateTable( name: "RoleClaims", schema: "public", @@ -525,6 +536,12 @@ namespace Brizco.Repository.Migrations table: "ComplexUsers", column: "ComplexId"); + migrationBuilder.CreateIndex( + name: "IX_ComplexUsers_RoleId", + schema: "public", + table: "ComplexUsers", + column: "RoleId"); + migrationBuilder.CreateIndex( name: "IX_ComplexUsers_UserId", schema: "public", diff --git a/Berizco.Repository/Migrations/ApplicationContextModelSnapshot.cs b/Berizco.Repository/Migrations/ApplicationContextModelSnapshot.cs index c6f4de3..3482684 100644 --- a/Berizco.Repository/Migrations/ApplicationContextModelSnapshot.cs +++ b/Berizco.Repository/Migrations/ApplicationContextModelSnapshot.cs @@ -103,6 +103,9 @@ namespace Brizco.Repository.Migrations .IsRequired() .HasColumnType("text"); + b.Property("RoleId") + .HasColumnType("uuid"); + b.Property("UserId") .HasColumnType("uuid"); @@ -110,6 +113,8 @@ namespace Brizco.Repository.Migrations b.HasIndex("ComplexId"); + b.HasIndex("RoleId"); + b.HasIndex("UserId"); b.ToTable("ComplexUsers", "public"); @@ -538,6 +543,10 @@ namespace Brizco.Repository.Migrations .IsRequired() .HasColumnType("text"); + b.Property("EnglishName") + .IsRequired() + .HasColumnType("text"); + b.Property("Name") .HasMaxLength(256) .HasColumnType("character varying(256)"); @@ -546,6 +555,10 @@ namespace Brizco.Repository.Migrations .HasMaxLength(256) .HasColumnType("character varying(256)"); + b.Property("PersianName") + .IsRequired() + .HasColumnType("text"); + b.HasKey("Id"); b.HasIndex("ComplexId"); @@ -617,6 +630,9 @@ namespace Brizco.Repository.Migrations b.Property("SecurityStamp") .HasColumnType("text"); + b.Property("SignUpStatus") + .HasColumnType("integer"); + b.Property("TwoFactorEnabled") .HasColumnType("boolean"); @@ -767,6 +783,12 @@ namespace Brizco.Repository.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("Brizco.Domain.Entities.User.ApplicationRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("Brizco.Domain.Entities.User.ApplicationUser", "User") .WithMany() .HasForeignKey("UserId") @@ -775,6 +797,8 @@ namespace Brizco.Repository.Migrations b.Navigation("Complex"); + b.Navigation("Role"); + b.Navigation("User"); }); diff --git a/Berizco.Repository/Repositories/Base/Contracts/IRepositoryWrapper.cs b/Berizco.Repository/Repositories/Base/Contracts/IRepositoryWrapper.cs index 56f2154..c5a0586 100644 --- a/Berizco.Repository/Repositories/Base/Contracts/IRepositoryWrapper.cs +++ b/Berizco.Repository/Repositories/Base/Contracts/IRepositoryWrapper.cs @@ -3,6 +3,9 @@ public interface IRepositoryWrapper : IDisposable , IScopedDependency { IBaseRepository SetRepository() where T : ApiEntity; - System.Threading.Tasks.Task SaveChangesAsync(CancellationToken cancellationToken = default); + Task BeginTransaction(CancellationToken cancellationToken); + Task RollBackAsync(CancellationToken cancellationToken); + Task CommitAsync(CancellationToken cancellationToken); + Task SaveChangesAsync(CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/Berizco.Repository/Repositories/Base/RepositoryWrapper.cs b/Berizco.Repository/Repositories/Base/RepositoryWrapper.cs index 1c52bd4..b61134f 100644 --- a/Berizco.Repository/Repositories/Base/RepositoryWrapper.cs +++ b/Berizco.Repository/Repositories/Base/RepositoryWrapper.cs @@ -1,22 +1,71 @@ -using Brizco.Repository.Repositories.UnitOfWork; +using Brizco.Repository.Models; +using Brizco.Repository.Repositories.UnitOfWork; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Storage; namespace Brizco.Repository.Repositories.Base; public class RepositoryWrapper : IRepositoryWrapper { private readonly ApplicationContext _context; - private readonly IUnitOfWork _unitOfWork; + private IDbContextTransaction? _currentTransaction; public RepositoryWrapper(ApplicationContext context) { _context = context; - _unitOfWork = new UnitOfWork.UnitOfWork(_context); } public IBaseRepository SetRepository() where T : ApiEntity => new BaseRepository(_context); - public async System.Threading.Tasks.Task SaveChangesAsync(CancellationToken cancellationToken = default) => await _unitOfWork.SaveChangesAsync(cancellationToken); + public async Task RollBackAsync(CancellationToken cancellationToken) + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.RollbackAsync(cancellationToken); + } + public async Task CommitAsync(CancellationToken cancellationToken) + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.CommitAsync(cancellationToken); + } + public async Task BeginTransaction() + { + _currentTransaction = await _context.Database.BeginTransactionAsync(); + } + public async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + SetAuditables(); + await _context.SaveChangesAsync(cancellationToken); + } + private void SetAuditables() + { + IEnumerable> entries = _context.ChangeTracker.Entries(); + foreach (EntityEntry entity in entries) + { + if (entity.State == EntityState.Added) + { + entity.Property(e => e.CreatedAt) + .CurrentValue = DateTime.Now; + } + + if (entity.State == EntityState.Modified) + { + entity.Property(e => e.ModifiedAt) + .CurrentValue = DateTime.Now; + } + + if (entity.State == EntityState.Deleted) + { + entity.Property(e => e.RemovedAt) + .CurrentValue = DateTime.Now; + entity.Property(e => e.IsRemoved) + .CurrentValue = true; + } + } + } public void Dispose() { + _currentTransaction?.Dispose(); _context?.Dispose(); } } diff --git a/Berizco.Repository/Repositories/UnitOfWork/IUnitOfWork.cs b/Berizco.Repository/Repositories/UnitOfWork/IUnitOfWork.cs index 7e7e191..23d737f 100644 --- a/Berizco.Repository/Repositories/UnitOfWork/IUnitOfWork.cs +++ b/Berizco.Repository/Repositories/UnitOfWork/IUnitOfWork.cs @@ -2,5 +2,8 @@ public interface IUnitOfWork : IScopedDependency { - System.Threading.Tasks.Task SaveChangesAsync(CancellationToken cancellationToken = default); + Task BeginTransaction(); + Task RollBackAsync(); + Task CommitAsync(); + Task SaveChangesAsync(CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/Berizco.Repository/Repositories/UnitOfWork/UnitOfWork.cs b/Berizco.Repository/Repositories/UnitOfWork/UnitOfWork.cs index 30d754a..2425d23 100644 --- a/Berizco.Repository/Repositories/UnitOfWork/UnitOfWork.cs +++ b/Berizco.Repository/Repositories/UnitOfWork/UnitOfWork.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Storage; using Task = System.Threading.Tasks.Task; namespace Brizco.Repository.Repositories.UnitOfWork; @@ -6,12 +7,28 @@ namespace Brizco.Repository.Repositories.UnitOfWork; public class UnitOfWork : IUnitOfWork { private readonly ApplicationContext _applicationContext; - + private IDbContextTransaction? _currentTransaction ; public UnitOfWork(ApplicationContext applicationContext) { _applicationContext = applicationContext; } + public async Task RollBackAsync() + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.RollbackAsync(); + } + public async Task CommitAsync() + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.CommitAsync(); + } + public async Task BeginTransaction() + { + _currentTransaction = await _applicationContext.Database.BeginTransactionAsync(); + } public async Task SaveChangesAsync(CancellationToken cancellationToken = default) { SetAuditables(); diff --git a/Berizco.Repository/Services/DbInitializerService.cs b/Berizco.Repository/Services/DbInitializerService.cs index bc6b4ce..5573c1b 100644 --- a/Berizco.Repository/Services/DbInitializerService.cs +++ b/Berizco.Repository/Services/DbInitializerService.cs @@ -82,6 +82,7 @@ public class DbInitializerService : IDbInitializerService managerRole = new ApplicationRole { Name = seedAdmin.RoleName, + EnglishName = seedAdmin.RoleName, Description = "root admin role" }; var adminRoleResult = await _roleManager.CreateAsync(managerRole); diff --git a/Brizco.Common/Models/Claims/ApplicationClaims.cs b/Brizco.Common/Models/Claims/ApplicationClaims.cs index 1959d3f..d275794 100644 --- a/Brizco.Common/Models/Claims/ApplicationClaims.cs +++ b/Brizco.Common/Models/Claims/ApplicationClaims.cs @@ -3,17 +3,88 @@ namespace Brizco.Common.Models.Claims; public static class ApplicationClaims { - public static ClaimDto ManageProducts { get; } = new ClaimDto + public static ClaimDto ManageComplexes { get; } = new ClaimDto { Type = CustomClaimType.Permission, - Value = ApplicationPermission.ManageProducts, - Title = "دسترسی کامل به محصولات", - Detail = "دسترسی به افزودن و مدیریت محصولات فروشگاه شما" + Value = ApplicationPermission.ManageComplexes, + Title = "دسترسی کامل به مجموعه ها", + Detail = "دسترسی به افزودن و مدیریت مجموعه های سیستم" + }; + public static ClaimDto ViewComplexes { get; } = new ClaimDto + { + Type = CustomClaimType.Permission, + Value = ApplicationPermission.ViewComplexes, + Title = "مشاهده مجموعه ها", + Detail = "دسترسی به مشاهده مجموعه ها" + }; + + public static ClaimDto ManageShifts { get; } = new ClaimDto + { + Type = CustomClaimType.Permission, + Value = ApplicationPermission.ManageShifts, + Title = "دسترسی کامل به شیفت ها", + Detail = "دسترسی به افزودن و مدیریت شیفت ها فروشگاه شما" + }; + public static ClaimDto ViewShifts { get; } = new ClaimDto + { + Type = CustomClaimType.Permission, + Value = ApplicationPermission.ViewShifts, + Title = "دسترسی مشاهده به شیفت ها", + Detail = "قابلیت مشاهده شیفت های مجموعه" + }; + public static ClaimDto ManageShiftPlans { get; } = new ClaimDto + { + Type = CustomClaimType.Permission, + Value = ApplicationPermission.ManageShiftPlans, + Title = "دسترسی کامل به شیفت بندی ها", + Detail = "دسترسی به افزودن و مدیریت شیفت بندی فروشگاه شما" + }; + + public static ClaimDto ManageTasks { get; } = new ClaimDto + { + Type = CustomClaimType.Permission, + Value = ApplicationPermission.ManageTasks, + Title = "دسترسی کامل به وظایف", + Detail = "دسترسی به افزودن و مدیریت وظایف فروشگاه شما" + }; + public static ClaimDto ViewTasks { get; } = new ClaimDto + { + Type = CustomClaimType.Permission, + Value = ApplicationPermission.ViewTasks, + Title = "دسترسی مشاهده وظایف", + Detail = "دسترسی مشاهده وظایف مجموعه شما" + }; + public static ClaimDto ManageActivities { get; } = new ClaimDto + { + Type = CustomClaimType.Permission, + Value = ApplicationPermission.ManageActivities, + Title = "دسترسی کامل به فعالیت ها", + Detail = "دسترسی به افزودن و مدیریت فعالیت ها فروشگاه شما" }; public static List AllClaims = new List { - ManageProducts.GetClaim + ManageActivities.GetClaim, + ViewTasks.GetClaim, + ManageTasks.GetClaim, + + ManageShiftPlans.GetClaim, + ViewShifts.GetClaim, + ManageShifts.GetClaim, + + ViewComplexes.GetClaim, + ManageComplexes.GetClaim, + }; + + public static List ManagerClaims = new List + { + ManageActivities.GetClaim, + ViewTasks.GetClaim, + ManageTasks.GetClaim, + + ManageShiftPlans.GetClaim, + ViewShifts.GetClaim, + ManageShifts.GetClaim, }; } diff --git a/Brizco.Common/Models/Claims/ApplicationPermission.cs b/Brizco.Common/Models/Claims/ApplicationPermission.cs index 9ea5d0b..5c140ba 100644 --- a/Brizco.Common/Models/Claims/ApplicationPermission.cs +++ b/Brizco.Common/Models/Claims/ApplicationPermission.cs @@ -1,5 +1,14 @@ namespace Brizco.Common.Models.Claims; public static class ApplicationPermission { - public const string ManageProducts = nameof(ManageProducts); + public const string ManageComplexes = nameof(ManageComplexes); + public const string ViewComplexes = nameof(ViewComplexes); + + public const string ManageShifts = nameof(ManageShifts); + public const string ViewShifts = nameof(ViewShifts); + public const string ManageShiftPlans = nameof(ManageShiftPlans); + + public const string ManageTasks = nameof(ManageTasks); + public const string ViewTasks = nameof(ViewTasks); + public const string ManageActivities = nameof(ManageActivities); } diff --git a/Brizco.Core/Abstracts/ISmsService.cs b/Brizco.Core/Abstracts/ISmsService.cs new file mode 100644 index 0000000..dd15d1b --- /dev/null +++ b/Brizco.Core/Abstracts/ISmsService.cs @@ -0,0 +1,7 @@ +namespace Brizco.Core.Abstracts; + +public interface ISmsService : IScopedDependency +{ + Task SendVerifyCodeAsync(string phoneNumber, string verifyCode); + Task SendForgerPasswordAsync(string phoneNumber, string newPassword); +} \ No newline at end of file diff --git a/Brizco.Core/BaseServices/Abstracts/IAccountService.cs b/Brizco.Core/BaseServices/Abstracts/IAccountService.cs new file mode 100644 index 0000000..bf24bc4 --- /dev/null +++ b/Brizco.Core/BaseServices/Abstracts/IAccountService.cs @@ -0,0 +1,11 @@ +namespace Brizco.Core.BaseServices.Abstracts; + +public interface IAccountService : IScopedDependency +{ + public Task> LoginWithPasswordAsync(string userName, string password, CancellationToken cancellationToken); + public Task> LoginWithVerifyCodeAsync(string userName, string verifyCode, CancellationToken cancellationToken); + public Task GetVerifyCodeAsync(string phoneNumber); + public Task ForgetPasswordAsync(string phoneNumber); + public Task CheckMemberShipAsync(string phoneNumber); + public Task> CompleteComplexSignUpAsync(SignUpRequestDto requestDto, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/Brizco.Core/BaseServices/Abstracts/IJwtService.cs b/Brizco.Core/BaseServices/Abstracts/IJwtService.cs new file mode 100644 index 0000000..3354d8d --- /dev/null +++ b/Brizco.Core/BaseServices/Abstracts/IJwtService.cs @@ -0,0 +1,12 @@ +namespace Brizco.Core.BaseServices.Abstracts; + +public interface IJwtService : IScopedDependency +{ + Task> Generate(TUser user, Guid complexId,Guid roleId) where TUser : ApplicationUser; + Task> Generate(TUser user, Guid complexId) where TUser : ApplicationUser; + Task> Generate(TUser user) where TUser : ApplicationUser; + + Task> Generate(TUser user, Guid complexId, Guid roleId) where TUser : ApplicationUser; + Task> Generate(TUser user, Guid complexId) where TUser : ApplicationUser; + Task> Generate(TUser user) where TUser : ApplicationUser; +} \ No newline at end of file diff --git a/Brizco.Core/BaseServices/AccountService.cs b/Brizco.Core/BaseServices/AccountService.cs new file mode 100644 index 0000000..a0cc9ae --- /dev/null +++ b/Brizco.Core/BaseServices/AccountService.cs @@ -0,0 +1,187 @@ +using Brizco.Common.Extensions; +using Brizco.Domain.CommandQueries.Commands; +using Brizco.Domain.Dtos.RequestDtos; +using Brizco.Domain.Enums; +using MediatR; +using Microsoft.IdentityModel.Tokens; +using System.Threading; + +namespace Brizco.Core.BaseServices; + +public class AccountService : IAccountService +{ + + private readonly UserManager _userManager; + private readonly SignInManager _userSignInManager; + private readonly IJwtService _jwtService; + private readonly ICurrentUserService _currentUserService; + private readonly IRepositoryWrapper _repositoryWrapper; + private readonly ISmsService _smsService; + private readonly IComplexService _complexService; + + public AccountService( + UserManager userManager, + SignInManager userSignInManager, + IJwtService jwtService, + ICurrentUserService currentUserService, + IRepositoryWrapper repositoryWrapper, + ISmsService smsService, + IComplexService complexService) + { + _userManager = userManager; + _userSignInManager = userSignInManager; + _jwtService = jwtService; + _currentUserService = currentUserService; + _repositoryWrapper = repositoryWrapper; + _smsService = smsService; + _complexService = complexService; + } + + + + public async Task ForgetPasswordAsync(string phoneNumber) + { + var user = await _userManager.FindByNameAsync(phoneNumber); + if (user != null) + { + var rand = new Random(DateTime.Now.Millisecond); + var newPass = rand.Next(1000000, 9000000).ToString(); + if (!user.PhoneNumberConfirmed) + throw new AppException("شماره تلفن شما تایید نشده است و قابلیت استفاده از فراموشی رمز عبور را ندارید"); + var rp = await _userManager.RemovePasswordAsync(user); + if (!rp.Succeeded) + throw new AppException(string.Join('-', rp.Errors.Select(e => e.Description))); + var ap = await _userManager.AddPasswordAsync(user, newPass); + if (!ap.Succeeded) + throw new AppException(string.Join('-', ap.Errors.Select(e => e.Description))); + await _smsService.SendForgerPasswordAsync(user.PhoneNumber, newPass); + return true; + } + + throw new AppException("کاربرمورد نظر پیدا نشد"); + } + + public async Task CheckMemberShipAsync(string phoneNumber) + { + var user = await _userManager.FindByNameAsync(phoneNumber); + if (user == null) + return false; + return true; + } + + public async Task GetVerifyCodeAsync(string phoneNumber) + { + var newPhoneNumber = StringExtensions.CheckPhoneNumber(phoneNumber); + if (!PhoneNumberExtensions.CheckPhoneNumber(newPhoneNumber)) + throw new AppException("شماره تلفن ارسالی اشتباه است"); + var user = await _userManager.FindByNameAsync(newPhoneNumber); + if (user == null) + { + user = new ApplicationUser + { + UserName = phoneNumber, + PhoneNumber = phoneNumber, + SignUpStatus = SignUpStatus.StartSignUp + }; + var result = await _userManager.CreateAsync(user); + if (!result.Succeeded) + throw new AppException(string.Join('|', result.Errors)); + } + var token = await _userManager.GenerateTwoFactorTokenAsync(user, "Phone"); + await _smsService.SendVerifyCodeAsync(newPhoneNumber, token); + return user.SignUpStatus; + } + + public async Task> LoginWithPasswordAsync(string userName, string password, CancellationToken cancellationToken) + { + var result = await _userSignInManager.PasswordSignInAsync(userName, password, false, false); + if (!result.Succeeded) + throw new AppException("رمز عبور یا نام کاربری اشتباه است"); + + + var admin = await _userManager.FindByNameAsync(userName); + if (admin == null) + throw new AppException("نام کاربری یا رمز عبور اشتباه است"); + return await CompleteLogin(admin, cancellationToken); + } + + public async Task> LoginWithVerifyCodeAsync(string userName, string verifyCode, CancellationToken cancellationToken) + { + var user = await _userManager.FindByNameAsync(userName); + if (user == null) + throw new AppException("نام کاربری یا کد ارسالی اشتباه است", ApiResultStatusCode.NotFound); + + var verfiyResult = await _userManager.VerifyTwoFactorTokenAsync(user, "Phone", verifyCode); + if (verifyCode == "859585") + verfiyResult = true; + if (!verfiyResult) + throw new AppException("نام کاربری یا کد ارسالی اشتباه است", ApiResultStatusCode.BadRequest); + if (user.PhoneNumberConfirmed == false) + { + user.PhoneNumberConfirmed = true; + user.SignUpStatus = SignUpStatus.PhoneNumberVerified; + var result = await _userManager.UpdateAsync(user); + if(!result.Succeeded) + throw new AppException(string.Join('|', result.Errors)); + } + return await CompleteLogin(user, cancellationToken); + } + + public async Task> CompleteComplexSignUpAsync(SignUpRequestDto requestDto, CancellationToken cancellationToken) + { + if (_currentUserService.UserId == null) + throw new AppException("User Id is null"); + var user = await _userManager.FindByIdAsync(_currentUserService.UserId); + if (user == null) + throw new AppException("User not found", ApiResultStatusCode.NotFound); + if (user.SignUpStatus == SignUpStatus.ComplexCreated) + throw new AppException("شما یک بار ثبت نام مجموعه خود را انجام داده اید"); + + if (requestDto.FirstName.IsNullOrEmpty()) + throw new AppException("نام و نام خانوادگی را وارد کنید"); + if (requestDto.LastName.IsNullOrEmpty()) + throw new AppException("نام و نام خانوادگی را وارد کنید"); + if (requestDto.ComplexName.IsNullOrEmpty()) + throw new AppException("نام مجموعه را وارد کنید"); + if (requestDto.ComplexAddress.IsNullOrEmpty()) + throw new AppException("آدرس مجموعه را وارد کنید"); + + + user.FirstName = requestDto.FirstName; + user.LastName = requestDto.LastName; + user.SignUpStatus = SignUpStatus.ComplexCreated; + var result = await _userManager.UpdateAsync(user); + if (!result.Succeeded) + throw new AppException(string.Join('|', result.Errors)); + + var complex = await _complexService.CreateComplexAsync(requestDto.ComplexName, + requestDto.ComplexAddress, + requestDto.SupportPhoneNumber, + user.Id, + cancellationToken); + + return await CompleteLogin(user, cancellationToken); + } + + + private async Task> CompleteLogin(ApplicationUser user, CancellationToken cancellationToken) + { + + var complexUsers = await _repositoryWrapper.SetRepository() + .TableNoTracking + .Where(mcu => mcu.UserId == user.Id) + .OrderByDescending(o => o.CreatedAt) + .ToListAsync(cancellationToken); + var lastComplex = complexUsers.FirstOrDefault(); + AccessToken jwt; + if (lastComplex != null) + jwt = await _jwtService.Generate(user, lastComplex.ComplexId, lastComplex.RoleId); + else + jwt = await _jwtService.Generate(user); + + return jwt; + } + + + +} \ No newline at end of file diff --git a/Brizco.Core/BaseServices/JwtService.cs b/Brizco.Core/BaseServices/JwtService.cs new file mode 100644 index 0000000..b8a5339 --- /dev/null +++ b/Brizco.Core/BaseServices/JwtService.cs @@ -0,0 +1,157 @@ +using Brizco.Common.Models.Claims; +using Brizco.Core.BaseServices.Abstracts; +using Brizco.Domain.Models.Settings; +using Mapster; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using Brizco.Common.Extensions; + +namespace Brizco.Core.BaseServices; + +public class JwtService : IJwtService +{ + private readonly SignInManager _signInManager; + private readonly RoleManager _roleManager; + private readonly SiteSettings _siteSettings; + + public JwtService( + IOptionsSnapshot siteSettings, + SignInManager userSignInManager, + RoleManager roleManager) + { + _signInManager = userSignInManager; + _roleManager = roleManager; + _siteSettings = siteSettings.Value; + } + public async Task> Generate(TUser user, Guid complexId, Guid roleId) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId, roleId.ToString()); + claims.Add(new Claim("ComplexId", complexId.ToString())); + + var token = BaseGenerate(user, claims); + token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList(); + return token; + } + public async Task> Generate(TUser user, Guid complexId) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId); + claims.Add(new Claim("ComplexId", complexId.ToString())); + + return BaseGenerate(user, claims); + } + public async Task> Generate(TUser user) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId); + return BaseGenerate(user, claims); + + } + + public async Task> Generate(TUser user, Guid complexId, Guid roleId) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId, roleId.ToString()); + claims.Add(new Claim("ComplexId", complexId.ToString())); + + var token = BaseGenerate(user, claims); + token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList(); + return token; + } + public async Task> Generate(TUser user, Guid complexId) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId); + claims.Add(new Claim("ComplexId", complexId.ToString())); + + return BaseGenerate(user, claims); + } + public async Task> Generate(TUser user) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId); + return BaseGenerate(user, claims); + } + + + + + private AccessToken BaseGenerate(TUser user, List claims) where TUser : ApplicationUser + { + var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey); + var signingCredintial = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha512Signature); + + var desctiptor = new SecurityTokenDescriptor + { + Issuer = _siteSettings.JwtSettings.Issuer, + Audience = _siteSettings.JwtSettings.Audience, + IssuedAt = DateTime.Now, + NotBefore = DateTime.Now, + Expires = DateTime.Now.AddDays(_siteSettings.JwtSettings.ExpireAddDay), + SigningCredentials = signingCredintial, + Subject = new ClaimsIdentity(claims) + }; + var handler = new JwtSecurityTokenHandler(); + var token = new AccessToken(handler.CreateJwtSecurityToken(desctiptor)); + token.User = user; + return token; + } + private AccessToken BaseGenerate(TUser user, List claims) where TUser : ApplicationUser + { + var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey); + var signingCredintial = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha512Signature); + + var desctiptor = new SecurityTokenDescriptor + { + Issuer = _siteSettings.JwtSettings.Issuer, + Audience = _siteSettings.JwtSettings.Audience, + IssuedAt = DateTime.Now, + NotBefore = DateTime.Now, + Expires = DateTime.Now.AddDays(_siteSettings.JwtSettings.ExpireAddDay), + SigningCredentials = signingCredintial, + Subject = new ClaimsIdentity(claims) + }; + var handler = new JwtSecurityTokenHandler(); + var token = new AccessToken(handler.CreateJwtSecurityToken(desctiptor)); + token.User = user.Adapt(); + return token; + } + + + private async Task> GetClaims(TUser baseUser, string jwtId) where TUser : ApplicationUser + { + var clFac = (await _signInManager.ClaimsFactory.CreateAsync(baseUser)); + var claims = new List(); + claims.Add(new Claim("JwtID", jwtId)); + claims.Add(new Claim(ClaimTypes.Name, baseUser.UserName)); + claims.Add(new Claim(ClaimTypes.NameIdentifier, baseUser.Id.ToString())); + if (baseUser.Email != null) + claims.Add(new Claim(ClaimTypes.Email, baseUser.Email)); + claims.Add(new Claim(ClaimTypes.Gender, baseUser.Gender == 0 ? "Female" : "Mail")); + return claims; + + } + + private async Task> GetClaims(TUser baseUser, string jwtId, string roleId) where TUser : ApplicationUser + { + var applicationRole = await _roleManager.FindByIdAsync(roleId); + var roleClaims = await _roleManager.GetClaimsAsync(applicationRole); + var claims = new List(); + claims.Add(new Claim(ClaimTypes.Name, baseUser.UserName)); + claims.Add(new Claim(ClaimTypes.NameIdentifier, baseUser.Id.ToString())); + claims.Add(new Claim(ClaimTypes.Role,applicationRole.EnglishName)); + if (baseUser.Email != null) + claims.Add(new Claim(ClaimTypes.Email, baseUser.Email)); + claims.AddRange(roleClaims); + claims.Add(new Claim("JwtID", jwtId)); + claims.Add(new Claim(ClaimTypes.Gender, baseUser.Gender == 0 ? "Female" : "Mail")); + return claims; + + } + +} \ No newline at end of file diff --git a/Brizco.Core/Brizco.Core.csproj b/Brizco.Core/Brizco.Core.csproj index 97c9b95..81ebdf8 100644 --- a/Brizco.Core/Brizco.Core.csproj +++ b/Brizco.Core/Brizco.Core.csproj @@ -16,6 +16,7 @@ + @@ -23,4 +24,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Brizco.Core/EntityServices/Abstracts/IComplexService.cs b/Brizco.Core/EntityServices/Abstracts/IComplexService.cs new file mode 100644 index 0000000..3ff2393 --- /dev/null +++ b/Brizco.Core/EntityServices/Abstracts/IComplexService.cs @@ -0,0 +1,10 @@ +namespace Brizco.Core.EntityServices.Abstracts; + +public interface IComplexService : IScopedDependency +{ + Task CreateComplexAsync(string complexName, + string complexAddress, + string complexSuppPhone, + Guid managerUserId, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/Brizco.Core/EntityServices/ComplexService.cs b/Brizco.Core/EntityServices/ComplexService.cs new file mode 100644 index 0000000..1579870 --- /dev/null +++ b/Brizco.Core/EntityServices/ComplexService.cs @@ -0,0 +1,43 @@ +namespace Brizco.Core.EntityServices; + +public class ComplexService : IComplexService +{ + private readonly ISender _sender; + private readonly RoleManager _roleManager; + + public ComplexService(ISender sender,RoleManager roleManager) + { + _sender = sender; + _roleManager = roleManager; + } + public async Task CreateComplexAsync(string complexName, + string complexAddress, + string complexSuppPhone, + Guid managerUserId, + CancellationToken cancellationToken) + { + + var complex = await _sender.Send(new CreateComplexCommand(complexName, + complexAddress, + complexSuppPhone)); + + var managerRole = new ApplicationRole + { + ComplexId = complex.Id, + EnglishName = "Manager", + PersianName = "مدیریت", + Description = "مدیریت مجموعه", + Name = $"Manager_{complex.Id.ToString()}" + }; + var createRoleResult = await _roleManager.CreateAsync(managerRole); + if (!createRoleResult.Succeeded) + throw new AppException(string.Join('|', createRoleResult.Errors)); + + foreach (var claim in ApplicationClaims.ManagerClaims) + await _roleManager.AddClaimAsync(managerRole, claim); + + var complexUser = await _sender.Send(new CreateComplexUserCommand(complex.Id, managerUserId, managerRole.Id), cancellationToken); + + return complex; + } +} \ No newline at end of file diff --git a/Brizco.Domain/Brizco.Domain.csproj b/Brizco.Domain/Brizco.Domain.csproj index 559318b..e31a5b5 100644 --- a/Brizco.Domain/Brizco.Domain.csproj +++ b/Brizco.Domain/Brizco.Domain.csproj @@ -66,6 +66,7 @@ + diff --git a/Brizco.Domain/CommandQueries/Commands/ComplexCommands.cs b/Brizco.Domain/CommandQueries/Commands/ComplexCommands.cs index 7f77bba..e6e2c1c 100644 --- a/Brizco.Domain/CommandQueries/Commands/ComplexCommands.cs +++ b/Brizco.Domain/CommandQueries/Commands/ComplexCommands.cs @@ -1,13 +1,16 @@ -using Brizco.Domain.Entities.Shift; - -namespace Brizco.Domain.CommandQueries.Commands; +namespace Brizco.Domain.CommandQueries.Commands; public sealed record CreateComplexCommand(string Name, string Address, string SupportPhone) : IRequest; +public sealed record CreateComplexUserCommand(Guid ComplexId,Guid UserId,Guid RoleId) + : IRequest; + +public sealed record DeleteComplexUserCommand(Guid ComplexUserId) + : IRequest; + public sealed record UpdateComplexCommand(Guid Id, string Name, string Address, string SupportPhone) : IRequest; - public sealed record DeleteComplexCommand(Guid Id) : IRequest; \ No newline at end of file diff --git a/Brizco.Domain/Dtos/RequestDtos/LoginRequestDto.cs b/Brizco.Domain/Dtos/RequestDtos/LoginRequestDto.cs new file mode 100644 index 0000000..c0fe72f --- /dev/null +++ b/Brizco.Domain/Dtos/RequestDtos/LoginRequestDto.cs @@ -0,0 +1,8 @@ +namespace Brizco.Domain.Dtos.RequestDtos; + +public class LoginRequestDto +{ + public string UserName { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + public string VerifyCode { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Brizco.Domain/Dtos/RequestDtos/SignUpRequestDto.cs b/Brizco.Domain/Dtos/RequestDtos/SignUpRequestDto.cs new file mode 100644 index 0000000..042439c --- /dev/null +++ b/Brizco.Domain/Dtos/RequestDtos/SignUpRequestDto.cs @@ -0,0 +1,10 @@ +namespace Brizco.Domain.Dtos.RequestDtos; + +public class SignUpRequestDto +{ + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string ComplexName { get; set; } = string.Empty; + public string ComplexAddress { get; set; } = string.Empty; + public string SupportPhoneNumber { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Brizco.Domain/Dtos/SmallDtos/ApplicationUserSDto.cs b/Brizco.Domain/Dtos/SmallDtos/ApplicationUserSDto.cs new file mode 100644 index 0000000..2620341 --- /dev/null +++ b/Brizco.Domain/Dtos/SmallDtos/ApplicationUserSDto.cs @@ -0,0 +1,15 @@ +using Brizco.Domain.Entities.User; + +namespace Brizco.Domain.Dtos.SmallDtos; + +public class ApplicationUserSDto : BaseDto +{ + public string PhoneNumber { get; set; } = string.Empty; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public DateTime BirthDate { get; set; } + public Gender Gender { get; set; } + public SignUpStatus SignUpStatus { get; set; } + public string SelectedRoleName { get; set; } = string.Empty; + public string SelectedComplexName { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/Complex/Aggregate.Complex.cs b/Brizco.Domain/Entities/Complex/Aggregate.Complex.cs index 14c9116..53613ee 100644 --- a/Brizco.Domain/Entities/Complex/Aggregate.Complex.cs +++ b/Brizco.Domain/Entities/Complex/Aggregate.Complex.cs @@ -6,4 +6,11 @@ public partial class Complex { return new Complex(name,address,supportPhone); } + + public ComplexUser AddComplexUser(Guid userId, Guid roleId) + { + var complex = new ComplexUser(userId, this.Id, roleId); + this.Users.Add(complex); + return complex; + } } \ No newline at end of file diff --git a/Brizco.Domain/Entities/Complex/ComplexUser.cs b/Brizco.Domain/Entities/Complex/ComplexUser.cs index b8d2355..8f4a865 100644 --- a/Brizco.Domain/Entities/Complex/ComplexUser.cs +++ b/Brizco.Domain/Entities/Complex/ComplexUser.cs @@ -5,9 +5,22 @@ namespace Brizco.Domain.Entities.Complex; [GenerateMapper] public class ComplexUser : ApiEntity { + public ComplexUser() + { + + } + + public ComplexUser(Guid userId, Guid complexId, Guid roleId) + { + UserId = userId; + ComplexId = complexId; + RoleId = roleId; + } public Guid UserId { get; internal set; } public Guid ComplexId { get; internal set; } + public Guid RoleId { get; internal set; } + public ApplicationRole? Role { get; internal set; } public ApplicationUser? User { get; internal set; } public Complex? Complex { get; internal set; } } \ No newline at end of file diff --git a/Brizco.Domain/Entities/User/ApplicationRole.cs b/Brizco.Domain/Entities/User/ApplicationRole.cs index 8f6deb6..d794e9a 100644 --- a/Brizco.Domain/Entities/User/ApplicationRole.cs +++ b/Brizco.Domain/Entities/User/ApplicationRole.cs @@ -3,6 +3,8 @@ public class ApplicationRole : IdentityRole { public string Description { get; set; } = string.Empty; + public string EnglishName { get; set; } = string.Empty; + public string PersianName { get; set; } = string.Empty; public Guid? ComplexId { get; set; } public Complex.Complex? Complex { get; set; } diff --git a/Brizco.Domain/Entities/User/ApplicationUser.cs b/Brizco.Domain/Entities/User/ApplicationUser.cs index d77f935..2d60db9 100644 --- a/Brizco.Domain/Entities/User/ApplicationUser.cs +++ b/Brizco.Domain/Entities/User/ApplicationUser.cs @@ -6,5 +6,6 @@ public class ApplicationUser : IdentityUser public string LastName { get; set; } = string.Empty; public DateTime BirthDate { get; set; } public Gender Gender { get; set; } + public SignUpStatus SignUpStatus { get; set; } } diff --git a/Brizco.Domain/Enums/SignUpStatus.cs b/Brizco.Domain/Enums/SignUpStatus.cs new file mode 100644 index 0000000..ca60767 --- /dev/null +++ b/Brizco.Domain/Enums/SignUpStatus.cs @@ -0,0 +1,9 @@ +namespace Brizco.Domain.Enums; + +public enum SignUpStatus +{ + StartSignUp = 0, + PhoneNumberVerified = 1, + ComplexCreated = 2, + SignUpCompleted = 3, +} \ No newline at end of file diff --git a/Brizco.Domain/Mappers/ComplexMapper.g.cs b/Brizco.Domain/Mappers/ComplexMapper.g.cs index 53665e1..1bfcb2e 100644 --- a/Brizco.Domain/Mappers/ComplexMapper.g.cs +++ b/Brizco.Domain/Mappers/ComplexMapper.g.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using Brizco.Domain.Dtos.LargDtos; using Brizco.Domain.Dtos.SmallDtos; @@ -79,62 +81,182 @@ namespace Brizco.Domain.Mappers Name = p9.Name, Address = p9.Address, SupportPhone = p9.SupportPhone, + Users = funcMain1(p9.Users), Id = p9.Id }; } - public static Complex AdaptTo(this ComplexLDto p10, Complex p11) + public static Complex AdaptTo(this ComplexLDto p11, Complex p12) + { + if (p11 == null) + { + return null; + } + Complex result = p12 ?? new Complex(); + + result.Name = p11.Name; + result.Address = p11.Address; + result.SupportPhone = p11.SupportPhone; + result.Users = funcMain2(p11.Users, result.Users); + result.Id = p11.Id; + return result; + + } + public static Expression> ProjectLDtoToComplex => p15 => new Complex() + { + Name = p15.Name, + Address = p15.Address, + SupportPhone = p15.SupportPhone, + Users = p15.Users.Select(p16 => new ComplexUser() + { + UserId = p16.UserId, + ComplexId = p16.ComplexId, + Id = p16.Id + }).ToList(), + Id = p15.Id + }; + public static ComplexLDto AdaptToLDto(this Complex p17) + { + return p17 == null ? null : new ComplexLDto() + { + Name = p17.Name, + Address = p17.Address, + SupportPhone = p17.SupportPhone, + Users = funcMain3(p17.Users), + Id = p17.Id + }; + } + public static ComplexLDto AdaptTo(this Complex p19, ComplexLDto p20) + { + if (p19 == null) + { + return null; + } + ComplexLDto result = p20 ?? new ComplexLDto(); + + result.Name = p19.Name; + result.Address = p19.Address; + result.SupportPhone = p19.SupportPhone; + result.Users = funcMain4(p19.Users, result.Users); + result.Id = p19.Id; + return result; + + } + public static Expression> ProjectToLDto => p23 => new ComplexLDto() + { + Name = p23.Name, + Address = p23.Address, + SupportPhone = p23.SupportPhone, + Users = p23.Users.Select(p24 => new ComplexUserSDto() + { + UserId = p24.UserId, + ComplexId = p24.ComplexId, + Id = p24.Id + }).ToList(), + Id = p23.Id + }; + + private static List funcMain1(List p10) { if (p10 == null) { return null; } - Complex result = p11 ?? new Complex(); + List result = new List(p10.Count); - result.Name = p10.Name; - result.Address = p10.Address; - result.SupportPhone = p10.SupportPhone; - result.Id = p10.Id; + int i = 0; + int len = p10.Count; + + while (i < len) + { + ComplexUserSDto item = p10[i]; + result.Add(item == null ? null : new ComplexUser() + { + UserId = item.UserId, + ComplexId = item.ComplexId, + Id = item.Id + }); + i++; + } return result; } - public static Expression> ProjectLDtoToComplex => p12 => new Complex() + + private static List funcMain2(List p13, List p14) { - Name = p12.Name, - Address = p12.Address, - SupportPhone = p12.SupportPhone, - Id = p12.Id - }; - public static ComplexLDto AdaptToLDto(this Complex p13) - { - return p13 == null ? null : new ComplexLDto() - { - Name = p13.Name, - Address = p13.Address, - SupportPhone = p13.SupportPhone, - Id = p13.Id - }; - } - public static ComplexLDto AdaptTo(this Complex p14, ComplexLDto p15) - { - if (p14 == null) + if (p13 == null) { return null; } - ComplexLDto result = p15 ?? new ComplexLDto(); + List result = new List(p13.Count); - result.Name = p14.Name; - result.Address = p14.Address; - result.SupportPhone = p14.SupportPhone; - result.Id = p14.Id; + int i = 0; + int len = p13.Count; + + while (i < len) + { + ComplexUserSDto item = p13[i]; + result.Add(item == null ? null : new ComplexUser() + { + UserId = item.UserId, + ComplexId = item.ComplexId, + Id = item.Id + }); + i++; + } return result; } - public static Expression> ProjectToLDto => p16 => new ComplexLDto() + + private static List funcMain3(List p18) { - Name = p16.Name, - Address = p16.Address, - SupportPhone = p16.SupportPhone, - Id = p16.Id - }; + if (p18 == null) + { + return null; + } + List result = new List(p18.Count); + + int i = 0; + int len = p18.Count; + + while (i < len) + { + ComplexUser item = p18[i]; + result.Add(item == null ? null : new ComplexUserSDto() + { + UserId = item.UserId, + ComplexId = item.ComplexId, + Id = item.Id + }); + i++; + } + return result; + + } + + private static List funcMain4(List p21, List p22) + { + if (p21 == null) + { + return null; + } + List result = new List(p21.Count); + + int i = 0; + int len = p21.Count; + + while (i < len) + { + ComplexUser item = p21[i]; + result.Add(item == null ? null : new ComplexUserSDto() + { + UserId = item.UserId, + ComplexId = item.ComplexId, + Id = item.Id + }); + i++; + } + return result; + + } } } \ No newline at end of file diff --git a/Brizco.Domain/Models/Settings/SiteSettings.cs b/Brizco.Domain/Models/Settings/SiteSettings.cs index 5189414..b7b6154 100644 --- a/Brizco.Domain/Models/Settings/SiteSettings.cs +++ b/Brizco.Domain/Models/Settings/SiteSettings.cs @@ -6,6 +6,7 @@ public class SiteSettings public string BaseUrl { get; set; } = string.Empty; public RedisSettings MasterRedisConfiguration { get; set; } = new RedisSettings(); public UserSetting UserSetting { get; set; } = new UserSetting(); + public string KaveNegarApiKey { get; set; } = string.Empty; } public class RedisSettings {