批量优化功能
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -106,6 +108,7 @@ func (c *PlatformWechatMpController) GetConfig() {
|
||||
data["encrypt_mode"] = cfg.EncryptMode
|
||||
data["template_id"] = cfg.TemplateID
|
||||
data["remark"] = cfg.Remark
|
||||
data["follow_qrcode"] = cfg.FollowQrcode
|
||||
data["configured"] = cfg.AppID != ""
|
||||
if cfg.Verified {
|
||||
data["verified"] = 1
|
||||
@@ -190,7 +193,8 @@ func (c *PlatformWechatMpController) TestConnection() {
|
||||
// ============================ 绑定(当前平台用户) ============================
|
||||
|
||||
// BindStart POST /platform/wechatMp/bind/start
|
||||
// 生成带参二维码;用户扫码关注后公众号将被动回复验证码
|
||||
// 未认证公众号无法生成带参二维码(48001):不再下发二维码,
|
||||
// 返回绑定指引(公众号内发送「验证码」取码 → 页面填码 → bind/confirm 核销)。
|
||||
func (c *PlatformWechatMpController) BindStart() {
|
||||
claims, err := c.claims()
|
||||
if err != nil {
|
||||
@@ -202,18 +206,137 @@ func (c *PlatformWechatMpController) BindStart() {
|
||||
c.jsonErr(401, 401, "未获取到用户信息")
|
||||
return
|
||||
}
|
||||
scene, _, qrURL, expireAt, err := wechatmp.CreateVerifyCode(models.WechatBindTypePlatformUser, uid, 0, 0)
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, "生成二维码失败:"+err.Error())
|
||||
if _, err := wechatmp.LoadEnabledConfig(); err != nil {
|
||||
c.jsonErr(400, 400, "公众号未启用:"+err.Error())
|
||||
return
|
||||
}
|
||||
c.ok(map[string]interface{}{
|
||||
"scene": scene,
|
||||
"qrcode_url": qrURL,
|
||||
"expire_at": expireAt,
|
||||
"expires_in": int(time.Until(expireAt).Seconds()),
|
||||
"callback_url": wechatmp.CallbackURL(),
|
||||
}, "生成成功")
|
||||
"mode": "mp_message",
|
||||
"guides": wechatmp.BindGuides(),
|
||||
"follow_qrcode": wechatmp.FollowQrcodeURL(),
|
||||
"expires_in": int(wechatmp.DefaultVerifyTTL.Seconds()),
|
||||
"callback_url": wechatmp.CallbackURL(),
|
||||
}, "请按指引完成绑定")
|
||||
}
|
||||
|
||||
// ============================ 自定义菜单 ============================
|
||||
|
||||
// PublishMenu POST /platform/wechatMp/menu/publish
|
||||
// 通过接口在公众号底部发布「扫码登录」菜单(点击 → 自动回复确认登录链接)。
|
||||
// 需认证公众号;未认证(48001)时提示改用「公众号内发送登录」路径。
|
||||
func (c *PlatformWechatMpController) PublishMenu() {
|
||||
if _, err := c.claims(); err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
cfg, err := wechatmp.LoadEnabledConfig()
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, "公众号未启用:"+err.Error())
|
||||
return
|
||||
}
|
||||
res, merr := wechatmp.PublishLoginMenu(cfg)
|
||||
if merr != nil {
|
||||
msg := "发布菜单失败:" + merr.Error()
|
||||
if hint, ok := wechatmp.ExplainError(merr); ok {
|
||||
msg += ";" + hint
|
||||
}
|
||||
c.jsonErr(400, 400, msg)
|
||||
return
|
||||
}
|
||||
|
||||
// 微信客户端有菜单缓存:发布后需重新关注或等待(最长 24 小时)才可见
|
||||
msg := "菜单已发布:微信客户端有缓存,请重新关注公众号或等待刷新(最长 24 小时)后可见「扫码登录」"
|
||||
if res.AlreadyExists {
|
||||
msg = "菜单中已存在「扫码登录」入口,未重复发布;若底部未显示,请等待客户端刷新或重新关注"
|
||||
}
|
||||
if res.ConditionalMenuCount > 0 {
|
||||
msg += fmt.Sprintf(";检测到个性化菜单 %d 条,个性化菜单会覆盖默认菜单,请到公众号后台确认",
|
||||
res.ConditionalMenuCount)
|
||||
}
|
||||
c.ok(map[string]interface{}{
|
||||
"menu": res.Buttons,
|
||||
"already_exists": res.AlreadyExists,
|
||||
"conditional_menu_count": res.ConditionalMenuCount,
|
||||
}, msg)
|
||||
}
|
||||
|
||||
// ============================ 关注二维码 ============================
|
||||
|
||||
// 二维码图片限制:2MB,常见图片格式
|
||||
const followQrcodeMaxBytes = 2 * 1024 * 1024
|
||||
|
||||
var followQrcodeAllowedExts = map[string]bool{
|
||||
"jpg": true, "jpeg": true, "png": true, "gif": true, "webp": true, "bmp": true,
|
||||
}
|
||||
|
||||
// saveFollowQrcode 校验图片并写入配置表(data URL 存储),返回 data URL
|
||||
func (c *PlatformWechatMpController) saveFollowQrcode() (string, error) {
|
||||
if err := c.Ctx.Request.ParseMultipartForm(followQrcodeMaxBytes); err != nil {
|
||||
return "", fmt.Errorf("解析上传失败: %w", err)
|
||||
}
|
||||
fh, header, err := c.GetFile("file")
|
||||
if err != nil || fh == nil {
|
||||
return "", fmt.Errorf("请选择要上传的二维码图片")
|
||||
}
|
||||
defer fh.Close()
|
||||
if header != nil && header.Size > followQrcodeMaxBytes {
|
||||
return "", fmt.Errorf("图片大小不能超过 2MB")
|
||||
}
|
||||
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(header.Filename), "."))
|
||||
if !followQrcodeAllowedExts[ext] {
|
||||
return "", fmt.Errorf("仅支持 jpg / png / gif / webp / bmp 格式")
|
||||
}
|
||||
raw, err := io.ReadAll(fh)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取图片失败: %w", err)
|
||||
}
|
||||
if len(raw) == 0 {
|
||||
return "", fmt.Errorf("图片内容为空")
|
||||
}
|
||||
mime := "image/png"
|
||||
switch ext {
|
||||
case "jpg", "jpeg":
|
||||
mime = "image/jpeg"
|
||||
case "gif":
|
||||
mime = "image/gif"
|
||||
case "webp":
|
||||
mime = "image/webp"
|
||||
case "bmp":
|
||||
mime = "image/bmp"
|
||||
}
|
||||
return "data:" + mime + ";base64," + base64.StdEncoding.EncodeToString(raw), nil
|
||||
}
|
||||
|
||||
// UploadFollowQrcode POST /platform/wechatMp/followQrcode
|
||||
// 上传公众号「关注二维码」图片(普通二维码,用户扫码进入公众号会话)
|
||||
func (c *PlatformWechatMpController) UploadFollowQrcode() {
|
||||
if _, err := c.claims(); err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
dataURL, err := c.saveFollowQrcode()
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, err.Error())
|
||||
return
|
||||
}
|
||||
if err := wechatmp.SaveFollowQrcode(dataURL); err != nil {
|
||||
c.jsonErr(500, 500, "保存失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
c.ok(map[string]interface{}{"follow_qrcode": dataURL}, "上传成功")
|
||||
}
|
||||
|
||||
// DeleteFollowQrcode POST /platform/wechatMp/followQrcode/delete
|
||||
func (c *PlatformWechatMpController) DeleteFollowQrcode() {
|
||||
if _, err := c.claims(); err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
if err := wechatmp.SaveFollowQrcode(""); err != nil {
|
||||
c.jsonErr(500, 500, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
c.ok(nil, "已删除")
|
||||
}
|
||||
|
||||
// BindStatus GET /platform/wechatMp/bind/status?scene=xxx
|
||||
|
||||
Reference in New Issue
Block a user