Skip to content

Commit

Permalink
Merge pull request #32 from 2024-TEAM-05/feature/post-like&share-incr…
Browse files Browse the repository at this point in the history
…ease

feat: 게시물 좋아요, 공유 수 올리는 service, controller 계층 구현
  • Loading branch information
yerim123456 authored Aug 26, 2024
2 parents ddf7eea + 6d6fc32 commit 22af3a2
Show file tree
Hide file tree
Showing 18 changed files with 505 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
@EnableFeignClients(basePackages = "team05.integrated_feed_backend.infra.sns.api")
public class IntegratedFeedBackendApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public enum StatusCode {
* 400 번대 CODE
**/
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "요청 경로가 지원되지 않습니다."),
POST_NOT_EXIST(HttpStatus.NOT_FOUND, "존재하지 않는 게시물입니다."),
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "요청된 사용자를 찾을 수 없습니다."),
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "인증 오류가 발생했습니다."),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
Expand All @@ -28,6 +29,7 @@

@Slf4j
@RequiredArgsConstructor
@RestControllerAdvice
public class GlobalExceptionHandler {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import team05.integrated_feed_backend.common.BaseApiResponse;
import team05.integrated_feed_backend.common.code.StatusCode;
import team05.integrated_feed_backend.module.post.dto.request.PostSearchReq;
import team05.integrated_feed_backend.module.post.dto.response.PostSearchRes;
import team05.integrated_feed_backend.module.post.service.PostService;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/posts")
public class PostController implements PostControllerDocs {

private final PostService postService;

@Override
@GetMapping
public BaseApiResponse<PostSearchRes> getPosts(
Expand All @@ -28,4 +34,20 @@ public BaseApiResponse<PostSearchRes> getPosts(
return new BaseApiResponse<>(HttpStatus.OK, StatusCode.OK.getMessage(), res);
}

// 좋아요 수 증가시키는 api
@ResponseStatus(HttpStatus.OK)
@PatchMapping("/{postId}/like")
public BaseApiResponse<Void> increaseLikeCount(@PathVariable(name = "postId") Long postId) {
postService.increaseLikeCount(postId);
return BaseApiResponse.of(StatusCode.OK);
}

// 공유 수 증가시키는 api
@ResponseStatus(HttpStatus.OK)
@PatchMapping("/{postId}/share")
public BaseApiResponse<Void> increaseShareCount(@PathVariable(name = "postId") Long postId) {
postService.increaseShareCount(postId);
return BaseApiResponse.of(StatusCode.OK);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import team05.integrated_feed_backend.common.BaseApiResponse;
import team05.integrated_feed_backend.module.post.dto.request.PostSearchReq;
Expand All @@ -16,4 +17,17 @@ BaseApiResponse<PostSearchRes> getPosts(
PostSearchReq postSearchReq
);

@Operation(summary = "게시물 좋아요 수 올리기")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "게시물 좋아요 수가 증가되었습니다.", useReturnTypeSchema = true),
@ApiResponse(responseCode = "404", description = "존재하지 않는 게시물입니다.", useReturnTypeSchema = true),
})
BaseApiResponse<Void> increaseLikeCount(Long postId);

@Operation(summary = "게시물 공유 수 올리기")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "게시물 공유 수가 증가되었습니다.", useReturnTypeSchema = true),
@ApiResponse(responseCode = "404", description = "존재하지 않는 게시물입니다.", useReturnTypeSchema = true),
})
BaseApiResponse<Void> increaseShareCount(Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,13 @@ public class Post extends BaseEntity {
@Builder.Default
private List<PostHashtag> postHashtags = new ArrayList<>();

// 좋아요 수 증가시키는 메서드
public void increaseLikeCount() {
this.likeCount += 1;
}

// 공유 수 증가시키는 메서드
public void increaseShareCount() {
this.shareCount += 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package team05.integrated_feed_backend.module.post.event;

import lombok.AllArgsConstructor;
import lombok.Getter;
import team05.integrated_feed_backend.common.enums.SocialMediaType;

@Getter
@AllArgsConstructor
public class LikeCountIncreasedEvent {
private final Long postId;
private final SocialMediaType type;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package team05.integrated_feed_backend.module.post.event;

import lombok.AllArgsConstructor;
import lombok.Getter;
import team05.integrated_feed_backend.common.enums.SocialMediaType;

@Getter
@AllArgsConstructor
public class ShareCountIncreasedEvent {
private final Long postId;
private final SocialMediaType type;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package team05.integrated_feed_backend.module.post.event.listener;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import team05.integrated_feed_backend.common.enums.SocialMediaType;
import team05.integrated_feed_backend.infra.sns.adapter.FacebookAdapter;
import team05.integrated_feed_backend.infra.sns.adapter.InstagramAdapter;
import team05.integrated_feed_backend.infra.sns.adapter.TwitterAdapter;
import team05.integrated_feed_backend.module.post.event.LikeCountIncreasedEvent;
import team05.integrated_feed_backend.module.post.event.ShareCountIncreasedEvent;

@Component
@RequiredArgsConstructor
@Slf4j
public class PostEventListener {

private final FacebookAdapter facebookAdapter;
private final TwitterAdapter twitterAdapter;
private final InstagramAdapter instagramAdapter;

@Async
@EventListener
public void handleLikeCountIncreasedEvent(LikeCountIncreasedEvent event) {
Long postId = event.getPostId();
SocialMediaType type = event.getType();
log.info("Asynchronously handling like count increase event for post ID: {}", postId);

// 외부 API 호출
switch (type) {
case FACEBOOK -> facebookAdapter.increaseLikeCount(postId);
case INSTAGRAM -> instagramAdapter.increaseLikeCount(postId);
case TWITTER -> twitterAdapter.increaseLikeCount(postId);
default -> log.error(postId + " 게시물의 " + type + ": facebook, instagram, twitter 중 하나로 설정되어 있지 않습니다.");
}
}

@Async
@EventListener
public void handleShareCountIncreasedEvent(ShareCountIncreasedEvent event) {
Long postId = event.getPostId();
SocialMediaType type = event.getType();
log.info("Asynchronously handling share count increase event for post ID: {}", postId);

// 외부 API 호출
switch (type) {
case FACEBOOK -> facebookAdapter.increaseShareCount(postId);
case INSTAGRAM -> instagramAdapter.increaseShareCount(postId);
case TWITTER -> twitterAdapter.increaseShareCount(postId);
default -> log.error(postId + " 게시물의 " + type + ": facebook, instagram, twitter 중 하나로 설정되어 있지 않습니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package team05.integrated_feed_backend.module.post.event.publisher;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import team05.integrated_feed_backend.common.enums.SocialMediaType;
import team05.integrated_feed_backend.module.post.event.LikeCountIncreasedEvent;
import team05.integrated_feed_backend.module.post.event.ShareCountIncreasedEvent;

@Component
@RequiredArgsConstructor
@Slf4j
public class PostEventPublisher {

private final ApplicationEventPublisher eventPublisher;

@Async
public void publishLikeCountIncreasedEvent(Long postId, SocialMediaType type) {
eventPublisher.publishEvent(new LikeCountIncreasedEvent(postId, type));
log.info("LikeCountIncreasedEvent published asynchronously for post ID: {}", postId);
}

@Async
public void publishShareCountIncreasedEvent(Long postId, SocialMediaType type) {
eventPublisher.publishEvent(new ShareCountIncreasedEvent(postId, type));
log.info("ShareCountIncreasedEvent published asynchronously for post ID: {}", postId);
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package team05.integrated_feed_backend.module.post.repository;

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

import team05.integrated_feed_backend.module.post.entity.Post;

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {

}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package team05.integrated_feed_backend.module.post.service;

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

import lombok.RequiredArgsConstructor;
import team05.integrated_feed_backend.common.code.StatusCode;
import team05.integrated_feed_backend.exception.custom.DataNotFoundException;
import team05.integrated_feed_backend.module.post.entity.Post;
import team05.integrated_feed_backend.module.post.event.publisher.PostEventPublisher;
import team05.integrated_feed_backend.module.post.repository.PostRepository;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class PostService {

private final PostRepository postRepository;
private final PostEventPublisher postEventPublisher;

@Transactional
public void increaseLikeCount(Long postId) {
// 게시물 존재 여부 확인
Post post = postRepository.findById(postId)
.orElseThrow(() -> new DataNotFoundException(StatusCode.POST_NOT_EXIST));

// 내부 DB count 올리기
post.increaseLikeCount();

// 이벤트 비동기 발행
postEventPublisher.publishLikeCountIncreasedEvent(postId, post.getType());

}

@Transactional
public void increaseShareCount(Long postId) {
// 게시물 존재 여부 확인
Post post = postRepository.findById(postId)
.orElseThrow(() -> new DataNotFoundException(StatusCode.POST_NOT_EXIST));

// 내부 db count 올리기
post.increaseShareCount();

// 이벤트 비동기 발행
postEventPublisher.publishShareCountIncreasedEvent(postId, post.getType());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package team05.integrated_feed_backend;

import java.util.ArrayList;

import team05.integrated_feed_backend.common.enums.SocialMediaType;
import team05.integrated_feed_backend.module.post.entity.Post;

public class MockEntityFactory {

// Post 생성
public static Post createMockPost() {
return Post.builder()
.title("Sample Post Title")
.content("This is a sample post content.")
.type(SocialMediaType.FACEBOOK)
.viewCount(100L)
.likeCount(10L)
.shareCount(5L)
.postHashtags(new ArrayList<>())
.build();
}

}
Loading

0 comments on commit 22af3a2

Please sign in to comment.