Files
yunzerwebsiteallinone/go/controllers/backend_wechat_mp.go
T
2026-09-16 18:15:58 +08:00

214 lines
5.6 KiB
Go

package controllers
import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
"server/models"
"server/pkg/jwtutil"
"server/services/wechatmp"
beego "github.com/beego/beego/v2/server/web"
)
// =============================================================
// 租户端「微信公众号绑定」:
// 扫码关注公众号 → 公众号被动回复验证码 → 本端输入验证码完成绑定
// 绑定后(公众号认证后)可接收平台/租户侧的提醒、公告推送
// =============================================================
// BackendWechatMpController 租户端微信公众号绑定
type BackendWechatMpController struct {
beego.Controller
}
func (c *BackendWechatMpController) claims() (*jwtutil.Claims, error) {
auth := c.Ctx.Request.Header.Get("Authorization")
if auth == "" {
return nil, fmt.Errorf("未登录")
}
parts := strings.SplitN(auth, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
return nil, fmt.Errorf("认证信息格式错误")
}
claims, err := jwtutil.ParseToken(parts[1])
if err != nil {
return nil, fmt.Errorf("无效的token")
}
if claims.UserType != "backend" && claims.UserType != "app" {
return nil, fmt.Errorf("无权访问")
}
return claims, nil
}
func (c *BackendWechatMpController) jsonErr(httpStatus, bizCode int, msg string) {
c.Ctx.Output.SetStatus(httpStatus)
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
_ = c.ServeJSON()
}
func (c *BackendWechatMpController) ok(data interface{}, msg string) {
if msg == "" {
msg = "success"
}
resp := map[string]interface{}{"code": 200, "msg": msg}
if data != nil {
resp["data"] = data
}
c.Data["json"] = resp
_ = c.ServeJSON()
}
func (c *BackendWechatMpController) readBody(out interface{}) bool {
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.jsonErr(400, 400, "参数错误")
return false
}
if err := json.Unmarshal(raw, out); err != nil {
c.jsonErr(400, 400, "参数错误")
return false
}
return true
}
// BindStart POST /backend/wechatMp/bind/start
func (c *BackendWechatMpController) BindStart() {
claims, err := c.claims()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
uid := uint64(claims.UserID)
tid := uint64(claims.TenantId)
if uid == 0 || tid == 0 {
c.jsonErr(401, 401, "未获取到用户信息")
return
}
scene, _, qrURL, expireAt, err := wechatmp.CreateVerifyCode(models.WechatBindTypeTenantUser, uid, tid, 0)
if 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()),
}, "生成成功")
}
// BindStatus GET /backend/wechatMp/bind/status?scene=xxx
func (c *BackendWechatMpController) BindStatus() {
claims, err := c.claims()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
scene := strings.TrimSpace(c.GetString("scene"))
row, err := wechatmp.GetVerifyCode(scene)
if err != nil {
c.jsonErr(404, 404, err.Error())
return
}
if row.BindType != models.WechatBindTypeTenantUser || row.BindID != uint64(claims.UserID) {
c.jsonErr(403, 403, "该二维码不是当前账号发起,请重新获取")
return
}
c.ok(map[string]interface{}{
"status": row.Status,
"expire_at": row.ExpireAt,
}, "获取成功")
}
// BindConfirm POST /backend/wechatMp/bind/confirm {scene, code}
func (c *BackendWechatMpController) BindConfirm() {
claims, err := c.claims()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
var p struct {
Scene string `json:"scene"`
Code string `json:"code"`
}
if !c.readBody(&p) {
return
}
if strings.TrimSpace(p.Code) == "" {
c.jsonErr(400, 400, "请输入公众号返回的验证码")
return
}
var follower *models.WechatMpFollower
if strings.TrimSpace(p.Scene) == "" {
// 未展示二维码(在公众号内发送「验证码」获取):按验证码认领待认领会话
follower, err = wechatmp.ConfirmVerifyCodeByCode(
models.WechatBindTypeTenantUser,
uint64(claims.UserID),
uint64(claims.TenantId),
p.Code,
)
} else {
follower, err = wechatmp.ConfirmVerifyCode(
models.WechatBindTypeTenantUser,
uint64(claims.UserID),
uint64(claims.TenantId),
p.Scene,
p.Code,
)
}
if err != nil {
c.jsonErr(400, 400, err.Error())
return
}
c.ok(map[string]interface{}{
"openid": follower.OpenID,
"nickname": follower.Nickname,
"bind_time": follower.BindTime,
}, "绑定成功")
}
// MyBinding GET /backend/wechatMp/my
func (c *BackendWechatMpController) MyBinding() {
claims, err := c.claims()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
follower, err := wechatmp.GetFollowerByUser(models.WechatBindTypeTenantUser, uint64(claims.UserID))
if err != nil {
c.jsonErr(500, 500, "查询失败:"+err.Error())
return
}
if follower == nil || follower.BindTid != uint64(claims.TenantId) {
c.ok(map[string]interface{}{"bound": false}, "获取成功")
return
}
c.ok(map[string]interface{}{
"bound": true,
"openid": follower.OpenID,
"nickname": follower.Nickname,
"avatar": follower.Avatar,
"subscribe": follower.Subscribe,
"bind_time": follower.BindTime,
"subscribe_time": follower.SubscribeTime,
}, "获取成功")
}
// Unbind POST /backend/wechatMp/unbind
func (c *BackendWechatMpController) Unbind() {
claims, err := c.claims()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
if err := wechatmp.UnbindByUser(models.WechatBindTypeTenantUser, uint64(claims.UserID)); err != nil {
c.jsonErr(500, 500, "解绑失败:"+err.Error())
return
}
c.ok(nil, "已解绑")
}