Salesforce Force.com Site 集成微信公众平台 (五) 菜单处理

这一篇我们介绍微信菜单和 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]  

Samba

Read more posts by this author.