149 changed files with 13105 additions and 900 deletions
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp |
|||
{ |
|||
public class AbpCommonModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp |
|||
{ |
|||
public class DynamicOptionsProvider<TValue> : IOptionsProvider<TValue>, ITransientDependency |
|||
where TValue : class, new() |
|||
{ |
|||
public TValue Value => _lazyValueFactory.Value; |
|||
|
|||
private readonly Lazy<TValue> _lazyValueFactory; |
|||
private readonly OneTimeRunner _oneTimeRunner; |
|||
|
|||
public DynamicOptionsProvider(IOptions<TValue> options) |
|||
{ |
|||
_oneTimeRunner = new OneTimeRunner(); |
|||
_lazyValueFactory = new Lazy<TValue>(() => CreateOptions(options)); |
|||
} |
|||
|
|||
protected virtual TValue CreateOptions(IOptions<TValue> options) |
|||
{ |
|||
// 用于简化需要在使用配置前自行调用此接口的繁复步骤
|
|||
// await options.SetAsync();
|
|||
// _onTimeRunner.Run(async () => await options.SetAsync());
|
|||
return options.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp |
|||
{ |
|||
public interface IOptionsProvider<TValue> |
|||
where TValue: class, new() |
|||
{ |
|||
TValue Value { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace>LINGYUN.Abp</RootNamespace> |
|||
<Description>Abp扩展基础库</Description> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Core" Version="3.3.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataCreateDto : DataCreateOrUpdateDto |
|||
{ |
|||
public Guid? ParentId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataCreateOrUpdateDto |
|||
{ |
|||
[Required] |
|||
[DynamicStringLength(typeof(DataConsts), nameof(DataConsts.MaxNameLength))] |
|||
public string Name { get; set; } |
|||
|
|||
[Required] |
|||
[DynamicStringLength(typeof(DataConsts), nameof(DataConsts.MaxDisplayNameLength))] |
|||
public string DisplayName { get; set; } |
|||
|
|||
[DynamicStringLength(typeof(DataConsts), nameof(DataConsts.MaxDescriptionLength))] |
|||
public string Description { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataDto : EntityDto<Guid> |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string Code { get; set; } |
|||
|
|||
public string DisplayName { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
|
|||
public Guid? ParentId { get; set; } |
|||
|
|||
public List<DataItemDto> Items { get; set; } = new List<DataItemDto>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataItemCreateDto : DataItemCreateOrUpdateDto |
|||
{ |
|||
[Required] |
|||
[DynamicStringLength(typeof(DataItemConsts), nameof(DataItemConsts.MaxNameLength))] |
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Validation; |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using LINGYUN.Platform.Localization; |
|||
using Microsoft.Extensions.Localization; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataItemCreateOrUpdateDto : IValidatableObject |
|||
{ |
|||
[Required] |
|||
[DynamicStringLength(typeof(DataItemConsts), nameof(DataItemConsts.MaxDisplayNameLength))] |
|||
public string DisplayName { get; set; } |
|||
|
|||
[DynamicStringLength(typeof(DataItemConsts), nameof(DataItemConsts.MaxValueLength))] |
|||
public string DefaultValue { get; set; } |
|||
|
|||
[DynamicStringLength(typeof(DataItemConsts), nameof(DataItemConsts.MaxDescriptionLength))] |
|||
public string Description { get; set; } |
|||
|
|||
public bool AllowBeNull { get; set; } |
|||
|
|||
public ValueType ValueType { get; set; } |
|||
|
|||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) |
|||
{ |
|||
if (!AllowBeNull && DefaultValue.IsNullOrWhiteSpace()) |
|||
{ |
|||
var localizer = validationContext.GetRequiredService<IStringLocalizer<PlatformResource>>(); |
|||
yield return new ValidationResult( |
|||
localizer["The {0} field is required.", localizer["DisplayName:Value"]], |
|||
new string[] { nameof(DefaultValue) }); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataItemDto : EntityDto<Guid> |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string DisplayName { get; set; } |
|||
|
|||
public string DefaultValue { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
|
|||
public bool AllowBeNull { get; set; } |
|||
|
|||
public ValueType ValueType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataItemUpdateDto : DataItemCreateOrUpdateDto |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataUpdateDto : DataCreateOrUpdateDto |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class GetDataByNameInput |
|||
{ |
|||
[Required] |
|||
[DynamicStringLength(typeof(DataConsts), nameof(DataConsts.MaxNameLength))] |
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class GetDataListInput : PagedAndSortedResultRequestDto |
|||
{ |
|||
public string Filter { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public interface IDataAppService : |
|||
ICrudAppService< |
|||
DataDto, |
|||
Guid, |
|||
GetDataListInput, |
|||
DataCreateDto, |
|||
DataUpdateDto> |
|||
{ |
|||
Task<ListResultDto<DataDto>> GetAllAsync(); |
|||
|
|||
Task CreateItemAsync(Guid id, DataItemCreateDto input); |
|||
|
|||
Task UpdateItemAsync(Guid id, string name, DataItemUpdateDto input); |
|||
|
|||
Task DeleteItemAsync(Guid id, string name); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public class GetLayoutListInput : PagedAndSortedResultRequestDto |
|||
{ |
|||
public string Filter { get; set; } |
|||
|
|||
public bool Reverse { get; set; } |
|||
|
|||
public PlatformType? PlatformType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public class LayoutCreateDto : LayoutCreateOrUpdateDto |
|||
{ |
|||
public Guid DataId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using LINGYUN.Platform.Routes; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public class LayoutCreateOrUpdateDto |
|||
{ |
|||
[Required] |
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxNameLength))] |
|||
public string Name { get; set; } |
|||
|
|||
[Required] |
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxDisplayNameLength))] |
|||
public string DisplayName { get; set; } |
|||
|
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxDescriptionLength))] |
|||
public string Description { get; set; } |
|||
|
|||
[Required] |
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxPathLength))] |
|||
public string Path { get; set; } |
|||
|
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxRedirectLength))] |
|||
public string Redirect { get; set; } |
|||
|
|||
public PlatformType PlatformType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using LINGYUN.Platform.Routes; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public class LayoutDto : RouteDto |
|||
{ |
|||
/// <summary>
|
|||
/// 布局编号
|
|||
/// </summary>
|
|||
public string Code { get; set; } |
|||
/// <summary>
|
|||
/// 所属平台
|
|||
/// </summary>
|
|||
public PlatformType PlatformType { get; set; } |
|||
/// <summary>
|
|||
/// 约定的Meta采用哪种数据字典,主要是约束路由必须字段的一致性
|
|||
/// </summary>
|
|||
public Guid DataId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public class LayoutUpdateDto : LayoutCreateOrUpdateDto |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public interface ILayoutAppService : |
|||
ICrudAppService< |
|||
LayoutDto, |
|||
Guid, |
|||
GetLayoutListInput, |
|||
LayoutCreateDto, |
|||
LayoutUpdateDto> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class GetMenuInput |
|||
{ |
|||
public PlatformType PlatformType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuCreateDto : MenuCreateOrUpdateDto |
|||
{ |
|||
public Guid? ParentId { get; set; } |
|||
|
|||
[Required] |
|||
public Guid LayoutId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using LINGYUN.Platform.Routes; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuCreateOrUpdateDto |
|||
{ |
|||
[Required] |
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxNameLength))] |
|||
public string Name { get; set; } |
|||
|
|||
[Required] |
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxDisplayNameLength))] |
|||
public string DisplayName { get; set; } |
|||
|
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxDescriptionLength))] |
|||
public string Description { get; set; } |
|||
|
|||
[Required] |
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxPathLength))] |
|||
public string Path { get; set; } |
|||
|
|||
[DynamicStringLength(typeof(RouteConsts), nameof(RouteConsts.MaxRedirectLength))] |
|||
public string Redirect { get; set; } |
|||
|
|||
[Required] |
|||
[DynamicStringLength(typeof(MenuConsts), nameof(MenuConsts.MaxComponentLength))] |
|||
public string Component { get; set; } |
|||
|
|||
public bool IsPublic { get; set; } |
|||
|
|||
public Dictionary<string, object> Meta { get; set; } = new Dictionary<string, object>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using LINGYUN.Platform.Routes; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuDto : RouteDto |
|||
{ |
|||
/// <summary>
|
|||
/// 菜单编号
|
|||
/// </summary>
|
|||
public string Code { get; set; } |
|||
/// <summary>
|
|||
/// 菜单布局页
|
|||
/// </summary>
|
|||
public string Component { get; set; } |
|||
/// <summary>
|
|||
/// 所属平台
|
|||
/// </summary>
|
|||
public PlatformType PlatformType { get; set; } |
|||
/// <summary>
|
|||
/// 父节点
|
|||
/// </summary>
|
|||
public Guid? ParentId { get; set; } |
|||
/// <summary>
|
|||
/// 所属布局标识
|
|||
/// </summary>
|
|||
public Guid LayoutId { get; set; } |
|||
|
|||
public bool IsPublic { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuGetAllInput : ISortedResultRequest |
|||
{ |
|||
public PlatformType? PlatformType { get; set; } |
|||
|
|||
public string Filter { get; set; } |
|||
|
|||
public bool Reverse { get; set; } |
|||
|
|||
public Guid? ParentId { get; set; } |
|||
|
|||
public string Sorting { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuGetByRoleInput |
|||
{ |
|||
[Required] |
|||
[StringLength(80)] |
|||
public string Role { get; set; } |
|||
|
|||
public PlatformType PlatformType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuGetByUserInput |
|||
{ |
|||
[Required] |
|||
public Guid UserId { get; set; } |
|||
|
|||
public string[] Roles { get; set; } = new string[0]; |
|||
|
|||
public PlatformType PlatformType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuGetListInput : PagedAndSortedResultRequestDto |
|||
{ |
|||
public PlatformType? PlatformType { get; set; } |
|||
|
|||
public string Filter { get; set; } |
|||
|
|||
public bool Reverse { get; set; } |
|||
|
|||
public Guid? ParentId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using LINGYUN.Platform.Routes; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuItemDto : RouteDto |
|||
{ |
|||
/// <summary>
|
|||
/// 菜单编号
|
|||
/// </summary>
|
|||
public string Code { get; set; } |
|||
/// <summary>
|
|||
/// 菜单组件
|
|||
/// </summary>
|
|||
public string Component { get; set; } |
|||
/// <summary>
|
|||
/// 子菜单列表
|
|||
/// </summary>
|
|||
public List<MenuItemDto> Children { get; set; } = new List<MenuItemDto>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuUpdateDto : MenuCreateOrUpdateDto |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class RoleMenuInput |
|||
{ |
|||
[Required] |
|||
[StringLength(80)] |
|||
public string RoleName { get; set; } |
|||
|
|||
[Required] |
|||
public List<Guid> MenuIds { get; set; } = new List<Guid>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class UserMenuInput |
|||
{ |
|||
[Required] |
|||
public Guid UserId { get; set; } |
|||
|
|||
[Required] |
|||
public List<Guid> MenuIds { get; set; } = new List<Guid>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public interface IMenuAppService : |
|||
ICrudAppService< |
|||
MenuDto, |
|||
Guid, |
|||
MenuGetListInput, |
|||
MenuCreateDto, |
|||
MenuUpdateDto> |
|||
{ |
|||
Task<ListResultDto<MenuDto>> GetAllAsync(MenuGetAllInput input); |
|||
|
|||
Task<ListResultDto<MenuDto>> GetUserMenuListAsync(MenuGetByUserInput input); |
|||
|
|||
Task<ListResultDto<MenuDto>> GetRoleMenuListAsync(MenuGetByRoleInput input); |
|||
|
|||
Task SetUserMenusAsync(UserMenuInput input); |
|||
|
|||
Task SetRoleMenusAsync(RoleMenuInput input); |
|||
|
|||
Task<ListResultDto<MenuDto>> GetCurrentUserMenuListAsync(GetMenuInput input); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Platform |
|||
{ |
|||
public static class PlatformRemoteServiceConsts |
|||
{ |
|||
public const string RemoteServiceName = "Platform"; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
public class RouteDto : EntityDto<Guid> |
|||
{ |
|||
/// <summary>
|
|||
/// 路径
|
|||
/// </summary>
|
|||
public string Path { get; set; } |
|||
/// <summary>
|
|||
/// 名称
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
/// <summary>
|
|||
/// 显示名称
|
|||
/// </summary>
|
|||
public string DisplayName { get; set; } |
|||
/// <summary>
|
|||
/// 说明
|
|||
/// </summary>
|
|||
public string Description { get; set; } |
|||
/// <summary>
|
|||
/// 重定向路径
|
|||
/// </summary>
|
|||
public string Redirect { get; set; } |
|||
/// <summary>
|
|||
/// 路由的一些辅助元素,取决于数据字典的设计
|
|||
/// </summary>
|
|||
public Dictionary<string, object> Meta { get; set; } |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
using AutoMapper; |
|||
using LINGYUN.Platform.Versions; |
|||
|
|||
namespace LINGYUN.Platform |
|||
{ |
|||
public class AppPlatformApplicationMappingProfile : Profile |
|||
{ |
|||
public AppPlatformApplicationMappingProfile() |
|||
{ |
|||
CreateMap<VersionFile, VersionFileDto>(); |
|||
CreateMap<AppVersion, VersionDto>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,191 @@ |
|||
using LINGYUN.Platform.Permissions; |
|||
using LINGYUN.Platform.Utils; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
[Authorize(PlatformPermissions.DataDictionary.Default)] |
|||
public class DataAppService : PlatformApplicationServiceBase, IDataAppService |
|||
{ |
|||
protected IDataRepository DataRepository { get; } |
|||
|
|||
public DataAppService( |
|||
IDataRepository dataRepository) |
|||
{ |
|||
DataRepository = dataRepository; |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.DataDictionary.Create)] |
|||
public virtual async Task<DataDto> CreateAsync(DataCreateDto input) |
|||
{ |
|||
var data = await DataRepository.FindByNameAsync(input.Name); |
|||
if (data != null) |
|||
{ |
|||
throw new UserFriendlyException("指定名称的数据字典已经存在!"); |
|||
} |
|||
|
|||
string code = string.Empty; |
|||
var children = await DataRepository.GetChildrenAsync(input.ParentId); |
|||
if (children.Any()) |
|||
{ |
|||
var lastChildren = children.OrderBy(x => x.Code).FirstOrDefault(); |
|||
code = CodeNumberGenerator.CalculateNextCode(lastChildren.Code); |
|||
} |
|||
else |
|||
{ |
|||
var parentData = input.ParentId != null |
|||
? await DataRepository.GetAsync(input.ParentId.Value) |
|||
: null; |
|||
|
|||
code = CodeNumberGenerator.AppendCode(parentData?.Code, CodeNumberGenerator.CreateCode(1)); |
|||
} |
|||
|
|||
data = new Data( |
|||
GuidGenerator.Create(), |
|||
input.Name, |
|||
code, |
|||
input.DisplayName, |
|||
input.Description, |
|||
input.ParentId, |
|||
CurrentTenant.Id |
|||
); |
|||
|
|||
data = await DataRepository.InsertAsync(data); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
|
|||
return ObjectMapper.Map<Data, DataDto>(data); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.DataDictionary.Delete)] |
|||
public virtual async Task DeleteAsync(Guid id) |
|||
{ |
|||
var data = await DataRepository.GetAsync(id); |
|||
|
|||
var children = await DataRepository.GetChildrenAsync(data.Id); |
|||
if (children.Any()) |
|||
{ |
|||
throw new UserFriendlyException("当前数据字典存在子节点,无法删除!"); |
|||
} |
|||
|
|||
await DataRepository.DeleteAsync(data); |
|||
} |
|||
|
|||
public virtual async Task<DataDto> GetAsync(Guid id) |
|||
{ |
|||
var data = await DataRepository.GetAsync(id); |
|||
|
|||
return ObjectMapper.Map<Data, DataDto>(data); |
|||
} |
|||
|
|||
public virtual async Task<ListResultDto<DataDto>> GetAllAsync() |
|||
{ |
|||
var datas = await DataRepository.GetListAsync(includeDetails: false); |
|||
|
|||
return new ListResultDto<DataDto>( |
|||
ObjectMapper.Map<List<Data>, List<DataDto>>(datas)); |
|||
} |
|||
|
|||
public virtual async Task<PagedResultDto<DataDto>> GetListAsync(GetDataListInput input) |
|||
{ |
|||
var count = await DataRepository.GetCountAsync(input.Filter); |
|||
|
|||
var datas = await DataRepository.GetPagedListAsync( |
|||
input.Filter, input.Sorting, |
|||
false, input.SkipCount, input.MaxResultCount); |
|||
|
|||
return new PagedResultDto<DataDto>(count, |
|||
ObjectMapper.Map<List<Data>, List<DataDto>>(datas)); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.DataDictionary.Update)] |
|||
public virtual async Task<DataDto> UpdateAsync(Guid id, DataUpdateDto input) |
|||
{ |
|||
var data = await DataRepository.GetAsync(id); |
|||
|
|||
if (!string.Equals(data.Name, input.Name, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
data.Name = input.Name; |
|||
} |
|||
if (!string.Equals(data.DisplayName, input.DisplayName, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
data.DisplayName = input.DisplayName; |
|||
} |
|||
if (!string.Equals(data.Description, input.Description, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
data.Description = input.Description; |
|||
} |
|||
|
|||
data = await DataRepository.UpdateAsync(data); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
|
|||
return ObjectMapper.Map<Data, DataDto>(data); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.DataDictionary.ManageItems)] |
|||
public virtual async Task UpdateItemAsync(Guid id, string name, DataItemUpdateDto input) |
|||
{ |
|||
var data = await DataRepository.GetAsync(id); |
|||
var dataItem = data.FindItem(name); |
|||
if (dataItem == null) |
|||
{ |
|||
throw new UserFriendlyException($"不存在名为 {name} 的数据字典项!"); |
|||
} |
|||
|
|||
if (!string.Equals(dataItem.DefaultValue, input.DefaultValue, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
dataItem.DefaultValue = input.DefaultValue; |
|||
} |
|||
if (!string.Equals(dataItem.DisplayName, input.DisplayName, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
dataItem.DisplayName = input.DisplayName; |
|||
} |
|||
if (!string.Equals(dataItem.Description, input.Description, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
dataItem.Description = input.Description; |
|||
} |
|||
dataItem.AllowBeNull = input.AllowBeNull; |
|||
|
|||
data = await DataRepository.UpdateAsync(data); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.DataDictionary.ManageItems)] |
|||
public virtual async Task CreateItemAsync(Guid id, DataItemCreateDto input) |
|||
{ |
|||
var data = await DataRepository.GetAsync(id); |
|||
var dataItem = data.FindItem(input.Name); |
|||
if (dataItem != null) |
|||
{ |
|||
throw new UserFriendlyException($"已经存在名为 {input.Name} 的数据字典项!"); |
|||
} |
|||
|
|||
data.AddItem( |
|||
GuidGenerator, |
|||
input.Name, |
|||
input.DisplayName, |
|||
input.DefaultValue, |
|||
input.ValueType, |
|||
input.Description, |
|||
input.AllowBeNull); |
|||
|
|||
await DataRepository.UpdateAsync(data); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.DataDictionary.ManageItems)] |
|||
public virtual async Task DeleteItemAsync(Guid id, string name) |
|||
{ |
|||
var data = await DataRepository.GetAsync(id); |
|||
data.RemoveItem(name); |
|||
|
|||
await DataRepository.UpdateAsync(data); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
using LINGYUN.Platform.Datas; |
|||
using LINGYUN.Platform.Permissions; |
|||
using LINGYUN.Platform.Utils; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
[Authorize(PlatformPermissions.Layout.Default)] |
|||
public class LayoutAppService : PlatformApplicationServiceBase, ILayoutAppService |
|||
{ |
|||
protected IDataRepository DataRepository { get; } |
|||
protected ILayoutRepository LayoutRepository { get; } |
|||
|
|||
public LayoutAppService( |
|||
IDataRepository dataRepository, |
|||
ILayoutRepository layoutRepository) |
|||
{ |
|||
DataRepository = dataRepository; |
|||
LayoutRepository = layoutRepository; |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Layout.Create)] |
|||
public virtual async Task<LayoutDto> CreateAsync(LayoutCreateDto input) |
|||
{ |
|||
var data = DataRepository.FindAsync(input.DataId); |
|||
if (data == null) |
|||
{ |
|||
throw new UserFriendlyException($"数据字典 {input.DataId} 的不存在或已删除!"); |
|||
} |
|||
|
|||
var layout = await LayoutRepository.FindByNameAsync(input.Name); |
|||
if (layout != null) |
|||
{ |
|||
throw new UserFriendlyException($"已经存在名为 {input.Name} 的布局!"); |
|||
} |
|||
|
|||
var lastLayout = await LayoutRepository.GetLastOrNullAsync(); |
|||
var code = lastLayout != null |
|||
? CodeNumberGenerator.CalculateNextCode(lastLayout.Code) |
|||
: CodeNumberGenerator.CreateCode(1); |
|||
|
|||
layout = new Layout( |
|||
GuidGenerator.Create(), |
|||
input.Path, |
|||
input.Name, |
|||
code, |
|||
input.DisplayName, |
|||
input.DataId, |
|||
input.PlatformType, |
|||
input.Redirect, |
|||
input.Description, |
|||
CurrentTenant.Id); |
|||
|
|||
layout = await LayoutRepository.InsertAsync(layout); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
|
|||
return ObjectMapper.Map<Layout, LayoutDto>(layout); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Layout.Delete)] |
|||
public virtual async Task DeleteAsync(Guid id) |
|||
{ |
|||
var layout = await LayoutRepository.GetAsync(id); |
|||
|
|||
//if (await LayoutRepository.AnyMenuAsync(layout.Id))
|
|||
//{
|
|||
// throw new UserFriendlyException($"不能删除存在菜单的布局!");
|
|||
//}
|
|||
|
|||
await LayoutRepository.DeleteAsync(layout); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
} |
|||
|
|||
public virtual async Task<LayoutDto> GetAsync(Guid id) |
|||
{ |
|||
var layout = await LayoutRepository.GetAsync(id); |
|||
|
|||
return ObjectMapper.Map<Layout, LayoutDto>(layout); |
|||
} |
|||
|
|||
public virtual async Task<PagedResultDto<LayoutDto>> GetListAsync(GetLayoutListInput input) |
|||
{ |
|||
var count = await LayoutRepository.GetCountAsync(input.PlatformType, input.Filter); |
|||
|
|||
var layouts = await LayoutRepository.GetListAsync( |
|||
input.PlatformType, input.Filter, |
|||
input.Sorting, input.Reverse, false, |
|||
input.SkipCount, input.MaxResultCount); |
|||
|
|||
return new PagedResultDto<LayoutDto>(count, |
|||
ObjectMapper.Map<List<Layout>, List<LayoutDto>>(layouts)); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Layout.Update)] |
|||
public virtual async Task<LayoutDto> UpdateAsync(Guid id, LayoutUpdateDto input) |
|||
{ |
|||
var layout = await LayoutRepository.GetAsync(id); |
|||
|
|||
if (!string.Equals(layout.Name, input.Name, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
layout.Name = input.Name; |
|||
} |
|||
if (!string.Equals(layout.DisplayName, input.DisplayName, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
layout.DisplayName = input.DisplayName; |
|||
} |
|||
if (!string.Equals(layout.Description, input.Description, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
layout.Description = input.Description; |
|||
} |
|||
if (!string.Equals(layout.Path, input.Path, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
layout.Path = input.Path; |
|||
} |
|||
if (!string.Equals(layout.Redirect, input.Redirect, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
layout.Redirect = input.Redirect; |
|||
} |
|||
layout = await LayoutRepository.UpdateAsync(layout); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
|
|||
return ObjectMapper.Map<Layout, LayoutDto>(layout); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,238 @@ |
|||
using LINGYUN.Platform.Datas; |
|||
using LINGYUN.Platform.Layouts; |
|||
using LINGYUN.Platform.Permissions; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
[Authorize(PlatformPermissions.Menu.Default)] |
|||
public class MenuAppService : PlatformApplicationServiceBase, IMenuAppService |
|||
{ |
|||
protected DataItemMappingOptions DataItemMapping { get; } |
|||
protected MenuManager MenuManager { get; } |
|||
protected IMenuRepository MenuRepository { get; } |
|||
protected IDataRepository DataRepository { get; } |
|||
protected ILayoutRepository LayoutRepository { get; } |
|||
|
|||
public MenuAppService( |
|||
MenuManager menuManager, |
|||
IMenuRepository menuRepository, |
|||
IDataRepository dataRepository, |
|||
ILayoutRepository layoutRepository, |
|||
IOptions<DataItemMappingOptions> options) |
|||
{ |
|||
MenuManager = menuManager; |
|||
MenuRepository = menuRepository; |
|||
DataRepository = dataRepository; |
|||
LayoutRepository = layoutRepository; |
|||
DataItemMapping = options.Value; |
|||
} |
|||
|
|||
[Authorize] |
|||
public virtual async Task<ListResultDto<MenuDto>> GetCurrentUserMenuListAsync(GetMenuInput input) |
|||
{ |
|||
var myMenus = await MenuRepository.GetUserMenusAsync( |
|||
CurrentUser.GetId(), |
|||
CurrentUser.Roles, |
|||
input.PlatformType); |
|||
|
|||
return new ListResultDto<MenuDto>( |
|||
ObjectMapper.Map<List<Menu>, List<MenuDto>>(myMenus)); |
|||
} |
|||
|
|||
public virtual async Task<MenuDto> GetAsync(Guid id) |
|||
{ |
|||
var menu = await MenuRepository.GetAsync(id); |
|||
|
|||
return ObjectMapper.Map<Menu, MenuDto>(menu); |
|||
} |
|||
|
|||
public virtual async Task<ListResultDto<MenuDto>> GetAllAsync(MenuGetAllInput input) |
|||
{ |
|||
var menus = await MenuRepository.GetAllAsync( |
|||
input.Filter, input.Sorting, input.Reverse, |
|||
input.PlatformType, input.ParentId); |
|||
|
|||
return new ListResultDto<MenuDto>( |
|||
ObjectMapper.Map<List<Menu>, List<MenuDto>>(menus)); |
|||
} |
|||
|
|||
public virtual async Task<PagedResultDto<MenuDto>> GetListAsync(MenuGetListInput input) |
|||
{ |
|||
var count = await MenuRepository.GetCountAsync(input.Filter, input.PlatformType, input.ParentId); |
|||
|
|||
var menus = await MenuRepository.GetListAsync( |
|||
input.Filter, input.Sorting, input.Reverse, |
|||
input.PlatformType, input.ParentId, |
|||
input.SkipCount, input.MaxResultCount); |
|||
|
|||
return new PagedResultDto<MenuDto>(count, |
|||
ObjectMapper.Map<List<Menu>, List<MenuDto>>(menus)); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Menu.Create)] |
|||
public virtual async Task<MenuDto> CreateAsync(MenuCreateDto input) |
|||
{ |
|||
var layout = await LayoutRepository.GetAsync(input.LayoutId); |
|||
var data = await DataRepository.GetAsync(layout.DataId); |
|||
|
|||
var menu = await MenuManager.CreateAsync( |
|||
GuidGenerator.Create(), |
|||
layout.Id, |
|||
input.Path, |
|||
input.Name, |
|||
input.Component, |
|||
input.DisplayName, |
|||
input.Redirect, |
|||
input.Description, |
|||
layout.PlatformType, |
|||
input.ParentId, |
|||
CurrentTenant.Id, |
|||
input.IsPublic); |
|||
|
|||
// 利用布局约定的数据字典来校验必须的路由元数据,元数据的加入是为了适配多端路由
|
|||
foreach (var dataItem in data.Items) |
|||
{ |
|||
if (!input.Meta.TryGetValue(dataItem.Name, out object meta)) |
|||
{ |
|||
if (!dataItem.AllowBeNull) |
|||
{ |
|||
throw new BusinessException(PlatformErrorCodes.MenuMissingMetadata) |
|||
.WithData("Name", dataItem.DisplayName) |
|||
.WithData("DataName", data.DisplayName); |
|||
} |
|||
// 是否需要设定默认值
|
|||
menu.SetProperty(dataItem.Name, dataItem.DefaultValue); |
|||
} |
|||
else |
|||
{ |
|||
// 需要检查参数是否有效
|
|||
menu.SetProperty(dataItem.Name, DataItemMapping.MapToString(dataItem.ValueType, meta)); |
|||
} |
|||
} |
|||
|
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
|
|||
return ObjectMapper.Map<Menu, MenuDto>(menu); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Menu.Update)] |
|||
public virtual async Task<MenuDto> UpdateAsync(Guid id, MenuUpdateDto input) |
|||
{ |
|||
var menu = await MenuRepository.GetAsync(id); |
|||
|
|||
// 利用布局约定的数据字典来校验必须的路由元数据,元数据的加入是为了适配多端路由
|
|||
var layout = await LayoutRepository.GetAsync(menu.LayoutId); |
|||
var data = await DataRepository.GetAsync(layout.DataId); |
|||
foreach (var dataItem in data.Items) |
|||
{ |
|||
if (!input.Meta.TryGetValue(dataItem.Name, out object meta)) |
|||
{ |
|||
if (!dataItem.AllowBeNull) |
|||
{ |
|||
throw new BusinessException(PlatformErrorCodes.MenuMissingMetadata) |
|||
.WithData("Name", dataItem.DisplayName) |
|||
.WithData("DataName", data.DisplayName); |
|||
} |
|||
// 是否需要设定默认值?
|
|||
menu.SetProperty(dataItem.Name, dataItem.DefaultValue); |
|||
} |
|||
else |
|||
{ |
|||
// 与现有的数据做对比
|
|||
var menuMeta = menu.GetProperty(dataItem.Name); |
|||
if (menuMeta != null && menuMeta.Equals(meta)) |
|||
{ |
|||
continue; |
|||
} |
|||
// 需要检查参数是否有效
|
|||
menu.SetProperty(dataItem.Name, DataItemMapping.MapToString(dataItem.ValueType, meta)); |
|||
} |
|||
} |
|||
|
|||
if (!string.Equals(menu.Name, input.Name, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
menu.Name = input.Name; |
|||
} |
|||
if (!string.Equals(menu.DisplayName, input.DisplayName, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
menu.DisplayName = input.DisplayName; |
|||
} |
|||
if (!string.Equals(menu.Description, input.Description, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
menu.Description = input.Description; |
|||
} |
|||
if (!string.Equals(menu.Path, input.Path, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
menu.Path = input.Path; |
|||
} |
|||
if (!string.Equals(menu.Redirect, input.Redirect, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
menu.Redirect = input.Redirect; |
|||
} |
|||
if (!string.Equals(menu.Component, input.Component, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
menu.Component = input.Component; |
|||
} |
|||
|
|||
menu.IsPublic = input.IsPublic; |
|||
|
|||
await MenuManager.UpdateAsync(menu); |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
|
|||
return ObjectMapper.Map<Menu, MenuDto>(menu); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Menu.Delete)] |
|||
public virtual async Task DeleteAsync(Guid id) |
|||
{ |
|||
var childrens = await MenuRepository.GetChildrenAsync(id); |
|||
if (childrens.Any()) |
|||
{ |
|||
throw new BusinessException(PlatformErrorCodes.DeleteMenuHaveChildren); |
|||
} |
|||
|
|||
var menu = await MenuRepository.GetAsync(id); |
|||
await MenuRepository.DeleteAsync(menu); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Menu.ManageUsers)] |
|||
public virtual async Task<ListResultDto<MenuDto>> GetUserMenuListAsync(MenuGetByUserInput input) |
|||
{ |
|||
var menus = await MenuRepository.GetUserMenusAsync(input.UserId, input.Roles, input.PlatformType); |
|||
|
|||
return new ListResultDto<MenuDto>( |
|||
ObjectMapper.Map<List<Menu>, List<MenuDto>>(menus)); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Menu.ManageUsers)] |
|||
public virtual async Task SetUserMenusAsync(UserMenuInput input) |
|||
{ |
|||
await MenuManager.SetUserMenusAsync(input.UserId, input.MenuIds); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Menu.ManageRoles)] |
|||
public virtual async Task SetRoleMenusAsync(RoleMenuInput input) |
|||
{ |
|||
await MenuManager.SetRoleMenusAsync(input.RoleName, input.MenuIds); |
|||
} |
|||
|
|||
[Authorize(PlatformPermissions.Menu.ManageRoles)] |
|||
public virtual async Task<ListResultDto<MenuDto>> GetRoleMenuListAsync(MenuGetByRoleInput input) |
|||
{ |
|||
var menus = await MenuRepository.GetRoleMenusAsync(new string[] { input.Role }, input.PlatformType); |
|||
|
|||
return new ListResultDto<MenuDto>( |
|||
ObjectMapper.Map<List<Menu>, List<MenuDto>>(menus)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using AutoMapper; |
|||
using LINGYUN.Platform.Datas; |
|||
using LINGYUN.Platform.Layouts; |
|||
using LINGYUN.Platform.Menus; |
|||
using LINGYUN.Platform.Versions; |
|||
|
|||
namespace LINGYUN.Platform |
|||
{ |
|||
public class PlatformApplicationMappingProfile : Profile |
|||
{ |
|||
public PlatformApplicationMappingProfile() |
|||
{ |
|||
CreateMap<VersionFile, VersionFileDto>(); |
|||
CreateMap<AppVersion, VersionDto>(); |
|||
|
|||
CreateMap<DataItem, DataItemDto>(); |
|||
CreateMap<Data, DataDto>(); |
|||
CreateMap<Menu, MenuDto>() |
|||
.ForMember(dto => dto.Meta, map => map.MapFrom(src => src.ExtraProperties)); |
|||
CreateMap<Layout, LayoutDto>() |
|||
.ForMember(dto => dto.Meta, map => map.MapFrom(src => src.ExtraProperties)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public static class DataConsts |
|||
{ |
|||
public static int MaxNameLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 30; |
|||
|
|||
public static int MaxCodeLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 1024; |
|||
|
|||
public static int MaxDisplayNameLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 128; |
|||
|
|||
public static int MaxDescriptionLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 1024; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public static class DataItemConsts |
|||
{ |
|||
public static int MaxNameLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 30; |
|||
|
|||
public static int MaxValueLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 128; |
|||
|
|||
public static int MaxDisplayNameLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 128; |
|||
|
|||
public static int MaxDescriptionLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 1024; |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public enum ValueType |
|||
{ |
|||
String = 0, |
|||
Numeic = 1, |
|||
Boolean = 2, |
|||
Date = 3, |
|||
DateTime = 4, |
|||
Array = 5 |
|||
|
|||
} |
|||
} |
|||
@ -1,12 +1,52 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Platform.VersionFile:404": "File not found, name: {FileName}, version:{FileVersion}!", |
|||
"Platform:01404": "File not found, name: {FileName}, version:{FileVersion}!", |
|||
"Platform:02001": "The same menu exists in the sibling directory: {Name}!", |
|||
"Platform:02002": "You are not allowed to delete menu nodes when there are other submenus!", |
|||
"Platform:02003": "The menu level has reached the specified maximum: {Depth}!", |
|||
"Platform:02101": "The menu metadata is missing the necessary element :{Name}, which is defined in the data dictionary :{DataName}!", |
|||
"Platform:03001": "The metadata format does not match!", |
|||
"UploadFileSizeBeyondLimit": "Upload file size cannot exceed {0} MB!", |
|||
"NotAllowedFileExtensionName": "Not allowed file extension: {0}!", |
|||
"DisplayName:VersionFileLimitLength": "File limit size", |
|||
"Description:VersionFileLimitLength": "Limit size of uploaded file in MB", |
|||
"DisplayName:AllowVersionFileExtensions": "File extension", |
|||
"Description:AllowVersionFileExtensions": "List of allowed extensions to upload files, with multiple extensions separated by, don't need a notation" |
|||
"Description:AllowVersionFileExtensions": "List of allowed extensions to upload files, with multiple extensions separated by, don't need a notation", |
|||
"DisplayName:Menus": "Menus", |
|||
"DisplayName:Name": "Name", |
|||
"DisplayName:Code": "Code", |
|||
"DisplayName:IsPublic": "Is Public", |
|||
"DisplayName:DisplayName": "Display Name", |
|||
"DisplayName:Description": "Description", |
|||
"DisplayName:AllowBeNull": "Allow Be Null", |
|||
"DisplayName:DefaultValue": "Default Value", |
|||
"DisplayName:ValueType": "Value Type", |
|||
"DisplayName:Path": "Path", |
|||
"DisplayName:Redirect": "Redirect", |
|||
"DisplayName:Meta": "Meta", |
|||
"DisplayName:Component": "Component", |
|||
"DisplayName:Filter": "Filter", |
|||
"DisplayName:PlatformType": "Platform Type", |
|||
"DisplayName:SecrchMenu": "Secrch", |
|||
"DisplayName:Layout": "Layout", |
|||
"DisplayName:Basic": "Basic", |
|||
"DisplayName:DataDictionary": "Data Dictionary", |
|||
"Menu:AddNew": "Add New", |
|||
"Menu:AddChildren": "Add Children", |
|||
"Menu:Edit": "Edit", |
|||
"Menu:EditByName": "Edit Menu - {0}", |
|||
"Menu:Delete": "Delete", |
|||
"Menu:WillDelete": "The menu {0} will be deleted", |
|||
"Data:WillDelete": "The dictionary {0} will be deleted", |
|||
"Menu:Manage": "Manage Menus", |
|||
"Data:AddNew": "Add New", |
|||
"Data:AddChildren": "Add Children", |
|||
"Data:Edit": "Edit", |
|||
"Data:Delete": "Delete", |
|||
"Data:AppendItem": "Append Item", |
|||
"Data:EditItem": "Edit Item", |
|||
"Data:RemoveItem": "Remove Item", |
|||
"Data:Items": "Data Items" |
|||
} |
|||
} |
|||
@ -1,12 +1,52 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Platform.VersionFile:404": "文件: {FileName}, 版本号:{FileVersion} 不存在!", |
|||
"Platform:01404": "文件: {FileName}, 版本号: {FileVersion} 不存在!", |
|||
"Platform:02001": "同级目录下存在相同的菜单: {Name}!", |
|||
"Platform:02002": "在有其他子菜单的情况下,不允许删除菜单节点!", |
|||
"Platform:02003": "菜单层级已达到规定最大值: {Depth}!", |
|||
"Platform:02101": "菜单元数据缺少必要的元素: {Name},此选项在数据字典:{DataName} 中定义!", |
|||
"Platform:03001": "元数据格式不匹配!", |
|||
"UploadFileSizeBeyondLimit": "上传文件大小不能超过 {0} MB!", |
|||
"NotAllowedFileExtensionName": "不被允许的文件扩展名: {0}!", |
|||
"DisplayName:VersionFileLimitLength": "文件限制大小", |
|||
"Description:VersionFileLimitLength": "上传文件的限制大小,单位(MB)", |
|||
"DisplayName:AllowVersionFileExtensions": "文件扩展名", |
|||
"Description:AllowVersionFileExtensions": "允许的上传文件扩展名列表,多个扩展名以,分隔,无需输入.符号" |
|||
"Description:AllowVersionFileExtensions": "允许的上传文件扩展名列表,多个扩展名以,分隔,无需输入.符号", |
|||
"DisplayName:Menus": "菜单列表", |
|||
"DisplayName:Name": "名称", |
|||
"DisplayName:Code": "编号", |
|||
"DisplayName:IsPublic": "是否公用的", |
|||
"DisplayName:DisplayName": "显示名称", |
|||
"DisplayName:Description": "说明", |
|||
"DisplayName:AllowBeNull": "允许空值", |
|||
"DisplayName:DefaultValue": "默认值", |
|||
"DisplayName:ValueType": "值类型", |
|||
"DisplayName:Path": "路径", |
|||
"DisplayName:Redirect": "重定向路径", |
|||
"DisplayName:Meta": "元数据", |
|||
"DisplayName:Component": "组件", |
|||
"DisplayName:Filter": "筛选", |
|||
"DisplayName:PlatformType": "平台类型", |
|||
"DisplayName:SecrchMenu": "查询菜单", |
|||
"DisplayName:Layout": "布局", |
|||
"DisplayName:Basic": "基础信息", |
|||
"DisplayName:DataDictionary": "数据字典", |
|||
"Menu:AddNew": "添加新菜单", |
|||
"Menu:AddChildren": "添加子菜单", |
|||
"Menu:Edit": "编辑菜单", |
|||
"Menu:EditByName": "编辑菜单 - {0}", |
|||
"Menu:Delete": "删除菜单", |
|||
"Menu:WillDelete": "将要删除菜单 {0}", |
|||
"Menu:Manage": "管理菜单", |
|||
"Data:WillDelete": "将要删除字典 {0}", |
|||
"Data:AddNew": "添加新字典", |
|||
"Data:AddChildren": "添加下级字典", |
|||
"Data:Edit": "编辑字典", |
|||
"Data:Delete": "删除字典", |
|||
"Data:AppendItem": "添加项目", |
|||
"Data:EditItem": "编辑项目", |
|||
"Data:RemoveItem": "删除项目", |
|||
"Data:Items": "字典项目" |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public static class MenuConsts |
|||
{ |
|||
public static int MaxComponentLength |
|||
{ |
|||
get; |
|||
set; |
|||
} = 255; |
|||
|
|||
/// <summary>
|
|||
/// 最大深度
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// 默认为4,仅支持四级子菜单
|
|||
/// </remarks>
|
|||
public const int MaxDepth = 4; |
|||
|
|||
public const int MaxCodeLength = MaxDepth * (PlatformConsts.CodeUnitLength + 1) - 1; |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
namespace LINGYUN.Platform |
|||
{ |
|||
public static class PlatformConsts |
|||
{ |
|||
/// <summary>
|
|||
/// 编号不足位补足字符,如编号为 3,长度5位,补足字符为 0,则编号为00003
|
|||
/// </summary>
|
|||
public static char CodePrefix { get; set; } = '0'; |
|||
/// <summary>
|
|||
/// 编号长度
|
|||
/// </summary>
|
|||
public const int CodeUnitLength = 5; |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using System.Linq; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
/// <summary>
|
|||
/// 数据字典
|
|||
/// </summary>
|
|||
public class Data : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
|
|||
public virtual string Name { get; set; } |
|||
|
|||
public virtual string Code { get; set; } |
|||
|
|||
public virtual string DisplayName { get; set; } |
|||
|
|||
public virtual string Description { get; set; } |
|||
|
|||
public virtual Guid? ParentId { get; set; } |
|||
|
|||
public virtual ICollection<DataItem> Items { get; protected set; } |
|||
|
|||
protected Data() |
|||
{ |
|||
Items = new Collection<DataItem>(); |
|||
} |
|||
|
|||
public Data( |
|||
[NotNull] Guid id, |
|||
[NotNull] string name, |
|||
[NotNull] string code, |
|||
[NotNull] string displayName, |
|||
string description = "", |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(id, nameof(id)); |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
Check.NotNullOrWhiteSpace(code, nameof(code)); |
|||
Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); |
|||
|
|||
Id = id; |
|||
Name = name; |
|||
Code = code; |
|||
DisplayName = displayName; |
|||
Description = description; |
|||
ParentId = parentId; |
|||
TenantId = tenantId; |
|||
|
|||
CreationTime = DateTime.Now; |
|||
|
|||
Items = new Collection<DataItem>(); |
|||
} |
|||
|
|||
public Data AddItem( |
|||
[NotNull] IGuidGenerator guidGenerator, |
|||
[NotNull] string name, |
|||
[NotNull] string displayName, |
|||
[CanBeNull] string defaultValue, |
|||
ValueType valueType = ValueType.String, |
|||
string description = "", |
|||
bool allowBeNull = true) |
|||
{ |
|||
Check.NotNull(guidGenerator, nameof(guidGenerator)); |
|||
Check.NotNull(name, nameof(name)); |
|||
Check.NotNull(displayName, nameof(displayName)); |
|||
|
|||
if (!IsInItem(name)) |
|||
{ |
|||
var dataItem = new DataItem( |
|||
guidGenerator.Create(), |
|||
Id, |
|||
name, |
|||
displayName, |
|||
defaultValue, |
|||
valueType, |
|||
description, |
|||
allowBeNull, |
|||
TenantId |
|||
); |
|||
Items.Add(dataItem); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public DataItem FindItem(string name) |
|||
{ |
|||
return Items.FirstOrDefault(item => item.Name == name); |
|||
} |
|||
|
|||
public DataItem FindItem(Guid id) |
|||
{ |
|||
return Items.FirstOrDefault(item => item.Id == id); |
|||
} |
|||
|
|||
public bool RemoveItem(string name) |
|||
{ |
|||
if (IsInItem(name)) |
|||
{ |
|||
Items.RemoveAll(item => item.Name == name); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public bool IsInItem(string name) |
|||
{ |
|||
return Items.Any(item => item.Name == name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataDictionaryDataSeeder : IDataDictionaryDataSeeder, ITransientDependency |
|||
{ |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
protected IDataRepository DataRepository { get; } |
|||
|
|||
public DataDictionaryDataSeeder( |
|||
IGuidGenerator guidGenerator, |
|||
IDataRepository dataRepository) |
|||
{ |
|||
GuidGenerator = guidGenerator; |
|||
DataRepository = dataRepository; |
|||
} |
|||
|
|||
public virtual async Task<Data> SeedAsync( |
|||
string name, |
|||
string code, |
|||
string displayName, |
|||
string description = "", |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var data = await DataRepository.FindByNameAsync(name, cancellationToken: cancellationToken); |
|||
|
|||
if (data == null) |
|||
{ |
|||
data = new Data( |
|||
GuidGenerator.Create(), |
|||
name, |
|||
code, |
|||
displayName, |
|||
description, |
|||
parentId, |
|||
tenantId); |
|||
|
|||
data = await DataRepository.InsertAsync(data); |
|||
} |
|||
|
|||
return data; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataItem : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
|
|||
public virtual string Name { get; protected set; } |
|||
|
|||
public virtual string DisplayName { get; set; } |
|||
|
|||
public virtual string DefaultValue { get; set; } |
|||
|
|||
public virtual string Description { get; set; } |
|||
|
|||
public virtual bool AllowBeNull { get; set; } |
|||
|
|||
public virtual ValueType ValueType { get; protected set; } |
|||
|
|||
public virtual Guid DataId { get; protected set; } |
|||
|
|||
protected DataItem() { } |
|||
|
|||
public DataItem( |
|||
[NotNull] Guid id, |
|||
[NotNull] Guid dataId, |
|||
[NotNull] string name, |
|||
[NotNull] string displayName, |
|||
[CanBeNull] string defaultValue = null, |
|||
ValueType valueType = ValueType.String, |
|||
string description = "", |
|||
bool allowBeNull = true, |
|||
Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(id, nameof(id)); |
|||
Check.NotNull(dataId, nameof(dataId)); |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); |
|||
|
|||
Id = id; |
|||
Name = name; |
|||
DefaultValue = defaultValue ?? SetDefaultValue(); |
|||
ValueType = valueType; |
|||
DisplayName = displayName; |
|||
AllowBeNull = allowBeNull; |
|||
|
|||
DataId = dataId; |
|||
TenantId = tenantId; |
|||
Description = description; |
|||
} |
|||
|
|||
public string SetDefaultValue() |
|||
{ |
|||
switch (ValueType) |
|||
{ |
|||
case ValueType.Array: |
|||
DefaultValue = "";// 当数据类型为数组对象时,需要前端来做转换了,约定的分隔符为英文逗号
|
|||
break; |
|||
case ValueType.Boolean: |
|||
DefaultValue = "false"; |
|||
break; |
|||
case ValueType.Date: |
|||
DefaultValue = !AllowBeNull ? DateTime.Now.ToString("yyyy-MM-dd") : ""; |
|||
break; |
|||
case ValueType.DateTime: |
|||
if (!AllowBeNull) |
|||
{ |
|||
DefaultValue = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); // TODO: 以当前时间作为默认值?
|
|||
} |
|||
DefaultValue = !AllowBeNull ? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") : ""; |
|||
break; |
|||
case ValueType.Numeic: |
|||
DefaultValue = "0"; |
|||
break; |
|||
default: |
|||
case ValueType.String: |
|||
DefaultValue = ""; |
|||
break; |
|||
} |
|||
|
|||
return DefaultValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,105 @@ |
|||
using Newtonsoft.Json.Linq; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class DataItemMappingOptions |
|||
{ |
|||
public Dictionary<ValueType, Func<object, string>> DataItemMaps { get; } |
|||
|
|||
public DataItemMappingOptions() |
|||
{ |
|||
DataItemMaps = new Dictionary<ValueType, Func<object, string>>(); |
|||
} |
|||
|
|||
internal void SetDefaultMapping() |
|||
{ |
|||
SetMapping(ValueType.Array, value => |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return ""; |
|||
} |
|||
|
|||
if (value is JArray array) |
|||
{ |
|||
var joinString = string.Empty; |
|||
foreach (var obj in array.Children()) |
|||
{ |
|||
joinString += obj.ToString() + ","; |
|||
} |
|||
return joinString.EndsWith(",") ? joinString.Substring(0, joinString.Length - 1) : joinString; |
|||
} |
|||
throw new BusinessException(PlatformErrorCodes.MetaFormatMissMatch); |
|||
}); |
|||
SetMapping(ValueType.Boolean, value => |
|||
{ |
|||
if (value != null) |
|||
{ |
|||
if (value is bool bo) |
|||
{ |
|||
return bo.ToString().ToLower(); |
|||
} |
|||
else |
|||
{ |
|||
var boInput = value.ToString().ToLower(); |
|||
if (boInput == "true" || |
|||
boInput == "false") |
|||
{ |
|||
return boInput; |
|||
} |
|||
} |
|||
} |
|||
throw new BusinessException(PlatformErrorCodes.MetaFormatMissMatch); |
|||
}); |
|||
SetMapping(ValueType.Date, value => |
|||
{ |
|||
if (value != null && value is DateTime date) |
|||
{ |
|||
return date.ToString("yyyy-MM-dd"); |
|||
} |
|||
throw new BusinessException(PlatformErrorCodes.MetaFormatMissMatch); |
|||
}); |
|||
SetMapping(ValueType.DateTime, value => |
|||
{ |
|||
if (value != null && value is DateTime date) |
|||
{ |
|||
return date.ToString("yyyy-MM-dd HH:mm:ss"); |
|||
} |
|||
throw new BusinessException(PlatformErrorCodes.MetaFormatMissMatch); |
|||
}); |
|||
SetMapping(ValueType.Numeic, value => |
|||
{ |
|||
if (value != null) |
|||
{ |
|||
var valueType = value.GetType(); |
|||
if (!valueType.IsClass && !valueType.IsInterface && typeof(IFormattable).IsAssignableFrom(valueType)) |
|||
{ |
|||
return value.ToString(); |
|||
} |
|||
} |
|||
throw new BusinessException(PlatformErrorCodes.MetaFormatMissMatch); |
|||
}); |
|||
SetMapping(ValueType.String, value => |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return ""; |
|||
} |
|||
return value.ToString(); |
|||
}); |
|||
} |
|||
|
|||
public void SetMapping(ValueType valueType, Func<object, string> func) |
|||
{ |
|||
DataItemMaps[valueType] = func; |
|||
} |
|||
|
|||
public string MapToString(ValueType valueType, object inputValue) |
|||
{ |
|||
return DataItemMaps[valueType](inputValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public interface IDataDictionaryDataSeeder |
|||
{ |
|||
Task<Data> SeedAsync( |
|||
string name, |
|||
string code, |
|||
string displayName, |
|||
string description = "", |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public interface IDataRepository : IBasicRepository<Data, Guid> |
|||
{ |
|||
Task<Data> FindByNameAsync( |
|||
string name, |
|||
bool includeDetails = true, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<Data>> GetChildrenAsync( |
|||
Guid? parentId, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<int> GetCountAsync( |
|||
string filter = "", |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<Data>> GetPagedListAsync( |
|||
string filter = "", |
|||
string sotring = nameof(Data.Code), |
|||
bool includeDetails = false, |
|||
int skipCount = 0, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public interface ILayoutRepository : IBasicRepository<Layout, Guid> |
|||
{ |
|||
/// <summary>
|
|||
/// 根据名称查询布局
|
|||
/// </summary>
|
|||
/// <param name="name"></param>
|
|||
/// <param name="includeDetails"></param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<Layout> FindByNameAsync( |
|||
string name, |
|||
bool includeDetails = true, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<Layout> GetLastOrNullAsync( |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<int> GetCountAsync( |
|||
PlatformType? platformType = null, |
|||
string filter = "", |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<Layout>> GetListAsync( |
|||
PlatformType? platformType = null, |
|||
string filter = "", |
|||
string sorting = nameof(Layout.Code), |
|||
bool reverse = false, |
|||
bool includeDetails = false, |
|||
int skipCount = 0, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using JetBrains.Annotations; |
|||
using LINGYUN.Platform.Routes; |
|||
using System; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
/// <summary>
|
|||
/// 布局视图实体
|
|||
/// </summary>
|
|||
public class Layout : Route |
|||
{ |
|||
/// <summary>
|
|||
/// 布局编号
|
|||
/// </summary>
|
|||
public virtual string Code { get; set; } |
|||
/// <summary>
|
|||
/// 所属平台
|
|||
/// </summary>
|
|||
public virtual PlatformType PlatformType { get; protected set; } |
|||
/// <summary>
|
|||
/// 约定的Meta采用哪种数据字典,主要是约束路由必须字段的一致性
|
|||
/// </summary>
|
|||
public virtual Guid DataId { get; protected set; } |
|||
|
|||
protected Layout() { } |
|||
|
|||
public Layout( |
|||
[NotNull] Guid id, |
|||
[NotNull] string path, |
|||
[NotNull] string name, |
|||
[NotNull] string code, |
|||
[NotNull] string displayName, |
|||
[NotNull] Guid dataId, |
|||
[NotNull] PlatformType platformType = PlatformType.None, |
|||
[CanBeNull] string redirect = "", |
|||
[CanBeNull] string description = "", |
|||
[CanBeNull] Guid? tenantId = null) |
|||
: base(id, path, name, displayName, redirect, description, tenantId) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(code, nameof(code)); |
|||
|
|||
Code = code; |
|||
DataId = dataId; |
|||
PlatformType = platformType; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public interface IMenuRepository : IBasicRepository<Menu, Guid> |
|||
{ |
|||
/// <summary>
|
|||
/// 根据名称查询菜单
|
|||
/// </summary>
|
|||
/// <param name="menuName"></param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<Menu> FindByNameAsync( |
|||
string menuName, |
|||
CancellationToken cancellationToken = default); |
|||
/// <summary>
|
|||
/// 查询主菜单,每一个布局页创建的时候都要创建路径为 / 的主菜单
|
|||
/// </summary>
|
|||
/// <param name="platformType"></param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<Menu> FindMainAsync( |
|||
PlatformType platformType = PlatformType.None, |
|||
CancellationToken cancellationToken = default); |
|||
/// <summary>
|
|||
/// 获取子节点
|
|||
/// </summary>
|
|||
/// <param name="parentId"></param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<List<Menu>> GetChildrenAsync( |
|||
Guid? parentId, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
/// <summary>
|
|||
/// 通过父菜单编码查询子菜单
|
|||
/// </summary>
|
|||
/// <param name="code"></param>
|
|||
/// <param name="parentId"></param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<List<Menu>> GetAllChildrenWithParentCodeAsync( |
|||
string code, |
|||
Guid? parentId, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
/// <summary>
|
|||
/// 查找用户可访问菜单
|
|||
/// </summary>
|
|||
/// <param name="userId">用户标识</param>
|
|||
/// <param name="roles">角色列表</param>
|
|||
/// <param name="platformType">平台类型</param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<List<Menu>> GetUserMenusAsync( |
|||
Guid userId, |
|||
string[] roles, |
|||
PlatformType platformType = PlatformType.None, |
|||
CancellationToken cancellationToken = default); |
|||
/// <summary>
|
|||
/// 查找角色可访问菜单
|
|||
/// </summary>
|
|||
/// <param name="roles">角色列表</param>
|
|||
/// <param name="platformType">平台类型</param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<List<Menu>> GetRoleMenusAsync( |
|||
string[] roles, |
|||
PlatformType platformType = PlatformType.None, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<int> GetCountAsync( |
|||
string filter = "", |
|||
PlatformType? platformType = null, |
|||
Guid? parentId = null, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<Menu>> GetListAsync( |
|||
string filter = "", |
|||
string sorting = nameof(Menu.Code), |
|||
bool reverse = false, |
|||
PlatformType? platformType = null, |
|||
Guid? parentId = null, |
|||
int skipCount = 0, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<Menu>> GetAllAsync( |
|||
string filter = "", |
|||
string sorting = nameof(Menu.Code), |
|||
bool reverse = false, |
|||
PlatformType? platformType = null, |
|||
Guid? parentId = null, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task RemoveAllRolesAsync( |
|||
Menu menu, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task RemoveAllMembersAsync( |
|||
Menu menu, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public interface IRoleMenuRepository : IBasicRepository<RoleMenu, Guid> |
|||
{ |
|||
/// <summary>
|
|||
/// 角色是否拥有菜单
|
|||
/// </summary>
|
|||
/// <param name="roleName"></param>
|
|||
/// <param name="menuName"></param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<bool> RoleHasInMenuAsync( |
|||
string roleName, |
|||
string menuName, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task SetRoleMenusAsync( |
|||
string roleName, |
|||
IEnumerable<Guid> menuIds, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public interface IUserMenuRepository : IBasicRepository<UserMenu, Guid> |
|||
{ |
|||
/// <summary>
|
|||
/// 用户是否拥有菜单
|
|||
/// </summary>
|
|||
/// <param name="userId"></param>
|
|||
/// <param name="menuName"></param>
|
|||
/// <param name="cancellationToken"></param>
|
|||
/// <returns></returns>
|
|||
Task<bool> UserHasInMenuAsync( |
|||
Guid userId, |
|||
string menuName, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task SetMemberMenusAsync( |
|||
Guid userId, |
|||
IEnumerable<Guid> menuIds, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
using JetBrains.Annotations; |
|||
using LINGYUN.Platform.Routes; |
|||
using System; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
/// <summary>
|
|||
/// 菜单
|
|||
/// </summary>
|
|||
public class Menu : Route |
|||
{ |
|||
/// <summary>
|
|||
/// 所属平台
|
|||
/// </summary>
|
|||
public virtual PlatformType PlatformType { get; set; } |
|||
/// <summary>
|
|||
/// 菜单编号
|
|||
/// </summary>
|
|||
public virtual string Code { get; set; } |
|||
/// <summary>
|
|||
/// 菜单布局页,Layout的路径
|
|||
/// </summary>
|
|||
public virtual string Component { get; set; } |
|||
/// <summary>
|
|||
/// 所属的父菜单
|
|||
/// </summary>
|
|||
public virtual Guid? ParentId { get; set; } |
|||
/// <summary>
|
|||
/// 所属布局标识
|
|||
/// </summary>
|
|||
public virtual Guid LayoutId { get; set; } |
|||
/// <summary>
|
|||
/// 公共菜单
|
|||
/// </summary>
|
|||
public virtual bool IsPublic { get; set; } |
|||
protected Menu() |
|||
{ |
|||
} |
|||
|
|||
public Menu( |
|||
[NotNull] Guid id, |
|||
[NotNull] Guid layoutId, |
|||
[NotNull] string path, |
|||
[NotNull] string name, |
|||
[NotNull] string code, |
|||
[NotNull] string component, |
|||
[NotNull] string displayName, |
|||
string redirect = "", |
|||
string description = "", |
|||
PlatformType platformType = PlatformType.None, |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null) |
|||
: base(id, path, name, displayName, redirect, description, tenantId) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(code, nameof(code)); |
|||
|
|||
LayoutId = layoutId; |
|||
Code = code; |
|||
Component = component;// 下属的一级菜单的Component应该是布局页, 例如vue-admin中的 component: Layout, 其他前端框架雷同, 此处应传递布局页的路径让前端import
|
|||
PlatformType = platformType; |
|||
ParentId = parentId; |
|||
|
|||
IsPublic = false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,191 @@ |
|||
using LINGYUN.Platform.Utils; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class MenuManager : DomainService |
|||
{ |
|||
protected IMenuRepository MenuRepository { get; } |
|||
protected IUserMenuRepository UserMenuRepository { get; } |
|||
protected IRoleMenuRepository RoleMenuRepository { get; } |
|||
|
|||
public MenuManager( |
|||
IMenuRepository menuRepository, |
|||
IUserMenuRepository userMenuRepository, |
|||
IRoleMenuRepository roleMenuRepository) |
|||
{ |
|||
MenuRepository = menuRepository; |
|||
UserMenuRepository = userMenuRepository; |
|||
RoleMenuRepository = roleMenuRepository; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task<Menu> CreateAsync( |
|||
Guid id, |
|||
Guid layoutId, |
|||
string path, |
|||
string name, |
|||
string component, |
|||
string displayName, |
|||
string redirect = "", |
|||
string description = "", |
|||
PlatformType platformType = PlatformType.None, |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null, |
|||
bool isPublic = false) |
|||
{ |
|||
var code = await GetNextChildCodeAsync(parentId); |
|||
if (code.Length > MenuConsts.MaxCodeLength) |
|||
{ |
|||
throw new BusinessException(PlatformErrorCodes.MenuAchieveMaxDepth) |
|||
.WithData("Depth", MenuConsts.MaxDepth); |
|||
} |
|||
var menu = new Menu( |
|||
id, |
|||
layoutId, |
|||
path, |
|||
name, |
|||
code, |
|||
component, |
|||
displayName, |
|||
redirect, |
|||
description, |
|||
platformType, |
|||
parentId, |
|||
tenantId) |
|||
{ |
|||
IsPublic = isPublic |
|||
}; |
|||
await ValidateMenuAsync(menu); |
|||
await MenuRepository.InsertAsync(menu); |
|||
|
|||
return menu; |
|||
} |
|||
|
|||
public virtual async Task UpdateAsync(Menu menu) |
|||
{ |
|||
await ValidateMenuAsync(menu); |
|||
await MenuRepository.UpdateAsync(menu); |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task DeleteAsync(Guid id) |
|||
{ |
|||
var children = await FindChildrenAsync(id, true); |
|||
|
|||
foreach (var child in children) |
|||
{ |
|||
await MenuRepository.RemoveAllMembersAsync(child); |
|||
await MenuRepository.RemoveAllRolesAsync(child); |
|||
await MenuRepository.DeleteAsync(child); |
|||
} |
|||
|
|||
var menu = await MenuRepository.GetAsync(id); |
|||
await MenuRepository.RemoveAllMembersAsync(menu); |
|||
await MenuRepository.RemoveAllRolesAsync(menu); |
|||
|
|||
await MenuRepository.DeleteAsync(id); |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task MoveAsync(Guid id, Guid? parentId) |
|||
{ |
|||
var menu = await MenuRepository.GetAsync(id); |
|||
if (menu.ParentId == parentId) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var children = await FindChildrenAsync(id, true); |
|||
|
|||
var oldCode = menu.Code; |
|||
|
|||
menu.Code = await GetNextChildCodeAsync(parentId); |
|||
menu.ParentId = parentId; |
|||
|
|||
await ValidateMenuAsync(menu); |
|||
|
|||
foreach (var child in children) |
|||
{ |
|||
child.Code = CodeNumberGenerator.AppendCode(menu.Code, CodeNumberGenerator.GetRelativeCode(child.Code, oldCode)); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task SetUserMenusAsync(Guid userId, IEnumerable<Guid> menuIds) |
|||
{ |
|||
await UserMenuRepository.SetMemberMenusAsync(userId, menuIds); |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task SetRoleMenusAsync(string roleName, IEnumerable<Guid> menuIds) |
|||
{ |
|||
await RoleMenuRepository.SetRoleMenusAsync(roleName, menuIds); |
|||
} |
|||
|
|||
protected virtual async Task ValidateMenuAsync(Menu menu) |
|||
{ |
|||
var siblings = (await FindChildrenAsync(menu.ParentId)) |
|||
.Where(x => x.Id != menu.Id) |
|||
.ToList(); |
|||
|
|||
if (siblings.Any(ou => ou.Name == menu.Name)) |
|||
{ |
|||
throw new BusinessException(PlatformErrorCodes.DuplicateMenu) |
|||
.WithData("Name", menu.Name); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task<string> GetNextChildCodeAsync(Guid? parentId) |
|||
{ |
|||
var lastChild = await GetLastChildOrNullAsync(parentId); |
|||
if (lastChild != null) |
|||
{ |
|||
return CodeNumberGenerator.CalculateNextCode(lastChild.Code); |
|||
} |
|||
|
|||
var parentCode = parentId != null |
|||
? await GetCodeOrDefaultAsync(parentId.Value) |
|||
: null; |
|||
|
|||
return CodeNumberGenerator.AppendCode( |
|||
parentCode, |
|||
CodeNumberGenerator.CreateCode(1) |
|||
); |
|||
} |
|||
|
|||
public virtual async Task<Menu> GetLastChildOrNullAsync(Guid? parentId) |
|||
{ |
|||
var children = await MenuRepository.GetChildrenAsync(parentId); |
|||
return children.OrderBy(c => c.Code).LastOrDefault(); |
|||
} |
|||
|
|||
public async Task<List<Menu>> FindChildrenAsync(Guid? parentId, bool recursive = false) |
|||
{ |
|||
if (!recursive) |
|||
{ |
|||
return await MenuRepository.GetChildrenAsync(parentId); |
|||
} |
|||
|
|||
if (!parentId.HasValue) |
|||
{ |
|||
return await MenuRepository.GetListAsync(includeDetails: true); |
|||
} |
|||
|
|||
var code = await GetCodeOrDefaultAsync(parentId.Value); |
|||
|
|||
return await MenuRepository.GetAllChildrenWithParentCodeAsync(code, parentId); |
|||
} |
|||
|
|||
public virtual async Task<string> GetCodeOrDefaultAsync(Guid id) |
|||
{ |
|||
var menu = await MenuRepository.GetAsync(id); |
|||
return menu?.Code; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
/// <summary>
|
|||
/// 角色菜单
|
|||
/// </summary>
|
|||
public class RoleMenu : AuditedEntity<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
|
|||
public virtual Guid MenuId { get; protected set; } |
|||
|
|||
public virtual string RoleName { get; protected set; } |
|||
|
|||
protected RoleMenu() { } |
|||
|
|||
public RoleMenu( |
|||
Guid menuId, |
|||
string roleName, |
|||
Guid? tenantId = null) |
|||
{ |
|||
MenuId = menuId; |
|||
RoleName = roleName; |
|||
TenantId = tenantId; |
|||
} |
|||
|
|||
public override object[] GetKeys() |
|||
{ |
|||
return new object[] { MenuId, RoleName }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
/// <summary>
|
|||
/// 用户菜单
|
|||
/// </summary>
|
|||
public class UserMenu : AuditedEntity<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
|
|||
public virtual Guid MenuId { get; protected set; } |
|||
|
|||
public virtual Guid UserId { get; protected set; } |
|||
|
|||
protected UserMenu() { } |
|||
|
|||
public UserMenu( |
|||
Guid menuId, |
|||
Guid userId, |
|||
Guid? tenantId = null) |
|||
{ |
|||
MenuId = menuId; |
|||
UserId = userId; |
|||
TenantId = tenantId; |
|||
} |
|||
|
|||
public override object[] GetKeys() |
|||
{ |
|||
return new object[] { MenuId, UserId }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,760 @@ |
|||
using LINGYUN.Platform.Datas; |
|||
using LINGYUN.Platform.Layouts; |
|||
using LINGYUN.Platform.Menus; |
|||
using LINGYUN.Platform.Routes; |
|||
using LINGYUN.Platform.Utils; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
|
|||
namespace LINGYUN.Platform |
|||
{ |
|||
public class PlatformDataSeedContributor : IDataSeedContributor, ITransientDependency |
|||
{ |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
protected IRouteDataSeeder RouteDataSeeder { get; } |
|||
protected IDataDictionaryDataSeeder DataDictionaryDataSeeder { get; } |
|||
protected IMenuRepository MenuRepository { get; } |
|||
protected ILayoutRepository LayoutRepository { get; } |
|||
|
|||
public PlatformDataSeedContributor( |
|||
IRouteDataSeeder routeDataSeeder, |
|||
IMenuRepository menuRepository, |
|||
ILayoutRepository layoutRepository, |
|||
IGuidGenerator guidGenerator, |
|||
IDataDictionaryDataSeeder dataDictionaryDataSeeder) |
|||
{ |
|||
GuidGenerator = guidGenerator; |
|||
RouteDataSeeder = routeDataSeeder; |
|||
MenuRepository = menuRepository; |
|||
LayoutRepository = layoutRepository; |
|||
DataDictionaryDataSeeder = dataDictionaryDataSeeder; |
|||
} |
|||
|
|||
public virtual async Task SeedAsync(DataSeedContext context) |
|||
{ |
|||
var data = await SeedDefaultDataDictionaryAsync(context.TenantId); |
|||
// 预置
|
|||
var layout = await SeedDefaultLayoutAsync(data); |
|||
// 首页
|
|||
await SeedHomeMenuAsync(layout, data); |
|||
// 管理菜单预置菜单数据
|
|||
await SeedAdminMenuAsync(layout, data); |
|||
// saas菜单数据
|
|||
await SeedSaasMenuAsync(layout, data); |
|||
// 身份资源菜单数据
|
|||
await SeedIdentityServerMenuAsync(layout, data); |
|||
// 审计日志菜单数据
|
|||
await SeedAuditingMenuAsync(layout, data); |
|||
// 布局容器预置菜单数据
|
|||
await SeedContainerMenuAsync(layout, data); |
|||
// 网关管理菜单数据
|
|||
await SeedApiGatewayMenuAsync(layout, data); |
|||
} |
|||
|
|||
private async Task<Data> SeedDefaultDataDictionaryAsync(Guid? tenantId) |
|||
{ |
|||
var data = await DataDictionaryDataSeeder |
|||
.SeedAsync( |
|||
"Layout", |
|||
CodeNumberGenerator.CreateCode(1), |
|||
"Vue Admin Layout Meta Dictionary", |
|||
"Vue Admin Layout Meta Dictionary", |
|||
null, |
|||
tenantId); |
|||
|
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"roles", // TODO: 是否需要把这一项写入到预置数据?
|
|||
"roles", |
|||
"", |
|||
Datas.ValueType.Array, |
|||
"will control the page roles (allow setting multiple roles)"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"title", |
|||
"title", |
|||
"component", |
|||
Datas.ValueType.String, |
|||
"the name showed in subMenu and breadcrumb (recommend set)"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"icon", |
|||
"icon", |
|||
"icon", |
|||
Datas.ValueType.String, |
|||
"the icon showed in the sidebar"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"hidden", |
|||
"hidden", |
|||
"false", |
|||
Datas.ValueType.Boolean, |
|||
"if true, this route will not show in the sidebar (default is false)"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"alwaysShow", |
|||
"alwaysShow", |
|||
"false", |
|||
Datas.ValueType.Boolean, |
|||
"if true, will always show the root menu (default is false)"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"breadcrumb", |
|||
"breadcrumb", |
|||
"true", |
|||
Datas.ValueType.Boolean, |
|||
"if false, the item will be hidden in breadcrumb (default is true)"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"noCache", |
|||
"noCache", |
|||
"false", |
|||
Datas.ValueType.Boolean, |
|||
"if true, the page will not be cached (default is false)"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"affix", |
|||
"affix", |
|||
"false", |
|||
Datas.ValueType.Boolean, |
|||
"if true, the tag will affix in the tags-view"); |
|||
data.AddItem( |
|||
GuidGenerator, |
|||
"activeMenu", |
|||
"activeMenu", |
|||
"", |
|||
Datas.ValueType.String, |
|||
"if set path, the sidebar will highlight the path you set"); |
|||
|
|||
return data; |
|||
} |
|||
|
|||
private async Task<Layout> SeedDefaultLayoutAsync(Data data) |
|||
{ |
|||
var layout = await RouteDataSeeder.SeedLayoutAsync( |
|||
"Layout", |
|||
"layout/index.vue", |
|||
CodeNumberGenerator.CreateCode(1), |
|||
"Vue Admin Layout", |
|||
data.Id, |
|||
PlatformType.WebMvvm, // 针对当前的vue管理页
|
|||
"", |
|||
"Vue Admin Layout", |
|||
data.TenantId |
|||
); |
|||
|
|||
return layout; |
|||
} |
|||
|
|||
private async Task SeedHomeMenuAsync(Layout layout, Data data) |
|||
{ |
|||
var adminMenu = await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"home", |
|||
"/", |
|||
CodeNumberGenerator.CreateCode(1), |
|||
layout.Path, |
|||
"Home", |
|||
"/dashboard", |
|||
"Home", |
|||
null, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "home" }, |
|||
{ "icon", "home" }, |
|||
{ "alwaysShow", true } |
|||
}, |
|||
// isPublic: true,
|
|||
isPublic: false); // 首页应该是共有的页面
|
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"dashboard", |
|||
"dashboard", |
|||
CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/dashboard/index.vue", |
|||
"Dashboard", |
|||
"", |
|||
"Dashboard", |
|||
adminMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "dashboard" }, |
|||
{ "icon", "dashboard" } |
|||
}, |
|||
isPublic: false); |
|||
} |
|||
|
|||
private async Task SeedAdminMenuAsync(Layout layout, Data data) |
|||
{ |
|||
var adminMenu = await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"admin", |
|||
"/admin", |
|||
CodeNumberGenerator.CreateCode(2), |
|||
layout.Path, |
|||
"Admin", |
|||
"", |
|||
"Admin", |
|||
null, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "admin" }, |
|||
{ "icon", "admin" }, |
|||
{ "alwaysShow", true } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"roles", |
|||
"roles", |
|||
CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/roles/index.vue", |
|||
"Manage Roles", |
|||
"", |
|||
"Manage Roles", |
|||
adminMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "roles" }, |
|||
{ "icon", "role" }, |
|||
{ "roles", new string[] { "AbpIdentity.Roles" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"users", |
|||
"users", |
|||
CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(2)), |
|||
"views/admin/users/index.vue", |
|||
"Manage Users", |
|||
"", |
|||
"Manage Users", |
|||
adminMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "users" }, |
|||
{ "icon", "users" }, |
|||
{ "roles", new string[] { "AbpIdentity.Users" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"organization-unit", |
|||
"organization-unit", |
|||
CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(3)), |
|||
"views/admin/organization-unit/index.vue", |
|||
"Manage Organization Units", |
|||
"", |
|||
"Manage Organization Units", |
|||
adminMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "organization-unit" }, |
|||
{ "icon", "organization-unit" }, |
|||
{ "roles", new string[] { "AbpIdentity.OrganizationUnits" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"claim-type", |
|||
"claim-type", |
|||
CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(4)), |
|||
"views/admin/claim-type/index.vue", |
|||
"Manage Organization Units", |
|||
"", |
|||
"Manage Organization Units", |
|||
adminMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "claim-type" }, |
|||
{ "icon", "claim-type" }, |
|||
{ "roles", new string[] { "AbpIdentity.IdentityClaimTypes" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"data-dictionary", |
|||
"data-dictionary", |
|||
CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(5)), |
|||
"views/admin/data-dictionary/index.vue", |
|||
"Manage Data Dictionarys", |
|||
"", |
|||
"Manage Data Dictionarys", |
|||
adminMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "data-dictionary" }, |
|||
{ "icon", "data-dictionary" }, |
|||
{ "roles", new string[] { "Platform.DataDictionary" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"settings", |
|||
"settings", |
|||
CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(6)), |
|||
"views/admin/settings/index.vue", |
|||
"Manage Settings", |
|||
"", |
|||
"Manage Settings", |
|||
adminMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "settings" }, |
|||
{ "icon", "settings" }, |
|||
{ "roles", new string[] { "AbpSettingManagement.Settings" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
} |
|||
|
|||
private async Task SeedSaasMenuAsync(Layout layout, Data data) |
|||
{ |
|||
var saasMenu = await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"saas", |
|||
"/saas", |
|||
CodeNumberGenerator.CreateCode(3), |
|||
layout.Path, |
|||
"Saas", |
|||
"", |
|||
"Saas", |
|||
null, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "saas" }, |
|||
{ "icon", "saas" }, |
|||
{ "alwaysShow", true } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"editions", |
|||
"editions", |
|||
CodeNumberGenerator.AppendCode(saasMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/edition/index.vue", |
|||
"Manage Editions", |
|||
"", |
|||
"Manage Editions", |
|||
saasMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "editions" }, |
|||
{ "icon", "editions" } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"tenants", |
|||
"tenants", |
|||
CodeNumberGenerator.AppendCode(saasMenu.Code, CodeNumberGenerator.CreateCode(2)), |
|||
"views/admin/tenants/index.vue", |
|||
"Manage Tenants", |
|||
"", |
|||
"Manage Tenants", |
|||
saasMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "tenants" }, |
|||
{ "icon", "tenants" } |
|||
}, |
|||
new string[] { "admin" }); |
|||
} |
|||
|
|||
private async Task SeedIdentityServerMenuAsync(Layout layout, Data data) |
|||
{ |
|||
var identityServerMenu = await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"identity-server", |
|||
"/identity-server", |
|||
CodeNumberGenerator.CreateCode(4), |
|||
layout.Path, |
|||
"Identity Server", |
|||
"", |
|||
"Identity Server", |
|||
null, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "identity-server" }, |
|||
{ "icon", "identity-server" }, |
|||
{ "alwaysShow", true }, |
|||
{ "roles", new string[]{ "AbpIdentityServer.Clients", "AbpIdentityServer.ApiResources", "AbpIdentityServer.IdentityResources" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"clients", |
|||
"clients", |
|||
CodeNumberGenerator.AppendCode(identityServerMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/identityServer/client/index.vue", |
|||
"Manage Clients", |
|||
"", |
|||
"Manage Clients", |
|||
identityServerMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "clients" }, |
|||
{ "icon", "clients" }, |
|||
{ "roles", new string[]{ "AbpIdentityServer.Clients" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"api-resources", |
|||
"api-resources", |
|||
CodeNumberGenerator.AppendCode(identityServerMenu.Code, CodeNumberGenerator.CreateCode(2)), |
|||
"views/admin/identityServer/api-resources/index.vue", |
|||
"Manage Api Resources", |
|||
"", |
|||
"Manage Api Resources", |
|||
identityServerMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "api-resources" }, |
|||
{ "icon", "api" }, |
|||
{ "roles", new string[]{ "AbpIdentityServer.ApiResources" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"identity-resources", |
|||
"identity-resources", |
|||
CodeNumberGenerator.AppendCode(identityServerMenu.Code, CodeNumberGenerator.CreateCode(2)), |
|||
"views/admin/identityServer/identity-resources/index.vue", |
|||
"Manage Identity Resources", |
|||
"", |
|||
"Manage Identity Resources", |
|||
identityServerMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "identity-resources" }, |
|||
{ "icon", "identity" }, |
|||
{ "roles", new string[]{ "AbpIdentityServer.IdentityResources" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
} |
|||
|
|||
private async Task SeedAuditingMenuAsync(Layout layout, Data data) |
|||
{ |
|||
var auditingMenu = await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"auditing", |
|||
"/auditing", |
|||
CodeNumberGenerator.CreateCode(5), |
|||
layout.Path, |
|||
"Auditing", |
|||
"", |
|||
"Auditing", |
|||
null, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "auditing" }, |
|||
{ "icon", "auditing" }, |
|||
{ "alwaysShow", true }, |
|||
{ "roles", new string[]{ "AbpAuditing.AuditLog", "AbpAuditing.SecurityLog" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"audit-log", |
|||
"audit-log", |
|||
CodeNumberGenerator.AppendCode(auditingMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/auditing/audit-log/index.vue", |
|||
"Manage AuditLog", |
|||
"", |
|||
"Manage AuditLog", |
|||
auditingMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "audit-log" }, |
|||
{ "icon", "audit-log" }, |
|||
{ "roles", new string[]{ "AbpAuditing.AuditLog" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"security-log", |
|||
"security-log", |
|||
CodeNumberGenerator.AppendCode(auditingMenu.Code, CodeNumberGenerator.CreateCode(2)), |
|||
"views/admin/auditing/security-log/index.vue", |
|||
"Manage SecurityLog", |
|||
"", |
|||
"Manage SecurityLog", |
|||
auditingMenu.Id, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "security-log" }, |
|||
{ "icon", "security-log" }, |
|||
{ "roles", new string[]{ "AbpAuditing.SecurityLog" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
} |
|||
|
|||
private async Task SeedContainerMenuAsync(Layout layout, Data data) |
|||
{ |
|||
var containerRoot = await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"container", |
|||
"/container", |
|||
CodeNumberGenerator.CreateCode(6), |
|||
layout.Path, |
|||
"Container", |
|||
"", |
|||
"Manage Container", |
|||
null, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "container" }, |
|||
{ "icon", "container" }, |
|||
{ "alwaysShow", true } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"layout", |
|||
"layout", |
|||
CodeNumberGenerator.AppendCode(containerRoot.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/layouts/index.vue", |
|||
"Manage Layouts", |
|||
"", |
|||
"Manage Layouts", |
|||
containerRoot.Id, |
|||
containerRoot.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "layout" }, |
|||
{ "icon", "layout" } |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"menus", |
|||
"menus", |
|||
CodeNumberGenerator.AppendCode(containerRoot.Code, CodeNumberGenerator.CreateCode(2)), |
|||
"views/admin/menus/index.vue", |
|||
"Manage Menus", |
|||
"", |
|||
"Manage Menus", |
|||
containerRoot.Id, |
|||
containerRoot.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "menus" }, |
|||
{ "icon", "menu" } |
|||
}, |
|||
new string[] { "admin" }); |
|||
} |
|||
|
|||
private async Task SeedApiGatewayMenuAsync(Layout layout, Data data) |
|||
{ |
|||
var apiGatewayMenu = await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"apigateway", |
|||
"/apigateway", |
|||
CodeNumberGenerator.CreateCode(7), |
|||
layout.Path, |
|||
"Manage Api Gateway", |
|||
"/group", |
|||
"Manage Api Gateway", |
|||
null, |
|||
layout.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "api-gateway" }, |
|||
{ "icon", "api-gateway" }, |
|||
{ "alwaysShow", true }, |
|||
{ "roles", new string[] { "ApiGateway.RouteGroup", "ApiGateway.Global", "ApiGateway.Route", "ApiGateway.DynamicRoute", "ApiGateway.AggregateRoute" } }, |
|||
}, |
|||
new string[] { "admin" }); |
|||
|
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"group", |
|||
"group", |
|||
CodeNumberGenerator.AppendCode(apiGatewayMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/apigateway/group.vue", |
|||
"Manage Groups", |
|||
"", |
|||
"Manage Groups", |
|||
apiGatewayMenu.Id, |
|||
apiGatewayMenu.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "group" }, |
|||
{ "icon", "group" }, |
|||
{ "roles", new string[] { "ApiGateway.RouteGroup" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"global", |
|||
"global", |
|||
CodeNumberGenerator.AppendCode(apiGatewayMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/apigateway/global.vue", |
|||
"Manage Globals", |
|||
"", |
|||
"Manage Globals", |
|||
apiGatewayMenu.Id, |
|||
apiGatewayMenu.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "global" }, |
|||
{ "icon", "global-setting" }, |
|||
{ "roles", new string[] { "ApiGateway.Global" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"route", |
|||
"route", |
|||
CodeNumberGenerator.AppendCode(apiGatewayMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/apigateway/route.vue", |
|||
"Manage Routes", |
|||
"", |
|||
"Manage Routes", |
|||
apiGatewayMenu.Id, |
|||
apiGatewayMenu.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "route" }, |
|||
{ "icon", "route" }, |
|||
{ "roles", new string[] { "ApiGateway.Route" } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
await SeedMenuAsync( |
|||
layout, |
|||
data, |
|||
"aggregate-route", |
|||
"aggregate-route", |
|||
CodeNumberGenerator.AppendCode(apiGatewayMenu.Code, CodeNumberGenerator.CreateCode(1)), |
|||
"views/admin/apigateway/aggregateRoute.vue", |
|||
"Manage Aggregate Routes", |
|||
"", |
|||
"Manage Aggregate Routes", |
|||
apiGatewayMenu.Id, |
|||
apiGatewayMenu.TenantId, |
|||
new Dictionary<string, object>() |
|||
{ |
|||
{ "title", "aggregate-route" }, |
|||
{ "icon", "aggregate" }, |
|||
{ "roles", new string[] { "ApiGateway.AggregateRoute " } } |
|||
}, |
|||
new string[] { "admin" }); |
|||
} |
|||
|
|||
private async Task<Menu> SeedMenuAsync( |
|||
Layout layout, |
|||
Data data, |
|||
string name, |
|||
string path, |
|||
string code, |
|||
string component, |
|||
string displayName, |
|||
string redirect = "", |
|||
string description = "", |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null, |
|||
Dictionary<string, object> meta = null, |
|||
string[] roles = null, |
|||
bool isPublic = false |
|||
) |
|||
{ |
|||
var menu = await RouteDataSeeder.SeedMenuAsync( |
|||
layout, |
|||
name, |
|||
path, |
|||
code, |
|||
component, |
|||
displayName, |
|||
redirect, |
|||
description, |
|||
parentId, |
|||
tenantId, |
|||
isPublic |
|||
); |
|||
foreach (var item in data.Items) |
|||
{ |
|||
menu.SetProperty(item.Name, item.DefaultValue); |
|||
} |
|||
if (meta != null) |
|||
{ |
|||
foreach (var item in meta) |
|||
{ |
|||
menu.SetProperty(item.Key, item.Value); |
|||
} |
|||
} |
|||
|
|||
if (roles != null) |
|||
{ |
|||
foreach (var role in roles) |
|||
{ |
|||
await RouteDataSeeder.SeedRoleMenuAsync(role, menu, tenantId); |
|||
} |
|||
} |
|||
|
|||
return menu; |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
namespace LINGYUN.Platform |
|||
{ |
|||
public static class AppPlatformDbProperties |
|||
public static class PlatformDbProperties |
|||
{ |
|||
public static string DbTablePrefix { get; set; } = "AppPlatform"; |
|||
|
|||
@ -0,0 +1,51 @@ |
|||
using LINGYUN.Platform.Layouts; |
|||
using LINGYUN.Platform.Menus; |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
public interface IRouteDataSeeder |
|||
{ |
|||
Task<Layout> SeedLayoutAsync( |
|||
string name, |
|||
string path, |
|||
string code, |
|||
string displayName, |
|||
Guid dataId, |
|||
PlatformType platformType = PlatformType.None, |
|||
string redirect = "", |
|||
string description = "", |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<Menu> SeedMenuAsync( |
|||
Layout layout, |
|||
string name, |
|||
string path, |
|||
string code, |
|||
string component, |
|||
string displayName, |
|||
string redirect = "", |
|||
string description = "", |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null, |
|||
bool isPublic = false, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task SeedUserMenuAsync( |
|||
Guid userId, |
|||
Menu menu, |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task SeedRoleMenuAsync( |
|||
string roleName, |
|||
Menu menu, |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
} |
|||
@ -1,78 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
public interface IRouteRepository : IBasicRepository<Route, Guid> |
|||
{ |
|||
Task<List<Route>> GetChildrenAsync( |
|||
Guid? parentId, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<List<Route>> GetAllChildrenWithParentCodeAsync( |
|||
string code, |
|||
Guid? parentId, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<Route> GetAsync( |
|||
string displayName, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<List<Route>> GetPagedListAsync( |
|||
string sorting = null, |
|||
int maxResultCount = int.MaxValue, |
|||
int skipCount = 0, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<List<Route>> GetRolesRouteAsync( |
|||
string roleName, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<List<Route>> GetUsersRouteAsync( |
|||
Guid userId, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task RemoveAllUsersRouteAsync( |
|||
Route route, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task RemoveUserRouteAsync( |
|||
Guid userId, |
|||
Route route, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task RemoveAllRolesRouteAsync( |
|||
Route route, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task RemoveRoleRouteAsync( |
|||
string roleName, |
|||
Route route, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<bool> IsInRouteAsync( |
|||
string roleName, |
|||
Route route, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<bool> IsInRouteAsync( |
|||
Guid userId, |
|||
Route route, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
} |
|||
@ -1,31 +0,0 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
public class RoleRoute : FullAuditedEntity<int>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual string RoleName { get; protected set; } |
|||
public virtual Guid RouteId { get; protected set; } |
|||
protected RoleRoute() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public RoleRoute(int id) |
|||
{ |
|||
Id = id; |
|||
} |
|||
|
|||
public RoleRoute(Guid routeId, [NotNull] string roleName, Guid? tenantId = null) |
|||
{ |
|||
RouteId = routeId; |
|||
TenantId = tenantId; |
|||
RoleName = Check.NotNullOrWhiteSpace(roleName, nameof(roleName)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,193 +1,79 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
// TODO: 因为abp的菜单设计模式是按照权限分配的,是否可以去掉路由的后台配置?
|
|||
// 按钮、菜单权限可以在子权限中定义
|
|||
// 数据权限需要DbContext拦截器或者创建一个数据过滤仓储的抽象类,需要过滤数据权限的继承自此仓储
|
|||
public class Route : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
/// <summary>
|
|||
/// 不管是布局还是视图或者页面,都作为路由的实现,因此抽象一个路由实体<br/>
|
|||
/// 注意:这是基于 Vue Router 的路由规则设计的实体,详情:https://router.vuejs.org/zh/api/#routes
|
|||
/// </summary>
|
|||
public abstract class Route : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
/// <summary>
|
|||
/// 租户标识
|
|||
/// </summary>
|
|||
public virtual Guid? TenantId { get; protected set; } |
|||
/// <summary>
|
|||
/// 编号
|
|||
/// 路径
|
|||
/// </summary>
|
|||
public virtual string Code { get; set; } |
|||
public virtual string Path { get; set; } |
|||
/// <summary>
|
|||
/// 名称
|
|||
/// </summary>
|
|||
public virtual string Name { get; protected set; } |
|||
/// <summary>
|
|||
/// 全名
|
|||
/// </summary>
|
|||
public virtual string FullName { get; set; } |
|||
public virtual string Name { get; set; } |
|||
/// <summary>
|
|||
/// 显示名称
|
|||
/// </summary>
|
|||
public virtual string DisplayName { get; protected set; } |
|||
/// <summary>
|
|||
/// 平台标识
|
|||
/// </summary>
|
|||
public virtual PlatformType PlatformType { get; set; } |
|||
public virtual string DisplayName { get; set; } |
|||
/// <summary>
|
|||
/// 说明
|
|||
/// </summary>
|
|||
public virtual string Description { get; set; } |
|||
/// <summary>
|
|||
/// 图标
|
|||
/// </summary>
|
|||
public virtual string Icon { get; protected set; } |
|||
/// <summary>
|
|||
/// 路由地址
|
|||
/// </summary>
|
|||
public virtual string LinkUrl { get; protected set; } |
|||
/// <summary>
|
|||
/// 是否菜单
|
|||
/// </summary>
|
|||
public virtual bool IsMenu { get; set; } |
|||
/// <summary>
|
|||
/// 是否工具栏
|
|||
/// </summary>
|
|||
public virtual bool IsToolBar { get; set; } |
|||
/// <summary>
|
|||
/// 是否侧边栏
|
|||
/// </summary>
|
|||
public virtual bool IsSideBar { get; set; } |
|||
/// <summary>
|
|||
/// 是否公共路由
|
|||
/// </summary>
|
|||
public virtual bool IsPublic { get; set; } |
|||
/// <summary>
|
|||
/// 是否内置
|
|||
/// 重定向路径
|
|||
/// </summary>
|
|||
public virtual bool IsStatic { get; set; } |
|||
/// <summary>
|
|||
/// 总是显示根菜单
|
|||
/// </summary>
|
|||
public virtual bool AlwaysShow { get; set; } |
|||
/// <summary>
|
|||
/// 父级标识
|
|||
/// </summary>
|
|||
public virtual Guid? ParentId { get; set; } |
|||
protected Route() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public Route(Guid id, string name, string displayName, string url) |
|||
{ |
|||
Id = id; |
|||
LinkToUrl(url); |
|||
ChangeName(name, displayName); |
|||
} |
|||
public virtual string Redirect { get; set; } |
|||
|
|||
public void LinkToUrl([NotNull] string linkUrl) |
|||
{ |
|||
LinkUrl = Check.NotNullOrWhiteSpace(linkUrl, nameof(linkUrl)); |
|||
} |
|||
protected Route() { } |
|||
|
|||
public void ChangeIcon(string icon) |
|||
protected Route( |
|||
[NotNull] Guid id, |
|||
[NotNull] string path, |
|||
[NotNull] string name, |
|||
[NotNull] string displayName, |
|||
[CanBeNull] string redirect = "", |
|||
[CanBeNull] string description = "", |
|||
[CanBeNull] Guid? tenantId = null) |
|||
: base(id) |
|||
{ |
|||
Icon = icon; |
|||
} |
|||
Check.NotNullOrWhiteSpace(path, nameof(path)); |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); |
|||
|
|||
public void ChangeName([NotNull] string name, [CanBeNull] string displayName) |
|||
{ |
|||
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
Path = path; |
|||
Name = name; |
|||
DisplayName = displayName; |
|||
Redirect = redirect; |
|||
Description = description; |
|||
TenantId = tenantId; |
|||
} |
|||
|
|||
public static string CreateCode(params int[] numbers) |
|||
public override int GetHashCode() |
|||
{ |
|||
if (numbers.IsNullOrEmpty()) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return numbers.Select(number => number.ToString(new string('0', RouteConsts.CodeUnitLength))).JoinAsString("."); |
|||
return Name.GetHashCode(); |
|||
} |
|||
|
|||
public static string AppendCode(string parentCode, string childCode) |
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (childCode.IsNullOrEmpty()) |
|||
if (obj == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(childCode), "childCode can not be null or empty."); |
|||
return false; |
|||
} |
|||
|
|||
if (parentCode.IsNullOrEmpty()) |
|||
if (obj is Route route) |
|||
{ |
|||
return childCode; |
|||
return route.Name.Equals(Name, StringComparison.InvariantCultureIgnoreCase); |
|||
} |
|||
|
|||
return parentCode + "." + childCode; |
|||
} |
|||
|
|||
public static string GetRelativeCode(string code, string parentCode) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
if (parentCode.IsNullOrEmpty()) |
|||
{ |
|||
return code; |
|||
} |
|||
|
|||
if (code.Length == parentCode.Length) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return code.Substring(parentCode.Length + 1); |
|||
} |
|||
|
|||
public static string CalculateNextCode(string code) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
var parentCode = GetParentCode(code); |
|||
var lastUnitCode = GetLastUnitCode(code); |
|||
|
|||
return AppendCode(parentCode, CreateCode(Convert.ToInt32(lastUnitCode) + 1)); |
|||
} |
|||
|
|||
public static string GetLastUnitCode(string code) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
var splittedCode = code.Split('.'); |
|||
return splittedCode[splittedCode.Length - 1]; |
|||
} |
|||
|
|||
public static string GetParentCode(string code) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
var splittedCode = code.Split('.'); |
|||
if (splittedCode.Length == 1) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return splittedCode.Take(splittedCode.Length - 1).JoinAsString("."); |
|||
return base.Equals(obj); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,152 @@ |
|||
using LINGYUN.Platform.Layouts; |
|||
using LINGYUN.Platform.Menus; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
public class RouteDataSeeder : IRouteDataSeeder, ITransientDependency |
|||
{ |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
protected ILayoutRepository LayoutRepository { get; } |
|||
protected IMenuRepository MenuRepository { get; } |
|||
protected IUserMenuRepository UserMenuRepository { get; } |
|||
protected IRoleMenuRepository RoleMenuRepository { get; } |
|||
|
|||
public RouteDataSeeder( |
|||
IGuidGenerator guidGenerator, |
|||
IMenuRepository menuRepository, |
|||
ILayoutRepository layoutRepository, |
|||
IUserMenuRepository userMenuRepository, |
|||
IRoleMenuRepository roleMenuRepository) |
|||
{ |
|||
GuidGenerator = guidGenerator; |
|||
MenuRepository = menuRepository; |
|||
LayoutRepository = layoutRepository; |
|||
UserMenuRepository = userMenuRepository; |
|||
RoleMenuRepository = roleMenuRepository; |
|||
} |
|||
|
|||
public virtual async Task<Layout> SeedLayoutAsync( |
|||
string name, |
|||
string path, |
|||
string code, |
|||
string displayName, |
|||
Guid dataId, |
|||
PlatformType platformType = PlatformType.None, |
|||
string redirect = "", |
|||
string description = "", |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var layout = await LayoutRepository.FindByNameAsync(name, cancellationToken: cancellationToken); |
|||
if (layout == null) |
|||
{ |
|||
layout = new Layout( |
|||
GuidGenerator.Create(), |
|||
path, |
|||
name, |
|||
code, |
|||
displayName, |
|||
dataId, |
|||
platformType, |
|||
redirect, |
|||
description, |
|||
tenantId); |
|||
layout = await LayoutRepository.InsertAsync(layout, cancellationToken: cancellationToken); |
|||
} |
|||
return layout; |
|||
} |
|||
|
|||
public virtual async Task<Menu> SeedMenuAsync( |
|||
Layout layout, |
|||
string name, |
|||
string path, |
|||
string code, |
|||
string component, |
|||
string displayName, |
|||
string redirect = "", |
|||
string description = "", |
|||
Guid? parentId = null, |
|||
Guid? tenantId = null, |
|||
bool isPublic = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
if (parentId.HasValue) |
|||
{ |
|||
var children = await MenuRepository.GetChildrenAsync(parentId); |
|||
var childMenu = children.FirstOrDefault(x => x.Name == name); |
|||
if (childMenu != null) |
|||
{ |
|||
return childMenu; |
|||
} |
|||
} |
|||
var menu = await MenuRepository.FindByNameAsync(name, cancellationToken: cancellationToken); |
|||
if (menu == null) |
|||
{ |
|||
menu = new Menu( |
|||
GuidGenerator.Create(), |
|||
layout.Id, |
|||
path, |
|||
name, |
|||
code, |
|||
component, |
|||
displayName, |
|||
redirect, |
|||
description, |
|||
layout.PlatformType, |
|||
parentId, |
|||
tenantId) |
|||
{ |
|||
IsPublic = isPublic |
|||
}; |
|||
|
|||
menu = await MenuRepository.InsertAsync(menu, cancellationToken: cancellationToken); |
|||
} |
|||
|
|||
return menu; |
|||
} |
|||
|
|||
public virtual async Task SeedRoleMenuAsync( |
|||
string roleName, |
|||
Menu menu, |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
if (! await RoleMenuRepository.RoleHasInMenuAsync(roleName, menu.Name, cancellationToken)) |
|||
{ |
|||
var roleMenu = new RoleMenu(menu.Id, roleName, tenantId); |
|||
await RoleMenuRepository.InsertAsync(roleMenu); |
|||
|
|||
var childrens = await MenuRepository.GetChildrenAsync(menu.Id); |
|||
foreach (var children in childrens) |
|||
{ |
|||
await SeedRoleMenuAsync(roleName, children, tenantId, cancellationToken); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public virtual async Task SeedUserMenuAsync( |
|||
Guid userId, |
|||
Menu menu, |
|||
Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
if (!await UserMenuRepository.UserHasInMenuAsync(userId, menu.Name, cancellationToken)) |
|||
{ |
|||
var userMenu = new UserMenu(menu.Id, userId, tenantId); |
|||
await UserMenuRepository.InsertAsync(userMenu); |
|||
|
|||
var childrens = await MenuRepository.GetChildrenAsync(menu.Id); |
|||
foreach (var children in childrens) |
|||
{ |
|||
await SeedUserMenuAsync(userId, children, tenantId, cancellationToken); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,139 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
public class RouteManager : DomainService |
|||
{ |
|||
protected IRouteRepository RouteRepository { get; } |
|||
public RouteManager( |
|||
IRouteRepository routeRepository) |
|||
{ |
|||
RouteRepository = routeRepository; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task CreateAsync(Route route) |
|||
{ |
|||
route.Code = await GetNextChildCodeAsync(route.ParentId); |
|||
await RouteRepository.InsertAsync(route); |
|||
} |
|||
|
|||
public virtual async Task UpdateAsync(Route route) |
|||
{ |
|||
await RouteRepository.UpdateAsync(route); |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task DeleteAsync(Guid id) |
|||
{ |
|||
var children = await FindChildrenAsync(id, true); |
|||
|
|||
foreach (var child in children) |
|||
{ |
|||
await RouteRepository.RemoveAllUsersRouteAsync(child); |
|||
await RouteRepository.RemoveAllRolesRouteAsync(child); |
|||
await RouteRepository.DeleteAsync(child); |
|||
} |
|||
|
|||
var organizationUnit = await RouteRepository.GetAsync(id); |
|||
|
|||
await RouteRepository.RemoveAllUsersRouteAsync(organizationUnit); |
|||
await RouteRepository.RemoveAllRolesRouteAsync(organizationUnit); |
|||
await RouteRepository.DeleteAsync(id); |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task MoveAsync(Guid id, Guid? parentId) |
|||
{ |
|||
var route = await RouteRepository.GetAsync(id); |
|||
if (route.ParentId == parentId) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var children = await FindChildrenAsync(id, true); |
|||
|
|||
var oldCode = route.Code; |
|||
|
|||
route.Code = await GetNextChildCodeAsync(parentId); |
|||
route.ParentId = parentId; |
|||
|
|||
foreach (var child in children) |
|||
{ |
|||
child.Code = Route.AppendCode(route.Code, Route.GetRelativeCode(child.Code, oldCode)); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task RemoveRoleFromRouteAsync(string roleName, Route route) |
|||
{ |
|||
await RouteRepository.RemoveRoleRouteAsync(roleName, route); |
|||
} |
|||
|
|||
public virtual async Task RemoveUserFromRouteAsync(Guid userId, Route route) |
|||
{ |
|||
await RouteRepository.RemoveUserRouteAsync(userId, route); |
|||
} |
|||
|
|||
public virtual async Task<string> GetNextChildCodeAsync(Guid? parentId) |
|||
{ |
|||
var lastChild = await GetLastChildOrNullAsync(parentId); |
|||
if (lastChild != null) |
|||
{ |
|||
return Route.CalculateNextCode(lastChild.Code); |
|||
} |
|||
|
|||
var parentCode = parentId != null |
|||
? await GetCodeOrDefaultAsync(parentId.Value) |
|||
: null; |
|||
|
|||
return Route.AppendCode( |
|||
parentCode, |
|||
Route.CreateCode(1) |
|||
); |
|||
} |
|||
|
|||
public virtual async Task<Route> GetLastChildOrNullAsync(Guid? parentId) |
|||
{ |
|||
var children = await RouteRepository.GetChildrenAsync(parentId); |
|||
return children.OrderBy(c => c.Code).LastOrDefault(); |
|||
} |
|||
|
|||
public virtual async Task<string> GetCodeOrDefaultAsync(Guid id) |
|||
{ |
|||
var ou = await RouteRepository.GetAsync(id); |
|||
return ou?.Code; |
|||
} |
|||
|
|||
public async Task<List<Route>> FindChildrenAsync(Guid? parentId, bool recursive = false) |
|||
{ |
|||
if (!recursive) |
|||
{ |
|||
return await RouteRepository.GetChildrenAsync(parentId); |
|||
} |
|||
|
|||
if (!parentId.HasValue) |
|||
{ |
|||
return await RouteRepository.GetListAsync(); |
|||
} |
|||
|
|||
var code = await GetCodeOrDefaultAsync(parentId.Value); |
|||
|
|||
return await RouteRepository.GetAllChildrenWithParentCodeAsync(code, parentId); |
|||
} |
|||
|
|||
public virtual async Task<bool> IsInRouteAsync(Guid userId, Route route) |
|||
{ |
|||
return await RouteRepository.IsInRouteAsync(userId, route); |
|||
} |
|||
|
|||
public virtual async Task<bool> IsInRouteAsync(string roleName, Route route) |
|||
{ |
|||
return await RouteRepository.IsInRouteAsync(roleName, route); |
|||
} |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Platform.Routes |
|||
{ |
|||
public class UserRoute : FullAuditedEntity<int>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual Guid UserId { get; protected set; } |
|||
public virtual Guid RouteId { get; protected set; } |
|||
protected UserRoute() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public UserRoute(int id) |
|||
{ |
|||
Id = id; |
|||
} |
|||
|
|||
public UserRoute(Guid routeId, Guid userId, Guid? tenantId = null) |
|||
{ |
|||
UserId = userId; |
|||
RouteId = routeId; |
|||
TenantId = tenantId; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace LINGYUN.Platform.Utils |
|||
{ |
|||
public static class CodeNumberGenerator |
|||
{ |
|||
public static string CreateCode(params int[] numbers) |
|||
{ |
|||
if (numbers.IsNullOrEmpty()) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return numbers.Select(number => number.ToString(new string(PlatformConsts.CodePrefix, PlatformConsts.CodeUnitLength))).JoinAsString("."); |
|||
} |
|||
|
|||
public static string AppendCode(string parentCode, string childCode) |
|||
{ |
|||
if (childCode.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(childCode), "childCode can not be null or empty."); |
|||
} |
|||
|
|||
if (parentCode.IsNullOrEmpty()) |
|||
{ |
|||
return childCode; |
|||
} |
|||
|
|||
return parentCode + "." + childCode; |
|||
} |
|||
|
|||
public static string GetRelativeCode(string code, string parentCode) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
if (parentCode.IsNullOrEmpty()) |
|||
{ |
|||
return code; |
|||
} |
|||
|
|||
if (code.Length == parentCode.Length) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return code.Substring(parentCode.Length + 1); |
|||
} |
|||
|
|||
public static string CalculateNextCode(string code) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
var parentCode = GetParentCode(code); |
|||
var lastUnitCode = GetLastCode(code); |
|||
|
|||
return AppendCode(parentCode, CreateCode(Convert.ToInt32(lastUnitCode) + 1)); |
|||
} |
|||
|
|||
public static string GetLastCode(string code) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
var splittedCode = code.Split('.'); |
|||
return splittedCode[splittedCode.Length - 1]; |
|||
} |
|||
|
|||
public static string GetParentCode(string code) |
|||
{ |
|||
if (code.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(code), "code can not be null or empty."); |
|||
} |
|||
|
|||
var splittedCode = code.Split('.'); |
|||
if (splittedCode.Length == 1) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return splittedCode.Take(splittedCode.Length - 1).JoinAsString("."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
using LINGYUN.Platform.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Platform.Datas |
|||
{ |
|||
public class EfCoreDataRepository : EfCoreRepository<PlatformDbContext, Data, Guid>, IDataRepository |
|||
{ |
|||
public EfCoreDataRepository( |
|||
IDbContextProvider<PlatformDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public virtual async Task<Data> FindByNameAsync( |
|||
string name, |
|||
bool includeDetails = true, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.IncludeDetails(includeDetails) |
|||
.Where(x => x.Name == name) |
|||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Data>> GetChildrenAsync( |
|||
Guid? parentId, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.IncludeDetails(includeDetails) |
|||
.Where(x => x.ParentId == parentId) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<int> GetCountAsync( |
|||
string filter = "", |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.WhereIf(!filter.IsNullOrWhiteSpace(), x => |
|||
x.Code.Contains(filter) || x.Description.Contains(filter) || |
|||
x.DisplayName.Contains(filter) || x.Name.Contains(filter)) |
|||
.CountAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Data>> GetPagedListAsync( |
|||
string filter = "", |
|||
string sotring = "Code", |
|||
bool includeDetails = false, |
|||
int skipCount = 0, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
sotring ??= nameof(Data.Code); |
|||
|
|||
return await DbSet |
|||
.IncludeDetails(includeDetails) |
|||
.WhereIf(!filter.IsNullOrWhiteSpace(), x => |
|||
x.Code.Contains(filter) || x.Description.Contains(filter) || |
|||
x.DisplayName.Contains(filter) || x.Name.Contains(filter)) |
|||
.OrderBy(sotring) |
|||
.PageBy(skipCount, maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public override IQueryable<Data> WithDetails() |
|||
{ |
|||
return GetQueryable().IncludeDetails(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,143 +0,0 @@ |
|||
using LINGYUN.Platform.Routes; |
|||
using LINGYUN.Platform.Versions; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace LINGYUN.Platform.EntityFrameworkCore |
|||
{ |
|||
public static class AppPlatformDbContextModelBuilderExtensions |
|||
{ |
|||
public static void ConfigurePlatform( |
|||
this ModelBuilder builder, |
|||
Action<AppPlatformModelBuilderConfigurationOptions> optionsAction = null) |
|||
{ |
|||
Check.NotNull(builder, nameof(builder)); |
|||
|
|||
var options = new AppPlatformModelBuilderConfigurationOptions( |
|||
AppPlatformDbProperties.DbTablePrefix, |
|||
AppPlatformDbProperties.DbSchema |
|||
); |
|||
|
|||
optionsAction?.Invoke(options); |
|||
|
|||
builder.Entity<Route>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "Route"); |
|||
|
|||
x.Property(p => p.Code) |
|||
.IsRequired() |
|||
.HasMaxLength(RouteConsts.MaxCodeLength) |
|||
.HasColumnName(nameof(Route.Code)); |
|||
x.Property(p => p.DisplayName) |
|||
.IsRequired() |
|||
.HasMaxLength(RouteConsts.MaxDisplayNameLength) |
|||
.HasColumnName(nameof(Route.DisplayName)); |
|||
x.Property(p => p.LinkUrl) |
|||
.IsRequired() |
|||
.HasMaxLength(RouteConsts.MaxLinkUrlLength) |
|||
.HasColumnName(nameof(Route.LinkUrl)); |
|||
x.Property(p => p.Name) |
|||
.IsRequired() |
|||
.HasMaxLength(RouteConsts.MaxNameLength) |
|||
.HasColumnName(nameof(Route.Name)); |
|||
|
|||
x.Property(p => p.Icon) |
|||
.HasMaxLength(RouteConsts.MaxIconLength) |
|||
.HasColumnName(nameof(Route.Icon)); |
|||
x.Property(p => p.FullName) |
|||
.HasMaxLength(RouteConsts.MaxFullNameLength) |
|||
.HasColumnName(nameof(Route.FullName)); |
|||
x.Property(p => p.Description) |
|||
.HasMaxLength(RouteConsts.MaxDescriptionLength) |
|||
.HasColumnName(nameof(Route.Description)); |
|||
|
|||
x.ConfigureByConvention(); |
|||
|
|||
x.HasMany<Route>().WithOne().HasForeignKey(p => p.ParentId); |
|||
|
|||
x.HasIndex(i => i.Code); |
|||
}); |
|||
|
|||
builder.Entity<RoleRoute>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "RoleRoute"); |
|||
|
|||
x.Property(p => p.RoleName) |
|||
.IsRequired() |
|||
.HasMaxLength(RoleRouteConsts.MaxRoleNameLength) |
|||
.HasColumnName(nameof(RoleRoute.RoleName)); |
|||
|
|||
x.ConfigureMultiTenant(); |
|||
|
|||
x.HasIndex(i => new { i.RoleName, i.RouteId }); |
|||
}); |
|||
|
|||
builder.Entity<UserRoute>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "UserRoute"); |
|||
|
|||
x.ConfigureMultiTenant(); |
|||
|
|||
x.HasIndex(i => new { i.UserId, i.RouteId }); |
|||
}); |
|||
|
|||
builder.Entity<AppVersion>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "Version", options.Schema); |
|||
|
|||
x.Property(p => p.Title) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(AppVersion.Title)) |
|||
.HasMaxLength(AppVersionConsts.MaxTitleLength); |
|||
x.Property(p => p.Version) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(AppVersion.Version)) |
|||
.HasMaxLength(AppVersionConsts.MaxVersionLength); |
|||
|
|||
x.Property(p => p.Description) |
|||
.HasColumnName(nameof(AppVersion.Description)) |
|||
.HasMaxLength(AppVersionConsts.MaxDescriptionLength); |
|||
|
|||
x.ConfigureByConvention(); |
|||
|
|||
x.HasIndex(i => i.Version); |
|||
|
|||
x.HasMany(p => p.Files) |
|||
.WithOne(q => q.AppVersion) |
|||
.HasPrincipalKey(pk => pk.Id) |
|||
.HasForeignKey(fk => fk.AppVersionId) |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
builder.Entity<VersionFile>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "VersionFile", options.Schema); |
|||
|
|||
x.Property(p => p.Name) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(VersionFile.Name)) |
|||
.HasMaxLength(VersionFileConsts.MaxNameLength); |
|||
x.Property(p => p.SHA256) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(VersionFile.SHA256)) |
|||
.HasMaxLength(VersionFileConsts.MaxSHA256Length); |
|||
x.Property(p => p.Version) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(VersionFile.Version)) |
|||
.HasMaxLength(VersionFileConsts.MaxVersionLength); |
|||
|
|||
x.Property(p => p.Path) |
|||
.HasColumnName(nameof(VersionFile.Path)) |
|||
.HasMaxLength(VersionFileConsts.MaxPathLength); |
|||
|
|||
x.ConfigureAudited(); |
|||
x.ConfigureMultiTenant(); |
|||
|
|||
x.HasIndex(i => new { i.Path, i.Name, i.Version }).IsUnique(); |
|||
|
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,249 @@ |
|||
using JetBrains.Annotations; |
|||
using LINGYUN.Platform.Datas; |
|||
using LINGYUN.Platform.Layouts; |
|||
using LINGYUN.Platform.Menus; |
|||
using LINGYUN.Platform.Routes; |
|||
using LINGYUN.Platform.Versions; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace LINGYUN.Platform.EntityFrameworkCore |
|||
{ |
|||
public static class PlatformDbContextModelBuilderExtensions |
|||
{ |
|||
public static void ConfigurePlatform( |
|||
this ModelBuilder builder, |
|||
Action<PlatformModelBuilderConfigurationOptions> optionsAction = null) |
|||
{ |
|||
Check.NotNull(builder, nameof(builder)); |
|||
|
|||
var options = new PlatformModelBuilderConfigurationOptions( |
|||
PlatformDbProperties.DbTablePrefix, |
|||
PlatformDbProperties.DbSchema |
|||
); |
|||
|
|||
optionsAction?.Invoke(options); |
|||
|
|||
builder.Entity<Layout>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "Layouts", options.Schema); |
|||
|
|||
b.ConfigureRoute(); |
|||
}); |
|||
|
|||
builder.Entity<Menu>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "Menus", options.Schema); |
|||
|
|||
b.ConfigureRoute(); |
|||
|
|||
b.Property(p => p.Component) |
|||
.HasMaxLength(MenuConsts.MaxComponentLength) |
|||
.HasColumnName(nameof(Menu.Component)) |
|||
.IsRequired(); |
|||
b.Property(p => p.Code) |
|||
.HasMaxLength(MenuConsts.MaxCodeLength) |
|||
.HasColumnName(nameof(Menu.Code)) |
|||
.IsRequired(); |
|||
}); |
|||
|
|||
builder.Entity<RoleMenu>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "RoleMenus"); |
|||
|
|||
x.Property(p => p.RoleName) |
|||
.IsRequired() |
|||
.HasMaxLength(RoleRouteConsts.MaxRoleNameLength) |
|||
.HasColumnName(nameof(RoleMenu.RoleName)); |
|||
|
|||
x.ConfigureByConvention(); |
|||
|
|||
x.HasIndex(i => new { i.RoleName, i.MenuId }); |
|||
}); |
|||
|
|||
builder.Entity<UserMenu>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "UserMenus"); |
|||
|
|||
x.ConfigureByConvention(); |
|||
|
|||
x.HasIndex(i => new { i.UserId, i.MenuId }); |
|||
}); |
|||
|
|||
builder.Entity<Data>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "Datas"); |
|||
|
|||
x.Property(p => p.Code) |
|||
.HasMaxLength(DataConsts.MaxCodeLength) |
|||
.HasColumnName(nameof(Data.Code)) |
|||
.IsRequired(); |
|||
x.Property(p => p.Name) |
|||
.HasMaxLength(DataConsts.MaxNameLength) |
|||
.HasColumnName(nameof(Data.Name)) |
|||
.IsRequired(); |
|||
x.Property(p => p.DisplayName) |
|||
.HasMaxLength(DataConsts.MaxDisplayNameLength) |
|||
.HasColumnName(nameof(Data.DisplayName)) |
|||
.IsRequired(); |
|||
x.Property(p => p.Description) |
|||
.HasMaxLength(DataConsts.MaxDescriptionLength) |
|||
.HasColumnName(nameof(Data.Description)); |
|||
|
|||
x.ConfigureByConvention(); |
|||
|
|||
x.HasMany(p => p.Items) |
|||
.WithOne() |
|||
.HasForeignKey(fk => fk.DataId) |
|||
.IsRequired(); |
|||
|
|||
x.HasIndex(i => new { i.Name }); |
|||
}); |
|||
|
|||
builder.Entity<DataItem>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "DataItems"); |
|||
|
|||
x.Property(p => p.DefaultValue) |
|||
.HasMaxLength(DataItemConsts.MaxValueLength) |
|||
.HasColumnName(nameof(DataItem.DefaultValue)); |
|||
x.Property(p => p.Name) |
|||
.HasMaxLength(DataItemConsts.MaxNameLength) |
|||
.HasColumnName(nameof(DataItem.Name)) |
|||
.IsRequired(); |
|||
x.Property(p => p.DisplayName) |
|||
.HasMaxLength(DataItemConsts.MaxDisplayNameLength) |
|||
.HasColumnName(nameof(DataItem.DisplayName)) |
|||
.IsRequired(); |
|||
x.Property(p => p.Description) |
|||
.HasMaxLength(DataItemConsts.MaxDescriptionLength) |
|||
.HasColumnName(nameof(DataItem.Description)); |
|||
|
|||
x.Property(p => p.AllowBeNull).HasDefaultValue(true); |
|||
|
|||
x.ConfigureByConvention(); |
|||
|
|||
x.HasIndex(i => new { i.Name }); |
|||
}); |
|||
|
|||
|
|||
builder.Entity<AppVersion>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "Version", options.Schema); |
|||
|
|||
x.Property(p => p.Title) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(AppVersion.Title)) |
|||
.HasMaxLength(AppVersionConsts.MaxTitleLength); |
|||
x.Property(p => p.Version) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(AppVersion.Version)) |
|||
.HasMaxLength(AppVersionConsts.MaxVersionLength); |
|||
|
|||
x.Property(p => p.Description) |
|||
.HasColumnName(nameof(AppVersion.Description)) |
|||
.HasMaxLength(AppVersionConsts.MaxDescriptionLength); |
|||
|
|||
x.ConfigureByConvention(); |
|||
|
|||
x.HasIndex(i => i.Version); |
|||
|
|||
x.HasMany(p => p.Files) |
|||
.WithOne(q => q.AppVersion) |
|||
.HasPrincipalKey(pk => pk.Id) |
|||
.HasForeignKey(fk => fk.AppVersionId) |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
builder.Entity<VersionFile>(x => |
|||
{ |
|||
x.ToTable(options.TablePrefix + "VersionFile", options.Schema); |
|||
|
|||
x.Property(p => p.Name) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(VersionFile.Name)) |
|||
.HasMaxLength(VersionFileConsts.MaxNameLength); |
|||
x.Property(p => p.SHA256) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(VersionFile.SHA256)) |
|||
.HasMaxLength(VersionFileConsts.MaxSHA256Length); |
|||
x.Property(p => p.Version) |
|||
.IsRequired() |
|||
.HasColumnName(nameof(VersionFile.Version)) |
|||
.HasMaxLength(VersionFileConsts.MaxVersionLength); |
|||
|
|||
x.Property(p => p.Path) |
|||
.HasColumnName(nameof(VersionFile.Path)) |
|||
.HasMaxLength(VersionFileConsts.MaxPathLength); |
|||
|
|||
x.ConfigureAudited(); |
|||
x.ConfigureMultiTenant(); |
|||
|
|||
x.HasIndex(i => new { i.Path, i.Name, i.Version }).IsUnique(); |
|||
|
|||
}); |
|||
} |
|||
|
|||
public static EntityTypeBuilder<TRoute> ConfigureRoute<TRoute>( |
|||
this EntityTypeBuilder<TRoute> builder) |
|||
where TRoute : Route |
|||
{ |
|||
builder |
|||
.Property(p => p.DisplayName) |
|||
.HasMaxLength(RouteConsts.MaxDisplayNameLength) |
|||
.HasColumnName(nameof(Route.DisplayName)) |
|||
.IsRequired(); |
|||
builder |
|||
.Property(p => p.Name) |
|||
.HasMaxLength(RouteConsts.MaxNameLength) |
|||
.HasColumnName(nameof(Route.Name)) |
|||
.IsRequired(); |
|||
builder |
|||
.Property(p => p.Path) |
|||
.HasMaxLength(RouteConsts.MaxPathLength) |
|||
.HasColumnName(nameof(Route.Path)); |
|||
builder |
|||
.Property(p => p.Redirect) |
|||
.HasMaxLength(RouteConsts.MaxRedirectLength) |
|||
.HasColumnName(nameof(Route.Redirect)); |
|||
|
|||
builder.ConfigureByConvention(); |
|||
|
|||
return builder; |
|||
} |
|||
|
|||
public static OwnedNavigationBuilder<TEntity, TRoute> ConfigureRoute<TEntity, TRoute>( |
|||
[NotNull] this OwnedNavigationBuilder<TEntity, TRoute> builder, |
|||
[CanBeNull] string tablePrefix = "", |
|||
[CanBeNull] string schema = null) |
|||
where TEntity : class |
|||
where TRoute : Route |
|||
{ |
|||
builder.ToTable(tablePrefix + "Routes", schema); |
|||
|
|||
builder |
|||
.Property(p => p.DisplayName) |
|||
.HasMaxLength(RouteConsts.MaxDisplayNameLength) |
|||
.HasColumnName(nameof(Route.DisplayName)) |
|||
.IsRequired(); |
|||
builder |
|||
.Property(p => p.Name) |
|||
.HasMaxLength(RouteConsts.MaxNameLength) |
|||
.HasColumnName(nameof(Route.Name)) |
|||
.IsRequired(); |
|||
builder |
|||
.Property(p => p.Path) |
|||
.HasMaxLength(RouteConsts.MaxPathLength) |
|||
.HasColumnName(nameof(Route.Path)); |
|||
builder |
|||
.Property(p => p.Redirect) |
|||
.HasMaxLength(RouteConsts.MaxRedirectLength) |
|||
.HasColumnName(nameof(Route.Redirect)); |
|||
|
|||
return builder; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using LINGYUN.Platform.Datas; |
|||
using LINGYUN.Platform.Layouts; |
|||
using LINGYUN.Platform.Menus; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System.Linq; |
|||
|
|||
namespace LINGYUN.Platform.EntityFrameworkCore |
|||
{ |
|||
public static class PlatformEfCoreQueryableExtensions |
|||
{ |
|||
|
|||
public static IQueryable<Layout> IncludeDetails(this IQueryable<Layout> queryable, bool include = true) |
|||
{ |
|||
if (!include) |
|||
{ |
|||
return queryable; |
|||
} |
|||
|
|||
return queryable; |
|||
} |
|||
|
|||
public static IQueryable<Data> IncludeDetails(this IQueryable<Data> queryable, bool include = true) |
|||
{ |
|||
if (!include) |
|||
{ |
|||
return queryable; |
|||
} |
|||
|
|||
return queryable |
|||
//.AsSplitQuery()
|
|||
.Include(x => x.Items); |
|||
} |
|||
} |
|||
} |
|||
4
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/AppPlatformModelBuilderConfigurationOptions.cs → aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformModelBuilderConfigurationOptions.cs
4
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/AppPlatformModelBuilderConfigurationOptions.cs → aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformModelBuilderConfigurationOptions.cs
@ -0,0 +1,82 @@ |
|||
using LINGYUN.Platform.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Platform.Layouts |
|||
{ |
|||
public class EfCoreLayoutRepository : EfCoreRepository<PlatformDbContext, Layout, Guid>, ILayoutRepository |
|||
{ |
|||
public EfCoreLayoutRepository(IDbContextProvider<PlatformDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public virtual async Task<Layout> FindByNameAsync( |
|||
string name, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.IncludeDetails(includeDetails) |
|||
.Where(x => x.Name == name) |
|||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<int> GetCountAsync( |
|||
PlatformType? platformType = null, |
|||
string filter = "", |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.WhereIf(platformType.HasValue, x => x.PlatformType == platformType.Value) |
|||
.WhereIf(!filter.IsNullOrWhiteSpace(), x => |
|||
x.Name.Contains(filter) || x.DisplayName.Contains(filter) || |
|||
x.Description.Contains(filter) || x.Redirect.Contains(filter)) |
|||
.CountAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<Layout> GetLastOrNullAsync( |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.OrderByDescending(x => x.Code) |
|||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Layout>> GetListAsync( |
|||
PlatformType? platformType = null, |
|||
string filter = "", |
|||
string sorting = "Code", |
|||
bool reverse = false, |
|||
bool includeDetails = false, |
|||
int skipCount = 0, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
sorting ??= nameof(Layout.Code); |
|||
sorting = reverse ? sorting + " DESC" : sorting; |
|||
|
|||
return await DbSet |
|||
.IncludeDetails(includeDetails) |
|||
.WhereIf(platformType.HasValue, x => x.PlatformType == platformType.Value) |
|||
.WhereIf(!filter.IsNullOrWhiteSpace(), x => |
|||
x.Name.Contains(filter) || x.DisplayName.Contains(filter) || |
|||
x.Description.Contains(filter) || x.Redirect.Contains(filter)) |
|||
.OrderBy(sorting) |
|||
.PageBy(skipCount, maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public override IQueryable<Layout> WithDetails() |
|||
{ |
|||
return GetQueryable().IncludeDetails(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,232 @@ |
|||
using LINGYUN.Platform.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class EfCoreMenuRepository : EfCoreRepository<PlatformDbContext, Menu, Guid>, IMenuRepository |
|||
{ |
|||
public EfCoreMenuRepository( |
|||
IDbContextProvider<PlatformDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public virtual async Task<bool> UserHasInMenuAsync( |
|||
Guid userId, |
|||
string menuName, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var menuQuery = DbSet.Where(x => x.Name == menuName); |
|||
|
|||
return await (from menu in menuQuery |
|||
join userMenu in DbContext.Set<UserMenu>() |
|||
on menu.Id equals userMenu.MenuId |
|||
select userMenu) |
|||
.AnyAsync(x => x.UserId == userId, GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<bool> RoleHasInMenuAsync( |
|||
string roleName, |
|||
string menuName, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var menuQuery = DbSet.Where(x => x.Name == menuName); |
|||
|
|||
return await (from menu in menuQuery |
|||
join roleMenu in DbContext.Set<RoleMenu>() |
|||
on menu.Id equals roleMenu.MenuId |
|||
select roleMenu) |
|||
.AnyAsync(x => x.RoleName == roleName, GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<Menu> FindByNameAsync( |
|||
string menuName, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.Where(x => x.Name == menuName) |
|||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<Menu> FindMainAsync( |
|||
PlatformType platformType = PlatformType.None, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.Where(menu => menu.PlatformType.HasFlag(platformType) && menu.Path == "/") |
|||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Menu>> GetRoleMenusAsync( |
|||
string[] roles, |
|||
PlatformType platformType = PlatformType.None, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var menuQuery = DbSet |
|||
.Where(menu => menu.PlatformType.HasFlag(platformType)); |
|||
|
|||
var roleMenuQuery = DbContext.Set<RoleMenu>() |
|||
.Where(menu => roles.Contains(menu.RoleName)); |
|||
|
|||
return await (from menu in menuQuery |
|||
join roleMenu in roleMenuQuery |
|||
on menu.Id equals roleMenu.MenuId |
|||
select menu) |
|||
.Union(menuQuery.Where(x => x.IsPublic)) |
|||
.Distinct() |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Menu>> GetUserMenusAsync( |
|||
Guid userId, |
|||
string[] roles, |
|||
PlatformType platformType = PlatformType.None, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var menuQuery = DbSet |
|||
.Where(menu => menu.PlatformType.HasFlag(platformType)); |
|||
|
|||
var userMenuQuery = from userMenu in DbContext.Set<UserMenu>() |
|||
join menu in menuQuery |
|||
on userMenu.MenuId equals menu.Id |
|||
where userMenu.UserId == userId |
|||
select menu; |
|||
|
|||
if (roles != null && roles.Length > 0) |
|||
{ |
|||
var roleMenuQuery = from roleMenu in DbContext.Set<RoleMenu>() |
|||
join menu in menuQuery |
|||
on roleMenu.MenuId equals menu.Id |
|||
where roles.Contains(roleMenu.RoleName) |
|||
select menu; ; |
|||
|
|||
return await userMenuQuery |
|||
.Union(roleMenuQuery) |
|||
.Union(menuQuery.Where(x => x.IsPublic)) |
|||
.Distinct() |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
return await userMenuQuery |
|||
.Union(menuQuery.Where(x => x.IsPublic)) |
|||
.Distinct() |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Menu>> GetChildrenAsync( |
|||
Guid? parentId, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
return await DbSet |
|||
.Where(x => x.ParentId == parentId) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Menu>> GetAllChildrenWithParentCodeAsync( |
|||
string code, |
|||
Guid? parentId, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
return await DbSet |
|||
.Where(x => x.Code.StartsWith(code) && x.Id != parentId.Value) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Menu>> GetAllAsync( |
|||
string filter = "", |
|||
string sorting = nameof(Menu.Code), |
|||
bool reverse = false, |
|||
PlatformType? platformType = null, |
|||
Guid? parentId = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
sorting ??= nameof(Menu.Code); |
|||
sorting = reverse ? sorting + " DESC" : sorting; |
|||
|
|||
return await DbSet |
|||
.WhereIf(parentId.HasValue, x => x.ParentId == parentId) |
|||
.WhereIf(platformType.HasValue, menu => menu.PlatformType.HasFlag(platformType.Value)) |
|||
.WhereIf(!filter.IsNullOrWhiteSpace(), menu => |
|||
menu.Path.Contains(filter) || menu.Name.Contains(filter) || |
|||
menu.DisplayName.Contains(filter) || menu.Description.Contains(filter) || |
|||
menu.Redirect.Contains(filter)) |
|||
.OrderBy(sorting) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<int> GetCountAsync( |
|||
string filter = "", |
|||
PlatformType? platformType = null, |
|||
Guid? parentId = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.WhereIf(parentId.HasValue, x => x.ParentId == parentId) |
|||
.WhereIf(platformType.HasValue, menu => menu.PlatformType.HasFlag(platformType.Value)) |
|||
.WhereIf(!filter.IsNullOrWhiteSpace(), menu => |
|||
menu.Path.Contains(filter) || menu.Name.Contains(filter) || |
|||
menu.DisplayName.Contains(filter) || menu.Description.Contains(filter) || |
|||
menu.Redirect.Contains(filter)) |
|||
.CountAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<Menu>> GetListAsync( |
|||
string filter = "", |
|||
string sorting = nameof(Menu.Code), |
|||
bool reverse = false, |
|||
PlatformType? platformType = null, |
|||
Guid? parentId = null, |
|||
int skipCount = 0, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
sorting ??= nameof(Menu.Code); |
|||
sorting = reverse ? sorting + " DESC" : sorting; |
|||
|
|||
return await DbSet |
|||
.WhereIf(parentId.HasValue, x => x.ParentId == parentId) |
|||
.WhereIf(platformType.HasValue, menu => menu.PlatformType.HasFlag(platformType.Value)) |
|||
.WhereIf(!filter.IsNullOrWhiteSpace(), menu => |
|||
menu.Path.Contains(filter) || menu.Name.Contains(filter) || |
|||
menu.DisplayName.Contains(filter) || menu.Description.Contains(filter) || |
|||
menu.Redirect.Contains(filter)) |
|||
.OrderBy(sorting) |
|||
.PageBy(skipCount, maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task RemoveAllRolesAsync( |
|||
Menu menu, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
var rolesQuery = await DbContext.Set<RoleMenu>() |
|||
.Where(q => q.MenuId == menu.Id) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
|
|||
DbContext.Set<RoleMenu>().RemoveRange(rolesQuery); |
|||
} |
|||
|
|||
public virtual async Task RemoveAllMembersAsync( |
|||
Menu menu, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
var membersQuery = await DbContext.Set<UserMenu>() |
|||
.Where(q => q.MenuId == menu.Id) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
|
|||
DbContext.Set<UserMenu>().RemoveRange(membersQuery); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
using LINGYUN.Platform.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class EfCoreRoleMenuRepository : EfCoreRepository<PlatformDbContext, RoleMenu, Guid>, IRoleMenuRepository |
|||
{ |
|||
public EfCoreRoleMenuRepository( |
|||
IDbContextProvider<PlatformDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public virtual async Task<bool> RoleHasInMenuAsync( |
|||
string roleName, |
|||
string menuName, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var menuQuery = DbContext.Set<Menu>().Where(x => x.Name == menuName); |
|||
|
|||
return await |
|||
(from roleMenu in DbSet |
|||
join menu in menuQuery |
|||
on roleMenu.MenuId equals menu.Id |
|||
select roleMenu) |
|||
.AnyAsync(x => x.RoleName == roleName, |
|||
GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task SetRoleMenusAsync( |
|||
string roleName, |
|||
IEnumerable<Guid> menuIds, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var hasInMenus = await DbSet |
|||
.Where(x => x.RoleName == roleName) |
|||
.ToArrayAsync(GetCancellationToken(cancellationToken)); |
|||
|
|||
var removes = hasInMenus.Where(x => !menuIds.Contains(x.MenuId)); |
|||
if (removes.Any()) |
|||
{ |
|||
DbContext.RemoveRange(removes); |
|||
} |
|||
|
|||
var adds = menuIds.Where(menuId => !hasInMenus.Any(x => x.MenuId == menuId)); |
|||
if (adds.Any()) |
|||
{ |
|||
var addInMenus = adds.Select(menuId => new RoleMenu(menuId, roleName, CurrentTenant.Id)); |
|||
await DbContext.AddRangeAsync(addInMenus, GetCancellationToken(cancellationToken)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
using LINGYUN.Platform.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Platform.Menus |
|||
{ |
|||
public class EfCoreUserMenuRepository : EfCoreRepository<PlatformDbContext, UserMenu, Guid>, IUserMenuRepository |
|||
{ |
|||
public EfCoreUserMenuRepository( |
|||
IDbContextProvider<PlatformDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public virtual async Task<bool> UserHasInMenuAsync( |
|||
Guid userId, |
|||
string menuName, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var menuQuery = DbContext.Set<Menu>().Where(x => x.Name == menuName); |
|||
|
|||
return await |
|||
(from roleMenu in DbSet |
|||
join menu in menuQuery |
|||
on roleMenu.MenuId equals menu.Id |
|||
select roleMenu) |
|||
.AnyAsync(x => x.UserId == userId, |
|||
GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task SetMemberMenusAsync( |
|||
Guid userId, |
|||
IEnumerable<Guid> menuIds, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var hasInMenus = await DbSet |
|||
.Where(x => x.UserId == userId) |
|||
.ToArrayAsync(GetCancellationToken(cancellationToken)); |
|||
|
|||
var removes = hasInMenus.Where(x => !menuIds.Contains(x.MenuId)); |
|||
if (removes.Any()) |
|||
{ |
|||
DbContext.RemoveRange(removes); |
|||
} |
|||
|
|||
var adds = menuIds.Where(menuId => !hasInMenus.Any(x => x.MenuId == menuId)); |
|||
if (adds.Any()) |
|||
{ |
|||
var addInMenus = adds.Select(menuId => new UserMenu(menuId, userId, CurrentTenant.Id)); |
|||
await DbContext.AddRangeAsync(addInMenus, GetCancellationToken(cancellationToken)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue