#24 simple sns: 댓글 기능 api

This commit is contained in:
haerong22
2022-11-23 01:32:05 +09:00
parent c4bb71e8e4
commit 4affa2cd2d
8 changed files with 202 additions and 68 deletions

View File

@@ -1,7 +1,9 @@
package com.example.sns.controller;
import com.example.sns.controller.request.PostCommentRequest;
import com.example.sns.controller.request.PostCreateRequest;
import com.example.sns.controller.request.PostModifyRequest;
import com.example.sns.controller.response.CommentResponse;
import com.example.sns.controller.response.PostResponse;
import com.example.sns.controller.response.Response;
import com.example.sns.model.Post;
@@ -66,4 +68,17 @@ public class PostController {
return Response.success(postService.likeCount(postId));
}
@PostMapping("/{postId}/comments")
public Response<Void> comment(@PathVariable Integer postId,
@RequestBody PostCommentRequest request,
Authentication authentication) {
postService.comment(postId, authentication.getName(), request.getComment());
return Response.success();
}
@GetMapping("/{postId}/comments")
public Response<Page<CommentResponse>> commentList(@PathVariable Integer postId,
Pageable pageable) {
return Response.success(postService.getComments(postId, pageable).map(CommentResponse::fromComment));
}
}

View File

@@ -2,9 +2,11 @@ package com.example.sns.controller.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PostCommentRequest {
private String comment;

View File

@@ -0,0 +1,33 @@
package com.example.sns.controller.response;
import com.example.sns.model.Comment;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
@Getter
@AllArgsConstructor
public class CommentResponse {
private Integer id;
private String comment;
private String username;
private Integer postId;
private Timestamp registeredAt;
private Timestamp updatedAt;
private Timestamp deletedAt;
public static CommentResponse fromComment(Comment comment) {
return new CommentResponse(
comment.getId(),
comment.getComment(),
comment.getUsername(),
comment.getPostId(),
comment.getRegisteredAt(),
comment.getUpdatedAt(),
comment.getDeletedAt()
);
}
}

View File

@@ -0,0 +1,34 @@
package com.example.sns.model;
import com.example.sns.model.entity.CommentEntity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
@Getter
@AllArgsConstructor
public class Comment {
private Integer id;
private String comment;
private String username;
private Integer postId;
private Timestamp registeredAt;
private Timestamp updatedAt;
private Timestamp deletedAt;
public static Comment fromEntity(CommentEntity entity) {
return new Comment(
entity.getId(),
entity.getComment(),
entity.getUser().getUsername(),
entity.getPost().getId(),
entity.getRegisteredAt(),
entity.getUpdatedAt(),
entity.getDeletedAt()
);
}
}

View File

@@ -0,0 +1,60 @@
package com.example.sns.model.entity;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.*;
import java.sql.Timestamp;
import java.time.Instant;
@Entity
@Table(name = "comments", indexes = {
@Index(name = "post_id_idx", columnList = "post_id")
})
@Getter
@Setter
@SQLDelete(sql = "UPDATE comments SET deleted_at = NOW() where id=?")
@Where(clause = "deleted_at IS NULL")
public class CommentEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne
@JoinColumn(name = "user_id")
private UserEntity user;
@ManyToOne
@JoinColumn(name = "post_id")
private PostEntity post;
@Column(name = "comment")
private String comment;
private Timestamp registeredAt;
private Timestamp updatedAt;
@Column(name = "deleted_at")
private Timestamp deletedAt;
@PrePersist
void registeredAt() {
this.registeredAt = Timestamp.from(Instant.now());
}
@PreUpdate
void updatedAt() {
this.updatedAt = Timestamp.from(Instant.now());
}
public static CommentEntity of(UserEntity userEntity, PostEntity postEntity, String comment) {
CommentEntity entity = new CommentEntity();
entity.setUser(userEntity);
entity.setPost(postEntity);
entity.setComment(comment);
return entity;
}
}

View File

@@ -0,0 +1,14 @@
package com.example.sns.repository;
import com.example.sns.model.entity.CommentEntity;
import com.example.sns.model.entity.PostEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CommentEntityRepository extends JpaRepository<CommentEntity, Integer> {
Page<CommentEntity> findAllByPost(PostEntity post, Pageable pageable);
}

View File

