Skip to content

chore: 릴리즈 — 주문 생성 API (createOrder) - #209

Merged
chanwoo7 merged 10 commits into
mainfrom
develop
Aug 24, 2026
Merged

chore: 릴리즈 — 주문 생성 API (createOrder)#209
chanwoo7 merged 10 commits into
mainfrom
develop

Conversation

@chanwoo7

@chanwoo7 chanwoo7 commented Aug 24, 2026

Copy link
Copy Markdown
Member

릴리즈 개요

구매자 주문 생성 mutation createOrder 1건을 릴리즈합니다.
develop → main이며, 포함된 변경은 PR #208 하나입니다.

배경

커스텀 단계 UI·기획이 미확정이라 주문 생성 API가 없어 FE가 주문 흐름을 테스트할 수단이 없었습니다.
임시 스텁 대신 정식 API의 확정 부분집합(단일 상품·옵션 일괄 제출·픽업 일시·수량·서버 가격 스냅샷)으로 구현했고, 커스텀은 스펙 확정 후 optional 필드로 확장합니다.
따라서 이후 계약 변경 없이 FE가 최종 계약의 부분집합에 맞춰 개발할 수 있습니다.

주요 결정 사항

  • 검증은 정식 수준: 옵션 그룹 필수/min/max/중복, 픽업 일시는 달력·슬롯과 동일 규칙 + 슬롯 정합 + 상품 제작 소요시간, 활성 USER 계정+프로필 강제.
  • capacity 원자 예약: 트랜잭션 내 capacity 행 FOR UPDATE 잠금 + 점유 재집계로 동시 주문 race 차단 (Codex 리뷰 반영).
  • 금액 안전성: 서버 재계산 + 32비트/음수 범위 검증으로 저장 후 직렬화 실패·언더플로 차단 (Codex 리뷰 반영).
  • 통화: Order에 통화 스냅샷 컬럼이 없어 KRW 외 상품은 거절 — 다국통화는 후속 이슈로 추적.
  • 주문번호 ORD-YYYYMMDD-XXXXXX, SUBMITTED 알림 미발송(기존 규칙), 스키마 변경(마이그레이션) 없음.

검증

  • real DB 회귀 테스트 30건(생성 스냅샷·옵션 규칙·픽업 판정·동시성·금액 범위·계정 검증·input) 추가, yarn validate 전체 통과.
  • Codex 리뷰 5라운드(P1 4건·P2 2건 전부 반영) 후 👍 + 필수 체크 4종 pass로 develop 머지됨.

Summary by CodeRabbit

  • 새로운 기능

    • 인증된 사용자가 상품, 옵션, 픽업 시간, 수량 및 구매자 정보를 입력해 주문을 생성할 수 있습니다.
    • 주문번호, 주문 상태, 픽업 시간, 총 결제 금액을 확인할 수 있습니다.
    • 매장 영업시간, 준비 시간, 휴무일 및 잔여 주문 가능 수량을 반영해 픽업 가능 여부를 검증합니다.
    • 상품·옵션, 계정, 연락처, 통화 및 주문 금액을 검증하고 가격 정보를 안전하게 확정합니다.
  • 버그 수정

    • 중복 주문번호와 동시 주문으로 인한 수량 초과를 방지합니다.
  • 테스트

    • 주문 생성 및 픽업 가능 여부에 대한 검증 범위를 확대했습니다.

커스텀 단계 스펙 미확정 상태에서 FE가 주문 생성 흐름을 테스트할 수 있도록,
확정된 부분(단일 상품·옵션 선택·픽업 일시·수량)만으로 정식 주문 생성
경로를 구현한다. 커스텀 입력은 스펙 확정 후 optional 필드로 확장한다
(임시 스텁이 아니므로 이후 계약 변경 없음 — 사용자 확정 방향).

- SDL order-checkout.graphql: createOrder(input) → 요약(CreateOrderOutput),
  상세는 기존 myOrder 재조회 전제
- OrderCheckoutService: 옵션 그룹 규칙(필수/min/max/중복/타 상품) 검증,
  가격 서버 스냅샷(subtotal 정가·discount 정가-판매가 차액·total 판매가 기준),
  주문자 정보 input 우선 + 프로필(닉네임·전화) fallback, 둘 다 없으면 거절
- StorePickupScheduleService.isPickupSlotAvailable 신설: 달력·슬롯과 동일
  규칙 + 슬롯 시작 시각 정합 + capacity 잔여(기존 점유+수량 ≤ capacity)
  재검증 — 판정 로직은 store feature에 유지(배럴 export)
- OrderRepository.createSubmittedOrder: Order+Item+옵션 스냅샷+상태 히스토리
  중첩 create 원자 생성, SUBMITTED는 기존 규칙대로 알림 미발송
- 주문번호 ORD-YYYYMMDD-XXXXXX(KST 날짜+혼동 문자 제외 랜덤 6자리,
  RandomService 주입) — 명세 외 정책 결정. P2002 충돌 시 3회 재시도

