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 }