[feat] 일반 구성원 추가 기능 구현#578
Conversation
|
Warning Review limit reached
More reviews will be available in 6 minutes and 13 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
Walkthrough관리자가 일반 구성원을 특정 기수에 추가할 수 있는 ChangesAdmin Member Semester 생성 기능
Sequence Diagram(s)sequenceDiagram
participant Client as 관리자 클라이언트
participant Controller as AdminMemberSemesterController
participant UseCase as AdminMemberUseCase
participant SemesterInquiry as SemesterInquiryUsecase
participant TeamService as AdminMemberTeamService
participant MemberService as AdminMemberService
participant ActivityService as AdminMemberActivityService
rect rgba(100, 149, 237, 0.5)
note over Client,Controller: POST /admin/members/semester
Client->>Controller: createAdminMemberSemester(request)
Controller->>UseCase: createMemberSemester(request)
end
rect rgba(144, 238, 144, 0.5)
note over UseCase,SemesterInquiry: 기수 존재 검증
UseCase->>SemesterInquiry: getSemester(semesterId)
SemesterInquiry-->>UseCase: SemesterResponse
end
rect rgba(255, 200, 100, 0.5)
note over UseCase,TeamService: 팀 소속 검증 (teamId != null 시)
UseCase->>TeamService: getTeamIdsBySemesterId(semesterId)
TeamService-->>UseCase: List teamIds
UseCase-->>UseCase: teamId 포함 여부 확인
end
rect rgba(255, 160, 122, 0.5)
note over UseCase,ActivityService: Member 조회/생성 및 활동 저장
UseCase->>MemberService: findOrCreateMember(request)
MemberService-->>UseCase: memberId
UseCase->>ActivityService: createMemberSemesterActivity(request, memberId)
ActivityService-->>UseCase: void
end
UseCase-->>Controller: void
Controller-->>Client: 200 OK
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
Test Results376 tests 376 ✅ 14s ⏱️ Results for commit 1d07d98. ♻️ This comment has been updated with latest results. |
📊테스트 커버리지
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/test/java/org/ject/support/admin/member/controller/AdminMemberSemesterControllerTest.java (1)
101-125: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win유효성 실패 응답의
code필드도 함께 검증해주세요.Line 105, Line 121은 HTTP 400만 확인하고 있어 에러 응답 포맷 회귀를 놓칠 수 있습니다.
$.code까지 단언하면GlobalExceptionHandler계약을 더 안정적으로 고정할 수 있습니다.테스트 보강 예시
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import org.ject.support.common.exception.GlobalErrorCode; ... mockMvc.perform(post("/admin/members/semester") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(GlobalErrorCode.METHOD_VALIDATION_FAILED.getCode())); ... mockMvc.perform(post("/admin/members/semester") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(GlobalErrorCode.METHOD_VALIDATION_FAILED.getCode()));Based on learnings, 이 저장소의 예외 응답 코드는
$.code경로로 검증해야 합니다 ($.data.code아님).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/org/ject/support/admin/member/controller/AdminMemberSemesterControllerTest.java` around lines 101 - 125, The test methods around the validation failure cases (with post request to /admin/members/semester endpoint at lines 101-107 and 113-125) are only verifying the HTTP 400 status but not validating the response code field. Add an additional assertion using andExpect(jsonPath("$.code")...) before each andExpect(status().isBadRequest()) to verify that the response includes the proper error code, ensuring the GlobalExceptionHandler contract is properly validated and preventing future regressions in error response format.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/org/ject/support/admin/member/dto/request/CreateMemberSemesterRequest.java`:
- Around line 23-33: The request DTO CreateMemberSemesterRequest is missing
validation annotations that match the constraints defined in the Member and
MemberActivity entities. Add size/length validations to the name field to
enforce the maximum length constraint, add a pattern validation to the
phoneNumber field to match the expected format constraint, and add size/length
validation to the memo field at line 51 to match the entity's maximum length
constraint. This ensures invalid data is rejected at the request boundary rather
than causing exceptions during entity persistence.
In
`@src/main/java/org/ject/support/admin/member/service/AdminMemberActivityService.java`:
- Around line 21-44: The createMemberSemesterActivity method has a race
condition where validateDuplicateSemesterActivity check and
memberActivityRepository.save can be bypassed by concurrent requests that both
pass validation. Keep the service-level validateDuplicateSemesterActivity check
in place, but additionally handle DataIntegrityViolationException from the
memberActivityRepository.save call and map it to throw MemberException with
ALREADY_EXIST_MEMBER_SEMESTER_ACTIVITY. This requires adding a unique constraint
at the database level for the semester activity combination to make the
duplicate prevention atomically safe.
In `@src/main/java/org/ject/support/admin/member/service/AdminMemberService.java`:
- Around line 17-35: The findOrCreateMember method has a race condition where
concurrent requests with the same email can both pass the findByEmail check and
create duplicate members. Add a database unique constraint on the email column
for the Member entity to prevent duplicates at the database level. Then modify
the createMember method to catch the constraint violation exception
(DataIntegrityViolationException) that occurs when a concurrent request already
saved a member with that email. When this exception is caught, query the
memberRepository again with findByEmail to get the member that was created by
the concurrent request and return that member's ID instead of propagating the
exception.
In
`@src/main/java/org/ject/support/domain/member/repository/MemberRepository.java`:
- Line 10: The DataIntegrityViolationException thrown when attempting to save a
member with a duplicate email (especially when that email belongs to a
soft-deleted record) is not being caught and handled properly. Either catch the
DataIntegrityViolationException in the createMember() method within
AdminMemberService and convert it to a MemberException with
MemberErrorCode.DUPLICATE_EMAIL, or add a dedicated exception handler for
DataIntegrityViolationException in GlobalExceptionHandler that returns an
appropriate error response. The MemberErrorCode.DUPLICATE_EMAIL constant already
exists and should be utilized to provide a proper error message instead of
exposing the raw 500 error to users.
---
Nitpick comments:
In
`@src/test/java/org/ject/support/admin/member/controller/AdminMemberSemesterControllerTest.java`:
- Around line 101-125: The test methods around the validation failure cases
(with post request to /admin/members/semester endpoint at lines 101-107 and
113-125) are only verifying the HTTP 400 status but not validating the response
code field. Add an additional assertion using andExpect(jsonPath("$.code")...)
before each andExpect(status().isBadRequest()) to verify that the response
includes the proper error code, ensuring the GlobalExceptionHandler contract is
properly validated and preventing future regressions in error response format.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c387ac18-1891-4558-83ba-b18c4b018cb5
📒 Files selected for processing (32)
src/main/java/org/ject/support/admin/member/controller/AdminMemberSemesterApiSpec.javasrc/main/java/org/ject/support/admin/member/controller/AdminMemberSemesterController.javasrc/main/java/org/ject/support/admin/member/dto/request/CreateMemberSemesterRequest.javasrc/main/java/org/ject/support/admin/member/service/AdminMemberActivityService.javasrc/main/java/org/ject/support/admin/member/service/AdminMemberService.javasrc/main/java/org/ject/support/admin/member/service/AdminMemberTeamService.javasrc/main/java/org/ject/support/admin/member/service/AdminMemberUseCase.javasrc/main/java/org/ject/support/domain/member/entity/Member.javasrc/main/java/org/ject/support/domain/member/entity/MemberActivity.javasrc/main/java/org/ject/support/domain/member/entity/MemberSemester.javasrc/main/java/org/ject/support/domain/member/exception/MemberErrorCode.javasrc/main/java/org/ject/support/domain/member/repository/MemberActivityRepository.javasrc/main/java/org/ject/support/domain/member/repository/MemberRepository.javasrc/main/java/org/ject/support/domain/member/repository/TeamRepository.javasrc/main/java/org/ject/support/domain/recruit/service/SemesterInquiryService.javasrc/main/java/org/ject/support/domain/recruit/service/SemesterInquiryUsecase.javasrc/test/java/org/ject/support/admin/member/controller/AdminMemberSemesterControllerTest.javasrc/test/java/org/ject/support/admin/member/service/AdminMemberActivityServiceTest.javasrc/test/java/org/ject/support/admin/member/service/AdminMemberServiceTest.javasrc/test/java/org/ject/support/admin/member/service/AdminMemberTeamServiceTest.javasrc/test/java/org/ject/support/admin/member/service/AdminMemberUseCaseTest.javasrc/test/java/org/ject/support/common/data/redis/resilience/CacheFallbackIntegrationTest.javasrc/test/java/org/ject/support/domain/file/controller/FileControllerTest.javasrc/test/java/org/ject/support/domain/member/entity/MemberActivityTest.javasrc/test/java/org/ject/support/domain/member/entity/MemberTest.javasrc/test/java/org/ject/support/domain/member/fixture/MemberFixture.javasrc/test/java/org/ject/support/domain/member/fixture/SemesterActivityFixture.javasrc/test/java/org/ject/support/domain/member/repository/MemberActivityRepositoryTest.javasrc/test/java/org/ject/support/domain/member/repository/MemberRepositoryTest.javasrc/test/java/org/ject/support/domain/member/repository/TeamRepositoryTest.javasrc/test/java/org/ject/support/domain/recruit/controller/QuestionControllerTest.javasrc/test/java/org/ject/support/domain/recruit/service/SemesterInquiryServiceTest.java
연관 이슈
Closes #518
작업 내용
호출 구조
UseCase는 구성원 추가 흐름을 조립하고, 실제 조회,생성,저장 책임은 도메인별 Service에 분리했습니다.
MemberActivity는 일반 구성원 활동의 Aggregate Root로서 MemberSemester를 생성하며, 두 엔티티는 식별 관계와 Cascade를 통해 함께 저장됩니다.
API
일반 구성원 추가
POST /admin/members/semesterSummary by CodeRabbit
릴리스 노트
New Features
Tests