회귀 테스트 24건: 생성 스냅샷·가격·히스토리 / 옵션 규칙 전 분기 /
buyer fallback / 픽업 판정(휴무·정렬·리드타임·capacity 잔여·초 이하) /
주문번호 충돌 재시도·소진 / NOT_FOUND / input 검증 / resolver 통합
- 커밋 전 금액 검증: subtotal/discount/itemSubtotal이 음수·비안전 정수·
  GraphQL Int 상한(2,147,483,647) 초과면 거절 — 저장 후 응답 직렬화
  실패로 인한 재시도 중복 주문 경로 차단, 음수 델타 언더플로 방지
- createSubmittedOrder를 트랜잭션화하고 capacity 행 FOR UPDATE 잠금 후
  점유 재집계 — 동시 주문이 마지막 잔여를 함께 차지하는 race 차단
  (초과 시 null 반환 → 서비스가 BadRequest 변환)
- 회귀 테스트 3건 추가: 32비트 초과/음수 금액 거절, 동시 주문 2건 중
  정확히 1건만 성공(real DB 동시성)
- findProductDetailById에 preparation_time_minutes·currency 활용 추가,
  체크아웃에서 pickupAt ≥ now + 제작 소요시간을 매장 리드타임과 별개로 강제
- Order/OrderItem에 통화 스냅샷 컬럼이 없어 비 KRW 상품은 주문 거절
  (명세 외 정책 결정 — 다국통화 스냅샷은 후속 이슈로 추적)
- 상품 팩토리에 currency·preparation_time_minutes override 추가
  (테스트 기본 제작시간 0분 — 스키마 기본 180분은 당일 픽업 테스트 방해)
- 회귀 테스트 2건 추가 (제작시간 미달 거절/충족 허용, USD 상품 거절)
- 체크아웃 진입 시 활성 USER 계정+활성 프로필을 강제한다
  (requireActiveUser와 동일 의미론 — SELLER/ADMIN이 구매자 mutation으로
  주문을 만들어 capacity·랭킹을 오염시키는 경로 차단)
- buyerPhone에 프로필 전화번호와 동일한 010-XXXX-XXXX 고정 형식 강제
  (임의 문자열이 검증된 프로필 값을 덮어쓰지 못하게)
- 프로필 닉네임이 NOT NULL 최종 fallback이라 도달 불가가 된
  BUYER_NAME_REQUIRED 분기 제거
- 회귀 테스트 3건 추가 (SELLER 거절/프로필 없음 거절/전화 형식)
buyerName을 trim해 빈 값이면 프로필 닉네임 fallback으로 처리한다 —
공백 이름이 검증된 닉네임을 덮어써 판매자에게 빈 표기로 보이는 것 방지.
회귀 케이스 1건 추가.
feat(order): 주문 생성 mutation(createOrder) — 정식 API의 확정 부분집합
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 37 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b9957c87-cd57-40d4-b3ac-eadda8f80171

📥 Commits

Reviewing files that changed from the base of the PR and between 77e0ea1 and 9181128.

📒 Files selected for processing (19)
  • src/features/order/constants/order-error-messages.ts
  • src/features/order/constants/order.constants.ts
  • src/features/order/dto/inputs/create-order.input.spec.ts
  • src/features/order/dto/inputs/create-order.input.ts
  • src/features/order/order-checkout.graphql
  • src/features/order/order.module.ts
  • src/features/order/repositories/order.repository.ts
  • src/features/order/resolvers/order-checkout-mutation.resolver.spec.ts
  • src/features/order/resolvers/order-checkout-mutation.resolver.ts
  • src/features/order/services/order-checkout.service.spec.ts
  • src/features/order/services/order-checkout.service.ts
  • src/features/order/types/create-order-output.type.ts
  • src/features/product/index.ts
  • src/features/product/repositories/product.repository.ts
  • src/features/store/index.ts
  • src/features/store/services/store-pickup-schedule.service.spec.ts
  • src/features/store/services/store-pickup-schedule.service.ts
  • src/features/store/store.module.ts
  • src/test/factories/product.factory.ts
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

🩺 NestJS Doctor — 89/100 (Good)

진단 282건 (error 0).

Category error warning info
architecture 0 0 13
correctness 0 129 0
performance 0 24 17
schema 0 0 86
security 0 13 0
architecture / security 상위 항목
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'IAuditLogRepository'.
  • warning security/security/no-exposed-env-vars: Direct 'process.env.NODE_ENV' access in 'AuthController'. Use ConfigService instead.
  • warning security/security/require-guards-on-endpoints: Endpoint 'start' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'callback' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'refresh' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'logout' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerLogin' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerRefresh' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerLogout' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'devIssueToken' has no @UseGuards() at class or method level.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/conversation/repositories/conversation.repository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'ConversationRepository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/order/repositories/order.repository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'OrderRepository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/product/repositories/product.repository'.

오탐 포함 가능 · 기준 docs/guide/architecture-conventions.md

