Browse Source

support dynamic menus

pull/159/head
cKey 5 years ago
parent
commit
f706a2ab00
  1. 51
      aspnet-core/LINGYUN.MicroService.Common.sln
  2. 8
      aspnet-core/modules/common/LINGYUN.Abp.Core/AbpCommonModule.cs
  3. 30
      aspnet-core/modules/common/LINGYUN.Abp.Core/DynamicOptionsProvider.cs
  4. 8
      aspnet-core/modules/common/LINGYUN.Abp.Core/IOptionsProvider.cs
  5. 15
      aspnet-core/modules/common/LINGYUN.Abp.Core/LINGYUN.Abp.Core.csproj
  6. 4
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN.Platform.Application.Contracts.csproj
  7. 9
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataCreateDto.cs
  8. 19
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataCreateOrUpdateDto.cs
  9. 21
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataDto.cs
  10. 13
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemCreateDto.cs
  11. 38
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemCreateOrUpdateDto.cs
  12. 20
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemDto.cs
  13. 8
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemUpdateDto.cs
  14. 7
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataUpdateDto.cs
  15. 12
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/GetDataByNameInput.cs
  16. 9
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/GetDataListInput.cs
  17. 24
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/IDataAppService.cs
  18. 13
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/GetLayoutListInput.cs
  19. 9
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutCreateDto.cs
  20. 29
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutCreateOrUpdateDto.cs
  21. 21
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutDto.cs
  22. 6
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutUpdateDto.cs
  23. 16
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/ILayoutAppService.cs
  24. 11
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/en.json
  25. 11
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/zh-Hans.json
  26. 7
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/GetMenuInput.cs
  27. 13
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuCreateDto.cs
  28. 36
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuCreateOrUpdateDto.cs
  29. 32
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuDto.cs
  30. 18
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetAllInput.cs
  31. 13
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetByRoleInput.cs
  32. 15
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetByUserInput.cs
  33. 16
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetListInput.cs
  34. 21
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuItemDto.cs
  35. 6
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuUpdateDto.cs
  36. 16
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/RoleMenuInput.cs
  37. 15
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserMenuInput.cs
  38. 28
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/IMenuAppService.cs
  39. 19
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissionDefinitionProvider.cs
  40. 41
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissions.cs
  41. 6
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/PlatformApplicationContractModule.cs
  42. 7
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/PlatformRemoteServiceConsts.cs
  43. 34
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Routes/Dto/RouteDto.cs
  44. 14
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/AppPlatformApplicationMappingProfile.cs
  45. 191
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Datas/DataAppService.cs
  46. 130
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Layouts/LayoutAppService.cs
  47. 238
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Menus/MenuAppService.cs
  48. 24
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/PlatformApplicationMappingProfile.cs
  49. 8
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/PlatformApplicationModule.cs
  50. 29
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Datas/DataConsts.cs
  51. 29
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Datas/DataItemConsts.cs
  52. 13
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Datas/ValueType.cs
  53. 44
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Localization/Resources/en.json
  54. 44
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Localization/Resources/zh-Hans.json
  55. 21
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/MenuConsts.cs
  56. 14
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformConsts.cs
  57. 6
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformDomainSharedModule.cs
  58. 24
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformErrorCodes.cs
  59. 6
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformType.cs
  60. 8
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RouteConsts.cs
  61. 7
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RouteEto.cs
  62. 121
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/Data.cs
  63. 50
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataDictionaryDataSeeder.cs
  64. 90
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataItem.cs
  65. 105
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataItemMappingOptions.cs
  66. 18
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/IDataDictionaryDataSeeder.cs
  67. 34
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/IDataRepository.cs
  68. 41
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Layouts/ILayoutRepository.cs
  69. 48
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Layouts/Layout.cs
  70. 110
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IMenuRepository.cs
  71. 28
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IRoleMenuRepository.cs
  72. 28
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IUserMenuRepository.cs
  73. 67
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/Menu.cs
  74. 191
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/MenuManager.cs
  75. 35
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/RoleMenu.cs
  76. 35
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/UserMenu.cs
  77. 760
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDataSeedContributor.cs
  78. 2
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDbProperties.cs
  79. 6
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainMappingProfile.cs
  80. 22
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainModule.cs
  81. 51
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/IRouteDataSeeder.cs
  82. 78
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/IRouteRepository.cs
  83. 31
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/RoleRoute.cs
  84. 188
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/Route.cs
  85. 152
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/RouteDataSeeder.cs
  86. 139
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/RouteManager.cs
  87. 29
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/UserRoute.cs
  88. 94
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Utils/CodeNumberGenerator.cs
  89. 79
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Datas/EfCoreDataRepository.cs
  90. 143
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/AppPlatformDbContextModelBuilderExtensions.cs
  91. 10
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/IPlatformDbContext.cs
  92. 10
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformDbContext.cs
  93. 249
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformDbContextModelBuilderExtensions.cs
  94. 34
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformEfCoreQueryableExtensions.cs
  95. 14
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformEntityFrameworkCoreModule.cs
  96. 4
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformModelBuilderConfigurationOptions.cs
  97. 82
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Layouts/EfCoreLayoutRepository.cs
  98. 232
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs
  99. 60
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreRoleMenuRepository.cs
  100. 60
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserMenuRepository.cs

