From 4406b2163a8244e80dba76448b8ddc8fd9ebdf19 Mon Sep 17 00:00:00 2001 From: chaeyoungeee Date: Wed, 31 Jul 2024 00:16:20 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=ED=9B=84=EA=B8=B0=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=9E=91=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 후기 댓글 작성 기능 추가함 --- .../api/review/CommentController.java | 30 ++++++++++ .../dto/member/MemberProfileDto.java | 2 + .../review/request/SaveCommentRequest.java | 14 +++++ .../review/response/SaveCommentResponse.java | 25 ++++++++ .../service/review/CommentService.java | 57 +++++++++++++++++++ .../service/review/CommentServiceTest.java | 4 ++ 6 files changed, 132 insertions(+) create mode 100644 src/main/java/likelion/MZConnent/api/review/CommentController.java create mode 100644 src/main/java/likelion/MZConnent/dto/review/request/SaveCommentRequest.java create mode 100644 src/main/java/likelion/MZConnent/dto/review/response/SaveCommentResponse.java create mode 100644 src/main/java/likelion/MZConnent/service/review/CommentService.java create mode 100644 src/test/java/likelion/MZConnent/service/review/CommentServiceTest.java diff --git a/src/main/java/likelion/MZConnent/api/review/CommentController.java b/src/main/java/likelion/MZConnent/api/review/CommentController.java new file mode 100644 index 0000000..87c4eb6 --- /dev/null +++ b/src/main/java/likelion/MZConnent/api/review/CommentController.java @@ -0,0 +1,30 @@ +package likelion.MZConnent.api.review; + +import likelion.MZConnent.dto.review.request.SaveCommentRequest; +import likelion.MZConnent.dto.review.response.SaveCommentResponse; +import likelion.MZConnent.jwt.principle.UserPrinciple; +import likelion.MZConnent.service.review.CommentService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequiredArgsConstructor +@Slf4j +public class CommentController { + private final CommentService commentService; + + @PostMapping("/api/reviews/{reviewId}/comments") + public ResponseEntity saveComment(@RequestBody Map request, @PathVariable("reviewId") Long reviewId, @AuthenticationPrincipal UserPrinciple userPrinciple) { + SaveCommentResponse comment = commentService.saveComment(userPrinciple.getEmail(), reviewId, request.get("content")); + + log.info("댓글 작성: {}", comment); + + return ResponseEntity.ok(comment); + } + +} diff --git a/src/main/java/likelion/MZConnent/dto/member/MemberProfileDto.java b/src/main/java/likelion/MZConnent/dto/member/MemberProfileDto.java index bfc177a..ad6b259 100644 --- a/src/main/java/likelion/MZConnent/dto/member/MemberProfileDto.java +++ b/src/main/java/likelion/MZConnent/dto/member/MemberProfileDto.java @@ -2,6 +2,7 @@ import likelion.MZConnent.domain.member.Member; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -12,6 +13,7 @@ public class MemberProfileDto { private String username; private String profileImageUrl = ""; // TODO: 기본 이미지로 초기화 + @Builder public MemberProfileDto(Member member) { this.userId = member.getId(); this.username = member.getUsername(); diff --git a/src/main/java/likelion/MZConnent/dto/review/request/SaveCommentRequest.java b/src/main/java/likelion/MZConnent/dto/review/request/SaveCommentRequest.java new file mode 100644 index 0000000..2ab90d1 --- /dev/null +++ b/src/main/java/likelion/MZConnent/dto/review/request/SaveCommentRequest.java @@ -0,0 +1,14 @@ +package likelion.MZConnent.dto.review.request; + +import likelion.MZConnent.domain.member.Member; +import likelion.MZConnent.domain.review.ReviewComment; +import likelion.MZConnent.dto.review.response.SaveCommentResponse; +import lombok.*; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class SaveCommentRequest { + private Long reviewId; + private String content; +} diff --git a/src/main/java/likelion/MZConnent/dto/review/response/SaveCommentResponse.java b/src/main/java/likelion/MZConnent/dto/review/response/SaveCommentResponse.java new file mode 100644 index 0000000..a35c570 --- /dev/null +++ b/src/main/java/likelion/MZConnent/dto/review/response/SaveCommentResponse.java @@ -0,0 +1,25 @@ +package likelion.MZConnent.dto.review.response; + +import likelion.MZConnent.domain.review.ReviewComment; +import likelion.MZConnent.dto.member.MemberProfileDto; +import lombok.*; + +@Getter +@ToString +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class SaveCommentResponse { + private Long commentId; + private Long reviewId; + private MemberProfileDto commenter; + private String content; + + + @Builder + public SaveCommentResponse(ReviewComment comment) { + this.commentId = comment.getCommentId(); + this.reviewId = comment.getReview().getReviewId(); + this.commenter = MemberProfileDto.builder() + .member(comment.getMember()).build(); + this.content = comment.getContent(); + } +} diff --git a/src/main/java/likelion/MZConnent/service/review/CommentService.java b/src/main/java/likelion/MZConnent/service/review/CommentService.java new file mode 100644 index 0000000..d0b4a2d --- /dev/null +++ b/src/main/java/likelion/MZConnent/service/review/CommentService.java @@ -0,0 +1,57 @@ +package likelion.MZConnent.service.review; + +import likelion.MZConnent.domain.member.Member; +import likelion.MZConnent.domain.review.Review; +import likelion.MZConnent.domain.review.ReviewComment; +import likelion.MZConnent.dto.culture.request.CreateCultureRequest; +import likelion.MZConnent.dto.review.request.SaveCommentRequest; +import likelion.MZConnent.dto.review.response.SaveCommentResponse; +import likelion.MZConnent.repository.member.MemberRepository; +import likelion.MZConnent.repository.review.ReviewCommentRepository; +import likelion.MZConnent.repository.review.ReviewRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Service +@Slf4j +@RequiredArgsConstructor +public class CommentService { + private final MemberRepository memberRepository; + private final ReviewCommentRepository reviewCommentRepository; + private final ReviewRepository reviewRepository; + + @Transactional + public SaveCommentResponse saveComment(String email, Long reviewId, String content) { + Member member = findMemberByEmail(email); + Review review = findReviewById(reviewId); + + ReviewComment comment = ReviewComment.builder() + .content(content) + .createdDate(LocalDateTime.now()) + .member(member) + .review(review) + .build(); + + reviewCommentRepository.save(comment); + + return SaveCommentResponse.builder().comment(comment).build(); + } + + private Review findReviewById(Long reviewId ) { + return reviewRepository.findById(reviewId).orElseThrow(() -> { + log.info("후기가 존재하지 않음."); + return new IllegalArgumentException("후기가 존재하지 않습니다."); + }); + } + + private Member findMemberByEmail(String email) { + return memberRepository.findByEmail(email).orElseThrow(() -> { + log.info("회원이 존재하지 않음."); + return new IllegalArgumentException("회원이 존재하지 않습니다."); + }); + } +} diff --git a/src/test/java/likelion/MZConnent/service/review/CommentServiceTest.java b/src/test/java/likelion/MZConnent/service/review/CommentServiceTest.java new file mode 100644 index 0000000..d2e23d5 --- /dev/null +++ b/src/test/java/likelion/MZConnent/service/review/CommentServiceTest.java @@ -0,0 +1,4 @@ +import static org.junit.jupiter.api.Assertions.*; +class CommentServiceTest { + +} \ No newline at end of file From 11cfe1b49a3822235ca163e27e7ee9852b04b492 Mon Sep 17 00:00:00 2001 From: chaeyoungeee Date: Wed, 31 Jul 2024 00:52:41 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=ED=9B=84=EA=B8=B0=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/review/CommentController.java | 6 +++++ .../service/review/CommentService.java | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/main/java/likelion/MZConnent/api/review/CommentController.java b/src/main/java/likelion/MZConnent/api/review/CommentController.java index 87c4eb6..8537a0a 100644 --- a/src/main/java/likelion/MZConnent/api/review/CommentController.java +++ b/src/main/java/likelion/MZConnent/api/review/CommentController.java @@ -27,4 +27,10 @@ public ResponseEntity saveComment(@RequestBody Map> deleteComment(@PathVariable("commentId") Long commentId, @AuthenticationPrincipal UserPrinciple userPrinciple) { + commentService.deleteComment(userPrinciple.getEmail(), commentId); + return ResponseEntity.ok(Map.of("message", "댓글 삭제 성공")); + } + } diff --git a/src/main/java/likelion/MZConnent/service/review/CommentService.java b/src/main/java/likelion/MZConnent/service/review/CommentService.java index d0b4a2d..aa53d1e 100644 --- a/src/main/java/likelion/MZConnent/service/review/CommentService.java +++ b/src/main/java/likelion/MZConnent/service/review/CommentService.java @@ -15,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.Optional; @Service @Slf4j @@ -24,6 +25,8 @@ public class CommentService { private final ReviewCommentRepository reviewCommentRepository; private final ReviewRepository reviewRepository; + + // 후기 댓글 작성 @Transactional public SaveCommentResponse saveComment(String email, Long reviewId, String content) { Member member = findMemberByEmail(email); @@ -41,6 +44,25 @@ public SaveCommentResponse saveComment(String email, Long reviewId, String conte return SaveCommentResponse.builder().comment(comment).build(); } + // 후기 댓글 삭제 + @Transactional + public void deleteComment(String email, Long commentId) { + ReviewComment comment = findCommentById(commentId); + Member member = findMemberByEmail(email); + Review review = findReviewById(comment.getReview().getReviewId()); + + member.getReviewComments().remove(comment); + review.getReviewComments().remove(comment); + reviewCommentRepository.delete(comment); + } + + private ReviewComment findCommentById(Long commentId) { + return reviewCommentRepository.findById(commentId).orElseThrow(() -> { + log.info("해당 후기 댓글이 존재하지 않음."); + return new IllegalArgumentException("해당 후기 댓글이 존재하지 않습니다."); + }); + } + private Review findReviewById(Long reviewId ) { return reviewRepository.findById(reviewId).orElseThrow(() -> { log.info("후기가 존재하지 않음."); @@ -48,10 +70,13 @@ private Review findReviewById(Long reviewId ) { }); } + private Member findMemberByEmail(String email) { return memberRepository.findByEmail(email).orElseThrow(() -> { log.info("회원이 존재하지 않음."); return new IllegalArgumentException("회원이 존재하지 않습니다."); }); } + + } From 72ae8eded8d86a1381a96979d848141424de365f Mon Sep 17 00:00:00 2001 From: chaeyoungeee Date: Wed, 31 Jul 2024 01:13:13 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC/?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=A4=91=EB=B3=B5=20=EC=A0=90?= =?UTF-8?q?=EA=B2=80=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 회원가입 시 한 번에 점검하던 이메일, 닉네임 중복 점검을 각각 이메일 점검 api, 닉네임 점검 api로 분리함 --- .../MZConnent/api/member/LoginController.java | 16 ++++++++-- .../MZConnent/config/SecurityConfig.java | 2 +- .../service/member/LoginService.java | 29 +++++++++++-------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/main/java/likelion/MZConnent/api/member/LoginController.java b/src/main/java/likelion/MZConnent/api/member/LoginController.java index 3efe560..aa84660 100644 --- a/src/main/java/likelion/MZConnent/api/member/LoginController.java +++ b/src/main/java/likelion/MZConnent/api/member/LoginController.java @@ -33,7 +33,7 @@ public ResponseEntity register(@Valid @RequestBody CreateMemberRequest request, Long memberId = loginService.createUser(request); log.info("회원가입 성공: {}", memberId); - return ResponseEntity.ok("회원가입 성공"); + return ResponseEntity.ok(Map.of("message", "회원가입 성공")); } @PostMapping("/api/auth/login") @@ -50,7 +50,7 @@ public ResponseEntity login(@Valid @RequestBody LoginMemberRequest request, Bind } @PostMapping("/api/auth/logout") - public ResponseEntity logout(@AuthenticationPrincipal UserPrinciple userPrinciple, @RequestHeader("Authorization") String authHeader) { + public ResponseEntity> logout(@AuthenticationPrincipal UserPrinciple userPrinciple, @RequestHeader("Authorization") String authHeader) { String email = userPrinciple.getEmail(); log.info("로그아웃 이메일: {}", email); @@ -60,4 +60,16 @@ public ResponseEntity logout(@AuthenticationPrincipal UserPrinciple userPrincipl return ResponseEntity.ok(Map.of("message", "로그아웃 성공")); } + + @GetMapping("/api/auth/email") + public ResponseEntity> checkDuplicateEmail(@RequestParam("email") String email) { + loginService.checkDuplicateEmail(email); + return ResponseEntity.ok(Map.of("message", "이메일 중복 점검 성공")); + } + + @GetMapping("/api/auth/username") + public ResponseEntity> checkDuplicateUsername(@RequestParam("username") String username) { + loginService.checkDuplicateUsername(username); + return ResponseEntity.ok(Map.of("message", "닉네임 중복 점검 성공")); + } } diff --git a/src/main/java/likelion/MZConnent/config/SecurityConfig.java b/src/main/java/likelion/MZConnent/config/SecurityConfig.java index 0aba1ad..96e98e8 100644 --- a/src/main/java/likelion/MZConnent/config/SecurityConfig.java +++ b/src/main/java/likelion/MZConnent/config/SecurityConfig.java @@ -44,7 +44,7 @@ public class SecurityConfig { // 아무나 접근 가능한 URI private final String[] permitAllUrl = {"/error", - "/api/auth/login", // 회원 + "/api/auth/login", "/api/auth/email", "/api/auth/username", // 회원 "/api/categories/culture", "/api/cultures", "/api/cultures/**", // 문화 "/api/reviews", // 후기 "/api/categories/region", "/api/clubs/list", diff --git a/src/main/java/likelion/MZConnent/service/member/LoginService.java b/src/main/java/likelion/MZConnent/service/member/LoginService.java index 379622f..8286e20 100644 --- a/src/main/java/likelion/MZConnent/service/member/LoginService.java +++ b/src/main/java/likelion/MZConnent/service/member/LoginService.java @@ -45,18 +45,6 @@ public Long createUser(CreateMemberRequest request) { // 비밀번호 정책에 맞는지 점검 checkPasswordPolicy(request.getPassword()); - // 이미 등록된 이메일인지 점검 - if (memberRepository.existsByEmail(request.getEmail())) { - log.info("이미 등록된 이메일={}", request.getEmail()); - throw new IllegalArgumentException("이미 등록된 이메일입니다."); - } - - // 중복되는 닉네임인지 점검 - if (memberRepository.existsByUsername(request.getUsername())) { - log.info("중복되는 닉네임={}", request.getEmail()); - throw new IllegalArgumentException("중복되는 닉네임입니다."); - } - Member member = Member.builder() .email(request.getEmail()) .password(passwordEncoder.encode(request.getPassword())) // 비밀번호 암호화 @@ -121,6 +109,22 @@ public void logoout(String accessToken, String email) { accessTokenBlackList.setBlackList(accessToken, email); } + // 이메일 중복 점검 + public void checkDuplicateEmail(String email) { + if (memberRepository.existsByEmail(email)) { + log.info("이미 등록된 이메일={}", email); + throw new IllegalArgumentException("이미 등록된 이메일입니다."); + } + } + + // 닉네임 중복 점검 + public void checkDuplicateUsername(String username) { + if (memberRepository.existsByUsername(username)) { + log.info("중복되는 닉네임={}", username); + throw new IllegalArgumentException("중복되는 닉네임입니다."); + } + } + // 비밀번호 정책에 맞는지 점검하는 함수 private void checkPasswordPolicy(String password) { @@ -146,4 +150,5 @@ private void checkPassword(String password, Member member) { } } + }