diff --git a/Bing.All.sln b/Bing.All.sln index 0b100e45a..b1cb0a9ee 100644 --- a/Bing.All.sln +++ b/Bing.All.sln @@ -50,18 +50,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.MiniProfiler", "framework\src\Bing.MiniProfiler\Bing.MiniProfiler.csproj", "{43BE3B85-AA7B-47B5-AC09-EE66B52358EA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_SolutionItems", "_SolutionItems", "{2BA5C677-C0B9-458B-8A6D-D3E74B6F60EA}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitattributes = .gitattributes - .gitignore = .gitignore - common.props = common.props - Directory.Build.targets = Directory.Build.targets - framework.props = framework.props - LICENSE = LICENSE - README.md = README.md - version.dev.props = version.dev.props - version.props = version.props - EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6690993E-6659-451F-907C-A48FE1121D40}" ProjectSection(SolutionItems) = preProject @@ -89,9 +77,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-admin", "01-admin", "{BDBABE46-FAAE-459D-BC78-B5BA6503EBBA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-tests", "02-tests", "{5C7976B4-C243-41B9-8303-8E8FAE099D31}" - ProjectSection(SolutionItems) = preProject - framework\tests\Directory.Build.props = framework\tests\Directory.Build.props - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.AutoMapper.Tests", "framework\tests\Bing.AutoMapper.Tests\Bing.AutoMapper.Tests.csproj", "{741EE94D-5F03-426E-9D34-CBDA41671742}" EndProject @@ -111,8 +96,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Ddd.Application", "fra EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Ddd.Application.Contracts", "framework\src\Bing.Ddd.Application.Contracts\Bing.Ddd.Application.Contracts.csproj", "{577D3EA3-5342-4E0D-AE51-39CBEE217089}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Validation.Abstractions", "framework\src\Bing.Validation.Abstractions\Bing.Validation.Abstractions.csproj", "{DB0D25EE-7489-4DD1-8CF9-A8F839B58446}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Auditing", "framework\src\Bing.Auditing\Bing.Auditing.csproj", "{F084AFFB-9136-43CE-874C-777DBA4D5205}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Emailing", "framework\src\Bing.Emailing\Bing.Emailing.csproj", "{5D81881D-FF17-4902-8BEB-A1D58D0EF688}" @@ -190,6 +173,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{D77F42E0 ProjectSection(SolutionItems) = preProject asset\props\misc.props = asset\props\misc.props asset\props\package.props = asset\props\package.props + asset\props\sourcelink.env.props = asset\props\sourcelink.env.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Admin.Data.FreeSQL", "modules\admin\src\Bing.Admin.Data.FreeSQL\Bing.Admin.Data.FreeSQL.csproj", "{05DFA67F-C861-48F9-BBD9-7777972A918F}" @@ -242,6 +226,45 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Logging.Tests", "frame EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Logging.Serilog.Tests", "framework\tests\Bing.Logging.Serilog.Tests\Bing.Logging.Serilog.Tests.csproj", "{857BF156-74BE-4D71-8814-A1ECAA958C2D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Validation.Abstractions", "framework\src\Bing.Validation.Abstractions\Bing.Validation.Abstractions.csproj", "{19A3A852-6F7B-4864-8F53-AE91A04D0732}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Aop.AspectCore", "framework\src\Bing.Aop.AspectCore\Bing.Aop.AspectCore.csproj", "{E7D1CB89-9657-44EC-8531-4C1B956D190E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-tests", "02-tests", "{FA7995A9-43C6-405A-B924-E4F182C245C2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bing.Biz.Payments.Tests", "components\tests\Bing.Biz.Payments.Tests\Bing.Biz.Payments.Tests.csproj", "{F41CF4E3-DB29-42C2-8999-39880376D862}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "images", "images", "{3EE7B5A9-FF9B-4786-9E0F-AFD2BED68CC2}" + ProjectSection(SolutionItems) = preProject + asset\images\icon.png = asset\images\icon.png + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{7ABB7FBC-A1B9-432F-BCC5-53B9FD0172D8}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{4A87040E-99A6-4A3F-BD6D-D2A3C3663012}" + ProjectSection(SolutionItems) = preProject + framework\Publish.bat = framework\Publish.bat + framework\UnPublish.bat = framework\UnPublish.bat + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution", "solution", "{B6E2B626-F561-4B47-8CFE-E8A305696522}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + common.props = common.props + common.tests.props = common.tests.props + framework.props = framework.props + LICENSE = LICENSE + version.dev.props = version.dev.props + version.props = version.props + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bing.Uow", "framework\src\Bing.Uow\Bing.Uow.csproj", "{659C6450-6A39-44A3-A1A5-449ECAA1AFBE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -396,10 +419,6 @@ Global {577D3EA3-5342-4E0D-AE51-39CBEE217089}.Debug|Any CPU.Build.0 = Debug|Any CPU {577D3EA3-5342-4E0D-AE51-39CBEE217089}.Release|Any CPU.ActiveCfg = Release|Any CPU {577D3EA3-5342-4E0D-AE51-39CBEE217089}.Release|Any CPU.Build.0 = Release|Any CPU - {DB0D25EE-7489-4DD1-8CF9-A8F839B58446}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB0D25EE-7489-4DD1-8CF9-A8F839B58446}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB0D25EE-7489-4DD1-8CF9-A8F839B58446}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB0D25EE-7489-4DD1-8CF9-A8F839B58446}.Release|Any CPU.Build.0 = Release|Any CPU {F084AFFB-9136-43CE-874C-777DBA4D5205}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F084AFFB-9136-43CE-874C-777DBA4D5205}.Debug|Any CPU.Build.0 = Debug|Any CPU {F084AFFB-9136-43CE-874C-777DBA4D5205}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -552,6 +571,22 @@ Global {857BF156-74BE-4D71-8814-A1ECAA958C2D}.Debug|Any CPU.Build.0 = Debug|Any CPU {857BF156-74BE-4D71-8814-A1ECAA958C2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {857BF156-74BE-4D71-8814-A1ECAA958C2D}.Release|Any CPU.Build.0 = Release|Any CPU + {19A3A852-6F7B-4864-8F53-AE91A04D0732}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19A3A852-6F7B-4864-8F53-AE91A04D0732}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19A3A852-6F7B-4864-8F53-AE91A04D0732}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19A3A852-6F7B-4864-8F53-AE91A04D0732}.Release|Any CPU.Build.0 = Release|Any CPU + {E7D1CB89-9657-44EC-8531-4C1B956D190E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7D1CB89-9657-44EC-8531-4C1B956D190E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7D1CB89-9657-44EC-8531-4C1B956D190E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7D1CB89-9657-44EC-8531-4C1B956D190E}.Release|Any CPU.Build.0 = Release|Any CPU + {F41CF4E3-DB29-42C2-8999-39880376D862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F41CF4E3-DB29-42C2-8999-39880376D862}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F41CF4E3-DB29-42C2-8999-39880376D862}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F41CF4E3-DB29-42C2-8999-39880376D862}.Release|Any CPU.Build.0 = Release|Any CPU + {659C6450-6A39-44A3-A1A5-449ECAA1AFBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {659C6450-6A39-44A3-A1A5-449ECAA1AFBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {659C6450-6A39-44A3-A1A5-449ECAA1AFBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {659C6450-6A39-44A3-A1A5-449ECAA1AFBE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -599,7 +634,6 @@ Global {25107D91-1E7D-422C-8B03-B1C94EE3A1B5} = {AED4D92B-5196-47E9-BD2C-84D4077F5A70} {CAC738F2-8F95-4940-90D2-76DABF403581} = {AED4D92B-5196-47E9-BD2C-84D4077F5A70} {577D3EA3-5342-4E0D-AE51-39CBEE217089} = {AED4D92B-5196-47E9-BD2C-84D4077F5A70} - {DB0D25EE-7489-4DD1-8CF9-A8F839B58446} = {472FFE79-B8B1-41A1-91F2-CFA0F8406BC9} {F084AFFB-9136-43CE-874C-777DBA4D5205} = {AED4D92B-5196-47E9-BD2C-84D4077F5A70} {5D81881D-FF17-4902-8BEB-A1D58D0EF688} = {B8142AAE-3792-44C0-9197-0F14A9749E33} {256EB128-60DC-406C-BED0-9D0013A6B49A} = {052D4342-8B65-4836-A9B6-681A7812760E} @@ -659,6 +693,15 @@ Global {C7E17279-AAEB-4A58-BBBB-AF443F924D9E} = {33F6CAB3-FD8A-4B91-9B94-A1E2DBC2759B} {73871EF9-9CF6-479F-BC1C-AD443CE037AE} = {5C7976B4-C243-41B9-8303-8E8FAE099D31} {857BF156-74BE-4D71-8814-A1ECAA958C2D} = {5C7976B4-C243-41B9-8303-8E8FAE099D31} + {19A3A852-6F7B-4864-8F53-AE91A04D0732} = {C6B2743E-B7EE-418C-9F9B-79976B71C46B} + {E7D1CB89-9657-44EC-8531-4C1B956D190E} = {C6B2743E-B7EE-418C-9F9B-79976B71C46B} + {FA7995A9-43C6-405A-B924-E4F182C245C2} = {8E2070BF-F6CF-47CC-8DB8-56B74EDFA7EB} + {F41CF4E3-DB29-42C2-8999-39880376D862} = {FA7995A9-43C6-405A-B924-E4F182C245C2} + {3EE7B5A9-FF9B-4786-9E0F-AFD2BED68CC2} = {F40A8736-2FEC-446A-99FD-3660729893FC} + {7ABB7FBC-A1B9-432F-BCC5-53B9FD0172D8} = {2BA5C677-C0B9-458B-8A6D-D3E74B6F60EA} + {4A87040E-99A6-4A3F-BD6D-D2A3C3663012} = {2BA5C677-C0B9-458B-8A6D-D3E74B6F60EA} + {B6E2B626-F561-4B47-8CFE-E8A305696522} = {2BA5C677-C0B9-458B-8A6D-D3E74B6F60EA} + {659C6450-6A39-44A3-A1A5-449ECAA1AFBE} = {AED4D92B-5196-47E9-BD2C-84D4077F5A70} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C1202A0F-83CC-4602-BCE5-20CB640BCAD4} diff --git a/Directory.Build.targets b/Directory.Build.targets deleted file mode 100644 index 564bbc920..000000000 --- a/Directory.Build.targets +++ /dev/null @@ -1,16 +0,0 @@ - - - - true - latest - - NU5125;$(NoWarn) - - - - - - - diff --git a/asset/props/sourcelink.env.props b/asset/props/sourcelink.env.props new file mode 100644 index 000000000..ac94a6d78 --- /dev/null +++ b/asset/props/sourcelink.env.props @@ -0,0 +1,17 @@ + + + + + 2.2.0 + 2.2.0 + 2.2.0 + 2.2.0 + 2.2.0 + 2.2.0 + 3.1.0 + 3.1.0 + 3.1.0 + + + + \ No newline at end of file diff --git a/common.props b/common.props index 6ea08493b..9bd180eac 100644 --- a/common.props +++ b/common.props @@ -1,9 +1,9 @@ - latest - $(NoWarn);CS1591 + $(NoWarn);NETSDK1138 + $(NoWarn);CS1591;NETSDK1138 @@ -22,6 +22,8 @@ $(AssemblyName).xml + + diff --git a/common.tests.props b/common.tests.props new file mode 100644 index 000000000..6745a5ea3 --- /dev/null +++ b/common.tests.props @@ -0,0 +1,23 @@ + + + latest + $(NoWarn);CS1591 + false + + + + + + + + 2.4.5 + 2.4.3 + 2.4.3 + 2.4.3 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + diff --git a/components/src/Bing.Biz.OAuthLogin/Bing.Biz.OAuthLogin.csproj b/components/src/Bing.Biz.OAuthLogin/Bing.Biz.OAuthLogin.csproj index 480e0e4a0..2e5ae873c 100644 --- a/components/src/Bing.Biz.OAuthLogin/Bing.Biz.OAuthLogin.csproj +++ b/components/src/Bing.Biz.OAuthLogin/Bing.Biz.OAuthLogin.csproj @@ -7,12 +7,13 @@ Bing是一个.net core平台下的应用框架,旨在于提升小型团队的 - + + diff --git a/components/src/Bing.Biz.OAuthLogin/Core/AccessTokenParam.cs b/components/src/Bing.Biz.OAuthLogin/Core/AccessTokenParam.cs index 8de6bc553..11c26db03 100644 --- a/components/src/Bing.Biz.OAuthLogin/Core/AccessTokenParam.cs +++ b/components/src/Bing.Biz.OAuthLogin/Core/AccessTokenParam.cs @@ -1,14 +1,14 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Bing.Exceptions; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.OAuthLogin.Core { /// /// 访问令牌参数 /// - public class AccessTokenParam : IValidation + public class AccessTokenParam : IVerifyModel { /// /// 授权类型 @@ -31,7 +31,7 @@ public class AccessTokenParam : IValidation /// /// 验证 /// - public virtual ValidationResultCollection Validate() + public virtual IValidationResult Validate() { var result = DataAnnotationValidation.Validate(this); if (result.IsValid) diff --git a/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationConfigBase.cs b/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationConfigBase.cs index 3ea3930a2..10734ff43 100644 --- a/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationConfigBase.cs +++ b/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationConfigBase.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Bing.Exceptions; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.OAuthLogin.Core { diff --git a/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationParamBase.cs b/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationParamBase.cs index 83ec53c1e..1fe6dde43 100644 --- a/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationParamBase.cs +++ b/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationParamBase.cs @@ -3,14 +3,14 @@ using System.Linq; using Bing.Exceptions; using Bing.ObjectMapping; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.OAuthLogin.Core { /// /// 授权参数基类 /// - public class AuthorizationParamBase : IValidation + public class AuthorizationParamBase : IVerifyModel { /// /// 授权类型 @@ -45,7 +45,7 @@ private void InitState() /// /// 验证 /// - public virtual ValidationResultCollection Validate() + public virtual IValidationResult Validate() { var result = DataAnnotationValidation.Validate(this); if (result.IsValid) diff --git a/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationUserParamBase.cs b/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationUserParamBase.cs index 285fb2563..3ca6749ba 100644 --- a/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationUserParamBase.cs +++ b/components/src/Bing.Biz.OAuthLogin/Core/AuthorizationUserParamBase.cs @@ -2,14 +2,14 @@ using System.Linq; using Bing.Exceptions; using Bing.ObjectMapping; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.OAuthLogin.Core { /// /// 授权用户参数基类 /// - public class AuthorizationUserParamBase : IValidation + public class AuthorizationUserParamBase : IVerifyModel { /// /// 访问令牌 @@ -20,7 +20,7 @@ public class AuthorizationUserParamBase : IValidation /// /// 验证 /// - public ValidationResultCollection Validate() + public IValidationResult Validate() { var result = DataAnnotationValidation.Validate(this); if (result.IsValid) diff --git a/components/src/Bing.Biz.OAuthLogin/MeiliShuo/Configs/MeiliShuoAuthorizationConfig.cs b/components/src/Bing.Biz.OAuthLogin/MeiliShuo/Configs/MeiliShuoAuthorizationConfig.cs index cfdddcd3f..3c44e66c2 100644 --- a/components/src/Bing.Biz.OAuthLogin/MeiliShuo/Configs/MeiliShuoAuthorizationConfig.cs +++ b/components/src/Bing.Biz.OAuthLogin/MeiliShuo/Configs/MeiliShuoAuthorizationConfig.cs @@ -12,18 +12,18 @@ public class MeiliShuoAuthorizationConfig : AuthorizationConfigBase /// 应用标识 /// [Required(ErrorMessage = "应用标识[AppId]不能为空")] - public string AppId { get; set; } + public override string AppId { get; set; } /// /// 应用密钥 /// [Required(ErrorMessage = "应用密钥[AppKey]不能为空")] - public string AppKey { get; set; } + public override string AppKey { get; set; } /// /// 回调地址 /// [Required(ErrorMessage = "回调地址[CallbackUrl]不能为空")] - public string CallbackUrl { get; set; } + public override string CallbackUrl { get; set; } } } diff --git a/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayAppPayService.cs b/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayAppPayService.cs index 72c83dcbb..46943a784 100644 --- a/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayAppPayService.cs +++ b/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayAppPayService.cs @@ -12,7 +12,6 @@ public interface IAlipayAppPayService /// 支付 /// /// 支付参数 - /// Task PayAsync(AlipayAppPayRequest request); } } diff --git a/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayReturnService.cs b/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayReturnService.cs index 2ada0f423..225c26160 100644 --- a/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayReturnService.cs +++ b/components/src/Bing.Biz.Payments/Alipay/Abstractions/IAlipayReturnService.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.Payments.Alipay.Abstractions { diff --git a/components/src/Bing.Biz.Payments/Alipay/Configs/AlipayConfig.cs b/components/src/Bing.Biz.Payments/Alipay/Configs/AlipayConfig.cs index aab8a9b8c..5f2f23ec0 100644 --- a/components/src/Bing.Biz.Payments/Alipay/Configs/AlipayConfig.cs +++ b/components/src/Bing.Biz.Payments/Alipay/Configs/AlipayConfig.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Bing.Exceptions; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.Payments.Alipay.Configs { @@ -19,7 +19,7 @@ public class AlipayConfig /// /// 应用标识 /// - [Required(ErrorMessage = "应用标识[AppId]不能为空")] + [Required(ErrorMessage = "应用标识[GetAppId]不能为空")] public string AppId { get; set; } /// diff --git a/components/src/Bing.Biz.Payments/Alipay/Services/AlipayNotifyService.cs b/components/src/Bing.Biz.Payments/Alipay/Services/AlipayNotifyService.cs index adbd8b417..ba4c6c1bf 100644 --- a/components/src/Bing.Biz.Payments/Alipay/Services/AlipayNotifyService.cs +++ b/components/src/Bing.Biz.Payments/Alipay/Services/AlipayNotifyService.cs @@ -5,6 +5,7 @@ using Bing.Extensions; using Bing.Parameters; using Bing.Utils.Parameters; +using Bing.Validation; using Bing.Validations; namespace Bing.Biz.Payments.Alipay.Services diff --git a/components/src/Bing.Biz.Payments/Alipay/Services/Base/AlipayNotifyServiceBase.cs b/components/src/Bing.Biz.Payments/Alipay/Services/Base/AlipayNotifyServiceBase.cs index 681e0a0e2..6c0fdafba 100644 --- a/components/src/Bing.Biz.Payments/Alipay/Services/Base/AlipayNotifyServiceBase.cs +++ b/components/src/Bing.Biz.Payments/Alipay/Services/Base/AlipayNotifyServiceBase.cs @@ -7,7 +7,7 @@ using Bing.Logs; using Bing.Utils.Parameters; using Bing.Utils.Signatures; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.Payments.Alipay.Services.Base { diff --git a/components/src/Bing.Biz.Payments/Bing.Biz.Payments.csproj b/components/src/Bing.Biz.Payments/Bing.Biz.Payments.csproj index 45967cd5c..0031726b5 100644 --- a/components/src/Bing.Biz.Payments/Bing.Biz.Payments.csproj +++ b/components/src/Bing.Biz.Payments/Bing.Biz.Payments.csproj @@ -1,35 +1,14 @@  - - - netstandard2.0 - Bing.Biz.Payments是Bing应用框架的支付操作类库。 -Bing是一个.net core平台下的应用框架,旨在于提升小型团队的开发能力,由常用公共操作类、架构基类、第三方组件封装、第三方业务接口封装等组成。 - - + - - - + - - - - - + True - True - PayResource.resx - - - ResXFileCodeGenerator - PayResource.Designer.cs - - - diff --git a/components/src/Bing.Biz.Payments/Core/DownloadBillParam.cs b/components/src/Bing.Biz.Payments/Core/DownloadBillParam.cs new file mode 100644 index 000000000..195466d86 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Core/DownloadBillParam.cs @@ -0,0 +1,23 @@ +namespace Bing.Biz.Payments.Core +{ + /// + /// 下载账单参数 + /// + public class DownloadBillParam: DownloadBillParamBase + { + /// + /// 账单类型 + /// + public string BillType { get; set; } + + /// + /// 资金账户类型 + /// + public string AccountType { get; set; } + + /// + /// 压缩账单 + /// + public string TarType { get; set; } + } +} diff --git a/components/src/Bing.Biz.Payments/Core/DownloadBillParamBase.cs b/components/src/Bing.Biz.Payments/Core/DownloadBillParamBase.cs new file mode 100644 index 000000000..aa6755f1c --- /dev/null +++ b/components/src/Bing.Biz.Payments/Core/DownloadBillParamBase.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using Bing.Exceptions; +using Bing.ObjectMapping; +using Bing.Validation; + +namespace Bing.Biz.Payments.Core +{ + /// + /// 下载账单参数基类 + /// + public class DownloadBillParamBase : IVerifyModel + { + /// + /// 账单日期 + /// + public DateTime BillDate { get; set; } + + /// + /// 验证 + /// + public virtual IValidationResult Validate() + { + var result = DataAnnotationValidation.Validate(this); + if (result.IsValid) + return ValidationResultCollection.Success; + throw new Warning(result.First().ErrorMessage); + } + + /// + /// 转换为下载账单参数 + /// + public virtual DownloadBillParam ToParam() => this.MapTo(); + } +} diff --git a/components/src/Bing.Biz.Payments/Core/DownloadBillWay.cs b/components/src/Bing.Biz.Payments/Core/DownloadBillWay.cs new file mode 100644 index 000000000..09f24d2a0 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Core/DownloadBillWay.cs @@ -0,0 +1,13 @@ +namespace Bing.Biz.Payments.Core +{ + /// + /// 下载对账单方式 + /// + public enum DownloadBillWay + { + WechatAllBill, + WechatSuccessBill, + WechatRefundBill, + WechatRechargeRefund, + } +} diff --git a/components/src/Bing.Biz.Payments/Core/INotifyService.cs b/components/src/Bing.Biz.Payments/Core/INotifyService.cs index bcaa32997..7895f046c 100644 --- a/components/src/Bing.Biz.Payments/Core/INotifyService.cs +++ b/components/src/Bing.Biz.Payments/Core/INotifyService.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.Payments.Core { @@ -28,7 +28,6 @@ public interface INotifyService /// 获取参数 /// /// 参数名 - /// string GetParam(string name); /// @@ -36,31 +35,26 @@ public interface INotifyService /// /// 类型 /// 参数名 - /// T GetParam(string name); /// /// 获取参数集合 /// - /// IDictionary GetParams(); /// /// 验证 /// - /// Task ValidateAsync(); /// /// 返回成功消息 /// - /// string Success(); /// /// 返回失败消息 /// - /// string Fail(); } } diff --git a/components/src/Bing.Biz.Payments/Core/PayParamBase.cs b/components/src/Bing.Biz.Payments/Core/PayParamBase.cs index 81d72f2f6..9ae1b76a9 100644 --- a/components/src/Bing.Biz.Payments/Core/PayParamBase.cs +++ b/components/src/Bing.Biz.Payments/Core/PayParamBase.cs @@ -4,14 +4,14 @@ using Bing.Exceptions; using Bing.Extensions; using Bing.ObjectMapping; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.Payments.Core { /// /// 支付参数基类 /// - public class PayParamBase : IValidation + public class PayParamBase : IVerifyModel { /// /// 订单标题 @@ -51,7 +51,7 @@ private void InitSubject() /// /// 验证 /// - public virtual ValidationResultCollection Validate() + public virtual IValidationResult Validate() { ValidateMoney(); var result = DataAnnotationValidation.Validate(this); diff --git a/components/src/Bing.Biz.Payments/Core/PayResult.cs b/components/src/Bing.Biz.Payments/Core/PayResult.cs index 21083e60d..3855b663d 100644 --- a/components/src/Bing.Biz.Payments/Core/PayResult.cs +++ b/components/src/Bing.Biz.Payments/Core/PayResult.cs @@ -5,6 +5,24 @@ /// public class PayResult { + /// + /// 初始化一个类型的实例 + /// + public PayResult() { } + + /// + /// 初始化一个类型的实例 + /// + /// 是否成功 + /// 交易编号 + /// 支付接口返回的原始消息 + public PayResult(bool success, string tradeId, string raw) + { + Success = success; + TradeId = tradeId; + Raw = raw; + } + /// /// 是否成功 /// @@ -34,23 +52,5 @@ public class PayResult /// 请求参数 /// public string Parameter { get; set; } - - /// - /// 初始化一个类型的实例 - /// - public PayResult() { } - - /// - /// 初始化一个类型的实例 - /// - /// 是否成功 - /// 交易编号 - /// 支付接口返回的原始消息 - public PayResult(bool success, string tradeId, string raw) - { - Success = success; - TradeId = tradeId; - Raw = raw; - } } } diff --git a/components/src/Bing.Biz.Payments/Core/PayWay.cs b/components/src/Bing.Biz.Payments/Core/PayWay.cs index bed4ca8fc..fd333a99d 100644 --- a/components/src/Bing.Biz.Payments/Core/PayWay.cs +++ b/components/src/Bing.Biz.Payments/Core/PayWay.cs @@ -38,39 +38,57 @@ public enum PayWay AlipayAppPay, /// - /// 微信App支付 + /// 微信付款码支付 /// - [Description("微信App支付")] - WechatpayAppPay, + /// + /// 用户打开微信钱包-付款码的界面,商户扫码后提交完成支付 + /// + [Description("微信付款码支付")] + WechatpayPaymentCodePay, /// - /// 微信小程序支付 + /// 微信JsApi支付 /// - [Description("微信小程序支付")] - WechatpayMiniProgramPay, + /// + /// 用户通过微信扫码,关注公众号等方式进入商家H5页面,并在微信内调用JsSdk完成支付 + /// + [Description("微信JsApi支付")] + WechatpayJsApiPay, /// - /// 微信电脑网站支付 + /// 微信Native支付 /// - [Description("微信电脑网站支付")] - WechatpayPagePay, + /// + /// 用户打开“微信扫一扫”,扫描商户的二维码后完成付款 + /// + [Description("微信Native支付")] + WechatpayNativePay, /// - /// 微信手机网站支付 + /// 微信App支付 /// - [Description("微信手机网站支付")] - WechatpayWapPay, + /// + /// 商户APP中集成微信SDK,用户点击后跳转到微信内完成支付 + /// + [Description("微信App支付")] + WechatpayAppPay, /// - /// 微信公众号支付 + /// 微信H5支付 /// - [Description("微信公众号支付")] - WechatpayPublicPay, + /// + /// 用户在微信以外的手机浏览器请求微信支付的场景唤醒微信支付 + /// + [Description("微信H5支付")] + WechatpayH5Pay, /// - /// 微信条码支付 + /// 微信小程序支付 /// - [Description("微信条码支付")] - WechatpayBarcodePay, + /// + /// 用户在微信小程序中使用微信支付的场景 + /// + [Description("微信小程序支付")] + WechatpayMiniProgramPay, } } diff --git a/components/src/Bing.Biz.Payments/Core/RefundResult.cs b/components/src/Bing.Biz.Payments/Core/RefundResult.cs new file mode 100644 index 000000000..f0c6bbe74 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Core/RefundResult.cs @@ -0,0 +1,58 @@ +namespace Bing.Biz.Payments.Core +{ + /// + /// 退款结果 + /// + public class RefundResult + { + /// + /// 初始化一个类型的实例 + /// + public RefundResult() + { + } + + /// + /// 初始化一个类型的实例 + /// + /// 是否成功 + /// 交易编号 + /// 支付接口返回的原始消息 + public RefundResult(bool success, string tradeId, string raw) + { + Success = success; + TradeId = tradeId; + Raw = raw; + } + + /// + /// 是否成功 + /// + public bool Success { get; } + + /// + /// 交易编号,外部支付系统的交易流水号 + /// + public string TradeId { get; } + + /// + /// 支付接口返回的原始消息 + /// + public string Raw { get; } + + /// + /// 结果 + /// + public string Result { get; set; } + + /// + /// 消息 + /// + public string Message { get; set; } + + /// + /// 请求参数 + /// + public string Parameter { get; set; } + } +} diff --git a/components/src/Bing.Biz.Payments/Factories/PayFactory.cs b/components/src/Bing.Biz.Payments/Factories/PayFactory.cs index 80ccb5178..b136a48d6 100644 --- a/components/src/Bing.Biz.Payments/Factories/PayFactory.cs +++ b/components/src/Bing.Biz.Payments/Factories/PayFactory.cs @@ -40,7 +40,6 @@ public PayFactory(IAlipayConfigProvider alipayConfigProvider, IWechatpayConfigPr /// 创建支付服务 /// /// 支付方式 - /// public IPayService CreatePayService(PayWay way) { switch (way) @@ -60,23 +59,23 @@ public IPayService CreatePayService(PayWay way) case PayWay.AlipayWapPay: return new AlipayWapPayService(_alipayConfigProvider); + case PayWay.WechatpayPaymentCodePay: + return new WechatpayPaymentCodePayService(_wechatpayConfigProvider); + + case PayWay.WechatpayJsApiPay: + return new WechatpayJsApiPayService(_wechatpayConfigProvider); + + case PayWay.WechatpayNativePay: + return new WechatpayNativePayService(_wechatpayConfigProvider); + case PayWay.WechatpayAppPay: return new WechatpayAppPayService(_wechatpayConfigProvider); case PayWay.WechatpayMiniProgramPay: return new WechatpayMiniProgramPayService(_wechatpayConfigProvider); - case PayWay.WechatpayPagePay: - return new WechatpayPagePayService(_wechatpayConfigProvider); - - case PayWay.WechatpayWapPay: - return new WechatpayWapPayService(_wechatpayConfigProvider); - - case PayWay.WechatpayPublicPay: - return new WechatpayPublicPayService(_wechatpayConfigProvider); - - case PayWay.WechatpayBarcodePay: - return new WechatpayBarcodePayService(_wechatpayConfigProvider); + case PayWay.WechatpayH5Pay: + return new WechatpayH5PayService(_wechatpayConfigProvider); } throw new NotImplementedException(way.Description()); @@ -148,64 +147,36 @@ public IAlipayWapPayService CreateAlipayWapPayService() /// /// 创建微信回调通知服务 /// - /// - public IWechatpayNotifyService CreateWechatpayNotifyService() - { - return new WechatpayNotifyService(_wechatpayConfigProvider); - } + public IWechatpayNotifyService CreateWechatpayNotifyService() => new WechatpayNotifyService(_wechatpayConfigProvider); /// - /// 创建微信App支付服务 + /// 创建微信下载交易账单服务 /// - /// - public IWechatpayAppPayService CreateWechatpayAppPayService() - { - return new WechatpayAppPayService(_wechatpayConfigProvider); - } + public IWechatpayDownloadBillService CreateWechatpayDownloadBillService() => new WechatpayDownloadBillService(_wechatpayConfigProvider); /// - /// 创建微信小程序支付服务 + /// 创建微信付款码支付服务 /// - /// - public IWechatpayMiniProgramPayService CreateWechatpayMiniProgramPayService() - { - return new WechatpayMiniProgramPayService(_wechatpayConfigProvider); - } + public IWechatpayPaymentCodePayService CreateWechatpayBarcodePayService() => new WechatpayPaymentCodePayService(_wechatpayConfigProvider); /// - /// 创建微信电脑网站支付服务 + /// 创建微信JsApi支付服务 /// - /// - public IWechatpayPagePayService CreateWechatpayPagePayService() - { - return new WechatpayPagePayService(_wechatpayConfigProvider); - } + public IWechatpayJsApiPayService CreateWechatpayJsApiPayService() => new WechatpayJsApiPayService(_wechatpayConfigProvider); /// - /// 创建微信手机网站支付服务 + /// 创建微信App支付服务 /// - /// - public IWechatpayWapPayService CreateWechatpayWapPayService() - { - return new WechatpayWapPayService(_wechatpayConfigProvider); - } + public IWechatpayAppPayService CreateWechatpayAppPayService() => new WechatpayAppPayService(_wechatpayConfigProvider); /// - /// 创建微信公众号支付服务 + /// 创建微信H5支付服务 /// - /// - public IWechatpayPublicPayService CreateWechatpayPublicPayService() - { - return new WechatpayPublicPayService(_wechatpayConfigProvider); - } + public IWechatpayH5PayService CreateH5PayService() => new WechatpayH5PayService(_wechatpayConfigProvider); /// - /// 创建微信条码支付服务 + /// 创建微信小程序支付服务 /// - /// - public IWechatpayBarcodePayService CreateWechatpayBarcodePayService() - { - return new WechatpayBarcodePayService(_wechatpayConfigProvider); - } + public IWechatpayMiniProgramPayService CreateWechatpayMiniProgramPayService() => new WechatpayMiniProgramPayService(_wechatpayConfigProvider); } } diff --git a/components/src/Bing.Biz.Payments/IPayFactory.cs b/components/src/Bing.Biz.Payments/IPayFactory.cs index 4c91ebec2..14d0834f8 100644 --- a/components/src/Bing.Biz.Payments/IPayFactory.cs +++ b/components/src/Bing.Biz.Payments/IPayFactory.cs @@ -13,91 +13,76 @@ public interface IPayFactory /// 创建支付服务 /// /// 支付方式 - /// IPayService CreatePayService(PayWay way); /// /// 创建支付宝回调通知服务 /// - /// IAlipayNotifyService CreateAlipayNotifyService(); /// /// 创建支付宝返回服务 /// - /// IAlipayReturnService CreateAlipayReturnService(); /// /// 创建支付宝条码支付服务 /// - /// IAlipayBarcodePayService CreateAlipayBarcodePayService(); /// /// 创建支付宝二维码支付服务 /// - /// IAlipayQrCodePayService CreateAlipayQrCodePayService(); /// /// 创建支付宝App支付服务 /// - /// IAlipayAppPayService CreateAlipayAppPayService(); /// /// 创建支付宝电脑网站支付服务 /// - /// IAlipayPagePayService CreateAlipayPagePayService(); /// /// 创建支付宝手机网站支付服务 /// - /// IAlipayWapPayService CreateAlipayWapPayService(); /// /// 创建微信回调通知服务 /// - /// IWechatpayNotifyService CreateWechatpayNotifyService(); /// - /// 创建微信App支付服务 + /// 创建微信下载交易账单服务 /// - /// - IWechatpayAppPayService CreateWechatpayAppPayService(); + IWechatpayDownloadBillService CreateWechatpayDownloadBillService(); /// - /// 创建微信小程序支付服务 + /// 创建微信付款码支付服务 /// - /// - IWechatpayMiniProgramPayService CreateWechatpayMiniProgramPayService(); + IWechatpayPaymentCodePayService CreateWechatpayBarcodePayService(); /// - /// 创建微信电脑网站支付服务 + /// 创建微信JsApi支付服务 /// - /// - IWechatpayPagePayService CreateWechatpayPagePayService(); + IWechatpayJsApiPayService CreateWechatpayJsApiPayService(); /// - /// 创建微信手机网站支付服务 + /// 创建微信App支付服务 /// - /// - IWechatpayWapPayService CreateWechatpayWapPayService(); + IWechatpayAppPayService CreateWechatpayAppPayService(); /// - /// 创建微信公众号支付服务 + /// 创建微信H5支付服务 /// - /// - IWechatpayPublicPayService CreateWechatpayPublicPayService(); + IWechatpayH5PayService CreateH5PayService(); /// - /// 创建微信条码支付服务 + /// 创建微信小程序支付服务 /// - /// - IWechatpayBarcodePayService CreateWechatpayBarcodePayService(); + IWechatpayMiniProgramPayService CreateWechatpayMiniProgramPayService(); } } diff --git a/components/src/Bing.Biz.Payments/Properties/PayResource.Designer.cs b/components/src/Bing.Biz.Payments/Properties/PayResource.Designer.cs index f92eb0b9f..0107521be 100644 --- a/components/src/Bing.Biz.Payments/Properties/PayResource.Designer.cs +++ b/components/src/Bing.Biz.Payments/Properties/PayResource.Designer.cs @@ -19,7 +19,7 @@ namespace Bing.Biz.Payments.Properties { // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class PayResource { @@ -39,7 +39,7 @@ internal PayResource() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Bing.Biz.Payments.Properties.PayResource", typeof(PayResource).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Properties.PayResource", typeof(PayResource).Assembly); resourceMan = temp; } return resourceMan; @@ -47,8 +47,8 @@ internal PayResource() { } /// - /// 重写当前线程的 CurrentUICulture 属性 - /// 重写当前线程的 CurrentUICulture 属性。 + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayAppPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayAppPayService.cs index 2ceb9a4b2..77f069da9 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayAppPayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayAppPayService.cs @@ -13,7 +13,6 @@ public interface IWechatpayAppPayService /// 支付 /// /// 支付参数 - /// Task PayAsync(WechatpayAppPayRequest request); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayDownloadBillService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayDownloadBillService.cs new file mode 100644 index 000000000..667848a9b --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayDownloadBillService.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; +using Bing.Biz.Payments.Wechatpay.Results; + +namespace Bing.Biz.Payments.Wechatpay.Abstractions +{ + /// + /// 微信支付下载交易账单服务 + /// + public interface IWechatpayDownloadBillService + { + /// + /// 下载对账单 + /// + /// 下载对账单参数 + Task DownloadAsync(WechatpayDownloadBillRequest request); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayWapPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayH5PayService.cs similarity index 65% rename from components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayWapPayService.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayH5PayService.cs index 5812a3771..24ab44f8b 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayWapPayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayH5PayService.cs @@ -5,15 +5,14 @@ namespace Bing.Biz.Payments.Wechatpay.Abstractions { /// - /// 微信手机网站支付服务 + /// 微信H5支付服务 /// - public interface IWechatpayWapPayService + public interface IWechatpayH5PayService { /// /// 支付 /// /// 支付参数 - /// - Task PayAsync(WechatpayWapPayRequest request); + Task PayAsync(WechatpayH5PayRequest request); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayBarcodePayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayJsApiPayService.cs similarity index 65% rename from components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayBarcodePayService.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayJsApiPayService.cs index 78205ce43..f78a17fd3 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayBarcodePayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayJsApiPayService.cs @@ -5,15 +5,14 @@ namespace Bing.Biz.Payments.Wechatpay.Abstractions { /// - /// 微信条码支付服务 + /// 微信JsApi支付服务 /// - public interface IWechatpayBarcodePayService + public interface IWechatpayJsApiPayService { /// /// 支付 /// /// 支付参数 - /// - Task PayAsync(WechatpayBarcodePayRequest request); + Task PayAsync(WechatpayJsApiPayRequest request); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayMiniProgramPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayMiniProgramPayService.cs index 657590bc7..339065355 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayMiniProgramPayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayMiniProgramPayService.cs @@ -13,7 +13,6 @@ public interface IWechatpayMiniProgramPayService /// 支付 /// /// 支付参数 - /// Task PayAsync(WechatpayMiniProgramPayRequest request); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPublicPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayNativePayService.cs similarity index 65% rename from components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPublicPayService.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayNativePayService.cs index 427556087..805c89f22 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPublicPayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayNativePayService.cs @@ -5,15 +5,14 @@ namespace Bing.Biz.Payments.Wechatpay.Abstractions { /// - /// 微信公众号支付服务 + /// 微信扫码支付服务 /// - public interface IWechatpayPublicPayService + public interface IWechatpayNativePayService { /// /// 支付 /// /// 支付参数 - /// - Task PayAsync(WechatpayPublicPayRequest request); + Task PayAsync(WechatpayNativePayRequest request); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPagePayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPaymentCodePayService.cs similarity index 65% rename from components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPagePayService.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPaymentCodePayService.cs index efd2890e3..ab94ad81f 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPagePayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayPaymentCodePayService.cs @@ -5,15 +5,14 @@ namespace Bing.Biz.Payments.Wechatpay.Abstractions { /// - /// 微信电脑网站支付服务 + /// 创建微信付款码支付服务 /// - public interface IWechatpayPagePayService + public interface IWechatpayPaymentCodePayService { /// /// 支付 /// /// 支付参数 - /// - Task PayAsync(WechatpayPagePayRequest request); + Task PayAsync(WechatpayPaymentCodePayRequest request); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayRefundService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayRefundService.cs new file mode 100644 index 000000000..fccea226c --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Abstractions/IWechatpayRefundService.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Core; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; + +namespace Bing.Biz.Payments.Wechatpay.Abstractions +{ + /// + /// 微信退款服务 + /// + public interface IWechatpayRefundService + { + /// + /// 退款 + /// + /// 退款参数 + Task RefundAsync(WechatRefundRequest request); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Configs/IWechatpayConfigProvider.cs b/components/src/Bing.Biz.Payments/Wechatpay/Configs/IWechatpayConfigProvider.cs index f7c83bc54..85485b3b8 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Configs/IWechatpayConfigProvider.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Configs/IWechatpayConfigProvider.cs @@ -1,18 +1,16 @@ using System.Threading.Tasks; -using Bing.Utils.Parameters; namespace Bing.Biz.Payments.Wechatpay.Configs { /// - /// 微信支付配置提供器 + /// 微信支付配置提供程序 /// public interface IWechatpayConfigProvider { /// /// 获取配置 /// - /// 参数管理器 - /// - Task GetConfigAsync(IParameterManager parameterManager = null); + /// 参数 + Task GetConfigAsync(object parameter = null); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfig.cs b/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfig.cs index f78b094fd..96399e2e7 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfig.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfig.cs @@ -3,7 +3,7 @@ using Bing.Biz.Payments.Wechatpay.Enums; using Bing.Exceptions; using Bing.Helpers; -using Bing.Validations; +using Bing.Validation; namespace Bing.Biz.Payments.Wechatpay.Configs { @@ -21,7 +21,7 @@ public class WechatpayConfig /// /// 应用标识 /// - [Required(ErrorMessage = "应用标识[AppId]不能为空")] + [Required(ErrorMessage = "应用标识[GetAppId]不能为空")] public string AppId { get; set; } /// @@ -46,6 +46,16 @@ public class WechatpayConfig /// public string NotifyUrl { get; set; } + /// + /// 证书绝对路径 + /// + public string Certificate { get; set; } + + /// + /// 证书密码 + /// + public string CertificatePassword { get; set; } + /// /// 验证 /// @@ -53,18 +63,32 @@ public void Validate() { var result = DataAnnotationValidation.Validate(this); if (result.IsValid == false) - { throw new Warning(result.First().ErrorMessage); - } } /// /// 获取统一下单地址 /// - /// - public string GetOrderUrl() - { - return Url.Combine(GatewayUrl, "pay/unifiedorder"); - } + public string GetOrderUrl() => Url.Combine(GatewayUrl, "pay/unifiedorder"); + + /// + /// 获取付款码支付地址 + /// + public string GetPaymentCodePayUrl() => Url.Combine(GatewayUrl, "pay/micropay"); + + /// + /// 获取关闭订单地址 + /// + public string GetCloseOrderUrl() => Url.Combine(GatewayUrl, "pay/closeorder"); + + /// + /// 获取退款地址 + /// + public string GetRefundUrl() => Url.Combine(GatewayUrl, "secapi/pay/refund"); + + /// + /// 获取下载对账单地址 + /// + public string GetDownloadBillUrl() => Url.Combine(GatewayUrl, "pay/downloadbill"); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfigProvider.cs b/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfigProvider.cs index a7091ae52..5ba3d4b67 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfigProvider.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConfigProvider.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Bing.Utils.Parameters; namespace Bing.Biz.Payments.Wechatpay.Configs { @@ -25,9 +24,8 @@ public WechatpayConfigProvider(WechatpayConfig config) /// /// 获取配置 /// - /// 参数管理器 - /// - public Task GetConfigAsync(IParameterManager parameterManager = null) + /// 参数 + public Task GetConfigAsync(object parameter = null) { return Task.FromResult(_config); } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConst.cs b/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConst.cs index fe5a893ac..146864514 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConst.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Configs/WechatpayConst.cs @@ -139,5 +139,30 @@ public static class WechatpayConst /// 用户授权码 /// public const string AuthCode = "auth_code"; + + /// + /// 对账单日期 + /// + public const string BillDate = "bill_date"; + + /// + /// 账单类型 + /// + public const string BillType = "bill_type"; + + /// + /// 压缩账单 + /// + public const string TarType = "tar_type"; + + /// + /// 资金账户类型 + /// + public const string AccountType = "account_type"; + + /// + /// 随机字符串 + /// + public const string NonceStr = "nonce_str"; } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Enums/WechatpayAccountType.cs b/components/src/Bing.Biz.Payments/Wechatpay/Enums/WechatpayAccountType.cs new file mode 100644 index 000000000..134a86e54 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Enums/WechatpayAccountType.cs @@ -0,0 +1,28 @@ +using System.ComponentModel; + +namespace Bing.Biz.Payments.Wechatpay.Enums +{ + /// + /// 微信支付资金账户类型 + /// + public enum WechatpayAccountType + { + /// + /// 基本账户 + /// + [Description("Basic")] + Basic, + + /// + /// 运营账户 + /// + [Description("Operation")] + Operation, + + /// + /// 手续费账户 + /// + [Description("Fees")] + Fees, + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Enums/WechatpayBillType.cs b/components/src/Bing.Biz.Payments/Wechatpay/Enums/WechatpayBillType.cs new file mode 100644 index 000000000..4afaa0555 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Enums/WechatpayBillType.cs @@ -0,0 +1,34 @@ +using System.ComponentModel; + +namespace Bing.Biz.Payments.Wechatpay.Enums +{ + /// + /// 微信支付账单类型 + /// + public enum WechatpayBillType + { + /// + /// 所有订单信息,不含充值退款订单 + /// + [Description("ALL")] + All, + + /// + /// 成功支付订单,不含充值退款订单 + /// + [Description("SUCCESS")] + Success, + + /// + /// 退款订单,不含充值退款订单 + /// + [Description("REFUND")] + Refund, + + /// + /// 充值退款订单 + /// + [Description("RECHARGE_REFUND")] + RechargeRefund, + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/IWechatpayParameterBuilder.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/IWechatpayParameterBuilder.cs new file mode 100644 index 000000000..9d51abdfa --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/IWechatpayParameterBuilder.cs @@ -0,0 +1,48 @@ +namespace Bing.Biz.Payments.Wechatpay.Parameters +{ + /// + /// 微信支付参数生成器 + /// + public interface IWechatpayParameterBuilder + { + /// + /// 获取签名 + /// + string GetSign(); + + /// + /// 获取Xml结果,包含签名 + /// + string ToXml(); + + /// + /// 获取Xml结果,不包含签名 + /// + string ToXmlNoContainsSign(); + + /// + /// 获取Json结果,包含签名 + /// + string ToJson(); + } + + /// + /// 微信支付参数生成器 + /// + public interface IWechatpayParameterBuilder : IWechatpayParameterBuilder + where TParameterBuilder : IWechatpayParameterBuilder + { + /// + /// 添加参数 + /// + /// 参数名 + /// 参数值 + TParameterBuilder Add(string name, object value); + + /// + /// 设置签名参数名称 + /// + /// 参数名 + TParameterBuilder SignParamName(string name); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatRefundRequest.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatRefundRequest.cs new file mode 100644 index 000000000..91043d226 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatRefundRequest.cs @@ -0,0 +1,30 @@ +using Bing.Biz.Payments.Core; + +namespace Bing.Biz.Payments.Wechatpay.Parameters.Requests +{ + /// + /// 微信支付退款参数 + /// + public class WechatRefundRequest : PayParamBase + { + /// + /// 退款原因 + /// + public string RefundDescription { get; set; } + + /// + /// 退款金额。单位:元 + /// + public decimal RefundFee { get; set; } + + /// + /// 商户退款单号 + /// + public string RefundId { get; set; } + + /// + /// 微信订单号或商户订单号 + /// + public string TransactionId { get; set; } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayDownloadBillRequest.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayDownloadBillRequest.cs new file mode 100644 index 000000000..199a6840a --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayDownloadBillRequest.cs @@ -0,0 +1,42 @@ +using System; +using Bing.Biz.Payments.Wechatpay.Enums; +using Bing.Exceptions; +using Bing.Extensions; +using Bing.Validation; + +namespace Bing.Biz.Payments.Wechatpay.Parameters.Requests +{ + /// + /// 微信支付下载对账单接口参数 + /// + public class WechatpayDownloadBillRequest : IVerifyModel + { + /// + /// 对账单日期 + /// + public DateTime? BillDate { get; set; } + + /// + /// 账单类型 + /// + public WechatpayBillType BillType { get; set; } = WechatpayBillType.All; + + /// + /// 验证 + /// + public IValidationResult Validate() + { + if (BillDate == null) + throw new Warning("必须设置账单日期"); + return ValidationResultCollection.Success; + } + + /// + /// 获取账单日期 + /// + public string GetBillDate() + { + return BillDate.SafeValue().ToString("yyyyMMdd"); + } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayWapPayRequest.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayH5PayRequest.cs similarity index 74% rename from components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayWapPayRequest.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayH5PayRequest.cs index 778bae0af..bddfcfa30 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayWapPayRequest.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayH5PayRequest.cs @@ -3,9 +3,9 @@ namespace Bing.Biz.Payments.Wechatpay.Parameters.Requests { /// - /// 微信手机网站支付参数 + /// 微信H5支付参数 /// - public class WechatpayWapPayRequest : PayParamBase + public class WechatpayH5PayRequest : PayParamBase { /// /// 附加数据,通知原样返回 diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPublicPayRequest.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayJsApiPayRequest.cs similarity index 80% rename from components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPublicPayRequest.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayJsApiPayRequest.cs index 12ab31475..83c6d733e 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPublicPayRequest.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayJsApiPayRequest.cs @@ -3,9 +3,9 @@ namespace Bing.Biz.Payments.Wechatpay.Parameters.Requests { /// - /// 微信公众号支付参数 + /// 微信JsApi支付参数 /// - public class WechatpayPublicPayRequest : PayParamBase + public class WechatpayJsApiPayRequest : PayParamBase { /// /// 用户标识 diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPagePayRequest.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayNativePayRequest.cs similarity index 74% rename from components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPagePayRequest.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayNativePayRequest.cs index af03c50bc..5baa57fb5 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPagePayRequest.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayNativePayRequest.cs @@ -3,9 +3,9 @@ namespace Bing.Biz.Payments.Wechatpay.Parameters.Requests { /// - /// 微信电脑网站支付参数 + /// 微信扫码支付参数 /// - public class WechatpayPagePayRequest : PayParamBase + public class WechatpayNativePayRequest : PayParamBase { /// /// 附加数据,通知时原样返回 diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayBarcodePayRequest.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPaymentCodePayRequest.cs similarity index 82% rename from components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayBarcodePayRequest.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPaymentCodePayRequest.cs index c617ff3bb..53e27154d 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayBarcodePayRequest.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/Requests/WechatpayPaymentCodePayRequest.cs @@ -5,7 +5,7 @@ namespace Bing.Biz.Payments.Wechatpay.Parameters.Requests /// /// 微信条码支付参数 /// - public class WechatpayBarcodePayRequest : PayParamBase + public class WechatpayPaymentCodePayRequest : PayParamBase { /// /// 用户付款授权码 diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilder.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilder.cs index 5079a7969..c08d67f39 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilder.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilder.cs @@ -1,103 +1,57 @@ -using System.Xml; -using Bing.Biz.Payments.Core; +using Bing.Biz.Payments.Core; using Bing.Biz.Payments.Wechatpay.Configs; -using Bing.Biz.Payments.Wechatpay.Signatures; using Bing.Extensions; using Bing.Helpers; -using Bing.Utils.Parameters; -using Xml = Bing.Helpers.Xml; namespace Bing.Biz.Payments.Wechatpay.Parameters { /// /// 微信支付参数生成器 /// - public class WechatpayParameterBuilder + public class WechatpayParameterBuilder : WechatpayParameterBuilderBase, IWechatpayParameterBuilder { /// - /// 参数生成器 - /// - private readonly ParameterBuilder _builder; - - /// - /// 微信支付配置 + /// 初始化一个类型的实例 /// - public WechatpayConfig Config { get; } + /// 微信支付配置 + public WechatpayParameterBuilder(WechatpayConfig config) : base(config) + { + } /// - /// 初始化一个类型的实例 + /// 初始化 /// - /// 微信支付配置 - public WechatpayParameterBuilder(WechatpayConfig config) + public void Init() { - config.CheckNotNull(nameof(config)); - Config = config; - _builder = new ParameterBuilder(); + this.AppId(Config.AppId) + .MerchantId(Config.MerchantId) + .SignType(Config.SignType.Description()) + .NonceStr(); } /// - /// 初始化 + /// 初始化支付参数 /// /// 支付参数 public void Init(PayParam param) { - param.CheckNotNull(nameof(param)); + param.CheckNull(nameof(param)); param.Init(); - AppId(Config.AppId) - .MerchantId(Config.MerchantId) - .SignType(Config.SignType.Description()) - .Add("nonce_str", Id.Guid()) - .SpbillCreateIp(Web.IP) + SpbillCreateIp(Web.IP) .Body(param.Subject) .OutTradeNo(param.OrderId) .TotalFee(param.Money) .NotifyUrl(param.NotifyUrl) - .Attach(param.Attach) - .OpenId(param.OpenId); - } - - /// - /// 添加参数 - /// - /// 参数名 - /// 参数值 - /// - public WechatpayParameterBuilder Add(string name, string value) - { - _builder.Add(name, value); - return this; - } - - /// - /// 设置应用标识 - /// - /// 应用标识 - /// - public WechatpayParameterBuilder AppId(string appId) - { - _builder.Add(WechatpayConst.AppId, appId); - return this; - } - - /// - /// 设置商户号 - /// - /// 商户号 - /// - public WechatpayParameterBuilder MerchantId(string merchantId) - { - _builder.Add(WechatpayConst.MerchantId, merchantId); - return this; + .Attach(param.Attach); } /// /// 设置标题 /// /// 标题 - /// public WechatpayParameterBuilder Body(string body) { - _builder.Add(WechatpayConst.Body, body); + Add(WechatpayConst.Body, body); return this; } @@ -105,10 +59,9 @@ public WechatpayParameterBuilder Body(string body) /// 设置商户订单号 /// /// 商户订单号 - /// public WechatpayParameterBuilder OutTradeNo(string orderId) { - _builder.Add(WechatpayConst.OutTradeNo, orderId); + Add(WechatpayConst.OutTradeNo, orderId); return this; } @@ -116,10 +69,9 @@ public WechatpayParameterBuilder OutTradeNo(string orderId) /// 设置货币类型 /// /// 货币类型 - /// public WechatpayParameterBuilder FeeType(string feeType) { - _builder.Add(WechatpayConst.FeeType, feeType); + Add(WechatpayConst.FeeType, feeType); return this; } @@ -127,10 +79,9 @@ public WechatpayParameterBuilder FeeType(string feeType) /// 设置总金额 /// /// 总金额。单位:元 - /// public WechatpayParameterBuilder TotalFee(decimal totalFee) { - _builder.Add(WechatpayConst.TotalFee, Conv.ToInt(totalFee * 100)); + Add(WechatpayConst.TotalFee, Conv.ToInt(totalFee * 100)); return this; } @@ -138,10 +89,9 @@ public WechatpayParameterBuilder TotalFee(decimal totalFee) /// 设置回调通知地址 /// /// 回调通知地址 - /// public WechatpayParameterBuilder NotifyUrl(string notifyUrl) { - _builder.Add(WechatpayConst.NotifyUrl, GetNotifyUrl(notifyUrl)); + Add(WechatpayConst.NotifyUrl, GetNotifyUrl(notifyUrl)); return this; } @@ -149,24 +99,15 @@ public WechatpayParameterBuilder NotifyUrl(string notifyUrl) /// 获取回调通知地址 /// /// 回调通知地址 - /// - private string GetNotifyUrl(string notifyUrl) - { - if (notifyUrl.IsEmpty()) - { - return Config.NotifyUrl; - } - return notifyUrl; - } + private string GetNotifyUrl(string notifyUrl) => notifyUrl.IsEmpty() ? Config.NotifyUrl : notifyUrl; /// /// 设置终端IP /// /// 终端IP - /// public WechatpayParameterBuilder SpbillCreateIp(string ip) { - _builder.Add(WechatpayConst.SpbillCreateIp, ip); + Add(WechatpayConst.SpbillCreateIp, ip); return this; } @@ -174,21 +115,9 @@ public WechatpayParameterBuilder SpbillCreateIp(string ip) /// 设置交易类型 /// /// 交易类型 - /// public WechatpayParameterBuilder TradeType(string type) { - _builder.Add(WechatpayConst.TradeType, type); - return this; - } - - /// - /// 设置签名类型 - /// - /// 签名类型 - /// - public WechatpayParameterBuilder SignType(string type) - { - _builder.Add(WechatpayConst.SignType, type); + Add(WechatpayConst.TradeType, type); return this; } @@ -196,10 +125,9 @@ public WechatpayParameterBuilder SignType(string type) /// 设置伙伴标识 /// /// 伙伴标识 - /// public WechatpayParameterBuilder PartnerId(string partnerId) { - _builder.Add(WechatpayConst.PartnerId, partnerId); + Add(WechatpayConst.PartnerId, partnerId); return this; } @@ -207,10 +135,9 @@ public WechatpayParameterBuilder PartnerId(string partnerId) /// 设置时间戳 /// /// 时间戳 - /// public WechatpayParameterBuilder Timestamp(long timestamp = 0) { - _builder.Add(WechatpayConst.Timestamp, timestamp == 0 ? Time.GetUnixTimestamp() : timestamp); + Add(WechatpayConst.Timestamp, timestamp == 0 ? Time.GetUnixTimestamp() : timestamp); return this; } @@ -218,10 +145,9 @@ public WechatpayParameterBuilder Timestamp(long timestamp = 0) /// 设置包 /// /// 包。默认值:"Sign=WXPay" - /// public WechatpayParameterBuilder Package(string package = "Sign=WXPay") { - _builder.Add(WechatpayConst.Package, package); + Add(WechatpayConst.Package, package); return this; } @@ -229,10 +155,9 @@ public WechatpayParameterBuilder Package(string package = "Sign=WXPay") /// 设置附加数据 /// /// 附加数据 - /// public WechatpayParameterBuilder Attach(string attach) { - _builder.Add(WechatpayConst.Attach, attach); + Add(WechatpayConst.Attach, attach); return this; } @@ -240,10 +165,9 @@ public WechatpayParameterBuilder Attach(string attach) /// 设置用户标识 /// /// 用户标识 - /// public WechatpayParameterBuilder OpenId(string openId) { - _builder.Add(WechatpayConst.OpenId, openId); + Add(WechatpayConst.OpenId, openId); return this; } @@ -251,99 +175,11 @@ public WechatpayParameterBuilder OpenId(string openId) /// 设置用户授权码 /// /// 用户授权码 - /// public WechatpayParameterBuilder AuthCode(string code) { - _builder.Add(WechatpayConst.AuthCode, code); + Add(WechatpayConst.AuthCode, code); return this; } - /// - /// 获取Xml结果,包含签名 - /// - /// - public string ToXml() - { - return ToXmlDocument(GetSignBuilder()).OuterXml; - } - - /// - /// 获取Xml文档 - /// - /// 参数生成器 - /// - private XmlDocument ToXmlDocument(ParameterBuilder builder) - { - var xml = new Xml(); - foreach (var param in builder.GetDictionary()) - { - AddNode(xml, param.Key, param.Value); - } - return xml.Document; - } - - /// - /// 添加Xml节点 - /// - /// Xml操作 - /// 键 - /// 值 - private void AddNode(Xml xml, string key, object value) - { - if (key.SafeString().ToLower() == WechatpayConst.TotalFee) - { - xml.AddNode(key, value); - return; - } - - xml.AddCDataNode(value, key); - } - - /// - /// 获取签名的参数生成器 - /// - /// - private ParameterBuilder GetSignBuilder() - { - var builder = new ParameterBuilder(_builder); - builder.Add(WechatpayConst.Sign, GetSign()); - return builder; - } - - /// - /// 获取签名 - /// - /// - public string GetSign() - { - return SignManagerFactory.Create(Config, _builder).Sign(); - } - - /// - /// 获取Xml结果,不包含签名 - /// - /// - public string ToXmlNoContainsSign() - { - return ToXmlDocument(_builder).OuterXml; - } - - /// - /// 获取Json结果,包含签名 - /// - /// - public string ToJson() - { - return GetSignBuilder().ToJson(); - } - - /// - /// 输出结果 - /// - /// - public override string ToString() - { - return ToXml(); - } } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilderBase.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilderBase.cs new file mode 100644 index 000000000..2c5f0ceaa --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilderBase.cs @@ -0,0 +1,138 @@ +using System.Xml; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Signatures; +using Bing.Extensions; +using Bing.Helpers; +using Bing.Utils.Parameters; + +namespace Bing.Biz.Payments.Wechatpay.Parameters +{ + /// + /// 微信支付参数生成器基类 + /// + /// 微信支付参数生成器 + public abstract class WechatpayParameterBuilderBase + where TParameterBuilder : IWechatpayParameterBuilder + { + /// + /// 参数生成器 + /// + private readonly ParameterBuilder _builder; + + /// + /// 签名参数名称 + /// + private string _signName; + + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置 + protected WechatpayParameterBuilderBase(WechatpayConfig config) + { + config.CheckNull(nameof(config)); + Config = config; + _builder = new ParameterBuilder(); + } + + /// + /// 微信支付配置 + /// + public WechatpayConfig Config { get; } + + /// + /// 添加参数 + /// + /// 参数名 + /// 参数值 + public virtual TParameterBuilder Add(string name, object value) + { + _builder.Add(name, value); + return This(); + } + + /// + /// 获取签名 + /// + public virtual string GetSign() => SignManagerFactory.Create(Config, _builder).Sign(); + + /// + /// 设置签名参数名称 + /// + /// 参数名 + public virtual TParameterBuilder SignParamName(string name) + { + _signName = name; + return This(); + } + + /// + /// 获取Xml结果,包含签名 + /// + public virtual string ToXml() => ToXmlDocument(GetSignBuilder()).OuterXml; + + /// + /// 获取Xml文档 + /// + /// 参数生成器 + private XmlDocument ToXmlDocument(ParameterBuilder builder) + { + var xml = new Xml(); + foreach (var param in builder.GetDictionary()) + AddNode(xml, param.Key, param.Value); + return xml.Document; + } + + /// + /// 添加Xml节点 + /// + /// Xml操作 + /// 键 + /// 值 + private void AddNode(Xml xml, string key, object value) + { + if (key.SafeString().ToLower() == WechatpayConst.TotalFee) + { + xml.AddNode(key, value); + return; + } + + xml.AddCDataNode(value, key); + } + + /// + /// 获取签名的参数生成器 + /// + private ParameterBuilder GetSignBuilder() + { + var builder = new ParameterBuilder(_builder); + builder.Add(GetSingName(), GetSign()); + return builder; + } + + /// + /// 获取签名参数名称 + /// + private string GetSingName() => _signName.IsEmpty() ? WechatpayConst.Sign : _signName; + + /// + /// 获取Xml结果,不包含签名 + /// + public virtual string ToXmlNoContainsSign() => ToXmlDocument(_builder).OuterXml; + + /// + /// 获取Json结果,包含签名 + /// + public virtual string ToJson() => GetSignBuilder().ToJson(); + + /// + /// 输出结果 + /// + public override string ToString() => ToXml(); + + /// + /// 返回自身 + /// + private TParameterBuilder This() => (TParameterBuilder)(object)this; + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilderExtensions.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilderExtensions.cs new file mode 100644 index 000000000..adc088d82 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayParameterBuilderExtensions.cs @@ -0,0 +1,91 @@ +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Helpers; + +namespace Bing.Biz.Payments.Wechatpay.Parameters +{ + /// + /// 微信支付参数生成器() 扩展 + /// + public static class WechatpayParameterBuilderExtensions + { + #region AppId(设置应用标识) + + /// + /// 设置应用标识 + /// + /// 微信支付参数生成器 + /// 微信支付参数生成器 + /// 应用标识 + public static TParameterBuilder AppId(this TParameterBuilder builder, string appId) + where TParameterBuilder : IWechatpayParameterBuilder + { + builder.Add(WechatpayConst.AppId, appId); + return builder; + } + + #endregion + + #region MerchantId(设置商户号) + + /// + /// 设置商户号 + /// + /// 微信支付参数生成器 + /// 微信支付参数生成器 + /// 商户号 + public static TParameterBuilder MerchantId(this TParameterBuilder builder, string merchantId) + where TParameterBuilder : IWechatpayParameterBuilder + { + builder.Add(WechatpayConst.MerchantId, merchantId); + return builder; + } + + #endregion + + #region NonceStr(设置随机字符串) + + /// + /// 设置随机字符串 + /// + /// 微信支付参数生成器 + /// 微信支付参数生成器 + public static TParameterBuilder NonceStr(this TParameterBuilder builder) + where TParameterBuilder : IWechatpayParameterBuilder + { + builder.Add(WechatpayConst.NonceStr, Id.Guid()); + return builder; + } + + /// + /// 设置随机字符串 + /// + /// 微信支付参数生成器 + /// 微信支付参数生成器 + /// 随机字符串 + public static TParameterBuilder NonceStr(this TParameterBuilder builder, string nonceStr) + where TParameterBuilder : IWechatpayParameterBuilder + { + builder.Add(WechatpayConst.NonceStr, nonceStr); + return builder; + } + + #endregion + + #region SignType(设置签名类型) + + /// + /// 设置签名类型 + /// + /// 微信支付参数生成器 + /// 微信支付参数生成器 + /// 签名类型 + public static TParameterBuilder SignType(this TParameterBuilder builder, string type) + where TParameterBuilder : IWechatpayParameterBuilder + { + builder.Add(WechatpayConst.SignType, type); + return builder; + } + + #endregion + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayRefundParameterBuilder.cs b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayRefundParameterBuilder.cs new file mode 100644 index 000000000..46677ced6 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Parameters/WechatpayRefundParameterBuilder.cs @@ -0,0 +1,77 @@ +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; +using Bing.Extensions; +using Bing.Helpers; + +namespace Bing.Biz.Payments.Wechatpay.Parameters +{ + /// + /// 微信支付退款参数生成器 + /// + public class WechatpayRefundParameterBuilder : WechatpayParameterBuilder + { + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置 + public WechatpayRefundParameterBuilder(WechatpayConfig config) : base(config) + { + } + + /// + /// 初始化退款参数 + /// + /// 退款参数 + public void Init(WechatRefundRequest param) + { + param.CheckNull(nameof(param)); + RefundFee(param.RefundFee) + .Description(param.RefundDescription) + .RefundId(param.RefundId) + .TransactionId(param.TransactionId) + .NotifyUrl(param.NotifyUrl) + .OutTradeNo(param.OrderId) + .TotalFee(param.Money); + } + + /// + /// 设置退款金额 + /// + /// 退款金额。单位:元 + public WechatpayRefundParameterBuilder RefundFee(decimal refundFee) + { + Add("refund_fee", Conv.ToInt(refundFee * 100)); + return this; + } + + /// + /// 设置微信订单号 + /// + /// 微信订单号 + public WechatpayRefundParameterBuilder TransactionId(string transactionId) + { + Add("transaction_id", transactionId); + return this; + } + + /// + /// 设置商户退款单号 + /// + /// 商户退款单号 + public WechatpayRefundParameterBuilder RefundId(string refundId) + { + Add("out_refund_no", refundId); + return this; + } + + /// + /// 设置退款原因 + /// + /// 退款原因 + public WechatpayRefundParameterBuilder Description(string description) + { + Add("refund_desc", description); + return this; + } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayBillInfo.cs b/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayBillInfo.cs new file mode 100644 index 000000000..c4d38116c --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayBillInfo.cs @@ -0,0 +1,165 @@ +namespace Bing.Biz.Payments.Wechatpay.Results +{ + /// + /// 微信对账单信息 + /// + public class WechatpayBillInfo + { + /// + /// 交易时间 + /// + public string TransactionTime { get; set; } + + /// + /// 公众账号ID + /// + public string AppId { get; set; } + + /// + /// 商户号 + /// + public string MerchantId { get; set; } + + /// + /// 子商户号/特约商户号 + /// + public string SubMerchantId { get; set; } + + /// + /// 设备号 + /// + public string DeviceNumber { get; set; } + + /// + /// 微信订单号 + /// + public string TradeId { get; set; } + + /// + /// 商户订单号 + /// + public string OrderId { get; set; } + + /// + /// 用户标识 + /// + public string OpenId { get; set; } + + /// + /// 交易类型 + /// + /// + /// MICROPAY:付款码支付
+ /// JSAPI:JSAPI支付、小程序支付
+ /// NATIVE:Native支付
+ /// APP:APP支付
+ /// FACE:刷脸支付 + ///
+ public string TradeType { get; set; } + + /// + /// 交易状态 + /// + /// + /// SUCCESS:支付成功,说明该行数据为一笔支付成功的订单
+ /// REFUND:转入退款,说明该行数据为一笔发起退款成功的退款单
+ /// REVOKED:已撤销,说明该行数据为一笔用户支付成功后发起撤销的退款单 + ///
+ public string TradeStatus { get; set; } + + /// + /// 付款银行 + /// + public string Bank { get; set; } + + /// + /// 货币种类 + /// + public string CurrencyType { get; set; } + + /// + /// 总金额/应结订单金额(元) + /// + public decimal TotalAmount { get; set; } + + /// + /// 代金券或立减优惠金额(元) + /// + public decimal CouponAmount { get; set; } + + /// + /// 退款申请时间 + /// + public string RefundApplyTime { get; set; } + + /// + /// 退款成功时间 + /// + public string RefundTime { get; set; } + + /// + /// 微信退款单号 + /// + public string WechatpayRefundId { get; set; } + + /// + /// 商户退款单号 + /// + public string MerchantRefundId { get; set; } + + /// + /// 退款金额(元) + /// + public decimal RefundAmount { get; set; } + + /// + /// 代金券或立减优惠退款金额/充值券退款金额(元) + /// + public decimal CouponRefundAmount { get; set; } + + /// + /// 退款类型 + /// + public string RefundType { get; set; } + + /// + /// 退款状态 + /// + public string RefundStatus { get; set; } + + /// + /// 商品名称 + /// + public string TradeName { get; set; } + + /// + /// 商户数据包 + /// + public string MerchantAttach { get; set; } + + /// + /// 手续费(元) + /// + public decimal Commission { get; set; } + + /// + /// 费率 + /// + public string Rate { get; set; } + + /// + /// 订单金额(元) + /// + public decimal OrderAmount { get; set; } + + /// + /// 申请退款金额(元) + /// + public decimal ApplyRefundAmount { get; set; } + + /// + /// 费率备注 + /// + public string RateRemark { get; set; } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayDownloadBillResult.cs b/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayDownloadBillResult.cs new file mode 100644 index 000000000..7c5c285c3 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayDownloadBillResult.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; + +namespace Bing.Biz.Payments.Wechatpay.Results +{ + /// + /// 微信支付下载对账单结果 + /// + public class WechatpayDownloadBillResult + { + /// + /// 初始化一个类型的实例 + /// + /// 是否成功 + /// 请求结果 + public WechatpayDownloadBillResult(bool success, WechatpayResult result) + { + Success = success; + ErrorCode = result.GetErrorCode(); + ErrorMessage = result.GetErrorCodeDescription(); + Raw = result.Raw; + Parameter = result.Builder.ToString(); + Bills = new List(); + } + + /// + /// 初始化一个类型的实例 + /// + /// 是否成功 + /// 请求参数 + /// 对账单信息列表 + public WechatpayDownloadBillResult(bool success, string parameter, List bills) + { + Success = success; + Parameter = parameter; + Bills = bills ?? new List(); + } + + /// + /// 是否成功 + /// + public bool Success { get; } + + /// + /// 错误码 + /// + public string ErrorCode { get; } + + /// + /// 错误消息 + /// + public string ErrorMessage { get; } + + /// + /// 支付结果返回的原始消息 + /// + public string Raw { get; } + + /// + /// 请求参数 + /// + public string Parameter { get; set; } + + /// + /// 对账单信息列表 + /// + public List Bills { get; set; } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayResult.cs b/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayResult.cs index 3ec8d388d..a887ca543 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayResult.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Results/WechatpayResult.cs @@ -2,11 +2,12 @@ using System.Linq; using System.Threading.Tasks; using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters; using Bing.Biz.Payments.Wechatpay.Signatures; using Bing.Extensions; using Bing.Logs; using Bing.Utils.Parameters; -using Bing.Validations; +using Bing.Validation; using Xml = Bing.Helpers.Xml; namespace Bing.Biz.Payments.Wechatpay.Results @@ -16,11 +17,6 @@ namespace Bing.Biz.Payments.Wechatpay.Results /// public class WechatpayResult { - /// - /// 配置提供者 - /// - private readonly IWechatpayConfigProvider _configProvider; - /// /// 响应结果 /// @@ -31,25 +27,44 @@ public class WechatpayResult /// private string _sign; - /// - /// 微信支付原始响应 - /// - public string Raw { get; } - /// /// 初始化一个类型的实例 /// - /// 配置提供器 + /// 微信支付配置提供程序 /// xml响应消息 - public WechatpayResult(IWechatpayConfigProvider configProvider, string response) + /// 微信支付配置 + /// 微信支付参数生成器 + public WechatpayResult(IWechatpayConfigProvider configProvider, string response, WechatpayConfig config = null, IWechatpayParameterBuilder parameterBuilder = null) { - configProvider.CheckNotNull(nameof(configProvider)); - _configProvider = configProvider; + configProvider.CheckNull(nameof(configProvider)); + ConfigProvider = configProvider; Raw = response; + Config = config; + Builder = parameterBuilder; _builder = new ParameterBuilder(); Resolve(response); } + /// + /// 微信支付原始响应 + /// + public string Raw { get; } + + /// + /// 微信支付配置提供程序 + /// + public IWechatpayConfigProvider ConfigProvider { get; } + + /// + /// 配置 + /// + public WechatpayConfig Config { get; } + + /// + /// 微信支付参数生成器 + /// + public IWechatpayParameterBuilder Builder { get; } + /// /// 解析响应 /// @@ -110,7 +125,7 @@ protected void WriteLog() /// /// 获取业务结果代码 /// - public string GetResultCode() => GetParam(WechatpayConst.ReturnCode); + public string GetResultCode() => GetParam(WechatpayConst.ResultCode); /// /// 获取返回消息 @@ -120,7 +135,7 @@ protected void WriteLog() /// /// 获取应用标识 /// - public string AppId() => GetParam(WechatpayConst.AppId); + public string GetAppId() => GetParam(WechatpayConst.AppId); /// /// 获取商户号 @@ -147,6 +162,11 @@ protected void WriteLog() /// public string GetMWebUrl() => GetParam("mweb_url"); + /// + /// 获取微信退款单号 + /// + public string GetRefundId() => GetParam("refund_id"); + /// /// 获取交易类型 /// @@ -155,12 +175,27 @@ protected void WriteLog() /// /// 获取错误码 /// - public string GetErrorCode() => GetParam(WechatpayConst.ErrorCode); + public string GetErrorCode() + { + var result = GetParam(WechatpayConst.ErrorCode); + if (string.IsNullOrWhiteSpace(result) == false) + return result; + return GetParam("error_code"); + } /// /// 获取错误码和描述 /// - public string GetErrorCodeDesciption() => GetParam(WechatpayConst.ErrorCodeDescription); + public string GetErrorCodeDescription() + { + var result = GetParam(WechatpayConst.ErrorCodeDescription); + if (string.IsNullOrWhiteSpace(result) == false) + return result; + result = GetParam("return_msg"); + if (string.IsNullOrWhiteSpace(result) == false) + return result; + return GetErrorCode(); + } /// /// 获取签名 @@ -183,7 +218,7 @@ public IDictionary GetParams() public async Task ValidateAsync() { if (GetReturnCode() != WechatpayConst.Success || GetResultCode() != WechatpayConst.Success) - return new ValidationResultCollection(GetErrorCodeDesciption()); + return new ValidationResultCollection(GetErrorCodeDescription()); var isValid = await VerifySign(); if (isValid == false) return new ValidationResultCollection("签名失败"); @@ -195,7 +230,7 @@ public async Task ValidateAsync() /// public async Task VerifySign() { - var config = await _configProvider.GetConfigAsync(_builder); + var config = Config ?? await ConfigProvider.GetConfigAsync(_builder); return SignManagerFactory.Create(config, _builder).Verify(GetSign()); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/Base/WechatpayPayServiceBase.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/Base/WechatpayPayServiceBase.cs new file mode 100644 index 000000000..502341181 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/Base/WechatpayPayServiceBase.cs @@ -0,0 +1,90 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Core; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters; +using Bing.Biz.Payments.Wechatpay.Results; + +namespace Bing.Biz.Payments.Wechatpay.Services.Base +{ + /// + /// 微信支付服务 + /// + public abstract class WechatpayPayServiceBase : WechatpayServiceBase, IPayService + { + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置提供器 + protected WechatpayPayServiceBase(IWechatpayConfigProvider configProvider) : base(configProvider) + { + } + + /// + /// 支付 + /// + /// 支付参数 + public virtual async Task PayAsync(PayParam param) + { + var result = await Request(param); + return await CreateResult(result); + } + + /// + /// 创建参数生成器 + /// + /// 微信支付配置 + protected override WechatpayParameterBuilder CreateParameterBuilder(WechatpayConfig config) => new WechatpayParameterBuilder(config); + + /// + /// 配置参数生成器 + /// + /// 微信支付参数生成器 + /// 请求参数 + protected override void ConfigBuilder(WechatpayParameterBuilder builder, PayParam param) + { + builder.Init(); + builder.Init(param); + builder.TradeType(GetTradeType()); + InitBuilder(builder, param); + } + + /// + /// 获取交易类型 + /// + protected abstract string GetTradeType(); + + /// + /// 初始化参数生成器 + /// + /// 微信支付参数生成器 + /// 支付参数 + protected virtual void InitBuilder(WechatpayParameterBuilder builder, PayParam param) { } + + /// + /// 获取接口地址 + /// + /// 微信支付配置 + protected override string GetUrl(WechatpayConfig config) => config.GetOrderUrl(); + + /// + /// 创建支付结果 + /// + /// 微信支付结果 + protected virtual async Task CreateResult(WechatpayResult result) + { + var success = (await result.ValidateAsync()).IsValid; + return new PayResult(success, result.GetPrepayId(), result.Raw) + { + Parameter = result.Builder.ToString(), + Message = result.GetReturnMessage(), + Result = success ? GetResult(result) : null + }; + } + + /// + /// 获取结果 + /// + /// 微信支付结果 + protected virtual string GetResult(WechatpayResult result) => result.GetPrepayId(); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/Base/WechatpayServiceBase.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/Base/WechatpayServiceBase.cs index a202ad9ee..033692de7 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/Base/WechatpayServiceBase.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/Base/WechatpayServiceBase.cs @@ -1,49 +1,53 @@ using System.Threading.Tasks; -using Bing.Biz.Payments.Core; using Bing.Biz.Payments.Wechatpay.Configs; using Bing.Biz.Payments.Wechatpay.Parameters; using Bing.Biz.Payments.Wechatpay.Results; using Bing.Extensions; using Bing.Helpers; using Bing.Logs; +using Bing.Validation; namespace Bing.Biz.Payments.Wechatpay.Services.Base { /// /// 微信支付服务 /// - public abstract class WechatpayServiceBase : IPayService + /// 请求参数类型 + /// 微信支付参数生成器 + public abstract class WechatpayServiceBase + where TRequest : IVerifyModel + where TParameterBuilder : IWechatpayParameterBuilder { /// - /// 配置提供器 + /// 微信支付配置提供程序 /// protected readonly IWechatpayConfigProvider ConfigProvider; /// - /// 是否发送请求 - /// - public bool IsSend { get; set; } = true; - - /// - /// 初始化一个类型的实例 + /// 初始化一个类型的实例 /// - /// 微信支付配置提供器 + /// 微信支付配置提供程序 protected WechatpayServiceBase(IWechatpayConfigProvider configProvider) { - configProvider.CheckNotNull(nameof(configProvider)); + configProvider.CheckNull(nameof(configProvider)); ConfigProvider = configProvider; } /// - /// 支付 + /// 是否发送请求 /// - /// 支付参数 - public virtual async Task PayAsync(PayParam param) + public bool IsSend { get; set; } = true; + + /// + /// 请求 + /// + /// 请求参数 + protected virtual async Task Request(TRequest param) { - var config = await ConfigProvider.GetConfigAsync(); + var config = await ConfigProvider.GetConfigAsync(param); Validate(config, param); - var builder = new WechatpayParameterBuilder(config); - Config(builder, param); + var builder = CreateParameterBuilder(config); + ConfigBuilder(builder, param); return await RequestResult(config, builder); } @@ -51,11 +55,11 @@ public virtual async Task PayAsync(PayParam param) /// 验证 /// /// 微信支付配置 - /// 支付参数 - protected void Validate(WechatpayConfig config, PayParam param) + /// 请求参数 + protected void Validate(WechatpayConfig config, TRequest param) { - config.CheckNotNull(nameof(config)); - param.CheckNotNull(nameof(param)); + config.CheckNull(nameof(config)); + param.CheckNull(nameof(param)); config.Validate(); param.Validate(); ValidateParam(param); @@ -64,65 +68,57 @@ protected void Validate(WechatpayConfig config, PayParam param) /// /// 验证参数 /// - /// 支付参数 - protected virtual void ValidateParam(PayParam param) { } - - /// - /// 配置 - /// - /// 微信支付参数生成器 - /// 支付参数 - protected void Config(WechatpayParameterBuilder builder, PayParam param) + /// 请求参数 + protected virtual void ValidateParam(TRequest param) { - builder.Init(param); - builder.TradeType(GetTradeType()); - InitBuilder(builder, param); } /// - /// 获取交易类型 + /// 创建参数生成器 /// - protected abstract string GetTradeType(); + /// 微信支付配置 + protected abstract TParameterBuilder CreateParameterBuilder(WechatpayConfig config); /// - /// 初始化参数生成器 + /// 配置参数生成器 /// /// 微信支付参数生成器 - /// 支付参数 - protected virtual void InitBuilder(WechatpayParameterBuilder builder, PayParam param) { } + /// 请求参数 + protected abstract void ConfigBuilder(TParameterBuilder builder, TRequest param); /// /// 请求结果 /// /// 微信支付配置 /// 微信支付参数生成器 - /// - protected virtual async Task RequestResult(WechatpayConfig config, WechatpayParameterBuilder builder) + protected virtual async Task RequestResult(WechatpayConfig config, TParameterBuilder builder) { - var result = new WechatpayResult(ConfigProvider, await Request(config, builder)); + var response = await SendRequest(config, builder); + var result = new WechatpayResult(ConfigProvider, response, config, builder); WriteLog(config, builder, result); - return await CreateResult(config, builder, result); + return result; } /// /// 发送请求 /// /// 微信支付配置 - /// 微信支付参数生辰器 - protected virtual async Task Request(WechatpayConfig config, WechatpayParameterBuilder builder) + /// 微信支付参数生成器 + protected virtual async Task SendRequest(WechatpayConfig config, TParameterBuilder builder) { if (IsSend == false) return string.Empty; return await Web.Client() - .Post(string.IsNullOrWhiteSpace(GetOrderUrl()) ? config.GetOrderUrl() : GetOrderUrl()) + .Post(GetUrl(config)) .XmlData(builder.ToXml()) .ResultAsync(); } /// - /// 获取统一下单地址 + /// 获取接口地址 /// - protected virtual string GetOrderUrl() => string.Empty; + /// 微信支付配置 + protected abstract string GetUrl(WechatpayConfig config); /// /// 写日志 @@ -130,19 +126,18 @@ protected virtual async Task Request(WechatpayConfig config, WechatpayPa /// 微信支付配置 /// 微信支付参数生成器 /// 微信支付结果 - protected void WriteLog(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayResult result) + protected void WriteLog(WechatpayConfig config, TParameterBuilder builder, WechatpayResult result) { var log = GetLog(); if (log.IsTraceEnabled == false) return; log.Class(GetType().FullName) .Caption("微信支付") - .Content($"支付方式 : {GetPayWay().Description()}") - .Content($"支付网关 : {config.GetOrderUrl()}") - .Content("请求参数:") + .Content($"支付网关: {GetUrl(config)}") + .Content("请求参数: ") .Content(builder.ToXml()) .Content() - .Content("返回结果:") + .Content("返回结果: ") .Content(result.GetParams()) .Content() .Content("原始响应: ") @@ -153,39 +148,16 @@ protected void WriteLog(WechatpayConfig config, WechatpayParameterBuilder builde /// /// 获取日志操作 /// - private ILog GetLog() => Log.GetLog(WechatpayConst.TraceLogName); - - /// - /// 获取支付方式 - /// - protected abstract PayWay GetPayWay(); - - /// - /// 创建支付结果 - /// - /// 微信支付配置 - /// 微信支付参数生成器 - /// 微信支付结果 - protected virtual async Task CreateResult(WechatpayConfig config, WechatpayParameterBuilder builder, - WechatpayResult result) + private ILog GetLog() { - var success = (await result.ValidateAsync()).IsValid; - return new PayResult(success, result.GetPrepayId(), result.Raw) + try + { + return Log.GetLog(WechatpayConst.TraceLogName); + } + catch { - Parameter = builder.ToString(), - Message = result.GetReturnMessage(), - Result = success ? GetResult(config, builder, result) : null - }; + return Log.Null; + } } - - /// - /// 获取结果 - /// - /// 微信支付配置 - /// 微信支付参数生成器 - /// 微信支付结果 - protected virtual string GetResult(WechatpayConfig config, WechatpayParameterBuilder builder, - WechatpayResult result) => - result.GetPrepayId(); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/RemovePrefixDecimalConvert.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/RemovePrefixDecimalConvert.cs new file mode 100644 index 000000000..15237dde7 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/RemovePrefixDecimalConvert.cs @@ -0,0 +1,26 @@ +using System; +using Bing.Extensions; +using TinyCsvParser.TypeConverter; + +namespace Bing.Biz.Payments.Wechatpay.Services.CsvMappings +{ + /// + /// 移除前缀`字符的decimal列转换器 + /// + internal class RemovePrefixDecimalConvert : ITypeConverter + { + /// + /// 转换 + /// + public bool TryConvert(string value, out decimal result) + { + result = string.IsNullOrWhiteSpace(value) ? 0 : value.TrimStart('`').ToDecimal(); + return true; + } + + /// + /// 目标类型 + /// + public Type TargetType => typeof(decimal); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/RemovePrefixStringConvert.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/RemovePrefixStringConvert.cs new file mode 100644 index 000000000..22e617dab --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/RemovePrefixStringConvert.cs @@ -0,0 +1,25 @@ +using System; +using TinyCsvParser.TypeConverter; + +namespace Bing.Biz.Payments.Wechatpay.Services.CsvMappings +{ + /// + /// 移除前缀`字符的字符串列转换器 + /// + internal class RemovePrefixStringConvert : ITypeConverter + { + /// + /// 转换 + /// + public bool TryConvert(string value, out string result) + { + result = string.IsNullOrWhiteSpace(value) ? "" : value.TrimStart('`'); + return true; + } + + /// + /// 目标类型 + /// + public Type TargetType => typeof(string); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpayAllBillInfoMapping.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpayAllBillInfoMapping.cs new file mode 100644 index 000000000..ca51fb3b1 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpayAllBillInfoMapping.cs @@ -0,0 +1,45 @@ +using Bing.Biz.Payments.Wechatpay.Results; +using TinyCsvParser.Mapping; + +namespace Bing.Biz.Payments.Wechatpay.Services.CsvMappings +{ + /// + /// 微信支付所有对账单信息CSV映射 + /// + internal class WechatpayAllBillInfoMapping : CsvMapping + { + /// + /// 初始化一个类型的实例 + /// + public WechatpayAllBillInfoMapping() + { + MapProperty(0, t => t.TransactionTime, new RemovePrefixStringConvert()); + MapProperty(1, t => t.AppId, new RemovePrefixStringConvert()); + MapProperty(2, t => t.MerchantId, new RemovePrefixStringConvert()); + MapProperty(3, t => t.SubMerchantId, new RemovePrefixStringConvert()); + MapProperty(4, t => t.DeviceNumber, new RemovePrefixStringConvert()); + MapProperty(5, t => t.TradeId, new RemovePrefixStringConvert()); + MapProperty(6, t => t.OrderId, new RemovePrefixStringConvert()); + MapProperty(7, t => t.OpenId, new RemovePrefixStringConvert()); + MapProperty(8, t => t.TradeType, new RemovePrefixStringConvert()); + MapProperty(9, t => t.TradeStatus, new RemovePrefixStringConvert()); + MapProperty(10, t => t.Bank, new RemovePrefixStringConvert()); + MapProperty(11, t => t.CurrencyType, new RemovePrefixStringConvert()); + MapProperty(12, t => t.TotalAmount, new RemovePrefixDecimalConvert()); + MapProperty(13, t => t.CouponAmount, new RemovePrefixDecimalConvert()); + MapProperty(14, t => t.WechatpayRefundId, new RemovePrefixStringConvert()); + MapProperty(15, t => t.MerchantRefundId, new RemovePrefixStringConvert()); + MapProperty(16, t => t.RefundAmount, new RemovePrefixDecimalConvert()); + MapProperty(17, t => t.CouponRefundAmount, new RemovePrefixDecimalConvert()); + MapProperty(18, t => t.RefundType, new RemovePrefixStringConvert()); + MapProperty(19, t => t.RefundStatus, new RemovePrefixStringConvert()); + MapProperty(20, t => t.TradeName, new RemovePrefixStringConvert()); + MapProperty(21, t => t.MerchantAttach, new RemovePrefixStringConvert()); + MapProperty(22, t => t.Commission, new RemovePrefixDecimalConvert()); + MapProperty(23, t => t.Rate, new RemovePrefixStringConvert()); + MapProperty(24, t => t.OrderAmount, new RemovePrefixDecimalConvert()); + MapProperty(25, t => t.ApplyRefundAmount, new RemovePrefixDecimalConvert()); + MapProperty(26, t => t.RateRemark, new RemovePrefixStringConvert()); + } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpayRefundBillInfoMapping.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpayRefundBillInfoMapping.cs new file mode 100644 index 000000000..2758a30f3 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpayRefundBillInfoMapping.cs @@ -0,0 +1,47 @@ +using Bing.Biz.Payments.Wechatpay.Results; +using TinyCsvParser.Mapping; + +namespace Bing.Biz.Payments.Wechatpay.Services.CsvMappings +{ + /// + /// 微信支付退款对账单信息Csv映射 + /// + internal class WechatpayRefundBillInfoMapping : CsvMapping + { + /// + /// 初始化一个类型的实例 + /// + public WechatpayRefundBillInfoMapping() + { + MapProperty(0, t => t.TransactionTime, new RemovePrefixStringConvert()); + MapProperty(1, t => t.AppId, new RemovePrefixStringConvert()); + MapProperty(2, t => t.MerchantId, new RemovePrefixStringConvert()); + MapProperty(3, t => t.SubMerchantId, new RemovePrefixStringConvert()); + MapProperty(4, t => t.DeviceNumber, new RemovePrefixStringConvert()); + MapProperty(5, t => t.TradeId, new RemovePrefixStringConvert()); + MapProperty(6, t => t.OrderId, new RemovePrefixStringConvert()); + MapProperty(7, t => t.OpenId, new RemovePrefixStringConvert()); + MapProperty(8, t => t.TradeType, new RemovePrefixStringConvert()); + MapProperty(9, t => t.TradeStatus, new RemovePrefixStringConvert()); + MapProperty(10, t => t.Bank, new RemovePrefixStringConvert()); + MapProperty(11, t => t.CurrencyType, new RemovePrefixStringConvert()); + MapProperty(12, t => t.TotalAmount, new RemovePrefixDecimalConvert()); + MapProperty(13, t => t.CouponAmount, new RemovePrefixDecimalConvert()); + MapProperty(14, t => t.RefundApplyTime, new RemovePrefixStringConvert()); + MapProperty(15, t => t.RefundTime, new RemovePrefixStringConvert()); + MapProperty(16, t => t.WechatpayRefundId, new RemovePrefixStringConvert()); + MapProperty(17, t => t.MerchantRefundId, new RemovePrefixStringConvert()); + MapProperty(18, t => t.RefundAmount, new RemovePrefixDecimalConvert()); + MapProperty(19, t => t.CouponRefundAmount, new RemovePrefixDecimalConvert()); + MapProperty(20, t => t.RefundType, new RemovePrefixStringConvert()); + MapProperty(21, t => t.RefundStatus, new RemovePrefixStringConvert()); + MapProperty(22, t => t.TradeName, new RemovePrefixStringConvert()); + MapProperty(23, t => t.MerchantAttach, new RemovePrefixStringConvert()); + MapProperty(24, t => t.Commission, new RemovePrefixDecimalConvert()); + MapProperty(25, t => t.Rate, new RemovePrefixStringConvert()); + MapProperty(26, t => t.OrderAmount, new RemovePrefixDecimalConvert()); + MapProperty(27, t => t.ApplyRefundAmount, new RemovePrefixDecimalConvert()); + MapProperty(28, t => t.RateRemark, new RemovePrefixStringConvert()); + } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpaySuccessBillInfoMapping.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpaySuccessBillInfoMapping.cs new file mode 100644 index 000000000..cdae09ac4 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/CsvMappings/WechatpaySuccessBillInfoMapping.cs @@ -0,0 +1,36 @@ +using Bing.Biz.Payments.Wechatpay.Results; +using TinyCsvParser.Mapping; + +namespace Bing.Biz.Payments.Wechatpay.Services.CsvMappings +{ + /// + /// 微信支付成功支付对账单信息Csv映射 + /// + internal class WechatpaySuccessBillInfoMapping : CsvMapping + { + /// + /// 初始化一个类型的实例 + /// + public WechatpaySuccessBillInfoMapping() + { + MapProperty(0, t => t.TransactionTime, new RemovePrefixStringConvert()); + MapProperty(1, t => t.AppId, new RemovePrefixStringConvert()); + MapProperty(2, t => t.MerchantId, new RemovePrefixStringConvert()); + MapProperty(3, t => t.SubMerchantId, new RemovePrefixStringConvert()); + MapProperty(4, t => t.DeviceNumber, new RemovePrefixStringConvert()); + MapProperty(5, t => t.TradeId, new RemovePrefixStringConvert()); + MapProperty(6, t => t.OrderId, new RemovePrefixStringConvert()); + MapProperty(7, t => t.OpenId, new RemovePrefixStringConvert()); + MapProperty(8, t => t.TradeType, new RemovePrefixStringConvert()); + MapProperty(9, t => t.TradeStatus, new RemovePrefixStringConvert()); + MapProperty(10, t => t.Bank, new RemovePrefixStringConvert()); + MapProperty(11, t => t.CurrencyType, new RemovePrefixStringConvert()); + MapProperty(12, t => t.TotalAmount, new RemovePrefixDecimalConvert()); + MapProperty(13, t => t.CouponAmount, new RemovePrefixDecimalConvert()); + MapProperty(14, t => t.TradeName, new RemovePrefixStringConvert()); + MapProperty(15, t => t.MerchantAttach, new RemovePrefixStringConvert()); + MapProperty(16, t => t.Commission, new RemovePrefixDecimalConvert()); + MapProperty(17, t => t.Rate, new RemovePrefixStringConvert()); + } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayAppPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayAppPayService.cs index c628776c7..b803f1c6b 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayAppPayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayAppPayService.cs @@ -13,7 +13,7 @@ namespace Bing.Biz.Payments.Wechatpay.Services /// /// 微信App支付服务 /// - public class WechatpayAppPayService : WechatpayServiceBase, IWechatpayAppPayService + public class WechatpayAppPayService : WechatpayPayServiceBase, IWechatpayAppPayService { /// /// 初始化一个类型的实例 @@ -24,45 +24,25 @@ public WechatpayAppPayService(IWechatpayConfigProvider configProvider) : base(co } /// - /// 获取交易类型 - /// - /// - protected override string GetTradeType() - { - return "APP"; - } - - /// - /// 获取支付方式 + /// 支付 /// - /// - protected override PayWay GetPayWay() - { - return PayWay.WechatpayAppPay; - } + /// 支付参数 + public async Task PayAsync(WechatpayAppPayRequest request) => await PayAsync(request.ToParam()); /// - /// 支付 + /// 获取交易类型 /// - /// 支付参数 - /// - public async Task PayAsync(WechatpayAppPayRequest request) - { - return await PayAsync(request.ToParam()); - } + protected override string GetTradeType() => "APP"; /// /// 获取结果 /// - /// 微信支付配置 - /// 微信支付参数生成器 /// 微信支付结果 - /// - protected override string GetResult(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayResult result) + protected override string GetResult( WechatpayResult result) { - return new WechatpayParameterBuilder(config) - .AppId(config.AppId) - .PartnerId(config.MerchantId) + return new WechatpayParameterBuilder(result.Config) + .AppId(result.Config.AppId) + .PartnerId(result.Config.MerchantId) .Add("prepayid", result.GetPrepayId()) .Add("noncestr", Id.Guid()) .Timestamp() diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayDownloadBillService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayDownloadBillService.cs new file mode 100644 index 000000000..8ae5cb357 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayDownloadBillService.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Bing.Biz.Payments.Wechatpay.Abstractions; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Enums; +using Bing.Biz.Payments.Wechatpay.Parameters; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; +using Bing.Biz.Payments.Wechatpay.Results; +using Bing.Biz.Payments.Wechatpay.Services.Base; +using Bing.Biz.Payments.Wechatpay.Services.CsvMappings; +using Bing.Extensions; +using TinyCsvParser; +using TinyCsvParser.Mapping; +using TinyCsvParser.Tokenizer; + +namespace Bing.Biz.Payments.Wechatpay.Services +{ + /// + /// 微信支付下载交易账单服务 + /// + public class WechatpayDownloadBillService : WechatpayServiceBase, IWechatpayDownloadBillService + { + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置提供程序 + public WechatpayDownloadBillService(IWechatpayConfigProvider configProvider) : base(configProvider) + { + } + + /// + /// 下载对账单 + /// + /// 下载对账单参数 + public async Task DownloadAsync(WechatpayDownloadBillRequest request) + { + var config = await ConfigProvider.GetConfigAsync(request); + Validate(config, request); + var builder = new WechatpayParameterBuilder(config); + ConfigBuilder(builder, request); + return await RequestAsync(config, builder, request); + } + + /// + /// 创建参数生成器 + /// + /// 微信支付配置 + protected override WechatpayParameterBuilder CreateParameterBuilder(WechatpayConfig config) => new WechatpayParameterBuilder(config); + + /// + /// 配置参数生成器 + /// + /// 微信支付参数生成器 + /// 请求参数 + protected override void ConfigBuilder(WechatpayParameterBuilder builder, WechatpayDownloadBillRequest param) + { + builder.Init(); + builder.Add("bill_date", param.GetBillDate()); + builder.Add("bill_type", param.BillType.Description()); + } + + /// + /// 请求结果 + /// + /// 微信支付配置 + /// 微信支付参数生成器 + /// 请求参数 + protected async Task RequestAsync(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayDownloadBillRequest request) + { + var response = await SendRequest(config, builder); + if (response.StartsWith("交易时间")) + return Success(response, builder, request); + return await Fail(response, config, builder); + } + + /// + /// 请求成功 + /// + /// 响应内容 + /// 微信支付参数生成器 + /// 请求参数 + private WechatpayDownloadBillResult Success(string response, WechatpayParameterBuilder builder, WechatpayDownloadBillRequest request) + { + var bills = GetBills(response, request); + return new WechatpayDownloadBillResult(true, builder.ToString(), bills); + } + + /// + /// 获取对账单 + /// + /// 响应内容 + /// 请求参数 + private List GetBills(string response, WechatpayDownloadBillRequest request) + { + var result = new List(); + var tokenizer = new QuotedStringTokenizer(','); + var options = new CsvParserOptions(true, tokenizer); + var parser = new CsvParser(options, GetCsvMapping(request)); + var readerOptions = new CsvReaderOptions(new[] { Environment.NewLine }); + var records = parser.ReadFromString(readerOptions, response); + foreach (var record in records) + { + if (record.IsValid == false) + continue; + if (record.Result.TransactionTime == "交易时间") + continue; + if (record.Result.TransactionTime == "总交易单数") + break; + result.Add(record.Result); + } + return result; + } + + /// + /// 获取Csv映射 + /// + /// 请求参数 + private ICsvMapping GetCsvMapping(WechatpayDownloadBillRequest request) + { + switch (request.BillType) + { + case WechatpayBillType.Success: + return new WechatpaySuccessBillInfoMapping(); + case WechatpayBillType.RechargeRefund: + return new WechatpayRefundBillInfoMapping(); + default: + return new WechatpayAllBillInfoMapping(); + } + } + + /// + /// 请求失败 + /// + /// 响应内容 + /// 微信支付配置 + /// 微信支付参数生成器 + private async Task Fail(string response, WechatpayConfig config, WechatpayParameterBuilder builder) + { + var result = new WechatpayResult(ConfigProvider, response, config, builder); + WriteLog(config, builder, result); + var success = (await result.ValidateAsync()).IsValid; + return new WechatpayDownloadBillResult(success, result); + } + + /// + /// 获取接口地址 + /// + /// 微信支付配置 + protected override string GetUrl(WechatpayConfig config) => config.GetDownloadBillUrl(); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayH5PayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayH5PayService.cs new file mode 100644 index 000000000..710a72d64 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayH5PayService.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Core; +using Bing.Biz.Payments.Wechatpay.Abstractions; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; +using Bing.Biz.Payments.Wechatpay.Results; +using Bing.Biz.Payments.Wechatpay.Services.Base; + +namespace Bing.Biz.Payments.Wechatpay.Services +{ + /// + /// 微信H5支付服务 + /// + public class WechatpayH5PayService : WechatpayPayServiceBase, IWechatpayH5PayService + { + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置提供器 + public WechatpayH5PayService(IWechatpayConfigProvider configProvider) : base(configProvider) + { + } + + /// + /// 支付 + /// + /// 支付参数 + public async Task PayAsync(WechatpayH5PayRequest request) => await PayAsync(request.ToParam()); + + /// + /// 获取交易类型 + /// + protected override string GetTradeType() => "MWEB"; + + /// + /// 获取结果 + /// + /// 微信支付结果 + protected override string GetResult(WechatpayResult result) + { + return result.GetMWebUrl(); + } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPublicPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayJsApiPayService.cs similarity index 56% rename from components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPublicPayService.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayJsApiPayService.cs index 1c3d502a5..d711e9fbb 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPublicPayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayJsApiPayService.cs @@ -13,15 +13,15 @@ namespace Bing.Biz.Payments.Wechatpay.Services { /// - /// 微信公众号支付服务 + /// 微信JsApi支付服务 /// - public class WechatpayPublicPayService : WechatpayServiceBase, IWechatpayPublicPayService + public class WechatpayJsApiPayService : WechatpayPayServiceBase, IWechatpayJsApiPayService { /// - /// 初始化一个类型的实例 + /// 初始化一个类型的实例 /// /// 微信支付配置提供器 - public WechatpayPublicPayService(IWechatpayConfigProvider configProvider) : base(configProvider) + public WechatpayJsApiPayService(IWechatpayConfigProvider configProvider) : base(configProvider) { } @@ -29,57 +29,45 @@ public WechatpayPublicPayService(IWechatpayConfigProvider configProvider) : base /// 支付 /// /// 支付参数 - /// - public async Task PayAsync(WechatpayPublicPayRequest request) - { - return await PayAsync(request.ToParam()); - } + public async Task PayAsync(WechatpayJsApiPayRequest request) => await PayAsync(request.ToParam()); /// /// 获取交易类型 /// - /// - protected override string GetTradeType() - { - return "JSAPI"; - } + protected override string GetTradeType() => "JSAPI"; /// /// 验证参数 /// - /// 支付参数 + /// 请求参数 protected override void ValidateParam(PayParam param) { if (param.OpenId.IsEmpty()) - { - throw new Warning("公众号支付必须设置OpenId"); - } + throw new Warning("必须设置OpenId"); } /// - /// 获取支付方式 + /// 初始化参数生成器 /// - /// - protected override PayWay GetPayWay() + /// 微信支付参数生成器 + /// 支付参数 + protected override void InitBuilder(WechatpayParameterBuilder builder, PayParam param) { - return PayWay.WechatpayPublicPay; + builder.OpenId(param.OpenId); } /// /// 获取结果 /// - /// 微信支付配置 - /// 微信支付参数生成器 /// 微信支付结果 - /// - protected override string GetResult(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayResult result) + protected override string GetResult(WechatpayResult result) { - return new WechatpayParameterBuilder(config) - .Add("appId", config.AppId) + return new WechatpayParameterBuilder(result.Config) + .Add("appId", result.Config.AppId) .Add("timeStamp", Time.GetUnixTimestamp().SafeString()) .Add("nonceStr", Id.Guid()) .Package($"prepay_id{result.GetPrepayId()}") - .Add("signType", config.SignType.Description()) + .Add("signType", result.Config.SignType.Description()) .Add("paySign", result.GetSign()) .ToJson(); } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayMiniProgramPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayMiniProgramPayService.cs index 83f8f39a5..6fa86d726 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayMiniProgramPayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayMiniProgramPayService.cs @@ -15,7 +15,7 @@ namespace Bing.Biz.Payments.Wechatpay.Services /// /// 微信小程序支付服务 /// - public class WechatpayMiniProgramPayService : WechatpayServiceBase, IWechatpayMiniProgramPayService + public class WechatpayMiniProgramPayService : WechatpayPayServiceBase, IWechatpayMiniProgramPayService { /// /// 初始化一个类型的实例 @@ -26,22 +26,15 @@ public WechatpayMiniProgramPayService(IWechatpayConfigProvider configProvider) : } /// - /// 获取交易类型 + /// 支付 /// - /// - protected override string GetTradeType() - { - return "JSAPI"; - } + /// 微信小程序支付参数 + public async Task PayAsync(WechatpayMiniProgramPayRequest request) => await PayAsync(request.ToParam()); /// - /// 获取支付方式 + /// 获取交易类型 /// - /// - protected override PayWay GetPayWay() - { - return PayWay.WechatpayMiniProgramPay; - } + protected override string GetTradeType() => "JSAPI"; /// /// 验证参数 @@ -50,36 +43,31 @@ protected override PayWay GetPayWay() protected override void ValidateParam(PayParam param) { if (param.OpenId.IsEmpty()) - { throw new Warning("小程序支付必须设置OpenId"); - } } /// - /// 支付 + /// 初始化参数生成器 /// - /// 微信小程序支付参数 - /// - public async Task PayAsync(WechatpayMiniProgramPayRequest request) + /// 微信支付参数生成器 + /// 支付参数 + protected override void InitBuilder(WechatpayParameterBuilder builder, PayParam param) { - return await PayAsync(request.ToParam()); + builder.OpenId(param.OpenId); } /// /// 获取结果 /// - /// 微信支付配置 - /// 微信支付参数生成器 /// 微信支付结果 - /// - protected override string GetResult(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayResult result) + protected override string GetResult(WechatpayResult result) { - return new WechatpayParameterBuilder(config) - .Add("appId", config.AppId) + return new WechatpayParameterBuilder(result.Config) + .Add("appId", result.Config.AppId) .Add("timeStamp", Time.GetUnixTimestamp().SafeString()) .Add("nonceStr", Id.Guid()) .Package($"prepay_id={result.GetPrepayId()}") - .Add("signType", config.SignType.Description()) + .Add("signType", result.Config.SignType.Description()) .ToJson(); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayNativePayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayNativePayService.cs new file mode 100644 index 000000000..0cb7f7686 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayNativePayService.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Core; +using Bing.Biz.Payments.Wechatpay.Abstractions; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; +using Bing.Biz.Payments.Wechatpay.Results; +using Bing.Biz.Payments.Wechatpay.Services.Base; + +namespace Bing.Biz.Payments.Wechatpay.Services +{ + /// + /// 微信扫码支付服务 + /// + public class WechatpayNativePayService : WechatpayPayServiceBase, IWechatpayNativePayService + { + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置提供器 + public WechatpayNativePayService(IWechatpayConfigProvider configProvider) : base(configProvider) + { + } + + /// + /// 支付 + /// + /// 支付参数 + public async Task PayAsync(WechatpayNativePayRequest request) => await PayAsync(request.ToParam()); + + /// + /// 获取交易类型 + /// + protected override string GetTradeType() => "NATIVE"; + + /// + /// 获取结果 + /// + /// 微信支付结果 + protected override string GetResult(WechatpayResult result) + { + return result.GetCodeUrl(); + } + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayNotifyService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayNotifyService.cs index 1ed923f15..b811cc26b 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayNotifyService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayNotifyService.cs @@ -6,7 +6,7 @@ using Bing.Biz.Payments.Wechatpay.Results; using Bing.Extensions; using Bing.Helpers; -using Bing.Validations; +using Bing.Validation; using Xml = Bing.Helpers.Xml; namespace Bing.Biz.Payments.Wechatpay.Services @@ -31,6 +31,17 @@ public class WechatpayNotifyService : IWechatpayNotifyService /// private bool _isLoad; + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置提供器 + public WechatpayNotifyService(IWechatpayConfigProvider configProvider) + { + configProvider.CheckNotNull(nameof(configProvider)); + _configProvider = configProvider; + _isLoad = false; + } + /// /// 商户订单号 /// @@ -46,22 +57,10 @@ public class WechatpayNotifyService : IWechatpayNotifyService /// public decimal Money => GetParam(WechatpayConst.TotalFee).ToDecimal() / 100M; - /// - /// 初始化一个类型的实例 - /// - /// 微信支付配置提供器 - public WechatpayNotifyService(IWechatpayConfigProvider configProvider) - { - configProvider.CheckNotNull(nameof(configProvider)); - _configProvider = configProvider; - _isLoad = false; - } - /// /// 获取参数 /// /// 参数名 - /// public string GetParam(string name) { Init(); @@ -73,16 +72,11 @@ public string GetParam(string name) /// /// 类型 /// 参数名 - /// - public T GetParam(string name) - { - return Conv.To(GetParam(name)); - } + public T GetParam(string name) => Conv.To(GetParam(name)); /// /// 获取参数集合 /// - /// public IDictionary GetParams() { Init(); @@ -95,9 +89,7 @@ public IDictionary GetParams() private void Init() { if (_isLoad) - { return; - } InitResult(); _isLoad = true; } @@ -105,50 +97,34 @@ private void Init() /// /// 初始化支付结果 /// - private void InitResult() - { - _result = new WechatpayResult(_configProvider, Web.Body); - } + private void InitResult() => _result = new WechatpayResult(_configProvider, Web.Body); /// /// 验证 /// - /// public async Task ValidateAsync() { Init(); if (Money <= 0) - { return new ValidationResultCollection(PayResource.InvalidMoney); - } - return await _result.ValidateAsync(); } /// /// 返回成功消息 /// - /// - public string Success() - { - return Return(WechatpayConst.Success, WechatpayConst.Ok); - } + public string Success() => Return(WechatpayConst.Success, WechatpayConst.Ok); /// /// 返回失败消息 /// - /// - public string Fail() - { - return Return(WechatpayConst.Fail, WechatpayConst.Fail); - } + public string Fail() => Return(WechatpayConst.Fail, WechatpayConst.Fail); /// /// 返回消息 /// /// 编码 /// 消息 - /// private string Return(string code, string message) { var xml = new Xml(); diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPagePayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPagePayService.cs deleted file mode 100644 index 6523cda61..000000000 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPagePayService.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Threading.Tasks; -using Bing.Biz.Payments.Core; -using Bing.Biz.Payments.Wechatpay.Abstractions; -using Bing.Biz.Payments.Wechatpay.Configs; -using Bing.Biz.Payments.Wechatpay.Parameters; -using Bing.Biz.Payments.Wechatpay.Parameters.Requests; -using Bing.Biz.Payments.Wechatpay.Results; -using Bing.Biz.Payments.Wechatpay.Services.Base; - -namespace Bing.Biz.Payments.Wechatpay.Services -{ - /// - /// 微信电脑网站支付服务 - /// - public class WechatpayPagePayService : WechatpayServiceBase, IWechatpayPagePayService - { - /// - /// 初始化一个类型的实例 - /// - /// 微信支付配置提供器 - public WechatpayPagePayService(IWechatpayConfigProvider configProvider) : base(configProvider) - { - } - - /// - /// 支付 - /// - /// 支付参数 - /// - public async Task PayAsync(WechatpayPagePayRequest request) - { - return await PayAsync(request.ToParam()); - } - - /// - /// 获取交易类型 - /// - /// - protected override string GetTradeType() - { - return "NATIVE"; - } - - /// - /// 获取支付方式 - /// - /// - protected override PayWay GetPayWay() - { - return PayWay.WechatpayPagePay; - } - - /// - /// 获取结果 - /// - /// 微信支付配置 - /// 微信支付参数生成器 - /// 微信支付结果 - /// - protected override string GetResult(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayResult result) - { - return result.GetCodeUrl(); - } - } -} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayBarcodePayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPaymentCodePayService.cs similarity index 64% rename from components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayBarcodePayService.cs rename to components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPaymentCodePayService.cs index 550653dd0..8a505d4e4 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayBarcodePayService.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayPaymentCodePayService.cs @@ -16,13 +16,13 @@ namespace Bing.Biz.Payments.Wechatpay.Services /// /// 微信条码支付服务 /// - public class WechatpayBarcodePayService : WechatpayServiceBase, IWechatpayBarcodePayService + public class WechatpayPaymentCodePayService : WechatpayPayServiceBase, IWechatpayPaymentCodePayService { /// - /// 初始化一个类型的实例 + /// 初始化一个类型的实例 /// /// 微信支付配置提供器 - public WechatpayBarcodePayService(IWechatpayConfigProvider configProvider) : base(configProvider) + public WechatpayPaymentCodePayService(IWechatpayConfigProvider configProvider) : base(configProvider) { } @@ -30,29 +30,12 @@ public WechatpayBarcodePayService(IWechatpayConfigProvider configProvider) : bas /// 支付 /// /// 支付参数 - /// - public async Task PayAsync(WechatpayBarcodePayRequest request) - { - return await PayAsync(request.ToParam()); - } + public async Task PayAsync(WechatpayPaymentCodePayRequest request) => await PayAsync(request.ToParam()); /// /// 获取交易类型 /// - /// - protected override string GetTradeType() - { - return string.Empty; - } - - /// - /// 获取支付方式 - /// - /// - protected override PayWay GetPayWay() - { - return PayWay.WechatpayBarcodePay; - } + protected override string GetTradeType() => string.Empty; /// /// 验证参数 @@ -61,9 +44,7 @@ protected override PayWay GetPayWay() protected override void ValidateParam(PayParam param) { if (param.AuthCode.IsEmpty()) - { throw new Warning(PayResource.AuthCodeIsEmpty); - } } /// @@ -77,13 +58,16 @@ protected override void InitBuilder(WechatpayParameterBuilder builder, PayParam } /// - /// 获取结果 + /// 获取接口地址 /// /// 微信支付配置 - /// 微信支付参数生成器 + protected override string GetUrl(WechatpayConfig config) => config.GetPaymentCodePayUrl(); + + /// + /// 获取结果 + /// /// 微信支付结果 - /// - protected override string GetResult(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayResult result) + protected override string GetResult(WechatpayResult result) { return result.GetParams().ToJson(); } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayRefundService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayRefundService.cs new file mode 100644 index 000000000..81a997fb2 --- /dev/null +++ b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayRefundService.cs @@ -0,0 +1,59 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Core; +using Bing.Biz.Payments.Wechatpay.Abstractions; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; +using Bing.Biz.Payments.Wechatpay.Services.Base; +using Bing.Helpers; + +namespace Bing.Biz.Payments.Wechatpay.Services +{ + /// + /// 微信退款服务 + /// + public class WechatpayRefundService : WechatpayServiceBase, IWechatpayRefundService + { + /// + /// 初始化一个类型的实例 + /// + /// 微信支付配置提供程序 + public WechatpayRefundService(IWechatpayConfigProvider configProvider) : base(configProvider) + { + } + + /// + /// 退款 + /// + /// 退款参数 + public Task RefundAsync(WechatRefundRequest request) + { + throw new System.NotImplementedException(); + } + + /// + /// 创建参数生成器 + /// + /// 微信支付配置 + protected override WechatpayRefundParameterBuilder CreateParameterBuilder(WechatpayConfig config) => new WechatpayRefundParameterBuilder(config); + + /// + /// 配置参数生成器 + /// + /// 微信支付参数生成器 + /// 请求参数 + protected override void ConfigBuilder(WechatpayRefundParameterBuilder builder, WechatRefundRequest param) + { + builder.Init(param); + builder.Add(WechatpayConst.AppId, builder.Config.AppId) + .Add(WechatpayConst.MerchantId, builder.Config.MerchantId) + .Add(WechatpayConst.NonceStr, Id.Guid()); + } + + /// + /// 获取接口地址 + /// + /// 微信支付配置 + protected override string GetUrl(WechatpayConfig config) => config.GetRefundUrl(); + } +} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayWapPayService.cs b/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayWapPayService.cs deleted file mode 100644 index 804002aca..000000000 --- a/components/src/Bing.Biz.Payments/Wechatpay/Services/WechatpayWapPayService.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Threading.Tasks; -using Bing.Biz.Payments.Core; -using Bing.Biz.Payments.Wechatpay.Abstractions; -using Bing.Biz.Payments.Wechatpay.Configs; -using Bing.Biz.Payments.Wechatpay.Parameters; -using Bing.Biz.Payments.Wechatpay.Parameters.Requests; -using Bing.Biz.Payments.Wechatpay.Results; -using Bing.Biz.Payments.Wechatpay.Services.Base; - -namespace Bing.Biz.Payments.Wechatpay.Services -{ - /// - /// 微信手机网站支付服务 - /// - public class WechatpayWapPayService : WechatpayServiceBase, IWechatpayWapPayService - { - /// - /// 初始化一个类型的实例 - /// - /// 微信支付配置提供器 - public WechatpayWapPayService(IWechatpayConfigProvider configProvider) : base(configProvider) - { - } - - /// - /// 支付 - /// - /// 支付参数 - /// - public async Task PayAsync(WechatpayWapPayRequest request) - { - return await PayAsync(request.ToParam()); - } - - /// - /// 获取交易类型 - /// - /// - protected override string GetTradeType() - { - return "MWEB"; - } - - /// - /// 获取支付方式 - /// - /// - protected override PayWay GetPayWay() - { - return PayWay.WechatpayWapPay; - } - - /// - /// 获取结果 - /// - /// 微信支付配置 - /// 微信支付参数生成器 - /// 微信支付结果 - /// - protected override string GetResult(WechatpayConfig config, WechatpayParameterBuilder builder, WechatpayResult result) - { - return result.GetMWebUrl(); - } - } -} diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Signatures/HmacSha256SignManager.cs b/components/src/Bing.Biz.Payments/Wechatpay/Signatures/HmacSha256SignManager.cs index 404e369b2..f8858e4fd 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Signatures/HmacSha256SignManager.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Signatures/HmacSha256SignManager.cs @@ -37,7 +37,6 @@ public HmacSha256SignManager(ISignKey key, ParameterBuilder builder = null) /// /// 键 /// 值 - /// public ISignManager Add(string key, object value) { _builder.Add(key, value); @@ -47,7 +46,6 @@ public ISignManager Add(string key, object value) /// /// 签名 /// - /// public string Sign() { var value = $"{_builder.Result(true)}&key={_key.GetKey()}"; @@ -58,13 +56,10 @@ public string Sign() /// 验证签名 /// /// 签名 - /// public bool Verify(string sign) { if (sign.IsEmpty()) - { return false; - } return sign == Sign(); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Signatures/Md5SignManager.cs b/components/src/Bing.Biz.Payments/Wechatpay/Signatures/Md5SignManager.cs index c99621193..144d4b2a4 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Signatures/Md5SignManager.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Signatures/Md5SignManager.cs @@ -37,7 +37,6 @@ public Md5SignManager(ISignKey key, ParameterBuilder builder = null) /// /// 键 /// 值 - /// public ISignManager Add(string key, object value) { _builder.Add(key, value); @@ -47,7 +46,6 @@ public ISignManager Add(string key, object value) /// /// 签名 /// - /// public string Sign() { var value = $"{_builder.Result(true)}&key={_key.GetKey()}"; @@ -58,14 +56,10 @@ public string Sign() /// 验证签名 /// /// 签名 - /// public bool Verify(string sign) { if (sign.IsEmpty()) - { return false; - } - return sign == Sign(); } } diff --git a/components/src/Bing.Biz.Payments/Wechatpay/Signatures/SignManagerFactory.cs b/components/src/Bing.Biz.Payments/Wechatpay/Signatures/SignManagerFactory.cs index 43385054a..aabb4fca7 100644 --- a/components/src/Bing.Biz.Payments/Wechatpay/Signatures/SignManagerFactory.cs +++ b/components/src/Bing.Biz.Payments/Wechatpay/Signatures/SignManagerFactory.cs @@ -17,19 +17,12 @@ public class SignManagerFactory /// /// 微信支付配置 /// 参数生成器 - /// public static ISignManager Create(WechatpayConfig config, ParameterBuilder builder) { if (config.SignType == WechatpaySignType.Md5) - { return new Md5SignManager(new SignKey(config.PrivateKey), builder); - } - if (config.SignType == WechatpaySignType.HmacSha256) - { return new HmacSha256SignManager(new SignKey(config.PrivateKey), builder); - } - throw new NotImplementedException($"未实现签名算法:{config.SignType.Description()}"); } } diff --git a/components/src/Bing.Biz.Payments/project.dependency.props b/components/src/Bing.Biz.Payments/project.dependency.props new file mode 100644 index 000000000..cc901947c --- /dev/null +++ b/components/src/Bing.Biz.Payments/project.dependency.props @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/src/Bing.Biz.Payments/project.props b/components/src/Bing.Biz.Payments/project.props new file mode 100644 index 000000000..9ecb980a1 --- /dev/null +++ b/components/src/Bing.Biz.Payments/project.props @@ -0,0 +1,25 @@ + + + Bing.Biz.Payments + Bing.Biz.Payments + Bing.Biz.Payments是Bing应用框架的支付操作类库。 +Bing是一个.net core平台下的应用框架,旨在于提升小型团队的开发能力,由常用公共操作类、架构基类、第三方组件封装、第三方业务接口封装等组成。 + + + + + + True + True + PayResource.resx + + + + + + ResXFileCodeGenerator + PayResource.Designer.cs + + + + \ No newline at end of file diff --git a/components/tests/Bing.Biz.Payments.Tests/Bing.Biz.Payments.Tests.csproj b/components/tests/Bing.Biz.Payments.Tests/Bing.Biz.Payments.Tests.csproj new file mode 100644 index 000000000..a4cb08793 --- /dev/null +++ b/components/tests/Bing.Biz.Payments.Tests/Bing.Biz.Payments.Tests.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.1 + false + + + + + + + diff --git a/components/tests/Bing.Biz.Payments.Tests/TestConst.cs b/components/tests/Bing.Biz.Payments.Tests/TestConst.cs new file mode 100644 index 000000000..9c7c47a14 --- /dev/null +++ b/components/tests/Bing.Biz.Payments.Tests/TestConst.cs @@ -0,0 +1,13 @@ +namespace Bing.Biz.Payments.Tests +{ + /// + /// 测试常量 + /// + public class TestConst + { + /// + /// 测试时间 + /// + public const string Time = "2010-10-10 10:10:10:10"; + } +} diff --git a/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Configs/TestConfigProvider.cs b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Configs/TestConfigProvider.cs new file mode 100644 index 000000000..489d5e602 --- /dev/null +++ b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Configs/TestConfigProvider.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Wechatpay.Configs; + +namespace Bing.Biz.Payments.Tests.Wechatpay.Configs +{ + /// + /// 微信支付测试配置提供器 + /// + public class TestConfigProvider : IWechatpayConfigProvider + { + /// + /// 获取配置 + /// + /// 参数 + public Task GetConfigAsync(object parameter = null) + { + var config = new WechatpayConfig { AppId = "", MerchantId = "", PrivateKey = "", NotifyUrl = "" }; + return Task.FromResult(config); + } + } +} diff --git a/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Parameters/WechatpayParameterBuilderTest.cs b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Parameters/WechatpayParameterBuilderTest.cs new file mode 100644 index 000000000..972cdec0e --- /dev/null +++ b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Parameters/WechatpayParameterBuilderTest.cs @@ -0,0 +1,241 @@ +using System; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters; +using Bing.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Bing.Biz.Payments.Tests.Wechatpay.Parameters +{ + /// + /// 微信支付参数生成器测试 + /// + public class WechatpayParameterBuilderTest : IDisposable + { + /// + /// 输出 + /// + private readonly ITestOutputHelper _output; + + /// + /// 微信支付参数生成器 + /// + private readonly WechatpayParameterBuilder _builder; + + /// + /// 测试初始化 + /// + public WechatpayParameterBuilderTest(ITestOutputHelper output) + { + _output = output; + Time.SetTime(TestConst.Time); + _builder = new WechatpayParameterBuilder(new WechatpayConfig()); + } + + /// + /// 释放资源 + /// + public void Dispose() + { + Time.Reset(); + } + + /// + /// 测试 - 设置应用标识 + /// + [Fact] + public void Test_AppId() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.AppId("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置商户号 + /// + [Fact] + public void Test_MerchantId() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.MerchantId("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置签名类型 + /// + [Fact] + public void Test_SignType() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.SignType("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置标题 + /// + [Fact] + public void Test_Body() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.Body("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置商户订单号 + /// + [Fact] + public void Test_OutTradeNo() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.OutTradeNo("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置总金额 + /// + [Fact] + public void Test_TotalFee() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append("123"); + result.Append(""); + + // 操作 + _builder.TotalFee(1.23M); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置回调通知地址 + /// + [Fact] + public void Test_NotifyUrl() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.NotifyUrl("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置终端IP + /// + [Fact] + public void Test_SpbillCreateIp() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.SpbillCreateIp("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + + /// + /// 测试 - 设置交易类型 + /// + [Fact] + public void Test_TradeType() + { + // 结果 + var result = new Str(); + result.Append(""); + result.Append(""); + result.Append(""); + + // 操作 + _builder.TradeType("a"); + + // 验证 + Assert.Equal(result.ToString(), _builder.ToXmlNoContainsSign()); + + // 输出结果 + _output.WriteLine(_builder.ToString()); + } + } +} diff --git a/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Results/WechatpayResultTest.cs b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Results/WechatpayResultTest.cs new file mode 100644 index 000000000..1fd47d8a4 --- /dev/null +++ b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Results/WechatpayResultTest.cs @@ -0,0 +1,70 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Enums; +using Bing.Biz.Payments.Wechatpay.Results; +using Xunit; + +namespace Bing.Biz.Payments.Tests.Wechatpay.Results +{ + /// + /// 微信支付结果测试 + /// + public class WechatpayResultTest + { + /// + /// 测试 - 校验响应内容 + /// + [Fact] + public async Task Test_VerifyResponse() + { + // 设置响应内容 + var response = @" + + + + + + + + + + "; + + // 操作 + var result = new WechatpayResult(new TestConfigProvider(), response); + + // 验证 + Assert.Equal("SUCCESS", result.GetReturnCode()); + Assert.Equal("OK", result.GetReturnMessage()); + Assert.Equal("wx9b90e1788b39fec6", result.GetAppId()); + Assert.Equal("1985518532", result.GetMerchantId()); + Assert.Equal("wrKodsjUFk34qYno", result.GetNonce()); + Assert.Equal("5F721ADF22DD2C60B4E171228F8DA36E", result.GetSign()); + Assert.Equal("SUCCESS", result.GetResultCode()); + Assert.Equal("wx141217433636466fe2c3b2a10139084028", result.GetPrepayId()); + Assert.Equal("APP", result.GetTradeType()); + var isValid = (await result.ValidateAsync()).IsValid; + Assert.True(isValid); + } + } + + /// + /// 微信支付测试配置提供器 + /// + public class TestConfigProvider : IWechatpayConfigProvider + { + /// + /// 获取配置 + /// + /// 参数 + public Task GetConfigAsync(object parameter = null) + { + var config = new WechatpayConfig + { + SignType = WechatpaySignType.Md5, + PrivateKey = "VVHZOaJEj44WbX0f3Lj7DHkfwEqvlURA" + }; + return Task.FromResult(config); + } + } +} diff --git a/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Services/WechatpayDownloadBillServiceTest.cs b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Services/WechatpayDownloadBillServiceTest.cs new file mode 100644 index 000000000..4ad3c6b13 --- /dev/null +++ b/components/tests/Bing.Biz.Payments.Tests/Wechatpay/Services/WechatpayDownloadBillServiceTest.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Bing.Biz.Payments.Tests.Wechatpay.Configs; +using Bing.Biz.Payments.Wechatpay.Parameters.Requests; +using Bing.Biz.Payments.Wechatpay.Services; +using Bing.Extensions; +using Xunit; + +namespace Bing.Biz.Payments.Tests.Wechatpay.Services +{ + /// + /// 微信支付 - 下载交易账单服务 + /// + public class WechatpayDownloadBillServiceTest + { + /// + /// 测试 - 下载交易账单 + /// + //[Fact] + [Fact(Skip = "更改支付配置后测试")] + public async Task Test_DownloadAsync() + { + var service = new WechatpayDownloadBillService(new TestConfigProvider()); + var request = new WechatpayDownloadBillRequest { BillDate = "2022-06-10".ToDate() }; + var result = await service.DownloadAsync(request); + Assert.NotEmpty(result.Bills); + } + } +} diff --git a/framework/Publish.bat b/framework/Publish.bat index c10f5b0d8..ebffe8767 100644 --- a/framework/Publish.bat +++ b/framework/Publish.bat @@ -14,6 +14,13 @@ for /R "nuget_pub" %%s in (*) do ( dotnet pack src/Bing -c Release -o nuget_pub dotnet pack src/Bing.AspNetCore -c Release -o nuget_pub +::AOP +dotnet pack src/Bing.Aop.AspectCore -c Release -o nuget_pub + +::Validation +dotnet pack src/Bing.Validation.Abstractions -c Release -o nuget_pub +dotnet pack src/Bing.Validation -c Release -o nuget_pub + ::Security dotnet pack src/Bing.Security -c Release -o nuget_pub @@ -34,6 +41,7 @@ dotnet pack src/Bing.Data -c Release -o nuget_pub dotnet pack src/Bing.Data.Sql -c Release -o nuget_pub ::Domain +dotnet pack src/Bing.Uow -c Release -o nuget_pub dotnet pack src/Bing.Auditing -c Release -o nuget_pub dotnet pack src/Bing.Ddd.Domain -c Release -o nuget_pub diff --git a/framework/UnPublish.bat b/framework/UnPublish.bat index be3ee9edb..04f94629e 100644 --- a/framework/UnPublish.bat +++ b/framework/UnPublish.bat @@ -19,6 +19,10 @@ dotnet nuget delete Bing.AspNetCore %version% -s %source% -k %key% --non-interac ::Security dotnet nuget delete Bing.Security %version% -s %source% -k %key% --non-interactive +::Validation +dotnet nuget delete Bing.Validation.Abstractions %version% -s %source% -k %key% --non-interactive +dotnet nuget delete Bing.Validation %version% -s %source% -k %key% --non-interactive + ::Logs dotnet nuget delete Bing.Logs %version% -s %source% -k %key% --non-interactive dotnet nuget delete Bing.Logs.Exceptionless %version% -s %source% -k %key% --non-interactive diff --git a/framework/src/Bing.Aop.AspectCore/Bing.Aop.AspectCore.csproj b/framework/src/Bing.Aop.AspectCore/Bing.Aop.AspectCore.csproj new file mode 100644 index 000000000..4b0e705e9 --- /dev/null +++ b/framework/src/Bing.Aop.AspectCore/Bing.Aop.AspectCore.csproj @@ -0,0 +1,14 @@ + + + Bing.Aop.AspectCore + Bing.Aop.AspectCore + Bing.Aop.AspectCore是Bing应用框架基于 AspectCore 实现AOP拦截功能的库。 + + + + + + + + + diff --git a/framework/src/Bing/Bing/Aspects/AutowiredAttribute.cs b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/AutowiredAttribute.cs similarity index 100% rename from framework/src/Bing/Bing/Aspects/AutowiredAttribute.cs rename to framework/src/Bing.Aop.AspectCore/Bing/Aspects/AutowiredAttribute.cs diff --git a/framework/src/Bing/Bing/Aspects/IgnoreAttribute.cs b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/IgnoreAttribute.cs similarity index 100% rename from framework/src/Bing/Bing/Aspects/IgnoreAttribute.cs rename to framework/src/Bing.Aop.AspectCore/Bing/Aspects/IgnoreAttribute.cs diff --git a/framework/src/Bing.Aop.AspectCore/Bing/Aspects/InterceptorBase.cs b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/InterceptorBase.cs new file mode 100644 index 000000000..1a5c26c73 --- /dev/null +++ b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/InterceptorBase.cs @@ -0,0 +1,9 @@ +namespace Bing.Aspects +{ + /// + /// 拦截器基类 + /// + public abstract class InterceptorBase : AspectCore.DynamicProxy.AbstractInterceptorAttribute + { + } +} diff --git a/framework/src/Bing/Bing/Aspects/NotEmptyAttribute.cs b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/NotEmptyAttribute.cs similarity index 96% rename from framework/src/Bing/Bing/Aspects/NotEmptyAttribute.cs rename to framework/src/Bing.Aop.AspectCore/Bing/Aspects/NotEmptyAttribute.cs index 8e0edd455..f42f2b707 100644 --- a/framework/src/Bing/Bing/Aspects/NotEmptyAttribute.cs +++ b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/NotEmptyAttribute.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using AspectCore.DynamicProxy.Parameters; -using Bing.Aspects.Base; using Bing.Extensions; namespace Bing.Aspects diff --git a/framework/src/Bing/Bing/Aspects/NotNullAttribute.cs b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/NotNullAttribute.cs similarity index 96% rename from framework/src/Bing/Bing/Aspects/NotNullAttribute.cs rename to framework/src/Bing.Aop.AspectCore/Bing/Aspects/NotNullAttribute.cs index 8acf26ae3..ca637a62f 100644 --- a/framework/src/Bing/Bing/Aspects/NotNullAttribute.cs +++ b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/NotNullAttribute.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using AspectCore.DynamicProxy.Parameters; -using Bing.Aspects.Base; namespace Bing.Aspects { diff --git a/framework/src/Bing.Aop.AspectCore/Bing/Aspects/ParameterInterceptorBase.cs b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/ParameterInterceptorBase.cs new file mode 100644 index 000000000..d59049ed0 --- /dev/null +++ b/framework/src/Bing.Aop.AspectCore/Bing/Aspects/ParameterInterceptorBase.cs @@ -0,0 +1,9 @@ +namespace Bing.Aspects +{ + /// + /// 参数拦截器基类 + /// + public abstract class ParameterInterceptorBase : AspectCore.DynamicProxy.Parameters.ParameterInterceptorAttribute + { + } +} diff --git a/framework/src/Bing/Bing/DependencyInjection/Extensions.Aop.cs b/framework/src/Bing.Aop.AspectCore/Bing/DependencyInjection/AspectCoreExtensions.cs similarity index 81% rename from framework/src/Bing/Bing/DependencyInjection/Extensions.Aop.cs rename to framework/src/Bing.Aop.AspectCore/Bing/DependencyInjection/AspectCoreExtensions.cs index f5db14550..aac736396 100644 --- a/framework/src/Bing/Bing/DependencyInjection/Extensions.Aop.cs +++ b/framework/src/Bing.Aop.AspectCore/Bing/DependencyInjection/AspectCoreExtensions.cs @@ -1,8 +1,10 @@ using System; using AspectCore.Configuration; using AspectCore.DynamicProxy; +using AspectCore.DynamicProxy.Parameters; using AspectCore.Extensions.AspectScope; using AspectCore.Extensions.DependencyInjection; +using Bing.Exceptions.Prompts; using Bing.Extensions; using Bing.Reflection; using Microsoft.Extensions.DependencyInjection; @@ -10,9 +12,9 @@ namespace Bing.DependencyInjection { /// - /// AspectCore扩展 + /// AspectCore 扩展 /// - public static partial class Extensions + public static class AspectCoreExtensions { /// /// 启用Aop @@ -24,12 +26,11 @@ public static partial class Extensions /// public static void EnableAop(this IServiceCollection services, Action configAction = null) { + ExceptionPrompt.AddPrompt(new AspectExceptionPrompt()); services.ConfigureDynamicProxy(config => { //config.EnableParameterAspect();// 启用参数拦截,会导致异常不能很好的定位 - config.NonAspectPredicates.Add(t => - Reflections.GetTopBaseType(t.DeclaringType).SafeString() == - "Microsoft.EntityFrameworkCore.DbContext"); + config.NonAspectPredicates.Add(t => Reflections.GetTopBaseType(t.DeclaringType).SafeString() == "Microsoft.EntityFrameworkCore.DbContext"); configAction?.Invoke(config); }); services.EnableAspectScoped(); diff --git a/framework/src/Bing.Aop.AspectCore/Bing/Exceptions/Prompts/AspectExceptionPrompt.cs b/framework/src/Bing.Aop.AspectCore/Bing/Exceptions/Prompts/AspectExceptionPrompt.cs new file mode 100644 index 000000000..687eac130 --- /dev/null +++ b/framework/src/Bing.Aop.AspectCore/Bing/Exceptions/Prompts/AspectExceptionPrompt.cs @@ -0,0 +1,42 @@ +using System; + +namespace Bing.Exceptions.Prompts +{ + /// + /// AOP 异常提示 + /// + public class AspectExceptionPrompt : IExceptionPrompt + { + /// + /// 获取异常提示 + /// + /// 异常 + public string GetPrompt(Exception exception) + { + if (exception == null) + return null; + if (exception is AspectCore.DynamicProxy.AspectInvocationException aspectInvocationException) + { + return aspectInvocationException.InnerException is null + ? aspectInvocationException.Message + : GetRawException(aspectInvocationException.InnerException).Message; + } + return string.Empty; + } + + /// + /// 获取原始异常 + /// + /// 异常 + public Exception GetRawException(Exception exception) + { + if (exception is AspectCore.DynamicProxy.AspectInvocationException aspectInvocationException) + { + if (aspectInvocationException.InnerException == null) + return aspectInvocationException; + return GetRawException(aspectInvocationException.InnerException); + } + return exception; + } + } +} diff --git a/framework/src/Bing.Aop.AspectCore/dependency.props b/framework/src/Bing.Aop.AspectCore/dependency.props new file mode 100644 index 000000000..04554f27f --- /dev/null +++ b/framework/src/Bing.Aop.AspectCore/dependency.props @@ -0,0 +1,11 @@ + + + 2.2.0 + + + + + + + + \ No newline at end of file diff --git a/framework/src/Bing.Aop.AspectCore/references.props b/framework/src/Bing.Aop.AspectCore/references.props new file mode 100644 index 000000000..a4ddeda5d --- /dev/null +++ b/framework/src/Bing.Aop.AspectCore/references.props @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/IModelStateValidator.cs b/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/IModelStateValidator.cs index 43533adc1..d227a0cec 100644 --- a/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/IModelStateValidator.cs +++ b/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/IModelStateValidator.cs @@ -1,4 +1,4 @@ -using Bing.Validations.Abstractions; +using Bing.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding; namespace Bing.AspNetCore.Mvc.Validation diff --git a/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/ModelStateValidator.cs b/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/ModelStateValidator.cs index e56698ab9..91475fd89 100644 --- a/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/ModelStateValidator.cs +++ b/framework/src/Bing.AspNetCore.Mvc/Bing/AspNetCore/Mvc/Validation/ModelStateValidator.cs @@ -1,6 +1,5 @@ using System.ComponentModel.DataAnnotations; -using Bing.Validations; -using Bing.Validations.Abstractions; +using Bing.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding; namespace Bing.AspNetCore.Mvc.Validation diff --git a/framework/src/Bing.AspNetCore/Bing.AspNetCore.csproj b/framework/src/Bing.AspNetCore/Bing.AspNetCore.csproj index 20006af65..3a1960359 100644 --- a/framework/src/Bing.AspNetCore/Bing.AspNetCore.csproj +++ b/framework/src/Bing.AspNetCore/Bing.AspNetCore.csproj @@ -4,4 +4,19 @@ + + + + True + True + MvcModelBindingResource.resx + + + + + + ResXFileCodeGenerator + MvcModelBindingResource.Designer.cs + + diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ApplicationBuilder.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ApplicationBuilder.cs index 6808cf0d3..d84d367e7 100644 --- a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ApplicationBuilder.cs +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ApplicationBuilder.cs @@ -52,5 +52,12 @@ public static IWebHostBuilder UseRealIp(this IWebHostBuilder hostBuilder, string return hostBuilder; } + + /// + /// 注册请求响应日志中间件。必须调用 services.AddRequestResponseLog 方法方可正常使用 + /// + /// 应用程序生成器 + /// + public static IApplicationBuilder UseRequestResponseLog(this IApplicationBuilder builder) => builder.UseMiddleware(); } } diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ServiceCollection.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ServiceCollection.cs index 5f0bff0e8..50a429c48 100644 --- a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ServiceCollection.cs +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Extensions/Extensions.ServiceCollection.cs @@ -1,4 +1,6 @@ -using Bing.AspNetCore.Mvc; +using System; +using Bing.AspNetCore.Logs; +using Bing.AspNetCore.Mvc; using Bing.AspNetCore.Uploads; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -36,5 +38,17 @@ public static class BingServiceCollectionExtensions /// 服务集合 public static void AddApiInterfaceService(this IServiceCollection services) where TApiInterfaceService : class, IApiInterfaceService => services.TryAddSingleton(); + + /// + /// 注册请求响应日志服务 + /// + /// 服务集合 + /// 配置操作 + public static void AddRequestResponseLog(this IServiceCollection services, Action setupAction) + { + services.Configure(setupAction); + services.AddSingleton(); + services.AddScoped(); + } } } diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/DefaultRequestResponseLogCreator.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/DefaultRequestResponseLogCreator.cs new file mode 100644 index 000000000..4f73a6edf --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/DefaultRequestResponseLogCreator.cs @@ -0,0 +1,30 @@ +using Bing.Utils.Json; + +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应日志创建者 + /// + public class DefaultRequestResponseLogCreator : IRequestResponseLogCreator + { + /// + /// 初始化一个类型的实例s + /// + public DefaultRequestResponseLogCreator() => Log = new RequestResponseLog(); + + /// + /// 请求响应日志 + /// + public RequestResponseLog Log { get; private set; } + + /// + /// 输出Json字符串 + /// + public string ToJsonString() + { + var jsonString = JsonHelper.ToJson(Log); + return jsonString; + } + + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/DefaultRequestResponseLogger.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/DefaultRequestResponseLogger.cs new file mode 100644 index 000000000..49924ace2 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/DefaultRequestResponseLogger.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.Logging; + +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应记录器 + /// + public class DefaultRequestResponseLogger : IRequestResponseLogger + { + /// + /// 日志记录器 + /// + private readonly ILogger _logger; + + /// + /// 初始化一个类型的实例 + /// + /// 日志记录器 + public DefaultRequestResponseLogger(ILogger logger) => _logger = logger; + + /// + /// 写入日志 + /// + /// 请求响应日志创建者 + public void Log(IRequestResponseLogCreator logCreator) => _logger.LogDebug(logCreator.ToJsonString()); + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/IRequestResponseLogCreator.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/IRequestResponseLogCreator.cs new file mode 100644 index 000000000..23192472d --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/IRequestResponseLogCreator.cs @@ -0,0 +1,18 @@ +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应日志创建者 + /// + public interface IRequestResponseLogCreator + { + /// + /// 请求响应日志 + /// + RequestResponseLog Log { get; } + + /// + /// 输出Json字符串 + /// + string ToJsonString(); + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/IRequestResponseLogger.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/IRequestResponseLogger.cs new file mode 100644 index 000000000..176827768 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/IRequestResponseLogger.cs @@ -0,0 +1,14 @@ +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应记录器 + /// + public interface IRequestResponseLogger + { + /// + /// 写入日志 + /// + /// 请求响应日志创建者 + void Log(IRequestResponseLogCreator logCreator); + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestLogMiddleware.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestLogMiddleware.cs index fd2173e90..6d846d41c 100644 --- a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestLogMiddleware.cs +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestLogMiddleware.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading.Tasks; @@ -12,6 +13,7 @@ namespace Bing.AspNetCore.Logs /// /// 请求日志中间件 /// + [Obsolete("请使用 RequestResponseLoggerMiddleware 中间件")] public class RequestLogMiddleware : IMiddleware { /// diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLog.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLog.cs new file mode 100644 index 000000000..de77f2db5 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLog.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; + +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应日志 + /// + public class RequestResponseLog + { + /// + /// 初始化一个类型的实例 + /// + public RequestResponseLog() => LogId = Guid.NewGuid().ToString(); + + /// + /// 日志标识 + /// + /// 默认:Guid.NewGuid().ToString() + public string LogId { get; set; } + + /// + /// 节点(项目名称) + /// + public string Node { get; set; } + + /// + /// 客户端IP + /// + public string ClientIp { get; set; } + + /// + /// 跟踪标识 + /// + /// 默认:HttpContext.TraceIdentifier + public string TraceId { get; set; } + + /// + /// 请求时间(UTC) + /// + public DateTime? RequestDateTimeUtc { get; set; } + + /// + /// 请求时间(UTC)操作层面 + /// + public DateTime? RequestDateTimeUtcActionLevel { get; set; } + + /// + /// 请求路径 + /// + public string RequestPath { get; set; } + + /// + /// 请求查询 + /// + public string RequestQuery { get; set; } + + /// + /// 请求查询列表 + /// + public List> RequestQueries { get; set; } + + /// + /// 请求方法 + /// + public string RequestMethod { get; set; } + + /// + /// 请求格式 + /// + public string RequestScheme { get; set; } + + /// + /// 请求主机 + /// + public string RequestHost { get; set; } + + /// + /// 请求头 + /// + public Dictionary RequestHeaders { get; set; } + + /// + /// 请求Cookie + /// + public Dictionary RequestCookies { get; set; } + + /// + /// 请求正文 + /// + public string RequestBody { get; set; } + + /// + /// 请求内容类型 + /// + public string RequestContentType { get; set; } + + /// + /// 响应时间(UTC) + /// + public DateTime? ResponseDateTimeUtc { get; set; } + + /// + /// 响应时间(UTC)操作层面 + /// + public DateTime? ResponseDateTimeUtcActionLevel { get; set; } + + /// + /// 响应状态 + /// + public string ResponseStatus { get; set; } + + /// + /// 响应头 + /// + public Dictionary ResponseHeaders { get; set; } + + /// + /// 响应正文 + /// + public string ResponseBody { get; set; } + + /// + /// 响应内容类型 + /// + public string ResponseContentType { get; set; } + + /// + /// 是否异常操作层面 + /// + public bool? IsExceptionActionLevel { get; set; } + + /// + /// 异常消息 + /// + public string ExceptionMessage { get; set; } + + /// + /// 异常堆栈跟踪 + /// + public string ExceptionStackTrace { get; set; } + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerActionFilter.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerActionFilter.cs new file mode 100644 index 000000000..36cdd9e67 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerActionFilter.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; + +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应记录器 操作过滤器 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class RequestResponseLoggerActionFilter : Attribute, IActionFilter + { + /// + /// 获取请求响应日志 + /// + /// Http上下文 + private RequestResponseLog GetLog(HttpContext context) + { + return context.RequestServices.GetRequiredService().Log; + } + + /// + /// 执行操作之前。在模型绑定完成之后,在执行操作之前调用。 + /// + /// 操作执行上下文 + public void OnActionExecuting(ActionExecutingContext context) + { + var log = GetLog(context.HttpContext); + log.RequestDateTimeUtcActionLevel = DateTime.UtcNow; + } + + /// + /// 执行操作之后。在操作执行之后,操作结果执行之前调用。 + /// + /// 已执行操作上下文 + public void OnActionExecuted(ActionExecutedContext context) + { + var log = GetLog(context.HttpContext); + log.ResponseDateTimeUtcActionLevel = DateTime.UtcNow; + } + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerErrorFilter.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerErrorFilter.cs new file mode 100644 index 000000000..761e292e3 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerErrorFilter.cs @@ -0,0 +1,34 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; + +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应记录器 错误过滤器 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class RequestResponseLoggerErrorFilter : Attribute, IExceptionFilter + { + /// + /// 获取请求响应日志 + /// + /// Http上下文 + private RequestResponseLog GetLog(HttpContext context) + { + return context.RequestServices.GetRequiredService().Log; + } + + /// + /// 执行操作异常 + /// + /// 异常上下文 + public void OnException(ExceptionContext context) + { + var log = GetLog(context.HttpContext); + log.IsExceptionActionLevel = true; + log.RequestDateTimeUtcActionLevel ??= DateTime.UtcNow; + } + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerMiddleware.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerMiddleware.cs new file mode 100644 index 000000000..c392fe179 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerMiddleware.cs @@ -0,0 +1,290 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Bing.Helpers; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应记录器中间件 + /// + public class RequestResponseLoggerMiddleware : IMiddleware + { + /// + /// 方法 + /// + private readonly RequestDelegate _next; + + /// + /// 请求响应记录器选项 + /// + private readonly RequestResponseLoggerOptions _options; + + /// + /// 请求响应记录器 + /// + private readonly IRequestResponseLogger _logger; + + /// + /// 初始化一个类型的实例 + /// + /// 方法 + /// 请求响应记录器选项 + /// 请求响应记录器 + public RequestResponseLoggerMiddleware(RequestDelegate next, IOptions options, IRequestResponseLogger logger) + { + _next = next; + _options = options.Value; + _logger = logger; + } + + /// + /// 执行中间件拦截逻辑 + /// + /// Http上下文 + public async Task InvokeAsync(HttpContext context) + { + if (_options == null || !_options.IsEnabled || FilterRequest(context)) + { + await _next(context); + return; + } + var logCreator = context?.RequestServices?.GetRequiredService(); + var log = logCreator?.Log; + if (log == null) + { + await _next(context); + return; + } + + log.RequestDateTimeUtc = DateTime.UtcNow; + var request = context.Request; + + // log + log.LogId = Guid.NewGuid().ToString(); + log.TraceId = context.TraceIdentifier; + var ip = request.HttpContext.Connection.RemoteIpAddress; + log.ClientIp = ip?.ToString(); + log.Node = _options.Name; + + // request + log.RequestMethod = request.Method; + log.RequestPath = request.Path; + log.RequestQuery = request.QueryString.ToString(); + log.RequestQueries = FormatQueries(request.QueryString.ToString()); + log.RequestHeaders = _options.WithHeader ? FormatHeaders(request.Headers) : null; + log.RequestCookies = _options.WithCookie ? FormatCookies(request.Cookies) : null; + log.RequestBody = await ReadBodyFromRequest(request); + log.RequestScheme = request.Scheme; + log.RequestHost = request.Host.ToString(); + log.RequestContentType = request.ContentType; + + // 暂时用 MemoryStream 替换 HttpResponseStream,用于获取其运行中的值 + var response = context.Response; + var originalResponseBody = response.Body; + using var newResponseBody = new MemoryStream(); + response.Body = newResponseBody; + + // 调用管道中的下一个中间件 + try + { + await _next(context); + } + catch (Exception e) + { + // 异常:app.UseExceptionHandler() 或者 中间件 + LogError(log, e); + } + + newResponseBody.Seek(0, SeekOrigin.Begin); + var responseBodyText = await ReadBodyFromResponse(context); + newResponseBody.Seek(0, SeekOrigin.Begin); + + await newResponseBody.CopyToAsync(originalResponseBody); + + // response + log.ResponseContentType = response.ContentType; + log.ResponseStatus = response.StatusCode.ToString(); + log.ResponseHeaders = _options.WithHeader ? FormatHeaders(response.Headers) : null; + log.ResponseBody = _options.WithResponse ? responseBodyText : null; + log.ResponseDateTimeUtc = DateTime.UtcNow; + +#if NETCOREAPP3_1_OR_GREATER + var contextFeature = context.Features.Get(); + if (contextFeature != null && contextFeature.Error != null) + { + var exception = contextFeature.Error; + LogError(log, exception); + } +#endif + + _logger.Log(logCreator); + } + + /// + /// 记录错误 + /// + /// 请求响应日志 + /// 异常 + private void LogError(RequestResponseLog log, Exception exception) + { + log.ExceptionMessage = exception.Message; + log.ExceptionStackTrace = exception.StackTrace; + } + + /// + /// 格式化请求头 + /// + /// 请求头 + private Dictionary FormatHeaders(IHeaderDictionary headers) + { + var pairs = new Dictionary(); + foreach (var header in headers) + pairs.Add(header.Key, header.Value); + return pairs; + } + + /// + /// 格式化Cookies + /// + /// Cookie集合 + private Dictionary FormatCookies(IRequestCookieCollection cookies) + { + var pairs = new Dictionary(); + foreach (var cookie in cookies) + pairs.Add(cookie.Key, cookie.Value); + return pairs; + } + + /// + /// 格式化查询字符串 + /// + /// 查询字符串 + private List> FormatQueries(string queryString) + { + var pairs = new List>(); + foreach (var query in queryString.TrimStart('?').Split('&')) + { + var items = query.Split('='); + var key = items.Any() ? items[0] : string.Empty; + var value = items.Length >= 2 ? items[1] : string.Empty; + if (!string.IsNullOrEmpty(key)) + pairs.Add(new KeyValuePair(key, value)); + } + return pairs; + } + + /// + /// 从请求中读取正文内容 + /// + /// Http请求 + private async Task ReadBodyFromRequest(HttpRequest request) + { + // 确保可以多次读取请求的正文,用于管道中的下一个中间件 + request.EnableBuffering(); +#if NETCOREAPP3_1_OR_GREATER + using var streamReader = new StreamReader(request.Body, leaveOpen: true); +#else + using var streamReader = new StreamReader(request.Body, System.Text.Encoding.UTF8, true, 1024, true); +#endif + var requestBody = await streamReader.ReadToEndAsync(); + // 重置请求的主体流位置,用于管道中的下一个中间件 + request.Body.Position = 0; + return requestBody; + } + + /// + /// 从响应中读取正文内容 + /// + /// Http上下文 + private async Task ReadBodyFromResponse(HttpContext context) + { + if (string.IsNullOrWhiteSpace(context.Response.ContentType) || !_options.WithResponse) + { + if (context.Response.Body.CanSeek) + context.Response.Body.Seek(0, SeekOrigin.Begin); + return string.Empty; + } + + if (FilterStaticFiles(context)) + { + if (context.Response.Body.CanSeek) + context.Response.Body.Seek(0, SeekOrigin.Begin); + return string.Empty; + } + + var result = string.Empty; + context.Response.Body.Seek(0, SeekOrigin.Begin); + + Stream source = null; + if (context.Response.Headers.ContainsKey("Content-Encoding")) + { + var contentEncoding = context.Response.Headers["Content-Encoding"].ToString(); + switch (contentEncoding) + { + case "gzip": + source = new GZipStream(context.Response.Body, CompressionMode.Decompress); + break; + case "deflate": + source = new DeflateStream(context.Response.Body, CompressionMode.Decompress); + break; +#if NETCOREAPP3_1_OR_GREATER + case "br": + source = new BrotliStream(context.Response.Body, CompressionMode.Decompress); + break; +#endif + } + } + + source ??= context.Response.Body; + + var responseBodyText = await new StreamReader(source, Encoding.UTF8).ReadToEndAsync(); + context.Response.Body.Seek(0, SeekOrigin.Begin); + return responseBodyText; + } + + /// + /// 过滤静态文件 + /// + /// Http上下文 + private bool FilterStaticFiles(HttpContext context) + { + if (!string.IsNullOrWhiteSpace(context.Request.ContentType) && context.Request.ContentType.Contains("application/grpc")) + return false; + if (context.Request.Method.ToLowerInvariant() == "options") + return true; + if (context.Request.Path.HasValue && context.Request.Path.Value.Contains(".")) + return true; + return false; + } + + /// + /// 过滤请求 + /// + /// Http上下文 + private bool FilterRequest(HttpContext context) + { + // 过滤上传文件请求 + if (context.Request.HasFormContentType && context.Request.Form != null && context.Request.Form.Files != null && context.Request.Form.Files.Any()) + return true; + // 过滤请求数据 + if (_options.RequestFilter == null || _options.RequestFilter.Count == 0) + return false; + // 请求路径 - 规则匹配 + var path = context.Request.Path.Value.ToLowerInvariant(); + foreach (var item in _options.RequestFilter) + { + if (FastPathMatcher.Match(item, path)) + return true; + } + return false; + } + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerOptions.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerOptions.cs new file mode 100644 index 000000000..2aab91c7e --- /dev/null +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/Logs/RequestResponseLoggerOptions.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; + +namespace Bing.AspNetCore.Logs +{ + /// + /// 请求响应记录器选项 + /// + public class RequestResponseLoggerOptions + { + /// + /// 是否开启收集数据 + /// + public bool IsEnabled { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 是否记录接口的入参 + /// + public bool WithRequest { get; set; } = false; + + /// + /// 是否记录接口的出参 + /// + public bool WithResponse { get; set; } = false; + + /// + /// 是否记录Cookie信息 + /// + public bool WithCookie { get; set; } = false; + + /// + /// 是否记录请求头信息 + /// + public bool WithHeader { get; set; } = false; + + /// + /// 请求数据过滤,用 * 来模糊匹配 + /// + public List RequestFilter { get; set; } = new(); + + /// + /// 日期时间格式 + /// + public string DateTimeFormat { get; set; } + } +} diff --git a/framework/src/Bing.AspNetCore/Bing/AspNetCore/RealIp/RealIpMiddleware.cs b/framework/src/Bing.AspNetCore/Bing/AspNetCore/RealIp/RealIpMiddleware.cs index 9c4bcaaff..903086bbd 100644 --- a/framework/src/Bing.AspNetCore/Bing/AspNetCore/RealIp/RealIpMiddleware.cs +++ b/framework/src/Bing.AspNetCore/Bing/AspNetCore/RealIp/RealIpMiddleware.cs @@ -52,20 +52,44 @@ public async Task InvokeAsync(HttpContext context) var headers = context.Request.Headers; try { - if (headers.ContainsKey(_options.HeaderKey)) - { - context.Connection.RemoteIpAddress = IPAddress.Parse( - _options.HeaderKey.Equals("x-forwarded-for", StringComparison.CurrentCultureIgnoreCase) - ? headers["X-Forwarded-For"].ToString().Split(',')[0] - : headers[_options.HeaderKey].ToString()); - _logger.LogDebug($"解析真实IP成功: {context.Connection.RemoteIpAddress}"); - } + var ip = TryGetIpAddress(headers, _options.HeaderKey) ?? TryGetIpAddress(headers, "x-forwarded-for") ?? TryGetIpAddress(headers, "X-Forwarded-For"); + if (ip != null) + context.Connection.RemoteIpAddress = ip; } finally { await _next(context); } } + + /// + /// 尝试获取IP地址 + /// + /// 请求头字典 + /// 请求头 + private IPAddress TryGetIpAddress(IHeaderDictionary headers, string key) + { + if (headers.ContainsKey(key)) + { + headers.TryGetValue(key, out var ip); + _logger.LogDebug($"解析真实IP地址: {ip}"); + if (string.IsNullOrEmpty(ip) == false && ip.ToString().ToLower() != "unknown") + { + var tmpIp = key.Equals("x-forwarded-for", StringComparison.CurrentCultureIgnoreCase) + ? ip.ToString().Split(',')[0] + : ip.ToString(); + if (IPAddress.TryParse(tmpIp, out var ipAddress)) + { + _logger.LogDebug($"解析真实IP成功: {ipAddress}"); + return ipAddress; + } + + _logger.LogError($"解析真实IP失败: {tmpIp}"); + } + } + + return null; + } } /// diff --git a/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/MvcModelBindingResource.Designer.cs b/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/MvcModelBindingResource.Designer.cs new file mode 100644 index 000000000..85a07fed7 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/MvcModelBindingResource.Designer.cs @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MvcModelBindingResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MvcModelBindingResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.MvcModelBindingResource", typeof(MvcModelBindingResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 '{0}' 不能作为 {1} 的值 的本地化字符串。 + /// + internal static string AttemptedValueIsInvalidAccessor { + get { + return ResourceManager.GetString("AttemptedValueIsInvalidAccessor", resourceCulture); + } + } + + /// + /// 查找类似 没有为属性 '{0}' 指定值 的本地化字符串。 + /// + internal static string MissingBindRequiredValueAccessor { + get { + return ResourceManager.GetString("MissingBindRequiredValueAccessor", resourceCulture); + } + } + + /// + /// 查找类似 必须指定值 的本地化字符串。 + /// + internal static string MissingKeyOrValueAccessor { + get { + return ResourceManager.GetString("MissingKeyOrValueAccessor", resourceCulture); + } + } + + /// + /// 查找类似 请求正文不能为空 的本地化字符串。 + /// + internal static string MissingRequestBodyRequiredValueAccessor { + get { + return ResourceManager.GetString("MissingRequestBodyRequiredValueAccessor", resourceCulture); + } + } + + /// + /// 查找类似 '{0}' 是无效的值 的本地化字符串。 + /// + internal static string NonPropertyAttemptedValueIsInvalidAccessor { + get { + return ResourceManager.GetString("NonPropertyAttemptedValueIsInvalidAccessor", resourceCulture); + } + } + + /// + /// 查找类似 指定的值无效 的本地化字符串。 + /// + internal static string NonPropertyUnknownValueIsInvalidAccessor { + get { + return ResourceManager.GetString("NonPropertyUnknownValueIsInvalidAccessor", resourceCulture); + } + } + + /// + /// 查找类似 字段的值应该是数字 的本地化字符串。 + /// + internal static string NonPropertyValueMustBeANumberAccessor { + get { + return ResourceManager.GetString("NonPropertyValueMustBeANumberAccessor", resourceCulture); + } + } + + /// + /// 查找类似 为 {0} 指定的值无效 的本地化字符串。 + /// + internal static string UnknownValueIsInvalidAccessor { + get { + return ResourceManager.GetString("UnknownValueIsInvalidAccessor", resourceCulture); + } + } + + /// + /// 查找类似 '{0}' 是无效的值 的本地化字符串。 + /// + internal static string ValueIsInvalidAccessor { + get { + return ResourceManager.GetString("ValueIsInvalidAccessor", resourceCulture); + } + } + + /// + /// 查找类似 字段 {0} 的值应该是数字 的本地化字符串。 + /// + internal static string ValueMustBeANumberAccessor { + get { + return ResourceManager.GetString("ValueMustBeANumberAccessor", resourceCulture); + } + } + + /// + /// 查找类似 '{0}' 是无效的值 的本地化字符串。 + /// + internal static string ValueMustNotBeNullAccessor { + get { + return ResourceManager.GetString("ValueMustNotBeNullAccessor", resourceCulture); + } + } + } +} diff --git a/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/MvcModelBindingResource.resx b/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/MvcModelBindingResource.resx new file mode 100644 index 000000000..34c800ce4 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/MvcModelBindingResource.resx @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + '{0}' 不能作为 {1} 的值 + The value '{0}' is not valid for {1}. + + + 没有为属性 '{0}' 指定值 + A value for the '{0}' parameter or property was not provided. + + + 必须指定值 + A value is required. + + + 请求正文不能为空 + A non-empty request body is required. + + + '{0}' 是无效的值 + The value '{0}' is not valid. + + + 指定的值无效 + The supplied value is invalid. + + + 字段的值应该是数字 + The field must be a number. + + + 为 {0} 指定的值无效 + The supplied value is invalid for {0}. + + + '{0}' 是无效的值 + The value '{0}' is invalid. + + + 字段 {0} 的值应该是数字 + The field {0} must be a number. + + + '{0}' 是无效的值 + The value '{0}' is invalid. + + \ No newline at end of file diff --git a/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/ResourceModelBindingMessageProviderExtensions.cs b/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/ResourceModelBindingMessageProviderExtensions.cs new file mode 100644 index 000000000..872040127 --- /dev/null +++ b/framework/src/Bing.AspNetCore/Microsoft/AspNetCore/Mvc/ModelBinding/Metadata/ResourceModelBindingMessageProviderExtensions.cs @@ -0,0 +1,44 @@ +using System; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata +{ + /// + /// 模型绑定消息提供程序()扩展 + /// + public static class ResourceModelBindingMessageProviderExtensions + { + /// + /// 应用翻译资源 + /// + /// 模型绑定消息提供程序 + public static void UseTranslatedResources(this DefaultModelBindingMessageProvider messageProvider) + { + messageProvider.SetMissingBindRequiredValueAccessor(One(MvcModelBindingResource.MissingBindRequiredValueAccessor)); + messageProvider.SetMissingKeyOrValueAccessor(Zero(MvcModelBindingResource.MissingKeyOrValueAccessor)); + messageProvider.SetMissingRequestBodyRequiredValueAccessor(Zero(MvcModelBindingResource.MissingRequestBodyRequiredValueAccessor)); + messageProvider.SetValueMustNotBeNullAccessor(One(MvcModelBindingResource.ValueMustNotBeNullAccessor)); + messageProvider.SetAttemptedValueIsInvalidAccessor(Two(MvcModelBindingResource.AttemptedValueIsInvalidAccessor)); + messageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(One(MvcModelBindingResource.NonPropertyAttemptedValueIsInvalidAccessor)); + messageProvider.SetUnknownValueIsInvalidAccessor(One(MvcModelBindingResource.UnknownValueIsInvalidAccessor)); + messageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(Zero(MvcModelBindingResource.NonPropertyUnknownValueIsInvalidAccessor)); + messageProvider.SetValueIsInvalidAccessor(One(MvcModelBindingResource.ValueIsInvalidAccessor)); + messageProvider.SetValueMustBeANumberAccessor(One(MvcModelBindingResource.ValueMustBeANumberAccessor)); + messageProvider.SetNonPropertyValueMustBeANumberAccessor(Zero(MvcModelBindingResource.NonPropertyValueMustBeANumberAccessor)); + } + + /// + /// 0个参字符串格式化 + /// + private static Func Zero(string resource) => () => resource; + + /// + /// 1个参数字符串格式化 + /// + private static Func One(string resource) => arg => string.Format(resource, arg); + + /// + /// 2个参数字符串格式化 + /// + private static Func Two(string resource) => (arg1, arg2) => string.Format(resource, arg1, arg2); + } +} diff --git a/framework/src/Bing.AspNetCore/Microsoft/Extensions/DependencyInjection/BingAspNetCoreServiceCollectionExtensions.cs b/framework/src/Bing.AspNetCore/Microsoft/Extensions/DependencyInjection/BingAspNetCoreServiceCollectionExtensions.cs index 9405d21f3..c6661622f 100644 --- a/framework/src/Bing.AspNetCore/Microsoft/Extensions/DependencyInjection/BingAspNetCoreServiceCollectionExtensions.cs +++ b/framework/src/Bing.AspNetCore/Microsoft/Extensions/DependencyInjection/BingAspNetCoreServiceCollectionExtensions.cs @@ -12,7 +12,11 @@ public static class BingAspNetCoreServiceCollectionExtensions /// 获取环境信息 /// /// 服务集合 - public static IWebHostEnvironment GetWebHostEnvironment(this IServiceCollection services) => services.GetSingletonInstance(); + public static IWebHostEnvironment GetWebHostEnvironment(this IServiceCollection services) + { + var hostingEnvironment = services.GetSingletonInstanceOrNull(); + return hostingEnvironment ?? new EmptyHostingEnvironment { EnvironmentName = Microsoft.Extensions.Hosting.Environments.Development }; + } #elif NETSTANDARD2_0 /// /// 获取环境信息 diff --git a/framework/src/Bing.AspNetCore/Microsoft/Extensions/DependencyInjection/EmptyHostingEnvironment.cs b/framework/src/Bing.AspNetCore/Microsoft/Extensions/DependencyInjection/EmptyHostingEnvironment.cs new file mode 100644 index 000000000..c56c6730a --- /dev/null +++ b/framework/src/Bing.AspNetCore/Microsoft/Extensions/DependencyInjection/EmptyHostingEnvironment.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.FileProviders; + +namespace Microsoft.Extensions.DependencyInjection +{ +#if NETCOREAPP3_0 || NETCOREAPP3_1 || NET5_0 + /// + /// 空主机环境变量 + /// + internal class EmptyHostingEnvironment : Microsoft.AspNetCore.Hosting.IWebHostEnvironment + { + /// + /// 环境名称 + /// + public string EnvironmentName { get; set; } + + /// + /// 应用程序名称 + /// + public string ApplicationName { get; set; } + + /// + /// Web根路径,即wwwroot + /// + public string WebRootPath { get; set; } + + /// + /// Web根路径文件提供程序 + /// + public IFileProvider WebRootFileProvider { get; set; } + + /// + /// 根路径 + /// + public string ContentRootPath { get; set; } + + /// + /// 根路径文件提供程序 + /// + public IFileProvider ContentRootFileProvider { get; set; } + + } +#endif +} diff --git a/framework/src/Bing.Auditing/Bing.Auditing.csproj b/framework/src/Bing.Auditing/Bing.Auditing.csproj index e9503931b..e5971f447 100644 --- a/framework/src/Bing.Auditing/Bing.Auditing.csproj +++ b/framework/src/Bing.Auditing/Bing.Auditing.csproj @@ -5,4 +5,8 @@ + + + + diff --git a/framework/src/Bing.Auditing/Bing/Auditing/AuditPropertySetter.cs b/framework/src/Bing.Auditing/Bing/Auditing/AuditPropertySetter.cs new file mode 100644 index 000000000..a0314d081 --- /dev/null +++ b/framework/src/Bing.Auditing/Bing/Auditing/AuditPropertySetter.cs @@ -0,0 +1,301 @@ +using System; +using Bing.DependencyInjection; +using Bing.Extensions; +using Bing.Users; + +namespace Bing.Auditing +{ + /// + /// 设计属性设置其 + /// + public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency + { + /// + /// 初始化一个类型的实例 + /// + /// 当前用户 + public AuditPropertySetter(ICurrentUser currentUser) + { + CurrentUser = currentUser; + } + + /// + /// 当前用户 + /// + protected ICurrentUser CurrentUser { get; } + + /// + /// 设置创建属性 + /// + /// 目标对象 + public virtual void SetCreationProperties(object targetObject) + { + if (targetObject == null) + return; + SetCreationTime(targetObject); + SetCreatorId(targetObject); + SetCreator(targetObject); + } + + /// + /// 设置修改属性 + /// + /// 目标对象 + public virtual void SetModificationProperties(object targetObject) + { + if (targetObject == null) + return; + SetLastModificationTime(targetObject); + SetLastModifierId(targetObject); + SetLastModifier(targetObject); + } + + /// + /// 设置删除属性 + /// + /// 目标对象 + public virtual void SetDeletionProperties(object targetObject) + { + if (targetObject == null) + return; + SetDeletionTime(targetObject); + SetDeleterId(targetObject); + SetDeleter(targetObject); + } + + /// + /// 设置创建时间 + /// + /// 目标对象 + protected virtual void SetCreationTime(object targetObject) + { + if (targetObject is not IHasCreationTime objectWithCreationTime) + return; + objectWithCreationTime.CreationTime ??= DateTime.Now; + } + + /// + /// 设置创建人标识 + /// + /// 目标对象 + protected virtual void SetCreatorId(object targetObject) + { + if (string.IsNullOrWhiteSpace(CurrentUser.UserId)) + return; + switch (targetObject) + { + case ICreationAuditedObject userIdWithGuid: + if (userIdWithGuid.CreatorId != default && userIdWithGuid.CreatorId != Guid.Empty) + return; + userIdWithGuid.CreatorId = CurrentUser.UserId.ToGuid(); + return; + + case ICreationAuditedObject userIdWithNullableGuid: + if (userIdWithNullableGuid.CreatorId.HasValue + && userIdWithNullableGuid.CreatorId.Value != default + && userIdWithNullableGuid.CreatorId.Value != Guid.Empty) + return; + userIdWithNullableGuid.CreatorId = CurrentUser.UserId.ToGuidOrNull(); + return; + + case ICreationAuditedObject userIdWithInt: + if (userIdWithInt.CreatorId != default) + return; + userIdWithInt.CreatorId = CurrentUser.UserId.ToInt(); + return; + + case ICreationAuditedObject userIdWithNullableInt: + if (userIdWithNullableInt.CreatorId.HasValue + && userIdWithNullableInt.CreatorId.Value != default) + return; + userIdWithNullableInt.CreatorId = CurrentUser.UserId.ToIntOrNull(); + return; + + case ICreationAuditedObject userIdWithString: + if (!string.IsNullOrWhiteSpace(userIdWithString.CreatorId) && userIdWithString.CreatorId != default) + return; + userIdWithString.CreatorId = CurrentUser.UserId.SafeString(); + return; + + case ICreationAuditedObject userIdWithLong: + if (userIdWithLong.CreatorId != default) + return; + userIdWithLong.CreatorId = CurrentUser.UserId.ToLong(); + return; + + case ICreationAuditedObject userIdWithNullableLong: + if (userIdWithNullableLong.CreatorId.HasValue + && userIdWithNullableLong.CreatorId.Value != default) + return; + userIdWithNullableLong.CreatorId = CurrentUser.UserId.ToLongOrNull(); + return; + } + } + + /// + /// 设置创建人 + /// + /// 目标对象 + protected virtual void SetCreator(object targetObject) + { + var userName = GetUserName(); + if (string.IsNullOrWhiteSpace(userName)) + return; + if (targetObject is IHasCreator objectWithCreator) + objectWithCreator.Creator = userName; + } + + /// + /// 设置修改时间 + /// + /// 目标对象 + protected virtual void SetLastModificationTime(object targetObject) + { + if (targetObject is IHasModificationTime objectWithModificationTime) + objectWithModificationTime.LastModificationTime = DateTime.Now; + } + + /// + /// 设置修改人标识 + /// + /// 目标对象 + protected virtual void SetLastModifierId(object targetObject) + { + if (string.IsNullOrWhiteSpace(CurrentUser.UserId)) + return; + switch (targetObject) + { + case IModificationAuditedObject userIdWithGuid: + userIdWithGuid.LastModifierId = CurrentUser.UserId.ToGuid(); + return; + + case IModificationAuditedObject userIdWithNullableGuid: + userIdWithNullableGuid.LastModifierId = CurrentUser.UserId.ToGuidOrNull(); + return; + + case IModificationAuditedObject userIdWithInt: + userIdWithInt.LastModifierId = CurrentUser.UserId.ToInt(); + return; + + case IModificationAuditedObject userIdWithNullableInt: + userIdWithNullableInt.LastModifierId = CurrentUser.UserId.ToIntOrNull(); + return; + + case IModificationAuditedObject userIdWithString: + userIdWithString.LastModifierId = CurrentUser.UserId.SafeString(); + return; + + case IModificationAuditedObject userIdWithLong: + userIdWithLong.LastModifierId = CurrentUser.UserId.ToLong(); + return; + + case IModificationAuditedObject userIdWithNullableLong: + userIdWithNullableLong.LastModifierId = CurrentUser.UserId.ToLongOrNull(); + return; + } + } + + /// + /// 设置修改人 + /// + /// 目标对象 + protected virtual void SetLastModifier(object targetObject) + { + var userName = GetUserName(); + if (string.IsNullOrWhiteSpace(userName)) + return; + if (targetObject is IHasModifier objectWithModifier) + objectWithModifier.LastModifier = userName; + } + + /// + /// 设置删除时间 + /// + /// 目标对象 + protected virtual void SetDeletionTime(object targetObject) + { + if (targetObject is IHasDeletionTime objectWithDeletionTime) + objectWithDeletionTime.DeletionTime = DateTime.Now; + } + + /// + /// 设置删除人标识 + /// + /// 目标对象 + protected virtual void SetDeleterId(object targetObject) + { + if (string.IsNullOrWhiteSpace(CurrentUser.UserId)) + return; + switch (targetObject) + { + case IDeletionAuditedObject userIdWithGuid: + if (userIdWithGuid.DeleterId != default && userIdWithGuid.DeleterId != Guid.Empty) + return; + userIdWithGuid.DeleterId = CurrentUser.UserId.ToGuid(); + return; + + case IDeletionAuditedObject userIdWithNullableGuid: + if (userIdWithNullableGuid.DeleterId.HasValue + && userIdWithNullableGuid.DeleterId.Value != default + && userIdWithNullableGuid.DeleterId.Value != Guid.Empty) + return; + userIdWithNullableGuid.DeleterId = CurrentUser.UserId.ToGuidOrNull(); + return; + + case IDeletionAuditedObject userIdWithInt: + if (userIdWithInt.DeleterId != default) + return; + userIdWithInt.DeleterId = CurrentUser.UserId.ToInt(); + return; + + case IDeletionAuditedObject userIdWithNullableInt: + if (userIdWithNullableInt.DeleterId.HasValue + && userIdWithNullableInt.DeleterId.Value != default) + return; + userIdWithNullableInt.DeleterId = CurrentUser.UserId.ToIntOrNull(); + return; + + case IDeletionAuditedObject userIdWithString: + if (!string.IsNullOrWhiteSpace(userIdWithString.DeleterId) && userIdWithString.DeleterId != default) + return; + userIdWithString.DeleterId = CurrentUser.UserId.SafeString(); + return; + + case IDeletionAuditedObject userIdWithLong: + if (userIdWithLong.DeleterId != default) + return; + userIdWithLong.DeleterId = CurrentUser.UserId.ToLong(); + return; + + case IDeletionAuditedObject userIdWithNullableLong: + if (userIdWithNullableLong.DeleterId.HasValue + && userIdWithNullableLong.DeleterId.Value != default) + return; + userIdWithNullableLong.DeleterId = CurrentUser.UserId.ToLongOrNull(); + return; + } + } + + /// + /// 设置删除人 + /// + /// 目标对象 + protected virtual void SetDeleter(object targetObject) + { + var userName = GetUserName(); + if (string.IsNullOrWhiteSpace(userName)) + return; + if (targetObject is IHasDeleter objectWithDeleter) + objectWithDeleter.Deleter = userName; + } + + /// + /// 获取用户名称 + /// + protected virtual string GetUserName() + { + var name = CurrentUser.GetFullName(); + return string.IsNullOrEmpty(name) ? CurrentUser.GetUserName() : name; + } + } +} diff --git a/framework/src/Bing.AutoMapper/Bing/AutoMapper/AutoMapperObjectMapper.cs b/framework/src/Bing.AutoMapper/Bing/AutoMapper/AutoMapperObjectMapper.cs index e2a29ba1a..f632f33a7 100644 --- a/framework/src/Bing.AutoMapper/Bing/AutoMapper/AutoMapperObjectMapper.cs +++ b/framework/src/Bing.AutoMapper/Bing/AutoMapper/AutoMapperObjectMapper.cs @@ -17,7 +17,7 @@ public class AutoMapperObjectMapper : Bing.ObjectMapping.IObjectMapper /// 同步锁 /// // ReSharper disable once InconsistentNaming - private static readonly object Sync = new object(); + private static readonly object Sync = new(); /// /// 配置提供程序 @@ -44,7 +44,7 @@ public AutoMapperObjectMapper(IConfigurationProvider configuration, IReadOnlyCol _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _profiles = profiles; _mapper = _configuration.CreateMapper(); - + } #region Map(将源对象映射到目标对象) @@ -76,7 +76,9 @@ public TDestination Map(TSource source, TDestination dest } catch (AutoMapperMappingException e) { - return GetResult(GetType(e.MemberMap.SourceType), GetType(e.MemberMap.DestinationType), source, destination); + if (e.InnerException != null && e.InnerException.Message.StartsWith("Missing type map configuration")) + return GetResult(GetType(e.MemberMap.SourceType), GetType(e.MemberMap.DestinationType), source, destination); + throw; } } @@ -140,7 +142,7 @@ private void ConfigMap(Type sourceType, Type destinationType) _configuration = new MapperConfiguration(t => { t.CreateMap(sourceType, destinationType); - foreach (var map in maps) + foreach (var map in maps) t.CreateMap(map.SourceType, map.DestinationType); foreach (var profile in _profiles) t.AddProfile(profile as Profile); diff --git a/framework/src/Bing.Data.Sql/project.dependency.props b/framework/src/Bing.Data.Sql/project.dependency.props index 5285b56bf..605bf0dfe 100644 --- a/framework/src/Bing.Data.Sql/project.dependency.props +++ b/framework/src/Bing.Data.Sql/project.dependency.props @@ -2,4 +2,8 @@ + + + + \ No newline at end of file diff --git a/framework/src/Bing.Data/Bing.Data.csproj b/framework/src/Bing.Data/Bing.Data.csproj index e9503931b..4723aa7b4 100644 --- a/framework/src/Bing.Data/Bing.Data.csproj +++ b/framework/src/Bing.Data/Bing.Data.csproj @@ -5,4 +5,8 @@ + + + + diff --git a/framework/src/Bing.Datas.EntityFramework/Core/TreeCompactRepositoryBase.cs b/framework/src/Bing.Datas.EntityFramework/Core/TreeCompactRepositoryBase.cs index 8cc16ea84..29e0d28d8 100644 --- a/framework/src/Bing.Datas.EntityFramework/Core/TreeCompactRepositoryBase.cs +++ b/framework/src/Bing.Datas.EntityFramework/Core/TreeCompactRepositoryBase.cs @@ -8,7 +8,7 @@ using Bing.Domain.Repositories; using Bing.Extensions; using Bing.Trees; -using Bing.Validations.Abstractions; +using Bing.Validation; using Microsoft.EntityFrameworkCore; namespace Bing.Datas.EntityFramework.Core @@ -19,7 +19,7 @@ namespace Bing.Datas.EntityFramework.Core /// 实体类型 /// 持久化对象类型 public abstract class TreeCompactRepositoryBase : TreeCompactRepositoryBase, ITreeCompactRepository - where TEntity : class, ITreeEntity, IValidatable + where TEntity : class, ITreeEntity, IVerifyModel where TPo : class, IKey, IVersion, IPath, IParentId, ISortId { /// @@ -52,7 +52,7 @@ public override async Task GenerateSortIdAsync(Guid? parentId) /// 实体标识类型 /// 父标识类型 public abstract class TreeCompactRepositoryBase : CompactRepositoryBase, ITreeCompactRepository - where TEntity : class, ITreeEntity, IValidatable + where TEntity : class, ITreeEntity, IVerifyModel where TPo : class, IKey, IVersion, IPath { /// diff --git a/framework/src/Bing.Datas.EntityFramework/Core/UnitOfWorkBase.cs b/framework/src/Bing.Datas.EntityFramework/Core/UnitOfWorkBase.cs index 3d6be04da..9cfcd66c0 100644 --- a/framework/src/Bing.Datas.EntityFramework/Core/UnitOfWorkBase.cs +++ b/framework/src/Bing.Datas.EntityFramework/Core/UnitOfWorkBase.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; @@ -79,6 +80,11 @@ public abstract class UnitOfWorkBase : DbContext, IUnitOfWork, IDatabase, IEntit /// protected ICurrentUser CurrentUser => LazyServiceProvider.LazyGetRequiredService(); + /// + /// 审计属性设置器 + /// + protected IAuditPropertySetter AuditPropertySetter => LazyServiceProvider.LazyGetRequiredService(); + #endregion #region 静态构造函数 @@ -169,7 +175,7 @@ protected void EnableLog(DbContextOptionsBuilder builder) return; builder.EnableSensitiveDataLogging(); builder.EnableDetailedErrors(); - builder.UseLoggerFactory(LoggerFactory); + //builder.UseLoggerFactory(LoggerFactory); } /// @@ -446,8 +452,10 @@ protected virtual void SaveChangesBefore() /// 输入实体 protected virtual void ApplyInterceptForAddedEntity(EntityEntry entry) { - SetCreationAudited(entry); - SetModificationAudited(entry); + AuditPropertySetter?.SetCreationProperties(entry.Entity); + AuditPropertySetter?.SetModificationProperties(entry.Entity); + //SetCreationAudited(entry); + //SetModificationAudited(entry); SetVersion(entry); } @@ -461,7 +469,8 @@ protected virtual void ApplyInterceptForAddedEntity(EntityEntry entry) /// 输入实体 protected virtual void ApplyInterceptForModifiedEntity(EntityEntry entry) { - SetModificationAudited(entry); + AuditPropertySetter?.SetModificationProperties(entry.Entity); + //SetModificationAudited(entry); SetVersion(entry); } @@ -475,7 +484,10 @@ protected virtual void ApplyInterceptForModifiedEntity(EntityEntry entry) /// 输入实体 protected virtual void ApplyInterceptForDeletedEntity(EntityEntry entry) { - SetModificationAudited(entry); + AuditPropertySetter?.SetDeletionProperties(entry.Entity); + AuditPropertySetter?.SetModificationProperties(entry.Entity); + SetVersion(entry); + //SetModificationAudited(entry); } #endregion diff --git a/framework/src/Bing.Ddd.Application.Contracts/Bing.Ddd.Application.Contracts.csproj b/framework/src/Bing.Ddd.Application.Contracts/Bing.Ddd.Application.Contracts.csproj index e9503931b..cdbd4b5f9 100644 --- a/framework/src/Bing.Ddd.Application.Contracts/Bing.Ddd.Application.Contracts.csproj +++ b/framework/src/Bing.Ddd.Application.Contracts/Bing.Ddd.Application.Contracts.csproj @@ -5,4 +5,9 @@ + + + + + diff --git a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Aspects/UnitOfWorkAttribute.cs b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Aspects/UnitOfWorkAttribute.cs index 3cdf5fc19..b67cfe7ed 100644 --- a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Aspects/UnitOfWorkAttribute.cs +++ b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Aspects/UnitOfWorkAttribute.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using AspectCore.DynamicProxy; using AspectCore.Extensions.AspectScope; -using Bing.Aspects.Base; +using Bing.Aspects; using Bing.Uow; using Microsoft.Extensions.DependencyInjection; diff --git a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/IRequest.cs b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/IRequest.cs index df59cbd17..5e82fc92e 100644 --- a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/IRequest.cs +++ b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/IRequest.cs @@ -1,11 +1,11 @@ -using Bing.Validations; +using Bing.Validation; namespace Bing.Application.Dtos { /// /// 请求对象 /// - public interface IRequest : IValidation + public interface IRequest : IVerifyModel { } } diff --git a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/RequestBase.cs b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/RequestBase.cs index 41d3c61b2..7cce6e973 100644 --- a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/RequestBase.cs +++ b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Dtos/RequestBase.cs @@ -1,6 +1,6 @@ using System.Linq; using Bing.Exceptions; -using Bing.Validations; +using Bing.Validation; namespace Bing.Application.Dtos { @@ -12,7 +12,7 @@ public abstract class RequestBase : IRequest /// /// 验证 /// - public virtual ValidationResultCollection Validate() + public virtual IValidationResult Validate() { var result = DataAnnotationValidation.Validate(this); if (result.IsValid) diff --git a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/ICrudAppService.cs b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/ICrudAppService.cs index 40cf6a887..2634621b7 100644 --- a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/ICrudAppService.cs +++ b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/ICrudAppService.cs @@ -3,7 +3,7 @@ using Bing.Application.Aspects; using Bing.Application.Dtos; using Bing.Data.Queries; -using Bing.Validations.Aspects; +using Bing.Validation; namespace Bing.Application.Services { @@ -44,13 +44,6 @@ public interface ICrudAppService - /// 创建 - /// - /// 请求参数 - [UnitOfWork] - string Create([Valid] TCreateRequest request); - /// /// 创建 /// @@ -58,13 +51,6 @@ public interface ICrudAppService CreateAsync([Valid] TCreateRequest request); - /// - /// 修改 - /// - /// 请求参数 - [UnitOfWork] - void Update([Valid] TUpdateRequest request); - /// /// 修改 /// @@ -88,13 +74,6 @@ public interface ICrudAppService - /// 保存 - /// - /// 请求参数 - [UnitOfWork] - void Save([Valid] TRequest request); - /// /// 保存 /// @@ -102,14 +81,6 @@ public interface ICrudAppService - /// 批量保存 - /// - /// 新增列表 - /// 修改列表 - /// 删除列表 - List Save(List addList, List updateList, List deleteList); - /// /// 批量保存 /// diff --git a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IDeleteAppService.cs b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IDeleteAppService.cs index 81f88c0e4..c19efb557 100644 --- a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IDeleteAppService.cs +++ b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IDeleteAppService.cs @@ -12,12 +12,6 @@ public interface IDeleteAppService : IQueryAppService< where TDto : new() where TQueryParameter : IQueryParameter { - /// - /// 删除 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - void Delete(string ids); - /// /// 删除 /// diff --git a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IQueryAppService.cs b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IQueryAppService.cs index 5a409c053..9405d813c 100644 --- a/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IQueryAppService.cs +++ b/framework/src/Bing.Ddd.Application.Contracts/Bing/Application/Services/IQueryAppService.cs @@ -14,58 +14,29 @@ public interface IQueryAppService : IAppService where TDto : new() where TQueryParameter : IQueryParameter { - /// - /// 通过编号获取 - /// - /// 实体编号 - TDto GetById(object id); - /// /// 通过编号获取 /// /// 实体编号 Task GetByIdAsync(object id); - /// - /// 通过编号列表获取 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - List GetByIds(string ids); - /// /// 通过编号列表获取 /// /// 用逗号分隔带额Id列表,范例:"1,2" Task> GetByIdsAsync(string ids); - /// - /// 获取全部 - /// - List GetAll(); - /// /// 获取全部 /// Task> GetAllAsync(); - /// - /// 查询 - /// - /// 查询参数 - List Query(TQueryParameter parameter); - /// /// 查询 /// /// 查询参数 Task> QueryAsync(TQueryParameter parameter); - /// - /// 分页查询 - /// - /// 查询参数 - PagerList PagerQuery(TQueryParameter parameter); - /// /// 分页查询 /// diff --git a/framework/src/Bing.Ddd.Application/Bing/Application/Services/CrudAppServiceBase.cs b/framework/src/Bing.Ddd.Application/Bing/Application/Services/CrudAppServiceBase.cs index 733bdc52c..a05a18346 100644 --- a/framework/src/Bing.Ddd.Application/Bing/Application/Services/CrudAppServiceBase.cs +++ b/framework/src/Bing.Ddd.Application/Bing/Application/Services/CrudAppServiceBase.cs @@ -153,30 +153,6 @@ protected CrudAppServiceBase(IUnitOfWork unitOfWork, IRepository #region Save(保存) - /// - /// 保存 - /// - /// 请求参数 - /// - public virtual void Save(TRequest request) - { - if (request == null) - throw new ArgumentNullException(nameof(request)); - SaveBefore(request); - var entity = ToEntity(request); - if (entity == null) - throw new ArgumentNullException(nameof(entity)); - if (IsNew(request, entity)) - { - Create(entity); - request.Id = entity.Id.ToString(); - } - else - { - Update(entity); - } - } - /// /// 保存 /// @@ -230,32 +206,6 @@ protected virtual void SaveBefore(TRequest request) #region BatchSave(批量保存) - /// - /// 批量保存 - /// - /// 新增列表 - /// 修改列表 - /// 删除列表 - public virtual List Save(List addList, List updateList, List deleteList) - { - if (addList == null && updateList == null && deleteList == null) - return new List(); - addList ??= new List(); - updateList ??= new List(); - deleteList ??= new List(); - FilterList(addList, updateList, deleteList); - var addEntities = ToEntities(addList); - var updateEntities = ToEntities(updateList); - var deleteEntities = ToEntities(deleteList); - SaveBefore(addEntities, updateEntities, deleteEntities); - AddList(addEntities); - UpdateList(updateEntities); - DeleteList(deleteEntities); - Commit(); - SaveAfter(addEntities, updateEntities, deleteEntities); - return GetResult(addEntities, updateEntities); - } - /// /// 批量保存 /// @@ -325,18 +275,6 @@ protected virtual void SaveBefore(List addList, List updateLis { } - /// - /// 添加列表 - /// - /// 新增列表 - private void AddList(List list) - { - if (list.Count == 0) - return; - Log.Content("创建实体:"); - list.ForEach(Create); - } - /// /// 添加列表 /// @@ -350,18 +288,6 @@ private async Task AddListAsync(List list) await CreateAsync(entity); } - /// - /// 更新列表 - /// - /// 修改列表 - private void UpdateList(List list) - { - if (list.Count == 0) - return; - Log.Content("修改实体:"); - list.ForEach(Update); - } - /// /// 更新列表 /// @@ -375,18 +301,6 @@ private async Task UpdateListAsync(List list) await UpdateAsync(entity); } - /// - /// 删除列表 - /// - /// 删除列表 - private void DeleteList(List list) - { - if (list.Count == 0) - return; - Log.Content("删除实体:"); - list.ForEach(DeleteChilds); - } - /// /// 删除列表 /// @@ -400,28 +314,12 @@ private async Task DeleteListAsync(List list) await DeleteChildsAsync(entity); } - /// - /// 删除子节点集合 - /// - /// 父节点 - protected virtual void DeleteChilds(TEntity parent) => DeleteEntity(parent); - /// /// 删除子节点集合 /// /// 父节点 protected virtual async Task DeleteChildsAsync(TEntity parent) => await DeleteEntityAsync(parent); - /// - /// 删除实体 - /// - /// 实体 - protected void DeleteEntity(TEntity entity) - { - _repository.Remove(entity.Id); - AddLog(entity); - } - /// /// 删除实体 /// @@ -432,11 +330,6 @@ protected async Task DeleteEntityAsync(TEntity entity) AddLog(entity); } - /// - /// 提交 - /// - private void Commit() => _unitOfWork.Commit(); - /// /// 提交 /// @@ -517,22 +410,6 @@ protected CrudAppServiceBase(IUnitOfWork unitOfWork, IRepository #region Create(创建) - /// - /// 创建 - /// - /// 创建参数 - /// - public virtual string Create(TCreateRequest request) - { - if (request == null) - throw new ArgumentNullException(nameof(request)); - var entity = ToEntityFromCreateRequest(request); - if (entity == null) - throw new ArgumentNullException(nameof(entity)); - Create(entity); - return entity.Id.ToString(); - } - /// /// 创建实体 /// @@ -605,21 +482,6 @@ protected async Task CreateAsync(TEntity entity) #region Update(修改) - /// - /// 修改 - /// - /// 修改参数 - /// - public virtual void Update(TUpdateRequest request) - { - if (request == null) - throw new ArgumentNullException(nameof(request)); - var entity = ToEntityFromUpdateRequest(request); - if (entity == null) - throw new ArgumentNullException(nameof(entity)); - Update(entity); - } - /// /// 修改实体 /// diff --git a/framework/src/Bing.Ddd.Application/Bing/Application/Services/DeleteAppServiceBase.cs b/framework/src/Bing.Ddd.Application/Bing/Application/Services/DeleteAppServiceBase.cs index 270b5aa55..331698d4b 100644 --- a/framework/src/Bing.Ddd.Application/Bing/Application/Services/DeleteAppServiceBase.cs +++ b/framework/src/Bing.Ddd.Application/Bing/Application/Services/DeleteAppServiceBase.cs @@ -132,23 +132,6 @@ protected void AddLog(IList entities) #endregion - /// - /// 删除 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - public virtual void Delete(string ids) - { - if (string.IsNullOrWhiteSpace(ids)) - return; - var entities = _store.FindByIds(ids); - if (entities?.Count == 0) - return; - DeleteBefore(entities); - _store.Remove(entities); - _unitOfWork.Commit(); - DeleteAfter(entities); - } - /// /// 删除 /// diff --git a/framework/src/Bing.Ddd.Application/Bing/Application/Services/QueryAppServiceBase.cs b/framework/src/Bing.Ddd.Application/Bing/Application/Services/QueryAppServiceBase.cs index be92afe34..cac96e746 100644 --- a/framework/src/Bing.Ddd.Application/Bing/Application/Services/QueryAppServiceBase.cs +++ b/framework/src/Bing.Ddd.Application/Bing/Application/Services/QueryAppServiceBase.cs @@ -65,17 +65,7 @@ public abstract class QueryAppServiceBase /// 实体 protected virtual TDto ToDto(TEntity entity) => entity.MapTo(); - #region GetById(通过编号获取) - - /// - /// 通过编号获取 - /// - /// 实体编号 - public virtual TDto GetById(object id) - { - var key = Conv.To(id); - return ToDto(_store.Find(key)); - } + #region GetByIdAsync(通过编号获取) /// /// 通过编号获取 @@ -89,13 +79,7 @@ public virtual async Task GetByIdAsync(object id) #endregion - #region GetByIds(通过编号列表获取) - - /// - /// 通过编号列表获取 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - public virtual List GetByIds(string ids) => _store.FindByIds(ids).Select(ToDto).ToList(); + #region GetByIdsAsync(通过编号列表获取) /// /// 通过编号列表获取 @@ -111,11 +95,6 @@ public virtual async Task> GetByIdsAsync(string ids) #region GetAll(获取全部) - /// - /// 获取全部 - /// - public virtual List GetAll() => _store.FindAll().Select(ToDto).ToList(); - /// /// 获取全部 /// diff --git a/framework/src/Bing.Ddd.Domain/Bing.Ddd.Domain.csproj b/framework/src/Bing.Ddd.Domain/Bing.Ddd.Domain.csproj index e9503931b..85e8937e2 100644 --- a/framework/src/Bing.Ddd.Domain/Bing.Ddd.Domain.csproj +++ b/framework/src/Bing.Ddd.Domain/Bing.Ddd.Domain.csproj @@ -5,4 +5,10 @@ + + + + + + diff --git a/framework/src/Bing.Ddd.Domain/Bing/Data/IStore.cs b/framework/src/Bing.Ddd.Domain/Bing/Data/IStore.cs index 24c4b48ed..5f2b99be8 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Data/IStore.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Data/IStore.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Bing.Domain.Entities; -using Bing.Validations.Aspects; +using Bing.Validation; namespace Bing.Data { diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/AggregateRoot.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/AggregateRoot.cs index ad7872e71..d2cb74b47 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/AggregateRoot.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/AggregateRoot.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Bing.Auditing; using Bing.Domain.Entities.Events; +using Bing.Validation; namespace Bing.Domain.Entities { @@ -10,7 +11,7 @@ namespace Bing.Domain.Entities /// /// 实体类型 public abstract class AggregateRoot : AggregateRoot - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 初始化一个类型的实例 @@ -34,7 +35,7 @@ protected AggregateRoot(Guid id) : base(id) /// 实体类型 /// 标识类型 public abstract class AggregateRoot : EntityBase, IAggregateRoot - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 领域事件列表 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRoot.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRoot.cs index 8f1f8ead2..01c422ece 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRoot.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRoot.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class AuditedAggregateRoot : CreationAuditedAggregateRoot, IAuditedObject - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 最后修改时间 @@ -29,7 +30,7 @@ public abstract class AuditedAggregateRoot : CreationAuditedAggregateRo /// 标识类型 [Serializable] public abstract class AuditedAggregateRoot : CreationAuditedAggregateRoot, IAuditedObject - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 最后修改时间 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRootWithName.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRootWithName.cs index 4aeaa111b..455f958a2 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRootWithName.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedAggregateRootWithName.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class AuditedAggregateRootWithName : AuditedAggregateRoot, IAuditedObjectWithName - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建人 @@ -29,7 +30,7 @@ public abstract class AuditedAggregateRootWithName : AuditedAggregateRo /// 标识类型 [Serializable] public abstract class AuditedAggregateRootWithName : AuditedAggregateRoot, IAuditedObjectWithName - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建人 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntity.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntity.cs index 9ce317dc6..ebfee8289 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntity.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntity.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class AuditedEntity : CreationAuditedEntity, IAuditedObject - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 最后修改时间 @@ -29,7 +30,7 @@ public abstract class AuditedEntity : CreationAuditedEntity, I /// 标识类型 [Serializable] public abstract class AuditedEntity : CreationAuditedEntity, IAuditedObject - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 最后修改时间 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntityWithName.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntityWithName.cs index aa193bace..8bc38a178 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntityWithName.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/AuditedEntityWithName.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class AuditedEntityWithName : AuditedEntity, IAuditedObjectWithName - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建人 @@ -29,7 +30,7 @@ public abstract class AuditedEntityWithName : AuditedEntity, I /// 标识类型 [Serializable] public abstract class AuditedEntityWithName : AuditedEntity, IAuditedObjectWithName - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建人 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs index 7645912a5..889cf8dc6 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class CreationAuditedAggregateRoot : AggregateRoot, ICreationAuditedObject - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建时间 @@ -30,7 +31,7 @@ public abstract class CreationAuditedAggregateRoot : AggregateRoot标识类型 [Serializable] public abstract class CreationAuditedAggregateRoot : AggregateRoot, ICreationAuditedObject - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建时间 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRootWithName.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRootWithName.cs index 61b455cac..d58629c65 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRootWithName.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedAggregateRootWithName.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class CreationAuditedAggregateRootWithName : CreationAuditedAggregateRoot, ICreationAuditedObjectWithName - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建人 @@ -24,7 +25,7 @@ public abstract class CreationAuditedAggregateRootWithName : CreationAu /// 标识类型 [Serializable] public abstract class CreationAuditedAggregateRootWithName : CreationAuditedAggregateRoot, ICreationAuditedObjectWithName - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建人 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntity.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntity.cs index 8b0b461ac..b5fc2a457 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntity.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntity.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class CreationAuditedEntity : EntityBase, ICreationAuditedObject - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建时间 @@ -29,7 +30,7 @@ public abstract class CreationAuditedEntity : EntityBase, ICre /// 标识类型 [Serializable] public abstract class CreationAuditedEntity : EntityBase, ICreationAuditedObject - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建时间 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntityWithName.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntityWithName.cs index bc231b913..4ec5e81c6 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntityWithName.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/CreationAuditedEntityWithName.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class CreationAuditedEntityWithName : CreationAuditedEntity, ICreationAuditedObjectWithName - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建人 @@ -24,7 +25,7 @@ public abstract class CreationAuditedEntityWithName : CreationAuditedEn /// 标识类型 [Serializable] public abstract class CreationAuditedEntityWithName : CreationAuditedEntity, ICreationAuditedObjectWithName - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建人 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs index 4ada05958..ba346dfb5 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class FullAuditedAggregateRoot : AuditedAggregateRoot, IFullAuditedObject - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 是否已删除 @@ -34,7 +35,7 @@ public abstract class FullAuditedAggregateRoot : AuditedAggregateRoot标识类型 [Serializable] public abstract class FullAuditedAggregateRoot : AuditedAggregateRoot, IFullAuditedObject - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 是否已删除 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRootWithName.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRootWithName.cs index 77efaac1d..e08f89f85 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRootWithName.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedAggregateRootWithName.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class FullAuditedAggregateRootWithName : FullAuditedAggregateRoot, IFullAuditedObjectWithName - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建人 @@ -34,7 +35,7 @@ public abstract class FullAuditedAggregateRootWithName : FullAuditedAgg /// 标识类型 [Serializable] public abstract class FullAuditedAggregateRootWithName : FullAuditedAggregateRoot, IFullAuditedObjectWithName - where TEntity : class, IAggregateRoot + where TEntity : class, IAggregateRoot, IVerifyModel { /// /// 创建人 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntity.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntity.cs index 0019303c1..6b3ee893f 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntity.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntity.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class FullAuditedEntity : AuditedEntity, IFullAuditedObject - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 是否已删除 @@ -34,7 +35,7 @@ public abstract class FullAuditedEntity : AuditedEntity, IFull /// 标识类型 [Serializable] public abstract class FullAuditedEntity : AuditedEntity, IFullAuditedObject - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 是否已删除 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntityWithName.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntityWithName.cs index 64ba6bca8..d77947c07 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntityWithName.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/Auditing/FullAuditedEntityWithName.cs @@ -1,5 +1,6 @@ using System; using Bing.Auditing; +using Bing.Validation; namespace Bing.Domain.Entities.Auditing { @@ -9,7 +10,7 @@ namespace Bing.Domain.Entities.Auditing /// 实体类型 [Serializable] public abstract class FullAuditedEntityWithName : FullAuditedEntity, IFullAuditedObjectWithName - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建人 @@ -34,7 +35,7 @@ public abstract class FullAuditedEntityWithName : FullAuditedEntity标识类型 [Serializable] public abstract class FullAuditedEntityWithName : FullAuditedEntity, IFullAuditedObjectWithName - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 创建人 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/DomainObjectBase.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/DomainObjectBase.cs index 9f93e23f4..21cac8586 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/DomainObjectBase.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/DomainObjectBase.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq.Expressions; using Bing.Domain.ChangeTracking; -using Bing.Validations; -using Bing.Validations.Abstractions; +using Bing.Validation; +using Bing.Validation.Strategies; namespace Bing.Domain.Entities { @@ -11,8 +11,8 @@ namespace Bing.Domain.Entities /// 领域对象基类 /// /// 领域对象 - public abstract class DomainObjectBase : IDomainObject, IValidatable, IChangeTrackable - where TObject : class, IDomainObject + public abstract class DomainObjectBase : IDomainObject, IVerifyModel, IChangeTrackable + where TObject : class, IDomainObject, IVerifyModel { /// /// 验证上下文 @@ -45,24 +45,32 @@ protected DomainObjectBase() /// 设置验证处理器 /// /// 验证处理器 - public void SetValidateHandler(IValidationHandler handler) => _validationContext.SetHandler(op => op.HandleAll(handler)); + public void SetValidationCallback(IValidationCallbackHandler handler) => _validationContext.SetHandler(op => op.HandleAll(handler)); /// - /// 添加验证策略 + /// 使用全局验证规则 + /// + public void UseValidationRules() + { + throw new NotImplementedException(); + } + + /// + /// 使用验证策略 /// /// 验证策略 - public void AddStrategy(IValidateStrategy strategy) => _validationContext.AddStrategy(strategy); + public void UseStrategy(IValidationStrategy strategy) => _validationContext.AddStrategy(strategy); /// - /// 添加验证策略集合 + /// 使用验证策略集合 /// /// 验证策略集合 - public void AddStrategyList(IEnumerable> strategies) => _validationContext.AddStrategyList(strategies); + public void UseStrategyList(IEnumerable> strategies) =>_validationContext.AddStrategyList(strategies); /// /// 验证 /// - public virtual ValidationResultCollection Validate() + public virtual IValidationResult Validate() { _validationContext.Validate(Validate); return _validationContext.GetValidationResultCollection(); diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/EntityBase.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/EntityBase.cs index 06f7f461b..e154d841c 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/EntityBase.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/EntityBase.cs @@ -3,7 +3,7 @@ using Bing.Extensions; using Bing.Helpers; using Bing.Properties; -using Bing.Validations; +using Bing.Validation; namespace Bing.Domain.Entities { @@ -16,7 +16,7 @@ public abstract class EntityBase : IEntity /// /// 验证 /// - public abstract ValidationResultCollection Validate(); + public abstract IValidationResult Validate(); /// /// 初始化 @@ -39,7 +39,7 @@ public abstract class EntityBase : IEntity /// /// 实体类型 [Serializable] - public abstract class EntityBase : EntityBase where TEntity : class, IEntity + public abstract class EntityBase : EntityBase where TEntity : class, IEntity, IVerifyModel { /// /// 初始化一个类型的实例 @@ -60,7 +60,7 @@ protected EntityBase(Guid id) : base(id) { } /// 标识类型 [Serializable] public abstract class EntityBase : DomainObjectBase, IEntity - where TEntity : class, IEntity + where TEntity : class, IEntity, IVerifyModel { /// /// 标识 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/IDomainObject.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/IDomainObject.cs index 9ecdf470c..ea17999ed 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/IDomainObject.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/IDomainObject.cs @@ -1,11 +1,11 @@ -using Bing.Validations.Abstractions; +using Bing.Validation; namespace Bing.Domain.Entities { /// /// 领域对象 /// - public interface IDomainObject : IValidatable + public interface IDomainObject : IVerifyModel { } } diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/TreeEntityBase.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/TreeEntityBase.cs index 57094b4dd..b61a269fe 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/TreeEntityBase.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/TreeEntityBase.cs @@ -3,7 +3,7 @@ using System.Linq; using Bing.Extensions; using Bing.Helpers; -using Bing.Validations.Abstractions; +using Bing.Validation; namespace Bing.Domain.Entities { @@ -12,7 +12,7 @@ namespace Bing.Domain.Entities /// /// 树型实体类型 public abstract class TreeEntityBase : TreeEntityBase - where TEntity : class, ITreeEntity, IValidatable + where TEntity : class, ITreeEntity, IVerifyModel { /// /// 初始化一个类型的实例 @@ -33,7 +33,7 @@ protected TreeEntityBase(Guid id, string path, int level) : base(id, path, level /// 父编号类型 public abstract class TreeEntityBase : AggregateRoot, ITreeEntity - where TEntity : class, ITreeEntity, IValidatable + where TEntity : class, ITreeEntity, IVerifyModel { /// /// 父标识 diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Repositories/ICompactRepository.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Repositories/ICompactRepository.cs index 76df51621..69179cae7 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Repositories/ICompactRepository.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Repositories/ICompactRepository.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Bing.DependencyInjection; using Bing.Domain.Entities; -using Bing.Validations.Aspects; +using Bing.Validation; namespace Bing.Domain.Repositories { diff --git a/framework/src/Bing.Ddd.Domain/Bing/Domain/Services/ParameterBase.cs b/framework/src/Bing.Ddd.Domain/Bing/Domain/Services/ParameterBase.cs index 77f7a82d8..549aa1440 100644 --- a/framework/src/Bing.Ddd.Domain/Bing/Domain/Services/ParameterBase.cs +++ b/framework/src/Bing.Ddd.Domain/Bing/Domain/Services/ParameterBase.cs @@ -1,18 +1,18 @@ using System.Linq; using Bing.Exceptions; -using Bing.Validations; +using Bing.Validation; namespace Bing.Domain.Services { /// /// 参数基类 /// - public abstract class ParameterBase : IValidation + public abstract class ParameterBase : IVerifyModel { /// /// 验证 /// - public virtual ValidationResultCollection Validate() + public virtual IValidationResult Validate() { var result = DataAnnotationValidation.Validate(this); if (result.IsValid) diff --git a/framework/src/Bing.EventBus/Bing/EventBus/Local/LocalEventBus.cs b/framework/src/Bing.EventBus/Bing/EventBus/Local/LocalEventBus.cs index 6dc0c8f78..462d89f31 100644 --- a/framework/src/Bing.EventBus/Bing/EventBus/Local/LocalEventBus.cs +++ b/framework/src/Bing.EventBus/Bing/EventBus/Local/LocalEventBus.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Bing.Extensions; using Bing.Helpers; +using Bing.Reflection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -55,7 +55,7 @@ public LocalEventBus( /// 事件处理器工厂 public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory) { - GetOrCreateHandlerFactories(eventType).Locking(factories => + GetOrCreateHandlerFactories(eventType).LockAndRun(factories => { if (!factory.IsInFactories(factories)) factories.Add(factory); @@ -73,7 +73,7 @@ public override void Unsubscribe(Func action) Check.NotNull(action, nameof(action)); GetOrCreateHandlerFactories(typeof(TEvent)) - .Locking(factories => + .LockAndRun(factories => { factories.RemoveAll(factory => { @@ -96,7 +96,7 @@ public override void Unsubscribe(Func action) public override void Unsubscribe(Type eventType, IEventHandler handler) { GetOrCreateHandlerFactories(eventType) - .Locking(factories => + .LockAndRun(factories => { factories.RemoveAll(factory => factory is SingleInstanceHandlerFactory && @@ -111,7 +111,7 @@ factory is SingleInstanceHandlerFactory && /// 事件处理器工厂 public override void Unsubscribe(Type eventType, IEventHandlerFactory factory) { - GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory)); + GetOrCreateHandlerFactories(eventType).LockAndRun(factories => factories.Remove(factory)); } /// @@ -120,7 +120,7 @@ public override void Unsubscribe(Type eventType, IEventHandlerFactory factory) /// 事件类型 public override void UnsubscribeAll(Type eventType) { - GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear()); + GetOrCreateHandlerFactories(eventType).LockAndRun(factories => factories.Clear()); } /// diff --git a/framework/src/Bing.EventBus/project.dependency.props b/framework/src/Bing.EventBus/project.dependency.props index 8d37a8e3d..a36ae83c8 100644 --- a/framework/src/Bing.EventBus/project.dependency.props +++ b/framework/src/Bing.EventBus/project.dependency.props @@ -1,4 +1,8 @@  + + + + diff --git a/framework/src/Bing.Events/Cap/CapTraceAttribute.cs b/framework/src/Bing.Events/Cap/CapTraceAttribute.cs index a64b8eda9..7fdc68b50 100644 --- a/framework/src/Bing.Events/Cap/CapTraceAttribute.cs +++ b/framework/src/Bing.Events/Cap/CapTraceAttribute.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using AspectCore.DynamicProxy; using AspectCore.DynamicProxy.Parameters; -using Bing.Aspects.Base; +using Bing.Aspects; using Bing.Tracing; using DotNetCore.CAP; diff --git a/framework/src/Bing.Events/Cap/MessageEventBus.cs b/framework/src/Bing.Events/Cap/MessageEventBus.cs index b7de9a40f..2a6992627 100644 --- a/framework/src/Bing.Events/Cap/MessageEventBus.cs +++ b/framework/src/Bing.Events/Cap/MessageEventBus.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Bing.Data.Transaction; using Bing.Events.Messages; @@ -36,7 +37,7 @@ public class MessageEventBus : IMessageEventBus /// 事件发布器 /// 事务操作管理器 /// 日志操作 - public MessageEventBus(ICapPublisher publisher, + public MessageEventBus(ICapPublisher publisher, ITransactionActionManager transactionActionManager, ILog log) { @@ -50,7 +51,10 @@ public MessageEventBus(ICapPublisher publisher, /// /// 事件类型 /// 事件 - public Task PublishAsync(TEvent @event) where TEvent : IMessageEvent => PublishAsync(@event.Name, @event.Data, @event.Callback, @event.Send); + /// 取消令牌 + public Task PublishAsync(TEvent @event, CancellationToken cancellationToken = default) + where TEvent : IMessageEvent => + PublishAsync(@event.Name, @event.Data, @event.Callback, @event.Send, cancellationToken); /// /// 发布事件 @@ -59,18 +63,19 @@ public MessageEventBus(ICapPublisher publisher, /// 事件数据 /// 回调名称 /// 是否立即发送消息 - public async Task PublishAsync(string name, object data, string callback = null, bool send = false) + /// 取消令牌 + public async Task PublishAsync(string name, object data, string callback = null, bool send = false, CancellationToken cancellationToken = default) { - var headers = new Dictionary {{DotNetCore.CAP.Messages.Headers.CallbackName, callback}}; + var headers = new Dictionary { { DotNetCore.CAP.Messages.Headers.CallbackName, callback } }; InitTraceIdContext(headers); if (send) { - await InternalPublishAsync(name, data, headers, callback); + await InternalPublishAsync(name, data, headers, callback, cancellationToken); return; } TransactionActionManager.Register(async transaction => { - await InternalPublishAsync(name, data, headers, callback); + await InternalPublishAsync(name, data, headers, callback, cancellationToken); }); } @@ -81,9 +86,10 @@ public async Task PublishAsync(string name, object data, string callback = null, /// 事件数据 /// 数据投 /// 回调名称 - private async Task InternalPublishAsync(string name, object data, IDictionary headers, string callback) + /// 取消令牌 + private async Task InternalPublishAsync(string name, object data, IDictionary headers, string callback, CancellationToken cancellationToken = default) { - await Publisher.PublishAsync(name, data, headers); + await Publisher.PublishAsync(name, data, headers, cancellationToken); WriteLog(name, data, callback); } @@ -112,7 +118,7 @@ private static void InitTraceIdContext(IDictionary headers) { if (TraceIdContext.Current == null) TraceIdContext.Current = new TraceIdContext(string.Empty); - if(!headers.ContainsKey(Headers.TraceId)) + if (!headers.ContainsKey(Headers.TraceId)) headers[Headers.TraceId] = TraceIdContext.Current.TraceId; } } diff --git a/framework/src/Bing.FreeSQL.MySql/Bing/FreeSQL/Extensions.Services.cs b/framework/src/Bing.FreeSQL.MySql/Bing/FreeSQL/Extensions.Services.cs index 9ad1adbc5..bdc25dbef 100644 --- a/framework/src/Bing.FreeSQL.MySql/Bing/FreeSQL/Extensions.Services.cs +++ b/framework/src/Bing.FreeSQL.MySql/Bing/FreeSQL/Extensions.Services.cs @@ -21,79 +21,85 @@ public static partial class Extensions /// /// 注册MySql工作单元服务 /// - /// 工作单元接口类型 + /// 工作单元接口类型 /// 工作单元实现类型 /// 服务集合 /// 连接字符串 /// 配置操作 /// FreeSql配置操作 - public static IServiceCollection AddMySqlUnitOfWork(this IServiceCollection services + public static IServiceCollection AddMySqlUnitOfWork(this IServiceCollection services , string connection - , Action setupAction = null - , Action freeSqlSetupAction = null) - where TUnitOfWOrk : class, IUnitOfWork - where TUnitOfWorkImplementation : UnitOfWorkBase, TUnitOfWOrk + , Action setupAction = null + , Action freeSqlSetupAction = null) + where TUnitOfWork : class, IUnitOfWork + where TUnitOfWorkImplementation : UnitOfWorkBase, TUnitOfWork { - - var freeSqlBuilder = new FreeSqlBuilder() + Func freeSqlWrapper = s => + { + var freeSqlBuilder = new FreeSqlBuilder() .UseConnectionString(DataType.MySql, connection) .UseLazyLoading(false); - setupAction?.Invoke(freeSqlBuilder); + setupAction?.Invoke(s, freeSqlBuilder); - var freeSql = freeSqlBuilder.Build(); - freeSqlSetupAction?.Invoke(freeSql); - freeSql.Aop.AuditValue += (s, e) => - { - // 乐观锁 - if (e.AuditValueType == AuditValueType.Insert || e.AuditValueType == AuditValueType.Update) - { - if (e.Property.Name == AuditedPropertyConst.Version) - if (e.Value is byte[] bytes && bytes.Length == 0) - e.Value = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()); - } - // 时间 - if (e.AuditValueType == AuditValueType.Insert) - { - if (e.Property.Name == AuditedPropertyConst.CreationTime || - e.Property.Name == AuditedPropertyConst.ModificationTime) - e.Value = DateTime.Now; - } - if (e.AuditValueType == AuditValueType.Update) - { - if (e.Property.Name == AuditedPropertyConst.ModificationTime) - e.Value = DateTime.Now; - } - // 用户 - if (e.Property.Name == AuditedPropertyConst.Creator || - e.Property.Name == AuditedPropertyConst.Modifier || - e.Property.Name == AuditedPropertyConst.CreatorId || - e.Property.Name == AuditedPropertyConst.ModifierId) + var freeSql = freeSqlBuilder.Build(); + freeSqlSetupAction?.Invoke(s, freeSql); + freeSql.Aop.AuditValue += (s, e) => { - var currentUser = ServiceLocator.Instance?.GetService(); - if (currentUser == null || currentUser.UserId.IsEmpty()) - return; + // 乐观锁 + if (e.AuditValueType == AuditValueType.Insert || e.AuditValueType == AuditValueType.Update) + { + if (e.Property.Name == AuditedPropertyConst.Version) + if (e.Value is byte[] bytes && bytes.Length == 0) + e.Value = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()); + } + // 时间 if (e.AuditValueType == AuditValueType.Insert) { - if (e.Property.Name == AuditedPropertyConst.Creator || - e.Property.Name == AuditedPropertyConst.Modifier) - e.Value = currentUser.GetFullName() ?? currentUser.GetUserName(); - if (e.Property.Name == AuditedPropertyConst.CreatorId || - e.Property.Name == AuditedPropertyConst.ModifierId) - e.Value = currentUser.GetUserId(); + if (e.Property.Name == AuditedPropertyConst.CreationTime || + e.Property.Name == AuditedPropertyConst.ModificationTime) + e.Value = DateTime.Now; } if (e.AuditValueType == AuditValueType.Update) { - if (e.Property.Name == AuditedPropertyConst.Modifier) - e.Value = currentUser.GetFullName() ?? currentUser.GetUserName(); - if (e.Property.Name == AuditedPropertyConst.ModifierId) - e.Value = currentUser.GetUserId(); + if (e.Property.Name == AuditedPropertyConst.ModificationTime) + e.Value = DateTime.Now; } - } + // 用户 + if (e.Property.Name == AuditedPropertyConst.Creator || + e.Property.Name == AuditedPropertyConst.Modifier || + e.Property.Name == AuditedPropertyConst.CreatorId || + e.Property.Name == AuditedPropertyConst.ModifierId) + { + var currentUser = ServiceLocator.Instance?.GetService(); + if (currentUser == null || currentUser.UserId.IsEmpty()) + return; + if (e.AuditValueType == AuditValueType.Insert) + { + if (e.Property.Name == AuditedPropertyConst.Creator || + e.Property.Name == AuditedPropertyConst.Modifier) + e.Value = currentUser.GetFullName() ?? currentUser.GetUserName(); + if (e.Property.Name == AuditedPropertyConst.CreatorId || + e.Property.Name == AuditedPropertyConst.ModifierId) + e.Value = currentUser.GetUserId(); + } + if (e.AuditValueType == AuditValueType.Update) + { + if (e.Property.Name == AuditedPropertyConst.Modifier) + e.Value = currentUser.GetFullName() ?? currentUser.GetUserName(); + if (e.Property.Name == AuditedPropertyConst.ModifierId) + e.Value = currentUser.GetUserId(); + } + } + }; + + var wrapper = new FreeSqlWrapper { Orm = freeSql }; + freeSql.GlobalFilter.Apply("SoftDelete", x => x.IsDeleted == false); + return wrapper; }; - var freeSqlWrapper = new FreeSqlWrapper { Orm = freeSql }; - freeSql.GlobalFilter.Apply("SoftDelete", x => x.IsDeleted == false); + + services.AddSingleton(freeSqlWrapper); - services.AddScoped(); + services.AddScoped(); return services; } diff --git a/framework/src/Bing.FreeSQL.MySql/project.dependency.props b/framework/src/Bing.FreeSQL.MySql/project.dependency.props index 670ee10d7..ad90ab30e 100644 --- a/framework/src/Bing.FreeSQL.MySql/project.dependency.props +++ b/framework/src/Bing.FreeSQL.MySql/project.dependency.props @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/framework/src/Bing.FreeSQL/Bing/Domain/Repositories/TreeCompactRepositoryBase.cs b/framework/src/Bing.FreeSQL/Bing/Domain/Repositories/TreeCompactRepositoryBase.cs index de7f5519b..9132f5da1 100644 --- a/framework/src/Bing.FreeSQL/Bing/Domain/Repositories/TreeCompactRepositoryBase.cs +++ b/framework/src/Bing.FreeSQL/Bing/Domain/Repositories/TreeCompactRepositoryBase.cs @@ -7,7 +7,7 @@ using Bing.Domain.Entities; using Bing.Extensions; using Bing.Trees; -using Bing.Validations.Abstractions; +using Bing.Validation; namespace Bing.Domain.Repositories { @@ -17,7 +17,7 @@ namespace Bing.Domain.Repositories /// 实体类型 /// 持久化对象类型 public abstract class TreeCompactRepositoryBase : TreeCompactRepositoryBase, ITreeCompactRepository - where TEntity : class, ITreeEntity, IValidatable + where TEntity : class, ITreeEntity, IVerifyModel where TPo : class, IKey, IVersion, IPath, IParentId, ISortId { /// @@ -50,7 +50,7 @@ public override async Task GenerateSortIdAsync(Guid? parentId) /// 实体标识类型 /// 父标识类型 public abstract class TreeCompactRepositoryBase : CompactRepositoryBase, ITreeCompactRepository - where TEntity : class, ITreeEntity, IValidatable + where TEntity : class, ITreeEntity, IVerifyModel where TPo : class, IKey, IVersion, IPath { /// diff --git a/framework/src/Bing.FreeSQL/project.dependency.props b/framework/src/Bing.FreeSQL/project.dependency.props index cc269c066..b6d7e2415 100644 --- a/framework/src/Bing.FreeSQL/project.dependency.props +++ b/framework/src/Bing.FreeSQL/project.dependency.props @@ -6,8 +6,8 @@ - - - + + + \ No newline at end of file diff --git a/framework/src/Bing.Logging.Serilog/Bing/Logging/Serilog/Enrichers/LogContextEnricher.cs b/framework/src/Bing.Logging.Serilog/Bing/Logging/Serilog/Enrichers/LogContextEnricher.cs index c0a91cb24..e351668d3 100644 --- a/framework/src/Bing.Logging.Serilog/Bing/Logging/Serilog/Enrichers/LogContextEnricher.cs +++ b/framework/src/Bing.Logging.Serilog/Bing/Logging/Serilog/Enrichers/LogContextEnricher.cs @@ -1,4 +1,5 @@ -using Bing.DependencyInjection; +using System.Linq; +using Bing.DependencyInjection; using Bing.Extensions; using Serilog.Core; using Serilog.Events; @@ -30,12 +31,14 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) _context = accessor.Context; if (_context == null) return; + RemoveProperties(logEvent); AddDuration(logEvent, propertyFactory); AddTraceId(logEvent, propertyFactory); AddUserId(logEvent, propertyFactory); AddApplication(logEvent, propertyFactory); AddEnvironment(logEvent, propertyFactory); AddExtraData(logEvent, propertyFactory); + AddTags(logEvent, propertyFactory); } /// @@ -118,5 +121,16 @@ private void AddExtraData(LogEvent logEvent, ILogEventPropertyFactory propertyFa logEvent.AddOrUpdateProperty(property); } } + + /// + /// 添加标签 + /// + private void AddTags(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + if (_context.Tags.Count == 0) + return; + var property = propertyFactory.CreateProperty("Tags", _context.Tags.Distinct(), true); + logEvent.AddOrUpdateProperty(property); + } } } diff --git a/framework/src/Bing.Logging.Sinks.Exceptionless/Serilog/Sinks/Exceptionless/ExceptionlessSink.cs b/framework/src/Bing.Logging.Sinks.Exceptionless/Serilog/Sinks/Exceptionless/ExceptionlessSink.cs index 7cdb234c0..7311db894 100644 --- a/framework/src/Bing.Logging.Sinks.Exceptionless/Serilog/Sinks/Exceptionless/ExceptionlessSink.cs +++ b/framework/src/Bing.Logging.Sinks.Exceptionless/Serilog/Sinks/Exceptionless/ExceptionlessSink.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Bing.Logging.Sinks.Exceptionless.Internals; using Exceptionless; using Exceptionless.Dependency; @@ -128,6 +129,12 @@ public void Emit(LogEvent logEvent) if (!string.IsNullOrWhiteSpace(emailAddress) || !string.IsNullOrWhiteSpace(description)) builder.SetUserDescription(emailAddress, description); break; + case "Tags" when property.Value is SequenceValue tags: + var tagList = tags.FlattenProperties() as List; + if(tagList is null) + continue; + builder.AddTags(tagList.Select(x=>x.ToString()).ToArray()); + break; default: builder.SetProperty(property.Key, property.Value.FlattenProperties()); break; diff --git a/framework/src/Bing.Logging/Bing/Logging/ILog.cs b/framework/src/Bing.Logging/Bing/Logging/ILog.cs index 8d26b9273..b80e8fbc6 100644 --- a/framework/src/Bing.Logging/Bing/Logging/ILog.cs +++ b/framework/src/Bing.Logging/Bing/Logging/ILog.cs @@ -29,6 +29,12 @@ public interface ILog : ITransientDependency /// 属性值 ILog Property(string propertyName, string propertyValue); + /// + /// 设置标签 + /// + /// 标签 + ILog Tags(params string[] tags); + /// /// 设置日志状态对象 /// diff --git a/framework/src/Bing.Logging/Bing/Logging/ILogExtensions.cs b/framework/src/Bing.Logging/Bing/Logging/ILogExtensions.cs index 20cdb483a..4aa82f968 100644 --- a/framework/src/Bing.Logging/Bing/Logging/ILogExtensions.cs +++ b/framework/src/Bing.Logging/Bing/Logging/ILogExtensions.cs @@ -7,6 +7,75 @@ namespace Bing.Logging /// public static class ILogExtensions { + /// + /// 添加消息 + /// + /// 日志类别 + /// 日志操作 + /// 消息 + /// 日志消息参数 + public static ILog Append(this ILog log, string message, params object[] args) + { + if (log is null) + throw new ArgumentNullException(nameof(log)); + log.Message(message, args); + return log; + } + + /// + /// 添加消息,当条件为true时 + /// + /// 日志类别 + /// 日志操作 + /// 消息 + /// 条件,值为true时,则添加消息 + /// 日志消息参数 + public static ILog AppendIf(this ILog log, string message, bool condition, params object[] args) + { + if (log is null) + throw new ArgumentNullException(nameof(log)); + if (condition) + log.Message(message, args); + return log; + } + + /// + /// 添加消息并换行 + /// + /// 日志类别 + /// 日志操作 + /// 消息 + /// 日志消息参数 + public static ILog AppendLine(this ILog log, string message, params object[] args) + { + if (log is null) + throw new ArgumentNullException(nameof(log)); + log.Message(message, args); + log.Message(Environment.NewLine); + return log; + } + + /// + /// 添加消息并换行,当条件为true时 + /// + /// 日志类别 + /// 日志操作 + /// 消息 + /// 条件,值为true时,则添加消息 + /// 日志消息参数 + /// + public static ILog AppendLineIf(this ILog log, string message, bool condition, params object[] args) + { + if (log is null) + throw new ArgumentNullException(nameof(log)); + if (condition) + { + log.Message(message, args); + log.Message(Environment.NewLine); + } + return log; + } + /// /// 消息换行 /// @@ -14,6 +83,8 @@ public static class ILogExtensions /// 日志操作 public static ILog Line(this ILog log) { + if (log is null) + throw new ArgumentNullException(nameof(log)); log.Message(Environment.NewLine); return log; } diff --git a/framework/src/Bing.Logging/Bing/Logging/Log.cs b/framework/src/Bing.Logging/Bing/Logging/Log.cs index 4f4f82386..d3fb9c896 100644 --- a/framework/src/Bing.Logging/Bing/Logging/Log.cs +++ b/framework/src/Bing.Logging/Bing/Logging/Log.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using Bing.Extensions; using Bing.Helpers; @@ -14,6 +15,8 @@ namespace Bing.Logging /// 日志类别 public class Log : ILog { + #region 构造函数 + /// /// 初始化一个类型的实例 /// @@ -28,6 +31,19 @@ public Log(ILoggerWrapper logger, ILogContextAccessor logContextA LogMessageArgs = new List(); } + #endregion + + #region Null(空日志操作实例) + + /// + /// 空日志操作实例 + /// + public static ILog Null = NullLog.Instance; + + #endregion + + #region 属性 + /// /// 日志记录包装器 /// @@ -73,6 +89,10 @@ public Log(ILoggerWrapper logger, ILogContextAccessor logContextA /// protected List LogMessageArgs { get; } + #endregion + + #region EventId(设置日志事件标识) + /// public virtual ILog EventId(EventId eventId) { @@ -80,6 +100,10 @@ public virtual ILog EventId(EventId eventId) return this; } + #endregion + + #region Exception(设置异常) + /// public virtual ILog Exception(Exception exception) { @@ -87,6 +111,10 @@ public virtual ILog Exception(Exception exception) return this; } + #endregion + + #region Property(设置自定义扩展属性) + /// public virtual ILog Property(string propertyName, string propertyValue) { @@ -97,11 +125,32 @@ public virtual ILog Property(string propertyName, string property LogProperties[propertyName] += propertyValue; return this; } - LogProperties.Add(propertyName, propertyValue); return this; } + #endregion + + #region Tags(设置标签) + + /// + public virtual ILog Tags(params string[] tags) + { + if (tags == null) + return this; + if (LogContext == null) + return this; + var tagList = tags.Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); + if (!tagList.Any()) + return this; + LogContext.Tags.AddRange(tagList); + return this; + } + + #endregion + + #region State(设置日志状态对象) + /// public virtual ILog State(object state) { @@ -109,6 +158,10 @@ public virtual ILog State(object state) return this; } + #endregion + + #region Message(设置日志消息) + /// public virtual ILog Message(string message, params object[] args) { @@ -117,12 +170,24 @@ public virtual ILog Message(string message, params object[] args) return this; } + #endregion + + #region IsEnabled(是否启用) + /// public virtual bool IsEnabled(LogLevel logLevel) => Logger.IsEnabled(logLevel); + #endregion + + #region BeginScope(开始日志范围) + /// public virtual IDisposable BeginScope(TState state) => Logger.BeginScope(state); + #endregion + + #region LogTrace(写跟踪日志) + /// public virtual ILog LogTrace() { @@ -144,6 +209,10 @@ public virtual ILog LogTrace() } } + #endregion + + #region LogDebug(写调试日志) + /// public virtual ILog LogDebug() { @@ -165,6 +234,10 @@ public virtual ILog LogDebug() } } + #endregion + + #region LogInformation(写信息日志) + /// public virtual ILog LogInformation() { @@ -186,6 +259,10 @@ public virtual ILog LogInformation() } } + #endregion + + #region LogWarning(写警告日志) + /// public virtual ILog LogWarning() { @@ -207,6 +284,10 @@ public virtual ILog LogWarning() } } + #endregion + + #region LogError(写错误日志) + /// public virtual ILog LogError() { @@ -228,6 +309,10 @@ public virtual ILog LogError() } } + #endregion + + #region LogCritical(写致命日志) + /// public virtual ILog LogCritical() { @@ -249,6 +334,8 @@ public virtual ILog LogCritical() } } + #endregion + #region 辅助方法 /// @@ -267,8 +354,10 @@ protected virtual void AddLogContext() { if (LogContext == null) return; - Property("TraceId", LogContext.TraceId); - Property("Duration", LogContext.Stopwatch.Elapsed.Description()); + if(!string.IsNullOrWhiteSpace(LogContext.TraceId)) + Property("TraceId", LogContext.TraceId); + if (LogContext.Stopwatch != null) + Property("Duration", LogContext.Stopwatch.Elapsed.Description()); } /// @@ -384,7 +473,5 @@ protected virtual void Clear() } #endregion - - } } diff --git a/framework/src/Bing.Logging/Bing/Logging/LogContext.cs b/framework/src/Bing.Logging/Bing/Logging/LogContext.cs index 71f18240e..5547b6c05 100644 --- a/framework/src/Bing.Logging/Bing/Logging/LogContext.cs +++ b/framework/src/Bing.Logging/Bing/Logging/LogContext.cs @@ -38,6 +38,11 @@ public class LogContext /// public string Environment { get; set; } + /// + /// 标签列表 + /// + public List Tags { get; set; } = new List(); + /// /// 扩展数据 /// diff --git a/framework/src/Bing.Logging/Bing/Logging/NullLog.cs b/framework/src/Bing.Logging/Bing/Logging/NullLog.cs new file mode 100644 index 000000000..f9ecedfae --- /dev/null +++ b/framework/src/Bing.Logging/Bing/Logging/NullLog.cs @@ -0,0 +1,99 @@ +using System; +using Microsoft.Extensions.Logging; + +namespace Bing.Logging +{ + /// + /// 空日志操作 + /// + /// 日志类别 + public class NullLog : ILog + { + /// + /// 空日志操作实例 + /// + public static readonly ILog Instance = new NullLog(); + + /// + /// 设置日志事件标识 + /// + /// 日志事件标识 + public ILog EventId(EventId eventId) => this; + + /// + /// 设置异常 + /// + /// 异常 + public ILog Exception(Exception exception) => this; + + /// + /// 设置自定义扩展属性 + /// + /// 属性名 + /// 属性值 + public ILog Property(string propertyName, string propertyValue) => this; + + /// + /// 设置标签 + /// + /// 标签 + public ILog Tags(params string[] tags) => this; + + /// + /// 设置日志状态对象 + /// + /// 状态对象 + public ILog State(object state) => this; + + /// + /// 设置日志消息 + /// + /// 日志消息 + /// 日志消息参数 + public ILog Message(string message, params object[] args) => this; + + /// + /// 是否启用 + /// + /// 日志级别 + /// true=启用;false=禁用 + public bool IsEnabled(LogLevel logLevel) => false; + + /// + /// 开启日志范围 + /// + /// 日志状态类型 + /// 日志状态 + public IDisposable BeginScope(TState state) => new DisposeAction(() => { }); + + /// + /// 写跟踪日志 + /// + public ILog LogTrace() => this; + + /// + /// 写调试日志 + /// + public ILog LogDebug() => this; + + /// + /// 写信息日志 + /// + public ILog LogInformation() => this; + + /// + /// 写警告日志 + /// + public ILog LogWarning() => this; + + /// + /// 写错误日志 + /// + public ILog LogError() => this; + + /// + /// 写致命日志 + /// + public ILog LogCritical() => this; + } +} diff --git a/framework/src/Bing.Logs.Exceptionless/Bing/Logs/Exceptionless/ExceptionlessProvider.cs b/framework/src/Bing.Logs.Exceptionless/Bing/Logs/Exceptionless/ExceptionlessProvider.cs index d2174d5b9..163f9383a 100644 --- a/framework/src/Bing.Logs.Exceptionless/Bing/Logs/Exceptionless/ExceptionlessProvider.cs +++ b/framework/src/Bing.Logs.Exceptionless/Bing/Logs/Exceptionless/ExceptionlessProvider.cs @@ -130,6 +130,12 @@ private void SetUser(ILogContent content) /// 日志内容 private void SetSource(EventBuilder builder, ILogContent content) { + // 优先设置日志名称 + if (!string.IsNullOrWhiteSpace(content.LogName)) + { + builder.SetSource(content.LogName); + return; + } if (string.IsNullOrWhiteSpace(content.Url)) return; builder.SetSource(RemoveParams(content.Url, UrlFilter)); diff --git a/framework/src/Bing.Logs/Bing/Logs/Aspects/ErrorLogAttribute.cs b/framework/src/Bing.Logs/Bing/Logs/Aspects/ErrorLogAttribute.cs index 11e70c8ab..a6bd2edfd 100644 --- a/framework/src/Bing.Logs/Bing/Logs/Aspects/ErrorLogAttribute.cs +++ b/framework/src/Bing.Logs/Bing/Logs/Aspects/ErrorLogAttribute.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using AspectCore.DynamicProxy; using AspectCore.DynamicProxy.Parameters; -using Bing.Aspects.Base; +using Bing.Aspects; namespace Bing.Logs.Aspects { diff --git a/framework/src/Bing.Logs/Bing/Logs/Aspects/LogAttributeBase.cs b/framework/src/Bing.Logs/Bing/Logs/Aspects/LogAttributeBase.cs index 130cdc619..1581ae23d 100644 --- a/framework/src/Bing.Logs/Bing/Logs/Aspects/LogAttributeBase.cs +++ b/framework/src/Bing.Logs/Bing/Logs/Aspects/LogAttributeBase.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using AspectCore.DynamicProxy; using AspectCore.DynamicProxy.Parameters; -using Bing.Aspects.Base; +using Bing.Aspects; using Bing.Extensions; using Microsoft.Extensions.DependencyInjection; diff --git a/framework/src/Bing.Logs/Bing/Logs/Extensions/LogExtensions.cs b/framework/src/Bing.Logs/Bing/Logs/Extensions/LogExtensions.cs index e5b4d0e4e..3596c1b26 100644 --- a/framework/src/Bing.Logs/Bing/Logs/Extensions/LogExtensions.cs +++ b/framework/src/Bing.Logs/Bing/Logs/Extensions/LogExtensions.cs @@ -102,6 +102,13 @@ public static ILog Params(this ILog log, IDictionary dictionary) /// 标题 public static ILog Caption(this ILog log, string caption) => log.Set(content => content.Caption = caption); + /// + /// 追加标题 + /// + /// 日志操作 + /// 标题 + public static ILog AppendCaption(this ILog log, string caption) => log.Set(content => content.Caption = $"{content.Caption}{caption}"); + /// /// 设置Sql语句 /// diff --git a/framework/src/Bing.Permissions/Authorization/Middlewares/JsonWebTokenCustomerAuthorizeOption.cs b/framework/src/Bing.Permissions/Authorization/Middlewares/JsonWebTokenCustomerAuthorizeOption.cs index 67a63e561..87c96e4f2 100644 --- a/framework/src/Bing.Permissions/Authorization/Middlewares/JsonWebTokenCustomerAuthorizeOption.cs +++ b/framework/src/Bing.Permissions/Authorization/Middlewares/JsonWebTokenCustomerAuthorizeOption.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Bing.Extensions; +using System.Linq; using Bing.Permissions.Identity.JwtBearer; namespace Bing.Permissions.Authorization.Middlewares @@ -31,7 +31,7 @@ public JsonWebTokenCustomerAuthorizeOption() { } /// 路径列表 public List SetAnonymousPaths(IList urls) { - urls.ForEach(url => + urls.ToList().ForEach(url => { AnonymousPaths.Add(url); }); diff --git a/framework/src/Bing.Permissions/Identity/Models/UserBase.cs b/framework/src/Bing.Permissions/Identity/Models/UserBase.cs index c557b8f6f..6b6c6eb51 100644 --- a/framework/src/Bing.Permissions/Identity/Models/UserBase.cs +++ b/framework/src/Bing.Permissions/Identity/Models/UserBase.cs @@ -2,7 +2,7 @@ using Bing.Exceptions; using Bing.Extensions; using Bing.Security.Encryption; -using Bing.Validations; +using Bing.Validation; namespace Bing.Permissions.Identity.Models { @@ -51,7 +51,7 @@ private void InitUserName() /// /// 验证 /// - public override ValidationResultCollection Validate() + public override IValidationResult Validate() { if (UserName.IsEmpty()) throw new Warning(Bing.Permissions.Properties.SecurityResources.UserNameIsEmpty); diff --git a/framework/src/Bing.Security/Bing.Security.csproj b/framework/src/Bing.Security/Bing.Security.csproj index c54ed9865..01d7a9c97 100644 --- a/framework/src/Bing.Security/Bing.Security.csproj +++ b/framework/src/Bing.Security/Bing.Security.csproj @@ -5,4 +5,8 @@ + + + + diff --git a/framework/src/Bing.Uow/Bing.Uow.csproj b/framework/src/Bing.Uow/Bing.Uow.csproj new file mode 100644 index 000000000..7f0d30cd2 --- /dev/null +++ b/framework/src/Bing.Uow/Bing.Uow.csproj @@ -0,0 +1,14 @@ + + + Bing.Uow + Bing.Uow + Bing.Uow 是Bing应用框架的工作单元类库。 + + + + + + + + + diff --git a/framework/src/Bing/Bing/Uow/IUnitOfWork.cs b/framework/src/Bing.Uow/Bing/Uow/IUnitOfWork.cs similarity index 100% rename from framework/src/Bing/Bing/Uow/IUnitOfWork.cs rename to framework/src/Bing.Uow/Bing/Uow/IUnitOfWork.cs diff --git a/framework/src/Bing/Bing/Uow/IUnitOfWorkManager.cs b/framework/src/Bing.Uow/Bing/Uow/IUnitOfWorkManager.cs similarity index 100% rename from framework/src/Bing/Bing/Uow/IUnitOfWorkManager.cs rename to framework/src/Bing.Uow/Bing/Uow/IUnitOfWorkManager.cs diff --git a/framework/src/Bing/Bing/Uow/UnitOfWorkManager.cs b/framework/src/Bing.Uow/Bing/Uow/UnitOfWorkManager.cs similarity index 100% rename from framework/src/Bing/Bing/Uow/UnitOfWorkManager.cs rename to framework/src/Bing.Uow/Bing/Uow/UnitOfWorkManager.cs diff --git a/framework/src/Bing.Uow/dependency.props b/framework/src/Bing.Uow/dependency.props new file mode 100644 index 000000000..3d3894b51 --- /dev/null +++ b/framework/src/Bing.Uow/dependency.props @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Bing.Uow/references.props b/framework/src/Bing.Uow/references.props new file mode 100644 index 000000000..8c71d4f17 --- /dev/null +++ b/framework/src/Bing.Uow/references.props @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/framework/src/Bing.Validation.Abstractions/Bing.Validation.Abstractions.csproj b/framework/src/Bing.Validation.Abstractions/Bing.Validation.Abstractions.csproj index 49897f303..766badeb2 100644 --- a/framework/src/Bing.Validation.Abstractions/Bing.Validation.Abstractions.csproj +++ b/framework/src/Bing.Validation.Abstractions/Bing.Validation.Abstractions.csproj @@ -1,8 +1,8 @@ - + - + - + \ No newline at end of file diff --git a/framework/src/Bing.Validation.Abstractions/Bing/Validation/IHasValidationErrors.cs b/framework/src/Bing.Validation.Abstractions/Bing/Validation/IHasValidationErrors.cs deleted file mode 100644 index 136f10d40..000000000 --- a/framework/src/Bing.Validation.Abstractions/Bing/Validation/IHasValidationErrors.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace Bing.Validation -{ - /// - /// 验证错误 - /// - public interface IHasValidationErrors - { - /// - /// 验证错误集合 - /// - IList ValidationErrors { get; } - } -} diff --git a/framework/src/Bing.Validation.Abstractions/Bing/Validation/IValidationCallbackHandler.cs b/framework/src/Bing.Validation.Abstractions/Bing/Validation/IValidationCallbackHandler.cs new file mode 100644 index 000000000..aaa32c403 --- /dev/null +++ b/framework/src/Bing.Validation.Abstractions/Bing/Validation/IValidationCallbackHandler.cs @@ -0,0 +1,14 @@ +namespace Bing.Validation +{ + /// + /// 验证回调处理器 + /// + public interface IValidationCallbackHandler + { + /// + /// 处理 + /// + /// 验证结果 + void Handle(IValidationResult result); + } +} diff --git a/framework/src/Bing/Bing/Validations/Abstractions/IValidationResult.cs b/framework/src/Bing.Validation.Abstractions/Bing/Validation/IValidationResult.cs similarity index 96% rename from framework/src/Bing/Bing/Validations/Abstractions/IValidationResult.cs rename to framework/src/Bing.Validation.Abstractions/Bing/Validation/IValidationResult.cs index e913befbe..d4459a398 100644 --- a/framework/src/Bing/Bing/Validations/Abstractions/IValidationResult.cs +++ b/framework/src/Bing.Validation.Abstractions/Bing/Validation/IValidationResult.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace Bing.Validations.Abstractions +namespace Bing.Validation { /// /// 验证结果集合 diff --git a/framework/src/Bing.Validation.Abstractions/Bing/Validation/IVerifyModel.cs b/framework/src/Bing.Validation.Abstractions/Bing/Validation/IVerifyModel.cs new file mode 100644 index 000000000..a55228819 --- /dev/null +++ b/framework/src/Bing.Validation.Abstractions/Bing/Validation/IVerifyModel.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using Bing.Validation.Strategies; + +namespace Bing.Validation +{ + /// + /// 可验证模型 + /// + public interface IVerifyModel + { + /// + /// 验证 + /// + IValidationResult Validate(); + } + + /// + /// 可验证模型 + /// + /// 对象类型 + public interface IVerifyModel : IVerifyModel where TObject : class, IVerifyModel + { + /// + /// 设置验证处理器 + /// + /// 验证处理器 + void SetValidationCallback(IValidationCallbackHandler handler); + + /// + /// 使用全局验证规则 + /// + void UseValidationRules(); + + /// + /// 使用验证策略 + /// + /// 验证策略 + void UseStrategy(IValidationStrategy strategy); + + /// + /// 使用验证策略集合 + /// + /// 验证策略集合 + void UseStrategyList(IEnumerable> strategies); + } +} diff --git a/framework/src/Bing/Bing/Validations/Abstractions/IValidateStrategy.cs b/framework/src/Bing.Validation.Abstractions/Bing/Validation/Strategies/IValidationStrategy.cs similarity index 73% rename from framework/src/Bing/Bing/Validations/Abstractions/IValidateStrategy.cs rename to framework/src/Bing.Validation.Abstractions/Bing/Validation/Strategies/IValidationStrategy.cs index a0dcb895d..a67e66168 100644 --- a/framework/src/Bing/Bing/Validations/Abstractions/IValidateStrategy.cs +++ b/framework/src/Bing.Validation.Abstractions/Bing/Validation/Strategies/IValidationStrategy.cs @@ -1,11 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace Bing.Validations.Abstractions +namespace Bing.Validation.Strategies { /// /// 验证策略 /// - public interface IValidateStrategy + public interface IValidationStrategy { /// /// 策略名称 @@ -17,7 +17,7 @@ public interface IValidateStrategy /// 验证策略 /// /// 对象类型 - public interface IValidateStrategy : IValidateStrategy where TObject : class, IValidatable + public interface IValidationStrategy : IValidationStrategy where TObject : class, IVerifyModel { /// /// 验证 diff --git a/framework/src/Bing.Validation.Abstractions/project.dependency.props b/framework/src/Bing.Validation.Abstractions/project.dependency.props index cb7d9e3f0..f3213256d 100644 --- a/framework/src/Bing.Validation.Abstractions/project.dependency.props +++ b/framework/src/Bing.Validation.Abstractions/project.dependency.props @@ -1,5 +1,9 @@  - + + + + + \ No newline at end of file diff --git a/framework/src/Bing.Validation.Abstractions/project.props b/framework/src/Bing.Validation.Abstractions/project.props index d86539093..572feeea0 100644 --- a/framework/src/Bing.Validation.Abstractions/project.props +++ b/framework/src/Bing.Validation.Abstractions/project.props @@ -2,6 +2,6 @@ Bing.Validation.Abstractions Bing.Validation.Abstractions - Bing.Validation.Abstractions 是Bing应用框架的校验抽象类库。 + Bing.Validation.Abstractions是Bing应用框架的验证模块的抽象库。 \ No newline at end of file diff --git a/framework/src/Bing.Validation/Bing.Validation.csproj b/framework/src/Bing.Validation/Bing.Validation.csproj index c54ed9865..01d7a9c97 100644 --- a/framework/src/Bing.Validation/Bing.Validation.csproj +++ b/framework/src/Bing.Validation/Bing.Validation.csproj @@ -5,4 +5,8 @@ + + + + diff --git a/framework/src/Bing/Bing/Validations/DataAnnotationValidation.cs b/framework/src/Bing.Validation/Bing/Validation/DataAnnotationValidation.cs similarity index 97% rename from framework/src/Bing/Bing/Validations/DataAnnotationValidation.cs rename to framework/src/Bing.Validation/Bing/Validation/DataAnnotationValidation.cs index 268757f42..c3cce7dee 100644 --- a/framework/src/Bing/Bing/Validations/DataAnnotationValidation.cs +++ b/framework/src/Bing.Validation/Bing/Validation/DataAnnotationValidation.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace Bing.Validations +namespace Bing.Validation { /// /// DataAnnotation 验证操作 diff --git a/framework/src/Bing.Validation/Bing/Validation/NothingHandler.cs b/framework/src/Bing.Validation/Bing/Validation/NothingHandler.cs new file mode 100644 index 000000000..0210c7629 --- /dev/null +++ b/framework/src/Bing.Validation/Bing/Validation/NothingHandler.cs @@ -0,0 +1,16 @@ +namespace Bing.Validation +{ + /// + /// 验证失败,不做任何处理 + /// + public class NothingHandler : IValidationCallbackHandler + { + /// + /// 处理验证错误 + /// + /// 验证结果 + public void Handle(IValidationResult result) + { + } + } +} diff --git a/framework/src/Bing.Validation/Bing/Validation/ThrowHandler.cs b/framework/src/Bing.Validation/Bing/Validation/ThrowHandler.cs new file mode 100644 index 000000000..867946196 --- /dev/null +++ b/framework/src/Bing.Validation/Bing/Validation/ThrowHandler.cs @@ -0,0 +1,22 @@ +using System.Linq; +using Bing.Exceptions; + +namespace Bing.Validation +{ + /// + /// 验证失败,抛出异常 - 默认验证处理器 + /// + public class ThrowHandler : IValidationCallbackHandler + { + /// + /// 处理验证错误 + /// + /// 验证错误 + public void Handle(IValidationResult result) + { + if (result.IsValid) + return; + throw new Warning(result.First().ErrorMessage); + } + } +} diff --git a/framework/src/Bing/Bing/Validations/Aspects/ValidAttribute.cs b/framework/src/Bing.Validation/Bing/Validation/ValidAttribute.cs similarity index 84% rename from framework/src/Bing/Bing/Validations/Aspects/ValidAttribute.cs rename to framework/src/Bing.Validation/Bing/Validation/ValidAttribute.cs index 5c6d93f0b..f3f48f1de 100644 --- a/framework/src/Bing/Bing/Validations/Aspects/ValidAttribute.cs +++ b/framework/src/Bing.Validation/Bing/Validation/ValidAttribute.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using AspectCore.DynamicProxy.Parameters; -using Bing.Aspects.Base; -using Bing.Validations.Abstractions; +using Bing.Aspects; -namespace Bing.Validations.Aspects +namespace Bing.Validation { /// /// 验证拦截器 @@ -31,7 +30,7 @@ private void Validate(Parameter parameter) ValidateCollection(parameter); return; } - var validation = parameter.Value as IValidatable; + var validation = parameter.Value as IVerifyModel; validation?.Validate(); } @@ -41,7 +40,7 @@ private void Validate(Parameter parameter) /// 参数 private void ValidateCollection(Parameter parameter) { - if (!(parameter.Value is IEnumerable validations)) + if (!(parameter.Value is IEnumerable validations)) return; foreach (var validation in validations) validation.Validate(); diff --git a/framework/src/Bing/Bing/Validations/ValidationContext.cs b/framework/src/Bing.Validation/Bing/Validation/ValidationContext.cs similarity index 88% rename from framework/src/Bing/Bing/Validations/ValidationContext.cs rename to framework/src/Bing.Validation/Bing/Validation/ValidationContext.cs index b8cf52e19..c796c202e 100644 --- a/framework/src/Bing/Bing/Validations/ValidationContext.cs +++ b/framework/src/Bing.Validation/Bing/Validation/ValidationContext.cs @@ -1,16 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; -using Bing.Configuration; -using Bing.Validations.Abstractions; +using Bing.Validation.Strategies; -namespace Bing.Validations +namespace Bing.Validation { /// /// 验证上下文 /// /// 对象类型 - public class ValidationContext where TObject : class, IValidatable + public class ValidationContext where TObject : class, IVerifyModel { /// /// 对象实例 @@ -20,7 +19,7 @@ public class ValidationContext where TObject : class, IValidatable /// /// 验证策略列表 /// - private List> ValidateStrategyList { get; } + private List> ValidateStrategyList { get; } /// /// 验证结果集合 @@ -39,7 +38,7 @@ public class ValidationContext where TObject : class, IValidatable public ValidationContext(TObject instanceToValidate) { Instance = instanceToValidate; - ValidateStrategyList = new List>(); + ValidateStrategyList = new List>(); } /// @@ -51,7 +50,7 @@ public ValidationContext(TObject instanceToValidate) /// 添加验证策略 /// /// 验证策略 - public void AddStrategy(IValidateStrategy strategy) + public void AddStrategy(IValidationStrategy strategy) { if (strategy == null) throw new ArgumentNullException(nameof(strategy)); @@ -64,7 +63,7 @@ public void AddStrategy(IValidateStrategy strategy) /// 添加验证策略列表 /// /// 验证策略列表 - public void AddStrategyList(IEnumerable> strategies) + public void AddStrategyList(IEnumerable> strategies) { if (strategies == null) throw new ArgumentNullException(nameof(strategies)); @@ -113,7 +112,7 @@ public void Validate(Action appendAction = null) if (ResultCollection.IsValid) return; if (Handle == null) - Handle = op => op.HandleAll(BingConfig.Current.ValidationHandler);// 如果没有处理器操作,则使用默认操作 + Handle = op => op.HandleAll(ValidationMe.ExposeCallbackHandler());// 如果没有处理器操作,则使用默认操作 Handle.Invoke(ResultCollection.Handle()); } diff --git a/framework/src/Bing/Bing/Validations/ValidationException.cs b/framework/src/Bing.Validation/Bing/Validation/ValidationException.cs similarity index 99% rename from framework/src/Bing/Bing/Validations/ValidationException.cs rename to framework/src/Bing.Validation/Bing/Validation/ValidationException.cs index b44b45087..9a8073002 100644 --- a/framework/src/Bing/Bing/Validations/ValidationException.cs +++ b/framework/src/Bing.Validation/Bing/Validation/ValidationException.cs @@ -5,7 +5,7 @@ using System.Text; using Bing.Exceptions; -namespace Bing.Validations +namespace Bing.Validation { /// /// 验证异常 diff --git a/framework/src/Bing/Bing/Validations/ValidationExceptionExtensions.cs b/framework/src/Bing.Validation/Bing/Validation/ValidationExceptionExtensions.cs similarity index 98% rename from framework/src/Bing/Bing/Validations/ValidationExceptionExtensions.cs rename to framework/src/Bing.Validation/Bing/Validation/ValidationExceptionExtensions.cs index 70089d30c..1f075c199 100644 --- a/framework/src/Bing/Bing/Validations/ValidationExceptionExtensions.cs +++ b/framework/src/Bing.Validation/Bing/Validation/ValidationExceptionExtensions.cs @@ -1,8 +1,7 @@ using System; using Bing.Reflection; -using Bing.Validations.Abstractions; -namespace Bing.Validations +namespace Bing.Validation { /// /// 验证异常扩展 diff --git a/framework/src/Bing/Bing/Validations/ValidationHandleExceptionExtensions.cs b/framework/src/Bing.Validation/Bing/Validation/ValidationHandleExceptionExtensions.cs similarity index 88% rename from framework/src/Bing/Bing/Validations/ValidationHandleExceptionExtensions.cs rename to framework/src/Bing.Validation/Bing/Validation/ValidationHandleExceptionExtensions.cs index a3453f4fa..f010deb94 100644 --- a/framework/src/Bing/Bing/Validations/ValidationHandleExceptionExtensions.cs +++ b/framework/src/Bing.Validation/Bing/Validation/ValidationHandleExceptionExtensions.cs @@ -1,7 +1,6 @@ using System; -using Bing.Validations.Abstractions; -namespace Bing.Validations +namespace Bing.Validation { /// /// 验证处理异常扩展 @@ -19,7 +18,7 @@ public static class ValidationHandleExceptionExtensions /// /// 验证处理操作 /// 验证处理器 - public static ValidationHandleOperation HandleAll(this ValidationHandleOperation op, IValidationHandler handler) + public static ValidationHandleOperation HandleAll(this ValidationHandleOperation op, IValidationCallbackHandler handler) { if (op == null) throw new ArgumentNullException(nameof(op)); diff --git a/framework/src/Bing/Bing/Validations/ValidationHandleOperation.cs b/framework/src/Bing.Validation/Bing/Validation/ValidationHandleOperation.cs similarity index 88% rename from framework/src/Bing/Bing/Validations/ValidationHandleOperation.cs rename to framework/src/Bing.Validation/Bing/Validation/ValidationHandleOperation.cs index f1efc757b..bd1b00710 100644 --- a/framework/src/Bing/Bing/Validations/ValidationHandleOperation.cs +++ b/framework/src/Bing.Validation/Bing/Validation/ValidationHandleOperation.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Bing.Validations.Abstractions; -namespace Bing.Validations +namespace Bing.Validation { /// /// 验证处理操作 @@ -26,7 +25,7 @@ public sealed class ValidationHandleOperation /// /// 验证处理器 /// 过滤函数 - internal void Handle(IValidationHandler handler, Action> filterFunc = null) + internal void Handle(IValidationCallbackHandler handler, Action> filterFunc = null) { if (handler == null) throw new ArgumentNullException(nameof(handler)); @@ -41,7 +40,7 @@ internal void Handle(IValidationHandler handler, Action /// 验证处理器 /// 策略名称 - internal void Handle(IValidationHandler handler, string strategyName) + internal void Handle(IValidationCallbackHandler handler, string strategyName) { if (handler == null) throw new ArgumentNullException(nameof(handler)); diff --git a/framework/src/Bing.Validation/Bing/Validation/ValidationMe.cs b/framework/src/Bing.Validation/Bing/Validation/ValidationMe.cs new file mode 100644 index 000000000..2d749f78d --- /dev/null +++ b/framework/src/Bing.Validation/Bing/Validation/ValidationMe.cs @@ -0,0 +1,32 @@ +namespace Bing.Validation +{ + /// + /// 验证组件静态入口 + /// + public static class ValidationMe + { + /// + /// 默认验证回调处理器 + /// + private static IValidationCallbackHandler DefaultCallbackHandler { get; set; } + + /// + /// 初始化一个静态构造函数 + /// + static ValidationMe() + { + DefaultCallbackHandler = new ThrowHandler(); + } + + /// + /// 注册验证回调处理器 + /// + /// 验证回调处理器 + public static void RegisterCallbackHandler(IValidationCallbackHandler handler) => DefaultCallbackHandler = handler; + + /// + /// 公开验证回调处理器 + /// + internal static IValidationCallbackHandler ExposeCallbackHandler() => DefaultCallbackHandler; + } +} diff --git a/framework/src/Bing.Validation/Bing/Validation/ValidationResultCollection.cs b/framework/src/Bing.Validation/Bing/Validation/ValidationResultCollection.cs index ce2d2675a..806adde96 100644 --- a/framework/src/Bing.Validation/Bing/Validation/ValidationResultCollection.cs +++ b/framework/src/Bing.Validation/Bing/Validation/ValidationResultCollection.cs @@ -1,63 +1,299 @@ -using System.Collections.Generic; +using System; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Text; namespace Bing.Validation { /// /// 验证结果集合 /// - public class ValidationResultCollection : List + public class ValidationResultCollection : IValidationResult { /// - /// 是否有效 + /// 无名称 /// - public bool IsValid => Count == 0; + private const string NoName = "unamed"; /// - /// 成功验证结果集合 + /// 验证结果集合 + /// + private readonly List _results; + + /// + /// 策略验证结果。策略 - 验证结果集合 /// - // ReSharper disable once InconsistentNaming - public static readonly ValidationResultCollection Success = new ValidationResultCollection(); + private readonly IDictionary> _resultsFlaggedByStrategy; /// /// 初始化一个类型的实例 /// - public ValidationResultCollection() : this("") + public ValidationResultCollection() { + _results = new List(); + _resultsFlaggedByStrategy = new Dictionary>(); + UpdateResultFlaggedByStrategy(NoName, new List()); } /// /// 初始化一个类型的实例 /// - /// 验证结果 + /// 结果 public ValidationResultCollection(string result) { + _results = new List(); if (string.IsNullOrWhiteSpace(result)) return; - Add(new ValidationResult(result)); + _results.Add(new ValidationResult(result)); + } + + /// + /// 初始化一个类型的实例 + /// + /// 验证结果 + public ValidationResultCollection(ValidationResult result) : this() + { + if (result == null) + throw new ArgumentNullException(nameof(result)); + _results.Add(result); + UpdateResultFlaggedByStrategy(NoName, result); + } + + /// + /// 初始化一个类型的实例 + /// + /// 验证结果 + /// 策略名称 + public ValidationResultCollection(ValidationResult result, string strategyName) : this() + { + if (result == null) + throw new ArgumentNullException(nameof(result)); + _results.Add(result); + UpdateResultFlaggedByStrategy(strategyName, result); } /// - /// 添加验证结果集合 + /// 初始化一个类型的实例 /// /// 验证结果集合 - public void AddList(IEnumerable results) + public ValidationResultCollection(IEnumerable results) : this() { if (results == null) + throw new ArgumentNullException(nameof(results)); + _results.AddRange(results); + UpdateResultFlaggedByStrategy(NoName, results.ToList()); + } + + /// + /// 初始化一个类型的实例 + /// + /// 验证结果集合 + /// 策略名称 + public ValidationResultCollection(IEnumerable results, string strategyName) : this() + { + if (results == null) + throw new ArgumentNullException(nameof(results)); + _results.AddRange(results); + UpdateResultFlaggedByStrategy(strategyName, results.ToList()); + } + + /// + /// 初始化一个类型的实例 + /// + /// 验证结果集合 + public ValidationResultCollection(ValidationResultCollection collection) : this() + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + ErrorCode = collection.ErrorCode; + Flag = collection.Flag; + _results = collection._results; + UpdateResultFlaggedByStrategy(collection); + } + + /// + /// 验证结果计数 + /// + public int Count => _results.Count; + + /// + /// 是否已验证 + /// + public bool IsValid => _results.Count == 0; + + /// + /// 错误码 + /// + public long ErrorCode { get; set; } = 1001; + + /// + /// 标识 + /// + public string Flag { get; set; } = "__EMPTY_FLG"; + + /// + /// 成功验证结果集合 + /// + public static ValidationResultCollection Success { get; } = new ValidationResultCollection(); + + /// + /// 添加 + /// + /// 验证结果 + public void Add(ValidationResult result) + { + if (result == null) return; - foreach (var result in results) - Add(result); + _results.Add(result); } /// - /// 输出验证消息 + /// 添加集合 /// - public override string ToString() + /// 验证结果集合 + public void AddRange(IEnumerable results) + { + if (results == null) + return; + _results.AddRange(results); + } + + /// + /// 转换为消息 + /// + public string ToMessage() { + var builder = new StringBuilder(); if (IsValid) - return string.Empty; - return this.First().ErrorMessage; + builder.Append("未发现验证错误。"); + else if (Count == 1) + builder.Append("发现1个验证错误,请检查详细信息。"); + else + builder.Append($"发现{Count}个验证错误,请检查详细信息。"); + builder.AppendLine(); + builder.Append($" (code: {ErrorCode}, Flag: {Flag})"); + return builder.ToString(); + } + + /// + /// 转换为验证消息集合 + /// + public IEnumerable ToValidationMessages() + { + return IsValid ? Enumerable.Empty() : __getErrorStringList(); + + // ReSharper disable once InconsistentNaming + IEnumerable __getErrorStringList() + { + foreach (var error in _results) + yield return $"{error.MemberNames.FirstOrDefault()}, {error.ErrorMessage}"; + } + } + + /// + /// 获取错误字符串 + /// + /// 空格数量 + private StringBuilder GetErrorString(int spaceCount = 0) + { + var space = ' '.Repeat(spaceCount); + var sb = new StringBuilder(); + foreach (var error in _results) + sb.AppendLine($"{space}{error.MemberNames.FirstOrDefault()}, {error.ErrorMessage}"); + return sb; + } + + /// + /// 获取迭代器 + /// + public IEnumerator GetEnumerator() => _results.GetEnumerator(); + + /// + /// 获取迭代器 + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// 输出字符串 + /// + public override string ToString() + { + var sb = new StringBuilder(); + sb.AppendLine(ToMessage()); + sb.AppendLine("详情:"); + + sb.Append(GetErrorString(6)); + sb.AppendLine(); + + return sb.ToString(); + } + + /// + /// 更新指定策略的验证结果 + /// + /// 验证结果集合 + private void UpdateResultFlaggedByStrategy(ValidationResultCollection collection) + { + if (collection == null) + return; + foreach (var set in collection._resultsFlaggedByStrategy) + UpdateResultFlaggedByStrategy(set.Key, set.Value); + } + + /// + /// 更新指定策略的验证结果 + /// + /// 策略名称 + /// 验证结果集合 + private void UpdateResultFlaggedByStrategy(string name, List results) + { + if (results == null || string.IsNullOrWhiteSpace(name)) + return; + if (_resultsFlaggedByStrategy.ContainsKey(name)) + _resultsFlaggedByStrategy[name].AddRange(results); + else + _resultsFlaggedByStrategy.Add(name, results); + } + + /// + /// 更新指定策略的验证结果 + /// + /// 策略名称 + /// 验证结果 + private void UpdateResultFlaggedByStrategy(string name, ValidationResult result) + { + if (result == null || string.IsNullOrWhiteSpace(name)) + return; + if (_resultsFlaggedByStrategy.ContainsKey(name)) + _resultsFlaggedByStrategy[name].Add(result); + else + _resultsFlaggedByStrategy.Add(name, new List { result }); + } + + /// + /// 过滤 + /// + /// 过滤操作 + internal ValidationResultCollection Filter(Action> filter) + { + var ret = _results; + filter?.Invoke(ret); + return ret.Count == 0 ? null : new ValidationResultCollection(ret); + } + + /// + /// 过滤 + /// + /// 策略名称 + internal ValidationResultCollection Filter(string strategyName) + { + if (string.IsNullOrWhiteSpace(strategyName)) + strategyName = NoName; + return _resultsFlaggedByStrategy.TryGetValue(strategyName, out var ret) + ? new ValidationResultCollection(ret) + : null; } } } diff --git a/framework/src/Bing/Validations/Validators/ChineseAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/ChineseAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/ChineseAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/ChineseAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/HttpUrlAddressAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/HttpUrlAddressAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/HttpUrlAddressAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/HttpUrlAddressAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/IdCardAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/IdCardAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/IdCardAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/IdCardAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/LetterAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/LetterAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/LetterAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/LetterAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/MoneyAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/MoneyAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/MoneyAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/MoneyAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/PlateNumberOfChinaAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/PlateNumberOfChinaAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/PlateNumberOfChinaAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/PlateNumberOfChinaAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/PostalCodeOfChinaAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/PostalCodeOfChinaAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/PostalCodeOfChinaAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/PostalCodeOfChinaAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/QQAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/QQAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/QQAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/QQAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/TelNoOfChinaAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/TelNoOfChinaAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/TelNoOfChinaAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/TelNoOfChinaAttribute.cs diff --git a/framework/src/Bing/Validations/Validators/ValidatePattern.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/ValidatePattern.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/ValidatePattern.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/ValidatePattern.cs diff --git a/framework/src/Bing/Validations/Validators/WechatNoAttribute.cs b/framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/WechatNoAttribute.cs similarity index 100% rename from framework/src/Bing/Validations/Validators/WechatNoAttribute.cs rename to framework/src/Bing.Validation/System/ComponentModel/DataAnnotations/WechatNoAttribute.cs diff --git a/framework/src/Bing.Validation/project.dependency.props b/framework/src/Bing.Validation/project.dependency.props index f3213256d..67387343f 100644 --- a/framework/src/Bing.Validation/project.dependency.props +++ b/framework/src/Bing.Validation/project.dependency.props @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/framework/src/Bing/Bing.csproj b/framework/src/Bing/Bing.csproj index 5493439aa..857f570ea 100644 --- a/framework/src/Bing/Bing.csproj +++ b/framework/src/Bing/Bing.csproj @@ -1,13 +1,35 @@  - - - + + Bing.Core + Bing.Core + Bing.Core是Bing应用框架的核心类库。 + + + True + True + LibraryResource.resx + + + True + True + R.resx + + + + PublicResXFileCodeGenerator + LibraryResource.Designer.cs + + + PublicResXFileCodeGenerator + R.Designer.cs + + diff --git a/framework/src/Bing/Bing/Aspects/Base/InterceptorBase.cs b/framework/src/Bing/Bing/Aspects/Base/InterceptorBase.cs deleted file mode 100644 index 43f129444..000000000 --- a/framework/src/Bing/Bing/Aspects/Base/InterceptorBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -using AspectCore.DynamicProxy; - -namespace Bing.Aspects.Base -{ - /// - /// 拦截器基类 - /// - public abstract class InterceptorBase : AbstractInterceptorAttribute - { - } -} diff --git a/framework/src/Bing/Bing/Aspects/Base/ParameterInterceptorBase.cs b/framework/src/Bing/Bing/Aspects/Base/ParameterInterceptorBase.cs deleted file mode 100644 index 5f87c1216..000000000 --- a/framework/src/Bing/Bing/Aspects/Base/ParameterInterceptorBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -using AspectCore.DynamicProxy.Parameters; - -namespace Bing.Aspects.Base -{ - /// - /// 参数拦截器基类 - /// - public abstract class ParameterInterceptorBase : ParameterInterceptorAttribute - { - } -} diff --git a/framework/src/Bing/Bing/Aspects/IBingInterceptor.cs b/framework/src/Bing/Bing/Aspects/IBingInterceptor.cs deleted file mode 100644 index 6dc7439ec..000000000 --- a/framework/src/Bing/Bing/Aspects/IBingInterceptor.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; - -namespace Bing.Aspects -{ - /// - /// 拦截器 - /// - public interface IBingInterceptor - { - /// - /// 拦截 - /// - /// 方法调用 - Task InterceptAsync(IBingMethodInvocation invocation); - } -} diff --git a/framework/src/Bing/Bing/Aspects/IBingMethodInvocation.cs b/framework/src/Bing/Bing/Aspects/IBingMethodInvocation.cs deleted file mode 100644 index e7ca2bef8..000000000 --- a/framework/src/Bing/Bing/Aspects/IBingMethodInvocation.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using System.Threading.Tasks; - -namespace Bing.Aspects -{ - /// - /// 方法调用 - /// - public interface IBingMethodInvocation - { - /// - /// 目标对象 - /// - object TargetObject { get; } - - /// - /// 方法 - /// - MethodInfo Method { get; } - - /// - /// 参数数组 - /// - object[] Parameters { get; } - - /// - /// 返回值 - /// - object ReturnValue { get; set; } - - /// - /// 属性 - /// - IDictionary Properties { get; } - - /// - /// 处理并继续 - /// - Task ProceedAsync(); - } -} diff --git a/framework/src/Bing/Bing/Caching/CacheAttribute.cs b/framework/src/Bing/Bing/Caching/CacheAttribute.cs index f626cceff..53cd33515 100644 --- a/framework/src/Bing/Bing/Caching/CacheAttribute.cs +++ b/framework/src/Bing/Bing/Caching/CacheAttribute.cs @@ -1,179 +1,179 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using AspectCore.DependencyInjection; -using AspectCore.DynamicProxy; -using Bing.Aspects.Base; -using Bing.Exceptions; -using Bing.Threading.Asyncs; - -namespace Bing.Caching -{ - /// - /// 缓存 属性 - /// - [AttributeUsage(AttributeTargets.Method)] - public class CacheAttribute : InterceptorBase - { - /// - /// 缓存键值。可以附加参数,例如:UserInfo{model:Nmae}_{type} - /// - public string Key { get; set; } - - /// - /// 过期时间间隔 - /// - public TimeSpan? Expiration { get; set; } - - /// - /// 拼接缓存键参数 - /// - private static readonly ConcurrentDictionary> AppendKeyParameters; - - /// - /// 异步结果方法 - /// - private static readonly MethodInfo TaskResultMethod; - - /// - /// 异步锁 - /// - private readonly AsyncLock _lock = new AsyncLock(); - - /// - /// 缓存 - /// - [FromServiceContext] - public ICache Cache { get; set; } - - /// - /// 静态构造函数 - /// - static CacheAttribute() - { - AppendKeyParameters = new ConcurrentDictionary>(); - TaskResultMethod = typeof(Task).GetMethods() - .FirstOrDefault(p => p.Name == "FromResult" && p.ContainsGenericParameters); - } - - /// - /// 执行 - /// - public override async Task Invoke(AspectContext context, AspectDelegate next) - { - List appendKeyArray = null; - if (string.IsNullOrWhiteSpace(Key)) - { - Key = - $"{context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name}:{context.ServiceMethod.ToString()}"; - } - else - { - if (!AppendKeyParameters.TryGetValue(context.ImplementationMethod, out appendKeyArray)) - { - if (Key.IndexOf("{", StringComparison.Ordinal) > -1) - { - appendKeyArray = new List(); - var matchs = Regex.Matches(Key, @"\{\w*\:?\w*\}", RegexOptions.None); - foreach (Match match in matchs) - { - if (match.Success) - { - appendKeyArray.Add(match.Value.TrimStart('{').TrimEnd('}')); - } - } - } - - AppendKeyParameters.TryAdd(context.ImplementationMethod, appendKeyArray); - } - } - - var currentCacheKey = Key; - - if (appendKeyArray != null && appendKeyArray.Count > 0) - { - // 获取方法的参数 - var pars = context.ProxyMethod.GetParameters(); - - // 设置参数名和值,加入字典 - var dicValue = new Dictionary(); - for (var i = 0; i < pars.Length; i++) - { - dicValue.Add(pars[i].Name, context.Parameters[i]); - } - - foreach (var key in appendKeyArray) - { - if (key.Contains(":")) - { - var arr = key.Split(':'); - var keyFirst = arr[0]; - var keySecond = arr[1]; - - if (!dicValue.TryGetValue(keyFirst, out var value)) - { - throw new Warning( - $"Cache {context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name} 不包含参数 {keyFirst}"); - } - - var ob = Internal.Helper.ToDictionary(value); - if (!ob.TryGetValue(keySecond, out object tokenValue)) - { - throw new Warning( - $"Cache {context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name} {keyFirst} 不包含参数 {keySecond}"); - } - - currentCacheKey = currentCacheKey.Replace("{" + key + "}", tokenValue.ToString()); - } - else - { - if (!dicValue.TryGetValue(key, out var value)) - { - throw new Warning( - $"Cache {context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name} 不包含参数 {key}"); - } - - currentCacheKey = currentCacheKey.Replace("{" + key + "}", value.ToString()); - } - } - } - - // 返回值类型 - var returnType = context.IsAsync() - ? context.ServiceMethod.ReturnType.GetGenericArguments().First() - : context.ServiceMethod.ReturnType; - - // 从缓存取值 - var cacheValue = await Cache.GetAsync(currentCacheKey, returnType); - if (cacheValue != null) - { - context.ReturnValue = context.IsAsync() - ? TaskResultMethod.MakeGenericMethod(returnType).Invoke(null, new object[] { cacheValue }) - : cacheValue; - return; - } - - using (await _lock.LockAsync()) - { - cacheValue = await Cache.GetAsync(currentCacheKey, returnType); - if (cacheValue != null) - { - context.ReturnValue = context.IsAsync() - ? TaskResultMethod.MakeGenericMethod(returnType).Invoke(null, new object[] { cacheValue }) - : cacheValue; - } - else - { - await next(context); - dynamic returnValue = - context.IsAsync() ? await context.UnwrapAsyncReturnValue() : context.ReturnValue; - Cache.TryAdd(currentCacheKey, returnValue, Expiration); - } - } - } - } -} +//using System; +//using System.Collections.Concurrent; +//using System.Collections.Generic; +//using System.Linq; +//using System.Reflection; +//using System.Text.RegularExpressions; +//using System.Threading.Tasks; +//using AspectCore.DependencyInjection; +//using AspectCore.DynamicProxy; +//using Bing.Aspects.Base; +//using Bing.Exceptions; +//using Bing.Threading.Asyncs; + +//namespace Bing.Caching +//{ +// /// +// /// 缓存 属性 +// /// +// [AttributeUsage(AttributeTargets.Method)] +// public class CacheAttribute : InterceptorBase +// { +// /// +// /// 缓存键值。可以附加参数,例如:UserInfo{model:Nmae}_{type} +// /// +// public string Key { get; set; } + +// /// +// /// 过期时间间隔 +// /// +// public TimeSpan? Expiration { get; set; } + +// /// +// /// 拼接缓存键参数 +// /// +// private static readonly ConcurrentDictionary> AppendKeyParameters; + +// /// +// /// 异步结果方法 +// /// +// private static readonly MethodInfo TaskResultMethod; + +// /// +// /// 异步锁 +// /// +// private readonly AsyncLock _lock = new AsyncLock(); + +// /// +// /// 缓存 +// /// +// [FromServiceContext] +// public ICache Cache { get; set; } + +// /// +// /// 静态构造函数 +// /// +// static CacheAttribute() +// { +// AppendKeyParameters = new ConcurrentDictionary>(); +// TaskResultMethod = typeof(Task).GetMethods() +// .FirstOrDefault(p => p.Name == "FromResult" && p.ContainsGenericParameters); +// } + +// /// +// /// 执行 +// /// +// public override async Task Invoke(AspectContext context, AspectDelegate next) +// { +// List appendKeyArray = null; +// if (string.IsNullOrWhiteSpace(Key)) +// { +// Key = +// $"{context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name}:{context.ServiceMethod.ToString()}"; +// } +// else +// { +// if (!AppendKeyParameters.TryGetValue(context.ImplementationMethod, out appendKeyArray)) +// { +// if (Key.IndexOf("{", StringComparison.Ordinal) > -1) +// { +// appendKeyArray = new List(); +// var matchs = Regex.Matches(Key, @"\{\w*\:?\w*\}", RegexOptions.None); +// foreach (Match match in matchs) +// { +// if (match.Success) +// { +// appendKeyArray.Add(match.Value.TrimStart('{').TrimEnd('}')); +// } +// } +// } + +// AppendKeyParameters.TryAdd(context.ImplementationMethod, appendKeyArray); +// } +// } + +// var currentCacheKey = Key; + +// if (appendKeyArray != null && appendKeyArray.Count > 0) +// { +// // 获取方法的参数 +// var pars = context.ProxyMethod.GetParameters(); + +// // 设置参数名和值,加入字典 +// var dicValue = new Dictionary(); +// for (var i = 0; i < pars.Length; i++) +// { +// dicValue.Add(pars[i].Name, context.Parameters[i]); +// } + +// foreach (var key in appendKeyArray) +// { +// if (key.Contains(":")) +// { +// var arr = key.Split(':'); +// var keyFirst = arr[0]; +// var keySecond = arr[1]; + +// if (!dicValue.TryGetValue(keyFirst, out var value)) +// { +// throw new Warning( +// $"Cache {context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name} 不包含参数 {keyFirst}"); +// } + +// var ob = Internal.Helper.ToDictionary(value); +// if (!ob.TryGetValue(keySecond, out object tokenValue)) +// { +// throw new Warning( +// $"Cache {context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name} {keyFirst} 不包含参数 {keySecond}"); +// } + +// currentCacheKey = currentCacheKey.Replace("{" + key + "}", tokenValue.ToString()); +// } +// else +// { +// if (!dicValue.TryGetValue(key, out var value)) +// { +// throw new Warning( +// $"Cache {context.ServiceMethod.DeclaringType}.{context.ImplementationMethod.Name} 不包含参数 {key}"); +// } + +// currentCacheKey = currentCacheKey.Replace("{" + key + "}", value.ToString()); +// } +// } +// } + +// // 返回值类型 +// var returnType = context.IsAsync() +// ? context.ServiceMethod.ReturnType.GetGenericArguments().First() +// : context.ServiceMethod.ReturnType; + +// // 从缓存取值 +// var cacheValue = await Cache.GetAsync(currentCacheKey, returnType); +// if (cacheValue != null) +// { +// context.ReturnValue = context.IsAsync() +// ? TaskResultMethod.MakeGenericMethod(returnType).Invoke(null, new object[] { cacheValue }) +// : cacheValue; +// return; +// } + +// using (await _lock.LockAsync()) +// { +// cacheValue = await Cache.GetAsync(currentCacheKey, returnType); +// if (cacheValue != null) +// { +// context.ReturnValue = context.IsAsync() +// ? TaskResultMethod.MakeGenericMethod(returnType).Invoke(null, new object[] { cacheValue }) +// : cacheValue; +// } +// else +// { +// await next(context); +// dynamic returnValue = +// context.IsAsync() ? await context.UnwrapAsyncReturnValue() : context.ReturnValue; +// Cache.TryAdd(currentCacheKey, returnValue, Expiration); +// } +// } +// } +// } +//} diff --git a/framework/src/Bing/Bing/Configuration/BingConfig.cs b/framework/src/Bing/Bing/Configuration/BingConfig.cs index fe1c20198..9d13d788a 100644 --- a/framework/src/Bing/Bing/Configuration/BingConfig.cs +++ b/framework/src/Bing/Bing/Configuration/BingConfig.cs @@ -1,7 +1,4 @@ -using Bing.Validations.Abstractions; -using Bing.Validations.Handlers; - -namespace Bing.Configuration +namespace Bing.Configuration { /// /// Bing 框架配置 @@ -37,11 +34,6 @@ public static BingConfig Current } } - /// - /// 验证处理器 - /// - public IValidationHandler ValidationHandler { get; set; } = new ThrowHandler(); - /// /// 是否启用调试日志 /// diff --git a/framework/src/Bing/Bing/Core/Modularity/BingModule.cs b/framework/src/Bing/Bing/Core/Modularity/BingModule.cs index 47e2d5b74..eb963a32b 100644 --- a/framework/src/Bing/Bing/Core/Modularity/BingModule.cs +++ b/framework/src/Bing/Bing/Core/Modularity/BingModule.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Bing.Extensions; using Microsoft.Extensions.DependencyInjection; @@ -45,11 +46,10 @@ public abstract class BingModule : IBingModule /// 模块类型 internal Type[] GetDependModuleTypes(Type moduleType = null) { - if (moduleType == null) - moduleType = GetType(); + moduleType ??= GetType(); var dependAttrs = moduleType.GetAttributes(true).ToList(); if (dependAttrs.Count == 0) - return new Type[0]; + return Type.EmptyTypes; var dependTypes = new List(); foreach (var dependAttr in dependAttrs) { @@ -62,5 +62,33 @@ internal Type[] GetDependModuleTypes(Type moduleType = null) } return dependTypes.Distinct().ToArray(); } + + #region 辅助方法 + + /// + /// 判断指定类型是否类型 + /// + /// 类型 + public static bool IsBingModule(Type type) + { + var typeInfo = type.GetTypeInfo(); + return typeInfo.IsClass && + !typeInfo.IsAbstract && + !typeInfo.IsGenericType && + typeof(IBingModule).GetTypeInfo().IsAssignableFrom(type); + } + + /// + /// 检查模块类型是否类型 + /// + /// 模块类型 + internal static void CheckBingModuleType(Type moduleType) + { + if (!IsBingModule(moduleType)) + throw new ArgumentException("Given type is not an Bing Module: " + moduleType.AssemblyQualifiedName); + } + + #endregion + } } diff --git a/framework/src/Bing/Bing/DependencyInjection/DependencyAttribute.cs b/framework/src/Bing/Bing/DependencyInjection/DependencyAttribute.cs index e3aba7beb..a790811fe 100644 --- a/framework/src/Bing/Bing/DependencyInjection/DependencyAttribute.cs +++ b/framework/src/Bing/Bing/DependencyInjection/DependencyAttribute.cs @@ -9,31 +9,31 @@ namespace Bing.DependencyInjection [AttributeUsage(AttributeTargets.Class)] public class DependencyAttribute : Attribute { + /// + /// 初始化一个类型的实例 + /// + /// 生命周期类型 + public DependencyAttribute(ServiceLifetime lifetime) => Lifetime = lifetime; + /// /// 声明周期类型,代替 - /// ,,三个接口的作用 + /// , , 三个接口的作用 /// public ServiceLifetime Lifetime { get; } /// - /// 是否为TryAdd方式注册,通常用于默认服务,当服务可被替换时,应设置为true + /// 是否为 TryAdd 方式注册,通常用于默认服务,当服务可被替换时,应设置为 true /// public bool TryAdd { get; set; } /// - /// 是否替换已存在的服务实现,通常用于主要服务,当服务存在时即优先使用时,应设置为true + /// 是否替换已存在的服务实现,通常用于主要服务,当服务存在时即优先使用时,应设置为 true /// public bool ReplaceExisting { get; set; } /// - /// 是否注册自身类型,默认没有接口的类型会注册自身,当此属性为true时,也会注册自身 + /// 是否注册自身类型,默认没有接口的类型会注册自身,当此属性为 true 时,也会注册自身 /// public bool AddSelf { get; set; } - - /// - /// 初始化一个类型的实例 - /// - /// 生命周期类型 - public DependencyAttribute(ServiceLifetime lifetime) => Lifetime = lifetime; } } diff --git a/framework/src/Bing/Bing/DependencyInjection/DependencyModule.cs b/framework/src/Bing/Bing/DependencyInjection/DependencyModule.cs index c0c225d48..9499ee31b 100644 --- a/framework/src/Bing/Bing/DependencyInjection/DependencyModule.cs +++ b/framework/src/Bing/Bing/DependencyInjection/DependencyModule.cs @@ -33,6 +33,7 @@ public override IServiceCollection AddServices(IServiceCollection services) { // 服务定位器设置 ServiceLocator.Instance.SetServiceCollection(services); + services.AddTransient(typeof(Lazy<>), typeof(Lazier<>));// 解决循环依赖问题 services.TryAddTransient(); services.AddScoped(); @@ -43,6 +44,7 @@ public override IServiceCollection AddServices(IServiceCollection services) var dependencyTypes = dependencyTypeFinder.FindAll(); foreach (var dependencyType in dependencyTypes) AddToServices(services, dependencyType); + return services; } @@ -121,7 +123,7 @@ protected virtual void AddToServices(IServiceCollection services, Type implement } /// - /// 重写以实现 从类型获取要注册的生命周期类型 + /// 重写以实现 从类型获取要注册的 生命周期类型 /// /// 依赖注入实现类型 protected virtual ServiceLifetime? GetLifetimeOrNull(Type type) diff --git a/framework/src/Bing/Bing/DependencyInjection/FromServiceAttribute.cs b/framework/src/Bing/Bing/DependencyInjection/FromServiceAttribute.cs deleted file mode 100644 index f9a628933..000000000 --- a/framework/src/Bing/Bing/DependencyInjection/FromServiceAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Bing.DependencyInjection -{ - /// - /// 属性注入 特性 - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - public sealed class FromServiceAttribute : Attribute - { - } -} diff --git a/framework/src/Bing/Bing/DependencyInjection/IObjectAccessor.cs b/framework/src/Bing/Bing/DependencyInjection/IObjectAccessor.cs index 9be29d7c5..ddd30cc5a 100644 --- a/framework/src/Bing/Bing/DependencyInjection/IObjectAccessor.cs +++ b/framework/src/Bing/Bing/DependencyInjection/IObjectAccessor.cs @@ -1,12 +1,9 @@ -using Bing.Aspects; - -namespace Bing.DependencyInjection +namespace Bing.DependencyInjection { /// /// 对象访问器 /// /// 类型 - [Ignore] public interface IObjectAccessor { /// diff --git a/framework/src/Bing/Bing/DependencyInjection/IScopedDependency.cs b/framework/src/Bing/Bing/DependencyInjection/IScopedDependency.cs index 9242e4533..b7ba97c81 100644 --- a/framework/src/Bing/Bing/DependencyInjection/IScopedDependency.cs +++ b/framework/src/Bing/Bing/DependencyInjection/IScopedDependency.cs @@ -3,8 +3,9 @@ namespace Bing.DependencyInjection { /// - /// 实现此接口的类型将注册为模式 + /// 实现此接口的类型将注册为 模式 /// + /// 生命周期为每次请求创建一个实例 [IgnoreDependency] public interface IScopedDependency { diff --git a/framework/src/Bing/Bing/DependencyInjection/ISingletonDependency.cs b/framework/src/Bing/Bing/DependencyInjection/ISingletonDependency.cs index 40f308001..894ac0b85 100644 --- a/framework/src/Bing/Bing/DependencyInjection/ISingletonDependency.cs +++ b/framework/src/Bing/Bing/DependencyInjection/ISingletonDependency.cs @@ -3,8 +3,9 @@ namespace Bing.DependencyInjection { /// - /// 实现此接口的类型将注册为模式 + /// 实现此接口的类型将注册为 模式 /// + /// 生命周期为单例 [IgnoreDependency] public interface ISingletonDependency { diff --git a/framework/src/Bing/Bing/DependencyInjection/ITransientDependency.cs b/framework/src/Bing/Bing/DependencyInjection/ITransientDependency.cs index d79cfb2c9..e6526e577 100644 --- a/framework/src/Bing/Bing/DependencyInjection/ITransientDependency.cs +++ b/framework/src/Bing/Bing/DependencyInjection/ITransientDependency.cs @@ -3,8 +3,9 @@ namespace Bing.DependencyInjection { /// - /// 实现此接口的类型将自动注册为模式 + /// 实现此接口的类型将自动注册为 模式 /// + /// 生命周期为每次创建一个新实例 [IgnoreDependency] public interface ITransientDependency { diff --git a/framework/src/Bing/Bing/DependencyInjection/LazyServiceProvider.cs b/framework/src/Bing/Bing/DependencyInjection/LazyServiceProvider.cs index 4b3775c88..7842052ee 100644 --- a/framework/src/Bing/Bing/DependencyInjection/LazyServiceProvider.cs +++ b/framework/src/Bing/Bing/DependencyInjection/LazyServiceProvider.cs @@ -13,7 +13,7 @@ public class LazyServiceProvider : ILazyServiceProvider, ITransientDependency /// /// 缓存服务字典 /// - protected IDictionary CachedServices { get; set; } + protected Dictionary CachedServices { get; set; } /// /// 服务提供程序 @@ -40,7 +40,7 @@ public LazyServiceProvider(IServiceProvider serviceProvider) /// 获取请求服务 /// /// 服务类型 - public virtual object LazyGetRequiredService(Type serviceType) => CachedServices.GetOrAdd(serviceType, () => ServiceProvider.GetRequiredService(serviceType)); + public virtual object LazyGetRequiredService(Type serviceType) => CachedServices.GetValueOrAdd(serviceType, _ => ServiceProvider.GetRequiredService(serviceType)); /// /// 获取服务 @@ -52,7 +52,7 @@ public LazyServiceProvider(IServiceProvider serviceProvider) /// 获取服务 /// /// 服务类型 - public virtual object LazyGetService(Type serviceType) => CachedServices.GetOrAdd(serviceType, () => ServiceProvider.GetService(serviceType)); + public virtual object LazyGetService(Type serviceType) => CachedServices.GetValueOrAdd(serviceType, _ => ServiceProvider.GetService(serviceType)); /// /// 获取服务 @@ -73,7 +73,7 @@ public LazyServiceProvider(IServiceProvider serviceProvider) /// /// 服务类型 /// 服务实例工厂 - public virtual object LazyGetService(Type serviceType, Func factory) => CachedServices.GetOrAdd(serviceType, () => factory(ServiceProvider)); + public virtual object LazyGetService(Type serviceType, Func factory) => CachedServices.GetValueOrAdd(serviceType, _ => factory(ServiceProvider)); /// /// 获取服务 diff --git a/framework/src/Bing/Bing/Exceptions/ConcurrencyException.cs b/framework/src/Bing/Bing/Exceptions/ConcurrencyException.cs index 0d797b77e..96eb91f71 100644 --- a/framework/src/Bing/Bing/Exceptions/ConcurrencyException.cs +++ b/framework/src/Bing/Bing/Exceptions/ConcurrencyException.cs @@ -60,5 +60,11 @@ public ConcurrencyException(string message, Exception exception, string code) : /// 错误消息 /// public override string Message => $"{LibraryResource.ConcurrencyExceptionMessage}.{_message}"; + + /// + /// 获取错误消息 + /// + /// 是否生产环境 + public override string GetMessage(bool isProduction = true) => isProduction ? LibraryResource.ConcurrencyExceptionMessage : GetMessage(this); } -} \ No newline at end of file +} diff --git a/framework/src/Bing/Bing/Exceptions/Prompts/ExceptionPrompt.cs b/framework/src/Bing/Bing/Exceptions/Prompts/ExceptionPrompt.cs index 8743c03c7..74d1a67e6 100644 --- a/framework/src/Bing/Bing/Exceptions/Prompts/ExceptionPrompt.cs +++ b/framework/src/Bing/Bing/Exceptions/Prompts/ExceptionPrompt.cs @@ -16,11 +16,6 @@ public static class ExceptionPrompt // ReSharper disable once InconsistentNaming private static readonly List Prompts = new List(); - /// - /// 是否显示系统异常消息 - /// - public static bool IsShowSystemException { get; set; } - /// /// 添加异常提示 /// @@ -38,7 +33,8 @@ public static void AddPrompt(IExceptionPrompt prompt) /// 获取异常提示 /// /// 异常 - public static string GetPrompt(Exception exception) + /// 是否生产环境 + public static string GetPrompt(Exception exception, bool isProduction) { if (exception == null) return null; @@ -47,10 +43,8 @@ public static string GetPrompt(Exception exception) if (string.IsNullOrWhiteSpace(prompt) == false) return prompt; if (exception is Warning warning) - return warning.Message; - if (IsShowSystemException) - return exception.Message; - return R.SystemError; + return warning.GetMessage(isProduction); + return isProduction ? R.SystemError : exception.Message; } /// @@ -67,5 +61,22 @@ private static string GetExceptionPrompt(Exception exception) } return string.Empty; } + + /// + /// 获取原始异常 + /// + /// 异常 + public static Exception GetException(Exception exception) + { + if (exception is null) + return null; + foreach (var prompt in Prompts) + { + var result = prompt.GetRawException(exception); + if (result != null) + return result; + } + return exception; + } } } diff --git a/framework/src/Bing/Bing/Exceptions/Prompts/IExceptionPrompt.cs b/framework/src/Bing/Bing/Exceptions/Prompts/IExceptionPrompt.cs index 1eab0e817..2804d17c7 100644 --- a/framework/src/Bing/Bing/Exceptions/Prompts/IExceptionPrompt.cs +++ b/framework/src/Bing/Bing/Exceptions/Prompts/IExceptionPrompt.cs @@ -12,5 +12,11 @@ public interface IExceptionPrompt /// /// 异常 string GetPrompt(Exception exception); + + /// + /// 获取原始异常 + /// + /// 异常 + Exception GetRawException(Exception exception); } } diff --git a/framework/src/Bing/Bing/Exceptions/Warning.cs b/framework/src/Bing/Bing/Exceptions/Warning.cs index 7c87780e0..17266cab9 100644 --- a/framework/src/Bing/Bing/Exceptions/Warning.cs +++ b/framework/src/Bing/Bing/Exceptions/Warning.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text; using Bing.ExceptionHandling; -using Bing.Extensions; using Bing.Properties; using Microsoft.Extensions.Logging; @@ -12,17 +11,8 @@ namespace Bing.Exceptions /// 应用程序异常 /// [Serializable] - public class Warning : Exception, IHasErrorCode + public class Warning : Exception, IHasErrorCode, IHasHttpStatusCode { - #region 属性 - - /// - /// 错误码 - /// - public string Code { get; set; } - - #endregion - #region 构造函数 /// @@ -46,27 +36,36 @@ public Warning(Exception exception) : this(null, null, exception) /// /// 错误消息 /// 错误码 - public Warning(string message, string code) : this(message, code, null) + /// 内部异常 + /// Http状态码 + public Warning(string message, string code = null, Exception exception = null, int httpStatusCode = default) + : base(message ?? "", exception) { + Code = code; + HttpStatusCode = httpStatusCode; } + #endregion + + #region 属性 + /// - /// 初始化一个类型的实例 + /// 错误码 /// - /// 错误消息 - /// 错误码 - /// 异常 - public Warning(string message, string code, Exception exception) : base(message ?? "", exception) - { - Code = code; - } + public string Code { get; set; } + + /// + /// Http状态码 + /// + public int HttpStatusCode { get; set; } #endregion /// /// 获取错误消息 /// - public string GetMessage() => GetMessage(this); + /// 是否生产环境 + public virtual string GetMessage(bool isProduction = true) => GetMessage(this); /// /// 获取错误消息 @@ -78,7 +77,7 @@ public static string GetMessage(Exception ex) var list = GetExceptions(ex); foreach (var exception in list) AppendMessage(result, exception); - return result.ToString().RemoveEnd(Environment.NewLine); + return result.ToString().Trim(Environment.NewLine.ToCharArray()); } /// diff --git a/framework/src/Bing/Bing/Extensions/ExceptionExtensions.cs b/framework/src/Bing/Bing/Extensions/ExceptionExtensions.cs index 1222d5ffb..e141bb97b 100644 --- a/framework/src/Bing/Bing/Extensions/ExceptionExtensions.cs +++ b/framework/src/Bing/Bing/Extensions/ExceptionExtensions.cs @@ -1,4 +1,5 @@ using System; +using Bing.Exceptions; using Bing.Exceptions.Prompts; // ReSharper disable once CheckNamespace @@ -13,22 +14,41 @@ public static partial class ExceptionExtensions /// 获取原始异常 /// /// 异常 - public static Exception GetRawException(this Exception exception) + public static Exception GetRawException(this Exception exception) => ExceptionPrompt.GetException(exception); + + /// + /// 获取异常提示 + /// + /// 异常 + /// 是否生产环境 + public static string GetPrompt(this Exception exception, bool isProduction = false) => ExceptionPrompt.GetPrompt(exception, isProduction); + + /// + /// 获取Http状态码 + /// + /// 异常 + public static int GetHttpStatusCode(this Exception exception) { - if (exception == null) - return null; - // 移除 Autofac - //if (exception is Autofac.Core.DependencyResolutionException dependencyResolutionException) - // return GetRawException(dependencyResolutionException.InnerException); - if (exception is AspectCore.DynamicProxy.AspectInvocationException aspectInvocationException) - return GetRawException(aspectInvocationException.InnerException); - return exception; + if (exception is null) + return 200; + exception = exception.GetRawException(); + if (exception is Warning warning) + return warning.HttpStatusCode; + return 200; } /// - /// 获取异常提示 + /// 获取错误码 /// /// 异常 - public static string GetPrompt(this Exception exception) => ExceptionPrompt.GetPrompt(exception); + public static string GetErrorCode(this Exception exception) + { + if (exception is null) + return null; + exception = exception.GetRawException(); + if (exception is Warning warning) + return warning.Code; + return null; + } } } diff --git a/framework/src/Bing/Bing/Reflection/AppDomainAllAssemblyFinder.cs b/framework/src/Bing/Bing/Reflection/AppDomainAllAssemblyFinder.cs index 11fc8a1e8..1885b7ec1 100644 --- a/framework/src/Bing/Bing/Reflection/AppDomainAllAssemblyFinder.cs +++ b/framework/src/Bing/Bing/Reflection/AppDomainAllAssemblyFinder.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -7,7 +8,6 @@ using Bing.Extensions; using Bing.Finders; using Microsoft.Extensions.DependencyModel; -using Microsoft.Extensions.PlatformAbstractions; namespace Bing.Reflection { @@ -72,7 +72,7 @@ select name.Substring(i, name.Length - i)) } else { - var path = PlatformServices.Default.Application.ApplicationBasePath; + var path = AppContext.BaseDirectory; var dllNames = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly) .Concat(Directory.GetFiles(path, "*.exe", SearchOption.TopDirectoryOnly)) .Select(m => m.Replace(".dll", "").Replace(".exe", "")) @@ -120,9 +120,10 @@ protected static Assembly[] LoadAssemblies(IEnumerable files) /// 程序集名称 protected virtual bool Match(string assemblyName) { - if (assemblyName.StartsWith($"{PlatformServices.Default.Application.ApplicationName}.Views")) + var applicationName = Assembly.GetEntryAssembly().GetName().Name; + if (assemblyName.StartsWith($"{applicationName}.Views")) return false; - if (assemblyName.StartsWith($"{PlatformServices.Default.Application.ApplicationName}.PrecompiledViews")) + if (assemblyName.StartsWith($"{applicationName}.PrecompiledViews")) return false; return Regex.IsMatch(assemblyName, SkipAssemblies, RegexOptions.IgnoreCase | RegexOptions.Compiled) == false; } diff --git a/framework/src/Bing/Bing/Validations/Abstractions/IValidatable.cs b/framework/src/Bing/Bing/Validations/Abstractions/IValidatable.cs deleted file mode 100644 index 2ad69006d..000000000 --- a/framework/src/Bing/Bing/Validations/Abstractions/IValidatable.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Generic; - -namespace Bing.Validations.Abstractions -{ - /// - /// 验证 - /// - public interface IValidatable - { - /// - /// 验证 - /// - ValidationResultCollection Validate(); - } - - /// - /// 验证 - /// - /// 对象类型 - public interface IValidatable : IValidatable where TObject : class, IValidatable - { - /// - /// 设置验证处理器 - /// - /// 验证处理器 - void SetValidateHandler(IValidationHandler handler); - - /// - /// 添加验证策略 - /// - /// 验证策略 - void AddStrategy(IValidateStrategy strategy); - - /// - /// 添加验证策略集合 - /// - /// 验证策略集合 - void AddStrategyList(IEnumerable> strategies); - } -} diff --git a/framework/src/Bing/Bing/Validations/Abstractions/IValidationHandler.cs b/framework/src/Bing/Bing/Validations/Abstractions/IValidationHandler.cs deleted file mode 100644 index f24991981..000000000 --- a/framework/src/Bing/Bing/Validations/Abstractions/IValidationHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Bing.Validations.Abstractions -{ - /// - /// 验证处理器 - /// - public interface IValidationHandler - { - /// - /// 处理验证错误 - /// - /// 验证结果集合 - void Handle(ValidationResultCollection results); - } -} diff --git a/framework/src/Bing/Bing/Validations/Abstractions/IValidator.cs b/framework/src/Bing/Bing/Validations/Abstractions/IValidator.cs deleted file mode 100644 index 36d348bf2..000000000 --- a/framework/src/Bing/Bing/Validations/Abstractions/IValidator.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace Bing.Validations.Abstractions -{ - /// - /// 验证器 - /// - public interface IValidator - { - } - - /// - /// 验证器 - /// - /// 对象类型 - public interface IValidator : IValidator where TObject : class, IValidatable - { - /// - /// 验证 - /// - /// 验证策略 - void Validate(IValidateStrategy strategy); - - /// - /// 验证 - /// - /// 验证策略集合 - void Validate(IEnumerable> strategies); - - /// - /// 获取验证结果集合 - /// - ValidationResultCollection GetValidationResult(); - } -} diff --git a/framework/src/Bing/Bing/Validations/ValidationResultCollection.cs b/framework/src/Bing/Bing/Validations/ValidationResultCollection.cs deleted file mode 100644 index 669c99fc2..000000000 --- a/framework/src/Bing/Bing/Validations/ValidationResultCollection.cs +++ /dev/null @@ -1,300 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using Bing.Validations.Abstractions; - -namespace Bing.Validations -{ - /// - /// 验证结果集合 - /// - public class ValidationResultCollection : IValidationResult - { - /// - /// 无名称 - /// - private const string NoName = "unamed"; - - /// - /// 验证结果集合 - /// - private readonly List _results; - - /// - /// 策略验证结果。策略 - 验证结果集合 - /// - private readonly IDictionary> _resultsFlaggedByStrategy; - - /// - /// 初始化一个类型的实例 - /// - public ValidationResultCollection() - { - _results = new List(); - _resultsFlaggedByStrategy = new Dictionary>(); - UpdateResultFlaggedByStrategy(NoName, new List()); - } - - /// - /// 初始化一个类型的实例 - /// - /// 结果 - public ValidationResultCollection(string result) - { - _results = new List(); - if (string.IsNullOrWhiteSpace(result)) - return; - _results.Add(new ValidationResult(result)); - } - - /// - /// 初始化一个类型的实例 - /// - /// 验证结果 - public ValidationResultCollection(ValidationResult result) : this() - { - if (result == null) - throw new ArgumentNullException(nameof(result)); - _results.Add(result); - UpdateResultFlaggedByStrategy(NoName, result); - } - - /// - /// 初始化一个类型的实例 - /// - /// 验证结果 - /// 策略名称 - public ValidationResultCollection(ValidationResult result, string strategyName) : this() - { - if (result == null) - throw new ArgumentNullException(nameof(result)); - _results.Add(result); - UpdateResultFlaggedByStrategy(strategyName, result); - } - - /// - /// 初始化一个类型的实例 - /// - /// 验证结果集合 - public ValidationResultCollection(IEnumerable results) : this() - { - if (results == null) - throw new ArgumentNullException(nameof(results)); - _results.AddRange(results); - UpdateResultFlaggedByStrategy(NoName, results.ToList()); - } - - /// - /// 初始化一个类型的实例 - /// - /// 验证结果集合 - /// 策略名称 - public ValidationResultCollection(IEnumerable results, string strategyName) : this() - { - if (results == null) - throw new ArgumentNullException(nameof(results)); - _results.AddRange(results); - UpdateResultFlaggedByStrategy(strategyName, results.ToList()); - } - - /// - /// 初始化一个类型的实例 - /// - /// 验证结果集合 - public ValidationResultCollection(ValidationResultCollection collection) : this() - { - if (collection == null) - throw new ArgumentNullException(nameof(collection)); - ErrorCode = collection.ErrorCode; - Flag = collection.Flag; - _results = collection._results; - UpdateResultFlaggedByStrategy(collection); - } - - /// - /// 验证结果计数 - /// - public int Count => _results.Count; - - /// - /// 是否已验证 - /// - public bool IsValid => _results.Count == 0; - - /// - /// 错误码 - /// - public long ErrorCode { get; set; } = 1001; - - /// - /// 标识 - /// - public string Flag { get; set; } = "__EMPTY_FLG"; - - /// - /// 成功验证结果集合 - /// - public static ValidationResultCollection Success { get; } = new ValidationResultCollection(); - - /// - /// 添加 - /// - /// 验证结果 - public void Add(ValidationResult result) - { - if (result == null) - return; - _results.Add(result); - } - - /// - /// 添加集合 - /// - /// 验证结果集合 - public void AddRange(IEnumerable results) - { - if (results == null) - return; - _results.AddRange(results); - } - - /// - /// 转换为消息 - /// - public string ToMessage() - { - var builder = new StringBuilder(); - if (IsValid) - builder.Append("未发现验证错误。"); - else if (Count == 1) - builder.Append("发现1个验证错误,请检查详细信息。"); - else - builder.Append($"发现{Count}个验证错误,请检查详细信息。"); - builder.AppendLine(); - builder.Append($" (code: {ErrorCode}, Flag: {Flag})"); - return builder.ToString(); - } - - /// - /// 转换为验证消息集合 - /// - public IEnumerable ToValidationMessages() - { - return IsValid ? Enumerable.Empty() : __getErrorStringList(); - - // ReSharper disable once InconsistentNaming - IEnumerable __getErrorStringList() - { - foreach (var error in _results) - yield return $"{error.MemberNames.FirstOrDefault()}, {error.ErrorMessage}"; - } - } - - /// - /// 获取错误字符串 - /// - /// 空格数量 - private StringBuilder GetErrorString(int spaceCount = 0) - { - var space = ' '.Repeat(spaceCount); - var sb = new StringBuilder(); - foreach (var error in _results) - sb.AppendLine($"{space}{error.MemberNames.FirstOrDefault()}, {error.ErrorMessage}"); - return sb; - } - - /// - /// 获取迭代器 - /// - public IEnumerator GetEnumerator() => _results.GetEnumerator(); - - /// - /// 获取迭代器 - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - /// 输出字符串 - /// - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendLine(ToMessage()); - sb.AppendLine("详情:"); - - sb.Append(GetErrorString(6)); - sb.AppendLine(); - - return sb.ToString(); - } - - /// - /// 更新指定策略的验证结果 - /// - /// 验证结果集合 - private void UpdateResultFlaggedByStrategy(ValidationResultCollection collection) - { - if (collection == null) - return; - foreach (var set in collection._resultsFlaggedByStrategy) - UpdateResultFlaggedByStrategy(set.Key, set.Value); - } - - /// - /// 更新指定策略的验证结果 - /// - /// 策略名称 - /// 验证结果集合 - private void UpdateResultFlaggedByStrategy(string name, List results) - { - if (results == null || string.IsNullOrWhiteSpace(name)) - return; - if (_resultsFlaggedByStrategy.ContainsKey(name)) - _resultsFlaggedByStrategy[name].AddRange(results); - else - _resultsFlaggedByStrategy.Add(name, results); - } - - /// - /// 更新指定策略的验证结果 - /// - /// 策略名称 - /// 验证结果 - private void UpdateResultFlaggedByStrategy(string name, ValidationResult result) - { - if (result == null || string.IsNullOrWhiteSpace(name)) - return; - if (_resultsFlaggedByStrategy.ContainsKey(name)) - _resultsFlaggedByStrategy[name].Add(result); - else - _resultsFlaggedByStrategy.Add(name, new List { result }); - } - - /// - /// 过滤 - /// - /// 过滤操作 - internal ValidationResultCollection Filter(Action> filter) - { - var ret = _results; - filter?.Invoke(ret); - return ret.Count == 0 ? null : new ValidationResultCollection(ret); - } - - /// - /// 过滤 - /// - /// 策略名称 - internal ValidationResultCollection Filter(string strategyName) - { - if (string.IsNullOrWhiteSpace(strategyName)) - strategyName = NoName; - return _resultsFlaggedByStrategy.TryGetValue(strategyName, out var ret) - ? new ValidationResultCollection(ret) - : null; - } - } -} diff --git a/framework/src/Bing/Events/Messages/IMessageEventBus.cs b/framework/src/Bing/Events/Messages/IMessageEventBus.cs index 8af1ef67c..56e21285e 100644 --- a/framework/src/Bing/Events/Messages/IMessageEventBus.cs +++ b/framework/src/Bing/Events/Messages/IMessageEventBus.cs @@ -1,12 +1,11 @@ -using System.Threading.Tasks; -using Bing.Aspects; +using System.Threading; +using System.Threading.Tasks; namespace Bing.Events.Messages { /// /// 消息事件总线 /// - [Ignore] public interface IMessageEventBus { /// @@ -14,7 +13,8 @@ public interface IMessageEventBus /// /// 事件类型 /// 事件 - Task PublishAsync(TEvent @event) where TEvent : IMessageEvent; + /// 取消令牌 + Task PublishAsync(TEvent @event, CancellationToken cancellationToken = default) where TEvent : IMessageEvent; /// /// 发布事件 @@ -23,6 +23,7 @@ public interface IMessageEventBus /// 事件数据 /// 回调名称 /// 是否立即发送消息 - Task PublishAsync(string name, object data, string callback, bool send = false); + /// 取消令牌 + Task PublishAsync(string name, object data, string callback, bool send = false, CancellationToken cancellationToken = default); } } diff --git a/framework/src/Bing/Logs/Abstractions/ILogContext.cs b/framework/src/Bing/Logs/Abstractions/ILogContext.cs index 2b11cdfe4..161f5bd01 100644 --- a/framework/src/Bing/Logs/Abstractions/ILogContext.cs +++ b/framework/src/Bing/Logs/Abstractions/ILogContext.cs @@ -1,12 +1,10 @@ using System.Diagnostics; -using Bing.Aspects; namespace Bing.Logs.Abstractions { /// /// 日志上下文 /// - [Ignore] public interface ILogContext { /// diff --git a/framework/src/Bing/Microsoft/Extensions/Logging/BingLoggerExtensions.cs b/framework/src/Bing/Microsoft/Extensions/Logging/BingLoggerExtensions.cs index bf5f4ba0e..8b8fe0802 100644 --- a/framework/src/Bing/Microsoft/Extensions/Logging/BingLoggerExtensions.cs +++ b/framework/src/Bing/Microsoft/Extensions/Logging/BingLoggerExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using Bing.ExceptionHandling; using Bing.Logging; @@ -118,8 +119,11 @@ private static void LogData(ILogger logger, Exception exception, LogLevel logLev { if (exception.Data == null || exception.Data.Count <= 0) return; - logger.LogWithLevel(logLevel, "---------- Exception Data ----------"); - foreach (var key in exception.Data.Keys) logger.LogWithLevel(logLevel, $"{key} = {exception.Data[key]}"); + var sb = new StringBuilder(); + sb.AppendLine("---------- Exception Data ----------"); + foreach (var key in exception.Data.Keys) + sb.AppendLine($"{key} = {exception.Data[key]}"); + logger.LogWithLevel(logLevel, sb.ToString()); } /// diff --git a/framework/src/Bing/Validations/Handlers/NothingHandler.cs b/framework/src/Bing/Validations/Handlers/NothingHandler.cs deleted file mode 100644 index f6f3d731a..000000000 --- a/framework/src/Bing/Validations/Handlers/NothingHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Bing.Validations.Abstractions; - -namespace Bing.Validations.Handlers -{ - /// - /// 验证失败,不做任何处理 - /// - public class NothingHandler : IValidationHandler - { - /// - /// 处理验证错误 - /// - /// 验证结果集合 - public void Handle(ValidationResultCollection results) - { - } - } -} diff --git a/framework/src/Bing/Validations/Handlers/ThrowHandler.cs b/framework/src/Bing/Validations/Handlers/ThrowHandler.cs deleted file mode 100644 index c3c47ac8f..000000000 --- a/framework/src/Bing/Validations/Handlers/ThrowHandler.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Linq; -using Bing.Exceptions; -using Bing.Validations.Abstractions; - -namespace Bing.Validations.Handlers -{ - /// - /// 验证失败,抛出异常 - 默认验证处理器 - /// - public class ThrowHandler : IValidationHandler - { - /// - /// 处理验证错误 - /// - /// 验证错误集合 - public void Handle(ValidationResultCollection results) - { - if (results.IsValid) - return; - throw new Warning(results.First().ErrorMessage); - } - } -} diff --git a/framework/src/Bing/Validations/IValidation.cs b/framework/src/Bing/Validations/IValidation.cs deleted file mode 100644 index 66b358b38..000000000 --- a/framework/src/Bing/Validations/IValidation.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Bing.Validations -{ - /// - /// 验证操作 - /// - public interface IValidation - { - /// - /// 验证 - /// - ValidationResultCollection Validate(); - } -} diff --git a/framework/src/Bing/Validations/IValidationRule.cs b/framework/src/Bing/Validations/IValidationRule.cs deleted file mode 100644 index b5e8cd1bb..000000000 --- a/framework/src/Bing/Validations/IValidationRule.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Bing.Validations -{ - /// - /// 验证规则 - /// - public interface IValidationRule - { - /// - /// 验证 - /// - ValidationResult Validate(); - } -} diff --git a/framework/src/Bing/Validations/ValidationResultCollection.cs b/framework/src/Bing/Validations/ValidationResultCollection.cs deleted file mode 100644 index 52273db9b..000000000 --- a/framework/src/Bing/Validations/ValidationResultCollection.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace Bing.Validations -{ - ///// - ///// 验证结果集合 - ///// - //public class ValidationResultCollection : IEnumerable - //{ - // #region 字段 - - // /// - // /// 验证结果 - // /// - // private readonly List _results; - - // #endregion - - // #region 属性 - - // /// - // /// 是否有效 - // /// - // public bool IsValid => _results.Count == 0; - - // /// - // /// 验证结果个数 - // /// - // public int Count => _results.Count; - - // /// - // /// 成功验证结果集合 - // /// - - // #endregion - - // #region 构造函数 - - // /// - // /// 初始化一个类型的实例 - // /// - // public ValidationResultCollection() : this("") - // { - // } - - // /// - // /// 初始化一个类型的实例 - // /// - // /// 验证结果 - // public ValidationResultCollection(string result) - // { - // _results = new List(); - // if (string.IsNullOrWhiteSpace(result)) - // return; - // _results.Add(new ValidationResult(result)); - // } - - // #endregion - - // /// - // /// 添加验证结果 - // /// - // /// 验证结果 - // public void Add(ValidationResult result) - // { - // if (result == null) - // return; - // _results.Add(result); - // } - - // /// - // /// 添加验证结果集合 - // /// - // /// 验证结果集合 - // public void AddList(IEnumerable results) - // { - // if (results == null) - // return; - // foreach (var result in results) - // Add(result); - // } - - // /// - // /// 获取迭代器 - // /// - // IEnumerator IEnumerable.GetEnumerator() => _results.GetEnumerator(); - - // /// - // /// 获取迭代器 - // /// - // IEnumerator IEnumerable.GetEnumerator() => _results.GetEnumerator(); - //} -} diff --git a/framework/src/Bing/project.dependency.props b/framework/src/Bing/dependency.props similarity index 61% rename from framework/src/Bing/project.dependency.props rename to framework/src/Bing/dependency.props index a120aab2c..dea54a740 100644 --- a/framework/src/Bing/project.dependency.props +++ b/framework/src/Bing/dependency.props @@ -1,18 +1,18 @@  - - - + + + + + - + + - - - \ No newline at end of file diff --git a/framework/src/Bing/project.props b/framework/src/Bing/project.props deleted file mode 100644 index f54fc3bd6..000000000 --- a/framework/src/Bing/project.props +++ /dev/null @@ -1,31 +0,0 @@ - - - Bing.Core - Bing.Core - Bing.Core是Bing应用框架的核心类库。 - - - - - True - True - LibraryResource.resx - - - True - True - R.resx - - - - - - PublicResXFileCodeGenerator - LibraryResource.Designer.cs - - - PublicResXFileCodeGenerator - R.Designer.cs - - - \ No newline at end of file diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing.AutoMapper.Tests.csproj b/framework/tests/Bing.AutoMapper.Tests/Bing.AutoMapper.Tests.csproj index 3b0fad382..1469e229d 100644 --- a/framework/tests/Bing.AutoMapper.Tests/Bing.AutoMapper.Tests.csproj +++ b/framework/tests/Bing.AutoMapper.Tests/Bing.AutoMapper.Tests.csproj @@ -1,7 +1,8 @@  - + - netcoreapp3.1 + + net5.0;netcoreapp3.1 @@ -9,14 +10,4 @@ - - - - - - - - - - diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/ObjectMapperExtensionsTest.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/ObjectMapperExtensionsTest.cs new file mode 100644 index 000000000..d2d5d7e11 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/ObjectMapperExtensionsTest.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using AutoMapper; +using Bing.Extensions; +using Bing.Helpers; +using Bing.ObjectMapping; +using Bing.Reflection; +using Bing.SampleClasses; +using Xunit; + +namespace Bing.AutoMapper +{ + /// + /// 对象映射器扩展测试 + /// + public class ObjectMapperExtensionsTest + { + /// + /// 测试初始化 + /// + public ObjectMapperExtensionsTest() + { + var allAssemblyFinder = new AppDomainAllAssemblyFinder(); + var mapperProfileTypeFinder = new MapperProfileTypeFinder(allAssemblyFinder); + var instances = mapperProfileTypeFinder + .FindAll() + .Select(type => Reflections.CreateInstance(type)) + .ToList(); + var configuration = new MapperConfiguration(cfg => + { + foreach (var instance in instances) + { + + Debug.WriteLine($"初始化AutoMapper配置:{instance.GetType().FullName}"); + instance.CreateMap(); + // ReSharper disable once SuspiciousTypeConversion.Global + cfg.AddProfile(instance as Profile); + } + }); + var mapper = new AutoMapperObjectMapper(configuration, instances); + ObjectMapperExtensions.SetMapper(mapper); + } + + /// + /// 测试 - 映射 - Sample -> Sample2 未在配置类中配置,将自动配置映射 + /// + [Fact] + public void Test_MapTo_1() + { + var sample = new Sample { StringValue = "a" }; + var sample2 = sample.MapTo(); + Assert.Equal("a", sample2.StringValue); + } + + /// + /// 测试 - 映射 - 映射相同属性名的不同对象 + /// + [Fact] + public void Test_MapTo_2() + { + var sample = new Sample { Test3 = new Sample3Copy { StringValue = "a" } }; + var sample2 = sample.MapTo(); + Assert.Equal("a", sample2.Test3.StringValue); + } + + /// + /// 测试 - 映射 - 映射相同属性名的不同对象集合 + /// + [Fact] + public void Test_MapTo_3() + { + var sample = new Sample { TestList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } } }; + var sample2 = sample.MapTo(); + Assert.Equal(2, sample2.TestList.Count); + Assert.Equal("a", sample2.TestList[0].StringValue); + Assert.Equal("b", sample2.TestList[1].StringValue); + } + + /// + /// 测试 - 映射 - Sample -> Sample4 已在配置类中配置映射关系 + /// + [Fact] + public void Test_MapTo_4() + { + var sample = new Sample { StringValue = "a" }; + var sample4 = sample.MapTo(); + Assert.Equal("a-1", sample4.StringValue); + } + + /// + /// 测试 - 映射- 已在配置类中配置映射关系 - 带参数重载 + /// + [Fact] + public void Test_MapTo_CustomProfile_1() + { + var sample = new Sample { StringValue = "a" }; + var sample4 = new Sample4(); + sample.MapTo(sample4); + Assert.Equal("a-1", sample4.StringValue); + } + + /// + /// 测试 - 映射 - 多次执行预配置映射 + /// + [Fact] + public void Test_MapTo_CustomProfile_2() + { + var sample = new Sample { StringValue = "a" }; + var sample4 = sample.MapTo(); + Assert.Equal("a-1", sample4.StringValue); + + sample.StringValue = "b"; + sample4 = sample.MapTo(); + Assert.Equal("b-1", sample4.StringValue); + + sample.StringValue = "c"; + sample4 = sample.MapTo(); + Assert.Equal("c-1", sample4.StringValue); + } + + /// + /// 测试 - 映射 - 预配置映射与自动配置映射混合执行 + /// + [Fact] + public void Test_MapTo_CustomProfile_3() + { + var sample = new Sample { StringValue = "a" }; + var sample4 = sample.MapTo(); + Assert.Equal("a-1", sample4.StringValue); + + sample.StringValue = "b"; + var sample2 = sample.MapTo(); + Assert.Equal("b", sample2.StringValue); + + sample.StringValue = "c"; + sample4 = sample.MapTo(); + Assert.Equal("c-1", sample4.StringValue); + } + + /// + /// 测试 - 映射集合 + /// + [Fact] + public void Test_MapTo_List_1() + { + var sampleList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } }; + var sample2List = new List(); + sampleList.MapTo(sample2List); + Assert.Equal(2, sample2List.Count); + Assert.Equal("a", sample2List[0].StringValue); + } + + /// + /// 测试 - 映射集合 + /// + [Fact] + public void Test_MapTo_List_2() + { + var sampleList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } }; + var sample2List = sampleList.MapTo>(); + Assert.Equal(2, sample2List.Count); + Assert.Equal("a", sample2List[0].StringValue); + } + + /// + /// 测试 - 映射 - 并发测试 + /// + [Fact] + public void Test_MapTo_MultipleThread() + { + Thread.ParallelExecute(() => + { + var sample = new Sample { StringValue = "a" }; + var sample2 = sample.MapTo(); + Assert.Equal("a", sample2.StringValue); + }, 20); + } + + /// + /// 测试 - 映射 - 忽略特性 + /// + [Fact] + public void Test_MapTo_Ignore() + { + var sample2 = new DtoSample { Name = "a", IgnoreValue = "b" }; + var sample = sample2.MapTo(); + Assert.Equal("a", sample.Name); + Assert.Null(sample.IgnoreValue); + + var sample3 = sample.MapTo(); + Assert.Equal("a", sample3.Name); + Assert.Null(sample3.IgnoreValue); + } + + /// + /// 测试 - 映射 - Castle代理类 + /// + [Fact] + public void Test_MapTo_CastleProxy() + { + var proxyGenerator = new Castle.DynamicProxy.ProxyGenerator(); + var proxy = proxyGenerator.CreateClassProxy(); + proxy.Name = "a"; + var sample = proxy.MapTo(); + Assert.Equal("a", sample.Name); + + var proxy2 = proxyGenerator.CreateClassProxy(); + proxy2.Name = "b"; + sample = proxy2.MapTo(); + Assert.Equal("b", sample.Name); + + var sample2 = new DtoSample { Name = "c" }; + var proxy3 = proxyGenerator.CreateClassProxy(); + sample2.MapTo(proxy3); + Assert.Equal("c", proxy3.Name); + } + + /// + /// 测试 - 映射集合 + /// + [Fact] + public void Test_MapToList_1() + { + var sampleList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } }; + var sample2List = sampleList.MapToList(); + Assert.Equal(2, sample2List.Count); + Assert.Equal("a", sample2List[0].StringValue); + } + + /// + /// 测试 - 映射集合 - 测试空集合 + /// + [Fact] + public void Test_MapToList_2() + { + var sampleList = new List(); + var sample2List = sampleList.MapToList(); + Assert.Empty(sample2List); + } + + /// + /// 测试 - 映射集合 - 测试数组 + /// + [Fact] + public void Test_MapToList_3() + { + Sample[] sampleList = { new() { StringValue = "a" }, new() { StringValue = "b" } }; + var sample2List = sampleList.MapToList(); + Assert.Equal(2, sample2List.Count); + Assert.Equal("a", sample2List[0].StringValue); + } + + /// + /// 测试 - 映射 - 失败映射抛出异常 + /// + [Fact] + public void Test_MapTo_Throw() + { + var parentId = Guid.NewGuid().SafeString(); + var dto = new TreeEntityDto { Name = "Test1", ParentId = parentId }; + var entity = dto.MapTo(); + Assert.Equal(parentId, entity.ParentId.SafeString()); + + //错误数据映射失败,抛出异常 + var parentId2 = "/"; + var dto2 = new TreeEntityDto { Name = "Test2", ParentId = parentId2 }; + Assert.Throws(() => dto2.MapTo()); + + //成功映射,失败映射不影响之前的映射关系 + var entity3 = dto.MapTo(); + Assert.Equal(parentId, entity3.ParentId.SafeString()); + } + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/ObjectMapperTest.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/ObjectMapperTest.cs new file mode 100644 index 000000000..909403e03 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/ObjectMapperTest.cs @@ -0,0 +1,100 @@ +using System.Diagnostics; +using System.Linq; +using AutoMapper; +using Bing.ObjectMapping; +using Bing.Reflection; +using Bing.SampleClasses; +using Xunit; + +namespace Bing.AutoMapper +{ + /// + /// 对象映射器测试 + /// + public class ObjectMapperTest + { + /// + /// 对象映射器 + /// + private readonly Bing.ObjectMapping.IObjectMapper _mapper; + + /// + /// 测试初始化 + /// + public ObjectMapperTest() + { + var allAssemblyFinder = new AppDomainAllAssemblyFinder(); + var mapperProfileTypeFinder = new MapperProfileTypeFinder(allAssemblyFinder); + var instances = mapperProfileTypeFinder + .FindAll() + .Select(type => Reflections.CreateInstance(type)) + .ToList(); + var configuration = new MapperConfiguration(cfg => + { + foreach (var instance in instances) + { + + Debug.WriteLine($"初始化AutoMapper配置:{instance.GetType().FullName}"); + instance.CreateMap(); + // ReSharper disable once SuspiciousTypeConversion.Global + cfg.AddProfile(instance as Profile); + } + }); + _mapper = new AutoMapperObjectMapper(configuration, instances); + } + + /// + /// 测试 - 映射 - Sample -> Sample2 已经配置类中配置映射关系 + /// + [Fact] + public void Test_Map_1() + { + var sample = new Sample { StringValue = "a" }; + var sample2 = _mapper.Map(sample); + Assert.Equal("a", sample2.StringValue); + } + + /// + /// 测试 - 映射 - Sample -> Sample2 已经配置类中配置映射关系 - 两参数重载 + /// + [Fact] + public void Test_Map_2() + { + var sample = new Sample { StringValue = "a" }; + var sample2 = new Sample2(); + _mapper.Map(sample, sample2); + Assert.Equal("a", sample2.StringValue); + } + + /// + /// 测试 - 映射 - Sample2 -> Sample 未在配置类中配置,将自动配置映射 + /// + [Fact] + public void Test_Map_3() + { + var sample2 = new Sample2 { StringValue = "a" }; + var sample = _mapper.Map(sample2); + Assert.Equal("a", sample.StringValue); + } + + /// + /// 测试 - 映射 - 用于重现动态配置后映射之前的配置出现问题 + /// 1. 执行Sample -> Sample2映射,已在配置类中配置映射关系 + /// 2. 执行Sample2 -> Sample映射,未在配置类中配置,将自动配置映射 + /// 3. 重复执行Sample -> Sample2映射 + /// + [Fact] + public void Test_Map_4() + { + var sample = new Sample { StringValue = "a" }; + var sample2 = _mapper.Map(sample); + Assert.Equal("a", sample2.StringValue); + + var sample3 = _mapper.Map(sample2); + Assert.Equal("a", sample3.StringValue); + + var sample4 = _mapper.Map(sample); + Assert.Equal("a", sample4.StringValue); + } + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/TestMapperConfiguration.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/TestMapperConfiguration.cs new file mode 100644 index 000000000..937395fe9 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/AutoMapper/TestMapperConfiguration.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using Bing.ObjectMapping; +using Bing.SampleClasses; + +namespace Bing.AutoMapper +{ + /// + /// 测试 - 映射器配置文件 + /// + public class TestMapperConfiguration : Profile, IObjectMapperProfile + { + /// + /// 创建映射配置 + /// + public void CreateMap() + { + CreateMap() + .ForMember(o => o.StringValue, o => o.MapFrom((s, d) => s.StringValue + "-1")); + //CreateMap() + // .ForMember(x => x.TargetSampleValue, x => x.MapFrom(p => p.SourceStringValue + "-001")); + } + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/EntitySample.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/EntitySample.cs new file mode 100644 index 000000000..15ddbae43 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/EntitySample.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using AutoMapper; +using Bing.Application.Dtos; +using Bing.Auditing; +using Bing.Domain.Entities; + +namespace Bing.SampleClasses +{ + /// + /// 实体样例 + /// + [Display(Name = "实体样例")] + public class EntitySample : AggregateRoot, IAuditedObjectWithName + { + /// + /// 初始化一个类型的实例 + /// + public EntitySample() : this(Guid.NewGuid()) { } + + /// + /// 初始化一个类型的实例 + /// + /// 标识 + public EntitySample(Guid id) : base(id) { } + + /// + /// 名称 + /// + [Required(ErrorMessage = "名称不能为空")] + public string Name { get; set; } + + /// + /// 忽略值 + /// + [IgnoreMap] + public string IgnoreValue { get; set; } + + /// + /// 创建时间 + /// + public DateTime? CreationTime { get; set; } + + /// + /// 创建人标识 + /// + public Guid? CreatorId { get; set; } + + /// + /// 创建人 + /// + public string Creator { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 最后修改人标识 + /// + public Guid? LastModifierId { get; set; } + + /// + /// 最后修改人 + /// + public string LastModifier { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges(EntitySample other) + { + AddChange(x => x.Id, other.Id); + AddChange(x => x.Name, other.Name); + AddChange(x => x.LastModificationTime, other.LastModificationTime); + } + } + + /// + /// 数据传输对象样例 + /// + public class DtoSample : DtoBase, IAuditedObjectWithName + { + /// + /// 名称 + /// + [Required(ErrorMessage = "名称不能为空")] + public string Name { get; set; } + + /// + /// 忽略值 + /// + [IgnoreMap] + public string IgnoreValue { get; set; } + + /// + /// 创建时间 + /// + public DateTime? CreationTime { get; set; } + + /// + /// 创建人标识 + /// + public Guid? CreatorId { get; set; } + + /// + /// 创建人 + /// + public string Creator { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 最后修改人标识 + /// + public Guid? LastModifierId { get; set; } + + /// + /// 最后修改人 + /// + public string LastModifier { get; set; } + + /// + /// 创建空集合 + /// + public static List EmptyList() => new(); + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/EnumSample.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/EnumSample.cs new file mode 100644 index 000000000..10970f636 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/EnumSample.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace Bing.SampleClasses +{ + /// + /// 枚举测试样例 + /// + public enum EnumSample + { + A = 1, + [Description("B2")] + B = 2, + [Description("C3")] + C = 3, + [Description("D4")] + D = 4, + [Description("E5")] + E = 5 + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample.cs new file mode 100644 index 000000000..355cc3ccb --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Bing.SampleClasses +{ + /// + /// 样例 + /// + public interface ISample + { + } + + /// + /// 测试样例 + /// + [Description("测试样例")] + public class Sample : ISample + { + /// + /// 初始化测试样例 + /// + public Sample() + { + StringList = new List(); + StringArray = StringList.ToArray(); + } + + /// + /// 测试值 + /// + [Display(Name = "测试值")] + [Required(ErrorMessage = "测试值不能为空")] + [StringLength(20, ErrorMessage = "测试值长度不能超过20")] + [EmailAddress] + [Url] + public string TestValue { get; set; } + /// + /// 电子邮件验证 + /// + [EmailAddress(ErrorMessage = "电子邮件不正确")] + public string Email { get; set; } + /// + /// 网址验证 + /// + [Url(ErrorMessage = "网址不正确")] + public string Url { get; set; } + /// + /// 最大长度 + /// + [MaxLength(2, ErrorMessage = "最大长度是2")] + public string MaxLengthValue { get; set; } + /// + /// 长度验证 + /// + [StringLength(3, MinimumLength = 2, ErrorMessage = "最大长度是3,最小长度是2")] + public string StringLengthValue { get; set; } + /// + /// DisplayName + /// + [DisplayName("DisplayNameValue值")] + public string DisplayNameValue { get; set; } + /// + /// Display + /// + [Display(Name = "DisplayValue值")] + public string DisplayValue { get; set; } + /// + /// string值 + /// + [Display(Name = "字符串值")] + [StringLength(20, ErrorMessage = "长度不能超过20")] + [Required(ErrorMessage = "不能为空")] + public string StringValue { get; set; } + /// + /// decimal值 + /// + public decimal DecimalValue { get; set; } + /// + /// 可空decimal值 + /// + public decimal? NullableDecimalValue { get; set; } + /// + /// float值 + /// + public float FloatValue { get; set; } + /// + /// 可空float值 + /// + public float? NullableFloatValue { get; set; } + /// + /// double值 + /// + public double DoubleValue { get; set; } + /// + /// 可空double值 + /// + public double? NullableDoubleValue { get; set; } + /// + /// bool值 + /// + public bool BoolValue { get; set; } + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + /// + /// Enum值 + /// + public Tests.Samples.EnumSample EnumValue { get; set; } + /// + /// 可空Enum值 + /// + public Tests.Samples.EnumSample? NullableEnumValue { get; set; } + /// + /// DateTime值 + /// + public DateTime DateValue { get; set; } + /// + /// 可空DateTime值 + /// + public DateTime? NullableDateValue { get; set; } + /// + /// int值 + /// + [Description("IntValue")] + public int IntValue { get; set; } + /// + /// 可空int值 + /// + [Description("TestNullableIntValue")] + public int? NullableIntValue { get; set; } + /// + /// short值 + /// + public short ShortValue { get; set; } + /// + /// 可空short值 + /// + public short? NullableShortValue { get; set; } + /// + /// long值 + /// + public long LongValue { get; set; } + /// + /// 可空long值 + /// + public long? NullableLongValue { get; set; } + /// + /// 字符串列表 + /// + public List StringList { get; set; } + /// + /// 字符串数据 + /// + public string[] StringArray { get; set; } + /// + /// Guid值 + /// + public Guid GuidValue { get; set; } + + /// + /// 测试2 + /// + public Sample2 Test2 { get; set; } + + /// + /// 测试3 + /// + public Sample3Copy Test3 { get; set; } + + /// + /// 导航属性 + /// + public List TestList { get; set; } + + /// + /// 静态属性 + /// + public static string StaticString => "TestStaticString"; + + /// + /// 静态对象 + /// + public static Sample2 StaticSample => new() { StringValue = "TestStaticSample" }; + + /// + /// 创建测试实例1 + /// + public static Sample Create1() => new() { StringValue = "A" }; + + /// + /// 创建测试实例2 + /// + public static Sample Create2() => new() { StringValue = "B" }; + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample2.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample2.cs new file mode 100644 index 000000000..2914fc094 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample2.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Bing.SampleClasses +{ + /// + /// 测试样例2 + /// + [DisplayName("测试样例2")] + public class Sample2 + { + /// + /// 描述 + /// + [Description("描述")] + public string Description { get; set; } + + /// + /// 显示名 + /// + [DisplayName("显示名")] + public string DisplayName { get; set; } + + /// + /// 显示描述 + /// + [Display(Description = "显示描述")] + public string Display { get; set; } + + /// + /// string值 + /// + public string StringValue { get; set; } + + /// + /// int值 + /// + public int IntValue { get; set; } + + /// + /// bool值 + /// + public bool BoolValue { get; set; } + + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + + /// + /// 导航属性 + /// + public Sample3 Test3 { get; set; } + + /// + /// 导航属性 + /// + public List TestList { get; set; } + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample3.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample3.cs new file mode 100644 index 000000000..e2d093345 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample3.cs @@ -0,0 +1,27 @@ +using System.ComponentModel; + +namespace Bing.SampleClasses +{ + /// + /// 测试样例3 + /// + [DisplayName("测试样例3")] + public class Sample3 + { + /// + /// string值 + /// + public string StringValue { get; set; } + } + + /// + /// 测试样例3 + /// + public class Sample3Copy + { + /// + /// string值 + /// + public string StringValue { get; set; } + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample4.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample4.cs new file mode 100644 index 000000000..b5a6e9927 --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/Sample4.cs @@ -0,0 +1,13 @@ +namespace Bing.SampleClasses +{ + /// + /// 测试样例4 + /// + public class Sample4 + { + /// + /// string值 + /// + public string StringValue { get; set; } + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/TreeEntitySample.cs b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/TreeEntitySample.cs new file mode 100644 index 000000000..0c9186e6c --- /dev/null +++ b/framework/tests/Bing.AutoMapper.Tests/Bing/SampleClasses/TreeEntitySample.cs @@ -0,0 +1,52 @@ +using System; +using Bing.Application.Dtos; +using Bing.Domain.Entities; + +namespace Bing.SampleClasses +{ + /// + /// 树型实体测试样例 + /// + public class TreeEntitySample : TreeEntityBase + { + /// + /// 初始化一个类型的实例 + /// + public TreeEntitySample() : this(Guid.Empty) + { + } + + /// + /// 初始化一个类型的实例 + /// + /// 标识 + public TreeEntitySample(Guid id) : base(id, "", 0) + { + } + + /// + /// 初始化一个类型的实例 + /// + /// 标识 + /// 路径 + public TreeEntitySample(Guid id, string path) : base(id, path, 0) + { + } + + /// + /// 名称 + /// + public string Name { get; set; } + } + + /// + /// 树形Dto测试样例 + /// + public class TreeEntityDto : TreeDtoBase + { + /// + /// 名称 + /// + public string Name { get; set; } + } +} diff --git a/framework/tests/Bing.AutoMapper.Tests/MapTest.cs b/framework/tests/Bing.AutoMapper.Tests/MapTest.cs index 23fda8913..c70a3f1eb 100644 --- a/framework/tests/Bing.AutoMapper.Tests/MapTest.cs +++ b/framework/tests/Bing.AutoMapper.Tests/MapTest.cs @@ -7,6 +7,9 @@ using Bing.Reflection; using Bing.Tests.Samples; using Xunit; +using Sample = Bing.SampleClasses.Sample; +using Sample2 = Bing.SampleClasses.Sample2; +using Sample3Copy = Bing.SampleClasses.Sample3Copy; namespace Bing.AutoMapper.Tests { @@ -45,7 +48,7 @@ public MapTest() public void Test_MapTo_1() { var sample = new Sample(); - var sample2 = new Sample2() { StringValue = "a" }; + var sample2 = new Sample2 { StringValue = "a" }; sample2.MapTo(sample); Assert.Equal("a", sample.StringValue); } diff --git a/framework/tests/Bing.AutoMapper.Tests/TestMapperConfiguration.cs b/framework/tests/Bing.AutoMapper.Tests/TestMapperConfiguration.cs deleted file mode 100644 index daed1431b..000000000 --- a/framework/tests/Bing.AutoMapper.Tests/TestMapperConfiguration.cs +++ /dev/null @@ -1,21 +0,0 @@ -using AutoMapper; -using Bing.ObjectMapping; -using Bing.Tests.Samples; - -namespace Bing.AutoMapper.Tests -{ - /// - /// 测试 - 映射器配置文件 - /// - public class TestMapperConfiguration : Profile, IObjectMapperProfile - { - /// - /// 创建映射配置 - /// - public void CreateMap() - { - CreateMap() - .ForMember(x => x.TargetSampleValue, x => x.MapFrom(p => p.SourceStringValue + "-001")); - } - } -} diff --git a/framework/tests/Bing.Datas.Test.Integration/Bing.Datas.Test.Integration.csproj b/framework/tests/Bing.Datas.Test.Integration/Bing.Datas.Test.Integration.csproj index 452e2c929..95928b036 100644 --- a/framework/tests/Bing.Datas.Test.Integration/Bing.Datas.Test.Integration.csproj +++ b/framework/tests/Bing.Datas.Test.Integration/Bing.Datas.Test.Integration.csproj @@ -1,21 +1,11 @@  - + - netcoreapp2.2 + net5.0;netcoreapp3.1; - - - - - - - - - - diff --git a/framework/tests/Bing.Datas.Test.Integration/Samples/Test1.cs b/framework/tests/Bing.Datas.Test.Integration/Samples/Test1.cs new file mode 100644 index 000000000..16afa65a6 --- /dev/null +++ b/framework/tests/Bing.Datas.Test.Integration/Samples/Test1.cs @@ -0,0 +1,9 @@ +namespace Bing.Data.Test.Integration.Samples +{ + public class Test1 + { + public string Name { get; set; } + + public int Age { get; set; } + } +} diff --git a/framework/tests/Bing.Datas.Test.Integration/Samples/Test2.cs b/framework/tests/Bing.Datas.Test.Integration/Samples/Test2.cs new file mode 100644 index 000000000..b74b0fdfe --- /dev/null +++ b/framework/tests/Bing.Datas.Test.Integration/Samples/Test2.cs @@ -0,0 +1,7 @@ +namespace Bing.Data.Test.Integration.Samples +{ + public class Test2 + { + public string Name { get; set; } + } +} diff --git a/framework/tests/Bing.Datas.Test.Integration/Sql/Builders/SqlServer/SqlServerBuilderTest.Where.cs b/framework/tests/Bing.Datas.Test.Integration/Sql/Builders/SqlServer/SqlServerBuilderTest.Where.cs index a6053bbf2..680387a56 100644 --- a/framework/tests/Bing.Datas.Test.Integration/Sql/Builders/SqlServer/SqlServerBuilderTest.Where.cs +++ b/framework/tests/Bing.Datas.Test.Integration/Sql/Builders/SqlServer/SqlServerBuilderTest.Where.cs @@ -816,6 +816,41 @@ public void TestWhereIf_7() Assert.Equal(1, _builder.GetParams()["@_p_1"]); } + /// + /// 添加Where子查询 - 委托 - 属性表达式 + /// + [Fact] + public void TestWhereIf_8() + { + //结果 + var result = new Str(); + result.AppendLine("Select * "); + result.AppendLine("From [Sample] As [s] "); + result.Append("Where [s].[Email]<>"); + result.AppendLine("(Select Count(*) "); + result.AppendLine("From [Test2] "); + result.Append("Where [Test2].[Name]=@_p_0) "); + result.Append("And [Age]=@_p_1"); + + //执行 + _builder.From("s") + .WhereIf(t => t.Email, builder => + { + builder.Count().From().Where(x => x.Name, "b"); + }, false) + .WhereIf(t => t.Email, builder => { + builder.Count().From().Where(x => x.Name, "a"); + }, true, Operator.NotEqual) + .Where("Age", 1); + Output.WriteLine(_builder.ToSql()); + + //验证 + Assert.Equal(result.ToString(), _builder.ToSql()); + Assert.Equal(2, _builder.GetParams().Count); + Assert.Equal("a", _builder.GetParams()["@_p_0"]); + Assert.Equal(1, _builder.GetParams()["@_p_1"]); + } + #endregion #region WhereIfNotEmpty diff --git a/framework/tests/Bing.Ddd.Domain.Tests/Bing.Ddd.Domain.Tests.csproj b/framework/tests/Bing.Ddd.Domain.Tests/Bing.Ddd.Domain.Tests.csproj index 3368b3787..46b823264 100644 --- a/framework/tests/Bing.Ddd.Domain.Tests/Bing.Ddd.Domain.Tests.csproj +++ b/framework/tests/Bing.Ddd.Domain.Tests/Bing.Ddd.Domain.Tests.csproj @@ -1,24 +1,9 @@ - + - netcoreapp3.1 - - false + net5.0;netcoreapp3.1; - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - diff --git a/framework/tests/Bing.EventBus.Tests/Bing.EventBus.Tests.csproj b/framework/tests/Bing.EventBus.Tests/Bing.EventBus.Tests.csproj index 1774a99c1..03765f9d2 100644 --- a/framework/tests/Bing.EventBus.Tests/Bing.EventBus.Tests.csproj +++ b/framework/tests/Bing.EventBus.Tests/Bing.EventBus.Tests.csproj @@ -1,23 +1,12 @@  - + net5.0;netcoreapp3.1; - false - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - diff --git a/framework/tests/Bing.Logging.Serilog.Tests/Bing.Logging.Serilog.Tests.csproj b/framework/tests/Bing.Logging.Serilog.Tests/Bing.Logging.Serilog.Tests.csproj index d6765a72b..94d5d7e35 100644 --- a/framework/tests/Bing.Logging.Serilog.Tests/Bing.Logging.Serilog.Tests.csproj +++ b/framework/tests/Bing.Logging.Serilog.Tests/Bing.Logging.Serilog.Tests.csproj @@ -1,27 +1,19 @@  + + + net5.0;netcoreapp3.1; + - netcoreapp3.1 Bing.Logging.Tests - false Bing.Logging.Tests.Startup - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - diff --git a/framework/tests/Bing.Logging.Tests/Bing.Logging.Tests.csproj b/framework/tests/Bing.Logging.Tests/Bing.Logging.Tests.csproj index ff0c10958..c0cd5b92c 100644 --- a/framework/tests/Bing.Logging.Tests/Bing.Logging.Tests.csproj +++ b/framework/tests/Bing.Logging.Tests/Bing.Logging.Tests.csproj @@ -1,26 +1,15 @@ - + - netcoreapp3.1 - - false + net5.0;netcoreapp3.1; - + - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + - + diff --git a/framework/tests/Bing.MailKit.Tests/Bing.MailKit.Tests.csproj b/framework/tests/Bing.MailKit.Tests/Bing.MailKit.Tests.csproj index a294aa28e..e97557af7 100644 --- a/framework/tests/Bing.MailKit.Tests/Bing.MailKit.Tests.csproj +++ b/framework/tests/Bing.MailKit.Tests/Bing.MailKit.Tests.csproj @@ -1,20 +1,11 @@  - + - netcoreapp2.2 + net5.0;netcoreapp3.1;netcoreapp2.2;netcoreapp2.1 - - - - - - - - - diff --git a/framework/tests/Bing.MailKit.Tests/MailKitEmailSenderTest.cs b/framework/tests/Bing.MailKit.Tests/MailKitEmailSenderTest.cs index b66dba99f..f203a3470 100644 --- a/framework/tests/Bing.MailKit.Tests/MailKitEmailSenderTest.cs +++ b/framework/tests/Bing.MailKit.Tests/MailKitEmailSenderTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Bing.Emailing; +using Bing.Emailing.Attachments; using Bing.MailKit.Configs; using MailKit.Security; using Xunit; @@ -56,86 +57,86 @@ public MailKitEmailSenderTest(ITestOutputHelper output) : base(output) /// /// 测试发送邮件 /// - [Fact] + [Fact(Skip = "需要提供配置")] public void Test_SendEmail() { - //var box = new EmailBox - //{ - // Subject = "MailKit 测试发送邮件", - // To = _to, - // Body = "

测试一下红色字体的邮件

", - // IsBodyHtml = true, - //}; - //this._mailKitEmailSender.Send(box); + var box = new EmailBox + { + Subject = "MailKit 测试发送邮件", + To = _to, + Body = "

测试一下红色字体的邮件

", + IsBodyHtml = true, + }; + this._mailKitEmailSender.Send(box); } /// /// 测试发送邮件以及附件 /// - [Fact] + [Fact(Skip = "需要提供配置")] public void Test_SendEmail_Attachment() { - //var box = new EmailBox() - //{ - // Subject = "MailKit 测试发送邮件以及附件", - // To = _to, - // Body = "

测试一下红色字体的邮件

", - // IsBodyHtml = true, - //}; - //box.Attachments.Add(new PhysicalFileAttachment("D:\\123.xlsx")); - //this._mailKitEmailSender.Send(box); + var box = new EmailBox() + { + Subject = "MailKit 测试发送邮件以及附件", + To = _to, + Body = "

测试一下红色字体的邮件

", + IsBodyHtml = true, + }; + box.Attachments.Add(new PhysicalFileAttachment("D:\\123.xlsx")); + this._mailKitEmailSender.Send(box); } /// /// 测试发送邮件以及附件_中文文件名 /// - [Fact] + [Fact(Skip = "需要提供配置")] public void Test_SendEmail_Attachment_ChineseFileName() { - //var box = new EmailBox() - //{ - // Subject = "MailKit 测试发送邮件以及附件_中文文件名", - // To = _to, - // Body = "

测试一下红色字体的邮件

", - // IsBodyHtml = true, - //}; - //box.Attachments.Add(new PhysicalFileAttachment("D:\\测试文件.xlsx")); - //this._mailKitEmailSender.Send(box); + var box = new EmailBox() + { + Subject = "MailKit 测试发送邮件以及附件_中文文件名", + To = _to, + Body = "

测试一下红色字体的邮件

", + IsBodyHtml = true, + }; + box.Attachments.Add(new PhysicalFileAttachment("D:\\测试文件.xlsx")); + this._mailKitEmailSender.Send(box); } /// /// 测试发送邮件以及附件_多个文件 /// - [Fact] + [Fact(Skip = "需要提供配置")] public void Test_SendEmail_Attachment_MultiFile() { - //var box = new EmailBox() - //{ - // Subject = "MailKit 测试发送邮件以及附件_多个文件", - // To = _to, - // Body = "

测试一下红色字体的邮件

", - // IsBodyHtml = true, - //}; - //box.Attachments.Add(new PhysicalFileAttachment("D:\\123.xlsx")); - //box.Attachments.Add(new PhysicalFileAttachment("D:\\测试文件.doc")); - //this._mailKitEmailSender.Send(box); + var box = new EmailBox() + { + Subject = "MailKit 测试发送邮件以及附件_多个文件", + To = _to, + Body = "

测试一下红色字体的邮件

", + IsBodyHtml = true, + }; + box.Attachments.Add(new PhysicalFileAttachment("D:\\123.xlsx")); + box.Attachments.Add(new PhysicalFileAttachment("D:\\测试文件.doc")); + this._mailKitEmailSender.Send(box); } /// /// 测试发送邮件以及附件_长文件名 /// - [Fact] + [Fact(Skip = "需要提供配置")] public void Test_SendEmail_Attachment_LongLengthFileName() { - //var box = new EmailBox() - //{ - // Subject = "MailKit 测试发送邮件以及附件_多个文件", - // To = _to, - // Body = "

测试一下红色字体的邮件

", - // IsBodyHtml = true, - //}; - //box.Attachments.Add(new PhysicalFileAttachment("D:\\测试文件123456789012345678901234567894444564645666666666666.doc")); - //this._mailKitEmailSender.Send(box); + var box = new EmailBox() + { + Subject = "MailKit 测试发送邮件以及附件_多个文件", + To = _to, + Body = "

测试一下红色字体的邮件

", + IsBodyHtml = true, + }; + box.Attachments.Add(new PhysicalFileAttachment("D:\\测试文件123456789012345678901234567894444564645666666666666.doc")); + this._mailKitEmailSender.Send(box); } } } diff --git a/framework/tests/Bing.Test.Shared/Bing.Test.Shared.csproj b/framework/tests/Bing.Test.Shared/Bing.Test.Shared.csproj index 6f77d27e0..14c4eb459 100644 --- a/framework/tests/Bing.Test.Shared/Bing.Test.Shared.csproj +++ b/framework/tests/Bing.Test.Shared/Bing.Test.Shared.csproj @@ -1,5 +1,5 @@ - + netcoreapp3.1 diff --git a/framework/tests/Bing.Tests/Applications/CrudServiceTest.Save.cs b/framework/tests/Bing.Tests/Applications/CrudServiceTest.Save.cs index 7d7a837a6..6d2e06a16 100644 --- a/framework/tests/Bing.Tests/Applications/CrudServiceTest.Save.cs +++ b/framework/tests/Bing.Tests/Applications/CrudServiceTest.Save.cs @@ -126,8 +126,9 @@ public async Task Test_SaveAsync_Add() [Fact] public async Task Test_SaveAsync_Update() { - _repository.FindAsync(_id).Returns(t => new EntitySample(_id) { Name = "a" }); - await _service.SaveAsync(new DtoSample { Id = _id.ToString(), Name = "b" }); + var now = DateTime.Now; + _repository.FindAsync(_id).Returns(t => new EntitySample(_id) { Name = "a", LastModificationTime = now }); + await _service.SaveAsync(new DtoSample { Id = _id.ToString(), Name = "b",LastModificationTime = now.AddDays(1)}); await _repository.DidNotReceive().AddAsync(Arg.Any()); await _repository.Received().UpdateAsync(Arg.Is(t => t.Name == "b")); } @@ -136,36 +137,25 @@ public async Task Test_SaveAsync_Update() /// 测试 - 删除 ///
[Fact] - public void Test_Delete() + public async Task Test_DeleteAsync() { var ids = new[] { _id, _id2 }; - _repository.FindByIds(ids.Join()).Returns(GetEntities()); - _service.Delete(ids.Join()); - _repository.Received().Remove(Arg.Is>(t => t.All(d => ids.Contains(d.Id)))); + _repository.FindByIdsAsync(ids.Join()).Returns(GetEntities()); + await _service.DeleteAsync(ids.Join()); + await _repository.Received().RemoveAsync(Arg.Is>(t => t.All(d => ids.Contains(d.Id)))); } /// /// 测试 - 删除 - id无效 /// [Fact] - public void Test_Delete_IdInvalid() + public async Task Test_DeleteAsync_IdInvalid() { var ids = new[] { Guid.NewGuid(), Guid.NewGuid() }; - _repository.FindByIds(ids.Join()).Returns(new List()); - _service.Delete(ids.Join()); - _repository.DidNotReceive().Remove(Arg.Any>()); - } - - /// - /// 测试 - 删除 - /// - [Fact] - public async Task Test_DeleteAsync() - { - var ids = new[] { _id, _id2 }; - _repository.FindByIdsAsync(ids.Join()).Returns(GetEntities()); + _repository.FindByIdsAsync(ids.Join()).Returns(new List()); await _service.DeleteAsync(ids.Join()); - await _repository.Received().RemoveAsync(Arg.Is>(t => t.All(d => ids.Contains(d.Id)))); + await _repository.DidNotReceive().RemoveAsync(Arg.Any>()); } + } } diff --git a/framework/tests/Bing.Tests/Applications/QueryServiceTest.cs b/framework/tests/Bing.Tests/Applications/QueryServiceTest.cs index 67a077cd0..c1489784a 100644 --- a/framework/tests/Bing.Tests/Applications/QueryServiceTest.cs +++ b/framework/tests/Bing.Tests/Applications/QueryServiceTest.cs @@ -90,19 +90,6 @@ private List GetEntities() return new List { _entity, _entity2 }; } - /// - /// 测试 - 获取全部 - /// - [Fact] - public void Test_GetAll() - { - _repository.FindAll().Returns(GetEntities()); - var dtos = _service.GetAll(); - Assert.Equal(2, dtos.Count); - Assert.Equal("A", dtos[0].Name); - Assert.Equal("B", dtos[1].Name); - } - /// /// 测试 - 获取全部 /// @@ -116,17 +103,6 @@ public async Task Test_GetAllAsync() Assert.Equal("B", dtos[1].Name); } - /// - /// 测试 - 通过编号获取 - /// - [Fact] - public void Test_GetById() - { - _repository.Find(_id).Returns(_entity); - var dto = _service.GetById(_id.ToString()); - Assert.Equal("A", dto.Name); - } - /// /// 测试 - 通过编号获取 /// @@ -138,20 +114,6 @@ public async Task Test_GetByIdAsync() Assert.Equal("A", dto.Name); } - /// - /// 测试 - 通过编号列表获取 - /// - [Fact] - public void Test_GetByIds() - { - var ids = $"{_id},{_id2}"; - _repository.FindByIds(ids).Returns(GetEntities()); - var dtos = _service.GetByIds(ids); - Assert.Equal(2, dtos.Count); - Assert.Equal("A", dtos[0].Name); - Assert.Equal("B", dtos[1].Name); - } - /// /// 测试 - 通过编号列表获取 /// diff --git a/framework/tests/Bing.Tests/Bing.Tests.csproj b/framework/tests/Bing.Tests/Bing.Tests.csproj index 17de0c0de..4733ab1eb 100644 --- a/framework/tests/Bing.Tests/Bing.Tests.csproj +++ b/framework/tests/Bing.Tests/Bing.Tests.csproj @@ -1,15 +1,9 @@  - + - netcoreapp2.2;netcoreapp3.1; + net5.0;netcoreapp3.1;netcoreapp2.2; - - - - - - @@ -31,4 +25,9 @@ + + + + + diff --git a/framework/tests/Bing.Tests/CloneTest.cs b/framework/tests/Bing.Tests/CloneTest.cs index 0d2bfaa9b..2f544e860 100644 --- a/framework/tests/Bing.Tests/CloneTest.cs +++ b/framework/tests/Bing.Tests/CloneTest.cs @@ -4,7 +4,7 @@ namespace Bing.Tests { - public class CloneTest:TestBase + public class CloneTest : TestBase { public CloneTest(ITestOutputHelper output) : base(output) { @@ -35,7 +35,7 @@ public void Test_Clone() clone.Child.One.Desc = "老王装逼套路"; clone.SetName("苦逼了吧老王"); - Assert.Equal("老铁",parent.Name); + Assert.Equal("老铁", parent.Name); Assert.Equal("老王", clone.Name); Assert.Equal("老铁Parent", parent.Child.Name); diff --git a/framework/tests/Bing.Tests/Datas/Queries/QueryTest.cs b/framework/tests/Bing.Tests/Datas/Queries/QueryTest.cs index e186f30d6..fe0cfb524 100644 --- a/framework/tests/Bing.Tests/Datas/Queries/QueryTest.cs +++ b/framework/tests/Bing.Tests/Datas/Queries/QueryTest.cs @@ -7,7 +7,6 @@ using Bing.Properties; using Bing.Tests.Samples; using Bing.Tests.XUnitHelpers; -using Bing.Extensions; using Xunit; namespace Bing.Tests.Datas.Queries diff --git a/framework/tests/Bing.Tests/Domains/GuidEntityTest.cs b/framework/tests/Bing.Tests/Domains/GuidEntityTest.cs index 8eaed24f8..3b7a31007 100644 --- a/framework/tests/Bing.Tests/Domains/GuidEntityTest.cs +++ b/framework/tests/Bing.Tests/Domains/GuidEntityTest.cs @@ -129,7 +129,7 @@ public void Test_AddValidateStrategy() AssertHelper.Throws(() => { _sample.Name = "abcd"; - _sample.AddStrategy(new ValidateStrategySample()); + _sample.UseStrategy(new ValidateStrategySample()); _sample.Validate(); }, "名称长度不能大于3"); } @@ -142,8 +142,8 @@ public void Test_SetValidationHandler() { _sample = AggregateRootSample.CreateSample(); _sample.Name = "abcd"; - _sample.AddStrategy(new ValidateStrategySample()); - _sample.SetValidateHandler(new ValidationHandlerSample()); + _sample.UseStrategy(new ValidateStrategySample()); + _sample.SetValidationCallback(new ValidationHandlerSample()); _sample.Validate(); } } diff --git a/framework/tests/Bing.Tests/Exceptions/ExceptionExtensionsTest.cs b/framework/tests/Bing.Tests/Exceptions/ExceptionExtensionsTest.cs new file mode 100644 index 000000000..3cbc1f607 --- /dev/null +++ b/framework/tests/Bing.Tests/Exceptions/ExceptionExtensionsTest.cs @@ -0,0 +1,101 @@ +using System; +using AspectCore.DynamicProxy; +using Bing.Exceptions; +using Bing.Exceptions.Prompts; +using Bing.Properties; +using Xunit; + +namespace Bing.Tests.Exceptions +{ + /// + /// 异常扩展测试 + /// + public class ExceptionExtensionsTest + { + public ExceptionExtensionsTest() + { + ExceptionPrompt.AddPrompt(new AspectExceptionPrompt()); + } + + /// + /// 测试 - 获取原始异常 + /// + [Fact] + public void Test_GetRawException() + { + var exception = new AspectInvocationException(null, new Exception("a")); + Assert.Equal("a", exception.GetRawException().Message); + } + + /// + /// 测试 - 获取异常提示 - 验证空 + /// + [Fact] + public void Test_GetPrompt_Null() + { + Exception exception = null; + Assert.Null(exception.GetPrompt()); + } + + /// + /// 测试 - 获取异常提示 - 生产环境隐藏系统异常消息 + /// + [Fact] + public void Test_GetPrompt_IsProduction_True() + { + var exception = new Exception("a"); + Assert.Equal(R.SystemError, exception.GetPrompt(true)); + } + + /// + /// 测试 - 获取异常提示 - 非生产环境显示系统异常消息 + /// + [Fact] + public void Test_GetPrompt_IsProduction_False() + { + var exception = new Exception("a"); + Assert.Equal("a", exception.GetPrompt()); + } + + /// + /// 测试 - 获取异常提示 - Warning异常生产环境显示正常 + /// + [Fact] + public void Test_GetPrompt_Warning_IsProduction_True() + { + var exception = new Warning("a"); + Assert.Equal("a", exception.GetPrompt(true)); + } + + /// + /// 测试 - 获取异常提示 - 非生产环境显示Warning异常消息 + /// + [Fact] + public void Test_GetPrompt_Warning_IsProduction_False() + { + var exception = new Warning("a"); + Assert.Equal("a", exception.GetPrompt()); + } + + /// + /// 测试 - 获取异常提示 - 显示Warning多层异常消息 + /// + [Fact] + public void Test_GetPrompt_Warning_2Level() + { + var rawException = new Exception("a"); + var exception = new Warning(rawException); + Assert.Equal("a",exception.GetPrompt()); + } + + /// + /// 测试 - 获取异常提示 - 显示原始异常消息 + /// + [Fact] + public void TestPrompt_AspectInvocationException() + { + var exception= new AspectInvocationException(null, new Exception("a")); + Assert.Equal("a", exception.GetPrompt()); + } + } +} diff --git a/framework/tests/Bing.Tests/Exceptions/WarningTest.cs b/framework/tests/Bing.Tests/Exceptions/WarningTest.cs index ec2ef6195..e5aeccd72 100644 --- a/framework/tests/Bing.Tests/Exceptions/WarningTest.cs +++ b/framework/tests/Bing.Tests/Exceptions/WarningTest.cs @@ -15,7 +15,7 @@ public class WarningTest [Fact] public void TestMessage() { - Warning warning = new Warning("A"); + var warning = new Warning("A"); Assert.Equal("A", warning.Message); } @@ -25,7 +25,7 @@ public void TestMessage() [Fact] public void TestMessage_Null() { - Warning warning = new Warning(null, "A"); + var warning = new Warning(null, "A"); Assert.Equal(string.Empty, warning.Message); } @@ -35,7 +35,7 @@ public void TestMessage_Null() [Fact] public void TestCode() { - Warning warning = new Warning("", "B"); + var warning = new Warning("", "B"); Assert.Equal("B", warning.Code); } @@ -45,7 +45,7 @@ public void TestCode() [Fact] public void TestException() { - Warning warning = new Warning(new Exception("A")); + var warning = new Warning(new Exception("A")); Assert.Empty(warning.Message); Assert.Equal("A", warning.GetMessage()); } @@ -56,7 +56,7 @@ public void TestException() [Fact] public void TestMessageAndException() { - Warning warning = new Warning("A", "", new Exception("C")); + var warning = new Warning("A", "", new Exception("C")); Assert.Equal("A", warning.Message); Assert.Equal($"A{Environment.NewLine}C", warning.GetMessage()); } @@ -67,7 +67,7 @@ public void TestMessageAndException() [Fact] public void TestException_2Layer() { - Warning warning = new Warning("A", "", new Exception("C", new NotImplementedException("D"))); + var warning = new Warning("A", "", new Exception("C", new NotImplementedException("D"))); Assert.Equal(3, warning.GetExceptions().Count); Assert.Equal(typeof(Warning), warning.GetExceptions()[0].GetType()); Assert.Equal(typeof(Exception), warning.GetExceptions()[1].GetType()); diff --git a/framework/tests/Bing.Tests/Reflections/AppDomainAllAssemblyFinderTest.cs b/framework/tests/Bing.Tests/Reflections/AppDomainAllAssemblyFinderTest.cs index 02c2867b0..50d896026 100644 --- a/framework/tests/Bing.Tests/Reflections/AppDomainAllAssemblyFinderTest.cs +++ b/framework/tests/Bing.Tests/Reflections/AppDomainAllAssemblyFinderTest.cs @@ -1,7 +1,8 @@ using Bing.Reflection; -using Bing.Extensions; +using Bing.Collections; using Xunit; using Xunit.Abstractions; +using System.Linq; namespace Bing.Tests.Reflections { @@ -30,7 +31,7 @@ public AppDomainAllAssemblyFinderTest(ITestOutputHelper output) : base(output) public void Test_Find() { var assemblies = _allAssemblyFinder.FindAll(); - assemblies.ForEach(x => + assemblies.ToList().ForEach(x => { Output.WriteLine(x.FullName); }); diff --git a/framework/tests/Bing.Tests/Validations/DataAnnotationValidationTest.cs b/framework/tests/Bing.Tests/Validations/DataAnnotationValidationTest.cs index 2d55571d9..ee3699a3c 100644 --- a/framework/tests/Bing.Tests/Validations/DataAnnotationValidationTest.cs +++ b/framework/tests/Bing.Tests/Validations/DataAnnotationValidationTest.cs @@ -1,6 +1,6 @@ using System.Linq; using Bing.Tests.Samples; -using Bing.Validations; +using Bing.Validation; using Xunit; namespace Bing.Tests.Validations diff --git a/framework/tests/Bing.Tests/Validations/ValidationResultCollectionTest.cs b/framework/tests/Bing.Tests/Validations/ValidationResultCollectionTest.cs index 3b4316777..06b3a47af 100644 --- a/framework/tests/Bing.Tests/Validations/ValidationResultCollectionTest.cs +++ b/framework/tests/Bing.Tests/Validations/ValidationResultCollectionTest.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Linq; -using Bing.Validations; +using Bing.Validation; using Xunit; namespace Bing.Tests.Validations diff --git a/framework/tests/Directory.Build.props b/framework/tests/Directory.Build.props deleted file mode 100644 index f055f7258..000000000 --- a/framework/tests/Directory.Build.props +++ /dev/null @@ -1,24 +0,0 @@ - - - - latest - false - - - - - - - - - - - - - - - - - diff --git a/modules/admin/src/Bing.Admin.FreeSQL/Apis/TestController.cs b/modules/admin/src/Bing.Admin.FreeSQL/Apis/TestController.cs index 919293c75..57fa34419 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/Apis/TestController.cs +++ b/modules/admin/src/Bing.Admin.FreeSQL/Apis/TestController.cs @@ -61,14 +61,17 @@ public async Task TestBatchInsertAsync([FromBody] long qty) return Success(); } + /// + /// 测试释放CAP资源 + /// [AllowAnonymous] [HttpPost("testDisposed")] - public async Task TestDisposed() + public Task TestDisposed() { //var temp = ServiceLocator.Instance.GetService(); //temp.Dispose(); ProcessingServer.Dispose(); - return Success(); + return Task.FromResult(Success()); } /// diff --git a/modules/admin/src/Bing.Admin.FreeSQL/Bing.Admin.FreeSQL.csproj b/modules/admin/src/Bing.Admin.FreeSQL/Bing.Admin.FreeSQL.csproj index 35fbfc6ae..67e3fec3e 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/Bing.Admin.FreeSQL.csproj +++ b/modules/admin/src/Bing.Admin.FreeSQL/Bing.Admin.FreeSQL.csproj @@ -8,17 +8,21 @@ - - - + + + + + + + diff --git a/modules/admin/src/Bing.Admin.FreeSQL/Modules/AppModule.cs b/modules/admin/src/Bing.Admin.FreeSQL/Modules/AppModule.cs index 61991ad37..bda46439c 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/Modules/AppModule.cs +++ b/modules/admin/src/Bing.Admin.FreeSQL/Modules/AppModule.cs @@ -2,6 +2,7 @@ using System.Text; using AspectCore.Configuration; using Bing.AspNetCore; +using Bing.AspNetCore.Mvc.ExceptionHandling; using Bing.AspNetCore.Mvc.Filters; using Bing.Core.Modularity; using Bing.DependencyInjection; @@ -12,6 +13,7 @@ using Bing.Tracing; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.Extensions.DependencyInjection; @@ -41,7 +43,9 @@ public override IServiceCollection AddServices(IServiceCollection services) // 注册Mvc services.AddControllers(o => { + o.Filters.Add(); o.Filters.Add(); + o.Filters.Add(); o.Conventions.Add(new AuthorizeControllerModelConvention()); }) .AddControllersAsServices()// 解决属性注入无法在控制器中注入的问题 @@ -49,6 +53,11 @@ public override IServiceCollection AddServices(IServiceCollection services) { options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; }); + // 配置模型校验 + services.Configure(o => + { + o.SuppressModelStateInvalidFilter = true; + }); services.EnableAop(o => { o.ThrowAspectException = false; diff --git a/modules/admin/src/Bing.Admin.FreeSQL/Modules/FreeSqlModule.cs b/modules/admin/src/Bing.Admin.FreeSQL/Modules/FreeSqlModule.cs index 9547b046c..7f01ca184 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/Modules/FreeSqlModule.cs +++ b/modules/admin/src/Bing.Admin.FreeSQL/Modules/FreeSqlModule.cs @@ -1,11 +1,14 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using Bing.Admin.Data; +using Bing.Admin.Data.UnitOfWorks.MySql; using Bing.Core.Modularity; using Bing.Data.Enums; using Bing.Datas.Dapper; using Bing.FreeSQL; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Bing.Admin.Modules { @@ -35,7 +38,16 @@ public override IServiceCollection AddServices(IServiceCollection services) var configuration = services.GetConfiguration(); var connectionStr = configuration.GetConnectionString("DefaultConnection"); // 注册工作单元 - services.AddMySqlUnitOfWork(connectionStr); + services.AddMySqlUnitOfWork( + connectionStr, + (serviceProvider, builder) => + { + var logger = serviceProvider.GetRequiredService>(); + builder.UseMonitorCommand(executing: _ => { }, executed: (cmd, traceLog) => + { + logger.LogInformation($"库名:{cmd?.Connection?.Database}{Environment.NewLine}{traceLog}"); + }); + }); //services.AddMySqlUnitOfWork(connectionStr); // 注册SqlQuery services.AddSqlQuery(options => diff --git a/modules/admin/src/Bing.Admin.FreeSQL/Modules/LogModule.cs b/modules/admin/src/Bing.Admin.FreeSQL/Modules/LogModule.cs index f2f19e601..3f21db0ee 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/Modules/LogModule.cs +++ b/modules/admin/src/Bing.Admin.FreeSQL/Modules/LogModule.cs @@ -1,9 +1,15 @@ using System.ComponentModel; +using System.Threading; using Bing.AspNetCore; using Bing.Core.Modularity; -using Bing.Logs.Exceptionless; -using Bing.Logs.NLog; +using Bing.Logging; +using Bing.Logging.Serilog; +using Bing.Tracing; +using Exceptionless; using Microsoft.Extensions.DependencyInjection; +using Serilog; +using Serilog.Enrichers.Span; +using serilog = Serilog; namespace Bing.Admin.Modules { @@ -32,12 +38,71 @@ public class LogModule : AspNetCoreBingModule public override IServiceCollection AddServices(IServiceCollection services) { //services.AddNLog(); - services.AddExceptionless(x => + // 同时输出2种方式的日志,可能存在重复 需要陆续兼容 + Logs.Exceptionless.Extensions.AddExceptionless(services, o => { - x.ServerUrl = "http://10.186.135.27:5100"; - x.ApiKey = "yIgaHwtLwbN9VoUCP0UYpSPVzwpmeNSdVSfVIVta"; + o.ApiKey = "N8HOaOLndl0hF7ZhOfiwJ9HmOi6kPwjKEKWLCMzE"; + o.ServerUrl = "http://10.186.132.40:5100"; + }); + //ExceptionlessClient.Default.Configuration.ApiKey= "ez9jumyxVxjTxqSm0oUQhCML3OGCkDfMGyW1hfmn"; + //ExceptionlessClient.Default.Configuration.ServerUrl = "http://10.186.132.40:5100"; + //ExceptionlessClient.Default.Startup(); + services.AddSingleton(); + services.AddLogging(loggingBuilder => + { + var configuration = services.GetConfiguration(); + serilog.Log.Logger = new serilog.LoggerConfiguration() + .Enrich.FromLogContext() + .Enrich.WithLogContext() + .Enrich.WithLogLevel() + .Enrich.WithSpan() + .WriteTo.Exceptionless(additionalOperation: (builder) => + { + if (builder.Target.Data.TryGetValue("TraceId", out var traceId)) + builder.Target.AddTags(traceId.ToString() ?? string.Empty); + builder.Target.AddTags((TraceIdContext.Current ??= new TraceIdContext(string.Empty)).TraceId); + return builder; + }) + .ReadFrom.Configuration(configuration) + .ConfigLogLevel(configuration) + .CreateLogger(); + loggingBuilder.AddSerilog(); }); return services; } } + + /// + /// 日志上下文访问器 + /// + public class LogContextAccessor : ILogContextAccessor + { + /// + /// 当前日志上下文 + /// + private readonly AsyncLocal _currentLogContext; + + /// + /// 初始化一个类型的实例 + /// + public LogContextAccessor() + { + _currentLogContext = new AsyncLocal(); + } + + /// + /// 日志上下文 + /// + public LogContext Context + { + get + { + return _currentLogContext.Value ??= new LogContext(); + } + set + { + _currentLogContext.Value = value; + } + } + } } diff --git a/modules/admin/src/Bing.Admin.FreeSQL/Program.cs b/modules/admin/src/Bing.Admin.FreeSQL/Program.cs index 94974caa1..52445c929 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/Program.cs +++ b/modules/admin/src/Bing.Admin.FreeSQL/Program.cs @@ -4,13 +4,21 @@ namespace Bing.Admin { + /// + /// 程序入口点 + /// public class Program { - public static void Main(string[] args) - { - CreateWebHostBuilder(args).Build().Run(); - } + /// + /// 主函数 + /// + /// 参数 + public static void Main(string[] args) => CreateWebHostBuilder(args).Build().Run(); + /// + /// 创建主机 + /// + /// 参数 public static IHostBuilder CreateWebHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceContext() diff --git a/modules/admin/src/Bing.Admin.FreeSQL/Properties/launchSettings.json b/modules/admin/src/Bing.Admin.FreeSQL/Properties/launchSettings.json index 57e6ab78a..64ca9e27d 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/Properties/launchSettings.json +++ b/modules/admin/src/Bing.Admin.FreeSQL/Properties/launchSettings.json @@ -2,17 +2,17 @@ "$schema": "http://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, - "anonymousAuthentication": true, + "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:44342", - "sslPort": 0 + "applicationUrl": "http://localhost:5662", + "sslPort": 44342 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "api/values", + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -20,8 +20,8 @@ "Bing.Admin.FreeSQL": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "api/values", - "applicationUrl": "http://localhost:5000", + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/modules/admin/src/Bing.Admin.FreeSQL/appsettings.json b/modules/admin/src/Bing.Admin.FreeSQL/appsettings.json index 5982220fb..c7923a9b1 100644 --- a/modules/admin/src/Bing.Admin.FreeSQL/appsettings.json +++ b/modules/admin/src/Bing.Admin.FreeSQL/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "server=10.186.132.21;port=3306;database=bing_admin;uid=root;pwd=bing2019.00;charset='utf8mb4';Allow User Variables=true", + "DefaultConnection": "server=10.186.135.162;port=3306;database=bing_admin;uid=root;pwd=bing2019.00;charset='utf8mb4';Allow User Variables=true;", //"DefaultConnection": "Host=10.186.132.31;Port=15432;Database=bing_admin;Username=admin;Password=bing2019.00", "RedisConnection": "127.0.0.1:6379,defaultDatabase=1,prefix=bing.admin.sample:,poolsize=2" //"DefaultConnection": "server=192.168.0.67;port=3306;database=Sample;uid=root;pwd=bing2019.00;charset='utf8mb4';Allow User Variables=true" diff --git a/modules/admin/src/Bing.Admin.Infrastructure/Bing.Admin.Infrastructure.csproj b/modules/admin/src/Bing.Admin.Infrastructure/Bing.Admin.Infrastructure.csproj index 54b736319..7965e6f1c 100644 --- a/modules/admin/src/Bing.Admin.Infrastructure/Bing.Admin.Infrastructure.csproj +++ b/modules/admin/src/Bing.Admin.Infrastructure/Bing.Admin.Infrastructure.csproj @@ -14,7 +14,7 @@ - + diff --git a/modules/admin/src/Bing.Admin.Service.Shared/Requests/Systems/UpdatePasswordRequest.cs b/modules/admin/src/Bing.Admin.Service.Shared/Requests/Systems/UpdatePasswordRequest.cs index a7c487a93..584b371a6 100644 --- a/modules/admin/src/Bing.Admin.Service.Shared/Requests/Systems/UpdatePasswordRequest.cs +++ b/modules/admin/src/Bing.Admin.Service.Shared/Requests/Systems/UpdatePasswordRequest.cs @@ -2,6 +2,7 @@ using System.Linq; using Bing.Application.Dtos; using Bing.Exceptions; +using Bing.Validation; using Bing.Validations; namespace Bing.Admin.Service.Shared.Requests.Systems @@ -32,7 +33,7 @@ public class UpdatePasswordRequest : RequestBase /// /// 验证 /// - public override ValidationResultCollection Validate() + public override IValidationResult Validate() { var result = DataAnnotationValidation.Validate(this); if (NewPassword != ConfirmPassword) diff --git a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IApplicationService.cs b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IApplicationService.cs index f3a136db8..2cd84d648 100644 --- a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IApplicationService.cs +++ b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IApplicationService.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Bing.Admin.Service.Shared.Requests.Systems; using Bing.Aspects; -using Bing.Validations.Aspects; +using Bing.Validation; namespace Bing.Admin.Service.Abstractions.Systems { diff --git a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IModuleService.cs b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IModuleService.cs index 008a43241..94199d98c 100644 --- a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IModuleService.cs +++ b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IModuleService.cs @@ -3,7 +3,7 @@ using Bing.Admin.Service.Shared.Requests.Systems; using Bing.Application.Services; using Bing.Aspects; -using Bing.Validations.Aspects; +using Bing.Validation; namespace Bing.Admin.Service.Abstractions.Systems { diff --git a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IOperationService.cs b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IOperationService.cs index 059fb69ab..cdf9baf10 100644 --- a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IOperationService.cs +++ b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IOperationService.cs @@ -3,7 +3,7 @@ using Bing.Admin.Service.Shared.Requests.Systems; using Bing.Application.Services; using Bing.Aspects; -using Bing.Validations.Aspects; +using Bing.Validation; namespace Bing.Admin.Service.Abstractions.Systems { diff --git a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IRoleService.cs b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IRoleService.cs index 880fac2a5..d66684635 100644 --- a/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IRoleService.cs +++ b/modules/admin/src/Bing.Admin.Service/Abstractions/Systems/IRoleService.cs @@ -7,7 +7,7 @@ using Bing.Admin.Service.Shared.Responses.Systems; using Bing.Application.Services; using Bing.Aspects; -using Bing.Validations.Aspects; +using Bing.Validation; namespace Bing.Admin.Service.Abstractions.Systems { diff --git a/modules/admin/src/Bing.Admin.Service/Implements/TestService.cs b/modules/admin/src/Bing.Admin.Service/Implements/TestService.cs index 490c30d11..46ab6f91c 100644 --- a/modules/admin/src/Bing.Admin.Service/Implements/TestService.cs +++ b/modules/admin/src/Bing.Admin.Service/Implements/TestService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using Bing.Admin.Commons.Domain.Models; using Bing.Admin.Commons.Domain.Repositories; @@ -66,6 +67,7 @@ public async Task BatchInsertFileAsync(long qty) /// public Task TestArgumentNullAsync(List list) { + Debug.WriteLine($"调用方法: {nameof(TestArgumentNullAsync)}"); list.ForEach(Console.WriteLine); return Task.CompletedTask; } diff --git a/modules/admin/src/Bing.Admin/Apis/TestController.cs b/modules/admin/src/Bing.Admin/Apis/TestController.cs index 6a3d8b575..455e6ddd4 100644 --- a/modules/admin/src/Bing.Admin/Apis/TestController.cs +++ b/modules/admin/src/Bing.Admin/Apis/TestController.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Bing.Admin.Data; using Bing.Admin.Service.Abstractions; using Bing.Admin.Systems.Domain.Events; @@ -85,14 +86,17 @@ public async Task TestBatchInsertAsync([FromBody]long qty) return Success(); } + /// + /// 测试释放CAP资源 + /// [AllowAnonymous] [HttpPost("testDisposed")] - public async Task TestDisposed() + public Task TestDisposed() { //var temp = ServiceLocator.Instance.GetService(); //temp.Dispose(); ProcessingServer.Dispose(); - return Success(); + return Task.FromResult(Success()); } /// @@ -125,6 +129,7 @@ public async Task TestArgumentNullAsync() await TestService.TestArgumentNullAsync(null); return Success(); } + /// /// 测试日志 /// @@ -133,10 +138,62 @@ public async Task TestArgumentNullAsync() public Task TestLoggerAsync(string text) { Logger.LogTrace($"输出调试信息: {text}"); + Logger.LogTrace("输出调试信息[参数化]: {text}", text); + OtherLog + .Message("输出调试信息[参数化]: {text}", text) + .Tags(nameof(TestController), "OtherLog", "LogTrace") + .LogTrace(); + Logger.LogDebug($"输出调试信息: {text}"); + Logger.LogDebug("输出调试信息[参数化]: {text}", text); + OtherLog + .Message("输出调试信息[参数化]: {text}", text) + .Tags(nameof(TestController), "OtherLog", "LogDebug") + .LogDebug(); + Logger.LogInformation($"输出调试信息: {text}"); + Logger.LogInformation("输出调试信息[参数化]: {text}", text); + OtherLog + .Message("输出调试信息[参数化]: {text}", text) + .Tags(nameof(TestController), "OtherLog", "LogInformation") + .LogInformation(); + Logger.LogWarning($"输出调试信息: {text}"); + Logger.LogWarning("输出调试信息[参数化]: {text}", text); + OtherLog + .Message("输出调试信息[参数化]: {text}", text) + .Tags(nameof(TestController), "OtherLog", "LogWarning") + .LogWarning(); return Task.FromResult(Success()); } + + /// + /// 测试GUID + /// + /// + [AllowAnonymous] + [HttpGet("testGuid")] + public IActionResult TestGuid(Guid id) + { + return Success(id); + } + + /// + /// 测试 GUID 模型 + /// + /// + [AllowAnonymous] + [HttpGet("testGuidModel")] + public IActionResult TestGuidModel([FromQuery] TestNullableGuidRequest request) + { + return Success(request.TestId); + } + + public class TestNullableGuidRequest + { + public Guid? TestId { get; set; } + + public Guid? TestId2 { get; set; } + } } } diff --git a/modules/admin/src/Bing.Admin/Bing.Admin.csproj b/modules/admin/src/Bing.Admin/Bing.Admin.csproj index bc558c006..bf29351f6 100644 --- a/modules/admin/src/Bing.Admin/Bing.Admin.csproj +++ b/modules/admin/src/Bing.Admin/Bing.Admin.csproj @@ -1,25 +1,17 @@  - netcoreapp3.1 InProcess - - - - bin\Debug\netcoreapp2.2\Bing.Admin.xml - - - - bin\Release\netcoreapp2.2\Bing.Admin.xml + Bing.Admin + true - - + + - diff --git a/modules/admin/src/Bing.Admin/Modules/AppModule.cs b/modules/admin/src/Bing.Admin/Modules/AppModule.cs index bfd2ef6eb..ab2e8e65f 100644 --- a/modules/admin/src/Bing.Admin/Modules/AppModule.cs +++ b/modules/admin/src/Bing.Admin/Modules/AppModule.cs @@ -1,8 +1,10 @@ using System.ComponentModel; +using System.Globalization; using System.Text; using AspectCore.Configuration; using Bing.AspNetCore; using Bing.AspNetCore.Extensions; +using Bing.AspNetCore.Logs; using Bing.AspNetCore.Mvc.ExceptionHandling; using Bing.AspNetCore.Mvc.Filters; using Bing.Core.Modularity; @@ -13,6 +15,7 @@ using Bing.Tracing; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Authorization; @@ -42,18 +45,38 @@ public override IServiceCollection AddServices(IServiceCollection services) BingClaimTypes.UserName = IdentityModel.JwtClaimTypes.Name; services.AddControllers(o => { + o.Filters.Add(); + o.Filters.Add(); + o.Filters.Add(); o.Filters.Add(); o.Filters.Add(); o.Conventions.Add(new AuthorizeControllerModelConvention()); + o.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor((x) => $"没有为属性 '{x}' 指定值"); + o.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => "必须指定值"); + o.ModelBindingMessageProvider.SetMissingRequestBodyRequiredValueAccessor(() => "请求正文不能为空"); + o.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor((x) => $"'{x}' 是无效的值"); + o.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) => $"'{x}' 不能作为 {y} 的值"); + o.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor((x) => $"'{x}' 是无效的值"); + o.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor((x) => $"为 {x} 指定的值无效"); + o.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(() => "指定的值无效"); + o.ModelBindingMessageProvider.SetValueIsInvalidAccessor((x) => $"值 {x} 无效"); + o.ModelBindingMessageProvider.SetValueMustBeANumberAccessor((x)=>$"字段 {x} 的值应该是数字"); + o.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(() => "字段的值应该是数字"); }) .AddControllersAsServices()// 解决属性注入无法在控制器中注入的问题 .AddNewtonsoftJson(options => { options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; }); + // 配置模型校验 + services.Configure(o => + { + o.SuppressModelStateInvalidFilter = true; + }); + services.EnableAop(o => { - o.ThrowAspectException = false; + o.ThrowAspectException = true; o.NonAspectPredicates.AddNamespace("Bing.Swashbuckle"); o.NonAspectPredicates.AddNamespace("DotNetCore.CAP"); }); @@ -65,6 +88,13 @@ public override IServiceCollection AddServices(IServiceCollection services) x.HttpHeaderName = "X-Correlation-Id"; x.SetResponseHeader = true; }); + services.AddRequestResponseLog(o => + { + o.IsEnabled = true; + o.Name = "Bing.Admin"; + //o.RequestFilter.Add("/swagger/**"); + //o.RequestFilter.Add("/swagger"); + }); return services; } @@ -75,9 +105,10 @@ public override IServiceCollection AddServices(IServiceCollection services) public override void UseModule(IApplicationBuilder app) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + app.UseRealIp(); app.UseCorrelationId(); + app.UseRequestResponseLog(); app.UseBingExceptionHandling(); - app.UseRealIp(x => x.HeaderKey = "test"); // 初始化Http上下文访问器 Web.HttpContextAccessor = app.ApplicationServices.GetService(); app.UseAuthentication(); diff --git a/modules/admin/src/Bing.Admin/Modules/LogModule.cs b/modules/admin/src/Bing.Admin/Modules/LogModule.cs index 7c1c56455..1e5d828a8 100644 --- a/modules/admin/src/Bing.Admin/Modules/LogModule.cs +++ b/modules/admin/src/Bing.Admin/Modules/LogModule.cs @@ -1,11 +1,15 @@ using System.ComponentModel; using System.Runtime.InteropServices; +using System.Threading; using Bing.AspNetCore; using Bing.Core.Modularity; +using Bing.Helpers; +using Bing.Logging; using Bing.Logging.Serilog; using Bing.Logs.NLog; using Bing.Tracing; using Exceptionless; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Enrichers.Span; @@ -41,12 +45,13 @@ public override IServiceCollection AddServices(IServiceCollection services) // 同时输出2种方式的日志,可能存在重复 需要陆续兼容 Logs.Exceptionless.Extensions.AddExceptionless(services, o => { - o.ApiKey = "ez9jumyxVxjTxqSm0oUQhCML3OGCkDfMGyW1hfmn"; + o.ApiKey = "N8HOaOLndl0hF7ZhOfiwJ9HmOi6kPwjKEKWLCMzE"; o.ServerUrl = "http://10.186.132.40:5100"; }); //ExceptionlessClient.Default.Configuration.ApiKey= "ez9jumyxVxjTxqSm0oUQhCML3OGCkDfMGyW1hfmn"; //ExceptionlessClient.Default.Configuration.ServerUrl = "http://10.186.132.40:5100"; //ExceptionlessClient.Default.Startup(); + services.AddSingleton(); services.AddLogging(loggingBuilder => { var configuration = services.GetConfiguration(); @@ -58,9 +63,7 @@ public override IServiceCollection AddServices(IServiceCollection services) .WriteTo.Exceptionless(additionalOperation: (builder) => { if (builder.Target.Data.TryGetValue("TraceId", out var traceId)) - { builder.Target.AddTags(traceId.ToString() ?? string.Empty); - } builder.Target.AddTags((TraceIdContext.Current ??= new TraceIdContext(string.Empty)).TraceId); return builder; }) @@ -72,4 +75,38 @@ public override IServiceCollection AddServices(IServiceCollection services) return services; } } + + /// + /// 日志上下文访问器 + /// + public class LogContextAccessor : ILogContextAccessor + { + /// + /// 当前日志上下文 + /// + private readonly AsyncLocal _currentLogContext; + + /// + /// 初始化一个类型的实例 + /// + public LogContextAccessor() + { + _currentLogContext = new AsyncLocal(); + } + + /// + /// 日志上下文 + /// + public LogContext Context + { + get + { + return _currentLogContext.Value ??= new LogContext(); + } + set + { + _currentLogContext.Value = value; + } + } + } } diff --git a/modules/admin/src/Bing.Admin/Modules/SwaggerModule.cs b/modules/admin/src/Bing.Admin/Modules/SwaggerModule.cs index 5e51ffda9..94dc24b58 100644 --- a/modules/admin/src/Bing.Admin/Modules/SwaggerModule.cs +++ b/modules/admin/src/Bing.Admin/Modules/SwaggerModule.cs @@ -99,6 +99,7 @@ public override void UseModule(IApplicationBuilder app) { UseSwaggerUI(config); config.UseDefaultSwaggerUI(); + config.EnableValidator(null); config.UseTokenStorage("oauth2"); }; }); @@ -111,6 +112,7 @@ public override void UseModule(IApplicationBuilder app) /// 配置 protected virtual void AddSwaggerEx(SwaggerExOptions options) { + options.ApiVersions.Add(new ApiVersion { Description = "默认", Version = "v1" }); options.ProjectName = "Bing.Admin 在线文档"; } @@ -148,7 +150,7 @@ protected virtual void UseSwagger(SwaggerOptions options) // ReSharper disable once InconsistentNaming protected virtual void UseSwaggerUI(SwaggerUIOptions options) { - options.SwaggerEndpoint("v1/swagger.json", "v1"); + //options.SwaggerEndpoint("v1/swagger.json", "v1"); } } } diff --git a/modules/admin/src/Bing.Admin/Program.cs b/modules/admin/src/Bing.Admin/Program.cs index 900e2e03b..d44e0dc8b 100644 --- a/modules/admin/src/Bing.Admin/Program.cs +++ b/modules/admin/src/Bing.Admin/Program.cs @@ -4,19 +4,28 @@ namespace Bing.Admin { + /// + /// 程序入口点 + /// public class Program { - public static void Main(string[] args) - { - CreaetHostBuilder(args).Build().Run(); - } + /// + /// 主函数 + /// + /// 参数 + public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); - public static IHostBuilder CreaetHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .UseServiceContext().ConfigureWebHostDefaults( + /// + /// 创建主机 + /// + /// 参数 + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseServiceContext() + .ConfigureWebHostDefaults( webBuilder => { webBuilder.UseStartup(); }); - } } diff --git a/modules/admin/src/Bing.Admin/Properties/launchSettings.json b/modules/admin/src/Bing.Admin/Properties/launchSettings.json index 5f1cef6ff..f3e1602ae 100644 --- a/modules/admin/src/Bing.Admin/Properties/launchSettings.json +++ b/modules/admin/src/Bing.Admin/Properties/launchSettings.json @@ -12,7 +12,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "api/values", + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -20,7 +20,7 @@ "Bing.Admin": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "api/values", + "launchUrl": "swagger", "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/modules/admin/src/Bing.Admin/Startup.cs b/modules/admin/src/Bing.Admin/Startup.cs index 278eed6a1..2b65e43f9 100644 --- a/modules/admin/src/Bing.Admin/Startup.cs +++ b/modules/admin/src/Bing.Admin/Startup.cs @@ -47,7 +47,7 @@ public void ConfigureServices(IServiceCollection services) /// /// 配置请求管道 /// - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { //loggerFactory.AddSysLogProvider(); app.UseBing(); diff --git a/modules/admin/src/Bing.Admin/appsettings.Development.json b/modules/admin/src/Bing.Admin/appsettings.Development.json index d76d68ea5..10cea1afe 100644 --- a/modules/admin/src/Bing.Admin/appsettings.Development.json +++ b/modules/admin/src/Bing.Admin/appsettings.Development.json @@ -3,7 +3,8 @@ "LogLevel": { "Default": "Debug", "System": "Warning", - "Microsoft": "Warning" + "Microsoft": "Warning", + "Microsoft.EntityFrameworkCore": "Debug" } } } diff --git a/modules/admin/src/Bing.Admin/appsettings.json b/modules/admin/src/Bing.Admin/appsettings.json index 97f39c497..9fbc5cce6 100644 --- a/modules/admin/src/Bing.Admin/appsettings.json +++ b/modules/admin/src/Bing.Admin/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "server=10.186.132.21;port=3306;database=bing_admin;uid=root;pwd=bing2019.00;charset='utf8mb4';Allow User Variables=true", + "DefaultConnection": "server=10.186.135.162;port=3306;database=bing_admin;uid=root;pwd=bing2019.00;charset='utf8mb4';Allow User Variables=true", //"DefaultConnection": "Host=10.186.132.31;Port=15432;Database=bing_admin;Username=admin;Password=bing2019.00", "RedisConnection": "127.0.0.1:6379,defaultDatabase=1,prefix=bing.admin.sample:,poolsize=2" //"DefaultConnection": "server=192.168.0.67;port=3306;database=Sample;uid=root;pwd=bing2019.00;charset='utf8mb4';Allow User Variables=true" diff --git a/tests/Bing.Tests.Samples/Bing.Tests.Samples.csproj b/tests/Bing.Tests.Samples/Bing.Tests.Samples.csproj index 0620211ae..f82aaa5e1 100644 --- a/tests/Bing.Tests.Samples/Bing.Tests.Samples.csproj +++ b/tests/Bing.Tests.Samples/Bing.Tests.Samples.csproj @@ -1,9 +1,9 @@ - - + + netstandard2.0 - Bing.Tests.Samples + @@ -28,9 +28,18 @@ - + + + + + + + + + + diff --git a/tests/Bing.Tests.Samples/Bing/Tests/Samples/QueryServiceSample.cs b/tests/Bing.Tests.Samples/Bing/Tests/Samples/QueryServiceSample.cs index 7dc79b8bc..744f9c5b1 100644 --- a/tests/Bing.Tests.Samples/Bing/Tests/Samples/QueryServiceSample.cs +++ b/tests/Bing.Tests.Samples/Bing/Tests/Samples/QueryServiceSample.cs @@ -1,8 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using AutoMapper; using Bing.Application.Dtos; using Bing.Application.Services; +using Bing.Auditing; using Bing.Data.Queries; namespace Bing.Tests.Samples @@ -10,7 +12,7 @@ namespace Bing.Tests.Samples /// /// 数据传输对象样例 /// - public class DtoSample : DtoBase + public class DtoSample : DtoBase, IAuditedObjectWithName { /// /// 名称 @@ -24,6 +26,36 @@ public class DtoSample : DtoBase [IgnoreMap] public string IgnoreValue { get; set; } + /// + /// 创建时间 + /// + public DateTime? CreationTime { get; set; } + + /// + /// 创建人标识 + /// + public Guid? CreatorId { get; set; } + + /// + /// 创建人 + /// + public string Creator { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 最后修改人标识 + /// + public Guid? LastModifierId { get; set; } + + /// + /// 最后修改人 + /// + public string LastModifier { get; set; } + /// /// 创建空集合 /// diff --git a/tests/Bing.Tests.Samples/Bing/Tests/Samples/RepositorySample.cs b/tests/Bing.Tests.Samples/Bing/Tests/Samples/RepositorySample.cs index 85be67337..5a9fc1373 100644 --- a/tests/Bing.Tests.Samples/Bing/Tests/Samples/RepositorySample.cs +++ b/tests/Bing.Tests.Samples/Bing/Tests/Samples/RepositorySample.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using AutoMapper; +using Bing.Auditing; using Bing.Data; using Bing.Data.Queries; using Bing.Domain.Entities; @@ -18,7 +19,7 @@ namespace Bing.Tests.Samples /// 实体样例 /// [Display(Name = "实体样例")] - public class EntitySample : AggregateRoot + public class EntitySample : AggregateRoot, IAuditedObjectWithName { /// /// 初始化一个类型的实例 @@ -43,6 +44,36 @@ public EntitySample(Guid id) : base(id) { } [IgnoreMap] public string IgnoreValue { get; set; } + /// + /// 创建时间 + /// + public DateTime? CreationTime { get; set; } + + /// + /// 创建人标识 + /// + public Guid? CreatorId { get; set; } + + /// + /// 创建人 + /// + public string Creator { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 最后修改人标识 + /// + public Guid? LastModifierId { get; set; } + + /// + /// 最后修改人 + /// + public string LastModifier { get; set; } + /// /// 添加变更列表 /// @@ -50,6 +81,7 @@ protected override void AddChanges(EntitySample other) { AddChange(x => x.Id, other.Id); AddChange(x => x.Name, other.Name); + AddChange(x => x.LastModificationTime, other.LastModificationTime); } } diff --git a/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidateStrategySample.cs b/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidateStrategySample.cs index fb8e43a94..913cef0e5 100644 --- a/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidateStrategySample.cs +++ b/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidateStrategySample.cs @@ -1,12 +1,12 @@ using System.ComponentModel.DataAnnotations; -using Bing.Validations.Abstractions; +using Bing.Validation.Strategies; namespace Bing.Tests.Samples { /// /// 验证策略样例 - 名称长度大于3将验证失败 /// - public class ValidateStrategySample : IValidateStrategy + public class ValidateStrategySample : IValidationStrategy { /// /// 策略名称 diff --git a/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidationHandlerSample.cs b/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidationHandlerSample.cs index f460df228..36e67b548 100644 --- a/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidationHandlerSample.cs +++ b/tests/Bing.Tests.Samples/Bing/Tests/Samples/ValidationHandlerSample.cs @@ -1,17 +1,16 @@ -using Bing.Validations; -using Bing.Validations.Abstractions; +using Bing.Validation; namespace Bing.Tests.Samples { /// /// 验证处理器样例 - 什么也不做 /// - public class ValidationHandlerSample : IValidationHandler + public class ValidationHandlerSample : IValidationCallbackHandler { /// /// 处理验证错误 /// /// 验证结果集合 - public void Handle(ValidationResultCollection results) { } + public void Handle(IValidationResult results) { } } } diff --git a/tests/Bing.Tests.Samples/Bing/Tests/TestResource.Designer.cs b/tests/Bing.Tests.Samples/Bing/Tests/TestResource.Designer.cs index 2eeeabdf3..2329e9039 100644 --- a/tests/Bing.Tests.Samples/Bing/Tests/TestResource.Designer.cs +++ b/tests/Bing.Tests.Samples/Bing/Tests/TestResource.Designer.cs @@ -19,7 +19,7 @@ namespace Bing.Tests { // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class TestResource { @@ -47,8 +47,8 @@ internal TestResource() { } /// - /// 重写当前线程的 CurrentUICulture 属性 - /// 重写当前线程的 CurrentUICulture 属性。 + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { diff --git a/version.dev.props b/version.dev.props index 58c6b70f9..59bc2d60a 100644 --- a/version.dev.props +++ b/version.dev.props @@ -2,6 +2,7 @@ 3.1.* - 1.3.0 + 1.4.0 + 3.2.610 \ No newline at end of file diff --git a/version.props b/version.props index 59fd75f83..ff2d90059 100644 --- a/version.props +++ b/version.props @@ -2,8 +2,8 @@ 2 2 - 3 - 20220104-1 + 4 + 20220907-1 $(VersionMajor).$(VersionMinor).$(VersionPatch)