51
aspnet-core/LINGYUN.MicroService.Common.sln

@ -59,10 +59,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wechat", "wechat", "{22C614
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat", "modules\wechat\LINGYUN.Abp.WeChat\LINGYUN.Abp.WeChat.csproj", "{865D5508-63CD-4D44-9F5B-AE5CD4A43D08}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.WeChat.WeApp", "modules\wechat\LINGYUN.Abp.Notifications.WeChat\LINGYUN.Abp.Notifications.WeChat.WeApp.csproj", "{076B511E-39C5-4C91-BE8D-CA666CCCEA46}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat.Authorization", "modules\wechat\LINGYUN.Abp.WeChat.Authorization\LINGYUN.Abp.WeChat.Authorization.csproj", "{BCB7E04B-4A60-4596-8051-8ABC08444CAF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Settings", "modules\common\LINGYUN.Abp.Settings\LINGYUN.Abp.Settings.csproj", "{ECAA4B82-A240-4747-888C-FACD8634D389}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Sms.Aliyun", "modules\common\LINGYUN.Abp.Sms.Aliyun\LINGYUN.Abp.Sms.Aliyun.csproj", "{8AF8FD6B-634F-41FA-B421-A4ACFD159FE0}"
@ -89,7 +85,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.Db
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.RemoteService", "modules\tenants\LINGYUN.Abp.MultiTenancy.RemoteService\LINGYUN.Abp.MultiTenancy.RemoteService.csproj", "{21C0A260-BC14-4A8F-9299-A9EE58682B96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Sms", "modules\common\LINGYUN.Abp.Notifications.Sms\LINGYUN.Abp.Notifications.Sms.csproj", "{15FC0C39-A604-491F-91F6-BD44167FC5F6}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Sms", "modules\common\LINGYUN.Abp.Notifications.Sms\LINGYUN.Abp.Notifications.Sms.csproj", "{15FC0C39-A604-491F-91F6-BD44167FC5F6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat.MiniProgram", "modules\wechat\LINGYUN.Abp.WeChat.MiniProgram\LINGYUN.Abp.WeChat.MiniProgram.csproj", "{9E59B1DB-E0D5-485D-BDA0-B6C31E1358A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat.Official", "modules\wechat\LINGYUN.Abp.WeChat.Official\LINGYUN.Abp.WeChat.Official.csproj", "{16942653-B746-4917-B3BF-464C99F8832F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat.SettingManagement", "modules\wechat\LINGYUN.Abp.WeChat.SettingManagement\LINGYUN.Abp.WeChat.SettingManagement.csproj", "{D0717513-2CEE-4AD5-A1DA-A08EA5DE6DD6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.WeChat.MiniProgram", "modules\wechat\LINGYUN.Abp.Notifications.WeChat.MiniProgram\LINGYUN.Abp.Notifications.WeChat.MiniProgram.csproj", "{D119C919-230B-4614-9A06-98586635DBFC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Core", "modules\common\LINGYUN.Abp.Core\LINGYUN.Abp.Core.csproj", "{A7A28D6C-6EDB-4615-8899-7DE1D435B750}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -197,14 +203,6 @@ Global
{865D5508-63CD-4D44-9F5B-AE5CD4A43D08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{865D5508-63CD-4D44-9F5B-AE5CD4A43D08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{865D5508-63CD-4D44-9F5B-AE5CD4A43D08}.Release|Any CPU.Build.0 = Release|Any CPU
{076B511E-39C5-4C91-BE8D-CA666CCCEA46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{076B511E-39C5-4C91-BE8D-CA666CCCEA46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{076B511E-39C5-4C91-BE8D-CA666CCCEA46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{076B511E-39C5-4C91-BE8D-CA666CCCEA46}.Release|Any CPU.Build.0 = Release|Any CPU
{BCB7E04B-4A60-4596-8051-8ABC08444CAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCB7E04B-4A60-4596-8051-8ABC08444CAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCB7E04B-4A60-4596-8051-8ABC08444CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCB7E04B-4A60-4596-8051-8ABC08444CAF}.Release|Any CPU.Build.0 = Release|Any CPU
{ECAA4B82-A240-4747-888C-FACD8634D389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECAA4B82-A240-4747-888C-FACD8634D389}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECAA4B82-A240-4747-888C-FACD8634D389}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -237,6 +235,26 @@ Global
{15FC0C39-A604-491F-91F6-BD44167FC5F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15FC0C39-A604-491F-91F6-BD44167FC5F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15FC0C39-A604-491F-91F6-BD44167FC5F6}.Release|Any CPU.Build.0 = Release|Any CPU
{9E59B1DB-E0D5-485D-BDA0-B6C31E1358A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E59B1DB-E0D5-485D-BDA0-B6C31E1358A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E59B1DB-E0D5-485D-BDA0-B6C31E1358A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E59B1DB-E0D5-485D-BDA0-B6C31E1358A8}.Release|Any CPU.Build.0 = Release|Any CPU
{16942653-B746-4917-B3BF-464C99F8832F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16942653-B746-4917-B3BF-464C99F8832F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16942653-B746-4917-B3BF-464C99F8832F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16942653-B746-4917-B3BF-464C99F8832F}.Release|Any CPU.Build.0 = Release|Any CPU
{D0717513-2CEE-4AD5-A1DA-A08EA5DE6DD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0717513-2CEE-4AD5-A1DA-A08EA5DE6DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0717513-2CEE-4AD5-A1DA-A08EA5DE6DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0717513-2CEE-4AD5-A1DA-A08EA5DE6DD6}.Release|Any CPU.Build.0 = Release|Any CPU
{D119C919-230B-4614-9A06-98586635DBFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D119C919-230B-4614-9A06-98586635DBFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D119C919-230B-4614-9A06-98586635DBFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D119C919-230B-4614-9A06-98586635DBFC}.Release|Any CPU.Build.0 = Release|Any CPU
{A7A28D6C-6EDB-4615-8899-7DE1D435B750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7A28D6C-6EDB-4615-8899-7DE1D435B750}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7A28D6C-6EDB-4615-8899-7DE1D435B750}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7A28D6C-6EDB-4615-8899-7DE1D435B750}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -269,8 +287,6 @@ Global
{B31CEE79-45F6-4BB9-9EEB-08843C324B37} = {B91F26C5-B148-4094-B5F1-71E5F945DBED}
{22C61434-D29A-4376-AD56-F5089F3E617A} = {02EA4E78-5891-43BC-944F-3E52FEE032E4}
{865D5508-63CD-4D44-9F5B-AE5CD4A43D08} = {22C61434-D29A-4376-AD56-F5089F3E617A}
{076B511E-39C5-4C91-BE8D-CA666CCCEA46} = {22C61434-D29A-4376-AD56-F5089F3E617A}
{BCB7E04B-4A60-4596-8051-8ABC08444CAF} = {22C61434-D29A-4376-AD56-F5089F3E617A}
{ECAA4B82-A240-4747-888C-FACD8634D389} = {086BE5BE-8594-4DA7-8819-935FEF76DABD}
{8AF8FD6B-634F-41FA-B421-A4ACFD159FE0} = {7F18BCA5-35BD-41FB-8EFF-801B56E5E414}
{7F18BCA5-35BD-41FB-8EFF-801B56E5E414} = {02EA4E78-5891-43BC-944F-3E52FEE032E4}
@ -285,6 +301,11 @@ Global
{BD0A1F2D-7667-45F8-872D-D41F36384163} = {38E21687-5F19-42C9-9D11-4B1D2EF64EDB}
{21C0A260-BC14-4A8F-9299-A9EE58682B96} = {38E21687-5F19-42C9-9D11-4B1D2EF64EDB}
{15FC0C39-A604-491F-91F6-BD44167FC5F6} = {B91F26C5-B148-4094-B5F1-71E5F945DBED}
{9E59B1DB-E0D5-485D-BDA0-B6C31E1358A8} = {22C61434-D29A-4376-AD56-F5089F3E617A}
{16942653-B746-4917-B3BF-464C99F8832F} = {22C61434-D29A-4376-AD56-F5089F3E617A}
{D0717513-2CEE-4AD5-A1DA-A08EA5DE6DD6} = {22C61434-D29A-4376-AD56-F5089F3E617A}
{D119C919-230B-4614-9A06-98586635DBFC} = {22C61434-D29A-4376-AD56-F5089F3E617A}
{A7A28D6C-6EDB-4615-8899-7DE1D435B750} = {086BE5BE-8594-4DA7-8819-935FEF76DABD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8}

8
aspnet-core/modules/common/LINGYUN.Abp.Core/AbpCommonModule.cs

@ -0,0 +1,8 @@
using Volo.Abp.Modularity;
namespace LINGYUN.Abp
{
public class AbpCommonModule : AbpModule
{
}
}

30
aspnet-core/modules/common/LINGYUN.Abp.Core/DynamicOptionsProvider.cs

@ -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;
}
}
}

8
aspnet-core/modules/common/LINGYUN.Abp.Core/IOptionsProvider.cs

@ -0,0 +1,8 @@
namespace LINGYUN.Abp
{
public interface IOptionsProvider<TValue>
where TValue: class, new()
{
TValue Value { get; }
}
}

15
aspnet-core/modules/common/LINGYUN.Abp.Core/LINGYUN.Abp.Core.csproj

@ -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>

4
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN.Platform.Application.Contracts.csproj

@ -21,10 +21,6 @@
<PackageReference Include="Volo.Abp.Ddd.Application" Version="3.3.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="LINGYUN\Platform\Routes\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Platform.Domain.Shared\LINGYUN.Platform.Domain.Shared.csproj" />
</ItemGroup>

9
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataCreateDto.cs

@ -0,0 +1,9 @@
using System;
namespace LINGYUN.Platform.Datas
{
public class DataCreateDto : DataCreateOrUpdateDto
{
public Guid? ParentId { get; set; }
}
}

19
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataCreateOrUpdateDto.cs

@ -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; }
}
}

21
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataDto.cs

@ -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>();
}
}

13
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemCreateDto.cs

@ -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; }
}
}

