微信公衆平臺開發者文檔 http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html
15年元旦的時候,產品要做微信版,便開始研究微信公衆平臺以及開發者文檔,給我的感覺是微信測試很不方便,職能配置一個回調URL,測試和正式通用,還會有回調兩次的現象。以及彈出哭臉:請在微信客戶端中打開。
半年多過去了,微信開放平臺也在不斷的完善和優化。自己憑着回憶和代碼,做下遲來的總結,作爲留念。
項目設計分爲兩大部分,基礎服務搭建和具體業務接入。
基礎服務搭建
1. 開發者接入
申請公衆號後,微信分配了開發者ID,包括AppID,AppSecret。
按照微信開發者文檔中的接入指南,配置微信交互的url、Token、EncodingAESKey.三者都是自定義的,啓用兼容模式,方便調試,項目上線穩定後改爲安全模式。url自己寫的controller,接受佈置在具有外網權限的服務器上。
<pre name="code" class="java"> @RequestMapping(value = {"mp/callback.action"}, method = RequestMethod.POST, produces = "text/xml")
@ResponseBody
public String receive(@RequestBody String xmlbody,
@RequestParam(value = "signature") String signature,
@RequestParam(value = "timestamp") String timestamp,
@RequestParam(value = "nonce") String nonce,
@RequestParam(value = "encrypt_type") String encrypt_type, @RequestParam(
value = "msg_signature") String msg_signature, HttpServletResponse response, Model model) {
// TODO 1. 通過對簽名的效驗判斷此條消息的真實性 2. 解密xmlbody密文 3. 解析消息,交給按照不同的處理策略,得到相應的響應消息加密後返回。
}
2. 菜單·這是重要的基礎環節。按照格式,構建菜單。按等級劃分: 一級菜單、二級菜單。二級菜單即一級菜單的子菜單。當然一級菜單可以不包含菜單。有種樹的感覺:節點,子節點。按按鈕事件類型劃分: view、 click。view是點擊按鈕打開url鏈接。click是點擊發送一條消息Message。很簡單。關鍵是做好click類型key與message消息的對應,做到動態配置,可以做管理後臺,菜單變更後,立即向微信發送restful請求,刷新菜單。
[{
name: "看點啥",
sub_button: [{
key: "TONIGHT_FOCUS",
name: "今晚看啥",
type: "click"
},
{
name: "小編推薦",
type: "view",
url: "http://"
},
{
key: "COLUMN_SEARCH",
name: "搜索影片",
type: "click"
},
{
name: "會員片庫",
type: "view",
url: "http://"
}]
},
{
name: "特色片",
sub_button: [{
name: "8月觀影指南",
type: "view",
url: "http://"
},
{
name: "新片推薦",
type: "view",
url: "http://"
},
{
name: "美味周邊",
type: "view",
url: "http://"
},
{
name: "免費電影票",
type: "view",
url: "http://"
},
{
name: "更多專題",
type: "view",
url: "http://"
}]
},
{
name: "我的",
sub_button: [{
key: "SERVICE_VIP",
name: "會員狀態",
type: "click"
},
{
key: "SERVICE_HIS",
name: "觀看記錄",
type: "click"
},
{
key: "SERVICE_KF",
name: "專屬客服",
type: "click"
},
{
name: "會員中心",
type: "view",
url: "http://"
}]
}]
3. 用戶
做用戶這塊,折騰了好久,難點在於與公司用戶體系打通。
/***
* 獲取用戶基本信息(UnionID機制)
*
* @param openid
* @return
*/
public UserInfo getUserInfo(String openid);
/***
* 通過code換取網頁授權access_token
*
* @param code
* @return null if code is blank.
*/
public Oauth2AccessToken oauth2_access_token(String code);
/***
* 拉取用戶信息(需scope爲 snsapi_userinfo)
*
* @param access_token
* @param openid
* @return
*/
public UserInfo oauth2_userinfo(String access_token, String openid);
4. 用戶與公衆平臺消息交互
用戶動作分爲消息和事件兩種,根據消息EventType爲msg或event,分別構建兩種策略模式。
public class EventStrategyFactory {
public static IEventStrategy getDealer(String event) {
// 按事件發生概率由高到低排序
switch (event) {
// 自定義菜單 —— 點擊菜單拉取消息時的事件
case "CLICK":
return EventStrategy.CLICK;
// 自定義菜單 —— 點擊菜單跳轉鏈接時的事件
case "VIEW":
return EventStrategy.VIEW;
// 關注事件 || 掃描帶參數二維碼, 用戶未關注時,進行關注後的事件推送
case "subscribe":
return EventStrategy.subscribe;
// 模版消息發送任務完成後的事件
case "TEMPLATESENDJOBFINISH":
return EventStrategy.TEMPLATESENDJOBFINISH;
// 上報地理位置事件
case "LOCATION":
return EventStrategy.LOCATION;
// 取消關注事件
case "unsubscribe":
return EventStrategy.unsubscribe;
// 掃描帶參數二維碼, 用戶已關注時的事件推送
case "SCAN":
return EventStrategy.SCAN;
// 自定義菜單 —— 掃碼推事件的事件
case "scancode_push":
return EventStrategy.scancode_push;
// 自定義菜單 —— 掃碼推事件且彈出“消息接收中”提示框的事件
case "scancode_waitmsg":
return EventStrategy.scancode_waitmsg;
// 自定義菜單 —— 彈出系統拍照發圖的事件
case "pic_sysphoto":
return EventStrategy.pic_sysphoto;
// 自定義菜單 —— 彈出拍照或者相冊發圖的事件
case "pic_photo_or_album":
return EventStrategy.pic_photo_or_album;
// 自定義菜單 —— 彈出微信相冊發圖器的事件
case "pic_weixin":
return EventStrategy.pic_weixin;
// 自定義菜單 —— 彈出地理位置選擇器的事件
case "location_select":
return EventStrategy.location_select;
default:
return null;
}
}
}
/***
* 事件枚舉策略
*
* @author charles
*
*/
public enum EventStrategy implements IEventStrategy {
// 關注事件 || 掃描帶參數二維碼, 用戶未關注時,進行關注後的事件推送
subscribe {
@Override
public String dealEvent(Element root) {
return SubscribeStrategy.dealEvent(root);
}
},
// 取消關注事件
unsubscribe {
@Override
public String dealEvent(Element root) {
return UnSubscribeStrategy.dealEvent(root);
}
},
// 掃描帶參數二維碼, 用戶已關注時的事件推送
SCAN {
@Override
public String dealEvent(Element root) {
return ScanStrategy.dealEvent(root);
}
},
// 上報地理位置事件
LOCATION {
@Override
public String dealEvent(Element root) {
return LocationStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 點擊菜單拉取消息時的事件
CLICK {
@Override
public String dealEvent(Element root) {
return ClickStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 點擊菜單跳轉鏈接時的事件
VIEW {
@Override
public String dealEvent(Element root) {
return ViewStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 掃碼推事件的事件
scancode_push {
@Override
public String dealEvent(Element root) {
return Scancode_pushStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 掃碼推事件且彈出“消息接收中”提示框的事件
scancode_waitmsg {
@Override
public String dealEvent(Element root) {
return Scancode_waitmsgStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 彈出系統拍照發圖的事件
pic_sysphoto {
@Override
public String dealEvent(Element root) {
return Pic_sysphotoStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 彈出拍照或者相冊發圖的事件
pic_photo_or_album {
@Override
public String dealEvent(Element root) {
return Pic_photo_or_albumStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 彈出微信相冊發圖器的事件
pic_weixin {
@Override
public String dealEvent(Element root) {
return Pic_weixinStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 彈出地理位置選擇器的事件
location_select {
@Override
public String dealEvent(Element root) {
return Location_selectStrategy.dealEvent(root);
}
},
// 模版消息發送任務完成後的事件
TEMPLATESENDJOBFINISH {
@Override
public String dealEvent(Element root) {
return TemplateSendStrategy.dealEvent(root);
}
}
}
/***
* 消息策略簡單工廠
*
* @author charles
*
*/
public class MessageStrategyFactory {
public static IMessageStrategy getDealer(String msgType) {
switch (msgType) {
// 文本消息
case "text":
return MessageStrategy.text;
// 圖片消息
case "image":
return MessageStrategy.image;
// 語音消息
case "voice":
return MessageStrategy.voice;
// 視頻消息
case "video":
return MessageStrategy.video;
// 地理位置消息
case "location":
return MessageStrategy.location;
// 鏈接消息
case "link":
return MessageStrategy.link;
default:
return null;
}
}
}
/***
* 消息枚舉策略
*
* @author charles
*
*/
public enum MessageStrategy implements IMessageStrategy {
// 文本消息
text {
@Override
public String dealMsg(Element root) {
return TextStrategy.dealMsg(root);
}
},
// 圖片消息
image {
@Override
public String dealMsg(Element root) {
return ImageStrategy.dealMsg(root);
}
},
// 語音消息
voice {
@Override
public String dealMsg(Element root) {
return VoiceStrategy.dealMsg(root);
}
},
// 視頻消息
video {
@Override
public String dealMsg(Element root) {
return VideoStrategy.dealMsg(root);
}
},
// 地理位置消息
location {
@Override
public String dealMsg(Element root) {
return LocationStrategy.dealMsg(root);
}
},
// 鏈接消息
link {
@Override
public String dealMsg(Element root) {
return LinkStrategy.dealMsg(root);
}
}
}