package models import ( "sync" "github.com/beego/beego/v2/client/orm" ) var adminRoleTenantColOnce sync.Once var adminRoleCustomColOnce sync.Once // EnsureAdminRoleTenantColumn 为角色表补齐 tenant_id 列(历史库可能早于租户隔离特性建表)。 // 字段已存在时 MySQL 报 duplicate column,统一忽略,不阻断主流程。 func EnsureAdminRoleTenantColumn() { if Orm == nil { return } adminRoleTenantColOnce.Do(func() { _, _ = Orm.Raw(` ALTER TABLE yz_system_admin_role ADD COLUMN tenant_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '租户ID 0-平台/全局 >0-具体租户' `).Exec() }) } // EnsureAdminRoleCustomColumn 为角色表补齐 is_custom 列(历史库可能早于自定义角色特性建表)。 // 字段已存在时 MySQL 报 duplicate column,统一忽略,不阻断主流程。 func EnsureAdminRoleCustomColumn() { if Orm == nil { return } adminRoleCustomColOnce.Do(func() { _, _ = Orm.Raw(` ALTER TABLE yz_system_admin_role ADD COLUMN is_custom tinyint unsigned NOT NULL DEFAULT 0 COMMENT '自定义角色 0-系统/平台预置 1-租户自建' `).Exec() }) } // EnsureDefaultTenantRoles 保证指定租户(cid=2)的默认角色齐全: // - 租户管理员:全权限(rights 为空,见 permission.go 空权限即放行) // - 部门负责人 // - 员工 // // 默认角色按租户隔离:每个租户首次访问角色列表时补建自己的一套,互不影响。 // 缺失的自动插入;已存在(同名同 cid 同租户)的不动。任何失败均静默忽略不阻断主流程。 func EnsureDefaultTenantRoles(tid uint64) { if Orm == nil || tid == 0 { return } // 确保 tenant_id / is_custom 列存在(兼容老表) EnsureAdminRoleTenantColumn() EnsureAdminRoleCustomColumn() defaults := []string{"租户管理员", "部门负责人", "员工"} for _, name := range defaults { cnt, err := Orm.QueryTable(new(AdminRole)). Filter("cid", 2). Filter("tenant_id", tid). Filter("name", name). Count() if err != nil { continue } if cnt > 0 { continue } role := &AdminRole{ TenantID: tid, Cid: 2, Name: name, Status: 1, Rights: nil, // 空权限 = 全权限(permission.go) } _, _ = Orm.Insert(role) } } // GetTenantAdminRole 返回指定租户(cid=2)的「租户管理员」角色 ID。 // 角色不存在时先调用 EnsureDefaultTenantRoles 补建再查;任何失败返回 (0, err)。 // 普通租户(租户第一个账号)与租户管理员实为同一概念,均绑定此角色以获得 backend 全权限。 func GetTenantAdminRole(tid uint64) (uint64, error) { if Orm == nil || tid == 0 { return 0, orm.ErrNoRows } EnsureDefaultTenantRoles(tid) var role AdminRole if err := Orm.QueryTable(new(AdminRole)). Filter("cid", 2). Filter("tenant_id", tid). Filter("name", "租户管理员"). One(&role); err != nil { return 0, err } return role.ID, nil }