SPMS_API/SPMS.Infrastructure/Persistence/Repositories/PushSendLogRepository.cs
SEAN bcc40b4c01 feat: 발송 상세 로그 조회 API 구현 (#136)
- POST /v1/in/stats/send-log (DDL-02)
- 특정 메시지의 개별 디바이스별 발송 로그 조회
- 플랫폼(iOS/Android/Web), 성공/실패 필터 지원
- Device Include로 디바이스 토큰, 플랫폼 정보 포함

Closes #136
2026-02-11 09:29:03 +09:00

135 lines
4.7 KiB
C#

using Microsoft.EntityFrameworkCore;
using SPMS.Domain.Entities;
using SPMS.Domain.Enums;
using SPMS.Domain.Interfaces;
namespace SPMS.Infrastructure.Persistence.Repositories;
public class PushSendLogRepository : Repository<PushSendLog>, IPushSendLogRepository
{
public PushSendLogRepository(AppDbContext context) : base(context) { }
public async Task<(IReadOnlyList<PushSendLog> Items, int TotalCount)> GetPagedWithMessageAsync(
long serviceId, int page, int size,
long? messageId = null, long? deviceId = null,
PushResult? status = null,
DateTime? startDate = null, DateTime? endDate = null)
{
var query = _dbSet
.Include(l => l.Message)
.Where(l => l.ServiceId == serviceId);
if (messageId.HasValue)
query = query.Where(l => l.MessageId == messageId.Value);
if (deviceId.HasValue)
query = query.Where(l => l.DeviceId == deviceId.Value);
if (status.HasValue)
query = query.Where(l => l.Status == status.Value);
if (startDate.HasValue)
query = query.Where(l => l.SentAt >= startDate.Value);
if (endDate.HasValue)
query = query.Where(l => l.SentAt < endDate.Value.AddDays(1));
var totalCount = await query.CountAsync();
var items = await query
.OrderByDescending(l => l.SentAt)
.Skip((page - 1) * size)
.Take(size)
.ToListAsync();
return (items, totalCount);
}
public async Task<List<HourlyStatRaw>> GetHourlyStatsAsync(long serviceId, DateTime startDate, DateTime endDate)
{
return await _dbSet
.Where(l => l.ServiceId == serviceId && l.SentAt >= startDate && l.SentAt < endDate)
.GroupBy(l => l.SentAt.Hour)
.Select(g => new HourlyStatRaw
{
Hour = g.Key,
SendCount = g.Count(),
SuccessCount = g.Count(l => l.Status == PushResult.Success),
FailCount = g.Count(l => l.Status == PushResult.Failed)
})
.OrderBy(h => h.Hour)
.ToListAsync();
}
public async Task<MessageStatRaw?> GetMessageStatsAsync(long serviceId, long messageId, DateTime? startDate, DateTime? endDate)
{
var query = _dbSet.Where(l => l.ServiceId == serviceId && l.MessageId == messageId);
if (startDate.HasValue)
query = query.Where(l => l.SentAt >= startDate.Value);
if (endDate.HasValue)
query = query.Where(l => l.SentAt < endDate.Value.AddDays(1));
var hasData = await query.AnyAsync();
if (!hasData) return null;
return await query
.GroupBy(_ => 1)
.Select(g => new MessageStatRaw
{
TotalSend = g.Count(),
TotalSuccess = g.Count(l => l.Status == PushResult.Success),
TotalFail = g.Count(l => l.Status == PushResult.Failed),
FirstSentAt = g.Min(l => l.SentAt),
LastSentAt = g.Max(l => l.SentAt)
})
.FirstOrDefaultAsync();
}
public async Task<(IReadOnlyList<PushSendLog> Items, int TotalCount)> GetDetailLogPagedAsync(
long serviceId, long messageId, int page, int size,
PushResult? status = null, Platform? platform = null)
{
var query = _dbSet
.Include(l => l.Device)
.Where(l => l.ServiceId == serviceId && l.MessageId == messageId);
if (status.HasValue)
query = query.Where(l => l.Status == status.Value);
if (platform.HasValue)
query = query.Where(l => l.Device.Platform == platform.Value);
var totalCount = await query.CountAsync();
var items = await query
.OrderByDescending(l => l.SentAt)
.Skip((page - 1) * size)
.Take(size)
.ToListAsync();
return (items, totalCount);
}
public async Task<List<MessageDailyStatRaw>> GetMessageDailyStatsAsync(long serviceId, long messageId, DateTime? startDate, DateTime? endDate)
{
var query = _dbSet.Where(l => l.ServiceId == serviceId && l.MessageId == messageId);
if (startDate.HasValue)
query = query.Where(l => l.SentAt >= startDate.Value);
if (endDate.HasValue)
query = query.Where(l => l.SentAt < endDate.Value.AddDays(1));
return await query
.GroupBy(l => DateOnly.FromDateTime(l.SentAt))
.Select(g => new MessageDailyStatRaw
{
StatDate = g.Key,
SendCount = g.Count(),
SuccessCount = g.Count(l => l.Status == PushResult.Success)
})
.OrderByDescending(d => d.StatDate)
.ToListAsync();
}
}