简介

AbleCloud打通了和微信的接入,实现了双方的云端对接,开启了微信硬件开发的全新模式。在该模式下,结合AbleCloud提供的jssdk,厂商可以在您的微信公众号里实现和native app几乎一样的智能硬件相关功能特性,并且保证足够的安全性。本文介绍连接微信公众平台mp和AbleCloud云平台的关键服务开发框架ac-weixin-server。 基于ac-weixin-server,您只需要关注常规的公众号开发内容,所有与AbleCloud云端交互的逻辑均已在该框架中完成,对设备的授权、控制等操作,直接在HTML页面中调用AbleCloud提供的jssdk即可。jssdk的接口说明请参见jssdk手册

架构框图

AbleCloud实现的和微信云端对接,其架构如下:

arch

在整个架构中,厂商的公众号开发人员只需要根据厂商自己的需求,扩展高亮部分ac-weixin-server的功能开发便可,图中其它模块不用太关注。

功能列表

整个ac-weixin-server的主要功能提供http服务器功能,提供如下几个功能:

框架说明

由于ac-weixin-server进行了高度的抽象,厂商只需要继承抽象类ACMPService,实现处理函数即可。通常情况下,在函数handleMsg内部,根据已经解析好的参数reqMap中的MsgType,做出不同的处理逻辑,具体请参见后面的demo。

主体框架类

public abstract class ACMPService {
    /**
     * 厂商为了开发自己的公众号,只需要实现该抽象方法即可。
     *
     * @param reqMap    已经由框架解析成key-value对的req
     * @param req       原生req,如果无特殊需求,通常情况下可以不使用该参数,直接用第一个参数reqMap即可
     * @param resp      处理请求后的response
     * @return  String  返回微信公众平台要求的xml文档格式字符串,可参考ACDefaultMPService的代码。
     * @throws Exception
     */
    public abstract String handleMsg(Map<String, String> reqMap,
                                     HttpServletRequest req,
                                     HttpServletResponse resp) throws Exception;

    /**
     * 允许开发者通过WebAppContext扩展框架定义的Servlet服务能力。这些扩展的Servlet的位于根路径"/"下。
     * @param webApp Servelet容器。
     */
    public void extendResource(WebAppContext webApp);

    /**
     * 设置AblceCloud远程服务的访问代理。
     * @param ac AbleCloud远程服务的访问代理。
     */
    public void setACCloudAgent(AC ac);

    /**
     * 设置AblceCloud远程服务的访问代理。
     * @return 一个AC对象,是AbleCloud远程服务的访问代理。
     */
    public AC getACCloudAgent();

    /**
     * 同步AbleCloud平台及微信硬件平台记录的用户-设备绑定信息。同时检查指定设备的在线状态。
     * @details 框架接收到微信硬件平台推送的subscribe_status/unsubscribe_status事件时(比如用户打开或关闭公众号主界面时),会调用本方法同步两个平台之间的信息。
     * 开发者也可根据实际需求主动调用该方法执行数据同步(比如处理蓝牙设备绑定关系的同步)。
     * @param fromUser 待检查的用户的微信OpenID。
     * @param physicalIdOfStatus 需要检查其在线状态的设备的物理ID。
     * @return 返回true表示设备在线,否则表示设备不在线。
     */
    public boolean syncBindings(String fromUser, String physicalIdOfStatus);
}

获取Access Token

ac-weixin-server提供了公众号Access Token的管理功能。在您开发公众号的过程,如果需要用到access token,只需调用utils包中AccessTokenKeeper的静态方法getTokenStr即可拿到公众号的Access Token,不用关注繁琐的获取、刷新过程。

public class AccessTokenKeeper {
    public static String getTokenStr();
}

获取JS API Ticket

ac-weixin-server提供了公众号微信JS API Ticket的管理功能。在您开发公众号的过程,如果需要用到JS API Ticket,只需调用utils包中JsApiTicketKeeper的静态方法getTicketStr即可拿到公众号的JS API Ticket,不用关注繁琐的获取、刷新过程。

public class JsApiTicketKeeper {
    public static String getTicketStr();
}

HttpUtil

为了方便主动向微信公众平台发送消息,封装了这个实用工具类:

public class HttpUtil {
    /**
     * 发送基本的GET请求
     *
     * @param url       目的地址url
     * @return  String  GET请求返回结果content
     */
    public static String executeGet(String url);

    /**
     * 发送GET请求
     *
     * @param url   目的地址url,该url的pattern中带有ACCESS_TOKEN,
     *              在该函数内部会通过AccessTokenKeeper获取正确的token替换,
     *              然后调用executeGet执行GET请求。
     */
    public static String doGet(String url);

    /**
     * 发送基本的POST请求
     *
     * @param url       目的地址url
     * @param body      POST请求内容
     * @return  String  POST请求返回结果
     */
    public static String executePost(String url, String body);

