feat(transactions): enhance transaction filtering and summary capabilities
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
- Added new date range filters (CreatedFrom, CreatedTo, PaymentDateFrom, PaymentDateTo) to GetAllTransactionsByFilterQuery for improved transaction querying. - Updated GetAllTransactionsByFilterQueryHandler to apply new filters and return detailed transaction models. - Introduced GetTransactionSummary RPC method in Protobuf for summarizing transaction data. - Enhanced mapping configurations to accommodate new filter properties and response models. - Bumped Protobuf project version to reflect the addition of new features.
This commit is contained in:
+4
@@ -26,4 +26,8 @@ public record GetAllTransactionsByFilterQuery : IRequest<GetAllTransactionsByFil
|
|||||||
public string? RefId { get; set; }
|
public string? RefId { get; set; }
|
||||||
//
|
//
|
||||||
public TransactionType? Type { get; set; }
|
public TransactionType? Type { get; set; }
|
||||||
|
public DateTime? CreatedFrom { get; set; }
|
||||||
|
public DateTime? CreatedTo { get; set; }
|
||||||
|
public DateTime? PaymentDateFrom { get; set; }
|
||||||
|
public DateTime? PaymentDateTo { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-17
@@ -1,4 +1,5 @@
|
|||||||
namespace CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
namespace CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||||
|
|
||||||
public class GetAllTransactionsByFilterQueryHandler : IRequestHandler<GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponseDto>
|
public class GetAllTransactionsByFilterQueryHandler : IRequestHandler<GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponseDto>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
private readonly IApplicationDbContext _context;
|
||||||
@@ -11,26 +12,27 @@ public class GetAllTransactionsByFilterQueryHandler : IRequestHandler<GetAllTran
|
|||||||
public async Task<GetAllTransactionsByFilterResponseDto> Handle(GetAllTransactionsByFilterQuery request, CancellationToken cancellationToken)
|
public async Task<GetAllTransactionsByFilterResponseDto> Handle(GetAllTransactionsByFilterQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var query = _context.Transactions
|
var query = _context.Transactions
|
||||||
.ApplyOrder(sortBy: request.SortBy)
|
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.AsQueryable();
|
.ApplyTransactionFilter(request.Filter)
|
||||||
if (request.Filter is not null)
|
.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||||
{
|
|
||||||
query = query
|
|
||||||
.Where(x => request.Filter.Id == null || x.Id == request.Filter.Id)
|
|
||||||
.Where(x => request.Filter.Amount == null || x.Amount == request.Filter.Amount)
|
|
||||||
.Where(x => request.Filter.Description == null || x.Description.Contains(request.Filter.Description))
|
|
||||||
.Where(x => request.Filter.PaymentStatus == null || x.PaymentStatus== request.Filter.PaymentStatus)
|
|
||||||
.Where(x => request.Filter.PaymentDate == null || x.PaymentDate == request.Filter.PaymentDate)
|
|
||||||
.Where(x => request.Filter.RefId == null || x.RefId.Contains(request.Filter.RefId))
|
|
||||||
.Where(x => request.Filter.Type == null || x.Type == request.Filter.Type)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
return new GetAllTransactionsByFilterResponseDto
|
return new GetAllTransactionsByFilterResponseDto
|
||||||
{
|
{
|
||||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||||
Models = await query.PaginatedListAsync(paginationState: request.PaginationState)
|
Models = await query
|
||||||
.ProjectToType<GetAllTransactionsByFilterResponseModel>().ToListAsync(cancellationToken)
|
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||||
};
|
.Select(x => new GetAllTransactionsByFilterResponseModel
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
Amount = x.Amount,
|
||||||
|
Description = x.Description,
|
||||||
|
PaymentStatus = x.PaymentStatus,
|
||||||
|
PaymentDate = x.PaymentDate,
|
||||||
|
RefId = x.RefId,
|
||||||
|
Type = x.Type,
|
||||||
|
Created = x.Created
|
||||||
|
})
|
||||||
|
.ToListAsync(cancellationToken)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -24,4 +24,5 @@ public class GetAllTransactionsByFilterResponseDto
|
|||||||
public string? RefId { get; set; }
|
public string? RefId { get; set; }
|
||||||
//
|
//
|
||||||
public TransactionType Type { get; set; }
|
public TransactionType Type { get; set; }
|
||||||
|
public DateTime Created { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
using CMSMicroservice.Domain.Entities;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||||
|
|
||||||
|
public static class TransactionFilterExtensions
|
||||||
|
{
|
||||||
|
public static IQueryable<Transaction> ApplyTransactionFilter(
|
||||||
|
this IQueryable<Transaction> query,
|
||||||
|
GetAllTransactionsByFilterFilter? filter)
|
||||||
|
{
|
||||||
|
if (filter == null) return query;
|
||||||
|
|
||||||
|
if (filter.Id.HasValue)
|
||||||
|
query = query.Where(x => x.Id == filter.Id.Value);
|
||||||
|
|
||||||
|
if (filter.Amount.HasValue)
|
||||||
|
query = query.Where(x => x.Amount == filter.Amount.Value);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(filter.Description))
|
||||||
|
query = query.Where(x => x.Description.Contains(filter.Description));
|
||||||
|
|
||||||
|
if (filter.PaymentStatus.HasValue)
|
||||||
|
query = query.Where(x => x.PaymentStatus == filter.PaymentStatus.Value);
|
||||||
|
|
||||||
|
if (filter.PaymentDate.HasValue)
|
||||||
|
query = query.Where(x => x.PaymentDate == filter.PaymentDate.Value);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(filter.RefId))
|
||||||
|
query = query.Where(x => x.RefId != null && x.RefId.Contains(filter.RefId));
|
||||||
|
|
||||||
|
if (filter.Type.HasValue)
|
||||||
|
query = query.Where(x => x.Type == filter.Type.Value);
|
||||||
|
|
||||||
|
if (filter.CreatedFrom.HasValue)
|
||||||
|
query = query.Where(x => x.Created >= filter.CreatedFrom.Value);
|
||||||
|
|
||||||
|
if (filter.CreatedTo.HasValue)
|
||||||
|
query = query.Where(x => x.Created <= filter.CreatedTo.Value);
|
||||||
|
|
||||||
|
if (filter.PaymentDateFrom.HasValue)
|
||||||
|
query = query.Where(x => x.PaymentDate >= filter.PaymentDateFrom.Value);
|
||||||
|
|
||||||
|
if (filter.PaymentDateTo.HasValue)
|
||||||
|
query = query.Where(x => x.PaymentDate <= filter.PaymentDateTo.Value);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
using CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactionSummary;
|
||||||
|
|
||||||
|
public record GetTransactionSummaryQuery : IRequest<GetTransactionSummaryResponseDto>
|
||||||
|
{
|
||||||
|
public GetAllTransactionsByFilterFilter? Filter { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetTransactionSummaryResponseDto
|
||||||
|
{
|
||||||
|
public long TotalCount { get; set; }
|
||||||
|
public long TotalAmount { get; set; }
|
||||||
|
public long SuccessCount { get; set; }
|
||||||
|
public long PendingCount { get; set; }
|
||||||
|
public long RejectCount { get; set; }
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
using CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||||
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactionSummary;
|
||||||
|
|
||||||
|
public class GetTransactionSummaryQueryHandler
|
||||||
|
: IRequestHandler<GetTransactionSummaryQuery, GetTransactionSummaryResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public GetTransactionSummaryQueryHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetTransactionSummaryResponseDto> Handle(
|
||||||
|
GetTransactionSummaryQuery request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var query = _context.Transactions
|
||||||
|
.AsNoTracking()
|
||||||
|
.ApplyTransactionFilter(request.Filter);
|
||||||
|
|
||||||
|
var totalCount = await query.LongCountAsync(cancellationToken);
|
||||||
|
var totalAmount = totalCount > 0 ? await query.SumAsync(x => x.Amount, cancellationToken) : 0;
|
||||||
|
var successCount = await query.LongCountAsync(x => x.PaymentStatus == PaymentStatus.Success, cancellationToken);
|
||||||
|
var pendingCount = await query.LongCountAsync(x => x.PaymentStatus == PaymentStatus.Pending, cancellationToken);
|
||||||
|
var rejectCount = await query.LongCountAsync(x => x.PaymentStatus == PaymentStatus.Reject, cancellationToken);
|
||||||
|
|
||||||
|
return new GetTransactionSummaryResponseDto
|
||||||
|
{
|
||||||
|
TotalCount = totalCount,
|
||||||
|
TotalAmount = totalAmount,
|
||||||
|
SuccessCount = successCount,
|
||||||
|
PendingCount = pendingCount,
|
||||||
|
RejectCount = rejectCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>0.0.202</Version>
|
<Version>0.0.203</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
|
|||||||
@@ -109,6 +109,13 @@ enum TransactionType
|
|||||||
DepositIpg = 1;
|
DepositIpg = 1;
|
||||||
DepositExternal1 = 2;
|
DepositExternal1 = 2;
|
||||||
Withdraw = 3;
|
Withdraw = 3;
|
||||||
|
NetworkCommission = 10;
|
||||||
|
ClubActivation = 11;
|
||||||
|
DiscountWalletCharge = 12;
|
||||||
|
DiscountShopPurchase = 13;
|
||||||
|
MagicWalletDeposit = 14;
|
||||||
|
MagicWalletBonus = 15;
|
||||||
|
CreditWalletCharge = 16;
|
||||||
}
|
}
|
||||||
enum ContractType
|
enum ContractType
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ service TransactionsContract
|
|||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
rpc GetTransactionSummary(GetTransactionSummaryRequest) returns (GetTransactionSummaryResponse){
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/GetTransactionSummary"
|
||||||
|
};
|
||||||
|
};
|
||||||
rpc VerifyTransaction(VerifyTransactionRequest) returns (VerifyTransactionResponse){
|
rpc VerifyTransaction(VerifyTransactionRequest) returns (VerifyTransactionResponse){
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
post: "/VerifyTransaction"
|
post: "/VerifyTransaction"
|
||||||
@@ -161,6 +166,10 @@ message GetAllTransactionsByFilterFilter
|
|||||||
{
|
{
|
||||||
messages.TransactionType type = 7;
|
messages.TransactionType type = 7;
|
||||||
}
|
}
|
||||||
|
google.protobuf.Timestamp created_from = 8;
|
||||||
|
google.protobuf.Timestamp created_to = 9;
|
||||||
|
google.protobuf.Timestamp payment_date_from = 10;
|
||||||
|
google.protobuf.Timestamp payment_date_to = 11;
|
||||||
}
|
}
|
||||||
message GetAllTransactionsByFilterResponse
|
message GetAllTransactionsByFilterResponse
|
||||||
{
|
{
|
||||||
@@ -182,6 +191,21 @@ message GetAllTransactionsByFilterResponseModel
|
|||||||
{
|
{
|
||||||
messages.TransactionType type = 7;
|
messages.TransactionType type = 7;
|
||||||
}
|
}
|
||||||
|
google.protobuf.Timestamp created = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTransactionSummaryRequest
|
||||||
|
{
|
||||||
|
GetAllTransactionsByFilterFilter filter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTransactionSummaryResponse
|
||||||
|
{
|
||||||
|
int64 total_count = 1;
|
||||||
|
int64 total_amount = 2;
|
||||||
|
int64 success_count = 3;
|
||||||
|
int64 pending_count = 4;
|
||||||
|
int64 reject_count = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyTransaction Messages
|
// VerifyTransaction Messages
|
||||||
|
|||||||
@@ -1,11 +1,55 @@
|
|||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Mapster;
|
||||||
|
using AppTx = CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||||
|
using AppTxSummary = CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactionSummary;
|
||||||
|
using ProtoTx = CMSMicroservice.Protobuf.Protos.Transactions;
|
||||||
|
|
||||||
namespace CMSMicroservice.WebApi.Common.Mappings;
|
namespace CMSMicroservice.WebApi.Common.Mappings;
|
||||||
|
|
||||||
public class TransactionsProfile : IRegister
|
public class TransactionsProfile : IRegister
|
||||||
{
|
{
|
||||||
void IRegister.Register(TypeAdapterConfig config)
|
public void Register(TypeAdapterConfig config)
|
||||||
{
|
{
|
||||||
config.NewConfig<Protobuf.Protos.Transactions.GetAllTransactionsByFilterRequest, Application.TransactionsCQ.Queries.GetAllTransactionsByFilter.GetAllTransactionsByFilterQuery>()
|
config.NewConfig<ProtoTx.GetAllTransactionsByFilterRequest, AppTx.GetAllTransactionsByFilterQuery>()
|
||||||
.IgnoreIf((src, dest) => !src.Filter.HasPaymentStatus, dest => dest.Filter.PaymentStatus)
|
.Map(dest => dest.PaginationState, src => src.PaginationState)
|
||||||
.IgnoreIf((src, dest) => !src.Filter.HasType, dest => dest.Filter.Type);
|
.Map(dest => dest.SortBy, src => src.SortBy)
|
||||||
|
.Map(dest => dest.Filter, src => src.Filter);
|
||||||
|
|
||||||
|
config.NewConfig<ProtoTx.GetAllTransactionsByFilterFilter, AppTx.GetAllTransactionsByFilterFilter>()
|
||||||
|
.Map(dest => dest.Id, src => src.Id)
|
||||||
|
.Map(dest => dest.Amount, src => src.Amount)
|
||||||
|
.Map(dest => dest.Description, src => src.Description)
|
||||||
|
.Map(dest => dest.PaymentStatus, src => src.HasPaymentStatus ? (Domain.Enums.PaymentStatus?)src.PaymentStatus : null)
|
||||||
|
.Map(dest => dest.PaymentDate, src => src.PaymentDate != null ? src.PaymentDate.ToDateTime() : (DateTime?)null)
|
||||||
|
.Map(dest => dest.RefId, src => src.RefId)
|
||||||
|
.Map(dest => dest.Type, src => src.HasType ? (Domain.Enums.TransactionType?)src.Type : null)
|
||||||
|
.Map(dest => dest.CreatedFrom, src => src.CreatedFrom != null ? src.CreatedFrom.ToDateTime() : (DateTime?)null)
|
||||||
|
.Map(dest => dest.CreatedTo, src => src.CreatedTo != null ? src.CreatedTo.ToDateTime() : (DateTime?)null)
|
||||||
|
.Map(dest => dest.PaymentDateFrom, src => src.PaymentDateFrom != null ? src.PaymentDateFrom.ToDateTime() : (DateTime?)null)
|
||||||
|
.Map(dest => dest.PaymentDateTo, src => src.PaymentDateTo != null ? src.PaymentDateTo.ToDateTime() : (DateTime?)null);
|
||||||
|
|
||||||
|
config.NewConfig<AppTx.GetAllTransactionsByFilterResponseDto, ProtoTx.GetAllTransactionsByFilterResponse>()
|
||||||
|
.Map(dest => dest.MetaData, src => src.MetaData)
|
||||||
|
.Map(dest => dest.Models, src => src.Models);
|
||||||
|
|
||||||
|
config.NewConfig<AppTx.GetAllTransactionsByFilterResponseModel, ProtoTx.GetAllTransactionsByFilterResponseModel>()
|
||||||
|
.Map(dest => dest.Id, src => src.Id)
|
||||||
|
.Map(dest => dest.Amount, src => src.Amount)
|
||||||
|
.Map(dest => dest.Description, src => src.Description)
|
||||||
|
.Map(dest => dest.PaymentStatus, src => src.PaymentStatus)
|
||||||
|
.Map(dest => dest.PaymentDate, src => src.PaymentDate.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.PaymentDate.Value, DateTimeKind.Utc)) : null)
|
||||||
|
.Map(dest => dest.RefId, src => src.RefId)
|
||||||
|
.Map(dest => dest.Type, src => src.Type)
|
||||||
|
.Map(dest => dest.Created, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.Created, DateTimeKind.Utc)));
|
||||||
|
|
||||||
|
config.NewConfig<ProtoTx.GetTransactionSummaryRequest, AppTxSummary.GetTransactionSummaryQuery>()
|
||||||
|
.Map(dest => dest.Filter, src => src.Filter);
|
||||||
|
|
||||||
|
config.NewConfig<AppTxSummary.GetTransactionSummaryResponseDto, ProtoTx.GetTransactionSummaryResponse>()
|
||||||
|
.Map(dest => dest.TotalCount, src => src.TotalCount)
|
||||||
|
.Map(dest => dest.TotalAmount, src => src.TotalAmount)
|
||||||
|
.Map(dest => dest.SuccessCount, src => src.SuccessCount)
|
||||||
|
.Map(dest => dest.PendingCount, src => src.PendingCount)
|
||||||
|
.Map(dest => dest.RejectCount, src => src.RejectCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using CMSMicroservice.Application.TransactionsCQ.Commands.UpdateTransactions;
|
|||||||
using CMSMicroservice.Application.TransactionsCQ.Commands.DeleteTransactions;
|
using CMSMicroservice.Application.TransactionsCQ.Commands.DeleteTransactions;
|
||||||
using CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactions;
|
using CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactions;
|
||||||
using CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
using CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||||
|
using CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactionSummary;
|
||||||
using CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
|
using CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
|
||||||
using CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
|
using CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
|
||||||
using CMSMicroservice.Application.TransactionsCQ.Queries.GetCustomerTransaction;
|
using CMSMicroservice.Application.TransactionsCQ.Queries.GetCustomerTransaction;
|
||||||
@@ -69,6 +70,10 @@ public class TransactionsService : TransactionsContract.TransactionsContractBase
|
|||||||
{
|
{
|
||||||
return await _dispatchRequestToCQRS.Handle<GetAllTransactionsByFilterRequest, GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponse>(request, context);
|
return await _dispatchRequestToCQRS.Handle<GetAllTransactionsByFilterRequest, GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponse>(request, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Task<GetTransactionSummaryResponse> GetTransactionSummary(
|
||||||
|
GetTransactionSummaryRequest request, ServerCallContext context) =>
|
||||||
|
_dispatchRequestToCQRS.Handle<GetTransactionSummaryRequest, GetTransactionSummaryQuery, GetTransactionSummaryResponse>(request, context);
|
||||||
|
|
||||||
public override async Task<VerifyTransactionResponse> VerifyTransaction(VerifyTransactionRequest request, ServerCallContext context)
|
public override async Task<VerifyTransactionResponse> VerifyTransaction(VerifyTransactionRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user