微信公众平台开发之模板消息(Java)

来源:互联网 发布:php 项目管理 编辑:程序博客网 时间:2024/04/29 20:31

由于柳峰老师的新书还没有出来,网上也没有过多介绍基于Java语言的开发微信公众平台模板消息的例子,因此有了本文。由于个人表达能力和编程能力有限,请多多包涵。本文仅介绍拥有模板消息权限的微信公众账号开发。

本文分为以下两部分:

1.开发模板消息SDK

2.构造模板消息并发送

首先看一下模板消息接口文档:

模版消息

  • 我的模板
  • 模板库

为了保证用户不受到骚扰,在开发者出现需要主动提醒、通知用户时,才允许开发者在公众平台网站中模板消息库中选择模板,选择后获得模板ID,再根据模板ID向用户主动推送提醒、通知消息。


模板消息调用时主要需要模板ID和模板中各参数的赋值内容。请注意:

1.模板中参数内容必须以".DATA"结尾,否则视为保留字;

2.模板保留符号"{{ }}"

具体调用方法

第一步:获取模板ID

通过在模板消息功能的模板库中使用需要的模板,可以获得模板ID。

第二步:请求接口

请注意,URL置空,则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android)。

POST请求

https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN

请求包为一个json:

{"touser":"OPENID","template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY","url":"http://weixin.qq.com/download","topcolor":"#FF0000","data":{"User": {"value":"黄先生","color":"#173177"},"Date":{"value":"06月07日 19时24分","color":"#173177"},"CardNumber":{"value":"0426","color":"#173177"},"Type":{"value":"消费","color":"#173177"},"Money":{"value":"人民币260.00元","color":"#173177"},"DeadTime":{"value":"06月07日19时24分","color":"#173177"},"Left":{"value":"6504.09","color":"#173177"}}}

发送效果图:

 


事件推送

在模版消息发送任务完成后,微信服务器会将是否送达成功作为通知,发送到开发者中心中填写的服务器配置地址中。

1、送达成功时,推送的XML如下:

<xml><ToUserName><![CDATA[gh_7f083739789a]]></ToUserName><FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]&g;</FromUserName><CreateTime>1395658920</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event><MsgID>200163836</MsgID><Status><![CDATA[success]]></Status></xml>

2、送达由于用户拒收(用户设置拒绝接收公众号消息)而失败时,推送的XML如下:

<xml><ToUserName><![CDATA[gh_7f083739789a]]></ToUserName><FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]></FromUserName><CreateTime>1395658984</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event><MsgID>200163840</MsgID><Status><![CDATA[failed:user block]]></Status></xml>

3、送达由于其他原因失败时,推送的XML如下:

<xml><ToUserName><![CDATA[gh_7f083739789a]]></ToUserName><FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]></FromUserName><CreateTime>1395658984</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event><MsgID>200163840</MsgID><Status><![CDATA[failed: system failed]]></Status></xml>

返回码说明

在调用模板消息接口后,会返回JSON数据包。正常时的返回JSON数据包示例:

{"errcode":0,"errmsg":"ok","msgid":200228332}

错误时的返回JSON数据,形式类似,错误码请见本页下方返回码说明。


返回码说明-1系统繁忙0请求成功40001验证失败40002不合法的凭证类型40003不合法的OpenID40004不合法的媒体文件类型40005不合法的文件类型40006不合法的文件大小40007不合法的媒体文件id40008不合法的消息类型40009不合法的图片文件大小40010不合法的语音文件大小40011不合法的视频文件大小40012不合法的缩略图文件大小40013不合法的APPID41001缺少access_token参数41002缺少appid参数41003缺少refresh_token参数41004缺少secret参数41005缺少多媒体文件数据41006access_token超时42001需要GET请求43002需要POST请求43003需要HTTPS请求44001多媒体文件为空44002POST的数据包为空44003图文消息内容为空45001多媒体文件大小超过限制45002消息内容超过限制45003标题字段超过限制45004描述字段超过限制45005链接字段超过限制45006图片链接字段超过限制45007语音播放时间超过限制45008图文消息超过限制45009接口调用超过限制46001不存在媒体数据47001解析JSON/XML内容错误

一、开发模板消息SDK

模板消息的定义如下:

模板消息也是使用access_token作为授权来发送。但是请大家注意:这里的access_token与网页授权的access_token完全不是一回事。可不要拿网页授权的access_token当作参数传递。

获取模板消息access_token:

String appId = "xxxxxxxxxxxxx"; //公众账号的唯一标识

String appSecret="xxxxxxxxxxx"; //公众账号的密钥

Token token = CommonUtil.getToken(appId, appSecret);

String access_token = token.getAccessToken();

#CommonUtil工具类具体代码:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 通用工具类
 *
 * @author liufeng
 */
public class CommonUtil {
    
    private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
    
    // 凭证获取(GET)
    public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    
    /**
     * 发送 https 请求
     *
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式(GET、POST)
     * @param outputStr 提交的数据
     * @return JSONObject(通过 JSONObject.get(key) 的方式获取 JSON 对象的属性值)
     */
    public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        
        JSONObject jsonObject = null;
        
