Skip to content

Commit

Permalink
feat: #128 게시글 좋아요 토글 API 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
kyxxgsoo committed Aug 19, 2024
1 parent ded08b9 commit 1835529
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.server.capple.domain.board.controller;

import com.server.capple.config.security.AuthMember;
import com.server.capple.domain.answer.dto.AnswerResponse;
import com.server.capple.domain.board.dto.BoardRequest;
import com.server.capple.domain.board.dto.BoardResponse;
import com.server.capple.domain.board.entity.BoardType;
Expand Down Expand Up @@ -75,4 +76,11 @@ private BaseResponse<BoardResponse.BoardsSearchByKeyword> searchBoardsByKeyword(
return BaseResponse.onSuccess(boardService.searchBoardsByKeyword(keyword));
}

@Operation(summary = "게시글 좋아요/취소 API", description = " 게시글 좋아요/취소 API 입니다." +
"pathvariable 으로 boardId를 주세요.")
@PostMapping("/{boardId}/heart")
public BaseResponse<BoardResponse.BoardToggleHeart> toggleBoardHeart(@AuthMember Member member, @PathVariable(value = "boardId") Long boardId) {
return BaseResponse.onSuccess(boardService.toggleBoardHeart(member, boardId));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static class BoardsGetByBoardType {
@AllArgsConstructor
@NoArgsConstructor
public static class BoardsGetByBoardTypeBoardInfo {
private Long boardId;
private Long writerId;
private String content;
private Integer heartCount;
Expand Down Expand Up @@ -61,10 +62,20 @@ public static class BoardsSearchByKeyword {
@AllArgsConstructor
@NoArgsConstructor
public static class BoardsSearchByKeywordBoardInfo {
private Long boardId;
private Long writerId;
private String content;
private Integer heartCount;
private Integer commentCount;
private LocalDateTime createAt;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class BoardToggleHeart {
private Long boardId;
private Boolean isLiked;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,9 @@ public class Board extends BaseEntity {
@Column(nullable = false)
private BoardType boardType;


@Column(nullable = false)
private String content;

@Column(nullable = false)
private Integer heartCount;

@Column(nullable = false)
private Integer commentCount;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.server.capple.domain.board.mapper;

import com.server.capple.domain.board.dto.BoardRequest;
import com.server.capple.domain.board.dto.BoardResponse;
import com.server.capple.domain.board.entity.Board;
import com.server.capple.domain.board.entity.BoardType;
Expand All @@ -23,7 +22,6 @@ public Board toBoard(
.writer(member)
.boardType(boardType)
.content(content)
.heartCount(heartCount)
.commentCount(commentCount)
.build();
}
Expand All @@ -45,13 +43,15 @@ public BoardResponse.BoardsGetByBoardType toBoardsGetByBoardType(
}

public BoardResponse.BoardsGetByBoardTypeBoardInfo toBoardsGetByBoardTypeBoardInfo(
Board board
) {
Board board,
Integer boardHeartsCount) {
return BoardResponse.BoardsGetByBoardTypeBoardInfo.builder()
.boardId(board.getId())
.writerId(board.getWriter().getId())
.content(board.getContent())
.heartCount(board.getHeartCount())
.commentCount(board.getCommentCount())
.heartCount(boardHeartsCount)
// TODO : 댓글 작성 API 나오면 추후 구현
.commentCount(0)
.createAt(board.getCreatedAt())
.build();
}
Expand All @@ -63,12 +63,14 @@ public BoardResponse.BoardDelete toBoardDelete(Board board) {
}

public BoardResponse.BoardsSearchByKeywordBoardInfo toBoardsSearchByKeywordBoardInfo(
Board board
Board board,
Integer heartCount
) {
return BoardResponse.BoardsSearchByKeywordBoardInfo.builder()
.boardId(board.getId())
.writerId(board.getWriter().getId())
.content(board.getContent())
.heartCount(board.getHeartCount())
.heartCount(heartCount)
.commentCount(board.getCommentCount())
.createAt(board.getCreatedAt())
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.server.capple.domain.board.repository;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;

@Repository
@RequiredArgsConstructor
public class BoardHeartRedisRepository implements Serializable {
public static final String BOARD_HEART_KEY_PREFIX = "boardHeart-";
public static final String MEMBER_KEY_PREFIX = "member-";

private final RedisTemplate<String, String> redisTemplate;

// 게시판 좋아요 토글
public Boolean toggleBoardHeart(Long memberId, Long boardId) {
String key = BOARD_HEART_KEY_PREFIX + boardId.toString();
String member = MEMBER_KEY_PREFIX + memberId.toString();
SetOperations<String, String> setOperations = redisTemplate.opsForSet();

//해당 key에 member가 존재하지 않으면 추가, 존재하면 삭제
if (FALSE.equals(setOperations.isMember(key, member))) {
setOperations.add(key, member);
return TRUE;
} else {
setOperations.remove(key, member);
return FALSE;
}
}

// 게시판 좋아요 수 조회
public Integer getBoardHeartsCount(Long boardId) {
String key = BOARD_HEART_KEY_PREFIX + boardId.toString();
Set<String> members = redisTemplate.opsForSet().members(key);
return members != null ? members.size() : 0;
}

// 좋아요 누른 게시판 조회
public Set<Long> getMemberHeartsBoard(Long memberId) {
String member = MEMBER_KEY_PREFIX + memberId.toString();
Set<String> keys = redisTemplate.keys(BOARD_HEART_KEY_PREFIX + "*"); // 모든 키 조회
Set<Long> boardIds = new HashSet<>();

for (String key : keys) {
if (redisTemplate.opsForSet().isMember(key, member)) {
String boardId = key.substring(BOARD_HEART_KEY_PREFIX.length());
boardIds.add(Long.parseLong(boardId));
}
}
return boardIds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface BoardService {
BoardResponse.BoardDelete deleteBoard(Member member, Long boardId);

BoardResponse.BoardsSearchByKeyword searchBoardsByKeyword(String keyword);

BoardResponse.BoardToggleHeart toggleBoardHeart(Member member, Long boardId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@
import com.server.capple.domain.board.entity.Board;
import com.server.capple.domain.board.entity.BoardType;
import com.server.capple.domain.board.mapper.BoardMapper;
import com.server.capple.domain.board.repository.BoardHeartRedisRepository;
import com.server.capple.domain.board.repository.BoardRepository;
import com.server.capple.domain.member.entity.Member;
import com.server.capple.global.exception.RestApiException;
import com.server.capple.global.exception.errorCode.BoardErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@RequiredArgsConstructor
@Transactional
public class BoardServiceImpl implements BoardService {

private final BoardRepository boardRepository;
private final BoardHeartRedisRepository boardHeartRedisRepository;
private final BoardMapper boardMapper;

@Override
Expand All @@ -49,7 +49,7 @@ public BoardResponse.BoardsGetByBoardType getBoardsByBoardType(BoardType boardTy
throw new RestApiException(BoardErrorCode.BOARD_BAD_REQUEST);
}
return boardMapper.toBoardsGetByBoardType(boards.stream()
.map(boardMapper::toBoardsGetByBoardTypeBoardInfo)
.map(board -> boardMapper.toBoardsGetByBoardTypeBoardInfo(board, boardHeartRedisRepository.getBoardHeartsCount(board.getId())))
.toList()
);
}
Expand All @@ -71,9 +71,18 @@ public BoardResponse.BoardDelete deleteBoard(Member member, Long boardId) {
public BoardResponse.BoardsSearchByKeyword searchBoardsByKeyword(String keyword) {
List<Board> boards = boardRepository.findBoardsByKeyword(keyword);
return boardMapper.toBoardsSearchByKeyword(boards.stream()
.map(boardMapper::toBoardsSearchByKeywordBoardInfo)
.map(board -> boardMapper.toBoardsSearchByKeywordBoardInfo(board, boardHeartRedisRepository.getBoardHeartsCount(board.getId())))
.toList());
}

@Override
public BoardResponse.BoardToggleHeart toggleBoardHeart(Member member, Long boardId) {
Board board = boardRepository.findById(boardId)
.orElseThrow(() -> new RestApiException(BoardErrorCode.BOARD_NOT_FOUND));

Boolean isLiked = boardHeartRedisRepository.toggleBoardHeart(member.getId(), board.getId());
return new BoardResponse.BoardToggleHeart(boardId, isLiked);
}


}

0 comments on commit 1835529

Please sign in to comment.