214 lines
5.6 KiB
Go
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, "已解绑")
|
|
}
|