您的位置:首页 > 移动开发 > 微信开发

微信公众号-消息推送-实现固定模式下的私人订制

2016-10-31 19:45 411 查看
开发微信已经将近4个月了,但是还是不会本地调试,只能部署到服务器上,通过打日志,来看是否有错,@ALL哪位同志,如果有好的方法,可以告诉我。

先简要介绍一下微信公众号,消息推送的原理。

微信公众号只是一个平台,这个平台的功能是一样的,但是这个功能的内容却可以由这个公众号的主人决定。这些内容实现了微信公众号的私人订制。

就像关注一个公众号之后,会有一个欢迎词,都是欢迎词的功能。有的公众号推送了满满一屏的文字,有的公众号只是寥寥数字。

利用内容的不同,微信公众号得以推广。
这个功能一个测试公众号就可以完成,对微信开发有兴趣的同学可以尝试一下。

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

 
本姑娘从事的是java开发,所以文中所有的代码均为java。
第一步,我们要写代码部署到一个解析了域名的服务器上,让外界可以访问到。
为了实现消息推送功能,我们需要@RequestMapping
value值一样的2个方法,但是RequestMethod一个为GET,一个为POST.

GET方法,用于微信公众号后台配置url+token,POST方法用于接收微信公众号推送的消息。
无论GET方法,还是POST方法,都需要验证签名的正确性。

