Skip to content

Commit b3a3917

Browse files
authored
Merge pull request #59 from Sportize/feat/join-match-message
✨ Feat: 동호회 가입 신청서 작성 기능 추가 및 신청 조회 API 개선
2 parents c85bac5 + 585be7a commit b3a3917

File tree

8 files changed

+89
-9
lines changed

8 files changed

+89
-9
lines changed

src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package com.be.sportizebe.domain.notification.controller;
22

3+
import com.be.sportizebe.domain.notification.dto.request.JoinClubApplicationRequest;
34
import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestResponse;
5+
import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestSummaryResponse;
46
import com.be.sportizebe.domain.notification.service.JoinClubRequestService;
57
import com.be.sportizebe.global.cache.dto.UserAuthInfo;
68
import com.be.sportizebe.global.response.BaseResponse;
79
import io.swagger.v3.oas.annotations.Operation;
810
import io.swagger.v3.oas.annotations.Parameter;
911
import io.swagger.v3.oas.annotations.tags.Tag;
12+
import jakarta.validation.Valid;
1013
import lombok.RequiredArgsConstructor;
1114
import org.springframework.http.HttpStatus;
1215
import org.springframework.http.ResponseEntity;
1316
import org.springframework.security.core.annotation.AuthenticationPrincipal;
17+
import org.springframework.validation.annotation.Validated;
1418
import org.springframework.web.bind.annotation.*;
1519

