Skip to content

Commit 0753dee

Browse files
committed
Merge branch 'dev' into refactor#114
2 parents 19777f0 + 70d2618 commit 0753dee

File tree

46 files changed

+1041
-94
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1041
-94
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.back.domain.cocktail.comment.controller;
2+
3+
import com.back.domain.cocktail.comment.dto.CocktailCommentCreateRequestDto;
4+
import com.back.domain.cocktail.comment.dto.CocktailCommentResponseDto;
5+
import com.back.domain.cocktail.comment.dto.CocktailCommentUpdateRequestDto;
6+
import com.back.domain.cocktail.comment.service.CocktailCommentService;
7+
import com.back.global.rsData.RsData;
8+
import io.swagger.v3.oas.annotations.Operation;
9+
import io.swagger.v3.oas.annotations.tags.Tag;
10+
import jakarta.validation.Valid;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.web.bind.annotation.*;
13+
14+
import java.util.List;
15+
16+
@RestController
17+
@RequestMapping("/api/cocktails/{cocktailId}/comments")
18+
@Tag(name = "ApiCocktailCommentController", description = "API 칵테일댓글 컨트롤러")
19+
@RequiredArgsConstructor
20+
public class CocktailCommentController {
21+
22+
private final CocktailCommentService cocktailCommentService;
23+
24+
/**
25+
* 칵테일댓글 작성 API
26+
*
27+
* @param cocktailId 칵테일댓글을 작성할 칵테일 ID
28+
* @param reqBody 칵테일댓글 작성 요청 DTO
29+
* @return 작성된 칵테일댓글 정보
30+
*/
31+
@PostMapping
32+
@Operation(summary = "칵테일댓글 작성")
33+
public RsData<CocktailCommentResponseDto> createCocktailComment(
34+
@PathVariable Long cocktailId,
35+
@Valid @RequestBody CocktailCommentCreateRequestDto reqBody
36+
) {
37+
return RsData.successOf(cocktailCommentService.createCocktailComment(cocktailId, reqBody)); // code=200, message="success"
38+
}
39+
40+
/**
41+
* 칵테일댓글 다건조회 API
42+
*
43+
* @param cocktailId 칵테일댓글 작성된 게시글 ID
44+
* @param lastId 마지막으로 조회한 칵테일댓글 ID (페이징 처리용, optional)
45+
* @return 칵테일댓글 목록
46+
*/
47+
@GetMapping
48+
@Operation(summary = "댓글 다건 조회")
49+
public RsData<List<CocktailCommentResponseDto>> getCocktailComments(
50+
@PathVariable Long cocktailId,
51+
@RequestParam(required = false) Long lastId
52+
) {
53+
return RsData.successOf(cocktailCommentService.getCocktailComments(cocktailId, lastId)); // code=200, message="success"
54+
}
55+
56+
/**
57+
* 칵테일댓글 단건 조회 API
58+
*
59+
* @param cocktailId 칵테일댓글이 작성된 칵테일 ID
60+
* @param cocktailCommentId 조회할 칵테일댓글 ID
61+
* @return 해당 ID의 칵테일댓글 정보
62+
*/
63+
@GetMapping("/{cocktailCommentId}")
64+
@Operation(summary = "칵테일 댓글 단건 조회")
65+
public RsData<CocktailCommentResponseDto> getCocktailComment(
66+
@PathVariable Long cocktailId,
67+
@PathVariable Long cocktailCommentId
68+
) {
69+
return RsData.successOf(cocktailCommentService.getCocktailComment(cocktailId, cocktailCommentId)); // code=200, message="success"
70+
}
71+
72+
/**
73+
* 칵테일댓글 수정 API
74+
*
75+
* @param cocktailId 칵테일댓글 작성된 칵테일 ID
76+
* @param cocktailCommentId 수정할 칵테일댓글 ID
77+
* @param reqBody 칵테일댓글 수정 요청 DTO
78+
* @return 수정된 칵테일댓글 정보
79+
*/
80+
@PatchMapping("/{cocktailCommentId}")
81+
@Operation(summary = "칵테일댓글 수정")
82+
public RsData<CocktailCommentResponseDto> updateComment(
83+
@PathVariable Long cocktailId,
84+
@PathVariable Long cocktailCommentId,
85+
@Valid @RequestBody CocktailCommentUpdateRequestDto reqBody
86+
) {
87+
return RsData.successOf(cocktailCommentService.updateCocktailComment(cocktailId, cocktailCommentId, reqBody)); // code=200, message="success"
88+
}
89+
90+
/**
91+
* 칵테일댓글 삭제 API
92+
*
93+
* @param cocktailId 칵테일댓글 작성된 칵테일 ID
94+
* @param cocktailCommentId 삭제할 칵테일댓글 ID
95+
* @return 삭제 성공 메시지
96+
*/
97+
@DeleteMapping("/{cocktailCommentId}")
98+
@Operation(summary = "댓글 삭제")
99+
public RsData<Void> deleteComment(
100+
@PathVariable Long cocktailId,
101+
@PathVariable Long cocktailCommentId
102+
) {
103+
cocktailCommentService.deleteCocktailComment(cocktailId, cocktailCommentId);
104+
return RsData.successOf(null); // code=200, message="success"
105+
}
106+
}
107+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.back.domain.cocktail.comment.dto;
2+
3+
import com.back.domain.post.comment.enums.CommentStatus;
4+
import jakarta.validation.constraints.NotBlank;
5+
6+
public record CocktailCommentCreateRequestDto(
7+
CommentStatus status,
8+
@NotBlank(message = "내용은 필수입니다.")
9+
String content
10+
) {
11+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.back.domain.cocktail.comment.dto;
2+
3+
import com.back.domain.cocktail.comment.entity.CocktailComment;
4+
import com.back.domain.post.comment.enums.CommentStatus;
5+
6+
import java.time.LocalDateTime;
7+
8+
public record CocktailCommentResponseDto(
9+
Long commentId,
10+
Long postId,
11+
String userNickName,
12+
LocalDateTime createdAt,
13+
LocalDateTime updatedAt,
14+
CommentStatus status,
15+
String content
16+
) {
17+
public CocktailCommentResponseDto(CocktailComment cocktailcomment) {
18+
this(
19+
cocktailcomment.getId(),
20+
cocktailcomment.getCocktail().getId(),
21+
cocktailcomment.getUser().getNickname(),
22+
cocktailcomment.getCreatedAt(),
23+
cocktailcomment.getUpdatedAt(),
24+
cocktailcomment.getStatus(),
25+
cocktailcomment.getContent()
26+
);
27+
}
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.back.domain.cocktail.comment.dto;
2+
3+
import com.back.domain.post.comment.enums.CommentStatus;
4+
5+
public record CocktailCommentUpdateRequestDto(
6+
CommentStatus status,
7+
String content
8+
) {
9+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.back.domain.cocktail.comment.entity;
2+
3+
import com.back.domain.cocktail.entity.Cocktail;
4+
import com.back.domain.post.comment.enums.CommentStatus;
5+
import com.back.domain.user.entity.User;
6+
import jakarta.persistence.*;
7+
import lombok.AllArgsConstructor;
8+
import lombok.Builder;
9+
import lombok.Getter;
10+
import lombok.NoArgsConstructor;
11+
import org.springframework.data.annotation.CreatedDate;
12+
import org.springframework.data.annotation.LastModifiedDate;
13+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
14+
15+
import java.time.LocalDateTime;
16+
17+
@Entity
18+
@Getter
19+
@Table(name = "cocktailcomment")
20+
@EntityListeners(AuditingEntityListener.class)
21+
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
22+
@AllArgsConstructor
23+
@Builder
24+
public class CocktailComment {
25+
26+
@Id
27+
@GeneratedValue(strategy = GenerationType.IDENTITY)
28+
@Column(name = "id")
29+
private Long id;
30+
31+
// 해당 칵테일댓글이 작성된 게시글의 고유 식별자
32+
@ManyToOne(fetch = FetchType.LAZY)
33+
@JoinColumn(name = "cocktail_id")
34+
private Cocktail cocktail;
35+
36+
// 해당 칵테일댓글을 작성한 유저의 고유 식별자
37+
@ManyToOne(fetch = FetchType.LAZY)
38+
@JoinColumn(name = "user_id")
39+
private User user;
40+
41+
// 칵테일댓글 작성 날짜
42+
@CreatedDate
43+
private LocalDateTime createdAt;
44+
45+
// 칵테일댓글 수정 날짜
46+
@LastModifiedDate
47+
private LocalDateTime updatedAt;
48+
49+
// 칵테일댓글 게시 상태 (기본값: 공개)
50+
@Builder.Default
51+
@Enumerated(EnumType.STRING)
52+
@Column(name = "status", nullable = false)
53+
private CommentStatus status = CommentStatus.PUBLIC;
54+
55+
// 칵테일댓글 내용
56+
@Column(name = "content", nullable = false, columnDefinition = "TEXT")
57+
private String content;
58+
59+
public void updateStatus(CommentStatus status) {
60+
this.status = status;
61+
}
62+
63+
public void updateContent(String content) {
64+
this.content = content;
65+
}
66+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.back.domain.cocktail.comment.repository;
2+
3+
import com.back.domain.cocktail.comment.entity.CocktailComment;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
import java.util.List;
8+
9+
@Repository
10+
public interface CocktailCommentRepository extends JpaRepository<CocktailComment, Long> {
11+
12+
List<CocktailComment> findTop10ByCocktailIdOrderByIdDesc(Long cocktailId);
13+
14+
List<CocktailComment> findTop10ByCocktailIdAndIdLessThanOrderByIdDesc(Long cocktailId, Long lastId);
15+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.back.domain.cocktail.comment.service;
2+
3+
import com.back.domain.cocktail.comment.dto.CocktailCommentCreateRequestDto;
4+
import com.back.domain.cocktail.comment.dto.CocktailCommentResponseDto;
5+
import com.back.domain.cocktail.comment.dto.CocktailCommentUpdateRequestDto;
6+
import com.back.domain.cocktail.comment.entity.CocktailComment;
7+
import com.back.domain.cocktail.comment.repository.CocktailCommentRepository;
8+
import com.back.domain.cocktail.entity.Cocktail;
9+
import com.back.domain.cocktail.repository.CocktailRepository;
10+
import com.back.domain.post.comment.enums.CommentStatus;
11+
import com.back.domain.user.entity.User;
12+
import com.back.global.rq.Rq;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.stereotype.Service;
15+
import org.springframework.transaction.annotation.Transactional;
16+
17+
import java.util.List;
18+
19+
@Service
20+
@RequiredArgsConstructor
21+
public class CocktailCommentService {
22+
private final CocktailCommentRepository cocktailCommentRepository;
23+
private final CocktailRepository cocktailRepository;
24+
private final Rq rq;
25+
26+
// 칵테일 댓글 작성 로직
27+
@Transactional
28+
public CocktailCommentResponseDto createCocktailComment(Long cocktailId, CocktailCommentCreateRequestDto reqBody) {
29+
User user = rq.getActor();
30+
31+
Cocktail cocktail = cocktailRepository.findById(cocktailId)
32+
.orElseThrow(() -> new IllegalArgumentException("칵테일이 존재하지 않습니다. id=" + cocktailId));
33+
34+
CocktailComment cocktailComment = CocktailComment.builder()
35+
.cocktail(cocktail)
36+
.user(user)
37+
.content(reqBody.content())
38+
.build();
39+
40+
return new CocktailCommentResponseDto(cocktailCommentRepository.save(cocktailComment));
41+
}
42+
43+
// 칵테일 댓글 다건 조회 로직 (무한스크롤)
44+
@Transactional(readOnly = true)
45+
public List<CocktailCommentResponseDto> getCocktailComments(Long cocktailId, Long lastId) {
46+
if (lastId == null) {
47+
return cocktailCommentRepository.findTop10ByCocktailIdOrderByIdDesc(cocktailId)
48+
.stream()
49+
.map(CocktailCommentResponseDto::new)
50+
.toList();
51+
} else {
52+
return cocktailCommentRepository.findTop10ByCocktailIdAndIdLessThanOrderByIdDesc(cocktailId, lastId)
53+
.stream()
54+
.map(CocktailCommentResponseDto::new)
55+
.toList();
56+
}
57+
}
58+
59+
// 칵테일 댓글 단건 조회 로직
60+
@Transactional(readOnly = true)
61+
public CocktailCommentResponseDto getCocktailComment(Long cocktailId, Long cocktailCommentId) {
62+
CocktailComment cocktailComment = findByIdAndValidateCocktail(cocktailId, cocktailCommentId);
63+
64+
return new CocktailCommentResponseDto(cocktailComment);
65+
}
66+
67+
// 칵테일댓글 ID로 찾고, 칵테일과의 관계를 검증
68+
private CocktailComment findByIdAndValidateCocktail(Long cocktailId, Long cocktailCommentId) {
69+
CocktailComment cocktailComment = cocktailCommentRepository.findById(cocktailCommentId)
70+
.orElseThrow(() -> new IllegalArgumentException("댓글이 존재하지 않습니다. id=" + cocktailCommentId));
71+
72+
if (!cocktailComment.getCocktail().getId().equals(cocktailId)) {
73+
throw new IllegalStateException("댓글이 해당 게시글에 속하지 않습니다.");
74+
}
75+
return cocktailComment;
76+
}
77+
78+
// 칵테일댓글 수정 로직
79+
@Transactional
80+
public CocktailCommentResponseDto updateCocktailComment(Long cocktailId, Long cocktailCommentId, CocktailCommentUpdateRequestDto requestDto) {
81+
User user = rq.getActor();
82+
83+
CocktailComment cocktailComment = findByIdAndValidateCocktail(cocktailId, cocktailCommentId);
84+
85+
if (!cocktailComment.getUser().getId().equals(user.getId())) {
86+
throw new IllegalStateException("본인의 댓글만 수정할 수 있습니다.");
87+
}
88+
89+
cocktailComment.updateContent(requestDto.content());
90+
return new CocktailCommentResponseDto(cocktailComment);
91+
}
92+
93+
// 칵테일댓글 삭제 로직
94+
@Transactional
95+
public void deleteCocktailComment(Long cocktailId, Long cocktailCommentId) {
96+
User user = rq.getActor();
97+
98+
CocktailComment cocktailComment = findByIdAndValidateCocktail(cocktailId, cocktailCommentId);
99+
100+
if (!cocktailComment.getUser().getId().equals(user.getId())) {
101+
throw new IllegalStateException("본인의 댓글만 삭제할 수 있습니다.");
102+
}
103+
104+
cocktailComment.updateStatus(CommentStatus.DELETED); // soft delete 사용.
105+
}
106+
}
107+
108+
109+
110+
111+
112+

src/main/java/com/back/domain/cocktail/controller/CocktailController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.back.domain.cocktail.service.CocktailService;
88
import com.back.global.rsData.RsData;
99
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.tags.Tag;
1011
import lombok.RequiredArgsConstructor;
1112
import org.springframework.transaction.annotation.Transactional;
1213
import org.springframework.web.bind.annotation.*;
@@ -15,6 +16,7 @@
1516

1617
@RestController
1718
@RequestMapping("api/cocktails")
19+
@Tag(name = "ApiCocktailController", description = "API 칵테일 컨트롤러")
1820
@RequiredArgsConstructor
1921
public class CocktailController {
2022

src/main/java/com/back/domain/cocktail/controller/CocktailShareController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public ResponseEntity<RsData<Map<String, String>>> getShareLink(@PathVariable Lo
2424
.map(cocktail -> {
2525
Map<String, String> response = Map.of(
2626
// 공유 URL
27-
"url", "https://www.ssoul.or/cocktails/" + cocktail.getCocktailId(),
27+
"url", "https://www.ssoul.or/cocktails/" + cocktail.getId(),
2828
// 공유 제목
2929
"title", cocktail.getCocktailName(),
3030
// 공유 이미지 (선택)

src/main/java/com/back/domain/cocktail/dto/CocktailDetailResponseDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public CocktailDetailResponseDto(
3939
}
4040

4141
public CocktailDetailResponseDto(Cocktail cocktail) {
42-
this.cocktailId = cocktail.getCocktailId();
42+
this.cocktailId = cocktail.getId();
4343
this.cocktailName = cocktail.getCocktailName();
4444
this.alcoholStrength = cocktail.getAlcoholStrength();
4545
this.cocktailType = cocktail.getCocktailType();

0 commit comments

Comments
 (0)