38
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemCreateOrUpdateDto.cs

@ -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) });
}
}
}
}

20
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemDto.cs

@ -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; }
}
}

8
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataItemUpdateDto.cs

@ -0,0 +1,8 @@
using System;
namespace LINGYUN.Platform.Datas
{
public class DataItemUpdateDto : DataItemCreateOrUpdateDto
{
}
}

7
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/DataUpdateDto.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Platform.Datas
{
public class DataUpdateDto : DataCreateOrUpdateDto
{
}
}

12
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/GetDataByNameInput.cs

@ -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; }
}
}

9
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/Dto/GetDataListInput.cs

@ -0,0 +1,9 @@
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Platform.Datas
{
public class GetDataListInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
}
}

24
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Datas/IDataAppService.cs

@ -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);
}
}

13
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/GetLayoutListInput.cs

@ -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; }
}
}

9
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutCreateDto.cs

@ -0,0 +1,9 @@
using System;
namespace LINGYUN.Platform.Layouts
{
public class LayoutCreateDto : LayoutCreateOrUpdateDto
{
public Guid DataId { get; set; }
}
}

29
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutCreateOrUpdateDto.cs

@ -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; }
}
}

21
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutDto.cs

@ -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; }
}
}

6
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/Dto/LayoutUpdateDto.cs

