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
@@ -1,5 +1,6 @@
package com.example.feeda.domain.post.controller;

import com.example.feeda.domain.post.dto.PostLikeResponseDTO;
import com.example.feeda.domain.post.dto.PostRequestDto;
import com.example.feeda.domain.post.dto.PostResponseDto;
import com.example.feeda.domain.post.service.PostService;
Expand All @@ -25,6 +26,7 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


@RestController
@Validated
@RequestMapping("/api/posts")
Expand All @@ -42,6 +44,26 @@ public ResponseEntity<PostResponseDto> createPost(@RequestBody PostRequestDto re
return new ResponseEntity<>(post, HttpStatus.CREATED);
}

@PostMapping("/{id}/likes")
public ResponseEntity<PostLikeResponseDTO> makeLikes(
@PathVariable Long id,
@AuthenticationPrincipal JwtPayload jwtPayload) {

Long profileId = jwtPayload.getProfileId();
return new ResponseEntity<>(postService.makeLikes(id, jwtPayload), HttpStatus.OK);
}

@DeleteMapping("/{id}/likes")
public ResponseEntity<Void> deleteLikes(
@PathVariable Long id,
@AuthenticationPrincipal JwtPayload jwtPayload
) {
Long profileId = jwtPayload.getProfileId();
postService.deleteLikes(id, profileId);

return ResponseEntity.ok().build();
}

