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 @@ -61,6 +61,6 @@ ResponseEntity<SuccessResponse<PageResponse<SeriesListResponse>>> getSeries(
)
})
ResponseEntity<SuccessResponse<SeriesDetailResponse>> getSeriesDetail(
@Parameter(description = "시리즈 ID", required = true, example = "1") @PathVariable Long seriesId
@Parameter(description = "미디어 ID", required = true, example = "1") @PathVariable Long mediaId
);
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
//package com.ott.api_admin.series.controller;
//
//import com.ott.api_admin.series.dto.response.SeriesDetailResponse;
//import com.ott.api_admin.series.dto.response.SeriesListResponse;
//import com.ott.api_admin.series.service.BackOfficeSeriesService;
//import com.ott.common.web.response.PageResponse;
//import com.ott.common.web.response.SuccessResponse;
//import lombok.RequiredArgsConstructor;
//import org.springframework.http.ResponseEntity;
//import org.springframework.web.bind.annotation.*;
//
//@RestController
//@RequestMapping("/back-office")
//@RequiredArgsConstructor
//public class BackOfficeSeriesController implements BackOfficeSeriesApi {
//
// private final BackOfficeSeriesService backOfficeSeriesService;
//
// @Override
// @GetMapping("/admin/series")
// public ResponseEntity<SuccessResponse<PageResponse<SeriesListResponse>>> getSeries(
// @RequestParam(value = "page", defaultValue = "0") Integer page,
// @RequestParam(value = "size", defaultValue = "10") Integer size,
// @RequestParam(value = "searchWord", required = false) String searchWord
// ) {
// return ResponseEntity.ok(
// SuccessResponse.of(backOfficeSeriesService.getSeries(page, size, searchWord))
// );
// }
//
// @Override
// @GetMapping("/admin/series/{seriesId}")
// public ResponseEntity<SuccessResponse<SeriesDetailResponse>> getSeriesDetail(@PathVariable("seriesId") Long seriesId) {
// return ResponseEntity.ok(
// SuccessResponse.of(backOfficeSeriesService.getSeriesDetail(seriesId))
// );
// }
//}
package com.ott.api_admin.series.controller;

