Skip to content

Commit

Permalink
Merge pull request #39 from jwpark1211/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jwpark1211 authored Jun 15, 2024
2 parents c75c98d + a8ef79c commit 49ee170
Show file tree
Hide file tree
Showing 15 changed files with 311 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/main/java/capstone/bookitty/common/RedisConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class RedisConfig {
@Value("${spring.redis.timeout:60000}")
private long timeout;

@Value("${spring.redis.cache.ttl:1800}")
@Value("${spring.redis.cache.ttl:10800}") //TTL = 3시간
private long cacheTtl;

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ public ResponseEntity<? extends BasicResponse> getStateById(
bookStateService.findStateByStateId(stateId)));
}

@Operation(summary = "isbn과 memberId로 책 상태 가져오기")
@GetMapping(path = "/isbn/{isbn}/member/{member-id}")
public ResponseEntity<? extends BasicResponse> getStateByMemberIdAndIsbn(
@PathVariable("member-id") Long memberId,
@PathVariable("isbn") String isbn
){
return ResponseEntity.ok()
.body(new ResponseCounter<StateInfoResponse>(
bookStateService.findStateByMemberAndIsbn(isbn,memberId)));
}

@Operation(summary = "memberId로 책 상태 리스트 가져오기 / page는 requestParam으로 요청할 수 있습니다. / "+
"size(한 페이지 당 element 수, default = 10), page(요청하는 페이지, 0부터 시작)")
@GetMapping(path = "/member/{member-id}")
Expand Down Expand Up @@ -90,6 +101,7 @@ public ResponseEntity<? extends BasicResponse> updateState(
bookStateService.updateState(stateId,request)));
}


