From e5dadde4a151e15c6540a252a5c1cd6ba308cf65 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:28:08 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20Kafka=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../java/com/wellmeet/config/KafkaConfig.java | 44 +++++++++++++++++++ .../consumer/NotificationConsumer.java | 17 +++++++ src/main/resources/application-local.yml | 24 ++++++++++ 4 files changed, 87 insertions(+) create mode 100644 src/main/java/com/wellmeet/config/KafkaConfig.java create mode 100644 src/main/java/com/wellmeet/consumer/NotificationConsumer.java diff --git a/build.gradle b/build.gradle index a4c59f0..28f67a6 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-mail' + implementation 'org.springframework.kafka:spring-kafka' implementation('nl.martijndwars:web-push:5.1.1') { exclude group: 'org.asynchttpclient', module: 'async-http-client' } diff --git a/src/main/java/com/wellmeet/config/KafkaConfig.java b/src/main/java/com/wellmeet/config/KafkaConfig.java new file mode 100644 index 0000000..7e70b33 --- /dev/null +++ b/src/main/java/com/wellmeet/config/KafkaConfig.java @@ -0,0 +1,44 @@ +package com.wellmeet.config; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; + +import java.util.HashMap; +import java.util.Map; + +@EnableKafka +@Configuration +public class KafkaConfig { + + @Value("${spring.kafka.bootstrap-servers}") + private String bootstrapServers; + + @Value("${spring.kafka.consumer.group-id}") + private String groupId; + + @Bean + public ConsumerFactory consumerFactory() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + return new DefaultKafkaConsumerFactory<>(props); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = + new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } +} diff --git a/src/main/java/com/wellmeet/consumer/NotificationConsumer.java b/src/main/java/com/wellmeet/consumer/NotificationConsumer.java new file mode 100644 index 0000000..74328d3 --- /dev/null +++ b/src/main/java/com/wellmeet/consumer/NotificationConsumer.java @@ -0,0 +1,17 @@ +package com.wellmeet.consumer; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class NotificationConsumer { + + @KafkaListener(topics = "notification", groupId = "notification-group") + public void consume(String message) { + log.info("Received message from notification topic: {}", message); + } +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 7a9b59c..6df18b7 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -20,6 +20,28 @@ spring: schema-locations: classpath:schema.sql platform: mysql encoding: UTF-8 + mail: + host: smtp.gmail.com + port: 587 + username: ${secret.mail.username:test@example.com} + password: ${secret.mail.password:testpassword} + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true + timeout: 5000 + connectiontimeout: 5000 + writetimeout: 5000 + kafka: + bootstrap-servers: localhost:9092 + consumer: + group-id: notification-group + auto-offset-reset: earliest + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer cors: origin: http://localhost:5173 @@ -28,3 +50,5 @@ vapid: public-key: ${secret.vapid.public-key} private-key: ${secret.vapid.private-key} subject: ${secret.vapid.subject} +server: + port: 8082 From c9e5ccd1ad595737e8c9fbc1b21be4ebf9e614fe Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:31:15 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CI.yml | 87 ++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/Dev_CI.yml diff --git a/.github/workflows/Dev_CI.yml b/.github/workflows/Dev_CI.yml new file mode 100644 index 0000000..750884a --- /dev/null +++ b/.github/workflows/Dev_CI.yml @@ -0,0 +1,87 @@ +name: dev-ci + +on: + pull_request: + branches: + - develop + workflow_call: + +permissions: + contents: read + checks: write + pull-requests: write + +jobs: + build-and-push: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + TEST_REPORT: true + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: "" + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + MYSQL_DATABASE: wellmeet_noti_test + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + zookeeper: + image: confluentinc/cp-zookeeper:7.0.1 + env: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + ports: + - 2181:2181 + options: >- + --health-cmd="curl -f http://localhost:8080/commands/stat || exit 1" + --health-interval=10s + --health-timeout=10s + --health-retries=5 + + kafka: + image: confluentinc/cp-kafka:7.0.1 + env: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: true + ports: + - 9092:9092 + options: >- + --health-cmd="timeout 10s bash -c 'until printf \"\" 2>>/dev/null >>/dev/tcp/localhost/9092; do sleep 1; done'" + --health-interval=10s + --health-timeout=10s + --health-retries=10 + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Grant Permission + run: chmod +x ./gradlew + + - name: Build With Gradle + run: ./gradlew clean build -x test + + - name: Run Tests With Gradle + run: ./gradlew test From 59fd16ee2530b073c29e14c3b039404c330b59da Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:34:58 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 4b6fd43..7a70e64 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -20,6 +20,13 @@ spring: schema-locations: classpath:schema.sql platform: mysql encoding: UTF-8 + kafka: + bootstrap-servers: localhost:9092 + consumer: + group-id: notification-group + auto-offset-reset: earliest + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer cors: origin: http://localhost:5173 From 16612bf991b764f95f46a5dab670f8b48f52c08a Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:44:16 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/wellmeet/webpush/WebPushServiceTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java b/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java index 5f181d4..99d0751 100644 --- a/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java +++ b/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java @@ -42,7 +42,7 @@ class Subscribe { } @Test - void 동일한_유저_아이디와_엔드포인트로_구독하면_기존_구독을_반환한다() { + void 동일한_유저_아이디와_엔드포인트로_구독하면_기존_구독을_업데이트한다() { String userId = UUID.randomUUID().toString(); String endpoint = "endpoint"; PushSubscription pushSubscription = new PushSubscription(userId, endpoint, "p256dh", "auth"); @@ -55,8 +55,8 @@ class Subscribe { assertAll( () -> assertThat(subscription.getUserId()).isEqualTo(userId), () -> assertThat(subscription.getEndpoint()).isEqualTo(endpoint), - () -> assertThat(subscription.getP256dh()).isEqualTo(pushSubscription.getP256dh()), - () -> assertThat(subscription.getAuth()).isEqualTo(pushSubscription.getAuth()) + () -> assertThat(subscription.getP256dh()).isEqualTo(request.p256dh()), + () -> assertThat(subscription.getAuth()).isEqualTo(request.auth()) ); } } From 68440149b27646f94c3bf36a116b449e55c91737 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:50:10 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CI.yml | 4 ++++ src/main/resources/local-secret.yml | 0 2 files changed, 4 insertions(+) create mode 100644 src/main/resources/local-secret.yml diff --git a/.github/workflows/Dev_CI.yml b/.github/workflows/Dev_CI.yml index 750884a..6283fa6 100644 --- a/.github/workflows/Dev_CI.yml +++ b/.github/workflows/Dev_CI.yml @@ -68,6 +68,10 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Setting dev-secret.yml + run: | + echo "${{ secrets.LOCAL_SECRET_YML }}" > ./api-owner/src/main/resources/local-secret.yml + - name: Set up JDK 21 uses: actions/setup-java@v4 with: diff --git a/src/main/resources/local-secret.yml b/src/main/resources/local-secret.yml new file mode 100644 index 0000000..e69de29 From 686a9a07c59934479c173796103313785343ca7c Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:52:06 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/main/resources/local-secret.yml | 0 2 files changed, 1 insertion(+) delete mode 100644 src/main/resources/local-secret.yml diff --git a/.gitignore b/.gitignore index 5d7bd8d..8586c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ out/ ### VS Code ### .vscode/ /src/main/resources/local-secret.yml +.serena/ diff --git a/src/main/resources/local-secret.yml b/src/main/resources/local-secret.yml deleted file mode 100644 index e69de29..0000000 From 0d3eb61bc9604c0b1042a8533a5d880852a69f22 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:53:59 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- src/main/resources/local-secret.yml | 0 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 src/main/resources/local-secret.yml diff --git a/.gitignore b/.gitignore index 8586c1a..18afe95 100644 --- a/.gitignore +++ b/.gitignore @@ -35,5 +35,5 @@ out/ ### VS Code ### .vscode/ -/src/main/resources/local-secret.yml +#/src/main/resources/local-secret.yml .serena/ diff --git a/src/main/resources/local-secret.yml b/src/main/resources/local-secret.yml new file mode 100644 index 0000000..e69de29 From ba9b9c3c1caa16b15bfbb4c0ea67b4b01af6fc59 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:56:10 +0900 Subject: [PATCH 08/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 18afe95..8586c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -35,5 +35,5 @@ out/ ### VS Code ### .vscode/ -#/src/main/resources/local-secret.yml +/src/main/resources/local-secret.yml .serena/ From 18b83b383e9fc7690c183993cb3b3cfee845a540 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 21:00:45 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat:=20CI=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Dev_CI.yml b/.github/workflows/Dev_CI.yml index 6283fa6..8e32e3c 100644 --- a/.github/workflows/Dev_CI.yml +++ b/.github/workflows/Dev_CI.yml @@ -70,7 +70,7 @@ jobs: - name: Setting dev-secret.yml run: | - echo "${{ secrets.LOCAL_SECRET_YML }}" > ./api-owner/src/main/resources/local-secret.yml + echo "${{ secrets.LOCAL_SECRET_YML }}" > ./src/main/resources/local-secret.yml - name: Set up JDK 21 uses: actions/setup-java@v4 From f033261bb1f2cf006b01bfbdedcec24ba8577e82 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 21:20:22 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20CD=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CD.yml | 78 ++++++++++++++++++++++++++ .github/workflows/Dev_CI.yml | 2 +- scripts/dev/replace-new-version.sh | 40 +++++++++++++ src/main/resources/application-dev.yml | 52 +++++++++++++++++ src/main/resources/dev-secret.yml | 0 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/Dev_CD.yml create mode 100644 scripts/dev/replace-new-version.sh create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/dev-secret.yml diff --git a/.github/workflows/Dev_CD.yml b/.github/workflows/Dev_CD.yml new file mode 100644 index 0000000..274324c --- /dev/null +++ b/.github/workflows/Dev_CD.yml @@ -0,0 +1,78 @@ +name: dev-cd + +on: + push: + branches: + - "develop" + - "SCRUM-117" + +permissions: + contents: read + checks: write + actions: read + pull-requests: write + +jobs: + test: + uses: ./.github/workflows/Dev_CI.yml + + build: + needs: test + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setting dev-secret.yml + run: | + echo "${{ secrets.DEV_SECRET_YML }}" > ./src/main/resources/dev-secret.yml + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Make gradlew executable + run: chmod +x gradlew + + - name: bootJar with Gradle + run: ./gradlew :api-owner:bootJar --info + + - name: Change artifact file name + run: mv build/libs/*.jar build/libs/app.jar + + - name: Upload artifact file + uses: actions/upload-artifact@v4 + with: + name: app-artifact + path: ./build/libs/app.jar + if-no-files-found: error + + - name: Upload deploy scripts + uses: actions/upload-artifact@v4 + with: + name: deploy-scripts + path: ./scripts/dev/ + if-no-files-found: error + + deploy: + needs: build + runs-on: dev-owner + + steps: + - name: Download artifact file + uses: actions/download-artifact@v4 + with: + name: app-artifact + path: ~/app + + - name: Download deploy scripts + uses: actions/download-artifact@v4 + with: + name: deploy-scripts + path: ~/app/scripts + + - name: Replace application to latest + run: sudo sh ~/app/scripts/replace-new-version.sh diff --git a/.github/workflows/Dev_CI.yml b/.github/workflows/Dev_CI.yml index 8e32e3c..d9d5192 100644 --- a/.github/workflows/Dev_CI.yml +++ b/.github/workflows/Dev_CI.yml @@ -68,7 +68,7 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Setting dev-secret.yml + - name: Setting local-secret.yml run: | echo "${{ secrets.LOCAL_SECRET_YML }}" > ./src/main/resources/local-secret.yml diff --git a/scripts/dev/replace-new-version.sh b/scripts/dev/replace-new-version.sh new file mode 100644 index 0000000..8f3900c --- /dev/null +++ b/scripts/dev/replace-new-version.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +PID=$(lsof -t -i:8080) + +# 프로세스 종료 +if [ -z "$PID" ]; then + echo "No process is using port 8080." +else + echo "Killing process with PID: $PID" + kill -15 "$PID" + + # 직전 명령(프로세스 종료 명령)이 정상 동작했는지 확인 + if [ $? -eq 0 ]; then + echo "Process $PID terminated successfully." + else + echo "Failed to terminate process $PID." + fi +fi + +JAR_FILE=$(ls /home/ubuntu/app/*.jar | head -n 1) + +echo "JAR 파일 실행: $JAR_FILE" + +# 애플리케이션 로그 파일 설정 +APP_LOG_DIR="/home/ubuntu/app/logs" +APP_LOG_FILE="$APP_LOG_DIR/application-$(date +%Y%m%d-%H%M%S).log" + +echo "애플리케이션 로그 파일: $APP_LOG_FILE" + +sudo nohup java \ + -Dspring.profiles.active=dev \ + -Duser.timezone=Asia/Seoul \ + -Dserver.port=8080 \ + -Ddd.service=wellmeet-notification \ + -Ddd.env=dev \ + -jar "$JAR_FILE" > "$APP_LOG_FILE" 2>&1 & + +echo "애플리케이션이 백그라운드에서 실행되었습니다." +echo "로그 확인: tail -f $APP_LOG_FILE" +echo "=== 배포 완료 ===" diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..a723c3c --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,52 @@ +spring: + application: + name: wellmeet-notification + config: + import: classpath:dev-secret.yml + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://${secret.datasource.url}:${secret.datasource.port}/${secret.datasource.database} + username: ${secret.datasource.username} + password: ${secret.datasource.password} + jpa: + show-sql: true + properties: + hibernate: + format_sql: true + dialect: org.hibernate.dialect.MySQLDialect + sql: + init: + mode: always + schema-locations: classpath:schema.sql + platform: mysql + encoding: UTF-8 + mail: + host: smtp.gmail.com + port: 587 + username: ${secret.mail.username} + password: ${secret.mail.password} + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true + timeout: 5000 + connectiontimeout: 5000 + writetimeout: 5000 + kafka: + bootstrap-servers: ${secret.kafka.bootstrap-servers} + consumer: + group-id: ${secret.kafka.consumer.group-id} + auto-offset-reset: ${secret.kafka.consumer.auto-offset-reset} + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + +cors: + origin: http://localhost:5173 + +vapid: + public-key: ${secret.vapid.public-key} + private-key: ${secret.vapid.private-key} + subject: ${secret.vapid.subject} diff --git a/src/main/resources/dev-secret.yml b/src/main/resources/dev-secret.yml new file mode 100644 index 0000000..e69de29 From 6c0b07c10a0ae87d08b209ba7e3df9b0291fec6c Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 21:29:44 +0900 Subject: [PATCH 11/15] =?UTF-8?q?feat:=20CD=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CD.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Dev_CD.yml b/.github/workflows/Dev_CD.yml index 274324c..0ffc006 100644 --- a/.github/workflows/Dev_CD.yml +++ b/.github/workflows/Dev_CD.yml @@ -15,6 +15,7 @@ permissions: jobs: test: uses: ./.github/workflows/Dev_CI.yml + secrets: inherit build: needs: test From 4d271b45e1fa26f6e82ae03df8609ae4bd473d43 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 21:33:53 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20CD=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Dev_CD.yml b/.github/workflows/Dev_CD.yml index 0ffc006..77431e0 100644 --- a/.github/workflows/Dev_CD.yml +++ b/.github/workflows/Dev_CD.yml @@ -39,7 +39,7 @@ jobs: run: chmod +x gradlew - name: bootJar with Gradle - run: ./gradlew :api-owner:bootJar --info + run: ./gradlew bootJar --info - name: Change artifact file name run: mv build/libs/*.jar build/libs/app.jar From ddbcc1b78945c0fe1b6af18e7db42004151cdc3d Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 21:42:36 +0900 Subject: [PATCH 13/15] =?UTF-8?q?feat:=20CD=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Dev_CD.yml b/.github/workflows/Dev_CD.yml index 77431e0..774b713 100644 --- a/.github/workflows/Dev_CD.yml +++ b/.github/workflows/Dev_CD.yml @@ -60,7 +60,7 @@ jobs: deploy: needs: build - runs-on: dev-owner + runs-on: dev steps: - name: Download artifact file From e162c4edd1e5785eb97992beb4f69a947648c436 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Tue, 23 Sep 2025 16:47:59 +0900 Subject: [PATCH 14/15] =?UTF-8?q?chore:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wellmeet/config/KafkaConfig.java | 19 ++++++++----- .../consumer/NotificationConsumer.java | 5 ++-- .../consumer/dto/MessageHeader.java | 14 ++++++++++ .../consumer/dto/NotificationInfo.java | 13 +++++++++ .../consumer/dto/NotificationMessage.java | 13 +++++++++ .../consumer/dto/NotificationPayload.java | 4 +++ .../consumer/dto/NotificationType.java | 16 +++++++++++ .../dto/ReservationCreatedPayload.java | 15 +++++++++++ .../domain/OwnerNotificationEnabled.java | 27 +++++++++++++++++++ .../webpush/WebPushController.java | 10 +++---- .../webpush/WebPushService.java | 18 +++++++------ .../webpush/domain/PushSubscription.java | 2 +- .../webpush/dto/SubscribeRequest.java | 4 +-- .../webpush/dto/SubscribeResponse.java | 4 +-- .../webpush/dto/TestPushRequest.java | 2 +- .../webpush/dto/UnsubscribeRequest.java | 2 +- .../webpush/infrastructure/WebPushSender.java | 6 ++--- .../PushSubscriptionRepository.java | 4 +-- src/main/resources/local-secret.yml | 5 ++++ .../java/com/wellmeet/BaseControllerTest.java | 2 +- .../java/com/wellmeet/BaseServiceTest.java | 2 +- .../webpush/WebPushControllerTest.java | 10 +++---- .../wellmeet/webpush/WebPushServiceTest.java | 9 ++++--- 23 files changed, 161 insertions(+), 45 deletions(-) rename src/main/java/com/wellmeet/{ => notification}/consumer/NotificationConsumer.java (71%) create mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/MessageHeader.java create mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java create mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java create mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/NotificationPayload.java create mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java create mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/ReservationCreatedPayload.java create mode 100644 src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java rename src/main/java/com/wellmeet/{ => notification}/webpush/WebPushController.java (83%) rename src/main/java/com/wellmeet/{ => notification}/webpush/WebPushService.java (80%) rename src/main/java/com/wellmeet/{ => notification}/webpush/domain/PushSubscription.java (96%) rename src/main/java/com/wellmeet/{ => notification}/webpush/dto/SubscribeRequest.java (87%) rename src/main/java/com/wellmeet/{ => notification}/webpush/dto/SubscribeResponse.java (80%) rename src/main/java/com/wellmeet/{ => notification}/webpush/dto/TestPushRequest.java (85%) rename src/main/java/com/wellmeet/{ => notification}/webpush/dto/UnsubscribeRequest.java (73%) rename src/main/java/com/wellmeet/{ => notification}/webpush/infrastructure/WebPushSender.java (94%) rename src/main/java/com/wellmeet/{ => notification}/webpush/repository/PushSubscriptionRepository.java (78%) diff --git a/src/main/java/com/wellmeet/config/KafkaConfig.java b/src/main/java/com/wellmeet/config/KafkaConfig.java index 7e70b33..ddc96ae 100644 --- a/src/main/java/com/wellmeet/config/KafkaConfig.java +++ b/src/main/java/com/wellmeet/config/KafkaConfig.java @@ -1,5 +1,8 @@ package com.wellmeet.config; +import com.wellmeet.notification.consumer.dto.NotificationMessage; +import java.util.HashMap; +import java.util.Map; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.common.serialization.StringDeserializer; import org.springframework.beans.factory.annotation.Value; @@ -9,9 +12,7 @@ import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; import org.springframework.kafka.core.ConsumerFactory; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; - -import java.util.HashMap; -import java.util.Map; +import org.springframework.kafka.support.serializer.JsonDeserializer; @EnableKafka @Configuration @@ -24,19 +25,23 @@ public class KafkaConfig { private String groupId; @Bean - public ConsumerFactory consumerFactory() { + public ConsumerFactory consumerFactory() { Map props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); - props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + + props.put(JsonDeserializer.USE_TYPE_INFO_HEADERS, false); + props.put(JsonDeserializer.TRUSTED_PACKAGES, "com.wellmeet.consumer.dto"); + props.put(JsonDeserializer.VALUE_DEFAULT_TYPE, NotificationMessage.class); return new DefaultKafkaConsumerFactory<>(props); } @Bean - public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { - ConcurrentKafkaListenerContainerFactory factory = + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(consumerFactory()); return factory; diff --git a/src/main/java/com/wellmeet/consumer/NotificationConsumer.java b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java similarity index 71% rename from src/main/java/com/wellmeet/consumer/NotificationConsumer.java rename to src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java index 74328d3..a5f819a 100644 --- a/src/main/java/com/wellmeet/consumer/NotificationConsumer.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java @@ -1,5 +1,6 @@ -package com.wellmeet.consumer; +package com.wellmeet.notification.consumer; +import com.wellmeet.notification.consumer.dto.NotificationMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; @@ -11,7 +12,7 @@ public class NotificationConsumer { @KafkaListener(topics = "notification", groupId = "notification-group") - public void consume(String message) { + public void consume(NotificationMessage message) { log.info("Received message from notification topic: {}", message); } } diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/MessageHeader.java b/src/main/java/com/wellmeet/notification/consumer/dto/MessageHeader.java new file mode 100644 index 0000000..2ac3a81 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/dto/MessageHeader.java @@ -0,0 +1,14 @@ +package com.wellmeet.notification.consumer.dto; + +import java.time.LocalDateTime; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class MessageHeader { + + private String messageId; + private LocalDateTime timestamp; + private String source; +} diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java new file mode 100644 index 0000000..1e7c62d --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java @@ -0,0 +1,13 @@ +package com.wellmeet.notification.consumer.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class NotificationInfo { + + private String type; + private String category; + private String recipient; +} diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java new file mode 100644 index 0000000..acec9f3 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java @@ -0,0 +1,13 @@ +package com.wellmeet.notification.consumer.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class NotificationMessage { + + private MessageHeader header; + private NotificationInfo notification; + private NotificationPayload payload; +} diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationPayload.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationPayload.java new file mode 100644 index 0000000..54ea0b3 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationPayload.java @@ -0,0 +1,4 @@ +package com.wellmeet.notification.consumer.dto; + +public abstract class NotificationPayload { +} diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java new file mode 100644 index 0000000..ba2021e --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java @@ -0,0 +1,16 @@ +package com.wellmeet.notification.consumer.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum NotificationType { + + RESERVATION_CREATED("reservation.created", "HIGH", "notification", "wellmeet-user-server"); + + private final String name; + private final String category; + private final String topic; + private final String source; +} diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/ReservationCreatedPayload.java b/src/main/java/com/wellmeet/notification/consumer/dto/ReservationCreatedPayload.java new file mode 100644 index 0000000..d4dc70e --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/dto/ReservationCreatedPayload.java @@ -0,0 +1,15 @@ +package com.wellmeet.notification.consumer.dto; + +import java.time.LocalDateTime; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ReservationCreatedPayload extends NotificationPayload { + + private Long reservationId; + private String customerName; + private LocalDateTime reservationTime; + private int partySize; +} diff --git a/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java b/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java new file mode 100644 index 0000000..dbd0fed --- /dev/null +++ b/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java @@ -0,0 +1,27 @@ +package com.wellmeet.notification.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OwnerNotificationEnabled { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String ownerId; + private boolean reservationRequestWebPushEnabled; + private boolean reservationRequestEmailEnabled; + private boolean reservationCancelWebPushEnabled; + private boolean reservationCancelEmailEnabled; + private boolean promotionWebPushEnabled; + private boolean promotionEmailEnabled; +} diff --git a/src/main/java/com/wellmeet/webpush/WebPushController.java b/src/main/java/com/wellmeet/notification/webpush/WebPushController.java similarity index 83% rename from src/main/java/com/wellmeet/webpush/WebPushController.java rename to src/main/java/com/wellmeet/notification/webpush/WebPushController.java index 17b2fd4..e2678be 100644 --- a/src/main/java/com/wellmeet/webpush/WebPushController.java +++ b/src/main/java/com/wellmeet/notification/webpush/WebPushController.java @@ -1,9 +1,9 @@ -package com.wellmeet.webpush; +package com.wellmeet.notification.webpush; -import com.wellmeet.webpush.dto.SubscribeRequest; -import com.wellmeet.webpush.dto.SubscribeResponse; -import com.wellmeet.webpush.dto.TestPushRequest; -import com.wellmeet.webpush.dto.UnsubscribeRequest; +import com.wellmeet.notification.webpush.dto.SubscribeRequest; +import com.wellmeet.notification.webpush.dto.SubscribeResponse; +import com.wellmeet.notification.webpush.dto.TestPushRequest; +import com.wellmeet.notification.webpush.dto.UnsubscribeRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/src/main/java/com/wellmeet/webpush/WebPushService.java b/src/main/java/com/wellmeet/notification/webpush/WebPushService.java similarity index 80% rename from src/main/java/com/wellmeet/webpush/WebPushService.java rename to src/main/java/com/wellmeet/notification/webpush/WebPushService.java index 8784251..616a2c2 100644 --- a/src/main/java/com/wellmeet/webpush/WebPushService.java +++ b/src/main/java/com/wellmeet/notification/webpush/WebPushService.java @@ -1,14 +1,14 @@ -package com.wellmeet.webpush; +package com.wellmeet.notification.webpush; import com.wellmeet.exception.ErrorCode; import com.wellmeet.exception.WellMeetNotificationException; -import com.wellmeet.webpush.domain.PushSubscription; -import com.wellmeet.webpush.dto.SubscribeRequest; -import com.wellmeet.webpush.dto.SubscribeResponse; -import com.wellmeet.webpush.dto.TestPushRequest; -import com.wellmeet.webpush.dto.UnsubscribeRequest; -import com.wellmeet.webpush.infrastructure.WebPushSender; -import com.wellmeet.webpush.repository.PushSubscriptionRepository; +import com.wellmeet.notification.webpush.domain.PushSubscription; +import com.wellmeet.notification.webpush.dto.SubscribeRequest; +import com.wellmeet.notification.webpush.dto.SubscribeResponse; +import com.wellmeet.notification.webpush.dto.TestPushRequest; +import com.wellmeet.notification.webpush.dto.UnsubscribeRequest; +import com.wellmeet.notification.webpush.infrastructure.WebPushSender; +import com.wellmeet.notification.webpush.repository.PushSubscriptionRepository; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -28,11 +28,13 @@ public SubscribeResponse subscribe(String userId, SubscribeRequest request) { Optional pushSubscription = existingSubscriptions.stream() .filter(subscription -> subscription.isSameEndpoint(request.endpoint())) .findAny(); + if (pushSubscription.isPresent()) { PushSubscription subscription = pushSubscription.get(); subscription.update(request.toDomain(userId)); return new SubscribeResponse(subscription); } + PushSubscription subscription = request.toDomain(userId); PushSubscription savedSubscription = pushSubscriptionRepository.save(subscription); return new SubscribeResponse(savedSubscription); diff --git a/src/main/java/com/wellmeet/webpush/domain/PushSubscription.java b/src/main/java/com/wellmeet/notification/webpush/domain/PushSubscription.java similarity index 96% rename from src/main/java/com/wellmeet/webpush/domain/PushSubscription.java rename to src/main/java/com/wellmeet/notification/webpush/domain/PushSubscription.java index 488a428..9520142 100644 --- a/src/main/java/com/wellmeet/webpush/domain/PushSubscription.java +++ b/src/main/java/com/wellmeet/notification/webpush/domain/PushSubscription.java @@ -1,4 +1,4 @@ -package com.wellmeet.webpush.domain; +package com.wellmeet.notification.webpush.domain; import com.wellmeet.common.domain.BaseEntity; import jakarta.persistence.Entity; diff --git a/src/main/java/com/wellmeet/webpush/dto/SubscribeRequest.java b/src/main/java/com/wellmeet/notification/webpush/dto/SubscribeRequest.java similarity index 87% rename from src/main/java/com/wellmeet/webpush/dto/SubscribeRequest.java rename to src/main/java/com/wellmeet/notification/webpush/dto/SubscribeRequest.java index b1fb8fa..3281cc9 100644 --- a/src/main/java/com/wellmeet/webpush/dto/SubscribeRequest.java +++ b/src/main/java/com/wellmeet/notification/webpush/dto/SubscribeRequest.java @@ -1,6 +1,6 @@ -package com.wellmeet.webpush.dto; +package com.wellmeet.notification.webpush.dto; -import com.wellmeet.webpush.domain.PushSubscription; +import com.wellmeet.notification.webpush.domain.PushSubscription; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/wellmeet/webpush/dto/SubscribeResponse.java b/src/main/java/com/wellmeet/notification/webpush/dto/SubscribeResponse.java similarity index 80% rename from src/main/java/com/wellmeet/webpush/dto/SubscribeResponse.java rename to src/main/java/com/wellmeet/notification/webpush/dto/SubscribeResponse.java index 832cc1d..0202344 100644 --- a/src/main/java/com/wellmeet/webpush/dto/SubscribeResponse.java +++ b/src/main/java/com/wellmeet/notification/webpush/dto/SubscribeResponse.java @@ -1,6 +1,6 @@ -package com.wellmeet.webpush.dto; +package com.wellmeet.notification.webpush.dto; -import com.wellmeet.webpush.domain.PushSubscription; +import com.wellmeet.notification.webpush.domain.PushSubscription; public record SubscribeResponse( Long subscriptionId, diff --git a/src/main/java/com/wellmeet/webpush/dto/TestPushRequest.java b/src/main/java/com/wellmeet/notification/webpush/dto/TestPushRequest.java similarity index 85% rename from src/main/java/com/wellmeet/webpush/dto/TestPushRequest.java rename to src/main/java/com/wellmeet/notification/webpush/dto/TestPushRequest.java index 133edf1..d4bc6fa 100644 --- a/src/main/java/com/wellmeet/webpush/dto/TestPushRequest.java +++ b/src/main/java/com/wellmeet/notification/webpush/dto/TestPushRequest.java @@ -1,4 +1,4 @@ -package com.wellmeet.webpush.dto; +package com.wellmeet.notification.webpush.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/wellmeet/webpush/dto/UnsubscribeRequest.java b/src/main/java/com/wellmeet/notification/webpush/dto/UnsubscribeRequest.java similarity index 73% rename from src/main/java/com/wellmeet/webpush/dto/UnsubscribeRequest.java rename to src/main/java/com/wellmeet/notification/webpush/dto/UnsubscribeRequest.java index eb8833e..a8cbbec 100644 --- a/src/main/java/com/wellmeet/webpush/dto/UnsubscribeRequest.java +++ b/src/main/java/com/wellmeet/notification/webpush/dto/UnsubscribeRequest.java @@ -1,4 +1,4 @@ -package com.wellmeet.webpush.dto; +package com.wellmeet.notification.webpush.dto; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/wellmeet/webpush/infrastructure/WebPushSender.java b/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java similarity index 94% rename from src/main/java/com/wellmeet/webpush/infrastructure/WebPushSender.java rename to src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java index 2bee5e4..c95956f 100644 --- a/src/main/java/com/wellmeet/webpush/infrastructure/WebPushSender.java +++ b/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java @@ -1,11 +1,11 @@ -package com.wellmeet.webpush.infrastructure; +package com.wellmeet.notification.webpush.infrastructure; import com.fasterxml.jackson.databind.ObjectMapper; import com.wellmeet.config.VapidConfig; import com.wellmeet.exception.ErrorCode; import com.wellmeet.exception.WellMeetNotificationException; -import com.wellmeet.webpush.domain.PushSubscription; -import com.wellmeet.webpush.dto.TestPushRequest; +import com.wellmeet.notification.webpush.domain.PushSubscription; +import com.wellmeet.notification.webpush.dto.TestPushRequest; import jakarta.annotation.PostConstruct; import java.io.IOException; import java.security.GeneralSecurityException; diff --git a/src/main/java/com/wellmeet/webpush/repository/PushSubscriptionRepository.java b/src/main/java/com/wellmeet/notification/webpush/repository/PushSubscriptionRepository.java similarity index 78% rename from src/main/java/com/wellmeet/webpush/repository/PushSubscriptionRepository.java rename to src/main/java/com/wellmeet/notification/webpush/repository/PushSubscriptionRepository.java index 5b60aa9..51e31bb 100644 --- a/src/main/java/com/wellmeet/webpush/repository/PushSubscriptionRepository.java +++ b/src/main/java/com/wellmeet/notification/webpush/repository/PushSubscriptionRepository.java @@ -1,6 +1,6 @@ -package com.wellmeet.webpush.repository; +package com.wellmeet.notification.webpush.repository; -import com.wellmeet.webpush.domain.PushSubscription; +import com.wellmeet.notification.webpush.domain.PushSubscription; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/resources/local-secret.yml b/src/main/resources/local-secret.yml index e69de29..b0ab58e 100644 --- a/src/main/resources/local-secret.yml +++ b/src/main/resources/local-secret.yml @@ -0,0 +1,5 @@ +secret: + vapid: + public-key: BCjLRdYi3EapfKAjZlIONNWb7PgUGnSo9-HDedbcd02o0zwriW-93jZ35Ufqu_C4jFtcKuHCdsGA_3TYyAHXqxs + private-key: LjC3sekYvWtxxtN6R4qEEUunAI592EcpK8bc1Ggy8tU + subject: mailto:admin@wellmeet.com diff --git a/src/test/java/com/wellmeet/BaseControllerTest.java b/src/test/java/com/wellmeet/BaseControllerTest.java index caa2ebd..e669f4d 100644 --- a/src/test/java/com/wellmeet/BaseControllerTest.java +++ b/src/test/java/com/wellmeet/BaseControllerTest.java @@ -1,6 +1,6 @@ package com.wellmeet; -import com.wellmeet.webpush.repository.PushSubscriptionRepository; +import com.wellmeet.notification.webpush.repository.PushSubscriptionRepository; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; diff --git a/src/test/java/com/wellmeet/BaseServiceTest.java b/src/test/java/com/wellmeet/BaseServiceTest.java index d1813aa..dbe7792 100644 --- a/src/test/java/com/wellmeet/BaseServiceTest.java +++ b/src/test/java/com/wellmeet/BaseServiceTest.java @@ -1,6 +1,6 @@ package com.wellmeet; -import com.wellmeet.webpush.repository.PushSubscriptionRepository; +import com.wellmeet.notification.webpush.repository.PushSubscriptionRepository; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; diff --git a/src/test/java/com/wellmeet/webpush/WebPushControllerTest.java b/src/test/java/com/wellmeet/webpush/WebPushControllerTest.java index 8091f6c..69fbfef 100644 --- a/src/test/java/com/wellmeet/webpush/WebPushControllerTest.java +++ b/src/test/java/com/wellmeet/webpush/WebPushControllerTest.java @@ -5,11 +5,11 @@ import com.wellmeet.BaseControllerTest; import com.wellmeet.fixture.NullAndEmptyAndBlankSource; -import com.wellmeet.webpush.domain.PushSubscription; -import com.wellmeet.webpush.dto.SubscribeRequest; -import com.wellmeet.webpush.dto.SubscribeResponse; -import com.wellmeet.webpush.dto.TestPushRequest; -import com.wellmeet.webpush.dto.UnsubscribeRequest; +import com.wellmeet.notification.webpush.domain.PushSubscription; +import com.wellmeet.notification.webpush.dto.SubscribeRequest; +import com.wellmeet.notification.webpush.dto.SubscribeResponse; +import com.wellmeet.notification.webpush.dto.TestPushRequest; +import com.wellmeet.notification.webpush.dto.UnsubscribeRequest; import io.restassured.http.ContentType; import java.util.Map; import org.junit.jupiter.api.Nested; diff --git a/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java b/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java index 99d0751..8a94b96 100644 --- a/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java +++ b/src/test/java/com/wellmeet/webpush/WebPushServiceTest.java @@ -7,10 +7,11 @@ import com.wellmeet.BaseServiceTest; import com.wellmeet.exception.ErrorCode; import com.wellmeet.exception.WellMeetNotificationException; -import com.wellmeet.webpush.domain.PushSubscription; -import com.wellmeet.webpush.dto.SubscribeRequest; -import com.wellmeet.webpush.dto.SubscribeResponse; -import com.wellmeet.webpush.dto.UnsubscribeRequest; +import com.wellmeet.notification.webpush.WebPushService; +import com.wellmeet.notification.webpush.domain.PushSubscription; +import com.wellmeet.notification.webpush.dto.SubscribeRequest; +import com.wellmeet.notification.webpush.dto.SubscribeResponse; +import com.wellmeet.notification.webpush.dto.UnsubscribeRequest; import java.util.Optional; import java.util.UUID; import org.junit.jupiter.api.Nested; From 743dc12991e498096202da7c9b8da8447ca5ed30 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 2 Oct 2025 12:09:03 +0900 Subject: [PATCH 15/15] =?UTF-8?q?refactor:=20=EC=BB=A8=EC=8A=88=EB=A8=B8?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=9E=AC=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumer/NotificationChecker.java | 19 +++++++++++++++ .../consumer/NotificationConsumer.java | 8 ++++++- .../consumer/NotificationSender.java | 20 ++++++++++++++++ .../consumer/dto/NotificationInfo.java | 1 + .../consumer/dto/NotificationType.java | 3 +-- .../domain/NotificationHistory.java | 19 +++++++++++++++ .../domain/OwnerNotificationEnabled.java | 5 ---- .../domain/UserNotificationEnabled.java | 24 +++++++++++++++++++ .../NotificationHistoryRepository.java | 9 +++++++ .../OwnerNotificationEnabledRepository.java | 9 +++++++ .../UserNotificationEnabledRepository.java | 9 +++++++ 11 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java create mode 100644 src/main/java/com/wellmeet/notification/consumer/NotificationSender.java create mode 100644 src/main/java/com/wellmeet/notification/domain/NotificationHistory.java create mode 100644 src/main/java/com/wellmeet/notification/domain/UserNotificationEnabled.java create mode 100644 src/main/java/com/wellmeet/notification/repository/NotificationHistoryRepository.java create mode 100644 src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java create mode 100644 src/main/java/com/wellmeet/notification/repository/UserNotificationEnabledRepository.java diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java b/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java new file mode 100644 index 0000000..9184440 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java @@ -0,0 +1,19 @@ +package com.wellmeet.notification.consumer; + +import com.wellmeet.notification.repository.OwnerNotificationEnabledRepository; +import com.wellmeet.notification.repository.UserNotificationEnabledRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class NotificationChecker { + + private final OwnerNotificationEnabledRepository ownerNotificationEnabledRepository; + private final UserNotificationEnabledRepository userNotificationEnabledRepository; + + public List check(String recipient, String recipientType) { + return null; + } +} diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java index a5f819a..3cf1f67 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java @@ -1,6 +1,7 @@ package com.wellmeet.notification.consumer; import com.wellmeet.notification.consumer.dto.NotificationMessage; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; @@ -11,8 +12,13 @@ @RequiredArgsConstructor public class NotificationConsumer { + private final NotificationChecker notificationChecker; + private final NotificationSender notificationSender; + @KafkaListener(topics = "notification", groupId = "notification-group") public void consume(NotificationMessage message) { - log.info("Received message from notification topic: {}", message); + List enabled = notificationChecker.check(message.getNotification().getRecipient(), + message.getNotification().getRecipientType()); + notificationSender.send(message, enabled); } } diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java new file mode 100644 index 0000000..98b22e9 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java @@ -0,0 +1,20 @@ +package com.wellmeet.notification.consumer; + +import com.wellmeet.notification.consumer.dto.NotificationMessage; +import com.wellmeet.notification.repository.NotificationHistoryRepository; +import com.wellmeet.notification.webpush.WebPushService; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class NotificationSender { + + private final WebPushService webPushService; + private final NotificationHistoryRepository notificationHistoryRepository; + + public void send(NotificationMessage payload, List enabled) { + + } +} diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java index 1e7c62d..559ba94 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java @@ -10,4 +10,5 @@ public class NotificationInfo { private String type; private String category; private String recipient; + private String recipientType; } diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java index ba2021e..0584827 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java @@ -7,10 +7,9 @@ @RequiredArgsConstructor public enum NotificationType { - RESERVATION_CREATED("reservation.created", "HIGH", "notification", "wellmeet-user-server"); + RESERVATION_CREATED("reservation.created", "HIGH", "wellmeet-user-server"); private final String name; private final String category; - private final String topic; private final String source; } diff --git a/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java b/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java new file mode 100644 index 0000000..4517219 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java @@ -0,0 +1,19 @@ +package com.wellmeet.notification.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class NotificationHistory { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java b/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java index dbd0fed..dfbe60f 100644 --- a/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java +++ b/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java @@ -1,8 +1,6 @@ package com.wellmeet.notification.domain; import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import lombok.AccessLevel; import lombok.Getter; @@ -14,9 +12,6 @@ public class OwnerNotificationEnabled { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - private String ownerId; private boolean reservationRequestWebPushEnabled; private boolean reservationRequestEmailEnabled; diff --git a/src/main/java/com/wellmeet/notification/domain/UserNotificationEnabled.java b/src/main/java/com/wellmeet/notification/domain/UserNotificationEnabled.java new file mode 100644 index 0000000..797e4c1 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/domain/UserNotificationEnabled.java @@ -0,0 +1,24 @@ +package com.wellmeet.notification.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class UserNotificationEnabled { + + @Id + private String userId; + private boolean reservationConfirmWebPushEnabled; + private boolean reservationConfirmEmailEnabled; + private boolean reservationCancelWebPushEnabled; + private boolean reservationCancelEmailEnabled; + private boolean reminderWebPushEnabled; + private boolean reminderEmailEnabled; + private boolean promotionWebPushEnabled; + private boolean promotionEmailEnabled; +} diff --git a/src/main/java/com/wellmeet/notification/repository/NotificationHistoryRepository.java b/src/main/java/com/wellmeet/notification/repository/NotificationHistoryRepository.java new file mode 100644 index 0000000..d7681b2 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/repository/NotificationHistoryRepository.java @@ -0,0 +1,9 @@ +package com.wellmeet.notification.repository; + +import com.wellmeet.notification.domain.NotificationHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface NotificationHistoryRepository extends JpaRepository { +} diff --git a/src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java b/src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java new file mode 100644 index 0000000..4dd2c02 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java @@ -0,0 +1,9 @@ +package com.wellmeet.notification.repository; + +import com.wellmeet.notification.domain.OwnerNotificationEnabled; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface OwnerNotificationEnabledRepository extends JpaRepository { +} diff --git a/src/main/java/com/wellmeet/notification/repository/UserNotificationEnabledRepository.java b/src/main/java/com/wellmeet/notification/repository/UserNotificationEnabledRepository.java new file mode 100644 index 0000000..6271ac5 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/repository/UserNotificationEnabledRepository.java @@ -0,0 +1,9 @@ +package com.wellmeet.notification.repository; + +import com.wellmeet.notification.domain.UserNotificationEnabled; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserNotificationEnabledRepository extends JpaRepository { +}