Skip to content

Conversation

@Sangwook02
Copy link
Member

@Sangwook02 Sangwook02 commented Sep 19, 2025

🌱 관련 이슈

📌 작업 내용 및 특이사항

  • dto의 경우 EventManualApplyRequest를 재활용 해도 되지만 이름이 너무 길어질 것 같아 분리했습니다.

📝 참고사항

📚 기타

Summary by CodeRabbit

  • 신기능
    • 관리자용 현장 참가 처리 엔드포인트 추가(POST /admin/event-participations/join/onsite). 요청 시 이벤트 ID와 참가자 정보 검증, 성공 시 200 OK 응답.
  • 버그 수정
    • 중복 참가 방지 검증을 도입해 이미 등록된 참가자의 현장 참가 처리를 차단.
  • 테스트
    • 중복 참가 시 예외가 발생하는지 검증하는 테스트 추가 및 관련 호출부 업데이트.

@Sangwook02 Sangwook02 self-assigned this Sep 19, 2025
@Sangwook02 Sangwook02 requested a review from a team as a code owner September 19, 2025 03:07
@coderabbitai
Copy link

coderabbitai bot commented Sep 19, 2025

📝 Walkthrough

Walkthrough

관리자용 현장 합류(온사이트 조인) 기능을 추가했다. 컨트롤러에 POST /admin/event-participations/join/onsite 엔드포인트를 신설하고, 서비스 계층과 도메인 서비스에 온사이트 처리 흐름을 연결했다. 중복 참여 여부 검증을 도메인 서비스 시그니처에 boolean 인자로 추가했고, DTO(EventOnsiteJoinRequest)와 해당 테스트를 반영했다.

Changes

Cohort / File(s) Summary
Admin API 추가
src/main/java/.../api/AdminEventParticipationController.java
관리자 온사이트 조인 엔드포인트(POST /admin/event-participations/join/onsite) 추가, EventOnsiteJoinRequest 수신, 서비스에 위임, 200 OK 반환.
애플리케이션 서비스 확장
src/main/java/.../application/EventParticipationService.java
joinOnsite(EventOnsiteJoinRequest) 공개 메서드 추가: 이벤트 조회, 중복 확인, Participant/Member 조회, 도메인 서비스 호출, 저장 및 로깅.
도메인 서비스 시그니처 변경/검증 추가
src/main/java/.../domain/service/EventParticipationDomainService.java
joinOnsite(...) 시그니처에 boolean isEventParticipationDuplicate 추가, 초기에 중복 검증 실행, 나머지 결제 상태 계산/생성 로직 유지.
요청 DTO 신설
src/main/java/.../dto/request/EventOnsiteJoinRequest.java
record EventOnsiteJoinRequest(Long eventId, Participant participant) 추가, @NotNull, @Positive 등 검증 애노테이션 적용.
도메인 테스트 업데이트
src/test/java/.../EventParticipationDomainServiceTest.java
변경된 4-인자 joinOnsite로 호출 수정, 중복 시 예외 발생 테스트 추가, 성공 케이스는 isEventParticipationDuplicate=false로 검증.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Admin
  participant API as AdminEventParticipationController
  participant App as EventParticipationService
  participant Dom as EventParticipationDomainService
  participant Repo as EventParticipationRepository

  Admin->>API: POST /admin/event-participations/join/onsite<br/>EventOnsiteJoinRequest
  API->>App: joinOnsite(request)
  App->>Repo: 이벤트 조회/참여 중복 조회
  App->>Dom: joinOnsite(participant, member, event, isDuplicate)
  Dom-->>Dom: validateEventParticipationDuplicate(isDuplicate)
  Dom-->>App: EventParticipation
  App->>Repo: 참여 저장
  App-->>API: 완료
  API-->>Admin: 200 OK
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

✨ feature

Poem

토끼는 두근, 입장 도장 쿵!
현장 줄 앞에서 귀를 쫑긋—중복은 노노, 한 번만 쏙.
새 길 열린 엔드포인트, 문 열고 톡.
이벤트 빛나는 밤, 로그에 콩콩 박수.
오늘도 합류 완료! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed PR 제목 "feat: 이벤트 뒤풀이 현장등록 API 구현"은 변경의 핵심인 뒤풀이(애프터파티) 현장 등록 API 추가를 명확하게 요약하고 있으며 접두사 "feat:"로 변경 유형도 적절히 표시합니다; 제목은 간결하고 불필요한 잡음 없이 팀원이 히스토리를 빠르게 파악하기에 충분합니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/1242-after-party-join-onsite-api

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.

