Skip to content
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
49 changes: 49 additions & 0 deletions src/main/java/run/attraction/api/v1/announcement/Post.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package run.attraction.api.v1.announcement;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import run.attraction.api.v1.archive.AuditableEntity;

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
public class Post extends AuditableEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, length = 100)
private String title;

@Enumerated(EnumType.STRING)
private PostCategory postCategory;

@Column(nullable = false)
private String content;

@Column(nullable = false)
@Builder.Default
private Long viewCount = 0L;

@Column(nullable = false)
private boolean isPinned;

public void update(String title, String content, PostCategory postCategory, boolean isPinned) {
this.title = title;
this.content = content;
this.postCategory = postCategory;
this.isPinned = isPinned;
}
}
25 changes: 25 additions & 0 deletions src/main/java/run/attraction/api/v1/announcement/PostCategory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package run.attraction.api.v1.announcement;

import java.util.Arrays;
import lombok.Getter;

@Getter
public enum PostCategory {
NOTICE("공지사항"),
UPDATE("업데이트"),
EVENT("이벤트"),
MAINTENANCE("점검");

private final String name;

PostCategory(final String name) {
this.name = name;
}

public static PostCategory findByName(final String name) {
return Arrays.stream(PostCategory.values())
.filter(postCategory -> postCategory.getName().equals(name))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게시글 카테고리입니다."));
}
}
24 changes: 24 additions & 0 deletions src/main/java/run/attraction/api/v1/announcement/SearchType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package run.attraction.api.v1.announcement;

import java.util.Arrays;
import lombok.Getter;

