feat : complex signup , unit of work , sms service

complete complex sign up , add new methods to unit of work , add sms service
release/production
Amir Hossein Khademi 2023-09-20 11:54:12 +03:30
parent 034b5270bb
commit 02abd6ddf3
62 changed files with 1392 additions and 256 deletions

View File

@ -59,6 +59,7 @@
<Using Include="Brizco.Common.Models.Mapper" /> <Using Include="Brizco.Common.Models.Mapper" />
<Using Include="Brizco.Domain.CommandQueries.Commands" /> <Using Include="Brizco.Domain.CommandQueries.Commands" />
<Using Include="Brizco.Domain.CommandQueries.Queries" /> <Using Include="Brizco.Domain.CommandQueries.Queries" />
<Using Include="Brizco.Domain.Dtos.RequestDtos" />
<Using Include="Brizco.Domain.Entities" /> <Using Include="Brizco.Domain.Entities" />
<Using Include="Brizco.Repository.Repositories.Base.Contracts" /> <Using Include="Brizco.Repository.Repositories.Base.Contracts" />
<Using Include="Carter" /> <Using Include="Carter" />

View File

@ -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<IResult> SignUpComplex([FromBody] SignUpRequestDto request, IAccountService accountService, CancellationToken cancellationToken) =>
TypedResults.Ok(await accountService.CompleteComplexSignUpAsync(request,cancellationToken));
public async Task<IResult> LoginWithPassword([FromBody] LoginRequestDto loginRequestDto,IAccountService accountService, CancellationToken cancellationToken) =>
TypedResults.Ok(await accountService.LoginWithPasswordAsync(loginRequestDto.UserName, loginRequestDto.Password,cancellationToken));
public async Task<IResult> LoginWithVerifyCode([FromBody] LoginRequestDto loginRequestDto, IAccountService accountService, CancellationToken cancellationToken) =>
TypedResults.Ok(await accountService.LoginWithVerifyCodeAsync(loginRequestDto.UserName, loginRequestDto.VerifyCode,cancellationToken));
public async Task<IResult> GetVerifyCodeCode([FromQuery] string phoneNumber, IAccountService accountService) =>
TypedResults.Ok(await accountService.GetVerifyCodeAsync(phoneNumber));
public async Task<IResult> ForgetPassword([FromQuery] string phoneNumber, IAccountService accountService) =>
TypedResults.Ok(await accountService.ForgetPasswordAsync(phoneNumber));
public async Task<IResult> LoginSwagger([FromForm] TokenRequest tokenRequest, IAccountService accountService, CancellationToken cancellationToken)
=> TypedResults.Json(await accountService.LoginWithPasswordAsync(tokenRequest.username, tokenRequest.password,cancellationToken));
}

View File

@ -96,11 +96,14 @@ var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.UseSwagger(); app.UseCustomSwagger(siteSetting.BaseUrl);
app.UseSwaggerUI(); //app.UseSwagger();
//app.UseSwaggerUI();
} }
app.UseAuthorization(); app.UseAuthorization();
app.UseAuthentication();
app.UseCors(x => x app.UseCors(x => x
.SetIsOriginAllowed(origin => true) .SetIsOriginAllowed(origin => true)
.AllowAnyMethod() .AllowAnyMethod()
@ -109,7 +112,6 @@ app.UseCors(x => x
app.UseExceptionHandlerMiddleware(); app.UseExceptionHandlerMiddleware();
app.UseCustomSwagger(siteSetting.BaseUrl);
app.MapCarter(); app.MapCarter();
app.UseStaticFiles(); app.UseStaticFiles();
await app.InitialDb(); await app.InitialDb();

View File

@ -11,7 +11,25 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Models\" /> <PackageReference Include="Refit" Version="6.3.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\RestApi\KaveNegar\" />
<Folder Include="Services\" />
</ItemGroup>
<ItemGroup>
<Using Include="Brizco.Common.Models" />
<Using Include="Brizco.Common.Models.Api" />
<Using Include="Brizco.Common.Models.Exception" />
<Using Include="Brizco.Core.Abstracts" />
<Using Include="Brizco.Domain.Models.Settings" />
<Using Include="Brizco.Infrastructure.Models" />
<Using Include="Brizco.Infrastructure.RestServices" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Microsoft.Extensions.Options" />
<Using Include="Refit" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,6 @@
namespace Brizco.Infrastructure.Models;
public static class RestAddress
{
public static string BaseKaveNegar { get => "https://api.kavenegar.com/v1/"; }
}

View File

@ -0,0 +1,7 @@
namespace Brizco.Infrastructure.Models.RestApi.KaveNegar;
public class KaveNegarResponse
{
public KaveNegarReturn Return { get; set; }
public KaveNegarResponseEntry[] entries { get; set; }
}

View File

@ -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; }
}

View File

@ -0,0 +1,7 @@
namespace Brizco.Infrastructure.Models.RestApi.KaveNegar;
public class KaveNegarReturn
{
public int status { get; set; }
public string message { get; set; }
}

View File

@ -0,0 +1,12 @@
using Brizco.Infrastructure.Models.RestApi.KaveNegar;
namespace Brizco.Infrastructure.RestServices;
public interface IKaveNegarRestApi
{
[Post("/{apiKey}/verify/lookup.json")]
Task<KaveNegarResponse> 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<KaveNegarResponse> SendSms(string apiKey, [Query] string receptor, [Query] string message, [Query] string sender);
}

View File

@ -0,0 +1,11 @@
namespace Brizco.Infrastructure.RestServices;
public interface IRestApiWrapper : IScopedDependency
{
IKaveNegarRestApi KaveNegarRestApi { get; }
}
public class RestApiWrapper : IRestApiWrapper
{
public IKaveNegarRestApi KaveNegarRestApi => RestService.For<IKaveNegarRestApi>(RestAddress.BaseKaveNegar);
}

View File

@ -0,0 +1,33 @@
namespace Brizco.Infrastructure.Services;
public class SmsService : ISmsService
{
private readonly IRestApiWrapper _restApiWrapper;
private readonly ILogger<SmsService> _logger;
private readonly SiteSettings _siteSettings;
public SmsService(IRestApiWrapper restApiWrapper,
IOptionsSnapshot<SiteSettings> optionsSnapshot,
ILogger<SmsService> 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);
}
}

