有一段時間沒有接著微信的主題繼續介紹裡面的功能模塊了,這段時間來,微信也做了不少的變化改動,針對這些特性我全面核對了一下相關的微信公眾號和企業號的接口,對原有的微信API和系統管理做了全面的更新,本隨筆以及後面的隨筆就是基於這個工作上的總結,以期把微信涉及的功能模塊,都使用C#實現的方式來介紹。本隨筆主要介紹微信公眾號的個性化菜單的實現,雖然目前微信公眾號和企業號已經在功能上接近一致,不過在企業號上還沒有個性化菜單的相關接口。
我們先了解一下個性化菜單的介紹,根據官方的資料,如下介紹。
為了幫助公眾號實現靈活的業務運營,微信公眾平台新增了個性化菜單接口,開發者可以通過該接口,讓公眾號的不同用戶群體看到不一樣的自定義菜單。該接口開放給已認證訂閱號和已認證服務號。
開發者可以通過以下條件來設置用戶看到的菜單:
1、用戶分組(開發者的業務需求可以借助用戶分組來完成) 2、性別 3、手機操作系統 4、地區(用戶在微信客戶端設置的地區) 5、語言(用戶在微信客戶端設置的語言)
個性化菜單接口說明:
1、個性化菜單要求用戶的微信客戶端版本在iPhone6.2.2,Android 6.2.4以上。 2、菜單的刷新策略是,在用戶進入公眾號會話頁或公眾號profile頁時,如果發現上一次拉取菜單的請求在5分鐘以前,就會拉取一下菜單,如果菜單有更新,就會刷新客戶端的菜單。測試時可以嘗試取消關注公眾賬號後再次關注,則可以看到創建後的效果。 3、普通公眾號的個性化菜單的新增接口每日限制次數為2000次,刪除接口也是2000次,測試個性化菜單匹配結果接口為20000次 4、出於安全考慮,一個公眾號的所有個性化菜單,最多只能設置為跳轉到3個域名下的鏈接 5、創建個性化菜單之前必須先創建默認菜單(默認菜單是指使用普通自定義菜單創建接口創建的菜單)。如果刪除默認菜單,個性化菜單也會全部刪除
個性化菜單匹配規則說明:
當公眾號創建多個個性化菜單時,將按照發布順序,由新到舊逐一匹配,直到用戶信息與matchrule相符合。如果全部個性化菜單都沒有匹配成功,則返回默認菜單。 例如公眾號先後發布了默認菜單,個性化菜單1,個性化菜單2,個性化菜單3。那麼當用戶進入公眾號頁面時,將從個性化菜單3開始匹配,如果個性化菜單3匹配成功,則直接返回個性化菜單3,否則繼續嘗試匹配個性化菜單2,直到成功匹配到一個菜單。 根據上述匹配規則,為了避免菜單生效時間的混淆,決定不予提供個性化菜單編輯API,開發者需要更新菜單時,需將完整配置重新發布一輪。
除正常的默認菜單外,個性化菜單提供了下面幾個處理操作。
1 創建個性化菜單
2 刪除個性化菜單
3 測試個性化菜單匹配結果
4 查詢個性化菜單
5 刪除所有菜單
個性化自定義菜單功能,是指商家可以根據粉絲的分組、性別、手機操作系統,甚至地區,來分別展示公眾號的菜單。
比如,一家健身會所的公眾號,將自定義菜單根據粉絲性別進行了分開設置。男性粉絲進入公眾號會展示男生們比較感興趣菜單內容,跆拳道、搏擊等;若是女性粉絲,則會展示瑜伽、舞蹈、防身術等女性粉絲感興趣的菜單課程等。

