Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pr Feat/12 뉴스레터 좋아요, 스크랩 기능 구현 #66

Merged
merged 22 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
fd93d2c
docs: 우리동네 스웨거 Tag 분리 (#63)
why-only-english Feb 14, 2024
7249175
feat: 뉴스레터 스크랩 엔티티 추가 (#63)
why-only-english Feb 14, 2024
4fe61e6
docs: 뉴스레터 스크랩 추가 및 취소 명세서 (#62)
why-only-english Feb 14, 2024
a687c5f
fix: 뉴스레터 스크랩 로직 추가 & Member와 연관관계 매핑 (#63)
why-only-english Feb 14, 2024
b7bdaba
feat: 뉴스레터 스크랩 생성 및 삭제 기능 추가 (#63)
why-only-english Feb 14, 2024
e75acc1
feat: 뉴스레터 스크랩 개수 카운트 (#63)
why-only-english Feb 14, 2024
a708c82
fix: 스크랩 수 계산 메서드 수정 (#63)
why-only-english Feb 14, 2024
abfe269
fix: 순환참조 문제 해결 (#63)
why-only-english Feb 14, 2024
b190416
fix: 단건 조회 가능하게 수정 (#63)
why-only-english Feb 14, 2024
d812029
feat: 뉴스레터 좋아요 엔티티 추가 (#63)
why-only-english Feb 14, 2024
a93ad32
fix: 뉴스레터 좋아요 로직 추가 & Member와 연관관계 매핑 (#63)
why-only-english Feb 14, 2024
9000552
feat: 뉴스레터 좋아요 생성 및 삭제 기능 추가 (#63)
why-only-english Feb 14, 2024
fb35fb9
feat: 뉴스레터 좋아요 개수 카운트 (#63)
why-only-english Feb 14, 2024
8d1edff
feat: 뉴스레터 좋아요 개수 조회 쿼리 (#63)
why-only-english Feb 14, 2024
0d6f7f5
fix: NewsletterId 조회 로직 수정 (#63)
why-only-english Feb 15, 2024
4975376
fix: 테스트한 코드 삭제 (#63)
why-only-english Feb 15, 2024
1ff233b
feat: 꿀팁공유해요 스크랩 엔티티 추가 (#63)
why-only-english Feb 15, 2024
a856f9a
fix: 꿀팁공유해요 스크랩 로직 추가 & Member와 연관관계 매핑 (#63)
why-only-english Feb 15, 2024
1852ac0
feat: 꿀팁공유해요 스크랩 생성 및 삭제 기능 추가 (#63)
why-only-english Feb 15, 2024
29851d6
feat: 꿀팁공유해요 스크랩 개수 카운트 (#63)
why-only-english Feb 15, 2024
3f69431
docs: 꿀팁공유해요 스크랩 명세서 추가 (#63)
why-only-english Feb 15, 2024
3b78fcf
fix: 스크랩 url 변경 (#63)
why-only-english Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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