Skip to content

Commit 550ba06

Browse files
authored
Merge pull request #35 from S-nect1/refactor/19-club
✨ feat: 유저 내보내기 기능 구현
2 parents 1b32e5d + 4321682 commit 550ba06

16 files changed

Lines changed: 125 additions & 22 deletions

src/main/java/com/example/moim/club/controller/ClubController.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ public class ClubController implements ClubControllerDocs{
2828

2929
private final UserRepository userRepository;
3030

31-
@PostMapping(value = "/club", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
31+
@PostMapping(value = "/clubs", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
3232
public BaseResponse<ClubSaveOutput> clubSave(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @ModelAttribute @Valid ClubInput clubInput) throws IOException {
3333
// public BaseResponse<ClubSaveOutput> clubSave(@ModelAttribute @Valid ClubInput clubInput) throws IOException {
3434
// User user = userRepository.findById(1L).get();
3535
return BaseResponse.onSuccess(clubCommandService.saveClub(userDetailsImpl.getUser(), clubInput), ResponseCode.OK);
3636
}
3737

38-
@PatchMapping(value = "/club/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
38+
@PatchMapping(value = "/clubs/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
3939
public BaseResponse<ClubUpdateOutput> clubUpdate(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @ModelAttribute ClubUpdateInput clubUpdateInput, @PathVariable("id") Long clubId) throws IOException {
4040
// public BaseResponse<ClubUpdateOutput> clubUpdate(@ModelAttribute ClubUpdateInput clubUpdateInput, @PathVariable("id") Long clubId) throws IOException {
4141
// User user = userRepository.findById(1L).get();
@@ -48,7 +48,7 @@ public BaseResponse<List<ClubSearchOutput>> findClubs(@AuthenticationPrincipal U
4848
return BaseResponse.onSuccess(clubQueryService.searchClub(clubSearchCond), ResponseCode.OK);
4949
}
5050

51-
@PostMapping("/club/{id}/join")
51+
@PostMapping("/clubs/{id}/join")
5252
public BaseResponse<UserClubOutput> clubUserSave(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestBody ClubUserSaveInput clubUserSaveInput, @PathVariable("id") Long clubId) {
5353
// public BaseResponse<UserClubOutput> clubUserSave(@RequestBody ClubUserSaveInput clubUserSaveInput, @PathVariable("id") Long clubId) {
5454
// User user = userRepository.findById(1L).get();
@@ -60,26 +60,30 @@ public BaseResponse<UserClubOutput> clubUserSave(@AuthenticationPrincipal UserDe
6060
// return clubService.inviteClubUser(userDetailsImpl.getUser(), clubInviteInput);
6161
// }
6262

63-
@PatchMapping("/club/{id}/role")
63+
@DeleteMapping("/clubs/{id}/users")
64+
public BaseResponse<String> clubUserDelete(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @PathVariable("id") Long clubId, @RequestBody ClubUserDeleteInput clubUserDeleteInput) {
65+
return BaseResponse.onSuccess(clubCommandService.deleteClubUser(userDetailsImpl.getUser(), clubId, clubUserDeleteInput.getUserId()), ResponseCode.OK);
66+
}
67+
68+
@PatchMapping("/clubs/{id}/role")
6469
public BaseResponse<UserClubOutput> clubUserUpdate(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestBody ClubUserUpdateInput clubInput, @PathVariable("id") Long clubId) {
6570
// public BaseResponse<UserClubOutput> clubUserUpdate(@RequestBody ClubUserUpdateInput clubInput, @PathVariable("id") Long clubId) {
6671
// User user = userRepository.findById(1L).get();
6772
return BaseResponse.onSuccess(clubCommandService.updateClubUser(userDetailsImpl.getUser(), clubInput, clubId), ResponseCode.OK);
6873
}
6974

70-
@GetMapping("/club/{id}")
75+
@GetMapping("/clubs/{id}")
7176
public BaseResponse<ClubOutput> clubFind(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @PathVariable("id") Long clubId) {
7277
// public BaseResponse<ClubOutput> clubFind(@Parameter(description = "조회할 모임 ID", required = true, example = "1") @PathVariable("id") Long id) {
7378
// User user = userRepository.findById(1L).get();
7479
return BaseResponse.onSuccess(clubQueryService.findClub(clubId, userDetailsImpl.getUser()), ResponseCode.OK);
7580
}
7681

77-
@PatchMapping(value = "/club/{id}/password", consumes = MediaType.APPLICATION_JSON_VALUE)
78-
public BaseResponse clubPasswordUpdate(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestBody @Valid ClubPswdUpdateInput clubPswdUpdateInput, @PathVariable("id") Long clubId) {
82+
@PatchMapping(value = "/clubs/{id}/password", consumes = MediaType.APPLICATION_JSON_VALUE)
83+
public BaseResponse<String> clubPasswordUpdate(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestBody @Valid ClubPswdUpdateInput clubPswdUpdateInput, @PathVariable("id") Long clubId) {
7984
// public BaseResponse clubPasswordUpdate(@RequestBody @Valid ClubPswdUpdateInput clubPswdUpdateInput, @PathVariable("id") Long clubId) {
8085
// User user = userRepository.findById(1L).get();
81-
clubCommandService.clubPasswordUpdate(userDetailsImpl.getUser(), clubPswdUpdateInput, clubId);
82-
return BaseResponse.onSuccess(null, ResponseCode.OK);
86+
return BaseResponse.onSuccess(clubCommandService.clubPasswordUpdate(userDetailsImpl.getUser(), clubPswdUpdateInput, clubId), ResponseCode.OK);
8387
}
8488

8589
// @PatchMapping(value = "/club/profile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

src/main/java/com/example/moim/club/controller/ClubControllerDocs.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ public interface ClubControllerDocs {
3333
BaseResponse<UserClubOutput> clubUserSave(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestBody ClubUserSaveInput clubInput, @PathVariable("id") Long clubId);
3434
// BaseResponse<UserClubOutput> clubUserSave(@RequestBody ClubUserSaveInput clubInput, @PathVariable Long clubId);
3535

36+
@Operation(summary = "모임에서 내보내기")
37+
BaseResponse<String> clubUserDelete(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @PathVariable("id") Long clubId, @RequestBody ClubUserDeleteInput clubUserDeleteInput);
38+
3639
// @Operation(summary = "모임에 초대")
3740
// UserClubOutput clubUserInvite(@AuthenticationPrincipal userDetailsImpl userDetailsImpl, @RequestBody ClubUserSaveInput clubInput);
3841

@@ -45,7 +48,7 @@ public interface ClubControllerDocs {
4548
// BaseResponse<ClubOutput> clubFind(@PathVariable("id") Long clubId);
4649

4750
@Operation(summary = "모임 비밀번호 변경")
48-
BaseResponse clubPasswordUpdate(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestBody ClubPswdUpdateInput clubPswdUpdateInput, @PathVariable("id") Long clubId);
51+
BaseResponse<String> clubPasswordUpdate(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestBody ClubPswdUpdateInput clubPswdUpdateInput, @PathVariable("id") Long clubId);
4952
// BaseResponse clubPasswordUpdate(@RequestBody ClubPswdUpdateInput clubPswdUpdateInput, @PathVariable Long clubId);
5053

5154
// @Operation(summary = "모임 사진 변경")

src/main/java/com/example/moim/club/dto/request/ClubInput.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.example.moim.club.dto.request;
22

3+
import com.example.moim.club.exception.advice.ClubControllerAdvice;
4+
import com.example.moim.global.exception.ResponseCode;
35
import jakarta.validation.constraints.NotBlank;
46
import lombok.Builder;
57
import lombok.Data;
@@ -14,9 +16,9 @@ public class ClubInput {
1416
private String title;
1517
@NotBlank(message = "모임 설명을 적어야합니다!")
1618
private String explanation;
17-
@NotBlank(message = "모임 소개를 적어야합니다!")
19+
@NotBlank(message = "모임 한줄 소개를 적어야합니다!")
1820
private String introduction;
19-
@NotBlank(message = "클럽 카테고리를 지정해야합니다!")
21+
@NotBlank(message = "모임 종류를 지정해야합니다!")
2022
private String clubCategory;
2123
private String university;
2224
@NotBlank(message = "성별을 지정해야합니다!")
@@ -29,14 +31,16 @@ public class ClubInput {
2931
private String sportsType;
3032
@NotBlank(message = "모임 비밀번호를 적어야합니다!")
3133
private String clubPassword;
34+
@NotBlank(message = "모임 확인 비밀번호를 적어야합니다!")
35+
private String clubCheckPassword;
3236
private MultipartFile profileImg;
3337
@NotBlank(message = "메인 유니폼 색을 지정해야합니다!")
3438
private String mainUniformColor;
3539
@NotBlank(message = "서브 유니폼 색을 지정해야합니다!")
3640
private String subUniformColor;
3741

3842
@Builder
39-
public ClubInput(String title, String explanation, String introduction, String clubCategory, String university, String gender, String activityArea, String ageRange, String sportsType, String clubPassword, MultipartFile profileImg, String mainUniformColor, String subUniformColor) {
43+
public ClubInput(String title, String explanation, String introduction, String clubCategory, String university, String gender, String activityArea, String ageRange, String sportsType, String clubPassword, String clubCheckPassword,MultipartFile profileImg, String mainUniformColor, String subUniformColor) {
4044
this.title = title;
4145
this.explanation = explanation;
4246
this.introduction = introduction;
@@ -46,6 +50,7 @@ public ClubInput(String title, String explanation, String introduction, String c
4650
this.activityArea = activityArea;
4751
this.ageRange = ageRange;
4852
this.sportsType = sportsType;
53+
this.clubCheckPassword = clubCheckPassword;
4954
this.clubPassword = clubPassword;
5055
this.profileImg = profileImg;
5156
this.mainUniformColor = mainUniformColor;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.example.moim.club.dto.request;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class ClubUserDeleteInput {
7+
private Long userId;
8+
}

src/main/java/com/example/moim/club/entity/Club.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ public static Club createClub(ClubInput clubInput, String profileImgPath) {
7575
club.activityArea = ActivityArea.fromKoreanName(clubInput.getActivityArea()).get();
7676
club.sportsType = SportsType.fromKoreanName(clubInput.getSportsType()).get();
7777
club.ageRange = AgeRange.fromKoreanName(clubInput.getAgeRange()).get();
78+
if (!clubInput.getClubPassword().equals(clubInput.getClubCheckPassword())) {
79+
throw new ClubControllerAdvice(ResponseCode.CLUB_CHECK_PASSWORD_INCORRECT);
80+
}
7881
club.clubPassword = clubInput.getClubPassword();
7982
club.profileImgPath = profileImgPath;
8083
club.mainUniformColor = clubInput.getMainUniformColor();

src/main/java/com/example/moim/club/repository/UserClubRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,6 @@ public interface UserClubRepository extends JpaRepository<UserClub, Long> {
4747
" where uc.club = :club" +
4848
" and (uc.clubRole = 'STAFF')")
4949
List<UserClub> findAdminByClub(@Param("club") Club Club);
50+
51+
void deleteByClubIdAndUserId(Long clubId, Long userId);
5052
}

src/main/java/com/example/moim/club/service/ClubCommandService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public interface ClubCommandService {
1111
ClubSaveOutput saveClub(User user, ClubInput clubInput) throws IOException;
1212
ClubUpdateOutput updateClub(User user, ClubUpdateInput clubUpdateInput, Long clubId) throws IOException;
1313
UserClubOutput saveClubUser(User user, ClubUserSaveInput clubUserSaveInput, Long clubId);
14+
String deleteClubUser(User user, Long clubId, Long userId);
1415
UserClubOutput updateClubUser(User user, ClubUserUpdateInput clubInput, Long clubId);
15-
void clubPasswordUpdate(User user, ClubPswdUpdateInput clubPswdUpdateInput, Long clubId);
16+
String clubPasswordUpdate(User user, ClubPswdUpdateInput clubPswdUpdateInput, Long clubId);
1617

1718
}

src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ public UserClubOutput saveClubUser(User user, ClubUserSaveInput clubUserSaveInpu
9292
throw new ClubControllerAdvice(ResponseCode.CLUB_PASSWORD_INCORRECT);
9393
}
9494

95+
@Transactional
96+
public String deleteClubUser(User user, Long clubId, Long userId) {
97+
98+
User targetUser = getUser(userId);
99+
100+
// 관리자 권한 확인
101+
validateIsStaff(getUserClub(getClub(clubId), user));
102+
103+
userClubRepository.deleteByClubIdAndUserId(clubId, userId);
104+
105+
return targetUser.getName() + "님이 가입에서 내보내졌습니다.";
106+
107+
}
108+
95109
// public UserClubOutput inviteClubUser(User user, ClubInviteInput clubInviteInput) {
96110
// userRepository.findById(clubInviteInput.getUser().)
97111
// }
@@ -112,7 +126,7 @@ public UserClubOutput updateClubUser(User user, ClubUserUpdateInput clubUserUpda
112126
}
113127

114128
@Transactional
115-
public void clubPasswordUpdate(User user, ClubPswdUpdateInput clubPswdUpdateInput, Long clubId) {
129+
public String clubPasswordUpdate(User user, ClubPswdUpdateInput clubPswdUpdateInput, Long clubId) {
116130
Club club = getClub(clubId);
117131
// Club club = clubRepository.findById(clubPswdUpdateInput.getId()).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND));
118132
UserClub userClub = userClubRepository.findByClubAndUser(club, user).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_USER_NOT_FOUND));
@@ -127,13 +141,30 @@ public void clubPasswordUpdate(User user, ClubPswdUpdateInput clubPswdUpdateInpu
127141
if (!clubPswdUpdateInput.getNewPassword().equals(clubPswdUpdateInput.getRePassword())) {
128142
throw new ClubControllerAdvice(ResponseCode.CLUB_CHECK_PASSWORD_INCORRECT);
129143
}
144+
130145
club.updateClubPassword(clubPswdUpdateInput.getNewPassword());
146+
147+
return club.getTitle() + "의 비밀번호를 변경하였습니다.";
148+
}
149+
150+
private UserClub getUserClub(Club club, User user) {
151+
return userClubRepository.findByClubAndUser(club, user).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_USER_NOT_FOUND));
131152
}
132153

133154
private Club getClub(Long clubId) {
134155
return clubRepository.findById(clubId).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND));
135156
}
136157

158+
private User getUser(Long userId) {
159+
return userRepository.findById(userId).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.MEMBER_NOT_FOUND));
160+
}
161+
162+
private void validateIsStaff(UserClub userClub) {
163+
if (!userClub.getClubRole().equals(ClubRole.STAFF)) {
164+
throw new ClubControllerAdvice(ResponseCode.CLUB_PERMISSION_DENIED);
165+
}
166+
}
167+
137168
private void saveClubSearch(Club club) {
138169
ClubSearch clubSearch = ClubSearch.builder()
139170
.club(club)

src/test/java/com/example/moim/club/repository/ClubRepositoryImplTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ void init() {
5050

5151
ClubInput clubInput = ClubInput.builder().title(title).explanation(explanation).introduction(introduction).clubCategory(clubCategory.getKoreanName())
5252
.university(university).gender(gender.getKoreanName()).activityArea(activityArea.getKoreanName()).ageRange(ageRange.getKoreanName()).sportsType(sportsType.getKoreanName())
53-
.clubPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build();
53+
.clubPassword(clubPassword).clubCheckPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build();
5454
ClubInput clubInput2 = ClubInput.builder().title(title2).explanation(explanation).introduction(introduction).clubCategory(clubCategory2.getKoreanName())
5555
.university(university).gender(gender.getKoreanName()).activityArea(activityArea.getKoreanName()).ageRange(ageRange.getKoreanName()).sportsType(sportsType.getKoreanName())
56-
.clubPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build();
56+
.clubPassword(clubPassword).clubCheckPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build();
5757

5858
Club savedClub = clubRepository.save(Club.createClub(clubInput, "/club"));
5959
Club savedClub2 = clubRepository.save(Club.createClub(clubInput2, "/club"));

src/test/java/com/example/moim/club/service/ClubCommandServiceImplTest.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void init() {
7575

7676
this.clubInput = ClubInput.builder().title(title).explanation(explanation).introduction(introduction).clubCategory(clubCategory.getKoreanName())
7777
.university(organization).gender(gender.getKoreanName()).activityArea(activityArea.getKoreanName()).ageRange(ageRange.getKoreanName()).sportsType(sportsType.getKoreanName())
78-
.clubPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build();
78+
.clubPassword(clubPassword).clubCheckPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build();
7979

8080
String updateTitle = "update title";
8181
String updateExplanation = "update explanation";
@@ -248,6 +248,49 @@ void updateClubUser_exception_wrong_permission() {
248248
verify(userClubRepository, times(1)).findByClubAndUser(any(Club.class), any(User.class));
249249
}
250250

251+
@Test
252+
@DisplayName("운영진은 사용자를 모임에서 내보낼 수 있다")
253+
void deleteClubUser() {
254+
//given
255+
Club club = Club.createClub(clubInput, null);
256+
257+
//when
258+
when(clubRepository.findById(any(Long.class))).thenReturn(Optional.of(club));
259+
when(userClubRepository.findByClubAndUser(any(Club.class), any(User.class)))
260+
.thenReturn(Optional.of(UserClub.createLeaderUserClub(new User(), club)))
261+
.thenReturn(Optional.of(UserClub.createUserClub(new User(), club)));
262+
when(userRepository.findById(any(Long.class))).thenReturn(Optional.of(new User()));
263+
String result = clubCommandService.deleteClubUser(new User(), 1L, 1L);
264+
265+
//then
266+
assertThat(result).contains("님이 가입에서 내보내졌습니다.");
267+
verify(clubRepository, times(1)).findById(any(Long.class));
268+
verify(userClubRepository, times(1)).findByClubAndUser(any(Club.class), any(User.class));
269+
verify(userClubRepository, times(1)).deleteByClubIdAndUserId(any(Long.class), any(Long.class));
270+
}
271+
272+
@Test
273+
@DisplayName("운영진이 아니면 모임에 속한 사용자를 내보내려 할 때 예외가 발생한다")
274+
void delete_ClubUser_exception_wrong_permission() {
275+
//given
276+
Club club = Club.createClub(clubInput, null);
277+
278+
//when
279+
//then
280+
when(clubRepository.findById(any(Long.class))).thenReturn(Optional.of(club));
281+
when(userClubRepository.findByClubAndUser(any(Club.class), any(User.class)))
282+
.thenReturn(Optional.of(UserClub.createUserClub(new User(), club)));
283+
when(userRepository.findById(any(Long.class))).thenReturn(Optional.of(new User()));
284+
285+
Exception exception = assertThrows(ClubControllerAdvice.class, () -> {
286+
clubCommandService.deleteClubUser(new User(), 1L, 1L);
287+
});
288+
assertThat(exception.getMessage()).isEqualTo(ResponseCode.CLUB_PERMISSION_DENIED.getMessage());
289+
verify(userRepository, times(1)).findById(any(Long.class));
290+
verify(clubRepository, times(1)).findById(any(Long.class));
291+
verify(userClubRepository, times(1)).findByClubAndUser(any(Club.class), any(User.class));
292+
}
293+
251294
@Test
252295
@DisplayName("운영진은 동아리 인증 비밀번호를 바꿀 수 있다")
253296
void clubPasswordUpdate() {

0 commit comments

Comments
 (0)