Handler.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using SharedKernel.Results;
  4. using Domain.Entities.Donations;
  5. using Domain.Entities.Donations.ValueObject;
  6. using Microsoft.EntityFrameworkCore;
  7. namespace Application.Features.Api.Crew.SaveWidgetConfig;
  8. internal sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Result>
  9. {
  10. public async Task<Result> Handle(Command r, CancellationToken ct)
  11. {
  12. var theme = (CrewWidgetTheme)r.Theme;
  13. var period = (RankPeriodType)r.Period;
  14. // CrewID 검증 — 채널 소유 Crew인지
  15. var crewExists = await db.Crew.AnyAsync(c => c.ID == r.CrewID && c.ChannelID == r.ChannelID, ct);
  16. if (!crewExists)
  17. {
  18. return Result.Failure(Error.NotFound("Crew.NotFound", "선택한 크루를 찾을 수 없습니다."));
  19. }
  20. // 기간 중복 검증 — 같은 Crew + 같은 Period 활성 설정 충돌 방지
  21. if (r.IsActive)
  22. {
  23. if (period == RankPeriodType.Custom && r.StartAt.HasValue && r.EndAt.HasValue)
  24. {
  25. var overlapping = await db.CrewWidgetConfig
  26. .AnyAsync(c => c.ChannelID == r.ChannelID
  27. && c.CrewID == r.CrewID
  28. && (!r.ID.HasValue || c.ID != r.ID.Value)
  29. && c.IsActive
  30. && c.Period == RankPeriodType.Custom
  31. && c.StartAt < r.EndAt && c.EndAt > r.StartAt, ct);
  32. if (overlapping)
  33. {
  34. return Result.Failure(Error.Conflict("CrewWidget.PeriodOverlap", "기간이 겹치는 활성 설정이 있습니다."));
  35. }
  36. }
  37. else
  38. {
  39. var existing = await db.CrewWidgetConfig
  40. .AnyAsync(c => c.ChannelID == r.ChannelID
  41. && c.CrewID == r.CrewID
  42. && (!r.ID.HasValue || c.ID != r.ID.Value)
  43. && c.IsActive
  44. && c.Period == period, ct);
  45. if (existing)
  46. {
  47. return Result.Failure(Error.Conflict("CrewWidget.SamePeriodExists", "같은 크루에 같은 기간의 활성 설정이 이미 있습니다."));
  48. }
  49. }
  50. }
  51. if (r.ID.HasValue)
  52. {
  53. var config = await db.CrewWidgetConfig
  54. .FirstOrDefaultAsync(c => c.ID == r.ID.Value && c.ChannelID == r.ChannelID, ct);
  55. if (config is null)
  56. {
  57. return Result.Failure(Error.NotFound("CrewWidget.NotFound", "설정을 찾을 수 없습니다."));
  58. }
  59. config.Update(
  60. r.CrewID, r.Title, theme, period, r.StartAt, r.EndAt,
  61. r.MaxDisplayCount, r.IsShowAmount, r.IsShowDonationCount,
  62. r.IsShowContributionRate, r.IsShowMemberIcon, r.IsActive, r.BgColor,
  63. r.TitleFontFamily, r.TitleFontSizePx, r.TitleFontColor,
  64. r.Rank1FontFamily, r.Rank1FontSizePx, r.Rank1FontColor,
  65. r.Rank2FontFamily, r.Rank2FontSizePx, r.Rank2FontColor,
  66. r.Rank3FontFamily, r.Rank3FontSizePx, r.Rank3FontColor,
  67. r.RowFontFamily, r.RowFontSizePx, r.RowFontColor
  68. );
  69. }
  70. else
  71. {
  72. var config = CrewWidgetConfig.Create(
  73. r.ChannelID, r.MemberID, r.CrewID, r.Title,
  74. theme, period, r.StartAt, r.EndAt,
  75. r.MaxDisplayCount, r.IsShowAmount, r.IsShowDonationCount,
  76. r.IsShowContributionRate, r.IsShowMemberIcon, r.IsActive, r.BgColor,
  77. r.TitleFontFamily, r.TitleFontSizePx, r.TitleFontColor,
  78. r.Rank1FontFamily, r.Rank1FontSizePx, r.Rank1FontColor,
  79. r.Rank2FontFamily, r.Rank2FontSizePx, r.Rank2FontColor,
  80. r.Rank3FontFamily, r.Rank3FontSizePx, r.Rank3FontColor,
  81. r.RowFontFamily, r.RowFontSizePx, r.RowFontColor
  82. );
  83. db.CrewWidgetConfig.Add(config);
  84. }
  85. await db.SaveChangesAsync(ct);
  86. return Result.Success();
  87. }
  88. }