-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #240 from urinaner/feat/238
[BE] [FEAT] 뉴스 CRUD API 생성
- Loading branch information
Showing
10 changed files
with
444 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
backend/src/main/java/org/example/backend/news/controller/NewsController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package org.example.backend.news.controller; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.Valid; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.example.backend.common.dto.PageRequestDto; | ||
import org.example.backend.common.dto.ResponseDto; | ||
import org.example.backend.news.domain.dto.NewsReqDto; | ||
import org.example.backend.news.domain.dto.NewsResDto; | ||
import org.example.backend.news.service.NewsService; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.ModelAttribute; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestPart; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@Tag(name = "뉴스", description = "뉴스 API") | ||
@RequestMapping("/api/news") | ||
public class NewsController { | ||
private final NewsService newsService; | ||
|
||
@Operation(summary = "뉴스 생성 API", description = "뉴스 생성") | ||
@PostMapping(consumes = "multipart/form-data") | ||
public ResponseEntity<Long> createNews( | ||
@RequestPart(value = "newsReqDto") @Valid NewsReqDto newsReqDto, | ||
@RequestPart(value = "newsImage", required = false) MultipartFile multipartFile | ||
) { | ||
Long newsId = newsService.saveNews(newsReqDto, multipartFile); | ||
return new ResponseEntity<>(newsId, HttpStatus.OK); | ||
} | ||
|
||
@Operation(summary = "모든 뉴스 조회 API", description = "모든 뉴스의 리스트 반환") | ||
@GetMapping | ||
public ResponseDto<List<NewsResDto>> getAllBoards(@Valid @ModelAttribute PageRequestDto pageRequest) { | ||
|
||
Page<NewsResDto> newsList = newsService.getAllNewss(pageRequest.toPageable()); | ||
return ResponseDto.ok(newsList.getNumber(), newsList.getTotalPages(), newsList.getContent()); | ||
} | ||
|
||
@Operation(summary = "단일 뉴스 조회 API", description = "단일 뉴스의 리스트 반환") | ||
@GetMapping("/{newsId}") | ||
public ResponseEntity<NewsResDto> getNews(@PathVariable(name = "newsId") Long newsId) { | ||
NewsResDto newsResDto = newsService.getNews(newsId); | ||
return new ResponseEntity<>(newsResDto, HttpStatus.OK); | ||
} | ||
|
||
@Operation(summary = "뉴스 정보 업데이트 API", description = "뉴스 정보 업데이트") | ||
@PostMapping("/{newsId}") | ||
public ResponseEntity<NewsResDto> updateNews(@PathVariable(name = "newsId") Long newsId, | ||
@RequestPart(value = "newsReqDto") NewsReqDto newsReqDto, | ||
@RequestPart(value = "newsImage", required = false) MultipartFile multipartFile) { | ||
NewsResDto newsResDto = newsService.updateNews(newsId, newsReqDto, multipartFile); | ||
return new ResponseEntity<>(newsResDto, HttpStatus.OK); | ||
} | ||
|
||
@Operation(summary = "뉴스 삭제 API", description = "뉴스 삭제") | ||
@DeleteMapping("/{newsId}") | ||
public ResponseEntity<?> deleteNews(@PathVariable(name = "newsId") Long newsId) { | ||
newsService.deleteNews(newsId); | ||
return new ResponseEntity<>(HttpStatus.OK); | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
backend/src/main/java/org/example/backend/news/domain/dto/NewsReqDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package org.example.backend.news.domain.dto; | ||
|
||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.Size; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class NewsReqDto { | ||
|
||
@NotBlank(message = "제목은 필수 입력값입니다.") | ||
@Size(max = 200, message = "제목은 최대 200자 입력 가능합니다.") | ||
private String title; | ||
|
||
@NotBlank(message = "내용은 필수 입력값입니다.") | ||
@Size(max = 5000, message = "내용은 최대 5000자까지 입력 가능합니다.") | ||
private String content; | ||
|
||
private String createDate; | ||
private String link; | ||
private String image; | ||
|
||
@Builder | ||
private NewsReqDto(String title, String content, String createDate, | ||
String link, String image) { | ||
this.title = title; | ||
this.content = content; | ||
this.createDate = createDate; | ||
this.link = link; | ||
this.image = image; | ||
} | ||
|
||
public static NewsReqDto of(String title, String content, String createDate, | ||
String link, String image) { | ||
return NewsReqDto.builder() | ||
.title(title) | ||
.content(content) | ||
.createDate(createDate) | ||
.link(link) | ||
.image(image) | ||
.build(); | ||
} | ||
|
||
public void setImage(String image) { | ||
this.image = image; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
backend/src/main/java/org/example/backend/news/domain/dto/NewsResDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package org.example.backend.news.domain.dto; | ||
|
||
import java.time.format.DateTimeFormatter; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.example.backend.news.domain.entity.News; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class NewsResDto { | ||
private Long id; | ||
private String title; | ||
private String content; | ||
private int view; | ||
private String createDate; | ||
private String link; | ||
private String image; | ||
|
||
@Builder | ||
private NewsResDto(Long id, String title, int view, String content, String createDate, | ||
String link, String image) { | ||
this.id = id; | ||
this.title = title; | ||
this.content = content; | ||
this.view = view; | ||
this.createDate = createDate; | ||
this.link = link; | ||
this.image = image; | ||
} | ||
|
||
public static NewsResDto of(News news) { | ||
return NewsResDto.builder() | ||
.id(news.getId()) | ||
.title(news.getTitle()) | ||
.content(news.getContent()) | ||
.view(news.getView()) | ||
.createDate(news.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))) | ||
.link(news.getLink()) | ||
.image(news.getImage()) | ||
.build(); | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
backend/src/main/java/org/example/backend/news/domain/entity/News.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package org.example.backend.news.domain.entity; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.example.backend.common.domain.BaseEntity; | ||
import org.example.backend.news.domain.dto.NewsReqDto; | ||
|
||
@Entity | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class News extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Column(name = "news_id", nullable = false) | ||
private Long id; | ||
|
||
@Column(name = "name") | ||
private String title; | ||
|
||
@Column(name = "content") | ||
private String content; | ||
|
||
@Column(name = "view") | ||
private int view; | ||
|
||
@Column(name = "link") | ||
private String link; | ||
|
||
@Column(name = "image", length = 1000) | ||
private String image; | ||
|
||
|
||
@Builder | ||
private News(String title, String content, int view, String link, | ||
String image) { | ||
this.title = title; | ||
this.content = content; | ||
this.view = view; | ||
this.link = link; | ||
this.image = image; | ||
} | ||
|
||
public static News of(NewsReqDto dto) { | ||
return News.builder() | ||
.title(dto.getTitle()) | ||
.content(dto.getContent()) | ||
.view(0) | ||
.link(dto.getLink()) | ||
.image(dto.getImage()) | ||
.build(); | ||
} | ||
|
||
public void update(NewsReqDto dto) { | ||
this.title = dto.getTitle(); | ||
this.content = dto.getContent(); | ||
this.link = dto.getLink(); | ||
this.image = dto.getImage(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
backend/src/main/java/org/example/backend/news/exception/NewsException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.example.backend.news.exception; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.example.backend.common.exception.BaseException; | ||
import org.example.backend.common.exception.BaseExceptionType; | ||
|
||
@RequiredArgsConstructor | ||
public class NewsException extends BaseException { | ||
|
||
private final NewsExceptionType exceptionType; | ||
|
||
@Override | ||
public BaseExceptionType exceptionType() { | ||
return exceptionType; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
backend/src/main/java/org/example/backend/news/exception/NewsExceptionType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package org.example.backend.news.exception; | ||
|
||
import static org.springframework.http.HttpStatus.BAD_REQUEST; | ||
import static org.springframework.http.HttpStatus.NOT_FOUND; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.example.backend.common.exception.BaseExceptionType; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@RequiredArgsConstructor | ||
public enum NewsExceptionType implements BaseExceptionType { | ||
|
||
NOT_FOUND_NEWS(NOT_FOUND, "뉴스를 찾을 수 없습니다") | ||
; | ||
|
||
private final HttpStatus httpStatus; | ||
private final String errorMessage; | ||
|
||
@Override | ||
public HttpStatus httpStatus() { | ||
return httpStatus; | ||
} | ||
|
||
@Override | ||
public String errorMessage() { | ||
return errorMessage; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
backend/src/main/java/org/example/backend/news/repository/NewsRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package org.example.backend.news.repository; | ||
|
||
import org.example.backend.news.domain.entity.News; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface NewsRepository extends JpaRepository<News, Long> { | ||
} |
70 changes: 70 additions & 0 deletions
70
backend/src/main/java/org/example/backend/news/service/NewsService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package org.example.backend.news.service; | ||
|
||
import static org.example.backend.news.exception.NewsExceptionType.NOT_FOUND_NEWS; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.example.backend.global.config.aws.S3Uploader; | ||
import org.example.backend.news.domain.dto.NewsReqDto; | ||
import org.example.backend.news.domain.dto.NewsResDto; | ||
import org.example.backend.news.domain.entity.News; | ||
import org.example.backend.news.exception.NewsException; | ||
import org.example.backend.news.exception.NewsExceptionType; | ||
import org.example.backend.news.repository.NewsRepository; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
public class NewsService { | ||
private final NewsRepository newsRepository; | ||
private final S3Uploader s3Uploader; | ||
private static final String dirName = "news"; | ||
|
||
@Transactional | ||
public Long saveNews(NewsReqDto newsReqDto, MultipartFile multipartFile) { | ||
|
||
if (multipartFile != null && !multipartFile.isEmpty()) { | ||
String uploadImageUrl = s3Uploader.upload(multipartFile, dirName); | ||
newsReqDto.setImage(uploadImageUrl); | ||
} | ||
News news = News.of(newsReqDto); | ||
News savedNews = newsRepository.save(news); | ||
return savedNews.getId(); | ||
} | ||
|
||
public NewsResDto getNews(Long newsId) { | ||
News news = findNewsById(newsId); | ||
return NewsResDto.of(news); | ||
} | ||
|
||
@Transactional | ||
public NewsResDto updateNews(Long newsId, NewsReqDto newsReqDto, MultipartFile multipartFile) { | ||
if (multipartFile != null && !multipartFile.isEmpty()) { | ||
String uploadImageUrl = s3Uploader.upload(multipartFile, dirName); | ||
newsReqDto.setImage(uploadImageUrl); | ||
} | ||
|
||
News news = findNewsById(newsId); | ||
news.update(newsReqDto); | ||
return NewsResDto.of(news); | ||
} | ||
|
||
public void deleteNews(Long newsId) { | ||
News news = findNewsById(newsId); | ||
newsRepository.delete(news); | ||
} | ||
|
||
private News findNewsById(Long newsId) { | ||
return newsRepository.findById(newsId) | ||
.orElseThrow(() -> new NewsException(NOT_FOUND_NEWS)); | ||
} | ||
|
||
public Page<NewsResDto> getAllNewss(Pageable pageable) { | ||
return newsRepository.findAll(pageable) | ||
.map(NewsResDto::of); | ||
} | ||
} |
Oops, something went wrong.