Browse Source

Merge pull request #518 from colinin/saas-support

feat(saas): add saas management support.
pull/533/head
yx lin 4 years ago
committed by GitHub
parent
commit
53ef4d9575
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      apps/vue/src/utils/cookie.ts
  2. 73
      aspnet-core/LINGYUN.MicroService.All.sln
  3. 2
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/en.json
  4. 2
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/zh-Hans.json
  5. 16
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs
  6. 16
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs
  7. 15
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs
  8. 3
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xml
  9. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xsd
  10. 20
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN.Abp.Saas.Application.Contracts.csproj
  11. 12
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasApplicationContractsModule.cs
  12. 8
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasRemoteServiceConsts.cs
  13. 5
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateDto.cs
  14. 13
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateOrUpdateBase.cs
  15. 9
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionDto.cs
  16. 8
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionGetListInput.cs
  17. 8
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionUpdateDto.cs
  18. 14
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/IEditionAppService.cs
  19. 32
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissionDefinitionProvider.cs
  20. 32
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissions.cs
  21. 15
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionGetByNameInput.cs
  22. 15
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringCreateOrUpdate.cs
  23. 8
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringDto.cs
  24. 25
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateDto.cs
  25. 22
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateOrUpdateBase.cs
  26. 19
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantDto.cs
  27. 17
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetByNameInput.cs
  28. 8
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetListInput.cs
  29. 7
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantUpdateDto.cs
  30. 34
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/ITenantAppService.cs
  31. 3
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xml
  32. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xsd
  33. 20
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN.Abp.Saas.Application.csproj
  34. 12
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasAppServiceBase.cs
  35. 33
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationAutoMapperProfile.cs
  36. 19
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationModule.cs
  37. 87
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Editions/EditionAppService.cs
  38. 208
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Tenants/TenantAppService.cs
  39. 3
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xml
  40. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xsd
  41. 23
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN.Abp.Saas.Domain.Shared.csproj
  42. 34
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasDomainSharedModule.cs
  43. 10
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasErrorCodes.cs
  44. 6
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionConsts.cs
  45. 11
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionEto.cs
  46. 8
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/AbpSaasResource.cs
  47. 43
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/en.json
  48. 43
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/zh-Hans.json
  49. 16
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfiguration.cs
  50. 17
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfigurationDictionaryExtensions.cs
  51. 11
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConsts.cs
  52. 7
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConnectionStringConsts.cs
  53. 6
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConsts.cs
  54. 11
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantEto.cs
  55. 3
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xml
  56. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xsd
  57. 23
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN.Abp.Saas.Domain.csproj
  58. 12
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDbProperties.cs
  59. 58
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainMappingProfile.cs
  60. 58
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainModule.cs
  61. 26
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/Edition.cs
  62. 29
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionCacheItem.cs
  63. 42
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionDataSeeder.cs
  64. 54
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionManager.cs
  65. 65
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionStore.cs
  66. 8
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionDataSeeder.cs
  67. 33
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionRepository.cs
  68. 13
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantManager.cs
  69. 40
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantRepository.cs
  70. 102
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/Tenant.cs
  71. 35
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItem.cs
  72. 25
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItemInvalidator.cs
  73. 37
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantConnectionString.cs
  74. 44
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantManager.cs
  75. 137
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantStore.cs
  76. 3
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xml
  77. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xsd
  78. 19
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN.Abp.Saas.EntityFrameworkCore.csproj
  79. 67
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasDbContextModelCreatingExtensions.cs
  80. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasEntityFrameworkCoreModule.cs
  81. 84
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreEditionRepository.cs
  82. 226
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreTenantRepository.cs
  83. 17
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/ISaasDbContext.cs
  84. 31
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasDbContext.cs
  85. 19
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasEfCoreQueryableExtensions.cs
  86. 3
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xml
  87. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xsd
  88. 19
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN.Abp.Saas.HttpApi.Client.csproj
  89. 18
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN/Abp/Saas/AbpSaasHttpApiClientModule.cs
  90. 3
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xml
  91. 30
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xsd
  92. 19
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN.Abp.Saas.HttpApi.csproj
  93. 11
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasControllerBase.cs
  94. 39
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasHttpApiModule.cs
  95. 59
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Editions/EditionController.cs
  96. 98
      aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Tenants/TenantController.cs
  97. 3
      aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/TenantConfigurationCacheItem.cs
  98. 4
      aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/README.md
  99. 3
      aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xml
  100. 30
      aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xsd

5
apps/vue/src/utils/cookie.ts

@ -13,8 +13,5 @@ export function set(c_name, value, expiredays) {
} }
export function del(name) { export function del(name) {
const exp = new Date(); set(name, 'a', -1);
exp.setTime(exp.getTime() - 1);
const cval = get(name);
if (cval != null) document.cookie = name + '=' + cval + ';expires=' + exp.toUTCString();
} }

73
aspnet-core/LINGYUN.MicroService.All.sln

@ -109,8 +109,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TenantManagemen
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.RemoteService", "modules\tenants\LINGYUN.Abp.MultiTenancy.RemoteService\LINGYUN.Abp.MultiTenancy.RemoteService.csproj", "{7208C9AB-AB76-43E7-95FA-A0E463E82A3C}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.RemoteService", "modules\tenants\LINGYUN.Abp.MultiTenancy.RemoteService\LINGYUN.Abp.MultiTenancy.RemoteService.csproj", "{7208C9AB-AB76-43E7-95FA-A0E463E82A3C}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.DbFinder", "modules\tenants\LINGYUN.Abp.MultiTenancy.DbFinder\LINGYUN.Abp.MultiTenancy.DbFinder.csproj", "{9CB71AC4-139C-40EA-8EFF-5CFEFDD80413}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy", "modules\tenants\LINGYUN.Abp.MultiTenancy\LINGYUN.Abp.MultiTenancy.csproj", "{A2B4ECAE-DABE-40F7-9335-496A79EDE671}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy", "modules\tenants\LINGYUN.Abp.MultiTenancy\LINGYUN.Abp.MultiTenancy.csproj", "{A2B4ECAE-DABE-40F7-9335-496A79EDE671}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Location.Tencent", "modules\common\LINGYUN.Abp.Location.Tencent\LINGYUN.Abp.Location.Tencent.csproj", "{1DA91161-8757-4A68-A0A1-8C94C36C9240}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Location.Tencent", "modules\common\LINGYUN.Abp.Location.Tencent\LINGYUN.Abp.Location.Tencent.csproj", "{1DA91161-8757-4A68-A0A1-8C94C36C9240}"
@ -386,6 +384,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.IdentityServer.
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Tencent.QQ", "modules\cloud-tencent\LINGYUN.Abp.Tencent.QQ\LINGYUN.Abp.Tencent.QQ.csproj", "{3FCB1BCD-34BC-4F66-968F-38DB28371D0F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Tencent.QQ", "modules\cloud-tencent\LINGYUN.Abp.Tencent.QQ\LINGYUN.Abp.Tencent.QQ.csproj", "{3FCB1BCD-34BC-4F66-968F-38DB28371D0F}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.Editions", "modules\tenants\LINGYUN.Abp.MultiTenancy.Editions\LINGYUN.Abp.MultiTenancy.Editions.csproj", "{A030CD8E-61F3-4C15-B28A-C301446DDBEC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "saas", "saas", "{D01D859E-4B72-478A-BABD-90F0981652D5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.Domain.Shared", "modules\saas\LINGYUN.Abp.Saas.Domain.Shared\LINGYUN.Abp.Saas.Domain.Shared.csproj", "{049E09BF-2E11-4E3B-926D-9DD6051A2DA4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.Domain", "modules\saas\LINGYUN.Abp.Saas.Domain\LINGYUN.Abp.Saas.Domain.csproj", "{F1BE6113-3439-45BB-8B58-20ACA4056895}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.EntityFrameworkCore", "modules\saas\LINGYUN.Abp.Saas.EntityFrameworkCore\LINGYUN.Abp.Saas.EntityFrameworkCore.csproj", "{77C2E24A-D143-44E7-86F9-AB6C5D444F63}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.Application.Contracts", "modules\saas\LINGYUN.Abp.Saas.Application.Contracts\LINGYUN.Abp.Saas.Application.Contracts.csproj", "{D8FB1E73-6F50-4CC9-9C0E-FB73205F8DB3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.Application", "modules\saas\LINGYUN.Abp.Saas.Application\LINGYUN.Abp.Saas.Application.csproj", "{B1DDC8AF-B0BE-46D1-A2FE-2D5219F847B5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.HttpApi", "modules\saas\LINGYUN.Abp.Saas.HttpApi\LINGYUN.Abp.Saas.HttpApi.csproj", "{8DF50094-6791-4C7C-B07D-C3E995B69C49}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.HttpApi.Client", "modules\saas\LINGYUN.Abp.Saas.HttpApi.Client\LINGYUN.Abp.Saas.HttpApi.Client.csproj", "{96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.Saas", "modules\tenants\LINGYUN.Abp.MultiTenancy.Saas\LINGYUN.Abp.MultiTenancy.Saas.csproj", "{F57594AA-10C2-4DFF-87F6-19F2548099EA}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -556,10 +574,6 @@ Global
{7208C9AB-AB76-43E7-95FA-A0E463E82A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU {7208C9AB-AB76-43E7-95FA-A0E463E82A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7208C9AB-AB76-43E7-95FA-A0E463E82A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {7208C9AB-AB76-43E7-95FA-A0E463E82A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7208C9AB-AB76-43E7-95FA-A0E463E82A3C}.Release|Any CPU.Build.0 = Release|Any CPU {7208C9AB-AB76-43E7-95FA-A0E463E82A3C}.Release|Any CPU.Build.0 = Release|Any CPU
{9CB71AC4-139C-40EA-8EFF-5CFEFDD80413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9CB71AC4-139C-40EA-8EFF-5CFEFDD80413}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9CB71AC4-139C-40EA-8EFF-5CFEFDD80413}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9CB71AC4-139C-40EA-8EFF-5CFEFDD80413}.Release|Any CPU.Build.0 = Release|Any CPU
{A2B4ECAE-DABE-40F7-9335-496A79EDE671}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2B4ECAE-DABE-40F7-9335-496A79EDE671}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2B4ECAE-DABE-40F7-9335-496A79EDE671}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2B4ECAE-DABE-40F7-9335-496A79EDE671}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2B4ECAE-DABE-40F7-9335-496A79EDE671}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2B4ECAE-DABE-40F7-9335-496A79EDE671}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -1000,6 +1014,42 @@ Global
{3FCB1BCD-34BC-4F66-968F-38DB28371D0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3FCB1BCD-34BC-4F66-968F-38DB28371D0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FCB1BCD-34BC-4F66-968F-38DB28371D0F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3FCB1BCD-34BC-4F66-968F-38DB28371D0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FCB1BCD-34BC-4F66-968F-38DB28371D0F}.Release|Any CPU.Build.0 = Release|Any CPU {3FCB1BCD-34BC-4F66-968F-38DB28371D0F}.Release|Any CPU.Build.0 = Release|Any CPU
{A030CD8E-61F3-4C15-B28A-C301446DDBEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A030CD8E-61F3-4C15-B28A-C301446DDBEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A030CD8E-61F3-4C15-B28A-C301446DDBEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A030CD8E-61F3-4C15-B28A-C301446DDBEC}.Release|Any CPU.Build.0 = Release|Any CPU
{049E09BF-2E11-4E3B-926D-9DD6051A2DA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{049E09BF-2E11-4E3B-926D-9DD6051A2DA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{049E09BF-2E11-4E3B-926D-9DD6051A2DA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{049E09BF-2E11-4E3B-926D-9DD6051A2DA4}.Release|Any CPU.Build.0 = Release|Any CPU
{F1BE6113-3439-45BB-8B58-20ACA4056895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1BE6113-3439-45BB-8B58-20ACA4056895}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1BE6113-3439-45BB-8B58-20ACA4056895}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1BE6113-3439-45BB-8B58-20ACA4056895}.Release|Any CPU.Build.0 = Release|Any CPU
{77C2E24A-D143-44E7-86F9-AB6C5D444F63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77C2E24A-D143-44E7-86F9-AB6C5D444F63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77C2E24A-D143-44E7-86F9-AB6C5D444F63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77C2E24A-D143-44E7-86F9-AB6C5D444F63}.Release|Any CPU.Build.0 = Release|Any CPU
{D8FB1E73-6F50-4CC9-9C0E-FB73205F8DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8FB1E73-6F50-4CC9-9C0E-FB73205F8DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8FB1E73-6F50-4CC9-9C0E-FB73205F8DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8FB1E73-6F50-4CC9-9C0E-FB73205F8DB3}.Release|Any CPU.Build.0 = Release|Any CPU
{B1DDC8AF-B0BE-46D1-A2FE-2D5219F847B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1DDC8AF-B0BE-46D1-A2FE-2D5219F847B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1DDC8AF-B0BE-46D1-A2FE-2D5219F847B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1DDC8AF-B0BE-46D1-A2FE-2D5219F847B5}.Release|Any CPU.Build.0 = Release|Any CPU
{8DF50094-6791-4C7C-B07D-C3E995B69C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DF50094-6791-4C7C-B07D-C3E995B69C49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DF50094-6791-4C7C-B07D-C3E995B69C49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DF50094-6791-4C7C-B07D-C3E995B69C49}.Release|Any CPU.Build.0 = Release|Any CPU
{96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC}.Release|Any CPU.Build.0 = Release|Any CPU
{F57594AA-10C2-4DFF-87F6-19F2548099EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F57594AA-10C2-4DFF-87F6-19F2548099EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F57594AA-10C2-4DFF-87F6-19F2548099EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F57594AA-10C2-4DFF-87F6-19F2548099EA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1055,7 +1105,6 @@ Global
{6259BCB9-A302-4CE7-AF48-9283A0DFD3CB} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F} {6259BCB9-A302-4CE7-AF48-9283A0DFD3CB} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F}
{87C7DDCF-F80D-4A15-9044-71552DCB5550} = {A5543E56-DA53-494D-A531-DA75091D46FF} {87C7DDCF-F80D-4A15-9044-71552DCB5550} = {A5543E56-DA53-494D-A531-DA75091D46FF}
{7208C9AB-AB76-43E7-95FA-A0E463E82A3C} = {A5543E56-DA53-494D-A531-DA75091D46FF} {7208C9AB-AB76-43E7-95FA-A0E463E82A3C} = {A5543E56-DA53-494D-A531-DA75091D46FF}
{9CB71AC4-139C-40EA-8EFF-5CFEFDD80413} = {A5543E56-DA53-494D-A531-DA75091D46FF}
{A2B4ECAE-DABE-40F7-9335-496A79EDE671} = {A5543E56-DA53-494D-A531-DA75091D46FF} {A2B4ECAE-DABE-40F7-9335-496A79EDE671} = {A5543E56-DA53-494D-A531-DA75091D46FF}
{1DA91161-8757-4A68-A0A1-8C94C36C9240} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E} {1DA91161-8757-4A68-A0A1-8C94C36C9240} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E}
{F4923692-D343-4318-AECA-96F580B1A563} = {C5CAD011-DF84-4914-939C-0C029DCEF26F} {F4923692-D343-4318-AECA-96F580B1A563} = {C5CAD011-DF84-4914-939C-0C029DCEF26F}
@ -1188,6 +1237,16 @@ Global
{25378F9D-2A66-4568-AAC6-E9282ACA3DD3} = {0439B173-F41E-4CE0-A44A-CCB70328F272} {25378F9D-2A66-4568-AAC6-E9282ACA3DD3} = {0439B173-F41E-4CE0-A44A-CCB70328F272}
{A34CCD3F-28A1-4BAD-A995-EB8720CE4105} = {0439B173-F41E-4CE0-A44A-CCB70328F272} {A34CCD3F-28A1-4BAD-A995-EB8720CE4105} = {0439B173-F41E-4CE0-A44A-CCB70328F272}
{3FCB1BCD-34BC-4F66-968F-38DB28371D0F} = {3B96F4D8-4993-419B-BCEB-AFE4ED39449F} {3FCB1BCD-34BC-4F66-968F-38DB28371D0F} = {3B96F4D8-4993-419B-BCEB-AFE4ED39449F}
{A030CD8E-61F3-4C15-B28A-C301446DDBEC} = {A5543E56-DA53-494D-A531-DA75091D46FF}
{D01D859E-4B72-478A-BABD-90F0981652D5} = {C5CAD011-DF84-4914-939C-0C029DCEF26F}
{049E09BF-2E11-4E3B-926D-9DD6051A2DA4} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{F1BE6113-3439-45BB-8B58-20ACA4056895} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{77C2E24A-D143-44E7-86F9-AB6C5D444F63} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{D8FB1E73-6F50-4CC9-9C0E-FB73205F8DB3} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{B1DDC8AF-B0BE-46D1-A2FE-2D5219F847B5} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{8DF50094-6791-4C7C-B07D-C3E995B69C49} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{F57594AA-10C2-4DFF-87F6-19F2548099EA} = {A5543E56-DA53-494D-A531-DA75091D46FF}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718} SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}

