Skip to content

Conversation

@sjk4618
Copy link
Member

@sjk4618 sjk4618 commented Oct 4, 2025

🔥Pull requests

⛳️ 작업한 브랜치

👷 작업한 내용

  • feat: 행사 티켓 라운드+타입 등록 API

🚨 참고 사항

@coderabbitai
Copy link

coderabbitai bot commented Oct 4, 2025

📝 Walkthrough

Summary by CodeRabbit

  • 신기능
    • 관리자에서 티켓 라운드와 여러 티켓 유형을 한 번에 등록할 수 있는 기능을 추가했습니다. 판매 시작/종료일, 유형별 이름·가격·총수량·판매기간을 포함해 일괄 등록이 가능합니다.
    • 요청 값 유효성 검증을 강화해 필수값 누락, 음수/0 가격·수량, 날짜 형식 오류(yyyy.MM.dd HH:mm)를 사전에 차단합니다.
    • 잘못된 날짜·시간 범위 입력 시 명확한 오류 응답을 제공합니다.

Walkthrough

관리자용 API에 이벤트별 티켓 라운드와 다수의 티켓 타입을 한 번에 등록하는 POST 엔드포인트와 해당 요청 DTO(record) 및 서비스 로직(라운드 생성 → 타입 일괄 저장, 예외 래핑)이 추가되었다. Saver 컴포넌트 의존성이 주입되었다.

Changes

Cohort / File(s) Summary of changes
Controller: AdminTicketController
src/main/java/.../admin/ticket/api/controller/AdminTicketController.java
POST /api/admin/tickets/{eventId} 엔드포인트 추가. @RequestBody @Valid TicketRoundWithTypeCreateRequest 수신 및 서비스 호출로 생성 위임. 임포트 정리.
DTO: TicketRoundWithTypeCreateRequest
src/main/java/.../admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java
라운드명, 판매 시작/종료일시, 타입 리스트를 갖는 public record 추가. 중첩 TicketTypeRequest 정의. Bean Validation 및 @JsonFormat 적용.
Service: AdminTicketService
src/main/java/.../admin/ticket/api/service/AdminTicketService.java
createTicketRoundWithType(...) 트랜잭셔널 메서드 추가. AdminTicketRoundSaver, AdminTicketTypeSaver 의존성 주입. 라운드 생성 후 타입 매핑·저장 헬퍼 추가 및 도메인 예외를 AdminApiException(BAD_REQUEST_DATE_TIME_ERROR)로 래핑.

Sequence Diagram(s)

sequenceDiagram
    actor Admin as Admin(요청자)
    participant C as AdminTicketController
    participant S as AdminTicketService
    participant RS as AdminTicketRoundSaver
    participant TS as AdminTicketTypeSaver

    Admin->>C: POST /api/admin/tickets/{eventId}\n(TicketRoundWithTypeCreateRequest)
    C->>S: createTicketRoundWithType(eventId, name, start, end, types)
    S->>RS: saveRound(eventId, name, start, end)
    RS-->>S: TicketRoundEntity
    loop for each type
        S->>TS: saveType(roundId, name, price, totalCount, start, end)
        TS-->>S: TicketTypeEntity
    end
    S-->>C: void (success)
    C-->>Admin: 200 OK

    alt 도메인 날짜/시간 오류
        RS--x S: TicketRoundIllegalArgumentException
        S-->>C: throw AdminApiException(BAD_REQUEST_DATE_TIME_ERROR)
        C-->>Admin: 400 Bad Request
    else 타입 유효성 오류
        TS--x S: TicketTypeIllegalException
        S-->>C: throw AdminApiException(BAD_REQUEST_DATE_TIME_ERROR)
        C-->>Admin: 400 Bad Request
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed 해당 제목은 행사 티켓 라운드와 타입 등록 API 구현이라는 주요 변경 사항을 구체적으로 요약하고 있어 PR의 핵심을 명확하게 전달합니다.
Linked Issues Check ✅ Passed PR에는 컨트롤러, DTO, 서비스 로직이 완전하게 구현되어 있어 이슈 #142의 ‘행사 티켓 라운드+타입 등록 API’ 목표를 충족합니다.
Out of Scope Changes Check ✅ Passed PR에 추가된 모든 변경 사항이 이슈 #142의 범위 내에서 이루어졌으며 관련 없는 코드나 설정 변경이 포함되지 않았습니다.
Description Check ✅ Passed 제공된 설명은 구현된 행사 티켓 라운드+타입 등록 API 기능을 명시적으로 언급하고 있어 변경 사항과 관련성이 높고 크게 벗어나지 않습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#142

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (10)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (2)

