From 9dcdd56b2f9cd6460061fbe5d2606d977c9b2530 Mon Sep 17 00:00:00 2001 From: SEAN Date: Sat, 28 Feb 2026 22:50:31 +0900 Subject: [PATCH] =?UTF-8?q?improvement:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20KPI=20=EB=B3=80=ED=99=94=EB=9F=89/=EB=B3=80?= =?UTF-8?q?=ED=99=94=EC=9C=A8=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#262)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DashboardKpiDto에 success_rate_change, device_count_change, today_sent_change_rate 필드 추가 - StatsService.GetDashboardAsync에 직전 기간 성공률 변화, 오늘 신규 디바이스 수, 발송 변화율 계산 로직 구현 Closes #262 --- .../DTOs/Stats/DashboardResponseDto.cs | 9 ++++ SPMS.Application/Services/StatsService.cs | 41 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/SPMS.Application/DTOs/Stats/DashboardResponseDto.cs b/SPMS.Application/DTOs/Stats/DashboardResponseDto.cs index a3fdce9..53d3560 100644 --- a/SPMS.Application/DTOs/Stats/DashboardResponseDto.cs +++ b/SPMS.Application/DTOs/Stats/DashboardResponseDto.cs @@ -46,6 +46,15 @@ public class DashboardKpiDto [JsonPropertyName("active_service_count")] public int ActiveServiceCount { get; set; } + [JsonPropertyName("success_rate_change")] + public double SuccessRateChange { get; set; } + + [JsonPropertyName("device_count_change")] + public int DeviceCountChange { get; set; } + + [JsonPropertyName("today_sent_change_rate")] + public double TodaySentChangeRate { get; set; } + [JsonPropertyName("today")] public PeriodStatDto Today { get; set; } = new(); diff --git a/SPMS.Application/Services/StatsService.cs b/SPMS.Application/Services/StatsService.cs index 6a8776c..3d8dbc8 100644 --- a/SPMS.Application/Services/StatsService.cs +++ b/SPMS.Application/Services/StatsService.cs @@ -433,6 +433,44 @@ public class StatsService : IStatsService var activeServiceCount = await _serviceRepository.CountAsync(s => s.Status == ServiceStatus.Active && !s.IsDeleted); var topMessages = await _messageRepository.GetTopBySendCountAsync(serviceId, 5); + // KPI 변화량 계산 + var (startDate, endDate) = ParseDateRange(request.StartDate, request.EndDate); + + // 1) success_rate_change: 직전 동일 기간 대비 성공률 변화 (pp) + var duration = endDate.DayNumber - startDate.DayNumber + 1; + var prevEnd = startDate.AddDays(-1); + var prevStart = prevEnd.AddDays(-(duration - 1)); + var prevStats = await _dailyStatRepository.GetByDateRangeAsync(serviceId, prevStart, prevEnd); + + var currentSend = daily.Summary.TotalSend; + var currentSuccess = daily.Summary.TotalSuccess; + var prevSend = prevStats.Sum(s => (long)s.SentCnt); + var prevSuccess = prevStats.Sum(s => (long)s.SuccessCnt); + + var currentRate = currentSend > 0 ? (double)currentSuccess / currentSend * 100 : 0; + var prevRate = prevSend > 0 ? (double)prevSuccess / prevSend * 100 : 0; + var successRateChange = prevSend > 0 ? Math.Round(currentRate - prevRate, 1) : 0; + + // 2) device_count_change: 오늘 신규 등록 디바이스 수 + var todayDateTime = DateTime.UtcNow.Date; + var tomorrowDateTime = todayDateTime.AddDays(1); + var deviceCountChange = serviceId.HasValue + ? await _deviceRepository.CountAsync(d => d.ServiceId == serviceId.Value + && d.CreatedAt >= todayDateTime && d.CreatedAt < tomorrowDateTime) + : await _deviceRepository.CountAsync(d => + d.CreatedAt >= todayDateTime && d.CreatedAt < tomorrowDateTime); + + // 3) today_sent_change_rate: 오늘 vs 어제 발송 수 변화율 (%) + var today = DateOnly.FromDateTime(DateTime.UtcNow); + var yesterday = today.AddDays(-1); + var yesterdayStats = await _dailyStatRepository.GetByDateRangeAsync(serviceId, yesterday, yesterday); + var yesterdaySent = yesterdayStats.Sum(s => s.SentCnt); + var todaySent = summary.Today.SendCount; + + var todaySentChangeRate = yesterdaySent > 0 + ? Math.Round((double)(todaySent - yesterdaySent) / yesterdaySent * 100, 1) + : 0; + return new DashboardResponseDto { Kpi = new DashboardKpiDto @@ -445,6 +483,9 @@ public class StatsService : IStatsService TotalOpen = summary.TotalOpen, AvgCtr = summary.AvgCtr, ActiveServiceCount = activeServiceCount, + SuccessRateChange = successRateChange, + DeviceCountChange = deviceCountChange, + TodaySentChangeRate = todaySentChangeRate, Today = summary.Today, ThisMonth = summary.ThisMonth },