@github-actions github-actions bot changed the title feat: 이벤트 뒤풀이 현장등록 api 구현 feat: 이벤트 뒤풀이 현장등록 API 구현 Sep 19, 2025
Copy link
Member

@uwoobeat uwoobeat left a comment

Choose a reason for hiding this comment

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

lgtm

@github-actions
Copy link

Job Summary for Gradle

Check Style and Test to Develop :: build-test
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
gdsc check 8.5 Build Scan published

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

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/gdschongik/gdsc/domain/event/domain/service/EventParticipationDomainService.java (1)

200-206: 뒤풀이 비활성화 행사에서의 현장등록 금지 누락

현장등록은 뒤풀이가 존재할 때만 의미가 있습니다. event.afterPartyExists() 검증이 누락되어 있어 비즈니스 규칙 위반 가능성이 있습니다.

적용 제안(diff):

     public EventParticipation joinOnsite(
             Participant participant, @Nullable Member member, Event event, boolean isEventParticipationDuplicate) {
         validateEventParticipationDuplicate(isEventParticipationDuplicate);
+        if (!event.afterPartyExists()) {
+            throw new CustomException(EVENT_NOT_APPLICABLE_AFTER_PARTY_DISABLED);
+        }
         PaymentStatus prePaymentStatus = PaymentStatus.getInitialPrePaymentStatus(event);
         PaymentStatus postPaymentStatus = PaymentStatus.getInitialPostPaymentStatus(event);
 
         return EventParticipation.createOnsite(participant, member, prePaymentStatus, postPaymentStatus, event);
     }
🧹 Nitpick comments (6)
src/main/java/com/gdschongik/gdsc/domain/event/application/EventParticipationService.java (1)

357-360: 로그 키 네이밍 정정: memberStudentId → studentId

회원 미매핑 케이스에서도 사용되는 값이므로 키명을 일반화하는 편이 명확합니다.

적용 제안(diff):

-        log.info(
-                "[EventParticipationService] 뒤풀이 현장등록: eventId={}, memberStudentId={}",
-                event.getId(),
-                participant.getStudentId());
+        log.info(
+                "[EventParticipationService] 뒤풀이 현장등록: eventId={}, studentId={}",
+                event.getId(),
+                participant.getStudentId());
src/main/java/com/gdschongik/gdsc/domain/event/api/AdminEventParticipationController.java (2)

155-162: 응답 코드 201 Created 권장 및 API 설명 보강

리소스 생성(참여 생성)에 해당하므로 200 OK 대신 201 Created가 더 적합합니다. 또한 실제 도메인 결과는 신청 상태는 NOT_APPLIED, 참석 상태는 ATTENDED이므로 설명을 명확히 적어 주세요.

적용 제안(diff):

-    @Operation(
-            summary = "뒤풀이 현장 신청",
-            description = "관리자가 참여자의 정보를 바탕으로 뒤풀이를 현장에서 수동으로 신청 처리합니다. 뒤풀이 신청 상태가 참석으로 처리됩니다.")
+    @Operation(
+            summary = "뒤풀이 현장 신청",
+            description = "관리자가 참여자의 정보를 바탕으로 뒤풀이를 현장에서 수동으로 신청 처리합니다. (afterPartyApplicationStatus=NOT_APPLIED, afterPartyAttendanceStatus=ATTENDED)")
     @PostMapping("/join/onsite")
     public ResponseEntity<Void> joinOnsite(@Valid @RequestBody EventOnsiteJoinRequest request) {
         eventParticipationService.joinOnsite(request);
-        return ResponseEntity.ok().build();
+        return ResponseEntity.status(HttpStatus.CREATED).build();
     }

컨트롤러 상단에 아래 import 추가가 필요합니다:

import org.springframework.http.HttpStatus;

155-162: 경로 네이밍 일관성 제안