2
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/en.json

@ -1,6 +1,8 @@
{ {
"culture": "en", "culture": "en",
"texts": { "texts": {
"MiniProgramAuthorizationDisabledMessage": "Applet authorization is not enabled for the application",
"OfficialAuthorizationDisabledMessage": "Official authorization is not enabled for the application",
"SelfRegistrationDisabledMessage": "Self-registration is disabled for this application. Please contact the application administrator to register a new user.", "SelfRegistrationDisabledMessage": "Self-registration is disabled for this application. Please contact the application administrator to register a new user.",
"InvalidGrant:GrantTypeInvalid": "The type of authorization that is not allowed!", "InvalidGrant:GrantTypeInvalid": "The type of authorization that is not allowed!",
"InvalidGrant:WeChatTokenInvalid": "WeChat authentication failed!", "InvalidGrant:WeChatTokenInvalid": "WeChat authentication failed!",

2
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/zh-Hans.json

@ -1,6 +1,8 @@
{ {
"culture": "zh-Hans", "culture": "zh-Hans",
"texts": { "texts": {
"MiniProgramAuthorizationDisabledMessage": "应用程序未开放小程序授权",
"OfficialAuthorizationDisabledMessage": "应用程序未开放公众平台授权",
"SelfRegistrationDisabledMessage": "应用程序未开放注册,请联系管理员添加新用户.", "SelfRegistrationDisabledMessage": "应用程序未开放注册,请联系管理员添加新用户.",
"InvalidGrant:GrantTypeInvalid": "不被允许的授权类型!", "InvalidGrant:GrantTypeInvalid": "不被允许的授权类型!",
"InvalidGrant:WeChatTokenInvalid": "微信认证失败!", "InvalidGrant:WeChatTokenInvalid": "微信认证失败!",

16
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs

@ -28,8 +28,6 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram
protected AbpWeChatMiniProgramOptionsFactory MiniProgramOptionsFactory { get; } protected AbpWeChatMiniProgramOptionsFactory MiniProgramOptionsFactory { get; }
protected IStringLocalizer<WeChatResource> WeChatLocalizer { get; }
protected IFeatureChecker FeatureChecker => ServiceProvider.LazyGetRequiredService<IFeatureChecker>(); protected IFeatureChecker FeatureChecker => ServiceProvider.LazyGetRequiredService<IFeatureChecker>();
public WeChatMiniProgramGrantValidator( public WeChatMiniProgramGrantValidator(
@ -42,9 +40,16 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer, IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer,
IStringLocalizer<WeChatResource> wechatLocalizer, IStringLocalizer<WeChatResource> wechatLocalizer,
AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory) AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory)
: base(eventService, weChatOpenIdFinder, userManager, userRepository, identitySecurityLogManager, identityLocalizer, identityServerLocalizer) : base(
eventService,
weChatOpenIdFinder,
userManager,
userRepository,
identitySecurityLogManager,
wechatLocalizer,
identityLocalizer,
identityServerLocalizer)
{ {
WeChatLocalizer = wechatLocalizer;
MiniProgramOptionsFactory = miniProgramOptionsFactory; MiniProgramOptionsFactory = miniProgramOptionsFactory;
} }
@ -54,8 +59,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram
{ {
context.Result = new GrantValidationResult( context.Result = new GrantValidationResult(
TokenRequestErrors.InvalidGrant, TokenRequestErrors.InvalidGrant,
IdentityServerLocalizer["AuthorizationDisabledMessage", WeChatLocalizer["MiniProgramAuthorizationDisabledMessage"]);
WeChatLocalizer["Features:WeChat.MiniProgram.EnableAuthorization"]]);
return false; return false;
} }
return true; return true;

16
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs

@ -28,8 +28,6 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official
protected AbpWeChatOfficialOptionsFactory WeChatOfficialOptionsFactory { get; } protected AbpWeChatOfficialOptionsFactory WeChatOfficialOptionsFactory { get; }
protected IStringLocalizer<WeChatResource> WeChatLocalizer { get; }
protected IFeatureChecker FeatureChecker => ServiceProvider.LazyGetRequiredService<IFeatureChecker>(); protected IFeatureChecker FeatureChecker => ServiceProvider.LazyGetRequiredService<IFeatureChecker>();
public WeChatOfficialGrantValidator( public WeChatOfficialGrantValidator(
@ -42,9 +40,16 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer, IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer,
IStringLocalizer<WeChatResource> wechatLocalizer, IStringLocalizer<WeChatResource> wechatLocalizer,
AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory) AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory)
: base(eventService, weChatOpenIdFinder, userManager, userRepository, identitySecurityLogManager, identityLocalizer, identityServerLocalizer) : base(
eventService,
weChatOpenIdFinder,
userManager,
userRepository,
identitySecurityLogManager,
wechatLocalizer,
identityLocalizer,
identityServerLocalizer)
{ {
WeChatLocalizer = wechatLocalizer;
WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory; WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory;
} }
@ -54,8 +59,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official
{ {
context.Result = new GrantValidationResult( context.Result = new GrantValidationResult(
TokenRequestErrors.InvalidGrant, TokenRequestErrors.InvalidGrant,
IdentityServerLocalizer["AuthorizationDisabledMessage", WeChatLocalizer["OfficialAuthorizationDisabledMessage"]);
WeChatLocalizer["Features:WeChat.Official.EnableAuthorization"]]);
return false; return false;
} }
return true; return true;

15
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs

@ -4,6 +4,7 @@ using IdentityServer4.Models;
using IdentityServer4.Services; using IdentityServer4.Services;
using IdentityServer4.Validation; using IdentityServer4.Validation;
using LINGYUN.Abp.WeChat; using LINGYUN.Abp.WeChat;
using LINGYUN.Abp.WeChat.Localization;
using LINGYUN.Abp.WeChat.OpenId; using LINGYUN.Abp.WeChat.OpenId;
using LINGYUN.Abp.WeChat.Security.Claims; using LINGYUN.Abp.WeChat.Security.Claims;
using LINGYUN.Abp.WeChat.Settings; using LINGYUN.Abp.WeChat.Settings;
@ -44,6 +45,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
protected IIdentityUserRepository UserRepository { get; } protected IIdentityUserRepository UserRepository { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; } protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
protected UserManager<IdentityUser> UserManager { get; } protected UserManager<IdentityUser> UserManager { get; }
protected IStringLocalizer<WeChatResource> WeChatLocalizer { get; }
protected IStringLocalizer<IdentityResource> IdentityLocalizer { get; } protected IStringLocalizer<IdentityResource> IdentityLocalizer { get; }
protected IStringLocalizer<AbpIdentityServerResource> IdentityServerLocalizer { get; } protected IStringLocalizer<AbpIdentityServerResource> IdentityServerLocalizer { get; }
@ -53,6 +55,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository, IIdentityUserRepository userRepository,
IdentitySecurityLogManager identitySecurityLogManager, IdentitySecurityLogManager identitySecurityLogManager,
IStringLocalizer<WeChatResource> wechatLocalizer,
IStringLocalizer<IdentityResource> identityLocalizer, IStringLocalizer<IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer) IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer)
{ {
@ -61,6 +64,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
UserRepository = userRepository; UserRepository = userRepository;
IdentitySecurityLogManager = identitySecurityLogManager; IdentitySecurityLogManager = identitySecurityLogManager;
WeChatOpenIdFinder = weChatOpenIdFinder; WeChatOpenIdFinder = weChatOpenIdFinder;
WeChatLocalizer = wechatLocalizer;
IdentityLocalizer = identityLocalizer; IdentityLocalizer = identityLocalizer;
IdentityServerLocalizer = identityServerLocalizer; IdentityServerLocalizer = identityServerLocalizer;
@ -94,7 +98,16 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
return; return;
} }
var wechatOpenId = await FindOpenIdAsync(wechatCode); WeChatOpenId wechatOpenId;
try
{
wechatOpenId = await FindOpenIdAsync(wechatCode);
}
catch(AbpWeChatException e)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, e.Message);
return;
}
var currentUser = await UserManager.FindByLoginAsync(LoginProvider, wechatOpenId.OpenId); var currentUser = await UserManager.FindByLoginAsync(LoginProvider, wechatOpenId.OpenId);
if (currentUser == null) if (currentUser == null)
{ {

3
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

20
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN.Abp.Saas.Application.Contracts.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Authorization.Abstractions" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Saas.Domain.Shared\LINGYUN.Abp.Saas.Domain.Shared.csproj" />
</ItemGroup>
</Project>

12
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasApplicationContractsModule.cs

@ -0,0 +1,12 @@
using Volo.Abp.Application;
using Volo.Abp.Modularity;
using Volo.Abp.Authorization;
namespace LINGYUN.Abp.Saas;
[DependsOn(typeof(AbpSaasDomainSharedModule))]
[DependsOn(typeof(AbpAuthorizationAbstractionsModule))]
[DependsOn(typeof(AbpDddApplicationContractsModule))]
public class AbpSaasApplicationContractsModule : AbpModule
{
}

8
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasRemoteServiceConsts.cs

@ -0,0 +1,8 @@
namespace LINGYUN.Abp.Saas;
public class AbpSaasRemoteServiceConsts
{
public const string RemoteServiceName = "AbpSaas";
public const string ModuleName = "saas";
}

5
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateDto.cs

@ -0,0 +1,5 @@
namespace LINGYUN.Abp.Saas.Editions;
public class EditionCreateDto : EditionCreateOrUpdateBase
{
}

13
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateOrUpdateBase.cs

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.Saas.Editions;
public abstract class EditionCreateOrUpdateBase : ExtensibleObject
{
[Required]
[DynamicStringLength(typeof(EditionConsts), nameof(EditionConsts.MaxDisplayNameLength))]
[Display(Name = "EditionName")]
public string DisplayName { get; set; }
}

9
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionDto.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.Saas.Editions;
public class EditionDto : ExtensibleFullAuditedEntityDto<Guid>
{
public string DisplayName { get; set; }
}

8
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionGetListInput.cs

@ -0,0 +1,8 @@
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.Saas.Editions;
public class EditionGetListInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
}

8
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionUpdateDto.cs

@ -0,0 +1,8 @@
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.Saas.Editions;
public class EditionUpdateDto : EditionCreateOrUpdateBase, IHasConcurrencyStamp
{
public string ConcurrencyStamp { get; set; }
}

14
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/IEditionAppService.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.Saas.Editions;
public interface IEditionAppService :
ICrudAppService<
EditionDto,
Guid,
EditionGetListInput,
EditionCreateDto,
EditionUpdateDto>
{
}

32
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissionDefinitionProvider.cs

@ -0,0 +1,32 @@
using LINGYUN.Abp.Saas.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas;
public class AbpSaasPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var saasGroup = context.AddGroup(AbpSaasPermissions.GroupName, L("Permission:Saas"));
var editionsPermission = saasGroup.AddPermission(AbpSaasPermissions.Editions.Default, L("Permission:EditionManagement"), multiTenancySide: MultiTenancySides.Host);
editionsPermission.AddChild(AbpSaasPermissions.Editions.Create, L("Permission:Create"), multiTenancySide: MultiTenancySides.Host);
editionsPermission.AddChild(AbpSaasPermissions.Editions.Update, L("Permission:Edit"), multiTenancySide: MultiTenancySides.Host);
editionsPermission.AddChild(AbpSaasPermissions.Editions.Delete, L("Permission:Delete"), multiTenancySide: MultiTenancySides.Host);
editionsPermission.AddChild(AbpSaasPermissions.Editions.ManageFeatures, L("Permission:ManageFeatures"), multiTenancySide: MultiTenancySides.Host);
var tenantsPermission = saasGroup.AddPermission(AbpSaasPermissions.Tenants.Default, L("Permission:TenantManagement"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(AbpSaasPermissions.Tenants.Create, L("Permission:Create"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(AbpSaasPermissions.Tenants.Update, L("Permission:Edit"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(AbpSaasPermissions.Tenants.Delete, L("Permission:Delete"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(AbpSaasPermissions.Tenants.ManageFeatures, L("Permission:ManageFeatures"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(AbpSaasPermissions.Tenants.ManageConnectionStrings, L("Permission:ManageConnectionStrings"), multiTenancySide: MultiTenancySides.Host);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<AbpSaasResource>(name);
}
}

32
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissions.cs

@ -0,0 +1,32 @@
using Volo.Abp.Reflection;
namespace LINGYUN.Abp.Saas;
public static class AbpSaasPermissions
{
public const string GroupName = "AbpSaas";
public static class Editions
{
public const string Default = GroupName + ".Editions";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string ManageFeatures = Default + ".ManageFeatures";
}
public static class Tenants
{
public const string Default = GroupName + ".Tenants";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string ManageFeatures = Default + ".ManageFeatures";
public const string ManageConnectionStrings = Default + ".ManageConnectionStrings";
}
public static string[] GetAll()
{
return ReflectionHelper.GetPublicConstantsRecursively(typeof(AbpSaasPermissions));
}
}

15
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionGetByNameInput.cs

@ -0,0 +1,15 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantConnectionGetByNameInput
{
[Required]
public Guid Id { get; set; }
[Required]
[DynamicStringLength(typeof(TenantConnectionStringConsts), nameof(TenantConnectionStringConsts.MaxNameLength))]
public string Name { get; set; }
}

15
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringCreateOrUpdate.cs

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantConnectionStringCreateOrUpdate
{
[Required]
[DynamicStringLength(typeof(TenantConnectionStringConsts), nameof(TenantConnectionStringConsts.MaxNameLength))]
public string Name { get; set; }
[Required]
[DynamicStringLength(typeof(TenantConnectionStringConsts), nameof(TenantConnectionStringConsts.MaxValueLength))]
public string Value { get; set; }
}

8
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringDto.cs

@ -0,0 +1,8 @@
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantConnectionStringDto
{
public string Name { get; set; }
public string Value { get; set; }
}

25
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateDto.cs

@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantCreateDto : TenantCreateOrUpdateBase
{
[Required]
[EmailAddress]
[MaxLength(256)]
public string AdminEmailAddress { get; set; }
[Required]
[MaxLength(128)]
public string AdminPassword { get; set; }
/// <summary>
/// 使用共享数据库
/// </summary>
public bool UseSharedDatabase { get; set; } = true;
/// <summary>
/// 默认数据库连接字符串
/// </summary>
public string DefaultConnectionString { get; set; }
}

22
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateOrUpdateBase.cs

@ -0,0 +1,22 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.Saas.Tenants;
public abstract class TenantCreateOrUpdateBase : ExtensibleObject
{
[Required]
[DynamicStringLength(typeof(TenantConsts), nameof(TenantConsts.MaxNameLength))]
public string Name { get; set; }
public bool IsActive { get; set; } = true;
public Guid? EditionId { get; set; }
public DateTime? EnableTime { get; set; }
public DateTime? DisableTime { get; set; }
}

19
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantDto.cs

@ -0,0 +1,19 @@
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantDto : ExtensibleFullAuditedEntityDto<Guid>
{
public string Name { get; set; }
public Guid? EditionId { get; set; }
public string EditionName { get; set; }
public bool IsActive { get; set; }
public DateTime? EnableTime { get; set; }
public DateTime? DisableTime { get; set; }
}

17
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetByNameInput.cs

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantGetByNameInput
{
[Required]
[DynamicStringLength(typeof(TenantConsts), nameof(TenantConsts.MaxNameLength))]
public string Name { get; set; }
public TenantGetByNameInput() { }
public TenantGetByNameInput(string name)
{
Name = name;
}
}

8
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetListInput.cs

@ -0,0 +1,8 @@
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantGetListInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
}

7
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantUpdateDto.cs

@ -0,0 +1,7 @@
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantUpdateDto : TenantCreateOrUpdateBase, IHasConcurrencyStamp
{
public string ConcurrencyStamp { get; set; }
}

34
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/ITenantAppService.cs

@ -0,0 +1,34 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.Saas.Tenants;
public interface ITenantAppService :
ICrudAppService<
TenantDto,
Guid,
TenantGetListInput,
TenantCreateDto,
TenantUpdateDto>
{
Task<TenantDto> GetAsync(
[Required] string name);
Task<TenantConnectionStringDto> GetConnectionStringAsync(
Guid id,
[Required] string connectionName);
Task<ListResultDto<TenantConnectionStringDto>> GetConnectionStringAsync(
Guid id);
Task<TenantConnectionStringDto> SetConnectionStringAsync(
Guid id,
TenantConnectionStringCreateOrUpdate input);
Task DeleteConnectionStringAsync(
Guid id,
[Required] string connectionName);
}

3
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

20
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN.Abp.Saas.Application.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Saas.Application.Contracts\LINGYUN.Abp.Saas.Application.Contracts.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.Saas.Domain\LINGYUN.Abp.Saas.Domain.csproj" />
</ItemGroup>
</Project>

12
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasAppServiceBase.cs

@ -0,0 +1,12 @@
using LINGYUN.Abp.Saas.Localization;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.Saas;
public abstract class AbpSaasAppServiceBase : ApplicationService
{
protected AbpSaasAppServiceBase()
{
ObjectMapperContext = typeof(AbpSaasApplicationModule);
LocalizationResource = typeof(AbpSaasResource);
}
}

33
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationAutoMapperProfile.cs

@ -0,0 +1,33 @@
using AutoMapper;
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.Tenants;
namespace LINGYUN.Abp.Saas;
public class AbpSaasApplicationAutoMapperProfile : Profile
{
public AbpSaasApplicationAutoMapperProfile()
{
CreateMap<TenantConnectionString, TenantConnectionStringDto>();
CreateMap<Tenant, TenantDto>()
.ForMember(dto => dto.EditionId, map =>
{
map.MapFrom((tenant, dto) =>
{
return tenant.Edition?.Id;
});
})
.ForMember(dto => dto.EditionName, map =>
{
map.MapFrom((tenant, dto) =>
{
return tenant.Edition?.DisplayName;
});
})
.MapExtraProperties();
CreateMap<Edition, EditionDto>()
.MapExtraProperties();
}
}

19
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationModule.cs

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Saas;
[DependsOn(typeof(AbpSaasDomainModule))]
[DependsOn(typeof(AbpSaasApplicationContractsModule))]
public class AbpSaasApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<AbpSaasApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<AbpSaasApplicationAutoMapperProfile>(validate: true);
});
}
}

87
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Editions/EditionAppService.cs

@ -0,0 +1,87 @@
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Data;
namespace LINGYUN.Abp.Saas.Editions;
[Authorize(AbpSaasPermissions.Editions.Default)]
public class EditionAppService : AbpSaasAppServiceBase, IEditionAppService
{
protected EditionManager EditionManager { get; }
protected IEditionRepository EditionRepository { get; }
public EditionAppService(
EditionManager editionManager,
IEditionRepository editionRepository)
{
EditionManager = editionManager;
EditionRepository = editionRepository;
}
[Authorize(AbpSaasPermissions.Editions.Create)]
public async virtual Task<EditionDto> CreateAsync(EditionCreateDto input)
{
var edition = await EditionManager.CreateAsync(input.DisplayName);
input.MapExtraPropertiesTo(edition);
await EditionRepository.InsertAsync(edition);
await CurrentUnitOfWork.SaveChangesAsync();
return ObjectMapper.Map<Edition, EditionDto>(edition);
}
[Authorize(AbpSaasPermissions.Editions.Delete)]
public async virtual Task DeleteAsync(Guid id)
{
var edition = await EditionRepository.GetAsync(id);
await EditionManager.DeleteAsync(edition);
}
public async virtual Task<EditionDto> GetAsync(Guid id)
{
var edition = await EditionRepository.GetAsync(id, false);
return ObjectMapper.Map<Edition, EditionDto>(edition);
}
public async virtual Task<PagedResultDto<EditionDto>> GetListAsync(EditionGetListInput input)
{
var totalCount = await EditionRepository.GetCountAsync(input.Filter);
var editions = await EditionRepository.GetListAsync(
input.Sorting,
input.MaxResultCount,
input.SkipCount,
input.Filter
);
return new PagedResultDto<EditionDto>(
totalCount,
ObjectMapper.Map<List<Edition>, List<EditionDto>>(editions)
);
}
[Authorize(AbpSaasPermissions.Editions.Update)]
public async virtual Task<EditionDto> UpdateAsync(Guid id, EditionUpdateDto input)
{
var edition = await EditionRepository.GetAsync(id, false);
if (!string.Equals(edition.DisplayName, input.DisplayName))
{
await EditionManager.ChangeDisplayNameAsync(edition, input.DisplayName);
}
edition.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
input.MapExtraPropertiesTo(edition);
await EditionRepository.UpdateAsync(edition);
await CurrentUnitOfWork.SaveChangesAsync();
return ObjectMapper.Map<Edition, EditionDto>(edition);
}
}

208
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Tenants/TenantAppService.cs

@ -0,0 +1,208 @@
using LINGYUN.Abp.MultiTenancy;
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;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Data;
namespace LINGYUN.Abp.Saas.Tenants;
[Authorize(AbpSaasPermissions.Tenants.Default)]
public class TenantAppService : AbpSaasAppServiceBase, ITenantAppService
{
protected IDistributedEventBus EventBus { get; }
protected ITenantRepository TenantRepository { get; }
protected ITenantManager TenantManager { get; }
public TenantAppService(
ITenantRepository tenantRepository,
ITenantManager tenantManager,
IDistributedEventBus eventBus)
{
EventBus = eventBus;
TenantRepository = tenantRepository;
TenantManager = tenantManager;
}
public virtual async Task<TenantDto> GetAsync(Guid id)
{
var tenant = await TenantRepository.FindAsync(id, false);
if (tenant == null)
{
throw new UserFriendlyException(L["TenantNotFoundById", id]);
}
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}
public virtual async Task<TenantDto> GetAsync(string name)
{
var tenant = await TenantRepository.FindByNameAsync(name, false);
if (tenant == null)
{
throw new UserFriendlyException(L["TenantNotFoundByName", name]);
}
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}
public virtual async Task<PagedResultDto<TenantDto>> GetListAsync(TenantGetListInput input)
{
var count = await TenantRepository.GetCountAsync(input.Filter);
var list = await TenantRepository.GetListAsync(
input.Sorting,
input.MaxResultCount,
input.SkipCount,
input.Filter
);
return new PagedResultDto<TenantDto>(
count,
ObjectMapper.Map<List<Tenant>, List<TenantDto>>(list)
);
}
[Authorize(AbpSaasPermissions.Tenants.Create)]
public virtual async Task<TenantDto> CreateAsync(TenantCreateDto input)
{
var tenant = await TenantManager.CreateAsync(input.Name);
tenant.IsActive = input.IsActive;
tenant.EditionId = input.EditionId;
tenant.SetEnableTime(input.EnableTime);
tenant.SetDisableTime(input.DisableTime);
input.MapExtraPropertiesTo(tenant);
if (!input.UseSharedDatabase && !input.DefaultConnectionString.IsNullOrWhiteSpace())
{
tenant.SetDefaultConnectionString(input.DefaultConnectionString);
}
await TenantRepository.InsertAsync(tenant);
CurrentUnitOfWork.OnCompleted(async () =>
{
var createEventData = new CreateEventData
{
Id = tenant.Id,
Name = tenant.Name,
AdminUserId = GuidGenerator.Create(),
AdminEmailAddress = input.AdminEmailAddress,
AdminPassword = input.AdminPassword
};
// 因为项目各自独立,租户增加时添加管理用户必须通过事件总线
// 而 TenantEto 对象没有包含所需的用户名密码,需要独立发布事件
await EventBus.PublishAsync(createEventData);
});
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}
[Authorize(AbpSaasPermissions.Tenants.Update)]
public virtual async Task<TenantDto> UpdateAsync(Guid id, TenantUpdateDto input)
{
var tenant = await TenantRepository.GetAsync(id, false);
if (!string.Equals(tenant.Name, input.Name))
{
await TenantManager.ChangeNameAsync(tenant, input.Name);
}
tenant.IsActive = input.IsActive;
tenant.EditionId = input.EditionId;
tenant.SetEnableTime(input.EnableTime);
tenant.SetDisableTime(input.DisableTime);
tenant.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
input.MapExtraPropertiesTo(tenant);
await TenantRepository.UpdateAsync(tenant);
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}
[Authorize(AbpSaasPermissions.Tenants.Delete)]
public virtual async Task DeleteAsync(Guid id)
{
var tenant = await TenantRepository.FindAsync(id);
if (tenant == null)
{
return;
}
await TenantRepository.DeleteAsync(tenant);
}
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual async Task<TenantConnectionStringDto> GetConnectionStringAsync(Guid id, string name)
{
var tenant = await TenantRepository.GetAsync(id);
var tenantConnectionString = tenant.FindConnectionString(name);
return new TenantConnectionStringDto
{
Name = name,
Value = tenantConnectionString
};
}
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual async Task<ListResultDto<TenantConnectionStringDto>> GetConnectionStringAsync(Guid id)
{
var tenant = await TenantRepository.GetAsync(id);
return new ListResultDto<TenantConnectionStringDto>(
ObjectMapper.Map<List<TenantConnectionString>, List<TenantConnectionStringDto>>(tenant.ConnectionStrings.ToList()));
}
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual async Task<TenantConnectionStringDto> SetConnectionStringAsync(Guid id, TenantConnectionStringCreateOrUpdate input)
{
var tenant = await TenantRepository.GetAsync(id);
if (tenant.FindConnectionString(input.Name) == null)
{
CurrentUnitOfWork.OnCompleted(async () =>
{
var eventData = new ConnectionStringCreatedEventData
{
TenantId = tenant.Id,
TenantName = tenant.Name,
Name = input.Name
};
await EventBus.PublishAsync(eventData);
});
}
tenant.SetConnectionString(input.Name, input.Value);
return new TenantConnectionStringDto
{
Name = input.Name,
Value = input.Value
};
}
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual async Task DeleteConnectionStringAsync(Guid id, string name)
{
var tenant = await TenantRepository.GetAsync(id);
tenant.RemoveConnectionString(name);
CurrentUnitOfWork.OnCompleted(async () =>
{
var eventData = new ConnectionStringDeletedEventData
{
TenantId = tenant.Id,
TenantName = tenant.Name,
Name = name
};
await EventBus.PublishAsync(eventData);
});
await TenantRepository.UpdateAsync(tenant);
}
}

3
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

23
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN.Abp.Saas.Domain.Shared.csproj

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\Saas\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\Saas\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Validation" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
</Project>

34
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasDomainSharedModule.cs

@ -0,0 +1,34 @@
using LINGYUN.Abp.Saas.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.Validation;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.Saas;
[DependsOn(typeof(AbpValidationModule))]
public class AbpSaasDomainSharedModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpSaasDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<AbpSaasResource>("en")
.AddVirtualJson("/LINGYUN/Abp/Saas/Localization/Resources");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace(AbpSaasErrorCodes.Namespace, typeof(AbpSaasResource));
// 见租户管理模块
options.MapCodeNamespace("Volo.AbpIo.MultiTenancy", typeof(AbpSaasResource));
});
}
}

10
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasErrorCodes.cs

@ -0,0 +1,10 @@
namespace LINGYUN.Abp.Saas;
public static class AbpSaasErrorCodes
{
public const string Namespace = "Saas";
public const string DuplicateEditionDisplayName = Namespace + ":010001";
public const string DeleteUsedEdition = Namespace + ":010002";
public const string DuplicateTenantName = Namespace + ":020001";
}

6
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionConsts.cs

@ -0,0 +1,6 @@
namespace LINGYUN.Abp.Saas.Editions;
public static class EditionConsts
{
public static int MaxDisplayNameLength { get; set; } = 64;
}

11
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionEto.cs

@ -0,0 +1,11 @@
using System;
namespace LINGYUN.Abp.Saas.Editions;
[Serializable]
public class EditionEto
{
public Guid Id { get; set; }
public string DisplayName { get; set; }
}

8
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/AbpSaasResource.cs

@ -0,0 +1,8 @@
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Saas.Localization;
[LocalizationResourceName("AbpSaas")]
public class AbpSaasResource
{
}

43
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/en.json

@ -0,0 +1,43 @@
{
"culture": "en",
"texts": {
"Saas:010001": "Unable to create duplicate editions {DisplayName}!",
"Saas:010002": "Tried to delete the edition in use: {DisplayName}!",
"Saas:020001": "Unable to create duplicate tenants {Name}!",
"Saas:020002": "The database string that cannot be connected!",
"Volo.AbpIo.MultiTenancy:010001": "The tenant is unavailable or restricted!",
"Volo.AbpIo.MultiTenancy:010002": "Tenant unavailable!",
"Menu:Saas": "Saas",
"Editions": "Editions",
"NewEdition": "New edition",
"EditionName": "Edition name",
"DisplayName:EditionName": "Edition name",
"EditionDeletionConfirmationMessage": "Edition '{0}' will be deleted. Do you confirm that?",
"Tenants": "Tenants",
"NewTenant": "New tenant",
"TenantName": "Tenant name",
"DisplayName:TenantName": "Tenant name",
"TenantDeletionConfirmationMessage": "Tenant '{0}' will be deleted. Do you confirm that?",
"ConnectionStrings": "Connection Strings",
"DisplayName:DefaultConnectionString": "Default Connection String",
"DisplayName:UseSharedDatabase": "Use the Shared Database",
"DisplayName:Name": "Name",
"DisplayName:Value": "Value",
"DisplayName:IsActive": "Active",
"DisplayName:EnableTime": "Enable Time",
"DisplayName:DisableTime": "Disable Time",
"ManageHostFeatures": "Manage Host features",
"Permission:Saas": "Saas",
"Permission:EditionManagement": "Edition management",
"Permission:TenantManagement": "Tenant management",
"Permission:Create": "Create",
"Permission:Edit": "Edit",
"Permission:Delete": "Delete",
"Permission:ManageConnectionStrings": "Manage connection strings",
"Permission:ManageFeatures": "Manage features",
"DisplayName:AdminEmailAddress": "Admin Email Address",
"DisplayName:AdminPassword": "Admin Password",
"TenantNotFoundById": "Tenant: {0} not found!",
"TenantNotFoundByName": "Tenant: {0} not found!"
}
}

43
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/zh-Hans.json

@ -0,0 +1,43 @@
{
"culture": "zh-Hans",
"texts": {
"Saas:010001": "已经存在名为 {DisplayName} 的版本!",
"Saas:010002": "试图删除正在使用的版本: {DisplayName}!",
"Saas:020001": "已经存在名为 {Name} 的租户!",
"Saas:020002": "无法连接的数据库字符串!",
"Volo.AbpIo.MultiTenancy:010001": "租户不可用或受限制!",
"Volo.AbpIo.MultiTenancy:010002": "租户不可用!",
"Menu:Saas": "Saas",
"Editions": "版本",
"NewEdition": "新版本",
"EditionName": "版本名称",
"DisplayName:EditionName": "版本名称",
"EditionDeletionConfirmationMessage": "版本 '{0}' 将被删除. 你确定吗?",
"Tenants": "租户",
"NewTenant": "新租户",
"TenantName": "租户名称",
"DisplayName:TenantName": "租户名称",
"TenantDeletionConfirmationMessage": "租户 '{0}' 将被删除. 你确定吗?",
"ConnectionStrings": "连接字符串",
"DisplayName:DefaultConnectionString": "默认连接字符串",
"DisplayName:UseSharedDatabase": "使用共享数据库",
"DisplayName:Name": "名称",
"DisplayName:Value": "值",
"DisplayName:IsActive": "启用",
"DisplayName:EnableTime": "启用时间",
"DisplayName:DisableTime": "禁用时间",
"ManageHostFeatures": "管理Host特性",
"Permission:Saas": "Saas",
"Permission:EditionManagement": "版本管理",
"Permission:TenantManagement": "租户管理",
"Permission:Create": "创建",
"Permission:Edit": "编辑",
"Permission:Delete": "删除",
"Permission:ManageConnectionStrings": "管理连接字符串",
"Permission:ManageFeatures": "管理功能",
"DisplayName:AdminEmailAddress": "管理员电子邮件地址",
"DisplayName:AdminPassword": "管理员密码",
"TenantNotFoundById": "租户: {0} 不存在!",
"TenantNotFoundByName": "租户: {0} 不存在!"
}
}

16
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfiguration.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp.ObjectExtending.Modularity;
namespace LINGYUN.Abp.Saas.ObjectExtending;
public class SaasModuleExtensionConfiguration : ModuleExtensionConfiguration
{
public SaasModuleExtensionConfiguration ConfigureTenant(
Action<EntityExtensionConfiguration> configureAction)
{
return this.ConfigureEntity(
SaasModuleExtensionConsts.EntityNames.Edition,
configureAction
);
}
}

17
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfigurationDictionaryExtensions.cs

@ -0,0 +1,17 @@
using System;
using Volo.Abp.ObjectExtending.Modularity;
namespace LINGYUN.Abp.Saas.ObjectExtending;
public static class SaasModuleExtensionConfigurationDictionaryExtensions
{
public static ModuleExtensionConfigurationDictionary ConfigureTenantManagement(
this ModuleExtensionConfigurationDictionary modules,
Action<SaasModuleExtensionConfiguration> configureAction)
{
return modules.ConfigureModule(
SaasModuleExtensionConsts.ModuleName,
configureAction
);
}
}

11
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConsts.cs

@ -0,0 +1,11 @@
namespace LINGYUN.Abp.Saas.ObjectExtending;
public class SaasModuleExtensionConsts
{
public const string ModuleName = "Saas";
public static class EntityNames
{
public const string Edition = "Edition";
public const string Tenant = "Tenant";
}
}

7
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConnectionStringConsts.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Abp.Saas.Tenants;
public static class TenantConnectionStringConsts
{
public static int MaxNameLength { get; set; } = 64;
public static int MaxValueLength { get; set; } = 1024;
}

6
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConsts.cs

@ -0,0 +1,6 @@
namespace LINGYUN.Abp.Saas.Tenants;
public static class TenantConsts
{
public static int MaxNameLength { get; set; } = 64;
}

11
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantEto.cs

@ -0,0 +1,11 @@
using System;
namespace LINGYUN.Abp.Saas.Tenants;
[Serializable]
public class TenantEto
{
public Guid Id { get; set; }
public string Name { get; set; }
}

3
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

23
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN.Abp.Saas.Domain.csproj

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Caching" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\tenants\LINGYUN.Abp.MultiTenancy.Editions\LINGYUN.Abp.MultiTenancy.Editions.csproj" />
<ProjectReference Include="..\..\tenants\LINGYUN.Abp.MultiTenancy\LINGYUN.Abp.MultiTenancy.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.Saas.Domain.Shared\LINGYUN.Abp.Saas.Domain.Shared.csproj" />
</ItemGroup>
</Project>

12
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDbProperties.cs

@ -0,0 +1,12 @@
using Volo.Abp.Data;
namespace LINGYUN.Abp.Saas;
public class AbpSaasDbProperties
{
public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix;
public static string DbSchema { get; set; } = AbpCommonDbProperties.DbSchema;
public const string ConnectionStringName = "AbpSaas";
}

58
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainMappingProfile.cs

@ -0,0 +1,58 @@
using AutoMapper;
using LINGYUN.Abp.MultiTenancy.Editions;
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.Tenants;
using System;
using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas;
public class AbpSaasDomainMappingProfile : Profile
{
public AbpSaasDomainMappingProfile()
{
CreateMap<Edition, EditionInfo>();
CreateMap<Edition, EditionEto>();
CreateMap<Tenant, TenantConfiguration>()
.ForMember(ti => ti.ConnectionStrings, opts =>
{
opts.MapFrom((tenant, ti) =>
{
var connStrings = new ConnectionStrings();
foreach (var connectionString in tenant.ConnectionStrings)
{
connStrings[connectionString.Name] = connectionString.Value;
}
return connStrings;
});
})
.ForMember(ti => ti.IsActive, opts =>
{
opts.MapFrom((tenant, ti) =>
{
if (!tenant.IsActive)
{
return false;
}
// Injection IClock ?
if (tenant.EnableTime.HasValue && DateTime.Now < tenant.EnableTime)
{
return false;
}
if(tenant.DisableTime.HasValue && DateTime.Now > tenant.DisableTime)
{
return false;
}
return true;
});
});
CreateMap<Tenant, TenantEto>();
}
}

58
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainModule.cs

@ -0,0 +1,58 @@
using LINGYUN.Abp.MultiTenancy.Editions;
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.ObjectExtending;
using LINGYUN.Abp.Saas.Tenants;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Domain;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending.Modularity;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.Saas;
[DependsOn(typeof(AbpSaasDomainSharedModule))]
[DependsOn(typeof(AbpAutoMapperModule))]
[DependsOn(typeof(AbpDddDomainModule))]
[DependsOn(typeof(AbpMultiTenancyEditionsModule))]
public class AbpSaasDomainModule : AbpModule
{
private static readonly OneTimeRunner OneTimeRunner = new();
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<AbpSaasDomainModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<AbpSaasDomainMappingProfile>(validate: true);
});
Configure<AbpDistributedEntityEventOptions>(options =>
{
options.EtoMappings.Add<Edition, EditionEto>();
options.EtoMappings.Add<Tenant, TenantEto>();
options.AutoEventSelectors.Add<Edition>();
options.AutoEventSelectors.Add<Tenant>();
});
}
public override void PostConfigureServices(ServiceConfigurationContext context)
{
OneTimeRunner.Run(() =>
{
ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity(
SaasModuleExtensionConsts.ModuleName,
SaasModuleExtensionConsts.EntityNames.Edition,
typeof(Edition)
);
ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity(
SaasModuleExtensionConsts.ModuleName,
SaasModuleExtensionConsts.EntityNames.Tenant,
typeof(Tenant)
);
});
}
}

26
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/Edition.cs

@ -0,0 +1,26 @@
using JetBrains.Annotations;
using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
namespace LINGYUN.Abp.Saas.Editions;
public class Edition : FullAuditedAggregateRoot<Guid>
{
public virtual string DisplayName { get; protected set; }
protected Edition()
{
}
protected internal Edition(Guid id, [NotNull] string displayName)
: base(id)
{
SetDisplayName(displayName);
}
protected internal virtual void SetDisplayName([NotNull] string displayName)
{
DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), EditionConsts.MaxDisplayNameLength);
}
}

