From 3b05fffab0d298ec332b9c445cb8e20cc3fbab54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=ED=98=81?= <67510260+LEEJaeHyeok97@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:00:43 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20:sparkles:=20=EA=B0=90=EC=A0=95=20?= =?UTF-8?q?=EB=B3=84=20=EC=9D=BC=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20(#55)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial commit * Feat/#1 oauth2login (#3) * feat: User 엔터티 생성 * feat: jwt 버전 11->12, JWTUtil 생성 * feat: JWTFilter(JwtAuthenticationFilter) 등록 * feat: kakao 로그인 구현 * docs: swagger 태그(Authorization) 추가 (#5) * feat: User 엔터티 생성 * feat: jwt 버전 11->12, JWTUtil 생성 * feat: JWTFilter(JwtAuthenticationFilter) 등록 * feat: kakao 로그인 구현 * docs: swagger 태그(Authorization) 추가 * feat: accesstoken 테스트를 위한 test login 생성 (#9) * feat: User 엔티티에 상속 (#12) * feat: BaseEntity 생성 * feat: User 엔티티에 상속 * feat: 일기 생성 기능 구현 (#14) * feat: accesstoken 테스트를 위한 test login 생성 * feat: 일기 생성 기능 구현 * hotfix: ci 에러 수정 (#16) * feat: accesstoken 테스트를 위한 test login 생성 * feat: 일기 생성 기능 구현 * hotfix: ci 에러 수정 * fix: OIDC 카카오 로그인 nullPointerException 해결 * feat: 닉네임 설정 기능 구현 (#21) * feat: 일기 수정 기능 구현 (#25) * feat: 일기에 감정 컬럼 추가 * feat: 일기 수정 기능 구현 * feat: 일기 삭제 기능 구현 (#27) * feat: 일기에 감정 컬럼 추가 * feat: 일기 수정 기능 구현 * feat: 일기 삭제 기능 구현 * feat: 일기 감정 분석 기능 구현 (#31) * feat: 감정 저장 기능 구현 (#33) * feat: 일기 감정 분석 기능 구현 * feat: 감정 저장 기능 구현 * fix: :bug: 감정 저장 안되던 오류 수정 (#35) * feat: 일기 감정 분석 기능 구현 * feat: 감정 저장 기능 구현 * fix: :bug: 감정 저장 안되던 오류 수정 * hotfix: :ambulance: 서버 꺼짐 현상 해결 (#37) * feat: 일기 감정 분석 기능 구현 * feat: 감정 저장 기능 구현 * fix: :bug: 감정 저장 안되던 오류 수정 * hotfix: :ambulance: 서버 꺼짐 현상 해결 * feat: :sparkles: 홈 화면 조회 기능 구현 (#41) * feat: :sparkles: 회원가입 완료 여부 필드 추가 (#44) * feat: :sparkles: 일기 상세 조회 구현 (#47) * feat: :sparkles: 기간 별 감정 통계 조회 기능 구현 (#50) * feat: :sparkles: 일기 내용 검색 기능 구현 (#52) * feat: :sparkles: 감정 별 일기 조회 (#54) --- .../repository/DiaryQueryDslRepository.java | 4 ++ .../DiaryQueryDslRepositoryImpl.java | 39 +++++++++++++++++-- .../domain/repository/DiaryRepository.java | 3 ++ .../emotion/application/EmotionService.java | 11 ++++++ .../emotion/dto/DiarysByEmotionRes.java | 19 +++++++++ .../presentation/EmotionController.java | 18 +++++++++ 6 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/aidiary/domain/emotion/dto/DiarysByEmotionRes.java diff --git a/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepository.java b/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepository.java index a630568..a415de0 100644 --- a/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepository.java +++ b/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepository.java @@ -2,12 +2,14 @@ import com.aidiary.domain.diary.dto.SearchDiariesRes; import com.aidiary.domain.diary.dto.condition.DiariesSearchCondition; +import com.aidiary.domain.emotion.dto.DiarysByEmotionRes; import com.aidiary.domain.emotion.dto.EmotionStatRes; import com.aidiary.domain.home.dto.HomeViewRes; import com.aidiary.domain.user.domain.User; import com.aidiary.global.config.security.token.UserPrincipal; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import java.time.LocalDate; import java.util.List; @@ -21,4 +23,6 @@ public interface DiaryQueryDslRepository { EmotionStatRes findEmotionsCountBetweenStartDateAndEndDate(Long id, LocalDate startDate, LocalDate endDate); Page findDiaries(User user, DiariesSearchCondition diariesSearchCondition, Pageable pageable); + + Slice findAllByEmotionAndUserId(String emotion, Long id, Pageable pageable); } diff --git a/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepositoryImpl.java b/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepositoryImpl.java index 5bc71d2..99b9b3f 100644 --- a/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepositoryImpl.java +++ b/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryQueryDslRepositoryImpl.java @@ -1,22 +1,23 @@ package com.aidiary.domain.diary.domain.repository; -import com.aidiary.domain.diary.domain.QDiary; import com.aidiary.domain.diary.dto.QSearchDiariesRes; import com.aidiary.domain.diary.dto.SearchDiariesRes; import com.aidiary.domain.diary.dto.condition.DiariesSearchCondition; +import com.aidiary.domain.emotion.dto.DiarysByEmotionRes; import com.aidiary.domain.emotion.dto.EmotionStatRes; +import com.aidiary.domain.emotion.dto.QDiarysByEmotionRes; import com.aidiary.domain.emotion.dto.QEmotionStatRes; import com.aidiary.domain.home.dto.HomeViewRes; import com.aidiary.domain.home.dto.QHomeViewRes; import com.aidiary.domain.user.domain.User; -import com.aidiary.global.config.security.token.UserPrincipal; -import com.querydsl.core.types.Ops; import com.querydsl.core.types.dsl.*; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.stereotype.Repository; @@ -144,6 +145,37 @@ public Page findDiaries(User user, DiariesSearchCondition diar return PageableExecutionUtils.getPage(results, pageable, countQuery::fetchOne); } + @Override + public Slice findAllByEmotionAndUserId(String emotion, Long userId, Pageable pageable) { + List results = queryFactory + .select(new QDiarysByEmotionRes( + diary.content, + diary.diaryEntryDate + )) + .from(diary) + .where(diary.emotion.eq(emotion), + diary.user.id.eq(userId) + ) + .orderBy(diary.diaryEntryDate.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + + return toSlice(results, pageable); + } + + private Slice toSlice(List results, Pageable pageable) { + int pageSize = pageable.getPageSize(); + boolean hasNext = false; + + if (results.size() > pageSize) { + hasNext = true; + results.remove(pageSize); + } + return new SliceImpl<>(results, pageable, hasNext); + } + public BooleanExpression equalsUser(User user) { return user != null ? diary.user.eq(user) : null; } @@ -155,5 +187,4 @@ private StringTemplate getExcerpt(StringPath content, String keyword) { ); } - } diff --git a/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryRepository.java b/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryRepository.java index d3b7f0a..35d56f5 100644 --- a/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryRepository.java +++ b/src/main/java/com/aidiary/domain/diary/domain/repository/DiaryRepository.java @@ -3,5 +3,8 @@ import com.aidiary.domain.diary.domain.Diary; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; +import java.util.Optional; + public interface DiaryRepository extends JpaRepository, DiaryQueryDslRepository { } diff --git a/src/main/java/com/aidiary/domain/emotion/application/EmotionService.java b/src/main/java/com/aidiary/domain/emotion/application/EmotionService.java index c9f555a..9a0702a 100644 --- a/src/main/java/com/aidiary/domain/emotion/application/EmotionService.java +++ b/src/main/java/com/aidiary/domain/emotion/application/EmotionService.java @@ -2,6 +2,7 @@ import com.aidiary.domain.diary.domain.Diary; import com.aidiary.domain.diary.domain.repository.DiaryRepository; +import com.aidiary.domain.emotion.dto.DiarysByEmotionRes; import com.aidiary.domain.emotion.dto.EmotionStatRes; import com.aidiary.domain.user.domain.User; import com.aidiary.domain.user.domain.repository.UserRepository; @@ -9,10 +10,13 @@ import com.aidiary.global.payload.Message; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.util.List; @Service @RequiredArgsConstructor @@ -60,10 +64,17 @@ public Message saveEmotion(UserPrincipal userPrincipal, Long diaryId, String emo .build(); } + @Transactional public EmotionStatRes loadEmotionStat(UserPrincipal userPrincipal, LocalDate startDate, LocalDate endDate) { EmotionStatRes emotionsCountBetweenStartDateAndEndDate = diaryRepository.findEmotionsCountBetweenStartDateAndEndDate(userPrincipal.getId(), startDate, endDate); return emotionsCountBetweenStartDateAndEndDate; } + + @Transactional + public Slice findDiarys(UserPrincipal userPrincipal, String emotion, Pageable pageable) { + Slice allByEmotionAndUserId = diaryRepository.findAllByEmotionAndUserId(emotion, userPrincipal.getId(), pageable); + return allByEmotionAndUserId; + } } diff --git a/src/main/java/com/aidiary/domain/emotion/dto/DiarysByEmotionRes.java b/src/main/java/com/aidiary/domain/emotion/dto/DiarysByEmotionRes.java new file mode 100644 index 0000000..8d4ce7f --- /dev/null +++ b/src/main/java/com/aidiary/domain/emotion/dto/DiarysByEmotionRes.java @@ -0,0 +1,19 @@ +package com.aidiary.domain.emotion.dto; + +import com.querydsl.core.annotations.QueryProjection; +import lombok.Builder; + +import java.time.LocalDate; + +@Builder +public record DiarysByEmotionRes( + String content, + LocalDate diaryEntryDate +) { + + @QueryProjection + public DiarysByEmotionRes(String content, LocalDate diaryEntryDate) { + this.content = content; + this.diaryEntryDate = diaryEntryDate; + } +} diff --git a/src/main/java/com/aidiary/domain/emotion/presentation/EmotionController.java b/src/main/java/com/aidiary/domain/emotion/presentation/EmotionController.java index 19cb0cd..213602e 100644 --- a/src/main/java/com/aidiary/domain/emotion/presentation/EmotionController.java +++ b/src/main/java/com/aidiary/domain/emotion/presentation/EmotionController.java @@ -4,6 +4,7 @@ import com.aidiary.domain.emotion.application.EmotionService; import com.aidiary.domain.emotion.dto.ChatGPTReq; import com.aidiary.domain.emotion.dto.ChatGPTRes; +import com.aidiary.domain.emotion.dto.DiarysByEmotionRes; import com.aidiary.domain.emotion.dto.EmotionStatRes; import com.aidiary.global.config.security.token.CurrentUser; import com.aidiary.global.config.security.token.UserPrincipal; @@ -20,6 +21,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.cglib.core.Local; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; @@ -87,4 +91,18 @@ public ResponseCustom getEmotionStat( ) { return ResponseCustom.OK(emotionService.loadEmotionStat(userPrincipal, startDate, endDate)); } + + @Operation(summary = "감정 별 일기 조회", description = "감정 별 일기들을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "감정 별 일기 조회 성공", content = {@Content(mediaType = "application/json", schema = @Schema(implementation = DiarysByEmotionRes.class))}), + @ApiResponse(responseCode = "400", description = "감정 별 일기 조회 실패", content = {@Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}), + }) + @GetMapping("/search") + public ResponseCustom> findDiarysByEmotion( + @Parameter(description = "Accesstoken을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal, + @Parameter(description = "감정(행복, 우울, 화남, 불안)을 입력해주세요.", required = true) @RequestParam String emotion, + @Parameter(description = "조회할 페이지 크기를 입력해주세요.") @PageableDefault(size = 4) Pageable pageable + ) { + return ResponseCustom.OK(emotionService.findDiarys(userPrincipal, emotion, pageable)); + } }