220 lines
5.7 KiB
Go
220 lines
5.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"server/models"
|
|
"server/pkg/passwordutil"
|
|
)
|
|
|
|
// SyncTenantUserInput 老表 → 认证中心同步入参
|
|
type SyncTenantUserInput struct {
|
|
Tid uint64
|
|
Account string // 企业内账号
|
|
Name string
|
|
Phone string
|
|
Email string
|
|
PasswordHash string // 已哈希的密码;为空表示不修改密码
|
|
GroupID uint64
|
|
OrgID uint64
|
|
Status int8
|
|
IsDefault int8
|
|
}
|
|
|
|
// SyncTenantUser 把租户用户同步到统一认证中心(幂等,可重复调用)。
|
|
//
|
|
// 归并规则与迁移脚本一致:手机号 > 邮箱 > 企业内账号。
|
|
// - 身份不存在 → 创建;已存在 → 更新手机/邮箱/昵称/状态
|
|
// - 绑定不存在 → 创建;已存在 → 更新账号/姓名/部门/角色/状态
|
|
//
|
|
// 注意:密码属于身份层(一人一份),因此在任一企业修改密码,
|
|
// 该用户在其他企业的登录密码会同步变化——这是统一认证的预期行为。
|
|
func SyncTenantUser(in SyncTenantUserInput) error {
|
|
identity, err := findOrCreateIdentity(in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return upsertTenantBinding(identity, in)
|
|
}
|
|
|
|
// RemoveTenantUser 删除该用户在指定企业的绑定(保留身份本身,
|
|
// 因为该身份可能还绑定着其他企业)。
|
|
func RemoveTenantUser(tid uint64, account, phone, email string) error {
|
|
identity := findIdentityByKey(account, phone, email)
|
|
if identity == nil {
|
|
return nil // 认证中心无此身份,无需处理
|
|
}
|
|
_, err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
|
Filter("tid", tid).
|
|
Filter("identity_id", identity.ID).
|
|
Delete()
|
|
return err
|
|
}
|
|
|
|
// findOrCreateIdentity 按归并键查找身份,不存在则创建
|
|
func findOrCreateIdentity(in SyncTenantUserInput) (*models.AuthIdentity, error) {
|
|
if m := findIdentityByKey(in.Account, in.Phone, in.Email); m != nil {
|
|
// 补全手机/邮箱/昵称;密码仅在显式传入时更新
|
|
update := map[string]interface{}{}
|
|
if in.Phone != "" {
|
|
update["mobile"] = in.Phone
|
|
}
|
|
if in.Email != "" {
|
|
update["email"] = in.Email
|
|
}
|
|
if in.Name != "" {
|
|
update["nickname"] = in.Name
|
|
}
|
|
if in.Status > 0 {
|
|
update["status"] = in.Status
|
|
}
|
|
if in.PasswordHash != "" {
|
|
update["password_hash"] = in.PasswordHash
|
|
update["password_algo"] = passwordutil.AlgoOf(in.PasswordHash)
|
|
}
|
|
if len(update) > 0 {
|
|
_, _ = models.Orm.QueryTable(new(models.AuthIdentity)).
|
|
Filter("id", m.ID).
|
|
Update(update)
|
|
}
|
|
// 重新读取,返回最新值
|
|
_ = models.Orm.QueryTable(new(models.AuthIdentity)).Filter("id", m.ID).One(m)
|
|
return m, nil
|
|
}
|
|
|
|
item := &models.AuthIdentity{
|
|
UnionID: strings.ReplaceAll(uuid.NewString(), "-", ""),
|
|
PasswordAlgo: passwordutil.AlgoLegacy,
|
|
Status: in.Status,
|
|
}
|
|
if in.Status == 0 {
|
|
item.Status = models.AuthIdentityStatusEnabled
|
|
}
|
|
if in.Phone != "" {
|
|
item.Mobile = &in.Phone
|
|
}
|
|
if in.Email != "" {
|
|
item.Email = &in.Email
|
|
}
|
|
if in.Name != "" {
|
|
item.Nickname = &in.Name
|
|
}
|
|
if in.PasswordHash != "" {
|
|
item.PasswordHash = &in.PasswordHash
|
|
item.PasswordAlgo = passwordutil.AlgoOf(in.PasswordHash)
|
|
}
|
|
id, err := models.Orm.Insert(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
item.ID = uint64(id)
|
|
return item, nil
|
|
}
|
|
|
|
// findIdentityByKey 按 手机 > 邮箱 > 账号 查找身份
|
|
func findIdentityByKey(account, phone, email string) *models.AuthIdentity {
|
|
base := models.Orm.QueryTable(new(models.AuthIdentity)).Filter("delete_time__isnull", true)
|
|
|
|
if v := strings.TrimSpace(phone); v != "" {
|
|
m := &models.AuthIdentity{}
|
|
if err := base.Filter("mobile", v).One(m); err == nil {
|
|
return m
|
|
}
|
|
}
|
|
if v := strings.TrimSpace(email); v != "" {
|
|
m := &models.AuthIdentity{}
|
|
if err := base.Filter("email", v).One(m); err == nil {
|
|
return m
|
|
}
|
|
}
|
|
// 企业内账号:先找绑定关系再回查身份
|
|
if v := strings.TrimSpace(account); v != "" {
|
|
var bind models.AuthTenantUser
|
|
if err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
|
Filter("account", v).
|
|
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
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// upsertTenantBinding 创建或更新身份-企业绑定
|
|
func upsertTenantBinding(identity *models.AuthIdentity, in SyncTenantUserInput) error {
|
|
var existed models.AuthTenantUser
|
|
err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
|
Filter("tid", in.Tid).
|
|
Filter("identity_id", identity.ID).
|
|
One(&existed)
|
|
|
|
if err != nil {
|
|
// 新建绑定
|
|
item := &models.AuthTenantUser{
|
|
Tid: in.Tid,
|
|
IdentityID: identity.ID,
|
|
GroupID: in.GroupID,
|
|
OrgID: in.OrgID,
|
|
Status: in.Status,
|
|
IsDefault: in.IsDefault,
|
|
}
|
|
if in.Status == 0 {
|
|
item.Status = 1
|
|
}
|
|
if in.Account != "" {
|
|
item.Account = &in.Account
|
|
}
|
|
if in.Name != "" {
|
|
item.Name = &in.Name
|
|
}
|
|
if in.Phone != "" {
|
|
item.Phone = &in.Phone
|
|
}
|
|
if in.Email != "" {
|
|
item.Email = &in.Email
|
|
}
|
|
_, err := models.Orm.Insert(item)
|
|
return err
|
|
}
|
|
|
|
update := map[string]interface{}{}
|
|
if in.Account != "" {
|
|
update["account"] = in.Account
|
|
}
|
|
if in.Name != "" {
|
|
update["name"] = in.Name
|
|
}
|
|
if in.Phone != "" {
|
|
update["phone"] = in.Phone
|
|
}
|
|
if in.Email != "" {
|
|
update["email"] = in.Email
|
|
}
|
|
if in.GroupID > 0 {
|
|
update["group_id"] = in.GroupID
|
|
}
|
|
if in.OrgID > 0 {
|
|
update["org_id"] = in.OrgID
|
|
}
|
|
if in.Status > 0 {
|
|
update["status"] = in.Status
|
|
}
|
|
if in.IsDefault >= 0 {
|
|
update["is_default"] = in.IsDefault
|
|
}
|
|
if len(update) == 0 {
|
|
return nil
|
|
}
|
|
_, err = models.Orm.QueryTable(new(models.AuthTenantUser)).
|
|
Filter("id", existed.ID).
|
|
Update(update)
|
|
return err
|
|
}
|