Skip to content

Commit

Permalink
Merge pull request #95 from 2023-Team-Joon-CheckIt/BE/feat/#91
Browse files Browse the repository at this point in the history
[feat/#91] 좋아요 기능 API 구현
  • Loading branch information
fnzl54 authored Sep 20, 2023
2 parents faae291 + abefc4e commit 940e620
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@
import com.techeer.checkIt.domain.book.dto.Response.BookRes;
import com.techeer.checkIt.domain.book.dto.Response.BookSearchRes;
import com.techeer.checkIt.domain.book.service.BookService;
import com.techeer.checkIt.domain.user.entity.User;
import com.techeer.checkIt.domain.user.entity.UserDetail;
import com.techeer.checkIt.domain.user.service.UserService;
import com.techeer.checkIt.global.result.ResultResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import static com.techeer.checkIt.global.result.ResultCode.GET_NEW_BOOK_SUCCESS;
import static com.techeer.checkIt.global.result.ResultCode.UPDATE_BOOK_LIKE_SUCCESS;
import static com.techeer.checkIt.global.result.ResultCode.GET_ONE_BOOK_SUCCESS;

@Api(tags = "책 API")
@RequestMapping("/api/v1/books")
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@RestController
public class BookController {
private final BookService bookService;
private final UserService userService;

@ApiOperation(value = "책 검색 API")
@GetMapping("/search")
Expand All @@ -32,9 +39,13 @@ public ResponseEntity<List<BookSearchRes>> searchTitle(@RequestParam(defaultValu

@ApiOperation(value = "책 한 권 조회 API")
@GetMapping("{bookId}")
public ResponseEntity<BookRes> getBookById(@PathVariable Long bookId){
BookRes bookResponse = bookService.findBookById(bookId);
return ResponseEntity.ok(bookResponse);
public ResponseEntity<ResultResponse> getBookById(
@AuthenticationPrincipal UserDetail userDetail,
@PathVariable Long bookId
){
User user = userService.findUserByUsername(userDetail.getUsername());
BookRes bookResponse = bookService.findBookById(user.getId(), bookId);
return ResponseEntity.ok(ResultResponse.of(GET_ONE_BOOK_SUCCESS, bookResponse));
}

@ApiOperation(value = "신규 도서 조회 API")
Expand All @@ -44,4 +55,16 @@ public ResponseEntity<ResultResponse> getNewBooksList() {
return ResponseEntity.ok(ResultResponse.of(GET_NEW_BOOK_SUCCESS, books));
}

@ApiOperation(value = "책 좋아요 API")
@GetMapping("/like/{bookId}")
public ResponseEntity<ResultResponse> updateLikeById(
@AuthenticationPrincipal UserDetail userDetail,
@PathVariable Long bookId
){
User user = userService.findUserByUsername(userDetail.getUsername());
bookService.updateLike(user.getId(), bookId);
BookRes bookResponse = bookService.findBookById(user.getId(), bookId);
return ResponseEntity.ok(ResultResponse.of(UPDATE_BOOK_LIKE_SUCCESS ,bookResponse));
}

}
40 changes: 40 additions & 0 deletions src/main/java/com/techeer/checkIt/domain/book/dao/RedisDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.techeer.checkIt.domain.book.dao;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class RedisDao {
private final RedisTemplate<String, String> redisLikeTemplate;

public String setValues(String key, String data) {
ValueOperations<String, String> values = redisLikeTemplate.opsForValue();
values.set(key, data);
return "0";
}

public void setValuesList(String key, String data) {
redisLikeTemplate.opsForList().rightPushAll(key,data);
}

public void deleteValueList(String key, String data) {
redisLikeTemplate.opsForList().remove(key, 0, data);
}

public List<String> getValuesList(String key) {
Long len = redisLikeTemplate.opsForList().size(key);
return len == 0 ? new ArrayList<>() : redisLikeTemplate.opsForList().range(key, 0, len-1);
}

public String getValues(String key) {
ValueOperations<String, String> values = redisLikeTemplate.opsForValue();
return values.get(key);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ public class BookRes {
private int height;
private int width;
private int pages;
private int likes;
private boolean likeStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@

@Component
public class BookMapper {
public BookRes toDto(Book book) {
public BookRes toDto(Book book, int likes, boolean likeStatus) {
return BookRes.builder()
.id(book.getId())
.title(book.getTitle())
.author(book.getAuthor())
.publisher(book.getPublisher())
.coverImageUrl(book.getCoverImageUrl())
.height(book.getHeight())
.width(book.getWidth())
.pages(book.getPages())
.build();
.id(book.getId())
.title(book.getTitle())
.author(book.getAuthor())
.publisher(book.getPublisher())
.coverImageUrl(book.getCoverImageUrl())
.height(book.getHeight())
.width(book.getWidth())
.pages(book.getPages())
.likes(likes)
.likeStatus(likeStatus)
.build();
}

public BookSearchRes toBookSearchDto(BookDocument book) {
return BookSearchRes.builder()
.title(book.getTitle())
Expand All @@ -41,7 +42,6 @@ public List<BookSearchRes> toDtoList(List<BookDocument> books){
.map(this::toBookSearchDto)
.collect(Collectors.toList());
}

public Page<BookSearchRes> toPageDtoList(Page<BookDocument> books) {
return new PageImpl<>(books.stream()
.map(this::toBookSearchDto)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.techeer.checkIt.domain.book.service;

import com.techeer.checkIt.domain.book.dao.RedisDao;
import com.techeer.checkIt.domain.book.dto.Response.BookRes;
import com.techeer.checkIt.domain.book.dto.Response.BookSearchRes;
import com.techeer.checkIt.domain.book.entity.Book;
Expand All @@ -25,6 +26,7 @@ public class BookService {
private final BookJpaRepository bookJpaRepository;
private final BookSearchRepository bookSearchRepository;
private final BookMapper bookMapper;
private final RedisDao redisDao;

public List<BookSearchRes> findBookByTitle(String title) {
List<BookDocument> books = bookSearchRepository.findByTitleContaining(title);
Expand All @@ -38,12 +40,35 @@ public Page<BookSearchRes> sortedBooksByTime() {
}

// id별 조회할 때
public BookRes findBookById(Long id) {
Book book = bookJpaRepository.findByBookId(id).orElseThrow(BookNotFoundException::new);
return bookMapper.toDto(book);
public BookRes findBookById(Long userId, Long bookId) {
Book book = bookJpaRepository.findByBookId(bookId).orElseThrow(BookNotFoundException::new);
String redisKey = "B" + bookId.toString();
String redisUserKey = "U" + userId.toString();
String values = (redisDao.getValues(redisKey) == null) ? redisDao.setValues(redisKey,"0") : redisDao.getValues(redisKey);
int likes = Integer.parseInt(values);
boolean likeStatus = !redisDao.getValuesList(redisUserKey).contains(redisKey.substring(1)) ? false : true;
return bookMapper.toDto(book, likes, likeStatus);
}
// 책 판별용
public Book findById(Long id) {
return bookJpaRepository.findById(id).orElseThrow(BookNotFoundException::new);
}
public void updateLike(Long userId, Long bookId) {
String redisKey = "B" + bookId.toString(); // 게시글 key
String redisUserKey = "U" + userId.toString(); // 유저 key
// redis에 없는 게시글 id가 들어올 경우 : 새롭게 데이터를 만들어주고 좋아요수를 0으로 초기화, 있는 경우 : 현제 좋아요수 반환
String values = (redisDao.getValues(redisKey) == null) ? redisDao.setValues(redisKey,"0") : redisDao.getValues(redisKey);
int likes = Integer.parseInt(values);

// 유저를 key로 조회한 게시글 ID List안에 해당 게시글 ID가 포함되어있지 않는다면,
if (!redisDao.getValuesList(redisUserKey).contains(redisKey.substring(1))) {
redisDao.setValuesList(redisUserKey, redisKey.substring(1)); // 유저 key로 해당 글 ID를 List 형태로 저장
likes = Integer.parseInt(values) + 1; // 좋아요 증가
redisDao.setValues(redisKey, String.valueOf(likes)); // 글ID key로 좋아요 저장
} else {
redisDao.deleteValueList(redisUserKey, redisKey.substring(1)); // 유저 key로 해당 글 ID를 List 형태에서 제거
likes = Integer.parseInt(values) - 1; // 좋아요 감소
redisDao.setValues(redisKey, String.valueOf(likes)); // 글ID key로 좋아요 저장
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ public RedisTemplate<String, Object> redisTemplate() {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setEnableTransactionSupport(true);
return redisTemplate;
}

@Bean
public RedisTemplate<String, String> redisLikeTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setEnableTransactionSupport(true);
return redisTemplate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public enum ResultCode {

// Book
GET_NEW_BOOK_SUCCESS("B001","신규 도서 조회 성공"),
UPDATE_BOOK_LIKE_SUCCESS("B002","좋아요 갱신 성공"),
GET_ONE_BOOK_SUCCESS("B003","책 조회 성공"),


// Reading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ void searchTitle() throws Exception {
@DisplayName("Controller) 책 id로 조회")
void getBookById() throws Exception {

when(bookService.findBookById(1L)).thenReturn(TEST_BOOK);
when(bookService.findBookById(2L)).thenReturn(TEST_BOOK2);
when(bookService.findBookById(1L, 1L)).thenReturn(TEST_BOOK);
when(bookService.findBookById(2L, 2L)).thenReturn(TEST_BOOK2);

mockMvc.perform(get("/api/v1/books/{bookId}", 1L))
.andExpect(status().isOk())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ void findBookById() {
BookRes bookRes = TEST_BOOK;

given(bookJpaRepository.findByBookId(1L)).willReturn(Optional.ofNullable(TEST_BOOK_ENT));
when(bookMapper.toDto(TEST_BOOK_ENT)).thenReturn(bookRes);
when(bookMapper.toDto(TEST_BOOK_ENT, 10, true)).thenReturn(bookRes);

// when
bookRes = bookService.findBookById(1L);
bookRes = bookService.findBookById(6L, 1L);

// then
assertThat(bookRes.getTitle()).isEqualTo(TEST_BOOK_ENT.getTitle());
Expand Down
4 changes: 4 additions & 0 deletions src/test/java/com/techeer/checkIt/fixture/BookFixtures.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class BookFixtures {
.width(153)
.pages(400)
.publisher("데이원")
.likes(10)
.likeStatus(true)
.build();
public static final BookRes TEST_BOOK2 =
BookRes.builder()
Expand All @@ -25,6 +27,8 @@ public class BookFixtures {
.width(145)
.pages(170)
.publisher("어웨이크북스")
.likes(10)
.likeStatus(true)
.build();
public static final BookSearchRes TEST_BOOK_SEARCH =
BookSearchRes.builder()
Expand Down

0 comments on commit 940e620

Please sign in to comment.