diff --git a/apps/vue/src/utils/cookie.ts b/apps/vue/src/utils/cookie.ts index 4dbcb1f8b..f093faa76 100644 --- a/apps/vue/src/utils/cookie.ts +++ b/apps/vue/src/utils/cookie.ts @@ -13,8 +13,5 @@ export function set(c_name, value, expiredays) { } export function del(name) { - const exp = new Date(); - exp.setTime(exp.getTime() - 1); - const cval = get(name); - if (cval != null) document.cookie = name + '=' + cval + ';expires=' + exp.toUTCString(); + set(name, 'a', -1); } diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln index a584101cc..477a62a56 100644 --- a/aspnet-core/LINGYUN.MicroService.All.sln +++ b/aspnet-core/LINGYUN.MicroService.All.sln @@ -109,8 +109,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TenantManagemen 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}" 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}" 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}" @@ -386,6 +384,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.IdentityServer. 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}" 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 GlobalSection(SolutionConfigurationPlatforms) = preSolution 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}.Release|Any CPU.ActiveCfg = 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.Build.0 = Debug|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}.Release|Any CPU.ActiveCfg = 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 GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1055,7 +1105,6 @@ Global {6259BCB9-A302-4CE7-AF48-9283A0DFD3CB} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F} {87C7DDCF-F80D-4A15-9044-71552DCB5550} = {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} {1DA91161-8757-4A68-A0A1-8C94C36C9240} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E} {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} {A34CCD3F-28A1-4BAD-A995-EB8720CE4105} = {0439B173-F41E-4CE0-A44A-CCB70328F272} {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 GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718} diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/en.json b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/en.json index 77d155b38..2f8fdd3b4 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/en.json +++ b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/en.json @@ -1,6 +1,8 @@ { "culture": "en", "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.", "InvalidGrant:GrantTypeInvalid": "The type of authorization that is not allowed!", "InvalidGrant:WeChatTokenInvalid": "WeChat authentication failed!", diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/zh-Hans.json b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/zh-Hans.json index 72a2995b4..b07fdf6f7 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/zh-Hans.json +++ b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Localization/zh-Hans.json @@ -1,6 +1,8 @@ { "culture": "zh-Hans", "texts": { + "MiniProgramAuthorizationDisabledMessage": "应用程序未开放小程序授权", + "OfficialAuthorizationDisabledMessage": "应用程序未开放公众平台授权", "SelfRegistrationDisabledMessage": "应用程序未开放注册,请联系管理员添加新用户.", "InvalidGrant:GrantTypeInvalid": "不被允许的授权类型!", "InvalidGrant:WeChatTokenInvalid": "微信认证失败!", diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs index 3a2201016..f94c6ec66 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs +++ b/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 IStringLocalizer WeChatLocalizer { get; } - protected IFeatureChecker FeatureChecker => ServiceProvider.LazyGetRequiredService(); public WeChatMiniProgramGrantValidator( @@ -42,9 +40,16 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram IStringLocalizer identityServerLocalizer, IStringLocalizer wechatLocalizer, AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory) - : base(eventService, weChatOpenIdFinder, userManager, userRepository, identitySecurityLogManager, identityLocalizer, identityServerLocalizer) + : base( + eventService, + weChatOpenIdFinder, + userManager, + userRepository, + identitySecurityLogManager, + wechatLocalizer, + identityLocalizer, + identityServerLocalizer) { - WeChatLocalizer = wechatLocalizer; MiniProgramOptionsFactory = miniProgramOptionsFactory; } @@ -54,8 +59,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram { context.Result = new GrantValidationResult( TokenRequestErrors.InvalidGrant, - IdentityServerLocalizer["AuthorizationDisabledMessage", - WeChatLocalizer["Features:WeChat.MiniProgram.EnableAuthorization"]]); + WeChatLocalizer["MiniProgramAuthorizationDisabledMessage"]); return false; } return true; diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs index 956b9432d..fb6117f24 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs +++ b/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 IStringLocalizer WeChatLocalizer { get; } - protected IFeatureChecker FeatureChecker => ServiceProvider.LazyGetRequiredService(); public WeChatOfficialGrantValidator( @@ -42,9 +40,16 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official IStringLocalizer identityServerLocalizer, IStringLocalizer wechatLocalizer, AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory) - : base(eventService, weChatOpenIdFinder, userManager, userRepository, identitySecurityLogManager, identityLocalizer, identityServerLocalizer) + : base( + eventService, + weChatOpenIdFinder, + userManager, + userRepository, + identitySecurityLogManager, + wechatLocalizer, + identityLocalizer, + identityServerLocalizer) { - WeChatLocalizer = wechatLocalizer; WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory; } @@ -54,8 +59,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official { context.Result = new GrantValidationResult( TokenRequestErrors.InvalidGrant, - IdentityServerLocalizer["AuthorizationDisabledMessage", - WeChatLocalizer["Features:WeChat.Official.EnableAuthorization"]]); + WeChatLocalizer["OfficialAuthorizationDisabledMessage"]); return false; } return true; diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs index 1215e081f..f821865de 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs +++ b/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.Validation; using LINGYUN.Abp.WeChat; +using LINGYUN.Abp.WeChat.Localization; using LINGYUN.Abp.WeChat.OpenId; using LINGYUN.Abp.WeChat.Security.Claims; using LINGYUN.Abp.WeChat.Settings; @@ -44,6 +45,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat protected IIdentityUserRepository UserRepository { get; } protected IdentitySecurityLogManager IdentitySecurityLogManager { get; } protected UserManager UserManager { get; } + protected IStringLocalizer WeChatLocalizer { get; } protected IStringLocalizer IdentityLocalizer { get; } protected IStringLocalizer IdentityServerLocalizer { get; } @@ -53,6 +55,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat UserManager userManager, IIdentityUserRepository userRepository, IdentitySecurityLogManager identitySecurityLogManager, + IStringLocalizer wechatLocalizer, IStringLocalizer identityLocalizer, IStringLocalizer identityServerLocalizer) { @@ -61,6 +64,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat UserRepository = userRepository; IdentitySecurityLogManager = identitySecurityLogManager; WeChatOpenIdFinder = weChatOpenIdFinder; + WeChatLocalizer = wechatLocalizer; IdentityLocalizer = identityLocalizer; IdentityServerLocalizer = identityServerLocalizer; @@ -94,7 +98,16 @@ namespace LINGYUN.Abp.IdentityServer.WeChat 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); if (currentUser == null) { diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xml b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xsd b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN.Abp.Saas.Application.Contracts.csproj b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN.Abp.Saas.Application.Contracts.csproj new file mode 100644 index 000000000..dfb278ff8 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN.Abp.Saas.Application.Contracts.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasApplicationContractsModule.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasApplicationContractsModule.cs new file mode 100644 index 000000000..4bafc3d1e --- /dev/null +++ b/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 +{ +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasRemoteServiceConsts.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/AbpSaasRemoteServiceConsts.cs new file mode 100644 index 000000000..8bcc1796e --- /dev/null +++ b/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"; +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateDto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateDto.cs new file mode 100644 index 000000000..fba0a0e91 --- /dev/null +++ b/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 +{ +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateOrUpdateBase.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionCreateOrUpdateBase.cs new file mode 100644 index 000000000..8dc15002c --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionDto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionDto.cs new file mode 100644 index 000000000..2b1fea3be --- /dev/null +++ b/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 +{ + public string DisplayName { get; set; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionGetListInput.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionGetListInput.cs new file mode 100644 index 000000000..d7404113b --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionUpdateDto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/Dto/EditionUpdateDto.cs new file mode 100644 index 000000000..b56243128 --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/IEditionAppService.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Editions/IEditionAppService.cs new file mode 100644 index 000000000..ecd4c3444 --- /dev/null +++ b/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> +{ +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissionDefinitionProvider.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissionDefinitionProvider.cs new file mode 100644 index 000000000..0776aab55 --- /dev/null +++ b/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(name); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissions.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Permissions/AbpSaasPermissions.cs new file mode 100644 index 000000000..729e1be49 --- /dev/null +++ b/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)); + } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionGetByNameInput.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionGetByNameInput.cs new file mode 100644 index 000000000..40f991bb3 --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringCreateOrUpdate.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringCreateOrUpdate.cs new file mode 100644 index 000000000..7158a3b97 --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringDto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantConnectionStringDto.cs new file mode 100644 index 000000000..bf7408ea4 --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateDto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateDto.cs new file mode 100644 index 000000000..afd1f57dc --- /dev/null +++ b/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; } + + /// + /// 使用共享数据库 + /// + public bool UseSharedDatabase { get; set; } = true; + + /// + /// 默认数据库连接字符串 + /// + public string DefaultConnectionString { get; set; } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateOrUpdateBase.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantCreateOrUpdateBase.cs new file mode 100644 index 000000000..f6a65c570 --- /dev/null +++ b/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; } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantDto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantDto.cs new file mode 100644 index 000000000..2e6be3ec3 --- /dev/null +++ b/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 +{ + 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; } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetByNameInput.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetByNameInput.cs new file mode 100644 index 000000000..3a7f0c9b1 --- /dev/null +++ b/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; + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetListInput.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantGetListInput.cs new file mode 100644 index 000000000..335e50ca3 --- /dev/null +++ b/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; } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantUpdateDto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/Dto/TenantUpdateDto.cs new file mode 100644 index 000000000..d753455b9 --- /dev/null +++ b/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; } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/ITenantAppService.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application.Contracts/LINGYUN/Abp/Saas/Tenants/ITenantAppService.cs new file mode 100644 index 000000000..de18fb821 --- /dev/null +++ b/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 GetAsync( + [Required] string name); + + Task GetConnectionStringAsync( + Guid id, + [Required] string connectionName); + + Task> GetConnectionStringAsync( + Guid id); + + Task SetConnectionStringAsync( + Guid id, + TenantConnectionStringCreateOrUpdate input); + + Task DeleteConnectionStringAsync( + Guid id, + [Required] string connectionName); +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xml b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xsd b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN.Abp.Saas.Application.csproj b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN.Abp.Saas.Application.csproj new file mode 100644 index 000000000..c47f6abd1 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN.Abp.Saas.Application.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasAppServiceBase.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasAppServiceBase.cs new file mode 100644 index 000000000..6036a12ec --- /dev/null +++ b/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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationAutoMapperProfile.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationAutoMapperProfile.cs new file mode 100644 index 000000000..8426fbfcd --- /dev/null +++ b/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(); + + CreateMap() + .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() + .MapExtraProperties(); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationModule.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/AbpSaasApplicationModule.cs new file mode 100644 index 000000000..bb9fc9d1f --- /dev/null +++ b/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(); + Configure(options => + { + options.AddProfile(validate: true); + }); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Editions/EditionAppService.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Editions/EditionAppService.cs new file mode 100644 index 000000000..81fcdd12e --- /dev/null +++ b/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 CreateAsync(EditionCreateDto input) + { + var edition = await EditionManager.CreateAsync(input.DisplayName); + input.MapExtraPropertiesTo(edition); + + await EditionRepository.InsertAsync(edition); + + await CurrentUnitOfWork.SaveChangesAsync(); + + return ObjectMapper.Map(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 GetAsync(Guid id) + { + var edition = await EditionRepository.GetAsync(id, false); + + return ObjectMapper.Map(edition); + } + + public async virtual Task> 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( + totalCount, + ObjectMapper.Map, List>(editions) + ); + } + + [Authorize(AbpSaasPermissions.Editions.Update)] + public async virtual Task 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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Tenants/TenantAppService.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Application/LINGYUN/Abp/Saas/Tenants/TenantAppService.cs new file mode 100644 index 000000000..452d10b86 --- /dev/null +++ b/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 GetAsync(Guid id) + { + var tenant = await TenantRepository.FindAsync(id, false); + if (tenant == null) + { + throw new UserFriendlyException(L["TenantNotFoundById", id]); + } + + return ObjectMapper.Map(tenant); + } + + public virtual async Task GetAsync(string name) + { + var tenant = await TenantRepository.FindByNameAsync(name, false); + if (tenant == null) + { + throw new UserFriendlyException(L["TenantNotFoundByName", name]); + } + return ObjectMapper.Map(tenant); + } + + public virtual async Task> 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( + count, + ObjectMapper.Map, List>(list) + ); + } + + [Authorize(AbpSaasPermissions.Tenants.Create)] + public virtual async Task 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); + } + + [Authorize(AbpSaasPermissions.Tenants.Update)] + public virtual async Task 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); + } + + [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 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> GetConnectionStringAsync(Guid id) + { + var tenant = await TenantRepository.GetAsync(id); + + return new ListResultDto( + ObjectMapper.Map, List>(tenant.ConnectionStrings.ToList())); + } + + [Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)] + public virtual async Task 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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xml b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xsd b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN.Abp.Saas.Domain.Shared.csproj b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN.Abp.Saas.Domain.Shared.csproj new file mode 100644 index 000000000..7325d0119 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN.Abp.Saas.Domain.Shared.csproj @@ -0,0 +1,23 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasDomainSharedModule.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasDomainSharedModule.cs new file mode 100644 index 000000000..08622ae4c --- /dev/null +++ b/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(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddVirtualJson("/LINGYUN/Abp/Saas/Localization/Resources"); + }); + + Configure(options => + { + options.MapCodeNamespace(AbpSaasErrorCodes.Namespace, typeof(AbpSaasResource)); + // 见租户管理模块 + options.MapCodeNamespace("Volo.AbpIo.MultiTenancy", typeof(AbpSaasResource)); + }); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasErrorCodes.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/AbpSaasErrorCodes.cs new file mode 100644 index 000000000..ec33cb29b --- /dev/null +++ b/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"; +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionConsts.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionConsts.cs new file mode 100644 index 000000000..ba5fbc7c5 --- /dev/null +++ b/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; +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionEto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Editions/EditionEto.cs new file mode 100644 index 000000000..80abf1090 --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/AbpSaasResource.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/AbpSaasResource.cs new file mode 100644 index 000000000..29005f733 --- /dev/null +++ b/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 +{ +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/en.json b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/en.json new file mode 100644 index 000000000..83abe1c41 --- /dev/null +++ b/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!" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/zh-Hans.json b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..498321c8d --- /dev/null +++ b/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} 不存在!" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfiguration.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfiguration.cs new file mode 100644 index 000000000..6746285c1 --- /dev/null +++ b/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 configureAction) + { + return this.ConfigureEntity( + SaasModuleExtensionConsts.EntityNames.Edition, + configureAction + ); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfigurationDictionaryExtensions.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConfigurationDictionaryExtensions.cs new file mode 100644 index 000000000..997c2973a --- /dev/null +++ b/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 configureAction) + { + return modules.ConfigureModule( + SaasModuleExtensionConsts.ModuleName, + configureAction + ); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConsts.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/ObjectExtending/SaasModuleExtensionConsts.cs new file mode 100644 index 000000000..8759f8de2 --- /dev/null +++ b/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"; + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConnectionStringConsts.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConnectionStringConsts.cs new file mode 100644 index 000000000..f9004d6ee --- /dev/null +++ b/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; +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConsts.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantConsts.cs new file mode 100644 index 000000000..14d31b6c9 --- /dev/null +++ b/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; +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantEto.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain.Shared/LINGYUN/Abp/Saas/Tenants/TenantEto.cs new file mode 100644 index 000000000..d7782fb1e --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xml b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xsd b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN.Abp.Saas.Domain.csproj b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN.Abp.Saas.Domain.csproj new file mode 100644 index 000000000..82a7feb85 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN.Abp.Saas.Domain.csproj @@ -0,0 +1,23 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDbProperties.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDbProperties.cs new file mode 100644 index 000000000..bfe3255c8 --- /dev/null +++ b/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"; +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainMappingProfile.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainMappingProfile.cs new file mode 100644 index 000000000..39a22d50c --- /dev/null +++ b/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(); + CreateMap(); + + CreateMap() + .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(); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainModule.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/AbpSaasDomainModule.cs new file mode 100644 index 000000000..6cf3abdf8 --- /dev/null +++ b/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(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + + Configure(options => + { + options.EtoMappings.Add(); + options.EtoMappings.Add(); + + options.AutoEventSelectors.Add(); + options.AutoEventSelectors.Add(); + }); + } + + 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) + ); + }); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/Edition.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/Edition.cs new file mode 100644 index 000000000..94a78ab50 --- /dev/null +++ b/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 +{ + 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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionCacheItem.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionCacheItem.cs new file mode 100644 index 000000000..96090eebc --- /dev/null +++ b/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()); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionDataSeeder.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionDataSeeder.cs new file mode 100644 index 000000000..c4945ad6a --- /dev/null +++ b/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 + ) + ); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionManager.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionManager.cs new file mode 100644 index 000000000..afb853111 --- /dev/null +++ b/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 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); + } + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionStore.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/EditionStore.cs new file mode 100644 index 000000000..5fffb75c3 --- /dev/null +++ b/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 ObjectMapper { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IDistributedCache Cache { get; } + + public EditionStore( + IEditionRepository editionRepository, + IObjectMapper objectMapper, + ICurrentTenant currentTenant, + IDistributedCache cache) + { + EditionRepository = editionRepository; + ObjectMapper = objectMapper; + CurrentTenant = currentTenant; + Cache = cache; + } + + public async virtual Task FindByTenantAsync(Guid tenantId) + { + return (await GetCacheItemAsync(tenantId)).Value; + } + + protected async virtual Task 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 SetCacheAsync(string cacheKey, [CanBeNull] Edition edition) + { + var editionInfo = edition != null ? ObjectMapper.Map(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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionDataSeeder.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionDataSeeder.cs new file mode 100644 index 000000000..0ae190cbe --- /dev/null +++ b/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(); +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionRepository.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Editions/IEditionRepository.cs new file mode 100644 index 000000000..1e7925d78 --- /dev/null +++ b/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 +{ + Task CheckUsedByTenantAsync( + Guid id, + CancellationToken cancellationToken = default); + + Task FindByDisplayNameAsync( + string displayName, + CancellationToken cancellationToken = default); + + Task FindByTenantIdAsync( + Guid tenantId, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + string sorting = null, + int maxResultCount = 10, + int skipCount = 0, + string filter = null, + CancellationToken cancellationToken = default); + + Task GetCountAsync( + string filter = null, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantManager.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantManager.cs new file mode 100644 index 000000000..6d0c3d814 --- /dev/null +++ b/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 CreateAsync([NotNull] string name); + + Task ChangeNameAsync([NotNull] Tenant tenant, [NotNull] string name); +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantRepository.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/ITenantRepository.cs new file mode 100644 index 000000000..ad2884a0b --- /dev/null +++ b/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 +{ + [Obsolete("Use FindByNameAsync method.")] + Tenant FindByName( + string name, + bool includeDetails = true + ); + + [Obsolete("Use FindAsync method.")] + Tenant FindById( + Guid id, + bool includeDetails = true + ); + + Task FindByNameAsync( + string name, + bool includeDetails = true, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + bool includeDetails = false, + CancellationToken cancellationToken = default); + + Task GetCountAsync( + string filter = null, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/Tenant.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/Tenant.cs new file mode 100644 index 000000000..deabce953 --- /dev/null +++ b/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 +{ + 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 ConnectionStrings { get; protected set; } + + protected Tenant() + { + ConnectionStrings = new Collection(); + } + + protected internal Tenant(Guid id, [NotNull] string name) + : base(id) + { + SetName(name); + + ConnectionStrings = new Collection(); + } + + 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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItem.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItem.cs new file mode 100644 index 000000000..9962e3b81 --- /dev/null +++ b/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)); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItemInvalidator.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantCacheItemInvalidator.cs new file mode 100644 index 000000000..c0630ddbf --- /dev/null +++ b/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>, ITransientDependency +{ + protected IDistributedCache Cache { get; } + + public TenantCacheItemInvalidator(IDistributedCache cache) + { + Cache = cache; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + await Cache.RemoveAsync(TenantCacheItem.CalculateCacheKey(eventData.Entity.Id, eventData.Entity.Name), considerUow: true); + // 同时移除租户版本缓存 + await Cache.RemoveAsync(EditionCacheItem.CalculateCacheKey(eventData.Entity.Id), considerUow: true); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantConnectionString.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantConnectionString.cs new file mode 100644 index 000000000..cbb9f7bd7 --- /dev/null +++ b/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 }; + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantManager.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantManager.cs new file mode 100644 index 000000000..194a599d4 --- /dev/null +++ b/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 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); + } + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantStore.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.Domain/LINGYUN/Abp/Saas/Tenants/TenantStore.cs new file mode 100644 index 000000000..51250befa --- /dev/null +++ b/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 ObjectMapper { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IDistributedCache Cache { get; } + + public TenantStore( + ITenantRepository tenantRepository, + IObjectMapper objectMapper, + ICurrentTenant currentTenant, + IDistributedCache cache) + { + TenantRepository = tenantRepository; + ObjectMapper = objectMapper; + CurrentTenant = currentTenant; + Cache = cache; + } + + public virtual async Task FindAsync(string name) + { + return (await GetCacheItemAsync(null, name)).Value; + } + + public virtual async Task 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 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 SetCacheAsync(string cacheKey, [CanBeNull] Tenant tenant) + { + var tenantConfiguration = tenant != null ? ObjectMapper.Map(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) : 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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xml b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xsd b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN.Abp.Saas.EntityFrameworkCore.csproj b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN.Abp.Saas.EntityFrameworkCore.csproj new file mode 100644 index 000000000..ebe702e68 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN.Abp.Saas.EntityFrameworkCore.csproj @@ -0,0 +1,19 @@ + + + + + + + net6.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasDbContextModelCreatingExtensions.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasDbContextModelCreatingExtensions.cs new file mode 100644 index 000000000..4931d6891 --- /dev/null +++ b/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(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(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(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(); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasEntityFrameworkCoreModule.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/AbpSaasEntityFrameworkCoreModule.cs new file mode 100644 index 000000000..61d5aa128 --- /dev/null +++ b/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(options => + { + options.AddDefaultRepositories(); + }); + + Configure(options => + { + // 将租户管理连接字符串聚合到 Saas 模块中 + options.Databases.Configure(AbpSaasDbProperties.ConnectionStringName, + database => + { + database.MappedConnections.Add("AbpTenantManagement"); + database.IsUsedByTenants = false; + }); + }); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreEditionRepository.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreEditionRepository.cs new file mode 100644 index 000000000..47f842214 --- /dev/null +++ b/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, IEditionRepository +{ + public EfCoreEditionRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async virtual Task CheckUsedByTenantAsync( + Guid id, + CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + var tenantDbSet = dbContext.Set(); + + return await tenantDbSet + .AnyAsync(x => x.EditionId == id, GetCancellationToken(cancellationToken)); + } + + public async virtual Task 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 FindByTenantIdAsync( + Guid tenantId, + CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + var editionDbSet = dbContext.Set(); + var tenantDbSet = dbContext.Set(); + + 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 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> 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)); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreTenantRepository.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/EfCoreTenantRepository.cs new file mode 100644 index 000000000..5313eecac --- /dev/null +++ b/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, ITenantRepository +{ + public EfCoreTenantRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public async override Task FindAsync(Guid id, bool includeDetails = true, CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + var tenantDbSet = dbContext.Set() + .IncludeDetails(includeDetails); + + if (includeDetails) + { + var editionDbSet = dbContext.Set(); + 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 FindByNameAsync( + string name, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + var tenantDbSet = dbContext.Set() + .IncludeDetails(includeDetails); + + if (includeDetails) + { + var editionDbSet = dbContext.Set(); + 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() + .IncludeDetails(includeDetails); + + if (includeDetails) + { + var editionDbSet = DbContext.Set(); + 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() + .IncludeDetails(includeDetails); + + if (includeDetails) + { + var editionDbSet = DbContext.Set(); + 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> 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(); + var tenantDbSet = dbContext.Set() + .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 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 WithDetails() + { + return GetQueryable().IncludeDetails(); + } + + public override async Task> WithDetailsAsync() + { + return (await GetQueryableAsync()).IncludeDetails(); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/ISaasDbContext.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/ISaasDbContext.cs new file mode 100644 index 000000000..00ff4c117 --- /dev/null +++ b/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 Editions { get; } + DbSet Tenants { get; } + DbSet TenantConnectionStrings { get; } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasDbContext.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasDbContext.cs new file mode 100644 index 000000000..b3e9b95f4 --- /dev/null +++ b/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, ISaasDbContext +{ + public DbSet Editions { get; set; } + + public DbSet Tenants { get; set; } + + public DbSet TenantConnectionStrings { get; set; } + + public SaasDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureSaas(); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasEfCoreQueryableExtensions.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.EntityFrameworkCore/LINGYUN/Abp/Saas/EntityFrameworkCore/SaasEfCoreQueryableExtensions.cs new file mode 100644 index 000000000..35b3bea74 --- /dev/null +++ b/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 IncludeDetails(this IQueryable queryable, bool include = true) + { + if (!include) + { + return queryable; + } + + return queryable + .Include(x => x.ConnectionStrings); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xml b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xsd b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN.Abp.Saas.HttpApi.Client.csproj b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN.Abp.Saas.HttpApi.Client.csproj new file mode 100644 index 000000000..87090af6c --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN.Abp.Saas.HttpApi.Client.csproj @@ -0,0 +1,19 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN/Abp/Saas/AbpSaasHttpApiClientModule.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi.Client/LINGYUN/Abp/Saas/AbpSaasHttpApiClientModule.cs new file mode 100644 index 000000000..9909fe9bc --- /dev/null +++ b/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 + ); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xml b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xsd b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN.Abp.Saas.HttpApi.csproj b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN.Abp.Saas.HttpApi.csproj new file mode 100644 index 000000000..3768d3c1a --- /dev/null +++ b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN.Abp.Saas.HttpApi.csproj @@ -0,0 +1,19 @@ + + + + + + + net6.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasControllerBase.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasControllerBase.cs new file mode 100644 index 000000000..f0a95ce86 --- /dev/null +++ b/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); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasHttpApiModule.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/AbpSaasHttpApiModule.cs new file mode 100644 index 000000000..647f1c298 --- /dev/null +++ b/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(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpSaasHttpApiModule).Assembly); + }); + + PreConfigure(options => + { + options.AddAssemblyResource( + typeof(AbpSaasResource), + typeof(AbpSaasApplicationContractsModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes(typeof(AbpUiResource)); + }); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Editions/EditionController.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Editions/EditionController.cs new file mode 100644 index 000000000..9810f661b --- /dev/null +++ b/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 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 GetAsync(Guid id) + { + return EditionAppService.GetAsync(id); + } + + [HttpGet] + public virtual Task> GetListAsync(EditionGetListInput input) + { + return EditionAppService.GetListAsync(input); + } + + [HttpPut] + [Route("{id}")] + [Authorize(AbpSaasPermissions.Editions.Update)] + public virtual Task UpdateAsync(Guid id, EditionUpdateDto input) + { + return EditionAppService.UpdateAsync(id, input); + } +} diff --git a/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Tenants/TenantController.cs b/aspnet-core/modules/saas/LINGYUN.Abp.Saas.HttpApi/LINGYUN/Abp/Saas/Tenants/TenantController.cs new file mode 100644 index 000000000..278e9dbd3 --- /dev/null +++ b/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 GetAsync(Guid id) + { + return TenantAppService.GetAsync(id); + } + + [HttpGet] + [Route("by-name/{name}")] + public virtual Task GetAsync(string name) + { + return TenantAppService.GetAsync(name); + } + + [HttpGet] + public virtual Task> GetListAsync(TenantGetListInput input) + { + return TenantAppService.GetListAsync(input); + } + + [HttpPost] + [Authorize(AbpSaasPermissions.Tenants.Create)] + public virtual Task CreateAsync(TenantCreateDto input) + { + return TenantAppService.CreateAsync(input); + } + + [HttpPut] + [Route("{id}")] + [Authorize(AbpSaasPermissions.Tenants.Update)] + public virtual Task 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 GetConnectionStringAsync(Guid id, string name) + { + return TenantAppService.GetConnectionStringAsync(id, name); + } + + [HttpGet] + [Route("{id}/connection-string")] + [Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)] + public virtual Task> GetConnectionStringAsync(Guid id) + { + return TenantAppService.GetConnectionStringAsync(id); + } + + [HttpPut] + [Route("{id}/connection-string")] + [Authorize(AbpSaasPermissions.Tenants.ManageConnectionStrings)] + public virtual Task 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); + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/TenantConfigurationCacheItem.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/TenantConfigurationCacheItem.cs index 88da5c93c..7043a744e 100644 --- a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/TenantConfigurationCacheItem.cs +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/TenantConfigurationCacheItem.cs @@ -1,8 +1,11 @@ using System; using Volo.Abp.Data; - +using Volo.Abp.MultiTenancy; + namespace LINGYUN.Abp.MultiTenancy.DbFinder -{ +{ + [Serializable] + [IgnoreMultiTenancy] public class TenantConfigurationCacheItem { protected const string FormatKey = "pn:{0},k:{1}"; diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/README.md b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/README.md index b18cc7c87..f54b135d0 100644 --- a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/README.md +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/README.md @@ -4,6 +4,10 @@ abp 多租户数据库查询组件,引用此模块将首先从分布式缓存查 如果缓存不存在,则调用租户仓储接口获取租户数据,并存储到分布式缓存中 +## 注意 + +框架已支持缓存租户,此模块取消引用 + ## 配置使用 模块按需引用 diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xml b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xml new file mode 100644 index 000000000..ac6b5b292 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xsd b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN.Abp.MultiTenancy.Editions.csproj b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN.Abp.MultiTenancy.Editions.csproj new file mode 100644 index 000000000..bb58e51e2 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN.Abp.MultiTenancy.Editions.csproj @@ -0,0 +1,16 @@ + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/AbpMultiTenancyEditionsModule.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/AbpMultiTenancyEditionsModule.cs new file mode 100644 index 000000000..18a93e6e7 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/AbpMultiTenancyEditionsModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.MultiTenancy.Editions; + +[DependsOn(typeof(AbpMultiTenancyModule))] +public class AbpMultiTenancyEditionsModule : AbpModule +{ +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionClaimsPrincipalContributor.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionClaimsPrincipalContributor.cs new file mode 100644 index 000000000..46d8c60e6 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionClaimsPrincipalContributor.cs @@ -0,0 +1,53 @@ +using LINGYUN.Abp.MultiTenancy.Editions.GlobalFeatures; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Security.Claims; + +namespace LINGYUN.Abp.MultiTenancy.Editions; + +public class EditionClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency +{ + // https://github.com/dotnet/aspnetcore/blob/v5.0.0/src/Identity/Extensions.Core/src/UserClaimsPrincipalFactory.cs#L79 + private static string IdentityAuthenticationType => "Identity.Application"; + + protected ICurrentTenant CurrentTenant { get; } + protected IEditionConfigurationProvider EditionConfigurationProvider { get; } + + public EditionClaimsPrincipalContributor( + ICurrentTenant currentTenant, + IEditionConfigurationProvider editionConfigurationProvider) + { + CurrentTenant = currentTenant; + EditionConfigurationProvider = editionConfigurationProvider; + } + + public async virtual Task ContributeAsync(AbpClaimsPrincipalContributorContext context) + { + if (!GlobalFeatureManager.Instance.IsEnabled()) + { + return; + } + + if (!CurrentTenant.IsAvailable) + { + return; + } + + var edition = await EditionConfigurationProvider.GetAsync(CurrentTenant.Id); + if (edition == null) + { + return; + } + + var claimsIdentity = context.ClaimsPrincipal.Identities.First(x => x.AuthenticationType == IdentityAuthenticationType); + + claimsIdentity.AddOrReplace(new Claim(AbpClaimTypes.EditionId, edition.Id.ToString())); + + context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity); + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionConfiguration.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionConfiguration.cs new file mode 100644 index 000000000..423efacde --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionConfiguration.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.MultiTenancy.Editions; + +[Serializable] +public class EditionConfiguration +{ + public Guid Id { get; set; } + + public string DisplayName { get; set; } + + public EditionConfiguration() + { + } + + public EditionConfiguration( + Guid id, + [NotNull] string displayName) + { + Check.NotNull(displayName, nameof(displayName)); + + Id = id; + DisplayName = displayName; + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionConfigurationProvider.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionConfigurationProvider.cs new file mode 100644 index 000000000..5e9edee60 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionConfigurationProvider.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.MultiTenancy.Editions; + +public class EditionConfigurationProvider : IEditionConfigurationProvider, ITransientDependency +{ + protected virtual IEditionStore EditionStore { get; } + + public EditionConfigurationProvider(IEditionStore editionStore) + { + EditionStore = editionStore; + } + + public async virtual Task GetAsync(Guid? tenantId = null) + { + EditionConfiguration edition = null; + if (tenantId.HasValue) + { + var editionInfo = await EditionStore.FindByTenantAsync(tenantId.Value); + if (editionInfo != null) + { + edition = new EditionConfiguration( + editionInfo.Id, + editionInfo.DisplayName); + } + } + + return edition; + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionInfo.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionInfo.cs new file mode 100644 index 000000000..7178b4e46 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/EditionInfo.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.MultiTenancy.Editions; + +[Serializable] +public class EditionInfo +{ + public Guid Id { get; set; } + + public string DisplayName { get; set; } + + public EditionInfo() + { + } + + public EditionInfo( + Guid id, + [NotNull] string displayName) + { + Check.NotNull(displayName, nameof(displayName)); + + Id = id; + DisplayName = displayName; + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/GlobalFeatures/EditionsFeature.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/GlobalFeatures/EditionsFeature.cs new file mode 100644 index 000000000..63748c21e --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/GlobalFeatures/EditionsFeature.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using Volo.Abp.GlobalFeatures; + +namespace LINGYUN.Abp.MultiTenancy.Editions.GlobalFeatures; + +[GlobalFeatureName(Name)] +public class EditionsFeature : GlobalFeature +{ + public const string Name = "Abp.Editions"; + + internal EditionsFeature([NotNull] GlobalEditionsFeatures module) + : base(module) + { + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/GlobalFeatures/GlobalEditionsFeatures.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/GlobalFeatures/GlobalEditionsFeatures.cs new file mode 100644 index 000000000..58ff42b91 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/GlobalFeatures/GlobalEditionsFeatures.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using Volo.Abp.GlobalFeatures; + +namespace LINGYUN.Abp.MultiTenancy.Editions.GlobalFeatures; + +public class GlobalEditionsFeatures : GlobalModuleFeatures +{ + public const string ModuleName = "Abp.Editions"; + + public EditionsFeature Editions => GetFeature(); + + public GlobalEditionsFeatures([NotNull] GlobalFeatureManager featureManager) + : base(featureManager) + { + AddFeature(new EditionsFeature(this)); + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/IEditionConfigurationProvider.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/IEditionConfigurationProvider.cs new file mode 100644 index 000000000..bab320cd8 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/IEditionConfigurationProvider.cs @@ -0,0 +1,9 @@ +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.MultiTenancy.Editions; + +public interface IEditionConfigurationProvider +{ + Task GetAsync(Guid? tenantId = null); +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/IEditionStore.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/IEditionStore.cs new file mode 100644 index 000000000..618bbb9c8 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/LINGYUN/Abp/MultiTenancy/Editions/IEditionStore.cs @@ -0,0 +1,9 @@ +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.MultiTenancy.Editions; + +public interface IEditionStore +{ + Task FindByTenantAsync(Guid tenantId); +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionaryEditionsExtensions.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionaryEditionsExtensions.cs new file mode 100644 index 000000000..b98b5acc8 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Editions/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionaryEditionsExtensions.cs @@ -0,0 +1,33 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.MultiTenancy.Editions.GlobalFeatures; +using System; +using System.Collections.Generic; + +namespace Volo.Abp.GlobalFeatures; + +public static class GlobalModuleFeaturesDictionaryEditionsExtensions +{ + public static GlobalEditionsFeatures Editions( + [NotNull] this GlobalModuleFeaturesDictionary modules) + { + Check.NotNull(modules, nameof(modules)); + + return modules + .GetOrAdd( + GlobalEditionsFeatures.ModuleName, + _ => new GlobalEditionsFeatures(modules.FeatureManager) + ) + as GlobalEditionsFeatures; + } + + public static GlobalModuleFeaturesDictionary Editions( + [NotNull] this GlobalModuleFeaturesDictionary modules, + [NotNull] Action configureAction) + { + Check.NotNull(configureAction, nameof(configureAction)); + + configureAction(modules.Editions()); + + return modules; + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.RemoteService/LINGYUN/Abp/MultiTenancy/RemoteService/TenantConfigurationCacheItem.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.RemoteService/LINGYUN/Abp/MultiTenancy/RemoteService/TenantConfigurationCacheItem.cs index 8ec83580b..eb6e81914 100644 --- a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.RemoteService/LINGYUN/Abp/MultiTenancy/RemoteService/TenantConfigurationCacheItem.cs +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.RemoteService/LINGYUN/Abp/MultiTenancy/RemoteService/TenantConfigurationCacheItem.cs @@ -1,8 +1,11 @@ using System; using Volo.Abp.Data; - +using Volo.Abp.MultiTenancy; + namespace LINGYUN.Abp.MultiTenancy.RemoteService { + [Serializable] + [IgnoreMultiTenancy] public class TenantConfigurationCacheItem { protected const string FormatKey = "pn:{0},k:{1}"; diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/FodyWeavers.xml b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/FodyWeavers.xsd b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN.Abp.MultiTenancy.Saas.csproj b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN.Abp.MultiTenancy.Saas.csproj new file mode 100644 index 000000000..ff3d84fc8 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN.Abp.MultiTenancy.Saas.csproj @@ -0,0 +1,23 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/AbpMultiTenancySaasModule.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/AbpMultiTenancySaasModule.cs new file mode 100644 index 000000000..25e2cb91d --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/AbpMultiTenancySaasModule.cs @@ -0,0 +1,13 @@ +using LINGYUN.Abp.Saas; +using Volo.Abp.Caching; +using Volo.Abp.EventBus; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.MultiTenancy.Saas; + +[DependsOn(typeof(AbpCachingModule))] +[DependsOn(typeof(AbpEventBusModule))] +[DependsOn(typeof(AbpSaasHttpApiClientModule))] +public class AbpMultiTenancySaasModule : AbpModule +{ +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/EditionCacheItem.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/EditionCacheItem.cs new file mode 100644 index 000000000..5189a2065 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/EditionCacheItem.cs @@ -0,0 +1,29 @@ +using LINGYUN.Abp.MultiTenancy.Editions; +using System; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.MultiTenancy.Saas; + +[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()); + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/EditionStore.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/EditionStore.cs new file mode 100644 index 000000000..698824070 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/EditionStore.cs @@ -0,0 +1,66 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.MultiTenancy.Editions; +using LINGYUN.Abp.Saas.Tenants; +using System; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.MultiTenancy.Saas; + +public class EditionStore : IEditionStore, ITransientDependency +{ + protected ITenantAppService TenantAppService { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IDistributedCache Cache { get; } + + public EditionStore( + ITenantAppService tenantAppService, + ICurrentTenant currentTenant, + IDistributedCache cache) + { + TenantAppService = tenantAppService; + CurrentTenant = currentTenant; + Cache = cache; + } + + public async virtual Task FindByTenantAsync(Guid tenantId) + { + return (await GetCacheItemAsync(tenantId)).Value; + } + + protected async virtual Task 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 tenant = await TenantAppService.GetAsync(tenantId); + return await SetCacheAsync(cacheKey, tenant); + } + } + + protected async virtual Task SetCacheAsync(string cacheKey, [CanBeNull] TenantDto tenant) + { + EditionInfo editionInfo = null; + if (tenant != null && tenant.EditionId.HasValue && !tenant.EditionName.IsNullOrWhiteSpace()) + { + editionInfo = new EditionInfo(tenant.EditionId.Value, tenant.EditionName); + } + var cacheItem = new EditionCacheItem(editionInfo); + await Cache.SetAsync(cacheKey, cacheItem, considerUow: true); + return cacheItem; + } + + protected virtual string CalculateCacheKey(Guid tenantId) + { + return EditionCacheItem.CalculateCacheKey(tenantId); + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantCacheItem.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantCacheItem.cs new file mode 100644 index 000000000..7a4d1ade4 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantCacheItem.cs @@ -0,0 +1,35 @@ +using System; +using Volo.Abp; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.MultiTenancy.Saas; + +[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)); + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantCacheItemInvalidator.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantCacheItemInvalidator.cs new file mode 100644 index 000000000..01efd49ae --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantCacheItemInvalidator.cs @@ -0,0 +1,69 @@ +using LINGYUN.Abp.Saas.Tenants; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus.Distributed; + +namespace LINGYUN.Abp.MultiTenancy.Saas; + +public class TenantCacheItemInvalidator : + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler, + IDistributedEventHandler, + ITransientDependency +{ + protected IDistributedCache Cache { get; } + + public TenantCacheItemInvalidator(IDistributedCache cache) + { + Cache = cache; + } + + public virtual async Task HandleEventAsync(EntityUpdatedEto eventData) + { + await Cache.RemoveAsync( + TenantCacheItem.CalculateCacheKey( + eventData.Entity.Id, + eventData.Entity.Name), + considerUow: true); + + await Cache.RemoveAsync( + EditionCacheItem.CalculateCacheKey( + eventData.Entity.Id), + considerUow: true); + } + + public virtual async Task HandleEventAsync(EntityDeletedEto eventData) + { + await Cache.RemoveAsync( + TenantCacheItem.CalculateCacheKey( + eventData.Entity.Id, + eventData.Entity.Name), + considerUow: true); + + await Cache.RemoveAsync( + EditionCacheItem.CalculateCacheKey( + eventData.Entity.Id), + considerUow: true); + } + + public virtual async Task HandleEventAsync(ConnectionStringCreatedEventData eventData) + { + await Cache.RemoveAsync( + TenantCacheItem.CalculateCacheKey( + eventData.TenantId, + eventData.TenantName), + considerUow: true); + } + + public virtual async Task HandleEventAsync(ConnectionStringDeletedEventData eventData) + { + await Cache.RemoveAsync( + TenantCacheItem.CalculateCacheKey( + eventData.TenantId, + eventData.TenantName), + considerUow: true); + } +} diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantStore.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantStore.cs new file mode 100644 index 000000000..4459d3d89 --- /dev/null +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.Saas/LINGYUN/Abp/MultiTenancy/Saas/TenantStore.cs @@ -0,0 +1,120 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.Saas.Tenants; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; + +namespace LINGYUN.Abp.MultiTenancy.Saas; + +public class TenantStore : ITenantStore, ITransientDependency +{ + protected ICurrentTenant CurrentTenant { get; } + protected ITenantAppService TenantAppService { get; } + protected IDistributedCache Cache { get; } + + public TenantStore( + ICurrentTenant currentTenant, + ITenantAppService tenantAppService, + IDistributedCache cache) + { + Cache = cache; + CurrentTenant = currentTenant; + TenantAppService = tenantAppService; + } + + public virtual async Task FindAsync(string name) + { + return (await GetCacheItemAsync(null, name)).Value; + } + + public virtual async Task FindAsync(Guid id) + { + return (await GetCacheItemAsync(id, null)).Value; + } + + [Obsolete("Use FindAsync method.")] + public virtual TenantConfiguration Find(string name) + { + return AsyncHelper.RunSync(async () => await FindAsync(name)); + } + + [Obsolete("Use FindAsync method.")] + public virtual TenantConfiguration Find(Guid id) + { + return AsyncHelper.RunSync(async () => await FindAsync(id)); + } + + protected virtual async Task 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 TenantAppService.GetAsync(id.Value); + var connectionStrings = await TenantAppService.GetConnectionStringAsync(id.Value); + return await SetCacheAsync(cacheKey, tenant, connectionStrings.Items); + } + } + + 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 TenantAppService.GetAsync(name); + IReadOnlyList connectionStrings = new List(); + if (tenant != null) + { + var connectionStringsResult = await TenantAppService.GetConnectionStringAsync(tenant.Id); + connectionStrings = connectionStringsResult.Items; + } + return await SetCacheAsync(cacheKey, tenant, connectionStrings); + } + } + + throw new AbpException("Both id and name can't be invalid."); + } + + protected virtual async Task SetCacheAsync( + string cacheKey, + [CanBeNull] TenantDto tenant, + [CanBeNull] IReadOnlyList connectionStrings) + { + var tenantConfiguration = tenant != null + ? new TenantConfiguration(tenant.Id, tenant.Name) + { + IsActive = tenant.IsActive, + } + : null; + if (tenantConfiguration != null && connectionStrings?.Any() == true) + { + foreach (var connectionString in connectionStrings) + { + tenantConfiguration.ConnectionStrings.Add( + connectionString.Name, + connectionString.Value); + } + } + var cacheItem = new TenantCacheItem(tenantConfiguration); + await Cache.SetAsync(cacheKey, cacheItem, considerUow: true); + return cacheItem; + } + + protected virtual string CalculateCacheKey(Guid? id, string name) + { + return TenantCacheItem.CalculateCacheKey(id, name); + } +} diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatException.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatException.cs new file mode 100644 index 000000000..b26de204f --- /dev/null +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatException.cs @@ -0,0 +1,26 @@ +using System; +using System.Runtime.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat; + +public class AbpWeChatException : BusinessException +{ + public AbpWeChatException() + { + } + + public AbpWeChatException( + string code = null, + string message = null, + string details = null, + Exception innerException = null) + : base(code, message, details, innerException) + { + } + + public AbpWeChatException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + } +} diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs index 4556795a4..46a21f8d3 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; using System; -using Volo.Abp; namespace LINGYUN.Abp.WeChat.OpenId { @@ -36,14 +35,15 @@ namespace LINGYUN.Abp.WeChat.OpenId { get { - switch (ErrorCode) + return ErrorCode switch { - case "-1": return "系统繁忙,此时请开发者稍候再试"; - case "0": return string.Empty; - case "40029": return "code 无效"; - case "45011": return "频率限制,每个用户每分钟100次"; - default: return $"未定义的异常代码:{ErrorCode},请重试"; + "-1" => "系统繁忙,此时请开发者稍候再试", + "0" => string.Empty, + "40029" => "code 无效", + "45011" => "频率限制,每个用户每分钟100次", + _ => $"未定义的异常代码:{ErrorCode},请重试", }; + ; } } /// @@ -55,7 +55,7 @@ namespace LINGYUN.Abp.WeChat.OpenId { if(IsError) { - throw new AbpException(ErrorMessage); + throw new AbpWeChatException(ErrorCode, ErrorMessage); } return new WeChatOpenId(OpenId, SessionKey, UnionId); } diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs index 49ecac0b2..37328b6aa 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs @@ -33,7 +33,7 @@ namespace LINGYUN.Abp.WeChat.Token { if(ErrorCode != 0) { - throw new AbpException(ErrorMessage); + throw new AbpWeChatException(ErrorCode.ToString(), ErrorMessage); } return new WeChatToken(AccessToken, ExpiresIn); } diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.Configure.cs index e64af5e9c..7c318bd03 100644 --- a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.Configure.cs @@ -22,12 +22,14 @@ using Volo.Abp.Authorization.Permissions; using Volo.Abp.Caching; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Identity.Localization; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.PermissionManagement; +using Volo.Abp.Threading; using Volo.Abp.VirtualFileSystem; namespace LY.MicroService.BackendAdmin; @@ -35,6 +37,17 @@ namespace LY.MicroService.BackendAdmin; public partial class BackendAdminHttpApiHostModule { protected const string DefaultCorsPolicyName = "Default"; + + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "Backend-Admin"; diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.cs index b8ef7a792..8d576b3a3 100644 --- a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.cs @@ -8,13 +8,12 @@ using LINGYUN.Abp.FeatureManagement; using LINGYUN.Abp.Localization.CultureMap; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; using LINGYUN.Abp.Logging.Serilog.Elasticsearch; -using LINGYUN.Abp.MultiTenancy.DbFinder; using LINGYUN.Abp.PermissionManagement.Identity; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.SettingManagement; using LINGYUN.Abp.Sms.Aliyun; -using LINGYUN.Abp.TenantManagement; +using LINGYUN.Abp.Saas; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -36,7 +35,7 @@ using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.PermissionManagement.HttpApi; using Volo.Abp.PermissionManagement.IdentityServer; using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.TenantManagement.EntityFrameworkCore; +using LINGYUN.Abp.Saas.EntityFrameworkCore; namespace LY.MicroService.BackendAdmin; @@ -56,12 +55,12 @@ namespace LY.MicroService.BackendAdmin; typeof(AbpFeatureManagementClientModule), typeof(AbpAuditingApplicationModule), typeof(AbpAuditingHttpApiModule), - typeof(AbpTenantManagementApplicationModule), - typeof(AbpTenantManagementHttpApiModule), + typeof(AbpSaasApplicationModule), + typeof(AbpSaasHttpApiModule), typeof(AbpEntityFrameworkCoreMySQLModule), typeof(AbpIdentityEntityFrameworkCoreModule),// 用户角色权限需要引用包 typeof(AbpIdentityServerEntityFrameworkCoreModule), // 客户端权限需要引用包 - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementDomainIdentityModule), typeof(AbpPermissionManagementDomainIdentityServerModule), @@ -73,7 +72,7 @@ namespace LY.MicroService.BackendAdmin; typeof(AbpEmailingExceptionHandlingModule), typeof(AbpCAPEventBusModule), typeof(AbpAliyunSmsModule), - typeof(AbpDbFinderMultiTenancyModule), + //typeof(AbpDbFinderMultiTenancyModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreHttpOverridesModule), typeof(AbpLocalizationCultureMapModule), @@ -86,6 +85,7 @@ public partial class BackendAdminHttpApiHostModule : AbpModule var configuration = context.Services.GetConfiguration(); PreConfigureApp(); + PreConfigureFeature(); PreConfigureCAP(configuration); } diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/EntityFrameworkCore/BackendAdminMigrationsDbContext.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/EntityFrameworkCore/BackendAdminMigrationsDbContext.cs index a2dca4430..04647134f 100644 --- a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/EntityFrameworkCore/BackendAdminMigrationsDbContext.cs +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/EntityFrameworkCore/BackendAdminMigrationsDbContext.cs @@ -3,7 +3,7 @@ using Volo.Abp.EntityFrameworkCore; using Volo.Abp.FeatureManagement.EntityFrameworkCore; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.TenantManagement.EntityFrameworkCore; +using LINGYUN.Abp.Saas.EntityFrameworkCore; namespace LY.MicroService.BackendAdmin.EntityFrameworkCore; @@ -20,7 +20,7 @@ public class BackendAdminMigrationsDbContext : AbpDbContext - @@ -68,8 +67,8 @@ - - - + + + diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220308091230_Add-Entity-Edtions-With-TenantManagement.Designer.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220308091230_Add-Entity-Edtions-With-TenantManagement.Designer.cs new file mode 100644 index 000000000..48e52ae96 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220308091230_Add-Entity-Edtions-With-TenantManagement.Designer.cs @@ -0,0 +1,274 @@ +// +using System; +using LY.MicroService.BackendAdmin.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.BackendAdmin.Migrations +{ + [DbContext(typeof(BackendAdminMigrationsDbContext))] + [Migration("20220308091230_Add-Entity-Edtions-With-TenantManagement")] + partial class AddEntityEdtionsWithTenantManagement + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "6.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.TenantManagement.Editions.Edition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("char(36)") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime(6)") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.ToTable("AbpEditions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("char(36)") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime(6)") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("char(36)"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220308091230_Add-Entity-Edtions-With-TenantManagement.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220308091230_Add-Entity-Edtions-With-TenantManagement.cs new file mode 100644 index 000000000..21b13621e --- /dev/null +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220308091230_Add-Entity-Edtions-With-TenantManagement.cs @@ -0,0 +1,106 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.BackendAdmin.Migrations +{ + public partial class AddEntityEdtionsWithTenantManagement : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings"); + + migrationBuilder.DropIndex( + name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants"); + + migrationBuilder.DropIndex( + name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey", + table: "AbpFeatureValues"); + + migrationBuilder.CreateTable( + name: "AbpEditions", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + DisplayName = table.Column(type: "varchar(64)", maxLength: 64, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExtraProperties = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "varchar(40)", maxLength: 40, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationTime = table.Column(type: "datetime(6)", nullable: false), + CreatorId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + LastModificationTime = table.Column(type: "datetime(6)", nullable: true), + LastModifierId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + DeletionTime = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEditions", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings", + columns: new[] { "Name", "ProviderName", "ProviderKey" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGrants_TenantId_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants", + columns: new[] { "TenantId", "Name", "ProviderName", "ProviderKey" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey", + table: "AbpFeatureValues", + columns: new[] { "Name", "ProviderName", "ProviderKey" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpEditions_DisplayName", + table: "AbpEditions", + column: "DisplayName"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpEditions"); + + migrationBuilder.DropIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings"); + + migrationBuilder.DropIndex( + name: "IX_AbpPermissionGrants_TenantId_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants"); + + migrationBuilder.DropIndex( + name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey", + table: "AbpFeatureValues"); + + migrationBuilder.CreateIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey", + table: "AbpFeatureValues", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220310034917_Reset-Saas-Tenant-With-Tenant-Management.Designer.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220310034917_Reset-Saas-Tenant-With-Tenant-Management.Designer.cs new file mode 100644 index 000000000..ba7911097 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220310034917_Reset-Saas-Tenant-With-Tenant-Management.Designer.cs @@ -0,0 +1,297 @@ +// +using System; +using LY.MicroService.BackendAdmin.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.BackendAdmin.Migrations +{ + [DbContext(typeof(BackendAdminMigrationsDbContext))] + [Migration("20220310034917_Reset-Saas-Tenant-With-Tenant-Management")] + partial class ResetSaasTenantWithTenantManagement + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Editions.Edition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("char(36)") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime(6)") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.ToTable("AbpEditions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("char(36)") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime(6)") + .HasColumnName("DeletionTime"); + + b.Property("DisableTime") + .HasColumnType("datetime(6)"); + + b.Property("EditionId") + .HasColumnType("char(36)"); + + b.Property("EnableTime") + .HasColumnType("datetime(6)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("EditionId"); + + b.HasIndex("Name"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("char(36)"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.HasOne("LINGYUN.Abp.Saas.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId"); + + b.Navigation("Edition"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.HasOne("LINGYUN.Abp.Saas.Tenants.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220310034917_Reset-Saas-Tenant-With-Tenant-Management.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220310034917_Reset-Saas-Tenant-With-Tenant-Management.cs new file mode 100644 index 000000000..659c1eec1 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/20220310034917_Reset-Saas-Tenant-With-Tenant-Management.cs @@ -0,0 +1,78 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.BackendAdmin.Migrations +{ + public partial class ResetSaasTenantWithTenantManagement : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DisableTime", + table: "AbpTenants", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "EditionId", + table: "AbpTenants", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "EnableTime", + table: "AbpTenants", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsActive", + table: "AbpTenants", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateIndex( + name: "IX_AbpTenants_EditionId", + table: "AbpTenants", + column: "EditionId"); + + migrationBuilder.AddForeignKey( + name: "FK_AbpTenants_AbpEditions_EditionId", + table: "AbpTenants", + column: "EditionId", + principalTable: "AbpEditions", + principalColumn: "Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AbpTenants_AbpEditions_EditionId", + table: "AbpTenants"); + + migrationBuilder.DropIndex( + name: "IX_AbpTenants_EditionId", + table: "AbpTenants"); + + migrationBuilder.DropColumn( + name: "DisableTime", + table: "AbpTenants"); + + migrationBuilder.DropColumn( + name: "EditionId", + table: "AbpTenants"); + + migrationBuilder.DropColumn( + name: "EnableTime", + table: "AbpTenants"); + + migrationBuilder.DropColumn( + name: "IsActive", + table: "AbpTenants"); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/BackendAdminHostMigrationsDbContextModelSnapshot.cs b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/BackendAdminHostMigrationsDbContextModelSnapshot.cs index d8822b668..86146a8dc 100644 --- a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/BackendAdminHostMigrationsDbContextModelSnapshot.cs +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Migrations/BackendAdminHostMigrationsDbContextModelSnapshot.cs @@ -6,6 +6,8 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Volo.Abp.EntityFrameworkCore; +#nullable disable + namespace LY.MicroService.BackendAdmin.Migrations { [DbContext(typeof(BackendAdminMigrationsDbContext))] @@ -16,104 +18,68 @@ namespace LY.MicroService.BackendAdmin.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) - .HasAnnotation("Relational:MaxIdentifierLength", 64) - .HasAnnotation("ProductVersion", "5.0.11"); + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); - modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + modelBuilder.Entity("LINGYUN.Abp.Saas.Editions.Edition", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("char(36)"); - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("ProviderKey") - .HasMaxLength(64) - .HasColumnType("varchar(64)"); - - b.Property("ProviderName") - .HasMaxLength(64) - .HasColumnType("varchar(64)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); - b.ToTable("AbpFeatureValues"); - }); + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); - modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); + b.Property("DeleterId") + .HasColumnType("char(36)") + .HasColumnName("DeleterId"); - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("varchar(64)"); + b.Property("DeletionTime") + .HasColumnType("datetime(6)") + .HasColumnName("DeletionTime"); - b.Property("ProviderName") + b.Property("DisplayName") .IsRequired() .HasMaxLength(64) .HasColumnType("varchar(64)"); - b.Property("TenantId") - .HasColumnType("char(36)") - .HasColumnName("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpPermissionGrants"); - }); + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); - modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => - { - b.Property("Id") + b.Property("IsDeleted") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("ProviderKey") - .HasMaxLength(64) - .HasColumnType("varchar(64)"); + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); - b.Property("ProviderName") - .HasMaxLength(64) - .HasColumnType("varchar(64)"); + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); - b.Property("Value") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("varchar(2048)"); + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); b.HasKey("Id"); - b.HasIndex("Name", "ProviderName", "ProviderKey"); + b.HasIndex("DisplayName"); - b.ToTable("AbpSettings"); + b.ToTable("AbpEditions", (string)null); }); - modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -141,10 +107,22 @@ namespace LY.MicroService.BackendAdmin.Migrations .HasColumnType("datetime(6)") .HasColumnName("DeletionTime"); + b.Property("DisableTime") + .HasColumnType("datetime(6)"); + + b.Property("EditionId") + .HasColumnType("char(36)"); + + b.Property("EnableTime") + .HasColumnType("datetime(6)"); + b.Property("ExtraProperties") .HasColumnType("longtext") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("tinyint(1)") @@ -166,12 +144,14 @@ namespace LY.MicroService.BackendAdmin.Migrations b.HasKey("Id"); + b.HasIndex("EditionId"); + b.HasIndex("Name"); - b.ToTable("AbpTenants"); + b.ToTable("AbpTenants", (string)null); }); - modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => { b.Property("TenantId") .HasColumnType("char(36)"); @@ -187,19 +167,125 @@ namespace LY.MicroService.BackendAdmin.Migrations b.HasKey("TenantId", "Name"); - b.ToTable("AbpTenantConnectionStrings"); + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.HasOne("LINGYUN.Abp.Saas.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId"); + + b.Navigation("Edition"); }); - modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => { - b.HasOne("Volo.Abp.TenantManagement.Tenant", null) + b.HasOne("LINGYUN.Abp.Saas.Tenants.Tenant", null) .WithMany("ConnectionStrings") .HasForeignKey("TenantId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => { b.Navigation("ConnectionStrings"); }); diff --git a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.Development.json index a5e788276..6d00e60b4 100644 --- a/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.Development.json @@ -21,7 +21,7 @@ "Default": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpIdentity": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", "AbpIdentityServer": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", - "AbpTenantManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", + "AbpSaas": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpFeatureManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpPermissionManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", diff --git a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LY.MicroService.LocalizationManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LY.MicroService.LocalizationManagement.HttpApi.Host.csproj index 8c511656f..60cfd030f 100644 --- a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LY.MicroService.LocalizationManagement.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LY.MicroService.LocalizationManagement.HttpApi.Host.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -30,7 +30,6 @@ - @@ -47,7 +46,8 @@ - + + diff --git a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs index bf46fa625..869e2ea22 100644 --- a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs @@ -20,10 +20,12 @@ using Volo.Abp; using Volo.Abp.Auditing; using Volo.Abp.Caching; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; using Volo.Abp.VirtualFileSystem; @@ -33,6 +35,16 @@ public partial class LocalizationManagementHttpApiHostModule { protected const string DefaultCorsPolicyName = "Default"; + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "Localization"; diff --git a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.cs index 7a0896862..f77788036 100644 --- a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.cs @@ -6,7 +6,7 @@ using LINGYUN.Abp.ExceptionHandling.Emailing; using LINGYUN.Abp.Localization.CultureMap; using LINGYUN.Abp.LocalizationManagement; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -using LINGYUN.Abp.MultiTenancy.DbFinder; +using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using Microsoft.AspNetCore.Builder; @@ -22,7 +22,6 @@ using Volo.Abp.EntityFrameworkCore.MySQL; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace LY.MicroService.LocalizationManagement { @@ -36,14 +35,13 @@ namespace LY.MicroService.LocalizationManagement typeof(AbpLocalizationManagementHttpApiModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule), typeof(AbpEntityFrameworkCoreMySQLModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpDataDbMigratorModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule), typeof(AbpEmailingExceptionHandlingModule), typeof(AbpCAPEventBusModule), - typeof(AbpDbFinderMultiTenancyModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreHttpOverridesModule), typeof(AbpLocalizationCultureMapModule), @@ -56,6 +54,7 @@ namespace LY.MicroService.LocalizationManagement var configuration = context.Services.GetConfiguration(); PreConfigureApp(); + PreConfigureFeature(); PreConfigureCAP(configuration); } diff --git a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json index fbb5e23e9..36225362f 100644 --- a/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json @@ -13,7 +13,7 @@ }, "ConnectionStrings": { "Default": "Server=localhost;Database=Platform;User Id=root;Password=123456", - "AbpTenantManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", + "AbpSaas": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpPermissionManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpLocalizationManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456" diff --git a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/LY.MicroService.PlatformManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/LY.MicroService.PlatformManagement.HttpApi.Host.csproj index ecbd725a0..0c69ac63b 100644 --- a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/LY.MicroService.PlatformManagement.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/LY.MicroService.PlatformManagement.HttpApi.Host.csproj @@ -34,7 +34,6 @@ - @@ -62,7 +61,7 @@ - + diff --git a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs index 1a5636120..c1ecaef44 100644 --- a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs @@ -25,10 +25,12 @@ using Volo.Abp.BlobStoring; using Volo.Abp.BlobStoring.FileSystem; using Volo.Abp.Caching; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; using Volo.Abp.VirtualFileSystem; namespace LY.MicroService.PlatformManagement; @@ -37,6 +39,16 @@ public partial class PlatformManagementHttpApiHostModule { protected const string DefaultCorsPolicyName = "Default"; + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "Platform"; diff --git a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.cs index 0c9a3753b..f769fadd5 100644 --- a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.cs @@ -6,12 +6,12 @@ using LINGYUN.Abp.ExceptionHandling.Emailing; using LINGYUN.Abp.Features.LimitValidation.Redis; using LINGYUN.Abp.Localization.CultureMap; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -using LINGYUN.Abp.MultiTenancy.DbFinder; using LINGYUN.Abp.Notifications; using LINGYUN.Abp.OssManagement; using LINGYUN.Abp.OssManagement.FileSystem; using LINGYUN.Abp.OssManagement.FileSystem.ImageSharp; using LINGYUN.Abp.OssManagement.SettingManagement; +using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.UI.Navigation.VueVbenAdmin; @@ -35,7 +35,6 @@ using Volo.Abp.Identity; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace LY.MicroService.PlatformManagement; @@ -58,7 +57,7 @@ namespace LY.MicroService.PlatformManagement; typeof(AbpIdentityHttpApiClientModule), typeof(AbpHttpClientIdentityModelWebModule), typeof(AbpFeatureManagementEntityFrameworkCoreModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule), @@ -70,7 +69,6 @@ namespace LY.MicroService.PlatformManagement; typeof(AbpFeaturesValidationRedisModule), // typeof(AbpFeaturesClientModule),// 当需要客户端特性限制时取消注释此模块 // typeof(AbpFeaturesValidationRedisClientModule),// 当需要客户端特性限制时取消注释此模块 - typeof(AbpDbFinderMultiTenancyModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreHttpOverridesModule), typeof(AbpLocalizationCultureMapModule), @@ -83,6 +81,7 @@ public partial class PlatformManagementHttpApiHostModule : AbpModule var configuration = context.Services.GetConfiguration(); PreConfigureApp(); + PreConfigureFeature(); PreConfigureCAP(configuration); } diff --git a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json index c35347b98..1aa021de3 100644 --- a/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json @@ -36,7 +36,7 @@ "Default": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AppPlatform": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpFeatureManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", - "AbpTenantManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", + "AbpSaas": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpPermissionManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpLocalizationManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456" diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj index dbd822994..40ffdc64f 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj @@ -35,7 +35,6 @@ - @@ -60,7 +59,7 @@ - + diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.Configure.cs index d871bd169..3a6bb8c74 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.Configure.cs @@ -28,10 +28,12 @@ using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.Auditing; using Volo.Abp.Caching; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; using Volo.Abp.Timing; using Volo.Abp.VirtualFileSystem; using HangfireDashboardOptions = Hangfire.DashboardOptions; @@ -41,6 +43,17 @@ namespace LY.MicroService.RealtimeMessage; public partial class RealtimeMessageHttpApiHostModule { protected static string[] PrefixTokenQueryStrings = new [] { "/signalr-hubs", "/hangfire" }; + + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "MessageService"; diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs index c0746caeb..b9b0d17a9 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs @@ -13,10 +13,10 @@ using LINGYUN.Abp.Localization.CultureMap; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; using LINGYUN.Abp.MessageService; using LINGYUN.Abp.MessageService.EntityFrameworkCore; -using LINGYUN.Abp.MultiTenancy.DbFinder; using LINGYUN.Abp.Notifications.SignalR; using LINGYUN.Abp.Notifications.Sms; using LINGYUN.Abp.Notifications.WeChat.MiniProgram; +using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using Microsoft.AspNetCore.Builder; @@ -34,7 +34,6 @@ using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace LY.MicroService.RealtimeMessage { @@ -49,7 +48,7 @@ namespace LY.MicroService.RealtimeMessage typeof(AbpIdentityWeChatModule), typeof(AbpMessageServiceEntityFrameworkCoreModule), typeof(AbpIdentityEntityFrameworkCoreModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule), @@ -65,7 +64,6 @@ namespace LY.MicroService.RealtimeMessage typeof(AbpNotificationsWeChatMiniProgramModule), typeof(AbpNotificationsExceptionHandlingModule), typeof(AbpCAPEventBusModule), - typeof(AbpDbFinderMultiTenancyModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreHttpOverridesModule), typeof(AbpLocalizationCultureMapModule), @@ -80,6 +78,7 @@ namespace LY.MicroService.RealtimeMessage var configuration = context.Services.GetConfiguration(); PreConfigureApp(); + PreConfigureFeature(); PreCongifureHangfire(); PreConfigureCAP(configuration); } diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/appsettings.Development.json index fa561c1c8..810dc5822 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/appsettings.Development.json @@ -26,7 +26,7 @@ "Default": "Server=localhost;Database=Messages;User Id=root;Password=123456", "MessageService": "Server=localhost;Database=Messages;User Id=root;Password=123456", "AbpIdentity": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", - "AbpTenantManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", + "AbpSaas": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpPermissionManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpLocalizationManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456" diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs index 4d883dd11..b25a5987b 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs @@ -2,6 +2,7 @@ using LINGYUN.Abp.BackgroundTasks.Internal; using LINGYUN.Abp.Data.DbMigrator; using LINGYUN.Abp.MultiTenancy; +using LINGYUN.Abp.Saas.Tenants; using LY.MicroService.TaskManagement.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -13,7 +14,6 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EventBus.Distributed; using Volo.Abp.MultiTenancy; -using Volo.Abp.TenantManagement; using Volo.Abp.Uow; namespace LY.MicroService.TaskManagement.EventBus.Handlers @@ -29,18 +29,21 @@ namespace LY.MicroService.TaskManagement.EventBus.Handlers protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IDbSchemaMigrator DbSchemaMigrator { get; } protected AbpBackgroundTasksOptions Options { get; } + protected IJobStore JobStore { get; } protected IJobScheduler JobScheduler { get; } public TenantSynchronizer( ICurrentTenant currentTenant, IUnitOfWorkManager unitOfWorkManager, IDbSchemaMigrator dbSchemaMigrator, IOptions options, + IJobStore jobStore, IJobScheduler jobScheduler, ILogger logger) { CurrentTenant = currentTenant; UnitOfWorkManager = unitOfWorkManager; DbSchemaMigrator = dbSchemaMigrator; + JobStore = jobStore; JobScheduler = jobScheduler; Options = options.Value; @@ -51,9 +54,11 @@ namespace LY.MicroService.TaskManagement.EventBus.Handlers { // 租户删除时移除轮询作业 var pollingJob = BuildPollingJobInfo(eventData.Entity.Id, eventData.Entity.Name); + await JobStore.StoreAsync(pollingJob); await JobScheduler.RemoveAsync(pollingJob); var cleaningJob = BuildCleaningJobInfo(eventData.Entity.Id, eventData.Entity.Name); + await JobStore.StoreAsync(cleaningJob); await JobScheduler.RemoveAsync(cleaningJob); } diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj index 1c3a0bc9d..98bc88491 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj @@ -49,7 +49,6 @@ - @@ -62,8 +61,7 @@ - - + diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs index 30cc4edb3..f522bcc31 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs @@ -21,17 +21,29 @@ using Volo.Abp; using Volo.Abp.Auditing; using Volo.Abp.Caching; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.Quartz; +using Volo.Abp.Threading; using Volo.Abp.VirtualFileSystem; namespace LY.MicroService.TaskManagement; public partial class TaskManagementHttpApiHostModule { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "TaskManagement"; diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs index dc3a8282f..ca75692ec 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs @@ -6,7 +6,7 @@ using LINGYUN.Abp.Data.DbMigrator; using LINGYUN.Abp.EventBus.CAP; using LINGYUN.Abp.ExceptionHandling.Emailing; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -using LINGYUN.Abp.MultiTenancy.DbFinder; +using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.TaskManagement; @@ -31,7 +31,6 @@ using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; using Volo.Abp.Swashbuckle; -using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace LY.MicroService.TaskManagement; @@ -46,7 +45,6 @@ namespace LY.MicroService.TaskManagement; typeof(AbpEmailingExceptionHandlingModule), typeof(AbpHttpClientIdentityModelWebModule), typeof(AbpAspNetCoreMultiTenancyModule), - typeof(AbpDbFinderMultiTenancyModule), typeof(AbpBackgroundTasksJobsModule), typeof(AbpBackgroundTasksQuartzModule), typeof(AbpBackgroundTasksExceptionHandlingModule), @@ -56,7 +54,7 @@ namespace LY.MicroService.TaskManagement; typeof(AbpFeatureManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule), typeof(AbpCAPEventBusModule), typeof(AbpDataDbMigratorModule), @@ -72,6 +70,7 @@ public partial class TaskManagementHttpApiHostModule : AbpModule var configuration = context.Services.GetConfiguration(); PreConfigureApp(); + PreConfigureFeature(); PreConfigureCAP(configuration); PreConfigureQuartz(configuration); } diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json index 13e0b79cd..4d2c8f6a0 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json @@ -51,7 +51,7 @@ "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", - "AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" + "AbpSaas": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" }, "RemoteServices": { "AbpOssManagement": { diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/LY.MicroService.WorkflowManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/LY.MicroService.WorkflowManagement.HttpApi.Host.csproj index 9c31de879..da09fc5a5 100644 --- a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/LY.MicroService.WorkflowManagement.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/LY.MicroService.WorkflowManagement.HttpApi.Host.csproj @@ -33,14 +33,12 @@ - - - + diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.Configure.cs index b61dec366..60e5e1e40 100644 --- a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.Configure.cs @@ -1,7 +1,8 @@ -using LINGYUN.Abp.ExceptionHandling; +using LINGYUN.Abp.BlobStoring.OssManagement; +using LINGYUN.Abp.ExceptionHandling; using LINGYUN.Abp.ExceptionHandling.Emailing; using LINGYUN.Abp.Serilog.Enrichers.Application; -using LINGYUN.Abp.BlobStoring.OssManagement; +using LINGYUN.Abp.WorkflowCore.Components; using Medallion.Threading; using Medallion.Threading.Redis; using Microsoft.AspNetCore.Authentication.JwtBearer; @@ -19,18 +20,28 @@ using Volo.Abp.Auditing; using Volo.Abp.BlobStoring; using Volo.Abp.Caching; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; -using Volo.Abp.Uow; +using Volo.Abp.Threading; using Volo.Abp.VirtualFileSystem; -using LINGYUN.Abp.WorkflowCore.Components; namespace LY.MicroService.WorkflowManagement; public partial class WorkflowManagementHttpApiHostModule { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "WorkflowManagement"; diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.cs index bd6c82995..d49f6da37 100644 --- a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.cs @@ -3,7 +3,7 @@ using LINGYUN.Abp.BlobStoring.OssManagement; using LINGYUN.Abp.Data.DbMigrator; using LINGYUN.Abp.ExceptionHandling.Emailing; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -using LINGYUN.Abp.MultiTenancy.DbFinder; +using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.WorkflowCore.Components; @@ -33,7 +33,6 @@ using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; using Volo.Abp.Swashbuckle; -using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace LY.MicroService.WorkflowManagement; @@ -57,11 +56,10 @@ namespace LY.MicroService.WorkflowManagement; typeof(AbpEmailingExceptionHandlingModule), typeof(AbpHttpClientIdentityModelWebModule), typeof(AbpAspNetCoreMultiTenancyModule), - typeof(AbpDbFinderMultiTenancyModule), typeof(AbpFeatureManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule), typeof(AbpDataDbMigratorModule), typeof(AbpCachingStackExchangeRedisModule), @@ -74,6 +72,7 @@ public partial class WorkflowManagementHttpApiHostModule : AbpModule public override void PreConfigureServices(ServiceConfigurationContext context) { PreConfigureApp(); + PreConfigureFeature(); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.Development.json index 215ca2e01..2a5e3458f 100644 --- a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.Development.json @@ -18,7 +18,7 @@ "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", - "AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" + "AbpSaas": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" }, "RemoteServices": { "AbpOssManagement": { diff --git a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.Configure.cs index c62646ca1..e878d92cc 100644 --- a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.Configure.cs @@ -23,12 +23,14 @@ using Volo.Abp.Authorization.Permissions; using Volo.Abp.Caching; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Identity.Localization; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.PermissionManagement; +using Volo.Abp.Threading; using Volo.Abp.VirtualFileSystem; namespace LY.MicroService.IdentityServer; @@ -37,6 +39,16 @@ public partial class IdentityServerHttpApiHostModule { protected const string DefaultCorsPolicyName = "Default"; + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "Identity-Server-Admin"; diff --git a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.cs index 78d5812ad..0837c4287 100644 --- a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/IdentityServerHttpApiHostModule.cs @@ -4,7 +4,7 @@ using LINGYUN.Abp.EventBus.CAP; using LINGYUN.Abp.ExceptionHandling.Emailing; using LINGYUN.Abp.Localization.CultureMap; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -using LINGYUN.Abp.MultiTenancy.DbFinder; +using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.Sms.Aliyun; @@ -22,7 +22,6 @@ using Volo.Abp.EntityFrameworkCore.MySQL; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace LY.MicroService.IdentityServer; @@ -40,7 +39,7 @@ namespace LY.MicroService.IdentityServer; typeof(LINGYUN.Abp.Identity.EntityFrameworkCore.AbpIdentityEntityFrameworkCoreModule), typeof(LINGYUN.Abp.IdentityServer.EntityFrameworkCore.AbpIdentityServerEntityFrameworkCoreModule), typeof(AbpEntityFrameworkCoreMySQLModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule), @@ -49,7 +48,6 @@ namespace LY.MicroService.IdentityServer; typeof(AbpEmailingExceptionHandlingModule), typeof(AbpCAPEventBusModule), typeof(AbpAliyunSmsModule), - typeof(AbpDbFinderMultiTenancyModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreHttpOverridesModule), typeof(AbpLocalizationCultureMapModule), @@ -62,6 +60,7 @@ public partial class IdentityServerHttpApiHostModule : AbpModule var configuration = context.Services.GetConfiguration(); PreConfigureApp(); + PreConfigureFeature(); PreConfigureCAP(configuration); PreConfigureIdentity(); } diff --git a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/LY.MicroService.identityServer.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/LY.MicroService.identityServer.HttpApi.Host.csproj index fe4576d55..5f221fe47 100644 --- a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/LY.MicroService.identityServer.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/LY.MicroService.identityServer.HttpApi.Host.csproj @@ -47,7 +47,6 @@ - @@ -70,7 +69,7 @@ - + \ No newline at end of file diff --git a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/appsettings.Development.json index b5e8b15e6..bc41e97a9 100644 --- a/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.identityServer.HttpApi.Host/appsettings.Development.json @@ -16,7 +16,7 @@ "Default": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", "AbpIdentity": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", "AbpIdentityServer": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", - "AbpTenantManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", + "AbpSaas": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpFeatureManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpPermissionManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", diff --git a/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.Configure.cs b/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.Configure.cs index ad3c3aad3..218fd02ee 100644 --- a/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.Configure.cs @@ -23,11 +23,13 @@ using Volo.Abp.Account.Localization; using Volo.Abp.Auditing; using Volo.Abp.Caching; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; using Volo.Abp.IdentityServer; using Volo.Abp.Json; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.VirtualFileSystem; @@ -35,6 +37,16 @@ namespace LY.MicroService.IdentityServer; public partial class IdentityServerModule { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + private void PreConfigureApp() { AbpSerilogEnrichersConsts.ApplicationName = "Identity-Server-STS"; diff --git a/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.cs b/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.cs index ee4118123..61a0211cf 100644 --- a/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.cs +++ b/aspnet-core/services/LY.MicroService.identityServer/IdentityServerModule.cs @@ -8,8 +8,8 @@ using LINGYUN.Abp.IdentityServer.EntityFrameworkCore; using LINGYUN.Abp.IdentityServer.QQ; using LINGYUN.Abp.IdentityServer.WeChat; using LINGYUN.Abp.Localization.CultureMap; -using LINGYUN.Abp.MultiTenancy.DbFinder; using LINGYUN.Abp.PermissionManagement.Identity; +using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.Sms.Aliyun; @@ -33,7 +33,6 @@ using Volo.Abp.IdentityServer.Jwt; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace LY.MicroService.IdentityServer; @@ -58,13 +57,12 @@ namespace LY.MicroService.IdentityServer; typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpFeatureManagementEntityFrameworkCoreModule), - typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), typeof(AbpDataDbMigratorModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule), typeof(AbpAuditLoggingElasticsearchModule), // 放在 AbpIdentity 模块之后,避免被覆盖 typeof(AbpAspNetCoreHttpOverridesModule), typeof(AbpLocalizationCultureMapModule), - typeof(AbpDbFinderMultiTenancyModule), typeof(AbpCAPEventBusModule), typeof(AbpAliyunSmsModule) )] @@ -78,6 +76,7 @@ public partial class IdentityServerModule : AbpModule var hostingEnvironment = context.Services.GetHostingEnvironment(); PreConfigureApp(); + PreConfigureFeature(); PreConfigureCAP(configuration); PreConfigureCertificate(configuration, hostingEnvironment); } diff --git a/aspnet-core/services/LY.MicroService.identityServer/LY.MicroService.IdentityServer.csproj b/aspnet-core/services/LY.MicroService.identityServer/LY.MicroService.IdentityServer.csproj index aa60d636a..5c975abed 100644 --- a/aspnet-core/services/LY.MicroService.identityServer/LY.MicroService.IdentityServer.csproj +++ b/aspnet-core/services/LY.MicroService.identityServer/LY.MicroService.IdentityServer.csproj @@ -41,7 +41,6 @@ - @@ -60,7 +59,7 @@ - + diff --git a/aspnet-core/services/LY.MicroService.identityServer/appsettings.Development.json b/aspnet-core/services/LY.MicroService.identityServer/appsettings.Development.json index 18e9079d0..795854ace 100644 --- a/aspnet-core/services/LY.MicroService.identityServer/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.identityServer/appsettings.Development.json @@ -17,7 +17,7 @@ "Default": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", "AbpIdentity": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", "AbpIdentityServer": "Server=localhost;Database=IdentityServer;User Id=root;Password=123456", - "AbpTenantManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", + "AbpSaas": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpSettingManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpPermissionManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456", "AbpFeatureManagement": "Server=localhost;Database=Platform;User Id=root;Password=123456"