View File

@ -29,8 +29,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="Models\" /> <Folder Include="Models\" />
<Folder Include="Extensions\" /> <Folder Include="Extensions\" />
<Folder Include="Repositories\Base\" />
<Folder Include="Repositories\UnitOfWork\" />
<Folder Include="Services\Contracts\" /> <Folder Include="Services\Contracts\" />
</ItemGroup> </ItemGroup>

View File

@ -17,7 +17,9 @@ public class CreateActivityCommandHandler : IRequestHandler<CreateActivityComman
throw new AppException("ComplexId is null", ApiResultStatusCode.NotFound); throw new AppException("ComplexId is null", ApiResultStatusCode.NotFound);
if (!Guid.TryParse(_currentUserService.ComplexId, out Guid complexId)) if (!Guid.TryParse(_currentUserService.ComplexId, out Guid complexId))
throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound); throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound);
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var task = Domain.Entities.Task.Activity var task = Domain.Entities.Task.Activity
.Create( .Create(
request.Status, request.Status,
@ -46,7 +48,8 @@ public class CreateActivityCommandHandler : IRequestHandler<CreateActivityComman
if (task.IsRelatedToRole) if (task.IsRelatedToRole)
{ {
if (request.Roles.Count == 0) if (request.Roles.Count == 0)
throw new AppException("اگر فعالیت برای یک گروه نقش انتخاب شده باشد باید لیست نقش ها را ارسال نمایید"); throw new AppException(
"اگر فعالیت برای یک گروه نقش انتخاب شده باشد باید لیست نقش ها را ارسال نمایید");
task.AddShift(request.Roles.ToArray()); task.AddShift(request.Roles.ToArray());
} }
@ -59,6 +62,13 @@ public class CreateActivityCommandHandler : IRequestHandler<CreateActivityComman
_repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>().Add(task); _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>().Add(task);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return task.AdaptToLDto(); return task.AdaptToLDto();
} }
catch (Exception)
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
} }

View File

@ -10,6 +10,9 @@ public class DeleteActivityCommandHandler : IRequestHandler<DeleteActivityComman
} }
public async Task<bool> Handle(DeleteActivityCommand request, CancellationToken cancellationToken) public async Task<bool> Handle(DeleteActivityCommand request, CancellationToken cancellationToken)
{ {
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>() var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>()
.TableNoTracking .TableNoTracking
.FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken);
@ -19,6 +22,13 @@ public class DeleteActivityCommandHandler : IRequestHandler<DeleteActivityComman
.Delete(task); .Delete(task);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return true; return true;
} }
catch (Exception )
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
} }

View File

@ -12,7 +12,7 @@ public class GetActivitiesQueryHandler : IRequestHandler<GetActivitiesQuery, Lis
{ {
var tasks = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>().TableNoTracking var tasks = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>().TableNoTracking
.OrderByDescending(s => s.CreatedAt) .OrderByDescending(s => s.CreatedAt)
.Skip(request.page * 15).Take(15) .Skip(request.Page * 15).Take(15)
.Select(ActivityMapper.ProjectToSDto) .Select(ActivityMapper.ProjectToSDto)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);

View File

@ -15,7 +15,7 @@ public class GetActivityQueryHandler : IRequestHandler<GetActivityQuery, Activit
{ {
var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>() var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>()
.TableNoTracking .TableNoTracking
.Where(s => s.Id == request.id) .Where(s => s.Id == request.Id)
.Select(ActivityMapper.ProjectToLDto) .Select(ActivityMapper.ProjectToLDto)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
if (task == null) if (task == null)

View File

@ -22,6 +22,9 @@ public class UpdateActivityCommandHandler : IRequestHandler<UpdateActivityComman
if (!Guid.TryParse(_currentUserService.ComplexId,out Guid complexId)) if (!Guid.TryParse(_currentUserService.ComplexId,out Guid complexId))
throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound); throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound);
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var newTask = Domain.Entities.Task.Activity.Create( var newTask = Domain.Entities.Task.Activity.Create(
request.Status, request.Status,
request.DoneAt, request.DoneAt,
@ -44,7 +47,14 @@ public class UpdateActivityCommandHandler : IRequestHandler<UpdateActivityComman
_repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>() _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>()
.Update(newTask); .Update(newTask);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return true; return true;
} }
catch (Exception )
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
} }

View File

@ -13,9 +13,19 @@ public class CreateComplexCommandHandler : IRequestHandler<CreateComplexCommand,
public async Task<ComplexSDto> Handle(CreateComplexCommand request, CancellationToken cancellationToken) public async Task<ComplexSDto> Handle(CreateComplexCommand request, CancellationToken cancellationToken)
{ {
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var complex = Domain.Entities.Complex.Complex.Create(request.Name, request.Address, request.SupportPhone); var complex = Domain.Entities.Complex.Complex.Create(request.Name, request.Address, request.SupportPhone);
_repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>().Add(complex); _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>().Add(complex);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return complex.AdaptToSDto(); return complex.AdaptToSDto();
} }
catch (Exception)
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
} }

View File

@ -0,0 +1,51 @@
using Brizco.Domain.Entities.User;
using Microsoft.AspNetCore.Identity;
namespace Brizco.Repository.Handlers.Complex;
public class CreateComplexUserCommandHandler : IRequestHandler<CreateComplexUserCommand, ComplexUserSDto>
{
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly ILogger<CreateComplexUserCommandHandler> _logger;
public CreateComplexUserCommandHandler(IRepositoryWrapper repositoryWrapper,
UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager,
ILogger<CreateComplexUserCommandHandler> logger)
{
_repositoryWrapper = repositoryWrapper;
_userManager = userManager;
_roleManager = roleManager;
_logger = logger;
}
public async Task<ComplexUserSDto> Handle(CreateComplexUserCommand request, CancellationToken cancellationToken)
{
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var complex = await _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>().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<Domain.Entities.Complex.Complex>().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;
}
}
}

View File