10-10: 와일드카드 import 사용 확인 필요

import org.springframework.web.bind.annotation.*;로 변경되었습니다. 와일드카드 import는 사용되는 어노테이션이 많을 때는 괜찮지만, 명시적 import가 코드 가독성과 유지보수성을 높입니다. 현재 @PostMapping, @PathVariable, @RequestBody 등 몇 가지만 사용 중이므로 명시적 import를 고려하세요.


40-47: 생성된 리소스 ID를 응답에 포함하는 것을 고려하세요.

현재 생성 성공 시 데이터 없이 OK만 반환합니다. RESTful 관례상 생성된 티켓 라운드 ID를 응답에 포함하면 클라이언트가 후속 작업(조회, 수정 등)을 더 쉽게 수행할 수 있습니다.

서비스 메서드가 생성된 ticketRoundId를 반환하도록 수정한 후, 다음과 같이 응답에 포함할 수 있습니다:

-adminTicketService.createTicketRoundWithType(
+long createdTicketRoundId = adminTicketService.createTicketRoundWithType(
         eventId,
         ticketRoundWithTypeCreateRequest.ticketRoundName(),
         ticketRoundWithTypeCreateRequest.ticketRoundSalesStartDate(),
         ticketRoundWithTypeCreateRequest.ticketRoundSalesEndDate(),
         ticketRoundWithTypeCreateRequest.ticketTypes()
 );
-return ApiResponseUtil.success(SuccessCode.OK);
+return ApiResponseUtil.success(SuccessCode.OK, Map.of("ticketRoundId", createdTicketRoundId));
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java (4)

31-33: 가격 타입 확인

price 필드가 Long 타입입니다. 금액을 다룰 때는 통화 단위와 정밀도를 고려해야 합니다. 정수 원화 금액이라면 Long이 적절하지만, 소수점이 필요하거나 정밀한 금액 계산이 필요하다면 BigDecimal 사용을 고려하세요.

현재 서비스 계층(AdminTicketService.java:126)에서 BigDecimal.valueOf(ticketType.price())로 변환하고 있으므로, 일관성을 위해 DTO부터 BigDecimal을 사용하는 것도 검토할 수 있습니다.


15-21: 날짜 범위의 논리적 일관성 검증을 고려하세요.

현재 ticketRoundSalesStartDateticketRoundSalesEndDate보다 이후일 수 있습니다. 커스텀 validation을 추가하여 시작일이 종료일보다 앞서는지 검증하는 것을 권장합니다.

클래스 레벨에 커스텀 검증 어노테이션을 추가할 수 있습니다:

@ValidDateRange(startField = "ticketRoundSalesStartDate", endField = "ticketRoundSalesEndDate")
public record TicketRoundWithTypeCreateRequest(
    // ...
) {

또는 서비스 레이어에서 검증하는 방법도 있습니다.


39-45: 중첩된 TicketTypeRequest에도 날짜 범위 검증이 필요합니다.

외부 레코드와 마찬가지로 startDateendDate보다 이후일 수 있습니다. 동일한 날짜 범위 검증 로직 적용을 권장합니다.


31-33: 통화 금액에 BigDecimal 사용을 고려하세요.

Long 타입으로 가격을 저장하면 정밀도 문제는 없지만, 금액 계산 시 일관성을 위해 BigDecimal을 사용하는 것이 일반적입니다. 특히 서비스 레이어(Line 126)에서 BigDecimal.valueOf()로 변환하고 있습니다.

DTO 단계에서 BigDecimal을 받도록 변경하면 변환 단계를 제거할 수 있습니다:

-@Positive(message = "티켓타입 가격은 0보다 커야 합니다.")
-Long price,
+@DecimalMin(value = "0.0", inclusive = false, message = "티켓타입 가격은 0보다 커야 합니다.")
+BigDecimal price,
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (4)

24-24: 사용되지 않는 import 제거

org.springframework.cglib.core.Local이 코드에서 사용되지 않습니다. 불필요한 import는 제거하세요.

-import org.springframework.cglib.core.Local;

105-118: 생성된 티켓 라운드 ID를 반환하도록 변경하세요.

RESTful API 관례에 따라 생성 API는 생성된 리소스의 식별자를 반환해야 합니다. 현재 void로 반환하고 있어 컨트롤러가 생성된 ID를 응답에 포함할 수 없습니다.

다음과 같이 메서드 시그니처와 반환문을 수정하세요:

 @Transactional
-public void createTicketRoundWithType(final long eventId,
+public long createTicketRoundWithType(final long eventId,
                                       final String ticketRoundName,
                                       final LocalDateTime roundSalesStartDate,
                                       final LocalDateTime roundSalesEndDate,
                                       final List<TicketRoundWithTypeCreateRequest.TicketTypeRequest> ticketTypeRequests) {
 
     try {
         final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName, roundSalesStartDate, roundSalesEndDate);
         saveTicketTypes(ticketTypeRequests, savedTicketRound.getTicketRoundId());
+        return savedTicketRound.getTicketRoundId();
     } catch (TicketRoundIllegalArgumentException | TicketTypeIllegalException e) {
         throw new AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR);
     }
 }

115-117: 예외 처리의 세분성을 개선하세요.

TicketRoundIllegalArgumentExceptionTicketTypeIllegalException은 서로 다른 도메인 예외이지만 동일한 BAD_REQUEST_DATE_TIME_ERROR로 매핑됩니다. 각 예외 유형에 맞는 구체적인 에러 코드를 사용하면 클라이언트가 문제를 더 명확히 파악할 수 있습니다.

예외 타입별로 다른 에러 코드를 사용하도록 개선할 수 있습니다:

-} catch (TicketRoundIllegalArgumentException | TicketTypeIllegalException e) {
-    throw new AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR);
+} catch (TicketRoundIllegalArgumentException e) {
+    throw new AdminApiException(ErrorCode.INVALID_TICKET_ROUND_DATE_RANGE);
+} catch (TicketTypeIllegalException e) {
+    throw new AdminApiException(ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE);
 }