@ -0,0 +1,6 @@
namespace LINGYUN.Platform.Layouts
{
public class LayoutUpdateDto : LayoutCreateOrUpdateDto
{
}
}

16
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Layouts/ILayoutAppService.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp.Application.Services;
namespace LINGYUN.Platform.Layouts
{
public interface ILayoutAppService :
ICrudAppService<
LayoutDto,
Guid,
GetLayoutListInput,
LayoutCreateDto,
LayoutUpdateDto>
{
}
}

11
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/en.json

@ -17,6 +17,15 @@
"Permission:DeleteFile": "Delete file",
"Permission:CopyFile": "Copy file",
"Permission:MoveFile": "Move file",
"Permission:DownloadFile": "Download file"
"Permission:DownloadFile": "Download file",
"Permission:DataDictionary": "Data Dictionary",
"Permission:Layout": "Layout",
"Permission:Menu": "Menu",
"Permission:Create": "Create",
"Permission:Update": "Update",
"Permission:Delete": "Delete",
"Permission:ManageItems": "Manage Items",
"Permission:ManageRoleMenus": "Manage Role Menus",
"Permission:ManageUserMenus": "Manage User Menus"
}
}

11
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/zh-Hans.json

@ -17,6 +17,15 @@
"Permission:DeleteFile": "删除文件",
"Permission:CopyFile": "复制文件",
"Permission:MoveFile": "移动文件",
"Permission:DownloadFile": "下载文件"
"Permission:DownloadFile": "下载文件",
"Permission:DataDictionary": "数据字典",
"Permission:Layout": "布局管理",
"Permission:Menu": "菜单管理",
"Permission:Create": "新增",
"Permission:Update": "新增",
"Permission:Delete": "新增",
"Permission:ManageItems": "管理项目",
"Permission:ManageRoleMenus": "管理角色菜单",
"Permission:ManageUserMenus": "管理用户菜单"
}
}

