Skip to content

Commit

Permalink
Merge pull request #22 from urdego/feat(#21)
Browse files Browse the repository at this point in the history
Feat(#21): 게임컨텐츠 api 구현
  • Loading branch information
j-ra1n authored Jan 20, 2025
2 parents e9e1fbc + 1c73a63 commit b2f6f02
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public class ContentController {
private final ContentService contentService;

// 컨텐츠 저장
// Todo: Security 인증 로직 필요
@Tag(name = "컨텐츠 API")
@Operation(summary = "컨텐츠 저장", description = "userId와 컨텐츠 그리고 사용자 입력을 받아 컨텐츠를 저장",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
Expand Down Expand Up @@ -54,7 +53,6 @@ public ResponseEntity<Void> saveContentMulti(@ModelAttribute ContentMultiSaveReq


// 컨텐츠 삭제
// Todo: 검증로직 필요
@Tag(name = "컨텐츠 API")
@Operation(summary = "컨텐츠 삭제", description = "userId와 contentId를 가지고 컨텐츠 개별 삭제")
@DeleteMapping(value = "{userId}/content/{contentId}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.urdego.urdego_content_service.api.controller;


import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.urdego.urdego_content_service.api.controller.dto.response.ContentResponse;
import io.urdego.urdego_content_service.domain.service.ContentService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/content-service")
@RequiredArgsConstructor
public class ContentGameController {

private final ContentService contentService;


@Tag(name = "백엔드 API")
@Operation(summary = "컨텐츠 개별 조회", description = "contentId로 컨텐츠 세부 조회")
@GetMapping(value = "/content")
public ResponseEntity<ContentResponse> getContent(@RequestParam Long contentId) {


ContentResponse responses = contentService.getContent(contentId);
return ResponseEntity.ok().body(responses);
}

@Tag(name = "백엔드 API")
@Operation(summary = "컨텐츠 랜덤 조회", description = "어데고 컨텐츠20개 랜덤 조회")
@GetMapping(value = "/urdego-content")
public ResponseEntity<List<ContentResponse>> getUrdegoContents(@RequestParam int counts) {

List<ContentResponse> responses = contentService.getUrdegoContents(counts);
return ResponseEntity.ok().body(responses);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.urdego.urdego_content_service.api.controller.dto.response;

import io.urdego.urdego_content_service.domain.entity.Content;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -22,4 +23,16 @@ public class ContentResponse {
private Double longitude;

private String hint;

public static ContentResponse of(Content content) {
return ContentResponse.builder()
.contentId(content.getId())
.url(content.getUrl())
.contentName(content.getContentName())
.address(content.getAddress())
.latitude(content.getLatitude())
.longitude(content.getLongitude())
.hint(content.getHint())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ public interface ContentRepositoryCustom {

// userId를 통해 유저의 전체 컨텐츠수를 반환한다.
Long countUserContentsByUserId(Long userId);

// userId를 통해 전체 컨텐츠를 조회한다.
List<ContentResponse> findUserContentsByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ public class ContentRepositoryImpl implements ContentRepositoryCustom {

// userId를 통해 해당 유저의 컨텐츠를 조회한다.
@Override
public List<ContentResponse> findUserContentsByUserId_CursorPaging(
Long userId, Long cursorIdx, Long limit) {
public List<ContentResponse> findUserContentsByUserId_CursorPaging(Long userId, Long cursorIdx, Long limit) {

JPAQuery<ContentResponse> query =
queryFactory.select(Projections.constructor(ContentResponse.class,
Expand Down Expand Up @@ -51,4 +50,24 @@ public Long countUserContentsByUserId(Long userId) {
.where(content.userId.eq(userId))
.fetchOne();
}


// userId를 통해 해당 유저의 컨텐츠를 조회한다.
@Override
public List<ContentResponse> findUserContentsByUserId(Long userId) {

JPAQuery<ContentResponse> query =
queryFactory.select(Projections.constructor(ContentResponse.class,
content.id,
content.url,
content.contentName,
content.address,
content.latitude,
content.longitude,
content.hint))
.from(content)
.where(content.userId.eq(userId));

return query.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private static String getFileExtension(String filename) {
String extension = filename.substring(filename.lastIndexOf(".")).toLowerCase();

// 유효한 확장자
List<String> validExtensions = List.of(".webp", ".WEBP", ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".mp4", ".mov");
List<String> validExtensions = List.of(".webp", ".WEBP", ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG");

// 확장자가 유효하지 않으면 예외 발생
if (!validExtensions.contains(extension.toLowerCase())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import io.urdego.urdego_content_service.api.controller.dto.request.ContentMultiSaveRequest;
import io.urdego.urdego_content_service.api.controller.dto.request.ContentSaveRequest;
import io.urdego.urdego_content_service.api.controller.dto.response.ContentResponse;
import io.urdego.urdego_content_service.api.controller.dto.response.UserContentListAndCursorIdxResponse;

import java.util.List;

public interface ContentService {

// 컨텐츠 저장
Expand All @@ -18,4 +21,9 @@ public interface ContentService {
// 컨텐츠 조회
UserContentListAndCursorIdxResponse getUserContents(Long userId, Long cursorIdx, Long limit);

// 컨텐츠 개별조회 (백엔드 API)
ContentResponse getContent(Long contentId);

// 컨텐츠 랜덤조회 (백엔드 API)
List<ContentResponse> getUrdegoContents(int counts);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ContentServiceImpl implements ContentService {

private static final Long MAX_LIMIT = 1L;
private static final Long ADMIN = 1L;
private final ContentRepository contentRepository;

// 컨텐츠 저장
Expand Down Expand Up @@ -133,6 +133,54 @@ public UserContentListAndCursorIdxResponse getUserContents(Long userId, Long cur
return response;
}

// 개별 컨텐츠 조회 (백엔드 API)
@Override
@Transactional(readOnly = true)
public ContentResponse getContent(Long contentId) {

Content content = findUserContentByIdOrException(contentId);

return ContentResponse.of(content);
}

// 어데고 컨텐츠 랜덤 조회 (백엔드 API)
@Override
@Transactional(readOnly = true)
public List<ContentResponse> getUrdegoContents(int counts) {

// 어데고 컨텐츠 조회
List<ContentResponse> urdegoContents = contentRepository.findUserContentsByUserId(ADMIN);

// 데이터가 3개미만 예외처리
if (urdegoContents.size() < 3) {
throw new UserContentException(ExceptionMessage.GAME_CONTENT_NOT_ENOUGH);
}

// contentName을 기준으로 그룹화
Map<String, List<ContentResponse>> groupedByName = urdegoContents.stream()
.collect(Collectors.groupingBy(ContentResponse::getContentName));

// 그룹화된 데이터를 처리
List<List<ContentResponse>> groupedList = groupedByName.values().stream()
.map(group -> {
// 그룹 내부를 셔플 후 최대 3개 선택
Collections.shuffle(group);
return group.stream().limit(3).toList();
})
.collect(Collectors.toList());

// 그룹 순서를 셔플
Collections.shuffle(groupedList);

// 결과 리스트 생성
List<ContentResponse> result = new ArrayList<>();
groupedList.forEach(result::addAll);

// counts에 맞게 제한
return result.stream()
.limit(counts)
.collect(Collectors.toList());
}

// 컨텐츠 엔티티 조회
private Content findUserContentByIdOrException(Long contentId) {
Expand Down

0 comments on commit b2f6f02

Please sign in to comment.