From e5dadde4a151e15c6540a252a5c1cd6ba308cf65 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 28 Aug 2025 20:28:08 +0900 Subject: [PATCH 01/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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/33] =?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 { +} From 818e796f331a6296cffb8f5709b8a832e669405f Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 2 Oct 2025 17:16:54 +0900 Subject: [PATCH 16/33] =?UTF-8?q?refactor:=20NotificationEnabled=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumer/NotificationChecker.java | 6 ++--- .../consumer/NotificationConsumer.java | 8 +++--- .../consumer/dto/NotificationInfo.java | 1 - .../consumer/dto/NotificationMessage.java | 3 ++- .../consumer/dto/NotificationPayload.java | 4 --- .../consumer/dto/NotificationType.java | 3 +-- .../dto/ReservationCreatedPayload.java | 15 ----------- .../domain/NotificationChannel.java | 4 +++ .../domain/NotificationEnabled.java | 27 +++++++++++++++++++ .../domain/OwnerNotificationEnabled.java | 22 --------------- .../domain/UserNotificationEnabled.java | 24 ----------------- ...ava => NotificationEnabledRepository.java} | 4 +-- .../OwnerNotificationEnabledRepository.java | 9 ------- 13 files changed, 41 insertions(+), 89 deletions(-) delete mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/NotificationPayload.java delete mode 100644 src/main/java/com/wellmeet/notification/consumer/dto/ReservationCreatedPayload.java create mode 100644 src/main/java/com/wellmeet/notification/domain/NotificationChannel.java create mode 100644 src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java delete mode 100644 src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java delete mode 100644 src/main/java/com/wellmeet/notification/domain/UserNotificationEnabled.java rename src/main/java/com/wellmeet/notification/repository/{UserNotificationEnabledRepository.java => NotificationEnabledRepository.java} (50%) delete mode 100644 src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java b/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java index 9184440..0cc4d60 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java @@ -1,7 +1,6 @@ package com.wellmeet.notification.consumer; -import com.wellmeet.notification.repository.OwnerNotificationEnabledRepository; -import com.wellmeet.notification.repository.UserNotificationEnabledRepository; +import com.wellmeet.notification.repository.NotificationEnabledRepository; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -10,8 +9,7 @@ @RequiredArgsConstructor public class NotificationChecker { - private final OwnerNotificationEnabledRepository ownerNotificationEnabledRepository; - private final UserNotificationEnabledRepository userNotificationEnabledRepository; + private final NotificationEnabledRepository notificationEnabledRepository; 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 3cf1f67..ac5c03a 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java @@ -1,7 +1,7 @@ package com.wellmeet.notification.consumer; import com.wellmeet.notification.consumer.dto.NotificationMessage; -import java.util.List; +import com.wellmeet.notification.repository.NotificationEnabledRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; @@ -12,13 +12,11 @@ @RequiredArgsConstructor public class NotificationConsumer { - private final NotificationChecker notificationChecker; + private final NotificationEnabledRepository notificationEnabledRepository; private final NotificationSender notificationSender; @KafkaListener(topics = "notification", groupId = "notification-group") public void consume(NotificationMessage 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/dto/NotificationInfo.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java index 559ba94..7e8fc4c 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java @@ -8,7 +8,6 @@ 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/NotificationMessage.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java index acec9f3..f7c74f0 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java @@ -1,5 +1,6 @@ package com.wellmeet.notification.consumer.dto; +import java.util.Map; import lombok.Getter; import lombok.NoArgsConstructor; @@ -9,5 +10,5 @@ public class NotificationMessage { private MessageHeader header; private NotificationInfo notification; - private NotificationPayload payload; + private Map 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 deleted file mode 100644 index 54ea0b3..0000000 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationPayload.java +++ /dev/null @@ -1,4 +0,0 @@ -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 index 0584827..9c64184 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java @@ -7,9 +7,8 @@ @RequiredArgsConstructor public enum NotificationType { - RESERVATION_CREATED("reservation.created", "HIGH", "wellmeet-user-server"); + RESERVATION_CREATED("reservation.created", "wellmeet-user-server"); private final String name; - private final String category; 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 deleted file mode 100644 index d4dc70e..0000000 --- a/src/main/java/com/wellmeet/notification/consumer/dto/ReservationCreatedPayload.java +++ /dev/null @@ -1,15 +0,0 @@ -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/NotificationChannel.java b/src/main/java/com/wellmeet/notification/domain/NotificationChannel.java new file mode 100644 index 0000000..a6ca86d --- /dev/null +++ b/src/main/java/com/wellmeet/notification/domain/NotificationChannel.java @@ -0,0 +1,4 @@ +package com.wellmeet.notification.domain; + +public enum NotificationChannel { +} diff --git a/src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java b/src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java new file mode 100644 index 0000000..54e6cba --- /dev/null +++ b/src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java @@ -0,0 +1,27 @@ +package com.wellmeet.notification.domain; + +import com.wellmeet.notification.consumer.dto.NotificationType; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class NotificationEnabled { + + @Id + private String userId; + + @Enumerated(value = EnumType.STRING) + private NotificationType type; + + @Enumerated(value = EnumType.STRING) + private NotificationChannel channel; + + private boolean enabled; +} diff --git a/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java b/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java deleted file mode 100644 index dfbe60f..0000000 --- a/src/main/java/com/wellmeet/notification/domain/OwnerNotificationEnabled.java +++ /dev/null @@ -1,22 +0,0 @@ -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 OwnerNotificationEnabled { - - @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/notification/domain/UserNotificationEnabled.java b/src/main/java/com/wellmeet/notification/domain/UserNotificationEnabled.java deleted file mode 100644 index 797e4c1..0000000 --- a/src/main/java/com/wellmeet/notification/domain/UserNotificationEnabled.java +++ /dev/null @@ -1,24 +0,0 @@ -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/UserNotificationEnabledRepository.java b/src/main/java/com/wellmeet/notification/repository/NotificationEnabledRepository.java similarity index 50% rename from src/main/java/com/wellmeet/notification/repository/UserNotificationEnabledRepository.java rename to src/main/java/com/wellmeet/notification/repository/NotificationEnabledRepository.java index 6271ac5..94fa09d 100644 --- a/src/main/java/com/wellmeet/notification/repository/UserNotificationEnabledRepository.java +++ b/src/main/java/com/wellmeet/notification/repository/NotificationEnabledRepository.java @@ -1,9 +1,9 @@ package com.wellmeet.notification.repository; -import com.wellmeet.notification.domain.UserNotificationEnabled; +import com.wellmeet.notification.domain.NotificationEnabled; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface UserNotificationEnabledRepository extends JpaRepository { +public interface NotificationEnabledRepository extends JpaRepository { } diff --git a/src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java b/src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java deleted file mode 100644 index 4dd2c02..0000000 --- a/src/main/java/com/wellmeet/notification/repository/OwnerNotificationEnabledRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -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 { -} From bab454350e5d370c11b9cd98deefc8c9c1929419 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 2 Oct 2025 19:12:33 +0900 Subject: [PATCH 17/33] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=B0=9C=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wellmeet/notification/Sender.java | 11 +++++++++++ .../consumer/NotificationConsumer.java | 8 +++++++- .../notification/consumer/NotificationSender.java | 15 +++++++++++---- .../consumer/dto/NotificationInfo.java | 3 +-- .../consumer/dto/NotificationType.java | 7 +------ .../notification/domain/NotificationChannel.java | 3 +++ .../repository/NotificationEnabledRepository.java | 4 ++++ .../webpush/infrastructure/WebPushSender.java | 15 ++++++++++++++- 8 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/wellmeet/notification/Sender.java diff --git a/src/main/java/com/wellmeet/notification/Sender.java b/src/main/java/com/wellmeet/notification/Sender.java new file mode 100644 index 0000000..8b4f302 --- /dev/null +++ b/src/main/java/com/wellmeet/notification/Sender.java @@ -0,0 +1,11 @@ +package com.wellmeet.notification; + +import com.wellmeet.notification.consumer.dto.NotificationMessage; +import com.wellmeet.notification.domain.NotificationChannel; + +public interface Sender { + + public boolean isEnabled(NotificationChannel channel); + + public void send(NotificationMessage message); +} diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java index ac5c03a..fbb141c 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java @@ -1,7 +1,9 @@ package com.wellmeet.notification.consumer; import com.wellmeet.notification.consumer.dto.NotificationMessage; +import com.wellmeet.notification.domain.NotificationEnabled; import com.wellmeet.notification.repository.NotificationEnabledRepository; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; @@ -17,6 +19,10 @@ public class NotificationConsumer { @KafkaListener(topics = "notification", groupId = "notification-group") public void consume(NotificationMessage message) { - + List enableds = notificationEnabledRepository.findByUserIdAndType( + message.getNotification().getRecipient(), + message.getNotification().getType() + ); + notificationSender.send(message, enableds); } } diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java index 98b22e9..b5e5c83 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java @@ -1,8 +1,9 @@ package com.wellmeet.notification.consumer; +import com.wellmeet.notification.Sender; import com.wellmeet.notification.consumer.dto.NotificationMessage; +import com.wellmeet.notification.domain.NotificationEnabled; import com.wellmeet.notification.repository.NotificationHistoryRepository; -import com.wellmeet.notification.webpush.WebPushService; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -11,10 +12,16 @@ @RequiredArgsConstructor public class NotificationSender { - private final WebPushService webPushService; + private final List senders; private final NotificationHistoryRepository notificationHistoryRepository; - public void send(NotificationMessage payload, List enabled) { - + public void send(NotificationMessage message, List enables) { + for (NotificationEnabled enabled : enables) { + Sender sender = senders.stream() + .filter(low -> low.isEnabled(enabled.getChannel())) + .findFirst() + .orElseThrow(); + sender.send(message); + } } } 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 7e8fc4c..e6a226f 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationInfo.java @@ -7,7 +7,6 @@ @NoArgsConstructor public class NotificationInfo { - private String type; + private NotificationType type; 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 9c64184..fd4f05f 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationType.java @@ -1,14 +1,9 @@ package com.wellmeet.notification.consumer.dto; import lombok.Getter; -import lombok.RequiredArgsConstructor; @Getter -@RequiredArgsConstructor public enum NotificationType { - RESERVATION_CREATED("reservation.created", "wellmeet-user-server"); - - private final String name; - private final String source; + RESERVATION_CREATED } diff --git a/src/main/java/com/wellmeet/notification/domain/NotificationChannel.java b/src/main/java/com/wellmeet/notification/domain/NotificationChannel.java index a6ca86d..6f47047 100644 --- a/src/main/java/com/wellmeet/notification/domain/NotificationChannel.java +++ b/src/main/java/com/wellmeet/notification/domain/NotificationChannel.java @@ -1,4 +1,7 @@ package com.wellmeet.notification.domain; public enum NotificationChannel { + + WEB_PUSH, + EMAIL, } diff --git a/src/main/java/com/wellmeet/notification/repository/NotificationEnabledRepository.java b/src/main/java/com/wellmeet/notification/repository/NotificationEnabledRepository.java index 94fa09d..26f26ee 100644 --- a/src/main/java/com/wellmeet/notification/repository/NotificationEnabledRepository.java +++ b/src/main/java/com/wellmeet/notification/repository/NotificationEnabledRepository.java @@ -1,9 +1,13 @@ package com.wellmeet.notification.repository; +import com.wellmeet.notification.consumer.dto.NotificationType; import com.wellmeet.notification.domain.NotificationEnabled; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface NotificationEnabledRepository extends JpaRepository { + + List findByUserIdAndType(String userId, NotificationType type); } diff --git a/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java b/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java index c95956f..9c5c140 100644 --- a/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java +++ b/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java @@ -4,6 +4,9 @@ import com.wellmeet.config.VapidConfig; import com.wellmeet.exception.ErrorCode; import com.wellmeet.exception.WellMeetNotificationException; +import com.wellmeet.notification.Sender; +import com.wellmeet.notification.consumer.dto.NotificationMessage; +import com.wellmeet.notification.domain.NotificationChannel; import com.wellmeet.notification.webpush.domain.PushSubscription; import com.wellmeet.notification.webpush.dto.TestPushRequest; import jakarta.annotation.PostConstruct; @@ -24,7 +27,7 @@ @Service @RequiredArgsConstructor -public class WebPushSender { +public class WebPushSender implements Sender { private final VapidConfig vapidConfig; private final ObjectMapper objectMapper = new ObjectMapper(); @@ -43,6 +46,16 @@ public void init() { } } + @Override + public boolean isEnabled(NotificationChannel channel) { + return NotificationChannel.WEB_PUSH == channel; + } + + @Override + public void send(NotificationMessage message) { + + } + public void send(PushSubscription subscription, TestPushRequest request) { Keys keys = new Keys(subscription.getP256dh(), subscription.getAuth()); Subscription sub = new Subscription(subscription.getEndpoint(), keys); From a47767eca8e83329181975547506944277ee22c2 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 2 Oct 2025 19:54:33 +0900 Subject: [PATCH 18/33] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=B0=9C=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumer/NotificationSender.java | 2 ++ .../domain/NotificationHistory.java | 6 ++++ .../notification/webpush/WebPushService.java | 6 ++-- .../webpush/infrastructure/WebPushSender.java | 30 +++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java index b5e5c83..603ce50 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java @@ -3,6 +3,7 @@ import com.wellmeet.notification.Sender; import com.wellmeet.notification.consumer.dto.NotificationMessage; import com.wellmeet.notification.domain.NotificationEnabled; +import com.wellmeet.notification.domain.NotificationHistory; import com.wellmeet.notification.repository.NotificationHistoryRepository; import java.util.List; import lombok.RequiredArgsConstructor; @@ -22,6 +23,7 @@ public void send(NotificationMessage message, List enables) .findFirst() .orElseThrow(); sender.send(message); + notificationHistoryRepository.save(new NotificationHistory(message.getNotification().getRecipient())); } } } diff --git a/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java b/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java index 4517219..e847475 100644 --- a/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java +++ b/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java @@ -16,4 +16,10 @@ public class NotificationHistory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + private String userId; + + public NotificationHistory(String userId) { + this.userId = userId; + } } diff --git a/src/main/java/com/wellmeet/notification/webpush/WebPushService.java b/src/main/java/com/wellmeet/notification/webpush/WebPushService.java index 616a2c2..6785a06 100644 --- a/src/main/java/com/wellmeet/notification/webpush/WebPushService.java +++ b/src/main/java/com/wellmeet/notification/webpush/WebPushService.java @@ -20,7 +20,7 @@ public class WebPushService { private final PushSubscriptionRepository pushSubscriptionRepository; - private final WebPushSender pushService; + private final WebPushSender webPushSender; @Transactional public SubscribeResponse subscribe(String userId, SubscribeRequest request) { @@ -34,7 +34,7 @@ public SubscribeResponse subscribe(String userId, SubscribeRequest request) { subscription.update(request.toDomain(userId)); return new SubscribeResponse(subscription); } - + PushSubscription subscription = request.toDomain(userId); PushSubscription savedSubscription = pushSubscriptionRepository.save(subscription); return new SubscribeResponse(savedSubscription); @@ -46,7 +46,7 @@ public void sendTestPush(String userId, TestPushRequest request) { throw new WellMeetNotificationException(ErrorCode.SUBSCRIPTION_NOT_FOUND); } - subscriptions.forEach(subscription -> pushService.send(subscription, request)); + subscriptions.forEach(subscription -> webPushSender.send(subscription, request)); } @Transactional diff --git a/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java b/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java index 9c5c140..42c09f9 100644 --- a/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java +++ b/src/main/java/com/wellmeet/notification/webpush/infrastructure/WebPushSender.java @@ -9,11 +9,13 @@ import com.wellmeet.notification.domain.NotificationChannel; import com.wellmeet.notification.webpush.domain.PushSubscription; import com.wellmeet.notification.webpush.dto.TestPushRequest; +import com.wellmeet.notification.webpush.repository.PushSubscriptionRepository; import jakarta.annotation.PostConstruct; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.Security; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import lombok.RequiredArgsConstructor; @@ -29,6 +31,7 @@ @RequiredArgsConstructor public class WebPushSender implements Sender { + private final PushSubscriptionRepository pushSubscriptionRepository; private final VapidConfig vapidConfig; private final ObjectMapper objectMapper = new ObjectMapper(); private PushService pushService; @@ -53,7 +56,34 @@ public boolean isEnabled(NotificationChannel channel) { @Override public void send(NotificationMessage message) { + List subscriptions = pushSubscriptionRepository.findByUserId( + message.getNotification().getRecipient()); + if (subscriptions.isEmpty()) { + throw new WellMeetNotificationException(ErrorCode.SUBSCRIPTION_NOT_FOUND); + } + + subscriptions.forEach(subscription -> { + Keys keys = new Keys(subscription.getP256dh(), subscription.getAuth()); + Subscription sub = new Subscription(subscription.getEndpoint(), keys); + Map notificationPayload = getNotificationPayload(message); + webPushSend(notificationPayload, sub); + }); + } + + private Map getNotificationPayload(NotificationMessage message) { + Map notificationPayload = new HashMap<>(); + notificationPayload.put("title", "WellMeet 알림"); + notificationPayload.put("body", message.getPayload()); + notificationPayload.put("icon", "/icon-192x192.png"); + notificationPayload.put("badge", "/badge-72x72.png"); + notificationPayload.put("vibrate", new int[]{100, 50, 100}); + notificationPayload.put("requireInteraction", false); + Map defaultData = new HashMap<>(); + defaultData.put("url", "/notifications"); + defaultData.put("timestamp", System.currentTimeMillis()); + notificationPayload.put("data", defaultData); + return notificationPayload; } public void send(PushSubscription subscription, TestPushRequest request) { From 95c2db1c6e774dee7670417bd620a0f4dfa60df0 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 11:57:30 +0900 Subject: [PATCH 19/33] =?UTF-8?q?chore:=20cd=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= 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 deletion(-) diff --git a/.github/workflows/Dev_CD.yml b/.github/workflows/Dev_CD.yml index 774b713..cff61a5 100644 --- a/.github/workflows/Dev_CD.yml +++ b/.github/workflows/Dev_CD.yml @@ -4,7 +4,6 @@ on: push: branches: - "develop" - - "SCRUM-117" permissions: contents: read From 0bc8e9e4aae452b467c075b0031d3fa6f2fee22c Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 11:58:31 +0900 Subject: [PATCH 20/33] =?UTF-8?q?refactor:=20=EB=B3=80=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumer/NotificationChecker.java | 17 ----------------- .../consumer/NotificationConsumer.java | 5 +++-- 2 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java b/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java deleted file mode 100644 index 0cc4d60..0000000 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationChecker.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.wellmeet.notification.consumer; - -import com.wellmeet.notification.repository.NotificationEnabledRepository; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class NotificationChecker { - - private final NotificationEnabledRepository notificationEnabledRepository; - - 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 fbb141c..6f4eaf8 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java @@ -19,10 +19,11 @@ public class NotificationConsumer { @KafkaListener(topics = "notification", groupId = "notification-group") public void consume(NotificationMessage message) { - List enableds = notificationEnabledRepository.findByUserIdAndType( + log.info("Received message: {}", message); + List enables = notificationEnabledRepository.findByUserIdAndType( message.getNotification().getRecipient(), message.getNotification().getType() ); - notificationSender.send(message, enableds); + notificationSender.send(message, enables); } } From c33a28e63944e7a786809a4f474368b5df93c8f4 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 11:58:46 +0900 Subject: [PATCH 21/33] =?UTF-8?q?refactor:=20sql=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/schema.sql | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index c870e07..6fa2dc7 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,10 +1,29 @@ CREATE TABLE IF NOT EXISTS push_subscription ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(255) NOT NULL, - endpoint VARCHAR(255) NOT NULL, - p256dh VARCHAR(100) NOT NULL, - auth VARCHAR(100) NOT NULL, + endpoint VARCHAR(500) NOT NULL, + p256dh VARCHAR(255) NOT NULL, + auth VARCHAR(255) NOT NULL, active BOOLEAN NOT NULL DEFAULT TRUE, created_at DATETIME(6) NOT NULL, - updated_at DATETIME(6) NOT NULL + updated_at DATETIME(6) NOT NULL, + INDEX idx_user_id (user_id), + INDEX idx_endpoint (endpoint) +); + +CREATE TABLE IF NOT EXISTS notification_enabled ( + user_id VARCHAR(255) PRIMARY KEY, + type VARCHAR(50) NOT NULL, + channel VARCHAR(50) NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + INDEX idx_type_channel (type, channel) +); + +CREATE TABLE IF NOT EXISTS notification_history ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id VARCHAR(255) NOT NULL, + created_at DATETIME(6), + updated_at DATETIME(6), + INDEX idx_user_id (user_id), + INDEX idx_created_at (created_at) ); From 8e7b48019bb6c1ebd3773b3b65241330ae3ae56b Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:00:16 +0900 Subject: [PATCH 22/33] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=B0=9C=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wellmeet/notification/consumer/NotificationSender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java index 603ce50..12d0aa2 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java @@ -18,12 +18,12 @@ public class NotificationSender { public void send(NotificationMessage message, List enables) { for (NotificationEnabled enabled : enables) { + notificationHistoryRepository.save(new NotificationHistory(message.getNotification().getRecipient())); Sender sender = senders.stream() .filter(low -> low.isEnabled(enabled.getChannel())) .findFirst() .orElseThrow(); sender.send(message); - notificationHistoryRepository.save(new NotificationHistory(message.getNotification().getRecipient())); } } } From a3f42c1dca39f8bce01b666c9abf30f27d3009d6 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:01:09 +0900 Subject: [PATCH 23/33] =?UTF-8?q?refactor:=20=EC=A0=91=EA=B7=BC=20?= =?UTF-8?q?=EC=A0=9C=ED=95=9C=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/wellmeet/notification/Sender.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/wellmeet/notification/Sender.java b/src/main/java/com/wellmeet/notification/Sender.java index 8b4f302..785cbdc 100644 --- a/src/main/java/com/wellmeet/notification/Sender.java +++ b/src/main/java/com/wellmeet/notification/Sender.java @@ -5,7 +5,7 @@ public interface Sender { - public boolean isEnabled(NotificationChannel channel); + boolean isEnabled(NotificationChannel channel); - public void send(NotificationMessage message); + void send(NotificationMessage message); } From 21282c06d80da3f1d01db0b5de710ee52d5fae32 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:10:54 +0900 Subject: [PATCH 24/33] =?UTF-8?q?refactor:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/wellmeet/DataBaseCleaner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/wellmeet/DataBaseCleaner.java b/src/test/java/com/wellmeet/DataBaseCleaner.java index b219456..071bf3d 100644 --- a/src/test/java/com/wellmeet/DataBaseCleaner.java +++ b/src/test/java/com/wellmeet/DataBaseCleaner.java @@ -40,7 +40,7 @@ private List findTableNames(EntityManager em) { String tableNameSelectQuery = """ SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = 'test' + WHERE TABLE_SCHEMA = 'wellmeet_noti_test' AND TABLE_TYPE = 'BASE TABLE' """; From 8c23522c98acab8fb767fc582c44a3301740ce60 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:14:46 +0900 Subject: [PATCH 25/33] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wellmeet/notification/domain/NotificationEnabled.java | 5 +++++ src/main/resources/schema.sql | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java b/src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java index 54e6cba..2000e5b 100644 --- a/src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java +++ b/src/main/java/com/wellmeet/notification/domain/NotificationEnabled.java @@ -4,6 +4,8 @@ import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import lombok.AccessLevel; import lombok.Getter; @@ -15,6 +17,9 @@ public class NotificationEnabled { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String userId; @Enumerated(value = EnumType.STRING) diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 6fa2dc7..4941468 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -12,10 +12,12 @@ CREATE TABLE IF NOT EXISTS push_subscription ( ); CREATE TABLE IF NOT EXISTS notification_enabled ( - user_id VARCHAR(255) PRIMARY KEY, + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id VARCHAR(255) NOT NULL, type VARCHAR(50) NOT NULL, channel VARCHAR(50) NOT NULL, enabled BOOLEAN NOT NULL DEFAULT TRUE, + INDEX idx_user_id (user_id), INDEX idx_type_channel (type, channel) ); From 8a63bf9a95e2e64c04f91c9332eac8f528fe7828 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:15:58 +0900 Subject: [PATCH 26/33] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B9=85=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wellmeet/notification/consumer/NotificationConsumer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java index 6f4eaf8..998189f 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationConsumer.java @@ -19,7 +19,6 @@ public class NotificationConsumer { @KafkaListener(topics = "notification", groupId = "notification-group") public void consume(NotificationMessage message) { - log.info("Received message: {}", message); List enables = notificationEnabledRepository.findByUserIdAndType( message.getNotification().getRecipient(), message.getNotification().getType() From 6ad2375455077c5d04f397636256db2cd6369921 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:26:40 +0900 Subject: [PATCH 27/33] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/consumer/NotificationSender.java | 4 +++- .../consumer/dto/NotificationMessage.java | 4 ++++ .../notification/domain/NotificationHistory.java | 13 +++++++++++-- src/main/resources/schema.sql | 2 ++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java index 12d0aa2..585afe8 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java @@ -18,7 +18,9 @@ public class NotificationSender { public void send(NotificationMessage message, List enables) { for (NotificationEnabled enabled : enables) { - notificationHistoryRepository.save(new NotificationHistory(message.getNotification().getRecipient())); + NotificationHistory history = new NotificationHistory(message.getRecipient(), + message.getPayload().toString()); + notificationHistoryRepository.save(history); Sender sender = senders.stream() .filter(low -> low.isEnabled(enabled.getChannel())) .findFirst() diff --git a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java index f7c74f0..e8086cc 100644 --- a/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java +++ b/src/main/java/com/wellmeet/notification/consumer/dto/NotificationMessage.java @@ -11,4 +11,8 @@ public class NotificationMessage { private MessageHeader header; private NotificationInfo notification; private Map payload; + + public String getRecipient() { + return notification.getRecipient(); + } } diff --git a/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java b/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java index e847475..b9a586e 100644 --- a/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java +++ b/src/main/java/com/wellmeet/notification/domain/NotificationHistory.java @@ -1,5 +1,6 @@ package com.wellmeet.notification.domain; +import com.wellmeet.common.domain.BaseEntity; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -11,15 +12,23 @@ @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class NotificationHistory { +public class NotificationHistory extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String userId; + private boolean readStatus; + private String contents; - public NotificationHistory(String userId) { + public NotificationHistory(String userId, String contents) { this.userId = userId; + this.readStatus = false; + this.contents = contents; + } + + public void markAsRead() { + this.readStatus = true; } } diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 4941468..29757b2 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -24,6 +24,8 @@ CREATE TABLE IF NOT EXISTS notification_enabled ( CREATE TABLE IF NOT EXISTS notification_history ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(255) NOT NULL, + read_status BOOLEAN NOT NULL DEFAULT FALSE, + contents TEXT, created_at DATETIME(6), updated_at DATETIME(6), INDEX idx_user_id (user_id), From ef393b7ea76044643ab71d0ec3ca3c94c9d2ef03 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:29:57 +0900 Subject: [PATCH 28/33] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=B0=9C=EC=86=A1=20=EC=98=88=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/wellmeet/exception/ErrorCode.java | 2 +- .../notification/consumer/NotificationSender.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/wellmeet/exception/ErrorCode.java b/src/main/java/com/wellmeet/exception/ErrorCode.java index b68947d..9d27760 100644 --- a/src/main/java/com/wellmeet/exception/ErrorCode.java +++ b/src/main/java/com/wellmeet/exception/ErrorCode.java @@ -16,7 +16,7 @@ public enum ErrorCode { INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류가 발생했습니다."), CORS_ORIGIN_EMPTY(HttpStatus.INTERNAL_SERVER_ERROR, "CORS Origin 은 적어도 한 개 있어야 합니다"), WEB_PUSH_SEND_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "웹 푸시 전송에 실패했습니다."), - ; + SENDER_NOT_FOUND(HttpStatus.BAD_REQUEST, "알림을 발송할 수 없습니다."); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java index 585afe8..17dff12 100644 --- a/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java +++ b/src/main/java/com/wellmeet/notification/consumer/NotificationSender.java @@ -1,5 +1,7 @@ package com.wellmeet.notification.consumer; +import com.wellmeet.exception.ErrorCode; +import com.wellmeet.exception.WellMeetNotificationException; import com.wellmeet.notification.Sender; import com.wellmeet.notification.consumer.dto.NotificationMessage; import com.wellmeet.notification.domain.NotificationEnabled; @@ -17,14 +19,14 @@ public class NotificationSender { private final NotificationHistoryRepository notificationHistoryRepository; public void send(NotificationMessage message, List enables) { + NotificationHistory history = new NotificationHistory(message.getRecipient(), + message.getPayload().toString()); for (NotificationEnabled enabled : enables) { - NotificationHistory history = new NotificationHistory(message.getRecipient(), - message.getPayload().toString()); notificationHistoryRepository.save(history); Sender sender = senders.stream() .filter(low -> low.isEnabled(enabled.getChannel())) .findFirst() - .orElseThrow(); + .orElseThrow(() -> new WellMeetNotificationException(ErrorCode.SENDER_NOT_FOUND)); sender.send(message); } } From dc5a9930038c958b901b87b7b4cca4642b7a6b7a Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:40:12 +0900 Subject: [PATCH 29/33] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/wellmeet/config/KafkaConfig.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/wellmeet/config/KafkaConfig.java b/src/main/java/com/wellmeet/config/KafkaConfig.java index ddc96ae..d8a4d8d 100644 --- a/src/main/java/com/wellmeet/config/KafkaConfig.java +++ b/src/main/java/com/wellmeet/config/KafkaConfig.java @@ -34,7 +34,6 @@ public ConsumerFactory consumerFactory() { 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); } From 785b6728880a76154800cb2cc699f9dfe7f66b3d Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:42:49 +0900 Subject: [PATCH 30/33] =?UTF-8?q?refactor:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-local.yml | 2 +- src/main/resources/application-test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index a723c3c..51a294e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -41,7 +41,7 @@ spring: 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 + value-deserializer: org.apache.kafka.common.serialization.JsonSerializer cors: origin: http://localhost:5173 diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 6df18b7..b0c76b3 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -41,7 +41,7 @@ spring: group-id: notification-group auto-offset-reset: earliest key-deserializer: org.apache.kafka.common.serialization.StringDeserializer - value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.JsonSerializer cors: origin: http://localhost:5173 diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 7a70e64..6095f98 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -26,7 +26,7 @@ spring: group-id: notification-group auto-offset-reset: earliest key-deserializer: org.apache.kafka.common.serialization.StringDeserializer - value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.JsonSerializer cors: origin: http://localhost:5173 From 4a924e906203ccbd48a36c84cfab4a8773619686 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:43:49 +0900 Subject: [PATCH 31/33] =?UTF-8?q?refactor:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/local-secret.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/resources/local-secret.yml b/src/main/resources/local-secret.yml index b0ab58e..e69de29 100644 --- a/src/main/resources/local-secret.yml +++ b/src/main/resources/local-secret.yml @@ -1,5 +0,0 @@ -secret: - vapid: - public-key: BCjLRdYi3EapfKAjZlIONNWb7PgUGnSo9-HDedbcd02o0zwriW-93jZ35Ufqu_C4jFtcKuHCdsGA_3TYyAHXqxs - private-key: LjC3sekYvWtxxtN6R4qEEUunAI592EcpK8bc1Ggy8tU - subject: mailto:admin@wellmeet.com From bd3cae6e09a3adb0894ca8f4718d6308da6d8a53 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 12:55:39 +0900 Subject: [PATCH 32/33] =?UTF-8?q?refactor:=20deserializer=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-local.yml | 2 +- src/main/resources/application-test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 51a294e..023f46d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -41,7 +41,7 @@ spring: 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.JsonSerializer + value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer cors: origin: http://localhost:5173 diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index b0c76b3..7c29757 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -41,7 +41,7 @@ spring: group-id: notification-group auto-offset-reset: earliest key-deserializer: org.apache.kafka.common.serialization.StringDeserializer - value-deserializer: org.apache.kafka.common.serialization.JsonSerializer + value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer cors: origin: http://localhost:5173 diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 6095f98..a5e49d5 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -26,7 +26,7 @@ spring: group-id: notification-group auto-offset-reset: earliest key-deserializer: org.apache.kafka.common.serialization.StringDeserializer - value-deserializer: org.apache.kafka.common.serialization.JsonSerializer + value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer cors: origin: http://localhost:5173 From 7ec23bc45823e9c9077df10a3baf42f65130a15e Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sat, 4 Oct 2025 13:12:28 +0900 Subject: [PATCH 33/33] =?UTF-8?q?refactor:=20ci=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/Dev_CI.yml b/.github/workflows/Dev_CI.yml index d9d5192..2c1bb2e 100644 --- a/.github/workflows/Dev_CI.yml +++ b/.github/workflows/Dev_CI.yml @@ -67,6 +67,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} - name: Setting local-secret.yml run: |