From c79b73b159ab7ed6a7eac799c1438380113fb387 Mon Sep 17 00:00:00 2001 From: Wang HoEun Date: Fri, 29 Mar 2024 14:00:12 +0900 Subject: [PATCH 01/43] =?UTF-8?q?chore:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/common/config/AWSConfig.java | 96 +++++++++---------- src/main/resources/application.yml | 37 +++---- 2 files changed, 63 insertions(+), 70 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java index 8499a83..00bbf5c 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java @@ -1,48 +1,48 @@ -package org.kau.kkoolbeeServer.global.common.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; - -@Configuration -public class AWSConfig { - - private static final String AWS_ACCESS_KEY_ID = "aws.accessKeyId"; - private static final String AWS_SECRET_ACCESS_KEY = "aws.secretAccessKey"; - - private final String accessKey; - private final String secretKey; - private final String regionString; - - public AWSConfig(@Value("${aws-property.access-key}") final String accessKey, - @Value("${aws-property.secret-key}") final String secretKey, - @Value("${aws-property.aws-region}") final String regionString) { - this.accessKey = accessKey; - this.secretKey = secretKey; - this.regionString = regionString; - } - - - @Bean - public SystemPropertyCredentialsProvider systemPropertyCredentialsProvider() { - System.setProperty(AWS_ACCESS_KEY_ID, accessKey); - System.setProperty(AWS_SECRET_ACCESS_KEY, secretKey); - return SystemPropertyCredentialsProvider.create(); - } - - @Bean - public Region getRegion() { - return Region.of(regionString); - } - - @Bean - public S3Client getS3Client() { - return S3Client.builder() - .region(getRegion()) - .credentialsProvider(systemPropertyCredentialsProvider()) - .build(); - } -} \ No newline at end of file +//package org.kau.kkoolbeeServer.global.common.config; +// +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider; +//import software.amazon.awssdk.regions.Region; +//import software.amazon.awssdk.services.s3.S3Client; +// +//@Configuration +//public class AWSConfig { +// +// private static final String AWS_ACCESS_KEY_ID = "aws.accessKeyId"; +// private static final String AWS_SECRET_ACCESS_KEY = "aws.secretAccessKey"; +// +// private final String accessKey; +// private final String secretKey; +// private final String regionString; +// +// public AWSConfig(@Value("${aws-property.access-key}") final String accessKey, +// @Value("${aws-property.secret-key}") final String secretKey, +// @Value("${aws-property.aws-region}") final String regionString) { +// this.accessKey = accessKey; +// this.secretKey = secretKey; +// this.regionString = regionString; +// } +// +// +// @Bean +// public SystemPropertyCredentialsProvider systemPropertyCredentialsProvider() { +// System.setProperty(AWS_ACCESS_KEY_ID, accessKey); +// System.setProperty(AWS_SECRET_ACCESS_KEY, secretKey); +// return SystemPropertyCredentialsProvider.create(); +// } +// +// @Bean +// public Region getRegion() { +// return Region.of(regionString); +// } +// +// @Bean +// public S3Client getS3Client() { +// return S3Client.builder() +// .region(getRegion()) +// .credentialsProvider(systemPropertyCredentialsProvider()) +// .build(); +// } +//} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8e4c066..721d9f0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,22 +1,15 @@ -#spring: -# datasource: -# driver-class-name: com.mysql.cj.jdbc.Driver -# url: jdbc:mysql://${DATABASE_ENDPOINT_URL}:3306/${DATABASE_NAME}?serverTimezone=UTC&characterEncoding=UTF-8 -# username: ${DATABASE_USER} -# password: ${DATABASE_PASSWORD} -# -# jpa: -# hibernate: -# ddl-auto: update -# properties: -# hibernate: -# auto_quote_keyword: true -# -# config: -# import: optional:application-secret.properties -# -#aws-property: -# access-key: ${ACCESS_KEY} -# secret-key: ${SECRET_KEY} -# aws-region: ap-northeast-2 -# s3-bucket-name: ${BUCKET_NAME} \ No newline at end of file +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://${DATABASE_ENDPOINT_URL}:3306/${DATABASE_NAME}?serverTimezone=UTC&characterEncoding=UTF-8 + username: ${DATABASE_USER} + password: ${DATABASE_PASSWORD} + jpa: + hibernate: + ddl-auto: update + properties: + hibernate: + auto_quote_keyword: true + + config: + import: optional:application-secret.properties \ No newline at end of file From cae4c98fab225ef7170206945a49a6cf38bae118 Mon Sep 17 00:00:00 2001 From: Wang HoEun Date: Mon, 1 Apr 2024 20:01:49 +0900 Subject: [PATCH 02/43] =?UTF-8?q?chore:=20aws=20secret=20manager=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/bootstrap.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/resources/bootstrap.yml diff --git a/src/main/resources/bootstrap.yml b/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..04a3803 --- /dev/null +++ b/src/main/resources/bootstrap.yml @@ -0,0 +1,7 @@ +aws: + secretsmanager: + name: kkoolbee +cloud: + aws: + region: + static: ap-northeast-2 \ No newline at end of file From 1b3f0eeae2101a29ece033c1f84e2e36b4fa585a Mon Sep 17 00:00:00 2001 From: Wang Hoeun <38005874+hoeun0723@users.noreply.github.com> Date: Wed, 3 Apr 2024 23:35:17 +0900 Subject: [PATCH 03/43] [chore] CI/CD settings (#13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: common 폴더 구조 변경 * chore: 환경변수 파일 분리 (local/dev) * feat: 서버 profile 확인용 api 추가 * chore: 무중단 배포시 실행할 스크립트 작성 * chore: Github Actions 스크립트 작성 (임시 trigger 설정) * chore: CI/CD trigger 재설정 * chore: 인덴트 오류 수정 * chore: CodeDeploy 옵션 문자열 오류 수정 * chore: Spring Security 및 Redis 임시 주석 처리 * chore: trigger 시점 develop 으로 변경 * chore: profile 조회 엔드포인트 수정 * style: CD 브랜치 trigger 시점 임시 변경 * fix: trigger 시점 develop 으로 원상 복귀 --------- Co-authored-by: Wang HoEun --- .github/workflows/CD-dev.yml | 86 +++++++++++++++++++ .github/workflows/CI-dev.yml | 45 ++++++++++ build.gradle | 6 +- scripts/appspec.yml | 22 +++++ scripts/deploy.sh | 86 +++++++++++++++++++ scripts/switch.sh | 30 +++++++ .../global/common/config/AWSConfig.java | 48 ----------- .../controller/ServerProfileController.java | 22 +++++ .../global/common/dto/ApiResponse.java | 4 +- .../common/{ => dto}/enums/ErrorType.java | 2 +- .../common/{ => dto}/enums/SuccessType.java | 2 +- .../exception/GlobalExceptionHandler.java | 6 +- .../model/CustomException.java | 4 +- src/main/resources/application-dev1.yml | 23 +++++ src/main/resources/application-dev2.yml | 23 +++++ src/main/resources/application-local.yml | 24 ++++++ src/main/resources/application.yml | 21 ++--- 17 files changed, 381 insertions(+), 73 deletions(-) create mode 100644 .github/workflows/CD-dev.yml create mode 100644 .github/workflows/CI-dev.yml create mode 100644 scripts/appspec.yml create mode 100644 scripts/deploy.sh create mode 100644 scripts/switch.sh delete mode 100644 src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/common/controller/ServerProfileController.java rename src/main/java/org/kau/kkoolbeeServer/global/common/{ => dto}/enums/ErrorType.java (92%) rename src/main/java/org/kau/kkoolbeeServer/global/common/{ => dto}/enums/SuccessType.java (88%) rename src/main/java/org/kau/kkoolbeeServer/global/common/{ => exception}/model/CustomException.java (80%) create mode 100644 src/main/resources/application-dev1.yml create mode 100644 src/main/resources/application-dev2.yml create mode 100644 src/main/resources/application-local.yml diff --git a/.github/workflows/CD-dev.yml b/.github/workflows/CD-dev.yml new file mode 100644 index 0000000..c767fdc --- /dev/null +++ b/.github/workflows/CD-dev.yml @@ -0,0 +1,86 @@ +# 워크플로우의 이름 지정 +name: Sanhak Server CD (Develop) + +# 해당 workflow가 언제 실행될 것인지에 대한 트리거를 지정 +on: + push: + branches: [ "develop" ] + +env: + S3_BUCKET_NAME: kkoolbee-storage + +jobs: + build: + name: Code deployment + + # 실행 환경 + runs-on: ubuntu-latest + + steps: + + # 1) 워크플로우 실행 전 기본적으로 체크아웃 필요 + - name: checkout + uses: actions/checkout@v3 + + # 2) JDK 17버전 설치, 다른 JDK 버전을 사용하다면 수정 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'corretto' + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ap-northeast-2 + + # 3) AWS Secrets Manger 환경변수 사용 + - name: Read secrets from AWS Secrets Manager into environment variables + uses: abhilash1in/aws-secrets-manager-action@v1.1.0 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ap-northeast-2 + secrets: /secret/kkoolbee + parse-json: false + + # 이 워크플로우는 gradle build + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle # 실제 application build (-x 옵션을 통해 test는 제외) + run: ./gradlew build -x test + + # 디렉토리 생성 + - name: Make Directory + run: mkdir -p deploy + + # Jar 파일 복사 + - name: Copy Jar + run: cp ./build/libs/*.jar ./deploy + + # appspec.yml, script files 파일 복사 + - name: Copy files + run: cp ./scripts/* ./deploy + + - name: Make zip file + run: zip -r ./sanhak-server.zip ./deploy + shell: bash + + - name: Upload to S3 + run: aws s3 cp --region ap-northeast-2 ./sanhak-server.zip s3://$S3_BUCKET_NAME/ + + # Deploy + - name: Deploy + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + run: + aws deploy create-deployment + --application-name sanhak-server-codedeploy + --deployment-group-name sanhak-server-codedeploy-group + --file-exists-behavior OVERWRITE + --s3-location bucket=kkoolbee-storage,bundleType=zip,key=sanhak-server.zip + --region ap-northeast-2 \ No newline at end of file diff --git a/.github/workflows/CI-dev.yml b/.github/workflows/CI-dev.yml new file mode 100644 index 0000000..9ed2303 --- /dev/null +++ b/.github/workflows/CI-dev.yml @@ -0,0 +1,45 @@ +name: Sanhak Server CI (Develop) + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + + # 1) 워크플로우 실행 전 기본적으로 체크아웃 필요 + - name: checkout + uses: actions/checkout@v3 + + # 2) JDK 17버전 설치, 다른 JDK 버전을 사용하다면 수정 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'corretto' + + # 3) AWS Secrets Manger 환경변수 사용 + - name: Read secrets from AWS Secrets Manager into environment variables + uses: abhilash1in/aws-secrets-manager-action@v1.1.0 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ap-northeast-2 + secrets: /secret/kkoolbee + parse-json: false + + # 이 워크플로우는 gradle build + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle # 실제 application build (-x 옵션을 통해 test는 제외) + run: ./gradlew build -x test \ No newline at end of file diff --git a/build.gradle b/build.gradle index 027092f..0cb1924 100644 --- a/build.gradle +++ b/build.gradle @@ -55,11 +55,11 @@ dependencies { runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' // Spring Security - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.security:spring-security-test' +// implementation 'org.springframework.boot:spring-boot-starter-security' +// implementation 'org.springframework.security:spring-security-test' // Redis - implementation 'org.springframework.boot:spring-boot-starter-data-redis' +// implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Open Feign implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.0.4' diff --git a/scripts/appspec.yml b/scripts/appspec.yml new file mode 100644 index 0000000..1dbcbc2 --- /dev/null +++ b/scripts/appspec.yml @@ -0,0 +1,22 @@ +version: 0.0 +os: linux + +files: + - source: / + destination: /home/ubuntu/sanhak-server + overwrite: yes + +permissions: + - object: / + pattern: "**" + owner: ubuntu + group: ubuntu + +hooks: + AfterInstall: + - location: deploy.sh + timeout: 180 + runas: ubuntu + - location: switch.sh + timeout: 180 + runas: ubuntu \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..77a2611 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,86 @@ +#!/bin/bash +NOW_TIME="$(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" + +BUILD_PATH=$(ls /home/ubuntu/sanhak-server/kkoolbeeServer-0.0.1-SNAPSHOT.jar) +JAR_NAME=$(basename $BUILD_PATH) +echo "[$NOW_TIME] build 파일명: $JAR_NAME" + +echo "[$NOW_TIME] build 파일 복사" +DEPLOY_PATH=/home/ubuntu/sanhak-server/nonstop/jar/ +cp $BUILD_PATH $DEPLOY_PATH + +echo "[$NOW_TIME] 현재 구동중인 Profile 확인" +CURRENT_PROFILE=$(curl -s https://www.honeyary-server.o-r.kr/profile) +echo "[$NOW_TIME] $CURRENT_PROFILE" + +# 쉬고 있는 profile 찾기: dev1이 사용중이면 dev2가 쉬고 있고, 반대면 dev1이 쉬고 있음 +if [ $CURRENT_PROFILE == dev1 ] +then + IDLE_PROFILE=dev2 + IDLE_PORT=8082 +elif [ $CURRENT_PROFILE == dev2 ] +then + IDLE_PROFILE=dev1 + IDLE_PORT=8081 +else + echo "[$NOW_TIME] 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE" + echo "[$NOW_TIME] dev1을 할당합니다. IDLE_PROFILE: dev1" + IDLE_PROFILE=dev1 + IDLE_PORT=8081 +fi + +echo "[$NOW_TIME] application.jar 교체" +IDLE_APPLICATION=$IDLE_PROFILE-Sanhak-Server.jar +IDLE_APPLICATION_PATH=$DEPLOY_PATH$IDLE_APPLICATION + +ln -Tfs $DEPLOY_PATH$JAR_NAME $IDLE_APPLICATION_PATH + +echo "[$NOW_TIME] $IDLE_PROFILE 에서 구동중인 애플리케이션 pid 확인" +IDLE_PID=$(pgrep -f $IDLE_APPLICATION) + +if [ -z $IDLE_PID ] +then + echo "[$NOW_TIME] 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." +else + echo "[$NOW_TIME] kill -15 $IDLE_PID" + kill -15 $IDLE_PID + + while ps -p $IDLE_PID > /dev/null; do + sleep 1 + done + echo "[$NOW_TIME] 애플리케이션이 정상 종료되었습니다." +fi + +echo "[$NOW_TIME] $IDLE_PROFILE 배포" +nohup java -jar -Duser.timezone=Asia/Seoul -Dspring.profiles.active=$IDLE_PROFILE $IDLE_APPLICATION_PATH >> /home/ubuntu/sanhak-server/deploy.log 2>/home/ubuntu/sanhak-server/deploy_err.log & + +################################################################## + +echo "[$NOW_TIME] $IDLE_PROFILE 10초 후 Health check 시작" +echo "[$NOW_TIME] curl -s http://localhost:$IDLE_PORT/health " +sleep 10 + +for retry_count in {1..10} +do + response=$(curl -s http://localhost:$IDLE_PORT/actuator/health) + up_count=$(echo $response | grep 'UP' | wc -l) + + if [ $up_count -ge 1 ] + then # $up_count >= 1 ("UP" 문자열이 있는지 검증) + echo "[$NOW_TIME] Health check 성공" + break + else + echo "[$NOW_TIME] Health check의 응답을 알 수 없거나 혹은 status가 UP이 아닙니다." + echo "[$NOW_TIME] Health check: ${response}" + fi + + if [ $retry_count -eq 10 ] + then + echo "[$NOW_TIME] Health check 실패. " + echo "[$NOW_TIME] Nginx에 연결하지 않고 배포를 종료합니다." + exit 1 + fi + + echo "[$NOW_TIME] Health check 연결 실패. 재시도…" + sleep 10 +done \ No newline at end of file diff --git a/scripts/switch.sh b/scripts/switch.sh new file mode 100644 index 0000000..a050979 --- /dev/null +++ b/scripts/switch.sh @@ -0,0 +1,30 @@ +#!/bin/bash +NOW_TIME="$(date +%Y)-$(date +%m)-$(date +%d) $(date +%H):$(date +%M):$(date +%S)" + +echo "[$NOW_TIME] 스위칭" +sleep 10 +echo "[$NOW_TIME] 현재 구동중인 Port 확인" +CURRENT_PROFILE=$(curl -s https://www.honeyary-server.o-r.kr/profile) + +# 쉬고 있는 profile 찾기: dev1이 사용중이면 dev2가 쉬고 있고, 반대면 dev1이 쉬고 있음 +if [ $CURRENT_PROFILE == dev1 ] +then + IDLE_PORT=8082 +elif [ $CURRENT_PROFILE == dev2 ] +then + IDLE_PORT=8081 +else + echo "[$NOW_TIME] 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE" + echo "[$NOW_TIME] 8081을 할당합니다." + IDLE_PORT=8081 +fi + +echo "[$NOW_TIME] 전환할 Port: $IDLE_PORT" +echo "[$NOW_TIME] Port 전환" +echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc + +PROXY_PORT=$(curl -s https://www.honeyary-server.o-r.kr/profile) +echo "[$NOW_TIME] Nginx Current Proxy Port: $PROXY_PORT" + +echo "[$NOW_TIME] Nginx Reload" +sudo service nginx reload \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java deleted file mode 100644 index 00bbf5c..0000000 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/config/AWSConfig.java +++ /dev/null @@ -1,48 +0,0 @@ -//package org.kau.kkoolbeeServer.global.common.config; -// -//import org.springframework.beans.factory.annotation.Value; -//import org.springframework.context.annotation.Bean; -//import org.springframework.context.annotation.Configuration; -//import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider; -//import software.amazon.awssdk.regions.Region; -//import software.amazon.awssdk.services.s3.S3Client; -// -//@Configuration -//public class AWSConfig { -// -// private static final String AWS_ACCESS_KEY_ID = "aws.accessKeyId"; -// private static final String AWS_SECRET_ACCESS_KEY = "aws.secretAccessKey"; -// -// private final String accessKey; -// private final String secretKey; -// private final String regionString; -// -// public AWSConfig(@Value("${aws-property.access-key}") final String accessKey, -// @Value("${aws-property.secret-key}") final String secretKey, -// @Value("${aws-property.aws-region}") final String regionString) { -// this.accessKey = accessKey; -// this.secretKey = secretKey; -// this.regionString = regionString; -// } -// -// -// @Bean -// public SystemPropertyCredentialsProvider systemPropertyCredentialsProvider() { -// System.setProperty(AWS_ACCESS_KEY_ID, accessKey); -// System.setProperty(AWS_SECRET_ACCESS_KEY, secretKey); -// return SystemPropertyCredentialsProvider.create(); -// } -// -// @Bean -// public Region getRegion() { -// return Region.of(regionString); -// } -// -// @Bean -// public S3Client getS3Client() { -// return S3Client.builder() -// .region(getRegion()) -// .credentialsProvider(systemPropertyCredentialsProvider()) -// .build(); -// } -//} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/controller/ServerProfileController.java b/src/main/java/org/kau/kkoolbeeServer/global/common/controller/ServerProfileController.java new file mode 100644 index 0000000..4c9e199 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/controller/ServerProfileController.java @@ -0,0 +1,22 @@ +package org.kau.kkoolbeeServer.global.common.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; + +@RestController +@RequiredArgsConstructor +public class ServerProfileController { + + private final Environment env; + + @GetMapping("/profile") + public String getProfile() { + return Arrays.stream(env.getActiveProfiles()) + .findFirst() + .orElse(""); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java index cd1822c..62edcb2 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java @@ -6,8 +6,8 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.RequiredArgsConstructor; -import org.kau.kkoolbeeServer.global.common.enums.ErrorType; -import org.kau.kkoolbeeServer.global.common.enums.SuccessType; +import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; +import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; @Getter @JsonPropertyOrder({"code", "message", "data"}) diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java similarity index 92% rename from src/main/java/org/kau/kkoolbeeServer/global/common/enums/ErrorType.java rename to src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index 44b7fb8..c6d79c5 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -1,4 +1,4 @@ -package org.kau.kkoolbeeServer.global.common.enums; +package org.kau.kkoolbeeServer.global.common.dto.enums; import lombok.AccessLevel; import lombok.AllArgsConstructor; diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/enums/SuccessType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java similarity index 88% rename from src/main/java/org/kau/kkoolbeeServer/global/common/enums/SuccessType.java rename to src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java index 7640e4b..b664160 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/enums/SuccessType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java @@ -1,4 +1,4 @@ -package org.kau.kkoolbeeServer.global.common.enums; +package org.kau.kkoolbeeServer.global.common.dto.enums; import lombok.AccessLevel; import lombok.AllArgsConstructor; diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java index d04d832..a23c14d 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java @@ -4,7 +4,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.kau.kkoolbeeServer.global.common.dto.ApiResponse; -import org.kau.kkoolbeeServer.global.common.model.CustomException; +import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -19,8 +19,8 @@ import java.util.HashMap; import java.util.Map; -import static org.kau.kkoolbeeServer.global.common.enums.ErrorType.INTERNAL_SERVER_ERROR; -import static org.kau.kkoolbeeServer.global.common.enums.ErrorType.REQUEST_VALIDATION_EXCEPTION; +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.INTERNAL_SERVER_ERROR; +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.REQUEST_VALIDATION_EXCEPTION; @Slf4j // 로그 출력 @RestControllerAdvice diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/model/CustomException.java b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/model/CustomException.java similarity index 80% rename from src/main/java/org/kau/kkoolbeeServer/global/common/model/CustomException.java rename to src/main/java/org/kau/kkoolbeeServer/global/common/exception/model/CustomException.java index 4cd39e3..a7f813a 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/model/CustomException.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/model/CustomException.java @@ -1,7 +1,7 @@ -package org.kau.kkoolbeeServer.global.common.model; +package org.kau.kkoolbeeServer.global.common.exception.model; import lombok.Getter; -import org.kau.kkoolbeeServer.global.common.enums.ErrorType; +import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; //[호출 관계] Controller(요청 처리) -> Service(순서) -> Repository -> DB //throw new CustomException(ERROR_TYPE); diff --git a/src/main/resources/application-dev1.yml b/src/main/resources/application-dev1.yml new file mode 100644 index 0000000..a7a2941 --- /dev/null +++ b/src/main/resources/application-dev1.yml @@ -0,0 +1,23 @@ +spring: + config: + activate: + on-profile: dev1 + + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${DB_URL_DEV} + username: ${DB_USER_DEV} + password: ${DB_PWD_DEV} + jpa: + show-sql: false + hibernate: + ddl-auto: update + ejb: + naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + properties: + hibernate: + auto_quote_keyword: true + format_sql: true + +server: + port: 8081 \ No newline at end of file diff --git a/src/main/resources/application-dev2.yml b/src/main/resources/application-dev2.yml new file mode 100644 index 0000000..3f49986 --- /dev/null +++ b/src/main/resources/application-dev2.yml @@ -0,0 +1,23 @@ +spring: + config: + activate: + on-profile: dev2 + + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${DB_URL_DEV} + username: ${DB_USER_DEV} + password: ${DB_PWD_DEV} + jpa: + show-sql: false + hibernate: + ddl-auto: update + ejb: + naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + properties: + hibernate: + auto_quote_keyword: true + format_sql: true + +server: + port: 8082 \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..29913bb --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,24 @@ +spring: + config: + activate: + on-profile: local + import: optional:application-secret.properties + + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${DB_URL_LOCAL} + username: ${DB_USER_LOCAL} + password: ${DB_PWD_LOCAL} + jpa: + show-sql: false + hibernate: + ddl-auto: update + ejb: + naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + properties: + hibernate: + auto_quote_keyword: true + format_sql: true + +server: + port: 8080 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 721d9f0..7555499 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,15 +1,10 @@ spring: - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://${DATABASE_ENDPOINT_URL}:3306/${DATABASE_NAME}?serverTimezone=UTC&characterEncoding=UTF-8 - username: ${DATABASE_USER} - password: ${DATABASE_PASSWORD} - jpa: - hibernate: - ddl-auto: update - properties: - hibernate: - auto_quote_keyword: true + profiles: + active: local - config: - import: optional:application-secret.properties \ No newline at end of file +logging: + level: + com: + amazonaws: + util: + EC2MetadataUtils: error \ No newline at end of file From e32370f7185a8d23d134f059fc3efd3d6480da45 Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Wed, 10 Apr 2024 11:58:16 +0900 Subject: [PATCH 04/43] =?UTF-8?q?feat/14-entity=20develop=EB=B8=8C?= =?UTF-8?q?=EB=9E=9C=EC=B9=98=EC=97=90=20merge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../KkoolbeeServerApplication.java | 1 + .../kkoolbeeServer/domain/advice/Advice.java | 15 +++++++ .../kkoolbeeServer/domain/diary/Diary.java | 45 +++++++++++++++++++ .../kkoolbeeServer/domain/diary/Feeling.java | 11 +++++ .../kkoolbeeServer/domain/member/Member.java | 29 ++++++++++++ .../domain/member/UserDiaryType.java | 6 +++ .../global/common/domain/BaseTimeEntity.java | 2 + 7 files changed, 109 insertions(+) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java diff --git a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java index 023b644..39d7b05 100644 --- a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java +++ b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java @@ -1,5 +1,6 @@ package org.kau.kkoolbeeServer; +import org.kau.kkoolbeeServer.domain.diary.Diary; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java new file mode 100644 index 0000000..dbc3b73 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java @@ -0,0 +1,15 @@ +package org.kau.kkoolbeeServer.domain.advice; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity +public class Advice { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; + + String content; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java new file mode 100644 index 0000000..0996ed8 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -0,0 +1,45 @@ +package org.kau.kkoolbeeServer.domain.diary; + +import jakarta.persistence.*; +import org.kau.kkoolbeeServer.domain.advice.Advice; +import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.global.common.domain.BaseTimeEntity; +import org.springframework.cglib.core.Local; + +import java.time.LocalDateTime; +import java.util.List; + +@Entity +public class Diary extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "diary_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + private LocalDateTime writedAt; //이거 중복가능성이 좀 보인다.. BaseTimeEntitiy와 ai서버를 위해서 만든 필드 + + @Enumerated(EnumType.STRING) + private Feeling feeling; + + @Column(nullable = false,length = 1000) + private String content; + + String title; + + + @Enumerated(EnumType.STRING) + private List summary; + + + + + @OneToOne + @JoinColumn(name = "advice_id") + private Advice advice; + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java new file mode 100644 index 0000000..42a72a3 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java @@ -0,0 +1,11 @@ +package org.kau.kkoolbeeServer.domain.diary; + +public enum Feeling { + HAPPY, + SAD, + ANGRY, + + WORRY, + + SURPRISED +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java new file mode 100644 index 0000000..fa70770 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java @@ -0,0 +1,29 @@ +package org.kau.kkoolbeeServer.domain.member; + +import jakarta.persistence.*; +import org.kau.kkoolbeeServer.domain.diary.Diary; + +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id") + private Long id; + + private String kakaoId; + @OneToMany(mappedBy = "member") + private final List diaries=new ArrayList<>(); + + private String socialNickname; + private String socialImage; + + @Enumerated(EnumType.STRING) + private UserDiaryType userDiaryType; + + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java new file mode 100644 index 0000000..b67d80d --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java @@ -0,0 +1,6 @@ +package org.kau.kkoolbeeServer.domain.member; + +public enum UserDiaryType { + FASTTYPE, + SLOWTYPE +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java b/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java index 15f25cd..50746a7 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java @@ -2,6 +2,7 @@ import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; +import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -10,6 +11,7 @@ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) +@Getter public abstract class BaseTimeEntity { @CreatedDate From b810ab80ff358d769fa981392446829cd3e2313b Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:28:13 +0900 Subject: [PATCH 05/43] Feat/#14 entity (#15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 엔티티 첫 커밋 * diary엔티티에 멤버와의 관계추가 * feat : diary엔티티에 summary추가 --- .../KkoolbeeServerApplication.java | 1 + .../kkoolbeeServer/domain/advice/Advice.java | 15 +++++++ .../kkoolbeeServer/domain/diary/Diary.java | 45 +++++++++++++++++++ .../kkoolbeeServer/domain/diary/Feeling.java | 11 +++++ .../kkoolbeeServer/domain/member/Member.java | 29 ++++++++++++ .../domain/member/UserDiaryType.java | 6 +++ .../global/common/domain/BaseTimeEntity.java | 2 + 7 files changed, 109 insertions(+) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java diff --git a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java index 023b644..39d7b05 100644 --- a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java +++ b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java @@ -1,5 +1,6 @@ package org.kau.kkoolbeeServer; +import org.kau.kkoolbeeServer.domain.diary.Diary; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java new file mode 100644 index 0000000..dbc3b73 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java @@ -0,0 +1,15 @@ +package org.kau.kkoolbeeServer.domain.advice; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity +public class Advice { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; + + String content; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java new file mode 100644 index 0000000..0996ed8 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -0,0 +1,45 @@ +package org.kau.kkoolbeeServer.domain.diary; + +import jakarta.persistence.*; +import org.kau.kkoolbeeServer.domain.advice.Advice; +import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.global.common.domain.BaseTimeEntity; +import org.springframework.cglib.core.Local; + +import java.time.LocalDateTime; +import java.util.List; + +@Entity +public class Diary extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "diary_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + private LocalDateTime writedAt; //이거 중복가능성이 좀 보인다.. BaseTimeEntitiy와 ai서버를 위해서 만든 필드 + + @Enumerated(EnumType.STRING) + private Feeling feeling; + + @Column(nullable = false,length = 1000) + private String content; + + String title; + + + @Enumerated(EnumType.STRING) + private List summary; + + + + + @OneToOne + @JoinColumn(name = "advice_id") + private Advice advice; + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java new file mode 100644 index 0000000..42a72a3 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java @@ -0,0 +1,11 @@ +package org.kau.kkoolbeeServer.domain.diary; + +public enum Feeling { + HAPPY, + SAD, + ANGRY, + + WORRY, + + SURPRISED +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java new file mode 100644 index 0000000..fa70770 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java @@ -0,0 +1,29 @@ +package org.kau.kkoolbeeServer.domain.member; + +import jakarta.persistence.*; +import org.kau.kkoolbeeServer.domain.diary.Diary; + +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id") + private Long id; + + private String kakaoId; + @OneToMany(mappedBy = "member") + private final List diaries=new ArrayList<>(); + + private String socialNickname; + private String socialImage; + + @Enumerated(EnumType.STRING) + private UserDiaryType userDiaryType; + + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java new file mode 100644 index 0000000..b67d80d --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/UserDiaryType.java @@ -0,0 +1,6 @@ +package org.kau.kkoolbeeServer.domain.member; + +public enum UserDiaryType { + FASTTYPE, + SLOWTYPE +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java b/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java index 15f25cd..50746a7 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java @@ -2,6 +2,7 @@ import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; +import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -10,6 +11,7 @@ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) +@Getter public abstract class BaseTimeEntity { @CreatedDate From 4959fa4b54cb86a386346614a4ee4a2203dba589 Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Wed, 10 Apr 2024 14:14:50 +0900 Subject: [PATCH 06/43] =?UTF-8?q?refactor:=20entity=EC=97=90=20=EA=B0=90?= =?UTF-8?q?=EC=A0=95,=EC=A1=B0=EC=96=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kau/kkoolbeeServer/domain/diary/Diary.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java index 0996ed8..ade95f5 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -23,7 +23,10 @@ public class Diary extends BaseTimeEntity { private LocalDateTime writedAt; //이거 중복가능성이 좀 보인다.. BaseTimeEntitiy와 ai서버를 위해서 만든 필드 @Enumerated(EnumType.STRING) - private Feeling feeling; + private Feeling firstFeeling; + + @Enumerated(EnumType.STRING) + private Feeling secondFeeling; @Column(nullable = false,length = 1000) private String content; @@ -38,8 +41,14 @@ public class Diary extends BaseTimeEntity { @OneToOne - @JoinColumn(name = "advice_id") - private Advice advice; + @JoinColumn(name = "kind_advice_id") + private Advice kindAdvice; + + @OneToOne + @JoinColumn(name = "spicy_advice_id") + private Advice spicyAdvice; + + } From 92f38b21c9b8ee1e8eefcaed26aecf2c4202dbf6 Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Wed, 10 Apr 2024 14:21:26 +0900 Subject: [PATCH 07/43] =?UTF-8?q?refactor:=EC=88=98=EC=A0=95=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java index ade95f5..8e233ea 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -38,8 +38,6 @@ public class Diary extends BaseTimeEntity { private List summary; - - @OneToOne @JoinColumn(name = "kind_advice_id") private Advice kindAdvice; From a378d3204fdd795a4fc10e90f0326fafa926d4ef Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Fri, 12 Apr 2024 13:31:04 +0900 Subject: [PATCH 08/43] =?UTF-8?q?refactor:=20feeling=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EA=B0=90=EC=A0=95=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/kau/kkoolbeeServer/domain/diary/Feeling.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java index 42a72a3..4fbbb65 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java @@ -5,7 +5,11 @@ public enum Feeling { SAD, ANGRY, - WORRY, + WORRYIED, - SURPRISED + RELAX, + + SURPRISED, + + NONE } From bd24f25de47f7ec5e1ee1779aef2fa6a4504cb1a Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Fri, 12 Apr 2024 14:15:14 +0900 Subject: [PATCH 09/43] =?UTF-8?q?feat:=20summary=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EB=94=B0=EB=A1=9C=EC=83=9D=EC=84=B1=20=ED=9B=84=20mem?= =?UTF-8?q?ber=EC=99=80=20=EB=8B=A4=EB=8C=80=EC=9D=BC=EC=96=91=EB=B0=A9?= =?UTF-8?q?=ED=96=A5=EA=B4=80=EA=B3=84=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkoolbeeServer/domain/advice/Advice.java | 5 +++- .../kkoolbeeServer/domain/diary/Diary.java | 19 +++++--------- .../kkoolbeeServer/domain/member/Member.java | 4 +++ .../domain/summary/Summary.java | 25 +++++++++++++++++++ 4 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/summary/Summary.java diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java index dbc3b73..8d74a15 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java @@ -11,5 +11,8 @@ public class Advice { @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; - String content; + String kind_advice; + + String spicy_advice; + } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java index 1ad9fb3..1aab123 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -23,30 +23,23 @@ public class Diary extends BaseTimeEntity { private LocalDateTime writedAt; //이거 중복가능성이 좀 보인다.. BaseTimeEntitiy와 ai서버를 위해서 만든 필드 @Enumerated(EnumType.STRING) - private Feeling firstFeeling; - - @Enumerated(EnumType.STRING) - private Feeling secondFeeling; + private Feeling feeling; @Column(nullable = false,length = 1000) private String content; - String title; + private String title; + + @OneToOne + @JoinColumn(name = "advice_id") + private Advice advice; - @Enumerated(EnumType.STRING) - private List summary; - @OneToOne - @JoinColumn(name = "kind_advice_id") - private Advice kindAdvice; - @OneToOne - @JoinColumn(name = "spicy_advice_id") - private Advice spicyAdvice; diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java index fa70770..2fa4488 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import org.kau.kkoolbeeServer.domain.diary.Diary; +import org.kau.kkoolbeeServer.domain.summary.Summary; import java.util.ArrayList; import java.util.List; @@ -24,6 +25,9 @@ public class Member { @Enumerated(EnumType.STRING) private UserDiaryType userDiaryType; + @OneToMany(mappedBy = "member") // Summary에 대한 참조 추가 + private List summaries = new ArrayList<>(); + } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/summary/Summary.java b/src/main/java/org/kau/kkoolbeeServer/domain/summary/Summary.java new file mode 100644 index 0000000..92312bf --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/summary/Summary.java @@ -0,0 +1,25 @@ +package org.kau.kkoolbeeServer.domain.summary; + +import jakarta.persistence.*; +import org.kau.kkoolbeeServer.domain.member.Member; + +@Entity +public class Summary { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; + + String first_feeling; + + String second_feeling; + + @ManyToOne // 다대일 관계 설정 + @JoinColumn(name = "member_id") // 외래 키로 사용될 컬럼 지정 + private Member member; + + + + + +} From d8429e4a627f8c7d66ca1bc5a506766cd7ef61bf Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Fri, 12 Apr 2024 14:45:11 +0900 Subject: [PATCH 10/43] =?UTF-8?q?worried=20=EC=8A=A4=ED=8E=A0=EB=A7=81?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/kau/kkoolbeeServer/domain/diary/Diary.java | 8 +++++--- .../java/org/kau/kkoolbeeServer/domain/diary/Feeling.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java index 1aab123..c09f3ea 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -16,6 +16,10 @@ public class Diary extends BaseTimeEntity { @Column(name = "diary_id") private Long id; + @OneToOne + @JoinColumn(name = "advice_id") + private Advice advice; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; @@ -31,9 +35,7 @@ public class Diary extends BaseTimeEntity { private String title; - @OneToOne - @JoinColumn(name = "advice_id") - private Advice advice; + diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java index 4fbbb65..4fa289e 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Feeling.java @@ -5,7 +5,7 @@ public enum Feeling { SAD, ANGRY, - WORRYIED, + WORRIED, RELAX, From e32641779b29e3ba3670707edb904945f26015a6 Mon Sep 17 00:00:00 2001 From: Wang Hoeun <38005874+hoeun0723@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:44:52 +0900 Subject: [PATCH 11/43] =?UTF-8?q?feat:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84+s3=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EC=84=B8=ED=8C=85=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 카카오 로그인 구현+s3서버 세팅 * fix: build gradle 수정 --------- Co-authored-by: Wang HoEun --- build.gradle | 6 +- .../KkoolbeeServerApplication.java | 6 +- .../kkoolbeeServer/domain/member/Member.java | 32 +++- .../member/controller/MemberController.java | 50 ++++++ .../dto/response/MemberLoginResponseDto.java | 29 +++ .../member/repository/MemberRepository.java | 21 +++ .../domain/member/service/MemberService.java | 93 ++++++++++ .../auth/fegin/kakao/KakaoApiClient.java | 15 ++ .../auth/fegin/kakao/KakaoAuthApiClient.java | 20 +++ .../auth/fegin/kakao/KakaoLoginService.java | 66 +++++++ .../response/KakaoAccessTokenResponse.java | 20 +++ .../fegin/kakao/response/KakaoAccount.java | 15 ++ .../kakao/response/KakaoUserProfile.java | 16 ++ .../kakao/response/KakaoUserResponse.java | 18 ++ .../global/auth/jwt/JwtProvider.java | 165 ++++++++++++++++++ .../global/auth/jwt/TokenDto.java | 18 ++ .../global/auth/redis/RefreshToken.java | 27 +++ .../global/auth/redis/TokenRepository.java | 6 + .../global/auth/security/AuthWhiteList.java | 24 +++ .../security/JwtAuthenticationFilter.java | 65 +++++++ .../auth/security/JwtExceptionFilter.java | 68 ++++++++ .../auth/security/UserAuthentication.java | 13 ++ .../global/common/dto/enums/ErrorType.java | 30 +++- .../global/common/dto/enums/SuccessType.java | 4 + .../exception/GlobalExceptionHandler.java | 4 +- .../global/config/RedisConfig.java | 24 +++ .../global/config/SecurityConfig.java | 41 +++++ src/main/resources/application-dev1.yml | 9 + src/main/resources/application-dev2.yml | 9 + src/main/resources/application-local.yml | 9 + src/main/resources/application.yml | 11 ++ 31 files changed, 922 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/dto/response/MemberLoginResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/repository/MemberRepository.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoApiClient.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoAuthApiClient.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoLoginService.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccessTokenResponse.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccount.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserProfile.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserResponse.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/TokenDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/redis/RefreshToken.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/redis/TokenRepository.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/security/AuthWhiteList.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtAuthenticationFilter.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtExceptionFilter.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/auth/security/UserAuthentication.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/config/RedisConfig.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/config/SecurityConfig.java diff --git a/build.gradle b/build.gradle index 0cb1924..027092f 100644 --- a/build.gradle +++ b/build.gradle @@ -55,11 +55,11 @@ dependencies { runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' // Spring Security -// implementation 'org.springframework.boot:spring-boot-starter-security' -// implementation 'org.springframework.security:spring-security-test' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.security:spring-security-test' // Redis -// implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Open Feign implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.0.4' diff --git a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java index 39d7b05..c480cb0 100644 --- a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java +++ b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java @@ -1,10 +1,12 @@ package org.kau.kkoolbeeServer; -import org.kau.kkoolbeeServer.domain.diary.Diary; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; +import org.springframework.cloud.openfeign.EnableFeignClients; -@SpringBootApplication +@SpringBootApplication(exclude = { UserDetailsServiceAutoConfiguration.class }) +@EnableFeignClients public class KkoolbeeServerApplication { public static void main(String[] args) { diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java index 2fa4488..60275ce 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java @@ -1,6 +1,10 @@ package org.kau.kkoolbeeServer.domain.member; import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.summary.Summary; @@ -8,6 +12,8 @@ import java.util.List; @Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member { @Id @@ -15,19 +21,37 @@ public class Member { @Column(name = "member_id") private Long id; - private String kakaoId; @OneToMany(mappedBy = "member") private final List diaries=new ArrayList<>(); - private String socialNickname; - private String socialImage; - @Enumerated(EnumType.STRING) private UserDiaryType userDiaryType; @OneToMany(mappedBy = "member") // Summary에 대한 참조 추가 private List summaries = new ArrayList<>(); + /** + * 소셜 로그인 관련 + */ + @Column(nullable = false) + private String kakaoId; + + private String socialNickname; + + private String socialImage; + + // 로그인 새롭게 할 때마다 해당 필드들 업데이트 + public void updateSocialInfo(String socialNickname, String socialImage) { + this.socialNickname = socialNickname; + this.socialImage = socialImage; + } + @Builder + public Member(String kakaoId) { + this.kakaoId = kakaoId; + } + public static Member of(String kakaoId) { + return new Member(kakaoId); + } } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java new file mode 100644 index 0000000..e3ffa37 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java @@ -0,0 +1,50 @@ +package org.kau.kkoolbeeServer.domain.member.controller; + +import lombok.RequiredArgsConstructor; +import org.kau.kkoolbeeServer.domain.member.dto.response.MemberLoginResponseDto; +import org.kau.kkoolbeeServer.domain.member.service.MemberService; +import org.kau.kkoolbeeServer.global.auth.fegin.kakao.KakaoLoginService; +import org.kau.kkoolbeeServer.global.auth.jwt.JwtProvider; +import org.kau.kkoolbeeServer.global.auth.jwt.TokenDto; +import org.kau.kkoolbeeServer.global.common.dto.ApiResponse; +import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; + +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor +public class MemberController { + + private final MemberService memberService; + private final KakaoLoginService kakaoLoginService; + + @PostMapping("/login") + public ResponseEntity> login( + @RequestHeader("Authorization") String socialAccessToken) { + + return ResponseEntity.ok(ApiResponse.success(SuccessType.LOGIN_SUCCESS, memberService.login(socialAccessToken))); + } + + @GetMapping("/reissue") + public ResponseEntity> reissue( + @RequestHeader("Authorization") String refreshToken) { + + return ResponseEntity.ok(ApiResponse.success(SuccessType.REISSUE_SUCCESS, memberService.reissueToken(refreshToken))); + } + + @PatchMapping("/log-out") // Spring Security 자체의 logout과 겹치지 않기 위해 이렇게 설정 + public ResponseEntity> logout(Principal principal) { + + memberService.logout(JwtProvider.getUserFromPrincial(principal)); + return ResponseEntity.ok(ApiResponse.success(SuccessType.LOGOUT_SUCCESS)); + } + + @GetMapping("/kakao") + public ResponseEntity> kakaoAccessToken( + @RequestHeader("Authorization") String code) { + return ResponseEntity.ok(ApiResponse.success(SuccessType.KAKAO_ACCESS_TOKEN_SUCCESS, kakaoLoginService.getKakaoAccessToken(code))); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/dto/response/MemberLoginResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/dto/response/MemberLoginResponseDto.java new file mode 100644 index 0000000..01ce979 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/dto/response/MemberLoginResponseDto.java @@ -0,0 +1,29 @@ +package org.kau.kkoolbeeServer.domain.member.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.global.auth.jwt.TokenDto; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class MemberLoginResponseDto { + + private Long memberId; + private TokenDto tokenDto; + + private String socialNickname; + private String socialProfileImage; + + public static MemberLoginResponseDto of(Member loginMember, TokenDto tokenDto) { + + return new MemberLoginResponseDto( + loginMember.getId(), tokenDto, + loginMember.getSocialNickname(), loginMember.getSocialImage() + ); + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/repository/MemberRepository.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/repository/MemberRepository.java new file mode 100644 index 0000000..4ef65e5 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/repository/MemberRepository.java @@ -0,0 +1,21 @@ +package org.kau.kkoolbeeServer.domain.member.repository; + +import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.NOT_FOUND_MEMBER_ERROR; + +public interface MemberRepository extends JpaRepository { + + boolean existsByKakaoId(String kakaoId); + + Optional findByKakaoId(String kakaoId); + + default Member findByIdOrThrow(Long id) { + return this.findById(id) + .orElseThrow(() -> new CustomException(NOT_FOUND_MEMBER_ERROR)); + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java new file mode 100644 index 0000000..e5973e4 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java @@ -0,0 +1,93 @@ +package org.kau.kkoolbeeServer.domain.member.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.domain.member.dto.response.MemberLoginResponseDto; +import org.kau.kkoolbeeServer.domain.member.repository.MemberRepository; +import org.kau.kkoolbeeServer.global.auth.fegin.kakao.KakaoLoginService; +import org.kau.kkoolbeeServer.global.auth.jwt.JwtProvider; +import org.kau.kkoolbeeServer.global.auth.jwt.TokenDto; +import org.kau.kkoolbeeServer.global.auth.security.UserAuthentication; +import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.INVALID_TOKEN_HEADER_ERROR; +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.NOT_FOUND_MEMBER_ERROR; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MemberService { + + private final MemberRepository memberRepository; + private final JwtProvider jwtProvider; + private final KakaoLoginService kakaoLoginService; + + private static String parseTokenString(String tokenString) { + String[] strings = tokenString.split(" "); + if (strings.length != 2) { + throw new CustomException(INVALID_TOKEN_HEADER_ERROR); + } + return strings[1]; + } + + @Transactional + public MemberLoginResponseDto login(String socialAccessToken) { + + socialAccessToken = parseTokenString(socialAccessToken); + + String kakaoId = kakaoLoginService.getKakaoId(socialAccessToken); + + boolean isRegistered = isUserByKakaoId(kakaoId); + if (!isRegistered) { + Member member = Member.builder() + .kakaoId(kakaoId).build(); + + memberRepository.save(member); + } + + Member loginMember = getUserBySocialAndSocialId(kakaoId); + // 카카오 로그인은 정보 더 많이 받아올 수 있으므로 추가 설정 + kakaoLoginService.setKakaoInfo(loginMember, socialAccessToken); + + TokenDto tokenDto = jwtProvider.issueToken( + new UserAuthentication(loginMember.getId(), null, null)); + + return MemberLoginResponseDto.of(loginMember, tokenDto); + } + + @Transactional + public TokenDto reissueToken(String refreshToken) { + + refreshToken = parseTokenString(refreshToken); + + Long memberId = jwtProvider.validateRefreshToken(refreshToken); + validateMemberId(memberId); // memberId가 DB에 저장된 유효한 값인지 검사 + + jwtProvider.deleteRefreshToken(memberId); + return jwtProvider.issueToken(new UserAuthentication(memberId, null, null)); + } + + @Transactional + public void logout(Long memberId) { + jwtProvider.deleteRefreshToken(memberId); + } + + private void validateMemberId(Long memberId) { + if (!memberRepository.existsById(memberId)) { + throw new CustomException(NOT_FOUND_MEMBER_ERROR); + } + } + + private Member getUserBySocialAndSocialId(String kakaoId) { + return memberRepository.findByKakaoId(kakaoId) + .orElseThrow(() -> new CustomException(NOT_FOUND_MEMBER_ERROR)); + } + + private boolean isUserByKakaoId(String kakaoId) { + return memberRepository.existsByKakaoId(kakaoId); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoApiClient.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoApiClient.java new file mode 100644 index 0000000..ad0bd82 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoApiClient.java @@ -0,0 +1,15 @@ +package org.kau.kkoolbeeServer.global.auth.fegin.kakao; + +import org.apache.http.HttpHeaders; +import org.kau.kkoolbeeServer.global.auth.fegin.kakao.response.KakaoUserResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient(name = "kakaoApiClient", url = "https://kapi.kakao.com") +public interface KakaoApiClient { + + //Access 토큰을 활용해서 실제 유저 정보를 가져오는 역할 + @GetMapping(value = "/v2/user/me") + KakaoUserResponse getUserInformation(@RequestHeader(HttpHeaders.AUTHORIZATION) String accessToken); +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoAuthApiClient.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoAuthApiClient.java new file mode 100644 index 0000000..43cc1aa --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoAuthApiClient.java @@ -0,0 +1,20 @@ +package org.kau.kkoolbeeServer.global.auth.fegin.kakao; + +import org.kau.kkoolbeeServer.global.auth.fegin.kakao.response.KakaoAccessTokenResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "kakaoAuthApiClient", url = "https://kauth.kakao.com") +public interface KakaoAuthApiClient { + + //Authorization Code를 활용해서 Access Token + Refresh Token을 받아오는 역할 + @PostMapping(value = "/oauth/token", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + KakaoAccessTokenResponse getOAuth2AccessToken( + @RequestParam("grant_type") String grantType, + @RequestParam("client_id") String clientId, + @RequestParam("redirect_uri") String redirectUri, + @RequestParam("code") String code + ); +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoLoginService.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoLoginService.java new file mode 100644 index 0000000..2e52a48 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/KakaoLoginService.java @@ -0,0 +1,66 @@ +package org.kau.kkoolbeeServer.global.auth.fegin.kakao; + +import lombok.RequiredArgsConstructor; +import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.global.auth.fegin.kakao.response.KakaoAccessTokenResponse; +import org.kau.kkoolbeeServer.global.auth.fegin.kakao.response.KakaoUserResponse; +import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.INVALID_CODE_HEADER_ERROR; + +@Service +@RequiredArgsConstructor +@Transactional +public class KakaoLoginService { + + @Value("${kakao.client-id}") + private String CLIENT_ID; + @Value("${kakao.authorization-grant-type}") + private String GRANT_TYPE; + @Value("${kakao.redirect-uri}") + private String REDIRECT_URL; + + private final KakaoAuthApiClient kakaoAuthApiClient; + private final KakaoApiClient kakaoApiClient; + + public String getKakaoAccessToken(String code) { + + // Authorization code로 카카오 Access 토큰 불러오기 + KakaoAccessTokenResponse tokenResponse = kakaoAuthApiClient.getOAuth2AccessToken( + GRANT_TYPE, + CLIENT_ID, + REDIRECT_URL, + parseCodeString(code) + ); + return tokenResponse.getAccessToken(); + // 카카오 Refresh 토큰은 미사용 + } + + public String getKakaoId(String socialAccessToken) { + + // 카카오 Access 토큰으로 유저 정보 불러오기 + KakaoUserResponse userResponse = kakaoApiClient.getUserInformation("Bearer " + socialAccessToken); + + return Long.toString(userResponse.getId()); + } + + public void setKakaoInfo(Member loginMember, String socialAccessToken) { + + // 카카오 Access 토큰으로 유저 정보 불러오기 + KakaoUserResponse userResponse = kakaoApiClient.getUserInformation("Bearer " + socialAccessToken); + + loginMember.updateSocialInfo(userResponse.getKakaoAccount().getProfile().getNickname(), + userResponse.getKakaoAccount().getProfile().getProfileImageUrl()); + } + + private static String parseCodeString(String codeString) { + String[] strings = codeString.split(" "); + if (strings.length != 2) { + throw new CustomException(INVALID_CODE_HEADER_ERROR); + } + return strings[1]; + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccessTokenResponse.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccessTokenResponse.java new file mode 100644 index 0000000..5455999 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccessTokenResponse.java @@ -0,0 +1,20 @@ +package org.kau.kkoolbeeServer.global.auth.fegin.kakao.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.*; + +@ToString +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class KakaoAccessTokenResponse { + + private String accessToken; + private String refreshToken; + + public static KakaoAccessTokenResponse of(String accessToken, String refreshToken) { + return new KakaoAccessTokenResponse(accessToken, refreshToken); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccount.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccount.java new file mode 100644 index 0000000..9ef3458 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoAccount.java @@ -0,0 +1,15 @@ +package org.kau.kkoolbeeServer.global.auth.fegin.kakao.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.*; + +@ToString +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class KakaoAccount { + + private KakaoUserProfile profile; +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserProfile.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserProfile.java new file mode 100644 index 0000000..2790421 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserProfile.java @@ -0,0 +1,16 @@ +package org.kau.kkoolbeeServer.global.auth.fegin.kakao.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.*; + +@ToString +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class KakaoUserProfile { + + private String nickname; + private String profileImageUrl; +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserResponse.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserResponse.java new file mode 100644 index 0000000..9e9a355 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/fegin/kakao/response/KakaoUserResponse.java @@ -0,0 +1,18 @@ +package org.kau.kkoolbeeServer.global.auth.fegin.kakao.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.*; + +@ToString +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class KakaoUserResponse { + + //받아올 땐 Long이지만, String으로 바꿔서 사용 + private Long id; + + private KakaoAccount kakaoAccount; +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java new file mode 100644 index 0000000..1cd503a --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java @@ -0,0 +1,165 @@ +package org.kau.kkoolbeeServer.global.auth.jwt; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SecurityException; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.kau.kkoolbeeServer.global.auth.redis.RefreshToken; +import org.kau.kkoolbeeServer.global.auth.redis.TokenRepository; +import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.security.Principal; +import java.util.Base64; +import java.util.Date; + +import static java.util.Objects.isNull; +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.*; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtProvider { + + private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 1000L * 60 * 24 * 365; // 액세스 토큰 만료 시간: 1년으로 지정 + private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 1000L * 2 * 60 * 24 * 365; // 리프레시 토큰 만료 시간: 2년으로 지정 + + @Value("${jwt.secret}") + private String JWT_SECRET; + private final TokenRepository tokenRepository; + + @PostConstruct + protected void init() { + JWT_SECRET = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes(StandardCharsets.UTF_8)); + } + + // Access 토큰, Refresh 토큰 발급 + public TokenDto issueToken(Authentication authentication) { + return TokenDto.of( + generateAccessToken(authentication), + generateRefreshToken(authentication)); + } + + // Access 토큰 생성 + private String generateAccessToken(Authentication authentication) { + final Date now = new Date(); + + final Claims claims = Jwts.claims() + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION_TIME)); + + claims.put("memberId", authentication.getPrincipal()); + + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setClaims(claims) + .signWith(getSigningKey()) + .compact(); + } + + // Refresh 토큰 생성 + + /** + * Redis 내부에 + * memberId: refreshToken 형태로 저장 + */ + private String generateRefreshToken(Authentication authentication) { + final Date now = new Date(); + + final Claims claims = Jwts.claims() + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + REFRESH_TOKEN_EXPIRATION_TIME)); + + claims.put("memberId", authentication.getPrincipal()); + + String refreshToken = Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setClaims(claims) + .signWith(getSigningKey()) + .compact(); + + tokenRepository.save( + RefreshToken.builder() + .memberId(Long.parseLong(authentication.getPrincipal().toString())) + .refreshToken(refreshToken) + .expiration(REFRESH_TOKEN_EXPIRATION_TIME.intValue() / 1000) + .build() + ); + + return refreshToken; + } + + // Access 토큰 검증 + public boolean validateAccessToken(String accessToken) { + try { + final Claims claims = getBody(accessToken); + return true; + } catch (MalformedJwtException ex) { + throw new CustomException(INVALID_JWT_TOKEN); + } catch (ExpiredJwtException ex) { + throw new CustomException(EXPIRED_JWT_TOKEN); + } catch (UnsupportedJwtException ex) { + throw new CustomException(UNSUPPORTED_JWT_TOKEN); + } catch (IllegalArgumentException ex) { + throw new CustomException(EMPTY_JWT_TOKEN); + } catch (SecurityException ex) { + throw new CustomException(INVALID_JWT_SIGNATURE); + } catch (Exception e) { + throw new CustomException(UNKNOWN_JWT_ERROR); + } + } + + // Refresh 토큰 검증 + public Long validateRefreshToken(String refreshToken) { + // Refresh 토큰 만료 : Redis에 해당 Refresh 토큰이 존재하지 않음 + Long memberId = getUserFromJwt(refreshToken); + if (tokenRepository.existsById(memberId)) { + return memberId; + } else { + throw new CustomException(INVALID_REFRESH_TOKEN); + } + } + + // Refresh 토큰 삭제 (memberId 기준으로) + public void deleteRefreshToken(Long memberId) { + if (tokenRepository.existsById(memberId)) { + tokenRepository.deleteById(memberId); + } else { + throw new CustomException(NOT_FOUND_REFRESH_TOKEN_ERROR); + } + } + + // 토큰에 담겨있는 memberId 획득 + public Long getUserFromJwt(String token) { + Claims claims = getBody(token); + return Long.parseLong(claims.get("memberId").toString()); + } + + private Claims getBody(final String token) { + // 만료된 토큰에 대해 parseClaimsJws를 수행하면 io.jsonwebtoken.ExpiredJwtException이 발생 + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + private SecretKey getSigningKey() { + String encodedKey = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes()); + return Keys.hmacShaKeyFor(encodedKey.getBytes()); + } + + public static Long getUserFromPrincial(Principal principal) { + if (isNull(principal)) { + throw new CustomException(EMPTY_PRINCIPLE_ERROR); + } + + return Long.valueOf(principal.getName()); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/TokenDto.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/TokenDto.java new file mode 100644 index 0000000..8af28c8 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/TokenDto.java @@ -0,0 +1,18 @@ +package org.kau.kkoolbeeServer.global.auth.jwt; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class TokenDto { + + private String accessToken; + private String refreshToken; + + public static TokenDto of(String accessToken, String refreshToken) { + return new TokenDto(accessToken, refreshToken); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/redis/RefreshToken.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/redis/RefreshToken.java new file mode 100644 index 0000000..bb3b0e7 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/redis/RefreshToken.java @@ -0,0 +1,27 @@ +package org.kau.kkoolbeeServer.global.auth.redis; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; + +import java.util.concurrent.TimeUnit; + +@Getter +@RedisHash(value = "refresh") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RefreshToken { + + @Id + private Long memberId; + + private String refreshToken; + + @TimeToLive(unit = TimeUnit.SECONDS) + private Integer expiration; +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/redis/TokenRepository.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/redis/TokenRepository.java new file mode 100644 index 0000000..326d804 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/redis/TokenRepository.java @@ -0,0 +1,6 @@ +package org.kau.kkoolbeeServer.global.auth.redis; + +import org.springframework.data.repository.CrudRepository; + +public interface TokenRepository extends CrudRepository { +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/security/AuthWhiteList.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/AuthWhiteList.java new file mode 100644 index 0000000..f39b94f --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/AuthWhiteList.java @@ -0,0 +1,24 @@ +package org.kau.kkoolbeeServer.global.auth.security; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class AuthWhiteList { + + public static final List AUTH_WHITELIST_DEFALUT = Arrays.asList( + "/loading", "/error", "/api/login", "/api/reissue", + "/health", "/actuator/health", "/" + ); + + public static final List AUTH_WHITELIST_WILDCARD = Arrays.asList( + "/api/kakao/**", "/api/test/**", "/api/**", //일단 API 다 필터에 안 걸리도록 설정, 나중에 바꾸기 + "/swagger-ui/**", "/swagger-resources/**", "/api-docs/**", + "/v3/api-docs/**", "/webjars/**" + ); + + public static final String[] AUTH_WHITELIST = Stream.concat( + AUTH_WHITELIST_DEFALUT.stream(), + AUTH_WHITELIST_WILDCARD.stream() + ).toArray(String[]::new); +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtAuthenticationFilter.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..84c0f48 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtAuthenticationFilter.java @@ -0,0 +1,65 @@ +package org.kau.kkoolbeeServer.global.auth.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.kau.kkoolbeeServer.global.auth.jwt.JwtProvider; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +import static org.kau.kkoolbeeServer.global.auth.security.AuthWhiteList.AUTH_WHITELIST_DEFALUT; +import static org.kau.kkoolbeeServer.global.auth.security.AuthWhiteList.AUTH_WHITELIST_WILDCARD; + +/** + * JWT의 유효성을 검증하는 Filter + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtProvider jwtProvider; + + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws IOException, ServletException, IOException { + + if (AUTH_WHITELIST_DEFALUT.stream() + .anyMatch(whiteUrl -> request.getRequestURI().equals(whiteUrl))) { + filterChain.doFilter(request, response); + return; + } + + if (AUTH_WHITELIST_WILDCARD.stream() + .anyMatch(whiteUrl -> request.getRequestURI().startsWith(whiteUrl.substring(0, whiteUrl.length() - 3)))) { + filterChain.doFilter(request, response); + return; + } + + // Request의 Header에서 JWT 토큰을 String으로 가져옴 + final String token = getJwtFromRequest(request); + if (jwtProvider.validateAccessToken(token)) { + Long memberId = jwtProvider.getUserFromJwt(token); + UserAuthentication authentication = new UserAuthentication(memberId, null, null); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(request, response); + } + + private String getJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring("Bearer ".length()); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtExceptionFilter.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtExceptionFilter.java new file mode 100644 index 0000000..001c228 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/JwtExceptionFilter.java @@ -0,0 +1,68 @@ +package org.kau.kkoolbeeServer.global.auth.security; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; +import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.*; + + +@Slf4j +@RequiredArgsConstructor +@Component +public class JwtExceptionFilter extends OncePerRequestFilter { + + private final ObjectMapper objectMapper; + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + response.setCharacterEncoding("utf-8"); + try { + filterChain.doFilter(request, response); + } catch (CustomException e) { + if (e.getErrorType().equals(INVALID_JWT_TOKEN)) { + setErrorResponse(response, INVALID_JWT_TOKEN); + } else if (e.getErrorType().equals(EXPIRED_JWT_TOKEN)) { + setErrorResponse(response, EXPIRED_JWT_TOKEN); + } else if (e.getErrorType().equals(UNSUPPORTED_JWT_TOKEN)) { + setErrorResponse(response, UNSUPPORTED_JWT_TOKEN); + } else if (e.getErrorType().equals(EMPTY_JWT_TOKEN)) { + setErrorResponse(response, EMPTY_JWT_TOKEN); + } else if (e.getErrorType().equals(INVALID_JWT_SIGNATURE)) { + setErrorResponse(response, INVALID_JWT_SIGNATURE); + } else if (e.getErrorType().equals(UNKNOWN_JWT_ERROR)) { + setErrorResponse(response, UNKNOWN_JWT_ERROR); + } + } + } + + private void setErrorResponse(HttpServletResponse response, ErrorType errorType) { + response.setStatus(errorType.getHttpStatus().value()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + ErrorResponse errorResponse = new ErrorResponse(errorType.getHttpStatusCode(), errorType.getMessage()); + try { + response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + + @Data + public static class ErrorResponse { + private final Integer code; + private final String message; + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/security/UserAuthentication.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/UserAuthentication.java new file mode 100644 index 0000000..1a4fa6b --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/security/UserAuthentication.java @@ -0,0 +1,13 @@ +package org.kau.kkoolbeeServer.global.auth.security; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class UserAuthentication extends UsernamePasswordAuthenticationToken { + + public UserAuthentication(Object principal, Object credentials, Collection authorities) { + super(principal, credentials, authorities); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index c6d79c5..fdf7904 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -12,7 +12,35 @@ public enum ErrorType { /** * 400 BAD REQUEST */ - REQUEST_VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다"), + // 표준 오류 + REQUEST_VALIDATION_ERROR(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), + INVALID_TYPE_ERROR(HttpStatus.BAD_REQUEST, "잘못된 타입이 입력되었습니다."), + INVALID_MISSING_HEADER_ERROR(HttpStatus.BAD_REQUEST, "요청에 필요한 헤더값이 존재하지 않습니다."), + INVALID_HTTP_REQUEST_ERROR(HttpStatus.BAD_REQUEST, "요청 형식이 허용된 형식과 다릅니다."), + INVALID_HTTP_METHOD_ERROR(HttpStatus.BAD_REQUEST, "지원되지 않는 HTTP method 요청입니다."), + INVALID_TOKEN_HEADER_ERROR(HttpStatus.BAD_REQUEST, "토큰 헤더값의 형식이 잘못되었습니다."), + INVALID_CODE_HEADER_ERROR(HttpStatus.BAD_REQUEST, "code 헤더값의 형식이 잘못되었습니다."), + // 인증 관련 오류 + EMPTY_PRINCIPLE_ERROR(HttpStatus.BAD_REQUEST, "Principle 객체가 없습니다. (null)"), + + /** + * 401 UNAUTHORIZED + */ + INVALID_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않는 JWT 토큰입니다."), + EXPIRED_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 JWT 토큰입니다."), + UNSUPPORTED_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "지원하지 않는 JWT 토큰입니다."), + EMPTY_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "JWT 토큰이 존재하지 않습니다."), + INVALID_JWT_SIGNATURE(HttpStatus.UNAUTHORIZED, "잘못된 JWT 서명입니다."), + UNKNOWN_JWT_ERROR(HttpStatus.UNAUTHORIZED, "알 수 없는 JWT 토큰 오류가 발생했습니다."), + + INVALID_SOCIAL_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 소셜 엑세스 토큰입니다."), + INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 리프레시 토큰입니다, 다시 로그인을 해주세요."), + + /** + * 404 NOT FOUND + */ + NOT_FOUND_MEMBER_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."), + NOT_FOUND_REFRESH_TOKEN_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 리프레시 토큰입니다."), /** * 500 INTERNAL SERVER ERROR diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java index b664160..41a3296 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java @@ -13,6 +13,10 @@ public enum SuccessType { * 200 OK */ PROCESS_SUCCESS(HttpStatus.OK, "OK"), + LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), + REISSUE_SUCCESS(HttpStatus.OK, "Access 토큰 재발급에 성공했습니다."), + LOGOUT_SUCCESS(HttpStatus.OK, "로그아웃에 성공했습니다."), + KAKAO_ACCESS_TOKEN_SUCCESS(HttpStatus.OK, "카카오 엑세스 토큰을 가져오는데 성공했습니다."), ; private final HttpStatus httpStatus; diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java index a23c14d..e72c13c 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java @@ -20,7 +20,7 @@ import java.util.Map; import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.INTERNAL_SERVER_ERROR; -import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.REQUEST_VALIDATION_EXCEPTION; +import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.REQUEST_VALIDATION_ERROR; @Slf4j // 로그 출력 @RestControllerAdvice @@ -46,7 +46,7 @@ protected ApiResponse handleMethodArgumentNotValidException(final MethodArgum String validKeyName = String.format("valid_%s", error.getField()); validateDetails.put(validKeyName, error.getDefaultMessage()); } - return ApiResponse.error(REQUEST_VALIDATION_EXCEPTION, validateDetails); + return ApiResponse.error(REQUEST_VALIDATION_ERROR, validateDetails); } /** diff --git a/src/main/java/org/kau/kkoolbeeServer/global/config/RedisConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/RedisConfig.java new file mode 100644 index 0000000..4d5fb14 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/RedisConfig.java @@ -0,0 +1,24 @@ +package org.kau.kkoolbeeServer.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; + +@EnableRedisRepositories +@Configuration +public class RedisConfig { + + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/config/SecurityConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/SecurityConfig.java new file mode 100644 index 0000000..67891bc --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/SecurityConfig.java @@ -0,0 +1,41 @@ +package org.kau.kkoolbeeServer.global.config; + +import lombok.RequiredArgsConstructor; +import org.kau.kkoolbeeServer.global.auth.security.JwtAuthenticationFilter; +import org.kau.kkoolbeeServer.global.auth.security.JwtExceptionFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import static org.kau.kkoolbeeServer.global.auth.security.AuthWhiteList.AUTH_WHITELIST; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final JwtExceptionFilter jwtExceptionFilter; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .formLogin(AbstractHttpConfigurer::disable) // Form Login 사용 X + .httpBasic(AbstractHttpConfigurer::disable) // HTTP Basic 사용 X + .csrf(AbstractHttpConfigurer::disable) // 쿠키 기반이 아닌 JWT 기반이므로 사용 X + .sessionManagement(sessionManagementConfigurer -> + sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // Spring Security 세션 정책 : 세션 생성 및 사용하지 않음 + .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> + authorizationManagerRequestMatcherRegistry + .requestMatchers(AUTH_WHITELIST).permitAll() + .anyRequest().authenticated()) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtExceptionFilter, JwtAuthenticationFilter.class) + .build(); + } +} \ No newline at end of file diff --git a/src/main/resources/application-dev1.yml b/src/main/resources/application-dev1.yml index a7a2941..30d4567 100644 --- a/src/main/resources/application-dev1.yml +++ b/src/main/resources/application-dev1.yml @@ -18,6 +18,15 @@ spring: hibernate: auto_quote_keyword: true format_sql: true + data: + redis: + host: localhost + port: 6379 + +kakao: + client-id: ${KAKAO_ID} + authorization-grant-type: authorization_code + redirect-uri: ${KAKAO_REDIRECT_DEV} server: port: 8081 \ No newline at end of file diff --git a/src/main/resources/application-dev2.yml b/src/main/resources/application-dev2.yml index 3f49986..bc464df 100644 --- a/src/main/resources/application-dev2.yml +++ b/src/main/resources/application-dev2.yml @@ -18,6 +18,15 @@ spring: hibernate: auto_quote_keyword: true format_sql: true + data: + redis: + host: localhost + port: 6379 + +kakao: + client-id: ${KAKAO_ID} + authorization-grant-type: authorization_code + redirect-uri: ${KAKAO_REDIRECT_DEV} server: port: 8082 \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 29913bb..a026553 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -19,6 +19,15 @@ spring: hibernate: auto_quote_keyword: true format_sql: true + data: + redis: + host: localhost + port: 6379 + +kakao: + client-id: ${KAKAO_ID} + authorization-grant-type: authorization_code + redirect-uri: ${KAKAO_REDIRECT_LOCAL} server: port: 8080 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7555499..336faf6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,17 @@ spring: profiles: active: local +cloud: + aws: + credentials: + accessKey: ${ACCESS_KEY} + secretKey: ${SECRET_KEY} + aws-region: ap-northeast-2 + s3-bucket-name: ${BUCKET_NAME} + +jwt: + secret: ${JWT_SECRET} + logging: level: com: From 58aa184ac18486de8d1a38088e550da29bdf4adc Mon Sep 17 00:00:00 2001 From: Wang HoEun Date: Mon, 29 Apr 2024 11:55:42 +0900 Subject: [PATCH 12/43] =?UTF-8?q?fix:=20build.gradle=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=B5=9C=EC=8B=A0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 0cb1924..027092f 100644 --- a/build.gradle +++ b/build.gradle @@ -55,11 +55,11 @@ dependencies { runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' // Spring Security -// implementation 'org.springframework.boot:spring-boot-starter-security' -// implementation 'org.springframework.security:spring-security-test' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.security:spring-security-test' // Redis -// implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Open Feign implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.0.4' From acb9ade1131d097932d209bd5f82f3c2656734e5 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Tue, 30 Apr 2024 12:28:15 +0900 Subject: [PATCH 13/43] Feat/#30 slowtype save api (#32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * diary-content-api구현 및 테스트성공 * json필드 이름변경 * refactor:ApiResponse초기세팅 필드이름변경status * api명세서 변경으로인한 초기세팅 변경 * succes type에서 오타로 인한 커밋 * feat: list-calender api구현 but 테스트는 아직 * api테스트완료 but 시간대 문제발생 * 애플리케이션 timezone을 utc로 설정하여 임시해결 * refactor /#24 json필드명 오류발견후수정 * index on feat/#26-list-calender: b531010 refactor /#24 json필드명 오류발견후수정 * feat-feelinglist 1차구현 * try catch구문 삭제 및 exceptionhandler수정, apiresponse수정 * entity 수정 * feat:apiresponse수정 및 s3설정파일 임시구현 * feat:S3연결제외 api구현 * refactor: imageurl추가에따른 컨텐트 api 살짝수정 * refactor: diarycontent반환값에 title추가 * refactor: s3연결전까지 코든짠것 * success type 추가 * refactor : successtype변경 * develop pull 마무리 * refactor : application.yml이랑 value값맞추기성공 * feat: api구현 및 테스트 완료 * refactor:content,advice길이수정 --- .../KkoolbeeServerApplication.java | 10 +- .../kkoolbeeServer/S3/S3UploaderService.java | 45 ++++++ .../kkoolbeeServer/domain/advice/Advice.java | 18 ++- .../domain/advice/dto/AdviceResponseDto.java | 12 ++ .../kkoolbeeServer/domain/diary/Diary.java | 20 ++- .../diary/controller/DiaryController.java | 150 ++++++++++++++++++ .../dto/request/CurrentDateRequestDto.java | 20 +++ .../dto/request/DiaryContentRequestDto.java | 14 ++ .../dto/request/FeelingListRequestDto.java | 10 ++ .../response/CalenderDiaryResponseDto.java | 18 +++ .../dto/response/DiaryContentResponseDto.java | 21 +++ .../dto/response/FeelingListResponseDto.java | 17 ++ .../response/SlowTypeCreateResponseDto.java | 17 ++ .../diary/repository/DiaryRepository.java | 18 +++ .../domain/diary/service/DiaryService.java | 45 ++++++ .../global/common/domain/BaseTimeEntity.java | 2 + .../global/common/dto/ApiResponse.java | 9 +- .../global/common/dto/enums/ErrorType.java | 2 +- .../global/common/dto/enums/SuccessType.java | 4 + .../exception/GlobalExceptionHandler.java | 44 ++++- .../global/config/S3Config.java | 33 ++++ src/main/resources/application.yml | 6 +- .../diary/controller/DiaryControllerTest.java | 99 ++++++++++++ 23 files changed, 610 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/CurrentDateRequestDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryContentRequestDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/FeelingListRequestDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/config/S3Config.java create mode 100644 src/test/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryControllerTest.java diff --git a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java index c480cb0..eef97be 100644 --- a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java +++ b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java @@ -5,12 +5,20 @@ import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.cloud.openfeign.EnableFeignClients; +import java.util.TimeZone; + @SpringBootApplication(exclude = { UserDetailsServiceAutoConfiguration.class }) @EnableFeignClients public class KkoolbeeServerApplication { public static void main(String[] args) { SpringApplication.run(KkoolbeeServerApplication.class, args); + + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + TimeZone tz = TimeZone.getDefault(); + System.out.println("현재 시간대: " + tz.getID()); + + } -} +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java new file mode 100644 index 0000000..64be6b9 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java @@ -0,0 +1,45 @@ +package org.kau.kkoolbeeServer.S3; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.PutObjectRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.UUID; + +@Service +public class S3UploaderService { + + private final AmazonS3 amazonS3Client; + @Value("${cloud.aws.s3.bucket-name}") + private String bucketName; + + @Autowired + public S3UploaderService(AmazonS3 amazonS3Client) { + this.amazonS3Client = amazonS3Client; + } + + public String upload(MultipartFile file) throws IOException { + String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename(); + + File convertedFile = convertMultiPartToFile(file); + amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, convertedFile)); + convertedFile.delete(); // 임시 파일 삭제 + + return amazonS3Client.getUrl(bucketName, fileName).toString(); + } + + private File convertMultiPartToFile(MultipartFile file) throws IOException { + File convertedFile = new File(file.getOriginalFilename()); + try (FileOutputStream fos = new FileOutputStream(convertedFile)) { + fos.write(file.getBytes()); + } + return convertedFile; + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java index 8d74a15..3f71714 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java @@ -1,18 +1,28 @@ package org.kau.kkoolbeeServer.domain.advice; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @Entity +@Getter +@Setter +@NoArgsConstructor public class Advice { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; + @Column(columnDefinition = "TEXT") String kind_advice; + @Column(columnDefinition = "TEXT") String spicy_advice; + public Advice(String kind_advice, String spicy_advice) { + this.kind_advice = kind_advice; + this.spicy_advice = spicy_advice; + } } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java new file mode 100644 index 0000000..1f88183 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java @@ -0,0 +1,12 @@ +package org.kau.kkoolbeeServer.domain.advice.dto; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class AdviceResponseDto { + private String spicy; + private String kind; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java index c09f3ea..8727f9e 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -1,6 +1,9 @@ package org.kau.kkoolbeeServer.domain.diary; import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.kau.kkoolbeeServer.domain.advice.Advice; import org.kau.kkoolbeeServer.domain.member.Member; import org.kau.kkoolbeeServer.global.common.domain.BaseTimeEntity; @@ -10,6 +13,9 @@ import java.util.List; @Entity +@Getter +@Setter +@NoArgsConstructor public class Diary extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -30,21 +36,13 @@ public class Diary extends BaseTimeEntity { private Feeling feeling; - @Column(nullable = false,length = 1000) + @Column(nullable = false,columnDefinition = "TEXT") private String content; - private String title; - - - - - - - - - + private String imageurl; + private String title; } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java new file mode 100644 index 0000000..68d7c70 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -0,0 +1,150 @@ +package org.kau.kkoolbeeServer.domain.diary.controller; + +import jakarta.validation.Valid; +import org.kau.kkoolbeeServer.S3.S3UploaderService; +import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; +import org.kau.kkoolbeeServer.domain.diary.Diary; +import org.kau.kkoolbeeServer.domain.diary.dto.request.FeelingListRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.CalenderDiaryResponseDto; +import org.kau.kkoolbeeServer.domain.diary.dto.request.CurrentDateRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.request.DiaryContentRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.DiaryContentResponseDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.FeelingListResponseDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.SlowTypeCreateResponseDto; +import org.kau.kkoolbeeServer.domain.diary.service.DiaryService; +import org.kau.kkoolbeeServer.global.common.dto.ApiResponse; +import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; +import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@RestController +public class DiaryController { + + + private DiaryService diaryService; + private final S3UploaderService s3UploaderService; + @Autowired + public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderService) { + this.diaryService = diaryService; + this.s3UploaderService=s3UploaderService; + + } + + @PostMapping("/api/diary/content") + public ResponseEntity> getDiaryContents(@RequestBody DiaryContentRequestDto diaryContentRequestDto) { + + Long diaryId = diaryContentRequestDto.getDiaryId(); + + Optional diaryOptional = diaryService.findDiaryById(diaryId); + + if (diaryOptional.isPresent()) { + + Diary diary = diaryOptional.get(); + // Diary의 Advice 정보를 AdviceResponseDto 객체로 변환 + AdviceResponseDto adviceResponseDto = new AdviceResponseDto( + diary.getAdvice().getSpicy_advice(), //여기서 null이 나오면 ? + diary.getAdvice().getKind_advice() + + ); + + + + DiaryContentResponseDto responseDto = new DiaryContentResponseDto( + diary.getContent(), + adviceResponseDto, + diary.getFeeling().toString(), + diary.getImageurl(), + diary.getTitle() + ); + + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseDto)); + } else { + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR)); + //diary 가 null 일 경우 요청이상함 반환 + } + + } + @PostMapping("/api/diary/list/calendar") + public ResponseEntity> getDiariesByMonth(@RequestBody CurrentDateRequestDto requestDto){ + LocalDateTime currentDate = requestDto.getCurrentDate(); + + Listdiaries=diaryService.findDiariesByMonth(currentDate); + if(diaries.isEmpty()){ + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 월에 대한 일기가 존재하지 않습니다.")); + } + + List diaryDtos=diaries.stream() + .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt())) + .collect(Collectors.toList()); + + Map> responseMap= Map.of("monthList",diaryDtos); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); + + + + + } + + @PostMapping("/api/diary/list/feeling") + public ResponseEntity> getDiariesByFeeling(@RequestBody FeelingListRequestDto requestDto) { + + + List diaries = diaryService.findDiariesByFeeling(requestDto.getFeeling()); + + if (diaries.isEmpty()) { + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 감정에 대한 일기가 존재하지 않습니다.")); + } + + List feelingList = diaries.stream() + .map(diary -> new FeelingListResponseDto(diary.getId(), diary.getWritedAt(), diary.getTitle())) + .collect(Collectors.toList()); + + Map> responseMap = Map.of("feelingList", feelingList); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); + + } + + @PostMapping("/api/diary/create/slow") + public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl")MultipartFile image, + @RequestPart("diaryTitle") String diaryTitle, + @RequestPart("diaryContent") String diaryContent){ + + try { + String imageUrl = s3UploaderService.upload(image); + Diary diary = new Diary(); + diary.setTitle(diaryTitle); + diary.setContent(diaryContent); + diary.setImageurl(imageUrl); + + Diary savedDiary=diaryService.saveDiary(diary); + SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl()); + + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,"서버 내부 오류")); + } + } + + + + +} \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/CurrentDateRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/CurrentDateRequestDto.java new file mode 100644 index 0000000..6ff92db --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/CurrentDateRequestDto.java @@ -0,0 +1,20 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CurrentDateRequestDto { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime currentDate; + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryContentRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryContentRequestDto.java new file mode 100644 index 0000000..8f48855 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryContentRequestDto.java @@ -0,0 +1,14 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class DiaryContentRequestDto { + private Long diaryId; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/FeelingListRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/FeelingListRequestDto.java new file mode 100644 index 0000000..334243e --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/FeelingListRequestDto.java @@ -0,0 +1,10 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + + +import lombok.Getter; + +@Getter +public class FeelingListRequestDto { + + private String feeling; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java new file mode 100644 index 0000000..b79d984 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java @@ -0,0 +1,18 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@AllArgsConstructor +public class CalenderDiaryResponseDto { + private Long diaryId; + private String diaryTitle; + private LocalDateTime createdDate; + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java new file mode 100644 index 0000000..fd0a8ef --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java @@ -0,0 +1,21 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; +@JsonPropertyOrder({ "diary_content", "advice","feeling","imageUrl","diaryTitle"}) + +@Getter +@AllArgsConstructor +public class DiaryContentResponseDto { + @JsonProperty("diary_content") + private String diaryContent; + private AdviceResponseDto advice; + private String feeling; + private String imageUrl; + private String diaryTitle; + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java new file mode 100644 index 0000000..fde0311 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java @@ -0,0 +1,17 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@AllArgsConstructor +public class FeelingListResponseDto { + private Long diaryId; + private LocalDateTime createdDate; + private String diaryTitle; + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java new file mode 100644 index 0000000..3cded2f --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java @@ -0,0 +1,17 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class SlowTypeCreateResponseDto { + + private Long diaryId; + private String diaryContent; + private String diaryTitle; + private String imageurl; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java new file mode 100644 index 0000000..9ac3087 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java @@ -0,0 +1,18 @@ +package org.kau.kkoolbeeServer.domain.diary.repository; + +import org.kau.kkoolbeeServer.domain.diary.Diary; +import org.kau.kkoolbeeServer.domain.diary.Feeling; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface DiaryRepository extends JpaRepository { + + List findByWritedAtBetween(LocalDateTime startDateTime, LocalDateTime endDateTime); + + List findByFeeling(Feeling feeling); + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java new file mode 100644 index 0000000..9726645 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -0,0 +1,45 @@ +package org.kau.kkoolbeeServer.domain.diary.service; + +import org.kau.kkoolbeeServer.domain.diary.Diary; +import org.kau.kkoolbeeServer.domain.diary.Feeling; +import org.kau.kkoolbeeServer.domain.diary.repository.DiaryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.swing.text.html.Option; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Service +public class DiaryService { + + private DiaryRepository diaryRepository; + + @Autowired + public DiaryService(DiaryRepository diaryRepository) { + this.diaryRepository = diaryRepository; + } + + public Optional findDiaryById(Long diary_id){ + + return diaryRepository.findById(diary_id); + + + } + public List findDiariesByMonth(LocalDateTime date) { + LocalDateTime startOfMonth = date.withDayOfMonth(1).toLocalDate().atStartOfDay(); + LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); + return diaryRepository.findByWritedAtBetween(startOfMonth, endOfMonth); + } + + public List findDiariesByFeeling(String feeling) { + return diaryRepository.findByFeeling(Feeling.valueOf(feeling)); + } + + public Diary saveDiary(Diary diary){ + return diaryRepository.save(diary); + } + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java b/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java index 50746a7..462b36f 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/domain/BaseTimeEntity.java @@ -3,6 +3,7 @@ import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; import lombok.Getter; +import lombok.Setter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -12,6 +13,7 @@ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) @Getter +@Setter public abstract class BaseTimeEntity { @CreatedDate diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java index 62edcb2..745ec95 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/ApiResponse.java @@ -10,14 +10,15 @@ import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; @Getter -@JsonPropertyOrder({"code", "message", "data"}) +@JsonPropertyOrder({"status","message", "data"}) @RequiredArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ApiResponse { - private final int code; // 상태 코드 + private final int status; // 상태 코드 private final String message; // 회원 조회가 정상 처리되었습니다! + @JsonInclude(JsonInclude.Include.NON_NULL) private T data; // 객체가 실제로 들어감 @@ -42,7 +43,9 @@ public static ApiResponse error(ErrorType errorType, String message, T da } public static ApiResponse error(ErrorType errorType, Exception e) { - return new ApiResponse<>(errorType.getHttpStatusCode(), errorType.getMessage(), e); + return new ApiResponse<>(errorType.getHttpStatusCode(), errorType.getMessage()); + + } public static ApiResponse error(ErrorType errorType, T data) { diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index fdf7904..217d955 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -45,7 +45,7 @@ public enum ErrorType { /** * 500 INTERNAL SERVER ERROR */ - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "알 수 없는 서버 에러가 발생했습니다."), + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류"), ; private final HttpStatus httpStatus; diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java index 41a3296..6e35222 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/SuccessType.java @@ -13,12 +13,16 @@ public enum SuccessType { * 200 OK */ PROCESS_SUCCESS(HttpStatus.OK, "OK"), + PROCESS_SUCCESSED(HttpStatus.CREATED, "요청이 성공했습니다."), LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), REISSUE_SUCCESS(HttpStatus.OK, "Access 토큰 재발급에 성공했습니다."), LOGOUT_SUCCESS(HttpStatus.OK, "로그아웃에 성공했습니다."), KAKAO_ACCESS_TOKEN_SUCCESS(HttpStatus.OK, "카카오 엑세스 토큰을 가져오는데 성공했습니다."), ; + + + private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java index e72c13c..0b48f7c 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java @@ -7,6 +7,8 @@ import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageConversionException; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.FieldError; @@ -41,6 +43,7 @@ protected ApiResponse handleMethodArgumentNotValidException(final MethodArgum Errors errors = e.getBindingResult(); Map validateDetails = new HashMap<>(); + log.error("Unexpected error occurred not valid", e); for (FieldError error : errors.getFieldErrors()) { String validKeyName = String.format("valid_%s", error.getField()); @@ -67,7 +70,44 @@ protected ResponseEntity> handleBusinessException(CustomException */ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) - protected ApiResponse handleException(final Exception e, final HttpServletRequest request) throws IOException { - return ApiResponse.error(INTERNAL_SERVER_ERROR, e); + protected ApiResponse handleException(final Exception e, final HttpServletRequest request) throws IOException { + log.error("Unexpected error occurred", e); + return ApiResponse.error(INTERNAL_SERVER_ERROR); } + + //이부분수정 서버내부오류는 오류 데이터를 줄 필요가 없이 서버내부오류만 띄우면 되므로!! + + @ResponseStatus(HttpStatus.BAD_REQUEST) //이부분추가 + @ExceptionHandler(HttpMessageConversionException.class) + protected ApiResponse handleHttpMessageConversionException(HttpMessageConversionException e) { + // 로그 기록, 에러 메시지 생성 등 필요한 처리 + log.error("Unexpected error occurred", e); + return ApiResponse.error(REQUEST_VALIDATION_ERROR, "잘못된 요청 형식입니다."); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(HttpMessageNotReadableException.class) + protected ApiResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { + // 로그 기록, 오류 메시지 생성 및 기타 필요한 처리 + log.error("Unexpected error occurred", e); + return ApiResponse.error(REQUEST_VALIDATION_ERROR, "잘못된 요청 형식입니다."); + } + + //이부분추가 + + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(IllegalArgumentException.class) + protected ApiResponse handleIllegalArgumentException(IllegalArgumentException e) { + // 로그 기록, 오류 메시지 생성 및 기타 필요한 처리 + log.error("Unexpected error occurred", e); + return ApiResponse.error(REQUEST_VALIDATION_ERROR, "잘못된 요청 형식입니다."); + } + + //이부분추가 + + + + + } diff --git a/src/main/java/org/kau/kkoolbeeServer/global/config/S3Config.java b/src/main/java/org/kau/kkoolbeeServer/global/config/S3Config.java new file mode 100644 index 0000000..5c6efe3 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/S3Config.java @@ -0,0 +1,33 @@ +package org.kau.kkoolbeeServer.global.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan +public class S3Config { + @Value("${cloud.aws.credentials.accessKey}") + private String accessKey; + + @Value("${cloud.aws.credentials.secretKey}") + private String secretKey; + + @Value("${cloud.aws.region}") + private String region; + + @Bean + public AmazonS3 amazonS3Client() { + BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey); + return AmazonS3ClientBuilder.standard() + .withRegion(Regions.fromName(region)) + .withCredentials(new AWSStaticCredentialsProvider(awsCreds)) + .build(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 336faf6..848a7d5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,13 +2,15 @@ spring: profiles: active: local + cloud: aws: credentials: accessKey: ${ACCESS_KEY} secretKey: ${SECRET_KEY} - aws-region: ap-northeast-2 - s3-bucket-name: ${BUCKET_NAME} + region: ap-northeast-2 + s3: + bucket-name: ${BUCKET_NAME} jwt: secret: ${JWT_SECRET} diff --git a/src/test/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryControllerTest.java b/src/test/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryControllerTest.java new file mode 100644 index 0000000..c8d7f59 --- /dev/null +++ b/src/test/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryControllerTest.java @@ -0,0 +1,99 @@ +/* +package org.kau.kkoolbeeServer.domain.diary.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.kau.kkoolbeeServer.domain.advice.Advice; +import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; +import org.kau.kkoolbeeServer.domain.diary.Diary; +import org.kau.kkoolbeeServer.domain.diary.Feeling; +import org.kau.kkoolbeeServer.domain.diary.dto.request.CurrentDateRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.request.DiaryContentRequestDto; + +import org.kau.kkoolbeeServer.domain.diary.dto.response.DiaryContentResponseDto; +import org.kau.kkoolbeeServer.domain.diary.service.DiaryService; +import org.kau.kkoolbeeServer.global.common.dto.ApiResponse; +import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@ExtendWith(SpringExtension.class) +@WebMvcTest(DiaryController.class) +public class DiaryControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private DiaryService diaryService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + public void testGetDiaryContents() throws Exception { + // Given + DiaryContentRequestDto requestDto = new DiaryContentRequestDto(); + requestDto.setDiaryId(1L); + + Advice advice = new Advice(); // Advice 객체 생성 + advice.setSpicy_advice("spicy_advice"); + advice.setKind_advice("kind_advice"); + Diary diary = new Diary(); // Diary 객체 생성 + diary.setContent("안뇽안뇽안뇽안뇽"); + diary.setFeeling(Feeling.ANGRY); + diary.setAdvice(advice); // Diary 객체에 Advice 설정 + + + AdviceResponseDto adviceResponseDto = new AdviceResponseDto("spicy_advice", "kind_advice"); + DiaryContentResponseDto responseDto = new DiaryContentResponseDto(diary.getContent(), adviceResponseDto,diary.getFeeling().toString()); + given(diaryService.findDiaryById(any(Long.class))).willReturn(Optional.of(diary)); + + // When & Then + mockMvc.perform(post("/api/diary/content") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isOk()) + .andExpect(content().json(objectMapper.writeValueAsString(ApiResponse.success(SuccessType.PROCESS_SUCCESS, responseDto)))) + .andDo(print()); + + } + + */ +/*@Test + public void getDiariesByMonth_ReturnsDiaryList() throws Exception { + given(diaryService.findDiariesByMonth(LocalDateTime.of(2024, 1, 7, 0, 0))).willReturn(diaryList); + Diary diary1 = new Diary(1L, "Title1", "Content1", LocalDateTime.of(2024, 1, 7, 0, 0)); + Diary diary2 = new Diary(2L, "Title2", "Content2", LocalDateTime.of(2024, 1, 8, 0, 0)); + Diary diary3 = new Diary(3L, "Title3", "Content3", LocalDateTime.of(2024, 1, 9, 0, 0)); + + diaryList = Arrays.asList(diary1, diary2, diary3); + mockMvc.perform(post("/api/diary/list/calendar") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new CurrentDateRequestDto(LocalDateTime.of(2024, 1, 7, 0, 0))))) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.status").value(200)) + .andExpect(jsonPath("$.message").value("요청이 성공했습니다.")) + .andExpect(jsonPath("$.data.monthList[0].diaryId").value(diaryList.get(0).getId())) + .andExpect(jsonPath("$.data.monthList[1].diaryTitle").value(diaryList.get(1).getTitle())) + .andExpect(jsonPath("$.data.monthList[2].createdDate").value(diaryList.get(2).getCreatedAt().toString())); + }*//* + +} +*/ From a776f79f845cc20013500b27c24987c2bcb124ed Mon Sep 17 00:00:00 2001 From: Wang HoEun Date: Tue, 30 Apr 2024 13:31:34 +0900 Subject: [PATCH 14/43] =?UTF-8?q?fix:=20bootstarp=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node_modules/.yarn-integrity | 10 ++++++++++ yarn.lock | 4 ++++ 2 files changed, 14 insertions(+) create mode 100644 node_modules/.yarn-integrity create mode 100644 yarn.lock diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 0000000..56c28ed --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "win32-x64-108", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + From 4d614a2644b7d1ea1f066744bbdc77898fd0fe46 Mon Sep 17 00:00:00 2001 From: Wang Hoeun <38005874+hoeun0723@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:32:04 +0900 Subject: [PATCH 15/43] =?UTF-8?q?refactor:=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=20=EC=9D=B8=EA=B0=80=EC=BD=94=EB=93=9C=20=ED=94=84=EB=A1=A0?= =?UTF-8?q?=ED=8A=B8=EC=B8=A1=EC=97=AB=EC=84=9C=20=EB=B0=9B=EC=95=84?= =?UTF-8?q?=EC=98=A4=EB=8A=94=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#36)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wang HoEun --- .../domain/member/controller/MemberController.java | 4 ++-- .../global/{common => }/config/JpaAuditingConfig.java | 2 +- .../kkoolbeeServer/global/{common => }/config/WebConfig.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/org/kau/kkoolbeeServer/global/{common => }/config/JpaAuditingConfig.java (79%) rename src/main/java/org/kau/kkoolbeeServer/global/{common => }/config/WebConfig.java (79%) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java index e3ffa37..8c2c58f 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java @@ -23,9 +23,9 @@ public class MemberController { @PostMapping("/login") public ResponseEntity> login( - @RequestHeader("Authorization") String socialAccessToken) { + @RequestHeader("Authorization") String code) { - return ResponseEntity.ok(ApiResponse.success(SuccessType.LOGIN_SUCCESS, memberService.login(socialAccessToken))); + return ResponseEntity.ok(ApiResponse.success(SuccessType.LOGIN_SUCCESS, memberService.login(kakaoLoginService.getKakaoAccessToken(code)))); } @GetMapping("/reissue") diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/config/JpaAuditingConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/JpaAuditingConfig.java similarity index 79% rename from src/main/java/org/kau/kkoolbeeServer/global/common/config/JpaAuditingConfig.java rename to src/main/java/org/kau/kkoolbeeServer/global/config/JpaAuditingConfig.java index a072121..dac25a7 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/config/JpaAuditingConfig.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/JpaAuditingConfig.java @@ -1,4 +1,4 @@ -package org.kau.kkoolbeeServer.global.common.config; +package org.kau.kkoolbeeServer.global.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/config/WebConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/WebConfig.java similarity index 79% rename from src/main/java/org/kau/kkoolbeeServer/global/common/config/WebConfig.java rename to src/main/java/org/kau/kkoolbeeServer/global/config/WebConfig.java index aa2dca5..6bc0a81 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/config/WebConfig.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/WebConfig.java @@ -1,4 +1,4 @@ -package org.kau.kkoolbeeServer.global.common.config; +package org.kau.kkoolbeeServer.global.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; @@ -10,7 +10,7 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOrigins("http://localhost:8080", "http://localhost:8081", "http://localhost:5173") + .allowedOrigins("http://localhost:8080", "http://localhost:8081", "http://localhost:5173","https://www.honeyary-server.o-r.kr","https://honeyary.vercel.app/") .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE") .allowCredentials(true) .maxAge(3000); From 6364cfb0e5b512760bed687fc799e0f8faf75b04 Mon Sep 17 00:00:00 2001 From: Wang Hoeun <38005874+hoeun0723@users.noreply.github.com> Date: Thu, 2 May 2024 21:50:42 +0900 Subject: [PATCH 16/43] =?UTF-8?q?[refactor]=20kakao=20login=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20=ED=94=84=EB=A1=A0=ED=8A=B8=EA=B0=80=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#38)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 카카오 인가코드 프론트측엫서 받아오는 방식으로 수정 * fix: 다시 login 되돌려두기 --------- Co-authored-by: Wang HoEun From d2b6f8c818ff5668549f7c358a156fa34290e3ed Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Sat, 4 May 2024 17:59:49 +0900 Subject: [PATCH 17/43] Refactor/#40 slow type diary retry (#41) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 수정전 commit * Principal객체사용한 memberId기반 api전체 수정 * refactor : slowtype api principal제외 --- .../diary/controller/DiaryController.java | 99 ++++++++++++++++--- .../diary/repository/DiaryRepository.java | 7 +- .../domain/diary/service/DiaryService.java | 19 +++- .../kkoolbeeServer/domain/member/Member.java | 5 + .../member/controller/MemberController.java | 19 +++- .../domain/member/service/MemberService.java | 30 ++++++ .../global/auth/jwt/JwtProvider.java | 25 ++++- 7 files changed, 184 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 68d7c70..808b3fe 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -12,6 +12,10 @@ import org.kau.kkoolbeeServer.domain.diary.dto.response.FeelingListResponseDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.SlowTypeCreateResponseDto; import org.kau.kkoolbeeServer.domain.diary.service.DiaryService; +import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.domain.member.service.MemberService; + +import org.kau.kkoolbeeServer.global.auth.jwt.JwtProvider; import org.kau.kkoolbeeServer.global.common.dto.ApiResponse; import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; @@ -20,13 +24,11 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.security.Principal; import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -39,10 +41,14 @@ public class DiaryController { private DiaryService diaryService; private final S3UploaderService s3UploaderService; + private MemberService memberService; + private JwtProvider jwtProvider; @Autowired - public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderService) { + public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderService,MemberService memberService,JwtProvider jwtProvider) { this.diaryService = diaryService; this.s3UploaderService=s3UploaderService; + this.memberService=memberService; + this.jwtProvider=jwtProvider; } @@ -64,7 +70,6 @@ public ResponseEntity> getDiaryContents(@RequestBody DiaryContent ); - DiaryContentResponseDto responseDto = new DiaryContentResponseDto( diary.getContent(), adviceResponseDto, @@ -81,12 +86,32 @@ public ResponseEntity> getDiaryContents(@RequestBody DiaryContent } } - @PostMapping("/api/diary/list/calendar") + /* @PostMapping("/api/diary/list/calendar") public ResponseEntity> getDiariesByMonth(@RequestBody CurrentDateRequestDto requestDto){ LocalDateTime currentDate = requestDto.getCurrentDate(); - Listdiaries=diaryService.findDiariesByMonth(currentDate); - if(diaries.isEmpty()){ + + Listdiaries=diaryService.findDiariesByMonth(currentDate); + if(diaries.isEmpty()){ + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 월에 대한 일기가 존재하지 않습니다.")); + } + + List diaryDtos=diaries.stream() + .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt())) + .collect(Collectors.toList()); + + Map> responseMap= Map.of("monthList",diaryDtos); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap));*/ + + @PostMapping("/api/diary/list/calendar") + public ResponseEntity> getDiariesByMonth(Principal principal,@RequestBody CurrentDateRequestDto requestDto){ + Long memberId= JwtProvider.getUserFromPrincipal(principal); + LocalDateTime currentDate = requestDto.getCurrentDate(); + + List diaries = diaryService.findDiariesByMonthAndMemberId(currentDate, memberId); + + if(diaries.isEmpty()){ return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 월에 대한 일기가 존재하지 않습니다.")); } @@ -103,8 +128,8 @@ public ResponseEntity> getDiariesByMonth(@RequestBody CurrentDate } - @PostMapping("/api/diary/list/feeling") - public ResponseEntity> getDiariesByFeeling(@RequestBody FeelingListRequestDto requestDto) { + /*@PostMapping("/api/diary/list/feeling") + public ResponseEntity> getDiariesByFeeling( @RequestBody FeelingListRequestDto requestDto) { List diaries = diaryService.findDiariesByFeeling(requestDto.getFeeling()); @@ -121,9 +146,29 @@ public ResponseEntity> getDiariesByFeeling(@RequestBody FeelingLi Map> responseMap = Map.of("feelingList", feelingList); return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); + }*/ + + @PostMapping("/api/diary/list/feeling") + public ResponseEntity> getDiariesByFeeling( Principal principal,@RequestBody FeelingListRequestDto requestDto) { + + Long memberId=JwtProvider.getUserFromPrincipal(principal); + List diaries = diaryService.findDiariesByMemberIdAndFeeling(memberId,requestDto.getFeeling()); + + if (diaries.isEmpty()) { + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 감정에 대한 일기가 존재하지 않습니다.")); + } + + List feelingList = diaries.stream() + .map(diary -> new FeelingListResponseDto(diary.getId(), diary.getWritedAt(), diary.getTitle())) + .collect(Collectors.toList()); + + Map> responseMap = Map.of("feelingList", feelingList); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); + } - @PostMapping("/api/diary/create/slow") + /* @PostMapping("/api/diary/create/slow") public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl")MultipartFile image, @RequestPart("diaryTitle") String diaryTitle, @RequestPart("diaryContent") String diaryContent){ @@ -142,6 +187,36 @@ public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,"서버 내부 오류")); } + }*/ + @PostMapping("/api/diary/create/slow") + public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageurl")MultipartFile image, + @RequestPart(value = "diaryTitle") String diaryTitle, + @RequestPart(value = "diaryContent") String diaryContent){ + + try { + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } + String imageUrl = s3UploaderService.upload(image); + Long memberId= jwtProvider.getUserFromJwt(accessToken); + Member member= memberService.findByIdOrThrow(memberId); + Diary diary = new Diary(); + diary.setTitle(diaryTitle); + diary.setMember(member); + diary.setContent(diaryContent); + diary.setImageurl(imageUrl); + + Diary savedDiary=diaryService.saveDiary(diary); + SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl()); + + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + } catch (Exception e) { + e.printStackTrace(); + + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); + } } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java index 9ac3087..aa6a1cb 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/repository/DiaryRepository.java @@ -11,8 +11,11 @@ @Repository public interface DiaryRepository extends JpaRepository { - List findByWritedAtBetween(LocalDateTime startDateTime, LocalDateTime endDateTime); + /*List findByWritedAtBetween(LocalDateTime startDateTime, LocalDateTime endDateTime);*/ + List findByMemberIdAndWritedAtBetween(Long memberId, LocalDateTime startDate, LocalDateTime endDate); - List findByFeeling(Feeling feeling); + /*List findByFeeling(Feeling feeling);*/ + + List findByMemberIdAndFeeling(Long memberId,Feeling feeling); } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 9726645..ccd76da 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -5,6 +5,7 @@ import org.kau.kkoolbeeServer.domain.diary.repository.DiaryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.swing.text.html.Option; import java.time.LocalDateTime; @@ -27,16 +28,28 @@ public Optional findDiaryById(Long diary_id){ } - public List findDiariesByMonth(LocalDateTime date) { + /* public List findDiariesByMonth(LocalDateTime date) { LocalDateTime startOfMonth = date.withDayOfMonth(1).toLocalDate().atStartOfDay(); LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); return diaryRepository.findByWritedAtBetween(startOfMonth, endOfMonth); + }*/ + + public List findDiariesByMonthAndMemberId(LocalDateTime date, Long memberId) { + LocalDateTime startOfMonth = date.withDayOfMonth(1).toLocalDate().atStartOfDay(); + LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); + return diaryRepository.findByMemberIdAndWritedAtBetween(memberId, startOfMonth, endOfMonth); } - public List findDiariesByFeeling(String feeling) { + + + /* public List findDiariesByFeeling(String feeling) { return diaryRepository.findByFeeling(Feeling.valueOf(feeling)); } - +*/ + public List findDiariesByMemberIdAndFeeling(Long memberId,String feeling) { + return diaryRepository.findByMemberIdAndFeeling(memberId,Feeling.valueOf(feeling)); + } + @Transactional public Diary saveDiary(Diary diary){ return diaryRepository.save(diary); } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java index 60275ce..8164106 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/Member.java @@ -54,4 +54,9 @@ public Member(String kakaoId) { public static Member of(String kakaoId) { return new Member(kakaoId); } + + + public void setDiaryType(UserDiaryType userDiaryType) { + this.userDiaryType = userDiaryType; + } } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java index e3ffa37..9062b84 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java @@ -1,6 +1,7 @@ package org.kau.kkoolbeeServer.domain.member.controller; import lombok.RequiredArgsConstructor; +import org.kau.kkoolbeeServer.domain.member.UserDiaryType; import org.kau.kkoolbeeServer.domain.member.dto.response.MemberLoginResponseDto; import org.kau.kkoolbeeServer.domain.member.service.MemberService; import org.kau.kkoolbeeServer.global.auth.fegin.kakao.KakaoLoginService; @@ -20,6 +21,7 @@ public class MemberController { private final MemberService memberService; private final KakaoLoginService kakaoLoginService; + private final JwtProvider jwtProvider; @PostMapping("/login") public ResponseEntity> login( @@ -38,7 +40,7 @@ public ResponseEntity> reissue( @PatchMapping("/log-out") // Spring Security 자체의 logout과 겹치지 않기 위해 이렇게 설정 public ResponseEntity> logout(Principal principal) { - memberService.logout(JwtProvider.getUserFromPrincial(principal)); + memberService.logout(JwtProvider.getUserFromPrincipal(principal)); return ResponseEntity.ok(ApiResponse.success(SuccessType.LOGOUT_SUCCESS)); } @@ -47,4 +49,19 @@ public ResponseEntity> kakaoAccessToken( @RequestHeader("Authorization") String code) { return ResponseEntity.ok(ApiResponse.success(SuccessType.KAKAO_ACCESS_TOKEN_SUCCESS, kakaoLoginService.getKakaoAccessToken(code))); } + + @PostMapping("/member/character") + public ResponseEntity> diaryType(Principal principal,@RequestBody String userDiaryType){ + + Long memberId=JwtProvider.getUserFromPrincipal(principal); + + UserDiaryType diaryType=UserDiaryType.valueOf(userDiaryType); + memberService.setUserDiaryType(memberId,diaryType); + + return ResponseEntity.ok(ApiResponse.success(SuccessType.PROCESS_SUCCESSED)); + + + } + + } \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java index e5973e4..43422a1 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.kau.kkoolbeeServer.domain.member.Member; +import org.kau.kkoolbeeServer.domain.member.UserDiaryType; import org.kau.kkoolbeeServer.domain.member.dto.response.MemberLoginResponseDto; import org.kau.kkoolbeeServer.domain.member.repository.MemberRepository; import org.kau.kkoolbeeServer.global.auth.fegin.kakao.KakaoLoginService; @@ -90,4 +91,33 @@ private Member getUserBySocialAndSocialId(String kakaoId) { private boolean isUserByKakaoId(String kakaoId) { return memberRepository.existsByKakaoId(kakaoId); } + + @Transactional + public void setUserDiaryType(Long memberId, UserDiaryType userDiaryType){ + + Member member=memberRepository.findByIdOrThrow(memberId); + member.setDiaryType(userDiaryType); + memberRepository.save(member); + + } + + @Transactional(readOnly = true) + public Member findByIdOrThrow(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new CustomException(NOT_FOUND_MEMBER_ERROR)); + } + + + /* @Transactional + public void setUserDiaryType(String Token, String userDiaryType){ + + Token=parseTokenString(Token); + UserDiaryType DiaryType=UserDiaryType.valueOf(userDiaryType); + + jwtProvider.getUserFromJwt() + + + + + }*/ } \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java b/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java index 1cd503a..b4186f8 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/auth/jwt/JwtProvider.java @@ -9,6 +9,8 @@ import org.kau.kkoolbeeServer.global.auth.redis.RefreshToken; import org.kau.kkoolbeeServer.global.auth.redis.TokenRepository; import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; @@ -26,6 +28,8 @@ @Component @RequiredArgsConstructor public class JwtProvider { + private static final Logger logger = LoggerFactory.getLogger(JwtProvider.class); + private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 1000L * 60 * 24 * 365; // 액세스 토큰 만료 시간: 1년으로 지정 private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 1000L * 2 * 60 * 24 * 365; // 리프레시 토큰 만료 시간: 2년으로 지정 @@ -136,9 +140,26 @@ public void deleteRefreshToken(Long memberId) { } // 토큰에 담겨있는 memberId 획득 - public Long getUserFromJwt(String token) { + /*public Long getUserFromJwt(String token) { Claims claims = getBody(token); return Long.parseLong(claims.get("memberId").toString()); + }*/ + + public Long getUserFromJwt(String token) { + try { + logger.info("Token 분석 시작: {}", token); + Claims claims = getBody(token); + logger.info("Claims 추출 성공"); + + + + String memberIdStr = claims.get("memberId").toString(); + logger.info("memberId 추출 성공: {}", memberIdStr); + return Long.parseLong(memberIdStr); + } catch (Exception e) { + logger.error("getUserFromJwt 메소드 에러", e); + throw new RuntimeException("JWT 토큰 분석 중 오류 발생", e); + } } private Claims getBody(final String token) { @@ -155,7 +176,7 @@ private SecretKey getSigningKey() { return Keys.hmacShaKeyFor(encodedKey.getBytes()); } - public static Long getUserFromPrincial(Principal principal) { + public static Long getUserFromPrincipal(Principal principal) { if (isNull(principal)) { throw new CustomException(EMPTY_PRINCIPLE_ERROR); } From 8debb906caff4ae80b863f44bec2a734d9c9ce31 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Sat, 4 May 2024 21:24:39 +0900 Subject: [PATCH 18/43] Refactor/#40 slow type diary retry (#44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 수정전 commit * Principal객체사용한 memberId기반 api전체 수정 * refactor : slowtype api principal제외 * refactor:오류수정을 위한 임시파일경로생성 * Update DiaryController.java 오타해결 * refactor 충돌해결 --- .../kkoolbeeServer/S3/S3UploaderService.java | 3 +- .../diary/controller/DiaryController.java | 65 ++++++++++++------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java index 64be6b9..ff70008 100644 --- a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java +++ b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java @@ -36,7 +36,8 @@ public String upload(MultipartFile file) throws IOException { } private File convertMultiPartToFile(MultipartFile file) throws IOException { - File convertedFile = new File(file.getOriginalFilename()); + File tempDir = new File(System.getProperty("java.io.tmpdir")); + File convertedFile = new File(tempDir,file.getOriginalFilename()); try (FileOutputStream fos = new FileOutputStream(convertedFile)) { fos.write(file.getBytes()); } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 808b3fe..a371f0d 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -55,19 +55,19 @@ public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderSer @PostMapping("/api/diary/content") public ResponseEntity> getDiaryContents(@RequestBody DiaryContentRequestDto diaryContentRequestDto) { - Long diaryId = diaryContentRequestDto.getDiaryId(); + Long diaryId = diaryContentRequestDto.getDiaryId(); - Optional diaryOptional = diaryService.findDiaryById(diaryId); + Optional diaryOptional = diaryService.findDiaryById(diaryId); - if (diaryOptional.isPresent()) { + if (diaryOptional.isPresent()) { - Diary diary = diaryOptional.get(); - // Diary의 Advice 정보를 AdviceResponseDto 객체로 변환 - AdviceResponseDto adviceResponseDto = new AdviceResponseDto( - diary.getAdvice().getSpicy_advice(), //여기서 null이 나오면 ? - diary.getAdvice().getKind_advice() + Diary diary = diaryOptional.get(); + // Diary의 Advice 정보를 AdviceResponseDto 객체로 변환 + AdviceResponseDto adviceResponseDto = new AdviceResponseDto( + diary.getAdvice().getSpicy_advice(), //여기서 null이 나오면 ? + diary.getAdvice().getKind_advice() - ); + ); DiaryContentResponseDto responseDto = new DiaryContentResponseDto( @@ -78,12 +78,13 @@ public ResponseEntity> getDiaryContents(@RequestBody DiaryContent diary.getTitle() ); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseDto)); - } else { - return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR)); - //diary 가 null 일 경우 요청이상함 반환 - } + + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseDto)); + } else { + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR)); + //diary 가 null 일 경우 요청이상함 반환 + } } /* @PostMapping("/api/diary/list/calendar") @@ -104,6 +105,8 @@ public ResponseEntity> getDiariesByMonth(@RequestBody CurrentDate Map> responseMap= Map.of("monthList",diaryDtos); return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap));*/ + + @PostMapping("/api/diary/list/calendar") public ResponseEntity> getDiariesByMonth(Principal principal,@RequestBody CurrentDateRequestDto requestDto){ Long memberId= JwtProvider.getUserFromPrincipal(principal); @@ -112,16 +115,16 @@ public ResponseEntity> getDiariesByMonth(Principal principal,@Req List diaries = diaryService.findDiariesByMonthAndMemberId(currentDate, memberId); if(diaries.isEmpty()){ - return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 월에 대한 일기가 존재하지 않습니다.")); - } + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 월에 대한 일기가 존재하지 않습니다.")); + } - List diaryDtos=diaries.stream() - .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt())) - .collect(Collectors.toList()); + List diaryDtos=diaries.stream() + .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt())) + .collect(Collectors.toList()); - Map> responseMap= Map.of("monthList",diaryDtos); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); + Map> responseMap= Map.of("monthList",diaryDtos); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); @@ -168,15 +171,24 @@ public ResponseEntity> getDiariesByFeeling( Principal principal,@ } + /* @PostMapping("/api/diary/create/slow") public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl")MultipartFile image, @RequestPart("diaryTitle") String diaryTitle, @RequestPart("diaryContent") String diaryContent){ + try { + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } String imageUrl = s3UploaderService.upload(image); + Long memberId= jwtProvider.getUserFromJwt(accessToken); + Member member= memberService.findByIdOrThrow(memberId); Diary diary = new Diary(); diary.setTitle(diaryTitle); + diary.setMember(member); diary.setContent(diaryContent); diary.setImageurl(imageUrl); @@ -185,7 +197,10 @@ public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,"서버 내부 오류")); + e.printStackTrace(); + + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); } }*/ @PostMapping("/api/diary/create/slow") @@ -222,4 +237,4 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = -} \ No newline at end of file +} From 4fcf436eb45e9d93291c23d08fecc0d78d603eee Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Sun, 5 May 2024 22:02:51 +0900 Subject: [PATCH 19/43] Refactor/#46 choose diary type api retry (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Develop (#45) * chore: 환경변수 세팅 * chore: aws secret manager 설정 * [chore] CI/CD settings (#13) * chore: common 폴더 구조 변경 * chore: 환경변수 파일 분리 (local/dev) * feat: 서버 profile 확인용 api 추가 * chore: 무중단 배포시 실행할 스크립트 작성 * chore: Github Actions 스크립트 작성 (임시 trigger 설정) * chore: CI/CD trigger 재설정 * chore: 인덴트 오류 수정 * chore: CodeDeploy 옵션 문자열 오류 수정 * chore: Spring Security 및 Redis 임시 주석 처리 * chore: trigger 시점 develop 으로 변경 * chore: profile 조회 엔드포인트 수정 * style: CD 브랜치 trigger 시점 임시 변경 * fix: trigger 시점 develop 으로 원상 복귀 --------- Co-authored-by: Wang HoEun * feat/14-entity develop브랜치에 merge * Feat/#14 entity (#15) * 엔티티 첫 커밋 * diary엔티티에 멤버와의 관계추가 * feat : diary엔티티에 summary추가 * refactor: entity에 감정,조언 분리 * refactor:수정완료 * refactor: feeling클래스 감정추가 * feat: summary엔티티따로생성 후 member와 다대일양방향관계생성 * worried 스펠링수정 * feat: 카카오 로그인 구현+s3서버 세팅 (#31) * feat: 카카오 로그인 구현+s3서버 세팅 * fix: build gradle 수정 --------- Co-authored-by: Wang HoEun * fix: build.gradle 파일 최신화 * Feat/#30 slowtype save api (#32) * diary-content-api구현 및 테스트성공 * json필드 이름변경 * refactor:ApiResponse초기세팅 필드이름변경status * api명세서 변경으로인한 초기세팅 변경 * succes type에서 오타로 인한 커밋 * feat: list-calender api구현 but 테스트는 아직 * api테스트완료 but 시간대 문제발생 * 애플리케이션 timezone을 utc로 설정하여 임시해결 * refactor /#24 json필드명 오류발견후수정 * index on feat/#26-list-calender: b531010 refactor /#24 json필드명 오류발견후수정 * feat-feelinglist 1차구현 * try catch구문 삭제 및 exceptionhandler수정, apiresponse수정 * entity 수정 * feat:apiresponse수정 및 s3설정파일 임시구현 * feat:S3연결제외 api구현 * refactor: imageurl추가에따른 컨텐트 api 살짝수정 * refactor: diarycontent반환값에 title추가 * refactor: s3연결전까지 코든짠것 * success type 추가 * refactor : successtype변경 * develop pull 마무리 * refactor : application.yml이랑 value값맞추기성공 * feat: api구현 및 테스트 완료 * refactor:content,advice길이수정 * fix: bootstarp 파일이 없는 오류 해결 * refactor: 카카오 인가코드 프론트측엫서 받아오는 방식으로 수정 (#36) Co-authored-by: Wang HoEun * [refactor] kakao login 기존 프론트가 토큰 가져오는 방식으로 수정 (#38) * refactor: 카카오 인가코드 프론트측엫서 받아오는 방식으로 수정 * fix: 다시 login 되돌려두기 --------- Co-authored-by: Wang HoEun * Refactor/#40 slow type diary retry (#41) * 수정전 commit * Principal객체사용한 memberId기반 api전체 수정 * refactor : slowtype api principal제외 * Refactor/#40 slow type diary retry (#44) * 수정전 commit * Principal객체사용한 memberId기반 api전체 수정 * refactor : slowtype api principal제외 * refactor:오류수정을 위한 임시파일경로생성 * Update DiaryController.java 오타해결 * refactor 충돌해결 --------- Co-authored-by: Wang HoEun Co-authored-by: Wang Hoeun <38005874+hoeun0723@users.noreply.github.com> * requestHeader를 사용한 코드수정 및 diarytyperequestDto추가 --------- Co-authored-by: Wang HoEun Co-authored-by: Wang Hoeun <38005874+hoeun0723@users.noreply.github.com> --- .../member/controller/MemberController.java | 13 ++++++++++--- .../domain/member/dto/UserDiaryTypeRequest.java | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/member/dto/UserDiaryTypeRequest.java diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java index 9062b84..69ca5e3 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/controller/MemberController.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.kau.kkoolbeeServer.domain.member.UserDiaryType; +import org.kau.kkoolbeeServer.domain.member.dto.UserDiaryTypeRequest; import org.kau.kkoolbeeServer.domain.member.dto.response.MemberLoginResponseDto; import org.kau.kkoolbeeServer.domain.member.service.MemberService; import org.kau.kkoolbeeServer.global.auth.fegin.kakao.KakaoLoginService; @@ -51,11 +52,17 @@ public ResponseEntity> kakaoAccessToken( } @PostMapping("/member/character") - public ResponseEntity> diaryType(Principal principal,@RequestBody String userDiaryType){ + public ResponseEntity> diaryType(@RequestHeader(value = "Authorization") String authHeader, @RequestBody UserDiaryTypeRequest request){ - Long memberId=JwtProvider.getUserFromPrincipal(principal); + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } - UserDiaryType diaryType=UserDiaryType.valueOf(userDiaryType); + Long memberId= jwtProvider.getUserFromJwt(accessToken); + + + UserDiaryType diaryType=UserDiaryType.valueOf(request.getUserDiaryType()); memberService.setUserDiaryType(memberId,diaryType); return ResponseEntity.ok(ApiResponse.success(SuccessType.PROCESS_SUCCESSED)); diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/dto/UserDiaryTypeRequest.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/dto/UserDiaryTypeRequest.java new file mode 100644 index 0000000..f85440e --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/dto/UserDiaryTypeRequest.java @@ -0,0 +1,16 @@ +package org.kau.kkoolbeeServer.domain.member.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class UserDiaryTypeRequest { + + private String userDiaryType; + +} From 4f39a5e7ac6a383b0a5315fbacc046bc53d5c259 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Mon, 6 May 2024 23:01:39 +0900 Subject: [PATCH 20/43] =?UTF-8?q?feeling=EB=B3=84=20diary=20list=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20api=EC=88=98=EC=A0=95=20(#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/controller/DiaryController.java | 14 +++++++++++--- .../domain/diary/service/DiaryService.java | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 808b3fe..39147de 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -4,6 +4,7 @@ import org.kau.kkoolbeeServer.S3.S3UploaderService; import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; import org.kau.kkoolbeeServer.domain.diary.Diary; +import org.kau.kkoolbeeServer.domain.diary.Feeling; import org.kau.kkoolbeeServer.domain.diary.dto.request.FeelingListRequestDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.CalenderDiaryResponseDto; import org.kau.kkoolbeeServer.domain.diary.dto.request.CurrentDateRequestDto; @@ -149,10 +150,17 @@ public ResponseEntity> getDiariesByFeeling( @RequestBody FeelingL }*/ @PostMapping("/api/diary/list/feeling") - public ResponseEntity> getDiariesByFeeling( Principal principal,@RequestBody FeelingListRequestDto requestDto) { + public ResponseEntity> getDiariesByFeeling( @RequestHeader(value = "Authorization") String authHeader,@RequestBody FeelingListRequestDto requestDto) { - Long memberId=JwtProvider.getUserFromPrincipal(principal); - List diaries = diaryService.findDiariesByMemberIdAndFeeling(memberId,requestDto.getFeeling()); + + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } + + Long memberId= jwtProvider.getUserFromJwt(accessToken); + Feeling feeling=Feeling.valueOf(requestDto.getFeeling()); + List diaries = diaryService.findDiariesByMemberIdAndFeeling(memberId,feeling); if (diaries.isEmpty()) { return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index ccd76da..f40ed40 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -46,8 +46,8 @@ public List findDiariesByMonthAndMemberId(LocalDateTime date, Long member return diaryRepository.findByFeeling(Feeling.valueOf(feeling)); } */ - public List findDiariesByMemberIdAndFeeling(Long memberId,String feeling) { - return diaryRepository.findByMemberIdAndFeeling(memberId,Feeling.valueOf(feeling)); + public List findDiariesByMemberIdAndFeeling(Long memberId,Feeling feeling) { + return diaryRepository.findByMemberIdAndFeeling(memberId,feeling); } @Transactional public Diary saveDiary(Diary diary){ From c6cacf62f1dea947c7f8a6e964d2d31d6b80d117 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Tue, 7 May 2024 18:33:18 +0900 Subject: [PATCH 21/43] =?UTF-8?q?refactor:content=20api=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EC=88=98=EC=A0=95=EB=B0=8F=20responseDto=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/controller/DiaryController.java | 14 ++++++++++++-- .../dto/response/DiaryContentResponseDto.java | 7 ++++++- .../global/common/dto/enums/ErrorType.java | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 39147de..48ab4dc 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -20,6 +20,7 @@ import org.kau.kkoolbeeServer.global.common.dto.ApiResponse; import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; +import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -54,15 +55,22 @@ public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderSer } @PostMapping("/api/diary/content") - public ResponseEntity> getDiaryContents(@RequestBody DiaryContentRequestDto diaryContentRequestDto) { + public ResponseEntity> getDiaryContents(@RequestHeader(value = "Authorization") String authHeader,@RequestBody DiaryContentRequestDto diaryContentRequestDto) { - Long diaryId = diaryContentRequestDto.getDiaryId(); + + Long diaryId = diaryContentRequestDto.getDiaryId(); Optional diaryOptional = diaryService.findDiaryById(diaryId); + if (diaryOptional.isPresent()) { + Diary diary = diaryOptional.get(); + if(diary.getAdvice()==null){ + throw new CustomException(ErrorType.ADVICE_NOT_FOUND); + } + // Diary의 Advice 정보를 AdviceResponseDto 객체로 변환 AdviceResponseDto adviceResponseDto = new AdviceResponseDto( diary.getAdvice().getSpicy_advice(), //여기서 null이 나오면 ? @@ -72,6 +80,8 @@ public ResponseEntity> getDiaryContents(@RequestBody DiaryContent DiaryContentResponseDto responseDto = new DiaryContentResponseDto( + diary.getId(), + diary.getWritedAt(), diary.getContent(), adviceResponseDto, diary.getFeeling().toString(), diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java index fd0a8ef..a3a3a4f 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryContentResponseDto.java @@ -5,11 +5,16 @@ import lombok.AllArgsConstructor; import lombok.Getter; import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; -@JsonPropertyOrder({ "diary_content", "advice","feeling","imageUrl","diaryTitle"}) + +import java.time.LocalDateTime; + +@JsonPropertyOrder({ "diaryId","createdDate","diary_content", "advice","feeling","imageUrl","diaryTitle"}) @Getter @AllArgsConstructor public class DiaryContentResponseDto { + private Long diaryId; + private LocalDateTime createdDate; @JsonProperty("diary_content") private String diaryContent; private AdviceResponseDto advice; diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index 217d955..f768bb1 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -40,6 +40,7 @@ public enum ErrorType { * 404 NOT FOUND */ NOT_FOUND_MEMBER_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."), + ADVICE_NOT_FOUND(HttpStatus.NOT_FOUND, "요청하신 Diary에 맞는 Advice가 없습니다."), NOT_FOUND_REFRESH_TOKEN_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 리프레시 토큰입니다."), /** From cc50f3d48d548682d4cd3e8bb6af1234a6e2e3aa Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Thu, 9 May 2024 22:08:54 +0900 Subject: [PATCH 22/43] Refactor/#55 list calendar api retry (#56) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: calendar api코드수정 및 diary생성시 writedAt필드 설정코드추가 * refactor:writedAt필드 현재시간값가져오기로 수정 * calendarapi수정 --- .../diary/controller/DiaryController.java | 47 ++++++++++++------- src/main/resources/application-dev1.yml | 4 ++ src/main/resources/application-dev2.yml | 4 ++ src/main/resources/application-local.yml | 4 ++ src/main/resources/application.yml | 4 ++ 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 48ab4dc..08b1435 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.security.Principal; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.List; import java.util.Map; import java.util.Optional; @@ -116,8 +117,14 @@ public ResponseEntity> getDiariesByMonth(@RequestBody CurrentDate return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap));*/ @PostMapping("/api/diary/list/calendar") - public ResponseEntity> getDiariesByMonth(Principal principal,@RequestBody CurrentDateRequestDto requestDto){ - Long memberId= JwtProvider.getUserFromPrincipal(principal); + public ResponseEntity> getDiariesByMonth(@RequestHeader("Authorization") String authHeader,@RequestBody CurrentDateRequestDto requestDto){ + + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } + Long memberId= jwtProvider.getUserFromJwt(accessToken); + LocalDateTime currentDate = requestDto.getCurrentDate(); List diaries = diaryService.findDiariesByMonthAndMemberId(currentDate, memberId); @@ -168,6 +175,7 @@ public ResponseEntity> getDiariesByFeeling( @RequestHeader(value accessToken = authHeader.substring(7); } + Long memberId= jwtProvider.getUserFromJwt(accessToken); Feeling feeling=Feeling.valueOf(requestDto.getFeeling()); List diaries = diaryService.findDiariesByMemberIdAndFeeling(memberId,feeling); @@ -207,22 +215,25 @@ public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl } }*/ @PostMapping("/api/diary/create/slow") - public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageurl")MultipartFile image, - @RequestPart(value = "diaryTitle") String diaryTitle, - @RequestPart(value = "diaryContent") String diaryContent){ - - try { - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - } - String imageUrl = s3UploaderService.upload(image); - Long memberId= jwtProvider.getUserFromJwt(accessToken); - Member member= memberService.findByIdOrThrow(memberId); - Diary diary = new Diary(); - diary.setTitle(diaryTitle); - diary.setMember(member); - diary.setContent(diaryContent); + public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageurl")MultipartFile image, + @RequestPart(value = "diaryTitle") String diaryTitle, + @RequestPart(value = "diaryContent") String diaryContent){ + + try { + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } + String imageUrl = s3UploaderService.upload(image); + Long memberId= jwtProvider.getUserFromJwt(accessToken); + Member member= memberService.findByIdOrThrow(memberId); + Diary diary = new Diary(); + diary.setTitle(diaryTitle); + diary.setMember(member); + diary.setContent(diaryContent); + System.out.println(LocalDateTime.now()); + diary.setWritedAt(LocalDateTime.now()); //이부분추가 + System.out.println(diary.getWritedAt()); diary.setImageurl(imageUrl); Diary savedDiary=diaryService.saveDiary(diary); diff --git a/src/main/resources/application-dev1.yml b/src/main/resources/application-dev1.yml index 30d4567..5211249 100644 --- a/src/main/resources/application-dev1.yml +++ b/src/main/resources/application-dev1.yml @@ -2,6 +2,10 @@ spring: config: activate: on-profile: dev1 + servlet: + multipart: + max-file-size: 15MB + max-request-size: 20MB datasource: driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/src/main/resources/application-dev2.yml b/src/main/resources/application-dev2.yml index bc464df..e15634b 100644 --- a/src/main/resources/application-dev2.yml +++ b/src/main/resources/application-dev2.yml @@ -2,6 +2,10 @@ spring: config: activate: on-profile: dev2 + servlet: + multipart: + max-file-size: 15MB + max-request-size: 20MB datasource: driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index a026553..9795dce 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -3,6 +3,10 @@ spring: activate: on-profile: local import: optional:application-secret.properties + servlet: + multipart: + max-file-size: 15MB + max-request-size: 20MB datasource: driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 848a7d5..34f7983 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,10 @@ spring: profiles: active: local + servlet: + multipart: + max-file-size: 15MB + max-request-size: 20MB cloud: From e62b5508fa2f8cddab9c6c6fade4544bc3330f39 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Fri, 10 May 2024 00:35:24 +0900 Subject: [PATCH 23/43] =?UTF-8?q?refactor:content=20api=EC=97=90=EC=84=9C?= =?UTF-8?q?=20advice=EC=99=80=20feeling=EA=B0=92=20null=EA=B0=92=ED=97=88?= =?UTF-8?q?=EC=9A=A9=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/advice/dto/AdviceResponseDto.java | 9 +++++++++ .../diary/controller/DiaryController.java | 17 ++++------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java index 1f88183..f2fd3ee 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/advice/dto/AdviceResponseDto.java @@ -3,10 +3,19 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import org.kau.kkoolbeeServer.domain.advice.Advice; @Getter @AllArgsConstructor public class AdviceResponseDto { private String spicy; private String kind; + + public static AdviceResponseDto fromAdviceOrNull(Advice advice) { + if (advice == null) { + return new AdviceResponseDto(null, null); + } else { + return new AdviceResponseDto(advice.getSpicy_advice(), advice.getKind_advice()); + } + } //advice가 null이어도 응답메시지를 보내야하므로 } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 08b1435..8b6fe54 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -68,24 +68,15 @@ public ResponseEntity> getDiaryContents(@RequestHeader(value = "A Diary diary = diaryOptional.get(); - if(diary.getAdvice()==null){ - throw new CustomException(ErrorType.ADVICE_NOT_FOUND); - } - - // Diary의 Advice 정보를 AdviceResponseDto 객체로 변환 - AdviceResponseDto adviceResponseDto = new AdviceResponseDto( - diary.getAdvice().getSpicy_advice(), //여기서 null이 나오면 ? - diary.getAdvice().getKind_advice() - - ); - - + AdviceResponseDto adviceResponseDto=AdviceResponseDto.fromAdviceOrNull(diary.getAdvice()); + //feeling이 null인경우 대비 + String feeling = diary.getFeeling() != null ? diary.getFeeling().toString() : null; DiaryContentResponseDto responseDto = new DiaryContentResponseDto( diary.getId(), diary.getWritedAt(), diary.getContent(), adviceResponseDto, - diary.getFeeling().toString(), + feeling, diary.getImageurl(), diary.getTitle() ); From b2fb07703e76c8f38ad560ad859dbef333a0ae4f Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Fri, 10 May 2024 01:13:00 +0900 Subject: [PATCH 24/43] Refactor/#58 diary content api exception retry (#61) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor:content api에서 advice와 feeling값 null값허용 * diary엔티티 content칼럼 Longtext로수정및 slowtype api 이미지파일없어도가능하게 --- .../org/kau/kkoolbeeServer/domain/advice/Advice.java | 4 ++-- .../org/kau/kkoolbeeServer/domain/diary/Diary.java | 2 +- .../domain/diary/controller/DiaryController.java | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java index 3f71714..403114c 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/advice/Advice.java @@ -15,10 +15,10 @@ public class Advice { @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; - @Column(columnDefinition = "TEXT") + @Column(columnDefinition = "LONGTEXT") String kind_advice; - @Column(columnDefinition = "TEXT") + @Column(columnDefinition = "LONGTEXT") String spicy_advice; public Advice(String kind_advice, String spicy_advice) { diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java index 8727f9e..73f3b8e 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -36,7 +36,7 @@ public class Diary extends BaseTimeEntity { private Feeling feeling; - @Column(nullable = false,columnDefinition = "TEXT") + @Column(nullable = false,columnDefinition = "LONGTEXT") private String content; diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 8b6fe54..d966c7b 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -215,7 +215,13 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = if (authHeader != null && authHeader.startsWith("Bearer ")) { accessToken = authHeader.substring(7); } - String imageUrl = s3UploaderService.upload(image); + String imageUrl=null; + + if(image!=null && !image.isEmpty() ){ + imageUrl = s3UploaderService.upload(image); + + } + Long memberId= jwtProvider.getUserFromJwt(accessToken); Member member= memberService.findByIdOrThrow(memberId); Diary diary = new Diary(); @@ -225,7 +231,7 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = System.out.println(LocalDateTime.now()); diary.setWritedAt(LocalDateTime.now()); //이부분추가 System.out.println(diary.getWritedAt()); - diary.setImageurl(imageUrl); + diary.setImageurl(imageUrl); Diary savedDiary=diaryService.saveDiary(diary); SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl()); From 95f2c166cf59546d44cfa3b11ad8160336ad7c93 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Sun, 12 May 2024 00:29:34 +0900 Subject: [PATCH 25/43] =?UTF-8?q?FeelingListResponseDto=EC=9D=98=20imageUr?= =?UTF-8?q?l=ED=95=84=EB=93=9C=EC=B6=94=EA=B0=80=20(#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkoolbeeServer/domain/diary/controller/DiaryController.java | 2 +- .../domain/diary/dto/response/FeelingListResponseDto.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 681fe81..a936fa8 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -177,7 +177,7 @@ public ResponseEntity> getDiariesByFeeling( @RequestHeader(value } List feelingList = diaries.stream() - .map(diary -> new FeelingListResponseDto(diary.getId(), diary.getWritedAt(), diary.getTitle())) + .map(diary -> new FeelingListResponseDto(diary.getId(), diary.getWritedAt(), diary.getTitle(), diary.getImageurl())) .collect(Collectors.toList()); Map> responseMap = Map.of("feelingList", feelingList); diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java index fde0311..ae985ac 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/FeelingListResponseDto.java @@ -13,5 +13,6 @@ public class FeelingListResponseDto { private Long diaryId; private LocalDateTime createdDate; private String diaryTitle; + private String imageUrl; } From 564a19b5192f8460806b80a4b0dad3976c716456 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Sun, 12 May 2024 00:45:40 +0900 Subject: [PATCH 26/43] =?UTF-8?q?CalendarDiaryResponseDto=EC=9D=98=20Image?= =?UTF-8?q?Url=ED=95=84=EB=93=9C=EC=B6=94=EA=B0=80=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkoolbeeServer/domain/diary/controller/DiaryController.java | 2 +- .../domain/diary/dto/response/CalenderDiaryResponseDto.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index a936fa8..172fdf4 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -126,7 +126,7 @@ public ResponseEntity> getDiariesByMonth(@RequestHeader("Authoriz } List diaryDtos=diaries.stream() - .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt())) + .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt(), diary.getImageurl())) .collect(Collectors.toList()); Map> responseMap= Map.of("monthList",diaryDtos); diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java index b79d984..8b0419f 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/CalenderDiaryResponseDto.java @@ -13,6 +13,7 @@ public class CalenderDiaryResponseDto { private Long diaryId; private String diaryTitle; private LocalDateTime createdDate; + private String imageUrl; } From 9b528dd4268aebf2f34253eecbc745dfc99a4ad4 Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Sun, 12 May 2024 21:40:16 +0900 Subject: [PATCH 27/43] =?UTF-8?q?feeling=20list,slowtype=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20request=EC=98=A4=EB=A5=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/controller/DiaryController.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 172fdf4..0d4c4a0 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -55,6 +55,7 @@ public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderSer } + @PostMapping("/api/diary/content") public ResponseEntity> getDiaryContents(@RequestHeader(value = "Authorization") String authHeader,@RequestBody DiaryContentRequestDto diaryContentRequestDto) { @@ -165,6 +166,10 @@ public ResponseEntity> getDiariesByFeeling( @RequestHeader(value if (authHeader != null && authHeader.startsWith("Bearer ")) { accessToken = authHeader.substring(7); } + else{ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + + } Long memberId= jwtProvider.getUserFromJwt(accessToken); @@ -215,6 +220,10 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = if (authHeader != null && authHeader.startsWith("Bearer ")) { accessToken = authHeader.substring(7); } + else{ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + + } String imageUrl=null; From 3e479e1ce363453797f54fde00a7f0692a41bc18 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Mon, 13 May 2024 11:24:54 +0900 Subject: [PATCH 28/43] =?UTF-8?q?refactor:ZonedDatetime=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/controller/DiaryController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 0d4c4a0..b58258b 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -33,6 +33,7 @@ import java.security.Principal; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.List; import java.util.Map; import java.util.Optional; @@ -239,9 +240,9 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = diary.setTitle(diaryTitle); diary.setMember(member); diary.setContent(diaryContent); - System.out.println(LocalDateTime.now()); - diary.setWritedAt(LocalDateTime.now()); //이부분추가 - System.out.println(diary.getWritedAt()); + ZonedDateTime kstNow = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + LocalDateTime now = kstNow.toLocalDateTime(); + diary.setWritedAt(now); diary.setImageurl(imageUrl); From eba831e13fe185cb2818ab90a23a5903db921b98 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Tue, 14 May 2024 21:59:02 +0900 Subject: [PATCH 29/43] =?UTF-8?q?refactor:=20imageFile=20=EC=95=88?= =?UTF-8?q?=EC=A4=98=EB=8F=84=20=EA=B0=80=EB=8A=A5=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=88=98=EC=A0=95=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkoolbeeServer/domain/diary/controller/DiaryController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 05db78f..17f6d64 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -212,7 +212,7 @@ public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl } }*/ @PostMapping("/api/diary/create/slow") - public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageurl")MultipartFile image, + public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageurl",required = false)MultipartFile image, @RequestPart(value = "diaryTitle") String diaryTitle, @RequestPart(value = "diaryContent") String diaryContent){ From 93f26f5a9ba1d8e8d5609966bf7a1a0e80a86071 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 15 May 2024 00:42:39 +0900 Subject: [PATCH 30/43] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95=20api=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../diary/controller/DiaryController.java | 59 +++++++++++++++++-- .../dto/response/UpdateDiaryResponseDto.java | 15 +++++ .../domain/diary/service/DiaryService.java | 52 ++++++++++++++++ .../global/common/dto/enums/ErrorType.java | 1 + 4 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/UpdateDiaryResponseDto.java diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 17f6d64..b129687 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -6,12 +6,9 @@ import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; import org.kau.kkoolbeeServer.domain.diary.dto.request.FeelingListRequestDto; -import org.kau.kkoolbeeServer.domain.diary.dto.response.CalenderDiaryResponseDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.*; import org.kau.kkoolbeeServer.domain.diary.dto.request.CurrentDateRequestDto; import org.kau.kkoolbeeServer.domain.diary.dto.request.DiaryContentRequestDto; -import org.kau.kkoolbeeServer.domain.diary.dto.response.DiaryContentResponseDto; -import org.kau.kkoolbeeServer.domain.diary.dto.response.FeelingListResponseDto; -import org.kau.kkoolbeeServer.domain.diary.dto.response.SlowTypeCreateResponseDto; import org.kau.kkoolbeeServer.domain.diary.service.DiaryService; import org.kau.kkoolbeeServer.domain.member.Member; import org.kau.kkoolbeeServer.domain.member.service.MemberService; @@ -36,6 +33,7 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.Collectors; @@ -259,6 +257,59 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = } } + @PatchMapping("/api/diary/update") + public ResponseEntity updateDiary( + @RequestHeader(value = "Authorization") String authHeader, + @RequestPart(value = "imageUrl", required = false) MultipartFile imageFile, + @RequestPart(value="diaryId") Long diaryId, + @RequestPart(value = "diaryTitle") String diaryTitle, + @RequestPart(value = "diaryContent") String diaryContent) { + + + try{ + + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + Long memberID= jwtProvider.getUserFromJwt(accessToken); + Diary diary=diaryService.findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if(diary.getMember().getId()!=memberID){ + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); + } + } + else{ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + + } + String imageUrl=null; + if (imageFile!=null && !imageFile.isEmpty()){ + imageUrl=s3UploaderService.upload(imageFile); + + UpdateDiaryResponseDto responseDto=diaryService.updateDiary(diaryId,diaryContent,diaryTitle,imageUrl); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + + } + else{ + UpdateDiaryResponseDto responseDto=diaryService.updateDiaryWithoutImage(diaryId,diaryContent,diaryTitle); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + + + } + + + } + catch (Exception e){ + e.printStackTrace(); + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); + } + + + + + } + diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/UpdateDiaryResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/UpdateDiaryResponseDto.java new file mode 100644 index 0000000..d794b88 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/UpdateDiaryResponseDto.java @@ -0,0 +1,15 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class UpdateDiaryResponseDto { + private Long diaryId; + private String diaryContent; + private String diaryTitle; + private String imageurl; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index f40ed40..1f3195e 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -2,6 +2,7 @@ import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; +import org.kau.kkoolbeeServer.domain.diary.dto.response.UpdateDiaryResponseDto; import org.kau.kkoolbeeServer.domain.diary.repository.DiaryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -9,7 +10,10 @@ import javax.swing.text.html.Option; import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.List; +import java.util.NoSuchElementException; import java.util.Optional; @Service @@ -54,5 +58,53 @@ public Diary saveDiary(Diary diary){ return diaryRepository.save(diary); } + public UpdateDiaryResponseDto updateDiary(Long diaryId,String diaryContent,String diaryTitle,String imageUrl){ + Diary diary=findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + diary.setImageurl(imageUrl); + if(!diary.getContent().equals(diaryContent)){ + diary.setFeeling(null); + diary.setAdvice(null); + diary.setContent(diaryContent); + } + if(diary.getTitle()!=diaryTitle){ + diary.setTitle(diaryTitle); + } + ZonedDateTime kstNow = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + LocalDateTime now = kstNow.toLocalDateTime(); + diary.setWritedAt(now); + + + Diary savedDiary=diaryRepository.save(diary); + return new UpdateDiaryResponseDto(diaryId,savedDiary.getContent(),savedDiary.getTitle(), + savedDiary.getImageurl()); + + + + + } + + public UpdateDiaryResponseDto updateDiaryWithoutImage(Long diaryId,String diaryContent,String diaryTitle){ + Diary diary=findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if(!diary.getContent().equals(diaryContent)){ + diary.setFeeling(null); + diary.setAdvice(null); + diary.setContent(diaryContent); + } + if(diary.getTitle()!=diaryTitle){ + diary.setTitle(diaryTitle); + } + + ZonedDateTime kstNow = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + LocalDateTime now = kstNow.toLocalDateTime(); + diary.setWritedAt(now); + Diary savedDiary=diaryRepository.save(diary); + return new UpdateDiaryResponseDto(diaryId,savedDiary.getContent(),savedDiary.getTitle(), + savedDiary.getImageurl()); + + + + + } + } diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index f768bb1..be0aaf0 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -42,6 +42,7 @@ public enum ErrorType { NOT_FOUND_MEMBER_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."), ADVICE_NOT_FOUND(HttpStatus.NOT_FOUND, "요청하신 Diary에 맞는 Advice가 없습니다."), NOT_FOUND_REFRESH_TOKEN_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 리프레시 토큰입니다."), + NOT_YOUR_DIARY(HttpStatus.NOT_FOUND,"해당 Diary에 접근할 수 있는 권한이 없습니다."), /** * 500 INTERNAL SERVER ERROR From 6f917c069e145adc0930af366c6f73ebc4e4dcbf Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 15 May 2024 02:09:07 +0900 Subject: [PATCH 31/43] =?UTF-8?q?refactor:=20logging=EC=B6=94=EA=B0=80=20(?= =?UTF-8?q?#80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/controller/DiaryController.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index b129687..2791e57 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -18,6 +18,8 @@ import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; import org.kau.kkoolbeeServer.global.common.dto.enums.SuccessType; import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -39,6 +41,7 @@ @RestController public class DiaryController { + private static final Logger logger = LoggerFactory.getLogger(DiaryController.class); private DiaryService diaryService; @@ -264,6 +267,11 @@ public ResponseEntity updateDiary( @RequestPart(value="diaryId") Long diaryId, @RequestPart(value = "diaryTitle") String diaryTitle, @RequestPart(value = "diaryContent") String diaryContent) { + logger.info("Authorization Header: {}", authHeader); + logger.info("Received diaryId: {}", diaryId); + logger.info("Received diaryTitle: {}", diaryTitle); + logger.info("Received diaryContent: {}", diaryContent); + try{ @@ -300,6 +308,7 @@ public ResponseEntity updateDiary( } catch (Exception e){ + logger.error("An error occurred while updating diary", e); e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); From c59d21fcc5ddccb9f60ce5fdbf5b60ff4504b13b Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 15 May 2024 21:32:07 +0900 Subject: [PATCH 32/43] =?UTF-8?q?refactor-requestpart=EB=8C=80=EC=8B=A0=20?= =?UTF-8?q?modelattribute=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkoolbeeServer/S3/S3UploaderService.java | 8 +++ .../diary/controller/DiaryController.java | 59 ++++++++++++++++++- .../dto/request/DiaryUpdateRequestDto.java | 18 ++++++ .../domain/diary/service/DiaryService.java | 9 ++- 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryUpdateRequestDto.java diff --git a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java index ff70008..75c4a79 100644 --- a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java +++ b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java @@ -43,4 +43,12 @@ private File convertMultiPartToFile(MultipartFile file) throws IOException { } return convertedFile; } + public void deleteFileFromS3(String fileUrl) { + // URL에서 파일 이름 추출 + String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1); + + // 파일 삭제 + amazonS3Client.deleteObject(bucketName, fileName); + + } } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 32b2920..7b6810b 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -5,6 +5,7 @@ import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; +import org.kau.kkoolbeeServer.domain.diary.dto.request.DiaryUpdateRequestDto; import org.kau.kkoolbeeServer.domain.diary.dto.request.FeelingListRequestDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.*; import org.kau.kkoolbeeServer.domain.diary.dto.request.CurrentDateRequestDto; @@ -260,7 +261,7 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = } } - @PatchMapping("/api/diary/update") +/* @PatchMapping("/api/diary/update") public ResponseEntity updateDiary( @RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageUrl", required = false) MultipartFile imageFile, @@ -319,9 +320,65 @@ public ResponseEntity updateDiary( + }*/ +@PatchMapping("/api/diary/update") +public ResponseEntity updateDiary( + @RequestHeader(value = "Authorization") String authHeader, + @ModelAttribute DiaryUpdateRequestDto requestDto ){ + Long diaryId= requestDto.getDiaryId(); + MultipartFile imageFile= requestDto.getImageUrl(); + String diaryTitle= requestDto.getDiaryTitle(); + String diaryContent= requestDto.getDiaryContent(); + + + + try{ + + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + Long memberID= jwtProvider.getUserFromJwt(accessToken); + Diary diary=diaryService.findDiaryById(requestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if(diary.getMember().getId()!=memberID){ + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); + } + } + else{ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + + } + String imageUrl=null; + if (imageFile!=null && !imageFile.isEmpty()){ + + imageUrl=s3UploaderService.upload(imageFile); + + UpdateDiaryResponseDto responseDto=diaryService.updateDiary(diaryId,diaryContent,diaryTitle,imageUrl); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + + } + else{ + UpdateDiaryResponseDto responseDto=diaryService.updateDiaryWithoutImage(diaryId,diaryContent,diaryTitle); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + + + } + + + } + catch (Exception e){ + logger.error("An error occurred while updating diary", e); + e.printStackTrace(); + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); } +} + + + + } \ No newline at end of file diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryUpdateRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryUpdateRequestDto.java new file mode 100644 index 0000000..1d8b68c --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryUpdateRequestDto.java @@ -0,0 +1,18 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.web.multipart.MultipartFile; + +@Getter +@Setter +@AllArgsConstructor +public class DiaryUpdateRequestDto { + private MultipartFile imageUrl; + private Long diaryId; + private String diaryTitle; + private String diaryContent; + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 1f3195e..8fb0be9 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -1,5 +1,6 @@ package org.kau.kkoolbeeServer.domain.diary.service; +import org.kau.kkoolbeeServer.S3.S3UploaderService; import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; import org.kau.kkoolbeeServer.domain.diary.dto.response.UpdateDiaryResponseDto; @@ -20,10 +21,12 @@ public class DiaryService { private DiaryRepository diaryRepository; + private S3UploaderService s3UploaderService; @Autowired - public DiaryService(DiaryRepository diaryRepository) { + public DiaryService(DiaryRepository diaryRepository,S3UploaderService s3UploaderService) { this.diaryRepository = diaryRepository; + this.s3UploaderService=s3UploaderService; } public Optional findDiaryById(Long diary_id){ @@ -60,6 +63,10 @@ public Diary saveDiary(Diary diary){ public UpdateDiaryResponseDto updateDiary(Long diaryId,String diaryContent,String diaryTitle,String imageUrl){ Diary diary=findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if(diary.getImageurl()!=null){ + s3UploaderService.deleteFileFromS3(diary.getImageurl()); + + } diary.setImageurl(imageUrl); if(!diary.getContent().equals(diaryContent)){ diary.setFeeling(null); From 97256df69b81997863d38f66d82fc9b70ca70099 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Thu, 16 May 2024 11:05:08 +0900 Subject: [PATCH 33/43] =?UTF-8?q?refactor:s3=ED=8C=8C=EC=9D=BC=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EC=B2=B4=ED=81=AC=ED=9B=84=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkoolbeeServer/S3/S3UploaderService.java | 31 +++++++++++++++++-- .../domain/diary/service/DiaryService.java | 4 +-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java index 75c4a79..937707e 100644 --- a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java +++ b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java @@ -3,6 +3,7 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.PutObjectRequest; +import org.apache.commons.codec.binary.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -11,6 +12,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.UUID; @Service @@ -25,7 +28,7 @@ public S3UploaderService(AmazonS3 amazonS3Client) { this.amazonS3Client = amazonS3Client; } - public String upload(MultipartFile file) throws IOException { + /* public String upload(MultipartFile file) throws IOException { String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename(); File convertedFile = convertMultiPartToFile(file); @@ -33,8 +36,30 @@ public String upload(MultipartFile file) throws IOException { convertedFile.delete(); // 임시 파일 삭제 return amazonS3Client.getUrl(bucketName, fileName).toString(); + }*/ + public String upload(MultipartFile file) throws IOException, NoSuchAlgorithmException { + String fileHash = calculateFileHash(file); + String fileName = fileHash + "-" + file.getOriginalFilename(); + + if (!amazonS3Client.doesObjectExist(bucketName, fileName)) { + File convertedFile = convertMultiPartToFile(file); + amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, convertedFile)); + convertedFile.delete(); + } + + return amazonS3Client.getUrl(bucketName, fileName).toString(); + } + + private String calculateFileHash(MultipartFile file) throws IOException, NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] fileBytes = file.getBytes(); + byte[] hash = digest.digest(fileBytes); + return Hex.encodeHexString(hash); } + + + private File convertMultiPartToFile(MultipartFile file) throws IOException { File tempDir = new File(System.getProperty("java.io.tmpdir")); File convertedFile = new File(tempDir,file.getOriginalFilename()); @@ -43,12 +68,12 @@ private File convertMultiPartToFile(MultipartFile file) throws IOException { } return convertedFile; } - public void deleteFileFromS3(String fileUrl) { + /*public void deleteFileFromS3(String fileUrl) { // URL에서 파일 이름 추출 String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1); // 파일 삭제 amazonS3Client.deleteObject(bucketName, fileName); - } + }*/ } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 8fb0be9..112418d 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -63,10 +63,10 @@ public Diary saveDiary(Diary diary){ public UpdateDiaryResponseDto updateDiary(Long diaryId,String diaryContent,String diaryTitle,String imageUrl){ Diary diary=findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); - if(diary.getImageurl()!=null){ + /* if(diary.getImageurl()!=null){ s3UploaderService.deleteFileFromS3(diary.getImageurl()); - } + }*/ diary.setImageurl(imageUrl); if(!diary.getContent().equals(diaryContent)){ diary.setFeeling(null); From 0a7cd4df27484a830d2488ce2c02b529c0f56779 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Thu, 16 May 2024 18:13:27 +0900 Subject: [PATCH 34/43] =?UTF-8?q?feat:=20=EC=9D=BC=EA=B8=B0=EC=82=AD?= =?UTF-8?q?=EC=A0=9Capi=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=B6=94=EA=B0=80,slowtypeapi=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EA=B0=92=EB=8C=80=EB=AC=B8=EC=9E=90=EB=A1=9C=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../diary/controller/DiaryController.java | 35 ++++++++++++++++--- .../dto/request/DiaryDeleteRequestDto.java | 14 ++++++++ .../response/SlowTypeCreateResponseDto.java | 2 +- .../domain/diary/service/DiaryService.java | 9 +++++ .../exception/GlobalExceptionHandler.java | 11 ++++++ 5 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryDeleteRequestDto.java diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 7b6810b..b823556 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -5,11 +5,8 @@ import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; -import org.kau.kkoolbeeServer.domain.diary.dto.request.DiaryUpdateRequestDto; -import org.kau.kkoolbeeServer.domain.diary.dto.request.FeelingListRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.request.*; import org.kau.kkoolbeeServer.domain.diary.dto.response.*; -import org.kau.kkoolbeeServer.domain.diary.dto.request.CurrentDateRequestDto; -import org.kau.kkoolbeeServer.domain.diary.dto.request.DiaryContentRequestDto; import org.kau.kkoolbeeServer.domain.diary.service.DiaryService; import org.kau.kkoolbeeServer.domain.member.Member; import org.kau.kkoolbeeServer.domain.member.service.MemberService; @@ -214,7 +211,7 @@ public ResponseEntity> createSlowTypeDiary(@RequestPart("imageurl } }*/ @PostMapping("/api/diary/create/slow") - public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageurl",required = false)MultipartFile image, + public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = "Authorization") String authHeader, @RequestPart(value = "imageUrl",required = false)MultipartFile image, @RequestPart(value = "diaryTitle") String diaryTitle, @RequestPart(value = "diaryContent") String diaryContent){ @@ -376,6 +373,34 @@ public ResponseEntity updateDiary( +} + +@DeleteMapping("api/diary/delete") + public ResponseEntitydeleteDiary(@RequestHeader(value = "Authorization") String authHeader,@RequestBody DiaryDeleteRequestDto diaryDeleteRequestDto){ + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + Long memberID= jwtProvider.getUserFromJwt(accessToken); + Diary diary=diaryService.findDiaryById(diaryDeleteRequestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if(diary.getMember().getId()!=memberID){ + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); + } + } + else{ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + + } + diaryService.deleteDiary(diaryDeleteRequestDto.getDiaryId()); + return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED)); + + + + + + + + } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryDeleteRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryDeleteRequestDto.java new file mode 100644 index 0000000..92fca08 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryDeleteRequestDto.java @@ -0,0 +1,14 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class DiaryDeleteRequestDto { + private Long diaryId; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java index 3cded2f..1b4ec3e 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java @@ -13,5 +13,5 @@ public class SlowTypeCreateResponseDto { private Long diaryId; private String diaryContent; private String diaryTitle; - private String imageurl; + private String imageUrl; } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index f2c6380..757a7b1 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -6,6 +6,7 @@ import org.kau.kkoolbeeServer.domain.diary.dto.response.UpdateDiaryResponseDto; import org.kau.kkoolbeeServer.domain.diary.repository.DiaryRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -112,6 +113,14 @@ public UpdateDiaryResponseDto updateDiaryWithoutImage(Long diaryId,String diaryC + } + + public void deleteDiary(Long diaryId){ + Diary diary=findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException()); + diaryRepository.delete(diary); + + + } diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java index 0b48f7c..60b715e 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/exception/GlobalExceptionHandler.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.INTERNAL_SERVER_ERROR; import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.REQUEST_VALIDATION_ERROR; @@ -104,6 +105,16 @@ protected ApiResponse handleIllegalArgumentException(IllegalArgumentException return ApiResponse.error(REQUEST_VALIDATION_ERROR, "잘못된 요청 형식입니다."); } + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(NoSuchElementException.class) + protected ApiResponse handleNoSuchElementException(NoSuchElementException e) { + // 로그 기록, 오류 메시지 생성 및 기타 필요한 처리 + log.error("Unexpected error occurred", e); + return ApiResponse.error(REQUEST_VALIDATION_ERROR, "해당 Id의 일기는 이미 존재하지 않습니다."); + } + + + //이부분추가 From 4fd5245a649c7b6662d7d014c1f448a8c8d1af11 Mon Sep 17 00:00:00 2001 From: jwaminseok Date: Thu, 16 May 2024 23:43:32 +0900 Subject: [PATCH 35/43] =?UTF-8?q?refactor:imageurl=20LONGTEXT=EB=A1=9C?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java index 73f3b8e..de8d885 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/Diary.java @@ -39,7 +39,7 @@ public class Diary extends BaseTimeEntity { @Column(nullable = false,columnDefinition = "LONGTEXT") private String content; - + @Column(columnDefinition = "LONGTEXT") private String imageurl; private String title; From 77a052b2bf9b3c281d5bcd04f6bd10b502c56625 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Sat, 18 May 2024 22:32:59 +0900 Subject: [PATCH 36/43] =?UTF-8?q?refactor:slowtyperesponseDto=EC=97=90=20c?= =?UTF-8?q?reatedDate=ED=95=84=EB=93=9C=EC=B6=94=EA=B0=80=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/controller/DiaryController.java | 2 +- .../domain/diary/dto/response/SlowTypeCreateResponseDto.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index b823556..a1abad1 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -247,7 +247,7 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = Diary savedDiary=diaryService.saveDiary(diary); - SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl()); + SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl(),diary.getWritedAt()); return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); } catch (Exception e) { diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java index 1b4ec3e..0cffd93 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java @@ -5,6 +5,8 @@ import lombok.Getter; import lombok.Setter; +import java.time.LocalDateTime; + @Getter @Setter @AllArgsConstructor @@ -14,4 +16,6 @@ public class SlowTypeCreateResponseDto { private String diaryContent; private String diaryTitle; private String imageUrl; + private LocalDateTime createdDate; + } From 96ef6b49cea50f4b7069dfa7522bc10bdea87379 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 22 May 2024 18:24:30 +0900 Subject: [PATCH 37/43] =?UTF-8?q?refactor:=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EC=83=9D=EC=84=B1api=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EC=BD=94=EB=93=9C=EC=A0=9C=EA=B1=B0=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 + .../KkoolbeeServerApplication.java | 2 +- .../kkoolbeeServer/S3/S3UploaderService.java | 24 ++ .../diary/controller/DiaryController.java | 250 ++++++------------ .../dto/request/ImageCreateRequestDto.java | 14 + .../OpenAiImageGenerationRequestDto.java | 25 ++ .../dto/response/ImageCreateResponseDto.java | 16 ++ .../OpenAiImageGenerationResponseDto.java | 20 ++ .../domain/diary/service/DiaryService.java | 131 ++++++++- .../global/common/dto/enums/ErrorType.java | 1 + .../global/config/AppConfig.java | 18 ++ .../global/config/OpenAiConfig.java | 17 ++ src/main/resources/application.yml | 4 +- 13 files changed, 346 insertions(+), 182 deletions(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/ImageCreateRequestDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/OpenAiImageGenerationRequestDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/ImageCreateResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/OpenAiImageGenerationResponseDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/config/AppConfig.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/global/config/OpenAiConfig.java diff --git a/build.gradle b/build.gradle index 027092f..cc446f0 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,12 @@ dependencies { // JSON implementation 'com.googlecode.json-simple:json-simple:1.1.1' + + + + + implementation 'org.apache.httpcomponents:httpclient:4.5.13' + } tasks.named('bootBuildImage') { diff --git a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java index eef97be..014c5f7 100644 --- a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java +++ b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java @@ -16,7 +16,7 @@ public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); TimeZone tz = TimeZone.getDefault(); - System.out.println("현재 시간대: " + tz.getID()); + } diff --git a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java index 937707e..7e04be9 100644 --- a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java +++ b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java @@ -10,6 +10,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.MessageDigest; @@ -50,6 +51,18 @@ public String upload(MultipartFile file) throws IOException, NoSuchAlgorithmExce return amazonS3Client.getUrl(bucketName, fileName).toString(); } + public String uploadFile(File file) throws IOException, NoSuchAlgorithmException { + String fileHash = calculateFileHash(file); + String fileName = fileHash + "-" + file.getName(); + + if (!amazonS3Client.doesObjectExist(bucketName, fileName)) { + amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, file)); + } + + return amazonS3Client.getUrl(bucketName, fileName).toString(); + + } + private String calculateFileHash(MultipartFile file) throws IOException, NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] fileBytes = file.getBytes(); @@ -57,6 +70,17 @@ private String calculateFileHash(MultipartFile file) throws IOException, NoSuchA return Hex.encodeHexString(hash); } + private String calculateFileHash(File file) throws IOException, NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + try (FileInputStream fis = new FileInputStream(file)) { + byte[] fileBytes = new byte[(int) file.length()]; + fis.read(fileBytes); + byte[] hash = digest.digest(fileBytes); + return Hex.encodeHexString(hash); + } + } + + diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index a1abad1..8aa8196 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -1,5 +1,7 @@ package org.kau.kkoolbeeServer.domain.diary.controller; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.Valid; import org.kau.kkoolbeeServer.S3.S3UploaderService; import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; @@ -37,6 +39,7 @@ import java.util.Optional; import java.util.stream.Collectors; + @RestController public class DiaryController { private static final Logger logger = LoggerFactory.getLogger(DiaryController.class); @@ -59,7 +62,20 @@ public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderSer @PostMapping("/api/diary/content") public ResponseEntity> getDiaryContents(@RequestHeader(value = "Authorization") String authHeader,@RequestBody DiaryContentRequestDto diaryContentRequestDto) { + try{ + Long memberId=extractMemberIdFromRequestHeader(authHeader); + + } + catch(IllegalArgumentException e){ + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } + catch(RuntimeException e){ + e.printStackTrace(); + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } Long diaryId = diaryContentRequestDto.getDiaryId(); Optional diaryOptional = diaryService.findDiaryById(diaryId); @@ -85,37 +101,17 @@ public ResponseEntity> getDiaryContents(@RequestHeader(value = "A return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseDto)); } else { return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR)); + .body(ApiResponse.error(ErrorType.NO_DIARY)); //diary 가 null 일 경우 요청이상함 반환 } } - /* @PostMapping("/api/diary/list/calendar") - public ResponseEntity> getDiariesByMonth(@RequestBody CurrentDateRequestDto requestDto){ - LocalDateTime currentDate = requestDto.getCurrentDate(); - - - Listdiaries=diaryService.findDiariesByMonth(currentDate); - if(diaries.isEmpty()){ - return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 월에 대한 일기가 존재하지 않습니다.")); - } - - List diaryDtos=diaries.stream() - .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt())) - .collect(Collectors.toList()); - - Map> responseMap= Map.of("monthList",diaryDtos); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap));*/ @PostMapping("/api/diary/list/calendar") public ResponseEntity> getDiariesByMonth(@RequestHeader("Authorization") String authHeader,@RequestBody CurrentDateRequestDto requestDto){ - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - } - Long memberId= jwtProvider.getUserFromJwt(accessToken); + + Long memberId= extractMemberIdFromRequestHeader(authHeader); LocalDateTime currentDate = requestDto.getCurrentDate(); @@ -134,45 +130,14 @@ public ResponseEntity> getDiariesByMonth(@RequestHeader("Authoriz return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); - - } - /*@PostMapping("/api/diary/list/feeling") - public ResponseEntity> getDiariesByFeeling( @RequestBody FeelingListRequestDto requestDto) { - - - List diaries = diaryService.findDiariesByFeeling(requestDto.getFeeling()); - - if (diaries.isEmpty()) { - return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 감정에 대한 일기가 존재하지 않습니다.")); - } - - List feelingList = diaries.stream() - .map(diary -> new FeelingListResponseDto(diary.getId(), diary.getWritedAt(), diary.getTitle())) - .collect(Collectors.toList()); - - Map> responseMap = Map.of("feelingList", feelingList); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); - - }*/ - @PostMapping("/api/diary/list/feeling") public ResponseEntity> getDiariesByFeeling( @RequestHeader(value = "Authorization") String authHeader,@RequestBody FeelingListRequestDto requestDto) { - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - - Long memberId= jwtProvider.getUserFromJwt(accessToken); + Long memberId= extractMemberIdFromRequestHeader(authHeader); Feeling feeling=Feeling.valueOf(requestDto.getFeeling()); List diaries = diaryService.findDiariesByMemberIdAndFeeling(memberId,feeling); @@ -216,25 +181,13 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = @RequestPart(value = "diaryContent") String diaryContent){ try { - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - + Long memberId=extractMemberIdFromRequestHeader(authHeader); String imageUrl=null; - if(image!=null && !image.isEmpty() ){ imageUrl = s3UploaderService.upload(image); } - - - Long memberId= jwtProvider.getUserFromJwt(accessToken); Member member= memberService.findByIdOrThrow(memberId); Diary diary = new Diary(); diary.setTitle(diaryTitle); @@ -250,74 +203,16 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl(),diary.getWritedAt()); return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); - } catch (Exception e) { - e.printStackTrace(); - - - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); - } - } - -/* @PatchMapping("/api/diary/update") - public ResponseEntity updateDiary( - @RequestHeader(value = "Authorization") String authHeader, - @RequestPart(value = "imageUrl", required = false) MultipartFile imageFile, - @RequestPart(value="diaryId") Long diaryId, - @RequestPart(value = "diaryTitle") String diaryTitle, - @RequestPart(value = "diaryContent") String diaryContent) { - - logger.info("Authorization Header: {}", authHeader); - logger.info("Received diaryId: {}", diaryId); - logger.info("Received diaryTitle: {}", diaryTitle); - logger.info("Received diaryContent: {}", diaryContent); - - - - try{ - - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - Long memberID= jwtProvider.getUserFromJwt(accessToken); - Diary diary=diaryService.findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); - if(diary.getMember().getId()!=memberID){ - - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); + }catch (IllegalArgumentException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); } - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - String imageUrl=null; - if (imageFile!=null && !imageFile.isEmpty()){ - imageUrl=s3UploaderService.upload(imageFile); - - UpdateDiaryResponseDto responseDto=diaryService.updateDiary(diaryId,diaryContent,diaryTitle,imageUrl); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); - - } - else{ - UpdateDiaryResponseDto responseDto=diaryService.updateDiaryWithoutImage(diaryId,diaryContent,diaryTitle); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); - - - } - - - } - catch (Exception e){ - - logger.error("An error occurred while updating diary", e); - e.printStackTrace(); - - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); - } - - - + catch(Exception e){ + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR)); + } + } - }*/ @PatchMapping("/api/diary/update") public ResponseEntity updateDiary( @RequestHeader(value = "Authorization") String authHeader, @@ -327,24 +222,19 @@ public ResponseEntity updateDiary( String diaryTitle= requestDto.getDiaryTitle(); String diaryContent= requestDto.getDiaryContent(); - - try{ String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - Long memberID= jwtProvider.getUserFromJwt(accessToken); - Diary diary=diaryService.findDiaryById(requestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); - if(diary.getMember().getId()!=memberID){ + Long memberId=extractMemberIdFromRequestHeader(authHeader); + accessToken = authHeader.substring(7); + + Diary diary=diaryService.findDiaryById(requestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if(diary.getMember().getId()!=memberId){ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); - } } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - } + String imageUrl=null; if (imageFile!=null && !imageFile.isEmpty()){ @@ -361,7 +251,9 @@ public ResponseEntity updateDiary( } - + } + catch (IllegalArgumentException e){ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); } catch (Exception e){ logger.error("An error occurred while updating diary", e); @@ -371,37 +263,61 @@ public ResponseEntity updateDiary( } - - } @DeleteMapping("api/diary/delete") - public ResponseEntitydeleteDiary(@RequestHeader(value = "Authorization") String authHeader,@RequestBody DiaryDeleteRequestDto diaryDeleteRequestDto){ - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - Long memberID= jwtProvider.getUserFromJwt(accessToken); - Diary diary=diaryService.findDiaryById(diaryDeleteRequestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); - if(diary.getMember().getId()!=memberID){ - + public ResponseEntitydeleteDiary(@RequestHeader(value = "Authorization") String authHeader,@RequestBody String rawBody){ + ObjectMapper objectMapper = new ObjectMapper(); + try { + JsonNode rootNode = objectMapper.readTree(rawBody); + JsonNode diaryIdNode = rootNode.get("diaryId"); + if (diaryIdNode == null) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid request body"); + } + Long diaryId = diaryIdNode.asLong(); + Long memberId = extractMemberIdFromRequestHeader(authHeader); + Diary diary = diaryService.findDiaryById(diaryId) + .orElseThrow(() -> new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if (!diary.getMember().getId().equals(memberId)) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); } - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - diaryService.deleteDiary(diaryDeleteRequestDto.getDiaryId()); - return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED)); - - - + diaryService.deleteDiary(diaryId); + return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED)); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error parsing request by"); + } +} + @PostMapping("/api/diary/image") + public ResponseEntity imageCreate(@RequestHeader("Authorization") String authHeader,@RequestBody ImageCreateRequestDto imageCreateRequestDto){ + try{ + Long memberId=extractMemberIdFromRequestHeader(authHeader); + ImageCreateResponseDto responseDto=diaryService.generateImageFromDiary(imageCreateRequestDto.getDiaryId()); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + } + catch (IllegalArgumentException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } + catch(Exception e){ + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR)); + } + } -} + public Long extractMemberIdFromRequestHeader(String authHeader) { + if (authHeader != null && authHeader.startsWith("Bearer ")) { + String accessToken = authHeader.substring(7); + return jwtProvider.getUserFromJwt(accessToken); + } else { + throw new IllegalArgumentException("Invalid Authorization header"); + } + } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/ImageCreateRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/ImageCreateRequestDto.java new file mode 100644 index 0000000..7d152fa --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/ImageCreateRequestDto.java @@ -0,0 +1,14 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +@NoArgsConstructor +public class ImageCreateRequestDto { + private Long diaryId; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/OpenAiImageGenerationRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/OpenAiImageGenerationRequestDto.java new file mode 100644 index 0000000..15ace21 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/OpenAiImageGenerationRequestDto.java @@ -0,0 +1,25 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class OpenAiImageGenerationRequestDto { + private String model; + private String prompt; + private int n; + + private String size; + + @Builder + public OpenAiImageGenerationRequestDto(String model,String prompt, int n, String size) { + this.model=model; + this.prompt = prompt; + this.n = n; + this.size = size; + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/ImageCreateResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/ImageCreateResponseDto.java new file mode 100644 index 0000000..c02ddc5 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/ImageCreateResponseDto.java @@ -0,0 +1,16 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class ImageCreateResponseDto { + + private Long diaryId; + private String image_url; + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/OpenAiImageGenerationResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/OpenAiImageGenerationResponseDto.java new file mode 100644 index 0000000..3b90a90 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/OpenAiImageGenerationResponseDto.java @@ -0,0 +1,20 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +public class OpenAiImageGenerationResponseDto { + + private Long created; + private List data; + + @Getter + @NoArgsConstructor + public static class ImageData { + private String url; + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 757a7b1..9d7aee9 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -3,14 +3,30 @@ import org.kau.kkoolbeeServer.S3.S3UploaderService; import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; +import org.kau.kkoolbeeServer.domain.diary.dto.request.OpenAiImageGenerationRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.ImageCreateResponseDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.OpenAiImageGenerationResponseDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.UpdateDiaryResponseDto; import org.kau.kkoolbeeServer.domain.diary.repository.DiaryRepository; +import org.kau.kkoolbeeServer.global.config.OpenAiConfig; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - -import javax.swing.text.html.Option; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -20,14 +36,19 @@ @Service public class DiaryService { + @Value("${openai.api.key}") + private String apiKey; + private RestTemplate restTemplate; private DiaryRepository diaryRepository; private S3UploaderService s3UploaderService; @Autowired - public DiaryService(DiaryRepository diaryRepository,S3UploaderService s3UploaderService) { + public DiaryService(RestTemplate restTemplate, DiaryRepository diaryRepository,S3UploaderService s3UploaderService) { + this.restTemplate=restTemplate; this.diaryRepository = diaryRepository; this.s3UploaderService=s3UploaderService; + } public Optional findDiaryById(Long diary_id){ @@ -36,12 +57,6 @@ public Optional findDiaryById(Long diary_id){ } - /* public List findDiariesByMonth(LocalDateTime date) { - LocalDateTime startOfMonth = date.withDayOfMonth(1).toLocalDate().atStartOfDay(); - LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); - return diaryRepository.findByWritedAtBetween(startOfMonth, endOfMonth); - }*/ - public List findDiariesByMonthAndMemberId(LocalDateTime date, Long memberId) { LocalDateTime startOfMonth = date.withDayOfMonth(1).toLocalDate().atStartOfDay(); LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); @@ -50,10 +65,7 @@ public List findDiariesByMonthAndMemberId(LocalDateTime date, Long member - /* public List findDiariesByFeeling(String feeling) { - return diaryRepository.findByFeeling(Feeling.valueOf(feeling)); - } -*/ + public List findDiariesByMemberIdAndFeeling(Long memberId,Feeling feeling) { return diaryRepository.findByMemberIdAndFeeling(memberId,feeling); } @@ -123,5 +135,98 @@ public void deleteDiary(Long diaryId){ } + public ImageCreateResponseDto generateImageFromDiary(Long diaryId) throws IOException, NoSuchAlgorithmException{ + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.parseMediaType(OpenAiConfig.MEDIA_TYPE)); + httpHeaders.add(OpenAiConfig.AUTHORIZATION, OpenAiConfig.BEARER + apiKey); + Diary diary=findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + String formattedPrompt=formatPromptForImageGeneration(diary.getContent()); + + OpenAiImageGenerationRequestDto requestDto= + OpenAiImageGenerationRequestDto.builder() + .model(OpenAiConfig.MODEL) + .prompt(formattedPrompt) + .n(OpenAiConfig.IMAGE_COUNT) + .size(OpenAiConfig.IMAGE_SIZE) + .build(); + + HttpEntity requestDtoHttpEntity=new HttpEntity<>(requestDto,httpHeaders); + + ResponseEntity responseEntity = + restTemplate.postForEntity(OpenAiConfig.IMAGE_URL, + requestDtoHttpEntity, + OpenAiImageGenerationResponseDto.class); + + OpenAiImageGenerationResponseDto responseBody = responseEntity.getBody(); + if (responseBody != null && !responseBody.getData().isEmpty()) { + String imageUrl = responseBody.getData().get(0).getUrl(); + File downloadedFile=downloadFileFromUrl(imageUrl); + String s3Url = s3UploaderService.uploadFile(downloadedFile); + diary.setImageurl(s3Url); + diaryRepository.save(diary); + downloadedFile.delete(); + return new ImageCreateResponseDto(diary.getId(), diary.getImageurl()); + + + } else { + throw new RuntimeException("이미지 생성에 실패했습니다."); + } + + + } + + private File downloadFileFromUrl(String fileUrl) throws IOException { + URL url = new URL(fileUrl); + InputStream inputStream = url.openStream(); + File tempFile = File.createTempFile("image-", ".png"); + + try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } finally { + inputStream.close(); + } + return tempFile; + } + + private String formatPromptForImageGeneration(String diaryContent) { + + String formattedPrompt = String.format(""" +유저의 diary 내용 : %s + +유저의 diary 내용을 요약해주세요. 아래는 예시입니다. +예시 + { 내용 : 2024년 4월 1일, 친구들과 함께 제주도의 한라산을 등반했다. + 한라산은 한국에서 가장 높은 산으로, 그 정상에서 바라보는 제주도의 풍경은 정말로 인상적이였다. + 제주도의 파란 바다와 울창한 숲이 한눈에 들어왔고, 그 아름다움에 모두가 말을 잃었다. + 우리는 등반하면서 서로를 의지하며 많은 얘기를 나누었고, 함께 등반한 친구들과 나는 더욱 가까워진 것을 느꼈다. + 우리는 서로에 대해 더 많이 알게 되었고, 이 여행이 우리 사이의 우정을 더욱 깊게 만들었다는 것을 깨달았다. + 한라산 등반은 단순한 여행 이상의 의미가 있었다. + 이 경험은 나에게 자연의 아름다움을 다시 한번 일깨워 주었고, 친구들과의 관계를 더욱 소중히 여기게 만들었다. + 이번 등반을 통해 얻은 추억과 교훈은 앞으로도 오랫동안 내 마음속에 남아있을 것이다. + 요약 : 친구들과 함께 제주도의 한라산을 등반했다. 한라산 정상에서의 풍경은 인상적이었고, 함께한 친구들과의 우정도 더 깊어졌다. + } +다음 요약된 내용을 바탕으로 다음과 같은 이미지를 생성해주세요. +이미지는 평면적인 2D 일러스트 스타일로, 유저의 감정을 반영하여 다음과 같은 색상을 주로 사용해주세요. +- 행복한 감정: 밝은 노란색과 분홍색 +- 슬픈 감정: 파란색 계열 +- 안정된 감정: 녹색 계열 +- 걱정되는 감정: 보라색 +- 놀란 감정: 오렌지색 +- 화난 감정: 분홍색 +캐릭터는 꿀벌 옷을 입고있는 곰을 모티브로 한 일러스트로, 사용자의 diary 내용에 맞는 포즈와 표정을 취하도록 해주세요. +다양한 요소들을 추가하기보단 불필요한 것들을 빼고 단조로운 스타일로 생성해주세요 +""", diaryContent); + + // 여기에 프롬프트를 포맷팅하는 로직을 구현합니다. + // 예를 들어, 특정 키워드를 추가하거나, 내용을 요약하는 등의 작업을 할 수 있습니다. + return formattedPrompt; // 일단은 단순히 일기 내용을 그대로 반환하도록 합니다. 실제 구현시에는 수정해야 합니다. + } + + + } diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index be0aaf0..65551b4 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -43,6 +43,7 @@ public enum ErrorType { ADVICE_NOT_FOUND(HttpStatus.NOT_FOUND, "요청하신 Diary에 맞는 Advice가 없습니다."), NOT_FOUND_REFRESH_TOKEN_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 리프레시 토큰입니다."), NOT_YOUR_DIARY(HttpStatus.NOT_FOUND,"해당 Diary에 접근할 수 있는 권한이 없습니다."), + NO_DIARY(HttpStatus.NOT_FOUND,"해당 Id에 맞는 Diary가 존재하지 않습니다."), /** * 500 INTERNAL SERVER ERROR diff --git a/src/main/java/org/kau/kkoolbeeServer/global/config/AppConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/AppConfig.java new file mode 100644 index 0000000..83593e9 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/AppConfig.java @@ -0,0 +1,18 @@ +package org.kau.kkoolbeeServer.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate=new RestTemplate(); + // JSON 메시지 컨버터 추가 + restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); + return new RestTemplate(); + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/config/OpenAiConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/OpenAiConfig.java new file mode 100644 index 0000000..dea8c98 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/OpenAiConfig.java @@ -0,0 +1,17 @@ +package org.kau.kkoolbeeServer.global.config; + +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenAiConfig { + public static final String AUTHORIZATION = "Authorization"; + public static final String BEARER = "Bearer "; + public static final String MEDIA_TYPE = "application/json; charset=UTF-8"; + + public static final String MODEL= "dall-e-3"; + public static final String IMAGE_URL = "https://api.openai.com/v1/images/generations"; + public static final int IMAGE_COUNT = 1;// 1~10 + public static final String IMAGE_SIZE = "1024x1024"; // '256x256', '512x512', '1024x1024' + + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 34f7983..cc310f3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -24,4 +24,6 @@ logging: com: amazonaws: util: - EC2MetadataUtils: error \ No newline at end of file + EC2MetadataUtils: error +openai: + api.key: ${API_KEY} \ No newline at end of file From 08d5eb71c6bfeb053312a6016c31326de948acc3 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Sun, 26 May 2024 15:37:42 +0900 Subject: [PATCH 38/43] =?UTF-8?q?feag:=EC=B9=B4=EC=B9=B4=EC=98=A4=ED=86=A1?= =?UTF-8?q?=EA=B3=B5=EC=9C=A0=ED=95=98=EA=B8=B0api=EA=B5=AC=ED=98=84=20(#9?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../diary/controller/DiaryController.java | 9 ++++++++ .../dto/request/DiaryShareRequestDto.java | 11 ++++++++++ .../dto/response/DiaryShareResponseDto.java | 22 +++++++++++++++++++ .../domain/diary/service/DiaryService.java | 12 ++++++++++ 4 files changed, 54 insertions(+) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryShareRequestDto.java create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 8aa8196..ee198cd 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import feign.Response; import jakarta.validation.Valid; import org.kau.kkoolbeeServer.S3.S3UploaderService; import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; @@ -310,6 +311,14 @@ public ResponseEntity imageCreate(@RequestHeader("Authorization") String auth } } + @GetMapping("/api/diary/share") + public ResponseEntity diaryShare(@RequestBody DiaryShareRequestDto requestDto){ + + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,diaryService.diaryShare(requestDto.getDiaryId()))); + + + } + public Long extractMemberIdFromRequestHeader(String authHeader) { if (authHeader != null && authHeader.startsWith("Bearer ")) { String accessToken = authHeader.substring(7); diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryShareRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryShareRequestDto.java new file mode 100644 index 0000000..446a1e8 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/DiaryShareRequestDto.java @@ -0,0 +1,11 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class DiaryShareRequestDto { + + private Long diaryId; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java new file mode 100644 index 0000000..fa180fa --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java @@ -0,0 +1,22 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; +import org.kau.kkoolbeeServer.domain.diary.Feeling; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class DiaryShareResponseDto { + private Long diaryId; + private String diaryContent; + private String diaryTitle; + private String imageUrl; + private String userName; + private AdviceResponseDto advice; + private String feeling; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 9d7aee9..6efec19 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -1,9 +1,11 @@ package org.kau.kkoolbeeServer.domain.diary.service; import org.kau.kkoolbeeServer.S3.S3UploaderService; +import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; import org.kau.kkoolbeeServer.domain.diary.dto.request.OpenAiImageGenerationRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.DiaryShareResponseDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.ImageCreateResponseDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.OpenAiImageGenerationResponseDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.UpdateDiaryResponseDto; @@ -133,6 +135,16 @@ public void deleteDiary(Long diaryId){ + } + public DiaryShareResponseDto diaryShare(Long diaryId){ + Diary findDiary=diaryRepository.findById(diaryId).orElseThrow(()->new NoSuchElementException()); + String feeling = findDiary.getFeeling() != null ? findDiary.getFeeling().toString() : null; + AdviceResponseDto adviceResponseDto=AdviceResponseDto.fromAdviceOrNull(findDiary.getAdvice()); + + DiaryShareResponseDto responseDto=new DiaryShareResponseDto(findDiary.getId(), findDiary.getContent(), + findDiary.getTitle(), findDiary.getImageurl(), findDiary.getMember().getSocialNickname(), + adviceResponseDto,feeling); + return responseDto; } public ImageCreateResponseDto generateImageFromDiary(Long diaryId) throws IOException, NoSuchAlgorithmException{ From 431f5e9183d71e32b533d38d8bfa92ece9127ab0 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Mon, 27 May 2024 00:20:25 +0900 Subject: [PATCH 39/43] =?UTF-8?q?refacotr:requestpart=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/controller/DiaryController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index 4205a35..6460d37 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -311,9 +311,9 @@ public ResponseEntity imageCreate(@RequestHeader("Authorization") String auth } @GetMapping("/api/diary/share") - public ResponseEntity diaryShare(@RequestBody DiaryShareRequestDto requestDto){ + public ResponseEntity diaryShare(@RequestParam("diaryId") Long diaryId){ - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,diaryService.diaryShare(requestDto.getDiaryId()))); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,diaryService.diaryShare(diaryId))); } From 0b2c056c32306dbb795f27e4931a1d73be0ccd56 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 29 May 2024 00:05:32 +0900 Subject: [PATCH 40/43] =?UTF-8?q?refactor:createdDate=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/diary/dto/response/DiaryShareResponseDto.java | 3 +++ .../kau/kkoolbeeServer/domain/diary/service/DiaryService.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java index fa180fa..28a5cbd 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/DiaryShareResponseDto.java @@ -7,6 +7,8 @@ import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; import org.kau.kkoolbeeServer.domain.diary.Feeling; +import java.time.LocalDateTime; + @Getter @Setter @NoArgsConstructor @@ -16,6 +18,7 @@ public class DiaryShareResponseDto { private String diaryContent; private String diaryTitle; private String imageUrl; + private LocalDateTime createdDate; private String userName; private AdviceResponseDto advice; private String feeling; diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 6efec19..82a2a2b 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -142,7 +142,7 @@ public DiaryShareResponseDto diaryShare(Long diaryId){ AdviceResponseDto adviceResponseDto=AdviceResponseDto.fromAdviceOrNull(findDiary.getAdvice()); DiaryShareResponseDto responseDto=new DiaryShareResponseDto(findDiary.getId(), findDiary.getContent(), - findDiary.getTitle(), findDiary.getImageurl(), findDiary.getMember().getSocialNickname(), + findDiary.getTitle(), findDiary.getImageurl(), findDiary.getWritedAt(),findDiary.getMember().getSocialNickname(), adviceResponseDto,feeling); return responseDto; } From 6027faf502ab243e3f83850dde368072d1d479f0 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 29 May 2024 17:05:00 +0900 Subject: [PATCH 41/43] =?UTF-8?q?refactor:=ED=94=84=EB=A1=AC=ED=94=84?= =?UTF-8?q?=ED=8A=B8=EC=88=98=EC=A0=95=20(#107)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkoolbeeServer/domain/diary/service/DiaryService.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 82a2a2b..7ee7779 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -222,13 +222,7 @@ private String formatPromptForImageGeneration(String diaryContent) { 요약 : 친구들과 함께 제주도의 한라산을 등반했다. 한라산 정상에서의 풍경은 인상적이었고, 함께한 친구들과의 우정도 더 깊어졌다. } 다음 요약된 내용을 바탕으로 다음과 같은 이미지를 생성해주세요. -이미지는 평면적인 2D 일러스트 스타일로, 유저의 감정을 반영하여 다음과 같은 색상을 주로 사용해주세요. -- 행복한 감정: 밝은 노란색과 분홍색 -- 슬픈 감정: 파란색 계열 -- 안정된 감정: 녹색 계열 -- 걱정되는 감정: 보라색 -- 놀란 감정: 오렌지색 -- 화난 감정: 분홍색 +이미지는 평면적인 2D 일러스트 스타일로 해주세요. 캐릭터는 꿀벌 옷을 입고있는 곰을 모티브로 한 일러스트로, 사용자의 diary 내용에 맞는 포즈와 표정을 취하도록 해주세요. 다양한 요소들을 추가하기보단 불필요한 것들을 빼고 단조로운 스타일로 생성해주세요 """, diaryContent); From 4147c6f23d23b339fb38bc65522de97f5a9928bb Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Fri, 31 May 2024 18:11:09 +0900 Subject: [PATCH 42/43] [FEAT]-109 payment (#110) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: iampory필요한 환경세팅 * feat:결제검증로직추가 --- build.gradle | 3 + .../payment/controller/PaymentController.java | 80 +++++++++++++++++++ .../global/common/dto/enums/ErrorType.java | 1 + src/main/resources/application.yml | 7 +- 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/kau/kkoolbeeServer/domain/payment/controller/PaymentController.java diff --git a/build.gradle b/build.gradle index cc446f0..9ae4af8 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ configurations { repositories { mavenCentral() + maven { url 'https://jitpack.io' } // JitPack 저장소 추가 } dependencies { @@ -67,6 +68,8 @@ dependencies { // JSON implementation 'com.googlecode.json-simple:json-simple:1.1.1' + // I'mport REST Client + implementation 'com.github.iamport:iamport-rest-client-java:0.2.21' diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/payment/controller/PaymentController.java b/src/main/java/org/kau/kkoolbeeServer/domain/payment/controller/PaymentController.java new file mode 100644 index 0000000..678a555 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/payment/controller/PaymentController.java @@ -0,0 +1,80 @@ +package org.kau.kkoolbeeServer.domain.payment.controller; + +import com.siot.IamportRestClient.IamportClient; +import com.siot.IamportRestClient.exception.IamportResponseException; +import com.siot.IamportRestClient.response.IamportResponse; +import com.siot.IamportRestClient.response.Payment; +import jakarta.annotation.PostConstruct; +import org.kau.kkoolbeeServer.global.common.dto.ApiResponse; +import org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@ComponentScan +@RestController +public class PaymentController { + + @Value("${iamport.key}") + private String restApiKey; + @Value("${iamport.secret}") + private String restApiSecret; + private IamportClient iamportClient; + + @PostConstruct + public void init() { + this.iamportClient = new IamportClient(restApiKey, restApiSecret); + } + + + /* @PostMapping("/verifyIamport/{imp_uid}") + public ResponseEntity> paymentByImpUid(@PathVariable("imp_uid") String imp_uid) throws IamportResponseException, IOException { + IamportResponse response = iamportClient.paymentByImpUid(imp_uid); + Map result = new HashMap<>(); + + if (response.getResponse().getStatus().equals("paid")) { + result.put("status", "paid"); + } else { + result.put("status", "failed"); + } + + return ResponseEntity.ok(result); + + + + + }*/ + + /*@PostMapping("/verifyIamport/{imp_uid}") + public IamportResponse paymentByImpUid(@PathVariable("imp_uid") String imp_uid) throws IamportResponseException, IOException { + return iamportClient.paymentByImpUid(imp_uid); + }*/ + @PostMapping("/verifyIamport/{imp_uid}") + public ResponseEntity verifyPayment(@PathVariable("imp_uid") String impUid) { + try { + IamportResponse response = iamportClient.paymentByImpUid(impUid); + if (response.getResponse() != null) { +// 결제 검증 로직 추가 (예: 결제 금액 검증, 결제자 정보 검증 등) + return ResponseEntity.ok(response.getResponse()); + } else { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ApiResponse.error(ErrorType.NOT_FOUND_PAYMENT)); + } + } catch (IamportResponseException e) { +// 아임포트 서버로부터 오류 응답을 받은 경우 + return ResponseEntity.status(e.getHttpStatusCode()).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); + } catch (IOException e) { +// 네트워크 오류 등의 IOException 처리 + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,"서버 오류가 발생했습니다.")); + } + } + + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index 65551b4..e42eaaf 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -44,6 +44,7 @@ public enum ErrorType { NOT_FOUND_REFRESH_TOKEN_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 리프레시 토큰입니다."), NOT_YOUR_DIARY(HttpStatus.NOT_FOUND,"해당 Diary에 접근할 수 있는 권한이 없습니다."), NO_DIARY(HttpStatus.NOT_FOUND,"해당 Id에 맞는 Diary가 존재하지 않습니다."), + NOT_FOUND_PAYMENT(HttpStatus.NOT_FOUND,"결제 정보를 찾을 수 없습니다."), /** * 500 INTERNAL SERVER ERROR diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index cc310f3..5dbc782 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -26,4 +26,9 @@ logging: util: EC2MetadataUtils: error openai: - api.key: ${API_KEY} \ No newline at end of file + api.key: ${API_KEY} + + +iamport: + key: ${IAMPORT_KEY} + secret: ${IAMPORT_SECRET} From 01469c9cb0a69bf783347ae1d61241ab212f5ab5 Mon Sep 17 00:00:00 2001 From: jwaminseok <109052897+jms0324@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:16:41 +0900 Subject: [PATCH 43/43] =?UTF-8?q?feat:discord=20=EC=9B=B9=ED=9B=85=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/service/MemberService.java | 46 +++++++++++++++++-- src/main/resources/application.yml | 4 ++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java b/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java index 43422a1..cf229df 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/member/service/MemberService.java @@ -1,5 +1,6 @@ package org.kau.kkoolbeeServer.domain.member.service; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.kau.kkoolbeeServer.domain.member.Member; @@ -11,8 +12,16 @@ import org.kau.kkoolbeeServer.global.auth.jwt.TokenDto; import org.kau.kkoolbeeServer.global.auth.security.UserAuthentication; import org.kau.kkoolbeeServer.global.common.exception.model.CustomException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.INVALID_TOKEN_HEADER_ERROR; import static org.kau.kkoolbeeServer.global.common.dto.enums.ErrorType.NOT_FOUND_MEMBER_ERROR; @@ -23,6 +32,8 @@ @Transactional(readOnly = true) public class MemberService { + @Value("${discord.webhook.url}") + private String discordWebhookUrl; private final MemberRepository memberRepository; private final JwtProvider jwtProvider; private final KakaoLoginService kakaoLoginService; @@ -43,16 +54,25 @@ public MemberLoginResponseDto login(String socialAccessToken) { String kakaoId = kakaoLoginService.getKakaoId(socialAccessToken); boolean isRegistered = isUserByKakaoId(kakaoId); + Member loginMember; if (!isRegistered) { Member member = Member.builder() .kakaoId(kakaoId).build(); memberRepository.save(member); + loginMember = getUserBySocialAndSocialId(kakaoId); + // 카카오 로그인은 정보 더 많이 받아올 수 있으므로 추가 설정 + kakaoLoginService.setKakaoInfo(loginMember, socialAccessToken); + sendDiscordNotification(member.getSocialNickname()); + } + else{ + loginMember = getUserBySocialAndSocialId(kakaoId); + // 카카오 로그인은 정보 더 많이 받아올 수 있으므로 추가 설정 + kakaoLoginService.setKakaoInfo(loginMember, socialAccessToken); + } + - Member loginMember = getUserBySocialAndSocialId(kakaoId); - // 카카오 로그인은 정보 더 많이 받아올 수 있으므로 추가 설정 - kakaoLoginService.setKakaoInfo(loginMember, socialAccessToken); TokenDto tokenDto = jwtProvider.issueToken( new UserAuthentication(loginMember.getId(), null, null)); @@ -106,6 +126,26 @@ public Member findByIdOrThrow(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new CustomException(NOT_FOUND_MEMBER_ERROR)); } + private void sendDiscordNotification(String nickname) { + RestTemplate restTemplate = new RestTemplate(); + + Long totalMembers = memberRepository.count(); + + String message = totalMembers + "번째 멤버가 회원가입했습니다.\n" + + "사용자명: " + nickname; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + Map body = new HashMap<>(); + body.put("content", message); + + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + restTemplate.postForEntity(discordWebhookUrl, requestEntity, String.class); + + + } /* @Transactional diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5dbc782..8e710c7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,3 +32,7 @@ openai: iamport: key: ${IAMPORT_KEY} secret: ${IAMPORT_SECRET} + +discord: + webhook: + url: ${DISCORD_URL}