Skip to content

Conversation

@sjk4618
Copy link
Member

@sjk4618 sjk4618 commented Oct 4, 2025

🔥Pull requests

⛳️ 작업한 브랜치

👷 작업한 내용

  • 행사 티켓 라운드+타입 전체 조회 API

🚨 참고 사항

@sjk4618 sjk4618 self-assigned this Oct 4, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 4, 2025

📝 Walkthrough

Summary by CodeRabbit

  • 신규 기능

    • 관리자에서 이벤트별 티켓 라운드와 티켓 유형을 함께 조회하고, 판매/환불/사용 수 및 매출 합계를 확인할 수 있습니다.
    • 각 티켓에 개별 가격이 저장되어, 유형별 가격과 쿠폰 적용 금액이 정확히 반영됩니다.
    • 결제 처리 시 쿠폰 사용 여부에 따라 티켓 가격이 자동 계산됩니다.
  • 리팩터링

    • 이벤트·라운드·티켓 유형별 집계 데이터를 안정적으로 제공하도록 조회 로직과 집계 흐름을 개선했습니다.

Walkthrough

이 PR은 이벤트별 티켓 라운드와 각 라운드의 티켓 유형별 집계(판매/환불/사용 수량 및 금액)를 반환하는 새 API와 DTO·서비스·리트리버를 추가하고, 티켓 도메인에 ticketPrice를 도입해 생성·저장·집계 로직 및 관련 저장소/컴포넌트 시그니처를 변경합니다.

Changes

Cohort / File(s) Summary
Admin Controller
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java
기존 핸들러명 변경: getTicketRoundAndTypeDetailsgetTicketRoundAndTicketTypeDetails(final long ticketRoundId); 신규 엔드포인트 getTicketRoundsWithTicketType(@PathVariable("eventId") final long eventId) 추가
Admin Service & DTO
src/main/java/.../admin/ticket/api/service/AdminTicketService.java, src/main/java/.../admin/ticket/api/dto/res/TicketRoundAndTicketTypeRes.java
서비스에 getTicketRoundWithTicketType(long eventId) 추가(라운드·타입별 집계 로직); 응답 DTO TicketRoundAndTicketTypeRes 및 내부 레코드(라운드·타입 정보) 추가
Admin Retrievers
src/main/java/.../admin/ticket/core/component/AdminTicketRetriever.java, src/main/java/.../admin/ticketround/core/AdminTicketRoundRetriever.java
신규 컴포넌트 AdminTicketRetriever 추가(타입별 판매/금액/상태별 카운트 조회); getTicketRoundByEventId(long) 추가 및 eventIds 조회에서 리포지토리 메서드명 변경 반영
Ticket domain: price 필드 추가
src/main/java/.../ticket/core/domain/Ticket.java, src/main/java/.../ticket/core/domain/entity/TicketEntity.java
ticketPrice(BigDecimal) 필드·엔티티 컬럼 추가, 생성자/팩토리 메서드/매핑에 ticketPrice 인자 반영
Ticket 생성/저장 변경
src/main/java/.../ticket/core/component/TicketGenerator.java, src/main/java/.../ticket/core/component/TicketSaver.java
generatePublicTickets(..., List<TicketTypeEntity>)로 시그니처 변경; 티켓별 가격 결정 로직 추가(쿠폰 적용 여부 고려); TicketEntity.create(..., ticketPrice) 호출로 저장 시 가격 전달
TicketRepository 확장
src/main/java/.../ticket/core/repository/TicketRepository.java
상태별 카운트 메서드 2개 추가 및 상태 집합별 티켓 가격 합계 반환 JPQL(@query) 메서드 추가
TicketRound Repository API
src/main/java/.../ticketround/core/repository/TicketRoundRepository.java
메서드명 변경: findByEventIdInfindAllByEventIdIn; 단건 조회 추가: findAllByEventId(long); 불필요 import 제거
TicketTypeRetriever & Payment 흐름 변경
src/main/java/.../tickettype/core/component/TicketTypeRetriever.java, src/main/java/.../payment/api/service/PaymentService.java
findAllByIds(List<Long>) 추가(티켓타입 일괄 조회); PaymentService에서 TicketTypeEntity 목록을 조회하여 TicketGenerator에 전달하도록 호출 변경

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Admin
  participant C as AdminTicketController
  participant S as AdminTicketService
  participant RR as AdminTicketRoundRetriever
  participant TR as AdminTicketRetriever
  participant R as TicketRepository

  Admin->>C: GET /admin/events/{eventId}/ticket-rounds-with-types
  C->>S: getTicketRoundWithTicketType(eventId)
  S->>RR: getTicketRoundByEventId(eventId)
  RR-->>S: List<TicketRound>

  loop for each round
    S->>TR: getSoldCount(ticketTypeId, SOLD_STATUSES)
    TR->>R: countByTicketTypeIdAndStatusIn(...)
    R-->>TR: long

    S->>TR: getSoldAmount(ticketTypeId, SOLD_STATUSES)
    TR->>R: sumTicketPriceByTicketTypeIdAndStatuses(...)
    R-->>TR: BigDecimal

    S->>TR: getCountByStatus(ticketTypeId, status)
    TR->>R: countByTicketTypeIdAndStatus(...)
    R-->>TR: long
  end

  S-->>C: TicketRoundAndTicketTypeRes
  C-->>Admin: 200 OK + BaseResponse
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning 티켓 생성·저장 로직 및 PaymentService, Ticket 엔티티의 가격 처리 기능 등은 조회 API 구현 목적과 무관한 변경사항으로 보이며 PR 범위를 벗어납니다. 티켓 가격 처리 및 생성 로직 변경을 별도의 PR로 분리하고 조회 API 관련 변경만 남기도록 수정해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed 한 문장으로 주요 변경 사항인 행사 티켓 라운드 및 타입 조회 API 기능을 명확히 표현하고 있어 PR 제목이 변경 내용과 직접적으로 일치합니다.
Linked Issues Check ✅ Passed 코드에서 컨트롤러, 서비스, DTO, 리트리버, 리포지토리 등 관련 컴포넌트를 통해 행사 티켓 라운드와 타입 전체 조회 API 기능이 구현되어 있어 연결된 이슈의 목적을 충족합니다.
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/#141

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3198cfa and 17dc577.

