Skip to content

Commit

Permalink
Merge pull request #42 from Team-Shaka/feature/37
Browse files Browse the repository at this point in the history
Feature/37 - 게시글 수정/삭제 API 구현
  • Loading branch information
CYY1007 authored May 26, 2024
2 parents d34f6bb + c0bf91b commit a657434
Show file tree
Hide file tree
Showing 16 changed files with 134 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import treehouse.server.global.annotations.Adapter;
import treehouse.server.global.entity.User.User;
import treehouse.server.global.entity.member.Member;
import treehouse.server.global.entity.treeHouse.TreeHouse;
import treehouse.server.global.exception.GlobalErrorCode;
import treehouse.server.global.exception.ThrowClass.MemberException;

Expand All @@ -16,6 +17,10 @@ public class MemberQueryAdapter {

public Member getMember(User user){

return memberRepository.findByUser(user).orElseThrow(()-> new MemberException(GlobalErrorCode.MEMBER_NOT_FOUND));
return memberRepository.findByUser(user).orElseThrow(()-> new MemberException(GlobalErrorCode.USER_NOT_FOUND));
}

public Member findByUserAndTreehouse(User user, TreeHouse treehouse) {
return memberRepository.findByUserAndTreeHouse(user, treehouse).orElseThrow(() -> new MemberException(GlobalErrorCode.USER_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import org.springframework.data.jpa.repository.JpaRepository;
import treehouse.server.global.entity.User.User;
import treehouse.server.global.entity.member.Member;
import treehouse.server.global.entity.treeHouse.TreeHouse;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {

public Optional<Member> findByUser(User user);

Optional<Member> findByUserAndTreeHouse(User user, TreeHouse treehouse);
}
15 changes: 8 additions & 7 deletions src/main/java/treehouse/server/api/post/business/PostMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.Page;
import treehouse.server.api.post.presentation.dto.PostRequestDTO;
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.User.User;
import treehouse.server.global.entity.member.Member;
import treehouse.server.global.entity.post.Post;
import treehouse.server.global.entity.post.PostImage;
Expand All @@ -16,19 +14,16 @@

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PostMapper {

public static PostResponseDTO.getPostDetails toGetPostDetails(Post post) {
public static PostResponseDTO.getPostDetails toGetPostDetails(Post post, List<String> postImageUrlList) {
return PostResponseDTO.getPostDetails.builder()
.memberProfile(MemberMapper.toGetMemberProfile(post.getWriter()))
.postId(post.getId())
.context(post.getContent())
.pictureUrlList(post.getPostImageList().stream()
.map(PostImage::getImageUrl).toList()
)
.pictureUrlList(postImageUrlList)
.commentCount(post.getCommentList().size())
// .reactionList() // Reaction 기능 개발 이후 수정
.postedAt(TimeFormatter.format(post.getCreatedAt()))
Expand Down Expand Up @@ -65,4 +60,10 @@ public static PostResponseDTO.createPresignedUrlResult toCreatePresignedUrlResul
.accessUrl(result.getDownloadUrl())
.build();
}

public static PostResponseDTO.updatePostResult toUpdatePostResult(Post post) {
return PostResponseDTO.updatePostResult.builder()
.postId(post.getId())
.build();
}
}
54 changes: 49 additions & 5 deletions src/main/java/treehouse/server/api/post/business/PostService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import treehouse.server.api.member.implementation.MemberQueryAdapter;
Expand Down Expand Up @@ -57,7 +58,11 @@ public class PostService {
@Transactional(readOnly = true)
public PostResponseDTO.getPostDetails getPostDetails(User user, Long postId, Long treehouseId) {
Post post = postQueryAdapter.findById(postId);
return PostMapper.toGetPostDetails(post);
List<PostImage> postImageList = post.getPostImageList();
List<String> postImageUrlList = postImageList.stream()
.map(PostImage::getImageUrl)
.toList();
return PostMapper.toGetPostDetails(post, postImageUrlList);
}

public PostResponseDTO.createPostResult createPost(User user, PostRequestDTO.createPost request, Long treehouseId) {
Expand Down Expand Up @@ -95,18 +100,57 @@ public PostResponseDTO.createPresignedUrlResult createPresignedUrl(PostRequestDT
* @return List<PostResponseDTO.getPostDetails>
*/
@Transactional(readOnly = true)
public List<PostResponseDTO.getPostDetails> getPosts (User user, Long treehouseId,int page){
public List<PostResponseDTO.getPostDetails> getPosts (User user, Long treehouseId, int page){
// TODO 신고한 게시물과 탈퇴 및 차단한 작성자의 게시물은 제외하는 로직 추가

TreeHouse treehouse = treehouseQueryAdapter.getTreehouseById(treehouseId);

Pageable pageable = PageRequest.of(page, 10);
Pageable pageable = PageRequest.of(page, 10, Sort.by(Sort.Direction.DESC, "createdAt"));
Page<Post> postsPage = postQueryAdapter.findAllByTreehouse(treehouse, pageable);

List<PostResponseDTO.getPostDetails> postDtos = postsPage.getContent().stream()
.map(PostMapper::toGetPostDetails)
.map(post -> {
List<PostImage> postImageList = post.getPostImageList();
List<String> postImageUrlList= postImageList.stream()
.map(PostImage::getImageUrl)
.toList();
return PostMapper.toGetPostDetails(post, postImageUrlList);
})
.collect(Collectors.toList());

return postDtos;
}

@Transactional
public PostResponseDTO.updatePostResult updatePost(User user, Long treehouseId, Long postId, PostRequestDTO.updatePost request) {
//TODO 현재 로그인 한 사용자가 게시글 작성자인지 확인하는 로직 개선
TreeHouse treehouse = treehouseQueryAdapter.getTreehouseById(treehouseId);
Member member = memberQueryAdapter.findByUserAndTreehouse(user, treehouse);

Post post = postQueryAdapter.findById(postId);
Member writer = post.getWriter();

if (!member.equals(writer)) {
throw new PostException(GlobalErrorCode.POST_UNAUTHORIZED);
}

post.update(request.getContext());
return PostMapper.toUpdatePostResult(postCommandAdapter.savePost(post));
}

@Transactional
public void deletePost(User user, Long treehouseId, Long postId) {

//TODO 현재 로그인 한 사용자가 게시글 작성자인지 확인하는 로직 개선
TreeHouse treehouse = treehouseQueryAdapter.getTreehouseById(treehouseId);
Member member = memberQueryAdapter.findByUserAndTreehouse(user, treehouse);

Post post = postQueryAdapter.findById(postId);
Member writer = post.getWriter();

if (!member.equals(writer)) {
throw new PostException(GlobalErrorCode.POST_UNAUTHORIZED);
}

postCommandAdapter.deletePost(post);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ public Post savePost(Post post){
return postRepository.save(post);
}

public void deletePost(Post post) {
postRepository.delete(post);
}
}
22 changes: 22 additions & 0 deletions src/main/java/treehouse/server/api/post/presentation/PostApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,26 @@ public CommonResponse<List<PostResponseDTO.getPostDetails>> getPosts(
){
return CommonResponse.onSuccess(postService.getPosts(user, treehouseId, page));
}

@PatchMapping("/posts/{postId}")
@Operation(summary = "게시글 수정 ✅ 🔑", description = "게시글을 수정합니다(이미지는 수정불가)")
public CommonResponse<PostResponseDTO.updatePostResult> updatePost(
@PathVariable Long treehouseId,
@PathVariable Long postId,
@RequestBody @Valid PostRequestDTO.updatePost request,
@AuthMember @Parameter(hidden = true) User user
){
return CommonResponse.onSuccess(postService.updatePost(user, treehouseId, postId, request));
}

@DeleteMapping("/posts/{postId}")
@Operation(summary = "게시글 삭제 ✅ 🔑", description = "게시글을 삭제합니다.")
public CommonResponse deletePost(
@PathVariable Long treehouseId,
@PathVariable Long postId,
@AuthMember @Parameter(hidden = true) User user
){
postService.deletePost(user, treehouseId, postId);
return CommonResponse.onSuccess(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,13 @@ public static class uploadFile{
@NotNull(message = "fileSize가 필요합니다.")
private Double fileSize;
}

@Getter
public static class updatePost{

@JsonProperty("context")
@Schema(description = "게시글 내용", example = "게시글 내용")
@NotBlank(message = "게시글 내용이 필요합니다.")
private String context;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,14 @@ public static class createPresignedUrlResult{
@JsonProperty("accessUrl")
private String accessUrl;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class updatePostResult {

@Schema(description = "수정 된 포스트 ID", example = "1")
private Long postId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import treehouse.server.api.user.presentation.dto.UserRequestDTO;
import treehouse.server.api.user.presentation.dto.UserResponseDTO;
import treehouse.server.global.entity.User.User;
import treehouse.server.global.entity.member.Member;
import treehouse.server.global.entity.redis.RefreshToken;
import treehouse.server.global.exception.GlobalErrorCode;
import treehouse.server.global.exception.ThrowClass.AuthException;
Expand Down Expand Up @@ -54,7 +53,7 @@ public UserResponseDTO.registerUser register(UserRequestDTO.registerUser request
@Transactional
public UserResponseDTO.registerUser login(UserRequestDTO.loginMember request){
User user = userQueryAdapter.findByPhoneNumber(request.getPhoneNumber())
.orElseThrow(() -> new GeneralException(GlobalErrorCode.MEMBER_NOT_FOUND));
.orElseThrow(() -> new GeneralException(GlobalErrorCode.USER_NOT_FOUND));
TokenDTO loginResult = userCommandAdapter.login(user);

return UserMapper.toRegister(user,loginResult.getAccessToken(), loginResult.getRefreshToken());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Optional<User> findByPhoneNumber(String phone){
}

public User findById(Long id){
return userRepository.findById(id).orElseThrow(()->new UserException(GlobalErrorCode.MEMBER_NOT_FOUND));
return userRepository.findById(id).orElseThrow(()->new UserException(GlobalErrorCode.USER_NOT_FOUND));
}

public Optional<User> optionalUserFindById(Long id){
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/treehouse/server/global/entity/post/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ public class Post extends BaseDateTimeEntity {

private String content;

@OneToMany(mappedBy = "post")
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Comment> commentList;

@OneToMany(mappedBy = "post")
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<PostImage> postImageList;

public void update(String context) {
this.content = context;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
import treehouse.server.global.exception.ThrowClass.GeneralException;
import treehouse.server.global.exception.dto.ErrorResponseDTO;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
Expand All @@ -29,11 +27,11 @@
@RestControllerAdvice(annotations = {RestController.class})
public class GeneralExceptionHandler extends ResponseEntityExceptionHandler {

// @ExceptionHandler(value = { GeneralException.class})
// protected ApiResponse handleCustomException(GeneralException e) {
// log.error("handleCustomException throw CustomException : {}", e.getErrorCode());
// return ApiResponse.onFailure(e.getErrorCode(), "");
// }
@ExceptionHandler(value = { GeneralException.class})
protected ResponseEntity<Object> handleCustomException(GeneralException e, HttpServletRequest request) {
log.error("handleCustomException throw CustomException : {}", e.getErrorCode());
return handleExceptionInternal(e, e.getErrorReasonHttpStatus(), HttpHeaders.EMPTY, request);
}


@ExceptionHandler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public enum GlobalErrorCode implements BaseErrorCode{
// USER + 403 Forbidden - 인증 거부

// USER + 404 Not Found - 찾을 수 없음
MEMBER_NOT_FOUND(NOT_FOUND, "USER404_1", "등록된 사용자 정보가 없습니다."),
USER_NOT_FOUND(NOT_FOUND, "USER404_1", "등록된 사용자 정보가 없습니다."),

// USER + 409 CONFLICT : Resource 를 찾을 수 없음
DUPLICATE_PHONE_NUMBER(CONFLICT, "USER409_1", "중복된 전화번호가 존재합니다."),
Expand All @@ -56,6 +56,8 @@ public enum GlobalErrorCode implements BaseErrorCode{
// INVITATION + 404 Not Found - 찾을 수 없음
INVITATION_NOT_FOUND(NOT_FOUND, "INVITATION404_1", "존재하지 않는 초대장입니다."),

// POST + 401 Unauthorized - 권한 없음
POST_UNAUTHORIZED(UNAUTHORIZED, "POST401_1", "게시글 수정 및 삭제 권한이 없습니다."),
// POST + 404 Not Found - 찾을 수 없음
POST_NOT_FOUND(NOT_FOUND, "POST404_1", "존재하지 않는 게시글입니다."),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class RedisService {
*/
public RefreshToken generateRefreshToken(User user) {
if (!userQueryAdapter.existById(user.getId()))
throw new GeneralException(GlobalErrorCode.MEMBER_NOT_FOUND);
throw new GeneralException(GlobalErrorCode.USER_NOT_FOUND);

String refreshToken = tokenProvider.createRefreshToken();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import treehouse.server.api.user.business.UserMapper;
import treehouse.server.api.user.business.UserService;
import treehouse.server.global.entity.User.User;
import treehouse.server.global.exception.GlobalErrorCode;
import treehouse.server.global.exception.ThrowClass.GeneralException;
Expand Down Expand Up @@ -51,7 +50,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m
principal = authentication.getPrincipal();
}
if (principal == null || principal.getClass() == String.class) { //Authentication 객체가 null이거나, principal이 String 타입('anonymousUser')인 경우
throw new GeneralException(GlobalErrorCode.MEMBER_NOT_FOUND);
throw new GeneralException(GlobalErrorCode.USER_NOT_FOUND);
}

UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) authentication;
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ spring:
password: ${aws.db.password}
url: jdbc:mysql://${aws.db.url}/${aws.db.name}?autoReconnect=true&setTimezone=Asia/Seoul
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
sql:
init:
mode: never
Expand Down

0 comments on commit a657434

Please sign in to comment.