From bbd0bd2cd89d385c4ddd305a5b0b1641aca62c05 Mon Sep 17 00:00:00 2001 From: gourderased <117848386+gourderased@users.noreply.github.com> Date: Fri, 5 Apr 2024 02:42:21 +0900 Subject: [PATCH] Main merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * hotfix: Swagger 경로 설정 * Chore: Swagger 환경변수 설정 * Chore: Swagger 환경변수 설정 * Feat: gitignore yml파일추가 * Feat: Swagger 주소 설정 * Hotfix: 스웨거 경로 설정 * Update README.md * Update README.md * Feat: 댓글 조회/작성 구현 * Feat: 댓글 작성, 조회 구현 * Fix: 디렉토리 에러 해결 * Fix: 게시글 조회 방식 수정 * Fix: dto 수정 * Fix: 날짜 표기 변경 * Refactor: 가방 API 멤버 관련 수정 * Refactor: #48 - memberId 삭제 및 주석 추가 * Refactor: #48 - 스웨거 어노테이션 추가 * Refactor: #48 - 스웨거 어노테이션 추가 --------- Co-authored-by: tjdgns8439 <100510247+tjdgns8439@users.noreply.github.com> Co-authored-by: Minhyeok Song <125117389+minhyeokDev@users.noreply.github.com> --- README.md | 2 +- .../tripy/domain/bag/BagController.java | 35 +++++++++--- .../example/tripy/domain/bag/BagService.java | 13 +++-- .../example/tripy/domain/comment/Comment.java | 45 +++++++++++++++ .../domain/comment/CommentController.java | 44 +++++++++++++++ .../domain/comment/CommentRepository.java | 12 ++++ .../tripy/domain/comment/CommentService.java | 56 +++++++++++++++++++ .../domain/comment/dto/CommentRequestDto.java | 15 +++++ .../comment/dto/CommentResponseDto.java | 29 ++++++++++ .../com/example/tripy/domain/post/Post.java | 4 +- .../tripy/domain/post/PostService.java | 7 ++- .../domain/post/dto/PostResponseDto.java | 4 +- .../travelplan/TravelPlanRepository.java | 2 +- .../tripy/global/utils/DateTimeConverter.java | 34 +++++++++++ 14 files changed, 283 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/example/tripy/domain/comment/Comment.java create mode 100644 src/main/java/com/example/tripy/domain/comment/CommentController.java create mode 100644 src/main/java/com/example/tripy/domain/comment/CommentRepository.java create mode 100644 src/main/java/com/example/tripy/domain/comment/CommentService.java create mode 100644 src/main/java/com/example/tripy/domain/comment/dto/CommentRequestDto.java create mode 100644 src/main/java/com/example/tripy/domain/comment/dto/CommentResponseDto.java create mode 100644 src/main/java/com/example/tripy/global/utils/DateTimeConverter.java diff --git a/README.md b/README.md index 0378030..d4d8905 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## 트리피 +## 클밋 ## MVP 핵심기능소개 diff --git a/src/main/java/com/example/tripy/domain/bag/BagController.java b/src/main/java/com/example/tripy/domain/bag/BagController.java index 89c5db8..85b4a98 100644 --- a/src/main/java/com/example/tripy/domain/bag/BagController.java +++ b/src/main/java/com/example/tripy/domain/bag/BagController.java @@ -7,6 +7,10 @@ import com.example.tripy.domain.material.dto.MaterialResponseDto.MaterialListByCountry; import com.example.tripy.global.common.dto.PageResponseDto; import com.example.tripy.global.common.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; @@ -17,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "여행 가방 API") @RequiredArgsConstructor @RestController @RequestMapping("/travel-bag") @@ -28,7 +33,10 @@ public class BagController { /** * [GET] 내 여행 가방 모두 불러오기 */ - @GetMapping("/members/bags/{memberId}") + @Operation(summary = "내 여행 가방 모두 불러오기", description = "여행 가방이 존재하는 목록을 불러옵니다.") + @Parameter(name = "page", description = "내 여행 가방 목록 페이지 번호, query string 입니다.") + @Parameter(name = "size", description = "내 여행 가방 목록 페이지 번호, query string 입니다.") + @GetMapping("/members/bags") public ApiResponse>> getBagsList( @RequestParam(value = "page") int page, @RequestParam(value = "size") int size) { return ApiResponse.onSuccess(bagService.getTravelBagExistsList(page, size)); @@ -37,32 +45,45 @@ public ApiResponse>> getBagsList( /** * [POST] 내 여행 일정 목록에서 해당하는 가방 목록 생성하기 */ + @Operation(summary = "내 여행 일정 목록에서 해당하는 가방 목록 생성하기", description = "여행 일정 목록에서 가방을 생성하기 위해 해당하는 가방 목록을 생성합니다.") + @Parameter(name = "travelPlanId", description = "여행 계획 Id, Path Variable 입니다.") @PostMapping("/members/bags/{travelPlanId}") public ApiResponse updateBagExists( @PathVariable(value = "travelPlanId") Long travelPlanId) { return ApiResponse.onSuccess(bagService.updateBagExists(travelPlanId)); } + /** + * [GET] 여행지별 준비물 불러오기 + */ + @Operation(summary = "여행지별 준비물 불러오기", description = "나라별 존재하는 추천 준비물을 불러옵니다.") + @Parameter(name = "countryName", description = "나라 이름, query string 입니다.") @GetMapping("/material-name") public ApiResponse getCountryMaterials( @RequestParam(value = "countryName") String countryName) { return ApiResponse.onSuccess(countryMaterialService.getCountryMaterials(countryName)); } + /** + * [POST] 여행 일정에 해당하는 개별 가방 생성(추가)하기 + */ + @Operation(summary = "여행 일정에 해당하는 개별 가방 생성(추가)하기", description = "여행 일정에 해당하는 캐리어, 크로스백과 같은 가방을 생성합니다.") + @Parameter(name = "travelPlanId", description = "여행 계획 Id, query string 입니다.") @PostMapping("/members/bags") public ApiResponse createBag(@RequestBody CreateBagRequest createBagRequest, @RequestParam Long travelPlanId) { return ApiResponse.onSuccess(bagService.addBag(createBagRequest, travelPlanId)); - } + } /** * [GET] 여행 가방 리스트와 가방 내 준비물 불러오기 */ - @GetMapping("/members/{memberId}/materials/{travelPlanId}") + @Operation(summary = "내 여행 가방 리스트와 가방 내 준비물 불러오기", description = "여행 가방 리스트와 그 가방에 존재하는 준비물을 불러오고, isChecked로 챙겼는지 확인합니다.") + @Parameter(name = "travelPlanId", description = "여행 계획 Id, Path Variable 입니다.") + @GetMapping("/members/materials/{travelPlanId}") public ApiResponse> getBagsListAndMaterialsByTravelPlan( - @PathVariable Long memberId, @PathVariable(value = "travelPlanId") Long travelPlanId) { - return ApiResponse.onSuccess( - bagService.getBagsListAndMaterialsByTravelPlan(memberId, travelPlanId)); - } + @PathVariable(value = "travelPlanId") Long travelPlanId) { + return ApiResponse.onSuccess(bagService.getBagsListAndMaterialsByTravelPlan(travelPlanId)); + } } diff --git a/src/main/java/com/example/tripy/domain/bag/BagService.java b/src/main/java/com/example/tripy/domain/bag/BagService.java index d0133e0..e9af0f2 100644 --- a/src/main/java/com/example/tripy/domain/bag/BagService.java +++ b/src/main/java/com/example/tripy/domain/bag/BagService.java @@ -58,8 +58,12 @@ public List setBagListSimpleInfo(List travelPlans // 내 일정에 맞는 가방 목록 모두 불러오기 public PageResponseDto> getTravelBagExistsList(int page, int size) { + // Member 관련 메서드가 추가되면 수정 예정 + Member member = memberRepository.findById(1L) + .orElseThrow(() -> new GeneralException(ErrorStatus._EMPTY_MEMBER)); + Pageable pageable = PageRequest.of(page, size); - Page result = travelPlanRepository.findAllByMemberIdAndBagExistsIsTrue(1L, + Page result = travelPlanRepository.findAllByMemberAndBagExistsIsTrue(member, pageable); return new PageResponseDto<>(result.getNumber(), result.getTotalPages(), @@ -100,12 +104,11 @@ public String addBag(CreateBagRequest createBagRequest, Long travelPlanId) { bagRepository.save(bag); return "가방 추가 완료"; - } + } - public List getBagsListAndMaterialsByTravelPlan(Long memberId, - Long travelPlanId) { + public List getBagsListAndMaterialsByTravelPlan(Long travelPlanId) { //Member 관련 메서드가 추가되면 수정 예정 - Member member = memberRepository.findById(memberId) + Member member = memberRepository.findById(1L) .orElseThrow(() -> new GeneralException(ErrorStatus._EMPTY_MEMBER)); TravelPlan travelPlan = travelPlanRepository.findByMemberAndIdAndBagExistsIsTrue(member, diff --git a/src/main/java/com/example/tripy/domain/comment/Comment.java b/src/main/java/com/example/tripy/domain/comment/Comment.java new file mode 100644 index 0000000..d1f2f05 --- /dev/null +++ b/src/main/java/com/example/tripy/domain/comment/Comment.java @@ -0,0 +1,45 @@ +package com.example.tripy.domain.comment; + +import com.example.tripy.domain.member.Member; +import com.example.tripy.domain.post.Post; +import com.example.tripy.global.utils.BaseTimeEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Builder +public class Comment extends BaseTimeEntity { + + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + private String content; + + @ManyToOne + private Member member; + + @ManyToOne + private Post post; + + public static Comment toEntity(Member member, Post post, String content) { + return Comment.builder() + .member(member) + .post(post) + .content(content) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/tripy/domain/comment/CommentController.java b/src/main/java/com/example/tripy/domain/comment/CommentController.java new file mode 100644 index 0000000..fb628e2 --- /dev/null +++ b/src/main/java/com/example/tripy/domain/comment/CommentController.java @@ -0,0 +1,44 @@ +package com.example.tripy.domain.comment; + +import com.example.tripy.domain.comment.dto.CommentRequestDto.CreateCommentRequest; +import com.example.tripy.domain.comment.dto.CommentResponseDto.GetCommentInfo; +import com.example.tripy.global.common.dto.PageResponseDto; +import com.example.tripy.global.common.response.ApiResponse; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RestController +public class CommentController { + + private final CommentService commentService; + + /** + * [POST] 댓글 작성 + */ + @PostMapping("api/{posts-id}/comments") + public ApiResponse createComment( + @PathVariable(value = "posts-id") Long postId, + @RequestBody CreateCommentRequest createCommentRequest + ) { + commentService.addComment(createCommentRequest.getContent(), postId); + return ApiResponse.onSuccess("댓글 작성에 성공했습니다."); + } + + /** + * [GET] 댓글 조회 + */ + @GetMapping("/api/{posts-id}/comments") + public ApiResponse>> findComments( + @RequestParam int page, @RequestParam int size, @PathVariable(value="posts-id") Long postsId + + ) { + return ApiResponse.onSuccess(commentService.findComments(page, size, postsId)); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/tripy/domain/comment/CommentRepository.java b/src/main/java/com/example/tripy/domain/comment/CommentRepository.java new file mode 100644 index 0000000..36c33a6 --- /dev/null +++ b/src/main/java/com/example/tripy/domain/comment/CommentRepository.java @@ -0,0 +1,12 @@ +package com.example.tripy.domain.comment; + +import com.example.tripy.domain.comment.Comment; +import com.example.tripy.domain.post.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentRepository extends JpaRepository { + + Page findByPost(Post post, Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/example/tripy/domain/comment/CommentService.java b/src/main/java/com/example/tripy/domain/comment/CommentService.java new file mode 100644 index 0000000..80fa55d --- /dev/null +++ b/src/main/java/com/example/tripy/domain/comment/CommentService.java @@ -0,0 +1,56 @@ +package com.example.tripy.domain.comment; + +import com.example.tripy.domain.comment.dto.CommentResponseDto.GetCommentInfo; +import com.example.tripy.domain.member.Member; +import com.example.tripy.domain.member.MemberRepository; +import com.example.tripy.domain.post.Post; +import com.example.tripy.domain.post.PostRepository; +import com.example.tripy.global.common.dto.PageResponseDto; +import com.example.tripy.global.common.response.code.status.ErrorStatus; +import com.example.tripy.global.common.response.exception.GeneralException; +import com.example.tripy.global.utils.DateTimeConverter; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CommentService { + + private final CommentRepository commentRepository; + private final MemberRepository memberRepository; + private final PostRepository postRepository; + + //댓글 추가 + public void addComment(String content, Long postId) { + + Member member = memberRepository.findById(1L) + .orElseThrow(() -> new GeneralException(ErrorStatus._EMPTY_MEMBER)); + + Post post = postRepository.findById(postId) + .orElseThrow(() -> new GeneralException(ErrorStatus._EMPTY_POST)); + + Comment comment = Comment.toEntity(member, post, content); + commentRepository.save(comment); + } + + //댓글 조회 + PageResponseDto> findComments(int page, int size, Long postsId) { + + Pageable pageable = PageRequest.of(page, size); + Post post = postRepository.findById(postsId) + .orElseThrow(() -> new GeneralException(ErrorStatus._EMPTY_POST)); + + Page commentList = commentRepository.findByPost(post, pageable); + + List dtoList = commentList.stream() + .map(comment -> GetCommentInfo.toDto(comment, comment.getMember(), + DateTimeConverter.convertToDisplayTime(comment.getCreatedAt()))) + .toList(); + + return new PageResponseDto<>(commentList.getNumber(), commentList.getTotalPages(), dtoList); + } +} diff --git a/src/main/java/com/example/tripy/domain/comment/dto/CommentRequestDto.java b/src/main/java/com/example/tripy/domain/comment/dto/CommentRequestDto.java new file mode 100644 index 0000000..42749d6 --- /dev/null +++ b/src/main/java/com/example/tripy/domain/comment/dto/CommentRequestDto.java @@ -0,0 +1,15 @@ +package com.example.tripy.domain.comment.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class CommentRequestDto { + + @AllArgsConstructor + @NoArgsConstructor + @Getter + public static class CreateCommentRequest { + private String content; + } +} diff --git a/src/main/java/com/example/tripy/domain/comment/dto/CommentResponseDto.java b/src/main/java/com/example/tripy/domain/comment/dto/CommentResponseDto.java new file mode 100644 index 0000000..e4fc023 --- /dev/null +++ b/src/main/java/com/example/tripy/domain/comment/dto/CommentResponseDto.java @@ -0,0 +1,29 @@ +package com.example.tripy.domain.comment.dto; + +import com.example.tripy.domain.comment.Comment; +import com.example.tripy.domain.member.Member; +import lombok.Builder; +import lombok.Getter; + +public class CommentResponseDto { + + @Getter + @Builder + public static class GetCommentInfo { + + private String nickname; + private String profileImageUrl; + private String content; + private String createdAt; + + public static GetCommentInfo toDto(Comment comment, Member member, String createdAt) { + return GetCommentInfo.builder() + .nickname(member.getNickName()) + .profileImageUrl(member.getProfileImgUrl()) + .content(comment.getContent()) + .createdAt(createdAt) + .build(); + + } + } +} diff --git a/src/main/java/com/example/tripy/domain/post/Post.java b/src/main/java/com/example/tripy/domain/post/Post.java index bfae2ba..7cf4a0a 100644 --- a/src/main/java/com/example/tripy/domain/post/Post.java +++ b/src/main/java/com/example/tripy/domain/post/Post.java @@ -38,10 +38,10 @@ public class Post extends BaseTimeEntity { private String content; @ColumnDefault("0") - private Long view; + private Long view = 0L; @ColumnDefault("0") - private Integer recommendationCount; + private Integer recommendationCount = 0; @ColumnDefault("0") private int rank; diff --git a/src/main/java/com/example/tripy/domain/post/PostService.java b/src/main/java/com/example/tripy/domain/post/PostService.java index ea80935..ab1baba 100644 --- a/src/main/java/com/example/tripy/domain/post/PostService.java +++ b/src/main/java/com/example/tripy/domain/post/PostService.java @@ -17,6 +17,7 @@ import com.example.tripy.global.common.response.exception.GeneralException; import jakarta.transaction.Transactional; import java.util.List; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -162,6 +163,10 @@ public GetPostDetailInfo findPost(Long postsId) { List fileUrls = postFileService.findImageFileUrlsByPostAndFileType(post, FileType.FILE); List postTags = postTagService.findTagsStringByPost(post); - return GetPostDetailInfo.toDto(post, imageUrls, fileUrls, postTags); + Long travelPlanId = Optional.ofNullable(post.getTravelPlan()) + .map(TravelPlan::getId) + .orElse(null); + + return GetPostDetailInfo.toDto(post, travelPlanId, imageUrls, fileUrls, postTags); } } \ No newline at end of file diff --git a/src/main/java/com/example/tripy/domain/post/dto/PostResponseDto.java b/src/main/java/com/example/tripy/domain/post/dto/PostResponseDto.java index 969be00..fa7dfd0 100644 --- a/src/main/java/com/example/tripy/domain/post/dto/PostResponseDto.java +++ b/src/main/java/com/example/tripy/domain/post/dto/PostResponseDto.java @@ -50,12 +50,12 @@ public static class GetPostDetailInfo { private String createdAt; - public static GetPostDetailInfo toDto(Post post, List imgUrls, List fileUrls, List postTags) { + public static GetPostDetailInfo toDto(Post post, Long travelPlanId, List imgUrls, List fileUrls, List postTags) { return GetPostDetailInfo.builder() .postId(post.getId()) .title(post.getTitle()) .nickname(post.getMember().getNickName()) - .travelPlanId(post.getTravelPlan().getId()) + .travelPlanId(travelPlanId) .content(post.getContent()) .imgUrls(imgUrls) .fileUrls(fileUrls) diff --git a/src/main/java/com/example/tripy/domain/travelplan/TravelPlanRepository.java b/src/main/java/com/example/tripy/domain/travelplan/TravelPlanRepository.java index 86fea0f..53f8738 100644 --- a/src/main/java/com/example/tripy/domain/travelplan/TravelPlanRepository.java +++ b/src/main/java/com/example/tripy/domain/travelplan/TravelPlanRepository.java @@ -10,7 +10,7 @@ public interface TravelPlanRepository extends JpaRepository { Optional findByMemberAndIdAndBagExistsIsFalse(Member member, Long id); - Page findAllByMemberIdAndBagExistsIsTrue(Long memberId, Pageable pageable); + Page findAllByMemberAndBagExistsIsTrue(Member member, Pageable pageable); Optional findByMemberAndIdAndBagExistsIsTrue(Member member, Long id); diff --git a/src/main/java/com/example/tripy/global/utils/DateTimeConverter.java b/src/main/java/com/example/tripy/global/utils/DateTimeConverter.java new file mode 100644 index 0000000..df05cab --- /dev/null +++ b/src/main/java/com/example/tripy/global/utils/DateTimeConverter.java @@ -0,0 +1,34 @@ +package com.example.tripy.global.utils; + + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +public class DateTimeConverter { + + public static String convertToDisplayTime(LocalDateTime dateTime) { + LocalDateTime currentTime = LocalDateTime.now(); + long minutes = ChronoUnit.MINUTES.between(dateTime, currentTime); + long hours = ChronoUnit.HOURS.between(dateTime, currentTime); + + if (minutes < 60) { + return minutes + "분 전"; + } else if (hours < 24) { + return hours + "시간 전"; + } else + return dateTime.format(DateTimeFormatter.ofPattern("yyyy.MM.dd")); + } + + public static String convertDoubleToStringTime(double totalSeconds) { + int seconds = (int) totalSeconds; + int hours = (seconds / 3600); + int minutes = (seconds % 3600) / 60; + int remainingSeconds = seconds % 60; + + String hoursString = (hours < 100) ? String.format("%02d", hours) : String.format("%03d", hours); + String totalTime = String.format("%s:%02d:%02d", hoursString, minutes, remainingSeconds); + + return totalTime; + } +} \ No newline at end of file