@github-actions

Copy link
Copy Markdown

🧹 knip — dead-code 리포트

요약 항목 없음
전체 리포트
(knip 출력 없음 — 이슈 0이거나 실행 실패)

청소 후보(오탐 가능) · 기준 docs/guide/architecture-conventions.md

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5966d09bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


const buyerProfile = await this.requireActiveBuyer(accountId);

const product = await this.productRepo.findProductDetailById(productId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject orders missing required option customization

When a selected option group has option_requires_description or option_requires_image enabled, this flow never loads those flags and CreateOrderInput has no corresponding customization data, yet the order is still submitted with only the option-item snapshot. This permits seller-configured products to enter SUBMITTED without required instructions or assets; either persist those required values or reject these products until custom checkout is supported.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영: PR #210 — 설명/이미지 필수 옵션 그룹을 선택한 주문은 커스텀 체크아웃 확장 전까지 거절 (develop 경유 후 본 릴리즈에 포함). 커스텀 입력 추가 시 이 가드를 실제 데이터 검증으로 대체 예정.

@@ -0,0 +1,27 @@
extend type Mutation {
"""주문 생성(구매자). 옵션·픽업 일시를 서버가 재검증하고 가격을 스냅샷한다. 로그인 필수."""
createOrder(input: CreateOrderInput!): CreateOrderOutput!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Deduplicate retried order submissions

When a client retries after a timeout or submits twice, this mutation has no idempotency key or equivalent lookup, and every invocation generates a fresh random order number. The same checkout therefore creates multiple independent SUBMITTED orders and consumes capacity multiple times; accept a client request key, enforce uniqueness per account, and return the existing result on replay.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미반영(후속 추적): 멱등 키는 주문 요청 키 컬럼+unique 등 스키마 변경 동반이라 릴리즈 리뷰 fix 범위 초과. 결제 없는 현 단계 위험도가 낮아, 커스텀 확장 시 입력 계약과 함께 설계하기로 하고 후속 이슈로 등록 예정.

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.61% 4945/5066
🟢 Branches 93.99% 2016/2145
🟢 Functions 96.08% 955/994
🟢 Lines 98.21% 4500/4582

Test suite run success

1603 tests passing in 187 suites.

Report generated by 🧪jest coverage report action from 9181128

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

릴리즈 PR #209 Codex 지적 반영. option_requires_description/image가
켜진 옵션 그룹은 판매자가 요구한 커스텀 정보 없이는 주문이 완성되지
않는데, 체크아웃이 플래그를 읽지 않아 스냅샷만으로 SUBMITTED가 됐다.

- findProductDetailById에 두 플래그 로드 추가
- 해당 그룹 옵션을 선택한 주문은 커스텀 체크아웃 확장 전까지 거절
  (미선택이면 기존대로 허용)
- 회귀 테스트 1건 추가
…on-customization

fix: 릴리즈 리뷰 반영 (설명/이미지 필수 옵션 선택 시 주문 거절)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2acfcc68d3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return false;
}

const isToday = kstDayDiff(now, args.pickupAt) === 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce lead times across future calendar days

When a seller configures minLeadTimeMinutes longer than the remainder of the current day—values up to seven days are allowed in seller.constants.ts—any pickup on tomorrow or later makes isToday false, causing buildDaySlots to replace the current time with -Infinity and ignore the lead time entirely. Consequently, createOrder can commit an order for tomorrow despite a seven-day store lead time whenever the product's separate preparation time is shorter; compare pickupAt against an absolute now + min_lead_time_minutes cutoff for future dates as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영: PR #211 — buildDaySlots 컷오프를 절대 시각(now+리드타임) 기준으로 변경해 하루 넘는 리드타임이 미래 날짜에도 적용되게 수정 (달력 CLOSED 판정 포함, develop 경유 후 본 릴리즈에 포함).

릴리즈 PR #209 Codex 지적 반영. 리드타임 컷오프를 당일(isToday)에만
적용해, 판매자 설정 리드타임이 하루를 넘으면(최대 7일) 미래 날짜에서
무시됐다 — 7일 리드타임 매장의 내일 슬롯이 예약 가능으로 보이고
createOrder도 통과하는 문제.

- buildDaySlots 컷오프를 절대 시각(now + 리드타임) 기준으로 변경
  (해당 날짜 자정 대비 경과 분이 미래일에 음수가 되어 자연 반영)
- 달력 판정도 동일 규칙: 리드타임에 완전히 덮인 미래일은 CLOSED
- 회귀 테스트 1건 추가 (3일 리드: 슬롯 판정·달력·시간 슬롯 3면 검증)
…-time

fix: 릴리즈 리뷰 반영 (하루 넘는 리드타임을 미래 날짜에도 적용)
@chanwoo7
chanwoo7 merged commit 1752ce1 into main Aug 24, 2026
15 checks passed
@chanwoo7
chanwoo7 deleted the develop branch August 24, 2026 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant