Skip to content

Commit

Permalink
Feat: videoSummary 삭제하는 기능 구현
Browse files Browse the repository at this point in the history
- videoSummary 엔티티에 isDeleted, deletedAt  필드 추가
- VideoSummary 엔티티에 markAsDeleted 메서드 추가
- VideoSummaryService에 deleteVideoSummary 메서드 구현
- 이미 삭제 요청된 영상의 경우 에러메시지 출력되게 구현

- 삭제된 숏폼 리스트 반환하는 기능 구현
- 삭제된 숏폼 복구하는 기능 구현
  • Loading branch information
yunhacandy authored Jul 20, 2024
2 parents 0a55c7a + 1e6f148 commit f1532a4
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public Response<VideoSummaryDto> getSummaryByVideoSummaryId(@PathVariable(name =
@AuthenticationPrincipal Long userId
) {
log.info("summary requested videoSummaryId = {}", videoSummaryId);
return Response.createSuccess(videoSummaryService.getVideoSummaryById(videoSummaryId, userId));
return Response.createSuccess(videoSummaryService.getSummaryByVideoSummaryId(videoSummaryId, userId));
}

@Operation(summary = "영상 요약 목록 조회", description = "categoryId로 영상 요약 목록 조회를 위한 메소드")
Expand All @@ -76,4 +76,33 @@ public List<Long> getAllVideoIdsBySearchWord(@Parameter(name = "searchWord", des
log.info("검색어 '{}' 에 대한 모든 videoIds 조회 결과: {}", searchWord, videoIds);
return Response.createSuccess(videoIds).getData();
}

@Operation(summary = "숏폼 삭제", description = "사용자가 원하는 숏폼 삭제하는 메소드")
@ApiResponse(content = @Content(schema = @Schema(implementation = Response.class)))
@DeleteMapping("/summaries/delete/{videoSummaryId}")
@ResponseStatus(HttpStatus.OK)
public Response<?> deleteVideoSummary(@PathVariable Long videoSummaryId) {
log.info("Deleting video summary with ID = {}", videoSummaryId);
videoSummaryService.deleteVideoSummary(videoSummaryId);
return Response.createSuccess("숏폼 삭제 완료");
}

@Operation(summary = "삭제된 숏폼 목록 조회", description = "삭제된 모든 숏폼의 목록 조회를 위한 api")
@ApiResponse(content = @Content(schema = @Schema(implementation = VideoSummaryListResponse.class)))
@GetMapping("/summaries/deleted")
@ResponseStatus(HttpStatus.OK)
public Response<VideoSummaryListResponse> getAllDeletedVideoSummary() {
log.info("삭제된 숏폼 목록을 반환하는 중");
return Response.createSuccess(videoSummaryService.getAllDeletedVideoSummary());
}

@Operation(summary = "삭제된 숏폼 복구", description = "삭제된 숏폼을 복구하는 메소드")
@ApiResponse(content = @Content(schema = @Schema(implementation = Response.class)))
@PatchMapping("/summaries/restore/{videoSummaryId}")
@ResponseStatus(HttpStatus.OK)
public Response<?> restoreVideoSummary(@PathVariable Long videoSummaryId) {
log.info("Restoring deleted video summary with ID = {}", videoSummaryId);
videoSummaryService.restoreVideoSummary(videoSummaryId);
return Response.createSuccess("숏폼 복구 완료");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.hongik.graduationproject.enums.MainCategory;
import com.hongik.graduationproject.enums.Platform;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import lombok.*;

import java.util.List;
Expand Down Expand Up @@ -33,6 +34,10 @@ public class VideoSummary extends BaseTimeEntity {
private Platform platform;
@Enumerated(EnumType.STRING)
private MainCategory generatedMainCategory;
@Column(name = "is_deleted")
private boolean isDeleted = false;
@Column(name = "deleted_at")
private LocalDateTime deletedAt;

public static VideoSummary of(VideoSummaryMessage videoSummaryMessage) {
return VideoSummary
Expand Down Expand Up @@ -60,4 +65,14 @@ private static String listToString(List<String> keywords) {
return "";
}
}

public void markAsDeleted() {
this.isDeleted = true;
this.deletedAt = LocalDateTime.now();
}

public void restore() {
this.isDeleted = false;
this.deletedAt = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public enum ErrorCode {
CATEGORY_NOT_EXIST(HttpStatus.NOT_FOUND, "카테고리를 찾을 수 없습니다."),
USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 가입한 사용자입니다."),
SUBCATEGORY_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 서브 카테고리입니다."),
VIDEO_SUMMARY_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "사용자가 이미 해당 영상을 삭제 요청했습니다."),

// JWT 관련 에러
JWT_NOT_EXISTS(HttpStatus.UNAUTHORIZED, "요청에 JWT가 존재하지 않습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import java.util.List;

public interface VideoSummaryCategoryRepository extends JpaRepository<VideoSummaryCategory, Long> {

@Query("SELECT vsc FROM VideoSummaryCategory vsc WHERE vsc.category = :category AND vsc.videoSummary.isDeleted = false")
List<VideoSummaryCategory> findAllByCategory(Category category);

@Query("select vsc " +
Expand All @@ -25,4 +27,5 @@ public interface VideoSummaryCategoryRepository extends JpaRepository<VideoSumma
"WHERE vsc.category.user.id = :userId " +
"AND vsc.videoSummary.videoCode = :videoCode")
boolean existsByVideoCodeAndUserId(String videoCode, Long userId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@ public interface VideoSummaryRepository extends JpaRepository<VideoSummary, Long
Optional<VideoSummary> findByVideoCode(String videoCode);

@Query("SELECT vs.id FROM VideoSummary vs " +
"WHERE vs.title LIKE CONCAT('%', :searchword, '%')"
"WHERE (vs.title LIKE CONCAT('%', :searchword, '%')"
+ "OR vs.description LIKE CONCAT('%', :searchword, '%')"
+ "OR vs.summary LIKE CONCAT('%', :searchword, '%')"
+ "OR vs.keywords LIKE CONCAT('%', :searchword, '%')")
+ "OR vs.keywords LIKE CONCAT('%', :searchword, '%'))"
+ "AND vs.isDeleted = false"
)
List<Long> getAllVideoIdsBySearchWord(@Param("searchword") String searchWord);

List<VideoSummary> findAllByIsDeletedTrue();

@Query("SELECT vs FROM VideoSummary vs WHERE vs.id = :videoSummaryId AND vs.isDeleted = true")
Optional<VideoSummary> findDeletedById(@Param("videoSummaryId") Long videoSummaryId);
}
Original file line number Diff line number Diff line change
@@ -1,110 +1,141 @@
package com.hongik.graduationproject.service;

import com.hongik.graduationproject.domain.dto.video.*;
import com.hongik.graduationproject.domain.entity.Category;
import com.hongik.graduationproject.domain.entity.User;
import com.hongik.graduationproject.domain.entity.VideoSummary;
import com.hongik.graduationproject.domain.entity.VideoSummaryCategory;
import com.hongik.graduationproject.domain.entity.cache.VideoSummaryStatusCache;
import com.hongik.graduationproject.enums.Platform;
import com.hongik.graduationproject.exception.AppException;
import com.hongik.graduationproject.exception.ErrorCode;
import com.hongik.graduationproject.repository.*;
import com.hongik.graduationproject.util.UrlUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

import static com.hongik.graduationproject.enums.SummaryStatus.*;

@Service
@RequiredArgsConstructor
public class VideoSummaryService {
private final MessageService messageService;
private final VideoSummaryRepository videoSummaryRepository;
private final VideoSummaryStatusCacheRepository summaryStatusCacheRepository;
private final CategoryRepository categoryRepository;
private final VideoSummaryCategoryRepository videoSummaryCategoryRepository;
private final UserRepository userRepository;

public VideoSummaryInitiateResponse initiateSummarizing(VideoSummaryInitiateRequest summaryInitiateRequest, Long userId) {
Platform platform = UrlUtils.getVideoPlatform(summaryInitiateRequest.getUrl());
String videoId = UrlUtils.getVideoId(summaryInitiateRequest.getUrl(), platform);

String videoCode = platform.name() + '_' + videoId;

if (checkDuplicateSummarizing(videoCode, userId)) {
throw new AppException(ErrorCode.ALREADY_REQUESTED_SUMMARIZING);
}

Optional<VideoSummaryStatusCache> mayBeStatusCache = summaryStatusCacheRepository.findFirstByVideoCode(videoCode);
if (mayBeStatusCache.isPresent()) {
summaryStatusCacheRepository.save(VideoSummaryStatusCache.of(summaryInitiateRequest, userId, mayBeStatusCache.get()));
return new VideoSummaryInitiateResponse(videoCode);
}

Optional<VideoSummary> mayBeVideoSummary = videoSummaryRepository.findByVideoCode(videoCode);
if (mayBeVideoSummary.isPresent()) {
VideoSummary videoSummary = mayBeVideoSummary.get();

summaryStatusCacheRepository.save(VideoSummaryStatusCache.of(summaryInitiateRequest, userId, videoSummary));
return new VideoSummaryInitiateResponse(videoCode);
}

messageService.sendVideoUrlToQueue(new VideoSummaryInitiateMessage(summaryInitiateRequest.getUrl(), videoCode, platform));

summaryStatusCacheRepository.save(VideoSummaryStatusCache.of(summaryInitiateRequest, userId, videoCode));
return new VideoSummaryInitiateResponse(videoCode);
}

private boolean checkDuplicateSummarizing(String videoCode, Long userId) {
return videoSummaryCategoryRepository.existsByVideoCodeAndUserId(videoCode, userId) ||
summaryStatusCacheRepository.existsByVideoCodeAndUserId(videoCode, userId);
}

// 무조건 중복허용이 안되는 로직
public VideoSummaryDto getVideoSummaryById(Long videoSummaryId, Long userId) {
VideoSummary videoSummary = videoSummaryRepository.getReferenceById(videoSummaryId);
User user = userRepository.getReferenceById(userId);
VideoSummaryCategory videoSummaryCategory = videoSummaryCategoryRepository.findByVideoSummaryAndUser(videoSummary, user);

return VideoSummaryDto.from(videoSummaryCategory);
}

@Transactional
public VideoSummaryStatusResponse getStatus(String videoCode, Long userId) {
VideoSummaryStatusCache statusCache = summaryStatusCacheRepository.findByVideoCodeAndUserId(videoCode, userId)
.orElseThrow(() -> new AppException(ErrorCode.SUMMARIZING_STATUS_NOT_EXIST));

if (statusCache.getStatus().equals(COMPLETE.name())) {
Category category = categoryRepository.findDefaultCategoryByUserIdAndMainCategory(userId, statusCache.getGeneratedMainCategory())
.orElseThrow(() -> new AppException(ErrorCode.CATEGORY_NOT_EXIST));
VideoSummary videoSummary = videoSummaryRepository.getReferenceById(statusCache.getVideoSummaryId());

videoSummaryCategoryRepository.save(VideoSummaryCategory.builder()
.category(category)
.videoSummary(videoSummary)
.build());

summaryStatusCacheRepository.delete(statusCache);

}
return VideoSummaryStatusResponse.from(statusCache);
}

public VideoSummaryListResponse getAllSummariesByCategoryId(Long categoryId) {
Category category = categoryRepository.getReferenceById(categoryId);
List<VideoSummaryResponse> videoSummaryResponseList = videoSummaryCategoryRepository.findAllByCategory(category).stream()
.map(videoSummaryCategory -> new VideoSummaryResponse(videoSummaryCategory.getVideoSummary()))
.toList();
return new VideoSummaryListResponse(videoSummaryResponseList);
}

// 검색어를 포함하는 video id들을 조회하는 메서드
public List<Long> getAllVideoIdsBySearchWord(String searchWord) {
return videoSummaryRepository.getAllVideoIdsBySearchWord(searchWord);
}
}
package com.hongik.graduationproject.service;

import com.hongik.graduationproject.domain.dto.video.*;
import com.hongik.graduationproject.domain.entity.Category;
import com.hongik.graduationproject.domain.entity.User;
import com.hongik.graduationproject.domain.entity.VideoSummary;
import com.hongik.graduationproject.domain.entity.VideoSummaryCategory;
import com.hongik.graduationproject.domain.entity.cache.VideoSummaryStatusCache;
import com.hongik.graduationproject.enums.Platform;
import com.hongik.graduationproject.exception.AppException;
import com.hongik.graduationproject.exception.ErrorCode;
import com.hongik.graduationproject.repository.*;
import com.hongik.graduationproject.util.UrlUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

import static com.hongik.graduationproject.enums.SummaryStatus.*;

@Service
@RequiredArgsConstructor
public class VideoSummaryService {
private final MessageService messageService;
private final VideoSummaryRepository videoSummaryRepository;
private final VideoSummaryStatusCacheRepository summaryStatusCacheRepository;
private final CategoryRepository categoryRepository;
private final VideoSummaryCategoryRepository videoSummaryCategoryRepository;
private final UserRepository userRepository;

public VideoSummaryInitiateResponse initiateSummarizing(VideoSummaryInitiateRequest summaryInitiateRequest, Long userId) {
Platform platform = UrlUtils.getVideoPlatform(summaryInitiateRequest.getUrl());
String videoId = UrlUtils.getVideoId(summaryInitiateRequest.getUrl(), platform);

String videoCode = platform.name() + '_' + videoId;

if (checkDuplicateSummarizing(videoCode, userId)) {
throw new AppException(ErrorCode.ALREADY_REQUESTED_SUMMARIZING);
}

Optional<VideoSummaryStatusCache> mayBeStatusCache = summaryStatusCacheRepository.findFirstByVideoCode(videoCode);
if (mayBeStatusCache.isPresent()) {
summaryStatusCacheRepository.save(VideoSummaryStatusCache.of(summaryInitiateRequest, userId, mayBeStatusCache.get()));
return new VideoSummaryInitiateResponse(videoCode);
}

Optional<VideoSummary> mayBeVideoSummary = videoSummaryRepository.findByVideoCode(videoCode);
if (mayBeVideoSummary.isPresent()) {
VideoSummary videoSummary = mayBeVideoSummary.get();

summaryStatusCacheRepository.save(VideoSummaryStatusCache.of(summaryInitiateRequest, userId, videoSummary));
return new VideoSummaryInitiateResponse(videoCode);
}

messageService.sendVideoUrlToQueue(new VideoSummaryInitiateMessage(summaryInitiateRequest.getUrl(), videoCode, platform));

summaryStatusCacheRepository.save(VideoSummaryStatusCache.of(summaryInitiateRequest, userId, videoCode));
return new VideoSummaryInitiateResponse(videoCode);
}

private boolean checkDuplicateSummarizing(String videoCode, Long userId) {
return videoSummaryCategoryRepository.existsByVideoCodeAndUserId(videoCode, userId) ||
summaryStatusCacheRepository.existsByVideoCodeAndUserId(videoCode, userId);
}

// 무조건 중복허용이 안되는 로직
public VideoSummaryDto getSummaryByVideoSummaryId(Long videoSummaryId, Long userId) {
VideoSummary videoSummary = videoSummaryRepository.getReferenceById(videoSummaryId);
User user = userRepository.getReferenceById(userId);
VideoSummaryCategory videoSummaryCategory = videoSummaryCategoryRepository.findByVideoSummaryAndUser(videoSummary, user);

return VideoSummaryDto.from(videoSummaryCategory);
}

@Transactional
public VideoSummaryStatusResponse getStatus(String videoCode, Long userId) {
VideoSummaryStatusCache statusCache = summaryStatusCacheRepository.findByVideoCodeAndUserId(videoCode, userId)
.orElseThrow(() -> new AppException(ErrorCode.SUMMARIZING_STATUS_NOT_EXIST));

if (statusCache.getStatus().equals(COMPLETE.name())) {
Category category = categoryRepository.findDefaultCategoryByUserIdAndMainCategory(userId, statusCache.getGeneratedMainCategory())
.orElseThrow(() -> new AppException(ErrorCode.CATEGORY_NOT_EXIST));
VideoSummary videoSummary = videoSummaryRepository.getReferenceById(statusCache.getVideoSummaryId());

videoSummaryCategoryRepository.save(VideoSummaryCategory.builder()
.category(category)
.videoSummary(videoSummary)
.build());

summaryStatusCacheRepository.delete(statusCache);

}
return VideoSummaryStatusResponse.from(statusCache);
}

public VideoSummaryListResponse getAllSummariesByCategoryId(Long categoryId) {
Category category = categoryRepository.getReferenceById(categoryId);
List<VideoSummaryResponse> videoSummaryResponseList = videoSummaryCategoryRepository.findAllByCategory(category).stream()
.map(videoSummaryCategory -> new VideoSummaryResponse(videoSummaryCategory.getVideoSummary()))
.toList();
return new VideoSummaryListResponse(videoSummaryResponseList);
}

// 검색어를 포함하는 video id들을 조회하는 메서드
public List<Long> getAllVideoIdsBySearchWord(String searchWord) {
return videoSummaryRepository.getAllVideoIdsBySearchWord(searchWord);
}

@Transactional
public void deleteVideoSummary(Long videoSummaryId) {
VideoSummary videoSummary = videoSummaryRepository.findById(videoSummaryId)
.orElseThrow(() -> new AppException(ErrorCode.VIDEO_SUMMARY_NOT_FOUND));

if (videoSummary.isDeleted()) {
throw new AppException(ErrorCode.VIDEO_SUMMARY_ALREADY_DELETED);
}

videoSummary.markAsDeleted();
videoSummaryRepository.save(videoSummary);
}

@Transactional
public VideoSummaryListResponse getAllDeletedVideoSummary(){
List<VideoSummaryResponse> deletedSummaryList = videoSummaryRepository.findAllByIsDeletedTrue()
.stream()
.map(VideoSummaryResponse::new)
.toList();
return new VideoSummaryListResponse(deletedSummaryList);
}

@Transactional
public void restoreVideoSummary(Long videoSummaryId) {
VideoSummary videoSummary = videoSummaryRepository.findDeletedById(videoSummaryId)
.orElseThrow(() -> new AppException(ErrorCode.VIDEO_SUMMARY_NOT_FOUND));

videoSummary.restore();
videoSummaryRepository.save(videoSummary);
}
}

0 comments on commit f1532a4

Please sign in to comment.