feat(wallet): add manual credit wallet charge functionality and update Protobuf definitions
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
- Introduced ManualCreditWalletCharge enum value to TransactionType for clarity. - Added AdminManualCreditCharge and GetManualCreditCharges RPC methods in UserWalletService for handling manual credit charges by admin. - Created corresponding request and response messages for manual credit charge operations in Protobuf. - Updated Protobuf project version to reflect the addition of new features.
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Commands.AdminManualCreditCharge;
|
||||
|
||||
public class AdminManualCreditChargeCommand : IRequest<AdminManualCreditChargeResult>
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public long Amount { get; set; }
|
||||
public string? Note { get; set; }
|
||||
public string? PerformedByAdminId { get; set; }
|
||||
}
|
||||
|
||||
public class AdminManualCreditChargeResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
public long TransactionId { get; set; }
|
||||
public long NewBalance { get; set; }
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
using CMSMicroservice.Application.Common;
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Commands.AdminManualCreditCharge;
|
||||
|
||||
public class AdminManualCreditChargeCommandHandler
|
||||
: IRequestHandler<AdminManualCreditChargeCommand, AdminManualCreditChargeResult>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<AdminManualCreditChargeCommandHandler> _logger;
|
||||
private readonly IUserPaymentLock _paymentLock;
|
||||
|
||||
public AdminManualCreditChargeCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<AdminManualCreditChargeCommandHandler> logger,
|
||||
IUserPaymentLock paymentLock)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
_paymentLock = paymentLock;
|
||||
}
|
||||
|
||||
public Task<AdminManualCreditChargeResult> Handle(
|
||||
AdminManualCreditChargeCommand request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_paymentLock.ExecuteAsync(
|
||||
PaymentLockScopes.Initiate(request.UserId),
|
||||
PaymentLockStrategy.FailFast,
|
||||
ct => HandleCore(request, ct),
|
||||
cancellationToken);
|
||||
|
||||
private async Task<AdminManualCreditChargeResult> HandleCore(
|
||||
AdminManualCreditChargeCommand request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken)
|
||||
?? throw new NotFoundException(nameof(User), request.UserId);
|
||||
|
||||
var wallet = await _context.UserWallets
|
||||
.FirstOrDefaultAsync(w => w.UserId == user.Id, cancellationToken)
|
||||
?? throw new NotFoundException($"کیف پول کاربر با شناسه {request.UserId} یافت نشد");
|
||||
|
||||
var oldBalance = wallet.Balance;
|
||||
wallet.Balance += request.Amount;
|
||||
|
||||
var refId = $"MANUAL-{Guid.NewGuid():N}";
|
||||
var notePart = string.IsNullOrWhiteSpace(request.Note)
|
||||
? string.Empty
|
||||
: $" — {request.Note.Trim()}";
|
||||
var adminPart = string.IsNullOrWhiteSpace(request.PerformedByAdminId)
|
||||
? string.Empty
|
||||
: $" (ادمین: {request.PerformedByAdminId})";
|
||||
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Amount = request.Amount,
|
||||
Description = $"شارژ دستی کیف پول اصلی - کاربر {user.Id}{notePart}{adminPart}",
|
||||
PaymentStatus = PaymentStatus.Success,
|
||||
PaymentDate = DateTime.Now,
|
||||
RefId = refId,
|
||||
Type = TransactionType.ManualCreditWalletCharge
|
||||
};
|
||||
|
||||
_context.Transactions.Add(transaction);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_context.UserWalletHistories.Add(new UserWalletHistory
|
||||
{
|
||||
WalletId = wallet.Id,
|
||||
CurrentBalance = wallet.Balance,
|
||||
ChangeValue = request.Amount,
|
||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||
ChangeNerworkValue = 0,
|
||||
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||
ChangeDiscountValue = 0,
|
||||
IsIncrease = true,
|
||||
RefrenceId = transaction.Id
|
||||
});
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Manual credit charge: UserId={UserId}, Amount={Amount}, OldBalance={Old}, NewBalance={New}, TxId={TxId}, Admin={Admin}",
|
||||
user.Id,
|
||||
request.Amount,
|
||||
oldBalance,
|
||||
wallet.Balance,
|
||||
transaction.Id,
|
||||
request.PerformedByAdminId);
|
||||
|
||||
return new AdminManualCreditChargeResult
|
||||
{
|
||||
Success = true,
|
||||
Message = "شارژ دستی کیف پول اصلی با موفقیت انجام شد",
|
||||
TransactionId = transaction.Id,
|
||||
NewBalance = wallet.Balance
|
||||
};
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using CMSMicroservice.Domain.Common;
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Commands.AdminManualCreditCharge;
|
||||
|
||||
public class AdminManualCreditChargeCommandValidator : AbstractValidator<AdminManualCreditChargeCommand>
|
||||
{
|
||||
public AdminManualCreditChargeCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.UserId)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
||||
|
||||
RuleFor(x => x.Amount)
|
||||
.GreaterThanOrEqualTo(SystemConstants.DiscountWalletMinCharge)
|
||||
.WithMessage("حداقل مبلغ شارژ ۱۰,۰۰۰ تومان است")
|
||||
.LessThanOrEqualTo(SystemConstants.WalletMaxSafeAmount)
|
||||
.WithMessage("مبلغ وارد شده بیش از حد مجاز است");
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Queries.GetManualCreditCharges;
|
||||
|
||||
public class GetManualCreditChargesQuery : IRequest<GetManualCreditChargesResponseDto>
|
||||
{
|
||||
public PaginationState? PaginationState { get; set; }
|
||||
public string? SortBy { get; set; }
|
||||
public GetManualCreditChargesFilter? Filter { get; set; }
|
||||
}
|
||||
|
||||
public class GetManualCreditChargesFilter
|
||||
{
|
||||
public long? UserId { get; set; }
|
||||
public long? TransactionId { get; set; }
|
||||
public string? RefId { get; set; }
|
||||
public DateTime? CreatedFrom { get; set; }
|
||||
public DateTime? CreatedTo { get; set; }
|
||||
}
|
||||
|
||||
public class GetManualCreditChargesResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; set; } = new();
|
||||
public List<ManualCreditChargeModelDto> Models { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ManualCreditChargeModelDto
|
||||
{
|
||||
public long TransactionId { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
public long Amount { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string? RefId { get; set; }
|
||||
public long NewBalance { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime? PaymentDate { get; set; }
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
using CMSMicroservice.Application.Common.Extensions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Queries.GetManualCreditCharges;
|
||||
|
||||
public class GetManualCreditChargesQueryHandler
|
||||
: IRequestHandler<GetManualCreditChargesQuery, GetManualCreditChargesResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GetManualCreditChargesQueryHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetManualCreditChargesResponseDto> Handle(
|
||||
GetManualCreditChargesQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var filter = request.Filter;
|
||||
|
||||
var query =
|
||||
from t in _context.Transactions.AsNoTracking()
|
||||
where t.Type == TransactionType.ManualCreditWalletCharge
|
||||
join h in _context.UserWalletHistories.AsNoTracking() on t.Id equals h.RefrenceId
|
||||
join w in _context.UserWallets.AsNoTracking() on h.WalletId equals w.Id
|
||||
join u in _context.Users.AsNoTracking() on w.UserId equals u.Id
|
||||
select new ManualCreditChargeModelDto
|
||||
{
|
||||
TransactionId = t.Id,
|
||||
UserId = w.UserId,
|
||||
UserName = ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim(),
|
||||
Amount = t.Amount,
|
||||
Description = t.Description ?? string.Empty,
|
||||
RefId = t.RefId,
|
||||
NewBalance = h.CurrentBalance,
|
||||
Created = t.Created,
|
||||
PaymentDate = t.PaymentDate
|
||||
};
|
||||
|
||||
if (filter?.UserId is long userId)
|
||||
query = query.Where(x => x.UserId == userId);
|
||||
|
||||
if (filter?.TransactionId is long txId)
|
||||
query = query.Where(x => x.TransactionId == txId);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filter?.RefId))
|
||||
query = query.Where(x => x.RefId != null && x.RefId.Contains(filter.RefId));
|
||||
|
||||
if (filter?.CreatedFrom is DateTime from)
|
||||
query = query.Where(x => x.Created >= from);
|
||||
|
||||
if (filter?.CreatedTo is DateTime to)
|
||||
query = query.Where(x => x.Created <= to);
|
||||
|
||||
query = query.OrderByDescending(x => x.Created);
|
||||
|
||||
return new GetManualCreditChargesResponseDto
|
||||
{
|
||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||
Models = await query
|
||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.ToListAsync(cancellationToken)
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user