Order Admin Revamp — Handoff Board
Order Admin Revamp — Handoff Board
Status: Closed 2026-05-11 — backbone + UI + E2E (자동 walker 10/10 + click-through 10/10) 통과. 잔여 4건은 README "Known Residuals" 참조.
본 spec 의 AI ↔ Human 핸드오프 인박스. 작업자가 진척 / 차단 / 검증 요청을 여기에 적는다. Step 1 ~ 시나리오 합의 ~ 서버/클라 구현 완료 + 자동 UI 풀루프 (10/10) 통과 (2026-05-09).
스크립트:
scripts/e2e/order-admin-revamp-seed.mjs— customer 1명 + 10 주문 슬롯 시드 (manual login 0)scripts/e2e/admin-web-walk.mjs— admin token cookie inject + henriette shop autoselect + /merchant/orders 풀루프
| Slot | Server status | UI 카드 | UI 셀러 액션 (count) | Server 매칭 |
|---|---|---|---|---|
| 1 PAYMENT_PENDING | PAYMENT_PENDING | 7/10 | 3 ✅ | MANUAL_COMPLETE_PAYMENT / MARK_PAYMENT_FAILED / ADD_NOTE |
| 2 PAID | PAID | 7/10 | 5 ✅ | START_PROCESSING / MARK_SHIPPED / CANCEL_BY_SELLER / REFUND_FULL / ADD_NOTE |
| 3 PROCESSING | PROCESSING | 7/10 | 4 ✅ | MARK_SHIPPED / CANCEL_BY_SELLER / REFUND_FULL / ADD_NOTE |
| 4 SHIPPED | SHIPPED | 7/10 | 3 ✅ | UPDATE_TRACKING / MARK_DELIVERED / ADD_NOTE |
| 5 DELIVERED | DELIVERED | 7/10 | 3 ✅ | REFUND_FULL / REFUND_PARTIAL / ADD_NOTE |
| 6 CANCEL_REQUESTED | CANCEL_REQUESTED | 8/10 | 3 ✅ | APPROVE_CANCEL / REJECT_CANCEL / ADD_NOTE |
| 7 RETURN_REQUESTED | RETURN_REQUESTED | 8/10 | 3 ✅ | APPROVE_RETURN / REJECT_RETURN / ADD_NOTE |
| 8 EXCHANGE_REQUESTED | EXCHANGE_REQUESTED | 8/10 | 3 ✅ | APPROVE_EXCHANGE / REJECT_EXCHANGE / ADD_NOTE |
| 9 CANCELED | CANCELED | 8/10 | 1 ✅ | ADD_NOTE (only) |
| 10 DELIVERED + partial refund 시도 | DELIVERED | 8/10 | 3 ✅ | REFUND_FULL / REFUND_PARTIAL / ADD_NOTE |
UI ↔ server 매칭 100% (10/10).
cards 7~8/10 의 의미: claim/refundRecords/discount/fulfillment 카드는 데이터 없을 때 conditional 미렌더 (정상). meta/customer/shipping/lineItems/pricing/payment/sellerNotes 7개는 항상.
스크린샷: /tmp/admin-revamp-shots/{before,detail}__slot*__order_*.png (20장)
스크립트: scripts/e2e/admin-web-click-through.mjs — 각 슬롯별 1개 액션 실제 UI click + 다이얼로그 입력 + 저장 + server diff 검증.
| Slot | Action | Start → End | Verdict |
|---|---|---|---|
| 1 PAYMENT_PENDING | MANUAL_COMPLETE_PAYMENT (confirm only) | PAYMENT_PENDING → PAID | ✅ status 전이 |
| 2 PAID | ADD_NOTE | notes 0→1 | ✅ |
| 3 PROCESSING | ADD_NOTE | notes 1→2 | ✅ |
| 4 SHIPPED | UPDATE_TRACKING (carrier=한진택배 + number) | notes 2→3 (SYSTEM audit 자동 기록) | ✅ |
| 5 DELIVERED | ADD_NOTE | notes 3→4 | ✅ |
| 6 CANCEL_REQUESTED | REJECT_CANCEL (reason 필드) | CANCEL_REQUESTED → PAID (이전 상태 복귀) | ✅ |
| 7 RETURN_REQUESTED | APPROVE_RETURN (confirm only) | RETURN_REQUESTED → RETURN_APPROVED | ✅ |
| 8 EXCHANGE_REQUESTED | APPROVE_EXCHANGE (confirm only) | EXCHANGE_REQUESTED → EXCHANGE_APPROVED | ✅ |
| 9 CANCELED | ADD_NOTE | notes 1→2 (terminal 메모) | ✅ |
| 10 DELIVERED + 부분환불 | REFUND_PARTIAL (라인 1개 qty=1, amount=1000) | refundRecords 1→2, latest=FAILED/PARTIAL/1000 | ✅ ledger 적재 |
slot10 의 latest refund=FAILED 는 의도된 동작: RefundDispatcher 가 MANUAL processor 를 default → UNSUPPORTED_PROCESSOR_MANUAL 로 분기 → 실 PG 안 호출, RefundRecord FAILED 로 ledger 정상 기록. PG 트랙 합류 시 MANUAL 라우팅 추가하거나 stub adapter binding 하면 SUCCESS 됨.
스크린샷: /tmp/admin-revamp-click/{slot}__{01_detail,02_dialog,03_filled,04_after}.png (40장)
- 고객 mypage 일관성 — SHIPPED 처리 후 고객 mypage 에서 송장 조회 (shop-api 응답 일관성)
- consumer 신규 event 처리 — OrderShippedBySeller / OrderRefunded / OrderTrackingUpdatedBySeller 등이 messaging rule + 적립금 회수 후속 흐름까지 영향
- REFUND_PARTIAL 누적 정합 — 두 번째 부분환불 + 라인 refundedQuantity 누적. 현재는 RefundDispatcher 가 MANUAL 라우팅 미지원이라 실제 누적 검증 미완 (PG 트랙 합류 후 또는 stub fallback 추가 후)
- list 정렬/기간 필터 server-side — controller 가 받지만 OrderSearchService 미활용 (open item)
- operator 식별 자동 주입 — admin 인증 필터 attribute 로 user/email 채워지면 audit 의 actor 자동 기록
- S11 Argo 자동 콜백 ↔ 셀러 수동 우선순위 — 합의 후 구현
- 이번 spec 닫기 결정 —
finished/이동 vs ongoing 유지 (위 후속 중 일부 닫고 가야 할지 사용자 결정)
🔍 2026-05-07 시점 검증해야 할 것들 — web api consumer 재시작 완료. 작업자 본인 ID/email 으로 admin web 어드민 로그인 후 진행:
-
list 페이지 진입 —
/merchant/orders- 한국어 상태 라벨 / 가격 한국어 통화 포맷 / 주문번호 / 상품 thumbnail+이름 / 결제수단 / 송장번호 컬럼 노출 여부
- 필터 칩: 20 status 모두 보이는지, 클릭하면 서버 필터 동작
- 검색: 주문번호, 이메일, 상품명, 송장번호 입력 시 server-side 필터
- 페이지네이션 정상
-
detail 모달 진입 — 주문 row 클릭
- 셀러 액션 바: 상태별 가능 액션 버튼 노출 (예: PAID 라면 START_PROCESSING / MARK_SHIPPED / CANCEL_BY_SELLER / REFUND_FULL / ADD_NOTE)
- 주문 정보 카드 / 주문자(customerSnapshot) 카드 / 배송지+옵션 카드 / 주문 상품 / 가격 (모든 formatted KRW) / 할인 적용 / 결제 / Fulfillment / Claim / RefundRecords / SellerNotes 모든 섹션 채워짐
- 환불 / 취소 / 메모 audit 누적
-
S1: 정상 풀루프 — PAID 주문 detail → "운송장 입력" → carrier 선택 + 번호 → 저장. status SHIPPED, 추가 액션 (UPDATE_TRACKING / MARK_DELIVERED) 노출. consumer 로그에
OrderShippedBySellerevent. -
S2: 직권 취소 — PAID 주문 → "주문 취소 (직권)" → 사유 입력. CANCELED 전이 + 환불 누적 + RefundRecord 1건 + OrderRefunded/OrderCanceledBySeller event.
-
S3: 고객 취소 요청 → 셀러 승인 — 사용자가 mypage 에서 취소 요청 → 어드민 detail activeClaim 카드 노출 → "취소 승인" 버튼 → CANCELED + 환불.
-
S4: 고객 취소 요청 → 셀러 거절 — "취소 거절" → 이전 상태 복귀 + claim REJECTED.
-
S5: 부분환불 — DELIVERED 주문 → "부분 환불" → 라인 1개 50% → reason. RefundRecord append + line.refundedQuantity 갱신 + OrderPartialRefunded event.
-
S6: 반품 풀루프 (DELIVERED → RETURN_REQUESTED → APPROVE → PICKUP → RETURNED → REFUND_PROCESSING → REFUNDED).
-
S7: 교환 풀루프.
-
S8: 송장 수정 (UPDATE_TRACKING).
-
S9: 메모 audit (ADD_NOTE 셀러 / SYSTEM 자동).
-
S10: list 검색/필터/정렬 종합.
-
S11 결정 후 검증 — Argo 자동 콜백 vs 셀러 수동 입력 우선순위 합의.
2026-05-07
- ✅ Step 1 surface dump →
surface-diff.md - ✅ E2E 시나리오 (S1~S11) 합의 →
scenarios.md - ✅ Seller action catalog →
seller-actions.md - ✅ 결정 게이트: RefundRecord 신설 / RefundPort+Stub / Payment 별도 entity (이미 있음, snapshot read 편의 유지) / Argo 우선순위 추후
- ✅ 서버 구현 완료:
SellerActionenum +OrderStatus.sellerActions()SSOTOrderResponseVo.availableSellerActions추가RefundRecord엔티티 + Repository + ServiceOrderNote엔티티 (audit append-only)RefundPortinterface +StubRefundAdapterRefundApplication(orchestrate)OrderNoteApplicationSellerActionApplication(단일 dispatch)- 신규 platform events: OrderShippedBySeller / OrderTrackingUpdatedBySeller / OrderCanceledBySeller / OrderRefunded / OrderPartialRefunded
- admin DTO 정상화:
AdminOrderResponse/AdminOrderListResponse/AdminOrderSummaryDto와 sub DTO 들 (rich, shop-api parity + admin 확장) - 새 endpoint:
POST /seller-actions/{action}+GET /seller-actions+GET /notes+GET /claims+GET /refund-records OrderController전면 재작성 (legacy /process /ship /deliver /cancel /refund /event 제거)OrderStateApplication.updateTracking신규EventCatalog5 신규 event 등재
- ✅ 클라이언트 구현 완료:
OrderResponse/OrderLineItem/OrderListResponseTS 타입 서버 SSOT 재생성SellerActionName/SellerActionRequestBody/OrderRefundRecord/OrderSellerNote/OrderClaim/OrderFulfillmentSummary/OrderShipment/OrderPayment/OrderPricing추가order.api.ts재작성 (셀러 액션 dispatch + sub-resource list)order.queries.ts재작성useOrder/useOrderList/useOrderSearch정상화- 레거시
useOrders/useUpdateOrderStatus/getOrders/exportOrdersToCSV제거 /merchant/orders/page.tsx전면 재작성 (multi-card detail / SellerActionBar / SellerActionDialog 11 액션 매핑 / NumberInput / Field.Root + Select / 20 status 필터)
- ✅ 서버 smoke (HTTP):
- List rich shape 정상
- Detail rich shape 정상 (customerSnapshot / availableSellerActions / claimHistory / refundRecords / sellerNotes 등)
- ADD_NOTE → SELLER_MANUAL note row append-only 저장
- MARK_SHIPPED → SHIPPED 전이 + SYSTEM audit note + OrderShipped + OrderShippedBySeller event
- UPDATE_TRACKING → tracking 갱신 + OrderTrackingUpdatedBySeller event
- REFUND_FULL → RefundRecord(COMPLETED) + StubRefundAdapter mock processorRefundId + OrderRefunded event
- CANCEL_BY_SELLER → 자동 환불(StubRefundAdapter) + RefundRecord + CANCELED 전이 + OrderRefunded + OrderCanceled + OrderCanceledBySeller (3 events)
- 422 검증: status 별 불가 액션 거부
- admin DTO 를 shop-api 와 같은 shape (rich) 로 재구성 — admin 확장 필드 add-on
- 셀러 액션 단일 dispatch endpoint
- OpenSearch 결과 enrich 통일 (snake_case raw 노출 X)
- RefundRecord 신설 (Order:N) — 부분환불 ledger 정확
- PG 호출은 RefundPort 추상 + StubRefundAdapter (PG 트랙 swap-in)
- TS 타입 서버 SSOT 재생성 (cancelReason/canceledAt/refundAmount 같은 잘못된 필드 정정)
- 페이지네이션은 1-indexed (shop-api 와 정합)
- EventCatalog 카탈로그 갱신 (셀러 알림 룰 surface)
-
S11 (Argo vs 수동 우선순위): 사용자 합의 후 구현. 현재 코드는 manual override 동작 (수동 입력이 자동 콜백을 갈음).
-
PG 어댑터 swap-in: PG 트랙 (3-G) 합류 시
RefundPort의 실제 구현체 (TossRefundAdapter / StripeRefundAdapter) 추가.@Primary를 stub 에서 제거. -
DTO 통합 정리 (tech debt): admin 의 AdminOrder* DTO 들이 shop-api 의 OrderDetail*/OrderSummaryDto 와 코드 duplication. 운영 진입 후 makitt-core 또는 별도 shared 모듈로 통합 권장.
-
list 정렬 / 기간 필터 server-side: 컨트롤러 파라미터 받지만 OrderSearchService 가 아직 활용 안 함. 후속 작업.
-
operator user/email 자동 주입: admin 인증 필터에서 HttpServletRequest attribute 로 채워지면 자동. 현재 anonymous 테스트는 null 로 기록됨.
-
부분환불 line.refundedQuantity 누적 정합: 현재 구현됨. line.refundableQuantity 검증 + 두 번째/세 번째 부분환불 누적까지 단위 검증 필요.
-
헨리엣 샵 ID:
71261f65-ee4e-4238-9b4f-d56cee9be523 -
spec 파일:
surface-diff.md,scenarios.md,seller-actions.md,README.md -
메모리:
project_platform_events.md(신규 5 event 등재 SSOT)
비어 있음
당신이 처리할 항목