29
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionCacheItem.cs

@ -0,0 +1,29 @@
using LINGYUN.Abp.MultiTenancy.Editions;
using System;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas.Editions;
[Serializable]
[IgnoreMultiTenancy]
public class EditionCacheItem
{
private const string CacheKeyFormat = "t:{0}";
public EditionInfo Value { get; set; }
public EditionCacheItem()
{
}
public EditionCacheItem(EditionInfo value)
{
Value = value;
}
public static string CalculateCacheKey(Guid tenantId)
{
return string.Format(CacheKeyFormat, tenantId.ToString());
}
}

42
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionDataSeeder.cs

@ -0,0 +1,42 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
namespace LINGYUN.Abp.Saas.Editions;
public class EditionDataSeeder : IEditionDataSeeder, ITransientDependency
{
protected IGuidGenerator GuidGenerator { get; }
protected IEditionRepository EditionRepository { get; }
public EditionDataSeeder(
IGuidGenerator guidGenerator,
IEditionRepository editionRepository)
{
GuidGenerator = guidGenerator;
EditionRepository = editionRepository;
}
public async virtual Task SeedDefaultEditionsAsync()
{
await AddEditionIfNotExistsAsync("Free");
await AddEditionIfNotExistsAsync("Standard");
await AddEditionIfNotExistsAsync("Professional");
await AddEditionIfNotExistsAsync("Enterprise");
}
protected async virtual Task AddEditionIfNotExistsAsync(string displayName)
{
if (await EditionRepository.FindByDisplayNameAsync(displayName) != null)
{
return;
}
await EditionRepository.InsertAsync(
new Edition(
GuidGenerator.Create(),
displayName
)
);
}
}

