Skip to content

Commit a7920a7

Browse files
authored
[DDING-131] 동아리원 개별 삭제 및 생성 API 구현 (#311)
1 parent 63955f9 commit a7920a7

File tree

13 files changed

+321
-0
lines changed

13 files changed

+321
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package ddingdong.ddingdongBE.domain.clubmember.api;
2+
3+
import ddingdong.ddingdongBE.auth.PrincipalDetails;
4+
import ddingdong.ddingdongBE.domain.clubmember.api.dto.request.CreateClubMemberRequest;
5+
import io.swagger.v3.oas.annotations.Operation;
6+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
7+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import org.springframework.http.HttpStatus;
10+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
11+
import org.springframework.web.bind.annotation.DeleteMapping;
12+
import org.springframework.web.bind.annotation.PathVariable;
13+
import org.springframework.web.bind.annotation.PostMapping;
14+
import org.springframework.web.bind.annotation.RequestBody;
15+
import org.springframework.web.bind.annotation.RequestMapping;
16+
import org.springframework.web.bind.annotation.ResponseStatus;
17+
18+
@Tag(name = "ClubMember - Central", description = "ClubMember API")
19+
@RequestMapping("/server/club-members")
20+
public interface ClubMemberApi {
21+
22+
@Operation(summary = "동아리원 개별 삭제 API")
23+
@ResponseStatus(HttpStatus.NO_CONTENT)
24+
@ApiResponse(responseCode = "204", description = "동아리원 개별 삭제 성공")
25+
@SecurityRequirement(name = "AccessToken")
26+
@DeleteMapping("/{id}")
27+
void delete(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable("id") Long clubMemberId);
28+
29+
@Operation(summary = "동아리원 개별 추가 API")
30+
@ResponseStatus(HttpStatus.CREATED)
31+
@ApiResponse(responseCode = "201", description = "동아리원 개별 생성 성공")
32+
@SecurityRequirement(name = "AccessToken")
33+
@PostMapping
34+
void create(@AuthenticationPrincipal PrincipalDetails principalDetails, @RequestBody CreateClubMemberRequest request);
35+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ddingdong.ddingdongBE.domain.clubmember.api;
2+
3+
import ddingdong.ddingdongBE.auth.PrincipalDetails;
4+
import ddingdong.ddingdongBE.domain.clubmember.api.dto.request.CreateClubMemberRequest;
5+
import ddingdong.ddingdongBE.domain.clubmember.service.FacadeCentralClubMemberService;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.web.bind.annotation.RestController;
8+
9+
@RestController
10+
@RequiredArgsConstructor
11+
public class ClubMemberController implements ClubMemberApi{
12+
13+
private final FacadeCentralClubMemberService facadeCentralClubMemberService;
14+
15+
@Override
16+
public void delete(PrincipalDetails principalDetails, Long clubMemberId) {
17+
Long userId = principalDetails.getUser().getId();
18+
facadeCentralClubMemberService.delete(userId, clubMemberId);
19+
}
20+
21+
@Override
22+
public void create(PrincipalDetails principalDetails, CreateClubMemberRequest request) {
23+
Long userId = principalDetails.getUser().getId();
24+
facadeCentralClubMemberService.create(request.toCommand(userId));
25+
}
26+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package ddingdong.ddingdongBE.domain.clubmember.api.dto.request;
2+
3+
import ddingdong.ddingdongBE.domain.club.entity.Position;
4+
import ddingdong.ddingdongBE.domain.clubmember.service.dto.command.CreateClubMemberCommand;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import jakarta.validation.constraints.NotNull;
7+
import lombok.Builder;
8+
9+
@Schema(
10+
name = "CreateClubMemberRequest",
11+
description = "동아리원 정보 생성 요청"
12+
)
13+
@Builder
14+
public record CreateClubMemberRequest(
15+
16+
@Schema(description = "이름", example = "홍길동")
17+
@NotNull(message = "이름은 필수로 입력해야 합니다.")
18+
String name,
19+
20+
@Schema(description = "학번", example = "60001234")
21+
@NotNull(message = "학번은 필수로 입력해야 합니다.")
22+
String studentNumber,
23+
24+
@Schema(description = "전화번호", example = "010-1234-5678")
25+
@NotNull(message = "전화번호는 필수로 입력해야 합니다.")
26+
String phoneNumber,
27+
28+
@Schema(description = "동아리원 역할",
29+
example = "LEADER",
30+
allowableValues = {"LEADER", "EXECUTION", "MEMBER"}
31+
)
32+
@NotNull(message = "역할은 필수로 입력해야 합니다.")
33+
String position,
34+
35+
@Schema(description = "학과(부)", example = "융합소프트웨어학부")
36+
@NotNull(message = "학과(부)는 필수로 입력해야 합니다.")
37+
String department
38+
) {
39+
40+
public CreateClubMemberCommand toCommand(Long userId) {
41+
return CreateClubMemberCommand.builder()
42+
.userId(userId)
43+
.name(name)
44+
.studentNumber(studentNumber)
45+
.phoneNumber(phoneNumber)
46+
.position(Position.from(position))
47+
.department(department)
48+
.build();
49+
}
50+
}

src/main/java/ddingdong/ddingdongBE/domain/clubmember/entity/ClubMember.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
import jakarta.persistence.JoinColumn;
1515
import jakarta.persistence.ManyToOne;
1616
import java.time.LocalDateTime;
17+
import java.util.Objects;
1718
import lombok.AccessLevel;
1819
import lombok.Builder;
1920
import lombok.Getter;
2021
import lombok.NoArgsConstructor;
22+
import lombok.NonNull;
2123
import org.hibernate.annotations.SQLDelete;
2224
import org.hibernate.annotations.SQLRestriction;
2325

@@ -73,4 +75,10 @@ public void updateInformation(ClubMember updateClubMember) {
7375
public void setClubForConvenience(Club club) {
7476
this.club = club;
7577
}
78+
79+
public void validateBelongsToClub(@NonNull final Club targetClub) {
80+
if (this.club == null || !Objects.equals(this.club.getId(), targetClub.getId())) {
81+
throw new IllegalArgumentException("동아리원은 해당 동아리에 속해 있지 않습니다");
82+
}
83+
}
7684
}

src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/ClubMemberService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ public interface ClubMemberService {
1010
void saveAll(List<ClubMember> clubMembers);
1111

1212
void deleteAll(List<ClubMember> clubMembers);
13+
14+
void delete(ClubMember clubMember);
15+
16+
void save(ClubMember clubMember);
1317
}

src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ddingdong.ddingdongBE.domain.clubmember.service;
22

3+
import ddingdong.ddingdongBE.domain.clubmember.service.dto.command.CreateClubMemberCommand;
34
import ddingdong.ddingdongBE.domain.clubmember.service.dto.command.UpdateClubMemberCommand;
45
import ddingdong.ddingdongBE.domain.clubmember.service.dto.command.UpdateClubMemberListCommand;
56
import ddingdong.ddingdongBE.domain.clubmember.service.dto.query.AllClubMemberInfoQuery;
@@ -14,4 +15,7 @@ public interface FacadeCentralClubMemberService {
1415

1516
void update(UpdateClubMemberCommand updateClubMemberCommand);
1617

18+
void delete(Long userId, Long clubMemberId);
19+
20+
void create(CreateClubMemberCommand command);
1721
}

src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceImpl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import ddingdong.ddingdongBE.domain.club.entity.Club;
44
import ddingdong.ddingdongBE.domain.club.service.ClubService;
55
import ddingdong.ddingdongBE.domain.clubmember.entity.ClubMember;
6+
import ddingdong.ddingdongBE.domain.clubmember.service.dto.command.CreateClubMemberCommand;
67
import ddingdong.ddingdongBE.domain.clubmember.service.dto.command.UpdateClubMemberCommand;
78
import ddingdong.ddingdongBE.domain.clubmember.service.dto.command.UpdateClubMemberListCommand;
89
import ddingdong.ddingdongBE.domain.clubmember.service.dto.query.AllClubMemberInfoQuery;
@@ -63,6 +64,23 @@ public void update(UpdateClubMemberCommand command) {
6364
clubMember.updateInformation(command.toEntity());
6465
}
6566

67+
@Override
68+
@Transactional
69+
public void delete(Long userId, Long clubMemberId) {
70+
Club club = clubService.getByUserId(userId);
71+
ClubMember clubMember = clubMemberService.getById(clubMemberId);
72+
clubMember.validateBelongsToClub(club);
73+
clubMemberService.delete(clubMember);
74+
}
75+
76+
@Override
77+
@Transactional
78+
public void create(final CreateClubMemberCommand command) {
79+
Club club = clubService.getByUserId(command.userId());
80+
ClubMember clubMember = command.toEntity(club);
81+
clubMemberService.save(clubMember);
82+
club.addClubMember(clubMember);
83+
}
6684

6785
private List<ClubMember> filterCreatedMembers(List<ClubMember> updatedClubMembers, Set<Long> updatedMemberIds,
6886
Set<Long> currentMemberIds) {

src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/GeneralClubMemberService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,15 @@ public void deleteAll(List<ClubMember> clubMembers) {
3333
clubMemberRepository.deleteAllInBatch(clubMembers);
3434
}
3535

36+
@Override
37+
@Transactional
38+
public void delete(ClubMember clubMember) {
39+
clubMemberRepository.delete(clubMember);
40+
}
41+
42+
@Override
43+
@Transactional
44+
public void save(ClubMember clubMember) {
45+
clubMemberRepository.save(clubMember);
46+
}
3647
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package ddingdong.ddingdongBE.domain.clubmember.service.dto.command;
2+
3+
import ddingdong.ddingdongBE.domain.club.entity.Club;
4+
import ddingdong.ddingdongBE.domain.club.entity.Position;
5+
import ddingdong.ddingdongBE.domain.clubmember.entity.ClubMember;
6+
import lombok.Builder;
7+
8+
@Builder
9+
public record CreateClubMemberCommand(
10+
Long userId,
11+
String name,
12+
String studentNumber,
13+
String phoneNumber,
14+
Position position,
15+
String department
16+
) {
17+
18+
public ClubMember toEntity(Club club) {
19+
return ClubMember.builder()
20+
.name(name)
21+
.studentNumber(studentNumber)
22+
.phoneNumber(phoneNumber)
23+
.position(position)
24+
.department(department)
25+
.club(club)
26+
.build();
27+
}
28+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package ddingdong.ddingdongBE.common.fixture;
2+
3+
import ddingdong.ddingdongBE.domain.club.entity.Club;
4+
import ddingdong.ddingdongBE.domain.club.entity.Location;
5+
import ddingdong.ddingdongBE.domain.club.entity.PhoneNumber;
6+
import ddingdong.ddingdongBE.domain.scorehistory.entity.Score;
7+
import ddingdong.ddingdongBE.domain.user.entity.User;
8+
import java.math.BigDecimal;
9+
import java.util.ArrayList;
10+
11+
public class ClubFixture {
12+
13+
public static Club createClub(final User user) {
14+
return Club.builder()
15+
.user(user)
16+
.clubMembers(new ArrayList<>())
17+
.name("컴퓨터공학과 동아리")
18+
.category("학술")
19+
.tag("프로그래밍, 개발, IT")
20+
.leader("김동아")
21+
.phoneNumber(PhoneNumber.from("010-1234-5678"))
22+
.location(Location.from("S3014")) // S + 4자리 숫자
23+
.regularMeeting("매주 수요일 18:00")
24+
.introduction("컴퓨터공학과 학생들이 함께 공부하고 프로젝트를 진행하는 동아리입니다.")
25+
.activity("알고리즘 스터디, 웹 개발 프로젝트, 해커톤 참가")
26+
.ideal("함께 성장하는 개발자 커뮤니티")
27+
.score(Score.from(BigDecimal.valueOf(85.5)))
28+
.build();
29+
}
30+
}

0 commit comments

Comments
 (0)