@GetMapping("/{id}")
public ResponseEntity<PostResponseDto> findPostById(@PathVariable @NotNull Long id) {
return new ResponseEntity<>(postService.findPostById(id), HttpStatus.OK);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.feeda.domain.post.dto;

import com.example.feeda.domain.post.entity.PostLike;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
public class PostLikeResponseDTO {

private final Long id;
private final Long postId;
private final Long profileId;
private final LocalDateTime createdAt;

public PostLikeResponseDTO(PostLike postLike) {
this.id = postLike.getId();
this.postId = postLike.getPost().getId();
this.profileId = postLike.getProfile().getId();
this.createdAt = postLike.getCreatedAt();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import jakarta.persistence.Id;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
public class PostResponseDto {

Expand All @@ -18,14 +20,23 @@ public class PostResponseDto {

private final String category;

public PostResponseDto(Long id, String title, String content, String category) {
this.id = id;
this.title = title;
this.content = content;
this.category = category;
private final Long likes;

private final LocalDateTime createdAt;

private final LocalDateTime updatedAt;

public PostResponseDto(Post post, Long likes) {
this.id = post.getId();
this.title = post.getTitle();
this.content = post.getContent();
this.category = post.getCategory();
this.likes = likes;
this.createdAt = post.getCreatedAt();
this.updatedAt = post.getUpdatedAt();
}

public static PostResponseDto toDto(Post post) {
return new PostResponseDto(post.getId(), post.getTitle(), post.getContent(), post.getCategory());
public static PostResponseDto toDto(Post post, Long likes) {
return new PostResponseDto(post, likes);
}
}
13 changes: 5 additions & 8 deletions src/main/java/com/example/feeda/domain/post/entity/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import jakarta.validation.constraints.Size;
import lombok.Getter;


@Getter
@Entity
@Table(name = "posts")
Expand All @@ -39,21 +38,19 @@ public class Post extends BaseEntity {
@JoinColumn(name = "profile_id")
private Profile profile;

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

protected Post() {

}

public void update(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() {
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package com.example.feeda.domain.post.repository;

import com.example.feeda.domain.post.entity.Post;
import com.example.feeda.domain.post.entity.PostLike;
import com.example.feeda.domain.profile.entity.Profile;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface PostLikeRepository extends JpaRepository<PostLike, Long> {
Optional<PostLike> findByPostAndProfile(Post post, Profile profile);
long countByPost(Post post); // PostLike 갯수
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.feeda.domain.post.service;

import com.example.feeda.domain.post.dto.PostLikeResponseDTO;
import com.example.feeda.domain.post.dto.PostRequestDto;
import com.example.feeda.domain.post.dto.PostResponseDto;
import com.example.feeda.security.jwt.JwtPayload;
Expand All @@ -8,7 +9,6 @@

public interface PostService {


PostResponseDto createPost(PostRequestDto postRequestDto,
JwtPayload jwtPayload);

Expand All @@ -21,4 +21,9 @@ PostResponseDto createPost(PostRequestDto postRequestDto,
PostResponseDto updatePost(Long id, PostRequestDto requestDto, JwtPayload jwtPayload);

void deletePost(Long id, JwtPayload jwtPayload);
}

PostLikeResponseDTO makeLikes(Long id, JwtPayload jwtPayload);

void deleteLikes(Long id, Long profileId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

import com.example.feeda.domain.follow.entity.Follows;
import com.example.feeda.domain.follow.repository.FollowsRepository;
import com.example.feeda.domain.post.dto.PostLikeResponseDTO;
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.entity.PostLike;
import com.example.feeda.domain.post.repository.PostLikeRepository;
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;
Expand All @@ -21,30 +26,60 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;


@Service
@RequiredArgsConstructor
public class PostServiceImpl implements PostService {

private final PostRepository postRepository;
private final ProfileRepository profileRepository;
private final ProfileRepository profileRepository; // 현재 로그인 사용자 정보를 찾기 위해 필요
private final FollowsRepository followsRepository;
private final PostLikeRepository postLikeRepository;

@Override
public PostResponseDto createPost(PostRequestDto postRequestDto, JwtPayload jwtPayload) {

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

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

Post savedPost = postRepository.save(post);

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

@Override
@Transactional
public PostLikeResponseDTO makeLikes(Long id, JwtPayload jwtPayload) {
Post post = postRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글이 존재하지 않습니다."));
Profile profile = profileRepository.findById(jwtPayload.getProfileId()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "프로필이 존재하지 않습니다."));
;

// 중복 좋아요 방지
postLikeRepository.findByPostAndProfile(post, profile).ifPresent(like -> {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "이미 좋아요를 눌렀습니다.");
});

PostLike savePost = postLikeRepository.save(new PostLike(post, profile));

return new PostLikeResponseDTO(savePost);
}

@Override
public void deleteLikes(Long id, Long profileId) {
Post post = postRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글이 존재하지 않습니다."));

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

PostLike postLike = postLikeRepository.findByPostAndProfile(post, profile)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "해당 사용자의 좋아요가 존재하지 않습니다."));

postLikeRepository.delete(postLike);
}

@Override
Expand All @@ -58,42 +93,43 @@ public PostResponseDto findPostById(Long id) {

Post findPost = optionalPost.get();

return new PostResponseDto(id, findPost.getTitle(), findPost.getContent(),
findPost.getCategory());
Long likeCount = postLikeRepository.countByPost(findPost);

return new PostResponseDto(findPost, likeCount);
}

@Override
@Transactional(readOnly = true)
public Page<PostResponseDto> findAll(Pageable pageable, String keyword) {

return postRepository.findAllByTitleContaining(keyword, pageable)
.map(PostResponseDto::toDto);
.map(post -> PostResponseDto.toDto(post, postLikeRepository.countByPost(post)));
}

@Override
@Transactional(readOnly = true)
public Page<PostResponseDto> findFollowingAllPost(Pageable pageable, JwtPayload jwtPayload) {

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

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

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

return postRepository.findAllByProfile_IdIn(followingProfileIds, newPageable)
.map(PostResponseDto::toDto);
.map(post -> PostResponseDto.toDto(post, postLikeRepository.countByPost(post)));
}

@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, "권한이 없습니다.");
Expand All @@ -102,18 +138,17 @@ public PostResponseDto updatePost(Long id, PostRequestDto requestDto, JwtPayload
findPost.update(requestDto.getTitle(), requestDto.getCategory(), requestDto.getCategory());
Post savedPost = postRepository.save(findPost);

return new PostResponseDto(savedPost.getId(),
savedPost.getTitle(),
savedPost.getContent(),
savedPost.getCategory());
Long likeCount = postLikeRepository.countByPost(findPost);

return new PostResponseDto(savedPost, likeCount);
}

@Override
@Transactional
public void deletePost(Long id, 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, "권한이 없습니다.");
Expand Down