54
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionManager.cs

@ -0,0 +1,54 @@
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
namespace LINGYUN.Abp.Saas.Editions;
public class EditionManager : DomainService
{
protected IEditionRepository EditionRepository { get; }
public EditionManager(IEditionRepository editionRepository)
{
EditionRepository = editionRepository;
}
public async virtual Task DeleteAsync(Edition edition)
{
if (await EditionRepository.CheckUsedByTenantAsync(edition.Id))
{
throw new BusinessException(AbpSaasErrorCodes.DeleteUsedEdition)
.WithData(nameof(Edition.DisplayName), edition.DisplayName);
}
await EditionRepository.DeleteAsync(edition);
}
public async virtual Task<Edition> CreateAsync(string displayName)
{
Check.NotNull(displayName, nameof(displayName));
await ValidateDisplayNameAsync(displayName);
return new Edition(GuidGenerator.Create(), displayName);
}
public virtual async Task ChangeDisplayNameAsync(Edition edition, string displayName)
{
Check.NotNull(edition, nameof(edition));
Check.NotNull(displayName, nameof(displayName));
await ValidateDisplayNameAsync(displayName, edition.Id);
edition.SetDisplayName(displayName);
}
protected virtual async Task ValidateDisplayNameAsync(string displayName, Guid? expectedId = null)
{
var edition = await EditionRepository.FindByDisplayNameAsync(displayName);
if (edition != null && edition.Id != expectedId)
{
throw new BusinessException(AbpSaasErrorCodes.DuplicateEditionDisplayName)
.WithData(nameof(Edition.DisplayName), displayName);
}
}
}

