using SharedKernel.Extensions; using Application.Abstractions.Messaging; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Admin.Pages.Channel.List; public class EditModel(IMediator mediator) : PageModel { [BindProperty] public string? QueryString { get; set; } public string? ReturnUrl { get; set; } [BindProperty] public InputModel Input { get; set; } = new(); public string? MemberInfo { get; set; } public sealed class InputModel { [Required(ErrorMessage = "ID는 필수입니다.")] public int ID { get; set; } public int MemberID { get; set; } [DisplayName("이름")] [Required(ErrorMessage = "{0}은 필수입니다.")] [StringLength(200, ErrorMessage = "{0}은 {1}자 이내로 입력하세요.")] public string Name { get; set; } = default!; [DisplayName("핸들")] [StringLength(30, ErrorMessage = "{0}은 {1}자 이내로 입력하세요.")] public string? Handle { get; set; } [DisplayName("YouTube 주소")] [Required(ErrorMessage = "{0}은 필수입니다.")] [StringLength(255, ErrorMessage = "{0}은 {1}자 이내로 입력하세요.")] public string YouTubeUrl { get; set; } = default!; [DisplayName("후원 수수료(%)")] [Range(0, 100, ErrorMessage = "{0}은 {1}~{2} 사이여야 합니다.")] public decimal PlatformFeeRate { get; set; } [DisplayName("상점 보상률(%)")] [Range(0, 100, ErrorMessage = "{0}은 {1}~{2} 사이여야 합니다.")] public decimal StoreCommissionRate { get; set; } [DisplayName("인증 여부")] public bool IsVerified { get; set; } [DisplayName("사용 여부")] public bool IsActive { get; set; } [DisplayName("후원 코드")] [StringLength(7, MinimumLength = 4, ErrorMessage = "{0}은 {2}~{1}자여야 합니다.")] [RegularExpression("^[A-Za-z0-9]{4,7}$", ErrorMessage = "{0}은 영문/숫자만 입력 가능합니다.")] public string? DonationCode { get; set; } public string? UpdatedAt { get; set; } public string CreatedAt { get; set; } = default!; } public async Task OnGetAsync(int id, CancellationToken ct) { var referer = Request.Headers.Referer.ToString(); ReturnUrl = string.IsNullOrEmpty(referer) ? null : referer; var result = await mediator.Send(new GetChannel.Query(id), ct); if (result is null) { return; } MemberInfo = $"{result.MemberEmail}, {result.MemberName ?? result.MemberSID ?? "-"}"; // 후원 코드 자동 생성 기능 제거됨(2026-07-03) — 기존 코드 표시/수동 입력만 지원 var donationCode = result.DonationCode; Input = new InputModel { ID = result.ID, MemberID = result.MemberID, Name = result.Name, Handle = result.Handle ?? "-", YouTubeUrl = result.YouTubeUrl, PlatformFeeRate = result.PlatformFeeRate, StoreCommissionRate = result.StoreCommissionRate, IsVerified = result.IsVerified, IsActive = result.IsActive, DonationCode = donationCode, UpdatedAt = result.UpdatedAt.GetDateAt() ?? "-", CreatedAt = result.CreatedAt.GetDateAt() }; QueryString = Request.QueryString.ToString(); } public async Task OnPostAsync(CancellationToken ct) { try { if (!ModelState.IsValid) { return Page(); } await mediator.Send(new UpdateChannel.Command( Input.ID, Input.Name, Input.Handle, Input.YouTubeUrl, Input.PlatformFeeRate, Input.StoreCommissionRate, Input.IsVerified, Input.IsActive, Input.DonationCode ), ct); TempData["SuccessMessage"] = "채널이 수정되었습니다."; } catch (Exception e) { TempData["ErrorMessages"] = e.Message; } return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}"); } public async Task OnPostDeleteAsync(CancellationToken ct) { try { await mediator.Send(new DeleteChannel.Command([Input.ID]), ct); TempData["SuccessMessage"] = "채널이 삭제되었습니다."; return RedirectToPage("/Channel/List/Index"); } catch (InvalidOperationException e) { TempData["ErrorMessages"] = e.Message; return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}"); } catch (Microsoft.EntityFrameworkCore.DbUpdateException e) { var detail = e.InnerException?.Message ?? e.Message; TempData["ErrorMessages"] = $"데이터베이스 제약 조건 위반으로 삭제할 수 없습니다. 연관된 데이터를 먼저 정리해 주세요.\n상세: {detail}"; return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}"); } catch (Exception e) { TempData["ErrorMessages"] = e.Message; return Redirect($"/Channel/List/Edit/{Input.ID}{Request.QueryString}"); } } }