1. 引入依赖
cn.jpush.api
jpush-client
3.3.10
cn.jpush.api
jiguang-common
1.1.4
com.notnoop.apns
apns
1.0.0.Beta6
com.huawei.android
huawei-push
1.1.1
com.xiaomi
mipush
2.2.20
com.vivo
vpush
1.1.0
2. 定义推送接口
public interface PushMessage {
/**
* 通知栏消息推送
*/
public int pushNcMsg(PushMessageDTO pushMessageDTO, String content, String title) throws Exception ;
/**
* 透传推送
*/
public int pushTransMsg(PushMessageDTO pushMessageDTO, String content) throws Exception ;
}
//辅助类 appName, appId, appKey ,masterSecret请去官网注册账号,添加app应用获取
@Getter
@Setter
@ToString
public class PushMessageDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String appName; //应用名称
private String appId; //应用id
private String appKey; //应用key
private String masterSecret; //秘钥
private String target; //推送目标
private String platform; //推送平台
}
3. 实现类
//苹果都是透传推送
@Component
public class ApplePushMessage implements PushMessage {
@Override
public int pushNcMsg(PushMessageDTO pushMessageDTO, String content, String title) throws Exception {
// TODO Auto-generated method stub
return 0;
}
//苹果的appKey是voippush.p12文件路径, masterSecret是密码
@Override
public int pushTransMsg(PushMessageDTO pushMessageDTO, String content) throws Exception {
ApnsService service = APNS.newService()
.withCert(pushMessageDTO.getAppKey(), pushMessageDTO.getMasterSecret())
.withSandboxDestination()
.withAppleDestination(true)
.build();
String payloadApple = APNS.newPayload()
.alertBody(content)
.build();
String token = pushMessageDTO.getTarget();
service.push(token, payloadApple);
return 1;
}
}
//华为
@Component
public class HuaWeiPushMessage implements PushMessage {
private static String tokenUrl = "https://login.cloud.huawei.com/oauth2/v2/token"; // 获取认证Token的URL
private static String apiUrl = "https://api.push.hicloud.com/pushsend.do"; // 应用级消息下发API
private static String accessToken;// 下发通知消息的认证Token
private static long tokenExpiredTime; // accessToken的过期时间
@Override
public int pushNcMsg(PushMessageDTO pushMessageDTO, String content, String title) throws Exception {
if (tokenExpiredTime <= System.currentTimeMillis()) {
refreshToken(pushMessageDTO.getMasterSecret(), pushMessageDTO.getAppKey());
}
/* PushManager.requestToken为客户端申请token的方法,可以调用多次以防止申请token失败 */
/* PushToken不支持手动编写,需使用客户端的onToken方法获取 */
JSONArray deviceTokens = new JSONArray();// 目标设备Token
deviceTokens.add(pushMessageDTO.getTarget());
JSONObject body = new JSONObject();// 仅通知栏消息需要设置标题和内容,透传消息key和value为用户自定义
body.put("title", title);// 消息标题
body.put("content", content);// 消息内容体
JSONObject param = new JSONObject();
param.put("appPkgName", pushMessageDTO.getAppName());// 定义需要打开的appPkgName
JSONObject action = new JSONObject();
action.put("type", 3);// 类型3为打开APP,其他行为请参考接口文档设置
action.put("param", param);// 消息点击动作参数
JSONObject msg = new JSONObject();
msg.put("type", 3);// 3: 通知栏消息,异步透传消息请根据接口文档设置
msg.put("action", action);// 消息点击动作
msg.put("body", body);// 通知栏消息body内容
JSONObject hps = new JSONObject();// 华为PUSH消息总结构体
hps.put("msg", msg);
JSONObject payload = new JSONObject();
payload.put("hps", hps);
String postBody = MessageFormat.format("access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
URLEncoder.encode(accessToken, "UTF-8"), URLEncoder.encode("openpush.message.api.send", "UTF-8"),
URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000), "UTF-8"), URLEncoder.encode(deviceTokens.toString(), "UTF-8"),
URLEncoder.encode(payload.toString(), "UTF-8"));
String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + pushMessageDTO.getAppKey() + "\"}", "UTF-8");
httpPost(postUrl, postBody, 5000, 5000);
return 1;
}
@Override
public int pushTransMsg(PushMessageDTO pushMessageDTO, String context) throws Exception {
if (tokenExpiredTime <= System.currentTimeMillis()) {
refreshToken(pushMessageDTO.getMasterSecret(),pushMessageDTO.getAppKey());
}
/* PushManager.requestToken为客户端申请token的方法,可以调用多次以防止申请token失败 */
/* PushToken不支持手动编写,需使用客户端的onToken方法获取 */
JSONArray deviceTokens = new JSONArray();// 目标设备Token
deviceTokens.add(pushMessageDTO.getTarget());
JSONObject body = new JSONObject();
body.put("online", 1);
JSONObject param = new JSONObject();
param.put("appPkgName", pushMessageDTO.getAppName());
JSONObject action = new JSONObject();
action.put("type", 3);// 类型3为打开APP,其他行为请参考接口文档设置
action.put("param", param);// 消息点击动作参数
JSONObject msg = new JSONObject();
msg.put("type", 1);// 1: 透传异步消息,通知栏消息请根据接口文档设置
msg.put("body", body.toString());// body内容不一定是JSON,可以是String,若为JSON需要转化为String发送
msg.put("action", action);// 消息点击动作
JSONObject hps = new JSONObject();// 华为PUSH消息总结构体
hps.put("msg", msg);
JSONObject payload = new JSONObject();
payload.put("hps", hps);
String postBody = MessageFormat.format("access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
URLEncoder.encode(accessToken, "UTF-8"), URLEncoder.encode("openpush.message.api.send", "UTF-8"),
URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000), "UTF-8"), URLEncoder.encode(deviceTokens.toString(), "UTF-8"),
URLEncoder.encode(payload.toString(), "UTF-8"));
String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + pushMessageDTO.getAppKey() + "\"}", "UTF-8");
httpPost(postUrl, postBody, 5000, 5000);
return 1;
}
// 获取下发通知消息的认证Token
private void refreshToken(String appSecret, String appId) throws IOException {
String msgBody = MessageFormat.format("grant_type=client_credentials&client_secret={0}&client_id={1}", URLEncoder.encode(appSecret, "UTF-8"), appId);
String response = httpPost(tokenUrl, msgBody, 5000, 5000);
JSONObject obj = JSONObject.parseObject(response);
accessToken = obj.getString("access_token");
tokenExpiredTime = System.currentTimeMillis() + (obj.getLong("expires_in") - 5 * 60) * 1000;
}
//httpPost请求
private String httpPost(String httpUrl, String data, int connectTimeout, int readTimeout) throws IOException {
OutputStream outPut = null;
HttpURLConnection urlConnection = null;
InputStream in = null;
try {
URL url = new URL(httpUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
urlConnection.setConnectTimeout(connectTimeout);
urlConnection.setReadTimeout(readTimeout);
urlConnection.connect();
// POST data
outPut = urlConnection.getOutputStream();
outPut.write(data.getBytes("UTF-8"));
outPut.flush();
// read response
if (urlConnection.getResponseCode() < 400) {
in = urlConnection.getInputStream();
} else {
in = urlConnection.getErrorStream();
}
List lines = IOUtils.readLines(in, urlConnection.getContentEncoding());
StringBuffer strBuf = new StringBuffer();
for (String line : lines) {
strBuf.append(line);
}
System.out.println(strBuf.toString());
return strBuf.toString();
} finally {
IOUtils.closeQuietly(outPut);
IOUtils.closeQuietly(in);
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
}
//小米
@Component
public class XiaoMiPushMessage implements PushMessage {
@Override
public int pushNcMsg(PushMessageDTO pushMessageDTO, String content, String title) throws Exception {
Constants.useOfficial();
Sender sender = new Sender(pushMessageDTO.getMasterSecret());
String messagePayload = "{\"online\":\"1\"}";
Message message = new Message.Builder()
.title(title)
.description(content)
.payload(messagePayload)
.restrictedPackageName(pushMessageDTO.getAppName())
.passThrough(0)
.extra(Constants.EXTRA_PARAM_NOTIFY_EFFECT, Constants.NOTIFY_LAUNCHER_ACTIVITY)
.notifyType(-1)
.build();
sender.sendToAlias(message, pushMessageDTO.getTarget(), 1);
return 1;
}
@Override
public int pushTransMsg(PushMessageDTO pushMessageDTO, String content) throws Exception {
Constants.useOfficial();
Sender sender = new Sender(pushMessageDTO.getMasterSecret());
String messagePayload = "{\"online\":\"1\"}";
Message message = new Message.Builder().description(content).payload(messagePayload).restrictedPackageName(pushMessageDTO.getAppName())
.passThrough(1)
.notifyType(-1)
.build();
sender.sendToAlias(message, pushMessageDTO.getTarget(), 1);
return 1;
}
}
//vivo暂时只有通知栏消息推送
@Component
public class VivoPushMessage implements PushMessage {
@Override
public int pushNcMsg(PushMessageDTO pushMessageDTO, String content, String title) throws Exception {
Sender sender = new Sender(pushMessageDTO.getMasterSecret());//注册登录开发平台网站获取到的appSecret
Result result = sender.getToken(Integer.valueOf(pushMessageDTO.getAppId()), pushMessageDTO.getAppKey());//注册登录开发平台网站获取到的appId和appKey
Sender senderMessage = new Sender(pushMessageDTO.getMasterSecret(),result.getAuthToken());
Message singleMessage = new Message.Builder()
.regId(pushMessageDTO.getTarget())//该测试手机设备订阅推送后生成的regId
.notifyType(3)
.title(title)
.content(content)
.timeToLive(1000)
.skipType(2)
.skipContent("http://www.vivo.com")
.networkType(-1)
.requestId("1234567890123456")
.build();
Result resultMessage = senderMessage.sendSingle(singleMessage);
return 1;
}
@Override
public int pushTransMsg(PushMessageDTO pushMessageDTO, String content) throws Exception {
return 1;
}
}
4 . 统一消息推送接口
public void pushMessageToAllPlatform(PushMessageVO pushMessageVO) {
String[] sipCodeList = pushMessageVO.getSipCode().split(",");
// 查询推送信息,在数据库构建的表(见文章末尾)
List pushMessageDTOList = rxSipPlatformTargetMapper.selectPushMessage(pushMessageVO.getAppName(), sipCodeList);
// 推送消息
if (null != pushMessageDTOList && !pushMessageDTOList.isEmpty()) {
for (PushMessageDTO pushMessageDTO : pushMessageDTOList) {
pushMessageToApp(pushMessageDTO, pushMessageVO.getPushType(), pushMessageVO.getContent(), pushMessageVO.getTitle());
}
}
}
private int pushMessageToApp(PushMessageDTO pushMessageDTO, String pushType,String content, String title) {
try {
PushMessage pushMessage = (PushMessage) SpringUtil.getBean(PlatformEnum.getNameByType(pushMessageDTO.getPlatform()));
if (StringUtils.isBlank(pushType) || "NC".equals(pushType)) {
pushMessage.pushNcMsg(pushMessageDTO, content, title);
}
if (StringUtils.isBlank(pushType) || "TS".equals(pushType)) {
pushMessage.pushTransMsg(pushMessageDTO, content);
}
} catch (Exception e) {
log.error(e.toString());
return 0;
}
return 1;
}
//辅助参数类
public enum PlatformEnum {
JIGUANG ("JIGUANG","jiGuangPushMessage"),
HUAWEI ("HUAWEI","huaWeiPushMessage"),
APPLE ("APPLE","applePushMessage"),
XIAOMI ("XIAOMI","xiaoMiPushMessage"),
OPPO ("OPPO","oppoPushMessage"),
VIVO ("VIVO","vivoPushMessage");
private String platformType;
private String platformName;
private PlatformEnum(String platformType, String platformName) {
this.platformType = platformType;
this.platformName = platformName;
}
public String getPlatformType() {
return platformType;
}
public void setPlatformType(String platformType) {
this.platformType = platformType;
}
public String getPlatformName() {
return platformName;
}
public void setPlatformName(String platformName) {
this.platformName = platformName;
}
public static String getNameByType(String platformType) {
for (PlatformEnum e : PlatformEnum.values()) {
if (e.getPlatformType().equals(platformType)) {
return e.getPlatformName();
}
}
return null;
}
}
public class PushMessageVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value="app名称" ,required=true)
private String appName;
@ApiModelProperty(value="设备编号(多个用逗号隔开)" ,required=true)
private String sipCode;
@ApiModelProperty(value="消息推送模式(NC-通知栏消息, TS-透传消息)" ,required=true)
private String pushType;
@ApiModelProperty(value="消息内容" ,required=true)
private String content;
@ApiModelProperty(value="消息标题")
private String title;
}
数据库表:
CREATE TABLE `rx_sip_platform_target` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`sip_code` varchar(255) DEFAULT NULL COMMENT '设备编号',
`platform` varchar(255) DEFAULT '0' COMMENT '推送平台',
`target` varchar(255) DEFAULT NULL COMMENT '推送目标',
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) COMMENT='设备登录推送目标表';
CREATE TABLE `rx_app_config` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`app_name` varchar(64) NOT NULL COMMENT '应用名称',
`app_id` varchar(20) DEFAULT NULL COMMENT '应用ID',
`app_key` varchar(128) NOT NULL COMMENT 'appKey',
`master_secret` varchar(64) NOT NULL COMMENT 'masterSecret',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT NULL,
`platform` varchar(255) DEFAULT '0' COMMENT '推送平台',
PRIMARY KEY (`id`)
) COMMENT='应用推送平台配置信息';
//苹果的appKey是voippush.p12文件路径, masterSecret是密码
//查询推送集sql