-
Notifications
You must be signed in to change notification settings - Fork 2
feat: door용 티켓 큐알 카메라 인증 api 구현 및 스태프 권한 추가 - #206 #207
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
✏️ Tip: You can customize this high-level summary in your review settings. Walkthrough스태프 권한(STAFF) 추가, 스태프 대상 카메라 기반 티켓 확인 API(게스트·일반) 도입, QR 코드 생성 시 이벤트명 렌더링 기능 추가 및 관련 시큐리티 규칙(/api/staff/**) 적용. Changes
Sequence DiagramsequenceDiagram
autonumber
participant Staff as 스태프(카메라)
participant Controller as StaffController<br/>(Ticket/Guest)
participant Service as Service<br/>(Ticket/Guest)
participant Repo as TicketRepository
participant TypeRepo as TicketTypeRepository
Staff->>Controller: POST /api/staff/*/door/confirm { ticketCode }
Controller->>Service: confirm*ByStaffCamera(ticketCode)
Service->>Repo: findByCode(ticketCode)
Repo-->>Service: Ticket / null
alt 티켓 없음
Service-->>Controller: NotFoundException
Controller-->>Staff: 404
else 티켓 존재
Service->>TypeRepo: findTicketTypeById(typeId)
TypeRepo-->>Service: TicketType
Service->>Service: validateTicketDate(TicketType)
alt 유효
Service->>Repo: updateStatus(USED)
Repo-->>Service: OK
Service-->>Controller: 완료
Controller-->>Staff: 200 OK
else 유효하지 않음
Service-->>Controller: ValidationError
Controller-->>Staff: 4xx
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (1)📓 Common learnings🔇 Additional comments (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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/permitseoul/permitserver/domain/ticket/api/service/TicketService.java (1)
127-163: 코드 중복 제거 필요
confirmTicketByStaffCode와confirmTicketByStaffCamera메서드가 거의 동일한 로직을 가지고 있습니다. 유일한 차이점은 체크 코드 검증(lines 135-136) 여부뿐입니다. 이는 DRY 원칙을 위반하며 유지보수 부담을 증가시킵니다.🔎 공통 로직을 추출한 리팩토링 제안
+ private void confirmTicketByStaff(final String ticketCode, final boolean verifyCheckCode, final String checkCodeFromTicket) { + try { + final TicketEntity ticketEntity = ticketRetriever.findTicketEntityByTicketCode(ticketCode); + verifyTicketStatus(ticketEntity.getStatus()); + + final TicketType ticketType = findTicketTypeById(ticketEntity.getTicketTypeId()); + verifyTicketDate(ticketType.getTicketStartAt(), ticketType.getTicketEndAt()); + + if (verifyCheckCode) { + final Event event = findEventById(ticketEntity.getEventId()); + verifyTicketCheckCode(event.getTicketCheckCode(), checkCodeFromTicket); + } + + ticketUpdater.updateTicketStatus(ticketEntity, TicketStatus.USED); + } catch (TicketNotFoundException e) { + throw new NotFoundTicketException(ErrorCode.NOT_FOUND_TICKET); + } catch (TicketTypeNotfoundException e) { + throw new NotFoundTicketException(ErrorCode.NOT_FOUND_TICKET_TYPE); + } catch (EventNotfoundException e) { + throw new NotFoundTicketException(ErrorCode.NOT_FOUND_EVENT); + } + } + @Transactional public void confirmTicketByStaffCode(final String ticketCode, final String checkCodeFromTicket) { - try { - final TicketEntity ticketEntity = ticketRetriever.findTicketEntityByTicketCode(ticketCode); - verifyTicketStatus(ticketEntity.getStatus()); - - final TicketType ticketType = findTicketTypeById(ticketEntity.getTicketTypeId()); - verifyTicketDate(ticketType.getTicketStartAt(), ticketType.getTicketEndAt()); - - final Event event = findEventById(ticketEntity.getEventId()); - verifyTicketCheckCode(event.getTicketCheckCode(), checkCodeFromTicket); - - ticketUpdater.updateTicketStatus(ticketEntity, TicketStatus.USED); - } catch (TicketNotFoundException e) { - throw new NotFoundTicketException(ErrorCode.NOT_FOUND_TICKET); - } catch (TicketTypeNotfoundException e) { - throw new NotFoundTicketException(ErrorCode.NOT_FOUND_TICKET_TYPE); - } catch (EventNotfoundException e) { - throw new NotFoundTicketException(ErrorCode.NOT_FOUND_EVENT); - } + confirmTicketByStaff(ticketCode, true, checkCodeFromTicket); } @Transactional public void confirmTicketByStaffCamera(final String ticketCode) { - try { - final TicketEntity ticketEntity = ticketRetriever.findTicketEntityByTicketCode(ticketCode); - verifyTicketStatus(ticketEntity.getStatus()); - - final TicketType ticketType = findTicketTypeById(ticketEntity.getTicketTypeId()); - verifyTicketDate(ticketType.getTicketStartAt(), ticketType.getTicketEndAt()); - - ticketUpdater.updateTicketStatus(ticketEntity, TicketStatus.USED); - } catch (TicketNotFoundException e) { - throw new NotFoundTicketException(ErrorCode.NOT_FOUND_TICKET); - } catch (TicketTypeNotfoundException e) { - throw new NotFoundTicketException(ErrorCode.NOT_FOUND_TICKET_TYPE); - } + confirmTicketByStaff(ticketCode, false, null); }
🧹 Nitpick comments (2)
src/main/java/com/permitseoul/permitserver/domain/admin/util/QrCodeUtil.java (1)
63-96: 폰트 설정 하드코딩 개선 권장현재 폰트 이름("SansSerif")과 크기(15)가 하드코딩되어 있습니다. 시스템에 해당 폰트가 없을 경우 대체 폰트가 사용되지만, 더 명시적인 폰트 fallback 처리나 설정 가능한 상수로 관리하는 것이 좋습니다.
🔎 제안하는 개선사항
클래스 상단에 폰트 관련 상수를 추가:
+ private static final String FONT_NAME = "SansSerif"; + private static final int FONT_SIZE = 15; + private static final int FONT_STYLE = Font.PLAIN;그리고 사용처를 다음과 같이 변경:
- g.setFont(new Font("SansSerif", Font.PLAIN, 15)); + g.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE));추가로, 폰트 가용성 체크를 고려할 수 있습니다:
private static Font getAvailableFont() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fonts = ge.getAvailableFontFamilyNames(); if (Arrays.asList(fonts).contains(FONT_NAME)) { return new Font(FONT_NAME, FONT_STYLE, FONT_SIZE); } return new Font(Font.SANS_SERIF, FONT_STYLE, FONT_SIZE); // fallback }src/main/java/com/permitseoul/permitserver/domain/ticket/api/service/TicketService.java (1)
268-272:findTicketTypeAndVerifyTicketDate메서드는 사용되지 않으므로 제거하세요이 헬퍼 메서드는 현재 사용되지 않는 데드 코드입니다. 리팩토링 후 132-133줄과 171-172줄에서 개별 호출(
findTicketTypeById()+verifyTicketDate())로 변경되었으며, 코드베이스 어디에도 이 메서드를 호출하는 곳이 없습니다. 제거를 권장합니다.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
src/main/java/com/permitseoul/permitserver/domain/admin/guestticket/api/service/AdminGuestTicketService.javasrc/main/java/com/permitseoul/permitserver/domain/admin/util/QrCodeUtil.javasrc/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestController.javasrc/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestStaffController.javasrc/main/java/com/permitseoul/permitserver/domain/guest/api/dto/req/GuestTicketConfirmByCameraRequest.javasrc/main/java/com/permitseoul/permitserver/domain/guest/api/service/GuestService.javasrc/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.javasrc/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketStaffController.javasrc/main/java/com/permitseoul/permitserver/domain/ticket/api/dto/req/TicketConfirmByCameraRequest.javasrc/main/java/com/permitseoul/permitserver/domain/ticket/api/service/TicketService.javasrc/main/java/com/permitseoul/permitserver/domain/user/core/domain/UserRole.javasrc/main/java/com/permitseoul/permitserver/global/config/SecurityConfig.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: sjk4618
Repo: PERMIT-SEOUL/permit-server PR: 48
File: src/main/java/com/permitseoul/permitserver/domain/guest/core/domain/entity/GuestEntity.java:31-37
Timestamp: 2025-07-15T09:37:32.765Z
Learning: sjk4618 prefers to implement factory methods or public constructors for entities when they are actually needed, rather than creating them proactively.
🧬 Code graph analysis (2)
src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketStaffController.java (3)
src/main/java/com/permitseoul/permitserver/global/response/ApiResponseUtil.java (1)
ApiResponseUtil(7-29)src/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestStaffController.java (1)
RestController(13-28)src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java (1)
RestController(17-55)
src/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestStaffController.java (4)
src/main/java/com/permitseoul/permitserver/global/response/ApiResponseUtil.java (1)
ApiResponseUtil(7-29)src/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestController.java (1)
RestController(14-36)src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java (1)
RestController(17-55)src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketStaffController.java (1)
RestController(16-30)
🔇 Additional comments (14)
src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java (1)
44-44: LGTM!메서드명 변경을 통해 코드 기반 확인과 카메라 기반 확인을 명확히 구분하고 있습니다.
src/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestController.java (1)
33-33: LGTM!메서드명 변경을 통해 코드 기반 확인과 카메라 기반 확인을 명확히 구분하고 있습니다.
src/main/java/com/permitseoul/permitserver/global/config/SecurityConfig.java (1)
63-65: 스태프 엔드포인트 보안 설정이 올바르게 구성되었습니다.staffURIList의
/api/staff/**패턴과 보안 규칙(hasAnyRole(STAFF, ADMIN))이 올바르게 설정되었으며, 권한 규칙의 순서도 적절합니다.GuestStaffController와 TicketStaffController 모두
/api/staff기본 경로를 사용하여/api/staff/**보안 패턴과 일치하므로, 스태프 엔드포인트 보안이 제대로 구현되었습니다.src/main/java/com/permitseoul/permitserver/domain/admin/guestticket/api/service/AdminGuestTicketService.java (1)
45-45: QR 코드에 이벤트명 추가 로직이 올바르게 구현되었습니다.변경사항을 검증한 결과,
QrCodeUtil.generatePng메서드가eventName파라미터를 올바르게 처리하고 있습니다:
- eventName이 null이거나 빈 문자열일 경우 원본 QR 이미지를 반환
- eventName이 제공될 경우
[ eventName ]형식으로 QR 코드 상단에 텍스트 영역을 추가하여 렌더링- 텍스트 길이가 초과하는 경우 말줄임표로 처리하는 안전한 구현
src/main/java/com/permitseoul/permitserver/domain/guest/api/dto/req/GuestTicketConfirmByCameraRequest.java (1)
5-8: LGTM!필드 검증이 적절하게 적용되어 있습니다.
src/main/java/com/permitseoul/permitserver/domain/guest/api/service/GuestService.java (2)
60-70: 이벤트 검증 누락 확인 필요
confirmGuestTicketByStaffCheckCode와 달리 이 메서드는 이벤트 존재 여부를 검증하지 않습니다. 카메라 기반 인증에서는 이벤트 검증이 필요 없는지, 아니면 의도적으로 생략된 것인지 확인이 필요합니다.만약 이벤트가 삭제된 후에도 티켓 사용이 가능해야 한다면 현재 구현이 맞지만, 그렇지 않다면 일관성을 위해 이벤트 검증을 추가하는 것이 좋습니다.
44-44: 메서드명 변경이 완전하게 적용됨기존 메서드명(
confirmGuestTicketByStaff)에 대한 참조가 모두 제거되었고, 새로운 메서드명(confirmGuestTicketByStaffCheckCode)으로 변경되었습니다. 추가 조치가 필요 없습니다.src/main/java/com/permitseoul/permitserver/domain/admin/util/QrCodeUtil.java (2)
98-110: 텍스트 줄임 로직이 올바르게 구현되었습니다긴 이벤트명을 적절히 줄여서 표시하는 로직이 정확합니다. FontMetrics를 활용한 너비 계산도 적절합니다.
36-36: 메서드 시그니처 변경이 올바르게 반영됨
generatePng메서드에 추가된eventName매개변수는 유일한 호출처인AdminGuestTicketService.java(67줄)에서 올바르게 반영되어 있습니다. 추가 업데이트는 필요하지 않습니다.src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketStaffController.java (1)
16-29: 구현이 올바르게 되어 있습니다컨트롤러 구조가
GuestStaffController와 일관성 있게 구현되었으며,TicketService.confirmTicketByStaffCamera메서드도@Transactional어노테이션과 함께 올바르게 구현되어 있습니다.src/main/java/com/permitseoul/permitserver/domain/ticket/api/service/TicketService.java (2)
127-127: 메서드명 변경 승인코드 기반 확인과 카메라 기반 확인을 명확히 구분하기 위한 적절한 네이밍 개선입니다.
148-163: 카메라 기반 확인 로직 승인카메라를 통한 QR 스캔 방식의 티켓 확인 로직이 올바르게 구현되었습니다. 체크 코드 검증을 생략하고 티켓 상태와 날짜만 검증하는 것이 적절합니다.
src/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestStaffController.java (2)
13-28: 컨트롤러 구조 승인스프링 MVC 패턴을 올바르게 따르고 있으며,
TicketStaffController와 일관된 구조로 구현되었습니다. 요청 검증 및 응답 처리가 적절합니다.
13-20: 보안 설정이 올바르게 구성되어 있습니다.SecurityConfig에서
/api/staff/**패턴에 대해.hasAnyRole(UserRole.STAFF.name(), UserRole.ADMIN.name())로 설정되어 있어, 이 엔드포인트는 URL 기반 접근 제어로 STAFF 또는 ADMIN 역할을 요구하도록 보호되어 있습니다. JWT 인증 필터를 통해 인증도 강제되므로 추가 보안 조치는 필요하지 않습니다.
| } | ||
|
|
||
| //도어용 게스트 티켓 스텝 확인 api | ||
| //도어용 게스트 티켓 스텝 코드 확인 api |
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.
주석의 용어를 수정하세요.
"스텝"은 "스태프"(staff)의 오타로 보입니다. 일관된 용어 사용을 위해 수정이 필요합니다.
🔎 제안하는 수정
- //도어용 게스트 티켓 스텝 코드 확인 api
+ //도어용 게스트 티켓 스태프 코드 확인 api📝 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.
| //도어용 게스트 티켓 스텝 코드 확인 api | |
| //도어용 게스트 티켓 스태프 코드 확인 api |
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/guest/api/controller/GuestController.java
around line 28, the comment uses "스텝" which is a typo for "스태프"; update the
comment to use the correct term ("스태프") and ensure the full comment reads
clearly (e.g., "도어용 게스트 티켓 스태프 코드 확인 API") maintaining capitalization/spacing
consistent with project comment style.
| } | ||
|
|
||
| //도어용 유저 티켓 스텝 확인 api | ||
| //도어용 유저 티켓 스텝 코드 확인 api |
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.
주석의 용어를 수정하세요.
"스텝"은 "스태프"(staff)의 오타로 보입니다. 일관된 용어 사용을 위해 수정이 필요합니다.
🔎 제안하는 수정
- //도어용 유저 티켓 스텝 코드 확인 api
+ //도어용 유저 티켓 스태프 코드 확인 api📝 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.
| //도어용 유저 티켓 스텝 코드 확인 api | |
| //도어용 유저 티켓 스태프 코드 확인 api |
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java
around line 39, update the comment "//도어용 유저 티켓 스텝 코드 확인 api" to correct the
typo: replace "스텝" with "스태프" (staff) so the comment reads something like "//도어용
유저 티켓 스태프 코드 확인 api" to maintain consistent terminology across the codebase.
...ava/com/permitseoul/permitserver/domain/ticket/api/dto/req/TicketConfirmByCameraRequest.java
Show resolved
Hide resolved
| USER, | ||
| ADMIN, | ||
|
|
||
| STAFF |
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
🏁 Script executed:
#!/bin/bash
# Search for UserRole.STAFF usage and role assignment logic
rg -n --type=java 'UserRole\.STAFF|UserRole\.valueOf.*STAFF' -C 2Repository: PERMIT-SEOUL/permit-server
Length of output: 867
🏁 Script executed:
#!/bin/bash
# Search for role assignment logic in user-related files
rg -n --type=java 'setRole|withRole|role\s*=' --glob='**/user/**/*.java' -C 2Repository: PERMIT-SEOUL/permit-server
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Look for User entity to understand role field
fd -e java --path '*/user/**' User | head -20 | xargs -I {} sh -c 'echo "=== {} ===" && head -50 {}'Repository: PERMIT-SEOUL/permit-server
Length of output: 303
🏁 Script executed:
#!/bin/bash
# Search for database migration files
fd -e sql -e yml | grep -i migrat | head -20Repository: PERMIT-SEOUL/permit-server
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Check SecurityConfig for STAFF role usage
rg -n --type=java -C 5 'STAFF'Repository: PERMIT-SEOUL/permit-server
Length of output: 2531
🏁 Script executed:
#!/bin/bash
# Find User entity definition
find . -type f -name "User.java" | xargs -I {} sh -c 'echo "=== {} ===" && cat -n {}'Repository: PERMIT-SEOUL/permit-server
Length of output: 1328
🏁 Script executed:
#!/bin/bash
# Search for role field assignments in entity or service
rg -n --type=java 'role\s*[:=]|@Enumerated' -C 3 | head -100Repository: PERMIT-SEOUL/permit-server
Length of output: 6738
🏁 Script executed:
#!/bin/bash
# Look for UserRepository and role-related methods
rg -n --type=java 'class.*UserRepository|interface.*UserRepository' -A 20Repository: PERMIT-SEOUL/permit-server
Length of output: 1542
🏁 Script executed:
#!/bin/bash
# Search for any SQL files or initialization data
find . -type f \( -name "*.sql" -o -name "data.sql" -o -name "*init*.sql" \) -exec echo "=== {} ===" \; -exec cat {} \;Repository: PERMIT-SEOUL/permit-server
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search for UserService or service classes handling user creation/updates
rg -n --type=java 'class.*UserService|interface.*UserService' -A 30Repository: PERMIT-SEOUL/permit-server
Length of output: 3926
🏁 Script executed:
#!/bin/bash
# Search for methods setting userRole in the entire codebase
rg -n --type=java 'setUserRole|userRole\s*=' -C 3Repository: PERMIT-SEOUL/permit-server
Length of output: 2859
🏁 Script executed:
#!/bin/bash
# Check UserEntity for any builder or constructor that might handle role assignment
cat -n src/main/java/com/permitseoul/permitserver/domain/user/core/domain/entity/UserEntity.java | head -80Repository: PERMIT-SEOUL/permit-server
Length of output: 2995
🏁 Script executed:
#!/bin/bash
# Search more broadly for migration-related files
find . -type f \( -name "*.sql" -o -name "*.yml" -o -name "*migration*" -o -name "*Flyway*" \) | grep -v node_modules | grep -v ".git"Repository: PERMIT-SEOUL/permit-server
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Check for any Setter methods or update methods for userRole in UserEntity
rg -n --type=java 'void.*[Uu]pdate|void.*[Ss]et' src/main/java/com/permitseoul/permitserver/domain/user/Repository: PERMIT-SEOUL/permit-server
Length of output: 411
🏁 Script executed:
#!/bin/bash
# Look for any command or use case classes that might handle role assignment
rg -n --type=java 'class.*Command|class.*UseCase' src/main/java/com/permitseoul/permitserver/domain/user/ -A 5Repository: PERMIT-SEOUL/permit-server
Length of output: 52
기존 사용자에게 STAFF 역할을 할당하는 메커니즘이 필요합니다.
STAFF 역할이 올바르게 정의되고 SecurityConfig에서 적절히 사용되고 있으며, UserEntity의 스키마도 이미 userRole 필드를 지원합니다. 그러나 신규 사용자 생성 시에만 역할을 설정할 수 있고, 기존 사용자에게 STAFF 역할을 할당하거나 업데이트하는 메서드가 없습니다. UserEntity와 UserService에 역할 업데이트 기능(예: updateUserRole())을 추가하고, 필요하다면 해당 관리 API를 구현해주세요.
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/user/core/domain/UserRole.java
around line 6, the STAFF enum constant exists but there is no mechanism to
assign or update STAFF for existing users; add a role-update flow: add an
updateUserRole(UserId, UserRole) method in UserService that loads the
UserEntity, validates permission/state, sets the userRole field on the entity
and persists the change (use repository.save or entity manager as appropriate),
and add a corresponding method on UserEntity (e.g., setUserRole(UserRole)) if
missing; finally expose a secured admin API endpoint in the controller to call
UserService.updateUserRole, validate inputs, authorize only permitted roles, and
return the updated user DTO.
🔥Pull requests
⛳️ 작업한 브랜치
👷 작업한 내용
🚨 참고 사항