@Operation(summary = "state 삭제")
@DeleteMapping(path = "/{state-id}")
public ResponseEntity<? extends BasicResponse> deleteState(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package capstone.bookitty.domain.controller;

import capstone.bookitty.domain.service.OpenApiBookService;
import capstone.bookitty.global.api.dto.AladinBestSellerResponseDTO;
import capstone.bookitty.global.api.dto.AladinBookListResponseDTO;
import capstone.bookitty.global.api.dto.AladinBookSearchResponseDTO;
import capstone.bookitty.global.api.dto.NaruPopularBookListDto;
import capstone.bookitty.global.api.dto.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
Expand All @@ -14,6 +11,8 @@
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import java.util.List;

@Tag(name = "openApi(book)", description = "알라딘 api 입니다.")
@RestController
@RequiredArgsConstructor
Expand Down Expand Up @@ -66,11 +65,18 @@ public Mono<AladinBestSellerResponseDTO> getBestSellerBlogChoice(){
return openApiBookService.getBlogChoice();
}

@Operation(summary = "사용자별 도서 추천 Top 10")
@GetMapping(path = "/recommend/members/{member-id}")
public NaruPopularBookListDto getRecommendationByAgeAndGender(
//@Operation(summary = "사용자별 도서 추천 Top 10")
//@GetMapping(path = "/recommend/members/{member-id}")
/*public NaruPopularBookListDto getRecommendationByAgeAndGender(
@PathVariable("member-id") Long memberId
){
return openApiBookService.getGenderAndAgeRecommendation(memberId);
}*/

@Operation(summary = "사용자별 도서 추천 Top 10")
@GetMapping(path = "/recommend/members/{member-id}")
public NaruPopularBookListDto getRecommendations(
@PathVariable("member-id") long memberId) {
return openApiBookService.getTop10ForMember(memberId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ public ResponseEntity<? extends BasicResponse> getStarByMemberId(
starService.findStarByMemberId(memberId,pageable)));
}

@Operation(summary = "member id와 isbn으로 평점 가져오기")
@GetMapping(path = "/member/{member-id}/isbn/{isbn}")
public ResponseEntity<? extends BasicResponse> getStarByIsbnAndMemberId(
@PathVariable("isbn") String isbn,
@PathVariable("member-id") Long memberId
){
return ResponseEntity.ok()
.body(new ResponseCounter<StarInfoResponse>(
starService.findStarByMemberIdAndIsbn(memberId,isbn)));
}

@Operation(summary = "평점 수정")
@PatchMapping(path="/{star-id}")
public ResponseEntity<? extends BasicResponse> updateStar(
Expand Down
7 changes: 2 additions & 5 deletions src/main/java/capstone/bookitty/domain/entity/Gender.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

@RequiredArgsConstructor
public enum Gender {
MALE("남성"),
FEMALE("여성")
;

private final String gender;
MALE, FEMALE
}

Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package capstone.bookitty.domain.repository;

import capstone.bookitty.domain.entity.BookState;
import capstone.bookitty.domain.entity.Gender;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import javax.swing.text.html.Option;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

Expand All @@ -17,5 +19,12 @@ public interface BookStateRepository extends JpaRepository<BookState,Long> {
List<BookState> findByMemberId(Long memberId);
boolean existsByMemberIdAndIsbn(Long memberId, String isbn);
Optional<BookState> findByMemberIdAndIsbn(Long memberId, String isbn);
@Query("SELECT b.isbn, COUNT(b) as stateCount " +
"FROM BookState b " +
"WHERE b.member.gender = :gender AND b.member.birthDate BETWEEN :startDate AND :endDate " +
"GROUP BY b.isbn")
List<Object[]> findStateCountByGenderAndBirthDate(@Param("gender") Gender gender,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
package capstone.bookitty.domain.repository;

import capstone.bookitty.domain.entity.BookState;
import capstone.bookitty.domain.entity.Gender;
import capstone.bookitty.domain.entity.Star;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

public interface StarRepository extends JpaRepository<Star, Long> {
boolean existsByMemberIdAndIsbn(Long memberId, String isbn);
Page<Star> findByIsbn(String isbn, Pageable pageable);
Page<Star> findByMemberId(Long memberId, Pageable pageable);
Optional<Star> findByMemberIdAndIsbn(Long memberId, String isbn);
@Query("SELECT s.isbn, SUM(s.score) as totalScore " +
"FROM Star s " +
"WHERE s.member.gender = :gender AND s.member.birthDate BETWEEN :startDate AND :endDate " +
"GROUP BY s.isbn")
List<Object[]> findTotalScoreByGenderAndBirthDate(@Param("gender") Gender gender,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ public Page<StateInfoResponse> findStateByISBN(String isbn, Pageable pageable) {
.map(StateInfoResponse::of);
}

public StateInfoResponse findStateByMemberAndIsbn(String isbn, Long memberId){
BookState state = stateRepository.findByMemberIdAndIsbn(memberId,isbn)
.orElseThrow(()-> new EntityNotFoundException(
"BookState with memberID:"+memberId+",Isbn:"+isbn+"not found."));
return StateInfoResponse.of(state);
}

public StateInfoResponse findStateByStateId(Long stateId) {
return stateRepository.findById(stateId)
.map(StateInfoResponse::of)
Expand Down
113 changes: 102 additions & 11 deletions src/main/java/capstone/bookitty/domain/service/OpenApiBookService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

import capstone.bookitty.domain.entity.Gender;
import capstone.bookitty.domain.entity.Member;
import capstone.bookitty.domain.repository.BookStateRepository;
import capstone.bookitty.domain.repository.MemberRepository;
import capstone.bookitty.global.api.dto.AladinBestSellerResponseDTO;
import capstone.bookitty.global.api.dto.AladinBookListResponseDTO;
import capstone.bookitty.global.api.dto.AladinBookSearchResponseDTO;
import capstone.bookitty.global.api.dto.NaruPopularBookListDto;
import capstone.bookitty.domain.repository.StarRepository;
import capstone.bookitty.global.api.dto.*;
import capstone.bookitty.global.api.openApi.AladinOpenApi;
import capstone.bookitty.global.api.openApi.NaruOpenApi;
import jakarta.persistence.EntityNotFoundException;
Expand All @@ -17,6 +16,10 @@

import java.time.LocalDate;
import java.time.Period;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
Expand All @@ -25,6 +28,8 @@ public class OpenApiBookService {
private final AladinOpenApi aladinOpenApi;
private final NaruOpenApi naruOpenApi;
private final MemberRepository memberRepository;
private final StarRepository starRepository;
private final BookStateRepository bookStateRepository;

public Mono<AladinBookListResponseDTO> searchByBookISBN(String isbn) {
return aladinOpenApi.searchByBookISBN(isbn);
Expand Down Expand Up @@ -54,15 +59,101 @@ public Mono<AladinBestSellerResponseDTO> getBlogChoice() {
return aladinOpenApi.getBlogChoice();
}

@Cacheable(value = "genderAndAge", key = "#a0",unless="#result == null")
public NaruPopularBookListDto getGenderAndAgeRecommendation(Long memberId) {
@Cacheable(value = "bookRecommendations", key = "#a0",unless="#result == null")
public NaruPopularBookListDto getTop10ForMember(long memberId) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new EntityNotFoundException("Member not found for ID: " + memberId));
.orElseThrow(() -> new IllegalArgumentException("Invalid member ID"));

int gender = (member.getGender() == Gender.MALE) ? 1 : 2;
int age = Period.between(member.getBirthDate(), LocalDate.now()).getYears();
age = (age / 10) * 10;
Gender gender = member.getGender();
LocalDate birthDate = member.getBirthDate();
int age = Period.between(birthDate, LocalDate.now()).getYears();
String ageGroup = determineAgeGroup(age);

return naruOpenApi.getPopularBook(gender, age);
List<String> top10IsbnList = getTop10IsbnForMember(gender, ageGroup);
List<NaruPopularBookDto> bookInfoList = top10IsbnList.stream()
.map(this::searchByBookISBN)
.map(mono -> mono.block()) // Mono를 block하여 동기적으로 결과를 가져오기
.map(this::convertToNaruPopularBookDto)
.collect(Collectors.toList());

// 각 책의 랭킹을 설정
for (int i = 0; i < bookInfoList.size(); i++) {
bookInfoList.get(i).getDoc().setRanking(i + 1);
}

NaruPopularBookListDto responseWrapper = new NaruPopularBookListDto();
NaruPopularBookListDto.PopularBookListResponse response = new NaruPopularBookListDto.PopularBookListResponse();
response.setResultNum(bookInfoList.size());
response.setDocs(bookInfoList);
responseWrapper.setResponse(response);

return responseWrapper;
}

private String determineAgeGroup(int age) {
if (age <= 20) {
return "TEEN";
} else if (age <= 40) {
return "ADULT";
} else {
return "MIDDLE_AGED";
}
}

private NaruPopularBookDto convertToNaruPopularBookDto(AladinBookListResponseDTO aladinBook) {
NaruPopularBookDto dto = new NaruPopularBookDto();
NaruPopularBookDto.PopularBook popularBook = new NaruPopularBookDto.PopularBook();

AladinBookListResponseDTO.DetailBook detailBook = aladinBook.getItem().get(0); // 첫 번째 책 정보 사용
popularBook.setBookname(detailBook.getTitle());
popularBook.setAuthors(detailBook.getAuthor());
popularBook.setPublisher(detailBook.getPublisher());
popularBook.setIsbn13(detailBook.getIsbn13());
popularBook.setBookImageURL(detailBook.getCover());

dto.setDoc(popularBook);
return dto;
}

private List<String> getTop10IsbnForMember(Gender gender, String ageGroup) {
LocalDate endDate = LocalDate.now();
LocalDate startDate;

switch (ageGroup) {
case "TEEN":
startDate = endDate.minusYears(20);
break;
case "ADULT":
startDate = endDate.minusYears(40);
break;
case "MIDDLE_AGED":
startDate = endDate.minusYears(60);
break;
default:
throw new IllegalArgumentException("Invalid age group");
}

List<Object[]> totalScores = starRepository.findTotalScoreByGenderAndBirthDate(gender, startDate, endDate);
List<Object[]> stateCounts = bookStateRepository.findStateCountByGenderAndBirthDate(gender, startDate, endDate);

Map<String, Double> isbnScores = new HashMap<>();

for (Object[] scoreData : totalScores) {
String isbn = (String) scoreData[0];
Double score = (Double) scoreData[1];
isbnScores.put(isbn, score);
}

for (Object[] stateData : stateCounts) {
String isbn = (String) stateData[0];
Long count = (Long) stateData[1];
isbnScores.merge(isbn, count * 5.0, Double::sum);
}

return isbnScores.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.limit(10)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,11 @@ public Page<StarInfoResponse> findAllStar(Pageable pageable) {
return starRepository.findAll(pageable)
.map(StarInfoResponse::of);
}

public StarInfoResponse findStarByMemberIdAndIsbn(Long memberId, String isbn) {
Star star = starRepository.findByMemberIdAndIsbn(memberId,isbn)
.orElseThrow(()-> new EntityNotFoundException(
"Star with memberID:"+memberId+",Isbn:"+isbn+"not found."));
return StarInfoResponse.of(star);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package capstone.bookitty.global.api.dto;

import lombok.Getter;

import java.util.List;

@Getter
public class AladinBookListResponseDTO {

public List<DetailBook> item;

@Getter
public static class DetailBook {
public String title;
public String link;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
public class NaruPopularBookDto {
public PopularBook doc;

@Getter
@Setter
public static class PopularBook {
public String bookname;
public String authors;
public String publisher;
public int ranking;
public String isbn13;
public String bookImageURL;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

import java.util.List;

@Getter @Setter
@Getter
@Setter
public class NaruPopularBookListDto {
public PopularBookListResponse response;

@Getter
@Setter
public static class PopularBookListResponse {
public int resultNum;
public List<NaruPopularBookDto> docs;
Expand Down
Loading

0 comments on commit 49ee170

Please sign in to comment.