From ad4151a5fe140415495ed03d6e8043f514c3e202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Thu, 26 Feb 2026 15:24:07 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=20=E2=9C=A8Feat:=20=EB=8F=99=ED=98=B8?= =?UTF-8?q?=ED=9A=8C=20=EA=B0=80=EC=9E=85=20=EC=8B=A0=EC=B2=AD=EC=84=9C(me?= =?UTF-8?q?ssage)=20=EC=9E=91=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/JoinClubRequestController.java | 6 +++++- .../dto/request/JoinClubApplicationRequest.java | 14 ++++++++++++++ .../dto/response/JoinClubRequestResponse.java | 4 ++++ .../notification/entity/JoinClubRequest.java | 3 +++ .../service/JoinClubRequestService.java | 2 +- .../service/JoinClubRequestServiceImpl.java | 3 ++- 6 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/be/sportizebe/domain/notification/dto/request/JoinClubApplicationRequest.java diff --git a/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java b/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java index 4dab40c..4c92dba 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java +++ b/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java @@ -1,5 +1,6 @@ package com.be.sportizebe.domain.notification.controller; +import com.be.sportizebe.domain.notification.dto.request.JoinClubApplicationRequest; import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestResponse; import com.be.sportizebe.domain.notification.service.JoinClubRequestService; import com.be.sportizebe.global.cache.dto.UserAuthInfo; @@ -7,10 +8,12 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -27,8 +30,9 @@ public class JoinClubRequestController { @Operation(summary = "가입 신청", description = "동호회에 가입을 신청합니다. 동호회장에게 알림이 전송됩니다.") public ResponseEntity> requestJoin( @Parameter(description = "동호회 ID") @PathVariable Long clubId, + @Valid @RequestBody JoinClubApplicationRequest request, @AuthenticationPrincipal UserAuthInfo userAuthInfo) { - JoinClubRequestResponse response = joinClubRequestService.requestJoin(clubId, userAuthInfo.getId()); + JoinClubRequestResponse response = joinClubRequestService.requestJoin(clubId, userAuthInfo.getId(), request.message()); return ResponseEntity.status(HttpStatus.CREATED) .body(BaseResponse.success("가입 신청 완료", response)); } diff --git a/src/main/java/com/be/sportizebe/domain/notification/dto/request/JoinClubApplicationRequest.java b/src/main/java/com/be/sportizebe/domain/notification/dto/request/JoinClubApplicationRequest.java new file mode 100644 index 0000000..088772f --- /dev/null +++ b/src/main/java/com/be/sportizebe/domain/notification/dto/request/JoinClubApplicationRequest.java @@ -0,0 +1,14 @@ +package com.be.sportizebe.domain.notification.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +@Schema(title = "JoinClubApplicationRequest DTO", description = "가입 신청 요청") +public record JoinClubApplicationRequest( + @NotBlank(message = "가입 신청서를 작성해주세요.") + @Schema(description = "가입 신청서", example = "안녕하세요! 열심히 활동하겠습니다.") + @Size(max = 200, message = "최대 200자 이하로 작성해주세요.") + String message +) { +} diff --git a/src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestResponse.java b/src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestResponse.java index 1a1517c..93f0d97 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestResponse.java +++ b/src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestResponse.java @@ -12,9 +12,11 @@ public record JoinClubRequestResponse( @Schema(description = "가입 신청 ID") Long id, @Schema(description = "신청자 ID") Long userId, @Schema(description = "신청자 닉네임") String userNickname, + @Schema(description = "신청자 한줄 소개") String userIntroduce, @Schema(description = "신청자 프로필 이미지") String userProfileImage, @Schema(description = "동호회 ID") Long clubId, @Schema(description = "동호회 이름") String clubName, + @Schema(description = "가입 신청서") String message, @Schema(description = "신청 상태") JoinClubRequest.JoinClubRequestStatus status, @Schema(description = "신청 일시") LocalDateTime createdAt ) { @@ -23,9 +25,11 @@ public static JoinClubRequestResponse from(JoinClubRequest request) { .id(request.getId()) .userId(request.getUser().getId()) .userNickname(request.getUser().getNickname()) + .userIntroduce(request.getUser().getIntroduce()) .userProfileImage(request.getUser().getProfileImage()) .clubId(request.getClub().getId()) .clubName(request.getClub().getName()) + .message(request.getMessage()) .status(request.getStatus()) .createdAt(request.getCreatedAt()) .build(); diff --git a/src/main/java/com/be/sportizebe/domain/notification/entity/JoinClubRequest.java b/src/main/java/com/be/sportizebe/domain/notification/entity/JoinClubRequest.java index 409cc5b..94d25c1 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/entity/JoinClubRequest.java +++ b/src/main/java/com/be/sportizebe/domain/notification/entity/JoinClubRequest.java @@ -37,6 +37,9 @@ public enum JoinClubRequestStatus { @JoinColumn(name = "club_id", nullable = false) private Club club; // 가입 신청 대상 동호회 + @Column(nullable = false, columnDefinition = "TEXT") + private String message; // 가입 신청서 + @Enumerated(EnumType.STRING) @Column(nullable = false) @Builder.Default diff --git a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java index fbe9ddd..61c739d 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java +++ b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java @@ -7,7 +7,7 @@ public interface JoinClubRequestService { // 가입 신청 - JoinClubRequestResponse requestJoin(Long clubId, Long userId); + JoinClubRequestResponse requestJoin(Long clubId, Long userId, String message); // 가입 신청 취소 void cancelRequest(Long requestId, Long userId); diff --git a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java index 8265f28..77dfd5c 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java +++ b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java @@ -34,7 +34,7 @@ public class JoinClubRequestServiceImpl implements JoinClubRequestService { @Override @Transactional - public JoinClubRequestResponse requestJoin(Long clubId, Long userId) { + public JoinClubRequestResponse requestJoin(Long clubId, Long userId, String message) { User user = findUserById(userId); Club club = findClubById(clubId); @@ -62,6 +62,7 @@ public JoinClubRequestResponse requestJoin(Long clubId, Long userId) { JoinClubRequest joinRequest = JoinClubRequest.builder() .user(user) .club(club) + .message(message) .build(); joinClubRequestRepository.save(joinRequest); From 87fb0c016174a906fb5999b52b310565661d1e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Thu, 26 Feb 2026 16:04:48 +0900 Subject: [PATCH 2/4] =?UTF-8?q?:sparkles:Feat:=20=EB=8F=99=ED=98=B8?= =?UTF-8?q?=ED=9A=8C=20=EA=B0=80=EC=9E=85=20=EC=8B=A0=EC=B2=AD=20=EB=8B=A8?= =?UTF-8?q?=20=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/JoinClubRequestController.java | 9 +++++++++ .../service/JoinClubRequestService.java | 3 +++ .../service/JoinClubRequestServiceImpl.java | 14 ++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java b/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java index 4c92dba..a304816 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java +++ b/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java @@ -37,6 +37,15 @@ public ResponseEntity> requestJoin( .body(BaseResponse.success("가입 신청 완료", response)); } + @GetMapping("/join-requests/{requestId}") + @Operation(summary = "가입 신청 단건 조회", description = "가입 신청 상세 정보를 조회합니다. 동호회장 또는 신청자 본인만 가능합니다.") + public ResponseEntity> getJoinRequest( + @Parameter(description = "가입 신청 ID") @PathVariable Long requestId, + @AuthenticationPrincipal UserAuthInfo userAuthInfo) { + JoinClubRequestResponse response = joinClubRequestService.getJoinRequest(requestId, userAuthInfo.getId()); + return ResponseEntity.ok(BaseResponse.success("가입 신청 조회 성공", response)); + } + @DeleteMapping("/join-requests/{requestId}") @Operation(summary = "가입 신청 취소", description = "본인의 가입 신청을 취소합니다.") public ResponseEntity> cancelRequest( diff --git a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java index 61c739d..8075654 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java +++ b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java @@ -9,6 +9,9 @@ public interface JoinClubRequestService { // 가입 신청 JoinClubRequestResponse requestJoin(Long clubId, Long userId, String message); + // 가입 신청 단건 조회 (동호회장 또는 신청자 본인) + JoinClubRequestResponse getJoinRequest(Long requestId, Long userId); + // 가입 신청 취소 void cancelRequest(Long requestId, Long userId); diff --git a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java index 77dfd5c..f693831 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java +++ b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java @@ -76,6 +76,20 @@ public JoinClubRequestResponse requestJoin(Long clubId, Long userId, String mess return JoinClubRequestResponse.from(joinRequest); } + @Override + public JoinClubRequestResponse getJoinRequest(Long requestId, Long userId) { + JoinClubRequest request = findRequestById(requestId); + + // 동호회장 또는 신청자 본인만 조회 가능 + boolean isLeader = request.getClub().isLeader(userId); + boolean isApplicant = request.getUser().getId().equals(userId); + if (!isLeader && !isApplicant) { + throw new CustomException(ClubErrorCode.CLUB_UPDATE_DENIED); + } + + return JoinClubRequestResponse.from(request); + } + @Override @Transactional public void cancelRequest(Long requestId, Long userId) { From d9a5cdcf669109698593bd44b3f4b2c84a72b8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Thu, 26 Feb 2026 16:20:04 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E2=99=BB=EF=B8=8FRefactor:=20=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=A0=EC=B2=AD=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=9D=91=EB=8B=B5=EC=9D=84=20=EA=B2=BD=EB=9F=89=20?= =?UTF-8?q?DTO(JoinClubRequestSummaryResponse)=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/JoinClubRequestController.java | 5 ++-- .../JoinClubRequestSummaryResponse.java | 25 +++++++++++++++++++ .../service/JoinClubRequestService.java | 3 ++- .../service/JoinClubRequestServiceImpl.java | 7 +++--- 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestSummaryResponse.java diff --git a/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java b/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java index a304816..a4a79f5 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java +++ b/src/main/java/com/be/sportizebe/domain/notification/controller/JoinClubRequestController.java @@ -2,6 +2,7 @@ import com.be.sportizebe.domain.notification.dto.request.JoinClubApplicationRequest; import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestResponse; +import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestSummaryResponse; import com.be.sportizebe.domain.notification.service.JoinClubRequestService; import com.be.sportizebe.global.cache.dto.UserAuthInfo; import com.be.sportizebe.global.response.BaseResponse; @@ -75,10 +76,10 @@ public ResponseEntity> rejectRequest( @GetMapping("/{clubId}/join-requests") @Operation(summary = "대기 중인 가입 신청 목록", description = "동호회의 대기 중인 가입 신청 목록을 조회합니다. 동호회장만 가능합니다.") - public ResponseEntity>> getPendingRequests( + public ResponseEntity>> getPendingRequests( @Parameter(description = "동호회 ID") @PathVariable Long clubId, @AuthenticationPrincipal UserAuthInfo userAuthInfo) { - List response = joinClubRequestService.getPendingRequests(clubId, userAuthInfo.getId()); + List response = joinClubRequestService.getPendingRequests(clubId, userAuthInfo.getId()); return ResponseEntity.ok(BaseResponse.success("가입 신청 목록 조회 성공", response)); } diff --git a/src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestSummaryResponse.java b/src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestSummaryResponse.java new file mode 100644 index 0000000..f3eb847 --- /dev/null +++ b/src/main/java/com/be/sportizebe/domain/notification/dto/response/JoinClubRequestSummaryResponse.java @@ -0,0 +1,25 @@ +package com.be.sportizebe.domain.notification.dto.response; + +import com.be.sportizebe.domain.notification.entity.JoinClubRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +@Builder +@Schema(title = "JoinClubRequestSummaryResponse DTO", description = "가입 신청 목록 응답 (요약)") +public record JoinClubRequestSummaryResponse( + @Schema(description = "가입 신청 ID") Long id, + @Schema(description = "신청자 ID") Long userId, + @Schema(description = "신청자 닉네임") String userNickname, + @Schema(description = "동호회 ID") Long clubId, + @Schema(description = "동호회 이름") String clubName +) { + public static JoinClubRequestSummaryResponse from(JoinClubRequest request) { + return JoinClubRequestSummaryResponse.builder() + .id(request.getId()) + .userId(request.getUser().getId()) + .userNickname(request.getUser().getNickname()) + .clubId(request.getClub().getId()) + .clubName(request.getClub().getName()) + .build(); + } +} diff --git a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java index 8075654..104a7c9 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java +++ b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestService.java @@ -1,6 +1,7 @@ package com.be.sportizebe.domain.notification.service; import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestResponse; +import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestSummaryResponse; import java.util.List; @@ -22,7 +23,7 @@ public interface JoinClubRequestService { JoinClubRequestResponse rejectRequest(Long requestId, Long leaderId); // 동호회의 대기 중인 가입 신청 목록 조회 (동호회장만) - List getPendingRequests(Long clubId, Long leaderId); + List getPendingRequests(Long clubId, Long leaderId); // 내 가입 신청 목록 조회 List getMyRequests(Long userId); diff --git a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java index f693831..9714e82 100644 --- a/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java +++ b/src/main/java/com/be/sportizebe/domain/notification/service/JoinClubRequestServiceImpl.java @@ -6,6 +6,7 @@ import com.be.sportizebe.domain.club.repository.ClubMemberRepository; import com.be.sportizebe.domain.club.repository.ClubRepository; import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestResponse; +import com.be.sportizebe.domain.notification.dto.response.JoinClubRequestSummaryResponse; import com.be.sportizebe.domain.notification.entity.JoinClubRequest; import com.be.sportizebe.domain.notification.exception.JoinClubRequestErrorCode; import com.be.sportizebe.domain.notification.repository.JoinClubRequestRepository; @@ -82,7 +83,7 @@ public JoinClubRequestResponse getJoinRequest(Long requestId, Long userId) { // 동호회장 또는 신청자 본인만 조회 가능 boolean isLeader = request.getClub().isLeader(userId); - boolean isApplicant = request.getUser().getId().equals(userId); + boolean isApplicant = userId.equals(request.getUser().getId()); if (!isLeader && !isApplicant) { throw new CustomException(ClubErrorCode.CLUB_UPDATE_DENIED); } @@ -175,7 +176,7 @@ public JoinClubRequestResponse rejectRequest(Long requestId, Long leaderId) { } @Override - public List getPendingRequests(Long clubId, Long leaderId) { + public List getPendingRequests(Long clubId, Long leaderId) { Club club = findClubById(clubId); // 동호회장만 조회 가능 @@ -186,7 +187,7 @@ public List getPendingRequests(Long clubId, Long leader return joinClubRequestRepository .findByClubAndStatus(club, JoinClubRequest.JoinClubRequestStatus.PENDING) .stream() - .map(JoinClubRequestResponse::from) + .map(JoinClubRequestSummaryResponse::from) .toList(); } From 585be7a69793cb37ee97308d80e7aef6af254cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Sat, 28 Feb 2026 22:34:32 +0900 Subject: [PATCH 4/4] submodule pointer update --- src/main/resources | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources b/src/main/resources index 05d0469..b0b5aca 160000 --- a/src/main/resources +++ b/src/main/resources @@ -1 +1 @@ -Subproject commit 05d0469d24d5c627fe5a94af9f6a7e797be874e6 +Subproject commit b0b5acaeef6cc2e32624b2f407d152dc2f5b5d4b