Conversation
|
Caution Review failedThe pull request is closed. Walkthrough이 PR은 티켓 발급·검증·소비 기능을 도메인으로 추가합니다. REST 컨트롤러, 서비스 구현, JPA 엔티티와 리포지토리(비관적 락 포함), DTO들, 매퍼, 토큰/QR 유틸리티, 및 티켓 전용 에러 코드가 새로 도입됩니다. (약 50단어) Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as TicketController
participant Service as TicketService
participant TokenGen as TicketTokenGenerator
participant Repo as TicketRepository
participant DB as Database
Client->>Controller: POST /dev/issue (type, targetId, userId)
Controller->>Service: issueTicket(type, targetId, userId)
Service->>TokenGen: generate()
TokenGen-->>Service: token
Service->>Repo: save(Ticket entity)
Repo->>DB: INSERT ticket
DB-->>Repo: persisted ticket
Repo-->>Service: saved entity
Service-->>Controller: TicketDetailResponse
Controller-->>Client: BaseResponse<TicketDetailResponse>
sequenceDiagram
participant Client
participant Controller as TicketController
participant Service as TicketService
participant Repo as TicketRepository
participant DB as Database
participant Mapper as TicketMapper
Client->>Controller: POST /admin/verify (token)
Controller->>Service: verifyTicket(request)
Service->>Repo: findByTokenForUpdate(token)
Note over Repo: PESSIMISTIC_WRITE
Repo->>DB: SELECT ... FOR UPDATE
alt found
DB-->>Repo: Ticket
Repo-->>Service: Ticket
Service->>Mapper: toVerifyFound(ticket)
Mapper-->>Service: VerifyTicketResponse(valid=true,...)
else not found
DB-->>Repo: empty
Repo-->>Service: empty
Service->>Mapper: toVerifyNotFound()
Mapper-->>Service: VerifyTicketResponse(valid=false)
end
Service-->>Controller: VerifyTicketResponse
Controller-->>Client: BaseResponse<VerifyTicketResponse>
sequenceDiagram
participant Client
participant Controller as TicketController
participant Service as TicketService
participant Repo as TicketRepository
participant DB as Database
participant Mapper as TicketMapper
Client->>Controller: POST /admin/consume (token)
Controller->>Service: consumeTicket(request)
Service->>Repo: findByToken(token)
Repo->>DB: SELECT ticket
alt found
DB-->>Repo: Ticket
Repo-->>Service: Ticket
Service->>Service: ticket.consume(now) (idempotent)
Service->>Repo: save(ticket)
Repo->>DB: UPDATE ticket
Service->>Mapper: toConsume(ticket, consumed)
Mapper-->>Service: ConsumeTicketResponse
else not found
Repo-->>Service: empty -> throw not-found
end
Service-->>Controller: ConsumeTicketResponse
Controller-->>Client: BaseResponse<ConsumeTicketResponse>
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (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.
Actionable comments posted: 5
🧹 Nitpick comments (9)
src/main/java/com/sku/refit/domain/ticket/util/TicketQrPayloadFactory.java (2)
11-12: QR 기본 URL을 설정 파일로 외부화하는 것을 권장합니다.현재 QR 페이로드 기본 URL과 버전이 하드코딩되어 있습니다. 환경별(개발/스테이징/프로덕션) URL 관리와 유연성을 위해
application.properties또는application.yml로 외부화하는 것을 고려해보세요.예시:
@Component public class TicketQrPayloadFactory { @Value("${ticket.qr.base-url}") private String qrBaseUrl; @Value("${ticket.qr.version}") private String qrVersion; public String create(String token) { return qrBaseUrl + "?v=" + qrVersion + "&token=" + token; } }
22-24: 토큰 파라미터 검증을 추가하는 것을 고려해보세요.
token이 null이거나 빈 문자열인 경우를 방어적으로 체크하면 더 안전합니다. TicketTokenGenerator가 항상 유효한 토큰을 생성하지만, 이 메서드가 다른 곳에서도 호출될 수 있습니다.public String create(String token) { + if (token == null || token.isBlank()) { + throw new IllegalArgumentException("Token은 필수입니다."); + } return QR_BASE_URL + "?v=" + QR_VERSION + "&token=" + token; }src/main/java/com/sku/refit/domain/ticket/service/TicketService.java (1)
25-96: 서비스 메서드 네이밍을 컨트롤러와 일치시키는 게 좋습니다.
getMyTicketsUsedEvents()(Line 86) vs 컨트롤러의getMyUsedEventTickets()로 표현이 달라 추후 탐색/유지보수 시 헷갈릴 수 있어요. 한쪽으로 통일 추천합니다.src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java (1)
31-86:qrPayloadFactory.create(ticket.getToken())중복 호출은 지역 변수로 정리 추천.현재
toDetail/toMyItem/toVerifyFound/toConsume에서 동일 표현이 반복됩니다. 토큰/페이로드를 지역 변수로 빼면 가독성과 디버깅이 좋아집니다.public TicketDetailResponse toDetail(Ticket ticket) { + final String token = ticket.getToken(); + final String qrPayload = qrPayloadFactory.create(token); return TicketDetailResponse.builder() .ticketId(ticket.getId()) .type(ticket.getType()) .targetId(ticket.getTargetId()) - .token(ticket.getToken()) - .qrPayload(qrPayloadFactory.create(ticket.getToken())) + .token(token) + .qrPayload(qrPayload) .issuedAt(ticket.getCreatedAt()) .usedAt(ticket.getUsedAt()) .build(); }src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
18-43: 요청 DTO에 최소한의 범위 검증(@Positive/@SiZe) 추가를 권장합니다.
targetId는 음수/0을 막고,token은 과도한 길이 입력을 제한하는 게 안전합니다.import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; public static class IssueTicketRequest { @NotNull private TicketType type; - @NotNull private Long targetId; + @NotNull @Positive private Long targetId; ... - private Long userId; + @Positive private Long userId; } public static class ConsumeTicketRequest { - @NotBlank private String token; + @NotBlank @Size(max = 256) private String token; } public static class VerifyTicketRequest { - @NotBlank private String token; + @NotBlank @Size(max = 256) private String token; }src/main/java/com/sku/refit/domain/ticket/exception/TicketErrorCode.java (1)
16-27: 에러 코드 포맷을 일관되게 맞추는 것을 추천합니다.
TICKET_NOT_FOUND("TICKET0010", ...)만 자릿수가 튀어서 운영/로그 필터링 시 혼동될 수 있어요. 정책(예: 3자리/4자리)을 정하고 통일하는 게 좋습니다.src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java (3)
12-13: 와일드카드 임포트 대신 명시적 임포트 사용 권장와일드카드 임포트(
.*)는 어떤 클래스가 실제로 사용되는지 파악하기 어렵게 만들고, 향후 동일한 이름의 클래스가 추가될 경우 충돌이 발생할 수 있습니다.-import com.sku.refit.domain.ticket.dto.request.TicketRequest.*; -import com.sku.refit.domain.ticket.dto.response.TicketResponse.*; +import com.sku.refit.domain.ticket.dto.request.TicketRequest.ConsumeTicketRequest; +import com.sku.refit.domain.ticket.dto.request.TicketRequest.IssueTicketRequest; +import com.sku.refit.domain.ticket.dto.request.TicketRequest.VerifyTicketRequest; +import com.sku.refit.domain.ticket.dto.response.TicketResponse.ConsumeTicketResponse; +import com.sku.refit.domain.ticket.dto.response.TicketResponse.MyTicketItemResponse; +import com.sku.refit.domain.ticket.dto.response.TicketResponse.TicketDetailResponse; +import com.sku.refit.domain.ticket.dto.response.TicketResponse.VerifyTicketResponse;
186-196: DTO의 Bean Validation과 중복되는 검증 로직
IssueTicketRequest의@NotNull어노테이션과VerifyTicketRequest/ConsumeTicketRequest의@NotBlank어노테이션이 이미 컨트롤러 레벨에서@Valid로 검증된다면, 이 수동 검증은 중복될 수 있습니다.단, 서비스가 컨트롤러 외 다른 곳에서 직접 호출될 수 있다면 방어적 프로그래밍으로서 유지해도 좋습니다.
65-75: 반복되는 예외 처리 패턴
catch (CustomException e) { throw e; } catch (Exception e) { ... }패턴이 여러 메서드에서 반복됩니다. 현재 구조도 동작하지만, 향후 유지보수를 위해 공통 유틸리티 메서드나 AOP를 고려해볼 수 있습니다.예시:
private <T> T executeWithErrorHandling(Supplier<T> action, String context, TicketErrorCode errorCode) { try { return action.get(); } catch (CustomException e) { throw e; } catch (Exception e) { log.error("[TICKET] {} - failed", context, e); throw new CustomException(errorCode); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
src/main/java/com/sku/refit/domain/ticket/controller/TicketController.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/controller/TicketControllerImpl.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/entity/Ticket.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/entity/TicketStatus.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/entity/TicketType.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/exception/TicketErrorCode.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/repository/TicketRepository.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/service/TicketService.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/util/TicketQrPayloadFactory.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/util/TicketTokenGenerator.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java (4)
src/main/java/com/sku/refit/domain/ticket/util/TicketQrPayloadFactory.java (1)
Component(8-25)src/main/java/com/sku/refit/domain/ticket/util/TicketTokenGenerator.java (1)
Component(11-21)src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-44)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
src/main/java/com/sku/refit/domain/ticket/controller/TicketControllerImpl.java (2)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-44)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (4)
Getter(14-26)Getter(28-42)Getter(44-56)Getter(59-71)
src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java (2)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-44)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
src/main/java/com/sku/refit/domain/ticket/util/TicketQrPayloadFactory.java (2)
src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java (1)
Component(15-87)src/main/java/com/sku/refit/domain/ticket/util/TicketTokenGenerator.java (1)
Component(11-21)
src/main/java/com/sku/refit/domain/ticket/service/TicketService.java (2)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-44)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
src/main/java/com/sku/refit/domain/ticket/util/TicketTokenGenerator.java (2)
src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java (1)
Component(15-87)src/main/java/com/sku/refit/domain/ticket/util/TicketQrPayloadFactory.java (1)
Component(8-25)
src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (3)
Getter(15-29)Getter(31-36)Getter(38-43)
src/main/java/com/sku/refit/domain/ticket/controller/TicketController.java (2)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-44)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (17)
src/main/java/com/sku/refit/domain/ticket/entity/TicketType.java (1)
8-14: 티켓 타입 정의가 명확합니다.두 가지 티켓 타입(EVENT, CLOTH)이 명확하게 정의되어 있고, Swagger 문서화를 위한 설명도 적절합니다.
src/main/java/com/sku/refit/domain/ticket/util/TicketTokenGenerator.java (1)
11-21: 안전한 토큰 생성 로직입니다.SecureRandom을 사용하여 256비트(32바이트) 토큰을 생성하고, URL-safe Base64 인코딩을 적용한 구현이 적절합니다. 토큰 강도와 URL 호환성이 모두 확보되었습니다.
src/main/java/com/sku/refit/domain/ticket/repository/TicketRepository.java (1)
14-22: Repository 메서드 정의가 적절합니다.쿼리 메서드명이 Spring Data JPA 규칙을 준수하고 있으며, Ticket 엔티티에 정의된 인덱스들(user_id, type+target_id, token, used_at)과 잘 매칭되어 쿼리 성능이 최적화되어 있습니다.
src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (4)
14-26: 발급 응답 DTO가 적절하게 구성되어 있습니다.티켓 발급 시 사용자가 QR 코드를 생성/저장할 수 있도록
token과qrPayload모두를 포함하고 있습니다. PR 목표에 명시된 대로 사용자가 QR을 제시할 수 있도록 설계되었습니다.
28-42: 검증 응답에서 토큰을 노출하지 않는 것이 적절합니다.관리자의 티켓 검증 시 사용되는 응답으로,
valid와used상태 정보만 포함하고 민감한token필드는 제외되어 있어 보안상 적절합니다.
44-56: 체크인 응답 구조가 명확합니다.소비(체크인) 처리 결과를 나타내는
consumed필드와 함께 필요한 티켓 정보만 포함하고 있습니다. 토큰을 포함하지 않은 것도 적절합니다.
58-71: 사용자 조회용 DTO가 요구사항을 잘 반영하고 있습니다.사용자가 본인의 티켓 목록을 조회할 때 QR 코드를 다시 확인할 수 있도록
token과qrPayload를 포함하고 있습니다. 주석에 명시된 대로 EVENT와 CLOTH 타입별 조회 정책 차이를 지원합니다.src/main/java/com/sku/refit/domain/ticket/entity/Ticket.java (4)
19-26: 인덱스 구성이 쿼리 패턴에 최적화되어 있습니다.정의된 4개의 인덱스가 TicketRepository의 쿼리 메서드들과 잘 매칭됩니다:
idx_ticket_user: 사용자별 티켓 조회idx_ticket_type_target: 타입+대상 조합 조회idx_ticket_token: 토큰 기반 검증 (unique 제약조건과 함께)idx_ticket_used_at: 사용 이력 필터링
47-49: 토큰 필드의 길이와 제약조건이 적절합니다.URL-safe Base64로 인코딩된 32바이트 토큰(약 43자)을 저장하기에 128자는 충분하며,
unique제약조건으로 토큰 중복을 방지하고 있습니다.
64-70: 멱등성이 보장된 소비 로직입니다.
consume()메서드가 이미 사용된 티켓을 재처리하지 않도록 구현되어 있어, PR 목표에 명시된 "멱등 처리로 중복 처리 방지"를 달성하고 있습니다. QR 코드 재스캔 시에도 안전합니다.
72-86: 도메인 검증 로직이 명확합니다.필수 필드들(type, targetId, userId, token)에 대한 검증을 수행하고, 위반 시 명확한 한글 메시지와 함께
IllegalStateException을 발생시킵니다. 도메인 무결성을 보장하는 좋은 방어 코드입니다.src/main/java/com/sku/refit/domain/ticket/controller/TicketControllerImpl.java (1)
21-54: 권한 검증이 전역 보안 설정에서 구현되어 있습니다. 추가 검토 필요.보안 설정을 확인한 결과, 권한 검증은
TicketController의 애노테이션이 아닌SecurityConfig의 전역 규칙에서 구현되어 있습니다 (lines 128-131):
/admin/*→ADMIN역할 필수/dev/*→DEVELOPER역할 필수- 그 외
/my/*→ 인증된 사용자만 접근 가능다만 다음을 확인해주세요:
issueTicket은 현재[개발자]권한이 필요한데, PR 요구사항과 맞는지 확인 필요/my/events,/my/cloth는 현재 인증된 모든 사용자가 접근 가능한데, 본인 티켓만 조회하도록 제한하는 로직이 서비스 계층에 구현되어 있는지 확인 필요src/main/java/com/sku/refit/domain/ticket/service/TicketService.java (1)
11-24: 인터페이스 분리/역할 정의가 명확합니다.발급/검증/체크인(멱등)/조회로 책임이 잘 나뉘어 있고, “verify는 사용 처리 없음”을 Javadoc에 명확히 박아둔 점이 좋아요.
src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java (1)
71-74:toVerifyNotFound()응답 최소 필드만 내려주는 방향은 적절합니다.
valid=false일 때 다른 필드를 비워두는 설계가 깔끔하고,used=false기본값도 무난합니다.src/main/java/com/sku/refit/domain/ticket/exception/TicketErrorCode.java (1)
16-31: 에러 케이스 분류와 HttpStatus 매핑은 무난합니다.BAD_REQUEST / NOT_FOUND / CONFLICT / 500 계열로 구분이 잘 되어 있어요.
src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java (2)
82-99: LGTM!티켓이 없는 경우 예외를 던지지 않고
toVerifyNotFound를 반환하는 설계가 검증 API의 목적에 적합합니다.
142-180: LGTM!EVENT 티켓은 사용된 것만, CLOTH 티켓은 전체를 조회하는 정책이 명확하게 구현되어 있습니다. 스트림 처리와 에러 핸들링도 일관성 있게 적용되었습니다.
src/main/java/com/sku/refit/domain/ticket/controller/TicketController.java
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/ticket/controller/TicketController.java
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/ticket/entity/TicketStatus.java
Outdated
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java
Outdated
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java (1)
110-129: 동시 consume 경쟁 조건:findByToken()대신findByTokenForUpdate()로 잠금 후 사용 처리 필요
현재 구현은 동시에 두 요청이 들어오면 둘 다alreadyUsed=false로 판단하고 둘 다consumed=true를 반환할 수 있습니다.Ticket ticket = ticketRepository - .findByToken(request.getToken()) + .findByTokenForUpdate(request.getToken()) .orElseThrow(() -> new CustomException(TicketErrorCode.TICKET_NOT_FOUND));
🧹 Nitpick comments (3)
src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java (1)
26-80: QR payload 생성 중복 호출은 로컬 변수로 한 번만 계산해도 충분
동일한ticket.getToken()으로qrPayloadFactory.create(...)를 여러 빌더에서 반복 호출하고 있어, 가독성 차원에서 한 번만 계산해도 됩니다.public TicketDetailResponse toDetail(Ticket ticket) { + String token = ticket.getToken(); + String qrPayload = qrPayloadFactory.create(token); return TicketDetailResponse.builder() .ticketId(ticket.getId()) .type(ticket.getType()) .targetId(ticket.getTargetId()) - .token(ticket.getToken()) - .qrPayload(qrPayloadFactory.create(ticket.getToken())) + .token(token) + .qrPayload(qrPayload) .issuedAt(ticket.getCreatedAt()) .usedAt(ticket.getUsedAt()) .build(); }src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java (2)
57-80: 토큰 유니크 충돌/재시도 전략(및 DB 유니크 제약) 고려 필요
SecureRandom기반이라 충돌 가능성은 매우 낮지만, 운영 안정성을 위해Ticket.token에 유니크 제약(및 인덱스) + 저장 시 충돌 발생하면 제한된 횟수로 재발급/재시도하는 방식을 고려해볼 만합니다.
191-195:validateIssueRequest()는 현재 코드에서 호출되지 않음
이 PR 상태만 보면 미사용 private 메서드라, 실제로 필요 없으면 제거하거나issueTicket(...)에서 사용하도록 정리해도 좋겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/main/java/com/sku/refit/domain/ticket/controller/TicketController.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/controller/TicketControllerImpl.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/repository/TicketRepository.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/service/TicketService.java(1 hunks)src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/com/sku/refit/domain/ticket/controller/TicketController.java
- src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java
🧰 Additional context used
🧬 Code graph analysis (4)
src/main/java/com/sku/refit/domain/ticket/mapper/TicketMapper.java (3)
src/main/java/com/sku/refit/domain/ticket/util/TicketQrPayloadFactory.java (1)
Component(8-25)src/main/java/com/sku/refit/domain/ticket/util/TicketTokenGenerator.java (1)
Component(11-21)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
src/main/java/com/sku/refit/domain/ticket/service/TicketService.java (2)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-38)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
src/main/java/com/sku/refit/domain/ticket/controller/TicketControllerImpl.java (2)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-38)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
src/main/java/com/sku/refit/domain/ticket/service/TicketServiceImpl.java (2)
src/main/java/com/sku/refit/domain/ticket/dto/request/TicketRequest.java (1)
TicketRequest(13-38)src/main/java/com/sku/refit/domain/ticket/dto/response/TicketResponse.java (1)
TicketResponse(12-72)
🔇 Additional comments (1)
src/main/java/com/sku/refit/domain/ticket/repository/TicketRepository.java (1)
21-31:findByTokenForUpdate()도입은 좋은데, “consume” 경로에서 실제로 사용되는지 확인 필요
리포지토리 자체는 깔끔합니다. 다만 현재 PR 목적(체크인 멱등/중복 스캔 방지)을 달성하려면 서비스의 consume 로직이 이 메서드를 사용해야 효과가 있습니다.
✨ 새로운 기능
🛠 개발 상세
Ticket엔티티로 EVENT / CLOTH 티켓을 통합 관리type + targetId + userId + token기반 구조https://api.refitlab.site/ticket?v=1&token={token}issueTicket)verifyTicket) – 사용 여부 확인만 수행consumeTicket) – 멱등 처리🔗 관련 문서 / 이슈
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.