Merge branch 'dev'

This commit is contained in:
jinia91
2022-03-24 00:04:55 +09:00
65 changed files with 986 additions and 586 deletions

View File

@@ -2,9 +2,9 @@ language: java
jdk:
- openjdk11
#branches:
# only:
# - main
branches:
only:
- main
# travis CI 서버 home
cache:

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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));

View File

@@ -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;
}
}

View File

@@ -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);}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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개까지 조회수가 높은 게시물 가져오기

View File

@@ -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);
}

View File

@@ -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> {
}

View File

@@ -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 처리

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,162 @@
package myblog.blog.article.application;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
import myblog.blog.article.application.port.incomming.ArticleQueriesUseCase;
import myblog.blog.article.application.port.outgoing.ArticleRepositoryPort;
import myblog.blog.article.domain.Article;
import myblog.blog.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;
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,21 @@
package myblog.blog.article.application.port.incomming;
import myblog.blog.article.application.port.response.ArticleResponseForCardBox;
import myblog.blog.article.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);
}

View File

@@ -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();
}

View File

@@ -0,0 +1,5 @@
package myblog.blog.article.application.port.incomming;
public interface RssUseCase {
String getRssFeed();
}

View File

@@ -0,0 +1,5 @@
package myblog.blog.article.application.port.incomming;
public interface SiteMapUseCase {
String getSiteMap();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}
/*

View File

@@ -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(

View File

@@ -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;
}
}
}

View File

@@ -4,6 +4,8 @@ import lombok.Getter;
import myblog.blog.shared.BasicEntity;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/*
- 임시 아티클 저장 Entity

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -12,7 +12,7 @@ import javax.validation.constraints.NotBlank;
@Getter
@Setter
@ToString
public class CategoryNormalDto {
public class CategorySimpleView {
private Long id;
@NotBlank(message = "카테고리명은 공백일 수 없습니다.")

View File

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

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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);

View File

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

View File

@@ -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;
}

View 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());
}
}

View File

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

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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