From 3de2eed7ecfcb9bdd4414376b1982b988ab9dd54 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 15:57:35 +0900 Subject: [PATCH] =?UTF-8?q?=20=20feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EC=9A=A9,=20=EA=B4=80=EB=A6=AC=EC=9E=90=EC=9A=A9=20=EA=B3=B5?= =?UTF-8?q?=EC=A7=80=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F?= =?UTF-8?q?=20=EA=B3=B5=EC=A7=80=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/application/domain/Notice.java | 14 +++ .../application/domain/NoticeContent.java | 2 +- .../application/service/NoticeService.java | 23 ++++ .../dto/NoticeFindAllResponseToAdmin.java | 35 ++++++ .../dto/NoticeFindAllResponseToUser.java | 35 ++++++ .../service/dto/NoticeFindResponse.java | 74 +++++++++++++ .../notice/presentation/NoticeController.java | 22 ++++ .../notice/repository/NoticeRepository.java | 9 ++ .../service/NoticeServiceTest.java | 101 ++++++++++++++---- 9 files changed, 291 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToAdmin.java create mode 100644 src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToUser.java create mode 100644 src/main/java/com/listywave/notice/application/service/dto/NoticeFindResponse.java diff --git a/src/main/java/com/listywave/notice/application/domain/Notice.java b/src/main/java/com/listywave/notice/application/domain/Notice.java index e4e510ec..633b9ea7 100644 --- a/src/main/java/com/listywave/notice/application/domain/Notice.java +++ b/src/main/java/com/listywave/notice/application/domain/Notice.java @@ -10,7 +10,9 @@ import jakarta.persistence.Entity; import jakarta.persistence.OneToMany; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Optional; import lombok.Getter; import lombok.NoArgsConstructor; @@ -28,6 +30,10 @@ public class Notice extends BaseEntity { @Embedded private NoticeDescription description; + private boolean isExposed; + + private boolean didSendAlarm; + @OneToMany(mappedBy = "notice", fetch = LAZY, cascade = ALL, orphanRemoval = true) private final List contents = new ArrayList<>(); @@ -40,4 +46,12 @@ public Notice(NoticeType type, NoticeTitle title, NoticeDescription description) public void addContents(List contents) { this.contents.addAll(contents); } + + public String getFirstImageUrl() { + Optional result = contents.stream() + .sorted(Comparator.comparingInt(NoticeContent::getOrder)) + .map(NoticeContent::getImageUrl) + .findFirst(); + return result.orElse(null); + } } diff --git a/src/main/java/com/listywave/notice/application/domain/NoticeContent.java b/src/main/java/com/listywave/notice/application/domain/NoticeContent.java index abc92457..9c90a959 100644 --- a/src/main/java/com/listywave/notice/application/domain/NoticeContent.java +++ b/src/main/java/com/listywave/notice/application/domain/NoticeContent.java @@ -37,7 +37,7 @@ public class NoticeContent { @Column(nullable = true, length = 1000) private String description; - + @Column(nullable = true, length = 2048) private String imageUrl; diff --git a/src/main/java/com/listywave/notice/application/service/NoticeService.java b/src/main/java/com/listywave/notice/application/service/NoticeService.java index 502fdce4..3b65ed8f 100644 --- a/src/main/java/com/listywave/notice/application/service/NoticeService.java +++ b/src/main/java/com/listywave/notice/application/service/NoticeService.java @@ -3,6 +3,9 @@ import com.listywave.notice.application.domain.Notice; import com.listywave.notice.application.domain.NoticeContent; import com.listywave.notice.application.service.dto.NoticeCreateRequest; +import com.listywave.notice.application.service.dto.NoticeFindAllResponseToAdmin; +import com.listywave.notice.application.service.dto.NoticeFindAllResponseToUser; +import com.listywave.notice.application.service.dto.NoticeFindResponse; import com.listywave.notice.repository.NoticeRepository; import java.util.List; import lombok.RequiredArgsConstructor; @@ -22,4 +25,24 @@ public Long create(NoticeCreateRequest request) { notice.addContents(noticeContents); return noticeRepository.save(notice).getId(); } + + @Transactional(readOnly = true) + public List findAllToAdmin() { + List notices = noticeRepository.findAll(); + return NoticeFindAllResponseToAdmin.toList(notices); + } + + @Transactional(readOnly = true) + public List findAllToUser() { + List notices = noticeRepository.findAll(); + return NoticeFindAllResponseToUser.toList(notices); + } + + @Transactional(readOnly = true) + public NoticeFindResponse findOneSpecific(Long id) { + Notice result = noticeRepository.findOne(id); + Notice prevNotice = noticeRepository.findOne(id - 1); + Notice nextNotice = noticeRepository.findOne(id + 1); + return NoticeFindResponse.of(result, prevNotice, nextNotice); + } } diff --git a/src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToAdmin.java b/src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToAdmin.java new file mode 100644 index 00000000..8fa477ef --- /dev/null +++ b/src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToAdmin.java @@ -0,0 +1,35 @@ +package com.listywave.notice.application.service.dto; + +import com.listywave.notice.application.domain.Notice; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Builder; + +@Builder +public record NoticeFindAllResponseToAdmin( + Long id, + LocalDateTime createdDate, + String title, + String category, + String description, + boolean isExposed, + boolean didSendAlarm +) { + + public static List toList(List notices) { + return notices.stream() + .map(NoticeFindAllResponseToAdmin::of) + .toList(); + } + + public static NoticeFindAllResponseToAdmin of(Notice notice) { + return NoticeFindAllResponseToAdmin.builder() + .id(notice.getId()) + .createdDate(notice.getCreatedDate()) + .title(notice.getTitle().getValue()) + .category(notice.getType().getViewName()) + .description(notice.getDescription().getValue()) + .isExposed(notice.isDidSendAlarm()) + .build(); + } +} diff --git a/src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToUser.java b/src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToUser.java new file mode 100644 index 00000000..6e03c73b --- /dev/null +++ b/src/main/java/com/listywave/notice/application/service/dto/NoticeFindAllResponseToUser.java @@ -0,0 +1,35 @@ +package com.listywave.notice.application.service.dto; + +import com.listywave.notice.application.domain.Notice; +import jakarta.annotation.Nullable; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Builder; + +@Builder +public record NoticeFindAllResponseToUser( + Long id, + LocalDateTime createdDate, + String title, + @Nullable String itemImageUrl, + String category, + String description +) { + + public static List toList(List notices) { + return notices.stream() + .map(NoticeFindAllResponseToUser::of) + .toList(); + } + + public static NoticeFindAllResponseToUser of(Notice notice) { + return NoticeFindAllResponseToUser.builder() + .id(notice.getId()) + .createdDate(notice.getCreatedDate()) + .title(notice.getTitle().getValue()) + .itemImageUrl(notice.getFirstImageUrl()) + .category(notice.getType().getViewName()) + .description(notice.getDescription().getValue()) + .build(); + } +} diff --git a/src/main/java/com/listywave/notice/application/service/dto/NoticeFindResponse.java b/src/main/java/com/listywave/notice/application/service/dto/NoticeFindResponse.java new file mode 100644 index 00000000..3e1c797f --- /dev/null +++ b/src/main/java/com/listywave/notice/application/service/dto/NoticeFindResponse.java @@ -0,0 +1,74 @@ +package com.listywave.notice.application.service.dto; + +import com.listywave.notice.application.domain.Notice; +import com.listywave.notice.application.domain.NoticeContent; +import jakarta.annotation.Nullable; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Builder; + +@Builder +public record NoticeFindResponse( + Long id, + String category, + String title, + String description, + List contents, + LocalDateTime createdDate, + BesideNoticeDto prevNotice, + BesideNoticeDto nextNotice +) { + + public static NoticeFindResponse of(Notice notice, Notice prevNotice, Notice nextNotice) { + return NoticeFindResponse.builder() + .id(notice.getId()) + .category(notice.getType().getViewName()) + .title(notice.getTitle().getValue()) + .description(notice.getDescription().getValue()) + .contents(ContentDto.toList(notice.getContents())) + .createdDate(notice.getCreatedDate()) + .prevNotice(BesideNoticeDto.of(prevNotice)) + .nextNotice(BesideNoticeDto.of(nextNotice)) + .build(); + } + + @Builder + public record ContentDto( + String type, + String description, + @Nullable String imageUrl, + @Nullable String buttonName, + @Nullable String buttonLink + ) { + + public static List toList(List contents) { + return contents.stream() + .map(ContentDto::of) + .toList(); + } + + public static ContentDto of(NoticeContent content) { + return ContentDto.builder() + .type(content.getType().name().toLowerCase()) + .description(content.getDescription()) + .imageUrl(content.getImageUrl()) + .buttonName(content.getButtonName()) + .buttonLink(content.getButtonLink()) + .build(); + } + } + + public record BesideNoticeDto( + Long id, + String title, + String description + ) { + + public static BesideNoticeDto of(Notice notice) { + if (notice == null) { + return null; + } + return new BesideNoticeDto(notice.getId(), notice.getTitle().getValue(), notice.getDescription().getValue()); + } + } +} diff --git a/src/main/java/com/listywave/notice/presentation/NoticeController.java b/src/main/java/com/listywave/notice/presentation/NoticeController.java index dde9df49..f45529c2 100644 --- a/src/main/java/com/listywave/notice/presentation/NoticeController.java +++ b/src/main/java/com/listywave/notice/presentation/NoticeController.java @@ -5,12 +5,16 @@ import com.listywave.notice.application.domain.NoticeType; import com.listywave.notice.application.service.NoticeService; import com.listywave.notice.application.service.dto.NoticeCreateRequest; +import com.listywave.notice.application.service.dto.NoticeFindAllResponseToAdmin; +import com.listywave.notice.application.service.dto.NoticeFindAllResponseToUser; +import com.listywave.notice.application.service.dto.NoticeFindResponse; import com.listywave.notice.presentation.dto.NoticeCategoryFindResponse; import java.net.URI; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -36,4 +40,22 @@ ResponseEntity create( Long id = noticeService.create(request); return ResponseEntity.created(URI.create("/admin/notices/" + id)).build(); } + + @GetMapping("/admin/notices") + ResponseEntity> findAllToAdmin() { + List result = noticeService.findAllToAdmin(); + return ResponseEntity.ok(result); + } + + @GetMapping("/notices") + ResponseEntity> findAllToUser() { + List result = noticeService.findAllToUser(); + return ResponseEntity.ok(result); + } + + @GetMapping("/notices/{noticeId}") + ResponseEntity findOneSpecific(@PathVariable Long noticeId) { + NoticeFindResponse result = noticeService.findOneSpecific(noticeId); + return ResponseEntity.ok(result); + } } diff --git a/src/main/java/com/listywave/notice/repository/NoticeRepository.java b/src/main/java/com/listywave/notice/repository/NoticeRepository.java index ec203a8e..52e75594 100644 --- a/src/main/java/com/listywave/notice/repository/NoticeRepository.java +++ b/src/main/java/com/listywave/notice/repository/NoticeRepository.java @@ -5,10 +5,19 @@ import com.listywave.common.exception.CustomException; import com.listywave.notice.application.domain.Notice; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface NoticeRepository extends JpaRepository { default Notice getById(Long id) { return findById(id).orElseThrow(() -> new CustomException(RESOURCE_NOT_FOUND, "존재하지 않는 공지입니다.")); } + + @Query(""" + select n + from Notice n + join NoticeContent nc on nc.notice = n + where n.id = :noticeId + """) + Notice findOne(Long noticeId); } diff --git a/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java b/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java index 47a2b750..eb152525 100644 --- a/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java +++ b/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java @@ -1,20 +1,91 @@ package com.listywave.notice.application.service; -import static com.listywave.notice.application.domain.NoticeType.NEWS; +import static com.listywave.notice.application.domain.ContentType.BODY; +import static com.listywave.notice.application.domain.ContentType.BUTTON; +import static com.listywave.notice.application.domain.ContentType.IMAGE; +import static com.listywave.notice.application.domain.ContentType.NOTE; +import static com.listywave.notice.application.domain.ContentType.SUBTITLE; +import static com.listywave.notice.application.domain.NoticeType.EVENT; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import com.listywave.common.IntegrationTest; -import com.listywave.notice.application.domain.Notice; import com.listywave.notice.application.service.dto.NoticeCreateRequest; import com.listywave.notice.application.service.dto.NoticeCreateRequest.ContentDto; +import com.listywave.notice.application.service.dto.NoticeFindResponse; import java.util.List; import org.junit.jupiter.api.Test; public class NoticeServiceTest extends IntegrationTest { @Test - void 공지를_생성한다() { + void 공지를_생성_후_상세_조회한다() { + // given + NoticeCreateRequest request1 = new NoticeCreateRequest( + 1, + "첫 번째 공지입니다", + "첫 번째 공지에요", + List.of( + new ContentDto(1, "subtitle", "소제목입니다", null, null, null), + new ContentDto(2, "body", "본문입니다", null, null, null), + new ContentDto(3, "image", "이미지입니다", "https://image.com", null, null), + new ContentDto(4, "button", "버튼입니다", null, "버튼 이름", "https://buttonLink.com"), + new ContentDto(5, "note", "유의사항입니다", null, null, null) + ) + ); + NoticeCreateRequest request2 = new NoticeCreateRequest( + 2, + "두 번째 공지입니다", + "두 번째 공지에요", + List.of( + new ContentDto(1, "subtitle", "소제목입니다", null, null, null), + new ContentDto(2, "body", "본문입니다", null, null, null), + new ContentDto(3, "image", "이미지입니다", "https://image.com", null, null), + new ContentDto(4, "button", "버튼입니다", null, "버튼 이름", "https://buttonLink.com"), + new ContentDto(5, "note", "유의사항입니다", null, null, null) + ) + ); + NoticeCreateRequest request3 = new NoticeCreateRequest( + 3, + "세 번째 공지입니다", + "세 번째 공지에요", + List.of( + new ContentDto(1, "subtitle", "소제목입니다", null, null, null), + new ContentDto(2, "body", "본문입니다", null, null, null), + new ContentDto(3, "image", "이미지입니다", "https://image.com", null, null), + new ContentDto(4, "button", "버튼입니다", null, "버튼 이름", "https://buttonLink.com"), + new ContentDto(5, "note", "유의사항입니다", null, null, null) + ) + ); + + // when + Long id1 = noticeService.create(request1); + Long id2 = noticeService.create(request2); + Long id3 = noticeService.create(request3); + + // then + NoticeFindResponse result = noticeService.findOneSpecific(id2); + assertAll( + () -> assertThat(result.id()).isEqualTo(id2), + () -> assertThat(result.category()).isEqualTo(EVENT.getViewName()), + () -> assertThat(result.title()).isEqualTo("두 번째 공지입니다"), + () -> assertThat(result.description()).isEqualTo("두 번째 공지에요"), + () -> { + List contents = result.contents(); + assertThat(contents).hasSize(5); + assertThat(contents.get(0).type()).isEqualTo(SUBTITLE.name().toLowerCase()); + assertThat(contents.get(1).type()).isEqualTo(BODY.name().toLowerCase()); + assertThat(contents.get(2).type()).isEqualTo(IMAGE.name().toLowerCase()); + assertThat(contents.get(3).type()).isEqualTo(BUTTON.name().toLowerCase()); + assertThat(contents.get(4).type()).isEqualTo(NOTE.name().toLowerCase()); + }, + () -> assertThat(result.prevNotice().id()).isEqualTo(id1), + () -> assertThat(result.nextNotice().id()).isEqualTo(id3) + ); + } + + @Test + void 이전_혹은_다음_공지가_없으면_null이_반환된다() { // given NoticeCreateRequest request = new NoticeCreateRequest( 1, @@ -30,29 +101,13 @@ public class NoticeServiceTest extends IntegrationTest { ); // when - noticeService.create(request); + Long id = noticeService.create(request); // then - Notice result = noticeRepository.getById(1L); + NoticeFindResponse result = noticeService.findOneSpecific(id); assertAll( - () -> assertThat(result.getType()).isEqualTo(NEWS), - () -> assertThat(result.getTitle().getValue()).isEqualTo("공지입니다"), - () -> assertThat(result.getDescription().getValue()).isEqualTo("공지에요") -// () -> assertThat(result.getContents().size()).isEqualTo(5) -// () -> { -// List contents = result.getContents(); -// -// ; -// assertAll( -// () -> assertThat(contents.get(0).getOrder()).isEqualTo(1), -// () -> assertThat(contents.get(0).getType()).isEqualTo(SUBTITLE), -// () -> assertThat(contents.get(0).getDescription()).isEqualTo("소제목입니다"), -// () -> assertThat(contents.get(0).getImageUrl()) -// .isEqualTo(contents.get(0).getButtonName()) -// .isEqualTo(contents.get(0).getButtonLink()) -// .isNull() -// ); -// } + () -> assertThat(result.prevNotice()).isNull(), + () -> assertThat(result.nextNotice()).isNull() ); } }