Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor/#105 게시글 생성, 조회 시 카테고리 컬럼 설정 추가 #106

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
package kgu.developers.api.post.application;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import kgu.developers.api.post.presentation.exception.PostNotFoundException;
import kgu.developers.api.post.presentation.request.PostRequest;
import kgu.developers.api.post.presentation.response.PostDetailResponse;
import kgu.developers.api.post.presentation.response.PostPersistResponse;
import kgu.developers.api.post.presentation.response.PostSummaryPageResponse;
import kgu.developers.api.user.application.UserService;
import kgu.developers.common.response.PaginatedListResponse;
import kgu.developers.domain.post.domain.Category;
import kgu.developers.domain.post.domain.Post;
import kgu.developers.domain.post.domain.PostRepository;
import kgu.developers.domain.user.domain.User;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Service
@RequiredArgsConstructor
Expand All @@ -30,16 +32,17 @@ public class PostService {
private LocalDateTime lastScheduledRun;

@Transactional
public PostPersistResponse createPost(PostRequest request) {
public PostPersistResponse createPost(PostRequest request, Category category) {
User author = userService.me();
Post createPost = Post.create(request.title(), request.content(), author);
Post createPost = Post.create(request.title(), request.content(), category, author);
postRepository.save(createPost);
return PostPersistResponse.from(createPost.getId());
}
minjo-on marked this conversation as resolved.
Show resolved Hide resolved

public PostSummaryPageResponse getPostsByKeyword(PageRequest request, String keyword) {
PaginatedListResponse<Post> paginatedListResponse = postRepository.findAllByTitleContainingOrderByCreatedAtDesc(
keyword, request);
public PostSummaryPageResponse getPostsByKeywordAndCategory(PageRequest request, String keyword,
Category category) {
PaginatedListResponse<Post> paginatedListResponse = postRepository.findAllByTitleContainingAndCategoryOrderByCreatedAtDesc(
keyword, category, request);
return PostSummaryPageResponse.of(paginatedListResponse.contents(), paginatedListResponse.pageable());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

import static org.springframework.http.HttpStatus.CREATED;

import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -16,17 +27,8 @@
import kgu.developers.api.post.presentation.response.PostDetailResponse;
import kgu.developers.api.post.presentation.response.PostPersistResponse;
import kgu.developers.api.post.presentation.response.PostSummaryPageResponse;
import kgu.developers.domain.post.domain.Category;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
Expand All @@ -42,9 +44,10 @@ public class PostController {
@ApiResponse(responseCode = "201", content = @Content(schema = @Schema(implementation = PostPersistResponse.class)))
@PostMapping
public ResponseEntity<PostPersistResponse> createPost(
@Parameter(description = "게시글 카테고리", example = "DEPT_INFO", required = true) @RequestParam Category category,
@RequestBody PostRequest request
) {
PostPersistResponse response = postService.createPost(request);
PostPersistResponse response = postService.createPost(request, category);
return ResponseEntity.status(CREATED).body(response);
}

Expand All @@ -54,12 +57,14 @@ public ResponseEntity<PostPersistResponse> createPost(
""")
@ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = PostSummaryPageResponse.class)))
@GetMapping
public ResponseEntity<PostSummaryPageResponse> getPostsByKeyword(
public ResponseEntity<PostSummaryPageResponse> getPostsByKeywordAndCategory(
@Parameter(description = "게시글 카테고리", example = "DEPT_INFO") @RequestParam(required = false) Category category,
@Parameter(description = "페이지 인덱스", example = "0", required = true) @RequestParam(defaultValue = "0") @PositiveOrZero int page,
@Parameter(description = "응답 개수", example = "10", required = true) @RequestParam(defaultValue = "10") @Positive int size,
@Parameter(description = "검색 키워드", example = "컴퓨터공학과") @RequestParam(required = false) String keyword
) {
PostSummaryPageResponse response = postService.getPostsByKeyword(PageRequest.of(page, size), keyword);
PostSummaryPageResponse response = postService.getPostsByKeywordAndCategory(PageRequest.of(page, size), keyword,
category);
return ResponseEntity.ok(response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import java.time.format.DateTimeFormatter;

import org.springframework.format.annotation.DateTimeFormat;

import io.swagger.v3.oas.annotations.media.Schema;
import kgu.developers.api.file.presentation.response.FileResponse;
import kgu.developers.domain.post.domain.Post;
import lombok.Builder;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.format.DateTimeFormatter;

@Builder
public record PostDetailResponse(
@Schema(description = "게시글 id", example = "1", requiredMode = REQUIRED)
Long postId,

@Schema(description = "게시글 카테고리", example = "학과공지", requiredMode = REQUIRED)
String category,

@Schema(description = "게시글 제목", example = "SW 부트캠프 4기 교육생 모집", requiredMode = REQUIRED)
String title,

Expand Down Expand Up @@ -53,6 +57,7 @@ public static PostDetailResponse from(Post post) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
return PostDetailResponse.builder()
.postId(post.getId())
.category(post.getCategory().getDescription())
.title(post.getTitle())
.content(post.getContent())
.author(post.getAuthor().getName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import java.time.format.DateTimeFormatter;

import org.springframework.format.annotation.DateTimeFormat;

import io.swagger.v3.oas.annotations.media.Schema;
import kgu.developers.domain.post.domain.Post;
import lombok.Builder;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.format.DateTimeFormatter;

@Builder
public record PostSummaryResponse(
@Schema(description = "게시글 id", example = "1", requiredMode = REQUIRED)
Long postId,

@Schema(description = "게시글 카테고리", example = "학과공지", requiredMode = REQUIRED)
String category,

@Schema(description = "게시글 제목", example = "SW 부트캠프 4기 교육생 모집", requiredMode = REQUIRED)
String title,

Expand All @@ -37,6 +41,7 @@ public static PostSummaryResponse from(Post post) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return PostSummaryResponse.builder()
.postId(post.getId())
.category(post.getCategory().getDescription())
.title(post.getTitle())
.author(post.getAuthor().getName())
.views(post.getViews())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package kgu.developers.common.exception;


import static kgu.developers.common.exception.GlobalExceptionCode.INVALID_INPUT;
import static kgu.developers.common.exception.GlobalExceptionCode.SERVER_ERROR;

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.TypeMismatchException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.http.HttpHeaders;
Expand Down Expand Up @@ -68,4 +68,17 @@ protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotV

return ResponseEntity.status(response.status()).body(response);
}

@Override
protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException exception,
HttpHeaders headers,
HttpStatusCode status,
WebRequest request) {
String message = String.format("Failed to convert '%s' with value: '%s'.",
exception.getPropertyName(),
exception.getValue());
ExceptionResponse response = ExceptionResponse.of(INVALID_INPUT.getStatus(), INVALID_INPUT.getCode(), message);

return ResponseEntity.status(response.status()).body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ public class Post extends BaseTimeEntity {
@OneToMany(mappedBy = "post", fetch = LAZY, cascade = ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();

public static Post create(String title, String content, User author) {
public static Post create(String title, String content, Category category, User author) {
return Post.builder()
.title(title)
.content(content)
.views(0)
.isPinned(false)
.category(category)
.author(author) // NOTE: User Setter 주입 방지 위해 생성자 주입
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
public interface PostRepository {
Post save(Post post);

PaginatedListResponse<Post> findAllByTitleContainingOrderByCreatedAtDesc(String keyword, Pageable pageable);
PaginatedListResponse<Post> findAllByTitleContainingAndCategoryOrderByCreatedAtDesc(String keyword,
Category category, Pageable pageable);

Optional<Post> findById(Long postId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.stereotype.Repository;

import kgu.developers.common.response.PaginatedListResponse;
import kgu.developers.domain.post.domain.Category;
import kgu.developers.domain.post.domain.Post;
import kgu.developers.domain.post.domain.PostRepository;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,8 +28,9 @@ public Optional<Post> findById(Long postId) {
}

@Override
public PaginatedListResponse findAllByTitleContainingOrderByCreatedAtDesc(String keyword, Pageable pageable) {
return queryPostRepository.findAllByTitleContainingOrderByCreatedAtDesc(keyword, pageable);
public PaginatedListResponse findAllByTitleContainingAndCategoryOrderByCreatedAtDesc(String keyword,
Category category, Pageable pageable) {
return queryPostRepository.findAllByTitleContainingAndCategoryOrderByCreatedAtDesc(keyword, category, pageable);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;

import kgu.developers.common.response.PageableResponse;
import kgu.developers.common.response.PaginatedListResponse;
import kgu.developers.domain.post.domain.Category;
import kgu.developers.domain.post.domain.Post;
import lombok.RequiredArgsConstructor;

Expand All @@ -21,20 +23,24 @@
public class QueryPostRepository {
private final JPAQueryFactory queryFactory;

public PaginatedListResponse findAllByTitleContainingOrderByCreatedAtDesc(String keyword, Pageable pageable) {
if (keyword == null)
keyword = "";
public PaginatedListResponse findAllByTitleContainingAndCategoryOrderByCreatedAtDesc(String keyword,
Category category, Pageable pageable) {

BooleanExpression whereClause = post.deletedAt.isNull()
.and(keyword != null ? post.title.contains(keyword) : null)
.and(category != null ? post.category.eq(category) : null);

minjo-on marked this conversation as resolved.
Show resolved Hide resolved
List<Post> posts = queryFactory.select(post)
.from(post)
.where(post.title.contains(keyword).and(post.deletedAt.isNull()))
.where(whereClause)
.orderBy(post.isPinned.desc(), post.createdAt.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

List<Long> postIds = queryFactory.select(post.id)
.from(post)
.where(post.title.contains(keyword).and(post.deletedAt.isNull()))
.where(whereClause)
.orderBy(post.isPinned.desc(), post.createdAt.desc())
.fetch();

Expand Down