@ -10,6 +10,9 @@ public class DeleteComplexCommandHandler : IRequestHandler<DeleteComplexCommand,
} }
public async Task<bool> Handle(DeleteComplexCommand request, CancellationToken cancellationToken) public async Task<bool> Handle(DeleteComplexCommand request, CancellationToken cancellationToken)
{ {
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var task = await _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>() var task = await _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>()
.TableNoTracking .TableNoTracking
.FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken); .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken);
@ -19,6 +22,13 @@ public class DeleteComplexCommandHandler : IRequestHandler<DeleteComplexCommand,
.Delete(task); .Delete(task);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return true; return true;
} }
catch (Exception)
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
} }

View File

@ -0,0 +1,36 @@
using Brizco.Domain.Entities.Complex;
namespace Brizco.Repository.Handlers.Complex;
public class DeleteComplexUserCommandHandler : IRequestHandler<DeleteComplexUserCommand, bool>
{
private readonly IRepositoryWrapper _repositoryWrapper;
public DeleteComplexUserCommandHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
public async Task<bool> Handle(DeleteComplexUserCommand request, CancellationToken cancellationToken)
{
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var complexUser = await _repositoryWrapper.SetRepository<ComplexUser>()
.TableNoTracking
.FirstOrDefaultAsync(c => c.Id == request.ComplexUserId, cancellationToken);
if (complexUser == null)
throw new AppException("ComplexUser not found", ApiResultStatusCode.NotFound);
_repositoryWrapper.SetRepository<ComplexUser>().Delete(complexUser);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return true;
}
catch (Exception)
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
}

View File

@ -14,7 +14,7 @@ public class GetComplexQueryHandler : IRequestHandler<GetComplexQuery, ComplexSD
var complex = await _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>() var complex = await _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>()
.TableNoTracking .TableNoTracking
.Where(s => s.Id == request.Id) .Where(s => s.Id == request.Id)
.Select(ComplexMapper.ProjectToLDto) .Select(ComplexMapper.ProjectToSDto)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
if (complex == null) if (complex == null)
throw new AppException("Complex not found", ApiResultStatusCode.NotFound); throw new AppException("Complex not found", ApiResultStatusCode.NotFound);

View File

@ -8,7 +8,7 @@ public class GetComplexesQueryHandler : IRequestHandler<GetComplexesQuery, List<
{ {
_repositoryWrapper = repositoryWrapper; _repositoryWrapper = repositoryWrapper;
} }
public async Task<List<ActivitySDto>> Handle(GetComplexesQuery request, CancellationToken cancellationToken) public async Task<List<ComplexSDto>> Handle(GetComplexesQuery request, CancellationToken cancellationToken)
{ {
var tasks = await _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>().TableNoTracking var tasks = await _repositoryWrapper.SetRepository<Domain.Entities.Complex.Complex>().TableNoTracking
.OrderByDescending(s => s.CreatedAt) .OrderByDescending(s => s.CreatedAt)

View File

@ -1,50 +0,0 @@
namespace Brizco.Repository.Handlers.Complex;
public class UpdateActivityCommandHandler : IRequestHandler<UpdateActivityCommand, bool>
{
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly ICurrentUserService _currentUserService;
public UpdateActivityCommandHandler(IRepositoryWrapper repositoryWrapper,ICurrentUserService currentUserService)
{
_repositoryWrapper = repositoryWrapper;
_currentUserService = currentUserService;
}
public async Task<bool> Handle(UpdateActivityCommand request, CancellationToken cancellationToken)
{
var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Activity>()
.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<Domain.Entities.Task.Activity>()
.Update(newTask);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return true;
}
}

View File

@ -18,6 +18,9 @@ public class CreateShiftCommandHandler : IRequestHandler<CreateShiftCommand, Dom
if (!Guid.TryParse(_currentUserService.ComplexId, out Guid complexId)) if (!Guid.TryParse(_currentUserService.ComplexId, out Guid complexId))
throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound); throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound);
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var shift = Domain.Entities.Shift.Shift var shift = Domain.Entities.Shift.Shift
.Create(request.Title, .Create(request.Title,
request.Description, request.Description,
@ -29,6 +32,13 @@ public class CreateShiftCommandHandler : IRequestHandler<CreateShiftCommand, Dom
_repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>().Add(shift); _repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>().Add(shift);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return shift; return shift;
} }
catch (Exception )
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
} }

View File

@ -12,7 +12,7 @@ public class DeleteShiftCommandHandler : IRequestHandler<DeleteShiftCommand, boo
{ {
var shift = await _repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>() var shift = await _repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>()
.TableNoTracking .TableNoTracking
.FirstOrDefaultAsync(s => s.Id == request.id, cancellationToken); .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken);
if (shift == null) if (shift == null)
throw new AppException("Shift not found", ApiResultStatusCode.NotFound); throw new AppException("Shift not found", ApiResultStatusCode.NotFound);
_repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>() _repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>()

View File

@ -14,7 +14,7 @@ public class UpdateShiftCommandHandler : IRequestHandler<UpdateShiftCommand, boo
public async Task<bool> Handle(UpdateShiftCommand request, CancellationToken cancellationToken) public async Task<bool> Handle(UpdateShiftCommand request, CancellationToken cancellationToken)
{ {
var shift = await _repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>() var shift = await _repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>()
.TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.id); .TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.Id);
if (shift == null) if (shift == null)
throw new AppException("Shift not found", ApiResultStatusCode.NotFound); throw new AppException("Shift not found", ApiResultStatusCode.NotFound);
@ -28,7 +28,7 @@ public class UpdateShiftCommandHandler : IRequestHandler<UpdateShiftCommand, boo
request.StartAt, request.StartAt,
request.EndAt, request.EndAt,
complexId); complexId);
newShift.Id = request.id; newShift.Id = request.Id;
_repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>() _repositoryWrapper.SetRepository<Domain.Entities.Shift.Shift>()
.Update(newShift); .Update(newShift);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);

View File

