Handler.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using Application.Abstractions.Cache;
  2. using Application.Abstractions.Data;
  3. using Application.Abstractions.Messaging;
  4. using Application.Features.Config.Get;
  5. using Domain.Entities.Members.ValueObject;
  6. using Microsoft.EntityFrameworkCore;
  7. namespace Application.Features.Api.Rewards.GetToday;
  8. internal sealed class Handler(IAppDbContext db, ICacheService cache) : IQueryHandler<Query, Response>
  9. {
  10. private static readonly TimeZoneInfo KstZone = ResolveKst();
  11. public async Task<Response> Handle(Query request, CancellationToken ct)
  12. {
  13. var today = DateOnly.FromDateTime(TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, KstZone));
  14. var counters = await db.MemberDailyRewardCounter.AsNoTracking()
  15. .Where(c => c.MemberID == request.MemberID && c.RewardDate == today)
  16. .ToListAsync(ct);
  17. var config = await LoadConfigAsync(ct);
  18. var actions = new List<ActionSummary>
  19. {
  20. BuildSummary(counters, RewardActionType.Post, "게시글", config?.PostDailyCount ?? 0, config?.PostDailyExp ?? 0, config?.PostDailyPoint ?? 0),
  21. BuildSummary(counters, RewardActionType.Comment, "댓글", config?.CommentDailyCount ?? 0, config?.CommentDailyExp ?? 0, config?.CommentDailyPoint ?? 0),
  22. BuildSummary(counters, RewardActionType.LikeGiven, "추천(누름)", config?.LikeGivenDailyCount ?? 0, config?.LikeGivenDailyExp ?? 0, 0),
  23. BuildSummary(counters, RewardActionType.LikeReceived, "추천(받음)", config?.LikeReceivedDailyCount ?? 0, 0, config?.LikeReceivedDailyPoint ?? 0),
  24. BuildSummary(counters, RewardActionType.Chat, "채팅", config?.ChatDailyCount ?? 0, config?.ChatDailyExp ?? 0, 0)
  25. };
  26. var totalExp = counters.Sum(c => c.ExpTotal);
  27. var totalPoint = counters.Sum(c => c.PointTotal);
  28. return new Response(today.ToString("yyyy-MM-dd"), totalExp, totalPoint, actions);
  29. }
  30. private static ActionSummary BuildSummary(
  31. List<Domain.Entities.Members.MemberDailyRewardCounter> counters,
  32. RewardActionType action,
  33. string name,
  34. int countCap,
  35. int expCap,
  36. int pointCap)
  37. {
  38. var c = counters.FirstOrDefault(x => x.ActionType == action);
  39. var count = c?.Count ?? 0;
  40. var exp = c?.ExpTotal ?? 0;
  41. var point = c?.PointTotal ?? 0;
  42. return new ActionSummary(
  43. (byte)action,
  44. name,
  45. count,
  46. exp,
  47. point,
  48. countCap > 0 ? Math.Max(0, countCap - count) : null,
  49. expCap > 0 ? Math.Max(0, expCap - exp) : null,
  50. pointCap > 0 ? Math.Max(0, pointCap - point) : null
  51. );
  52. }
  53. private async Task<Response.RewardConfigView?> LoadConfigAsync(CancellationToken ct)
  54. {
  55. var cached = await cache.GetAsync<Features.Config.Get.Response>(CacheKeys.Config, ct);
  56. if (cached is not null)
  57. {
  58. var r = cached.Reward;
  59. return new Response.RewardConfigView(
  60. r.PostDailyCount, r.PostDailyExp, r.PostDailyPoint,
  61. r.CommentDailyCount, r.CommentDailyExp, r.CommentDailyPoint,
  62. r.LikeGivenDailyCount, r.LikeGivenDailyExp,
  63. r.LikeReceivedDailyCount, r.LikeReceivedDailyPoint,
  64. r.ChatDailyCount, r.ChatDailyExp);
  65. }
  66. var entity = await db.Config.AsNoTracking().OrderByDescending(c => c.ID).Select(c => c.Reward).FirstOrDefaultAsync(ct);
  67. if (entity is null)
  68. {
  69. return null;
  70. }
  71. return new Response.RewardConfigView(
  72. entity.PostDailyCount, entity.PostDailyExp, entity.PostDailyPoint,
  73. entity.CommentDailyCount, entity.CommentDailyExp, entity.CommentDailyPoint,
  74. entity.LikeGivenDailyCount, entity.LikeGivenDailyExp,
  75. entity.LikeReceivedDailyCount, entity.LikeReceivedDailyPoint,
  76. entity.ChatDailyCount, entity.ChatDailyExp);
  77. }
  78. private static TimeZoneInfo ResolveKst()
  79. {
  80. if (TimeZoneInfo.TryFindSystemTimeZoneById("Asia/Seoul", out var tz))
  81. {
  82. return tz;
  83. }
  84. if (TimeZoneInfo.TryFindSystemTimeZoneById("Korea Standard Time", out tz))
  85. {
  86. return tz;
  87. }
  88. return TimeZoneInfo.CreateCustomTimeZone("KST", TimeSpan.FromHours(9), "KST", "KST");
  89. }
  90. }