微信的菜單是微信處理中很重要的一環,因為它是微信給用戶入口體驗重要的一環,也是很直觀的界面呈現之一,菜單的展示就是需要我們絞盡腦汁來精簡的界面元素了,由於菜單的重要性以及功能的豐富性,我在之前好幾篇隨筆專門介紹了菜單的各種處理。
在《C#開發微信門戶及應用(6)--微信門戶菜單的管理操作》、《C#開發微信門戶及應用(9)-微信門戶菜單管理及提交到微信服務器》、《C#開發微信門戶及應用(11)--微信菜單的多種表現方式介紹》、《C#開發微信門戶及應用(20)-微信企業號的菜單管理》分別對相關的菜單有過介紹,有需要可以參考了解一下。
菜單模塊,在微信公眾號和企業號,它們都可以共用一個模型,因此我們把這些內容都放在微信共用模塊裡面,包括它的實體類信息(如菜單模塊),雖然有些地方,部分字段內容沒有,但是我們可以通過實體類的JSON標識來進行處理,從而實現比較彈性化的實體類信息承載。
如下標識代碼所示。
/// <summary>
/// 菜單基本信息(公眾號、企業號公用,其他部分一樣)
/// </summary>
public class MenuJson : BaseJsonResult
{
/// <summary>
/// 按鈕描述,既按鈕名字,不超過16個字節,子菜單不超過40個字節
/// </summary>
public string name { get; set; }
/// <summary>
/// 按鈕類型(click或view)
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string type { get; set; }
/// <summary>
/// 按鈕KEY值,用於消息接口(event類型)推送,不超過128字節
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string key { get; set; }
/// <summary>
/// 網頁鏈接,用戶點擊按鈕可打開鏈接,不超過256字節
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string url { get; set; }
/// <summary>
/// media_id類型和view_limited類型必須,調用新增永久素材接口返回的合法media_id
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string media_id { get; set; }
/// <summary>
/// 子按鈕數組,按鈕個數應為2~5個
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<MenuJson> sub_button { get; set; }
上面這個實體類是菜單信息的基礎信息,我們需要從這個基礎上擴展出來幾個實體類,方便在構建,獲取菜單信息的時候進行信息轉換,如下幾個類圖的設計所示。

通過這幾個類的關系,我們就可以適應所有的默認菜單和個性化菜單,以及企業號的菜單信息轉換處理了,通過JSON轉換為對應的實體類,實現信息的強類型處理。
其中我們菜單處理的API實現類代碼如下所示。
/// <summary>
/// 菜單的相關操作
/// </summary>
public class MenuApi : IMenuApi
這樣我們在IMenuApi的基礎上增加幾個個性化的接口。
/// <summary>
/// 創建個性化菜單
/// </summary>
/// <param name="accessToken">調用接口憑證</param>
/// <param name="menuJson">菜單對象</param>
/// <returns></returns>
string CreateConditionalMenu(string accessToken, MenuListJson menuJson);
/// <summary>
/// 根據菜單ID刪除個性化菜單
/// </summary>
/// <param name="accessToken">調用接口憑證</param>
/// <param name="menuid">菜單ID</param>
/// <returns></returns>
CommonResult DeleteConditionalMenu(string accessToken, string menuid);
/// <summary>
/// 測試個性化菜單匹配結果
/// </summary>
/// <param name="accessToken">調用接口憑證</param>
/// <returns></returns>
MenuListJson TryMatchConditionalMenu(string accessToken, string user_id);
然後在MenuApi實現類裡面實現相關的邏輯處理即可。
下面是創建個性化菜單的接口說明。
http請求方式:POST(請使用https協議)
https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=ACCESS_TOKEN
請求示例
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"name":"菜單",
"sub_button":[
{
"type":"view",
"name":"搜索",
"url":"http://www.soso.com/"
},
{
"type":"view",
"name":"視頻",
"url":"http://v.qq.com/"
},
{
"type":"click",
"name":"贊一下我們",
"key":"V1001_GOOD"
}]
}],
"matchrule":{
"group_id":"2",
"sex":"1",
"country":"中國",
"province":"廣東",
"city":"廣州",
"client_platform_type":"2"
"language":"zh_CN"
}
}
其中上面的信息,轉換為我們的實體類對象信息就是如下所示。
MenuListJson menuJson
它API返回結果如下所示。
{
"menuid":"208379533"
}
為了承載這個信息,我們需要定義一個實體類來承載這個信息。
/// <summary>
/// 返回MenuId的結果
/// </summary>
public class MenuIdResult
{
/// <summary>
/// 菜單的ID
/// </summary>
public string menuid { get; set; }
}
最後我們的API接口只需要獲取裡面的菜單ID值,字符串類型的就可以了,API具體實現代碼如下所示。
/// <summary>
/// 創建個性化菜單
/// </summary>
/// <param name="accessToken">調用接口憑證</param>
/// <param name="menuJson">菜單對象</param>
/// <returns></returns>
public string CreateConditionalMenu(string accessToken, MenuListJson menuJson)
{
string result = "";
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token={0}", accessToken);
//matchrule不能為空
ArgumentValidation.CheckForNullReference(menuJson.matchrule, "matchrule");
string postData = menuJson.ToJson();
MenuIdResult list = JsonHelper<MenuIdResult>.ConvertJson(url, postData);
if (list != null)
{
result = list.menuid;
}
return result;
}
而刪除個性化菜單的接口也是類似的處理,如下所示。
/// <summary>
/// 根據菜單ID刪除個性化菜單
/// </summary>
/// <param name="accessToken">調用接口憑證</param>
/// <param name="menuid">菜單ID</param>
/// <returns></returns>
public CommonResult DeleteConditionalMenu(string accessToken, string menuid)
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token={0}", accessToken);
var data = new
{
menuid = menuid
};
string postData = data.ToJson();
return Helper.GetExecuteResult(url, postData);
}
另外,對於個性化菜單來說,查詢、刪除所有的菜單操作接口和普通默認菜單是一樣的,因此可以重用這兩個接口,不過由於我們擴展了查詢返回的信息類,所以如果是個性化菜單,那麼它返回的JSON字符串裡面,包含了conditionalmenu的屬性,這個是一個集合 ,也就是說個性化菜單是多個規則的個性化菜單的集合。

