104 lines
3.8 KiB
Go
104 lines
3.8 KiB
Go
package wechatmp
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/beego/beego/v2/core/logs"
|
|
)
|
|
|
|
// =============================================================
|
|
// 公众号自定义菜单:发布「扫码登录」菜单(点击事件 → CLICK 事件 → 自动回复
|
|
// 确认登录链接,用户不用输入任何内容)。
|
|
// 注意:菜单接口(menu/create)与带参二维码同属认证后能力,
|
|
// 未认证公众号会返回 48001,届时需改用「公众号内发送登录」路径。
|
|
// =============================================================
|
|
|
|
// LoginMenuKey 登录菜单的事件键值(与 isLoginMenuKey 保持一致)
|
|
const LoginMenuKey = "LOGIN"
|
|
|
|
type mpMenuButton struct {
|
|
Type string `json:"type,omitempty"`
|
|
Name string `json:"name"`
|
|
Key string `json:"key,omitempty"`
|
|
URL string `json:"url,omitempty"`
|
|
SubButton []mpMenuButton `json:"sub_button,omitempty"`
|
|
}
|
|
|
|
type mpMenuGetResp struct {
|
|
Menu struct {
|
|
Button []mpMenuButton `json:"button"`
|
|
} `json:"menu"`
|
|
// 个性化菜单:命中条件时会覆盖默认菜单(公众号底部看到的可能来自这里)
|
|
ConditionalMenu []map[string]interface{} `json:"conditionalmenu"`
|
|
ErrCode int `json:"errcode"`
|
|
ErrMsg string `json:"errmsg"`
|
|
}
|
|
|
|
// PublishMenuResult 发布结果(供页面展示与排查)
|
|
type PublishMenuResult struct {
|
|
Buttons []mpMenuButton `json:"button"`
|
|
AlreadyExists bool `json:"already_exists"`
|
|
ConditionalMenuCount int `json:"conditional_menu_count"`
|
|
}
|
|
|
|
type mpMenuCreateResp struct {
|
|
ErrCode int `json:"errcode"`
|
|
ErrMsg string `json:"errmsg"`
|
|
}
|
|
|
|
// PublishLoginMenu 在现有菜单基础上追加「扫码登录」菜单并发布。
|
|
// - 已存在登录菜单:不再重复发布(AlreadyExists=true);
|
|
// - 一级菜单满 3 个:返回错误提示;
|
|
// - 存在个性化菜单:一并返回条数(个性化菜单会覆盖默认菜单,需到公众号后台处理)。
|
|
//
|
|
// 注意:微信客户端有菜单缓存,发布后通常需重新关注或等待(最长 24 小时)才可见。
|
|
func PublishLoginMenu(cfg *Config) (*PublishMenuResult, error) {
|
|
token, err := GetAccessToken(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 1) 读取现有菜单;46003(菜单不存在)视为空菜单
|
|
var cur mpMenuGetResp
|
|
if gerr := httpGetJSON(apiBase+"/cgi-bin/menu/get?access_token="+url.QueryEscape(token), &cur); gerr != nil {
|
|
return nil, gerr
|
|
}
|
|
buttons := cur.Menu.Button
|
|
if cur.ErrCode != 0 && cur.ErrCode != 46003 {
|
|
return nil, &APIError{API: "读取自定义菜单", Code: cur.ErrCode, Msg: cur.ErrMsg}
|
|
}
|
|
logs.Info("[wechat-mp] 当前默认菜单 %d 项,个性化菜单 %d 条", len(buttons), len(cur.ConditionalMenu))
|
|
|
|
for i := range buttons {
|
|
if strings.EqualFold(strings.TrimSpace(buttons[i].Key), LoginMenuKey) {
|
|
return &PublishMenuResult{
|
|
Buttons: buttons,
|
|
AlreadyExists: true,
|
|
ConditionalMenuCount: len(cur.ConditionalMenu),
|
|
}, nil
|
|
}
|
|
}
|
|
if len(buttons) >= 3 {
|
|
return nil, errors.New("一级菜单已达 3 个上限,请到公众号后台精简后再发布")
|
|
}
|
|
|
|
// 2) 追加登录菜单并发布
|
|
buttons = append(buttons, mpMenuButton{Type: "click", Name: "扫码登录", Key: LoginMenuKey})
|
|
var created mpMenuCreateResp
|
|
if perr := httpPostJSON(apiBase+"/cgi-bin/menu/create?access_token="+url.QueryEscape(token),
|
|
map[string]interface{}{"button": buttons}, &created); perr != nil {
|
|
return nil, perr
|
|
}
|
|
if created.ErrCode != 0 {
|
|
return nil, &APIError{API: "发布自定义菜单", Code: created.ErrCode, Msg: created.ErrMsg}
|
|
}
|
|
logs.Info("[wechat-mp] 菜单发布成功,当前 %d 项", len(buttons))
|
|
|
|
return &PublishMenuResult{
|
|
Buttons: buttons,
|
|
ConditionalMenuCount: len(cur.ConditionalMenu),
|
|
}, nil
|
|
}
|