Skip to content

Commit

Permalink
Merge branch 'refs/heads/develop' into feature/#70
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/main/java/org/recordy/server/record/controller/RecordApi.java
#	src/main/java/org/recordy/server/record/controller/RecordController.java
#	src/main/java/org/recordy/server/record/repository/RecordRepository.java
#	src/main/java/org/recordy/server/record/repository/impl/RecordQueryDslRepository.java
#	src/main/java/org/recordy/server/record/service/RecordService.java
#	src/main/java/org/recordy/server/record/service/impl/RecordServiceImpl.java
#	src/main/java/org/recordy/server/user/controller/UserApi.java
#	src/test/java/org/recordy/server/mock/record/FakeRecordRepository.java
#	src/test/java/org/recordy/server/record/service/S3ServiceTest.java
  • Loading branch information
jinkonu committed Jul 15, 2024
2 parents 24d7b4e + 276de1b commit 356a94c
Show file tree
Hide file tree
Showing 32 changed files with 686 additions and 797 deletions.
105 changes: 97 additions & 8 deletions src/main/java/org/recordy/server/record/controller/RecordApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
import org.recordy.server.auth.security.UserId;
import org.recordy.server.common.message.ErrorMessage;
import org.recordy.server.record.controller.dto.request.RecordCreateRequest;
import org.recordy.server.record.controller.dto.response.RecordInfoWithBookmark;
import org.recordy.server.record.domain.File;
import org.recordy.server.record.domain.Record;
import org.springframework.data.domain.Slice;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Tag(name = "기록 관련 API")
public interface RecordApi {

Expand Down Expand Up @@ -163,9 +167,9 @@ public ResponseEntity<Void> deleteRecord(
)
}
)
public ResponseEntity<Slice<Record>> getRecentRecordsByUser(
public ResponseEntity<Slice<RecordInfoWithBookmark>> getRecentRecordInfosWithBookmarksByUser(
@UserId Long userId,
@RequestParam(required = false, defaultValue = "0") Long cursorId,
@RequestParam(required = false, defaultValue = "0") long cursorId,
@RequestParam(required = false, defaultValue = "10") int size
);

Expand Down Expand Up @@ -206,7 +210,7 @@ public ResponseEntity<Void> watch(
);

@Operation(
summary = "인기 레코드 조회 API",
summary = "인기 레코드 리스트 조회 API",
description = "사용자가 인기 레코드를 키워드와 함께 조회합니다. 키워드가 없으면 전체 인기 레코드를 조회합니다.",
responses = {
@ApiResponse(
Expand Down Expand Up @@ -241,14 +245,15 @@ public ResponseEntity<Void> watch(
)
}
)
public ResponseEntity<Slice<Record>> getFamousRecords(
@RequestParam(required = false) String keywords,
public ResponseEntity<Slice<RecordInfoWithBookmark>> getFamousRecordInfoWithBookmarks(
@UserId Long userId,
@RequestParam(required = false) List<String> keywords,
@RequestParam(required = false, defaultValue = "0") int pageNumber,
@RequestParam(required = false, defaultValue = "10") int pageSize
) ;

@Operation(
summary = "최근 레코드 조회 API",
summary = "최근 레코드 리스트 조회 API",
description = "사용자가 최근 레코드를 키워드와 함께 조회합니다. 키워드가 없으면 전체 최근 레코드를 조회합니다.",
responses = {
@ApiResponse(
Expand Down Expand Up @@ -283,9 +288,93 @@ public ResponseEntity<Slice<Record>> getFamousRecords(
)
}
)
public ResponseEntity<Slice<Record>> getRecentRecords(
@RequestParam(required = false) String keywords,
public ResponseEntity<Slice<RecordInfoWithBookmark>> getRecentRecordInfosWithBookmarks(
@UserId Long userId,
@RequestParam(required = false) List<String> keywords,
@RequestParam(required = false, defaultValue = "0") Long cursorId,
@RequestParam(required = false, defaultValue = "10") int size
);

@Operation(
summary = "구독 레코드 리스트 조회 API",
description = "구독 중인 유저의 레코드 리스트를 최신순으로 반환합니다.",
responses = {
@ApiResponse(
responseCode = "200",
description = "요청이 성공적으로 처리되었습니다.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(
implementation = Slice.class
)
)
),
@ApiResponse(
responseCode = "401",
description = "Unauthorized - 인증이 필요합니다.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(
implementation = ErrorMessage.class
)
)
),
@ApiResponse(
responseCode = "500",
description = "Internal Server Error - 서버 내부 오류입니다.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(
implementation = ErrorMessage.class
)
)
)
}
)
public ResponseEntity<Slice<RecordInfoWithBookmark>> getSubscribingRecordInfosWithBookmarks(
@UserId Long userId,
@RequestParam(required = false, defaultValue = "0") long cursorId,
@RequestParam(required = false, defaultValue = "10") int size
);

