diff --git a/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java b/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java index 4ac097a..cc8f142 100644 --- a/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java +++ b/src/main/java/com/back/catchmate/domain/board/controller/BoardController.java @@ -1,8 +1,15 @@ package com.back.catchmate.domain.board.controller; -import com.back.catchmate.domain.board.dto.BoardRequest.*; -import com.back.catchmate.domain.board.dto.BoardResponse.*; +import com.back.catchmate.domain.board.dto.BoardRequest; +import com.back.catchmate.domain.board.dto.BoardRequest.CreateBoardRequest; +import com.back.catchmate.domain.board.dto.BoardRequest.UpdateBoardRequest; +import com.back.catchmate.domain.board.dto.BoardResponse; +import com.back.catchmate.domain.board.dto.BoardResponse.BoardDeleteInfo; +import com.back.catchmate.domain.board.dto.BoardResponse.BoardInfo; +import com.back.catchmate.domain.board.dto.BoardResponse.PagedBoardInfo; import com.back.catchmate.domain.board.service.BoardService; +import com.back.catchmate.domain.board.service.BookMarkService; +import com.back.catchmate.global.dto.StateResponse; import com.back.catchmate.global.jwt.JwtValidation; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -30,6 +37,7 @@ @RequiredArgsConstructor public class BoardController { private final BoardService boardService; + private final BookMarkService bookMarkService; @PostMapping @Operation(summary = "게시글 등록 API", description = "게시글을 등록합니다.") @@ -65,11 +73,18 @@ public PagedBoardInfo getBoardListByUserId(@JwtValidation Long loginUserId, return boardService.getBoardListByUserId(loginUserId, userId, pageable); } - @DeleteMapping("/{boardId}") - @Operation(summary = "게시글 삭제 API", description = "게시글을 삭제합니다.") - public BoardDeleteInfo deleteBoard(@JwtValidation Long userId, - @PathVariable Long boardId) { - return boardService.deleteBoard(userId, boardId); + @PostMapping("/bookmark/{boardId}") + @Operation(summary = "원하는 게시글을 찜하는 API", description = "원하는 게시글을 찜하는 API 입니다.") + public StateResponse addBookMark(@JwtValidation Long userId, + @PathVariable Long boardId) { + return bookMarkService.addBookMark(userId, boardId); + } + + @DeleteMapping("/bookmark/{boardId}") + @Operation(summary = "원하는 게시글을의 찜을 삭제하는 API", description = "원하는 게시글을의 찜을 삭제하는 API 입니다.") + public StateResponse removeBookMark(@JwtValidation Long userId, + @PathVariable Long boardId) { + return bookMarkService.removeBookMark(userId, boardId); } @PatchMapping("/{boardId}") @@ -79,4 +94,11 @@ public BoardInfo updateBoard(@JwtValidation Long userId, @Valid @RequestBody UpdateBoardRequest request) { return boardService.updateBoard(userId, boardId, request); } + + @DeleteMapping("/{boardId}") + @Operation(summary = "게시글 삭제 API", description = "게시글을 삭제합니다.") + public BoardDeleteInfo deleteBoard(@JwtValidation Long userId, + @PathVariable Long boardId) { + return boardService.deleteBoard(userId, boardId); + } } diff --git a/src/main/java/com/back/catchmate/domain/board/converter/BoardConverter.java b/src/main/java/com/back/catchmate/domain/board/converter/BoardConverter.java index d1aa34e..a4345a9 100644 --- a/src/main/java/com/back/catchmate/domain/board/converter/BoardConverter.java +++ b/src/main/java/com/back/catchmate/domain/board/converter/BoardConverter.java @@ -5,7 +5,6 @@ import com.back.catchmate.domain.board.entity.Board; import com.back.catchmate.domain.club.entity.Club; import com.back.catchmate.domain.game.converter.GameConverter; -import com.back.catchmate.domain.game.dto.GameResponse; import com.back.catchmate.domain.game.dto.GameResponse.GameInfo; import com.back.catchmate.domain.game.entity.Game; import com.back.catchmate.domain.user.entity.User; @@ -15,7 +14,6 @@ import java.time.LocalDateTime; import java.util.List; -import java.util.stream.Collectors; @Component @RequiredArgsConstructor diff --git a/src/main/java/com/back/catchmate/domain/board/converter/BookMarkConverter.java b/src/main/java/com/back/catchmate/domain/board/converter/BookMarkConverter.java new file mode 100644 index 0000000..24231be --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/board/converter/BookMarkConverter.java @@ -0,0 +1,16 @@ +package com.back.catchmate.domain.board.converter; + +import com.back.catchmate.domain.board.entity.Board; +import com.back.catchmate.domain.board.entity.BookMark; +import com.back.catchmate.domain.user.entity.User; +import org.springframework.stereotype.Component; + +@Component +public class BookMarkConverter { + public BookMark toEntity(User user, Board board) { + return BookMark.builder() + .user(user) + .board(board) + .build(); + } +} diff --git a/src/main/java/com/back/catchmate/domain/board/dto/BoardRequest.java b/src/main/java/com/back/catchmate/domain/board/dto/BoardRequest.java index eb19f00..a51749d 100644 --- a/src/main/java/com/back/catchmate/domain/board/dto/BoardRequest.java +++ b/src/main/java/com/back/catchmate/domain/board/dto/BoardRequest.java @@ -8,8 +8,6 @@ import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Range; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; public abstract class BoardRequest { diff --git a/src/main/java/com/back/catchmate/domain/board/entity/Board.java b/src/main/java/com/back/catchmate/domain/board/entity/Board.java index 6a24692..ec524f7 100644 --- a/src/main/java/com/back/catchmate/domain/board/entity/Board.java +++ b/src/main/java/com/back/catchmate/domain/board/entity/Board.java @@ -67,6 +67,10 @@ public class Board extends BaseTimeEntity { @OneToMany(mappedBy = "board") private List notificationList = new ArrayList<>(); + @Builder.Default + @OneToMany(mappedBy = "board") + private List bookMarkList = new ArrayList<>(); + @Column(nullable = false) private Boolean isCompleted = false; diff --git a/src/main/java/com/back/catchmate/domain/board/entity/BookMark.java b/src/main/java/com/back/catchmate/domain/board/entity/BookMark.java new file mode 100644 index 0000000..24f57e9 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/board/entity/BookMark.java @@ -0,0 +1,41 @@ +package com.back.catchmate.domain.board.entity; + +import com.back.catchmate.domain.user.entity.User; +import com.back.catchmate.global.entity.BaseTimeEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class BookMark extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "bookmark_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "board_id", nullable = false) + private Board board; + + public void delete() { + super.delete(); + } +} diff --git a/src/main/java/com/back/catchmate/domain/board/repository/BookMarkRepository.java b/src/main/java/com/back/catchmate/domain/board/repository/BookMarkRepository.java new file mode 100644 index 0000000..5fe7c34 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/board/repository/BookMarkRepository.java @@ -0,0 +1,14 @@ +package com.back.catchmate.domain.board.repository; + +import com.back.catchmate.domain.board.entity.Board; +import com.back.catchmate.domain.board.entity.BookMark; +import com.back.catchmate.domain.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface BookMarkRepository extends JpaRepository { + boolean existsByUserAndBoard(User user, Board board); + + Optional findByUserIdAndBoardId(Long userId, Long boardId); +} diff --git a/src/main/java/com/back/catchmate/domain/board/service/BoardService.java b/src/main/java/com/back/catchmate/domain/board/service/BoardService.java index 7d63873..27d87f7 100644 --- a/src/main/java/com/back/catchmate/domain/board/service/BoardService.java +++ b/src/main/java/com/back/catchmate/domain/board/service/BoardService.java @@ -11,7 +11,7 @@ import java.time.LocalDate; public interface BoardService { - BoardInfo createBoard(Long userId, CreateBoardRequest boardRequest); + BoardInfo createBoard(Long userId, CreateBoardRequest request); BoardInfo getBoard(Long userId, Long boardId); diff --git a/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java b/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java index d973c7d..a1b012a 100644 --- a/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java +++ b/src/main/java/com/back/catchmate/domain/board/service/BoardServiceImpl.java @@ -1,9 +1,11 @@ package com.back.catchmate.domain.board.service; import com.back.catchmate.domain.board.converter.BoardConverter; -import com.back.catchmate.domain.board.dto.BoardRequest; -import com.back.catchmate.domain.board.dto.BoardRequest.*; -import com.back.catchmate.domain.board.dto.BoardResponse.*; +import com.back.catchmate.domain.board.dto.BoardRequest.CreateBoardRequest; +import com.back.catchmate.domain.board.dto.BoardRequest.UpdateBoardRequest; +import com.back.catchmate.domain.board.dto.BoardResponse.BoardDeleteInfo; +import com.back.catchmate.domain.board.dto.BoardResponse.BoardInfo; +import com.back.catchmate.domain.board.dto.BoardResponse.PagedBoardInfo; import com.back.catchmate.domain.board.entity.Board; import com.back.catchmate.domain.board.repository.BoardRepository; import com.back.catchmate.domain.club.entity.Club; @@ -38,22 +40,22 @@ public class BoardServiceImpl implements BoardService { @Override @Transactional - public BoardInfo createBoard(Long userId, CreateBoardRequest createBoardRequest) { + public BoardInfo createBoard(Long userId, CreateBoardRequest request) { User user = userRepository.findById(userId) .orElseThrow(() -> new BaseException(ErrorCode.USER_NOT_FOUND)); - Club cheerClub = clubRepository.findById(createBoardRequest.getCheerClubId()) + Club cheerClub = clubRepository.findById(request.getCheerClubId()) .orElseThrow(() -> new BaseException(ErrorCode.CLUB_NOT_FOUND)); - Club homeClub = clubRepository.findById(createBoardRequest.getGameRequest().getHomeClubId()) + Club homeClub = clubRepository.findById(request.getGameRequest().getHomeClubId()) .orElseThrow(() -> new BaseException(ErrorCode.CLUB_NOT_FOUND)); - Club awayClub = clubRepository.findById(createBoardRequest.getGameRequest().getAwayClubId()) + Club awayClub = clubRepository.findById(request.getGameRequest().getAwayClubId()) .orElseThrow(() -> new BaseException(ErrorCode.CLUB_NOT_FOUND)); - Game game = findOrCreateGame(homeClub, awayClub, createBoardRequest.getGameRequest()); + Game game = findOrCreateGame(homeClub, awayClub, request.getGameRequest()); - Board board = boardConverter.toEntity(user, game, cheerClub, createBoardRequest); + Board board = boardConverter.toEntity(user, game, cheerClub, request); this.boardRepository.save(board); return boardConverter.toBoardInfo(board, game); @@ -113,19 +115,7 @@ public PagedBoardInfo getBoardListByUserId(Long loginUserId, Long userId, Pageab @Override @Transactional - public BoardDeleteInfo deleteBoard(Long userId, Long boardId) { - int updatedRows = boardRepository.softDeleteByUserIdAndBoardId(userId, boardId); - - if (updatedRows == 0) { - throw new BaseException(ErrorCode.BOARD_NOT_FOUND); - } - - return boardConverter.toBoardDeleteInfo(boardId); - } - - @Override - @Transactional - public BoardInfo updateBoard(Long userId, Long boardId, UpdateBoardRequest updateBoardRequest) { + public BoardInfo updateBoard(Long userId, Long boardId, UpdateBoardRequest request) { Board board = this.boardRepository.findById(boardId) .orElseThrow(() -> new BaseException(ErrorCode.BOARD_NOT_FOUND)); @@ -136,18 +126,30 @@ public BoardInfo updateBoard(Long userId, Long boardId, UpdateBoardRequest updat throw new BaseException(ErrorCode.BOARD_BAD_REQUEST); } - Club cheerClub = this.clubRepository.findById(updateBoardRequest.getCheerClubId()) + Club cheerClub = clubRepository.findById(request.getCheerClubId()) .orElseThrow(() -> new BaseException(ErrorCode.CLUB_NOT_FOUND)); - Club homeClub = this.clubRepository.findById(updateBoardRequest.getGameRequest().getHomeClubId()) + Club homeClub = clubRepository.findById(request.getGameRequest().getHomeClubId()) .orElseThrow(() -> new BaseException(ErrorCode.CLUB_NOT_FOUND)); - Club awayClub = this.clubRepository.findById(updateBoardRequest.getGameRequest().getAwayClubId()) + Club awayClub = clubRepository.findById(request.getGameRequest().getAwayClubId()) .orElseThrow(() -> new BaseException(ErrorCode.CLUB_NOT_FOUND)); - Game game = this.findOrCreateGame(homeClub, awayClub, updateBoardRequest.getGameRequest()); + Game game = findOrCreateGame(homeClub, awayClub, request.getGameRequest()); - board.updateBoard(cheerClub, game, updateBoardRequest); + board.updateBoard(cheerClub, game, request); return boardConverter.toBoardInfo(board, game); } + + @Override + @Transactional + public BoardDeleteInfo deleteBoard(Long userId, Long boardId) { + int updatedRows = boardRepository.softDeleteByUserIdAndBoardId(userId, boardId); + + if (updatedRows == 0) { + throw new BaseException(ErrorCode.BOARD_NOT_FOUND); + } + + return boardConverter.toBoardDeleteInfo(boardId); + } } diff --git a/src/main/java/com/back/catchmate/domain/board/service/BookMarkService.java b/src/main/java/com/back/catchmate/domain/board/service/BookMarkService.java new file mode 100644 index 0000000..8fd7be0 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/board/service/BookMarkService.java @@ -0,0 +1,9 @@ +package com.back.catchmate.domain.board.service; + +import com.back.catchmate.global.dto.StateResponse; + +public interface BookMarkService { + StateResponse addBookMark(Long userId, Long boardId); + + StateResponse removeBookMark(Long userId, Long boardId); +} diff --git a/src/main/java/com/back/catchmate/domain/board/service/BookMarkServiceImpl.java b/src/main/java/com/back/catchmate/domain/board/service/BookMarkServiceImpl.java new file mode 100644 index 0000000..57cd1a3 --- /dev/null +++ b/src/main/java/com/back/catchmate/domain/board/service/BookMarkServiceImpl.java @@ -0,0 +1,58 @@ +package com.back.catchmate.domain.board.service; + +import com.back.catchmate.domain.board.converter.BookMarkConverter; +import com.back.catchmate.domain.board.entity.Board; +import com.back.catchmate.domain.board.entity.BookMark; +import com.back.catchmate.domain.board.repository.BoardRepository; +import com.back.catchmate.domain.board.repository.BookMarkRepository; +import com.back.catchmate.domain.user.entity.User; +import com.back.catchmate.domain.user.repository.UserRepository; +import com.back.catchmate.global.dto.StateResponse; +import com.back.catchmate.global.error.ErrorCode; +import com.back.catchmate.global.error.exception.BaseException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class BookMarkServiceImpl implements BookMarkService { + private final BookMarkRepository bookMarkRepository; + private final UserRepository userRepository; + private final BoardRepository boardRepository; + private final BookMarkConverter bookMarkConverter; + + @Override + @Transactional + public StateResponse addBookMark(Long userId, Long boardId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new BaseException(ErrorCode.USER_NOT_FOUND)); + + Board board = boardRepository.findById(boardId) + .orElseThrow(() -> new BaseException(ErrorCode.BOARD_NOT_FOUND)); + + if (bookMarkRepository.existsByUserAndBoard(user, board)) { + throw new BaseException(ErrorCode.USER_NOT_FOUND); + } + + BookMark bookMark = bookMarkConverter.toEntity(user, board); + bookMarkRepository.save(bookMark); + return new StateResponse(true); + } + + @Override + @Transactional + public StateResponse removeBookMark(Long userId, Long boardId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new BaseException(ErrorCode.USER_NOT_FOUND)); + + Board board = boardRepository.findById(boardId) + .orElseThrow(() -> new BaseException(ErrorCode.BOARD_NOT_FOUND)); + + BookMark bookMark = bookMarkRepository.findByUserIdAndBoardId(user.getId(), board.getId()) + .orElseThrow(() -> new BaseException(ErrorCode.BOOKMARK_NOT_FOUND)); + + bookMark.delete(); + return new StateResponse(true); + } +} diff --git a/src/main/java/com/back/catchmate/domain/game/entity/Game.java b/src/main/java/com/back/catchmate/domain/game/entity/Game.java index 50cdc89..a59b818 100644 --- a/src/main/java/com/back/catchmate/domain/game/entity/Game.java +++ b/src/main/java/com/back/catchmate/domain/game/entity/Game.java @@ -5,7 +5,6 @@ import com.back.catchmate.global.entity.BaseTimeEntity; import jakarta.persistence.*; import lombok.*; -import org.joda.time.DateTime; import java.time.LocalDateTime; import java.util.ArrayList; @@ -28,11 +27,11 @@ public class Game extends BaseTimeEntity { @Column(nullable = false) private LocalDateTime gameStartDate; - @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "home_club_id", nullable = false) private Club homeClub; - @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "away_club_id", nullable = false) private Club awayClub; diff --git a/src/main/java/com/back/catchmate/domain/user/entity/User.java b/src/main/java/com/back/catchmate/domain/user/entity/User.java index e67d8c3..3a2bb9f 100644 --- a/src/main/java/com/back/catchmate/domain/user/entity/User.java +++ b/src/main/java/com/back/catchmate/domain/user/entity/User.java @@ -1,6 +1,7 @@ package com.back.catchmate.domain.user.entity; import com.back.catchmate.domain.board.entity.Board; +import com.back.catchmate.domain.board.entity.BookMark; import com.back.catchmate.domain.club.entity.Club; import com.back.catchmate.domain.enroll.entity.Enroll; import com.back.catchmate.domain.user.dto.UserRequest.UserProfileUpdateRequest; @@ -42,6 +43,10 @@ public class User extends BaseTimeEntity { @JoinColumn(name = "club_id", nullable = false) private Club club; + @Builder.Default + @OneToMany(mappedBy = "user") + private List bookMarkList = new ArrayList<>(); + @Column(nullable = false) private String email; diff --git a/src/main/java/com/back/catchmate/global/entity/BaseTimeEntity.java b/src/main/java/com/back/catchmate/global/entity/BaseTimeEntity.java index fffe7c4..d871b13 100644 --- a/src/main/java/com/back/catchmate/global/entity/BaseTimeEntity.java +++ b/src/main/java/com/back/catchmate/global/entity/BaseTimeEntity.java @@ -26,4 +26,8 @@ public abstract class BaseTimeEntity { @Column(name = "deleted_at") private LocalDateTime deletedAt; + + public void delete() { + this.deletedAt = LocalDateTime.now(); + } } diff --git a/src/main/java/com/back/catchmate/global/error/ErrorCode.java b/src/main/java/com/back/catchmate/global/error/ErrorCode.java index c5d537a..bb45891 100644 --- a/src/main/java/com/back/catchmate/global/error/ErrorCode.java +++ b/src/main/java/com/back/catchmate/global/error/ErrorCode.java @@ -32,6 +32,8 @@ public enum ErrorCode { BOARD_BAD_REQUEST(HttpStatus.BAD_REQUEST, "자신의 게시글만 수정할 수 있습니다."), INVALID_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 액세스 토큰입니다."), INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 리프레시 토큰입니다."), + ALREADY_BOOKMARK(HttpStatus.BAD_REQUEST, "이미 찜한 게시글입니다."), + BOOKMARK_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 찜입니다."), // 알림 NOTIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 알림입니다."),