generated from ttipo/project-base
fix(backend): 행사 목록에서 끝을 생략한 없는 시작일이 500 이 나던 것 수정
from=2030-13-01 처럼 없는 시작일에 to 를 생략하면 끝 날짜를 계산하다 RangeError 가 나서 VAL_001(400) 대신 SYS_001(500)이 나갔다. from=9999-12-31 은 DB 가 읽지 못하는 끝 날짜를 만들었다. 날짜 계산을 shared/utils/iso-date.ts 순수 함수로 옮기고, 없는 날짜와 네 자리 연도를 넘는 결과는 null 로 알려 VAL_001 로 응답한다. 앱은 항상 두 날짜를 보내므로 사용자에게는 보이지 않았다. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
toPaginatedResult,
|
||||
} from '@/common/dto/pagination.dto';
|
||||
import { BusinessException } from '@/common/errors/business.exception';
|
||||
import { addDaysIso, daysBetweenIso } from '@/shared/utils/iso-date';
|
||||
import { kstDateString } from '@/shared/utils/kst';
|
||||
import { EventDetailDto, EventListItemDto } from './dto/event-list-item.dto';
|
||||
import {
|
||||
@@ -21,7 +22,6 @@ import type { EventSource } from './place.constants';
|
||||
import { decodePlaceCursor, encodePlaceCursor } from './place-cursor';
|
||||
import { boundingBox, sqlDistanceM } from './place.sql';
|
||||
|
||||
const DAY_MS = 86_400_000;
|
||||
const KST_OFFSET_MS = 9 * 60 * 60 * 1000;
|
||||
|
||||
/** 출처 표기(행사 상세 하단·앱 정보 화면) */
|
||||
@@ -37,23 +37,6 @@ const MAX_SPAN_SQL = `CASE e.kind ${Object.entries(EVENT_MAX_SPAN_DAYS)
|
||||
.map(([kind, days]) => `WHEN '${kind}' THEN ${days}`)
|
||||
.join(' ')} END`;
|
||||
|
||||
/** 'YYYY-MM-DD' 두 날짜 사이 일수(b − a). 형식이 틀리거나 없는 날짜면 NaN */
|
||||
function daysBetween(a: string, b: string): number {
|
||||
const ta = Date.parse(`${a}T00:00:00Z`);
|
||||
const tb = Date.parse(`${b}T00:00:00Z`);
|
||||
// '2030-13-01' 처럼 해석이 안 되면 NaN — toISOString 이 예외를 던지지 않게 먼저 거른다
|
||||
if (Number.isNaN(ta) || Number.isNaN(tb)) return Number.NaN;
|
||||
const validA = new Date(ta).toISOString().startsWith(a);
|
||||
const validB = new Date(tb).toISOString().startsWith(b);
|
||||
return validA && validB ? Math.round((tb - ta) / DAY_MS) : Number.NaN;
|
||||
}
|
||||
|
||||
function addDays(iso: string, days: number): string {
|
||||
return new Date(Date.parse(`${iso}T00:00:00Z`) + days * DAY_MS)
|
||||
.toISOString()
|
||||
.slice(0, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* 축제·공연·행사 조회 — 홈 '이번 주말 축제·행사', 장소 상세 '근처 축제·행사'.
|
||||
* 60일 넘는 상시 프로그램과 숨김 행사는 목록에서 뺀다.
|
||||
@@ -71,9 +54,10 @@ export class FestivalService {
|
||||
): Promise<PaginatedResult<EventListItemDto>> {
|
||||
const today = kstDateString(now);
|
||||
const from = query.from ?? today;
|
||||
const to = query.to ?? addDays(from, EVENT_RANGE_DEFAULT_DAYS);
|
||||
const span = daysBetween(from, to);
|
||||
if (!(span >= 0 && span <= EVENT_RANGE_MAX_DAYS)) {
|
||||
// 끝을 생략하면 시작일 + 기본 일수. 시작일이 없는 날짜("2030-13-01")면 null 이 되어 아래에서 VAL_001 로 거른다
|
||||
const to = query.to ?? addDaysIso(from, EVENT_RANGE_DEFAULT_DAYS);
|
||||
const span = to === null ? Number.NaN : daysBetweenIso(from, to);
|
||||
if (to === null || !(span >= 0 && span <= EVENT_RANGE_MAX_DAYS)) {
|
||||
throw new BusinessException('VAL_001', {
|
||||
context: { reason: '기간 오류', from, to },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user