(해당 에러 코드가 ErrorCode enum에 존재하는지 확인 필요)


113-113: 공백 제거 권장

saveTicketRound(eventId,ticketRoundName 부분에서 쉼표 뒤 공백이 누락되었습니다. 일관성을 위해 공백을 추가하는 것이 좋습니다.

-final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName, roundSalesStartDate, roundSalesEndDate);
+final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId, ticketRoundName, roundSalesStartDate, roundSalesEndDate);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51ede5a and 505a312.

📒 Files selected for processing (3)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (2 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (1)
src/main/java/com/permitseoul/permitserver/global/response/ApiResponseUtil.java (1)
  • ApiResponseUtil (7-29)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (3)
src/main/java/com/permitseoul/permitserver/domain/admin/ticketround/exception/AdminTicketRoundNotFoundException.java (1)
  • AdminTicketRoundNotFoundException (3-4)
src/main/java/com/permitseoul/permitserver/domain/ticketround/core/exception/TicketRoundIllegalArgumentException.java (1)
  • TicketRoundIllegalArgumentException (3-4)
src/main/java/com/permitseoul/permitserver/domain/tickettype/core/exception/TicketTypeIllegalException.java (1)
  • TicketTypeIllegalException (3-4)
🔇 Additional comments (4)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (3)

105-132: 티켓 라운드 및 타입 생성 로직 승인

새로운 createTicketRoundWithType 메서드는 트랜잭션 경계가 올바르게 설정되어 있고, 라운드 저장 후 타입을 일괄 저장하는 흐름이 논리적으로 정확합니다. Saver 컴포넌트를 통한 영속화와 예외 래핑도 적절합니다.

다만 위에서 언급한 날짜 범위 검증과 에러 코드 적절성을 확인하시기 바랍니다.


115-117: 에러 코드 매핑 적절
TicketRoundIllegalArgumentExceptionTicketTypeIllegalException은 모두 날짜/시간 비교 실패 시에만 발생하므로 BAD_REQUEST_DATE_TIME_ERROR로 매핑하는 것이 적합합니다.


120-132: LGTM!

헬퍼 메서드 saveTicketTypes는 DTO를 엔티티로 매핑하고 일괄 저장하는 로직이 명확하게 구현되어 있습니다. Stream API를 사용한 변환과 saveAllTicketTypes 호출이 적절합니다.

src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java (1)

16-16: JsonFormat 패턴 점 구분자 사용 여부 확인
해당 DTO 및 타 응답 DTO에서 모두 @JsonFormat(pattern = "yyyy.MM.dd HH:mm")를 사용 중입니다. API 스펙에서 점(.) 구분자가 의도된 것인지 검증해 주세요.

Comment on lines +106 to +110
public void createTicketRoundWithType(final long eventId,
final String ticketRoundName,
final LocalDateTime roundSalesStartDate,
final LocalDateTime roundSalesEndDate,
final List<TicketRoundWithTypeCreateRequest.TicketTypeRequest> ticketTypeRequests) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

날짜 범위 검증 부재

서비스 계층에서 날짜 논리 검증(예: roundSalesStartDate < roundSalesEndDate, 각 티켓타입의 startDate < endDate, 티켓타입 날짜가 라운드 판매 기간 내인지)을 수행하지 않습니다.

현재는 Saver 컴포넌트에서 TicketRoundIllegalArgumentException 또는 TicketTypeIllegalException을 발생시키는 것으로 보이지만, 비즈니스 규칙 검증은 서비스 계층에서 명시적으로 수행하는 것이 좋습니다. 이렇게 하면 에러 메시지가 더 구체적이고 디버깅이 쉬워집니다.

다음과 같이 검증 로직을 추가하는 것을 고려하세요:

if (roundSalesStartDate.isAfter(roundSalesEndDate)) {
    throw new AdminApiException(ErrorCode.INVALID_SALES_DATE_RANGE);
}

for (TicketRoundWithTypeCreateRequest.TicketTypeRequest typeReq : ticketTypeRequests) {
    if (typeReq.startDate().isAfter(typeReq.endDate())) {
        throw new AdminApiException(ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE);
    }
    // 필요시: 티켓타입 날짜가 라운드 판매 기간 내에 있는지 확인
}
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java
around lines 106 to 110, add explicit service-layer date validations: verify
roundSalesStartDate is not after roundSalesEndDate and throw an
AdminApiException with ErrorCode.INVALID_SALES_DATE_RANGE if it is; for each
TicketRoundWithTypeCreateRequest.TicketTypeRequest ensure its startDate is not
after its endDate and throw AdminApiException with
ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE on violation; additionally validate
each ticket type's start/end dates fall within the roundSalesStartDate and
roundSalesEndDate bounds and throw an appropriate AdminApiException (new or
existing error code) when out of range.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (1)

102-115: 날짜 범위 검증이 여전히 누락되었습니다.

이전 리뷰에서 지적된 날짜 논리 검증이 아직 구현되지 않았습니다. 현재 코드는 Saver 컴포넌트에서 발생하는 예외만 포착하고 있지만, 다음과 같은 비즈니스 규칙 검증을 서비스 계층에서 명시적으로 수행해야 합니다:

  • 라운드 판매 시작일이 종료일보다 이전인지 검증
  • 각 티켓 타입의 시작일이 종료일보다 이전인지 검증
  • (선택사항) 티켓 타입의 날짜가 라운드 판매 기간 내에 있는지 검증

이렇게 하면 에러 메시지가 더 구체적이고 디버깅이 용이해집니다.

다음과 같이 검증 로직을 추가하세요:

 @Transactional
 public void createTicketRoundWithType(final long eventId,
                                       final String ticketRoundName,
                                       final LocalDateTime roundSalesStartDate,
                                       final LocalDateTime roundSalesEndDate,
                                       final List<TicketRoundWithTypeCreateRequest.TicketTypeRequest> ticketTypeRequests) {
+    // 라운드 판매 기간 검증
+    if (roundSalesStartDate.isAfter(roundSalesEndDate)) {
+        throw new AdminApiException(ErrorCode.INVALID_SALES_DATE_RANGE);
+    }
+    
+    // 티켓 타입 날짜 검증
+    for (TicketRoundWithTypeCreateRequest.TicketTypeRequest typeReq : ticketTypeRequests) {
+        if (typeReq.startDate().isAfter(typeReq.endDate())) {
+            throw new AdminApiException(ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE);
+        }
+    }

     try {
         final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName, roundSalesStartDate, roundSalesEndDate);
         saveTicketTypes(ticketTypeRequests, savedTicketRound.getTicketRoundId());
     } catch (TicketRoundIllegalArgumentException | TicketTypeIllegalException e) {
         throw new AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR);
     }
 }