代码附上:
/**
* 验证url+token token+timestamp+nonce asc排序SHA1加密与signature对比,相同

*@param request
*@return
ture验证通过,false验证未通过
*/
public boolean checkSignature(HttpServletRequestrequest) {
 
String
signature = request.getParameter("signature");
String
timestamp = request.getParameter("timestamp");
String
nonce = request.getParameter("nonce");
String
echostr = request.getParameter("echostr");
 
logger.debug("signature=============================:" +signature);
logger.debug("timestamp=============================:" +timestamp);
logger.debug("nonce=================================:" +nonce);
logger.debug("echostr===============================:" +echostr);
 
// 1.将token,timestamp,nonce三个参数排序
//TODO
这个参数之后可能会变,暂时先写死
String
token = "qichejiadaojavaweixintoken";//这里的token值就是微信公众号后台配置的token,值必须一样。
String[]
str = new String[] {
token, timestamp,
nonce };
Arrays.sort(str);
// 2.将三个参数字符串接成一个字符串
StringBuilder
buff = new StringBuilder();
for (inti = 0;
i <str.length;i++) {
buff.append(str[i]);
}
// 3.进行sha1加密
MessageDigest
md = null;
String
result = "";
try {
md = MessageDigest.getInstance("SHA-1");
byte[]date =
md.digest(buff.toString().getBytes());
// 将字节数组转换成字符串
result = bytesToStr(date);
logger.debug("加密后" +result);
 
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
returnresult !=
null ? (result.equals(signature.toUpperCase())) :false;
}
 
// 将直接数组转换成十六进制字符串
private static String bytesToStr(byte[]byteArray) {
String
strDigest = "";
for (inti = 0;
i <byteArray.length;i++) {
strDigest += byteToHexStr(byteArray[i]);
}
returnstrDigest;
}
 
// 将一个字节转换成16进制字符串
private static String byteToHexStr(bytemByte) {
char[]Digit = {
'0','1',
'2', '3', '4',
'5','6',
'7', '8', '9',
'A','B',
'C', 'D', 'E',
'F' };
char[]temp1 =
new char[2];
temp1[0] =Digit[mByte >>> 4 & 0X0F];
temp1[1] =Digit[mByte & 0X0F];
String
str = new String(temp1);
returnstr;
}
 

GET方法验证签名正确后,直接返回签名,微信服务端得到的签名与后台配置的签名一致。url+token,即可配置成功。

代码附上:
/**
* 验证url和token,当所请求的方法为GET时,正常返回echostr即可

*@param request
*@param response
*@throws IOException
*/
@RequestMapping(value ="/eventpush", method = RequestMethod.GET)
public void EventPushGet(HttpServletRequestrequest, HttpServletResponse
response) throws IOException {
 
logger.debug("==================eventpush:Get========================");
PrintWriter
out = response.getWriter();
if (checkSignature(request)) {
out.write(request.getParameter("echostr"));
}
else {
out.write("error");
}
}
完成了GET方法,我们将代码部署到服务器上。到微信公众号后台,找到开发-->基本配置。



url处填写,我们刚刚完成的get方法的访问地址,当然要是全域名的例如:http://test.mywechat.cn/eventpush

Token处填写我们代码中用到的token,这个值可以随便定义,但是验证签名的代码用到的token值一定要和这里的一致。否则验证不通过。

EncodingASEKey处生成就可以。
消息加解密方式没有深的研究。

提交并启用。

url+token,配置成功,我们只做到了消息推送的第一步。

接下来我们来看一下,我们第一步中强调的POST方法能为我们做些什么。

附上controller代码:
@RequestMapping(value ="/eventpush", method = RequestMethod.POST)
public void EventPushPost(HttpServletRequestrequest, HttpServletResponse
response) throws IOException {
 
// 1.判断推动的数据类型
logger.debug("==================eventpush:POST========================");
PrintWriter
out = response.getWriter();
if (checkSignature(request)) {
String
message = eventPushService.DoEvent(request);
out.write(message);
}
else {
out.write("error");
}
}
微信公众号会将用户发送的消息(语音,图片),点击的事件(例如用户点击的底部的菜单)推送到我们上边完成的POST方法中。

我们如何处理这些消息,这些事件,就是我们的私人订制。

附上service代码:
public String DoEvent(HttpServletRequestrequest) {
 
String
respContent = "success";
try {
// 调用parseXml方法解析请求消息
Map<String, String>
requestMap = MessageUtil.parseXml(request);
// 发送方帐号
String
fromUserName = requestMap.get("FromUserName").toString();
logger.debug("FromUserName==================================>" +fromUserName);
// 开发者微信号
String
toUserName = requestMap.get("ToUserName").toString();
logger.debug("ToUserName====================================>" +toUserName);
// 消息类型
String
msgType = requestMap.get("MsgType").toString();
logger.debug("MsgType=======================================>" +msgType);
 
String
eventKey = requestMap.get("EventKey") ==null ?
null :requestMap.get("EventKey").toString();
logger.debug("EventKey===========================>" +eventKey);
 
String
ticket = requestMap.get("Ticket") ==null ?
null :requestMap.get("Ticket").toString();
logger.debug("Ticket===========================> " +ticket);
 
// 回复文本消息
TextMessage
textMessage = new TextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(new Date().getTime());
textMessage.setMsgType(MessageConstant.RESP_MESSAGE_TYPE_TEXT);
 
// 文本消息
if (msgType.equals(MessageConstant.REQ_MESSAGE_TYPE_TEXT)) {
 
}
// 图片消息
else if (msgType.equals(MessageConstant.REQ_MESSAGE_TYPE_IMAGE)) {
 
}
// 语音消息
else if (msgType.equals(MessageConstant.REQ_MESSAGE_TYPE_VOICE)) {
 
}
// 视频消息
else if (msgType.equals(MessageConstant.REQ_MESSAGE_TYPE_VIDEO)) {
 
}
// 地理位置消息
else if (msgType.equals(MessageConstant.REQ_MESSAGE_TYPE_LOCATION)) {
 
}
// 链接消息
else if (msgType.equals(MessageConstant.REQ_MESSAGE_TYPE_LINK)) {
 
}
// 事件推送
else if (msgType.equals(MessageConstant.REQ_MESSAGE_TYPE_EVENT)) {
// 事件类型
String
eventType = requestMap.get("Event").toString();
 
logger.debug("Event================================>" +eventType);
 
// 关注
if (eventType.equals(MessageConstant.EVENT_TYPE_SUBSCRIBE)) {
booleanbool =
false;
logger.debug(eventType +"==" + MessageConstant.EVENT_TYPE_SUBSCRIBE
+":"
+
eventType.equals(MessageConstant.EVENT_TYPE_SUBSCRIBE));
if (true ==bool) {
 
String
content = "关注欢迎词";
Integer
time = (int) (new Date().getTime() / 1000);
respContent ="<xml><ToUserName><![CDATA[" +
fromUserName
+
"]]></ToUserName><FromUserName><![CDATA[" + toUserName
+
"]]></FromUserName><CreateTime>" + time
+
"</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" +
content
+
"]]></Content></xml>";
}
}
// 取消关注
else if (eventType.equals(MessageConstant.EVENT_TYPE_UNSUBSCRIBE)) {
logger.debug(eventType +"==" + MessageConstant.EVENT_TYPE_UNSUBSCRIBE
+":"
+
eventType.equals(MessageConstant.EVENT_TYPE_UNSUBSCRIBE));
 
// wxUserService.AddOrUpdateWxUser(requestMap);
}
// 扫描带参数二维码
else if (eventType.equals(MessageConstant.EVENT_TYPE_SCAN)) {
 
}
// 上报地理位置
else if (eventType.equals(MessageConstant.EVENT_TYPE_LOCATION)) {
 
}
// 自定义菜单 CLICK
else if (eventType.equals(MessageConstant.EVENT_TYPE_CLICK)) {
logger.debug("EVENTKEY==================>" +requestMap.get("EventKey").toString());
if (requestMap.get("EventKey").toString().equals("V1001_GOOD"))
{
String
content = "推送消息内容";
Integer
time = (int) (new Date().getTime() / 1000);
respContent ="<xml><ToUserName><![CDATA[" +
fromUserName
+
"]]></ToUserName><FromUserName><![CDATA[" + toUserName
+
"]]></FromUserName><CreateTime>" + time
+
"</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" +
content
+
"]]></Content></xml>";
}
 
}
}
 
}
catch (Exception e) {
e.printStackTrace();
}
logger.debug("respContent==>" +respContent);
returnrespContent;
}
 
 
 

MessageConstant类
 
/**
 * 此类用来汇总公众号消息类型和事件类型
 *@author
zeqi
 *
 */
public class MessageConstant {
public static final StringRESP_MESSAGE_TYPE_TEXT =
null;
public static final StringREQ_MESSAGE_TYPE_TEXT =
"text";
public static final StringREQ_MESSAGE_TYPE_IMAGE =
"image";
public static final StringREQ_MESSAGE_TYPE_VOICE =
"voice";
public static final StringREQ_MESSAGE_TYPE_VIDEO =
"video";
public static final StringREQ_MESSAGE_TYPE_SHORT_VIDEO =
"shortvideo";
public static final StringREQ_MESSAGE_TYPE_LOCATION =
"location";
public static final StringREQ_MESSAGE_TYPE_LINK =
"link";
public static final StringREQ_MESSAGE_TYPE_EVENT =
"event";
public static final StringEVENT_TYPE_SUBSCRIBE =
"subscribe";
public static final StringEVENT_TYPE_UNSUBSCRIBE =
"unsubscribe";
public static final StringEVENT_TYPE_SCAN =
"SCAN";
public static final StringEVENT_TYPE_LOCATION =
"LOCATION";
public static final StringEVENT_TYPE_CLICK =
"CLICK";
public static final StringEVENT_TYPE_VIEW =
"VIEW";
}
 

当然如果不是特别个性的公众号,许多私人订制是不需要的,那么那些不需要回复的消息,响应的事件,我们统一回复“success”字符串,如果我们不回复任何内容,微信这边会发送5次重试,如果我们的服务器5秒之内给不出任何回复,微信公众号提示用户的就:



 

       下篇博客,说一下我遇到的“该公众号暂时无法提供服务,请稍后再试”的坑。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息