Skip to content

Commit

Permalink
[#63] 좋아요 생성/취소 기능 및 해당 게시물에 대한 좋아요 개수 기능 추가 (#87)
Browse files Browse the repository at this point in the history
* add: 좋아요 도메인

- likes 테이블 생성
- userId와 postId FK

* add: 좋아요 생성 및 취소 기능

* add: 게시물 좋아요 개수 증가/감소 기능

- JPQL UPDATE 사용을 위해 @Modifying

* add: Like 관련 에러 메시지 및 테스트용 entity 생성

* add: 좋아요 생성 및 취소 API, LikeRequestDto

* add: testUtils 수정으로 인한 Post 데이터 값 변경

* add: 좋아요 리포지토리와 서비스 테스트

- repository : 추가한 findByUserAndPost 메서드만 진행
- service : BDDMockito 테스트

* add: 좋아요 컨트롤러 테스트 및 Rest docs 생성

* add: build를 위한 test Lombok 제거

* add: 좋아요 생성 API 수정 및 LikeDto userId 변경

- 컨트롤러 API에 postId 추가
- @AuthenticationPrincipal 처리
  • Loading branch information
hikarigin99 authored Jan 27, 2023
1 parent 9071e53 commit 08dfa72
Show file tree
Hide file tree
Showing 20 changed files with 650 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.prgrms.prolog.domain.like.api;

import java.net.URI;

import javax.validation.Valid;

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RestController;
import org.springframework.web.util.UriComponentsBuilder;

import com.prgrms.prolog.domain.like.dto.LikeDto;
import com.prgrms.prolog.domain.like.dto.LikeDto.likeRequest;
import com.prgrms.prolog.domain.like.service.LikeServiceImpl;
import com.prgrms.prolog.global.jwt.JwtAuthentication;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/like")
public class LikeController {

private final LikeServiceImpl likeService;

@PostMapping(value = "/{postId}")
public ResponseEntity<Long> insert(
@PathVariable Long postId,
@AuthenticationPrincipal JwtAuthentication user
) {
LikeDto.likeRequest request = new likeRequest(user.id(), postId);
Long likeId = likeService.save(request);
URI location = UriComponentsBuilder.fromUriString("/api/v1/like/" + likeId).build().toUri();
return ResponseEntity.created(location).build();
}

@DeleteMapping
public ResponseEntity<Void> delete(@RequestBody @Valid likeRequest likeRequest) {
likeService.cancel(likeRequest);
return ResponseEntity.noContent().build();
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/prgrms/prolog/domain/like/dto/LikeDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.prgrms.prolog.domain.like.dto;

import javax.validation.constraints.NotNull;

public class LikeDto {

public record likeRequest(@NotNull Long userId,
@NotNull Long postId) {
}
}
44 changes: 44 additions & 0 deletions src/main/java/com/prgrms/prolog/domain/like/model/Like.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.prgrms.prolog.domain.like.model;

import static javax.persistence.FetchType.*;
import static javax.persistence.GenerationType.*;
import static lombok.AccessLevel.*;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import com.prgrms.prolog.domain.post.model.Post;
import com.prgrms.prolog.domain.user.model.User;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Table(name = "likes")
@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
public class Like {

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

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "user_id")
private User user;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "post_id")
private Post post;

@Builder
public Like(User user, Post post) {
this.user = user;
this.post = post;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.prgrms.prolog.domain.like.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.prgrms.prolog.domain.like.model.Like;
import com.prgrms.prolog.domain.post.model.Post;
import com.prgrms.prolog.domain.user.model.User;

@Repository
public interface LikeRepository extends JpaRepository<Like, Long> {
Optional<Like> findByUserAndPost(User user, Post post);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.prgrms.prolog.domain.like.service;

import com.prgrms.prolog.domain.like.dto.LikeDto;

public interface LikeService {
Long save(LikeDto.likeRequest likeRequest);

void cancel(LikeDto.likeRequest likeRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.prgrms.prolog.domain.like.service;

import javax.persistence.EntityNotFoundException;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.prgrms.prolog.domain.like.dto.LikeDto.likeRequest;
import com.prgrms.prolog.domain.like.model.Like;
import com.prgrms.prolog.domain.like.repository.LikeRepository;
import com.prgrms.prolog.domain.post.model.Post;
import com.prgrms.prolog.domain.post.repository.PostRepository;
import com.prgrms.prolog.domain.user.model.User;
import com.prgrms.prolog.domain.user.repository.UserRepository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Transactional
@Service
public class LikeServiceImpl implements LikeService {

private final LikeRepository likeRepository;
private final UserRepository userRepository;
private final PostRepository postRepository;

@Override
public Long save(likeRequest likeRequest) {

User user = getFindUserBy(likeRequest.userId());
Post post = getFindPostBy(likeRequest.postId());

//TODO 이미 좋아요 되어있으면 에러 반환 -> 409 Conflict 오류로 변환
if (likeRepository.findByUserAndPost(user, post).isPresent()) {
throw new EntityNotFoundException("exception.like.alreadyExist");
}

Like like = likeRepository.save(saveLike(user, post));

postRepository.addLikeCount(post.getId());
return like.getId();
}

@Override
public void cancel(likeRequest likeRequest) {

User user = getFindUserBy(likeRequest.userId());
Post post = getFindPostBy(likeRequest.postId());

Like like = likeRepository.findByUserAndPost(user, post)
.orElseThrow(() -> new EntityNotFoundException("exception.like.notExist"));

likeRepository.delete(like);
postRepository.subLikeCount(post.getId());
}

private Like saveLike(User user, Post post) {
return Like.builder()
.user(user)
.post(post)
.build();
}

private User getFindUserBy(Long userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("exception.user.notExists"));
}

private Post getFindPostBy(Long postId) {
return postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("exception.post.notExists"));
}
}
10 changes: 6 additions & 4 deletions src/main/java/com/prgrms/prolog/domain/post/model/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Objects;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
Expand All @@ -17,6 +18,7 @@
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.hibernate.annotations.ColumnDefault;
import org.springframework.util.Assert;

import com.prgrms.prolog.domain.comment.model.Comment;
Expand All @@ -39,10 +41,6 @@ public class Post extends BaseEntity {
private static final int TITLE_MAX_SIZE = 50;
private static final int CONTENT_MAX_SIZE = 65535;

private static final String USER_INFO_NEED_MESSAGE = "게시글은 작성자 정보가 필요합니다.";
private static final String NOT_NULL_DATA_MESSAGE = "빈 값일 수 없는 데이터입니다.";
private static final String OVER_LENGTH_MESSAGE = "입력할 수 있는 범위를 초과하였습니다.";

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
Expand All @@ -68,6 +66,10 @@ public class Post extends BaseEntity {
@JoinColumn(name = "series_id")
private Series series;

@ColumnDefault("0")
@Column(name = "like_count")
private int likeCount;

@Builder
public Post(String title, String content, boolean openStatus, User user, Series series) {
this.title = validateTitle(title);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
Expand All @@ -27,4 +28,12 @@ public interface PostRepository extends JpaRepository<Post, Long> {
WHERE p.id = :postId
""")
Optional<Post> joinUserFindById(@Param(value = "postId") Long postId);

@Modifying
@Query("UPDATE Post p SET p.likeCount = p.likeCount + 1 WHERE p.id = :postId")
int addLikeCount(@Param(value = "postId") Long postId);

@Modifying
@Query("UPDATE Post p SET p.likeCount = p.likeCount - 1 WHERE p.id = :postId")
int subLikeCount(@Param(value = "postId") Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@

public interface PostService {
Long save(PostRequest.CreateRequest request, Long userId);

PostResponse findById(Long postId);

Page<PostResponse> findAll(Pageable pageable);

PostResponse update(PostRequest.UpdateRequest update, Long userId, Long postId);

void delete(Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public class PostServiceImpl implements PostService {
private final PostTagRepository postTagRepository;
private final UserTagRepository userTagRepository;


@Override
@Transactional
public Long save(CreateRequest request, Long userId) {
Expand All @@ -66,10 +65,10 @@ private void registerSeries(CreateRequest request, Post post, User owner) {
Series series = seriesRepository
.findByIdAndTitle(owner.getId(), seriesTitle)
.orElseGet(() -> seriesRepository.save(
Series.builder()
.title(seriesTitle)
.user(owner)
.build()
Series.builder()
.title(seriesTitle)
.user(owner)
.build()
)
);
post.setSeries(series);
Expand All @@ -78,7 +77,7 @@ private void registerSeries(CreateRequest request, Post post, User owner) {
@Override
public PostResponse findById(Long postId) {
Post post = postRepository.joinCommentFindById(postId)
.orElseThrow(() -> new IllegalArgumentException(POST_NOT_EXIST_MESSAGE));
.orElseThrow(() -> new IllegalArgumentException("exception.post.notExists"));
Set<PostTag> findPostTags = postTagRepository.joinRootTagFindByPostId(postId);
post.addPostTagsFrom(findPostTags);
return PostResponse.toPostResponse(post);
Expand All @@ -94,7 +93,7 @@ public Page<PostResponse> findAll(Pageable pageable) {
@Transactional
public PostResponse update(UpdateRequest update, Long userId, Long postId) {
Post findPost = postRepository.joinUserFindById(postId)
.orElseThrow(() -> new IllegalArgumentException(POST_NOT_EXIST_MESSAGE));
.orElseThrow(() -> new IllegalArgumentException("exception.post.notExists"));

if (!findPost.getUser().checkSameUserId(userId)) {
throw new IllegalArgumentException("exception.post.not.owner");
Expand Down
22 changes: 19 additions & 3 deletions src/main/java/com/prgrms/prolog/domain/user/dto/UserDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

public class UserDto {

@Builder
public record UserProfile(
Long id,
String email,
Expand All @@ -15,6 +14,17 @@ public record UserProfile(
String prologName,
String profileImgUrl
) {
@Builder
public UserProfile(Long id, String email, String nickName, String introduce, String prologName,
String profileImgUrl) {
this.id = id;
this.email = email;
this.nickName = nickName;
this.introduce = introduce;
this.prologName = prologName;
this.profileImgUrl = profileImgUrl;
}

public static UserProfile toUserProfile(User user) {
return new UserProfile(
user.getId(),
Expand All @@ -27,15 +37,21 @@ public static UserProfile toUserProfile(User user) {
}
}

@Builder
public record UserInfo(
String email,
String nickName,
String provider,
String oauthId,
String profileImgUrl
) {

@Builder
public UserInfo(String email, String nickName, String provider, String oauthId, String profileImgUrl) {
this.email = email;
this.nickName = nickName;
this.provider = provider;
this.oauthId = oauthId;
this.profileImgUrl = profileImgUrl;
}
}

public record IdResponse(Long id) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/resources/db/migration/V2.2__add_like_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
DROP TABLE IF EXISTS likes;

CREATE TABLE likes
(
id bigint NOT NULL PRIMARY KEY AUTO_INCREMENT,
user_id bigint NOT NULL,
post_id bigint NOT NULL,
FOREIGN KEY fk_likes_user_id (user_id) REFERENCES users (id),
FOREIGN KEY fk_likes_post_id (post_id) REFERENCES post (id)
);

ALTER TABLE post ADD like_count INT DEFAULT 0;
Loading

0 comments on commit 08dfa72

Please sign in to comment.