Edit.cshtml.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using SharedKernel.Extensions;
  2. using MediatR;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.AspNetCore.Mvc.RazorPages;
  5. using System.ComponentModel;
  6. using System.ComponentModel.DataAnnotations;
  7. namespace Admin.Pages.Channel.List;
  8. public class EditModel(IMediator mediator) : PageModel
  9. {
  10. [BindProperty]
  11. public string? QueryString { get; set; }
  12. public string? ReturnUrl { get; set; }
  13. [BindProperty]
  14. public InputModel Input { get; set; } = new();
  15. public string? MemberInfo { get; set; }
  16. public sealed class InputModel
  17. {
  18. [Required(ErrorMessage = "ID는 필수입니다.")]
  19. public int ID { get; set; }
  20. public int MemberID { get; set; }
  21. [DisplayName("이름")]
  22. [Required(ErrorMessage = "{0}은 필수입니다.")]
  23. [StringLength(200, ErrorMessage = "{0}은 {1}자 이내로 입력하세요.")]
  24. public string Name { get; set; } = default!;
  25. [DisplayName("핸들")]
  26. [StringLength(30, ErrorMessage = "{0}은 {1}자 이내로 입력하세요.")]
  27. public string? Handle { get; set; }
  28. [DisplayName("YouTube 주소")]
  29. [Required(ErrorMessage = "{0}은 필수입니다.")]
  30. [StringLength(255, ErrorMessage = "{0}은 {1}자 이내로 입력하세요.")]
  31. public string YouTubeUrl { get; set; } = default!;
  32. [DisplayName("후원 수수료(%)")]
  33. [Range(0, 100, ErrorMessage = "{0}은 {1}~{2} 사이여야 합니다.")]
  34. public decimal PlatformFeeRate { get; set; }
  35. [DisplayName("상점 보상률(%)")]
  36. [Range(0, 100, ErrorMessage = "{0}은 {1}~{2} 사이여야 합니다.")]
  37. public decimal StoreCommissionRate { get; set; }
  38. [DisplayName("인증 여부")]
  39. public bool IsVerified { get; set; }
  40. [DisplayName("사용 여부")]
  41. public bool IsActive { get; set; }
  42. [DisplayName("후원 코드")]
  43. [StringLength(7, MinimumLength = 4, ErrorMessage = "{0}은 {2}~{1}자여야 합니다.")]
  44. [RegularExpression("^[A-Za-z0-9]{4,7}$", ErrorMessage = "{0}은 영문/숫자만 입력 가능합니다.")]
  45. public string? DonationCode { get; set; }
  46. public string? UpdatedAt { get; set; }
  47. public string CreatedAt { get; set; } = default!;
  48. }
  49. public async Task OnGetAsync(int id, CancellationToken ct)
  50. {
  51. var referer = Request.Headers.Referer.ToString();
  52. ReturnUrl = string.IsNullOrEmpty(referer) ? null : referer;
  53. var result = await mediator.Send(new GetChannel.Query(id), ct);
  54. if (result is null)
  55. {
  56. return;
  57. }
  58. MemberInfo = $"{result.MemberEmail}, {result.MemberName ?? result.MemberSID ?? "-"}";
  59. // 기존 코드가 없으면 자동 추천값 생성 (저장 안 하면 NULL 유지)
  60. var donationCode = result.DonationCode
  61. ?? await mediator.Send(new GenerateChannelDonationCode.Query(), ct);
  62. Input = new InputModel
  63. {
  64. ID = result.ID,
  65. MemberID = result.MemberID,
  66. Name = result.Name,
  67. Handle = result.Handle ?? "-",
  68. YouTubeUrl = result.YouTubeUrl,
  69. PlatformFeeRate = result.PlatformFeeRate,
  70. StoreCommissionRate = result.StoreCommissionRate,
  71. IsVerified = result.IsVerified,
  72. IsActive = result.IsActive,
  73. DonationCode = donationCode,
  74. UpdatedAt = result.UpdatedAt.GetDateAt() ?? "-",
  75. CreatedAt = result.CreatedAt.GetDateAt()
  76. };
  77. QueryString = Request.QueryString.ToString();
  78. }
  79. public async Task<IActionResult> OnGetGenerateDonationCodeAsync(CancellationToken ct)
  80. {
  81. var code = await mediator.Send(new GenerateChannelDonationCode.Query(), ct);
  82. return new JsonResult(new { code });
  83. }
  84. public async Task<IActionResult> OnPostAsync(CancellationToken ct)
  85. {
  86. try
  87. {
  88. if (!ModelState.IsValid)
  89. {
  90. return Page();
  91. }
  92. await mediator.Send(new UpdateChannel.Command(
  93. Input.ID,
  94. Input.Name,
  95. Input.Handle,
  96. Input.YouTubeUrl,
  97. Input.PlatformFeeRate,
  98. Input.StoreCommissionRate,
  99. Input.IsVerified,
  100. Input.IsActive,
  101. Input.DonationCode
  102. ), ct);
  103. TempData["SuccessMessage"] = "채널이 수정되었습니다.";
  104. }
  105. catch (Exception e)
  106. {
  107. TempData["ErrorMessages"] = e.Message;
  108. }
  109. return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}");
  110. }
  111. public async Task<IActionResult> OnPostDeleteAsync(CancellationToken ct)
  112. {
  113. try
  114. {
  115. await mediator.Send(new DeleteChannel.Command([Input.ID]), ct);
  116. TempData["SuccessMessage"] = "채널이 삭제되었습니다.";
  117. return RedirectToPage("/Channel/List/Index");
  118. }
  119. catch (InvalidOperationException e)
  120. {
  121. TempData["ErrorMessages"] = e.Message;
  122. return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}");
  123. }
  124. catch (Microsoft.EntityFrameworkCore.DbUpdateException e)
  125. {
  126. var detail = e.InnerException?.Message ?? e.Message;
  127. TempData["ErrorMessages"] = $"데이터베이스 제약 조건 위반으로 삭제할 수 없습니다. 연관된 데이터를 먼저 정리해 주세요.\n상세: {detail}";
  128. return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}");
  129. }
  130. catch (Exception e)
  131. {
  132. TempData["ErrorMessages"] = e.Message;
  133. return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}");
  134. }
  135. }
  136. }