    /**
     * 发送POST请求
     * @param url   目的地址url,该url的pattern中带有ACCESS_TOKEN,
     *              在该函数内部会通过AccessTokenKeeper获取正确的token替换,
     *              然后调用executeGet执行GET请求。
     */
    public static String doPost(String url, String body);
}

自定义Servlet

在继承抽象类ACMPService、并实现其抽象方法handleMsg的类中,厂商可以通过重载ACMPService::extendResource方法来添加自定义的Servlet,实现对框架的扩展。代码示例如下:

/**
 * 重载父类的 extendResource 方法,添加自定义的Servlet。这些扩展的Servlet位于根路径"/"下。
 * @param webApp 添加扩展Servlet的容器。
 */
@Override
public void extendResource(WebAppContext webApp) {
    AC ac = this.getACCloudAgent();
    webApp.addServlet(new ServletHolder(new AirListServlet()), "/airlist");
    webApp.addServlet(new ServletHolder(new AirCodeServlet(ac)), "/aircode");
}

网页授权并转向指定页面

本框架的Servlet类ACWXOauthServlet封装了微信提供的网页授权功能。该Servlet的地址是:/weixin/oauth2。 当提供redirect_url参数时,可通过该Servlet实现完成微信用户网页授权并转向指定页面的功能。 如将公众号的某VIEW类型菜单项的URL设置为:http://www.example.com/weixin/oauth2?redirect_url=http://www.example.com/some_page.html,那么用户点击该菜单打开微信客户端的浏览器后, 将首先跳转至微信的网页授权页面。完成授权后,浏览器将最终自动跳转至参数redirect_url指定的页面地址。

在这个过程中,ACWXOauthServlet将自动向cookies中添加如下三项键值对:

AirKiss页面

本框架封装了缺省的微信AirKiss页面(由Servlet类ACWXAirKissServlet提供)。该页面的地址是:/weixin/airkiss

开发流程

基于ac-weixin-server开发厂商自己的公众号服务,除了不用关注硬件和微信交互细节外,我们还提供了常规公众号开发的基础封装,如access token管理,微信公众平台推送过来的消息解析等功能。因此厂商开发自己的公众号将变得更加简单。

1、申请微信公众号

由于公众号属于各个厂商,因此,申请公众号这个步骤由厂商完成。申请好后,会拿到微信公众平台分配的appID和appsecret,如下图所示:

public_account

2、准备服务器

当前,AbleCloud暂时还未提供主机代理服务,因此需要厂商准备机器,并配置外网ip,最好申请一个域名。该服务器用来运行ac-weixin-server。由于微信方的要求,现在该后台服务只能使用80端口。

注:后续AbleCloud提供主机代理服务后,厂商无需再准备服务器,厂商的公众号后台ac-weixin-server服务也运行在AbleCloud的云端。

3、扩展ac-weixin-server功能

ac-weixin-server的发布库目录结构如下:

release

config目录下存放配置文件,lib目录下存放所有的jar文件,包括ac-weixin-server框架及其依赖包,继承ACMPService,开发实现自己的handleMsg编译成功后,也放入lib目录即可。start.cmd用于windows平台启动服务,start.sh用于linux平台启动服务。这里解释下webRoot目录,该目录用来存放公众号所需的html页面及其它静态资源,其结构大致如下:

webroot

4、编写html页面

在webRoot目录下,编写您所需的网页html或h5页面,如果要和已经接入AbleCloud平台的设备进行控制等功能,请在html页面中调用js目录下AbleCloud提供ac.js,该jssdk提供了和AbleCloud云端交互所需的丰富接口,具体参见jssdk手册。 当然,您可以为您的公众号编写除硬件相关页面之外的其它任意html页面。

5、修改配置文件

config目录下配置文件config.properties完整配置项如下,需要注意的是,配置项token后面在微信公众号接口配置信息时要用到。

app_id = wx37200xxxxxxxxxxxxxxxx
app_secret = a58001xxxxxxxxxxxxxxxxxxxxxxxxx
token = xxxxxx

host = 127.0.0.1
port = 80
major_domain = testwx
sub_domain = demo
developer_id = 2
access_key = 5a11d05exxxxxxxxxxxxxxxxxxxxxxxx
secret_key = 61a5edd9xxxxxxxxxxxxxxxxxxxxxxxx

router_addr = http://test.ablecloud.cn:5000

redirect_url_after_airkiss = http://www.xxx.com/xxx.html
servlet_context_path_prefix =
weixin_mp_message_handler_class = com.ablecloud.weixin.service.ACDefaultMPService

上述配置项中:

6、部署ac-weixin-server

当开发完消息处理handler,html页面后,便可以部署整个微信公众号后台服务。我们提供了启动脚本,部署变得非常简单。

windows平台

windows下在cmd中运行如下命令启动服务

start.cmd

