Skip to content

Commit

Permalink
Merge pull request #155 from Team-SilverTown/feature
Browse files Browse the repository at this point in the history
🦾 개발 서버 배포
  • Loading branch information
IjjS authored Mar 21, 2024
2 parents 08c4398 + 27ec004 commit 3659a43
Show file tree
Hide file tree
Showing 16 changed files with 396 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
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.security.SecurityRequirements;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.net.URI;
import java.util.Objects;
Expand Down Expand Up @@ -71,7 +70,6 @@ public ResponseEntity<CreateMateResponse> create(
schema = @Schema(implementation = MateDetailResponse.class)
)
)
@SecurityRequirements
public ResponseEntity<MateDetailResponse> getDetailById(
@AuthenticationPrincipal
Long userId,
Expand Down Expand Up @@ -115,7 +113,6 @@ public ResponseEntity<MateDetailResponse> getDetailById(
)
}
)
@SecurityRequirements
public ResponseEntity<ScrollResponse<SimpleMateResponse>> getScrollBy(
@RequestParam(required = false)
Long postId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static ParticipantResponse withMessageFrom(MateParticipant participant) {

return ParticipantResponse.builder()
.id(participant.getId())
.message(participant.getMessage())
.userId(user.getId())
.nickname(user.getNickname())
.profileUrl(user.getProfileImg())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
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.security.SecurityRequirements;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.net.URI;
import java.util.Objects;
Expand Down Expand Up @@ -72,7 +71,6 @@ public ResponseEntity<CreatePostResponse> create(
schema = @Schema(implementation = PostDetailResponse.class)
)
)
@SecurityRequirements
public ResponseEntity<PostDetailResponse> getById(
@PathVariable
Long id
Expand Down Expand Up @@ -119,7 +117,6 @@ public ResponseEntity<PostDetailResponse> getById(
)
}
)
@SecurityRequirements()
public ResponseEntity<ScrollResponse<SimplePostResponse>> getScrollBy(
@AuthenticationPrincipal
Long loginId,
Expand Down
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();
}

}
2 changes: 2 additions & 0 deletions src/main/java/team/silvertown/masil/post/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.locationtech.jts.geom.LineString;
import team.silvertown.masil.common.BaseEntity;
import team.silvertown.masil.common.map.Address;
Expand Down Expand Up @@ -73,6 +74,7 @@ public class Post extends BaseEntity {
private int viewCount;

@Column(name = "like_count", nullable = false)
@Setter
private int likeCount;

@Builder
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/team/silvertown/masil/post/domain/PostLike.java
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 src/main/java/team/silvertown/masil/post/domain/PostLikeId.java
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 src/main/java/team/silvertown/masil/post/dto/SaveLikeDto.java
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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ public enum PostErrorCode implements ErrorCode {
NON_POSITIVE_DISTANCE(20212000, "산책로 포스트의 산책 거리는 양수여야 합니다"),
NON_POSITIVE_TOTAL_TIME(202_12001, "산책로 포스트의 산책 시간은 양수여야 합니다"),

POST_NOT_FOUND(202204000, "해당 아이디의 산책로 포스트를 찾을 수 없습니다"),
POST_NOT_FOUND(20220400, "해당 아이디의 산책로 포스트를 찾을 수 없습니다"),

NULL_MASIL(202_30000, "핀의 산책로 포스트를 확인할 수 없습니다"),
NULL_IS_LIKE(202_30001, "좋아요 상태는 null이 될 수 없습니다"),
PIN_OWNER_NOT_MATCHING(202_30300, "산책로 포스트의 사용자와 핀의 사용자가 다릅니다"),

NULL_USER(202_90000, "산책로 포스트 사용자를 확인할 수 없습니다"),
Expand Down
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);

}
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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ private User create(Provider authenticatedProvider, String providerId) {
return User.builder()
.socialId(providerId)
.provider(authenticatedProvider)
.totalCalories(0)
.totalCount(0)
.totalDistance(0)
.isPublic(true)
.build();
}
Expand Down
12 changes: 12 additions & 0 deletions src/main/resources/db/migration/V2__create_post_likes_table.sql
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)
);
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ class MateServiceTest {
@BeforeEach
void setUp() {
author = userRepository.save(UserTexture.createValidUser());
// TODO: apply post texture
post = postRepository.save(PostTexture.createDependentPost(author, 100));
title = MateTexture.getRandomSentenceWithMax(30);
content = MateTexture.getRandomSentenceWithMax(1000);
Expand Down Expand Up @@ -188,7 +187,7 @@ void setUp() {
MateDetailResponse actual = mateService.getDetailById(author.getId(), expected.getId());

// then
ParticipantResponse expectedAuthor = ParticipantResponse.withoutMessageFrom(savedAuthor);
ParticipantResponse expectedAuthor = ParticipantResponse.withMessageFrom(savedAuthor);

assertThat(actual)
.hasFieldOrPropertyWithValue("id", expected.getId())
Expand Down
Loading

0 comments on commit 3659a43

Please sign in to comment.