Explorar o código

refactor(paper): Paper API enum 문자열 직렬화 (Stocks 컨벤션 통일)

Web.Api 에 전역 JsonStringEnumConverter 부재 → Paper Response DTO 의
byte enum 이 숫자로 직렬화되던 것을 Stocks 처럼 문자열(enum.ToString()
PascalCase)로 통일.

- GetOrders/GetLedger/PlaceOrder Response: Side/FillRule/Status/Type →
  string (핸들러 in-memory 매핑에서 .ToString(), EF 번역 회피).
- PlaceOrder 엔드포인트: request.Side string 수용 →
  Enum.TryParse(ignoreCase)+IsDefined, 부적합 시 400. Command 내부는 enum 유지.
- 값: Buy/Sell·Open/Close·Pending/Filled/Cancelled/Rejected·Deposit/Withdraw.

프론트 types/paper.ts 가 동일 문자열 리터럴 사용(별도 커밋) — 양쪽 정합.
빌드 0 에러, 테스트 101/101.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
KIM-JINO5 hai 2 semanas
pai
achega
bc72843f07

+ 13 - 3
Application/Features/Api/Paper/GetLedger/Handler.cs

@@ -19,20 +19,30 @@ internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
 
         var total = await query.CountAsync(ct);
 
-        var list = await query
+        var rows = await query
             .OrderByDescending(c => c.ID)
             .Skip((request.Page - 1) * request.PerPage)
             .Take(request.PerPage)
-            .Select(c => new Response.Row(
+            .Select(c => new
+            {
                 c.ID,
                 c.Type,
                 c.TokenAmount,
                 c.UnitsDelta,
                 c.BalanceAfter,
                 c.CreatedAt
-            ))
+            })
             .ToListAsync(ct);
 
+        var list = rows.Select(c => new Response.Row(
+            c.ID,
+            c.Type.ToString(),
+            c.TokenAmount,
+            c.UnitsDelta,
+            c.BalanceAfter,
+            c.CreatedAt
+        )).ToList();
+
         return new Response(total, list);
     }
 }

+ 1 - 3
Application/Features/Api/Paper/GetLedger/Response.cs

@@ -1,12 +1,10 @@
-using Domain.Entities.Paper.ValueObject;
-
 namespace Application.Features.Api.Paper.GetLedger;
 
 public sealed record Response(int Total, List<Response.Row> List)
 {
     public sealed record Row(
         int ID,
-        PaperLedgerType Type,
+        string Type,
         decimal TokenAmount,
         decimal UnitsDelta,
         decimal BalanceAfter,

+ 3 - 3
Application/Features/Api/Paper/GetOrders/Handler.cs

@@ -64,13 +64,13 @@ internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
             c.ID,
             c.StockCode,
             names.GetValueOrDefault(c.StockCode, c.StockCode),
-            c.Side,
-            c.FillRule,
+            c.Side.ToString(),
+            c.FillRule.ToString(),
             c.Quantity,
             c.ReservedAmount,
             c.TargetDate,
             c.CancelableUntil,
-            c.Status,
+            c.Status.ToString(),
             c.RejectReason,
             c.CreatedAt,
             c.Fill

+ 3 - 5
Application/Features/Api/Paper/GetOrders/Response.cs

@@ -1,5 +1,3 @@
-using Domain.Entities.Paper.ValueObject;
-
 namespace Application.Features.Api.Paper.GetOrders;
 
 public sealed record Response(int Total, List<Response.Row> List)
@@ -8,13 +6,13 @@ public sealed record Response(int Total, List<Response.Row> List)
         int ID,
         string StockCode,
         string StockName,
-        PaperOrderSide Side,
-        PaperFillRule FillRule,
+        string Side,
+        string FillRule,
         int Quantity,
         decimal ReservedAmount,
         DateOnly TargetDate,
         DateTime CancelableUntil,
-        PaperOrderStatus Status,
+        string Status,
         string? RejectReason,
         DateTime CreatedAt,
         FillInfo? Fill

+ 2 - 2
Application/Features/Api/Paper/PlaceOrder/Handler.cs

@@ -140,8 +140,8 @@ internal sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Resul
 
         return Result.Success(new Response(
             order.ID,
-            order.Side,
-            order.FillRule,
+            order.Side.ToString(),
+            order.FillRule.ToString(),
             order.TargetDate,
             order.CancelableUntil,
             order.Quantity,

+ 2 - 4
Application/Features/Api/Paper/PlaceOrder/Response.cs

@@ -1,11 +1,9 @@
-using Domain.Entities.Paper.ValueObject;
-
 namespace Application.Features.Api.Paper.PlaceOrder;
 
 public sealed record Response(
     int OrderID,
-    PaperOrderSide Side,
-    PaperFillRule FillRule,
+    string Side,
+    string FillRule,
     DateOnly TargetDate,
     DateTime CancelableUntil,
     int Quantity,

+ 9 - 2
Web.Api/Endpoints/Paper/PlaceOrder.cs

@@ -2,6 +2,7 @@ using System.Security.Claims;
 using System.Text.Json.Serialization;
 using Application.Abstractions.Messaging;
 using Domain.Entities.Paper.ValueObject;
+using SharedKernel.Results;
 using Web.Api.Common;
 using Web.Api.Extensions;
 
@@ -12,8 +13,9 @@ public sealed class PlaceOrderRequest
     [JsonPropertyName("stockCode")]
     public string StockCode { get; set; } = "";
 
+    /// <summary>주문 방향 — "Buy" | "Sell" (대소문자 무시).</summary>
     [JsonPropertyName("side")]
-    public PaperOrderSide Side { get; set; }
+    public string Side { get; set; } = "";
 
     [JsonPropertyName("quantity")]
     public int Quantity { get; set; }
@@ -30,8 +32,13 @@ internal sealed class PlaceOrder : IEndpoint
             ISender sender,
             CancellationToken ct
         ) => {
+            if (!Enum.TryParse<PaperOrderSide>(body.Side, ignoreCase: true, out var side) || !Enum.IsDefined(side))
+            {
+                return CustomResults.Problem(Result.Failure(Error.Problem("Paper.InvalidSide", "주문 방향(side)이 올바르지 않습니다. (Buy | Sell)")));
+            }
+
             var memberID = user.GetRequiredMemberID();
-            var result = await sender.Send(new Application.Features.Api.Paper.PlaceOrder.Command(memberID, body.StockCode, body.Side, body.Quantity), ct);
+            var result = await sender.Send(new Application.Features.Api.Paper.PlaceOrder.Command(memberID, body.StockCode, side, body.Quantity), ct);
 
             if (result.IsFailure)
             {