using MediatR; using MiningService.Domain.AggregatesModel.MinerAggregate; namespace MiningService.API.Application.Queries; /// /// EN: Query to get mining history for a user. /// VI: Query để lấy lịch sử đào của người dùng. /// public record GetMiningHistoryQuery(Guid UserId, int Page = 1, int PageSize = 20) : IRequest; public record MiningHistoryDto( List Items, int TotalCount, int Page, int PageSize); public record MiningHistoryItemDto( Guid Id, decimal Amount, string Source, decimal RateSnapshot, DateTime CreatedAt); public class GetMiningHistoryQueryHandler : IRequestHandler { private readonly IMinerRepository _minerRepository; public GetMiningHistoryQueryHandler(IMinerRepository minerRepository) { _minerRepository = minerRepository; } public async Task Handle(GetMiningHistoryQuery request, CancellationToken cancellationToken) { var miner = await _minerRepository.GetByUserIdAsync(request.UserId, cancellationToken); if (miner == null) return new MiningHistoryDto(new List(), 0, request.Page, request.PageSize); var history = miner.MiningHistories .OrderByDescending(h => h.EarnedAt) .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .Select(h => new MiningHistoryItemDto(h.Id, h.PointsEarned, h.Source, h.HourlyRateSnapshot, h.EarnedAt)) .ToList(); return new MiningHistoryDto(history, miner.MiningHistories.Count, request.Page, request.PageSize); } } /// /// EN: Query to get current mining rate breakdown. /// VI: Query để lấy chi tiết tỷ lệ đào hiện tại. /// public record GetMiningRateQuery(Guid UserId) : IRequest; public record MiningRateDto( decimal BaseRate, decimal RoleMultiplier, decimal CircleBonus, decimal ReferralBonus, decimal StreakBonus, decimal TotalRate, decimal HourlyPoints, decimal DailyPoints); public class GetMiningRateQueryHandler : IRequestHandler { private readonly IMinerRepository _minerRepository; public GetMiningRateQueryHandler(IMinerRepository minerRepository) { _minerRepository = minerRepository; } public async Task Handle(GetMiningRateQuery request, CancellationToken cancellationToken) { var miner = await _minerRepository.GetByUserIdAsync(request.UserId, cancellationToken); if (miner == null) return null; var rate = miner.CurrentRate; var baseRate = 0.25m; var roleMultiplier = GetRoleMultiplier(miner.Role); var circleBonus = miner.CircleId.HasValue ? 0.25m : 0m; var referralBonus = 0m; // Would need to query repository for actual count var streakBonus = miner.Streak.BonusMultiplier; var totalRate = baseRate * (1 + roleMultiplier) * (1 + circleBonus) * (1 + referralBonus) * (1 + streakBonus); return new MiningRateDto( baseRate, roleMultiplier, circleBonus, referralBonus, streakBonus, totalRate, totalRate, totalRate * 24); } private static decimal GetRoleMultiplier(MinerRole role) => role switch { MinerRole.Pioneer => 0m, MinerRole.Contributor => 0.10m, MinerRole.Ambassador => 0.25m, MinerRole.NodeOperator => 0.50m, _ => 0m }; } /// /// EN: Query to get top miners leaderboard. /// VI: Query để lấy bảng xếp hạng thợ đào. /// public record GetLeaderboardQuery(int Limit = 100) : IRequest; public record LeaderboardDto(List Entries); public record LeaderboardEntryDto( int Rank, Guid MinerId, Guid UserId, decimal TotalPoints, int StreakDays, MinerRole Role); public class GetLeaderboardQueryHandler : IRequestHandler { private readonly IMinerRepository _minerRepository; public GetLeaderboardQueryHandler(IMinerRepository minerRepository) { _minerRepository = minerRepository; } public async Task Handle(GetLeaderboardQuery request, CancellationToken cancellationToken) { var topMiners = await _minerRepository.GetTopMinersAsync(request.Limit, cancellationToken); var entries = topMiners.Select((m, i) => new LeaderboardEntryDto( i + 1, m.Id, m.UserId, m.TotalMinedPoints, m.Streak.CurrentStreak, m.Role)).ToList(); return new LeaderboardDto(entries); } }