Skip to content
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 @@ -37,8 +37,8 @@ public RsData<UploadUrlGetResponse> getUploadUrl(@RequestParam String filename)

@GetMapping("/videos/download")
@Operation(summary = "다운로드용 URL 요청", description = "파일 다운로드를 위한 Presigned URL을 발급받습니다.")
public RsData<DownLoadUrlGetResponse> getDownloadUrls(@RequestParam String objectKey) {
PresignedUrlResponse downloadUrl = fileManager.getDownloadUrl(objectKey);
public RsData<DownLoadUrlGetResponse> getDownloadUrls(@RequestParam String uuid, @RequestParam String resolution) {
PresignedUrlResponse downloadUrl = fileManager.getDownloadUrl(uuid, resolution);
DownLoadUrlGetResponse response = new DownLoadUrlGetResponse(downloadUrl.url().toString(), downloadUrl.expiresAt());
return new RsData<>("200", "다운로드용 URL 요청완료", response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
@Repository
public interface VideoRepository extends JpaRepository<Video, Integer> {
Optional<Video> findByUuid(String uuid);
boolean existsByUuid(String uuid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

import java.net.URL;
import java.time.LocalDateTime;
import java.util.Set;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class FileManager {
private final VideoService videoService;
private final S3Service s3Service;
private final Set<String> validResolutions = Set.of("480p", "720p", "1080p");

public PresignedUrlResponse getUploadUrl(String filename) {
String contentType = validateAndGetContentType(filename);
Expand Down Expand Up @@ -53,8 +55,11 @@ private String validateAndGetContentType(String filename) {
}
}

public PresignedUrlResponse getDownloadUrl(String objectKey) {
public PresignedUrlResponse getDownloadUrl(String uuid, String resolution) {
Integer expires = 60;
validateResolution(resolution);
videoService.isExistByUuid(uuid);
String objectKey = "transcoded/videos/" + uuid + "/" + resolution + "/manifest.mpd";
URL url = s3Service.generateDownloadUrl(objectKey, expires);
LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(expires);
return new PresignedUrlResponse(url, expiresAt);
Expand All @@ -68,4 +73,10 @@ public void updateVideoStatus(String videoId, String status) {
videoService.createVideo(videoId, status, "/", 0);
}
}

public void validateResolution(String resolution) {
if (!validResolutions.contains(resolution)) {
throw new ServiceException("400", "지원하지 않는 해상도입니다: " + resolution);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ public Video updateStatus(String uuid, String status) {
news.updateStatus(status);
return videoRepository.save(news);
}

public void isExistByUuid(String uuid) {
if (!videoRepository.existsByUuid(uuid)) {
throw new ServiceException("404", "Video not found");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,12 @@
import com.back.domain.post.comment.dto.CommentCreateRequest;
import com.back.domain.post.comment.dto.CommentDeleteRequest;
import com.back.domain.post.comment.dto.CommentModifyRequest;
import com.back.domain.post.comment.entity.PostComment;
import com.back.domain.post.comment.service.PostCommentService;
import com.back.global.rq.Rq;
import com.back.global.rsData.RsData;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand All @@ -30,40 +25,42 @@ public class PostCommentController {

@Operation(summary = "댓글 생성", description = "comment는 공백이나 Null이 될 수 없습니다. comment의 글자 수 제한은 없습니다.")
@PostMapping("/{post_id}/comment")
public RsData<Void> createComment(@PathVariable Long post_id,
public RsData<Void> createComment(@PathVariable(name = "post_id") Long postId,
@Valid @RequestBody CommentCreateRequest commentCreateRequest
) {
Member member = rq.getActor();
postCommentService.createComment(member, post_id, commentCreateRequest);
postCommentService.createComment(member, postId, commentCreateRequest);

return new RsData<>("200", "댓글 작성 완료", null);
}

@Operation(summary = "댓글 다건 조회")
@GetMapping("/post/{post_id}")
public RsData<List<CommentAllResponse>> getAllPostComment(@PathVariable Long post_id) {
List<CommentAllResponse> postAllResponse = postCommentService.getAllPostCommentResponse(post_id);
public RsData<List<CommentAllResponse>> getAllPostComment(@PathVariable(name = "post_id") Long postId) {
List<CommentAllResponse> postAllResponse = postCommentService.getAllPostCommentResponse(postId);
return new RsData<>("200", "댓글 조회 성공", postAllResponse);
}

@Operation(summary = "댓글 삭제", description = "commentId는 공백이나 Null이 될 수 없습니다.")
@DeleteMapping("/{post_id}/comment")
public RsData<Void> removePostComment(@PathVariable @Positive Long post_id
public RsData<Void> removePostComment(@PathVariable(name = "post_id") Long postId

, @RequestBody @Valid CommentDeleteRequest commentDeleteRequest) {
Member member = rq.getActor();

postCommentService.removePostComment(post_id, commentDeleteRequest, member);
postCommentService.removePostComment(postId, commentDeleteRequest, member);

return new RsData<>("200", "댓글 삭제 성공", null);
}

@Operation(summary = "댓글 수정", description = "commentId, content는 공백이나 Null이 될 수 없습니다. content의 글자 수 제한은 없습니다. ")
@PutMapping("/{post_id}/comment")
public RsData<Void> updatePostComment(@PathVariable Long post_id
public RsData<Void> updatePostComment(@PathVariable(name = "post_id") Long postId

, @Valid @RequestBody CommentModifyRequest commentModifyRequest) {
Member member = rq.getActor();

postCommentService.updatePostComment(post_id, commentModifyRequest, member);
postCommentService.updatePostComment(postId, commentModifyRequest, member);

return new RsData<>("200", "댓글 수정 성공", null);
}
Expand All @@ -77,8 +74,8 @@ public RsData<Void> adoptComment(@PathVariable Long commentId) {

@Operation(summary = "채택된 댓글 가져오기 ")
@GetMapping("isAdopted/{post_id}")
public RsData<CommentAllResponse> getAdoptComment(@PathVariable Long post_id) {
CommentAllResponse commentAllResponse = postCommentService.getAdoptedComment(post_id);
public RsData<CommentAllResponse> getAdoptComment(@PathVariable(name = "post_id") Long postId) {
CommentAllResponse commentAllResponse = postCommentService.getAdoptedComment(postId);

return new RsData<>("200", "채택된 댓글 조회 성공", commentAllResponse);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import com.back.domain.post.comment.entity.PostComment;
import com.back.domain.post.comment.repository.PostCommentRepository;
import com.back.domain.post.post.entity.Post;
import com.back.domain.post.post.repository.PostRepository;
import com.back.domain.post.post.service.PostService;
import com.back.global.exception.ServiceException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,16 +20,13 @@
@Service
@RequiredArgsConstructor
public class PostCommentService {
private final PostRepository postRepository;
private final PostService postService;
private final PostCommentRepository postCommentRepository;

@Transactional
public void createComment(Member member, Long postId, CommentCreateRequest commentCreateRequest) {
Post post = postRepository.findById(postId).orElseThrow(() -> new ServiceException("400", "해당 Id의 게시글이 없습니다."));

if ( commentCreateRequest.comment() == null || commentCreateRequest.comment().isEmpty()) {
throw new ServiceException("400", "댓글은 비어 있을 수 없습니다.");
}
validateComment(commentCreateRequest.comment());
Post post = postService.findPostById(postId);

PostComment postComment = PostComment.builder()
.post(post)
Expand All @@ -49,111 +46,113 @@ public List<CommentAllResponse> getAllPostCommentResponse(Long postId) {
validatePostExists(postId);

List<PostComment> listPostComment = postCommentRepository.findCommentsWithMemberByPostId(postId);

return listPostComment.stream()
.map(CommentAllResponse::from)
.toList();
}

@Transactional
public void removePostComment(Long postId, CommentDeleteRequest commentDeleteRequest, Member member) {
validatePostExists(postId);

PostComment postComment = getPostCommentById(commentDeleteRequest.commentId());
PostComment postComment = findCommentById(commentDeleteRequest.commentId());
Member author = postComment.getMember();


if(!Objects.equals(member.getId(), author.getId())) {
throw new ServiceException("400", "삭제 권한이 없습니다.");
}

// if(postComment.getIsAdopted()) {
// throw new ServiceException("400", "채택된 댓글은 삭제할 수 없습니다.");
// }
validateAuthorized(member,author);
validatePostExists(postId);

postCommentRepository.delete(postComment);

}

@Transactional
public void updatePostComment(Long postId, CommentModifyRequest commentModifyRequest, Member member) {
validatePostExists(postId);

PostComment postComment = getPostCommentById(commentModifyRequest.commentId());
PostComment postComment = findCommentById(commentModifyRequest.commentId());
Member author = postComment.getMember();


if(!Objects.equals(member.getId(), author.getId())) {
throw new ServiceException("400", "수정 권한이 없습니다.");
}

if ( commentModifyRequest.content() == null || commentModifyRequest.content().isEmpty()) {
throw new ServiceException("400", "댓글은 비어 있을 수 없습니다.");
}
validateAuthorized(member, author);
validateComment(commentModifyRequest.content());

postComment.updateContent(commentModifyRequest.content());
}

@Transactional
public void adoptComment(Long commentId, Member member) {
PostComment postComment = findCommentById(commentId);

Post post = postComment.getPost();

private void validatePostExists(Long postId) {
if(postId == null || postId <= 0) {
throw new ServiceException("400", "유효하지 않은 게시글 Id입니다.");
}
validateIsPostAuthor(post, member);
validatePostType(post);
validateAlreadyAdoptedComment(postComment);
validateAlreadyExistsAdoptedComment(post);

if (!postRepository.existsById(postId)) {
throw new ServiceException("400", "해당 Id의 게시글이 없습니다.");
}
postComment.adoptComment();
post.updateResolveStatus(true);
}

@Transactional
public CommentAllResponse getAdoptedComment(Long postId) {
Post post = postService.findPostById(postId);

}
validatePostType(post);

private PostComment getPostCommentById(Long commentId) {
return postCommentRepository.findById(commentId).orElseThrow(() -> new ServiceException("400", "해당 Id의 댓글이 없습니다."));
PostComment postComment = validateAdoptedComment(post);
return CommentAllResponse.from(postComment);
}

@Transactional
public void adoptComment(Long commentId, Member member) {
PostComment postComment = postCommentRepository.findById(commentId)
.orElseThrow(() -> new ServiceException("400", "해당 Id의 댓글이 없습니다."));

Post post = postComment.getPost();



// ========== 헬퍼 메소드들 ========== //
private void validateIsPostAuthor(Post post, Member member){
if (!post.isAuthor(member)) {
throw new ServiceException("400", "채택 권한이 없습니다.");
}
}

if (post.getPostType() != Post.PostType.QUESTIONPOST) {
throw new ServiceException("400", "질문 게시글에만 댓글 채택이 가능합니다.");
}

private void validateAlreadyAdoptedComment(PostComment postComment){
if (postComment.getIsAdopted()) {
throw new ServiceException("400", "이미 채택된 댓글입니다.");
}
}

// 이미 채택된 댓글이 있는지 확인
boolean alreadyAdopted = postCommentRepository.existsByPostAndIsAdoptedTrue(post);
if (alreadyAdopted) {
private void validateAlreadyExistsAdoptedComment(Post post) {
if (postCommentRepository.existsByPostAndIsAdoptedTrue(post)) {
throw new ServiceException("400", "이미 채택된 댓글이 있습니다.");
}
}

postComment.adoptComment();

post.updateResolveStatus(true);
private void validateAuthorized(Member member, Member author) {
if(!Objects.equals(member.getId(), author.getId())) {
throw new ServiceException("400", "변경 권한이 없습니다.");
}
}

@Transactional
public CommentAllResponse getAdoptedComment(Long postId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new ServiceException("400", "해당 Id의 게시글이 없습니다."));
private PostComment validateAdoptedComment(Post post) {
return postCommentRepository.findByPostAndIsAdoptedTrue(post)
.orElseThrow(() -> new ServiceException("400", "채택된 댓글이 없습니다."));
}

private void validatePostType(Post post) {
if (post.getPostType() != Post.PostType.QUESTIONPOST) {
throw new ServiceException("400", "질문 게시글만 채택된 댓글을 가질 수 있습니다.");
}
}

PostComment postComment = postCommentRepository.findByPostAndIsAdoptedTrue(post)
.orElseThrow(() -> new ServiceException("400", "채택된 댓글이 없습니다."));
private void validateComment(String comment) {
if (comment == null || comment.isEmpty()) {
throw new ServiceException("400", "댓글은 비어 있을 수 없습니다.");
}
}

return CommentAllResponse.from(postComment);
private void validatePostExists(Long postId) {
if (!postService.existsById(postId)) {
throw new ServiceException("400", "유효하지 않은 게시글 Id입니다.");
}
}

private PostComment findCommentById(Long commentId) {
return postCommentRepository.findById(commentId).orElseThrow(() -> new ServiceException("400", "해당 Id의 댓글이 없습니다."));
}
}
Loading