feat: Implement Approve and Reject Withdrawal commands with handlers

This commit is contained in:
masoodafar-web
2025-12-01 16:48:07 +03:30
parent 8d31a8c026
commit 4aaf2247ff
27 changed files with 989 additions and 1 deletions

View File

@@ -0,0 +1,7 @@
namespace CMSMicroservice.Application.CommissionCQ.Commands.ApproveWithdrawal;
public class ApproveWithdrawalCommand : IRequest<Unit>
{
public long PayoutId { get; set; }
public string? Notes { get; set; }
}

View File

@@ -0,0 +1,55 @@
using CMSMicroservice.Domain.Enums;
using CMSMicroservice.Application.Common.Exceptions;
namespace CMSMicroservice.Application.CommissionCQ.Commands.ApproveWithdrawal;
public class ApproveWithdrawalCommandHandler : IRequestHandler<ApproveWithdrawalCommand, Unit>
{
private readonly IApplicationDbContext _context;
public ApproveWithdrawalCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<Unit> Handle(ApproveWithdrawalCommand request, CancellationToken cancellationToken)
{
var payout = await _context.UserCommissionPayouts
.FirstOrDefaultAsync(x => x.Id == request.PayoutId, cancellationToken);
if (payout == null)
{
throw new NotFoundException($"Payout با شناسه {request.PayoutId} یافت نشد");
}
if (payout.Status != CommissionPayoutStatus.WithdrawRequested)
{
throw new BadRequestException($"فقط درخواست‌های در وضعیت WithdrawRequested قابل تایید هستند");
}
// Update status to Withdrawn (approved)
payout.Status = CommissionPayoutStatus.Withdrawn;
payout.WithdrawnAt = DateTime.UtcNow;
payout.LastModified = DateTime.UtcNow;
// TODO: Add PayoutHistory record
// var history = new CommissionPayoutHistory
// {
// PayoutId = payout.Id,
// UserId = payout.UserId,
// WeekNumber = payout.WeekNumber,
// AmountBefore = payout.TotalAmount,
// AmountAfter = payout.TotalAmount,
// OldStatus = (int)CommissionPayoutStatus.Pending,
// NewStatus = (int)CommissionPayoutStatus.Approved,
// Action = (int)CommissionPayoutAction.Approved,
// PerformedBy = "Admin", // TODO: Get from authenticated user
// Reason = request.Notes,
// Created = DateTime.UtcNow
// };
// _context.CommissionPayoutHistories.Add(history);
await _context.SaveChangesAsync(cancellationToken);
return Unit.Value;
}
}

View File

