diff --git a/.ebextensions-aligo/01_set_up_swap.config b/.ebextensions-aligo/01_set_up_swap.config new file mode 100644 index 00000000..6b82f6f5 --- /dev/null +++ b/.ebextensions-aligo/01_set_up_swap.config @@ -0,0 +1,26 @@ +files: + "/home/ec2-user/setup_swap.sh": + mode: "000755" + owner: root + group: root + content: | + #!/bin/bash + # based on http://steinn.org/post/elasticbeanstalk-swap/ + + SWAPFILE=/var/swapfile + SWAP_MEGABYTES=2048 + + if [ -f $SWAPFILE ]; then + echo "Swapfile $SWAPFILE found, assuming already setup" + exit; + fi + + /bin/dd if=/dev/zero of=$SWAPFILE bs=1M count=$SWAP_MEGABYTES + /bin/chmod 600 $SWAPFILE + /sbin/mkswap $SWAPFILE + /sbin/swapon $SWAPFILE + +commands: + 01setup_swap: + command: "bash setup_swap.sh" + cwd: "/home/ec2-user/" diff --git a/.ebextensions-dev/01_set_up_swap.config b/.ebextensions-dev/01_set_up_swap.config new file mode 100644 index 00000000..6b82f6f5 --- /dev/null +++ b/.ebextensions-dev/01_set_up_swap.config @@ -0,0 +1,26 @@ +files: + "/home/ec2-user/setup_swap.sh": + mode: "000755" + owner: root + group: root + content: | + #!/bin/bash + # based on http://steinn.org/post/elasticbeanstalk-swap/ + + SWAPFILE=/var/swapfile + SWAP_MEGABYTES=2048 + + if [ -f $SWAPFILE ]; then + echo "Swapfile $SWAPFILE found, assuming already setup" + exit; + fi + + /bin/dd if=/dev/zero of=$SWAPFILE bs=1M count=$SWAP_MEGABYTES + /bin/chmod 600 $SWAPFILE + /sbin/mkswap $SWAPFILE + /sbin/swapon $SWAPFILE + +commands: + 01setup_swap: + command: "bash setup_swap.sh" + cwd: "/home/ec2-user/" diff --git a/.ebextensions-prod/01_set_up_swap.config b/.ebextensions-prod/01_set_up_swap.config new file mode 100644 index 00000000..6b82f6f5 --- /dev/null +++ b/.ebextensions-prod/01_set_up_swap.config @@ -0,0 +1,26 @@ +files: + "/home/ec2-user/setup_swap.sh": + mode: "000755" + owner: root + group: root + content: | + #!/bin/bash + # based on http://steinn.org/post/elasticbeanstalk-swap/ + + SWAPFILE=/var/swapfile + SWAP_MEGABYTES=2048 + + if [ -f $SWAPFILE ]; then + echo "Swapfile $SWAPFILE found, assuming already setup" + exit; + fi + + /bin/dd if=/dev/zero of=$SWAPFILE bs=1M count=$SWAP_MEGABYTES + /bin/chmod 600 $SWAPFILE + /sbin/mkswap $SWAPFILE + /sbin/swapon $SWAPFILE + +commands: + 01setup_swap: + command: "bash setup_swap.sh" + cwd: "/home/ec2-user/" diff --git "a/.github/ISSUE_TEMPLATE/\360\237\224\250refactor_issue_temlate.md" "b/.github/ISSUE_TEMPLATE/\360\237\224\250refactor_issue_temlate.md" index f6044057..b288e809 100644 --- "a/.github/ISSUE_TEMPLATE/\360\237\224\250refactor_issue_temlate.md" +++ "b/.github/ISSUE_TEMPLATE/\360\237\224\250refactor_issue_temlate.md" @@ -13,5 +13,5 @@ assignees: '' ### ✅ refactoring TODO - + - [ ] diff --git "a/.github/ISSUE_TEMPLATE/\360\237\232\200feture_issue_temlate.md" "b/.github/ISSUE_TEMPLATE/\360\237\232\200feture_issue_temlate.md" index 7665c482..9fbc4d6b 100644 --- "a/.github/ISSUE_TEMPLATE/\360\237\232\200feture_issue_temlate.md" +++ "b/.github/ISSUE_TEMPLATE/\360\237\232\200feture_issue_temlate.md" @@ -1,7 +1,7 @@ --- name: "\U0001F680FETURE_ISSUE_TEMLATE" about: 기능 추가시에 적는 템플릿 입니다 -title: "\U0001F680 [Feature]" +title: "\U0001F680 [feature]" labels: '' assignees: '' @@ -11,7 +11,5 @@ assignees: '' ## ✅ Todo - [ ] 구현 내용 1 -- [ ] 구현 내용 2 -- [ ] 구현 내용 3 ### 📚 Remarks diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 47783df4..2c144eb8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,9 @@ -## 개요 +## Pull Request +## 📌 개요 - 작업 브랜치 - close #issueNumber -## 작업사항 +## ⚡️작업사항 - 내용을 적어주세요. -## 변경로직 +## ♻️ 변경사항 (Optional) - 내용을 적어주세요. \ No newline at end of file diff --git a/.github/workflows/admin_api_ci_cd.yml b/.github/workflows/admin_api_ci_cd.yml deleted file mode 100644 index 3fb78be3..00000000 --- a/.github/workflows/admin_api_ci_cd.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: admin MATCH API Server CI/CD - -on: - push: - branches: [admin] - #tags: - # - Dev-Api-v*.*.* - workflow_dispatch: # 수동 실행 옵션 (생략) - -jobs: - build: - runs-on: ubuntu-latest # action 스크립트가 작동될 OS - - steps: # 작업 단계 - - name: Checkout source code # 단계별 이름, 구분자로 소스를 가져옴 - uses: actions/checkout@v2 - - - name: Setup JDK 11 - uses: actions/setup-java@v3 - with: - java-version: '11' - distribution: 'temurin' - - - name: Set Environment - Common Yml - uses: microsoft/variable-substitution@v1 - with: - files: ./Match-Common/src/main/resources/application-common.yml - env: - oauth.kakao.base-url: ${{ secrets.KAKAO_BASE_URL }} - oauth.kakao.client-id: ${{ secrets.KAKAO_CLIENT }} - oauth.kakao.client-secret: ${{ secrets.KAKAO_SECRET }} - oauth.kakao.redirect-url: ${{ secrets.KAKAO_REDIRECT }} - oauth.kakao.app-id: ${{ secrets.KAKAO_APP_ID }} - oauth.kakao.admin-key: ${{ secrets.KAKAO_ADMIN_KEY }} - jwt.secret: ${{ secrets.JWT_SECRET_KEY }} - jwt.refresh: ${{ secrets.JWT_REFRESH_KEY }} - oauth.naver.secret: ${{ secrets.NAVER_SECRET }} - oauth.naver.client: ${{ secrets.NAVER_CLIENT }} - oauth.naver.redirect-url: ${{ secrets.NAVER_REDIRECT}} - sms.secret: ${{ secrets.SMS_SECRET_KEY }} - sms.api: ${{ secrets.SMS_API_KEY }} - sms.sender: ${{ secrets.SMS_SENDER }} - nice.secret: ${{ secrets.NICE_SECRET}} - nice.client: ${{ secrets.NICE_CLIENT }} - nice.url: ${{ secrets.NICE_DEV_URL}} - - - - name: Set Environment Domain - Domain DEV Yml - uses: microsoft/variable-substitution@v1 - with: - files: ./Match-Domain/src/main/resources/application-domain-dev.yml - env: - spring.datasource.url: ${{ secrets.DB_URL_HOST }} - spring.datasource.username: ${{ secrets.AWS_DB_USER_NAME }} - spring.datasource.password: ${{ secrets.AWS_DB_PASSWORD }} - - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - shell: bash - - - name: Build with Gradle - run: ./gradlew Match-Api:bootJar - shell: bash - - - name: Get current time - uses: 1466587594/get-current-time@v2 - id: current-time - with: - format: YYYYMMDDTHHmm - utcOffset: "+09:00" - - - name: Generate deployment package - run: | - mkdir -p deploy - cp Match-Api/build/libs/*.jar deploy/match-dev-api.jar - cp Procfile deploy/Procfile - cp -r .ebextensions-dev deploy/.ebextensions - cp -r .platform deploy/.platform - cd deploy && zip -r match-server-api-${{steps.current-time.outputs.formattedTime}}-${{github.sha}} . - - - name: Deploy Consumer to EB - uses: einaregilsson/beanstalk-deploy@v19 - with: - aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - application_name: MatchServerApp - environment_name: match-dev-env - version_label: match-server-api-${{steps.current-time.outputs.formattedTime}}-${{github.sha}} - region: ap-northeast-2 - deployment_package: deploy/match-server-api-${{steps.current-time.outputs.formattedTime}}-${{github.sha}}.zip - wait_for_deployment: false diff --git a/.github/workflows/aligo_api_ci_cd.yml b/.github/workflows/aligo_api_ci_cd.yml index 6b1d35de..afb9aba8 100644 --- a/.github/workflows/aligo_api_ci_cd.yml +++ b/.github/workflows/aligo_api_ci_cd.yml @@ -21,53 +21,61 @@ jobs: java-version: '11' distribution: 'temurin' - - name: Set Environment - Common Yml + - name: Set Environment - Common Prod Yml uses: microsoft/variable-substitution@v1 with: files: ./Match-Common/src/main/resources/application-common.yml env: - oauth.kakao.base-url: ${{ secrets.KAKAO_BASE_URL }} + oauth.kakao.base-url: ${{ secrets.KAKAO_PROD_BASE_URL }} oauth.kakao.client-id: ${{ secrets.KAKAO_CLIENT }} oauth.kakao.client-secret: ${{ secrets.KAKAO_SECRET }} - oauth.kakao.redirect-url: ${{ secrets.KAKAO_REDIRECT }} + oauth.kakao.redirect-url: ${{ secrets.KAKAO_PROD_REDIRECT }} oauth.kakao.app-id: ${{ secrets.KAKAO_APP_ID }} oauth.kakao.admin-key: ${{ secrets.KAKAO_ADMIN_KEY }} jwt.secret: ${{ secrets.JWT_SECRET_KEY }} jwt.refresh: ${{ secrets.JWT_REFRESH_KEY }} oauth.naver.secret: ${{ secrets.NAVER_SECRET }} oauth.naver.client: ${{ secrets.NAVER_CLIENT }} - oauth.naver.redirect-url: ${{ secrets.NAVER_REDIRECT}} + oauth.naver.redirect-url: ${{ secrets.NAVER_PROD_REDIRECT}} sms.secret: ${{ secrets.SMS_SECRET_KEY }} sms.api: ${{ secrets.SMS_API_KEY }} sms.sender: ${{ secrets.SMS_SENDER }} - nice.secret: ${{ secrets.NICE_SECRET_KEY}} - nice.client: ${{ secrets.NICE_CLIENT_KEY }} - nice.url: ${{ secrets.NICE_DEV_URL}} + nice.secret: ${{ secrets.NICE_PROD_SECRET_KEY}} + nice.client: ${{ secrets.NICE_PROD_CLIENT_KEY }} + nice.url: ${{ secrets.NICE_PROD_URL}} aws.access-key: ${{ secrets.AWS_S3_ACCESS_KEY }} aws.secret-key: ${{ secrets.AWS_S3_SECRET_KEY }} aws.s3.bucket: ${{ secrets.AWS_S3_BUCKET }} aws.s3.bucket-url: ${{ secrets.AWS_S3_BASE_URL }} discord.webhook.error: ${{ secrets.DISCORD_WEBHOOK_ERROR}} discord.webhook.alert: ${{ secrets.DISCORD_WEBHOOK_ALERT }} - server.host: ${{ secrets.DEV_SERVER_HOST}} + web.return.url: ${{ secrets.PROD_RETURN_URL }} + match.aligo.url: ${ secrets.MATCH_ALIGO_URL }} + server.host: ${{ secrets.PROD_SERVER_HOST}} spring.password: ${{ secrets.APP_PASSWORD}} aligo.username: ${{ secrets.ALIGO_USERNAME }} aligo.key: ${{ secrets.ALIGO_KEY }} - web.return.url: ${{ secrets.DEV_RETURN_URL }} - match.aligo.url: ${ secrets.MATCH_ALIGO_URL }} portone.code: ${{ secrets.PORTONE_CODE }} portone.key: ${{ secrets.PORTONE_KEY }} portone.secret: ${{ secrets.PORTONE_SECRET }} portone.billmid: ${{ secrets.PROD_PORTONE_BILL_MID}} + private.aes.key: ${{ secrets.AES_PRIVATE_KEY }} + apple.bundle.id: ${{ secrets.APPLE_BUNDLE_ID }} + apple.team.id: ${{ secrets.APPLE_TEAM_ID }} + aligo.sender-key: ${{ secrets.KAKAO_SENDER_KEY }} + - name: Set Environment Domain - Domain Prod Yml uses: microsoft/variable-substitution@v1 with: files: ./Match-Domain/src/main/resources/application-domain-prod.yml env: - spring.datasource.url: ${{ secrets.PROD_DB_URL_HOST }} - spring.datasource.username: ${{ secrets.AWS_DB_USER_NAME }} - spring.datasource.password: ${{ secrets.AWS_DB_PASSWORD }} + spring.datasource.master.hikari.jdbc-url: ${{ secrets.PROD_DB_URL_HOST }} + spring.datasource.master.hikari.username: ${{ secrets.AWS_DB_USER_NAME }} + spring.datasource.master.hikari.password: ${{ secrets.AWS_DB_PASSWORD }} + spring.datasource.slave.hikari.jdbc-url: ${{ secrets.PROD_SLAVE_URL_HOST }} + spring.datasource.slave.hikari.username: ${{ secrets.AWS_DB_USER_NAME }} + spring.datasource.slave.hikari.password: ${{ secrets.AWS_DB_PASSWORD }} spring.redis.host : ${{ secrets.REDIS_PROD_HOST }} - name: create-fcm-json @@ -78,6 +86,10 @@ jobs: json: ${{ secrets.FCM_JSON }} dir: "Match-Common/src/main/resources/" + - name: Create Auth Key File + run: | + echo "${{ secrets.AUTH_KEY }}" > ./Match-Common/src/main/resources/AuthKey_74JQ8SGRU2.p8 + - name: Grant execute permission for gradlew run: chmod +x ./gradlew shell: bash diff --git a/.github/workflows/aligo_docker_ci_cd.yml b/.github/workflows/aligo_docker_ci_cd.yml deleted file mode 100644 index 3bb75b55..00000000 --- a/.github/workflows/aligo_docker_ci_cd.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: aligo MATCH API Server CI/CD - -# 언제 이 파일의 내용이 실행될 것인지 정의합니다. -# 여기서는 main 브랜치에 코드가 push 되거나 pull_request되었을 때 실행할 것을 말하고 있습니다. -on: - push: - branches: [ "aligo2" ] - pull_request: - branches: [ "aligo2" ] - - # 코드의 내용을 이 파일을 실행하여 action을 수행하는 주체(Github Actions에서 사용하는 VM)가 읽을 수 있도록 허용합니다. -permissions: - contents: read - - # 실제 실행될 내용들을 정의합니다. -jobs: - build: - runs-on: ubuntu-latest # ubuntu 최신 버전에서 script를 실행 - steps: # 작업 단계 - - name: Checkout source code # 단계별 이름, 구분자로 소스를 가져옴 - uses: actions/checkout@v2 - - - name: Setup JDK 11 - uses: actions/setup-java@v3 - with: - java-version: '11' - distribution: 'temurin' - - - - - name: Set Environment - Common Prod Yml - uses: microsoft/variable-substitution@v1 - with: - files: ./Match-Common/src/main/resources/application-common.yml - env: - oauth.kakao.base-url: ${{ secrets.KAKAO_PROD_BASE_URL }} - oauth.kakao.client-id: ${{ secrets.KAKAO_CLIENT }} - oauth.kakao.client-secret: ${{ secrets.KAKAO_SECRET }} - oauth.kakao.redirect-url: ${{ secrets.KAKAO_PROD_REDIRECT }} - oauth.kakao.app-id: ${{ secrets.KAKAO_APP_ID }} - oauth.kakao.admin-key: ${{ secrets.KAKAO_ADMIN_KEY }} - jwt.secret: ${{ secrets.JWT_SECRET_KEY }} - jwt.refresh: ${{ secrets.JWT_REFRESH_KEY }} - oauth.naver.secret: ${{ secrets.NAVER_SECRET }} - oauth.naver.client: ${{ secrets.NAVER_CLIENT }} - oauth.naver.redirect-url: ${{ secrets.NAVER_PROD_REDIRECT}} - sms.secret: ${{ secrets.SMS_SECRET_KEY }} - sms.api: ${{ secrets.SMS_API_KEY }} - sms.sender: ${{ secrets.SMS_SENDER }} - nice.secret: ${{ secrets.NICE_PROD_SECRET_KEY}} - nice.client: ${{ secrets.NICE_PROD_CLIENT_KEY }} - nice.url: ${{ secrets.NICE_PROD_URL}} - aws.access-key: ${{ secrets.AWS_S3_ACCESS_KEY }} - aws.secret-key: ${{ secrets.AWS_S3_SECRET_KEY }} - aws.s3.bucket: ${{ secrets.AWS_S3_BUCKET }} - aws.s3.bucket-url: ${{ secrets.AWS_S3_BASE_URL }} - discord.webhook.error: ${{ secrets.DISCORD_WEBHOOK_ERROR}} - discord.webhook.alert: ${{ secrets.DISCORD_WEBHOOK_ALERT }} - server.host: ${{ secrets.DEV_SERVER_HOST}} - spring.password: ${{ secrets.APP_PASSWORD}} - web.return.url: ${{ secrets.PROD_RETURN_URL }} - match.aligo.url: ${ secrets.MATCH_ALIGO_URL }} - aligo.username: ${{ secrets.ALIGO_USERNAME }} - aligo.key: ${{ secrets.ALIGO_KEY }} - aligo.sender: ${{ secrets.SMS_SENDER }} - - - name: Set Environment Domain - Domain Prod Yml - uses: microsoft/variable-substitution@v1 - with: - files: ./Match-Domain/src/main/resources/application-domain-prod.yml - env: - spring.datasource.url: ${{ secrets.PROD_DB_URL_HOST }} - spring.datasource.username: ${{ secrets.AWS_DB_USER_NAME }} - spring.datasource.password: ${{ secrets.AWS_DB_PASSWORD }} - spring.redis.host : ${{ secrets.REDIS_PROD_HOST }} - - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - shell: bash - - - name: Build with Gradle - run: ./gradlew Match-Aligo:bootJar - shell: bash - - - # dockerfile을 통해 이미지를 빌드하고, 이를 docker repo로 push 합니다. - # 이 때 사용되는 ${{ secrets.DOCKER_REPO }}/directors-dev 가 위에서 만든 도커 repository 입니다. - - name: Docker build & push to docker repo - run: | - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker build -f Dockerfile -t ${{ secrets.DOCKER_REPO }}/aligo_match . - docker push ${{ secrets.DOCKER_REPO }}/aligo_match - - # appleboy/ssh-action@master 액션을 사용하여 지정한 서버에 ssh로 접속하고, script를 실행합니다. - # script의 내용은 도커의 기존 프로세스들을 제거하고, docker repo로부터 방금 위에서 push한 내용을 pull 받아 실행하는 것입니다. - # 실행 시, docker-compose를 사용합니다. - - name: Deploy to server - uses: appleboy/ssh-action@master - id: deploy - with: - host: ${{ secrets.HOST }} - username: ubuntu - key: ${{ secrets.KEY }} - envs: GITHUB_SHA - script: | - sudo docker rm -f $(docker ps -qa) - sudo docker pull ${{ secrets.DOCKER_REPO }}/aligo_match - sudo docker compose up -d - sudo docker image prune -f \ No newline at end of file diff --git a/.github/workflows/batch_api_ci_cd.yml b/.github/workflows/batch_api_ci_cd.yml index a807e184..460f6eb1 100644 --- a/.github/workflows/batch_api_ci_cd.yml +++ b/.github/workflows/batch_api_ci_cd.yml @@ -57,6 +57,10 @@ jobs: portone.key: ${{ secrets.PORTONE_KEY }} portone.secret: ${{ secrets.PORTONE_SECRET }} portone.billmid: ${{ secrets.PROD_PORTONE_BILL_MID}} + apple.bundle.id: ${{ secrets.APPLE_BUNDLE_ID }} + apple.team.id: ${{ secrets.APPLE_TEAM_ID }} + aligo.sender-key: ${{ secrets.KAKAO_SENDER_KEY }} + @@ -81,6 +85,11 @@ jobs: json: ${{ secrets.FCM_JSON }} dir: "Match-Common/src/main/resources/" + + - name: Create Auth Key File + run: | + echo "${{ secrets.AUTH_KEY }}" > ./Match-Common/src/main/resources/AuthKey_74JQ8SGRU2.p8 + - name: Grant execute permission for gradlew run: chmod +x ./gradlew shell: bash diff --git a/.github/workflows/dev_api_ci_cd.yml b/.github/workflows/dev_api_ci_cd.yml index 14598de8..7cc20ecb 100644 --- a/.github/workflows/dev_api_ci_cd.yml +++ b/.github/workflows/dev_api_ci_cd.yml @@ -50,7 +50,6 @@ jobs: discord.webhook.error: ${{ secrets.DISCORD_WEBHOOK_ERROR}} discord.webhook.alert: ${{ secrets.DISCORD_WEBHOOK_ALERT }} server.host: ${{ secrets.DEV_SERVER_HOST}} - spring.password: ${{ secrets.APP_PASSWORD}} aligo.username: ${{ secrets.ALIGO_USERNAME }} aligo.key: ${{ secrets.ALIGO_KEY }} @@ -60,6 +59,11 @@ jobs: portone.key: ${{ secrets.PORTONE_KEY }} portone.secret : ${{ secrets.PORTONE_SECRET }} portone.billmid: ${{ secrets.DEV_PORTONE_BILL_MID}} + private.aes.key: ${{ secrets.AES_PRIVATE_KEY }} + apple.bundle.id: ${{ secrets.APPLE_BUNDLE_ID }} + apple.team.id: ${{ secrets.APPLE_TEAM_ID }} + aligo.sender-key: ${{ secrets.KAKAO_SENDER_KEY }} + - name: Set Environment Domain - Domain DEV Yml @@ -73,7 +77,7 @@ jobs: spring.datasource.slave.hikari.jdbc-url: ${{ secrets.DEV_SLAVE_URL_HOST }} spring.datasource.slave.hikari.username: ${{ secrets.AWS_DB_USER_NAME }} spring.datasource.slave.hikari.password: ${{ secrets.AWS_DB_PASSWORD }} - spring.redis.host : ${{ secrets.REDIS_DEV_HOST }} + spring.redis.host : ${{ secrets.REDIS_PROD_HOST }} - name: create-fcm-json id: create-fcm-json @@ -83,6 +87,10 @@ jobs: json: ${{ secrets.FCM_JSON }} dir: "Match-Common/src/main/resources/" + - name: Create Auth Key File + run: | + echo "${{ secrets.AUTH_KEY }}" > ./Match-Common/src/main/resources/AuthKey_74JQ8SGRU2.p8 + - name: Grant execute permission for gradlew run: chmod +x ./gradlew shell: bash diff --git a/.github/workflows/prod_api_ci_cd.yml b/.github/workflows/prod_api_ci_cd.yml index d0000d30..df707a77 100644 --- a/.github/workflows/prod_api_ci_cd.yml +++ b/.github/workflows/prod_api_ci_cd.yml @@ -63,7 +63,10 @@ jobs: portone.key: ${{ secrets.PORTONE_KEY }} portone.secret: ${{ secrets.PORTONE_SECRET }} portone.billmid: ${{ secrets.PROD_PORTONE_BILL_MID}} - + private.aes.key: ${{ secrets.AES_PRIVATE_KEY }} + apple.bundle.id: ${{ secrets.APPLE_BUNDLE_ID }} + apple.team.id: ${{ secrets.APPLE_TEAM_ID }} + aligo.sender-key: ${{ secrets.KAKAO_SENDER_KEY }} - name: Set Environment Domain - Domain Prod Yml @@ -87,6 +90,10 @@ jobs: json: ${{ secrets.FCM_JSON }} dir: "Match-Common/src/main/resources/" + - name: Create Auth Key File + run: | + echo "${{ secrets.AUTH_KEY }}" > ./Match-Common/src/main/resources/AuthKey_74JQ8SGRU2.p8 + - name: Grant execute permission for gradlew run: chmod +x ./gradlew shell: bash diff --git a/.gitignore b/.gitignore index 1b64d05c..a8814bca 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ out/ ### VS Code ### .vscode/ -Match-Common/src/main/resources/serviceAccountKey.json \ No newline at end of file +Match-Common/src/main/resources/serviceAccountKey.json +Match-Common/src/main/resources/AuthKey_74JQ8SGRU2.p8 \ No newline at end of file diff --git a/Admin-Api/src/main/java/com/example/adminapi/user/convertor/UserConvertor.java b/Admin-Api/src/main/java/com/example/adminapi/user/convertor/UserConverter.java similarity index 90% rename from Admin-Api/src/main/java/com/example/adminapi/user/convertor/UserConvertor.java rename to Admin-Api/src/main/java/com/example/adminapi/user/convertor/UserConverter.java index 2a21c17e..0f977cec 100644 --- a/Admin-Api/src/main/java/com/example/adminapi/user/convertor/UserConvertor.java +++ b/Admin-Api/src/main/java/com/example/adminapi/user/convertor/UserConverter.java @@ -1,11 +1,11 @@ -package com.example.adminapi.user.convertor; +package com.example.adminapi.user.converter; import com.example.adminapi.user.dto.UserRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.user.repository.UserRepository; -@Convertor -public class UserConvertor { +@Converter +public class UserConverter { public UserRes.SignUpInfo UserSignUpInfo(Long oneDayUser, Long weekUser, Long monthUser, Long totalUser) { return UserRes.SignUpInfo.builder() .totalUserCnt(totalUser) diff --git a/Admin-Api/src/main/java/com/example/adminapi/user/service/AuthService.java b/Admin-Api/src/main/java/com/example/adminapi/user/service/AuthService.java index 8701c6a8..1c1e06a0 100644 --- a/Admin-Api/src/main/java/com/example/adminapi/user/service/AuthService.java +++ b/Admin-Api/src/main/java/com/example/adminapi/user/service/AuthService.java @@ -11,7 +11,6 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import static com.example.matchdomain.user.exception.AdminLoginErrorCode.NOT_AUTHORITY_USER; import static com.example.matchdomain.user.exception.UserAuthErrorCode.NOT_EXIST_USER; import static com.example.matchdomain.user.exception.UserLoginErrorCode.NOT_CORRECT_PASSWORD; diff --git a/Admin-Api/src/main/java/com/example/adminapi/user/service/UserService.java b/Admin-Api/src/main/java/com/example/adminapi/user/service/UserService.java index fca7b553..883d38fa 100644 --- a/Admin-Api/src/main/java/com/example/adminapi/user/service/UserService.java +++ b/Admin-Api/src/main/java/com/example/adminapi/user/service/UserService.java @@ -1,6 +1,6 @@ package com.example.adminapi.user.service; -import com.example.adminapi.user.convertor.UserConvertor; +import com.example.adminapi.user.converter.UserConverter; import com.example.adminapi.user.dto.UserRes; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.user.repository.UserRepository; @@ -21,7 +21,7 @@ @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; - private final UserConvertor userConvertor; + private final UserConverter userConverter; private static final String FIRST_TIME = "T00:00:00"; private static final String LAST_TIME = "T23:59:59"; public UserRes.SignUpInfo getUserSignUpInfo() { @@ -32,7 +32,7 @@ public UserRes.SignUpInfo getUserSignUpInfo() { Long weekUser = userRepository.countByCreatedAtGreaterThanAndCreatedAtLessThan(LocalDateTime.parse(localDate.minusWeeks(1)+FIRST_TIME) , LocalDateTime.parse(localDate+LAST_TIME)); Long monthUser = userRepository.countByCreatedAtGreaterThanAndCreatedAtLessThan(LocalDateTime.parse(localDate.with(TemporalAdjusters.firstDayOfMonth())+FIRST_TIME), LocalDateTime.parse(localDate.with(TemporalAdjusters.lastDayOfMonth())+LAST_TIME)); - return userConvertor.UserSignUpInfo(oneDayUser,weekUser,monthUser,totalUser); + return userConverter.UserSignUpInfo(oneDayUser,weekUser,monthUser,totalUser); } public PageResponse> getUserList(int page, int size) { @@ -44,7 +44,7 @@ public PageResponse> getUserList(int page, int size) { userList.getContent().forEach( result -> userLists.add( - userConvertor.UserList(result) + userConverter.UserList(result) ) ); diff --git a/Match-Aligo/src/main/java/com/example/matchaligo/aligo/controller/AligoController.java b/Match-Aligo/src/main/java/com/example/matchaligo/aligo/controller/AligoController.java index daec518a..7c359883 100644 --- a/Match-Aligo/src/main/java/com/example/matchaligo/aligo/controller/AligoController.java +++ b/Match-Aligo/src/main/java/com/example/matchaligo/aligo/controller/AligoController.java @@ -1,17 +1,20 @@ package com.example.matchaligo.aligo.controller; -import com.example.matchaligo.aligo.service.AligoService; +import com.example.matchaligo.aligo.dto.AligoReq; import com.example.matchcommon.annotation.ApiErrorCodeExample; import com.example.matchcommon.exception.errorcode.RequestErrorCode; import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchinfrastructure.aligo.dto.AlimTalkReq; +import com.example.matchinfrastructure.aligo.dto.AlimType; +import com.example.matchinfrastructure.aligo.service.AligoInfraService; +import com.example.matchinfrastructure.match_aligo.dto.AlimTalkDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequiredArgsConstructor @@ -19,15 +22,28 @@ @Tag(name = "ALIGO 문자 관련 API") @RequestMapping("/send") public class AligoController { - private final AligoService aligoService; + private final AligoInfraService aligoInfraService; @ApiErrorCodeExample(RequestErrorCode.class) @Operation(summary= "01-01🔑 회원 문자인증 요청", description = "회원 문자인증 용 API 입니다.") @GetMapping(value="") public CommonResponse checkPhone(@RequestParam String phone, @RequestParam String code){ log.info("01-01 비회원 문자인증 = " + phone); - aligoService.sendPhone(phone, code); + aligoInfraService.sendPhone(phone, code); return CommonResponse.onSuccess("문자 전송 성공"); } + @PostMapping("/alim-talk") + public CommonResponse sendAlimTalk(@RequestParam AlimType alimType, @RequestBody AlimTalkDto alimTalk){ + aligoInfraService.sendAlimTalk(alimTalk, alimType); + return CommonResponse.onSuccess("알림톡 전송 성공"); + } + + @PostMapping("/alim-talk-execution") + public CommonResponse sendAlimTalks(@RequestParam AlimType alimType, @RequestBody List alimTalks){ + aligoInfraService.sendAlimTalks(alimTalks, alimType); + return CommonResponse.onSuccess("알림톡 전송 성공"); + } + + } diff --git a/Match-Aligo/src/main/java/com/example/matchaligo/aligo/convertor/AligoConvertor.java b/Match-Aligo/src/main/java/com/example/matchaligo/aligo/convertor/AligoConvertor.java deleted file mode 100644 index bb8496f4..00000000 --- a/Match-Aligo/src/main/java/com/example/matchaligo/aligo/convertor/AligoConvertor.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.example.matchaligo.aligo.convertor; - -import com.example.matchcommon.annotation.Convertor; - -@Convertor -public class AligoConvertor { -} diff --git a/Match-Aligo/src/main/java/com/example/matchaligo/aligo/service/AligoService.java b/Match-Aligo/src/main/java/com/example/matchaligo/aligo/service/AligoService.java deleted file mode 100644 index b5b68b80..00000000 --- a/Match-Aligo/src/main/java/com/example/matchaligo/aligo/service/AligoService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.matchaligo.aligo.service; - -import com.example.matchcommon.properties.AligoProperties; -import com.example.matchdomain.redis.entity.CodeAuth; -import com.example.matchinfrastructure.aligo.client.AligoFeignClient; -import com.example.matchinfrastructure.aligo.dto.SendRes; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.Random; - -@Service -@RequiredArgsConstructor -public class AligoService { - private final AligoFeignClient aligoFeignClient; - private final AligoProperties aligoProperties; - - public void sendPhone(String phone, String code) { - String msg = "[MATCH] 회원님의 인증번호는 [" + code + "] 입니다."; - SendRes sendRes = aligoFeignClient.sendOneMsg(aligoProperties.getKey(), aligoProperties.getUsername(), - aligoProperties.getSender(), phone, msg); - System.out.println(sendRes.getResultCode()); - System.out.println(sendRes.getMessage()); - } - -} diff --git a/Match-Aligo/src/main/resources/application.yml b/Match-Aligo/src/main/resources/application.yml index 5586a95a..6c4c0135 100644 --- a/Match-Aligo/src/main/resources/application.yml +++ b/Match-Aligo/src/main/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 9000 + port: 9001 servlet: context-path: / diff --git a/Match-Api/src/main/generated/com/example/matchapi/admin/notice/mapper/AdminNoticeMapperImpl.java b/Match-Api/src/main/generated/com/example/matchapi/admin/notice/mapper/AdminNoticeMapperImpl.java new file mode 100644 index 00000000..a6f654b8 --- /dev/null +++ b/Match-Api/src/main/generated/com/example/matchapi/admin/notice/mapper/AdminNoticeMapperImpl.java @@ -0,0 +1,58 @@ +package com.example.matchapi.admin.notice.mapper; + +import com.example.matchapi.admin.notice.dto.NoticeUploadReq; +import com.example.matchapi.common.model.ContentsList; +import com.example.matchdomain.notice.entity.Notice; +import com.example.matchdomain.notice.entity.NoticeContent; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-12-05T09:50:48+0900", + comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.19 (Oracle Corporation)" +) +public class AdminNoticeMapperImpl implements AdminNoticeMapper { + + @Override + public List toEntityNoticeContents(List contentsList) { + if ( contentsList == null ) { + return null; + } + + List list = new ArrayList( contentsList.size() ); + for ( ContentsList contentsList1 : contentsList ) { + list.add( contentsListToNoticeContent( contentsList1 ) ); + } + + return list; + } + + @Override + public Notice toEntityNotice(NoticeUploadReq noticeUploadReq) { + if ( noticeUploadReq == null ) { + return null; + } + + Notice.NoticeBuilder notice = Notice.builder(); + + notice.noticeType( noticeUploadReq.getNoticeType() ); + notice.title( noticeUploadReq.getTitle() ); + + return notice.build(); + } + + protected NoticeContent contentsListToNoticeContent(ContentsList contentsList) { + if ( contentsList == null ) { + return null; + } + + NoticeContent.NoticeContentBuilder noticeContent = NoticeContent.builder(); + + noticeContent.contentsType( contentsList.getContentsType() ); + noticeContent.contents( contentsList.getContents() ); + + return noticeContent.build(); + } +} diff --git a/Match-Api/src/main/generated/com/example/matchapi/order/mapper/OrderMapperImpl.java b/Match-Api/src/main/generated/com/example/matchapi/order/mapper/OrderMapperImpl.java new file mode 100644 index 00000000..5bc23f92 --- /dev/null +++ b/Match-Api/src/main/generated/com/example/matchapi/order/mapper/OrderMapperImpl.java @@ -0,0 +1,54 @@ +package com.example.matchapi.order.mapper; + +import com.example.matchapi.order.dto.OrderCommand; +import com.example.matchapi.order.dto.OrderReq; +import com.example.matchdomain.donation.entity.UserCard; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.user.entity.User; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-12-05T09:50:48+0900", + comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.19 (Oracle Corporation)" +) +public class OrderMapperImpl implements OrderMapper { + + @Override + public OrderCommand.OneTimeDonation toOneTimeDonation(UserCard userCard, OrderReq.OneTimeDonation oneTimeDonation, User user, Project project, String orderId) { + if ( userCard == null && oneTimeDonation == null && user == null && project == null && orderId == null ) { + return null; + } + + OrderCommand.OneTimeDonation.OneTimeDonationBuilder oneTimeDonation1 = OrderCommand.OneTimeDonation.builder(); + + if ( userCard != null ) { + oneTimeDonation1.userCard( userCard ); + oneTimeDonation1.user( userCard.getUser() ); + } + oneTimeDonation1.oneTimeDonation( oneTimeDonation ); + oneTimeDonation1.project( project ); + oneTimeDonation1.orderId( orderId ); + + return oneTimeDonation1.build(); + } + + @Override + public OrderCommand.RegularDonation toRegularDonation(UserCard userCard, OrderReq.RegularDonation regularDonation, User user, Project project, String orderId) { + if ( userCard == null && regularDonation == null && user == null && project == null && orderId == null ) { + return null; + } + + OrderCommand.RegularDonation.RegularDonationBuilder regularDonation1 = OrderCommand.RegularDonation.builder(); + + if ( userCard != null ) { + regularDonation1.userCard( userCard ); + regularDonation1.user( userCard.getUser() ); + } + regularDonation1.regularDonation( regularDonation ); + regularDonation1.project( project ); + regularDonation1.orderId( orderId ); + + return regularDonation1.build(); + } +} diff --git a/Match-Api/src/main/generated/com/example/matchapi/portone/mapper/PaymentMapperImpl.java b/Match-Api/src/main/generated/com/example/matchapi/portone/mapper/PaymentMapperImpl.java new file mode 100644 index 00000000..9330c095 --- /dev/null +++ b/Match-Api/src/main/generated/com/example/matchapi/portone/mapper/PaymentMapperImpl.java @@ -0,0 +1,69 @@ +package com.example.matchapi.portone.mapper; + +import com.example.matchapi.order.dto.OrderRes; +import com.example.matchapi.portone.dto.PaymentCommand; +import com.example.matchapi.portone.dto.PaymentReq; +import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.redis.entity.OrderRequest; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.pay.portone.dto.PortOneWebhook; +import java.time.LocalDate; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-12-05T09:50:48+0900", + comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.19 (Oracle Corporation)" +) +public class PaymentMapperImpl implements PaymentMapper { + + @Override + public PaymentCommand.PaymentValidation toPaymentValidationCommand(OrderRequest orderRequest, User user, Project project, PaymentReq.ValidatePayment validatePayment) { + if ( orderRequest == null && user == null && project == null && validatePayment == null ) { + return null; + } + + PaymentCommand.PaymentValidation.PaymentValidationBuilder paymentValidation = PaymentCommand.PaymentValidation.builder(); + + paymentValidation.orderRequest( orderRequest ); + paymentValidation.user( user ); + paymentValidation.project( project ); + paymentValidation.validatePayment( validatePayment ); + + return paymentValidation.build(); + } + + @Override + public PaymentCommand.PaymentValidation toCheckValidationCommand(OrderRequest orderRequest, User user, Project project, PortOneWebhook portOneWebhook) { + if ( orderRequest == null && user == null && project == null && portOneWebhook == null ) { + return null; + } + + PaymentCommand.PaymentValidation.PaymentValidationBuilder paymentValidation = PaymentCommand.PaymentValidation.builder(); + + paymentValidation.orderRequest( orderRequest ); + paymentValidation.user( user ); + paymentValidation.project( project ); + + return paymentValidation.build(); + } + + @Override + public OrderRes.PaymentInfoDto toPaymentInfoDto(String name, LocalDate birth, String phone, String usages, RegularStatus regularStatus, String accessToken) { + if ( name == null && birth == null && phone == null && usages == null && regularStatus == null && accessToken == null ) { + return null; + } + + OrderRes.PaymentInfoDto.PaymentInfoDtoBuilder paymentInfoDto = OrderRes.PaymentInfoDto.builder(); + + paymentInfoDto.name( name ); + paymentInfoDto.birth( birth ); + paymentInfoDto.phone( phone ); + paymentInfoDto.usages( usages ); + paymentInfoDto.regularStatus( regularStatus ); + paymentInfoDto.accessToken( accessToken ); + + return paymentInfoDto.build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/auth/controller/AdminAuthController.java b/Match-Api/src/main/java/com/example/matchapi/admin/auth/controller/AdminAuthController.java index ceb0123f..abb000da 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/auth/controller/AdminAuthController.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/auth/controller/AdminAuthController.java @@ -4,10 +4,10 @@ import com.example.matchapi.user.dto.UserRes; import com.example.matchapi.user.service.AuthService; import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.annotation.DisableSecurity; import com.example.matchcommon.exception.errorcode.RequestErrorCode; import com.example.matchcommon.reponse.CommonResponse; import com.example.matchdomain.user.exception.AdminLoginErrorCode; -import com.example.matchdomain.user.exception.UserLoginErrorCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -27,6 +27,7 @@ public class AdminAuthController { private final AuthService authService; @ApiErrorCodeExample({AdminLoginErrorCode.class, RequestErrorCode.class}) + @DisableSecurity @Operation(summary="ADMIN-00-01🔑 관리자 로그인", description= "회원가입 용 API 입니다.") @PostMapping(value="/logIn") public CommonResponse logIn(@RequestBody @Valid UserReq.LogIn logIn){ diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/banner/controller/AdminBannerController.java b/Match-Api/src/main/java/com/example/matchapi/admin/banner/controller/AdminBannerController.java index 1cb03655..485874c6 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/banner/controller/AdminBannerController.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/banner/controller/AdminBannerController.java @@ -6,14 +6,19 @@ import com.example.matchcommon.reponse.CommonResponse; import com.example.matchdomain.banner.enums.BannerType; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.util.List; + @RestController @RequiredArgsConstructor @Slf4j @@ -21,14 +26,14 @@ @Tag(name = "ADMIN-08-Banner💳 관리자 배너 관리 API") public class AdminBannerController { private final AdminBannerService adminBannerService; - @PostMapping("") + @PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @Operation(summary = "ADMIN-08-01 배너 업로드") public CommonResponse> uploadBanner( - @RequestParam BannerType bannerType, + //@RequestPart BannerType bannerType, @RequestPart MultipartFile bannerImage, @RequestPart BannerReq.BannerUpload bannerUploadDto ){ System.out.println(bannerUploadDto.getContentsUrl()); - return CommonResponse.onSuccess(adminBannerService.uploadBanner(bannerType, bannerImage, bannerUploadDto)); + return CommonResponse.onSuccess(adminBannerService.uploadBanner(BannerType.EVENT, bannerImage, bannerUploadDto)); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/banner/service/AdminBannerService.java b/Match-Api/src/main/java/com/example/matchapi/admin/banner/service/AdminBannerService.java index 1375439d..524f4af2 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/banner/service/AdminBannerService.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/banner/service/AdminBannerService.java @@ -1,6 +1,6 @@ package com.example.matchapi.admin.banner.service; -import com.example.matchapi.banner.convertor.BannerConvertor; +import com.example.matchapi.banner.converter.BannerConverter; import com.example.matchapi.banner.dto.BannerReq; import com.example.matchapi.banner.dto.BannerRes; import com.example.matchdomain.banner.adaptor.BannerAdaptor; @@ -21,7 +21,7 @@ @RequiredArgsConstructor public class AdminBannerService { private final BannerRepository bannerRepository; - private final BannerConvertor bannerConvertor; + private final BannerConverter bannerConverter; private final S3UploadService s3UploadService; private final BannerAdaptor bannerAdaptor; @@ -31,7 +31,7 @@ public List uploadBanner(BannerType bannerType, MultipartFile bannerImage, BannerReq.BannerUpload bannerUploadDto) { String bannerImg = s3UploadService.uploadBannerImage(bannerImage); - bannerRepository.save(bannerConvertor.convertToBannerUpload(bannerType, bannerImg, bannerUploadDto)); + bannerRepository.save(bannerConverter.convertToBannerUpload(bannerType, bannerImg, bannerUploadDto)); return cachingBannerList(); } @@ -39,6 +39,6 @@ public List uploadBanner(BannerType bannerType, public List cachingBannerList() { List banners = bannerAdaptor.getBannerList(); - return bannerConvertor.convertToBannerList(banners); + return bannerConverter.convertToBannerList(banners); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/donation/controller/AdminDonationController.java b/Match-Api/src/main/java/com/example/matchapi/admin/donation/controller/AdminDonationController.java index e0b28bd5..06691b62 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/donation/controller/AdminDonationController.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/donation/controller/AdminDonationController.java @@ -4,9 +4,12 @@ import com.example.matchapi.donation.dto.DonationReq; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.service.DonationService; +import com.example.matchapi.project.service.ProjectService; import com.example.matchcommon.annotation.ApiErrorCodeExample; import com.example.matchcommon.exception.errorcode.RequestErrorCode; import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.user.exception.UserAuthErrorCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -27,6 +30,7 @@ @Slf4j public class AdminDonationController { private final AdminDonationService adminDonationService; + private final ProjectService projectService; @GetMapping("") @ApiErrorCodeExample(UserAuthErrorCode.class) @Operation(summary = "ADMIN-05-01💸 기부금 현황파악 API.",description = "기부금 현황파악 API 입니다.") @@ -42,9 +46,21 @@ public CommonResponse getDonationDetail(@PathVariabl return CommonResponse.onSuccess(adminDonationService.getDonationDetail(donationId)); } + /*@PostMapping("/complete") + @ApiErrorCodeExample({UserAuthErrorCode.class, RequestErrorCode.class}) + @Operation(summary = "ADMIN-05-03 기부금 집행 중 API POST API", description = "기부금 집행 중 API") + public CommonResponse postExecution( + @RequestBody DonationReq.EnforceDonation enforceDonation + ){ + adminDonationService.postExecution(enforceDonation); + return CommonResponse.onSuccess("집행 중 으로 변환 성공"); + } + */ + + @PostMapping(value = "/enforce", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @ApiErrorCodeExample({UserAuthErrorCode.class, RequestErrorCode.class}) - @Operation(summary = "ADMIN-05-03 기부금 집행 전달완료 POST API", description = "기부금 집행 API") + @Operation(summary = "ADMIN-05-04 기부금 전달완료 POST API", description = "기부금 집행 API") public CommonResponse enforceDonation( @RequestPart("imageLists") List imageLists, @Parameter( @@ -55,4 +71,24 @@ public CommonResponse enforceDonation( adminDonationService.enforceDonation(imageLists, enforceDonation); return CommonResponse.onSuccess("성공"); } + + @GetMapping("/execution") + @Operation(summary = "기부금 전반 내용 확인") + public CommonResponse>> getProjectDonationStatus( + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "5") int size + ){ + return CommonResponse.onSuccess(adminDonationService.getProjectDonationStatus(page, size)); + } + + @GetMapping("/execution/{projectId}") + @Operation(summary = "기부금 리스트 확인") + public CommonResponse>> getProjectDonationLists( + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "5") int size, + @PathVariable("projectId") Long projectId + ){ + Project project = projectService.findByProjectId(projectId); + return CommonResponse.onSuccess(adminDonationService.getProjectDonationLists(project, page, size)); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/donation/converter/AdminDonationConverter.java b/Match-Api/src/main/java/com/example/matchapi/admin/donation/converter/AdminDonationConverter.java new file mode 100644 index 00000000..e527a3d8 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/donation/converter/AdminDonationConverter.java @@ -0,0 +1,130 @@ +package com.example.matchapi.admin.donation.converter; + +import com.example.matchapi.donation.dto.DonationReq; +import com.example.matchapi.donation.dto.DonationRes; +import com.example.matchcommon.annotation.Converter; +import com.example.matchdomain.donation.dto.DonationExecutionDto; +import com.example.matchdomain.donation.entity.DonationHistory; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.HistoryImage; +import com.example.matchdomain.donation.entity.enums.DonationStatus; +import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import com.example.matchdomain.project.entity.Project; + +import java.util.ArrayList; +import java.util.List; + +@Converter +public class AdminDonationConverter { + public DonationRes.DonationDetail getDonationDetail(DonationUser donationUser) { + return DonationRes.DonationDetail + .builder() + .donationId(donationUser.getId()) + .userId(donationUser.getUserId()) + .name(donationUser.getUser().getName()) + .email(donationUser.getUser().getEmail()) + .phoneNumber(donationUser.getUser().getPhoneNumber()) + .amount(donationUser.getPrice()) + .inherenceName(donationUser.getInherenceName()) + .inherenceNumber(donationUser.getInherenceNumber()) + .payMethod(donationUser.getPayMethod().getValue()) + .donationStatus(donationUser.getDonationStatus()) + .regularStatus(donationUser.getRegularStatus().getName()) + .donationDate(donationUser.getCreatedAt().toString()) + .build(); + } + + public DonationHistory convertToDonationHistoryComplete(Long projectId, List donationUserLists, List item) { + return DonationHistory + .builder() + .projectId(projectId) + .historyStatus(HistoryStatus.COMPLETE) + .completeIdLists(donationUserLists) + .item(item.toString()) + .build(); + } + + public HistoryImage convertToHistoryImage(String image, Long id) { + return HistoryImage + .builder() + .imgUrl(image) + .donationHistoryId(id) + .build(); + } + + public DonationHistory convertToDonationHistoryChange(DonationReq.EnforceDonation enforceDonation) { + return DonationHistory + .builder() + .projectId(enforceDonation.getProjectId()) + .cnt(enforceDonation.getDonationUserLists().size()) + .historyStatus(HistoryStatus.CHANGE) + .changeIdLists(enforceDonation.getDonationUserLists()) + .build(); + } + + /* public List convertToProjectDonationStatus(List projects) { + List projectDonations = new ArrayList<>(); + projects.forEach( + result -> { + projectDonations.add(convertToStatusDetail(result)); + } + ); + return projectDonations; + }*/ + + public DonationRes.ProjectDonationStatus convertToStatusDetail(List donationUsers, Project project) { + int totalAmount = 0; + int waitingSortingAmount = 0; + int completeAmount = 0; + + for(DonationExecutionDto donationUser : donationUsers){ + System.out.println(completeAmount); + if(donationUser.getDonationStatus().equals(DonationStatus.EXECUTION_SUCCESS)){ + completeAmount+=donationUser.getExecutionPrice(); + } + if(donationUser.getDonationStatus().equals(DonationStatus.PARTIAL_EXECUTION)){ + completeAmount += donationUser.getExecutionPrice(); + waitingSortingAmount += donationUser.getPrice() - donationUser.getExecutionPrice(); + } + if(donationUser.getDonationStatus().equals(DonationStatus.EXECUTION_BEFORE)){ + waitingSortingAmount += donationUser.getPrice(); + } + totalAmount += donationUser.getPrice(); + } + return DonationRes.ProjectDonationStatus + .builder() + .projectId(project.getId()) + .usages(project.getUsages()) + .totalAmount(totalAmount) + .waitingSortingAmount(waitingSortingAmount) + .importedAmount((int) (totalAmount*0.1)) + .completeAmount(completeAmount) + .build(); + } + + public List convertToDonationLists(List content) { + List dtos = new ArrayList<>(); + + content.forEach( + result -> { + dtos.add(convertToDonationInfo(result)); + } + ); + return dtos; + } + + private DonationRes.ProjectDonationDto convertToDonationInfo(DonationUser result) { + return DonationRes.ProjectDonationDto + .builder() + .donationId(result.getId()) + .donationDate(result.getCreatedAt()) + .donationStatusName(result.getDonationStatus().getName()) + .donationStatus(result.getDonationStatus()) + .userId(result.getUserId()) + .userName(result.getUser().getName()) + .amount(result.getPrice()) + .importedAmount((int) (result.getPrice()*0.1)) + .waitingSortingAmount(result.getDonationStatus().equals(DonationStatus.PARTIAL_EXECUTION) ? result.getExecutionPrice() : (long) (result.getPrice() * 0.9)) + .build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/donation/convertor/AdminDonationConvertor.java b/Match-Api/src/main/java/com/example/matchapi/admin/donation/convertor/AdminDonationConvertor.java deleted file mode 100644 index eb5dd3a2..00000000 --- a/Match-Api/src/main/java/com/example/matchapi/admin/donation/convertor/AdminDonationConvertor.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.example.matchapi.admin.donation.convertor; - -import com.example.matchapi.donation.dto.DonationReq; -import com.example.matchapi.donation.dto.DonationRes; -import com.example.matchcommon.annotation.Convertor; -import com.example.matchdomain.donation.entity.DonationHistory; -import com.example.matchdomain.donation.entity.DonationUser; -import com.example.matchdomain.donation.entity.HistoryImage; -import com.example.matchdomain.donation.entity.enums.HistoryStatus; - -import java.util.List; - -@Convertor -public class AdminDonationConvertor { - public DonationRes.DonationDetail getDonationDetail(DonationUser donationUser) { - return DonationRes.DonationDetail - .builder() - .donationId(donationUser.getId()) - .userId(donationUser.getUserId()) - .name(donationUser.getUser().getName()) - .email(donationUser.getUser().getEmail()) - .phoneNumber(donationUser.getUser().getPhoneNumber()) - .amount(donationUser.getPrice()) - .inherenceName(donationUser.getInherenceName()) - .inherenceNumber(donationUser.getInherenceNumber()) - .payMethod(donationUser.getPayMethod().getValue()) - .donationStatus(donationUser.getDonationStatus()) - .regularStatus(donationUser.getRegularStatus().getName()) - .donationDate(donationUser.getCreatedAt().toString()) - .build(); - } - - public DonationHistory convertToDonationHistoryComplete(Long projectId, List donationUserLists) { - return DonationHistory - .builder() - .projectId(projectId) - .historyStatus(HistoryStatus.COMPLETE) - .completeIdLists(donationUserLists) - .build(); - } - - public HistoryImage convertToHistoryImage(String image, Long id) { - return HistoryImage - .builder() - .imgUrl(image) - .donationHistoryId(id) - .build(); - } - - public DonationHistory convertToDonationHistoryChange(DonationReq.EnforceDonation enforceDonation) { - return DonationHistory - .builder() - .projectId(enforceDonation.getProjectId()) - .cnt(enforceDonation.getDonationUserLists().size()) - .historyStatus(HistoryStatus.CHANGE) - .changeIdLists(enforceDonation.getDonationUserLists()) - .build(); - } -} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java b/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java index 69349589..444be6a8 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java @@ -1,18 +1,24 @@ package com.example.matchapi.admin.donation.service; -import com.example.matchapi.admin.donation.convertor.AdminDonationConvertor; +import com.example.matchapi.admin.donation.converter.AdminDonationConverter; +import com.example.matchapi.common.lisetner.ExecutionEvent; import com.example.matchapi.donation.dto.DonationReq; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; -import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.adaptor.DonationHistoryAdaptor; import com.example.matchdomain.donation.entity.DonationHistory; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.HistoryImage; -import com.example.matchdomain.donation.repository.DonationHistoryRepository; -import com.example.matchdomain.donation.repository.DonationUserRepository; import com.example.matchdomain.donation.repository.HistoryImageRepository; +import com.example.matchdomain.project.adaptor.ProjectAdaptor; +import com.example.matchdomain.project.entity.Project; import com.example.matchinfrastructure.config.s3.S3UploadService; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -22,29 +28,31 @@ import java.time.temporal.TemporalAdjusters; import java.util.ArrayList; import java.util.List; -import java.util.Optional; +import java.util.stream.Collectors; import static com.example.matchcommon.constants.MatchStatic.FIRST_TIME; import static com.example.matchcommon.constants.MatchStatic.LAST_TIME; -import static com.example.matchdomain.donation.entity.enums.DonationStatus.EXECUTION_REFUND; -import static com.example.matchdomain.donation.entity.enums.DonationStatus.EXECUTION_SUCCESS; -import static com.example.matchdomain.donation.exception.DonationRefundErrorCode.DONATION_NOT_EXIST; +import static com.example.matchdomain.donation.entity.enums.DonationStatus.*; @Service @RequiredArgsConstructor public class AdminDonationService { - private final DonationUserRepository donationUserRepository; + private final DonationAdaptor donationAdaptor; + private final DonationHistoryAdaptor donationHistoryAdaptor; private final DonationHelper donationHelper; - private final AdminDonationConvertor adminDonationConvertor; - private final DonationHistoryRepository donationHistoryRepository; + private final AdminDonationConverter adminDonationConverter; private final S3UploadService s3UploadService; + private final ProjectAdaptor projectAdaptor; private final HistoryImageRepository historyImageRepository; + @Autowired + private ApplicationEventPublisher eventPublisher; + @Transactional public DonationRes.DonationInfo getDonationInfo() { LocalDate localDate = LocalDate.now(); - List donationUsers = donationUserRepository.findByDonationStatusNot(EXECUTION_REFUND); + List donationUsers = donationAdaptor.findByDonationNotRefund(); int oneDayDonationAmount = 0; int weekendDonationAmount = 0; @@ -65,29 +73,79 @@ public DonationRes.DonationInfo getDonationInfo() { } public DonationRes.DonationDetail getDonationDetail(Long donationId) { - DonationUser donationUser = donationUserRepository.findById(donationId).orElseThrow(()-> new BadRequestException(DONATION_NOT_EXIST)); - return adminDonationConvertor.getDonationDetail(donationUser); + DonationUser donationUser = donationAdaptor.findById(donationId); + return adminDonationConverter.getDonationDetail(donationUser); } + @Transactional public void enforceDonation(List imageLists, DonationReq.EnforceDonation enforceDonation) { - donationHistoryRepository.save(adminDonationConvertor.convertToDonationHistoryChange(enforceDonation)); + List someExecutionIds = getSomeExecutionIds(enforceDonation.getSomeExecutions()); + List allDonationUserIds = new ArrayList<>(enforceDonation.getDonationUserLists()); - DonationHistory donationHistory = donationHistoryRepository.save(adminDonationConvertor.convertToDonationHistoryComplete(enforceDonation.getProjectId(), enforceDonation.getDonationUserLists())); + DonationHistory donationHistory = donationHistoryAdaptor.saveDonationHistory( + adminDonationConverter.convertToDonationHistoryComplete(enforceDonation.getProjectId(), allDonationUserIds, enforceDonation.getItem())); saveDonationHistoryImages(imageLists, donationHistory.getId()); - executionSuccessDonation(enforceDonation.getDonationUserLists()); + List donationUsers = new ArrayList<>(); + + donationUsers.addAll(executePartialDonations(enforceDonation.getSomeExecutions())); + donationUsers.addAll(executeSuccessfulDonations(excludeSomeExecutionIds(allDonationUserIds, someExecutionIds))); + + donationAdaptor.saveAll(donationUsers); + + Project project = projectAdaptor.findById(enforceDonation.getProjectId()); + + ExecutionEvent event = new ExecutionEvent(this, donationUsers, project, enforceDonation.getItem()); + + eventPublisher.publishEvent(event); + } + + private List getSomeExecutionIds(List someExecutions) { + return someExecutions.stream() + .map(DonationReq.SomeExecution::getDonationUserId) + .collect(Collectors.toList()); } - private void executionSuccessDonation(List donationUserLists) { - List donationUsers = donationUserRepository.findByIdIn(donationUserLists); - for(DonationUser donationUser : donationUsers){ - donationUser.setDonationStatus(EXECUTION_SUCCESS); + private List executePartialDonations(List someExecutions) { + List someExecutionIds = getSomeExecutionIds(someExecutions); + List partialDonationUsers = donationAdaptor.findByListIn(someExecutionIds); + + for (DonationUser donationUser : partialDonationUsers) { + if(!donationUser.getDonationStatus().equals(EXECUTION_SUCCESS)) { + DonationReq.SomeExecution execution = findSomeExecutionByUserId(someExecutions, donationUser.getId()); + donationUser.updateDonationExecution(PARTIAL_EXECUTION, execution.getAmount()); + } } - donationUserRepository.saveAll(donationUsers); + return partialDonationUsers; + } + + private List executeSuccessfulDonations(List donationUserIds) { + List successfulDonationUsers = donationAdaptor.findByListIn(donationUserIds); + + for (DonationUser donationUser : successfulDonationUsers) { + if(!donationUser.getDonationStatus().equals(EXECUTION_SUCCESS)) { + donationUser.updateDonationExecution(EXECUTION_SUCCESS, (long) (donationUser.getPrice() * 0.9)); + } + } + return successfulDonationUsers; + } + + private DonationReq.SomeExecution findSomeExecutionByUserId(List someExecutions, Long userId) { + return someExecutions.stream() + .filter(execution -> execution.getDonationUserId().equals(userId)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); + } + + + private List excludeSomeExecutionIds(List allIds, List excludeIds) { + return allIds.stream() + .filter(id -> !excludeIds.contains(id)) + .collect(Collectors.toList()); } private void saveDonationHistoryImages(List imageLists, Long historyId) { @@ -96,8 +154,31 @@ private void saveDonationHistoryImages(List imageLists, Long hist List historyImages = new ArrayList<>(); for(String image : images){ - historyImages.add(adminDonationConvertor.convertToHistoryImage(image, historyId)); + historyImages.add(adminDonationConverter.convertToHistoryImage(image, historyId)); } + historyImageRepository.saveAll(historyImages); } + + public void postExecution(DonationReq.EnforceDonation enforceDonation) { + donationHistoryAdaptor.saveDonationHistory(adminDonationConverter.convertToDonationHistoryChange(enforceDonation)); + } + + public PageResponse> getProjectDonationStatus(int page, int size) { + Page projects = projectAdaptor.findAll(page, size); + + List projectDonations = new ArrayList<>(); + + for(Project project : projects){ + projectDonations.add(adminDonationConverter.convertToStatusDetail(donationAdaptor.findByProject(project), project)); + } + + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectDonations); + } + + public PageResponse> getProjectDonationLists(Project project, int page, int size) { + Page donationUsers = donationAdaptor.findDonationLists(project.getId(), page, size); + + return new PageResponse<>(donationUsers.isLast(),donationUsers.getTotalElements(), adminDonationConverter.convertToDonationLists(donationUsers.getContent())); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/event/service/AdminEventService.java b/Match-Api/src/main/java/com/example/matchapi/admin/event/service/AdminEventService.java index 4acfe9c6..1265b47a 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/event/service/AdminEventService.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/event/service/AdminEventService.java @@ -2,27 +2,23 @@ import com.example.matchapi.admin.event.dto.EventUploadReq; import com.example.matchapi.common.model.ContentsList; -import com.example.matchapi.event.convetor.EventConvertor; -import com.example.matchapi.event.dto.EventRes; +import com.example.matchapi.common.util.MessageHelper; +import com.example.matchapi.event.converter.EventConverter; import com.example.matchapi.event.service.EventService; import com.example.matchcommon.annotation.RedissonLock; -import com.example.matchdomain.common.model.ContentsType; +import com.example.matchcommon.constants.enums.Topic; import com.example.matchdomain.event.entity.Event; import com.example.matchdomain.event.entity.EventContent; import com.example.matchdomain.event.repository.EventContentRepository; import com.example.matchdomain.event.repository.EventRepository; -import com.example.matchinfrastructure.config.s3.S3UploadService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.CachePut; -import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.List; -import static com.example.matchcommon.constants.MatchStatic.EVENT_S3_DIR; +import static com.example.matchcommon.constants.MatchAlertStatic.EVENT_UPLOAD_BODY; import static com.example.matchdomain.common.model.ContentsType.IMG; import static com.example.matchdomain.common.model.ContentsType.TEXT; @@ -31,27 +27,31 @@ public class AdminEventService { private final EventRepository eventRepository; private final EventContentRepository eventContentRepository; - private final EventConvertor eventConvertor; + private final EventConverter eventConverter; private final EventService eventService; + private final MessageHelper messageHelper; @RedissonLock(LockName = "이벤트 업로드", key = "#eventUploadReq") @CacheEvict(value = "eventCache", allEntries = true, cacheManager = "ehcacheManager") public void uploadEventList(EventUploadReq eventUploadReq) { - Event event = eventRepository.save(eventConvertor.convertToEventUpload(eventUploadReq, eventUploadReq.getThumbnail())); + Event event = eventRepository.save(eventConverter.convertToEventUpload(eventUploadReq, eventUploadReq.getThumbnail())); Long eventId = event.getId(); List eventContents = new ArrayList<>(); for(ContentsList content : eventUploadReq.getContentsList()){ if(content.getContentsType().equals(IMG)){ - eventContents.add(eventConvertor.convertToEventContents(eventId, content.getContents(), IMG)); + eventContents.add(eventConverter.convertToEventContents(eventId, content.getContents(), IMG)); } else{ - eventContents.add(eventConvertor.convertToEventContents(eventId, content.getContents(), TEXT)); + eventContents.add(eventConverter.convertToEventContents(eventId, content.getContents(), TEXT)); } } eventContentRepository.saveAll(eventContents); + + messageHelper.helpFcmMessage(EVENT_UPLOAD_BODY, Topic.EVENT_UPLOAD, event.getId()); } + } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/convertor/AdminKeywordConvertor.java b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/converter/AdminKeywordConverter.java similarity index 68% rename from Match-Api/src/main/java/com/example/matchapi/admin/keyword/convertor/AdminKeywordConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/admin/keyword/converter/AdminKeywordConverter.java index 2d54d49d..ba39d0b4 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/convertor/AdminKeywordConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/converter/AdminKeywordConverter.java @@ -1,11 +1,11 @@ -package com.example.matchapi.admin.keyword.convertor; +package com.example.matchapi.admin.keyword.converter; import com.example.matchapi.admin.keyword.dto.AdminKeywordReq; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.keyword.entity.SearchKeyword; -@Convertor -public class AdminKeywordConvertor { +@Converter +public class AdminKeywordConverter { public SearchKeyword convertToKeyword(AdminKeywordReq.KeywordUpload keyword) { return SearchKeyword.builder().keyword(keyword.getSearchKeyword()).priority(keyword.getPriority()).build(); } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/dto/AdminKeywordReq.java b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/dto/AdminKeywordReq.java index f898c6fe..2122a3d6 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/dto/AdminKeywordReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/dto/AdminKeywordReq.java @@ -10,6 +10,7 @@ public class AdminKeywordReq { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class KeywordUpload implements Serializable { private String searchKeyword; diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/service/AdminKeywordService.java b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/service/AdminKeywordService.java index 1a79f7bd..d44f66aa 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/service/AdminKeywordService.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/service/AdminKeywordService.java @@ -1,8 +1,8 @@ package com.example.matchapi.admin.keyword.service; -import com.example.matchapi.admin.keyword.convertor.AdminKeywordConvertor; +import com.example.matchapi.admin.keyword.converter.AdminKeywordConverter; import com.example.matchapi.admin.keyword.dto.AdminKeywordReq; -import com.example.matchapi.keword.convertor.KeywordConvertor; +import com.example.matchapi.keword.converter.KeywordConverter; import com.example.matchapi.keword.dto.KeywordRes; import com.example.matchdomain.keyword.entity.SearchKeyword; import com.example.matchdomain.keyword.repository.SearchKeywordRepository; @@ -20,13 +20,13 @@ @RequiredArgsConstructor public class AdminKeywordService { private final SearchKeywordRepository searchKeywordRepository; - private final KeywordConvertor keywordConvertor; - private final AdminKeywordConvertor adminKeywordConvertor; + private final KeywordConverter keywordConverter; + private final AdminKeywordConverter adminKeywordConverter; @Transactional @CachePut(cacheNames = "keywordList", key = "'all'", cacheManager = "ehcacheManager") public List postKeyword(AdminKeywordReq.KeywordUpload keyword) { - searchKeywordRepository.save(adminKeywordConvertor.convertToKeyword(keyword)); + searchKeywordRepository.save(adminKeywordConverter.convertToKeyword(keyword)); return cachingKeywordList(); } @@ -36,7 +36,7 @@ public List cachingKeywordList(){ List keywordLists = new ArrayList<>(); searchKeywords.forEach( - result -> keywordLists.add(keywordConvertor.convertToKeywordList(result)) + result -> keywordLists.add(keywordConverter.convertToKeywordList(result)) ); return keywordLists; } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/notice/dto/NoticeUploadReq.java b/Match-Api/src/main/java/com/example/matchapi/admin/notice/dto/NoticeUploadReq.java index 63e74cad..a273e1c4 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/notice/dto/NoticeUploadReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/notice/dto/NoticeUploadReq.java @@ -11,6 +11,7 @@ @Builder @AllArgsConstructor @NoArgsConstructor +@ToString public class NoticeUploadReq { private String title; diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/order/controller/AdminOrderController.java b/Match-Api/src/main/java/com/example/matchapi/admin/order/controller/AdminOrderController.java index fe854318..a5e9bda1 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/order/controller/AdminOrderController.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/order/controller/AdminOrderController.java @@ -27,13 +27,5 @@ public CommonResponse refundDonation(@PathVariable Long donationId){ return CommonResponse.onSuccess("환불 성공"); } - @Operation(summary = "ADMIN-ORDER-04-02💸 기부금 상태 변경 API", description = "관리자 기부금 상태변경 API 입니다,") - @ApiErrorCodeExample({UserAuthErrorCode.class,DonationGerErrorCode.class}) - @PatchMapping("/{donationId}") - public CommonResponse modifyDonationStatus(@RequestParam("donationStatus")DonationStatus donationStatus, @PathVariable Long donationId){ - orderService.modifyDonationStatus(donationId, donationStatus); - return CommonResponse.onSuccess("기부 상태 수정 완료 : " + donationStatus.getName()); - } - } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/project/controller/AdminProjectController.java b/Match-Api/src/main/java/com/example/matchapi/admin/project/controller/AdminProjectController.java index b89839f3..49d53976 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/project/controller/AdminProjectController.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/project/controller/AdminProjectController.java @@ -36,11 +36,9 @@ public class AdminProjectController { private final ProjectService projectService; @Operation(summary = "ADMIN-03-01💻 프로젝트 리스트 업로드 API.",description = "프로젝트 업로드 API 입니다.") - @PostMapping(value = "", consumes = {"multipart/form-data"}, produces = "application/json") + @PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @ApiErrorCodeExample({UserAuthErrorCode.class, RequestErrorCode.class, FileUploadException.class}) public CommonResponse postProject( - @Parameter(description = "project 상세 내용입니다. type 은 application/json" - , content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) @Valid @RequestPart("project") ProjectReq.Project project, @RequestPart("presentFile") MultipartFile presentFile, @RequestPart("multipartFiles") List multipartFiles){ diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/user/controller/AdminUserController.java b/Match-Api/src/main/java/com/example/matchapi/admin/user/controller/AdminUserController.java index 6bf636f8..1f93c6a4 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/user/controller/AdminUserController.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/user/controller/AdminUserController.java @@ -1,17 +1,21 @@ package com.example.matchapi.admin.user.controller; +import com.example.matchapi.donation.service.DonationService; +import com.example.matchapi.user.converter.UserConverter; import com.example.matchapi.user.dto.UserRes; import com.example.matchapi.user.service.UserService; import com.example.matchcommon.annotation.ApiErrorCodeExample; import com.example.matchcommon.reponse.CommonResponse; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.exception.UserAuthErrorCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.Min; @@ -23,6 +27,8 @@ @Tag(name = "ADMIN-02 User👤 관리자 유저 관련 API 입니다.", description = "ADMIN 유저 관련 API 입니다.") public class AdminUserController { private final UserService userService; + private final DonationService donationService; + private final UserConverter userConverter; @GetMapping("/info") @ApiErrorCodeExample(UserAuthErrorCode.class) @Operation(summary = "ADMIN-02-01👤 유저저 가입 현황파악 API.",description = "프로젝트 리스트 조회 API 입니다.") @@ -51,4 +57,16 @@ public CommonResponse getUserDetail(@PathVariable Long UserRes.UserAdminDetail userAdminDetail = userService.getUserAdminDetail(userId); return CommonResponse.onSuccess(userAdminDetail); } + + @GetMapping("/flame/{userId}") + @ApiErrorCodeExample(UserAuthErrorCode.class) + @Operation(summary = "ADMIN-02-04 유저 불꽃이 생성기록 조회" ,description = "유저 불꽃이 기록 조회") + public CommonResponse>> getUserFlameList(@PathVariable Long userId, + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") @Min(value = 0) int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "10") int size){ + User user = userService.findByUserId(userId); + Page donationUsers = donationService.findByUserId(user, page, size); + + return CommonResponse.onSuccess(new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), userConverter.convertToFlameList(donationUsers.getContent()))); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/convertor/BannerConvertor.java b/Match-Api/src/main/java/com/example/matchapi/banner/converter/BannerConverter.java similarity index 92% rename from Match-Api/src/main/java/com/example/matchapi/banner/convertor/BannerConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/banner/converter/BannerConverter.java index 8e2f46d4..b3735aa6 100644 --- a/Match-Api/src/main/java/com/example/matchapi/banner/convertor/BannerConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/banner/converter/BannerConverter.java @@ -1,16 +1,16 @@ -package com.example.matchapi.banner.convertor; +package com.example.matchapi.banner.converter; import com.example.matchapi.banner.dto.BannerReq; import com.example.matchapi.banner.dto.BannerRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.banner.entity.Banner; import com.example.matchdomain.banner.enums.BannerType; import java.util.ArrayList; import java.util.List; -@Convertor -public class BannerConvertor { +@Converter +public class BannerConverter { public Banner convertToBannerUpload(BannerType bannerType, String bannerImg, BannerReq.BannerUpload bannerUploadDto) { if(bannerType.equals(BannerType.EVENT)){ diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerReq.java b/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerReq.java index 26359a67..e143305d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerReq.java @@ -9,6 +9,7 @@ public class BannerReq { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class BannerUpload { private Long eventId; diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java b/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java index 07bfe36c..88a94ab9 100644 --- a/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java +++ b/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java @@ -1,6 +1,6 @@ package com.example.matchapi.banner.service; -import com.example.matchapi.banner.convertor.BannerConvertor; +import com.example.matchapi.banner.converter.BannerConverter; import com.example.matchapi.banner.dto.BannerRes; import com.example.matchdomain.banner.adaptor.BannerAdaptor; import com.example.matchdomain.banner.entity.Banner; @@ -14,11 +14,11 @@ @RequiredArgsConstructor public class BannerService { private final BannerAdaptor bannerAdaptor; - private final BannerConvertor bannerConvertor; + private final BannerConverter bannerConverter; @Cacheable(cacheNames = "bannerCache", key = "'all'", cacheManager = "ehcacheManager") public List getBannerList() { List banners = bannerAdaptor.getBannerList(); - return bannerConvertor.convertToBannerList(banners); + return bannerConverter.convertToBannerList(banners); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java b/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java index ec013354..3400e8f5 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java @@ -91,7 +91,6 @@ public ResponseEntity onKnownDynamicException(BaseDynamicException baseDynamicEx } - @ExceptionHandler(value = Exception.class) public ResponseEntity onException(Exception exception, @AuthenticationPrincipal User user, HttpServletRequest request) { @@ -127,7 +126,7 @@ private void getExceptionStackTrace(Exception e, User user, HttpServletRequest r pw.append("ERROR_MESSAGE : ").append(errors.toString()).append("\n"); } else { - pw.append("ERROR_MESSAGE : ").append(e.getMessage()).append("\n"); + pw.append("ERROR_MESSAGE : ").append(e.toString()).append("\n"); pw.append("ERROR_MESSAGE : ").append(e.getLocalizedMessage()).append("\n"); } pw.append("====================================================================="); diff --git a/Match-Api/src/main/java/com/example/matchapi/common/TestController.java b/Match-Api/src/main/java/com/example/matchapi/common/TestController.java index 413a04d2..365fdefa 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/TestController.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/TestController.java @@ -1,67 +1,30 @@ package com.example.matchapi.common; -import com.example.matchapi.common.aop.CheckIdExist; -import com.example.matchapi.notification.service.NotificationService; -import com.example.matchapi.order.helper.OrderHelper; import com.example.matchcommon.reponse.CommonResponse; -import com.example.matchcommon.service.MailService; -import com.example.matchdomain.user.entity.User; -import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; -import com.example.matchinfrastructure.fcm.service.FcmNotificationService; -import io.swagger.v3.oas.annotations.Parameter; +import com.example.matchinfrastructure.aligo.service.AligoInfraService; +import com.example.matchinfrastructure.match_aligo.dto.AlimTalkDto; import lombok.RequiredArgsConstructor; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import static com.example.matchinfrastructure.aligo.dto.AlimType.EXECUTION; +import static com.example.matchinfrastructure.aligo.dto.AlimType.PAYMENT; - -@RequestMapping("/test") @RestController @RequiredArgsConstructor +@RequestMapping("/test") public class TestController { - private final OrderHelper orderHelper; - private final MailService mailService; - private final NotificationService notificationService; - private final FcmNotificationService fcmNotificationService; + private final AligoInfraService aligoInfraService; @GetMapping("") - public void exRedirect3(HttpServletResponse httpServletResponse) throws IOException { - httpServletResponse.sendRedirect("https://naver.com"); - } + public CommonResponse testAlimTalk( + @RequestParam("name") String name, + @RequestParam("phone") String phone) { + aligoInfraService.sendAlimTalkTest(phone, name, PAYMENT); - @CheckIdExist - @GetMapping("/{projectId}/{userId}/{donationId}") - public CommonResponse getTest(@PathVariable Long projectId, @PathVariable Long userId, @PathVariable Long donationId){ + //aligoInfraService.sendAlimTalk(new AlimTalkDto(1L,name, phone, "TBT", "강아지 사료"), EXECUTION); return CommonResponse.onSuccess("성공"); } - - @PostMapping("/fcm") - public String fcmTest( - @RequestBody FCMNotificationRequestDto fcmNotificationRequestDto - ){ - fcmNotificationService.testNotification(fcmNotificationRequestDto); - return "성공"; - } - - @PostMapping("/fcm/user") - public String fcmUserTest( - @AuthenticationPrincipal User user, - @RequestBody FCMNotificationRequestDto fcmNotificationRequestDto - ){ - fcmNotificationService.testNotification(fcmNotificationRequestDto); - notificationService.saveTestNotification(user, fcmNotificationRequestDto); - - return "성공"; - } - /* - - @GetMapping("/email") - public CommonResponse testEmail(@Parameter String email) throws Exception { - mailService.sendEmailMessage(email, code); - return CommonResponse.onSuccess("이메일 전송 성공"); - } - - */ -} +} \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/common/aop/LogAspect.java b/Match-Api/src/main/java/com/example/matchapi/common/aop/LogAspect.java new file mode 100644 index 00000000..eeccfe28 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/aop/LogAspect.java @@ -0,0 +1,92 @@ +package com.example.matchapi.common.aop; + +import com.example.matchcommon.reponse.CommonResponse; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.*; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.Arrays; + +import static com.example.matchcommon.constants.MatchStatic.ignoredMethods; + +@Component +@Aspect +@Slf4j +public class LogAspect { + @Pointcut("execution(* com.example.matchapi..*Controller.*(..))") + public void controller() { + } + + + @Pointcut("execution(* com.example.matchapi..*Service.*(..))") + public void service() { + } + + + @Before("controller()") + public void beforeLogic(JoinPoint joinPoint) throws Throwable { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + Method method = methodSignature.getMethod(); + String methodName = getMethodName(method); + + if(!ignoredMethods.contains(methodName)) { + log.info("==========================LOG_START=========================="); + + log.info("logging start method = {}", methodName); + + String[] parameterNames = methodSignature.getParameterNames(); + + Object[] args = joinPoint.getArgs(); + int index = 0; + for (Object arg : args) { + if (arg != null) { + log.info("parameterNames = {} type = {}, value = {}", parameterNames[index], arg.getClass().getSimpleName(), arg.toString()); + } + index += 1; + } + } + } + + private String getMethodName(Method method) { + return method.getName(); + } + + @After("controller()") + public void afterLogic(JoinPoint joinPoint) throws Throwable { + Method method = getMethod(joinPoint); + + String methodName = getMethodName(method); + + if(!ignoredMethods.contains(methodName)) { + + log.info("logging finish method = {}", methodName); + + log.info("==========================LOG_FINISH=========================="); + } + } + + @AfterReturning(value = "controller()", returning = "returnObj") + public void afterReturnLog(JoinPoint joinPoint, Object returnObj) { + Method method = getMethod(joinPoint); + + String methodName = getMethodName(method); + + if(!ignoredMethods.contains(methodName)) { + if (returnObj != null) { + log.info("========================RETURN_LOG============================"); + log.info("method name = {}", methodName); + log.info("return type = {}", returnObj.getClass().getSimpleName()); + log.info("return value = {}", returnObj.toString()); + log.info("=============================================================="); + } + } + } + + private Method getMethod(JoinPoint joinPoint) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + return signature.getMethod(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/lisetner/ExecutionEvent.java b/Match-Api/src/main/java/com/example/matchapi/common/lisetner/ExecutionEvent.java new file mode 100644 index 00000000..1726b5e7 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/lisetner/ExecutionEvent.java @@ -0,0 +1,34 @@ +package com.example.matchapi.common.lisetner; + + +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.project.entity.Project; +import org.springframework.context.ApplicationEvent; + +import java.util.ArrayList; +import java.util.List; + +public class ExecutionEvent extends ApplicationEvent { + private final List donationUsers; + private final Project project; + private final List items; + + public ExecutionEvent(Object source, List donationUsers, Project project, List items) { + super(source); + this.donationUsers = donationUsers; + this.project = project; + this.items = items; + } + + public List getDonationUsers() { + return donationUsers; + } + + public Project getProject() { + return project; + } + + public List getItems(){ + return items; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/lisetner/ExecutionEventListener.java b/Match-Api/src/main/java/com/example/matchapi/common/lisetner/ExecutionEventListener.java new file mode 100644 index 00000000..48aa95be --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/lisetner/ExecutionEventListener.java @@ -0,0 +1,51 @@ +package com.example.matchapi.common.lisetner; + +import com.example.matchapi.user.service.AligoService; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.aligo.converter.AligoConverter; +import com.example.matchinfrastructure.aligo.dto.AlimType; +import com.example.matchinfrastructure.aligo.service.AligoInfraService; +import com.example.matchinfrastructure.match_aligo.dto.AlimTalkDto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +@Slf4j +public class ExecutionEventListener { + private final AligoConverter aligoConverter; + private final AligoService aligoService; + @EventListener + @Async("event-listener") + public void onDonationExecution(ExecutionEvent event){ + log.info("event start"); + List donationUsers = event.getDonationUsers(); + + Project project = event.getProject(); + + List alimTalkDtos = new ArrayList<>(); + + String article = getArticle(event.getItems()); + + for(DonationUser donationUser : donationUsers){ + User user = donationUser.getUser(); + alimTalkDtos.add(aligoConverter.convertToAlimTalkExecution(donationUser.getId(), user.getName(), user.getPhoneNumber(), article, project.getUsages())); + } + + aligoService.sendAlimTalks(AlimType.EXECUTION, alimTalkDtos); + log.info("event finish"); + + } + + private String getArticle(List items) { + return items.get(0) + " 외 " + items.size() + "개"; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/model/UploadFolder.java b/Match-Api/src/main/java/com/example/matchapi/common/model/UploadFolder.java index 43345457..4bdc6bbd 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/model/UploadFolder.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/model/UploadFolder.java @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor public enum UploadFolder { - EVENT("event"), NOTICE("notice"); + EVENT("event"), NOTICE("notice"), BANNER("banner"); private final String folder; } diff --git a/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAuthenticationEntryPoint.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAuthenticationEntryPoint.java index 121b8e89..1fe581e0 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAuthenticationEntryPoint.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAuthenticationEntryPoint.java @@ -57,6 +57,10 @@ public void commence(HttpServletRequest request, errorCode = NOT_EXISTS_USER_HAVE_TOKEN; setResponse(response, errorCode, request); return; + case "NotUserActiveException": + errorCode = NOT_USER_ACTIVE; + setResponse(response, errorCode, request); + return; } } diff --git a/Match-Api/src/main/java/com/example/matchapi/common/security/JwtFilter.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtFilter.java index 316971f9..2cc0b71d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/security/JwtFilter.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtFilter.java @@ -15,6 +15,8 @@ import javax.servlet.http.HttpServletRequest; import java.io.IOException; +import static com.example.matchcommon.constants.MatchStatic.ignoredUri; + @RequiredArgsConstructor public class JwtFilter extends GenericFilterBean{ @@ -37,15 +39,18 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo if (StringUtils.hasText(jwt)&& jwtService.validateToken(servletRequest,jwt)) { - Authentication authentication = jwtService.getAuthentication(jwt,servletRequest); + Authentication authentication = jwtService.getAuthentication(jwt, servletRequest); SecurityContextHolder.getContext().setAuthentication(authentication); if(authentication !=null) { - logger.info("Security Context '{}' 인증 정보를 저장했습니다, uri: {} method: {}", authentication.getName(), requestURI, httpServletRequest.getMethod()); + logger.info("method : {} ", ((HttpServletRequest) servletRequest).getMethod()); + logger.info("Security Context 에 '{}' 인증 정보를 저장했습니다, uri: {}", authentication.getName(), requestURI); }else{ logger.info("해당 토큰을 가진 유저가 존재하지 않습니다, uri: {}", requestURI); } } else { - logger.info("유효한 JWT 토큰이 없습니다, uri: {}", requestURI); + if(!ignoredUri.contains(requestURI)) { + logger.info("유효한 JWT 토큰이 없습니다, uri: {}", requestURI); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/common/security/JwtService.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtService.java index b27227d2..8f8b186b 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/security/JwtService.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtService.java @@ -1,6 +1,7 @@ package com.example.matchapi.common.security; import com.example.matchapi.user.dto.UserRes; +import com.example.matchcommon.exception.NotUserActiveException; import com.example.matchcommon.properties.JwtProperties; import com.example.matchdomain.redis.entity.AccessToken; import com.example.matchdomain.redis.entity.RefreshToken; @@ -27,11 +28,16 @@ import javax.servlet.http.HttpServletRequest; import java.nio.charset.StandardCharsets; import java.security.Key; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.LinkedHashMap; import java.util.NoSuchElementException; import java.util.Optional; +import static com.example.matchdomain.common.model.Status.INACTIVE; + @RequiredArgsConstructor @Component @Slf4j @@ -51,60 +57,50 @@ private Key getRefreshKey() { } - public String createToken(Long userId) { - Date now =new Date(); + public UserRes.Token createTokens(Long userId){ + String accessToken = createToken(userId); - final Key encodedKey = getSecretKey(); + String refreshToken = createRefreshToken(userId); - return Jwts.builder() - .setHeaderParam("type","jwt") - .claim("userId",userId) - .setIssuedAt(now) - .setExpiration(new Date(System.currentTimeMillis()+jwtProperties.getAccessTokenSeconds())) - .signWith(encodedKey) - .compact(); + return new UserRes.Token(accessToken,refreshToken); } - public String createRefreshToken(Long userId){ - Date now=new Date(); - final Key encodedKey = getRefreshKey(); + private String createJwtToken(Long userId, Duration duration, Key key, String typeHeader) { + Instant issuedAt = Instant.now(); + Instant expiration = issuedAt.plus(duration); return Jwts.builder() - .setHeaderParam("type","refresh") - .claim("userId",userId) - .setIssuedAt(now) - .setExpiration(new Date(System.currentTimeMillis()+jwtProperties.getRefreshTokenSeconds())) - .signWith(encodedKey) + .setHeaderParam("type", typeHeader) + .claim("userId", userId) + .setIssuedAt(Date.from(issuedAt)) + .setExpiration(Date.from(expiration)) + .signWith(key) .compact(); } - public UserRes.Token createTokens(Long userId){ - Date now=new Date(); - final Key encodedAccessKey = getSecretKey(); - - final Key encodedRefreshKey = getRefreshKey(); + public String createToken(Long userId) { + return createJwtToken(userId, Duration.ofDays(jwtProperties.getAccessTokenSeconds()), getSecretKey(), "jwt"); + } - String accessToken = Jwts.builder() - .setHeaderParam("type","jwt") - .claim("userId",userId) - .setIssuedAt(now) - .setExpiration(new Date(System.currentTimeMillis()+jwtProperties.getAccessTokenSeconds())) - .signWith(encodedAccessKey) - .compact(); + public String createTokenToWeb(Long userId, Long seconds) { + Instant issuedAt = Instant.now(); + Instant expiration = issuedAt.plusSeconds(seconds); - String refreshToken= Jwts.builder() - .setHeaderParam("type","refresh") - .claim("userId",userId) - .setIssuedAt(now) - .setExpiration(new Date(System.currentTimeMillis()+jwtProperties.getRefreshTokenSeconds())) - .signWith(encodedRefreshKey) + return Jwts.builder() + .setHeaderParam("type", "jwt") + .claim("userId", userId) + .setIssuedAt(Date.from(issuedAt)) + .setExpiration(Date.from(expiration)) + .signWith(getSecretKey()) .compact(); - - refreshTokenRepository.save(RefreshToken.builder().userId(userId.toString()).token(refreshToken).ttl(jwtProperties.getRefreshTokenSeconds()).build()); - - return new UserRes.Token(accessToken,refreshToken); } + public String createRefreshToken(Long userId) { + Long ttl = Duration.ofDays(jwtProperties.getRefreshTokenSeconds()).getSeconds(); + String refreshToken = createJwtToken(userId, Duration.ofDays(jwtProperties.getRefreshTokenSeconds()), getRefreshKey(), "refresh"); + refreshTokenRepository.save(RefreshToken.builder().userId(String.valueOf(userId)).token(refreshToken).ttl(ttl).build()); + return refreshToken; + } public Authentication getAuthentication(String token, ServletRequest servletRequest) { try { Jws claims; @@ -114,13 +110,22 @@ public Authentication getAuthentication(String token, ServletRequest servletRequ .parseClaimsJws(token); Long userId=claims.getBody().get("userId",Long.class); - log.info("user find"); Optional users = userAdaptor.findByUserId(userId); - log.info("user find"); + + if(users.isEmpty()){ + throw new NoSuchElementException("NOT EXISTS USER"); + } + if(users.get().getStatus().equals(INACTIVE)){ + throw new NotUserActiveException("NOT ACTIVE USER"); + } + return new UsernamePasswordAuthenticationToken(users.get(),"",users.get().getAuthorities()); }catch(NoSuchElementException e){ servletRequest.setAttribute("exception","NoSuchElementException"); log.info("유저가 존재하지 않습니다."); + }catch (NotUserActiveException e){ + servletRequest.setAttribute("exception","NotUserActiveException"); + log.info("유저가 비활성 상태입니다."); } return null; } @@ -146,9 +151,6 @@ public boolean validateToken(ServletRequest servletRequest, String token) { return false; } } - - - return true; } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { servletRequest.setAttribute("exception","MalformedJwtException"); @@ -171,21 +173,15 @@ public String getJwt(){ return request.getHeader(JwtFilter.AUTHORIZATION_HEADER); } - public String getRefreshToken(){ - HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); - return request.getHeader(JwtFilter.REFRESH_TOKEN_HEADER); - } - public Date getExpiredTime(String token){ //받은 토큰의 유효 시간을 받아오기 return Jwts.parser().setSigningKey(getSecretKey()).parseClaimsJws(token).getBody().getExpiration(); } public Long getUserIdByRefreshToken(String refreshToken) { - Jws claims = - Jwts.parser() - .setSigningKey(getRefreshKey()) - .parseClaimsJws(refreshToken); + Jws claims = Jwts.parser() + .setSigningKey(getRefreshKey()) + .parseClaimsJws(refreshToken); System.out.println(claims.getBody().get("userId",Long.class)); @@ -200,7 +196,7 @@ public RequestContextListener requestContextListener(){ public void logOut(Long userId) { long expiredAccessTokenTime=getExpiredTime(getJwt()).getTime() - new Date().getTime(); - log.info(String.valueOf(expiredAccessTokenTime)); + log.info("만료시간 : {} ",String.valueOf(expiredAccessTokenTime)); accessTokenRepository.save(AccessToken.builder().token(getJwt()).userId(String.valueOf(userId)).ttl(expiredAccessTokenTime).build()); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/common/util/MessageHelper.java b/Match-Api/src/main/java/com/example/matchapi/common/util/MessageHelper.java new file mode 100644 index 00000000..52fa9b81 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/util/MessageHelper.java @@ -0,0 +1,66 @@ +package com.example.matchapi.common.util; + +import com.example.matchcommon.annotation.Helper; +import com.example.matchdomain.user.adaptor.UserAdaptor; +import com.example.matchdomain.user.adaptor.UserFcmAdaptor; +import com.example.matchdomain.user.entity.UserFcmToken; +import com.example.matchinfrastructure.fcm.converter.FcmConverter; +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import com.example.matchcommon.constants.enums.Topic; +import com.example.matchinfrastructure.fcm.service.FcmNotificationService; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.TopicManagementResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.scheduling.annotation.Async; + +import java.util.List; +import java.util.stream.Collectors; + +@Helper +@Slf4j +@RequiredArgsConstructor +public class MessageHelper { + private final FcmNotificationService firebaseService; + private final FcmConverter fcmConverter; + private final UserAdaptor userAdaptor; + private final UserFcmAdaptor userFcmAdaptor; + + @Async("topic-fcm") + public void helpFcmMessage(String body, Topic topic, Long id) { + FCMNotificationRequestDto fcmNotificationRequestDto = fcmConverter.convertToFcmTopicMessageDto(body); + + int page = 0, size = 1000; + + Page userFcmTokens = userFcmAdaptor.findByUserServiceAlarm(page, size, topic); + + subscribeTopic(convertToList(userFcmTokens.getContent()), topic); + + if(!userFcmTokens.isLast()){ + for(int i = 1; i < userFcmTokens.getTotalPages(); i++){ + userFcmTokens = userFcmAdaptor.findByUserServiceAlarm(i, size, topic); + subscribeTopic(convertToList(userFcmTokens.getContent()),topic); + } + } + + firebaseService.sendTopicNotification(fcmNotificationRequestDto, topic, id); + } + + private void subscribeTopic(List tokensList, Topic topic){ + try { + TopicManagementResponse response = FirebaseMessaging.getInstance().subscribeToTopic( + tokensList, topic.getTopic() + ); + log.info("구독 성공 토큰 갯수 : " + response.getSuccessCount()); + } catch (FirebaseMessagingException e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } + } + + private List convertToList(List userFcmTokens){ + return userFcmTokens.stream().map(UserFcmToken::getFcmToken).collect(Collectors.toList()); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/config/MultipartJackson2HttpMessageConverter.java b/Match-Api/src/main/java/com/example/matchapi/config/MultipartJackson2HttpMessageConverter.java new file mode 100644 index 00000000..1550d245 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/config/MultipartJackson2HttpMessageConverter.java @@ -0,0 +1,42 @@ +package com.example.matchapi.config; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; + +@Component +@Slf4j +public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter { + + /** + * "Content-Type: multipart/form-data" 헤더를 지원하는 HTTP 요청 변환기 + */ + public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) { + super(objectMapper, MediaType.APPLICATION_OCTET_STREAM); + } + + + @Override + public boolean canWrite(Class clazz, MediaType mediaType) { + return false; + } + + @Override + public boolean canWrite(Type type, Class clazz, MediaType mediaType) { + return false; + } + + @Override + protected boolean canWrite(MediaType mediaType) { + return false; + } +} \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java b/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java index 9b17c21b..c7545321 100644 --- a/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java +++ b/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java @@ -3,6 +3,7 @@ import com.example.matchapi.common.security.*; import com.example.matchdomain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -15,7 +16,6 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.cors.CorsUtils; -import org.springframework.http.HttpMethod; @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) @@ -26,6 +26,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final UserDetailsService userDetailsService; + @Value("${spring.config.activate.on-profile}") + private String profile; @Bean public PasswordEncoder passwordEncoder() { @@ -48,67 +50,67 @@ public void configure(WebSecurity web) throws Exception { ; } + protected void configure(HttpSecurity httpSecurity) throws Exception { + configureCsrfAndHeaders(httpSecurity); + configureSessionManagement(httpSecurity); + configureExceptionHandling(httpSecurity); + configureAuthorizationRequests(httpSecurity); + disableFormLogin(httpSecurity); + configureEnvironmentSpecificAccess(httpSecurity); + } + private void configureCsrfAndHeaders(HttpSecurity httpSecurity) throws Exception { httpSecurity - // token을 사용하는 방식이기 때문에 csrf를 disable합니다. .csrf().disable() - // enable h2-console .headers() .frameOptions() - .sameOrigin() + .sameOrigin(); + } - // 세션을 사용하지 않기 때문에 STATELESS로 설정 - .and() + private void configureSessionManagement(HttpSecurity httpSecurity) throws Exception { + httpSecurity .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } - .and() + private void configureExceptionHandling(HttpSecurity httpSecurity) throws Exception { + httpSecurity .exceptionHandling() .authenticationEntryPoint(jwtAuthenticationEntryPoint) - .accessDeniedHandler(jwtAccessDeniedHandler) - + .accessDeniedHandler(jwtAccessDeniedHandler); + } - .and() + private void configureAuthorizationRequests(HttpSecurity httpSecurity) throws Exception { + httpSecurity .authorizeRequests() .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() - .antMatchers("/swagger-resources/**").permitAll() - .antMatchers("/swagger-ui/**").permitAll() - .antMatchers("/api-docs/**").permitAll() .antMatchers("/webjars/**").permitAll() - .antMatchers("/v3/api-docs").permitAll() - .antMatchers("/image/**").permitAll() .antMatchers("/users/refresh").permitAll() - .antMatchers("/profile").permitAll() - .antMatchers("/login/**").permitAll() - .antMatchers("/test/**").permitAll() - .antMatchers("/login/**").permitAll() .antMatchers("/auth/**").permitAll() .antMatchers("/health").permitAll() .antMatchers("/order").permitAll() - .antMatchers("/order/serverAuth").permitAll() .antMatchers("/projects").permitAll() - .antMatchers("/projects/**").permitAll() - .antMatchers("/projects/list").authenticated() - .antMatchers("/").permitAll() - .antMatchers("/serverAuth").permitAll() + .antMatchers(HttpMethod.GET, "/projects/{projectId}").permitAll() .antMatchers(HttpMethod.GET,"/donation-temporaries").permitAll() + .antMatchers("/projects/list").authenticated() .antMatchers("/users/refresh").permitAll() - .antMatchers(HttpMethod.GET,"/donation-temporaries").permitAll() + .antMatchers("/terms/**").permitAll() .antMatchers("/admin/projects/**").hasAnyRole("ADMIN") .antMatchers("/admin/users/**").hasAnyRole("ADMIN") - .antMatchers("/admin/users/**").hasAnyRole("ADMIN") .antMatchers("/admin/donation-users/**").hasAnyRole("ADMIN") .antMatchers("/admin/donation-temporaries/**").hasAnyRole("ADMIN") .antMatchers("/admin/order/**").hasAnyRole("ADMIN") .antMatchers("/admin/auth/logIn").permitAll() - .antMatchers("/test/fcm/user").authenticated() - .antMatchers("/terms/**").permitAll() - .anyRequest().authenticated() - + .antMatchers("/payments/web-hook").permitAll() + .antMatchers("/payments/validate").permitAll() + .antMatchers("/payments/info").permitAll() + .antMatchers("/test").permitAll() .and() .apply(new JwtSecurityConfig(jwtService)); + } + private void disableFormLogin(HttpSecurity httpSecurity) throws Exception { httpSecurity .httpBasic() .and() @@ -116,6 +118,31 @@ protected void configure(HttpSecurity httpSecurity) throws Exception { .disable(); } + private void configureEnvironmentSpecificAccess(HttpSecurity httpSecurity) throws Exception { + if (!profile.equals("prod")) { + allowSwaggerForNonProd(httpSecurity); + } else { + restrictSwaggerForProd(httpSecurity); + } + } + + private void allowSwaggerForNonProd(HttpSecurity httpSecurity) throws Exception { + httpSecurity + .authorizeRequests() + .antMatchers("/swagger-ui/**").permitAll() + .antMatchers("/api-docs/**").permitAll() + .antMatchers("/swagger-resources/**").permitAll() + .anyRequest().authenticated(); + } + + private void restrictSwaggerForProd(HttpSecurity httpSecurity) throws Exception { + httpSecurity + .authorizeRequests() + .antMatchers("/swagger-ui/**").authenticated() + .antMatchers("/api-docs/**").authenticated() + .antMatchers("/swagger-resources/**").authenticated() + .anyRequest().authenticated(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java b/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java index a330f53a..59f566fa 100644 --- a/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java +++ b/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java @@ -2,6 +2,7 @@ import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.annotation.DisableSecurity; import com.example.matchcommon.dto.ErrorReason; import com.example.matchcommon.exception.errorcode.BaseErrorCode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -20,9 +21,9 @@ import io.swagger.v3.oas.models.security.SecurityScheme; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.GroupedOpenApi; import org.springdoc.core.SpringDocUtils; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -43,21 +44,49 @@ public class SwaggerConfig { } - private final ApplicationContext applicationContext; @Value("${spring.config.activate.on-profile}") private String profile; + @Bean + public GroupedOpenApi v1ApiDocs() { + String[] paths = {"/**"}; + String[] excludesPath = {"/v2/**", "/admin/**"}; + return GroupedOpenApi.builder() + .group("API Version 1") + .pathsToMatch(paths) + .addOperationCustomizer(customize()) + .pathsToExclude(excludesPath) + .build(); + } @Bean - public ModelResolver modelResolver(ObjectMapper objectMapper) { - return new ModelResolver(objectMapper); + public GroupedOpenApi v2ApiDocs() { + String[] paths = { "/v2/**" }; + + return GroupedOpenApi.builder() + .group("API Version 2") + .pathsToMatch(paths) + .addOperationCustomizer(customize()) + .build(); + } + + @Bean + public GroupedOpenApi adminApiDocs(){ + String[] paths = { "/admin/**" }; + + return GroupedOpenApi.builder() + .group("Docs for ADMIN API") + .pathsToMatch(paths) + .addOperationCustomizer(customize()) + .build(); } @Bean public OpenAPI openAPI() { Info info = new Info() - .title(profile + "환경 Match Rest API 문서") // 타이틀 + .title(profile + " 환경 Match Rest API 문서") // 타이틀 .version("0.0.1") // 문서 버전 - .description("잘못된 부분이나 오류 발생 시 바로 말씀해주세요.") // 문서 설명 + .description("잘못된 부분이나 오류 발생 시 바로 말씀해주세요.\n" + + "우측 상단 Select a definition 클릭시 v1 v2 API 버전 분리") // 문서 설명 .contact(new Contact() // 연락처 .name("임현우") .email("gusdn8926@naver.com")); @@ -80,26 +109,9 @@ public OpenAPI openAPI() { .info(info); } - - - - - - @Bean - public OperationCustomizer customize() { - return (Operation operation, HandlerMethod handlerMethod) -> { - ApiErrorCodeExample apiErrorCodeExample = - handlerMethod.getMethodAnnotation(ApiErrorCodeExample.class); - // ApiErrorCodeExample 어노테이션 단 메소드 적용 - if (apiErrorCodeExample != null) { - Class[] errorCodes = apiErrorCodeExample.value(); - generateErrorCodeResponseExample(operation, errorCodes); - - } - return operation; - }; - + public ModelResolver modelResolver(ObjectMapper objectMapper) { + return new ModelResolver(objectMapper); } private void generateErrorCodeResponseExample( @@ -167,16 +179,24 @@ private void addExamplesToResponses( }); } + @Bean + public OperationCustomizer customize() { + return (Operation operation, HandlerMethod handlerMethod) -> { + ApiErrorCodeExample apiErrorCodeExample = + handlerMethod.getMethodAnnotation(ApiErrorCodeExample.class); + DisableSecurity methodAnnotation = + handlerMethod.getMethodAnnotation(DisableSecurity.class); + if (apiErrorCodeExample != null) { + Class[] errorCodes = apiErrorCodeExample.value(); + generateErrorCodeResponseExample(operation, errorCodes); + } + if(methodAnnotation !=null){ + operation.setSecurity(Collections.emptyList()); + } + return operation; + }; - - - - - - - - - + } } \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java b/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java index 88ea4703..8b8a1f2d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java @@ -1,17 +1,23 @@ package com.example.matchapi.donation.controller; +import com.example.matchapi.donation.dto.DonationReq; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.service.DonationService; +import com.example.matchapi.project.service.ProjectService; import com.example.matchapi.user.dto.UserRes; import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.annotation.RedissonLock; +import com.example.matchcommon.exception.errorcode.RequestErrorCode; import com.example.matchcommon.reponse.CommonResponse; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.donation.exception.*; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.exception.UserAuthErrorCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -25,6 +31,7 @@ @Tag(name = "05-Donation💸",description = "기부금 관련 API 입니다.") public class DonationController { private final DonationService donationService; + private final ProjectService projectService; @PatchMapping("/{donationId}") @ApiErrorCodeExample({UserAuthErrorCode.class, DonationRefundErrorCode.class}) @@ -183,4 +190,24 @@ public CommonResponse>> getBurnin return CommonResponse.onSuccess(donationService.getBurningFlameList(user, page, size)); } + + @Operation(summary = "05-13 튜토리얼 기부 리스트", description = "튜토리얼 기부 리스트") + @GetMapping("/tutorial") + @ApiErrorCodeExample(UserAuthErrorCode.class) + public CommonResponse> getTutorialDonation( + @AuthenticationPrincipal User user + ){ + return CommonResponse.onSuccess(projectService.getTutorialDonation()); + } + + @Operation(summary = "05-14 튜토리얼 기부 ", description = "튜토리얼 1원 기부 POST API 입니다.") + @PostMapping("/tutorial") + @ApiErrorCodeExample({UserAuthErrorCode.class, RequestErrorCode.class}) + @RedissonLock(LockName = "유저 튜토리얼 기부", key = "#user.id") + public CommonResponse postTutorialDonation( + @AuthenticationPrincipal User user, + @RequestBody DonationReq.Tutorial tutorial){ + Project project = projectService.findByProjectId(tutorial.getProjectId()); + return CommonResponse.onSuccess(donationService.postTutorialDonation(user, tutorial, project)); + } } \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationConvertor.java b/Match-Api/src/main/java/com/example/matchapi/donation/converter/DonationConverter.java similarity index 87% rename from Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/donation/converter/DonationConverter.java index 6847b7e4..3200b480 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/converter/DonationConverter.java @@ -1,14 +1,22 @@ -package com.example.matchapi.donation.convertor; +package com.example.matchapi.donation.converter; import com.example.matchapi.common.util.TimeHelper; +import com.example.matchapi.donation.dto.DonationReq; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; +import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.project.dto.ProjectRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.donation.entity.*; +import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import com.example.matchdomain.donation.entity.enums.PayMethod; +import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.donation.entity.flameEnum.FlameImage; +import com.example.matchdomain.donation.entity.flameEnum.FlameType; import com.example.matchdomain.donation.repository.DonationUserRepository; -import com.example.matchdomain.donation.repository.HistoryImageRepository; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.user.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -21,9 +29,9 @@ import static com.example.matchcommon.constants.MatchStatic.MATCH_PROFILE; import static com.example.matchdomain.donation.entity.enums.DonationStatus.*; -@Convertor +@Converter @RequiredArgsConstructor -public class DonationConvertor { +public class DonationConverter { private final DonationHelper donationHelper; private final TimeHelper timeHelper; @@ -90,7 +98,6 @@ public DonationRes.DonationRegularList convertToDonationRegularListDetail(Donati String histories = ""; String flameImage = null; List donationHistoryImages = new ArrayList<>(); - System.out.println("분기"); if(result.getHistoryStatus() == HistoryStatus.CREATE){ System.out.println("CREATE"); histories = inherenceName + "가 생성되었습니다."; @@ -307,4 +314,28 @@ public List convertToPayList(List donationUse } + public DonationUser convertToTutorialDonation(User user, DonationReq.Tutorial tutorial, OrderRes.CreateInherenceDto createInherenceDto) { + return DonationUser.builder() + .userId(user.getId()) + .projectId(tutorial.getProjectId()) + .price(1L) + .donationStatus(DonationStatus.EXECUTION_BEFORE) + .payMethod(PayMethod.TUTORIAL) + .inherenceName(createInherenceDto.getInherenceName()) + .inherenceNumber(createInherenceDto.getInherenceNumber()) + .regularStatus(RegularStatus.ONE_TIME) + .flameImage(FlameImage.NORMAL_IMG.getImg()) + .flameType(FlameType.NORMAL_FLAME) + .build(); + } + + public DonationRes.CompleteDonation convertToCompleteDonation(DonationUser donationUser, Project project) { + return DonationRes.CompleteDonation + .builder() + .projectId(donationUser.getProjectId()) + .image(donationUser.getFlameImage()) + .inherenceName(donationUser.getInherenceName()) + .randomMessage(donationHelper.createRandomMessageTutorial(project.getProjectKind())) + .build(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationTemporaryConvertor.java b/Match-Api/src/main/java/com/example/matchapi/donation/converter/DonationTemporaryConverter.java similarity index 97% rename from Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationTemporaryConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/donation/converter/DonationTemporaryConverter.java index 1e30e104..82b2891c 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationTemporaryConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/converter/DonationTemporaryConverter.java @@ -1,8 +1,8 @@ -package com.example.matchapi.donation.convertor; +package com.example.matchapi.donation.converter; import com.example.matchapi.donation.dto.DonationTemporaryReq; import com.example.matchapi.donation.dto.DonationTemporaryRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.donationTemporary.entity.Deposit; import com.example.matchdomain.donationTemporary.entity.DonationList; import com.example.matchdomain.donationTemporary.entity.DonationTemporary; @@ -11,8 +11,8 @@ import java.util.ArrayList; import java.util.List; -@Convertor -public class DonationTemporaryConvertor { +@Converter +public class DonationTemporaryConverter { public DonationTemporaryRes.UserInfo convertToUserInfo(User user) { return DonationTemporaryRes.UserInfo .builder() diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/RegularPaymentConvertor.java b/Match-Api/src/main/java/com/example/matchapi/donation/converter/RegularPaymentConverter.java similarity index 89% rename from Match-Api/src/main/java/com/example/matchapi/donation/convertor/RegularPaymentConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/donation/converter/RegularPaymentConverter.java index 5306b7fe..d2bd1910 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/RegularPaymentConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/converter/RegularPaymentConverter.java @@ -1,9 +1,9 @@ -package com.example.matchapi.donation.convertor; +package com.example.matchapi.donation.converter; import com.example.matchapi.common.util.TimeHelper; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.RegularPayment; import lombok.RequiredArgsConstructor; @@ -11,9 +11,9 @@ import java.util.ArrayList; import java.util.List; -@Convertor +@Converter @RequiredArgsConstructor -public class RegularPaymentConvertor { +public class RegularPaymentConverter { private final TimeHelper timeHelper; private final DonationHelper donationHelper; public List convertToMatchList(List regularPayments) { @@ -29,6 +29,7 @@ public List convertToMatchList(List regul private DonationRes.MatchList convertToMatchListDetail(RegularPayment result) { return DonationRes.MatchList .builder() + .regularId(result.getId()) .regularDate(timeHelper.matchTimeFormat(result.getCreatedAt())) .projectTitle(result.getProject().getProjectName()) .regularPayStatus(result.getRegularPayStatus()) @@ -55,6 +56,7 @@ private DonationRes.BurningFlameDto convertToBurningFlame(DonationUser donationU return DonationRes.BurningFlameDto .builder() .donationId(donationUser.getId()) + .projectId(donationUser.getProjectId()) .usages(donationUser.getProject().getUsages()) .image(donationUser.getFlameImage()) .inherenceName(donationUser.getInherenceName()) diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationReq.java b/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationReq.java index 9f4040e9..8f618b17 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationReq.java @@ -10,9 +10,36 @@ public class DonationReq { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class EnforceDonation { private Long projectId; + + private List item; + private List donationUserLists; + private List someExecutions; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @ToString + public static class SomeExecution{ + private Long donationUserId; + + private Long amount; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @ToString + public static class Tutorial { + private Long projectId; } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java b/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java index 818570b7..c141caf1 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java @@ -3,11 +3,14 @@ import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.enums.HistoryStatus; import com.example.matchdomain.donation.entity.enums.RegularPayStatus; +import com.example.matchdomain.project.entity.enums.ProjectKind; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.io.Serializable; +import java.time.LocalDateTime; import java.util.List; public class DonationRes { @@ -275,6 +278,8 @@ public static class FlameProjectList { @AllArgsConstructor @NoArgsConstructor public static class MatchList { + private Long regularId; + private String projectTitle; private String regularDate; @@ -292,6 +297,9 @@ public static class MatchList { @AllArgsConstructor @NoArgsConstructor public static class BurningFlameDto implements Serializable { + @Schema(description = "프로젝트 id", example = "1") + private Long projectId; + @Schema(description = "불꽃이 id 상세조회시 필요", required = true, example = "2") private Long donationId; @@ -307,4 +315,89 @@ public static class BurningFlameDto implements Serializable { @Schema(description = "랜덤 불꽃이 메세지") private String randomMessage; } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ProjectDonationStatus { + @Schema(description = "프로젝트 id", example = "1") + private Long projectId; + + @Schema(description = "후원처 명") + private String usages; + + private int totalAmount; + + @Schema(description = "분류 대기 금액") + private int waitingSortingAmount; + + @Schema(description = "수입 금액") + private int importedAmount; + + @Schema(description = "지출 완료 금액") + private int completeAmount; + } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class Tutorial { + private Long projectId; + + private ProjectKind projectKind; + + @Schema(description = "랜덤 불꽃이 메세지") + private String randomMessage; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class CompleteDonation { + private Long projectId; + + @Schema(description = "고유 불꽃이 이름") + private String inherenceName; + + @Schema(description = "불꽃이 이미지") + private String image; + + @Schema(description = "랜덤 불꽃이 메세지") + private String randomMessage; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ProjectDonationDto { + private Long donationId; + + @JsonFormat(pattern = "yyyy.MM.dd.HH:mm") + private LocalDateTime donationDate; + + private DonationStatus donationStatus; + + private String donationStatusName; + + private Long userId; + + private String userName; + + private Long amount; + + private Long waitingSortingAmount; + + @Schema(description = "수입 금액") + private int importedAmount; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java b/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java index f461824b..a1ec4b17 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java @@ -5,6 +5,7 @@ import com.example.matchdomain.common.model.RandomMessage; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.DonationStatus; +import com.example.matchdomain.project.entity.enums.ProjectKind; import lombok.RequiredArgsConstructor; import java.text.DecimalFormat; @@ -15,6 +16,7 @@ import java.util.Random; import java.util.stream.Collectors; +import static com.example.matchcommon.constants.RandomMessageStatic.*; import static com.example.matchdomain.common.model.MessageType.*; import static com.example.matchdomain.donation.entity.enums.RegularStatus.ONE_TIME; @@ -38,6 +40,7 @@ public String createRandomMessage(DonationUser donationUser) { case EXECUTION_UNDER: return getRandomMessageType(UNDER); case EXECUTION_SUCCESS: + case PARTIAL_EXECUTION: return getRandomMessageType(COMPLETE); } return "하이"; @@ -70,4 +73,31 @@ public String getRandomMessageType(MessageType messageType) { public boolean isOneDayPassed(LocalDateTime dateTime) { return Duration.between(dateTime, LocalDateTime.now()).toDays() >= 1; } + + public String createRandomMessageTutorial(ProjectKind projectKind) { + switch (projectKind){ + case CHILDREN: + return CHILDREN_MESSAGE; + case YOUTH: + return YOUTH_MESSAGE; + case WOMEN: + return WOMEN_MESSAGE; + case ELDER: + return ELDER_MESSAGE; + case DISABLED: + return DISABLED_MESSAGE; + case SOCIAL: + return SOCIAL_MESSAGE; + case NEIGHBOR: + return NEIGHBOR_MESSAGE; + case EARTH: + return EARTH_MESSAGE; + case ANIMAL: + return ANIMAL_MESSAGE; + case ENVIRONMENT: + return ENVIRONMENT_MESSAGE; + default: + return RANDOM_MESSAGE; + } + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationHistoryService.java b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationHistoryService.java index 9c4f8711..6ff67b55 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationHistoryService.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationHistoryService.java @@ -1,6 +1,6 @@ package com.example.matchapi.donation.service; -import com.example.matchapi.donation.convertor.DonationConvertor; +import com.example.matchapi.donation.converter.DonationConverter; import com.example.matchdomain.donation.adaptor.DonationHistoryAdaptor; import com.example.matchdomain.donation.entity.DonationHistory; import lombok.RequiredArgsConstructor; @@ -13,12 +13,12 @@ @RequiredArgsConstructor public class DonationHistoryService { private final DonationHistoryAdaptor donationHistoryAdaptor; - private final DonationConvertor donationConvertor; + private final DonationConverter donationConverter; public void postRegularDonationHistory(Long regularPaymentId, Long donationId) { - saveDonationHistory(donationConvertor.convertToDonationHistoryTurnOn(regularPaymentId, TURN_ON)); + saveDonationHistory(donationConverter.convertToDonationHistoryTurnOn(regularPaymentId, TURN_ON)); - saveDonationHistory(donationConvertor.convertToDonationHistory(donationId, CREATE)); + saveDonationHistory(donationConverter.convertToDonationHistory(donationId, CREATE)); } public void saveDonationHistory(DonationHistory donationHistory){ @@ -26,6 +26,6 @@ public void saveDonationHistory(DonationHistory donationHistory){ } public void oneTimeDonationHistory(Long donationId){ - saveDonationHistory(donationConvertor.convertToDonationHistory(donationId, CREATE)); + donationHistoryAdaptor.saveDonationHistory(donationConverter.convertToDonationHistory(donationId, CREATE)); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java index d50243fc..1dc67775 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java @@ -1,11 +1,11 @@ package com.example.matchapi.donation.service; -import com.example.matchapi.donation.convertor.DonationConvertor; -import com.example.matchapi.donation.convertor.RegularPaymentConvertor; +import com.example.matchapi.donation.converter.DonationConverter; +import com.example.matchapi.donation.converter.RegularPaymentConverter; +import com.example.matchapi.donation.dto.DonationReq; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; -import com.example.matchapi.portone.dto.PaymentReq; -import com.example.matchapi.portone.service.PaymentService; +import com.example.matchapi.order.helper.OrderHelper; import com.example.matchapi.project.dto.ProjectRes; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.reponse.PageResponse; @@ -18,18 +18,18 @@ import com.example.matchdomain.donation.repository.RegularPaymentRepository; import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.user.entity.User; -import com.siot.IamportRestClient.response.Payment; +import com.example.matchinfrastructure.pay.portone.service.PortOneService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import javax.transaction.Transactional; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import javax.validation.constraints.Min; import java.util.List; import static com.example.matchdomain.common.model.Status.ACTIVE; +import static com.example.matchdomain.common.model.Status.INACTIVE; import static com.example.matchdomain.donation.entity.enums.DonationStatus.*; import static com.example.matchdomain.donation.entity.enums.RegularPayStatus.PROCEEDING; import static com.example.matchdomain.donation.entity.enums.RegularPayStatus.USER_CANCEL; @@ -40,20 +40,22 @@ @Service @RequiredArgsConstructor public class DonationService { - private final DonationConvertor donationConvertor; + private final DonationConverter donationConverter; private final RegularPaymentRepository regularPaymentRepository; - private final PaymentService paymentService; private final RegularPaymentAdaptor regularPaymentAdaptor; private final DonationAdaptor donationAdaptor; private final DonationHistoryAdaptor donationHistoryAdaptor; - private final RegularPaymentConvertor regularPaymentConvertor; + private final RegularPaymentConverter regularPaymentConverter; private final DonationHelper donationHelper; + private final PortOneService portOneService; + private final OrderHelper orderHelper; + private final DonationHistoryService donationHistoryService; public PageResponse> getDonationList(Long userId, int filter, int page, int size) { Page donationUsers = donationAdaptor.findDonationList(userId, filter, page ,size); - return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationConvertor.convertToDonationList(donationUsers)); + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationConverter.convertToDonationList(donationUsers)); } @Transactional @@ -64,7 +66,7 @@ public void refundDonation(User user, Long donationId) { if(!donationUser.getDonationStatus().equals(EXECUTION_BEFORE)) throw new BadRequestException(CANNOT_DELETE_DONATION_STATUS); - paymentService.refundPayment(donationUser.getTid()); + portOneService.refundPayment(donationUser.getTid()); donationUser.setDonationStatus(EXECUTION_REFUND); } @@ -82,19 +84,19 @@ public void cancelRegularPay(User user, Long regularId) { public DonationRes.DonationCount getDonationCount(User user) { List donationUser = donationAdaptor.findByDonationCount(user); - return donationConvertor.convertToDonationCount(donationUser); + return donationConverter.convertToDonationCount(donationUser); } public PageResponse> getBurningMatch(User user, int page, int size) { Page flameLists = donationAdaptor.findFlameList(user, page, size); - return new PageResponse<>(flameLists.isLast(), flameLists.getTotalElements(), donationConvertor.BurningMatch(flameLists.getContent())); + return new PageResponse<>(flameLists.isLast(), flameLists.getTotalElements(), donationConverter.BurningMatch(flameLists.getContent())); } @Transactional public DonationRes.DonationRegular getDonationRegular(Long regularPayId, User user) { RegularPayment regularPayment = regularPaymentAdaptor.findById(regularPayId); - return donationConvertor.convertToDonationRegular(regularPayment); + return donationConverter.convertToDonationRegular(regularPayment); } @Transactional @@ -103,19 +105,19 @@ public PageResponse> getDonationRegularLis Page donationHistories = donationHistoryAdaptor.findDonationRegularList(regularPayId, regularPayment.getProjectId(), HistoryStatus.TURN_ON ,page, size); - return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConvertor.convertToDonationRegularList(donationHistories.getContent(), "")); + return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConverter.convertToDonationRegularList(donationHistories.getContent(), "")); } public List getPayList(User user, Long regularPayId) { List donationUsers = donationAdaptor.findPayList(regularPayId); - return donationConvertor.convertToPayList(donationUsers); + return donationConverter.convertToPayList(donationUsers); } public PageResponse> getFlameProjectList(User user, String content, int page, int size) { Page donationUsers = donationAdaptor.getFlameProjectList(user, content, page, size); - return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationConvertor.convertToFlameProjectList(donationUsers.getContent())); + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationConverter.convertToFlameProjectList(donationUsers.getContent())); } public PageResponse> getFlameRegularList(Long donationId, User user, int page, int size) { @@ -123,31 +125,51 @@ public PageResponse> getFlameRegularList(L Page donationHistories = donationHistoryAdaptor.findDonationHistory(donationUser, donationId, page, size); - return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConvertor.convertToDonationRegularList(donationHistories.getContent(), donationUser.getInherenceName())); + return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConverter.convertToDonationRegularList(donationHistories.getContent(), donationUser.getInherenceName())); } public DonationRes.DonationFlame getFlameRegular(Long donationId, User user) { DonationUser donationUser = donationAdaptor.findById(donationId); int sequence = donationHelper.getDonationSequence(donationUser, donationId); - return donationConvertor.convertToDonationFlame(sequence, donationUser); + return donationConverter.convertToDonationFlame(sequence, donationUser); } public PageResponse> getMatchHistory(User user, Long projectId, int page, int size) { Page donationHistories = donationHistoryAdaptor.findMatchHistory(projectId, page, size); - return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConvertor.convertToMatchHistory(donationHistories.getContent())); + return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConverter.convertToMatchHistory(donationHistories.getContent())); } public PageResponse> getUserMatchList(User user, int page, int size) { Page regularPayments = regularPaymentAdaptor.findByUser(user, page, size); - return new PageResponse<>(regularPayments.isLast(), regularPayments.getTotalElements(), regularPaymentConvertor.convertToMatchList(regularPayments.getContent())); + return new PageResponse<>(regularPayments.isLast(), regularPayments.getTotalElements(), regularPaymentConverter.convertToMatchList(regularPayments.getContent())); } @Cacheable(value = "flameCache", key = "{#user.id, #page, #size}", cacheManager = "ehcacheManager") public PageResponse> getBurningFlameList(User user, int page, int size) { Page donationUsers = donationAdaptor.findByUser(user, page, size); - return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), regularPaymentConvertor.convertToBurningFlameList(donationUsers.getContent())); + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), regularPaymentConverter.convertToBurningFlameList(donationUsers.getContent())); } + public void deleteRegularPayment(User user) { + List regularPayments = regularPaymentRepository.findByUser(user); + for (RegularPayment regularPayment : regularPayments) { + regularPayment.setStatus(INACTIVE); + regularPayment.setRegularPayStatus(USER_CANCEL); + } + regularPaymentAdaptor.saveAll(regularPayments); + } + + public DonationRes.CompleteDonation postTutorialDonation(User user, DonationReq.Tutorial tutorial, Project project) { + DonationUser donationUser = donationAdaptor.save(donationConverter.convertToTutorialDonation(user, tutorial, orderHelper.createInherence(user))); + + donationHistoryService.oneTimeDonationHistory(donationUser.getId()); + + return donationConverter.convertToCompleteDonation(donationUser, project); + } + + public Page findByUserId(User user, int page, int size) { + return donationAdaptor.findByUserForAdminPage(user, page, size); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java index 9fac9ded..85ffef0d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java @@ -1,6 +1,6 @@ package com.example.matchapi.donation.service; -import com.example.matchapi.donation.convertor.DonationTemporaryConvertor; +import com.example.matchapi.donation.converter.DonationTemporaryConverter; import com.example.matchapi.donation.dto.DonationTemporaryReq; import com.example.matchapi.donation.dto.DonationTemporaryRes; import com.example.matchapi.donation.helper.DonationHelper; @@ -26,19 +26,19 @@ @Service @RequiredArgsConstructor public class DonationTemporaryService { - private final DonationTemporaryConvertor donationTemporaryConvertor; + private final DonationTemporaryConverter donationTemporaryConverter; private final DonationTemporaryRepository donationTemporaryRepository; private final DonationListRepository donationListRepository; private final DonationHelper donationHelper; public DonationTemporaryRes.UserInfo getUserInfo(User user) { - return donationTemporaryConvertor.convertToUserInfo(user); + return donationTemporaryConverter.convertToUserInfo(user); } public void postDonationTemporary(User user, DonationTemporaryReq.DonationInfo donationInfo) { if(donationInfo.getAlarmMethod() == AlarmMethod.SMS) { - donationTemporaryRepository.save(donationTemporaryConvertor.convertToDonationInfo(user, donationInfo)); + donationTemporaryRepository.save(donationTemporaryConverter.convertToDonationInfo(user, donationInfo)); }else{ - donationTemporaryRepository.save(donationTemporaryConvertor.convertToDonationInfoEmail(user, donationInfo)); + donationTemporaryRepository.save(donationTemporaryConverter.convertToDonationInfoEmail(user, donationInfo)); } } @@ -49,7 +49,7 @@ public PageResponse> getDonationList(Don donationLists.getContent().forEach( result -> donationList.add( - donationTemporaryConvertor.convertToDonationList(result, donationHelper.parsePriceComma(result.getAmount())) + donationTemporaryConverter.convertToDonationList(result, donationHelper.parsePriceComma(result.getAmount())) ) ); @@ -73,7 +73,7 @@ public PageResponse> getDona donationTemporaries = donationTemporaryRepository.findByNameContainingAndDepositOrderByCreatedAtDesc(content,deposit, pageable); } } - return new PageResponse<>(donationTemporaries.isLast(), donationTemporaries.getTotalElements(), donationTemporaryConvertor.convertToDonationRequestAdminList(donationTemporaries.getContent())); + return new PageResponse<>(donationTemporaries.isLast(), donationTemporaries.getTotalElements(), donationTemporaryConverter.convertToDonationRequestAdminList(donationTemporaries.getContent())); } @Transactional @@ -81,7 +81,7 @@ public void postDonationDeposit(DonationTemporaryReq.DonationDeposit donationDep DonationTemporary donationTemporary = donationTemporaryRepository.findById(donationDeposit.getDonationRequestId()) .orElseThrow(()-> new BadRequestException(NOT_EXIST_DONATION_REQUEST)); - donationListRepository.save(donationTemporaryConvertor.convertToDonationDeposit(donationDeposit)); + donationListRepository.save(donationTemporaryConverter.convertToDonationDeposit(donationDeposit)); donationTemporary.setDeposit(Deposit.EXISTENCE); donationTemporaryRepository.save(donationTemporary); @@ -90,6 +90,6 @@ public void postDonationDeposit(DonationTemporaryReq.DonationDeposit donationDep public DonationTemporaryRes.DonationDetail getDonationInfo(Long donationRequestId) { DonationTemporary donationTemporary = donationTemporaryRepository.findById(donationRequestId) .orElseThrow(()-> new BadRequestException(NOT_EXIST_DONATION_REQUEST)); - return donationTemporaryConvertor.convertToDonationInfoDetail(donationTemporary); + return donationTemporaryConverter.convertToDonationInfoDetail(donationTemporary); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/event/convetor/EventConvertor.java b/Match-Api/src/main/java/com/example/matchapi/event/converter/EventConverter.java similarity index 94% rename from Match-Api/src/main/java/com/example/matchapi/event/convetor/EventConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/event/converter/EventConverter.java index 96baa480..88305ca5 100644 --- a/Match-Api/src/main/java/com/example/matchapi/event/convetor/EventConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/event/converter/EventConverter.java @@ -1,10 +1,9 @@ -package com.example.matchapi.event.convetor; +package com.example.matchapi.event.converter; import com.example.matchapi.admin.event.dto.EventUploadReq; import com.example.matchapi.common.util.TimeHelper; import com.example.matchapi.event.dto.EventRes; -import com.example.matchapi.notice.dto.NoticeRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.common.model.ContentsType; import com.example.matchdomain.event.entity.Event; import com.example.matchdomain.event.entity.EventContent; @@ -13,9 +12,9 @@ import java.util.ArrayList; import java.util.List; -@Convertor +@Converter @RequiredArgsConstructor -public class EventConvertor { +public class EventConverter { private final TimeHelper timeHelper; public List convertToEventList(List content) { List eventLists = new ArrayList<>(); diff --git a/Match-Api/src/main/java/com/example/matchapi/event/service/EventService.java b/Match-Api/src/main/java/com/example/matchapi/event/service/EventService.java index f528209e..5ddb426a 100644 --- a/Match-Api/src/main/java/com/example/matchapi/event/service/EventService.java +++ b/Match-Api/src/main/java/com/example/matchapi/event/service/EventService.java @@ -1,12 +1,11 @@ package com.example.matchapi.event.service; -import com.example.matchapi.event.convetor.EventConvertor; +import com.example.matchapi.event.converter.EventConverter; import com.example.matchapi.event.dto.EventRes; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.event.adaptor.EventAdaptor; import com.example.matchdomain.event.entity.Event; import lombok.RequiredArgsConstructor; -import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; @@ -16,7 +15,7 @@ @Service @RequiredArgsConstructor public class EventService { - private final EventConvertor eventConvertor; + private final EventConverter eventConverter; private final EventAdaptor eventAdaptor; @Cacheable(value = "eventCache", key = "{#page, #size}", cacheManager = "ehcacheManager") @@ -27,12 +26,12 @@ public PageResponse> getEventList(int page, int size) { } public List cachingEventLists(List content) { - return eventConvertor.convertToEventList(content); + return eventConverter.convertToEventList(content); } public EventRes.EventDetail getEventDetail(Long eventId) { Event event = eventAdaptor.findByEvent(eventId); - return eventConvertor.convertToEventDetail(event); + return eventConverter.convertToEventDetail(event); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/keword/convertor/KeywordConvertor.java b/Match-Api/src/main/java/com/example/matchapi/keword/converter/KeywordConverter.java similarity index 77% rename from Match-Api/src/main/java/com/example/matchapi/keword/convertor/KeywordConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/keword/converter/KeywordConverter.java index 1721983c..4718e898 100644 --- a/Match-Api/src/main/java/com/example/matchapi/keword/convertor/KeywordConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/keword/converter/KeywordConverter.java @@ -1,12 +1,12 @@ -package com.example.matchapi.keword.convertor; +package com.example.matchapi.keword.converter; import com.example.matchapi.keword.dto.KeywordRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchcommon.reponse.CommonResponse; import com.example.matchdomain.keyword.entity.SearchKeyword; -@Convertor -public class KeywordConvertor { +@Converter +public class KeywordConverter { public KeywordRes.KeywordList convertToKeywordList(SearchKeyword result) { return KeywordRes.KeywordList .builder() diff --git a/Match-Api/src/main/java/com/example/matchapi/keword/service/KeywordService.java b/Match-Api/src/main/java/com/example/matchapi/keword/service/KeywordService.java index a4a0a852..7ab59152 100644 --- a/Match-Api/src/main/java/com/example/matchapi/keword/service/KeywordService.java +++ b/Match-Api/src/main/java/com/example/matchapi/keword/service/KeywordService.java @@ -1,7 +1,7 @@ package com.example.matchapi.keword.service; import com.example.matchapi.admin.keyword.dto.AdminKeywordReq; -import com.example.matchapi.keword.convertor.KeywordConvertor; +import com.example.matchapi.keword.converter.KeywordConverter; import com.example.matchapi.keword.dto.KeywordRes; import com.example.matchdomain.keyword.entity.SearchKeyword; import com.example.matchdomain.keyword.repository.SearchKeywordRepository; @@ -17,7 +17,7 @@ @Service @RequiredArgsConstructor public class KeywordService { - private final KeywordConvertor keywordConvertor; + private final KeywordConverter keywordConverter; private final SearchKeywordRepository searchKeywordRepository; @@ -31,7 +31,7 @@ public List getKeyword(){ List keywordLists = new ArrayList<>(); searchKeywords.forEach( - result -> keywordLists.add(keywordConvertor.convertToKeywordList(result)) + result -> keywordLists.add(keywordConverter.convertToKeywordList(result)) ); return keywordLists; } diff --git a/Match-Api/src/main/java/com/example/matchapi/notice/convertor/NoticeConvertor.java b/Match-Api/src/main/java/com/example/matchapi/notice/converter/NoticeConverter.java similarity index 93% rename from Match-Api/src/main/java/com/example/matchapi/notice/convertor/NoticeConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/notice/converter/NoticeConverter.java index 37895de3..fba91e08 100644 --- a/Match-Api/src/main/java/com/example/matchapi/notice/convertor/NoticeConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/notice/converter/NoticeConverter.java @@ -1,8 +1,8 @@ -package com.example.matchapi.notice.convertor; +package com.example.matchapi.notice.converter; import com.example.matchapi.common.util.TimeHelper; import com.example.matchapi.notice.dto.NoticeRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.notice.entity.Notice; import com.example.matchdomain.notice.entity.NoticeContent; import lombok.RequiredArgsConstructor; @@ -10,9 +10,9 @@ import java.util.ArrayList; import java.util.List; -@Convertor +@Converter @RequiredArgsConstructor -public class NoticeConvertor { +public class NoticeConverter { private final TimeHelper timeHelper; public List convertToNoticeList(List content) { List noticeLists = new ArrayList<>(); diff --git a/Match-Api/src/main/java/com/example/matchapi/notice/service/NoticeService.java b/Match-Api/src/main/java/com/example/matchapi/notice/service/NoticeService.java index 8b7c7dfb..d4f70379 100644 --- a/Match-Api/src/main/java/com/example/matchapi/notice/service/NoticeService.java +++ b/Match-Api/src/main/java/com/example/matchapi/notice/service/NoticeService.java @@ -1,6 +1,6 @@ package com.example.matchapi.notice.service; -import com.example.matchapi.notice.convertor.NoticeConvertor; +import com.example.matchapi.notice.converter.NoticeConverter; import com.example.matchapi.notice.dto.NoticeRes; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.notice.adaptor.NoticeAdapter; @@ -15,18 +15,18 @@ @Service @RequiredArgsConstructor public class NoticeService { - private final NoticeConvertor noticeConvertor; + private final NoticeConverter noticeConverter; private final NoticeAdapter noticeAdapter; @Cacheable(value = "noticeCache", key = "{#page, #size}", cacheManager = "ehcacheManager") public PageResponse> getNoticeList(int page, int size) { Page notices = noticeAdapter.getNoticeList(page, size); - return new PageResponse<>(notices.isLast(), notices.getTotalElements(), noticeConvertor.convertToNoticeList(notices.getContent())); + return new PageResponse<>(notices.isLast(), notices.getTotalElements(), noticeConverter.convertToNoticeList(notices.getContent())); } public NoticeRes.NoticeDetail getNoticeDetail(Long noticeId) { Notice notice = noticeAdapter.findNoticeDetail(noticeId); - return noticeConvertor.convertToNoticeDetail(notice); + return noticeConverter.convertToNoticeDetail(notice); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/notification/convertor/NotificationConvertor.java b/Match-Api/src/main/java/com/example/matchapi/notification/converter/NotificationConverter.java similarity index 94% rename from Match-Api/src/main/java/com/example/matchapi/notification/convertor/NotificationConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/notification/converter/NotificationConverter.java index ef7c2c91..384fab67 100644 --- a/Match-Api/src/main/java/com/example/matchapi/notification/convertor/NotificationConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/notification/converter/NotificationConverter.java @@ -1,8 +1,8 @@ -package com.example.matchapi.notification.convertor; +package com.example.matchapi.notification.converter; import com.example.matchapi.common.util.TimeHelper; import com.example.matchapi.notification.dto.NotificationRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.notification.entity.Notification; import com.example.matchdomain.notification.enums.NotificationType; @@ -14,9 +14,9 @@ import java.util.ArrayList; import java.util.List; -@Convertor +@Converter @RequiredArgsConstructor -public class NotificationConvertor { +public class NotificationConverter { private final TimeHelper timeHelper; public List convertToNotificationList(List notifications) { List notificationLists = new ArrayList<>(); diff --git a/Match-Api/src/main/java/com/example/matchapi/notification/service/NotificationService.java b/Match-Api/src/main/java/com/example/matchapi/notification/service/NotificationService.java index af5052d9..0f805bff 100644 --- a/Match-Api/src/main/java/com/example/matchapi/notification/service/NotificationService.java +++ b/Match-Api/src/main/java/com/example/matchapi/notification/service/NotificationService.java @@ -1,6 +1,6 @@ package com.example.matchapi.notification.service; -import com.example.matchapi.notification.convertor.NotificationConvertor; +import com.example.matchapi.notification.converter.NotificationConverter; import com.example.matchapi.notification.dto.NotificationRes; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.notification.adaptor.NotificationAdaptor; @@ -17,16 +17,16 @@ @RequiredArgsConstructor public class NotificationService { private final NotificationAdaptor notificationAdaptor; - private final NotificationConvertor notificationConvertor; + private final NotificationConverter notificationConverter; public PageResponse getNotificationList(User user, int page, int size) { Page notifications = notificationAdaptor.findByUser(user, page, size); int notificationCount = notificationAdaptor.countByUnRead(user); - return new PageResponse<>(notifications.isLast(), notifications.getTotalElements(), new NotificationRes.NotificationListInfo(notificationCount, notificationConvertor.convertToNotificationList(notifications.getContent()))); + return new PageResponse<>(notifications.isLast(), notifications.getTotalElements(), new NotificationRes.NotificationListInfo(notificationCount, notificationConverter.convertToNotificationList(notifications.getContent()))); } public void saveTestNotification(User user, FCMNotificationRequestDto fcmNotificationRequestDto) { - Notification notification = notificationConvertor.convertToNotificationTest(user, fcmNotificationRequestDto); + Notification notification = notificationConverter.convertToNotificationTest(user, fcmNotificationRequestDto); notificationAdaptor.saveNotification(notification); } @@ -34,6 +34,6 @@ public void saveTestNotification(User user, FCMNotificationRequestDto fcmNotific public NotificationRes.NotificationDetail getNotificationDetail(Long notificationId) { Notification notification = notificationAdaptor.findNotification(notificationId); notificationAdaptor.readNotification(notification); - return notificationConvertor.convertNotificationDetail(notification); + return notificationConverter.convertNotificationDetail(notification); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java b/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java index 97a5c4b2..762e083f 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java @@ -4,22 +4,32 @@ import com.example.matchapi.common.aop.CheckOneTimeProject; import com.example.matchapi.common.aop.CheckRegularProject; import com.example.matchapi.donation.dto.DonationRes; +import com.example.matchapi.order.dto.OrderCommand; import com.example.matchapi.order.dto.OrderReq; import com.example.matchapi.order.dto.OrderRes; +import com.example.matchapi.order.helper.OrderHelper; +import com.example.matchapi.order.mapper.OrderMapper; +import com.example.matchapi.order.service.CardService; import com.example.matchapi.order.service.OrderService; +import com.example.matchapi.project.service.ProjectService; +import com.example.matchapi.user.service.AligoService; import com.example.matchapi.user.service.UserService; import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.constants.MatchStatic; import com.example.matchcommon.exception.errorcode.NicePayErrorCode; import com.example.matchcommon.exception.errorcode.OtherServerErrorCode; import com.example.matchcommon.exception.errorcode.RequestErrorCode; import com.example.matchcommon.properties.NicePayProperties; import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchdomain.donation.entity.UserCard; import com.example.matchdomain.donation.exception.DeleteCardErrorCode; import com.example.matchdomain.order.exception.RegistrationCardErrorCode; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.project.exception.ProjectOneTimeErrorCode; import com.example.matchdomain.project.exception.ProjectRegualrErrorCode; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.exception.UserAuthErrorCode; +import com.example.matchinfrastructure.aligo.converter.AligoConverter; import com.example.matchinfrastructure.pay.nice.dto.NicePaymentAuth; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; import io.swagger.v3.oas.annotations.Operation; @@ -27,6 +37,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.aspectj.weaver.ast.Or; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.ui.Model; @@ -38,6 +49,9 @@ import java.io.IOException; import java.util.List; +import static com.example.matchdomain.donation.entity.enums.RegularStatus.ONE_TIME; +import static com.example.matchdomain.donation.entity.enums.RegularStatus.REGULAR; + @RequestMapping("/order") @RequiredArgsConstructor @@ -47,6 +61,11 @@ public class OrderController { private final OrderService orderService; private final UserService userService; + private final CardService cardService; + private final ProjectService projectService; + private final OrderHelper orderHelper; + private final OrderMapper orderMapper = OrderMapper.INSTANCE; + @PostMapping("/{projectId}") @ApiErrorCodeExample(UserAuthErrorCode.class) @@ -108,7 +127,10 @@ public CommonResponse regularDonation( @Parameter(description = "카드 id",example = "1") @PathVariable Long cardId, @Parameter(description = "프로젝트 id", example = "2") @PathVariable Long projectId, @Valid @RequestBody OrderReq.RegularDonation regularDonation){ - return CommonResponse.onSuccess(orderService.regularDonation(user, regularDonation, cardId, projectId)); + UserCard card = cardService.findByCardId(cardId); + Project project = projectService.checkProjectExists(projectId, REGULAR); + String orderId = orderHelper.createOrderId(MatchStatic.REGULAR); + return CommonResponse.onSuccess(orderService.paymentForRegular(orderMapper.toRegularDonation(card, regularDonation, user, project, orderId))); } @PostMapping("/pay/one/card/{cardId}/{projectId}") @@ -119,7 +141,11 @@ public CommonResponse oneTimeDonationCard( @Parameter(description = "카드 id",example = "1") @PathVariable Long cardId, @Parameter(description = "프로젝트 id", example = "2") @PathVariable Long projectId, @Valid @RequestBody OrderReq.OneTimeDonation oneTimeDonation){ - return CommonResponse.onSuccess(orderService.oneTimeDonationCard(user, oneTimeDonation, cardId, projectId)); + UserCard card = cardService.findByCardId(cardId); + Project project = projectService.checkProjectExists(projectId, ONE_TIME); + String orderId = orderHelper.createOrderId(MatchStatic.ONE_TIME); + log.info(orderId); + return CommonResponse.onSuccess(orderService.paymentForOnetime(orderMapper.toOneTimeDonation(card, oneTimeDonation, user, project, orderId))); } @PostMapping("/user") diff --git a/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java b/Match-Api/src/main/java/com/example/matchapi/order/converter/OrderConverter.java similarity index 86% rename from Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/order/converter/OrderConverter.java index 0ae2ee69..9e0d4a1d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/converter/OrderConverter.java @@ -1,11 +1,12 @@ -package com.example.matchapi.order.convertor; +package com.example.matchapi.order.converter; import com.example.matchapi.donation.helper.DonationHelper; import com.example.matchapi.order.dto.OrderReq; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.helper.OrderHelper; import com.example.matchapi.portone.dto.PaymentReq; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; +import com.example.matchcommon.util.AesUtil; import com.example.matchdomain.donation.entity.*; import com.example.matchdomain.donation.entity.enums.*; import com.example.matchdomain.donation.entity.flameEnum.FlameImage; @@ -15,6 +16,7 @@ import com.example.matchinfrastructure.pay.nice.dto.*; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; +import com.siot.IamportRestClient.response.Payment; import lombok.RequiredArgsConstructor; import java.util.ArrayList; @@ -22,11 +24,12 @@ import static java.lang.Integer.parseInt; -@Convertor +@Converter @RequiredArgsConstructor -public class OrderConvertor { +public class OrderConverter { private final OrderHelper orderHelper; private final DonationHelper donationHelper; + private final AesUtil aesUtil; public RegularPayment convertToRegularPayment(Long id, OrderReq.RegularDonation regularDonation, Long userCardId, Long projectId) { return RegularPayment.builder() @@ -44,23 +47,23 @@ public OrderRequest CreateRequest(Long userId, Long projectId, String orderId) { .userId(String.valueOf(userId)) .projectId(String.valueOf(projectId)) .orderId(orderId) - .ttl(10L) + .ttl(2400L) .build(); } - public DonationUser convertToDonationUserPortone(Long userId, PaymentReq.ValidatePayment validatePayment, Long projectId, OrderRes.CreateInherenceDto createInherenceDto) { + public DonationUser convertToDonationUserPortone(Long userId, Payment payment, Long projectId, OrderRes.CreateInherenceDto createInherenceDto) { return DonationUser.builder() .userId(userId) - .payMethod(orderHelper.getPayMethod(validatePayment.getPayMethod())) .projectId(projectId) - .price((long) validatePayment.getAmount()) - .tid(validatePayment.getImpUid()) - .orderId(validatePayment.getOrderId()) + .price((long) payment.getAmount().intValue()) + .tid(payment.getImpUid()) + .orderId(payment.getMerchantUid()) .donationStatus(DonationStatus.EXECUTION_BEFORE) - .payMethod(orderHelper.getPayMethod(validatePayment.getPayMethod())) + .payMethod(orderHelper.getPayMethod(payment.getPayMethod())) .inherenceName(createInherenceDto.getInherenceName()) .inherenceNumber(createInherenceDto.getInherenceNumber()) .regularStatus(RegularStatus.ONE_TIME) .flameImage(FlameImage.NORMAL_IMG.getImg()) + .flameType(FlameType.NORMAL_FLAME) .build(); } @@ -68,11 +71,7 @@ public UserCard convertToUserBillCard(Long id, OrderReq.RegistrationCard registr return UserCard.builder() .userId(id) .bid(portOneBillResponse.getCustomer_uid()) - .cardNo(registrationCard.getCardNo()) - .expYear(registrationCard.getExpYear()) - .expMonth(registrationCard.getExpMonth()) - .idNo(registrationCard.getIdNo()) - .cardPw(registrationCard.getCardPw()) + .cardNo(aesUtil.aesCBCEncode(registrationCard.getCardNo())) .cardCode(CardCode.getNameByCode(portOneBillResponse.getCard_code())) .cardName(portOneBillResponse.getCard_code()) .customerId(portOneBillResponse.getCustomer_id()) @@ -119,6 +118,7 @@ public List convertToUserCardLists(List userCar .builder() .id(result.getId()) .cardCode(result.getCardCode().getCode()) + .cardName(result.getCardCode().getName()) .cardNo(orderHelper.maskMiddleNum(result.getCardNo())) .cardAbleStatus(result.getCardAbleStatus().getName()) .build() diff --git a/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderCommand.java b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderCommand.java new file mode 100644 index 00000000..55e94ad8 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderCommand.java @@ -0,0 +1,44 @@ +package com.example.matchapi.order.dto; + +import com.example.matchdomain.donation.entity.UserCard; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.user.entity.User; +import lombok.*; + +public class OrderCommand { + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class OneTimeDonation{ + private UserCard userCard; + + private OrderReq.OneTimeDonation oneTimeDonation; + + private User user; + + private Project project; + + private String orderId; + } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class RegularDonation{ + private UserCard userCard; + + private OrderReq.RegularDonation regularDonation; + + private User user; + + private Project project; + + private String orderId; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderReq.java b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderReq.java index ed770152..7f66dea7 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderReq.java @@ -13,6 +13,7 @@ public class OrderReq { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString @Schema(description = "04-01💸 기부 결제 요청 API Request") public static class OrderDetail { @NotEmpty(message = "나이스 페이먼츠 고유 거래 키 값을 입력해주세요") @@ -28,6 +29,7 @@ public static class OrderDetail { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString @Schema(description = "04-02💸 정기 결제용 카드 등록 API Request") public static class RegistrationCard { @@ -61,6 +63,7 @@ public static class RegistrationCard { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString @Schema(description = "04-02💸 정기 결제용 카드 등록 API Request") public static class RegularDonation { @@ -78,6 +81,7 @@ public static class RegularDonation { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString @Schema(description = "04-06💸 단기 결제용 카드 결제 API Request") public static class OneTimeDonation { diff --git a/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java index 928292c4..c4ba2e22 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java @@ -1,9 +1,12 @@ package com.example.matchapi.order.dto; import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; +import java.time.LocalDate; + public class OrderRes { @Getter @Setter @@ -31,7 +34,8 @@ public static class UserBillCard { public static class UserDetail { private String name; - private String birthDay; + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate birthDay; private String phoneNumber; } @@ -65,4 +69,25 @@ public static class CreateInherenceDto { private String inherenceNumber; } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class PaymentInfoDto { + private String name; + + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate birth; + + private String phone; + + private String usages; + + private RegularStatus regularStatus; + + private String accessToken; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java b/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java index 5f550f1b..878de4a0 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java @@ -2,6 +2,7 @@ import com.example.matchapi.order.dto.OrderRes; import com.example.matchcommon.annotation.Helper; +import com.example.matchcommon.util.AesUtil; import com.example.matchdomain.donation.adaptor.DonationAdaptor; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.PayMethod; @@ -24,6 +25,7 @@ @RequiredArgsConstructor public class OrderHelper { private final DonationAdaptor donationAdaptor; + private final AesUtil aesUtil; public PayMethod getPayMethod(String value) { for (PayMethod payMethod : PayMethod.values()) { @@ -46,7 +48,7 @@ public String createFlameName(User user) { String randomName; do { - randomName = user.getName() + "님의 " + getRandomEnumValue(AdjectiveFlame.class).getValue() + " " + getRandomEnumValue(Adjective.class).getValue() + " 불꽃이"; + randomName = getRandomEnumValue(AdjectiveFlame.class).getValue() + " 불꽃이"; } while (inherenceNames.contains(randomName)); return randomName; @@ -60,6 +62,7 @@ public static > T getRandomEnumValue(Class enumClass) { } public String maskMiddleNum(String cardNo) { + cardNo = aesUtil.aesCBCDecode(cardNo); String firstFourDigits = cardNo.substring(0, 4); String lastFourDigits = cardNo.substring(12); String middleDigitsMasked = "********"; diff --git a/Match-Api/src/main/java/com/example/matchapi/order/mapper/OrderMapper.java b/Match-Api/src/main/java/com/example/matchapi/order/mapper/OrderMapper.java new file mode 100644 index 00000000..ba17f3f3 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/order/mapper/OrderMapper.java @@ -0,0 +1,23 @@ +package com.example.matchapi.order.mapper; + +import com.example.matchapi.order.dto.OrderCommand; +import com.example.matchapi.order.dto.OrderReq; +import com.example.matchdomain.donation.entity.UserCard; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.user.entity.User; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface OrderMapper { + OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class); + + @Mapping(target = "userCard", source = "userCard") + @Mapping(target = "orderId", source = "orderId") + OrderCommand.OneTimeDonation toOneTimeDonation(UserCard userCard, OrderReq.OneTimeDonation oneTimeDonation, User user, Project project, String orderId); + + @Mapping(target = "userCard", source = "userCard") + @Mapping(target = "orderId", source = "orderId") + OrderCommand.RegularDonation toRegularDonation(UserCard userCard, OrderReq.RegularDonation regularDonation, User user, Project project, String orderId); +} diff --git a/Match-Api/src/main/java/com/example/matchapi/order/service/CardService.java b/Match-Api/src/main/java/com/example/matchapi/order/service/CardService.java new file mode 100644 index 00000000..01e88b23 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/order/service/CardService.java @@ -0,0 +1,16 @@ +package com.example.matchapi.order.service; + +import com.example.matchdomain.donation.entity.UserCard; +import com.example.matchdomain.user.adaptor.UserCardAdaptor; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class CardService { + private final UserCardAdaptor userCardAdaptor; + + public UserCard findByCardId(Long cardId) { + return userCardAdaptor.findCardByCardId(cardId); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/order/service/OrderRequestService.java b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderRequestService.java new file mode 100644 index 00000000..421c4cca --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderRequestService.java @@ -0,0 +1,24 @@ +package com.example.matchapi.order.service; + +import com.example.matchdomain.redis.adaptor.OrderAdaptor; +import com.example.matchdomain.redis.entity.OrderRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class OrderRequestService { + private final OrderAdaptor orderAdaptor; + public OrderRequest findByOrderIdForPayment(String orderId) { + OrderRequest orderRequest = findByOrderId(orderId); + + orderAdaptor.deleteById(orderId); + + return orderRequest; + } + + public OrderRequest findByOrderId(String orderId) { + return orderAdaptor.findById(orderId); + } + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java index 163fcbdd..56fb866a 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java @@ -1,17 +1,22 @@ package com.example.matchapi.order.service; +import com.example.matchapi.common.security.JwtService; import com.example.matchapi.donation.service.DonationHistoryService; -import com.example.matchapi.order.convertor.OrderConvertor; +import com.example.matchapi.order.converter.OrderConverter; +import com.example.matchapi.order.dto.OrderCommand; import com.example.matchapi.order.dto.OrderReq; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.helper.OrderHelper; import com.example.matchapi.portone.service.PaymentService; import com.example.matchapi.project.service.ProjectService; +import com.example.matchapi.user.service.AligoService; +import com.example.matchcommon.annotation.PaymentIntercept; import com.example.matchcommon.annotation.RedissonLock; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.exception.BaseException; import com.example.matchdomain.common.model.Status; import com.example.matchdomain.donation.adaptor.RegularPaymentAdaptor; +import com.example.matchdomain.donation.adaptor.RequestFailedHistoryAdapter; import com.example.matchdomain.donation.entity.*; import com.example.matchdomain.donation.entity.enums.CardAbleStatus; import com.example.matchdomain.donation.entity.enums.DonationStatus; @@ -22,15 +27,20 @@ import com.example.matchdomain.redis.repository.OrderRequestRepository; import com.example.matchdomain.user.adaptor.UserCardAdaptor; import com.example.matchdomain.user.entity.User; -import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; +import com.example.matchinfrastructure.aligo.converter.AligoConverter; +import com.example.matchinfrastructure.aligo.dto.AlimType; +import com.example.matchinfrastructure.pay.portone.converter.PortOneConverter; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; import com.example.matchinfrastructure.pay.portone.dto.req.PortOnePrepareReq; import com.example.matchinfrastructure.pay.portone.service.PortOneAuthService; import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; +import com.example.matchinfrastructure.pay.portone.service.PortOneService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; + import javax.transaction.Transactional; import java.util.*; @@ -41,27 +51,32 @@ @RequiredArgsConstructor @Service +@Slf4j public class OrderService { private final DonationUserRepository donationUserRepository; - private final OrderConvertor orderConvertor; + private final OrderConverter orderConverter; private final OrderHelper orderHelper; private final RegularPaymentRepository regularPaymentRepository; private final UserCardRepository userCardRepository; private final OrderRequestRepository orderRequestRepository; private final PortOneFeignClient portOneFeignClient; private final PortOneAuthService portOneAuthService; - private final PortOneConvertor portOneConvertor; + private final PortOneConverter portOneConverter; private final PaymentService paymentService; - private final ProjectService projectService; private final RegularPaymentAdaptor regularPaymentAdaptor; private final UserCardAdaptor userCardAdaptor; private final DonationHistoryService donationHistoryService; + private final RequestFailedHistoryAdapter failedHistoryAdapter; + private final PortOneService portOneService; + private final AligoService aligoService; + private final AligoConverter aligoConverter; + private final JwtService jwtService; @Transactional public List getUserBillCard(Long userId) { List userCards = userCardAdaptor.findCardLists(userId); - return orderConvertor.convertToUserCardLists(userCards); + return orderConverter.convertToUserCardLists(userCards); } @RedissonLock(LockName = "카드-삭제", key = "#cardId") @@ -81,80 +96,88 @@ public void deleteBillCard(Long cardId) { userCardRepository.save(userCard); } - @RedissonLock(LockName = "정기-기부-신청", key = "#cardId") - public OrderRes.CompleteDonation regularDonation(User user, OrderReq.RegularDonation regularDonation, Long cardId, Long projectId) { - UserCard card = userCardAdaptor.findCardByCardId(cardId); + private void validateCard(UserCard card, User user) { + if (!card.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); - validateCard(card, user); + if (!card.getCardAbleStatus().equals(CardAbleStatus.ABLE)) throw new BadRequestException(CARD_NOT_ABLE); + } - Project project = projectService.checkProjectExists(projectId, RegularStatus.REGULAR); + @PaymentIntercept(key = "#orderCommand.orderId") + @RedissonLock(LockName = "빌키-단기-기부", key = "#orderCommand.userCard.id") + public OrderRes.CompleteDonation paymentForOnetime(OrderCommand.OneTimeDonation orderCommand) { + UserCard card = orderCommand.getUserCard(); + Project project = orderCommand.getProject(); + User user = orderCommand.getUser(); + OrderReq.OneTimeDonation oneTimeDonation = orderCommand.getOneTimeDonation(); - PortOneResponse portOneResponse = paymentService.payBillKey(card, regularDonation.getAmount(), project.getProjectName(), REGULAR); + System.out.println(orderCommand.getOrderId()); + + PortOneResponse portOneResponse = paymentService.payBillKey(card, oneTimeDonation.getAmount(), project.getProjectName(), orderCommand.getOrderId()); OrderRes.CreateInherenceDto createInherenceDto = orderHelper.createInherence(user); - RegularPayment regularPayment = regularPaymentRepository.save(orderConvertor.convertToRegularPayment(user.getId(), regularDonation, cardId, projectId)); + DonationUser donationUser = donationUserRepository.save( + orderConverter.donationBillPayUser(portOneResponse.getResponse(), user.getId(), + oneTimeDonation.getAmount(), project.getId(), + createInherenceDto, RegularStatus.ONE_TIME, null)); - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), regularDonation.getAmount(), projectId, createInherenceDto, RegularStatus.REGULAR, regularPayment.getId())); + donationHistoryService.oneTimeDonationHistory(donationUser.getId()); - donationHistoryService.postRegularDonationHistory(regularPayment.getId(), donationUser.getId()); + aligoService.sendAlimTalk(AlimType.PAYMENT, aligoConverter.convertToAlimTalkPayment(donationUser.getId(), user.getName(), user.getPhoneNumber())); - return orderConvertor.convertToCompleteDonation(user.getName(), project, regularDonation.getAmount()); + return orderConverter.convertToCompleteDonation(user.getName(), project, oneTimeDonation.getAmount()); } + @PaymentIntercept(key = "#orderCommand.orderId") + @RedissonLock(LockName = "정기-기부-신청", key = "#orderCommand.userCard.id") + public OrderRes.CompleteDonation paymentForRegular(OrderCommand.RegularDonation orderCommand) { + UserCard card = orderCommand.getUserCard(); + Project project = orderCommand.getProject(); + User user = orderCommand.getUser(); - private void validateCard(UserCard card, User user) { - if(!card.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); - - if(!card.getCardAbleStatus().equals(CardAbleStatus.ABLE)) throw new BadRequestException(CARD_NOT_ABLE); - } - - @RedissonLock(LockName = "빌키-단기-기부", key = "#cardId") - public OrderRes.CompleteDonation oneTimeDonationCard(User user, OrderReq.OneTimeDonation oneTimeDonation, Long cardId, Long projectId) { - try { - UserCard card = userCardAdaptor.findCardByCardId(cardId); + OrderReq.RegularDonation regularDonation = orderCommand.getRegularDonation(); - validateCard(card, user); + PortOneResponse portOneResponse = paymentService.payBillKey(card, regularDonation.getAmount(), project.getProjectName(), orderCommand.getOrderId()); - Project project = projectService.checkProjectExists(projectId, RegularStatus.ONE_TIME); - - PortOneResponse portOneResponse = paymentService.payBillKey(card, oneTimeDonation.getAmount(), project.getProjectName(), ONE_TIME); + OrderRes.CreateInherenceDto createInherenceDto = orderHelper.createInherence(user); - OrderRes.CreateInherenceDto createInherenceDto = orderHelper.createInherence(user); + RegularPayment regularPayment = regularPaymentRepository.save(orderConverter.convertToRegularPayment(user.getId(), regularDonation, card.getId(), project.getId())); - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), oneTimeDonation.getAmount(), projectId, - createInherenceDto, RegularStatus.ONE_TIME, null)); + DonationUser donationUser = donationUserRepository.save(orderConverter.donationBillPayUser( + portOneResponse.getResponse(), user.getId(), regularDonation.getAmount(), project.getId(), + createInherenceDto, RegularStatus.REGULAR, regularPayment.getId())); - donationHistoryService.oneTimeDonationHistory(donationUser.getId()); + donationHistoryService.postRegularDonationHistory(regularPayment.getId(), donationUser.getId()); - return orderConvertor.convertToCompleteDonation(user.getName(), project, oneTimeDonation.getAmount()); - }catch (Exception e){ - //paymentService.refundPayment(); - } + aligoService.sendAlimTalk(AlimType.PAYMENT, aligoConverter.convertToAlimTalkPayment(donationUser.getId(), user.getName(), user.getPhoneNumber())); - return null; + return orderConverter.convertToCompleteDonation(user.getName(), project, regularDonation.getAmount()); } @Transactional public String saveRequest(User user, Long projectId) { String orderId = orderHelper.createOrderId(ONE_TIME); - orderRequestRepository.save(orderConvertor.CreateRequest(user.getId(), projectId, orderId)); + orderRequestRepository.save(orderConverter.CreateRequest(user.getId(), projectId, orderId)); + + PortOnePrepareReq portOnePrepareReq = portOneConverter.convertToRequestPrepare(orderId, 1000); + + portOneFeignClient.preparePayments(portOneAuthService.getToken(), portOnePrepareReq); return orderId; } @RedissonLock(LockName = "관리자-환불-처리", key = "#donationUserId") public void adminRefundDonation(Long donationUserId) { - DonationUser donationUser = donationUserRepository.findById(donationUserId).orElseThrow(()-> new BadRequestException(DONATION_NOT_EXIST)); + DonationUser donationUser = donationUserRepository.findById(donationUserId).orElseThrow(() -> new BadRequestException(DONATION_NOT_EXIST)); donationUser.setDonationStatus(DonationStatus.EXECUTION_REFUND); - paymentService.refundPayment(donationUser.getTid()); + portOneService.refundPayment(donationUser.getTid()); donationUserRepository.save(donationUser); } @Transactional public void modifyDonationStatus(Long donationUserId, DonationStatus donationStatus) { - DonationUser donationUser = donationUserRepository.findById(donationUserId).orElseThrow(()-> new BadRequestException(DONATION_NOT_EXIST)); + DonationUser donationUser = donationUserRepository.findById(donationUserId).orElseThrow(() -> new BadRequestException(DONATION_NOT_EXIST)); donationUser.setDonationStatus(donationStatus); donationUserRepository.save(donationUser); } @@ -162,10 +185,10 @@ public void modifyDonationStatus(Long donationUserId, DonationStatus donationSta @Transactional public void revokePay(User user, Long cardId) { UserCard userCard = userCardAdaptor.findCardByCardId(cardId); - if(!userCard.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); + if (!userCard.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); List regularPayments = regularPaymentRepository.findByUserCardId(cardId); - for(RegularPayment regularPayment : regularPayments){ + for (RegularPayment regularPayment : regularPayments) { regularPayment.setRegularPayStatus(RegularPayStatus.USER_CANCEL); } @@ -180,24 +203,24 @@ public PortOneBillResponse postCard(User user, OrderReq.RegistrationCard registr PortOneResponse portOneResponse = portOneFeignClient.getBillKey( accessToken, orderHelper.createOrderId(BILL), - portOneConvertor.convertToPortOneBill(cardNo, expiry, registrationCard.getIdNo(), registrationCard.getCardPw()) + portOneConverter.convertToPortOneBill(cardNo, expiry, registrationCard.getIdNo(), registrationCard.getCardPw()) ); - if(portOneResponse.getCode()!=0){ + if (portOneResponse.getCode() != 0) { throw new BaseException(BAD_REQUEST, false, "PORT_ONE_BILL_AUTH_001", portOneResponse.getMessage()); } System.out.println(portOneResponse.getResponse().getCard_code()); - userCardRepository.save(orderConvertor.convertToUserBillCard(user.getId(), registrationCard, portOneResponse.getResponse())); + userCardRepository.save(orderConverter.convertToUserBillCard(user.getId(), registrationCard, portOneResponse.getResponse())); return portOneResponse.getResponse(); } - private void cancelRegularPayment(List regularPayments) { - for(RegularPayment regularPayment : regularPayments){ + for (RegularPayment regularPayment : regularPayments) { regularPayment.setRegularPayStatus(RegularPayStatus.USER_CANCEL); + failedHistoryAdapter.deleteByRegularPaymentId(regularPayment.getId()); } regularPaymentRepository.saveAll(regularPayments); } @@ -205,12 +228,12 @@ private void cancelRegularPayment(List regularPayments) { public String saveRequestPrepare(User user, Long projectId, int amount) { String orderId = orderHelper.createOrderId(ONE_TIME); - orderRequestRepository.save(orderConvertor.convertToRequestPrepare(user.getId(), projectId, amount, orderId)); + orderRequestRepository.save(orderConverter.convertToRequestPrepare(user.getId(), projectId, amount, orderId)); - PortOnePrepareReq portOnePrepareReq = portOneConvertor.convertToRequestPrepare(orderId, amount); + PortOnePrepareReq portOnePrepareReq = portOneConverter.convertToRequestPrepare(orderId, amount); portOneFeignClient.preparePayments(portOneAuthService.getToken(), portOnePrepareReq); return orderId; } -} \ No newline at end of file +} diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java b/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java index af4e5a53..c9a1abfc 100644 --- a/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java +++ b/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java @@ -1,31 +1,85 @@ package com.example.matchapi.portone.controller; +import com.example.matchapi.common.security.JwtService; import com.example.matchapi.order.dto.OrderRes; +import com.example.matchapi.order.service.OrderRequestService; import com.example.matchapi.portone.dto.PaymentReq; +import com.example.matchapi.portone.mapper.PaymentMapper; import com.example.matchapi.portone.service.PaymentService; -import com.example.matchapi.project.dto.ProjectReq; +import com.example.matchapi.project.service.ProjectService; +import com.example.matchapi.user.service.UserService; +import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.annotation.DisableSecurity; +import com.example.matchcommon.annotation.PaymentIntercept; import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchdomain.order.exception.PortOneAuthErrorCode; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.redis.entity.OrderRequest; +import com.example.matchdomain.user.entity.User; +import com.example.matchdomain.user.exception.UserAuthErrorCode; +import com.example.matchinfrastructure.pay.portone.dto.PortOneWebhook; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/payments") @Tag(name = "08-PortOne💸",description = "PortOne 결제 API") +@Slf4j @RequiredArgsConstructor public class PaymentController { private final PaymentService paymentService; + private final PaymentMapper mapper = PaymentMapper.INSTANCE; + private final UserService userService; + private final ProjectService projectService; + private final OrderRequestService orderRequestService; + private final JwtService jwtService; + @Value("${spring.config.activate.on-profile}") + private String profile; + @PostMapping("/validate") - @Operation(summary = "08-01 Payment 가격 검증💸") + @Operation(summary = "08-01 Payment 가격 검증💸", description = "결제 검증용 API 해당 API") + @PaymentIntercept(key = "#validatePayment.impUid") + @ApiErrorCodeExample({UserAuthErrorCode.class, PortOneAuthErrorCode.class}) + @DisableSecurity public CommonResponse validatePayment(@RequestBody PaymentReq.ValidatePayment validatePayment){ - return CommonResponse.onSuccess(paymentService.checkPayment(validatePayment)); + log.info("가격 검증"); + OrderRequest orderRequest = orderRequestService.findByOrderIdForPayment(validatePayment.getOrderId()); + + User user = userService.findByUser(orderRequest.getUserId()); + + Project project = projectService.findByProject(orderRequest.getProjectId()); + + return CommonResponse.onSuccess(paymentService.checkPayment(mapper.toPaymentValidationCommand(orderRequest, user, project, validatePayment))); } - @PostMapping("/refund") - @Deprecated - public CommonResponse refundPayment(@RequestParam String impUid){ - paymentService.refundPayment(impUid); - return CommonResponse.onSuccess("환불 성공"); + @GetMapping("/info") + @Operation(summary = "08-02 Payment Web 사용자 정보 불러오기", description = "웹에서 결제를 위한 사용자 정보 불러오기 입니다.") + @DisableSecurity + public CommonResponse getPaymentInfo(@RequestParam String orderId){ + OrderRequest orderRequest = orderRequestService.findByOrderId(orderId); + + User user = userService.findByUser(orderRequest.getUserId()); + + Project project = projectService.findByProject(orderRequest.getProjectId()); + + String accessToken = jwtService.createTokenToWeb(user.getId(), 6000L); + + return CommonResponse.onSuccess(mapper.toPaymentInfoDto(user.getName(), user.getBirth(), user.getPhoneNumber(), project.getUsages(), project.getRegularStatus(), accessToken)); } + + /* + @PostMapping("/web-hook") + public CommonResponse webhookAlert(@RequestBody PortOneWebhook portOneWebhook){ + if(profile.equals("prod")) { + OrderRequest orderRequest = orderRequestService.findByOrderIdForPayment(portOneWebhook.getMerchant_uid()); + paymentService.webHookCheck(portOneWebhook, orderRequest); + } + return CommonResponse.onSuccess("인증 성공"); + } + + */ } diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentCommand.java b/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentCommand.java new file mode 100644 index 00000000..968b0a05 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentCommand.java @@ -0,0 +1,25 @@ +package com.example.matchapi.portone.dto; + +import com.example.matchapi.order.dto.OrderReq; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.redis.entity.OrderRequest; +import com.example.matchdomain.user.entity.User; +import lombok.*; + +public class PaymentCommand { + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class PaymentValidation{ + private OrderRequest orderRequest; + + private User user; + + private Project project; + + private PaymentReq.ValidatePayment validatePayment; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java b/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java index b5101d63..2bf9a66f 100644 --- a/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java @@ -8,6 +8,7 @@ public class PaymentReq { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class ValidatePayment{ private String impUid; private String orderId; diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/mapper/PaymentMapper.java b/Match-Api/src/main/java/com/example/matchapi/portone/mapper/PaymentMapper.java new file mode 100644 index 00000000..a32b1efa --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/portone/mapper/PaymentMapper.java @@ -0,0 +1,28 @@ +package com.example.matchapi.portone.mapper; + +import com.example.matchapi.order.dto.OrderRes; +import com.example.matchapi.portone.dto.PaymentCommand; +import com.example.matchapi.portone.dto.PaymentReq; +import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.redis.entity.OrderRequest; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.pay.portone.dto.PortOneWebhook; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.time.LocalDate; + +@Mapper +public interface PaymentMapper { + PaymentMapper INSTANCE = Mappers.getMapper(PaymentMapper.class); + + PaymentCommand.PaymentValidation toPaymentValidationCommand(OrderRequest orderRequest, User user, Project project, PaymentReq.ValidatePayment validatePayment); + + PaymentCommand.PaymentValidation toCheckValidationCommand(OrderRequest orderRequest, User user, Project project, PortOneWebhook portOneWebhook); + + @Mapping(source = "regularStatus", target = "regularStatus") + @Mapping(source = "name", target = "name") + OrderRes.PaymentInfoDto toPaymentInfoDto(String name, LocalDate birth, String phone, String usages, RegularStatus regularStatus, String accessToken); +} diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java b/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java index 22ed564f..c43c8cca 100644 --- a/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java +++ b/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java @@ -1,141 +1,163 @@ package com.example.matchapi.portone.service; +import com.example.matchapi.common.security.JwtService; import com.example.matchapi.donation.service.DonationHistoryService; -import com.example.matchapi.order.convertor.OrderConvertor; +import com.example.matchapi.order.converter.OrderConverter; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.helper.OrderHelper; +import com.example.matchapi.portone.dto.PaymentCommand; import com.example.matchapi.portone.dto.PaymentReq; +import com.example.matchapi.user.service.AligoService; +import com.example.matchcommon.annotation.PaymentIntercept; import com.example.matchcommon.annotation.RedissonLock; import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.BaseException; import com.example.matchcommon.properties.PortOneProperties; -import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.UserCard; -import com.example.matchdomain.donation.repository.DonationUserRepository; import com.example.matchdomain.project.adaptor.ProjectAdaptor; import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.redis.adaptor.OrderAdaptor; import com.example.matchdomain.redis.entity.OrderRequest; -import com.example.matchdomain.redis.repository.OrderRequestRepository; +import com.example.matchdomain.user.adaptor.UserAdaptor; import com.example.matchdomain.user.entity.User; -import com.example.matchdomain.user.repository.UserRepository; +import com.example.matchinfrastructure.aligo.converter.AligoConverter; +import com.example.matchinfrastructure.aligo.dto.AlimType; import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; -import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; +import com.example.matchinfrastructure.pay.portone.converter.PortOneConverter; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneWebhook; import com.example.matchinfrastructure.pay.portone.service.PortOneAuthService; -import com.example.matchinfrastructure.pay.portone.service.PortOneService; import com.siot.IamportRestClient.IamportClient; import com.siot.IamportRestClient.exception.IamportResponseException; -import com.siot.IamportRestClient.request.CancelData; import com.siot.IamportRestClient.response.IamportResponse; import com.siot.IamportRestClient.response.Payment; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.annotation.PostConstruct; import java.io.IOException; -import java.math.BigDecimal; -import java.util.UUID; -import static com.example.matchcommon.constants.MatchStatic.ONE_TIME; +import static com.example.matchcommon.constants.MatchStatic.CANCEL_STATUS; import static com.example.matchdomain.order.exception.PortOneAuthErrorCode.*; -import static com.example.matchdomain.project.exception.ProjectOneTimeErrorCode.PROJECT_NOT_EXIST; -import static com.example.matchdomain.user.exception.UserLoginErrorCode.NOT_EXIST_USER; +import static org.springframework.http.HttpStatus.BAD_REQUEST; @Service @Slf4j @RequiredArgsConstructor public class PaymentService { - private final OrderRequestRepository orderRequestRepository; - private final UserRepository userRepository; + private final OrderAdaptor orderAdaptor; private final OrderHelper orderHelper; - private final DonationUserRepository donationUserRepository; - private final OrderConvertor orderConvertor; - private final IamportClient iamportClient; - private final ProjectAdaptor projectAdaptor; + private final OrderConverter orderConverter; private final DonationHistoryService donationHistoryService; private final PortOneProperties portOneProperties; - private final PortOneConvertor portOneConvertor; + private final PortOneConverter portOneConverter; private final PortOneFeignClient portOneFeignClient; private final PortOneAuthService portOneAuthService; + private final DonationAdaptor donationAdaptor; + private final ProjectAdaptor projectAdaptor; + private final UserAdaptor userAdaptor; + private final AligoService aligoService; + private final AligoConverter aligoConverter; + private final JwtService jwtService; + private IamportClient iamportClient; - @Autowired - public PaymentService(PortOneProperties portOneProperties, - OrderRequestRepository orderRequestRepository, - UserRepository userRepository, - OrderHelper orderHelper, - DonationUserRepository donationUserRepository, - OrderConvertor orderConvertor, ProjectAdaptor projectAdaptor, DonationHistoryService donationHistoryService, PortOneConvertor portOneConvertor, PortOneFeignClient portOneFeignClient, PortOneAuthService portOneAuthService) { - this.portOneProperties = portOneProperties; - this.orderRequestRepository = orderRequestRepository; - this.userRepository = userRepository; - this.orderHelper = orderHelper; - this.donationUserRepository = donationUserRepository; - this.orderConvertor = orderConvertor; + @PostConstruct + private void init() { this.iamportClient = new IamportClient(portOneProperties.getKey(), portOneProperties.getSecret()); - this.projectAdaptor = projectAdaptor; - this.donationHistoryService = donationHistoryService; - this.portOneConvertor = portOneConvertor; - this.portOneFeignClient = portOneFeignClient; - this.portOneAuthService = portOneAuthService; } - @RedissonLock(LockName = "결제-검증", key = "#validatePayment.orderId") - public OrderRes.CompleteDonation checkPayment(PaymentReq.ValidatePayment validatePayment){ + @RedissonLock(LockName = "결제-검증", key = "#paymentValidation.validatePayment.orderId") + public OrderRes.CompleteDonation checkPayment(PaymentCommand.PaymentValidation paymentValidation){ try { - OrderRequest orderRequest = orderRequestRepository.findById(validatePayment.getOrderId()).orElseThrow(()->new BadRequestException(NOT_EXIST_ORDER_ID)); + IamportResponse payment = iamportClient.paymentByImpUid(paymentValidation.getValidatePayment().getImpUid()); - log.info(orderRequest.getOrderId()); + validatePayments(payment, paymentValidation.getValidatePayment(), paymentValidation.getOrderRequest()); - IamportResponse payment = iamportClient.paymentByImpUid(validatePayment.getImpUid()); + DonationUser donationUser = saveDonationUser(paymentValidation.getUser(), paymentValidation.getProject(), payment.getResponse()); - if(payment.getResponse().getAmount().intValue()!=validatePayment.getAmount()) throw new BadRequestException(FAILED_ERROR_AUTH_AMOUNT); + orderAdaptor.deleteById(paymentValidation.getValidatePayment().getOrderId()); - User user = userRepository.findByIdAndStatus(Long.valueOf(orderRequest.getUserId()), Status.ACTIVE).orElseThrow(()->new BadRequestException(NOT_EXIST_USER)); + aligoService.sendAlimTalk(AlimType.PAYMENT, aligoConverter.convertToAlimTalkPayment(donationUser.getId(), paymentValidation.getUser().getName(), paymentValidation.getUser().getPhoneNumber())); - Project project = projectAdaptor.findByProjectId(Long.valueOf(orderRequest.getProjectId())).orElseThrow(()->new BadRequestException(PROJECT_NOT_EXIST)); + return orderConverter.convertToCompleteDonation(paymentValidation.getUser().getName(), paymentValidation.getProject(), (long) paymentValidation.getValidatePayment().getAmount()); + } catch (IamportResponseException | IOException e) { + throw new BadRequestException(FAILED_ERROR_AUTH); + } + } - saveDonationUser(user, validatePayment, project); - orderRequestRepository.deleteById(validatePayment.getOrderId()); + private void validatePayments(IamportResponse payment, PaymentReq.ValidatePayment validatePayment, OrderRequest orderRequest) { + checkCancelPayment(payment.getResponse().getStatus()); - return orderConvertor.convertToCompleteDonation(user.getName(), project, (long) validatePayment.getAmount()); - } catch (BadRequestException | IamportResponseException | IOException e) { - try { - refundPayment(validatePayment.getImpUid()); - } catch(Exception ex) { - System.out.println(ex.getMessage()); - } - throw new BadRequestException(FAILED_ERROR_AUTH); - } + checkExistsImpUid(validatePayment.getImpUid()); + + checkPaymentAmount(payment.getResponse().getAmount().intValue(), orderRequest.getAmount()); + + checkOrderId(payment.getResponse().getMerchantUid(), validatePayment.getOrderId()); } - private CancelData createCancelData(IamportResponse response, int refundAmount) { - if (refundAmount == 0) { //전액 환불일 경우 - return new CancelData(response.getResponse().getImpUid(), true); - } - return new CancelData(response.getResponse().getImpUid(), true, new BigDecimal(refundAmount)); + private void checkOrderId(String merchantUid, String orderId) { + if(!merchantUid.equals(orderId)) throw new BadRequestException(NOT_CORRECT_ORDER_ID); } - public void refundPayment(String impUid) { - try { - iamportClient.cancelPaymentByImpUid(new CancelData(impUid, true)); - } catch (IamportResponseException | IOException e) { - throw new RuntimeException(e); - } + private void checkPaymentAmount(int paymentAmount, int amount) { + if(paymentAmount!=amount) throw new BadRequestException(FAILED_ERROR_AUTH_AMOUNT); + } + + private void checkExistsImpUid(String impUid) { + if(donationAdaptor.existsByImpId(impUid)) throw new BadRequestException(EXIST_IMP_UID); + } + + private void checkCancelPayment(String status) { + if(status.equals(CANCEL_STATUS)) throw new BadRequestException(EXIST_CANCEL_STATUS); } - public void saveDonationUser(User user, PaymentReq.ValidatePayment validatePayment, Project project) { + @PaymentIntercept(key = "#payment.imp_uid") + public DonationUser saveDonationUser(User user, Project project, Payment payment) { OrderRes.CreateInherenceDto createInherenceDto = orderHelper.createInherence(user); - DonationUser donationUser = donationUserRepository.save(orderConvertor.convertToDonationUserPortone(user.getId(), validatePayment, project.getId(), createInherenceDto)); + DonationUser donationUser = donationAdaptor.save(orderConverter.convertToDonationUserPortone(user.getId(), payment, project.getId(), createInherenceDto)); donationHistoryService.oneTimeDonationHistory(donationUser.getId()); + return donationUser; } - public PortOneResponse payBillKey(UserCard card, Long amount, String projectName, String type) { - String orderId = orderHelper.createOrderId(type); + public PortOneResponse payBillKey(UserCard card, Long amount, String projectName, String orderId) { String accessToken = portOneAuthService.getToken(); - return portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.convertPayWithBillKey(card.getBid(), orderId, amount, projectName, card.getCustomerId())); + + PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConverter.convertPayWithBillKey(card.getBid(), orderId, amount, projectName, card.getCustomerId())); + + if (portOneResponse.getCode()!=0){ + if (portOneResponse.getCode() != 0) { + throw new BaseException(BAD_REQUEST, false, "PORT_ONE_BILL_AUTH_001", portOneResponse.getMessage()); + } + } + return portOneResponse; + } + + @RedissonLock(LockName = "결제-검증", key = "#orderRequest.orderId") + public void webHookCheck(PortOneWebhook portOneWebhook, OrderRequest orderRequest) { + try { + IamportResponse payment = iamportClient.paymentByImpUid(portOneWebhook.getImp_uid()); + + checkCancelPayment(payment.getResponse().getStatus()); + checkPaymentAmount(payment.getResponse().getAmount().intValue(), orderRequest.getAmount()); + + System.out.println(payment.getResponse().getPayMethod()); + + if(!donationAdaptor.existsByImpId(portOneWebhook.getImp_uid())){ + User user = userAdaptor.findByUser(orderRequest.getUserId()); + + Project project = projectAdaptor.findByProject(orderRequest.getProjectId()); + + saveDonationUser(user, project, payment.getResponse()); + } + + } catch (IamportResponseException | IOException e) { + throw new BadRequestException(FAILED_ERROR_AUTH); + } } } diff --git a/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java b/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java index d192e573..c87bc70b 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java @@ -72,6 +72,18 @@ public CommonResponse>> searchProjectL return CommonResponse.onSuccess(projectService.searchProjectList(user, content, page, size)); } + @Operation(summary = "03-04💻 프로젝트 댓글 조회",description = "프로젝트 댓글 조회 API 입니다.") + @GetMapping("/comment/{projectId}") + public CommonResponse>> getProjectComment( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @Parameter(description = "페이지", example = "0") @RequestParam(required = true, defaultValue = "0") @Min(value = 0) int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = true, defaultValue = "10") int size, + @Parameter(description = "프로젝트 id") @PathVariable("projectId") Long projectId + ){ + log.info("03-04 프로젝트 댓글 조회 projectId : "+ projectId); + return CommonResponse.onSuccess(projectService.getProjectComment(user, projectId, page, size)); + } + @Operation(summary = "03-05💻 프로젝트 리스트 조회 API #FRAME_프로젝트 리스트 조회.",description = "프로젝트 리스트 조회 API 입니다.") @GetMapping("/list") @ApiErrorCodeExample(UserAuthErrorCode.class) @@ -137,11 +149,10 @@ public CommonResponse>> getMatchHisto @CheckIdExist @PostMapping("/comment/{projectId}") @ApiErrorCodeExample({UserAuthErrorCode.class, ProjectGetErrorCode.class, RequestErrorCode.class}) - public CommonResponse postComment(@Parameter(hidden = true) @AuthenticationPrincipal User user, + public CommonResponse postComment(@Parameter(hidden = true) @AuthenticationPrincipal User user, @Parameter(description = "프로젝트 id") @PathVariable("projectId") Long projectId, @Valid @RequestBody ProjectReq.Comment comment){ - projectService.postComment(user, projectId, comment); - return CommonResponse.onSuccess("응원 달기 성공"); + return CommonResponse.onSuccess(projectService.postComment(user, projectId, comment)); } @Operation(summary = "03-11💻 후원 응원 신고하기 #FRAME_후원 상세조회", description = "후원 응원 신고하기 기능입니다") @@ -160,7 +171,17 @@ public CommonResponse deleteComment( @Parameter(hidden = true) @AuthenticationPrincipal User user, @PathVariable Long commentId){ projectService.deleteComment(user, commentId); - return CommonResponse.onSuccess("신고 성공"); + return CommonResponse.onSuccess("삭제 성공"); } + @Operation(summary = "03-13 내가 찜한 기부처 모아보기 ", description = "내가 찜한 기부처 모아보기") + @GetMapping("/like") + @ApiErrorCodeExample(UserAuthErrorCode.class) + public CommonResponse>> getLikeProjects( + @AuthenticationPrincipal User user, + @Parameter(description = "페이지", example = "0") @RequestParam(required = true, defaultValue = "0") @Min(value = 0) int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = true, defaultValue = "10") int size + ){ + return CommonResponse.onSuccess(projectService.getLikeProjects(user, page, size)); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/project/convertor/ProjectConvertor.java b/Match-Api/src/main/java/com/example/matchapi/project/converter/ProjectConverter.java similarity index 89% rename from Match-Api/src/main/java/com/example/matchapi/project/convertor/ProjectConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/project/converter/ProjectConverter.java index 82bf3234..fa97ea7e 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/convertor/ProjectConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/converter/ProjectConverter.java @@ -1,22 +1,23 @@ -package com.example.matchapi.project.convertor; +package com.example.matchapi.project.converter; -import com.example.matchapi.common.util.TimeHelper; +import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; +import com.example.matchapi.order.helper.OrderHelper; import com.example.matchapi.project.dto.ProjectReq; import com.example.matchapi.project.dto.ProjectRes; import com.example.matchapi.project.helper.ProjectHelper; import com.example.matchapi.user.dto.UserRes; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchdomain.donation.entity.*; import com.example.matchdomain.donation.entity.enums.HistoryStatus; import com.example.matchdomain.donation.entity.enums.RegularPayStatus; import com.example.matchdomain.donation.repository.RegularPaymentRepository; import com.example.matchdomain.project.dto.ProjectDto; -import com.example.matchdomain.project.dto.ProjectList; import com.example.matchdomain.project.entity.*; import com.example.matchdomain.project.entity.enums.ImageRepresentStatus; import com.example.matchdomain.project.entity.enums.ReportReason; import com.example.matchdomain.project.repository.ProjectRepository; +import com.example.matchdomain.user.entity.User; import lombok.RequiredArgsConstructor; import java.time.LocalDateTime; @@ -27,10 +28,11 @@ import static com.example.matchdomain.project.entity.enums.ProjectStatus.BEFORE_START; -@Convertor +@Converter @RequiredArgsConstructor -public class ProjectConvertor { +public class ProjectConverter { private final ProjectHelper projectHelper; + private final DonationHelper donationHelper; private final RegularPaymentRepository regularPaymentRepository; private static final String FIRST_TIME = "T00:00:00"; private static final String LAST_TIME = "T23:59:59"; @@ -325,7 +327,7 @@ public List convertToProjectListWebForNotLogin(List { - String imageUrl = result.getProjectImage().isEmpty() ? null : result.getProjectImage().get(0).getUrl(); + String imageUrl = result.getProjectImage().isEmpty() ? null : result.getProjectImage().get(result.getProjectImage().size()-1).getUrl(); projectLists.add(new ProjectRes.ProjectList( result.getId(), imageUrl, @@ -338,4 +340,36 @@ public List convertToProjectListWebForNotLogin(List convertToTutorialDonation(List projects) { + List tutorials = new ArrayList<>(); + + projects.forEach( + result -> tutorials.add( + convertToTutorialDetail(result) + ) + ); + return tutorials; + } + + private DonationRes.Tutorial convertToTutorialDetail(Project result) { + return DonationRes.Tutorial + .builder() + .projectId(result.getId()) + .projectKind(result.getProjectKind()) + .randomMessage(donationHelper.createRandomMessageTutorial(result.getProjectKind())) + .build(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectReq.java b/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectReq.java index 22f956b6..2ef4d71d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectReq.java @@ -18,6 +18,7 @@ public class ProjectReq { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class Project{ @NotEmpty(message = "프로젝트 이름을 입력해주세요") @Schema(description = "프로젝트 이름", required = true, example = "tbt") @@ -49,6 +50,7 @@ public static class Project{ @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class ModifyProject { @NotEmpty(message = "프로젝트 이름을 입력해주세요") @Schema(description = "프로젝트 이름", required = true, example = "tbt") @@ -80,6 +82,7 @@ public static class ModifyProject { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class Comment { @NotEmpty(message = "프로젝트 이름을 입력해주세요") @Schema(description = "프로젝트 이름", required = true, example = "tbt") diff --git a/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectRes.java b/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectRes.java index 677c4f60..6ad2cd24 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectRes.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/dto/ProjectRes.java @@ -1,9 +1,11 @@ package com.example.matchapi.project.dto; import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; +import java.time.LocalDateTime; import java.util.List; public class ProjectRes { @@ -12,6 +14,7 @@ public class ProjectRes { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class ProjectList { @Schema(description = "projectId 값", required = true, example = "1") private Long projectId; @@ -94,12 +97,12 @@ public static class ProjectImgList{ @AllArgsConstructor @NoArgsConstructor public static class CommentList { - private Long commentId; private String comment; - private String commentDate; + @JsonFormat(pattern = "yyyy.MM.dd HH.mm") + private LocalDateTime commentDate; @Schema(description = "이미지 URL", required = true, example = "imgUrl") private String profileImgUrl; diff --git a/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java b/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java index bf5478ba..8ae0316d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java @@ -1,6 +1,8 @@ package com.example.matchapi.project.service; -import com.example.matchapi.project.convertor.ProjectConvertor; +import com.example.matchapi.common.util.MessageHelper; +import com.example.matchapi.donation.dto.DonationRes; +import com.example.matchapi.project.converter.ProjectConverter; import com.example.matchapi.project.dto.ProjectReq; import com.example.matchapi.project.dto.ProjectRes; import com.example.matchcommon.constants.enums.FILTER; @@ -10,11 +12,10 @@ import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.common.model.Status; import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.adaptor.DonationHistoryAdaptor; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.HistoryStatus; import com.example.matchdomain.donation.entity.enums.RegularStatus; -import com.example.matchdomain.donation.repository.DonationHistoryRepository; -import com.example.matchdomain.donation.repository.DonationUserRepository; import com.example.matchdomain.project.adaptor.ProjectAdaptor; import com.example.matchdomain.project.adaptor.ProjectImgAdaptor; import com.example.matchdomain.project.dto.ProjectDto; @@ -26,6 +27,7 @@ import com.example.matchdomain.project.repository.*; import com.example.matchdomain.user.entity.User; import com.example.matchinfrastructure.config.s3.S3UploadService; +import com.example.matchcommon.constants.enums.Topic; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -38,6 +40,7 @@ import java.util.ArrayList; import java.util.List; +import static com.example.matchcommon.constants.MatchAlertStatic.PROJECT_UPLOAD_BODY; import static com.example.matchdomain.common.model.Status.ACTIVE; import static com.example.matchdomain.common.model.Status.INACTIVE; import static com.example.matchdomain.project.entity.enums.ImageRepresentStatus.NORMAL; @@ -54,16 +57,17 @@ public class ProjectService { private final ProjectAdaptor projectAdaptor; private final ProjectRepository projectRepository; - private final ProjectConvertor projectConvertor; + private final ProjectConverter projectConverter; private final ProjectImageRepository projectImageRepository; private final AuthHelper authHelper; private final ProjectCommentRepository projectCommentRepository; private final S3UploadService s3UploadService; private final ProjectUserAttentionRepository projectUserAttentionRepository; - private final DonationHistoryRepository donationHistoryRepository; + private final DonationHistoryAdaptor donationHistoryAdaptor; private final CommentReportRepository commentReportRepository; private final ProjectImgAdaptor projectImgAdaptor; private final DonationAdaptor donationAdaptor; + private final MessageHelper messageHelper; public PageResponse> getProjectList(User user, int page, int size) { Long userId = 0L; @@ -73,17 +77,17 @@ public PageResponse> getProjectList(User user, int if(!userId.equals(0L)){ Page projects = projectAdaptor.findLoginUserProjectList(userId, page, size); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWeb(projects.getContent())); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConverter.convertToProjectListWeb(projects.getContent())); } else{ Page projects = projectAdaptor.findNotLoginUserProjectList(page, size); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWebForNotLogin(projects.getContent())); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConverter.convertToProjectListWebForNotLogin(projects.getContent())); } } public ProjectRes.ProjectDetail getProjectDetail(User user, Long projectId) { - return projectConvertor.projectImgList(projectImgAdaptor.getProjectDetail(projectId)); + return projectConverter.projectImgList(projectImgAdaptor.getProjectDetail(projectId)); } public PageResponse> searchProjectList(User user, String content, int page, int size) { @@ -94,18 +98,18 @@ public PageResponse> searchProjectList(User user, S if(!userId.equals(0L)){ Page projects = projectAdaptor.findLoginUserSearchProjectList(userId, page, size, content); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWeb(projects.getContent())); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConverter.convertToProjectListWeb(projects.getContent())); } else{ Page projects = projectAdaptor.findNotLoginUserSearchProjectList(content, page, size); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWebForNotLogin(projects.getContent())); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConverter.convertToProjectListWebForNotLogin(projects.getContent())); } } @Transactional public void postProject(ProjectReq.Project projects, MultipartFile presentFile, List multipartFiles) { - Project project = projectRepository.save(projectConvertor.postProject(projects)); + Project project = projectRepository.save(projectConverter.postProject(projects)); String url = s3UploadService.uploadProjectPresentFile(project.getId() ,presentFile); @@ -113,7 +117,10 @@ public void postProject(ProjectReq.Project projects, MultipartFile presentFile, saveImgList(project.getId(), url, imgUrlList); - donationHistoryRepository.save(projectConvertor.convertToDonationHistory(project.getId(), HistoryStatus.TURN_ON)); + donationHistoryAdaptor.saveDonationHistory(projectConverter.convertToDonationHistory(project.getId(), HistoryStatus.START)); + + messageHelper.helpFcmMessage(PROJECT_UPLOAD_BODY, Topic.PROJECT_UPLOAD, project.getId()); + } public PageResponse> getProjectList(int page, int size) { @@ -125,7 +132,7 @@ public PageResponse> getProjectList(int page, projectAdminLists.getContent().forEach( result -> projectLists.add( - projectConvertor.convertToProjectList(result) + projectConverter.convertToProjectList(result) ) ); @@ -138,7 +145,7 @@ public void patchProjectStatus(ProjectStatus projectStatus, Long projectId) { project.setProjectStatus(projectStatus); - if(projectStatus.equals(ProjectStatus.DEADLINE)) donationHistoryRepository.save(projectConvertor.convertToDonationHistory(projectId, HistoryStatus.FINISH)); + if(projectStatus.equals(ProjectStatus.DEADLINE)) donationHistoryAdaptor.saveDonationHistory(projectConverter.convertToDonationHistory(projectId, HistoryStatus.FINISH)); projectRepository.save(project); } @@ -167,9 +174,9 @@ public void saveImgList(Long id, String url, List imgUrlList) { for (int i=1 ; i <= imgUrlList.size(); i++) { if(i==imgUrlList.size()){ - projectImages.add(projectConvertor.postProjectImage(id,imgUrlList.get(i-1),REPRESENT,i)); + projectImages.add(projectConverter.postProjectImage(id,imgUrlList.get(i-1),REPRESENT,i)); }else { - projectImages.add(projectConvertor.postProjectImage(id, imgUrlList.get(i-1),NORMAL, i)); + projectImages.add(projectConverter.postProjectImage(id, imgUrlList.get(i-1),NORMAL, i)); } } @@ -184,13 +191,13 @@ public ProjectRes.ProjectAdminDetail getProjectAdminDetail(Long projectId) { List projectImages = projectImgAdaptor.findProjectImages(projectId); - return projectConvertor.convertToProjectAdminDetail(projectAdminDetail,projectImages); + return projectConverter.convertToProjectAdminDetail(projectAdminDetail,projectImages); } public PageResponse> getDonationList(Long projectId, int page, int size) { Page donationUsers = donationAdaptor.findDonationUsers(projectId, page, size); - return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), projectConvertor.convertToDonationUserInfo(donationUsers.getContent())); + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), projectConverter.convertToDonationUserInfo(donationUsers.getContent())); } @Transactional @@ -221,7 +228,7 @@ public void patchProjectActive(Long projectId) { public PageResponse> getProjectLists(User user, int page, int size, ProjectKind projectKind, String content, FILTER filter) { Page projects = projectAdaptor.findProject(user, page, size, projectKind, content, filter); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectLists(projects.getContent())); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConverter.convertToProjectLists(projects.getContent())); } @Transactional @@ -239,14 +246,14 @@ public ProjectRes.ProjectLike patchProjectLike(User user, Long projectId) { public PageResponse> getTodayProjectLists(User user, int page, int size) { Page projects = projectAdaptor.getTodayProjectLists(user.getId(), page, size); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectLists(projects.getContent())); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConverter.convertToProjectLists(projects.getContent())); } public ProjectRes.ProjectAppDetail getProjectAppDetail(User user, Long projectId) { ProjectRepository.ProjectDetail projects = projectRepository.getProjectAppDetail(user.getId(), projectId); List projectImages = projectImageRepository.findByProjectIdOrderBySequenceAsc(projectId); - return projectConvertor.convertToProjectAppDetail(projects, projectImages); + return projectConverter.convertToProjectAppDetail(projects, projectImages); } public PageResponse> projectList(User user, int page, int size, ProjectKind projectKind, String content) { @@ -259,7 +266,7 @@ public PageResponse> projectList(User user, int pa projects.getContent().forEach( result -> { - project.add(projectConvertor.convertToProjectToDto(result)); + project.add(projectConverter.convertToProjectToDto(result)); } ); @@ -267,14 +274,16 @@ public PageResponse> projectList(User user, int pa } - public void postComment(User user, Long projectId, ProjectReq.Comment comment) { - projectCommentRepository.save(projectConvertor.convertToComment(user.getId(), projectId, comment.getComment())); + public ProjectRes.CommentList postComment(User user, Long projectId, ProjectReq.Comment comment) { + ProjectComment projectComment = projectCommentRepository.save(projectConverter.convertToComment(user.getId(), projectId, comment.getComment())); + + return projectConverter.projectComment(user, projectComment); } public void reportComment(Long commentId, ReportReason reportReason) { ProjectComment projectComment = projectCommentRepository.findByIdAndStatus(commentId, ACTIVE).orElseThrow(()-> new NotFoundException(COMMENT_NOT_EXIST)); - commentReportRepository.save(projectConvertor.convertToReportComment(commentId, reportReason)); + commentReportRepository.save(projectConverter.convertToReportComment(commentId, reportReason)); } public void deleteComment(User user, Long commentId) { @@ -287,4 +296,41 @@ public void deleteComment(User user, Long commentId) { public Project checkProjectExists(Long projectId, RegularStatus regularStatus) { return projectAdaptor.checkRegularProjects(projectId, regularStatus); } + + public PageResponse> getProjectComment(User user, Long projectId, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + + Page projectComments = projectCommentRepository.findByProjectIdAndStatusOrderByCreatedAtAsc(projectId, ACTIVE,pageable); + + List commentLists = new ArrayList<>(); + projectComments.getContent().forEach( + result-> { + commentLists.add( + projectConverter.projectComment(user, result) + ); + } + ); + + + return new PageResponse<>(projectComments.isLast(), projectComments.getTotalElements(), commentLists); + } + + public PageResponse> getLikeProjects(User user, int page, int size) { + Page projects = projectAdaptor.findLikeProjects(user, page, size); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConverter.convertToProjectLists(projects.getContent())); + } + + public Project findByProject(String projectId) { + return projectAdaptor.findByProject(projectId); + } + + public Project findByProjectId(Long projectId) { + return projectAdaptor.findById(projectId); + } + + public List getTutorialDonation() { + List projects = projectAdaptor.getRandom3Project(); + return projectConverter.convertToTutorialDonation(projects); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/review/controller/ReviewController.java b/Match-Api/src/main/java/com/example/matchapi/review/controller/ReviewController.java new file mode 100644 index 00000000..49217acf --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/review/controller/ReviewController.java @@ -0,0 +1,43 @@ +package com.example.matchapi.review.controller; + +import com.example.matchapi.review.dto.ReviewReq; +import com.example.matchapi.review.dto.ReviewRes; +import com.example.matchapi.review.service.ReviewService; +import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.exception.errorcode.RequestErrorCode; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchdomain.donation.exception.CheckExecutionCode; +import com.example.matchdomain.user.entity.User; +import com.example.matchdomain.user.exception.UserAuthErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/reviews") +@RequiredArgsConstructor +@Tag(name = "13-Review 관련 API") +public class ReviewController { + private final ReviewService reviewService; + @ApiErrorCodeExample({UserAuthErrorCode.class, CheckExecutionCode.class}) + @GetMapping("") + @Operation(summary = "13-01 Review 작성 유무 팝업 창 띄우기 유무",description = "매 번 스플레시 단 or 홈화면에서 호출 하여 팝업창 띄우기") + public CommonResponse checkPopUp(@AuthenticationPrincipal User user){ + ReviewRes.PopUpInfo popUpInfo = reviewService.checkPopUp(user); + + if(popUpInfo == null){ + return CommonResponse.onSuccessesFail(null); + } + return CommonResponse.onSuccess(popUpInfo); + } + + @ApiErrorCodeExample({UserAuthErrorCode.class, RequestErrorCode.class}) + @PostMapping("") + @Operation(summary = "13-02 Review 작성 POST", description = "리뷰작성") + public CommonResponse postReview(@AuthenticationPrincipal User user, @RequestBody ReviewReq.ReviewUpload reviewUpload){ + reviewService.postReview(user, reviewUpload); + return CommonResponse.onSuccess("성공"); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/review/converter/ReviewConverter.java b/Match-Api/src/main/java/com/example/matchapi/review/converter/ReviewConverter.java new file mode 100644 index 00000000..a3552de2 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/review/converter/ReviewConverter.java @@ -0,0 +1,32 @@ +package com.example.matchapi.review.converter; + +import com.example.matchapi.review.dto.ReviewReq; +import com.example.matchapi.review.dto.ReviewRes; +import com.example.matchcommon.annotation.Converter; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.review.entity.Review; + +@Converter +public class ReviewConverter { + public ReviewRes.PopUpInfo convertToPopUp(DonationUser donationUser, Long donationId) { + Project project = donationUser.getProject(); + + return ReviewRes.PopUpInfo + .builder() + .executionId(donationId) + .regularStatus(project.getRegularStatus().getName()) + .title(project.getProjectName()) + .build(); + } + + public Review convertToReview(ReviewReq.ReviewUpload reviewUpload) { + return Review.builder() + .comment(reviewUpload.getComment()) + .donationId(reviewUpload.getExecutionId()) + .information(reviewUpload.getInformation()) + .donation(reviewUpload.getDonation()) + .transparency(reviewUpload.getTransparency()) + .build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/review/dto/ReviewReq.java b/Match-Api/src/main/java/com/example/matchapi/review/dto/ReviewReq.java new file mode 100644 index 00000000..645967cb --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/review/dto/ReviewReq.java @@ -0,0 +1,37 @@ +package com.example.matchapi.review.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotNull; + +public class ReviewReq { + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class ReviewUpload{ + @NotNull(message = "id 값을 입력해주세요") + @Schema(description = "executionId 값") + private Long executionId; + + @Schema(description = "후원 경험") + @NotNull(message = "후원 경험을 입력해주세요") + private int donation; + + @Schema(description = "투명성") + @NotNull(message = "투명성을 입력해주세요") + private int transparency; + + @Schema(description = "정보 제공") + @NotNull(message = "정보 제공을 입력해주세요") + private int information; + + @Schema(description = "함께 한 후원 경험") + private String comment; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/review/dto/ReviewRes.java b/Match-Api/src/main/java/com/example/matchapi/review/dto/ReviewRes.java new file mode 100644 index 00000000..2c06540b --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/review/dto/ReviewRes.java @@ -0,0 +1,20 @@ +package com.example.matchapi.review.dto; + +import com.example.matchdomain.donation.entity.enums.RegularStatus; +import lombok.*; + +public class ReviewRes { + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class PopUpInfo { + private Long executionId; + + private String title; + + private String regularStatus; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/review/service/ReviewService.java b/Match-Api/src/main/java/com/example/matchapi/review/service/ReviewService.java new file mode 100644 index 00000000..693e0c94 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/review/service/ReviewService.java @@ -0,0 +1,42 @@ +package com.example.matchapi.review.service; + +import com.example.matchapi.review.converter.ReviewConverter; +import com.example.matchapi.review.dto.ReviewReq; +import com.example.matchapi.review.dto.ReviewRes; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.review.adaptor.ReviewAdaptor; +import com.example.matchdomain.review.entity.Review; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + + +@Service +@RequiredArgsConstructor +public class ReviewService { + private final ReviewAdaptor reviewAdaptor; + private final ReviewConverter reviewConverter; + private final DonationAdaptor donationAdaptor; + + public ReviewRes.PopUpInfo checkPopUp(User user) { + List donationUsers = donationAdaptor.checkPopUp(user); + + if(donationUsers.isEmpty()) return null; + + DonationUser donationUser = donationUsers.get(0); + + Optional review = reviewAdaptor.findByDonationUser(donationUser); + + if(review.isEmpty()) return null; + + return reviewConverter.convertToPopUp(donationUser, donationUser.getId()); + } + + public void postReview(User user, ReviewReq.ReviewUpload reviewUpload) { + reviewAdaptor.save(reviewConverter.convertToReview(reviewUpload)); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java b/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java index a2546a5b..26149096 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java @@ -5,13 +5,11 @@ import com.example.matchapi.user.service.AuthService; import com.example.matchapi.user.helper.SmsHelper; import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.annotation.DisableSecurity; import com.example.matchcommon.exception.errorcode.MailSendErrorCode; import com.example.matchcommon.exception.errorcode.OtherServerErrorCode; import com.example.matchcommon.exception.errorcode.RequestErrorCode; -import com.example.matchdomain.user.exception.CodeAuthErrorCode; -import com.example.matchdomain.user.exception.UserLoginErrorCode; -import com.example.matchdomain.user.exception.UserNormalSignUpErrorCode; -import com.example.matchdomain.user.exception.UserSignUpErrorCode; +import com.example.matchdomain.user.exception.*; import com.example.matchcommon.reponse.CommonResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -32,6 +30,7 @@ public class AuthController { private final SmsHelper smsHelper; @Operation(summary = "kakao 코드 발급 후 토큰 생성용 개발용 API 입니다",description = "kakao 코드를 발급 할 수 있음") @GetMapping(value = "/kakao") + @DisableSecurity public String kakaoOauthRedirect(@RequestParam String code) { log.info("카카오 로그인 액세스 토큰 발급"); return "카카오 로그인 액세스 토큰 발급 완료, 액세스 토큰 :" + authService.getOauthToken(code,"").getAccess_token(); @@ -41,6 +40,7 @@ public String kakaoOauthRedirect(@RequestParam String code) { @ApiErrorCodeExample({UserSignUpErrorCode.class, OtherServerErrorCode.class}) @Operation(summary= "01-02🔑 카카오 로그인" , description = "카카오 액세스 토큰 보내주기") @PostMapping(value="/kakao") + @DisableSecurity public CommonResponse kakaoLogIn(@RequestBody @Valid UserReq.SocialLoginToken socialLoginToken){ log.info("01-02 카카오 로그인"); return CommonResponse.onSuccess(authService.kakaoLogIn(socialLoginToken)); @@ -53,6 +53,7 @@ public CommonResponse kakaoLogIn(@RequestBody @Valid UserReq. @ApiErrorCodeExample({UserSignUpErrorCode.class, OtherServerErrorCode.class}) @GetMapping(value="/naver") @Operation(summary = "01-03-01🔑 web version API naver 코드 발급 후 회원가입", description = "naver 코드를 발급 할 수 있음") + @DisableSecurity public CommonResponse naverOauthRedirect(@RequestParam String code){ log.info("01-03-01 웹 버전 naver 로그인,회원가입"); return CommonResponse.onSuccess(authService.getNaverOauthToken(code)); @@ -61,9 +62,10 @@ public CommonResponse naverOauthRedirect(@RequestParam Strin @ApiErrorCodeExample({UserSignUpErrorCode.class, OtherServerErrorCode.class, RequestErrorCode.class}) @Operation(summary= "01-03🔑 네이버 로그인" , description = "네이버 액세스 토큰 보내주기") @PostMapping(value="/naver") + @DisableSecurity public CommonResponse naverLogIn(@RequestBody @Valid UserReq.SocialLoginToken socialLoginToken){ log.info("01-03 네이버 로그인,회원가입 API"); - return CommonResponse.onSuccess(authService.naverLogIn(socialLoginToken)); + return CommonResponse.onSuccess(authService.naverLogIn(socialLoginToken.getAccessToken())); } /* @@ -85,6 +87,7 @@ CommonResponse getNaverAddress(@RequestBody UserReq.SocialLogin @Operation(summary= "01-04🔑 회원 문자인증 요청", description = "회원 문자인증 용 API 입니다.") @PostMapping(value="/sms") @Deprecated + @DisableSecurity public CommonResponse checkSms(@RequestBody @Valid UserReq.Sms sms){ log.info("01-04 비회원 문자인증 = " +sms.getPhone()); String number = smsHelper.sendSms(sms.getPhone()); @@ -94,6 +97,7 @@ public CommonResponse checkSms(@RequestBody @Valid UserReq.Sms sms) @ApiErrorCodeExample({UserNormalSignUpErrorCode.class, UserSignUpErrorCode.class, RequestErrorCode.class}) @Operation(summary="01-05🔑 유저 회원가입", description= "회원가입 용 API 입니다.") @PostMapping(value="/user") + @DisableSecurity public CommonResponse signUpUser(@RequestBody @Valid UserReq.SignUpUser signUpUser){ log.info("01-05 유저 회원가입 API"); return CommonResponse.onSuccess(authService.signUpUser(signUpUser)); @@ -102,6 +106,7 @@ public CommonResponse signUpUser(@RequestBody @Valid UserReq. @ApiErrorCodeExample(RequestErrorCode.class) @Operation(summary="01-05-01🔑 유저 회원가입 이메일 검증용", description= "회원가입 용 API 입니다.") @PostMapping(value="/email") + @DisableSecurity public CommonResponse checkUserEmail(@RequestBody @Valid UserReq.UserEmail userEmail){ log.info("01-05-01 유저 회원가입 이메일 검증"+userEmail.getEmail()); authService.checkUserEmail(userEmail); @@ -111,6 +116,7 @@ public CommonResponse checkUserEmail(@RequestBody @Valid UserReq.UserEma @ApiErrorCodeExample(RequestErrorCode.class) @Operation(summary="01-05-02🔑 유저 회원가입 전화번호 인증용", description= "회원가입 용 API 입니다.") @PostMapping(value="/phone") + @DisableSecurity public CommonResponse checkUserPhone(@RequestBody @Valid UserReq.UserPhone userPhone){ log.info("01-05-01 유저 회원가입 전화번호 검증"+userPhone.getPhone()); authService.checkUserPhone(userPhone); @@ -120,18 +126,17 @@ public CommonResponse checkUserPhone(@RequestBody @Valid UserReq.UserPho @ApiErrorCodeExample({UserLoginErrorCode.class, RequestErrorCode.class}) @Operation(summary="01-06🔑 유저 로그인", description= "회원가입 용 API 입니다.") @PostMapping(value="/logIn") + @DisableSecurity public CommonResponse logIn(@RequestBody @Valid UserReq.LogIn logIn){ log.info("01-06 유저 로그인 회원가입 API "+logIn.getEmail()); return CommonResponse.onSuccess(authService.logIn(logIn)); } - - - @Operation(summary="01-07🔑 유저 이메일 인증번호 보내기", description= "이메일 인증번호 보내기 API 입니다.") @ApiErrorCodeExample({MailSendErrorCode.class, UserNormalSignUpErrorCode.class}) @GetMapping("/email") + @DisableSecurity public CommonResponse emailAuth(@RequestParam String email){ authService.sendEmailMessage(email); return CommonResponse.onSuccess("메일 전송 성공"); @@ -140,6 +145,7 @@ public CommonResponse emailAuth(@RequestParam String email){ @Operation(summary="01-08🔑 유저 이메일 인증번호 확인 API", description= "이메일 인증번호 확인 API 입니다.") @PostMapping("/check/email") @ApiErrorCodeExample(CodeAuthErrorCode.class) + @DisableSecurity public CommonResponse checkEmailAuth(@RequestBody UserReq.UserEmailAuth email){ authService.checkUserEmailAuth(email); return CommonResponse.onSuccess("메일 인증 성공"); @@ -149,6 +155,7 @@ public CommonResponse checkEmailAuth(@RequestBody UserReq.UserEmailAuth @ApiErrorCodeExample(RequestErrorCode.class) @Operation(summary= "01-09🔑 회원 문자인증 요청", description = "회원 문자인증 용 API 입니다.") @GetMapping(value="/phone") + @DisableSecurity public CommonResponse checkPhone(@RequestParam String phone){ authService.sendPhone(phone); return CommonResponse.onSuccess("문자 전송 성공"); @@ -157,16 +164,47 @@ public CommonResponse checkPhone(@RequestParam String phone){ @Operation(summary="01-10🔑 유저 전화번호 인증번호 확인 API", description= "전화번호 인증번호 확인 API 입니다.") @PostMapping("/check/phone") @ApiErrorCodeExample(CodeAuthErrorCode.class) + @DisableSecurity public CommonResponse checkEmailAuth(@RequestBody UserReq.UserPhoneAuth phone){ authService.checkPhoneAuth(phone); return CommonResponse.onSuccess("핸드폰 인증 성공"); } - @Operation(summary="01-11🔑 애플로그인 API", description= "애플로그인 API 입니다.") + + @Operation(summary="01-11🔑 애플로그인 API", description= "애플로그인 API 입니다. APPLE_SIGN_UP 에러 코드 발생 시 01-11-01 API 로 회원가입 요청") @PostMapping("/apple") - @ApiErrorCodeExample({UserSignUpErrorCode.class, OtherServerErrorCode.class, RequestErrorCode.class}) + @ApiErrorCodeExample({UserSignUpErrorCode.class, OtherServerErrorCode.class, RequestErrorCode.class, AppleLoginErrorCode.class}) + @DisableSecurity public CommonResponse appleLogin(@RequestBody @Valid UserReq.SocialLoginToken socialLoginToken){ return CommonResponse.onSuccess(authService.appleLogin(socialLoginToken)); } + @Operation(summary = "01-11-01 애플 회원가입🔑",description = "애플유저용 회원가입") + @PostMapping("/apple/sign-up") + @ApiErrorCodeExample({UserSignUpErrorCode.class, RequestErrorCode.class}) + @DisableSecurity + public CommonResponse appleSignUp(@RequestBody @Valid UserReq.AppleSignUp appleSignUp){ + return CommonResponse.onSuccess(authService.appleSignUp(appleSignUp)); + } + + @Operation(summary = "01-14🔑 비밀번호 찾기용 이메일 전송 이메일 전송 시 01-08 API 로 인증번호 확인 입니다.", description = "만료시간 5분") + @PostMapping("/password/email") + @ApiErrorCodeExample({UserSignUpErrorCode.class, SendEmailFindPassword.class}) + @DisableSecurity + public CommonResponse sendEmailPasswordFind(@RequestParam String email){ + authService.sendEmailPasswordFind(email); + return CommonResponse.onSuccess("메일 인증 성공"); + } + + + @Operation(summary = "01-13🔑 비밀번호 찾기", description = "여기서 또 한번 인증 코드를 받는 이유는 이중 인증을 위함 입니다. 변경은 5분안에 마무리 되야합니다.") + @PostMapping("/password") + @ApiErrorCodeExample({UserSignUpErrorCode.class, RequestErrorCode.class, CodeAuthErrorCode.class}) + @DisableSecurity + public CommonResponse modifyPassword(@RequestBody @Valid UserReq.FindPassword findPassword){ + authService.modifyPassword(findPassword); + return CommonResponse.onSuccess("비밀번호 변경 성공"); + } + + } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java b/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java index 51dd9cf8..d06caf64 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java @@ -6,13 +6,13 @@ import com.example.matchapi.user.dto.UserReq; import com.example.matchapi.user.service.UserService; import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.annotation.DisableSecurity; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.exception.errorcode.RequestErrorCode; import com.example.matchdomain.redis.entity.RefreshToken; import com.example.matchdomain.redis.repository.RefreshTokenRepository; -import com.example.matchdomain.user.exception.ModifyEmailCode; -import com.example.matchdomain.user.exception.ModifyPhoneErrorCode; -import com.example.matchdomain.user.exception.UserAuthErrorCode; +import com.example.matchdomain.user.entity.enums.SocialType; +import com.example.matchdomain.user.exception.*; import com.example.matchcommon.reponse.CommonResponse; import com.example.matchdomain.user.entity.User; import io.swagger.v3.oas.annotations.Operation; @@ -22,13 +22,17 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import javax.transaction.Transactional; +import javax.validation.Valid; import java.io.IOException; +import static com.example.matchdomain.user.exception.DeleteUserErrorCode.APPLE_USER_NOT_API; import static com.example.matchdomain.user.exception.UserAuthErrorCode.INVALID_REFRESH_TOKEN; @RestController @@ -74,11 +78,12 @@ public CommonResponse editMyPage(@Parameter(hidden = true) @Operation(summary = "02-04 로그아웃 👤", description = "로그아웃 요청 API") - @ResponseBody @GetMapping("/logout") + @Transactional public CommonResponse logOut(@Parameter(hidden = true) @AuthenticationPrincipal User user, - @Parameter(description = "디바이스 아이디", required = true, in = ParameterIn.HEADER, name = "DEVICE_ID", schema = @Schema(type = "string")) @RequestHeader("DEVICE_ID") String deviceId){ + @Parameter(description = "디바이스 아이디") @RequestParam(value = "DEVICE_ID", required = true) String deviceId){ log.info("api = logout 02-03"); + Long userId = user.getId(); jwtService.logOut(userId); @@ -87,8 +92,8 @@ public CommonResponse logOut(@Parameter(hidden = true) @AuthenticationPr } @Operation(summary = "02-05 토큰 재발급 👤", description = "액세스 토큰 만료시 재발급 요청 하는 API X-REFRESH-TOKEN 을 헤더에 담아서 보내주세요, accessToken 은 보내지 않습니다.") - @ResponseBody @PostMapping("/refresh") + @DisableSecurity public CommonResponse reIssueToken( @Parameter(description = "리프레쉬 토큰", required = true, in = ParameterIn.HEADER, name = "X-REFRESH-TOKEN", schema = @Schema(type = "string")) @RequestHeader("X-REFRESH-TOKEN") String refreshToken ){ @@ -99,9 +104,7 @@ public CommonResponse reIssueToken( if(!redisRefreshToken.getToken().equals(refreshToken)) throw new BadRequestException(INVALID_REFRESH_TOKEN); - UserRes.ReIssueToken tokenRes=new UserRes.ReIssueToken(jwtService.createToken(userId)); - - return CommonResponse.onSuccess(tokenRes); + return CommonResponse.onSuccess(new UserRes.ReIssueToken(jwtService.createToken(userId))); } @@ -182,6 +185,36 @@ public CommonResponse patchAlarmAgree(@AuthenticationPri @RequestParam AlarmType alarmType){ return CommonResponse.onSuccess(userService.patchAlarm(user, alarmType)); } + @Operation(summary = "02-11 애플유저 결제화면 추가 정보 POST 👤" , description = "애플 유저 결제 화면 추가정보 POST") + @PostMapping("/apple") + @ApiErrorCodeExample({UserAuthErrorCode.class, CheckUserPhoneErrorCode.class}) + public CommonResponse postAppleUserInfo(@AuthenticationPrincipal User user, + @Valid @RequestBody UserReq.AppleUserInfo appleUserInfo){ + log.info("02-11 애플 유저 결제화면 추가 정보 POST API"); + userService.postAppleUserInfo(user, appleUserInfo); + return CommonResponse.onSuccess("성공"); + } + @Operation(summary = "02-12 유저 탈퇴 API") + @DeleteMapping("") + @ApiErrorCodeExample({UserAuthErrorCode.class, DeleteUserErrorCode.class}) + public CommonResponse deleteUserInfo(@AuthenticationPrincipal User user){ + log.info("02-12 유저 탈퇴 API userId : " + user.getId()); + if(user.getSocialType().equals(SocialType.APPLE)){ + throw new BadRequestException(APPLE_USER_NOT_API); + } + userService.deleteUserInfo(user); + return CommonResponse.onSuccess("탈퇴 성공"); + } + + @Operation(summary = "02-13 애플 유저 탈퇴 API") + @DeleteMapping("/apple") + @ApiErrorCodeExample({UserAuthErrorCode.class}) + public CommonResponse deleteAppleUserInfo(@AuthenticationPrincipal User user, + @Valid @RequestBody UserReq.AppleCode appleCode){ + log.info("02-13 애플 유저 탈퇴 code : " + appleCode.getCode()); + userService.deleteAppleUserInfo(user, appleCode); + return CommonResponse.onSuccess("탈퇴 성공"); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/convertor/UserConvertor.java b/Match-Api/src/main/java/com/example/matchapi/user/converter/UserConverter.java similarity index 82% rename from Match-Api/src/main/java/com/example/matchapi/user/convertor/UserConvertor.java rename to Match-Api/src/main/java/com/example/matchapi/user/converter/UserConverter.java index f924e780..8582cd73 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/convertor/UserConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/converter/UserConverter.java @@ -1,35 +1,41 @@ -package com.example.matchapi.user.convertor; +package com.example.matchapi.user.converter; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.user.dto.UserReq; import com.example.matchapi.user.dto.UserRes; import com.example.matchapi.user.helper.AuthHelper; import com.example.matchapi.user.helper.UserHelper; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchcommon.properties.AligoProperties; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.redis.entity.RefreshToken; import com.example.matchdomain.user.entity.*; import com.example.matchdomain.user.entity.enums.AddressType; import com.example.matchdomain.user.entity.enums.AuthorityEnum; +import com.example.matchdomain.user.entity.enums.Gender; import com.example.matchdomain.user.entity.enums.SocialType; import com.example.matchdomain.user.entity.pk.UserFcmPk; import com.example.matchdomain.user.repository.UserRepository; import com.example.matchinfrastructure.aligo.dto.SendReq; -import com.example.matchinfrastructure.oauth.apple.dto.AppleUserRes; import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserAddressDto; import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserInfoDto; import com.example.matchinfrastructure.oauth.naver.dto.NaverUserInfoDto; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.security.crypto.password.PasswordEncoder; +import javax.validation.Valid; +import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import static com.example.matchcommon.constants.MatchStatic.BASE_PROFILE; import static com.example.matchdomain.user.entity.enums.Alarm.ACTIVE; -@Convertor +@Converter @RequiredArgsConstructor -public class UserConvertor { +public class UserConverter { private final AuthHelper authHelper; private final UserHelper userHelper; private final PasswordEncoder passwordEncoder; @@ -142,7 +148,7 @@ public RefreshToken convertToRefreshToken(Long userId, String refreshToken, Long public OrderRes.UserDetail convertToUserInfo(User user) { return OrderRes.UserDetail.builder() .name(user.getName()) - .birthDay(user.getBirth().toString()) + .birthDay(user.getBirth()) .phoneNumber(user.getPhoneNumber()).build(); } @@ -226,19 +232,22 @@ public UserFcmToken convertToUserFcm(User user, UserReq.FcmToken token) { .build(); } - public User convertToAppleUserSignUp(AppleUserRes appleUserRes) { + public User convertToAppleUserSignUp(UserReq.@Valid AppleSignUp appleSignUp) { return User.builder() - .username(appleUserRes.getSocialId()) + .username(appleSignUp.getSocialId()) + .name(appleSignUp.getName()) .password(authHelper.createRandomPassword()) .profileImgUrl(BASE_PROFILE) - .name(userHelper.createRandomNickName()) - .email(appleUserRes.getEmail()) - .socialId(appleUserRes.getSocialId()) + .email(appleSignUp.getEmail()) + .phoneNumber(appleSignUp.getPhone()) + .socialId(appleSignUp.getSocialId()) .socialType(SocialType.APPLE) .role(AuthorityEnum.ROLE_USER.getValue()) .nickname(userHelper.createRandomNickName()) .serviceAlarm(ACTIVE) .eventAlarm(ACTIVE) + .gender(appleSignUp.getGender()) + .birth(authHelper.birthConversionToLocalDate(appleSignUp.getBirthDate())) .build(); } @@ -249,4 +258,38 @@ public UserRes.AlarmAgreeList convertToAlarmAgree(User user) { .eventAlarm(user.getEventAlarm()) .build(); } + + public UserRes.UserToken convertToToken(Long userId, String accessToken, String refreshToken, boolean isNew) { + return UserRes.UserToken + .builder() + .userId(userId) + .accessToken(accessToken) + .refreshToken(refreshToken) + .isNew(isNew) + .build(); + } + + public List convertToFlameList(List donationUsers) { + List userFlameLists = new ArrayList<>(); + + int donationCnt = 1; + + for(DonationUser donationUser : donationUsers) { + userFlameLists.add(convertToFlameDto(donationUser, donationCnt)); + donationCnt ++; + } + return userFlameLists; + } + + private UserRes.UserFlameListDto convertToFlameDto(DonationUser donationUser, int donationCnt) { + return UserRes.UserFlameListDto + .builder() + .donationId(donationUser.getId()) + .inherenceName(donationUser.getInherenceName()) + .inherenceNumber(donationUser.getInherenceNumber()) + .donationCnt(donationCnt) + .donationStatus(donationUser.getDonationStatus()) + .donationStatusName(donationUser.getDonationStatus().getName()) + .build(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/dto/UserReq.java b/Match-Api/src/main/java/com/example/matchapi/user/dto/UserReq.java index e43c05c2..879c5467 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/dto/UserReq.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/dto/UserReq.java @@ -9,6 +9,7 @@ import javax.validation.constraints.*; +import java.time.LocalDate; public class UserReq { @@ -17,6 +18,7 @@ public class UserReq { @NoArgsConstructor @Getter @Setter + @ToString @Schema(description = "01-02,03🔑 소셜 로그인 토큰 API Request") public static class SocialLoginToken{ @Schema(description = "소셜 액세스 토큰", required = true, example = "asdkjanwjkldnjk----") @@ -29,6 +31,7 @@ public static class SocialLoginToken{ @Builder @AllArgsConstructor @NoArgsConstructor + @ToString @Schema(description ="01-04🔑 문자인증 API Request") public static class Sms { @Schema(description = "전화번호 입력", required = true, example = "01012345678") @@ -42,6 +45,7 @@ public static class Sms { @Setter @Builder @AllArgsConstructor + @ToString @NoArgsConstructor @Schema(description = "01-05🔑 회원가입 API Request") public static class SignUpUser { @@ -72,6 +76,7 @@ public static class SignUpUser { @Setter @Builder @AllArgsConstructor + @ToString @NoArgsConstructor @Schema(description = "01-05🔑 회원가입 핸드폰 번호 검증 API Request") public static class UserPhone { @@ -86,6 +91,7 @@ public static class UserPhone { @Setter @Builder @AllArgsConstructor + @ToString @NoArgsConstructor @Schema(description = "01-05-01🔑 회원가입 이메일 검증 API Request") public static class UserEmail { @@ -99,6 +105,7 @@ public static class UserEmail { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString @Schema(description = "01-06🔑 로그인 API Request") public static class LogIn { @Email @@ -112,6 +119,7 @@ public static class LogIn { @Getter @Setter @Builder + @ToString @AllArgsConstructor @NoArgsConstructor public static class EditMyPage { @@ -123,6 +131,7 @@ public static class EditMyPage { @Getter @Setter @Builder + @ToString @AllArgsConstructor @NoArgsConstructor public static class UserEmailAuth { @@ -134,6 +143,7 @@ public static class UserEmailAuth { @Setter @Builder @AllArgsConstructor + @ToString @NoArgsConstructor public static class UserPhoneAuth { private String phone; @@ -146,6 +156,7 @@ public static class UserPhoneAuth { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class ModifyProfile { @Schema(description = "이름", required = false, example = "이메누") private String name; @@ -159,6 +170,7 @@ public static class ModifyProfile { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class FcmToken { private String fcmToken; @@ -170,6 +182,7 @@ public static class FcmToken { @Builder @AllArgsConstructor @NoArgsConstructor + @ToString public static class ModifyPhone { private String oldPhone; @@ -179,10 +192,96 @@ public static class ModifyPhone { @Setter @Builder @AllArgsConstructor + @ToString @NoArgsConstructor public static class ModifyEmail { private String oldEmail; private String newEmail; } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @ToString + public static class AppleUserInfo { + @NotBlank(message = "이름을 입력해주세요") + private String name; + + @NotNull(message = "생일을 입력해주세요") + private LocalDate birthDate; + + @NotBlank(message = "전화번호를 입력해주세요") + private String phone; + } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @ToString + public static class AppleCode { + @Schema(description = "애플 코드 입력", required = true) + @NotBlank(message = "코드를 입력해주세요") + private String code; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @ToString + public static class FindPassword { + @Schema(description = "인증 번호 받은 이메일") + @NotBlank(message = "이메일을 입력해주세요") + private String email; + + @Schema(description = "애플 코드 입력", required = true) + @NotBlank(message = "코드를 입력해주세요") + private String code; + + @Schema(description = "변경할 비밀번호") + @NotBlank(message = "변경할 비밀번호를 입력해주세요") + private String modifyPassword; + } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @ToString + public static class AppleSignUp { + @NotEmpty (message = "소셜 ID를 입력해주세요") + @Schema(description ="소셜 ID",required = true,example = "match123") + private String socialId; + + @NotEmpty (message = "이메일을 입력해주세요") + @Schema(description ="이메일",required = true,example = "match123@gmail.com") + private String email; + + @NotEmpty (message = "이름을 입력해주세요") + @Schema(description ="이름",required = true,example = "match123") + private String name; + + @NotEmpty(message = "전화번호를 입력해주세요") + @Size(min = 11, max = 11, message = "전화번호는 11자리 이어야 합니다.") + @Pattern(regexp = "^01(?:0|1|[6-9])[0-9]{7,8}$", message = "전화번호 형식에 맞지 않습니다. 01012345678 '-' 를 제외하고 입력해주세요. ") + @Schema(description ="전화번호",required = true,example = "0101234567") + private String phone; + + @Schema(description ="성별",required = true,example = "남성은 남성 여성은 여성 선택 안함 ") + @Enum(message="남성, 여성, 선택 안함에 맞춰서 입력해주세요") + private Gender gender; + + @NotEmpty (message = "생년월일을 입력해주세요") + @Schema(description ="생일",required = true,example = "20200101") + private String birthDate; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java b/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java index d0c35260..9329ea21 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java @@ -1,5 +1,6 @@ package com.example.matchapi.user.dto; +import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.user.entity.enums.Alarm; import com.example.matchdomain.user.entity.enums.SocialType; import io.swagger.v3.oas.annotations.media.Schema; @@ -20,6 +21,8 @@ public static class UserToken{ private String accessToken; @Schema(description = "리프레쉬 토큰", required = true, example = "asdkjanwjkldnjk----") private String refreshToken; + @Schema(description = "회원가입 유무 true - 회원가입, false - 로그인") + private boolean isNew; } @Getter @Setter @@ -212,4 +215,23 @@ public static class AlarmAgreeList { private Alarm eventAlarm; } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class UserFlameListDto { + private Long donationId; + + private int donationCnt; + + private String inherenceName; + + private String inherenceNumber; + + private DonationStatus donationStatus; + + private String donationStatusName; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/helper/AuthHelper.java b/Match-Api/src/main/java/com/example/matchapi/user/helper/AuthHelper.java index 10e43939..8121b845 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/helper/AuthHelper.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/helper/AuthHelper.java @@ -17,6 +17,7 @@ import java.util.Optional; import java.util.UUID; +import static com.example.matchdomain.common.model.Status.ACTIVE; import static com.example.matchdomain.user.exception.UserSignUpErrorCode.EXIST_USER_PHONENUMBER; import static com.example.matchdomain.user.entity.enums.SocialType.*; @@ -46,7 +47,7 @@ public LocalDate birthConversion(String birthYear, String birthDay) { public void checkUserExists(String phoneNumber, SocialType socialType) { HashMap errorType = new HashMap<>(); - Optional user = userRepository.findByPhoneNumberAndSocialTypeNot(phoneNumber.replaceAll("\\D+", "").replaceFirst("^82", "0"), socialType); + Optional user = userRepository.findByPhoneNumberAndSocialTypeNotAndStatus(phoneNumber.replaceAll("\\D+", "").replaceFirst("^82", "0"), socialType, ACTIVE); if (user.isPresent()) { errorType.put("signUpType", socialTypeConversion(user.get().getSocialType())); diff --git a/Match-Api/src/main/java/com/example/matchapi/user/service/AligoService.java b/Match-Api/src/main/java/com/example/matchapi/user/service/AligoService.java new file mode 100644 index 00000000..6ed92a0b --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/user/service/AligoService.java @@ -0,0 +1,42 @@ +package com.example.matchapi.user.service; + +import com.example.matchapi.common.security.JwtService; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchinfrastructure.aligo.dto.AlimType; +import com.example.matchinfrastructure.match_aligo.client.MatchAligoFeignClient; +import com.example.matchinfrastructure.match_aligo.dto.AlimTalkDto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@Slf4j +@RequiredArgsConstructor +public class AligoService { + private final MatchAligoFeignClient matchAligoFeignClient; + private final JwtService jwtService; + + public CommonResponse sendSmsAuth(String phone, String code){ + return matchAligoFeignClient.sendSmsAuth(createJwt(), phone, code); + } + + @Async("alim-talk") + public void sendAlimTalk(AlimType alimType, AlimTalkDto alimTalkDto){ + CommonResponse response = matchAligoFeignClient.sendAlimTalk(createJwt(), alimType, alimTalkDto); + log.info(response.toString()); + } + + + public void sendAlimTalks(AlimType alimType, List alimTalkDtos){ + CommonResponse response = matchAligoFeignClient.sendAlimTalks(createJwt(), alimType, alimTalkDtos); + log.info(response.toString()); + } + + public String createJwt(){ + return jwtService.createToken(1L); + } + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java b/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java index f5db4ed5..01ca3840 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java @@ -1,12 +1,13 @@ package com.example.matchapi.user.service; import com.example.matchapi.common.security.JwtService; -import com.example.matchapi.user.convertor.UserConvertor; +import com.example.matchapi.user.converter.UserConverter; import com.example.matchapi.user.dto.UserReq; import com.example.matchapi.user.dto.UserRes; import com.example.matchapi.user.helper.AuthHelper; import com.example.matchapi.user.helper.SmsHelper; import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.BaseDynamicException; import com.example.matchcommon.exception.UnauthorizedException; import com.example.matchcommon.properties.JwtProperties; import com.example.matchcommon.properties.KakaoProperties; @@ -15,16 +16,12 @@ import com.example.matchcommon.service.MailService; import com.example.matchdomain.redis.entity.CodeAuth; import com.example.matchdomain.redis.repository.CodeAuthRepository; -import com.example.matchdomain.redis.repository.RefreshTokenRepository; import com.example.matchdomain.user.adaptor.UserAdaptor; -import com.example.matchdomain.user.entity.Authority; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.entity.UserAddress; import com.example.matchdomain.user.repository.UserAddressRepository; import com.example.matchdomain.user.repository.UserRepository; import com.example.matchinfrastructure.match_aligo.client.MatchAligoFeignClient; -import com.example.matchinfrastructure.oauth.apple.client.AppleFeignClient; -import com.example.matchinfrastructure.oauth.apple.dto.ApplePublicResponse; import com.example.matchinfrastructure.oauth.apple.dto.AppleUserRes; import com.example.matchinfrastructure.oauth.apple.service.AppleAuthService; import com.example.matchinfrastructure.oauth.kakao.client.KakaoFeignClient; @@ -42,16 +39,21 @@ import org.springframework.stereotype.Service; import javax.transaction.Transactional; +import javax.validation.Valid; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Optional; import static com.example.matchcommon.constants.MatchStatic.BEARER; +import static com.example.matchdomain.common.model.Status.ACTIVE; import static com.example.matchdomain.user.entity.enums.AuthorityEnum.ROLE_ADMIN; import static com.example.matchdomain.user.entity.enums.SocialType.*; import static com.example.matchdomain.user.exception.AdminLoginErrorCode.NOT_ADMIN; +import static com.example.matchdomain.user.exception.AppleLoginErrorCode.NOT_EXISTS_APPLE_USER; import static com.example.matchdomain.user.exception.CodeAuthErrorCode.NOT_CORRECT_AUTH; import static com.example.matchdomain.user.exception.CodeAuthErrorCode.NOT_CORRECT_CODE; +import static com.example.matchdomain.user.exception.SendEmailFindPassword.NOT_EXISTS_EMAIL; import static com.example.matchdomain.user.exception.UserAuthErrorCode.NOT_EXIST_USER; import static com.example.matchdomain.user.exception.UserLoginErrorCode.NOT_CORRECT_PASSWORD; import static com.example.matchdomain.user.exception.UserNormalSignUpErrorCode.USERS_EXISTS_EMAIL; @@ -60,171 +62,136 @@ @Service @RequiredArgsConstructor public class AuthService { - private final KakaoFeignClient kakaoFeignClient; - private final KakaoLoginFeignClient kakaoLoginFeignClient; - private final NaverLoginFeignClient naverLoginFeignClient; - private final NaverFeignClient naverFeignClient; - private final KakaoProperties kakaoProperties; - private final NaverProperties naverProperties; - private final UserRepository userRepository; private final UserAddressRepository userAddressRepository; private final JwtService jwtService; private final AuthHelper authHelper; - private final UserConvertor userConvertor; + private final UserConverter userConverter; private final PasswordEncoder passwordEncoder; private final SmsHelper smsHelper; - private final RefreshTokenRepository refreshTokenRepository; - private final JwtProperties jwtProperties; - private final MailService mailService; private final CodeAuthRepository codeAuthRepository; - private final MatchAligoFeignClient matchAligoFeignClient; private final AppleAuthService authService; private final UserAdaptor userAdaptor; + private final KakaoService kakaoService; + private final NaverService naverService; + private final MailService mailService; + private final AligoService aligoService; @Transactional public UserRes.UserToken kakaoLogIn(UserReq.SocialLoginToken socialLoginToken) { - KakaoUserInfoDto kakaoUserInfoDto = kakaoFeignClient.getInfo(BEARER + socialLoginToken.getAccessToken()); + boolean isNew ; + + KakaoUserInfoDto kakaoUserInfoDto = kakaoService.getKakaoInfo(socialLoginToken.getAccessToken()); Long userId; - Optional user = userRepository.findBySocialIdAndSocialType(kakaoUserInfoDto.getId(), KAKAO); + + Optional user = userAdaptor.existsSocialUser(kakaoUserInfoDto.getId(), KAKAO); + authHelper.checkUserExists(kakaoUserInfoDto.getPhoneNumber(), KAKAO); - //소셜 로그인 정보가 없을 시 if (user.isEmpty()){ userId = kakaoSignUp(kakaoUserInfoDto); - KakaoUserAddressDto kakaoUserAddressDto = kakaoFeignClient.getUserAddress(BEARER+socialLoginToken.getAccessToken()); - if(!kakaoUserAddressDto.isShippingAddressesNeedsAgreement()){ - List userAddressList = new ArrayList<>(); - for(KakaoUserAddressDto.ShippingAddresses shippingAddresses : kakaoUserAddressDto.getShippingAddresses()){ - UserAddress userAddress = userConvertor.convertToAddUserAddress(userId,shippingAddresses); - userAddressList.add(userAddress); - } - userAddressRepository.saveAll(userAddressList); - } + saveUserAddress(userId, socialLoginToken.getAccessToken()); + isNew = true; } - //소셜 로그인 정보가 있을 시 else { - authHelper.checkUserExists(kakaoUserInfoDto.getPhoneNumber(), KAKAO); userId = user.get().getId(); + isNew = false; } - UserRes.Token token = createToken(userId); - + return createToken(userId, isNew); + } - return new UserRes.UserToken(userId, token.getAccessToken(), token.getRefreshToken()); + private void saveUserAddress(Long userId, String accessToken) { + KakaoUserAddressDto kakaoUserAddressDto = kakaoService.getKakaoUserAddress(accessToken); + if(!kakaoUserAddressDto.isShippingAddressesNeedsAgreement()){ + List userAddressList = new ArrayList<>(); + for(KakaoUserAddressDto.ShippingAddresses shippingAddresses : kakaoUserAddressDto.getShippingAddresses()){ + UserAddress userAddress = userConverter.convertToAddUserAddress(userId,shippingAddresses); + userAddressList.add(userAddress); + } + userAddressRepository.saveAll(userAddressList); + } } - private UserRes.Token createToken(Long userId) { - UserRes.Token token = jwtService.createTokens(userId); - refreshTokenRepository.save(userConvertor.convertToRefreshToken(userId,token.getRefreshToken(),jwtProperties.getRefreshTokenSeconds())); - return token; + private UserRes.UserToken createToken(Long userId, boolean isNew) { + UserRes.Token token = jwtService.createTokens(userId); + + return userConverter.convertToToken(userId, token.getAccessToken(), token.getRefreshToken(), isNew); } private Long kakaoSignUp(KakaoUserInfoDto kakaoUserInfoDto) { - User user = userConvertor.convertToKakaoSignUpUser(kakaoUserInfoDto, KAKAO); - - System.out.println(kakaoUserInfoDto.getPhoneNumber()); + User user = userConverter.convertToKakaoSignUpUser(kakaoUserInfoDto, KAKAO); - return userRepository.save(user).getId(); + return userAdaptor.save(user).getId(); } @Transactional public Long naverSignUp(NaverUserInfoDto naverUserInfoDto) { - return userRepository.save(userConvertor.convertToNaverSignUpUser(naverUserInfoDto, NAVER)).getId(); + return userAdaptor.save(userConverter.convertToNaverSignUpUser(naverUserInfoDto, NAVER)).getId(); } public KakaoLoginTokenRes getOauthToken(String code, String referer) { - return kakaoLoginFeignClient.kakaoAuth( - kakaoProperties.getKakaoClientId(), - kakaoProperties.getKakaoRedirectUrl(), - kakaoProperties.getKakaoClientSecret(), - code); + return kakaoService.getKakaoOauthToken(code); } public UserRes.UserToken getNaverOauthToken(String code) { - NaverTokenRes naverTokenRes = naverLoginFeignClient.naverAuth( - naverProperties.getNaverClientId(), - naverProperties.getNaverClientSecret(), - code - ); - - return naverLogIn(new UserReq.SocialLoginToken(naverTokenRes.getAccess_token())); + return naverLogIn(naverService.getNaverOauthToken(code).getAccess_token()); } - public UserRes.UserToken naverLogIn(UserReq.SocialLoginToken socialLoginToken) { - NaverUserInfoDto naverUserInfoDto = naverFeignClient.getInfo(BEARER + socialLoginToken.getAccessToken()); - Long userId; - authHelper.checkUserExists(naverUserInfoDto.getMobile(), NAVER); + public UserRes.UserToken naverLogIn(String socialToken) { + Long userId; boolean isNew; - Optional user = userRepository.findBySocialIdAndSocialType(naverUserInfoDto.getResponse().getId(), NAVER); + NaverUserInfoDto naverUserInfoDto = naverService.getNaverUserInfo(socialToken); - if (user.isEmpty()) userId = naverSignUp(naverUserInfoDto); + authHelper.checkUserExists(naverUserInfoDto.getMobile(), NAVER); - else userId = user.get().getId(); + Optional user = userAdaptor.existsSocialUser(naverUserInfoDto.getResponse().getId(), NAVER); - UserRes.Token token = createToken(userId); - - return new UserRes.UserToken(userId, token.getAccessToken(), token.getRefreshToken()); + if (user.isEmpty()) { + userId = naverSignUp(naverUserInfoDto); + isNew = true; + } + else { + userId = user.get().getId(); + isNew = false; + } + + return createToken(userId, isNew); } public UserRes.UserToken signUpUser(UserReq.SignUpUser signUpUser) { - if(userRepository.existsByPhoneNumber(signUpUser.getPhone())) throw new BadRequestException(USERS_EXISTS_PHONE); - if(userRepository.existsByEmail(signUpUser.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); + if(userAdaptor.existsPhoneNumber(signUpUser.getPhone())) throw new BadRequestException(USERS_EXISTS_PHONE); + if(userAdaptor.existsEmail(signUpUser.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); - Long userId = userRepository.save(userConvertor.convertToSignUpUser(signUpUser)).getId(); - - UserRes.Token token = createToken(userId); - - - return new UserRes.UserToken(userId, token.getAccessToken(), token.getRefreshToken()); + return createToken(userAdaptor.save(userConverter.convertToSignUpUser(signUpUser)).getId(), true); } public void checkUserPhone(UserReq.UserPhone userPhone) { - if(userRepository.existsByPhoneNumber(userPhone.getPhone())) throw new BadRequestException(USERS_EXISTS_PHONE); + if(userAdaptor.existsPhoneNumber(userPhone.getPhone())) throw new BadRequestException(USERS_EXISTS_PHONE); } public void checkUserEmail(UserReq.UserEmail userEmail) { - if(userRepository.existsByEmail(userEmail.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); + if(userAdaptor.existsEmail(userEmail.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); } public UserRes.UserToken logIn(UserReq.LogIn logIn) { - User user=userRepository.findByUsername(logIn.getEmail()).orElseThrow(() -> new UnauthorizedException(NOT_EXIST_USER)); + User user = userAdaptor.findByUsernameAndStatus(logIn.getEmail()); if(!passwordEncoder.matches(logIn.getPassword(),user.getPassword())) throw new BadRequestException(NOT_CORRECT_PASSWORD); - Long userId = user.getId(); - - UserRes.Token token = createToken(userId); - - //반환 값 아이디 추가 - return new UserRes.UserToken(userId, token.getAccessToken(), token.getRefreshToken()); - } - - public KakaoUserAddressDto getKakaoAddress(String accessToken) { - return kakaoFeignClient.getUserAddress(BEARER + accessToken); + return createToken(user.getId(), false); } - - public NaverAddressDto getNaverAddress(String accessToken) { - return naverFeignClient.getUserAddress(BEARER + accessToken); - } - - public UserRes.UserToken adminLogIn(UserReq.LogIn logIn) { - User user=userRepository.findByUsername(logIn.getEmail()).orElseThrow(() -> new UnauthorizedException(NOT_EXIST_USER)); - + User user= userAdaptor.findByUserName(logIn.getEmail()); if(!passwordEncoder.matches(logIn.getPassword(),user.getPassword())) throw new BadRequestException(NOT_CORRECT_PASSWORD); if(!user.getRole().contains(ROLE_ADMIN.getValue())) throw new BadRequestException(NOT_ADMIN); - Long userId = user.getId(); - - UserRes.Token token = createToken(userId); - - return new UserRes.UserToken(userId, token.getAccessToken(), token.getRefreshToken()); + return createToken(user.getId(), false); } public void sendEmailMessage(String email) { @@ -235,7 +202,6 @@ public void sendEmailMessage(String email) { codeAuthRepository.save(CodeAuth.builder().auth(email).code(code).ttl(300).build()); mailService.sendEmailMessage(email, code); - } public void checkUserEmailAuth(UserReq.UserEmailAuth email) { @@ -247,9 +213,7 @@ public void sendPhone(String phone) { checkUserPhone(new UserReq.UserPhone(phone)); String code = smsHelper.createRandomNumber(); codeAuthRepository.save(CodeAuth.builder().auth(phone).code(code).ttl(300).build()); - CommonResponse sendRes = matchAligoFeignClient.sendSmsAuth(jwtService.createToken(1L), phone, code); - System.out.println(sendRes.getCode()); - System.out.println(sendRes.getMessage()); + CommonResponse sendRes = aligoService.sendSmsAuth(phone, code); } public void checkPhoneAuth(UserReq.UserPhoneAuth phone) { @@ -260,20 +224,52 @@ public void checkPhoneAuth(UserReq.UserPhoneAuth phone) { public UserRes.UserToken appleLogin(UserReq.SocialLoginToken socialLoginToken) { AppleUserRes appleUserRes = authService.appleLogin(socialLoginToken.getAccessToken()); - if(userRepository.existsByEmail(appleUserRes.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); + if(userAdaptor.existsEmailAndSocial(appleUserRes.getEmail(), APPLE)) throw new BadRequestException(USERS_EXISTS_EMAIL); + Optional user = userAdaptor.existsSocialUser(appleUserRes.getSocialId(), APPLE); - Long userId; - if (user.isEmpty()) userId = appleSignUp(appleUserRes); + if (user.isEmpty()) { + HashMap userInfo = new HashMap<>(); - else userId = user.get().getId(); + userInfo.put("socialId", appleUserRes.getSocialId()); + userInfo.put("email", appleUserRes.getEmail()); + + throw new BaseDynamicException(NOT_EXISTS_APPLE_USER, userInfo); + } - UserRes.Token token = createToken(userId); + Long userId = user.get().getId(); - return new UserRes.UserToken(userId, token.getAccessToken(), token.getRefreshToken()); + return createToken(userId, false); } - private Long appleSignUp(AppleUserRes appleUserRes) { - return userRepository.save(userConvertor.convertToAppleUserSignUp(appleUserRes)).getId(); + public UserRes.UserToken appleSignUp(UserReq.@Valid AppleSignUp appleSignUp) { + if(userAdaptor.existsPhoneNumber(appleSignUp.getPhone())) throw new BadRequestException(USERS_EXISTS_PHONE); + + if(userAdaptor.existsEmail(appleSignUp.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); + + return createToken(userAdaptor.save(userConverter.convertToAppleUserSignUp(appleSignUp)).getId(), true); + } + + + public void sendEmailPasswordFind(String email) { + if(!userAdaptor.checkEmailPassword(email, NORMAL)) throw new BadRequestException(NOT_EXISTS_EMAIL); + + String code = smsHelper.createRandomNumber(); + + codeAuthRepository.save(CodeAuth.builder().auth(email).code(code).ttl(300).build()); + + mailService.sendEmailMessage(email, code); + } + + public void modifyPassword(UserReq.FindPassword findPassword) { + CodeAuth codeAuth = codeAuthRepository.findById(findPassword.getEmail()).orElseThrow(()->new BadRequestException(NOT_CORRECT_AUTH)); + + if(!codeAuth.getCode().equals(findPassword.getCode()))throw new BadRequestException(NOT_CORRECT_CODE); + + User user = userAdaptor.findByUserName(findPassword.getEmail()); + + user.setPassword(passwordEncoder.encode(findPassword.getModifyPassword())); + + user = userAdaptor.save(user); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/service/KakaoService.java b/Match-Api/src/main/java/com/example/matchapi/user/service/KakaoService.java new file mode 100644 index 00000000..e0279eb8 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/user/service/KakaoService.java @@ -0,0 +1,39 @@ +package com.example.matchapi.user.service; + +import com.example.matchcommon.properties.KakaoProperties; +import com.example.matchinfrastructure.oauth.kakao.client.KakaoFeignClient; +import com.example.matchinfrastructure.oauth.kakao.client.KakaoLoginFeignClient; +import com.example.matchinfrastructure.oauth.kakao.dto.KakaoLoginTokenRes; +import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserAddressDto; +import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserInfoDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import static com.example.matchcommon.constants.MatchStatic.BEARER; + +@Service +@RequiredArgsConstructor +public class KakaoService { + private final KakaoFeignClient kakaoFeignClient; + private final KakaoLoginFeignClient kakaoLoginFeignClient; + private final KakaoProperties kakaoProperties; + + + public KakaoUserInfoDto getKakaoInfo(String socialToken){ + return kakaoFeignClient.getInfo(BEARER + socialToken); + } + + public KakaoLoginTokenRes getKakaoOauthToken(String code){ + return kakaoLoginFeignClient.kakaoAuth( + kakaoProperties.getKakaoClientId(), + kakaoProperties.getKakaoRedirectUrl(), + kakaoProperties.getKakaoClientSecret(), + code); + } + + public KakaoUserAddressDto getKakaoUserAddress(String socialToken){ + return kakaoFeignClient.getUserAddress(BEARER + socialToken); + } + + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/user/service/NaverService.java b/Match-Api/src/main/java/com/example/matchapi/user/service/NaverService.java new file mode 100644 index 00000000..f4823964 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/user/service/NaverService.java @@ -0,0 +1,34 @@ +package com.example.matchapi.user.service; + +import com.example.matchcommon.properties.NaverProperties; +import com.example.matchinfrastructure.oauth.naver.client.NaverFeignClient; +import com.example.matchinfrastructure.oauth.naver.client.NaverLoginFeignClient; +import com.example.matchinfrastructure.oauth.naver.dto.NaverTokenRes; +import com.example.matchinfrastructure.oauth.naver.dto.NaverUserInfoDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import static com.example.matchcommon.constants.MatchStatic.BEARER; + +@Service +@RequiredArgsConstructor +public class NaverService { + private final NaverLoginFeignClient naverLoginFeignClient; + private final NaverFeignClient naverFeignClient; + private final NaverProperties naverProperties; + + + public NaverTokenRes getNaverOauthToken(String code){ + return naverLoginFeignClient.naverAuth( + naverProperties.getNaverClientId(), + naverProperties.getNaverClientSecret(), + code + ); + } + + public NaverUserInfoDto getNaverUserInfo(String socialToken){ + return naverFeignClient.getInfo(BEARER + socialToken); + } + + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java b/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java index da3fa4f7..d84c1cdc 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java @@ -1,29 +1,35 @@ package com.example.matchapi.user.service; import com.example.matchapi.common.model.AlarmType; +import com.example.matchapi.donation.service.DonationService; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.service.OrderService; -import com.example.matchapi.project.convertor.ProjectConvertor; -import com.example.matchapi.project.helper.ProjectHelper; -import com.example.matchapi.user.convertor.UserConvertor; +import com.example.matchapi.project.converter.ProjectConverter; +import com.example.matchapi.user.converter.UserConverter; import com.example.matchapi.user.dto.UserReq; import com.example.matchapi.user.dto.UserRes; +import com.example.matchcommon.annotation.RedissonLock; import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.NotFoundException; +import com.example.matchcommon.exception.UnauthorizedException; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.common.model.Status; import com.example.matchdomain.donation.entity.RegularPayment; -import com.example.matchdomain.donation.repository.DonationUserRepository; import com.example.matchdomain.donation.repository.RegularPaymentRepository; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.project.repository.ProjectUserAttentionRepository; +import com.example.matchdomain.user.adaptor.UserAdaptor; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.entity.UserAddress; import com.example.matchdomain.user.entity.enums.Alarm; import com.example.matchdomain.user.entity.pk.UserFcmPk; import com.example.matchdomain.user.exception.ModifyEmailCode; +import com.example.matchdomain.user.exception.UserAuthErrorCode; import com.example.matchdomain.user.repository.UserAddressRepository; import com.example.matchdomain.user.repository.UserFcmTokenRepository; import com.example.matchdomain.user.repository.UserRepository; import com.example.matchinfrastructure.config.s3.S3UploadService; +import com.example.matchinfrastructure.oauth.apple.service.AppleAuthService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.data.domain.Page; @@ -32,6 +38,7 @@ import org.springframework.stereotype.Service; import javax.transaction.Transactional; +import javax.validation.Valid; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.TemporalAdjusters; @@ -45,6 +52,7 @@ import static com.example.matchdomain.user.entity.enums.Alarm.INACTIVE; import static com.example.matchdomain.user.exception.ModifyEmailCode.NOT_CORRECT_EMAIL; import static com.example.matchdomain.user.exception.ModifyPhoneErrorCode.NOT_CORRECT_PHONE; +import static com.example.matchdomain.user.exception.UserAuthErrorCode.NOT_EXIST_USER; import static com.example.matchdomain.user.exception.UserNormalSignUpErrorCode.USERS_EXISTS_PHONE; @Service @@ -53,13 +61,17 @@ public class UserService { private final UserRepository userRepository; private final UserAddressRepository userAddressRepository; - private final UserConvertor userConvertor; - private final ProjectConvertor projectConvertor; + private final UserConverter userConverter; + private final ProjectConverter projectConverter; private final ProjectUserAttentionRepository projectUserAttentionRepository; private final OrderService orderService; private final RegularPaymentRepository regularPaymentRepository; private final S3UploadService s3UploadService; private final UserFcmTokenRepository userFcmTokenRepository; + private final DonationService donationService; + private final AppleAuthService appleAuthService; + private final AuthService authService; + private final UserAdaptor userAdaptor; public Optional findUser(long id) { return userRepository.findById(id); @@ -72,18 +84,18 @@ public List findUserAddress(Long id) { } public UserRes.EditMyPage getEditMyPage(User user) { - return userConvertor.convertToMyPage(user); + return userConverter.convertToMyPage(user); } public UserRes.MyPage getMyPage(User user) { List regularPayments = regularPaymentRepository.findByUser(user); Long projectAttentionCnt = projectUserAttentionRepository.countById_userId(user.getId()); - return projectConvertor.getMyPage(regularPayments,projectAttentionCnt, user.getNickname()); + return projectConverter.getMyPage(regularPayments,projectAttentionCnt, user.getNickname()); } public OrderRes.UserDetail getUserInfo(User user) { - return userConvertor.convertToUserInfo(user); + return userConverter.convertToUserInfo(user); } public UserRes.SignUpInfo getUserSignUpInfo() { @@ -93,7 +105,7 @@ public UserRes.SignUpInfo getUserSignUpInfo() { Long weekUser = userRepository.countByCreatedAtGreaterThanAndCreatedAtLessThan(LocalDateTime.parse(localDate.minusWeeks(1)+FIRST_TIME) , LocalDateTime.parse(localDate+LAST_TIME)); Long monthUser = userRepository.countByCreatedAtGreaterThanAndCreatedAtLessThan(LocalDateTime.parse(localDate.with(TemporalAdjusters.firstDayOfMonth())+FIRST_TIME), LocalDateTime.parse(localDate.with(TemporalAdjusters.lastDayOfMonth())+LAST_TIME)); - return userConvertor.convertToUserSignUpInfo(oneDayUser,weekUser,monthUser,totalUser); + return userConverter.convertToUserSignUpInfo(oneDayUser,weekUser,monthUser,totalUser); } @Transactional @@ -117,7 +129,7 @@ else if(status!=null){ userList.getContent().forEach( result -> userLists.add( - userConvertor.convertToUserList(result) + userConverter.convertToUserList(result) ) ); @@ -130,11 +142,11 @@ public UserRes.UserAdminDetail getUserAdminDetail(Long userId) { List userCards = orderService.getUserBillCard(userId); - return userConvertor.convertToUserAdminDetail(userDetail,userCards); + return userConverter.convertToUserAdminDetail(userDetail,userCards); } public UserRes.Profile getProfile(User user) { - return userConvertor.convertToUserProfile(user); + return userConverter.convertToUserProfile(user); } @Transactional @@ -165,11 +177,14 @@ else if (modifyProfile.getMultipartFile() != null){ } public void saveFcmToken(User user, UserReq.FcmToken token) { - userFcmTokenRepository.save(userConvertor.convertToUserFcm(user, token)); + userFcmTokenRepository.save(userConverter.convertToUserFcm(user, token)); } public void deleteFcmToken(Long userId, String deviceId) { - userFcmTokenRepository.deleteById(UserFcmPk.builder().userId(userId).deviceId(deviceId).build()); + UserFcmPk userFcmPk = UserFcmPk.builder().userId(userId).deviceId(deviceId).build(); + if(userFcmTokenRepository.existsById(userFcmPk)) { + userFcmTokenRepository.deleteById(userFcmPk); + } } @Transactional @@ -184,14 +199,14 @@ public void modifyPhoneNumber(User user, UserReq.ModifyPhone phone) { @Transactional public void modifyEmail(User user, UserReq.ModifyEmail email) { if(!user.getEmail().equals(email.getOldEmail())) throw new BadRequestException(NOT_CORRECT_EMAIL); - if(userRepository.existsByEmail(email.getNewEmail())) throw new BadRequestException(ModifyEmailCode.USERS_EXISTS_EMAIL); + if(userRepository.existsByEmailAndStatus(email.getNewEmail(), Status.ACTIVE)) throw new BadRequestException(ModifyEmailCode.USERS_EXISTS_EMAIL); user.setEmail(email.getNewEmail()); userRepository.save(user); } public UserRes.AlarmAgreeList getAlarmAgreeList(User user) { System.out.println(user.getName()); - return userConvertor.convertToAlarmAgree(user); + return userConverter.convertToAlarmAgree(user); } @CacheEvict(value = "userCache", key = "#user.id", cacheManager = "redisCacheManager") @@ -216,6 +231,34 @@ public UserRes.AlarmAgreeList patchAlarm(User user, AlarmType alarmType) { user = userRepository.save(user); - return userConvertor.convertToAlarmAgree(user); + return userConverter.convertToAlarmAgree(user); + } + + @Transactional + public void postAppleUserInfo(User user, UserReq.AppleUserInfo appleUserInfo) { + authService.checkUserPhone(new UserReq.UserPhone(appleUserInfo.getPhone())); + user.updateUserInfo(appleUserInfo.getBirthDate(), appleUserInfo.getName(), appleUserInfo.getPhone()); + + userRepository.save(user); + } + + @RedissonLock(LockName = "유저 탈퇴", key = "#user.id") + public void deleteUserInfo(User user) { + user.setStatus(Status.INACTIVE); + donationService.deleteRegularPayment(user); + userRepository.save(user); + } + + public void deleteAppleUserInfo(User user, UserReq.AppleCode appleCode) { + appleAuthService.revokeUser(appleCode.getCode()); + deleteUserInfo(user); + } + + public User findByUser(String userId) { + return userAdaptor.findByUser(userId); + } + + public User findByUserId(Long userId) { + return userAdaptor.findByUserId(userId).orElseThrow(() -> new NotFoundException(NOT_EXIST_USER)); } } diff --git a/Match-Api/src/main/resources/application.yml b/Match-Api/src/main/resources/application.yml index 54412c78..b15867a5 100644 --- a/Match-Api/src/main/resources/application.yml +++ b/Match-Api/src/main/resources/application.yml @@ -36,12 +36,14 @@ springdoc: tags-sorter: alpha # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 operations-sorter: alpha # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 doc-expansion: none + display-request-duration: true api-docs: path: /api-docs/json groups: enabled: true cache: disabled: true + show-actuator: true --- diff --git a/Match-Batch/src/main/java/com/example/matchbatch/OrderHelper.java b/Match-Batch/src/main/java/com/example/matchbatch/OrderHelper.java deleted file mode 100644 index edb10655..00000000 --- a/Match-Batch/src/main/java/com/example/matchbatch/OrderHelper.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.example.matchbatch; - -import com.example.matchcommon.annotation.Helper; -import com.example.matchcommon.properties.NicePayProperties; -import com.example.matchdomain.donation.entity.enums.PayMethod; -import com.example.matchdomain.donation.entity.flameEnum.Adjective; -import com.example.matchdomain.donation.entity.flameEnum.AdjectiveFlame; -import com.example.matchdomain.donation.repository.DonationUserRepository; -import lombok.RequiredArgsConstructor; - -import java.util.Base64; -import java.util.Random; - -import static com.example.matchcommon.constants.MatchStatic.BASIC; - -@RequiredArgsConstructor -@Helper -public class OrderHelper { - - private final NicePayProperties nicePayProperties; - private final DonationUserRepository donationUserRepository; - public String getNicePaymentAuthorizationHeader() { - return BASIC + Base64.getEncoder().encodeToString((nicePayProperties.getClient() + ":" + nicePayProperties.getSecret()).getBytes()); - } - - public String createFlameName(String name) { - String randomName; - do { - randomName = name + "님의 " + getRandomEnumValue(AdjectiveFlame.class).getValue() + " " + getRandomEnumValue(Adjective.class).getValue() + " 불꽃이"; - } while (donationUserRepository.existsByInherenceName(randomName)); - - return randomName; - } - - public static > T getRandomEnumValue(Class enumClass) { - Random random = new Random(); - T[] values = enumClass.getEnumConstants(); - return values[random.nextInt(values.length)]; - } - - public PayMethod getPayMethod(String value) { - for (PayMethod payMethod : PayMethod.values()) { - if(payMethod.getValue().equalsIgnoreCase(value)){ - return payMethod; - } - } - return null; - } -} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/converter/DonationConverter.java b/Match-Batch/src/main/java/com/example/matchbatch/converter/DonationConverter.java new file mode 100644 index 00000000..b383f13c --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/converter/DonationConverter.java @@ -0,0 +1,26 @@ +package com.example.matchbatch.converter; + +import com.example.matchcommon.annotation.Converter; +import com.example.matchdomain.donation.entity.DonationHistory; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.enums.HistoryStatus; + +import static com.example.matchdomain.donation.entity.enums.HistoryStatus.CREATE; + +@Converter +public class DonationConverter { + public DonationHistory DonationHistory(Long id, HistoryStatus historyStatus) { + return DonationHistory.builder() + .donationUserId(id) + .historyStatus(historyStatus) + .build(); + } + + public DonationHistory convertToDonationHistory(DonationUser donationUser) { + return DonationHistory.builder() + .donationUserId(donationUser.getId()) + .regularPaymentId(donationUser.getRegularPaymentId()) + .historyStatus(CREATE) + .build(); + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/converter/NotificationConverter.java b/Match-Batch/src/main/java/com/example/matchbatch/converter/NotificationConverter.java new file mode 100644 index 00000000..e559b11b --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/converter/NotificationConverter.java @@ -0,0 +1,22 @@ +package com.example.matchbatch.converter; + +import com.example.matchcommon.annotation.Converter; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.notification.entity.Notification; +import com.example.matchdomain.notification.enums.NotificationType; +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; + +@Converter +public class NotificationConverter { + + public Notification convertToNotification(FCMNotificationRequestDto fcmNotificationRequestDto, DonationUser donationUser) { + return Notification + .builder() + .userId(donationUser.getUserId()) + .donationUser(donationUser) + .notificationType(NotificationType.DONATION) + .title(fcmNotificationRequestDto.getTitle()) + .body(fcmNotificationRequestDto.getBody()) + .build(); + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/converter/OrderConverter.java b/Match-Batch/src/main/java/com/example/matchbatch/converter/OrderConverter.java new file mode 100644 index 00000000..5a8cb59a --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/converter/OrderConverter.java @@ -0,0 +1,53 @@ +package com.example.matchbatch.converter; + +import com.example.matchbatch.helper.OrderHelper; +import com.example.matchbatch.model.PaymentCntDto; +import com.example.matchcommon.annotation.Converter; +import com.example.matchdomain.donation.entity.*; +import com.example.matchdomain.donation.entity.enums.DonationStatus; +import com.example.matchdomain.donation.entity.enums.PayMethod; +import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.donation.entity.flameEnum.FlameImage; +import com.example.matchdomain.donation.entity.flameEnum.FlameType; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import lombok.RequiredArgsConstructor; + +import static java.lang.Integer.parseInt; + +@Converter +@RequiredArgsConstructor +public class OrderConverter { + public DonationUser donationUser(PortOneBillPayResponse response, Long userId, String flameName, String inherenceNumber, Long projectId, Long regularPaymentId) { + return DonationUser.builder() + .userId(userId) + .price((long) response.getAmount()) + .tid(response.getImp_uid()) + .orderId(response.getMerchant_uid()) + .donationStatus(DonationStatus.EXECUTION_BEFORE) + .payMethod(PayMethod.CARD) + .inherenceName(flameName) + .inherenceNumber(inherenceNumber) + .regularStatus(RegularStatus.REGULAR) + .projectId(projectId) + .regularPaymentId(regularPaymentId) + .flameImage(FlameImage.NORMAL_IMG.getImg()) + .flameType(FlameType.NORMAL_FLAME) + .build(); + } + + public PaymentCntDto convertToPaymentCnt(int totalAmount, int successCount) { + return PaymentCntDto + .builder() + .totalAmount(totalAmount) + .successCnt(successCount) + .build(); + } + + public RequestFailedHistory convertToRequestFailedHistory(RegularPayment payment, String reason) { + return RequestFailedHistory + .builder() + .regularPaymentId(payment.getId()) + .reason(reason) + .build(); + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/convertor/DonationConvertor.java b/Match-Batch/src/main/java/com/example/matchbatch/convertor/DonationConvertor.java deleted file mode 100644 index 6bcfdee0..00000000 --- a/Match-Batch/src/main/java/com/example/matchbatch/convertor/DonationConvertor.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.matchbatch.convertor; - -import com.example.matchcommon.annotation.Convertor; -import com.example.matchdomain.donation.entity.DonationHistory; -import com.example.matchdomain.donation.entity.enums.HistoryStatus; - -@Convertor -public class DonationConvertor { - public DonationHistory DonationHistory(Long id, HistoryStatus historyStatus) { - return DonationHistory.builder() - .donationUserId(id) - .historyStatus(historyStatus) - .build(); - } -} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java b/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java deleted file mode 100644 index e00a7ae9..00000000 --- a/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.example.matchbatch.convertor; - -import com.example.matchbatch.OrderHelper; -import com.example.matchcommon.annotation.Convertor; -import com.example.matchdomain.donation.entity.*; -import com.example.matchdomain.donation.entity.enums.DonationStatus; -import com.example.matchdomain.donation.entity.enums.PayMethod; -import com.example.matchdomain.donation.entity.enums.PaymentStatus; -import com.example.matchdomain.donation.entity.enums.RegularStatus; -import com.example.matchdomain.donation.entity.flameEnum.FlameImage; -import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkRequest; -import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkResponse; -import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; -import lombok.RequiredArgsConstructor; - -import java.time.LocalDateTime; - -import static java.lang.Integer.parseInt; - -@Convertor -@RequiredArgsConstructor -public class OrderConvertor { - private final OrderHelper orderHelper; - public NiceBillOkRequest niceBillRequest(RegularPayment regularPayment, String orderId) { - return NiceBillOkRequest.builder() - .cardQuota(0) - .amount(regularPayment.getAmount()) - .goodsName(LocalDateTime.now().getDayOfMonth() + "월 달 " +"MATCH 기부금 정기 결제") - .useShopInterest(false) - .orderId(orderId) - .build(); - } - - public DonationUser donationUser(PortOneBillPayResponse response, Long userId, String flameName, String inherenceNumber, Long projectId, Long regularPaymentId) { - return DonationUser.builder() - .userId(userId) - .price(Long.valueOf(response.getAmount())) - .tid(response.getImp_uid()) - .orderId(response.getMerchant_uid()) - .donationStatus(DonationStatus.EXECUTION_BEFORE) - .payMethod(PayMethod.CARD) - .inherenceName(flameName) - .inherenceNumber(inherenceNumber) - .regularStatus(RegularStatus.REGULAR) - .projectId(projectId) - .regularPaymentId(regularPaymentId) - .flameImage(FlameImage.NORMAL_IMG.getImg()) - .build(); - } - - public RequestPaymentHistory RegularHistory(NiceBillOkResponse niceBillOkResponse, Long userId, PaymentStatus paymentStatus, String reason, Long regularPaymentId, int payDate, Long userCardId) { - return RequestPaymentHistory.builder() - .orderId(niceBillOkResponse.getOrderId()) - .tid(niceBillOkResponse.getTid()) - .userId(userId) - .paymentStatus(paymentStatus) - .reason(reason) - .amount(niceBillOkResponse.getAmount()) - .regularPaymentId(regularPaymentId) - .payDate(payDate) - .userCardId(userCardId) - .build(); - } -} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/helper/OrderHelper.java b/Match-Batch/src/main/java/com/example/matchbatch/helper/OrderHelper.java new file mode 100644 index 00000000..ebf41a74 --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/helper/OrderHelper.java @@ -0,0 +1,75 @@ +package com.example.matchbatch.helper; + +import com.example.matchcommon.annotation.Helper; +import com.example.matchcommon.properties.NicePayProperties; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.enums.PayMethod; +import com.example.matchdomain.donation.entity.flameEnum.Adjective; +import com.example.matchdomain.donation.entity.flameEnum.AdjectiveFlame; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.RandomStringUtils; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Base64; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +import static com.example.matchcommon.constants.MatchStatic.BASIC; +import static com.example.matchcommon.constants.MatchStatic.REGULAR; + +@RequiredArgsConstructor +@Helper +public class OrderHelper { + + private final NicePayProperties nicePayProperties; + private final DonationAdaptor donationAdaptor; + public String getNicePaymentAuthorizationHeader() { + return BASIC + Base64.getEncoder().encodeToString((nicePayProperties.getClient() + ":" + nicePayProperties.getSecret()).getBytes()); + } + + public String createFlameName(User user) { + List donationUsers = donationAdaptor.findDonationListsByUser(user); + + List inherenceNames = getInherenceName(donationUsers); + + String randomName; + do { + randomName = getRandomEnumValue(AdjectiveFlame.class).getValue() + " 불꽃이"; + } while (inherenceNames.contains(randomName)); + + return randomName; + } + + public List getInherenceName(List donationUsers){ + return donationUsers.stream() + .map(DonationUser :: getInherenceName).collect(Collectors.toList()); + } + + + public static > T getRandomEnumValue(Class enumClass) { + Random random = new Random(); + T[] values = enumClass.getEnumConstants(); + return values[random.nextInt(values.length)]; + } + + public PayMethod getPayMethod(String value) { + for (PayMethod payMethod : PayMethod.values()) { + if(payMethod.getValue().equalsIgnoreCase(value)){ + return payMethod; + } + } + return null; + } + + public String createRandomOrderId() { + boolean useLetters = true; + boolean useNumbers = true; + String randomStr = RandomStringUtils.random(12, useLetters, useNumbers); + + return REGULAR + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + randomStr; + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/helper/TimeUtils.java b/Match-Batch/src/main/java/com/example/matchbatch/helper/TimeUtils.java new file mode 100644 index 00000000..b6707ff8 --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/helper/TimeUtils.java @@ -0,0 +1,30 @@ +package com.example.matchbatch.helper; + +import com.example.matchbatch.model.CalculateMonthLastDateDto; +import com.example.matchdomain.donation.entity.RegularPayment; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.Calendar; +import java.util.Date; + +@Component +public class TimeUtils { + public boolean isPaymentDue(RegularPayment payment, LocalDate currentDate) { + return !(payment.getCreatedAt().getMonthValue() == currentDate.getMonthValue() && payment.getCreatedAt().getYear() == currentDate.getYear()); + } + + public CalculateMonthLastDateDto calculateMonthLastDate() { + Date currentDate = new Date(); + + Calendar calendar = Calendar.getInstance(); + + calendar.setTime(currentDate); + + int lastDayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); + + int currentDay = calendar.get(Calendar.DAY_OF_MONTH); + + return CalculateMonthLastDateDto.builder().lastDayOfMonth(lastDayOfMonth).currentDay(currentDay).build(); + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/job/DonationFailedRetry.java b/Match-Batch/src/main/java/com/example/matchbatch/job/DonationFailedRetry.java index dbb2636b..a1eb9c1e 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/job/DonationFailedRetry.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/job/DonationFailedRetry.java @@ -1,7 +1,6 @@ package com.example.matchbatch.job; import com.example.matchbatch.service.OrderService; -import com.example.matchdomain.donation.repository.RegularPaymentRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; @@ -18,12 +17,9 @@ public class DonationFailedRetry { private final JobBuilderFactory jobBuilderFactory; private final StepBuilderFactory stepBuilderFactory; - private final RegularPaymentRepository regularPaymentRepository; private final OrderService orderService; private static final String JOB_NAME = "정기 구독(기부) 결제 재시도"; - - @Bean public Job regularPaymentRetryJob() { log.info(JOB_NAME + " job start"); @@ -33,10 +29,10 @@ public Job regularPaymentRetryJob() { } private Step regularPaymentRetryStep() { - return stepBuilderFactory.get(JOB_NAME+"step") + return stepBuilderFactory.get(JOB_NAME+" STEP") .tasklet((contribution, chunkContext) -> { log.info("Step!"); - orderService.regularPaymentRetry(); + orderService.regularPaymentRetry(JOB_NAME); return RepeatStatus.FINISHED; }) .build(); diff --git a/Match-Batch/src/main/java/com/example/matchbatch/job/DonationRegularPayment.java b/Match-Batch/src/main/java/com/example/matchbatch/job/DonationRegularPayment.java index 03e26ee7..49a8f9cb 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/job/DonationRegularPayment.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/job/DonationRegularPayment.java @@ -1,21 +1,14 @@ package com.example.matchbatch.job; import com.example.matchbatch.service.OrderService; -import com.example.matchdomain.donation.entity.RegularPayment; -import com.example.matchdomain.donation.repository.RegularPaymentRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.*; import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.time.LocalDate; -import java.util.Date; -import java.util.List; - @Configuration @RequiredArgsConstructor @@ -28,9 +21,8 @@ public class DonationRegularPayment { // Job 정의 private static final String JOB_NAME = "정기 구독(기부) 결제"; - @Bean public Job regularPaymentJob() { - log.info(JOB_NAME + " job start"); + log.info(JOB_NAME + " JOB START"); return jobBuilderFactory.get(JOB_NAME) .start(regularPaymentStep()) @@ -38,11 +30,10 @@ public Job regularPaymentJob() { } private Step regularPaymentStep() { - return stepBuilderFactory.get(JOB_NAME+"step") + return stepBuilderFactory.get(JOB_NAME+" STEP") .tasklet((contribution, chunkContext) -> { log.info("Step!"); - //orderService.regularDonationPayment(); - + orderService.regularDonationPayment(); return RepeatStatus.FINISHED; }) .build(); diff --git a/Match-Batch/src/main/java/com/example/matchbatch/job/UserDeleteJob.java b/Match-Batch/src/main/java/com/example/matchbatch/job/UserDeleteJob.java new file mode 100644 index 00000000..43f66d4a --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/job/UserDeleteJob.java @@ -0,0 +1,41 @@ +package com.example.matchbatch.job; + +import com.example.matchbatch.service.OrderService; +import com.example.matchbatch.service.UserService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class UserDeleteJob { + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + private final UserService userService; + private static final String JOB_NAME = "유저 삭제"; + + + public Job deleteUserSchedulingJob() { + log.info(JOB_NAME + " JOB START"); + + return jobBuilderFactory.get(JOB_NAME) + .start(deleteUserStep()) + .build(); + } + + private Step deleteUserStep() { + return stepBuilderFactory.get(JOB_NAME+" STEP") + .tasklet((contribution, chunkContext) -> { + log.info("Step!"); + userService.deleteUserInfos(); + return RepeatStatus.FINISHED; + }) + .build(); + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/model/CalculateMonthLastDateDto.java b/Match-Batch/src/main/java/com/example/matchbatch/model/CalculateMonthLastDateDto.java new file mode 100644 index 00000000..0de647a9 --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/model/CalculateMonthLastDateDto.java @@ -0,0 +1,14 @@ +package com.example.matchbatch.model; + +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CalculateMonthLastDateDto { + private int lastDayOfMonth; + + private int currentDay; +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/model/PaymentCntDto.java b/Match-Batch/src/main/java/com/example/matchbatch/model/PaymentCntDto.java new file mode 100644 index 00000000..7181f55e --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/model/PaymentCntDto.java @@ -0,0 +1,13 @@ +package com.example.matchbatch.model; + +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PaymentCntDto { + private int totalAmount; + private int successCnt; +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java b/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java index c5238fff..418c7ec0 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java @@ -4,7 +4,7 @@ import com.example.matchbatch.job.DonationRegularPayment; import com.example.matchcommon.annotation.Scheduler; import com.example.matchinfrastructure.discord.client.DiscordFeignClient; -import com.example.matchinfrastructure.discord.convertor.DiscordConvertor; +import com.example.matchinfrastructure.discord.converter.DiscordConverter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.JobParameter; @@ -26,16 +26,15 @@ public class PaymentScheduler { private final DonationRegularPayment regularPaymentJob; private final DonationFailedRetry donationFailedRetry; private final DiscordFeignClient discordFeignClient; - private final DiscordConvertor discordConvertor; + private final DiscordConverter discordConverter; //매일 12시 30분에 실행되는 스케줄러 - //@Scheduled(cron = "0 30 12 * * *") + @Scheduled(cron = "0 30 12 * * *") //매 1분마다 실행 //@Scheduled(cron = "0,20,40 * * * * *", zone = "asia/seoul") - @Scheduled(fixedDelay = 10000) + //@Scheduled(fixedDelay = 30000) public void RegularPayScheduler(){ - log.info("정기 결제 스케줄러가 시작"); - discordFeignClient.alertMessage(discordConvertor.convertToAlertBatchMessage("정기 결제 스케줄러 시작",20)); + log.info("정기 결제 스케줄러 시작"); Map confMap = new HashMap<>(); confMap.put("time", new JobParameter(System.currentTimeMillis())); @@ -46,14 +45,13 @@ public void RegularPayScheduler(){ jobLauncher.run(regularPaymentJob.regularPaymentJob(), jobParameters); } catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException | JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) { - discordFeignClient.errorMessage(discordConvertor.convertToErrorBatchServer("정기 결제 스케줄러", e.getMessage())); + discordFeignClient.errorMessage(discordConverter.convertToErrorBatchServer("정기 결제 스케줄러", e.getMessage())); log.error(e.getMessage()); + throw new RuntimeException(e); } } - - @Scheduled(cron = "0 0 13/1 * * *") - //@Scheduled(fixedDelay = 10000) + @Scheduled(cron = "0 0 15 */2 * ?") // 매월 짝수 일의 15시(3 PM)에 실행 public void RegularFailedPayScheduler(){ log.info("정기 결제 실패 한 결제들 재시도 시작"); @@ -67,8 +65,9 @@ public void RegularFailedPayScheduler(){ jobLauncher.run(donationFailedRetry.regularPaymentRetryJob(), jobParameters); } catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException | JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) { - discordFeignClient.errorMessage(discordConvertor.convertToErrorBatchServer("정기 결제 실패 재시도 스케줄러", e.getMessage())); + discordFeignClient.errorMessage(discordConverter.convertToErrorBatchServer("정기 결제 실패 재시도 스케줄러", e.getMessage())); log.error(e.getMessage()); + throw new RuntimeException(e); } } } diff --git a/Match-Batch/src/main/java/com/example/matchbatch/scheduler/UserDeleteScheduler.java b/Match-Batch/src/main/java/com/example/matchbatch/scheduler/UserDeleteScheduler.java new file mode 100644 index 00000000..26642661 --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/scheduler/UserDeleteScheduler.java @@ -0,0 +1,43 @@ +package com.example.matchbatch.scheduler; + +import com.example.matchbatch.job.UserDeleteJob; +import com.example.matchbatch.service.UserService; +import com.example.matchcommon.annotation.Scheduler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.JobParameter; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersInvalidException; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; +import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; +import org.springframework.scheduling.annotation.Scheduled; + +import java.util.HashMap; +import java.util.Map; + +@Scheduler +@RequiredArgsConstructor +@Slf4j +public class UserDeleteScheduler { + private final JobLauncher jobLauncher; + private final UserDeleteJob userDeleteJob; + + @Scheduled(cron = "0 30 3 * * *") + public void deleteUserSchedule(){ + log.info("유저 삭제 정기 스케줄러 시작"); + Map confMap = new HashMap<>(); + confMap.put("time", new JobParameter(System.currentTimeMillis())); + + JobParameters jobParameters = new JobParameters(confMap); + + try { + jobLauncher.run(userDeleteJob.deleteUserSchedulingJob(), jobParameters); + } catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException + | JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } + } + +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/service/DonationService.java b/Match-Batch/src/main/java/com/example/matchbatch/service/DonationService.java new file mode 100644 index 00000000..bf3dc0d7 --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/service/DonationService.java @@ -0,0 +1,64 @@ +package com.example.matchbatch.service; + +import com.example.matchbatch.converter.DonationConverter; +import com.example.matchbatch.converter.OrderConverter; +import com.example.matchbatch.helper.OrderHelper; +import com.example.matchcommon.annotation.PaymentIntercept; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.adaptor.DonationHistoryAdaptor; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.RegularPayment; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +import static com.example.matchcommon.constants.MatchStatic.PAYMENT_DATE_FORMAT; + +@Service +@RequiredArgsConstructor +public class DonationService { + private final OrderHelper orderHelper; + private final DonationAdaptor donationAdaptor; + private final DonationHistoryAdaptor donationHistoryAdaptor; + private final OrderConverter orderConverter; + private final DonationConverter donationConverter; + + @PaymentIntercept(key = "#portOneBillPayResponse.imp_uid") + @Transactional + public DonationUser processSaveDonationPayment(PortOneBillPayResponse portOneBillPayResponse, RegularPayment payment) { + DonationUser donationUser = createDonationUser(payment, portOneBillPayResponse); + createDonationHistory(donationUser); + return donationUser; + } + + public DonationUser createDonationUser(RegularPayment payment, PortOneBillPayResponse portOneResponse) { + String flameName = orderHelper.createFlameName(payment.getUser()); + String inherenceNumber = getCurrentDateFormatted() + "." + createRandomUUID(); + return donationAdaptor.save( + orderConverter.donationUser(portOneResponse, payment.getUserId(), flameName, inherenceNumber, payment.getProjectId(), payment.getId()) + ); + } + + public void createDonationHistory(DonationUser donationUser) { + donationHistoryAdaptor.saveDonationHistory(donationConverter.convertToDonationHistory(donationUser)); + } + + public String createRandomUUID() { + return UUID.randomUUID().toString(); + } + + private String getCurrentDateFormatted() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern(PAYMENT_DATE_FORMAT)); + } + + + public void deleteForDonation(User user) { + + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/service/NotificationService.java b/Match-Batch/src/main/java/com/example/matchbatch/service/NotificationService.java new file mode 100644 index 00000000..04f73a1b --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/service/NotificationService.java @@ -0,0 +1,48 @@ +package com.example.matchbatch.service; + +import com.example.matchbatch.converter.NotificationConverter; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.adaptor.RegularPaymentAdaptor; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.RegularPayment; +import com.example.matchdomain.notification.adaptor.NotificationAdaptor; +import com.example.matchdomain.user.adaptor.UserFcmAdaptor; +import com.example.matchdomain.user.entity.User; +import com.example.matchdomain.user.entity.UserFcmToken; +import com.example.matchinfrastructure.fcm.converter.FcmConverter; +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import com.example.matchinfrastructure.fcm.service.FcmNotificationService; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NotificationService { + private final NotificationConverter converter; + private final NotificationAdaptor notificationAdaptor; + private final FcmConverter fcmConverter; + private final UserFcmAdaptor userFcmAdaptor; + private final FcmNotificationService fcmNotificationService; + + + @Async("notification-payment") + public void sendPaymentSuccess(DonationUser donationUser) { + List userFcmTokens = userFcmAdaptor.findByUser(donationUser.getUserId()); + + FCMNotificationRequestDto fcmNotificationRequestDto = fcmConverter.convertToDto(donationUser.getInherenceName()); + for(UserFcmToken userFcmToken : userFcmTokens) { + fcmNotificationRequestDto.setToken(userFcmToken.getFcmToken()); + fcmNotificationService.sendNotificationRegularPayments(fcmNotificationRequestDto, fcmConverter.convertToPayData()); + } + + notificationAdaptor.saveNotification(converter.convertToNotification(fcmNotificationRequestDto, donationUser)); + } + + public void deleteForNotification(User user) { + notificationAdaptor.deleteByUserId(user.getId()); + } +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java b/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java index a9fb13af..51a096e9 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java @@ -1,192 +1,187 @@ package com.example.matchbatch.service; -import com.example.matchbatch.OrderHelper; -import com.example.matchbatch.convertor.DonationConvertor; -import com.example.matchbatch.convertor.OrderConvertor; -import com.example.matchdomain.common.model.Status; +import com.example.matchbatch.converter.OrderConverter; +import com.example.matchbatch.helper.OrderHelper; +import com.example.matchbatch.helper.TimeUtils; +import com.example.matchbatch.model.CalculateMonthLastDateDto; +import com.example.matchbatch.model.PaymentCntDto; +import com.example.matchcommon.annotation.RedissonLock; +import com.example.matchdomain.donation.adaptor.RegularPaymentAdaptor; +import com.example.matchdomain.donation.adaptor.RequestFailedHistoryAdapter; import com.example.matchdomain.donation.entity.*; -import com.example.matchdomain.donation.entity.enums.RegularPayStatus; -import com.example.matchdomain.donation.repository.DonationHistoryRepository; -import com.example.matchdomain.donation.repository.DonationUserRepository; -import com.example.matchdomain.donation.repository.RegularPaymentRepository; -import com.example.matchdomain.donation.repository.RequestPaymentHistoryRepository; -import com.example.matchinfrastructure.discord.client.DiscordFeignClient; -import com.example.matchinfrastructure.discord.convertor.DiscordConvertor; -import com.example.matchinfrastructure.pay.nice.client.NiceAuthFeignClient; -import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkResponse; +import com.example.matchdomain.user.adaptor.UserCardAdaptor; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.discord.service.DiscordService; import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; -import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; +import com.example.matchinfrastructure.pay.portone.converter.PortOneConverter; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; import com.example.matchinfrastructure.pay.portone.service.PortOneAuthService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.RandomStringUtils; import org.springframework.stereotype.Service; -import javax.transaction.Transactional; import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.stream.Collectors; -import static com.example.matchcommon.constants.MatchStatic.REGULAR; -import static com.example.matchdomain.donation.entity.enums.HistoryStatus.CREATE; -import static com.example.matchdomain.donation.entity.enums.PaymentStatus.COMPLETE; -import static com.example.matchdomain.donation.entity.enums.PaymentStatus.FAIL; +import static com.example.matchcommon.constants.MatchStatic.*; @Service @RequiredArgsConstructor @Slf4j public class OrderService { - private final RegularPaymentRepository regularPaymentRepository; private final OrderHelper orderHelper; - private final OrderConvertor orderConvertor; - private final RequestPaymentHistoryRepository regularPaymentHistoryRepository; - private final DonationUserRepository donationUserRepository; - private final DiscordFeignClient discordFeignClient; - private final DiscordConvertor discordConvertor; - private final DonationHistoryRepository donationHistoryRepository; - private final DonationConvertor donationConvertor; private final PortOneFeignClient portOneFeignClient; + private final RegularPaymentAdaptor regularPaymentAdaptor; + private final RequestFailedHistoryAdapter requestFailedHistoryAdapter; private final PortOneAuthService portOneAuthService; - private final PortOneConvertor portOneConvertor; + private final PortOneConverter portOneConverter; + private final DiscordService discordService; + private final DonationService donationService; + private final OrderConverter orderConverter; + private final TimeUtils timeUtils; + private final NotificationService notificationService; + private final UserCardAdaptor userCardAdaptor; - @Transactional public void regularDonationPayment() { - List regularPayments = calculateDay(); - List requestPaymentHistories = new ArrayList<>(); - List donationUsers = new ArrayList<>(); - List donationHistories = new ArrayList<>(); + if (regularPayments.isEmpty()) { + return; + } - int trueCnt = 0; - int amount = 0; - int successCnt = 0; + discordService.sendBatchStartAlert(PAYMENT_ALERT_START, regularPayments.size()); - discordFeignClient.alertMessage(discordConvertor.convertToAlertBatchMessage("정기 결제 스케줄러 시작", regularPayments.size())); + PaymentCntDto paymentCntDto = paymentsTry(regularPayments, REGULAR_PAYMENT); - String accessToken = portOneAuthService.getTokens(); + discordService.sendBatchFinishAlert(PAYMENT_ALERT_FINISH, paymentCntDto.getTotalAmount(), regularPayments.size(), paymentCntDto.getSuccessCnt(), regularPayments.size() - paymentCntDto.getSuccessCnt()); + } - if (regularPayments.size() > 0) { - LocalDate currentDate = LocalDate.now(); - int currentYear = currentDate.getYear(); - int currentMonth = currentDate.getMonthValue(); - for (RegularPayment regularPayment : regularPayments) { - if(regularPayment.getCreatedAt().getMonthValue()==currentMonth & regularPayment.getCreatedAt().getYear()==currentYear) { - log.info("not pay Day of Month " + "userId :" + regularPayment.getUserId()+ " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount() + "원 projectId :" + regularPayment.getProjectId() + " payId : "+ regularPayment.getId()); - } - else{ - trueCnt +=1 ; - UserCard userCard = regularPayment.getUserCard(); - Long userId = regularPayment.getUserId(); + @RedissonLock(LockName = "결제 재시도 LOCK", key = "#jobName") + public void regularPaymentRetry(String jobName) { + List requestFailedHistories = requestFailedHistoryAdapter.findFailHistory(); - String orderId = REGULAR + createRandomOrderId(); + List regularPayments = historyToRegularPayments(requestFailedHistories); - PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.convertPayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "월 달 " +"MATCH 기부금 정기 결제", userCard.getCustomerId())); + if (regularPayments.isEmpty()) { + return; + } + discordService.sendBatchStartAlert(PAYMENT_RETRY_START, regularPayments.size()); - String flameName = orderHelper.createFlameName(regularPayment.getUser().getName()); + PaymentCntDto paymentCntDto = paymentsTry(regularPayments, RETRY_PAYMENT); - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); + discordService.sendBatchFinishAlert(PAYMENT_RETRY_FINISH, paymentCntDto.getTotalAmount(), regularPayments.size(), paymentCntDto.getSuccessCnt(), regularPayments.size() - paymentCntDto.getSuccessCnt()); + } - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationUser(portOneResponse.getResponse(), userId, flameName, inherenceNumber, regularPayment.getProjectId(),regularPayment.getId())); - donationHistories.add(donationConvertor.DonationHistory(donationUser.getId(), CREATE )); - //requestPaymentHistories.add(orderConvertor.RegularHistory(niceBillOkResponse, userId, COMPLETE, "SUCCESS",regularPayment.getId(), regularPayment.getPayDate(),regularPayment.getUserCardId())); - log.info("success Payment " + "userId :" + regularPayment.getUserId() + " orderId : " + orderId + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount() + "원 projectId :" + regularPayment.getProjectId()); - amount += regularPayment.getAmount(); - successCnt += 1; + private PaymentCntDto paymentsTry(List regularPayments, String type) { + String accessToken = portOneAuthService.getAuthToken(); + int successCount = 0, totalAmount = 0; - } + for (RegularPayment payment : regularPayments) { + log.info("paymentId: " + payment.getId()); + if (!timeUtils.isPaymentDue(payment, LocalDate.now())) { + logPaymentSkipped(payment); + continue; } - donationHistoryRepository.saveAll(donationHistories); - regularPaymentHistoryRepository.saveAll(requestPaymentHistories); - discordFeignClient.alertMessage(discordConvertor.convertToAlertFinishMessage("정기 결제 스케줄러 종료", amount, regularPayments.size(),successCnt, trueCnt)); + boolean isSuccess = processRegularPayment(payment, accessToken, type); + if(isSuccess) { + totalAmount += payment.getAmount(); + successCount++; + } } - } - @Transactional - public List calculateDay() { - Date currentDate = new Date(); - Calendar calendar = Calendar.getInstance(); - calendar.setTime(currentDate); + return orderConverter.convertToPaymentCnt(totalAmount, successCount); + } + + private void logPaymentSkipped(RegularPayment payment) { + log.info(String.format(PAYMENT_LOG_INFO, + payment.getUserId(), + payment.getUserCard().getBid(), + payment.getAmount(), + payment.getProjectId(), + payment.getId())); + } - // 이번달에 마지막 날짜 - int lastDayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); - // 오늘 날짜 구하기 - int currentDay = calendar.get(Calendar.DAY_OF_MONTH); + public boolean processRegularPayment(RegularPayment payment, String accessToken, String type) { + String orderId = orderHelper.createRandomOrderId(); - if (currentDay == lastDayOfMonth) { - //현재 날짜와 달의 마지막 날짜가 같거나 클때의 로직 - System.out.println("현재 날짜가 같아요"); - return regularPaymentRepository.findByPayDateGreaterThanEqualAndStatusAndRegularPayStatus(currentDay, Status.ACTIVE, RegularPayStatus.PROCEEDING); - } else { - // 현재 날짜가 같지 않을 때의 로직 - System.out.println("현재 날짜가 달라요"); - return regularPaymentRepository.findByPayDateGreaterThanEqualAndStatusAndRegularPayStatus(currentDay, Status.ACTIVE, RegularPayStatus.PROCEEDING); + PortOneResponse portOneResponse = attemptPayment(payment, accessToken, orderId); + if(portOneResponse.getCode()!=0){ + handlePaymentFailure(payment, portOneResponse.getMessage(), type); + return true; + } + else { + handlePaymentSuccess(payment, portOneResponse.getResponse(), type, orderId); + return false; } } - public String createRandomUUID() { - return UUID.randomUUID().toString(); + private void handlePaymentFailure(RegularPayment payment, String reason, String type) { + if (REGULAR_PAYMENT.equals(type)) { + requestFailedHistoryAdapter.save(orderConverter.convertToRequestFailedHistory(payment, reason)); + } + log.error(String.format(FAILED_PAYMENT_LOG, payment.getId(), reason)); } - @Transactional - public String createRandomOrderId() { - boolean useLetters = true; - boolean useNumbers = true; - String randomStr = RandomStringUtils.random(12, useLetters, useNumbers); - - return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + randomStr; + private void handlePaymentSuccess(RegularPayment payment, PortOneBillPayResponse response, String type, String orderId) { + if (RETRY_PAYMENT.equals(type)) { + requestFailedHistoryAdapter.deleteByRegularPaymentId(payment.getId()); + } + DonationUser donationUser = donationService.processSaveDonationPayment(response, payment); + log.info(String.format(SUCCESS_PAYMENT_LOG, + payment.getUserId(), + orderId, + payment.getUserCard().getBid(), + payment.getAmount(), + payment.getProjectId())); + notificationService.sendPaymentSuccess(donationUser); } - @Transactional - public void regularPaymentRetry() { - List donationUsers = new ArrayList<>(); - List requestPaymentHistories = regularPaymentHistoryRepository.findByPaymentStatusAndStatus(FAIL, Status.ACTIVE); - - List donationHistories = new ArrayList<>(); - - int amount = 0; - int trueCnt = 0; - int successCnt = 0 ; - String accessToken = portOneAuthService.getTokens(); - discordFeignClient.alertMessage(discordConvertor.convertToAlertBatchMessage("정기 결제 실패 한 리스트 스케줄러가 시작", requestPaymentHistories.size())); - - for(RequestPaymentHistory requestPaymentHistory : requestPaymentHistories){ - RegularPayment regularPayment = requestPaymentHistory.getRegularPayment(); - UserCard userCard = requestPaymentHistory.getUserCard(); - String orderId = REGULAR + createRandomOrderId(); - - PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.convertPayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "월 달 " +"MATCH 기부금 정기 결제", userCard.getCustomerId())); - - trueCnt +=1 ; - - String flameName = orderHelper.createFlameName(requestPaymentHistory.getUser().getName()); - - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); + private PortOneResponse attemptPayment(RegularPayment payment, String accessToken, String orderId) { + UserCard userCard = payment.getUserCard(); + return portOneFeignClient.payWithBillKey( + accessToken, + portOneConverter.convertPayWithBillKey( + userCard.getBid(), + orderId, + payment.getAmount(), + PAY_TITLE, + userCard.getCustomerId())); + } - donationUsers.add(orderConvertor.donationUser(portOneResponse.getResponse(), requestPaymentHistory.getUserId(), flameName, inherenceNumber, regularPayment.getProjectId(), regularPayment.getId())); + public List calculateDay() { + CalculateMonthLastDateDto date = timeUtils.calculateMonthLastDate(); + if (date.getCurrentDay() == date.getLastDayOfMonth()) { + return regularPaymentAdaptor.findByLastDayRegularPayment(date.getCurrentDay()); + } else { + return regularPaymentAdaptor.findByDate(date.getCurrentDay()); + } + } - requestPaymentHistory.setPaymentStatus(COMPLETE); + private List historyToRegularPayments(List requestFailedHistories) { + return requestFailedHistories.stream() + .map(RequestFailedHistory::getRegularPayment).collect(Collectors.toList()); + } - amount += regularPayment.getAmount(); - successCnt +=1; + public void deleteForOrder(User user) { + List userCards = userCardAdaptor.findCardLists(user.getId()); - log.info("success Payment Retry historyId: " + requestPaymentHistory.getId()+" userId :" + regularPayment.getUserId() + " orderId : " + orderId + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount() + "원 projectId :" + regularPayment.getProjectId()); + String accessToken = portOneAuthService.getAuthToken(); + for(UserCard userCard : userCards){ + deleteUserCard(userCard, accessToken); } - donationUserRepository.saveAll(donationUsers); - - discordFeignClient.alertMessage(discordConvertor.convertToAlertFinishMessage("실패한 정기 결제 스케줄러가 종료", amount, requestPaymentHistories.size(),successCnt,trueCnt)); - } + private void deleteUserCard(UserCard userCard, String accessToken) { + portOneFeignClient.deleteBillKey(accessToken, userCard.getCustomerId()); + } } diff --git a/Match-Batch/src/main/java/com/example/matchbatch/service/ProjectService.java b/Match-Batch/src/main/java/com/example/matchbatch/service/ProjectService.java new file mode 100644 index 00000000..4c88cf3f --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/service/ProjectService.java @@ -0,0 +1,24 @@ +package com.example.matchbatch.service; + +import com.example.matchdomain.project.adaptor.AttentionAdaptor; +import com.example.matchdomain.project.adaptor.ProjectCommentAdaptor; +import com.example.matchdomain.project.repository.ProjectUserAttentionRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; + +@Service +@RequiredArgsConstructor +public class ProjectService { + private final AttentionAdaptor attentionAdaptor; + private final ProjectCommentAdaptor projectCommentAdaptor; + + @Transactional + public void deleteForProject(User user) { + projectCommentAdaptor.deleteByUser(user); + attentionAdaptor.deleteByUser(user); + } + +} diff --git a/Match-Batch/src/main/java/com/example/matchbatch/service/UserService.java b/Match-Batch/src/main/java/com/example/matchbatch/service/UserService.java new file mode 100644 index 00000000..f9923422 --- /dev/null +++ b/Match-Batch/src/main/java/com/example/matchbatch/service/UserService.java @@ -0,0 +1,36 @@ +package com.example.matchbatch.service; + +import com.example.matchdomain.project.adaptor.ProjectCommentAdaptor; +import com.example.matchdomain.user.adaptor.UserAdaptor; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class UserService { + private final UserAdaptor userAdaptor; + private final ProjectService projectService; + private final DonationService donationService; + private final OrderService orderService; + private final NotificationService notificationService; + + //기부 관련 제외 모두 삭제 + public void deleteUserInfos() { + List users = userAdaptor.findDeleteUsers(); + + for(User user : users){ + deleteUserInfo(user); + } + } + + private void deleteUserInfo(User user) { + projectService.deleteForProject(user); + notificationService.deleteForNotification(user); + orderService.deleteForOrder(user); + } + +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/annotation/Convertor.java b/Match-Common/src/main/java/com/example/matchcommon/annotation/Converter.java similarity index 92% rename from Match-Common/src/main/java/com/example/matchcommon/annotation/Convertor.java rename to Match-Common/src/main/java/com/example/matchcommon/annotation/Converter.java index faf6506c..6531049f 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/annotation/Convertor.java +++ b/Match-Common/src/main/java/com/example/matchcommon/annotation/Converter.java @@ -10,7 +10,7 @@ @Retention(RetentionPolicy.RUNTIME) @Documented @Component -public @interface Convertor { +public @interface Converter { @AliasFor(annotation = Component.class) String value() default ""; } diff --git a/Match-Common/src/main/java/com/example/matchcommon/annotation/DisableSecurity.java b/Match-Common/src/main/java/com/example/matchcommon/annotation/DisableSecurity.java new file mode 100644 index 00000000..e1d5a8f3 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/annotation/DisableSecurity.java @@ -0,0 +1,11 @@ +package com.example.matchcommon.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface DisableSecurity { +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/annotation/PaymentIntercept.java b/Match-Common/src/main/java/com/example/matchcommon/annotation/PaymentIntercept.java new file mode 100644 index 00000000..1618f35b --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/annotation/PaymentIntercept.java @@ -0,0 +1,12 @@ +package com.example.matchcommon.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface PaymentIntercept { + String key(); +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/aop/KeyGenerator.java b/Match-Common/src/main/java/com/example/matchcommon/aop/KeyGenerator.java new file mode 100644 index 00000000..33b158a2 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/aop/KeyGenerator.java @@ -0,0 +1,20 @@ +package com.example.matchcommon.aop; + +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.stereotype.Component; + +@Component +public class KeyGenerator { + public Object getDynamicValue(String[] parameterNames, Object[] args, String key) { + ExpressionParser parser = new SpelExpressionParser(); + StandardEvaluationContext context = new StandardEvaluationContext(); + + for (int i = 0; i < parameterNames.length; i++) { + context.setVariable(parameterNames[i], args[i]); + } + + return parser.parseExpression(key).getValue(context, Object.class); + } +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/aop/RedissonCheckLockAop.java b/Match-Common/src/main/java/com/example/matchcommon/aop/RedissonCheckLockAop.java index 3bc20933..c9782b8e 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/aop/RedissonCheckLockAop.java +++ b/Match-Common/src/main/java/com/example/matchcommon/aop/RedissonCheckLockAop.java @@ -37,6 +37,7 @@ public Object lock(final ProceedingJoinPoint joinPoint) throws Throwable { RLock rLock = redissonClient.getLock("key" + key); // (1) try { + log.info("Redisson Lock Start key : " + key); boolean available = rLock.tryLock(redissonLock.waitTime(), redissonLock.leaseTime(), redissonLock.timeUnit()); // (2) if (!available) { return false; diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java index 38137fb1..d789568b 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java @@ -14,10 +14,10 @@ public class AsyncConfig extends AsyncConfigurerSupport { @Bean(name = "email") public Executor emailThreadAsync() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(10); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setCorePoolSize(3); // 기본적으로 실행 대기 중인 Thread 개수 executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) - executor.setThreadNamePrefix("async-mail-thread"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.setThreadNamePrefix("async-mail-thread"); executor.initialize(); return executor; } @@ -25,22 +25,65 @@ public Executor emailThreadAsync() { @Bean(name = "fcm") public Executor fcmThreadAsync() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(10); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setCorePoolSize(3); // 기본적으로 실행 대기 중인 Thread 개수 executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) executor.setThreadNamePrefix("async-fcm-thread"); // Spring에서 생성하는 Thread 이름의 접두사 executor.initialize(); return executor; } + @Bean(name = "topic-fcm") + public Executor topicFcmThreadAsync() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(3); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 + executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) + executor.setThreadNamePrefix("topic-fcm"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.initialize(); + return executor; + } @Bean(name = "discord-message") public Executor discordMessageThreadAsync() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(10); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setCorePoolSize(2); // 기본적으로 실행 대기 중인 Thread 개수 executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) executor.setThreadNamePrefix("discord-message-thread"); // Spring에서 생성하는 Thread 이름의 접두사 executor.initialize(); return executor; } + + @Bean(name = "notification-payment") + public Executor notificationPayment() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(3); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 + executor.setQueueCapacity(1000); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) + executor.setThreadNamePrefix("notification-payment-thread"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.initialize(); + return executor; + } + + @Bean(name = "alim-talk") + public Executor alimTalk() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 + executor.setQueueCapacity(1000); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) + executor.setThreadNamePrefix("alim-talk-thread"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.initialize(); + return executor; + } + + @Bean(name = "event-listener") + public Executor eventListener() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 + executor.setQueueCapacity(1000); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) + executor.setThreadNamePrefix("event-listener-thread"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.initialize(); + return executor; + } } \ No newline at end of file diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java index 58ef4ff5..b3992dec 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java @@ -19,7 +19,8 @@ MatchAligoUrl.class, ServerHostProperties.class, WebReturnUrl.class, - PortOneProperties.class + PortOneProperties.class, + AppleProperties.class }) @Configuration public class ConfigurationPropertiesConfig {} diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java index cdbd4ae5..4b002180 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java @@ -41,7 +41,7 @@ public RedisConnectionFactory redisConnectionFactory() { .build()) .commandTimeout(Duration.ofSeconds(1000L)).build(); - if(profile.equals("prod")){ + if(profile.equals("prod")||profile.equals("dev")){ log.info(profile + " profile"); return new LettuceConnectionFactory(clusterConfiguration, clientConfiguration); }else { diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/RedissonConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/RedissonConfig.java index fc26f113..9d9cb8d8 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/RedissonConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/RedissonConfig.java @@ -24,7 +24,7 @@ public class RedissonConfig { public RedissonClient redissonClient() { Config config = new Config(); log.info("RedissonConfig " + profile + " profile"); - if (profile.equals("prod")) { + if (profile.equals("prod")||profile.equals("dev")) { config.useClusterServers() .addNodeAddress(REDISSON_HOST_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort()) // 다른 클러스터 노드도 필요에 따라 추가 diff --git a/Match-Common/src/main/java/com/example/matchcommon/constants/MatchAlertStatic.java b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchAlertStatic.java new file mode 100644 index 00000000..2706b254 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchAlertStatic.java @@ -0,0 +1,13 @@ +package com.example.matchcommon.constants; + +public class MatchAlertStatic { + public static final String ALERT_TITLE = "성냥이가 알려준다냥"; + + public static final String PAY_ALERT_BODY = " 탄생했어요! 앱에서 새로운 불꽃이를 만나보세요!"; + + public static final String PROJECT_UPLOAD_BODY = "매치에 새로운 기부처가 생겼어요 어떤 일을 하는 곳인지 궁금하신가요?"; + + public static final String EVENT_UPLOAD_BODY = "기부자님을 위한 특별한 이벤트가 나타났어요!"; + + +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java index 83dedf02..7e58c323 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java +++ b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java @@ -1,5 +1,9 @@ package com.example.matchcommon.constants; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + public class MatchStatic { public static final String BEARER = "Bearer "; public static final String BASIC = "Basic "; @@ -31,6 +35,42 @@ public class MatchStatic { public static final String NOTICE_S3_DIR = "notice"; + public static final String PAYMENT_LOG = "Payment userId: %d, orderId: %s, bid: %s, amount: %d원, projectId: %d"; + public static final String PAYMENT_DATE_FORMAT = "yy.MM.dd.HH:mm"; + public static final String PAYMENT_ALERT_START = "정기 결제 스케줄러 시작"; + public static final String PAYMENT_ALERT_FINISH = "정기 결제 스케줄러 종료"; + public static final String PAYMENT_RETRY_START = "정기 결제 실패 한 리스트 스케줄러가 시작"; + public static final String PAYMENT_RETRY_FINISH = "실패한 정기 결제 스케줄러가 종료"; + public static final String REGULAR_PAYMENT = "REGULAR"; + + public static final String PAYMENT_LOG_INFO = "Not pay Day of Month userId : %s bid : %s amount : %d원 projectId : %s payId : %s"; + + + public static final String SUCCESS_PAYMENT_LOG = "Success Payment userId : %s orderId : %s bid : %s amount : %d원 projectId : %s"; + + public static final String FAILED_PAYMENT_LOG = "Fail Payment regularPaymentId : %s Reason : %s"; + + public static final String RETRY_PAYMENT = "RETRY"; + + public static final String PAY_TITLE = getCurrentDateFormatted() + " MATCH 기부금 정기 결제"; + + public static final String CANCEL_STATUS = "cancelled"; + + public static final String CANCEL_IMP_UID = "impUid"; + + public static final String CANCEL_ORDER_ID = "orderId"; + + public static final String KID = "74JQ8SGRU2"; + + public static final String CLASS_PATH = "AuthKey_74JQ8SGRU2.p8"; + + public static final List ignoredMethods = List.of(new String[]{"healthCheck", ""}); + + public static final List ignoredUri = List.of(new String[]{"/health", "/error"}); + + private static String getCurrentDateFormatted() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern(PAYMENT_DATE_FORMAT)); + } } diff --git a/Match-Common/src/main/java/com/example/matchcommon/constants/RandomMessageStatic.java b/Match-Common/src/main/java/com/example/matchcommon/constants/RandomMessageStatic.java new file mode 100644 index 00000000..f8271d09 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/constants/RandomMessageStatic.java @@ -0,0 +1,26 @@ +package com.example.matchcommon.constants; + +public class RandomMessageStatic { + public static final String CHILDREN_MESSAGE = "난 아이들에게 도움이 될거야"; + + public static final String YOUTH_MESSAGE = "난 청년들에게 도움이 될거야"; + + public static final String WOMEN_MESSAGE = "나는 여성들에게 도움이 될거야"; + + public static final String ELDER_MESSAGE = "나는 어르신분들에게 도움이 될거야"; + + public static final String DISABLED_MESSAGE = "나는 어려운 사람들에게 도움이 될거야."; + + public static final String SOCIAL_MESSAGE = "나는 사회에 도움이 될거야."; + + public static final String EARTH_MESSAGE = "나는 지구촌 이웃에 도움이 될거야."; + + public static final String NEIGHBOR_MESSAGE = "나는 이웃에 도움이 될거야."; + + public static final String ANIMAL_MESSAGE = "나는 아픈 유기견을 치료할테야."; + + public static final String ENVIRONMENT_MESSAGE = "나는 환경을 깨끗하게 만들거야."; + + public static final String RANDOM_MESSAGE = "나는 좋은 불꽃이가 될거야."; + +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/constants/enums/Topic.java b/Match-Common/src/main/java/com/example/matchcommon/constants/enums/Topic.java new file mode 100644 index 00000000..c1febdab --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/constants/enums/Topic.java @@ -0,0 +1,11 @@ +package com.example.matchcommon.constants.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Topic { + PROJECT_UPLOAD("PROJECT_UPLOAD"), EVENT_UPLOAD("EVENT_UPLOAD"); + private final String topic; +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/exception/NotUserActiveException.java b/Match-Common/src/main/java/com/example/matchcommon/exception/NotUserActiveException.java new file mode 100644 index 00000000..84045d8c --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/exception/NotUserActiveException.java @@ -0,0 +1,24 @@ +package com.example.matchcommon.exception; + +public class NotUserActiveException extends RuntimeException { + private static final long serialVersionUID = 6769829250639411880L; + + /** + * Constructs a {@code NoSuchElementException} with {@code null} + * as its error message string. + */ + public NotUserActiveException() { + super(); + } + + /** + * Constructs a {@code NoSuchElementException}, saving a reference + * to the error message string {@code s} for later retrieval by the + * {@code getMessage} method. + * + * @param s the detail message. + */ + public NotUserActiveException(String s) { + super(s); + } +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/properties/AligoProperties.java b/Match-Common/src/main/java/com/example/matchcommon/properties/AligoProperties.java index 40f73765..a0eab92f 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/properties/AligoProperties.java +++ b/Match-Common/src/main/java/com/example/matchcommon/properties/AligoProperties.java @@ -17,4 +17,5 @@ public class AligoProperties { private String username; private String key; private String sender; + private String senderKey; } diff --git a/Match-Common/src/main/java/com/example/matchcommon/properties/AppleProperties.java b/Match-Common/src/main/java/com/example/matchcommon/properties/AppleProperties.java new file mode 100644 index 00000000..24a0ec74 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/properties/AppleProperties.java @@ -0,0 +1,53 @@ +package com.example.matchcommon.properties; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@RequiredArgsConstructor +@ConstructorBinding +@Component +@ConfigurationProperties("apple") +public class AppleProperties { + private Key key; + + private Bundle bundle; + + private Team team; + + @Getter + @Setter + private static class Key { + private String path; + } + + @Getter + @Setter + private static class Bundle{ + private String id; + } + + @Getter + @Setter + private static class Team{ + private String id; + } + + public String getKeyPath(){ + return key.getPath(); + } + + public String getBundleId(){ + return bundle.getId(); + } + + public String getTeamId(){ + return team.getId(); + } + +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java b/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java index ec48dc1c..3d84def6 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java +++ b/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java @@ -7,10 +7,12 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.ToString; @Getter @AllArgsConstructor +@ToString @JsonPropertyOrder({"isSuccess", "code", "message", "result"}) @Schema(description = "기본 응답") public class CommonResponse { @@ -42,6 +44,10 @@ public static CommonResponse onSuccess(T data) { return new CommonResponse<>(true, "요청에 성공하였습니다.","1000", data); } + public static CommonResponse onSuccessesFail(T data) { + return new CommonResponse<>(false,"요청에 성공하였습니다.", "1000", data); + } + public static CommonResponse onFailure(String code, String message, T data) { return new CommonResponse<>(false, message, code, data); } diff --git a/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java b/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java index a6afda85..7d9f3dc0 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java +++ b/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java @@ -4,11 +4,13 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.ToString; import java.io.Serializable; @Getter @AllArgsConstructor +@ToString @Schema(description = "페이징 처리 응답") public class PageResponse implements Serializable { @Schema(description = "마지막 페이지 유무", required = true, example = "true") diff --git a/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java b/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java index c8586cdb..d5d656aa 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java +++ b/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -57,14 +58,19 @@ private SimpleMailMessage createEmailForm(String toEmail, public void sendEmailMessage(String email, String code){ try { MimeMessage message = emailSender.createMimeMessage(); - message.addRecipients(MimeMessage.RecipientType.TO, email); - message.setSubject("[MATCH] 인증번호 발송"); - message.setText(setContext(code), "utf-8", "html"); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + helper.setTo(email); + helper.setSubject("[MATCH] 인증번호 발송"); + String html = setContext(code); + helper.setText(html, true); + emailSender.send(message); - }catch(Exception exception){ + } catch(Exception exception){ throw new InternalServerException(UNABLE_TO_SEND_EMAIL); } } + private String setContext(String code) { Context context = new Context(); context.setVariable("code", code); @@ -72,14 +78,13 @@ private String setContext(String code) { ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); templateResolver.setPrefix("templates/"); - templateResolver.setSuffix("TemplateMail.html"); + templateResolver.setSuffix(".html"); templateResolver.setTemplateMode(TemplateMode.HTML); templateResolver.setCharacterEncoding("UTF-8"); - templateResolver.setOrder(0); + templateResolver.setOrder(1); templateEngine.setTemplateResolver(templateResolver); - return templateEngine.process("", context); + return templateEngine.process("TemplateMail", context); } - } diff --git a/Match-Common/src/main/java/com/example/matchcommon/util/AesUtil.java b/Match-Common/src/main/java/com/example/matchcommon/util/AesUtil.java new file mode 100644 index 00000000..04866de3 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/util/AesUtil.java @@ -0,0 +1,59 @@ +package com.example.matchcommon.util; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Hex; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; + +@Slf4j +@Component +public class AesUtil { + + + @Value("${private.aes.key}") + private String privateKey; + + + public String aesCBCEncode(String plainText) { + SecretKeySpec secretKey = new SecretKeySpec(privateKey.getBytes(StandardCharsets.UTF_8), "AES"); + IvParameterSpec IV = new IvParameterSpec(privateKey.substring(0,16).getBytes()); + + try { + Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); + c.init(Cipher.ENCRYPT_MODE, secretKey, IV); + + byte[] encrpytionByte = c.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + + return Hex.encodeHexString(encrpytionByte); + } catch (Exception e) { + log.error(e.toString()); + throw new RuntimeException(e); + } + } + + + public String aesCBCDecode(String encodeText) { + + SecretKeySpec secretKey = new SecretKeySpec(privateKey.getBytes(StandardCharsets.UTF_8), "AES"); + IvParameterSpec IV = new IvParameterSpec(privateKey.substring(0,16).getBytes()); + + try { + Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + c.init(Cipher.DECRYPT_MODE, secretKey, IV); + + byte[] decodeByte = Hex.decodeHex(encodeText.toCharArray()); + + return new String(c.doFinal(decodeByte), StandardCharsets.UTF_8); + }catch (Exception e) { + log.error(e.toString()); + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/Match-Common/src/main/resources/application-common.yml b/Match-Common/src/main/resources/application-common.yml index faa47706..975b241d 100644 --- a/Match-Common/src/main/resources/application-common.yml +++ b/Match-Common/src/main/resources/application-common.yml @@ -18,8 +18,8 @@ jwt: header: X-AUTH-TOKEN secret: ${JWT_SECRET_KEY} refresh: ${JWT_REFRESH_KEY} - access-token-seconds: 31536000 - refresh-token-seconds: 31536000 + access-token-seconds: 1 + refresh-token-seconds: 30 sms: secret: ${SMS_SECRET_KEY} api: ${SMS_API_KEY} @@ -67,6 +67,7 @@ aligo: username: ${ALIGO_USERNAME} key: ${ALIGO_KEY} sender: ${SMS_SENDER} + sender-key: ${KAKAO_SENDER_KEY} web: return: @@ -82,3 +83,14 @@ portone: secret: ${PORTONE_SECRET} billmid: ${PORTONE_BILL_MID} +private: + aes: + key: ${AES_PRIVATE_KEY} + +apple: + key: + path: ${APPLE_KEY_PATH} + bundle: + id: ${APPLE_BUNDLE_ID} + team: + id: ${APPLE_TEAM_ID} diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/banner/entity/QBanner.java b/Match-Domain/src/main/generated/com/example/matchdomain/banner/entity/QBanner.java new file mode 100644 index 00000000..fd118ba3 --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/banner/entity/QBanner.java @@ -0,0 +1,70 @@ +package com.example.matchdomain.banner.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QBanner is a Querydsl query type for Banner + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QBanner extends EntityPathBase { + + private static final long serialVersionUID = 558390347L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QBanner banner = new QBanner("banner"); + + public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + + public final StringPath bannerImg = createString("bannerImg"); + + public final EnumPath bannerType = createEnum("bannerType", com.example.matchdomain.banner.enums.BannerType.class); + + public final StringPath contentsUrl = createString("contentsUrl"); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final com.example.matchdomain.event.entity.QEvent event; + + public final NumberPath eventId = createNumber("eventId", Long.class); + + public final NumberPath id = createNumber("id", Long.class); + + //inherited + public final EnumPath status = _super.status; + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QBanner(String variable) { + this(Banner.class, forVariable(variable), INITS); + } + + public QBanner(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QBanner(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QBanner(PathMetadata metadata, PathInits inits) { + this(Banner.class, metadata, inits); + } + + public QBanner(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.event = inits.isInitialized("event") ? new com.example.matchdomain.event.entity.QEvent(forProperty("event")) : null; + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/common/model/QContentsEntity.java b/Match-Domain/src/main/generated/com/example/matchdomain/common/model/QContentsEntity.java new file mode 100644 index 00000000..e7dab0aa --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/common/model/QContentsEntity.java @@ -0,0 +1,45 @@ +package com.example.matchdomain.common.model; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; + + +/** + * QContentsEntity is a Querydsl query type for ContentsEntity + */ +@Generated("com.querydsl.codegen.DefaultSupertypeSerializer") +public class QContentsEntity extends EntityPathBase { + + private static final long serialVersionUID = 2070380719L; + + public static final QContentsEntity contentsEntity = new QContentsEntity("contentsEntity"); + + public final StringPath contents = createString("contents"); + + public final EnumPath contentsType = createEnum("contentsType", ContentsType.class); + + public final DateTimePath createdAt = createDateTime("createdAt", java.time.LocalDateTime.class); + + public final EnumPath status = createEnum("status", Status.class); + + public final DateTimePath updatedAt = createDateTime("updatedAt", java.time.LocalDateTime.class); + + public QContentsEntity(String variable) { + super(ContentsEntity.class, forVariable(variable)); + } + + public QContentsEntity(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QContentsEntity(PathMetadata metadata) { + super(ContentsEntity.class, metadata); + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java index 42e74dc6..8a3f9d36 100644 --- a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java +++ b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java @@ -43,6 +43,8 @@ public class QDonationHistory extends EntityPathBase { public final NumberPath id = createNumber("id", Long.class); + public final StringPath item = createString("item"); + public final com.example.matchdomain.project.entity.QProject project; public final NumberPath projectId = createNumber("projectId", Long.class); diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java index 4a2d38f9..f3d2462c 100644 --- a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java +++ b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java @@ -31,6 +31,8 @@ public class QDonationUser extends EntityPathBase { public final EnumPath donationStatus = createEnum("donationStatus", com.example.matchdomain.donation.entity.enums.DonationStatus.class); + public final NumberPath executionPrice = createNumber("executionPrice", Long.class); + public final StringPath flameImage = createString("flameImage"); public final EnumPath flameType = createEnum("flameType", com.example.matchdomain.donation.entity.flameEnum.FlameType.class); diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRegularPayment.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRegularPayment.java index 3d8b9874..97e46962 100644 --- a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRegularPayment.java +++ b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRegularPayment.java @@ -41,7 +41,7 @@ public class QRegularPayment extends EntityPathBase { public final EnumPath regularPayStatus = createEnum("regularPayStatus", com.example.matchdomain.donation.entity.enums.RegularPayStatus.class); - public final ListPath requestPaymentHistory = this.createList("requestPaymentHistory", RequestPaymentHistory.class, QRequestPaymentHistory.class, PathInits.DIRECT2); + public final ListPath requestFailedHistories = this.createList("requestFailedHistories", RequestFailedHistory.class, QRequestFailedHistory.class, PathInits.DIRECT2); //inherited public final EnumPath status = _super.status; diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRequestFailedHistory.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRequestFailedHistory.java new file mode 100644 index 00000000..91d964f7 --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRequestFailedHistory.java @@ -0,0 +1,66 @@ +package com.example.matchdomain.donation.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QRequestFailedHistory is a Querydsl query type for RequestFailedHistory + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QRequestFailedHistory extends EntityPathBase { + + private static final long serialVersionUID = -443267059L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QRequestFailedHistory requestFailedHistory = new QRequestFailedHistory("requestFailedHistory"); + + public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath reason = createString("reason"); + + public final QRegularPayment regularPayment; + + public final NumberPath regularPaymentId = createNumber("regularPaymentId", Long.class); + + //inherited + public final EnumPath status = _super.status; + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QRequestFailedHistory(String variable) { + this(RequestFailedHistory.class, forVariable(variable), INITS); + } + + public QRequestFailedHistory(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QRequestFailedHistory(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QRequestFailedHistory(PathMetadata metadata, PathInits inits) { + this(RequestFailedHistory.class, metadata, inits); + } + + public QRequestFailedHistory(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.regularPayment = inits.isInitialized("regularPayment") ? new QRegularPayment(forProperty("regularPayment"), inits.get("regularPayment")) : null; + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRequestPaymentHistory.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRequestPaymentHistory.java deleted file mode 100644 index 0db1393c..00000000 --- a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QRequestPaymentHistory.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.example.matchdomain.donation.entity; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; -import com.querydsl.core.types.dsl.PathInits; - - -/** - * QRequestPaymentHistory is a Querydsl query type for RequestPaymentHistory - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QRequestPaymentHistory extends EntityPathBase { - - private static final long serialVersionUID = 1915992056L; - - private static final PathInits INITS = PathInits.DIRECT2; - - public static final QRequestPaymentHistory requestPaymentHistory = new QRequestPaymentHistory("requestPaymentHistory"); - - public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); - - public final NumberPath amount = createNumber("amount", Long.class); - - //inherited - public final DateTimePath createdAt = _super.createdAt; - - public final NumberPath id = createNumber("id", Long.class); - - public final StringPath orderId = createString("orderId"); - - public final NumberPath payDate = createNumber("payDate", Integer.class); - - public final EnumPath paymentStatus = createEnum("paymentStatus", com.example.matchdomain.donation.entity.enums.PaymentStatus.class); - - public final StringPath reason = createString("reason"); - - public final QRegularPayment regularPayment; - - public final NumberPath regularPaymentId = createNumber("regularPaymentId", Long.class); - - //inherited - public final EnumPath status = _super.status; - - public final StringPath tid = createString("tid"); - - //inherited - public final DateTimePath updatedAt = _super.updatedAt; - - public final com.example.matchdomain.user.entity.QUser user; - - public final QUserCard userCard; - - public final NumberPath userCardId = createNumber("userCardId", Long.class); - - public final NumberPath userId = createNumber("userId", Long.class); - - public QRequestPaymentHistory(String variable) { - this(RequestPaymentHistory.class, forVariable(variable), INITS); - } - - public QRequestPaymentHistory(Path path) { - this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); - } - - public QRequestPaymentHistory(PathMetadata metadata) { - this(metadata, PathInits.getFor(metadata, INITS)); - } - - public QRequestPaymentHistory(PathMetadata metadata, PathInits inits) { - this(RequestPaymentHistory.class, metadata, inits); - } - - public QRequestPaymentHistory(Class type, PathMetadata metadata, PathInits inits) { - super(type, metadata, inits); - this.regularPayment = inits.isInitialized("regularPayment") ? new QRegularPayment(forProperty("regularPayment"), inits.get("regularPayment")) : null; - this.user = inits.isInitialized("user") ? new com.example.matchdomain.user.entity.QUser(forProperty("user")) : null; - this.userCard = inits.isInitialized("userCard") ? new QUserCard(forProperty("userCard"), inits.get("userCard")) : null; - } - -} - diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/event/entity/QEvent.java b/Match-Domain/src/main/generated/com/example/matchdomain/event/entity/QEvent.java new file mode 100644 index 00000000..0939f452 --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/event/entity/QEvent.java @@ -0,0 +1,63 @@ +package com.example.matchdomain.event.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QEvent is a Querydsl query type for Event + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QEvent extends EntityPathBase { + + private static final long serialVersionUID = 1559356633L; + + public static final QEvent event = new QEvent("event"); + + public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final ListPath eventContents = this.createList("eventContents", EventContent.class, QEventContent.class, PathInits.DIRECT2); + + public final DatePath eventEndDate = createDate("eventEndDate", java.time.LocalDate.class); + + public final DatePath eventStartDate = createDate("eventStartDate", java.time.LocalDate.class); + + public final EnumPath eventType = createEnum("eventType", com.example.matchdomain.event.enums.EventType.class); + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath smallTitle = createString("smallTitle"); + + //inherited + public final EnumPath status = _super.status; + + public final StringPath thumbnail = createString("thumbnail"); + + public final StringPath title = createString("title"); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QEvent(String variable) { + super(Event.class, forVariable(variable)); + } + + public QEvent(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QEvent(PathMetadata metadata) { + super(Event.class, metadata); + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/event/entity/QEventContent.java b/Match-Domain/src/main/generated/com/example/matchdomain/event/entity/QEventContent.java new file mode 100644 index 00000000..9c41e272 --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/event/entity/QEventContent.java @@ -0,0 +1,70 @@ +package com.example.matchdomain.event.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QEventContent is a Querydsl query type for EventContent + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QEventContent extends EntityPathBase { + + private static final long serialVersionUID = -1749782176L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QEventContent eventContent = new QEventContent("eventContent"); + + public final com.example.matchdomain.common.model.QContentsEntity _super = new com.example.matchdomain.common.model.QContentsEntity(this); + + //inherited + public final StringPath contents = _super.contents; + + //inherited + public final EnumPath contentsType = _super.contentsType; + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final QEvent event; + + public final NumberPath eventId = createNumber("eventId", Long.class); + + public final NumberPath id = createNumber("id", Long.class); + + //inherited + public final EnumPath status = _super.status; + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QEventContent(String variable) { + this(EventContent.class, forVariable(variable), INITS); + } + + public QEventContent(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QEventContent(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QEventContent(PathMetadata metadata, PathInits inits) { + this(EventContent.class, metadata, inits); + } + + public QEventContent(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.event = inits.isInitialized("event") ? new QEvent(forProperty("event")) : null; + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/keyword/entity/QSearchKeyword.java b/Match-Domain/src/main/generated/com/example/matchdomain/keyword/entity/QSearchKeyword.java new file mode 100644 index 00000000..6b8345fa --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/keyword/entity/QSearchKeyword.java @@ -0,0 +1,52 @@ +package com.example.matchdomain.keyword.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; + + +/** + * QSearchKeyword is a Querydsl query type for SearchKeyword + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QSearchKeyword extends EntityPathBase { + + private static final long serialVersionUID = -2088356623L; + + public static final QSearchKeyword searchKeyword = new QSearchKeyword("searchKeyword"); + + public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath keyword = createString("keyword"); + + public final NumberPath priority = createNumber("priority", Integer.class); + + //inherited + public final EnumPath status = _super.status; + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QSearchKeyword(String variable) { + super(SearchKeyword.class, forVariable(variable)); + } + + public QSearchKeyword(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QSearchKeyword(PathMetadata metadata) { + super(SearchKeyword.class, metadata); + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/notice/entity/QNotice.java b/Match-Domain/src/main/generated/com/example/matchdomain/notice/entity/QNotice.java new file mode 100644 index 00000000..77bbca2d --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/notice/entity/QNotice.java @@ -0,0 +1,55 @@ +package com.example.matchdomain.notice.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QNotice is a Querydsl query type for Notice + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QNotice extends EntityPathBase { + + private static final long serialVersionUID = 105098915L; + + public static final QNotice notice = new QNotice("notice"); + + public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final ListPath noticeContents = this.createList("noticeContents", NoticeContent.class, QNoticeContent.class, PathInits.DIRECT2); + + public final EnumPath noticeType = createEnum("noticeType", com.example.matchdomain.notice.enums.NoticeType.class); + + //inherited + public final EnumPath status = _super.status; + + public final StringPath title = createString("title"); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QNotice(String variable) { + super(Notice.class, forVariable(variable)); + } + + public QNotice(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QNotice(PathMetadata metadata) { + super(Notice.class, metadata); + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/notice/entity/QNoticeContent.java b/Match-Domain/src/main/generated/com/example/matchdomain/notice/entity/QNoticeContent.java new file mode 100644 index 00000000..cbecfbf1 --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/notice/entity/QNoticeContent.java @@ -0,0 +1,70 @@ +package com.example.matchdomain.notice.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QNoticeContent is a Querydsl query type for NoticeContent + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QNoticeContent extends EntityPathBase { + + private static final long serialVersionUID = -986976170L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QNoticeContent noticeContent = new QNoticeContent("noticeContent"); + + public final com.example.matchdomain.common.model.QContentsEntity _super = new com.example.matchdomain.common.model.QContentsEntity(this); + + //inherited + public final StringPath contents = _super.contents; + + //inherited + public final EnumPath contentsType = _super.contentsType; + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final QNotice notice; + + public final NumberPath noticeId = createNumber("noticeId", Long.class); + + //inherited + public final EnumPath status = _super.status; + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QNoticeContent(String variable) { + this(NoticeContent.class, forVariable(variable), INITS); + } + + public QNoticeContent(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QNoticeContent(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QNoticeContent(PathMetadata metadata, PathInits inits) { + this(NoticeContent.class, metadata, inits); + } + + public QNoticeContent(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.notice = inits.isInitialized("notice") ? new QNotice(forProperty("notice")) : null; + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/notification/entity/QNotification.java b/Match-Domain/src/main/generated/com/example/matchdomain/notification/entity/QNotification.java new file mode 100644 index 00000000..befb67d2 --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/notification/entity/QNotification.java @@ -0,0 +1,82 @@ +package com.example.matchdomain.notification.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QNotification is a Querydsl query type for Notification + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QNotification extends EntityPathBase { + + private static final long serialVersionUID = 1041968777L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QNotification notification = new QNotification("notification"); + + public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + + public final StringPath body = createString("body"); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final com.example.matchdomain.donation.entity.QDonationUser donationUser; + + public final NumberPath donationUserId = createNumber("donationUserId", Long.class); + + public final com.example.matchdomain.event.entity.QEvent event; + + public final NumberPath eventId = createNumber("eventId", Long.class); + + public final NumberPath id = createNumber("id", Long.class); + + public final BooleanPath isRead = createBoolean("isRead"); + + public final EnumPath notificationType = createEnum("notificationType", com.example.matchdomain.notification.enums.NotificationType.class); + + //inherited + public final EnumPath status = _super.status; + + public final StringPath title = createString("title"); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public final com.example.matchdomain.user.entity.QUser user; + + public final NumberPath userId = createNumber("userId", Long.class); + + public QNotification(String variable) { + this(Notification.class, forVariable(variable), INITS); + } + + public QNotification(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QNotification(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QNotification(PathMetadata metadata, PathInits inits) { + this(Notification.class, metadata, inits); + } + + public QNotification(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.donationUser = inits.isInitialized("donationUser") ? new com.example.matchdomain.donation.entity.QDonationUser(forProperty("donationUser"), inits.get("donationUser")) : null; + this.event = inits.isInitialized("event") ? new com.example.matchdomain.event.entity.QEvent(forProperty("event")) : null; + this.user = inits.isInitialized("user") ? new com.example.matchdomain.user.entity.QUser(forProperty("user")) : null; + } + +} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/review/entity/QReview.java b/Match-Domain/src/main/generated/com/example/matchdomain/review/entity/QReview.java new file mode 100644 index 00000000..feaef80c --- /dev/null +++ b/Match-Domain/src/main/generated/com/example/matchdomain/review/entity/QReview.java @@ -0,0 +1,72 @@ +package com.example.matchdomain.review.entity; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QReview is a Querydsl query type for Review + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QReview extends EntityPathBase { + + private static final long serialVersionUID = 1075810211L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QReview review = new QReview("review"); + + public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + + public final StringPath comment = createString("comment"); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath donation = createNumber("donation", Integer.class); + + public final NumberPath donationId = createNumber("donationId", Long.class); + + public final com.example.matchdomain.donation.entity.QDonationUser donationUser; + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath information = createNumber("information", Integer.class); + + //inherited + public final EnumPath status = _super.status; + + public final NumberPath transparency = createNumber("transparency", Integer.class); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QReview(String variable) { + this(Review.class, forVariable(variable), INITS); + } + + public QReview(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QReview(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QReview(PathMetadata metadata, PathInits inits) { + this(Review.class, metadata, inits); + } + + public QReview(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.donationUser = inits.isInitialized("donationUser") ? new com.example.matchdomain.donation.entity.QDonationUser(forProperty("donationUser"), inits.get("donationUser")) : null; + } + +} + diff --git a/Match-Domain/src/main/java/com/example/matchdomain/common/model/BaseEntity.java b/Match-Domain/src/main/java/com/example/matchdomain/common/model/BaseEntity.java index 590a7a75..f7984687 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/common/model/BaseEntity.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/common/model/BaseEntity.java @@ -1,6 +1,7 @@ package com.example.matchdomain.common.model; import lombok.Getter; +import org.hibernate.annotations.ColumnDefault; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -21,6 +22,7 @@ public abstract class BaseEntity { private LocalDateTime updatedAt; @Enumerated(EnumType.STRING) + @ColumnDefault(value = "ACTIVE") private Status status = Status.ACTIVE; public void setStatus(Status status) { diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationAdaptor.java index e72d6d03..1b92e024 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationAdaptor.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationAdaptor.java @@ -3,9 +3,12 @@ import com.example.matchcommon.annotation.Adaptor; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.exception.NotFoundException; +import com.example.matchdomain.donation.dto.DonationExecutionDto; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.DonationStatus; +import com.example.matchdomain.donation.repository.DonationCustomRepository; import com.example.matchdomain.donation.repository.DonationUserRepository; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.user.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -15,7 +18,7 @@ import java.util.List; import static com.example.matchdomain.common.model.Status.ACTIVE; -import static com.example.matchdomain.donation.entity.enums.DonationStatus.EXECUTION_REFUND; +import static com.example.matchdomain.donation.entity.enums.DonationStatus.*; import static com.example.matchdomain.donation.exception.DonationListErrorCode.FILTER_NOT_EXIST; import static com.example.matchdomain.donation.exception.DonationRefundErrorCode.DONATION_NOT_EXIST; import static com.example.matchdomain.project.entity.enums.ImageRepresentStatus.REPRESENT; @@ -86,4 +89,46 @@ public Page findByUser(User user, int page, int size) { public List findDonationListsByUser(User user) { return donationUserRepository.findByUser(user); } + + public DonationUser save(DonationUser donationUser) { + return donationUserRepository.save(donationUser); + } + + public List findByDonationNotRefund() { + return donationUserRepository.findByDonationStatusNot(EXECUTION_REFUND); + } + + public List findByListIn(List donationUserLists) { + return donationUserRepository.findByIdIn(donationUserLists); + } + + public void saveAll(List donationUsers) { + donationUserRepository.saveAll(donationUsers); + } + + public boolean existsByImpId(String impUid) { + return donationUserRepository.existsByTid(impUid); + } + + public List checkPopUp(User user) { + return donationUserRepository.checkPopUp(user); + } + + public List findByProject(Project project) { + return donationUserRepository.findAllDtoByProjectId(project.getId()); + } + + public Page findByUserForAdminPage(User user, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + return donationUserRepository.findByUserOrderByIdAsc(user, pageable); + } + + public Page findDonationLists(Long projectId, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + List in = List.of(new DonationStatus[]{EXECUTION_BEFORE, PARTIAL_EXECUTION}); + + return donationUserRepository.findByProjectIdAndDonationStatusInOrderByCreatedAtAsc(projectId, in, pageable); + + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationHistoryAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationHistoryAdaptor.java index ab5db77a..84b90065 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationHistoryAdaptor.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationHistoryAdaptor.java @@ -32,7 +32,7 @@ public Page findDonationHistory(DonationUser donationUser, Long return donationHistoryRepository.getDonationHistoryCustom(donationUser.getRegularPaymentId(), donationId, CREATE, pageable, donationUser.getProjectId()); } - public void saveDonationHistory(DonationHistory donationHistory) { - donationHistoryRepository.save(donationHistory); + public DonationHistory saveDonationHistory(DonationHistory donationHistory) { + return donationHistoryRepository.save(donationHistory); } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RegularPaymentAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RegularPaymentAdaptor.java index ab5829ea..87cadee5 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RegularPaymentAdaptor.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RegularPaymentAdaptor.java @@ -5,6 +5,7 @@ import com.example.matchdomain.common.model.Status; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.RegularPayment; +import com.example.matchdomain.donation.entity.enums.RegularPayStatus; import com.example.matchdomain.donation.repository.RegularPaymentRepository; import com.example.matchdomain.user.entity.User; import lombok.RequiredArgsConstructor; @@ -55,4 +56,16 @@ public List findByIdList(Long regularPayId, User user){ public List findByCardId(Long cardId) { return regularPaymentRepository.findByUserCardIdAndRegularPayStatus(cardId, PROCEEDING); } + + public List findByLastDayRegularPayment(int currentDay) { + return regularPaymentRepository.findByPayDateGreaterThanEqualAndStatusAndRegularPayStatus(currentDay, Status.ACTIVE, RegularPayStatus.PROCEEDING); + } + + public List findByDate(int currentDay) { + return regularPaymentRepository.findByPayDateAndStatusAndRegularPayStatus(currentDay, Status.ACTIVE, RegularPayStatus.PROCEEDING); + } + + public void saveAll(List regularPayments) { + regularPaymentRepository.saveAll(regularPayments); + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RequestFailedHistoryAdapter.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RequestFailedHistoryAdapter.java new file mode 100644 index 00000000..bd92e02e --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RequestFailedHistoryAdapter.java @@ -0,0 +1,28 @@ +package com.example.matchdomain.donation.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.RequestFailedHistory; +import com.example.matchdomain.donation.repository.RequestFailedHistoryRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Adaptor +@RequiredArgsConstructor +public class RequestFailedHistoryAdapter { + private final RequestFailedHistoryRepository requestFailedHistoryRepository; + + public List findFailHistory() { + return requestFailedHistoryRepository.findDistinctByStatus(Status.ACTIVE); + } + + public void save(RequestFailedHistory requestFailedHistory) { + requestFailedHistoryRepository.save(requestFailedHistory); + } + + public void deleteByRegularPaymentId(Long regularPaymentId) { + requestFailedHistoryRepository.deleteByRegularPaymentId(regularPaymentId); + + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/dto/DonationExecutionDto.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/dto/DonationExecutionDto.java new file mode 100644 index 00000000..812d8cab --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/dto/DonationExecutionDto.java @@ -0,0 +1,24 @@ +package com.example.matchdomain.donation.dto; + +import com.example.matchdomain.donation.entity.enums.DonationStatus; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class DonationExecutionDto { + private DonationStatus donationStatus; + + private Long price; + + private Long executionPrice; + + public DonationExecutionDto(DonationStatus donationStatus, Long price, Long executionPrice) { + this.donationStatus = donationStatus; + this.price = price; + this.executionPrice = executionPrice; + } + +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java index fbd6380b..bf571737 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java @@ -54,6 +54,8 @@ public class DonationHistory extends BaseEntity { @Column(name="projectId") private Long projectId; + private String item; + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "donationHistoryId") private List historyImages = new ArrayList<>(); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java index 96194a7b..879089b8 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java @@ -6,9 +6,11 @@ import com.example.matchdomain.donation.entity.enums.RegularStatus; import com.example.matchdomain.donation.entity.flameEnum.FlameType; import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.review.entity.Review; import com.example.matchdomain.user.entity.User; import lombok.*; import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @@ -25,7 +27,7 @@ @NoArgsConstructor @DynamicUpdate @DynamicInsert -@BatchSize(size = 1000) +@BatchSize(size = 100) public class DonationUser extends BaseEntity { @Id @Column(name = "id") @@ -64,6 +66,7 @@ public class DonationUser extends BaseEntity { private String inherenceName; @Enumerated(EnumType.STRING) + @Comment("donation Status ") private DonationStatus donationStatus; @Enumerated(EnumType.STRING) @@ -82,6 +85,9 @@ public class DonationUser extends BaseEntity { @BatchSize(size = 100) private List donationHistories = new ArrayList<>(); + @Comment("집행 금액, DonationStatus SomeExecution 인 경우와 Execution Success 인 경우") + private Long executionPrice; + public void updateInherenceNumber(String inherenceNumber, String flameName) { this.inherenceNumber = inherenceNumber; this.inherenceName = flameName; @@ -90,4 +96,9 @@ public void updateInherenceNumber(String inherenceNumber, String flameName) { public void updateDonationStatus(DonationStatus donationStatus) { this.donationStatus = donationStatus; } + + public void updateDonationExecution(DonationStatus donationStatus, Long executionPrice) { + this.donationStatus = donationStatus; + this.executionPrice = executionPrice; + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RegularPayment.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RegularPayment.java index 409f79cd..0f692003 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RegularPayment.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RegularPayment.java @@ -1,6 +1,7 @@ package com.example.matchdomain.donation.entity; import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.common.model.Status; import com.example.matchdomain.donation.entity.enums.RegularPayStatus; import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.user.entity.User; @@ -24,6 +25,7 @@ @NoArgsConstructor @DynamicUpdate @DynamicInsert +@BatchSize(size = 100) public class RegularPayment extends BaseEntity { @Id @Column(name = "id") @@ -61,11 +63,12 @@ public class RegularPayment extends BaseEntity { @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) @JoinColumn(name = "regularPaymentId") @BatchSize(size = 100) - private List requestPaymentHistory = new ArrayList<>(); + private List requestFailedHistories = new ArrayList<>(); @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) @JoinColumn(name = "regularPaymentId") @BatchSize(size = 100) private List donationUser = new ArrayList<>(); + } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RequestPaymentHistory.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RequestFailedHistory.java similarity index 50% rename from Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RequestPaymentHistory.java rename to Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RequestFailedHistory.java index 5cbe9e0a..ca7bb37b 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RequestPaymentHistory.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/RequestFailedHistory.java @@ -4,13 +4,14 @@ import com.example.matchdomain.donation.entity.enums.PaymentStatus; import com.example.matchdomain.user.entity.User; import lombok.*; +import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import javax.persistence.*; @Entity -@Table(name = "RequestPaymentHistory") +@Table(name = "RequestFailedHistory") @Getter @Setter @Builder @@ -18,43 +19,19 @@ @NoArgsConstructor @DynamicUpdate @DynamicInsert -//정기 결제 시도 내역 과 성공, 실패, 취소 저장 -> 실패 시 계속 시도해야 하므로 -public class RequestPaymentHistory extends BaseEntity { +@BatchSize(size = 100) +//정기 결제 시도 내역 과 실패 저장 -> 실패 시 계속 시도해야 하므로 +public class RequestFailedHistory extends BaseEntity { @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "userId",nullable = false, insertable=false, updatable=false) - private User user; - - @Column(name="userId") - private Long userId; - - private String tid; - - private String orderId; - - private Long amount; - - private String reason; - - private int payDate; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "regularPaymentId",nullable = false, insertable=false, updatable=false) private RegularPayment regularPayment; private Long regularPaymentId; - @Enumerated(EnumType.STRING) - private PaymentStatus paymentStatus; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "userCardId",nullable = false, insertable=false, updatable=false) - private UserCard userCard; - - @Column(name="userCardId") - private Long userCardId; + private String reason; } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/DonationStatus.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/DonationStatus.java index 0fae696c..7d13df3c 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/DonationStatus.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/DonationStatus.java @@ -8,6 +8,7 @@ public enum DonationStatus { EXECUTION_BEFORE("EXECUTION_BEFORE","집행 전"), EXECUTION_UNDER("EXECUTION_UNDER","집행 중"), + PARTIAL_EXECUTION("PARTIAL_EXECUTION","일부 집행"), EXECUTION_SUCCESS("EXECUTION_SUCCESS","집행 완료"), EXECUTION_REFUND("EXECUTION_REFUND","기부금 환불"); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/Execution.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/Execution.java new file mode 100644 index 00000000..b3e32b95 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/Execution.java @@ -0,0 +1,10 @@ +package com.example.matchdomain.donation.entity.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Execution { + ALL, SOME +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/PayMethod.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/PayMethod.java index 29df4c80..7df91e20 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/PayMethod.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/PayMethod.java @@ -7,12 +7,14 @@ @AllArgsConstructor public enum PayMethod { CARD("CARD","신용카드"), + POINT("point", "카카오페이 포인트"), VBANK("VBANK","카드"), NAVER_PAY("naverpay","네이버 페이"), KAKAO_PAY("kakaopay","카카오 페이"), PAYCO("payco","페이코"), SSGPAY("ssgpay","쓱페이"), - SAMSUNG_PAY("samsungpay","삼성페이"); + SAMSUNG_PAY("samsungpay","삼성페이"), + TUTORIAL("TUTORIAL","튜토리얼"); private final String value; private final String name; diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/AdjectiveFlame.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/AdjectiveFlame.java index 114ddf51..dc83909a 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/AdjectiveFlame.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/AdjectiveFlame.java @@ -6,80 +6,94 @@ @Getter @AllArgsConstructor public enum AdjectiveFlame { - BIG_FLAME("큰 불꽃을 입은"), - SMALL_FLAME("작은 불꽃을 입은"), - FAST_FLAME("빠른 불꽃을 입은"), - SLOW_FLAME("느린 불꽃을 입은"), - HIGH_FLAME("높은 불꽃을 입은"), - LOW_FLAME("낮은 불꽃을 입은"), - DARK_FLAME("어두운 불꽃을 입은"), - BRIGHT_FLAME("밝은 불꽃을 입은"), - MANY_FLAMES("많은 불꽃을 입은"), - FEW_FLAMES("적은 불꽃을 입은"), - GOOD_FLAME("좋은 불꽃을 입은"), - BAD_FLAME("나쁜 불꽃을 입은"), - PRETTY_FLAME("예쁜 불꽃을 입은"), - UGLY_FLAME("추운 불꽃을 입은"), - HOT_FLAME("더운 불꽃을 입은"), - CUTE_FLAME("깜찍한 불꽃을 입은"), - COOL_FLAME("멋진 불꽃을 입은"), - SEXY_FLAME("섹시한 불꽃을 입은"), - VARIOUS_FLAMES("다양한 불꽃을 입은"), - UNIQUE_FLAME("유니크한 불꽃을 입은"), - SPECIAL_FLAME("특별한 불꽃을 입은"), - ORDINARY_FLAME("평범한 불꽃을 입은"), - MYSTERIOUS_FLAME("신비로운 불꽃을 입은"), - ELEGANTED_FLAME("우아한 불꽃을 입은"), - SOFT_FLAME("부드러운 불꽃을 입은"), - ROUGH_FLAME("거친 불꽃을 입은"), - LOVELY_FLAME("사랑스러운 불꽃을 입은"), - CHARMING_FLAME("매력적인 불꽃을 입은"), - ELEGANT_FLAME("고급스러운 불꽃을 입은"), - MAGICAL_FLAME("마법적인 불꽃을 입은"), - POWERFUL_FLAME("강력한 불꽃을 입은"), - BRILLIANT_FLAME("찬란한 불꽃을 입은"), - FANTASTIC_FLAME("환상적인 불꽃을 입은"), - MYSTICAL_FLAME("신비한 불꽃을 입은"), - PEACEFUL_FLAME("평화로운 불꽃을 입은"), - ENERGETIC_FLAME("활기찬 불꽃을 입은"), - DAZZLING_FLAME("눈부신 불꽃을 입은"), - COLORFUL_FLAME("색상이 풍부한 불꽃을 입은"), - GENTLE_FLAME("온화한 불꽃을 입은"), - WILD_FLAME("야생적인 불꽃을 입은"), - ADVENTUROUS_FLAME("모험적인 불꽃을 입은"), - CHILL_FLAME("차가운 불꽃을 입은"), - CRISP_FLAME("시원한 불꽃을 입은"), - FRESH_FLAME("신선한 불꽃을 입은"), - FLOWING_FLAME("흐르는 불꽃을 입은"), - SPARKLING_FLAME("반짝이는 불꽃을 입은"), - FLICKERING_FLAME("희미하게 번뜩이는 불꽃을 입은"), - GLITTERING_FLAME("빛나는 불꽃을 입은"), - RADIANT_FLAME("빛나는 불꽃을 입은"), - SHIMMERING_FLAME("희미하게 빛나는 불꽃을 입은"), - SHINING_FLAME("빛나는 불꽃을 입은"), - GLOWING_FLAME("빛나는 불꽃을 입은"), - FLYING_FLAME("날아가는 불꽃을 입은"), - DANCING_FLAME("춤추는 불꽃을 입은"), - WHISPERING_FLAME("속삭이는 불꽃을 입은"), - SINGING_FLAME("노래하는 불꽃을 입은"), - LAUGHING_FLAME("웃는 불꽃을 입은"), - CRYING_FLAME("울고 있는 불꽃을 입은"), - JUMPING_FLAME("뛰어오르는 불꽃을 입은"), - FADING_FLAME("사라지는 불꽃을 입은"), - BURNING_FLAME("불타는 불꽃을 입은"), - EXPLODING_FLAME("터지는 불꽃을 입은"), - CRACKLING_FLAME("말랑말랑 불꽃을 입은"), - POPPING_FLAME("팝팝 불꽃을 입은"), - ZIPPING_FLAME("징징 불꽃을 입은"), - ZIGZAG_FLAME("지그재그 불꽃을 입은"), - SHOWERING_FLAME("소나기 불꽃을 입은"), - DRIZZLING_FLAME("이슬비 불꽃을 입은"), - DRENCHING_FLAME("비오는 불꽃을 입은"), - SPLENDID_FLAME("화려한 불꽃을 입은"), - GORGEOUS_FLAME("화려한 불꽃을 입은"), - GRACEFUL_FLAME("우아한 불꽃을 입은"), - FANCY_FLAME("화려한 불꽃을 입은"), - GLAMOROUS_FLAME("매혹적인 불꽃을 입은"); + SMALL("작은"), + BRIGHT("누구보다 밝은"), + MANY("잠이 많은"), + FEW("걱정이 적은"), + GOOD("기분이 좋은"), + PRETTY("폰 케이스가 예쁜"), + UGLY("추운 날을 좋아하는"), + HOT("누구보다 따뜻한"), + CUTE("귀여운"), + COOL("멋진 신발을 가진"), + VARIOUS("간식을 많이 가진"), + UNIQUE("유니크한"), + SPECIAL("특별한 잠버릇이 있는"), + ORDINARY("평범한날이 좋은"), + MYSTERIOUS("신비로운"), + ELEGANTED("우아한 노래를 듣는"), + SOFT("부드러운 빵이 좋은"), + LOVELY("사랑스러운"), + CHARMING("매력적인 이마를 가진"), + ELEGANT("고급스러운 텀블러를 가진"), + MAGICAL("마법같이 나타난"), + POWERFUL("강력한 화력의"), + BRILLIANT("맑은 날을 좋아하는"), + FANTASTIC("환상적인 식사를 한"), + MYSTICAL("신기한 나라 물약을 먹고 커진"), + PEACEFUL("평화로운 하루를 보내는"), + ENERGETIC("활기찬 불꽃을 가진"), + DAZZLING("방금 일어나 눈이 부신"), + COLORFUL("색깔이 풍부한"), + GENTLE("온화한 입은"), + WILD("야생 다큐를 좋아하는"), + ADVENTUROUS("모험적인"), + CHILL("시크한"), + CRISP("의외로 시원한"), + FRESH("신선한 야채를 키우는"), + FLOWING("미끄럼틀 타는"), + SPARKLING("반짝이는"), + GLITTERING("빛이 나는"), + FLYING("날아다니는"), + DANCING("요즘 춤을 배우는"), + WHISPERING("나무에게 속삭이는"), + SINGING("노래하는"), + LAUGHING("강아지만 보면 웃는"), + JUMPING("점프가 취미인"), + FADING("바람과 함께 사라지는"), + BURNING("불타는 열정의"), + EXPLODING("부끄러움이 많은"), + CRACKLING("말랑말랑"), + POPPING("팝핀"), + ZIGZAG("지그재그로 걷는"), + SHOWERING("소나기 소리가 좋은"), + DRIZZLING("이슬비가 좋은"), + DRENCHING("비오는 소리를 듣는"), + SPLENDID("화려한 조명이 감싸는"), + GORGEOUS("화려한 옷을 살 예정인"), + GRACEFUL("우아한"), + FANCY("화려한 케이크를 받은"), + GLAMOROUS("매력적인 장바구니를 가진"), + BIG("의외로 큰"), + LITTLE_SMALL("의외로 작은"), + FAST("누구보다 빠른"), + SLOW("잠 드는게 조금 느린"), + HIGH("시력이 높은"), + LOW("앉은 키가 낮은"), + DARK("다크 커피를 좋아하는"), + BRIGHTNESS("밝은"), + FEW_FLAME("적은 장작으로도 잘 타는"), + GOOD_NIGHT("좋은 잠을 잔"), + NEW("새로운 전세집을 찾는"), + OLD("오래된 식탁이 좋은"), + EXPENSIVE("비싼 A4 용지를 먹은"), + HEAVY("무거운 가방도 잘드는"), + LIGHT("가벼운 핸드폰이 좋은"), + NICE("근사한 식사를 한"), + COLD("추워도 아아 먹는"), + HOT_AMERICANO("더운날 뜨아 먹는"), + BUSY("바쁜척 하고 싶은"), + QUIET("한적한 시골이 좋은"), + TALL("높은 발화점을 가진"), + SHORT("낮은 발화점을 가진"), + STRONG("강한 멘탈을 가진"), + HAPPY("행복한 내일을 기다리는"), + SAD("슬픈 눈의"), + SLEEPY("졸린 눈의"), + FUNNY("재미있는 영화를 본"), + PRETTY_BALL_PEN("예쁜 볼펜을 모으는"), + CUTE_VIDEO("귀여운 영상을 모으는"), + GLAD("기쁜 소식을 가져오는"); private final String value; } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/exception/CheckExecutionCode.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/exception/CheckExecutionCode.java new file mode 100644 index 00000000..93adc404 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/exception/CheckExecutionCode.java @@ -0,0 +1,42 @@ +package com.example.matchdomain.donation.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@Getter +@AllArgsConstructor +public enum CheckExecutionCode implements BaseErrorCode { + @ExplainError("리뷰 작성할게 없을시 뜨는 에러코드.") + NOT_EXISTS_DONATION(NOT_FOUND,"CHECK_REVIEW_POPUP", "작성할 리뷰가 존재하지 않습니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.builder().message(message).code(code).isSuccess(false).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} \ No newline at end of file diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepository.java index b6618a4f..0c8ee53c 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepository.java @@ -1,10 +1,16 @@ package com.example.matchdomain.donation.repository; import com.example.matchdomain.donation.entity.DonationHistory; +import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import com.example.matchdomain.user.entity.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.util.List; + public interface DonationCustomRepository { Page getDonationHistoryCustom(Long regularPaymentId, Long donationId, HistoryStatus historyStatus, Pageable pageable, Long projectId); + + List checkPopUp(User user); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java index d35adaeb..baf4c710 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java @@ -1,12 +1,16 @@ package com.example.matchdomain.donation.repository; import com.example.matchdomain.donation.entity.*; +import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import com.example.matchdomain.review.entity.QReview; +import com.example.matchdomain.user.entity.User; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.support.PageableExecutionUtils; import java.util.List; @@ -14,6 +18,7 @@ import static com.example.matchdomain.donation.entity.enums.HistoryStatus.COMPLETE; @RequiredArgsConstructor +@EnableJpaRepositories public class DonationCustomRepositoryImpl implements DonationCustomRepository{ private final JPAQueryFactory queryFactory; @Override @@ -68,4 +73,20 @@ public Page getDonationHistoryCustom(Long regularPaymentId, Lon .orderBy(donationHistory.createdAt.asc()); return PageableExecutionUtils.getPage(donationHistories, pageable, countQuery::fetchCount); } + + @Override + public List checkPopUp(User user) { + + QDonationUser qDonationUser = QDonationUser.donationUser; + QReview qReview = QReview.review; + + return queryFactory + .select(qDonationUser) + .from(qDonationUser) + .leftJoin(qReview).on(qDonationUser.id.eq(qReview.donationUser.id)) + .where(qReview.isNull().and(qDonationUser.userId.eq(user.getId())).and(qDonationUser.donationStatus.eq(DonationStatus.EXECUTION_SUCCESS).or(qDonationUser.donationStatus.eq(DonationStatus.PARTIAL_EXECUTION)))) + .orderBy(qDonationUser.createdAt.desc()) + .limit(1) + .fetch(); + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java index 727f02f5..0da83b60 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java @@ -2,6 +2,7 @@ import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.dto.DonationExecutionDto; import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.project.entity.enums.ImageRepresentStatus; @@ -14,54 +15,11 @@ import java.util.List; -public interface DonationUserRepository extends JpaRepository { - - boolean existsByInherenceName(String randomName); - - Page findByUserId(Long userId, Pageable pageable); - - - - - List findByUserAndDonationStatusNot(User user, DonationStatus donationStatus); - - - Page findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceAsc(Long id, DonationStatus donationStatus, String content, String s, String content1, Status active, Pageable pageable); - - Page findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceDesc(Long id, DonationStatus donationStatus, String content, String s, String content1, Status active, Pageable pageable); - - Page findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtAsc(Long id, DonationStatus donationStatus, String content, String s, String content1, Status active, Pageable pageable); - - Page findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtDesc(Long id, DonationStatus donationStatus, String content, String s, String content1, Status active, Pageable pageable); - - +public interface DonationUserRepository extends JpaRepository, DonationCustomRepository { List findByUserAndDonationStatusNotAndStatus(User user, DonationStatus donationStatus, Status status); Page findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(Long userId, DonationStatus donationStatus,Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusNotAndStatusOrderByCreatedAtAsc(Long id, DonationStatus donationStatus, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusNotAndStatusOrderByCreatedAtDesc(Long id, DonationStatus donationStatus, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusNotAndStatusOrderByPriceDesc(Long id, DonationStatus donationStatus, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusNotAndStatusOrderByPriceAsc(Long id, DonationStatus donationStatus, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusAndStatusOrderByCreatedAtAsc(Long id, DonationStatus donationStatus, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusAndStatusOrderByPriceDesc(Long id, DonationStatus donationStatus, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusAndStatusOrderByPriceAsc(Long id, DonationStatus donationStatus, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtAsc(Long id, DonationStatus donationStatus, String content, String content1, String content2, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtDesc(Long id, DonationStatus donationStatus, String content, String content1, String content2, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceDesc(Long id, DonationStatus donationStatus, String content, String content1, String content2, Status status, Pageable pageable); - - Page findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceAsc(Long id, DonationStatus donationStatus, String content, String content1, String content2, Status status, Pageable pageable); - Page findByUserIdAndStatusAndDonationStatusNotOrderByCreatedAtDesc(Long userId, Status status, DonationStatus donationStatus, Pageable pageable); @Query(value = "SELECT DU FROM DonationUser DU JOIN FETCH DU.user " + @@ -117,6 +75,19 @@ public interface DonationUserRepository extends JpaRepository List findByUser(User user); + boolean existsByTid(String impUid); + + @Query("SELECT new com.example.matchdomain.donation.dto.DonationExecutionDto(DU.donationStatus, DU.price, DU.executionPrice) " + + "FROM DonationUser DU WHERE DU.projectId = :projectId AND DU.donationStatus != 'EXECUTION_REFUND'") + List findAllDtoByProjectId(@Param("projectId") Long projectId); + + Page findByUserOrderByIdAsc(User user, Pageable pageable); + + @Query(value = "SELECT DU FROM DonationUser DU JOIN FETCH DU.user " + + "WHERE DU.projectId = :projectId AND DU.donationStatus IN :statuses ORDER BY DU.createdAt ASC", + countQuery = "SELECT count(DU) FROM DonationUser DU WHERE DU.projectId = :projectId AND DU.donationStatus IN :statuses") + Page findByProjectIdAndDonationStatusInOrderByCreatedAtAsc(@Param("projectId") Long projectId, @Param("statuses") List donationStatuses, Pageable pageable); + interface flameList { Long getRegularPayId(); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java index 5f42b8d0..81fb3a16 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java @@ -46,6 +46,8 @@ Page getBurningFlameListCustom(@Param("userId") Long userId List findByUserCardIdAndRegularPayStatus(Long cardId, RegularPayStatus regularPayStatus); + List findByPayDateAndStatusAndRegularPayStatus(int currentDay, Status status, RegularPayStatus regularPayStatus); + interface RegularPaymentFlame { Long getRegularPayId(); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RequestFailedHistoryRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RequestFailedHistoryRepository.java new file mode 100644 index 00000000..d3614532 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RequestFailedHistoryRepository.java @@ -0,0 +1,17 @@ +package com.example.matchdomain.donation.repository; + +import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.RequestFailedHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface RequestFailedHistoryRepository extends JpaRepository { + + @Query("SELECT DISTINCT rf FROM RequestFailedHistory rf WHERE rf.status = :status GROUP BY rf.regularPaymentId") + List findDistinctByStatus(@Param("status") Status status); + + void deleteByRegularPaymentId(Long regularPaymentId); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RequestPaymentHistoryRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RequestPaymentHistoryRepository.java deleted file mode 100644 index 4a2bb483..00000000 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RequestPaymentHistoryRepository.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.example.matchdomain.donation.repository; - -import com.example.matchdomain.common.model.Status; -import com.example.matchdomain.donation.entity.enums.PaymentStatus; -import com.example.matchdomain.donation.entity.RequestPaymentHistory; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -import java.util.List; - -public interface RequestPaymentHistoryRepository extends JpaRepository { - @Query("SELECT rph FROM RequestPaymentHistory rph " + - "left join fetch RegularPayment rp on rph.regularPaymentId = rp.id " + - "left join fetch UserCard uc on uc.id = rp.userCardId " + - "left join fetch User u on rph.userId = u.id " + - "where rph.paymentStatus=:paymentStatus and rph.status = :status") - List findByPaymentStatusAndStatus(@Param("paymentStatus") PaymentStatus paymentStatus,@Param("status") Status status); -} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/UserCardRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/UserCardRepository.java index 7f4bcbe9..9374d187 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/UserCardRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/UserCardRepository.java @@ -16,4 +16,6 @@ public interface UserCardRepository extends JpaRepository { Optional findByIdAndStatus(Long cardId, Status status); List findByUserIdAndStatus(Long userId, Status status); + + void deleteByUserId(Long userId); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notification/adaptor/NotificationAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/notification/adaptor/NotificationAdaptor.java index c614a019..9101ae6b 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/notification/adaptor/NotificationAdaptor.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/notification/adaptor/NotificationAdaptor.java @@ -39,4 +39,8 @@ public void readNotification(Notification notification) { notification.setRead(true); notificationRepository.save(notification); } + + public void deleteByUserId(Long id) { + notificationRepository.deleteByUserId(id); + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notification/repository/NotificationRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/notification/repository/NotificationRepository.java index 2f54d445..c9321fe4 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/notification/repository/NotificationRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/notification/repository/NotificationRepository.java @@ -10,4 +10,6 @@ public interface NotificationRepository extends JpaRepository findByUserOrderByCreatedAt(User user, Pageable pageable); int countByUserAndIsRead(User user, boolean b); + + void deleteByUserId(Long id); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java index 74b5e887..6174297b 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java @@ -18,6 +18,9 @@ public enum PortOneAuthErrorCode implements BaseErrorCode { FAILED_ERROR_AUTH_AMOUNT(BAD_REQUEST,"ORDER_001", "주문 가격이 일치하지 않습니다."), NOT_EXIST_ORDER_ID(BAD_REQUEST,"ORDER_002", "주문 번호가 존재하지 않습니다."), + EXIST_IMP_UID(BAD_REQUEST,"ORDER_004", "이미 결제 완료된 imp_uid 값 입니다."), + NOT_CORRECT_ORDER_ID(BAD_REQUEST, "ORDER_005", "주문 ID가 일치하지 않습니다"), + EXIST_CANCEL_STATUS(BAD_REQUEST,"ORDER_006", "이미 취소된 결제 번호입니다."), FAILED_ERROR_AUTH(INTERNAL_SERVER_ERROR,"ORDER_003", "결제요청에 실패했습니다. 다시 시도해주세요."); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/AttentionAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/AttentionAdaptor.java new file mode 100644 index 00000000..1abb2459 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/AttentionAdaptor.java @@ -0,0 +1,16 @@ +package com.example.matchdomain.project.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchdomain.project.repository.ProjectUserAttentionRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class AttentionAdaptor { + private final ProjectUserAttentionRepository projectUserAttentionRepository; + + public void deleteByUser(User user) { + projectUserAttentionRepository.deleteById_userId(user.getId()); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectAdaptor.java index 90de255e..a763314e 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectAdaptor.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectAdaptor.java @@ -14,10 +14,12 @@ import com.example.matchdomain.user.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import static com.example.matchdomain.common.model.Status.ACTIVE; @@ -64,48 +66,49 @@ public Project findById(Long projectId) { public Page findProject(User user, int page, int size, ProjectKind projectKind, String content, FILTER filter) { - Page projects = null; + Page projects; + List projectLists; Pageable pageable = PageRequest.of(page, size); + long totalCnt = projectRepository.countQueryForProject(PROCEEDING, LocalDateTime.now(), ACTIVE, content, projectKind); + + if(filter == FILTER.RECOMMEND) { if (projectKind == null) { if (content == null) { - projects = projectRepository.findLoginUserProjectList(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); + projectLists = projectRepository.getProjectLists(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); } else { - projects = projectRepository.findByContent(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), content); - + projectLists = projectRepository.findByContent(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), ACTIVE.getValue(), content, pageable); } } else { if (content == null) { - projects = projectRepository.findByProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + projectLists = projectRepository.findByProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue()); } else { - projects = projectRepository.findByContentAndProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), - ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue(), content); + projectLists = projectRepository.findByProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue()); } } }else{ if (projectKind == null) { if (content == null) { - projects = projectRepository.findLoginUserProjectListLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); + projectLists = projectRepository.findLoginUserProjectListLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); } else { - projects = projectRepository.findByContentLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), content); + projectLists = projectRepository.findByContentLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), content); } } else { if (content == null) { - projects = projectRepository.findByProjectKindLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + projectLists = projectRepository.findByProjectKindLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue()); - } else { - projects = projectRepository.findByContentAndProjectKindLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + projectLists = projectRepository.findByContentAndProjectKindLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue(), content); } } } - - return projects; + return new PageImpl<>(projectLists, pageable, totalCnt); } public Page getTodayProjectLists(Long userId, int page, int size) { @@ -121,4 +124,24 @@ public Project checkRegularProjects(Long projectId, RegularStatus regularStatus) public Optional findByProjectId(Long projectId) { return projectRepository.findById(projectId); } + + public Page findLikeProjects(User user, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + Page projects = projectRepository.findLikeProjects(user.getId(), pageable); + return projects; + } + + public Project findByProject(String projectId) { + return findByProjectId(Long.valueOf(projectId)).orElseThrow(()->new BadRequestException(ProjectOneTimeErrorCode.PROJECT_NOT_EXIST)); + } + + public Page findAll(int page, int size) { + Pageable pageable = PageRequest.of(page, size); + return projectRepository.findByOrderByCreatedAtAsc(pageable); + } + + public List getRandom3Project() { + return projectRepository.findRandomThreeProject(LocalDateTime.now()); + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectCommentAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectCommentAdaptor.java new file mode 100644 index 00000000..c9646f90 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectCommentAdaptor.java @@ -0,0 +1,16 @@ +package com.example.matchdomain.project.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchdomain.project.repository.ProjectCommentRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class ProjectCommentAdaptor { + private final ProjectCommentRepository projectCommentRepository; + + public void deleteByUser(User user) { + projectCommentRepository.deleteByUserId(user.getId()); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCommentRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCommentRepository.java index 1f6fe8b1..a6a199ec 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCommentRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCommentRepository.java @@ -18,4 +18,6 @@ public interface ProjectCommentRepository extends JpaRepository findByProjectIdAndStatusOrderByCreatedAtAsc(Long projectId, Status status, Pageable pageable); Optional findByIdAndStatus(Long commentId, Status status); + + void deleteByUserId(Long id); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepository.java index d8529031..e3ffba03 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepository.java @@ -21,4 +21,6 @@ public interface ProjectCustomRepository { Page getProjectList(User user, ProjectStatus projectStatus, LocalDateTime now, ImageRepresentStatus imageRepresentStatus, Status status, ProjectKind projectKind, String content, Pageable pageable); Page findProject(User user, ProjectStatus projectStatus, LocalDateTime now, ImageRepresentStatus imageRepresentStatus, Status status, ProjectKind projectKind, String content, Pageable pageable); + + long countQueryForProject(ProjectStatus projectStatus, LocalDateTime now, Status status, String content, ProjectKind projectKind); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepositoryImpl.java b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepositoryImpl.java index 5b4fb696..db6ba663 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepositoryImpl.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectCustomRepositoryImpl.java @@ -207,4 +207,34 @@ public Page findProject(User user, ProjectStatus projectStatus, Loca return PageableExecutionUtils.getPage(projectDtos, pageable, countQuery::fetchCount); } + @Override + public long countQueryForProject(ProjectStatus projectStatus, LocalDateTime now, Status status, String content, ProjectKind projectKind) { + QProject project = QProject.project; + + Predicate predicate = buildSearchCountPredicate(projectStatus, now, status, content, projectKind, project); + + return queryFactory.selectFrom(project) + .where(predicate) + .fetchCount(); + } + + private Predicate buildSearchCountPredicate(ProjectStatus projectStatus, LocalDateTime now, Status status, String content, ProjectKind projectKind, QProject project) { + BooleanExpression predicate = project.projectStatus.eq(projectStatus) + .and(project.finishedAt.goe(now)) + .and(project.status.eq(status)); + + if (projectKind != null) { + predicate = predicate.and(project.projectKind.eq(projectKind)); + } + if (content != null) { + predicate = predicate.and(project.projectName.like("%" + content + "%") + .or(project.projectExplanation.like("%" + content + "%")) + .or(project.usages.like("%" + content + "%")) + .or(project.searchKeyword.like("%" + content + "%"))); + } + + + return predicate; + } + } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java index 1da1607f..452fc25e 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java @@ -10,8 +10,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.security.core.parameters.P; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; public interface ProjectRepository extends JpaRepository, ProjectCustomRepository { @@ -41,7 +43,7 @@ Page searchProject(@Param("content") String content, @Param("content1") "where (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation LIKE concat('%',:content1,'%') OR P.usages LIKE concat('%',:content2,'%') OR P.searchKeyword LIKE concat('%',:content2,'%')) " + "and PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status order by viewCnt asc" , nativeQuery = true - , countQuery = "select * from Project P where projectStatus = :projectStatus and finishedAt = :now and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation " + + , countQuery = "select count(*) from Project P where projectStatus = :projectStatus and finishedAt = :now and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation " + "LIKE concat('%',:content1,'%') OR P.usages LIKE concat('%',:content2,'%')) and P.status =:status") Page searchProjectLoginUser(@Param("userId") Long userId, @Param("content") String content, @Param("content1") String content1, @Param("content2") String content2, @Param("projectStatus") String projectStatus, @@ -91,10 +93,40 @@ Page searchProjectLoginUser(@Param("userId") Long userId, @Param("c "GROUP BY P.id\n" + "ORDER BY totalDonationCnt DESC" , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status") + , countQuery = "select count(*) from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status") Page findLoginUserProjectList(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status); + @Query(value = "SELECT\n" + + " P.id as 'id',\n" + + " P.usages as 'usages',\n" + + " P.projectKind as 'projectKind',\n" + + " viewCnt,\n" + + " P.projectName as 'projectName',\n" + + " PI.url as 'imgUrl',\n" + + " IF((\n" + + " SELECT EXISTS (\n" + + " SELECT *\n" + + " FROM ProjectUserAttention PUA\n" + + " WHERE PUA.userId = :userId\n" + + " AND P.id = PUA.projectId\n" + + " )\n" + + " ), 'true', 'false') AS 'like',\n" + + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + + " COUNT(RP.id) AS 'totalDonationCnt'\n" + + "FROM Project P\n" + + "JOIN ProjectImage PI ON P.id = PI.projectId\n" + + "LEFT JOIN RegularPayment RP ON RP.projectId = P.id AND RP.regularPayStatus = 'PROCEEDING'\n" + + "left join User U on U.id = RP.userId " + + "WHERE PI.imageRepresentStatus = :imageRepresentStatus\n" + + "AND P.projectStatus = :projectStatus\n" + + "AND P.finishedAt >= :now\n" + + "AND P.status = :status\n" + + "GROUP BY P.id\n" + + "ORDER BY totalDonationCnt DESC" + , nativeQuery = true) + List getProjectLists(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status); @Query(value = "SELECT\n" + " P.id as 'id',\n" + " P.usages as 'usages',\n" + @@ -123,8 +155,8 @@ Page findLoginUserProjectList(@Param("userId") Long userId, @Param( "GROUP BY P.id\n" + "ORDER BY P.createdAt DESC" , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status") - Page findLoginUserProjectListLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + , countQuery = "select count(*) from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status") + List findLoginUserProjectListLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status); @Query(value = "select P.id'projectId', " + @@ -150,9 +182,8 @@ Page findLoginUserProjectListLatest(@Param("userId") Long userId, @ "from Project P join ProjectImage PI on P.id = PI.projectId left join RegularPayment RP on RP.projectId=P.id and RP.regularPayStatus = 'PROCEEDING' " + "left join User U on U.id = RP.userId " + "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status and P.projectKind =:projectKind group by P.id order by totalDonationCnt desc" - , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and projectKind = :projectKind") - Page findByProjectKind(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + , nativeQuery = true) + List findByProjectKind(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("projectKind") String projectKind); @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + @@ -160,12 +191,12 @@ Page findByProjectKind(@Param("userId") Long userId, @Param("projec "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + "count(RP.id)'totalDonationCnt' \n" + - "from Project P join ProjectImage PI on P.id = PI.projectId left join RegularPayment RP on RP.projectId=P.id and RP.regularPayStatus = 'PROCEEDING' " + + "from Project P join ProjectImage PI on P.id = PI.projectId " + + "left join RegularPayment RP on RP.projectId=P.id and RP.regularPayStatus = 'PROCEEDING' " + "left join User U on U.id = RP.userId " + "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status and P.projectKind =:projectKind group by P.id order by P.createdAt desc" - , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and projectKind = :projectKind") - Page findByProjectKindLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + , nativeQuery = true) + List findByProjectKindLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("projectKind") String projectKind); @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + "P.projectName as 'projectName', PI.url as 'imgUrl', " + @@ -178,11 +209,8 @@ Page findByProjectKindLatest(@Param("userId") Long userId, @Param(" "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status and P.projectKind =:projectKind " + " and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation " + " LIKE concat('%',:content,'%') OR P.usages LIKE concat('%',:content,'%') OR P.searchKeyword LIKE concat('%',:content,'%')) group by P.id order by totalDonationCnt desc" - , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and projectKind = :projectKind " + - "and (projectName LIKE concat('%',:content,'%') OR projectExplanation LIKE concat('%',:content,'%') " + - "OR usages LIKE concat('%',:content,'%') OR searchKeyword LIKE concat('%',:content,'%'))") - Page findByContentAndProjectKind(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + , nativeQuery = true) + List findByContentAndProjectKind(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("projectKind") String projectKind, @Param("content") String content); @@ -197,11 +225,8 @@ Page findByContentAndProjectKind(@Param("userId") Long userId, @Par "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status and P.projectKind =:projectKind " + " and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation " + " LIKE concat('%',:content,'%') OR P.usages LIKE concat('%',:content,'%') OR P.searchKeyword LIKE concat('%',:content,'%')) group by P.id order by P.createdAt desc" - , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and projectKind = :projectKind " + - "and (projectName LIKE concat('%',:content,'%') OR projectExplanation LIKE concat('%',:content,'%') " + - "OR usages LIKE concat('%',:content,'%') OR searchKeyword LIKE concat('%',:content,'%'))") - Page findByContentAndProjectKindLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + , nativeQuery = true) + List findByContentAndProjectKindLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("projectKind") String projectKind, @Param("content") String content); @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + @@ -213,20 +238,20 @@ Page findByContentAndProjectKindLatest(@Param("userId") Long userId "join ProjectImage PI on P.id = PI.projectId " + "left join RegularPayment RP on RP.projectId=P.id and RP.regularPayStatus = 'PROCEEDING'" + "left join User U on U.id = RP.userId " + - "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status" + + "where P.projectStatus = :projectStatus and P.finishedAt>=:now " + + "and PI.imageRepresentStatus = :imageRepresentStatus " + + "and P.status = :status" + " and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation LIKE concat('%',:content,'%') " + - " OR P.usages LIKE concat('%',:content,'%') OR P.searchKeyword LIKE concat('%',:content,'%')) group by P.id order by totalDonationCnt desc" - , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status " + - "and (projectName LIKE concat('%',:content,'%') " + - "OR projectExplanation LIKE concat('%',:content,'%') OR usages LIKE concat('%',:content,'%') OR searchKeyword LIKE concat('%',:content,'%')) ") - Page findByContent(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, - @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, - @Param("content") String content); + " OR P.usages LIKE concat('%',:content,'%') OR P.searchKeyword LIKE concat('%',:content,'%')) " + + "group by P.id order by totalDonationCnt desc" + , nativeQuery = true) + List findByContent(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + @Param("imageRepresentStatus") String imageRepresentStatus, @Param("status") String status, + @Param("content") String content, Pageable pageable); @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + "P.projectName as 'projectName', PI.url as 'imgUrl', " + - "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + + "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false') AS 'like', " + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + "count(RP.id)'totalDonationCnt' \n" + "from Project P " + @@ -236,11 +261,16 @@ Page findByContent(@Param("userId") Long userId, @Param("projectSta "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status" + " and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation LIKE concat('%',:content,'%') " + " OR P.usages LIKE concat('%',:content,'%') OR P.searchKeyword LIKE concat('%',:content,'%')) group by P.id order by P.createdAt desc" - , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status " + + , countQuery = "select count(*) from Project where " + + "projectStatus = :projectStatus" + + " and finishedAt >= :now " + + "and status = :status " + "and (projectName LIKE concat('%',:content,'%') " + - "OR projectExplanation LIKE concat('%',:content,'%') OR usages LIKE concat('%',:content,'%') OR searchKeyword LIKE concat('%',:content,'%')) ") - Page findByContentLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + "OR projectExplanation LIKE concat('%',:content,'%') " + + "OR usages LIKE concat('%',:content,'%') " + + "OR searchKeyword LIKE concat('%',:content,'%'))", + nativeQuery = true) + List findByContentLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("content") String content); @@ -256,7 +286,7 @@ Page findByContentLatest(@Param("userId") Long userId, @Param("proj "and P.finishedAt>=:now and P.status = :status and P.todayStatus = :todayStatus " + "group by P.id order by totalDonationCnt desc" , nativeQuery = true - , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and todayStatus=:todayStatus") + , countQuery = "select count(*) from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and todayStatus=:todayStatus") Page findTodayProject(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus,@Param("status") String status, @Param("todayStatus") String todayStatus, Pageable pageable); @@ -280,7 +310,7 @@ Page findTodayProject(@Param("userId") Long userId, @Param("project " count(RP.id)'totalDonationCnt',\n" + " GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList'\n" + "FROM Project P\n" + - " LEFT JOIN RegularPayment RP ON P.id = RP.projectId\n" + + " LEFT JOIN RegularPayment RP ON P.id = RP.projectId and RP.regularPayStatus = 'PROCEEDING'\n" + " LEFT JOIN User U ON RP.userId = U.id\n" + "WHERE P.id = :projectId \n" + "GROUP BY P.id ", nativeQuery = true) @@ -288,6 +318,27 @@ Page findTodayProject(@Param("userId") Long userId, @Param("project Optional findByIdAndStatusAndRegularStatus(Long projectId, Status status, RegularStatus regular); + @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + + "P.projectName as 'projectName', PI.url as 'imgUrl', " + + "If((select exists (select * from ProjectUserAttention PUA2 where PUA2.userId=:userId and P.id = PUA2.projectId )),'true','false')'like', " + + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + + "count(RP.id)'totalDonationCnt' \n" + + "from Project P join ProjectImage PI on P.id = PI.projectId and PI.imageRepresentStatus = 'REPRESENT'" + + "LEFT JOIN RegularPayment RP ON RP.projectId = P.id AND RP.regularPayStatus = 'PROCEEDING'\n" + + "left join User U on U.id = RP.userId " + + "join ProjectUserAttention PUA on PUA.projectId = P.id " + + "where PUA.userId = :userId " + + "group by P.id order by totalDonationCnt desc" + , nativeQuery = true + , countQuery = "select count(*) from Project P join ProjectUserAttention PUA on PUA.userId = :userId and P.id = PUA.projectId") + Page findLikeProjects(@Param("userId") Long userId, Pageable pageable); + + Page findByOrderByCreatedAtAsc(Pageable pageable); + + @Query(value = "SELECT * FROM Project WHERE projectStatus = 'PROCEEDING' and finishedAt > :now and status = 'ACTIVE' ORDER BY RAND() LIMIT 3", nativeQuery = true) + List findRandomThreeProject(@Param("now") LocalDateTime now); + + interface ProjectList { Long getId(); String getImgUrl(); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectUserAttentionRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectUserAttentionRepository.java index e82ca715..5ca01ae4 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectUserAttentionRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectUserAttentionRepository.java @@ -25,4 +25,6 @@ List findById_userIdAndProject_ProjectImage_imageRepresent List findById_userId(Long id); Long countById_userId(Long id); + + void deleteById_userId(Long id); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/redis/adaptor/OrderAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/redis/adaptor/OrderAdaptor.java new file mode 100644 index 00000000..90a241a1 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/redis/adaptor/OrderAdaptor.java @@ -0,0 +1,23 @@ +package com.example.matchdomain.redis.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchdomain.redis.entity.OrderRequest; +import com.example.matchdomain.redis.repository.OrderRequestRepository; +import lombok.RequiredArgsConstructor; + +import static com.example.matchdomain.order.exception.PortOneAuthErrorCode.NOT_EXIST_ORDER_ID; + +@Adaptor +@RequiredArgsConstructor +public class OrderAdaptor { + private final OrderRequestRepository orderRequestRepository; + + public OrderRequest findById(String orderId) { + return orderRequestRepository.findById(orderId).orElseThrow(()->new BadRequestException(NOT_EXIST_ORDER_ID)); + } + + public void deleteById(String orderId) { + orderRequestRepository.deleteById(orderId); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/review/adaptor/ReviewAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/review/adaptor/ReviewAdaptor.java new file mode 100644 index 00000000..5f9491ad --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/review/adaptor/ReviewAdaptor.java @@ -0,0 +1,24 @@ +package com.example.matchdomain.review.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.review.entity.Review; +import com.example.matchdomain.review.repository.ReviewRepository; +import lombok.RequiredArgsConstructor; + +import java.util.Optional; + +@Adaptor +@RequiredArgsConstructor +public class ReviewAdaptor { + private final ReviewRepository reviewRepository; + + + public Review save(Review review) { + return reviewRepository.save(review); + } + + public Optional findByDonationUser(DonationUser donationUser) { + return reviewRepository.findByDonationUser(donationUser); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/review/entity/Review.java b/Match-Domain/src/main/java/com/example/matchdomain/review/entity/Review.java new file mode 100644 index 00000000..8731e4d2 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/review/entity/Review.java @@ -0,0 +1,41 @@ +package com.example.matchdomain.review.entity; + +import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.donation.entity.DonationUser; +import lombok.*; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.*; + +@Entity +@Getter +@Table(name = "Review") +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@DynamicInsert +@BatchSize(size = 10) +public class Review extends BaseEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "donationId",nullable = false, insertable=false, updatable=false) + private DonationUser donationUser; + + private Long donationId; + + private int donation; + + private int transparency; + + private int information; + + private String comment; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewCustomRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewCustomRepository.java new file mode 100644 index 00000000..b514fc8e --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewCustomRepository.java @@ -0,0 +1,6 @@ +package com.example.matchdomain.review.repository; + +import com.example.matchdomain.review.entity.Review; + +public interface ReviewCustomRepository { +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewCustomRepositoryImpl.java b/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewCustomRepositoryImpl.java new file mode 100644 index 00000000..5d1e7040 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewCustomRepositoryImpl.java @@ -0,0 +1,10 @@ +package com.example.matchdomain.review.repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ReviewCustomRepositoryImpl implements ReviewCustomRepository{ + private final JPAQueryFactory queryFactory; + +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewRepository.java new file mode 100644 index 00000000..38b853eb --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/review/repository/ReviewRepository.java @@ -0,0 +1,11 @@ +package com.example.matchdomain.review.repository; + +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.review.entity.Review; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface ReviewRepository extends JpaRepository, ReviewCustomRepository { + Optional findByDonationUser(DonationUser donationUser); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserAdaptor.java index 993b93a7..0bdc2f22 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserAdaptor.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserAdaptor.java @@ -1,24 +1,72 @@ package com.example.matchdomain.user.adaptor; import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.UnauthorizedException; +import com.example.matchdomain.common.model.Status; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.entity.enums.SocialType; +import com.example.matchdomain.user.exception.UserAuthErrorCode; import com.example.matchdomain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; -import org.springframework.cache.annotation.Cacheable; +import java.util.List; import java.util.Optional; +import static com.example.matchdomain.user.entity.enums.Alarm.ACTIVE; +import static com.example.matchdomain.user.entity.enums.SocialType.APPLE; +import static com.example.matchdomain.user.entity.enums.SocialType.NORMAL; +import static com.example.matchdomain.user.exception.UserLoginErrorCode.NOT_EXIST_USER; + @Adaptor @RequiredArgsConstructor public class UserAdaptor { private final UserRepository userRepository; public Optional existsSocialUser(String socialId, SocialType socialType){ - return userRepository.findBySocialIdAndSocialType(socialId, socialType); + return userRepository.findBySocialIdAndSocialTypeAndStatus(socialId, socialType, Status.ACTIVE); } public Optional findByUserId(Long userId) { - System.out.println("캐싱 하자"); return userRepository.findById(userId); } + + public List findByServiceAlarm() { + return userRepository.findByServiceAlarm(ACTIVE); + } + + public List findDeleteUsers() { + return userRepository.findByStatus(Status.INACTIVE); + } + + public User findByUser(String userId) { + return userRepository.findByIdAndStatus(Long.valueOf(userId), Status.ACTIVE).orElseThrow(()->new BadRequestException(NOT_EXIST_USER)); + } + + public boolean existsEmail(String email) { + return userRepository.existsByEmailAndStatus(email, Status.ACTIVE); + } + + public boolean existsPhoneNumber(String phone) { + return userRepository.existsByPhoneNumberAndStatus(phone, Status.ACTIVE); + } + + public User findByUserName(String email) { + return userRepository.findByUsernameAndStatusAndSocialType(email, Status.ACTIVE, NORMAL).orElseThrow(()->new BadRequestException(NOT_EXIST_USER)); + } + + public User save(User user) { + return userRepository.save(user); + } + + public boolean checkEmailPassword(String email, SocialType socialType) { + return userRepository.existsByUsernameAndSocialTypeAndStatus(email, socialType, Status.ACTIVE); + } + + public boolean existsEmailAndSocial(String email, SocialType socialType){ + return userRepository.existsByEmailAndSocialTypeNotAndStatus(email, socialType, Status.ACTIVE); + } + + public User findByUsernameAndStatus(String username){ + return userRepository.findByUsernameAndStatus(username, Status.ACTIVE).orElseThrow(() -> new UnauthorizedException(UserAuthErrorCode.NOT_EXIST_USER)); + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserCardAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserCardAdaptor.java index 94f07e3b..fdd678fb 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserCardAdaptor.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserCardAdaptor.java @@ -23,4 +23,8 @@ public List findCardLists(Long userId) { public UserCard findCardByCardId(Long cardId) { return userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); } + + public void deleteByUser(Long userId) { + userCardRepository.deleteByUserId(userId); + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserFcmAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserFcmAdaptor.java new file mode 100644 index 00000000..4967c799 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserFcmAdaptor.java @@ -0,0 +1,33 @@ +package com.example.matchdomain.user.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.constants.enums.Topic; +import com.example.matchdomain.user.entity.UserFcmToken; +import com.example.matchdomain.user.repository.UserFcmTokenRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +import static com.example.matchdomain.user.entity.enums.Alarm.ACTIVE; + +@Adaptor +@RequiredArgsConstructor +public class UserFcmAdaptor { + private final UserFcmTokenRepository fcmTokenRepository; + + public List findByUser(Long userId) { + return fcmTokenRepository.findByUserIdAndUser_ServiceAlarm(userId, ACTIVE); + } + + public Page findByUserServiceAlarm(int page, int size, Topic topic) { + Pageable pageable = PageRequest.of(page, size); + if(topic.equals(Topic.PROJECT_UPLOAD)) { + return fcmTokenRepository.findByUser_ServiceAlarm(ACTIVE, pageable); + }else{ + return fcmTokenRepository.findByUser_EventAlarm(ACTIVE, pageable); + } + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java b/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java index b0b68fa8..c46f8d13 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java @@ -149,4 +149,10 @@ public void setModifyProfile(String newProfileImg, String name) { this.profileImgUrl = newProfileImg; this.nickname = name; } + + public void updateUserInfo(LocalDate birthDate, String name, String phone) { + this.birth = birthDate; + this.name = name; + this.phoneNumber = phone; + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/entity/enums/SocialType.java b/Match-Domain/src/main/java/com/example/matchdomain/user/entity/enums/SocialType.java index c4af0dcd..8ceee180 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/entity/enums/SocialType.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/entity/enums/SocialType.java @@ -9,7 +9,7 @@ public enum SocialType { KAKAO("kakao","카카오"), NAVER("naver","네이버"), APPLE("apple","애플"), - NORMAL("normal","전화번호"); + NORMAL("normal","이메일"); private final String value; private final String name; diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/exception/AppleLoginErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/AppleLoginErrorCode.java new file mode 100644 index 00000000..f3005b7e --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/AppleLoginErrorCode.java @@ -0,0 +1,43 @@ +package com.example.matchdomain.user.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@Getter +@AllArgsConstructor +public enum AppleLoginErrorCode implements BaseErrorCode { + NOT_EXISTS_APPLE_USER(NOT_FOUND,"APPLE_SIGN_UP","해당 애플 로그인 유저의 로그인 정보가 존재하지 않습니다. 반환 필드 중 result 에 있는 값을 가지고 회원가입 진행해주세요"); + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + HashMap data = new HashMap<>(); + data.put("socialId","daoiisjo4e21424.412j4io1241.b231"); + return ErrorReason.builder().message(message).code(code).isSuccess(false).result(data).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/exception/CheckUserPhoneErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/CheckUserPhoneErrorCode.java new file mode 100644 index 00000000..43010819 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/CheckUserPhoneErrorCode.java @@ -0,0 +1,42 @@ +package com.example.matchdomain.user.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.FORBIDDEN; + +@Getter +@AllArgsConstructor +public enum CheckUserPhoneErrorCode implements BaseErrorCode { + @ExplainError("유저가 이미 가입된 전화번호로 가입을 요청하는 경우") + USERS_EXISTS_PHONE(FORBIDDEN,"U005","중복된 전화번호입니다."); + + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.builder().message(message).code(code).isSuccess(false).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/exception/DeleteUserErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/DeleteUserErrorCode.java new file mode 100644 index 00000000..77daa268 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/DeleteUserErrorCode.java @@ -0,0 +1,43 @@ +package com.example.matchdomain.user.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +@Getter +@AllArgsConstructor +public enum DeleteUserErrorCode implements BaseErrorCode { + @ExplainError("애플 유저는 02-13 API 사용") + APPLE_USER_NOT_API(BAD_REQUEST,"USER_DELETE_001","애플 유저는 02-13 API 사용"); + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + HashMap data = new HashMap<>(); + data.put("signUpType","이미 가입된 (가입한 타입) 계정이 존재 합니다. (가입한 타입) 계정으로 로그인 해주세요."); + return ErrorReason.builder().message(message).code(code).isSuccess(false).result(data).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/exception/SendEmailFindPassword.java b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/SendEmailFindPassword.java new file mode 100644 index 00000000..eb2ca631 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/SendEmailFindPassword.java @@ -0,0 +1,44 @@ +package com.example.matchdomain.user.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.FORBIDDEN; + +@Getter +@AllArgsConstructor +public enum SendEmailFindPassword implements BaseErrorCode { + @ExplainError("이메일이 일치하지 않습니다.") + NOT_EXISTS_EMAIL(BAD_REQUEST,"FIND_PASSWORD_001","해당 이메일로 가입된 이메일이 존재하지 않습니다."); + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + HashMap data = new HashMap<>(); + data.put("signUpType","이미 가입된 (가입한 타입) 계정이 존재 합니다. (가입한 타입) 계정으로 로그인 해주세요."); + return ErrorReason.builder().message(message).code(code).isSuccess(false).result(data).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/exception/UserAuthErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/UserAuthErrorCode.java index 407003b8..2dd040d1 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/exception/UserAuthErrorCode.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/UserAuthErrorCode.java @@ -9,10 +9,10 @@ import java.lang.reflect.Field; import java.util.Objects; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.UNAUTHORIZED; import com.example.matchcommon.annotation.ExplainError; +import static org.springframework.http.HttpStatus.*; + @Getter @AllArgsConstructor @@ -39,8 +39,9 @@ public enum UserAuthErrorCode implements BaseErrorCode { NOT_EMPTY_TOKEN(UNAUTHORIZED,"AUTH010","토큰이 비어있습니다 토큰을 보내주세요"), @ExplainError("토큰에 담긴 유저가 없는 경우") NOT_EXISTS_USER_HAVE_TOKEN(UNAUTHORIZED,"AUTH011", "해당 토큰을 가진 유저가 존재하지 않습니다."), + NOT_USER_ACTIVE(UNAUTHORIZED,"AUTH012","유저가 비활성 상태입니다 로그인 할 수 없습니다."), @ExplainError("유저가 존재하지 않는 경우") - NOT_EXIST_USER(UNAUTHORIZED,"U009" , "해당 유저가 존재하지 않습니다."), + NOT_EXIST_USER(NOT_FOUND,"U009" , "해당 유저가 존재하지 않습니다."), NOT_ALLOWED_ACCESS(UNAUTHORIZED,"U010","접근 권한이 없습니다."); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserFcmTokenRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserFcmTokenRepository.java index 6fe7ca6a..146d03f1 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserFcmTokenRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserFcmTokenRepository.java @@ -1,9 +1,19 @@ package com.example.matchdomain.user.repository; +import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.entity.UserFcmToken; +import com.example.matchdomain.user.entity.enums.Alarm; import com.example.matchdomain.user.entity.pk.UserFcmPk; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface UserFcmTokenRepository extends JpaRepository { + List findByUserIdAndUser_ServiceAlarm(Long id, Alarm alarm); + + Page findByUser_ServiceAlarm(Alarm alarm, Pageable pageable); + Page findByUser_EventAlarm(Alarm alarm, Pageable pageable); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserRepository.java index 377ddf93..74e7456b 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/repository/UserRepository.java @@ -1,10 +1,12 @@ package com.example.matchdomain.user.repository; import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.user.entity.enums.Alarm; import com.example.matchdomain.user.entity.enums.Gender; import com.example.matchdomain.user.entity.enums.SocialType; import com.example.matchdomain.user.entity.User; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; @@ -14,6 +16,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; @Repository public interface UserRepository extends JpaRepository { @@ -77,6 +80,28 @@ public interface UserRepository extends JpaRepository { "FROM User U where U.id = :userId " ,nativeQuery = true) UserList getUserDetail(@Param("userId") Long userId); + List findByServiceAlarm(Alarm alarm); + + boolean existsByEmailAndSocialTypeNot(String email, SocialType socialType); + + Optional findBySocialIdAndSocialTypeAndStatus(String id, SocialType socialType, Status status); + + Optional findByPhoneNumberAndSocialTypeNotAndStatus(String s, SocialType socialType, Status status); + + Optional findByUsernameAndStatus(String email, Status status); + + boolean existsByPhoneNumberAndStatus(String phone, Status status); + + List findByStatus(Status status); + + boolean existsByEmailAndStatus(String email, Status status); + + boolean existsByUsernameAndSocialTypeAndStatus(String username, SocialType socialType, Status status); + + Optional findByUsernameAndStatusAndSocialType(String email, Status status, SocialType socialType); + + boolean existsByEmailAndSocialTypeNotAndStatus(String email, SocialType socialType, Status status); + public interface UserList { Long getUserId(); String getName(); diff --git a/Match-Domain/src/main/resources/application-domain-dev.yml b/Match-Domain/src/main/resources/application-domain-dev.yml index 218d935c..5bdfb0b2 100644 --- a/Match-Domain/src/main/resources/application-domain-dev.yml +++ b/Match-Domain/src/main/resources/application-domain-dev.yml @@ -41,5 +41,8 @@ spring: hibernate: format_sql: true default_batch_fetch_size: 1000 + main: + allow-bean-definition-overriding: true + diff --git a/Match-Domain/src/main/resources/application-domain-localDev.yml b/Match-Domain/src/main/resources/application-domain-localDev.yml index 1fe03d90..035e2426 100644 --- a/Match-Domain/src/main/resources/application-domain-localDev.yml +++ b/Match-Domain/src/main/resources/application-domain-localDev.yml @@ -41,4 +41,7 @@ spring: hibernate: format_sql: true default_batch_fetch_size: 1000 + main: + allow-bean-definition-overriding: true + diff --git a/Match-Domain/src/main/resources/application-domain-prod.yml b/Match-Domain/src/main/resources/application-domain-prod.yml index 4411b518..0f47dd8f 100644 --- a/Match-Domain/src/main/resources/application-domain-prod.yml +++ b/Match-Domain/src/main/resources/application-domain-prod.yml @@ -37,10 +37,12 @@ spring: # DDl 생성 시 데이터베이스 고유의 기능 사용여부 generate-ddl: false # 실행되는 쿼리문 보여주기 여부 - show-sql: true + show-sql: false properties: hibernate: format_sql: true default_batch_fetch_size: 1000 + main: + allow-bean-definition-overriding: true diff --git a/Match-Domain/src/main/resources/application-domain.yml b/Match-Domain/src/main/resources/application-domain.yml index 731621c7..a821f215 100644 --- a/Match-Domain/src/main/resources/application-domain.yml +++ b/Match-Domain/src/main/resources/application-domain.yml @@ -36,4 +36,7 @@ spring: hibernate: format_sql: true default_batch_fetch_size: 1000 + main: + allow-bean-definition-overriding: true + diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/AligoStatic.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/AligoStatic.java new file mode 100644 index 00000000..a042e333 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/AligoStatic.java @@ -0,0 +1,30 @@ +package com.example.matchinfrastructure.aligo; + +public class AligoStatic { + public static final String PAYMENT_TEMPLATE = "TQ_1760"; + public static final String EXECUTION_TEMPLATE = "TQ_1761"; + public static final String AOS_DEEP_LINK = "https://teammatch.page.link/flame?id="; + public static final String IOS_DEEP_LINK = "https://teammatch.page.link/flame?id="; + public static final String APP_LINK = "AL"; + public static final String APP_LINK_NAME = "앱링크"; + public static final String PAYMENT_BUTTON_NAME = "내 불꽃이 보러 가기"; + public static final String PAYMENT_EM_TITLE = "기부가 시작되었습니다"; + public static final String PAYMENT_SUBJECT = "결제 알림"; + public static final String PAYMENT_MESSAGE = + "%s님! 기부 신청이 완료되었습니다.\n\n" + + "%s님의 마음 속 따뜻함이\n" + + "세상을 따뜻하게 할 불꽃이가 되었습니다.\n" + + "\n" + + "어떤 불꽃이가 탄생했는지 \n" + + "앱에서 확인해보세요 (/^^)/\n"; + public static final String BUTTON = "{\"button\": %s }"; + public static final String EXECUTION_EM_TITLE = "기부금 집행 내용 알림"; + public static final String EXECUTION_MESSAGE = + "%s님의 불꽃이는\n" + + "%s(이)가 되어\n" + + "%s에게 전달되었습니다 (^^)γ"; + + public static final String EXECUTION_BUTTON_NAME = "자세한 내용 보러가기"; + public static final String EXECUTION_SUBJECT = "집행 알림"; + +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/client/KakaoAligoFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/client/KakaoAligoFeignClient.java new file mode 100644 index 00000000..e2e9c21a --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/client/KakaoAligoFeignClient.java @@ -0,0 +1,31 @@ +package com.example.matchinfrastructure.aligo.client; + +import com.example.matchinfrastructure.aligo.config.AligoFeignConfiguration; +import com.example.matchinfrastructure.aligo.config.AligoKakaoConfiguration; +import com.example.matchinfrastructure.aligo.dto.AligoResponse; +import com.example.matchinfrastructure.aligo.dto.AlimTalkReq; +import com.example.matchinfrastructure.aligo.dto.AlimTalkRes; +import com.example.matchinfrastructure.aligo.dto.CreateTokenRes; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient( + name = "KakaoAligoFeignClient", + url = "https://kakaoapi.aligo.in", + configuration = AligoKakaoConfiguration.class) +public interface KakaoAligoFeignClient { + @PostMapping("/akv10/token/create/30/s") + CreateTokenRes createToken( + @RequestParam("apikey") String key, + @RequestParam("userid") String userId + ); + + @PostMapping(value = "/akv10/alimtalk/send", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + AligoResponse sendAlimTalk( + @RequestBody AlimTalkReq form + ); + +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/config/AligoKakaoConfiguration.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/config/AligoKakaoConfiguration.java new file mode 100644 index 00000000..6d3817d5 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/config/AligoKakaoConfiguration.java @@ -0,0 +1,24 @@ +package com.example.matchinfrastructure.aligo.config; + +import feign.Logger; +import feign.RequestInterceptor; +import feign.codec.ErrorDecoder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(AligoInfoErrorDecoder.class) +public class AligoKakaoConfiguration { + @Bean + public RequestInterceptor requestInterceptor() { + return template -> template.header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); + } + @Bean + public ErrorDecoder errorDecoder() { + return new AligoInfoErrorDecoder(); + } + + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/converter/AligoConverter.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/converter/AligoConverter.java new file mode 100644 index 00000000..3cdaad25 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/converter/AligoConverter.java @@ -0,0 +1,98 @@ +package com.example.matchinfrastructure.aligo.converter; + +import com.example.matchcommon.annotation.Converter; +import com.example.matchcommon.properties.AligoProperties; +import com.example.matchinfrastructure.aligo.dto.AlimTalkReq; +import com.example.matchinfrastructure.aligo.dto.AlimType; +import com.example.matchinfrastructure.match_aligo.dto.AlimTalkDto; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.ArrayList; +import java.util.List; + +import static com.example.matchinfrastructure.aligo.AligoStatic.*; + +@Converter +public class AligoConverter { + private final ObjectMapper mapper = new ObjectMapper(); + + public AlimTalkReq.Button createButton(AlimType alimType, Long regularId) { + return AlimTalkReq.Button + .builder() + .name(alimType.equals(AlimType.PAYMENT) ? PAYMENT_BUTTON_NAME : EXECUTION_BUTTON_NAME) + .linkType(APP_LINK) + .linkTypeName(APP_LINK_NAME) + .linkIos(IOS_DEEP_LINK + regularId) + .linkAnd(AOS_DEEP_LINK + regularId) + .build(); + } + + private String createButtonJson(AlimType alimType, Long regularId) { + List buttons = new ArrayList<>(); + buttons.add(createButton(alimType, regularId)); + + try { + return mapper.writeValueAsString(buttons); + } catch (Exception e) { + throw new RuntimeException("Error creating JSON for buttons", e); + } + } + + private AlimTalkReq createAlimTalkReq(AligoProperties aligoProperties, String token, AlimType alimType, String phone, String name, String tplCode, String emTitle, String subject, String message, String buttonJson) { + return AlimTalkReq + .builder() + .apikey(aligoProperties.getKey()) + .userid(aligoProperties.getUsername()) + .senderkey(aligoProperties.getSenderKey()) + .tpl_code(tplCode) + .sender(aligoProperties.getSender()) + .emtitle_1(emTitle) + .receiver_1(phone) + .subject_1(subject) + .message_1(message) + .token(token) + .button_1(String.format(BUTTON, buttonJson)) + .build(); + } + + public AlimTalkReq convertToAlimTalkTest(String phone, String name, AligoProperties aligoProperties, String token, AlimType alimType) { + String json = createButtonJson(alimType, 1L); + String tplCode = alimType.equals(AlimType.PAYMENT) ? PAYMENT_TEMPLATE : EXECUTION_TEMPLATE; + String emTitle = alimType.equals(AlimType.PAYMENT) ? PAYMENT_EM_TITLE : EXECUTION_EM_TITLE; + String subject = alimType.equals(AlimType.PAYMENT) ? PAYMENT_SUBJECT : EXECUTION_SUBJECT; + String message = alimType.equals(AlimType.PAYMENT) ? String.format(PAYMENT_MESSAGE, name, name) : String.format(EXECUTION_MESSAGE, name, "", ""); + + return createAlimTalkReq(aligoProperties, token, alimType, phone, name, tplCode, emTitle, subject, message, json); + } + + public AlimTalkReq convertToAlimTalk(AligoProperties aligoProperties, String token, AlimType alimType, AlimTalkDto alimTalkDto) { + String json = createButtonJson(alimType, alimTalkDto.getDonationId()); + String tplCode = alimType.equals(AlimType.PAYMENT) ? PAYMENT_TEMPLATE : EXECUTION_TEMPLATE; + String emTitle = alimType.equals(AlimType.PAYMENT) ? PAYMENT_EM_TITLE : EXECUTION_EM_TITLE; + String subject = alimType.equals(AlimType.PAYMENT) ? PAYMENT_SUBJECT : EXECUTION_SUBJECT; + String message = alimType.equals(AlimType.PAYMENT) ? String.format(PAYMENT_MESSAGE, alimTalkDto.getName(), alimTalkDto.getName()) : String.format(EXECUTION_MESSAGE, alimTalkDto.getName(), alimTalkDto.getArticle(), alimTalkDto.getUsages()); + + return createAlimTalkReq(aligoProperties, token, alimType, alimTalkDto.getPhone(), alimTalkDto.getName(), tplCode, emTitle, subject, message, json); + } + + public AlimTalkDto convertToAlimTalkPayment(Long donationId, String name, String phoneNumber) { + return AlimTalkDto + .builder() + .donationId(donationId) + .name(name) + .phone(phoneNumber) + .build(); + + } + + public AlimTalkDto convertToAlimTalkExecution(Long donationId, String name, String phoneNumber, String article, String usages){ + return AlimTalkDto + .builder() + .donationId(donationId) + .name(name) + .phone(phoneNumber) + .article(article) + .usages(usages) + .build(); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AligoResponse.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AligoResponse.java new file mode 100644 index 00000000..5e940788 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AligoResponse.java @@ -0,0 +1,34 @@ +package com.example.matchinfrastructure.aligo.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class AligoResponse { + @SerializedName("code") + int code; + + @SerializedName("message") + String message; + + @SerializedName("response") + T info; + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public T getInfo() { + return info; + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimTalkReq.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimTalkReq.java new file mode 100644 index 00000000..79b83bba --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimTalkReq.java @@ -0,0 +1,42 @@ +package com.example.matchinfrastructure.aligo.dto; + +import lombok.*; + +import java.util.List; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ToString +public class AlimTalkReq { + private String apikey; + private String userid; + private String senderkey; + private String tpl_code; + private String sender; + private String recvname_1; + private String receiver_1; + private String emtitle_1; + private String subject_1; + private String message_1; + private String token; + private String button_1; + + @Getter + @AllArgsConstructor + @NoArgsConstructor + @Builder + @ToString + public static class Button{ + private String name; + + private String linkType; + + private String linkTypeName; + + private String linkIos; + + private String linkAnd; + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimTalkRes.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimTalkRes.java new file mode 100644 index 00000000..eaa4bb27 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimTalkRes.java @@ -0,0 +1,19 @@ +package com.example.matchinfrastructure.aligo.dto; + +import lombok.*; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ToString +public class AlimTalkRes { + private String type; + private String mid; + private float current; + private float unit; + private float total; + private int scnt; + private int fcnt; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimType.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimType.java new file mode 100644 index 00000000..38d564b7 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/AlimType.java @@ -0,0 +1,5 @@ +package com.example.matchinfrastructure.aligo.dto; + +public enum AlimType { + EXECUTION, PAYMENT +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/CreateTokenRes.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/CreateTokenRes.java new file mode 100644 index 00000000..2013b715 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/dto/CreateTokenRes.java @@ -0,0 +1,16 @@ +package com.example.matchinfrastructure.aligo.dto; + +import lombok.*; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ToString +public class CreateTokenRes { + private int code; + private String message; + private String token; + private String urlencode; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/service/AligoInfraService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/service/AligoInfraService.java new file mode 100644 index 00000000..ea776155 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/aligo/service/AligoInfraService.java @@ -0,0 +1,75 @@ +package com.example.matchinfrastructure.aligo.service; + +import com.example.matchcommon.properties.AligoProperties; +import com.example.matchinfrastructure.aligo.client.AligoFeignClient; +import com.example.matchinfrastructure.aligo.client.KakaoAligoFeignClient; +import com.example.matchinfrastructure.aligo.converter.AligoConverter; +import com.example.matchinfrastructure.aligo.dto.*; +import com.example.matchinfrastructure.match_aligo.dto.AlimTalkDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class AligoInfraService { + private final AligoFeignClient aligoFeignClient; + private final KakaoAligoFeignClient kakaoAligoFeignClient; + private final AligoProperties aligoProperties; + private final AligoConverter aligoConverter; + + public void sendPhone(String phone, String code) { + String msg = "[MATCH] 회원님의 인증번호는 [" + code + "] 입니다."; + SendRes sendRes = aligoFeignClient.sendOneMsg(aligoProperties.getKey(), aligoProperties.getUsername(), + aligoProperties.getSender(), phone, msg); + System.out.println(sendRes.getResultCode()); + System.out.println(sendRes.getMessage()); + } + + public void sendAlimTalkTest(String phone, String name, AlimType alimType){ + CreateTokenRes token = getAligoToken(); + + AlimTalkReq alimTalkReq = aligoConverter.convertToAlimTalkTest(phone, name, aligoProperties, token.getToken(), alimType); + + System.out.println(alimTalkReq.toString()); + + AligoResponse aligoResponse = + kakaoAligoFeignClient.sendAlimTalk( + alimTalkReq + ); + System.out.println(aligoResponse.toString()); + } + private CreateTokenRes getAligoToken() { + return kakaoAligoFeignClient.createToken(aligoProperties.getKey(), aligoProperties.getUsername()); + } + + public void sendAlimTalk(AlimTalkDto alimTalkDto, AlimType alimType) { + CreateTokenRes token = getAligoToken(); + + AlimTalkReq alimTalkReq = aligoConverter.convertToAlimTalk(aligoProperties, token.getToken(), alimType, alimTalkDto); + + System.out.println(alimTalkReq.toString()); + + AligoResponse aligoResponse = + kakaoAligoFeignClient.sendAlimTalk( + alimTalkReq + ); + System.out.println(aligoResponse.toString()); + } + + public void sendAlimTalks(List alimTalks, AlimType alimType) { + CreateTokenRes token = getAligoToken(); + + for(AlimTalkDto alimTalkDto : alimTalks) { + AlimTalkReq alimTalkReq = aligoConverter.convertToAlimTalk(aligoProperties, token.getToken(), alimType, alimTalkDto); + + System.out.println(alimTalkReq.toString()); + + AligoResponse aligoResponse = + kakaoAligoFeignClient.sendAlimTalk( + alimTalkReq + ); + } + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/convertor/DiscordConvertor.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/converter/DiscordConverter.java similarity index 97% rename from Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/convertor/DiscordConvertor.java rename to Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/converter/DiscordConverter.java index 5a8cd855..ed99929b 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/convertor/DiscordConvertor.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/converter/DiscordConverter.java @@ -1,6 +1,6 @@ -package com.example.matchinfrastructure.discord.convertor; +package com.example.matchinfrastructure.discord.converter; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchinfrastructure.discord.dto.Message; import lombok.RequiredArgsConstructor; import org.springframework.core.env.Environment; @@ -12,9 +12,9 @@ import java.util.Arrays; import java.util.List; -@Convertor +@Converter @RequiredArgsConstructor -public class DiscordConvertor { +public class DiscordConverter { private final Environment environment; diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/service/DiscordService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/service/DiscordService.java index 7730e6ec..8b557247 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/service/DiscordService.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/service/DiscordService.java @@ -1,7 +1,7 @@ package com.example.matchinfrastructure.discord.service; import com.example.matchinfrastructure.discord.client.DiscordFeignClient; -import com.example.matchinfrastructure.discord.convertor.DiscordConvertor; +import com.example.matchinfrastructure.discord.converter.DiscordConverter; import lombok.RequiredArgsConstructor; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -12,15 +12,24 @@ @RequiredArgsConstructor public class DiscordService { private final DiscordFeignClient discordFeignClient; - private final DiscordConvertor discordConvertor; + private final DiscordConverter discordConverter; @Async("discord-message") public void sendUnKnownMessage(String username, Exception exception, HttpServletRequest request) { - discordFeignClient.errorMessage(discordConvertor.convertToUnknownMessage(username, exception, request)); + discordFeignClient.errorMessage(discordConverter.convertToUnknownMessage(username, exception, request)); } @Async("discord-message") public void sendKnownErrorMessage(String message) { - discordFeignClient.errorMessage(discordConvertor.convertToKnownMessage(message)); + discordFeignClient.errorMessage(discordConverter.convertToKnownMessage(message)); + } + + @Async("discord-message") + public void sendBatchStartAlert(String message, int size){ + discordFeignClient.alertMessage(discordConverter.convertToAlertBatchMessage(message, size)); + } + + public void sendBatchFinishAlert(String message, int totalAmount, int totalPayments, int successCount, int trueCount) { + discordFeignClient.alertMessage(discordConverter.convertToAlertFinishMessage(message, totalAmount, totalPayments, successCount, trueCount)); } } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/converter/FcmConverter.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/converter/FcmConverter.java new file mode 100644 index 00000000..d481fe53 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/converter/FcmConverter.java @@ -0,0 +1,64 @@ +package com.example.matchinfrastructure.fcm.converter; + +import com.example.matchcommon.annotation.Converter; +import com.example.matchinfrastructure.fcm.dto.AlertType; +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import com.example.matchinfrastructure.fcm.dto.NotificationPayDto; +import com.example.matchcommon.constants.enums.Topic; +import com.google.firebase.messaging.Message; +import com.google.firebase.messaging.Notification; + +import java.util.Map; + +import static com.example.matchcommon.constants.MatchAlertStatic.PAY_ALERT_BODY; +import static com.example.matchcommon.constants.MatchAlertStatic.ALERT_TITLE; + +@Converter +public class FcmConverter { + + public FCMNotificationRequestDto convertToDto(String inherenceName) { + return FCMNotificationRequestDto + .builder() + .title(ALERT_TITLE) + .body(inherenceName + PAY_ALERT_BODY) + .build(); + } + + public NotificationPayDto convertToPayData() { + return NotificationPayDto.builder().screen(AlertType.HOME_SCREEN).build(); + } + + public Notification convertToNotification(FCMNotificationRequestDto fcmNotificationRequestDto) { + return Notification + .builder() + .setTitle(fcmNotificationRequestDto.getTitle()) + .setBody(fcmNotificationRequestDto.getBody()) + .build(); + } + + public Message convertToTopicMessage(Notification notification, Topic topic, Map data){ + return Message + .builder() + .setTopic(topic.getTopic()) + .setNotification(notification) + .putAllData(data) + .build(); + } + + public Message convertToDataMessage(Notification notification, Map data, FCMNotificationRequestDto fcmNotificationRequestDto){ + return Message + .builder() + .setToken(fcmNotificationRequestDto.getToken()) + .setNotification(notification) + .putAllData(data) + .build(); + } + + public FCMNotificationRequestDto convertToFcmTopicMessageDto(String body) { + return FCMNotificationRequestDto + .builder() + .title(ALERT_TITLE) + .body(body) + .build(); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/convertor/FcmConvertor.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/convertor/FcmConvertor.java deleted file mode 100644 index 2f0531e2..00000000 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/convertor/FcmConvertor.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.example.matchinfrastructure.fcm.convertor; - -import com.example.matchcommon.annotation.Convertor; - -@Convertor -public class FcmConvertor { - -} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/AlertType.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/AlertType.java new file mode 100644 index 00000000..82b51d1f --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/AlertType.java @@ -0,0 +1,10 @@ +package com.example.matchinfrastructure.fcm.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum AlertType { + PROJECT_SCREEN, HOME_SCREEN, MATCH_SCREEN, EVENT_SCREEN +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/FCMNotificationRequestDto.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/FCMNotificationRequestDto.java index 11c41119..1bf58e3e 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/FCMNotificationRequestDto.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/FCMNotificationRequestDto.java @@ -1,12 +1,12 @@ package com.example.matchinfrastructure.fcm.dto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; @Getter @AllArgsConstructor @NoArgsConstructor +@Setter +@Builder public class FCMNotificationRequestDto { private String title; diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/NotificationPayDto.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/NotificationPayDto.java new file mode 100644 index 00000000..97e4a3ae --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/NotificationPayDto.java @@ -0,0 +1,14 @@ +package com.example.matchinfrastructure.fcm.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class NotificationPayDto { + private AlertType screen; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/service/FcmNotificationService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/service/FcmNotificationService.java index d6824466..4960e578 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/service/FcmNotificationService.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/service/FcmNotificationService.java @@ -1,6 +1,9 @@ package com.example.matchinfrastructure.fcm.service; +import com.example.matchinfrastructure.fcm.converter.FcmConverter; import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import com.example.matchinfrastructure.fcm.dto.NotificationPayDto; +import com.example.matchcommon.constants.enums.Topic; import com.google.auth.oauth2.GoogleCredentials; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; @@ -13,15 +16,21 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import java.util.HashMap; import javax.annotation.PostConstruct; import java.io.IOException; import java.util.List; +import java.util.Map; + +import static com.example.matchinfrastructure.fcm.dto.AlertType.EVENT_SCREEN; +import static com.example.matchinfrastructure.fcm.dto.AlertType.PROJECT_SCREEN; @Service @RequiredArgsConstructor @Slf4j public class FcmNotificationService { + private final FcmConverter fcmConverter; @PostConstruct public void init() { log.info("init"); @@ -45,25 +54,61 @@ public void init() { } } - @Async("fcm") public void testNotification(FCMNotificationRequestDto fcmNotificationRequestDto){ - Notification notification = Notification - .builder() - .setTitle(fcmNotificationRequestDto.getTitle()) - .setBody(fcmNotificationRequestDto.getBody()) - .build(); + Notification notification = fcmConverter.convertToNotification(fcmNotificationRequestDto); Message message = Message .builder() .setToken(fcmNotificationRequestDto.getToken()) .setNotification(notification) .build(); + + sendNotification(message); + + } + + + public void sendNotificationRegularPayments(FCMNotificationRequestDto fcmNotificationRequestDto, NotificationPayDto notificationPayDto){ + Notification notification = fcmConverter.convertToNotification(fcmNotificationRequestDto); + + + Map data = new HashMap<>(); + data.put("screen", notificationPayDto.getScreen().toString()); + + Message message = fcmConverter.convertToDataMessage(notification, data, fcmNotificationRequestDto); + + sendNotification(message); + + + } + + public void sendTopicNotification(FCMNotificationRequestDto fcmNotificationRequestDto, Topic topic, Long id){ + Notification notification = fcmConverter.convertToNotification(fcmNotificationRequestDto); + + Map data = new HashMap<>(); + if(topic.equals(Topic.PROJECT_UPLOAD)){ + data.put("screen", String.valueOf(PROJECT_SCREEN)); + data.put("projectId", String.valueOf(id)); + } + else{ + data.put("screen", String.valueOf(EVENT_SCREEN)); + data.put("eventId", String.valueOf(id)); + } + + Message message = fcmConverter.convertToTopicMessage(notification, topic, data); + + sendNotification(message); + } + + @Async("fcm") + public void sendNotification(Message message){ try { String result = FirebaseMessaging.getInstance().send(message); - System.out.println(result); + log.info(result); } catch (FirebaseMessagingException e) { + log.error(e.getMessage()); throw new RuntimeException(e); } - } + } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/match_aligo/client/MatchAligoFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/match_aligo/client/MatchAligoFeignClient.java index 136a7ee1..8b0226bc 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/match_aligo/client/MatchAligoFeignClient.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/match_aligo/client/MatchAligoFeignClient.java @@ -1,11 +1,13 @@ package com.example.matchinfrastructure.match_aligo.client; import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchinfrastructure.aligo.dto.AlimType; import com.example.matchinfrastructure.match_aligo.config.MatchAligoFeignConfiguration; +import com.example.matchinfrastructure.match_aligo.dto.AlimTalkDto; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @FeignClient( name = "MatchAligoFeignClient", @@ -13,7 +15,20 @@ configuration = MatchAligoFeignConfiguration.class) public interface MatchAligoFeignClient { @GetMapping("/send") - CommonResponse sendSmsAuth(@RequestHeader(name = "X-AUTH-TOKEN") String token, @RequestParam("phone") String phone, @RequestParam("code") String code); + CommonResponse sendSmsAuth(@RequestHeader(name = "X-AUTH-TOKEN") String token, + @RequestParam("phone") String phone, + @RequestParam("code") String code); + + + @PostMapping("/send/alim-talk") + CommonResponse sendAlimTalk(@RequestHeader(name = "X-AUTH-TOKEN") String token, + @RequestParam("alimType")AlimType alimType, + @RequestBody AlimTalkDto alimTalkDto); + @PostMapping("/send/alim-talk-execution") + CommonResponse sendAlimTalks( + @RequestHeader(name = "X-AUTH-TOKEN") String token, + @RequestParam("alimType")AlimType alimType, + @RequestBody List alimTalkDtos); } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/match_aligo/dto/AlimTalkDto.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/match_aligo/dto/AlimTalkDto.java new file mode 100644 index 00000000..baf6e7e8 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/match_aligo/dto/AlimTalkDto.java @@ -0,0 +1,18 @@ +package com.example.matchinfrastructure.match_aligo.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AlimTalkDto { + private Long donationId; + private String name; + private String phone; + //사용처 + private String usages; + //기부 품목 + private String article; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/AppleAuthTokenResponse.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/AppleAuthTokenResponse.java new file mode 100644 index 00000000..e7b51724 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/AppleAuthTokenResponse.java @@ -0,0 +1,21 @@ +package com.example.matchinfrastructure.oauth.apple.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class AppleAuthTokenResponse { + private String access_token; + private String expires_in; + private String id_token; + private String refresh_token; + private String token_type; + private String error; + + +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/service/AppleAuthService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/service/AppleAuthService.java index 8d066737..653e7bd4 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/service/AppleAuthService.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/service/AppleAuthService.java @@ -1,7 +1,10 @@ package com.example.matchinfrastructure.oauth.apple.service; import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.OtherServerException; +import com.example.matchcommon.properties.AppleProperties; import com.example.matchinfrastructure.oauth.apple.client.AppleFeignClient; +import com.example.matchinfrastructure.oauth.apple.dto.AppleAuthTokenResponse; import com.example.matchinfrastructure.oauth.apple.dto.ApplePublicResponse; import com.example.matchinfrastructure.oauth.apple.dto.AppleUserRes; import com.example.matchinfrastructure.oauth.apple.dto.Key; @@ -10,24 +13,53 @@ import com.google.gson.JsonParser; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.openssl.PEMException; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import java.io.*; import java.math.BigInteger; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.KeyFactory; +import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.RSAPublicKeySpec; -import java.util.Base64; -import java.util.List; -import java.util.Objects; - +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +import static com.example.matchcommon.constants.MatchStatic.CLASS_PATH; +import static com.example.matchcommon.constants.MatchStatic.KID; +import static com.example.matchcommon.exception.errorcode.OtherServerErrorCode.OTHER_SERVER_BAD_REQUEST; +import static com.example.matchcommon.exception.errorcode.OtherServerErrorCode.OTHER_SERVER_INTERNAL_SERVER_ERROR; import static com.example.matchinfrastructure.oauth.apple.exception.AppleErrorCode.*; @Service +@Slf4j @RequiredArgsConstructor public class AppleAuthService { private final AppleFeignClient appleFeignClient; + private final AppleProperties appleProperties; + + public AppleUserRes appleLogin(String identityToken) { JsonParser parser = new JsonParser(); ApplePublicResponse applePublicResponse = appleFeignClient.getPublicKey(); @@ -52,8 +84,9 @@ public AppleUserRes appleLogin(String identityToken) { checkValidationInfo(iss, aud); - String appleId = userInfoObject.get("sub").getAsString(); - String email = userInfoObject.get("email").getAsString(); + String appleId = String.valueOf(userInfoObject.get("sub")).replace("\"",""); + System.out.println(appleId); + String email = String.valueOf(userInfoObject.get("email")).replace("\"",""); return new AppleUserRes(email, appleId); } @@ -92,4 +125,98 @@ public PublicKey getPublicKey(Key key) { throw new BadRequestException(FAIL_MAKE_PUBLIC_KEY); } } + + public void revokeUser(String code) { + + String appleAuthToken = generateAuthToken(code); + + if (appleAuthToken != null) { + System.out.println(appleAuthToken); + RestTemplate restTemplate = new RestTemplateBuilder().build(); + String revokeUrl = "https://appleid.apple.com/auth/revoke"; + + LinkedMultiValueMap params = new LinkedMultiValueMap<>(); + params.add("client_id", appleProperties.getBundleId()); + params.add("client_secret", createClientSecret()); + params.add("token", appleAuthToken); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + HttpEntity> httpEntity = new HttpEntity<>(params, headers); + + restTemplate.postForEntity(revokeUrl, httpEntity, String.class); + } + + } + + private String generateAuthToken(String code) { + RestTemplate restTemplate = new RestTemplateBuilder().build(); + String authUrl = "https://appleid.apple.com/auth/token"; + String secret = createClientSecret(); + + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("code", code); + params.add("client_id", appleProperties.getBundleId()); + log.info("secret: " + secret); + params.add("client_secret", secret); + params.add("grant_type", "authorization_code"); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + + HttpEntity> httpEntity = new HttpEntity<>(params, headers); + + log.info(params.toString()); + log.info(String.valueOf(httpEntity)); + try { + ResponseEntity response = restTemplate.postForEntity(authUrl, httpEntity, AppleAuthTokenResponse.class); + log.info("상태코드:"+response.getStatusCode()); + log.info("토큰:"+response.getBody().getAccess_token()); + return response.getBody().getAccess_token(); + } catch (HttpClientErrorException e) { + log.error(String.valueOf(e)); + log.error(e.getMessage()); + throw new OtherServerException(OTHER_SERVER_BAD_REQUEST); + } + } + + private String createClientSecret(){ + Date expirationDate = Date.from(LocalDateTime.now().plusDays(30).atZone(ZoneId.systemDefault()).toInstant()); + Map jwtHeader = new HashMap<>(); + + jwtHeader.put("kid", KID); + jwtHeader.put("alg", "ES256"); + + return Jwts.builder() + .setHeaderParams(jwtHeader) + .setIssuer(appleProperties.getTeamId()) + .setIssuedAt(new Date(System.currentTimeMillis())) // 발행 시간 - UNIX 시간 + .setExpiration(expirationDate) // 만료 시간 + .setAudience("https://appleid.apple.com") + .setSubject(appleProperties.getBundleId()) + .signWith(SignatureAlgorithm.ES256, getPrivateKey()) + .compact(); + } + + private PrivateKey getPrivateKey(){ + ClassPathResource resource = new ClassPathResource(CLASS_PATH); + try (InputStream inputStream = resource.getInputStream()) { + String privateKeyPEM = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)) + .lines().collect(Collectors.joining("\n")); + + try (PEMParser pemParser = new PEMParser(new StringReader(privateKeyPEM))) { + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject(); + return converter.getPrivateKey(object); + } catch (IOException e) { + log.error(String.valueOf(e)); + throw new OtherServerException(OTHER_SERVER_BAD_REQUEST); + } + } catch (IOException e) { + log.error(String.valueOf(e)); + throw new OtherServerException(OTHER_SERVER_BAD_REQUEST); + } + } } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/aop/AopForPayment.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/aop/AopForPayment.java new file mode 100644 index 00000000..df239a86 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/aop/AopForPayment.java @@ -0,0 +1,51 @@ +package com.example.matchinfrastructure.pay.portone.aop; + +import com.example.matchcommon.annotation.PaymentIntercept; +import com.example.matchcommon.aop.KeyGenerator; +import com.example.matchinfrastructure.pay.portone.service.PortOneService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import static com.example.matchcommon.constants.MatchStatic.CANCEL_IMP_UID; +import static com.example.matchcommon.constants.MatchStatic.CANCEL_ORDER_ID; + +@Component +@Aspect +@RequiredArgsConstructor +@Slf4j +public class AopForPayment { + private final PortOneService portOneService; + private final KeyGenerator keyGenerator; + + + // @PaymentValidator 어노테이션이 붙은 메소드에서 예외가 발생하면 이 메소드가 호출됩니다. + @AfterThrowing(pointcut = "execution(* *(..)) && @annotation(paymentIntercept)", throwing = "exception") + public void refundOnPaymentFailure(JoinPoint joinPoint, PaymentIntercept paymentIntercept, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + + String parameter = paymentIntercept.key(); + + String impUid = (String) keyGenerator.getDynamicValue(methodSignature.getParameterNames(), joinPoint.getArgs(), parameter); + + log.info("ERROR OCCUR : " + impUid); + try { + if(parameter.contains(CANCEL_IMP_UID)) { + log.info(CANCEL_IMP_UID + " 값 환불"); + log.error("에러 발생 환불 IMP_UID : " + impUid); + portOneService.refundPayment(impUid); + }else{ + log.info(CANCEL_ORDER_ID + " 값 환불"); + log.error("에러 발생 환불 ORDER_ID : " + impUid); + portOneService.refundPaymentOrderId(impUid); + } + } catch (Exception e) { + log.error("환불 처리 중 에러 발생 IMP_UID : " + impUid); + } + } + +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java index 3c58272e..93bbe987 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java @@ -42,4 +42,11 @@ PortOneResponse preparePayments( @RequestHeader("Authorization") String accessToken, @RequestBody PortOnePrepareReq portOnePrepareReq ); + + + @GetMapping("/payments/prepare/{merchant_uid}") + PortOneResponse getPrepare( + @RequestHeader("Authorization") String accessToken, + @PathVariable("merchant_uid") String merchant_uid + ); } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/converter/PortOneConverter.java similarity index 89% rename from Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java rename to Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/converter/PortOneConverter.java index 17c4fcbf..f8da8437 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/converter/PortOneConverter.java @@ -1,15 +1,15 @@ -package com.example.matchinfrastructure.pay.portone.convertor; +package com.example.matchinfrastructure.pay.portone.converter; -import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.annotation.Converter; import com.example.matchcommon.properties.PortOneProperties; import com.example.matchinfrastructure.pay.portone.dto.req.PayWithBillKeyReq; import com.example.matchinfrastructure.pay.portone.dto.req.PortOneBillReq; import com.example.matchinfrastructure.pay.portone.dto.req.PortOnePrepareReq; import lombok.RequiredArgsConstructor; -@Convertor +@Converter @RequiredArgsConstructor -public class PortOneConvertor { +public class PortOneConverter { private final PortOneProperties portOneProperties; public PortOneBillReq convertToPortOneBill(String cardNo, String expiry, String idNo, String cardPw) { return PortOneBillReq diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PaidStatus.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PaidStatus.java new file mode 100644 index 00000000..37c49a74 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PaidStatus.java @@ -0,0 +1,15 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@AllArgsConstructor +public enum PaidStatus { + paid("성공"), + ready("가상 계좌 발급"), + failed("실패"), + cancelled("결제 취소"); + private final String type; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneWebhook.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneWebhook.java new file mode 100644 index 00000000..24267968 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneWebhook.java @@ -0,0 +1,17 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@ToString +@AllArgsConstructor +@Builder +public class PortOneWebhook { + private String imp_uid; + + private String merchant_uid; + + private PaidStatus status; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/Prepare.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/Prepare.java new file mode 100644 index 00000000..5cf02420 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/Prepare.java @@ -0,0 +1,19 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.*; + +import java.math.BigDecimal; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Prepare { + @SerializedName("merchant_uid") + String merchant_uid; + + @SerializedName("amount") + BigDecimal amount; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java index 588b6a8f..223229ac 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java @@ -6,11 +6,14 @@ import com.example.matchinfrastructure.pay.portone.dto.req.PortOneAuthReq; import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @Service +@Slf4j @RequiredArgsConstructor public class PortOneAuthService { private final PortOneFeignClient portOneFeignClient; @@ -19,12 +22,12 @@ public class PortOneAuthService { @Cacheable(value = "portOneTokenCache", key = "'all'") public String getToken() { String token = fetchPortOneToken(); - System.out.println("request : " + token); + log.info("request : " + token); return token; } public String fetchPortOneToken() { - System.out.println("request token"); + log.info("request token"); return getTokens(); } @@ -33,5 +36,16 @@ public String getTokens() { PortOneResponse portOneResponse = portOneFeignClient.getAccessToken(PortOneAuthReq.builder().imp_key(portOneProperties.getKey()).imp_secret(portOneProperties.getSecret()).build()); return portOneResponse.getResponse().getAccess_token(); } + + public String getAuthToken() { + PortOneResponse portOneResponse = portOneFeignClient.getAccessToken(PortOneAuthReq.builder().imp_key(portOneProperties.getKey()).imp_secret(portOneProperties.getSecret()).build()); + return portOneResponse.getResponse().getAccess_token(); + } + + @Scheduled(fixedRate = 1200000) + public void refreshAuthToken() { + String refreshToken = getTokens(); + log.info("refresh token {} ", refreshToken); + } } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneService.java index 8b820159..2c674a9e 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneService.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneService.java @@ -1,15 +1,50 @@ package com.example.matchinfrastructure.pay.portone.service; -import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; -import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; -import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; -import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchcommon.properties.PortOneProperties; +import com.siot.IamportRestClient.IamportClient; +import com.siot.IamportRestClient.exception.IamportResponseException; +import com.siot.IamportRestClient.request.CancelData; +import com.siot.IamportRestClient.response.IamportResponse; +import com.siot.IamportRestClient.response.Payment; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import static com.example.matchcommon.constants.MatchStatic.REGULAR; +import javax.annotation.PostConstruct; +import java.io.IOException; @Service +@Slf4j @RequiredArgsConstructor public class PortOneService { + private IamportClient iamportClient; + private final PortOneProperties portOneProperties; + + @PostConstruct + private void init() { + this.iamportClient = new IamportClient(portOneProperties.getKey(), portOneProperties.getSecret()); + } + + + public void refundPayment(String impUid) { + try { + IamportResponse paymentIamportResponse = iamportClient.cancelPaymentByImpUid(new CancelData(impUid, true)); + System.out.println(paymentIamportResponse.getCode()); + System.out.println(paymentIamportResponse.getResponse()); + } catch (IamportResponseException | IOException e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } + } + + public void refundPaymentOrderId(String orderId) { + try { + IamportResponse paymentIamportResponse = iamportClient.cancelPaymentByImpUid(new CancelData(orderId, false)); + System.out.println(paymentIamportResponse.getCode()); + System.out.println(paymentIamportResponse.getResponse()); + } catch (IamportResponseException | IOException e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } + } } diff --git a/Match-Infrastructure/src/main/resources/application-infrastructure.yml b/Match-Infrastructure/src/main/resources/application-infrastructure.yml index fa2d047b..5ebba1bd 100644 --- a/Match-Infrastructure/src/main/resources/application-infrastructure.yml +++ b/Match-Infrastructure/src/main/resources/application-infrastructure.yml @@ -3,6 +3,9 @@ feign: config: default: loggerLevel: FULL +logging: + level: + com.example.matchinfrastructure.aligo.client.KakaoAligoFeignClient: DEBUG --- spring: config: diff --git a/README.md b/README.md index b85b54e5..abe4616c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # BackendMatch -## +## 아키텍쳐 +![image](https://github.com/TEAMMatchDev/Match_Server/assets/81962309/66ff5856-b41e-4537-a579-62276b9f2743) + + ## 📂 멀티 모듈 구조 @@ -30,7 +33,7 @@ │        │           ├─ security │        │           └─ user │        │              ├─ controller -│        │              ├─ convertor +│        │              ├─ Converter │        │              ├─ dto │        │              ├─ service │        │              └─ utils