1. 程式人生 > >微信公眾平臺開發之模板訊息(Java)

微信公眾平臺開發之模板訊息(Java)

由於柳峰老師的新書還沒有出來,網上也沒有過多介紹基於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 不合法的OpenID
40004 不合法的媒體檔案型別
40005 不合法的檔案型別
40006 不合法的檔案大小
40007 不合法的媒體檔案id
40008 不合法的訊息型別
40009 不合法的圖片檔案大小
40010 不合法的語音檔案大小
40011 不合法的視訊檔案大小
40012 不合法的縮圖檔案大小
40013 不合法的APPID
41001 缺少access_token引數
41002 缺少appid引數
41003 缺少refresh_token引數
41004 缺少secret引數
41005 缺少多媒體檔案資料
41006 access_token超時
42001 需要GET請求
43002 需要POST請求
43003 需要HTTPS請求
44001 多媒體檔案為空
44002 POST的資料包為空
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。

//得到構造好的模板訊息請求地址

二、構造模板訊息併發送

我們以一個餐飲行業的收到新訂單通知的模板為例,模板詳情如下:

模版訊息

返回上一層我的模板/ 模板詳情
  • 模板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工具類在柳峰老師的部落格裡有原始碼。。。。。

實現效果如下:

如果小夥伴們覺得有什麼問題可以提出,大家共同學習。