🧹 Nitpick comments (5)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (2)

11-11: 와일드카드 import 대신 명시적 import를 권장합니다.

코드 가독성과 명확성을 위해 와일드카드 import(.*) 대신 필요한 클래스들을 명시적으로 import하는 것이 좋습니다.

다음과 같이 수정할 수 있습니다:

-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;

11-11: 와일드카드 임포트 대신 명시적 임포트를 사용하는 것을 고려하세요.

import org.springframework.web.bind.annotation.*;와 같은 와일드카드 임포트는 네임스페이스 충돌 가능성을 높이고 코드 가독성을 떨어뜨릴 수 있습니다.

다음과 같이 명시적 임포트로 변경하는 것을 고려하세요:

-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (3)

102-115: 서비스 계층에서 날짜 유효성 검증을 명시적으로 수행하는 것을 권장합니다.

현재는 Saver 컴포넌트에서 예외를 발생시키는 방식으로 검증이 이루어지고 있습니다. 비즈니스 규칙 검증을 서비스 계층에서 명시적으로 수행하면 다음과 같은 장점이 있습니다:

  • 더 명확한 에러 메시지 제공
  • 디버깅 용이성 향상
  • 비즈니스 로직의 명확한 표현

다음과 같은 검증 로직 추가를 고려하세요:

@Transactional
public void createTicketRoundWithType(final long eventId,
                                      final String ticketRoundName,
                                      final LocalDateTime roundSalesStartDate,
                                      final LocalDateTime roundSalesEndDate,
                                      final List<TicketRoundWithTypeCreateRequest.TicketTypeRequest> ticketTypeRequests) {
    
    // 티켓 라운드 날짜 검증
    if (roundSalesStartDate.isAfter(roundSalesEndDate)) {
        throw new AdminApiException(ErrorCode.INVALID_TICKET_ROUND_DATE_RANGE);
    }
    
    // 각 티켓 타입의 날짜 검증
    for (TicketRoundWithTypeCreateRequest.TicketTypeRequest typeReq : ticketTypeRequests) {
        if (typeReq.startDate().isAfter(typeReq.endDate())) {
            throw new AdminApiException(ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE);
        }
        // 선택사항: 티켓 타입 날짜가 라운드 판매 기간 내에 있는지 확인
        if (typeReq.startDate().isBefore(roundSalesStartDate) || typeReq.endDate().isAfter(roundSalesEndDate)) {
            throw new AdminApiException(ErrorCode.TICKET_TYPE_DATE_OUT_OF_ROUND_RANGE);
        }
    }

    final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId, ticketRoundName, roundSalesStartDate, roundSalesEndDate);
    saveTicketTypes(ticketTypeRequests, savedTicketRound.getTicketRoundId());
}

110-110: 코드 포맷팅 일관성을 개선하세요.

eventId 다음 콤마 뒤에 공백이 누락되었습니다.

-final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName, roundSalesStartDate, roundSalesEndDate);
+final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId, ticketRoundName, roundSalesStartDate, roundSalesEndDate);

112-114: 예외 처리 전략을 검토하세요.

서로 다른 두 가지 예외(TicketRoundIllegalArgumentException, TicketTypeIllegalException)를 동일한 제네릭 에러 코드(BAD_REQUEST_DATE_TIME_ERROR)로 매핑하고 있습니다. 이는 디버깅을 어렵게 만들 수 있습니다.

각 예외 타입에 대해 보다 구체적인 에러 코드를 사용하는 것을 고려하세요:

-} catch (TicketRoundIllegalArgumentException | TicketTypeIllegalException e) {
-    throw new AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR);
+} catch (TicketRoundIllegalArgumentException e) {
+    throw new AdminApiException(ErrorCode.INVALID_TICKET_ROUND_DATE_RANGE);
+} catch (TicketTypeIllegalException e) {
+    throw new AdminApiException(ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 505a312 and ece2372.

📒 Files selected for processing (3)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (2 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (1)
src/main/java/com/permitseoul/permitserver/global/response/ApiResponseUtil.java (1)
  • ApiResponseUtil (7-29)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (2)
src/main/java/com/permitseoul/permitserver/domain/ticketround/core/exception/TicketRoundIllegalArgumentException.java (1)
  • TicketRoundIllegalArgumentException (3-4)
src/main/java/com/permitseoul/permitserver/domain/tickettype/core/exception/TicketTypeIllegalException.java (1)
  • TicketTypeIllegalException (3-4)
🔇 Additional comments (3)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (2)

37-40: 과거 리뷰 이슈가 해결되었습니다!

@Valid 어노테이션이 요청 바디에 올바르게 추가되어, TicketRoundWithTypeCreateRequest의 검증 제약조건들이 이제 정상적으로 실행됩니다.


35-49: 엔드포인트 구현이 올바르게 되었습니다.

  • @Valid 어노테이션이 올바르게 추가되어 요청 바디 검증이 활성화되었습니다.
  • 서비스 계층으로의 위임이 적절하게 구현되었습니다.
  • 응답 처리가 다른 엔드포인트들과 일관성 있게 처리되었습니다.
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (1)

117-129: 헬퍼 메서드 구현이 적절합니다.

티켓 타입 엔티티 생성 및 일괄 저장 로직이 명확하게 분리되어 있으며, 스트림을 활용한 매핑이 효율적으로 구현되었습니다.

final List<TicketRoundWithTypeCreateRequest.TicketTypeRequest> ticketTypeRequests) {

try {
final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName, roundSalesStartDate, roundSalesEndDate);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

콤마 뒤 공백이 누락되었습니다.

eventId,ticketRoundName 사이에 공백이 없습니다.

다음과 같이 수정하세요:

-        final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName, roundSalesStartDate, roundSalesEndDate);
+        final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId, ticketRoundName, roundSalesStartDate, roundSalesEndDate);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName, roundSalesStartDate, roundSalesEndDate);
final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId, ticketRoundName, roundSalesStartDate, roundSalesEndDate);
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java
around line 110, there is a missing space after the comma in the method call
"adminTicketRoundSaver.saveTicketRound(eventId,ticketRoundName,
roundSalesStartDate, roundSalesEndDate)"; add a single space after the comma so
it reads "eventId, ticketRoundName" to conform to spacing style.