        try {
            // 创建 SSLContext 对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述 SSLContext 对象中得到 SSLSocketFactory 对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            
            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);
            
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            
            // 设置请求方式(GET/POST)
            conn.setRequestMethod(requestMethod);
            
            // 当 outputStr 不为 null 时,向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            
            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            
            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            jsonObject = JSONObject.fromObject(buffer.toString());
        } catch (ConnectException ce) {
            log.error(" 连接超时:{}", ce);
        } catch (Exception e) {
            log.error("https 请求异常:{}", e);
        }
        
        return jsonObject;
    }

    /**
     * 获取接口访问凭证
     *
     * @param appid 凭证
     * @param appsecret 密钥
     * @return
     */
    public static Token getToken(String appid, String appsecret) {
        Token token = null;
        String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
        // 发起GET请求获取凭证
        JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);

        if (null != jsonObject) {
            try {
                token = new Token();
                token.setAccessToken(jsonObject.getString("access_token"));
                token.setExpiresIn(jsonObject.getInt("expires_in"));
            } catch (JSONException e) {
                token = null;
                // 获取token失败
                log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return token;
    }
    
    /**
     * URL编码(utf-8)
     *
     * @param source
     * @return
     */
    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    /**
     * 根据内容类型判断文件扩展名
     *
     * @param contentType 内容类型
     * @return
     */
    public static String getFileExt(String contentType) {
        String fileExt = "";
        if ("image/jpeg".equals(contentType))
            fileExt = ".jpg";
        else if ("audio/mpeg".equals(contentType))
            fileExt = ".mp3";
        else if ("audio/amr".equals(contentType))
            fileExt = ".amr";
        else if ("video/mp4".equals(contentType))
            fileExt = ".mp4";
        else if ("video/mpeg4".equals(contentType))
            fileExt = ".mp4";
        return fileExt;
    }
    
}

#Token实体类具体代码:

/**
 * 凭证实体类
 *
 * @author liufeng
 */
public class Token {

    // 接口访问凭证
    private String accessToken;
    // 凭证有效期,单位:秒
    private int expiresIn;
    
    public String getAccessToken() {
        return accessToken;
    }
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }
    public int getExpiresIn() {
        return expiresIn;
    }
    public void setExpiresIn(int expiresIn) {
        this.expiresIn = expiresIn;
    }
    
}


至此,我们获得了模板消息的access_token。将它作为参数传递到模板消息接口文档中POST请求的ACCESS_TOKEN。

//得到构造好的模板消息请求地址

String requestURL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+access_token;


二、构造模板消息并发送

我们以一个餐饮行业的收到新订单通知的模板为例,模板详情如下:

模版消息

  • 我的模板
  • 模板库


  • 模板ID

    YtO2XATY0VtRbgE4jWWNl-Zdc992FDdguhMUbomNkA0
    开发者调用模板消息接口时需提供模板ID

  • 标题收到新订单通知
  • 行业餐饮 - 餐饮
  • 详细内容
    {{first.DATA}}日期:{{Day.DATA}} 订单编号:{{orderId.DATA}}订单类型:{{orderType.DATA}}联系人:{{customerName.DATA}} 联系电话:{{customerPhone.DATA}} {{remark.DATA}}
    在发送时,需要将内容中的参数({{.DATA}}内为参数)赋值替换为需要的信息
  • 内容示例
    收到一个新的订单,确认接受请回复0,拒绝请回复1。日期:2014-10-10 订单编号:1002订单类型:订位 联系人:陈丑丑 联系电话:13222222222订单金额:100.00元付款状态:已微信支付  请及时处理您的订单。 
  • 按照上述条件,我们的消息体构造如下:
  • {"data":{"customerName":{"color":"#173177","value":"陈丑丑"},"customerPhone":{"color":"#173177","value":"13222222222"},"Day":{"color":"#173177","value":"15时06分"},"first":{"color":"#173177","value":"收到一个新的订单"},"orderId":{"color":"#173177","value":"1002"},"orderType":{"color":"#173177","value":"订位"},"remark":{"color":"#173177","value":"请及时处理您的订单"}},"template_id":"YtO2XATY0VtRbgE4jWWNl-Zdc992FDdguhMUbomNkA0","topcolor":"#173177","touser":"orsrOt9qBgO6dC-F3IL_MF52eplI","url":"http://weixin.qq.com/download"}
  • 该消息体为一个json。具体封装如下:
  • /**
     * 餐饮行业收到新订单通知模板消息实体类
     * @author xjw
     *
     */
    public class NewOrdersTemplate {
        private String touser; //用户OpenID
        
        private String template_id; //模板消息ID
        
        private String url; //URL置空,则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android)。
        
        private String topcolor; //标题颜色
        
        private Data data; //详细内容

        public String getTouser() {
            return touser;
        }

        public void setTouser(String touser) {
            this.touser = touser;
        }

        public String getTemplate_id() {
            return template_id;
        }

        public void setTemplate_id(String templateId) {
            template_id = templateId;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getTopcolor() {
            return topcolor;
        }

        public void setTopcolor(String topcolor) {
            this.topcolor = topcolor;
        }

        public Data getData() {
            return data;
        }

        public void setData(Data data) {
            this.data = data;
        }
    }
  • /**
     * "餐饮行业收到新订单通知"模板消息详细内容实体类
     * @author xjw
     *
     */
    public class Data {
        private Data_first first;
        
        private Data_Day Day; //日期
        
        private Data_orderId orderId; //订单编号
        
        private Data_orderType orderType; //订单类型
        
        private Data_customerName customerName; //联系人
        
        private Data_customerPhone customerPhone; //联系电话
        
        private Data_remark remark;

        public Data_first getFirst() {
            return first;
        }

        public void setFirst(Data_first first) {
            this.first = first;
        }

        

        public Data_Day getDay() {
            return Day;
        }

        public void setDay(Data_Day day) {
            Day = day;
        }

        public Data_orderId getOrderId() {
            return orderId;
        }

        public void setOrderId(Data_orderId orderId) {
            this.orderId = orderId;
        }

        public Data_orderType getOrderType() {
            return orderType;
        }

        public void setOrderType(Data_orderType orderType) {
            this.orderType = orderType;
        }

        public Data_customerName getCustomerName() {
            return customerName;
        }

        public void setCustomerName(Data_customerName customerName) {
            this.customerName = customerName;
        }

        public Data_customerPhone getCustomerPhone() {
            return customerPhone;
        }

        public void setCustomerPhone(Data_customerPhone customerPhone) {
            this.customerPhone = customerPhone;
        }

        public Data_remark getRemark() {
            return remark;
        }

        public void setRemark(Data_remark remark) {
            this.remark = remark;
        }
    }

/**
 * first
 * @author xjw
 *
 */
public class Data_first {
    private String value;
    
    private String color;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    
}

/**
 * 日期
 * @author xjw
 *
 */
public class Data_Day {
    
    private String value;
    
    private String color;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    
}


/**
 * 订单标号
 * @author xjw
 *
 */
public class Data_orderId {

    private String value;
    
    private String color;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
 
}

/**
 * 订单类型
 * @author xjw
 *
 */
public class Data_orderType {

    private String value;
    
    private String color;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    
}

/**
 * 联系人
 * @author xjw
 *
 */
public class Data_customerName {

    private String value;
    
    private String color;


    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    
}

/**
 * 联系电话
 * @author xjw
 *
 */
public class Data_customerPhone {

    private String value;
    
    private String color;


    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    
}

/**
 * remark
 * @author xjw
 *
 */
public class Data_remark {
    
    private String value;
    
    private String color;


    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    
}

/**
     * 发送模板消息
     * appId 公众账号的唯一标识
     * appSecret 公众账号的密钥
     * openId 用户标识
     */
    public void send_template_message(String appId, String appSecret, String openId) {


        Token token = CommonUtil.getToken(appId, appSecret);
        String access_token = token.getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+access_token;


        NewOrdersTemplate temp = new NewOrdersTemplate();
        Data data = new Data();
        Data_first first = new Data_first();
        Data_Day Day = new Data_Day();
        Data_orderId orderId = new Data_orderId();
        Data_orderType orderType = new Data_orderType();
        Data_customerName customerName = new Data_customerName();
        Data_customerPhone customerPhone = new Data_customerPhone();
        Data_remark remark = new Data_remark();


        first.setValue("收到一个新的订单");
        first.setColor("#173177");
        Day.setValue("14时56分");
        Day.setColor("#173177");
        orderId.setValue("1002");
        orderId.setColor("#173177");
        orderType.setValue("订位");
        orderType.setColor("#173177");
        customerName.setValue("陈丑丑");
        customerName.setColor("#173177");
        customerPhone.setValue("13222222222");
        customerPhone.setColor("#173177");
        remark.setValue("请及时处理您的订单");
        remark.setColor("#173177");


        data.setFirst(first);
        data.setDay(Day);
        data.setOrderId(orderId);
        data.setOrderType(orderType);
        data.setCustomerName(customerName);
        data.setCustomerPhone(customerPhone);
        data.setRemark(remark);
        temp.setTouser(openId);
        temp.setTemplate_id("YtO2XATY0VtRbgE4jWWNl-Zdc992FDdguhMUbomNkA0");
        temp.setUrl("http://weixin.qq.com/download");
        temp.setTopcolor("#173177");
        temp.setData(data);
        
        String jsonString = JSONObject.fromObject(temp).toString().replace("day", "Day");
        JSONObject jsonObject = WeixinUtil.httpRequest(url, "POST", jsonString);
        System.out.println(jsonObject);
        int result = 0;
        if (null != jsonObject) {  
             if (0 != jsonObject.getInt("errcode")) {  
                 result = jsonObject.getInt("errcode");  
                  log.error("错误 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));  
             }  
         }
        log.info("模板消息发送结果:"+result);
    }


WeixinUtil工具类在柳峰老师的博客里有源码。。。。。

实现效果如下:


如果小伙伴们觉得有什么问题可以提出,大家共同学习。





0 0