From a1b6e28d35dd15dae9bc4e10ee87c6368ed58a4b Mon Sep 17 00:00:00 2001 From: MeysamMoghaddam <65253484+MeysamMoghaddam@users.noreply.github.com> Date: Sat, 27 Sep 2025 23:48:41 +0330 Subject: [PATCH] Generator Changes at 9/27/2025 11:07:17 PM --- .../CMSMicroservice.Application.csproj | 1 + .../Common/Extensions/UtilExtensions.cs | 38 ++ .../Interfaces/IApplicationDbContext.cs | 3 +- .../Common/Mappings/OtpTokenProfile.cs | 10 + .../CreateNewOtpTokenCommand.cs | 9 + .../CreateNewOtpTokenCommandHandler.cs | 83 ++++ .../CreateNewOtpTokenCommandValidator.cs | 18 + .../CreateNewOtpTokenResponseDto.cs | 11 + .../VerifyOtpToken/VerifyOtpTokenCommand.cs | 11 + .../VerifyOtpTokenCommandHandler.cs | 87 ++++ .../VerifyOtpTokenCommandValidator.cs | 20 + .../VerifyOtpTokenResponseDto.cs | 9 + .../CreateNewOtpTokenEventHandler.cs | 21 + .../VerifyOtpTokenEventHandler.cs | 21 + .../GetAllOtpTokenByFilterQuery.cs | 27 ++ .../GetAllOtpTokenByFilterQueryHandler.cs | 36 ++ .../GetAllOtpTokenByFilterQueryValidator.cs | 14 + .../GetAllOtpTokenByFilterResponseDto.cs | 25 + .../CreateNewUserCommandHandler.cs | 5 +- .../Commands/UpdateUser/UpdateUserCommand.cs | 4 - .../UpdateUser/UpdateUserCommandValidator.cs | 2 - .../GetAllUserByFilterQuery.cs | 6 + .../GetAllUserByFilterQueryHandler.cs | 2 + .../GetAllUserByFilterResponseDto.cs | 6 + .../Queries/GetUser/GetUserResponseDto.cs | 6 + .../Entities/OtpToken.cs | 17 + src/CMSMicroservice.Domain/Entities/User.cs | 10 +- .../OtpTokenEvents/CreateNewOtpTokenEvent.cs | 10 + .../OtpTokenEvents/VerifyOtpTokenEvent.cs | 10 + .../Persistence/ApplicationDbContext.cs | 3 +- .../Configurations/OtpTokenConfiguration.cs | 22 + .../Configurations/UserConfiguration.cs | 3 + .../Migrations/20250927194115_u02.Designer.cs | 436 ++++++++++++++++++ .../Migrations/20250927194115_u02.cs | 85 ++++ .../ApplicationDbContextModelSnapshot.cs | 59 +++ .../CMSMicroservice.Protobuf.csproj | 3 +- .../Protos/otptoken.proto | 87 ++++ .../Protos/user.proto | 15 +- .../CreateNewOtpTokenRequestValidator.cs | 21 + .../GetAllOtpTokenByFilterRequestValidator.cs | 17 + .../VerifyOtpTokenRequestValidator.cs | 23 + .../User/UpdateUserRequestValidator.cs | 2 - .../Common/Mappings/OtpTokenProfile.cs | 10 + .../Services/OtpTokenService.cs | 27 ++ src/CMSMicroservice.WebApi/appsettings.json | 3 + 45 files changed, 1320 insertions(+), 18 deletions(-) create mode 100644 src/CMSMicroservice.Application/Common/Extensions/UtilExtensions.cs create mode 100644 src/CMSMicroservice.Application/Common/Mappings/OtpTokenProfile.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommand.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandValidator.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenResponseDto.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommand.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandValidator.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenResponseDto.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/CreateNewOtpTokenEventHandlers/CreateNewOtpTokenEventHandler.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/VerifyOtpTokenEventHandlers/VerifyOtpTokenEventHandler.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQuery.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryHandler.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryValidator.cs create mode 100644 src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterResponseDto.cs create mode 100644 src/CMSMicroservice.Domain/Entities/OtpToken.cs create mode 100644 src/CMSMicroservice.Domain/Events/OtpTokenEvents/CreateNewOtpTokenEvent.cs create mode 100644 src/CMSMicroservice.Domain/Events/OtpTokenEvents/VerifyOtpTokenEvent.cs create mode 100644 src/CMSMicroservice.Infrastructure/Persistence/Configurations/OtpTokenConfiguration.cs create mode 100644 src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.Designer.cs create mode 100644 src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.cs create mode 100644 src/CMSMicroservice.Protobuf/Protos/otptoken.proto create mode 100644 src/CMSMicroservice.Protobuf/Validator/OtpToken/CreateNewOtpTokenRequestValidator.cs create mode 100644 src/CMSMicroservice.Protobuf/Validator/OtpToken/GetAllOtpTokenByFilterRequestValidator.cs create mode 100644 src/CMSMicroservice.Protobuf/Validator/OtpToken/VerifyOtpTokenRequestValidator.cs create mode 100644 src/CMSMicroservice.WebApi/Common/Mappings/OtpTokenProfile.cs create mode 100644 src/CMSMicroservice.WebApi/Services/OtpTokenService.cs diff --git a/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj b/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj index f292b0e..793a4d9 100644 --- a/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj +++ b/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj @@ -10,6 +10,7 @@ + diff --git a/src/CMSMicroservice.Application/Common/Extensions/UtilExtensions.cs b/src/CMSMicroservice.Application/Common/Extensions/UtilExtensions.cs new file mode 100644 index 0000000..42c258a --- /dev/null +++ b/src/CMSMicroservice.Application/Common/Extensions/UtilExtensions.cs @@ -0,0 +1,38 @@ +using System.Security.Cryptography; + +namespace CMSMicroservice.Application.Common.Extensions; +public static class UtilExtensions +{ + public static string NormalizeIranMobile(this string input) + { + if (string.IsNullOrWhiteSpace(input)) throw new ArgumentException("mobile is empty"); + var m = new string(input.Where(char.IsDigit).ToArray()); + + if (m.StartsWith("0098")) m = m[4..]; + else if (m.StartsWith("098")) m = m[3..]; + else if (m.StartsWith("98")) m = m[2..]; + if (!m.StartsWith("0")) m = "0" + m; + + if (m.Length != 11 || !m.StartsWith("09")) + throw new ArgumentException("شماره موبایل نامعتبر است."); + return m; + } + public static string Generate(int digits = 10, bool firstDigitNonZero = false) + { + if (digits <= 0) throw new ArgumentOutOfRangeException(nameof(digits)); + + var chars = new char[digits]; + + // رقم اول + if (firstDigitNonZero) + chars[0] = (char)('0' + RandomNumberGenerator.GetInt32(1, 10)); // 1..9 + else + chars[0] = (char)('0' + RandomNumberGenerator.GetInt32(10)); // 0..9 + + // بقیه ارقام + for (int i = 1; i < digits; i++) + chars[i] = (char)('0' + RandomNumberGenerator.GetInt32(10)); + + return new string(chars); + } +} diff --git a/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs b/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs index 3d0f94a..f173703 100644 --- a/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs +++ b/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs @@ -2,11 +2,12 @@ namespace CMSMicroservice.Application.Common.Interfaces; public interface IApplicationDbContext { - DbSet Users { get; } DbSet UserAddresss { get; } DbSet Packages { get; } DbSet UserOrders { get; } DbSet Roles { get; } DbSet UserRoles { get; } + DbSet Users { get; } + DbSet OtpTokens { get; } Task SaveChangesAsync(CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/CMSMicroservice.Application/Common/Mappings/OtpTokenProfile.cs b/src/CMSMicroservice.Application/Common/Mappings/OtpTokenProfile.cs new file mode 100644 index 0000000..96ed714 --- /dev/null +++ b/src/CMSMicroservice.Application/Common/Mappings/OtpTokenProfile.cs @@ -0,0 +1,10 @@ +namespace CMSMicroservice.Application.Common.Mappings; + +public class OtpTokenProfile : IRegister +{ + void IRegister.Register(TypeAdapterConfig config) + { + //config.NewConfig() + // .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}"); + } +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommand.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommand.cs new file mode 100644 index 0000000..dc2181b --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommand.cs @@ -0,0 +1,9 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.CreateNewOtpToken; +public record CreateNewOtpTokenCommand : IRequest +{ + //موبایل مقصد + public string Mobile { get; init; } + //مقصود + public string Purpose { get; init; } + +} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs new file mode 100644 index 0000000..79dd318 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs @@ -0,0 +1,83 @@ +using CMSMicroservice.Domain.Events; +using Microsoft.Extensions.Configuration; +using System.Security.Cryptography; +using System.Text; +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.CreateNewOtpToken; +public class CreateNewOtpTokenCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + private readonly IConfiguration _cfg; + + public CreateNewOtpTokenCommandHandler(IApplicationDbContext context, IConfiguration cfg) + { + _context = context; + _cfg = cfg; + } + + const int CodeLength = 6; // 4-6 مناسب است + static readonly TimeSpan Ttl = TimeSpan.FromMinutes(2); + static readonly TimeSpan Cooldown = TimeSpan.FromSeconds(60); // فاصله ارسال مجدد + public async Task Handle(CreateNewOtpTokenCommand request, + CancellationToken cancellationToken) + { + var mobile = request.Mobile.NormalizeIranMobile(); + var purpose = request.Purpose?.ToLowerInvariant() ?? "signup"; + + // ریت‌لیمیت ساده: اگر هنوز کدی فعال و تازه داریم، اجازه نده + var now = DateTime.Now; + var lastActive = await _context.OtpTokens + .Where(o => o.Mobile == mobile && o.Purpose == purpose && !o.IsUsed && o.ExpiresAt > now) + .OrderByDescending(o => o.Created) + .FirstOrDefaultAsync(cancellationToken); + + if (lastActive is not null && (now - lastActive.Created) < Cooldown) + return new CreateNewOtpTokenResponseDto() + { + Success = false, + Message = "لطفاً کمی بعد دوباره تلاش کنید." + }; + + // تولید کد + var code = GenerateNumericCode(CodeLength); + var codeHash = Hash(code); + + var entity = new OtpToken + { + Mobile = mobile, + Purpose = purpose, + CodeHash = codeHash, + ExpiresAt = now.Add(Ttl), + Attempts = 0, + IsUsed = false + }; + await _context.OtpTokens.AddAsync(entity, cancellationToken); + entity.AddDomainEvent(new CreateNewOtpTokenEvent(entity)); + await _context.SaveChangesAsync(cancellationToken); + return new CreateNewOtpTokenResponseDto() + { + Success = true, + Message = "کد ارسال شد.", + Code = code + }; + } + + // --- util‌ها --- + private string GenerateNumericCode(int len) + { + // امن‌تر از Random(): تولید ارقام با RNG + var bytes = new byte[len]; + RandomNumberGenerator.Fill(bytes); + var sb = new StringBuilder(len); + foreach (var b in bytes) sb.Append((b % 10).ToString()); + return sb.ToString(); + } + + private string Hash(string code) + { + // HMAC با secret اپ (نیازی به ذخیره salt جدا نیست) + var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set"); + using var h = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); + var hash = h.ComputeHash(Encoding.UTF8.GetBytes(code)); + return Convert.ToHexString(hash); + } +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandValidator.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandValidator.cs new file mode 100644 index 0000000..8ab92fb --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandValidator.cs @@ -0,0 +1,18 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.CreateNewOtpToken; +public class CreateNewOtpTokenCommandValidator : AbstractValidator +{ + public CreateNewOtpTokenCommandValidator() + { + RuleFor(model => model.Mobile) + .NotEmpty(); + RuleFor(model => model.Purpose) + .NotEmpty(); + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((CreateNewOtpTokenCommand)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenResponseDto.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenResponseDto.cs new file mode 100644 index 0000000..3ff71dc --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenResponseDto.cs @@ -0,0 +1,11 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.CreateNewOtpToken; +public class CreateNewOtpTokenResponseDto +{ + //موفق؟ + public bool Success { get; set; } + //پیام + public string Message { get; set; } + //کد + public string? Code { get; set; } + +} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommand.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommand.cs new file mode 100644 index 0000000..9d26463 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommand.cs @@ -0,0 +1,11 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.VerifyOtpToken; +public record VerifyOtpTokenCommand : IRequest +{ + //موبایل مقصد + public string Mobile { get; init; } + //مقصود + public string Purpose { get; init; } + //کد + public string Code { get; init; } + +} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs new file mode 100644 index 0000000..bf76490 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs @@ -0,0 +1,87 @@ +using CMSMicroservice.Domain.Events; +using Microsoft.Extensions.Configuration; +using System.Security.Cryptography; +using System.Text; +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.VerifyOtpToken; +public class VerifyOtpTokenCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + private readonly IConfiguration _cfg; + + public VerifyOtpTokenCommandHandler(IApplicationDbContext context, IConfiguration cfg) + { + _context = context; + _cfg = cfg; + } + + const int MaxAttempts = 5; // محدودیت تلاش + public async Task Handle(VerifyOtpTokenCommand request, CancellationToken cancellationToken) + { + var mobile = request.Mobile.NormalizeIranMobile(); + var purpose = request.Purpose?.ToLowerInvariant() ?? "signup"; + var now = DateTime.Now; + + + var otp = await _context.OtpTokens + .Where(o => o.Mobile == mobile && o.Purpose == purpose && !o.IsUsed && o.ExpiresAt > now) + .OrderByDescending(o => o.Created) + .FirstOrDefaultAsync(cancellationToken); + + if (otp is null) return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد پیدا نشد یا منقضی شده است." }; + + if (otp.Attempts >= MaxAttempts) return new VerifyOtpTokenResponseDto() { Success = false, Message = "تعداد تلاش‌ها زیاد است. دوباره کد بگیرید." }; + + otp.Attempts++; + + if (!VerifyHash(request.Code, otp.CodeHash)) + { + await _context.SaveChangesAsync(cancellationToken); + return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد نادرست است." }; + } + + // موفق + otp.IsUsed = true; + + // کاربر را بساز/به‌روزرسانی کن + var user = await _context.Users.FirstOrDefaultAsync(u => u.Mobile == mobile, cancellationToken); + if (user is null) + { + user = new User + { + Mobile = mobile, + ReferralCode = UtilExtensions.Generate(digits: 10, firstDigitNonZero: true), + IsMobileVerified = true, + MobileVerifiedAt = now + }; + await _context.Users.AddAsync(user, cancellationToken); + user.AddDomainEvent(new CreateNewUserEvent(user)); + await _context.SaveChangesAsync(cancellationToken); + } + else + { + user.IsMobileVerified = true; + user.MobileVerifiedAt ??= now; + + _context.Users.Update(user); + user.AddDomainEvent(new UpdateUserEvent(user)); + await _context.SaveChangesAsync(cancellationToken); + } + + return new VerifyOtpTokenResponseDto() + { + Success = true, + Message = "اعتبارسنجی موفق." + }; + } + + private string Hash(string code) + { + // HMAC با secret اپ (نیازی به ذخیره salt جدا نیست) + var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set"); + using var h = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); + var hash = h.ComputeHash(Encoding.UTF8.GetBytes(code)); + return Convert.ToHexString(hash); + } + private bool VerifyHash(string code, string hexHash) => Hash(code).Equals(hexHash, StringComparison.OrdinalIgnoreCase); + +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandValidator.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandValidator.cs new file mode 100644 index 0000000..c917d37 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandValidator.cs @@ -0,0 +1,20 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.VerifyOtpToken; +public class VerifyOtpTokenCommandValidator : AbstractValidator +{ + public VerifyOtpTokenCommandValidator() + { + RuleFor(model => model.Mobile) + .NotEmpty(); + RuleFor(model => model.Purpose) + .NotEmpty(); + RuleFor(model => model.Code) + .NotEmpty(); + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((VerifyOtpTokenCommand)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenResponseDto.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenResponseDto.cs new file mode 100644 index 0000000..2947677 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenResponseDto.cs @@ -0,0 +1,9 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Commands.VerifyOtpToken; +public class VerifyOtpTokenResponseDto +{ + //موفق؟ + public bool Success { get; set; } + //پیام + public string Message { get; set; } + +} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/CreateNewOtpTokenEventHandlers/CreateNewOtpTokenEventHandler.cs b/src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/CreateNewOtpTokenEventHandlers/CreateNewOtpTokenEventHandler.cs new file mode 100644 index 0000000..f6397fb --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/CreateNewOtpTokenEventHandlers/CreateNewOtpTokenEventHandler.cs @@ -0,0 +1,21 @@ +using CMSMicroservice.Domain.Events; +using Microsoft.Extensions.Logging; + +namespace CMSMicroservice.Application.OtpTokenCQ.EventHandlers; + +public class CreateNewOtpTokenEventHandler : INotificationHandler +{ + private readonly ILogger _logger; + + public CreateNewOtpTokenEventHandler(ILogger logger) + { + _logger = logger; + } + + public Task Handle(CreateNewOtpTokenEvent notification, CancellationToken cancellationToken) + { + _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); + + return Task.CompletedTask; + } +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/VerifyOtpTokenEventHandlers/VerifyOtpTokenEventHandler.cs b/src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/VerifyOtpTokenEventHandlers/VerifyOtpTokenEventHandler.cs new file mode 100644 index 0000000..f1df160 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/EventHandlers/VerifyOtpTokenEventHandlers/VerifyOtpTokenEventHandler.cs @@ -0,0 +1,21 @@ +using CMSMicroservice.Domain.Events; +using Microsoft.Extensions.Logging; + +namespace CMSMicroservice.Application.OtpTokenCQ.EventHandlers; + +public class VerifyOtpTokenEventHandler : INotificationHandler +{ + private readonly ILogger _logger; + + public VerifyOtpTokenEventHandler(ILogger logger) + { + _logger = logger; + } + + public Task Handle(VerifyOtpTokenEvent notification, CancellationToken cancellationToken) + { + _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); + + return Task.CompletedTask; + } +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQuery.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQuery.cs new file mode 100644 index 0000000..caaa200 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQuery.cs @@ -0,0 +1,27 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Queries.GetAllOtpTokenByFilter; +public record GetAllOtpTokenByFilterQuery : IRequest +{ + //موقعیت صفحه بندی + public PaginationState? PaginationState { get; init; } + //مرتب سازی بر اساس + public string? SortBy { get; init; } + //فیلتر + public GetAllOtpTokenByFilterFilter? Filter { get; init; } + +}public class GetAllOtpTokenByFilterFilter +{ + //شناسه + public long? Id { get; set; } + //موبایل مقصد + public string? Mobile { get; set; } + //مقصود + public string? Purpose { get; set; } + //کد هش شده + public string? CodeHash { get; set; } + //زمان انقضا + public DateTime? ExpiresAt { get; set; } + //شمارش تلاش‌ها + public int? Attempts { get; set; } + //موفق بود؟ + public bool? IsUsed { get; set; } +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryHandler.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryHandler.cs new file mode 100644 index 0000000..4ff7868 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryHandler.cs @@ -0,0 +1,36 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Queries.GetAllOtpTokenByFilter; +public class GetAllOtpTokenByFilterQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public GetAllOtpTokenByFilterQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(GetAllOtpTokenByFilterQuery request, CancellationToken cancellationToken) + { + var query = _context.OtpTokens + .ApplyOrder(sortBy: request.SortBy) + .AsNoTracking() + .AsQueryable(); + if (request.Filter is not null) + { + query = query + .Where(x => request.Filter.Id == null || x.Id == request.Filter.Id) + .Where(x => request.Filter.Mobile == null || x.Mobile.Contains(request.Filter.Mobile)) + .Where(x => request.Filter.Purpose == null || x.Purpose.Contains(request.Filter.Purpose)) + .Where(x => request.Filter.CodeHash == null || x.CodeHash.Contains(request.Filter.CodeHash)) + .Where(x => request.Filter.ExpiresAt == null || x.ExpiresAt == request.Filter.ExpiresAt) + .Where(x => request.Filter.Attempts == null || x.Attempts == request.Filter.Attempts) + .Where(x => request.Filter.IsUsed == null || x.IsUsed == request.Filter.IsUsed) +; + } + return new GetAllOtpTokenByFilterResponseDto + { + MetaData = await query.GetMetaData(request.PaginationState, cancellationToken), + Models = await query.PaginatedListAsync(paginationState: request.PaginationState) + .ProjectToType().ToListAsync(cancellationToken) + }; + } +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryValidator.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryValidator.cs new file mode 100644 index 0000000..13312d3 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterQueryValidator.cs @@ -0,0 +1,14 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Queries.GetAllOtpTokenByFilter; +public class GetAllOtpTokenByFilterQueryValidator : AbstractValidator +{ + public GetAllOtpTokenByFilterQueryValidator() + { + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetAllOtpTokenByFilterQuery)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterResponseDto.cs b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterResponseDto.cs new file mode 100644 index 0000000..e99c582 --- /dev/null +++ b/src/CMSMicroservice.Application/OtpTokenCQ/Queries/GetAllOtpTokenByFilter/GetAllOtpTokenByFilterResponseDto.cs @@ -0,0 +1,25 @@ +namespace CMSMicroservice.Application.OtpTokenCQ.Queries.GetAllOtpTokenByFilter; +public class GetAllOtpTokenByFilterResponseDto +{ + //متادیتا + public MetaData MetaData { get; set; } + //مدل خروجی + public List? Models { get; set; } + +}public class GetAllOtpTokenByFilterResponseModel +{ + //شناسه + public long Id { get; set; } + //موبایل مقصد + public string Mobile { get; set; } + //مقصود + public string Purpose { get; set; } + //کد هش شده + public string CodeHash { get; set; } + //زمان انقضا + public DateTime ExpiresAt { get; set; } + //شمارش تلاش‌ها + public int Attempts { get; set; } + //موفق بود؟ + public bool IsUsed { get; set; } +} diff --git a/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewUser/CreateNewUserCommandHandler.cs b/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewUser/CreateNewUserCommandHandler.cs index 12d2301..0523745 100644 --- a/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewUser/CreateNewUserCommandHandler.cs +++ b/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewUser/CreateNewUserCommandHandler.cs @@ -1,4 +1,5 @@ -using CMSMicroservice.Domain.Events; +using CMSMicroservice.Domain.Events; +using System.Security.Cryptography; namespace CMSMicroservice.Application.UserCQ.Commands.CreateNewUser; public class CreateNewUserCommandHandler : IRequestHandler { @@ -13,6 +14,8 @@ public class CreateNewUserCommandHandler : IRequestHandler(); + entity.ReferralCode = UtilExtensions.Generate(digits: 10, firstDigitNonZero: true); + await _context.Users.AddAsync(entity, cancellationToken); entity.AddDomainEvent(new CreateNewUserEvent(entity)); await _context.SaveChangesAsync(cancellationToken); diff --git a/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommand.cs b/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommand.cs index fa10886..9263a71 100644 --- a/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommand.cs +++ b/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommand.cs @@ -7,13 +7,9 @@ public record UpdateUserCommand : IRequest public string? FirstName { get; init; } //نام خانوادگی public string? LastName { get; init; } - //شماره موبایل - public string Mobile { get; init; } //کد ملی public string? NationalCode { get; init; } //آدرس آواتار public string? AvatarPath { get; init; } - //شناسه والد - public long? ParentId { get; init; } } \ No newline at end of file diff --git a/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommandValidator.cs b/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommandValidator.cs index bc37700..44a4fbe 100644 --- a/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommandValidator.cs +++ b/src/CMSMicroservice.Application/UserCQ/Commands/UpdateUser/UpdateUserCommandValidator.cs @@ -5,8 +5,6 @@ public class UpdateUserCommandValidator : AbstractValidator { RuleFor(model => model.Id) .NotNull(); - RuleFor(model => model.Mobile) - .NotEmpty(); } public Func>> ValidateValue => async (model, propertyName) => { diff --git a/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQuery.cs b/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQuery.cs index 12e601c..0252115 100644 --- a/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQuery.cs +++ b/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQuery.cs @@ -24,4 +24,10 @@ public record GetAllUserByFilterQuery : IRequest public string? AvatarPath { get; set; } //شناسه والد public long? ParentId { get; set; } + //کد ارجاع + public string? ReferralCode { get; set; } + //موبایل فعال شده؟ + public bool? IsMobileVerified { get; set; } + //تاریخ فعال سازی موبایل + public DateTime? MobileVerifiedAt { get; set; } } diff --git a/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQueryHandler.cs b/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQueryHandler.cs index 6e96911..cfc88ea 100644 --- a/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQueryHandler.cs +++ b/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterQueryHandler.cs @@ -23,6 +23,8 @@ public class GetAllUserByFilterQueryHandler : IRequestHandler request.Filter.Mobile == null || x.Mobile.Contains(request.Filter.Mobile)) .Where(x => request.Filter.NationalCode == null || x.NationalCode.Contains(request.Filter.NationalCode)) .Where(x => request.Filter.AvatarPath == null || x.AvatarPath.Contains(request.Filter.AvatarPath)) + .Where(x => request.Filter.ReferralCode == null || x.ReferralCode == request.Filter.ReferralCode) + .Where(x => request.Filter.IsMobileVerified == null || x.IsMobileVerified == request.Filter.IsMobileVerified) .Where(x => request.Filter.ParentId == null || x.ParentId == request.Filter.ParentId) ; } diff --git a/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterResponseDto.cs b/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterResponseDto.cs index 1ae3fd3..36def23 100644 --- a/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterResponseDto.cs +++ b/src/CMSMicroservice.Application/UserCQ/Queries/GetAllUserByFilter/GetAllUserByFilterResponseDto.cs @@ -22,4 +22,10 @@ public class GetAllUserByFilterResponseDto public string? AvatarPath { get; set; } //شناسه والد public long? ParentId { get; set; } + //کد ارجاع + public string ReferralCode { get; set; } + //موبایل فعال شده؟ + public bool IsMobileVerified { get; set; } + //تاریخ فعال سازی موبایل + public DateTime? MobileVerifiedAt { get; set; } } diff --git a/src/CMSMicroservice.Application/UserCQ/Queries/GetUser/GetUserResponseDto.cs b/src/CMSMicroservice.Application/UserCQ/Queries/GetUser/GetUserResponseDto.cs index df3f6c0..8fc26f8 100644 --- a/src/CMSMicroservice.Application/UserCQ/Queries/GetUser/GetUserResponseDto.cs +++ b/src/CMSMicroservice.Application/UserCQ/Queries/GetUser/GetUserResponseDto.cs @@ -15,5 +15,11 @@ public class GetUserResponseDto public string? AvatarPath { get; set; } //شناسه والد public long? ParentId { get; set; } + //کد ارجاع + public string ReferralCode { get; set; } + //موبایل فعال شده؟ + public bool IsMobileVerified { get; set; } + //تاریخ فعال سازی موبایل + public DateTime? MobileVerifiedAt { get; set; } } \ No newline at end of file diff --git a/src/CMSMicroservice.Domain/Entities/OtpToken.cs b/src/CMSMicroservice.Domain/Entities/OtpToken.cs new file mode 100644 index 0000000..939d6be --- /dev/null +++ b/src/CMSMicroservice.Domain/Entities/OtpToken.cs @@ -0,0 +1,17 @@ +namespace CMSMicroservice.Domain.Entities; +//توکن Otp +public class OtpToken : BaseAuditableEntity +{ + //موبایل مقصد + public string Mobile { get; set; } + //مقصود + public string Purpose { get; set; } + //کد هش شده + public string CodeHash { get; set; } + //زمان انقضا + public DateTime ExpiresAt { get; set; } + //شمارش تلاش‌ها + public int Attempts { get; set; } + //موفق بود؟ + public bool IsUsed { get; set; } +} \ No newline at end of file diff --git a/src/CMSMicroservice.Domain/Entities/User.cs b/src/CMSMicroservice.Domain/Entities/User.cs index 02b8cf8..1bcd92d 100644 --- a/src/CMSMicroservice.Domain/Entities/User.cs +++ b/src/CMSMicroservice.Domain/Entities/User.cs @@ -16,12 +16,18 @@ public class User : BaseAuditableEntity public long? ParentId { get; set; } //User Navigation Property public virtual User? Parent { get; set; } - //User Collection Navigation Reference - public virtual ICollection Users { get; set; } + //کد ارجاع + public string ReferralCode { get; set; } + //موبایل فعال شده؟ + public bool IsMobileVerified { get; set; } + //تاریخ فعال سازی موبایل + public DateTime? MobileVerifiedAt { get; set; } //UserAddress Collection Navigation Reference public virtual ICollection UserAddresss { get; set; } //UserOrder Collection Navigation Reference public virtual ICollection UserOrders { get; set; } //UserRole Collection Navigation Reference public virtual ICollection UserRoles { get; set; } + //User Collection Navigation Reference + public virtual ICollection Users { get; set; } } \ No newline at end of file diff --git a/src/CMSMicroservice.Domain/Events/OtpTokenEvents/CreateNewOtpTokenEvent.cs b/src/CMSMicroservice.Domain/Events/OtpTokenEvents/CreateNewOtpTokenEvent.cs new file mode 100644 index 0000000..fe088b7 --- /dev/null +++ b/src/CMSMicroservice.Domain/Events/OtpTokenEvents/CreateNewOtpTokenEvent.cs @@ -0,0 +1,10 @@ +namespace CMSMicroservice.Domain.Events; +public class CreateNewOtpTokenEvent : BaseEvent +{ + public CreateNewOtpTokenEvent(OtpToken item) + { + Item = item; + } + + public OtpToken Item { get; } +} diff --git a/src/CMSMicroservice.Domain/Events/OtpTokenEvents/VerifyOtpTokenEvent.cs b/src/CMSMicroservice.Domain/Events/OtpTokenEvents/VerifyOtpTokenEvent.cs new file mode 100644 index 0000000..a7ed2b1 --- /dev/null +++ b/src/CMSMicroservice.Domain/Events/OtpTokenEvents/VerifyOtpTokenEvent.cs @@ -0,0 +1,10 @@ +namespace CMSMicroservice.Domain.Events; +public class VerifyOtpTokenEvent : BaseEvent +{ + public VerifyOtpTokenEvent(OtpToken item) + { + Item = item; + } + + public OtpToken Item { get; } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs b/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs index bc84efd..1487938 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs @@ -39,11 +39,12 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext return await base.SaveChangesAsync(cancellationToken); } - public DbSet Users => Set(); public DbSet UserAddresss => Set(); public DbSet Packages => Set(); public DbSet UserOrders => Set(); public DbSet Roles => Set(); public DbSet UserRoles => Set(); + public DbSet Users => Set(); + public DbSet OtpTokens => Set(); } \ No newline at end of file diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/OtpTokenConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/OtpTokenConfiguration.cs new file mode 100644 index 0000000..15f14b1 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/OtpTokenConfiguration.cs @@ -0,0 +1,22 @@ +using CMSMicroservice.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace CMSMicroservice.Infrastructure.Persistence.Configurations; + +public class OtpTokenConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasQueryFilter(p => !p.IsDeleted); + builder.Ignore(entity => entity.DomainEvents); + builder.HasKey(entity => entity.Id); + builder.Property(entity => entity.Id).UseIdentityColumn(); + builder.Property(entity => entity.Mobile).IsRequired(true); + builder.Property(entity => entity.Purpose).IsRequired(true); + builder.Property(entity => entity.CodeHash).IsRequired(true); + builder.Property(entity => entity.ExpiresAt).IsRequired(true); + builder.Property(entity => entity.Attempts).IsRequired(true); + builder.Property(entity => entity.IsUsed).IsRequired(true); + } +} \ No newline at end of file diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserConfiguration.cs index b69e77e..0493b73 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserConfiguration.cs @@ -22,5 +22,8 @@ public class UserConfiguration : IEntityTypeConfiguration .WithMany(entity => entity.Users) .HasForeignKey(entity => entity.ParentId) .IsRequired(false); + builder.Property(entity => entity.ReferralCode).IsRequired(true); + builder.Property(entity => entity.IsMobileVerified ).IsRequired(true); + builder.Property(entity => entity.MobileVerifiedAt).IsRequired(false); } } \ No newline at end of file diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.Designer.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.Designer.cs new file mode 100644 index 0000000..a79d5f8 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.Designer.cs @@ -0,0 +1,436 @@ +// +using System; +using CMSMicroservice.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace CMSMicroservice.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250927194115_u02")] + partial class u02 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("CMS") + .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.OtpToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Attempts") + .HasColumnType("int"); + + b.Property("CodeHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsUsed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Mobile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Purpose") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OtpTokens", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Packages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AvatarPath") + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsMobileVerified") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("Mobile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MobileVerifiedAt") + .HasColumnType("datetime2"); + + b.Property("NationalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("bigint"); + + b.Property("ReferralCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("Users", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CityId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDefault") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserAddresss", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PaymentDate") + .HasColumnType("datetime2"); + + b.Property("PaymentStatus") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("UserId"); + + b.ToTable("UserOrders", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("UserRoles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "Parent") + .WithMany("Users") + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserAddresss") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("UserOrders") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserOrders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => + { + b.Navigation("UserOrders"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.Navigation("UserAddresss"); + + b.Navigation("UserOrders"); + + b.Navigation("UserRoles"); + + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.cs new file mode 100644 index 0000000..fcf6850 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20250927194115_u02.cs @@ -0,0 +1,85 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace CMSMicroservice.Infrastructure.Persistence.Migrations +{ + /// + public partial class u02 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsMobileVerified", + schema: "CMS", + table: "Users", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "MobileVerifiedAt", + schema: "CMS", + table: "Users", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "ReferralCode", + schema: "CMS", + table: "Users", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.CreateTable( + name: "OtpTokens", + schema: "CMS", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Mobile = table.Column(type: "nvarchar(max)", nullable: false), + Purpose = table.Column(type: "nvarchar(max)", nullable: false), + CodeHash = table.Column(type: "nvarchar(max)", nullable: false), + ExpiresAt = table.Column(type: "datetime2", nullable: false), + Attempts = table.Column(type: "int", nullable: false), + IsUsed = table.Column(type: "bit", nullable: false), + Created = table.Column(type: "datetime2", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + LastModified = table.Column(type: "datetime2", nullable: true), + LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OtpTokens", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OtpTokens", + schema: "CMS"); + + migrationBuilder.DropColumn( + name: "IsMobileVerified", + schema: "CMS", + table: "Users"); + + migrationBuilder.DropColumn( + name: "MobileVerifiedAt", + schema: "CMS", + table: "Users"); + + migrationBuilder.DropColumn( + name: "ReferralCode", + schema: "CMS", + table: "Users"); + } + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs index 4db6a33..cf4360b 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs @@ -23,6 +23,55 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.OtpToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Attempts") + .HasColumnType("int"); + + b.Property("CodeHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsUsed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Mobile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Purpose") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OtpTokens", "CMS"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => { b.Property("Id") @@ -125,6 +174,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("IsDeleted") .HasColumnType("bit"); + b.Property("IsMobileVerified") + .HasColumnType("bit"); + b.Property("LastModified") .HasColumnType("datetime2"); @@ -138,12 +190,19 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("MobileVerifiedAt") + .HasColumnType("datetime2"); + b.Property("NationalCode") .HasColumnType("nvarchar(max)"); b.Property("ParentId") .HasColumnType("bigint"); + b.Property("ReferralCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.HasKey("Id"); b.HasIndex("ParentId"); diff --git a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj index 502a4e4..97553fd 100644 --- a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj +++ b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj @@ -4,7 +4,7 @@ net7.0 enable enable - 0.0.111 + 0.0.112 None False False @@ -29,6 +29,7 @@ + diff --git a/src/CMSMicroservice.Protobuf/Protos/otptoken.proto b/src/CMSMicroservice.Protobuf/Protos/otptoken.proto new file mode 100644 index 0000000..ed89925 --- /dev/null +++ b/src/CMSMicroservice.Protobuf/Protos/otptoken.proto @@ -0,0 +1,87 @@ +syntax = "proto3"; + +package otptoken; + +import "public_messages.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "google/api/annotations.proto"; + +option csharp_namespace = "CMSMicroservice.Protobuf.Protos.OtpToken"; + +service OtpTokenContract +{ + rpc CreateNewOtpToken(CreateNewOtpTokenRequest) returns (CreateNewOtpTokenResponse){ + option (google.api.http) = { + post: "/CreateNewOtpToken" + body: "*" + }; + }; + rpc VerifyOtpToken(VerifyOtpTokenRequest) returns (VerifyOtpTokenResponse){ + option (google.api.http) = { + post: "/VerifyOtpToken" + body: "*" + }; + }; + rpc GetAllOtpTokenByFilter(GetAllOtpTokenByFilterRequest) returns (GetAllOtpTokenByFilterResponse){ + option (google.api.http) = { + get: "/GetAllOtpTokenByFilter" + + }; + }; +} +message CreateNewOtpTokenRequest +{ + string mobile = 1; + string purpose = 2; +} +message CreateNewOtpTokenResponse +{ + bool success = 1; + string message = 2; + google.protobuf.StringValue code = 3; +} +message VerifyOtpTokenRequest +{ + string mobile = 1; + string purpose = 2; + string code = 3; +} +message VerifyOtpTokenResponse +{ + bool success = 1; + string message = 2; +} +message GetAllOtpTokenByFilterRequest +{ + messages.PaginationState pagination_state = 1; + google.protobuf.StringValue sort_by = 2; + GetAllOtpTokenByFilterFilter filter = 3; +} +message GetAllOtpTokenByFilterFilter +{ + google.protobuf.Int64Value id = 1; + google.protobuf.StringValue mobile = 2; + google.protobuf.StringValue purpose = 3; + google.protobuf.StringValue code_hash = 4; + google.protobuf.Timestamp expires_at = 5; + google.protobuf.Int32Value attempts = 6; + google.protobuf.BoolValue is_used = 7; +} +message GetAllOtpTokenByFilterResponse +{ + messages.MetaData meta_data = 1; + repeated GetAllOtpTokenByFilterResponseModel models = 2; +} +message GetAllOtpTokenByFilterResponseModel +{ + int64 id = 1; + string mobile = 2; + string purpose = 3; + string code_hash = 4; + google.protobuf.Timestamp expires_at = 5; + int32 attempts = 6; + bool is_used = 7; +} diff --git a/src/CMSMicroservice.Protobuf/Protos/user.proto b/src/CMSMicroservice.Protobuf/Protos/user.proto index 0af6c13..6deaa50 100644 --- a/src/CMSMicroservice.Protobuf/Protos/user.proto +++ b/src/CMSMicroservice.Protobuf/Protos/user.proto @@ -62,10 +62,8 @@ message UpdateUserRequest int64 id = 1; google.protobuf.StringValue first_name = 2; google.protobuf.StringValue last_name = 3; - string mobile = 4; - google.protobuf.StringValue national_code = 5; - google.protobuf.StringValue avatar_path = 6; - google.protobuf.Int64Value parent_id = 7; + google.protobuf.StringValue national_code = 4; + google.protobuf.StringValue avatar_path = 5; } message DeleteUserRequest { @@ -84,6 +82,9 @@ message GetUserResponse google.protobuf.StringValue national_code = 5; google.protobuf.StringValue avatar_path = 6; google.protobuf.Int64Value parent_id = 7; + string referral_code = 8; + bool is_mobile_verified = 9; + google.protobuf.Timestamp mobile_verified_at = 10; } message GetAllUserByFilterRequest { @@ -100,6 +101,9 @@ message GetAllUserByFilterFilter google.protobuf.StringValue national_code = 5; google.protobuf.StringValue avatar_path = 6; google.protobuf.Int64Value parent_id = 7; + google.protobuf.StringValue referral_code = 8; + google.protobuf.BoolValue is_mobile_verified = 9; + google.protobuf.Timestamp mobile_verified_at = 10; } message GetAllUserByFilterResponse { @@ -115,4 +119,7 @@ message GetAllUserByFilterResponseModel google.protobuf.StringValue national_code = 5; google.protobuf.StringValue avatar_path = 6; google.protobuf.Int64Value parent_id = 7; + string referral_code = 8; + bool is_mobile_verified = 9; + google.protobuf.Timestamp mobile_verified_at = 10; } diff --git a/src/CMSMicroservice.Protobuf/Validator/OtpToken/CreateNewOtpTokenRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/OtpToken/CreateNewOtpTokenRequestValidator.cs new file mode 100644 index 0000000..5633ef5 --- /dev/null +++ b/src/CMSMicroservice.Protobuf/Validator/OtpToken/CreateNewOtpTokenRequestValidator.cs @@ -0,0 +1,21 @@ +using FluentValidation; +using CMSMicroservice.Protobuf.Protos.OtpToken; +namespace CMSMicroservice.Protobuf.Validator.OtpToken; + +public class CreateNewOtpTokenRequestValidator : AbstractValidator +{ + public CreateNewOtpTokenRequestValidator() + { + RuleFor(model => model.Mobile) + .NotEmpty(); + RuleFor(model => model.Purpose) + .NotEmpty(); + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((CreateNewOtpTokenRequest)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Protobuf/Validator/OtpToken/GetAllOtpTokenByFilterRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/OtpToken/GetAllOtpTokenByFilterRequestValidator.cs new file mode 100644 index 0000000..07be630 --- /dev/null +++ b/src/CMSMicroservice.Protobuf/Validator/OtpToken/GetAllOtpTokenByFilterRequestValidator.cs @@ -0,0 +1,17 @@ +using FluentValidation; +using CMSMicroservice.Protobuf.Protos.OtpToken; +namespace CMSMicroservice.Protobuf.Validator.OtpToken; + +public class GetAllOtpTokenByFilterRequestValidator : AbstractValidator +{ + public GetAllOtpTokenByFilterRequestValidator() + { + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetAllOtpTokenByFilterRequest)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Protobuf/Validator/OtpToken/VerifyOtpTokenRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/OtpToken/VerifyOtpTokenRequestValidator.cs new file mode 100644 index 0000000..116aedc --- /dev/null +++ b/src/CMSMicroservice.Protobuf/Validator/OtpToken/VerifyOtpTokenRequestValidator.cs @@ -0,0 +1,23 @@ +using FluentValidation; +using CMSMicroservice.Protobuf.Protos.OtpToken; +namespace CMSMicroservice.Protobuf.Validator.OtpToken; + +public class VerifyOtpTokenRequestValidator : AbstractValidator +{ + public VerifyOtpTokenRequestValidator() + { + RuleFor(model => model.Mobile) + .NotEmpty(); + RuleFor(model => model.Purpose) + .NotEmpty(); + RuleFor(model => model.Code) + .NotEmpty(); + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((VerifyOtpTokenRequest)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Protobuf/Validator/User/UpdateUserRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/User/UpdateUserRequestValidator.cs index f65bc5f..335859e 100644 --- a/src/CMSMicroservice.Protobuf/Validator/User/UpdateUserRequestValidator.cs +++ b/src/CMSMicroservice.Protobuf/Validator/User/UpdateUserRequestValidator.cs @@ -8,8 +8,6 @@ public class UpdateUserRequestValidator : AbstractValidator { RuleFor(model => model.Id) .NotNull(); - RuleFor(model => model.Mobile) - .NotEmpty(); } public Func>> ValidateValue => async (model, propertyName) => { diff --git a/src/CMSMicroservice.WebApi/Common/Mappings/OtpTokenProfile.cs b/src/CMSMicroservice.WebApi/Common/Mappings/OtpTokenProfile.cs new file mode 100644 index 0000000..bbbef34 --- /dev/null +++ b/src/CMSMicroservice.WebApi/Common/Mappings/OtpTokenProfile.cs @@ -0,0 +1,10 @@ +namespace CMSMicroservice.WebApi.Common.Mappings; + +public class OtpTokenProfile : IRegister +{ + void IRegister.Register(TypeAdapterConfig config) + { + //config.NewConfig() + // .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}"); + } +} diff --git a/src/CMSMicroservice.WebApi/Services/OtpTokenService.cs b/src/CMSMicroservice.WebApi/Services/OtpTokenService.cs new file mode 100644 index 0000000..694661e --- /dev/null +++ b/src/CMSMicroservice.WebApi/Services/OtpTokenService.cs @@ -0,0 +1,27 @@ +using CMSMicroservice.Protobuf.Protos.OtpToken; +using CMSMicroservice.WebApi.Common.Services; +using CMSMicroservice.Application.OtpTokenCQ.Commands.CreateNewOtpToken; +using CMSMicroservice.Application.OtpTokenCQ.Commands.VerifyOtpToken; +using CMSMicroservice.Application.OtpTokenCQ.Queries.GetAllOtpTokenByFilter; +namespace CMSMicroservice.WebApi.Services; +public class OtpTokenService : OtpTokenContract.OtpTokenContractBase +{ + private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS; + + public OtpTokenService(IDispatchRequestToCQRS dispatchRequestToCQRS) + { + _dispatchRequestToCQRS = dispatchRequestToCQRS; + } + public override async Task CreateNewOtpToken(CreateNewOtpTokenRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + public override async Task VerifyOtpToken(VerifyOtpTokenRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + public override async Task GetAllOtpTokenByFilter(GetAllOtpTokenByFilterRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } +} diff --git a/src/CMSMicroservice.WebApi/appsettings.json b/src/CMSMicroservice.WebApi/appsettings.json index 7242cd5..9b32709 100644 --- a/src/CMSMicroservice.WebApi/appsettings.json +++ b/src/CMSMicroservice.WebApi/appsettings.json @@ -7,6 +7,9 @@ "DefaultConnection": "Data Source=185.252.31.42,2019; Initial Catalog=Foursat;User ID=afrino;Password=87zH26nbqT%;Connection Timeout=300000;MultipleActiveResultSets=True;Encrypt=False", "providerName": "System.Data.SqlClient" }, + "Otp": { + "Secret": "K2w8k1h1mH2Qz1kqWk0c8kQ2Pq8q9H1eE2nqN1qQ8x7M=" + }, "AllowedHosts": "*", "Kestrel": { "EndpointDefaults": {