mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-26 13:25:18 +08:00
924 lines
39 KiB
C#
924 lines
39 KiB
C#
//------------------------------------------------------------------------------
|
||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||
// 使用文档:https://thingsgateway.cn/
|
||
// QQ群:605534569
|
||
//------------------------------------------------------------------------------
|
||
|
||
using BootstrapBlazor.Components;
|
||
|
||
using Mapster;
|
||
|
||
using ThingsGateway.DataEncryption;
|
||
using ThingsGateway.Extension;
|
||
using ThingsGateway.Extension.Generic;
|
||
using ThingsGateway.FriendlyException;
|
||
using ThingsGateway.NewLife.Json.Extension;
|
||
using ThingsGateway.SqlSugar;
|
||
|
||
namespace ThingsGateway.Admin.Application;
|
||
|
||
internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||
{
|
||
private readonly IRelationService _relationService;
|
||
private readonly ISysResourceService _sysResourceService;
|
||
private readonly ISysRoleService _roleService;
|
||
private readonly ISysDictService _configService;
|
||
private readonly ISysPositionService _sysPositionService;
|
||
private readonly ISysOrgService _sysOrgService;
|
||
private readonly IVerificatInfoService _verificatInfoService;
|
||
|
||
public SysUserService(
|
||
IVerificatInfoService verificatInfoService,
|
||
IRelationService relationService,
|
||
ISysPositionService sysPositionService,
|
||
ISysOrgService sysOrgService,
|
||
ISysResourceService sysResourceService,
|
||
ISysRoleService roleService,
|
||
ISysDictService configService)
|
||
{
|
||
_sysOrgService = sysOrgService;
|
||
_sysPositionService = sysPositionService;
|
||
_relationService = relationService;
|
||
_sysResourceService = sysResourceService;
|
||
_roleService = roleService;
|
||
_configService = configService;
|
||
_verificatInfoService = verificatInfoService;
|
||
}
|
||
|
||
|
||
#region 数据范围相关
|
||
|
||
/// <inheritdoc/>
|
||
public async Task<HashSet<long>?> GetCurrentUserDataScopeAsync()
|
||
{
|
||
if (UserManager.SuperAdmin || UserManager.UserId == 0)
|
||
return null;
|
||
var userInfo = await GetUserByIdAsync(UserManager.UserId).ConfigureAwait(false);//获取用户信息
|
||
var roles = await _roleService.GetRoleListByUserIdAsync(UserManager.UserId).ConfigureAwait(false);
|
||
if (roles.Any(a => a.DefaultDataScope.ScopeCategory == DataScopeEnum.SCOPE_ALL))
|
||
{
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
var scopeDefineOrgIdList = roles.Where(a => a.DefaultDataScope.ScopeCategory == DataScopeEnum.SCOPE_ORG_DEFINE).SelectMany(a => a.DefaultDataScope.ScopeDefineOrgIdList);
|
||
|
||
HashSet<long> orgChilds = new();
|
||
HashSet<long> orgs = new();
|
||
if (roles.Any(a => a.DefaultDataScope.ScopeCategory == DataScopeEnum.SCOPE_ORG_CHILD))
|
||
{
|
||
orgChilds = userInfo.ScopeOrgChildList;
|
||
}
|
||
if (roles.Any(a => a.DefaultDataScope.ScopeCategory == DataScopeEnum.SCOPE_ORG_CHILD))
|
||
{
|
||
orgs = new HashSet<long>() { userInfo.OrgId };
|
||
}
|
||
return scopeDefineOrgIdList.Concat(orgChilds).Concat(orgs).ToHashSet();
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public async Task<bool> CheckApiDataScopeAsync(long? orgId, long createUerId, bool throwEnable = true)
|
||
{
|
||
var hasPermission = true;
|
||
//判断数据范围
|
||
var dataScope = await GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||
if (dataScope is { Count: > 0 })//如果有机构
|
||
{
|
||
if (orgId == null || !dataScope.Contains(orgId.Value))//判断机构id是否在数据范围
|
||
hasPermission = false;
|
||
}
|
||
else if (dataScope is { Count: 0 })// 表示仅自己
|
||
{
|
||
if (createUerId != 0 && createUerId != UserManager.UserId)
|
||
hasPermission = false;//机构的创建人不是自己则报错
|
||
}
|
||
if (!hasPermission && throwEnable)
|
||
{
|
||
throw Oops.Bah(App.CreateLocalizerByType(typeof(ThingsGateway.Admin.Application.OperDescAttribute))["NoPermission"]);
|
||
}
|
||
return hasPermission;
|
||
}
|
||
|
||
|
||
public async Task<bool> CheckApiDataScopeAsync(IEnumerable<long> orgIds, IEnumerable<long> createUerIds, bool throwEnable = true)
|
||
{
|
||
var hasPermission = true;
|
||
//判断数据范围
|
||
var dataScope = await GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||
if (dataScope is { Count: > 0 })//如果有机构
|
||
{
|
||
if (orgIds == null || !dataScope.ContainsAll(orgIds))//判断机构id列表是否全在数据范围
|
||
hasPermission = false;
|
||
}
|
||
else if (dataScope is { Count: 0 })// 表示仅自己
|
||
{
|
||
if (createUerIds.Any(it => it != 0 && it != UserManager.UserId))//如果创建者id里有任何不是自己创建的机构
|
||
hasPermission = false;
|
||
}
|
||
if (!hasPermission && throwEnable)
|
||
{
|
||
throw Oops.Bah(App.CreateLocalizerByType(typeof(ThingsGateway.Admin.Application.OperDescAttribute))["NoPermission"]);
|
||
}
|
||
return hasPermission;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 查询
|
||
|
||
|
||
/// <inheritdoc/>
|
||
public async Task<SysUser?> GetUserByAccountAsync(string account, long? tenantId)
|
||
{
|
||
var userId = await GetIdByAccountAsync(account, tenantId).ConfigureAwait(false);//获取用户ID
|
||
if (userId > 0)
|
||
{
|
||
var sysUser = await GetUserByIdAsync(userId).ConfigureAwait(false);//获取用户信息
|
||
if (sysUser?.Account == account)
|
||
return sysUser;
|
||
else
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据用户id获取用户,不存在返回null
|
||
/// </summary>
|
||
/// <param name="userId">用户id</param>
|
||
/// <returns>用户</returns>
|
||
public async Task<SysUser?> GetUserByIdAsync(long userId)
|
||
{
|
||
//先从Cache拿
|
||
var sysUser = App.CacheService.HashGetOne<SysUser>(CacheConst.Cache_SysUser, userId.ToString());
|
||
sysUser ??= await GetUserFromDbAsync(userId).ConfigureAwait(false);//从数据库拿用户信息
|
||
return sysUser;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据账号获取用户id
|
||
/// </summary>
|
||
/// <param name="account">账号</param>
|
||
/// <param name="tenantId">租户id</param>
|
||
/// <returns>用户id</returns>
|
||
public async Task<long> GetIdByAccountAsync(string account, long? tenantId)
|
||
{
|
||
var key = CacheConst.Cache_SysUserAccount;
|
||
var orgIds = new HashSet<long>();
|
||
if (tenantId > 0)
|
||
{
|
||
key += $":{tenantId}";
|
||
orgIds = await _sysOrgService.GetOrgChildIdsAsync(tenantId.Value).ConfigureAwait(false);//获取下级机构
|
||
}
|
||
//先从Cache拿
|
||
var userId = App.CacheService.HashGetOne<long>(key, account);
|
||
if (userId == 0)
|
||
{
|
||
|
||
//单查获取用户账号对应ID
|
||
using var db = GetDB();
|
||
userId = await db.Queryable<SysUser>()
|
||
.Where(it => it.Account == account)
|
||
.WhereIF(orgIds.Count > 0, it => orgIds.Contains(it.OrgId))
|
||
.Select(it => it.Id).FirstAsync().ConfigureAwait(false);
|
||
if (userId != 0)
|
||
{
|
||
//插入Cache
|
||
App.CacheService.HashAdd(key, account, userId);
|
||
}
|
||
}
|
||
return userId;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户拥有的按钮编码
|
||
/// </summary>
|
||
/// <param name="userId">用户id</param>
|
||
/// <returns>按钮编码</returns>
|
||
public async Task<Dictionary<string, List<string>>> GetButtonCodeListAsync(long userId)
|
||
{
|
||
//获取用户资源集合
|
||
var resourceList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, RelationCategoryEnum.UserHasResource).ConfigureAwait(false);
|
||
if (!resourceList.Any())//如果有表示用户单独授权了不走用户角色
|
||
{
|
||
//获取用户角色关系集合
|
||
var roleList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, RelationCategoryEnum.UserHasRole).ConfigureAwait(false);
|
||
var roleIdList = roleList.Select(x => x.TargetId.ToLong());//角色ID列表
|
||
if (roleIdList.Any())//如果该用户有角色
|
||
{
|
||
resourceList = await _relationService.GetRelationListByObjectIdListAndCategoryAsync(roleIdList,
|
||
RelationCategoryEnum.RoleHasResource).ConfigureAwait(false);//获取资源集合
|
||
}
|
||
}
|
||
var relationResourcePermissions = resourceList.Select(it => it.ExtJson?.FromJsonNetString<RelationResourcePermission>());
|
||
var allResources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
|
||
|
||
var ids = relationResourcePermissions.Select(a => a.MenuId);
|
||
var menus = allResources.Where(it => it.Category == ResourceCategoryEnum.Menu && ids.Contains(it.Id)).ToDictionary(a => a, a => relationResourcePermissions.FirstOrDefault(b => b.MenuId == a.Id));
|
||
|
||
Dictionary<string, List<string>> buttonCodeList = new();
|
||
foreach (var item in menus)
|
||
{
|
||
if (buttonCodeList.TryGetValue(item.Key.Href, out var buttonCode))
|
||
{
|
||
var buttonS = allResources.Where(a => item.Value.ButtonIds.Contains(a.Id));
|
||
buttonCode.AddRange(buttonS.Select(a => a.Title));
|
||
}
|
||
else
|
||
{
|
||
var buttonS = allResources.Where(a => item.Value.ButtonIds.Contains(a.Id));
|
||
buttonCodeList.Add(item.Key.Href, buttonS.Select(a => a.Title).ToList());
|
||
}
|
||
}
|
||
|
||
var firstbuttons = allResources.Where(it => it.Category == ResourceCategoryEnum.Button && relationResourcePermissions.FirstOrDefault(a => a.MenuId == 0)?.ButtonIds?.Contains(it.Id) == true);
|
||
buttonCodeList.Add(string.Empty, firstbuttons?.Select(a => a.Title).ToList());
|
||
|
||
return buttonCodeList!;
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public async Task<IEnumerable<DataScope>> GetPermissionListByUserIdAsync(long userId)
|
||
{
|
||
List<DataScope>? permissions = new();
|
||
|
||
#region Razor页面权限
|
||
|
||
{
|
||
var sysRelations =
|
||
await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, RelationCategoryEnum.UserHasPermission).ConfigureAwait(false);//根据用户ID获取用户权限
|
||
if (!sysRelations.Any())//如果有表示用户单独授权了不走用户角色
|
||
{
|
||
var roleIdList =
|
||
await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, RelationCategoryEnum.UserHasRole).ConfigureAwait(false);//根据用户ID获取角色ID
|
||
if (roleIdList.Any())//如果角色ID不为空
|
||
{
|
||
//获取角色权限信息
|
||
sysRelations = await _relationService.GetRelationListByObjectIdListAndCategoryAsync(roleIdList.Select(it => it.TargetId.ToLong()),
|
||
RelationCategoryEnum.RoleHasPermission).ConfigureAwait(false);
|
||
}
|
||
}
|
||
var relationGroup = sysRelations.GroupBy(it => it.TargetId);//根据目标ID,也就是接口名分组,因为存在一个用户多个角色
|
||
|
||
//遍历分组
|
||
foreach (var it in relationGroup)
|
||
{
|
||
permissions.Add(new DataScope
|
||
{
|
||
ApiUrl = it.Key,
|
||
});
|
||
}
|
||
|
||
}
|
||
|
||
#endregion Razor页面权限
|
||
|
||
#region API权限
|
||
|
||
{
|
||
var apiRelations =
|
||
await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, RelationCategoryEnum.UserHasOpenApiPermission).ConfigureAwait(false);//根据用户ID获取用户权限
|
||
if (!apiRelations.Any())//如果有表示用户单独授权了不走用户角色
|
||
{
|
||
var roleIdList =
|
||
await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, RelationCategoryEnum.UserHasRole).ConfigureAwait(false);//根据用户ID获取角色ID
|
||
if (roleIdList.Any())//如果角色ID不为空
|
||
{
|
||
//获取角色权限信息
|
||
apiRelations = await _relationService.GetRelationListByObjectIdListAndCategoryAsync(roleIdList.Select(it => it.TargetId.ToLong()),
|
||
RelationCategoryEnum.RoleHasOpenApiPermission).ConfigureAwait(false);
|
||
}
|
||
}
|
||
var relationGroup = apiRelations.GroupBy(it => it.TargetId);//根据目标ID,也就是接口名分组,因为存在一个用户多个角色
|
||
|
||
//遍历分组
|
||
foreach (var it in relationGroup)
|
||
{
|
||
permissions.Add(new DataScope
|
||
{
|
||
ApiUrl = it.Key,
|
||
});
|
||
}
|
||
|
||
}
|
||
|
||
#endregion API权限
|
||
|
||
return permissions;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 表格查询
|
||
/// </summary>
|
||
/// <param name="option">查询条件</param>
|
||
/// <param name="input">查询条件</param>
|
||
public async Task<QueryData<SysUser>> PageAsync(QueryPageOptions option, UserSelectorInput input)
|
||
{
|
||
if (input != null)
|
||
{
|
||
option.SortName = "u." + option.SortName;
|
||
var orgIds = await _sysOrgService.GetOrgChildIdsAsync(input.OrgId).ConfigureAwait(false);//获取下级机构
|
||
var dataScope = await GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||
|
||
return await QueryAsync(option, query =>
|
||
query.WhereIF(!option.SearchText.IsNullOrWhiteSpace(), a => a.Account.Contains(option.SearchText))
|
||
.WhereIF(input.OrgId > 0, u => orgIds.Contains(u.OrgId))//指定机构
|
||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.OrgId))//在指定机构列表查询
|
||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||
.WhereIF(input.PositionId > 0, u => u.PositionId == input.PositionId)//指定职位
|
||
|
||
.WhereIF(input.RoleId > 0,
|
||
u => SqlFunc.Subqueryable<SysRelation>()
|
||
.Where(r => r.TargetId == input.RoleId.ToString() && r.ObjectId == u.Id && r.Category == RelationCategoryEnum.UserHasRole)
|
||
.Any())//指定角色
|
||
|
||
.LeftJoin<SysOrg>((u, o) => u.OrgId == o.Id).LeftJoin<SysPosition>((u, o, p) => u.PositionId == p.Id)
|
||
.Select((u, o, p) => new SysUser
|
||
{
|
||
Id = u.Id.SelectAll(),
|
||
OrgName = o.Name,
|
||
PositionName = p.Name,
|
||
OrgNames = o.Names
|
||
}
|
||
)
|
||
.Mapper(u =>
|
||
{
|
||
#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
|
||
u.Password = null;//密码清空
|
||
u.Phone = DESEncryption.Decrypt(u.Phone);//解密手机号
|
||
#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
|
||
})).ConfigureAwait(false);
|
||
|
||
}
|
||
else
|
||
{
|
||
return await QueryAsync(option, query =>
|
||
query.WhereIF(!option.SearchText.IsNullOrWhiteSpace(), a => a.Account.Contains(option.SearchText)).Mapper(u =>
|
||
{
|
||
#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
|
||
u.Password = null;//密码清空
|
||
u.Phone = DESEncryption.Decrypt(u.Phone);//解密手机号
|
||
#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
|
||
})).ConfigureAwait(false);
|
||
}
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户拥有的角色
|
||
/// </summary>
|
||
/// <param name="id">用户id</param>
|
||
/// <returns>角色id列表</returns>
|
||
public async Task<IEnumerable<long>> OwnRoleAsync(long id)
|
||
{
|
||
var relations = await _relationService.GetRelationListByObjectIdAndCategoryAsync(id, RelationCategoryEnum.UserHasRole).ConfigureAwait(false);
|
||
return relations.Select(it => it.TargetId.ToLong());
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户拥有的资源
|
||
/// </summary>
|
||
/// <param name="id">用户id</param>
|
||
public async Task<GrantResourceData> OwnResourceAsync(long id)
|
||
{
|
||
return await _roleService.OwnResourceAsync(id, RelationCategoryEnum.UserHasResource).ConfigureAwait(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据用户id获取用户列表
|
||
/// </summary>
|
||
/// <param name="input">用户id列表</param>
|
||
/// <returns>用户列表</returns>
|
||
public async Task<List<UserSelectorOutput>> GetUserListByIdListAsync(IEnumerable<long> input)
|
||
{
|
||
using var db = GetDB();
|
||
var userList = await db.Queryable<SysUser>().Where(it => input.Contains(it.Id)).Select<UserSelectorOutput>().ToListAsync().ConfigureAwait(false);
|
||
return userList;
|
||
}
|
||
|
||
#endregion 查询
|
||
|
||
#region OPENAPI
|
||
|
||
/// <summary>
|
||
/// 获取用户拥有的OpenApi权限
|
||
/// </summary>
|
||
/// <param name="id">用户id</param>
|
||
public async Task<GrantPermissionData> ApiOwnPermissionAsync(long id)
|
||
{
|
||
var roleOwnPermission = new GrantPermissionData { Id = id };//定义结果集
|
||
//获取关系列表
|
||
var relations = await _relationService.GetRelationListByObjectIdAndCategoryAsync(id, RelationCategoryEnum.UserHasOpenApiPermission).ConfigureAwait(false);
|
||
roleOwnPermission.GrantInfoList = relations.Select(it => it.ExtJson?.FromJsonNetString<RelationPermission>()!).Where(a => a != null);
|
||
return roleOwnPermission;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
[OperDesc("UserGrantApiPermission")]
|
||
public async Task GrantApiPermissionAsync(GrantPermissionData input)
|
||
{
|
||
var sysUser = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户
|
||
|
||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||
if (sysUser != null)
|
||
{
|
||
await _relationService.SaveRelationBatchAsync(RelationCategoryEnum.UserHasOpenApiPermission, input.Id,
|
||
input.GrantInfoList.Select(a => (a.ApiUrl, a.ToSystemTextJsonString())),
|
||
true).ConfigureAwait(false);//添加到数据库
|
||
DeleteUserFromCache(input.Id);
|
||
}
|
||
}
|
||
|
||
#endregion OPENAPI
|
||
|
||
#region 新增
|
||
|
||
/// <inheritdoc/>
|
||
[OperDesc("SaveUser", isRecordPar: false)]
|
||
public async Task<bool> SaveUserAsync(SysUser input, ItemChangedType changedType)
|
||
{
|
||
await CheckInput(input).ConfigureAwait(false);//检查参数
|
||
|
||
if (changedType == ItemChangedType.Add)
|
||
{
|
||
var sysUser = input.Adapt<SysUser>();
|
||
//获取默认密码
|
||
sysUser.Avatar = input.Avatar;
|
||
sysUser.Password = await GetDefaultPassWord(true).ConfigureAwait(false);//设置密码
|
||
sysUser.Status = true;//默认状态
|
||
return await SaveAsync(sysUser, changedType).ConfigureAwait(false);//添加数据
|
||
}
|
||
else
|
||
{
|
||
await CheckApiDataScopeAsync(input.OrgId, input.CreateUserId).ConfigureAwait(false);
|
||
var exist = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户信息
|
||
if (exist != null)
|
||
{
|
||
var isSuperAdmin = exist.Id == RoleConst.SuperAdminId;//判断是否有超管
|
||
if (isSuperAdmin && !UserManager.SuperAdmin)
|
||
throw Oops.Bah(Localizer["CanotEditAdminUser"]);
|
||
|
||
if (input.Status != exist.Status)
|
||
CheckSelf(input.Id, input.Status ? Localizer["Enable"] : Localizer["Disable"]);//判断是不是自己
|
||
|
||
var sysUser = input;//实体转换
|
||
using var db = GetDB();
|
||
var result = await db.Updateable(sysUser).IgnoreColumns(it =>
|
||
new
|
||
{
|
||
//忽略更新字段
|
||
it.Password,
|
||
it.LastLoginDevice,
|
||
it.LastLoginIp,
|
||
it.LastLoginTime,
|
||
it.LatestLoginDevice,
|
||
it.LatestLoginIp,
|
||
it.LatestLoginTime
|
||
}).ExecuteCommandAsync().ConfigureAwait(false) > 0;
|
||
if (result)//修改数据
|
||
{
|
||
DeleteUserFromCache(sysUser.Id);//删除用户缓存
|
||
|
||
var verificatInfoIds = _verificatInfoService.GetListByUserId(sysUser.Id);
|
||
|
||
//从列表中删除
|
||
//删除用户verificat缓存
|
||
_verificatInfoService.Delete(verificatInfoIds.Select(a => a.Id).ToList());
|
||
await NoticeUtil.UserLoginOut(new UserLoginOutEvent() { ClientIds = verificatInfoIds.SelectMany(a => a.ClientIds).ToList(), Message = Localizer["ExitVerificat"] }).ConfigureAwait(false);
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
#endregion 新增
|
||
|
||
#region 编辑
|
||
|
||
/// <inheritdoc/>
|
||
[OperDesc("ResetPassword")]
|
||
public async Task ResetPasswordAsync(long id)
|
||
{
|
||
var sysUser = await GetUserByIdAsync(id).ConfigureAwait(false);
|
||
|
||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||
|
||
var password = await GetDefaultPassWord(true).ConfigureAwait(false);//获取默认密码,这里不走Aop所以需要加密一下
|
||
using var db = GetDB();
|
||
//重置密码
|
||
if (await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
||
{
|
||
Password = password
|
||
}, it => it.Id == id).ConfigureAwait(false))
|
||
{
|
||
DeleteUserFromCache(id);//从cache删除用户信息
|
||
var verificatInfoIds = _verificatInfoService.GetListByUserId(id);
|
||
//删除用户verificat缓存
|
||
_verificatInfoService.Delete(verificatInfoIds.Select(a => a.Id).ToList());
|
||
await NoticeUtil.UserLoginOut(new UserLoginOutEvent() { ClientIds = verificatInfoIds.SelectMany(a => a.ClientIds).ToList(), Message = Localizer["ExitVerificat"] }).ConfigureAwait(false);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
[OperDesc("UserGrantRole")]
|
||
public async Task GrantRoleAsync(GrantUserOrRoleInput input)
|
||
{
|
||
var sysUser = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户信息
|
||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||
if (sysUser != null)
|
||
{
|
||
var isSuperAdmin = (sysUser.Id == RoleConst.SuperAdminId || input.GrantInfoList.Any(a => a == RoleConst.SuperAdminRoleId)) && !UserManager.SuperAdmin;//判断是否有超管
|
||
if (isSuperAdmin)
|
||
throw Oops.Bah(Localizer["CanotGrantAdmin"]);
|
||
|
||
CheckSelf(input.Id, Localizer["GrantRole"]);//判断是不是自己
|
||
|
||
//给用户赋角色
|
||
await _relationService.SaveRelationBatchAsync(RelationCategoryEnum.UserHasRole, input.Id, input.GrantInfoList.Select(it => (it.ToString(), string.Empty)), true).ConfigureAwait(false);
|
||
DeleteUserFromCache(input.Id);//从cache删除用户信息
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
[OperDesc("UserGrantResource")]
|
||
public async Task GrantResourceAsync(GrantResourceData input)
|
||
{
|
||
var menuIds = input.GrantInfoList.Select(it => it.MenuId).ToList();//菜单ID
|
||
var extJsons = input.GrantInfoList.Select(it => it.ToSystemTextJsonString()).ToList();//拓展信息
|
||
var relationUsers = new List<SysRelation>();//要添加的用户资源和授权关系表
|
||
var sysUser = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户
|
||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||
if (sysUser != null)
|
||
{
|
||
var resources = await _sysResourceService.GetAllAsync().ConfigureAwait(false);
|
||
var menusList = resources.Where(a => a.Category == ResourceCategoryEnum.Menu).Where(a => menuIds.Contains(a.Id));
|
||
|
||
|
||
#region 用户模块处理
|
||
|
||
//获取我的模块信息Id列表
|
||
var moduleIds = menusList.Select(it => it.Module).Distinct();
|
||
foreach (var item in moduleIds)
|
||
{
|
||
//将角色资源添加到列表
|
||
relationUsers.Add(new SysRelation
|
||
{
|
||
ObjectId = sysUser.Id,
|
||
TargetId = item.ToString(),
|
||
Category = RelationCategoryEnum.UserHasModule
|
||
});
|
||
}
|
||
|
||
#endregion 用户模块处理
|
||
|
||
#region 用户资源处理
|
||
|
||
for (var i = 0; i < menuIds.Count; i++)
|
||
{
|
||
//将角色资源添加到列表
|
||
relationUsers.Add(new SysRelation
|
||
{
|
||
ObjectId = sysUser.Id,
|
||
TargetId = menuIds[i].ToString(),
|
||
Category = RelationCategoryEnum.UserHasResource,
|
||
ExtJson = extJsons?[i]
|
||
});
|
||
}
|
||
#endregion 用户资源处理
|
||
|
||
#region 用户权限处理.
|
||
|
||
//获取菜单信息
|
||
if (menusList.Any())
|
||
{
|
||
//获取权限授权树
|
||
var permissions = App.GetService<IApiPermissionService>().PermissionTreeSelector(menusList.Select(it => it.Href));
|
||
//要添加的角色有哪些权限列表
|
||
var relationUserPer = permissions.Select(it => new SysRelation
|
||
{
|
||
ObjectId = sysUser.Id,
|
||
TargetId = it.ApiRoute,
|
||
Category = RelationCategoryEnum.UserHasPermission,
|
||
ExtJson = new RelationPermission { ApiUrl = it.ApiRoute }
|
||
.ToSystemTextJsonString()
|
||
});
|
||
relationUsers.AddRange(relationUserPer);//合并列表
|
||
}
|
||
|
||
#endregion 用户权限处理.
|
||
|
||
#region 保存数据库
|
||
|
||
using var db = GetDB();
|
||
|
||
//事务
|
||
var result = await db.UseTranAsync(async () =>
|
||
{
|
||
await db.Deleteable<SysRelation>(it =>
|
||
it.ObjectId == sysUser.Id && (it.Category == RelationCategoryEnum.UserHasPermission
|
||
|| it.Category == RelationCategoryEnum.UserHasResource
|
||
|| it.Category == RelationCategoryEnum.UserHasModule
|
||
|
||
)).ExecuteCommandAsync().ConfigureAwait(false);
|
||
await db.Insertable(relationUsers).ExecuteCommandAsync().ConfigureAwait(false);//添加新的
|
||
}).ConfigureAwait(false);
|
||
if (result.IsSuccess)//如果成功了
|
||
{
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasPermission);//刷新关系缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasResource);//刷新关系缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasModule);//刷新关系缓存
|
||
DeleteUserFromCache(input.Id);//删除该用户缓存
|
||
}
|
||
else
|
||
{
|
||
throw new(result.ErrorMessage, result.ErrorException);
|
||
}
|
||
|
||
#endregion 保存数据库
|
||
}
|
||
}
|
||
|
||
#endregion 编辑
|
||
|
||
#region 删除
|
||
|
||
/// <inheritdoc/>
|
||
[OperDesc("DeleteUser")]
|
||
public async Task<bool> DeleteUserAsync(IEnumerable<long> ids)
|
||
{
|
||
using var db = GetDB();
|
||
var containsSuperAdmin = await db.Queryable<SysUser>().Where(it => it.Id == RoleConst.SuperAdminId && ids.Contains(it.Id)).AnyAsync().ConfigureAwait(false);//判断是否有超管
|
||
if (containsSuperAdmin)
|
||
throw Oops.Bah(Localizer["CanotDeleteAdminUser"]);
|
||
if (ids.Contains(UserManager.UserId))
|
||
throw Oops.Bah(Localizer["CanotDeleteSelf"]);
|
||
|
||
var sysUsers = await GetUserListByIdListAsync(ids).ConfigureAwait(false);//获取用户信息
|
||
await CheckApiDataScopeAsync(sysUsers.Select(a => a.OrgId).ToList(), sysUsers.Select(a => a.CreateUserId).ToList()).ConfigureAwait(false);
|
||
|
||
//定义删除的关系
|
||
var delRelations = new List<RelationCategoryEnum>
|
||
{
|
||
RelationCategoryEnum.UserHasResource, RelationCategoryEnum.UserHasPermission, RelationCategoryEnum.UserHasRole, RelationCategoryEnum.UserHasOpenApiPermission
|
||
, RelationCategoryEnum.UserHasModule
|
||
};
|
||
//事务
|
||
var result = await db.UseTranAsync(async () =>
|
||
{
|
||
//清除该用户作为主管信息
|
||
await db.Updateable<SysUser>().SetColumns(it => new SysUser
|
||
{
|
||
DirectorId = null
|
||
})
|
||
.Where(it => ids.Contains(it.DirectorId.Value))
|
||
.ExecuteCommandAsync().ConfigureAwait(false);
|
||
|
||
//删除用户
|
||
await db.Deleteable<SysUser>().In(ids.ToList()).ExecuteCommandHasChangeAsync().ConfigureAwait(false);//删除
|
||
|
||
//删除关系表用户与资源关系,用户与权限关系,用户与角色关系
|
||
await db.Deleteable<SysRelation>(it => ids.Contains(it.ObjectId) && delRelations.Contains(it.Category)).ExecuteCommandAsync().ConfigureAwait(false);
|
||
|
||
//删除组织表主管信息
|
||
await db.Deleteable<SysOrg>(it => ids.Contains(it.DirectorId.Value)).ExecuteCommandAsync().ConfigureAwait(false);
|
||
|
||
}).ConfigureAwait(false);
|
||
|
||
if (result.IsSuccess)//如果成功了
|
||
{
|
||
DeleteUserFromCache(ids);//cache删除用户
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasRole);//关系表刷新UserHasRole缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasResource);//关系表刷新UserHasRole缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasModule);//关系表刷新UserHasModule缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasPermission);//关系表刷新UserHasRole缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasOpenApiPermission);//关系表刷新Relation_SYS_USER_HAS_OPENAPIPERMISSION缓存
|
||
//将这些用户踢下线,并永久注销这些用户
|
||
foreach (var id in ids)
|
||
{
|
||
var verificatInfoIds = _verificatInfoService.GetListByUserId(id);
|
||
_verificatInfoService.Delete(verificatInfoIds.Select(a => a.Id).ToList());
|
||
await UserLoginOut(id, verificatInfoIds.SelectMany(a => a.ClientIds).ToList()).ConfigureAwait(false);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
throw new(result.ErrorMessage, result.ErrorException);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public void DeleteUserFromCache(long userId)
|
||
{
|
||
DeleteUserFromCache(new List<long>
|
||
{
|
||
userId
|
||
});
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public void DeleteUserFromCache(IEnumerable<long> ids)
|
||
{
|
||
var userIds = ids.Select(it => it.ToString()).ToArray();//id转string列表
|
||
var sysUsers = App.CacheService.HashGet<SysUser>(CacheConst.Cache_SysUser, userIds).Where(it => it != null);//获取用户列表
|
||
if (sysUsers.Any() == true)
|
||
{
|
||
var accounts = sysUsers.Where(it => it != null).Select(it => it.Account).ToArray();//账号集合
|
||
var phones = sysUsers.Select(it => it.Phone);//手机号集合
|
||
|
||
if (sysUsers.Any(it => it.TenantId != null))//如果有租户id不是空的表示是多租户模式
|
||
{
|
||
var userAccountKey = CacheConst.Cache_SysUserAccount;
|
||
var tenantIds = sysUsers.Where(it => it.TenantId != null).Select(it => it.TenantId.Value).Distinct().ToArray();//租户id列表
|
||
foreach (var tenantId in tenantIds)
|
||
{
|
||
userAccountKey = $"{userAccountKey}:{tenantId}";
|
||
//删除账号
|
||
App.CacheService.HashDel<long>(userAccountKey, accounts);
|
||
}
|
||
}
|
||
//删除用户信息
|
||
App.CacheService.HashDel<SysUser>(CacheConst.Cache_SysUser, userIds);
|
||
//删除账号
|
||
App.CacheService.HashDel<long>(CacheConst.Cache_SysUserAccount, accounts);
|
||
|
||
App.CacheService.HashDel<VerificatInfo>(CacheConst.Cache_Token, userIds.Select(it => it.ToString()).ToArray());
|
||
|
||
|
||
}
|
||
}
|
||
|
||
#endregion 删除
|
||
|
||
#region 方法
|
||
|
||
/// <summary>
|
||
/// 通知用户下线
|
||
/// </summary>
|
||
/// <param name="userId">用户ID</param>
|
||
/// <param name="verificatInfoIds">Token列表</param>
|
||
private async Task UserLoginOut(long userId, List<string>? verificatInfoIds)
|
||
{
|
||
await NoticeUtil.UserLoginOut(new UserLoginOutEvent
|
||
{
|
||
Message = Localizer["ExitVerificat"],
|
||
ClientIds = verificatInfoIds,
|
||
}).ConfigureAwait(false);//通知用户下线
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取默认密码
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private async Task<string> GetDefaultPassWord(bool isEncrypt = false)
|
||
{
|
||
//获取默认密码
|
||
var appConfig = await _configService.GetAppConfigAsync().ConfigureAwait(false);
|
||
return isEncrypt ? DESEncryption.Encrypt(appConfig.PasswordPolicy.DefaultPassword) : appConfig.PasswordPolicy.DefaultPassword;//判断是否需要加密
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查输入参数
|
||
/// </summary>
|
||
/// <param name="sysUser"></param>
|
||
private async Task CheckInput(SysUser sysUser)
|
||
{
|
||
|
||
var sysOrgList = await _sysOrgService.GetAllAsync().ConfigureAwait(false);//获取组织列表
|
||
var userOrg = sysOrgList.FirstOrDefault(it => it.Id == sysUser.OrgId);
|
||
if (userOrg == null)
|
||
throw Oops.Bah(Localizer[$"NoOrg"]);
|
||
var tenantId = await _sysOrgService.GetTenantIdByOrgIdAsync(sysUser.OrgId, sysOrgList).ConfigureAwait(false);
|
||
|
||
//判断账号重复,直接从cache拿
|
||
var accountId = await GetIdByAccountAsync(sysUser.Account, tenantId).ConfigureAwait(false);
|
||
if (accountId > 0 && accountId != sysUser.Id)
|
||
throw Oops.Bah(Localizer["AccountDup", sysUser.Account]);
|
||
//如果邮箱不是空
|
||
if (!string.IsNullOrEmpty(sysUser.Email))
|
||
{
|
||
var isMatch = sysUser.Email.MatchEmail();//验证邮箱格式
|
||
if (!isMatch)
|
||
throw Oops.Bah(Localizer["EmailError", sysUser.Email]);
|
||
|
||
using var db = GetDB();
|
||
if (await db.Queryable<SysUser>().Where(it => it.Email == sysUser.Email && it.Id != sysUser.Id).AnyAsync().ConfigureAwait(false))
|
||
throw Oops.Bah(Localizer["EmailDup", sysUser.Email]);
|
||
}
|
||
//如果手机号不是空
|
||
if (!string.IsNullOrEmpty(sysUser.Phone))
|
||
{
|
||
if (!sysUser.Phone.MatchPhoneNumber())//验证手机格式
|
||
throw Oops.Bah(Localizer["PhoneError", sysUser.Phone]);
|
||
sysUser.Phone = DESEncryption.Encrypt(sysUser.Phone);
|
||
}
|
||
|
||
if (sysUser.DirectorId == UserManager.UserId)
|
||
throw Oops.Bah(Localizer["DirectorSelf"]);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查是否为自己
|
||
/// </summary>
|
||
/// <param name="id"></param>
|
||
/// <param name="operate">操作名称</param>
|
||
private void CheckSelf(long id, string operate)
|
||
{
|
||
if (id == UserManager.UserId)//如果是自己
|
||
{
|
||
throw Oops.Bah(Localizer["CheckSelf", operate]);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 数据库获取用户信息
|
||
/// </summary>
|
||
/// <param name="userId">用户ID</param>
|
||
/// <returns></returns>
|
||
private async Task<SysUser?> GetUserFromDbAsync(long userId)
|
||
{
|
||
using var db = GetDB();
|
||
var sysUser = await db.Queryable<SysUser>()
|
||
.LeftJoin<SysOrg>((u, o) => u.OrgId == o.Id)//连表
|
||
.LeftJoin<SysPosition>((u, o, p) => u.PositionId == p.Id)//连表
|
||
.Where(u => u.Id == userId)
|
||
.Select((u, o, p) => new SysUser
|
||
{
|
||
Id = u.Id.SelectAll(),
|
||
OrgName = o.Name,
|
||
OrgNames = o.Names,
|
||
PositionName = p.Name,
|
||
OrgAndPosIdList = o.ParentIdList
|
||
}).FirstAsync()
|
||
.ConfigureAwait(false);
|
||
if (sysUser != null)
|
||
{
|
||
sysUser.Password = DESEncryption.Decrypt(sysUser.Password);//解密密码
|
||
sysUser.Phone = DESEncryption.Decrypt(sysUser.Phone);//解密手机号
|
||
|
||
sysUser.OrgAndPosIdList.AddRange(sysUser.OrgId, sysUser.PositionId ?? 0);//添加组织和职位Id
|
||
if (sysUser.DirectorId != null)
|
||
{
|
||
sysUser.DirectorInfo = (await GetUserByIdAsync(sysUser.DirectorId.Value).ConfigureAwait(false)).Adapt<UserSelectorOutput>();//获取主管信息
|
||
}
|
||
|
||
//获取按钮码
|
||
var buttonCodeList = await GetButtonCodeListAsync(sysUser.Id).ConfigureAwait(false);
|
||
//获取数据权限
|
||
var dataScopeList = await GetPermissionListByUserIdAsync(sysUser.Id).ConfigureAwait(false);
|
||
//获取权限码
|
||
var permissionCodeList = dataScopeList.Select(it => it.ApiUrl).ToHashSet();
|
||
//获取角色码
|
||
var roleCodeList = await _roleService.GetRoleListByUserIdAsync(sysUser.Id).ConfigureAwait(false);
|
||
//权限码赋值
|
||
sysUser.ButtonCodeList = buttonCodeList;
|
||
sysUser.RoleIdList = roleCodeList.Select(it => it.Id).ToHashSet();
|
||
sysUser.PermissionCodeList = permissionCodeList;
|
||
sysUser.IsGlobal = roleCodeList.Any(a => a.Category == RoleCategoryEnum.Global);
|
||
|
||
|
||
|
||
var sysOrgList = await _sysOrgService.GetAllAsync().ConfigureAwait(false);
|
||
var scopeOrgChildList =
|
||
(await _sysOrgService.GetChildListByIdAsync(sysUser.OrgId, true, sysOrgList).ConfigureAwait(false)).Select(it => it.Id).ToHashSet();//获取所属机构的下级机构Id列表
|
||
sysUser.ScopeOrgChildList = scopeOrgChildList;
|
||
var tenantId = await _sysOrgService.GetTenantIdByOrgIdAsync(sysUser.OrgId, sysOrgList).ConfigureAwait(false);
|
||
sysUser.TenantId = tenantId;
|
||
|
||
if (sysUser.Id == RoleConst.SuperAdminId)
|
||
{
|
||
var modules = (await _sysResourceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.Category == ResourceCategoryEnum.Module).OrderBy(a => a.SortCode);
|
||
sysUser.ModuleList = modules.ToList();//模块列表赋值给用户
|
||
}
|
||
else
|
||
{
|
||
var moduleIds = await _relationService.GetUserModuleId(sysUser.RoleIdList, sysUser.Id).ConfigureAwait(false);//获取模块ID列表
|
||
var modules = (await _sysResourceService.GetMuduleByMuduleIdsAsync(moduleIds).ConfigureAwait(false)).OrderBy(a => a.SortCode);//获取模块列表
|
||
sysUser.ModuleList = modules.ToList();//模块列表赋值给用户
|
||
}
|
||
|
||
//插入Cache
|
||
App.CacheService.HashAdd(CacheConst.Cache_SysUserAccount, sysUser.Account, sysUser.Id);
|
||
App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);
|
||
|
||
return sysUser;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
#endregion 方法
|
||
}
|