namespace TaxBaik.Application.Services; using Microsoft.Extensions.Caching.Memory; using TaxBaik.Domain.Entities; public record AdminDashboardSummary( int ThisMonthInquiries, int NewInquiries, int TotalPosts, int PublishedPosts, IReadOnlyList RecentInquiries); public class AdminDashboardService( InquiryService inquiryService, BlogService blogService, IMemoryCache memoryCache) { private static readonly TimeSpan CacheDuration = TimeSpan.FromSeconds(30); public const string CacheKey = "admin-dashboard-summary"; public async Task GetSummaryAsync(CancellationToken ct = default) { if (memoryCache.TryGetValue(CacheKey, out AdminDashboardSummary? cached) && cached != null) return cached; var recentTask = inquiryService.GetPagedAsync(1, 5, ct: ct); var thisMonthTask = inquiryService.CountThisMonthAsync(ct); var newTask = inquiryService.CountByStatusAsync("new", ct); var statsTask = blogService.GetStatsAsync(ct); var (recentInquiries, _) = await recentTask; var stats = await statsTask; var summary = new AdminDashboardSummary( ThisMonthInquiries: await thisMonthTask, NewInquiries: await newTask, TotalPosts: stats.TotalPosts, PublishedPosts: stats.PublishedPosts, RecentInquiries: recentInquiries.OrderByDescending(x => x.CreatedAt).Take(5).ToList()); memoryCache.Set(CacheKey, summary, CacheDuration); return summary; } /// /// 최근 문의 조회 /// public async Task> GetRecentInquiriesAsync(int limit, CancellationToken ct = default) { var (inquiries, _) = await inquiryService.GetPagedAsync(1, limit, ct: ct); return inquiries.OrderByDescending(x => x.CreatedAt).ToList(); } /// /// 월별 통계 (접수 건수, 진행 중, 완료) /// public async Task GetMonthlyStatsAsync(string? month, CancellationToken ct = default) { var targetMonth = month != null && DateTime.TryParse($"{month}-01", out var dt) ? dt : DateTime.Today; var startDate = new DateTime(targetMonth.Year, targetMonth.Month, 1); var endDate = startDate.AddMonths(1).AddDays(-1); // 캐시 시도 (일 단위) var cacheKey = $"admin-stats-{startDate:yyyy-MM}"; if (memoryCache.TryGetValue(cacheKey, out object? cachedStats) && cachedStats != null) return cachedStats; var total = await inquiryService.CountByDateRangeAsync(startDate, endDate, ct); var consulting = await inquiryService.CountByStatusAndDateAsync("consulting", startDate, endDate, ct); var completed = await inquiryService.CountByStatusAndDateAsync("contracted", startDate, endDate, ct); var result = new { month = startDate.ToString("yyyy-MM"), totalInquiries = total, consultingCount = consulting, completedCount = completed, newCount = total - consulting - completed, completionRate = total > 0 ? (completed * 100.0 / total) : 0.0 }; memoryCache.Set(cacheKey, result, TimeSpan.FromHours(1)); return result; } }