批量优化功能
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
authsvc "server/services/auth"
|
||||
"server/services/wechatmp"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
// WechatMpCallbackController 微信公众号服务器回调(微信服务器调用,无需平台鉴权)
|
||||
@@ -100,6 +104,10 @@ func (c *WechatMpCallbackController) Callback() {
|
||||
return
|
||||
}
|
||||
|
||||
// 排查用:记录微信推送的消息/事件类型
|
||||
logs.Info("[wechat-mp] 收到推送: type=%s event=%s from=%s key=%s",
|
||||
msg.MsgType, msg.Event, msg.FromUserName, msg.EventKey)
|
||||
|
||||
reply := wechatmp.HandleInbound(cfg, msg)
|
||||
if strings.TrimSpace(reply) == "" {
|
||||
// 无需回复:返回空串,微信视为处理成功
|
||||
@@ -122,3 +130,144 @@ func (c *WechatMpCallbackController) Callback() {
|
||||
}
|
||||
c.Ctx.Output.Body([]byte(plainReply))
|
||||
}
|
||||
|
||||
// Login GET/POST /api/wechat/mp/login?token=xxx&client_id=yyy
|
||||
// 公众号「确认登录」一次性令牌兑换登录态(无平台鉴权:令牌本身即凭证,一次性 + 短时效)。
|
||||
// 已绑定身份:单企业直接签发令牌;多企业签发待选令牌并由前端展示企业选择。
|
||||
// 同时认领等待中的 PC 扫码会话(存在时),PC 端轮询后自动登录。
|
||||
func (c *WechatMpCallbackController) Login() {
|
||||
token := strings.TrimSpace(c.GetString("token"))
|
||||
clientID := strings.TrimSpace(c.GetString("client_id"))
|
||||
if clientID == "" {
|
||||
clientID = "yz-backend"
|
||||
}
|
||||
|
||||
row, err := wechatmp.ConsumeLoginToken(token)
|
||||
if err != nil {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
switch row.BindType {
|
||||
case models.WechatBindTypeTenantUser:
|
||||
// 认领 PC 扫码会话:确认链接携带 scene 时精确认领;「登录」文本路径无 scene,FIFO 兜底
|
||||
if scene := strings.TrimSpace(c.GetString("scene")); scene != "" {
|
||||
wechatmp.ConfirmScanLoginByToken(scene, token, row.BindType, row.BindID, row.BindTid)
|
||||
} else {
|
||||
wechatmp.ConfirmOldestPendingScanLogin(row.BindType, row.BindID, row.BindTid)
|
||||
}
|
||||
|
||||
// 身份状态校验(禁用/锁定则拒绝登录)
|
||||
var identity models.AuthIdentity
|
||||
if err := models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", row.BindID).One(&identity); err != nil {
|
||||
c.failJSON(400, "账号不存在或已注销")
|
||||
return
|
||||
}
|
||||
if identity.Status != models.AuthIdentityStatusEnabled {
|
||||
c.failJSON(400, "账号已被禁用或锁定,无法登录")
|
||||
return
|
||||
}
|
||||
username := "微信用户"
|
||||
if identity.Nickname != nil && strings.TrimSpace(*identity.Nickname) != "" {
|
||||
username = *identity.Nickname
|
||||
}
|
||||
|
||||
// 企业选择:单企业直接登录;多企业签发待选令牌,由前端展示选择界面
|
||||
tenants, lerr := authsvc.ListTenantOptions(row.BindID)
|
||||
if lerr != nil || len(tenants) == 0 {
|
||||
c.failJSON(400, "该账号未加入任何企业,无法登录")
|
||||
return
|
||||
}
|
||||
tid := uint64(0)
|
||||
needChoose := false
|
||||
if len(tenants) == 1 {
|
||||
tid = tenants[0].Tid
|
||||
} else {
|
||||
tid = authsvc.PendingTenantID
|
||||
needChoose = true
|
||||
}
|
||||
|
||||
tokens, terr := c.issueMpLoginTokens(row.BindID, tid, clientID, username, "公众号确认链接登录成功", needChoose)
|
||||
if terr != nil {
|
||||
c.failJSON(400, terr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"type": "backend",
|
||||
"tokens": tokens,
|
||||
"tenant": row.BindTid,
|
||||
}
|
||||
if needChoose {
|
||||
data["need_choose_tenant"] = true
|
||||
data["tenants"] = tenants
|
||||
}
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "登录成功",
|
||||
"data": data,
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
default:
|
||||
c.failJSON(400, "该微信绑定的账号类型暂不支持链接登录")
|
||||
}
|
||||
}
|
||||
|
||||
// issueMpLoginTokens 为绑定身份建会话并签发 UAC 令牌(含登录日志,失败不影响主流程)
|
||||
// tid 为 authsvc.PendingTenantID 时签发待选企业用的短期令牌
|
||||
func (c *WechatMpCallbackController) issueMpLoginTokens(identityID, tid uint64, clientID, username, logMsg string, needChoose bool) (*authsvc.TokenPair, error) {
|
||||
accessTTL := 0
|
||||
if needChoose {
|
||||
accessTTL = 600 // 待选状态令牌只够用户完成企业选择
|
||||
}
|
||||
sess, serr := authsvc.CreateSession(authsvc.SessionInfo{
|
||||
IdentityID: identityID,
|
||||
Tid: tid,
|
||||
ClientID: clientID,
|
||||
IP: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Request.UserAgent(),
|
||||
LoginType: "wechat_mp",
|
||||
Amr: "wechat_mp",
|
||||
})
|
||||
if serr != nil {
|
||||
return nil, fmt.Errorf("登录失败:%s", serr.Error())
|
||||
}
|
||||
tokens, terr := authsvc.IssueTokens(authsvc.TokenIssue{
|
||||
IdentityID: identityID,
|
||||
Tid: tid,
|
||||
ClientID: clientID,
|
||||
Sid: sess.Sid,
|
||||
Username: username,
|
||||
Amr: "wechat_mp",
|
||||
AccessTTL: accessTTL,
|
||||
})
|
||||
if terr != nil {
|
||||
return nil, fmt.Errorf("签发令牌失败:%s", terr.Error())
|
||||
}
|
||||
|
||||
identityIDCopy := identityID
|
||||
tidCopy := tid
|
||||
amr := "wechat_mp"
|
||||
_, _ = models.Orm.Insert(&models.AuthLoginLog{
|
||||
Tid: &tidCopy,
|
||||
IdentityID: &identityIDCopy,
|
||||
UserName: username,
|
||||
ClientID: clientID,
|
||||
LoginType: "wechat_mp",
|
||||
Amr: &amr,
|
||||
Status: 1,
|
||||
Message: logMsg,
|
||||
IP: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Request.UserAgent(),
|
||||
})
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (c *WechatMpCallbackController) failJSON(bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user