Skip to content

Commit

Permalink
Merge pull request #110 from venus-lion/be/feat/#4-new-bookdetail
Browse files Browse the repository at this point in the history
feat[be] #4 - book 상세 : 관련 도서 추가
  • Loading branch information
G1Huh authored Jan 8, 2025
2 parents fab4143 + 9fbe1b7 commit f55990b
Show file tree
Hide file tree
Showing 21 changed files with 305 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package movlit.be.book.application.service;

import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import movlit.be.book.domain.Book;
import movlit.be.book.domain.BookComment;
Expand All @@ -8,33 +14,45 @@
import movlit.be.book.presentation.dto.BookCommentResponseDto;
import movlit.be.book.domain.repository.BookCommentLikeRepository;
import movlit.be.book.domain.repository.BookCommentRepository;
import movlit.be.bookES.BookES;
import movlit.be.bookES.BookESConvertor;
import movlit.be.bookES.BookESDomain;
import movlit.be.bookES.BookESRepository;
import movlit.be.common.util.ids.BookCommentId;
import movlit.be.common.util.ids.BookId;
import movlit.be.common.util.ids.MemberId;
import movlit.be.member.domain.Member;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class BookCommentReadService {
public static final int PAGE_SIZE = 10;

public static final int PAGE_SIZE = 10;

private final BookCommentRepository bookCommentRepository;
private final BookCommentLikeRepository bookCommentLikeRepository;
private final BookCommentLikeCountRepository bookCommentLikeCountRepository;



// 리뷰 리스트
public Slice<BookCommentResponseDto> getPagedBookComments(BookId bookId, MemberId memberId, Pageable pageable) {
Slice<BookCommentResponseDto> bookCommentPage = null;
if(memberId == null)
if (memberId == null) {
bookCommentPage = bookCommentRepository.findByBookId(bookId, pageable);
else
} else {
bookCommentPage = bookCommentRepository.findByBookIdAndMemberId(bookId, memberId, pageable);
}

System.out.println("$$$$$$$ 리뷰리스트 : " );
System.out.println("$$$$$$$ 리뷰리스트 : ");
for (BookCommentResponseDto comment : bookCommentPage.getContent()) {
// 각 BookCommentResponseDto의 내용 출력
System.out.println("Comment TEXT: " + comment.getComment());
Expand All @@ -56,18 +74,20 @@ public BookComment findByBookCommentId(BookCommentId bookCommentId) {
return bookCommentRepository.findByBookCommentId(bookCommentId);
}


// 댓글 좋아요 갯수
public int countLikesByCommentId(BookCommentId bookCommentId){
public int countLikesByCommentId(BookCommentId bookCommentId) {
return bookCommentLikeCountRepository.countLikeByCommentId(bookCommentId);
}

// 해당 댓글 나의 좋아요 여부
public boolean isLikedByComment(BookComment bookComment, Member member){
if(bookCommentLikeRepository.findByBookCommentAndMember(bookComment, member) != null)
public boolean isLikedByComment(BookComment bookComment, Member member) {
if (bookCommentLikeRepository.findByBookCommentAndMember(bookComment, member) != null) {
return true;
else
} else {
return false;
}
}



}
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
package movlit.be.book.application.service;

import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import movlit.be.book.domain.Book;
import movlit.be.book.domain.BookCommentLike;
import movlit.be.book.domain.repository.BookCommentLikeRepository;
import movlit.be.book.domain.repository.BookCommentRepository;
import movlit.be.book.presentation.dto.BookDetailResponseDto;
import movlit.be.book.domain.Bookcrew;
import movlit.be.book.domain.repository.BookHeartCountRepository;
import movlit.be.book.domain.repository.BookHeartRepository;
import movlit.be.book.domain.repository.BookRepository;
import movlit.be.book.domain.repository.BookcrewRepository;
import movlit.be.bookES.BookES;
import movlit.be.bookES.BookESConvertor;
import movlit.be.bookES.BookESDomain;
import movlit.be.bookES.BookESRepository;
import movlit.be.common.util.ids.BookId;
import movlit.be.member.domain.Member;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -19,12 +37,19 @@ public class BookDetailReadService {

private final BookRepository bookRepository;
private final BookcrewRepository bookcrewRepository;
private final BookCommentRepository bookCommentRepository;
private final BookHeartRepository bookHeartRepository;
private final BookHeartCountRepository bookHeartCountRepository;

// Elasticsearch - 관련 도서 추천
private final BookESRepository bookESRepository;
private final ElasticsearchOperations elasticsearchOperations;

// 도서 상세 정보
public BookDetailResponseDto getBookDetail(BookId bookId, Member member) {
Book book = findByBookId(bookId);
// 평점 - 소수 둘째자리까지
double averageScore = Math.round(getAverageScoreByBookId(bookId)*100)/100.0;
int heartCount = countHeartsByBookId(bookId);
boolean isHearted = false;
if(member != null)
Expand All @@ -41,6 +66,7 @@ public BookDetailResponseDto getBookDetail(BookId bookId, Member member) {
.bookImgUrl(book.getBookImgUrl())
.stockStatus(book.getStockStatus())
.mallUrl(book.getMallUrl())
.averageScore(BigDecimal.valueOf(averageScore))
.heartCount(heartCount)
.isHearted(isHearted)
.bookcrewList(bookcrewList)
Expand All @@ -58,6 +84,11 @@ public List<Bookcrew> findBookcrewByBook(Book book) {
return bookcrewRepository.findByBook(book);
}

// 해당 책의 평점
public double getAverageScoreByBookId(BookId bookId){
return bookCommentRepository.getAverageScoreByBookId(bookId);
}

// 찜 갯수
public int countHeartsByBookId(BookId bookId) {
return bookHeartCountRepository.countHeartByBookId(bookId);
Expand All @@ -71,5 +102,88 @@ public boolean isHeartedByBook(Book book, Member member){
return false;
}

// 관련 도서 추천
public List<BookESDomain> getRecommendedBooks(BookId bookId) {
Pageable pageable = PageRequest.of(0, 10);
BookES bookES = bookESRepository.findById(bookId.getValue()).orElse(null);
System.out.println("## bookId : " + bookId.getValue() + "\n## bookES : " + bookES);
if (bookES != null) {
String category = bookES.getCategoryName();
String titleKeyword = bookES.getTitleKeyword();
String description = bookES.getDescription();
System.out.println("%% 해당 책의 category : " + category + "\n%% 해당 책의 titleKeyword : " + titleKeyword);

// 3. elasticsearch 쿼리
BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

// 첫 번째 MatchQuery: description에 대한 쿼리와 boost 3.0 설정
Query titleKeywordMatchQueryForShould = MatchQuery.of(t -> t
.field("titleKeyword")
.query(titleKeyword)
// .boost(2.0f) // boost 값 추가
.fuzziness("AUTO")
)._toQuery();

// 두 번째 MatchQuery: categoryName에 대한 쿼리와 boost 1.5 설정
Query categoryNameMatchQuery = MatchQuery.of(t -> t
.field("categoryName")
.query(category)
.boost(1.5f) // boost 값 추가
)._toQuery();

Query DescriptionMatchQueryForShould = MatchQuery.of(t -> t
.field("description")
.query(description)
.boost(1.5f) // boost 값 추가
)._toQuery();

// BoolQuery에 MatchQuery 추가
boolQueryBuilder
.should(titleKeywordMatchQueryForShould)
.should(categoryNameMatchQuery)
.should(DescriptionMatchQueryForShould)
.minimumShouldMatch("1");

// BoolQuery 빌드
BoolQuery boolQuery = boolQueryBuilder.build();

// NativeSearchQuery 빌드 -- 최종 쿼리
NativeQuery nativeQuery = new NativeQueryBuilder()
.withQuery(boolQuery._toQuery())
.withPageable(pageable)
// .withSort(Sort.by(
// Sort.Order.desc("_score")
//// Sort.Order.desc("voteAverage") // score순, 평점 순
// ))
.build();

// 검색 실행
SearchHits<BookES> searchHits = elasticsearchOperations.search(nativeQuery, BookES.class);

// 값이 없을경우
if (!searchHits.hasSearchHits()) {
System.out.println("사용자 취향에 맞는 도서가 없습니다.");
return null; // 사용자 취향 리스트가 없음
}

List<BookES> bookESListForReturn = searchHits.getSearchHits().stream()
.map(hit -> hit.getContent())
.collect(Collectors.toList());

System.out.println("bookESListForReturn ::: " + bookESListForReturn);

List<BookESDomain> bookESDomainList = bookESListForReturn.stream()
.map(resultbookES -> BookESConvertor.documentToDomain(resultbookES))
.collect(Collectors.toList());

return bookESDomainList;

} else {
return null;
}


}


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

import movlit.be.book.domain.BookComment;
import movlit.be.book.domain.BookCommentLike;
import movlit.be.common.util.ids.BookId;
import movlit.be.member.domain.Member;

public interface BookCommentLikeRepository {
Expand All @@ -14,4 +15,6 @@ public interface BookCommentLikeRepository {
void delete(BookCommentLike bookCommentLike);




}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public interface BookCommentRepository {

void deleteById(BookCommentId bookCommentId);

double getAverageScoreByBookId(BookId bookId);


Slice<BookCommentResponseDto> findByBookId(BookId bookId, Pageable pageable);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import movlit.be.book.domain.repository.BookCommentLikeRepository;
import movlit.be.book.infra.persistence.jpa.BookCommentLikeJpaRepository;
import movlit.be.common.exception.BookCommentNotFoundException;
import movlit.be.common.util.ids.BookId;
import movlit.be.member.application.converter.MemberConverter;
import movlit.be.member.domain.Member;
import org.springframework.stereotype.Repository;
Expand Down Expand Up @@ -40,4 +41,6 @@ public void delete(BookCommentLike bookCommentLike) {
bookCommentLikeJpaRepository.delete(BookCommentLikeConverter.toEntity(bookCommentLike));
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public void deleteById(BookCommentId bookCommentId) {
bookCommentJpaRepository.deleteById(bookCommentId);
}

@Override
public double getAverageScoreByBookId(BookId bookId) {
return bookCommentJpaRepository.getAverageScoreByBookId(bookId).orElse(0.0);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,7 @@ Slice<BookCommentResponseDto> getCommentsByBookIdAndMemberId(@Param("bookId") Bo
Optional<BookCommentEntity> findByMemberEntityAndBookEntity(MemberEntity memberEntity, BookEntity bookEntity);

void deleteById(BookCommentId bookCommentId);

@Query("SELECT AVG(bc.score) FROM BookCommentEntity bc where bc.bookEntity.bookId= :bookId")
Optional<Double> getAverageScoreByBookId(@Param("bookId") BookId bookId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
import movlit.be.book.domain.entity.BookCommentEntity;
import movlit.be.book.domain.entity.BookCommentLikeEntity;
import movlit.be.common.util.ids.BookCommentLikeId;
import movlit.be.common.util.ids.BookId;
import movlit.be.member.domain.Member;
import movlit.be.member.domain.entity.MemberEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface BookCommentLikeJpaRepository extends JpaRepository<BookCommentLikeEntity, Long> {

Optional<BookCommentLikeEntity> findByBookCommentEntityAndMemberEntity(BookCommentEntity bookCommentEntity, MemberEntity memberEntity);



}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public ResponseEntity myComment(@PathVariable BookId bookId, @AuthenticationPrin
Book book = bookDetailReadService.findByBookId(bookId);

BookComment myComment = bookCommentReadService.findByMemberAndBook(member, book);
if(myComment == null)
return ResponseEntity.badRequest().build();
BookCommentResponseDto myCommentRes = BookCommentResponseDto.builder()
.bookCommentId(myComment.getBookCommentId())
.score(myComment.getScore())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package movlit.be.book.presentation;

import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import movlit.be.auth.application.service.MyMemberDetails;
import movlit.be.book.application.service.BookDetailReadService;
import movlit.be.book.application.service.BookDetailWriteService;
import movlit.be.book.domain.Book;
import movlit.be.book.domain.BookHeart;
import movlit.be.book.presentation.dto.BookCommentResponseDto;
import movlit.be.book.presentation.dto.BookDetailResponseDto;
import movlit.be.book.presentation.dto.BooksResponse;
import movlit.be.book.presentation.dto.BooksResponse.BookItemDto;
import movlit.be.bookES.BookESDomain;
import movlit.be.common.util.ids.BookId;
import movlit.be.common.util.ids.MemberId;
import movlit.be.member.application.service.MemberReadService;
Expand All @@ -19,6 +24,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand Down Expand Up @@ -88,4 +94,19 @@ public ResponseEntity removeHearts(@PathVariable BookId bookId, @AuthenticationP

}

@GetMapping("{bookId}/recommendedBooks")
public ResponseEntity<List<BookESDomain>> getRecommendedBooks(@PathVariable BookId bookId) {
List<BookESDomain> recommendedBookList = bookDetailReadService.getRecommendedBooks(bookId);
for (BookESDomain recBook : recommendedBookList) {
// 각 BookCommentResponseDto의 내용 출력
System.out.println("@@ 추천 책 제목 : " + recBook.getTitle());
System.out.println("@@ 추천 책 카테고리: " + recBook.getCategoryName());
System.out.println("@@ 추천 책 설명 : " + recBook.getDescription());
// 필요에 따라 추가 속성을 출력할 수 있습니다.
}
return ResponseEntity.ok(recommendedBookList);
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class BookDetailResponseDto {
private String stockStatus;
@JsonProperty("mall_url")
private String mallUrl;
@JsonProperty("average_score")
private BigDecimal averageScore;
@JsonProperty("heart_count")
private int heartCount;
@JsonProperty("is_hearted")
Expand Down
Loading

0 comments on commit f55990b

Please sign in to comment.