7
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/GetMenuInput.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Platform.Menus
{
public class GetMenuInput
{
public PlatformType PlatformType { get; set; }
}
}

13
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuCreateDto.cs

@ -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; }
}
}

36
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuCreateOrUpdateDto.cs

@ -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>();
}
}

32
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuDto.cs

@ -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; }
}
}

18
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetAllInput.cs

@ -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; }
}
}

13
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetByRoleInput.cs

@ -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; }
}
}

15
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetByUserInput.cs

@ -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; }
}
}

16
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuGetListInput.cs

@ -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; }
}
}

21
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuItemDto.cs

@ -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>();
}
}

6
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/MenuUpdateDto.cs

@ -0,0 +1,6 @@
namespace LINGYUN.Platform.Menus
{
public class MenuUpdateDto : MenuCreateOrUpdateDto
{
}
}

16
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/RoleMenuInput.cs

@ -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>();
}
}

15
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserMenuInput.cs

@ -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>();
}
}

28
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/IMenuAppService.cs

@ -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);
}
}

19
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissionDefinitionProvider.cs

@ -19,6 +19,25 @@ namespace LINGYUN.Platform.Permissions
versionFile.AddChild(PlatformPermissions.AppVersion.FileManager.Delete, L("Permission:DeleteFile"));
versionFile.AddChild(PlatformPermissions.AppVersion.FileManager.Download, L("Permission:DownloadFile"));
var dataDictionary = platform.AddPermission(PlatformPermissions.DataDictionary.Default, L("Permission:DataDictionary"));
dataDictionary.AddChild(PlatformPermissions.DataDictionary.Create, L("Permission:Create"));
dataDictionary.AddChild(PlatformPermissions.DataDictionary.Update, L("Permission:Update"));
dataDictionary.AddChild(PlatformPermissions.DataDictionary.Delete, L("Permission:Delete"));
dataDictionary.AddChild(PlatformPermissions.DataDictionary.ManageItems, L("Permission:ManageItems"));
var layout = platform.AddPermission(PlatformPermissions.Layout.Default, L("Permission:Layout"));
layout.AddChild(PlatformPermissions.Layout.Create, L("Permission:Create"));
layout.AddChild(PlatformPermissions.Layout.Update, L("Permission:Update"));
layout.AddChild(PlatformPermissions.Layout.Delete, L("Permission:Delete"));
var menu = platform.AddPermission(PlatformPermissions.Menu.Default, L("Permission:Menu"));
menu.AddChild(PlatformPermissions.Menu.Create, L("Permission:Create"));
menu.AddChild(PlatformPermissions.Menu.Update, L("Permission:Update"));
menu.AddChild(PlatformPermissions.Menu.Delete, L("Permission:Delete"));
menu.AddChild(PlatformPermissions.Menu.ManageRoles, L("Permission:ManageRoleMenus"));
menu.AddChild(PlatformPermissions.Menu.ManageUsers, L("Permission:ManageUserMenus"));
// TODO: 2020-07-27 目前abp不支持对象存储管理(或者属于企业版?)需要创建一个 LINGYUN.Abp.BlobStoring 项目自行实现
//var fileSystem = platform.AddPermission(PlatformPermissions.FileSystem.Default, L("Permission:FileSystem"));

41
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissions.cs

@ -2,10 +2,49 @@
namespace LINGYUN.Platform.Permissions
{
public class PlatformPermissions
public static class PlatformPermissions
{
public const string GroupName = "Platform";
public class DataDictionary
{
public const string Default = GroupName + ".DataDictionary";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string ManageItems = Default + ".ManageItems";
}
public class Layout
{
public const string Default = GroupName + ".Layout";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public class Menu
{
public const string Default = GroupName + ".Menu";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string ManageRoles = Default + ".ManageRoles";
public const string ManageUsers = Default + ".ManageUsers";
}
// 如果abp后期提供对象存储的目录管理接口,则启用此权限
/// <summary>
/// 文件系统

6
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/AppPlatformApplicationContractModule.cs → aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/PlatformApplicationContractModule.cs

@ -5,14 +5,14 @@ using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Platform
{
[DependsOn(typeof(AppPlatformDomainSharedModule))]
public class AppPlatformApplicationContractModule : AbpModule
[DependsOn(typeof(PlatformDomainSharedModule))]
public class PlatformApplicationContractModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AppPlatformApplicationContractModule>();
options.FileSets.AddEmbedded<PlatformApplicationContractModule>();
});
Configure<AbpLocalizationOptions>(options =>

7
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/PlatformRemoteServiceConsts.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Platform
{
public static class PlatformRemoteServiceConsts
{
public const string RemoteServiceName = "Platform";
}
}

34
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Routes/Dto/RouteDto.cs

@ -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; }
}
}

