From 48f0af24f7966bdda1e0890699db85529101ec71 Mon Sep 17 00:00:00 2001
From: cKey <35512826+colinin@users.noreply.github.com>
Date: Fri, 31 Dec 2021 16:45:27 +0800
Subject: [PATCH] feat(cloud): add support tencent cloud
---
aspnet-core/.editorconfig | 137 ++++++
aspnet-core/Directory.Build.props | 1 +
aspnet-core/LINGYUN.MicroService.All.sln | 37 +-
.../LINGYUN.Abp.BlobStoring.Tencent.csproj | 21 +
.../AbpBlobStoringTencentCloudModule.cs | 12 +
.../BlobStoring/Tencent/CosClientFactory.cs | 51 +++
.../DefaultTencentBlobNameCalculator.cs | 24 ++
.../BlobStoring/Tencent/ICosClientFactory.cs | 11 +
.../Tencent/ITencentBlobNameCalculator.cs | 9 +
...entBlobContainerConfigurationExtensions.cs | 25 ++
.../Tencent/TencentBlobNamingNormalizer.cs | 61 +++
.../TencentBlobProviderConfiguration.cs | 63 +++
.../TencentBlobProviderConfigurationNames.cs | 26 ++
.../Tencent/TencentCloudBlobProvider.cs | 165 +++++++
.../LINGYUN.Abp.Sms.Tencent.csproj | 23 +
.../Abp/Sms/Tencent/AbpSmsTencentModule.cs | 30 ++
.../Tencent/Localization/Resources/en.json | 12 +
.../Localization/Resources/zh-Hans.json | 12 +
.../Settings/TencentCloudSmsSettingNames.cs | 23 +
.../TencentCloudSmsSettingProvider.cs | 59 +++
.../Abp/Sms/Tencent/TencentCloudSmsSender.cs | 97 +++++
.../Abp/Sms/TencentSmsSenderExtensions.cs | 58 +++
...NGYUN.Abp.Tencent.SettingManagement.csproj | 26 ++
.../AbpTencentCloudSettingManagementModule.cs | 16 +
.../ITenantCloudSettingAppService.cs | 7 +
.../TenantCloudSettingAppService.cs | 168 ++++++++
...loudSettingPermissionDefinitionProvider.cs | 23 +
.../TenantCloudSettingPermissionNames.cs | 9 +
.../LINGYUN.Abp.Tencent.csproj | 8 +-
.../Abp/Tencent/AbpTencentCloudModule.cs | 16 +-
.../Abp/Tencent/AbpTencentCloudOptions.cs | 16 +
.../AbstractTencentCloudClientFactory.cs | 124 ++++++
.../Tencent/Localization/Resources/en.json | 37 ++
.../Localization/Resources/zh-Hans.json | 37 ++
.../Localization/TencentCloudResource.cs | 9 +
.../Tencent/Localization/TencentResource.cs | 9 -
.../TencentCloudSettingDefinitionProvider.cs | 94 ++++
.../Settings/TencentCloudSettingNames.cs | 47 ++
.../Tencent/TencentCloudClientCacheItem.cs | 21 +
.../Abp/Tencent/TencentCloudClientFactory.cs | 55 +++
.../Aliyun/AbpBlobStoringAliyunModule.cs | 40 +-
.../LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs | 14 +-
.../Cors/AbpCorsPolicyBuilderExtensions.cs | 15 +
.../FodyWeavers.xml | 3 +
.../LINGYUN.Abp.OssManagement.Tencent.csproj | 16 +
.../Tencent/AbpOssManagementTencentModule.cs | 17 +
.../Tencent/TencentOssContainer.cs | 403 ++++++++++++++++++
.../Tencent/TencentOssContainerFactory.cs | 31 ++
.../README.md | 15 +
49 files changed, 2191 insertions(+), 42 deletions(-)
create mode 100644 aspnet-core/.editorconfig
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/DefaultTencentBlobNameCalculator.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ICosClientFactory.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ITencentBlobNameCalculator.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobContainerConfigurationExtensions.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobNamingNormalizer.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfigurationNames.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN.Abp.Sms.Tencent.csproj
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/AbpSmsTencentModule.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/en.json
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/zh-Hans.json
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingNames.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingProvider.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/TencentCloudSmsSender.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/Volo/Abp/Sms/TencentSmsSenderExtensions.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN.Abp.Tencent.SettingManagement.csproj
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/AbpTencentCloudSettingManagementModule.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/ITenantCloudSettingAppService.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingAppService.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionDefinitionProvider.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionNames.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudOptions.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbstractTencentCloudClientFactory.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentCloudResource.cs
delete mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentResource.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingDefinitionProvider.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingNames.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientCacheItem.cs
create mode 100644 aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientFactory.cs
create mode 100644 aspnet-core/modules/mvc/LINGYUN.Abp.AspNetCore.Mvc.Wrapper/Microsoft/AspNetCore/Cors/AbpCorsPolicyBuilderExtensions.cs
create mode 100644 aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/FodyWeavers.xml
create mode 100644 aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN.Abp.OssManagement.Tencent.csproj
create mode 100644 aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/AbpOssManagementTencentModule.cs
create mode 100644 aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainer.cs
create mode 100644 aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainerFactory.cs
create mode 100644 aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/README.md
diff --git a/aspnet-core/.editorconfig b/aspnet-core/.editorconfig
new file mode 100644
index 000000000..2289110b2
--- /dev/null
+++ b/aspnet-core/.editorconfig
@@ -0,0 +1,137 @@
+# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\Projects\MicroService\CRM\Vue\abp-next-admin\aspnet-core codebase based on best match to current usage at 2021-12-27
+# You can modify the rules from these initially generated values to suit your own policies
+# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
+[*.cs]
+
+
+#Core editorconfig formatting - indentation
+
+#use soft tabs (spaces) for indentation
+indent_style = space
+
+#Formatting - indentation options
+
+#indent switch case contents.
+csharp_indent_case_contents = true
+#indent switch labels
+csharp_indent_switch_labels = true
+
+#Formatting - new line options
+
+#place catch statements on a new line
+csharp_new_line_before_catch = true
+#place else statements on a new line
+csharp_new_line_before_else = true
+#require members of anonymous types to be on separate lines
+csharp_new_line_before_members_in_anonymous_types = true
+#require members of object initializers to be on the same line
+csharp_new_line_before_members_in_object_initializers = false
+#require braces to be on a new line for anonymous_types, control_blocks, types, lambdas, object_collection_array_initializers, methods, and anonymous_methods (also known as "Allman" style)
+csharp_new_line_before_open_brace = anonymous_types, control_blocks, types, lambdas, object_collection_array_initializers, methods, anonymous_methods
+
+#Formatting - organize using options
+
+#do not place System.* using directives before other using directives
+dotnet_sort_system_directives_first = false
+
+#Formatting - spacing options
+
+#require NO space between a cast and the value
+csharp_space_after_cast = false
+#require a space before the colon for bases or interfaces in a type declaration
+csharp_space_after_colon_in_inheritance_clause = true
+#require a space after a keyword in a control flow statement such as a for loop
+csharp_space_after_keywords_in_control_flow_statements = true
+#require a space before the colon for bases or interfaces in a type declaration
+csharp_space_before_colon_in_inheritance_clause = true
+#remove space within empty argument list parentheses
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+#remove space between method call name and opening parenthesis
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
+csharp_space_between_method_call_parameter_list_parentheses = false
+#remove space within empty parameter list parentheses for a method declaration
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+
+#Formatting - wrapping options
+
+#leave code block on single line
+csharp_preserve_single_line_blocks = true
+#leave statements and member declarations on the same line
+csharp_preserve_single_line_statements = true
+
+#Style - Code block preferences
+
+#prefer curly braces even for one line of code
+csharp_prefer_braces = true:suggestion
+
+#Style - expression bodied member options
+
+#prefer block bodies for constructors
+csharp_style_expression_bodied_constructors = false:suggestion
+#prefer block bodies for methods
+csharp_style_expression_bodied_methods = false:suggestion
+#prefer expression-bodied members for properties
+csharp_style_expression_bodied_properties = true:suggestion
+
+#Style - expression level options
+
+#prefer out variables to be declared inline in the argument list of a method call when possible
+csharp_style_inlined_variable_declaration = true:suggestion
+#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+#Style - Expression-level preferences
+
+#prefer default over default(T)
+csharp_prefer_simple_default_expression = true:suggestion
+#prefer objects to be initialized using object initializers when possible
+dotnet_style_object_initializer = true:suggestion
+#prefer inferred anonymous type member names
+dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
+
+#Style - implicit and explicit types
+
+#prefer var over explicit type in all cases, unless overridden by another code style rule
+csharp_style_var_elsewhere = true:suggestion
+#prefer var is used to declare variables with built-in system types such as int
+csharp_style_var_for_built_in_types = true:suggestion
+#prefer var when the type is already mentioned on the right-hand side of a declaration expression
+csharp_style_var_when_type_is_apparent = true:suggestion
+
+#Style - language keyword and framework type options
+
+#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+
+#Style - Miscellaneous preferences
+
+#prefer anonymous functions over local functions
+csharp_style_pattern_local_over_anonymous_function = false:suggestion
+
+#Style - modifier options
+
+#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
+
+#Style - Modifier preferences
+
+#when this rule is set to a list of modifiers, prefer the specified ordering.
+csharp_preferred_modifier_order = public,private,protected,internal,async,virtual,readonly,static,override,abstract:suggestion
+
+#Style - Pattern matching
+
+#prefer pattern matching instead of is expression with type casts
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+
+#Style - qualification options
+
+#prefer fields not to be prefaced with this. or Me. in Visual Basic
+dotnet_style_qualification_for_field = false:suggestion
+#prefer methods not to be prefaced with this. or Me. in Visual Basic
+dotnet_style_qualification_for_method = false:suggestion
+#prefer properties not to be prefaced with this. or Me. in Visual Basic
+dotnet_style_qualification_for_property = false:suggestion
+csharp_style_namespace_declarations=file_scoped:silent
diff --git a/aspnet-core/Directory.Build.props b/aspnet-core/Directory.Build.props
index a84b845ce..fead421c4 100644
--- a/aspnet-core/Directory.Build.props
+++ b/aspnet-core/Directory.Build.props
@@ -7,6 +7,7 @@
5.2.0
1.5.10
2.13.0
+ 3.0.434
1.2.1.5
2.0.3
1.7.27
diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln
index bc28da9f3..6448be090 100644
--- a/aspnet-core/LINGYUN.MicroService.All.sln
+++ b/aspnet-core/LINGYUN.MicroService.All.sln
@@ -363,9 +363,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.DataProtection"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.DataProtection.EntityFrameworkCore", "modules\data-protection\LINGYUN.Abp.DataProtection.EntityFrameworkCore\LINGYUN.Abp.DataProtection.EntityFrameworkCore.csproj", "{19B860CA-2E1E-45CC-A5E2-ED3F2BCEAB5D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.IdGenerator", "modules\common\LINGYUN.Abp.IdGenerator\LINGYUN.Abp.IdGenerator.csproj", "{440C9BD9-85EA-4473-AB1C-7C3562DF4915}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.IdGenerator", "modules\common\LINGYUN.Abp.IdGenerator\LINGYUN.Abp.IdGenerator.csproj", "{440C9BD9-85EA-4473-AB1C-7C3562DF4915}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Serilog.Enrichers.UniqueId", "modules\logging\LINGYUN.Abp.Serilog.Enrichers.UniqueId\LINGYUN.Abp.Serilog.Enrichers.UniqueId.csproj", "{23C3B247-523A-4FBF-B785-2F035E0089BD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Serilog.Enrichers.UniqueId", "modules\logging\LINGYUN.Abp.Serilog.Enrichers.UniqueId\LINGYUN.Abp.Serilog.Enrichers.UniqueId.csproj", "{23C3B247-523A-4FBF-B785-2F035E0089BD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A76A4741-AF91-44EF-A7B6-8E7AF4961146}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Tencent.SettingManagement", "modules\cloud-tencent\LINGYUN.Abp.Tencent.SettingManagement\LINGYUN.Abp.Tencent.SettingManagement.csproj", "{C7CF4193-6397-4450-AF42-3BACD7CF292E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Sms.Tencent", "modules\cloud-tencent\LINGYUN.Abp.Sms.Tencent\LINGYUN.Abp.Sms.Tencent.csproj", "{8FE2725C-6829-4778-93BA-A53260697AFB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.BlobStoring.Tencent", "modules\cloud-tencent\LINGYUN.Abp.BlobStoring.Tencent\LINGYUN.Abp.BlobStoring.Tencent.csproj", "{A4B972EC-9F0B-4405-9965-766FABC9B07E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.OssManagement.Tencent", "modules\oss-management\LINGYUN.Abp.OssManagement.Tencent\LINGYUN.Abp.OssManagement.Tencent.csproj", "{31E60E23-FD98-4D5E-A137-2B3F2968BA09}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -953,6 +966,22 @@ Global
{23C3B247-523A-4FBF-B785-2F035E0089BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23C3B247-523A-4FBF-B785-2F035E0089BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23C3B247-523A-4FBF-B785-2F035E0089BD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C7CF4193-6397-4450-AF42-3BACD7CF292E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C7CF4193-6397-4450-AF42-3BACD7CF292E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C7CF4193-6397-4450-AF42-3BACD7CF292E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C7CF4193-6397-4450-AF42-3BACD7CF292E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8FE2725C-6829-4778-93BA-A53260697AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8FE2725C-6829-4778-93BA-A53260697AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8FE2725C-6829-4778-93BA-A53260697AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8FE2725C-6829-4778-93BA-A53260697AFB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A4B972EC-9F0B-4405-9965-766FABC9B07E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A4B972EC-9F0B-4405-9965-766FABC9B07E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A4B972EC-9F0B-4405-9965-766FABC9B07E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A4B972EC-9F0B-4405-9965-766FABC9B07E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {31E60E23-FD98-4D5E-A137-2B3F2968BA09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31E60E23-FD98-4D5E-A137-2B3F2968BA09}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31E60E23-FD98-4D5E-A137-2B3F2968BA09}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31E60E23-FD98-4D5E-A137-2B3F2968BA09}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1134,6 +1163,10 @@ Global
{19B860CA-2E1E-45CC-A5E2-ED3F2BCEAB5D} = {529DF802-97C4-4BF2-BE7C-39663B3D9EA3}
{440C9BD9-85EA-4473-AB1C-7C3562DF4915} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E}
{23C3B247-523A-4FBF-B785-2F035E0089BD} = {6FC0578B-CDF1-43AD-9F7E-4AA7E4720A02}
+ {C7CF4193-6397-4450-AF42-3BACD7CF292E} = {3B96F4D8-4993-419B-BCEB-AFE4ED39449F}
+ {8FE2725C-6829-4778-93BA-A53260697AFB} = {3B96F4D8-4993-419B-BCEB-AFE4ED39449F}
+ {A4B972EC-9F0B-4405-9965-766FABC9B07E} = {3B96F4D8-4993-419B-BCEB-AFE4ED39449F}
+ {31E60E23-FD98-4D5E-A137-2B3F2968BA09} = {B05CB08F-C088-4D6D-97EE-A94A5D1AE4A6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj
new file mode 100644
index 000000000..9697fa205
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN.Abp.BlobStoring.Tencent.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ netstandard2.0
+
+ 腾讯云Oss对象存储Abp集成
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs
new file mode 100644
index 000000000..db9a6c0dd
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentCloudModule.cs
@@ -0,0 +1,12 @@
+using LINGYUN.Abp.Tencent;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+[DependsOn(
+ typeof(AbpBlobStoringModule),
+ typeof(AbpTencentCloudModule))]
+public class AbpBlobStoringTencentCloudModule : AbpModule
+{
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs
new file mode 100644
index 000000000..d45c1846e
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/CosClientFactory.cs
@@ -0,0 +1,51 @@
+using COSXML;
+using COSXML.Auth;
+using LINGYUN.Abp.Tencent;
+using Microsoft.Extensions.Caching.Memory;
+using System.Threading.Tasks;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+public class CosClientFactory : AbstractTencentCloudClientFactory,
+ ICosClientFactory,
+ ITransientDependency
+{
+ protected IBlobContainerConfigurationProvider ConfigurationProvider { get; }
+
+ public CosClientFactory(
+ IMemoryCache clientCache,
+ ICurrentTenant currentTenant,
+ ISettingProvider settingProvider,
+ IBlobContainerConfigurationProvider configurationProvider)
+ : base(clientCache, currentTenant, settingProvider)
+ {
+ ConfigurationProvider = configurationProvider;
+ }
+
+ public virtual async Task CreateAsync()
+ {
+ var configuration = ConfigurationProvider.Get();
+
+ return await CreateAsync(configuration.GetTencentConfiguration());
+ }
+
+ protected override CosXml CreateClient(TencentBlobProviderConfiguration configuration, TencentCloudClientCacheItem cloudCache)
+ {
+ var configBuilder = new CosXmlConfig.Builder();
+ configBuilder
+ .SetAppid(configuration.AppId)
+ .SetRegion(configuration.Region);
+
+ var cred = new DefaultQCloudCredentialProvider(
+ cloudCache.SecretId,
+ cloudCache.SecretKey,
+ cloudCache.DurationSecond);
+
+ // TODO: 推荐全局单个对象,需要解决缓存过期事件
+ return new CosXmlServer(configBuilder.Build(), cred);
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/DefaultTencentBlobNameCalculator.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/DefaultTencentBlobNameCalculator.cs
new file mode 100644
index 000000000..a4ae97625
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/DefaultTencentBlobNameCalculator.cs
@@ -0,0 +1,24 @@
+using Volo.Abp.BlobStoring;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent
+{
+ public class DefaultTencentBlobNameCalculator : ITencentBlobNameCalculator, ITransientDependency
+ {
+ protected ICurrentTenant CurrentTenant { get; }
+
+ public DefaultTencentBlobNameCalculator(
+ ICurrentTenant currentTenant)
+ {
+ CurrentTenant = currentTenant;
+ }
+
+ public string Calculate(BlobProviderArgs args)
+ {
+ return CurrentTenant.Id == null
+ ? $"host/{args.BlobName}"
+ : $"tenants/{CurrentTenant.Id.Value:D}/{args.BlobName}";
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ICosClientFactory.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ICosClientFactory.cs
new file mode 100644
index 000000000..29127d49f
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ICosClientFactory.cs
@@ -0,0 +1,11 @@
+using COSXML;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+public interface ICosClientFactory
+{
+ Task CreateAsync();
+
+ Task CreateAsync(TencentBlobProviderConfiguration configuration);
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ITencentBlobNameCalculator.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ITencentBlobNameCalculator.cs
new file mode 100644
index 000000000..4ab662c4f
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/ITencentBlobNameCalculator.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.BlobStoring;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent
+{
+ public interface ITencentBlobNameCalculator
+ {
+ string Calculate(BlobProviderArgs args);
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobContainerConfigurationExtensions.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobContainerConfigurationExtensions.cs
new file mode 100644
index 000000000..f86663dfb
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobContainerConfigurationExtensions.cs
@@ -0,0 +1,25 @@
+using System;
+using Volo.Abp.BlobStoring;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent
+{
+ public static class TencentBlobContainerConfigurationExtensions
+ {
+ public static TencentBlobProviderConfiguration GetTencentConfiguration(
+ this BlobContainerConfiguration containerConfiguration)
+ {
+ return new TencentBlobProviderConfiguration(containerConfiguration);
+ }
+
+ public static BlobContainerConfiguration UseTencentCloud(
+ this BlobContainerConfiguration containerConfiguration,
+ Action aliyunConfigureAction)
+ {
+ containerConfiguration.ProviderType = typeof(TencentCloudBlobProvider);
+
+ aliyunConfigureAction(new TencentBlobProviderConfiguration(containerConfiguration));
+
+ return containerConfiguration;
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobNamingNormalizer.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobNamingNormalizer.cs
new file mode 100644
index 000000000..1db121278
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobNamingNormalizer.cs
@@ -0,0 +1,61 @@
+using System.Text.RegularExpressions;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent
+{
+ public class TencentBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency
+ {
+ ///
+ /// 腾讯云对象命名规范
+ /// https://cloud.tencent.com/document/product/436/13324
+ ///
+ ///
+ ///
+ public virtual string NormalizeBlobName(string blobName)
+ {
+ // 不允许以正斜线/或者反斜线\开头。
+ blobName = Regex.Replace(blobName, "^/", string.Empty);
+ blobName = blobName.StartsWith("\\") ? blobName.Substring(1) : blobName;
+
+ // 对象键中不支持 ASCII 控制字符中的
+ // 字符上(↑),字符下(↓),字符右(→),字符左(←),
+ // 分别对应 CAN(24),EM(25),SUB(26),ESC(27)。
+ blobName = blobName.Replace("↑", "");
+ blobName = blobName.Replace("↓", "");
+ blobName = blobName.Replace("←", "");
+ blobName = blobName.Replace("→", "");
+
+ // TODO: 要求还真多...其他暂时不写了
+
+ return blobName;
+ }
+
+ ///
+ /// 腾讯云BucketName命名规范
+ /// https://cloud.tencent.com/document/product/436/13312
+ ///
+ ///
+ ///
+ public virtual string NormalizeContainerName(string containerName)
+ {
+ // 仅支持小写英文字母和数字,即[a-z,0-9]、中划线“-”及其组合。
+ containerName = containerName.ToLower();
+ containerName = Regex.Replace(containerName, "[^a-z0-9-]", string.Empty);
+
+ // 不能以短划线(-)开头
+ containerName = Regex.Replace(containerName, "^-", string.Empty);
+ // 不能以短划线(-)结尾
+ containerName = Regex.Replace(containerName, "-$", string.Empty);
+
+ // 存储桶名称的最大允许字符受到 地域简称 和 APPID 的字符数影响,组成的完整请求域名字符数总计最多60个字符。
+ // 例如请求域名123456789012345678901-1250000000.cos.ap-beijing.myqcloud.com总和为60个字符。
+ if (containerName.Length > 60)
+ {
+ containerName = containerName.Substring(0, 60);
+ }
+
+ return containerName;
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs
new file mode 100644
index 000000000..270e3fb9b
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs
@@ -0,0 +1,63 @@
+using System.Collections.Generic;
+using Volo.Abp;
+using Volo.Abp.BlobStoring;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent
+{
+ public class TencentBlobProviderConfiguration
+ {
+ ///
+ /// AppId
+ ///
+ public string AppId {
+ get => _containerConfiguration.GetConfiguration(TencentBlobProviderConfigurationNames.AppId);
+ set => _containerConfiguration.SetConfiguration(TencentBlobProviderConfigurationNames.AppId, Check.NotNullOrWhiteSpace(value, nameof(value)));
+ }
+ ///
+ /// 区域
+ ///
+ public string Region {
+ get => _containerConfiguration.GetConfiguration(TencentBlobProviderConfigurationNames.Region);
+ set => _containerConfiguration.SetConfiguration(TencentBlobProviderConfigurationNames.Region, value);
+ }
+ ///
+ /// 命名空间
+ ///
+ public string BucketName
+ {
+ get => _containerConfiguration.GetConfiguration(TencentBlobProviderConfigurationNames.BucketName);
+ set => _containerConfiguration.SetConfiguration(TencentBlobProviderConfigurationNames.BucketName, Check.NotNullOrWhiteSpace(value, nameof(value)));
+ }
+ ///
+ /// 命名空间不存在是否创建
+ ///
+ public bool CreateBucketIfNotExists
+ {
+ get => _containerConfiguration.GetConfigurationOrDefault(TencentBlobProviderConfigurationNames.CreateBucketIfNotExists, false);
+ set => _containerConfiguration.SetConfiguration(TencentBlobProviderConfigurationNames.CreateBucketIfNotExists, value);
+ }
+ ///
+ /// 创建命名空间时防盗链列表
+ ///
+ public List CreateBucketReferer {
+ get => _containerConfiguration.GetConfiguration>(TencentBlobProviderConfigurationNames.CreateBucketReferer);
+ set {
+ if (value == null)
+ {
+ _containerConfiguration.SetConfiguration(TencentBlobProviderConfigurationNames.CreateBucketReferer, new List());
+ }
+ else
+ {
+ _containerConfiguration.SetConfiguration(TencentBlobProviderConfigurationNames.CreateBucketReferer, value);
+ }
+ }
+ }
+
+ private readonly BlobContainerConfiguration _containerConfiguration;
+
+ public TencentBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration)
+ {
+ _containerConfiguration = containerConfiguration;
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfigurationNames.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfigurationNames.cs
new file mode 100644
index 000000000..dbe79e467
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfigurationNames.cs
@@ -0,0 +1,26 @@
+namespace LINGYUN.Abp.BlobStoring.Tencent
+{
+ public static class TencentBlobProviderConfigurationNames
+ {
+ ///
+ /// AppId
+ ///
+ public const string AppId = "Tencent:OSS:AppId";
+ ///
+ /// 区域
+ ///
+ public const string Region = "Tencent:OSS:Region";
+ ///
+ /// 命名空间
+ ///
+ public const string BucketName = "Tencent:OSS:BucketName";
+ ///
+ /// 命名空间不存在是否创建
+ ///
+ public const string CreateBucketIfNotExists = "Tencent:OSS:CreateBucketIfNotExists";
+ ///
+ /// 创建命名空间时防盗链列表
+ ///
+ public const string CreateBucketReferer = "Tencent:OSS:CreateBucketReferer";
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs
new file mode 100644
index 000000000..e6e0adf1e
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs
@@ -0,0 +1,165 @@
+using COSXML;
+using COSXML.Common;
+using COSXML.Model.Bucket;
+using COSXML.Model.Object;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+public class TencentCloudBlobProvider : BlobProviderBase, ITransientDependency
+{
+ protected ICosClientFactory CosClientFactory { get; }
+ protected ITencentBlobNameCalculator TencentBlobNameCalculator { get; }
+
+ public TencentCloudBlobProvider(
+ ICosClientFactory cosClientFactory,
+ ITencentBlobNameCalculator tencentBlobNameCalculator)
+ {
+ CosClientFactory = cosClientFactory;
+ TencentBlobNameCalculator = tencentBlobNameCalculator;
+ }
+
+ public override async Task DeleteAsync(BlobProviderDeleteArgs args)
+ {
+ var ossClient = await GetOssClientAsync(args);
+ var blobName = TencentBlobNameCalculator.Calculate(args);
+
+ if (await BlobExistsAsync(ossClient, args, blobName))
+ {
+ var request = new DeleteObjectRequest(GetBucketName(args), blobName);
+ return ossClient.DeleteObject(request).IsSuccessful();
+ }
+
+ return false;
+ }
+
+ public override async Task ExistsAsync(BlobProviderExistsArgs args)
+ {
+ var ossClient = await GetOssClientAsync(args);
+ var blobName = TencentBlobNameCalculator.Calculate(args);
+
+ return await BlobExistsAsync(ossClient, args, blobName);
+ }
+
+ public override async Task GetOrNullAsync(BlobProviderGetArgs args)
+ {
+ var ossClient = await GetOssClientAsync(args);
+ var blobName = TencentBlobNameCalculator.Calculate(args);
+
+ if (!await BlobExistsAsync(ossClient, args, blobName))
+ {
+ return null;
+ }
+
+ // TODO: 未经验证
+
+ var request = new GetObjectBytesRequest(GetBucketName(args), blobName);
+ var ossObject = ossClient.GetObject(request);
+ var memoryStream = new MemoryStream();
+ await memoryStream.WriteAsync(ossObject.content, 0, ossObject.content.Length);
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ return memoryStream;
+ }
+
+ public override async Task SaveAsync(BlobProviderSaveArgs args)
+ {
+ var ossClient = await GetOssClientAsync(args);
+ var blobName = TencentBlobNameCalculator.Calculate(args);
+ var configuration = args.Configuration.GetTencentConfiguration();
+
+ // 先检查Bucket
+ if (configuration.CreateBucketIfNotExists)
+ {
+ await CreateBucketIfNotExists(ossClient, args, configuration.CreateBucketReferer);
+ }
+
+ var bucketName = GetBucketName(args);
+
+ // 是否已存在
+ if (await BlobExistsAsync(ossClient, args, blobName))
+ {
+ // 是否覆盖
+ if (!args.OverrideExisting)
+ {
+ throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the bucketName '{GetBucketName(args)}'! Set {nameof(args.OverrideExisting)} if it should be overwritten.");
+ }
+ else
+ {
+ // 删除原文件
+ var deleteRequest = new DeleteObjectRequest(bucketName, blobName);
+ ossClient.DeleteObject(deleteRequest);
+ }
+ }
+ // 保存
+ var putRequest = new PutObjectRequest(bucketName, blobName, args.BlobStream);
+ ossClient.PutObject(putRequest);
+ }
+
+ protected virtual async Task GetOssClientAsync(BlobProviderArgs args)
+ {
+ var configuration = args.Configuration.GetTencentConfiguration();
+ var ossClient = await CosClientFactory.CreateAsync(configuration);
+ return ossClient;
+ }
+
+ protected virtual async Task CreateBucketIfNotExists(CosXml cos, BlobProviderArgs args, IList refererList = null)
+ {
+ if (!await BucketExistsAsync(cos, args))
+ {
+ var bucketName = GetBucketName(args);
+
+ var request = new PutBucketRequest(bucketName);
+ // TODO: good! 这很Java
+ request.SetCosACL(CosACL.PublicReadWrite);
+
+ cos.PutBucket(request);
+
+ if (refererList != null && refererList.Count > 0)
+ {
+ var srq = new PutBucketRefererRequest(bucketName);
+ var refererConfig = new COSXML.Model.Tag.RefererConfiguration();
+ foreach (var domain in refererList)
+ {
+ refererConfig.domainList.AddDomain(domain);
+ }
+ srq.SetRefererConfiguration(refererConfig);
+
+ cos.PutBucketReferer(srq);
+ }
+ }
+ }
+
+ private async Task BlobExistsAsync(CosXml cos, BlobProviderArgs args, string blobName)
+ {
+ var bucketExists = await BucketExistsAsync(cos, args);
+ if (bucketExists)
+ {
+ var request = new DoesObjectExistRequest(GetBucketName(args), blobName);
+ var objectExists = cos.DoesObjectExist(request);
+
+ return objectExists;
+ }
+ return false;
+ }
+
+ private Task BucketExistsAsync(CosXml cos, BlobProviderArgs args)
+ {
+ var request = new DoesBucketExistRequest(GetBucketName(args));
+ var bucketExists = cos.DoesBucketExist(request);
+
+ return Task.FromResult(bucketExists);
+ }
+
+ private static string GetBucketName(BlobProviderArgs args)
+ {
+ var configuration = args.Configuration.GetTencentConfiguration();
+ return configuration.BucketName.IsNullOrWhiteSpace()
+ ? args.ContainerName
+ : configuration.BucketName;
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN.Abp.Sms.Tencent.csproj b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN.Abp.Sms.Tencent.csproj
new file mode 100644
index 000000000..75746b80c
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN.Abp.Sms.Tencent.csproj
@@ -0,0 +1,23 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/AbpSmsTencentModule.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/AbpSmsTencentModule.cs
new file mode 100644
index 000000000..f808861aa
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/AbpSmsTencentModule.cs
@@ -0,0 +1,30 @@
+using LINGYUN.Abp.Tencent;
+using LINGYUN.Abp.Tencent.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.Sms;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.Sms.Tencent
+{
+ [DependsOn(
+ typeof(AbpSmsModule),
+ typeof(AbpTencentCloudModule))]
+ public class AbpSmsTencentModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("/LINGYUN/Abp/Sms/Tencent/Localization/Resources");
+ });
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/en.json b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/en.json
new file mode 100644
index 000000000..5cbf681ec
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/en.json
@@ -0,0 +1,12 @@
+{
+ "culture": "en",
+ "texts": {
+ "DisplayName:TenantCloud.SmsSetting": "Sms Setting",
+ "DisplayName:AppId": "App Id",
+ "Description:AppId": "AppId applied on Tencent Cloud SMS control platform.",
+ "DisplayName:DefaultSignName": "Default Sign Name",
+ "Description:DefaultSignName": "Default signname if no signname is specified in the SMS message body",
+ "DisplayName:DefaultTemplateId": "Default Template Id",
+ "Description:DefaultTemplateId": "Default template id if the Template Id number is not specified in the SMS message body."
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/zh-Hans.json b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..2f6f529bf
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Localization/Resources/zh-Hans.json
@@ -0,0 +1,12 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "DisplayName:TenantCloud.SmsSetting": "短信设置",
+ "DisplayName:AppId": "应用Id",
+ "Description:AppId": "在腾讯云短信控制平台申请的应用标识",
+ "DisplayName:DefaultSignName": "默认签名",
+ "Description:DefaultSignName": "当短信消息体未指定签名时的默认签名",
+ "DisplayName:DefaultTemplateId": "默认模板",
+ "Description:DefaultTemplateId": "当短信消息体未指定模板号时的默认模板标识"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingNames.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingNames.cs
new file mode 100644
index 000000000..185f31eb0
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingNames.cs
@@ -0,0 +1,23 @@
+using LINGYUN.Abp.Tencent.Settings;
+
+namespace LINGYUN.Abp.Sms.Tencent.Settings
+{
+ public static class TencentCloudSmsSettingNames
+ {
+ public const string Prefix = TencentCloudSettingNames.Prefix + ".Sms";
+
+ ///
+ /// 短信 SdkAppId
+ /// 在 短信控制台 添加应用后生成的实际 SdkAppId,示例如1400006666。
+ ///
+ public const string AppId = Prefix + ".Domain";
+ ///
+ /// 短信签名内容
+ ///
+ public const string DefaultSignName = Prefix + ".DefaultSignName";
+ ///
+ /// 默认短信模板 ID
+ ///
+ public const string DefaultTemplateId = Prefix + ".DefaultTemplateId";
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingProvider.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingProvider.cs
new file mode 100644
index 000000000..0c4a4e72f
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/Settings/TencentCloudSmsSettingProvider.cs
@@ -0,0 +1,59 @@
+using LINGYUN.Abp.Tencent.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Sms.Tencent.Settings
+{
+ public class TencentCloudSmsSettingProvider : SettingDefinitionProvider
+ {
+ public override void Define(ISettingDefinitionContext context)
+ {
+ context.Add(CreateAliyunSettings());
+ }
+
+ private SettingDefinition[] CreateAliyunSettings()
+ {
+ return new SettingDefinition[]
+ {
+ new SettingDefinition(
+ TencentCloudSmsSettingNames.AppId,
+ displayName: L("DisplayName:AppId"),
+ description: L("Description:AppId"),
+ isVisibleToClients: false,
+ isEncrypted: true
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ TencentCloudSmsSettingNames.DefaultSignName,
+ displayName: L("DisplayName:DefaultSignName"),
+ description: L("Description:DefaultSignName"),
+ isVisibleToClients: false,
+ isEncrypted: true
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ TencentCloudSmsSettingNames.DefaultTemplateId,
+ displayName: L("DisplayName:DefaultTemplateId"),
+ description: L("Description:DefaultTemplateId"),
+ isVisibleToClients: false,
+ isEncrypted: true
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ };
+ }
+
+ private ILocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/TencentCloudSmsSender.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/TencentCloudSmsSender.cs
new file mode 100644
index 000000000..fb10946b1
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/LINGYUN/Abp/Sms/Tencent/TencentCloudSmsSender.cs
@@ -0,0 +1,97 @@
+using LINGYUN.Abp.Sms.Tencent.Settings;
+using LINGYUN.Abp.Tencent;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TencentCloud.Sms.V20210111;
+using TencentCloud.Sms.V20210111.Models;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Json;
+using Volo.Abp.Settings;
+using Volo.Abp.Sms;
+
+namespace LINGYUN.Abp.Sms.Tencent
+{
+ [Dependency(ReplaceServices = true)]
+ public class TencentCloudSmsSender : ISmsSender, ITransientDependency
+ {
+ public ILogger Logger { protected get; set; }
+
+ protected IJsonSerializer JsonSerializer { get; }
+ protected ISettingProvider SettingProvider { get; }
+ protected IServiceProvider ServiceProvider { get; }
+ protected TencentCloudClientFactory TencentCloudClientFactory { get; }
+ public TencentCloudSmsSender(
+ IJsonSerializer jsonSerializer,
+ ISettingProvider settingProvider,
+ IServiceProvider serviceProvider,
+ TencentCloudClientFactory tencentCloudClientFactory)
+ {
+ JsonSerializer = jsonSerializer;
+ SettingProvider = settingProvider;
+ ServiceProvider = serviceProvider;
+ TencentCloudClientFactory = tencentCloudClientFactory;
+
+ Logger = NullLogger.Instance;
+ }
+
+ public virtual async Task SendAsync(SmsMessage smsMessage)
+ {
+ var appId = await SettingProvider.GetOrNullAsync(TencentCloudSmsSettingNames.AppId);
+
+ Check.NotNullOrWhiteSpace(appId, TencentCloudSmsSettingNames.AppId);
+
+ // 统一使用 TemplateCode作为模板参数, 解决不一样的sms提供商参数差异
+ if (!smsMessage.Properties.TryGetValue("TemplateCode", out var templateId))
+ {
+ templateId = await SettingProvider.GetOrNullAsync(TencentCloudSmsSettingNames.DefaultTemplateId);
+ }
+
+ if (!smsMessage.Properties.TryGetValue("SignName", out var signName))
+ {
+ signName = await SettingProvider.GetOrNullAsync(TencentCloudSmsSettingNames.DefaultSignName);
+ }
+
+ if (!smsMessage.Properties.TryGetValue("TemplateParam", out var templateParam))
+ {
+ templateParam = await SettingProvider.GetOrNullAsync(TencentCloudSmsSettingNames.DefaultSignName);
+ }
+
+ var smsClient = await TencentCloudClientFactory.CreateAsync();
+
+ var request = new SendSmsRequest
+ {
+ SmsSdkAppId = appId,
+ SignName = signName?.ToString(),
+ TemplateId = templateId?.ToString(),
+ PhoneNumberSet = smsMessage.PhoneNumber.Split(';'),
+ TemplateParamSet = templateParam?.ToString()?.Split(';'),
+ };
+
+ var response = await smsClient.SendSms(request);
+
+ var warningMessage = response.SendStatusSet
+ .Where(x => !"ok".Equals(x.Code, StringComparison.InvariantCultureIgnoreCase))
+ // 记录流水号, 手机号, 错误代码, 错误信息
+ .Select(x => $"SerialNo: {x.SerialNo}, PhoneNumber: {x.PhoneNumber}, Status: {x.Code}, Error: {x.Message}");
+ // 所有短信发送失败, 抛出错误
+ // 只要一条发送成功,即视为成功
+ if (!response.SendStatusSet.Any(x => "ok".Equals(x.Code, StringComparison.InvariantCultureIgnoreCase)))
+ {
+ var errorMessage = warningMessage.JoinAsString(Environment.NewLine);
+
+ throw new AbpException($"Send tencent cloud sms error: {errorMessage}");
+ }
+
+ if (warningMessage.Any())
+ {
+ Logger.LogWarning("Some failed send sms messages, error info:");
+ Logger.LogWarning(warningMessage.JoinAsString(Environment.NewLine));
+ }
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/Volo/Abp/Sms/TencentSmsSenderExtensions.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/Volo/Abp/Sms/TencentSmsSenderExtensions.cs
new file mode 100644
index 000000000..799a2e6a3
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Sms.Tencent/Volo/Abp/Sms/TencentSmsSenderExtensions.cs
@@ -0,0 +1,58 @@
+using LINGYUN.Abp.Sms.Tencent;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Sms
+{
+ public static class TencentSmsSenderExtensions
+ {
+ ///
+ /// 扩展短信接口
+ ///
+ ///
+ /// 短信模板号
+ /// 发送手机号
+ /// 短信模板参数
+ ///
+ public static async Task SendAsync(
+ this ISmsSender smsSender,
+ string templateCode,
+ string phoneNumber,
+ IDictionary templateParams = null)
+ {
+ var smsMessage = new SmsMessage(phoneNumber, nameof(TencentCloudSmsSender));
+ smsMessage.Properties.Add("TemplateCode", templateCode);
+ if(templateParams != null)
+ {
+ smsMessage.Properties.Add("TemplateParam", templateParams);
+ }
+ await smsSender.SendAsync(smsMessage);
+ }
+
+ ///
+ /// 扩展短信接口
+ ///
+ ///
+ /// 短信签名
+ /// 短信模板号
+ /// 发送手机号
+ /// 短信模板参数
+ ///
+ public static async Task SendAsync(
+ this ISmsSender smsSender,
+ string signName,
+ string templateCode,
+ string phoneNumber,
+ IDictionary templateParams = null)
+ {
+ var smsMessage = new SmsMessage(phoneNumber, nameof(TencentCloudSmsSender));
+ smsMessage.Properties.Add("SignName", signName);
+ smsMessage.Properties.Add("TemplateCode", templateCode);
+ if (templateParams != null)
+ {
+ smsMessage.Properties.Add("TemplateParam", templateParams);
+ }
+ await smsSender.SendAsync(smsMessage);
+ }
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN.Abp.Tencent.SettingManagement.csproj b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN.Abp.Tencent.SettingManagement.csproj
new file mode 100644
index 000000000..1b20b4cf4
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN.Abp.Tencent.SettingManagement.csproj
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/AbpTencentCloudSettingManagementModule.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/AbpTencentCloudSettingManagementModule.cs
new file mode 100644
index 000000000..a15337dca
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/AbpTencentCloudSettingManagementModule.cs
@@ -0,0 +1,16 @@
+using LINGYUN.Abp.SettingManagement;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Modularity;
+using Volo.Abp.SettingManagement;
+
+namespace LINGYUN.Abp.Tencent.SettingManagement;
+
+[DependsOn(
+ typeof(AbpTencentCloudModule),
+ typeof(AbpSettingManagementApplicationContractsModule),
+ typeof(AbpSettingManagementDomainModule),
+ typeof(AbpAspNetCoreMvcModule))]
+public class AbpTencentCloudSettingManagementModule : AbpModule
+{
+
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/ITenantCloudSettingAppService.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/ITenantCloudSettingAppService.cs
new file mode 100644
index 000000000..ca935832e
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/ITenantCloudSettingAppService.cs
@@ -0,0 +1,7 @@
+using LINGYUN.Abp.SettingManagement;
+
+namespace LINGYUN.Abp.Tencent.SettingManagement;
+
+public interface ITenantCloudSettingAppService : IReadonlySettingAppService
+{
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingAppService.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingAppService.cs
new file mode 100644
index 000000000..8fd8df93e
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingAppService.cs
@@ -0,0 +1,168 @@
+using LINGYUN.Abp.SettingManagement;
+using LINGYUN.Abp.Sms.Tencent.Settings;
+using LINGYUN.Abp.Tencent.Localization;
+using LINGYUN.Abp.Tencent.Settings;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Services;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.SettingManagement;
+using Volo.Abp.Settings;
+using ValueType = LINGYUN.Abp.SettingManagement.ValueType;
+
+namespace LINGYUN.Abp.Tencent.SettingManagement;
+
+public class TenantCloudSettingAppService : ApplicationService, ITenantCloudSettingAppService
+{
+ protected ISettingManager SettingManager { get; }
+ protected IPermissionChecker PermissionChecker { get; }
+ protected ISettingDefinitionManager SettingDefinitionManager { get; }
+ protected ITencentCloudClientFactory TencentCloudClientFactory { get; }
+
+ public TenantCloudSettingAppService(
+ ISettingManager settingManager,
+ IPermissionChecker permissionChecker,
+ ISettingDefinitionManager settingDefinitionManager,
+ ITencentCloudClientFactory tencentCloudClientFactory)
+ {
+ SettingManager = settingManager;
+ PermissionChecker = permissionChecker;
+ SettingDefinitionManager = settingDefinitionManager;
+ TencentCloudClientFactory = tencentCloudClientFactory;
+
+ LocalizationResource = typeof(TencentCloudResource);
+ }
+
+ public virtual async Task GetAllForCurrentTenantAsync()
+ {
+ return await GetAllForProviderAsync(TenantSettingValueProvider.ProviderName, CurrentTenant.GetId().ToString());
+ }
+
+ public virtual async Task GetAllForGlobalAsync()
+ {
+ return await GetAllForProviderAsync(GlobalSettingValueProvider.ProviderName, null);
+ }
+
+ protected virtual async Task GetAllForProviderAsync(string providerName, string providerKey)
+ {
+ var settingGroups = new SettingGroupResult();
+
+ // 无权限返回空结果,直接报错的话,网关聚合会抛出异常
+ if (await PermissionChecker.IsGrantedAsync(TenantCloudSettingPermissionNames.Settings))
+ {
+ var settingGroup = new SettingGroupDto(L["DisplayName:TenantCloud"], L["DisplayName:TenantCloud"]);
+
+ #region 基本设置
+
+ var basicSetting = settingGroup.AddSetting(L["DisplayName:TenantCloud.BasicSetting"], L["Description:TenantCloud.BasicSetting"]);
+
+ basicSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSettingNames.EndPoint),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSettingNames.EndPoint, providerName, providerKey),
+ ValueType.Option,
+ providerName)
+ .AddOptions(GetAvailableRegionOptions());
+ basicSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSettingNames.SecretId),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSettingNames.SecretId, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ basicSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSettingNames.SecretKey),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSettingNames.SecretKey, providerName, providerKey),
+ ValueType.String,
+ providerName);
+
+ #endregion
+
+ #region 连接信息
+
+ var connectionSetting = settingGroup.AddSetting(
+ L["DisplayName:TenantCloud.ConnectionSetting"], L["Description:TenantCloud.ConnectionSetting"]);
+
+ connectionSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSettingNames.Connection.HttpMethod),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSettingNames.Connection.HttpMethod, providerName, providerKey),
+ ValueType.Option,
+ providerName)
+ .AddOption("POST", "POST")
+ .AddOption("GET", "GET");
+ connectionSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSettingNames.Connection.Timeout),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSettingNames.Connection.Timeout, providerName, providerKey),
+ ValueType.Number,
+ providerName);
+ connectionSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSettingNames.Connection.WebProxy),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSettingNames.Connection.WebProxy, providerName, providerKey),
+ ValueType.Number,
+ providerName);
+
+ #endregion
+
+ #region 短信设置
+
+ var smsSetting = settingGroup.AddSetting(
+ L["DisplayName:TenantCloud.SmsSetting"], L["Description:TenantCloud.SmsSetting"]);
+
+ smsSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSmsSettingNames.AppId),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSmsSettingNames.AppId, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ smsSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSmsSettingNames.DefaultTemplateId),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSmsSettingNames.DefaultTemplateId, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ smsSetting.AddDetail(
+ SettingDefinitionManager.Get(TencentCloudSmsSettingNames.DefaultSignName),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TencentCloudSmsSettingNames.DefaultSignName, providerName, providerKey),
+ ValueType.String,
+ providerName);
+
+ #endregion
+
+ settingGroups.AddGroup(settingGroup);
+ }
+
+ return settingGroups;
+ }
+
+ protected virtual IEnumerable GetAvailableRegionOptions()
+ {
+ return new OptionDto[]
+ {
+ new OptionDto(L["Region:Beijing"], "ap-beijing"),
+ new OptionDto(L["Region:Chengdu"], "ap-chengdu"),
+ new OptionDto(L["Region:Chongqing"], "ap-chongqing"),
+ new OptionDto(L["Region:Guangzhou"], "ap-guangzhou"),
+ new OptionDto(L["Region:Hongkong"], "ap-hongkong"),
+ new OptionDto(L["Region:Nanjing"], "ap-nanjing"),
+ new OptionDto(L["Region:Shanghai"], "ap-shanghai"),
+ new OptionDto(L["Region:ShanghaiFsi"], "ap-shanghai-fsi"),
+ new OptionDto(L["Region:ShenzhenFsi"], "ap-shenzhen-fsi"),
+ new OptionDto(L["Region:Bangkok"], "ap-bangkok"),
+ new OptionDto(L["Region:Jakarta"], "ap-jakarta"),
+ new OptionDto(L["Region:Mumbai"], "ap-mumbai"),
+ new OptionDto(L["Region:Seoul"], "ap-seoul"),
+ new OptionDto(L["Region:Singapore"], "ap-singapore"),
+ new OptionDto(L["Region:Tokyo"], "ap-tokyo"),
+ new OptionDto(L["Region:Frankfurt"], "eu-frankfurt"),
+ new OptionDto(L["Region:Moscow"], "eu-moscow"),
+ new OptionDto(L["Region:Virginia"], "na-ashburn"),
+ new OptionDto(L["Region:SiliconValley"], "na-siliconvalley"),
+ new OptionDto(L["Region:Toronto"], "na-toronto"),
+ };
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionDefinitionProvider.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionDefinitionProvider.cs
new file mode 100644
index 000000000..4fa39af5c
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionDefinitionProvider.cs
@@ -0,0 +1,23 @@
+using LINGYUN.Abp.Tencent.Localization;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Tencent.SettingManagement;
+
+public class TenantCloudSettingPermissionDefinitionProvider : PermissionDefinitionProvider
+{
+ public override void Define(IPermissionDefinitionContext context)
+ {
+ var wechatGroup = context.AddGroup(
+ TenantCloudSettingPermissionNames.GroupName,
+ L("Permission:TencentCloud"));
+
+ wechatGroup.AddPermission(
+ TenantCloudSettingPermissionNames.Settings, L("Permission:TencentCloud.Settings"));
+ }
+
+ protected LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionNames.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionNames.cs
new file mode 100644
index 000000000..9e2f09e80
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent.SettingManagement/LINGYUN/Abp/Tencent/SettingManagement/TenantCloudSettingPermissionNames.cs
@@ -0,0 +1,9 @@
+namespace LINGYUN.Abp.Tencent.SettingManagement
+{
+ public class TenantCloudSettingPermissionNames
+ {
+ public const string GroupName = "Abp.Tencent";
+
+ public const string Settings = GroupName + ".Settings";
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN.Abp.Tencent.csproj b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN.Abp.Tencent.csproj
index d5aea83db..aa3675404 100644
--- a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN.Abp.Tencent.csproj
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINGYUN.Abp.Tencent.csproj
@@ -10,19 +10,19 @@
-
-
+
-
-
+
+
+
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudModule.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudModule.cs
index 7af09c44e..9a0ccd660 100644
--- a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudModule.cs
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudModule.cs
@@ -1,12 +1,15 @@
-using LINYUN.Abp.Tencent.Localization;
+using LINGYUN.Abp.Tencent.Localization;
+using Volo.Abp.Caching;
using Volo.Abp.Json;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
+using Microsoft.Extensions.DependencyInjection;
namespace LINGYUN.Abp.Tencent
{
[DependsOn(
+ typeof(AbpCachingModule),
typeof(AbpJsonModule),
typeof(AbpLocalizationModule))]
public class AbpTencentCloudModule : AbpModule
@@ -21,9 +24,18 @@ namespace LINGYUN.Abp.Tencent
Configure(options =>
{
options.Resources
- .Add()
+ .Add()
.AddVirtualJson("/LINGYUN/Abp/Tencent/Localization/Resources");
});
+
+ context.Services.AddTransient>();
+ //Configure(options =>
+ //{
+ // 按照腾讯SDK, 所有客户端都有三个参数组成, 暂时设计为通过反射构造函数来创建客户端
+ // options.ClientProxies.Add(typeof(AaClient), (cred, endpoint, profile) => new AaClient(cred, endpoint, profile));
+ // options.ClientProxies.Add(typeof(AaiClient), (cred, endpoint, profile) => new AaiClient(cred, endpoint, profile));
+ // options.ClientProxies.Add(typeof(AdvisorClient), (cred, endpoint, profile) => new AdvisorClient(cred, endpoint, profile));
+ //});
}
}
}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudOptions.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudOptions.cs
new file mode 100644
index 000000000..3ff855a67
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbpTencentCloudOptions.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using TencentCloud.Common;
+using TencentCloud.Common.Profile;
+
+namespace LINGYUN.Abp.Tencent;
+
+public class AbpTencentCloudOptions
+{
+ public IDictionary> ClientProxies { get; }
+
+ public AbpTencentCloudOptions()
+ {
+ ClientProxies = new Dictionary>();
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbstractTencentCloudClientFactory.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbstractTencentCloudClientFactory.cs
new file mode 100644
index 000000000..235914c36
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/AbstractTencentCloudClientFactory.cs
@@ -0,0 +1,124 @@
+using LINGYUN.Abp.Tencent.Settings;
+using Microsoft.Extensions.Caching.Memory;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Tencent;
+
+public abstract class AbstractTencentCloudClientFactory
+{
+ protected IMemoryCache ClientCache { get; }
+ protected ICurrentTenant CurrentTenant { get; }
+ protected ISettingProvider SettingProvider { get; }
+
+ public AbstractTencentCloudClientFactory(
+ IMemoryCache clientCache,
+ ICurrentTenant currentTenant,
+ ISettingProvider settingProvider)
+ {
+ ClientCache = clientCache;
+ CurrentTenant = currentTenant;
+ SettingProvider = settingProvider;
+ }
+
+ public virtual async Task CreateAsync()
+ {
+ var clientCacheItem = await GetClientCacheItemAsync();
+
+ return CreateClient(clientCacheItem);
+ }
+
+ protected abstract TClient CreateClient(TencentCloudClientCacheItem cloudCache);
+
+ protected virtual async Task GetClientCacheItemAsync()
+ {
+ return await ClientCache.GetOrCreateAsync(
+ TencentCloudClientCacheItem.CalculateCacheKey(CurrentTenant),
+ async (_) =>
+ {
+ var secretId = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.SecretId);
+ var secretKey = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.SecretKey);
+ var endpoint = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.EndPoint);
+
+ Check.NotNullOrWhiteSpace(secretId, TencentCloudSettingNames.SecretId);
+ Check.NotNullOrWhiteSpace(secretKey, TencentCloudSettingNames.SecretKey);
+
+
+ var method = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.Connection.HttpMethod);
+ var webProxy = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.Connection.WebProxy);
+ var timeout = await SettingProvider.GetAsync(TencentCloudSettingNames.Connection.Timeout, 60);
+
+ return new TencentCloudClientCacheItem
+ {
+ SecretId = secretId,
+ SecretKey = secretKey,
+ // 连接区域
+ EndPoint = endpoint,
+ HttpMethod = method,
+ WebProxy = webProxy,
+ Timeout = timeout,
+ };
+ });
+ }
+}
+
+public abstract class AbstractTencentCloudClientFactory
+{
+ protected IMemoryCache ClientCache { get; }
+ protected ICurrentTenant CurrentTenant { get; }
+ protected ISettingProvider SettingProvider { get; }
+
+ public AbstractTencentCloudClientFactory(
+ IMemoryCache clientCache,
+ ICurrentTenant currentTenant,
+ ISettingProvider settingProvider)
+ {
+ ClientCache = clientCache;
+ CurrentTenant = currentTenant;
+ SettingProvider = settingProvider;
+ }
+
+ public virtual async Task CreateAsync(TConfiguration configuration)
+ {
+ var clientCacheItem = await GetClientCacheItemAsync();
+
+ return CreateClient(configuration, clientCacheItem);
+ }
+
+ protected abstract TClient CreateClient(TConfiguration configuration, TencentCloudClientCacheItem cloudCache);
+
+ protected virtual async Task GetClientCacheItemAsync()
+ {
+ return await ClientCache.GetOrCreateAsync(
+ TencentCloudClientCacheItem.CalculateCacheKey(CurrentTenant),
+ async (_) =>
+ {
+ var secretId = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.SecretId);
+ var secretKey = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.SecretKey);
+ var endpoint = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.EndPoint);
+ var durationSecond = await SettingProvider.GetAsync(TencentCloudSettingNames.DurationSecond, 3600);
+
+ Check.NotNullOrWhiteSpace(secretId, TencentCloudSettingNames.SecretId);
+ Check.NotNullOrWhiteSpace(secretKey, TencentCloudSettingNames.SecretKey);
+
+
+ var method = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.Connection.HttpMethod);
+ var webProxy = await SettingProvider.GetOrNullAsync(TencentCloudSettingNames.Connection.WebProxy);
+ var timeout = await SettingProvider.GetAsync(TencentCloudSettingNames.Connection.Timeout, 60);
+
+ return new TencentCloudClientCacheItem
+ {
+ SecretId = secretId,
+ SecretKey = secretKey,
+ // 连接区域
+ EndPoint = endpoint,
+ DurationSecond = durationSecond,
+ HttpMethod = method,
+ WebProxy = webProxy,
+ Timeout = timeout,
+ };
+ });
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/en.json b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/en.json
index a9c8dcc3f..2dcca815b 100644
--- a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/en.json
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/en.json
@@ -1,5 +1,42 @@
{
"culture": "en",
"texts": {
+ "DisplayName:TenantCloud": "Tenant Cloud",
+ "DisplayName:TenantCloud.BasicSetting": "Basic Setting",
+ "Description:TenantCloud.BasicSetting": "Tencent cloud basic information configuration",
+ "DisplayName:TenantCloud.ConnectionSetting": "Connection Setting",
+ "Description:TenantCloud.ConnectionSetting": "Tencent Cloud SDK connection configuration",
+ "DisplayName:EndPoint": "EndPoint",
+ "Description:EndPoint": "Represents a region where a resource resides, and each region contains one or more available regions.",
+ "DisplayName:SecretId": "Secret Id",
+ "Description:SecretId": "The Secret Id can be obtained only from Tencent Cloud Management Console.",
+ "DisplayName:SecretKey": "Secret Key",
+ "Description:SecretKey": "The Secret Key can be obtained only from Tencent Cloud Management Console.",
+ "DisplayName:HttpMethod": "Http Method",
+ "Description:HttpMethod": "The SDK uses the POST method by default, but if you must use the GET method, you can set it here. The GET method cannot handle some large requests.",
+ "DisplayName:Timeout": "Timeout",
+ "Description:Timeout": "Connection timeout, in seconds (default: 60 seconds)",
+ "DisplayName:WebProxy": "Web Proxy",
+ "Description:WebProxy": "Proxy server, set when there are proxy servers in your environment.",
+ "Region:Beijing": "North China (Beijing)",
+ "Region:Chengdu": "Southwest China (Chengdu)",
+ "Region:Chongqing": "Southwest China (Chongqing)",
+ "Region:Guangzhou": "South China (Guangzhou)",
+ "Region:Hongkong": "Hong Kong/Macao/Taiwan (Hong Kong, China)",
+ "Region:Nanjing": "East China (Nanjing)",
+ "Region:Shanghai": "East China (Shanghai)",
+ "Region:ShanghaiFsi": "East China (Shanghai financial)",
+ "Region:ShenzhenFsi": "South China (Shenzhen financial)",
+ "Region:Bangkok": "Southeast Asia (Bangkok)",
+ "Region:Jakarta": "Southeast Asia (Jakarta)",
+ "Region:Mumbai": "South Asia (Mumbai)",
+ "Region:Seoul": "Northeast Asia (Seoul)",
+ "Region:Singapore": "Southeast Asia (Singapore)",
+ "Region:Tokyo": "Northeast Asia (Tokyo)",
+ "Region:Frankfurt": "Europe (Frankfurt)",
+ "Region:Moscow": "Europe (Moscow)",
+ "Region:Virginia": "Eastern US (Virginia)",
+ "Region:SiliconValley": "Western US (Silicon Valley)",
+ "Region:Toronto": "North America (Toronto)"
}
}
\ No newline at end of file
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/zh-Hans.json b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/zh-Hans.json
index c5ad81326..4b76d952b 100644
--- a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/zh-Hans.json
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/Resources/zh-Hans.json
@@ -1,5 +1,42 @@
{
"culture": "zh-Hans",
"texts": {
+ "DisplayName:TenantCloud": "腾讯云服务",
+ "DisplayName:TenantCloud.BasicSetting": "基础配置",
+ "Description:TenantCloud.BasicSetting": "腾讯云基础信息配置",
+ "DisplayName:TenantCloud.ConnectionSetting": "连接配置",
+ "Description:TenantCloud.ConnectionSetting": "腾讯云SDK连接相关配置",
+ "DisplayName:EndPoint": "资源地域",
+ "Description:EndPoint": "表示资源所在的地域,每个地域包含一个或多个可用区。",
+ "DisplayName:SecretId": "密钥Id",
+ "Description:SecretId": "密钥Id,只能从腾讯云管理控制台获取。",
+ "DisplayName:SecretKey": "密钥Key",
+ "Description:SecretKey": "密钥Key,只能从腾讯云管理控制台获取。",
+ "DisplayName:HttpMethod": "请求类型",
+ "Description:HttpMethod": "SDK默认使用POST方法,如果你一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求。",
+ "DisplayName:Timeout": "超时时间",
+ "Description:Timeout": "请求连接超时时间,单位为秒(默认60秒)",
+ "DisplayName:WebProxy": "代理服务器",
+ "Description:WebProxy": "代理服务器,当你的环境下有代理服务器时设定",
+ "Region:Beijing": "华北地区(北京)",
+ "Region:Chengdu": "西南地区(成都)",
+ "Region:Chongqing": "西南地区(重庆)",
+ "Region:Guangzhou": "华南地区(广州)",
+ "Region:Hongkong": "港澳台地区(中国香港)",
+ "Region:Nanjing": "华东地区(南京)",
+ "Region:Shanghai": "华东地区(上海)",
+ "Region:ShanghaiFsi": "华东地区(上海金融)",
+ "Region:ShenzhenFsi": "华南地区(深圳金融)",
+ "Region:Bangkok": "亚太东南(曼谷)",
+ "Region:Jakarta": "亚太东南(雅加达)",
+ "Region:Mumbai": "亚太南部(孟买)",
+ "Region:Seoul": "亚太东北(首尔)",
+ "Region:Singapore": "亚太东南(新加坡)",
+ "Region:Tokyo": "亚太东北(东京)",
+ "Region:Frankfurt": "欧洲地区(法兰克福)",
+ "Region:Moscow": "欧洲地区(莫斯科)",
+ "Region:Virginia": "美国东部(弗吉尼亚)",
+ "Region:SiliconValley": "美国西部(硅谷)",
+ "Region:Toronto": "北美地区(多伦多)"
}
}
\ No newline at end of file
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentCloudResource.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentCloudResource.cs
new file mode 100644
index 000000000..160fcbc6f
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentCloudResource.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Tencent.Localization
+{
+ [LocalizationResourceName("TencentCloud")]
+ public class TencentCloudResource
+ {
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentResource.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentResource.cs
deleted file mode 100644
index cc839f75a..000000000
--- a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Localization/TencentResource.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Volo.Abp.Localization;
-
-namespace LINYUN.Abp.Tencent.Localization
-{
- [LocalizationResourceName("Tencent")]
- public class TencentResource
- {
- }
-}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingDefinitionProvider.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingDefinitionProvider.cs
new file mode 100644
index 000000000..4eef82366
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingDefinitionProvider.cs
@@ -0,0 +1,94 @@
+using LINGYUN.Abp.Tencent.Localization;
+using LINGYUN.Abp.Tencent.Settings;
+using Volo.Abp.Localization;
+using Volo.Abp.Settings;
+
+namespace LINYUN.Abp.Tencent.Settings;
+
+public class TencentCloudSettingDefinitionProvider : SettingDefinitionProvider
+{
+ public override void Define(ISettingDefinitionContext context)
+ {
+ context.Add(CreateTencentCloudSettings());
+ }
+
+ private SettingDefinition[] CreateTencentCloudSettings()
+ {
+ return new SettingDefinition[]
+ {
+ new SettingDefinition(
+ TencentCloudSettingNames.EndPoint,
+ // 腾讯云默认使用广州区域
+ defaultValue: "ap-guangzhou",
+ displayName: L("DisplayName:EndPoint"),
+ description: L("Description:EndPoint"),
+ isVisibleToClients: false
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ TencentCloudSettingNames.SecretId,
+ displayName: L("DisplayName:SecretId"),
+ description: L("Description:SecretId"),
+ isVisibleToClients: false,
+ isEncrypted: true
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ TencentCloudSettingNames.SecretKey,
+ displayName: L("DisplayName:SecretKey"),
+ description: L("Description:SecretKey"),
+ isVisibleToClients: false,
+ isEncrypted: true
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ TencentCloudSettingNames.Connection.HttpMethod,
+ // 默认 post
+ defaultValue: "POST",
+ displayName: L("DisplayName:HttpMethod"),
+ description: L("Description:HttpMethod"),
+ isVisibleToClients: false
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ TencentCloudSettingNames.Connection.Timeout,
+ // 默认 60秒
+ defaultValue: "60",
+ displayName: L("DisplayName:Timeout"),
+ description: L("Description:Timeout"),
+ isVisibleToClients: false
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ TencentCloudSettingNames.Connection.WebProxy,
+ displayName: L("DisplayName:WebProxy"),
+ description: L("Description:WebProxy"),
+ isVisibleToClients: false
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ };
+ }
+
+ private ILocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingNames.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingNames.cs
new file mode 100644
index 000000000..944713db2
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/Settings/TencentCloudSettingNames.cs
@@ -0,0 +1,47 @@
+namespace LINGYUN.Abp.Tencent.Settings;
+
+public static class TencentCloudSettingNames
+{
+ public const string Prefix = "Abp.TencentCloud";
+ ///
+ /// SecretId
+ ///
+ public const string SecretId = Prefix + ".SecretId";
+ ///
+ /// SecretKey
+ ///
+ public const string SecretKey = Prefix + ".SecretKey";
+ ///
+ /// 连接地域
+ ///
+ public const string EndPoint = Prefix + ".EndPoint";
+ ///
+ /// 会话持续时间
+ ///
+ public const string DurationSecond = Prefix + ".DurationSecond";
+ ///
+ /// 连接设置
+ ///
+ public class Connection
+ {
+ public const string Prefix = TencentCloudSettingNames.Prefix + ".Connection";
+ ///
+ /// 代理服务器
+ ///
+ public const string WebProxy = Prefix + ".WebProxy";
+ ///
+ /// 连接地域
+ /// 按照腾讯云文档,对于金融区服务需要指定域名
+ ///
+ public const string EndPoint = Prefix + ".EndPoint";
+ ///
+ /// Http方法,按照腾讯云文档,不同的方法对于请求大小有限制
+ /// 默认:POST
+ ///
+ public const string HttpMethod = Prefix + ".HttpMethod";
+ ///
+ /// 超时时间
+ ///
+ public const string Timeout = Prefix + ".Timeout";
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientCacheItem.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientCacheItem.cs
new file mode 100644
index 000000000..16a353e99
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientCacheItem.cs
@@ -0,0 +1,21 @@
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.Tencent;
+
+public class TencentCloudClientCacheItem
+{
+ public const string CacheKeyFormat = "pn:{0},n:tenant-cloud";
+ public string SecretId { get; set; }
+ public string SecretKey { get; set; }
+ public string EndPoint { get; set; }
+ public string WebProxy { get; set; }
+ public string ApiEndPoint { get; set; }
+ public string HttpMethod { get; set; }
+ public int Timeout { get; set; }
+ public int DurationSecond { get; set; }
+
+ public static string CalculateCacheKey(ICurrentTenant currentTenant)
+ {
+ return string.Format(CacheKeyFormat, currentTenant.IsAvailable ? currentTenant.GetId().ToString() : "global");
+ }
+}
diff --git a/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientFactory.cs b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientFactory.cs
new file mode 100644
index 000000000..6d9bfd7ba
--- /dev/null
+++ b/aspnet-core/modules/cloud-tencent/LINGYUN.Abp.Tencent/LINYUN/Abp/Tencent/TencentCloudClientFactory.cs
@@ -0,0 +1,55 @@
+using Microsoft.Extensions.Caching.Memory;
+using System.Linq;
+using TencentCloud.Common;
+using TencentCloud.Common.Profile;
+using Volo.Abp;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Tencent;
+
+public class TencentCloudClientFactory : AbstractTencentCloudClientFactory
+{
+ public TencentCloudClientFactory(
+ IMemoryCache clientCache,
+ ICurrentTenant currentTenant,
+ ISettingProvider settingProvider)
+ : base(clientCache, currentTenant, settingProvider)
+ {
+ }
+
+ protected override TClient CreateClient(TencentCloudClientCacheItem cloudCache)
+ {
+ var clientCtr = typeof(TClient)
+ .GetConstructors()
+ .Where(x => x.GetParameters().Length == 3)
+ .FirstOrDefault();
+ if (clientCtr != null)
+ {
+ var cred = new Credential
+ {
+ SecretId = cloudCache.SecretId,
+ SecretKey = cloudCache.SecretKey,
+ };
+
+ var httpProfile = new HttpProfile
+ {
+ ReqMethod = cloudCache.HttpMethod ?? "POST",
+ Timeout = cloudCache.Timeout,
+ // 不同的api需要的区域不同, 有的
+ Endpoint = cloudCache.ApiEndPoint,
+ WebProxy = cloudCache.WebProxy,
+ };
+ var clientProfile = new ClientProfile
+ {
+ HttpProfile = httpProfile,
+ };
+
+ // 通过反射创建客户端实例
+ // TODO: 如果影响到性能需要调整到通过Options手动创建实例
+ return (TClient)clientCtr.Invoke(new object[] { cred, cloudCache.EndPoint, clientProfile });
+ }
+
+ throw new AbpException($"Failed to specify initialization Type for client {typeof(TClient).FullName}. Client instance could not be created");
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs
index 3da8694e0..a8d591496 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs
@@ -1,7 +1,4 @@
using LINGYUN.Abp.Aliyun;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using System.Collections.Generic;
using Volo.Abp.BlobStoring;
using Volo.Abp.Modularity;
@@ -12,24 +9,25 @@ namespace LINGYUN.Abp.BlobStoring.Aliyun
typeof(AbpAliyunModule))]
public class AbpBlobStoringAliyunModule : AbpModule
{
- public override void ConfigureServices(ServiceConfigurationContext context)
- {
- var configuration = context.Services.GetConfiguration();
+ // 需要时引用配置
+ //public override void ConfigureServices(ServiceConfigurationContext context)
+ //{
+ // var configuration = context.Services.GetConfiguration();
- Configure(options =>
- {
- context.Services.ExecutePreConfiguredActions(options);
- options.Containers.ConfigureAll((containerName, containerConfiguration) =>
- {
- containerConfiguration.UseAliyun(aliyun =>
- {
- aliyun.BucketName = configuration[AliyunBlobProviderConfigurationNames.BucketName] ?? "";
- aliyun.CreateBucketIfNotExists = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists).Get();
- aliyun.CreateBucketReferer = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketReferer).Get>();
- aliyun.Endpoint = configuration[AliyunBlobProviderConfigurationNames.Endpoint];
- });
- });
- });
- }
+ // Configure(options =>
+ // {
+ // context.Services.ExecutePreConfiguredActions(options);
+ // options.Containers.ConfigureAll((containerName, containerConfiguration) =>
+ // {
+ // containerConfiguration.UseAliyun(aliyun =>
+ // {
+ // aliyun.BucketName = configuration[AliyunBlobProviderConfigurationNames.BucketName] ?? "";
+ // aliyun.CreateBucketIfNotExists = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists).Get();
+ // aliyun.CreateBucketReferer = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketReferer).Get>();
+ // aliyun.Endpoint = configuration[AliyunBlobProviderConfigurationNames.Endpoint];
+ // });
+ // });
+ // });
+ //}
}
}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs b/aspnet-core/modules/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
index 34d1d9c90..c886b97e4 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
@@ -133,11 +133,17 @@ namespace LINGYUN.Abp.Sms.Aliyun
private void TryAddTemplateParam(CommonRequest request, SmsMessage smsMessage)
{
- if (smsMessage.Properties.Count > 0)
- {
- var queryParamJson = JsonSerializer.Serialize(smsMessage.Properties);
- request.AddQueryParameters("TemplateParam", queryParamJson);
+ // 统一一下模板参数名称
+ if (smsMessage.Properties.TryGetValue("TemplateParam", out var templateParam))
+ {
+ request.AddQueryParameters("TemplateParam", templateParam.ToString());
}
+
+ //if (smsMessage.Properties.Count > 0)
+ //{
+ // var queryParamJson = JsonSerializer.Serialize(smsMessage.Properties);
+ // request.AddQueryParameters("TemplateParam", queryParamJson);
+ //}
}
}
}
diff --git a/aspnet-core/modules/mvc/LINGYUN.Abp.AspNetCore.Mvc.Wrapper/Microsoft/AspNetCore/Cors/AbpCorsPolicyBuilderExtensions.cs b/aspnet-core/modules/mvc/LINGYUN.Abp.AspNetCore.Mvc.Wrapper/Microsoft/AspNetCore/Cors/AbpCorsPolicyBuilderExtensions.cs
new file mode 100644
index 000000000..e1f7b185c
--- /dev/null
+++ b/aspnet-core/modules/mvc/LINGYUN.Abp.AspNetCore.Mvc.Wrapper/Microsoft/AspNetCore/Cors/AbpCorsPolicyBuilderExtensions.cs
@@ -0,0 +1,15 @@
+using LINGYUN.Abp.Wrapper;
+using Microsoft.AspNetCore.Cors.Infrastructure;
+
+namespace Microsoft.AspNetCore.Cors;
+
+public static class AbpCorsPolicyBuilderExtensions
+{
+ public static CorsPolicyBuilder WithAbpWrapExposedHeaders(this CorsPolicyBuilder corsPolicyBuilder)
+ {
+ return corsPolicyBuilder
+ .WithExposedHeaders(
+ AbpHttpWrapConsts.AbpWrapResult,
+ AbpHttpWrapConsts.AbpDontWrapResult);
+ }
+}
diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/FodyWeavers.xml b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN.Abp.OssManagement.Tencent.csproj b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN.Abp.OssManagement.Tencent.csproj
new file mode 100644
index 000000000..d0518eb31
--- /dev/null
+++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN.Abp.OssManagement.Tencent.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/AbpOssManagementTencentModule.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/AbpOssManagementTencentModule.cs
new file mode 100644
index 000000000..8c84947e2
--- /dev/null
+++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/AbpOssManagementTencentModule.cs
@@ -0,0 +1,17 @@
+using LINGYUN.Abp.BlobStoring.Tencent;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.OssManagement.Tencent
+{
+ [DependsOn(
+ typeof(AbpBlobStoringTencentCloudModule),
+ typeof(AbpOssManagementDomainModule))]
+ public class AbpOssManagementTencentModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddTransient();
+ }
+ }
+}
diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainer.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainer.cs
new file mode 100644
index 000000000..aa1505ada
--- /dev/null
+++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainer.cs
@@ -0,0 +1,403 @@
+using COSXML;
+using COSXML.Model.Bucket;
+using COSXML.Model.Object;
+using COSXML.Model.Service;
+using LINGYUN.Abp.BlobStoring.Tencent;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Timing;
+
+namespace LINGYUN.Abp.OssManagement.Tencent
+{
+ ///
+ /// Oss容器的阿里云实现
+ ///
+ internal class TencentOssContainer : IOssContainer
+ {
+ protected IClock Clock { get; }
+ protected ICurrentTenant CurrentTenant { get; }
+ protected ICosClientFactory CosClientFactory { get; }
+ public TencentOssContainer(
+ IClock clock,
+ ICurrentTenant currentTenant,
+ ICosClientFactory cosClientFactory)
+ {
+ Clock = clock;
+ CurrentTenant = currentTenant;
+ CosClientFactory = cosClientFactory;
+ }
+ public virtual async Task BulkDeleteObjectsAsync(BulkDeleteObjectRequest request)
+ {
+ var ossClient = await CreateClientAsync();
+
+ var path = GetBasePath(request.Path);
+ var deleteRequest = new DeleteMultiObjectRequest(request.Bucket);
+ deleteRequest.SetObjectKeys(request.Objects.Select(x => x += path).ToList());
+
+ ossClient.DeleteMultiObjects(deleteRequest);
+ }
+
+ public virtual async Task CreateAsync(string name)
+ {
+ var ossClient = await CreateClientAsync();
+
+ if (BucketExists(ossClient, name))
+ {
+ throw new BusinessException(code: OssManagementErrorCodes.ContainerAlreadyExists);
+ }
+
+ var putBucketRequest = new PutBucketRequest(name);
+ var bucketResult = ossClient.PutBucket(putBucketRequest);
+
+ return new OssContainer(
+ bucketResult.Key,
+ Clock.Now,
+ 0L,
+ Clock.Now,
+ new Dictionary
+ {
+ { "Id", bucketResult.Key },
+ { "DisplayName", bucketResult.Key }
+ });
+ }
+
+ public virtual async Task CreateObjectAsync(CreateOssObjectRequest request)
+ {
+ var ossClient = await CreateClientAsync();
+
+ var objectPath = GetBasePath(request.Path);
+
+ var objectName = objectPath.IsNullOrWhiteSpace()
+ ? request.Object
+ : objectPath + request.Object;
+
+ if (!request.Overwrite && ObjectExists(ossClient, request.Bucket, objectName))
+ {
+ throw new BusinessException(code: OssManagementErrorCodes.ObjectAlreadyExists);
+ }
+
+ // 当一个对象名称是以 / 结尾时,不论该对象是否存有数据,都以目录的形式存在
+ // 详情见:https://help.aliyun.com/document_detail/31910.html
+ if (objectName.EndsWith("/") &&
+ request.Content.IsNullOrEmpty())
+ {
+ var emptyStream = new MemoryStream();
+ var emptyData = System.Text.Encoding.UTF8.GetBytes("");
+ await emptyStream.WriteAsync(emptyData, 0, emptyData.Length);
+ request.SetContent(emptyStream);
+ }
+
+ // 没有bucket则创建
+ if (!BucketExists(ossClient, request.Bucket))
+ {
+ var putBucketRequest = new PutBucketRequest(request.Bucket);
+ ossClient.PutBucket(putBucketRequest);
+ }
+
+ var contentLength = request.Content.Length;
+ var putObjectRequest = new PutObjectRequest(request.Bucket, objectName, request.Content);
+
+ var objectResult = ossClient.PutObject(putObjectRequest);
+
+ if (objectResult.IsSuccessful() && request.ExpirationTime.HasValue)
+ {
+ var putBuckerLifeRequest = new PutBucketLifecycleRequest(request.Bucket);
+
+ var rule = new COSXML.Model.Tag.LifecycleConfiguration.Rule();
+ rule.id = "lfiecycleConfigureId";
+ rule.status = "Enabled"; //Enabled,Disabled
+
+ rule.filter = new COSXML.Model.Tag.LifecycleConfiguration.Filter();
+ // TODO: 需要测试
+ rule.filter.prefix = objectName;
+
+ putBuckerLifeRequest.SetRule(rule);
+
+ ossClient.PutBucketLifecycle(putBuckerLifeRequest);
+ }
+
+ var ossObject = new OssObject(
+ !objectPath.IsNullOrWhiteSpace()
+ ? objectName.Replace(objectPath, "")
+ : objectName,
+ objectPath,
+ objectResult.eTag,
+ DateTime.Now,
+ contentLength,
+ DateTime.Now,
+ new Dictionary(),
+ objectName.EndsWith("/") // 名称结尾是 / 符号的则为目录:https://cloud.tencent.com/document/product/436/13324
+ )
+ {
+ FullName = objectName
+ };
+
+ if (!Equals(request.Content, Stream.Null))
+ {
+ request.Content.Seek(0, SeekOrigin.Begin);
+ ossObject.SetContent(request.Content);
+ }
+
+ return ossObject;
+ }
+
+ public virtual async Task DeleteAsync(string name)
+ {
+ // 阿里云oss在控制台设置即可,无需改变
+ var ossClient = await CreateClientAsync();
+
+ if (BucketExists(ossClient, name))
+ {
+ var deleteBucketRequest = new DeleteBucketRequest(name);
+ ossClient.DeleteBucket(deleteBucketRequest);
+ }
+ }
+
+ public virtual async Task DeleteObjectAsync(GetOssObjectRequest request)
+ {
+ var ossClient = await CreateClientAsync();
+
+ var objectPath = GetBasePath(request.Path);
+
+ var objectName = objectPath.IsNullOrWhiteSpace()
+ ? request.Object
+ : objectPath + request.Object;
+
+ if (BucketExists(ossClient, request.Bucket) &&
+ ObjectExists(ossClient, request.Bucket, objectName))
+ {
+ var getBucketRequest = new GetBucketRequest(request.Bucket);
+
+ var getBucketResult = ossClient.GetBucket(getBucketRequest);
+ if (getBucketResult.listBucket.commonPrefixesList.Any() ||
+ getBucketResult.listBucket.contentsList.Any())
+ {
+ throw new BusinessException(code: OssManagementErrorCodes.ObjectDeleteWithNotEmpty);
+ }
+ var deleteObjectRequest = new DeleteObjectRequest(request.Bucket, objectName);
+ ossClient.DeleteObject(deleteObjectRequest);
+ }
+ }
+
+ public virtual async Task ExistsAsync(string name)
+ {
+ var ossClient = await CreateClientAsync();
+
+ return BucketExists(ossClient, name);
+ }
+
+ public virtual async Task GetAsync(string name)
+ {
+ var ossClient = await CreateClientAsync();
+ if (!BucketExists(ossClient, name))
+ {
+ throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound);
+ // throw new ContainerNotFoundException($"Can't not found container {name} in aliyun blob storing");
+ }
+ var getBucketRequest = new GetBucketRequest(name);
+ var bucket = ossClient.GetBucket(getBucketRequest);
+
+ return new OssContainer(
+ bucket.Key,
+ new DateTime(1970, 1, 1, 0, 0, 0), // TODO: 从header获取? 需要测试
+ 0L,
+ null,
+ new Dictionary
+ {
+ { "Id", bucket.Key },
+ { "DisplayName", bucket.Key }
+ });
+ }
+
+ public virtual async Task GetObjectAsync(GetOssObjectRequest request)
+ {
+ var ossClient = await CreateClientAsync();
+ if (!BucketExists(ossClient, request.Bucket))
+ {
+ throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound);
+ // throw new ContainerNotFoundException($"Can't not found container {request.Bucket} in aliyun blob storing");
+ }
+
+ var objectPath = GetBasePath(request.Path);
+ var objectName = objectPath.IsNullOrWhiteSpace()
+ ? request.Object
+ : objectPath + request.Object;
+
+ if (!ObjectExists(ossClient, request.Bucket, objectName))
+ {
+ throw new BusinessException(code: OssManagementErrorCodes.ObjectNotFound);
+ // throw new ContainerNotFoundException($"Can't not found object {objectName} in container {request.Bucket} with aliyun blob storing");
+ }
+
+ var getObjectRequest = new GetObjectBytesRequest(request.Bucket, objectName);
+ if (!request.Process.IsNullOrWhiteSpace())
+ {
+ getObjectRequest.SetQueryParameter(request.Process, null);
+ }
+ var objectResult = ossClient.GetObject(getObjectRequest);
+ var ossObject = new OssObject(
+ !objectPath.IsNullOrWhiteSpace()
+ ? objectResult.Key.Replace(objectPath, "")
+ : objectResult.Key,
+ request.Path,
+ objectResult.eTag,
+ null,
+ objectResult.content.Length,
+ null,
+ new Dictionary(),
+ objectResult.Key.EndsWith("/"))
+ {
+ FullName = objectResult.Key
+ };
+
+ if (objectResult.content.Length > 0)
+ {
+ var memoryStream = new MemoryStream();
+ await memoryStream.WriteAsync(objectResult.content, 0, objectResult.content.Length);
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ ossObject.SetContent(memoryStream);
+ }
+
+ return ossObject;
+ }
+
+ public virtual async Task GetListAsync(GetOssContainersRequest request)
+ {
+ var ossClient = await CreateClientAsync();
+
+ // TODO: 腾讯云直接返回所有列表?
+ var getBucketRequest = new GetServiceRequest();
+ var bucket = ossClient.GetService(getBucketRequest);
+
+ return new GetOssContainersResponse(
+ request.Prefix,
+ request.Marker,
+ null,
+ bucket.listAllMyBuckets.buckets.Count,
+ bucket.listAllMyBuckets.buckets
+ .Select(x => new OssContainer(
+ x.name,
+ DateTime.TryParse(x.createDate, out var time) ? time : new DateTime(1970, 1, 1),
+ 0L,
+ null,
+ new Dictionary
+ {
+ { "Id", x.name },
+ { "DisplayName", x.name }
+ }))
+ .ToList());
+ }
+
+ public virtual async Task GetObjectsAsync(GetOssObjectsRequest request)
+ {
+
+ var ossClient = await CreateClientAsync();
+
+ var objectPath = GetBasePath(request.Prefix);
+ var marker = !objectPath.IsNullOrWhiteSpace() && !request.Marker.IsNullOrWhiteSpace()
+ ? request.Marker.Replace(objectPath, "")
+ : request.Marker;
+
+ // TODO: 阿里云的分页差异需要前端来弥补,传递Marker, 按照Oss控制台的逻辑,直接把MaxKeys设置较大值就行了
+
+ var getBucketRequest = new GetBucketRequest(request.BucketName);
+ getBucketRequest.SetMarker(!marker.IsNullOrWhiteSpace() ? objectPath + marker : marker);
+ getBucketRequest.SetMaxKeys(request.MaxKeys?.ToString() ?? "10");
+ getBucketRequest.SetPrefix(objectPath);
+ getBucketRequest.SetDelimiter(request.Delimiter);
+ getBucketRequest.SetEncodingType(request.EncodingType);
+
+ var getBucketResult = ossClient.GetBucket(getBucketRequest);
+
+ var ossObjects = getBucketResult.listBucket.contentsList
+ .Where(x => !x.key.Equals(objectPath))// 过滤当前的目录返回值
+ .Select(x => new OssObject(
+ !objectPath.IsNullOrWhiteSpace() && !x.key.Equals(objectPath)
+ ? x.key.Replace(objectPath, "")
+ : x.key, // 去除目录名称
+ request.Prefix,
+ x.eTag,
+ DateTime.TryParse(x.lastModified, out var ctime) ? ctime : null,
+ x.size,
+ DateTime.TryParse(x.lastModified, out var mtime) ? mtime : null,
+ new Dictionary
+ {
+ { "Id", x.key },
+ { "DisplayName", x.key }
+ },
+ x.key.EndsWith("/"))
+ {
+ FullName = x.key
+ })
+ .ToList();
+ // 当 Delimiter 为 / 时, objectsResponse.CommonPrefixes 可用于代表层级目录
+ if (getBucketResult.listBucket.commonPrefixesList.Any())
+ {
+ ossObjects.InsertRange(0,
+ getBucketResult.listBucket.commonPrefixesList
+ .Select(x => new OssObject(
+ x.prefix.Replace(objectPath, ""),
+ request.Prefix,
+ "",
+ null,
+ 0L,
+ null,
+ null,
+ true)));
+ }
+ // 排序
+ // TODO: 是否需要客户端来排序
+ ossObjects.Sort(new OssObjectComparer());
+
+ return new GetOssObjectsResponse(
+ getBucketResult.Key,
+ request.Prefix,
+ marker,
+ !objectPath.IsNullOrWhiteSpace() && !getBucketResult.listBucket.nextMarker.IsNullOrWhiteSpace()
+ ? getBucketResult.listBucket.nextMarker.Replace(objectPath, "")
+ : getBucketResult.listBucket.nextMarker,
+ getBucketResult.listBucket.delimiter,
+ getBucketResult.listBucket.maxKeys,
+ ossObjects);
+ }
+
+ protected virtual string GetBasePath(string path)
+ {
+ string objectPath = "";
+ if (CurrentTenant.Id == null)
+ {
+ objectPath += "host/";
+ }
+ else
+ {
+ objectPath += "tenants/" + CurrentTenant.Id.Value.ToString("D");
+ }
+
+ objectPath += path ?? "";
+
+ return objectPath.EnsureEndsWith('/');
+ }
+
+ protected virtual bool BucketExists(CosXml cos, string bucketName)
+ {
+ var request = new DoesBucketExistRequest(bucketName);
+ return cos.DoesBucketExist(request);
+ }
+
+ protected virtual bool ObjectExists(CosXml cos, string bucketName, string objectName)
+ {
+ var request = new DoesObjectExistRequest(bucketName, objectName);
+ return cos.DoesObjectExist(request);
+ }
+
+ protected virtual async Task CreateClientAsync()
+ {
+ return await CosClientFactory.CreateAsync();
+ }
+ }
+}
diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainerFactory.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainerFactory.cs
new file mode 100644
index 000000000..5c64675f0
--- /dev/null
+++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/LINGYUN/Abp/OssManagement/Tencent/TencentOssContainerFactory.cs
@@ -0,0 +1,31 @@
+using LINGYUN.Abp.BlobStoring.Tencent;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Timing;
+
+namespace LINGYUN.Abp.OssManagement.Tencent
+{
+ public class TencentOssContainerFactory : IOssContainerFactory
+ {
+ protected IClock Clock { get; }
+ protected ICurrentTenant CurrentTenant { get; }
+ protected ICosClientFactory CosClientFactory { get; }
+
+ public TencentOssContainerFactory(
+ IClock clock,
+ ICurrentTenant currentTenant,
+ ICosClientFactory cosClientFactory)
+ {
+ Clock = clock;
+ CurrentTenant = currentTenant;
+ CosClientFactory = cosClientFactory;
+ }
+
+ public IOssContainer Create()
+ {
+ return new TencentOssContainer(
+ Clock,
+ CurrentTenant,
+ CosClientFactory);
+ }
+ }
+}
diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/README.md b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/README.md
new file mode 100644
index 000000000..e46d7a4ba
--- /dev/null
+++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Tencent/README.md
@@ -0,0 +1,15 @@
+# LINGYUN.Abp.OssManagement.Tencent
+
+腾讯云oss容器接口
+
+## 配置使用
+
+模块按需引用
+
+```csharp
+[DependsOn(typeof(AbpOssManagementTencentCloudModule))]
+public class YouProjectModule : AbpModule
+{
+ // other
+}
+```