linux平台

linux下在终端运行如下命令启动服务

sh start.sh

真正线上启动,需要用nohup等将服务放后台,如nohup sh start.sh &,并且可能需要使用supervisor配合,防止进程异常退出。

7、修改公众号接口配置信息

在微信公众平台修改接口配置信息,如下图所示:

modify_config

点击提交,正常情况下,会收到微信公众平台提示验证成功。如果验证失败,请查看log目录下的日志。

注:这里填写的token要和配置文件中的token一致。并且URL的URI部分(除去域名)必须是/weixin/message,域名部分则是您服务器所申请的域名。

8、创建公众号菜单

根据微信公众平台的要求,创建菜单即可,这里不做详述。

注:菜单中需要创建一个VIEW类型的菜单,用来激活设备之用,该菜单url的URI部分必须是/weixin/airkiss,域名部分则是您服务器所申请的域名。

demo

这里以一个非常简单的实例演示厂商如何扩展开发完整的公众账号。该demo在实现handleMsg接口时,处理了MsgType为event,并且具体的event为用户关注公众号消息subscribe,在收到此请求的时候发送欢迎语给用户。 你可以在handlMsg中处理任何微信公众平台的消息,如文本text,图片image,语音voice,视频video等,当然,你也可以处理任意的自定义菜单点击事件消息。一句话,所有微信公众平台推送过来的消息,你都能方便的进行处理。

新建工程

这里以Intellij为例简单说明,首先新建工程,然后修改工程属性配置,选择JDK为1.7和语言级别为7.0,如下图所示:

project

修改library,添加对ac-weixin-server框架的依赖,如下图所示(具体的依赖目录需要您根据存放框架lib库的地址不同而进行调整):

lib

修改工程pom.xml配置

DemoServer的完整编译配置如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ablecloud.weixin.demo</groupId>
    <artifactId>DemoServer</artifactId>
    <version>1.0.0</version>

    <properties>
        <ablecloud.weixin.lib.dir>/home/chenpeng/IdeaProjects/ac-weixin-server/target/lib</ablecloud.weixin.lib.dir>
    </properties>

    <build>
        <plugins>
            <plugin>
                <!--this plugin and dependency jars are used for testing-->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.maven.surefire</groupId>
                        <artifactId>surefire-junit47</artifactId>
                        <version>2.18.1</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <encoding>UTF-8</encoding>
                    <compilerArguments>
                        <extdirs>${ablecloud.weixin.lib.dir}</extdirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <outputDirectory>${project.build.directory}/lib</outputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

其中绝大部分内容无需修改,您进需要修改配置项存放ac-weixin-server框架的lib库路径即可:

<properties>
        <ablecloud.weixin.lib.dir>/home/chenpeng/IdeaProjects/ac-weixin-server/target/lib</ablecloud.weixin.lib.dir>
    </properties>

编译工程

在终端运行命令mvn package即可将DemoServer编译成功,编译后的jar包存放于工程主目录下的target/lib路径下,这里为DemoServer-1.0.0.jar

运行公众号后端服务

  1. 将上步编译好的您公众号具体的处理逻辑jar包DemoServer-1.0.0.jar拷贝到AbleCloud发布的框架lib目录中;
  2. 修改框架配置文件config/config.properties中的配置项weixin_mp_message_handler_class为你具体的处理类名,这里是com.ablecloud.weixin.ACDemoMPService;
  3. 运行sh start.sh即可。

源码展示

package com.ablecloud.weixin;

import com.ablecloud.weixin.common.ACWXMsgType;
import com.ablecloud.weixin.common.ACWXXmlResp;
import com.ablecloud.weixin.service.ACMPService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

public class ACDemoMPService extends ACMPService {
    public String handleMsg(Map<String, String> reqMap,
                            HttpServletRequest req,
                            HttpServletResponse resp) throws Exception {
        String msgType = reqMap.get("MsgType");
        String fromUser = reqMap.get("FromUserName");
        String toUser = reqMap.get("ToUserName");
        String xmlResp;

        switch (msgType) {
            case ACWXMsgType.TEXT: {
                break;
            }

            case ACWXMsgType.EVENT: {
                String event = reqMap.get("Event");
                switch (event) {
                    case ACWXMsgType.Event.SUBSCRIBE:
                        // handle subscribe msg
                        xmlResp = "欢迎关注AbleCloud智能硬件公众号,开启全新的微信硬件功能开发模式。<a href='http://docs.ablecloud.cn/sdk/js_sdk_manual/'>了解更多</a>";
                        return ACWXXmlResp.buildTextResp(fromUser, toUser, xmlResp);
                }
                break;
            }
        }
        return "";
    }
}

这么一个简单的demo实现,便完成了公众号后台的开发,当用户扫描设备二维码时,会自动关注您的公众号,您同时也给了用户一个热烈的欢迎辞。