65
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionStore.cs

@ -0,0 +1,65 @@
using JetBrains.Annotations;
using LINGYUN.Abp.MultiTenancy.Editions;
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping;
namespace LINGYUN.Abp.Saas.Editions;
public class EditionStore : IEditionStore, ITransientDependency
{
protected IEditionRepository EditionRepository { get; }
protected IObjectMapper<AbpSaasDomainModule> ObjectMapper { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IDistributedCache<EditionCacheItem> Cache { get; }
public EditionStore(
IEditionRepository editionRepository,
IObjectMapper<AbpSaasDomainModule> objectMapper,
ICurrentTenant currentTenant,
IDistributedCache<EditionCacheItem> cache)
{
EditionRepository = editionRepository;
ObjectMapper = objectMapper;
CurrentTenant = currentTenant;
Cache = cache;
}
public async virtual Task<EditionInfo> FindByTenantAsync(Guid tenantId)
{
return (await GetCacheItemAsync(tenantId)).Value;
}
protected async virtual Task<EditionCacheItem> GetCacheItemAsync(Guid tenantId)
{
var cacheKey = CalculateCacheKey(tenantId);
var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null)
{
return cacheItem;
}
using (CurrentTenant.Change(null))
{
var edition = await EditionRepository.FindByTenantIdAsync(tenantId);
return await SetCacheAsync(cacheKey, edition);
}
}
protected async virtual Task<EditionCacheItem> SetCacheAsync(string cacheKey, [CanBeNull] Edition edition)
{
var editionInfo = edition != null ? ObjectMapper.Map<Edition, EditionInfo>(edition) : null;
var cacheItem = new EditionCacheItem(editionInfo);
await Cache.SetAsync(cacheKey, cacheItem, considerUow: true);
return cacheItem;
}
protected virtual string CalculateCacheKey(Guid tenantId)
{
return EditionCacheItem.CalculateCacheKey(tenantId);
}
}

