From 415f6af35ae7240c139c320a9dd754141fad01f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=9B=88?= <2dh2@naver.com> Date: Tue, 27 Jan 2026 16:09:50 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=EC=9D=84=20=EA=B0=80=EC=A7=80=EB=8A=94=20=EB=8F=99?= =?UTF-8?q?=EC=95=84=EB=A6=AC=20=EC=A1=B0=ED=9A=8C=20API=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 --- .../domain/club/controller/ClubApi.java | 12 +++++++ .../club/controller/ClubController.java | 10 ++++++ .../club/dto/MyManagedClubResponse.java | 36 +++++++++++++++++++ .../club/repository/ClubMemberRepository.java | 14 ++++++++ .../domain/club/service/ClubService.java | 19 +++++++++- 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/main/java/gg/agit/konect/domain/club/dto/MyManagedClubResponse.java diff --git a/src/main/java/gg/agit/konect/domain/club/controller/ClubApi.java b/src/main/java/gg/agit/konect/domain/club/controller/ClubApi.java index b3bc6343..8f9dcd6b 100644 --- a/src/main/java/gg/agit/konect/domain/club/controller/ClubApi.java +++ b/src/main/java/gg/agit/konect/domain/club/controller/ClubApi.java @@ -38,6 +38,7 @@ import gg.agit.konect.domain.club.dto.ClubRecruitmentUpdateRequest; import gg.agit.konect.domain.club.dto.ClubTagsResponse; import gg.agit.konect.domain.club.dto.ClubsResponse; +import gg.agit.konect.domain.club.dto.MyManagedClubResponse; import gg.agit.konect.domain.club.dto.MemberPositionChangeRequest; import gg.agit.konect.domain.club.dto.PresidentTransferRequest; import gg.agit.konect.domain.club.dto.VicePresidentChangeRequest; @@ -153,6 +154,17 @@ ResponseEntity getManagedClubs( @UserId Integer userId ); + @Operation(summary = "관리자 권한을 가지고 있는 동아리 단건을 조회한다.", description = """ + ## 에러 + - FORBIDDEN_CLUB_MANAGER_ACCESS (403): 동아리 매니저 권한이 없습니다. + - NOT_FOUND_CLUB (404): 동아리를 찾을 수 없습니다. + """) + @GetMapping("/managed/{clubId}") + ResponseEntity getManagedClubDetail( + @PathVariable(name = "clubId") Integer clubId, + @UserId Integer userId + ); + @Operation(summary = "가입 승인 대기 중인 동아리 리스트를 조회한다.") @GetMapping("/applied") ResponseEntity getAppliedClubs( diff --git a/src/main/java/gg/agit/konect/domain/club/controller/ClubController.java b/src/main/java/gg/agit/konect/domain/club/controller/ClubController.java index 7fc747c4..6746b9d5 100644 --- a/src/main/java/gg/agit/konect/domain/club/controller/ClubController.java +++ b/src/main/java/gg/agit/konect/domain/club/controller/ClubController.java @@ -34,6 +34,7 @@ import gg.agit.konect.domain.club.dto.ClubRecruitmentUpdateRequest; import gg.agit.konect.domain.club.dto.ClubTagsResponse; import gg.agit.konect.domain.club.dto.ClubsResponse; +import gg.agit.konect.domain.club.dto.MyManagedClubResponse; import gg.agit.konect.domain.club.dto.MemberPositionChangeRequest; import gg.agit.konect.domain.club.dto.PresidentTransferRequest; import gg.agit.konect.domain.club.dto.VicePresidentChangeRequest; @@ -130,6 +131,15 @@ public ResponseEntity getManagedClubs( return ResponseEntity.ok(response); } + @Override + public ResponseEntity getManagedClubDetail( + @PathVariable(name = "clubId") Integer clubId, + @UserId Integer userId + ) { + MyManagedClubResponse response = clubService.getManagedClubDetail(clubId, userId); + return ResponseEntity.ok(response); + } + @Override public ResponseEntity getAppliedClubs( @UserId Integer userId diff --git a/src/main/java/gg/agit/konect/domain/club/dto/MyManagedClubResponse.java b/src/main/java/gg/agit/konect/domain/club/dto/MyManagedClubResponse.java new file mode 100644 index 00000000..8421e34b --- /dev/null +++ b/src/main/java/gg/agit/konect/domain/club/dto/MyManagedClubResponse.java @@ -0,0 +1,36 @@ +package gg.agit.konect.domain.club.dto; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import gg.agit.konect.domain.club.model.Club; +import gg.agit.konect.domain.club.model.ClubMember; +import gg.agit.konect.domain.user.model.User; +import io.swagger.v3.oas.annotations.media.Schema; + +public record MyManagedClubResponse( + @Schema(description = "동아리 고유 ID", example = "1", requiredMode = REQUIRED) + Integer clubId, + + @Schema(description = "동아리 이름", example = "BCSD", requiredMode = REQUIRED) + String clubName, + + @Schema(description = "회원 이름", example = "배진호", requiredMode = REQUIRED) + String name, + + @Schema(description = "회원 학번", example = "2020136061", requiredMode = REQUIRED) + String studentNumber, + + @Schema(description = "직책", example = "회장", requiredMode = REQUIRED) + String position +) { + public static MyManagedClubResponse from(Club club, ClubMember clubMember) { + User user = clubMember.getUser(); + return new MyManagedClubResponse( + club.getId(), + club.getName(), + user.getName(), + user.getStudentNumber(), + clubMember.getClubPosition().getName() + ); + } +} diff --git a/src/main/java/gg/agit/konect/domain/club/repository/ClubMemberRepository.java b/src/main/java/gg/agit/konect/domain/club/repository/ClubMemberRepository.java index 16f375f6..b44b2d11 100644 --- a/src/main/java/gg/agit/konect/domain/club/repository/ClubMemberRepository.java +++ b/src/main/java/gg/agit/konect/domain/club/repository/ClubMemberRepository.java @@ -79,6 +79,19 @@ List findAllByUserIdAndClubPosition( @Param("clubPositionGroup") ClubPositionGroup clubPositionGroup ); + @Query(""" + SELECT cm + FROM ClubMember cm + JOIN FETCH cm.club c + JOIN FETCH cm.clubPosition cp + WHERE cm.id.userId = :userId + AND cp.clubPositionGroup IN :clubPositionGroups + """) + List findAllByUserIdAndClubPositionGroups( + @Param("userId") Integer userId, + @Param("clubPositionGroups") Set clubPositionGroups + ); + @Query(""" SELECT COUNT(cm) > 0 FROM ClubMember cm @@ -96,6 +109,7 @@ boolean existsByClubIdAndUserIdAndPositionGroupIn( @Query(""" SELECT cm FROM ClubMember cm + JOIN FETCH cm.user JOIN FETCH cm.clubPosition WHERE cm.club.id = :clubId AND cm.user.id = :userId diff --git a/src/main/java/gg/agit/konect/domain/club/service/ClubService.java b/src/main/java/gg/agit/konect/domain/club/service/ClubService.java index 7d7ddec8..da843ccb 100644 --- a/src/main/java/gg/agit/konect/domain/club/service/ClubService.java +++ b/src/main/java/gg/agit/konect/domain/club/service/ClubService.java @@ -43,6 +43,7 @@ import gg.agit.konect.domain.club.dto.ClubRecruitmentUpdateRequest; import gg.agit.konect.domain.club.dto.ClubTagsResponse; import gg.agit.konect.domain.club.dto.ClubsResponse; +import gg.agit.konect.domain.club.dto.MyManagedClubResponse; import gg.agit.konect.domain.club.enums.ClubPositionGroup; import gg.agit.konect.domain.club.model.Club; import gg.agit.konect.domain.club.model.ClubApply; @@ -81,6 +82,8 @@ public class ClubService { EnumSet.of(PRESIDENT); private static final Set MANAGER_ALLOWED_GROUPS = EnumSet.of(PRESIDENT, MANAGER); + private static final Set MANAGER_OR_HIGHER_ALLOWED_GROUPS = + EnumSet.of(PRESIDENT, VICE_PRESIDENT, MANAGER); private static final Set LEADER_ALLOWED_GROUPS = EnumSet.of(PRESIDENT, VICE_PRESIDENT); @@ -238,10 +241,24 @@ public ClubMembershipsResponse getJoinedClubs(Integer userId) { } public ClubMembershipsResponse getManagedClubs(Integer userId) { - List clubMembers = clubMemberRepository.findAllByUserIdAndClubPosition(userId, PRESIDENT); + List clubMembers = clubMemberRepository.findAllByUserIdAndClubPositionGroups( + userId, + MANAGER_OR_HIGHER_ALLOWED_GROUPS + ); return ClubMembershipsResponse.from(clubMembers); } + public MyManagedClubResponse getManagedClubDetail(Integer clubId, Integer userId) { + Club club = clubRepository.getById(clubId); + + if (!hasClubManageAccess(clubId, userId, MANAGER_OR_HIGHER_ALLOWED_GROUPS)) { + throw CustomException.of(FORBIDDEN_CLUB_MANAGER_ACCESS); + } + + ClubMember clubMember = clubMemberRepository.getByClubIdAndUserId(clubId, userId); + return MyManagedClubResponse.from(club, clubMember); + } + public ClubAppliedClubsResponse getAppliedClubs(Integer userId) { List clubApplies = clubApplyRepository.findAllPendingByUserIdWithClub(userId); return ClubAppliedClubsResponse.from(clubApplies);