14
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/AppPlatformApplicationMappingProfile.cs

@ -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>();
}
}
}

191
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Datas/DataAppService.cs

@ -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();
}
}
}

130
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Layouts/LayoutAppService.cs

@ -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);
}
}
}

238
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Menus/MenuAppService.cs

@ -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));
}
}
}

24
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/PlatformApplicationMappingProfile.cs

@ -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));
}
}
}

8
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/AppPlatformApplicationModule.cs → aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/PlatformApplicationModule.cs

@ -4,16 +4,16 @@ using Volo.Abp.Modularity;
namespace LINGYUN.Platform
{
[DependsOn(typeof(AppPlatformApplicationContractModule))]
public class AppPlatformApplicationModule : AbpModule
[DependsOn(typeof(PlatformApplicationContractModule))]
public class PlatformApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<AppPlatformApplicationModule>();
context.Services.AddAutoMapperObjectMapper<PlatformApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<AppPlatformApplicationMappingProfile>(validate: true);
options.AddProfile<PlatformApplicationMappingProfile>(validate: true);
});
}
}

29
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Datas/DataConsts.cs

@ -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;
}
}

29
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Datas/DataItemConsts.cs

@ -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;
}
}

13
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Datas/ValueType.cs

@ -0,0 +1,13 @@
namespace LINGYUN.Platform.Datas
{
public enum ValueType
{
String = 0,
Numeic = 1,
Boolean = 2,
Date = 3,
DateTime = 4,
Array = 5
}
}

44
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Localization/Resources/en.json

@ -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"
}
}

44
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Localization/Resources/zh-Hans.json

@ -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": "字典项目"
}
}

21
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/MenuConsts.cs

@ -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;
}
}

14
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformConsts.cs

@ -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;
}
}

6
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/AppPlatformDomainSharedModule.cs → aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformDomainSharedModule.cs

@ -8,13 +8,13 @@ using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Platform
{
[DependsOn(typeof(AbpLocalizationModule))]
public class AppPlatformDomainSharedModule : AbpModule
public class PlatformDomainSharedModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AppPlatformDomainSharedModule>();
options.FileSets.AddEmbedded<PlatformDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
@ -27,7 +27,7 @@ namespace LINGYUN.Platform
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Platform.VersionFile", typeof(PlatformResource));
options.MapCodeNamespace("Platform", typeof(PlatformResource));
});
}
}

24
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformErrorCodes.cs

@ -2,6 +2,28 @@
{
public static class PlatformErrorCodes
{
public const string VersionFileNotFound = "Platform.VersionFile:404";
private const string Namespace = "Platform";
public const string VersionFileNotFound = Namespace + ":01404";
/// <summary>
/// 同级菜单已经存在
/// </summary>
public const string DuplicateMenu = Namespace + ":02001";
/// <summary>
/// 不能删除拥有子菜单的节点
/// </summary>
public const string DeleteMenuHaveChildren = Namespace + ":02002";
/// <summary>
/// 菜单层级已达到最大值
/// </summary>
public const string MenuAchieveMaxDepth = Namespace + ":02003";
/// <summary>
/// 菜单元数据缺少必要的元素
/// </summary>
public const string MenuMissingMetadata = Namespace + ":02101";
/// <summary>
/// 元数据格式不匹配
/// </summary>
public const string MetaFormatMissMatch = Namespace + ":03001";
}
}

6
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/PlatformType.cs

@ -53,8 +53,12 @@ namespace LINGYUN.Platform
/// </summary>
Mobile = Android | iOS,
/// <summary>
/// 小程序
/// </summary>
MiniProgram = 256,
/// <summary>
/// 所有平台通用
/// </summary>
All = Desktop | Web | Mobile
All = Desktop | Web | Mobile | MiniProgram
}
}

8
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RouteConsts.cs

