在搞定使用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
}
}
好了,我们现在拿到了用户的昵称头像,搞定收工