8
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionDataSeeder.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.Saas.Editions;
public interface IEditionDataSeeder
{
Task SeedDefaultEditionsAsync();
}

33
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionRepository.cs

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.Saas.Editions;
public interface IEditionRepository : IBasicRepository<Edition, Guid>
{
Task<bool> CheckUsedByTenantAsync(
Guid id,
CancellationToken cancellationToken = default);
Task<Edition> FindByDisplayNameAsync(
string displayName,
CancellationToken cancellationToken = default);
Task<Edition> FindByTenantIdAsync(
Guid tenantId,
CancellationToken cancellationToken = default);
Task<List<Edition>> GetListAsync(
string sorting = null,
int maxResultCount = 10,
int skipCount = 0,
string filter = null,
CancellationToken cancellationToken = default);
Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default);
}

13
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantManager.cs

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Domain.Services;
namespace LINGYUN.Abp.Saas.Tenants;
public interface ITenantManager : IDomainService
{
[NotNull]
Task<Tenant> CreateAsync([NotNull] string name);
Task ChangeNameAsync([NotNull] Tenant tenant, [NotNull] string name);
}

40
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantRepository.cs

@ -0,0 +1,40 @@

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.Saas.Tenants;
public interface ITenantRepository : IBasicRepository<Tenant, Guid>
{
[Obsolete("Use FindByNameAsync method.")]
Tenant FindByName(
string name,
bool includeDetails = true
);
[Obsolete("Use FindAsync method.")]
Tenant FindById(
Guid id,
bool includeDetails = true
);
Task<Tenant> FindByNameAsync(
string name,
bool includeDetails = true,
CancellationToken cancellationToken = default);
Task<List<Tenant>> GetListAsync(
string sorting = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
bool includeDetails = false,
CancellationToken cancellationToken = default);
Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default);
}

102
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/Tenant.cs

@ -0,0 +1,102 @@
using JetBrains.Annotations;
using LINGYUN.Abp.Saas.Editions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
namespace LINGYUN.Abp.Saas.Tenants;
public class Tenant : FullAuditedAggregateRoot<Guid>
{
protected const string DefaultConnectionStringName = Volo.Abp.Data.ConnectionStrings.DefaultConnectionStringName;
public virtual string Name { get; protected set; }
public virtual bool IsActive { get; set; }
public virtual DateTime? EnableTime { get; protected set; }
public virtual DateTime? DisableTime { get; protected set; }
public virtual Guid? EditionId { get; set; }
public virtual Edition Edition { get; set; }
public virtual ICollection<TenantConnectionString> ConnectionStrings { get; protected set; }
protected Tenant()
{
ConnectionStrings = new Collection<TenantConnectionString>();
}
protected internal Tenant(Guid id, [NotNull] string name)
: base(id)
{
SetName(name);
ConnectionStrings = new Collection<TenantConnectionString>();
}
public virtual void SetEnableTime(DateTime? enableTime = null)
{
EnableTime = enableTime;
}
public virtual void SetDisableTime(DateTime? disableTime = null)
{
DisableTime = disableTime;
}
[CanBeNull]
public virtual string FindDefaultConnectionString()
{
return FindConnectionString(DefaultConnectionStringName);
}
[CanBeNull]
public virtual string FindConnectionString(string name)
{
return ConnectionStrings.FirstOrDefault(c => c.Name == name)?.Value;
}
public virtual void SetDefaultConnectionString(string connectionString)
{
SetConnectionString(DefaultConnectionStringName, connectionString);
}
public virtual void SetConnectionString(string name, string connectionString)
{
var tenantConnectionString = ConnectionStrings.FirstOrDefault(x => x.Name == name);
if (tenantConnectionString != null)
{
tenantConnectionString.SetValue(connectionString);
}
else
{
ConnectionStrings.Add(new TenantConnectionString(Id, name, connectionString));
}
}
public virtual void RemoveDefaultConnectionString()
{
RemoveConnectionString(DefaultConnectionStringName);
}
public virtual void RemoveConnectionString(string name)
{
var tenantConnectionString = ConnectionStrings.FirstOrDefault(x => x.Name == name);
if (tenantConnectionString != null)
{
ConnectionStrings.Remove(tenantConnectionString);
}
}
protected internal virtual void SetName([NotNull] string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConsts.MaxNameLength);
}
}

35
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItem.cs

@ -0,0 +1,35 @@
using System;
using Volo.Abp;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas.Tenants;
[Serializable]
[IgnoreMultiTenancy]
public class TenantCacheItem
{
private const string CacheKeyFormat = "i:{0},n:{1}";
public TenantConfiguration Value { get; set; }
public TenantCacheItem()
{
}
public TenantCacheItem(TenantConfiguration value)
{
Value = value;
}
public static string CalculateCacheKey(Guid? id, string name)
{
if (id == null && name.IsNullOrWhiteSpace())
{
throw new AbpException("Both id and name can't be invalid.");
}
return string.Format(CacheKeyFormat,
id?.ToString() ?? "null",
(name.IsNullOrWhiteSpace() ? "null" : name));
}
}

25
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItemInvalidator.cs

@ -0,0 +1,25 @@
using LINGYUN.Abp.Saas.Editions;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantCacheItemInvalidator : ILocalEventHandler<EntityChangedEventData<Tenant>>, ITransientDependency
{
protected IDistributedCache<TenantCacheItem> Cache { get; }
public TenantCacheItemInvalidator(IDistributedCache<TenantCacheItem> cache)
{
Cache = cache;
}
public virtual async Task HandleEventAsync(EntityChangedEventData<Tenant> eventData)
{
await Cache.RemoveAsync(TenantCacheItem.CalculateCacheKey(eventData.Entity.Id, eventData.Entity.Name), considerUow: true);
// 同时移除租户版本缓存
await Cache.RemoveAsync(EditionCacheItem.CalculateCacheKey(eventData.Entity.Id), considerUow: true);
}
}

37
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantConnectionString.cs

@ -0,0 +1,37 @@
using JetBrains.Annotations;
using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantConnectionString : Entity
{
public virtual Guid TenantId { get; protected set; }
public virtual string Name { get; protected set; }
public virtual string Value { get; protected set; }
protected TenantConnectionString()
{
}
public TenantConnectionString(Guid tenantId, [NotNull] string name, [NotNull] string value)
{
TenantId = tenantId;
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConnectionStringConsts.MaxNameLength);
SetValue(value);
}
public virtual void SetValue([NotNull] string value)
{
Value = Check.NotNullOrWhiteSpace(value, nameof(value), TenantConnectionStringConsts.MaxValueLength);
}
public override object[] GetKeys()
{
return new object[] { TenantId, Name };
}
}

44
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantManager.cs