@ -16,15 +16,26 @@ public class CreateShiftPlanCommandHandler : IRequestHandler<CreateShiftPlanComm
if (shift == null) if (shift == null)
throw new AppException("Shift not found", ApiResultStatusCode.NotFound); throw new AppException("Shift not found", ApiResultStatusCode.NotFound);
try
{
await _repositoryWrapper.BeginTransaction(cancellationToken);
var shiftPlan = shift.AddPlan(request.StartAt, request.EndAt); var shiftPlan = shift.AddPlan(request.StartAt, request.EndAt);
if (request.UserIds.Count == 0) if (request.UserIds.Count == 0)
throw new AppException("شیفت بندی مورد نظر باید حداقل متشکل از یک فرد باشد", ApiResultStatusCode.BadRequest); throw new AppException("شیفت بندی مورد نظر باید حداقل متشکل از یک فرد باشد",
ApiResultStatusCode.BadRequest);
request.UserIds.ForEach(i => shiftPlan.AddUser(i)); request.UserIds.ForEach(i => shiftPlan.AddUser(i));
_repositoryWrapper.SetRepository<Domain.Entities.Shift.ShiftPlan>().Add(shiftPlan); _repositoryWrapper.SetRepository<Domain.Entities.Shift.ShiftPlan>().Add(shiftPlan);
await _repositoryWrapper.SaveChangesAsync(cancellationToken); await _repositoryWrapper.SaveChangesAsync(cancellationToken);
await _repositoryWrapper.CommitAsync(cancellationToken);
return shiftPlan.AdaptToLDto(); return shiftPlan.AdaptToLDto();
} }
catch (Exception )
{
await _repositoryWrapper.RollBackAsync(cancellationToken);
throw;
}
}
} }

View File

