Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package org.ject.support.admin.member.dto.request;

import java.util.List;

import org.ject.support.domain.member.ActivityStatus;
import org.ject.support.domain.member.CareerDetails;
import org.ject.support.domain.member.JobFamily;
Expand Down Expand Up @@ -29,16 +27,17 @@ public record MemberSemesterSearchCondition(
// 기수
@Positive
Long semesterId,
// 직군 (다중 선택)
List<JobFamily> jobFamilies,
// 모집 단위 (다중 선택)
List<RecruitTypeDetail> recruitTypeDetails,
// 신분 (다중 선택)
List<CareerDetails> careerDetails,
// 팀 (다중 선택)
List<@Positive Long> teamIds,
// 활동 상태 (다중 선택)
List<ActivityStatus> statuses
// 직군
JobFamily jobFamily,
// 모집 단위
RecruitTypeDetail recruitTypeDetail,
// 신분
CareerDetails careerDetails,
// 팀
@Positive
Long teamId,
// 활동 상태
ActivityStatus status
) {
//size가 없으면 기본 30개
public int getSizeOrDefault() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public class AdminMemberService {
/*
(2026.06.23)
1. email로 삭제 포함 구성원 신상 전부 조회
2. 삭제된 구성원 존재시: isDeleted를 복구하고 새로 입력받은 값으로 덮어쓰기
3. 활성 구성원 존재시: 기존 값 재사용 Todo: 대체 가능한 값은 새로운 입력으로 대체
2. 삭제된 구성원 존재시: isDeleted 복구
3. 활성 구성원 존재시: 새로 입력받은 값으로 덮어쓰기
4. 미 존재시: 새로 생성
*/

Expand All @@ -28,18 +28,13 @@ public Long findOrCreateMember(CreateMemberSemesterRequest request) {
.orElseGet(() -> createMember(request));
}

// 기존 구성원 상태에 따라 재사용 또는 복구
// 기존 구성원 복구 후 신상정보 갱신
private Long useExistingMember(Member member, CreateMemberSemesterRequest request) {
if (member.getIsDeleted()) {
return restoreMember(member, request);
member.restore();
}

return member.getId();
}

// 삭제된 구성원 신상정보 갱신 후 복구
private Long restoreMember(Member member, CreateMemberSemesterRequest request) {
member.restore(
member.updateProfile(
request.name(),
request.phoneNumber(),
request.interestedDomains(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import static org.ject.support.domain.member.exception.MemberErrorCode.*;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.ject.support.admin.member.dto.projection.SearchMemberSemesterProjection;
import org.ject.support.admin.member.dto.request.CreateMemberSemesterRequest;
Expand Down Expand Up @@ -35,7 +33,7 @@ public class AdminMemberUseCase {
public void createMemberSemester(CreateMemberSemesterRequest request) {
// 유효성 검증
semesterInquiryUsecase.getSemester(request.semesterId());
validateTeam(request.semesterId(), request.teamId());
validateCreateTeam(request.semesterId(), request.teamId());

// email 기준 기존 Member 조회 또는 신규 생성
Long memberId = adminMemberService.findOrCreateMember(request);
Expand All @@ -52,8 +50,8 @@ public CursorPageResponse<SearchMemberSemesterResponse> searchMemberSemester(
) {
// 유효성 검증
validateSemester(condition.semesterId());
validateTeams(condition.semesterId(), condition.teamIds());
validateStatuses(condition.statuses(), MemberType.SEMESTER);
validateSearchTeam(condition.semesterId(), condition.teamId());
validateStatus(condition.status(), MemberType.SEMESTER);
// size만큼 조회 + 전체 행 수 조회
SearchMemberSemesterPageResult pageResult = adminMemberActivityService.searchMemberSemesterList(condition);
// 페이징 값 처리
Expand Down Expand Up @@ -95,7 +93,7 @@ private void validateSemester(Long semesterId) {
}

// 선택한 팀의 기수 소속 검증
private void validateTeam(Long semesterId, Long teamId) {
private void validateCreateTeam(Long semesterId, Long teamId) {
if (teamId == null) {
return;
}
Expand All @@ -105,9 +103,9 @@ private void validateTeam(Long semesterId, Long teamId) {
}
}

// 넘겨받은 팀 목록 전체의 기수 소속 및 유효성 검증
private void validateTeams(Long semesterId, List<Long> teamIds) {
if(teamIds == null || teamIds.isEmpty()){
// 조회 필터 팀의 기수 소속 및 유효성 검증
private void validateSearchTeam(Long semesterId, Long teamId) {
if(teamId == null){
return;
}

Expand All @@ -116,18 +114,15 @@ private void validateTeams(Long semesterId, List<Long> teamIds) {
throw new MemberException(REQUIRED_SEMESTER_FOR_TEAM_FILTER);
}

Set<Long> validTeamIds = new HashSet<>(
adminMemberTeamService.getTeamIdsBySemesterId(semesterId)
);

if(!validTeamIds.containsAll(teamIds)) {
List<Long> validTeamIds = adminMemberTeamService.getTeamIdsBySemesterId(semesterId);
if(!validTeamIds.contains(teamId)) {
throw new MemberException(NOT_FOUND_TEAM_OF_SEMESTER);
}
}

// 구성원 유형에서 허용되는 활동 상태인지 검증
private void validateStatuses(List<ActivityStatus> statuses, MemberType type) {
if (!ActivityStatus.isAllAvailableFor(statuses, type)) {
private void validateStatus(ActivityStatus status, MemberType type) {
if (status != null && !status.isAvailableFor(type)) {
throw new MemberException(INVALID_ACTIVITY_STATUS);
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/org/ject/support/domain/member/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,18 @@ public static Member create(
.build();
}

// 삭제된 구성원 정보가 있는 경우 복구한 뒤 새로 입력한 값으로 덮어쓰기
public void restore(
// 삭제된 구성원 복구
public void restore() {
this.isDeleted = false;
}

// 기존 구성원 신상정보를 새 입력값으로 갱신
public void updateProfile(
String name,
String phoneNumber,
List<String> interestedDomains,
Region region
) {
this.isDeleted = false;
this.name = name;
this.phoneNumber = phoneNumber;
this.interestedDomains = interestedDomains;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class MemberActivityQueryRepositoryImpl implements MemberActivityQueryRep

private final JPAQueryFactory jpaQueryFactory;

//다중 값은 in, 단일 값은 eq로 필터링
// 단일 값 기준으로 일반 구성원 필터링
//id기준 내림차순 - 최신순
//size+1개 조회
@Override
Expand All @@ -47,12 +47,12 @@ public List<SearchMemberSemesterProjection> searchMemberSemesters(MemberSemester
.join(memberSemester).on(memberSemester.id.eq(memberActivity.id))
.where(
cursorLt(condition.cursor()),
semesterIdIn(condition.semesterId()),
jobFamilyIn(condition.jobFamilies()),
careerDetailsIn(condition.careerDetails()),
teamIdIn(condition.teamIds()),
recruitTypeDetailIn(condition.recruitTypeDetails()),
statusIn(condition.statuses()),
semesterIdEq(condition.semesterId()),
jobFamilyEq(condition.jobFamily()),
careerDetailsEq(condition.careerDetails()),
teamIdEq(condition.teamId()),
recruitTypeDetailEq(condition.recruitTypeDetail()),
statusEq(condition.status()),
member.isDeleted.isFalse(),
memberActivity.isDeleted.isFalse(),
memberActivity.memberType.eq(MemberType.SEMESTER)
Expand All @@ -68,12 +68,12 @@ public long countMemberSemesters(MemberSemesterSearchCondition condition) {
.join(member).on(member.id.eq(memberActivity.memberId))
.join(memberSemester).on(memberSemester.id.eq(memberActivity.id))
.where(
semesterIdIn(condition.semesterId()),
jobFamilyIn(condition.jobFamilies()),
careerDetailsIn(condition.careerDetails()),
teamIdIn(condition.teamIds()),
recruitTypeDetailIn(condition.recruitTypeDetails()),
statusIn(condition.statuses()),
semesterIdEq(condition.semesterId()),
jobFamilyEq(condition.jobFamily()),
careerDetailsEq(condition.careerDetails()),
teamIdEq(condition.teamId()),
recruitTypeDetailEq(condition.recruitTypeDetail()),
statusEq(condition.status()),
member.isDeleted.isFalse(),
memberActivity.isDeleted.isFalse(),
memberActivity.memberType.eq(MemberType.SEMESTER)
Expand Down Expand Up @@ -120,38 +120,33 @@ private BooleanExpression cursorLt(Long cursor){
return cursor == null ? null : memberActivity.id.lt(cursor);
}

private BooleanExpression semesterIdIn(Long semesterId) {
private BooleanExpression semesterIdEq(Long semesterId) {
return semesterId == null ? null
: memberSemester.semesterId.eq(semesterId);
}

private BooleanExpression jobFamilyIn(List<JobFamily> jobFamilies) {
return jobFamilies == null || jobFamilies.isEmpty()
? null
: memberActivity.jobFamily.in(jobFamilies);
private BooleanExpression jobFamilyEq(JobFamily jobFamily) {
return jobFamily == null ? null
: memberActivity.jobFamily.eq(jobFamily);
}

private BooleanExpression careerDetailsIn(List<CareerDetails> careerDetails) {
return careerDetails == null || careerDetails.isEmpty()
? null
: memberActivity.careerDetails.in(careerDetails);
private BooleanExpression careerDetailsEq(CareerDetails careerDetails) {
return careerDetails == null ? null
: memberActivity.careerDetails.eq(careerDetails);
}

private BooleanExpression teamIdIn(List<Long> teamIds) {
return teamIds == null || teamIds.isEmpty()
? null
: memberSemester.teamId.in(teamIds);
private BooleanExpression teamIdEq(Long teamId) {
return teamId == null ? null
: memberSemester.teamId.eq(teamId);
}

private BooleanExpression recruitTypeDetailIn(List<RecruitTypeDetail> recruitTypeDetails) {
return recruitTypeDetails == null || recruitTypeDetails.isEmpty()
? null
: memberActivity.recruitTypeDetail.in(recruitTypeDetails);
private BooleanExpression recruitTypeDetailEq(RecruitTypeDetail recruitTypeDetail) {
return recruitTypeDetail == null ? null
: memberActivity.recruitTypeDetail.eq(recruitTypeDetail);
}

private BooleanExpression statusIn(List<ActivityStatus> statuses) {
return statuses == null || statuses.isEmpty()
? null
: memberActivity.activityStatus.in(statuses);
private BooleanExpression statusEq(ActivityStatus status) {
return status == null ? null
: memberActivity.activityStatus.eq(status);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public Page<ProjectResponse> findProjectsByCategory(final Category category,

JPAQuery<Long> countQuery = queryFactory
.select(project.count())
.from(project);
.from(project)
.where(eqCategory(category));

return PageResponse.from(content, pageable, countQuery.fetchFirst());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,11 @@ class AdminMemberSemesterControllerTest {
mockMvc.perform(get("/admin/members/semester")
.param("size", "30")
.param("semesterId", String.valueOf(semester.getId()))
.param("jobFamilies", "BE")
.param("recruitTypeDetails", "REGULAR")
.param("jobFamily", "BE")
.param("recruitTypeDetail", "REGULAR")
.param("careerDetails", "EMPLOYEE")
.param("teamIds", String.valueOf(team.getId()))
.param("statuses", ActivityStatus.ACTIVE.name()))
.param("teamId", String.valueOf(team.getId()))
.param("status", ActivityStatus.ACTIVE.name()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("SUCCESS"))
.andExpect(jsonPath("$.data.content", hasSize(1)))
Expand All @@ -248,7 +248,7 @@ class AdminMemberSemesterControllerTest {
void 팀_필터만_있으면_일반_구성원_목록을_조회하지_않는다() throws Exception {
// when & then
mockMvc.perform(get("/admin/members/semester")
.param("teamIds", "1"))
.param("teamId", "1"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.status").value(MemberErrorCode.REQUIRED_SEMESTER_FOR_TEAM_FILTER.getCode()));
}
Expand All @@ -258,7 +258,7 @@ class AdminMemberSemesterControllerTest {
void 잘못된_enum_값이면_일반_구성원_목록을_조회하지_않는다() throws Exception {
// when & then
mockMvc.perform(get("/admin/members/semester")
.param("jobFamilies", "WRONG"))
.param("jobFamily", "WRONG"))
.andExpect(status().isBadRequest());
}

Expand All @@ -267,7 +267,7 @@ class AdminMemberSemesterControllerTest {
void 일반_구성원에서_사용할_수_없는_활동_상태면_목록을_조회하지_않는다() throws Exception {
// when & then
mockMvc.perform(get("/admin/members/semester")
.param("statuses", ActivityStatus.DROPOUT.name()))
.param("status", ActivityStatus.DROPOUT.name()))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.status").value(MemberErrorCode.INVALID_ACTIVITY_STATUS.getCode()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,18 @@ class AdminMemberActivityServiceTest {
*/

@Test
@DisplayName("다중 필터를 적용해서 일반 구성원 목록을 조회한다")
void 다중_필터를_적용해서_일반_구성원_목록을_조회한다() {
@DisplayName("단일 필터를 적용해서 일반 구성원 목록을 조회한다")
void 단일_필터를_적용해서_일반_구성원_목록을_조회한다() {
// given
MemberSemesterSearchCondition condition = new MemberSemesterSearchCondition(
null,
20,
1L,
List.of(JobFamily.BE),
List.of(RecruitTypeDetail.REGULAR),
List.of(CareerDetails.EMPLOYEE),
List.of(1L,2L,3L),
List.of(ActivityStatus.ACTIVE)
JobFamily.BE,
RecruitTypeDetail.REGULAR,
CareerDetails.EMPLOYEE,
1L,
ActivityStatus.ACTIVE
);

List<SearchMemberSemesterProjection> projections = List.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,18 @@ private CreateMemberSemesterRequest createMemberSemesterRequest() {
}

@Test
@DisplayName("입력한 이메일로 기존 구성원이 존재하면 새로 저장하지 않는다")
void 입력한_이메일로_기존_구성원이_존재하면_새로_저장하지_않는다() {
@DisplayName("입력한 이메일로 기존 구성원이 존재하면 값을 덮어쓰고 새로 저장하지 않는다")
void 입력한_이메일로_기존_구성원이_존재하면_값을_덮어쓰고_새로_저장하지_않는다() {
// given
CreateMemberSemesterRequest request = createMemberSemesterRequest();
Member existMember = mock(Member.class);
given(existMember.getId()).willReturn(3L);
given(existMember.getIsDeleted()).willReturn(false);
Member existMember = Member.create(
"기존 구성원",
request.email(),
"01087654321",
List.of("COMMERCE"),
Region.BUSAN
);
ReflectionTestUtils.setField(existMember, "id", 3L);

given(memberRepository.findByEmailIncludingDeleted(request.email())).willReturn(Optional.of(existMember));

Expand All @@ -95,6 +100,10 @@ private CreateMemberSemesterRequest createMemberSemesterRequest() {

// then
assertThat(memberId).isEqualTo(existMember.getId());
assertThat(existMember.getName()).isEqualTo(request.name());
assertThat(existMember.getPhoneNumber()).isEqualTo(request.phoneNumber());
assertThat(existMember.getInterestedDomains()).containsExactlyElementsOf(request.interestedDomains());
assertThat(existMember.getRegion()).isEqualTo(request.region());
verify(memberRepository).findByEmailIncludingDeleted(request.email());
verify(memberRepository, never()).save(any(Member.class));
}
Expand Down
Loading
Loading