@ -32,7 +32,13 @@
set;
} = 128;
public static int MaxLinkUrlLength
public static int MaxPathLength
{
get;
set;
} = 255;
public static int MaxRedirectLength
{
get;
set;

7
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RouteEto.cs

@ -7,11 +7,10 @@ namespace LINGYUN.Platform.Routes
{
public Guid? TenantId { get; set; }
public Guid Id { get; set; }
public string Code { get; set; }
public string Path { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public string LinkUrl { get; set; }
public string Icon { get; set; }
public PlatformType PlatformType { get; set; }
public string Description { get; set; }
public string Redirect { get; set; }
}
}

121
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/Data.cs

@ -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);
}
}
}

50
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataDictionaryDataSeeder.cs

@ -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;
}
}
}

90
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataItem.cs

@ -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;
}
}
}

105
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataItemMappingOptions.cs

@ -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);
}
}
}

18
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/IDataDictionaryDataSeeder.cs

@ -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);
}
}

34
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/IDataRepository.cs

@ -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);
}
}

41
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Layouts/ILayoutRepository.cs

@ -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);
}
}

48
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Layouts/Layout.cs

@ -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;
}
}
}

110
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IMenuRepository.cs

@ -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
);
}
}

28
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IRoleMenuRepository.cs

@ -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);
}
}

28
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IUserMenuRepository.cs

@ -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);
}
}

67
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/Menu.cs

@ -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;
}
}
}

191
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/MenuManager.cs

@ -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;
}
}
}

35
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/RoleMenu.cs

@ -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 };
}
}
}

35
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/UserMenu.cs

@ -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 };
}
}
}

760
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDataSeedContributor.cs

@ -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;
}
}
}

2
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/AppPlatformDbProperties.cs → aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDbProperties.cs

@ -1,6 +1,6 @@
namespace LINGYUN.Platform
{
public static class AppPlatformDbProperties
public static class PlatformDbProperties
{
public static string DbTablePrefix { get; set; } = "AppPlatform";

6
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/AppPlatformDomainMappingProfile.cs → aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainMappingProfile.cs

@ -4,13 +4,11 @@ using LINGYUN.Platform.Versions;
namespace LINGYUN.Platform
{
public class AppPlatformDomainMappingProfile : Profile
public class PlatformDomainMappingProfile : Profile
{
public AppPlatformDomainMappingProfile()
public PlatformDomainMappingProfile()
{
CreateMap<Route, RouteEto>();
CreateMap<UserRoute, UserRouteEto>();
CreateMap<RoleRoute, RoleRouteEto>();
CreateMap<AppVersion, AppVersionEto>()
.ForMember(eto => eto.FileCount, map => map.MapFrom(src => src.Files.Count));

22
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/AppPlatformDomainModule.cs → aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Platform.ObjectExtending;
using LINGYUN.Platform.Datas;
using LINGYUN.Platform.ObjectExtending;
using LINGYUN.Platform.Routes;
using LINGYUN.Platform.Versions;
using Microsoft.Extensions.DependencyInjection;
@ -12,18 +13,23 @@ using Volo.Abp.ObjectExtending.Modularity;
namespace LINGYUN.Platform
{
[DependsOn(
typeof(AppPlatformDomainSharedModule),
typeof(PlatformDomainSharedModule),
typeof(AbpBlobStoringModule),
typeof(AbpEventBusModule))]
public class AppPlatformDomainModule : AbpModule
public class PlatformDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<AppPlatformDomainModule>();
context.Services.AddAutoMapperObjectMapper<PlatformDomainModule>();
Configure<DataItemMappingOptions>(options =>
{
options.SetDefaultMapping();
});
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<AppPlatformDomainMappingProfile>(validate: true);
options.AddProfile<PlatformDomainMappingProfile>(validate: true);
});
Configure<AbpBlobStoringOptions>(options =>
@ -36,11 +42,9 @@ namespace LINGYUN.Platform
Configure<AbpDistributedEntityEventOptions>(options =>
{
options.EtoMappings.Add<Route, RouteEto>(typeof(AppPlatformDomainModule));
options.EtoMappings.Add<UserRoute, UserRouteEto>(typeof(AppPlatformDomainModule));
options.EtoMappings.Add<RoleRoute, RoleRouteEto>(typeof(AppPlatformDomainModule));
options.EtoMappings.Add<Route, RouteEto>(typeof(PlatformDomainModule));
options.EtoMappings.Add<AppVersion, AppVersionEto>(typeof(AppPlatformDomainModule));
options.EtoMappings.Add<AppVersion, AppVersionEto>(typeof(PlatformDomainModule));
options.AutoEventSelectors.Add<AppVersion>();
});

51
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/IRouteDataSeeder.cs

@ -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
);
}
}

78
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/IRouteRepository.cs

@ -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
);
}
}

