-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #155 from Team-SilverTown/feature
🦾 개발 서버 배포
- Loading branch information
Showing
16 changed files
with
396 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
src/main/java/team/silvertown/masil/post/controller/PostLikeController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package team.silvertown.masil.post.controller; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.media.Content; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import io.swagger.v3.oas.annotations.security.SecurityRequirements; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import java.net.URI; | ||
import java.util.Objects; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.http.ResponseEntity.BodyBuilder; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PutMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import team.silvertown.masil.post.dto.SaveLikeDto; | ||
import team.silvertown.masil.post.service.PostLikeService; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@Tag(name = "산책로 좋아요 API") | ||
public class PostLikeController { | ||
|
||
private final PostLikeService postLikeService; | ||
|
||
@PutMapping( | ||
value = "/api/v1/posts/{postId}/likes", | ||
consumes = MediaType.APPLICATION_JSON_VALUE, | ||
produces = MediaType.APPLICATION_JSON_VALUE | ||
) | ||
@Operation(summary = "산책로 포스트 좋아요") | ||
@ApiResponses({ | ||
@ApiResponse( | ||
responseCode = "200", | ||
description = "좋아요 데이터 변경", | ||
content = @Content(schema = @Schema(implementation = SaveLikeDto.class)) | ||
), | ||
@ApiResponse( | ||
responseCode = "201", | ||
description = "최초 좋아요 데이터 생성", | ||
content = @Content(schema = @Schema(implementation = SaveLikeDto.class)) | ||
), | ||
}) | ||
@SecurityRequirements | ||
public ResponseEntity<SaveLikeDto> save( | ||
@AuthenticationPrincipal | ||
Long userId, | ||
@PathVariable | ||
Long postId, | ||
@RequestBody | ||
SaveLikeDto request | ||
) { | ||
SaveLikeDto response = postLikeService.save(userId, postId, request); | ||
|
||
return createResponseEntity(response.isCreated(), postId) | ||
.body(response); | ||
} | ||
|
||
private BodyBuilder createResponseEntity(Boolean isCreated, Long postId) { | ||
if (Objects.nonNull(isCreated) && isCreated) { | ||
URI uri = URI.create("/api/v1/posts/" + postId); | ||
|
||
return ResponseEntity.created(uri); | ||
} | ||
|
||
return ResponseEntity.ok(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
src/main/java/team/silvertown/masil/post/domain/PostLike.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package team.silvertown.masil.post.domain; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.EmbeddedId; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.Table; | ||
import jakarta.persistence.Transient; | ||
import java.util.Objects; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import team.silvertown.masil.common.BaseEntity; | ||
|
||
@Entity | ||
@Table(name = "post_likes") | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@AllArgsConstructor | ||
public class PostLike extends BaseEntity { | ||
|
||
@EmbeddedId | ||
private PostLikeId id; | ||
|
||
@Column(name = "is_like", nullable = false) | ||
private boolean isLike; | ||
|
||
@Transient | ||
private boolean isCreated = false; | ||
|
||
public void setIsLike(Boolean isLike) { | ||
this.isLike = Objects.nonNull(isLike) && isLike; | ||
} | ||
|
||
public void setCreatedTrue() { | ||
isCreated = true; | ||
} | ||
|
||
} |
23 changes: 23 additions & 0 deletions
23
src/main/java/team/silvertown/masil/post/domain/PostLikeId.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package team.silvertown.masil.post.domain; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Embeddable; | ||
import java.io.Serializable; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Embeddable | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@AllArgsConstructor | ||
public class PostLikeId implements Serializable { | ||
|
||
@Column(name = "user_id", nullable = false) | ||
private Long userId; | ||
|
||
@Column(name = "post_id", nullable = false) | ||
private Long postId; | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/team/silvertown/masil/post/dto/SaveLikeDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package team.silvertown.masil.post.dto; | ||
|
||
import com.fasterxml.jackson.annotation.JsonGetter; | ||
import com.fasterxml.jackson.annotation.JsonIgnore; | ||
import lombok.Value; | ||
import team.silvertown.masil.common.validator.Validator; | ||
import team.silvertown.masil.post.exception.PostErrorCode; | ||
|
||
@Value | ||
public class SaveLikeDto { | ||
|
||
boolean isLike; | ||
boolean isCreated; | ||
|
||
public SaveLikeDto(Boolean isLike, boolean isCreated) { | ||
Validator.notNull(isLike, PostErrorCode.NULL_IS_LIKE); | ||
|
||
this.isLike = isLike; | ||
this.isCreated = isCreated; | ||
} | ||
|
||
@JsonGetter("isLike") | ||
public boolean isLike() { | ||
return isLike; | ||
} | ||
|
||
@JsonIgnore | ||
public boolean isCreated() { | ||
return isCreated; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/main/java/team/silvertown/masil/post/repository/PostLikeRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package team.silvertown.masil.post.repository; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
import team.silvertown.masil.post.domain.PostLike; | ||
import team.silvertown.masil.post.domain.PostLikeId; | ||
|
||
public interface PostLikeRepository extends JpaRepository<PostLike, PostLikeId> { | ||
|
||
@Query("SELECT COUNT(*) FROM PostLike pl WHERE pl.id.postId = :postId AND pl.isLike = true") | ||
int countByPostId(Long postId); | ||
|
||
} |
59 changes: 59 additions & 0 deletions
59
src/main/java/team/silvertown/masil/post/service/PostLikeService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package team.silvertown.masil.post.service; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import team.silvertown.masil.common.exception.DataNotFoundException; | ||
import team.silvertown.masil.common.validator.Validator; | ||
import team.silvertown.masil.post.domain.Post; | ||
import team.silvertown.masil.post.domain.PostLike; | ||
import team.silvertown.masil.post.domain.PostLikeId; | ||
import team.silvertown.masil.post.dto.SaveLikeDto; | ||
import team.silvertown.masil.post.exception.PostErrorCode; | ||
import team.silvertown.masil.post.repository.PostLikeRepository; | ||
import team.silvertown.masil.post.repository.PostRepository; | ||
import team.silvertown.masil.user.exception.UserErrorCode; | ||
import team.silvertown.masil.user.repository.UserRepository; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class PostLikeService { | ||
|
||
private final UserRepository userRepository; | ||
private final PostRepository postRepository; | ||
private final PostLikeRepository postLikeRepository; | ||
|
||
@Transactional | ||
public SaveLikeDto save(Long userId, Long postId, SaveLikeDto request) { | ||
validateUserId(userId); | ||
|
||
Post post = postRepository.findById(postId) | ||
.orElseThrow(() -> new DataNotFoundException(PostErrorCode.POST_NOT_FOUND)); | ||
PostLikeId postLikeId = new PostLikeId(userId, postId); | ||
PostLike postLike = postLikeRepository.findById(postLikeId) | ||
.orElseGet(() -> saveNewPostLike(postLikeId, request)); | ||
|
||
postLike.setIsLike(request.isLike()); | ||
|
||
// TODO: 반정규화 필드를 활용한 성능 개선 | ||
post.setLikeCount(postLikeRepository.countByPostId(post.getId())); | ||
|
||
return new SaveLikeDto(postLike.isLike(), postLike.isCreated()); | ||
} | ||
|
||
private void validateUserId(Long userId) { | ||
boolean notExistUser = !userRepository.existsById(userId); | ||
|
||
Validator.throwIf(notExistUser, () -> new DataNotFoundException(UserErrorCode.USER_NOT_FOUND)); | ||
} | ||
|
||
private PostLike saveNewPostLike(PostLikeId postLikeId, SaveLikeDto request) { | ||
PostLike postLike = new PostLike(postLikeId, request.isLike(), false); | ||
PostLike savedPostLike = postLikeRepository.save(postLike); | ||
|
||
savedPostLike.setCreatedTrue(); | ||
|
||
return savedPostLike; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/main/resources/db/migration/V2__create_post_likes_table.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
CREATE TABLE post_likes | ||
( | ||
user_id BIGINT NOT NULL, | ||
post_id BIGINT NOT NULL, | ||
is_like TINYINT(1) NOT NULL, | ||
created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), | ||
updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), | ||
|
||
CONSTRAINT post_likes_pk PRIMARY KEY (user_id, post_id), | ||
CONSTRAINT post_likes_users_fk FOREIGN KEY (user_id) REFERENCES users (id), | ||
CONSTRAINT post_likes_posts_fk FOREIGN KEY (post_id) REFERENCES posts (id) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.