@ -12,7 +12,7 @@ public class DeleteActivityCommandHandler : IRequestHandler<DeleteTaskCommand, b
{ {
var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>() var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>()
.TableNoTracking .TableNoTracking
.FirstOrDefaultAsync(s => s.Id == request.id, cancellationToken); .FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken);
if (task == null) if (task == null)
throw new AppException("Task not found", ApiResultStatusCode.NotFound); throw new AppException("Task not found", ApiResultStatusCode.NotFound);
_repositoryWrapper.SetRepository<Domain.Entities.Task.Task>() _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>()

View File

@ -15,7 +15,7 @@ public class GetActivityQueryHandler : IRequestHandler<GetTaskQuery, TaskLDto>
{ {
var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>() var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>()
.TableNoTracking .TableNoTracking
.Where(s => s.Id == request.id) .Where(s => s.Id == request.Id)
.Select(TaskMapper.ProjectToLDto) .Select(TaskMapper.ProjectToLDto)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
if (task == null) if (task == null)

View File

@ -12,7 +12,7 @@ public class GetActivitiesQueryHandler : IRequestHandler<GetTasksQuery, List<Tas
{ {
var tasks = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>().TableNoTracking var tasks = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>().TableNoTracking
.OrderByDescending(s => s.CreatedAt) .OrderByDescending(s => s.CreatedAt)
.Skip(request.page * 15).Take(15) .Skip(request.Page * 15).Take(15)
.Select(TaskMapper.ProjectToSDto) .Select(TaskMapper.ProjectToSDto)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);

View File

@ -14,7 +14,7 @@ public class UpdateActivityCommandHandler : IRequestHandler<UpdateTaskCommand, b
public async Task<bool> Handle(UpdateTaskCommand request, CancellationToken cancellationToken) public async Task<bool> Handle(UpdateTaskCommand request, CancellationToken cancellationToken)
{ {
var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>() var task = await _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>()
.TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.id,cancellationToken); .TableNoTracking.FirstOrDefaultAsync(s => s.Id == request.Id,cancellationToken);
if (task == null) if (task == null)
throw new AppException("Task not found", ApiResultStatusCode.NotFound); throw new AppException("Task not found", ApiResultStatusCode.NotFound);
@ -35,7 +35,7 @@ public class UpdateActivityCommandHandler : IRequestHandler<UpdateTaskCommand, b
request.Amount, request.Amount,
request.AmountType, request.AmountType,
complexId); complexId);
newTask.Id = request.id; newTask.Id = request.Id;
_repositoryWrapper.SetRepository<Domain.Entities.Task.Task>() _repositoryWrapper.SetRepository<Domain.Entities.Task.Task>()
.Update(newTask); .Update(newTask);

View File

@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Brizco.Repository.Migrations namespace Brizco.Repository.Migrations
{ {
[DbContext(typeof(ApplicationContext))] [DbContext(typeof(ApplicationContext))]
[Migration("20230918112118_Init")] [Migration("20230919130824_Init")]
partial class Init partial class Init
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -106,6 +106,9 @@ namespace Brizco.Repository.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.Property<Guid>("UserId") b.Property<Guid>("UserId")
.HasColumnType("uuid"); .HasColumnType("uuid");
@ -113,6 +116,8 @@ namespace Brizco.Repository.Migrations
b.HasIndex("ComplexId"); b.HasIndex("ComplexId");
b.HasIndex("RoleId");
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("ComplexUsers", "public"); b.ToTable("ComplexUsers", "public");
@ -541,6 +546,10 @@ namespace Brizco.Repository.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<string>("EnglishName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name") b.Property<string>("Name")
.HasMaxLength(256) .HasMaxLength(256)
.HasColumnType("character varying(256)"); .HasColumnType("character varying(256)");
@ -549,6 +558,10 @@ namespace Brizco.Repository.Migrations
.HasMaxLength(256) .HasMaxLength(256)
.HasColumnType("character varying(256)"); .HasColumnType("character varying(256)");
b.Property<string>("PersianName")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("ComplexId"); b.HasIndex("ComplexId");
@ -620,6 +633,9 @@ namespace Brizco.Repository.Migrations
b.Property<string>("SecurityStamp") b.Property<string>("SecurityStamp")
.HasColumnType("text"); .HasColumnType("text");
b.Property<int>("SignUpStatus")
.HasColumnType("integer");
b.Property<bool>("TwoFactorEnabled") b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean"); .HasColumnType("boolean");
@ -770,6 +786,12 @@ namespace Brizco.Repository.Migrations
.OnDelete(DeleteBehavior.Restrict) .OnDelete(DeleteBehavior.Restrict)
.IsRequired(); .IsRequired();
b.HasOne("Brizco.Domain.Entities.User.ApplicationRole", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Brizco.Domain.Entities.User.ApplicationUser", "User") b.HasOne("Brizco.Domain.Entities.User.ApplicationUser", "User")
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
@ -778,6 +800,8 @@ namespace Brizco.Repository.Migrations
b.Navigation("Complex"); b.Navigation("Complex");
b.Navigation("Role");
b.Navigation("User"); b.Navigation("User");
}); });

View File

@ -47,6 +47,7 @@ namespace Brizco.Repository.Migrations
LastName = table.Column<string>(type: "text", nullable: false), LastName = table.Column<string>(type: "text", nullable: false),
BirthDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: false), BirthDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
Gender = table.Column<int>(type: "integer", nullable: false), Gender = table.Column<int>(type: "integer", nullable: false),
SignUpStatus = table.Column<int>(type: "integer", nullable: false),
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
@ -74,6 +75,8 @@ namespace Brizco.Repository.Migrations
{ {
Id = table.Column<Guid>(type: "uuid", nullable: false), Id = table.Column<Guid>(type: "uuid", nullable: false),
Description = table.Column<string>(type: "text", nullable: false), Description = table.Column<string>(type: "text", nullable: false),
EnglishName = table.Column<string>(type: "text", nullable: false),
PersianName = table.Column<string>(type: "text", nullable: false),
ComplexId = table.Column<Guid>(type: "uuid", nullable: true), ComplexId = table.Column<Guid>(type: "uuid", nullable: true),
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
@ -187,41 +190,6 @@ namespace Brizco.Repository.Migrations
onDelete: ReferentialAction.Restrict); onDelete: ReferentialAction.Restrict);
}); });
migrationBuilder.CreateTable(
name: "ComplexUsers",
schema: "public",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
ComplexId = table.Column<Guid>(type: "uuid", nullable: false),
RemovedAt = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
CreatedBy = table.Column<string>(type: "text", nullable: false),
IsRemoved = table.Column<bool>(type: "boolean", nullable: false),
RemovedBy = table.Column<string>(type: "text", nullable: false),
ModifiedAt = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
ModifiedBy = table.Column<string>(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( migrationBuilder.CreateTable(
name: "Logins", name: "Logins",
schema: "public", schema: "public",
@ -266,6 +234,49 @@ namespace Brizco.Repository.Migrations
onDelete: ReferentialAction.Restrict); onDelete: ReferentialAction.Restrict);
}); });
migrationBuilder.CreateTable(
name: "ComplexUsers",
schema: "public",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
ComplexId = table.Column<Guid>(type: "uuid", nullable: false),
RoleId = table.Column<Guid>(type: "uuid", nullable: false),
RemovedAt = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
CreatedBy = table.Column<string>(type: "text", nullable: false),
IsRemoved = table.Column<bool>(type: "boolean", nullable: false),
RemovedBy = table.Column<string>(type: "text", nullable: false),
ModifiedAt = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
ModifiedBy = table.Column<string>(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( migrationBuilder.CreateTable(
name: "RoleClaims", name: "RoleClaims",
schema: "public", schema: "public",
@ -525,6 +536,12 @@ namespace Brizco.Repository.Migrations
table: "ComplexUsers", table: "ComplexUsers",
column: "ComplexId"); column: "ComplexId");
migrationBuilder.CreateIndex(
name: "IX_ComplexUsers_RoleId",
schema: "public",
table: "ComplexUsers",
column: "RoleId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_ComplexUsers_UserId", name: "IX_ComplexUsers_UserId",
schema: "public", schema: "public",

View File

@ -103,6 +103,9 @@ namespace Brizco.Repository.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.Property<Guid>("UserId") b.Property<Guid>("UserId")
.HasColumnType("uuid"); .HasColumnType("uuid");
@ -110,6 +113,8 @@ namespace Brizco.Repository.Migrations
b.HasIndex("ComplexId"); b.HasIndex("ComplexId");
b.HasIndex("RoleId");
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("ComplexUsers", "public"); b.ToTable("ComplexUsers", "public");
@ -538,6 +543,10 @@ namespace Brizco.Repository.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<string>("EnglishName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name") b.Property<string>("Name")
.HasMaxLength(256) .HasMaxLength(256)
.HasColumnType("character varying(256)"); .HasColumnType("character varying(256)");
@ -546,6 +555,10 @@ namespace Brizco.Repository.Migrations
.HasMaxLength(256) .HasMaxLength(256)
.HasColumnType("character varying(256)"); .HasColumnType("character varying(256)");
b.Property<string>("PersianName")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("ComplexId"); b.HasIndex("ComplexId");
@ -617,6 +630,9 @@ namespace Brizco.Repository.Migrations
b.Property<string>("SecurityStamp") b.Property<string>("SecurityStamp")
.HasColumnType("text"); .HasColumnType("text");
b.Property<int>("SignUpStatus")
.HasColumnType("integer");
b.Property<bool>("TwoFactorEnabled") b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean"); .HasColumnType("boolean");
@ -767,6 +783,12 @@ namespace Brizco.Repository.Migrations
.OnDelete(DeleteBehavior.Restrict) .OnDelete(DeleteBehavior.Restrict)
.IsRequired(); .IsRequired();
b.HasOne("Brizco.Domain.Entities.User.ApplicationRole", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Brizco.Domain.Entities.User.ApplicationUser", "User") b.HasOne("Brizco.Domain.Entities.User.ApplicationUser", "User")
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
@ -775,6 +797,8 @@ namespace Brizco.Repository.Migrations
b.Navigation("Complex"); b.Navigation("Complex");
b.Navigation("Role");
b.Navigation("User"); b.Navigation("User");
}); });

View File

@ -3,6 +3,9 @@
public interface IRepositoryWrapper : IDisposable , IScopedDependency public interface IRepositoryWrapper : IDisposable , IScopedDependency
{ {
IBaseRepository<T> SetRepository<T>() where T : ApiEntity; IBaseRepository<T> SetRepository<T>() 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);
} }
} }

View File

