feat(transactions): enhance transaction queries with user information
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 21m45s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 21m45s
- Added UserFullName and UserId properties to transaction response models for better user context. - Updated query handlers to populate user information from related entities, ensuring accurate user details in transaction responses. - Modified Protobuf definitions to include new fields for user identification and display. - Adjusted mapping profiles to accommodate new user-related fields in transaction data.
This commit is contained in:
+1
@@ -43,4 +43,5 @@ public class GetAllPaymentTransactionByFilterResponseModel
|
||||
public long? TransactionId { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
public string? UserFullName { get; set; }
|
||||
}
|
||||
|
||||
+34
-21
@@ -19,30 +19,43 @@ public class GetAllPaymentTransactionByFilterQueryHandler
|
||||
.ApplyPaymentTransactionFilter(request.Filter)
|
||||
.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
|
||||
var models = await (
|
||||
from x in query.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
join u in _context.Users.AsNoTracking() on x.UserId equals u.Id into ug
|
||||
from u in ug.DefaultIfEmpty()
|
||||
select new GetAllPaymentTransactionByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
Mobile = x.Mobile,
|
||||
GatewayProvider = x.GatewayProvider,
|
||||
Amount = x.Amount,
|
||||
Authority = x.Authority,
|
||||
PaymentStatus = x.PaymentStatus,
|
||||
VerificationStatusCode = x.VerificationStatusCode,
|
||||
VerificationStatusMessage = x.VerificationStatusMessage,
|
||||
RefId = x.RefId,
|
||||
CardPan = x.CardPan,
|
||||
OrderId = x.OrderId,
|
||||
TransactionId = x.TransactionId,
|
||||
Description = x.Description,
|
||||
Created = x.Created,
|
||||
UserFullName = u != null
|
||||
? ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim()
|
||||
: null
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(model.UserFullName))
|
||||
model.UserFullName = "—";
|
||||
}
|
||||
|
||||
return new GetAllPaymentTransactionByFilterResponseDto
|
||||
{
|
||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||
Models = await query
|
||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.Select(x => new GetAllPaymentTransactionByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
Mobile = x.Mobile,
|
||||
GatewayProvider = x.GatewayProvider,
|
||||
Amount = x.Amount,
|
||||
Authority = x.Authority,
|
||||
PaymentStatus = x.PaymentStatus,
|
||||
VerificationStatusCode = x.VerificationStatusCode,
|
||||
VerificationStatusMessage = x.VerificationStatusMessage,
|
||||
RefId = x.RefId,
|
||||
CardPan = x.CardPan,
|
||||
OrderId = x.OrderId,
|
||||
TransactionId = x.TransactionId,
|
||||
Description = x.Description,
|
||||
Created = x.Created
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
Models = models
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+99
-14
@@ -1,3 +1,5 @@
|
||||
using CMSMicroservice.Application.Common;
|
||||
|
||||
namespace CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||
|
||||
public class GetAllTransactionsByFilterQueryHandler : IRequestHandler<GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponseDto>
|
||||
@@ -16,23 +18,106 @@ public class GetAllTransactionsByFilterQueryHandler : IRequestHandler<GetAllTran
|
||||
.ApplyTransactionFilter(request.Filter)
|
||||
.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
|
||||
var models = await query
|
||||
.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);
|
||||
|
||||
await PopulateUserInfoAsync(models, cancellationToken);
|
||||
|
||||
return new GetAllTransactionsByFilterResponseDto
|
||||
{
|
||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||
Models = await query
|
||||
.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)
|
||||
Models = models
|
||||
};
|
||||
}
|
||||
|
||||
private async Task PopulateUserInfoAsync(
|
||||
List<GetAllTransactionsByFilterResponseModel> models,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (models.Count == 0)
|
||||
return;
|
||||
|
||||
var txIds = models.Select(m => m.Id).ToList();
|
||||
var userByTx = new Dictionary<long, long>();
|
||||
|
||||
void Merge(IEnumerable<(long TxId, long UserId)> rows)
|
||||
{
|
||||
foreach (var (txId, userId) in rows)
|
||||
{
|
||||
if (!userByTx.ContainsKey(txId))
|
||||
userByTx[txId] = userId;
|
||||
}
|
||||
}
|
||||
|
||||
// Priority: PaymentTransactions → UserPackagePurchases → UserOrders → ManualPayments
|
||||
var fromPt = await _context.PaymentTransactions.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value) && x.UserId != null)
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId!.Value })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromPt.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
var fromUpp = await _context.UserPackagePurchases.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value))
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromUpp.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
var fromOrders = await _context.UserOrders.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value))
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromOrders.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
var fromManual = await _context.ManualPayments.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value))
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromManual.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
if (userByTx.Count == 0)
|
||||
{
|
||||
foreach (var model in models)
|
||||
model.UserFullName = "—";
|
||||
return;
|
||||
}
|
||||
|
||||
var userIds = userByTx.Values.Distinct().ToList();
|
||||
var users = await _context.Users.AsNoTracking()
|
||||
.Where(u => userIds.Contains(u.Id))
|
||||
.Select(u => new { u.Id, u.FirstName, u.LastName, u.Mobile })
|
||||
.ToDictionaryAsync(u => u.Id, cancellationToken);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
if (!userByTx.TryGetValue(model.Id, out var userId))
|
||||
{
|
||||
model.UserFullName = "—";
|
||||
continue;
|
||||
}
|
||||
|
||||
model.UserId = userId;
|
||||
if (users.TryGetValue(userId, out var user))
|
||||
{
|
||||
model.Mobile = user.Mobile;
|
||||
var name = UserDisplayName.From(user.FirstName, user.LastName, user.Mobile, user.Id);
|
||||
model.UserFullName = string.IsNullOrWhiteSpace(name) ? "—" : name;
|
||||
}
|
||||
else
|
||||
{
|
||||
model.UserFullName = "—";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -25,4 +25,7 @@ public class GetAllTransactionsByFilterResponseDto
|
||||
//
|
||||
public TransactionType Type { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public long? UserId { get; set; }
|
||||
public string? UserFullName { get; set; }
|
||||
public string? Mobile { get; set; }
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.0.213</Version>
|
||||
<Version>0.0.214</Version>
|
||||
<DebugType>None</DebugType>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
|
||||
@@ -66,6 +66,7 @@ message PaymentTransactionModel
|
||||
google.protobuf.Int64Value transaction_id = 13;
|
||||
string description = 14;
|
||||
google.protobuf.Timestamp created_at = 15;
|
||||
string user_full_name = 16;
|
||||
}
|
||||
|
||||
message GetPaymentTransactionSummaryRequest
|
||||
|
||||
@@ -192,6 +192,9 @@ message GetAllTransactionsByFilterResponseModel
|
||||
messages.TransactionType type = 7;
|
||||
}
|
||||
google.protobuf.Timestamp created = 8;
|
||||
google.protobuf.Int64Value user_id = 9;
|
||||
string user_full_name = 10;
|
||||
google.protobuf.StringValue mobile = 11;
|
||||
}
|
||||
|
||||
message GetTransactionSummaryRequest
|
||||
|
||||
@@ -46,7 +46,8 @@ public class PaymentTransactionProfile : IRegister
|
||||
.Map(dest => dest.OrderId, src => src.OrderId ?? string.Empty)
|
||||
.Map(dest => dest.TransactionId, src => src.TransactionId)
|
||||
.Map(dest => dest.Description, src => src.Description ?? string.Empty)
|
||||
.Map(dest => dest.CreatedAt, src => Timestamp.FromDateTimeOffset(src.Created));
|
||||
.Map(dest => dest.CreatedAt, src => Timestamp.FromDateTimeOffset(src.Created))
|
||||
.Map(dest => dest.UserFullName, src => src.UserFullName ?? string.Empty);
|
||||
|
||||
config.NewConfig<ProtoPt.GetPaymentTransactionSummaryRequest, AppPtSummary.GetPaymentTransactionSummaryQuery>()
|
||||
.Map(dest => dest.Filter, src => src.Filter);
|
||||
|
||||
@@ -40,7 +40,10 @@ public class TransactionsProfile : IRegister
|
||||
.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)));
|
||||
.Map(dest => dest.Created, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.Created, DateTimeKind.Utc)))
|
||||
.Map(dest => dest.UserId, src => src.UserId)
|
||||
.Map(dest => dest.UserFullName, src => src.UserFullName ?? string.Empty)
|
||||
.Map(dest => dest.Mobile, src => src.Mobile);
|
||||
|
||||
config.NewConfig<ProtoTx.GetTransactionSummaryRequest, AppTxSummary.GetTransactionSummaryQuery>()
|
||||
.Map(dest => dest.Filter, src => src.Filter);
|
||||
|
||||
Reference in New Issue
Block a user