diff --git a/src/main/java/com/spaceclub/club/controller/ClubController.java b/src/main/java/com/spaceclub/club/controller/ClubController.java index af9e661b..6d668633 100644 --- a/src/main/java/com/spaceclub/club/controller/ClubController.java +++ b/src/main/java/com/spaceclub/club/controller/ClubController.java @@ -1,6 +1,7 @@ package com.spaceclub.club.controller; -import com.spaceclub.club.controller.dto.CreateClubRequest; +import com.spaceclub.club.controller.dto.ClubGetResponse; +import com.spaceclub.club.controller.dto.ClubCreateRequest; import com.spaceclub.club.domain.Club; import com.spaceclub.club.service.ClubService; import lombok.RequiredArgsConstructor; @@ -23,7 +24,7 @@ public class ClubController { private final ClubService service; @PostMapping("/clubs") - public ResponseEntity createClub(@RequestBody CreateClubRequest request) { + public ResponseEntity createClub(@RequestBody ClubCreateRequest request) { Club newClub = request.toEntity(); Club createdClub = service.createClub(newClub); Long id = createdClub.getId(); @@ -32,8 +33,11 @@ public ResponseEntity createClub(@RequestBody CreateClubRequest request) } @GetMapping("/clubs/{clubId}") - public ResponseEntity getClub(@PathVariable Long clubId) { - return ResponseEntity.ok("get club."); + public ResponseEntity getClub(@PathVariable Long clubId) { + Club club = service.getClub(clubId); + ClubGetResponse response = ClubGetResponse.from(club); + + return ResponseEntity.ok(response); } @DeleteMapping("/clubs/{clubId}") diff --git a/src/main/java/com/spaceclub/club/controller/dto/CreateClubRequest.java b/src/main/java/com/spaceclub/club/controller/dto/ClubCreateRequest.java similarity index 92% rename from src/main/java/com/spaceclub/club/controller/dto/CreateClubRequest.java rename to src/main/java/com/spaceclub/club/controller/dto/ClubCreateRequest.java index cb9e82c9..7784a2eb 100644 --- a/src/main/java/com/spaceclub/club/controller/dto/CreateClubRequest.java +++ b/src/main/java/com/spaceclub/club/controller/dto/ClubCreateRequest.java @@ -2,7 +2,7 @@ import com.spaceclub.club.domain.Club; -public record CreateClubRequest( +public record ClubCreateRequest( String name, String info, String owner, diff --git a/src/main/java/com/spaceclub/club/controller/dto/ClubGetResponse.java b/src/main/java/com/spaceclub/club/controller/dto/ClubGetResponse.java new file mode 100644 index 00000000..ae62111d --- /dev/null +++ b/src/main/java/com/spaceclub/club/controller/dto/ClubGetResponse.java @@ -0,0 +1,43 @@ +package com.spaceclub.club.controller.dto; + +import com.spaceclub.club.domain.Club; +import com.spaceclub.club.domain.ClubNotice; +import lombok.Builder; + +import java.util.ArrayList; +import java.util.List; + +public record ClubGetResponse( + String name, + String info, + Long memberCount, + String image, + List notices +) { + + @Builder + public ClubGetResponse(String name, String info, Long memberCount, String image, List notices) { + this.name = name; + this.info = info; + this.memberCount = memberCount; + this.image = image; + this.notices = new ArrayList<>(notices); + } + + public static ClubGetResponse from(Club club) { + long memberCount = club.getClubUser().stream() + .filter((user) -> user.getClub().getId().equals(club.getId())) + .count(); + + return ClubGetResponse.builder() + .name(club.getName()) + .info(club.getInfo()) + .memberCount(memberCount) + .image(club.getImage()) + .notices(club.getNotices().stream() + .map(ClubNotice::getNotice) + .toList()) + .build(); + } + +} diff --git a/src/main/java/com/spaceclub/club/domain/Club.java b/src/main/java/com/spaceclub/club/domain/Club.java index db50248c..1947e18e 100644 --- a/src/main/java/com/spaceclub/club/domain/Club.java +++ b/src/main/java/com/spaceclub/club/domain/Club.java @@ -6,12 +6,16 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Lob; +import jakarta.persistence.OneToMany; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.util.Assert; +import java.util.ArrayList; +import java.util.List; + @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Club extends BaseTimeEntity { @@ -23,22 +27,34 @@ public class Club extends BaseTimeEntity { private Long id; @Column(length = 12, nullable = false) + @Getter private String name; @Lob + @Getter private String image; @Lob + @Getter private String info; + @Getter private String owner; + @Getter + @OneToMany(mappedBy = "club") + private List notices = new ArrayList<>(); + + @Getter + @OneToMany(mappedBy = "club") + private List clubUser = new ArrayList<>(); + private boolean validateNameLength(String name) { return name.length() <= 12; } @Builder - public Club(String name, String image, String info, String owner) { + public Club(String name, String image, String info, String owner, List notices) { Assert.notNull(name, "이름에 null 값이 올 수 없습니다"); Assert.hasText(name, "이름이 빈 값일 수 없습니다"); Assert.isTrue(validateNameLength(name), "이름의 길이는 12글자를 넘을 수 없습니다"); @@ -48,6 +64,10 @@ public Club(String name, String image, String info, String owner) { this.image = image; this.info = info; this.owner = owner; + + if (notices != null) { + this.notices = new ArrayList<>(notices); + } } } diff --git a/src/main/java/com/spaceclub/club/domain/ClubNotice.java b/src/main/java/com/spaceclub/club/domain/ClubNotice.java new file mode 100644 index 00000000..0367c6dd --- /dev/null +++ b/src/main/java/com/spaceclub/club/domain/ClubNotice.java @@ -0,0 +1,31 @@ +package com.spaceclub.club.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ClubNotice { + + @Id + @Column(name = "club_notice_id") + private Long id; + + @ManyToOne + @JoinColumn(name = "club_id") + private Club club; + + @Getter + private String notice; + + public ClubNotice(String notice) { + this.notice = notice; + } + +} diff --git a/src/main/java/com/spaceclub/club/domain/ClubUser.java b/src/main/java/com/spaceclub/club/domain/ClubUser.java new file mode 100644 index 00000000..c6d07f3d --- /dev/null +++ b/src/main/java/com/spaceclub/club/domain/ClubUser.java @@ -0,0 +1,33 @@ +package com.spaceclub.club.domain; + +import com.spaceclub.global.BaseTimeEntity; +import com.spaceclub.user.domain.User; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.Getter; + +@Table(name = "club_user") +@Entity +public class ClubUser extends BaseTimeEntity { + + @Id + @Column(name = "club_user_id") + private Long id; + + @Getter + @ManyToOne + @JoinColumn(name = "club_id") + private Club club; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @Column(length = 16, nullable = false) + private String role; + +} diff --git a/src/main/java/com/spaceclub/club/service/ClubService.java b/src/main/java/com/spaceclub/club/service/ClubService.java index 83bb65b0..132aded1 100644 --- a/src/main/java/com/spaceclub/club/service/ClubService.java +++ b/src/main/java/com/spaceclub/club/service/ClubService.java @@ -4,8 +4,10 @@ import com.spaceclub.club.repository.ClubRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional @RequiredArgsConstructor public class ClubService { @@ -15,4 +17,9 @@ public Club createClub(Club club) { return repository.save(club); } + public Club getClub(Long clubId) { + return repository.findById(clubId) + .orElseThrow(() -> new IllegalArgumentException("해당하는 클럽이 없습니다")); + } + } diff --git a/src/test/java/com/spaceclub/club/controller/ClubControllerTest.java b/src/test/java/com/spaceclub/club/controller/ClubControllerTest.java index 0b502044..cd23b560 100644 --- a/src/test/java/com/spaceclub/club/controller/ClubControllerTest.java +++ b/src/test/java/com/spaceclub/club/controller/ClubControllerTest.java @@ -1,21 +1,22 @@ package com.spaceclub.club.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import com.spaceclub.club.controller.dto.CreateClubRequest; +import com.spaceclub.club.controller.dto.ClubCreateRequest; import com.spaceclub.club.domain.Club; +import com.spaceclub.club.domain.ClubNotice; import com.spaceclub.club.service.ClubService; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; -import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; +import java.util.List; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; @@ -25,8 +26,12 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -45,9 +50,8 @@ class ClubControllerTest { private ClubService clubService; @Test - @DisplayName("클럽 생성에 성공한다") @WithMockUser - void createClubTest() throws Exception { + void 클럽_생성에_성공한다() throws Exception { // given given(clubService.createClub(any(Club.class))).willReturn( Club.builder() @@ -56,7 +60,7 @@ void createClubTest() throws Exception { .owner("연어대장") .image("연어.png") .build()); - CreateClubRequest request = new CreateClubRequest("연사모", + ClubCreateRequest request = new ClubCreateRequest("연사모", "연어를 사랑하는 모임", "연어대장", "연어.png"); @@ -74,18 +78,26 @@ void createClubTest() throws Exception { preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields( - fieldWithPath("name").type(JsonFieldType.STRING).description("클럽 이름"), - fieldWithPath("info").type(JsonFieldType.STRING).description("클럽 소개"), - fieldWithPath("owner").type(JsonFieldType.STRING).description("클럽 생성자"), - fieldWithPath("image").type(JsonFieldType.STRING).description("클럽 썸네일 이미지") + fieldWithPath("name").type(STRING).description("클럽 이름"), + fieldWithPath("info").type(STRING).description("클럽 소개"), + fieldWithPath("owner").type(STRING).description("클럽 생성자"), + fieldWithPath("image").type(STRING).description("클럽 썸네일 이미지") ))); } @Test - @DisplayName("클럽 조회에 성공한다") @WithMockUser - void getClubTest() throws Exception { + void 클럽_조회에_성공한다() throws Exception { // given + given(clubService.getClub(any(Long.class))).willReturn( + Club.builder() + .name("연사모") + .info("이곳은 연사모입니다") + .image("연어.png") + .owner("연어대장") + .notices(List.of(new ClubNotice("연사모의 공지사항1"))) + .build() + ); Long clubId = 1L; // when @@ -98,13 +110,19 @@ void getClubTest() throws Exception { .andDo(print()) .andDo(document("club/get", preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()))); + preprocessResponse(prettyPrint()), + responseFields( + fieldWithPath("name").type(STRING).description("클럽 이름"), + fieldWithPath("info").type(STRING).description("클럽 소개"), + fieldWithPath("memberCount").type(NUMBER).description("클럽 멤버수"), + fieldWithPath("image").type(STRING).description("클럽 썸네일 이미지"), + fieldWithPath("notices").type(ARRAY).description("클럽 공지사항 리스트") + ))); } @Test - @DisplayName("클럽 삭제에 성공한다") @WithMockUser - void deleteClubTest() throws Exception { + void 클럽_삭제에_성공한다() throws Exception { // given Long clubId = 1L; @@ -118,7 +136,8 @@ void deleteClubTest() throws Exception { .andDo(print()) .andDo(document("club/delete", preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()))); + preprocessResponse(prettyPrint()) + )); } }