Skip to content

Commit

Permalink
Merge pull request #66 from ttoklip/Feat/12-뉴스레터-스크랩-api
Browse files Browse the repository at this point in the history
pr Feat/12 뉴스레터 좋아요, 스크랩 기능 구현
  • Loading branch information
why-only-english authored Feb 15, 2024
2 parents bfa242e + 3b78fcf commit 6998a3b
Show file tree
Hide file tree
Showing 35 changed files with 854 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.api.ttoklip.domain.honeytip.Scrap.domain;

import com.api.ttoklip.domain.common.base.BaseTimeEntity;
import com.api.ttoklip.domain.honeytip.post.domain.HoneyTip;
import com.api.ttoklip.domain.member.domain.Member;
import jakarta.persistence.*;
import lombok.*;

import static com.api.ttoklip.global.util.SecurityUtil.getCurrentMember;

@Getter
@Builder
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class HoneyTipScrap extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "honey_tip_id")
private HoneyTip honeyTip;

public static HoneyTipScrap from(final HoneyTip honeyTip) {
return HoneyTipScrap.builder()
.member(getCurrentMember())
.honeyTip(honeyTip)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.api.ttoklip.domain.honeytip.Scrap.repository;

import com.api.ttoklip.domain.honeytip.Scrap.domain.HoneyTipScrap;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface HoneyTipScrapRepository extends JpaRepository<HoneyTipScrap, Long>, HoneyTipScrapRepositoryCustom {

Optional<HoneyTipScrap> findByHoneyTipIdAndMemberId(Long honeyTipId, Long memberId);
boolean existsByHoneyTipIdAndMemberId(Long honeyTipId, Long memberId);

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.api.ttoklip.domain.honeytip.Scrap.repository;

public interface HoneyTipScrapRepositoryCustom {

Long countHoneyTipScrapsByHoneyTipId(final Long honeyTipId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.api.ttoklip.domain.honeytip.Scrap.service;

import com.api.ttoklip.domain.honeytip.Scrap.domain.HoneyTipScrap;
import com.api.ttoklip.domain.honeytip.Scrap.repository.HoneyTipScrapRepository;
import com.api.ttoklip.domain.honeytip.like.domain.HoneyTipLike;
import com.api.ttoklip.domain.honeytip.post.domain.HoneyTip;
import com.api.ttoklip.domain.honeytip.post.service.HoneyTipCommonService;
import com.api.ttoklip.global.exception.ApiException;
import com.api.ttoklip.global.exception.ErrorType;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.api.ttoklip.global.util.SecurityUtil.getCurrentMember;

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

private final HoneyTipScrapRepository honeyTipScrapRepository;
private final HoneyTipCommonService honeyTipCommonService;

// 스크랩 생성
public void registerScrap(final Long honeyTipId) {
Long currentMemberId = getCurrentMember().getId();
boolean exists = honeyTipScrapRepository.existsByHoneyTipIdAndMemberId(honeyTipId, currentMemberId);
if (exists) {
return; // 이미 스크랩이 존재하면 좋아요를 생성하지 않고 return
}

HoneyTip findHoneyTip = honeyTipCommonService.getHoneytip(honeyTipId);
HoneyTipScrap honeyTipScrap = HoneyTipScrap.from(findHoneyTip);
honeyTipScrapRepository.save(honeyTipScrap);
}

// 스크랩 취소
public void cancelScrap(final Long honeyTipId) {
// HoneyTipId (게시글 ID)
HoneyTip findHoneyTip = honeyTipCommonService.getHoneytip(honeyTipId);
Long findHoneyTipId = findHoneyTip.getId();
Long currentMemberId = getCurrentMember().getId();

HoneyTipScrap honeyTipScrap = honeyTipScrapRepository.findByHoneyTipIdAndMemberId(findHoneyTipId, currentMemberId)
.orElseThrow(() -> new ApiException(ErrorType.SCRAP_NOT_FOUND));

// 자격 검증: 이 단계에서는 findByHoneyTipIdAndMemberId 결과가 존재하므로, 현재 사용자가 스크랩을 누른 것입니다.
// 별도의 자격 검증 로직이 필요 없으며, 바로 삭제를 진행할 수 있습니다.
honeyTipScrapRepository.deleteById(honeyTipScrap.getId());
}

public Long countHoneyTipScraps(final Long honeyTipId) {
return honeyTipScrapRepository.countHoneyTipScrapsByHoneyTipId(honeyTipId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,27 @@ public class HoneyTipResponseConstant {
}
}
""";

public static final String REGISTER_SCRAP = """
{
"time": "2024-02-10T12:55:35.127794",
"status": 200,
"code": "200",
"message": "요청에 성공하였습니다.",
"result": {
"message": "HoneyTip Type의 3번째 스크랩을(를) 생성했습니다."
}
}
""";
public static final String CANCEL_SCRAP = """
{
"time": "2024-02-10T13:01:49.26421",
"status": 200,
"code": "200",
"message": "요청에 성공하였습니다.",
"result": {
"message": "HoneyTip Type의 3번째 스크랩을(를) 삭제했습니다."
}
}
""";
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public SuccessResponse<Message> report(final @PathVariable Long postId,
return new SuccessResponse<>(message);
}

/* LIKE */
@Operation(summary = "꿀팁공유해요 도움이되었어요 추가", description = "꿀팁 ID에 해당하는 게시글을 도움이되었어요를 추가합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "도움이되었어요 추가 성공",
Expand Down Expand Up @@ -144,6 +145,41 @@ public SuccessResponse<Message> cancelLike(final @PathVariable Long postId) {
return new SuccessResponse<>(message);
}

/* SCRAP */
@Operation(summary = "꿀팁공유해요 스크랩 추가", description = "꿀팁 ID에 해당하는 게시글에 스크랩을 추가합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "스크랩 추가 성공",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = SuccessResponse.class),
examples = @ExampleObject(
name = "SuccessResponse",
value = HoneyTipResponseConstant.REGISTER_SCRAP,
description = "꿀팁의 스크랩을 추가했습니다."
)))})
@PostMapping("/scrap/{postId}")
public SuccessResponse<Message> registerScrap(final @PathVariable Long postId) {
Message message = honeytipPostService.registerScrap(postId);
return new SuccessResponse<>(message);
}

@Operation(summary = "꿀팁공유해요 스크랩 취소", description = "꿀팁 ID에 해당하는 게시글에 스크랩을 취소합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "스크랩 취소 성공",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = SuccessResponse.class),
examples = @ExampleObject(
name = "SuccessResponse",
value = HoneyTipResponseConstant.CANCEL_SCRAP,
description = "꿀팁의 스크랩을 취소했습니다."
)))})
@DeleteMapping("/scrap/{postId}")
public SuccessResponse<Message> cancleScrap(final @PathVariable Long postId) {
Message message = honeytipPostService.cancelScrap(postId);
return new SuccessResponse<>(message);
}

/* READ */
@Operation(summary = "꿀팁 게시글 조회", description = "꿀팁 ID에 해당하는 게시글을 조회합니다.")
@ApiResponses(value = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.api.ttoklip.domain.common.Category;
import com.api.ttoklip.domain.common.base.BaseEntity;
import com.api.ttoklip.domain.honeytip.Scrap.domain.HoneyTipScrap;
import com.api.ttoklip.domain.honeytip.comment.domain.HoneyTipComment;
import com.api.ttoklip.domain.honeytip.image.domain.HoneyTipImage;
import com.api.ttoklip.domain.honeytip.like.domain.HoneyTipLike;
Expand Down Expand Up @@ -70,6 +71,10 @@ public class HoneyTip extends BaseEntity {
@OneToMany(mappedBy = "honeyTip", cascade = CascadeType.ALL, orphanRemoval = true)
private List<HoneyTipLike> honeyTipLikes = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "honeyTip", cascade = CascadeType.ALL, orphanRemoval = true)
private List<HoneyTipScrap> honeyTipScraps = new ArrayList<>();

public static HoneyTip of(final HoneyTipCreateReq req, final Member member) {
return HoneyTip.builder()
.title(req.getTitle())
Expand Down Expand Up @@ -108,4 +113,8 @@ private void deactivateHoneyTipImages() {
public long getLikesCount() {
return honeyTipLikes.size();
}

public long getScrapsCount() {
return honeyTipScraps.size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public class HoneyTipSingleResponse {
@Schema(description = "좋아요 개수", example = "2")
private int likeCount;

@Schema(description = "스크랩 개수", example = "3")
private int scrapCount;

@Schema(description = "댓글 개수", example = "5")
private int commentCount;

Expand All @@ -57,7 +60,10 @@ public class HoneyTipSingleResponse {
@Schema(description = "꿀팁 채팅 url 목록")
private List<UrlResponse> urlResponses;

public static HoneyTipSingleResponse of(final HoneyTip honeyTip, final List<HoneyTipComment> activeComments, final int likeCount) {
public static HoneyTipSingleResponse of(final HoneyTip honeyTip,
final List<HoneyTipComment> activeComments,
final int likeCount,
final int scrapCount) {
String formattedCreatedDate = getFormattedCreatedDate(honeyTip);
List<ImageResponse> imageResponses = getImageResponses(honeyTip);
List<CommentResponse> commentResponses = getCommentResponses(activeComments);
Expand All @@ -71,6 +77,7 @@ public static HoneyTipSingleResponse of(final HoneyTip honeyTip, final List<Hone
.writtenTime(formattedCreatedDate)
.category(honeyTip.getCategory()) // 한글 카테고리 이름으로 반환
.likeCount(likeCount)
.scrapCount(scrapCount)
.commentCount(commentResponses.size())
.imageUrls(imageResponses)
.commentResponses(commentResponses)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import com.api.ttoklip.domain.common.report.dto.ReportCreateRequest;
import com.api.ttoklip.domain.common.report.service.ReportService;
import com.api.ttoklip.domain.honeytip.Scrap.service.HoneyTipScrapService;
import com.api.ttoklip.domain.honeytip.comment.domain.HoneyTipComment;
import com.api.ttoklip.domain.honeytip.image.service.HoneyTipImageService;
import com.api.ttoklip.domain.honeytip.like.service.HoneyTipLikeService;
Expand Down Expand Up @@ -37,6 +38,7 @@ public class HoneyTipPostService {
private final HoneyTipUrlService honeyTipUrlService;
private final HoneyTipImageService honeyTipImageService;
private final HoneyTipLikeService honeyTipLikeService;
private final HoneyTipScrapService honeyTipScrapService;
private final HoneyTipCommonService honeyTipCommonService;


Expand Down Expand Up @@ -160,8 +162,10 @@ public HoneyTipSingleResponse getSinglePost(final Long postId) {
HoneyTip honeyTipWithImgAndUrl = honeytipRepository.findByIdFetchJoin(postId);
List<HoneyTipComment> activeComments = honeytipRepository.findActiveCommentsByHoneyTipId(postId);
int likeCount = honeyTipLikeService.countHoneyTipLikes(postId).intValue();
int scrapCount = honeyTipScrapService.countHoneyTipScraps(postId).intValue();

HoneyTipSingleResponse honeyTipSingleResponse = HoneyTipSingleResponse.of(honeyTipWithImgAndUrl,
activeComments, likeCount);
activeComments, likeCount, scrapCount);
return honeyTipSingleResponse;
}

Expand Down Expand Up @@ -215,4 +219,18 @@ public Message cancelLike(final Long postId) {
/* -------------------------------------------- 좋아요 추가 & 취소 끝 -------------------------------------------- */


/* -------------------------------------------- 스크랩 추가 & 취소 -------------------------------------------- */
@Transactional
public Message registerScrap(Long postId) {
honeyTipScrapService.registerScrap(postId);
return Message.scrapPostSuccess(HoneyTip.class, postId);
}

@Transactional
public Message cancelScrap(Long postId){
honeyTipScrapService.cancelScrap(postId);
return Message.scrapPostCancel(HoneyTip.class, postId);
}
/* -------------------------------------------- 스크랩 추가 & 취소 끝 -------------------------------------------- */

}
40 changes: 28 additions & 12 deletions src/main/java/com/api/ttoklip/domain/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import com.api.ttoklip.domain.common.base.BaseEntity;
import com.api.ttoklip.domain.common.comment.Comment;
import com.api.ttoklip.domain.common.report.domain.Report;
import com.api.ttoklip.domain.honeytip.Scrap.domain.HoneyTipScrap;
import com.api.ttoklip.domain.honeytip.like.domain.HoneyTipLike;
import com.api.ttoklip.domain.honeytip.post.domain.HoneyTip;
import com.api.ttoklip.domain.member.editor.MemberEditor;
import com.api.ttoklip.domain.member.editor.MemberEditor.MemberEditorBuilder;
import com.api.ttoklip.domain.newsletter.like.entity.NewsletterLike;
import com.api.ttoklip.domain.newsletter.scarp.entity.NewsletterScrap;
import com.api.ttoklip.domain.privacy.domain.Interest;
import com.api.ttoklip.domain.privacy.domain.Profile;
import com.api.ttoklip.domain.question.post.domain.Question;
Expand Down Expand Up @@ -75,6 +78,31 @@ public class Member extends BaseEntity {
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<HoneyTipLike> honeyTipLikes = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<HoneyTipScrap> honeyTipScraps = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Community> communities = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<CommunityLike> communityLikes = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<CommunityScrap> communityScraps = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<NewsletterLike> newsletterLikes = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<NewsletterScrap> newsletterScraps = new ArrayList<>();


public MemberEditorBuilder toEditor() {
return MemberEditor.builder()
.independentYear(independentYear)
Expand All @@ -88,16 +116,4 @@ public void insertPrivacy(MemberEditor memberEditor) {
independentMonth = memberEditor.getIndependentMonth();
nickname = memberEditor.getNickname();
}

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Community> communities = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<CommunityLike> communityLikes = new ArrayList<>();

@Builder.Default
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<CommunityScrap> communityScraps = new ArrayList<>();
}
Loading

0 comments on commit 6998a3b

Please sign in to comment.