리아키텍쳐링중

This commit is contained in:
jinia91
2022-03-23 23:43:40 +09:00
parent 5b7ec9b287
commit 561df13bca
26 changed files with 401 additions and 299 deletions

View File

@@ -2,11 +2,14 @@ package myblog.blog.article.adapter.incomming.web;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.TagsQueries;
import myblog.blog.article.application.port.request.ArticleCreateRequest;
import myblog.blog.article.application.port.request.ArticleEditRequest;
import myblog.blog.article.application.port.incomming.ArticleUseCase;
import myblog.blog.article.application.port.incomming.TempArticleUseCase;
import myblog.blog.article.domain.Article;
import myblog.blog.article.dto.*;
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
import myblog.blog.article.model.*;
import myblog.blog.article.application.port.incomming.ArticleQueriesUseCase;
import myblog.blog.article.application.port.incomming.TagsQueriesUseCase;
import myblog.blog.category.service.CategoryService;
import myblog.blog.category.dto.*;
import myblog.blog.member.auth.PrincipalDetails;
@@ -39,9 +42,10 @@ import static myblog.blog.shared.utils.MarkdownUtils.*;
public class ArticleController {
private final ArticleUseCase articleUseCase;
private final ArticleQueriesUseCase articleQueriesUseCase;
private final TempArticleUseCase tempArticleUseCase;
private final CategoryService categoryService;
private final TagsQueries tagsQueries;
private final TagsQueriesUseCase tagsQueriesUseCase;
private final LayoutRenderingQueries layoutRenderingQueries;
private final ModelMapper modelMapper;
@@ -49,7 +53,7 @@ public class ArticleController {
String getArticleWriteForm(Model model) {
layoutRenderingQueries.AddLayoutTo(model);
model.addAttribute("categoryInput", getCategoryDtosForForm());
model.addAttribute("tagsInput", tagsQueries.findAllTagDtos());
model.addAttribute("tagsInput", tagsQueriesUseCase.findAllTagDtos());
model.addAttribute("articleDto", new ArticleForm());
return "article/articleWriteForm";
}
@@ -64,7 +68,7 @@ public class ArticleController {
if (errors.hasErrors()) {
getArticleWriteForm(model);
}
Long articleId = articleUseCase.writeArticle(articleForm, principal.getMember());
Long articleId = articleUseCase.writeArticle(ArticleCreateRequest.from(articleForm,principal.getMemberId()));
articleUseCase.backupArticle(articleId);
tempArticleUseCase.deleteTemp();
return "redirect:/article/view?articleId=" + articleId;
@@ -74,18 +78,10 @@ public class ArticleController {
*/
@GetMapping("/article/edit")
String updateArticle(@RequestParam Long articleId, Model model) {
// 기존 아티클 DTO 전처리
Article article = articleUseCase.readArticle(articleId);
ArticleDtoForEdit articleDto = modelMapper.map(article, ArticleDtoForEdit.class);
List<String> articleTagStrings = article.getArticleTagLists()
.stream()
.map(articleTag -> articleTag.getTags().getName())
.collect(Collectors.toList());
articleDto.setArticleTagList(articleTagStrings);
//
ArticleResponseForEdit articleDto = articleQueriesUseCase.getArticleForEdit(articleId);
layoutRenderingQueries.AddLayoutTo(model);
model.addAttribute("categoryInput", getCategoryDtosForForm());
model.addAttribute("tagsInput", tagsQueries.findAllTagDtos());
model.addAttribute("tagsInput", tagsQueriesUseCase.findAllTagDtos());
model.addAttribute("articleDto", articleDto);
return "article/articleEditForm";
}
@@ -97,7 +93,7 @@ public class ArticleController {
@Transactional
String editArticle(@RequestParam Long articleId,
@ModelAttribute ArticleForm articleForm) {
articleUseCase.editArticle(articleId, articleForm);
articleUseCase.editArticle(ArticleEditRequest.from(articleId, articleForm));
return "redirect:/article/view?articleId=" + articleId;
}
@@ -120,16 +116,13 @@ public class ArticleController {
@RequestParam Integer tier,
@RequestParam Integer page,
Model model) {
// DTO 매핑 전처리
PagingBoxDto pagingBoxDto =
PagingBoxDto.createOf(page, getTotalArticleCntByCategory(category, categoryService.getCategoryForView()));
Slice<ArticleDtoForCardBox> articleDtoList =
articleUseCase.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum())
.map(article -> modelMapper.map(article, ArticleDtoForCardBox.class));
//
Slice<ArticleResponseForCardBox> articleDtoList =
articleQueriesUseCase.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum());
for(ArticleDtoForCardBox articleDto : articleDtoList){
for(ArticleResponseForCardBox articleDto : articleDtoList){
articleDto.setContent(Jsoup.parse(getHtmlRenderer().render(getParser().parse(articleDto.getContent()))).text());
}
@@ -148,13 +141,10 @@ public class ArticleController {
String getArticlesListByTag(@RequestParam Integer page,
@RequestParam String tagName,
Model model) {
// DTO 매핑 전처리
Page<ArticleDtoForCardBox> articleList =
articleUseCase.getArticlesByTag(tagName, page)
.map(article ->
modelMapper.map(article, ArticleDtoForCardBox.class));
Page<ArticleResponseForCardBox> articleList =
articleQueriesUseCase.getArticlesByTag(tagName, page);
for(ArticleDtoForCardBox article : articleList){
for(ArticleResponseForCardBox article : articleList){
article.setContent(Jsoup.parse(getHtmlRenderer().render(getParser().parse(article.getContent()))).text());
}
@@ -176,13 +166,10 @@ public class ArticleController {
String getArticlesListByKeyword(@RequestParam Integer page,
@RequestParam String keyword,
Model model) {
// DTO 매핑 전처리
Page<ArticleDtoForCardBox> articleList =
articleUseCase.getArticlesByKeyword(keyword, page)
.map(article ->
modelMapper.map(article, ArticleDtoForCardBox.class));
Page<ArticleResponseForCardBox> articleList =
articleQueriesUseCase.getArticlesByKeyword(keyword, page);
for(ArticleDtoForCardBox article : articleList){
for(ArticleResponseForCardBox article : articleList){
article.setContent(Jsoup.parse(getHtmlRenderer().render(getParser().parse(article.getContent()))).text());
}
@@ -220,33 +207,18 @@ public class ArticleController {
}
/*
DTO 매핑 전처리
2. 게시물 상세조회용
2.화면단을 위한 처리
*/
Article article = articleUseCase.readArticle(articleId);
ArticleDtoForDetail articleDtoForDetail = articleQueriesUseCase.getArticleForDetail(articleId);
articleDtoForDetail.setContent(getHtmlRenderer().render(getParser().parse(articleDtoForDetail.getContent())));
ArticleDtoForDetail articleDtoForDetail =
modelMapper.map(article, ArticleDtoForDetail.class);
List<String> tags =
article.getArticleTagLists()
.stream()
.map(tag -> tag.getTags().getName())
.collect(Collectors.toList());
articleDtoForDetail.setTags(tags);
articleDtoForDetail.setContent(getHtmlRenderer().render(getParser().parse(article.getContent())));
List<ArticleDtoByCategory> articleTitlesSortByCategory =
articleUseCase
.getArticlesByCategoryForDetailView(article.getCategory())
.stream()
.map(article1 -> modelMapper.map(article1, ArticleDtoByCategory.class))
.collect(Collectors.toList());
List<ArticleResponseByCategory> articleTitlesSortByCategory =
articleQueriesUseCase
.getArticlesByCategoryForDetailView(articleDtoForDetail.getCategory());
// 3. 메타 태그용 Dto 전처리
StringBuilder metaTags = new StringBuilder();
for (String tag : tags) {
for (String tag : articleDtoForDetail.getTags()) {
metaTags.append(tag).append(", ");
}
@@ -263,25 +235,24 @@ public class ArticleController {
model.addAttribute("metaContents",Jsoup.parse(substringContents).text());
model.addAttribute("articlesSortBycategory", articleTitlesSortByCategory);
// 5. 조회수 증가 검토
addHitWithCookie(article, cookie, response);
// 5. 조회수 증가 검토 및 증가
if(needToAddHitThroughCheckingCookie(articleId, cookie, response)) articleUseCase.addHit(articleId);
return "article/articleView";
}
/*
- 쿠키 추가 / 조회수 증가 검토
*/
private void addHitWithCookie(Article article, String cookie, HttpServletResponse response) {
Long articleId = article.getId();
private boolean needToAddHitThroughCheckingCookie(Long articleId, String cookie, HttpServletResponse response) {
if (cookie == null) {
Cookie viewCookie = new Cookie("view", articleId + "/");
viewCookie.setComment("게시물 조회 확인용");
viewCookie.setMaxAge(60 * 60);
articleUseCase.addHit(article);
response.addCookie(viewCookie);
return true;
} else {
boolean addHitAvailable = false;
boolean isRead = false;
String[] viewCookieList = cookie.split("/");
for (String alreadyRead : viewCookieList) {
@@ -292,9 +263,10 @@ public class ArticleController {
}
if (!isRead) {
cookie += articleId + "/";
article.addHit();
addHitAvailable = true;
}
response.addCookie(new Cookie("view", cookie));
return addHitAvailable;
}
}

View File

@@ -1,28 +1,23 @@
package myblog.blog.main;
package myblog.blog.article.adapter.incomming.web;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import myblog.blog.article.dto.ArticleDtoForCardBox;
import myblog.blog.article.application.ArticleService;
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
import myblog.blog.article.application.port.incomming.ArticleQueriesUseCase;
import myblog.blog.shared.queries.LayoutRenderingQueries;
import org.jsoup.Jsoup;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
import static myblog.blog.shared.utils.MarkdownUtils.*;
@Controller
@RequiredArgsConstructor
@Slf4j
public class MainController {
private final ArticleService articleService;
private final ModelMapper modelMapper;
private final ArticleQueriesUseCase articleQueriesUseCase;
private final LayoutRenderingQueries layoutRenderingQueries;
/*
- 메인 화면 제어용 컨트롤러
@@ -30,10 +25,7 @@ public class MainController {
@GetMapping("/")
public String main(Model model) {
// Dto 전처리
List<ArticleDtoForCardBox> popularArticles = articleService.getPopularArticles()
.stream()
.map(article -> modelMapper.map(article, ArticleDtoForCardBox.class))
.collect(Collectors.toList());
List<ArticleResponseForCardBox> popularArticles = articleQueriesUseCase.getPopularArticles();
//
layoutRenderingQueries.AddLayoutTo(model);
model.addAttribute("popularArticles", popularArticles);
@@ -45,17 +37,14 @@ public class MainController {
*/
@GetMapping("/main/article/{lastArticleId}")
public @ResponseBody
List<ArticleDtoForCardBox> mainNextPage(@PathVariable(required = false) Long lastArticleId) {
List<ArticleResponseForCardBox> mainNextPage(@PathVariable(required = false) Long lastArticleId) {
// Entity to Dto
List<ArticleDtoForCardBox> articles = articleService.getRecentArticles(lastArticleId)
.stream()
.map(article -> modelMapper.map(article, ArticleDtoForCardBox.class))
.collect(Collectors.toList());
List<ArticleResponseForCardBox> articles = articleQueriesUseCase.getRecentArticles(lastArticleId);
// 화면렌더링을 위한 파싱
for(ArticleDtoForCardBox article : articles){
for(ArticleResponseForCardBox article : articles){
String content = Jsoup.parse(getHtmlRenderer().render(getParser().parse(article.getContent()))).text();
if(content.length()>300) {
content = content.substring(0, 300);

View File

@@ -3,7 +3,7 @@ package myblog.blog.article.adapter.incomming.web;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.domain.TempArticle;
import myblog.blog.article.application.TempArticleService;
import myblog.blog.article.dto.TempArticleDto;
import myblog.blog.article.model.TempArticleDto;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;

View File

@@ -0,0 +1,162 @@
package myblog.blog.article.application;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
import myblog.blog.article.application.port.incomming.ArticleQueriesUseCase;
import myblog.blog.article.application.port.outgoing.ArticleRepositoryPort;
import myblog.blog.article.domain.Article;
import myblog.blog.article.model.ArticleResponseByCategory;
import myblog.blog.article.model.ArticleDtoForDetail;
import myblog.blog.article.model.ArticleResponseForEdit;
import myblog.blog.category.domain.Category;
import myblog.blog.category.service.CategoryService;
import org.modelmapper.ModelMapper;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
@RequiredArgsConstructor
public class ArticleQueries implements ArticleQueriesUseCase {
private final ArticleRepositoryPort articleRepositoryPort;
private final CategoryService categoryService;
private final ModelMapper modelMapper;
/*
- 메인화면 위한 인기 아티클 6개 목록 가져오기
- 레이아웃 렌더링 성능 향상을 위해 캐싱작업
카테고리 변경 / 아티클 변경이 존재할경우 레이아웃 캐시 초기화
DTO 매핑 로직 서비스단에서 처리
*/
@Override
@Cacheable(value = "layoutCaching", key = "1")
public List<ArticleResponseForCardBox> getPopularArticles() {
return articleRepositoryPort.findTop6ByOrderByHitDesc()
.stream()
.map(article -> modelMapper.map(article, ArticleResponseForCardBox.class))
.collect(Collectors.toList());
}
/*
- 메인화면 위한 최신 아티클 커서 페이징해서 가져오기
- 레이아웃 렌더링 성능 향상을 위해 캐싱작업
카테고리 변경 / 아티클 변경이 존재할경우 레이아웃 캐시 초기화
*/
@Override
@Cacheable(value = "layoutRecentArticleCaching", key = "#lastArticleId")
public List<ArticleResponseForCardBox> getRecentArticles(Long lastArticleId) {
List<Article> articles = lastArticleId.equals(0L) ?
articleRepositoryPort
.findByOrderByIdDescWithList(PageRequest.of(0, 5))
:
articleRepositoryPort
.findByOrderByIdDesc(lastArticleId, PageRequest.of(0, 5));
return articles
.stream()
.map(article -> modelMapper.map(article, ArticleResponseForCardBox.class))
.collect(Collectors.toList());
}
/*
- 카테고리별 게시물 페이징 처리해서 가져오기
*/
@Override
public Slice<ArticleResponseForCardBox> getArticlesByCategory(String category, Integer tier, Integer page) {
Slice<Article> articles;
if (tier.equals(0)) {
articles = articleRepositoryPort
.findByOrderByIdDesc(
PageRequest.of(pageResolve(page), 5));
}
else {
articles = articleRepositoryPort
.findBySupCategoryOrderByIdDesc(
PageRequest.of(pageResolve(page), 5), category);
}
if(articles == null) throw new IllegalArgumentException("NotFoundArticleException");
return articles.map(article -> modelMapper.map(article, ArticleResponseForCardBox.class));
}
/*
- 아티클 수정을 위한 반환
*/
@Override
public ArticleResponseForEdit getArticleForEdit(Long id){
Article article = articleRepositoryPort.findArticleByIdFetchCategoryAndTags(id);
ArticleResponseForEdit articleDto = modelMapper.map(article, ArticleResponseForEdit.class);
List<String> articleTagStrings = article.getArticleTagLists()
.stream()
.map(articleTag -> articleTag.getTags().getName())
.collect(Collectors.toList());
articleDto.setArticleTagList(articleTagStrings);
return articleDto;
}
/*
* - 아티클 상세 조회를 위한 쿼리
* */
@Override
public ArticleDtoForDetail getArticleForDetail(Long id){
Article article = articleRepositoryPort.findArticleByIdFetchCategoryAndTags(id);
ArticleDtoForDetail articleDtoForDetail =
modelMapper.map(article, ArticleDtoForDetail.class);
List<String> tags =
article.getArticleTagLists()
.stream()
.map(tag -> tag.getTags().getName())
.collect(Collectors.toList());
articleDtoForDetail.setTags(tags);
return articleDtoForDetail;
}
/*
- 카테고리별 최신게시물 6개만 아티클 상세뷰 위해 가져오는로직
*/
@Override
public List<ArticleResponseByCategory> getArticlesByCategoryForDetailView(String categoryName){
Category category = categoryService.findCategory(categoryName);
return articleRepositoryPort.findTop6ByCategoryOrderByIdDesc(category)
.stream()
.map(article -> modelMapper.map(article, ArticleResponseByCategory.class))
.collect(Collectors.toList());
}
/*
- 태그별 게시물 페이징 처리해서 가져오기
*/
@Override
public Page<ArticleResponseForCardBox> getArticlesByTag(String tag, Integer page) {
return articleRepositoryPort
.findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag)
.map(article ->
modelMapper.map(article, ArticleResponseForCardBox.class));
}
/*
- 검색어별 게시물 페이징 처리해서 가져오기
*/
@Override
public Page<ArticleResponseForCardBox> getArticlesByKeyword(String keyword, Integer page) {
return articleRepositoryPort
.findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword)
.map(article ->
modelMapper.map(article, ArticleResponseForCardBox.class));
}
/*
- 페이지 시작점 0~1변경 메서드
*/
private int pageResolve(Integer rawPage) {
if (rawPage == null || rawPage == 1) {
return 0;
} else return rawPage - 1;
}
}

View File

@@ -1,21 +1,25 @@
package myblog.blog.article.application;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.port.request.ArticleCreateRequest;
import myblog.blog.article.application.port.request.ArticleEditRequest;
import myblog.blog.article.application.port.incomming.ArticleUseCase;
import myblog.blog.article.adapter.incomming.web.ArticleForm;
import myblog.blog.article.application.port.incomming.TagUseCase;
import myblog.blog.article.application.port.outgoing.ArticleBackupRepositoryPort;
import myblog.blog.article.application.port.outgoing.ArticleRepositoryPort;
import myblog.blog.article.domain.Article;
import myblog.blog.category.domain.Category;
import myblog.blog.member.doamin.Member;
import myblog.blog.category.service.CategoryService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import myblog.blog.member.service.Oauth2MemberService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@@ -23,157 +27,48 @@ import java.util.List;
@RequiredArgsConstructor
public class ArticleService implements ArticleUseCase {
private final TagsService tagsService;
private final TagUseCase tagUseCase;
private final CategoryService categoryService;
private final Oauth2MemberService memberService;
private final ArticleRepositoryPort articleRepositoryPort;
private final ArticleBackupRepositoryPort articleBackupRepositoryPort;
/*
- 아티클 작성 로직
- 글작성시 아티클 캐싱 초기화
*/
@Override
@CacheEvict(value = {"layoutCaching", "layoutRecentArticleCaching","seoCaching"}, allEntries = true)
public Long writeArticle(ArticleForm articleDto, Member writer) {
Article newArticle = articleFrom(articleDto, writer);
public Long writeArticle(ArticleCreateRequest articleCreateRequest) {
Member writer = memberService.findById(articleCreateRequest.getMemberId());
Category category = categoryService.findCategory(articleCreateRequest.getCategory());
Article newArticle = new Article(articleCreateRequest.getTitle(),
articleCreateRequest.getContent(),
articleCreateRequest.getToc(),
writer,
articleCreateRequest.getThumbnailUrl(),
category);
articleRepositoryPort.save(newArticle);
tagsService.createNewTagsAndArticleTagList(articleDto.getTags(), newArticle);
tagUseCase.createNewTagsAndArticleTagList(articleCreateRequest.getTags(), newArticle);
return newArticle.getId();
}
/*
- 아티클 수정 로직
- 글 수정시 아티클 캐싱 초기화
*/
@Override
@CacheEvict(value = {"layoutCaching", "layoutRecentArticleCaching","seoCaching"}, allEntries = true)
public void editArticle(Long articleId, ArticleForm articleForm) {
Article article = articleRepositoryPort.findById(articleId).get();
Category category = categoryService.findCategory(articleForm.getCategory());
tagsService.deleteAllTagsWith(article);
tagsService.createNewTagsAndArticleTagList(articleForm.getTags(), article);
article.isEditedFrom(articleForm,category);
public void editArticle(ArticleEditRequest articleEditRequest) {
Article article = articleRepositoryPort.findById(articleEditRequest.getArticleId())
.orElseThrow(() -> new IllegalArgumentException("NotFoundArticleException"));
Category category = categoryService.findCategory(articleEditRequest.getCategoryName());
tagUseCase.deleteAllTagsWith(article);
tagUseCase.createNewTagsAndArticleTagList(articleEditRequest.getTags(), article);
article.edit(articleEditRequest.getContent(),
articleEditRequest.getTitle(),
articleEditRequest.getToc(),
articleEditRequest.getThumbnailUrl(), category);
}
/*
- 아티클 삭제 로직
- 글 삭제시 아티클 캐싱 초기화
*/
@Override
@CacheEvict(value = {"layoutCaching", "layoutRecentArticleCaching","seoCaching"}, allEntries = true)
public void deleteArticle(Long articleId) {
articleRepositoryPort.deleteArticle(articleId);
}
/*
- 메인화면 위한 인기 아티클 6개 목록 가져오기
- 레이아웃 렌더링 성능 향상을 위해 캐싱작업
카테고리 변경 / 아티클 변경이 존재할경우 레이아웃 캐시 초기화
DTO 매핑 로직 서비스단에서 처리
*/
@Override
@Cacheable(value = "layoutCaching", key = "1")
public List<Article> getPopularArticles() {
return articleRepositoryPort.findTop6ByOrderByHitDesc();
}
/*
- 메인화면 위한 최신 아티클 커서 페이징해서 가져오기
- 레이아웃 렌더링 성능 향상을 위해 캐싱작업
카테고리 변경 / 아티클 변경이 존재할경우 레이아웃 캐시 초기화
*/
@Override
@Cacheable(value = "layoutRecentArticleCaching", key = "#lastArticleId")
public List<Article> getRecentArticles(Long lastArticleId) {
return lastArticleId.equals(0L)?
articleRepositoryPort
.findByOrderByIdDescWithList(PageRequest.of(0, 5))
:
articleRepositoryPort
.findByOrderByIdDesc(lastArticleId, PageRequest.of(0, 5));
}
/*
- 카테고리별 게시물 페이징 처리해서 가져오기
*/
@Override
public Slice<Article> getArticlesByCategory(String category, Integer tier, Integer page) {
Slice<Article> articles = null;
if (tier.equals(0)) {
articles = articleRepositoryPort
.findByOrderByIdDesc(
PageRequest.of(pageResolve(page), 5));
}
if (tier.equals(1)) {
articles = articleRepositoryPort
.findBySupCategoryOrderByIdDesc(
PageRequest.of(pageResolve(page), 5), category);
}
if (tier.equals(2)) {
articles = articleRepositoryPort
.findBySubCategoryOrderByIdDesc(
PageRequest.of(pageResolve(page), 5), category);
}
return articles;
}
/*
- 아티클 읽기 위한 페치로 전체 가져오기
*/
@Override
public Article readArticle(Long id){
return articleRepositoryPort.findArticleByIdFetchCategoryAndTags(id);
}
/*
- 모든 게시물 조회
*/
@Override
public List<Article> getTotalArticle(){
return articleRepositoryPort.findAllByOrderByIdDesc();
}
/*
- 카테고리별 최신게시물 6개만 아티클 상세뷰 위해 가져오는로직
*/
@Override
public List<Article> getArticlesByCategoryForDetailView(Category category){
return articleRepositoryPort.findTop6ByCategoryOrderByIdDesc(category);
}
/*
- 태그별 게시물 페이징 처리해서 가져오기
*/
@Override
public Page<Article> getArticlesByTag(String tag, Integer page) {
return articleRepositoryPort
.findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag);
}
/*
- 검색어별 게시물 페이징 처리해서 가져오기
*/
@Override
public Page<Article> getArticlesByKeyword(String keyword, Integer page) {
return articleRepositoryPort
.findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword);
}
/*
- 아티클 조회수 추가
*/
@Override
public void addHit(Article article) {
article.addHit();
}
@Override
public void backupArticle(Long articleId) {
Article article = articleRepositoryPort.findById(articleId)
@@ -181,27 +76,25 @@ public class ArticleService implements ArticleUseCase {
articleBackupRepositoryPort.backup(article);
}
/*
- 페이지 시작점 0~1변경 메서드
*/
private int pageResolve(Integer rawPage) {
if (rawPage == null || rawPage == 1) {
return 0;
} else return rawPage - 1;
@Override
public void addHit(Long articleId) {
Article article = articleRepositoryPort.findById(articleId)
.orElseThrow(() -> new IllegalArgumentException("NotFoundArticleException"));
article.addHit();
}
/*
- 새로운 아티클 도메인 생성 메서드
- 아티클 도메인으로 반환
*/
private Article articleFrom(ArticleForm articleDto, Member writer) {
return Article.builder()
.title(articleDto.getTitle())
.content(articleDto.getContent())
.toc(articleDto.getToc())
.member(writer)
.thumbnailUrl(articleDto.getThumbnailUrl())
.category(categoryService.findCategory(articleDto.getCategory()))
.build();
@Override
public Article getArticle(Long id){
return articleRepositoryPort.findArticleByIdFetchCategoryAndTags(id);
}
/*
- 모든 아티클 도메인으로 반환
*/
@Override
public List<Article> getTotalArticle(){
return articleRepositoryPort.findAllByOrderByIdDesc();
}
}

View File

@@ -1,13 +1,13 @@
package myblog.blog.article.application;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.port.incomming.ArticleUseCase;
import myblog.blog.article.application.port.incomming.RssUseCase;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import myblog.blog.article.domain.Article;
import myblog.blog.article.application.ArticleService;
import org.jdom2.*;
import org.jdom2.output.*;
import java.sql.Timestamp;
@@ -29,12 +29,12 @@ public class RssService implements RssUseCase {
static final String ITEM_ROOT = "https://www.jiniaslog.co.kr/article/view?articleId=";
private final ArticleService articleService;
private final ArticleUseCase articleUseCase;
@Override
@Cacheable(value = "seoCaching", key = "0")
public String getRssFeed() {
List<Article> articles = articleService.getTotalArticle();
List<Article> articles = articleUseCase.getTotalArticle();
Document doc = makeRssFeedDocumentFrom(articles);
XMLOutputter xmlOutputter = getXmlOutputter();
return xmlOutputter.outputString(doc);

View File

@@ -1,5 +1,6 @@
package myblog.blog.article.application;
import myblog.blog.article.application.port.incomming.ArticleUseCase;
import myblog.blog.article.application.port.incomming.SiteMapUseCase;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;
@@ -8,7 +9,6 @@ import lombok.RequiredArgsConstructor;
import myblog.blog.article.domain.Article;
import myblog.blog.category.domain.Category;
import myblog.blog.article.application.ArticleService;
import myblog.blog.category.service.CategoryService;
import org.jdom2.*;
import org.jdom2.output.*;
@@ -28,13 +28,13 @@ public class SiteMapService implements SiteMapUseCase {
static final String CATEGORYPRO = "&page=1";
static final String ARTICLEPREV = "/article/view?articleId=";
private final ArticleService articleService;
private final ArticleUseCase articleUseCase;
private final CategoryService categoryService;
@Override
@Cacheable(value = "seoCaching", key = "1")
public String getSiteMap(){
List<Article> articles = articleService.getTotalArticle();
List<Article> articles = articleUseCase.getTotalArticle();
List<Category> allCategories = categoryService.getAllCategories();
Document doc = makeSiteMapDocument(articles, allCategories);
XMLOutputter xmlOutputter = getXmlOutputter();

View File

@@ -3,9 +3,9 @@ package myblog.blog.article.application;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.port.outgoing.TagRepositoryPort;
import myblog.blog.article.domain.Tags;
import myblog.blog.article.application.port.incomming.TagsQueriesUseCase;
import myblog.blog.shared.utils.MapperUtils;
import myblog.blog.article.dto.TagsDto;
import myblog.blog.article.adapter.outgoing.persistence.JpaTagsRepository;
import myblog.blog.article.model.TagsDto;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -15,7 +15,7 @@ import java.util.stream.Collectors;
@Component
@Transactional
@RequiredArgsConstructor
public class TagsQueries {
public class TagsQueries implements TagsQueriesUseCase {
private final TagRepositoryPort tagRepositoryPort;
public List<TagsDto> findAllTagDtos(){

View File

@@ -0,0 +1,21 @@
package myblog.blog.article.application.port.incomming;
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
import myblog.blog.article.model.ArticleResponseByCategory;
import myblog.blog.article.model.ArticleDtoForDetail;
import myblog.blog.article.model.ArticleResponseForEdit;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice;
import java.util.List;
public interface ArticleQueriesUseCase {
List<ArticleResponseForCardBox> getPopularArticles();
List<ArticleResponseForCardBox> getRecentArticles(Long lastArticleId);
Slice<ArticleResponseForCardBox> getArticlesByCategory(String category, Integer tier, Integer page);
ArticleResponseForEdit getArticleForEdit(Long id);
ArticleDtoForDetail getArticleForDetail(Long id);
List<ArticleResponseByCategory> getArticlesByCategoryForDetailView(String category);
Page<ArticleResponseForCardBox> getArticlesByTag(String tag, Integer page);
Page<ArticleResponseForCardBox> getArticlesByKeyword(String keyword, Integer page);
}

View File

@@ -1,27 +1,17 @@
package myblog.blog.article.application.port.incomming;
import myblog.blog.article.adapter.incomming.web.ArticleForm;
import myblog.blog.article.application.port.request.ArticleCreateRequest;
import myblog.blog.article.application.port.request.ArticleEditRequest;
import myblog.blog.article.domain.Article;
import myblog.blog.category.domain.Category;
import myblog.blog.member.doamin.Member;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice;
import java.util.List;
public interface ArticleUseCase {
Long writeArticle(ArticleForm articleForm, Member member);
void editArticle(Long articleId, ArticleForm articleForm);
Long writeArticle(ArticleCreateRequest articleCreateRequest);
void editArticle(ArticleEditRequest articleEditRequest);
void deleteArticle(Long articleId);
List<Article> getPopularArticles();
List<Article> getRecentArticles(Long lastArticleId);
Slice<Article> getArticlesByCategory(String category, Integer tier, Integer page);
Article readArticle(Long id);
List<Article> getTotalArticle();
List<Article> getArticlesByCategoryForDetailView(Category category);
Page<Article> getArticlesByTag(String tag, Integer page);
Page<Article> getArticlesByKeyword(String keyword, Integer page);
void addHit(Article article);
void addHit(Long articleId);
void backupArticle(Long articleId);
Article getArticle(Long id);
List<Article> getTotalArticle();
}

View File

@@ -0,0 +1,9 @@
package myblog.blog.article.application.port.incomming;
import myblog.blog.article.model.TagsDto;
import java.util.List;
public interface TagsQueriesUseCase {
List<TagsDto> findAllTagDtos();
}

View File

@@ -0,0 +1,27 @@
package myblog.blog.article.application.port.request;
import lombok.AllArgsConstructor;
import lombok.Getter;
import myblog.blog.article.adapter.incomming.web.ArticleForm;
@Getter
@AllArgsConstructor
public class ArticleCreateRequest {
private Long memberId;
private String title;
private String content;
private String toc;
private String thumbnailUrl;
private String category;
private String tags;
static public ArticleCreateRequest from(ArticleForm articleForm, Long memberId){
return new ArticleCreateRequest(memberId,
articleForm.getTitle(),
articleForm.getContent(),
articleForm.getToc(),
articleForm.getThumbnailUrl(),
articleForm.getCategory(),
articleForm.getTags());
}
}

View File

@@ -0,0 +1,29 @@
package myblog.blog.article.application.port.request;
import lombok.AllArgsConstructor;
import lombok.Getter;
import myblog.blog.article.adapter.incomming.web.ArticleForm;
@Getter
@AllArgsConstructor
public class ArticleEditRequest {
private Long articleId;
private String title;
private String content;
private String toc;
private String thumbnailUrl;
private String categoryName;
private String tags;
static public ArticleEditRequest from(Long articleId, ArticleForm articleForm){
return new ArticleEditRequest(articleId,
articleForm.getTitle(),
articleForm.getContent(),
articleForm.getToc(),
articleForm.getThumbnailUrl(),
articleForm.getCategory(),
articleForm.getTags());
}
}

View File

@@ -1,4 +1,4 @@
package myblog.blog.article.dto;
package myblog.blog.article.application.port.response;
import lombok.Getter;
import lombok.Setter;
@@ -6,11 +6,11 @@ import lombok.Setter;
import java.time.LocalDateTime;
/*
- 메인 화면 출력 아티클 DTO
- 메인 화면 렌더링 아티클 DTO
*/
@Getter
@Setter
public class ArticleDtoForCardBox {
public class ArticleResponseForCardBox {
private Long id;
private String title;
private String content;

View File

@@ -1,19 +1,20 @@
package myblog.blog.article.domain;
import lombok.Builder;
import lombok.Getter;
import myblog.blog.article.adapter.incomming.web.ArticleForm;
import myblog.blog.shared.BasicEntity;
import myblog.blog.category.domain.Category;
import myblog.blog.comment.domain.Comment;
import myblog.blog.member.doamin.Member;
import lombok.Builder;
import lombok.Getter;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
import java.util.*;
/*
- 아티클 Entity
- toc 추후 개발 예정
@@ -88,14 +89,13 @@ public class Article extends BasicEntity {
/*
- 아티클 수정을 위한 로직
*/
public void isEditedFrom(ArticleForm articleForm, Category category){
this.content = articleForm.getContent();
this.title = articleForm.getTitle();
this.toc = articleForm.getToc();
public void edit(String content, String title, String toc, String thumbnailUrl, Category category){
this.content = content;
this.title = title;
this.toc = toc;
this.category = category;
if(articleForm.getThumbnailUrl() != null){
this.thumbnailUrl = articleForm.getThumbnailUrl();
if(thumbnailUrl != null){
this.thumbnailUrl = getThumbnailUrl();
}
}
/*

View File

@@ -1,9 +1,8 @@
package myblog.blog.article.dto;
package myblog.blog.article.model;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import java.util.List;

View File

@@ -1,4 +1,4 @@
package myblog.blog.article.dto;
package myblog.blog.article.model;
import lombok.Getter;
import lombok.Setter;
@@ -7,7 +7,7 @@ import lombok.Setter;
- 카테고리별 게시물 표시용 DTO
*/
@Getter @Setter
public class ArticleDtoByCategory {
public class ArticleResponseByCategory {
private String title;
private Long id;
}

View File

@@ -1,4 +1,4 @@
package myblog.blog.article.dto;
package myblog.blog.article.model;
import lombok.Getter;
import lombok.Setter;
@@ -11,7 +11,7 @@ import java.util.List;
- 아티클 수정 폼을 위한 DTO
*/
@Getter @Setter
public class ArticleDtoForEdit {
public class ArticleResponseForEdit {
private Long id;
private String title;

View File

@@ -1,8 +1,7 @@
package myblog.blog.article.dto;
package myblog.blog.article.model;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.domain.Page;
/*
- 뷰단 페이징 박스 처리를 위한 핸들러

View File

@@ -1,4 +1,4 @@
package myblog.blog.article.dto;
package myblog.blog.article.model;
import lombok.Data;

View File

@@ -1,4 +1,4 @@
package myblog.blog.article.dto;
package myblog.blog.article.model;
import lombok.Getter;
import lombok.Setter;

View File

@@ -5,13 +5,14 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
import java.util.Optional;
public interface CategoryRepository extends JpaRepository<Category, Long> {
/*
- 카테고리 이름으로 카테고리 찾기
*/
Category findByTitle(String title);
Optional<Category> findByTitle(String title);
/*
- 티어별 카테고리들 가져오기

View File

@@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
@@ -25,7 +26,8 @@ public class CategoryService {
- 카테고리 이름으로 카테고리 찾기
*/
public Category findCategory(String title) {
return categoryRepository.findByTitle(title);
return categoryRepository.findByTitle(title)
.orElseThrow(() -> new IllegalArgumentException("NotFoundCategoryException"));
}
/*
@@ -152,7 +154,8 @@ public class CategoryService {
private Category createNewCategory(CategorySimpleView categorySimpleView, String parent) {
Category parentCategory = null;
if (parent != null) {
parentCategory = categoryRepository.findByTitle(parent);
parentCategory = categoryRepository.findByTitle(parent)
.orElseThrow(() -> new IllegalArgumentException("NotFoundCategoryException"));
}
Category category = Category.builder()
@@ -190,7 +193,7 @@ public class CategoryService {
/*
- 최초 필수 더미 카테고리 추가 코드
*/
@PostConstruct
// @PostConstruct
private void insertDummyCategory() {
if(categoryRepository.findByTitle("total")==null) {
Category category0 = Category.builder()

View File

@@ -1,6 +1,7 @@
package myblog.blog.comment.controller;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.port.incomming.ArticleQueriesUseCase;
import myblog.blog.article.domain.Article;
import myblog.blog.article.application.ArticleService;
import myblog.blog.comment.dto.CommentDto;
@@ -22,7 +23,6 @@ import java.util.Objects;
public class CommentController {
private final CommentService commentService;
private final ArticleService articleService;
/*
- 아티클 조회시 아티클에 달린 댓글들 전체 조회
@@ -45,14 +45,12 @@ public class CommentController {
}
Member member = principal.getMember();
Article article = articleService.readArticle(articleId);
// 부모 댓글인지 자식댓글인지 분기로 저장
if(parentId != null){
commentService.saveCComment(commentForm, member, article, parentId);
commentService.saveCComment(commentForm, member, articleId, parentId);
}
else {
commentService.savePComment(commentForm, member, article);
commentService.savePComment(commentForm, member, articleId);
}
return CommentDto.listCreateFrom(commentService.getCommentList(articleId),0);

View File

@@ -1,6 +1,7 @@
package myblog.blog.comment.service;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.port.incomming.ArticleUseCase;
import myblog.blog.article.domain.Article;
import myblog.blog.comment.domain.Comment;
import myblog.blog.comment.dto.CommentDtoForLayout;
@@ -21,6 +22,8 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class CommentService {
private final ArticleUseCase articleUseCase;
private final CommentRepository commentRepository;
private final NaCommentRepository naCommentRepository;
@@ -35,7 +38,9 @@ public class CommentService {
- 부모 댓글 저장
*/
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
public void savePComment(CommentForm commentForm, Member member, Article article){
public void savePComment(CommentForm commentForm, Member member, Long articleId){
Article article = articleUseCase.getArticle(articleId);
Comment comment = Comment.builder()
.article(article)
@@ -54,8 +59,9 @@ public class CommentService {
- 자식 댓글 저장
*/
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
public void saveCComment(CommentForm commentForm, Member member, Article article, Long parentId) {
public void saveCComment(CommentForm commentForm, Member member, Long articleId, Long parentId) {
Article article = articleUseCase.getArticle(articleId);
Comment pComment = commentRepository.findById(parentId).get();
Comment comment = Comment.builder()

View File

@@ -112,4 +112,8 @@ public class Oauth2MemberService extends DefaultOAuth2UserService {
}
public Member findById(Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("NotFoundMemberException"));
}
}