Skip to content

Commit

Permalink
Merge pull request #104 from APPS-sookmyung/feature/#103-home-tab
Browse files Browse the repository at this point in the history
[feature/#103-home-tab] 홈 탭 조회 API
  • Loading branch information
5jisoo authored Feb 16, 2024
2 parents d58f056 + 642bb18 commit 39df565
Show file tree
Hide file tree
Showing 11 changed files with 394 additions and 145 deletions.
34 changes: 25 additions & 9 deletions src/docs/asciidoc/post.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,42 @@ ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]

== `GET` Home Tab
홈 탭 조회 API

=== Request

include::{snippets}/get-home-tab/http-request.adoc[]

==== request headers
include::{snippets}/get-home-tab/request-headers.adoc[]

==== query params
include::{snippets}/get-home-tab/query-parameters.adoc[]


=== Response
==== response body
include::{snippets}/get-home-tab/response-body.adoc[]
include::{snippets}/get-home-tab/response-fields.adoc[]

== `POST` Post Upload API

게시물 업로드 API

=== Request

include::{snippets}/upload-post/curl-request.adoc[]

==== request headers
include::{snippets}/upload-post/request-headers.adoc[]

==== request parts
include::{snippets}/upload-post/request-parts.adoc[]

- request는 `application/json` 타입으로 다음과 같이 전달합니다.

```json
{
"caption" : "게시물 내용",
"taggedMemberHandleList" : ["habongee"]
}
```
==== query params
include::{snippets}/upload-post/query-parameters.adoc[]

include::{snippets}/upload-post/request-part-request-fields.adoc[]

=== Response
==== response body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

import static com.apps.pochak.global.apiPayload.code.status.ErrorStatus.NOT_FOLLOW;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.security.core.parameters.P;

import java.util.List;
import java.util.Optional;

import static com.apps.pochak.global.apiPayload.code.status.ErrorStatus.INVALID_MEMBER_HANDLE;

public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findMemberByHandle(String handle);
Optional<Member> findMemberByHandle(final String handle);

@Query("select m from Member m " +
"where m.handle in :handleList")
List<Member> findMemberByHandleList(@Param("handleList") final List<String> handle);

Optional<Member> findMemberBySocialId(String socialId);

Expand Down
41 changes: 18 additions & 23 deletions src/main/java/com/apps/pochak/post/controller/PostController.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,35 @@
package com.apps.pochak.post.controller;

import com.apps.pochak.comment.dto.response.CommentElements;
import com.apps.pochak.comment.service.CommentService;
import com.apps.pochak.global.apiPayload.ApiResponse;
import com.apps.pochak.global.apiPayload.code.status.SuccessStatus;
import com.apps.pochak.global.s3.ValidFile;
import com.apps.pochak.like.service.LikeService;
import com.apps.pochak.post.dto.PostElements;
import com.apps.pochak.post.dto.request.PostUploadRequest;
import com.apps.pochak.post.dto.response.PostDetailResponse;
import com.apps.pochak.post.service.PostService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import static com.apps.pochak.comment.service.CommentService.DEFAULT_PAGING_SIZE;
import static com.apps.pochak.global.apiPayload.code.status.SuccessStatus.*;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static com.apps.pochak.global.apiPayload.code.status.SuccessStatus.SUCCESS_DELETE_POST;
import static com.apps.pochak.global.apiPayload.code.status.SuccessStatus.SUCCESS_UPLOAD_POST;

