Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: 게시글 공동저자 설정 / 공개 범위 설정 / 소속 팀 설정 API 기능 구현 #299

Merged
merged 3 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,13 @@ public void setPost(Post post) {
public void setMember(Member member) {
this.member = member;
}

public static Author createAuthors(Post post, Member member) {
Author authors = new Author();
authors.setPost(post);
authors.setMember(member);
return authors;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,39 @@ public ApiResponse<Page<PostResponseDTO.SimplePostResponseDTO>> getBookmarkPost(
return ApiResponse.onSuccess(SuccessStatus.POST_OK,
PostConverter.toPostListResponseDto(postQueryService.getBookmarkPost(memberDetails.getId(), pageable)));
}


@PatchMapping("/coauthor/{postId}")
@Operation(summary = "게시글 공동 저자 설정 API", description = "게시글의 공동 저자를 설정합니다.")
public ApiResponse<PostResponseDTO.UpdatePostResultDTO> setPostCoauthor(@PathVariable Long postId, @RequestBody PostRequestDTO.UpdateCoauthorRequestDTO request) {
Member member = memberCommandService.getRequester();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

멤버정보는 @AuthenticationPrincipal CustomMemberDetails customMemberDetails 사용하면 될 것 같습니다!

jwtTokenProvider.isValidToken(member.getMemberId());

Post updatedPost = postCommandService.updateCoauthors(postId, request);
return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toUpdatePostResultDTO(updatedPost));
}


@PatchMapping("/team/{postId}")
@Operation(summary = "게시글의 소속 팀 설정 API", description = "게시글의 소속 팀을 설정합니다.")
public ApiResponse<PostResponseDTO.UpdatePostResultDTO> setPostTeam(@PathVariable Long postId, @RequestBody PostRequestDTO.SetTeamRequestDTO request) {
Member member = memberCommandService.getRequester();
jwtTokenProvider.isValidToken(member.getMemberId());

Post updatedPost = postCommandService.setPostTeam(postId, request.getTeamId());
return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toUpdatePostResultDTO(updatedPost));
}


@PatchMapping("/visibility/{postId}")
@Operation(summary = "게시글 공개 범위 설정 API", description = "게시글의 공개 범위를 설정합니다. (MEMBER / TEAM / ENTIRE)")
public ApiResponse<PostResponseDTO.UpdatePostResultDTO> setPostVisibility(@PathVariable Long postId, @RequestBody PostRequestDTO.UpdateVisibilityRequestDTO request) {
Member member = memberCommandService.getRequester();
jwtTokenProvider.isValidToken(member.getMemberId());

Post updatedPost = postCommandService.updateVisibility(postId, request);
return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toUpdatePostResultDTO(updatedPost));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ public static class UpdatePostDTO {
private List<MultipartFile> addedPostFiles;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class UpdateCoauthorRequestDTO {
private List<Long> memberIds;
}

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SetTeamRequestDTO {
private Long teamId;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class UpdateVisibilityRequestDTO {
private String postAccess; // MEMBER, TEAM, ENTIRE
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public class Post extends BaseEntity {
public void setMember(Member member) { this.member = member;}
public void setTeam(Team team) { this.team = team;}
public void setPostStatus(Boolean postStatus) { this.postStatus = postStatus;}

public void setPostAccess(PostAccess postAccess) { this.postAccess = postAccess;}
public void setProject(Project project) { this.project = project;}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -55,6 +57,31 @@ public class PostCommandService {
private final CategoryService categoryService;
private final AmazonS3Manager s3Manager;


private void validatePostAccess(Post post, Member member) {
// MEMBER 접근 권한: 작성자만 접근 가능
if (post.getPostAccess() == PostAccess.MEMBER && !post.getMember().equals(member)) {
throw new GeneralException(ErrorStatus.NO_ACCESS_PERMISSION);
}
// TEAM 접근 권한: 팀 멤버만 접근 가능
if (post.getPostAccess() == PostAccess.TEAM && post.getTeam() != null) {
if (!teamRepository.isTeamMember(post.getTeam(), member)) {
throw new GeneralException(ErrorStatus.NO_ACCESS_PERMISSION);
}
}
}

private Member getAuthenticatedMember() {
// 현재 인증된 사용자 가져오기
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// username (식별자) 가져오기
String username = authentication.getName();
// username으로 Member 엔터티 조회
return memberRepository.findByEmail(username)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));
}


// 포스트 생성
public Post createPost(Long memberId, PostRequestDTO.CreatePostRequestDTO request) {
// validation: member|team|project 유무 확인 (team 및 project 없는 경우 null)
Expand Down Expand Up @@ -204,4 +231,49 @@ public Post setPostCategories(Long postId, Set<String> categoryNames) {
return postRepository.save(post);
}


public Post updateCoauthors(Long postId, PostRequestDTO.UpdateCoauthorRequestDTO request) {
Post post = postRepository.findById(postId).orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND));
Member authenticatedMember = getAuthenticatedMember();
validatePostAccess(post, authenticatedMember);
// 기존 공동 저자 리스트 삭제
post.getAuthorList().clear();
// 새로운 공동 저자 리스트 추가
Set<Author> coauthors = request.getMemberIds().stream()
.map(newCoauthorId -> {
Member newCoauthor = memberRepository.findById(newCoauthorId).orElseThrow(() -> new IllegalArgumentException("Member not found: " + newCoauthorId));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외처리 GeneralException 사용 안하신 이유가 있나요?

return Author.createAuthors(post, newCoauthor);
})
.collect(Collectors.toSet());
post.getAuthorList().addAll(coauthors);

return postRepository.save(post);
}


public Post setPostTeam(Long postId, Long teamId) {
Post post = postRepository.findById(postId).orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND));

Member authenticatedMember = getAuthenticatedMember();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAuthenticatedMember를 만드신 이유가 궁금합니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 로그인한 사용자가 게시글을 작성한 저자와 일치하는지 확인하기 위해 썼습니다!

validatePostAccess(post, authenticatedMember);

Team team = teamRepository.findById(teamId).orElseThrow(() -> new GeneralException(ErrorStatus.TEAM_NOT_FOUND));
post.setTeam(team);

return postRepository.save(post);
}


public Post updateVisibility(Long postId, PostRequestDTO.UpdateVisibilityRequestDTO request) {
Post post = postRepository.findById(postId).orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND));
Member authenticatedMember = getAuthenticatedMember();

if (!post.getMember().equals(authenticatedMember)) { throw new GeneralException(ErrorStatus.POST_ACCESS_SET_UNAUTHORIZED);}
post.setPostAccess(PostAccess.valueOf(request.getPostAccess()));

return postRepository.save(post);
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public enum ErrorStatus implements BaseErrorCode {
POST_NOT_FOUND(HttpStatus.BAD_REQUEST, "POST_3009", "포스트가 없습니다."),
NO_ACCESS_PERMISSION(HttpStatus.BAD_REQUEST, "POST_3010", "전체 공개 게시글이 없습니다."),
INVALID_REQUEST(HttpStatus.BAD_REQUEST, "POST_3011", "멤버 ID 또는 팀 ID 중 하나를 입력해야 합니다."),
POST_COAUTHOR_UNAUTHORIZED(HttpStatus.BAD_REQUEST, "POST_3012", "공동 저자 설정은 작성자만 가능합니다."),
POST_ACCESS_SET_UNAUTHORIZED(HttpStatus.BAD_REQUEST, "POST_3013", "가시성 설정은 작성자만 가능합니다."),

// 코멘트 관련 에러 4000
COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT_4005", "댓글이 없습니다."),
Expand Down