@@ -1,10 +1,13 @@
package com.example.sns.service;
import com.example.sns.exception.SnsApplicationException;
import com.example.sns.model.Comment;
import com.example.sns.model.Post;
import com.example.sns.model.entity.CommentEntity;
import com.example.sns.model.entity.LikeEntity;
import com.example.sns.model.entity.PostEntity;
import com.example.sns.model.entity.UserEntity;
import com.example.sns.repository.CommentEntityRepository;
import com.example.sns.repository.LikeEntityRepository;
import com.example.sns.repository.PostEntityRepository;
import com.example.sns.repository.UserEntityRepository;
@@ -23,37 +26,19 @@ public class PostService {
private final PostEntityRepository postEntityRepository;
private final UserEntityRepository userEntityRepository;
private final LikeEntityRepository likeEntityRepository;
private final CommentEntityRepository commentEntityRepository;
@Transactional
public void create(String title, String body, String username) {
UserEntity userEntity = userEntityRepository.findByUsername(username)
.orElseThrow(
() -> new SnsApplicationException(
USER_NOT_FOUND,
String.format("%s not founded", username)
)
);
UserEntity userEntity = getUserEntityOrException(username);
postEntityRepository.save(PostEntity.of(title, body, userEntity));
}
@Transactional
public Post modify(String title, String body, String username, Integer postId) {
UserEntity userEntity = userEntityRepository.findByUsername(username)
.orElseThrow(
() -> new SnsApplicationException(
USER_NOT_FOUND,
String.format("%s not founded", username)
)
);
PostEntity postEntity = postEntityRepository.findById(postId)
.orElseThrow(
() -> new SnsApplicationException(
POST_NOT_FOUND,
String.format("%s not founded", postId)
)
);
UserEntity userEntity = getUserEntityOrException(username);
PostEntity postEntity = getPostEntityOrException(postId);
if (postEntity.getUser() != userEntity) {
throw new SnsApplicationException(
@@ -70,21 +55,8 @@ public class PostService {
@Transactional
public void delete(String username, Integer postId) {
UserEntity userEntity = userEntityRepository.findByUsername(username)
.orElseThrow(
() -> new SnsApplicationException(
USER_NOT_FOUND,
String.format("%s not founded", username)
)
);
PostEntity postEntity = postEntityRepository.findById(postId)
.orElseThrow(
() -> new SnsApplicationException(
POST_NOT_FOUND,
String.format("%s not founded", postId)
)
);
UserEntity userEntity = getUserEntityOrException(username);
PostEntity postEntity = getPostEntityOrException(postId);
if (postEntity.getUser() != userEntity) {
throw new SnsApplicationException(
@@ -101,33 +73,15 @@ public class PostService {
}
public Page<Post> my(String username, Pageable pageable) {
UserEntity userEntity = userEntityRepository.findByUsername(username)
.orElseThrow(
() -> new SnsApplicationException(
USER_NOT_FOUND,
String.format("%s not founded", username)
)
);
UserEntity userEntity = getUserEntityOrException(username);
return postEntityRepository.findAllByUser(userEntity, pageable).map(Post::fromEntity);
}
@Transactional
public void like(Integer postId, String username) {
PostEntity postEntity = postEntityRepository.findById(postId)
.orElseThrow(
() -> new SnsApplicationException(
POST_NOT_FOUND,
String.format("%s not founded", postId)
)
);
UserEntity userEntity = userEntityRepository.findByUsername(username)
.orElseThrow(
() -> new SnsApplicationException(
USER_NOT_FOUND,
String.format("%s not founded", username)
)
);
PostEntity postEntity = getPostEntityOrException(postId);
UserEntity userEntity = getUserEntityOrException(username);
// check liked
likeEntityRepository.findByUserAndPost(userEntity, postEntity)
@@ -143,20 +97,42 @@ public class PostService {
}
public int likeCount(Integer postId) {
PostEntity postEntity = postEntityRepository.findById(postId)
.orElseThrow(
() -> new SnsApplicationException(
POST_NOT_FOUND,
String.format("%s not founded", postId)
)
);
PostEntity postEntity = getPostEntityOrException(postId);
// count like
return likeEntityRepository.countByPost(postEntity);
}
@Transactional
public void comment(Integer postId, String username) {
public void comment(Integer postId, String username, String comment) {
PostEntity postEntity = getPostEntityOrException(postId);
UserEntity userEntity = getUserEntityOrException(username);
commentEntityRepository.save(CommentEntity.of(userEntity, postEntity, comment));
}
public Page<Comment> getComments(Integer postId, Pageable pageable) {
PostEntity postEntity = getPostEntityOrException(postId);
return commentEntityRepository.findAllByPost(postEntity, pageable).map(Comment::fromEntity);
}
private PostEntity getPostEntityOrException(Integer postId) {
return postEntityRepository.findById(postId)
.orElseThrow(
() -> new SnsApplicationException(
POST_NOT_FOUND,
String.format("%s not founded", postId)
)
);
}
private UserEntity getUserEntityOrException(String username) {
return userEntityRepository.findByUsername(username)
.orElseThrow(
() -> new SnsApplicationException(
USER_NOT_FOUND,
String.format("%s not founded", username)
)
);
}
}

View File

@@ -286,7 +286,7 @@ public class PostControllerTest {
@WithMockUser
void 댓글작성시_게시글이_없는경우() throws Exception {
doThrow(new SnsApplicationException(ErrorCode.POST_NOT_FOUND)).when(postService).comment(any(), any());
doThrow(new SnsApplicationException(ErrorCode.POST_NOT_FOUND)).when(postService).comment(any(), any(), any());
mockMvc.perform(post("/api/v1/posts/1/comments")
.contentType(MediaType.APPLICATION_JSON)