feat: Add ClearCart command and response, implement CancelOrder command with validation, and enhance DeliveryStatus and User models

This commit is contained in:
masoodafar-web
2025-12-02 03:30:36 +03:30
parent 25fc73ae28
commit 78606cc5cc
100 changed files with 12925 additions and 8137 deletions
+4
View File
@@ -187,6 +187,10 @@ using (var scope = app.Services.CreateScope())
});
app.Logger.LogInformation("✅ Hangfire recurring job 'weekly-commission-calculation' registered (Cron: 5 0 * * 0 - Sunday 00:05 UTC)");
// Daya Loan Check: Every 15 minutes
CMSMicroservice.WebApi.Workers.DayaLoanCheckWorker.Schedule(recurringJobManager);
app.Logger.LogInformation("✅ Hangfire recurring job 'daya-loan-check' registered (Cron: */15 * * * * - Every 15 minutes)");
}
app.Run();
@@ -5,6 +5,9 @@ using CMSMicroservice.Application.TransactionsCQ.Commands.UpdateTransactions;
using CMSMicroservice.Application.TransactionsCQ.Commands.DeleteTransactions;
using CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactions;
using CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
using CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
using CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
namespace CMSMicroservice.WebApi.Services;
public class TransactionsService : TransactionsContract.TransactionsContractBase
{
@@ -34,4 +37,14 @@ public class TransactionsService : TransactionsContract.TransactionsContractBase
{
return await _dispatchRequestToCQRS.Handle<GetAllTransactionsByFilterRequest, GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponse>(request, context);
}
public override async Task<VerifyTransactionResponse> VerifyTransaction(VerifyTransactionRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<VerifyTransactionRequest, VerifyTransactionCommand, VerifyTransactionResponse>(request, context);
}
public override async Task<RefundTransactionResponse> RefundTransaction(RefundTransactionRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<RefundTransactionRequest, RefundTransactionCommand, RefundTransactionResponse>(request, context);
}
}
@@ -5,6 +5,8 @@ using CMSMicroservice.Application.UserCartsCQ.Commands.UpdateUserCarts;
using CMSMicroservice.Application.UserCartsCQ.Commands.DeleteUserCarts;
using CMSMicroservice.Application.UserCartsCQ.Queries.GetUserCarts;
using CMSMicroservice.Application.UserCartsCQ.Queries.GetAllUserCartsByFilter;
using CMSMicroservice.Application.UserCartsCQ.Commands.ClearCart;
namespace CMSMicroservice.WebApi.Services;
public class UserCartsService : UserCartsContract.UserCartsContractBase
{
@@ -34,4 +36,9 @@ public class UserCartsService : UserCartsContract.UserCartsContractBase
{
return await _dispatchRequestToCQRS.Handle<GetAllUserCartsByFilterRequest, GetAllUserCartsByFilterQuery, GetAllUserCartsByFilterResponse>(request, context);
}
public override async Task<ClearCartResponse> ClearCart(ClearCartRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ClearCartRequest, ClearCartCommand, ClearCartResponse>(request, context);
}
}
@@ -6,6 +6,8 @@ using CMSMicroservice.Application.UserOrderCQ.Commands.DeleteUserOrder;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetUserOrder;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetAllUserOrderByFilter;
using CMSMicroservice.Application.UserOrderCQ.Commands.SubmitShopBuyOrder;
using CMSMicroservice.Application.UserOrderCQ.Commands.CancelOrder;
namespace CMSMicroservice.WebApi.Services;
public class UserOrderService : UserOrderContract.UserOrderContractBase
{
@@ -39,4 +41,9 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
{
return await _dispatchRequestToCQRS.Handle<SubmitShopBuyOrderRequest, SubmitShopBuyOrderCommand, SubmitShopBuyOrderResponse>(request, context);
}
public override async Task<CancelOrderResponse> CancelOrder(CancelOrderRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<CancelOrderRequest, CancelOrderCommand, CancelOrderResponse>(request, context);
}
}
@@ -0,0 +1,121 @@
using Hangfire;
using MediatR;
using Microsoft.Extensions.Logging;
using CMSMicroservice.Application.DayaLoanCQ.Commands.CheckDayaLoanStatus;
using CMSMicroservice.Application.DayaLoanCQ.Commands.ProcessDayaLoanApproval;
using CMSMicroservice.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using CMSMicroservice.Infrastructure.Persistence;
using System.Linq;
namespace CMSMicroservice.WebApi.Workers;
/// <summary>
/// Worker برای استعلام خودکار وضعیت وام دایا (هر 15 دقیقه)
/// </summary>
public class DayaLoanCheckWorker
{
private readonly IMediator _mediator;
private readonly ApplicationDbContext _context;
private readonly ILogger<DayaLoanCheckWorker> _logger;
public DayaLoanCheckWorker(
IMediator mediator,
ApplicationDbContext context,
ILogger<DayaLoanCheckWorker> logger)
{
_mediator = mediator;
_context = context;
_logger = logger;
}
/// <summary>
/// متد اصلی که توسط Hangfire فراخوانی می‌شود
/// </summary>
[AutomaticRetry(Attempts = 3)]
public async Task ExecuteAsync()
{
_logger.LogInformation("DayaLoanCheckWorker started at {Time}", DateTime.UtcNow);
try
{
// پیدا کردن کاربرانی که اعتبار دایا را دریافت نکرده‌اند
var pendingUsers = await _context.Users
.Where(u =>
u.HasReceivedDayaCredit == false &&
u.NationalCode != null &&
u.NationalCode != "")
.Select(u => new { u.Id, u.NationalCode })
.ToListAsync();
if (!pendingUsers.Any())
{
_logger.LogInformation("No pending users found for Daya loan check");
return;
}
_logger.LogInformation("Found {Count} users with pending Daya loan status", pendingUsers.Count);
// استعلام از دایا
var checkCommand = new CheckDayaLoanStatusCommand
{
NationalCodes = pendingUsers.Select(u => u.NationalCode).ToList()
};
var checkResult = await _mediator.Send(checkCommand);
// پردازش نتایج
foreach (var result in checkResult.Results)
{
// فقط وضعیت PendingReceive را پردازش می‌کنیم (یعنی وام درخواست شده)
if (result.Status == DayaLoanStatus.PendingReceive && !string.IsNullOrEmpty(result.ContractNumber))
{
var user = pendingUsers.FirstOrDefault(u => u.NationalCode == result.NationalCode);
if (user != null)
{
try
{
// پردازش تایید وام و شارژ کیف پول
var processCommand = new ProcessDayaLoanApprovalCommand
{
UserId = user.Id,
ContractNumber = result.ContractNumber
};
var processResult = await _mediator.Send(processCommand);
_logger.LogInformation("Daya loan processed for user {UserId}. Contract: {ContractNumber}",
user.Id, result.ContractNumber);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing Daya loan for user {UserId}", user.Id);
}
}
}
}
_logger.LogInformation("DayaLoanCheckWorker completed. Checked: {Total}, Processed: {Success}",
checkResult.TotalChecked, checkResult.SuccessCount);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in DayaLoanCheckWorker");
throw; // Hangfire will retry
}
}
/// <summary>
/// متد برای Schedule کردن Worker (هر 15 دقیقه)
/// </summary>
public static void Schedule(IRecurringJobManager recurringJobManager)
{
// هر 15 دقیقه: */15 * * * *
recurringJobManager.AddOrUpdate<DayaLoanCheckWorker>(
"daya-loan-check",
worker => worker.ExecuteAsync(),
"*/15 * * * *", // هر 15 دقیقه
TimeZoneInfo.Utc
);
}
}
@@ -0,0 +1,34 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=YOUR_PRODUCTION_SERVER;Database=FourSat_CMS;User Id=YOUR_USER;Password=YOUR_PASSWORD;TrustServerCertificate=True;MultipleActiveResultSets=true"
},
"Email": {
"Enabled": true,
"SmtpHost": "smtp.gmail.com",
"SmtpPort": 587,
"SmtpUsername": "your-production-email@gmail.com",
"SmtpPassword": "your-gmail-app-password",
"FromEmail": "noreply@foursat.com",
"FromName": "FourSat CMS",
"EnableSsl": true
},
"Sms": {
"Enabled": true,
"Provider": "Kavenegar",
"KavenegarApiKey": "YOUR_PRODUCTION_KAVENEGAR_API_KEY",
"Sender": "10008663"
},
"Jwt": {
"Issuer": "https://api.foursat.com",
"Audience": "https://foursat.com",
"SecretKey": "YOUR_PRODUCTION_SECRET_KEY_MINIMUM_32_CHARACTERS_LONG"
},
"AllowedHosts": "*"
}
+13 -1
View File
@@ -1,4 +1,6 @@
{
"UseRealPaymentGateway": false,
"PaymentProvider": "BankMellat",
"JwtSecurityKey": "TvlZVx5TJaHs8e9HgUdGzhGP2CIidoI444nAj+8+g7c=",
"JwtIssuer": "https://localhost",
"JwtAudience": "https://localhost",
@@ -39,10 +41,20 @@
"KavenegarApiKey": "YOUR_KAVENEGAR_API_KEY",
"Sender": "10008663"
},
"DayaPayment": {
"BaseUrl": "https://api.daya.ir",
"ApiKey": "YOUR_DAYA_API_KEY"
},
"BankMellat": {
"ServiceUrl": "https://bpm.shaparak.ir/pgwchannel/services/pgw",
"TerminalId": "YOUR_TERMINAL_ID",
"Username": "YOUR_USERNAME",
"Password": "YOUR_PASSWORD"
},
"AllowedHosts": "*",
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
"Protocols": "Http1AndHttp2"
}
},
"Authentication": {