Skip to content

Commit

Permalink
feat : 중앙동아리 클럽 목록 조회 API 구현 및 feed File 로직 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
KoSeonJe committed Jan 18, 2025
1 parent f2c527e commit f526a24
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.feed.controller.dto.request.CreateFeedRequest;
import ddingdong.ddingdongBE.domain.feed.controller.dto.request.UpdateFeedRequest;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.MyFeedPageResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -16,6 +19,7 @@
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;

@Tag(name = "Feed - Club", description = "Feed API")
Expand All @@ -26,7 +30,7 @@ public interface ClubFeedApi {
@ApiResponse(responseCode = "201", description = "동아리 피드 생성 성공")
@ResponseStatus(HttpStatus.CREATED)
@SecurityRequirement(name = "AccessToken")
@PostMapping("/clubs/feeds")
@PostMapping("/my/feeds")
void createFeed(
@RequestBody @Valid CreateFeedRequest createFeedRequest,
@AuthenticationPrincipal PrincipalDetails principalDetails
Expand All @@ -36,7 +40,7 @@ void createFeed(
@ApiResponse(responseCode = "204", description = "동아리 피드 수정 성공")
@ResponseStatus(HttpStatus.NO_CONTENT)
@SecurityRequirement(name = "AccessToken")
@PutMapping("/clubs/feeds/{feedId}")
@PutMapping("/my/feeds/{feedId}")
void updateFeed(
@PathVariable("feedId") Long feedId,
@RequestBody @Valid UpdateFeedRequest updateFeedRequest
Expand All @@ -46,9 +50,20 @@ void updateFeed(
@ApiResponse(responseCode = "204", description = "동아리 피드 삭제 성공")
@ResponseStatus(HttpStatus.NO_CONTENT)
@SecurityRequirement(name = "AccessToken")
@DeleteMapping("/clubs/feeds/{feedId}")
@DeleteMapping("/my/feeds/{feedId}")
void deleteFeed(
@PathVariable("feedId") Long feedId
);

@Operation(summary = "중앙 동아리 피드 목록 조회 API")
@ApiResponse(responseCode = "200", description = "중앙 동아리 피드 목록 조회 성공",
content = @Content(schema = @Schema(implementation = MyFeedPageResponse.class)))
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
@DeleteMapping("/my/feeds")
MyFeedPageResponse getMyFeedPage(
@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestParam(value = "size", defaultValue = "9") int size,
@RequestParam(value = "currentCursorId", defaultValue = "-1") Long currentCursorId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import ddingdong.ddingdongBE.domain.feed.api.ClubFeedApi;
import ddingdong.ddingdongBE.domain.feed.controller.dto.request.CreateFeedRequest;
import ddingdong.ddingdongBE.domain.feed.controller.dto.request.UpdateFeedRequest;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.MyFeedPageResponse;
import ddingdong.ddingdongBE.domain.feed.service.FacadeClubFeedService;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.MyFeedPageQuery;
import ddingdong.ddingdongBE.domain.user.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -36,4 +38,11 @@ public void updateFeed(
public void deleteFeed(Long feedId) {
facadeClubFeedService.delete(feedId);
}

@Override
public MyFeedPageResponse getMyFeedPage(PrincipalDetails principalDetails, int size, Long currentCursorId) {
User user = principalDetails.getUser();
MyFeedPageQuery query = facadeClubFeedService.getMyFeedPage(user, size, currentCursorId);
return MyFeedPageResponse.from(query);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ddingdong.ddingdongBE.domain.feed.controller.dto.response;

import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedListQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.MyFeedPageQuery;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.Builder;

public record MyFeedPageResponse(
@ArraySchema(schema = @Schema(name = "동아리 피드 정보", implementation = MyFeedPageResponse.class))
List<MyFeedListResponse> clubFeeds,
@Schema(name = "피드 페이지 정보", implementation = PagingResponse.class)
PagingResponse pagingInfo
) {


public static MyFeedPageResponse from(MyFeedPageQuery myFeedPageQuery) {
List<MyFeedListResponse> clubFeeds = myFeedPageQuery.feedListQueries().stream()
.map(MyFeedListResponse::from)
.toList();
return new MyFeedPageResponse(clubFeeds, PagingResponse.from(myFeedPageQuery.pagingQuery()));
}

@Builder
record MyFeedListResponse(
@Schema(description = "피드 ID", example = "1")
Long id,
@Schema(description = "피드 썸네일 CDN URL", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String thumbnailCdnUrl,
@Schema(description = "피드 썸네일 S3 URL", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String thumbnailOriginUrl,
@Schema(description = "피드 타입", example = "IMAGE")
String feedType
) {

public static MyFeedListResponse from(FeedListQuery feedListQuery) {
return MyFeedListResponse.builder()
.id(feedListQuery.id())
.thumbnailCdnUrl(feedListQuery.thumbnailCdnUrl())
.thumbnailOriginUrl(feedListQuery.thumbnailOriginUrl())
.feedType(feedListQuery.feedType())
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import ddingdong.ddingdongBE.domain.feed.service.dto.command.CreateFeedCommand;
import ddingdong.ddingdongBE.domain.feed.service.dto.command.UpdateFeedCommand;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.MyFeedPageQuery;
import ddingdong.ddingdongBE.domain.user.entity.User;

public interface FacadeClubFeedService {

Expand All @@ -10,4 +12,6 @@ public interface FacadeClubFeedService {
void update(UpdateFeedCommand command);

void delete(Long feedId);

MyFeedPageQuery getMyFeedPage(User user, int size, Long currentCursorId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
import ddingdong.ddingdongBE.domain.feed.entity.Feed;
import ddingdong.ddingdongBE.domain.feed.service.dto.command.CreateFeedCommand;
import ddingdong.ddingdongBE.domain.feed.service.dto.command.UpdateFeedCommand;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedListQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.MyFeedPageQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.PagingQuery;
import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType;
import ddingdong.ddingdongBE.domain.filemetadata.service.FileMetaDataService;
import ddingdong.ddingdongBE.domain.user.entity.User;
import ddingdong.ddingdongBE.domain.vodprocessing.entity.VodProcessingJob;
import ddingdong.ddingdongBE.domain.vodprocessing.service.VodProcessingJobService;
import ddingdong.ddingdongBE.sse.service.SseConnectionService;
import ddingdong.ddingdongBE.sse.service.dto.SseEvent;
import ddingdong.ddingdongBE.sse.service.dto.SseVodProcessingNotificationDto;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -27,6 +33,7 @@ public class FacadeClubFeedServiceImpl implements FacadeClubFeedService {
private final FeedService feedService;
private final VodProcessingJobService vodProcessingJobService;
private final SseConnectionService sseConnectionService;
private final FeedFileService feedFileService;

@Override
@Transactional
Expand Down Expand Up @@ -62,6 +69,18 @@ public void delete(Long feedId) {
fileMetaDataService.updateStatusToDelete(feed.getFeedType().getDomainType(), feed.getId());
}

@Override
public MyFeedPageQuery getMyFeedPage(User user, int size, Long currentCursorId) {
Club club = clubService.getByUserId(user.getId());
Slice<Feed> feedPage = feedService.getFeedPageByClubId(club.getId(), size, currentCursorId);
List<Feed> feeds = feedPage.getContent();

List<FeedListQuery> feedListQueries = feeds.stream().map(feedFileService::extractFeedThumbnailInfo).toList();
PagingQuery pagingQuery = PagingQuery.of(currentCursorId, feeds.get(feeds.size() -1).getId(), feedPage.hasNext());

return MyFeedPageQuery.of(feedListQueries, pagingQuery);
}

private void checkVodProcessingJobAndNotify(Feed feed) {
VodProcessingJob vodProcessingJob = vodProcessingJobService.getByVideoFeedId(feed.getId());
if (vodProcessingJob.isPossibleNotify()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package ddingdong.ddingdongBE.domain.feed.service;

import ddingdong.ddingdongBE.common.exception.PersistenceException.ResourceNotFound;
import ddingdong.ddingdongBE.domain.club.entity.Club;
import ddingdong.ddingdongBE.domain.feed.entity.Feed;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.ClubFeedPageQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.ClubProfileQuery;
Expand All @@ -10,12 +8,6 @@
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.NewestFeedPerClubPageQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.PagingQuery;
import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType;
import ddingdong.ddingdongBE.domain.filemetadata.entity.FileMetaData;
import ddingdong.ddingdongBE.domain.filemetadata.service.FileMetaDataService;
import ddingdong.ddingdongBE.file.service.S3FileService;
import ddingdong.ddingdongBE.file.service.dto.query.UploadedFileUrlQuery;
import ddingdong.ddingdongBE.file.service.dto.query.UploadedVideoUrlQuery;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Slice;
Expand All @@ -28,14 +20,14 @@
public class FacadeFeedService {

private final FeedService feedService;
private final FileMetaDataService fileMetaDataService;
private final S3FileService s3FileService;
private final FeedFileService feedFileService;


public ClubFeedPageQuery getFeedPageByClub(Long clubId, int size, Long currentCursorId) {
Slice<Feed> feedPage = feedService.getFeedPageByClubId(clubId, size, currentCursorId);
List<Feed> feeds = feedPage.getContent();

List<FeedListQuery> feedListQueries = feeds.stream().map(this::extractFeedThumbnailInfo).toList();
List<FeedListQuery> feedListQueries = feeds.stream().map(feedFileService::extractFeedThumbnailInfo).toList();
PagingQuery pagingQuery = PagingQuery.of(currentCursorId, feeds.get(feeds.size() -1).getId(), feedPage.hasNext());

return ClubFeedPageQuery.of(feedListQueries, pagingQuery);
Expand All @@ -45,70 +37,16 @@ public NewestFeedPerClubPageQuery getNewestFeedPerClubPage(int size, Long curren
Slice<Feed> feedPage = feedService.getNewestFeedPerClubPage(size, currentCursorId);
List<Feed> feeds = feedPage.getContent();

List<FeedListQuery> feedListQueries = feeds.stream().map(this::extractFeedThumbnailInfo).toList();
List<FeedListQuery> feedListQueries = feeds.stream().map(feedFileService::extractFeedThumbnailInfo).toList();
PagingQuery pagingQuery = PagingQuery.of(currentCursorId, feeds.get(feeds.size() -1).getId(), feedPage.hasNext());

return NewestFeedPerClubPageQuery.of(feedListQueries, pagingQuery);
}

public FeedQuery getById(Long feedId) {
Feed feed = feedService.getById(feedId);
ClubProfileQuery clubProfileQuery = extractClubInfo(feed.getClub());
FeedFileUrlQuery feedFileUrlQuery = extractFeedFileInfo(feed);
ClubProfileQuery clubProfileQuery = feedFileService.extractClubInfo(feed.getClub());
FeedFileUrlQuery feedFileUrlQuery = feedFileService.extractFeedFileInfo(feed);
return FeedQuery.of(feed, clubProfileQuery, feedFileUrlQuery);
}

private FeedListQuery extractFeedThumbnailInfo(Feed feed) {
FileMetaData fileMetaData = getFileMetaData(feed.getFeedType().getDomainType(), feed.getId());
if (feed.isImage()) {
UploadedFileUrlQuery urlQuery = s3FileService.getUploadedFileUrl(fileMetaData.getFileKey());
return new FeedListQuery(feed.getId(), urlQuery.cdnUrl(), urlQuery.originUrl(), feed.getFeedType().name());
}

if (feed.isVideo()) {
UploadedVideoUrlQuery urlQuery = s3FileService.getUploadedVideoUrl(fileMetaData.getFileKey());
return new FeedListQuery(feed.getId(), urlQuery.thumbnailCdnUrl(), urlQuery.thumbnailOriginUrl(), feed.getFeedType().name());
}

throw new IllegalArgumentException("FeedType은 Image 혹은 Video여야 합니다.");
}

private FeedFileUrlQuery extractFeedFileInfo(Feed feed) {
FileMetaData fileMetaData = getFileMetaData(feed.getFeedType().getDomainType(), feed.getId());
if (feed.isImage()) {
UploadedFileUrlQuery urlQuery = s3FileService.getUploadedFileUrl(fileMetaData.getFileKey());
return new FeedFileUrlQuery(urlQuery.id(), urlQuery.originUrl(), urlQuery.cdnUrl());
}

if (feed.isVideo()) {
UploadedVideoUrlQuery urlQuery = s3FileService.getUploadedVideoUrl(fileMetaData.getFileKey());
return new FeedFileUrlQuery(fileMetaData.getId().toString(), urlQuery.videoOriginUrl(), urlQuery.videoCdnUrl());
}

throw new IllegalArgumentException("FeedType은 Image 혹은 Video여야 합니다.");
}

private ClubProfileQuery extractClubInfo(Club club) {
String clubName = club.getName();
UploadedFileUrlQuery urlQuery = getFileUrl(DomainType.CLUB_PROFILE, club.getId());
if (urlQuery == null) {
return new ClubProfileQuery(club.getId(), clubName, null, null);
}
return new ClubProfileQuery(club.getId(), clubName, urlQuery.originUrl(), urlQuery.cdnUrl());
}

private FileMetaData getFileMetaData(DomainType domainType, Long id) {
return fileMetaDataService.getCoupledAllByDomainTypeAndEntityId(domainType, id)
.stream()
.findFirst()
.orElseThrow(() -> new ResourceNotFound("해당 FileMetaData(feedId: " + id + ")를 찾을 수 없습니다.)"));
}

private UploadedFileUrlQuery getFileUrl(DomainType domainType, Long clubId) {
return fileMetaDataService.getCoupledAllByDomainTypeAndEntityId(domainType, clubId)
.stream()
.map(fileMetaData -> s3FileService.getUploadedFileUrl(fileMetaData.getFileKey()))
.findFirst()
.orElse(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package ddingdong.ddingdongBE.domain.feed.service;

import ddingdong.ddingdongBE.common.exception.PersistenceException.ResourceNotFound;
import ddingdong.ddingdongBE.domain.club.entity.Club;
import ddingdong.ddingdongBE.domain.feed.entity.Feed;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.ClubProfileQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedFileUrlQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedListQuery;
import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType;
import ddingdong.ddingdongBE.domain.filemetadata.entity.FileMetaData;
import ddingdong.ddingdongBE.domain.filemetadata.service.FileMetaDataService;
import ddingdong.ddingdongBE.file.service.S3FileService;
import ddingdong.ddingdongBE.file.service.dto.query.UploadedFileUrlQuery;
import ddingdong.ddingdongBE.file.service.dto.query.UploadedVideoUrlQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class FeedFileService {

private final FileMetaDataService fileMetaDataService;
private final S3FileService s3FileService;

public FeedListQuery extractFeedThumbnailInfo(Feed feed) {
FileMetaData fileMetaData = getFileMetaData(feed.getFeedType().getDomainType(), feed.getId());
if (feed.isImage()) {
UploadedFileUrlQuery urlQuery = s3FileService.getUploadedFileUrl(fileMetaData.getFileKey());
return new FeedListQuery(feed.getId(), urlQuery.cdnUrl(), urlQuery.originUrl(), feed.getFeedType().name());
}

if (feed.isVideo()) {
UploadedVideoUrlQuery urlQuery = s3FileService.getUploadedVideoUrl(fileMetaData.getFileKey());
return new FeedListQuery(feed.getId(), urlQuery.thumbnailCdnUrl(), urlQuery.thumbnailOriginUrl(), feed.getFeedType().name());
}

throw new IllegalArgumentException("FeedType은 Image 혹은 Video여야 합니다.");
}

public FeedFileUrlQuery extractFeedFileInfo(Feed feed) {
FileMetaData fileMetaData = getFileMetaData(feed.getFeedType().getDomainType(), feed.getId());
if (feed.isImage()) {
UploadedFileUrlQuery urlQuery = s3FileService.getUploadedFileUrl(fileMetaData.getFileKey());
return new FeedFileUrlQuery(urlQuery.id(), urlQuery.originUrl(), urlQuery.cdnUrl());
}

if (feed.isVideo()) {
UploadedVideoUrlQuery urlQuery = s3FileService.getUploadedVideoUrl(fileMetaData.getFileKey());
return new FeedFileUrlQuery(fileMetaData.getId().toString(), urlQuery.videoOriginUrl(), urlQuery.videoCdnUrl());
}

throw new IllegalArgumentException("FeedType은 Image 혹은 Video여야 합니다.");
}

public ClubProfileQuery extractClubInfo(Club club) {
String clubName = club.getName();
UploadedFileUrlQuery urlQuery = getFileUrl(DomainType.CLUB_PROFILE, club.getId());
if (urlQuery == null) {
return new ClubProfileQuery(club.getId(), clubName, null, null);
}
return new ClubProfileQuery(club.getId(), clubName, urlQuery.originUrl(), urlQuery.cdnUrl());
}

private FileMetaData getFileMetaData(DomainType domainType, Long id) {
return fileMetaDataService.getCoupledAllByDomainTypeAndEntityId(domainType, id)
.stream()
.findFirst()
.orElseThrow(() -> new ResourceNotFound("해당 FileMetaData(feedId: " + id + ")를 찾을 수 없습니다.)"));
}

private UploadedFileUrlQuery getFileUrl(DomainType domainType, Long clubId) {
return fileMetaDataService.getCoupledAllByDomainTypeAndEntityId(domainType, clubId)
.stream()
.map(fileMetaData -> s3FileService.getUploadedFileUrl(fileMetaData.getFileKey()))
.findFirst()
.orElse(null);
}
}
Loading

0 comments on commit f526a24

Please sign in to comment.