namespace Admin.NET.Core.Service; /// /// 系统租户管理服务 /// [ApiDescriptionSettings(Order = 390)] public class SysTenantService : IDynamicApiController, ITransient { private readonly SqlSugarRepository _sysTenantRep; private readonly SqlSugarRepository _sysOrgRep; private readonly SqlSugarRepository _sysRoleRep; private readonly SqlSugarRepository _sysPosRep; private readonly SqlSugarRepository _sysUserRep; private readonly SqlSugarRepository _sysUserExtOrgRep; private readonly SqlSugarRepository _sysRoleMenuRep; private readonly SqlSugarRepository _userRoleRep; private readonly SysUserRoleService _sysUserRoleService; private readonly SysRoleMenuService _sysRoleMenuService; private readonly SysConfigService _sysConfigService; private readonly SysCacheService _sysCacheService; public SysTenantService(SqlSugarRepository sysTenantRep, SqlSugarRepository sysOrgRep, SqlSugarRepository sysRoleRep, SqlSugarRepository sysPosRep, SqlSugarRepository sysUserRep, SqlSugarRepository sysUserExtOrgRep, SqlSugarRepository sysRoleMenuRep, SqlSugarRepository userRoleRep, SysUserRoleService sysUserRoleService, SysRoleMenuService sysRoleMenuService, SysConfigService sysConfigService, SysCacheService sysCacheService) { _sysTenantRep = sysTenantRep; _sysOrgRep = sysOrgRep; _sysRoleRep = sysRoleRep; _sysPosRep = sysPosRep; _sysUserRep = sysUserRep; _sysUserExtOrgRep = sysUserExtOrgRep; _sysRoleMenuRep = sysRoleMenuRep; _userRoleRep = userRoleRep; _sysUserRoleService = sysUserRoleService; _sysRoleMenuService = sysRoleMenuService; _sysConfigService = sysConfigService; _sysCacheService = sysCacheService; } /// /// 获取租户分页列表 /// /// /// [DisplayName("获取租户分页列表")] public async Task> Page(PageTenantInput input) { return await _sysTenantRep.AsQueryable() .LeftJoin((u, a) => u.UserId == a.Id) .LeftJoin((u, a, b) => u.OrgId == b.Id) .WhereIF(!string.IsNullOrWhiteSpace(input.Phone), (u, a) => a.Phone.Contains(input.Phone.Trim())) .WhereIF(!string.IsNullOrWhiteSpace(input.Name), (u, a, b) => b.Name.Contains(input.Name.Trim())) .OrderBy(u => u.OrderNo) .Select((u, a, b) => new TenantOutput { Id = u.Id, OrgId = b.Id, Name = b.Name, UserId = a.Id, AdminAccount = a.Account, Phone = a.Phone, Email = a.Email, TenantType = u.TenantType, DbType = u.DbType, Connection = u.Connection, ConfigId = u.ConfigId, OrderNo = u.OrderNo, Remark = u.Remark, Status = u.Status, }) .ToPagedListAsync(input.Page, input.PageSize); } /// /// 获取库隔离的租户列表 /// /// [NonAction] public async Task> GetTenantDbList() { return await _sysTenantRep.GetListAsync(u => u.TenantType == TenantTypeEnum.Db && u.Status == StatusEnum.Enable); } /// /// 增加租户 /// /// /// [UnitOfWork] [ApiDescriptionSettings(Name = "Add"), HttpPost] [DisplayName("增加租户")] public async Task AddTenant(AddTenantInput input) { var isExist = await _sysOrgRep.IsAnyAsync(u => u.Name == input.Name); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1300); isExist = await _sysUserRep.AsQueryable().ClearFilter().AnyAsync(u => u.Account == input.AdminAccount); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1301); // 从库配置判断 if (!string.IsNullOrWhiteSpace(input.SlaveConnections) && !JSON.IsValid(input.SlaveConnections)) throw Oops.Oh(ErrorCodeEnum.D1302); // ID隔离时设置与主库一致 if (input.TenantType == TenantTypeEnum.Id) { var config = _sysTenantRep.AsSugarClient().CurrentConnectionConfig; input.DbType = config.DbType; input.Connection = config.ConnectionString; } var tenant = input.Adapt(); await _sysTenantRep.InsertAsync(tenant); await InitNewTenant(tenant); await CacheTenant(); } /// /// 设置租户状态 /// /// /// [DisplayName("设置租户状态")] public async Task SetStatus(TenantInput input) { var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == input.Id); if (tenant == null || tenant.ConfigId == SqlSugarConst.MainConfigId) throw Oops.Oh(ErrorCodeEnum.Z1001); if (!Enum.IsDefined(typeof(StatusEnum), input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005); tenant.Status = input.Status; return await _sysTenantRep.AsUpdateable(tenant).UpdateColumns(u => new { u.Status }).ExecuteCommandAsync(); } /// /// 新增租户初始化 /// /// private async Task InitNewTenant(TenantOutput tenant) { var tenantId = tenant.Id; var tenantName = tenant.Name; // 初始化机构 var newOrg = new SysOrg { TenantId = tenantId, Pid = 0, Name = tenantName, Code = tenantName, Remark = tenantName, }; await _sysOrgRep.InsertAsync(newOrg); // 初始化角色 var newRole = new SysRole { TenantId = tenantId, Name = "租管-" + tenantName, Code = CommonConst.SysAdminRole, DataScope = DataScopeEnum.All, Remark = tenantName }; await _sysRoleRep.InsertAsync(newRole); // 初始化职位 var newPos = new SysPos { TenantId = tenantId, Name = "租管-" + tenantName, Code = tenantName, Remark = tenantName, }; await _sysPosRep.InsertAsync(newPos); // 初始化系统账号 var password = await _sysConfigService.GetConfigValue(CommonConst.SysPassword); var newUser = new SysUser { TenantId = tenantId, Account = tenant.AdminAccount, Password = CryptogramUtil.Encrypt(password), NickName = "租管", Email = tenant.Email, Phone = tenant.Phone, AccountType = AccountTypeEnum.SysAdmin, OrgId = newOrg.Id, PosId = newPos.Id, Birthday = DateTime.Parse("2000-01-01"), RealName = "租管", Remark = "租管" + tenantName, }; await _sysUserRep.InsertAsync(newUser); // 关联用户及角色 var newUserRole = new SysUserRole { RoleId = newRole.Id, UserId = newUser.Id }; await _userRoleRep.InsertAsync(newUserRole); // 关联租户组织机构和管理员用户 await _sysTenantRep.UpdateAsync(u => new SysTenant() { UserId = newUser.Id, OrgId = newOrg.Id }, u => u.Id == tenantId); // 默认租户管理员角色菜单集合 var menuIdList = new List { 1300000000111,1300000000121, // 工作台 1310000000111,1310000000112,1310000000113,1310000000114,1310000000115,1310000000116,1310000000117,1310000000118,1310000000119,1310000000120, // 账号 1310000000131,1310000000132,1310000000133,1310000000134,1310000000135,1310000000136,1310000000137,1310000000138, // 角色 1310000000141,1310000000142,1310000000143,1310000000144,1310000000145, // 机构 1310000000151,1310000000152,1310000000153,1310000000154,1310000000155, // 职位 1310000000161,1310000000162,1310000000163,1310000000164, // 个人中心 1310000000171,1310000000172,1310000000173,1310000000174,1310000000175,1310000000176,1310000000177 // 通知公告 }; await _sysRoleMenuService.GrantRoleMenu(new RoleMenuInput() { Id = newRole.Id, MenuIdList = menuIdList }); } /// /// 删除租户 /// /// /// [ApiDescriptionSettings(Name = "Delete"), HttpPost] [DisplayName("删除租户")] public async Task DeleteTenant(DeleteTenantInput input) { // 禁止删除默认租户 if (input.Id.ToString() == SqlSugarConst.MainConfigId) throw Oops.Oh(ErrorCodeEnum.D1023); await _sysTenantRep.DeleteAsync(u => u.Id == input.Id); await CacheTenant(input.Id); // 删除与租户相关的表数据 var users = await _sysUserRep.AsQueryable().ClearFilter().Where(u => u.TenantId == input.Id).ToListAsync(); var userIds = users.Select(u => u.Id).ToList(); await _sysUserRep.AsDeleteable().Where(u => userIds.Contains(u.Id)).ExecuteCommandAsync(); await _userRoleRep.AsDeleteable().Where(u => userIds.Contains(u.UserId)).ExecuteCommandAsync(); await _sysUserExtOrgRep.AsDeleteable().Where(u => userIds.Contains(u.UserId)).ExecuteCommandAsync(); await _sysRoleRep.AsDeleteable().Where(u => u.TenantId == input.Id).ExecuteCommandAsync(); var roleIds = await _sysRoleRep.AsQueryable().ClearFilter() .Where(u => u.TenantId == input.Id).Select(u => u.Id).ToListAsync(); await _sysRoleMenuRep.AsDeleteable().Where(u => roleIds.Contains(u.RoleId)).ExecuteCommandAsync(); await _sysOrgRep.AsDeleteable().Where(u => u.TenantId == input.Id).ExecuteCommandAsync(); await _sysPosRep.AsDeleteable().Where(u => u.TenantId == input.Id).ExecuteCommandAsync(); } /// /// 更新租户 /// /// /// [ApiDescriptionSettings(Name = "Update"), HttpPost] [DisplayName("更新租户")] public async Task UpdateTenant(UpdateTenantInput input) { var isExist = await _sysOrgRep.IsAnyAsync(u => u.Name == input.Name && u.Id != input.OrgId); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1300); isExist = await _sysUserRep.IsAnyAsync(u => u.Account == input.AdminAccount && u.Id != input.UserId); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1301); // 从库配置判断 if (!string.IsNullOrWhiteSpace(input.SlaveConnections) && !JSON.IsValid(input.SlaveConnections)) throw Oops.Oh(ErrorCodeEnum.D1302); await _sysTenantRep.AsUpdateable(input.Adapt()).IgnoreColumns(true).ExecuteCommandAsync(); // 更新系统机构 await _sysOrgRep.UpdateAsync(u => new SysOrg() { Name = input.Name }, u => u.Id == input.OrgId); // 更新系统用户 await _sysUserRep.UpdateAsync(u => new SysUser() { Account = input.AdminAccount, Phone = input.Phone, Email = input.Email }, u => u.Id == input.UserId); await CacheTenant(input.Id); } /// /// 授权租户管理员角色菜单 /// /// /// [UnitOfWork] [DisplayName("授权租户管理员角色菜单")] public async Task GrantMenu(RoleMenuInput input) { var tenantAdminUser = await _sysUserRep.GetFirstAsync(u => u.TenantId == input.Id && u.AccountType == AccountTypeEnum.SysAdmin); if (tenantAdminUser == null) return; var roleIds = await _sysUserRoleService.GetUserRoleIdList(tenantAdminUser.Id); input.Id = roleIds[0]; // 重置租户管理员角色Id await _sysRoleMenuService.GrantRoleMenu(input); } /// /// 获取租户管理员角色拥有菜单Id集合 /// /// /// [DisplayName("获取租户管理员角色拥有菜单Id集合")] public async Task> GetOwnMenuList([FromQuery] TenantUserInput input) { var roleIds = await _sysUserRoleService.GetUserRoleIdList(input.UserId); return await _sysRoleMenuService.GetRoleMenuIdList(new List { roleIds[0] }); } /// /// 重置租户管理员密码 /// /// /// [DisplayName("重置租户管理员密码")] public async Task ResetPwd(TenantUserInput input) { var password = await _sysConfigService.GetConfigValue(CommonConst.SysPassword); var encryptPassword = CryptogramUtil.Encrypt(password); await _sysUserRep.UpdateAsync(u => new SysUser() { Password = encryptPassword }, u => u.Id == input.UserId); return password; } /// /// 缓存所有租户 /// /// /// [NonAction] public async Task CacheTenant(long tenantId = 0) { // 移除 ISqlSugarClient 中的库连接并排除默认主库 if (tenantId > 0 && tenantId.ToString() != SqlSugarConst.MainConfigId) _sysTenantRep.AsTenant().RemoveConnection(tenantId); var tenantList = await _sysTenantRep.GetListAsync(); // 对租户库连接进行SM2加密 foreach (var tenant in tenantList) { tenant.Connection = CryptogramUtil.SM2Encrypt(tenant.Connection); } _sysCacheService.Set(CacheConst.KeyTenant, tenantList); } /// /// 创建租户数据库 /// /// /// [ApiDescriptionSettings(Name = "CreateDb"), HttpPost] [DisplayName("创建租户数据库")] public async Task CreateDb(TenantInput input) { var tenant = await _sysTenantRep.GetSingleAsync(u => u.Id == input.Id); if (tenant == null) return; if (tenant.DbType == SqlSugar.DbType.Oracle) throw Oops.Oh(ErrorCodeEnum.Z1002); if (string.IsNullOrWhiteSpace(tenant.Connection) || tenant.Connection.Length < 10) throw Oops.Oh(ErrorCodeEnum.Z1004); // 默认数据库配置 var defaultConfig = App.GetOptions().ConnectionConfigs.FirstOrDefault(); var config = new DbConnectionConfig { ConfigId = tenant.Id.ToString(), DbType = tenant.DbType, ConnectionString = tenant.Connection, DbSettings = new DbSettings() { EnableInitDb = true, EnableDiffLog = false, EnableUnderLine = defaultConfig.DbSettings.EnableUnderLine, } }; SqlSugarSetup.InitTenantDatabase(App.GetRequiredService().AsTenant(), config); } /// /// 获取租户下的用户列表 /// /// /// [DisplayName("获取租户下的用户列表")] public async Task> UserList(TenantIdInput input) { return await _sysUserRep.AsQueryable().ClearFilter().Where(u => u.TenantId == input.TenantId).ToListAsync(); } /// /// 获取租户数据库连接 /// /// [NonAction] public SqlSugarScopeProvider GetTenantDbConnectionScope(long tenantId) { var iTenant = _sysTenantRep.AsTenant(); // 若已存在租户库连接,则直接返回 if (iTenant.IsAnyConnection(tenantId.ToString())) return iTenant.GetConnectionScope(tenantId.ToString()); lock (iTenant) { // 从缓存里面获取租户信息 var tenant = _sysCacheService.Get>(CacheConst.KeyTenant)?.First(u => u.Id == tenantId); if (tenant == null) return null; // 获取默认库连接配置 var dbOptions = App.GetOptions(); var mainConnConfig = dbOptions.ConnectionConfigs.First(u => u.ConfigId.ToString() == SqlSugarConst.MainConfigId); // 设置租户库连接配置 var tenantConnConfig = new DbConnectionConfig { ConfigId = tenant.Id.ToString(), DbType = tenant.DbType, IsAutoCloseConnection = true, ConnectionString = CryptogramUtil.SM2Decrypt(tenant.Connection), // 对租户库连接进行SM2解密 DbSettings = new DbSettings() { EnableUnderLine = mainConnConfig.DbSettings.EnableUnderLine, }, SlaveConnectionConfigs = JSON.IsValid(tenant.SlaveConnections) ? JSON.Deserialize>(tenant.SlaveConnections) : null // 从库连接配置 }; iTenant.AddConnection(tenantConnConfig); var sqlSugarScopeProvider = iTenant.GetConnectionScope(tenantId.ToString()); SqlSugarSetup.SetDbConfig(tenantConnConfig); SqlSugarSetup.SetDbAop(sqlSugarScopeProvider, dbOptions.EnableConsoleSql); return sqlSugarScopeProvider; } } }