diff --git a/src/main/java/treehouse/server/api/invitation/presentation/InvitationApi.java b/src/main/java/treehouse/server/api/invitation/presentation/InvitationApi.java index 2e47210..39b068f 100644 --- a/src/main/java/treehouse/server/api/invitation/presentation/InvitationApi.java +++ b/src/main/java/treehouse/server/api/invitation/presentation/InvitationApi.java @@ -25,7 +25,7 @@ public class InvitationApi { private final InvitationService invitationService; @GetMapping("/invitation") - @Operation(summary = "초대장 조회 \uD83D\uDD11", description = "내가 받은 초대장을 조회합니다.") + @Operation(summary = "초대장 조회 \uD83D\uDD11✅", description = "내가 받은 초대장을 조회합니다.") public CommonResponse getInvitations( @AuthMember @Parameter(hidden = true) User user ) { @@ -33,14 +33,14 @@ public CommonResponse getInvitations( } @GetMapping("/availableInvitation") - @Operation(summary = "소유한 초대장 개수 및 게이지 조회 \uD83D\uDD11", description = "소유한 초대장 개수 및 게이지를 조회합니다.") + @Operation(summary = "소유한 초대장 개수 및 게이지 조회 \uD83D\uDD11✅", description = "소유한 초대장 개수 및 게이지를 조회합니다.") public CommonResponse getAvailableInvitation(@AuthMember @Parameter(hidden = true) User user){ return CommonResponse.onSuccess(invitationService.getMyInvitationInfo(user)); } @PostMapping("/invitations/accept") - @Operation(summary = "초대장을 수락할지 거절할지 결정 \uD83D\uDD11", description = "초대장을 수락할지 거절할지 결정하는 API 입니다.") + @Operation(summary = "초대장을 수락할지 거절할지 결정 \uD83D\uDD11✅", description = "초대장을 수락할지 거절할지 결정하는 API 입니다.") public CommonResponse acceptInvitation( @AuthMember @Parameter(hidden = true) User user, @RequestBody InvitationRequestDTO.invitationAcceptDecision request) { return CommonResponse.onSuccess(invitationService.decisionInvitation(user, request)); diff --git a/src/main/java/treehouse/server/api/member/business/MemberMapper.java b/src/main/java/treehouse/server/api/member/business/MemberMapper.java index b52f195..4801ff3 100644 --- a/src/main/java/treehouse/server/api/member/business/MemberMapper.java +++ b/src/main/java/treehouse/server/api/member/business/MemberMapper.java @@ -25,4 +25,13 @@ public static MemberResponseDTO.registerMember toRegister(Long treehouseId, Memb .treehouseId(treehouseId) // treehouseId는 관련 기능 구현 후 변경 예정 .build(); } + + public static MemberResponseDTO.getMemberProfile toGetMemberProfile(Member member) { + return MemberResponseDTO.getMemberProfile.builder() + .memberId(member.getId()) + .memberName(member.getName()) + .memberProfileImageUrl(member.getProfileImageUrl()) + .memberBranch(3) // Branch 기능 개발 이후 변경 예정 + .build(); + } } diff --git a/src/main/java/treehouse/server/api/member/presentation/MemberApi.java b/src/main/java/treehouse/server/api/member/presentation/MemberApi.java index fdaf6c0..04c8a0f 100644 --- a/src/main/java/treehouse/server/api/member/presentation/MemberApi.java +++ b/src/main/java/treehouse/server/api/member/presentation/MemberApi.java @@ -28,7 +28,7 @@ public class MemberApi { private final MemberService memberService; @PostMapping("/register") - @Operation(summary = "회원가입", description = "트리하우스 멤버로 가입합니다.") + @Operation(summary = "트리하우스 회원가입 \uD83D\uDD11✅", description = "트리하우스 멤버로 가입합니다.") public CommonResponse registerTreehouseMember( @RequestBody final MemberRequestDTO.registerMember request, @AuthMember @Parameter(hidden = true) User user diff --git a/src/main/java/treehouse/server/api/member/presentation/dto/MemberResponseDTO.java b/src/main/java/treehouse/server/api/member/presentation/dto/MemberResponseDTO.java index be800ba..728cf96 100644 --- a/src/main/java/treehouse/server/api/member/presentation/dto/MemberResponseDTO.java +++ b/src/main/java/treehouse/server/api/member/presentation/dto/MemberResponseDTO.java @@ -13,4 +13,15 @@ public static class registerMember { private Long userId; private Long treehouseId; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class getMemberProfile { + private Long memberId; + private String memberName; + private String memberProfileImageUrl; + private Integer memberBranch; + } } diff --git a/src/main/java/treehouse/server/api/notification/presentation/NotificationApi.java b/src/main/java/treehouse/server/api/notification/presentation/NotificationApi.java index 279dba0..0bb81d7 100644 --- a/src/main/java/treehouse/server/api/notification/presentation/NotificationApi.java +++ b/src/main/java/treehouse/server/api/notification/presentation/NotificationApi.java @@ -24,7 +24,7 @@ public class NotificationApi { private final NotificationService notificationService; @GetMapping("/users/notifications") - @Operation(summary = "알림 조회 \uD83D\uDD11", description = "사용자의 알림을 조회합니다.") + @Operation(summary = "알림 조회 \uD83D\uDD11✅", description = "사용자의 알림을 조회합니다.") public CommonResponse getNotifications( @AuthMember @Parameter(hidden = true) User user ){ diff --git a/src/main/java/treehouse/server/api/post/business/PostMapper.java b/src/main/java/treehouse/server/api/post/business/PostMapper.java new file mode 100644 index 0000000..53bae2f --- /dev/null +++ b/src/main/java/treehouse/server/api/post/business/PostMapper.java @@ -0,0 +1,26 @@ +package treehouse.server.api.post.business; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import treehouse.server.api.post.presentation.dto.PostResponseDTO; +import treehouse.server.api.member.business.MemberMapper; +import treehouse.server.global.common.TimeFormatter; +import treehouse.server.global.entity.post.Post; +import treehouse.server.global.entity.post.PostImage; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PostMapper { + + public static PostResponseDTO.getPostDetails toGetPostDetails(Post post) { + return PostResponseDTO.getPostDetails.builder() + .memberProfile(MemberMapper.toGetMemberProfile(post.getWriter())) + .postId(post.getId()) + .context(post.getContent()) + .pictureUrlList(post.getPostImageList().stream() + .map(PostImage::getImageUrl).toList() + ) +// .reactionList() // Reaction 기능 개발 이후 수정 + .postedAt(TimeFormatter.format(post.getCreatedAt())) + .build(); + } +} diff --git a/src/main/java/treehouse/server/api/post/business/PostService.java b/src/main/java/treehouse/server/api/post/business/PostService.java new file mode 100644 index 0000000..e99d3f6 --- /dev/null +++ b/src/main/java/treehouse/server/api/post/business/PostService.java @@ -0,0 +1,33 @@ +package treehouse.server.api.post.business; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import treehouse.server.api.post.implement.PostCommandAdapter; +import treehouse.server.api.post.implement.PostQueryAdapter; +import treehouse.server.api.post.presentation.dto.PostResponseDTO; +import treehouse.server.global.entity.User.User; +import treehouse.server.global.entity.post.Post; + +@Service +@AllArgsConstructor +@Slf4j +public class PostService { + + private final PostCommandAdapter postCommandAdapter; + private final PostQueryAdapter postQueryAdapter; + + /** + * 게시글 상세조회 + * @param user + * @param postId + * @param treehouseId - 게시글 정보에 표시할 memberBranch을 계산하고 감정표현의 isPushed 상태를 반환하기 위해 user와 treehouseId 사용 + * @return PostResponseDTO.getPostDetails + */ + @Transactional(readOnly = true) + public PostResponseDTO.getPostDetails getPostDetails(User user, Long postId, Long treehouseId){ + Post post = postQueryAdapter.findById(postId); + return PostMapper.toGetPostDetails(post); + } +} diff --git a/src/main/java/treehouse/server/api/post/implement/PostCommandAdapter.java b/src/main/java/treehouse/server/api/post/implement/PostCommandAdapter.java new file mode 100644 index 0000000..af74dcd --- /dev/null +++ b/src/main/java/treehouse/server/api/post/implement/PostCommandAdapter.java @@ -0,0 +1,9 @@ +package treehouse.server.api.post.implement; + +import lombok.RequiredArgsConstructor; +import treehouse.server.global.annotations.Adapter; + +@Adapter +@RequiredArgsConstructor +public class PostCommandAdapter { +} diff --git a/src/main/java/treehouse/server/api/post/implement/PostQueryAdapter.java b/src/main/java/treehouse/server/api/post/implement/PostQueryAdapter.java new file mode 100644 index 0000000..d66ddc6 --- /dev/null +++ b/src/main/java/treehouse/server/api/post/implement/PostQueryAdapter.java @@ -0,0 +1,19 @@ +package treehouse.server.api.post.implement; + +import lombok.RequiredArgsConstructor; +import treehouse.server.api.post.persistence.PostRepository; +import treehouse.server.global.annotations.Adapter; +import treehouse.server.global.entity.post.Post; +import treehouse.server.global.exception.GlobalErrorCode; +import treehouse.server.global.exception.ThrowClass.PostException; + +@Adapter +@RequiredArgsConstructor +public class PostQueryAdapter { + + private final PostRepository postRepository; + + public Post findById(Long postId) { + return postRepository.findById(postId).orElseThrow(() -> new PostException(GlobalErrorCode.POST_NOT_FOUND)); + } +} diff --git a/src/main/java/treehouse/server/api/post/persistence/PostRepository.java b/src/main/java/treehouse/server/api/post/persistence/PostRepository.java new file mode 100644 index 0000000..4278c5f --- /dev/null +++ b/src/main/java/treehouse/server/api/post/persistence/PostRepository.java @@ -0,0 +1,7 @@ +package treehouse.server.api.post.persistence; + +import org.springframework.data.jpa.repository.JpaRepository; +import treehouse.server.global.entity.post.Post; + +public interface PostRepository extends JpaRepository { +} diff --git a/src/main/java/treehouse/server/api/post/presentation/PostApi.java b/src/main/java/treehouse/server/api/post/presentation/PostApi.java new file mode 100644 index 0000000..f7b7beb --- /dev/null +++ b/src/main/java/treehouse/server/api/post/presentation/PostApi.java @@ -0,0 +1,38 @@ +package treehouse.server.api.post.presentation; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import treehouse.server.api.post.business.PostService; +import treehouse.server.api.post.presentation.dto.PostResponseDTO; +import treehouse.server.global.common.CommonResponse; +import treehouse.server.global.entity.User.User; +import treehouse.server.global.security.handler.annotation.AuthMember; + +@RestController +@RequiredArgsConstructor +@Slf4j +@Validated +@Tag(name = "\uD83D\uDCF0 Feed API", description = "트리하우스 피드 관련 API 입니다. 게시글 작성, 피드 조회 등의 API가 포함됩니다.") +@RequestMapping("/treehouses/{treehouseId}/feeds") +public class PostApi { + + private final PostService postService; + + @GetMapping("/posts/{postId}") + @Operation(summary = "게시글 상세조회 \uD83D\uDD11✅", description = "특정 게시글의 상세정보를 조회합니다.") + public CommonResponse getPostDetails( + @PathVariable Long treehouseId, + @PathVariable Long postId, + @AuthMember @Parameter(hidden = true) User user + ){ + return CommonResponse.onSuccess(postService.getPostDetails(user, postId, treehouseId)); + } +} diff --git a/src/main/java/treehouse/server/api/post/presentation/dto/PostResponseDTO.java b/src/main/java/treehouse/server/api/post/presentation/dto/PostResponseDTO.java new file mode 100644 index 0000000..4ec227b --- /dev/null +++ b/src/main/java/treehouse/server/api/post/presentation/dto/PostResponseDTO.java @@ -0,0 +1,24 @@ +package treehouse.server.api.post.presentation.dto; + +import lombok.*; +import treehouse.server.api.member.presentation.dto.MemberResponseDTO; + +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PostResponseDTO { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class getPostDetails { + + private MemberResponseDTO.getMemberProfile memberProfile; + private Long postId; + private String context; + private List pictureUrlList; +// private List reactionList; Reaction 기능 개발 이후 적용 + private String postedAt; + } +} diff --git a/src/main/java/treehouse/server/api/user/presentation/UserApi.java b/src/main/java/treehouse/server/api/user/presentation/UserApi.java index 76b2a4c..52282e8 100644 --- a/src/main/java/treehouse/server/api/user/presentation/UserApi.java +++ b/src/main/java/treehouse/server/api/user/presentation/UserApi.java @@ -25,7 +25,7 @@ public class UserApi { private final UserService userService; @PostMapping("/checkName") - @Operation(summary = "아이디 중복 체크", description = "서비스에서 사용할 유저이름을 중복 체크합니다.") + @Operation(summary = "아이디 중복 체크 ✅", description = "서비스에서 사용할 유저이름을 중복 체크합니다.") public CommonResponse checkName( @RequestBody final UserRequestDTO.checkName request ){ @@ -33,7 +33,7 @@ public CommonResponse checkName( } @PostMapping("/register") - @Operation(summary = "회원가입", description = "회원가입을 진행합니다.") + @Operation(summary = "회원가입 ✅", description = "회원가입을 진행합니다.") public CommonResponse registerMember( @RequestBody final UserRequestDTO.registerUser request ){ @@ -41,7 +41,7 @@ public CommonResponse registerMember( } @PostMapping("/reissue") - @Operation(summary = "토큰 재발급", description = "토큰을 재발급 합니다.") + @Operation(summary = "토큰 재발급 ✅", description = "토큰을 재발급 합니다.") public CommonResponse reissue( @RequestBody final UserRequestDTO.reissue request ){ diff --git a/src/main/java/treehouse/server/global/common/TimeFormatter.java b/src/main/java/treehouse/server/global/common/TimeFormatter.java new file mode 100644 index 0000000..6c72430 --- /dev/null +++ b/src/main/java/treehouse/server/global/common/TimeFormatter.java @@ -0,0 +1,31 @@ +package treehouse.server.global.common; + +import java.time.Duration; +import java.time.LocalDateTime; + +// 특정 게시글/댓글의 작성 시간을 현재 시간과 비교하여 문자열로 변환하는 유틸리티 클래스 +public class TimeFormatter { + + private TimeFormatter() { + } + + public static String format(LocalDateTime createdAt) { + LocalDateTime now = LocalDateTime.now(); + Duration duration = Duration.between(createdAt, now); + + long seconds = duration.getSeconds(); + long minutes = duration.toMinutes(); + long hours = duration.toHours(); + long days = duration.toDays(); + + if (seconds < 60) { + return seconds + "초 전"; + } else if (minutes < 60) { + return minutes + "분 전"; + } else if (hours < 24) { + return hours + "시간 전"; + } else { + return days + "일 전"; + } + } +} diff --git a/src/main/java/treehouse/server/global/entity/comment/Comment.java b/src/main/java/treehouse/server/global/entity/comment/Comment.java index bae18cc..3a9e6c9 100644 --- a/src/main/java/treehouse/server/global/entity/comment/Comment.java +++ b/src/main/java/treehouse/server/global/entity/comment/Comment.java @@ -3,7 +3,7 @@ import jakarta.persistence.*; import lombok.*; import treehouse.server.global.entity.common.BaseDateTimeEntity; -import treehouse.server.global.entity.feed.Feed; +import treehouse.server.global.entity.post.Post; import treehouse.server.global.entity.member.Member; @Entity @@ -22,5 +22,5 @@ public class Comment extends BaseDateTimeEntity { @JoinColumn(name = "feedId") @ManyToOne(fetch = FetchType.LAZY) - private Feed feed; + private Post post; } \ No newline at end of file diff --git a/src/main/java/treehouse/server/global/entity/feed/Feed.java b/src/main/java/treehouse/server/global/entity/post/Post.java similarity index 73% rename from src/main/java/treehouse/server/global/entity/feed/Feed.java rename to src/main/java/treehouse/server/global/entity/post/Post.java index aae4d0b..60ead54 100644 --- a/src/main/java/treehouse/server/global/entity/feed/Feed.java +++ b/src/main/java/treehouse/server/global/entity/post/Post.java @@ -1,4 +1,4 @@ -package treehouse.server.global.entity.feed; +package treehouse.server.global.entity.post; import jakarta.persistence.*; import lombok.*; @@ -7,7 +7,6 @@ import treehouse.server.global.entity.member.Member; import treehouse.server.global.entity.treeHouse.TreeHouse; -import java.util.Comparator; import java.util.List; @Entity @@ -15,7 +14,7 @@ @Builder @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Feed extends BaseDateTimeEntity { +public class Post extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -24,15 +23,15 @@ public class Feed extends BaseDateTimeEntity { @ManyToOne(fetch = FetchType.LAZY) private Member writer; - @JoinColumn(name = "userId") + @JoinColumn(name = "treehouseId") @ManyToOne(fetch = FetchType.LAZY) private TreeHouse treeHouse; private String content; - @OneToMany(mappedBy = "feed") + @OneToMany(mappedBy = "post") private List commentList; - @OneToMany(mappedBy = "feed") - private List feedImageList; + @OneToMany(mappedBy = "post") + private List postImageList; } \ No newline at end of file diff --git a/src/main/java/treehouse/server/global/entity/feed/FeedImage.java b/src/main/java/treehouse/server/global/entity/post/PostImage.java similarity index 70% rename from src/main/java/treehouse/server/global/entity/feed/FeedImage.java rename to src/main/java/treehouse/server/global/entity/post/PostImage.java index cf245a8..3f5932f 100644 --- a/src/main/java/treehouse/server/global/entity/feed/FeedImage.java +++ b/src/main/java/treehouse/server/global/entity/post/PostImage.java @@ -1,4 +1,4 @@ -package treehouse.server.global.entity.feed; +package treehouse.server.global.entity.post; import jakarta.persistence.*; import lombok.*; @@ -9,15 +9,15 @@ @Builder @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class FeedImage extends BaseDateTimeEntity { +public class PostImage extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String imageUrl; - @JoinColumn(name = "feedId") + @JoinColumn(name = "postId") @ManyToOne(fetch = FetchType.LAZY) - private Feed feed; + private Post post; } \ No newline at end of file diff --git a/src/main/java/treehouse/server/global/exception/ThrowClass/PostException.java b/src/main/java/treehouse/server/global/exception/ThrowClass/PostException.java new file mode 100644 index 0000000..78f46da --- /dev/null +++ b/src/main/java/treehouse/server/global/exception/ThrowClass/PostException.java @@ -0,0 +1,10 @@ +package treehouse.server.global.exception.ThrowClass; + +import treehouse.server.global.exception.BaseErrorCode; + +public class PostException extends GeneralException{ + + public PostException(BaseErrorCode errorCode) { + super(errorCode); + } +}