1620
import java.util.List;
@@ -27,12 +31,22 @@ public class JoinClubRequestController {
2731
@Operation(summary = "가입 신청", description = "동호회에 가입을 신청합니다. 동호회장에게 알림이 전송됩니다.")
2832
public ResponseEntity<BaseResponse<JoinClubRequestResponse>> requestJoin(
2933
@Parameter(description = "동호회 ID") @PathVariable Long clubId,
34+
@Valid @RequestBody JoinClubApplicationRequest request,
3035
@AuthenticationPrincipal UserAuthInfo userAuthInfo) {
31-
JoinClubRequestResponse response = joinClubRequestService.requestJoin(clubId, userAuthInfo.getId());
36+
JoinClubRequestResponse response = joinClubRequestService.requestJoin(clubId, userAuthInfo.getId(), request.message());
3237
return ResponseEntity.status(HttpStatus.CREATED)
3338
.body(BaseResponse.success("가입 신청 완료", response));
3439
}
3540

41+
@GetMapping("/join-requests/{requestId}")
42+
@Operation(summary = "가입 신청 단건 조회", description = "가입 신청 상세 정보를 조회합니다. 동호회장 또는 신청자 본인만 가능합니다.")
43+
public ResponseEntity<BaseResponse<JoinClubRequestResponse>> getJoinRequest(
44+
@Parameter(description = "가입 신청 ID") @PathVariable Long requestId,
45+
@AuthenticationPrincipal UserAuthInfo userAuthInfo) {
46+
JoinClubRequestResponse response = joinClubRequestService.getJoinRequest(requestId, userAuthInfo.getId());
47+
return ResponseEntity.ok(BaseResponse.success("가입 신청 조회 성공", response));
48+
}
49+
3650
@DeleteMapping("/join-requests/{requestId}")
3751
@Operation(summary = "가입 신청 취소", description = "본인의 가입 신청을 취소합니다.")
3852
public ResponseEntity<BaseResponse<Void>> cancelRequest(
@@ -62,10 +76,10 @@ public ResponseEntity<BaseResponse<JoinClubRequestResponse>> rejectRequest(
6276

6377
@GetMapping("/{clubId}/join-requests")
6478
@Operation(summary = "대기 중인 가입 신청 목록", description = "동호회의 대기 중인 가입 신청 목록을 조회합니다. 동호회장만 가능합니다.")
65-
public ResponseEntity<BaseResponse<List<JoinClubRequestResponse>>> getPendingRequests(
79+
public ResponseEntity<BaseResponse<List<JoinClubRequestSummaryResponse>>> getPendingRequests(
6680
@Parameter(description = "동호회 ID") @PathVariable Long clubId,
6781
@AuthenticationPrincipal UserAuthInfo userAuthInfo) {
68-
List<JoinClubRequestResponse> response = joinClubRequestService.getPendingRequests(clubId, userAuthInfo.getId());
82+
List<JoinClubRequestSummaryResponse> response = joinClubRequestService.getPendingRequests(clubId, userAuthInfo.getId());
6983
return ResponseEntity.ok(BaseResponse.success("가입 신청 목록 조회 성공", response));
7084
}
7185

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.be.sportizebe.domain.notification.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.Size;
6+
7+
@Schema(title = "JoinClubApplicationRequest DTO", description = "가입 신청 요청")
8+
public record JoinClubApplicationRequest(
9+
@NotBlank(message = "가입 신청서를 작성해주세요.")
10+
@Schema(description = "가입 신청서", example = "안녕하세요! 열심히 활동하겠습니다.")
11+
@Size(max = 200, message = "최대 200자 이하로 작성해주세요.")
12+
String message
13+
) {
14+
}

src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestResponse.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ public record JoinClubRequestResponse(
1212
@Schema(description = "가입 신청 ID") Long id,
1313
@Schema(description = "신청자 ID") Long userId,
1414
@Schema(description = "신청자 닉네임") String userNickname,
15+
@Schema(description = "신청자 한줄 소개") String userIntroduce,
1516
@Schema(description = "신청자 프로필 이미지") String userProfileImage,
1617
@Schema(description = "동호회 ID") Long clubId,
1718
@Schema(description = "동호회 이름") String clubName,
19+
@Schema(description = "가입 신청서") String message,
1820
@Schema(description = "신청 상태") JoinClubRequest.JoinClubRequestStatus status,
1921
@Schema(description = "신청 일시") LocalDateTime createdAt
2022
) {
@@ -23,9 +25,11 @@ public static JoinClubRequestResponse from(JoinClubRequest request) {
2325
.id(request.getId())
2426
.userId(request.getUser().getId())
2527
.userNickname(request.getUser().getNickname())
28+
.userIntroduce(request.getUser().getIntroduce())
2629
.userProfileImage(request.getUser().getProfileImage())
2730
.clubId(request.getClub().getId())
2831
.clubName(request.getClub().getName())
32+
.message(request.getMessage())
2933
.status(request.getStatus())
3034
.createdAt(request.getCreatedAt())
3135
.build();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.be.sportizebe.domain.notification.dto.response;
2+
3+
import com.be.sportizebe.domain.notification.entity.JoinClubRequest;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.Builder;
6+
7+
@Builder
8+
@Schema(title = "JoinClubRequestSummaryResponse DTO", description = "가입 신청 목록 응답 (요약)")
9+
public record JoinClubRequestSummaryResponse(
10+
@Schema(description = "가입 신청 ID") Long id,
11+
@Schema(description = "신청자 ID") Long userId,
12+
@Schema(description = "신청자 닉네임") String userNickname,
13+
@Schema(description = "동호회 ID") Long clubId,
14+
@Schema(description = "동호회 이름") String clubName
15+
) {
16+
public static JoinClubRequestSummaryResponse from(JoinClubRequest request) {
17+
return JoinClubRequestSummaryResponse.builder()
18+
.id(request.getId())
19+
.userId(request.getUser().getId())
20+
.userNickname(request.getUser().getNickname())
21+
.clubId(request.getClub().getId())
22+
.clubName(request.getClub().getName())
23+
.build();
24+
}
25+
}

src/main/java/com/be/sportizebe/domain/notification/entity/JoinClubRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ public enum JoinClubRequestStatus {
3737
@JoinColumn(name = "club_id", nullable = false)
3838
private Club club; // 가입 신청 대상 동호회
3939

40+
@Column(nullable = false, columnDefinition = "TEXT")
41+
private String message; // 가입 신청서
42+
4043
@Enumerated(EnumType.STRING)
4144
@Column(nullable = false)
4245
@Builder.Default

src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package com.be.sportizebe.domain.notification.service;
22

33
import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestResponse;
4+
import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestSummaryResponse;
45

56
import java.util.List;
67

78
public interface JoinClubRequestService {
89

910
// 가입 신청
10-
JoinClubRequestResponse requestJoin(Long clubId, Long userId);
11+
JoinClubRequestResponse requestJoin(Long clubId, Long userId, String message);
12+
13+
// 가입 신청 단건 조회 (동호회장 또는 신청자 본인)
14+
JoinClubRequestResponse getJoinRequest(Long requestId, Long userId);
1115

1216
// 가입 신청 취소
1317
void cancelRequest(Long requestId, Long userId);
@@ -19,7 +23,7 @@ public interface JoinClubRequestService {
1923
JoinClubRequestResponse rejectRequest(Long requestId, Long leaderId);
2024

2125
// 동호회의 대기 중인 가입 신청 목록 조회 (동호회장만)
22-
List<JoinClubRequestResponse> getPendingRequests(Long clubId, Long leaderId);
26+
List<JoinClubRequestSummaryResponse> getPendingRequests(Long clubId, Long leaderId);
2327

2428
// 내 가입 신청 목록 조회
2529
List<JoinClubRequestResponse> getMyRequests(Long userId);

src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.be.sportizebe.domain.club.repository.ClubMemberRepository;
77
import com.be.sportizebe.domain.club.repository.ClubRepository;
88
import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestResponse;
9+
import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestSummaryResponse;
910
import com.be.sportizebe.domain.notification.entity.JoinClubRequest;
1011
import com.be.sportizebe.domain.notification.exception.JoinClubRequestErrorCode;
1112
import com.be.sportizebe.domain.notification.repository.JoinClubRequestRepository;
@@ -34,7 +35,7 @@ public class JoinClubRequestServiceImpl implements JoinClubRequestService {
3435

3536
@Override
3637
@Transactional
37-
public JoinClubRequestResponse requestJoin(Long clubId, Long userId) {
38+
public JoinClubRequestResponse requestJoin(Long clubId, Long userId, String message) {
3839
User user = findUserById(userId);
3940
Club club = findClubById(clubId);
4041

@@ -62,6 +63,7 @@ public JoinClubRequestResponse requestJoin(Long clubId, Long userId) {
6263
JoinClubRequest joinRequest = JoinClubRequest.builder()
6364
.user(user)
6465
.club(club)
66+
.message(message)
6567
.build();
6668
joinClubRequestRepository.save(joinRequest);
6769

@@ -75,6 +77,20 @@ public JoinClubRequestResponse requestJoin(Long clubId, Long userId) {
7577
return JoinClubRequestResponse.from(joinRequest);
7678
}
7779

80+
@Override
81+
public JoinClubRequestResponse getJoinRequest(Long requestId, Long userId) {
82+
JoinClubRequest request = findRequestById(requestId);
83+
84+
// 동호회장 또는 신청자 본인만 조회 가능
85+
boolean isLeader = request.getClub().isLeader(userId);
86+
boolean isApplicant = userId.equals(request.getUser().getId());
87+
if (!isLeader && !isApplicant) {
88+
throw new CustomException(ClubErrorCode.CLUB_UPDATE_DENIED);
89+
}
90+
91+
return JoinClubRequestResponse.from(request);
92+
}
93+
7894
@Override
7995
@Transactional
8096
public void cancelRequest(Long requestId, Long userId) {
@@ -160,7 +176,7 @@ public JoinClubRequestResponse rejectRequest(Long requestId, Long leaderId) {
160176
}
161177

162178
@Override
163-
public List<JoinClubRequestResponse> getPendingRequests(Long clubId, Long leaderId) {
179+
public List<JoinClubRequestSummaryResponse> getPendingRequests(Long clubId, Long leaderId) {
164180
Club club = findClubById(clubId);
165181

166182
// 동호회장만 조회 가능
@@ -171,7 +187,7 @@ public List<JoinClubRequestResponse> getPendingRequests(Long clubId, Long leader
171187
return joinClubRequestRepository
172188
.findByClubAndStatus(club, JoinClubRequest.JoinClubRequestStatus.PENDING)
173189
.stream()
174-
.map(JoinClubRequestResponse::from)
190+
.map(JoinClubRequestSummaryResponse::from)
175191
.toList();
176192
}
177193

src/main/resources

0 commit comments

Comments
 (0)