import com.ott.api_admin.series.dto.response.SeriesDetailResponse;
import com.ott.api_admin.series.dto.response.SeriesListResponse;
import com.ott.api_admin.series.service.BackOfficeSeriesService;
import com.ott.common.web.response.PageResponse;
import com.ott.common.web.response.SuccessResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/back-office")
@RequiredArgsConstructor
public class BackOfficeSeriesController implements BackOfficeSeriesApi {

private final BackOfficeSeriesService backOfficeSeriesService;

@Override
@GetMapping("/admin/series")
public ResponseEntity<SuccessResponse<PageResponse<SeriesListResponse>>> getSeries(
@RequestParam(value = "page", defaultValue = "0") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size,
@RequestParam(value = "searchWord", required = false) String searchWord
) {
return ResponseEntity.ok(
SuccessResponse.of(backOfficeSeriesService.getSeries(page, size, searchWord))
);
}

@Override
@GetMapping("/admin/series/{mediaId}")
public ResponseEntity<SuccessResponse<SeriesDetailResponse>> getSeriesDetail(@PathVariable("mediaId") Long mediaId) {
return ResponseEntity.ok(
SuccessResponse.of(backOfficeSeriesService.getSeriesDetail(mediaId))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
@Schema(description = "시리즈 목록 조회 응답")
public record SeriesListResponse(

@Schema(type = "Long", description = "시리즈 ID", example = "1")
Long seriesId,
@Schema(type = "Long", description = "미디어 ID (시리즈에서 참조)", example = "1")
Long mediaId,

@Schema(type = "String", description = "썸네일 URL", example = "https://cdn.example.com/thumbnail.jpg")
String thumbnailUrl,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,60 @@
//package com.ott.api_admin.series.mapper;
//
//import com.ott.api_admin.series.dto.response.SeriesDetailResponse;
//import com.ott.api_admin.series.dto.response.SeriesListResponse;
//import com.ott.domain.series.domain.Series;
//import com.ott.domain.series_tag.domain.SeriesTag;
//import org.springframework.stereotype.Component;
//
//import java.util.List;
//
//@Component
//public class BackOfficeSeriesMapper {
//
// public SeriesListResponse toSeriesListResponse(Series series, List<SeriesTag> seriesTagList) {
// String categoryName = extractCategoryName(seriesTagList);
// List<String> tagNameList = extractTagNameList(seriesTagList);
//
// return new SeriesListResponse(
// series.getId(),
// series.getThumbnailUrl(),
// series.getTitle(),
// categoryName,
// tagNameList,
// series.getPublicStatus()
// );
// }
//
// public SeriesDetailResponse toSeriesDetailResponse(Series series, List<SeriesTag> seriesTagList) {
// String categoryName = extractCategoryName(seriesTagList);
// List<String> tagNameList = extractTagNameList(seriesTagList);
//
// return new SeriesDetailResponse(
// series.getId(),
// series.getTitle(),
// series.getDescription(),
// categoryName,
// tagNameList,
// series.getPublicStatus(),
// series.getUploader().getNickname(),
// series.getBookmarkCount(),
// series.getActors(),
// series.getPosterUrl(),
// series.getThumbnailUrl()
// );
// }
//
// private String extractCategoryName(List<SeriesTag> seriesTagList) {
// return seriesTagList.stream()
// .findFirst()
// .map(st -> st.getTag().getCategory().getName())
// .orElse(null);
// }
//
// private List<String> extractTagNameList(List<SeriesTag> seriesTagList) {
// return seriesTagList.stream()
// .map(st -> st.getTag().getName())
// .toList();
// }
//}
package com.ott.api_admin.series.mapper;

import com.ott.api_admin.series.dto.response.SeriesDetailResponse;
import com.ott.api_admin.series.dto.response.SeriesListResponse;
import com.ott.domain.media.domain.Media;
import com.ott.domain.media_tag.domain.MediaTag;
import com.ott.domain.series.domain.Series;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class BackOfficeSeriesMapper {

public SeriesListResponse toSeriesListResponse(Media media, List<MediaTag> mediaTagList) {
String categoryName = extractCategoryName(mediaTagList);
List<String> tagNameList = extractTagNameList(mediaTagList);

return new SeriesListResponse(
media.getId(),
media.getThumbnailUrl(),
media.getTitle(),
categoryName,
tagNameList,
media.getPublicStatus()
);
}

public SeriesDetailResponse toSeriesDetailResponse(Series series, Media media, String uploaderName, List<MediaTag> mediaTagList) {
String categoryName = extractCategoryName(mediaTagList);
List<String> tagNameList = extractTagNameList(mediaTagList);

return new SeriesDetailResponse(
series.getId(),
media.getTitle(),
media.getDescription(),
categoryName,
tagNameList,
media.getPublicStatus(),
uploaderName,
media.getBookmarkCount(),
series.getActors(),
media.getPosterUrl(),
media.getThumbnailUrl()
);
}

private String extractCategoryName(List<MediaTag> mediaTagList) {
return mediaTagList.stream()
.findFirst()
.map(mt -> mt.getTag().getCategory().getName())
.orElse(null);
}

private List<String> extractTagNameList(List<MediaTag> mediaTagList) {
return mediaTagList.stream()
.map(mt -> mt.getTag().getName())
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,80 +1,86 @@
//package com.ott.api_admin.series.service;
//
//import com.ott.api_admin.series.dto.response.SeriesDetailResponse;
//import com.ott.api_admin.series.dto.response.SeriesListResponse;
//import com.ott.api_admin.series.mapper.BackOfficeSeriesMapper;
//import com.ott.common.web.exception.BusinessException;
//import com.ott.common.web.exception.ErrorCode;
//import com.ott.domain.series.repository.SeriesRepository;
//import com.ott.domain.series_tag.repository.SeriesTagRepository;
//import com.ott.common.web.response.PageInfo;
//import com.ott.common.web.response.PageResponse;
//import com.ott.domain.series.domain.Series;
//import com.ott.domain.series_tag.domain.SeriesTag;
//import lombok.RequiredArgsConstructor;
//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 org.springframework.util.StringUtils;
//
//import java.util.Collections;
//import java.util.List;
//import java.util.Map;
//import java.util.stream.Collectors;
//
//@RequiredArgsConstructor
//@Service
//public class BackOfficeSeriesService {
//
// private final BackOfficeSeriesMapper backOfficeSeriesMapper;
//
// private final SeriesRepository seriesRepository;
// private final SeriesTagRepository seriesTagRepository;
//
// @Transactional(readOnly = true)
// public PageResponse<SeriesListResponse> getSeries(int page, int size, String searchWord) {
// Pageable pageable = PageRequest.of(page, size);
//
// // 1. keyword 유무에 따라 분기 / 시리즈 대상 페이징
// Page<Series> seriesPage = seriesRepository.findSeriesList(pageable, searchWord);
//
// // 2. 조회된 시리즈 ID 목록 추출
// List<Long> seriesIdList = seriesPage.getContent().stream()
// .map(Series::getId)
// .toList();
//
// // 3. IN절로 태그 일괄 조회
// Map<Long, List<SeriesTag>> tagListBySeriesId = seriesIdList.isEmpty()
// ? Collections.emptyMap()
// : seriesTagRepository.findWithTagAndCategoryBySeriesIds(seriesIdList).stream()
// .collect(Collectors.groupingBy(st -> st.getSeries().getId()));
//
// List<SeriesListResponse> responseList = seriesPage.getContent().stream()
// .map(series -> backOfficeSeriesMapper.toSeriesListResponse(
// series,
// tagListBySeriesId.getOrDefault(series.getId(), List.of())
// ))
// .toList();
//
// PageInfo pageInfo = PageInfo.toPageInfo(
// seriesPage.getNumber(),
// seriesPage.getTotalPages(),
// seriesPage.getSize()
// );
// return PageResponse.toPageResponse(pageInfo, responseList);
// }
//
// @Transactional(readOnly = true)
// public SeriesDetailResponse getSeriesDetail(Long seriesId) {
// Series series = seriesRepository.findById(seriesId)
// .orElseThrow(() -> new BusinessException(ErrorCode.SERIES_NOT_FOUND));
//
// List<SeriesTag> seriesTagList = seriesTagRepository
// .findWithTagAndCategoryBySeriesIds(List.of(seriesId));
//
// return backOfficeSeriesMapper.toSeriesDetailResponse(series, seriesTagList);
// }
//}
package com.ott.api_admin.series.service;

import com.ott.api_admin.series.dto.response.SeriesDetailResponse;
import com.ott.api_admin.series.dto.response.SeriesListResponse;
import com.ott.api_admin.series.mapper.BackOfficeSeriesMapper;
import com.ott.common.web.exception.BusinessException;
import com.ott.common.web.exception.ErrorCode;
import com.ott.domain.common.MediaType;
import com.ott.domain.media.domain.Media;
import com.ott.domain.media.repository.MediaRepository;
import com.ott.domain.media_tag.domain.MediaTag;
import com.ott.domain.media_tag.repository.MediaTagRepository;
import com.ott.common.web.response.PageInfo;
import com.ott.common.web.response.PageResponse;
import com.ott.domain.series.domain.Series;
import com.ott.domain.series.repository.SeriesRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@RequiredArgsConstructor
@Service
public class BackOfficeSeriesService {

private final BackOfficeSeriesMapper backOfficeSeriesMapper;

private final MediaRepository mediaRepository;
private final MediaTagRepository mediaTagRepository;
private final SeriesRepository seriesRepository;

@Transactional(readOnly = true)
public PageResponse<SeriesListResponse> getSeries(int page, int size, String searchWord) {
Pageable pageable = PageRequest.of(page, size);

// 1. 미디어 중 시리즈 대상 페이징
Page<Media> mediaPage = mediaRepository.findMediaListByMediaType(pageable, MediaType.SERIES, searchWord);

// 2. 조회된 미디어 ID 목록 추출
List<Long> mediaIdList = mediaPage.getContent().stream()
.map(Media::getId)
.toList();

// 3. IN절로 태그 일괄 조회
Map<Long, List<MediaTag>> tagListByMediaId = mediaIdList.isEmpty()
? Collections.emptyMap()
: mediaTagRepository.findWithTagAndCategoryByMediaIds(mediaIdList).stream()
.collect(Collectors.groupingBy(mt -> mt.getMedia().getId()));

List<SeriesListResponse> responseList = mediaPage.getContent().stream()
.map(media -> backOfficeSeriesMapper.toSeriesListResponse(
media,
tagListByMediaId.getOrDefault(media.getId(), List.of())
))
.toList();

PageInfo pageInfo = PageInfo.toPageInfo(
mediaPage.getNumber(),
mediaPage.getTotalPages(),
mediaPage.getSize()
);
return PageResponse.toPageResponse(pageInfo, responseList);
}

@Transactional(readOnly = true)
public SeriesDetailResponse getSeriesDetail(Long mediaId) {
// 1. Series + Media + Uploader 한 번에 조회
Series series = seriesRepository.findWithMediaAndUploaderByMediaId(mediaId)
.orElseThrow(() -> new BusinessException(ErrorCode.SERIES_NOT_FOUND));

Media media = series.getMedia();
String uploaderNickname = media.getUploader().getNickname();

// 2. 태그 조회
List<MediaTag> mediaTagList = mediaTagRepository.findWithTagAndCategoryByMediaId(mediaId);

return backOfficeSeriesMapper.toSeriesDetailResponse(series, media, uploaderNickname, mediaTagList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.ott.domain.media.repository;

import com.ott.domain.media.domain.Media;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MediaRepository extends JpaRepository<Media, Long>, MediaRepositoryCustom {
}
Loading