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
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,89 @@

import com.example.feeda.domain.post.dto.PostRequestDto;
import com.example.feeda.domain.post.dto.PostResponseDto;
import com.example.feeda.domain.post.entity.Post;
import com.example.feeda.domain.post.service.PostService;
import com.example.feeda.security.jwt.JwtPayload;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
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
@Validated
@RequestMapping("/api/posts")
@RequiredArgsConstructor
public class PostController {

private final PostService postService;

@PostMapping
public ResponseEntity<PostResponseDto> createPost(@RequestBody PostRequestDto requestDto) {
public ResponseEntity<PostResponseDto> createPost(@RequestBody PostRequestDto requestDto,
@AuthenticationPrincipal JwtPayload jwtPayload) {

PostResponseDto post = postService.createPost(requestDto.getTitle(), requestDto.getContent(), requestDto.getCategory());
PostResponseDto post = postService.createPost(requestDto, jwtPayload);

return new ResponseEntity<>(post,
HttpStatus.CREATED);
return new ResponseEntity<>(post, HttpStatus.CREATED);
}

@GetMapping("/{id}")
public ResponseEntity<PostResponseDto> findPostById(@PathVariable Long id) {
public ResponseEntity<PostResponseDto> findPostById(@PathVariable @NotNull Long id) {
return new ResponseEntity<>(postService.findPostById(id), HttpStatus.OK);
}

@GetMapping
public ResponseEntity<Page<PostResponseDto>> findAllPost(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "") String keyword
@RequestParam(defaultValue = "1") @Min(1) int page,
@RequestParam(defaultValue = "10") @Min(1) int size,
@RequestParam(defaultValue = "") String keyword
) {
if (page < 1 || size < 1) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "page 와 size 는 1 이상의 값이어야 합니다.");
}

Pageable pageable = PageRequest.of(page - 1, size, Sort.Direction.DESC, "updatedAt");

return new ResponseEntity<>(postService.findAll(pageable, keyword), HttpStatus.OK);
}

@GetMapping("/followings")
public ResponseEntity<Page<PostResponseDto>> findFollowingAllPost(
@RequestParam(defaultValue = "1") @Min(1) int page,
@RequestParam(defaultValue = "10") @Min(1) int size,
@AuthenticationPrincipal JwtPayload jwtPayload
) {
Pageable pageable = PageRequest.of(page - 1, size);

return new ResponseEntity<>(postService.findFollowingAllPost(pageable, jwtPayload),
HttpStatus.OK);
}