31
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/RoleRoute.cs

@ -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));
}
}
}

188
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/Route.cs

@ -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);
}
}
}

152
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/RouteDataSeeder.cs

@ -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);
}
}
}
}
}

139
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/RouteManager.cs

@ -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);
}
}
}

29
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Routes/UserRoute.cs

@ -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;
}
}
}

94
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Utils/CodeNumberGenerator.cs

@ -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(".");
}
}
}

79
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Datas/EfCoreDataRepository.cs

@ -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();
}
}
}

143
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/AppPlatformDbContextModelBuilderExtensions.cs

@ -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();
});
}
}
}

10
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/IPlatformDbContext.cs

@ -1,4 +1,6 @@
using LINGYUN.Platform.Routes;
using LINGYUN.Platform.Datas;
using LINGYUN.Platform.Layouts;
using LINGYUN.Platform.Menus;
using LINGYUN.Platform.Versions;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
@ -6,10 +8,12 @@ using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Platform.EntityFrameworkCore
{
[ConnectionStringName(AppPlatformDbProperties.ConnectionStringName)]
[ConnectionStringName(PlatformDbProperties.ConnectionStringName)]
public interface IPlatformDbContext : IEfCoreDbContext
{
DbSet<Route> Routes { get; set; }
DbSet<Menu> Menus { get; set; }
DbSet<Layout> Layouts { get; set; }
DbSet<Data> Datas { get; set; }
DbSet<AppVersion> AppVersions { get; set; }
}
}

10
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformDbContext.cs

@ -1,4 +1,6 @@
using LINGYUN.Platform.Routes;
using LINGYUN.Platform.Datas;
using LINGYUN.Platform.Layouts;
using LINGYUN.Platform.Menus;
using LINGYUN.Platform.Versions;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
@ -6,10 +8,12 @@ using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Platform.EntityFrameworkCore
{
[ConnectionStringName(AppPlatformDbProperties.ConnectionStringName)]
[ConnectionStringName(PlatformDbProperties.ConnectionStringName)]
public class PlatformDbContext : AbpDbContext<PlatformDbContext>, IPlatformDbContext
{
public DbSet<Route> Routes { get; set; }
public DbSet<Menu> Menus { get; set; }
public DbSet<Layout> Layouts { get; set; }
public DbSet<Data> Datas { get; set; }
public DbSet<AppVersion> AppVersions { get; set; }

249
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformDbContextModelBuilderExtensions.cs

@ -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;
}
}
}

34
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformEfCoreQueryableExtensions.cs

@ -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);
}
}
}

14
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/AppPlatformEntityFrameworkCoreModule.cs → aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformEntityFrameworkCoreModule.cs

@ -1,4 +1,6 @@
using LINGYUN.Platform.Routes;
using LINGYUN.Platform.Datas;
using LINGYUN.Platform.Layouts;
using LINGYUN.Platform.Menus;
using LINGYUN.Platform.Versions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
@ -8,15 +10,19 @@ using Volo.Abp.Modularity;
namespace LINGYUN.Platform.EntityFrameworkCore
{
[DependsOn(
typeof(AppPlatformDomainModule),
typeof(PlatformDomainModule),
typeof(AbpEntityFrameworkCoreModule))]
public class AppPlatformEntityFrameworkCoreModule : AbpModule
public class PlatformEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<PlatformDbContext>(options =>
{
options.AddRepository<Route, EfCoreRouteRepository>();
options.AddRepository<Data, EfCoreDataRepository>();
options.AddRepository<Menu, EfCoreMenuRepository>();
options.AddRepository<UserMenu, EfCoreUserMenuRepository>();
options.AddRepository<RoleMenu, EfCoreRoleMenuRepository>();
options.AddRepository<Layout, EfCoreLayoutRepository>();
options.AddRepository<AppVersion, EfCoreVersionRepository>();
options.AddDefaultRepositories(includeAllEntities: true);

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

@ -3,9 +3,9 @@ using Volo.Abp.EntityFrameworkCore.Modeling;
namespace LINGYUN.Platform.EntityFrameworkCore
{
public class AppPlatformModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
public class PlatformModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{
public AppPlatformModelBuilderConfigurationOptions(
public PlatformModelBuilderConfigurationOptions(
[NotNull] string tablePrefix = "",
[CanBeNull] string schema = null)
: base(

82
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Layouts/EfCoreLayoutRepository.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();
}
}
}

232
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs

@ -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);
}
}
}

60
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreRoleMenuRepository.cs

@ -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));
}
}
}
}

60
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserMenuRepository.cs

@ -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…
Cancel
Save