From 6223a37ec34fbae39aab95e7a39730527e4ce320 Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Sun, 6 Mar 2022 20:05:35 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/DetailedArticleDto.java | 46 ++++++++++++ .../in/ArticleAdditionalDataInjector.java | 16 ++++ .../application/port/in/ArticleService.java | 9 +++ .../port/out/repository/AlimiRepository.java | 9 +++ .../common/mapper/ArticleMapper.java | 10 +++ .../community/infra/api/AlimiClient.java | 73 +++++++++++++++++++ .../community/infra/api/CommentClient.java | 2 - .../infra/controller/ArticleController.java | 24 ++++-- 8 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/khumu/community/application/dto/DetailedArticleDto.java create mode 100644 src/main/java/com/khumu/community/application/port/out/repository/AlimiRepository.java create mode 100644 src/main/java/com/khumu/community/infra/api/AlimiClient.java diff --git a/src/main/java/com/khumu/community/application/dto/DetailedArticleDto.java b/src/main/java/com/khumu/community/application/dto/DetailedArticleDto.java new file mode 100644 index 0000000..dae746d --- /dev/null +++ b/src/main/java/com/khumu/community/application/dto/DetailedArticleDto.java @@ -0,0 +1,46 @@ +package com.khumu.community.application.dto; + +import lombok.*; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Builder +public class DetailedArticleDto { + Integer id; + String boardName; + String boardDisplayName; + // TODO: 익명의 경우 작성자를 숨기기 + SimpleUserDto author; + String title; + String content; + @Builder.Default + List images = new ArrayList<>(); + + @Builder.Default + Long commentCount = 0L; + + @Builder.Default + Boolean liked = false; + @Builder.Default + Long likeArticleCount = 0L; + + @Builder.Default + Boolean bookmarked = false; + @Builder.Default + Long bookmarkArticleCount = 0L; + + String kind; + Boolean isHot; + + // TODO: createdAt을 human readable한 text로 + LocalDateTime createdAt; + + @Builder.Default + Boolean isSubscribed = false; +} diff --git a/src/main/java/com/khumu/community/application/port/in/ArticleAdditionalDataInjector.java b/src/main/java/com/khumu/community/application/port/in/ArticleAdditionalDataInjector.java index 160356a..2177eaa 100644 --- a/src/main/java/com/khumu/community/application/port/in/ArticleAdditionalDataInjector.java +++ b/src/main/java/com/khumu/community/application/port/in/ArticleAdditionalDataInjector.java @@ -1,10 +1,12 @@ package com.khumu.community.application.port.in; import com.khumu.community.application.dto.ArticleDto; +import com.khumu.community.application.dto.DetailedArticleDto; import com.khumu.community.application.entity.Article; import com.khumu.community.application.entity.BookmarkArticle; import com.khumu.community.application.entity.LikeArticle; import com.khumu.community.application.entity.User; +import com.khumu.community.application.port.out.repository.AlimiRepository; import com.khumu.community.application.port.out.repository.BookmarkArticleRepository; import com.khumu.community.application.port.out.repository.CommentRepository; import com.khumu.community.application.port.out.repository.LikeArticleRepository; @@ -21,6 +23,7 @@ public class ArticleAdditionalDataInjector { private final LikeArticleRepository likeArticleRepository; private final BookmarkArticleRepository bookmarkArticleRepository; private final CommentRepository commentRepository; + private final AlimiRepository alimiRepository; public ArticleDto inject(ArticleDto articleDto, User requestUser) { articleDto.setCommentCount(commentRepository.countByArticle(articleDto.getId())); @@ -54,4 +57,17 @@ public List inject(List articleDtos, User requestUser) { return articleDtos; } + + public DetailedArticleDto inject(DetailedArticleDto src, User requestUser) { + + if (requestUser == null || requestUser.getUsername() == null) { + src.setIsSubscribed(false); + + return src; + } + + src.setIsSubscribed(alimiRepository.isSubscribed(requestUser.getUsername(), "article", String.valueOf(src.getId()))); + + return src; + } } diff --git a/src/main/java/com/khumu/community/application/port/in/ArticleService.java b/src/main/java/com/khumu/community/application/port/in/ArticleService.java index b386aec..f2e87b7 100644 --- a/src/main/java/com/khumu/community/application/port/in/ArticleService.java +++ b/src/main/java/com/khumu/community/application/port/in/ArticleService.java @@ -1,6 +1,7 @@ package com.khumu.community.application.port.in; import com.khumu.community.application.dto.ArticleDto; +import com.khumu.community.application.dto.DetailedArticleDto; import com.khumu.community.application.dto.input.CreateArticleRequest; import com.khumu.community.application.dto.input.IsAuthorInput; import com.khumu.community.application.dto.input.UpdateArticleRequest; @@ -172,6 +173,14 @@ public Page listArticlesByBoard(User requestUser, String board, Page return articleAdditionalDataInjector.inject(articles.map(articleMapper::toDto), requestUser); } + + // 특정 게시글의 상세 조회 + @Transactional + public DetailedArticleDto getArticle(User requestUser, Integer id) { + Article article = articleRepository.findById(id).get(); + + return articleAdditionalDataInjector.inject(articleMapper.toDetailedDto(article), requestUser); + } @Transactional // TODO: 기존에는 Patch(부분 수정) 방식이었는데 이거 어떻게 대응해줄 것인가? diff --git a/src/main/java/com/khumu/community/application/port/out/repository/AlimiRepository.java b/src/main/java/com/khumu/community/application/port/out/repository/AlimiRepository.java new file mode 100644 index 0000000..074d417 --- /dev/null +++ b/src/main/java/com/khumu/community/application/port/out/repository/AlimiRepository.java @@ -0,0 +1,9 @@ +package com.khumu.community.application.port.out.repository; + +import org.springframework.data.domain.Pageable; + +import java.util.List; + +public interface AlimiRepository { + Boolean isSubscribed(String username, String resourceKind, String resourceId); +} diff --git a/src/main/java/com/khumu/community/common/mapper/ArticleMapper.java b/src/main/java/com/khumu/community/common/mapper/ArticleMapper.java index 8d0f78d..6853991 100644 --- a/src/main/java/com/khumu/community/common/mapper/ArticleMapper.java +++ b/src/main/java/com/khumu/community/common/mapper/ArticleMapper.java @@ -2,6 +2,7 @@ import com.khumu.community.application.dto.ArticleDto; import com.khumu.community.application.dto.ArticleEventDto; +import com.khumu.community.application.dto.DetailedArticleDto; import com.khumu.community.application.dto.ReportDto; import com.khumu.community.application.entity.Article; import com.khumu.community.application.entity.Report; @@ -21,7 +22,16 @@ public interface ArticleMapper { // 나중엔 newImages -> images로 컬럼 명 자체를 바꿔야할 듯 @Mapping(target="images", source="newImages") ArticleDto toDto(Article src); + + @Mapping(target="boardName", source="board.name") + @Mapping(target="boardDisplayName", source="board.displayName") + // TODO + // 나중엔 newImages -> images로 컬럼 명 자체를 바꿔야할 듯 + @Mapping(target="images", source="newImages") + DetailedArticleDto toDetailedDto(Article src); + Article toEntity(ArticleDto src); + @Mapping(target="author", source = "author.username") @Mapping(target="board", source = "board.name") ArticleEventDto toEventDto(Article src); diff --git a/src/main/java/com/khumu/community/infra/api/AlimiClient.java b/src/main/java/com/khumu/community/infra/api/AlimiClient.java new file mode 100644 index 0000000..67a6dc4 --- /dev/null +++ b/src/main/java/com/khumu/community/infra/api/AlimiClient.java @@ -0,0 +1,73 @@ +package com.khumu.community.infra.api; + +import com.khumu.community.application.port.out.repository.AlimiRepository; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.*; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@RequiredArgsConstructor +@Component +@Slf4j +public class AlimiClient implements AlimiRepository { + private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter; + + @Value("${khumu.alimi.host}") + private String alimiApiHost; + + @Override + public Boolean isSubscribed(String username, String resourceKind, String resourceId) { + RestTemplate restTemplate = new RestTemplateBuilder() + .messageConverters(mappingJackson2HttpMessageConverter) + .build(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); // send the post request + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity resp = restTemplate.exchange( + String.format("%s/api/subscriptions/%s/%s/%s", alimiApiHost, username, resourceKind, resourceId), + HttpMethod.GET, + entity, + AlimiClient.IsSubscribedHttpResponse.class + ); + + if (resp.getStatusCode() != HttpStatus.OK) { + log.error("AlimiClient.isSubscribed 의 응답이 200이 아닙니다."); + return false; + } + if (resp.getBody() == null) { + log.error("AlimiClient.isSubscribed 의 응답 body가 null입니다."); + return false; + } + + log.info("AlimiClient.isSubscribed 의 응답: " + resp.getBody()); + + return resp.getBody().getData().getIsActivated(); + } + + @AllArgsConstructor + @NoArgsConstructor + @Getter + @Setter + @ToString + private static class IsSubscribedHttpResponse{ + private IsSubscribedResponse data; + + @AllArgsConstructor + @NoArgsConstructor + @Getter + @Setter + @ToString + private static class IsSubscribedResponse{ + private Long resourceId; + private String resourceKind; + private String subscriber; + private Boolean isActivated; + } + + } +} diff --git a/src/main/java/com/khumu/community/infra/api/CommentClient.java b/src/main/java/com/khumu/community/infra/api/CommentClient.java index 4a98ea7..8d3e9e9 100644 --- a/src/main/java/com/khumu/community/infra/api/CommentClient.java +++ b/src/main/java/com/khumu/community/infra/api/CommentClient.java @@ -28,7 +28,6 @@ @Component @Slf4j public class CommentClient implements CommentRepository { -// private final ObjectMapper objectMapper; private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter; @Value("${khumu.comment.host}") @@ -56,7 +55,6 @@ public Long countByArticle(Integer article) { log.info("CommentClient.countByArticle 의 응답: " + resp.getBody()); return resp.getBody().getCommentCount(); - } @Override diff --git a/src/main/java/com/khumu/community/infra/controller/ArticleController.java b/src/main/java/com/khumu/community/infra/controller/ArticleController.java index 6ef4fe0..93a23b7 100644 --- a/src/main/java/com/khumu/community/infra/controller/ArticleController.java +++ b/src/main/java/com/khumu/community/infra/controller/ArticleController.java @@ -1,6 +1,7 @@ package com.khumu.community.infra.controller; import com.khumu.community.application.dto.ArticleDto; +import com.khumu.community.application.dto.DetailedArticleDto; import com.khumu.community.application.dto.input.CreateArticleRequest; import com.khumu.community.application.dto.input.IsAuthorInput; import com.khumu.community.application.dto.input.UpdateArticleRequest; @@ -27,7 +28,17 @@ public class ArticleController { final private ArticleService articleService; final private LikeArticleService likeArticleService; final private BookmarkArticleService bookmarkArticleService; - + + @PostMapping(value = "/api/community/v1/articles") + @ResponseBody + @ResponseStatus(code = HttpStatus.CREATED) + public DefaultResponse create(@AuthenticationPrincipal User user, @RequestBody CreateArticleRequest body) { + ArticleDto article = articleService.write(user, body); + return DefaultResponse.builder() + .data(article) + .build(); + } + @GetMapping(value = "/api/community/v1/articles") @ResponseBody @ResponseStatus(code = HttpStatus.OK) @@ -67,12 +78,13 @@ public DefaultResponse> list( .build(); } - @PostMapping(value = "/api/community/v1/articles") + @GetMapping(value = "/api/community/v1/articles/{id}") @ResponseBody - @ResponseStatus(code = HttpStatus.CREATED) - public DefaultResponse create(@AuthenticationPrincipal User user, @RequestBody CreateArticleRequest body) { - ArticleDto article = articleService.write(user, body); - return DefaultResponse.builder() + @ResponseStatus(code = HttpStatus.OK) + public DefaultResponse get(@AuthenticationPrincipal User user, @PathVariable Integer id) { + DetailedArticleDto article = articleService.getArticle(user, id); + + return DefaultResponse.builder() .data(article) .build(); }