@RestController
@RequiredArgsConstructor
@RequestMapping("api/v2/posts")
public class PostController {
private final PostService postService;

@DeleteMapping("/{postId}")
public ApiResponse<Void> deletePost(
@PathVariable("postId") final Long postId
@GetMapping("")
public ApiResponse<PostElements> getHomeTab(@PageableDefault(30) Pageable pageable) {
return ApiResponse.onSuccess(postService.getHomeTab(pageable));
}

@PostMapping(value = "")
public ApiResponse<Void> uploadPost(
@ModelAttribute final PostUploadRequest request
) {
postService.deletePost(postId);
return ApiResponse.of(SUCCESS_DELETE_POST);
postService.savePost(request);
return ApiResponse.of(SUCCESS_UPLOAD_POST);
}

@GetMapping("/{postId}")
Expand All @@ -42,13 +39,11 @@ public ApiResponse<PostDetailResponse> getPostDetail(
return ApiResponse.onSuccess(postService.getPostDetail(postId));
}

@PostMapping(value = "", consumes = {APPLICATION_JSON_VALUE, MULTIPART_FORM_DATA_VALUE})
public ApiResponse<Void> uploadPost(
@RequestPart(value = "postImage")
@ValidFile(message = "게시물 이미지는 필수로 전달해야 합니다.") final MultipartFile postImage,
@RequestPart("request") @Valid final PostUploadRequest request
@DeleteMapping("/{postId}")
public ApiResponse<Void> deletePost(
@PathVariable("postId") final Long postId
) {
postService.savePost(postImage, request);
return ApiResponse.of(SUCCESS_UPLOAD_POST);
postService.deletePost(postId);
return ApiResponse.of(SUCCESS_DELETE_POST);
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/apps/pochak/post/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

import java.time.LocalDateTime;

import static com.apps.pochak.post.domain.PostStatus.PRIVATE;
import static jakarta.persistence.EnumType.STRING;
import static jakarta.persistence.FetchType.LAZY;
Expand All @@ -31,6 +33,8 @@ public class Post extends BaseEntity {
@Column(columnDefinition = "VARCHAR(255) DEFAULT 'PRIVATE'")
private PostStatus postStatus;

private LocalDateTime allowedDate;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "owner_id")
private Member owner;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,15 @@ Page<Post> findPostByOwnerAndPostStatusOrderByCreatedDateDesc(final Member owner
default Post findPostById(final Long postId) {
return findById(postId).orElseThrow(() -> new GeneralException(INVALID_POST_ID));
}

@Query("select distinct p from Post p " +
"join Tag t on p = t.post and p.postStatus = 'PUBLIC' and t.status = 'ACTIVE' and ( t.member.id in ( " +
"select f.receiver.id from Follow f where f.sender = :loginMember and f.status = 'ACTIVE' " +
") or t.member = :loginMember ) " +
"order by p.allowedDate desc "
)
Page<Post> findTaggedPostsOfFollowing(
@Param("loginMember") final Member loginMember,
final Pageable pageable
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apps.pochak.post.dto.request;

import com.apps.pochak.global.s3.ValidFile;
import com.apps.pochak.member.domain.Member;
import com.apps.pochak.post.domain.Post;
import jakarta.validation.Valid;
Expand All @@ -8,13 +9,17 @@
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PostUploadRequest {
@ValidFile(message = "게시물 이미지는 필수로 전달해야 합니다.")
private MultipartFile postImage;

private String caption;

@Valid
Expand Down
62 changes: 30 additions & 32 deletions src/main/java/com/apps/pochak/post/service/PostService.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
package com.apps.pochak.post.service;

import com.apps.pochak.comment.domain.repository.CommentRepository;
import com.apps.pochak.comment.dto.response.CommentElements;
import com.apps.pochak.global.apiPayload.exception.GeneralException;
import com.apps.pochak.login.jwt.JwtService;
import com.apps.pochak.member.domain.Member;
import com.apps.pochak.post.domain.Post;
import com.apps.pochak.post.domain.repository.PostRepository;
import com.apps.pochak.alarm.domain.TagApprovalAlarm;
import com.apps.pochak.alarm.domain.repository.AlarmRepository;
import com.apps.pochak.comment.domain.Comment;
Expand All @@ -20,24 +13,23 @@
import com.apps.pochak.member.domain.repository.MemberRepository;
import com.apps.pochak.post.domain.Post;
import com.apps.pochak.post.domain.repository.PostRepository;
import com.apps.pochak.post.dto.PostElements;
import com.apps.pochak.post.dto.request.PostUploadRequest;
import com.apps.pochak.post.dto.response.PostDetailResponse;
import com.apps.pochak.tag.domain.Tag;
import com.apps.pochak.tag.domain.repository.TagRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.stream.Collectors;

import static com.apps.pochak.global.apiPayload.code.status.ErrorStatus.PRIVATE_POST;
import static com.apps.pochak.global.apiPayload.code.status.ErrorStatus.*;
import static com.apps.pochak.global.s3.DirName.POST;

import static com.apps.pochak.global.apiPayload.code.status.ErrorStatus.INVALID_POST_ID;
import static com.apps.pochak.global.apiPayload.code.status.ErrorStatus.NOT_YOUR_POST;

@Service
@RequiredArgsConstructor
public class PostService {
Expand All @@ -52,17 +44,12 @@ public class PostService {
private final S3Service s3Service;
private final JwtService jwtService;

@Transactional
public void deletePost(final Long postId) {
public PostElements getHomeTab(Pageable pageable) {
final Member loginMember = jwtService.getLoginMember();
final Post post = postRepository.findById(postId).orElseThrow(() -> new GeneralException(INVALID_POST_ID));
if (!post.getOwner().getId().equals(loginMember.getId())) {
throw new GeneralException(NOT_YOUR_POST);
}
postRepository.delete(post);
commentRepository.bulkDeleteByPost(post);
final Page<Post> taggedPost = postRepository.findTaggedPostsOfFollowing(loginMember, pageable);
return PostElements.from(taggedPost);
}

public PostDetailResponse getPostDetail(final Long postId) {
final Member loginMember = jwtService.getLoginMember();
final Post post = postRepository.findPostById(postId);
Expand Down Expand Up @@ -102,29 +89,29 @@ private Boolean isMyPost(final Post post,
}

@Transactional
public void savePost(
final MultipartFile postImage,
final PostUploadRequest request
) {
public void savePost(final PostUploadRequest request) {
final Member loginMember = jwtService.getLoginMember();
final String image = s3Service.upload(postImage, POST);
final String image = s3Service.upload(request.getPostImage(), POST);
final Post post = request.toEntity(image, loginMember);
postRepository.save(post);
final List<String> taggedMemberHandles = request.getTaggedMemberHandleList();

// TODO: N+1 고치기
final List<Member> taggedMemberList = taggedMemberHandles.stream().map(
memberRepository::findByHandle
).collect(Collectors.toList());
final List<String> taggedMemberHandles = request.getTaggedMemberHandleList();
final List<Member> taggedMemberList = memberRepository.findMemberByHandleList(taggedMemberHandles);
final List<Tag> tagList = saveTags(taggedMemberList, post);
saveTagApprovalAlarms(tagList);
}

private List<Tag> saveTags(List<Member> taggedMemberList, Post post) {
final List<Tag> tagList = taggedMemberList.stream().map(
member -> Tag.builder()
.member(member)
.post(post)
.build()
).collect(Collectors.toList());
tagRepository.saveAll(tagList);
return tagRepository.saveAll(tagList);
}

private void saveTagApprovalAlarms(List<Tag> tagList) {
final List<TagApprovalAlarm> tagApprovalAlarmList = tagList.stream().map(
tag -> TagApprovalAlarm.builder()
.tag(tag)
Expand All @@ -133,4 +120,15 @@ public void savePost(
).collect(Collectors.toList());
alarmRepository.saveAll(tagApprovalAlarmList);
}

@Transactional
public void deletePost(final Long postId) {
final Member loginMember = jwtService.getLoginMember();
final Post post = postRepository.findById(postId).orElseThrow(() -> new GeneralException(INVALID_POST_ID));
if (!post.getOwner().getId().equals(loginMember.getId())) {
throw new GeneralException(NOT_YOUR_POST);
}
postRepository.delete(post);
commentRepository.bulkDeleteByPost(post);
}
}
Loading

0 comments on commit 39df565

Please sign in to comment.