From 4e3af74b4a1052d9a6c20512af34555ca37f8247 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Tue, 29 Oct 2024 14:58:53 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=20test:=20=EC=A0=84=EC=B2=B4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EA=B0=80=20=EC=8B=A4=ED=8C=A8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 정확한 원인은 파악하지 못했지만, 추측컨데 AcceptanceTest와 IntegrationTest의 테스트 DB가 공유되면서 외래키 제약 조건으로 발생한 것으로 예상 (#312) --- .../java/com/listywave/common/IntegrationTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/java/com/listywave/common/IntegrationTest.java b/src/test/java/com/listywave/common/IntegrationTest.java index f2f5076f..34cc226c 100644 --- a/src/test/java/com/listywave/common/IntegrationTest.java +++ b/src/test/java/com/listywave/common/IntegrationTest.java @@ -13,6 +13,7 @@ import com.listywave.collection.application.service.CollectionService; import com.listywave.collection.application.service.FolderService; import com.listywave.collection.repository.CollectionRepository; +import com.listywave.history.repository.HistoryRepository; import com.listywave.list.application.domain.list.ListEntity; import com.listywave.list.application.service.CommentService; import com.listywave.list.application.service.ReplyService; @@ -22,6 +23,8 @@ import com.listywave.list.repository.list.ListRepository; import com.listywave.list.repository.reply.ReplyRepository; import com.listywave.mention.MentionRepository; +import com.listywave.reaction.repository.ReactionStatsRepository; +import com.listywave.reaction.repository.UserReactionRepository; import com.listywave.topic.application.service.TopicService; import com.listywave.topic.repository.TopicRepository; import com.listywave.user.application.domain.User; @@ -81,12 +84,21 @@ public abstract class IntegrationTest { protected TopicService topicService; @Autowired protected TopicRepository topicRepository; + @Autowired + protected HistoryRepository historyRepository; + @Autowired + protected UserReactionRepository userReactionRepository; + @Autowired + protected ReactionStatsRepository reactionStatsRepository; protected User dh, js, ej, sy; protected ListEntity list; @BeforeEach void setUp() { + historyRepository.deleteAll(); + userReactionRepository.deleteAllInBatch(); + reactionStatsRepository.deleteAllInBatch(); topicRepository.deleteAllInBatch(); followRepository.deleteAllInBatch(); collectionRepository.deleteAllInBatch(); From 8d5ece2d4923541000f602b8162ff2c0b173b7c5 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Tue, 29 Oct 2024 15:04:22 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=20test:=20=EC=9A=94=EC=B2=AD=20=EC=A3=BC?= =?UTF-8?q?=EC=A0=9C(=ED=86=A0=ED=94=BD)=20=EC=B4=88=EA=B8=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=8B=9C=20isExposed=EB=A5=BC=20=ED=95=AD=EC=83=81?= =?UTF-8?q?=20true=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=A8=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listywave/topic/application/service/TopicServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/listywave/topic/application/service/TopicServiceTest.java b/src/test/java/com/listywave/topic/application/service/TopicServiceTest.java index 41f72fd0..8df38f94 100644 --- a/src/test/java/com/listywave/topic/application/service/TopicServiceTest.java +++ b/src/test/java/com/listywave/topic/application/service/TopicServiceTest.java @@ -42,7 +42,7 @@ class 토픽_생성 { assertThat(topic.getTitle().getValue()).isEqualTo("제일 좋아하는 여자 아이돌 TOP3"); assertThat(topic.getDescription().getValue()).isEqualTo("여러분들은 어떤 여돌을 가장 좋아하나요?"); assertThat(topic.isAnonymous()).isFalse(); - assertThat(topic.isExposed()).isFalse(); + assertThat(topic.isExposed()).isTrue(); // TODO: 어드민 로그인 붙이기 전까지는 항상 True로 생성 } ); } From 4c39059fd0fc43fb86c102bd7c53c692010d9f60 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Tue, 29 Oct 2024 16:09:13 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=20=20feat:=20=EA=B3=B5=EC=A7=80=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC(=ED=83=80=EC=9E=85)=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=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 --- src/main/java/com/listywave/admin/Admin.java | 32 ++++++++++++++++ .../com/listywave/admin/AdminRepository.java | 13 +++++++ .../com/listywave/admin/AdminService.java | 17 +++++++++ .../converter/NoticeTypeConverter.java | 19 ++++++++++ .../notice/application/domain/NoticeType.java | 28 ++++++++++++++ .../notice/presentation/NoticeController.java | 25 ++++++++++++ .../dto/NoticeCategoryFindResponse.java | 17 +++++++++ .../application/domain/NoticeTypeTest.java | 38 +++++++++++++++++++ 8 files changed, 189 insertions(+) create mode 100644 src/main/java/com/listywave/admin/Admin.java create mode 100644 src/main/java/com/listywave/admin/AdminRepository.java create mode 100644 src/main/java/com/listywave/admin/AdminService.java create mode 100644 src/main/java/com/listywave/notice/application/converter/NoticeTypeConverter.java create mode 100644 src/main/java/com/listywave/notice/application/domain/NoticeType.java create mode 100644 src/main/java/com/listywave/notice/presentation/NoticeController.java create mode 100644 src/main/java/com/listywave/notice/presentation/dto/NoticeCategoryFindResponse.java create mode 100644 src/test/java/com/listywave/notice/application/domain/NoticeTypeTest.java diff --git a/src/main/java/com/listywave/admin/Admin.java b/src/main/java/com/listywave/admin/Admin.java new file mode 100644 index 00000000..a5059f0a --- /dev/null +++ b/src/main/java/com/listywave/admin/Admin.java @@ -0,0 +1,32 @@ +package com.listywave.admin; + +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PROTECTED; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = PROTECTED) +@AllArgsConstructor +public class Admin { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @Column(nullable = false, length = 40, unique = true) + private String ip; + + @Column(nullable = false, length = 50, unique = true) + private String account; + + @Column(nullable = false, length = 50) + private String password; +} diff --git a/src/main/java/com/listywave/admin/AdminRepository.java b/src/main/java/com/listywave/admin/AdminRepository.java new file mode 100644 index 00000000..121781b8 --- /dev/null +++ b/src/main/java/com/listywave/admin/AdminRepository.java @@ -0,0 +1,13 @@ +package com.listywave.admin; + +import static com.listywave.common.exception.ErrorCode.RESOURCE_NOT_FOUND; + +import com.listywave.common.exception.CustomException; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AdminRepository extends JpaRepository { + + default Admin getById(Long id) { + return findById(id).orElseThrow(() -> new CustomException(RESOURCE_NOT_FOUND)); + } +} diff --git a/src/main/java/com/listywave/admin/AdminService.java b/src/main/java/com/listywave/admin/AdminService.java new file mode 100644 index 00000000..1024ef80 --- /dev/null +++ b/src/main/java/com/listywave/admin/AdminService.java @@ -0,0 +1,17 @@ +package com.listywave.admin; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +public class AdminService { + + private final AdminRepository adminRepository; + + public void validateExist(Long adminId) { + adminRepository.getById(adminId); + } +} diff --git a/src/main/java/com/listywave/notice/application/converter/NoticeTypeConverter.java b/src/main/java/com/listywave/notice/application/converter/NoticeTypeConverter.java new file mode 100644 index 00000000..fb7c1ca7 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/converter/NoticeTypeConverter.java @@ -0,0 +1,19 @@ +package com.listywave.notice.application.converter; + +import com.listywave.notice.application.domain.NoticeType; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter(autoApply = true) +public class NoticeTypeConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(NoticeType noticeType) { + return String.valueOf(noticeType.getCode()); + } + + @Override + public NoticeType convertToEntityAttribute(String s) { + return NoticeType.codeOf(Integer.parseInt(s)); + } +} diff --git a/src/main/java/com/listywave/notice/application/domain/NoticeType.java b/src/main/java/com/listywave/notice/application/domain/NoticeType.java new file mode 100644 index 00000000..1a8d2353 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/domain/NoticeType.java @@ -0,0 +1,28 @@ +package com.listywave.notice.application.domain; + +import static com.listywave.common.exception.ErrorCode.NOT_EXIST_CODE; + +import com.listywave.common.exception.CustomException; +import java.util.Arrays; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum NoticeType { + + NEWS(1, "소식"), + EVENT(2, "이벤트"), + TIP(3, "팁"), + ; + + private final int code; + private final String viewName; + + public static NoticeType codeOf(int code) { + return Arrays.stream(NoticeType.values()) + .filter(noticeType -> noticeType.getCode() == code) + .findFirst() + .orElseThrow(() -> new CustomException(NOT_EXIST_CODE, NOT_EXIST_CODE.getDetail() + " 입력값: " + code)); + } +} diff --git a/src/main/java/com/listywave/notice/presentation/NoticeController.java b/src/main/java/com/listywave/notice/presentation/NoticeController.java new file mode 100644 index 00000000..8a0d752f --- /dev/null +++ b/src/main/java/com/listywave/notice/presentation/NoticeController.java @@ -0,0 +1,25 @@ +package com.listywave.notice.presentation; + +import com.listywave.admin.AdminService; +import com.listywave.common.auth.Auth; +import com.listywave.notice.application.domain.NoticeType; +import com.listywave.notice.presentation.dto.NoticeCategoryFindResponse; +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.RestController; + +@RestController +@RequiredArgsConstructor +public class NoticeController { + + private final AdminService adminService; + + @GetMapping("/admin/notices/categories") + ResponseEntity> findNoticeCategories(@Auth Long adminId) { + adminService.validateExist(adminId); + List result = NoticeCategoryFindResponse.toList(NoticeType.values()); + return ResponseEntity.ok(result); + } +} diff --git a/src/main/java/com/listywave/notice/presentation/dto/NoticeCategoryFindResponse.java b/src/main/java/com/listywave/notice/presentation/dto/NoticeCategoryFindResponse.java new file mode 100644 index 00000000..0d8d2a84 --- /dev/null +++ b/src/main/java/com/listywave/notice/presentation/dto/NoticeCategoryFindResponse.java @@ -0,0 +1,17 @@ +package com.listywave.notice.presentation.dto; + +import com.listywave.notice.application.domain.NoticeType; +import java.util.Arrays; +import java.util.List; + +public record NoticeCategoryFindResponse( + int code, + String viewName +) { + + public static List toList(NoticeType[] types) { + return Arrays.stream(types) + .map(type -> new NoticeCategoryFindResponse(type.getCode(), type.getViewName())) + .toList(); + } +} diff --git a/src/test/java/com/listywave/notice/application/domain/NoticeTypeTest.java b/src/test/java/com/listywave/notice/application/domain/NoticeTypeTest.java new file mode 100644 index 00000000..3e2e84ca --- /dev/null +++ b/src/test/java/com/listywave/notice/application/domain/NoticeTypeTest.java @@ -0,0 +1,38 @@ +package com.listywave.notice.application.domain; + +import static com.listywave.common.exception.ErrorCode.NOT_EXIST_CODE; +import static com.listywave.notice.application.domain.NoticeType.NEWS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.listywave.common.exception.CustomException; +import com.listywave.common.exception.ErrorCode; +import org.junit.jupiter.api.Test; + +class NoticeTypeTest { + + @Test + void 존재하지_않는_코드_번호로_생성하면_예외가_발생한다() { + // given + int code = 0; + + // when + ErrorCode result = assertThrows(CustomException.class, () -> NoticeType.codeOf(code)) + .getErrorCode(); + + // then + assertThat(result).isEqualTo(NOT_EXIST_CODE); + } + + @Test + void 코드_번호로_생성한다() { + // given + int code = 1; + + // when + NoticeType result = NoticeType.codeOf(code); + + // then + assertThat(result).isEqualTo(NEWS); + } +} From 58e606e1c93c206593038ebe77be481dad9636e4 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 14:08:51 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=20=20feat:=20Notice=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listywave/common/exception/ErrorCode.java | 2 + .../converter/ContentTypeConverter.java | 20 ++++++ .../application/domain/ContentType.java | 17 +++++ .../notice/application/domain/Notice.java | 43 +++++++++++++ .../application/domain/NoticeContent.java | 61 ++++++++++++++++++ .../application/domain/NoticeDescription.java | 38 +++++++++++ .../application/domain/NoticeTitle.java | 38 +++++++++++ .../domain/NoticeDescriptionTest.java | 64 +++++++++++++++++++ .../application/domain/NoticeTitleTest.java | 64 +++++++++++++++++++ 9 files changed, 347 insertions(+) create mode 100644 src/main/java/com/listywave/notice/application/converter/ContentTypeConverter.java create mode 100644 src/main/java/com/listywave/notice/application/domain/ContentType.java create mode 100644 src/main/java/com/listywave/notice/application/domain/Notice.java create mode 100644 src/main/java/com/listywave/notice/application/domain/NoticeContent.java create mode 100644 src/main/java/com/listywave/notice/application/domain/NoticeDescription.java create mode 100644 src/main/java/com/listywave/notice/application/domain/NoticeTitle.java create mode 100644 src/test/java/com/listywave/notice/application/domain/NoticeDescriptionTest.java create mode 100644 src/test/java/com/listywave/notice/application/domain/NoticeTitleTest.java diff --git a/src/main/java/com/listywave/common/exception/ErrorCode.java b/src/main/java/com/listywave/common/exception/ErrorCode.java index 61cb5dd0..4e429d42 100644 --- a/src/main/java/com/listywave/common/exception/ErrorCode.java +++ b/src/main/java/com/listywave/common/exception/ErrorCode.java @@ -44,6 +44,8 @@ public enum ErrorCode { DUPLICATE_NICKNAME_EXCEPTION(BAD_REQUEST, "중복된 닉네임입니다."), DUPLICATE_COLLABORATOR_EXCEPTION(BAD_REQUEST, "이미 동일한 콜라보레이터가 존재합니다"), DUPLICATE_FOLDER_NAME_EXCEPTION(BAD_REQUEST, "중복된 폴더명입니다."), + NULL_OR_BLANK_EXCEPTION(BAD_REQUEST, "값이 null이거나 공백일 수 없습니다."), + NOT_EXIST_CODE(BAD_REQUEST, "존재하지 않는 코드입니다."), // S3 S3_DELETE_OBJECTS_EXCEPTION(INTERNAL_SERVER_ERROR, "S3의 이미지를 삭제 요청하는 과정에서 에러가 발생했습니다."), diff --git a/src/main/java/com/listywave/notice/application/converter/ContentTypeConverter.java b/src/main/java/com/listywave/notice/application/converter/ContentTypeConverter.java new file mode 100644 index 00000000..e31533f9 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/converter/ContentTypeConverter.java @@ -0,0 +1,20 @@ +package com.listywave.notice.application.converter; + + +import com.listywave.notice.application.domain.ContentType; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter(autoApply = true) +public class ContentTypeConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(ContentType contentType) { + return contentType.name().toLowerCase(); + } + + @Override + public ContentType convertToEntityAttribute(String s) { + return ContentType.valueOf(s.toUpperCase()); + } +} diff --git a/src/main/java/com/listywave/notice/application/domain/ContentType.java b/src/main/java/com/listywave/notice/application/domain/ContentType.java new file mode 100644 index 00000000..4508dd76 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/domain/ContentType.java @@ -0,0 +1,17 @@ +package com.listywave.notice.application.domain; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ContentType { + + SUBTITLE, + BODY, + IMAGE, + BUTTON, + LINE, + NOTE, + ; +} diff --git a/src/main/java/com/listywave/notice/application/domain/Notice.java b/src/main/java/com/listywave/notice/application/domain/Notice.java new file mode 100644 index 00000000..e4e510ec --- /dev/null +++ b/src/main/java/com/listywave/notice/application/domain/Notice.java @@ -0,0 +1,43 @@ +package com.listywave.notice.application.domain; + +import static jakarta.persistence.CascadeType.ALL; +import static jakarta.persistence.FetchType.LAZY; +import static lombok.AccessLevel.PROTECTED; + +import com.listywave.common.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = PROTECTED) +public class Notice extends BaseEntity { + + @Column(name = "code", nullable = false) + private NoticeType type; + + @Embedded + private NoticeTitle title; + + @Embedded + private NoticeDescription description; + + @OneToMany(mappedBy = "notice", fetch = LAZY, cascade = ALL, orphanRemoval = true) + private final List contents = new ArrayList<>(); + + public Notice(NoticeType type, NoticeTitle title, NoticeDescription description) { + this.type = type; + this.title = title; + this.description = description; + } + + public void addContents(List contents) { + this.contents.addAll(contents); + } +} diff --git a/src/main/java/com/listywave/notice/application/domain/NoticeContent.java b/src/main/java/com/listywave/notice/application/domain/NoticeContent.java new file mode 100644 index 00000000..abc92457 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/domain/NoticeContent.java @@ -0,0 +1,61 @@ +package com.listywave.notice.application.domain; + +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PRIVATE; +import static lombok.AccessLevel.PROTECTED; + +import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@AllArgsConstructor(access = PRIVATE) +@NoArgsConstructor(access = PROTECTED) +public class NoticeContent { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "notice_id", nullable = false) + private Notice notice; + + @Column(name = "orders", nullable = false) + private int order; + + @Column(nullable = false, length = 30) + private ContentType type; + + @Column(nullable = true, length = 1000) + private String description; + + @Column(nullable = true, length = 2048) + private String imageUrl; + + @Column(nullable = true, length = 50) + private String buttonName; + + @Column(nullable = true, length = 2048) + private String buttonLink; + + public static NoticeContent create( + Notice notice, + int order, + ContentType type, + @Nullable String description, + @Nullable String imageUrl, + @Nullable String buttonName, + @Nullable String buttonLink + ) { + return new NoticeContent(null, notice, order, type, description, imageUrl, buttonName, buttonLink); + } +} diff --git a/src/main/java/com/listywave/notice/application/domain/NoticeDescription.java b/src/main/java/com/listywave/notice/application/domain/NoticeDescription.java new file mode 100644 index 00000000..a1ab884b --- /dev/null +++ b/src/main/java/com/listywave/notice/application/domain/NoticeDescription.java @@ -0,0 +1,38 @@ +package com.listywave.notice.application.domain; + +import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION; +import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION; +import static lombok.AccessLevel.PROTECTED; + +import com.listywave.common.exception.CustomException; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Embeddable +@EqualsAndHashCode +@NoArgsConstructor(access = PROTECTED, force = true) +public class NoticeDescription { + + private static final int MAX_LENGTH = 30; + + @Column(name = "description", nullable = false, length = MAX_LENGTH) + private final String value; + + public NoticeDescription(String value) { + validate(value); + this.value = value; + } + + private void validate(String value) { + if (value == null || value.isBlank()) { + throw new CustomException(NULL_OR_BLANK_EXCEPTION, NULL_OR_BLANK_EXCEPTION.getDetail() + " 입력값: " + value); + } + if (value.length() > MAX_LENGTH) { + throw new CustomException(LENGTH_EXCEEDED_EXCEPTION, LENGTH_EXCEEDED_EXCEPTION.getDetail() + " 입력값의 길이: " + value.length()); + } + } +} diff --git a/src/main/java/com/listywave/notice/application/domain/NoticeTitle.java b/src/main/java/com/listywave/notice/application/domain/NoticeTitle.java new file mode 100644 index 00000000..585f8bcb --- /dev/null +++ b/src/main/java/com/listywave/notice/application/domain/NoticeTitle.java @@ -0,0 +1,38 @@ +package com.listywave.notice.application.domain; + +import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION; +import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION; +import static lombok.AccessLevel.PROTECTED; + +import com.listywave.common.exception.CustomException; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Embeddable +@EqualsAndHashCode +@NoArgsConstructor(access = PROTECTED, force = true) +public class NoticeTitle { + + private static final int MAX_LENGTH = 30; + + @Column(name = "title", nullable = false, length = MAX_LENGTH) + private final String value; + + public NoticeTitle(String value) { + validate(value); + this.value = value; + } + + private void validate(String value) { + if (value == null || value.isBlank()) { + throw new CustomException(NULL_OR_BLANK_EXCEPTION, NULL_OR_BLANK_EXCEPTION.getDetail() + " 입력값: " + value); + } + if (value.length() > MAX_LENGTH) { + throw new CustomException(LENGTH_EXCEEDED_EXCEPTION, LENGTH_EXCEEDED_EXCEPTION.getDetail() + " 입력값의 길이: " + value.length()); + } + } +} diff --git a/src/test/java/com/listywave/notice/application/domain/NoticeDescriptionTest.java b/src/test/java/com/listywave/notice/application/domain/NoticeDescriptionTest.java new file mode 100644 index 00000000..e77f9359 --- /dev/null +++ b/src/test/java/com/listywave/notice/application/domain/NoticeDescriptionTest.java @@ -0,0 +1,64 @@ +package com.listywave.notice.application.domain; + +import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION; +import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.listywave.common.exception.CustomException; +import com.listywave.common.exception.ErrorCode; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +class NoticeDescriptionTest { + + @Test + void 공지_제목의_최대_길이를_넘으면_예외를_발생한다() { + // given + String value = IntStream.range(0, 30) + .mapToObj(String::valueOf) + .collect(Collectors.joining("")); + + // when + ErrorCode result = assertThrows(CustomException.class, () -> new NoticeDescription(value)) + .getErrorCode(); + + // then + assertThat(result).isEqualTo(LENGTH_EXCEEDED_EXCEPTION); + } + + @ParameterizedTest + @NullAndEmptySource + void 값이_null이거나_빈_값이면_예외를_발생한다(String value) { + // when + ErrorCode result = assertThrows(CustomException.class, () -> new NoticeDescription(value)) + .getErrorCode(); + + // then + assertThat(result).isEqualTo(NULL_OR_BLANK_EXCEPTION); + } + + @Test + void 공지_제목을_정상적으로_생성한다() { + // given + String value = "12345678911234567891123456789"; + + // expect + assertThatNoException().isThrownBy(() -> new NoticeDescription(value)); + } + + @Test + void 값이_같으면_같은_객체다() { + // given + NoticeDescription title1 = new NoticeDescription("123456789"); + NoticeDescription title2 = new NoticeDescription("123456789"); + + // expect + assertThat(title1).isEqualTo(title2); + assertThat(title1).hasSameHashCodeAs(title2); + } +} diff --git a/src/test/java/com/listywave/notice/application/domain/NoticeTitleTest.java b/src/test/java/com/listywave/notice/application/domain/NoticeTitleTest.java new file mode 100644 index 00000000..cc5ab6e8 --- /dev/null +++ b/src/test/java/com/listywave/notice/application/domain/NoticeTitleTest.java @@ -0,0 +1,64 @@ +package com.listywave.notice.application.domain; + +import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION; +import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.listywave.common.exception.CustomException; +import com.listywave.common.exception.ErrorCode; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +class NoticeTitleTest { + + @Test + void 공지_제목의_최대_길이를_넘으면_예외를_발생한다() { + // given + String value = IntStream.range(0, 30) + .mapToObj(String::valueOf) + .collect(Collectors.joining("")); + + // when + ErrorCode result = assertThrows(CustomException.class, () -> new NoticeTitle(value)) + .getErrorCode(); + + // then + assertThat(result).isEqualTo(LENGTH_EXCEEDED_EXCEPTION); + } + + @ParameterizedTest + @NullAndEmptySource + void 값이_null이거나_빈_값이면_예외를_발생한다(String value) { + // when + ErrorCode result = assertThrows(CustomException.class, () -> new NoticeTitle(value)) + .getErrorCode(); + + // then + assertThat(result).isEqualTo(NULL_OR_BLANK_EXCEPTION); + } + + @Test + void 공지_제목을_정상적으로_생성한다() { + // given + String value = "12345678911234567891123456789"; + + // expect + assertThatNoException().isThrownBy(() -> new NoticeTitle(value)); + } + + @Test + void 값이_같으면_같은_객체다() { + // given + NoticeTitle title1 = new NoticeTitle("123456789"); + NoticeTitle title2 = new NoticeTitle("123456789"); + + // expect + assertThat(title1).isEqualTo(title2); + assertThat(title1).hasSameHashCodeAs(title2); + } +} From d59695abbfb9bd1b44aa1b89b62ff2397732f11c Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 14:09:08 +0900 Subject: [PATCH 05/11] =?UTF-8?q?=20feat:=20=EA=B3=B5=EC=A7=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=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 --- .../application/service/NoticeService.java | 25 ++++++++ .../service/dto/NoticeCreateRequest.java | 44 ++++++++++++++ .../notice/presentation/NoticeController.java | 14 +++++ .../notice/repository/NoticeRepository.java | 14 +++++ .../com/listywave/common/IntegrationTest.java | 11 ++++ .../service/NoticeServiceTest.java | 58 +++++++++++++++++++ 6 files changed, 166 insertions(+) create mode 100644 src/main/java/com/listywave/notice/application/service/NoticeService.java create mode 100644 src/main/java/com/listywave/notice/application/service/dto/NoticeCreateRequest.java create mode 100644 src/main/java/com/listywave/notice/repository/NoticeRepository.java create mode 100644 src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java diff --git a/src/main/java/com/listywave/notice/application/service/NoticeService.java b/src/main/java/com/listywave/notice/application/service/NoticeService.java new file mode 100644 index 00000000..502fdce4 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/service/NoticeService.java @@ -0,0 +1,25 @@ +package com.listywave.notice.application.service; + +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.repository.NoticeRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +public class NoticeService { + + private final NoticeRepository noticeRepository; + + public Long create(NoticeCreateRequest request) { + Notice notice = request.toNotice(); + List noticeContents = request.toNoticeContents(notice); + notice.addContents(noticeContents); + return noticeRepository.save(notice).getId(); + } +} diff --git a/src/main/java/com/listywave/notice/application/service/dto/NoticeCreateRequest.java b/src/main/java/com/listywave/notice/application/service/dto/NoticeCreateRequest.java new file mode 100644 index 00000000..bdfd8518 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/service/dto/NoticeCreateRequest.java @@ -0,0 +1,44 @@ +package com.listywave.notice.application.service.dto; + +import com.listywave.notice.application.domain.ContentType; +import com.listywave.notice.application.domain.Notice; +import com.listywave.notice.application.domain.NoticeContent; +import com.listywave.notice.application.domain.NoticeDescription; +import com.listywave.notice.application.domain.NoticeTitle; +import com.listywave.notice.application.domain.NoticeType; +import jakarta.annotation.Nullable; +import java.util.List; + +public record NoticeCreateRequest( + int categoryCode, + String title, + String description, + List contents +) { + + public record ContentDto( + int order, + String type, + @Nullable String description, + @Nullable String imageUrl, + @Nullable String buttonName, + @Nullable String buttonLink + ) { + } + + public Notice toNotice() { + return new Notice(NoticeType.codeOf(categoryCode), new NoticeTitle(title), new NoticeDescription(description)); + } + + public List toNoticeContents(Notice notice) { + return contents.stream() + .map(it -> NoticeContent.create(notice, + it.order, + ContentType.valueOf(it.type.toUpperCase()), + it.description, + it.imageUrl, + it.buttonName, + it.buttonLink) + ).toList(); + } +} diff --git a/src/main/java/com/listywave/notice/presentation/NoticeController.java b/src/main/java/com/listywave/notice/presentation/NoticeController.java index 8a0d752f..dde9df49 100644 --- a/src/main/java/com/listywave/notice/presentation/NoticeController.java +++ b/src/main/java/com/listywave/notice/presentation/NoticeController.java @@ -3,11 +3,16 @@ import com.listywave.admin.AdminService; import com.listywave.common.auth.Auth; 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.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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @@ -15,6 +20,7 @@ public class NoticeController { private final AdminService adminService; + private final NoticeService noticeService; @GetMapping("/admin/notices/categories") ResponseEntity> findNoticeCategories(@Auth Long adminId) { @@ -22,4 +28,12 @@ ResponseEntity> findNoticeCategories(@Auth Long List result = NoticeCategoryFindResponse.toList(NoticeType.values()); return ResponseEntity.ok(result); } + + @PostMapping("/admin/notices") + ResponseEntity create( + @RequestBody NoticeCreateRequest request + ) { + Long id = noticeService.create(request); + return ResponseEntity.created(URI.create("/admin/notices/" + id)).build(); + } } diff --git a/src/main/java/com/listywave/notice/repository/NoticeRepository.java b/src/main/java/com/listywave/notice/repository/NoticeRepository.java new file mode 100644 index 00000000..ec203a8e --- /dev/null +++ b/src/main/java/com/listywave/notice/repository/NoticeRepository.java @@ -0,0 +1,14 @@ +package com.listywave.notice.repository; + +import static com.listywave.common.exception.ErrorCode.RESOURCE_NOT_FOUND; + +import com.listywave.common.exception.CustomException; +import com.listywave.notice.application.domain.Notice; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NoticeRepository extends JpaRepository { + + default Notice getById(Long id) { + return findById(id).orElseThrow(() -> new CustomException(RESOURCE_NOT_FOUND, "존재하지 않는 공지입니다.")); + } +} diff --git a/src/test/java/com/listywave/common/IntegrationTest.java b/src/test/java/com/listywave/common/IntegrationTest.java index 34cc226c..56ce1507 100644 --- a/src/test/java/com/listywave/common/IntegrationTest.java +++ b/src/test/java/com/listywave/common/IntegrationTest.java @@ -6,6 +6,7 @@ import static com.listywave.user.fixture.UserFixture.유진; import static com.listywave.user.fixture.UserFixture.정수; +import com.listywave.admin.AdminRepository; import com.listywave.alarm.application.service.AlarmService; import com.listywave.alarm.repository.AlarmRepository; import com.listywave.auth.application.domain.kakao.KakaoOauthClient; @@ -23,6 +24,8 @@ import com.listywave.list.repository.list.ListRepository; import com.listywave.list.repository.reply.ReplyRepository; import com.listywave.mention.MentionRepository; +import com.listywave.notice.application.service.NoticeService; +import com.listywave.notice.repository.NoticeRepository; import com.listywave.reaction.repository.ReactionStatsRepository; import com.listywave.reaction.repository.UserReactionRepository; import com.listywave.topic.application.service.TopicService; @@ -90,12 +93,20 @@ public abstract class IntegrationTest { protected UserReactionRepository userReactionRepository; @Autowired protected ReactionStatsRepository reactionStatsRepository; + @Autowired + protected AdminRepository adminRepository; + @Autowired + protected NoticeService noticeService; + @Autowired + protected NoticeRepository noticeRepository; protected User dh, js, ej, sy; protected ListEntity list; @BeforeEach void setUp() { + noticeRepository.deleteAll(); + adminRepository.deleteAllInBatch(); historyRepository.deleteAll(); userReactionRepository.deleteAllInBatch(); reactionStatsRepository.deleteAllInBatch(); diff --git a/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java b/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java new file mode 100644 index 00000000..47a2b750 --- /dev/null +++ b/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java @@ -0,0 +1,58 @@ +package com.listywave.notice.application.service; + +import static com.listywave.notice.application.domain.NoticeType.NEWS; +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 java.util.List; +import org.junit.jupiter.api.Test; + +public class NoticeServiceTest extends IntegrationTest { + + @Test + void 공지를_생성한다() { + // given + NoticeCreateRequest request = 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) + ) + ); + + // when + noticeService.create(request); + + // then + Notice result = noticeRepository.getById(1L); + 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() +// ); +// } + ); + } +} From 3de2eed7ecfcb9bdd4414376b1982b988ab9dd54 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 15:57:35 +0900 Subject: [PATCH 06/11] =?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() ); } } From 2063ce619fe64ee941be7fe5a026059d3525c147 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 16:22:21 +0900 Subject: [PATCH 07/11] =?UTF-8?q?=20=20feat:=20=EA=B3=B5=EC=A7=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=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 | 13 +++ .../application/service/NoticeService.java | 27 +++++- .../service/dto/NoticeUpdateRequest.java | 39 ++++++++ .../notice/presentation/NoticeController.java | 15 +++ .../notice/repository/NoticeRepository.java | 4 +- .../service/NoticeServiceTest.java | 91 +++++++++++++++++-- 6 files changed, 175 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/listywave/notice/application/service/dto/NoticeUpdateRequest.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 633b9ea7..56bc399f 100644 --- a/src/main/java/com/listywave/notice/application/domain/Notice.java +++ b/src/main/java/com/listywave/notice/application/domain/Notice.java @@ -54,4 +54,17 @@ public String getFirstImageUrl() { .findFirst(); return result.orElse(null); } + + public void update(NoticeType type, NoticeTitle title, NoticeDescription description, List contents) { + this.type = type; + this.title = title; + this.description = description; + + this.contents.clear(); + this.contents.addAll(contents); + } + + public void changeExposure() { + this.isExposed = !this.isExposed; + } } 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 3b65ed8f..2f79855e 100644 --- a/src/main/java/com/listywave/notice/application/service/NoticeService.java +++ b/src/main/java/com/listywave/notice/application/service/NoticeService.java @@ -2,10 +2,14 @@ import com.listywave.notice.application.domain.Notice; import com.listywave.notice.application.domain.NoticeContent; +import com.listywave.notice.application.domain.NoticeDescription; +import com.listywave.notice.application.domain.NoticeTitle; +import com.listywave.notice.application.domain.NoticeType; 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.application.service.dto.NoticeUpdateRequest; import com.listywave.notice.repository.NoticeRepository; import java.util.List; import lombok.RequiredArgsConstructor; @@ -40,9 +44,26 @@ public List findAllToUser() { @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); + Notice result = noticeRepository.findByIdWithFetch(id); + Notice prevNotice = noticeRepository.findByIdWithFetch(id - 1); + Notice nextNotice = noticeRepository.findByIdWithFetch(id + 1); return NoticeFindResponse.of(result, prevNotice, nextNotice); } + + public void update(NoticeUpdateRequest request, Long noticeId) { + Notice notice = noticeRepository.findByIdWithFetch(noticeId); + List newNoticeContents = request.toNoticeContents(notice); + + notice.update( + NoticeType.codeOf(request.categoryCode()), + new NoticeTitle(request.title()), + new NoticeDescription(request.description()), + newNoticeContents + ); + } + + public void updateExposure(Long id) { + Notice notice = noticeRepository.findByIdWithFetch(id); + notice.changeExposure(); + } } diff --git a/src/main/java/com/listywave/notice/application/service/dto/NoticeUpdateRequest.java b/src/main/java/com/listywave/notice/application/service/dto/NoticeUpdateRequest.java new file mode 100644 index 00000000..359348d9 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/service/dto/NoticeUpdateRequest.java @@ -0,0 +1,39 @@ +package com.listywave.notice.application.service.dto; + +import com.listywave.notice.application.domain.ContentType; +import com.listywave.notice.application.domain.Notice; +import com.listywave.notice.application.domain.NoticeContent; +import java.util.List; + +public record NoticeUpdateRequest( + int categoryCode, + String title, + String description, + List contents +) { + + + public List toNoticeContents(Notice notice) { + return contents.stream() + .map(it -> NoticeContent.create( + notice, + it.order, + ContentType.valueOf(it.type.toUpperCase()), + it.description, + it.imageUrl, + it.buttonName, + it.buttonLink + ) + ).toList(); + } + + public record ContentDto( + int order, + String type, + String description, + String imageUrl, + String buttonName, + String buttonLink + ) { + } +} diff --git a/src/main/java/com/listywave/notice/presentation/NoticeController.java b/src/main/java/com/listywave/notice/presentation/NoticeController.java index f45529c2..6e9bb98a 100644 --- a/src/main/java/com/listywave/notice/presentation/NoticeController.java +++ b/src/main/java/com/listywave/notice/presentation/NoticeController.java @@ -8,14 +8,17 @@ 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.application.service.dto.NoticeUpdateRequest; 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.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -58,4 +61,16 @@ ResponseEntity findOneSpecific(@PathVariable Long noticeId) NoticeFindResponse result = noticeService.findOneSpecific(noticeId); return ResponseEntity.ok(result); } + + @PutMapping("/admin/notices/{noticeId}") + ResponseEntity update(@RequestBody NoticeUpdateRequest request, @PathVariable Long noticeId) { + noticeService.update(request, noticeId); + return ResponseEntity.noContent().build(); + } + + @PatchMapping("/admin/notices/{noticeId}") + ResponseEntity updateExposure(@PathVariable Long noticeId) { + noticeService.updateExposure(noticeId); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/com/listywave/notice/repository/NoticeRepository.java b/src/main/java/com/listywave/notice/repository/NoticeRepository.java index 52e75594..2c609e87 100644 --- a/src/main/java/com/listywave/notice/repository/NoticeRepository.java +++ b/src/main/java/com/listywave/notice/repository/NoticeRepository.java @@ -16,8 +16,8 @@ default Notice getById(Long id) { @Query(""" select n from Notice n - join NoticeContent nc on nc.notice = n + join fetch NoticeContent nc on nc.notice = n where n.id = :noticeId """) - Notice findOne(Long noticeId); + Notice findByIdWithFetch(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 eb152525..0c54ac83 100644 --- a/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java +++ b/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java @@ -10,10 +10,13 @@ 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 com.listywave.notice.application.service.dto.NoticeUpdateRequest; import java.util.List; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public class NoticeServiceTest extends IntegrationTest { @@ -87,7 +90,21 @@ public class NoticeServiceTest extends IntegrationTest { @Test void 이전_혹은_다음_공지가_없으면_null이_반환된다() { // given - NoticeCreateRequest request = new NoticeCreateRequest( + NoticeCreateRequest request = createNoticeCreateRequest(); + + // when + Long id = noticeService.create(request); + + // then + NoticeFindResponse result = noticeService.findOneSpecific(id); + assertAll( + () -> assertThat(result.prevNotice()).isNull(), + () -> assertThat(result.nextNotice()).isNull() + ); + } + + private NoticeCreateRequest createNoticeCreateRequest() { + return new NoticeCreateRequest( 1, "공지입니다", "공지에요", @@ -99,15 +116,71 @@ public class NoticeServiceTest extends IntegrationTest { new ContentDto(5, "note", "유의사항입니다", null, null, null) ) ); + } - // when - Long id = noticeService.create(request); + @Nested + class 공지_수정 { - // then - NoticeFindResponse result = noticeService.findOneSpecific(id); - assertAll( - () -> assertThat(result.prevNotice()).isNull(), - () -> assertThat(result.nextNotice()).isNull() - ); + @Test + void 공지를_수정한다() { + // given + NoticeCreateRequest createRequest = createNoticeCreateRequest(); + Long noticeId = noticeService.create(createRequest); + + // when + NoticeUpdateRequest updateRequest = new NoticeUpdateRequest( + 2, + "수정했습니다", + "수정했어요", + List.of( + new NoticeUpdateRequest.ContentDto(5, "subtitle", "소제목입니다", null, null, null), + new NoticeUpdateRequest.ContentDto(4, "body", "본문입니다", null, null, null), + new NoticeUpdateRequest.ContentDto(3, "image", "이미지입니다", "https://image.com", null, null), + new NoticeUpdateRequest.ContentDto(2, "button", "버튼입니다", null, "버튼 이름", "https://buttonLink.com"), + new NoticeUpdateRequest.ContentDto(1, "note", "유의사항입니다", null, null, null) + ) + ); + noticeService.update(updateRequest, noticeId); + + // then + NoticeFindResponse result = noticeService.findOneSpecific(noticeId); + List contents = result.contents(); + assertAll( + () -> 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()) + ); + } + + @Test + void 공지_노출_여부를_수정한다() { + // given + NoticeCreateRequest createRequest = createNoticeCreateRequest(); + Long noticeId = noticeService.create(createRequest); + + // when + noticeService.updateExposure(noticeId); + + // then + Notice result = noticeRepository.getById(noticeId); + assertThat(result.isExposed()).isTrue(); + } + + @Test + void 노출이_된_공지를_미노출로_변경한다() { + // given + NoticeCreateRequest createRequest = createNoticeCreateRequest(); + Long noticeId = noticeService.create(createRequest); + noticeService.updateExposure(noticeId); + + // when + noticeService.updateExposure(noticeId); + + // then + Notice result = noticeRepository.getById(noticeId); + assertThat(result.isExposed()).isFalse(); + } } } From 1c6b03e89efbd40b25b7df76b3252e840402cc5c Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 16:36:34 +0900 Subject: [PATCH 08/11] =?UTF-8?q?=20feat:=20=EA=B3=B5=EC=A7=80=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=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/service/NoticeService.java | 4 ++++ .../notice/presentation/NoticeController.java | 7 +++++++ .../application/service/NoticeServiceTest.java | 15 +++++++++++++++ 3 files changed, 26 insertions(+) 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 2f79855e..d67fd331 100644 --- a/src/main/java/com/listywave/notice/application/service/NoticeService.java +++ b/src/main/java/com/listywave/notice/application/service/NoticeService.java @@ -66,4 +66,8 @@ public void updateExposure(Long id) { Notice notice = noticeRepository.findByIdWithFetch(id); notice.changeExposure(); } + + public void delete(Long id) { + noticeRepository.deleteById(id); + } } diff --git a/src/main/java/com/listywave/notice/presentation/NoticeController.java b/src/main/java/com/listywave/notice/presentation/NoticeController.java index 6e9bb98a..3ce13dd5 100644 --- a/src/main/java/com/listywave/notice/presentation/NoticeController.java +++ b/src/main/java/com/listywave/notice/presentation/NoticeController.java @@ -14,6 +14,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -73,4 +74,10 @@ ResponseEntity updateExposure(@PathVariable Long noticeId) { noticeService.updateExposure(noticeId); return ResponseEntity.noContent().build(); } + + @DeleteMapping("/admin/notices/{noticeId}") + ResponseEntity delete(@PathVariable Long noticeId) { + noticeService.delete(noticeId); + return ResponseEntity.noContent().build(); + } } 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 0c54ac83..016e79f9 100644 --- a/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java +++ b/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java @@ -16,6 +16,7 @@ import com.listywave.notice.application.service.dto.NoticeFindResponse; import com.listywave.notice.application.service.dto.NoticeUpdateRequest; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -183,4 +184,18 @@ class 공지_수정 { assertThat(result.isExposed()).isFalse(); } } + + @Test + void 공지_삭제() { + // given + NoticeCreateRequest createRequest = createNoticeCreateRequest(); + Long noticeId = noticeService.create(createRequest); + + // when + noticeService.delete(noticeId); + + // then + Optional result = noticeRepository.findById(noticeId); + assertThat(result).isEmpty(); + } } From 8989a840a1f542648c3e6493d5dd78118087bf8f Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 16:39:47 +0900 Subject: [PATCH 09/11] =?UTF-8?q?=20fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=B2=B4=ED=81=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/listywave/notice/presentation/NoticeController.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/com/listywave/notice/presentation/NoticeController.java b/src/main/java/com/listywave/notice/presentation/NoticeController.java index 3ce13dd5..f7fa8ff1 100644 --- a/src/main/java/com/listywave/notice/presentation/NoticeController.java +++ b/src/main/java/com/listywave/notice/presentation/NoticeController.java @@ -1,7 +1,5 @@ package com.listywave.notice.presentation; -import com.listywave.admin.AdminService; -import com.listywave.common.auth.Auth; import com.listywave.notice.application.domain.NoticeType; import com.listywave.notice.application.service.NoticeService; import com.listywave.notice.application.service.dto.NoticeCreateRequest; @@ -27,12 +25,10 @@ @RequiredArgsConstructor public class NoticeController { - private final AdminService adminService; private final NoticeService noticeService; @GetMapping("/admin/notices/categories") - ResponseEntity> findNoticeCategories(@Auth Long adminId) { - adminService.validateExist(adminId); + ResponseEntity> findNoticeCategories() { List result = NoticeCategoryFindResponse.toList(NoticeType.values()); return ResponseEntity.ok(result); } From 6a7d20499a965b135c56117b2e239b104d575d8f Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 18:12:31 +0900 Subject: [PATCH 10/11] =?UTF-8?q?=20refactor:=20=EB=B9=A0=EC=A7=84=20@Null?= =?UTF-8?q?able=20=EC=B6=94=EA=B0=80=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/application/service/dto/NoticeFindResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 3e1c797f..c96689d6 100644 --- a/src/main/java/com/listywave/notice/application/service/dto/NoticeFindResponse.java +++ b/src/main/java/com/listywave/notice/application/service/dto/NoticeFindResponse.java @@ -19,7 +19,7 @@ public record NoticeFindResponse( BesideNoticeDto nextNotice ) { - public static NoticeFindResponse of(Notice notice, Notice prevNotice, Notice nextNotice) { + public static NoticeFindResponse of(Notice notice, @Nullable Notice prevNotice, @Nullable Notice nextNotice) { return NoticeFindResponse.builder() .id(notice.getId()) .category(notice.getType().getViewName()) From dffe121e15dfdc7f514b06ffbd4fe6e1028d4a8c Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Wed, 30 Oct 2024 18:17:25 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=20test:=20NoticeCreateRequest=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B6=80=EB=B6=84=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/NoticeServiceTest.java | 83 ++++++------------- 1 file changed, 25 insertions(+), 58 deletions(-) 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 016e79f9..0caa0ff1 100644 --- a/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java +++ b/src/test/java/com/listywave/notice/application/service/NoticeServiceTest.java @@ -25,42 +25,9 @@ public class NoticeServiceTest extends IntegrationTest { @Test 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) - ) - ); + NoticeCreateRequest request1 = createNoticeCreateRequest(1); + NoticeCreateRequest request2 = createNoticeCreateRequest(2); + NoticeCreateRequest request3 = createNoticeCreateRequest(3); // when Long id1 = noticeService.create(request1); @@ -72,8 +39,8 @@ public class NoticeServiceTest extends IntegrationTest { assertAll( () -> assertThat(result.id()).isEqualTo(id2), () -> assertThat(result.category()).isEqualTo(EVENT.getViewName()), - () -> assertThat(result.title()).isEqualTo("두 번째 공지입니다"), - () -> assertThat(result.description()).isEqualTo("두 번째 공지에요"), + () -> assertThat(result.title()).isEqualTo(2 + "번 째 공지입니다"), + () -> assertThat(result.description()).isEqualTo(2 + "번 째 공지에요"), () -> { List contents = result.contents(); assertThat(contents).hasSize(5); @@ -88,10 +55,25 @@ public class NoticeServiceTest extends IntegrationTest { ); } + private NoticeCreateRequest createNoticeCreateRequest(int th) { + return new NoticeCreateRequest( + th, + th + "번 째 공지입니다", + th + "번 째 공지에요", + 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) + ) + ); + } + @Test void 이전_혹은_다음_공지가_없으면_null이_반환된다() { // given - NoticeCreateRequest request = createNoticeCreateRequest(); + NoticeCreateRequest request = createNoticeCreateRequest(1); // when Long id = noticeService.create(request); @@ -104,28 +86,13 @@ public class NoticeServiceTest extends IntegrationTest { ); } - private NoticeCreateRequest createNoticeCreateRequest() { - return 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) - ) - ); - } - @Nested class 공지_수정 { @Test void 공지를_수정한다() { // given - NoticeCreateRequest createRequest = createNoticeCreateRequest(); + NoticeCreateRequest createRequest = createNoticeCreateRequest(1); Long noticeId = noticeService.create(createRequest); // when @@ -158,7 +125,7 @@ class 공지_수정 { @Test void 공지_노출_여부를_수정한다() { // given - NoticeCreateRequest createRequest = createNoticeCreateRequest(); + NoticeCreateRequest createRequest = createNoticeCreateRequest(1); Long noticeId = noticeService.create(createRequest); // when @@ -172,7 +139,7 @@ class 공지_수정 { @Test void 노출이_된_공지를_미노출로_변경한다() { // given - NoticeCreateRequest createRequest = createNoticeCreateRequest(); + NoticeCreateRequest createRequest = createNoticeCreateRequest(1); Long noticeId = noticeService.create(createRequest); noticeService.updateExposure(noticeId); @@ -188,7 +155,7 @@ class 공지_수정 { @Test void 공지_삭제() { // given - NoticeCreateRequest createRequest = createNoticeCreateRequest(); + NoticeCreateRequest createRequest = createNoticeCreateRequest(1); Long noticeId = noticeService.create(createRequest); // when