@Operation(
summary = "전체 레코드 조회 API",
description = "전체 레코드 중 정해진 크기만큼 랜덤으로 만든 레코드 리스트를 반환합니다.",
responses = {
@ApiResponse(
responseCode = "200",
description = "요청이 성공적으로 처리되었습니다.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(
implementation = Slice.class
)
)
),
@ApiResponse(
responseCode = "401",
description = "Unauthorized - 인증이 필요합니다.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(
implementation = ErrorMessage.class
)
)
),
@ApiResponse(
responseCode = "500",
description = "Internal Server Error - 서버 내부 오류입니다.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(
implementation = ErrorMessage.class
)
)
)
}
)
public ResponseEntity<List<RecordInfoWithBookmark>> getTotalRecordInfosWithBookmarks(
@UserId Long userId,
@RequestParam(required = false, defaultValue = "10") int size
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,31 @@

import lombok.RequiredArgsConstructor;
import org.recordy.server.auth.security.UserId;
import org.recordy.server.keyword.domain.Keyword;
import org.recordy.server.record.controller.dto.request.RecordCreateRequest;
import org.recordy.server.record.controller.dto.response.RecordInfoWithBookmark;
import org.recordy.server.record.domain.File;
import org.recordy.server.record.domain.Record;

import org.recordy.server.record.domain.usecase.RecordCreate;
import org.recordy.server.record.service.RecordService;
import org.recordy.server.record_stat.service.RecordStatService;
import org.recordy.server.record.service.S3Service;
import org.springframework.data.domain.Slice;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

@RequiredArgsConstructor
@RequestMapping("/api/v1/records")
@RestController
public class RecordController implements RecordApi {

private final RecordService recordService;
private final S3Service s3Service;
private final RecordStatService recordStatService;

@Override
@PostMapping
Expand All @@ -32,15 +36,15 @@ public ResponseEntity<Record> createRecord(
@RequestPart MultipartFile thumbnail,
@RequestPart MultipartFile video
) {
RecordCreate recordCreate = RecordCreate.of(uploaderId, request);
RecordCreate recordCreate = RecordCreate.from(uploaderId, request);
Record record = recordService.create(recordCreate, File.of(video, thumbnail));

return ResponseEntity
.status(HttpStatus.CREATED)
.body(record);
}

@Override
@Override
@DeleteMapping("/{recordId}")
public ResponseEntity<Void> deleteRecord(
@UserId Long uploaderId,
Expand All @@ -55,28 +59,32 @@ public ResponseEntity<Void> deleteRecord(

@Override
@GetMapping("/recent")
public ResponseEntity<Slice<Record>> getRecentRecords(
@RequestParam(required = false) String keywords,
public ResponseEntity<Slice<RecordInfoWithBookmark>> getRecentRecordInfosWithBookmarks(
@UserId Long userId,
@RequestParam(required = false) List<String> keywords,
@RequestParam(required = false, defaultValue = "0") Long cursorId,
@RequestParam(required = false, defaultValue = "10") int size
) {
Slice<Record> records = recordService.getRecentRecords(Keyword.decode(keywords), cursorId, size);
Slice<Record> records = recordService.getRecentRecords(keywords, cursorId, size);
List<Boolean> bookmarks = recordStatService.findBookmarks(userId, records.getContent());

return ResponseEntity
.ok()
.body(records);
return ResponseEntity.ok().body(RecordInfoWithBookmark.of(records, bookmarks));
}

@Override
@GetMapping("/famous")
public ResponseEntity<Slice<Record>> getFamousRecords(
@RequestParam(required = false) String keywords,
public ResponseEntity<Slice<RecordInfoWithBookmark>> getFamousRecordInfoWithBookmarks(
@UserId Long userId,
@RequestParam(required = false) List<String> keywords,
@RequestParam(required = false, defaultValue = "0") int pageNumber,
@RequestParam(required = false, defaultValue = "10") int pageSize
) {
){
Slice<Record> records = recordService.getFamousRecords(keywords, pageNumber, pageSize);
List<Boolean> bookmarks = recordStatService.findBookmarks(userId, records.getContent());

return ResponseEntity
.ok()
.body(recordService.getFamousRecords(Keyword.decode(keywords), pageNumber, pageSize));
.body(RecordInfoWithBookmark.of(records, bookmarks));
}

@Override
Expand All @@ -92,16 +100,47 @@ public ResponseEntity<Void> watch(
}

@Override
@GetMapping
public ResponseEntity<Slice<Record>> getRecentRecordsByUser(
@RequestParam Long userId,
@RequestParam(required = false, defaultValue = "0") Long cursorId,
@GetMapping("/user")
public ResponseEntity<Slice<RecordInfoWithBookmark>> getRecentRecordInfosWithBookmarksByUser(
@UserId Long userId,
@RequestParam(required = false, defaultValue = "0") long cursorId,
@RequestParam(required = false, defaultValue = "10") int size
) {
Slice<Record> records = recordService.getRecentRecordsByUser(userId, cursorId, size);
List<Boolean> bookmarks = recordStatService.findBookmarks(userId, records.getContent());

return ResponseEntity
.ok()
.body(RecordInfoWithBookmark.of(records, bookmarks));
}

@Override
@GetMapping("/follow")
public ResponseEntity<Slice<RecordInfoWithBookmark>> getSubscribingRecordInfosWithBookmarks(
@UserId Long userId,
@RequestParam(required = false, defaultValue = "0") long cursorId,
@RequestParam(required = false, defaultValue = "10") int size
) {
Slice<Record> records = recordService.getSubscribingRecords(userId, cursorId, size);
List<Boolean> bookmarks = recordStatService.findBookmarks(userId, records.getContent());

return ResponseEntity
.ok()
.body(records);
.body(RecordInfoWithBookmark.of(records, bookmarks));
}

@Override
@GetMapping
public ResponseEntity<List<RecordInfoWithBookmark>> getTotalRecordInfosWithBookmarks(
@UserId Long userId,
@RequestParam(required = false, defaultValue = "10") int size
){
List<Record> records = recordService.getTotalRecords(size);
List<Boolean> bookmarks = recordStatService.findBookmarks(userId, records);

return ResponseEntity
.ok()
.body(RecordInfoWithBookmark.of(records, bookmarks));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.recordy.server.record.controller.dto.response;

import org.recordy.server.record.service.dto.FileUrl;
import org.recordy.server.record.domain.Record;
public record RecordInfo (
Long id,
FileUrl fileUrl,
String location,
String content,
Long uploaderId,
String uploaderNickname,
Long bookmarkCount
){
public static RecordInfo from(Record record){
return new RecordInfo(
record.getId(),
record.getFileUrl(),
record.getLocation(),
record.getContent(),
record.getUploader().getId(),
record.getUploader().getNickname(),
record.getBookmarkCount()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.recordy.server.record.controller.dto.response;

import java.util.List;
import java.util.stream.Collectors;
import org.recordy.server.record.domain.Record;
import org.springframework.data.domain.Slice;

public record RecordInfoWithBookmark (
RecordInfo recordInfo,
Boolean isBookmark
){
public static Slice<RecordInfoWithBookmark> of (Slice<Record> records, List<Boolean> bookmarks) {
return records.map(record -> {
int index = records.getContent().indexOf(record);
Boolean isBookmarked = bookmarks.get(index);
RecordInfo recordInfo = RecordInfo.from(record);
return new RecordInfoWithBookmark(recordInfo, isBookmarked);
});
}

public static List<RecordInfoWithBookmark> of (List<Record> records, List<Boolean> bookmarks) {
return records.stream()
.map(record -> {
int index = records.indexOf(record);
Boolean isBookmarked = bookmarks.get(index);
RecordInfo recordInfo = RecordInfo.from(record);
return new RecordInfoWithBookmark(recordInfo, isBookmarked);
})
.collect(Collectors.toCollection(java.util.ArrayList::new));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public interface RecordRepository {
Slice<Record> findAllByIdAfterAndKeywordsOrderByIdDesc(List<Keyword> keywords, long cursor, Pageable pageable);
Slice<Record> findAllByUserIdOrderByIdDesc(long userId, long cursor, Pageable pageable);
Slice<Record> findAllBySubscribingUserIdOrderByIdDesc(long userId, long cursor, Pageable pageable);
Map<Keyword, Long> countAllByUserIdGroupByKeyword(long userId);
long countAllByUserId(long userId);
Optional<Long> findMaxId();
Long count();
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,13 @@ public long countAllByUserId(long userId) {
.where(recordEntity.user.id.eq(userId))
.fetchOne();
}

public Optional<Long> findMaxId() {
return Optional.ofNullable(jpaQueryFactory
.select(recordEntity.id.max())
.from(recordEntity)
.fetchOne()
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,14 @@ public Map<Keyword, Long> countAllByUserIdGroupByKeyword(long userId) {
public long countAllByUserId(long userId) {
return recordQueryDslRepository.countAllByUserId(userId);
}

@Override
public Optional<Long> findMaxId() {
return recordQueryDslRepository.findMaxId();
}

@Override
public Long count() {
return recordJpaRepository.count();
}
}
Loading

0 comments on commit 356a94c

Please sign in to comment.