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
@@ -0,0 +1,22 @@
namespace CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
/// <summary>
/// Command برای استرداد تراکنش
/// </summary>
public record RefundTransactionCommand : IRequest<RefundTransactionResponseDto>
{
/// <summary>
/// شناسه تراکنش برای استرداد
/// </summary>
public long TransactionId { get; init; }
/// <summary>
/// دلیل استرداد
/// </summary>
public string RefundReason { get; init; }
/// <summary>
/// مبلغ استرداد (اگر null باشد، کل مبلغ استرداد می‌شود)
/// </summary>
public long? RefundAmount { get; init; }
}
@@ -0,0 +1,64 @@
using CMSMicroservice.Domain.Events;
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
public class RefundTransactionCommandHandler : IRequestHandler<RefundTransactionCommand, RefundTransactionResponseDto>
{
private readonly IApplicationDbContext _context;
public RefundTransactionCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<RefundTransactionResponseDto> Handle(RefundTransactionCommand request, CancellationToken cancellationToken)
{
// پیدا کردن تراکنش اصلی
var originalTransaction = await _context.Transactionss
.FirstOrDefaultAsync(t => t.Id == request.TransactionId, cancellationToken);
if (originalTransaction == null)
{
throw new NotFoundException(nameof(Transactions), request.TransactionId);
}
// چک کردن که تراکنش Success باشد
if (originalTransaction.PaymentStatus != PaymentStatus.Success)
{
throw new InvalidOperationException($"فقط تراکنش‌های موفق قابل استرداد هستند. وضعیت فعلی: {originalTransaction.PaymentStatus}");
}
// محاسبه مبلغ استرداد
var refundAmount = request.RefundAmount ?? originalTransaction.Amount;
if (refundAmount > originalTransaction.Amount)
{
throw new InvalidOperationException("مبلغ استرداد نمی‌تواند بیشتر از مبلغ اصلی باشد");
}
// ایجاد تراکنش استرداد جدید
var refundTransaction = new Transactions
{
Amount = -refundAmount, // مبلغ منفی برای استرداد
Description = $"استرداد تراکنش {request.TransactionId}: {request.RefundReason}",
PaymentStatus = PaymentStatus.Success,
PaymentDate = DateTime.UtcNow,
RefId = $"REFUND-{originalTransaction.RefId}",
Type = TransactionType.Buy // یا می‌تونیم یک نوع جدید برای Refund تعریف کنیم
};
await _context.Transactionss.AddAsync(refundTransaction, cancellationToken);
refundTransaction.AddDomainEvent(new RefundTransactionEvent(refundTransaction, originalTransaction));
await _context.SaveChangesAsync(cancellationToken);
return new RefundTransactionResponseDto
{
OriginalTransactionId = originalTransaction.Id,
RefundTransactionId = refundTransaction.Id,
RefundAmount = refundAmount,
Message = "استرداد با موفقیت انجام شد"
};
}
}
@@ -0,0 +1,22 @@
namespace CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
public class RefundTransactionCommandValidator : AbstractValidator<RefundTransactionCommand>
{
public RefundTransactionCommandValidator()
{
RuleFor(v => v.TransactionId)
.GreaterThan(0)
.WithMessage("شناسه تراکنش باید بزرگتر از صفر باشد");
RuleFor(v => v.RefundReason)
.NotEmpty()
.WithMessage("دلیل استرداد الزامی است")
.MaximumLength(500)
.WithMessage("دلیل استرداد نباید بیش از 500 کاراکتر باشد");
RuleFor(v => v.RefundAmount)
.GreaterThan(0)
.When(v => v.RefundAmount.HasValue)
.WithMessage("مبلغ استرداد باید بزرگتر از صفر باشد");
}
}
@@ -0,0 +1,9 @@
namespace CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
public class RefundTransactionResponseDto
{
public long OriginalTransactionId { get; set; }
public long RefundTransactionId { get; set; }
public long RefundAmount { get; set; }
public string Message { get; set; }
}
@@ -0,0 +1,29 @@
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
/// <summary>
/// Command برای تایید پرداخت (Callback از درگاه)
/// </summary>
public record VerifyTransactionCommand : IRequest<VerifyTransactionResponseDto>
{
/// <summary>
/// شناسه تراکنش در سیستم
/// </summary>
public long TransactionId { get; init; }
/// <summary>
/// کد رهگیری از درگاه پرداخت (RefId)
/// </summary>
public string RefId { get; init; }
/// <summary>
/// وضعیت پرداخت از درگاه
/// </summary>
public PaymentStatus Status { get; init; }
/// <summary>
/// تاریخ پرداخت
/// </summary>
public DateTime PaymentDate { get; init; }
}
@@ -0,0 +1,52 @@
using CMSMicroservice.Domain.Events;
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
public class VerifyTransactionCommandHandler : IRequestHandler<VerifyTransactionCommand, VerifyTransactionResponseDto>
{
private readonly IApplicationDbContext _context;
public VerifyTransactionCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<VerifyTransactionResponseDto> Handle(VerifyTransactionCommand request, CancellationToken cancellationToken)
{
// پیدا کردن تراکنش
var transaction = await _context.Transactionss
.FirstOrDefaultAsync(t => t.Id == request.TransactionId, cancellationToken);
if (transaction == null)
{
throw new NotFoundException(nameof(Transactions), request.TransactionId);
}
// چک کردن که تراکنش در وضعیت Pending باشد
if (transaction.PaymentStatus != PaymentStatus.Pending)
{
throw new InvalidOperationException($"Transaction {request.TransactionId} is not in Pending status. Current status: {transaction.PaymentStatus}");
}
// به‌روزرسانی وضعیت تراکنش
transaction.PaymentStatus = request.Status;
transaction.RefId = request.RefId;
transaction.PaymentDate = request.PaymentDate;
// ثبت Event
transaction.AddDomainEvent(new VerifyTransactionEvent(transaction));
await _context.SaveChangesAsync(cancellationToken);
return new VerifyTransactionResponseDto
{
TransactionId = transaction.Id,
Status = transaction.PaymentStatus,
RefId = transaction.RefId,
Message = transaction.PaymentStatus == PaymentStatus.Success
? "پرداخت با موفقیت انجام شد"
: "پرداخت ناموفق بود"
};
}
}
@@ -0,0 +1,21 @@
namespace CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
public class VerifyTransactionCommandValidator : AbstractValidator<VerifyTransactionCommand>
{
public VerifyTransactionCommandValidator()
{
RuleFor(v => v.TransactionId)
.GreaterThan(0)
.WithMessage("شناسه تراکنش باید بزرگتر از صفر باشد");
RuleFor(v => v.RefId)
.NotEmpty()
.WithMessage("کد رهگیری الزامی است")
.MaximumLength(100)
.WithMessage("کد رهگیری نباید بیش از 100 کاراکتر باشد");
RuleFor(v => v.PaymentDate)
.NotEmpty()
.WithMessage("تاریخ پرداخت الزامی است");
}
}
@@ -0,0 +1,11 @@
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
public class VerifyTransactionResponseDto
{
public long TransactionId { get; set; }
public PaymentStatus Status { get; set; }
public string RefId { get; set; }
public string Message { get; set; }
}
@@ -0,0 +1,26 @@
using Microsoft.Extensions.Logging;
using CMSMicroservice.Domain.Events;
namespace CMSMicroservice.Application.TransactionsCQ.EventHandlers.RefundTransactionEventHandlers;
public class RefundTransactionEventHandler : INotificationHandler<RefundTransactionEvent>
{
private readonly ILogger<RefundTransactionEventHandler> _logger;
public RefundTransactionEventHandler(ILogger<RefundTransactionEventHandler> logger)
{
_logger = logger;
}
public Task Handle(RefundTransactionEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation("Transaction {OriginalId} refunded with new transaction {RefundId}. Amount: {Amount}",
notification.OriginalTransaction.Id,
notification.RefundTransaction.Id,
notification.RefundTransaction.Amount);
// اینجا می‌تونیم اعلان به کاربر بفرستیم یا کارهای دیگه انجام بدیم
return Task.CompletedTask;
}
}
@@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
using CMSMicroservice.Domain.Events;
namespace CMSMicroservice.Application.TransactionsCQ.EventHandlers.VerifyTransactionEventHandlers;
public class VerifyTransactionEventHandler : INotificationHandler<VerifyTransactionEvent>
{
private readonly ILogger<VerifyTransactionEventHandler> _logger;
public VerifyTransactionEventHandler(ILogger<VerifyTransactionEventHandler> logger)
{
_logger = logger;
}
public Task Handle(VerifyTransactionEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation("Transaction {TransactionId} verified with status {Status} and RefId {RefId}",
notification.Item.Id,
notification.Item.PaymentStatus,
notification.Item.RefId);
return Task.CompletedTask;
}
}