@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantManager : DomainService, ITenantManager
{
protected ITenantRepository TenantRepository { get; }
public TenantManager(ITenantRepository tenantRepository)
{
TenantRepository = tenantRepository;
}
public virtual async Task<Tenant> CreateAsync(string name)
{
Check.NotNull(name, nameof(name));
await ValidateNameAsync(name);
return new Tenant(GuidGenerator.Create(), name);
}
public virtual async Task ChangeNameAsync(Tenant tenant, string name)
{
Check.NotNull(tenant, nameof(tenant));
Check.NotNull(name, nameof(name));
await ValidateNameAsync(name, tenant.Id);
tenant.SetName(name);
}
protected virtual async Task ValidateNameAsync(string name, Guid? expectedId = null)
{
var tenant = await TenantRepository.FindByNameAsync(name);
if (tenant != null && tenant.Id != expectedId)
{
throw new BusinessException(AbpSaasErrorCodes.DuplicateTenantName)
.WithData(nameof(Tenant.Name), name);
}
}
}

137
aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantStore.cs

@ -0,0 +1,137 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping;
namespace LINGYUN.Abp.Saas.Tenants;
public class TenantStore : ITenantStore, ITransientDependency
{
protected ITenantRepository TenantRepository { get; }
protected IObjectMapper<AbpSaasDomainModule> ObjectMapper { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IDistributedCache<TenantCacheItem> Cache { get; }
public TenantStore(
ITenantRepository tenantRepository,
IObjectMapper<AbpSaasDomainModule> objectMapper,
ICurrentTenant currentTenant,
IDistributedCache<TenantCacheItem> cache)
{
TenantRepository = tenantRepository;
ObjectMapper = objectMapper;
CurrentTenant = currentTenant;
Cache = cache;
}
public virtual async Task<TenantConfiguration> FindAsync(string name)
{
return (await GetCacheItemAsync(null, name)).Value;
}
public virtual async Task<TenantConfiguration> FindAsync(Guid id)
{
return (await GetCacheItemAsync(id, null)).Value;
}
[Obsolete("Use FindAsync method.")]
public virtual TenantConfiguration Find(string name)
{
return (GetCacheItem(null, name)).Value;
}
[Obsolete("Use FindAsync method.")]
public virtual TenantConfiguration Find(Guid id)
{
return (GetCacheItem(id, null)).Value;
}
protected virtual async Task<TenantCacheItem> GetCacheItemAsync(Guid? id, string name)
{
var cacheKey = CalculateCacheKey(id, name);
var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null)
{
return cacheItem;
}
if (id.HasValue)
{
using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities!
{
var tenant = await TenantRepository.FindAsync(id.Value);
return await SetCacheAsync(cacheKey, tenant);
}
}
if (!name.IsNullOrWhiteSpace())
{
using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities!
{
var tenant = await TenantRepository.FindByNameAsync(name);
return await SetCacheAsync(cacheKey, tenant);
}
}
throw new AbpException("Both id and name can't be invalid.");
}
protected virtual async Task<TenantCacheItem> SetCacheAsync(string cacheKey, [CanBeNull] Tenant tenant)
{
var tenantConfiguration = tenant != null ? ObjectMapper.Map<Tenant, TenantConfiguration>(tenant) : null;
var cacheItem = new TenantCacheItem(tenantConfiguration);
await Cache.SetAsync(cacheKey, cacheItem, considerUow: true);
return cacheItem;
}
[Obsolete("Use GetCacheItemAsync method.")]
protected virtual TenantCacheItem GetCacheItem(Guid? id, string name)
{
var cacheKey = CalculateCacheKey(id, name);
var cacheItem = Cache.Get(cacheKey, considerUow: true);
if (cacheItem != null)
{
return cacheItem;
}
if (id.HasValue)
{
using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities!
{
var tenant = TenantRepository.FindById(id.Value);
return SetCache(cacheKey, tenant);
}
}
if (!name.IsNullOrWhiteSpace())
{
using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities!
{
var tenant = TenantRepository.FindByName(name);
return SetCache(cacheKey, tenant);
}
}
throw new AbpException("Both id and name can't be invalid.");
}
[Obsolete("Use SetCacheAsync method.")]
protected virtual TenantCacheItem SetCache(string cacheKey, [CanBeNull] Tenant tenant)
{
var tenantConfiguration = tenant != null ? ObjectMapper.Map<Tenant, TenantConfiguration>(tenant) : null;
var cacheItem = new TenantCacheItem(tenantConfiguration);
Cache.Set(cacheKey, cacheItem, considerUow: true);
return cacheItem;
}
protected virtual string CalculateCacheKey(Guid? id, string name)
{
return TenantCacheItem.CalculateCacheKey(id, name);
}
}

3
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

19
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN.Abp.Saas.EntityFrameworkCore.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Saas.Domain\LINGYUN.Abp.Saas.Domain.csproj" />
</ItemGroup>
</Project>

67
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasDbContextModelCreatingExtensions.cs

@ -0,0 +1,67 @@
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.Tenants;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace LINGYUN.Abp.Saas.EntityFrameworkCore;
public static class AbpSaasDbContextModelCreatingExtensions
{
public static void ConfigureSaas(
this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
if (builder.IsTenantOnlyDatabase())
{
return;
}
builder.Entity<Edition>(b =>
{
b.ToTable(AbpSaasDbProperties.DbTablePrefix + "Editions", AbpSaasDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(t => t.DisplayName)
.HasMaxLength(EditionConsts.MaxDisplayNameLength)
.IsRequired();
b.HasIndex(u => u.DisplayName);
b.ApplyObjectExtensionMappings();
});
builder.Entity<Tenant>(b =>
{
b.ToTable(AbpSaasDbProperties.DbTablePrefix + "Tenants", AbpSaasDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(t => t.Name).IsRequired().HasMaxLength(TenantConsts.MaxNameLength);
b.HasMany(u => u.ConnectionStrings).WithOne().HasForeignKey(uc => uc.TenantId).IsRequired();
b.HasIndex(u => u.Name);
b.ApplyObjectExtensionMappings();
});
builder.Entity<TenantConnectionString>(b =>
{
b.ToTable(AbpSaasDbProperties.DbTablePrefix + "TenantConnectionStrings", AbpSaasDbProperties.DbSchema);
b.ConfigureByConvention();
b.HasKey(x => new { x.TenantId, x.Name });
b.Property(cs => cs.Name).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxNameLength);
b.Property(cs => cs.Value).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxValueLength);
b.ApplyObjectExtensionMappings();
});
builder.TryConfigureObjectExtensions<SaasDbContext>();
}
}

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasEntityFrameworkCoreModule.cs

@ -0,0 +1,30 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Saas.EntityFrameworkCore;
[DependsOn(typeof(AbpSaasDomainModule))]
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
public class AbpSaasEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<SaasDbContext>(options =>
{
options.AddDefaultRepositories<ISaasDbContext>();
});
Configure<AbpDbConnectionOptions>(options =>
{
// 将租户管理连接字符串聚合到 Saas 模块中
options.Databases.Configure(AbpSaasDbProperties.ConnectionStringName,
database =>
{
database.MappedConnections.Add("AbpTenantManagement");
database.IsUsedByTenants = false;
});
});
}
}

84
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreEditionRepository.cs