기존 뒤풀이 API들이 /after-party/** 네임스페이스를 사용하므로 @PostMapping("/after-party/join/onsite")와 같이 정렬하면 검색/권한 부여에 유리합니다. 변경은 호환성 영향이 있으니 신중히 결정하세요.

src/main/java/com/gdschongik/gdsc/domain/event/dto/request/EventOnsiteJoinRequest.java (1)

7-7: 중첩 객체 유효성 검사 보강(@Valid) 또는 별도 DTO 도입

Participant에 Bean Validation이 있다면 @Valid를 추가해 중첩 검증을 활성화하세요. 없다면 API 계층용 ParticipantRequest DTO를 별도로 두는 것을 권장합니다(도메인 타입 직접 노출 최소화).

적용 제안(diff):

-import jakarta.validation.constraints.NotNull;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
@@
-public record EventOnsiteJoinRequest(@NotNull @Positive Long eventId, @NotNull Participant participant) {}
+public record EventOnsiteJoinRequest(
+        @NotNull @Positive Long eventId,
+        @NotNull @Valid Participant participant) {}
src/main/java/com/gdschongik/gdsc/domain/event/domain/service/EventParticipationDomainService.java (1)

200-206: 정책 의도 주석/Javadoc으로 명시

온사이트는 정회원 전용 검증을 수행하지 않는 정책이라면 메서드 Javadoc에 의도를 남겨 혼선을 줄이세요.

적용 제안(주석 추가):

/**
 * 뒤풀이 현장등록 확정 참여 메서드.
 * 주의: 정회원 전용 검증은 applyOnline에서만 수행하며, 온사이트/수동 등록은 관리자 오버라이드로 간주하여 검증하지 않습니다.
 */
src/test/java/com/gdschongik/gdsc/domain/event/domain/EventParticipationDomainServiceTest.java (1)

541-552: 비즈니스 규칙 테스트 보강: 뒤풀이 비활성화 행사에서 실패 케이스 추가

도메인에서 afterPartyExists 검증을 추가하면, 비활성화 행사에서의 온사이트 등록 실패 테스트를 함께 추가하세요.

추가 테스트 예시:

@Test
void 뒤풀이가_없는_행사에서는_현장등록이_실패한다() {
    // given
    Participant participant = Participant.of(NAME, STUDENT_ID, PHONE_NUMBER);
    Event eventWithoutAfterParty = fixtureHelper.createEventWithoutAfterParty(1L);
    boolean isEventParticipationDuplicate = false;

    // when & then
    assertThatThrownBy(() -> domainService.joinOnsite(participant, null, eventWithoutAfterParty, isEventParticipationDuplicate))
            .isInstanceOf(CustomException.class)
            .hasMessageContaining(EVENT_NOT_APPLICABLE_AFTER_PARTY_DISABLED.getMessage());
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03e81fe and c8cc7fc.

📒 Files selected for processing (5)
  • src/main/java/com/gdschongik/gdsc/domain/event/api/AdminEventParticipationController.java (2 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/event/application/EventParticipationService.java (2 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/event/domain/service/EventParticipationDomainService.java (1 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/event/dto/request/EventOnsiteJoinRequest.java (1 hunks)
  • src/test/java/com/gdschongik/gdsc/domain/event/domain/EventParticipationDomainServiceTest.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-03T16:49:19.668Z
Learnt from: kckc0608
PR: gdg-hongik-univ/gdsc-server#1180
File: src/main/java/com/gdschongik/gdsc/domain/event/dto/request/AfterPartyUpdateTarget.java:3-7
Timestamp: 2025-09-03T16:49:19.668Z
Learning: AfterPartyUpdateTarget enum은 서비스 계층에서도 사용되므로 dto.request 패키지가 아닌 도메인 패키지에 위치해야 한다는 아키텍처 관점이 있다.

Applied to files:

  • src/main/java/com/gdschongik/gdsc/domain/event/application/EventParticipationService.java
⏰ 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-test
🔇 Additional comments (4)
src/main/java/com/gdschongik/gdsc/domain/event/application/EventParticipationService.java (2)

20-20: 신규 DTO import 적절

신규 온사이트 조인 요청 DTO import 추가 문제 없습니다.


349-351: 정회원 전용 이벤트 정책 확인 필요

도메인 joinOnsite는 정회원 전용 검증을 수행하지 않습니다(관리자 오버라이드 의도일 수 있음). 정책적으로 허용인지 확인 부탁드립니다. 필요 시 서비스/도메인 한쪽에 명시적 검증 또는 주석으로 의도를 남기세요.

src/main/java/com/gdschongik/gdsc/domain/event/api/AdminEventParticipationController.java (1)

9-9: 신규 DTO import 적절

컨트롤러에서 요청 DTO import 추가 문제 없습니다.

src/test/java/com/gdschongik/gdsc/domain/event/domain/EventParticipationDomainServiceTest.java (1)

526-531: 온사이트 성공 케이스 커버리지 추가 적절

중복 플래그 추가 반영 및 정상 플로우 검증이 명확합니다.

Comment on lines +343 to +351
Event event =
eventRepository.findById(request.eventId()).orElseThrow(() -> new CustomException(EVENT_NOT_FOUND));

boolean isEventParticipationDuplicate = eventParticipationRepository.existsByEventAndParticipantStudentId(
event, request.participant().getStudentId());
Participant participant = request.participant();
Member memberByParticipant =
memberRepository.findByStudentId(participant.getStudentId()).orElse(null);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

뒤풀이 비활성화 행사에서의 현장등록을 사전에 차단하세요

현재 온사이트 조인은 행사에 뒤풀이가 없더라도 진행될 수 있습니다. 상태 일관성(ATTENDED 설정) 보장을 위해 뒤풀이 비활성화 이벤트에서는 예외를 던지도록 서비스 레벨에서 검증을 추가하는 것이 안전합니다. 기존 메서드에서 사용 중인 검증 유틸을 재사용하세요.

적용 제안(diff):

         Event event =
                 eventRepository.findById(request.eventId()).orElseThrow(() -> new CustomException(EVENT_NOT_FOUND));
 
+        // 뒤풀이가 없는 행사에서는 현장등록 불가
+        validateEventEnabledForAfterParty(event);
+
         boolean isEventParticipationDuplicate = eventParticipationRepository.existsByEventAndParticipantStudentId(
                 event, request.participant().getStudentId());

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/com/gdschongik/gdsc/domain/event/application/EventParticipationService.java
around lines 343 to 351, add a validation right after loading the Event to
prevent onsite participation when the event's after-party is disabled: call the
existing validation utility method used elsewhere in this service (e.g., the
method that enforces on-site join constraints) or throw a
CustomException(EVENT_AFTERPARTY_DISABLED or appropriate error) if the event
indicates the after-party is not enabled; place this check before checking
duplicates or resolving Member so the method aborts early for disallowed onsite
registrations.

Comment on lines +346 to +356
boolean isEventParticipationDuplicate = eventParticipationRepository.existsByEventAndParticipantStudentId(
event, request.participant().getStudentId());
Participant participant = request.participant();
Member memberByParticipant =
memberRepository.findByStudentId(participant.getStudentId()).orElse(null);

EventParticipation eventParticipation = eventParticipationDomainService.joinOnsite(
participant, memberByParticipant, event, isEventParticipationDuplicate);

eventParticipationRepository.save(eventParticipation);

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

🧩 Analysis chain

중복 체크 경쟁 조건 대비: DB 유니크 제약 및 예외 변환 권장

exists → save 사이 경쟁 조건으로 중복이 저장될 수 있습니다. (동시 요청) JPA/DB 레벨 유니크 제약(예: event_id, participant_student_id) 추가 및 DataIntegrityViolationException → CustomException(PARTICIPATION_DUPLICATE) 변환을 권장합니다.

레포지토리 전역에서 유니크 제약 존재 여부를 확인하는 스크립트:


🏁 Script executed:

#!/usr/bin/env bash
# EventParticipation 엔티티 및 테이블 유니크 제약 존재 여부 조사
rg -nP --type=java -C3 '(?s)@Entity\b.*?class\s+EventParticipation\b'
rg -nP --type=java -C3 '@Table\('
rg -nP --type=java -C2 '@UniqueConstraint\('
rg -nP --type=sql -C2 'unique|constraint' | sed -n '1,200p'

Length of output: 18076


DataIntegrityViolationException 처리 로직 추가
EventParticipation 엔티티에는 이미 @Table(..., uniqueConstraints = @UniqueConstraint(columnNames = {"event_id","student_id"})) 제약이 정의되어 있습니다. 동시 요청 시 발생 가능한 중복 저장을 예방하려면 eventParticipationRepository.save(...) 호출부에서 DataIntegrityViolationException을 잡아 CustomException(PARTICIPATION_DUPLICATE)으로 변환하는 로직을 추가하세요.

🤖 Prompt for AI Agents
In
src/main/java/com/gdschongik/gdsc/domain/event/application/EventParticipationService.java
around lines 346 to 356, wrap the
eventParticipationRepository.save(eventParticipation) call in a try/catch that
catches org.springframework.dao.DataIntegrityViolationException and converts it
to the existing CustomException with error code PARTICIPATION_DUPLICATE; keep
current duplicate-check logic but handle database-constraint race conditions by
catching the exception and throwing new CustomException(PARTICIPATION_DUPLICATE)
so concurrent inserts yield the proper application-level error.

Copy link
Member

@kckc0608 kckc0608 left a comment

Choose a reason for hiding this comment

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

lgtm

@Sangwook02 Sangwook02 merged commit 6b78702 into develop Sep 19, 2025
4 checks passed
@Sangwook02 Sangwook02 deleted the feature/1242-after-party-join-onsite-api branch September 19, 2025 05:52
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.

✨ 이벤트 뒤풀이 현장등록 API 구현

4 participants