@ -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; namespace Brizco.Repository.Repositories.Base;
public class RepositoryWrapper : IRepositoryWrapper public class RepositoryWrapper : IRepositoryWrapper
{ {
private readonly ApplicationContext _context; private readonly ApplicationContext _context;
private readonly IUnitOfWork _unitOfWork; private IDbContextTransaction? _currentTransaction;
public RepositoryWrapper(ApplicationContext context) public RepositoryWrapper(ApplicationContext context)
{ {
_context = context; _context = context;
_unitOfWork = new UnitOfWork.UnitOfWork(_context);
} }
public IBaseRepository<T> SetRepository<T>() where T : ApiEntity => new BaseRepository<T>(_context); public IBaseRepository<T> SetRepository<T>() where T : ApiEntity => new BaseRepository<T>(_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<EntityEntry<IApiEntity>> entries = _context.ChangeTracker.Entries<IApiEntity>();
foreach (EntityEntry<IApiEntity> 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() public void Dispose()
{ {
_currentTransaction?.Dispose();
_context?.Dispose(); _context?.Dispose();
} }
} }

View File

@ -2,5 +2,8 @@
public interface IUnitOfWork : IScopedDependency public interface IUnitOfWork : IScopedDependency
{ {
System.Threading.Tasks.Task SaveChangesAsync(CancellationToken cancellationToken = default); Task BeginTransaction();
Task RollBackAsync();
Task CommitAsync();
Task SaveChangesAsync(CancellationToken cancellationToken = default);
} }

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage;
using Task = System.Threading.Tasks.Task; using Task = System.Threading.Tasks.Task;
namespace Brizco.Repository.Repositories.UnitOfWork; namespace Brizco.Repository.Repositories.UnitOfWork;
@ -6,12 +7,28 @@ namespace Brizco.Repository.Repositories.UnitOfWork;
public class UnitOfWork : IUnitOfWork public class UnitOfWork : IUnitOfWork
{ {
private readonly ApplicationContext _applicationContext; private readonly ApplicationContext _applicationContext;
private IDbContextTransaction? _currentTransaction ;
public UnitOfWork(ApplicationContext applicationContext) public UnitOfWork(ApplicationContext applicationContext)
{ {
_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) public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
{ {
SetAuditables(); SetAuditables();

View File

@ -82,6 +82,7 @@ public class DbInitializerService : IDbInitializerService
managerRole = new ApplicationRole managerRole = new ApplicationRole
{ {
Name = seedAdmin.RoleName, Name = seedAdmin.RoleName,
EnglishName = seedAdmin.RoleName,
Description = "root admin role" Description = "root admin role"
}; };
var adminRoleResult = await _roleManager.CreateAsync(managerRole); var adminRoleResult = await _roleManager.CreateAsync(managerRole);

View File

@ -3,17 +3,88 @@
namespace Brizco.Common.Models.Claims; namespace Brizco.Common.Models.Claims;
public static class ApplicationClaims public static class ApplicationClaims
{ {
public static ClaimDto ManageProducts { get; } = new ClaimDto public static ClaimDto ManageComplexes { get; } = new ClaimDto
{ {
Type = CustomClaimType.Permission, Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageProducts, Value = ApplicationPermission.ManageComplexes,
Title = "دسترسی کامل به محصولات", Title = "دسترسی کامل به مجموعه ها",
Detail = "دسترسی به افزودن و مدیریت محصولات فروشگاه شما" 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<Claim> AllClaims = new List<Claim> public static List<Claim> AllClaims = new List<Claim>
{ {
ManageProducts.GetClaim ManageActivities.GetClaim,
ViewTasks.GetClaim,
ManageTasks.GetClaim,
ManageShiftPlans.GetClaim,
ViewShifts.GetClaim,
ManageShifts.GetClaim,
ViewComplexes.GetClaim,
ManageComplexes.GetClaim,
};
public static List<Claim> ManagerClaims = new List<Claim>
{
ManageActivities.GetClaim,
ViewTasks.GetClaim,
ManageTasks.GetClaim,
ManageShiftPlans.GetClaim,
ViewShifts.GetClaim,
ManageShifts.GetClaim,
}; };
} }

View File

@ -1,5 +1,14 @@
namespace Brizco.Common.Models.Claims; namespace Brizco.Common.Models.Claims;
public static class ApplicationPermission 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);
} }

View File

@ -0,0 +1,7 @@
namespace Brizco.Core.Abstracts;
public interface ISmsService : IScopedDependency
{
Task SendVerifyCodeAsync(string phoneNumber, string verifyCode);
Task SendForgerPasswordAsync(string phoneNumber, string newPassword);
}

View File

@ -0,0 +1,11 @@
namespace Brizco.Core.BaseServices.Abstracts;
public interface IAccountService : IScopedDependency
{
public Task<AccessToken<ApplicationUserSDto>> LoginWithPasswordAsync(string userName, string password, CancellationToken cancellationToken);
public Task<AccessToken<ApplicationUserSDto>> LoginWithVerifyCodeAsync(string userName, string verifyCode, CancellationToken cancellationToken);
public Task<SignUpStatus> GetVerifyCodeAsync(string phoneNumber);
public Task<bool> ForgetPasswordAsync(string phoneNumber);
public Task<bool> CheckMemberShipAsync(string phoneNumber);
public Task<AccessToken<ApplicationUserSDto>> CompleteComplexSignUpAsync(SignUpRequestDto requestDto, CancellationToken cancellationToken);
}

View File

@ -0,0 +1,12 @@
namespace Brizco.Core.BaseServices.Abstracts;
public interface IJwtService : IScopedDependency
{
Task<AccessToken<TUser>> Generate<TUser>(TUser user, Guid complexId,Guid roleId) where TUser : ApplicationUser;
Task<AccessToken<TUser>> Generate<TUser>(TUser user, Guid complexId) where TUser : ApplicationUser;
Task<AccessToken<TUser>> Generate<TUser>(TUser user) where TUser : ApplicationUser;
Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user, Guid complexId, Guid roleId) where TUser : ApplicationUser;
Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user, Guid complexId) where TUser : ApplicationUser;
Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user) where TUser : ApplicationUser;
}

View File

@ -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<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _userSignInManager;
private readonly IJwtService _jwtService;
private readonly ICurrentUserService _currentUserService;
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly ISmsService _smsService;
private readonly IComplexService _complexService;
public AccountService(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> 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<bool> 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<bool> CheckMemberShipAsync(string phoneNumber)
{
var user = await _userManager.FindByNameAsync(phoneNumber);
if (user == null)
return false;
return true;
}
public async Task<SignUpStatus> 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<AccessToken<ApplicationUserSDto>> 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<AccessToken<ApplicationUserSDto>> 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<AccessToken<ApplicationUserSDto>> 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<AccessToken<ApplicationUserSDto>> CompleteLogin(ApplicationUser user, CancellationToken cancellationToken)
{
var complexUsers = await _repositoryWrapper.SetRepository<ComplexUser>()
.TableNoTracking
.Where(mcu => mcu.UserId == user.Id)
.OrderByDescending(o => o.CreatedAt)
.ToListAsync(cancellationToken);
var lastComplex = complexUsers.FirstOrDefault();
AccessToken<ApplicationUserSDto> jwt;
if (lastComplex != null)
jwt = await _jwtService.Generate<ApplicationUserSDto, ApplicationUser>(user, lastComplex.ComplexId, lastComplex.RoleId);
else
jwt = await _jwtService.Generate<ApplicationUserSDto, ApplicationUser>(user);
return jwt;
}
}

View File

@ -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<ApplicationUser> _signInManager;
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly SiteSettings _siteSettings;
public JwtService(
IOptionsSnapshot<SiteSettings> siteSettings,
SignInManager<ApplicationUser> userSignInManager,
RoleManager<ApplicationRole> roleManager)
{
_signInManager = userSignInManager;
_roleManager = roleManager;
_siteSettings = siteSettings.Value;
}
public async Task<AccessToken<TUser>> Generate<TUser>(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<TUser>(user, claims);
token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList();
return token;
}
public async Task<AccessToken<TUser>> Generate<TUser>(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<AccessToken<TUser>> Generate<TUser>(TUser user) where TUser : ApplicationUser
{
var tokenId = StringExtensions.GetId(8);
var claims = await GetClaims(user, tokenId);
return BaseGenerate(user, claims);
}
public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(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<TUserDto, TUser>(user, claims);
token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList();
return token;
}
public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(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<TUserDto, TUser>(user, claims);
}
public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user) where TUser : ApplicationUser
{
var tokenId = StringExtensions.GetId(8);
var claims = await GetClaims(user, tokenId);
return BaseGenerate<TUserDto, TUser>(user, claims);
}
private AccessToken<TUser> BaseGenerate<TUser>(TUser user, List<Claim> 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<TUser>(handler.CreateJwtSecurityToken(desctiptor));
token.User = user;
return token;
}
private AccessToken<TUserDto> BaseGenerate<TUserDto, TUser>(TUser user, List<Claim> 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<TUserDto>(handler.CreateJwtSecurityToken(desctiptor));
token.User = user.Adapt<TUserDto>();
return token;
}
private async Task<List<Claim>> GetClaims<TUser>(TUser baseUser, string jwtId) where TUser : ApplicationUser
{
var clFac = (await _signInManager.ClaimsFactory.CreateAsync(baseUser));
var claims = new List<Claim>();
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<List<Claim>> GetClaims<TUser>(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<Claim>();
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;
}
}

View File

@ -16,6 +16,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="EntityServices\Abstracts\" />
<Folder Include="Models\Api\" /> <Folder Include="Models\Api\" />
</ItemGroup> </ItemGroup>
@ -23,4 +24,25 @@
<ProjectReference Include="..\Berizco.Repository\Brizco.Repository.csproj" /> <ProjectReference Include="..\Berizco.Repository\Brizco.Repository.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Using Include="Brizco.Common.Models" />
<Using Include="Brizco.Common.Models.Api" />
<Using Include="Brizco.Common.Models.Claims" />
<Using Include="Brizco.Common.Models.Exception" />
<Using Include="Brizco.Core.Abstracts" />
<Using Include="Brizco.Core.BaseServices.Abstracts" />
<Using Include="Brizco.Core.EntityServices.Abstracts" />
<Using Include="Brizco.Domain.CommandQueries.Commands" />
<Using Include="Brizco.Domain.Dtos.RequestDtos" />
<Using Include="Brizco.Domain.Dtos.SmallDtos" />
<Using Include="Brizco.Domain.Entities.Complex" />
<Using Include="Brizco.Domain.Entities.User" />
<Using Include="Brizco.Domain.Enums" />
<Using Include="Brizco.Repository.Abstracts" />
<Using Include="Brizco.Repository.Repositories.Base.Contracts" />
<Using Include="MediatR" />
<Using Include="Microsoft.AspNetCore.Identity" />
<Using Include="Microsoft.EntityFrameworkCore" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,10 @@
namespace Brizco.Core.EntityServices.Abstracts;
public interface IComplexService : IScopedDependency
{
Task<ComplexSDto> CreateComplexAsync(string complexName,
string complexAddress,
string complexSuppPhone,
Guid managerUserId,
CancellationToken cancellationToken);
}

View File

@ -0,0 +1,43 @@
namespace Brizco.Core.EntityServices;
public class ComplexService : IComplexService
{
private readonly ISender _sender;
private readonly RoleManager<ApplicationRole> _roleManager;
public ComplexService(ISender sender,RoleManager<ApplicationRole> roleManager)
{
_sender = sender;
_roleManager = roleManager;
}
public async Task<ComplexSDto> 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;
}
}

View File

@ -66,6 +66,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Dtos\LargDtos\" /> <Folder Include="Dtos\LargDtos\" />
<Folder Include="Dtos\RequestDtos\" />
<Folder Include="Models\Settings\" /> <Folder Include="Models\Settings\" />
</ItemGroup> </ItemGroup>

View File

@ -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) public sealed record CreateComplexCommand(string Name, string Address, string SupportPhone)
: IRequest<ComplexSDto>; : IRequest<ComplexSDto>;
public sealed record CreateComplexUserCommand(Guid ComplexId,Guid UserId,Guid RoleId)
: IRequest<ComplexUserSDto>;
public sealed record DeleteComplexUserCommand(Guid ComplexUserId)
: IRequest<bool>;
public sealed record UpdateComplexCommand(Guid Id, string Name, string Address, string SupportPhone) public sealed record UpdateComplexCommand(Guid Id, string Name, string Address, string SupportPhone)
: IRequest<bool>; : IRequest<bool>;
public sealed record DeleteComplexCommand(Guid Id) public sealed record DeleteComplexCommand(Guid Id)
: IRequest<bool>; : IRequest<bool>;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,15 @@
using Brizco.Domain.Entities.User;
namespace Brizco.Domain.Dtos.SmallDtos;
public class ApplicationUserSDto : BaseDto<ApplicationUserSDto,ApplicationUser>
{
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;
}

View File

@ -6,4 +6,11 @@ public partial class Complex
{ {
return new Complex(name,address,supportPhone); 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;
}
} }

