diff --git a/.github/pr-labeler.yml b/.github/pr-labeler.yml new file mode 100644 index 000000000..cc61b832b --- /dev/null +++ b/.github/pr-labeler.yml @@ -0,0 +1,6 @@ +기능: feature/* +버그: fix/* +리팩터링: refactor/* +문서: docs/* +테스트: test/* +인프라: infra/* diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml new file mode 100644 index 000000000..712c71bc1 --- /dev/null +++ b/.github/workflows/pr-labeler.yml @@ -0,0 +1,12 @@ +name: PR Labeler +on: + pull_request: + types: [ opened ] + +jobs: + pr-labeler: + runs-on: ubuntu-latest + steps: + - uses: TimonVS/pr-labeler-action@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/main/java/in/koreatech/koin/global/domain/notification/model/Notification.java b/src/main/java/in/koreatech/koin/global/domain/notification/model/Notification.java index c5ead4348..58d5509df 100644 --- a/src/main/java/in/koreatech/koin/global/domain/notification/model/Notification.java +++ b/src/main/java/in/koreatech/koin/global/domain/notification/model/Notification.java @@ -1,19 +1,21 @@ package in.koreatech.koin.global.domain.notification.model; +import static jakarta.persistence.EnumType.STRING; +import static jakarta.persistence.FetchType.LAZY; +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PROTECTED; + import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.global.domain.BaseEntity; +import in.koreatech.koin.global.fcm.MobileAppPath; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import static jakarta.persistence.EnumType.STRING; import jakarta.persistence.Enumerated; -import static jakarta.persistence.FetchType.LAZY; import jakarta.persistence.GeneratedValue; -import static jakarta.persistence.GenerationType.IDENTITY; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; -import static lombok.AccessLevel.PROTECTED; import lombok.Getter; import lombok.NoArgsConstructor; @@ -27,8 +29,9 @@ public class Notification extends BaseEntity { @GeneratedValue(strategy = IDENTITY) private Long id; - @Column(name = "url", nullable = false) - private String url; + @Enumerated(STRING) + @Column(name = "app_path", nullable = false) + private MobileAppPath mobileAppPath; @Column(name = "title", nullable = false) private String title; @@ -51,14 +54,14 @@ public class Notification extends BaseEntity { private boolean isRead = false; public Notification( - String url, + MobileAppPath appPath, String title, String message, String imageUrl, NotificationType type, User user ) { - this.url = url; + this.mobileAppPath = appPath; this.title = title; this.message = message; this.imageUrl = imageUrl; diff --git a/src/main/java/in/koreatech/koin/global/domain/notification/model/NotificationFactory.java b/src/main/java/in/koreatech/koin/global/domain/notification/model/NotificationFactory.java index b59ba57be..fae534c34 100644 --- a/src/main/java/in/koreatech/koin/global/domain/notification/model/NotificationFactory.java +++ b/src/main/java/in/koreatech/koin/global/domain/notification/model/NotificationFactory.java @@ -1,27 +1,20 @@ package in.koreatech.koin.global.domain.notification.model; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.global.fcm.MobileAppPath; @Component public class NotificationFactory { - private final String koinAppUrl; - - public NotificationFactory( - @Value("${fcm.koin.url}") String koinAppUrl - ) { - this.koinAppUrl = koinAppUrl; - } - public Notification generateOwnerNotification( + MobileAppPath path, String shopName, User target ) { return new Notification( - koinAppUrl, + path, "새로운 이벤트가 개설되었어요!", "%s 가게의 이벤트가 오픈되었어요!🎁" .formatted(shopName), diff --git a/src/main/java/in/koreatech/koin/global/domain/notification/service/NotificationService.java b/src/main/java/in/koreatech/koin/global/domain/notification/service/NotificationService.java index ff248360c..d828282bc 100644 --- a/src/main/java/in/koreatech/koin/global/domain/notification/service/NotificationService.java +++ b/src/main/java/in/koreatech/koin/global/domain/notification/service/NotificationService.java @@ -7,10 +7,10 @@ import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.domain.user.repository.UserRepository; -import in.koreatech.koin.global.domain.notification.model.NotificationSubscribe; import in.koreatech.koin.global.domain.notification.dto.NotificationStatusResponse; import in.koreatech.koin.global.domain.notification.dto.NotificationSubscribePermitRequest; import in.koreatech.koin.global.domain.notification.model.Notification; +import in.koreatech.koin.global.domain.notification.model.NotificationSubscribe; import in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType; import in.koreatech.koin.global.domain.notification.repository.NotificationRepository; import in.koreatech.koin.global.domain.notification.repository.NotificationSubscribeRepository; @@ -34,7 +34,7 @@ public void push(Notification notification) { notification.getTitle(), notification.getMessage(), notification.getImageUrl(), - notification.getUrl(), + notification.getMobileAppPath(), notification.getType() ); } diff --git a/src/main/java/in/koreatech/koin/global/fcm/FcmClient.java b/src/main/java/in/koreatech/koin/global/fcm/FcmClient.java index 1c14287fc..00d4cb3c4 100644 --- a/src/main/java/in/koreatech/koin/global/fcm/FcmClient.java +++ b/src/main/java/in/koreatech/koin/global/fcm/FcmClient.java @@ -1,14 +1,21 @@ package in.koreatech.koin.global.fcm; +import static com.google.firebase.messaging.AndroidConfig.Priority.HIGH; + +import java.util.Map; + import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import com.google.firebase.messaging.AndroidConfig; +import com.google.firebase.messaging.AndroidNotification; +import com.google.firebase.messaging.ApnsConfig; +import com.google.firebase.messaging.ApnsFcmOptions; +import com.google.firebase.messaging.Aps; +import com.google.firebase.messaging.ApsAlert; import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.Message; -import com.google.firebase.messaging.Notification; -import static com.google.firebase.messaging.AndroidConfig.Priority.HIGH; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -21,27 +28,21 @@ public void sendMessage( String title, String content, String imageUrl, - String url, + MobileAppPath path, String type ) { if (targetDeviceToken == null) { return; } log.info("call FcmClient sendMessage: title: {}, content: {}", title, content); - Notification notification = Notification.builder() - .setTitle(title) - .setBody(content) - .setImage(imageUrl) - .build(); + + AndroidConfig androidConfig = generateAndroidConfig(title, content, imageUrl, path, type); + ApnsConfig apnsConfig = generateAppleConfig(title, content, imageUrl, path, type); + Message message = Message.builder() .setToken(targetDeviceToken) - .setNotification(notification) - .putData("url", url) - .putData("type", type) - .setAndroidConfig(AndroidConfig.builder() - .setPriority(HIGH) - .build() - ).build(); + .setApnsConfig(apnsConfig) + .setAndroidConfig(androidConfig).build(); try { String result = FirebaseMessaging.getInstance().send(message); log.info("FCM 알림 전송 성공: {}", result); @@ -49,4 +50,59 @@ public void sendMessage( log.warn("FCM 알림 전송 실패", e); } } + + private ApnsConfig generateAppleConfig( + String title, + String content, + String imageUrl, + MobileAppPath path, + String type + ) { + return ApnsConfig.builder() + .setAps( + Aps.builder() + .setAlert( + ApsAlert.builder() + .setTitle(title) + .setBody(content) + .build() + ) + .setSound("default") + .setCategory(path.getApple()) + .setMutableContent(true) + .build() + ) + .setFcmOptions( + ApnsFcmOptions.builder() + .setImage(imageUrl) + .build() + ) + .putAllCustomData( + Map.of( + "type", type + ) + ) + .build(); + } + + private AndroidConfig generateAndroidConfig( + String title, + String content, + String imageUrl, + MobileAppPath path, + String type + ) { + AndroidNotification androidNotification = AndroidNotification.builder() + .setTitle(title) + .setBody(content) + .setImage(imageUrl) + .setClickAction(path.getAndroid()) + .build(); + + return AndroidConfig.builder() + .setNotification(androidNotification) + .putData("type", type) + .setPriority(HIGH) + .build(); + } } diff --git a/src/main/java/in/koreatech/koin/global/fcm/MobileAppPath.java b/src/main/java/in/koreatech/koin/global/fcm/MobileAppPath.java new file mode 100644 index 000000000..271aed1b9 --- /dev/null +++ b/src/main/java/in/koreatech/koin/global/fcm/MobileAppPath.java @@ -0,0 +1,18 @@ +package in.koreatech.koin.global.fcm; + +import lombok.Getter; + +@Getter +public enum MobileAppPath { + HOME("home", "home"), + LOGIN("login", "login"), + ; + + private final String android; + private final String apple; + + MobileAppPath(String android, String apple) { + this.android = android; + this.apple = apple; + } +} diff --git a/src/main/resources/db/migration/V8__alter_notificaion_change_column_name.sql b/src/main/resources/db/migration/V8__alter_notificaion_change_column_name.sql new file mode 100644 index 000000000..8fc960d7c --- /dev/null +++ b/src/main/resources/db/migration/V8__alter_notificaion_change_column_name.sql @@ -0,0 +1,2 @@ +ALTER TABLE `notification` + CHANGE COLUMN url app_path VARCHAR(255);