这一篇我们介绍微信菜单和 Salesforce 的集成。目前自定义菜单最多包括三个一级菜单,每个一级菜单最多包含五个二级菜单。一级菜单最多4个汉字,二级菜单最多8个汉字,多出来的部分会以“…”代替。请注意,创建自定义菜单后,由于微信客户端缓存,需要一定时间才能在微信客户端展现出来,最快捷的方式是重新关注微信公众账号,这样马上就能看到自定义菜单。
自定义菜单接口可实现两种类型的按钮:
click:微信服务器会通过消息接口推送类型为 event 的结构给开发者,并且带上按钮中填写的 key 值,开发者可以通过自定义的 key 值与用户进行交互。
view:微信客户端将会打开开发者在按钮中填写的 url 值(网页链接),达到打开网页的目的。
创建菜单的接口如下:
https://api.weixin.qq.com/cgi-bin/menu/create?access_token=[ACCESS_TOKEN])
其中中括号内的变量 ACCESS_TOKEN
是我们需要传入的一个参数,下面我们来看一下怎样获取这个 ACCESS_TOKEN
。
(注意: 目前服务号和通过认证的订阅号均可申请自定义菜单。本章中用到的是订阅号,且没有认证通过,但是我们可以进入公众平台测试账号进行测试自定义菜单。)
获取Access Token接口如下:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=[APPID]&secret=[APPSECRET]
方括号内的参数可以在公众平台测试账号首页找到,如下图如示
在浏览器中访问这个连接,将得到我们需要的 ACCESS_TOKEN = v1ouHtdrq5dCt8rG_WY7Y1GgLYuL5L6ZSS2UbCgTcmGu9AoR47U4WmS6_ZYqUOD2rx60G2Ao5jDnYl_O-LlPY2rxQOXOkbYQ2hp5Slp621_Iw8q3UCr3Bs4p8S3W-hgzVKOcAGAKZD
。
(注意: 该 Token 将在7200秒,也就是2个小时内失效,之后需要重新请求前面的URL获取新的 Token 。)
接下来我们创建一个简单的 Apex 网页,我们将通过这个网页来创建自定义菜单。
我们创建了一个 WeChatMenuPage Visualforce Page ,源代码如下:
<apex:page standardstylesheets="false" showHeader="false" sidebar="false" controller="WechatMenuController">
<apex:form >
<strong>测试创建微信菜单:</strong><br /><br />
<apex:commandButton value="创建微信菜单" action="{!register}" id="register" />
</apex:form>
{!msg}
<apex:pageMessages />
</apex:page>
页面显示如下:
点击按钮将调用 WechatMenuController
里的 register
方法,返回消息通过 msg 对象来显示,该对象的定义也在 WechatMenuController
里,如果有系统异常,则将通过 <apex: pageMessage/>
来显示异常堆栈信息。下面我们来看一看 WechatMenuController
代码。
public class WechatMenuController
{
public static String msg {get; set;}
public static final String accessToken = 'v1ouHtdrq5dCt8rG_WY7Y1GgLYuL5L6ZSS2UbCgTcmGu9AoR47U4WmS6_ZYqUOD2rx60G2Ao5jDnYl_O-LlPY2rxQOXOkbYQ2hp5Slp621_Iw8q3UCr3Bs4p8S3W-hgzVKOcAGAKZD';
public void register()
{
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setHeader('Accept-Encoding','gzip,deflate');
req.setHeader('Content-Type','text/xml;charset=UTF-8');
req.setHeader('User-Agent','Jakarta Commons-HttpClient/3.1');
String xml = '{"button":[{"name":"关于我们","sub_button":[{"type":"click","name":"公司简介","key":"公司简介"},{"type":"click","name":"社会责任","key":"社会责任"}]},{"name":"技术支持","sub_button":[{"type":"click","name":"文档下载","key":"文档下载"},{"type":"click","name":"技术社区","key":"技术社区"}]}]}';
req.setBody(xml);
req.setEndpoint('https://api.weixin.qq.com/cgi-bin/menu/create?access_token=' + accessToken);
String bodyRes = '';
try
{
HttpResponse res = h.send(req);
bodyRes = res.getBody();
}
catch(System.CalloutException e)
{
System.debug('Callout error: '+ e);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, e.getMessage()));
}
msg = bodyRes;
}
}
在上面的代码中,register
方法构造了一段 XML 文,并将 XML 发送到 req.setEnpoint
方法里制定的 URL 。XML 里即包含了对自定义菜单内容的具体定义,构建菜单的 XML 格式如下:
接下来我们测试一下 WeChatMenuPage
上的"创建微信菜单" Button 。效果如下:
我们忘记了另一件事情,我们需要把 https://api.weixin.qq.com
添加到 Remote Site Setting
中。添加完成如下:
再次单击这个 Button , 结果如下图所示:
这表明菜单已经创建完成,接下来我们关注一下测试公众号,之后会看到创建的菜单,如下图所示:
菜单点击事件处理方法
前面的菜单中我们定义的都是 click
类型的菜单,该类型菜单被点击的时候,微信将经由腾讯服务器向开发者指定的 URL 发送一段 XML 文,该 XML 的结构说明如下:
和我们前面处理用户发送消息的方式其实是一致的,我们可以在前文准备的方法架构基础上添加如下处理代码。打开 WeChatRestController
类并定位到 doPost
方法找到如下代如:
if(msgType != null && msgType.equals('text'))
{
rtnMsg = handleText(receiveMsg);
}
然后在该代码段的基础上添加 else 处理分支:
if(msgType != null && msgType.equals('text'))
{
rtnMsg = handleText(receiveMsg);
}
else if(msgType.equals('event'))
{
rtnMsg = handleEvent(receiveMsg);
}
上面代码的 else 分支判断,如果用户发送来的消息类型是 event
,则调用 handleEvent
方法来处理,此时用户可能是关注了微信账号,可能是取消了关注,也可能是点击了菜单…,handleEvent
方法代码如下:
private static String handleEvent(ReceiveMsg msg)
{
String event = msg.event;
String strTmp = '';
if(event.equals('subscribe'))
{
strTmp = '欢迎关注本账号!';
}
else if(event.equals('unsubscribe'))
{
strTmp = '';
}
else if(event.equals('CLICK'))
{
strTmp = '您点击了' + msg.eventKey;
}
String result = composeTextReply(msg, strTmp);
return result;
}
其中 composeTextReply
方法的定义如下:
private static String composeTextReply(ReceiveMsg msg, String strReply)
{
Datetime dt = System.now();
String returnDT = dt.format('EEEE, MMMM d, yyyy');
String replyMSG = '<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>' + returnDT + '</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[{2}]]></Content></xml>';
String[] arguments = new String[]{msg.fromUserName, msg.toUserName, strReply};
return String.format(replyMSG, arguments);
}
方法运行效果如下,当用户点击了微信菜单后,系统会自动将 eventKey
里包含的信息发送给用户,演示效果图如下所示:
附加
有兴趣的开发者可以测试查询菜单接口和删除菜单接口,在此不做过多的解释。
查询菜单接口:
https://api.weixin.qq.com/cgi-bin/menu/get?access_token=[ACCESS_TOKEN]
删除菜单接口:
https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=[ACCESS_TOKEN]