diff --git a/pom.xml b/pom.xml index 8e39fb4..2a66cba 100644 --- a/pom.xml +++ b/pom.xml @@ -305,6 +305,11 @@ spring-boot-starter-thymeleaf ${spring-boot.version} + + org.json + json + 20090211 + diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 289bace..473c65e 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -68,7 +68,7 @@ spring: # redis 配置 redis: # 地址 - host: 127.0.0.1 + host: 192.168.31.55 # 端口,默认为6379 port: 6379 # 数据库索引 @@ -142,6 +142,5 @@ ali: umApp: appAliKey: 204918113 appAliSecret: v4UrIhhLZlo0adpmevyCfvThGFbrRer0 - androidKey: 687b2df479267e0210b79b6f - IOSKey: 687b2e1679267e0210b79b70 - + androidKey: 68a9988ce563686f4288e26d + IOSKey: 68a99a66ec2b5b6f8825b8b1 diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 827315c..1c4a9f8 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -193,7 +193,10 @@ org.springframework.boot spring-boot-starter-thymeleaf - + + + org.json + json diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/UmengConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/UmengConfig.java index 93154c5..c13aa93 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/config/UmengConfig.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/UmengConfig.java @@ -6,6 +6,9 @@ import com.ruoyi.common.constant.AliKeyConfig; import com.ruoyi.common.core.domain.UmResp; import com.ruoyi.common.enums.DeviceTypeEnum; import com.ruoyi.common.utils.uuid.UUID; +import com.ruoyi.system.config.push.AndroidNotification; +import com.ruoyi.system.config.push.PushClient; +import com.ruoyi.system.config.push.android.AndroidCustomizedcast; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; import org.apache.http.Header; @@ -34,6 +37,8 @@ public class UmengConfig { private static final Logger log = LoggerFactory.getLogger(UmengConfig.class); + private PushClient client = new PushClient(); + public UmResp send(String token,String deviceTypeUp){ String umAppkey = null; if (deviceTypeUp.equals(DeviceTypeEnum.ANDROID.name())){ @@ -108,6 +113,25 @@ public class UmengConfig { } + public void sendAndroidCustomizedcast() throws Exception { + AndroidCustomizedcast customizedcast = new AndroidCustomizedcast(AliKeyConfig.UMApp_AliKey,AliKeyConfig.UMApp_AliSecret); + // TODO Set your alias here, and use comma to split them if there are multiple alias. + // And if you have many alias, you can also upload a file containing these alias, then + // use file_id to send customized notification. + customizedcast.setAlias("alias", "alias_type"); + customizedcast.setTicker( "Android customizedcast ticker"); + customizedcast.setTitle( "中文的title"); + customizedcast.setText( "Android customizedcast text"); + customizedcast.goAppAfterOpen(); + customizedcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION); + // TODO Set 'production_mode' to 'false' if it's a test device. + // For how to register a test device, please see the developer doc. + customizedcast.setProductionMode(); + //厂商通道相关参数 + customizedcast.setChannelActivity("your channel activity"); + customizedcast.setChannelProperties("abc"); + client.send(customizedcast); + } public static void main(String[] args) { diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/AndroidNotification.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/AndroidNotification.java new file mode 100644 index 0000000..d3ca5fe --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/AndroidNotification.java @@ -0,0 +1,198 @@ +package com.ruoyi.system.config.push; + +import java.util.Arrays; +import java.util.HashSet; +import org.json.JSONObject; + +public abstract class AndroidNotification extends UmengNotification { + // Keys can be set in the payload level + protected static final HashSet PAYLOAD_KEYS = new HashSet(Arrays.asList(new String[]{ + "display_type"})); + + // Keys can be set in the body level + protected static final HashSet BODY_KEYS = new HashSet(Arrays.asList(new String[]{ + "ticker", "title", "text", "builder_id", "icon", "largeIcon", "img", "play_vibrate", "play_lights", "play_sound", + "sound", "after_open", "url", "activity", "custom"})); + + public enum DisplayType{ + NOTIFICATION{public String getValue(){return "notification";}},///通知:消息送达到用户设备后,由友盟SDK接管处理并在通知栏上显示通知内容。 + MESSAGE{public String getValue(){return "message";}};///消息:消息送达到用户设备后,消息内容透传给应用自身进行解析处理。 + public abstract String getValue(); + } + public enum AfterOpenAction{ + go_app,//打开应用 + go_url,//跳转到URL + go_activity,//打开特定的activity + go_custom//用户自定义内容。 + } + // Set key/value in the rootJson, for the keys can be set please see ROOT_KEYS, PAYLOAD_KEYS, + // BODY_KEYS and POLICY_KEYS. + @Override + public boolean setPredefinedKeyValue(String key, Object value) throws Exception { + if (ROOT_KEYS.contains(key)) { + // This key should be in the root level + rootJson.put(key, value); + } else if (PAYLOAD_KEYS.contains(key)) { + // This key should be in the payload level + JSONObject payloadJson = null; + if (rootJson.has("payload")) { + payloadJson = rootJson.getJSONObject("payload"); + } else { + payloadJson = new JSONObject(); + rootJson.put("payload", payloadJson); + } + payloadJson.put(key, value); + } else if (BODY_KEYS.contains(key)) { + // This key should be in the body level + JSONObject bodyJson = null; + JSONObject payloadJson = null; + // 'body' is under 'payload', so build a payload if it doesn't exist + if (rootJson.has("payload")) { + payloadJson = rootJson.getJSONObject("payload"); + } else { + payloadJson = new JSONObject(); + rootJson.put("payload", payloadJson); + } + // Get body JSONObject, generate one if not existed + if (payloadJson.has("body")) { + bodyJson = payloadJson.getJSONObject("body"); + } else { + bodyJson = new JSONObject(); + payloadJson.put("body", bodyJson); + } + bodyJson.put(key, value); + } else if (POLICY_KEYS.contains(key)) { + // This key should be in the body level + JSONObject policyJson = null; + if (rootJson.has("policy")) { + policyJson = rootJson.getJSONObject("policy"); + } else { + policyJson = new JSONObject(); + rootJson.put("policy", policyJson); + } + policyJson.put(key, value); + } else { + if (key == "payload" || key == "body" || key == "policy" || key == "extra") { + throw new Exception("You don't need to set value for " + key + " , just set values for the sub keys in it."); + } else { + throw new Exception("Unknown key: " + key); + } + } + return true; + } + + // Set extra key/value for Android notification + public boolean setExtraField(String key, String value) throws Exception { + JSONObject payloadJson = null; + JSONObject extraJson = null; + if (rootJson.has("payload")) { + payloadJson = rootJson.getJSONObject("payload"); + } else { + payloadJson = new JSONObject(); + rootJson.put("payload", payloadJson); + } + + if (payloadJson.has("extra")) { + extraJson = payloadJson.getJSONObject("extra"); + } else { + extraJson = new JSONObject(); + payloadJson.put("extra", extraJson); + } + extraJson.put(key, value); + return true; + } + + // + public void setDisplayType(DisplayType d) throws Exception { + setPredefinedKeyValue("display_type", d.getValue()); + } + ///通知栏提示文字 + public void setTicker(String ticker) throws Exception { + setPredefinedKeyValue("ticker", ticker); + } + ///通知标题 + public void setTitle(String title) throws Exception { + setPredefinedKeyValue("title", title); + } + ///通知文字描述 + public void setText(String text) throws Exception { + setPredefinedKeyValue("text", text); + } + ///用于标识该通知采用的样式。使用该参数时, 必须在SDK里面实现自定义通知栏样式。 + public void setBuilderId(Integer builder_id) throws Exception { + setPredefinedKeyValue("builder_id", builder_id); + } + ///状态栏图标ID, R.drawable.[smallIcon],如果没有, 默认使用应用图标。 + public void setIcon(String icon) throws Exception { + setPredefinedKeyValue("icon", icon); + } + ///通知栏拉开后左侧图标ID + public void setLargeIcon(String largeIcon) throws Exception { + setPredefinedKeyValue("largeIcon", largeIcon); + } + ///通知栏大图标的URL链接。该字段的优先级大于largeIcon。该字段要求以http或者https开头。 + public void setImg(String img) throws Exception { + setPredefinedKeyValue("img", img); + } + ///收到通知是否震动,默认为"true" + public void setPlayVibrate(Boolean play_vibrate) throws Exception { + setPredefinedKeyValue("play_vibrate", play_vibrate.toString()); + } + ///收到通知是否闪灯,默认为"true" + public void setPlayLights(Boolean play_lights) throws Exception { + setPredefinedKeyValue("play_lights", play_lights.toString()); + } + ///收到通知是否发出声音,默认为"true" + public void setPlaySound(Boolean play_sound) throws Exception { + setPredefinedKeyValue("play_sound", play_sound.toString()); + } + ///通知声音,R.raw.[sound]. 如果该字段为空,采用SDK默认的声音 + public void setSound(String sound) throws Exception { + setPredefinedKeyValue("sound", sound); + } + ///收到通知后播放指定的声音文件 + public void setPlaySound(String sound) throws Exception { + setPlaySound(true); + setSound(sound); + } + + ///点击"通知"的后续行为,默认为打开app。 + public void goAppAfterOpen() throws Exception { + setAfterOpenAction(AfterOpenAction.go_app); + } + public void goUrlAfterOpen(String url) throws Exception { + setAfterOpenAction(AfterOpenAction.go_url); + setUrl(url); + } + public void goActivityAfterOpen(String activity) throws Exception { + setAfterOpenAction(AfterOpenAction.go_activity); + setActivity(activity); + } + public void goCustomAfterOpen(String custom) throws Exception { + setAfterOpenAction(AfterOpenAction.go_custom); + setCustomField(custom); + } + public void goCustomAfterOpen(JSONObject custom) throws Exception { + setAfterOpenAction(AfterOpenAction.go_custom); + setCustomField(custom); + } + + ///点击"通知"的后续行为,默认为打开app。原始接口 + public void setAfterOpenAction(AfterOpenAction action) throws Exception { + setPredefinedKeyValue("after_open", action.toString()); + } + public void setUrl(String url) throws Exception { + setPredefinedKeyValue("url", url); + } + public void setActivity(String activity) throws Exception { + setPredefinedKeyValue("activity", activity); + } + ///can be a string of json + public void setCustomField(String custom) throws Exception { + setPredefinedKeyValue("custom", custom); + } + public void setCustomField(JSONObject custom) throws Exception { + setPredefinedKeyValue("custom", custom); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/App.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/App.java new file mode 100644 index 0000000..d9781a5 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/App.java @@ -0,0 +1,6 @@ +package com.ruoyi.system.config.push; + + +public class App { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/Demo.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/Demo.java new file mode 100644 index 0000000..9a8de1b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/Demo.java @@ -0,0 +1,283 @@ +package com.ruoyi.system.config.push; + + +import com.ruoyi.system.config.push.android.*; +import com.ruoyi.system.config.push.ios.*; +import org.json.JSONArray; +import org.json.JSONObject; + +public class Demo { + private String appkey = null; + private String appMasterSecret = null; + private String timestamp = null; + private PushClient client = new PushClient(); + + public Demo(String key, String secret) { + try { + appkey = key; + appMasterSecret = secret; + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + public void sendAndroidBroadcast() throws Exception { + AndroidBroadcast broadcast = new AndroidBroadcast(appkey,appMasterSecret); + broadcast.setTicker( "Android broadcast ticker"); + broadcast.setTitle( "中文的title"); + broadcast.setText( "Android broadcast text"); + broadcast.goAppAfterOpen(); + broadcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION); + // TODO Set 'production_mode' to 'false' if it's a test device. + // For how to register a test device, please see the developer doc. + broadcast.setProductionMode(); + // Set customized fields + broadcast.setExtraField("test", "helloworld"); + //厂商通道相关参数 + broadcast.setChannelActivity("your channel activity"); + broadcast.setChannelProperties("abc"); + client.send(broadcast); + } + + public void sendAndroidUnicast() throws Exception { + AndroidUnicast unicast = new AndroidUnicast(appkey,appMasterSecret); + // TODO Set your device token + unicast.setDeviceToken( "your device_token"); + unicast.setTicker( "Android unicast ticker"); + unicast.setTitle( "中文的title"); + unicast.setText( "Android unicast text"); + unicast.goAppAfterOpen(); + unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION); + // TODO Set 'production_mode' to 'false' if it's a test device. + // For how to register a test device, please see the developer doc. + unicast.setProductionMode(); + // Set customized fields + unicast.setExtraField("test", "helloworld"); + unicast.setChannelActivity("your channel activity"); + unicast.setChannelProperties("abc"); + client.send(unicast); + } + + public void sendAndroidGroupcast() throws Exception { + AndroidGroupcast groupcast = new AndroidGroupcast(appkey,appMasterSecret); + /* TODO + * Construct the filter condition: + * "where": + * { + * "and": + * [ + * {"tag":"test"}, + * {"tag":"Test"} + * ] + * } + */ + JSONObject filterJson = new JSONObject(); + JSONObject whereJson = new JSONObject(); + JSONArray tagArray = new JSONArray(); + JSONObject testTag = new JSONObject(); + JSONObject TestTag = new JSONObject(); + testTag.put("tag", "test"); + TestTag.put("tag", "Test"); + tagArray.put(testTag); + tagArray.put(TestTag); + whereJson.put("and", tagArray); + filterJson.put("where", whereJson); + + groupcast.setFilter(filterJson); + groupcast.setTicker( "Android groupcast ticker"); + groupcast.setTitle( "中文的title"); + groupcast.setText( "Android groupcast text"); + groupcast.goAppAfterOpen(); + groupcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION); + groupcast.setChannelActivity("your channel activity"); + // TODO Set 'production_mode' to 'false' if it's a test device. + // For how to register a test device, please see the developer doc. + groupcast.setProductionMode(); + //厂商通道相关参数 + groupcast.setChannelActivity("your channel activity"); + groupcast.setChannelProperties("abc"); + client.send(groupcast); + } + +// public void sendAndroidCustomizedcast() throws Exception { +// AndroidCustomizedcast customizedcast = new AndroidCustomizedcast(appkey,appMasterSecret); +// // TODO Set your alias here, and use comma to split them if there are multiple alias. +// // And if you have many alias, you can also upload a file containing these alias, then +// // use file_id to send customized notification. +// customizedcast.setAlias("alias", "alias_type"); +// customizedcast.setTicker( "Android customizedcast ticker"); +// customizedcast.setTitle( "中文的title"); +// customizedcast.setText( "Android customizedcast text"); +// customizedcast.goAppAfterOpen(); +// customizedcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION); +// // TODO Set 'production_mode' to 'false' if it's a test device. +// // For how to register a test device, please see the developer doc. +// customizedcast.setProductionMode(); +// //厂商通道相关参数 +// customizedcast.setChannelActivity("your channel activity"); +// customizedcast.setChannelProperties("abc"); +// client.send(customizedcast); +// } + + public void sendAndroidCustomizedcastFile() throws Exception { + AndroidCustomizedcast customizedcast = new AndroidCustomizedcast(appkey,appMasterSecret); + // TODO Set your alias here, and use comma to split them if there are multiple alias. + // And if you have many alias, you can also upload a file containing these alias, then + // use file_id to send customized notification. + String fileId = client.uploadContents(appkey,appMasterSecret,"aa"+"\n"+"bb"+"\n"+"alias"); + customizedcast.setFileId(fileId, "alias_type"); + customizedcast.setTicker( "Android customizedcast ticker"); + customizedcast.setTitle( "中文的title"); + customizedcast.setText( "Android customizedcast text"); + customizedcast.goAppAfterOpen(); + customizedcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION); + // TODO Set 'production_mode' to 'false' if it's a test device. + // For how to register a test device, please see the developer doc. + customizedcast.setProductionMode(); + //厂商通道相关参数 + customizedcast.setChannelActivity("your channel activity"); + customizedcast.setChannelProperties("abc"); + client.send(customizedcast); + } + + public void sendAndroidFilecast() throws Exception { + AndroidFilecast filecast = new AndroidFilecast(appkey,appMasterSecret); + // TODO upload your device tokens, and use '\n' to split them if there are multiple tokens + String fileId = client.uploadContents(appkey,appMasterSecret,"aa"+"\n"+"bb"); + filecast.setFileId( fileId); + filecast.setTicker( "Android filecast ticker"); + filecast.setTitle( "中文的title"); + filecast.setText( "Android filecast text"); + filecast.goAppAfterOpen(); + filecast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION); + //厂商通道相关参数 + filecast.setChannelActivity("your channel activity"); + filecast.setChannelProperties("abc"); + client.send(filecast); + } + + public void sendIOSBroadcast() throws Exception { + IOSBroadcast broadcast = new IOSBroadcast(appkey,appMasterSecret); + //alert值设置为字符串 + //broadcast.setAlert("IOS 广播测试"); + //alert的值设置为字典 + broadcast.setAlert("今日天气" , "" , "今日可能下雨🌂"); + broadcast.setBadge( 0); + broadcast.setSound( "default"); + // TODO set 'production_mode' to 'true' if your app is under production mode + broadcast.setTestMode(); + // Set customized fields + broadcast.setCustomizedField("test", "helloworld"); + client.send(broadcast); + } + + public void sendIOSUnicast() throws Exception { + IOSUnicast unicast = new IOSUnicast(appkey,appMasterSecret); + // TODO Set your device token + unicast.setDeviceToken( "your device_token"); + //alert值设置为字符串 + //unicast.setAlert("IOS 单播测试"); + //alert的值设置为字典 + unicast.setAlert("今日天气" , "" , "今日可能下雨🌂"); + unicast.setBadge( 0); + unicast.setSound( "default"); + // TODO set 'production_mode' to 'true' if your app is under production mode + unicast.setTestMode(); + // Set customized fields + unicast.setCustomizedField("test", "helloworld"); + client.send(unicast); + } + + + public void sendIOSGroupcast() throws Exception { + IOSGroupcast groupcast = new IOSGroupcast(appkey,appMasterSecret); + /* TODO + * Construct the filter condition: + * "where": + * { + * "and": + * [ + * {"tag":"iostest"} + * ] + * } + */ + JSONObject filterJson = new JSONObject(); + JSONObject whereJson = new JSONObject(); + JSONArray tagArray = new JSONArray(); + JSONObject testTag = new JSONObject(); + testTag.put("tag", "iostest"); + tagArray.put(testTag); + whereJson.put("and", tagArray); + filterJson.put("where", whereJson); + System.out.println(filterJson.toString()); + + // Set filter condition into rootJson + groupcast.setFilter(filterJson); + //groupcast.setAlert("IOS 组播测试"); + //alert的值设置为字典 + groupcast.setAlert("今日天气" , "subtitle" , "今日可能下雨🌂"); + groupcast.setBadge( 0); + groupcast.setSound( "default"); + // TODO set 'production_mode' to 'true' if your app is under production mode + groupcast.setTestMode(); + client.send(groupcast); + } + + public void sendIOSCustomizedcast() throws Exception { + IOSCustomizedcast customizedcast = new IOSCustomizedcast(appkey,appMasterSecret); + // TODO Set your alias and alias_type here, and use comma to split them if there are multiple alias. + // And if you have many alias, you can also upload a file containing these alias, then + // use file_id to send customized notification. + customizedcast.setAlias("alias", "alias_type"); + //customizedcast.setAlert("IOS 个性化测试"); + //alert的值设置为字典 + customizedcast.setAlert("今日天气" , "" , "今日可能下雨🌂"); + customizedcast.setBadge( 0); + customizedcast.setSound( "default"); + // TODO set 'production_mode' to 'true' if your app is under production mode + customizedcast.setTestMode(); + client.send(customizedcast); + } + + public void sendIOSFilecast() throws Exception { + IOSFilecast filecast = new IOSFilecast(appkey,appMasterSecret); + // TODO upload your device tokens, and use '\n' to split them if there are multiple tokens + String fileId = client.uploadContents(appkey,appMasterSecret,"aa"+"\n"+"bb"); + filecast.setFileId( fileId); + //filecast.setAlert("IOS 文件播测试"); + //alert的值设置为字典 + filecast.setAlert("今日天气" , "" , "今日可能下雨🌂"); + filecast.setBadge( 0); + filecast.setSound( "default"); + // TODO set 'production_mode' to 'true' if your app is under production mode + filecast.setTestMode(); + client.send(filecast); + } + + public static void main(String[] args) { + // TODO set your appkey and master secret here + Demo demo = new Demo("your appkey", "your master secret"); + try { + demo.sendAndroidUnicast(); + //demo.sendIOSUnicast(); + /* TODO these methods are all available, just fill in some fields and do the test + * demo.sendAndroidCustomizedcastFile(); + * demo.sendAndroidBroadcast(); + * demo.sendAndroidGroupcast(); + * demo.sendAndroidCustomizedcast(); + * demo.sendAndroidFilecast(); + * + * demo.sendIOSBroadcast(); + * demo.sendIOSUnicast(); + * demo.sendIOSGroupcast(); + * demo.sendIOSCustomizedcast(); + * demo.sendIOSFilecast(); + */ + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/IOSNotification.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/IOSNotification.java new file mode 100644 index 0000000..814a800 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/IOSNotification.java @@ -0,0 +1,93 @@ +package com.ruoyi.system.config.push; + +import java.util.Arrays; +import java.util.HashSet; + +import org.json.JSONObject; + +public abstract class IOSNotification extends UmengNotification { + + // Keys can be set in the aps level + protected static final HashSet APS_KEYS = new HashSet(Arrays.asList(new String[]{ + "alert", "badge", "sound", "content-available" + })); + + @Override + public boolean setPredefinedKeyValue(String key, Object value) throws Exception { + if (ROOT_KEYS.contains(key)) { + // This key should be in the root level + rootJson.put(key, value); + } else if (APS_KEYS.contains(key)) { + // This key should be in the aps level + JSONObject apsJson = null; + JSONObject payloadJson = null; + if (rootJson.has("payload")) { + payloadJson = rootJson.getJSONObject("payload"); + } else { + payloadJson = new JSONObject(); + rootJson.put("payload", payloadJson); + } + if (payloadJson.has("aps")) { + apsJson = payloadJson.getJSONObject("aps"); + } else { + apsJson = new JSONObject(); + payloadJson.put("aps", apsJson); + } + apsJson.put(key, value); + } else if (POLICY_KEYS.contains(key)) { + // This key should be in the body level + JSONObject policyJson = null; + if (rootJson.has("policy")) { + policyJson = rootJson.getJSONObject("policy"); + } else { + policyJson = new JSONObject(); + rootJson.put("policy", policyJson); + } + policyJson.put(key, value); + } else { + if (key == "payload" || key == "aps" || key == "policy") { + throw new Exception("You don't need to set value for " + key + " , just set values for the sub keys in it."); + } else { + throw new Exception("Unknownd key: " + key); + } + } + + return true; + } + // Set customized key/value for IOS notification + public boolean setCustomizedField(String key, String value) throws Exception { + //rootJson.put(key, value); + JSONObject payloadJson = null; + if (rootJson.has("payload")) { + payloadJson = rootJson.getJSONObject("payload"); + } else { + payloadJson = new JSONObject(); + rootJson.put("payload", payloadJson); + } + payloadJson.put(key, value); + return true; + } + + public void setAlert(String token) throws Exception { + setPredefinedKeyValue("alert", token); + } + + public void setAlert(String title ,String subtitle , String body) throws Exception{ + JSONObject object = new JSONObject(); + object.put("title" , title); + object.put("subtitle" , subtitle); + object.put("body" , body); + setPredefinedKeyValue("alert",object ); + } + public void setBadge(Integer badge) throws Exception { + setPredefinedKeyValue("badge", badge); + } + + public void setSound(String sound) throws Exception { + setPredefinedKeyValue("sound", sound); + } + + public void setContentAvailable(Integer contentAvailable) throws Exception { + setPredefinedKeyValue("content-available", contentAvailable); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/PushClient.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/PushClient.java new file mode 100644 index 0000000..3625b92 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/PushClient.java @@ -0,0 +1,100 @@ +package com.ruoyi.system.config.push; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +import com.alibaba.fastjson2.JSONObject; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +public class PushClient { + + // The user agent + protected final String USER_AGENT = "Mozilla/5.0"; + + // This object is used for sending the post request to Umeng + protected HttpClient client = new DefaultHttpClient(); + + // The host + protected static final String host = "http://msg.umeng.com"; + + // The upload path + protected static final String uploadPath = "/upload"; + + // The post path + protected static final String postPath = "/api/send"; + + public boolean send(UmengNotification msg) throws Exception { + String timestamp = Integer.toString((int)(System.currentTimeMillis() / 1000)); + msg.setPredefinedKeyValue("timestamp", timestamp); + String url = host + postPath; + String postBody = msg.getPostBody(); + String sign = DigestUtils.md5Hex(("POST" + url + postBody + msg.getAppMasterSecret()).getBytes("utf8")); + url = url + "?sign=" + sign; + HttpPost post = new HttpPost(url); + post.setHeader("User-Agent", USER_AGENT); + StringEntity se = new StringEntity(postBody, "UTF-8"); + post.setEntity(se); + // Send the post request and get the response + HttpResponse response = client.execute(post); + int status = response.getStatusLine().getStatusCode(); + System.out.println("Response Code : " + status); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + StringBuffer result = new StringBuffer(); + String line = ""; + while ((line = rd.readLine()) != null) { + result.append(line); + } + System.out.println(result); + if (status == 200) { + System.out.println("Notification sent successfully."); + } else { + System.out.println("Failed to send the notification!"); + } + return true; + } + + // Upload file with device_tokens to Umeng + public String uploadContents(String appkey,String appMasterSecret,String contents) throws Exception { + // Construct the json string + JSONObject uploadJson = new JSONObject(); + uploadJson.put("appkey", appkey); + String timestamp = Integer.toString((int)(System.currentTimeMillis() / 1000)); + uploadJson.put("timestamp", timestamp); + uploadJson.put("content", contents); + // Construct the request + String url = host + uploadPath; + String postBody = uploadJson.toString(); + String sign = DigestUtils.md5Hex(("POST" + url + postBody + appMasterSecret).getBytes("utf8")); + url = url + "?sign=" + sign; + HttpPost post = new HttpPost(url); + post.setHeader("User-Agent", USER_AGENT); + StringEntity se = new StringEntity(postBody, "UTF-8"); + post.setEntity(se); + // Send the post request and get the response + HttpResponse response = client.execute(post); + System.out.println("Response Code : " + response.getStatusLine().getStatusCode()); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + StringBuffer result = new StringBuffer(); + String line = ""; + while ((line = rd.readLine()) != null) { + result.append(line); + } + System.out.println(result.toString()); + // Decode response string and get file_id from it + JSONObject respJson = JSONObject.parseObject(result.toString()); + String ret = respJson.getString("ret"); + if (!ret.equals("SUCCESS")) { + throw new Exception("Failed to upload file"); + } + JSONObject data = respJson.getJSONObject("data"); + String fileId = data.getString("file_id"); + // Set file_id into rootJson using setPredefinedKeyValue + + return fileId; + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/UmengNotification.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/UmengNotification.java new file mode 100644 index 0000000..34c02fc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/UmengNotification.java @@ -0,0 +1,87 @@ +package com.ruoyi.system.config.push; + + +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.HashSet; + +public abstract class UmengNotification { + // This JSONObject is used for constructing the whole request string. + protected final JSONObject rootJson = new JSONObject(); + + + // The app master secret + protected String appMasterSecret; + + // Keys can be set in the root level + protected static final HashSet ROOT_KEYS = new HashSet(Arrays.asList(new String[]{ + "appkey", "timestamp", "type", "device_tokens", "alias", "alias_type", "file_id", + "filter", "production_mode", "feedback", "description", "thirdparty_id" , "mipush" , "mi_activity" , "channel_properties"})); + + // Keys can be set in the policy level + protected static final HashSet POLICY_KEYS = new HashSet(Arrays.asList(new String[]{ + "start_time", "expire_time", "max_send_num" + })); + + // Set predefined keys in the rootJson, for extra keys(Android) or customized keys(IOS) please + // refer to corresponding methods in the subclass. + public abstract boolean setPredefinedKeyValue(String key, Object value) throws Exception; + public void setAppMasterSecret(String secret) { + appMasterSecret = secret; + } + + public String getPostBody(){ + return rootJson.toString(); + } + + protected final String getAppMasterSecret(){ + return appMasterSecret; + } + + protected void setProductionMode(Boolean prod) throws Exception { + setPredefinedKeyValue("production_mode", prod.toString()); + } + + ///正式模式 + public void setProductionMode() throws Exception { + setProductionMode(true); + } + + ///测试模式 + public void setTestMode() throws Exception { + setProductionMode(false); + } + + ///发送消息描述,建议填写。 + public void setDescription(String description) throws Exception { + setPredefinedKeyValue("description", description); + } + + ///定时发送时间,若不填写表示立即发送。格式: "YYYY-MM-DD hh:mm:ss"。 + public void setStartTime(String startTime) throws Exception { + setPredefinedKeyValue("start_time", startTime); + } + ///消息过期时间,格式: "YYYY-MM-DD hh:mm:ss"。 + public void setExpireTime(String expireTime) throws Exception { + setPredefinedKeyValue("expire_time", expireTime); + } + ///发送限速,每秒发送的最大条数。 + public void setMaxSendNum(Integer num) throws Exception { + setPredefinedKeyValue("max_send_num", num); + } + + //厂商弹窗activity + public void setChannelActivity(String activity) throws Exception{ + setPredefinedKeyValue("mipush", "true"); + setPredefinedKeyValue("mi_activity",activity ); + } + + //厂商属性配置 + public void setChannelProperties(String xiaoMiChannelId) throws Exception{ + JSONObject object = new JSONObject(); + object.put("xiaomi_channel_id" , xiaoMiChannelId); + setPredefinedKeyValue("channel_properties", object); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidBroadcast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidBroadcast.java new file mode 100644 index 0000000..98fa1f9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidBroadcast.java @@ -0,0 +1,11 @@ +package com.ruoyi.system.config.push.android; + +import com.ruoyi.system.config.push.AndroidNotification; + +public class AndroidBroadcast extends AndroidNotification { + public AndroidBroadcast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "broadcast"); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidCustomizedcast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidCustomizedcast.java new file mode 100644 index 0000000..b5fc7f4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidCustomizedcast.java @@ -0,0 +1,22 @@ +package com.ruoyi.system.config.push.android; + +import com.ruoyi.system.config.push.AndroidNotification; + +public class AndroidCustomizedcast extends AndroidNotification { + public AndroidCustomizedcast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "customizedcast"); + } + + public void setAlias(String alias,String aliasType) throws Exception { + setPredefinedKeyValue("alias", alias); + setPredefinedKeyValue("alias_type", aliasType); + } + + public void setFileId(String fileId,String aliasType) throws Exception { + setPredefinedKeyValue("file_id", fileId); + setPredefinedKeyValue("alias_type", aliasType); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidFilecast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidFilecast.java new file mode 100644 index 0000000..cd4ea4d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidFilecast.java @@ -0,0 +1,15 @@ +package com.ruoyi.system.config.push.android; + +import com.ruoyi.system.config.push.AndroidNotification; + +public class AndroidFilecast extends AndroidNotification { + public AndroidFilecast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "filecast"); + } + + public void setFileId(String fileId) throws Exception { + setPredefinedKeyValue("file_id", fileId); + } +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidGroupcast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidGroupcast.java new file mode 100644 index 0000000..1f0300f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidGroupcast.java @@ -0,0 +1,16 @@ +package com.ruoyi.system.config.push.android; + +import com.ruoyi.system.config.push.AndroidNotification; +import org.json.JSONObject; + +public class AndroidGroupcast extends AndroidNotification { + public AndroidGroupcast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "groupcast"); + } + + public void setFilter(JSONObject filter) throws Exception { + setPredefinedKeyValue("filter", filter); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidUnicast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidUnicast.java new file mode 100644 index 0000000..ffee49f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/android/AndroidUnicast.java @@ -0,0 +1,16 @@ +package com.ruoyi.system.config.push.android; + +import com.ruoyi.system.config.push.AndroidNotification; + +public class AndroidUnicast extends AndroidNotification { + public AndroidUnicast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "unicast"); + } + + public void setDeviceToken(String token) throws Exception { + setPredefinedKeyValue("device_tokens", token); + } + +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSBroadcast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSBroadcast.java new file mode 100644 index 0000000..54bf6ec --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSBroadcast.java @@ -0,0 +1,12 @@ +package com.ruoyi.system.config.push.ios; + +import com.ruoyi.system.config.push.IOSNotification; + +public class IOSBroadcast extends IOSNotification { + public IOSBroadcast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "broadcast"); + + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSCustomizedcast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSCustomizedcast.java new file mode 100644 index 0000000..b9ba2bf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSCustomizedcast.java @@ -0,0 +1,22 @@ +package com.ruoyi.system.config.push.ios; + +import com.ruoyi.system.config.push.IOSNotification; + +public class IOSCustomizedcast extends IOSNotification { + public IOSCustomizedcast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "customizedcast"); + } + + public void setAlias(String alias,String aliasType) throws Exception { + setPredefinedKeyValue("alias", alias); + setPredefinedKeyValue("alias_type", aliasType); + } + + public void setFileId(String fileId, String aliasType) throws Exception { + setPredefinedKeyValue("file_id", fileId); + setPredefinedKeyValue("alias_type", aliasType); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSFilecast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSFilecast.java new file mode 100644 index 0000000..aeecff8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSFilecast.java @@ -0,0 +1,15 @@ +package com.ruoyi.system.config.push.ios; + +import com.ruoyi.system.config.push.IOSNotification; + +public class IOSFilecast extends IOSNotification { + public IOSFilecast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "filecast"); + } + + public void setFileId(String fileId) throws Exception { + setPredefinedKeyValue("file_id", fileId); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSGroupcast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSGroupcast.java new file mode 100644 index 0000000..3b99ace --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSGroupcast.java @@ -0,0 +1,16 @@ +package com.ruoyi.system.config.push.ios; + +import com.ruoyi.system.config.push.IOSNotification; +import org.json.JSONObject; + +public class IOSGroupcast extends IOSNotification { + public IOSGroupcast(String appkey,String appMasterSecret) throws Exception { + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "groupcast"); + } + + public void setFilter(JSONObject filter) throws Exception { + setPredefinedKeyValue("filter", filter); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSUnicast.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSUnicast.java new file mode 100644 index 0000000..37f8d2f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/push/ios/IOSUnicast.java @@ -0,0 +1,15 @@ +package com.ruoyi.system.config.push.ios; + +import com.ruoyi.system.config.push.IOSNotification; + +public class IOSUnicast extends IOSNotification { + public IOSUnicast(String appkey,String appMasterSecret) throws Exception{ + setAppMasterSecret(appMasterSecret); + setPredefinedKeyValue("appkey", appkey); + this.setPredefinedKeyValue("type", "unicast"); + } + + public void setDeviceToken(String token) throws Exception { + setPredefinedKeyValue("device_tokens", token); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotificationsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotificationsServiceImpl.java index da4b5ab..9224997 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotificationsServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotificationsServiceImpl.java @@ -142,11 +142,11 @@ public class NotificationsServiceImpl implements INotificationsService for (NotificationRecords record : records) { // 如果记录已经发送过,则不再创建新记录,只更新状态 + boolean sendResult = sendNotificationToThirdParty(notification, record); if ("sent".equals(record.getStatus())) { // 调用第三方接口发送通知 - boolean sendResult = sendNotificationToThirdParty(notification, record); - + if (sendResult) { successCount++; @@ -162,8 +162,7 @@ public class NotificationsServiceImpl implements INotificationsService else { // 调用第三方接口发送通知 - boolean sendResult = sendNotificationToThirdParty(notification, record); - + if (sendResult) { // 更新记录状态为已发送