@@ -0,0 +1,7 @@
namespace CMSMicroservice.Application.CommissionCQ.Commands.RejectWithdrawal;
public class RejectWithdrawalCommand : IRequest<Unit>
{
public long PayoutId { get; set; }
public string Reason { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,54 @@
using CMSMicroservice.Domain.Enums;
using CMSMicroservice.Application.Common.Exceptions;
namespace CMSMicroservice.Application.CommissionCQ.Commands.RejectWithdrawal;
public class RejectWithdrawalCommandHandler : IRequestHandler<RejectWithdrawalCommand, Unit>
{
private readonly IApplicationDbContext _context;
public RejectWithdrawalCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<Unit> Handle(RejectWithdrawalCommand request, CancellationToken cancellationToken)
{
var payout = await _context.UserCommissionPayouts
.FirstOrDefaultAsync(x => x.Id == request.PayoutId, cancellationToken);
if (payout == null)
{
throw new NotFoundException($"Payout با شناسه {request.PayoutId} یافت نشد");
}
if (payout.Status != CommissionPayoutStatus.WithdrawRequested)
{
throw new BadRequestException($"فقط درخواست‌های در وضعیت WithdrawRequested قابل رد هستند");
}
// Update status to Cancelled (rejected)
payout.Status = CommissionPayoutStatus.Cancelled;
payout.LastModified = DateTime.UtcNow;
// TODO: Add PayoutHistory record with rejection reason
// var history = new CommissionPayoutHistory
// {
// PayoutId = payout.Id,
// UserId = payout.UserId,
// WeekNumber = payout.WeekNumber,
// AmountBefore = payout.TotalAmount,
// AmountAfter = payout.TotalAmount,
// OldStatus = (int)CommissionPayoutStatus.Pending,
// NewStatus = (int)CommissionPayoutStatus.Rejected,
// Action = (int)CommissionPayoutAction.Rejected,
// PerformedBy = "Admin", // TODO: Get from authenticated user
// Reason = request.Reason,
// Created = DateTime.UtcNow
// };
// _context.CommissionPayoutHistories.Add(history);
await _context.SaveChangesAsync(cancellationToken);
return Unit.Value;
}
}

View File

@@ -0,0 +1,29 @@
namespace CMSMicroservice.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
public record TriggerWeeklyCalculationCommand : IRequest<TriggerWeeklyCalculationResponseDto>
{
/// <summary>
/// شماره هفته (فرمت: "YYYY-Www")
/// </summary>
public string WeekNumber { get; init; } = string.Empty;
/// <summary>
/// اگر true باشد، محاسبات قبلی را حذف و دوباره محاسبه می‌کند
/// </summary>
public bool ForceRecalculate { get; init; }
/// <summary>
/// Skip balance calculation
/// </summary>
public bool SkipBalances { get; init; }
/// <summary>
/// Skip pool calculation
/// </summary>
public bool SkipPool { get; init; }
/// <summary>
/// Skip payout processing
/// </summary>
public bool SkipPayouts { get; init; }
}

View File

@@ -0,0 +1,95 @@
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyBalances;
using CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyCommissionPool;
using CMSMicroservice.Application.CommissionCQ.Commands.ProcessUserPayouts;
namespace CMSMicroservice.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWeeklyCalculationCommand, TriggerWeeklyCalculationResponseDto>
{
private readonly IApplicationDbContext _context;
private readonly IMediator _mediator;
public TriggerWeeklyCalculationCommandHandler(
IApplicationDbContext context,
IMediator mediator)
{
_context = context;
_mediator = mediator;
}
public async Task<TriggerWeeklyCalculationResponseDto> Handle(
TriggerWeeklyCalculationCommand request,
CancellationToken cancellationToken)
{
var executionId = Guid.NewGuid().ToString();
var startedAt = DateTime.UtcNow;
try
{
// Validate week number format
if (string.IsNullOrWhiteSpace(request.WeekNumber))
{
return new TriggerWeeklyCalculationResponseDto
{
Success = false,
Message = "شماره هفته نمی‌تواند خالی باشد",
ExecutionId = executionId,
StartedAt = startedAt
};
}
var steps = new List<string>();
// Step 1: Calculate Weekly Balances
if (!request.SkipBalances)
{
await _mediator.Send(new CalculateWeeklyBalancesCommand
{
WeekNumber = request.WeekNumber,
ForceRecalculate = request.ForceRecalculate
}, cancellationToken);
steps.Add("محاسبه امتیازات هفتگی");
}
// Step 2: Calculate Weekly Commission Pool
if (!request.SkipPool)
{
await _mediator.Send(new CalculateWeeklyCommissionPoolCommand
{
WeekNumber = request.WeekNumber
}, cancellationToken);
steps.Add("محاسبه استخر کمیسیون");
}
// Step 3: Process User Payouts
if (!request.SkipPayouts)
{
await _mediator.Send(new ProcessUserPayoutsCommand
{
WeekNumber = request.WeekNumber,
ForceReprocess = request.ForceRecalculate
}, cancellationToken);
steps.Add("پردازش پرداخت‌های کاربران");
}
return new TriggerWeeklyCalculationResponseDto
{
Success = true,
Message = $"محاسبات هفته {request.WeekNumber} با موفقیت انجام شد. مراحل: {string.Join(", ", steps)}",
ExecutionId = executionId,
StartedAt = startedAt
};
}
catch (Exception ex)
{
return new TriggerWeeklyCalculationResponseDto
{
Success = false,
Message = $"خطا در اجرای محاسبات: {ex.Message}",
ExecutionId = executionId,
StartedAt = startedAt
};
}
}
}

View File

@@ -0,0 +1,9 @@
namespace CMSMicroservice.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
public class TriggerWeeklyCalculationResponseDto
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public string ExecutionId { get; set; } = string.Empty;
public DateTime StartedAt { get; set; }
}