feat(store): 매장별 픽업 달력·시간 슬롯 조회(storePickupCalendar/storePickupTimeSlots) - #193
Conversation
…ots) 상품 상세 주문 전 단계의 픽업 일정 화면은 달력의 날짜별 '마감' 표기와 시간 슬롯 비활성(회색)을 요구한다. 기존 pickupCalendar/pickupTimeSlots는 홈 전역 고정 정책이라 매장별 휴무·영업시간·capacity를 표현할 수 없어 매장별 조회 쿼리를 신설한다. - SDL store-pickup-schedule.graphql: storePickupCalendar(storeId, yearMonth) / storePickupTimeSlots(storeId, date), StorePickupDay.reason은 PAST | OUT_OF_RANGE | CLOSED | CAPACITY_FULL - StorePickupScheduleService: todayPickupStores와 동일한 매장 정책 판정을 월 단위로 확장 — 특별휴무·요일 영업시간·일일 capacity(레코드 없으면 무제한, 취소·soft-delete 주문 제외 수량 합 기준)·당일 리드타임 잔여 슬롯 - StoreRepository: 매장 픽업 정책·요일 영업시간 전체·기간 휴무/capacity· KST 날짜별 픽업 수량 합(raw SQL, DST 없는 고정 +9h 변환) 조회 5종 추가 - 달력 범위는 매장 max_days_ahead 컬럼 사용. 명세 외 정책 결정: 오전/오후 경계(12:00)는 매장 무관 UI 규칙으로 보고 pickup 전역 상수 재사용 - store 테스트 팩토리에 max_days_ahead override 추가 회귀 테스트 18건: 달력 판정 전 분기(과거/범위 초과/특별휴무/요일 휴무·미설정/ capacity 소진·무제한/취소·soft-delete 제외/당일 잔여 슬롯), KST 자정 경계 집계, 슬롯 간격·리드타임 컷오프·오전오후 분리, 형식 오류·NOT_FOUND
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
🧹 knip — dead-code 리포트전체 리포트
|
🩺 NestJS Doctor — 89/100 (Good)진단 279건 (error 0).
architecture / security 상위 항목
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f09ad0bf6d
ℹ️ 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".
| storeId: bigint, | ||
| yearMonth: string, | ||
| ): Promise<StorePickupCalendar> { | ||
| const ym = parseKstYearMonth(yearMonth); |
There was a problem hiding this comment.
Reject years outside the safe database date range
The year-month parser accepts every four-digit year, but this endpoint immediately converts it into database bounds. For example, 0000-01 is silently converted by Date.UTC to January 1900 and returns dates from the wrong century, while years below MySQL's DATE minimum (such as 0100-01) and the upper bound for 9999-12 can cause Prisma/MySQL errors instead of the documented BadRequestException. Validate that the year can be represented and round-tripped through both JavaScript and the database before loading the schedule context.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영: 연도를 10009998로 제한하고 벗어나면 BadRequest (storePickupCalendar 연월·storePickupTimeSlots 날짜 모두). 근거 — MySQL DATE 하한 1000-01-01, 9999-12(-31)는 익월/익일 상한 계산이 DATE 상한 초과, 099년은 Date.UTC의 1900년대 매핑 오동작. 회귀 테스트 2건 추가.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Coverage report
Test suite run success1572 tests passing in 184 suites. Report generated by 🧪jest coverage report action from 2fee27b |
Codex 리뷰 반영. 연월/날짜 파서가 임의 4자리 연도를 통과시켜 0~99년은 Date.UTC의 1900년대 매핑으로 엉뚱한 세기를 반환하고, MySQL DATE 범위(1000-01-01~9999-12-31) 밖(및 9999-12의 익월 상한 오버플로)은 BadRequest 대신 Prisma/MySQL 에러가 될 수 있었다. - 연도 1000~9998 밖이면 INVALID_YEAR_MONTH/INVALID_DATE로 거절 - 날짜 검증을 매장 조회(DB 접근)보다 앞으로 이동 - 회귀 테스트 2건 추가 (0000-01/0999-01/9999-12, 9999-12-31)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4dcf7430b3
ℹ️ 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".
| store.id, | ||
| new Date(Date.UTC(ym.year, ym.month - 1, 1)), | ||
| new Date(Date.UTC(ym.year, ym.month, 1)), | ||
| kstMidnightUtc(ym.year, ym.month, 1), |
There was a problem hiding this comment.
Reject the KST boundary that underflows DATETIME
When yearMonth is 1000-01, this KST month boundary becomes 0999-12-31T15:00:00Z and is passed to sumPickupQuantitiesByKstDate for comparison with order.pickup_at, whose schema type is MySQL DATETIME(3) and whose minimum is 1000-01-01 00:00:00; the query can therefore fail instead of returning a calendar. Fresh evidence after the earlier fix is that the newly added MIN_SCHEDULE_YEAR = 1000 check still permits this UTC-underflowing boundary, so validate or clamp the constructed UTC range as well; the same issue affects storePickupTimeSlots for 1000-01-01.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영: 하한을 1001로 상향 (1000-01의 KST 월 시작 경계 -9h가 0999-12-31T15:00Z로 DATETIME 하한 미달). storePickupTimeSlots 1000-01-01 동일 차단. 회귀 테스트 2건 추가.
Codex 리뷰 반영. 1000-01의 KST 월 시작 경계(-9h)가 0999-12-31T15:00Z로 MySQL DATETIME 하한(1000-01-01)을 밑돌아 pickup_at 범위 비교가 실패할 수 있었다. storePickupTimeSlots의 1000-01-01도 동일. - MIN_SCHEDULE_YEAR 1000 → 1001, 근거 주석 갱신 - 회귀 테스트 2건 추가 (1000-01, 1000-01-01)
배경
상품 상세의 주문 전(커스텀 진입 전) 픽업 일정 선택 화면은 달력의 날짜별 "마감" 표기와 시간 슬롯 비활성 표시를 요구합니다.
기존
pickupCalendar/pickupTimeSlots는 홈 화면용 전역 고정 정책이라 매장별 휴무·영업시간·일일 capacity를 표현할 수 없었습니다.그래서 매장별 조회 쿼리
storePickupCalendar(storeId, yearMonth)/storePickupTimeSlots(storeId, date)2건을 신설했습니다.전역 쿼리는 변경하지 않았습니다.
주요 결정 사항
todayPickupStores선례를 월 단위로 확장: 특별휴무·요일 휴무·영업시간 미설정은CLOSED로 처리합니다.capacity는 레코드가 없으면 무제한이고, 취소·soft-delete 제외 주문 수량 합이 capacity 이상이면
CAPACITY_FULL입니다.슬롯은 매장
pickup_slot_interval_minutes간격과[open, close)규칙으로 만들고, 당일만min_lead_time_minutes컷오프를 적용합니다(초 잔여 시 다음 분 올림).max_days_ahead컬럼 사용: 과거는PAST, 초과는OUT_OF_RANGE이며, 당일에 잔여 가용 슬롯이 없으면 날짜 자체를CLOSED처리합니다(전역 달력 선례와 동일).오전/오후 경계(12:00)는 매장 무관 UI 규칙으로 판단해 pickup feature 전역 상수를 배럴 export로 가져왔습니다(명세 외 정책 결정).
0~99년을 1900년대로 매핑하는
Date.UTC오동작도 함께 차단합니다.비로그인 접근 가능하며, 비활성/삭제 매장은 NOT_FOUND입니다.
스키마 변경(마이그레이션) 없음.
검증
yarn validate전체 통과.