Просмотр исходного кода

feat(push): add native channel configuration

qmj 2 недель назад
Родитель
Сommit
cd640dd8cc

+ 108 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/push/BusinessPushProperties.java

@@ -0,0 +1,108 @@
+package com.ruoyi.app.push;
+
+import jakarta.annotation.PostConstruct;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+@Component
+@ConfigurationProperties(prefix = "business-push")
+public class BusinessPushProperties {
+    public enum Mode { UNI, NATIVE }
+
+    public enum ApnsEnvironment { DEVELOPMENT, PRODUCTION }
+
+    private Mode mode = Mode.UNI;
+    private ApnsEnvironment apnsEnvironment = ApnsEnvironment.PRODUCTION;
+    private Map<RecipientApp, AppProperties> apps = new EnumMap<>(RecipientApp.class);
+
+    @PostConstruct
+    public void validate() {
+        if (mode != Mode.NATIVE) {
+            return;
+        }
+        if (apnsEnvironment == null) {
+            throw new IllegalStateException("business-push.apns-environment is required");
+        }
+        for (RecipientApp app : RecipientApp.values()) {
+            AppProperties config = apps.get(app);
+            if (config == null || blank(config.fcmCredentialsPath) || blank(config.apnsCertificatePath)
+                    || blank(config.apnsCertificatePassword) || blank(config.apnsTopic)) {
+                throw new IllegalStateException("Incomplete native push configuration for " + app);
+            }
+        }
+    }
+
+    private static boolean blank(String value) {
+        return value == null || value.isBlank();
+    }
+
+    public Mode getMode() {
+        return mode;
+    }
+
+    public void setMode(Mode mode) {
+        this.mode = mode;
+    }
+
+    public ApnsEnvironment getApnsEnvironment() {
+        return apnsEnvironment;
+    }
+
+    public void setApnsEnvironment(ApnsEnvironment apnsEnvironment) {
+        this.apnsEnvironment = apnsEnvironment;
+    }
+
+    public Map<RecipientApp, AppProperties> getApps() {
+        return apps;
+    }
+
+    public void setApps(Map<RecipientApp, AppProperties> apps) {
+        this.apps = apps;
+    }
+
+    public AppProperties getApp(RecipientApp app) {
+        return apps.get(app);
+    }
+
+    public static class AppProperties {
+        private String fcmCredentialsPath;
+        private String apnsCertificatePath;
+        private String apnsCertificatePassword;
+        private String apnsTopic;
+
+        public String getFcmCredentialsPath() {
+            return fcmCredentialsPath;
+        }
+
+        public void setFcmCredentialsPath(String fcmCredentialsPath) {
+            this.fcmCredentialsPath = fcmCredentialsPath;
+        }
+
+        public String getApnsCertificatePath() {
+            return apnsCertificatePath;
+        }
+
+        public void setApnsCertificatePath(String apnsCertificatePath) {
+            this.apnsCertificatePath = apnsCertificatePath;
+        }
+
+        public String getApnsCertificatePassword() {
+            return apnsCertificatePassword;
+        }
+
+        public void setApnsCertificatePassword(String apnsCertificatePassword) {
+            this.apnsCertificatePassword = apnsCertificatePassword;
+        }
+
+        public String getApnsTopic() {
+            return apnsTopic;
+        }
+
+        public void setApnsTopic(String apnsTopic) {
+            this.apnsTopic = apnsTopic;
+        }
+    }
+}

+ 7 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/push/RecipientApp.java

@@ -0,0 +1,7 @@
+package com.ruoyi.app.push;
+
+public enum RecipientApp {
+    USER,
+    MERCHANT,
+    RIDER
+}

+ 21 - 0
ruoyi-admin/src/main/resources/application.yml

@@ -50,6 +50,27 @@ omgpay:
     retry-delay-ms: 300000
     retry-delay-ms: 300000
     round-budget-seconds: 45
     round-budget-seconds: 45
 
 
