Skip to content

Commit b86b220

Browse files
authored
[FEAT] 모임 PENDING 목록 조회 구현
[FEAT] 모임 PENDING 목록 조회 구현
2 parents a6aa916 + 9d8be77 commit b86b220

17 files changed

+567
-12
lines changed

src/main/java/team/wego/wegobackend/group/domain/exception/GroupErrorCode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
@RequiredArgsConstructor
1010
public enum GroupErrorCode implements ErrorCode {
1111

12+
NO_PERMISSION_TO_VIEW_JOIN_REQUESTS(HttpStatus.FORBIDDEN,
13+
"모임: 참여 요청 목록 조회 권한이 없습니다. 모임 ID: %s 회원 ID: %s"),
14+
JOIN_REQUEST_MESSAGE_TOO_LONG(HttpStatus.BAD_REQUEST, "모임: 모임 메시지는 300자 이하입니다."),
1215
// 이미지 검증 / 정렬 / 업로드
1316
INVALID_GROUP_IMAGE_ITEM(HttpStatus.BAD_REQUEST,
1417
"모임: images 항목이 올바르지 않습니다. 모임 ID: %s 회원 ID: %s"),
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package team.wego.wegobackend.group.v2.application.dto.common;
2+
3+
import java.time.LocalDateTime;
4+
import team.wego.wegobackend.group.v2.domain.entity.GroupUserV2Status;
5+
6+
public record JoinRequestItem(
7+
Long userId,
8+
String nickName,
9+
String profileImage,
10+
Long groupUserId,
11+
GroupUserV2Status status,
12+
LocalDateTime joinedAt,
13+
String joinRequestMessage
14+
) {
15+
16+
}
17+
18+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package team.wego.wegobackend.group.v2.application.dto.request;
2+
3+
import jakarta.validation.constraints.Size;
4+
5+
public record AttendGroupV2Request(
6+
@Size(max = 300, message = "모임: 모임 메시지는 최대 300자 이하입니다.")
7+
String message
8+
) {
9+
10+
}
11+

src/main/java/team/wego/wegobackend/group/v2/application/dto/request/UpdateGroupV2Request.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
* “이미지 변경 없음” 으로 가자 빈 리스트([])면 “이미지 전체 삭제”(정책 허용 시) 하자
1313
*/
1414
public record UpdateGroupV2Request(
15-
@Size(max = 50)
15+
@Size(max = 50, message = "모임: 모임 제목은 50자 이하 입니다.")
1616
String title,
17-
@Size(max = 300)
17+
@Size(max = 300, message = "모임: 모임 설명은 300자 이하 입니다.")
1818
String description,
1919

2020
String location,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package team.wego.wegobackend.group.v2.application.dto.response;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
import team.wego.wegobackend.group.v2.application.dto.common.JoinRequestItem;
6+
import team.wego.wegobackend.group.v2.domain.entity.GroupUserV2Status;
7+
8+
public record GroupJoinRequestsResponse(
9+
Long groupId,
10+
GroupUserV2Status status,
11+
int count,
12+
List<JoinRequestItem> items,
13+
LocalDateTime serverTime
14+
) {
15+
public static GroupJoinRequestsResponse of(
16+
Long groupId,
17+
GroupUserV2Status status,
18+
List<JoinRequestItem> items
19+
) {
20+
return new GroupJoinRequestsResponse(
21+
groupId,
22+
status,
23+
items == null ? 0 : items.size(),
24+
items,
25+
LocalDateTime.now()
26+
);
27+
}
28+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package team.wego.wegobackend.group.v2.application.service;
2+
3+
import java.util.List;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Transactional;
8+
import team.wego.wegobackend.common.security.CustomUserDetails;
9+
import team.wego.wegobackend.group.domain.exception.GroupErrorCode;
10+
import team.wego.wegobackend.group.domain.exception.GroupException;
11+
import team.wego.wegobackend.group.v2.application.dto.common.JoinRequestItem;
12+
import team.wego.wegobackend.group.v2.application.dto.response.GroupJoinRequestsResponse;
13+
import team.wego.wegobackend.group.v2.domain.entity.GroupUserV2Status;
14+
import team.wego.wegobackend.group.v2.domain.entity.GroupV2;
15+
import team.wego.wegobackend.group.v2.domain.repository.GroupUserV2QueryRepository;
16+
import team.wego.wegobackend.group.v2.domain.repository.GroupUserV2Repository;
17+
import team.wego.wegobackend.group.v2.domain.repository.GroupV2Repository;
18+
19+
@Slf4j
20+
@RequiredArgsConstructor
21+
@Service
22+
public class GroupJoinRequestService {
23+
24+
private final GroupV2Repository groupV2Repository;
25+
private final GroupUserV2QueryRepository groupUserV2QueryRepository;
26+
27+
28+
@Transactional(readOnly = true)
29+
public GroupJoinRequestsResponse getJoinRequests(
30+
Long groupId,
31+
CustomUserDetails userDetails,
32+
GroupUserV2Status status
33+
) {
34+
if (userDetails == null || userDetails.getId() == null) {
35+
throw new GroupException(GroupErrorCode.USER_ID_NULL);
36+
}
37+
38+
// 모임 조회
39+
GroupV2 groupV2 = groupV2Repository.findById(groupId)
40+
.orElseThrow(
41+
() -> new GroupException(GroupErrorCode.GROUP_NOT_FOUND_BY_ID, groupId));
42+
43+
// HOST 확인
44+
if (!groupV2.getHost().getId().equals(userDetails.getId())) {
45+
throw new GroupException(
46+
GroupErrorCode.NO_PERMISSION_TO_VIEW_JOIN_REQUESTS,
47+
groupId,
48+
userDetails.getId()
49+
);
50+
}
51+
52+
// status 기본값 방어
53+
GroupUserV2Status targetStatus = (status == null) ? GroupUserV2Status.PENDING : status;
54+
55+
// QueryDSL 조회
56+
List<JoinRequestItem> items = groupUserV2QueryRepository
57+
.fetchJoinRequests(groupId, targetStatus)
58+
.stream()
59+
.map(joinRequestRow -> new JoinRequestItem(
60+
joinRequestRow.userId(),
61+
joinRequestRow.nickName(),
62+
joinRequestRow.profileImage(),
63+
joinRequestRow.groupUserId(),
64+
joinRequestRow.status(),
65+
joinRequestRow.joinedAt(),
66+
joinRequestRow.joinRequestMessage()
67+
))
68+
.toList();
69+
70+
return GroupJoinRequestsResponse.of(groupId, targetStatus, items);
71+
}
72+
}

src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class GroupV2AttendanceService {
4949

5050
// TODO: 참석, 취소 동시성 해결 필요.
5151
@Transactional
52-
public AttendanceGroupV2Response attend(Long userId, Long groupId) {
52+
public AttendanceGroupV2Response attend(Long userId, Long groupId, String message) {
5353
// 회원 체크
5454
if (userId == null) {
5555
throw new GroupException(GroupErrorCode.USER_ID_NULL);
@@ -141,7 +141,8 @@ public AttendanceGroupV2Response attend(Long userId, Long groupId) {
141141
} else {
142142
groupUserV2 = GroupUserV2.createPending(
143143
group,
144-
userRepository.getReferenceById(userId)
144+
userRepository.getReferenceById(userId),
145+
message
145146
);
146147
groupUserV2Repository.save(groupUserV2);
147148
}

src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupUserV2.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public class GroupUserV2 extends BaseTimeEntity {
5858
@Column(name = "group_user_status", nullable = false, length = 20)
5959
private GroupUserV2Status status;
6060

61+
@Column(name = "join_request_message", length = 300)
62+
private String joinRequestMessage;
63+
6164
private GroupUserV2(User user, GroupUserV2Role groupRole, GroupUserV2Status status) {
6265
this.user = user;
6366
this.groupRole = groupRole;
@@ -144,6 +147,13 @@ public static GroupUserV2 createPending(GroupV2 group, User user) {
144147
return groupUser;
145148
}
146149

150+
public static GroupUserV2 createPending(GroupV2 group, User user, String message) {
151+
GroupUserV2 groupUser = new GroupUserV2(user, GroupUserV2Role.MEMBER, GroupUserV2Status.PENDING);
152+
groupUser.applyJoinRequestMessage(message);
153+
group.addUser(groupUser);
154+
return groupUser;
155+
}
156+
147157
public void requestJoin() {
148158
if (this.status == GroupUserV2Status.BANNED) {
149159
throw new GroupException(GroupErrorCode.GROUP_BANNED_USER);
@@ -154,6 +164,16 @@ public void requestJoin() {
154164
this.leftAt = null;
155165
}
156166

167+
public void requestJoin(String message) {
168+
if (this.status == GroupUserV2Status.BANNED) {
169+
throw new GroupException(GroupErrorCode.GROUP_BANNED_USER);
170+
}
171+
this.status = GroupUserV2Status.PENDING;
172+
this.joinedAt = LocalDateTime.now();
173+
this.leftAt = null;
174+
applyJoinRequestMessage(message); // 여기서 갱신
175+
}
176+
157177
public void leaveOrCancel() {
158178
switch (this.status) {
159179
case ATTEND -> this.leave();
@@ -227,5 +247,19 @@ public void unban() {
227247
this.status = GroupUserV2Status.KICKED; // 재참여는 스스로 가능
228248
this.leftAt = LocalDateTime.now();
229249
}
250+
251+
public void applyJoinRequestMessage(String message) {
252+
// 정책: null/blank 허용 여부 결정
253+
if (message == null || message.isBlank()) {
254+
this.joinRequestMessage = null;
255+
return;
256+
}
257+
258+
String trimmed = message.trim();
259+
if (trimmed.length() > 300) {
260+
throw new GroupException(GroupErrorCode.JOIN_REQUEST_MESSAGE_TOO_LONG); // 신규 에러코드 추천
261+
}
262+
this.joinRequestMessage = trimmed;
263+
}
230264
}
231265

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package team.wego.wegobackend.group.v2.domain.repository;
22

33
import java.util.List;
4+
import team.wego.wegobackend.group.v2.domain.entity.GroupUserV2Status;
45
import team.wego.wegobackend.group.v2.infrastructure.querydsl.projection.AttendanceTargetRow;
6+
import team.wego.wegobackend.group.v2.infrastructure.querydsl.projection.JoinRequestRow;
57

68
public interface GroupUserV2QueryRepository {
79

810
List<AttendanceTargetRow> fetchAttendMembersExceptHost(Long groupId);
911

1012
List<AttendanceTargetRow> fetchBannedMembersExceptHost(Long groupId);
13+
14+
List<JoinRequestRow> fetchJoinRequests(Long groupId, GroupUserV2Status status);
1115
}
1216

src/main/java/team/wego/wegobackend/group/v2/infrastructure/querydsl/GroupUserV2QueryRepositoryImpl.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
import java.util.List;
99
import lombok.RequiredArgsConstructor;
1010
import org.springframework.stereotype.Repository;
11+
import team.wego.wegobackend.group.v2.domain.entity.GroupUserV2Role;
1112
import team.wego.wegobackend.group.v2.domain.entity.GroupUserV2Status;
1213
import team.wego.wegobackend.group.v2.domain.repository.GroupUserV2QueryRepository;
1314
import team.wego.wegobackend.group.v2.infrastructure.querydsl.projection.AttendanceTargetRow;
15+
import team.wego.wegobackend.group.v2.infrastructure.querydsl.projection.JoinRequestRow;
1416

1517
@RequiredArgsConstructor
1618
@Repository
@@ -67,4 +69,29 @@ public List<AttendanceTargetRow> fetchBannedMembersExceptHost(Long groupId) {
6769
.orderBy(groupUserV2.leftAt.desc().nullsLast())
6870
.fetch();
6971
}
72+
73+
@Override
74+
public List<JoinRequestRow> fetchJoinRequests(Long groupId, GroupUserV2Status status) {
75+
return queryFactory
76+
.select(Projections.constructor(
77+
JoinRequestRow.class,
78+
user.id,
79+
user.nickName,
80+
user.profileImage,
81+
groupUserV2.id,
82+
groupUserV2.status,
83+
groupUserV2.joinedAt,
84+
groupUserV2.joinRequestMessage
85+
))
86+
.from(groupUserV2)
87+
.join(groupUserV2.user, user)
88+
.where(
89+
groupUserV2.group.id.eq(groupId),
90+
groupUserV2.status.eq(status),
91+
groupUserV2.groupRole.ne(GroupUserV2Role.HOST) // HOST 제외
92+
)
93+
// joinRequest는 최신 신청이 위로 오도록
94+
.orderBy(groupUserV2.joinedAt.desc())
95+
.fetch();
96+
}
7097
}

0 commit comments

Comments
 (0)