菜單的JSON數據結構如下所示。

有了上面接口的封裝,我們可以在創建個性化菜單的時候很方便了,構建個性化菜單的時候,我們需要把全部需要出現的菜單都需要放在一個conditionalmenu的單元裡面,雖然這樣的單元在集合裡面出現多個。
如下面是構建一個個性化的菜單項目,和普通的默認菜單不同,它需要指定一個matchrule的對象,如下代碼所示。
MenuJson conditional = new MenuJson("相關鏈接", new MenuJson[] {
new MenuJson("個性化菜單", ButtonType.click, "event_company")
});
MatchRule rule = new MatchRule()
{
sex = "1"
};
MenuListJson menuJson = new MenuListJson();
menuJson.button.AddRange(new MenuJson[] { conditional });
menuJson.matchrule = rule;
通過上面的代碼了解,我們如果需要增加多個個性化菜單,那麼就是通過這個方法,每次創建一個個性化菜單的方式進行構建,當然如果我們使用查詢個性化菜單的結果可以看到,如果創建多個,那麼conditionalmenu集合裡面是多個對象的。
調用代碼每次添加一個個性化菜單的操作如下所示。
string menuid = menuBLL.CreateConditionalMenu(token, menuJson);
Console.WriteLine("創建菜單:" + (!string.IsNullOrEmpty(menuid) ? "成功:" + menuid : "失敗"));
我們創建多個規則後,可以通過TryMatch的方式來檢查匹配的結果,也就是對應不同用戶(openid)會出現不同的個性化菜單列表了。
var myResult = menuBLL.TryMatchConditionalMenu(token, openId);
if (myResult != null)
{
Console.WriteLine(myResult.ToJson());
}
var userid_female = "oSiLnt2J4mYkhVG3aLTdMIF1hv-s";//女性的ID
myResult = menuBLL.TryMatchConditionalMenu(token, userid_female);
if (myResult != null)
{
Console.WriteLine(myResult.ToJson());
}
如果對這個《C#開發微信門戶及應用》系列感興趣,可以關注我的其他文章,系列隨筆如下所示:
C#開發微信門戶及應用(28)--微信“搖一搖·周邊”功能的使用和接口的實現
C#開發微信門戶及應用(27)-公眾號模板消息管理
C#開發微信門戶及應用(26)-公眾號微信素材管理
C#開發微信門戶及應用(25)-微信企業號的客戶端管理功能
C#開發微信門戶及應用(24)-微信小店貨架信息管理
C#開發微信門戶及應用(23)-微信小店商品管理接口的封裝和測試
C#開發微信門戶及應用(22)-微信小店的開發和使用
C#開發微信門戶及應用(21)-微信企業號的消息和事件的接收處理及解密
C#開發微信門戶及應用(20)-微信企業號的菜單管理
C#開發微信門戶及應用(19)-微信企業號的消息發送(文本、圖片、文件、語音、視頻、圖文消息等)
C#開發微信門戶及應用(18)-微信企業號的通訊錄管理開發之成員管理
C#開發微信門戶及應用(17)-微信企業號的通訊錄管理開發之部門管理
C#開發微信門戶及應用(16)-微信企業號的配置和使用
C#開發微信門戶及應用(15)-微信菜單增加掃一掃、發圖片、發地理位置功能
C#開發微信門戶及應用(14)-在微信菜單中采用重定向獲取用戶數據
C#開發微信門戶及應用(13)-使用地理位置擴展相關應用
C#開發微信門戶及應用(12)-使用語音處理
C#開發微信門戶及應用(11)--微信菜單的多種表現方式介紹
C#開發微信門戶及應用(10)--在管理系統中同步微信用戶分組信息
C#開發微信門戶及應用(9)-微信門戶菜單管理及提交到微信服務器
C#開發微信門戶及應用(8)-微信門戶應用管理系統功能介紹
C#開發微信門戶及應用(7)-微信多客服功能及開發集成
C#開發微信門戶及應用(6)--微信門戶菜單的管理操作
C#開發微信門戶及應用(5)--用戶分組信息管理
C#開發微信門戶及應用(4)--關注用戶列表及詳細信息管理
C#開發微信門戶及應用(3)--文本消息和圖文消息的應答
C#開發微信門戶及應用(2)--微信消息的處理和應答
C#開發微信門戶及應用(1)--開始使用微信接口