| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- using Domain.Entities.Store.ValueObject;
- using SharedKernel.Attributes;
- using SharedKernel.Extensions;
- using SharedKernel.Helpers;
- using Application.Abstractions.Messaging;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.AspNetCore.Mvc.RazorPages;
- using System.ComponentModel;
- using System.ComponentModel.DataAnnotations;
- namespace Admin.Pages.Store.Coupon;
- public class CodesModel(IMediator mediator) : PageModel
- {
- [BindProperty(SupportsGet = true)]
- public int ID { get; set; }
- [BindProperty(SupportsGet = true)]
- public QueryParams Query { get; set; } = new();
- public string CouponName { get; private set; } = "";
- public string GameName { get; private set; } = "";
- public string ProductName { get; private set; } = "";
- public int Total { get; set; }
- public int AvailableCount { get; set; }
- public int ReservedCount { get; set; }
- public int IssuedCount { get; set; }
- public int UsedCount { get; set; }
- public int ExpiredCount { get; set; }
- public sealed class QueryParams
- {
- [Range(1, int.MaxValue)]
- public int PageNum { get; set; } = 1;
- [Range(1, 200)]
- public ushort PerPage { get; set; } = 50;
- [DisplayName("코드 상태")]
- public CouponCodeStatus? Status { get; set; }
- }
- public List<(
- int Num,
- int ID,
- string Code,
- string StatusText,
- int? IssuedToMemberID,
- string? IssuedToMemberName,
- string? IssuedToMemberEmail,
- string? IssuedAt,
- string? UsedAt,
- string CreatedAt
- )> List { get; set; } = [];
- public Pagination? Pagination { get; set; }
- [BindProperty]
- public UploadInput Upload { get; set; } = new();
- public sealed class UploadInput
- {
- [DisplayName("CSV 파일")]
- [Required(ErrorMessage = "{0}을(를) 선택하세요.")]
- [AllowedExtensions("csv,txt", ErrorMessage = "CSV 또는 TXT 파일만 허용됩니다.")]
- public IFormFile? CsvFile { get; set; }
- }
- public async Task OnGetAsync(CancellationToken ct)
- {
- if (!ModelState.IsValid)
- {
- return;
- }
- await LoadHeaderAsync(ct);
- await LoadListAsync(ct);
- }
- public async Task<IActionResult> OnPostUploadAsync(CancellationToken ct)
- {
- if (!ModelState.IsValid)
- {
- TempData["ErrorMessages"] = ModelState.GetErrorMessages();
- return RedirectToPage("/Store/Coupon/Codes", new { id = ID });
- }
- var result = await mediator.Send(new ImportCouponCodes.Command(ID, Upload.CsvFile!), ct);
- if (result.IsFailure)
- {
- TempData["ErrorMessages"] = result.Error.Description;
- }
- else
- {
- var r = result.Value;
- TempData["SuccessMessage"] =
- $"CSV 업로드 완료. 총 {r.TotalLines}줄 / 추가 {r.Added}개 (미사용 {r.AddedAvailable} + 사용됨 {r.AddedUsed}) / 중복 skip {r.SkippedDuplicate}개 / 회원미존재 skip {r.SkippedMissingMember}개 / 무효 skip {r.SkippedInvalid}개.";
- }
- return RedirectToPage("/Store/Coupon/Codes", new { id = ID });
- }
- public async Task<IActionResult> OnGetExportAsync(int id, CancellationToken ct)
- {
- var result = await mediator.Send(new ExportCouponCodes.Query(id), ct);
- if (result.IsFailure)
- {
- TempData["ErrorMessages"] = result.Error.Description;
- return RedirectToPage("/Store/Coupon/Codes", new { id });
- }
- var data = result.Value;
- return File(data.CsvBytes, "text/csv; charset=utf-8", data.FileName);
- }
- public async Task<IActionResult> OnPostDeleteCodesAsync(int id, int[] codeIDs, CancellationToken ct)
- {
- if (codeIDs is null || codeIDs.Length == 0)
- {
- TempData["ErrorMessages"] = "삭제할 코드를 선택해 주세요.";
- return RedirectToPage("/Store/Coupon/Codes", new { id });
- }
- var result = await mediator.Send(new DeleteCouponCodes.Command(id, codeIDs), ct);
- if (result.IsFailure)
- {
- TempData["ErrorMessages"] = result.Error.Description;
- }
- else
- {
- TempData["SuccessMessage"] = $"{result.Value.Deleted}개 코드가 삭제되었습니다.";
- }
- return RedirectToPage("/Store/Coupon/Codes", new { id });
- }
- public async Task<IActionResult> OnPostExpireCodesAsync(int id, int[] codeIDs, CancellationToken ct)
- {
- if (codeIDs is null || codeIDs.Length == 0)
- {
- TempData["ErrorMessages"] = "만료 처리할 코드를 선택해 주세요.";
- return RedirectToPage("/Store/Coupon/Codes", new { id });
- }
- var result = await mediator.Send(new ExpireCouponCodes.Command(id, codeIDs), ct);
- if (result.IsFailure)
- {
- TempData["ErrorMessages"] = result.Error.Description;
- }
- else
- {
- TempData["SuccessMessage"] = $"{result.Value.Expired}개 코드가 만료 처리되었습니다.";
- }
- return RedirectToPage("/Store/Coupon/Codes", new { id });
- }
- public async Task<IActionResult> OnPostRestoreCodesAsync(int id, int[] codeIDs, CancellationToken ct)
- {
- if (codeIDs is null || codeIDs.Length == 0)
- {
- TempData["ErrorMessages"] = "복귀 처리할 코드를 선택해 주세요.";
- return RedirectToPage("/Store/Coupon/Codes", new { id });
- }
- var result = await mediator.Send(new RestoreCouponCodes.Command(id, codeIDs), ct);
- if (result.IsFailure)
- {
- TempData["ErrorMessages"] = result.Error.Description;
- }
- else
- {
- TempData["SuccessMessage"] = $"{result.Value.Restored}개 코드가 복귀 처리되었습니다.";
- }
- return RedirectToPage("/Store/Coupon/Codes", new { id });
- }
- public IActionResult OnGetTemplate()
- {
- // 헤더만 담긴 빈 양식
- var header = new[]
- {
- "ID",
- "Code",
- "Status",
- "IssuedToMemberID",
- "IssuedToMemberName",
- "IssuedToMemberEmail",
- "IssuedAt",
- "UsedAt",
- "CreatedAt"
- };
- var bytes = SharedKernel.Helpers.CsvHelper.WriteUtf8Bom([header]);
- return File(bytes, "text/csv; charset=utf-8", "coupon-codes-template.csv");
- }
- private async Task LoadHeaderAsync(CancellationToken ct)
- {
- var head = await mediator.Send(new GetCoupon.Query(ID), ct);
- if (head.IsFailure)
- {
- TempData["ErrorMessages"] = head.Error.Description;
- return;
- }
- CouponName = head.Value.Name;
- GameName = head.Value.GameName;
- ProductName = head.Value.ProductName;
- }
- private async Task LoadListAsync(CancellationToken ct)
- {
- var result = await mediator.Send(new ListCouponCodes.Query(ID, Query.Status, Query.PageNum, Query.PerPage), ct);
- Total = result.Total;
- AvailableCount = result.AvailableCount;
- ReservedCount = result.ReservedCount;
- IssuedCount = result.IssuedCount;
- UsedCount = result.UsedCount;
- ExpiredCount = result.ExpiredCount;
- List = [.. result.List.Select(c => (
- c.Num,
- c.ID,
- c.Code,
- StatusText: c.Status switch
- {
- CouponCodeStatus.Available => "사용 가능",
- CouponCodeStatus.Reserved => "결제 진행 중",
- CouponCodeStatus.Issued => "발급됨",
- CouponCodeStatus.Used => "사용됨",
- CouponCodeStatus.Expired => "만료",
- _ => "?"
- },
- c.IssuedToMemberID,
- c.IssuedToMemberName,
- c.IssuedToMemberEmail,
- c.IssuedAt?.ToString("yyyy-MM-dd HH:mm"),
- c.UsedAt?.ToString("yyyy-MM-dd HH:mm"),
- c.CreatedAt.GetDateAt()
- ))];
- Pagination = new Pagination(result.Total, Query.PageNum, Query.PerPage);
- }
- }
|