-
Notifications
You must be signed in to change notification settings - Fork 2
feat: admin 행사 티켓 라운드+타입 전체 조회 API - #141 #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughSummary by CodeRabbit
Walkthrough이 PR은 이벤트별 티켓 라운드와 각 라운드의 티켓 유형별 집계(판매/환불/사용 수량 및 금액)를 반환하는 새 API와 DTO·서비스·리트리버를 추가하고, 티켓 도메인에 ticketPrice를 도입해 생성·저장·집계 로직 및 관련 저장소/컴포넌트 시그니처를 변경합니다. Changes
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
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 |
There was a problem hiding this 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})를 우선하지만, 명확성을 위해 다음 중 하나를 권장합니다:
- 경로 접두사 분리:
/api/admin/events/{eventId}/ticket-rounds- 쿼리 파라미터 사용:
/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()를 호출하여BigDecimal을long으로 변환하고 있습니다.잠재적 문제:
- 소수점 이하 값이 있는 경우 정밀도 손실 발생
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
📒 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: 메서드 네이밍 및 추가 변경사항 확인 완료
findByEventIdIn→findAllByEventIdIn리네이밍은 Spring Data JPA의 명명 규칙과 일관성을 높입니다.- 새로 추가된
findAllByEventId메서드는 단일 이벤트 ID로 조회하는 명확한 시그니처를 제공합니다.두 메서드 모두 컬렉션 반환에 적합한
findAll접두사를 사용하여 코드베이스 전반에 걸쳐 일관된 네이밍을 유지합니다.src/main/java/com/permitseoul/permitserver/domain/ticket/core/repository/TicketRepository.java (1)
23-27: 카운트 메서드 구현 확인 완료
countByTicketTypeIdAndStatus와countByTicketTypeIdAndStatusIn메서드는 Spring Data JPA의 파생 쿼리 명명 규칙을 올바르게 따르고 있으며, 타입별·상태별 티켓 집계에 적합한 시그니처를 제공합니다.src/main/java/com/permitseoul/permitserver/domain/ticket/core/domain/entity/TicketEntity.java (2)
46-64: 생성자 및 팩토리 메서드 업데이트 확인 완료
ticketPrice파라미터가 생성자와create팩토리 메서드에 올바르게 추가되었으며, 필드 할당이 정확하게 이루어지고 있습니다. 변경 사항은 기존 엔티티 생성 패턴과 일관성을 유지합니다.
43-44: 마이그레이션 파일 및 백필 전략 확인 필요
새로운 non-nullticketPrice컬럼 추가로 인해 기존 티켓 레코드에 대한 DDL 및 백필 전략을 직접 확인하세요. 현재.sql/.xml마이그레이션 파일에서 해당 항목을 찾을 수 없습니다.
| 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 | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
...in/java/com/permitseoul/permitserver/domain/admin/ticket/api/service/AdminTicketService.java
Show resolved
Hide resolved
| public long getSoldCount(final long ticketTypeId, final List<TicketStatus> statuses) { | ||
| return ticketRepository.countByTicketTypeIdAndStatusIn(ticketTypeId, statuses); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
파라미터 null 체크 검증 필요
ticketTypeId와 statuses 파라미터에 대한 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=javaLength 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.
src/main/java/com/permitseoul/permitserver/domain/ticket/core/component/TicketGenerator.java
Outdated
Show resolved
Hide resolved
src/main/java/com/permitseoul/permitserver/domain/ticket/core/repository/TicketRepository.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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
📒 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)
| 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()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쿠폰 적용 시 티켓 가격 집계가 잘못됩니다
쿠폰이 적용된 예약에서 각 티켓에 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.
🔥Pull requests
⛳️ 작업한 브랜치
👷 작업한 내용
🚨 참고 사항