📒 Files selected for processing (1)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/repository/TicketRepository.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/repository/TicketRepository.java

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: 5

🧹 Nitpick comments (5)
src/main/java/com/permitseoul/permitserver/domain/admin/ticketround/core/AdminTicketRoundRetriever.java (1)

30-34: 메서드명과 반환 타입 불일치

메서드명이 getTicketRoundByEventId (단수)이지만 List<TicketRound> (복수)를 반환합니다. 메서드명을 getTicketRoundsByEventId로 변경하여 복수 반환을 명확히 표현하는 것을 권장합니다.

다음 diff를 적용하여 메서드명을 개선하세요:

-    public List<TicketRound> getTicketRoundByEventId(final long eventId) {
+    public List<TicketRound> getTicketRoundsByEventId(final long eventId) {
         return ticketRoundRepository.findAllByEventId(eventId).stream()
                 .map(TicketRound::fromEntity)
                 .toList();
     }

호출하는 곳(AdminTicketService line 59)도 함께 업데이트해야 합니다.

src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (1)

28-34: 엔드포인트 경로 충돌 가능성 검토

새 엔드포인트 GET /api/admin/tickets/{eventId}와 기존 GET /api/admin/tickets/details/{ticketRoundId} 간 경로 충돌 가능성이 있습니다.

만약 eventId로 문자열 "details"가 전달되면 라우팅 모호성이 발생할 수 있습니다. Spring MVC는 구체적 경로(/details/{id})를 우선하지만, 명확성을 위해 다음 중 하나를 권장합니다:

  1. 경로 접두사 분리: /api/admin/events/{eventId}/ticket-rounds
  2. 쿼리 파라미터 사용: /api/admin/tickets?eventId={eventId}
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/res/TicketRoundAndTicketTypeRes.java (1)

13-23: BigDecimal → long 변환 시 정밀도 손실 및 오버플로우 위험

Line 20에서 totalTicketSoldAmount.longValue()를 호출하여 BigDecimallong으로 변환하고 있습니다.

잠재적 문제:

  1. 소수점 이하 값이 있는 경우 정밀도 손실 발생
  2. BigDecimal 값이 Long.MAX_VALUE(9,223,372,036,854,775,807)를 초과하면 오버플로우 발생

권장 사항:

  • 금액을 센트/원 단위 정수로 저장하고 있다면 현재 구현 유지 가능
  • 소수점 금액을 다루는 경우 DTO에서도 BigDecimal 타입 유지 권장

금액이 매우 크거나 소수점이 필요한 경우, 다음과 같이 타입을 변경하세요:

 public record TicketRoundAndTicketTypeRes(
         long totalTicketCount,
         long totalTicketSoldCount,
-        long totalTicketSoldAmount,
+        BigDecimal totalTicketSoldAmount,
         List<TicketRoundWithTypes> ticketRoundsWithTypes
 ) {
 
     public static TicketRoundAndTicketTypeRes of(final long totalTicketCount,
                                                  final long totalTicketSoldCount,
                                                  final BigDecimal totalTicketSoldAmount,
                                                  final List<TicketRoundWithTypes> ticketRoundsWithTypes) {
         return new TicketRoundAndTicketTypeRes(
                 totalTicketCount,
                 totalTicketSoldCount,
-                totalTicketSoldAmount.longValue(),
+                totalTicketSoldAmount,
                 ticketRoundsWithTypes
         );
     }
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (2)

70-83: 성능 최적화: 단일 스트림 패스로 집계 개선

Lines 70-83에서 roundsWithTypes 스트림을 3번 순회하여 totals를 계산하고 있습니다. 이는 비효율적이며, 단일 패스로 최적화할 수 있습니다.

다음과 같이 리팩토링하여 성능을 개선하세요:

-        final long totalTicketCount = roundsWithTypes.stream()
-                .flatMap(r -> r.ticketTypes().stream())
-                .mapToLong(TicketRoundAndTicketTypeRes.TicketRoundWithTypes.TicketTypeInfo::ticketTypeTotalCount)
-                .sum();
-
-        final long totalSoldCount = roundsWithTypes.stream()
-                .flatMap(r -> r.ticketTypes().stream())
-                .mapToLong(TicketRoundAndTicketTypeRes.TicketRoundWithTypes.TicketTypeInfo::ticketTypeSoldCount)
-                .sum();
-
-        final long totalSoldAmount = roundsWithTypes.stream()
-                .flatMap(r -> r.ticketTypes().stream())
-                .mapToLong(TicketRoundAndTicketTypeRes.TicketRoundWithTypes.TicketTypeInfo::ticketTypeSoldAmount)
-                .sum();
+        record Totals(long count, long sold, long amount) {}
+        
+        final Totals totals = roundsWithTypes.stream()
+                .flatMap(r -> r.ticketTypes().stream())
+                .reduce(
+                    new Totals(0L, 0L, 0L),
+                    (acc, t) -> new Totals(
+                        acc.count + t.ticketTypeTotalCount(),
+                        acc.sold + t.ticketTypeSoldCount(),
+                        acc.amount + t.ticketTypeSoldAmount()
+                    ),
+                    (a, b) -> new Totals(a.count + b.count, a.sold + b.sold, a.amount + b.amount)
+                );
+        
+        final long totalTicketCount = totals.count;
+        final long totalSoldCount = totals.sold;
+        final long totalSoldAmount = totals.amount;

85-90: 불필요한 타입 변환 제거

Line 88에서 BigDecimal.valueOf(totalSoldAmount)로 long을 BigDecimal로 변환한 후, factory 메서드(line 20)에서 다시 longValue()로 변환하고 있습니다.

Factory 메서드에 long을 직접 받는 오버로드를 추가하거나, DTO 필드 타입을 검토하여 불필요한 변환을 제거하세요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f8a002 and 431972d.

📒 Files selected for processing (11)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/res/TicketRoundAndTicketTypeRes.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (4 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/core/component/AdminTicketRetriever.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticketround/core/AdminTicketRoundRetriever.java (2 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketGenerator.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketSaver.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/domain/Ticket.java (3 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/domain/entity/TicketEntity.java (2 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/repository/TicketRepository.java (2 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticketround/core/repository/TicketRoundRepository.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/core/component/AdminTicketRetriever.java (1)
src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketSaver.java (1)
  • Component (13-30)
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)
🔇 Additional comments (13)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/core/component/AdminTicketRetriever.java (2)

11-14: LGTM! 컴포넌트 구조가 적절합니다.

생성자 주입을 사용한 표준 Spring 컴포넌트 패턴이 올바르게 적용되었습니다.


22-24: null 검증 불필요: COALESCE로 기본값 반환
sumTicketPriceByTicketTypeIdAndStatuses 쿼리에서 COALESCE(SUM(t.ticketPrice), 0)를 사용해 결과가 없을 때 0을 반환하므로 별도 null 처리 필요 없습니다.

Likely an incorrect or invalid review comment.

src/main/java/com/permitseoul/permitserver/domain/admin/ticketround/core/AdminTicketRoundRetriever.java (1)

19-19: Repository 메서드 변경 확인 완료

findByEventIdIn에서 findAllByEventIdIn으로의 메서드명 변경이 repository 계층과 일관되게 적용되었습니다.

src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketSaver.java (1)

18-29: LGTM! 엔티티 매핑 확장이 올바릅니다.

ticketPrice 필드가 도메인에서 엔티티로 올바르게 전달되고 있습니다. 매핑 로직이 일관성 있게 구현되었습니다.

src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/controller/AdminTicketController.java (1)

22-26: 메서드명 개선 완료

핸들러 메서드명이 getTicketRoundAndTicketTypeDetails로 더 명확하게 변경되었고, 파라미터에 final 수정자가 추가되었습니다.

src/main/java/com/permitseoul/permitserver/domain/ticket/core/domain/Ticket.java (2)

23-23: LGTM! 금액 필드 추가가 적절합니다.

BigDecimal 타입을 사용한 ticketPrice 필드 추가가 올바르며, 불변 객체 패턴이 유지되고 있습니다.


34-35: 엔티티 매핑 업데이트 확인 완료

fromEntity 메서드가 ticketPrice 필드를 올바르게 매핑하도록 업데이트되었습니다.

src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (2)

57-62: 빈 라운드 처리 로직 검증 필요

Line 60에서 ticketRounds.isEmpty() 체크 후 빈 응답을 반환하고 있습니다. 하지만 이는 정상적인 상황(행사에 아직 라운드가 생성되지 않음)일 수 있으므로, 빈 결과를 반환하는 것이 올바른 비즈니스 로직인지 확인하세요.

만약 라운드가 없으면 404 오류를 반환해야 한다면, 예외 처리를 추가해야 합니다.


113-133: null 안전성 검증 필요

parseTicketTypeInfo 메서드에서 adminTicketRetriever의 반환값이 null일 경우를 처리하지 않고 있습니다.

특히 Line 119의 soldAmount가 null이면 DTO factory 메서드에서 NPE가 발생합니다.

AdminTicketRetriever의 메서드들이 null을 반환하지 않는지 확인하세요. 필요시 방어 코드를 추가하세요:

final BigDecimal soldAmount = Optional.ofNullable(
    adminTicketRetriever.getSoldAmount(type.getTicketTypeId(), SOLD_STATUSES)
).orElse(BigDecimal.ZERO);
src/main/java/com/permitseoul/permitserver/domain/ticketround/core/repository/TicketRoundRepository.java (1)

17-19: 메서드 네이밍 및 추가 변경사항 확인 완료

  • findByEventIdInfindAllByEventIdIn 리네이밍은 Spring Data JPA의 명명 규칙과 일관성을 높입니다.
  • 새로 추가된 findAllByEventId 메서드는 단일 이벤트 ID로 조회하는 명확한 시그니처를 제공합니다.

두 메서드 모두 컬렉션 반환에 적합한 findAll 접두사를 사용하여 코드베이스 전반에 걸쳐 일관된 네이밍을 유지합니다.

src/main/java/com/permitseoul/permitserver/domain/ticket/core/repository/TicketRepository.java (1)

23-27: 카운트 메서드 구현 확인 완료

countByTicketTypeIdAndStatuscountByTicketTypeIdAndStatusIn 메서드는 Spring Data JPA의 파생 쿼리 명명 규칙을 올바르게 따르고 있으며, 타입별·상태별 티켓 집계에 적합한 시그니처를 제공합니다.

src/main/java/com/permitseoul/permitserver/domain/ticket/core/domain/entity/TicketEntity.java (2)

46-64: 생성자 및 팩토리 메서드 업데이트 확인 완료

ticketPrice 파라미터가 생성자와 create 팩토리 메서드에 올바르게 추가되었으며, 필드 할당이 정확하게 이루어지고 있습니다. 변경 사항은 기존 엔티티 생성 패턴과 일관성을 유지합니다.


43-44: 마이그레이션 파일 및 백필 전략 확인 필요
새로운 non-null ticketPrice 컬럼 추가로 인해 기존 티켓 레코드에 대한 DDL 및 백필 전략을 직접 확인하세요. 현재 .sql/.xml 마이그레이션 파일에서 해당 항목을 찾을 수 없습니다.

Comment on lines +64 to +82
public static TicketTypeInfo of(final long ticketTypeId,
final String ticketTypeName,
final BigDecimal ticketTypePrice,
final long ticketTypeSoldCount,
final long ticketTypeTotalCount,
final BigDecimal ticketTypeSoldAmount,
final long ticketTypeRefundCount,
final long ticketTypeUsedCount) {
return new TicketTypeInfo(
ticketTypeId,
ticketTypeName,
ticketTypePrice.longValue(),
ticketTypeSoldCount,
ticketTypeTotalCount,
ticketTypeSoldAmount.longValue(),
ticketTypeRefundCount,
ticketTypeUsedCount
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

null 파라미터 처리 검증 필요

Factory 메서드 TicketTypeInfo.of()BigDecimal 파라미터(ticketTypePrice, ticketTypeSoldAmount)에 대한 null 체크 없이 longValue()를 호출합니다 (lines 75, 78).

Null이 전달되면 NullPointerException이 발생합니다. 호출하는 곳(AdminTicketService)에서 null이 전달되지 않는지 확인하거나, 방어적으로 null 처리를 추가하세요.

다음 스크립트로 null이 전달될 가능성을 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: Verify that price and amount values are non-null before calling factory

# Search for calls to TicketTypeInfo.of
rg -A 15 'TicketTypeInfo\.of\(' --type=java

# Check AdminTicketRetriever methods for null return handling
ast-grep --pattern $'getSoldAmount($_, $_) {
  $$$
}'

Length of output: 4377


추가 null 가능성 확인을 위해 아래 스크립트를 실행해주세요:


🏁 Script executed:

#!/bin/bash
# 1. 호출 위치 및 변수 타입 확인
rg -n 'TicketTypeInfo.of' src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java
sed -n '1,200p' src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java

# 2. TicketType 엔티티 정의 확인
ticketTypeFile=$(rg -l 'class TicketType' src)
echo "TicketType file: $ticketTypeFile"
sed -n '1,200p' "$ticketTypeFile"

# 3. AdminTicketRetriever.getSoldAmount null 처리 확인
retrieverFile=$(rg -l 'class AdminTicketRetriever' src)
echo "AdminTicketRetriever file: $retrieverFile"
rg -n 'getSoldAmount' -n "$retrieverFile"
sed -n '1,200p' "$retrieverFile"

Length of output: 11621


TicketTypeInfo.of에서 BigDecimal null 체크 추가 필요
Factory 메서드(of) 내에서 ticketTypePrice·ticketTypeSoldAmount가 null일 경우 .longValue() 호출 시 NPE가 발생합니다. Objects.requireNonNull 또는 기본값(0L) 반환 로직을 추가하세요.

🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/res/TicketRoundAndTicketTypeRes.java
around lines 64 to 82, the TicketTypeInfo.of factory method calls .longValue()
on ticketTypePrice and ticketTypeSoldAmount without null checks which can throw
NPE; update the method to either use Objects.requireNonNull(...) with a clear
message for both BigDecimal parameters or guard them and substitute a default
BigDecimal.ZERO (returning 0L) when null, then call .longValue() on the non-null
value so the method never dereferences a null BigDecimal.

Comment on lines +17 to +19
public long getSoldCount(final long ticketTypeId, final List<TicketStatus> statuses) {
return ticketRepository.countByTicketTypeIdAndStatusIn(ticketTypeId, statuses);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

파라미터 null 체크 검증 필요

ticketTypeIdstatuses 파라미터에 대한 null 또는 빈 리스트 처리가 repository 계층에서 적절히 이루어지는지 확인하세요. 빈 statuses 리스트가 전달될 경우 예상치 못한 동작이 발생할 수 있습니다.

다음 스크립트로 repository 구현을 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: Verify TicketRepository method implementation and null handling

# Search for countByTicketTypeIdAndStatusIn method implementation
ast-grep --pattern $'countByTicketTypeIdAndStatusIn($_, $_) {
  $$$
}'

# Search for repository interface definition
rg -A 10 'interface TicketRepository' --type=java

Length of output: 1472


AdminTicketRetriever.getSoldCount: statuses null/빈 리스트 방어 로직 추가 필요
statuses가 null이거나 비어있을 때 예외를 던지거나 0을 반환하도록 방어 코드를 추가하세요. (ticketTypeId는 primitive long으로 null 불가능)

🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/core/component/AdminTicketRetriever.java
around lines 17 to 19, the method getSoldCount doesn't guard against statuses
being null or empty; update the method to check if statuses is null or
statuses.isEmpty() and return 0 in that case, otherwise call
ticketRepository.countByTicketTypeIdAndStatusIn(ticketTypeId, statuses); ensure
no NullPointerException is possible by performing this check before invoking the
repository.

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

🧹 Nitpick comments (1)
src/main/java/com/permitseoul/permitserver/domain/tickettype/core/component/TicketTypeRetriever.java (1)

36-39: 중복된 조회 메서드 정리 필요

이미 동일 역할의 findAllTicketTypeEntityByIds가 존재하는데 findAllByIds가 똑같은 구현을 다시 들고 있습니다. API가 이중으로 노출되면 유지보수 시 혼란과 분기 처리가 늘어납니다. 기존 메서드에 위임하거나 하나로 통합해서 중복을 제거해 주세요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 431972d and 3198cfa.

📒 Files selected for processing (5)
  • src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java (4 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/payment/api/service/PaymentService.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketGenerator.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/ticket/core/repository/TicketRepository.java (2 hunks)
  • src/main/java/com/permitseoul/permitserver/domain/tickettype/core/component/TicketTypeRetriever.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/permitseoul/permitserver/domain/payment/api/service/PaymentService.java (1)
src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketGenerator.java (1)
  • TicketGenerator (13-49)

Comment on lines +31 to +44
return IntStream.range(0, reservationTicket.getCount())
.mapToObj(i -> Ticket.builder()
.userId(userId)
.orderId(reservationTicket.getOrderId())
.ticketTypeId(reservationTicket.getTicketTypeId())
.eventId(reservation.getEventId())
.ticketCode(TicketOrCouponCodeGenerator.generateCode())
.status(TicketStatus.RESERVED)
.ticketPrice(
hasCoupon
? reservation.getTotalAmount() // ✅ 쿠폰 사용 시: 전체 예약 금액
: ticketTypeEntity.getTicketPrice() // ✅ 일반 결제 시: 티켓 단가
)
.build());
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

쿠폰 적용 시 티켓 가격 집계가 잘못됩니다

쿠폰이 적용된 예약에서 각 티켓에 reservation.getTotalAmount() 전체 금액을 그대로 넣고 있어, 동일 주문에서 여러 장의 티켓이 생성되면 매 티켓마다 전체 금액이 중복 저장됩니다. 예를 들어 총액 80,000원짜리 2매 예약은 각 티켓이 80,000원을 기록해 집계 시 160,000원으로 부풀어집니다. 이번에 추가한 통계 API의 매출/판매금액이 실제와 크게 달라지니, 티켓 단가에 할인 금액을 적절히 배분하거나 최소한 타입 단가·예약티켓 단가 등 실제 1매 기준 금액을 저장하도록 수정해 주세요.

🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketGenerator.java
around lines 31 to 44, the code sets ticketPrice to reservation.getTotalAmount()
for every ticket when a coupon is used, causing the full reservation amount to
be stored per ticket; change this so each Ticket stores the per-ticket amount
instead of the full reservation total — compute the per-ticket price by dividing
the reservation total (or the discounted reservation total) by
reservationTicket.getCount(), or otherwise prorate the discount across tickets,
ensure you use the correct numeric type/rounding (e.g., BigDecimal with a
defined rounding mode) and store that per-ticket value in ticketPrice so
aggregations reflect actual totals.

@sjk4618 sjk4618 merged commit 51ede5a into dev Oct 4, 2025
2 checks passed
@sjk4618 sjk4618 deleted the feat/#141 branch October 4, 2025 04:59
@sjk4618 sjk4618 changed the title feat: 행사 티켓 라운드+타입 전체 조회 API - #141 feat: admin 행사 티켓 라운드+타입 전체 조회 API - #141 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: 행사 티켓 라운드+타입 전체 조회 API

2 participants