Comment on lines +112 to +114
} catch (TicketRoundIllegalArgumentException | TicketTypeIllegalException e) {
throw new AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

서로 다른 예외를 하나의 에러 코드로 래핑하면 에러 컨텍스트가 손실됩니다.

TicketRoundIllegalArgumentExceptionTicketTypeIllegalException을 모두 BAD_REQUEST_DATE_TIME_ERROR로 처리하면, 티켓 라운드 날짜 오류인지 티켓 타입 날짜 오류인지 구분할 수 없어 디버깅과 사용자 피드백이 어려워집니다.

다음과 같이 각 예외를 별도로 처리하여 구체적인 에러 메시지를 제공하세요:

     try {
         final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(eventId, ticketRoundName, roundSalesStartDate, roundSalesEndDate);
         saveTicketTypes(ticketTypeRequests, savedTicketRound.getTicketRoundId());
-    } catch (TicketRoundIllegalArgumentException | TicketTypeIllegalException e) {
-        throw new AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR);
+    } catch (TicketRoundIllegalArgumentException e) {
+        throw new AdminApiException(ErrorCode.INVALID_TICKET_ROUND_DATE_RANGE);
+    } catch (TicketTypeIllegalException e) {
+        throw new AdminApiException(ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (TicketRoundIllegalArgumentException | TicketTypeIllegalException e) {
throw new AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR);
}
try {
final TicketRound savedTicketRound = adminTicketRoundSaver.saveTicketRound(
eventId, ticketRoundName, roundSalesStartDate, roundSalesEndDate
);
saveTicketTypes(ticketTypeRequests, savedTicketRound.getTicketRoundId());
} catch (TicketRoundIllegalArgumentException e) {
throw new AdminApiException(ErrorCode.INVALID_TICKET_ROUND_DATE_RANGE);
} catch (TicketTypeIllegalException e) {
throw new AdminApiException(ErrorCode.INVALID_TICKET_TYPE_DATE_RANGE);
}
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java
around lines 112-114, the catch block currently wraps both
TicketRoundIllegalArgumentException and TicketTypeIllegalException into the same
AdminApiException(ErrorCode.BAD_REQUEST_DATE_TIME_ERROR), losing error context;
update the catch handling to catch each exception type separately and throw
distinct AdminApiException instances (or use different ErrorCode values) so
TicketRoundIllegalArgumentException maps to a specific error code/message
indicating a ticket round/date issue and TicketTypeIllegalException maps to a
different error code/message indicating a ticket type/date issue, ensuring each
thrown AdminApiException includes the original exception as the cause.

@sjk4618 sjk4618 merged commit fc5eb28 into dev Oct 4, 2025
2 checks passed
@sjk4618 sjk4618 deleted the feat/#142 branch October 4, 2025 06:49
@sjk4618 sjk4618 changed the title feat: 행사 티켓 라운드+타입 등록 api 구현 - #142 feat: admin 행사 티켓 라운드+타입 등록 api 구현 - #142 Oct 5, 2025
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.

feat: admin 행사 티켓 라운드+타입 등록 API

2 participants