View File

@ -5,9 +5,22 @@ namespace Brizco.Domain.Entities.Complex;
[GenerateMapper] [GenerateMapper]
public class ComplexUser : ApiEntity 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 UserId { get; internal set; }
public Guid ComplexId { 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 ApplicationUser? User { get; internal set; }
public Complex? Complex { get; internal set; } public Complex? Complex { get; internal set; }
} }

View File

@ -3,6 +3,8 @@
public class ApplicationRole : IdentityRole<Guid> public class ApplicationRole : IdentityRole<Guid>
{ {
public string Description { get; set; } = string.Empty; 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 Guid? ComplexId { get; set; }
public Complex.Complex? Complex { get; set; } public Complex.Complex? Complex { get; set; }

View File

@ -6,5 +6,6 @@ public class ApplicationUser : IdentityUser<Guid>
public string LastName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty;
public DateTime BirthDate { get; set; } public DateTime BirthDate { get; set; }
public Gender Gender { get; set; } public Gender Gender { get; set; }
public SignUpStatus SignUpStatus { get; set; }
} }

View File

@ -0,0 +1,9 @@
namespace Brizco.Domain.Enums;
public enum SignUpStatus
{
StartSignUp = 0,
PhoneNumberVerified = 1,
ComplexCreated = 2,
SignUpCompleted = 3,
}

