优化小程序
This commit is contained in:
1 parent
2e2b837a20
commit
727319a6eb
24 files changed
+1509
-200
No files matched your search
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
authsvc "server/services/auth"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -30,30 +30,67 @@ import (
|
||||
// - 暂不加入:只完成身份注册。
|
||||
// =============================================================
|
||||
|
||||
// registerTokenTTL register_token 有效期
|
||||
const registerTokenTTL = 10 * time.Minute
|
||||
// registerTokenTTL register_token 有效期(覆盖第一步核验到第二步企业归属的完整操作:
|
||||
// 填企业资料 + 上传营业执照可能较久,给 2 小时;会话已落库,服务重启也不会丢)
|
||||
const registerTokenTTL = 2 * time.Hour
|
||||
|
||||
// registerTokenItem 一次性注册令牌:身份已创建,等待第二步归属操作
|
||||
// registerTokenItem 一次性注册令牌:
|
||||
// 第一步核验通过时只保存「待注册信息」,身份尚未创建;
|
||||
// 第二步归属操作调用 CompleteRegister 时才真正建号并写回 IdentityID。
|
||||
// 这样令牌过期(或用户放弃)时不会有「已占用账号但没完成注册」的残留身份。
|
||||
type registerTokenItem struct {
|
||||
IdentityID uint64
|
||||
OpenID string // 账号注册模式扫码人的微信 openid(用于注册后绑定微信登录)
|
||||
ExpiredAt time.Time
|
||||
IdentityID uint64 // 第二步完成建号后写回;0 = 尚未建号
|
||||
Account string // 账号注册 / 小程序核验的账号名
|
||||
Mobile string // 手机号注册的手机号
|
||||
Nickname string
|
||||
PasswordHash string
|
||||
PasswordAlgo string
|
||||
OpenID string // 核验扫码人的微信 openid(公众号 / 小程序,用于注册后绑定微信登录)
|
||||
UnionID string // 小程序核验时的 unionid(同主体多端互通,可为空)
|
||||
Provider string // models.IdPWechat(公众号) / models.IdPWechatMini(小程序)
|
||||
ExpiredAt time.Time
|
||||
}
|
||||
|
||||
var registerTokenStore sync.Map // key: token
|
||||
// RegisterPending 第一步核验结果(尚未落库为身份)
|
||||
type RegisterPending struct {
|
||||
Account string
|
||||
Mobile string
|
||||
Nickname string
|
||||
PasswordHash string
|
||||
PasswordAlgo string
|
||||
OpenID string
|
||||
UnionID string
|
||||
Provider string
|
||||
}
|
||||
|
||||
// IssueRegisterToken 身份创建成功后签发注册令牌
|
||||
func IssueRegisterToken(identityID uint64, openid string) string {
|
||||
// IssuePendingRegisterToken 保存第一步核验结果并签发待注册令牌(此时尚未创建身份)。
|
||||
// 会话落库(yz_auth_register_session):服务重启 / 多副本部署都不会中途失效。
|
||||
func IssuePendingRegisterToken(p RegisterPending) string {
|
||||
b := make([]byte, 24)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return fmt.Sprintf("rt%d%d", identityID, time.Now().UnixNano())
|
||||
return fmt.Sprintf("rt%d%d", time.Now().UnixNano(), time.Now().UnixNano())
|
||||
}
|
||||
token := hex.EncodeToString(b)
|
||||
registerTokenStore.Store(token, registerTokenItem{
|
||||
IdentityID: identityID,
|
||||
OpenID: openid,
|
||||
ExpiredAt: time.Now().Add(registerTokenTTL),
|
||||
})
|
||||
now := time.Now()
|
||||
row := &models.AuthRegisterSession{
|
||||
Token: token,
|
||||
Account: strings.TrimSpace(p.Account),
|
||||
Mobile: strings.TrimSpace(p.Mobile),
|
||||
Nickname: strings.TrimSpace(p.Nickname),
|
||||
PasswordHash: p.PasswordHash,
|
||||
PasswordAlgo: p.PasswordAlgo,
|
||||
OpenID: strings.TrimSpace(p.OpenID),
|
||||
UnionID: strings.TrimSpace(p.UnionID),
|
||||
Provider: p.Provider,
|
||||
Status: models.RegisterSessionPending,
|
||||
ExpireAt: now.Add(registerTokenTTL),
|
||||
CreateTime: now,
|
||||
UpdateTime: &now,
|
||||
}
|
||||
if _, err := models.Orm.Insert(row); err != nil {
|
||||
logs.Error("[register] 保存注册会话失败: %v", err)
|
||||
return ""
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
@@ -63,43 +100,53 @@ func ConsumeRegisterToken(token string) (identityID uint64, openid string, err e
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
registerTokenStore.Delete(strings.TrimSpace(token))
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthRegisterSession)).
|
||||
Filter("token", strings.TrimSpace(token)).
|
||||
Delete()
|
||||
return identityID, openid, nil
|
||||
}
|
||||
|
||||
// PeekRegisterToken 查看注册令牌(不消费);第二步先 Peek 校验,
|
||||
// 业务成功后再 Consume,避免申请失败导致令牌作废无法重试。
|
||||
func PeekRegisterToken(token string) (identityID uint64, openid string, err error) {
|
||||
identityID, openid, _, _, err = PeekRegisterTokenThird(token)
|
||||
return identityID, openid, err
|
||||
}
|
||||
|
||||
// PeekRegisterTokenThird 同 PeekRegisterToken,额外返回 unionid 与第三方来源
|
||||
// (公众号 / 小程序),供第二步按来源绑定对应 provider。
|
||||
func PeekRegisterTokenThird(token string) (identityID uint64, openid, unionid, provider string, err error) {
|
||||
token = strings.TrimSpace(token)
|
||||
if token == "" {
|
||||
return 0, "", errors.New("注册会话已失效,请重新注册")
|
||||
return 0, "", "", "", errors.New("注册会话已失效,请重新注册")
|
||||
}
|
||||
v, ok := registerTokenStore.Load(token)
|
||||
if !ok {
|
||||
return 0, "", errors.New("注册会话已失效,请重新注册")
|
||||
var row models.AuthRegisterSession
|
||||
if err = models.Orm.QueryTable(new(models.AuthRegisterSession)).
|
||||
Filter("token", token).One(&row); err != nil {
|
||||
return 0, "", "", "", errors.New("注册会话已失效,请重新注册")
|
||||
}
|
||||
item, ok := v.(registerTokenItem)
|
||||
if !ok {
|
||||
return 0, "", errors.New("注册会话已失效,请重新注册")
|
||||
if time.Now().After(row.ExpireAt) {
|
||||
// 令牌过期:若期间已建号但没走完归属,回滚,避免留下占用账号的半成品
|
||||
rollbackRegisterIdentity(row.IdentityID)
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthRegisterSession)).Filter("token", token).Delete()
|
||||
return 0, "", "", "", errors.New("注册会话已过期,请重新注册")
|
||||
}
|
||||
if time.Now().After(item.ExpiredAt) {
|
||||
registerTokenStore.Delete(token)
|
||||
return 0, "", errors.New("注册会话已过期,请重新注册")
|
||||
}
|
||||
return item.IdentityID, item.OpenID, nil
|
||||
return row.IdentityID, row.OpenID, row.UnionID, row.Provider, nil
|
||||
}
|
||||
|
||||
// ---------------- 第一步:核验并创建身份 ----------------
|
||||
|
||||
// RegisterVerifyInput 第一步核验入参
|
||||
type RegisterVerifyInput struct {
|
||||
Mode string // phone / account
|
||||
Mode string // phone / account / mini
|
||||
Phone string // mode=phone:手机号
|
||||
Account string // mode=account:自定义账号
|
||||
Code string // 验证码(短信 或 服务号下发)
|
||||
Account string // mode=account|mini:自定义账号
|
||||
Code string // 验证码(短信 或 服务号下发);mode=mini 不使用
|
||||
Password string
|
||||
Confirm string
|
||||
OpenID string // mode=account:核销服务号验证码得到的 openid
|
||||
OpenID string // 核验得到的微信 openid(公众号 或 小程序)
|
||||
UnionID string // mode=mini:小程序 unionid(可为空)
|
||||
Provider string // 微信来源:models.IdPWechat / models.IdPWechatMini
|
||||
}
|
||||
|
||||
// validateAccountName 账号名规则:4-32 位,字母开头,字母/数字/下划线/中划线
|
||||
@@ -132,68 +179,207 @@ func findIdentityByAccountName(account string) *models.AuthIdentity {
|
||||
return &m
|
||||
}
|
||||
|
||||
// VerifyAndCreateIdentity 第一步:校验验证码与密码,创建统一身份,返回身份与新令牌。
|
||||
// 调用方保证:mode=phone 已发短信验证码;mode=account 已核销服务号验证码并传入 openid。
|
||||
func VerifyAndCreateIdentity(in RegisterVerifyInput) (*models.AuthIdentity, string, error) {
|
||||
// VerifyAndIssueRegisterToken 第一步:校验验证码与密码、检查账号唯一性,签发待注册令牌。
|
||||
// 注意:此处【不创建身份】——身份推迟到第二步归属操作(CompleteRegister)才落库,
|
||||
// 避免「核验通过但第二步超时未完成」留下占用账号却无法使用的半成品账号。
|
||||
// 调用方保证:mode=phone 已发短信验证码;mode=account 已核销服务号验证码并传入 openid;
|
||||
// mode=mini 已核销小程序注册会话并传入小程序 openid(Provider=wechat_mini)。
|
||||
func VerifyAndIssueRegisterToken(in RegisterVerifyInput) (string, error) {
|
||||
in.Mode = strings.TrimSpace(in.Mode)
|
||||
if in.Mode != "phone" && in.Mode != "account" {
|
||||
return nil, "", errors.New("请选择注册方式")
|
||||
if in.Mode != "phone" && in.Mode != "account" && in.Mode != "mini" {
|
||||
return "", errors.New("请选择注册方式")
|
||||
}
|
||||
if len(strings.TrimSpace(in.Password)) < 6 {
|
||||
return nil, "", errors.New("密码长度不能少于6个字符")
|
||||
return "", errors.New("密码长度不能少于6个字符")
|
||||
}
|
||||
if in.Password != in.Confirm {
|
||||
return nil, "", errors.New("两次输入的密码不一致")
|
||||
return "", errors.New("两次输入的密码不一致")
|
||||
}
|
||||
|
||||
identity := &models.AuthIdentity{
|
||||
UnionID: strings.ReplaceAll(uuid.NewString(), "-", ""),
|
||||
pending := RegisterPending{
|
||||
OpenID: strings.TrimSpace(in.OpenID),
|
||||
UnionID: strings.TrimSpace(in.UnionID),
|
||||
Provider: in.Provider,
|
||||
}
|
||||
|
||||
switch in.Mode {
|
||||
case "phone":
|
||||
phone := strings.TrimSpace(in.Phone)
|
||||
if !IsMobile(phone) {
|
||||
return nil, "", errors.New("请输入正确的手机号")
|
||||
return "", errors.New("请输入正确的手机号")
|
||||
}
|
||||
if err := VerifyIdentityCode("register", phone, in.Code); err != nil {
|
||||
return nil, "", err
|
||||
return "", err
|
||||
}
|
||||
if findIdentityByMobile(phone) != nil {
|
||||
return nil, "", errors.New("该手机号已注册,请直接登录")
|
||||
if exist := findIdentityByMobile(phone); exist != nil {
|
||||
// 孤儿身份(注册未完成、从未登录、无企业归属)自动回收,账号/手机号可重新注册
|
||||
if !isOrphanIdentity(exist) {
|
||||
return "", errors.New("该手机号已注册,请直接登录")
|
||||
}
|
||||
discardOrphanIdentity(exist.ID)
|
||||
}
|
||||
identity.Mobile = &phone
|
||||
nick := "用户" + phone[len(phone)-4:]
|
||||
identity.Nickname = &nick
|
||||
case "account":
|
||||
pending.Mobile = phone
|
||||
pending.Nickname = "用户" + phone[len(phone)-4:]
|
||||
case "account", "mini":
|
||||
account := strings.TrimSpace(in.Account)
|
||||
if err := validateAccountName(account); err != nil {
|
||||
return nil, "", err
|
||||
return "", err
|
||||
}
|
||||
if findIdentityByAccountName(account) != nil {
|
||||
return nil, "", errors.New("该账号已被占用,请换一个")
|
||||
if exist := findIdentityByAccountName(account); exist != nil {
|
||||
// 同上:回收「核验通过但没走完第二步」留下的孤儿身份
|
||||
if !isOrphanIdentity(exist) {
|
||||
return "", errors.New("该账号已被注册,请直接登录,或换一个账号名")
|
||||
}
|
||||
discardOrphanIdentity(exist.ID)
|
||||
}
|
||||
identity.Account = &account
|
||||
nick := account
|
||||
identity.Nickname = &nick
|
||||
if in.Mode == "mini" && strings.TrimSpace(in.OpenID) == "" {
|
||||
return "", errors.New("微信核验已失效,请重新扫码确认")
|
||||
}
|
||||
pending.Account = account
|
||||
pending.Nickname = account
|
||||
}
|
||||
|
||||
hashed, err := passwordutil.Hash(strings.TrimSpace(in.Password))
|
||||
if err != nil {
|
||||
return nil, "", errors.New("密码处理失败")
|
||||
return "", errors.New("密码处理失败")
|
||||
}
|
||||
identity.PasswordHash = &hashed
|
||||
identity.PasswordAlgo = passwordutil.AlgoOf(hashed)
|
||||
identity.Status = models.AuthIdentityStatusEnabled
|
||||
pending.PasswordHash = hashed
|
||||
pending.PasswordAlgo = passwordutil.AlgoOf(hashed)
|
||||
|
||||
token := IssuePendingRegisterToken(pending)
|
||||
if token == "" {
|
||||
return "", errors.New("注册会话创建失败,请稍后重试")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// CompleteRegister 第二步归属操作开始时真正创建身份:
|
||||
// 取出第一步核验结果 → 复核唯一性 → 建号 → 绑定微信(若有)→ 写回令牌。
|
||||
// 令牌此时不消费(业务失败可重试);令牌过期时由 rollbackRegisterIdentity 清理半成品身份。
|
||||
func CompleteRegister(token string) (uint64, error) {
|
||||
tk := strings.TrimSpace(token)
|
||||
var row models.AuthRegisterSession
|
||||
if err := models.Orm.QueryTable(new(models.AuthRegisterSession)).
|
||||
Filter("token", tk).One(&row); err != nil {
|
||||
return 0, errors.New("注册会话已失效,请重新注册")
|
||||
}
|
||||
if time.Now().After(row.ExpireAt) {
|
||||
rollbackRegisterIdentity(row.IdentityID)
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthRegisterSession)).Filter("token", tk).Delete()
|
||||
return 0, errors.New("注册会话已过期,请重新注册")
|
||||
}
|
||||
if row.IdentityID != 0 {
|
||||
return row.IdentityID, nil // 已建号(重试场景),直接复用
|
||||
}
|
||||
|
||||
// 建号前复核:核验后到这一步之间可能被他人抢注;孤儿身份仍可回收
|
||||
if row.Account != "" {
|
||||
if exist := findIdentityByAccountName(row.Account); exist != nil {
|
||||
if !isOrphanIdentity(exist) {
|
||||
return 0, errors.New("该账号已被注册,请直接登录,或换一个账号名")
|
||||
}
|
||||
discardOrphanIdentity(exist.ID)
|
||||
}
|
||||
}
|
||||
if row.Mobile != "" {
|
||||
if exist := findIdentityByMobile(row.Mobile); exist != nil {
|
||||
if !isOrphanIdentity(exist) {
|
||||
return 0, errors.New("该手机号已注册,请直接登录")
|
||||
}
|
||||
discardOrphanIdentity(exist.ID)
|
||||
}
|
||||
}
|
||||
|
||||
identity := &models.AuthIdentity{
|
||||
UnionID: strings.ReplaceAll(uuid.NewString(), "-", ""),
|
||||
PasswordAlgo: row.PasswordAlgo,
|
||||
PasswordHash: &row.PasswordHash,
|
||||
Status: models.AuthIdentityStatusEnabled,
|
||||
}
|
||||
if row.Account != "" {
|
||||
account := row.Account
|
||||
identity.Account = &account
|
||||
}
|
||||
if row.Mobile != "" {
|
||||
mobile := row.Mobile
|
||||
identity.Mobile = &mobile
|
||||
}
|
||||
if row.Nickname != "" {
|
||||
nick := row.Nickname
|
||||
identity.Nickname = &nick
|
||||
}
|
||||
id, err := models.Orm.Insert(identity)
|
||||
if err != nil {
|
||||
return nil, "", errors.New("注册失败,请稍后重试")
|
||||
return 0, errors.New("注册失败,请稍后重试")
|
||||
}
|
||||
identity.ID = uint64(id)
|
||||
BindRegisterThird(identity.ID, row.OpenID, row.UnionID, row.Provider)
|
||||
|
||||
token := IssueRegisterToken(identity.ID, in.OpenID)
|
||||
return identity, token, nil
|
||||
now := time.Now()
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthRegisterSession)).
|
||||
Filter("id", row.ID).
|
||||
Update(map[string]interface{}{"identity_id": identity.ID, "update_time": now})
|
||||
return identity.ID, nil
|
||||
}
|
||||
|
||||
// isOrphanIdentity 判定「注册未完成」的孤儿身份:从未登录成功、没有任何企业归属、
|
||||
// 也没有待审批的加入/创建企业申请。这类身份占用账号名(或手机号)却无法使用,
|
||||
// 回收后同名账号可正常重新注册。
|
||||
func isOrphanIdentity(m *models.AuthIdentity) bool {
|
||||
if m == nil || m.ID == 0 {
|
||||
return false
|
||||
}
|
||||
if m.LastLoginAt != nil {
|
||||
return false
|
||||
}
|
||||
var tu models.AuthTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
||||
Filter("identity_id", m.ID).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&tu); err == nil {
|
||||
return false
|
||||
}
|
||||
var ta models.AuthTenantApply
|
||||
if err := models.Orm.QueryTable(new(models.AuthTenantApply)).
|
||||
Filter("identity_id", m.ID).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&ta); err == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// discardOrphanIdentity 物理删除孤儿身份及其第三方绑定。
|
||||
// 用物理删而非软删:account/mobile 上有唯一索引,软删行仍占着索引会导致同名无法重新注册。
|
||||
// 调用方必须先用 isOrphanIdentity 确认该身份没有任何关联数据。
|
||||
func discardOrphanIdentity(identityID uint64) {
|
||||
if identityID == 0 {
|
||||
return
|
||||
}
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", identityID).
|
||||
Delete()
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", identityID).
|
||||
Delete()
|
||||
}
|
||||
|
||||
// rollbackRegisterIdentity 令牌过期 / 放弃注册时清理已建但未完成归属的身份,
|
||||
// 防止占用账号名却没有企业归属的「半成品账号」(同名账号可正常重新注册)。
|
||||
func rollbackRegisterIdentity(identityID uint64) {
|
||||
if identityID == 0 {
|
||||
return
|
||||
}
|
||||
var m models.AuthIdentity
|
||||
if err := models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", identityID).One(&m); err != nil {
|
||||
return
|
||||
}
|
||||
// 已产生企业归属或申请(业务已成功)时绝不能删
|
||||
if !isOrphanIdentity(&m) {
|
||||
return
|
||||
}
|
||||
discardOrphanIdentity(identityID)
|
||||
}
|
||||
|
||||
// ---------------- 第二步:企业归属 ----------------
|
||||
@@ -558,17 +744,23 @@ func getPendingApply(applyID uint64, applyType int8, tid uint64) (*models.AuthTe
|
||||
// finishApply CAS 置申请终态,防止并发重复审批
|
||||
func finishApply(apply *models.AuthTenantApply, status int8, reason string, reviewerID uint64, reviewerName string) error {
|
||||
now := time.Now()
|
||||
update := map[string]interface{}{
|
||||
"status": status,
|
||||
"reject_reason": reason,
|
||||
"reviewer_id": reviewerID,
|
||||
"reviewer_name": reviewerName,
|
||||
"review_time": now,
|
||||
"update_time": now,
|
||||
}
|
||||
// 创建企业审批通过时新建的租户必须回写到申请单(否则申请记录里 tid 一直为 0,
|
||||
// 事后无法按 tid 追溯/修数据)
|
||||
if apply.Tid > 0 {
|
||||
update["tid"] = apply.Tid
|
||||
}
|
||||
affected, err := models.Orm.QueryTable(new(models.AuthTenantApply)).
|
||||
Filter("id", apply.ID).
|
||||
Filter("status", models.TenantApplyStatusPending).
|
||||
Update(map[string]interface{}{
|
||||
"status": status,
|
||||
"reject_reason": reason,
|
||||
"reviewer_id": reviewerID,
|
||||
"reviewer_name": reviewerName,
|
||||
"review_time": now,
|
||||
"update_time": now,
|
||||
})
|
||||
Update(update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -622,22 +814,66 @@ func bindIdentityToTenant(identity *models.AuthIdentity, tid uint64, rolePicker
|
||||
Update(map[string]interface{}{"group_id": roleID})
|
||||
}
|
||||
|
||||
if err := authsvc.SyncTenantUser(authsvc.SyncTenantUserInput{
|
||||
Tid: tid,
|
||||
Account: account,
|
||||
Name: name,
|
||||
Phone: applyStrVal(identity.Mobile),
|
||||
Email: applyStrVal(identity.Email),
|
||||
PasswordHash: applyStrVal(identity.PasswordHash),
|
||||
GroupID: groupID,
|
||||
Status: 1,
|
||||
IsDefault: 0,
|
||||
}); err != nil {
|
||||
return errors.New("认证中心同步失败:" + err.Error())
|
||||
if err := upsertAuthTenantBind(identity, tid, account, name, groupID); err != nil {
|
||||
return errors.New("认证中心绑定失败:" + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// upsertAuthTenantBind 按已知身份 ID 直接写认证中心绑定表 yz_auth_tenant_user
|
||||
// (登录时的企业列表读的就是这张表)。
|
||||
//
|
||||
// 不能用 SyncTenantUser 的「账号/手机/邮箱归并键」来匹配:账号注册的身份没有手机号、
|
||||
// 邮箱,企业内账号又是拼出来的 user{id},归并键匹配不到既有身份,会新建一个空身份并
|
||||
// 绑定到新租户,申请人自己的身份反而没有绑定 → 登录后提示「该账号未绑定任何企业」。
|
||||
func upsertAuthTenantBind(identity *models.AuthIdentity, tid uint64, account, name string, groupID uint64) error {
|
||||
if identity == nil || identity.ID == 0 || tid == 0 {
|
||||
return errors.New("参数错误")
|
||||
}
|
||||
now := time.Now()
|
||||
var existed models.AuthTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
||||
Filter("tid", tid).
|
||||
Filter("identity_id", identity.ID).
|
||||
One(&existed); err == nil {
|
||||
_, uerr := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
||||
Filter("id", existed.ID).
|
||||
Update(map[string]interface{}{
|
||||
"account": account,
|
||||
"name": name,
|
||||
"phone": identity.Mobile,
|
||||
"email": identity.Email,
|
||||
"group_id": groupID,
|
||||
"status": 1,
|
||||
"update_time": now,
|
||||
})
|
||||
return uerr
|
||||
}
|
||||
item := &models.AuthTenantUser{
|
||||
Tid: tid,
|
||||
IdentityID: identity.ID,
|
||||
GroupID: groupID,
|
||||
Status: 1,
|
||||
IsDefault: 1, // 申请人是该企业的第一个成员,设为默认企业
|
||||
CreateTime: now,
|
||||
UpdateTime: &now,
|
||||
}
|
||||
if account != "" {
|
||||
item.Account = &account
|
||||
}
|
||||
if name != "" {
|
||||
item.Name = &name
|
||||
}
|
||||
if identity.Mobile != nil {
|
||||
item.Phone = identity.Mobile
|
||||
}
|
||||
if identity.Email != nil {
|
||||
item.Email = identity.Email
|
||||
}
|
||||
_, err := models.Orm.Insert(item)
|
||||
return err
|
||||
}
|
||||
|
||||
// applyStrVal 取指针字符串值(nil 返回空串)
|
||||
func applyStrVal(s *string) string {
|
||||
if s == nil {
|
||||
@@ -674,16 +910,25 @@ func genUniqueTenantCode() (string, error) {
|
||||
return "", errors.New("生成租户编码失败,请稍后重试")
|
||||
}
|
||||
|
||||
// BindRegisterWechat 注册完成后把服务号 openid 绑到新身份(账号注册模式)。
|
||||
// BindRegisterWechat 注册完成后把服务号 openid 绑到新身份(公众号核验模式)。
|
||||
// openid 已绑定其他身份时不抢占,静默跳过。
|
||||
func BindRegisterWechat(identityID uint64, openid string) {
|
||||
BindRegisterThird(identityID, openid, "", models.IdPWechat)
|
||||
}
|
||||
|
||||
// BindRegisterThird 注册完成后把微信身份绑到新身份。
|
||||
// provider 区分公众号(wechat)与小程序(wechat_mini);openid 已绑定时不抢占,静默跳过。
|
||||
func BindRegisterThird(identityID uint64, openid, unionid, provider string) {
|
||||
openid = strings.TrimSpace(openid)
|
||||
if openid == "" || identityID == 0 {
|
||||
return
|
||||
}
|
||||
if provider == "" {
|
||||
provider = models.IdPWechat
|
||||
}
|
||||
var row models.AuthIdentityThird
|
||||
err := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("provider", models.IdPWechat).
|
||||
Filter("provider", provider).
|
||||
Filter("open_id", openid).
|
||||
One(&row)
|
||||
if err == nil {
|
||||
@@ -692,9 +937,13 @@ func BindRegisterWechat(identityID uint64, openid string) {
|
||||
if err != orm.ErrNoRows {
|
||||
return
|
||||
}
|
||||
_, _ = models.Orm.Insert(&models.AuthIdentityThird{
|
||||
item := &models.AuthIdentityThird{
|
||||
IdentityID: identityID,
|
||||
Provider: models.IdPWechat,
|
||||
Provider: provider,
|
||||
OpenID: openid,
|
||||
})
|
||||
}
|
||||
if uid := strings.TrimSpace(unionid); uid != "" {
|
||||
item.UnionID = &uid
|
||||
}
|
||||
_, _ = models.Orm.Insert(item)
|
||||
}
|
||||
Reference in new issue
Block a user