Skip to content

Commit edb9aa6

Browse files
authored
feat: 게시글 추천,추천삭제 API 추가 & 게시글 조회 시 로그인 사용자 추천 여부 응답값 추가 (#117)
* feat: 게시글 추천,추천삭제 API 추가 & 게시글 조회 시 로그인 사용자 추천 여부 응답값 추가 * chore: TODO 완료 작업 주석 삭제 * chore: JsonProperty 추가 * chore: BoardType 프론트와 협의한 Enum으로 수정 * feat: 생성일시, 수정일시를 BaseTime을 상속하도록 수정 * feat: 에러 코드 수정
1 parent 5c90c04 commit edb9aa6

16 files changed

+319
-59
lines changed

src/main/java/org/myteam/server/board/controller/BoardController.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ public class BoardController {
4141
*/
4242
@PostMapping
4343
public ResponseEntity<ResponseDto<BoardResponse>> saveBoard(
44-
@AuthenticationPrincipal final CustomUserDetails userDetails,
4544
@Valid @RequestBody final BoardSaveRequest boardSaveRequest,
4645
final HttpServletRequest request) {
4746
final String clientIP = ClientUtils.getRemoteIP(request);
48-
final BoardResponse response = boardService.saveBoard(boardSaveRequest, userDetails, clientIP);
47+
final BoardResponse response = boardService.saveBoard(boardSaveRequest, clientIP);
4948
return ResponseEntity.ok(new ResponseDto<>(SUCCESS.name(), "게시글 생성 성공", response));
5049
}
5150

@@ -54,19 +53,17 @@ public ResponseEntity<ResponseDto<BoardResponse>> saveBoard(
5453
*/
5554
@PutMapping("/{boardId}")
5655
public ResponseEntity<ResponseDto<BoardResponse>> updateBoard(
57-
@AuthenticationPrincipal final CustomUserDetails userDetails, @PathVariable final Long boardId,
58-
@Valid @RequestBody final BoardSaveRequest boardSaveRequest) {
59-
final BoardResponse response = boardService.updateBoard(boardSaveRequest, userDetails, boardId);
56+
@PathVariable final Long boardId, @Valid @RequestBody final BoardSaveRequest boardSaveRequest) {
57+
final BoardResponse response = boardService.updateBoard(boardSaveRequest, boardId);
6058
return ResponseEntity.ok(new ResponseDto<>(SUCCESS.name(), "게시글 수정 성공", response));
6159
}
6260

6361
/**
6462
* 게시글 삭제
6563
*/
6664
@DeleteMapping("/{boardId}")
67-
public ResponseEntity<ResponseDto<Void>> deleteBoard(@PathVariable final Long boardId,
68-
@AuthenticationPrincipal final CustomUserDetails userDetails) {
69-
boardService.deleteBoard(boardId, userDetails);
65+
public ResponseEntity<ResponseDto<Void>> deleteBoard(@PathVariable final Long boardId) {
66+
boardService.deleteBoard(boardId);
7067
return ResponseEntity.ok(new ResponseDto<>(SUCCESS.name(), "게시글 삭제 성공", null));
7168
}
7269

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.myteam.server.board.controller;
2+
3+
import static org.myteam.server.global.web.response.ResponseStatus.SUCCESS;
4+
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.myteam.server.board.service.BoardRecommendService;
8+
import org.myteam.server.global.web.response.ResponseDto;
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.web.bind.annotation.DeleteMapping;
11+
import org.springframework.web.bind.annotation.PathVariable;
12+
import org.springframework.web.bind.annotation.PostMapping;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
@Slf4j
17+
@RestController
18+
@RequestMapping("/api/board/recommend")
19+
@RequiredArgsConstructor
20+
public class BoardRecommendController {
21+
22+
private final BoardRecommendService boardRecommendService;
23+
24+
/**
25+
* 게시글 추천
26+
*/
27+
@PostMapping("/{boardId}")
28+
public ResponseEntity<ResponseDto<Void>> recommendBoard(@PathVariable Long boardId) {
29+
boardRecommendService.recommendBoard(boardId);
30+
return ResponseEntity.ok(
31+
new ResponseDto<>(SUCCESS.name(), "게시글 추천 성공", null));
32+
}
33+
34+
/**
35+
* 게시글 추천 삭제
36+
*/
37+
@DeleteMapping("/{boardId}")
38+
public ResponseEntity<ResponseDto<Void>> deleteBoard(@PathVariable Long boardId) {
39+
boardRecommendService.deleteRecommendBoard(boardId);
40+
return ResponseEntity.ok(new ResponseDto<>(SUCCESS.name(), "게시글 추천 삭제 성공", null));
41+
}
42+
}

src/main/java/org/myteam/server/board/domain/Board.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.myteam.server.board.domain;
22

33
import jakarta.persistence.CascadeType;
4+
import jakarta.persistence.Column;
45
import jakarta.persistence.Entity;
56
import jakarta.persistence.EnumType;
67
import jakarta.persistence.Enumerated;
@@ -12,19 +13,19 @@
1213
import jakarta.persistence.ManyToOne;
1314
import jakarta.persistence.OneToOne;
1415
import jakarta.persistence.Table;
15-
import java.time.LocalDateTime;
1616
import lombok.AccessLevel;
1717
import lombok.Builder;
1818
import lombok.Getter;
1919
import lombok.NoArgsConstructor;
2020
import org.myteam.server.board.dto.request.BoardSaveRequest;
21+
import org.myteam.server.global.domain.BaseTime;
2122
import org.myteam.server.member.entity.Member;
2223

2324
@Getter
2425
@Entity
2526
@NoArgsConstructor(access = AccessLevel.PROTECTED)
2627
@Table(name = "p_board")
27-
public class Board {
28+
public class Board extends BaseTime {
2829

2930
@Id
3031
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -34,9 +35,11 @@ public class Board {
3435
@JoinColumn(name = "public_id")
3536
private Member member;
3637

38+
@Column(name = "board_type")
3739
@Enumerated(EnumType.STRING)
3840
private BoardType boardType;
3941

42+
@Column(name = "category_type")
4043
@Enumerated(EnumType.STRING)
4144
private CategoryType categoryType;
4245

@@ -46,14 +49,11 @@ public class Board {
4649

4750
private String link;
4851

52+
@Column(name = "created_ip")
4953
private String createdIp;
5054

5155
private String thumbnail;
5256

53-
private LocalDateTime createdAt;
54-
55-
private LocalDateTime updatedAt;
56-
5757
@OneToOne(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true)
5858
private BoardCount boardCount;
5959

@@ -69,8 +69,6 @@ public Board(Member member, BoardType boardType, CategoryType categoryType, Stri
6969
this.link = link;
7070
this.createdIp = createdIp;
7171
this.thumbnail = thumbnail;
72-
this.createdAt = LocalDateTime.now();
73-
this.updatedAt = LocalDateTime.now();
7472
this.boardCount = boardCount;
7573
}
7674

@@ -81,7 +79,6 @@ public void updateBoard(BoardSaveRequest request) {
8179
this.content = request.getContent();
8280
this.link = request.getLink();
8381
this.thumbnail = request.getThumbnail();
84-
this.updatedAt = LocalDateTime.now();
8582
}
8683

8784
public boolean isAuthor(Member member) {

src/main/java/org/myteam/server/board/domain/BoardCount.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,12 @@ public static BoardCount createBoardCount(Board board) {
5656
.viewCount(COUNT_SETTING_NUMBER)
5757
.build();
5858
}
59+
60+
public void addRecommendCount() {
61+
this.recommendCount += 1;
62+
}
63+
64+
public void minusRecommendCount() {
65+
this.recommendCount -= 1;
66+
}
5967
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.myteam.server.board.domain;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.FetchType;
5+
import jakarta.persistence.GeneratedValue;
6+
import jakarta.persistence.GenerationType;
7+
import jakarta.persistence.Id;
8+
import jakarta.persistence.ManyToOne;
9+
import jakarta.persistence.Table;
10+
import lombok.AccessLevel;
11+
import lombok.Builder;
12+
import lombok.Getter;
13+
import lombok.NoArgsConstructor;
14+
import org.myteam.server.global.domain.BaseTime;
15+
import org.myteam.server.member.entity.Member;
16+
17+
@Getter
18+
@Entity
19+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
20+
@Table(name = "p_board_recommend")
21+
public class BoardRecommend extends BaseTime {
22+
@Id
23+
@GeneratedValue(strategy = GenerationType.IDENTITY)
24+
private Long id;
25+
26+
@ManyToOne(fetch = FetchType.LAZY)
27+
private Board board;
28+
29+
@ManyToOne(fetch = FetchType.LAZY)
30+
private Member member;
31+
32+
@Builder
33+
public BoardRecommend(Long id, Board board, Member member) {
34+
this.id = id;
35+
this.board = board;
36+
this.member = member;
37+
}
38+
}

src/main/java/org/myteam/server/board/domain/BoardType.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ public enum BoardType {
44
/**
55
* e-sports
66
*/
7-
E_SPORTS,
7+
ESPORTS,
88
/**
99
* 야구
1010
*/
@@ -15,6 +15,6 @@ public enum BoardType {
1515
FOOTBALL;
1616

1717
public boolean isEsports() {
18-
return this.equals(E_SPORTS);
18+
return this.equals(ESPORTS);
1919
}
2020
}

src/main/java/org/myteam/server/board/dto/reponse/BoardResponse.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.myteam.server.board.dto.reponse;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
34
import java.time.LocalDateTime;
45
import java.util.UUID;
56
import lombok.Getter;
@@ -53,7 +54,12 @@ public class BoardResponse {
5354
*/
5455
private String thumbnail;
5556
/**
56-
* 좋아요 수
57+
* 로그인한 사용자 게시글 추천 여부
58+
*/
59+
@JsonProperty("isRecommended")
60+
private boolean isRecommended;
61+
/**
62+
* 추천 수
5763
*/
5864
private Integer recommendCount;
5965
/**
@@ -67,13 +73,13 @@ public class BoardResponse {
6773
/**
6874
* 작성 일시
6975
*/
70-
private LocalDateTime createdAt;
76+
private LocalDateTime createDate;
7177
/**
7278
* 수정 일시
7379
*/
74-
private LocalDateTime updatedAt;
80+
private LocalDateTime lastModifiedDate;
7581

76-
public BoardResponse(Board board, BoardCount boardCount) {
82+
public BoardResponse(Board board, BoardCount boardCount, boolean isRecommended) {
7783
this.boardType = board.getBoardType();
7884
this.categoryType = board.getCategoryType();
7985
this.boardId = board.getId();
@@ -84,10 +90,11 @@ public BoardResponse(Board board, BoardCount boardCount) {
8490
this.content = board.getContent();
8591
this.link = board.getLink();
8692
this.thumbnail = board.getThumbnail();
93+
this.isRecommended = isRecommended;
8794
this.recommendCount = boardCount.getRecommendCount();
8895
this.commentCount = boardCount.getCommentCount();
8996
this.viewCount = boardCount.getViewCount();
90-
this.createdAt = board.getCreatedAt();
91-
this.updatedAt = board.getUpdatedAt();
97+
this.createDate = board.getCreateDate();
98+
this.lastModifiedDate = board.getLastModifiedDate();
9299
}
93100
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package org.myteam.server.board.repository;
22

3+
import jakarta.persistence.LockModeType;
4+
import java.util.Optional;
35
import org.myteam.server.board.domain.BoardCount;
46
import org.springframework.data.jpa.repository.JpaRepository;
7+
import org.springframework.data.jpa.repository.Lock;
58
import org.springframework.stereotype.Repository;
69

710
@Repository
811
public interface BoardCountRepository extends JpaRepository<BoardCount, Long> {
12+
913
void deleteByBoardId(Long id);
14+
15+
// 비관적 락으로 동시성 문제 해결
16+
@Lock(LockModeType.PESSIMISTIC_WRITE)
17+
Optional<BoardCount> findByBoardId(Long boardId);
1018
}

src/main/java/org/myteam/server/board/repository/BoardQueryRepository.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ public Page<BoardDto> getBoardList(BoardType boardType, CategoryType categoryTyp
4848
member.publicId,
4949
member.nickname,
5050
boardCount.commentCount,
51-
board.createdAt,
52-
board.updatedAt
51+
board.createDate,
52+
board.lastModifiedDate
5353
))
5454
.from(board)
5555
.join(boardCount).on(boardCount.boardId.eq(board.id))
@@ -98,7 +98,7 @@ private OrderSpecifier<?> isOrderByEqualToOrderCategory(BoardOrderType orderType
9898
// default 최신순
9999
BoardOrderType boardOrderType = Optional.ofNullable(orderType).orElse(BoardOrderType.CREATE);
100100
return switch (boardOrderType) {
101-
case CREATE -> board.createdAt.desc();
101+
case CREATE -> board.createDate.desc();
102102
case RECOMMEND -> boardCount.recommendCount.desc();
103103
case COMMENT -> boardCount.commentCount.desc();
104104
};
@@ -129,8 +129,8 @@ public Page<BoardDto> getMyBoardList(BoardOrderType orderType, BoardSearchType s
129129
member.publicId,
130130
member.nickname,
131131
boardCount.commentCount,
132-
board.createdAt,
133-
board.updatedAt
132+
board.createDate,
133+
board.lastModifiedDate
134134
))
135135
.from(board)
136136
.join(boardCount).on(boardCount.boardId.eq(board.id))
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.myteam.server.board.repository;
2+
3+
import java.util.Optional;
4+
import java.util.UUID;
5+
import org.myteam.server.board.domain.BoardRecommend;
6+
import org.springframework.data.jpa.repository.JpaRepository;
7+
import org.springframework.stereotype.Repository;
8+
9+
@Repository
10+
public interface BoardRecommendRepository extends JpaRepository<BoardRecommend, Long> {
11+
12+
Optional<BoardRecommend> findByBoardIdAndMemberPublicId(Long boardId, UUID memberId);
13+
14+
void deleteByBoardIdAndMemberPublicId(Long boardId, UUID publicId);
15+
}

0 commit comments

Comments
 (0)