Skip to content

Commit

Permalink
feat: 편지 모아보기 API 구현 (#158)
Browse files Browse the repository at this point in the history
* v1.0.1: Github Actions 릴리즈 버저닝 자동화 스크립트 추가 및 잘못된  (#143)

* chore: Github Actions 릴리즈 버저닝 자동화 스크립트 추가 (#140)

* fix: Github Actions 잘못된 Indent 수정 (#142)

* feat: 편지 모아보기 API 구현
  • Loading branch information
leeeeeyeon committed Feb 22, 2024
1 parent d2efa0e commit 14b5b9d
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 1 deletion.
19 changes: 19 additions & 0 deletions packy-api/src/main/java/com/dilly/gift/api/GiftController.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dilly.gift.api;

import com.dilly.gift.application.GiftService;
import com.dilly.gift.dto.response.LetterResponse;
import com.dilly.gift.dto.response.PhotoResponseDto.PhotoWithoutSequenceResponse;
import com.dilly.global.response.DataResponseDto;
import com.dilly.global.response.SliceResponseDto;
Expand Down Expand Up @@ -42,4 +43,22 @@ public DataResponseDto<SliceResponseDto<PhotoWithoutSequenceResponse>> getPhotos
return DataResponseDto.from(
SliceResponseDto.from(giftService.getPhotos(lastPhotoId, pageable)));
}

@Operation(summary = "편지 모아보기")
@Parameter(in = ParameterIn.QUERY,
description = "한 페이지에 보여줄 편지 개수. 기본값은 6개",
name = "size",
schema = @Schema(type = "integer"))
@GetMapping("/letters")
public DataResponseDto<SliceResponseDto<LetterResponse>> getLetters(
@PageableDefault(size = 6)
@Parameter(hidden = true)
Pageable pageable,
@Schema(description = "마지막 편지의 id", type = "integer")
@RequestParam(value = "last-letter-id", required = false)
Long lastLetterId
) {
return DataResponseDto.from(
SliceResponseDto.from(giftService.getLetters(lastLetterId, pageable)));
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.dilly.gift.application;

import com.dilly.gift.adaptor.GiftBoxReader;
import com.dilly.gift.adaptor.LetterReader;
import com.dilly.gift.adaptor.PhotoReader;
import com.dilly.gift.adaptor.ReceiverReader;
import com.dilly.gift.domain.GiftBox;
import com.dilly.gift.domain.Letter;
import com.dilly.gift.domain.Photo;
import com.dilly.gift.domain.Receiver;
import com.dilly.gift.dto.response.LetterResponse;
import com.dilly.gift.dto.response.PhotoResponseDto.PhotoWithoutSequenceResponse;
import com.dilly.global.utils.SecurityUtil;
import com.dilly.member.adaptor.MemberReader;
Expand All @@ -27,8 +30,9 @@
public class GiftService {

private final MemberReader memberReader;
private final PhotoReader photoReader;
private final GiftBoxReader giftBoxReader;
private final PhotoReader photoReader;
private final LetterReader letterReader;
private final ReceiverReader receiverReader;

public Slice<PhotoWithoutSequenceResponse> getPhotos(Long lastPhotoId, Pageable pageable) {
Expand All @@ -51,4 +55,25 @@ public Slice<PhotoWithoutSequenceResponse> getPhotos(Long lastPhotoId, Pageable

return new SliceImpl<>(photoResponses, pageable, photoSlice.hasNext());
}

public Slice<LetterResponse> getLetters(Long lastLetterId, Pageable pageable) {
Long memberId = SecurityUtil.getMemberId();
Member member = memberReader.findById(memberId);

LocalDateTime lastLetterDate = LocalDateTime.now();
if (lastLetterId != null) {
Letter lastLetter = letterReader.findById(lastLetterId);
GiftBox giftBox = giftBoxReader.findByLetter(lastLetter);
Receiver lastReceiver = receiverReader.findByMemberAndGiftBox(member, giftBox);

lastLetterDate = lastReceiver.getCreatedAt();
}
Slice<Letter> letterSlice = letterReader.searchBySlice(member, lastLetterDate, pageable);

List<LetterResponse> letterResponses = letterSlice.stream()
.map(LetterResponse::from)
.toList();

return new SliceImpl<>(letterResponses, pageable, letterSlice.hasNext());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.dilly.gift.dto.response;

import com.dilly.gift.domain.Letter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Builder
public record LetterResponse(
@Schema(example = "1")
Long id,
@Schema(example = "이 편지는 영국에서 시작되어...")
String letterContent,
EnvelopeResponse envelope
) {

public static LetterResponse from(Letter letter) {
return LetterResponse.builder()
.id(letter.getId())
.letterContent(letter.getContent())
.envelope(EnvelopeResponse.of(letter.getEnvelope()))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public enum ErrorCode {
STICKER_NOT_FOUND(HttpStatus.NOT_FOUND, "스티커를 찾을 수 없습니다."),
GIFTBOX_NOT_FOUND(HttpStatus.NOT_FOUND, "선물박스를 찾을 수 없습니다."),
PHOTO_NOT_FOUND(HttpStatus.NOT_FOUND, "사진을 찾을 수 없습니다."),
LETTER_NOT_FOUND(HttpStatus.NOT_FOUND, "편지를 찾을 수 없습니다."),

// Unsupported
UNSUPPORTED_LOGIN_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 로그인 타입입니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.dilly.gift.dao.GiftBoxRepository;
import com.dilly.gift.dao.querydsl.GiftBoxQueryRepository;
import com.dilly.gift.domain.GiftBox;
import com.dilly.gift.domain.Letter;
import com.dilly.member.domain.Member;
import java.time.LocalDateTime;
import java.util.Comparator;
Expand All @@ -25,6 +26,10 @@ public GiftBox findById(Long giftBoxId) {
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.GIFTBOX_NOT_FOUND));
}

public GiftBox findByLetter(Letter letter) {
return giftBoxRepository.findByLetter(letter);
}

public Slice<GiftBox> searchSentGiftBoxesBySlice(Member member, LocalDateTime lastGiftBoxDate,
Pageable pageable) {
return giftBoxQueryRepository.searchSentGiftBoxesBySlice(member, lastGiftBoxDate, pageable);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.dilly.gift.adaptor;

import com.dilly.exception.ErrorCode;
import com.dilly.exception.entitynotfound.EntityNotFoundException;
import com.dilly.gift.dao.LetterRepository;
import com.dilly.gift.dao.querydsl.LetterQueryRepository;
import com.dilly.gift.domain.Letter;
import com.dilly.member.domain.Member;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class LetterReader {

private final LetterRepository letterRepository;
private final LetterQueryRepository letterQueryRepository;

public Letter findById(Long id) {
return letterRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.LETTER_NOT_FOUND));
}

public Slice<Letter> searchBySlice(Member member, LocalDateTime lastLetterDate, Pageable pageable) {
return letterQueryRepository.searchBySlice(member, lastLetterDate, pageable);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.dilly.gift.dao;

import com.dilly.gift.domain.GiftBox;
import com.dilly.gift.domain.Letter;
import org.springframework.data.jpa.repository.JpaRepository;

public interface GiftBoxRepository extends JpaRepository<GiftBox, Long> {

GiftBox findTopByOrderByIdDesc();

GiftBox findByLetter(Letter letter);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.dilly.gift.dao.querydsl;

import static com.dilly.gift.domain.QGiftBox.giftBox;
import static com.dilly.gift.domain.QLetter.letter;
import static com.dilly.gift.domain.QReceiver.receiver;

import com.dilly.gift.domain.Letter;
import com.dilly.member.domain.Member;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class LetterQueryRepository {

private final JPAQueryFactory jpaQueryFactory;

public Slice<Letter> searchBySlice(Member member, LocalDateTime lastLetterDate, Pageable pageable) {
List<Letter> results = jpaQueryFactory.select(letter)
.from(receiver)
.join(receiver.giftBox, giftBox)
.join(giftBox.letter, letter)
.where(
ltLetterDate(lastLetterDate),
receiver.member.eq(member)
)
.orderBy(receiver.createdAt.desc())
.limit(pageable.getPageSize() + 1L)
.fetch();

return checkLastPage(pageable, results);
}

private BooleanExpression ltLetterDate(LocalDateTime lastLetterDate) {
if (lastLetterDate == null) {
return null;
}

return receiver.createdAt.lt(lastLetterDate);
}

private Slice<Letter> checkLastPage(Pageable pageable, List<Letter> results) {
boolean hasNext = false;

if (results.size() > pageable.getPageSize()) {
results.remove(results.size() - 1);
hasNext = true;
}

return new SliceImpl<>(results, pageable, hasNext);
}
}

0 comments on commit 14b5b9d

Please sign in to comment.