@Getter
public enum SearchType {
TITLE("제목"),
CONTENT("내용"),
TITLE_CONTENT("제목내용");

private final String type;

SearchType(final String type) {
this.type = type;
}

public static SearchType findSearchType(final String type) {
return Arrays.stream(SearchType.values())
.filter(searchType -> searchType.type.equals(type))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 검색 타입입니다."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package run.attraction.api.v1.announcement.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
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.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 run.attraction.api.v1.announcement.dto.PostDTO;
import run.attraction.api.v1.announcement.dto.PostSummaryDTO;
import run.attraction.api.v1.announcement.dto.request.PostCreateRequestDTO;
import run.attraction.api.v1.announcement.dto.request.PostSearchRequest;
import run.attraction.api.v1.announcement.dto.request.UpdatePostRequestDTO;
import run.attraction.api.v1.announcement.service.AnnouncementService;
import run.attraction.api.v1.archive.dto.response.ApiResponse;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/announcement")
@Validated
@Tag(name = "공지사항", description = "AnnouncementController")
public class AnnouncementController {

private final AnnouncementService announcementService;

@PostMapping
@Operation(summary = "게시글 생성", description = "게시글을 생성하는 로직입니다.")
public ApiResponse<Void> createPost(@Valid @RequestBody PostCreateRequestDTO request) {
announcementService.createPost(request);

return ApiResponse.from(HttpStatus.CREATED, "성공", null);
}

@GetMapping("/{postId}")
@Operation(summary = "게시글 조회", description = "postId를 입력받아 해당 게시글을 조회하는 로직입니다.")
public ApiResponse<PostDTO> getPost(@PathVariable Long postId) {
final PostDTO post = announcementService.findPostById(postId);

return ApiResponse.from(HttpStatus.OK, "성공", post);
}

@DeleteMapping("/{postId}")
@Operation(summary = "게시글 삭제", description = "postId를 입력받아 해당 게시글을 삭제하는 로직입니다.")
public ApiResponse<Void> deletePost(@PathVariable Long postId) {
announcementService.deletePostById(postId);

return ApiResponse.from(HttpStatus.OK, "성공", null);
}

@PatchMapping("/{postId}")
@Operation(summary = "게시글 수정", description = "postId를 입력받아 해당 게시글을 수정하는 로직입니다.")
public ApiResponse<Void> updatePost(@PathVariable Long postId, @RequestBody UpdatePostRequestDTO post) {
announcementService.updatePostById(postId, post);

return ApiResponse.from(HttpStatus.OK, "성공", null);
}

@GetMapping
@Operation(summary = "고정되지 않은 모든 게시물 가져오기", description = "고정되지 않은 모든 게시물을 가져오는 로직입니다.")
public ApiResponse<Page<PostSummaryDTO>> getPosts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
Pageable pageable = PageRequest.of(page, size);
final Page<PostSummaryDTO> posts = announcementService.findPosts(pageable);

return ApiResponse.from(HttpStatus.OK, "성공", posts);
}

@GetMapping("/pinned")
@Operation(summary = "고정된 모든 게시물 가져오기", description = "고정된 모든 게시물을 가져오는 로직입니다.")
public ApiResponse<Page<PostSummaryDTO>> getPinnedPosts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
Pageable pageable = PageRequest.of(page, size);
final Page<PostSummaryDTO> posts = announcementService.findPinnedPosts(pageable);

return ApiResponse.from(HttpStatus.OK, "성공", posts);
}

@GetMapping("/search")
@Operation(summary = "게시물 검색", description = "검색 타입(제목/내용/제목+내용)에 맞는 게시물을 검색해주는 로직")
public ApiResponse<Page<PostSummaryDTO>> searchPosts(@ModelAttribute PostSearchRequest request
) {
Pageable pageable = PageRequest.of(request.page(), request.size());
final Page<PostSummaryDTO> posts = announcementService.findPostsBySearchQuery(pageable, request);

return ApiResponse.from(HttpStatus.OK, "성공", posts);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package run.attraction.api.v1.announcement.dto;

import java.time.LocalDateTime;
import run.attraction.api.v1.announcement.Post;

public record PinnedPostSummaryDTO(
Long id,
String title,
String content,
String postCategory,
LocalDateTime createdAt,
LocalDateTime modifiedAt,
Long viewCount
) {
public PinnedPostSummaryDTO(Post post) {
this(
post.getId(),
post.getTitle(),
post.getContent(),
post.getPostCategory().getName(),
post.getCreatedAt(),
post.getModifiedAt(),
post.getViewCount()
);
}
}
30 changes: 30 additions & 0 deletions src/main/java/run/attraction/api/v1/announcement/dto/PostDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package run.attraction.api.v1.announcement.dto;

import java.time.LocalDateTime;
import run.attraction.api.v1.announcement.Post;

public record PostDTO(
Long id,
String title,
String content,
String postCategory,
LocalDateTime createdAt,
LocalDateTime modifiedAt,
Long viewCount,
boolean isPinned,
RelatedDTO related
) {
public PostDTO(Post post, Post previousPost, Post nextPost) {
this(
post.getId(),
post.getTitle(),
post.getContent(),
post.getPostCategory().getName(),
post.getCreatedAt(),
post.getModifiedAt(),
post.getViewCount(),
post.isPinned(),
new RelatedDTO(previousPost, nextPost)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package run.attraction.api.v1.announcement.dto;

import java.time.LocalDateTime;
import run.attraction.api.v1.announcement.Post;

public record PostSummaryDTO(
Long id,
String title,
String content,
String postCategory,
LocalDateTime createdAt,
LocalDateTime modifiedAt,
Long viewCount
) {
public PostSummaryDTO(Post post) {
this(
post.getId(),
post.getTitle(),
post.getContent(),
post.getPostCategory().getName(),
post.getCreatedAt(),
post.getModifiedAt(),
post.getViewCount()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package run.attraction.api.v1.announcement.dto;

import java.util.Objects;
import run.attraction.api.v1.announcement.Post;

public record RelatedDTO(
RelatedPostDTO previous,
RelatedPostDTO next
) {
public RelatedDTO(Post previousPost, Post nextPost) {
this(
Objects.isNull(previousPost) ? null : new RelatedPostDTO(previousPost),
Objects.isNull(nextPost) ? null : new RelatedPostDTO(nextPost)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package run.attraction.api.v1.announcement.dto;

import java.time.LocalDateTime;
import run.attraction.api.v1.announcement.Post;

public record RelatedPostDTO(
Long id,
String title,
String postCategory,
LocalDateTime createdAt,
boolean isPinned
) {
public RelatedPostDTO(Post post) {
this(post.getId(), post.getTitle(), post.getPostCategory().getName(), post.getCreatedAt(), post.isPinned());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package run.attraction.api.v1.announcement.dto.request;

import org.apache.logging.log4j.core.config.plugins.validation.constraints.NotBlank;

public record PostCreateRequestDTO(
@NotBlank(message = "제목은 필수입니다.")
String title,

@NotBlank(message = "글 내용 작성은 필 수 입니다.")
String content,

@NotBlank(message = "카테고리 선택은 필수입니다.")
String postCategory,

boolean isPinned) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package run.attraction.api.v1.announcement.dto.request;

public record PostSearchRequest(
String query,
String type,
Integer page,
Integer size
) {
public PostSearchRequest {
if (page == null || page < 0) {
page = 0;
}
if (size == null || size <= 0) {
size = 3;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package run.attraction.api.v1.announcement.dto.request;

public record UpdatePostRequestDTO(
String title,
String content,
String postCategory,
boolean isPinned
) {
}
Loading
Loading