Files
2026-09-20 00:19:08 +08:00

168 lines
4.8 KiB
Go

// Package auth 统一认证中心(UAC)业务层。
//
// 认证中心只负责「你是谁、你能进哪些企业、你能用哪些应用」,
// 菜单/按钮/数据权限仍由各业务端自治。
package auth
import (
"strings"
"github.com/beego/beego/v2/client/orm"
"server/models"
)
// TenantOption 登录后可选择进入的企业(一人可在多家企业任职)
type TenantOption struct {
Tid uint64 `json:"tid"`
TenantName string `json:"tenant_name"`
Name string `json:"name"`
GroupID uint64 `json:"group_id"`
OrgID uint64 `json:"org_id"`
IsDefault bool `json:"is_default"`
}
// IdentityProfile 认证中心视角的身份概要
type IdentityProfile struct {
ID uint64 `json:"id"`
UnionID string `json:"union_id"`
Mobile string `json:"mobile"`
Email string `json:"email"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Status int8 `json:"status"`
MfaOn bool `json:"mfa_enabled"`
Tenants []TenantOption `json:"tenants"`
}
// FindIdentityByAccount 按登录账号查找身份。
// 匹配顺序:手机号 → 邮箱 → 企业内账号(先查绑定再回查身份)。
func FindIdentityByAccount(account string) (*models.AuthIdentity, error) {
account = strings.TrimSpace(account)
if account == "" {
return nil, orm.ErrNoRows
}
base := models.Orm.QueryTable(new(models.AuthIdentity)).Filter("delete_time__isnull", true)
for _, field := range []string{"mobile", "email"} {
m := &models.AuthIdentity{}
if err := base.Filter(field, account).One(m); err == nil {
return m, nil
}
}
// 企业内账号:yz_auth_tenant_user.account → identity_id
var bind models.AuthTenantUser
if err := models.Orm.QueryTable(new(models.AuthTenantUser)).
Filter("account", account).
Filter("delete_time__isnull", true).
OrderBy("-is_default", "id").
One(&bind); err == nil {
m := &models.AuthIdentity{}
if err := models.Orm.QueryTable(new(models.AuthIdentity)).Filter("id", bind.IdentityID).One(m); err == nil {
return m, nil
}
}
return nil, orm.ErrNoRows
}
// ListTenantOptions 列出该身份可登录的企业(过滤企业停用与绑定停用)。
func ListTenantOptions(identityID uint64) ([]TenantOption, error) {
var binds []models.AuthTenantUser
if _, err := models.Orm.QueryTable(new(models.AuthTenantUser)).
Filter("identity_id", identityID).
Filter("status", 1).
Filter("delete_time__isnull", true).
OrderBy("-is_default", "id").
All(&binds); err != nil {
return nil, err
}
out := make([]TenantOption, 0, len(binds))
for _, b := range binds {
var tenant models.SystemTenant
if err := models.Orm.QueryTable(new(models.SystemTenant)).
Filter("id", b.Tid).
Filter("delete_time__isnull", true).
One(&tenant); err != nil {
continue // 租户不存在或已删除
}
if tenant.Status != 1 {
continue // 租户停用
}
opt := TenantOption{
Tid: b.Tid,
TenantName: strings.TrimSpace(tenant.TenantName),
GroupID: b.GroupID,
OrgID: b.OrgID,
IsDefault: b.IsDefault == 1,
}
if b.Name != nil {
opt.Name = strings.TrimSpace(*b.Name)
}
out = append(out, opt)
}
return out, nil
}
// GetTenantUser 获取身份在指定企业的绑定关系
func GetTenantUser(identityID, tid uint64) (*models.AuthTenantUser, error) {
var bind models.AuthTenantUser
err := models.Orm.QueryTable(new(models.AuthTenantUser)).
Filter("identity_id", identityID).
Filter("tid", tid).
Filter("delete_time__isnull", true).
One(&bind)
if err != nil {
return nil, err
}
return &bind, nil
}
// FindIdentityIDByAccount 按「企业 + 账号」查找认证中心身份ID。
// 账号可以是企业内账号、手机号或邮箱(老登录入口按账号登录时使用)。
func FindIdentityIDByAccount(tid uint64, account string) uint64 {
account = strings.TrimSpace(account)
if tid == 0 || account == "" {
return 0
}
base := models.Orm.QueryTable(new(models.AuthTenantUser)).
Filter("tid", tid).
Filter("delete_time__isnull", true)
var bind models.AuthTenantUser
for _, field := range []string{"account", "phone", "email"} {
if err := base.Filter(field, account).One(&bind); err == nil {
return bind.IdentityID
}
}
return 0
}
// BuildProfile 组装身份概要(含可选企业列表)
func BuildProfile(m *models.AuthIdentity) (*IdentityProfile, error) {
tenants, err := ListTenantOptions(m.ID)
if err != nil {
return nil, err
}
p := &IdentityProfile{
ID: m.ID,
UnionID: m.UnionID,
Status: m.Status,
MfaOn: m.MfaEnabled == 1,
Tenants: tenants,
}
if m.Mobile != nil {
p.Mobile = *m.Mobile
}
if m.Email != nil {
p.Email = *m.Email
}
if m.Nickname != nil {
p.Nickname = *m.Nickname
}
if m.Avatar != nil {
p.Avatar = *m.Avatar
}
return p, nil
}