feat(wallet): add GetMagicWalletCharges RPC and related data structures
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 16m57s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 16m57s
- Introduced GetMagicWalletCharges RPC in userwallet.proto for fetching magic wallet charges. - Added request and response message definitions for GetMagicWalletCharges, including pagination and filtering options. - Updated UserWalletService to implement the new RPC, handling request parameters and constructing the response with relevant data. - Bumped project version to 0.0.215 to reflect the new feature addition.
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Queries.GetMagicWalletCharges;
|
||||
|
||||
public class GetMagicWalletChargesQuery : IRequest<GetMagicWalletChargesResponseDto>
|
||||
{
|
||||
public PaginationState? PaginationState { get; set; }
|
||||
public string? SortBy { get; set; }
|
||||
public GetMagicWalletChargesFilter? Filter { get; set; }
|
||||
}
|
||||
|
||||
public class GetMagicWalletChargesFilter
|
||||
{
|
||||
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 GetMagicWalletChargesResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; set; } = new();
|
||||
public List<MagicWalletChargeModelDto> Models { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MagicWalletChargeModelDto
|
||||
{
|
||||
public long TransactionId { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
public long DepositAmount { get; set; }
|
||||
public double Multiplier { get; set; }
|
||||
public long CreditedAmount { get; set; }
|
||||
public long? RemainingCapacity { get; set; }
|
||||
public string? RefId { get; set; }
|
||||
public long NewBalance { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime? PaymentDate { get; set; }
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
using CMSMicroservice.Application.Common.Extensions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Queries.GetMagicWalletCharges;
|
||||
|
||||
public class GetMagicWalletChargesQueryHandler
|
||||
: IRequestHandler<GetMagicWalletChargesQuery, GetMagicWalletChargesResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GetMagicWalletChargesQueryHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetMagicWalletChargesResponseDto> Handle(
|
||||
GetMagicWalletChargesQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var filter = request.Filter;
|
||||
|
||||
var query =
|
||||
from t in _context.Transactions.AsNoTracking()
|
||||
where t.Type == TransactionType.MagicWalletDeposit
|
||||
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 MagicWalletChargeModelDto
|
||||
{
|
||||
TransactionId = t.Id,
|
||||
UserId = w.UserId,
|
||||
UserName = ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim(),
|
||||
DepositAmount = t.Amount,
|
||||
CreditedAmount = h.ChangeValue,
|
||||
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);
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
var models = await query
|
||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
model.Multiplier = model.DepositAmount == 0
|
||||
? 0
|
||||
: (double)model.CreditedAmount / model.DepositAmount;
|
||||
}
|
||||
|
||||
await PopulateRemainingCapacityAsync(models, cancellationToken);
|
||||
|
||||
return new GetMagicWalletChargesResponseDto
|
||||
{
|
||||
MetaData = meta,
|
||||
Models = models
|
||||
};
|
||||
}
|
||||
|
||||
private async Task PopulateRemainingCapacityAsync(
|
||||
List<MagicWalletChargeModelDto> models,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (models.Count == 0)
|
||||
return;
|
||||
|
||||
var userIds = models.Select(m => m.UserId).Distinct().ToList();
|
||||
|
||||
var wallets = await _context.UserWallets.AsNoTracking()
|
||||
.Where(w => userIds.Contains(w.UserId))
|
||||
.Select(w => new { w.UserId, w.MagicActivatedAt })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var walletByUser = wallets.ToDictionary(w => w.UserId, w => w.MagicActivatedAt);
|
||||
|
||||
var cycles = await _context.ClubMembershipCycles.AsNoTracking()
|
||||
.Where(c => userIds.Contains(c.UserId) && c.IsCurrentCycle)
|
||||
.Select(c => new { c.UserId, c.PackageId })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var packageIds = cycles.Select(c => c.PackageId).Distinct().ToList();
|
||||
var packages = await _context.Packages.AsNoTracking()
|
||||
.Where(p => packageIds.Contains(p.Id) || (p.IsBasePackage && !p.IsDeleted))
|
||||
.Select(p => new { p.Id, p.IsBasePackage, p.IsDeleted, p.MagicWalletMaxDeposit })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var basePackageMax = packages
|
||||
.Where(p => p.IsBasePackage && !p.IsDeleted)
|
||||
.Select(p => (long?)p.MagicWalletMaxDeposit)
|
||||
.FirstOrDefault();
|
||||
|
||||
var packageIdByUser = cycles.ToDictionary(c => c.UserId, c => c.PackageId);
|
||||
var maxByPackageId = packages.ToDictionary(p => p.Id, p => p.MagicWalletMaxDeposit);
|
||||
|
||||
// All magic deposits for these users (for cumulative sum within magic cycle)
|
||||
var depositRows = await (
|
||||
from t in _context.Transactions.AsNoTracking()
|
||||
where t.Type == TransactionType.MagicWalletDeposit
|
||||
join h in _context.UserWalletHistories.AsNoTracking() on t.Id equals h.RefrenceId
|
||||
join w in _context.UserWallets.AsNoTracking() on h.WalletId equals w.Id
|
||||
where userIds.Contains(w.UserId)
|
||||
select new { w.UserId, t.Id, t.Amount, t.Created }
|
||||
).ToListAsync(cancellationToken);
|
||||
|
||||
var depositsByUser = depositRows
|
||||
.GroupBy(d => d.UserId)
|
||||
.ToDictionary(g => g.Key, g => g.OrderBy(x => x.Created).ThenBy(x => x.Id).ToList());
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
if (!walletByUser.TryGetValue(model.UserId, out var magicActivatedAt) || !magicActivatedAt.HasValue)
|
||||
{
|
||||
model.RemainingCapacity = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
long? maxDeposit = null;
|
||||
if (packageIdByUser.TryGetValue(model.UserId, out var pkgId) &&
|
||||
maxByPackageId.TryGetValue(pkgId, out var pkgMax))
|
||||
{
|
||||
maxDeposit = pkgMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxDeposit = basePackageMax;
|
||||
}
|
||||
|
||||
if (maxDeposit is null)
|
||||
{
|
||||
model.RemainingCapacity = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!depositsByUser.TryGetValue(model.UserId, out var userDeposits))
|
||||
{
|
||||
model.RemainingCapacity = maxDeposit;
|
||||
continue;
|
||||
}
|
||||
|
||||
var depositedThroughThis = userDeposits
|
||||
.Where(d => d.Created >= magicActivatedAt.Value &&
|
||||
(d.Created < model.Created ||
|
||||
(d.Created == model.Created && d.Id <= model.TransactionId)))
|
||||
.Sum(d => d.Amount);
|
||||
|
||||
model.RemainingCapacity = Math.Max(0, maxDeposit.Value - depositedThroughThis);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.0.214</Version>
|
||||
<Version>0.0.215</Version>
|
||||
<DebugType>None</DebugType>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
|
||||
@@ -139,6 +139,14 @@ service UserWalletContract
|
||||
get: "/Admin/GetManualCreditCharges"
|
||||
};
|
||||
};
|
||||
|
||||
// ============= Admin Magic Wallet Charges =============
|
||||
|
||||
rpc GetMagicWalletCharges(GetMagicWalletChargesRequest) returns (GetMagicWalletChargesResponse){
|
||||
option (google.api.http) = {
|
||||
get: "/Admin/GetMagicWalletCharges"
|
||||
};
|
||||
};
|
||||
}
|
||||
message CreateNewUserWalletRequest
|
||||
{
|
||||
@@ -390,4 +398,41 @@ message ManualCreditChargeModel
|
||||
int64 new_balance = 7;
|
||||
google.protobuf.Timestamp created = 8;
|
||||
google.protobuf.Timestamp payment_date = 9;
|
||||
}
|
||||
|
||||
message GetMagicWalletChargesRequest
|
||||
{
|
||||
messages.PaginationState pagination_state = 1;
|
||||
google.protobuf.StringValue sort_by = 2;
|
||||
GetMagicWalletChargesFilter filter = 3;
|
||||
}
|
||||
|
||||
message GetMagicWalletChargesFilter
|
||||
{
|
||||
google.protobuf.Int64Value user_id = 1;
|
||||
google.protobuf.Int64Value transaction_id = 2;
|
||||
google.protobuf.StringValue ref_id = 3;
|
||||
google.protobuf.Timestamp created_from = 4;
|
||||
google.protobuf.Timestamp created_to = 5;
|
||||
}
|
||||
|
||||
message GetMagicWalletChargesResponse
|
||||
{
|
||||
messages.MetaData meta_data = 1;
|
||||
repeated MagicWalletChargeModel models = 2;
|
||||
}
|
||||
|
||||
message MagicWalletChargeModel
|
||||
{
|
||||
int64 transaction_id = 1;
|
||||
int64 user_id = 2;
|
||||
string user_name = 3;
|
||||
int64 deposit_amount = 4;
|
||||
double multiplier = 5;
|
||||
int64 credited_amount = 6;
|
||||
google.protobuf.Int64Value remaining_capacity = 7;
|
||||
string ref_id = 8;
|
||||
int64 new_balance = 9;
|
||||
google.protobuf.Timestamp created = 10;
|
||||
google.protobuf.Timestamp payment_date = 11;
|
||||
}
|
||||
@@ -12,6 +12,8 @@ using CMSMicroservice.Application.WalletCQ.Commands.VerifyCreditWalletCharge;
|
||||
using CMSMicroservice.Application.WalletCQ.Commands.AdminManualCreditCharge;
|
||||
using CMSMicroservice.Application.WalletCQ.Queries.GetManualCreditCharges;
|
||||
using AppManualChargeFilter = CMSMicroservice.Application.WalletCQ.Queries.GetManualCreditCharges.GetManualCreditChargesFilter;
|
||||
using CMSMicroservice.Application.WalletCQ.Queries.GetMagicWalletCharges;
|
||||
using AppMagicChargeFilter = CMSMicroservice.Application.WalletCQ.Queries.GetMagicWalletCharges.GetMagicWalletChargesFilter;
|
||||
using CMSMicroservice.Application.UserWalletCQ.Queries.GetUserWallet;
|
||||
using CMSMicroservice.Application.UserWalletCQ.Queries.GetAllUserWalletByFilter;
|
||||
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory;
|
||||
@@ -653,4 +655,70 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<GetMagicWalletChargesResponse> GetMagicWalletCharges(
|
||||
GetMagicWalletChargesRequest request, ServerCallContext context)
|
||||
{
|
||||
var result = await _sender.Send(new GetMagicWalletChargesQuery
|
||||
{
|
||||
SortBy = request.SortBy,
|
||||
PaginationState = request.PaginationState != null
|
||||
? new Application.Common.Models.PaginationState
|
||||
{
|
||||
PageNumber = request.PaginationState.PageNumber,
|
||||
PageSize = request.PaginationState.PageSize
|
||||
}
|
||||
: new Application.Common.Models.PaginationState { PageNumber = 1, PageSize = 20 },
|
||||
Filter = new AppMagicChargeFilter
|
||||
{
|
||||
UserId = request.Filter?.UserId,
|
||||
TransactionId = request.Filter?.TransactionId,
|
||||
RefId = request.Filter?.RefId,
|
||||
CreatedFrom = request.Filter?.CreatedFrom?.ToDateTime(),
|
||||
CreatedTo = request.Filter?.CreatedTo?.ToDateTime()
|
||||
}
|
||||
}, context.CancellationToken);
|
||||
|
||||
var response = new GetMagicWalletChargesResponse
|
||||
{
|
||||
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
|
||||
{
|
||||
CurrentPage = result.MetaData.CurrentPage,
|
||||
TotalPage = result.MetaData.TotalPage,
|
||||
PageSize = result.MetaData.PageSize,
|
||||
TotalCount = result.MetaData.TotalCount,
|
||||
HasPrevious = result.MetaData.HasPrevious,
|
||||
HasNext = result.MetaData.HasNext
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var m in result.Models)
|
||||
{
|
||||
var model = new MagicWalletChargeModel
|
||||
{
|
||||
TransactionId = m.TransactionId,
|
||||
UserId = m.UserId,
|
||||
UserName = m.UserName ?? string.Empty,
|
||||
DepositAmount = m.DepositAmount,
|
||||
Multiplier = m.Multiplier,
|
||||
CreditedAmount = m.CreditedAmount,
|
||||
RefId = m.RefId ?? string.Empty,
|
||||
NewBalance = m.NewBalance,
|
||||
Created = Timestamp.FromDateTime(DateTime.SpecifyKind(m.Created, DateTimeKind.Utc))
|
||||
};
|
||||
|
||||
if (m.RemainingCapacity.HasValue)
|
||||
model.RemainingCapacity = m.RemainingCapacity.Value;
|
||||
|
||||
if (m.PaymentDate.HasValue)
|
||||
{
|
||||
model.PaymentDate = Timestamp.FromDateTime(
|
||||
DateTime.SpecifyKind(m.PaymentDate.Value, DateTimeKind.Utc));
|
||||
}
|
||||
|
||||
response.Models.Add(model);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user