Skip to content

Commit

Permalink
Merge pull request #44 from TRIP-Side-Project/feature/#35-interestart…
Browse files Browse the repository at this point in the history
…icle

게시글 좋아요 기능 구현
  • Loading branch information
kwondongwook authored Dec 13, 2023
2 parents 03a86b0 + 3ee93f6 commit 4f6f28d
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public ResponseEntity<Void> deleteArticle(@PathVariable Long articleId) {

@GetMapping("/{articleId}")
public ResponseEntity<ReadArticleResponse> readArticle(@PathVariable Long articleId) {
return ResponseEntity.ok(articleService.readArticle(articleId));
String email = SecurityContextHolder.getContext().getAuthentication().getName();
return ResponseEntity.ok(articleService.readArticle(articleId, email));
}

@GetMapping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ private static class ArticleDto {
private Long writerId;
private String writerNickname;
private String writerRole;
private long viewCount;
private long likeCount;
private LocalDateTime createdAt;

private static ArticleDto of(Article article) {
Expand All @@ -55,6 +57,8 @@ private static ArticleDto of(Article article) {
.writerId(writer.getId())
.writerNickname(writer.getNickname())
.writerRole(writer.getRole().name())
.viewCount(article.getViewCount())
.likeCount(article.getLikeCount())
.createdAt(article.getCreatedAt())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.api.trip.domain.article.controller.dto;

import com.api.trip.domain.article.model.Article;
import com.api.trip.domain.interestarticle.model.InterestArticle;
import com.api.trip.domain.member.model.Member;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -16,21 +17,27 @@ public class ReadArticleResponse {
private Long writerId;
private String writerNickname;
private String writerRole;
private String writerProfileImg;
private String content;
private long viewCount;
private long likeCount;
private LocalDateTime createdAt;
private Long interestArticleId;

public static ReadArticleResponse of(Article article) {
public static ReadArticleResponse of(Article article, InterestArticle interestArticle) {
Member writer = article.getWriter();
return builder()
.articleId(article.getId())
.title(article.getTitle())
.writerId(writer.getId())
.writerNickname(writer.getNickname())
.writerProfileImg(writer.getProfileImg())
.writerRole(writer.getRole().name())
.content(article.getContent())
.viewCount(article.getViewCount())
.viewCount(article.getViewCount() + 1)
.likeCount(article.getLikeCount())
.createdAt(article.getCreatedAt())
.interestArticleId(interestArticle != null ? interestArticle.getId() : null)
.build();
}
}
9 changes: 4 additions & 5 deletions src/main/java/com/api/trip/domain/article/model/Article.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,19 @@ public class Article extends BaseTimeEntity {

private long viewCount;

private long likeCount;

@Builder
private Article(Member writer, String title, String content, long viewCount) {
private Article(Member writer, String title, String content, long viewCount, long likeCount) {
this.writer = writer;
this.title = title;
this.content = content;
this.viewCount = viewCount;
this.likeCount = likeCount;
}

public void modify(String title, String content) {
this.title = title;
this.content = content;
}

public void increaseViewCount() {
this.viewCount++;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,25 @@
import com.api.trip.domain.article.model.Article;
import com.api.trip.domain.member.model.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface ArticleRepository extends JpaRepository<Article, Long>, ArticleRepositoryCustom {

List<Article> findAllByWriterOrderByIdDesc(Member writer);

@Modifying
@Query("UPDATE Article a SET a.viewCount = a.viewCount + 1 WHERE a = :article")
void increaseViewCount(@Param("article") Article article);

@Modifying
@Query("UPDATE Article a SET a.likeCount = a.likeCount + 1 WHERE a = :article")
void increaseLikeCount(@Param("article") Article article);

@Modifying
@Query("UPDATE Article a SET a.likeCount = a.likeCount - 1 WHERE a = :article")
void decreaseLikeCount(@Param("article") Article article);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

Expand All @@ -27,12 +28,12 @@ public ArticleRepositoryCustomImpl(EntityManager em) {
}

@Override
public Optional<Article> findArticle(Long id) {
public Optional<Article> findArticle(Long articleId) {
return Optional.ofNullable(
jpaQueryFactory
.selectFrom(article)
.innerJoin(article.writer, member).fetchJoin()
.where(article.id.eq(id))
.where(article.id.eq(articleId))
.fetchOne()
);
}
Expand All @@ -43,7 +44,7 @@ public Page<Article> findArticles(Pageable pageable, String filter) {
.selectFrom(article)
.innerJoin(article.writer, member).fetchJoin()
.where(eqFilter(filter))
.orderBy(getOrderSpecifier(pageable))
.orderBy(getOrderSpecifiers(pageable))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
Expand All @@ -67,12 +68,14 @@ private BooleanExpression eqFilter(String filter) {
return null;
}

private OrderSpecifier<?> getOrderSpecifier(Pageable pageable) {
private OrderSpecifier<?>[] getOrderSpecifiers(Pageable pageable) {
List<OrderSpecifier<?>> orderSpecifierList = new ArrayList<>();
for (Sort.Order order : pageable.getSort()) {
if ("POPULAR".equals(order.getProperty())) {
return new OrderSpecifier<>(Order.DESC, article.viewCount);
if ("popular".equals(order.getProperty())) {
orderSpecifierList.add(new OrderSpecifier<>(Order.DESC, article.likeCount));
}
}
return new OrderSpecifier<>(Order.DESC, article.id);
orderSpecifierList.add(new OrderSpecifier<>(Order.DESC, article.id));
return orderSpecifierList.toArray(OrderSpecifier[]::new);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.api.trip.domain.article.controller.dto.*;
import com.api.trip.domain.article.model.Article;
import com.api.trip.domain.article.repository.ArticleRepository;
import com.api.trip.domain.interestarticle.model.InterestArticle;
import com.api.trip.domain.interestarticle.repository.InterestArticleRepository;
import com.api.trip.domain.member.model.Member;
import com.api.trip.domain.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
Expand All @@ -12,6 +14,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

@Service
@Transactional
Expand All @@ -20,6 +23,7 @@ public class ArticleService {

private final ArticleRepository articleRepository;
private final MemberRepository memberRepository;
private final InterestArticleRepository interestArticleRepository;

public Long createArticle(CreateArticleRequest request, String email) {
Member member = memberRepository.findByEmail(email).orElseThrow();
Expand Down Expand Up @@ -51,12 +55,18 @@ public void deleteArticle(Long articleId, String email) {
articleRepository.delete(article);
}

public ReadArticleResponse readArticle(Long articleId) {
public ReadArticleResponse readArticle(Long articleId, String email) {
Article article = articleRepository.findArticle(articleId).orElseThrow();

article.increaseViewCount();
articleRepository.increaseViewCount(article);

return ReadArticleResponse.of(article);
InterestArticle interestArticle = null;
if (!Objects.equals(email, "anonymousUser")) {
Member member = memberRepository.findByEmail(email).orElseThrow();
interestArticle = interestArticleRepository.findByMemberAndArticle(member, article);
}

return ReadArticleResponse.of(article, interestArticle);
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ private static class CommentDto {
private Long commentId;
private Long writerId;
private String writerNickname;
private String writerProfileImg;
private Long articleId;
private String content;
private Long parentId;
Expand All @@ -58,6 +59,7 @@ private static CommentDto of(Comment comment) {
.commentId(comment.getId())
.writerId(comment.getWriter().getId())
.writerNickname(comment.getWriter().getNickname())
.writerProfileImg(comment.getWriter().getProfileImg())
.articleId(comment.getArticle().getId())
.content(comment.getContent())
.parentId(comment.getParent() != null ? comment.getParent().getId() : null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.api.trip.domain.interestarticle.controller;

import com.api.trip.domain.interestarticle.controller.dto.CreateInterestArticleRequest;
import com.api.trip.domain.interestarticle.service.InterestArticleService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/interest-articles")
@RequiredArgsConstructor
public class InterestArticleController {

private final InterestArticleService interestArticleService;

@PostMapping
public ResponseEntity<Long> createInterestArticle(@RequestBody CreateInterestArticleRequest request) {
String email = SecurityContextHolder.getContext().getAuthentication().getName();
return ResponseEntity.ok(interestArticleService.createInterestArticle(request, email));
}

@DeleteMapping("/{interestArticleId}")
public ResponseEntity<Void> deleteInterestArticle(@PathVariable Long interestArticleId) {
String email = SecurityContextHolder.getContext().getAuthentication().getName();
interestArticleService.deleteInterestArticle(interestArticleId, email);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.api.trip.domain.interestarticle.controller.dto;

import com.api.trip.domain.article.model.Article;
import com.api.trip.domain.interestarticle.model.InterestArticle;
import com.api.trip.domain.member.model.Member;
import lombok.Getter;

@Getter
public class CreateInterestArticleRequest {

private Long articleId;

public InterestArticle toEntity(Member member, Article article) {
return InterestArticle.builder()
.member(member)
.article(article)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.api.trip.domain.interestarticle.model;

import com.api.trip.common.auditing.entity.BaseTimeEntity;
import com.api.trip.domain.article.model.Article;
import com.api.trip.domain.member.model.Member;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"member_id", "article_id"})})
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class InterestArticle extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private Member member;

@ManyToOne(fetch = FetchType.LAZY)
private Article article;

@Builder
private InterestArticle(Member member, Article article) {
this.member = member;
this.article = article;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.api.trip.domain.interestarticle.repository;

import com.api.trip.domain.article.model.Article;
import com.api.trip.domain.interestarticle.model.InterestArticle;
import com.api.trip.domain.member.model.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface InterestArticleRepository extends JpaRepository<InterestArticle, Long> {

InterestArticle findByMemberAndArticle(Member member, Article article);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.api.trip.domain.interestarticle.service;

import com.api.trip.domain.article.model.Article;
import com.api.trip.domain.article.repository.ArticleRepository;
import com.api.trip.domain.interestarticle.controller.dto.CreateInterestArticleRequest;
import com.api.trip.domain.interestarticle.model.InterestArticle;
import com.api.trip.domain.interestarticle.repository.InterestArticleRepository;
import com.api.trip.domain.member.model.Member;
import com.api.trip.domain.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class InterestArticleService {

private final InterestArticleRepository interestArticleRepository;
private final ArticleRepository articleRepository;
private final MemberRepository memberRepository;

public Long createInterestArticle(CreateInterestArticleRequest request, String email) {
Member member = memberRepository.findByEmail(email).orElseThrow();

Article article = articleRepository.findById(request.getArticleId()).orElseThrow();

InterestArticle interestArticle = interestArticleRepository.findByMemberAndArticle(member, article);
if (interestArticle != null) {
throw new RuntimeException("잘못된 요청입니다.");
}

interestArticle = request.toEntity(member, article);

articleRepository.increaseLikeCount(interestArticle.getArticle());

return interestArticleRepository.save(interestArticle).getId();
}

public void deleteInterestArticle(Long interestArticleId, String email) {
Member member = memberRepository.findByEmail(email).orElseThrow();

InterestArticle interestArticle = interestArticleRepository.findById(interestArticleId).orElseThrow();
if (interestArticle.getMember() != member) {
throw new RuntimeException("삭제 권한이 없습니다.");
}

articleRepository.decreaseLikeCount(interestArticle.getArticle());

interestArticleRepository.delete(interestArticle);
}
}

0 comments on commit 4f6f28d

Please sign in to comment.