mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-24 04:17:08 +08:00
390 lines
16 KiB
C#
390 lines
16 KiB
C#
//------------------------------------------------------------------------------
|
||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||
// 使用文档:https://thingsgateway.cn/
|
||
// QQ群:605534569
|
||
//------------------------------------------------------------------------------
|
||
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
|
||
using ThingsGateway.FriendlyException;
|
||
|
||
namespace ThingsGateway.Admin.Application;
|
||
|
||
internal sealed class SysResourceService : BaseService<SysResource>, ISysResourceService
|
||
{
|
||
private readonly IRelationService _relationService;
|
||
|
||
private string CacheKey = $"{CacheConst.Cache_SysResource}";
|
||
|
||
public SysResourceService(IRelationService relationService)
|
||
{
|
||
_relationService = relationService;
|
||
}
|
||
|
||
#region 增删改查
|
||
|
||
[OperDesc("CopyResource")]
|
||
public async Task CopyAsync(IEnumerable<long> ids, long moduleId)
|
||
{
|
||
var resourceList = await GetAllAsync().ConfigureAwait(false);
|
||
var myResourceList = resourceList.Where(a => ids.Contains(a.Id)).ToList();
|
||
|
||
var parent = GetMyParentResources(resourceList, myResourceList);
|
||
myResourceList = myResourceList.Concat(parent).Where(a => a.Category != ResourceCategoryEnum.Module).DistinctBy(a => a.Id).ToList();
|
||
var tree = ConstructMenuTrees(myResourceList).ToList();
|
||
SysResourceService.SetTreeValue(tree, moduleId, 0);
|
||
var data = MenuTreesToSaveLevel(tree);
|
||
using var db = GetDB();
|
||
var result = await db.Insertable(data).ExecuteCommandAsync().ConfigureAwait(false);
|
||
RefreshCache();//刷新缓存
|
||
}
|
||
|
||
private static void SetTreeValue(List<SysResource> tree, long moduleId, long parentId)
|
||
{
|
||
if (tree == null) return;
|
||
foreach (var item in tree)
|
||
{
|
||
item.Id = CommonUtils.GetSingleId();
|
||
item.ParentId = parentId;
|
||
item.Code = RandomHelper.CreateRandomString(10);
|
||
item.Module = moduleId;
|
||
SysResourceService.SetTreeValue(item.Children, moduleId, item.Id);
|
||
}
|
||
}
|
||
|
||
[OperDesc("ChangeParentResource")]
|
||
public async Task ChangeParentAsync(long id, long parentMenuId)
|
||
{
|
||
var resourceList = await GetAllAsync().ConfigureAwait(false);
|
||
var resource = resourceList.First(a => a.Id == id);
|
||
resource.ParentId = parentMenuId;
|
||
using var db = GetDB();
|
||
var result = await db.UpdateableT(resource).ExecuteCommandAsync().ConfigureAwait(false);
|
||
RefreshCache();//刷新缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.RoleHasResource);//关系表刷新缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasResource);//关系表刷新缓存
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除资源
|
||
/// </summary>
|
||
/// <param name="ids">id列表</param>
|
||
/// <returns></returns>
|
||
[OperDesc("DeleteResource")]
|
||
public async Task<bool> DeleteResourceAsync(HashSet<long> ids)
|
||
{
|
||
//删除
|
||
if (ids.Count != 0)
|
||
{
|
||
//获取所有菜单和按钮
|
||
var resourceList = await GetAllAsync().ConfigureAwait(false);
|
||
//找到要删除的菜单
|
||
var delSysResources = resourceList.Where(it => ids.Contains(it.Id));
|
||
//找到要删除的模块
|
||
var delModules = resourceList.Where(a => a.Category == ResourceCategoryEnum.Module).Where(it => ids.Contains(it.Id));
|
||
|
||
//获取模块下的所有列表
|
||
var delHashSet = delModules.Select(a => a.Id).ToHashSet();
|
||
if (delHashSet.Count != 0)
|
||
{
|
||
var delModuleResources = resourceList.Where(it => delHashSet.Contains(it.Module));
|
||
delSysResources = delSysResources.Concat(delModuleResources).ToHashSet();
|
||
}
|
||
//查找内置菜单
|
||
var system = delSysResources.FirstOrDefault(it => it.Code == ResourceConst.System);
|
||
if (system != null)
|
||
throw Oops.Bah(Localizer["CanotDeleteSystemResource", system.Title]);
|
||
|
||
//需要删除的资源ID列表
|
||
var resourceIds = delSysResources.SelectMany(it =>
|
||
{
|
||
var child = GetResourceChilden(resourceList, it.Id);
|
||
return child.Select(c => c.Id).Concat(new List<long>() { it.Id });
|
||
});
|
||
var deleteIds = ids.Concat(resourceIds).ToHashSet();//添加到删除ID列表
|
||
|
||
using var db = GetDB();
|
||
//事务
|
||
var result = await db.UseTranAsync(async () =>
|
||
{
|
||
await db.Deleteable<SysResource>().In(deleteIds.ToList()).ExecuteCommandAsync().ConfigureAwait(false);//删除菜单和按钮
|
||
await db.Deleteable<SysRelation>()//关系表删除对应RoleHasResource
|
||
.Where(it => it.Category == RelationCategoryEnum.RoleHasResource && resourceIds.Contains(SqlFunc.ToInt64(it.TargetId))).ExecuteCommandAsync().ConfigureAwait(false);
|
||
await db.Deleteable<SysRelation>()//关系表删除对应UserHasResource
|
||
.Where(it => it.Category == RelationCategoryEnum.UserHasResource && resourceIds.Contains(SqlFunc.ToInt64(it.TargetId))).ExecuteCommandAsync().ConfigureAwait(false);
|
||
}).ConfigureAwait(false);
|
||
if (result.IsSuccess)//如果成功了
|
||
{
|
||
RefreshCache();//资源表菜单刷新缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.RoleHasResource);//关系表刷新缓存
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasResource);//关系表刷新缓存
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
throw new(result.ErrorMessage, result.ErrorException);
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从缓存/数据库读取全部资源列表
|
||
/// </summary>
|
||
/// <returns>全部资源列表</returns>
|
||
public async Task<List<SysResource>> GetAllAsync()
|
||
{
|
||
var sysResources = App.CacheService.Get<List<SysResource>>(CacheConst.Cache_SysResource);
|
||
if (sysResources == null)
|
||
{
|
||
using var db = GetDB();
|
||
sysResources = await db.Queryable<SysResource>().ToListAsync().ConfigureAwait(false);
|
||
App.CacheService.Set(CacheConst.Cache_SysResource, sysResources);
|
||
}
|
||
return sysResources;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据菜单Id获取菜单列表
|
||
/// </summary>
|
||
/// <param name="menuIds">菜单id列表</param>
|
||
/// <returns>菜单列表</returns>
|
||
public async Task<IEnumerable<SysResource>> GetMenuByMenuIdsAsync(IEnumerable<long> menuIds)
|
||
{
|
||
var menuList = await GetAllAsync().ConfigureAwait(false);
|
||
var menus = menuList.Where(it => it.Category == ResourceCategoryEnum.Menu && menuIds.Contains(it.Id));
|
||
return menus;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据模块Id获取模块列表
|
||
/// </summary>
|
||
/// <param name="moduleIds">模块id列表</param>
|
||
/// <returns>菜单列表</returns>
|
||
public async Task<IEnumerable<SysResource>> GetMuduleByMuduleIdsAsync(IEnumerable<long> moduleIds)
|
||
{
|
||
var moduleList = await GetAllAsync().ConfigureAwait(false);
|
||
var modules = moduleList.Where(it => it.Category == ResourceCategoryEnum.Module && moduleIds.Contains(it.Id));
|
||
return modules;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 表格查询
|
||
/// </summary>
|
||
/// <param name="options">查询条件</param>
|
||
/// <param name="searchModel">查询条件</param>
|
||
/// <returns></returns>
|
||
public Task<QueryData<SysResource>> PageAsync(QueryPageOptions options, ResourceTableSearchModel searchModel)
|
||
{
|
||
return QueryAsync(options, b => b.Where(a => (a.Category == ResourceCategoryEnum.Module && a.Id == searchModel.Module) || (a.Category != ResourceCategoryEnum.Module && a.Module == searchModel.Module)));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存资源
|
||
/// </summary>
|
||
/// <param name="input">资源</param>
|
||
/// <param name="type">保存类型</param>
|
||
[OperDesc("SaveResource")]
|
||
public async Task<bool> SaveResourceAsync(SysResource input, ItemChangedType type)
|
||
{
|
||
var resource = await CheckInput(input).ConfigureAwait(false);//检查参数
|
||
using var db = GetDB();
|
||
|
||
if (type == ItemChangedType.Add)
|
||
{
|
||
var result = await db.InsertableT(input).ExecuteCommandAsync().ConfigureAwait(false);
|
||
RefreshCache();//刷新缓存
|
||
return result > 0;
|
||
}
|
||
else
|
||
{
|
||
var permissions = new List<SysRelation>();
|
||
if (resource.Href != input.Href)
|
||
{
|
||
//获取所有角色和用户的权限关系
|
||
var rolePermissions = await _relationService.GetRelationByCategoryAsync(RelationCategoryEnum.RoleHasPermission).ConfigureAwait(false);
|
||
var userPermissions = await _relationService.GetRelationByCategoryAsync(RelationCategoryEnum.UserHasPermission).ConfigureAwait(false);
|
||
//找到所有匹配的权限
|
||
rolePermissions = rolePermissions.Where(it => it.TargetId!.Contains(resource.Href)).ToList();
|
||
userPermissions = userPermissions.Where(it => it.TargetId!.Contains(resource.Href)).ToList();
|
||
//更新路径
|
||
rolePermissions.ForEach(it => it.TargetId = it.TargetId!.Replace(resource.Href, input.Href));
|
||
userPermissions.ForEach(it => it.TargetId = it.TargetId!.Replace(resource.Href, input.Href));
|
||
//添加到权限列表
|
||
permissions.AddRange(rolePermissions);
|
||
permissions.AddRange(userPermissions);
|
||
}
|
||
//事务
|
||
var result = await db.UseTranAsync(async () =>
|
||
{
|
||
await db.UpdateableT(input).ExecuteCommandAsync().ConfigureAwait(false);//更新数据
|
||
if (permissions.Count > 0)//如果权限列表大于0就更新
|
||
{
|
||
await db.Updateable(permissions).ExecuteCommandAsync().ConfigureAwait(false);//更新关系表
|
||
}
|
||
}).ConfigureAwait(false);
|
||
if (result.IsSuccess)//如果成功了
|
||
{
|
||
RefreshCache();//刷新菜单缓存
|
||
if (resource.Href != input.Href)
|
||
{
|
||
_relationService.RefreshCache(RelationCategoryEnum.RoleHasPermission);
|
||
_relationService.RefreshCache(RelationCategoryEnum.UserHasPermission);
|
||
}
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
throw new(result.ErrorMessage, result.ErrorException);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion 增删改查
|
||
|
||
#region 缓存
|
||
|
||
/// <summary>
|
||
/// 刷新缓存
|
||
/// </summary>
|
||
public void RefreshCache()
|
||
{
|
||
App.CacheService.Remove(CacheConst.Cache_SysResource);
|
||
//删除超级管理员的缓存
|
||
App.RootServices.GetRequiredService<ISysUserService>().DeleteUserFromCache(RoleConst.SuperAdminId);
|
||
}
|
||
|
||
#endregion 缓存
|
||
|
||
#region 方法
|
||
|
||
/// <summary>
|
||
/// 检查输入参数
|
||
/// </summary>
|
||
/// <param name="sysResource">资源</param>
|
||
private async Task<SysResource> CheckInput(SysResource sysResource)
|
||
{
|
||
if (sysResource.Code.IsNullOrWhiteSpace()) //默认编码
|
||
{
|
||
sysResource.Code = RandomHelper.CreateRandomString(10);
|
||
}
|
||
|
||
//如果菜单类型是菜单
|
||
//if (sysResource.Category == ResourceCategoryEnum.Menu)
|
||
//{
|
||
// if (string.IsNullOrEmpty(sysResource.Href))
|
||
// throw Oops.Bah("ResourceMenuHrefNotNull");
|
||
//}
|
||
|
||
//获取所有列表
|
||
var menList = await GetAllAsync().ConfigureAwait(false);
|
||
//判断是否有同级且同名
|
||
if (menList.Any(it => it.ParentId == sysResource.ParentId && it.Title == sysResource.Title && it.Id != sysResource.Id && it.Module == sysResource.Module))
|
||
throw Oops.Bah(Localizer["ResourceDup", sysResource.Title]);
|
||
if (sysResource.ParentId != 0)
|
||
{
|
||
//获取父级,判断父级ID正不正确
|
||
var parent = menList.Where(it => it.Id == sysResource.ParentId).FirstOrDefault();
|
||
if (parent != null)
|
||
{
|
||
if (parent.Module != sysResource.Module)//如果父级的模块和当前模块不一样
|
||
throw Oops.Bah(Localizer["ModuleIdDiff"]);
|
||
if (parent.Id == sysResource.Id)
|
||
throw Oops.Bah(Localizer["ResourceChoiceSelf"]);
|
||
}
|
||
else
|
||
{
|
||
throw Oops.Bah(Localizer["ResourceParentNull", sysResource.ParentId]);
|
||
}
|
||
}
|
||
|
||
//如果ID大于0表示编辑
|
||
if (sysResource.Id > 0)
|
||
{
|
||
var resource = menList.FirstOrDefault(it => it.Id == sysResource.Id);
|
||
if (resource == null)
|
||
throw Oops.Bah(Localizer["NotFoundResource"]);
|
||
return resource;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
#endregion 方法
|
||
|
||
/// <inheritdoc/>
|
||
private static List<SysResource> MenuTreesToSaveLevel(IEnumerable<SysResource> resourceList)
|
||
{
|
||
var flatList = new List<SysResource>();
|
||
|
||
void TraverseTree(SysResource node)
|
||
{
|
||
// 添加当前节点到平级列表
|
||
flatList.Add(node);
|
||
|
||
// 如果当前节点有子节点,则递归处理每个子节点
|
||
if (node.Children?.Count > 0)
|
||
{
|
||
foreach (var child in node.Children)
|
||
{
|
||
TraverseTree(child);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 遍历资源列表中的每个顶级节点
|
||
foreach (var resource in resourceList)
|
||
{
|
||
TraverseTree(resource);
|
||
}
|
||
|
||
return flatList;
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public IEnumerable<SysResource> ConstructMenuTrees(List<SysResource> resourceList, long parentId = 0)
|
||
{
|
||
//找下级资源ID列表
|
||
var resources = resourceList.Where(it => it.ParentId == parentId).OrderBy(it => it.SortCode);
|
||
foreach (var item in resources)//遍历资源
|
||
{
|
||
var children = ConstructMenuTrees(resourceList, item.Id).ToList();//添加子节点
|
||
item.Children = children.Count > 0 ? children : null;
|
||
}
|
||
return resources;
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public IEnumerable<SysResource> GetMyParentResources(IEnumerable<SysResource> allMenuList, IEnumerable<SysResource> myMenus)
|
||
{
|
||
var parentList = myMenus
|
||
.SelectMany(it => GetResourceParent(allMenuList, it.ParentId))
|
||
.Where(parent => parent != null
|
||
&& !myMenus.Contains(parent)
|
||
&& !myMenus.Any(m => m.Id == parent.Id))
|
||
.Distinct();
|
||
return parentList;
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public IEnumerable<SysResource> GetResourceChilden(IEnumerable<SysResource> resourceList, long parentId)
|
||
{
|
||
//找下级资源ID列表
|
||
return resourceList.Where(it => it.ParentId == parentId)
|
||
.SelectMany(item => new List<SysResource> { item }.Concat(GetResourceChilden(resourceList, item.Id)));
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public IEnumerable<SysResource> GetResourceParent(IEnumerable<SysResource> resourceList, long resourceId)
|
||
{
|
||
//找上级资源ID列表
|
||
return resourceList.Where(it => it.Id == resourceId)
|
||
.SelectMany(item => new List<SysResource> { item }.Concat(GetResourceParent(resourceList, item.ParentId)));
|
||
}
|
||
|
||
}
|