@ -0,0 +1,84 @@
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.Tenants;
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.Abp.Saas.EntityFrameworkCore;
public class EfCoreEditionRepository : EfCoreRepository<ISaasDbContext, Edition, Guid>, IEditionRepository
{
public EfCoreEditionRepository(
IDbContextProvider<ISaasDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async virtual Task<bool> CheckUsedByTenantAsync(
Guid id,
CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
var tenantDbSet = dbContext.Set<Tenant>();
return await tenantDbSet
.AnyAsync(x => x.EditionId == id, GetCancellationToken(cancellationToken));
}
public async virtual Task<Edition> FindByDisplayNameAsync(
string displayName,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.OrderBy(t => t.Id)
.FirstOrDefaultAsync(t => t.DisplayName == displayName, GetCancellationToken(cancellationToken));
}
public async virtual Task<Edition> FindByTenantIdAsync(
Guid tenantId,
CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
var editionDbSet = dbContext.Set<Edition>();
var tenantDbSet = dbContext.Set<Tenant>();
var queryable = from tenant in tenantDbSet
join edition in editionDbSet
on tenant.EditionId equals edition.Id
where tenant.Id == tenantId
select edition;
return await queryable
.OrderBy(t => t.Id)
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
}
public async virtual Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.WhereIf(!filter.IsNullOrWhiteSpace(), x => x.DisplayName.Contains(filter))
.CountAsync(GetCancellationToken(cancellationToken));
}
public async virtual Task<List<Edition>> GetListAsync(
string sorting = null,
int maxResultCount = 10,
int skipCount = 0,
string filter = null,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.WhereIf(!filter.IsNullOrWhiteSpace(), x => x.DisplayName.Contains(filter))
.OrderBy(sorting.IsNullOrEmpty() ? nameof(Edition.DisplayName) : sorting)
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
}

226
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreTenantRepository.cs

@ -0,0 +1,226 @@
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.Tenants;
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.Abp.Saas.EntityFrameworkCore;
public class EfCoreTenantRepository : EfCoreRepository<ISaasDbContext, Tenant, Guid>, ITenantRepository
{
public EfCoreTenantRepository(IDbContextProvider<ISaasDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async override Task<Tenant> FindAsync(Guid id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
var tenantDbSet = dbContext.Set<Tenant>()
.IncludeDetails(includeDetails);
if (includeDetails)
{
var editionDbSet = dbContext.Set<Edition>();
var queryable = from tenant in tenantDbSet
join edition in editionDbSet on tenant.EditionId equals edition.Id into eg
from e in eg.DefaultIfEmpty()
where tenant.Id.Equals(id)
orderby tenant.Id
select new
{
Tenant = tenant,
Edition = e,
};
var result = await queryable
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
if (result != null && result.Tenant != null)
{
result.Tenant.Edition = result.Edition;
}
return result?.Tenant;
}
return await tenantDbSet
.OrderBy(t => t.Id)
.FirstOrDefaultAsync(t => t.Id == id, GetCancellationToken(cancellationToken));
}
public virtual async Task<Tenant> FindByNameAsync(
string name,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
var tenantDbSet = dbContext.Set<Tenant>()
.IncludeDetails(includeDetails);
if (includeDetails)
{
var editionDbSet = dbContext.Set<Edition>();
var queryable = from tenant in tenantDbSet
join edition in editionDbSet on tenant.EditionId equals edition.Id into eg
from e in eg.DefaultIfEmpty()
where tenant.Name.Equals(name)
orderby tenant.Id
select new
{
Tenant = tenant,
Edition = e,
};
var result = await queryable
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
if (result != null && result.Tenant != null)
{
result.Tenant.Edition = result.Edition;
}
return result?.Tenant;
}
return await tenantDbSet
.OrderBy(t => t.Id)
.FirstOrDefaultAsync(t => t.Name == name, GetCancellationToken(cancellationToken));
}
[Obsolete("Use FindByNameAsync method.")]
public virtual Tenant FindByName(string name, bool includeDetails = true)
{
var tenantDbSet = DbContext.Set<Tenant>()
.IncludeDetails(includeDetails);
if (includeDetails)
{
var editionDbSet = DbContext.Set<Edition>();
var queryable = from tenant in tenantDbSet
join edition in editionDbSet on tenant.EditionId equals edition.Id into eg
from e in eg.DefaultIfEmpty()
where tenant.Name.Equals(name)
orderby tenant.Id
select new
{
Tenant = tenant,
Edition = e,
};
var result = queryable
.FirstOrDefault();
if (result != null && result.Tenant != null)
{
result.Tenant.Edition = result.Edition;
}
return result?.Tenant;
}
return tenantDbSet
.OrderBy(t => t.Id)
.FirstOrDefault(t => t.Name == name);
}
[Obsolete("Use FindAsync method.")]
public virtual Tenant FindById(Guid id, bool includeDetails = true)
{
var tenantDbSet = DbContext.Set<Tenant>()
.IncludeDetails(includeDetails);
if (includeDetails)
{
var editionDbSet = DbContext.Set<Edition>();
var queryable = from tenant in tenantDbSet
join edition in editionDbSet on tenant.EditionId equals edition.Id into eg
from e in eg.DefaultIfEmpty()
where tenant.Id.Equals(id)
orderby tenant.Id
select new
{
Tenant = tenant,
Edition = e,
};
var result = queryable
.FirstOrDefault();
if (result != null && result.Tenant != null)
{
result.Tenant.Edition = result.Edition;
}
return result?.Tenant;
}
return tenantDbSet
.OrderBy(t => t.Id)
.FirstOrDefault(t => t.Id == id);
}
public virtual async Task<List<Tenant>> GetListAsync(
string sorting = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
if (includeDetails)
{
var dbContext = await GetDbContextAsync();
var editionDbSet = dbContext.Set<Edition>();
var tenantDbSet = dbContext.Set<Tenant>()
.IncludeDetails(includeDetails);
var queryable = tenantDbSet
.WhereIf(!filter.IsNullOrWhiteSpace(), u => u.Name.Contains(filter))
.OrderBy(sorting.IsNullOrEmpty() ? nameof(Tenant.Name) : sorting);
var combinedResult = await queryable
.Join(
editionDbSet,
o => o.EditionId,
i => i.Id,
(tenant, edition) => new { tenant, edition })
.Skip(skipCount)
.Take(maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
return combinedResult.Select(s =>
{
s.tenant.Edition = s.edition;
return s.tenant;
}).ToList();
}
return await (await GetDbSetAsync())
.WhereIf(!filter.IsNullOrWhiteSpace(), u => u.Name.Contains(filter))
.OrderBy(sorting.IsNullOrEmpty() ? nameof(Tenant.Name) : sorting)
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<long> GetCountAsync(string filter = null, CancellationToken cancellationToken = default)
{
return await (await GetQueryableAsync())
.WhereIf(
!filter.IsNullOrWhiteSpace(),
u =>
u.Name.Contains(filter)
).CountAsync(cancellationToken: cancellationToken);
}
[Obsolete("Use WithDetailsAsync method.")]
public override IQueryable<Tenant> WithDetails()
{
return GetQueryable().IncludeDetails();
}
public override async Task<IQueryable<Tenant>> WithDetailsAsync()
{
return (await GetQueryableAsync()).IncludeDetails();
}
}

17
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/ISaasDbContext.cs

@ -0,0 +1,17 @@
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.Tenants;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas.EntityFrameworkCore;
[IgnoreMultiTenancy]
[ConnectionStringName(AbpSaasDbProperties.ConnectionStringName)]
public interface ISaasDbContext : IEfCoreDbContext
{
DbSet<Edition> Editions { get; }
DbSet<Tenant> Tenants { get; }
DbSet<TenantConnectionString> TenantConnectionStrings { get; }
}

31
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasDbContext.cs

@ -0,0 +1,31 @@
using LINGYUN.Abp.Saas.Editions;
using LINGYUN.Abp.Saas.Tenants;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Saas.EntityFrameworkCore;
[IgnoreMultiTenancy]
[ConnectionStringName(AbpSaasDbProperties.ConnectionStringName)]
public class SaasDbContext : AbpDbContext<SaasDbContext>, ISaasDbContext
{
public DbSet<Edition> Editions { get; set; }
public DbSet<Tenant> Tenants { get; set; }
public DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }
public SaasDbContext(DbContextOptions<SaasDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureSaas();
}
}

19
aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasEfCoreQueryableExtensions.cs

@ -0,0 +1,19 @@
using LINGYUN.Abp.Saas.Tenants;
using Microsoft.EntityFrameworkCore;
using System.Linq;
namespace LINGYUN.Abp.Saas.EntityFrameworkCore;
public static class SaasEfCoreQueryableExtensions
{
public static IQueryable<Tenant> IncludeDetails(this IQueryable<Tenant> queryable, bool include = true)
{
if (!include)
{
return queryable;
}
return queryable
.Include(x => x.ConnectionStrings);
}
}

3
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

19
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN.Abp.Saas.HttpApi.Client.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Http.Client" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Saas.Application.Contracts\LINGYUN.Abp.Saas.Application.Contracts.csproj" />
</ItemGroup>
</Project>

18
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN/Abp/Saas/AbpSaasHttpApiClientModule.cs

@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Saas;
[DependsOn(typeof(AbpSaasApplicationContractsModule))]
[DependsOn(typeof(AbpHttpClientModule))]
public class AbpSaasHttpApiClientModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(
typeof(AbpSaasApplicationContractsModule).Assembly,
AbpSaasRemoteServiceConsts.RemoteServiceName
);
}
}

3
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

19
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN.Abp.Saas.HttpApi.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Saas.Application.Contracts\LINGYUN.Abp.Saas.Application.Contracts.csproj" />
</ItemGroup>
</Project>

11
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasControllerBase.cs

@ -0,0 +1,11 @@
using LINGYUN.Abp.Saas.Localization;
using Volo.Abp.AspNetCore.Mvc;
namespace LINGYUN.Abp.Saas;
public abstract class AbpSaasControllerBase : AbpControllerBase
{
protected AbpSaasControllerBase()
{
LocalizationResource = typeof(AbpSaasResource);
}
}

39
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasHttpApiModule.cs

@ -0,0 +1,39 @@
using LINGYUN.Abp.Saas.Localization;
using Localization.Resources.AbpUi;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Saas;
[DependsOn(typeof(AbpSaasApplicationContractsModule))]
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
public class AbpSaasHttpApiModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpSaasHttpApiModule).Assembly);
});
PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
{
options.AddAssemblyResource(
typeof(AbpSaasResource),
typeof(AbpSaasApplicationContractsModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<AbpSaasResource>()
.AddBaseTypes(typeof(AbpUiResource));
});
}
}

59
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Editions/EditionController.cs

@ -0,0 +1,59 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.Saas.Editions;
[Controller]
[Authorize(AbpSaasPermissions.Editions.Default)]
[RemoteService(Name = AbpSaasRemoteServiceConsts.RemoteServiceName)]
[Area(AbpSaasRemoteServiceConsts.ModuleName)]
[Route("api/saas/editions")]
public class EditionController : AbpSaasControllerBase, IEditionAppService
{
protected IEditionAppService EditionAppService { get; }
public EditionController(IEditionAppService editionAppService)
{
EditionAppService = editionAppService;
}
[HttpPost]
[Authorize(AbpSaasPermissions.Editions.Create)]
public virtual Task<EditionDto> CreateAsync(EditionCreateDto input)
{
return EditionAppService.CreateAsync(input);
}
[HttpDelete]
[Route("{id}")]
[Authorize(AbpSaasPermissions.Editions.Delete)]
public virtual Task DeleteAsync(Guid id)
{
return EditionAppService.DeleteAsync(id);
}
[HttpGet]
[Route("{id}")]
public virtual Task<EditionDto> GetAsync(Guid id)
{
return EditionAppService.GetAsync(id);
}
[HttpGet]
public virtual Task<PagedResultDto<EditionDto>> GetListAsync(EditionGetListInput input)
{
return EditionAppService.GetListAsync(input);
}
[HttpPut]
[Route("{id}")]
[Authorize(AbpSaasPermissions.Editions.Update)]
public virtual Task<EditionDto> UpdateAsync(Guid id, EditionUpdateDto input)
{
return EditionAppService.UpdateAsync(id, input);
}
}

98
aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Tenants/TenantController.cs

@ -0,0 +1,98 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.Saas.Tenants;
[Controller]
[Authorize(AbpSaasPermissions.Tenants.Default)]
[RemoteService(Name = AbpSaasRemoteServiceConsts.RemoteServiceName)]
[Area(AbpSaasRemoteServiceConsts.ModuleName)]
[Route("api/saas/tenants")]
public class TenantController : AbpSaasControllerBase, ITenantAppService
{
protected ITenantAppService TenantAppService { get; }
public TenantController(ITenantAppService tenantAppService)
{
TenantAppService = tenantAppService;
}
[HttpGet]
[Route("{id}")]
public virtual Task<TenantDto> GetAsync(Guid id)
{
return TenantAppService.GetAsync(id);
}
[HttpGet]
[Route("by-name/{name}")]
public virtual Task<TenantDto> GetAsync(string name)
{
return TenantAppService.GetAsync(name);
}
[HttpGet]
public virtual Task<PagedResultDto<TenantDto>> GetListAsync(TenantGetListInput input)
{
return TenantAppService.GetListAsync(input);
}
[HttpPost]
[Authorize(AbpSaasPermissions.Tenants.Create)]
public virtual Task<TenantDto> CreateAsync(TenantCreateDto input)
{
return TenantAppService.CreateAsync(input);
}
[HttpPut]
[Route("{id}")]
[Authorize(AbpSaasPermissions.Tenants.Update)]
public virtual Task<TenantDto> UpdateAsync(Guid id, TenantUpdateDto input)
{
return TenantAppService.UpdateAsync(id, input);
}
[HttpDelete]
[Route("{id}")]
[Authorize(AbpSaasPermissions.Tenants.Delete)]
public virtual Task DeleteAsync(Guid id)
{
return TenantAppService.DeleteAsync(id);
}
[HttpGet]
[Route("{id}/connection-string/{name}")]
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual Task<TenantConnectionStringDto> GetConnectionStringAsync(Guid id, string name)
{
return TenantAppService.GetConnectionStringAsync(id, name);
}
[HttpGet]
[Route("{id}/connection-string")]
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual Task<ListResultDto<TenantConnectionStringDto>> GetConnectionStringAsync(Guid id)
{
return TenantAppService.GetConnectionStringAsync(id);
}
[HttpPut]
[Route("{id}/connection-string")]
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual Task<TenantConnectionStringDto> SetConnectionStringAsync(Guid id, TenantConnectionStringCreateOrUpdate input)
{
return TenantAppService.SetConnectionStringAsync(id, input);
}
[HttpDelete]
[Route("{id}/connection-string/{name}")]
[Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)]
public virtual Task DeleteConnectionStringAsync(Guid id, string name)
{
return TenantAppService.DeleteConnectionStringAsync(id, name);
}
}

3
aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/TenantConfigurationCacheItem.cs

@ -1,8 +1,11 @@
using System; using System;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.MultiTenancy.DbFinder namespace LINGYUN.Abp.MultiTenancy.DbFinder
{ {
[Serializable]
[IgnoreMultiTenancy]
public class TenantConfigurationCacheItem public class TenantConfigurationCacheItem
{ {
protected const string FormatKey = "pn:{0},k:{1}"; protected const string FormatKey = "pn:{0},k:{1}";

4
aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/README.md

@ -4,6 +4,10 @@ abp 多租户数据库查询组件,引用此模块将首先从分布式缓存查
如果缓存不存在,则调用租户仓储接口获取租户数据,并存储到分布式缓存中 如果缓存不存在,则调用租户仓储接口获取租户数据,并存储到分布式缓存中
## 注意
框架已支持缓存租户,此模块取消引用
## 配置使用 ## 配置使用
模块按需引用 模块按需引用

3
aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait />
</Weavers>

30
aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save