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/api/review/CommentController.java b/src/main/java/likelion/MZConnent/api/review/CommentController.java new file mode 100644 index 0000000..8537a0a --- /dev/null +++ b/src/main/java/likelion/MZConnent/api/review/CommentController.java @@ -0,0 +1,36 @@ +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); + } + + @DeleteMapping("/api/reviews/{reviewId}/comments/{commentId}") + public ResponseEntity> 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/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/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/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) { } } + } 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..aa53d1e --- /dev/null +++ b/src/main/java/likelion/MZConnent/service/review/CommentService.java @@ -0,0 +1,82 @@ +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; +import java.util.Optional; + +@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(); + } + + // 후기 댓글 삭제 + @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("후기가 존재하지 않음."); + 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