using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.Donations.ValueObject; using SharedKernel.Results; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.DonationGoal.GetProgress; internal sealed class Handler(IAppDbContext db) : IQueryHandler> { public async Task> Handle(Query request, CancellationToken ct) { var goal = await db.DonationGoalConfig.AsNoTracking().FirstOrDefaultAsync(g => g.ID == request.GoalConfigID && g.ChannelID == request.ChannelID && g.IsActive, ct); if (goal is null) { return Result.Failure(Error.NotFound("DonationGoal.NotFound", "목표 설정을 찾을 수 없습니다.")); } // Period 기반 시간 범위 계산 var now = DateTime.UtcNow; DateTime? rangeStart = goal.Period switch { RankPeriodType.Daily => now.Date, RankPeriodType.Weekly => now.Date.AddDays(-(int)now.DayOfWeek), RankPeriodType.Monthly => new DateTime(now.Year, now.Month, 1, 0, 0, 0, DateTimeKind.Utc), RankPeriodType.Yearly => new DateTime(now.Year, 1, 1, 0, 0, 0, DateTimeKind.Utc), RankPeriodType.Custom => goal.StartAt, _ => null // AllTime }; DateTime? rangeEnd = goal.Period == RankPeriodType.Custom ? goal.EndAt : null; var currentAmount = await db.Donation.AsNoTracking() .Where(d => d.ChannelID == request.ChannelID && !d.IsTest && (rangeStart == null || d.CreatedAt >= rangeStart) && (rangeEnd == null || d.CreatedAt <= rangeEnd)) .SumAsync(d => d.Amount, ct); var adjusted = currentAmount + goal.StartAmount; var percent = goal.TargetAmount > 0 ? Math.Min((decimal)adjusted / goal.TargetAmount * 100, 100) : 0; return Result.Success(new Response(goal.ID, goal.Title, goal.StartAmount, goal.TargetAmount, adjusted, Math.Round(percent, 1))); } }