Skip to content

Commit 5445e95

Browse files
committed
[feat] 활동 상태 필터 추가
1 parent 7d3ad21 commit 5445e95

15 files changed

Lines changed: 339 additions & 24 deletions

src/main/java/org/ject/support/admin/member/dto/projection/SearchMemberSemesterProjection.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.ject.support.admin.member.dto.projection;
22

3+
import org.ject.support.domain.member.ActivityStatus;
34
import org.ject.support.domain.member.CareerDetails;
45
import org.ject.support.domain.member.ExperiencePeriod;
56
import org.ject.support.domain.member.JobFamily;
@@ -10,6 +11,7 @@ public record SearchMemberSemesterProjection(
1011
JobFamily jobFamily,
1112
String phoneNumber,
1213
CareerDetails careerDetails,
13-
ExperiencePeriod experiencePeriod
14+
ExperiencePeriod experiencePeriod,
15+
ActivityStatus status
1416
) {
1517
}

src/main/java/org/ject/support/admin/member/dto/request/MemberSemesterSearchCondition.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.List;
44

5+
import org.ject.support.domain.member.ActivityStatus;
56
import org.ject.support.domain.member.CareerDetails;
67
import org.ject.support.domain.member.JobFamily;
78
import org.ject.support.domain.recruit.domain.RecruitTypeDetail;
@@ -35,7 +36,9 @@ public record MemberSemesterSearchCondition(
3536
// 신분 (다중 선택)
3637
List<CareerDetails> careerDetails,
3738
// 팀 (다중 선택)
38-
List<@Positive Long> teamIds
39+
List<@Positive Long> teamIds,
40+
// 활동 상태 (다중 선택)
41+
List<ActivityStatus> statuses
3942
) {
4043
//size가 없으면 기본 30개
4144
public int getSizeOrDefault() {

src/main/java/org/ject/support/admin/member/dto/response/SearchMemberSemesterResponse.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.ject.support.admin.member.dto.response;
22

33
import org.ject.support.admin.member.dto.projection.SearchMemberSemesterProjection;
4+
import org.ject.support.domain.member.ActivityStatus;
45
import org.ject.support.domain.member.CareerDetails;
56
import org.ject.support.domain.member.ExperiencePeriod;
67
import org.ject.support.domain.member.JobFamily;
@@ -11,7 +12,8 @@ public record SearchMemberSemesterResponse(
1112
JobFamily jobFamily,
1213
String phoneNumber,
1314
CareerDetails careerDetails,
14-
ExperiencePeriod experiencePeriod
15+
ExperiencePeriod experiencePeriod,
16+
ActivityStatus status
1517
) {
1618
public static SearchMemberSemesterResponse from(SearchMemberSemesterProjection projection) {
1719
return new SearchMemberSemesterResponse(
@@ -20,7 +22,8 @@ public static SearchMemberSemesterResponse from(SearchMemberSemesterProjection p
2022
projection.jobFamily(),
2123
projection.phoneNumber(),
2224
projection.careerDetails(),
23-
projection.experiencePeriod()
25+
projection.experiencePeriod(),
26+
projection.status()
2427
);
2528
}
2629
}

src/main/java/org/ject/support/admin/member/service/AdminMemberUseCase.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
import java.util.HashSet;
66
import java.util.List;
77
import java.util.Set;
8-
import java.util.stream.Collectors;
98

109
import org.ject.support.admin.member.dto.projection.SearchMemberSemesterProjection;
1110
import org.ject.support.admin.member.dto.request.CreateMemberSemesterRequest;
1211
import org.ject.support.admin.member.dto.request.MemberSemesterSearchCondition;
1312
import org.ject.support.admin.member.dto.response.SearchMemberSemesterResponse;
1413
import org.ject.support.admin.member.dto.result.SearchMemberSemesterPageResult;
1514
import org.ject.support.common.response.CursorPageResponse;
15+
import org.ject.support.domain.member.ActivityStatus;
16+
import org.ject.support.domain.member.MemberType;
1617
import org.ject.support.domain.member.exception.MemberException;
1718
import org.ject.support.domain.recruit.service.SemesterInquiryUsecase;
1819
import org.springframework.stereotype.Service;
@@ -53,6 +54,8 @@ public CursorPageResponse<SearchMemberSemesterResponse> searchMemberSemester(
5354
validateSemester(condition.semesterId());
5455
// 팀 유효성 체크
5556
validateTeams(condition.semesterId(), condition.teamIds());
57+
// 활동 상태 체크
58+
validateStatuses(condition.statuses(), MemberType.SEMESTER);
5659
// size만큼 조회 + 전체 행 수 조회
5760
SearchMemberSemesterPageResult pageResult = adminMemberActivityService.searchMemberSemesterList(condition);
5861
// 페이징 값 처리
@@ -125,4 +128,11 @@ private void validateTeams(Long semesterId, List<Long> teamIds) {
125128
}
126129
}
127130

131+
// 구성원 유형에서 허용되는 활동 상태인지 검증
132+
private void validateStatuses(List<ActivityStatus> statuses, MemberType type) {
133+
if (!ActivityStatus.isAllAvailableFor(statuses, type)) {
134+
throw new MemberException(INVALID_ACTIVITY_STATUS);
135+
}
136+
}
137+
128138
}
Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,34 @@
11
package org.ject.support.domain.member;
22

3+
import java.util.List;
4+
35
import lombok.AllArgsConstructor;
46
import lombok.Getter;
57

68
@Getter
79
@AllArgsConstructor
810
public enum ActivityStatus {
9-
ACTIVE("활동중"),
11+
ACTIVE("활동 중"),
1012
COMPLETED("완주"),
11-
ENDED("활동종료"),
12-
DROPOUT("중도하차");
13+
WITHDRAWN("탈퇴"),
14+
ENDED("활동 종료"),
15+
DROPOUT("중도 이탈");
1316

1417
private final String description;
18+
19+
public boolean isAvailableFor(MemberType type) {
20+
return switch (type) {
21+
case SEMESTER -> this == ACTIVE || this == COMPLETED || this == WITHDRAWN;
22+
case MAKERS, SUPPORTERS -> this == ACTIVE || this == ENDED || this == DROPOUT;
23+
};
24+
}
25+
26+
public static boolean isAllAvailableFor(List<ActivityStatus> statuses, MemberType type) {
27+
if (statuses == null || statuses.isEmpty()) {
28+
return true;
29+
}
30+
31+
return statuses.stream()
32+
.allMatch(status -> status.isAvailableFor(type));
33+
}
1534
}

src/main/java/org/ject/support/domain/member/entity/MemberActivity.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,13 @@ public static MemberActivity createSemesterActivity(
111111
//Todo: 메이커스 구성원 활동과 관리 항목 생성
112112

113113
//Todo: 운영 서포터즈 구성원 활동과 관리 항목 생성
114+
115+
// 구성원 유형에 맞는 활동 상태로 변경
116+
public void updateActivityStatus(ActivityStatus activityStatus) {
117+
if (!activityStatus.isAvailableFor(memberType)) {
118+
throw new MemberException(MemberErrorCode.INVALID_ACTIVITY_STATUS);
119+
}
120+
121+
this.activityStatus = activityStatus;
122+
}
114123
}

src/main/java/org/ject/support/domain/member/exception/MemberErrorCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public enum MemberErrorCode implements ErrorCode {
1818
ALREADY_EXIST_MEMBER_SEMESTER_ACTIVITY(CONFLICT, "MEMBER-6", "해당 기수에 이미 등록된 일반 구성원 활동입니다."),
1919
NOT_FOUND_TEAM_OF_SEMESTER(NOT_FOUND, "MEMBER-7", "해당 기수의 팀을 찾을 수 없습니다."),
2020
REQUIRED_SEMESTER_FOR_TEAM_FILTER(BAD_REQUEST, "MEMBER-8", "팀 필터를 사용할 때는 기수를 함께 선택해야 합니다."),
21+
INVALID_ACTIVITY_STATUS(BAD_REQUEST, "MEMBER-9", "구성원 유형에 맞지 않는 활동 상태입니다."),
2122
;
2223

2324
private final HttpStatus httpStatus;

src/main/java/org/ject/support/domain/member/repository/MemberActivityQueryRepositoryImpl.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import org.ject.support.admin.member.dto.projection.SearchMemberSemesterProjection;
1010
import org.ject.support.admin.member.dto.request.MemberSemesterSearchCondition;
11+
import org.ject.support.domain.member.ActivityStatus;
1112
import org.ject.support.domain.member.CareerDetails;
1213
import org.ject.support.domain.member.JobFamily;
1314
import org.ject.support.domain.member.MemberType;
@@ -36,7 +37,8 @@ public List<SearchMemberSemesterProjection> searchMemberSemesters(MemberSemester
3637
memberActivity.jobFamily,
3738
member.phoneNumber,
3839
memberActivity.careerDetails,
39-
memberActivity.experiencePeriod
40+
memberActivity.experiencePeriod,
41+
memberActivity.activityStatus
4042
))
4143
.from(memberActivity)
4244
.join(member).on(member.id.eq(memberActivity.memberId))
@@ -47,7 +49,8 @@ public List<SearchMemberSemesterProjection> searchMemberSemesters(MemberSemester
4749
jobFamilyIn(condition.jobFamilies()),
4850
careerDetailsIn(condition.careerDetails()),
4951
teamIdIn(condition.teamIds()),
50-
recruitTypeDetailsIn(condition.recruitTypeDetails()),
52+
recruitTypeDetailIn(condition.recruitTypeDetails()),
53+
statusIn(condition.statuses()),
5154
member.isDeleted.isFalse(),
5255
memberActivity.isDeleted.isFalse(),
5356
memberActivity.memberType.eq(MemberType.SEMESTER)
@@ -67,7 +70,8 @@ public long countMemberSemesters(MemberSemesterSearchCondition condition) {
6770
jobFamilyIn(condition.jobFamilies()),
6871
careerDetailsIn(condition.careerDetails()),
6972
teamIdIn(condition.teamIds()),
70-
recruitTypeDetailsIn(condition.recruitTypeDetails()),
73+
recruitTypeDetailIn(condition.recruitTypeDetails()),
74+
statusIn(condition.statuses()),
7175
member.isDeleted.isFalse(),
7276
memberActivity.isDeleted.isFalse(),
7377
memberActivity.memberType.eq(MemberType.SEMESTER)
@@ -103,9 +107,15 @@ private BooleanExpression teamIdIn(List<Long> teamIds) {
103107
: memberSemester.teamId.in(teamIds);
104108
}
105109

106-
private BooleanExpression recruitTypeDetailsIn(List<RecruitTypeDetail> recruitTypeDetails) {
110+
private BooleanExpression recruitTypeDetailIn(List<RecruitTypeDetail> recruitTypeDetails) {
107111
return recruitTypeDetails == null || recruitTypeDetails.isEmpty()
108112
? null
109113
: memberActivity.recruitTypeDetail.in(recruitTypeDetails);
110114
}
115+
116+
private BooleanExpression statusIn(List<ActivityStatus> statuses) {
117+
return statuses == null || statuses.isEmpty()
118+
? null
119+
: memberActivity.activityStatus.in(statuses);
120+
}
111121
}

src/test/java/org/ject/support/admin/member/controller/AdminMemberSemesterControllerTest.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.Comparator;
1414
import java.util.List;
1515
import org.ject.support.admin.member.dto.request.CreateMemberSemesterRequest;
16+
import org.ject.support.domain.member.ActivityStatus;
1617
import org.ject.support.domain.member.CareerDetails;
1718
import org.ject.support.domain.member.ExperiencePeriod;
1819
import org.ject.support.domain.member.JobFamily;
@@ -232,11 +233,13 @@ class AdminMemberSemesterControllerTest {
232233
.param("jobFamilies", "BE")
233234
.param("recruitTypeDetails", "REGULAR")
234235
.param("careerDetails", "EMPLOYEE")
235-
.param("teamIds", String.valueOf(team.getId())))
236+
.param("teamIds", String.valueOf(team.getId()))
237+
.param("statuses", ActivityStatus.ACTIVE.name()))
236238
.andExpect(status().isOk())
237239
.andExpect(jsonPath("$.status").value("SUCCESS"))
238240
.andExpect(jsonPath("$.data.content", hasSize(1)))
239241
.andExpect(jsonPath("$.data.content[0].memberActivityId").value(matched.getId()))
242+
.andExpect(jsonPath("$.data.content[0].status").value(ActivityStatus.ACTIVE.name()))
240243
.andExpect(jsonPath("$.data.totalCount").value(1));
241244
}
242245

@@ -259,6 +262,16 @@ class AdminMemberSemesterControllerTest {
259262
.andExpect(status().isBadRequest());
260263
}
261264

265+
@Test
266+
@DisplayName("일반 구성원에서 사용할 수 없는 활동 상태면 목록을 조회하지 않는다")
267+
void 일반_구성원에서_사용할_수_없는_활동_상태면_목록을_조회하지_않는다() throws Exception {
268+
// when & then
269+
mockMvc.perform(get("/admin/members/semester")
270+
.param("statuses", ActivityStatus.DROPOUT.name()))
271+
.andExpect(status().isBadRequest())
272+
.andExpect(jsonPath("$.status").value(MemberErrorCode.INVALID_ACTIVITY_STATUS.getCode()));
273+
}
274+
262275
private CreateMemberSemesterRequest createMemberSemesterRequest(String email, Long semesterId, Long teamId) {
263276
return new CreateMemberSemesterRequest(
264277
"김젝트",

src/test/java/org/ject/support/admin/member/service/AdminMemberActivityServiceTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.ject.support.admin.member.dto.request.MemberSemesterSearchCondition;
1515
import org.ject.support.admin.member.dto.result.SearchMemberSemesterPageResult;
1616
import org.ject.support.common.response.CursorPageResponse;
17+
import org.ject.support.domain.member.ActivityStatus;
1718
import org.ject.support.domain.member.CareerDetails;
1819
import org.ject.support.domain.member.ExperiencePeriod;
1920
import org.ject.support.domain.member.JobFamily;
@@ -114,7 +115,8 @@ class AdminMemberActivityServiceTest {
114115
List.of(JobFamily.BE),
115116
List.of(RecruitTypeDetail.REGULAR),
116117
List.of(CareerDetails.EMPLOYEE),
117-
List.of(1L,2L,3L)
118+
List.of(1L,2L,3L),
119+
List.of(ActivityStatus.ACTIVE)
118120
);
119121

120122
List<SearchMemberSemesterProjection> projections = List.of(
@@ -124,7 +126,8 @@ class AdminMemberActivityServiceTest {
124126
JobFamily.BE,
125127
"01012345678",
126128
CareerDetails.EMPLOYEE,
127-
ExperiencePeriod.ONE_TO_TWO
129+
ExperiencePeriod.ONE_TO_TWO,
130+
ActivityStatus.ACTIVE
128131
)
129132
);
130133
given(memberActivityRepository.searchMemberSemesters(condition, condition.getSizeOrDefault()+1)).willReturn(projections);

0 commit comments

Comments
 (0)