@PutMapping("/{id}")
public ResponseEntity<Post> updatePost(
@PathVariable Long id,
@RequestBody PostRequestDto requestDto) {
Post post = postService.updatePost(id, requestDto);
public ResponseEntity<PostResponseDto> updatePost(
@PathVariable @NotNull Long id,
@RequestBody PostRequestDto requestDto, @AuthenticationPrincipal JwtPayload jwtPayload) {

PostResponseDto post = postService.updatePost(id, requestDto, jwtPayload);
return new ResponseEntity<>(post, HttpStatus.OK);
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePost(@PathVariable Long id) {
postService.deletePost(id);
public ResponseEntity<Void> deletePost(@PathVariable @NotNull Long id,
@AuthenticationPrincipal JwtPayload jwtPayload) {

postService.deletePost(id, jwtPayload);
return new ResponseEntity<>(HttpStatus.OK);
}
}
30 changes: 19 additions & 11 deletions src/main/java/com/example/feeda/domain/post/entity/Post.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package com.example.feeda.domain.post.entity;

import com.example.feeda.domain.profile.entity.Profile;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
Expand All @@ -10,7 +17,7 @@
@Getter
@Entity
@Table(name = "posts")
public class Post extends BaseEntity{
public class Post extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -28,24 +35,25 @@ public class Post extends BaseEntity{
@Column(length = 50)
private String category;

public void update(String title, String content, String category) {
this.title = title;
this.content = content;
this.category = category;
}
@ManyToOne
@JoinColumn(name = "profile_id")
private Profile profile;

public Post(String title, String content, String category) {
public Post(String title, String content, String category, Profile profile) {
this.title = title;
this.content = content;
this.category = category;
this.profile = profile;
}

protected Post() {

}

@ManyToOne
@JoinColumn(name = "profile_id")
private Profile profile;
public void update(String title, String content, String category) {
this.title = title;
this.content = content;
this.category = category;
}

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.example.feeda.domain.post.repository;

import com.example.feeda.domain.post.entity.Post;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;

public interface PostRepository extends JpaRepository<Post, Long> {

Page<Post> findAllByTitleContaining(String title, Pageable pageable);

Page<Post> findAllByProfile_IdIn(List<Long> followingProfileIds, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@

import com.example.feeda.domain.post.dto.PostRequestDto;
import com.example.feeda.domain.post.dto.PostResponseDto;
import com.example.feeda.domain.post.entity.Post;
import com.example.feeda.security.jwt.JwtPayload;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

public interface PostService {


PostResponseDto createPost(String title, String content, String category);
PostResponseDto createPost(PostRequestDto postRequestDto,
JwtPayload jwtPayload);

PostResponseDto findPostById(Long id);

Page<PostResponseDto> findAll(Pageable pageable, String keyword);

Post updatePost(Long id, PostRequestDto requestDto);
Page<PostResponseDto> findFollowingAllPost(Pageable pageable, JwtPayload jwtPayload);

PostResponseDto updatePost(Long id, PostRequestDto requestDto, JwtPayload jwtPayload);

void deletePost(Long id);
void deletePost(Long id, JwtPayload jwtPayload);
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,54 @@
package com.example.feeda.domain.post.service;

import com.example.feeda.domain.follow.entity.Follows;
import com.example.feeda.domain.follow.repository.FollowsRepository;
import com.example.feeda.domain.post.dto.PostRequestDto;
import com.example.feeda.domain.post.dto.PostResponseDto;
import com.example.feeda.domain.post.entity.Post;
import com.example.feeda.domain.post.repository.PostRepository;
import com.example.feeda.domain.profile.entity.Profile;
import com.example.feeda.domain.profile.repository.ProfileRepository;
import com.example.feeda.security.jwt.JwtPayload;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class PostServiceImpl implements PostService {

private final PostRepository postRepository;
private final ProfileRepository profileRepository;
private final FollowsRepository followsRepository;

@Override
public PostResponseDto createPost(String title, String content, String category) {
Post post = new Post(title, content, category);
public PostResponseDto createPost(PostRequestDto postRequestDto, JwtPayload jwtPayload) {

Profile profile = profileRepository.findById(jwtPayload.getProfileId())
.orElseThrow(
() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로필입니다."));

Post post = new Post(postRequestDto.getTitle(), postRequestDto.getContent(),
postRequestDto.getCategory(), profile);

Post savedPost = postRepository.save(post);

return new PostResponseDto(savedPost.getId(),
savedPost.getTitle(),
savedPost.getContent(),
savedPost.getCategory());
savedPost.getTitle(),
savedPost.getContent(),
savedPost.getCategory());
}

@Override
@Transactional(readOnly = true)
public PostResponseDto findPostById(Long id) {
Optional<Post> optionalPost = postRepository.findById(id);

Expand All @@ -40,29 +58,66 @@ public PostResponseDto findPostById(Long id) {

Post findPost = optionalPost.get();

return new PostResponseDto(id, findPost.getTitle(), findPost.getContent(), findPost.getCategory());
return new PostResponseDto(id, findPost.getTitle(), findPost.getContent(),
findPost.getCategory());
}

@Override
@Transactional(readOnly = true)
public Page<PostResponseDto> findAll(Pageable pageable, String keyword) {
return postRepository.findAllByTitleContaining(keyword, pageable).map(PostResponseDto::toDto);

return postRepository.findAllByTitleContaining(keyword, pageable)
.map(PostResponseDto::toDto);
}

@Override
public Post updatePost(Long id, PostRequestDto requestDto) {
@Transactional(readOnly = true)
public Page<PostResponseDto> findFollowingAllPost(Pageable pageable, JwtPayload jwtPayload) {

Page<Follows> followings = followsRepository.findAllByFollowers_Id(
jwtPayload.getProfileId(), pageable);

List<Long> followingProfileIds = followings.stream()
.map(following -> following.getFollowings().getId())
.toList();

Pageable newPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(),
Sort.Direction.DESC, "updatedAt");

return postRepository.findAllByProfile_IdIn(followingProfileIds, newPageable)
.map(PostResponseDto::toDto);
}

@Override
@Transactional
public PostResponseDto updatePost(Long id, PostRequestDto requestDto, JwtPayload jwtPayload) {

Post findPost = postRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글"));
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글"));

if (!findPost.getProfile().getId().equals(jwtPayload.getProfileId())) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "권한이 없습니다.");
}

findPost.update(requestDto.getTitle(), requestDto.getCategory(), requestDto.getCategory());
postRepository.save(findPost);
Post savedPost = postRepository.save(findPost);

return findPost;
return new PostResponseDto(savedPost.getId(),
savedPost.getTitle(),
savedPost.getContent(),
savedPost.getCategory());
}

@Override
public void deletePost(Long id) {
Post findPost = postRepository.findById(id).orElseThrow( () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글"));
@Transactional
public void deletePost(Long id, JwtPayload jwtPayload) {

Post findPost = postRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글"));

if (!findPost.getProfile().getId().equals(jwtPayload.getProfileId())) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "권한이 없습니다.");
}

postRepository.delete(findPost);
}
Expand Down