View File

@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using Brizco.Domain.Dtos.LargDtos; using Brizco.Domain.Dtos.LargDtos;
using Brizco.Domain.Dtos.SmallDtos; using Brizco.Domain.Dtos.SmallDtos;
@ -79,62 +81,182 @@ namespace Brizco.Domain.Mappers
Name = p9.Name, Name = p9.Name,
Address = p9.Address, Address = p9.Address,
SupportPhone = p9.SupportPhone, SupportPhone = p9.SupportPhone,
Users = funcMain1(p9.Users),
Id = p9.Id 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<Func<ComplexLDto, Complex>> ProjectLDtoToComplex => p15 => new Complex()
{
Name = p15.Name,
Address = p15.Address,
SupportPhone = p15.SupportPhone,
Users = p15.Users.Select<ComplexUserSDto, ComplexUser>(p16 => new ComplexUser()
{
UserId = p16.UserId,
ComplexId = p16.ComplexId,
Id = p16.Id
}).ToList<ComplexUser>(),
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<Func<Complex, ComplexLDto>> ProjectToLDto => p23 => new ComplexLDto()
{
Name = p23.Name,
Address = p23.Address,
SupportPhone = p23.SupportPhone,
Users = p23.Users.Select<ComplexUser, ComplexUserSDto>(p24 => new ComplexUserSDto()
{
UserId = p24.UserId,
ComplexId = p24.ComplexId,
Id = p24.Id
}).ToList<ComplexUserSDto>(),
Id = p23.Id
};
private static List<ComplexUser> funcMain1(List<ComplexUserSDto> p10)
{ {
if (p10 == null) if (p10 == null)
{ {
return null; return null;
} }
Complex result = p11 ?? new Complex(); List<ComplexUser> result = new List<ComplexUser>(p10.Count);
result.Name = p10.Name; int i = 0;
result.Address = p10.Address; int len = p10.Count;
result.SupportPhone = p10.SupportPhone;
result.Id = p10.Id; 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; return result;
} }
public static Expression<Func<ComplexLDto, Complex>> ProjectLDtoToComplex => p12 => new Complex()
private static List<ComplexUser> funcMain2(List<ComplexUserSDto> p13, List<ComplexUser> p14)
{ {
Name = p12.Name, if (p13 == null)
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)
{ {
return null; return null;
} }
ComplexLDto result = p15 ?? new ComplexLDto(); List<ComplexUser> result = new List<ComplexUser>(p13.Count);
result.Name = p14.Name; int i = 0;
result.Address = p14.Address; int len = p13.Count;
result.SupportPhone = p14.SupportPhone;
result.Id = p14.Id; 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; return result;
} }
public static Expression<Func<Complex, ComplexLDto>> ProjectToLDto => p16 => new ComplexLDto()
private static List<ComplexUserSDto> funcMain3(List<ComplexUser> p18)
{ {
Name = p16.Name, if (p18 == null)
Address = p16.Address, {
SupportPhone = p16.SupportPhone, return null;
Id = p16.Id }
}; List<ComplexUserSDto> result = new List<ComplexUserSDto>(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<ComplexUserSDto> funcMain4(List<ComplexUser> p21, List<ComplexUserSDto> p22)
{
if (p21 == null)
{
return null;
}
List<ComplexUserSDto> result = new List<ComplexUserSDto>(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;
}
} }
} }

View File

@ -6,6 +6,7 @@ public class SiteSettings
public string BaseUrl { get; set; } = string.Empty; public string BaseUrl { get; set; } = string.Empty;
public RedisSettings MasterRedisConfiguration { get; set; } = new RedisSettings(); public RedisSettings MasterRedisConfiguration { get; set; } = new RedisSettings();
public UserSetting UserSetting { get; set; } = new UserSetting(); public UserSetting UserSetting { get; set; } = new UserSetting();
public string KaveNegarApiKey { get; set; } = string.Empty;
} }
public class RedisSettings public class RedisSettings
{ {