Merge branch 'dev'
This commit is contained in:
@@ -2,9 +2,9 @@ language: java
|
||||
jdk:
|
||||
- openjdk11
|
||||
|
||||
#branches:
|
||||
# only:
|
||||
# - main
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
|
||||
# travis CI 서버 home
|
||||
cache:
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
package myblog.blog.article.controller;
|
||||
package myblog.blog.article.adapter.incomming.web;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.article.service.*;
|
||||
import myblog.blog.article.dto.*;
|
||||
import myblog.blog.article.application.port.incomming.ArticleUseCase;
|
||||
import myblog.blog.article.application.port.incomming.TempArticleUseCase;
|
||||
import myblog.blog.article.application.port.incomming.ArticleQueriesUseCase;
|
||||
import myblog.blog.article.application.port.incomming.TagsQueriesUseCase;
|
||||
import myblog.blog.article.application.port.request.ArticleCreateRequest;
|
||||
import myblog.blog.article.application.port.request.ArticleEditRequest;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseByCategory;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseForDetail;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseForEdit;
|
||||
import myblog.blog.category.service.CategoryService;
|
||||
import myblog.blog.category.dto.*;
|
||||
import myblog.blog.member.auth.PrincipalDetails;
|
||||
import myblog.blog.member.dto.MemberDto;
|
||||
import myblog.blog.tags.queries.TagsQueries;
|
||||
import myblog.blog.tags.service.TagsService;
|
||||
import myblog.blog.member.dto.MemberVo;
|
||||
import myblog.blog.shared.queries.LayoutRenderingQueries;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
|
||||
import org.springframework.data.domain.*;
|
||||
@@ -38,24 +42,19 @@ import static myblog.blog.shared.utils.MarkdownUtils.*;
|
||||
@RequiredArgsConstructor
|
||||
public class ArticleController {
|
||||
|
||||
private final ArticleService articleService;
|
||||
private final TagsService tagsService;
|
||||
private final ArticleUseCase articleUseCase;
|
||||
private final ArticleQueriesUseCase articleQueriesUseCase;
|
||||
private final TempArticleUseCase tempArticleUseCase;
|
||||
private final TagsQueriesUseCase tagsQueriesUseCase;
|
||||
private final CategoryService categoryService;
|
||||
private final TempArticleService tempArticleService;
|
||||
|
||||
private final TagsQueries tagsQueries;
|
||||
private final LayoutRenderingQueries layoutRenderingQueries;
|
||||
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
/*
|
||||
- 아티클 작성 폼 조회
|
||||
*/
|
||||
@GetMapping("article/write")
|
||||
public String getWriteArticleForm(Model model) {
|
||||
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,35 +63,26 @@ public class ArticleController {
|
||||
*/
|
||||
@PostMapping("article/write")
|
||||
@Transactional
|
||||
public String writeArticle(@Validated ArticleForm articleForm,
|
||||
String writeArticle(@Validated ArticleForm articleForm,
|
||||
@AuthenticationPrincipal PrincipalDetails principal,
|
||||
Errors errors, Model model) {
|
||||
if (errors.hasErrors()) {
|
||||
getWriteArticleForm(model);
|
||||
getArticleWriteForm(model);
|
||||
}
|
||||
Long articleId = articleService.writeArticle(articleForm, principal.getMember());
|
||||
articleService.pushArticleToGithub(articleId);
|
||||
tempArticleService.deleteTemp();
|
||||
Long articleId = articleUseCase.writeArticle(ArticleCreateRequest.from(articleForm,principal.getMemberId()));
|
||||
articleUseCase.backupArticle(articleId);
|
||||
tempArticleUseCase.deleteTemp();
|
||||
return "redirect:/article/view?articleId=" + articleId;
|
||||
}
|
||||
/*
|
||||
- 아티클 수정 폼 조회
|
||||
*/
|
||||
@GetMapping("/article/edit")
|
||||
public String updateArticle(@RequestParam Long articleId,
|
||||
Model model) {
|
||||
// 기존 아티클 DTO 전처리
|
||||
Article article = articleService.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);
|
||||
//
|
||||
String updateArticle(@RequestParam Long articleId, Model model) {
|
||||
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";
|
||||
}
|
||||
@@ -102,9 +92,9 @@ public class ArticleController {
|
||||
*/
|
||||
@PostMapping("/article/edit")
|
||||
@Transactional
|
||||
public String editArticle(@RequestParam Long articleId,
|
||||
@ModelAttribute ArticleForm articleForm) {
|
||||
articleService.editArticle(articleId, articleForm);
|
||||
String editArticle(@RequestParam Long articleId,
|
||||
@ModelAttribute ArticleForm articleForm) {
|
||||
articleUseCase.editArticle(ArticleEditRequest.from(articleId, articleForm));
|
||||
return "redirect:/article/view?articleId=" + articleId;
|
||||
}
|
||||
|
||||
@@ -113,8 +103,8 @@ public class ArticleController {
|
||||
*/
|
||||
@PostMapping("/article/delete")
|
||||
@Transactional
|
||||
public String deleteArticle(@RequestParam Long articleId) {
|
||||
articleService.deleteArticle(articleId);
|
||||
String deleteArticle(@RequestParam Long articleId) {
|
||||
articleUseCase.deleteArticle(articleId);
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
@@ -123,25 +113,22 @@ public class ArticleController {
|
||||
*/
|
||||
@Transactional
|
||||
@GetMapping("article/list")
|
||||
public String getArticlesListByCategory(@RequestParam String category,
|
||||
@RequestParam Integer tier,
|
||||
@RequestParam Integer page,
|
||||
Model model) {
|
||||
// DTO 매핑 전처리
|
||||
PagingBoxDto pagingBoxDto =
|
||||
PagingBoxDto.createOf(page, getTotalArticleCntByCategory(category, categoryService.getCategoryForView()));
|
||||
String getArticlesListByCategory(@RequestParam String category,
|
||||
@RequestParam Integer tier,
|
||||
@RequestParam Integer page,
|
||||
Model model) {
|
||||
PagingBoxHandler pagingBoxHandler =
|
||||
PagingBoxHandler.createOf(page, getTotalArticleCntByCategory(category, categoryService.getCategoryForView()));
|
||||
|
||||
Slice<ArticleDtoForCardBox> articleDtoList =
|
||||
articleService.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum())
|
||||
.map(article -> modelMapper.map(article, ArticleDtoForCardBox.class));
|
||||
//
|
||||
Slice<ArticleResponseForCardBox> articleDtoList =
|
||||
articleQueriesUseCase.getArticlesByCategory(category, tier, pagingBoxHandler.getCurPageNum());
|
||||
|
||||
for(ArticleDtoForCardBox articleDto : articleDtoList){
|
||||
for(ArticleResponseForCardBox articleDto : articleDtoList){
|
||||
articleDto.setContent(Jsoup.parse(getHtmlRenderer().render(getParser().parse(articleDto.getContent()))).text());
|
||||
}
|
||||
|
||||
layoutRenderingQueries.AddLayoutTo(model);
|
||||
model.addAttribute("pagingBox", pagingBoxDto);
|
||||
model.addAttribute("pagingBox", pagingBoxHandler);
|
||||
model.addAttribute("articleList", articleDtoList);
|
||||
|
||||
return "article/articleList";
|
||||
@@ -152,25 +139,22 @@ public class ArticleController {
|
||||
*/
|
||||
@Transactional
|
||||
@GetMapping("article/list/tag/")
|
||||
public String getArticlesListByTag(@RequestParam Integer page,
|
||||
@RequestParam String tagName,
|
||||
Model model) {
|
||||
// DTO 매핑 전처리
|
||||
Page<ArticleDtoForCardBox> articleList =
|
||||
articleService.getArticlesByTag(tagName, page)
|
||||
.map(article ->
|
||||
modelMapper.map(article, ArticleDtoForCardBox.class));
|
||||
String getArticlesListByTag(@RequestParam Integer page,
|
||||
@RequestParam String tagName,
|
||||
Model model) {
|
||||
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());
|
||||
}
|
||||
|
||||
PagingBoxDto pagingBoxDto =
|
||||
PagingBoxDto.createOf(page, (int)articleList.getTotalElements());
|
||||
PagingBoxHandler pagingBoxHandler =
|
||||
PagingBoxHandler.createOf(page, (int)articleList.getTotalElements());
|
||||
|
||||
layoutRenderingQueries.AddLayoutTo(model);
|
||||
model.addAttribute("articleList", articleList);
|
||||
model.addAttribute("pagingBox", pagingBoxDto);
|
||||
model.addAttribute("pagingBox", pagingBoxHandler);
|
||||
|
||||
return "article/articleListByTag";
|
||||
}
|
||||
@@ -180,25 +164,22 @@ public class ArticleController {
|
||||
*/
|
||||
@Transactional
|
||||
@GetMapping("article/list/search/")
|
||||
public String getArticlesListByKeyword(@RequestParam Integer page,
|
||||
@RequestParam String keyword,
|
||||
Model model) {
|
||||
// DTO 매핑 전처리
|
||||
Page<ArticleDtoForCardBox> articleList =
|
||||
articleService.getArticlesByKeyword(keyword, page)
|
||||
.map(article ->
|
||||
modelMapper.map(article, ArticleDtoForCardBox.class));
|
||||
String getArticlesListByKeyword(@RequestParam Integer page,
|
||||
@RequestParam String keyword,
|
||||
Model model) {
|
||||
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());
|
||||
}
|
||||
|
||||
PagingBoxDto pagingBoxDto =
|
||||
PagingBoxDto.createOf(page, (int)articleList.getTotalElements());
|
||||
PagingBoxHandler pagingBoxHandler =
|
||||
PagingBoxHandler.createOf(page, (int)articleList.getTotalElements());
|
||||
|
||||
layoutRenderingQueries.AddLayoutTo(model);
|
||||
model.addAttribute("articleList", articleList);
|
||||
model.addAttribute("pagingBox", pagingBoxDto);
|
||||
model.addAttribute("pagingBox", pagingBoxHandler);
|
||||
|
||||
return "article/articleListByKeyword";
|
||||
|
||||
@@ -214,81 +195,65 @@ public class ArticleController {
|
||||
*/
|
||||
@Transactional
|
||||
@GetMapping("/article/view")
|
||||
public String readArticle(@RequestParam Long articleId,
|
||||
@AuthenticationPrincipal PrincipalDetails principal,
|
||||
@CookieValue(required = false, name = "view") String cookie,
|
||||
HttpServletResponse response,
|
||||
Model model) {
|
||||
String readArticle(@RequestParam Long articleId,
|
||||
@AuthenticationPrincipal PrincipalDetails principal,
|
||||
@CookieValue(required = false, name = "view") String cookie,
|
||||
HttpServletResponse response,
|
||||
Model model) {
|
||||
// 1. 로그인 여부에 따라 뷰단에 회원정보 출력 여부 결정
|
||||
if (principal != null) {
|
||||
model.addAttribute("member", modelMapper.map(principal.getMember(), MemberDto.class));
|
||||
model.addAttribute("member", MemberVo.from(principal.getMember()));
|
||||
} else {
|
||||
model.addAttribute("member", null);
|
||||
}
|
||||
|
||||
/*
|
||||
DTO 매핑 전처리
|
||||
2. 게시물 상세조회용
|
||||
2.화면단을 위한 처리
|
||||
*/
|
||||
Article article = articleService.readArticle(articleId);
|
||||
ArticleResponseForDetail articleResponseForDetail = articleQueriesUseCase.getArticleForDetail(articleId);
|
||||
articleResponseForDetail.setContent(getHtmlRenderer().render(getParser().parse(articleResponseForDetail.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 =
|
||||
articleService
|
||||
.getArticlesByCategoryForDetailView(article.getCategory())
|
||||
.stream()
|
||||
.map(article1 -> modelMapper.map(article1, ArticleDtoByCategory.class))
|
||||
.collect(Collectors.toList());
|
||||
List<ArticleResponseByCategory> articleTitlesSortByCategory =
|
||||
articleQueriesUseCase
|
||||
.getArticlesByCategoryForDetailView(articleResponseForDetail.getCategory());
|
||||
|
||||
// 3. 메타 태그용 Dto 전처리
|
||||
StringBuilder metaTags = new StringBuilder();
|
||||
for (String tag : tags) {
|
||||
for (String tag : articleResponseForDetail.getTags()) {
|
||||
metaTags.append(tag).append(", ");
|
||||
}
|
||||
|
||||
String substringContents = null;
|
||||
if(articleDtoForDetail.getContent().length()>200) {
|
||||
substringContents = articleDtoForDetail.getContent().substring(0, 200);
|
||||
if(articleResponseForDetail.getContent().length()>200) {
|
||||
substringContents = articleResponseForDetail.getContent().substring(0, 200);
|
||||
}
|
||||
else substringContents = articleDtoForDetail.getContent();
|
||||
else substringContents = articleResponseForDetail.getContent();
|
||||
|
||||
// 4. 모델 담기
|
||||
layoutRenderingQueries.AddLayoutTo(model);
|
||||
model.addAttribute("article", articleDtoForDetail);
|
||||
model.addAttribute("article", articleResponseForDetail);
|
||||
model.addAttribute("metaTags",metaTags);
|
||||
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);
|
||||
articleService.addHit(article);
|
||||
response.addCookie(viewCookie);
|
||||
return true;
|
||||
} else {
|
||||
boolean addHitAvailable = false;
|
||||
boolean isRead = false;
|
||||
String[] viewCookieList = cookie.split("/");
|
||||
for (String alreadyRead : viewCookieList) {
|
||||
@@ -299,9 +264,10 @@ public class ArticleController {
|
||||
}
|
||||
if (!isRead) {
|
||||
cookie += articleId + "/";
|
||||
article.addHit();
|
||||
addHitAvailable = true;
|
||||
}
|
||||
response.addCookie(new Cookie("view", cookie));
|
||||
return addHitAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,11 +295,11 @@ public class ArticleController {
|
||||
/*
|
||||
- 아티클 폼에 필요한 카테고리 dtos
|
||||
*/
|
||||
private List<CategoryNormalDto> getCategoryDtosForForm() {
|
||||
private List<CategorySimpleView> getCategoryDtosForForm() {
|
||||
return categoryService
|
||||
.findCategoryByTier(2)
|
||||
.stream()
|
||||
.map(category -> modelMapper.map(category, CategoryNormalDto.class))
|
||||
.map(category -> modelMapper.map(category, CategorySimpleView.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package myblog.blog.article.dto;
|
||||
package myblog.blog.article.adapter.incomming.web;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import myblog.blog.article.domain.Article;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.Objects;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@@ -1,28 +1,24 @@
|
||||
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.service.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 +26,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 +38,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);
|
||||
@@ -1,14 +1,13 @@
|
||||
package myblog.blog.article.dto;
|
||||
package myblog.blog.article.adapter.incomming.web;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/*
|
||||
- 뷰단 페이징 박스 처리를 위한 핸들러
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class PagingBoxDto {
|
||||
public class PagingBoxHandler {
|
||||
|
||||
private int curPageNum;
|
||||
private int lastPageNum;
|
||||
@@ -20,9 +19,9 @@ public class PagingBoxDto {
|
||||
private final int displayArticlePerPage = 5;
|
||||
|
||||
// 스태틱 생성 메소드
|
||||
public static PagingBoxDto createOf(int page, int totalArticles) {
|
||||
public static PagingBoxHandler createOf(int page, int totalArticles) {
|
||||
|
||||
PagingBoxDto box = new PagingBoxDto();
|
||||
PagingBoxHandler box = new PagingBoxHandler();
|
||||
box.curPageNum = page;
|
||||
box.lastPageNum = (int) (Math.ceil(totalArticles / (double) box.displayArticlePerPage));
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package myblog.blog.article.controller;
|
||||
package myblog.blog.article.adapter.incomming.web;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.domain.TempArticle;
|
||||
import myblog.blog.article.service.TempArticleService;
|
||||
import myblog.blog.article.dto.TempArticleDto;
|
||||
import myblog.blog.article.application.TempArticleService;
|
||||
import myblog.blog.article.application.port.response.TempArticleResponse;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -21,9 +21,9 @@ public class TempArticleController {
|
||||
- 임시 아티클 저장 요청
|
||||
*/
|
||||
@PostMapping("/article/temp/autoSave")
|
||||
public String autoSaveTemp(@RequestBody TempArticleDto tempArticleDto){
|
||||
public String autoSaveTemp(@RequestBody TempArticleResponse tempArticleResponse){
|
||||
|
||||
tempArticleService.saveTemp(new TempArticle(tempArticleDto.getContent()));
|
||||
tempArticleService.saveTemp(new TempArticle(tempArticleResponse.getContent()));
|
||||
|
||||
return "저장성공";
|
||||
}
|
||||
@@ -32,13 +32,14 @@ public class TempArticleController {
|
||||
- 임시 아티클 조회
|
||||
*/
|
||||
@GetMapping("/article/temp/getTemp")
|
||||
public @ResponseBody TempArticleDto getTempArticle(){
|
||||
public @ResponseBody
|
||||
TempArticleResponse getTempArticle(){
|
||||
|
||||
Optional<TempArticle> tempArticle = tempArticleService.getTempArticle();
|
||||
|
||||
TempArticleDto tempArticleDto = new TempArticleDto();
|
||||
tempArticleDto.setContent(tempArticle.orElse(new TempArticle()).getContent());
|
||||
TempArticleResponse tempArticleResponse = new TempArticleResponse();
|
||||
tempArticleResponse.setContent(tempArticle.orElse(new TempArticle()).getContent());
|
||||
|
||||
return tempArticleDto;
|
||||
return tempArticleResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.application.port.outgoing.ArticleBackupRepositoryPort;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ArticleBackupRepositoryAdapter implements ArticleBackupRepositoryPort {
|
||||
|
||||
private final GithubRepoArticleRepository githubRepoArticleRepository;
|
||||
|
||||
@Override
|
||||
public void backup(Article article) {githubRepoArticleRepository.pushArticleToGithub(article);}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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 org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class ArticleRepositoryAdapter implements ArticleRepositoryPort {
|
||||
|
||||
private final JpaArticleRepository jpaArticleRepository;
|
||||
private final MybatisArticleRepository mybatisArticleRepository;
|
||||
|
||||
@Override
|
||||
public List<Article> findTop6ByOrderByHitDesc() {
|
||||
return jpaArticleRepository.findTop6ByOrderByHitDesc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Article> findTop6ByCategoryOrderByIdDesc(Category category) {
|
||||
return jpaArticleRepository.findTop6ByCategoryOrderByIdDesc(category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Article> findByOrderByIdDesc(Pageable pageable) {
|
||||
return jpaArticleRepository.findByOrderByIdDesc(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Article> findByOrderByIdDescWithList(Pageable pageable) {
|
||||
return jpaArticleRepository.findByOrderByIdDescWithList(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Article> findByOrderByIdDesc(Long articleId, Pageable pageable) {
|
||||
return jpaArticleRepository.findByOrderByIdDesc(articleId, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Article> findBySubCategoryOrderByIdDesc(Pageable pageable, String category) {
|
||||
return jpaArticleRepository.findBySubCategoryOrderByIdDesc(pageable,category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Article> findBySupCategoryOrderByIdDesc(Pageable pageable, String category) {
|
||||
return jpaArticleRepository.findBySupCategoryOrderByIdDesc(pageable,category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Article findArticleByIdFetchCategoryAndTags(Long articleId) {
|
||||
return jpaArticleRepository.findArticleByIdFetchCategoryAndTags(articleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Article> findAllByArticleTagsOrderById(Pageable pageable, String tag) {
|
||||
return jpaArticleRepository.findAllByArticleTagsOrderById(pageable,tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Article> findAllByKeywordOrderById(Pageable pageable, String keyword) {
|
||||
return jpaArticleRepository.findAllByArticleTagsOrderById(pageable, keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Article> findAllByOrderByIdDesc() {
|
||||
return jpaArticleRepository.findAllByOrderByIdDesc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Article> findById(Long articleId) {
|
||||
return jpaArticleRepository.findById(articleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Article newArticle) {
|
||||
jpaArticleRepository.save(newArticle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteArticle(Long articleId) {
|
||||
mybatisArticleRepository.deleteArticle(articleId);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package myblog.blog.tags.repository;
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.tags.domain.ArticleTagList;
|
||||
import myblog.blog.article.domain.ArticleTagList;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
@@ -0,0 +1,35 @@
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
import org.kohsuke.github.GHRepository;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import org.kohsuke.github.GitHubBuilder;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class GithubRepoArticleRepository {
|
||||
|
||||
@Value("${git.gitToken}")
|
||||
private String gitToken;
|
||||
@Value("${git.repo}")
|
||||
private String gitRepo;
|
||||
|
||||
void pushArticleToGithub(Article article) {
|
||||
try {
|
||||
GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build();
|
||||
GHRepository repository = gitHub.getRepository(gitRepo);
|
||||
repository.createContent()
|
||||
.path(article.getCategory().getParents().getTitle()+"/"+ article.getCategory().getTitle()+"/"+ article.getTitle()+".md")
|
||||
.content(article.getContent())
|
||||
.message("test")
|
||||
.branch("main")
|
||||
.commit();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package myblog.blog.article.repository;
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.category.domain.Category;
|
||||
@@ -11,7 +11,7 @@ import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import java.util.List;
|
||||
|
||||
public interface ArticleRepository extends JpaRepository<Article, Long> {
|
||||
public interface JpaArticleRepository extends JpaRepository<Article, Long> {
|
||||
|
||||
/*
|
||||
- 최대 6개까지 조회수가 높은 게시물 가져오기
|
||||
@@ -0,0 +1,10 @@
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import myblog.blog.article.domain.Tags;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface JpaTagsRepository extends JpaRepository<Tags, Long> {
|
||||
Optional<Tags> findByName(String name);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package myblog.blog.article.repository;
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import myblog.blog.article.domain.TempArticle;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
// 기본 JPA 메소드 사용
|
||||
public interface TempArticleRepository extends JpaRepository<TempArticle, Long> {
|
||||
public interface JpaTempArticleRepository extends JpaRepository<TempArticle, Long> {
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package myblog.blog.article.repository;
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface NaArticleRepository {
|
||||
public interface MybatisArticleRepository {
|
||||
|
||||
/*
|
||||
- 삭제처리시 불필요한 조회방지 위해 네이티브 쿼리 사용 cascade delete 처리
|
||||
@@ -0,0 +1,20 @@
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.application.port.outgoing.TagRepositoryPort;
|
||||
import myblog.blog.article.domain.Tags;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TagRepositoryAdapter implements TagRepositoryPort {
|
||||
|
||||
private final JpaTagsRepository jpaTagsRepository;
|
||||
|
||||
@Override
|
||||
public List<Tags> findAll() {
|
||||
return jpaTagsRepository.findAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package myblog.blog.article.adapter.outgoing.persistence;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import myblog.blog.article.application.port.outgoing.TempArticleRepositoryPort;
|
||||
import myblog.blog.article.domain.TempArticle;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TempArticleRepositoryAdapter implements TempArticleRepositoryPort {
|
||||
|
||||
private final JpaTempArticleRepository jpaTempArticleRepository;
|
||||
|
||||
@Override
|
||||
public void save(TempArticle tempArticle) {
|
||||
jpaTempArticleRepository.save(tempArticle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TempArticle> findById(long id) {
|
||||
return jpaTempArticleRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(TempArticle tempArticle) {
|
||||
jpaTempArticleRepository.delete(tempArticle);
|
||||
}
|
||||
}
|
||||
@@ -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.category.domain.Category;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseByCategory;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseForDetail;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseForEdit;
|
||||
|
||||
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 ArticleResponseForDetail getArticleForDetail(Long id){
|
||||
Article article = articleRepositoryPort.findArticleByIdFetchCategoryAndTags(id);
|
||||
ArticleResponseForDetail articleResponseForDetail =
|
||||
modelMapper.map(article, ArticleResponseForDetail.class);
|
||||
|
||||
List<String> tags =
|
||||
article.getArticleTagLists()
|
||||
.stream()
|
||||
.map(tag -> tag.getTags().getName())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
articleResponseForDetail.setTags(tags);
|
||||
return articleResponseForDetail;
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리별 최신게시물 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
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.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 myblog.blog.member.service.Oauth2MemberService;
|
||||
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class ArticleService implements ArticleUseCase {
|
||||
|
||||
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(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);
|
||||
tagUseCase.createNewTagsAndArticleTagList(articleCreateRequest.getTags(), newArticle);
|
||||
return newArticle.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = {"layoutCaching", "layoutRecentArticleCaching","seoCaching"}, allEntries = true)
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backupArticle(Long articleId) {
|
||||
Article article = articleRepositoryPort.findById(articleId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("NotFoundArticle"));
|
||||
articleBackupRepositoryPort.backup(article);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHit(Long articleId) {
|
||||
Article article = articleRepositoryPort.findById(articleId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("NotFoundArticleException"));
|
||||
article.addHit();
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 도메인으로 반환
|
||||
*/
|
||||
@Override
|
||||
public Article getArticle(Long id){
|
||||
return articleRepositoryPort.findArticleByIdFetchCategoryAndTags(id);
|
||||
}
|
||||
/*
|
||||
- 모든 아티클 도메인으로 반환
|
||||
*/
|
||||
@Override
|
||||
public List<Article> getTotalArticle(){
|
||||
return articleRepositoryPort.findAllByOrderByIdDesc();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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.application.port.response.TagsResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class TagsQueries implements TagsQueriesUseCase {
|
||||
private final TagRepositoryPort tagRepositoryPort;
|
||||
|
||||
public List<TagsResponse> findAllTagDtos(){
|
||||
List<Tags> tags = tagRepositoryPort.findAll();
|
||||
return tags.stream()
|
||||
.map(tag -> MapperUtils.getModelMapper().map(tag, TagsResponse.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,27 @@
|
||||
package myblog.blog.tags.service;
|
||||
package myblog.blog.article.application;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.adapter.outgoing.persistence.ArticleTagListsRepository;
|
||||
import myblog.blog.article.adapter.outgoing.persistence.JpaTagsRepository;
|
||||
import myblog.blog.article.application.port.incomming.TagUseCase;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import myblog.blog.article.domain.*;
|
||||
import myblog.blog.tags.domain.*;
|
||||
import myblog.blog.shared.utils.MapperUtils;
|
||||
import myblog.blog.tags.repository.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class TagsService {
|
||||
private final TagsRepository tagsRepository;
|
||||
public class TagsService implements TagUseCase {
|
||||
private final JpaTagsRepository jpaTagsRepository;
|
||||
private final ArticleTagListsRepository articleTagListsRepository;
|
||||
/*
|
||||
- Json 객체로 넘어온 태그들을 파싱해서 신규 태그인경우 저장
|
||||
*/
|
||||
@Override
|
||||
public void createNewTagsAndArticleTagList(String names, Article article) {
|
||||
List<Map<String,String>> tagsDtoArrayList = MapperUtils.getGson().fromJson(names, ArrayList.class);
|
||||
for (var tagDto : tagsDtoArrayList) {
|
||||
@@ -29,10 +31,11 @@ public class TagsService {
|
||||
}
|
||||
|
||||
private Tags findOrCreateTagFrom(Map<String, String> tags) {
|
||||
return tagsRepository.findByName(tags.get("value"))
|
||||
.orElseGet(() -> tagsRepository.save(new Tags(tags.get("value"))));
|
||||
return jpaTagsRepository.findByName(tags.get("value"))
|
||||
.orElseGet(() -> jpaTagsRepository.save(new Tags(tags.get("value"))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAllTagsWith(Article article){
|
||||
articleTagListsRepository.deleteByArticle(article);
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package myblog.blog.article.service;
|
||||
package myblog.blog.article.application;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.application.port.incomming.TempArticleUseCase;
|
||||
import myblog.blog.article.application.port.outgoing.TempArticleRepositoryPort;
|
||||
import myblog.blog.article.domain.TempArticle;
|
||||
import myblog.blog.article.repository.TempArticleRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -11,31 +12,31 @@ import java.util.Optional;
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class TempArticleService {
|
||||
public class TempArticleService implements TempArticleUseCase {
|
||||
|
||||
private final TempArticleRepository tempArticleRepository;
|
||||
private final TempArticleRepositoryPort tempArticleRepositoryPort;
|
||||
|
||||
/*
|
||||
- 자동 저장 로직
|
||||
- ID값 고정으로 머지를 작동시켜 임시글 DB에 1개 유지
|
||||
*/
|
||||
public void saveTemp(TempArticle tempArticle){
|
||||
tempArticleRepository.save(tempArticle);
|
||||
tempArticleRepositoryPort.save(tempArticle);
|
||||
}
|
||||
|
||||
/*
|
||||
- 임시글 가져오기
|
||||
*/
|
||||
public Optional<TempArticle> getTempArticle(){
|
||||
return tempArticleRepository.findById(1L);
|
||||
return tempArticleRepositoryPort.findById(1L);
|
||||
}
|
||||
|
||||
/*
|
||||
- 임시글 삭제
|
||||
*/
|
||||
public void deleteTemp(){
|
||||
Optional<TempArticle> deleteArticle = tempArticleRepository.findById(1L);
|
||||
deleteArticle.ifPresent(tempArticleRepository::delete);
|
||||
Optional<TempArticle> deleteArticle = tempArticleRepositoryPort.findById(1L);
|
||||
deleteArticle.ifPresent(tempArticleRepositoryPort::delete);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package myblog.blog.article.application.port.incomming;
|
||||
|
||||
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseByCategory;
|
||||
import myblog.blog.article.application.port.response.ArticleResponseForDetail;
|
||||
import myblog.blog.article.application.port.response.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);
|
||||
ArticleResponseForDetail getArticleForDetail(Long id);
|
||||
List<ArticleResponseByCategory> getArticlesByCategoryForDetailView(String category);
|
||||
Page<ArticleResponseForCardBox> getArticlesByTag(String tag, Integer page);
|
||||
Page<ArticleResponseForCardBox> getArticlesByKeyword(String keyword, Integer page);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package myblog.blog.article.application.port.incomming;
|
||||
|
||||
import myblog.blog.article.application.port.request.ArticleCreateRequest;
|
||||
import myblog.blog.article.application.port.request.ArticleEditRequest;
|
||||
import myblog.blog.article.domain.Article;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ArticleUseCase {
|
||||
Long writeArticle(ArticleCreateRequest articleCreateRequest);
|
||||
void editArticle(ArticleEditRequest articleEditRequest);
|
||||
void deleteArticle(Long articleId);
|
||||
void addHit(Long articleId);
|
||||
void backupArticle(Long articleId);
|
||||
Article getArticle(Long id);
|
||||
List<Article> getTotalArticle();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package myblog.blog.article.application.port.incomming;
|
||||
|
||||
public interface RssUseCase {
|
||||
String getRssFeed();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package myblog.blog.article.application.port.incomming;
|
||||
|
||||
public interface SiteMapUseCase {
|
||||
String getSiteMap();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package myblog.blog.article.application.port.incomming;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
|
||||
public interface TagUseCase {
|
||||
void createNewTagsAndArticleTagList(String names, Article article);
|
||||
void deleteAllTagsWith(Article article);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package myblog.blog.article.application.port.incomming;
|
||||
|
||||
import myblog.blog.article.application.port.response.TagsResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TagsQueriesUseCase {
|
||||
List<TagsResponse> findAllTagDtos();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package myblog.blog.article.application.port.incomming;
|
||||
|
||||
import myblog.blog.article.domain.TempArticle;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TempArticleUseCase {
|
||||
void saveTemp(TempArticle tempArticle);
|
||||
Optional<TempArticle> getTempArticle();
|
||||
void deleteTemp();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package myblog.blog.article.application.port.outgoing;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
|
||||
public interface ArticleBackupRepositoryPort {
|
||||
public void backup(Article article);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package myblog.blog.article.application.port.outgoing;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ArticleRepositoryPort {
|
||||
List<Article> findTop6ByOrderByHitDesc();
|
||||
List<Article> findTop6ByCategoryOrderByIdDesc(Category category);
|
||||
Slice<Article> findByOrderByIdDesc(Pageable pageable);
|
||||
List<Article> findByOrderByIdDescWithList(Pageable pageable);
|
||||
List<Article> findByOrderByIdDesc(Long articleId, Pageable pageable);
|
||||
Slice<Article> findBySubCategoryOrderByIdDesc(Pageable pageable, String category);
|
||||
Slice<Article> findBySupCategoryOrderByIdDesc(Pageable pageable, String category);
|
||||
Article findArticleByIdFetchCategoryAndTags(Long articleId);
|
||||
Page<Article> findAllByArticleTagsOrderById(Pageable pageable, String tag);
|
||||
Page<Article> findAllByKeywordOrderById(Pageable pageable, String keyword);
|
||||
List<Article> findAllByOrderByIdDesc();
|
||||
Optional<Article> findById(Long articleId);
|
||||
void save(Article newArticle);
|
||||
void deleteArticle(Long articleId);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package myblog.blog.article.application.port.outgoing;
|
||||
|
||||
import myblog.blog.article.domain.Tags;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TagRepositoryPort {
|
||||
List<Tags> findAll();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package myblog.blog.article.application.port.outgoing;
|
||||
|
||||
import myblog.blog.article.domain.TempArticle;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TempArticleRepositoryPort {
|
||||
void save(TempArticle tempArticle);
|
||||
Optional<TempArticle> findById(long l);
|
||||
void delete(TempArticle tempArticle);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package myblog.blog.article.dto;
|
||||
package myblog.blog.article.application.port.response;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1,9 +1,8 @@
|
||||
package myblog.blog.article.dto;
|
||||
package myblog.blog.article.application.port.response;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -11,7 +10,7 @@ import java.util.List;
|
||||
- 아티클 상세조회용 DTO
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class ArticleDtoForDetail {
|
||||
public class ArticleResponseForDetail {
|
||||
|
||||
private Long id;
|
||||
private String title;
|
||||
@@ -1,10 +1,8 @@
|
||||
package myblog.blog.article.dto;
|
||||
package myblog.blog.article.application.port.response;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import myblog.blog.tags.domain.ArticleTagList;
|
||||
import myblog.blog.tags.dto.TagsDto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -13,7 +11,7 @@ import java.util.List;
|
||||
- 아티클 수정 폼을 위한 DTO
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class ArticleDtoForEdit {
|
||||
public class ArticleResponseForEdit {
|
||||
|
||||
private Long id;
|
||||
private String title;
|
||||
@@ -0,0 +1,15 @@
|
||||
package myblog.blog.article.application.port.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/*
|
||||
- 뷰단 사용을 위한 DTO
|
||||
*/
|
||||
@Data
|
||||
public class TagsResponse {
|
||||
private String name;
|
||||
public TagsResponse(){}
|
||||
public TagsResponse(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package myblog.blog.article.dto;
|
||||
package myblog.blog.article.application.port.response;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -7,6 +7,6 @@ import lombok.Setter;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TempArticleDto {
|
||||
public class TempArticleResponse {
|
||||
private String content;
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
package myblog.blog.article.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import myblog.blog.article.dto.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 myblog.blog.tags.domain.ArticleTagList;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/*
|
||||
- 아티클 Entity
|
||||
- toc 추후 개발 예정
|
||||
@@ -89,14 +86,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();
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package myblog.blog.tags.domain;
|
||||
package myblog.blog.article.domain;
|
||||
|
||||
import lombok.*;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import lombok.Getter;
|
||||
import myblog.blog.shared.BasicEntity;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/*
|
||||
- 다 대 다 연관관계 해소 엔티티
|
||||
*/
|
||||
- 다 대 다 연관관계 해소 엔티티
|
||||
*/
|
||||
@Entity
|
||||
@Getter
|
||||
@SequenceGenerator(
|
||||
@@ -1,10 +1,11 @@
|
||||
package myblog.blog.tags.domain;
|
||||
package myblog.blog.article.domain;
|
||||
|
||||
import lombok.*;
|
||||
import lombok.Getter;
|
||||
import myblog.blog.shared.BasicEntity;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@SequenceGenerator(
|
||||
@@ -30,4 +31,4 @@ public class Tags extends BasicEntity {
|
||||
public Tags(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import lombok.Getter;
|
||||
import myblog.blog.shared.BasicEntity;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
- 임시 아티클 저장 Entity
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
package myblog.blog.article.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import myblog.blog.member.doamin.Member;
|
||||
import myblog.blog.article.dto.ArticleForm;
|
||||
import myblog.blog.article.repository.ArticleRepository;
|
||||
import myblog.blog.article.repository.NaArticleRepository;
|
||||
import myblog.blog.category.service.CategoryService;
|
||||
import myblog.blog.tags.service.TagsService;
|
||||
import org.kohsuke.github.GHRepository;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import org.kohsuke.github.GitHubBuilder;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
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.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class ArticleService {
|
||||
|
||||
@Value("${git.gitToken}")
|
||||
private String gitToken;
|
||||
@Value("${git.repo}")
|
||||
private String gitRepo;
|
||||
|
||||
private final TagsService tagsService;
|
||||
private final CategoryService categoryService;
|
||||
private final ArticleRepository articleRepository;
|
||||
private final NaArticleRepository naArticleRepository;
|
||||
|
||||
/*
|
||||
- 아티클 작성 로직
|
||||
- 글작성시 아티클 캐싱 초기화
|
||||
*/
|
||||
@CacheEvict(value = {"layoutCaching", "layoutRecentArticleCaching","seoCaching"}, allEntries = true)
|
||||
public Long writeArticle(ArticleForm articleDto, Member writer) {
|
||||
Article newArticle = articleFrom(articleDto, writer);
|
||||
articleRepository.save(newArticle);
|
||||
tagsService.createNewTagsAndArticleTagList(articleDto.getTags(), newArticle);
|
||||
return newArticle.getId();
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 수정 로직
|
||||
- 글 수정시 아티클 캐싱 초기화
|
||||
*/
|
||||
@CacheEvict(value = {"layoutCaching", "layoutRecentArticleCaching","seoCaching"}, allEntries = true)
|
||||
public void editArticle(Long articleId, ArticleForm articleForm) {
|
||||
Article article = articleRepository.findById(articleId).get();
|
||||
Category category = categoryService.findCategory(articleForm.getCategory());
|
||||
tagsService.deleteAllTagsWith(article);
|
||||
tagsService.createNewTagsAndArticleTagList(articleForm.getTags(), article);
|
||||
article.isEditedFrom(articleForm,category);
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 삭제 로직
|
||||
- 글 삭제시 아티클 캐싱 초기화
|
||||
*/
|
||||
@CacheEvict(value = {"layoutCaching", "layoutRecentArticleCaching","seoCaching"}, allEntries = true)
|
||||
public void deleteArticle(Long articleId) {
|
||||
naArticleRepository.deleteArticle(articleId);
|
||||
}
|
||||
|
||||
/*
|
||||
- 메인화면 위한 인기 아티클 6개 목록 가져오기
|
||||
- 레이아웃 렌더링 성능 향상을 위해 캐싱작업
|
||||
카테고리 변경 / 아티클 변경이 존재할경우 레이아웃 캐시 초기화
|
||||
DTO 매핑 로직 서비스단에서 처리
|
||||
*/
|
||||
@Cacheable(value = "layoutCaching", key = "1")
|
||||
public List<Article> getPopularArticles() {
|
||||
return articleRepository.findTop6ByOrderByHitDesc();
|
||||
}
|
||||
|
||||
/*
|
||||
- 메인화면 위한 최신 아티클 커서 페이징해서 가져오기
|
||||
- 레이아웃 렌더링 성능 향상을 위해 캐싱작업
|
||||
카테고리 변경 / 아티클 변경이 존재할경우 레이아웃 캐시 초기화
|
||||
*/
|
||||
@Cacheable(value = "layoutRecentArticleCaching", key = "#lastArticleId")
|
||||
public List<Article> getRecentArticles(Long lastArticleId) {
|
||||
return lastArticleId.equals(0L)?
|
||||
articleRepository
|
||||
.findByOrderByIdDescWithList(PageRequest.of(0, 5))
|
||||
:
|
||||
articleRepository
|
||||
.findByOrderByIdDesc(lastArticleId, PageRequest.of(0, 5));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리별 게시물 페이징 처리해서 가져오기
|
||||
*/
|
||||
public Slice<Article> getArticlesByCategory(String category, Integer tier, Integer page) {
|
||||
Slice<Article> articles = null;
|
||||
|
||||
if (tier.equals(0)) {
|
||||
articles = articleRepository
|
||||
.findByOrderByIdDesc(
|
||||
PageRequest.of(pageResolve(page), 5));
|
||||
}
|
||||
|
||||
if (tier.equals(1)) {
|
||||
articles = articleRepository
|
||||
.findBySupCategoryOrderByIdDesc(
|
||||
PageRequest.of(pageResolve(page), 5), category);
|
||||
|
||||
}
|
||||
|
||||
if (tier.equals(2)) {
|
||||
articles = articleRepository
|
||||
.findBySubCategoryOrderByIdDesc(
|
||||
PageRequest.of(pageResolve(page), 5), category);
|
||||
}
|
||||
|
||||
return articles;
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 읽기 위한 페치로 전체 가져오기
|
||||
*/
|
||||
public Article readArticle(Long id){
|
||||
return articleRepository.findArticleByIdFetchCategoryAndTags(id);
|
||||
}
|
||||
|
||||
/*
|
||||
- 모든 게시물 조회
|
||||
*/
|
||||
public List<Article> getTotalArticle(){
|
||||
return articleRepository.findAllByOrderByIdDesc();
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리별 최신게시물 6개만 아티클 상세뷰 위해 가져오는로직
|
||||
*/
|
||||
public List<Article> getArticlesByCategoryForDetailView(Category category){
|
||||
return articleRepository.findTop6ByCategoryOrderByIdDesc(category);
|
||||
}
|
||||
|
||||
/*
|
||||
- 태그별 게시물 페이징 처리해서 가져오기
|
||||
*/
|
||||
public Page<Article> getArticlesByTag(String tag, Integer page) {
|
||||
return articleRepository
|
||||
.findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag);
|
||||
}
|
||||
|
||||
/*
|
||||
- 검색어별 게시물 페이징 처리해서 가져오기
|
||||
*/
|
||||
public Page<Article> getArticlesByKeyword(String keyword, Integer page) {
|
||||
return articleRepository
|
||||
.findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword);
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 조회수 추가
|
||||
*/
|
||||
public void addHit(Article article) {
|
||||
article.addHit();
|
||||
}
|
||||
|
||||
/*
|
||||
- 깃헙에 아티클 푸시하기
|
||||
*/
|
||||
public void pushArticleToGithub(Long articleId) {
|
||||
articleRepository.findById(articleId).ifPresent(this::pushArticleToGithub);
|
||||
}
|
||||
|
||||
private void pushArticleToGithub(Article article) {
|
||||
try {
|
||||
GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build();
|
||||
GHRepository repository = gitHub.getRepository(gitRepo);
|
||||
repository.createContent()
|
||||
.path(article.getCategory().getParents().getTitle()+"/"+ article.getCategory().getTitle()+"/"+ article.getTitle()+".md")
|
||||
.content(article.getContent())
|
||||
.message("test")
|
||||
.branch("main")
|
||||
.commit();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
- 페이지 시작점 0~1변경 메서드
|
||||
*/
|
||||
private int pageResolve(Integer rawPage) {
|
||||
if (rawPage == null || rawPage == 1) {
|
||||
return 0;
|
||||
} else return rawPage - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
- 새로운 아티클 도메인 생성 메서드
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.shared.exception.CustomFormException;
|
||||
import myblog.blog.shared.exception.ListValidator;
|
||||
import myblog.blog.category.dto.CategoryForView;
|
||||
import myblog.blog.category.dto.CategoryNormalDto;
|
||||
import myblog.blog.category.dto.CategorySimpleView;
|
||||
import myblog.blog.category.service.CategoryService;
|
||||
import myblog.blog.comment.dto.CommentDtoForLayout;
|
||||
import myblog.blog.comment.service.CommentService;
|
||||
@@ -37,8 +37,8 @@ public class CategoryController {
|
||||
public String editCategoryForm(Model model) {
|
||||
|
||||
// DTO 매핑 전처리
|
||||
List<CategoryNormalDto> categoryList = categoryService.getCategorytCountList();
|
||||
List<CategoryNormalDto> copyList = cloneList(categoryList);
|
||||
List<CategorySimpleView> categoryList = categoryService.getCategorytCountList();
|
||||
List<CategorySimpleView> copyList = cloneList(categoryList);
|
||||
copyList.remove(0);
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryList);
|
||||
List<CommentDtoForLayout> comments = commentService.recentCommentList();
|
||||
@@ -56,7 +56,7 @@ public class CategoryController {
|
||||
*/
|
||||
@PostMapping("/category/edit")
|
||||
public @ResponseBody
|
||||
String editCategory(@RequestBody List<CategoryNormalDto> categoryList, Errors errors) {
|
||||
String editCategory(@RequestBody List<CategorySimpleView> categoryList, Errors errors) {
|
||||
// List DTO 검증을 위한 커스텀 validator
|
||||
listValidator.validate(categoryList, errors);
|
||||
// 유효성 검사
|
||||
@@ -67,11 +67,11 @@ public class CategoryController {
|
||||
categoryService.changeCategory(categoryList);
|
||||
return "변경 성공";
|
||||
}
|
||||
private List<CategoryNormalDto> cloneList(List<CategoryNormalDto> categoryList) {
|
||||
private List<CategorySimpleView> cloneList(List<CategorySimpleView> categoryList) {
|
||||
return categoryList
|
||||
.stream()
|
||||
.map(categoryNormalDto ->
|
||||
modelMapper.map(categoryNormalDto, CategoryNormalDto.class))
|
||||
modelMapper.map(categoryNormalDto, CategorySimpleView.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
@@ -25,7 +24,7 @@ public class CategoryForView {
|
||||
/*
|
||||
- 스태틱 생성 메서드
|
||||
*/
|
||||
public static CategoryForView createCategory(List<CategoryNormalDto> crList) {
|
||||
public static CategoryForView createCategory(List<CategorySimpleView> crList) {
|
||||
return recursiveBuildFromCategoryDto(0, crList);
|
||||
}
|
||||
|
||||
@@ -35,12 +34,12 @@ public class CategoryForView {
|
||||
2. Depth 변화시 재귀 호출 / 재귀 탈출
|
||||
3. 탈출시 상위 카테고리 list로 삽입하여 트리구조 작성
|
||||
*/
|
||||
private static CategoryForView recursiveBuildFromCategoryDto(int tier, List<CategoryNormalDto> source) {
|
||||
private static CategoryForView recursiveBuildFromCategoryDto(int tier, List<CategorySimpleView> source) {
|
||||
|
||||
CategoryForView categoryForView = new CategoryForView();
|
||||
|
||||
while (!source.isEmpty()) {
|
||||
CategoryNormalDto cSource = source.get(0);
|
||||
CategorySimpleView cSource = source.get(0);
|
||||
|
||||
if (cSource.getTier() == tier) {
|
||||
if(categoryForView.getTitle() != null
|
||||
|
||||
@@ -12,7 +12,7 @@ import javax.validation.constraints.NotBlank;
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class CategoryNormalDto {
|
||||
public class CategorySimpleView {
|
||||
|
||||
private Long id;
|
||||
@NotBlank(message = "카테고리명은 공백일 수 없습니다.")
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
- 티어별 카테고리들 가져오기
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package myblog.blog.category.repository;
|
||||
|
||||
import myblog.blog.category.dto.CategoryNormalDto;
|
||||
import myblog.blog.category.dto.CategorySimpleView;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.springframework.stereotype.Repository;
|
||||
@@ -23,6 +23,6 @@ public interface NaCategoryRepository {
|
||||
" group by c.title, b.title with rollup) e\n" +
|
||||
" right join category f on (e.title = f.title)\n" +
|
||||
" order by pOrder, cOrder ")
|
||||
List<CategoryNormalDto> getCategoryCount();
|
||||
List<CategorySimpleView> getCategoryCount();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,15 @@ package myblog.blog.category.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import myblog.blog.category.dto.CategoryNormalDto;
|
||||
import myblog.blog.category.dto.CategorySimpleView;
|
||||
import myblog.blog.category.dto.CategoryForView;
|
||||
import myblog.blog.category.repository.CategoryRepository;
|
||||
import myblog.blog.category.repository.NaCategoryRepository;
|
||||
import myblog.blog.member.doamin.Role;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -28,7 +26,8 @@ public class CategoryService {
|
||||
- 카테고리 이름으로 카테고리 찾기
|
||||
*/
|
||||
public Category findCategory(String title) {
|
||||
return categoryRepository.findByTitle(title);
|
||||
return categoryRepository.findByTitle(title)
|
||||
.orElseThrow(() -> new IllegalArgumentException("NotFoundCategoryException"));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -41,7 +40,7 @@ public class CategoryService {
|
||||
/*
|
||||
- 카테고리와 카테고리별 아티클 수 찾기
|
||||
*/
|
||||
public List<CategoryNormalDto> getCategorytCountList() {
|
||||
public List<CategorySimpleView> getCategorytCountList() {
|
||||
return naCategoryRepository.getCategoryCount();
|
||||
}
|
||||
|
||||
@@ -74,7 +73,7 @@ public class CategoryService {
|
||||
*/
|
||||
@Transactional
|
||||
@CacheEvict(value = {"layoutCaching", "seoCaching"}, allEntries = true)
|
||||
public void changeCategory(List<CategoryNormalDto> categoryList) {
|
||||
public void changeCategory(List<CategorySimpleView> categoryList) {
|
||||
|
||||
// 1.카테고리 리스트 순서 작성
|
||||
sortingOrder(categoryList);
|
||||
@@ -83,62 +82,62 @@ public class CategoryService {
|
||||
|
||||
// 3. 카테고리 변경 루프
|
||||
while (!categoryList.isEmpty()) {
|
||||
CategoryNormalDto categoryNormalDto = categoryList.get(0);
|
||||
CategorySimpleView categorySimpleView = categoryList.get(0);
|
||||
categoryList.remove(0);
|
||||
|
||||
// 부모카테고리인경우
|
||||
if (categoryNormalDto.getTier() == 1) {
|
||||
if (categorySimpleView.getTier() == 1) {
|
||||
|
||||
Category pCategory = null;
|
||||
|
||||
// 부모카테고리가 기존에 존재 x
|
||||
if (categoryNormalDto.getId() == null) {
|
||||
pCategory = createNewCategory(categoryNormalDto, null);
|
||||
if (categorySimpleView.getId() == null) {
|
||||
pCategory = createNewCategory(categorySimpleView, null);
|
||||
}
|
||||
// 부모카테고리가 기존에 존재 o
|
||||
else {
|
||||
for (int i = 0; i < categoryListFromDb.size(); i++) {
|
||||
if (categoryListFromDb.get(i).getId().equals(categoryNormalDto.getId())) {
|
||||
if (categoryListFromDb.get(i).getId().equals(categorySimpleView.getId())) {
|
||||
pCategory = categoryListFromDb.get(i);
|
||||
categoryListFromDb.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pCategory.updateCategory(
|
||||
categoryNormalDto.getTitle(),
|
||||
categoryNormalDto.getTier(),
|
||||
categoryNormalDto.getPOrder(),
|
||||
categoryNormalDto.getCOrder(),
|
||||
categorySimpleView.getTitle(),
|
||||
categorySimpleView.getTier(),
|
||||
categorySimpleView.getPOrder(),
|
||||
categorySimpleView.getCOrder(),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
while (!categoryList.isEmpty()) {
|
||||
|
||||
CategoryNormalDto subCategoryNormalDto = categoryList.get(0);
|
||||
if (subCategoryNormalDto.getTier() == 1) break;
|
||||
CategorySimpleView subCategorySimpleView = categoryList.get(0);
|
||||
if (subCategorySimpleView.getTier() == 1) break;
|
||||
categoryList.remove(0);
|
||||
|
||||
// 자식 카테고리인경우
|
||||
Category cCategory = null;
|
||||
// 카테고리가 기존에 존재 x
|
||||
if (subCategoryNormalDto.getId() == null) {
|
||||
cCategory = createNewCategory(subCategoryNormalDto, pCategory.getTitle());
|
||||
if (subCategorySimpleView.getId() == null) {
|
||||
cCategory = createNewCategory(subCategorySimpleView, pCategory.getTitle());
|
||||
}
|
||||
// 카테고리가 기존에 존재 o
|
||||
else {
|
||||
for (int i = 0; i < categoryListFromDb.size(); i++) {
|
||||
if (categoryListFromDb.get(i).getId().equals(subCategoryNormalDto.getId())) {
|
||||
if (categoryListFromDb.get(i).getId().equals(subCategorySimpleView.getId())) {
|
||||
cCategory = categoryListFromDb.get(i);
|
||||
categoryListFromDb.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
cCategory.updateCategory(
|
||||
subCategoryNormalDto.getTitle(),
|
||||
subCategoryNormalDto.getTier(),
|
||||
subCategoryNormalDto.getPOrder(),
|
||||
subCategoryNormalDto.getCOrder(),
|
||||
subCategorySimpleView.getTitle(),
|
||||
subCategorySimpleView.getTier(),
|
||||
subCategorySimpleView.getPOrder(),
|
||||
subCategorySimpleView.getCOrder(),
|
||||
pCategory);
|
||||
}
|
||||
}
|
||||
@@ -152,17 +151,18 @@ public class CategoryService {
|
||||
- 새로운 카테고리 생성하기
|
||||
- 상위 카테고리 존재 유무 분기
|
||||
*/
|
||||
private Category createNewCategory(CategoryNormalDto categoryNormalDto, String parent) {
|
||||
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()
|
||||
.title(categoryNormalDto.getTitle())
|
||||
.pSortNum(categoryNormalDto.getPOrder())
|
||||
.cSortNum(categoryNormalDto.getCOrder())
|
||||
.tier(categoryNormalDto.getTier())
|
||||
.title(categorySimpleView.getTitle())
|
||||
.pSortNum(categorySimpleView.getPOrder())
|
||||
.cSortNum(categorySimpleView.getCOrder())
|
||||
.tier(categorySimpleView.getTier())
|
||||
.parents(parentCategory)
|
||||
.build();
|
||||
categoryRepository.save(category);
|
||||
@@ -172,12 +172,12 @@ public class CategoryService {
|
||||
/*
|
||||
- 카테고리 변경을 위해 카테고리의 순번을 작성하는 로직
|
||||
*/
|
||||
private void sortingOrder(List<CategoryNormalDto> categoryList) {
|
||||
private void sortingOrder(List<CategorySimpleView> categoryList) {
|
||||
int pOrderIndex = 0;
|
||||
int cOrderIndex = 0;
|
||||
|
||||
//티어별 트리구조로 순서 작성 로직
|
||||
for (CategoryNormalDto categoryDto : categoryList) {
|
||||
for (CategorySimpleView categoryDto : categoryList) {
|
||||
|
||||
if (categoryDto.getTier() == 1) {
|
||||
cOrderIndex = 0;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
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.service.ArticleService;
|
||||
import myblog.blog.article.application.ArticleService;
|
||||
import myblog.blog.comment.dto.CommentDto;
|
||||
import myblog.blog.comment.dto.CommentForm;
|
||||
import myblog.blog.comment.service.CommentService;
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package myblog.blog.member.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.persistence.Column;
|
||||
|
||||
/*
|
||||
- 뷰단에 사용할 멈버 DTO
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class MemberDto {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String email;
|
||||
|
||||
private String picUrl;
|
||||
|
||||
}
|
||||
32
src/main/java/myblog/blog/member/dto/MemberVo.java
Normal file
32
src/main/java/myblog/blog/member/dto/MemberVo.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package myblog.blog.member.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import myblog.blog.member.doamin.Member;
|
||||
|
||||
/*
|
||||
- 뷰단에 사용할 멈버 DTO
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class MemberVo {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String email;
|
||||
|
||||
private String picUrl;
|
||||
|
||||
static public MemberVo from(Member member){
|
||||
return new MemberVo(member.getId(),
|
||||
member.getUsername(),
|
||||
member.getUserId(),
|
||||
member.getEmail(),
|
||||
member.getPicUrl());
|
||||
}
|
||||
}
|
||||
@@ -112,4 +112,8 @@ public class Oauth2MemberService extends DefaultOAuth2UserService {
|
||||
|
||||
}
|
||||
|
||||
public Member findById(Long memberId) {
|
||||
return memberRepository.findById(memberId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("NotFoundMemberException"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package myblog.blog.rss;
|
||||
package myblog.blog.seo.adapter.incomming;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import myblog.blog.article.application.port.incomming.RssUseCase;
|
||||
|
||||
/*
|
||||
- rss 피드 발행 요청
|
||||
@@ -12,10 +13,10 @@ import org.springframework.web.bind.annotation.*;
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class RssController {
|
||||
private final RssService rssService;
|
||||
private final RssUseCase rssUseCase;
|
||||
|
||||
@GetMapping(value = "/rss",produces = "application/xml;charset=utf-8")
|
||||
public @ResponseBody String rssFeed() {
|
||||
return rssService.getRssFeed();
|
||||
return rssUseCase.getRssFeed();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package myblog.blog.sitemap;
|
||||
package myblog.blog.seo.adapter.incomming;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import myblog.blog.article.application.port.incomming.SiteMapUseCase;
|
||||
|
||||
/*
|
||||
- siteMap.xml 요청
|
||||
@@ -12,10 +13,10 @@ import org.springframework.web.bind.annotation.*;
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class SiteMapController {
|
||||
private final SiteMapService siteMapService;
|
||||
private final SiteMapUseCase siteMapUseCase;
|
||||
|
||||
@GetMapping(value = "/sitemap",produces = "application/xml;charset=utf-8")
|
||||
public @ResponseBody String getSiteMap() {
|
||||
return siteMapService.getSiteMap();
|
||||
return siteMapUseCase.getSiteMap();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package myblog.blog.rss;
|
||||
package myblog.blog.seo.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.service.ArticleService;
|
||||
import org.jdom2.*;
|
||||
import org.jdom2.output.*;
|
||||
import java.sql.Timestamp;
|
||||
@@ -24,15 +25,16 @@ import static myblog.blog.shared.utils.MarkdownUtils.*;
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class RssService {
|
||||
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);
|
||||
@@ -1,12 +1,14 @@
|
||||
package myblog.blog.sitemap;
|
||||
package myblog.blog.seo.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;
|
||||
import org.springframework.stereotype.Service;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import myblog.blog.article.service.ArticleService;
|
||||
import myblog.blog.category.service.CategoryService;
|
||||
import org.jdom2.*;
|
||||
import org.jdom2.output.*;
|
||||
@@ -18,7 +20,7 @@ import java.util.*;
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class SiteMapService {
|
||||
public class SiteMapService implements SiteMapUseCase {
|
||||
|
||||
static final String NAMESPACE = "http://www.sitemaps.org/schemas/sitemap/0.9";
|
||||
static final String ROOT = "https://www.jiniaslog.co.kr";
|
||||
@@ -26,11 +28,13 @@ public class SiteMapService {
|
||||
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();
|
||||
@@ -1,15 +0,0 @@
|
||||
package myblog.blog.tags.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/*
|
||||
- 뷰단 사용을 위한 DTO
|
||||
*/
|
||||
@Data
|
||||
public class TagsDto {
|
||||
private String name;
|
||||
public TagsDto(){}
|
||||
public TagsDto(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package myblog.blog.tags.queries;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.shared.utils.MapperUtils;
|
||||
import myblog.blog.tags.domain.Tags;
|
||||
import myblog.blog.tags.dto.TagsDto;
|
||||
import myblog.blog.tags.repository.ArticleTagListsRepository;
|
||||
import myblog.blog.tags.repository.TagsRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class TagsQueries {
|
||||
private final TagsRepository tagsRepository;
|
||||
private final ArticleTagListsRepository articleTagListsRepository;
|
||||
|
||||
public List<TagsDto> findAllTagDtos(){
|
||||
List<Tags> tags = tagsRepository.findAll();
|
||||
return tags.stream()
|
||||
.map(tag -> MapperUtils.getModelMapper().map(tag, TagsDto.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package myblog.blog.tags.repository;
|
||||
|
||||
import myblog.blog.tags.domain.Tags;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TagsRepository extends JpaRepository<Tags, Long> {
|
||||
Optional<Tags> findByName(String name);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package myblog.blog.rss
|
||||
|
||||
import myblog.blog.article.domain.Article
|
||||
import myblog.blog.article.service.ArticleService
|
||||
import myblog.blog.article.application.ArticleService
|
||||
import myblog.blog.seo.application.RssService
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package myblog.blog.sitemap
|
||||
|
||||
import myblog.blog.article.domain.Article
|
||||
import myblog.blog.article.service.ArticleService
|
||||
import myblog.blog.article.application.ArticleService
|
||||
import myblog.blog.seo.application.SiteMapService
|
||||
import myblog.blog.category.domain.Category
|
||||
import myblog.blog.category.service.CategoryService
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
||||
Reference in New Issue
Block a user