Skip to content

Commit

Permalink
feat(article): Article 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobhboy committed Apr 29, 2024
1 parent b435a59 commit 81aac39
Show file tree
Hide file tree
Showing 14 changed files with 396 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.sickgyun.server.article.domain;

import java.time.LocalDateTime;

import org.springframework.data.annotation.CreatedDate;

import com.sickgyun.server.user.domain.User;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String imgUrl;
private String url;
@OneToOne(fetch = FetchType.LAZY)
private User user;
@CreatedDate
private LocalDateTime createdAt;

public Article(String title, String imgUrl, String url, User user) {
this.title = title;
this.imgUrl = imgUrl;
this.url = url;
this.user = user;
}

public void update(Article article) {
this.title = article.getTitle();
this.imgUrl = article.getImgUrl();
this.url = article.getUrl();
this.user = article.getUser();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.sickgyun.server.article.domain.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.article.exception.ArticleNotFoundException;
import com.sickgyun.server.user.domain.User;

public interface ArticleRepository extends JpaRepository<Article, Long> {

default Article getById(Long id) {
return findById(id)
.orElseThrow(() -> new ArticleNotFoundException(id));
}

List<Article> findByUser(User user);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sickgyun.server.article.exception;

import org.springframework.http.HttpStatus;

import com.sickgyun.server.common.exception.SickgyunException;

public class ArticleNotFoundException extends SickgyunException {
public ArticleNotFoundException(Long id) {
super(HttpStatus.NOT_FOUND, "id가 " + id + "인 아티클을 찾을 수 없어요");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sickgyun.server.article.exception;

import org.springframework.http.HttpStatus;

import com.sickgyun.server.common.exception.SickgyunException;

public class CannotDeleteOthersArticleException extends SickgyunException {
public CannotDeleteOthersArticleException() {
super(HttpStatus.FORBIDDEN, "다른 사람의 글을 삭제할 수 없습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.sickgyun.server.article.presentation;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.sickgyun.server.article.presentation.dto.ArticleCreateRequest;
import com.sickgyun.server.article.presentation.dto.ArticleResponse;
import com.sickgyun.server.article.presentation.dto.ArticleUpdateRequest;
import com.sickgyun.server.article.service.CommandArticleService;
import com.sickgyun.server.article.service.QueryArticleService;
import com.sickgyun.server.auth.annotation.LoginRequired;
import com.sickgyun.server.auth.service.implementation.AuthReader;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/articles")
public class ArticleController {
private final CommandArticleService commandService;
private final QueryArticleService queryService;
private final AuthReader authReader;
private final CommandArticleService commandArticleService;

@PostMapping
@LoginRequired
@ResponseStatus(HttpStatus.CREATED)
public void create(ArticleCreateRequest creatRequest) {
commandService.create(
creatRequest.toEntity(
authReader.getCurrentUser()
)
);
}

@GetMapping
@ResponseStatus(HttpStatus.OK)
public List<ArticleResponse> getAll() {
return queryService.getAll()
.stream()
.map(ArticleResponse::new)
.toList();
}

@GetMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public ArticleResponse getOne(@PathVariable Long id) {
return new ArticleResponse(queryService.getOne(id));
}

@LoginRequired
@GetMapping("/mine")
@ResponseStatus(HttpStatus.OK)
public List<ArticleResponse> findMine() {
return queryService.findMine(authReader.getCurrentUser())
.stream()
.map(ArticleResponse::new)
.toList();
}

@LoginRequired
@PutMapping("/{id}")
@ResponseStatus(HttpStatus.CREATED)
public void update(ArticleUpdateRequest updateRequest, @PathVariable Long id) {
commandService.update(
updateRequest.toEntity(
authReader.getCurrentUser()
),
id
);
}

@DeleteMapping("/{id}")
@LoginRequired
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
commandArticleService.delete(id, authReader.getCurrentUser());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.sickgyun.server.article.presentation.dto;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.user.domain.User;

import jakarta.validation.constraints.NotNull;

public record ArticleCreateRequest(
@NotNull(message = "title은 필수 값입니다.")
String title,
@NotNull(message = "imgUrl은 필수 값입니다.")
String imgUrl,
@NotNull(message = "url은 필수 값입니다.")
String url

) {
public Article toEntity(User currentUser) {
return new Article(
title,
imgUrl,
url,
currentUser
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.sickgyun.server.article.presentation.dto;

import java.time.LocalDateTime;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.user.presentation.dto.UserResponse;

public record ArticleResponse(
Long id,
String title,
String imgUrl,
String url,
UserResponse user,
LocalDateTime createdAt
) {
public ArticleResponse(Article article) {
this(
article.getId(),
article.getTitle(),
article.getImgUrl(),
article.getUrl(),
UserResponse.from(article.getUser()),
article.getCreatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sickgyun.server.article.presentation.dto;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.user.domain.User;

public record ArticleUpdateRequest() {
public Article toEntity(User currentUser) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.sickgyun.server.article.service;

import java.util.Objects;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.article.exception.CannotDeleteOthersArticleException;
import com.sickgyun.server.article.service.implementation.ArticleCreator;
import com.sickgyun.server.article.service.implementation.ArticleDeleter;
import com.sickgyun.server.article.service.implementation.ArticleReader;
import com.sickgyun.server.article.service.implementation.ArticleUpdater;
import com.sickgyun.server.user.domain.User;

import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
public class CommandArticleService {
private final ArticleCreator articleCreator;
private final ArticleUpdater articleUpdater;
private final ArticleReader articleReader;
private final ArticleDeleter articleDeleter;

public void create(Article entity) {
articleCreator.create(entity);
}

public void update(Article entity, Long updatableId) {
articleUpdater.update(entity, updatableId);
}

public void delete(Long id, User currentUser) {
Article byId = articleReader.findById(id);
if (!Objects.equals(byId.getUser().getId(), currentUser.getId())) {
throw new CannotDeleteOthersArticleException();
}

articleDeleter.deleteById(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.sickgyun.server.article.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.article.service.implementation.ArticleReader;
import com.sickgyun.server.user.domain.User;

import lombok.RequiredArgsConstructor;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class QueryArticleService {
private final ArticleReader articleReader;

public List<Article> getAll() {
return articleReader.findAll();
}

public Article getOne(Long id) {
return articleReader.findById(id);
}

public List<Article> findMine(User user) {
return articleReader.findMine(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.sickgyun.server.article.service.implementation;

import org.springframework.stereotype.Service;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.article.domain.repository.ArticleRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class ArticleCreator {
private final ArticleRepository articleRepository;

public void create(Article entity) {
articleRepository.save(entity);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.sickgyun.server.article.service.implementation;

import org.springframework.stereotype.Service;

import com.sickgyun.server.article.domain.repository.ArticleRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class ArticleDeleter {
private final ArticleRepository articleRepository;

public void deleteById(Long id) {
articleRepository.deleteById(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.sickgyun.server.article.service.implementation;

import java.util.List;

import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import com.sickgyun.server.article.domain.Article;
import com.sickgyun.server.article.domain.repository.ArticleRepository;
import com.sickgyun.server.user.domain.User;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class ArticleReader {
private final ArticleRepository articleRepository;

public List<Article> findAll() {
return articleRepository.findAll(Sort.by(Sort.Direction.DESC, "createdAt"));
}

public Article findById(Long id) {
return articleRepository.getById(id);
}

public List<Article> findMine(User user) {
return articleRepository.findByUser(user);
}
}
Loading

0 comments on commit 81aac39

Please sign in to comment.