這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在搞定使用golang快速開發公眾平台(二):擷取accessToken後,我們可以開始著手自訂菜單
自訂菜單簡單粗暴,post請求裡塞入要定義的菜單即可。
開始搓代碼
func PushWxMenuCreate(accessToken string, menuJsonBytes []byte) error { postReq, err := http.NewRequest("POST", strings.Join([]string{"https://api.weixin.qq.com/cgi-bin/menu/create", "?access_token=", accessToken}, ""), bytes.NewReader(menuJsonBytes)) if err != nil { fmt.Println("向發送菜單建立請求失敗", err) logUtils.GetLog().Error("向發送菜單建立請求失敗", err) return err } postReq.Header.Set("Content-Type", "application/json; encoding=utf-8") client := &http.Client{} resp, err := client.Do(postReq) if err != nil { fmt.Println("client向發送菜單建立請求失敗", err) logUtils.GetLog().Error("client向發送菜單建立請求失敗", err) return err } else { fmt.Println("向發送菜單建立成功") } defer resp.Body.Close() return nil}func createWxMenu(o orm.Ormer) { //btn1 := models.Btn{Name: "進入商城", Url: "http://www.baidu.com/", Btype: "view"} //btn2 := models.Btn{Name: "測試人員中樞", Key: "molan_user_center", Btype: "click"} //btn3 := models.Btn{Name: "我的", Url: "http://www.baidu.com/user_view", Btype: "view"} // //btns := []models.Btn{btn1, btn2, btn3} //wxMenu := models.WxMenu{Button: btns} //menuJsonBytes, err := json.Marshal(wxMenu) menuStr := `{ "button": [ { "name": "進入商城", "type": "view", "url": "http://www.baidu.com/" }, { "name":"管理中心", "sub_button":[ { "name": "使用者中心", "type": "click", "key": "molan_user_center" }, { "name": "公告", "type": "click", "key": "molan_institution" }] }, { "name": "資料修改", "type": "view", "url": "http://www.baidu.com/user_view" } ] }` //if err == nil { //fmt.Println("產生的菜單json--->", menuStr) at := models.WxAccessToken{Id: 1} o.ReadOrCreate(&at, "id") //發送建立菜單的post請求 WxPlatUtil.PushWxMenuCreate(at.AccessToken, []byte(menuStr)) //} else { // logUtils.GetLog().Error("菜單json轉換錯誤", err) //}}
使用struct來產生json收到了慘不忍睹的效果,於是我乾脆粗暴的把json直接寫出來傳進去。。。
使用者在點擊菜單的時候,會把響應資訊以post請求的方式發送到我們在伺服器配置中填寫的URL中去,即使用beego快速開發公眾平台(一):開啟伺服器配置中的 /wx_connect。而這一點文檔壓根不提,如果你是上來就做的支付功能,做到這一步一定會問候文檔作者的八輩祖宗。
好在我們機智的把支付功能放在了後面,並未對咱們的思路造成混亂,所以接下來還是無壓力的。
當使用者點擊菜單,伺服器會post過來一個xml,類似這樣:
<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[FromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[VIEW]]></Event><EventKey><![CDATA[www.qq.com]]></EventKey><MenuId>MENUID</MenuId></xml>
我們可以找到是發給誰的(ToUserName) 哪個使用者觸發的(FromUserName) 通過什麼方式觸發的(Event EventKey),解析這個xml就行
都閃開 我要搓代碼了:
WxConnect.go
//type WxMenuEvent struct {// ToUserName string `xml:"ToUserName"`// FromUserName string `xml:"FromUserName"`// CreateTime int64 `xml:"CreateTime"`// MsgType string `xml:"MsgType"`// Event string `xml:"Event"` //VIEW // EventKey string `xml:"EventKey"`// MenuId string `xml:"MenuId"`// ScanCodeInfo *ScanCodeInfo `xml:"ScanCodeInfo"` //專屬於掃碼// Content string `xml:"Content"`// Ticket string `xml:"Ticket"`//}//type ScanCodeInfo struct {// ScanType string `xml:"ScanType"`// ScanResult string `xml:"ScanResult"`//}//菜單使用者點擊 掃碼響應func (c *WxConnectController) Post() { if bytes, err := ioutil.ReadAll(c.Ctx.Request.Body); err == nil { //解析xml wxEvent := new(models.WxMenuEvent) if err := xml.Unmarshal(bytes, wxEvent); err == nil { //處理菜單點擊 dealWithMenuEvent(&wxEvent) } else { fmt.Println("菜單使用者點擊、掃碼響應錯誤", err) } } else { fmt.Println("菜單使用者點擊、掃碼解析body錯誤", err) } c.EnableRender = false}func dealWithMenuEvent(wxEvent **models.WxMenuEvent){ switch wxEvent.Event { case "VIEW"://說明是點擊底部功能表列進入商城頁面 case "CLICK"://點擊按鈕 if strings.EqualFold(wxEvent.EventKey, "molan_user_center") { //點擊使用者中心 } else if strings.EqualFold(wxEvent.EventKey, "molan_institution") { //點擊公告 } case "subscribe"://掃碼 if strings.Contains(wxEvent.EventKey, "qrscene") { //未關注過商城 掃描他人二維碼首次關注 upperId := strings.Split(wxEvent.EventKey, "_")[1]//EventKey:qrscene_16 這個16就是upper在資料庫的id 注意不是wxid } else { //關注(掃官方二維碼 而不是掃個人二維碼) } case "SCAN"://已關注 掃描他人二維碼 }}
這裡掃碼比較噁心,eventKey可以作為識別是目前使用者掃哪個使用者的碼,qrscene_xxxx 這個xxxx是你自己設的,大家知道就行。
到這一步,響應基本算是走完了。
但是 GRD需求總是不放過我們
老闆說了:我想知道是哪個使用者點了某個按鈕,然後根據不同使用者給予不同的響應。
這就需要用到WxMenuEvent.FromUserName,這個FromUserName是使用者openID,注意這玩意不是使用者的號。
官網是這樣寫的:
快閃開我憋不住了:
func FetchWxInfoAndIcon(o orm.Ormer, openid, wxUserInforUrl string) (*models.WxUserInfo, error) { at := models.WxAccessToken{Id: 1} o.ReadOrCreate(&at, "id") requestLine := strings.Join([]string{wxUserInforUrl, "?access_token=", at.AccessToken, "&openid=", openid, "&lang=zh_CN"}, "") resp, err := http.Get(requestLine) if err != nil || resp.StatusCode != http.StatusOK { fmt.Println("發送get請求擷取 wxUserInfo 錯誤", err) logUtils.GetLog().Error("發送get請求擷取 wxUserInfo 錯誤", err) return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("發送get請求擷取 wxUserInfo 讀取返回body錯誤", err) logUtils.GetLog().Error("發送get請求擷取 wxUserInfo 讀取返回body錯誤", err) return nil, err } if bytes.Contains(body, []byte("errcode")) { ater := models.AccessTokenErrorResponse{} err = json.Unmarshal(body, &ater) if err != nil { fmt.Printf("發送get請求擷取 wxUserInfo 的錯誤資訊 %+v\n", ater) logUtils.GetLog().Error("發送get請求擷取 wxUserInfo 的錯誤資訊 %+v\n", ater) return nil, err } return nil, fmt.Errorf("%s", ater.Errmsg) } else { atr := models.WxUserInfo{} err = json.Unmarshal(body, &atr) if err != nil { fmt.Println("發送get請求擷取 wxUserInfo 返回資料json解析錯誤", err) logUtils.GetLog().Error("發送get請求擷取 wxUserInfo 返回資料json解析錯誤", err) return nil, 0.0, err } return &atr, nil }}
好了,我們現在拿到了使用者的暱稱頭像,搞定收工