1. 需求
因门户app开发需求,要求对从crm推送过来的新订单对用户进行定向推送提醒,使得相关人员能够及时处理订单(注:以下是按照自己的业务需求来写的,相关方法没有做抽取和封装,只是提供最基础的实现参考)
2. 实现方案
使用极光推送作为推送服务,极光推送为免费版即可满足需求,如果后期推送量大,可申请使用付费版本。极光推送分为客户端开发和服务端开发。客户端有android和ios相关开发人员参照极光官网官方文档完成对app的开发集成。而服务端采用调用REST API v3 – Push接口的方式进行调用。本接口为restful风格,只需要按照极光的api要求格式发送https请求即可实现调用,不需要在服务器端编写java api代码。因需求要求为定向推送,按照极光官网说明,要过要定向推送,需要服务端保存用户和对应的registration_id。registration_id为根据用户登录的设备客户端调用极光api生成的。当crm推送过来的新订单时,服务端根据customer_id查找对应的用户,然后再根据用户和registration_id查出用户对应的registration_id,根据registration_id调用极光REST API v3 – Push接口即可。
3. 具体实现
3.1 申请极光账号得到AppKey和MasterSecret
3.2保存用户和对应的registration_id
要实现定向对应需要保存用户和对应的registration_id,新增表emp_registration_id_rel如下:
在用户使用移动设备登录时,保存用户账号和registration_id,代码如下:
/**
* 登陆验证
*/
@RequestMapping("login")
public @ResponseBody String login(HttpServletRequestrequest,HttpServletResponse response) {
JSONObject json = CommonUtil.getParams(request);
// 人员账号
String empAccount =(String)json.getString("empAccount");
// 人员密码
String password =(String)json.getString("password");
// 极光推送registration_id(设备标识)
String registrationId =(String)json.getString("registrationId");
// 查询用户数据
String str = appLoginService.doLogin(empAccount,password);
//登录成功保存极光ID
appLoginService.saveRegistrationId(empAccount,registrationId);
//appLoginService.updateSendJpush("06","100000042", "1016052401808581");
return str;
}
保存逻辑为:为防止用户在多台设备上登录过,或者一台设备多个用户登录过而导致定向推送信息误推送,保存时先根据用户账号或者registration_id作为查询条件,如果能查出记录,则先根据用户账号或者registration_id作为
条件删除记录在做保存操作,登录成功保存极光ID方法如下:
/**
*
* @param empAccount用户账号
* @param registration_id极光推送设备id
* @return
*/
public int saveRegistrationId(StringempAccount, String registrationId) {
Map<String, String> map = new HashMap<String, String>();
map.put("empAccount", empAccount);
map.put("registrationId", registrationId);
List<Map<String, String>>list = jpushDao.getRegId(map);
if(list!=null&&list.size()>0){
jpushDao.delRegId(map);
}
int result =jpushDao.saveRegId(map);
return result;
}
相关sql如下:
<selectid="getRegId"resultType="map"parameterType="map">
select * from emp_registration_id_rel awhere 1=1
<iftest="empAccount != null and empAccount != '' ">
and a.emp_account = #{empAccount}
</if>
<iftest="registrationId != null and registrationId != ''">
or a.registration_id =#{registrationId}
</if>
</select>
<!-- 根据用户账号或registration_id删除相关信息-->
<deleteid="delRegId"parameterType="map">
delete from
emp_registration_id_rel where emp_account= #{empAccount} or
registration_id = #{registrationId}
</delete>
<!-- 保存用户账号和 registration_id-->
<insertid="saveRegId"parameterType="map">
insert into
emp_registration_id_rel(emp_account,registration_id) values
(#{empAccount},
#{registrationId})
</insert>
<!-- 根据biz_info_t表中的customer_id查看对应的emp_account -->
<selectid="getEmpAccount"resultType="map"parameterType="string">
select t1.emp_account from om_employee_tt1,biz_info_t
t2,customerid_org_rel_t t3
where t1.org_id=t3.org_id andt2.customer_id=t3.customer_id and
t2.customer_id=#{registrationId}
group by
t1.emp_account
</select>
3.3调用封装的极光推送方法进行定向对送
因为极光推送要求使用restful接口调用时要发送https请求,推送内容完全使用 JSON 的格式,所以在JPushUtil中封装了sendSSLPostRequest方法用于方式https请求,请求方式为post,请求数据格式设置为json。发送json数据需要设置contentType 为 application/json。具体的json格式可以参照http://docs.jiguang.cn/server/rest_api_v3_push/
因极光使用 HTTP Basic Authentication 的方式做访问授权,需要我们在HTTP Header(头)里加一个字段(Key/Value对),如下图所示:
相关代码:
String base64_auth_string ="Basic "+getBASE64(JPushUtil.AppKey+":"+JPushUtil.masterScrect);
System.out.println("base64_auth_string"+base64_auth_string);
httpPost.addHeader("Authorization", base64_auth_string);
其中的AppKey,masterScrect为我们申请极光账号得到AppKey和MasterSecret。
具体完整代码如下:
package com.tower.webframe.app.tool;
import java.io.IOException;
importjava.io.UnsupportedEncodingException;
importjava.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
importjava.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
importorg.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
importorg.apache.http.client.entity.UrlEncodedFormEntity;
importorg.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
importorg.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
importorg.apache.http.impl.client.DefaultHttpClient;
importorg.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class JPushUtil {
privatestatic final String AppKey ="xxxxxxxxxxx";//登录极光官网申请的appKey
privatestatic final String masterScrect ="xxxxxxxxxxxxxxx";//登录极光官网申请的masterScrect
publicstatic void main(String[] args) throws Exception {
Map<String,String> params = new HashMap<String, String>();
Strings =
// "{"
// +"\"platform\":\"all\","
// + "\"audience\" :\"all\","
// + "\"notification\": {"
// + "\"android\": {"
// + "\"alert\": \"订单确认:需求单号:123456789!\","
// + "\"title\": \"Send to Android\","
// + "\"builder_id\": 1,"
// + "\"extras\": {"
// + "\"newsid\": 321"
// + "}"
// + "},"
// + "\"ios\": {"
// + "\"alert\": \"订单确认:需求单号:123456789\","
// + "\"sound\": \"default\","
// + "\"badge\": \"+1\","
// + "\"extras\": {"
// + "\"newsid\": 321"
// + "}"
// + "}"
// + "},"
// + "\"message\": {"
// + "\"msg_content\": \"订单确认:需求单号:123456789\","
// + "\"content_type\": \"text\","
// + "\"title\": \"msg\","
// + "\"extras\": {"
// + "\"key\": \"value\""
// + "}"
// + "},"
// + "\"sms_message\":{"
// + "\"content\":\"sms msg content\","
// + "\"delay_time\":3600"
// + "},"
// + "\"options\": {"
// + "\"time_to_live\": 60,"
// + "\"apns_production\": false"
// + "}"
// +"}";
"{"
// +"\"platform\":\"all\","
+"\"platform\":\"ios\","
+ "\"audience\" : { \"registration_id\" :[\"101d85590941878f97a\",\"141fe1da9ead512c5a8\"]},"
+"\"notification\": {"
+ "\"android\": {"
+ "\"alert\": \"订单确认:需求单号:123456789!\","
+ "\"title\": \"Sendto Android\","
+ "\"builder_id\":1,"
+ "\"extras\": {"
+ "\"newsid\":321"
+ "}"
+ "},"
+ "\"ios\": {"
+ "\"alert\": \"订单确认:需求单号:123456789\","
+ "\"sound\":\"default\","
+ "\"badge\":\"+1\","
+ "\"content-available\":true,"
+ "\"extras\": {"
+ "\"bizType\":\"06\""
+ "}"
+ "}"
+"},"
+"\"message\": {"
+ "\"msg_content\": \"订单确认:需求单号:123456789\","
+ "\"content_type\":\"text\","
+ "\"title\":\"msg\","
+ "\"extras\": {"
+ "\"bizType\":\"06\""
+ "}"
+"},"
+"\"sms_message\":{"
+ "\"content\":\"sms msgcontent\","
+ "\"delay_time\":3600"
+"},"
+"\"options\": {"
+ "\"time_to_live\":60,"
+ "\"apns_production\":false"
+"}"
+"}";
// "{"
// + "\"platform\": \"all\","
// + "\"audience\" : \"all\","
// + "\"notification\" : {"
// + "\"android\" : {"
// + "\"alert\" : \"hello,world!\","
// + "\"title\" : \"而退哦;立刻脚后跟范德萨无色的日方提供用户均可\","
// + "\"builder_id\" : 3,"
// + "\"extras\" : {"
// + "\"news_id\" :134,"
// + "\"my_key\" : \"avalue\""
// + "}"
// +"}"
// + "}"
// + "}"
// + "}";
JSONObjectjs = JSONObject.fromObject(s);
System.out.println(js);
sendSSLPostRequest("https://api.jpush.cn/v3/push",js);
}
publicstatic void sendJPush(String registration_id,String msg_content,Stringbiz_type){
Strings =
"{"
+"\"platform\":\"all\","
+ "\"audience\" : { \"registration_id\" :[\""+registration_id+"\"]},"
+"\"notification\": {"
+ "\"android\": {"
+ "\"alert\":\""+msg_content+"\","
+ "\"title\": \"Sendto Android\","
+ "\"builder_id\":1,"
+ "\"extras\": {"
+ "\"bizType\":"+biz_type+""
+ "}"
+ "},"
+ "\"ios\": {"
+ "\"alert\":\""+msg_content+"\","
+ "\"sound\":\"default\","
+ "\"badge\":\"+1\","
+ "\"extras\": {"
+ "\"bizType\":"+biz_type+""
+ "}"
+ "}"
+"},"
+"\"message\": {"
+ "\"msg_content\":\""+msg_content+"\","
+ "\"content_type\":\"text\","
+ "\"title\":\"msg\","
+ "\"extras\": {"
+ "\"bizType\":"+biz_type+""
+ "}"
+"},"
+"\"sms_message\":{"
+ "\"content\":\"sms msgcontent\","
+ "\"delay_time\":3600"
+"},"
+"\"options\": {"
+ "\"time_to_live\":60,"
+ "\"apns_production\":false"
+"}"
+"}";
JSONObjectjs = JSONObject.fromObject(s);
System.out.println(js);
sendSSLPostRequest("https://api.jpush.cn/v3/push",js);
}
/**
* 向HTTPS地址发送POST请求
*
* @param reqURL
* 请求地址
* @param params
* 请求参数
* @return 响应内容
*/
@SuppressWarnings("finally")
publicstatic String sendSSLPostRequest(String reqURL,
JSONObjectparams) {
longresponseLength = 0; // 响应长度
StringresponseContent = null; // 响应内容
HttpClienthttpClient = new DefaultHttpClient(); // 创建默认的httpClient实例
X509TrustManagerxtm = new X509TrustManager() { // 创建TrustManager
publicvoid checkClientTrusted(X509Certificate[] chain,
StringauthType) throws CertificateException {
}
publicvoid checkServerTrusted(X509Certificate[] chain,
StringauthType) throws CertificateException {
}
publicX509Certificate[] getAcceptedIssuers() {
returnnull;
}
};
try{
//TLS1.0与SSL3.0基本上没有太大的差别,可粗略理解为TLS是SSL的继承者,但它们使用的是相同的SSLContext
SSLContextctx = SSLContext.getInstance("TLS");
//使用TrustManager来初始化该上下文,TrustManager只是被SSL的Socket所使用
ctx.init(null,new TrustManager[] { xtm }, null);
//创建SSLSocketFactory
SSLSocketFactorysocketFactory = new SSLSocketFactory(ctx);
//通过SchemeRegistry将SSLSocketFactory注册到我们的HttpClient上
httpClient.getConnectionManager().getSchemeRegistry()
.register(newScheme("https", 443, socketFactory));
HttpPosthttpPost = new HttpPost(reqURL); // 创建HttpPost
List<NameValuePair>formParams = new ArrayList<NameValuePair>(); // 构建POST请求的表单参数
// for(Map.Entry<String, String> entry : params.entrySet()) {
// formParams.add(newBasicNameValuePair(entry.getKey(), entry
// .getValue()));
// }
StringEntity s = newStringEntity(params.toString(),"UTF-8");
s.setContentEncoding("UTF-8");
s.setContentType("application/json");//发送json数据需要设置contentType multipart/form-data application/json
// Stringbase64_auth_string = "Basic "+getBASE64("xxxxxxxxxxx"//登录极光官网申请的appKey"xxxxxxxxxxxxxxx"//登录极光官网申请的masterScrect );//test
Stringbase64_auth_string = "Basic "+getBASE64(JPushUtil.AppKey+":"+JPushUtil.masterScrect);
System.out.println("base64_auth_string"+base64_auth_string);
httpPost.addHeader("Authorization",base64_auth_string); //认证token
httpPost.setEntity(s);
HttpResponseresponse = httpClient.execute(httpPost); // 执行POST请求
HttpEntityentity = response.getEntity(); // 获取响应实体
if(null != entity) {
responseLength= entity.getContentLength();
responseContent= EntityUtils.toString(entity, "UTF-8");
EntityUtils.consume(entity);// Consume response content
}
System.out.println("请求地址: "+ httpPost.getURI());
System.out.println("响应状态: "+ response.getStatusLine());
System.out.println("响应长度: "+ responseLength);
System.out.println("响应内容: "+ responseContent);
}catch (KeyManagementException e) {
e.printStackTrace();
}catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}catch (UnsupportedEncodingException e) {
e.printStackTrace();
}catch (ClientProtocolException e) {
e.printStackTrace();
}catch (ParseException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
httpClient.getConnectionManager().shutdown();// 关闭连接,释放资源
returnresponseContent;
}
}
///将 s 进行 BASE64 编码
publicstatic String getBASE64(String s) {
if(s == null)
returnnull;
return(new sun.misc.BASE64Encoder()).encode(s.getBytes());
}
}