+# 普通业务推送通道(默认沿用 uniPush)
+business-push:
+  mode: "${BUSINESS_PUSH_MODE:uni}"
+  apns-environment: "${BUSINESS_PUSH_APNS_ENVIRONMENT:production}"
+  apps:
+    user:
+      fcm-credentials-path: "${BUSINESS_PUSH_USER_FCM_CREDENTIALS_PATH:}"
+      apns-certificate-path: "${BUSINESS_PUSH_USER_APNS_CERTIFICATE_PATH:}"
+      apns-certificate-password: "${BUSINESS_PUSH_USER_APNS_CERTIFICATE_PASSWORD:}"
+      apns-topic: "${BUSINESS_PUSH_USER_APNS_TOPIC:}"
+    merchant:
+      fcm-credentials-path: "${BUSINESS_PUSH_MERCHANT_FCM_CREDENTIALS_PATH:}"
+      apns-certificate-path: "${BUSINESS_PUSH_MERCHANT_APNS_CERTIFICATE_PATH:}"
+      apns-certificate-password: "${BUSINESS_PUSH_MERCHANT_APNS_CERTIFICATE_PASSWORD:}"
+      apns-topic: "${BUSINESS_PUSH_MERCHANT_APNS_TOPIC:}"
+    rider:
+      fcm-credentials-path: "${BUSINESS_PUSH_RIDER_FCM_CREDENTIALS_PATH:}"
+      apns-certificate-path: "${BUSINESS_PUSH_RIDER_APNS_CERTIFICATE_PATH:}"
+      apns-certificate-password: "${BUSINESS_PUSH_RIDER_APNS_CERTIFICATE_PASSWORD:}"
+      apns-topic: "${BUSINESS_PUSH_RIDER_APNS_TOPIC:}"
+
 # IM 即时沟通配置
 # IM 即时沟通配置
 im:
 im:
   # IM 平台根地址(测试环境 test-im.abtim-my.com;正式环境另行配置)
   # IM 平台根地址(测试环境 test-im.abtim-my.com;正式环境另行配置)

+ 79 - 0
ruoyi-admin/src/test/java/com/ruoyi/app/push/BusinessPushPropertiesTest.java

@@ -0,0 +1,79 @@
+package com.ruoyi.app.push;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class BusinessPushPropertiesTest {
+    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+            .withUserConfiguration(PushConfiguration.class);
+
+    @Test
+    void defaultsToUniWithoutNativeCredentials() {
+        contextRunner.run(context -> {
+            assertTrue(context.isRunning());
+            assertEquals(BusinessPushProperties.Mode.UNI,
+                    context.getBean(BusinessPushProperties.class).getMode());
+        });
+    }
+
+    @Test
+    void explicitUniDoesNotRequireNativeCredentials() {
+        contextRunner.withPropertyValues("business-push.mode=uni")
+                .run(context -> assertTrue(context.isRunning()));
+    }
+
+    @Test
+    void nativeModeRejectsIncompleteAppConfiguration() {
+        contextRunner.withPropertyValues("business-push.mode=native")
+                .run(context -> assertNotNull(context.getStartupFailure()));
+    }
+
+    @Test
+    void nativeModeAcceptsThreeCompleteAppConfigurations() {
+        contextRunner.withPropertyValues(completeNativeProperties())
+                .run(context -> {
+                    assertTrue(context.isRunning());
+                    BusinessPushProperties properties = context.getBean(BusinessPushProperties.class);
+                    assertEquals(BusinessPushProperties.Mode.NATIVE, properties.getMode());
+                    assertEquals("user.json", properties.getApp(RecipientApp.USER).getFcmCredentialsPath());
+                    assertEquals("merchant.json", properties.getApp(RecipientApp.MERCHANT).getFcmCredentialsPath());
+                    assertEquals("rider.json", properties.getApp(RecipientApp.RIDER).getFcmCredentialsPath());
+                });
+    }
+
+    @Test
+    void nativeModeRejectsUnknownApnsEnvironment() {
+        String[] values = completeNativeProperties();
+        values[1] = "business-push.apns-environment=staging";
+        contextRunner.withPropertyValues(values)
+                .run(context -> assertNotNull(context.getStartupFailure()));
+    }
+
+    private String[] completeNativeProperties() {
+        return new String[]{
+                "business-push.mode=native",
+                "business-push.apns-environment=production",
+                "business-push.apps.user.fcm-credentials-path=user.json",
+                "business-push.apps.user.apns-certificate-path=user.p12",
+                "business-push.apps.user.apns-certificate-password=secret",
+                "business-push.apps.user.apns-topic=com.example.user",
+                "business-push.apps.merchant.fcm-credentials-path=merchant.json",
+                "business-push.apps.merchant.apns-certificate-path=merchant.p12",
+                "business-push.apps.merchant.apns-certificate-password=secret",
+                "business-push.apps.merchant.apns-topic=com.example.merchant",
+                "business-push.apps.rider.fcm-credentials-path=rider.json",
+                "business-push.apps.rider.apns-certificate-path=rider.p12",
+                "business-push.apps.rider.apns-certificate-password=secret",
+                "business-push.apps.rider.apns-topic=com.example.rider"
+        };
+    }
+
+    @EnableConfigurationProperties(BusinessPushProperties.class)
+    static class PushConfiguration {
+    }
+}