From 40a40b6d36dc8ad8d4f5d2ba350dd6e22fbaa9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cwanyongkang=E2=80=9D?= <“937888580@qq.com”> Date: Mon, 28 Dec 2020 14:55:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BF=BD=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Hncore.Infrastructure/AliYun/AliDayu.cs | 140 +- .../Hncore.Infrastructure/AliYun/Util.cs | 162 +- .../Autofac/MvcAutoRegister.cs | 120 +- .../Hncore.Infrastructure/CSV/CsvRow.cs | 96 +- .../CSV/CsvTemporaryFile.cs | 166 +- .../Common/AssemblyUtil.cs | 548 +++--- .../Common/BinaryHelper.cs | 88 +- .../Common/CheckHelper.cs | 108 +- .../Common/ConcurrentHelper.cs | 148 +- .../Common/ContentTypeHelper.cs | 202 +-- .../Common/DateTimeHelper.cs | 158 +- .../Common/DebugClass.cs | 56 +- .../Common/DingTalkHelper.cs | 300 +-- .../Common/EnvironmentVariableHelper.cs | 26 +- .../Common/ExcelHelper.cs | 1286 ++++++------- .../Hncore.Infrastructure/Common/HttpHelp.cs | 138 +- .../Common/ImageCloudHelper.cs | 168 +- .../Hncore.Infrastructure/Common/IoHelper.cs | 78 +- .../Common/ListHelper.cs | 276 +-- .../Hncore.Infrastructure/Common/LogHelper.cs | 116 +- .../Common/MySqlHelper.cs | 56 +- .../Common/NetworkHelper.cs | 40 +- .../Common/QiNiuCloudHelper.cs | 236 +-- .../Hncore.Infrastructure/Common/RSAHelper.cs | 664 +++---- .../Common/RandomHelper.cs | 286 +-- .../Common/RedisLocklHelper.cs | 142 +- .../Common/RegexPattern.cs | 70 +- .../Common/SecurityHelper.cs | 774 ++++---- .../Common/ShellHelper.cs | 124 +- .../Hncore.Infrastructure/Common/UrlHelper.cs | 174 +- .../Common/ValidateCodeHelper.cs | 218 +-- .../DDD/AggregateRoot.cs | 16 +- .../Hncore.Infrastructure/DDD/Entity.cs | 94 +- .../DDD/IAggregateRoot.cs | 10 +- .../Hncore.Infrastructure/DDD/IEntity.cs | 32 +- .../Hncore.Infrastructure/DDD/IQuery.cs | 80 +- .../Hncore.Infrastructure/DDD/IRepository.cs | 84 +- .../Hncore.Infrastructure/DDD/ISoftDelete.cs | 12 +- .../Hncore.Infrastructure/DDD/ITenant.cs | 12 +- .../Hncore.Infrastructure/DDD/ITenantStore.cs | 12 +- .../Data/BusinessException.cs | 56 +- .../Data/HttpException.cs | 32 +- .../Hncore.Infrastructure/Data/PageData.cs | 80 +- .../Data/PageQueryable.cs | 50 +- .../Data/ResultMessage.cs | 96 +- .../Data/TransactionsHelper.cs | 46 +- .../Hncore.Infrastructure/EF/DbContextBase.cs | 236 +-- .../EF/DbContextExtension.cs | 456 ++--- .../EF/DbSetExtension.cs | 242 +-- .../Hncore.Infrastructure/EF/EntityMapBase.cs | 48 +- .../EF/Extensions/AutoMap.cs | 92 +- .../EF/Extensions/DebugLog.cs | 240 +-- .../EF/Extensions/QueryFilter.cs | 98 +- .../EF/Extensions/Sql.cs | 390 ++-- .../EF/IQueryDbContext.cs | 16 +- .../EF/IRepositoryDbContext.cs | 16 +- .../Hncore.Infrastructure/EF/QueryBase.cs | 178 +- .../EF/QueryExtension.cs | 218 +-- .../EF/RepositoryBase.cs | 178 +- .../Hncore.Infrastructure/EF/迁移命令.txt | 24 +- .../CommonEqualityComparer.cs | 96 +- .../EntitiesExtension/ConditionBuilder.cs | 288 +-- .../EntitiesExtension/ExpressionBuilder.cs | 106 +- .../EntitiesExtension/ExpressionVisitor.cs | 728 ++++---- .../EntitiesExtension/IQueryableExtend.cs | 212 +-- .../EntitiesExtension/ParameterRebinder.cs | 88 +- .../EntitiesExtension/PartialEvaluator.cs | 230 +-- .../EventBus/ActionEventHandler.cs | 48 +- .../EventBus/EventBus.cs | 300 +-- .../EventBus/EventData.cs | 78 +- .../EventBus/IEventBus.cs | 38 +- .../EventBus/IEventData.cs | 48 +- .../EventBus/IEventHandler.cs | 58 +- .../Extension/AssemblyExtension.cs | 40 +- .../Extension/BoolExtension.cs | 118 +- .../Extension/DateTimeExtension.cs | 352 ++-- .../Extension/DbDataReaderExtension.cs | 46 +- .../Extension/EnumExtension.cs | 296 +-- .../Extension/ExceptionExtension.cs | 48 +- .../Extension/HttpClientExtension.cs | 186 +- .../Extension/ListExtension.cs | 250 +-- .../Extension/ListForEachExtension.cs | 36 +- .../Extension/NumberExtension.cs | 124 +- .../Extension/ObjectExtension.cs | 286 +-- .../Extension/RequestExtension.cs | 90 +- .../Extension/StreamExtension.cs | 40 +- .../Extension/StringExtension.cs | 1556 ++++++++-------- .../Extension/TinyMapperExtension.cs | 32 +- .../Hncore.Infrastructure.csproj | 92 +- .../Hncore.Infrastructure/IOC/IDependency.cs | 12 +- .../Hncore.Infrastructure/IOC/IPerRequest.cs | 12 +- .../IOC/ISingleInstance.cs | 12 +- .../Hncore.Infrastructure/Mqtt/MQTTClient.cs | 300 +-- .../OpenApi/Application.cs | 50 +- .../OpenApi/OpenApiAuthAttribute.cs | 140 +- .../OpenApi/OpenApiException.cs | 54 +- .../OpenApi/OpenApiRequestBase.cs | 58 +- .../OpenApi/OpenApiResult.cs | 220 +-- .../OpenApi/OpenApiSignUtil.cs | 28 +- .../OperationLog/OperationLog.cs | 310 ++-- .../SMS/AliSmsService.cs | 92 +- .../SMS/SendSMSService.cs | 270 +-- .../Serializer/JsonNetSetting.cs | 146 +- .../Serializer/ObjectExtension.cs | 368 ++-- .../Hncore.Infrastructure/Serializer/XML.cs | 106 +- .../Service/IServiceCollectionExtension.cs | 26 +- .../Service/ServiceBase.cs | 410 ++--- .../Service/ServiceHttpClient.cs | 66 +- .../Service/ServiceIOCExt.cs | 52 +- .../Hncore.Infrastructure/Tree/DataNode.cs | 148 +- .../Hncore.Infrastructure/Tree/DataTree.cs | 120 +- .../CommonController/CheckController.cs | 30 +- .../CommonController/EtorControllerBase.cs | 246 +-- .../CommonController/PodHookController.cs | 68 +- .../WebApi/DTO/ApiResult.cs | 546 +++--- .../WebApi/DTO/EtorRequestBase.cs | 250 +-- .../WebApi/EtorJwtValidator.cs | 436 ++--- .../WebApi/Filter/Auth/AuthBase.cs | 98 +- .../WebApi/Filter/Auth/HttpContextExt.cs | 358 ++-- .../Filter/Auth/InternalApiAuthAttribute.cs | 138 +- .../WebApi/Filter/Auth/LimitQosAttribute.cs | 174 +- .../WebApi/Filter/Auth/ManageAuthAttribute.cs | 294 +-- .../WebApi/Filter/Auth/UserAuthAttribute.cs | 330 ++-- ...waggerAddEnumDescriptionsDocumentFilter.cs | 212 +-- .../WebApi/Filter/SwaggerOperationFilter.cs | 104 +- .../WebApi/Filter/ValidateModelAttribute.cs | 92 +- .../WebApi/GlobalData.cs | 12 +- .../Middleware/ErrorHandlingMiddleware.cs | 204 +-- .../ApplicationBuilderExtend.cs | 150 +- .../HostingEnvironmentExtend.cs | 82 +- .../ServiceCollectionExtend.cs | 224 +-- .../WebApi/WebRequest.cs | 140 +- .../Hncore.Infrastructure/nlog.config | 48 +- .../xUnit/PriorityOrderer.cs | 110 +- .../Alipay.AopSdk.Core/DefaultAopClient.cs | 1296 ++++++------- .../Util/AlipaySignature.cs | 1614 ++++++++--------- .../Alipay.AopSdk.Core/Util/RSAHelper.cs | 638 +++---- ....AopSdk.Core.csprojAssemblyReference.cache | Bin 81190 -> 81190 bytes .../BaseInfoClient/BaseInfoClient.csproj | 30 +- .../BaseInfoClient/BaseInfoHttpClient.cs | 46 +- .../IServiceCollectionExtension.cs | 26 +- .../Response/Householder/HouseholderItem.cs | 24 +- .../QueryAllHouseholdersByOwnerIdResponse.cs | 110 +- .../QueryHouseholderInfoByUserIdResponse.cs | 106 +- .../Response/Manage/ManageItem.cs | 260 +-- .../Manage/PermissionProjectByManagerItem.cs | 66 +- .../Manage/QueryAllManageByOwnerIdResponse.cs | 96 +- ...eryPermissionProjectByManagerIdResponse.cs | 88 +- .../Response/Project/ProjectItem.cs | 34 +- .../QueryAllProjectsByOwnerIdResponse.cs | 108 +- .../Project/QueryOneProjectByCodeResponse.cs | 104 +- .../Response/PropertyEmployee/EmployeeItem.cs | 32 +- .../QueryAllEmployeesByOwnerIdResponse.cs | 112 +- .../QueryEmployeeInfoByUserIdResponse.cs | 112 +- ...eryAllHouseholderRoomsByOwnerIdResponse.cs | 106 +- .../QueryHouseholderRoomsByUserIdResponse.cs | 112 +- .../Response/Room/UserRoomItem.cs | 48 +- .../QueryVisiterAndUserByWxOpenIdResponse.cs | 124 +- .../User/QueryVisterByVisterIdResponse.cs | 104 +- .../Response/User/VisterItem.cs | 192 +- .../Visitor/QueryVisitorByOpenidOrId.cs | 290 +-- .../Response/Visitor/VisitorItem.cs | 112 +- .../Response/`Build/BuildItem.cs | 24 +- .../`Build/QueryAllBuildsByOwnerIdResponse.cs | 108 +- .../MsgCenterClient/DataBodyAttribute.cs | 26 +- .../IServiceCollectionExtension.cs | 26 +- .../MsgCenterClient/MsgCenterClient.csproj | 30 +- .../MsgCenterClient/MsgCenterHttpClient.cs | 156 +- .../WechatMiniAppTplMsg/FangKeShenHeMsg.cs | 98 +- .../WechatMiniAppTplMsg/MsgBase.cs | 202 +-- .../WechatMpTplMsg/AssetDeductMsg.cs | 134 +- .../WechatMpTplMsg/CheLiangShenHeShiBaiMsg.cs | 52 +- .../WechatMpTplMsg/CheLiangShenHeTiJiaoMsg.cs | 52 +- .../CheLiangShenHeTongGuoMsg.cs | 76 +- .../WechatMpTplMsg/DataItem.cs | 54 +- .../WechatMpTplMsg/FangKeDengJiTongZhi.cs | 72 +- .../FangWuRenZhengChengGongMsg.cs | 64 +- .../WechatMpTplMsg/FangWuRenZhengShiBaiMsg.cs | 76 +- .../WechatMpTplMsg/GongDanWanJieMsg.cs | 64 +- .../WechatMpTplMsg/JiaoFeiChengGongMsg.cs | 64 +- .../WechatMpTplMsg/JieDanMsg.cs | 64 +- .../WechatMpTplMsg/LinShiDaoFangMsg.cs | 64 +- .../WechatMpTplMsg/MenJinYaoShiShouQuanMsg.cs | 64 +- .../MsgCenterClient/WechatMpTplMsg/MsgBase.cs | 232 +-- .../WechatMpTplMsg/PaiGongMsg.cs | 64 +- .../RenLianRenZhengChengGongMsg.cs | 64 +- .../RenLianRenZhengShiBaiMsg.cs | 76 +- .../WechatMpTplMsg/ShouDongCuiJiaoMsg.cs | 52 +- .../WechatMpTplMsg/YaoYueDaoFangMsg.cs | 64 +- .../WechatMpTplMsg/ZiDongCuiJiaoMsg.cs | 52 +- .../ClientExtension/DaiFu.cs | 50 +- .../ClientExtension/EPay.cs | 52 +- .../ClientExtension/OffLinePay.cs | 50 +- .../ClientExtension/QrPay.cs | 58 +- .../ClientExtension/QueryOrder.cs | 50 +- .../ClientExtension/SwipeCard.cs | 50 +- .../ClientExtension/WechatJsPay.cs | 58 +- .../PaymentCenterClient/Enum/OrderType.cs | 48 +- .../Enum/PaymentChannel.cs | 28 +- .../PaymentCenterClient/Enum/PaymentMethod.cs | 58 +- .../PaymentCenterClient/Enum/PaymentStatus.cs | 68 +- .../PaymentCenterClient/Enum/PaymentType.cs | 142 +- .../PaymentCenterClient/Enum/ResultCode.cs | 310 ++-- .../IServiceCollectionExtension.cs | 40 +- .../PaymentCenterClient.csproj | 26 +- .../PaymentCenterHttpClient.cs | 48 +- .../CreateOffLinePaySuccessedRecordRequest.cs | 114 +- .../Request/CreateOrderRequestBase.cs | 72 +- .../Request/EPayCreateOrderRequest.cs | 20 +- .../Request/MqttMessage.cs | 16 +- .../Request/PaymentRequestBase.cs | 22 +- .../Request/QrPayCreateOrderRequest.cs | 54 +- .../Request/SingleDaiFuRequest.cs | 64 +- .../Request/SwipeCardCreateOrderRequest.cs | 24 +- .../Request/WechatJsPayCreateOrderRequest.cs | 78 +- .../Response/QrPayCreateOrderResponse.cs | 28 +- .../Response/QueryOrderResponse.cs | 20 +- .../WechatJsPayCreateOrderResponse.cs | 18 +- ...CenterClient.csprojAssemblyReference.cache | Bin 295017 -> 360590 bytes .../IServiceCollectionExtension.cs | 26 +- .../ScheduledTaskClient/ScheduledMessage.cs | 68 +- .../ScheduledTaskClient.csproj | 22 +- .../ScheduledTaskHttpClient.cs | 98 +- .../ScheduledTaskClient/ScheduledType.cs | 34 +- Infrastructure/WxApi/ChannelType.cs | 40 +- Infrastructure/WxApi/Cryptography.cs | 464 ++--- Infrastructure/WxApi/Enums.cs | 618 +++---- Infrastructure/WxApi/MessageHandler.cs | 24 +- Infrastructure/WxApi/Model/WxMenuModel.cs | 24 +- Infrastructure/WxApi/Model/WxOpenModel.cs | 196 +- Infrastructure/WxApi/Notice/InfoTypeHelper.cs | 112 +- Infrastructure/WxApi/Notice/MessageBase.cs | 32 +- Infrastructure/WxApi/Notice/MessageDefault.cs | 76 +- Infrastructure/WxApi/Notice/MessageFactory.cs | 190 +- .../Notice/MpMessage/MessageEventScan.cs | 120 +- .../Notice/MpMessage/MessageEventSubscribe.cs | 76 +- .../MpMessage/MessageEventSubscribeQrscene.cs | 128 +- .../WxApi/Notice/MpMessage/MessageMPBase.cs | 70 +- .../Notice/OpenMessage/MessageAuthorized.cs | 90 +- .../MessageComponentVerifyTicket.cs | 64 +- .../Notice/OpenMessage/MessageOpenBase.cs | 56 +- .../OpenMessage/MessageThirdFasteRegister.cs | 140 +- .../Notice/OpenMessage/MessageUnauthorized.cs | 88 +- .../OpenMessage/MessageUpdateAuthorized.cs | 72 +- .../Request/GetAuthenticationUrlRequest.cs | 28 +- .../GetAuthorizerClientUserOpenIdRequest.cs | 18 +- .../WxApi/Request/GetAuthorizerInfoRequest.cs | 32 +- .../GetAuthorizerInvokeAccessTokenRequest.cs | 48 +- .../Request/GetComponentAccessTokenRequest.cs | 32 +- .../GetComponentVerifyTicketRequest.cs | 18 +- .../WxApi/Request/GetPreAuthCodeRequest.cs | 20 +- .../Request/GetSavedAuthorizerInfoRequest.cs | 14 +- .../WxApi/Request/GetWechatTicketRequest.cs | 16 +- .../WxApi/Request/GetWxJsConfigRequest.cs | 100 +- ...freshAuthorizerInvokeAccessTokenRequest.cs | 36 +- .../Request/SaveAuthorizerInfoRequest.cs | 16 +- .../WxApi/Request/SaveWechatAppMenuRequest.cs | 14 +- .../Request/UpdateWechatTicketRequest.cs | 18 +- .../Request/UploadWxImageMaterialRequest.cs | 76 +- .../WxApi/Request/UploadWxMediaRequest.cs | 18 +- .../WxApi/Request/WechatRequestBase.cs | 32 +- .../WxApi/Response/CreateQrcodeResponse.cs | 18 +- .../Response/GetAuthenticationUrlResponse.cs | 10 +- .../GetAuthorizerClientUserOpenIdResponse.cs | 12 +- .../Response/GetAuthorizerInfoResponse.cs | 16 +- .../GetAuthorizerInvokeAccessTokenResponse.cs | 62 +- .../GetComponentAccessTokenResponse.cs | 34 +- .../GetComponentVerifyTicketResponse.cs | 18 +- .../Response/GetGetWebAccessTokenResponse.cs | 18 +- .../WxApi/Response/GetJsApiTicketResponse.cs | 26 +- .../Response/GetMpAccessTokenResponse.cs | 22 +- .../WxApi/Response/GetPreAuthCodeResponse.cs | 32 +- .../GetSavedAuthorizerInfoResponse.cs | 10 +- .../WxApi/Response/GetTagsResponse.cs | 34 +- .../WxApi/Response/GetTemplateResponse.cs | 12 +- .../WxApi/Response/GetTempleteListResponse.cs | 40 +- .../WxApi/Response/GetUserinfoResponse.cs | 32 +- .../WxApi/Response/GetWechatTicketResponse.cs | 18 +- ...reshAuthorizerInvokeAccessTokenResponse.cs | 32 +- .../Response/SaveAuthorizerInfoResponse.cs | 12 +- .../Response/SaveWechatAppMenuResponse.cs | 12 +- .../Response/UpdateWechatTicketResponse.cs | 22 +- .../WxApi/Response/UploadMediaResponse.cs | 40 +- .../Response/WechatNotificationResponses.cs | 44 +- .../WxApi/Response/WxResponseBase.cs | 46 +- .../WxApi/TemplateMessage/SendResult.cs | 30 +- .../WxApi/TemplateMessage/TemplateApi.cs | 532 +++--- .../WxApi/TemplateMessage/TemplateModel.cs | 258 +-- Infrastructure/WxApi/Util/Constant.cs | 212 +-- Infrastructure/WxApi/Util/HostContext.cs | 36 +- Infrastructure/WxApi/Util/ServiceContext.cs | 64 +- Infrastructure/WxApi/Util/UrlHelper.cs | 94 +- Infrastructure/WxApi/WxApi.csproj | 30 +- Infrastructure/WxApi/WxApiExt.cs | 60 +- Infrastructure/WxApi/WxOpenApi.cs | 1518 ++++++++-------- Infrastructure/WxApi/WxOpenCrypt.cs | 444 ++--- .../WxApi.csprojAssemblyReference.cache | Bin 278636 -> 261457 bytes Infrastructure/log_storage/build.bat | 4 +- Infrastructure/log_storage/go.mod | 16 +- Infrastructure/log_storage/go.sum | 8 +- .../log_storage/logparse/logparse.go | 262 +-- .../log_storage/logstorage/logstorage.go | 58 +- Infrastructure/log_storage/main.go | 80 +- Infrastructure/log_storage/redis/redispull.go | 64 +- Infrastructure/log_storage/run.sh | 8 +- 305 files changed, 20629 insertions(+), 20629 deletions(-) diff --git a/Infrastructure/Hncore.Infrastructure/AliYun/AliDayu.cs b/Infrastructure/Hncore.Infrastructure/AliYun/AliDayu.cs index 3f196ba..a0d582b 100644 --- a/Infrastructure/Hncore.Infrastructure/AliYun/AliDayu.cs +++ b/Infrastructure/Hncore.Infrastructure/AliYun/AliDayu.cs @@ -1,71 +1,71 @@ -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Serialization; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Serializer; - -namespace Hncore.Infrastructure.AliYun -{ - public class AliDayu - { - const string SignName = ""; - private IHttpClientFactory _httpClientFactory; - - public AliDayu(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - public async Task SendSms(List PhoneNumbers, string templateCode, object content) - { - Dictionary paramDic = Util.BuildCommonParam(); - - paramDic.Add("Action", "SendSms"); - paramDic.Add("Version", "2017-05-25"); - paramDic.Add("RegionId", "cn-hangzhou"); - paramDic.Add("PhoneNumbers", ListHelper.ListToStr(PhoneNumbers)); - paramDic.Add("SignName", SignName); - paramDic.Add("TemplateCode", templateCode); - paramDic.Add("TemplateParam", content.ToJson()); - - string sign = Util.CreateSign(paramDic); - - paramDic.Add("Signature", sign); - - var httpClient = _httpClientFactory.CreateClient("AliDayu"); - - string url = "http://dysmsapi.aliyuncs.com"; - - foreach (var keyValuePair in paramDic) - { - url = UrlHelper.SetUrlParam(keyValuePair.Key, keyValuePair.Value); - } - - var res = await httpClient.GetStringAsync(url); - - SendSmsResponse result = XML.XmlDeserialize(res); - - if (result.Code == "OK") - { - return true; - } - - LogHelper.Error("阿里大于短信发送失败", res); - return false; - } - } - - [XmlRoot] - public class SendSmsResponse - { - [XmlElement] public string Message { get; set; } - - [XmlElement] public string RequestId { get; set; } - - [XmlElement] public string BizId { get; set; } - - [XmlElement] public string Code { get; set; } - } +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Serializer; + +namespace Hncore.Infrastructure.AliYun +{ + public class AliDayu + { + const string SignName = ""; + private IHttpClientFactory _httpClientFactory; + + public AliDayu(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public async Task SendSms(List PhoneNumbers, string templateCode, object content) + { + Dictionary paramDic = Util.BuildCommonParam(); + + paramDic.Add("Action", "SendSms"); + paramDic.Add("Version", "2017-05-25"); + paramDic.Add("RegionId", "cn-hangzhou"); + paramDic.Add("PhoneNumbers", ListHelper.ListToStr(PhoneNumbers)); + paramDic.Add("SignName", SignName); + paramDic.Add("TemplateCode", templateCode); + paramDic.Add("TemplateParam", content.ToJson()); + + string sign = Util.CreateSign(paramDic); + + paramDic.Add("Signature", sign); + + var httpClient = _httpClientFactory.CreateClient("AliDayu"); + + string url = "http://dysmsapi.aliyuncs.com"; + + foreach (var keyValuePair in paramDic) + { + url = UrlHelper.SetUrlParam(keyValuePair.Key, keyValuePair.Value); + } + + var res = await httpClient.GetStringAsync(url); + + SendSmsResponse result = XML.XmlDeserialize(res); + + if (result.Code == "OK") + { + return true; + } + + LogHelper.Error("阿里大于短信发送失败", res); + return false; + } + } + + [XmlRoot] + public class SendSmsResponse + { + [XmlElement] public string Message { get; set; } + + [XmlElement] public string RequestId { get; set; } + + [XmlElement] public string BizId { get; set; } + + [XmlElement] public string Code { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/AliYun/Util.cs b/Infrastructure/Hncore.Infrastructure/AliYun/Util.cs index 66abd56..e4ce937 100644 --- a/Infrastructure/Hncore.Infrastructure/AliYun/Util.cs +++ b/Infrastructure/Hncore.Infrastructure/AliYun/Util.cs @@ -1,82 +1,82 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Web; -using Microsoft.AspNetCore.Routing; - -namespace Hncore.Infrastructure.AliYun -{ - public class Util - { - const string QUERY_SEPARATOR = "&"; - const string HEADER_SEPARATOR = "\n"; - - const string AccessSecret = "r8FfRmoeWcCJyZSqqkQP2G3dKPPl2N "; - private const string AccessKeyId = "LTAI4FmSkDSwFuXeLxsDB3jB"; - - public static string CreateSign(Dictionary data, string method = "GET") - { - var dic = new RouteValueDictionary(data); - - - string[] array = dic.OrderBy(a => a.Key, StringComparer.Ordinal) - .Select(a => PercentEncode(a.Key) + "=" + PercentEncode(a.Value.ToString())).ToArray(); - string dataStr = string.Join("&", array); - string signStr = method + "&" + PercentEncode("/") + "&" + PercentEncode(dataStr); - - HMACSHA1 myhmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(AccessSecret + "&")); - byte[] byteArray = Encoding.UTF8.GetBytes(signStr); - MemoryStream stream = new MemoryStream(byteArray); - string signature = Convert.ToBase64String(myhmacsha1.ComputeHash(stream)); - - return signature; - } - - private static string PercentEncode(string value) - { - return UpperCaseUrlEncode(value) - .Replace("+", "%20") - .Replace("*", "%2A") - .Replace("%7E", "~"); - } - - private static string UpperCaseUrlEncode(string s) - { - char[] temp = HttpUtility.UrlEncode(s).ToCharArray(); - for (int i = 0; i < temp.Length - 2; i++) - { - if (temp[i] == '%') - { - temp[i + 1] = char.ToUpper(temp[i + 1]); - temp[i + 2] = char.ToUpper(temp[i + 2]); - } - } - - return new string(temp); - } - - public static IDictionary SortDictionary(Dictionary dic) - { - IDictionary sortedDictionary = - new SortedDictionary(dic, StringComparer.Ordinal); - return sortedDictionary; - } - - - public static Dictionary BuildCommonParam() - { - Dictionary dic = new Dictionary(); - - dic.Add("AccessKeyId", AccessKeyId); - dic.Add("Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")); - dic.Add("SignatureMethod", "HMAC-SHA1"); - dic.Add("SignatureVersion", "1.0"); - dic.Add("SignatureNonce", Guid.NewGuid().ToString()); - - return dic; - } - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Web; +using Microsoft.AspNetCore.Routing; + +namespace Hncore.Infrastructure.AliYun +{ + public class Util + { + const string QUERY_SEPARATOR = "&"; + const string HEADER_SEPARATOR = "\n"; + + const string AccessSecret = "r8FfRmoeWcCJyZSqqkQP2G3dKPPl2N "; + private const string AccessKeyId = "LTAI4FmSkDSwFuXeLxsDB3jB"; + + public static string CreateSign(Dictionary data, string method = "GET") + { + var dic = new RouteValueDictionary(data); + + + string[] array = dic.OrderBy(a => a.Key, StringComparer.Ordinal) + .Select(a => PercentEncode(a.Key) + "=" + PercentEncode(a.Value.ToString())).ToArray(); + string dataStr = string.Join("&", array); + string signStr = method + "&" + PercentEncode("/") + "&" + PercentEncode(dataStr); + + HMACSHA1 myhmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(AccessSecret + "&")); + byte[] byteArray = Encoding.UTF8.GetBytes(signStr); + MemoryStream stream = new MemoryStream(byteArray); + string signature = Convert.ToBase64String(myhmacsha1.ComputeHash(stream)); + + return signature; + } + + private static string PercentEncode(string value) + { + return UpperCaseUrlEncode(value) + .Replace("+", "%20") + .Replace("*", "%2A") + .Replace("%7E", "~"); + } + + private static string UpperCaseUrlEncode(string s) + { + char[] temp = HttpUtility.UrlEncode(s).ToCharArray(); + for (int i = 0; i < temp.Length - 2; i++) + { + if (temp[i] == '%') + { + temp[i + 1] = char.ToUpper(temp[i + 1]); + temp[i + 2] = char.ToUpper(temp[i + 2]); + } + } + + return new string(temp); + } + + public static IDictionary SortDictionary(Dictionary dic) + { + IDictionary sortedDictionary = + new SortedDictionary(dic, StringComparer.Ordinal); + return sortedDictionary; + } + + + public static Dictionary BuildCommonParam() + { + Dictionary dic = new Dictionary(); + + dic.Add("AccessKeyId", AccessKeyId); + dic.Add("Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")); + dic.Add("SignatureMethod", "HMAC-SHA1"); + dic.Add("SignatureVersion", "1.0"); + dic.Add("SignatureNonce", Guid.NewGuid().ToString()); + + return dic; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Autofac/MvcAutoRegister.cs b/Infrastructure/Hncore.Infrastructure/Autofac/MvcAutoRegister.cs index 58661a2..92a454c 100644 --- a/Infrastructure/Hncore.Infrastructure/Autofac/MvcAutoRegister.cs +++ b/Infrastructure/Hncore.Infrastructure/Autofac/MvcAutoRegister.cs @@ -1,61 +1,61 @@ -using System; -using System.Linq; -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Hncore.Infrastructure.IOC; -using Microsoft.Extensions.DependencyInjection; -using Hncore.Infrastructure.DDD; -using Hncore.Infrastructure.EF; - -namespace Hncore.Infrastructure.Autofac -{ - public class MvcAutoRegister - { - public IServiceProvider Build(IServiceCollection services, IMvcBuilder mvcBuilder, - Action action = null) - { - mvcBuilder.AddControllersAsServices(); - - var builder = new ContainerBuilder(); - - var assemblys = AppDomain.CurrentDomain.GetAssemblies().ToArray(); - - var perRequestType = typeof(IPerRequest); - builder.RegisterAssemblyTypes(assemblys) - .Where(t => perRequestType.IsAssignableFrom(t) && t != perRequestType) - .PropertiesAutowired() - .AsImplementedInterfaces() - .InstancePerLifetimeScope(); - - var perDependencyType = typeof(IDependency); - builder.RegisterAssemblyTypes(assemblys) - .Where(t => perDependencyType.IsAssignableFrom(t) && t != perDependencyType) - .PropertiesAutowired() - .AsImplementedInterfaces() - .InstancePerDependency(); - - var singleInstanceType = typeof(ISingleInstance); - builder.RegisterAssemblyTypes(assemblys) - .Where(t => singleInstanceType.IsAssignableFrom(t) && t != singleInstanceType) - .PropertiesAutowired() - .AsImplementedInterfaces() - .SingleInstance(); - - builder.RegisterGeneric(typeof(QueryBase<,>)).As(typeof(IQuery<,>)).PropertiesAutowired() - .InstancePerLifetimeScope(); - - builder.RegisterGeneric(typeof(RepositoryBase<,>)).As(typeof(IRepository<,>)).PropertiesAutowired() - .InstancePerLifetimeScope(); - - action?.Invoke(builder); - - builder.Populate(services); - - var container = builder.Build(); - - var servicesProvider = new AutofacServiceProvider(container); - - return servicesProvider; - } - } +using System; +using System.Linq; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Hncore.Infrastructure.IOC; +using Microsoft.Extensions.DependencyInjection; +using Hncore.Infrastructure.DDD; +using Hncore.Infrastructure.EF; + +namespace Hncore.Infrastructure.Autofac +{ + public class MvcAutoRegister + { + public IServiceProvider Build(IServiceCollection services, IMvcBuilder mvcBuilder, + Action action = null) + { + mvcBuilder.AddControllersAsServices(); + + var builder = new ContainerBuilder(); + + var assemblys = AppDomain.CurrentDomain.GetAssemblies().ToArray(); + + var perRequestType = typeof(IPerRequest); + builder.RegisterAssemblyTypes(assemblys) + .Where(t => perRequestType.IsAssignableFrom(t) && t != perRequestType) + .PropertiesAutowired() + .AsImplementedInterfaces() + .InstancePerLifetimeScope(); + + var perDependencyType = typeof(IDependency); + builder.RegisterAssemblyTypes(assemblys) + .Where(t => perDependencyType.IsAssignableFrom(t) && t != perDependencyType) + .PropertiesAutowired() + .AsImplementedInterfaces() + .InstancePerDependency(); + + var singleInstanceType = typeof(ISingleInstance); + builder.RegisterAssemblyTypes(assemblys) + .Where(t => singleInstanceType.IsAssignableFrom(t) && t != singleInstanceType) + .PropertiesAutowired() + .AsImplementedInterfaces() + .SingleInstance(); + + builder.RegisterGeneric(typeof(QueryBase<,>)).As(typeof(IQuery<,>)).PropertiesAutowired() + .InstancePerLifetimeScope(); + + builder.RegisterGeneric(typeof(RepositoryBase<,>)).As(typeof(IRepository<,>)).PropertiesAutowired() + .InstancePerLifetimeScope(); + + action?.Invoke(builder); + + builder.Populate(services); + + var container = builder.Build(); + + var servicesProvider = new AutofacServiceProvider(container); + + return servicesProvider; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/CSV/CsvRow.cs b/Infrastructure/Hncore.Infrastructure/CSV/CsvRow.cs index 15e6ad7..a8822a4 100644 --- a/Infrastructure/Hncore.Infrastructure/CSV/CsvRow.cs +++ b/Infrastructure/Hncore.Infrastructure/CSV/CsvRow.cs @@ -1,49 +1,49 @@ -using System.Text; - -namespace Hncore.Infrastructure.CSV -{ - public class CsvRow - { - private string _content = ""; - - public CsvRow AddCell(string content) - { - if (_content != "") - { - _content += ","; - } - - _content += StringToCsvCell(content); - - return this; - } - - public override string ToString() - { - return _content; - } - - private static string StringToCsvCell(string str) - { - bool mustQuote = str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"); - if (mustQuote) - { - StringBuilder sb = new StringBuilder(); - sb.Append("\""); - foreach (char nextChar in str) - { - sb.Append(nextChar); - if (nextChar == '"') - { - sb.Append("\""); - } - } - - sb.Append("\""); - return sb.ToString(); - } - - return str; - } - } +using System.Text; + +namespace Hncore.Infrastructure.CSV +{ + public class CsvRow + { + private string _content = ""; + + public CsvRow AddCell(string content) + { + if (_content != "") + { + _content += ","; + } + + _content += StringToCsvCell(content); + + return this; + } + + public override string ToString() + { + return _content; + } + + private static string StringToCsvCell(string str) + { + bool mustQuote = str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"); + if (mustQuote) + { + StringBuilder sb = new StringBuilder(); + sb.Append("\""); + foreach (char nextChar in str) + { + sb.Append(nextChar); + if (nextChar == '"') + { + sb.Append("\""); + } + } + + sb.Append("\""); + return sb.ToString(); + } + + return str; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/CSV/CsvTemporaryFile.cs b/Infrastructure/Hncore.Infrastructure/CSV/CsvTemporaryFile.cs index 15f47a9..262e8d4 100644 --- a/Infrastructure/Hncore.Infrastructure/CSV/CsvTemporaryFile.cs +++ b/Infrastructure/Hncore.Infrastructure/CSV/CsvTemporaryFile.cs @@ -1,84 +1,84 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Http; - -namespace Hncore.Infrastructure.CSV -{ - public class CsvTemporaryFile : IDisposable - { - private string filePath = ""; - private FileStream _fileStream; - private StreamWriter _streamWriter; - - public CsvTemporaryFile() - { - var execDir = Path.GetDirectoryName(typeof(CsvTemporaryFile).Assembly.Location); - - string tempDir = Path.Combine(execDir, "temp", DateTime.Now.ToString("yyyyMMdd")); - - if (!Directory.Exists(tempDir)) - { - Directory.CreateDirectory(tempDir); - } - - filePath = Path.Combine(tempDir, Guid.NewGuid().ToString()); - - _fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write); - _streamWriter = new StreamWriter(_fileStream, Encoding.GetEncoding("GB2312")); - _streamWriter.AutoFlush = true; - } - - public void WriteLine(CsvRow row) - { - _streamWriter.WriteLine(row.ToString()); - } - - public async Task ResponseAsync(HttpResponse httpResponse, string fileName) - { - httpResponse.ContentType = "application/octet-stream"; - httpResponse.Headers.Add("Content-Disposition", $"attachment; filename={fileName.UrlEncode()}"); - httpResponse.Headers.Add("X-Suggested-Filename", fileName.UrlEncode()); - - using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - using (BufferedStream bs = new BufferedStream(fs)) - { - byte[] buffer = new byte[4096]; - int bytesRead; - - long total_read = 0; - DateTime begin = DateTime.Now; - TimeSpan ts = new TimeSpan(); - - while ((bytesRead = bs.Read(buffer, 0, buffer.Length)) > 0) - { - await httpResponse.Body.WriteAsync(buffer, 0, bytesRead); - await httpResponse.Body.FlushAsync(); - - total_read += bytesRead; - ts = DateTime.Now - begin; - if (total_read / ts.TotalSeconds > 1024 * 1000) - { - await Task.Delay(1); - } - } - } - } - } - - public void Dispose() - { - _streamWriter?.Dispose(); - _fileStream?.Dispose(); - - - if (File.Exists(filePath)) - { - File.Delete(filePath); - } - } - } +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Http; + +namespace Hncore.Infrastructure.CSV +{ + public class CsvTemporaryFile : IDisposable + { + private string filePath = ""; + private FileStream _fileStream; + private StreamWriter _streamWriter; + + public CsvTemporaryFile() + { + var execDir = Path.GetDirectoryName(typeof(CsvTemporaryFile).Assembly.Location); + + string tempDir = Path.Combine(execDir, "temp", DateTime.Now.ToString("yyyyMMdd")); + + if (!Directory.Exists(tempDir)) + { + Directory.CreateDirectory(tempDir); + } + + filePath = Path.Combine(tempDir, Guid.NewGuid().ToString()); + + _fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write); + _streamWriter = new StreamWriter(_fileStream, Encoding.GetEncoding("GB2312")); + _streamWriter.AutoFlush = true; + } + + public void WriteLine(CsvRow row) + { + _streamWriter.WriteLine(row.ToString()); + } + + public async Task ResponseAsync(HttpResponse httpResponse, string fileName) + { + httpResponse.ContentType = "application/octet-stream"; + httpResponse.Headers.Add("Content-Disposition", $"attachment; filename={fileName.UrlEncode()}"); + httpResponse.Headers.Add("X-Suggested-Filename", fileName.UrlEncode()); + + using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (BufferedStream bs = new BufferedStream(fs)) + { + byte[] buffer = new byte[4096]; + int bytesRead; + + long total_read = 0; + DateTime begin = DateTime.Now; + TimeSpan ts = new TimeSpan(); + + while ((bytesRead = bs.Read(buffer, 0, buffer.Length)) > 0) + { + await httpResponse.Body.WriteAsync(buffer, 0, bytesRead); + await httpResponse.Body.FlushAsync(); + + total_read += bytesRead; + ts = DateTime.Now - begin; + if (total_read / ts.TotalSeconds > 1024 * 1000) + { + await Task.Delay(1); + } + } + } + } + } + + public void Dispose() + { + _streamWriter?.Dispose(); + _fileStream?.Dispose(); + + + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/AssemblyUtil.cs b/Infrastructure/Hncore.Infrastructure/Common/AssemblyUtil.cs index b776dd3..c561798 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/AssemblyUtil.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/AssemblyUtil.cs @@ -1,274 +1,274 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Reflection; -using System.IO; -using System.Linq; -using System.Globalization; - -#if !SILVERLIGHT -using System.Runtime.Serialization.Formatters.Binary; -#endif - -namespace Hncore.Infrastructure.Utils -{ - /// - /// Assembly Util Class - /// - public static class AssemblyUtil - { - /// - /// Creates the instance from type name. - /// - /// - /// The type. - /// - public static T CreateInstance(string type) - { - return CreateInstance(type, new object[0]); - } - - /// - /// Creates the instance from type name and parameters. - /// - /// - /// The type. - /// The parameters. - /// - public static T CreateInstance(string type, object[] parameters) - { - Type instanceType = null; - var result = default(T); - - instanceType = Type.GetType(type, true); - - if (instanceType == null) - throw new Exception(string.Format("The type '{0}' was not found!", type)); - - object instance = Activator.CreateInstance(instanceType, parameters); - result = (T)instance; - return result; - } - - /// - /// Gets the type by the full name, also return matched generic type without checking generic type parameters in the name. - /// - /// Full name of the type. - /// if set to true [throw on error]. - /// if set to true [ignore case]. - /// -#if !NET35 - public static Type GetType(string fullTypeName, bool throwOnError, bool ignoreCase) - { - var targetType = Type.GetType(fullTypeName, false, ignoreCase); - - if (targetType != null) - return targetType; - - var names = fullTypeName.Split(','); - var assemblyName = names[1].Trim(); - - try - { - var assembly = Assembly.Load(assemblyName); - - var typeNamePrefix = names[0].Trim() + "`"; - - var matchedTypes = assembly.GetExportedTypes().Where(t => t.IsGenericType - && t.FullName.StartsWith(typeNamePrefix, ignoreCase, CultureInfo.InvariantCulture)).ToArray(); - - if (matchedTypes.Length != 1) - return null; - - return matchedTypes[0]; - } - catch (Exception e) - { - if (throwOnError) - throw e; - - return null; - } - } -#else - public static Type GetType(string fullTypeName, bool throwOnError, bool ignoreCase) - { - return Type.GetType(fullTypeName, null, (a, n, ign) => - { - var targetType = a.GetType(n, false, ign); - - if (targetType != null) - return targetType; - - var typeNamePrefix = n + "`"; - - var matchedTypes = a.GetExportedTypes().Where(t => t.IsGenericType - && t.FullName.StartsWith(typeNamePrefix, ign, CultureInfo.InvariantCulture)).ToArray(); - - if (matchedTypes.Length != 1) - return null; - - return matchedTypes[0]; - }, throwOnError, ignoreCase); - } -#endif - - /// - /// Gets the implement types from assembly. - /// - /// The type of the base type. - /// The assembly. - /// - public static IEnumerable GetImplementTypes(this Assembly assembly) - { - return assembly.GetExportedTypes().Where(t => - t.IsSubclassOf(typeof(TBaseType)) && t.IsClass && !t.IsAbstract); - } - - /// - /// Gets the implemented objects by interface. - /// - /// The type of the base interface. - /// The assembly. - /// - public static IEnumerable GetImplementedObjectsByInterface(this Assembly assembly) - where TBaseInterface : class - { - return GetImplementedObjectsByInterface(assembly, typeof(TBaseInterface)); - } - - /// - /// Gets the implemented objects by interface. - /// - /// The type of the base interface. - /// The assembly. - /// Type of the target. - /// - public static IEnumerable GetImplementedObjectsByInterface(this Assembly assembly, Type targetType) - where TBaseInterface : class - { - Type[] arrType = assembly.GetExportedTypes(); - - var result = new List(); - - for (int i = 0; i < arrType.Length; i++) - { - var currentImplementType = arrType[i]; - - if (currentImplementType.IsAbstract) - continue; - - if (!targetType.IsAssignableFrom(currentImplementType)) - continue; - - result.Add((TBaseInterface)Activator.CreateInstance(currentImplementType)); - } - - return result; - } - -#if SILVERLIGHT -#else - /// - /// Clone object in binary format. - /// - /// - /// The target. - /// - public static T BinaryClone(this T target) - { - BinaryFormatter formatter = new BinaryFormatter(); - using (MemoryStream ms = new MemoryStream()) - { - formatter.Serialize(ms, target); - ms.Position = 0; - return (T)formatter.Deserialize(ms); - } - } -#endif - - - /// - /// Copies the properties of one object to another object. - /// - /// - /// The source. - /// The target. - /// - public static T CopyPropertiesTo(this T source, T target) - { - return source.CopyPropertiesTo(p => true, target); - } - - /// - /// Copies the properties of one object to another object. - /// - /// - /// The source. - /// The properties predict. - /// The target. - /// - public static T CopyPropertiesTo(this T source, Predicate predict, T target) - { - PropertyInfo[] properties = source.GetType() - .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); - - Dictionary sourcePropertiesDict = properties.ToDictionary(p => p.Name); - - PropertyInfo[] targetProperties = target.GetType() - .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty) - .Where(p => predict(p)).ToArray(); - - for (int i = 0; i < targetProperties.Length; i++) - { - var p = targetProperties[i]; - PropertyInfo sourceProperty; - - if (sourcePropertiesDict.TryGetValue(p.Name, out sourceProperty)) - { - if (sourceProperty.PropertyType != p.PropertyType) - continue; - - if (!sourceProperty.PropertyType.IsSerializable) - continue; - - p.SetValue(target, sourceProperty.GetValue(source, null), null); - } - } - - return target; - } - - /// - /// Gets the assemblies from string. - /// - /// The assembly def. - /// - public static IEnumerable GetAssembliesFromString(string assemblyDef) - { - return GetAssembliesFromStrings(assemblyDef.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)); - } - - /// - /// Gets the assemblies from strings. - /// - /// The assemblies. - /// - public static IEnumerable GetAssembliesFromStrings(string[] assemblies) - { - List result = new List(assemblies.Length); - - foreach (var a in assemblies) - { - result.Add(Assembly.Load(a)); - } - - return result; - } - - public static bool IsImplementedInterface() - { - return typeof(TInterface).IsAssignableFrom(typeof(T)); - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.IO; +using System.Linq; +using System.Globalization; + +#if !SILVERLIGHT +using System.Runtime.Serialization.Formatters.Binary; +#endif + +namespace Hncore.Infrastructure.Utils +{ + /// + /// Assembly Util Class + /// + public static class AssemblyUtil + { + /// + /// Creates the instance from type name. + /// + /// + /// The type. + /// + public static T CreateInstance(string type) + { + return CreateInstance(type, new object[0]); + } + + /// + /// Creates the instance from type name and parameters. + /// + /// + /// The type. + /// The parameters. + /// + public static T CreateInstance(string type, object[] parameters) + { + Type instanceType = null; + var result = default(T); + + instanceType = Type.GetType(type, true); + + if (instanceType == null) + throw new Exception(string.Format("The type '{0}' was not found!", type)); + + object instance = Activator.CreateInstance(instanceType, parameters); + result = (T)instance; + return result; + } + + /// + /// Gets the type by the full name, also return matched generic type without checking generic type parameters in the name. + /// + /// Full name of the type. + /// if set to true [throw on error]. + /// if set to true [ignore case]. + /// +#if !NET35 + public static Type GetType(string fullTypeName, bool throwOnError, bool ignoreCase) + { + var targetType = Type.GetType(fullTypeName, false, ignoreCase); + + if (targetType != null) + return targetType; + + var names = fullTypeName.Split(','); + var assemblyName = names[1].Trim(); + + try + { + var assembly = Assembly.Load(assemblyName); + + var typeNamePrefix = names[0].Trim() + "`"; + + var matchedTypes = assembly.GetExportedTypes().Where(t => t.IsGenericType + && t.FullName.StartsWith(typeNamePrefix, ignoreCase, CultureInfo.InvariantCulture)).ToArray(); + + if (matchedTypes.Length != 1) + return null; + + return matchedTypes[0]; + } + catch (Exception e) + { + if (throwOnError) + throw e; + + return null; + } + } +#else + public static Type GetType(string fullTypeName, bool throwOnError, bool ignoreCase) + { + return Type.GetType(fullTypeName, null, (a, n, ign) => + { + var targetType = a.GetType(n, false, ign); + + if (targetType != null) + return targetType; + + var typeNamePrefix = n + "`"; + + var matchedTypes = a.GetExportedTypes().Where(t => t.IsGenericType + && t.FullName.StartsWith(typeNamePrefix, ign, CultureInfo.InvariantCulture)).ToArray(); + + if (matchedTypes.Length != 1) + return null; + + return matchedTypes[0]; + }, throwOnError, ignoreCase); + } +#endif + + /// + /// Gets the implement types from assembly. + /// + /// The type of the base type. + /// The assembly. + /// + public static IEnumerable GetImplementTypes(this Assembly assembly) + { + return assembly.GetExportedTypes().Where(t => + t.IsSubclassOf(typeof(TBaseType)) && t.IsClass && !t.IsAbstract); + } + + /// + /// Gets the implemented objects by interface. + /// + /// The type of the base interface. + /// The assembly. + /// + public static IEnumerable GetImplementedObjectsByInterface(this Assembly assembly) + where TBaseInterface : class + { + return GetImplementedObjectsByInterface(assembly, typeof(TBaseInterface)); + } + + /// + /// Gets the implemented objects by interface. + /// + /// The type of the base interface. + /// The assembly. + /// Type of the target. + /// + public static IEnumerable GetImplementedObjectsByInterface(this Assembly assembly, Type targetType) + where TBaseInterface : class + { + Type[] arrType = assembly.GetExportedTypes(); + + var result = new List(); + + for (int i = 0; i < arrType.Length; i++) + { + var currentImplementType = arrType[i]; + + if (currentImplementType.IsAbstract) + continue; + + if (!targetType.IsAssignableFrom(currentImplementType)) + continue; + + result.Add((TBaseInterface)Activator.CreateInstance(currentImplementType)); + } + + return result; + } + +#if SILVERLIGHT +#else + /// + /// Clone object in binary format. + /// + /// + /// The target. + /// + public static T BinaryClone(this T target) + { + BinaryFormatter formatter = new BinaryFormatter(); + using (MemoryStream ms = new MemoryStream()) + { + formatter.Serialize(ms, target); + ms.Position = 0; + return (T)formatter.Deserialize(ms); + } + } +#endif + + + /// + /// Copies the properties of one object to another object. + /// + /// + /// The source. + /// The target. + /// + public static T CopyPropertiesTo(this T source, T target) + { + return source.CopyPropertiesTo(p => true, target); + } + + /// + /// Copies the properties of one object to another object. + /// + /// + /// The source. + /// The properties predict. + /// The target. + /// + public static T CopyPropertiesTo(this T source, Predicate predict, T target) + { + PropertyInfo[] properties = source.GetType() + .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); + + Dictionary sourcePropertiesDict = properties.ToDictionary(p => p.Name); + + PropertyInfo[] targetProperties = target.GetType() + .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty) + .Where(p => predict(p)).ToArray(); + + for (int i = 0; i < targetProperties.Length; i++) + { + var p = targetProperties[i]; + PropertyInfo sourceProperty; + + if (sourcePropertiesDict.TryGetValue(p.Name, out sourceProperty)) + { + if (sourceProperty.PropertyType != p.PropertyType) + continue; + + if (!sourceProperty.PropertyType.IsSerializable) + continue; + + p.SetValue(target, sourceProperty.GetValue(source, null), null); + } + } + + return target; + } + + /// + /// Gets the assemblies from string. + /// + /// The assembly def. + /// + public static IEnumerable GetAssembliesFromString(string assemblyDef) + { + return GetAssembliesFromStrings(assemblyDef.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)); + } + + /// + /// Gets the assemblies from strings. + /// + /// The assemblies. + /// + public static IEnumerable GetAssembliesFromStrings(string[] assemblies) + { + List result = new List(assemblies.Length); + + foreach (var a in assemblies) + { + result.Add(Assembly.Load(a)); + } + + return result; + } + + public static bool IsImplementedInterface() + { + return typeof(TInterface).IsAssignableFrom(typeof(T)); + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Common/BinaryHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/BinaryHelper.cs index ca09ede..da2b5dd 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/BinaryHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/BinaryHelper.cs @@ -1,44 +1,44 @@ -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; - -namespace Hncore.Infrastructure.Common -{ - /// - /// 二进制序列化 - /// - public class BinaryHelper - { - /// - /// 序列化对象(二进制) - /// - /// 需要序列化的对象 - public static byte[] Serialize(object obj) - { - using (MemoryStream ms = new MemoryStream()) - { - BinaryFormatter binaryFormatter = new BinaryFormatter(); - binaryFormatter.Serialize(ms, obj); - return ms.ToArray(); - } - } - - /// - /// 反序列化对象(二进制) - /// - /// 需要反序列化的字符串 - public static object Deserialize(byte[] bytes) - { - if (bytes == null) - { - return null; - } - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(bytes, 0, bytes.Length); - ms.Seek(0, SeekOrigin.Begin); - BinaryFormatter binaryFormatter = new BinaryFormatter(); - return binaryFormatter.Deserialize(ms); - } - } - } -} +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +namespace Hncore.Infrastructure.Common +{ + /// + /// 二进制序列化 + /// + public class BinaryHelper + { + /// + /// 序列化对象(二进制) + /// + /// 需要序列化的对象 + public static byte[] Serialize(object obj) + { + using (MemoryStream ms = new MemoryStream()) + { + BinaryFormatter binaryFormatter = new BinaryFormatter(); + binaryFormatter.Serialize(ms, obj); + return ms.ToArray(); + } + } + + /// + /// 反序列化对象(二进制) + /// + /// 需要反序列化的字符串 + public static object Deserialize(byte[] bytes) + { + if (bytes == null) + { + return null; + } + using (MemoryStream ms = new MemoryStream()) + { + ms.Write(bytes, 0, bytes.Length); + ms.Seek(0, SeekOrigin.Begin); + BinaryFormatter binaryFormatter = new BinaryFormatter(); + return binaryFormatter.Deserialize(ms); + } + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Common/CheckHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/CheckHelper.cs index 468a659..5459be4 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/CheckHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/CheckHelper.cs @@ -1,55 +1,55 @@ -using System; -using System.Linq; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Hncore.Infrastructure.Data; - -namespace Hncore.Infrastructure.Common -{ - public class CheckHelper - { - public static void NotNull(object obj, string message = "") - { - if (ReferenceEquals(obj, null)) - { - if (string.IsNullOrEmpty(message)) - { - message = nameof(obj) + "空引用"; - } - - throw new BusinessException(message); - } - } - - public static void NotEmpty(string obj, string message = "") - { - NotNull(obj,message); - - if (obj.Trim() == "") - { - if (string.IsNullOrEmpty(message)) - { - message = nameof(obj) + "值不能为空"; - } - - throw new BusinessException(message); - } - } - } - - public static class Ext - { - public static void Check(this ModelStateDictionary dic) - { - if (!dic.IsValid) - { - var errs = dic.Values.SelectMany(x => x.Errors); - - string msg = ""; - - errs.Select(t => t.Exception.Message).ToList().ForEach((s => { msg += s + "\r\n"; })); - - throw new Exception(msg); - } - } - } +using System; +using System.Linq; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Hncore.Infrastructure.Data; + +namespace Hncore.Infrastructure.Common +{ + public class CheckHelper + { + public static void NotNull(object obj, string message = "") + { + if (ReferenceEquals(obj, null)) + { + if (string.IsNullOrEmpty(message)) + { + message = nameof(obj) + "空引用"; + } + + throw new BusinessException(message); + } + } + + public static void NotEmpty(string obj, string message = "") + { + NotNull(obj,message); + + if (obj.Trim() == "") + { + if (string.IsNullOrEmpty(message)) + { + message = nameof(obj) + "值不能为空"; + } + + throw new BusinessException(message); + } + } + } + + public static class Ext + { + public static void Check(this ModelStateDictionary dic) + { + if (!dic.IsValid) + { + var errs = dic.Values.SelectMany(x => x.Errors); + + string msg = ""; + + errs.Select(t => t.Exception.Message).ToList().ForEach((s => { msg += s + "\r\n"; })); + + throw new Exception(msg); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/ConcurrentHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/ConcurrentHelper.cs index c85991f..36a5177 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/ConcurrentHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/ConcurrentHelper.cs @@ -1,75 +1,75 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Dapper; -using MySql.Data.MySqlClient; - -namespace Hncore.Infrastructure.Common -{ - public class ConcurrentHelper - { - private static string _connString = ""; - private static string _tableName = "concurrent_helper"; - - private List _infos = new List(); - - private string _id; - - public static void Init(string mysqlConn) - { - _connString = mysqlConn; - - MySqlHelper.Execute(_connString - , $@"create table if not exists {_tableName} - ( - Id VARCHAR(32) PRIMARY KEY UNIQUE, - CreateTime datetime DEFAULT now(), - Info text - )" - ); - } - - public ConcurrentHelper AddInfo(string info) - { - _infos.Add(info); - - return this; - } - - private async Task GetAuthority() - { - try - { - string info = ListHelper.ListToStr(_infos, "\n"); - _id = SecurityHelper.GetMd5Hash(info); - - string sql = $"insert into {_tableName}(Id,Info) values(@Id,@Info)"; - - await MySqlHelper.ExecuteAsync(_connString, sql, new {Id = _id, Info = info}); - } - catch - { - return false; - } - - return true; - } - - public async Task Execute(Action action) - { - try - { - if (await GetAuthority()) - { - action(); - } - } - catch (Exception e) - { - await MySqlHelper.ExecuteAsync(_connString, $"DELETE FROM {_tableName} where Id='{_id}'"); - - throw e; - } - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Dapper; +using MySql.Data.MySqlClient; + +namespace Hncore.Infrastructure.Common +{ + public class ConcurrentHelper + { + private static string _connString = ""; + private static string _tableName = "concurrent_helper"; + + private List _infos = new List(); + + private string _id; + + public static void Init(string mysqlConn) + { + _connString = mysqlConn; + + MySqlHelper.Execute(_connString + , $@"create table if not exists {_tableName} + ( + Id VARCHAR(32) PRIMARY KEY UNIQUE, + CreateTime datetime DEFAULT now(), + Info text + )" + ); + } + + public ConcurrentHelper AddInfo(string info) + { + _infos.Add(info); + + return this; + } + + private async Task GetAuthority() + { + try + { + string info = ListHelper.ListToStr(_infos, "\n"); + _id = SecurityHelper.GetMd5Hash(info); + + string sql = $"insert into {_tableName}(Id,Info) values(@Id,@Info)"; + + await MySqlHelper.ExecuteAsync(_connString, sql, new {Id = _id, Info = info}); + } + catch + { + return false; + } + + return true; + } + + public async Task Execute(Action action) + { + try + { + if (await GetAuthority()) + { + action(); + } + } + catch (Exception e) + { + await MySqlHelper.ExecuteAsync(_connString, $"DELETE FROM {_tableName} where Id='{_id}'"); + + throw e; + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/ContentTypeHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/ContentTypeHelper.cs index 80884bb..6bb2132 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/ContentTypeHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/ContentTypeHelper.cs @@ -1,102 +1,102 @@ -using System.Collections.Generic; -using System.ComponentModel; - -namespace Hncore.Infrastructure.Common -{ - /// - /// 文件类型 - /// - public class ContentTypeHelper - { - /// - /// 图片格式 - /// - public static List ImageList = new List() - { - "image/jpeg", - "application/x-jpe", - "application/x-jpg", - "application/x-bmp", - "image/png", - "application/x-png", - "image/gif" - }; - - /// - /// 视频格式 - /// - public static List VideoList = new List() - { - "video/mpeg4", - "video/avi", - "video/x-ms-wmv", - "video/x-mpeg", - "video/mpeg4", - "video/x-sgi-movie", - "application/vnd.rn-realmedia", - "application/x-shockwave-flash", - "application/vnd.rn-realmedia-vbr", - "application/octet-stream", - "flv-application/octet-stream", - "video/mpg", - "video/mpeg", - "video/mp4", - "video/x-msvideo" - }; - - #region 得到文件类型 - - /// - /// 得到文件类型 - /// - /// 文件类型 - /// - public static FileTypeEnum GetFileType(string contentType) - { - if (ImageList.Contains(contentType)) - { - return FileTypeEnum.Img; - } - - if (VideoList.Contains(contentType)) - { - return FileTypeEnum.Video; - } - - return FileTypeEnum.Unknow; - } - - #endregion - } - - /// - /// 文件类型 - /// - public enum FileTypeEnum - { - /// - /// 未知 - /// - [Description("未知")] Unknow = 0, - - /// - /// 图片 - /// - [Description("图片")] Img = 1, - - /// - /// 音频 - /// - [Description("音频")] Audio = 2, - - /// - /// 视频 - /// - [Description("视频")] Video = 3, - - /// - /// 其他 - /// - [Description("其他")] Other = 4 - } +using System.Collections.Generic; +using System.ComponentModel; + +namespace Hncore.Infrastructure.Common +{ + /// + /// 文件类型 + /// + public class ContentTypeHelper + { + /// + /// 图片格式 + /// + public static List ImageList = new List() + { + "image/jpeg", + "application/x-jpe", + "application/x-jpg", + "application/x-bmp", + "image/png", + "application/x-png", + "image/gif" + }; + + /// + /// 视频格式 + /// + public static List VideoList = new List() + { + "video/mpeg4", + "video/avi", + "video/x-ms-wmv", + "video/x-mpeg", + "video/mpeg4", + "video/x-sgi-movie", + "application/vnd.rn-realmedia", + "application/x-shockwave-flash", + "application/vnd.rn-realmedia-vbr", + "application/octet-stream", + "flv-application/octet-stream", + "video/mpg", + "video/mpeg", + "video/mp4", + "video/x-msvideo" + }; + + #region 得到文件类型 + + /// + /// 得到文件类型 + /// + /// 文件类型 + /// + public static FileTypeEnum GetFileType(string contentType) + { + if (ImageList.Contains(contentType)) + { + return FileTypeEnum.Img; + } + + if (VideoList.Contains(contentType)) + { + return FileTypeEnum.Video; + } + + return FileTypeEnum.Unknow; + } + + #endregion + } + + /// + /// 文件类型 + /// + public enum FileTypeEnum + { + /// + /// 未知 + /// + [Description("未知")] Unknow = 0, + + /// + /// 图片 + /// + [Description("图片")] Img = 1, + + /// + /// 音频 + /// + [Description("音频")] Audio = 2, + + /// + /// 视频 + /// + [Description("视频")] Video = 3, + + /// + /// 其他 + /// + [Description("其他")] Other = 4 + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/DateTimeHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/DateTimeHelper.cs index f0c53da..91b2dc6 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/DateTimeHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/DateTimeHelper.cs @@ -1,80 +1,80 @@ -using System; -using System.Runtime.InteropServices; - -namespace Hncore.Infrastructure.Common -{ - public class DateTimeHelper - { - public static DateTime SqlMinTime => Convert.ToDateTime("1975-01-01 00:00:00"); - - public static DateTime SqlMaxTime => Convert.ToDateTime("9999-12-31 23:59:59"); - - /// - /// 将10位时间戳转时间 - /// - /// - /// - public static DateTime UnixTimeStampToDateTime(long unixTimeStamp) - { - // Unix timestamp is seconds past epoch - DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime(); - return dtDateTime; - } - - public static long ToUnixTimestamp(DateTime target) - { - return Convert.ToInt64((TimeZoneInfo.ConvertTimeToUtc(target) - - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds); - } - - /// - /// 将13位时间戳转为时间 - /// - /// - /// - public static DateTime JsTimeStampToDateTime(double javaTimeStamp) - { - var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - dtDateTime = dtDateTime.AddMilliseconds(javaTimeStamp).ToLocalTime(); - return dtDateTime; - } - - /// - /// 获取时间戳 - /// - /// - /// 为真时获取10位时间戳,为假时获取13位时间戳 - /// - public static long ToUnixTime(DateTime target, bool bflag = false) - { - TimeSpan ts = target - new DateTime(1970, 1, 1, 0, 0, 0, 0); - long timer = 0; - timer = Convert.ToInt64(!bflag ? ts.TotalSeconds : ts.TotalMilliseconds); - return timer; - } - - public static TimeZoneInfo GetCstTimeZoneInfo() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai"); - } - - return TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"); - } - - public static bool IsSameDayOrLess(DateTime dt1, DateTime dt2) - { - return dt1.Year == dt2.Year - && dt1.Month == dt2.Month - && dt1.Day == dt2.Day - || dt1 < dt2; - } - - public static DateTime GetDatePart(DateTime dt) - { - return new DateTime(dt.Year, dt.Month, dt.Day); - } - } +using System; +using System.Runtime.InteropServices; + +namespace Hncore.Infrastructure.Common +{ + public class DateTimeHelper + { + public static DateTime SqlMinTime => Convert.ToDateTime("1975-01-01 00:00:00"); + + public static DateTime SqlMaxTime => Convert.ToDateTime("9999-12-31 23:59:59"); + + /// + /// 将10位时间戳转时间 + /// + /// + /// + public static DateTime UnixTimeStampToDateTime(long unixTimeStamp) + { + // Unix timestamp is seconds past epoch + DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime(); + return dtDateTime; + } + + public static long ToUnixTimestamp(DateTime target) + { + return Convert.ToInt64((TimeZoneInfo.ConvertTimeToUtc(target) - + new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds); + } + + /// + /// 将13位时间戳转为时间 + /// + /// + /// + public static DateTime JsTimeStampToDateTime(double javaTimeStamp) + { + var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + dtDateTime = dtDateTime.AddMilliseconds(javaTimeStamp).ToLocalTime(); + return dtDateTime; + } + + /// + /// 获取时间戳 + /// + /// + /// 为真时获取10位时间戳,为假时获取13位时间戳 + /// + public static long ToUnixTime(DateTime target, bool bflag = false) + { + TimeSpan ts = target - new DateTime(1970, 1, 1, 0, 0, 0, 0); + long timer = 0; + timer = Convert.ToInt64(!bflag ? ts.TotalSeconds : ts.TotalMilliseconds); + return timer; + } + + public static TimeZoneInfo GetCstTimeZoneInfo() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai"); + } + + return TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"); + } + + public static bool IsSameDayOrLess(DateTime dt1, DateTime dt2) + { + return dt1.Year == dt2.Year + && dt1.Month == dt2.Month + && dt1.Day == dt2.Day + || dt1 < dt2; + } + + public static DateTime GetDatePart(DateTime dt) + { + return new DateTime(dt.Year, dt.Month, dt.Day); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/DebugClass.cs b/Infrastructure/Hncore.Infrastructure/Common/DebugClass.cs index e40af52..7ee3fa6 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/DebugClass.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/DebugClass.cs @@ -1,28 +1,28 @@ -using System; -using System.Diagnostics; -using System.Text; - -namespace Hncore.Infrastructure.Utils -{ - public static class DebugClass - { - public static string PrintStack(bool isOutToDebugWin) - { - StackFrame[] stacks = new StackTrace().GetFrames(); - StringBuilder result = new StringBuilder(); - foreach (StackFrame stack in stacks) - { - result.AppendFormat("{0} {1} {2} {3}{4}", - stack.GetFileName(), - stack.GetFileLineNumber(), - stack.GetFileColumnNumber(), - stack.GetMethod().ToString(), - Environment.NewLine - ); - } - if (isOutToDebugWin) - Debug.WriteLine(result); - return result.ToString(); - } - } -} +using System; +using System.Diagnostics; +using System.Text; + +namespace Hncore.Infrastructure.Utils +{ + public static class DebugClass + { + public static string PrintStack(bool isOutToDebugWin) + { + StackFrame[] stacks = new StackTrace().GetFrames(); + StringBuilder result = new StringBuilder(); + foreach (StackFrame stack in stacks) + { + result.AppendFormat("{0} {1} {2} {3}{4}", + stack.GetFileName(), + stack.GetFileLineNumber(), + stack.GetFileColumnNumber(), + stack.GetMethod().ToString(), + Environment.NewLine + ); + } + if (isOutToDebugWin) + Debug.WriteLine(result); + return result.ToString(); + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Common/DingTalkHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/DingTalkHelper.cs index 005fc70..e6fbadb 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/DingTalkHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/DingTalkHelper.cs @@ -1,151 +1,151 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; -using Hncore.Infrastructure.Serializer; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Hncore.Infrastructure.Common.DingTalk -{ - public class DingTalkHelper - { - private static HttpClient _httpClient = new HttpClient(new HttpClientHandler() {UseProxy = false}) - {Timeout = TimeSpan.FromMinutes(1)}; - - static DingTalkHelper() - { - _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - } - - public static Task SendMessage(object message) - { - return null; - // return _httpClient.PostAsync("https://oapi.dingtalk.com/robot/send?access_token=33", JsonContent(message)); - } - - private static StringContent JsonContent(object obj) - { - return new StringContent(obj.ToJson(), Encoding.UTF8, "application/json"); - } - - - private static async Task CheckSuccess(HttpResponseMessage res) - { - var content = await res.Content.ReadAsStringAsync(); - - JObject jObject = JsonConvert.DeserializeObject(content); - - if (jObject["errmsg"].ToString() == "ok" && Convert.ToInt32(jObject["errcode"]) != 0) - { - return false; - } - - return true; - } - } - - - /// - /// 此消息类型为固定text - /// - public class TextModel - { - /// - /// 此消息类型为固定text - /// - public string msgtype => "text"; - - /// - /// 消息内容 - /// - public text text { get; set; } - - /// - /// @人 - /// - public atText at { get; set; } - } - - /// - /// 消息内容 - /// - public class text - { - /// - /// 消息内容 - /// - public string content { get; set; } - } - - /// - /// @人 - /// - public class atText - { - /// - /// 被@人的手机号 - /// - public List atMobiles { get; set; } - - /// - /// @所有人时:true,否则为:false - /// - public bool isAtAll { get; set; } = false; - } - - /// - /// 此消息类型为固定markdown - /// - public class MarkDownModel - { - /// - /// 此消息类型为固定markdown - /// - public string msgtype => "markdown"; - - /// - /// 消息内容 - /// - public markdown markdown { get; set; } - - /// - /// @人 - /// - public atMarkdown at { get; set; } - } - - /// - /// 消息内容 - /// - public class markdown - { - /// - /// 标题 - /// - public string title { get; set; } - - /// - /// 消息内容 - /// - public string text { get; set; } - } - - /// - /// @人 - /// - public class atMarkdown - { - /// - /// 被@人的手机号 - /// - public List atMobiles { get; set; } - - /// - /// @所有人时:true,否则为:false - /// - public bool isAtAll { get; set; } = false; - } +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Hncore.Infrastructure.Serializer; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Hncore.Infrastructure.Common.DingTalk +{ + public class DingTalkHelper + { + private static HttpClient _httpClient = new HttpClient(new HttpClientHandler() {UseProxy = false}) + {Timeout = TimeSpan.FromMinutes(1)}; + + static DingTalkHelper() + { + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + } + + public static Task SendMessage(object message) + { + return null; + // return _httpClient.PostAsync("https://oapi.dingtalk.com/robot/send?access_token=33", JsonContent(message)); + } + + private static StringContent JsonContent(object obj) + { + return new StringContent(obj.ToJson(), Encoding.UTF8, "application/json"); + } + + + private static async Task CheckSuccess(HttpResponseMessage res) + { + var content = await res.Content.ReadAsStringAsync(); + + JObject jObject = JsonConvert.DeserializeObject(content); + + if (jObject["errmsg"].ToString() == "ok" && Convert.ToInt32(jObject["errcode"]) != 0) + { + return false; + } + + return true; + } + } + + + /// + /// 此消息类型为固定text + /// + public class TextModel + { + /// + /// 此消息类型为固定text + /// + public string msgtype => "text"; + + /// + /// 消息内容 + /// + public text text { get; set; } + + /// + /// @人 + /// + public atText at { get; set; } + } + + /// + /// 消息内容 + /// + public class text + { + /// + /// 消息内容 + /// + public string content { get; set; } + } + + /// + /// @人 + /// + public class atText + { + /// + /// 被@人的手机号 + /// + public List atMobiles { get; set; } + + /// + /// @所有人时:true,否则为:false + /// + public bool isAtAll { get; set; } = false; + } + + /// + /// 此消息类型为固定markdown + /// + public class MarkDownModel + { + /// + /// 此消息类型为固定markdown + /// + public string msgtype => "markdown"; + + /// + /// 消息内容 + /// + public markdown markdown { get; set; } + + /// + /// @人 + /// + public atMarkdown at { get; set; } + } + + /// + /// 消息内容 + /// + public class markdown + { + /// + /// 标题 + /// + public string title { get; set; } + + /// + /// 消息内容 + /// + public string text { get; set; } + } + + /// + /// @人 + /// + public class atMarkdown + { + /// + /// 被@人的手机号 + /// + public List atMobiles { get; set; } + + /// + /// @所有人时:true,否则为:false + /// + public bool isAtAll { get; set; } = false; + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/EnvironmentVariableHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/EnvironmentVariableHelper.cs index d02b92c..4c108d5 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/EnvironmentVariableHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/EnvironmentVariableHelper.cs @@ -1,14 +1,14 @@ -using System; - -namespace Hncore.Infrastructure.Common -{ - public class EnvironmentVariableHelper - { - /// - /// 当前环境是否为生产模式 - /// - public static bool IsAspNetCoreProduction => Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production"; - - public static string HostName => Environment.GetEnvironmentVariable("HOSTNAME"); - } +using System; + +namespace Hncore.Infrastructure.Common +{ + public class EnvironmentVariableHelper + { + /// + /// 当前环境是否为生产模式 + /// + public static bool IsAspNetCoreProduction => Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production"; + + public static string HostName => Environment.GetEnvironmentVariable("HOSTNAME"); + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/ExcelHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/ExcelHelper.cs index 7678340..fc9c923 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/ExcelHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/ExcelHelper.cs @@ -1,644 +1,644 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Web; -using AngleSharp; -using AngleSharp.Dom; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Mvc; -using NPOI.HSSF.UserModel; - -namespace Hncore.Infrastructure.Common -{ - public static class ExcelHelper - { - public static async Task GetStreamFromHtml(string html) - { - MemoryStream ms = new MemoryStream(); - - HSSFWorkbook book = new HSSFWorkbook(); - - var context = BrowsingContext.New(Configuration.Default); - - var document = await context.OpenAsync(req => req.Content(html)); - - try - { - var tables = document.GetElementsByTagName("table"); - - foreach (var table in tables) - { - var sheetName = table.GetAttribute("sheetname"); - - var sheet = book.CreateSheet(sheetName); - - var trs = table.GetElementsByTagName("tr"); - - var rowIndex = 0; - - foreach (var tr in trs) - { - var row = sheet.CreateRow(rowIndex); - - var tds = tr.GetElementsByTagName("td"); - - var columnIndex = 0; - - foreach (var td in tds) - { - row.CreateCell(columnIndex).SetCellValue(td.InnerHtml.Trim()); - - columnIndex++; - } - - rowIndex++; - - } - } - - book.Write(ms); - ms.Position = 0; - } - finally - { - document.Close(); - document.Dispose(); - - context.Active.Close(); - context.Active.Dispose(); - - document = null; - context = null; - - book.Clear(); - book.Close(); - book = null; - } - - - return ms; - } - - public static async Task ResponseExcelFromHtml(this ControllerBase controllerBase, string fileName, string html) - { - using (var ms = await GetStreamFromHtml(html)) - { - var bytes = ms.StreamToBytes(); - - var response = controllerBase.HttpContext.Response; - - response.ContentType = "application/octet-stream"; - response.Headers.Add("Content-Disposition", $"attachment; filename={fileName.UrlEncode()}"); - response.Headers.Add("X-Suggested-Filename", fileName.UrlEncode()); - response.Headers.Add("Content-Length", bytes.Length.ToString()); - - await response.Body.WriteAsync(bytes, 0, bytes.Length); - await response.Body.FlushAsync(); - } - } - /// - /// excel二进制文件转二维string结合 - /// - /// - /// - /// - public static List> ReadFromStream(Stream stream, int st = 0) - { - //根据路径通过已存在的excel来创建HSSFWorkbook,即整个excel文档 - HSSFWorkbook workbook; - try - { - workbook = new HSSFWorkbook(stream); - } - catch (Exception ex) - { - LogHelper.Error("ReadFromStream",ex); - throw new BusinessException("文件读取错误!"); - } - List> lis1 = new List>(); - { - //获取excel的第一个sheet - var sheet = workbook.GetSheetAt(st); - if (sheet == null) - throw new BusinessException("该文件内没有包含任何工作簿"); - //获取sheet的首行 - var headerRow = sheet.GetRow(0); - //一行最后一个方格的编号 即总的列数 - int cellCount = headerRow.LastCellNum; - - try - { - for (int i = 0; i <= sheet.LastRowNum; i++) - { - List lis = new List(); - var row = sheet.GetRow(i); - if (row == null) - continue; - for (int j = 0; j < cellCount; j++) - { - var cell = row.GetCell(j); - if (cell == null) - { - lis.Add(""); - continue; - } - try - { - switch (cell.CellType) - { - case NPOI.SS.UserModel.CellType.Unknown: - lis.Add("Unknown"); - break; - case NPOI.SS.UserModel.CellType.Numeric: - if (HSSFDateUtil.IsCellDateFormatted(cell))//对日期格式进行特殊对待 - lis.Add(HSSFDateUtil.GetJavaDate(cell.NumericCellValue).ToString()); - else - lis.Add(cell.NumericCellValue.ToString()); - break; - case NPOI.SS.UserModel.CellType.String: - lis.Add(cell.StringCellValue.ToString()); - break; - case NPOI.SS.UserModel.CellType.Formula: - lis.Add(cell.CellFormula.ToString()); - break; - case NPOI.SS.UserModel.CellType.Blank: - lis.Add(""); - break; - case NPOI.SS.UserModel.CellType.Boolean: - lis.Add(cell.BooleanCellValue.ToString()); - break; - case NPOI.SS.UserModel.CellType.Error: - lis.Add(cell.ErrorCellValue.ToString()); - break; - default: - break; - } - } - catch - { - lis.Add(""); - } - } - - //如果本行所有单元格都是空,就跳过本行 - if (lis.All(item => string.IsNullOrEmpty(item))) - continue; - - lis1.Add(lis); - } - } - catch(Exception ex) - { - LogHelper.Error("ReadFromStream", ex); - throw new BusinessException("文件格式错误!"); - } - } - #region 细节化处理 - lis1 = lis1.Where(s => !s.TrueForAll(f => string.IsNullOrWhiteSpace(f))).ToList();//去除全是空格的空行。 - lis1 = lis1.Select(s => s = s.Select(y => y = y.Trim()).ToList()).ToList();//去除空格 - if (lis1.Count == 1) throw new Exception("Excel中无有效数据!"); - #endregion - return lis1; - } - /// - /// 导出列表到excel - /// 导出到sheet的数据一致 - /// - /// - /// 每一个sheet的数据 - /// 列表的属性和名称值 - /// - public static byte[] ExportListToExcel(List> excelData, List excelTitle) - { - var workbook = new NPOI.XSSF.UserModel.XSSFWorkbook(); - - var entityType = typeof(T); - PropertyInfo[] entityProperties = entityType.GetProperties(); - - if (excelData == null || excelData.Count == 0) - { - return null; - } - try - { - foreach (var item in excelData) - { - #region MyRegion - //var sheet = workbook.CreateSheet(item.SheetName.Replace('/','-')); - //var titleRow = sheet.CreateRow(0); - //for (int i = 0; i < excelTitle.Count; i++) - //{ - // titleRow.CreateCell(i).SetCellValue(excelTitle[i].Title); - //} - //var sheetData = item.Data; - //for (int j = 0; j < sheetData.Count; j++) - //{ - // var dataRow = sheet.CreateRow(j + 1); - // for (int i = 0; i < excelTitle.Count; i++) - // { - // if (excelTitle[i].Property.ToUpper().Equals("ID")) - // { - // var num = j + 1; - // dataRow.CreateCell(i).SetCellValue(num); - // continue; - // } - // var entityProperty = entityProperties.FirstOrDefault(m => m.Name == excelTitle[i].Property); - - // var cellVal = entityProperty?.GetValue(sheetData[j]); - // if (cellVal?.GetType().Name == "DateTime") - // { - // dataRow.CreateCell(i).SetCellValue(((DateTime?)cellVal)?.ToString("yyyy/MM/dd HH:mm:ss")); - // } - // else - // { - // dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); - // } - // //dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); - // } - //} - #endregion - CreateSheetData(workbook, item, excelTitle, entityProperties); - } - using (var ms = new MemoryStream()) - { - workbook.Write(ms); - var bytes = ms.ToArray(); - return bytes; - } - } - catch(Exception ex) - { - LogHelper.Error("ExportListToExcel=>" + ex); - - throw ex; - } - finally - { - workbook.Clear(); - workbook.Close(); - workbook = null; - } - } - public static byte[] ExportListToExcel(ExcelData excelData, List excelTitle) - { - var workbook = new NPOI.XSSF.UserModel.XSSFWorkbook(); - - var entityType = typeof(T); - PropertyInfo[] entityProperties = entityType.GetProperties(); - - try - { - #region MyRegion - //var sheet = workbook.CreateSheet(excelData.SheetName.Replace('/', '-')); - //var titleRow = sheet.CreateRow(0); - //for (int i = 0; i < excelTitle.Count; i++) - //{ - // titleRow.CreateCell(i).SetCellValue(excelTitle[i].Title); - //} - //var sheetData = excelData.Data; - //for (int j = 0; j < sheetData.Count; j++) - //{ - // var dataRow = sheet.CreateRow(j + 1); - // for (int i = 0; i < excelTitle.Count; i++) - // { - // var cellVal = entityProperties.FirstOrDefault(m => m.Name == excelTitle[i].Property)?.GetValue(sheetData[j]); - - // if (cellVal?.GetType().Name == "DateTime") - // { - // dataRow.CreateCell(i).SetCellValue(((DateTime?)cellVal)?.ToString("yyyy/MM/dd HH:mm:ss")); - // } - // else - // { - // dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); - // } - // } - //} - #endregion - CreateSheetData(workbook, excelData, excelTitle, entityProperties); - using (var ms = new MemoryStream()) - { - workbook.Write(ms); - var bytes = ms.ToArray(); - return bytes; - } - } - catch (Exception ex) - { - LogHelper.Error("ExportListToExcel=>" + ex); - - throw ex; - } - finally - { - workbook.Clear(); - workbook.Close(); - workbook = null; - } - } - /// - /// 创建excel表单数据 - /// - /// - /// - /// - /// - /// - private static void CreateSheetData(NPOI.XSSF.UserModel.XSSFWorkbook workbook, - ExcelData excelData, List excelTitle,PropertyInfo[] entityProperties) - { - var sheet = workbook.CreateSheet(excelData.SheetName.Replace('/', '-')); - var titleRow = sheet.CreateRow(0); - for (int i = 0; i < excelTitle.Count; i++) - { - titleRow.CreateCell(i).SetCellValue(excelTitle[i].Title); - } - var sheetData = excelData.Data; - for (int j = 0; j < sheetData.Count; j++) - { - var dataRow = sheet.CreateRow(j + 1); - for (int i = 0; i < excelTitle.Count; i++) - { - var currentTitle = excelTitle[i]; - if (currentTitle.Property.Equals("序号")) - { - var num = j + 1; - dataRow.CreateCell(i).SetCellValue(num); - continue; - } - var cellVal = entityProperties.FirstOrDefault(m => m.Name == currentTitle.Property)?.GetValue(sheetData[j]); - if (currentTitle.Format != null) - { - cellVal = currentTitle.Format(cellVal); - } - if (currentTitle.Expr != null) - { - cellVal = currentTitle.Expr(sheetData[j]); - } - else if (cellVal?.GetType().Name == "DateTime") - { - dataRow.CreateCell(i).SetCellValue(((DateTime?)cellVal)?.ToString("yyyy/MM/dd HH:mm:ss")); - } - dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); - } - } - } - #region DownloadAsync(下载) - - - /// - /// 下载 - /// - /// 流 - /// 文件名,包含扩展名 - public static async Task DownloadAsync(this ControllerBase controllerBase, Stream stream, string fileName) - { - await DownloadAsync(controllerBase,stream, fileName, Encoding.UTF8); - } - /// - /// 下载 - /// - /// 流 - /// 文件名,包含扩展名 - /// 字符编码 - public static async Task DownloadAsync(this ControllerBase controllerBase, Stream stream, string fileName, Encoding encoding) - { - stream.Seek(0, SeekOrigin.Begin); - var buffer = new byte[stream.Length]; - stream.Read(buffer, 0, buffer.Length); - - await DownloadAsync(controllerBase, buffer, fileName, encoding); - } - /// - /// 下载 - /// - /// 字节流 - /// 文件名,包含扩展名 - public static async Task DownloadAsync(this ControllerBase controllerBase, byte[] bytes, string fileName) - { - await DownloadAsync(controllerBase,bytes, fileName, Encoding.UTF8); - } - - /// - /// 下载 - /// - /// 字节流 - /// 文件名,包含扩展名 - /// 字符编码 - public static async Task DownloadAsync(this ControllerBase controllerBase,byte[] bytes, string fileName, Encoding encoding) - { - var response = controllerBase.HttpContext.Response; - if (bytes == null || bytes.Length == 0) - return; - fileName = fileName.Replace(" ", ""); - fileName = HttpUtility.UrlEncode(fileName, encoding); - response.ContentType = "application/octet-stream"; - response.Headers.Add("Content-Disposition", $"attachment; filename={fileName}"); - response.Headers.Add("Content-Length", bytes.Length.ToString()); - response.Headers.Add("X-Suggested-Filename", fileName); - await response.Body.WriteAsync(bytes, 0, bytes.Length); - await response.Body.FlushAsync(); - } - - #endregion - } - - public class ExcelTitle - { - /// - /// 导出数据对应的 属性字段名 - /// - public string Property { get; set; } - /// - /// excel 的title - /// - public string Title { get; set; } - - public Func Format { get; set; } - public Func Expr { get; set; } - } - public class ExcelData - { - /// - /// 导出数据对应的 属性字段名 - /// - public string SheetName { get; set; } - /// - /// excel 的title - /// - public List Data { get; set; } - } - #region Excel导入验证 - /// - /// Excel导入验证 - /// - public static class ValidForExcel - { - - /// - /// 解析字符串到int32、double、datetime... - /// - /// - /// - /// - /// - /// - /// 自然数0,1,2,3... - /// - public static T ValidParseStr(string str,string message = "数据", bool isCanNullOrEmpty = true, bool isPositiveOrZero = true) - { - var val = default(T); - if (string.IsNullOrEmpty(str)) - { - if (isCanNullOrEmpty) - { - return default(T); - } - else - { - throw new Exception($"{message}不能为空"); - } - } - else - { - if (typeof(T).Name == typeof(int).Name || typeof(T).FullName == typeof(int?).FullName) - { - if (int.TryParse(str, out var valueInt)) - { - if (isPositiveOrZero && valueInt < 0) - { - goto error; - } - return (T)Convert.ChangeType(valueInt, TypeCode.Int32); - } - else - { - goto error; - } - } - if (typeof(T).Name == typeof(Decimal).Name || typeof(T).FullName == typeof(Decimal?).FullName) - { - if (Decimal.TryParse(str, out var valueInt)) - { - if (isPositiveOrZero && valueInt < 0) - { - goto error; - } - return (T)Convert.ChangeType(valueInt, TypeCode.Decimal); - } - else - { - goto error; - } - } - if (typeof(T).Name == typeof(Double).Name || typeof(T).FullName == typeof(Double?).FullName) - { - if (Double.TryParse(str, out var valueInt)) - { - if (isPositiveOrZero && valueInt < 0) - { - goto error; - } - return (T)Convert.ChangeType(valueInt, TypeCode.Double); - } - else - { - goto error; - } - } - if (typeof(T).Name == typeof(Single).Name || typeof(T).FullName == typeof(Single?).FullName) - { - if (Single.TryParse(str, out var valueInt)) - { - if (isPositiveOrZero && valueInt < 0) - { - goto error; - } - return (T)Convert.ChangeType(valueInt, TypeCode.Single); - } - else - { - goto error; - } - } - if (typeof(T).Name == typeof(DateTime).Name || typeof(T).FullName == typeof(DateTime?).FullName) - { - if (DateTime.TryParse(str, out var valueInt)) - { - return (T)Convert.ChangeType(valueInt, TypeCode.DateTime); - } - else - { - goto error; - } - } - } - return val; - error: throw new Exception($"{message}的值有误"); - } - - /// - /// 提供正则、提示信息,返回验证结果 - /// - /// - /// - /// - /// - /// - /// - public static string ValidByPattern(string str,string pattern, string message = "数据", bool isCanNullOrEmpty = true) - { - if (string.IsNullOrEmpty(str)) - { - if (isCanNullOrEmpty) - { - return str; - } - else - { - throw new BusinessException($"{message}不能为空"); - } - } - else - { - if (!Regex.IsMatch(str, pattern)) throw new BusinessException($"{message}输入有误"); - return str; - } - } - /// - /// 根据枚举判断 - /// - /// - /// - /// - /// - /// - public static int? ValidByEnum(string str, string message, bool isCanNullOrEmpty = true) //where T:Enum - { - - if (string.IsNullOrEmpty(str)) - { - if (isCanNullOrEmpty) - { - return null; - } - else - { - throw new BusinessException($"{message}不能为空"); - } - } - else - { - var value = EnumExtension.EnumToList().Find(s => s.Name == str)?.Value; - if (value == null) throw new BusinessException($"未知的数据:{str}"); - return Convert.ToInt32(value); - } - } - } - #endregion +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Web; +using AngleSharp; +using AngleSharp.Dom; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Mvc; +using NPOI.HSSF.UserModel; + +namespace Hncore.Infrastructure.Common +{ + public static class ExcelHelper + { + public static async Task GetStreamFromHtml(string html) + { + MemoryStream ms = new MemoryStream(); + + HSSFWorkbook book = new HSSFWorkbook(); + + var context = BrowsingContext.New(Configuration.Default); + + var document = await context.OpenAsync(req => req.Content(html)); + + try + { + var tables = document.GetElementsByTagName("table"); + + foreach (var table in tables) + { + var sheetName = table.GetAttribute("sheetname"); + + var sheet = book.CreateSheet(sheetName); + + var trs = table.GetElementsByTagName("tr"); + + var rowIndex = 0; + + foreach (var tr in trs) + { + var row = sheet.CreateRow(rowIndex); + + var tds = tr.GetElementsByTagName("td"); + + var columnIndex = 0; + + foreach (var td in tds) + { + row.CreateCell(columnIndex).SetCellValue(td.InnerHtml.Trim()); + + columnIndex++; + } + + rowIndex++; + + } + } + + book.Write(ms); + ms.Position = 0; + } + finally + { + document.Close(); + document.Dispose(); + + context.Active.Close(); + context.Active.Dispose(); + + document = null; + context = null; + + book.Clear(); + book.Close(); + book = null; + } + + + return ms; + } + + public static async Task ResponseExcelFromHtml(this ControllerBase controllerBase, string fileName, string html) + { + using (var ms = await GetStreamFromHtml(html)) + { + var bytes = ms.StreamToBytes(); + + var response = controllerBase.HttpContext.Response; + + response.ContentType = "application/octet-stream"; + response.Headers.Add("Content-Disposition", $"attachment; filename={fileName.UrlEncode()}"); + response.Headers.Add("X-Suggested-Filename", fileName.UrlEncode()); + response.Headers.Add("Content-Length", bytes.Length.ToString()); + + await response.Body.WriteAsync(bytes, 0, bytes.Length); + await response.Body.FlushAsync(); + } + } + /// + /// excel二进制文件转二维string结合 + /// + /// + /// + /// + public static List> ReadFromStream(Stream stream, int st = 0) + { + //根据路径通过已存在的excel来创建HSSFWorkbook,即整个excel文档 + HSSFWorkbook workbook; + try + { + workbook = new HSSFWorkbook(stream); + } + catch (Exception ex) + { + LogHelper.Error("ReadFromStream",ex); + throw new BusinessException("文件读取错误!"); + } + List> lis1 = new List>(); + { + //获取excel的第一个sheet + var sheet = workbook.GetSheetAt(st); + if (sheet == null) + throw new BusinessException("该文件内没有包含任何工作簿"); + //获取sheet的首行 + var headerRow = sheet.GetRow(0); + //一行最后一个方格的编号 即总的列数 + int cellCount = headerRow.LastCellNum; + + try + { + for (int i = 0; i <= sheet.LastRowNum; i++) + { + List lis = new List(); + var row = sheet.GetRow(i); + if (row == null) + continue; + for (int j = 0; j < cellCount; j++) + { + var cell = row.GetCell(j); + if (cell == null) + { + lis.Add(""); + continue; + } + try + { + switch (cell.CellType) + { + case NPOI.SS.UserModel.CellType.Unknown: + lis.Add("Unknown"); + break; + case NPOI.SS.UserModel.CellType.Numeric: + if (HSSFDateUtil.IsCellDateFormatted(cell))//对日期格式进行特殊对待 + lis.Add(HSSFDateUtil.GetJavaDate(cell.NumericCellValue).ToString()); + else + lis.Add(cell.NumericCellValue.ToString()); + break; + case NPOI.SS.UserModel.CellType.String: + lis.Add(cell.StringCellValue.ToString()); + break; + case NPOI.SS.UserModel.CellType.Formula: + lis.Add(cell.CellFormula.ToString()); + break; + case NPOI.SS.UserModel.CellType.Blank: + lis.Add(""); + break; + case NPOI.SS.UserModel.CellType.Boolean: + lis.Add(cell.BooleanCellValue.ToString()); + break; + case NPOI.SS.UserModel.CellType.Error: + lis.Add(cell.ErrorCellValue.ToString()); + break; + default: + break; + } + } + catch + { + lis.Add(""); + } + } + + //如果本行所有单元格都是空,就跳过本行 + if (lis.All(item => string.IsNullOrEmpty(item))) + continue; + + lis1.Add(lis); + } + } + catch(Exception ex) + { + LogHelper.Error("ReadFromStream", ex); + throw new BusinessException("文件格式错误!"); + } + } + #region 细节化处理 + lis1 = lis1.Where(s => !s.TrueForAll(f => string.IsNullOrWhiteSpace(f))).ToList();//去除全是空格的空行。 + lis1 = lis1.Select(s => s = s.Select(y => y = y.Trim()).ToList()).ToList();//去除空格 + if (lis1.Count == 1) throw new Exception("Excel中无有效数据!"); + #endregion + return lis1; + } + /// + /// 导出列表到excel + /// 导出到sheet的数据一致 + /// + /// + /// 每一个sheet的数据 + /// 列表的属性和名称值 + /// + public static byte[] ExportListToExcel(List> excelData, List excelTitle) + { + var workbook = new NPOI.XSSF.UserModel.XSSFWorkbook(); + + var entityType = typeof(T); + PropertyInfo[] entityProperties = entityType.GetProperties(); + + if (excelData == null || excelData.Count == 0) + { + return null; + } + try + { + foreach (var item in excelData) + { + #region MyRegion + //var sheet = workbook.CreateSheet(item.SheetName.Replace('/','-')); + //var titleRow = sheet.CreateRow(0); + //for (int i = 0; i < excelTitle.Count; i++) + //{ + // titleRow.CreateCell(i).SetCellValue(excelTitle[i].Title); + //} + //var sheetData = item.Data; + //for (int j = 0; j < sheetData.Count; j++) + //{ + // var dataRow = sheet.CreateRow(j + 1); + // for (int i = 0; i < excelTitle.Count; i++) + // { + // if (excelTitle[i].Property.ToUpper().Equals("ID")) + // { + // var num = j + 1; + // dataRow.CreateCell(i).SetCellValue(num); + // continue; + // } + // var entityProperty = entityProperties.FirstOrDefault(m => m.Name == excelTitle[i].Property); + + // var cellVal = entityProperty?.GetValue(sheetData[j]); + // if (cellVal?.GetType().Name == "DateTime") + // { + // dataRow.CreateCell(i).SetCellValue(((DateTime?)cellVal)?.ToString("yyyy/MM/dd HH:mm:ss")); + // } + // else + // { + // dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); + // } + // //dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); + // } + //} + #endregion + CreateSheetData(workbook, item, excelTitle, entityProperties); + } + using (var ms = new MemoryStream()) + { + workbook.Write(ms); + var bytes = ms.ToArray(); + return bytes; + } + } + catch(Exception ex) + { + LogHelper.Error("ExportListToExcel=>" + ex); + + throw ex; + } + finally + { + workbook.Clear(); + workbook.Close(); + workbook = null; + } + } + public static byte[] ExportListToExcel(ExcelData excelData, List excelTitle) + { + var workbook = new NPOI.XSSF.UserModel.XSSFWorkbook(); + + var entityType = typeof(T); + PropertyInfo[] entityProperties = entityType.GetProperties(); + + try + { + #region MyRegion + //var sheet = workbook.CreateSheet(excelData.SheetName.Replace('/', '-')); + //var titleRow = sheet.CreateRow(0); + //for (int i = 0; i < excelTitle.Count; i++) + //{ + // titleRow.CreateCell(i).SetCellValue(excelTitle[i].Title); + //} + //var sheetData = excelData.Data; + //for (int j = 0; j < sheetData.Count; j++) + //{ + // var dataRow = sheet.CreateRow(j + 1); + // for (int i = 0; i < excelTitle.Count; i++) + // { + // var cellVal = entityProperties.FirstOrDefault(m => m.Name == excelTitle[i].Property)?.GetValue(sheetData[j]); + + // if (cellVal?.GetType().Name == "DateTime") + // { + // dataRow.CreateCell(i).SetCellValue(((DateTime?)cellVal)?.ToString("yyyy/MM/dd HH:mm:ss")); + // } + // else + // { + // dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); + // } + // } + //} + #endregion + CreateSheetData(workbook, excelData, excelTitle, entityProperties); + using (var ms = new MemoryStream()) + { + workbook.Write(ms); + var bytes = ms.ToArray(); + return bytes; + } + } + catch (Exception ex) + { + LogHelper.Error("ExportListToExcel=>" + ex); + + throw ex; + } + finally + { + workbook.Clear(); + workbook.Close(); + workbook = null; + } + } + /// + /// 创建excel表单数据 + /// + /// + /// + /// + /// + /// + private static void CreateSheetData(NPOI.XSSF.UserModel.XSSFWorkbook workbook, + ExcelData excelData, List excelTitle,PropertyInfo[] entityProperties) + { + var sheet = workbook.CreateSheet(excelData.SheetName.Replace('/', '-')); + var titleRow = sheet.CreateRow(0); + for (int i = 0; i < excelTitle.Count; i++) + { + titleRow.CreateCell(i).SetCellValue(excelTitle[i].Title); + } + var sheetData = excelData.Data; + for (int j = 0; j < sheetData.Count; j++) + { + var dataRow = sheet.CreateRow(j + 1); + for (int i = 0; i < excelTitle.Count; i++) + { + var currentTitle = excelTitle[i]; + if (currentTitle.Property.Equals("序号")) + { + var num = j + 1; + dataRow.CreateCell(i).SetCellValue(num); + continue; + } + var cellVal = entityProperties.FirstOrDefault(m => m.Name == currentTitle.Property)?.GetValue(sheetData[j]); + if (currentTitle.Format != null) + { + cellVal = currentTitle.Format(cellVal); + } + if (currentTitle.Expr != null) + { + cellVal = currentTitle.Expr(sheetData[j]); + } + else if (cellVal?.GetType().Name == "DateTime") + { + dataRow.CreateCell(i).SetCellValue(((DateTime?)cellVal)?.ToString("yyyy/MM/dd HH:mm:ss")); + } + dataRow.CreateCell(i).SetCellValue(cellVal?.ToString()); + } + } + } + #region DownloadAsync(下载) + + + /// + /// 下载 + /// + /// 流 + /// 文件名,包含扩展名 + public static async Task DownloadAsync(this ControllerBase controllerBase, Stream stream, string fileName) + { + await DownloadAsync(controllerBase,stream, fileName, Encoding.UTF8); + } + /// + /// 下载 + /// + /// 流 + /// 文件名,包含扩展名 + /// 字符编码 + public static async Task DownloadAsync(this ControllerBase controllerBase, Stream stream, string fileName, Encoding encoding) + { + stream.Seek(0, SeekOrigin.Begin); + var buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + + await DownloadAsync(controllerBase, buffer, fileName, encoding); + } + /// + /// 下载 + /// + /// 字节流 + /// 文件名,包含扩展名 + public static async Task DownloadAsync(this ControllerBase controllerBase, byte[] bytes, string fileName) + { + await DownloadAsync(controllerBase,bytes, fileName, Encoding.UTF8); + } + + /// + /// 下载 + /// + /// 字节流 + /// 文件名,包含扩展名 + /// 字符编码 + public static async Task DownloadAsync(this ControllerBase controllerBase,byte[] bytes, string fileName, Encoding encoding) + { + var response = controllerBase.HttpContext.Response; + if (bytes == null || bytes.Length == 0) + return; + fileName = fileName.Replace(" ", ""); + fileName = HttpUtility.UrlEncode(fileName, encoding); + response.ContentType = "application/octet-stream"; + response.Headers.Add("Content-Disposition", $"attachment; filename={fileName}"); + response.Headers.Add("Content-Length", bytes.Length.ToString()); + response.Headers.Add("X-Suggested-Filename", fileName); + await response.Body.WriteAsync(bytes, 0, bytes.Length); + await response.Body.FlushAsync(); + } + + #endregion + } + + public class ExcelTitle + { + /// + /// 导出数据对应的 属性字段名 + /// + public string Property { get; set; } + /// + /// excel 的title + /// + public string Title { get; set; } + + public Func Format { get; set; } + public Func Expr { get; set; } + } + public class ExcelData + { + /// + /// 导出数据对应的 属性字段名 + /// + public string SheetName { get; set; } + /// + /// excel 的title + /// + public List Data { get; set; } + } + #region Excel导入验证 + /// + /// Excel导入验证 + /// + public static class ValidForExcel + { + + /// + /// 解析字符串到int32、double、datetime... + /// + /// + /// + /// + /// + /// + /// 自然数0,1,2,3... + /// + public static T ValidParseStr(string str,string message = "数据", bool isCanNullOrEmpty = true, bool isPositiveOrZero = true) + { + var val = default(T); + if (string.IsNullOrEmpty(str)) + { + if (isCanNullOrEmpty) + { + return default(T); + } + else + { + throw new Exception($"{message}不能为空"); + } + } + else + { + if (typeof(T).Name == typeof(int).Name || typeof(T).FullName == typeof(int?).FullName) + { + if (int.TryParse(str, out var valueInt)) + { + if (isPositiveOrZero && valueInt < 0) + { + goto error; + } + return (T)Convert.ChangeType(valueInt, TypeCode.Int32); + } + else + { + goto error; + } + } + if (typeof(T).Name == typeof(Decimal).Name || typeof(T).FullName == typeof(Decimal?).FullName) + { + if (Decimal.TryParse(str, out var valueInt)) + { + if (isPositiveOrZero && valueInt < 0) + { + goto error; + } + return (T)Convert.ChangeType(valueInt, TypeCode.Decimal); + } + else + { + goto error; + } + } + if (typeof(T).Name == typeof(Double).Name || typeof(T).FullName == typeof(Double?).FullName) + { + if (Double.TryParse(str, out var valueInt)) + { + if (isPositiveOrZero && valueInt < 0) + { + goto error; + } + return (T)Convert.ChangeType(valueInt, TypeCode.Double); + } + else + { + goto error; + } + } + if (typeof(T).Name == typeof(Single).Name || typeof(T).FullName == typeof(Single?).FullName) + { + if (Single.TryParse(str, out var valueInt)) + { + if (isPositiveOrZero && valueInt < 0) + { + goto error; + } + return (T)Convert.ChangeType(valueInt, TypeCode.Single); + } + else + { + goto error; + } + } + if (typeof(T).Name == typeof(DateTime).Name || typeof(T).FullName == typeof(DateTime?).FullName) + { + if (DateTime.TryParse(str, out var valueInt)) + { + return (T)Convert.ChangeType(valueInt, TypeCode.DateTime); + } + else + { + goto error; + } + } + } + return val; + error: throw new Exception($"{message}的值有误"); + } + + /// + /// 提供正则、提示信息,返回验证结果 + /// + /// + /// + /// + /// + /// + /// + public static string ValidByPattern(string str,string pattern, string message = "数据", bool isCanNullOrEmpty = true) + { + if (string.IsNullOrEmpty(str)) + { + if (isCanNullOrEmpty) + { + return str; + } + else + { + throw new BusinessException($"{message}不能为空"); + } + } + else + { + if (!Regex.IsMatch(str, pattern)) throw new BusinessException($"{message}输入有误"); + return str; + } + } + /// + /// 根据枚举判断 + /// + /// + /// + /// + /// + /// + public static int? ValidByEnum(string str, string message, bool isCanNullOrEmpty = true) //where T:Enum + { + + if (string.IsNullOrEmpty(str)) + { + if (isCanNullOrEmpty) + { + return null; + } + else + { + throw new BusinessException($"{message}不能为空"); + } + } + else + { + var value = EnumExtension.EnumToList().Find(s => s.Name == str)?.Value; + if (value == null) throw new BusinessException($"未知的数据:{str}"); + return Convert.ToInt32(value); + } + } + } + #endregion } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/HttpHelp.cs b/Infrastructure/Hncore.Infrastructure/Common/HttpHelp.cs index 1aae178..2bf7a81 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/HttpHelp.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/HttpHelp.cs @@ -1,69 +1,69 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Text; - -namespace Hncore.Infrastructure.Common -{ - public static class HttpHelp - { - /// - /// get请求 - /// - /// - /// - public static string HttpGet(string url) - { - string result = string.Empty; - try - { - HttpWebRequest wbRequest = (HttpWebRequest)WebRequest.Create(url); - wbRequest.Method = "GET"; - HttpWebResponse wbResponse = (HttpWebResponse)wbRequest.GetResponse(); - using (Stream responseStream = wbResponse.GetResponseStream()) - { - using (StreamReader sReader = new StreamReader(responseStream)) - { - result = sReader.ReadToEnd(); - } - } - } - catch/* (Exception ex) 此处暂时屏蔽掉了,要不然编译光弹出变量未使用的警告,谁用得着再打开*/ - { - - } - return result; - } - /// - /// get 请求,带token - /// - /// - /// - /// - public static string HttpGet(string token,string url) - { - string result = string.Empty; - try - { - HttpWebRequest wbRequest = (HttpWebRequest)WebRequest.Create(url); - wbRequest.Headers.Add("token", token); - wbRequest.Method = "GET"; - HttpWebResponse wbResponse = (HttpWebResponse)wbRequest.GetResponse(); - using (Stream responseStream = wbResponse.GetResponseStream()) - { - using (StreamReader sReader = new StreamReader(responseStream)) - { - result = sReader.ReadToEnd(); - } - } - } - catch/* (Exception ex) 此处暂时屏蔽掉了,要不然编译光弹出变量未使用的警告,谁用得着再打开*/ - { - - } - return result; - } - - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; + +namespace Hncore.Infrastructure.Common +{ + public static class HttpHelp + { + /// + /// get请求 + /// + /// + /// + public static string HttpGet(string url) + { + string result = string.Empty; + try + { + HttpWebRequest wbRequest = (HttpWebRequest)WebRequest.Create(url); + wbRequest.Method = "GET"; + HttpWebResponse wbResponse = (HttpWebResponse)wbRequest.GetResponse(); + using (Stream responseStream = wbResponse.GetResponseStream()) + { + using (StreamReader sReader = new StreamReader(responseStream)) + { + result = sReader.ReadToEnd(); + } + } + } + catch/* (Exception ex) 此处暂时屏蔽掉了,要不然编译光弹出变量未使用的警告,谁用得着再打开*/ + { + + } + return result; + } + /// + /// get 请求,带token + /// + /// + /// + /// + public static string HttpGet(string token,string url) + { + string result = string.Empty; + try + { + HttpWebRequest wbRequest = (HttpWebRequest)WebRequest.Create(url); + wbRequest.Headers.Add("token", token); + wbRequest.Method = "GET"; + HttpWebResponse wbResponse = (HttpWebResponse)wbRequest.GetResponse(); + using (Stream responseStream = wbResponse.GetResponseStream()) + { + using (StreamReader sReader = new StreamReader(responseStream)) + { + result = sReader.ReadToEnd(); + } + } + } + catch/* (Exception ex) 此处暂时屏蔽掉了,要不然编译光弹出变量未使用的警告,谁用得着再打开*/ + { + + } + return result; + } + + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Common/ImageCloudHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/ImageCloudHelper.cs index f205c28..a4d62af 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/ImageCloudHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/ImageCloudHelper.cs @@ -1,85 +1,85 @@ -using System; -using System.IO; -using Qiniu.Storage; -using Qiniu.Util; - -namespace Hncore.Infrastructure.Common -{ - public class ImageCloudHelper - { - /// - /// - /// - /// - /// 图片地址 - public static string UploadImage(Stream stream, string prefix) - { - //todo - return ""; - Mac mac = new Mac("3bmkJLD-inSGpQnLr_9UlommFT81B5L0ryesJLhS", "X22vza-l53jcZyi_fmaex88R065_Ip2_3j5Im0Se"); - - string bucket = "property"; - - // 上传策略,参见 - // https://developer.qiniu.com/kodo/manual/put-policy - PutPolicy putPolicy = new PutPolicy(); - // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" - // putPolicy.Scope = bucket + ":" + saveKey; - putPolicy.Scope = bucket; - // 上传策略有效期(对应于生成的凭证的有效期) - putPolicy.SetExpires(3600); - - string jstr = putPolicy.ToJsonString(); - string token = Auth.CreateUploadToken(mac, jstr); - - FormUploader fu = new FormUploader(new Config() - { - Zone = Zone.ZONE_CN_East - }); - - - string fileName = prefix + Guid.NewGuid() + ".jpg"; - - var result = fu.UploadStream(stream, fileName, token, null); - - if (result.Code == 200) - { - return "http://propertyimages.etor.vip/" + fileName; - } - - - return null; - } - - public static string UploadImage(string imageBase64, string prefix) - { - byte[] imageByte = Convert.FromBase64String(imageBase64); - - var stream = new MemoryStream(imageByte); - - return UploadImage(stream, prefix); - } - - public static string GetToken(int expireInSeconds=3600) - { - Mac mac = new Mac("3bmkJLD-inSGpQnLr_9UlommFT81B5L0ryesJLhS", "X22vza-l53jcZyi_fmaex88R065_Ip2_3j5Im0Se"); - - string bucket = "property"; - - // 上传策略,参见 - // https://developer.qiniu.com/kodo/manual/put-policy - PutPolicy putPolicy = new PutPolicy(); - // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" - // putPolicy.Scope = bucket + ":" + saveKey; - putPolicy.Scope = bucket; - // 上传策略有效期(对应于生成的凭证的有效期) - putPolicy.SetExpires(expireInSeconds); - putPolicy.ReturnBody = "{\"key\":$(key),\"hash\":$(etag),\"mimeType\":$(mimeType),\"fname\":$(fname),\"fsize\":$(fsize),\"avinfo\":$(avinfo),\"ext\":$(ext),\"imageInfo\":$(imageInfo)}"; - - string jstr = putPolicy.ToJsonString(); - string token = Auth.CreateUploadToken(mac, jstr); - - return token; - } - } +using System; +using System.IO; +using Qiniu.Storage; +using Qiniu.Util; + +namespace Hncore.Infrastructure.Common +{ + public class ImageCloudHelper + { + /// + /// + /// + /// + /// 图片地址 + public static string UploadImage(Stream stream, string prefix) + { + //todo + return ""; + Mac mac = new Mac("3bmkJLD-inSGpQnLr_9UlommFT81B5L0ryesJLhS", "X22vza-l53jcZyi_fmaex88R065_Ip2_3j5Im0Se"); + + string bucket = "property"; + + // 上传策略,参见 + // https://developer.qiniu.com/kodo/manual/put-policy + PutPolicy putPolicy = new PutPolicy(); + // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" + // putPolicy.Scope = bucket + ":" + saveKey; + putPolicy.Scope = bucket; + // 上传策略有效期(对应于生成的凭证的有效期) + putPolicy.SetExpires(3600); + + string jstr = putPolicy.ToJsonString(); + string token = Auth.CreateUploadToken(mac, jstr); + + FormUploader fu = new FormUploader(new Config() + { + Zone = Zone.ZONE_CN_East + }); + + + string fileName = prefix + Guid.NewGuid() + ".jpg"; + + var result = fu.UploadStream(stream, fileName, token, null); + + if (result.Code == 200) + { + return "http://propertyimages.etor.vip/" + fileName; + } + + + return null; + } + + public static string UploadImage(string imageBase64, string prefix) + { + byte[] imageByte = Convert.FromBase64String(imageBase64); + + var stream = new MemoryStream(imageByte); + + return UploadImage(stream, prefix); + } + + public static string GetToken(int expireInSeconds=3600) + { + Mac mac = new Mac("3bmkJLD-inSGpQnLr_9UlommFT81B5L0ryesJLhS", "X22vza-l53jcZyi_fmaex88R065_Ip2_3j5Im0Se"); + + string bucket = "property"; + + // 上传策略,参见 + // https://developer.qiniu.com/kodo/manual/put-policy + PutPolicy putPolicy = new PutPolicy(); + // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" + // putPolicy.Scope = bucket + ":" + saveKey; + putPolicy.Scope = bucket; + // 上传策略有效期(对应于生成的凭证的有效期) + putPolicy.SetExpires(expireInSeconds); + putPolicy.ReturnBody = "{\"key\":$(key),\"hash\":$(etag),\"mimeType\":$(mimeType),\"fname\":$(fname),\"fsize\":$(fsize),\"avinfo\":$(avinfo),\"ext\":$(ext),\"imageInfo\":$(imageInfo)}"; + + string jstr = putPolicy.ToJsonString(); + string token = Auth.CreateUploadToken(mac, jstr); + + return token; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/IoHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/IoHelper.cs index 4042085..a1a6014 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/IoHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/IoHelper.cs @@ -1,40 +1,40 @@ -using System.IO; - -namespace Hncore.Infrastructure.Common -{ - /// - /// - /// - public static class IoHelper - { - #region 数据流转字节数组 - - /// - /// 数据流转字节数组 - /// - /// - /// - public static byte[] StreamToBytes(this Stream stream) - { - byte[] bytes = new byte[stream.Length]; - stream.Read(bytes, 0, bytes.Length); - // 设置当前流的位置为流的开始 - stream.Seek(0, SeekOrigin.Begin); - - return bytes; - } - - #endregion - - #region 将 byte[] 转成 Stream - - public static Stream BytesToStream(this byte[] bytes) - - { - Stream stream = new MemoryStream(bytes); - return stream; - } - - #endregion - } +using System.IO; + +namespace Hncore.Infrastructure.Common +{ + /// + /// + /// + public static class IoHelper + { + #region 数据流转字节数组 + + /// + /// 数据流转字节数组 + /// + /// + /// + public static byte[] StreamToBytes(this Stream stream) + { + byte[] bytes = new byte[stream.Length]; + stream.Read(bytes, 0, bytes.Length); + // 设置当前流的位置为流的开始 + stream.Seek(0, SeekOrigin.Begin); + + return bytes; + } + + #endregion + + #region 将 byte[] 转成 Stream + + public static Stream BytesToStream(this byte[] bytes) + + { + Stream stream = new MemoryStream(bytes); + return stream; + } + + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/ListHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/ListHelper.cs index f6d2fd6..27c7f42 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/ListHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/ListHelper.cs @@ -1,138 +1,138 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Reflection; - -namespace Hncore.Infrastructure.Common -{ - public static class ListHelper - { - #region 字符串转化为泛型集合 - - /// - /// 字符串转化为泛型集合 - /// - /// 字符串 - /// 要分割的字符 - /// - public static List StrToList(string str, char splitstr) - { - List list = new List(); - if (string.IsNullOrEmpty(str)) - { - return list; - } - if (!str.Contains(splitstr)) - { - list.Add((T)Convert.ChangeType(str, typeof(T))); - return list; - } - else - { - string[] strarray = str.Split(splitstr); - - foreach (string s in strarray) - { - if (s != "") - list.Add((T)Convert.ChangeType(s, typeof(T))); - } - return list; - } - } - - /// - /// 字符串转化为泛型集合 - /// - /// 字符串 - /// - public static List StrToList(string str) - { - return StrToList(str, ','); - } - - #endregion - - public static string ListToStr(List list, string splitstr=",") - { - string str = ""; - - list.ForEach(t => - { - str += t + splitstr; - }); - - if (str.EndsWith(splitstr)) - { - str = str.Substring(0, str.Length - splitstr.Length); - } - - return str; - } - - #region 转换几个中所有元素的类型 - - /// - /// 转换几个中所有元素的类型 - /// - /// - /// - /// - public static List ConvertListType(List list) - { - if (list == null) - { - return null; - } - List newlist = new List(); - foreach (T t in list) - { - object to = new object(); - if (typeof(To).Name == "Guid") - { - to = Guid.Parse(t.ToString()); - } - else - { - to = Convert.ChangeType(t, typeof(To)); - } - newlist.Add((To)to); - } - return newlist; - } - - #endregion - - #region 转化一个DataTable - - /// - /// 转化一个DataTable - /// - /// - /// - /// - public static DataTable ToDataTable(IEnumerable list) - { - //创建属性的集合 - List pList = new List(); - //获得反射的入口 - Type type = typeof(T); - DataTable dt = new DataTable(); - //把所有的public属性加入到集合 并添加DataTable的列 - Array.ForEach(type.GetProperties(), p => { pList.Add(p); dt.Columns.Add(p.Name, p.PropertyType); }); - foreach (var item in list) - { - //创建一个DataRow实例 - DataRow row = dt.NewRow(); - //给row 赋值 - pList.ForEach(p => row[p.Name] = p.GetValue(item, null)); - //加入到DataTable - dt.Rows.Add(row); - } - return dt; - } - - #endregion - - } -} +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection; + +namespace Hncore.Infrastructure.Common +{ + public static class ListHelper + { + #region 字符串转化为泛型集合 + + /// + /// 字符串转化为泛型集合 + /// + /// 字符串 + /// 要分割的字符 + /// + public static List StrToList(string str, char splitstr) + { + List list = new List(); + if (string.IsNullOrEmpty(str)) + { + return list; + } + if (!str.Contains(splitstr)) + { + list.Add((T)Convert.ChangeType(str, typeof(T))); + return list; + } + else + { + string[] strarray = str.Split(splitstr); + + foreach (string s in strarray) + { + if (s != "") + list.Add((T)Convert.ChangeType(s, typeof(T))); + } + return list; + } + } + + /// + /// 字符串转化为泛型集合 + /// + /// 字符串 + /// + public static List StrToList(string str) + { + return StrToList(str, ','); + } + + #endregion + + public static string ListToStr(List list, string splitstr=",") + { + string str = ""; + + list.ForEach(t => + { + str += t + splitstr; + }); + + if (str.EndsWith(splitstr)) + { + str = str.Substring(0, str.Length - splitstr.Length); + } + + return str; + } + + #region 转换几个中所有元素的类型 + + /// + /// 转换几个中所有元素的类型 + /// + /// + /// + /// + public static List ConvertListType(List list) + { + if (list == null) + { + return null; + } + List newlist = new List(); + foreach (T t in list) + { + object to = new object(); + if (typeof(To).Name == "Guid") + { + to = Guid.Parse(t.ToString()); + } + else + { + to = Convert.ChangeType(t, typeof(To)); + } + newlist.Add((To)to); + } + return newlist; + } + + #endregion + + #region 转化一个DataTable + + /// + /// 转化一个DataTable + /// + /// + /// + /// + public static DataTable ToDataTable(IEnumerable list) + { + //创建属性的集合 + List pList = new List(); + //获得反射的入口 + Type type = typeof(T); + DataTable dt = new DataTable(); + //把所有的public属性加入到集合 并添加DataTable的列 + Array.ForEach(type.GetProperties(), p => { pList.Add(p); dt.Columns.Add(p.Name, p.PropertyType); }); + foreach (var item in list) + { + //创建一个DataRow实例 + DataRow row = dt.NewRow(); + //给row 赋值 + pList.ForEach(p => row[p.Name] = p.GetValue(item, null)); + //加入到DataTable + dt.Rows.Add(row); + } + return dt; + } + + #endregion + + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Common/LogHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/LogHelper.cs index d1f098e..0870dcb 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/LogHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/LogHelper.cs @@ -1,59 +1,59 @@ -using System; -using NLog; - -namespace Hncore.Infrastructure.Common -{ - public class LogHelper - { - private static readonly Logger Log = LogManager.GetLogger("UserLog"); - private static string assName = AppDomain.CurrentDomain.FriendlyName; - - private static string FormatMsg(string title, object msg) - { - return "Assembly:" + assName + "\r\nTitle : " + title + "\r\nMessage : " + msg + "\r\n"; - } - - public static void Error(string title, object msg = null) - { - Log?.Error(FormatMsg(title, msg)); - - Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); - } - - public static void Debug(string title, object msg = null) - { - Log?.Debug(FormatMsg(title, msg)); - - Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); - } - - public static void Info(string title, object msg = null) - { - Log?.Info(FormatMsg(title, msg)); - - Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); - } - - public static void Warn(string title, object msg = null) - { - Log?.Warn(FormatMsg(title, msg)); - - Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); - } - - public static void Trace(string title, object msg = null) - { - Log?.Trace(FormatMsg(title, msg)); - - Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); - } - - public static void Fatal(string title, object msg = null) - { - Log?.Fatal(FormatMsg(title, msg)); - - Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); - } - } - +using System; +using NLog; + +namespace Hncore.Infrastructure.Common +{ + public class LogHelper + { + private static readonly Logger Log = LogManager.GetLogger("UserLog"); + private static string assName = AppDomain.CurrentDomain.FriendlyName; + + private static string FormatMsg(string title, object msg) + { + return "Assembly:" + assName + "\r\nTitle : " + title + "\r\nMessage : " + msg + "\r\n"; + } + + public static void Error(string title, object msg = null) + { + Log?.Error(FormatMsg(title, msg)); + + Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); + } + + public static void Debug(string title, object msg = null) + { + Log?.Debug(FormatMsg(title, msg)); + + Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); + } + + public static void Info(string title, object msg = null) + { + Log?.Info(FormatMsg(title, msg)); + + Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); + } + + public static void Warn(string title, object msg = null) + { + Log?.Warn(FormatMsg(title, msg)); + + Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); + } + + public static void Trace(string title, object msg = null) + { + Log?.Trace(FormatMsg(title, msg)); + + Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); + } + + public static void Fatal(string title, object msg = null) + { + Log?.Fatal(FormatMsg(title, msg)); + + Console.WriteLine(DateTime.Now+"\r\n"+FormatMsg(title, msg)); + } + } + } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/MySqlHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/MySqlHelper.cs index 10091c9..43c268f 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/MySqlHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/MySqlHelper.cs @@ -1,29 +1,29 @@ -using System.Threading.Tasks; -using Dapper; -using MySql.Data.MySqlClient; - -namespace Hncore.Infrastructure.Common -{ - public class MySqlHelper - { - public static int Execute(string connStr, string sql, object param = null) - { - using (var conn = new MySqlConnection(connStr)) - { - conn.Open(); - - return conn.Execute(sql, param); - } - } - - public static async Task ExecuteAsync(string connStr, string sql, object param = null) - { - using (var conn = new MySqlConnection(connStr)) - { - conn.Open(); - - return await conn.ExecuteAsync(sql, param); - } - } - } +using System.Threading.Tasks; +using Dapper; +using MySql.Data.MySqlClient; + +namespace Hncore.Infrastructure.Common +{ + public class MySqlHelper + { + public static int Execute(string connStr, string sql, object param = null) + { + using (var conn = new MySqlConnection(connStr)) + { + conn.Open(); + + return conn.Execute(sql, param); + } + } + + public static async Task ExecuteAsync(string connStr, string sql, object param = null) + { + using (var conn = new MySqlConnection(connStr)) + { + conn.Open(); + + return await conn.ExecuteAsync(sql, param); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/NetworkHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/NetworkHelper.cs index 30c3af8..b3d3f05 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/NetworkHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/NetworkHelper.cs @@ -1,21 +1,21 @@ -using System.Linq; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; - -namespace Hncore.Infrastructure.Common -{ - public class NetworkHelper - { - public static string GetPublicIp() - { - return NetworkInterface - .GetAllNetworkInterfaces() - .Select(p => p.GetIPProperties()) - .SelectMany(p => p.UnicastAddresses) - .FirstOrDefault(p => - p.Address.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(p.Address))?.Address - .ToString(); - } - } +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace Hncore.Infrastructure.Common +{ + public class NetworkHelper + { + public static string GetPublicIp() + { + return NetworkInterface + .GetAllNetworkInterfaces() + .Select(p => p.GetIPProperties()) + .SelectMany(p => p.UnicastAddresses) + .FirstOrDefault(p => + p.Address.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(p.Address))?.Address + .ToString(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/QiNiuCloudHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/QiNiuCloudHelper.cs index 8a74556..710835a 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/QiNiuCloudHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/QiNiuCloudHelper.cs @@ -1,119 +1,119 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Hncore.Infrastructure.Extension; -using Qiniu.Http; -using Qiniu.Storage; -using Qiniu.Util; - -namespace Hncore.Infrastructure.Common -{ - public class QiNiuCloudHelper - { - static Mac mac = new Mac("3bmkJLD-inSGpQnLr_9UlommFT81B5L0ryesJLhS", - "X22vza-l53jcZyi_fmaex88R065_Ip2_3j5Im0Se"); - - static string bucket = "property"; - - /// - /// - /// - /// - /// 图片地址 - public static string UploadImage(Stream stream, string prefix, string persistentOps = "") - { - string fileName = prefix + Guid.NewGuid() + ".jpg"; - - // 上传策略,参见 - // https://developer.qiniu.com/kodo/manual/put-policy - PutPolicy putPolicy = new PutPolicy(); - // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" - // putPolicy.Scope = bucket + ":" + saveKey; - putPolicy.Scope = bucket; - // 上传策略有效期(对应于生成的凭证的有效期) - putPolicy.SetExpires(3600); - - if (!string.IsNullOrEmpty(persistentOps)) - { - string saveAs = (bucket + ":" + fileName).ToBase64String() - .Replace("+", "-") - .Replace("/", "_"); - putPolicy.PersistentOps = persistentOps + $"|saveas/{saveAs}"; - putPolicy.PersistentPipeline = "face_image"; - } - - - string jstr = putPolicy.ToJsonString(); - string token = Auth.CreateUploadToken(mac, jstr); - - ResumableUploader fu = new ResumableUploader(new Config() - { - Zone = Zone.ZONE_CN_East - }); - - - var result = fu.UploadStream(stream, fileName, token, null); - - if (result.Code == 200) - { - return "http://propertyimages.etor.vip/" + fileName; - } - - LogHelper.Error("七牛上传图片失败", result.ToString()); - - return null; - } - - public static string UploadImage(string imageBase64, string prefix, string persistentOps = "") - { - byte[] imageByte = Convert.FromBase64String(imageBase64); - - var stream = new MemoryStream(imageByte); - - return UploadImage(stream, prefix, persistentOps); - } - - /// - /// 删除资源 - /// - /// - public static void Delete(string key) - { - Config config = new Config(); - config.Zone = Zone.ZONE_CN_East; - - BucketManager bucketManager = new BucketManager(mac, config); - - var res = bucketManager.Delete(bucket, key); - } - - public static List Domains(string bucket) - { - Config config = new Config(); - config.Zone = Zone.ZONE_CN_East; - - BucketManager bucketManager = new BucketManager(mac, config); - - return bucketManager.Domains("property").Result; - } - - public static string GetToken(int expireInSeconds = 3600) - { - // 上传策略,参见 - // https://developer.qiniu.com/kodo/manual/put-policy - PutPolicy putPolicy = new PutPolicy(); - // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" - // putPolicy.Scope = bucket + ":" + saveKey; - putPolicy.Scope = bucket; - // 上传策略有效期(对应于生成的凭证的有效期) - putPolicy.SetExpires(expireInSeconds); - putPolicy.ReturnBody = - "{\"key\":$(key),\"hash\":$(etag),\"mimeType\":$(mimeType),\"fname\":$(fname),\"fsize\":$(fsize),\"avinfo\":$(avinfo),\"ext\":$(ext),\"imageInfo\":$(imageInfo)}"; - - string jstr = putPolicy.ToJsonString(); - string token = Auth.CreateUploadToken(mac, jstr); - - return token; - } - } +using System; +using System.Collections.Generic; +using System.IO; +using Hncore.Infrastructure.Extension; +using Qiniu.Http; +using Qiniu.Storage; +using Qiniu.Util; + +namespace Hncore.Infrastructure.Common +{ + public class QiNiuCloudHelper + { + static Mac mac = new Mac("3bmkJLD-inSGpQnLr_9UlommFT81B5L0ryesJLhS", + "X22vza-l53jcZyi_fmaex88R065_Ip2_3j5Im0Se"); + + static string bucket = "property"; + + /// + /// + /// + /// + /// 图片地址 + public static string UploadImage(Stream stream, string prefix, string persistentOps = "") + { + string fileName = prefix + Guid.NewGuid() + ".jpg"; + + // 上传策略,参见 + // https://developer.qiniu.com/kodo/manual/put-policy + PutPolicy putPolicy = new PutPolicy(); + // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" + // putPolicy.Scope = bucket + ":" + saveKey; + putPolicy.Scope = bucket; + // 上传策略有效期(对应于生成的凭证的有效期) + putPolicy.SetExpires(3600); + + if (!string.IsNullOrEmpty(persistentOps)) + { + string saveAs = (bucket + ":" + fileName).ToBase64String() + .Replace("+", "-") + .Replace("/", "_"); + putPolicy.PersistentOps = persistentOps + $"|saveas/{saveAs}"; + putPolicy.PersistentPipeline = "face_image"; + } + + + string jstr = putPolicy.ToJsonString(); + string token = Auth.CreateUploadToken(mac, jstr); + + ResumableUploader fu = new ResumableUploader(new Config() + { + Zone = Zone.ZONE_CN_East + }); + + + var result = fu.UploadStream(stream, fileName, token, null); + + if (result.Code == 200) + { + return "http://propertyimages.etor.vip/" + fileName; + } + + LogHelper.Error("七牛上传图片失败", result.ToString()); + + return null; + } + + public static string UploadImage(string imageBase64, string prefix, string persistentOps = "") + { + byte[] imageByte = Convert.FromBase64String(imageBase64); + + var stream = new MemoryStream(imageByte); + + return UploadImage(stream, prefix, persistentOps); + } + + /// + /// 删除资源 + /// + /// + public static void Delete(string key) + { + Config config = new Config(); + config.Zone = Zone.ZONE_CN_East; + + BucketManager bucketManager = new BucketManager(mac, config); + + var res = bucketManager.Delete(bucket, key); + } + + public static List Domains(string bucket) + { + Config config = new Config(); + config.Zone = Zone.ZONE_CN_East; + + BucketManager bucketManager = new BucketManager(mac, config); + + return bucketManager.Domains("property").Result; + } + + public static string GetToken(int expireInSeconds = 3600) + { + // 上传策略,参见 + // https://developer.qiniu.com/kodo/manual/put-policy + PutPolicy putPolicy = new PutPolicy(); + // 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY" + // putPolicy.Scope = bucket + ":" + saveKey; + putPolicy.Scope = bucket; + // 上传策略有效期(对应于生成的凭证的有效期) + putPolicy.SetExpires(expireInSeconds); + putPolicy.ReturnBody = + "{\"key\":$(key),\"hash\":$(etag),\"mimeType\":$(mimeType),\"fname\":$(fname),\"fsize\":$(fsize),\"avinfo\":$(avinfo),\"ext\":$(ext),\"imageInfo\":$(imageInfo)}"; + + string jstr = putPolicy.ToJsonString(); + string token = Auth.CreateUploadToken(mac, jstr); + + return token; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/RSAHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/RSAHelper.cs index ed2e840..2b5a584 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/RSAHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/RSAHelper.cs @@ -1,333 +1,333 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace Hncore.Infrastructure.Common -{ - /// - /// RSA加解密 使用OpenSSL的公钥加密/私钥解密 - /// - /// 公私钥请使用openssl生成 ssh-keygen -t rsa 命令生成的公钥私钥是不行的 - /// - public class RsaHelper - { - private readonly RSA _privateKeyRsaProvider; - private readonly RSA _publicKeyRsaProvider; - private readonly HashAlgorithmName _hashAlgorithmName; - private readonly Encoding _encoding; - - /// - /// 实例化RSAHelper - /// - /// 加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048 - /// 编码类型 - /// 私钥 - /// 公钥 - public RsaHelper(RsaType rsaType, Encoding encoding, string privateKey, string publicKey = null) - { - _encoding = encoding; - if (!string.IsNullOrEmpty(privateKey)) - { - _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey); - } - - if (!string.IsNullOrEmpty(publicKey)) - { - _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey); - } - - _hashAlgorithmName = rsaType == RsaType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256; - } - - #region 使用私钥签名 - - /// - /// 使用私钥签名 - /// - /// 原始数据 - /// - public string Sign(string data) - { - byte[] dataBytes = _encoding.GetBytes(data); - - var signatureBytes = - _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); - - return Convert.ToBase64String(signatureBytes); - } - - #endregion - - #region 使用公钥验证签名 - - /// - /// 使用公钥验证签名 - /// - /// 原始数据 - /// 签名 - /// - public bool Verify(string data, string sign) - { - byte[] dataBytes = _encoding.GetBytes(data); - byte[] signBytes = Convert.FromBase64String(sign); - - var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, - RSASignaturePadding.Pkcs1); - - return verify; - } - - #endregion - - #region 解密 - - public string Decrypt(string cipherText) - { - if (_privateKeyRsaProvider == null) - { - throw new Exception("_privateKeyRsaProvider is null"); - } - - return Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), - RSAEncryptionPadding.Pkcs1)); - } - - #endregion - - #region 加密 - - public string Encrypt(string text) - { - if (_publicKeyRsaProvider == null) - { - throw new Exception("_publicKeyRsaProvider is null"); - } - - return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), - RSAEncryptionPadding.Pkcs1)); - } - - #endregion - - #region 使用私钥创建RSA实例 - - public RSA CreateRsaProviderFromPrivateKey(string privateKey) - { - var privateKeyBits = Convert.FromBase64String(privateKey); - - var rsa = RSA.Create(); - var rsaParameters = new RSAParameters(); - - using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))) - { - byte bt = 0; - ushort twobytes = 0; - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) - binr.ReadByte(); - else if (twobytes == 0x8230) - binr.ReadInt16(); - else - throw new Exception("Unexpected value read binr.ReadUInt16()"); - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) - throw new Exception("Unexpected version"); - - bt = binr.ReadByte(); - if (bt != 0x00) - throw new Exception("Unexpected value read binr.ReadByte()"); - - rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr)); - } - - rsa.ImportParameters(rsaParameters); - return rsa; - } - - #endregion - - #region 使用公钥创建RSA实例 - - public RSA CreateRsaProviderFromPublicKey(string publicKeyString) - { - // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" - byte[] seqOid = - {0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00}; - byte[] seq = new byte[15]; - - var x509Key = Convert.FromBase64String(publicKeyString); - - // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ - using (MemoryStream mem = new MemoryStream(x509Key)) - { - using (BinaryReader binr = new BinaryReader(mem) - ) //wrap Memory Stream with BinaryReader for easy reading - { - byte bt = 0; - ushort twobytes = 0; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130 - ) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - seq = binr.ReadBytes(15); //read the Sequence OID - if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8103 - ) //data read as little endian order (actual data order for Bit String is 03 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8203) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - bt = binr.ReadByte(); - if (bt != 0x00) //expect null byte next - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130 - ) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - byte lowbyte = 0x00; - byte highbyte = 0x00; - - if (twobytes == 0x8102 - ) //data read as little endian order (actual data order for Integer is 02 81) - lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus - else if (twobytes == 0x8202) - { - highbyte = binr.ReadByte(); //advance 2 bytes - lowbyte = binr.ReadByte(); - } - else - return null; - - byte[] modint = - {lowbyte, highbyte, 0x00, 0x00}; //reverse byte order since asn.1 key uses big endian order - int modsize = BitConverter.ToInt32(modint, 0); - - int firstbyte = binr.PeekChar(); - if (firstbyte == 0x00) - { - //if first byte (highest order) of modulus is zero, don't include it - binr.ReadByte(); //skip this null byte - modsize -= 1; //reduce modulus buffer size by 1 - } - - byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes - - if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data - return null; - int expbytes = - binr - .ReadByte(); // should only need one byte for actual exponent data (for all useful values) - byte[] exponent = binr.ReadBytes(expbytes); - - // ------- create RSACryptoServiceProvider instance and initialize with public key ----- - var rsa = RSA.Create(); - RSAParameters rsaKeyInfo = new RSAParameters - { - Modulus = modulus, - Exponent = exponent - }; - rsa.ImportParameters(rsaKeyInfo); - - return rsa; - } - } - } - - #endregion - - #region 导入密钥算法 - - private int GetIntegerSize(BinaryReader binr) - { - byte bt = 0; - int count = 0; - bt = binr.ReadByte(); - if (bt != 0x02) - return 0; - bt = binr.ReadByte(); - - if (bt == 0x81) - count = binr.ReadByte(); - else if (bt == 0x82) - { - var highbyte = binr.ReadByte(); - var lowbyte = binr.ReadByte(); - byte[] modint = {lowbyte, highbyte, 0x00, 0x00}; - count = BitConverter.ToInt32(modint, 0); - } - else - { - count = bt; - } - - while (binr.ReadByte() == 0x00) - { - count -= 1; - } - - binr.BaseStream.Seek(-1, SeekOrigin.Current); - return count; - } - - private bool CompareBytearrays(byte[] a, byte[] b) - { - if (a.Length != b.Length) - return false; - int i = 0; - foreach (byte c in a) - { - if (c != b[i]) - return false; - i++; - } - - return true; - } - - #endregion - } - - /// - /// RSA算法类型 - /// - public enum RsaType - { - /// - /// SHA1 - /// - RSA = 0, - - /// - /// RSA2 密钥长度至少为2048 - /// SHA256 - /// - RSA2 - } +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Hncore.Infrastructure.Common +{ + /// + /// RSA加解密 使用OpenSSL的公钥加密/私钥解密 + /// + /// 公私钥请使用openssl生成 ssh-keygen -t rsa 命令生成的公钥私钥是不行的 + /// + public class RsaHelper + { + private readonly RSA _privateKeyRsaProvider; + private readonly RSA _publicKeyRsaProvider; + private readonly HashAlgorithmName _hashAlgorithmName; + private readonly Encoding _encoding; + + /// + /// 实例化RSAHelper + /// + /// 加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048 + /// 编码类型 + /// 私钥 + /// 公钥 + public RsaHelper(RsaType rsaType, Encoding encoding, string privateKey, string publicKey = null) + { + _encoding = encoding; + if (!string.IsNullOrEmpty(privateKey)) + { + _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey); + } + + if (!string.IsNullOrEmpty(publicKey)) + { + _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey); + } + + _hashAlgorithmName = rsaType == RsaType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256; + } + + #region 使用私钥签名 + + /// + /// 使用私钥签名 + /// + /// 原始数据 + /// + public string Sign(string data) + { + byte[] dataBytes = _encoding.GetBytes(data); + + var signatureBytes = + _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); + + return Convert.ToBase64String(signatureBytes); + } + + #endregion + + #region 使用公钥验证签名 + + /// + /// 使用公钥验证签名 + /// + /// 原始数据 + /// 签名 + /// + public bool Verify(string data, string sign) + { + byte[] dataBytes = _encoding.GetBytes(data); + byte[] signBytes = Convert.FromBase64String(sign); + + var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, + RSASignaturePadding.Pkcs1); + + return verify; + } + + #endregion + + #region 解密 + + public string Decrypt(string cipherText) + { + if (_privateKeyRsaProvider == null) + { + throw new Exception("_privateKeyRsaProvider is null"); + } + + return Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), + RSAEncryptionPadding.Pkcs1)); + } + + #endregion + + #region 加密 + + public string Encrypt(string text) + { + if (_publicKeyRsaProvider == null) + { + throw new Exception("_publicKeyRsaProvider is null"); + } + + return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), + RSAEncryptionPadding.Pkcs1)); + } + + #endregion + + #region 使用私钥创建RSA实例 + + public RSA CreateRsaProviderFromPrivateKey(string privateKey) + { + var privateKeyBits = Convert.FromBase64String(privateKey); + + var rsa = RSA.Create(); + var rsaParameters = new RSAParameters(); + + using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))) + { + byte bt = 0; + ushort twobytes = 0; + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) + binr.ReadByte(); + else if (twobytes == 0x8230) + binr.ReadInt16(); + else + throw new Exception("Unexpected value read binr.ReadUInt16()"); + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) + throw new Exception("Unexpected version"); + + bt = binr.ReadByte(); + if (bt != 0x00) + throw new Exception("Unexpected value read binr.ReadByte()"); + + rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr)); + } + + rsa.ImportParameters(rsaParameters); + return rsa; + } + + #endregion + + #region 使用公钥创建RSA实例 + + public RSA CreateRsaProviderFromPublicKey(string publicKeyString) + { + // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" + byte[] seqOid = + {0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00}; + byte[] seq = new byte[15]; + + var x509Key = Convert.FromBase64String(publicKeyString); + + // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ + using (MemoryStream mem = new MemoryStream(x509Key)) + { + using (BinaryReader binr = new BinaryReader(mem) + ) //wrap Memory Stream with BinaryReader for easy reading + { + byte bt = 0; + ushort twobytes = 0; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130 + ) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + seq = binr.ReadBytes(15); //read the Sequence OID + if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8103 + ) //data read as little endian order (actual data order for Bit String is 03 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8203) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + bt = binr.ReadByte(); + if (bt != 0x00) //expect null byte next + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130 + ) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + byte lowbyte = 0x00; + byte highbyte = 0x00; + + if (twobytes == 0x8102 + ) //data read as little endian order (actual data order for Integer is 02 81) + lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus + else if (twobytes == 0x8202) + { + highbyte = binr.ReadByte(); //advance 2 bytes + lowbyte = binr.ReadByte(); + } + else + return null; + + byte[] modint = + {lowbyte, highbyte, 0x00, 0x00}; //reverse byte order since asn.1 key uses big endian order + int modsize = BitConverter.ToInt32(modint, 0); + + int firstbyte = binr.PeekChar(); + if (firstbyte == 0x00) + { + //if first byte (highest order) of modulus is zero, don't include it + binr.ReadByte(); //skip this null byte + modsize -= 1; //reduce modulus buffer size by 1 + } + + byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes + + if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data + return null; + int expbytes = + binr + .ReadByte(); // should only need one byte for actual exponent data (for all useful values) + byte[] exponent = binr.ReadBytes(expbytes); + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + var rsa = RSA.Create(); + RSAParameters rsaKeyInfo = new RSAParameters + { + Modulus = modulus, + Exponent = exponent + }; + rsa.ImportParameters(rsaKeyInfo); + + return rsa; + } + } + } + + #endregion + + #region 导入密钥算法 + + private int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + int count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) + return 0; + bt = binr.ReadByte(); + + if (bt == 0x81) + count = binr.ReadByte(); + else if (bt == 0x82) + { + var highbyte = binr.ReadByte(); + var lowbyte = binr.ReadByte(); + byte[] modint = {lowbyte, highbyte, 0x00, 0x00}; + count = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; + } + + while (binr.ReadByte() == 0x00) + { + count -= 1; + } + + binr.BaseStream.Seek(-1, SeekOrigin.Current); + return count; + } + + private bool CompareBytearrays(byte[] a, byte[] b) + { + if (a.Length != b.Length) + return false; + int i = 0; + foreach (byte c in a) + { + if (c != b[i]) + return false; + i++; + } + + return true; + } + + #endregion + } + + /// + /// RSA算法类型 + /// + public enum RsaType + { + /// + /// SHA1 + /// + RSA = 0, + + /// + /// RSA2 密钥长度至少为2048 + /// SHA256 + /// + RSA2 + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/RandomHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/RandomHelper.cs index c5788b0..9c64bb7 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/RandomHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/RandomHelper.cs @@ -1,143 +1,143 @@ -using System; -using System.Collections.Generic; - -namespace Hncore.Infrastructure.Common -{ - public class RandomHelper - { - #region 私有属性 - - /// - /// 随机数最小值 - /// - private static int MiniNum => int.MinValue; - - /// - /// 随机数最大值 - /// - private static int MaxNum => int.MaxValue; - - /// - /// 随机数长度 - /// - private static int RandomLength => 4; - - /// - /// 随机数来源 - /// - private static string RandomString => "0123456789ABCDEFGHIJKMLNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz"; - - /// - /// 系统默认生成随机数长度 - /// - private const int RandomLengthPresent = 6; - - /// - /// 系统默认随机数来源 - /// - private const string RandomStringPresent = "0123456789ABCDEFGHIJKMLNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz"; - - private static readonly Random Random = new Random(DateTime.Now.Millisecond); - #endregion - - #region 产生随机字符 - - /// - /// 产生随机字符 - /// - /// 产生随机数长度,默认为-1 - /// 随机数来源 - /// - public static string GetRandomString(int randomLength = -1, string randomString = "") - { - int randomLengthTemp;//随机数长度 - if (randomLength > 0) - randomLengthTemp = randomLength; - else if (RandomLength > 0) - randomLengthTemp = RandomLength; - else - randomLengthTemp = RandomLengthPresent; - string randomStringTemp;//随机数来源 - if (!string.IsNullOrEmpty(randomString)) - randomStringTemp = randomString; - else if (!string.IsNullOrEmpty(RandomString)) - randomStringTemp = RandomString; - else - randomStringTemp = RandomStringPresent; - string returnValue = string.Empty; - for (int i = 0; i < randomLengthTemp; i++) - { - int r = Random.Next(0, randomStringTemp.Length - 1); - returnValue += randomStringTemp[r]; - } - return returnValue; - } - #endregion - - #region 产生随机数 - /// - /// 产生随机数 - /// - /// 最小随机数 - /// 最大随机数 - /// - public static int GetRandom(int minNum = -1, int maxNum = -1) - { - int minNumTemp = minNum == -1 ? MiniNum : minNum;//最小随机数 - int maxNumTemp = maxNum == -1 ? MaxNum : maxNum;//最大随机数 - return Random.Next(minNumTemp, maxNumTemp); - } - #endregion - - #region 生成一个0.0到1.0的随机小数 - /// - /// 生成一个0.0到1.0的随机小数 - /// - public double GetRandomDouble() - { - return Random.NextDouble(); - } - #endregion - - #region 对一个数组进行随机排序 - /// - /// 对一个数组进行随机排序 - /// - /// 数组的类型 - /// 需要随机排序的数组 - public void GetRandomArray(T[] arr) - { - //对数组进行随机排序的算法:随机选择两个位置,将两个位置上的值交换 - //交换的次数,这里使用数组的长度作为交换次数 - int count = arr.Length; - //开始交换 - for (int i = 0; i < count; i++) - { - //生成两个随机数位置 - int randomNum1 = GetRandom(0, arr.Length); - int randomNum2 = GetRandom(0, arr.Length); - //定义临时变量 - //交换两个随机数位置的值 - var temp = arr[randomNum1]; - arr[randomNum1] = arr[randomNum2]; - arr[randomNum2] = temp; - } - } - - public static string Uuid(int len) - { - len = len > 32 ? 32 : len; - var str = Guid.NewGuid().ToString("N"); - var list = new List(); - while (true) - { - var index = GetRandom(0, 32); - list.Add(str[index].ToString()); - if (list.Count >= len) - break; - } - return string.Join("", list); - } - #endregion - } -} +using System; +using System.Collections.Generic; + +namespace Hncore.Infrastructure.Common +{ + public class RandomHelper + { + #region 私有属性 + + /// + /// 随机数最小值 + /// + private static int MiniNum => int.MinValue; + + /// + /// 随机数最大值 + /// + private static int MaxNum => int.MaxValue; + + /// + /// 随机数长度 + /// + private static int RandomLength => 4; + + /// + /// 随机数来源 + /// + private static string RandomString => "0123456789ABCDEFGHIJKMLNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz"; + + /// + /// 系统默认生成随机数长度 + /// + private const int RandomLengthPresent = 6; + + /// + /// 系统默认随机数来源 + /// + private const string RandomStringPresent = "0123456789ABCDEFGHIJKMLNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz"; + + private static readonly Random Random = new Random(DateTime.Now.Millisecond); + #endregion + + #region 产生随机字符 + + /// + /// 产生随机字符 + /// + /// 产生随机数长度,默认为-1 + /// 随机数来源 + /// + public static string GetRandomString(int randomLength = -1, string randomString = "") + { + int randomLengthTemp;//随机数长度 + if (randomLength > 0) + randomLengthTemp = randomLength; + else if (RandomLength > 0) + randomLengthTemp = RandomLength; + else + randomLengthTemp = RandomLengthPresent; + string randomStringTemp;//随机数来源 + if (!string.IsNullOrEmpty(randomString)) + randomStringTemp = randomString; + else if (!string.IsNullOrEmpty(RandomString)) + randomStringTemp = RandomString; + else + randomStringTemp = RandomStringPresent; + string returnValue = string.Empty; + for (int i = 0; i < randomLengthTemp; i++) + { + int r = Random.Next(0, randomStringTemp.Length - 1); + returnValue += randomStringTemp[r]; + } + return returnValue; + } + #endregion + + #region 产生随机数 + /// + /// 产生随机数 + /// + /// 最小随机数 + /// 最大随机数 + /// + public static int GetRandom(int minNum = -1, int maxNum = -1) + { + int minNumTemp = minNum == -1 ? MiniNum : minNum;//最小随机数 + int maxNumTemp = maxNum == -1 ? MaxNum : maxNum;//最大随机数 + return Random.Next(minNumTemp, maxNumTemp); + } + #endregion + + #region 生成一个0.0到1.0的随机小数 + /// + /// 生成一个0.0到1.0的随机小数 + /// + public double GetRandomDouble() + { + return Random.NextDouble(); + } + #endregion + + #region 对一个数组进行随机排序 + /// + /// 对一个数组进行随机排序 + /// + /// 数组的类型 + /// 需要随机排序的数组 + public void GetRandomArray(T[] arr) + { + //对数组进行随机排序的算法:随机选择两个位置,将两个位置上的值交换 + //交换的次数,这里使用数组的长度作为交换次数 + int count = arr.Length; + //开始交换 + for (int i = 0; i < count; i++) + { + //生成两个随机数位置 + int randomNum1 = GetRandom(0, arr.Length); + int randomNum2 = GetRandom(0, arr.Length); + //定义临时变量 + //交换两个随机数位置的值 + var temp = arr[randomNum1]; + arr[randomNum1] = arr[randomNum2]; + arr[randomNum2] = temp; + } + } + + public static string Uuid(int len) + { + len = len > 32 ? 32 : len; + var str = Guid.NewGuid().ToString("N"); + var list = new List(); + while (true) + { + var index = GetRandom(0, 32); + list.Add(str[index].ToString()); + if (list.Count >= len) + break; + } + return string.Join("", list); + } + #endregion + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Common/RedisLocklHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/RedisLocklHelper.cs index eb92261..1a6eb47 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/RedisLocklHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/RedisLocklHelper.cs @@ -1,72 +1,72 @@ -using Hncore.Infrastructure.Extension; -using System; -using System.Diagnostics; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace Hncore.Infrastructure.Common -{ - public class RedisLocker - { - private string _key; - private long _lockTime; - /// - /// - /// - /// 锁的键 - /// 锁超时时间 单位毫秒 - public RedisLocker(string key,long outTime) - { - _key =$"Lock:{Assembly.GetCallingAssembly().GetFriendName()}:{key}" ; - _lockTime = outTime; - } - public void Exec(Action action) - { - if (GetLock()) - { - action(); - ReleaseLock(); - } - } - - protected bool GetLock() - { - try - { - var currentTime = DateTime.Now.GetUnixTimeStamp(); - if (RedisHelper.SetNx(this._key, currentTime + _lockTime)) - { - Console.WriteLine("获取到Redis锁了"); - RedisHelper.Expire(_key, TimeSpan.FromMilliseconds(_lockTime)); //设置过期时间 - return true; - } - - //防止SetNx成功但是设置过期时间(Expire)失败造成死锁 - var lockValue = Convert.ToInt64(RedisHelper.Get(_key)); - currentTime = DateTime.Now.GetUnixTimeStamp(); - if (lockValue > 0 && currentTime > lockValue) - { - var getsetResult = Convert.ToInt64(RedisHelper.GetSet(_key, currentTime)); - if (getsetResult == 0 || getsetResult == lockValue) - { - Console.WriteLine("获取到Redis锁了"); - RedisHelper.Expire(_key, TimeSpan.FromMilliseconds(_lockTime)); - return true; - } - } - Console.WriteLine("没有获取到锁"); - return false; - } - catch - { - ReleaseLock(); - return false; - } - } - - protected bool ReleaseLock() - { - return RedisHelper.Del(_key) > 0; - } - } +using Hncore.Infrastructure.Extension; +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace Hncore.Infrastructure.Common +{ + public class RedisLocker + { + private string _key; + private long _lockTime; + /// + /// + /// + /// 锁的键 + /// 锁超时时间 单位毫秒 + public RedisLocker(string key,long outTime) + { + _key =$"Lock:{Assembly.GetCallingAssembly().GetFriendName()}:{key}" ; + _lockTime = outTime; + } + public void Exec(Action action) + { + if (GetLock()) + { + action(); + ReleaseLock(); + } + } + + protected bool GetLock() + { + try + { + var currentTime = DateTime.Now.GetUnixTimeStamp(); + if (RedisHelper.SetNx(this._key, currentTime + _lockTime)) + { + Console.WriteLine("获取到Redis锁了"); + RedisHelper.Expire(_key, TimeSpan.FromMilliseconds(_lockTime)); //设置过期时间 + return true; + } + + //防止SetNx成功但是设置过期时间(Expire)失败造成死锁 + var lockValue = Convert.ToInt64(RedisHelper.Get(_key)); + currentTime = DateTime.Now.GetUnixTimeStamp(); + if (lockValue > 0 && currentTime > lockValue) + { + var getsetResult = Convert.ToInt64(RedisHelper.GetSet(_key, currentTime)); + if (getsetResult == 0 || getsetResult == lockValue) + { + Console.WriteLine("获取到Redis锁了"); + RedisHelper.Expire(_key, TimeSpan.FromMilliseconds(_lockTime)); + return true; + } + } + Console.WriteLine("没有获取到锁"); + return false; + } + catch + { + ReleaseLock(); + return false; + } + } + + protected bool ReleaseLock() + { + return RedisHelper.Del(_key) > 0; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/RegexPattern.cs b/Infrastructure/Hncore.Infrastructure/Common/RegexPattern.cs index a93d6e8..8d03404 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/RegexPattern.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/RegexPattern.cs @@ -1,35 +1,35 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace Hncore.Infrastructure.Common -{ - public static class RegexPattern - { - public const string Mobile = @"^1[123456789]\d{9}$";//宽松的手机验证。包含运营商可能的新增号段。 - public const string Email = @"^[\w-]+@[\w-]+\.(com|net|org|edu|mil|tv|biz|info)$";// 邮箱验证 - public const string IdCard = @"^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$";//18位身份证 - public const string CarNumber = @"^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1})$"; - public static bool IsMatch(string str,string pattern) - { - return Regex.IsMatch(str, pattern); - } - public static bool IsMobile(string str) - { - return IsMatch(str, Mobile); - } - public static bool IsEmail(string str) - { - return IsMatch(str, Email); - } - public static bool IsIdCard(string str) - { - return IsMatch(str, IdCard); - } - public static bool IsCarNumber(string str) - { - return IsMatch(str, CarNumber); - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Hncore.Infrastructure.Common +{ + public static class RegexPattern + { + public const string Mobile = @"^1[123456789]\d{9}$";//宽松的手机验证。包含运营商可能的新增号段。 + public const string Email = @"^[\w-]+@[\w-]+\.(com|net|org|edu|mil|tv|biz|info)$";// 邮箱验证 + public const string IdCard = @"^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$";//18位身份证 + public const string CarNumber = @"^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1})$"; + public static bool IsMatch(string str,string pattern) + { + return Regex.IsMatch(str, pattern); + } + public static bool IsMobile(string str) + { + return IsMatch(str, Mobile); + } + public static bool IsEmail(string str) + { + return IsMatch(str, Email); + } + public static bool IsIdCard(string str) + { + return IsMatch(str, IdCard); + } + public static bool IsCarNumber(string str) + { + return IsMatch(str, CarNumber); + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Common/SecurityHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/SecurityHelper.cs index 4a80d1b..04f4787 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/SecurityHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/SecurityHelper.cs @@ -1,388 +1,388 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace Hncore.Infrastructure.Common -{ - public class SecurityHelper - { - #region AES加密 - - /// - /// AES加密 - /// - /// - /// - public static string AESEncrypt(string toEncrypt, string key) - { - if (string.IsNullOrWhiteSpace(toEncrypt)) - return string.Empty; - // 256-AES key - byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); - byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt); - - RijndaelManaged rDel = new RijndaelManaged(); - rDel.Key = keyArray; - rDel.Mode = CipherMode.ECB; - rDel.Padding = PaddingMode.PKCS7; - - ICryptoTransform cTransform = rDel.CreateEncryptor(); - byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); - - - return Convert.ToBase64String(resultArray, 0, resultArray.Length); - } - - #endregion - - #region AES解密 - - /// - /// AES解密 - /// - /// - /// - public static string Decrypt(string toDecrypt, string key) - { - if (string.IsNullOrWhiteSpace(toDecrypt)) - return string.Empty; - try - { - // 256-AES key - byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); - byte[] toEncryptArray = Convert.FromBase64String(toDecrypt); - - RijndaelManaged rDel = new RijndaelManaged(); - rDel.Key = keyArray; - rDel.Mode = CipherMode.ECB; - rDel.Padding = PaddingMode.PKCS7; - - ICryptoTransform cTransform = rDel.CreateDecryptor(); - - - byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); - return UTF8Encoding.UTF8.GetString(resultArray); - } - catch(Exception ex) - { - LogHelper.Error("aes Decrypt", ex.Message); - return toDecrypt; - } - } - - #endregion - - #region MD5加密 - - /// - /// MD5加密 - /// - /// - /// - public static string GetMd5Hash(string input, Encoding encoding = null) - { - if (encoding == null) - { - encoding = Encoding.UTF8; - } - - - MD5 myMD5 = new MD5CryptoServiceProvider(); - byte[] signed = myMD5.ComputeHash(encoding.GetBytes(input)); - string signResult = byte2mac(signed); - return signResult.ToUpper(); - } - - //MD5加密方法 - private static string byte2mac(byte[] signed) - { - StringBuilder EnText = new StringBuilder(); - foreach (byte Byte in signed) - { - EnText.AppendFormat("{0:x2}", Byte); - } - - return EnText.ToString(); - } - - #endregion - - #region 对字符串进行DES加密 - - /// - /// 对字符串进行DES加密 - /// - /// 待加密的字符串 - /// 加密后的BASE64编码的字符串 - public static string DesEncrypt(string sourceString, string key, string iv) - { - byte[] btKey = Encoding.Default.GetBytes(key); - byte[] btIV = Encoding.Default.GetBytes(iv); - DESCryptoServiceProvider des = new DESCryptoServiceProvider(); - using (MemoryStream ms = new MemoryStream()) - { - byte[] inData = Encoding.Default.GetBytes(sourceString); - using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(btKey, btIV), CryptoStreamMode.Write)) - { - cs.Write(inData, 0, inData.Length); - cs.FlushFinalBlock(); - } - - return Convert.ToBase64String(ms.ToArray()); - } - } - - #endregion - - #region 对DES加密后的字符串进行解密 - - /// - /// 对DES加密后的字符串进行解密 - /// - /// 待解密的字符串 - /// 解密后的字符串 - public static string DesDecrypt(string encryptedString, string key, string iv) - { - byte[] btKey = Encoding.Default.GetBytes(key); - byte[] btIV = Encoding.Default.GetBytes(iv); - DESCryptoServiceProvider des = new DESCryptoServiceProvider(); - using (MemoryStream ms = new MemoryStream()) - { - byte[] inData = Convert.FromBase64String(encryptedString); - using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(btKey, btIV), CryptoStreamMode.Write)) - { - cs.Write(inData, 0, inData.Length); - cs.FlushFinalBlock(); - } - - return Encoding.Default.GetString(ms.ToArray()); - } - } - - #endregion - - - public static string Sha1(string str) - { - SHA1 sha1 = new SHA1CryptoServiceProvider(); - - byte[] bytes_in = Encoding.UTF8.GetBytes(str); - byte[] bytes_out = sha1.ComputeHash(bytes_in); - sha1.Dispose(); - - var sb = new StringBuilder(); - foreach (byte b in bytes_out) - { - sb.Append(b.ToString("x2")); - } - - return sb.ToString(); - } - - public static string HMACSHA1(string text, string key) - { - HMACSHA1 myhmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(key)); - byte[] byteArray = Encoding.UTF8.GetBytes(text); - MemoryStream stream = new MemoryStream(byteArray); - string signature = Convert.ToBase64String(myhmacsha1.ComputeHash(stream)); - - return signature; - } - - #region JS Aes解密 - - /// - /// JS Aes解密 - /// - /// - /// - /// - /// - public static string JsAesDecrypt(string toDecrypt, string key, string iv) - { - byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); - byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv); - byte[] cipherText = HexToByteArray(toDecrypt); - // Check arguments. - if (cipherText == null || cipherText.Length <= 0) - { - throw new ArgumentNullException("cipherText"); - } - - if (key == null || key.Length <= 0) - { - throw new ArgumentNullException("key"); - } - - if (iv == null || iv.Length <= 0) - { - throw new ArgumentNullException("key"); - } - - string plaintext = null; - using (var rijAlg = new RijndaelManaged()) - { - //Settings - rijAlg.Mode = CipherMode.CBC; - rijAlg.Padding = PaddingMode.PKCS7; - rijAlg.FeedbackSize = 128; - - rijAlg.Key = keyArray; - rijAlg.IV = ivArray; - - var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); - - using (var msDecrypt = new MemoryStream(cipherText)) - { - using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) - { - using (var srDecrypt = new StreamReader(csDecrypt)) - { - plaintext = srDecrypt.ReadToEnd(); - } - } - } - } - - return plaintext; - } - - private static byte[] HexToByteArray(string hex) - { - int NumberChars = hex.Length; - byte[] bytes = new byte[NumberChars / 2]; - for (int i = 0; i < NumberChars; i += 2) - bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); - return bytes; - } - - #endregion - - #region JS Aes 加密 - - /// - /// JsAesEncrypt - /// - /// - /// - /// - /// - public static string JsAesEncrypt(string plainText, string key, string iv) - { - byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); - byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv); - - // Check arguments. - if (plainText == null || plainText.Length <= 0) - { - throw new ArgumentNullException("plainText"); - } - - if (key == null || key.Length <= 0) - { - throw new ArgumentNullException("key"); - } - - if (iv == null || iv.Length <= 0) - { - throw new ArgumentNullException("key"); - } - - byte[] encrypted; - using (var rijAlg = new RijndaelManaged()) - { - rijAlg.Mode = CipherMode.CBC; - rijAlg.Padding = PaddingMode.PKCS7; - rijAlg.FeedbackSize = 128; - - rijAlg.Key = keyArray; - rijAlg.IV = ivArray; - - var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); - using (var msEncrypt = new MemoryStream()) - { - using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) - { - using (var swEncrypt = new StreamWriter(csEncrypt)) - { - swEncrypt.Write(plainText); - } - - encrypted = msEncrypt.ToArray(); - } - } - } - - // Return the encrypted bytes from the memory stream. - return ByteArrayToHex(encrypted); - } - - private static string ByteArrayToHex(byte[] ba) - { - string hex = BitConverter.ToString(ba); - return hex.Replace("-", ""); - } - - #endregion - - #region 加密隐藏信息(将原信息其中一部分数据替换为特殊字符) - - /// - /// 加密隐藏信息(将原信息其中一部分数据替换为特殊字符) - /// - /// 原参数信息 - /// 更换后的特殊字符 - /// 下标 - /// 位数,-1代表到队尾 - /// - public static string Encrypt(string param, string key, int index, int length = -1) - { - if (string.IsNullOrEmpty(param)) - { - return ""; - } - - string str = ""; - if (index > param.Length - 1) - { - return param; - } - - str = param.Substring(0, index); - if (length == -1) - { - length = param.Length - index; - } - - for (int i = 0; i < length; i++) - { - str += key; - } - - if (index + length < param.Length) - { - str += param.Substring(index + length); - } - - return str; - } - - #endregion - - - /// - /// 将密码使用MD5算法求哈希值 - /// - /// - /// - public static string HashPassword(string password) - { - using (MD5 md5 = MD5.Create()) - { - byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(password)); - return Convert.ToBase64String(bytes); - } - } - } +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Hncore.Infrastructure.Common +{ + public class SecurityHelper + { + #region AES加密 + + /// + /// AES加密 + /// + /// + /// + public static string AESEncrypt(string toEncrypt, string key) + { + if (string.IsNullOrWhiteSpace(toEncrypt)) + return string.Empty; + // 256-AES key + byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); + byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt); + + RijndaelManaged rDel = new RijndaelManaged(); + rDel.Key = keyArray; + rDel.Mode = CipherMode.ECB; + rDel.Padding = PaddingMode.PKCS7; + + ICryptoTransform cTransform = rDel.CreateEncryptor(); + byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); + + + return Convert.ToBase64String(resultArray, 0, resultArray.Length); + } + + #endregion + + #region AES解密 + + /// + /// AES解密 + /// + /// + /// + public static string Decrypt(string toDecrypt, string key) + { + if (string.IsNullOrWhiteSpace(toDecrypt)) + return string.Empty; + try + { + // 256-AES key + byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); + byte[] toEncryptArray = Convert.FromBase64String(toDecrypt); + + RijndaelManaged rDel = new RijndaelManaged(); + rDel.Key = keyArray; + rDel.Mode = CipherMode.ECB; + rDel.Padding = PaddingMode.PKCS7; + + ICryptoTransform cTransform = rDel.CreateDecryptor(); + + + byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); + return UTF8Encoding.UTF8.GetString(resultArray); + } + catch(Exception ex) + { + LogHelper.Error("aes Decrypt", ex.Message); + return toDecrypt; + } + } + + #endregion + + #region MD5加密 + + /// + /// MD5加密 + /// + /// + /// + public static string GetMd5Hash(string input, Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + + MD5 myMD5 = new MD5CryptoServiceProvider(); + byte[] signed = myMD5.ComputeHash(encoding.GetBytes(input)); + string signResult = byte2mac(signed); + return signResult.ToUpper(); + } + + //MD5加密方法 + private static string byte2mac(byte[] signed) + { + StringBuilder EnText = new StringBuilder(); + foreach (byte Byte in signed) + { + EnText.AppendFormat("{0:x2}", Byte); + } + + return EnText.ToString(); + } + + #endregion + + #region 对字符串进行DES加密 + + /// + /// 对字符串进行DES加密 + /// + /// 待加密的字符串 + /// 加密后的BASE64编码的字符串 + public static string DesEncrypt(string sourceString, string key, string iv) + { + byte[] btKey = Encoding.Default.GetBytes(key); + byte[] btIV = Encoding.Default.GetBytes(iv); + DESCryptoServiceProvider des = new DESCryptoServiceProvider(); + using (MemoryStream ms = new MemoryStream()) + { + byte[] inData = Encoding.Default.GetBytes(sourceString); + using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(btKey, btIV), CryptoStreamMode.Write)) + { + cs.Write(inData, 0, inData.Length); + cs.FlushFinalBlock(); + } + + return Convert.ToBase64String(ms.ToArray()); + } + } + + #endregion + + #region 对DES加密后的字符串进行解密 + + /// + /// 对DES加密后的字符串进行解密 + /// + /// 待解密的字符串 + /// 解密后的字符串 + public static string DesDecrypt(string encryptedString, string key, string iv) + { + byte[] btKey = Encoding.Default.GetBytes(key); + byte[] btIV = Encoding.Default.GetBytes(iv); + DESCryptoServiceProvider des = new DESCryptoServiceProvider(); + using (MemoryStream ms = new MemoryStream()) + { + byte[] inData = Convert.FromBase64String(encryptedString); + using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(btKey, btIV), CryptoStreamMode.Write)) + { + cs.Write(inData, 0, inData.Length); + cs.FlushFinalBlock(); + } + + return Encoding.Default.GetString(ms.ToArray()); + } + } + + #endregion + + + public static string Sha1(string str) + { + SHA1 sha1 = new SHA1CryptoServiceProvider(); + + byte[] bytes_in = Encoding.UTF8.GetBytes(str); + byte[] bytes_out = sha1.ComputeHash(bytes_in); + sha1.Dispose(); + + var sb = new StringBuilder(); + foreach (byte b in bytes_out) + { + sb.Append(b.ToString("x2")); + } + + return sb.ToString(); + } + + public static string HMACSHA1(string text, string key) + { + HMACSHA1 myhmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(key)); + byte[] byteArray = Encoding.UTF8.GetBytes(text); + MemoryStream stream = new MemoryStream(byteArray); + string signature = Convert.ToBase64String(myhmacsha1.ComputeHash(stream)); + + return signature; + } + + #region JS Aes解密 + + /// + /// JS Aes解密 + /// + /// + /// + /// + /// + public static string JsAesDecrypt(string toDecrypt, string key, string iv) + { + byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); + byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv); + byte[] cipherText = HexToByteArray(toDecrypt); + // Check arguments. + if (cipherText == null || cipherText.Length <= 0) + { + throw new ArgumentNullException("cipherText"); + } + + if (key == null || key.Length <= 0) + { + throw new ArgumentNullException("key"); + } + + if (iv == null || iv.Length <= 0) + { + throw new ArgumentNullException("key"); + } + + string plaintext = null; + using (var rijAlg = new RijndaelManaged()) + { + //Settings + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + rijAlg.FeedbackSize = 128; + + rijAlg.Key = keyArray; + rijAlg.IV = ivArray; + + var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); + + using (var msDecrypt = new MemoryStream(cipherText)) + { + using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (var srDecrypt = new StreamReader(csDecrypt)) + { + plaintext = srDecrypt.ReadToEnd(); + } + } + } + } + + return plaintext; + } + + private static byte[] HexToByteArray(string hex) + { + int NumberChars = hex.Length; + byte[] bytes = new byte[NumberChars / 2]; + for (int i = 0; i < NumberChars; i += 2) + bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); + return bytes; + } + + #endregion + + #region JS Aes 加密 + + /// + /// JsAesEncrypt + /// + /// + /// + /// + /// + public static string JsAesEncrypt(string plainText, string key, string iv) + { + byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key); + byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv); + + // Check arguments. + if (plainText == null || plainText.Length <= 0) + { + throw new ArgumentNullException("plainText"); + } + + if (key == null || key.Length <= 0) + { + throw new ArgumentNullException("key"); + } + + if (iv == null || iv.Length <= 0) + { + throw new ArgumentNullException("key"); + } + + byte[] encrypted; + using (var rijAlg = new RijndaelManaged()) + { + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + rijAlg.FeedbackSize = 128; + + rijAlg.Key = keyArray; + rijAlg.IV = ivArray; + + var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); + using (var msEncrypt = new MemoryStream()) + { + using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) + { + using (var swEncrypt = new StreamWriter(csEncrypt)) + { + swEncrypt.Write(plainText); + } + + encrypted = msEncrypt.ToArray(); + } + } + } + + // Return the encrypted bytes from the memory stream. + return ByteArrayToHex(encrypted); + } + + private static string ByteArrayToHex(byte[] ba) + { + string hex = BitConverter.ToString(ba); + return hex.Replace("-", ""); + } + + #endregion + + #region 加密隐藏信息(将原信息其中一部分数据替换为特殊字符) + + /// + /// 加密隐藏信息(将原信息其中一部分数据替换为特殊字符) + /// + /// 原参数信息 + /// 更换后的特殊字符 + /// 下标 + /// 位数,-1代表到队尾 + /// + public static string Encrypt(string param, string key, int index, int length = -1) + { + if (string.IsNullOrEmpty(param)) + { + return ""; + } + + string str = ""; + if (index > param.Length - 1) + { + return param; + } + + str = param.Substring(0, index); + if (length == -1) + { + length = param.Length - index; + } + + for (int i = 0; i < length; i++) + { + str += key; + } + + if (index + length < param.Length) + { + str += param.Substring(index + length); + } + + return str; + } + + #endregion + + + /// + /// 将密码使用MD5算法求哈希值 + /// + /// + /// + public static string HashPassword(string password) + { + using (MD5 md5 = MD5.Create()) + { + byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(password)); + return Convert.ToBase64String(bytes); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/ShellHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/ShellHelper.cs index cc01e0a..86b6102 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/ShellHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/ShellHelper.cs @@ -1,63 +1,63 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace Hncore.Infrastructure.Common -{ - public class ShellHelper - { - public static string Bash(string cmd) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - var escapedArgs = cmd.Replace("\"", "\\\""); - - var process = new Process() - { - StartInfo = new ProcessStartInfo - { - FileName = "/bin/bash", - Arguments = $"-c \"{escapedArgs}\"", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - } - }; - process.Start(); - string result = process.StandardOutput.ReadToEnd(); - process.WaitForExit(); - return result; - } - - return ""; - } - - public static void RedirectOutputBash(string cmd) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - cmd = cmd.Replace("\"", "\\\""); - - Console.WriteLine("执行命令"); - Console.WriteLine(cmd); - - var process = new Process() - { - StartInfo = new ProcessStartInfo - { - FileName = "/bin/bash", - Arguments = $"-c \"{cmd}\"", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, - } - }; - - process.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data); - process.Start(); - process.BeginOutputReadLine(); - process.WaitForExit(); - } - } - } +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Hncore.Infrastructure.Common +{ + public class ShellHelper + { + public static string Bash(string cmd) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + var escapedArgs = cmd.Replace("\"", "\\\""); + + var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{escapedArgs}\"", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + process.Start(); + string result = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + return result; + } + + return ""; + } + + public static void RedirectOutputBash(string cmd) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + cmd = cmd.Replace("\"", "\\\""); + + Console.WriteLine("执行命令"); + Console.WriteLine(cmd); + + var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{cmd}\"", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; + + process.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data); + process.Start(); + process.BeginOutputReadLine(); + process.WaitForExit(); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/UrlHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/UrlHelper.cs index 0fef710..7ffc5fa 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/UrlHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/UrlHelper.cs @@ -1,88 +1,88 @@ -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Hncore.Infrastructure.Common -{ - public class UrlHelper - { - #region 设置url参数 - - /// - /// 设置url参数 - /// - /// - /// - /// - /// - public static string SetUrlParam(string url, string paramName, string paramValue) - { - paramName = paramName.ToLower(); - - string currentUrl = url; - - if (!string.IsNullOrEmpty(paramValue)) - { - paramValue = HttpUtility.UrlEncode(paramValue); - } - - if (!currentUrl.Contains("?")) - { - return currentUrl += "?" + paramName + "=" + paramValue; - } - - List paramItems = currentUrl.Split('?')[1].Split('&').ToList(); - - string paramItem = paramItems.SingleOrDefault(t => t.ToLower().Split('=')[0] == paramName); - - if (!string.IsNullOrEmpty(paramItem)) - { - return currentUrl.Replace(paramItem, paramName + "=" + paramValue); - } - else - { - if (currentUrl.Contains("?")) - { - currentUrl += "&"; - } - else - { - currentUrl += "?"; - } - return currentUrl + paramName + "=" + paramValue; - } - } - - public static string SetUrlParam(string url, object paramObj) - { - var type = paramObj.GetType(); - var properties = type.GetProperties(); - - foreach (var property in properties) - { - string name = property.Name; - - object valueObj = property.GetValue(paramObj, null); - - if (valueObj == null) - { - continue; - } - - string value = valueObj.ToString(); - - url = SetUrlParam(url, name, value); - } - - return url; - } - - - public static string ToUrlParam(IDictionary kvs) - { - return string.Join("&", kvs.Select(m => $"{m.Key}={m.Value}")); - } - - #endregion - } +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace Hncore.Infrastructure.Common +{ + public class UrlHelper + { + #region 设置url参数 + + /// + /// 设置url参数 + /// + /// + /// + /// + /// + public static string SetUrlParam(string url, string paramName, string paramValue) + { + paramName = paramName.ToLower(); + + string currentUrl = url; + + if (!string.IsNullOrEmpty(paramValue)) + { + paramValue = HttpUtility.UrlEncode(paramValue); + } + + if (!currentUrl.Contains("?")) + { + return currentUrl += "?" + paramName + "=" + paramValue; + } + + List paramItems = currentUrl.Split('?')[1].Split('&').ToList(); + + string paramItem = paramItems.SingleOrDefault(t => t.ToLower().Split('=')[0] == paramName); + + if (!string.IsNullOrEmpty(paramItem)) + { + return currentUrl.Replace(paramItem, paramName + "=" + paramValue); + } + else + { + if (currentUrl.Contains("?")) + { + currentUrl += "&"; + } + else + { + currentUrl += "?"; + } + return currentUrl + paramName + "=" + paramValue; + } + } + + public static string SetUrlParam(string url, object paramObj) + { + var type = paramObj.GetType(); + var properties = type.GetProperties(); + + foreach (var property in properties) + { + string name = property.Name; + + object valueObj = property.GetValue(paramObj, null); + + if (valueObj == null) + { + continue; + } + + string value = valueObj.ToString(); + + url = SetUrlParam(url, name, value); + } + + return url; + } + + + public static string ToUrlParam(IDictionary kvs) + { + return string.Join("&", kvs.Select(m => $"{m.Key}={m.Value}")); + } + + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Common/ValidateCodeHelper.cs b/Infrastructure/Hncore.Infrastructure/Common/ValidateCodeHelper.cs index e50d33c..9427e34 100644 --- a/Infrastructure/Hncore.Infrastructure/Common/ValidateCodeHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Common/ValidateCodeHelper.cs @@ -1,110 +1,110 @@ -using System; -using System.Drawing; -using System.Drawing.Imaging; - -namespace Hncore.Infrastructure.Common -{ - public class ValidateCodeHelper - { - public static string MakeCode(int length = 4) - { - char[] allCharArray = new char[] { '2', '3', '4', '5', '6', '7', '8', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z' }; - string randomCode = ""; - int temp = -1; - - Random rand = new Random(); - for (int i = 0; i < length; i++) - { - if (temp != -1) - { - rand = new Random(i * temp * ((int)DateTime.Now.Ticks)); - } - int t = rand.Next(allCharArray.Length); - if (temp == t) - { - return MakeCode(length); - } - temp = t; - randomCode += allCharArray[t]; - } - return randomCode; - } - public static string MakeNumCode(int length = 4) - { - char[] allCharArray = new char[] { '1','2', '3', '4', '5', '6', '7', '8','9'}; - string randomCode = ""; - int temp = -1; - - Random rand = new Random(); - for (int i = 0; i < length; i++) - { - if (temp != -1) - { - rand = new Random(i * temp * ((int)DateTime.Now.Ticks)); - } - int t = rand.Next(allCharArray.Length); - if (temp == t) - { - return MakeNumCode(length); - } - temp = t; - randomCode += allCharArray[t]; - } - return randomCode; - } - - public static string MakeCharCode(int length = 4) - { - char[] allCharArray = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z' }; - string randomCode = ""; - int temp = -1; - - Random rand = new Random(); - for (int i = 0; i < length; i++) - { - if (temp != -1) - { - rand = new Random(i * temp * ((int)DateTime.Now.Ticks)); - } - int t = rand.Next(allCharArray.Length); - if (temp == t) - { - return MakeCharCode(length); - } - temp = t; - randomCode += allCharArray[t]; - } - return randomCode; - } - - public static byte[] GenerateCodeImg(string code) - { - int Gheight = (int)(code.Length * 15) + 10; - - //gheight为图片宽度,根据字符长度自动更改图片宽度 - using (var img = new Bitmap(Gheight, 22)) - { - using (var g = Graphics.FromImage(img)) - { - SolidBrush whiteBrush = new SolidBrush(Color.White); - g.FillRectangle(whiteBrush, 0, 0, Gheight, 22); - int i = 0; - foreach (char ch in code.ToCharArray()) - { - g.DrawString(ch.ToString(), - new Font("Arial", 13, FontStyle.Italic), - new SolidBrush(Color.FromArgb(0, 0, 0)), - i * 15, - 0); - i++; - } - - //在矩形内绘制字串(字串,字体,画笔颜色,左上x.左上y) - System.IO.MemoryStream ms = new System.IO.MemoryStream(); - img.Save(ms, ImageFormat.Jpeg); - return ms.ToArray(); - } - } - } - } +using System; +using System.Drawing; +using System.Drawing.Imaging; + +namespace Hncore.Infrastructure.Common +{ + public class ValidateCodeHelper + { + public static string MakeCode(int length = 4) + { + char[] allCharArray = new char[] { '2', '3', '4', '5', '6', '7', '8', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z' }; + string randomCode = ""; + int temp = -1; + + Random rand = new Random(); + for (int i = 0; i < length; i++) + { + if (temp != -1) + { + rand = new Random(i * temp * ((int)DateTime.Now.Ticks)); + } + int t = rand.Next(allCharArray.Length); + if (temp == t) + { + return MakeCode(length); + } + temp = t; + randomCode += allCharArray[t]; + } + return randomCode; + } + public static string MakeNumCode(int length = 4) + { + char[] allCharArray = new char[] { '1','2', '3', '4', '5', '6', '7', '8','9'}; + string randomCode = ""; + int temp = -1; + + Random rand = new Random(); + for (int i = 0; i < length; i++) + { + if (temp != -1) + { + rand = new Random(i * temp * ((int)DateTime.Now.Ticks)); + } + int t = rand.Next(allCharArray.Length); + if (temp == t) + { + return MakeNumCode(length); + } + temp = t; + randomCode += allCharArray[t]; + } + return randomCode; + } + + public static string MakeCharCode(int length = 4) + { + char[] allCharArray = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z' }; + string randomCode = ""; + int temp = -1; + + Random rand = new Random(); + for (int i = 0; i < length; i++) + { + if (temp != -1) + { + rand = new Random(i * temp * ((int)DateTime.Now.Ticks)); + } + int t = rand.Next(allCharArray.Length); + if (temp == t) + { + return MakeCharCode(length); + } + temp = t; + randomCode += allCharArray[t]; + } + return randomCode; + } + + public static byte[] GenerateCodeImg(string code) + { + int Gheight = (int)(code.Length * 15) + 10; + + //gheight为图片宽度,根据字符长度自动更改图片宽度 + using (var img = new Bitmap(Gheight, 22)) + { + using (var g = Graphics.FromImage(img)) + { + SolidBrush whiteBrush = new SolidBrush(Color.White); + g.FillRectangle(whiteBrush, 0, 0, Gheight, 22); + int i = 0; + foreach (char ch in code.ToCharArray()) + { + g.DrawString(ch.ToString(), + new Font("Arial", 13, FontStyle.Italic), + new SolidBrush(Color.FromArgb(0, 0, 0)), + i * 15, + 0); + i++; + } + + //在矩形内绘制字串(字串,字体,画笔颜色,左上x.左上y) + System.IO.MemoryStream ms = new System.IO.MemoryStream(); + img.Save(ms, ImageFormat.Jpeg); + return ms.ToArray(); + } + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/DDD/AggregateRoot.cs b/Infrastructure/Hncore.Infrastructure/DDD/AggregateRoot.cs index b527574..3436361 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/AggregateRoot.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/AggregateRoot.cs @@ -1,9 +1,9 @@ -namespace Hncore.Infrastructure.DDD -{ - public abstract class AggregateRoot : Entity, IAggregateRoot - { - public AggregateRoot() - { - } - } +namespace Hncore.Infrastructure.DDD +{ + public abstract class AggregateRoot : Entity, IAggregateRoot + { + public AggregateRoot() + { + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/DDD/Entity.cs b/Infrastructure/Hncore.Infrastructure/DDD/Entity.cs index 6c78ef6..6c2ef93 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/Entity.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/Entity.cs @@ -1,48 +1,48 @@ -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Hncore.Infrastructure.DDD -{ - /// - /// 实现模型抽象基类 - /// - /// 主键数据类型 - public abstract class Entity : IEntity - { - /// - /// 记录数据库主键ID - /// - [JsonProperty("Id")] - public virtual TId Id { get; set; } - } - - public abstract class EntityWithTime : Entity - { - /// - /// 记录添加(创建)时间 - /// - public virtual DateTime CreateTime { get; set; } = DateTime.Now; - - /// - /// 记录最后更新时间 - /// - public virtual DateTime UpdateTime { get; set; } = DateTime.Now; - - /// - /// 记录软删除标记,0.代表正常,1.代表已删除 - /// - public virtual int DeleteTag { get; set; } - - } - - public abstract class EntityWithDelete : Entity,ISoftDelete - { - /// - /// 记录软删除标记,0.代表正常,1.代表已删除 - /// - public virtual int DeleteTag { get; set; } = 0; - - } - +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Hncore.Infrastructure.DDD +{ + /// + /// 实现模型抽象基类 + /// + /// 主键数据类型 + public abstract class Entity : IEntity + { + /// + /// 记录数据库主键ID + /// + [JsonProperty("Id")] + public virtual TId Id { get; set; } + } + + public abstract class EntityWithTime : Entity + { + /// + /// 记录添加(创建)时间 + /// + public virtual DateTime CreateTime { get; set; } = DateTime.Now; + + /// + /// 记录最后更新时间 + /// + public virtual DateTime UpdateTime { get; set; } = DateTime.Now; + + /// + /// 记录软删除标记,0.代表正常,1.代表已删除 + /// + public virtual int DeleteTag { get; set; } + + } + + public abstract class EntityWithDelete : Entity,ISoftDelete + { + /// + /// 记录软删除标记,0.代表正常,1.代表已删除 + /// + public virtual int DeleteTag { get; set; } = 0; + + } + } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/DDD/IAggregateRoot.cs b/Infrastructure/Hncore.Infrastructure/DDD/IAggregateRoot.cs index b10c5bd..a4bd488 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/IAggregateRoot.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/IAggregateRoot.cs @@ -1,6 +1,6 @@ -namespace Hncore.Infrastructure.DDD -{ - public interface IAggregateRoot : IEntity - { - } +namespace Hncore.Infrastructure.DDD +{ + public interface IAggregateRoot : IEntity + { + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/DDD/IEntity.cs b/Infrastructure/Hncore.Infrastructure/DDD/IEntity.cs index 01dac88..43ae0c9 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/IEntity.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/IEntity.cs @@ -1,16 +1,16 @@ -using System; - -namespace Hncore.Infrastructure.DDD -{ - public interface IEntity - { - - } - public interface IEntity: IEntity - { - TId Id - { - get; - } - } -} +using System; + +namespace Hncore.Infrastructure.DDD +{ + public interface IEntity + { + + } + public interface IEntity: IEntity + { + TId Id + { + get; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/DDD/IQuery.cs b/Infrastructure/Hncore.Infrastructure/DDD/IQuery.cs index aee99c0..3aeb4f6 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/IQuery.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/IQuery.cs @@ -1,40 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Hncore.Infrastructure.Data; -using Microsoft.EntityFrameworkCore; - -namespace Hncore.Infrastructure.DDD -{ - public interface IQuery where TEntity : IEntity - { - TEntity GetOne(Expression> condition); - - Task GetOneAsync(Expression> condition); - - PageData GetList(Expression> condition, int pagesize, int pageindex, bool istotal); - - Task> GetListAsync(Expression> condition, int pagesize, int pageindex, bool istotal); - - List GetList(Expression> condition); - - Task> GetListAsync(Expression> condition); - - bool Exists(Expression> condition); - - Task ExistsAsync(Expression> condition); - - List TopN(Expression> condition, int topN); - - Task> TopNAsync(Expression> condition, int topN); - - IQueryable GetListQueryable(Expression> condition); - - IQueryable GetQueryable(); - - DbContext DbContext(); - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Hncore.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; + +namespace Hncore.Infrastructure.DDD +{ + public interface IQuery where TEntity : IEntity + { + TEntity GetOne(Expression> condition); + + Task GetOneAsync(Expression> condition); + + PageData GetList(Expression> condition, int pagesize, int pageindex, bool istotal); + + Task> GetListAsync(Expression> condition, int pagesize, int pageindex, bool istotal); + + List GetList(Expression> condition); + + Task> GetListAsync(Expression> condition); + + bool Exists(Expression> condition); + + Task ExistsAsync(Expression> condition); + + List TopN(Expression> condition, int topN); + + Task> TopNAsync(Expression> condition, int topN); + + IQueryable GetListQueryable(Expression> condition); + + IQueryable GetQueryable(); + + DbContext DbContext(); + + } +} diff --git a/Infrastructure/Hncore.Infrastructure/DDD/IRepository.cs b/Infrastructure/Hncore.Infrastructure/DDD/IRepository.cs index 72f24c7..758bb19 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/IRepository.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/IRepository.cs @@ -1,43 +1,43 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.DDD -{ - public interface IRepository where TEntity : IEntity - { - TEntity FindById(TId id); - - Task FindByIdAsync(TId id); - - void Add(TEntity entity); - - Task AddAsync(TEntity entity); - /// - /// 批量添加 - /// - /// - void AddRange(List entity); - /// - /// 批量添加 - /// - /// - Task AddRangeAsync(List entity); - /// - /// 批量修改 - /// - /// - void UpdateRange(List entity); - /// - /// 批量删除 - /// - /// - void RemoveRange(List entity); - - void Remove(TEntity entity); - void Update(TEntity entity); - - - IQueryable GetQueryable(); - } +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.DDD +{ + public interface IRepository where TEntity : IEntity + { + TEntity FindById(TId id); + + Task FindByIdAsync(TId id); + + void Add(TEntity entity); + + Task AddAsync(TEntity entity); + /// + /// 批量添加 + /// + /// + void AddRange(List entity); + /// + /// 批量添加 + /// + /// + Task AddRangeAsync(List entity); + /// + /// 批量修改 + /// + /// + void UpdateRange(List entity); + /// + /// 批量删除 + /// + /// + void RemoveRange(List entity); + + void Remove(TEntity entity); + void Update(TEntity entity); + + + IQueryable GetQueryable(); + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/DDD/ISoftDelete.cs b/Infrastructure/Hncore.Infrastructure/DDD/ISoftDelete.cs index 55f49f2..1c2b80d 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/ISoftDelete.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/ISoftDelete.cs @@ -1,7 +1,7 @@ -namespace Hncore.Infrastructure.DDD -{ - public interface ISoftDelete - { - int DeleteTag { get; set; } - } +namespace Hncore.Infrastructure.DDD +{ + public interface ISoftDelete + { + int DeleteTag { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/DDD/ITenant.cs b/Infrastructure/Hncore.Infrastructure/DDD/ITenant.cs index 21e4e64..5cd971b 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/ITenant.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/ITenant.cs @@ -1,7 +1,7 @@ -namespace Hncore.Infrastructure.DDD -{ - public interface ITenant - { - int TenantId { get; set; } - } +namespace Hncore.Infrastructure.DDD +{ + public interface ITenant + { + int TenantId { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/DDD/ITenantStore.cs b/Infrastructure/Hncore.Infrastructure/DDD/ITenantStore.cs index a173b68..0c3cd68 100644 --- a/Infrastructure/Hncore.Infrastructure/DDD/ITenantStore.cs +++ b/Infrastructure/Hncore.Infrastructure/DDD/ITenantStore.cs @@ -1,7 +1,7 @@ -namespace Hncore.Infrastructure.DDD -{ - public interface ITenantStore - { - int StoreId { get; set; } - } +namespace Hncore.Infrastructure.DDD +{ + public interface ITenantStore + { + int StoreId { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Data/BusinessException.cs b/Infrastructure/Hncore.Infrastructure/Data/BusinessException.cs index f41880c..9472674 100644 --- a/Infrastructure/Hncore.Infrastructure/Data/BusinessException.cs +++ b/Infrastructure/Hncore.Infrastructure/Data/BusinessException.cs @@ -1,29 +1,29 @@ -using System; -using Hncore.Infrastructure.WebApi; - -namespace Hncore.Infrastructure.Data -{ - public class BusinessException : Exception - { - public ResultCode Code { get; } = ResultCode.C_UNKNOWN_ERROR; - - public BusinessException(string message) : base(message) - { - } - - public BusinessException(ResultCode code, string message = "") : base(message) - { - Code = code; - } - - public static void Throw(string message = "") - { - throw new BusinessException(message); - } - - public static void Throw(ResultCode code, string message = "") - { - throw new BusinessException(code, message); - } - } +using System; +using Hncore.Infrastructure.WebApi; + +namespace Hncore.Infrastructure.Data +{ + public class BusinessException : Exception + { + public ResultCode Code { get; } = ResultCode.C_UNKNOWN_ERROR; + + public BusinessException(string message) : base(message) + { + } + + public BusinessException(ResultCode code, string message = "") : base(message) + { + Code = code; + } + + public static void Throw(string message = "") + { + throw new BusinessException(message); + } + + public static void Throw(ResultCode code, string message = "") + { + throw new BusinessException(code, message); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Data/HttpException.cs b/Infrastructure/Hncore.Infrastructure/Data/HttpException.cs index b9bf6a8..fc633ee 100644 --- a/Infrastructure/Hncore.Infrastructure/Data/HttpException.cs +++ b/Infrastructure/Hncore.Infrastructure/Data/HttpException.cs @@ -1,17 +1,17 @@ -using System; -using System.Net; - -namespace Hncore.Infrastructure.Data -{ - public class HttpException: Exception - { - public HttpStatusCode HttpStatusCode { get; set; } - - public string Content { get; set; } - - public HttpException(HttpStatusCode httpStatusCode) - { - HttpStatusCode = httpStatusCode; - } - } +using System; +using System.Net; + +namespace Hncore.Infrastructure.Data +{ + public class HttpException: Exception + { + public HttpStatusCode HttpStatusCode { get; set; } + + public string Content { get; set; } + + public HttpException(HttpStatusCode httpStatusCode) + { + HttpStatusCode = httpStatusCode; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Data/PageData.cs b/Infrastructure/Hncore.Infrastructure/Data/PageData.cs index ed50c4f..af9c74b 100644 --- a/Infrastructure/Hncore.Infrastructure/Data/PageData.cs +++ b/Infrastructure/Hncore.Infrastructure/Data/PageData.cs @@ -1,40 +1,40 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Hncore.Infrastructure.Data -{ - public interface IPageData - { - /// - /// 总行数 - /// - int RowCount { get; set; } - - } - /// - /// 分页数据集合 - /// - public class PageData - { - public PageData() - { - List = new List(); - } - - public PageData(int rowCount, List data) - { - this.RowCount = rowCount; - this.List = data; - } - - /// - /// 总行数 - /// - public int RowCount { get; set; } - - /// - /// 当前页数据集合 - /// - public List List { get; set; } - } -} +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Hncore.Infrastructure.Data +{ + public interface IPageData + { + /// + /// 总行数 + /// + int RowCount { get; set; } + + } + /// + /// 分页数据集合 + /// + public class PageData + { + public PageData() + { + List = new List(); + } + + public PageData(int rowCount, List data) + { + this.RowCount = rowCount; + this.List = data; + } + + /// + /// 总行数 + /// + public int RowCount { get; set; } + + /// + /// 当前页数据集合 + /// + public List List { get; set; } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Data/PageQueryable.cs b/Infrastructure/Hncore.Infrastructure/Data/PageQueryable.cs index 5162b5f..c28fb07 100644 --- a/Infrastructure/Hncore.Infrastructure/Data/PageQueryable.cs +++ b/Infrastructure/Hncore.Infrastructure/Data/PageQueryable.cs @@ -1,25 +1,25 @@ -using System.Linq; - -namespace Hncore.Infrastructure.Data -{ - /// - /// 分页数据源 - /// - public class PageQueryable - { - /// - /// 总页数 - /// - public int RowCount { get; set; } - - /// - /// 当前页数据集合 - /// - public IQueryable Data { get; set; } - - public PageData ToList() - { - return new PageData(){List=Data.ToList(),RowCount=RowCount}; - } - } -} +using System.Linq; + +namespace Hncore.Infrastructure.Data +{ + /// + /// 分页数据源 + /// + public class PageQueryable + { + /// + /// 总页数 + /// + public int RowCount { get; set; } + + /// + /// 当前页数据集合 + /// + public IQueryable Data { get; set; } + + public PageData ToList() + { + return new PageData(){List=Data.ToList(),RowCount=RowCount}; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Data/ResultMessage.cs b/Infrastructure/Hncore.Infrastructure/Data/ResultMessage.cs index 8716a62..5a41963 100644 --- a/Infrastructure/Hncore.Infrastructure/Data/ResultMessage.cs +++ b/Infrastructure/Hncore.Infrastructure/Data/ResultMessage.cs @@ -1,48 +1,48 @@ - - -using System; - -namespace Hncore.Infrastructure.Data -{ - public class ResultMessage - { - public ResultMessage() - { - Success = true; - } - - public string Message { get; set; } = ""; - - public bool Success { get; set; } - - public string Code { get; set; } = ""; - - public Action CallBack { get; set; } = null; - - public object Data { get; set; } = null; - - public ResultMessage(bool success, string message) - { - this.Success = success; - this.Message = message; - } - - public ResultMessage(bool success, string message,object data) - { - this.Success = success; - this.Message = message; - this.Data = data; - } - - public ResultMessage(bool success) - { - this.Success = success; - } - - public ResultMessage(string message) - { - Success = true; - this.Message = message; - } - } -} + + +using System; + +namespace Hncore.Infrastructure.Data +{ + public class ResultMessage + { + public ResultMessage() + { + Success = true; + } + + public string Message { get; set; } = ""; + + public bool Success { get; set; } + + public string Code { get; set; } = ""; + + public Action CallBack { get; set; } = null; + + public object Data { get; set; } = null; + + public ResultMessage(bool success, string message) + { + this.Success = success; + this.Message = message; + } + + public ResultMessage(bool success, string message,object data) + { + this.Success = success; + this.Message = message; + this.Data = data; + } + + public ResultMessage(bool success) + { + this.Success = success; + } + + public ResultMessage(string message) + { + Success = true; + this.Message = message; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Data/TransactionsHelper.cs b/Infrastructure/Hncore.Infrastructure/Data/TransactionsHelper.cs index d17c96d..f1ad18d 100644 --- a/Infrastructure/Hncore.Infrastructure/Data/TransactionsHelper.cs +++ b/Infrastructure/Hncore.Infrastructure/Data/TransactionsHelper.cs @@ -1,24 +1,24 @@ -using System; - -namespace Hncore.Infrastructure.Data -{ - public class TransactionsHelper - { - public static void NoLockInvokeDB(Action action) - { - var transactionOptions = new System.Transactions.TransactionOptions(); - transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted; - using (var transactionScope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions)) - { - try - { - action(); - } - finally - { - transactionScope.Complete(); - } - } - } - } +using System; + +namespace Hncore.Infrastructure.Data +{ + public class TransactionsHelper + { + public static void NoLockInvokeDB(Action action) + { + var transactionOptions = new System.Transactions.TransactionOptions(); + transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted; + using (var transactionScope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions)) + { + try + { + action(); + } + finally + { + transactionScope.Complete(); + } + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/DbContextBase.cs b/Infrastructure/Hncore.Infrastructure/EF/DbContextBase.cs index 4b44e9b..eec0568 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/DbContextBase.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/DbContextBase.cs @@ -1,119 +1,119 @@ -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.DDD; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using System; -using System.Linq; - - -namespace Hncore.Infrastructure.EF -{ - /// - /// 上下文构造器的基类 - /// - public class DbContextBase : DbContext - { - private IHttpContextAccessor _httpContextAccessor; - - private bool _enabledLog = false; - - private int _tenantid = 0; - - private int _storeId = 0; - - private bool _root = false; - - private bool _allow = false; - - public DbContextBase(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options) - { - _httpContextAccessor = httpContextAccessor; - - if (UseTenantFilter()) - { - ManageUserInfo manageUserInfo = _httpContextAccessor.HttpContext.Request.GetManageUserInfo(); - - if (manageUserInfo != null) - { - _tenantid = manageUserInfo.TenantId; - _storeId = manageUserInfo.StoreId; - } - } - else - { - _allow = true; - } - } - - private bool UseTenantFilter() - { - if (_httpContextAccessor == null || _httpContextAccessor.HttpContext == null) - { - return false; - } - - return _httpContextAccessor.HttpContext.Items.ContainsKey("AuthPassedFilterName") - && _httpContextAccessor.HttpContext.Items["AuthPassedFilterName"].ToString() == "ManageAuth"; - } - - - /// - /// model构造器 创建实体映射 - /// - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - if (!EnvironmentVariableHelper.IsAspNetCoreProduction) - { - LogHelper.Debug("进入DbContextBase的OnModelCreating函数", - $"UseGlobalManageAuthFilter:{GlobalData.UseGlobalManageAuthFilter}\ntoken:{_httpContextAccessor?.HttpContext?.Request?.GetManageUserInfo()?.ToJson(true)}"); - } - - foreach (var type in modelBuilder.Model.GetEntityTypes()) - { - if (typeof(ISoftDelete).IsAssignableFrom(type.ClrType)) - { - modelBuilder.Entity(type.ClrType).AddQueryFilter(t => t.DeleteTag == 0); - } - //if (typeof(ITenant).IsAssignableFrom(type.ClrType)) - //{ - // modelBuilder.Entity(type.ClrType).AddQueryFilter(t => _allow || t.TenantId == _tenantid); - //} - - //if (typeof(ITenantStore).IsAssignableFrom(type.ClrType)) - //{ - // modelBuilder.Entity(type.ClrType) - // .AddQueryFilter(t => _storeId == 0|| t.StoreId==_storeId); - //} - } - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - if (_httpContextAccessor?.HttpContext?.Request?.Headers != null) - { - if (_httpContextAccessor.HttpContext.Request.Headers.ContainsKey("enable-ef-log")) - { - if (_httpContextAccessor.HttpContext.Request.Headers.ContainsKey("enable-ef-log").ToBool()) - { - if (_enabledLog == false) - { - optionsBuilder.EnableDebugTrace(_httpContextAccessor); - _enabledLog = true; - } - } - } - } -#if DEBUG - Console.WriteLine("当前为debug模式,开启EF DebugTrace"); - optionsBuilder.EnableDebugTrace(null); -#endif - } - - } +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.DDD; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + + +namespace Hncore.Infrastructure.EF +{ + /// + /// 上下文构造器的基类 + /// + public class DbContextBase : DbContext + { + private IHttpContextAccessor _httpContextAccessor; + + private bool _enabledLog = false; + + private int _tenantid = 0; + + private int _storeId = 0; + + private bool _root = false; + + private bool _allow = false; + + public DbContextBase(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options) + { + _httpContextAccessor = httpContextAccessor; + + if (UseTenantFilter()) + { + ManageUserInfo manageUserInfo = _httpContextAccessor.HttpContext.Request.GetManageUserInfo(); + + if (manageUserInfo != null) + { + _tenantid = manageUserInfo.TenantId; + _storeId = manageUserInfo.StoreId; + } + } + else + { + _allow = true; + } + } + + private bool UseTenantFilter() + { + if (_httpContextAccessor == null || _httpContextAccessor.HttpContext == null) + { + return false; + } + + return _httpContextAccessor.HttpContext.Items.ContainsKey("AuthPassedFilterName") + && _httpContextAccessor.HttpContext.Items["AuthPassedFilterName"].ToString() == "ManageAuth"; + } + + + /// + /// model构造器 创建实体映射 + /// + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + if (!EnvironmentVariableHelper.IsAspNetCoreProduction) + { + LogHelper.Debug("进入DbContextBase的OnModelCreating函数", + $"UseGlobalManageAuthFilter:{GlobalData.UseGlobalManageAuthFilter}\ntoken:{_httpContextAccessor?.HttpContext?.Request?.GetManageUserInfo()?.ToJson(true)}"); + } + + foreach (var type in modelBuilder.Model.GetEntityTypes()) + { + if (typeof(ISoftDelete).IsAssignableFrom(type.ClrType)) + { + modelBuilder.Entity(type.ClrType).AddQueryFilter(t => t.DeleteTag == 0); + } + //if (typeof(ITenant).IsAssignableFrom(type.ClrType)) + //{ + // modelBuilder.Entity(type.ClrType).AddQueryFilter(t => _allow || t.TenantId == _tenantid); + //} + + //if (typeof(ITenantStore).IsAssignableFrom(type.ClrType)) + //{ + // modelBuilder.Entity(type.ClrType) + // .AddQueryFilter(t => _storeId == 0|| t.StoreId==_storeId); + //} + } + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (_httpContextAccessor?.HttpContext?.Request?.Headers != null) + { + if (_httpContextAccessor.HttpContext.Request.Headers.ContainsKey("enable-ef-log")) + { + if (_httpContextAccessor.HttpContext.Request.Headers.ContainsKey("enable-ef-log").ToBool()) + { + if (_enabledLog == false) + { + optionsBuilder.EnableDebugTrace(_httpContextAccessor); + _enabledLog = true; + } + } + } + } +#if DEBUG + Console.WriteLine("当前为debug模式,开启EF DebugTrace"); + optionsBuilder.EnableDebugTrace(null); +#endif + } + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/DbContextExtension.cs b/Infrastructure/Hncore.Infrastructure/EF/DbContextExtension.cs index a1f5692..26cfe37 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/DbContextExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/DbContextExtension.cs @@ -1,228 +1,228 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Data.Common; -using System.Linq; -using System.Reflection; - -namespace Hncore.Infrastructure.Extension -{ - /// - /// EF上下文对象扩展类 - /// - /// - public static class DbContextExtension - { - /// - /// 执行SQL返回受影响的行数 - /// - public static int ExecSqlNoQuery(this DbContext db, string sql, DbParameter[] sqlParams = null) - { - return ExecuteNoQuery(db, sql, sqlParams); - } - /// - /// 执行存储过程返回IEnumerable数据集 - /// - public static IEnumerable ExecProcQuery(this DbContext db, string sql, DbParameter[] sqlParams = null) where T : new() - { - return Execute(db, sql, CommandType.StoredProcedure, sqlParams); - } - /// - /// 执行存储过程返回IEnumerable数据集 - /// - public static DataSet ExecProcDataSet(this DbContext db, string sql, DbParameter[] sqlParams = null) - { - return ExecuteDataSet(db, sql, CommandType.StoredProcedure, sqlParams); - } - /// - /// 执行sql返回IEnumerable数据集 - /// - public static IEnumerable ExecSqlQuery(this DbContext db, string sql, DbParameter[] sqlParams = null) where T : new() - { - return Execute(db, sql, CommandType.Text, sqlParams); - } - /// - /// 执行SQL并返回受影响的行数 - /// - /// - /// - /// - /// - private static int ExecuteNoQuery(this DbContext db, string sql, DbParameter[] sqlParams) - { - DbConnection connection = db.Database.GetDbConnection(); - DbCommand cmd = connection.CreateCommand(); - int result = 0; - db.Database.OpenConnection(); - cmd.CommandText = sql; - cmd.CommandType = CommandType.Text; - if (sqlParams != null) - { - cmd.Parameters.AddRange(sqlParams); - } - result = cmd.ExecuteNonQuery(); - db.Database.CloseConnection(); - return result; - } - /// - /// 执行SQL,返回查询结果 - /// - /// - /// - /// - /// - /// - /// - private static IEnumerable Execute(this DbContext db, string sql, CommandType type, DbParameter[] sqlParams) where T : new() - { - DbConnection connection = db.Database.GetDbConnection(); - DbCommand cmd = connection.CreateCommand(); - DataTable dt = new DataTable(); - try - { - db.Database.OpenConnection(); - cmd.CommandText = sql; - cmd.CommandType = type; - if (sqlParams != null) - { - cmd.Parameters.AddRange(sqlParams); - } - using (DbDataReader reader = cmd.ExecuteReader()) - { - dt.Load(reader); - } - } - finally - { - db.Database.CloseConnection(); - } - return dt.ToCollection(); - } - /// - /// 执行SQL,返回查询结果 - /// - /// - /// - /// - /// - /// - /// - private static DataSet ExecuteDataSet(this DbContext db, string sql, CommandType type, DbParameter[] sqlParams) - { - DbConnection connection = db.Database.GetDbConnection(); - DbCommand cmd = connection.CreateCommand(); - db.Database.OpenConnection(); - cmd.CommandText = sql; - cmd.CommandType = type; - if (sqlParams != null) - { - cmd.Parameters.AddRange(sqlParams); - } - DataSet ds = new DataSet(); - using (DbDataReader reader = cmd.ExecuteReader()) - { - ds.Load(reader,LoadOption.PreserveChanges,"data","info"); - } - db.Database.CloseConnection(); - return ds; - } - } - - /// - /// DataTable扩展类 - /// - /// - public static class ExtendDataTable - { - /// - /// 将对象转换成DataTable - /// - /// 源对象类型 - /// 源对象列表 - /// 转换后的DataTable - /// - public static DataTable ToDataTable(this IEnumerable data) - { - PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); - var table = new DataTable(); - foreach (PropertyDescriptor prop in properties) - table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); - foreach (T item in data) - { - DataRow row = table.NewRow(); - foreach (PropertyDescriptor prop in properties) - row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; - table.Rows.Add(row); - } - return table; - } - - /// - /// 将DataTable首行转换成目标对象 - /// - /// 目标对象类型 - /// 源DataTable对象 - /// 转换后的目标对象 - /// - public static T ToEntity(this DataTable dt) where T : new() - { - IEnumerable entities = dt.ToCollection(); - return entities.FirstOrDefault(); - } - - /// - /// 将DataTable转换成目标对象列表 - /// - /// 目标对象类型 - /// 源DataTable对象 - /// 转换后的目标对象列表 - /// - public static IEnumerable ToCollection(this DataTable dt) where T : new() - { - if (dt == null || dt.Rows.Count == 0) - { - return Enumerable.Empty(); - } - IList ts = new List(); - // 获得此模型的类型 - Type type = typeof(T); - string tempName = string.Empty; - foreach (DataRow dr in dt.Rows) - { - T t = new T(); - PropertyInfo[] propertys = t.GetType().GetProperties(); - foreach (PropertyInfo pi in propertys) - { - tempName = pi.Name; - //检查DataTable是否包含此列(列名==对象的属性名) - if (dt.Columns.Contains(tempName)) - { - // 判断此属性是否有Setter - if (!pi.CanWrite) continue;//该属性不可写,直接跳出 - object value = dr[tempName]; - if (value != DBNull.Value) - { - if (!pi.PropertyType.IsGenericType) - { - value = Convert.ChangeType(value, pi.PropertyType); - pi.SetValue(t, value); - } - else { - Type genericTypeDefinition = pi.PropertyType.GetGenericTypeDefinition(); - if (genericTypeDefinition == typeof(Nullable<>)) - { - value = Convert.ChangeType(value, Nullable.GetUnderlyingType(pi.PropertyType)); - pi.SetValue(t, value); - } - } - } - } - } - ts.Add(t); - } - return ts; - } - } -} +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Reflection; + +namespace Hncore.Infrastructure.Extension +{ + /// + /// EF上下文对象扩展类 + /// + /// + public static class DbContextExtension + { + /// + /// 执行SQL返回受影响的行数 + /// + public static int ExecSqlNoQuery(this DbContext db, string sql, DbParameter[] sqlParams = null) + { + return ExecuteNoQuery(db, sql, sqlParams); + } + /// + /// 执行存储过程返回IEnumerable数据集 + /// + public static IEnumerable ExecProcQuery(this DbContext db, string sql, DbParameter[] sqlParams = null) where T : new() + { + return Execute(db, sql, CommandType.StoredProcedure, sqlParams); + } + /// + /// 执行存储过程返回IEnumerable数据集 + /// + public static DataSet ExecProcDataSet(this DbContext db, string sql, DbParameter[] sqlParams = null) + { + return ExecuteDataSet(db, sql, CommandType.StoredProcedure, sqlParams); + } + /// + /// 执行sql返回IEnumerable数据集 + /// + public static IEnumerable ExecSqlQuery(this DbContext db, string sql, DbParameter[] sqlParams = null) where T : new() + { + return Execute(db, sql, CommandType.Text, sqlParams); + } + /// + /// 执行SQL并返回受影响的行数 + /// + /// + /// + /// + /// + private static int ExecuteNoQuery(this DbContext db, string sql, DbParameter[] sqlParams) + { + DbConnection connection = db.Database.GetDbConnection(); + DbCommand cmd = connection.CreateCommand(); + int result = 0; + db.Database.OpenConnection(); + cmd.CommandText = sql; + cmd.CommandType = CommandType.Text; + if (sqlParams != null) + { + cmd.Parameters.AddRange(sqlParams); + } + result = cmd.ExecuteNonQuery(); + db.Database.CloseConnection(); + return result; + } + /// + /// 执行SQL,返回查询结果 + /// + /// + /// + /// + /// + /// + /// + private static IEnumerable Execute(this DbContext db, string sql, CommandType type, DbParameter[] sqlParams) where T : new() + { + DbConnection connection = db.Database.GetDbConnection(); + DbCommand cmd = connection.CreateCommand(); + DataTable dt = new DataTable(); + try + { + db.Database.OpenConnection(); + cmd.CommandText = sql; + cmd.CommandType = type; + if (sqlParams != null) + { + cmd.Parameters.AddRange(sqlParams); + } + using (DbDataReader reader = cmd.ExecuteReader()) + { + dt.Load(reader); + } + } + finally + { + db.Database.CloseConnection(); + } + return dt.ToCollection(); + } + /// + /// 执行SQL,返回查询结果 + /// + /// + /// + /// + /// + /// + /// + private static DataSet ExecuteDataSet(this DbContext db, string sql, CommandType type, DbParameter[] sqlParams) + { + DbConnection connection = db.Database.GetDbConnection(); + DbCommand cmd = connection.CreateCommand(); + db.Database.OpenConnection(); + cmd.CommandText = sql; + cmd.CommandType = type; + if (sqlParams != null) + { + cmd.Parameters.AddRange(sqlParams); + } + DataSet ds = new DataSet(); + using (DbDataReader reader = cmd.ExecuteReader()) + { + ds.Load(reader,LoadOption.PreserveChanges,"data","info"); + } + db.Database.CloseConnection(); + return ds; + } + } + + /// + /// DataTable扩展类 + /// + /// + public static class ExtendDataTable + { + /// + /// 将对象转换成DataTable + /// + /// 源对象类型 + /// 源对象列表 + /// 转换后的DataTable + /// + public static DataTable ToDataTable(this IEnumerable data) + { + PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); + var table = new DataTable(); + foreach (PropertyDescriptor prop in properties) + table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); + foreach (T item in data) + { + DataRow row = table.NewRow(); + foreach (PropertyDescriptor prop in properties) + row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; + table.Rows.Add(row); + } + return table; + } + + /// + /// 将DataTable首行转换成目标对象 + /// + /// 目标对象类型 + /// 源DataTable对象 + /// 转换后的目标对象 + /// + public static T ToEntity(this DataTable dt) where T : new() + { + IEnumerable entities = dt.ToCollection(); + return entities.FirstOrDefault(); + } + + /// + /// 将DataTable转换成目标对象列表 + /// + /// 目标对象类型 + /// 源DataTable对象 + /// 转换后的目标对象列表 + /// + public static IEnumerable ToCollection(this DataTable dt) where T : new() + { + if (dt == null || dt.Rows.Count == 0) + { + return Enumerable.Empty(); + } + IList ts = new List(); + // 获得此模型的类型 + Type type = typeof(T); + string tempName = string.Empty; + foreach (DataRow dr in dt.Rows) + { + T t = new T(); + PropertyInfo[] propertys = t.GetType().GetProperties(); + foreach (PropertyInfo pi in propertys) + { + tempName = pi.Name; + //检查DataTable是否包含此列(列名==对象的属性名) + if (dt.Columns.Contains(tempName)) + { + // 判断此属性是否有Setter + if (!pi.CanWrite) continue;//该属性不可写,直接跳出 + object value = dr[tempName]; + if (value != DBNull.Value) + { + if (!pi.PropertyType.IsGenericType) + { + value = Convert.ChangeType(value, pi.PropertyType); + pi.SetValue(t, value); + } + else { + Type genericTypeDefinition = pi.PropertyType.GetGenericTypeDefinition(); + if (genericTypeDefinition == typeof(Nullable<>)) + { + value = Convert.ChangeType(value, Nullable.GetUnderlyingType(pi.PropertyType)); + pi.SetValue(t, value); + } + } + } + } + } + ts.Add(t); + } + return ts; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EF/DbSetExtension.cs b/Infrastructure/Hncore.Infrastructure/EF/DbSetExtension.cs index 954a49b..574bb9f 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/DbSetExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/DbSetExtension.cs @@ -1,122 +1,122 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.DDD; -using Hncore.Infrastructure.EntitiesExtension; - -namespace Hncore.Infrastructure.EF -{ - public static class DbSetExtension - { - public static TEntity GetOne(this DbSet dbSet, Expression> exp) - where TEntity : class - { - return dbSet.AsNoTracking().FirstOrDefault(exp); - } - - public static Task GetOneAsync(this DbSet dbSet, - Expression> exp) where TEntity : class - { - return dbSet.AsNoTracking().FirstOrDefaultAsync(exp); - } - - public static PageData GetList(this DbSet dbSet, - Expression> exp, int pagesize, int pageindex, bool istotal) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .Where(exp) - .OrderByDescending(t => t.Id) - .ListPager(pagesize, pageindex, istotal); - } - - public static Task> GetListAsync(this DbSet dbSet, - Expression> exp, int pagesize, int pageindex, bool istotal) - where TEntity : class - { - return dbSet.AsNoTracking() - .Where(exp) - .ListPagerAsync(pagesize, pageindex, istotal); - } - - public static List GetList(this DbSet dbSet, Expression> exp) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .Where(exp) - .ToList(); - } - - public static Task> GetListAsync(this DbSet dbSet, - Expression> exp) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .Where(exp) - .ToListAsync(); - } - - public static IQueryable GetListQueryable(this DbSet dbSet, - Expression> exp) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .Where(exp); - } - - public static bool Exists(this DbSet dbSet, Expression> exp) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .Any(exp); - } - - public static Task ExistsAsync(this DbSet dbSet, - Expression> exp) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .AnyAsync(exp); - } - - public static List TopN(this DbSet dbSet, - Expression> condition, int topN) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .Where(condition) - .TopN(topN) - .ToList(); - } - - public static Task> TopNAsync(this DbSet dbSet, - Expression> condition, int topN) - where TEntity : class, IEntity - { - return dbSet.AsNoTracking() - .Where(condition) - .TopN(topN) - .ToListAsync(); - } - - public static IQueryable GetQueryable(this DbSet dbSet) where TEntity : class - { - return dbSet.AsNoTracking(); - } - - public static TEntity FindById(this DbSet dbSet,object id) where TEntity : class - { - return dbSet.Find(id); - } - - public static Task FindByIdAsync(this DbSet dbSet, object id) where TEntity : class - { - return dbSet.FindAsync(id); - } - - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.DDD; +using Hncore.Infrastructure.EntitiesExtension; + +namespace Hncore.Infrastructure.EF +{ + public static class DbSetExtension + { + public static TEntity GetOne(this DbSet dbSet, Expression> exp) + where TEntity : class + { + return dbSet.AsNoTracking().FirstOrDefault(exp); + } + + public static Task GetOneAsync(this DbSet dbSet, + Expression> exp) where TEntity : class + { + return dbSet.AsNoTracking().FirstOrDefaultAsync(exp); + } + + public static PageData GetList(this DbSet dbSet, + Expression> exp, int pagesize, int pageindex, bool istotal) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .Where(exp) + .OrderByDescending(t => t.Id) + .ListPager(pagesize, pageindex, istotal); + } + + public static Task> GetListAsync(this DbSet dbSet, + Expression> exp, int pagesize, int pageindex, bool istotal) + where TEntity : class + { + return dbSet.AsNoTracking() + .Where(exp) + .ListPagerAsync(pagesize, pageindex, istotal); + } + + public static List GetList(this DbSet dbSet, Expression> exp) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .Where(exp) + .ToList(); + } + + public static Task> GetListAsync(this DbSet dbSet, + Expression> exp) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .Where(exp) + .ToListAsync(); + } + + public static IQueryable GetListQueryable(this DbSet dbSet, + Expression> exp) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .Where(exp); + } + + public static bool Exists(this DbSet dbSet, Expression> exp) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .Any(exp); + } + + public static Task ExistsAsync(this DbSet dbSet, + Expression> exp) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .AnyAsync(exp); + } + + public static List TopN(this DbSet dbSet, + Expression> condition, int topN) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .Where(condition) + .TopN(topN) + .ToList(); + } + + public static Task> TopNAsync(this DbSet dbSet, + Expression> condition, int topN) + where TEntity : class, IEntity + { + return dbSet.AsNoTracking() + .Where(condition) + .TopN(topN) + .ToListAsync(); + } + + public static IQueryable GetQueryable(this DbSet dbSet) where TEntity : class + { + return dbSet.AsNoTracking(); + } + + public static TEntity FindById(this DbSet dbSet,object id) where TEntity : class + { + return dbSet.Find(id); + } + + public static Task FindByIdAsync(this DbSet dbSet, object id) where TEntity : class + { + return dbSet.FindAsync(id); + } + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/EntityMapBase.cs b/Infrastructure/Hncore.Infrastructure/EF/EntityMapBase.cs index 56c8e9c..034d1fd 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/EntityMapBase.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/EntityMapBase.cs @@ -1,25 +1,25 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Hncore.Infrastructure.EF -{ - public interface IEntityMap - { - void Map(ModelBuilder builder); - } - - public interface IEntityMap : IEntityMap where TEntityType : class - { - void Map(EntityTypeBuilder builder); - } - - public abstract class EntityMapBase : IEntityMap where T : class - { - public abstract void Map(EntityTypeBuilder builder); - - public void Map(ModelBuilder builder) - { - Map(builder.Entity()); - } - } +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Hncore.Infrastructure.EF +{ + public interface IEntityMap + { + void Map(ModelBuilder builder); + } + + public interface IEntityMap : IEntityMap where TEntityType : class + { + void Map(EntityTypeBuilder builder); + } + + public abstract class EntityMapBase : IEntityMap where T : class + { + public abstract void Map(EntityTypeBuilder builder); + + public void Map(ModelBuilder builder) + { + Map(builder.Entity()); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/Extensions/AutoMap.cs b/Infrastructure/Hncore.Infrastructure/EF/Extensions/AutoMap.cs index e5cd598..4652d83 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/Extensions/AutoMap.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/Extensions/AutoMap.cs @@ -1,47 +1,47 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.EntityFrameworkCore; - -namespace Hncore.Infrastructure.EF -{ - public static class AutoMapExtensions - { - private static object syncRoot = new object(); - private static ConcurrentDictionary maps; - - public static void AutoMap(this ModelBuilder modelBuilder, Type assType) - { - if (maps == null) - { - lock (syncRoot) - { - if (maps == null) - { - maps = new ConcurrentDictionary(); - - Type mappingInterface = typeof(IEntityMap<>); - - var mappingTypes = assType.GetTypeInfo().Assembly.GetTypes() - .Where(x => !x.IsAbstract - && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType - && y.GetGenericTypeDefinition() == - mappingInterface)); - - foreach (var map in mappingTypes.Select(Activator.CreateInstance).Cast()) - { - maps.TryAdd(map, null); - } - } - } - } - - foreach (var map in maps.Keys) - { - map.Map(modelBuilder); - } - } - } +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.EntityFrameworkCore; + +namespace Hncore.Infrastructure.EF +{ + public static class AutoMapExtensions + { + private static object syncRoot = new object(); + private static ConcurrentDictionary maps; + + public static void AutoMap(this ModelBuilder modelBuilder, Type assType) + { + if (maps == null) + { + lock (syncRoot) + { + if (maps == null) + { + maps = new ConcurrentDictionary(); + + Type mappingInterface = typeof(IEntityMap<>); + + var mappingTypes = assType.GetTypeInfo().Assembly.GetTypes() + .Where(x => !x.IsAbstract + && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType + && y.GetGenericTypeDefinition() == + mappingInterface)); + + foreach (var map in mappingTypes.Select(Activator.CreateInstance).Cast()) + { + maps.TryAdd(map, null); + } + } + } + } + + foreach (var map in maps.Keys) + { + map.Map(modelBuilder); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/Extensions/DebugLog.cs b/Infrastructure/Hncore.Infrastructure/EF/Extensions/DebugLog.cs index f525444..3e4e997 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/Extensions/DebugLog.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/Extensions/DebugLog.cs @@ -1,121 +1,121 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Text; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Hncore.Infrastructure.Core.Web; - -namespace Hncore.Infrastructure.EF -{ - public class TraceLogger : ILogger - { - private readonly string categoryName; - private IHttpContextAccessor _httpContextAccessor; - - public TraceLogger(string categoryName, IHttpContextAccessor httpContextAccessor) - { - this.categoryName = categoryName; - this._httpContextAccessor = httpContextAccessor; - } - - public bool IsEnabled(LogLevel logLevel) => true; - - public void Log( - LogLevel logLevel, - EventId eventId, - TState state, - Exception exception, - Func formatter) - { - if (logLevel == LogLevel.Information && categoryName == "Microsoft.EntityFrameworkCore.Database.Command") - { - Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 执行sql:"); - - if (exception != null) - { - Console.WriteLine("发生异常:\n" + exception); - } - else - { - if (state.GetType().Name == "LogValues`6") - { - var paramText = state.GetType().GetField("_value1").GetValue(state).ToString(); - var sql = state.GetType().GetField("_value5").GetValue(state).ToString(); - - var paramList = paramText.RegexMatches("@__.*?='.*?'"); - - paramList.ForEach(param => - { - var arr = param.Split('='); - - sql = sql.Replace(arr[0], arr[1]); - }); - - Console.WriteLine(sql); - - if (_httpContextAccessor?.HttpContext?.Request?.Headers != null - && _httpContextAccessor.HttpContext.Request.Headers - .ContainsKey("enable-ef-log").ToBool()) - { - StringBuilder log = new StringBuilder(); - - log.Append("请求URL:" + _httpContextAccessor.HttpContext.Request.GetAbsoluteUri() + ""); - log.Append("\nMethod:" + _httpContextAccessor.HttpContext.Request.Method + "\n"); - if (_httpContextAccessor.HttpContext.Request.Method.ToLower() != "get") - { - log.Append("Body:\n" + _httpContextAccessor.HttpContext.Items["___requestbody"] + - "\n------------------------\n"); - } - else - { - log.Append("\n------------------------\n"); - } - - log.Append(sql); - - LogHelper.Debug("efcore日志", log.ToString()); - } - } - } - - //Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {logLevel} {eventId.Id} {this.categoryName}"); - //Console.WriteLine(formatter(state, exception)); - } - } - - public IDisposable BeginScope(TState state) => null; - } - - public class TraceLoggerProvider : ILoggerProvider - { - private IHttpContextAccessor _httpContextAccessor; - - public TraceLoggerProvider(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - public ILogger CreateLogger(string categoryName) => new TraceLogger(categoryName, _httpContextAccessor); - - public void Dispose() - { - } - } - - - public static class DebugLog - { - public static void EnableDebugTrace(this DbContextOptionsBuilder optionsBuilder, - IHttpContextAccessor httpContextAccessor) - { - LoggerFactory loggerFactory = new LoggerFactory(); - loggerFactory.AddProvider(new TraceLoggerProvider(httpContextAccessor)); - optionsBuilder.UseLoggerFactory(loggerFactory); - optionsBuilder.EnableSensitiveDataLogging(); - } - } +using System; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Hncore.Infrastructure.Core.Web; + +namespace Hncore.Infrastructure.EF +{ + public class TraceLogger : ILogger + { + private readonly string categoryName; + private IHttpContextAccessor _httpContextAccessor; + + public TraceLogger(string categoryName, IHttpContextAccessor httpContextAccessor) + { + this.categoryName = categoryName; + this._httpContextAccessor = httpContextAccessor; + } + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log( + LogLevel logLevel, + EventId eventId, + TState state, + Exception exception, + Func formatter) + { + if (logLevel == LogLevel.Information && categoryName == "Microsoft.EntityFrameworkCore.Database.Command") + { + Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 执行sql:"); + + if (exception != null) + { + Console.WriteLine("发生异常:\n" + exception); + } + else + { + if (state.GetType().Name == "LogValues`6") + { + var paramText = state.GetType().GetField("_value1").GetValue(state).ToString(); + var sql = state.GetType().GetField("_value5").GetValue(state).ToString(); + + var paramList = paramText.RegexMatches("@__.*?='.*?'"); + + paramList.ForEach(param => + { + var arr = param.Split('='); + + sql = sql.Replace(arr[0], arr[1]); + }); + + Console.WriteLine(sql); + + if (_httpContextAccessor?.HttpContext?.Request?.Headers != null + && _httpContextAccessor.HttpContext.Request.Headers + .ContainsKey("enable-ef-log").ToBool()) + { + StringBuilder log = new StringBuilder(); + + log.Append("请求URL:" + _httpContextAccessor.HttpContext.Request.GetAbsoluteUri() + ""); + log.Append("\nMethod:" + _httpContextAccessor.HttpContext.Request.Method + "\n"); + if (_httpContextAccessor.HttpContext.Request.Method.ToLower() != "get") + { + log.Append("Body:\n" + _httpContextAccessor.HttpContext.Items["___requestbody"] + + "\n------------------------\n"); + } + else + { + log.Append("\n------------------------\n"); + } + + log.Append(sql); + + LogHelper.Debug("efcore日志", log.ToString()); + } + } + } + + //Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {logLevel} {eventId.Id} {this.categoryName}"); + //Console.WriteLine(formatter(state, exception)); + } + } + + public IDisposable BeginScope(TState state) => null; + } + + public class TraceLoggerProvider : ILoggerProvider + { + private IHttpContextAccessor _httpContextAccessor; + + public TraceLoggerProvider(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public ILogger CreateLogger(string categoryName) => new TraceLogger(categoryName, _httpContextAccessor); + + public void Dispose() + { + } + } + + + public static class DebugLog + { + public static void EnableDebugTrace(this DbContextOptionsBuilder optionsBuilder, + IHttpContextAccessor httpContextAccessor) + { + LoggerFactory loggerFactory = new LoggerFactory(); + loggerFactory.AddProvider(new TraceLoggerProvider(httpContextAccessor)); + optionsBuilder.UseLoggerFactory(loggerFactory); + optionsBuilder.EnableSensitiveDataLogging(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/Extensions/QueryFilter.cs b/Infrastructure/Hncore.Infrastructure/EF/Extensions/QueryFilter.cs index 741ba41..fe4cdcf 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/Extensions/QueryFilter.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/Extensions/QueryFilter.cs @@ -1,50 +1,50 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.DDD; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.EntityFrameworkCore.Metadata.Internal; -using Remotion.Linq.Parsing.ExpressionVisitors; -using Hncore.Infrastructure.Core.Web; - -namespace Hncore.Infrastructure.EF -{ - public static class QueryFilterExtensions - { - public static void AddQueryFilter(this EntityTypeBuilder entityTypeBuilder, - Expression> expression) - { - var parameterType = Expression.Parameter(entityTypeBuilder.Metadata.ClrType); - var expressionFilter = ReplacingExpressionVisitor.Replace( - expression.Parameters.Single(), parameterType, expression.Body); - - var internalEntityTypeBuilder = entityTypeBuilder.GetInternalEntityTypeBuilder(); - if (internalEntityTypeBuilder.Metadata.QueryFilter != null) - { - var currentQueryFilter = internalEntityTypeBuilder.Metadata.QueryFilter; - var currentExpressionFilter = ReplacingExpressionVisitor.Replace( - currentQueryFilter.Parameters.Single(), parameterType, currentQueryFilter.Body); - expressionFilter = Expression.AndAlso(currentExpressionFilter, expressionFilter); - } - - var lambdaExpression = Expression.Lambda(expressionFilter, parameterType); - entityTypeBuilder.HasQueryFilter(lambdaExpression); - } - - internal static InternalEntityTypeBuilder GetInternalEntityTypeBuilder(this EntityTypeBuilder entityTypeBuilder) - { - var internalEntityTypeBuilder = typeof(EntityTypeBuilder) - .GetProperty("Builder", BindingFlags.NonPublic | BindingFlags.Instance)? - .GetValue(entityTypeBuilder) as InternalEntityTypeBuilder; - - return internalEntityTypeBuilder; - } - } +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.DDD; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Remotion.Linq.Parsing.ExpressionVisitors; +using Hncore.Infrastructure.Core.Web; + +namespace Hncore.Infrastructure.EF +{ + public static class QueryFilterExtensions + { + public static void AddQueryFilter(this EntityTypeBuilder entityTypeBuilder, + Expression> expression) + { + var parameterType = Expression.Parameter(entityTypeBuilder.Metadata.ClrType); + var expressionFilter = ReplacingExpressionVisitor.Replace( + expression.Parameters.Single(), parameterType, expression.Body); + + var internalEntityTypeBuilder = entityTypeBuilder.GetInternalEntityTypeBuilder(); + if (internalEntityTypeBuilder.Metadata.QueryFilter != null) + { + var currentQueryFilter = internalEntityTypeBuilder.Metadata.QueryFilter; + var currentExpressionFilter = ReplacingExpressionVisitor.Replace( + currentQueryFilter.Parameters.Single(), parameterType, currentQueryFilter.Body); + expressionFilter = Expression.AndAlso(currentExpressionFilter, expressionFilter); + } + + var lambdaExpression = Expression.Lambda(expressionFilter, parameterType); + entityTypeBuilder.HasQueryFilter(lambdaExpression); + } + + internal static InternalEntityTypeBuilder GetInternalEntityTypeBuilder(this EntityTypeBuilder entityTypeBuilder) + { + var internalEntityTypeBuilder = typeof(EntityTypeBuilder) + .GetProperty("Builder", BindingFlags.NonPublic | BindingFlags.Instance)? + .GetValue(entityTypeBuilder) as InternalEntityTypeBuilder; + + return internalEntityTypeBuilder; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/Extensions/Sql.cs b/Infrastructure/Hncore.Infrastructure/EF/Extensions/Sql.cs index 4663b23..be2a65a 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/Extensions/Sql.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/Extensions/Sql.cs @@ -1,196 +1,196 @@ -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Reflection; -using Microsoft.EntityFrameworkCore; - -namespace Hncore.Infrastructure.EF -{ - public static class Sql - { - /// - /// 执行Reader - /// - /// - /// - /// - public static void Reader(this DbContext dbContext, string sql, Action action) - { - var conn = dbContext.Database.GetDbConnection(); - - try - { - conn.Open(); - - using (var command = conn.CreateCommand()) - { - string query = sql; - - command.CommandText = query; - - using (DbDataReader reader = command.ExecuteReader()) - { - if (reader.HasRows) - { - while (reader.Read()) - { - action(reader); - } - } - } - } - } - finally - { - conn.Close(); - } - } - - /// - /// 执行Query - /// - /// - /// - /// - /// - public static List SqlQuery(this DbContext dbContext, string sql) - { - List list = new List(); - - var conn = dbContext.Database.GetDbConnection(); - - try - { - conn.Open(); - - using (var command = conn.CreateCommand()) - { - string query = sql; - - command.CommandText = query; - - using (DbDataReader reader = command.ExecuteReader()) - { - if (reader.HasRows) - { - list = reader.ReaderToList(); - } - } - } - } - finally - { - conn.Close(); - } - - - return list; - } - - /// - /// 执行Sql命令 - /// - /// - /// - /// - public static int ExecuteSql(this DbContext dbContext, string sql) - { - var conn = dbContext.Database.GetDbConnection(); - int rowAffected = 0; - try - { - conn.Open(); - - using (var command = conn.CreateCommand()) - { - command.CommandText = sql; - rowAffected = command.ExecuteNonQuery(); - } - } - finally - { - conn.Close(); - } - - return rowAffected; - } - - /// - /// DataReader转泛型 - /// - /// 传入的实体类 - /// DataReader对象 - /// - public static List ReaderToList(this DbDataReader objReader) - { - using (objReader) - { - List list = new List(); - - //获取传入的数据类型 - Type modelType = typeof(T); - - //遍历DataReader对象 - while (objReader.Read()) - { - //使用与指定参数匹配最高的构造函数,来创建指定类型的实例 - T model = Activator.CreateInstance(); - for (int i = 0; i < objReader.FieldCount; i++) - { - //判断字段值是否为空或不存在的值 - if (!IsNullOrDBNull(objReader[i])) - { - //匹配字段名 - PropertyInfo pi = modelType.GetProperty(objReader.GetName(i), - BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | - BindingFlags.IgnoreCase); - if (pi != null) - { - //绑定实体对象中同名的字段 - pi.SetValue(model, CheckType(objReader[i], pi.PropertyType), null); - } - } - } - - list.Add(model); - } - - return list; - } - } - - /// - /// 判断指定对象是否是有效值 - /// - /// - /// - private static bool IsNullOrDBNull(object obj) - { - return (obj == null || (obj is DBNull)) ? true : false; - } - - /// - /// 对可空类型进行判断转换 - /// - /// DataReader字段的值 - /// 该字段的类型 - /// - private static object CheckType(object value, Type conversionType) - { - if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) - { - if (value == null) - return null; - System.ComponentModel.NullableConverter nullableConverter = - new System.ComponentModel.NullableConverter(conversionType); - conversionType = nullableConverter.UnderlyingType; - } - - if (typeof(System.Enum).IsAssignableFrom(conversionType)) - { - return Enum.Parse(conversionType, value.ToString()); - } - return Convert.ChangeType(value, conversionType); - } - } +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Reflection; +using Microsoft.EntityFrameworkCore; + +namespace Hncore.Infrastructure.EF +{ + public static class Sql + { + /// + /// 执行Reader + /// + /// + /// + /// + public static void Reader(this DbContext dbContext, string sql, Action action) + { + var conn = dbContext.Database.GetDbConnection(); + + try + { + conn.Open(); + + using (var command = conn.CreateCommand()) + { + string query = sql; + + command.CommandText = query; + + using (DbDataReader reader = command.ExecuteReader()) + { + if (reader.HasRows) + { + while (reader.Read()) + { + action(reader); + } + } + } + } + } + finally + { + conn.Close(); + } + } + + /// + /// 执行Query + /// + /// + /// + /// + /// + public static List SqlQuery(this DbContext dbContext, string sql) + { + List list = new List(); + + var conn = dbContext.Database.GetDbConnection(); + + try + { + conn.Open(); + + using (var command = conn.CreateCommand()) + { + string query = sql; + + command.CommandText = query; + + using (DbDataReader reader = command.ExecuteReader()) + { + if (reader.HasRows) + { + list = reader.ReaderToList(); + } + } + } + } + finally + { + conn.Close(); + } + + + return list; + } + + /// + /// 执行Sql命令 + /// + /// + /// + /// + public static int ExecuteSql(this DbContext dbContext, string sql) + { + var conn = dbContext.Database.GetDbConnection(); + int rowAffected = 0; + try + { + conn.Open(); + + using (var command = conn.CreateCommand()) + { + command.CommandText = sql; + rowAffected = command.ExecuteNonQuery(); + } + } + finally + { + conn.Close(); + } + + return rowAffected; + } + + /// + /// DataReader转泛型 + /// + /// 传入的实体类 + /// DataReader对象 + /// + public static List ReaderToList(this DbDataReader objReader) + { + using (objReader) + { + List list = new List(); + + //获取传入的数据类型 + Type modelType = typeof(T); + + //遍历DataReader对象 + while (objReader.Read()) + { + //使用与指定参数匹配最高的构造函数,来创建指定类型的实例 + T model = Activator.CreateInstance(); + for (int i = 0; i < objReader.FieldCount; i++) + { + //判断字段值是否为空或不存在的值 + if (!IsNullOrDBNull(objReader[i])) + { + //匹配字段名 + PropertyInfo pi = modelType.GetProperty(objReader.GetName(i), + BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | + BindingFlags.IgnoreCase); + if (pi != null) + { + //绑定实体对象中同名的字段 + pi.SetValue(model, CheckType(objReader[i], pi.PropertyType), null); + } + } + } + + list.Add(model); + } + + return list; + } + } + + /// + /// 判断指定对象是否是有效值 + /// + /// + /// + private static bool IsNullOrDBNull(object obj) + { + return (obj == null || (obj is DBNull)) ? true : false; + } + + /// + /// 对可空类型进行判断转换 + /// + /// DataReader字段的值 + /// 该字段的类型 + /// + private static object CheckType(object value, Type conversionType) + { + if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) + { + if (value == null) + return null; + System.ComponentModel.NullableConverter nullableConverter = + new System.ComponentModel.NullableConverter(conversionType); + conversionType = nullableConverter.UnderlyingType; + } + + if (typeof(System.Enum).IsAssignableFrom(conversionType)) + { + return Enum.Parse(conversionType, value.ToString()); + } + return Convert.ChangeType(value, conversionType); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/IQueryDbContext.cs b/Infrastructure/Hncore.Infrastructure/EF/IQueryDbContext.cs index 3ef13a9..1780043 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/IQueryDbContext.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/IQueryDbContext.cs @@ -1,9 +1,9 @@ -using Microsoft.EntityFrameworkCore; - -namespace Hncore.Infrastructure.EF -{ - public interface IQueryDbContext - { - DbContext DbContext { get; } - } +using Microsoft.EntityFrameworkCore; + +namespace Hncore.Infrastructure.EF +{ + public interface IQueryDbContext + { + DbContext DbContext { get; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/IRepositoryDbContext.cs b/Infrastructure/Hncore.Infrastructure/EF/IRepositoryDbContext.cs index 2e86775..6f54ff7 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/IRepositoryDbContext.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/IRepositoryDbContext.cs @@ -1,9 +1,9 @@ -using Microsoft.EntityFrameworkCore; - -namespace Hncore.Infrastructure.EF -{ - public interface IRepositoryDbContext - { - DbContext DbContext { get; } - } +using Microsoft.EntityFrameworkCore; + +namespace Hncore.Infrastructure.EF +{ + public interface IRepositoryDbContext + { + DbContext DbContext { get; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/QueryBase.cs b/Infrastructure/Hncore.Infrastructure/EF/QueryBase.cs index 5e2b9e9..2537b59 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/QueryBase.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/QueryBase.cs @@ -1,90 +1,90 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.DDD; - - -namespace Hncore.Infrastructure.EF -{ - public class QueryBase : IQuery where TEntity : class, IEntity - { - protected DbContext Dbcontext; - - public QueryBase(IQueryDbContext dbContext) - { - Dbcontext = dbContext.DbContext; - } - - public TEntity GetOne(Expression> condition) - { - return Dbcontext.GetOne(condition); - } - - public Task GetOneAsync(Expression> condition) - { - return Dbcontext.GetOneAsync(condition); - } - - public PageData GetList(Expression> condition, int pagesize, int pageindex, - bool istotal) - { - return Dbcontext.GetList(condition, pagesize, pageindex, istotal); - } - - public Task> GetListAsync(Expression> condition, int pagesize, - int pageindex, bool istotal) - { - return Dbcontext.GetListAsync(condition, pagesize, pageindex, istotal); - } - - public List GetList(Expression> condition) - { - return Dbcontext.GetList(condition); - } - - public Task> GetListAsync(Expression> condition) - { - return Dbcontext.GetListAsync(condition); - } - - public List TopN(Expression> condition, int topN) - { - return Dbcontext.TopN(condition, topN); - } - - public Task> TopNAsync(Expression> condition, int topN) - { - return Dbcontext.TopNAsync(condition, topN); - } - - public IQueryable GetListQueryable(Expression> exp) - { - return Dbcontext.GetListQueryable(exp); - } - - public bool Exists(Expression> exp) - { - return Dbcontext.Exists(exp); - } - - public Task ExistsAsync(Expression> condition) - { - return Dbcontext.ExistsAsync(condition); - } - - public IQueryable GetQueryable() - { - return Dbcontext.Set().AsNoTracking(); - } - public DbContext DbContext() - { - return Dbcontext; - } - - - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.DDD; + + +namespace Hncore.Infrastructure.EF +{ + public class QueryBase : IQuery where TEntity : class, IEntity + { + protected DbContext Dbcontext; + + public QueryBase(IQueryDbContext dbContext) + { + Dbcontext = dbContext.DbContext; + } + + public TEntity GetOne(Expression> condition) + { + return Dbcontext.GetOne(condition); + } + + public Task GetOneAsync(Expression> condition) + { + return Dbcontext.GetOneAsync(condition); + } + + public PageData GetList(Expression> condition, int pagesize, int pageindex, + bool istotal) + { + return Dbcontext.GetList(condition, pagesize, pageindex, istotal); + } + + public Task> GetListAsync(Expression> condition, int pagesize, + int pageindex, bool istotal) + { + return Dbcontext.GetListAsync(condition, pagesize, pageindex, istotal); + } + + public List GetList(Expression> condition) + { + return Dbcontext.GetList(condition); + } + + public Task> GetListAsync(Expression> condition) + { + return Dbcontext.GetListAsync(condition); + } + + public List TopN(Expression> condition, int topN) + { + return Dbcontext.TopN(condition, topN); + } + + public Task> TopNAsync(Expression> condition, int topN) + { + return Dbcontext.TopNAsync(condition, topN); + } + + public IQueryable GetListQueryable(Expression> exp) + { + return Dbcontext.GetListQueryable(exp); + } + + public bool Exists(Expression> exp) + { + return Dbcontext.Exists(exp); + } + + public Task ExistsAsync(Expression> condition) + { + return Dbcontext.ExistsAsync(condition); + } + + public IQueryable GetQueryable() + { + return Dbcontext.Set().AsNoTracking(); + } + public DbContext DbContext() + { + return Dbcontext; + } + + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/QueryExtension.cs b/Infrastructure/Hncore.Infrastructure/EF/QueryExtension.cs index ff85a9c..6eb330b 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/QueryExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/QueryExtension.cs @@ -1,110 +1,110 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.DDD; -using Hncore.Infrastructure.EntitiesExtension; - -namespace Hncore.Infrastructure.EF -{ - public static class QueryExtension - { - public static TEntity GetOne(this DbContext dbcontext, Expression> exp) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .FirstOrDefault(exp); - } - - public static Task GetOneAsync(this DbContext dbcontext, - Expression> exp) where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .FirstOrDefaultAsync(exp); - } - - public static PageData GetList(this DbContext dbcontext, - Expression> exp, int pagesize, int pageindex, bool istotal) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Where(exp) - .OrderByDescending(t => t.Id) - .ListPager(pagesize, pageindex, istotal); - } - - public static Task> GetListAsync(this DbContext dbcontext, - Expression> exp, int pagesize, int pageindex, bool istotal) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Where(exp) - .OrderByDescending(t => t.Id) - .ListPagerAsync(pagesize, pageindex, istotal); - } - - public static List GetList(this DbContext dbcontext, Expression> exp) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Where(exp) - .ToList(); - } - - public static Task> GetListAsync(this DbContext dbcontext, - Expression> exp) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Where(exp) - .ToListAsync(); - } - - public static IQueryable GetListQueryable(this DbContext dbcontext, - Expression> exp) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Where(exp); - } - - public static bool Exists(this DbContext dbcontext, Expression> exp) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Any(exp); - } - - public static Task ExistsAsync(this DbContext dbcontext, - Expression> exp) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .AnyAsync(exp); - } - - public static List TopN(this DbContext dbcontext, - Expression> condition, int topN) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Where(condition) - .TopN(topN) - .ToList(); - } - - public static Task> TopNAsync(this DbContext dbcontext, - Expression> condition, int topN) - where TEntity : class, IEntity - { - return dbcontext.Set().AsNoTracking() - .Where(condition) - .TopN(topN) - .ToListAsync(); - } - - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.DDD; +using Hncore.Infrastructure.EntitiesExtension; + +namespace Hncore.Infrastructure.EF +{ + public static class QueryExtension + { + public static TEntity GetOne(this DbContext dbcontext, Expression> exp) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .FirstOrDefault(exp); + } + + public static Task GetOneAsync(this DbContext dbcontext, + Expression> exp) where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .FirstOrDefaultAsync(exp); + } + + public static PageData GetList(this DbContext dbcontext, + Expression> exp, int pagesize, int pageindex, bool istotal) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Where(exp) + .OrderByDescending(t => t.Id) + .ListPager(pagesize, pageindex, istotal); + } + + public static Task> GetListAsync(this DbContext dbcontext, + Expression> exp, int pagesize, int pageindex, bool istotal) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Where(exp) + .OrderByDescending(t => t.Id) + .ListPagerAsync(pagesize, pageindex, istotal); + } + + public static List GetList(this DbContext dbcontext, Expression> exp) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Where(exp) + .ToList(); + } + + public static Task> GetListAsync(this DbContext dbcontext, + Expression> exp) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Where(exp) + .ToListAsync(); + } + + public static IQueryable GetListQueryable(this DbContext dbcontext, + Expression> exp) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Where(exp); + } + + public static bool Exists(this DbContext dbcontext, Expression> exp) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Any(exp); + } + + public static Task ExistsAsync(this DbContext dbcontext, + Expression> exp) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .AnyAsync(exp); + } + + public static List TopN(this DbContext dbcontext, + Expression> condition, int topN) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Where(condition) + .TopN(topN) + .ToList(); + } + + public static Task> TopNAsync(this DbContext dbcontext, + Expression> condition, int topN) + where TEntity : class, IEntity + { + return dbcontext.Set().AsNoTracking() + .Where(condition) + .TopN(topN) + .ToListAsync(); + } + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/RepositoryBase.cs b/Infrastructure/Hncore.Infrastructure/EF/RepositoryBase.cs index 16cb9d7..63e44b5 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/RepositoryBase.cs +++ b/Infrastructure/Hncore.Infrastructure/EF/RepositoryBase.cs @@ -1,90 +1,90 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Hncore.Infrastructure.DDD; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore.ChangeTracking; - -namespace Hncore.Infrastructure.EF -{ - public class RepositoryBase : IRepository - where TEntity : Entity - { - protected DbContext Dbcontext; - - public RepositoryBase(IRepositoryDbContext dbContext) - { - Dbcontext = dbContext.DbContext; - } - - public TEntity FindById(TId id) - { - return Dbcontext.Set().Find(id); - } - - public Task FindByIdAsync(TId id) - { - return Dbcontext.Set().FindAsync(id); - } - - public void Add(TEntity entity) - { - Dbcontext.Set().Add(entity); - } - - - public Task AddAsync(TEntity entity) - { - return Dbcontext.Set().AddAsync(entity); - } - /// - /// 批量添加 - /// - /// - public void AddRange(List entity) - { - Dbcontext.Set().AddRange(entity); - } - /// - /// 批量添加 - /// - /// - public Task AddRangeAsync(List entity) - { - return Dbcontext.Set().AddRangeAsync(entity); - } - /// - /// 批量修改 - /// - /// - public void UpdateRange(List entity) - { - Dbcontext.Set().UpdateRange(entity); - } - /// - /// 批量删除 - /// - /// - public void RemoveRange(List entity) - { - Dbcontext.Set().RemoveRange(entity); - } - - public void Remove(TEntity entity) - { - Dbcontext.Set().Remove(entity); - } - public void Update(TEntity entity) - { - Dbcontext.Set().Update(entity); - } - - public IQueryable GetQueryable() - { - return Dbcontext.Set(); - } - - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Hncore.Infrastructure.DDD; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +namespace Hncore.Infrastructure.EF +{ + public class RepositoryBase : IRepository + where TEntity : Entity + { + protected DbContext Dbcontext; + + public RepositoryBase(IRepositoryDbContext dbContext) + { + Dbcontext = dbContext.DbContext; + } + + public TEntity FindById(TId id) + { + return Dbcontext.Set().Find(id); + } + + public Task FindByIdAsync(TId id) + { + return Dbcontext.Set().FindAsync(id); + } + + public void Add(TEntity entity) + { + Dbcontext.Set().Add(entity); + } + + + public Task AddAsync(TEntity entity) + { + return Dbcontext.Set().AddAsync(entity); + } + /// + /// 批量添加 + /// + /// + public void AddRange(List entity) + { + Dbcontext.Set().AddRange(entity); + } + /// + /// 批量添加 + /// + /// + public Task AddRangeAsync(List entity) + { + return Dbcontext.Set().AddRangeAsync(entity); + } + /// + /// 批量修改 + /// + /// + public void UpdateRange(List entity) + { + Dbcontext.Set().UpdateRange(entity); + } + /// + /// 批量删除 + /// + /// + public void RemoveRange(List entity) + { + Dbcontext.Set().RemoveRange(entity); + } + + public void Remove(TEntity entity) + { + Dbcontext.Set().Remove(entity); + } + public void Update(TEntity entity) + { + Dbcontext.Set().Update(entity); + } + + public IQueryable GetQueryable() + { + return Dbcontext.Set(); + } + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EF/迁移命令.txt b/Infrastructure/Hncore.Infrastructure/EF/迁移命令.txt index f5dacca..ebcba70 100644 --- a/Infrastructure/Hncore.Infrastructure/EF/迁移命令.txt +++ b/Infrastructure/Hncore.Infrastructure/EF/迁移命令.txt @@ -1,13 +1,13 @@ -vs -Add-Migration -Update-Database - - - -cli -dotnet ef migrations add init - -dotnet ef database update - -输出脚本 +vs +Add-Migration +Update-Database + + + +cli +dotnet ef migrations add init + +dotnet ef database update + +输出脚本 dotnet ef migrations script \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/CommonEqualityComparer.cs b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/CommonEqualityComparer.cs index 3aca6ff..d50da74 100644 --- a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/CommonEqualityComparer.cs +++ b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/CommonEqualityComparer.cs @@ -1,48 +1,48 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Hncore.Infrastructure.EntitiesExtension -{ - public class CommonEqualityComparer : IEqualityComparer - { - private Func keySelector; - private IEqualityComparer comparer; - - public CommonEqualityComparer(Func keySelector, IEqualityComparer comparer) - { - this.keySelector = keySelector; - this.comparer = comparer; - } - - public CommonEqualityComparer(Func keySelector) - : this(keySelector, EqualityComparer.Default) - { } - - public bool Equals(T x, T y) - { - return comparer.Equals(keySelector(x), keySelector(y)); - } - - public int GetHashCode(T obj) - { - return comparer.GetHashCode(keySelector(obj)); - } - } - /// - /// 扩展类 - /// - public static class DistinctExtensions - { - public static IEnumerable Distinctx(this IEnumerable source, Func keySelector) - { - return source.Distinct(new CommonEqualityComparer(keySelector)); - } - - public static IEnumerable Distinctx(this IEnumerable source, Func keySelector, IEqualityComparer comparer) - { - return source.Distinct(new CommonEqualityComparer(keySelector, comparer)); - } - } - -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Hncore.Infrastructure.EntitiesExtension +{ + public class CommonEqualityComparer : IEqualityComparer + { + private Func keySelector; + private IEqualityComparer comparer; + + public CommonEqualityComparer(Func keySelector, IEqualityComparer comparer) + { + this.keySelector = keySelector; + this.comparer = comparer; + } + + public CommonEqualityComparer(Func keySelector) + : this(keySelector, EqualityComparer.Default) + { } + + public bool Equals(T x, T y) + { + return comparer.Equals(keySelector(x), keySelector(y)); + } + + public int GetHashCode(T obj) + { + return comparer.GetHashCode(keySelector(obj)); + } + } + /// + /// 扩展类 + /// + public static class DistinctExtensions + { + public static IEnumerable Distinctx(this IEnumerable source, Func keySelector) + { + return source.Distinct(new CommonEqualityComparer(keySelector)); + } + + public static IEnumerable Distinctx(this IEnumerable source, Func keySelector, IEqualityComparer comparer) + { + return source.Distinct(new CommonEqualityComparer(keySelector, comparer)); + } + } + +} diff --git a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ConditionBuilder.cs b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ConditionBuilder.cs index 0d1bdd8..81690a7 100644 --- a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ConditionBuilder.cs +++ b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ConditionBuilder.cs @@ -1,144 +1,144 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; - -namespace Hncore.Infrastructure.EntitiesExtension -{ - internal class ConditionBuilder : ExpressionVisitor - { - private List m_arguments; - private Stack m_conditionParts; - - public string Condition { get; private set; } - - public object[] Arguments { get; private set; } - - public void Build(Expression expression) - { - PartialEvaluator evaluator = new PartialEvaluator(); - Expression evaluatedExpression = evaluator.Eval(expression); - - this.m_arguments = new List(); - this.m_conditionParts = new Stack(); - - this.Visit(evaluatedExpression); - - this.Arguments = this.m_arguments.ToArray(); - this.Condition = this.m_conditionParts.Count > 0 ? this.m_conditionParts.Pop() : null; - } - - protected override Expression VisitBinary(BinaryExpression b) - { - if (b == null) return b; - - string opr; - switch (b.NodeType) - { - case ExpressionType.Equal: - opr = "="; - break; - case ExpressionType.NotEqual: - opr = "<>"; - break; - case ExpressionType.GreaterThan: - opr = ">"; - break; - case ExpressionType.GreaterThanOrEqual: - opr = ">="; - break; - case ExpressionType.LessThan: - opr = "<"; - break; - case ExpressionType.LessThanOrEqual: - opr = "<="; - break; - case ExpressionType.AndAlso: - opr = "AND"; - break; - case ExpressionType.OrElse: - opr = "OR"; - break; - case ExpressionType.Add: - opr = "+"; - break; - case ExpressionType.Subtract: - opr = "-"; - break; - case ExpressionType.Multiply: - opr = "*"; - break; - case ExpressionType.Divide: - opr = "/"; - break; - default: - throw new NotSupportedException(b.NodeType + "is not supported."); - } - - this.Visit(b.Left); - this.Visit(b.Right); - - string right = this.m_conditionParts.Pop(); - string left = this.m_conditionParts.Pop(); - - string condition = String.Format("({0} {1} {2})", left, opr, right); - this.m_conditionParts.Push(condition); - - return b; - } - - protected override Expression VisitConstant(ConstantExpression c) - { - if (c == null) return c; - - this.m_arguments.Add(c.Value); - this.m_conditionParts.Push(String.Format("{{{0}}}", this.m_arguments.Count - 1)); - - return c; - } - - protected override Expression VisitMemberAccess(MemberExpression m) - { - if (m == null) return m; - - PropertyInfo propertyInfo = m.Member as PropertyInfo; - if (propertyInfo == null) return m; - - this.m_conditionParts.Push(String.Format("[{0}]", propertyInfo.Name)); - - return m; - } - - protected override Expression VisitMethodCall(MethodCallExpression m) - { - if (m == null) return m; - - string format; - switch (m.Method.Name) - { - case "StartsWith": - format = "({0} LIKE {1}+'%')"; - break; - - case "Contains": - format = "({0} LIKE '%'+{1}+'%')"; - break; - - case "EndsWith": - format = "({0} LIKE '%'+{1})"; - break; - - default: - throw new NotSupportedException(m.NodeType + " is not supported!"); - } - - this.Visit(m.Object); - this.Visit(m.Arguments[0]); - string right = this.m_conditionParts.Pop(); - string left = this.m_conditionParts.Pop(); - this.m_conditionParts.Push(String.Format(format, left, right)); - - return m; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; + +namespace Hncore.Infrastructure.EntitiesExtension +{ + internal class ConditionBuilder : ExpressionVisitor + { + private List m_arguments; + private Stack m_conditionParts; + + public string Condition { get; private set; } + + public object[] Arguments { get; private set; } + + public void Build(Expression expression) + { + PartialEvaluator evaluator = new PartialEvaluator(); + Expression evaluatedExpression = evaluator.Eval(expression); + + this.m_arguments = new List(); + this.m_conditionParts = new Stack(); + + this.Visit(evaluatedExpression); + + this.Arguments = this.m_arguments.ToArray(); + this.Condition = this.m_conditionParts.Count > 0 ? this.m_conditionParts.Pop() : null; + } + + protected override Expression VisitBinary(BinaryExpression b) + { + if (b == null) return b; + + string opr; + switch (b.NodeType) + { + case ExpressionType.Equal: + opr = "="; + break; + case ExpressionType.NotEqual: + opr = "<>"; + break; + case ExpressionType.GreaterThan: + opr = ">"; + break; + case ExpressionType.GreaterThanOrEqual: + opr = ">="; + break; + case ExpressionType.LessThan: + opr = "<"; + break; + case ExpressionType.LessThanOrEqual: + opr = "<="; + break; + case ExpressionType.AndAlso: + opr = "AND"; + break; + case ExpressionType.OrElse: + opr = "OR"; + break; + case ExpressionType.Add: + opr = "+"; + break; + case ExpressionType.Subtract: + opr = "-"; + break; + case ExpressionType.Multiply: + opr = "*"; + break; + case ExpressionType.Divide: + opr = "/"; + break; + default: + throw new NotSupportedException(b.NodeType + "is not supported."); + } + + this.Visit(b.Left); + this.Visit(b.Right); + + string right = this.m_conditionParts.Pop(); + string left = this.m_conditionParts.Pop(); + + string condition = String.Format("({0} {1} {2})", left, opr, right); + this.m_conditionParts.Push(condition); + + return b; + } + + protected override Expression VisitConstant(ConstantExpression c) + { + if (c == null) return c; + + this.m_arguments.Add(c.Value); + this.m_conditionParts.Push(String.Format("{{{0}}}", this.m_arguments.Count - 1)); + + return c; + } + + protected override Expression VisitMemberAccess(MemberExpression m) + { + if (m == null) return m; + + PropertyInfo propertyInfo = m.Member as PropertyInfo; + if (propertyInfo == null) return m; + + this.m_conditionParts.Push(String.Format("[{0}]", propertyInfo.Name)); + + return m; + } + + protected override Expression VisitMethodCall(MethodCallExpression m) + { + if (m == null) return m; + + string format; + switch (m.Method.Name) + { + case "StartsWith": + format = "({0} LIKE {1}+'%')"; + break; + + case "Contains": + format = "({0} LIKE '%'+{1}+'%')"; + break; + + case "EndsWith": + format = "({0} LIKE '%'+{1})"; + break; + + default: + throw new NotSupportedException(m.NodeType + " is not supported!"); + } + + this.Visit(m.Object); + this.Visit(m.Arguments[0]); + string right = this.m_conditionParts.Pop(); + string left = this.m_conditionParts.Pop(); + this.m_conditionParts.Push(String.Format(format, left, right)); + + return m; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionBuilder.cs b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionBuilder.cs index f0bd712..b3d6a70 100644 --- a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionBuilder.cs +++ b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionBuilder.cs @@ -1,53 +1,53 @@ -using System; -using System.Linq; -using System.Linq.Expressions; - -namespace Hncore.Infrastructure.EntitiesExtension -{ - /// - /// Extension methods for add And and Or with parameters rebinder - /// - public static class ExpressionBuilder - { - /// - /// Compose two expression and merge all in a new expression - /// - /// Type of params in expression - /// Expression instance - /// Expression to merge - /// Function to merge - /// New merged expressions - public static Expression Compose(this Expression first, Expression second, Func merge) - { - // build parameter map (from parameters of second to parameters of first) - var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); - - // replace parameters in the second lambda expression with parameters from the first - var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); - // apply composition of lambda expression bodies to parameters from the first expression - return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); - } - /// - /// And operator - /// - /// Type of params in expression - /// Right Expression in AND operation - /// Left Expression in And operation - /// New AND expression - public static Expression> And(this Expression> first, Expression> second) - { - return first.Compose(second, Expression.AndAlso); - } - /// - /// Or operator - /// - /// Type of param in expression - /// Right expression in OR operation - /// Left expression in OR operation - /// New Or expressions - public static Expression> Or(this Expression> first, Expression> second) - { - return first.Compose(second, Expression.Or); - } - } -} +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace Hncore.Infrastructure.EntitiesExtension +{ + /// + /// Extension methods for add And and Or with parameters rebinder + /// + public static class ExpressionBuilder + { + /// + /// Compose two expression and merge all in a new expression + /// + /// Type of params in expression + /// Expression instance + /// Expression to merge + /// Function to merge + /// New merged expressions + public static Expression Compose(this Expression first, Expression second, Func merge) + { + // build parameter map (from parameters of second to parameters of first) + var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); + + // replace parameters in the second lambda expression with parameters from the first + var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); + // apply composition of lambda expression bodies to parameters from the first expression + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } + /// + /// And operator + /// + /// Type of params in expression + /// Right Expression in AND operation + /// Left Expression in And operation + /// New AND expression + public static Expression> And(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.AndAlso); + } + /// + /// Or operator + /// + /// Type of param in expression + /// Right expression in OR operation + /// Left expression in OR operation + /// New Or expressions + public static Expression> Or(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.Or); + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionVisitor.cs b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionVisitor.cs index af4436f..dc7914a 100644 --- a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionVisitor.cs +++ b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ExpressionVisitor.cs @@ -1,364 +1,364 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq.Expressions; - -namespace Hncore.Infrastructure.EntitiesExtension -{ - public abstract class ExpressionVisitor - { - protected ExpressionVisitor() { } - - protected virtual Expression Visit(Expression exp) - { - if (exp == null) - return exp; - switch (exp.NodeType) - { - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - case ExpressionType.Not: - case ExpressionType.Convert: - case ExpressionType.ConvertChecked: - case ExpressionType.ArrayLength: - case ExpressionType.Quote: - case ExpressionType.TypeAs: - return this.VisitUnary((UnaryExpression)exp); - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.Divide: - case ExpressionType.Modulo: - case ExpressionType.And: - case ExpressionType.AndAlso: - case ExpressionType.Or: - case ExpressionType.OrElse: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.Equal: - case ExpressionType.NotEqual: - case ExpressionType.Coalesce: - case ExpressionType.ArrayIndex: - case ExpressionType.RightShift: - case ExpressionType.LeftShift: - case ExpressionType.ExclusiveOr: - return this.VisitBinary((BinaryExpression)exp); - case ExpressionType.TypeIs: - return this.VisitTypeIs((TypeBinaryExpression)exp); - case ExpressionType.Conditional: - return this.VisitConditional((ConditionalExpression)exp); - case ExpressionType.Constant: - return this.VisitConstant((ConstantExpression)exp); - case ExpressionType.Parameter: - return this.VisitParameter((ParameterExpression)exp); - case ExpressionType.MemberAccess: - return this.VisitMemberAccess((MemberExpression)exp); - case ExpressionType.Call: - return this.VisitMethodCall((MethodCallExpression)exp); - case ExpressionType.Lambda: - return this.VisitLambda((LambdaExpression)exp); - case ExpressionType.New: - return this.VisitNew((NewExpression)exp); - case ExpressionType.NewArrayInit: - case ExpressionType.NewArrayBounds: - return this.VisitNewArray((NewArrayExpression)exp); - case ExpressionType.Invoke: - return this.VisitInvocation((InvocationExpression)exp); - case ExpressionType.MemberInit: - return this.VisitMemberInit((MemberInitExpression)exp); - case ExpressionType.ListInit: - return this.VisitListInit((ListInitExpression)exp); - default: - throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType)); - } - } - - protected virtual MemberBinding VisitBinding(MemberBinding binding) - { - switch (binding.BindingType) - { - case MemberBindingType.Assignment: - return this.VisitMemberAssignment((MemberAssignment)binding); - case MemberBindingType.MemberBinding: - return this.VisitMemberMemberBinding((MemberMemberBinding)binding); - case MemberBindingType.ListBinding: - return this.VisitMemberListBinding((MemberListBinding)binding); - default: - throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType)); - } - } - - protected virtual ElementInit VisitElementInitializer(ElementInit initializer) - { - ReadOnlyCollection arguments = this.VisitExpressionList(initializer.Arguments); - if (arguments != initializer.Arguments) - { - return Expression.ElementInit(initializer.AddMethod, arguments); - } - return initializer; - } - - protected virtual Expression VisitUnary(UnaryExpression u) - { - Expression operand = this.Visit(u.Operand); - if (operand != u.Operand) - { - return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method); - } - return u; - } - - protected virtual Expression VisitBinary(BinaryExpression b) - { - Expression left = this.Visit(b.Left); - Expression right = this.Visit(b.Right); - Expression conversion = this.Visit(b.Conversion); - if (left != b.Left || right != b.Right || conversion != b.Conversion) - { - if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null) - return Expression.Coalesce(left, right, conversion as LambdaExpression); - else - return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method); - } - return b; - } - - protected virtual Expression VisitTypeIs(TypeBinaryExpression b) - { - Expression expr = this.Visit(b.Expression); - if (expr != b.Expression) - { - return Expression.TypeIs(expr, b.TypeOperand); - } - return b; - } - - protected virtual Expression VisitConstant(ConstantExpression c) - { - return c; - } - - protected virtual Expression VisitConditional(ConditionalExpression c) - { - Expression test = this.Visit(c.Test); - Expression ifTrue = this.Visit(c.IfTrue); - Expression ifFalse = this.Visit(c.IfFalse); - if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse) - { - return Expression.Condition(test, ifTrue, ifFalse); - } - return c; - } - - protected virtual Expression VisitParameter(ParameterExpression p) - { - return p; - } - - protected virtual Expression VisitMemberAccess(MemberExpression m) - { - Expression exp = this.Visit(m.Expression); - if (exp != m.Expression) - { - return Expression.MakeMemberAccess(exp, m.Member); - } - return m; - } - - protected virtual Expression VisitMethodCall(MethodCallExpression m) - { - Expression obj = this.Visit(m.Object); - IEnumerable args = this.VisitExpressionList(m.Arguments); - if (obj != m.Object || args != m.Arguments) - { - return Expression.Call(obj, m.Method, args); - } - return m; - } - - protected virtual ReadOnlyCollection VisitExpressionList(ReadOnlyCollection original) - { - List list = null; - for (int i = 0, n = original.Count; i < n; i++) - { - Expression p = this.Visit(original[i]); - if (list != null) - { - list.Add(p); - } - else if (p != original[i]) - { - list = new List(n); - for (int j = 0; j < i; j++) - { - list.Add(original[j]); - } - list.Add(p); - } - } - if (list != null) - { - return list.AsReadOnly(); - } - return original; - } - - protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment) - { - Expression e = this.Visit(assignment.Expression); - if (e != assignment.Expression) - { - return Expression.Bind(assignment.Member, e); - } - return assignment; - } - - protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) - { - IEnumerable bindings = this.VisitBindingList(binding.Bindings); - if (bindings != binding.Bindings) - { - return Expression.MemberBind(binding.Member, bindings); - } - return binding; - } - - protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) - { - IEnumerable initializers = this.VisitElementInitializerList(binding.Initializers); - if (initializers != binding.Initializers) - { - return Expression.ListBind(binding.Member, initializers); - } - return binding; - } - - protected virtual IEnumerable VisitBindingList(ReadOnlyCollection original) - { - List list = null; - for (int i = 0, n = original.Count; i < n; i++) - { - MemberBinding b = this.VisitBinding(original[i]); - if (list != null) - { - list.Add(b); - } - else if (b != original[i]) - { - list = new List(n); - for (int j = 0; j < i; j++) - { - list.Add(original[j]); - } - list.Add(b); - } - } - if (list != null) - return list; - return original; - } - - protected virtual IEnumerable VisitElementInitializerList(ReadOnlyCollection original) - { - List list = null; - for (int i = 0, n = original.Count; i < n; i++) - { - ElementInit init = this.VisitElementInitializer(original[i]); - if (list != null) - { - list.Add(init); - } - else if (init != original[i]) - { - list = new List(n); - for (int j = 0; j < i; j++) - { - list.Add(original[j]); - } - list.Add(init); - } - } - if (list != null) - return list; - return original; - } - - protected virtual Expression VisitLambda(LambdaExpression lambda) - { - Expression body = this.Visit(lambda.Body); - if (body != lambda.Body) - { - return Expression.Lambda(lambda.Type, body, lambda.Parameters); - } - return lambda; - } - - protected virtual NewExpression VisitNew(NewExpression nex) - { - IEnumerable args = this.VisitExpressionList(nex.Arguments); - if (args != nex.Arguments) - { - if (nex.Members != null) - return Expression.New(nex.Constructor, args, nex.Members); - else - return Expression.New(nex.Constructor, args); - } - return nex; - } - - protected virtual Expression VisitMemberInit(MemberInitExpression init) - { - NewExpression n = this.VisitNew(init.NewExpression); - IEnumerable bindings = this.VisitBindingList(init.Bindings); - if (n != init.NewExpression || bindings != init.Bindings) - { - return Expression.MemberInit(n, bindings); - } - return init; - } - - protected virtual Expression VisitListInit(ListInitExpression init) - { - NewExpression n = this.VisitNew(init.NewExpression); - IEnumerable initializers = this.VisitElementInitializerList(init.Initializers); - if (n != init.NewExpression || initializers != init.Initializers) - { - return Expression.ListInit(n, initializers); - } - return init; - } - - protected virtual Expression VisitNewArray(NewArrayExpression na) - { - IEnumerable exprs = this.VisitExpressionList(na.Expressions); - if (exprs != na.Expressions) - { - if (na.NodeType == ExpressionType.NewArrayInit) - { - return Expression.NewArrayInit(na.Type.GetElementType(), exprs); - } - else - { - return Expression.NewArrayBounds(na.Type.GetElementType(), exprs); - } - } - return na; - } - - protected virtual Expression VisitInvocation(InvocationExpression iv) - { - IEnumerable args = this.VisitExpressionList(iv.Arguments); - Expression expr = this.Visit(iv.Expression); - if (args != iv.Arguments || expr != iv.Expression) - { - return Expression.Invoke(expr, args); - } - return iv; - } - } -} +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; + +namespace Hncore.Infrastructure.EntitiesExtension +{ + public abstract class ExpressionVisitor + { + protected ExpressionVisitor() { } + + protected virtual Expression Visit(Expression exp) + { + if (exp == null) + return exp; + switch (exp.NodeType) + { + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.ArrayLength: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + return this.VisitUnary((UnaryExpression)exp); + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + case ExpressionType.Divide: + case ExpressionType.Modulo: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.Or: + case ExpressionType.OrElse: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.Coalesce: + case ExpressionType.ArrayIndex: + case ExpressionType.RightShift: + case ExpressionType.LeftShift: + case ExpressionType.ExclusiveOr: + return this.VisitBinary((BinaryExpression)exp); + case ExpressionType.TypeIs: + return this.VisitTypeIs((TypeBinaryExpression)exp); + case ExpressionType.Conditional: + return this.VisitConditional((ConditionalExpression)exp); + case ExpressionType.Constant: + return this.VisitConstant((ConstantExpression)exp); + case ExpressionType.Parameter: + return this.VisitParameter((ParameterExpression)exp); + case ExpressionType.MemberAccess: + return this.VisitMemberAccess((MemberExpression)exp); + case ExpressionType.Call: + return this.VisitMethodCall((MethodCallExpression)exp); + case ExpressionType.Lambda: + return this.VisitLambda((LambdaExpression)exp); + case ExpressionType.New: + return this.VisitNew((NewExpression)exp); + case ExpressionType.NewArrayInit: + case ExpressionType.NewArrayBounds: + return this.VisitNewArray((NewArrayExpression)exp); + case ExpressionType.Invoke: + return this.VisitInvocation((InvocationExpression)exp); + case ExpressionType.MemberInit: + return this.VisitMemberInit((MemberInitExpression)exp); + case ExpressionType.ListInit: + return this.VisitListInit((ListInitExpression)exp); + default: + throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType)); + } + } + + protected virtual MemberBinding VisitBinding(MemberBinding binding) + { + switch (binding.BindingType) + { + case MemberBindingType.Assignment: + return this.VisitMemberAssignment((MemberAssignment)binding); + case MemberBindingType.MemberBinding: + return this.VisitMemberMemberBinding((MemberMemberBinding)binding); + case MemberBindingType.ListBinding: + return this.VisitMemberListBinding((MemberListBinding)binding); + default: + throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType)); + } + } + + protected virtual ElementInit VisitElementInitializer(ElementInit initializer) + { + ReadOnlyCollection arguments = this.VisitExpressionList(initializer.Arguments); + if (arguments != initializer.Arguments) + { + return Expression.ElementInit(initializer.AddMethod, arguments); + } + return initializer; + } + + protected virtual Expression VisitUnary(UnaryExpression u) + { + Expression operand = this.Visit(u.Operand); + if (operand != u.Operand) + { + return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method); + } + return u; + } + + protected virtual Expression VisitBinary(BinaryExpression b) + { + Expression left = this.Visit(b.Left); + Expression right = this.Visit(b.Right); + Expression conversion = this.Visit(b.Conversion); + if (left != b.Left || right != b.Right || conversion != b.Conversion) + { + if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null) + return Expression.Coalesce(left, right, conversion as LambdaExpression); + else + return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method); + } + return b; + } + + protected virtual Expression VisitTypeIs(TypeBinaryExpression b) + { + Expression expr = this.Visit(b.Expression); + if (expr != b.Expression) + { + return Expression.TypeIs(expr, b.TypeOperand); + } + return b; + } + + protected virtual Expression VisitConstant(ConstantExpression c) + { + return c; + } + + protected virtual Expression VisitConditional(ConditionalExpression c) + { + Expression test = this.Visit(c.Test); + Expression ifTrue = this.Visit(c.IfTrue); + Expression ifFalse = this.Visit(c.IfFalse); + if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse) + { + return Expression.Condition(test, ifTrue, ifFalse); + } + return c; + } + + protected virtual Expression VisitParameter(ParameterExpression p) + { + return p; + } + + protected virtual Expression VisitMemberAccess(MemberExpression m) + { + Expression exp = this.Visit(m.Expression); + if (exp != m.Expression) + { + return Expression.MakeMemberAccess(exp, m.Member); + } + return m; + } + + protected virtual Expression VisitMethodCall(MethodCallExpression m) + { + Expression obj = this.Visit(m.Object); + IEnumerable args = this.VisitExpressionList(m.Arguments); + if (obj != m.Object || args != m.Arguments) + { + return Expression.Call(obj, m.Method, args); + } + return m; + } + + protected virtual ReadOnlyCollection VisitExpressionList(ReadOnlyCollection original) + { + List list = null; + for (int i = 0, n = original.Count; i < n; i++) + { + Expression p = this.Visit(original[i]); + if (list != null) + { + list.Add(p); + } + else if (p != original[i]) + { + list = new List(n); + for (int j = 0; j < i; j++) + { + list.Add(original[j]); + } + list.Add(p); + } + } + if (list != null) + { + return list.AsReadOnly(); + } + return original; + } + + protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment) + { + Expression e = this.Visit(assignment.Expression); + if (e != assignment.Expression) + { + return Expression.Bind(assignment.Member, e); + } + return assignment; + } + + protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) + { + IEnumerable bindings = this.VisitBindingList(binding.Bindings); + if (bindings != binding.Bindings) + { + return Expression.MemberBind(binding.Member, bindings); + } + return binding; + } + + protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) + { + IEnumerable initializers = this.VisitElementInitializerList(binding.Initializers); + if (initializers != binding.Initializers) + { + return Expression.ListBind(binding.Member, initializers); + } + return binding; + } + + protected virtual IEnumerable VisitBindingList(ReadOnlyCollection original) + { + List list = null; + for (int i = 0, n = original.Count; i < n; i++) + { + MemberBinding b = this.VisitBinding(original[i]); + if (list != null) + { + list.Add(b); + } + else if (b != original[i]) + { + list = new List(n); + for (int j = 0; j < i; j++) + { + list.Add(original[j]); + } + list.Add(b); + } + } + if (list != null) + return list; + return original; + } + + protected virtual IEnumerable VisitElementInitializerList(ReadOnlyCollection original) + { + List list = null; + for (int i = 0, n = original.Count; i < n; i++) + { + ElementInit init = this.VisitElementInitializer(original[i]); + if (list != null) + { + list.Add(init); + } + else if (init != original[i]) + { + list = new List(n); + for (int j = 0; j < i; j++) + { + list.Add(original[j]); + } + list.Add(init); + } + } + if (list != null) + return list; + return original; + } + + protected virtual Expression VisitLambda(LambdaExpression lambda) + { + Expression body = this.Visit(lambda.Body); + if (body != lambda.Body) + { + return Expression.Lambda(lambda.Type, body, lambda.Parameters); + } + return lambda; + } + + protected virtual NewExpression VisitNew(NewExpression nex) + { + IEnumerable args = this.VisitExpressionList(nex.Arguments); + if (args != nex.Arguments) + { + if (nex.Members != null) + return Expression.New(nex.Constructor, args, nex.Members); + else + return Expression.New(nex.Constructor, args); + } + return nex; + } + + protected virtual Expression VisitMemberInit(MemberInitExpression init) + { + NewExpression n = this.VisitNew(init.NewExpression); + IEnumerable bindings = this.VisitBindingList(init.Bindings); + if (n != init.NewExpression || bindings != init.Bindings) + { + return Expression.MemberInit(n, bindings); + } + return init; + } + + protected virtual Expression VisitListInit(ListInitExpression init) + { + NewExpression n = this.VisitNew(init.NewExpression); + IEnumerable initializers = this.VisitElementInitializerList(init.Initializers); + if (n != init.NewExpression || initializers != init.Initializers) + { + return Expression.ListInit(n, initializers); + } + return init; + } + + protected virtual Expression VisitNewArray(NewArrayExpression na) + { + IEnumerable exprs = this.VisitExpressionList(na.Expressions); + if (exprs != na.Expressions) + { + if (na.NodeType == ExpressionType.NewArrayInit) + { + return Expression.NewArrayInit(na.Type.GetElementType(), exprs); + } + else + { + return Expression.NewArrayBounds(na.Type.GetElementType(), exprs); + } + } + return na; + } + + protected virtual Expression VisitInvocation(InvocationExpression iv) + { + IEnumerable args = this.VisitExpressionList(iv.Arguments); + Expression expr = this.Visit(iv.Expression); + if (args != iv.Arguments || expr != iv.Expression) + { + return Expression.Invoke(expr, args); + } + return iv; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/IQueryableExtend.cs b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/IQueryableExtend.cs index 217207d..4024cb2 100644 --- a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/IQueryableExtend.cs +++ b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/IQueryableExtend.cs @@ -1,107 +1,107 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.Extension; -using Microsoft.EntityFrameworkCore; - -namespace Hncore.Infrastructure.EntitiesExtension -{ - public static class IQueryableExtend - { - #region 返回IQueryable前几条数据 - - /// - /// 返回IQueryable前几条数据 - /// - /// - /// - /// - /// - public static IQueryable TopN(this IQueryable query, int TopN) - { - return query.Take(TopN); - } - - #endregion - - #region 对IQueryable进行分页 - - /// - /// 对IQueryable进行分页 - /// - /// - /// - /// 每页多少条数据 - /// 当前页 - /// - public static IQueryable QueryPager(this IQueryable query, int PageSize, int PageIndex) - { - if (PageIndex <= 0) - { - PageIndex = 1; - } - - if (PageSize <= 0) - { - PageSize = 1; - } - - if (PageSize > 0) - return query.Skip((PageIndex - 1) * PageSize).Take(PageSize); - return query; - } - - #endregion - - #region 得到IQueryable的分页后实体集合 - - /// - /// 得到IQueryable的分页后实体集合 - /// - /// - /// 每页多少条数据 - /// 当前页 - /// 是否统计总行数 - /// - public static PageData ListPager(this IQueryable query, int pageSize, int pageIndex, bool isTotal) - { - PageData list = new PageData(); - - if (isTotal) - { - list.RowCount = query.Count(); - } - - list.List = query.QueryPager(pageSize, pageIndex).ToList(); - - return list; - } - - /// - /// 得到IQueryable的分页后实体集合 - /// - /// - /// 每页多少条数据 - /// 当前页 - /// 是否统计总行数 - /// - public static async Task> ListPagerAsync(this IQueryable query, int pageSize, int pageIndex, - bool isTotal) - { - PageData list = new PageData(); - - if (isTotal) - { - list.RowCount = await query.CountAsync(); - } - - list.List = await query.QueryPager(pageSize, pageIndex).ToListAsync(); - - return list; - } - - #endregion - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.Extension; +using Microsoft.EntityFrameworkCore; + +namespace Hncore.Infrastructure.EntitiesExtension +{ + public static class IQueryableExtend + { + #region 返回IQueryable前几条数据 + + /// + /// 返回IQueryable前几条数据 + /// + /// + /// + /// + /// + public static IQueryable TopN(this IQueryable query, int TopN) + { + return query.Take(TopN); + } + + #endregion + + #region 对IQueryable进行分页 + + /// + /// 对IQueryable进行分页 + /// + /// + /// + /// 每页多少条数据 + /// 当前页 + /// + public static IQueryable QueryPager(this IQueryable query, int PageSize, int PageIndex) + { + if (PageIndex <= 0) + { + PageIndex = 1; + } + + if (PageSize <= 0) + { + PageSize = 1; + } + + if (PageSize > 0) + return query.Skip((PageIndex - 1) * PageSize).Take(PageSize); + return query; + } + + #endregion + + #region 得到IQueryable的分页后实体集合 + + /// + /// 得到IQueryable的分页后实体集合 + /// + /// + /// 每页多少条数据 + /// 当前页 + /// 是否统计总行数 + /// + public static PageData ListPager(this IQueryable query, int pageSize, int pageIndex, bool isTotal) + { + PageData list = new PageData(); + + if (isTotal) + { + list.RowCount = query.Count(); + } + + list.List = query.QueryPager(pageSize, pageIndex).ToList(); + + return list; + } + + /// + /// 得到IQueryable的分页后实体集合 + /// + /// + /// 每页多少条数据 + /// 当前页 + /// 是否统计总行数 + /// + public static async Task> ListPagerAsync(this IQueryable query, int pageSize, int pageIndex, + bool isTotal) + { + PageData list = new PageData(); + + if (isTotal) + { + list.RowCount = await query.CountAsync(); + } + + list.List = await query.QueryPager(pageSize, pageIndex).ToListAsync(); + + return list; + } + + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ParameterRebinder.cs b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ParameterRebinder.cs index 305ca0d..040a991 100644 --- a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ParameterRebinder.cs +++ b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/ParameterRebinder.cs @@ -1,44 +1,44 @@ -using System.Collections.Generic; -using System.Linq.Expressions; - -namespace Hncore.Infrastructure.EntitiesExtension -{ - public class ParameterRebinder : ExpressionVisitor - { - private readonly Dictionary map; - - /// - /// Default construcotr - /// - /// Map specification - public ParameterRebinder(Dictionary map) - { - this.map = map ?? new Dictionary(); - } - /// - /// Replate parameters in expression with a Map information - /// - /// Map information - /// Expression to replace parameters - /// Expression with parameters replaced - public static Expression ReplaceParameters(Dictionary map, Expression exp) - { - return new ParameterRebinder(map).Visit(exp); - } - /// - /// Visit pattern method - /// - /// A Parameter expression - /// New visited expression - protected override Expression VisitParameter(ParameterExpression p) - { - ParameterExpression replacement; - if (map.TryGetValue(p, out replacement)) - { - p = replacement; - } - - return base.VisitParameter(p); - } - } -} +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Hncore.Infrastructure.EntitiesExtension +{ + public class ParameterRebinder : ExpressionVisitor + { + private readonly Dictionary map; + + /// + /// Default construcotr + /// + /// Map specification + public ParameterRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + /// + /// Replate parameters in expression with a Map information + /// + /// Map information + /// Expression to replace parameters + /// Expression with parameters replaced + public static Expression ReplaceParameters(Dictionary map, Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } + /// + /// Visit pattern method + /// + /// A Parameter expression + /// New visited expression + protected override Expression VisitParameter(ParameterExpression p) + { + ParameterExpression replacement; + if (map.TryGetValue(p, out replacement)) + { + p = replacement; + } + + return base.VisitParameter(p); + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/PartialEvaluator.cs b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/PartialEvaluator.cs index 3288771..9503d06 100644 --- a/Infrastructure/Hncore.Infrastructure/EntitiesExtension/PartialEvaluator.cs +++ b/Infrastructure/Hncore.Infrastructure/EntitiesExtension/PartialEvaluator.cs @@ -1,115 +1,115 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; - -namespace Hncore.Infrastructure.EntitiesExtension -{ - public class PartialEvaluator : ExpressionVisitor - { - private Func m_fnCanBeEvaluated; - private HashSet m_candidates; - - public PartialEvaluator() - : this(CanBeEvaluatedLocally) - { } - - public PartialEvaluator(Func fnCanBeEvaluated) - { - this.m_fnCanBeEvaluated = fnCanBeEvaluated; - } - - public Expression Eval(Expression exp) - { - this.m_candidates = new Nominator(this.m_fnCanBeEvaluated).Nominate(exp); - - return this.Visit(exp); - } - - protected override Expression Visit(Expression exp) - { - if (exp == null) - { - return null; - } - - if (this.m_candidates.Contains(exp)) - { - return this.Evaluate(exp); - } - - return base.Visit(exp); - } - - private Expression Evaluate(Expression e) - { - if (e.NodeType == ExpressionType.Constant) - { - return e; - } - - LambdaExpression lambda = Expression.Lambda(e); - Delegate fn = lambda.Compile(); - - return Expression.Constant(fn.DynamicInvoke(null), e.Type); - } - - private static bool CanBeEvaluatedLocally(Expression exp) - { - return exp.NodeType != ExpressionType.Parameter; - } - - #region Nominator - - /// - /// Performs bottom-up analysis to determine which nodes can possibly - /// be part of an evaluated sub-tree. - /// - private class Nominator : ExpressionVisitor - { - private Func m_fnCanBeEvaluated; - private HashSet m_candidates; - private bool m_cannotBeEvaluated; - - internal Nominator(Func fnCanBeEvaluated) - { - this.m_fnCanBeEvaluated = fnCanBeEvaluated; - } - - internal HashSet Nominate(Expression expression) - { - this.m_candidates = new HashSet(); - this.Visit(expression); - return this.m_candidates; - } - - protected override Expression Visit(Expression expression) - { - if (expression != null) - { - bool saveCannotBeEvaluated = this.m_cannotBeEvaluated; - this.m_cannotBeEvaluated = false; - - base.Visit(expression); - - if (!this.m_cannotBeEvaluated) - { - if (this.m_fnCanBeEvaluated(expression)) - { - this.m_candidates.Add(expression); - } - else - { - this.m_cannotBeEvaluated = true; - } - } - - this.m_cannotBeEvaluated |= saveCannotBeEvaluated; - } - - return expression; - } - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Hncore.Infrastructure.EntitiesExtension +{ + public class PartialEvaluator : ExpressionVisitor + { + private Func m_fnCanBeEvaluated; + private HashSet m_candidates; + + public PartialEvaluator() + : this(CanBeEvaluatedLocally) + { } + + public PartialEvaluator(Func fnCanBeEvaluated) + { + this.m_fnCanBeEvaluated = fnCanBeEvaluated; + } + + public Expression Eval(Expression exp) + { + this.m_candidates = new Nominator(this.m_fnCanBeEvaluated).Nominate(exp); + + return this.Visit(exp); + } + + protected override Expression Visit(Expression exp) + { + if (exp == null) + { + return null; + } + + if (this.m_candidates.Contains(exp)) + { + return this.Evaluate(exp); + } + + return base.Visit(exp); + } + + private Expression Evaluate(Expression e) + { + if (e.NodeType == ExpressionType.Constant) + { + return e; + } + + LambdaExpression lambda = Expression.Lambda(e); + Delegate fn = lambda.Compile(); + + return Expression.Constant(fn.DynamicInvoke(null), e.Type); + } + + private static bool CanBeEvaluatedLocally(Expression exp) + { + return exp.NodeType != ExpressionType.Parameter; + } + + #region Nominator + + /// + /// Performs bottom-up analysis to determine which nodes can possibly + /// be part of an evaluated sub-tree. + /// + private class Nominator : ExpressionVisitor + { + private Func m_fnCanBeEvaluated; + private HashSet m_candidates; + private bool m_cannotBeEvaluated; + + internal Nominator(Func fnCanBeEvaluated) + { + this.m_fnCanBeEvaluated = fnCanBeEvaluated; + } + + internal HashSet Nominate(Expression expression) + { + this.m_candidates = new HashSet(); + this.Visit(expression); + return this.m_candidates; + } + + protected override Expression Visit(Expression expression) + { + if (expression != null) + { + bool saveCannotBeEvaluated = this.m_cannotBeEvaluated; + this.m_cannotBeEvaluated = false; + + base.Visit(expression); + + if (!this.m_cannotBeEvaluated) + { + if (this.m_fnCanBeEvaluated(expression)) + { + this.m_candidates.Add(expression); + } + else + { + this.m_cannotBeEvaluated = true; + } + } + + this.m_cannotBeEvaluated |= saveCannotBeEvaluated; + } + + return expression; + } + } + + #endregion + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EventBus/ActionEventHandler.cs b/Infrastructure/Hncore.Infrastructure/EventBus/ActionEventHandler.cs index f20722b..fce6539 100644 --- a/Infrastructure/Hncore.Infrastructure/EventBus/ActionEventHandler.cs +++ b/Infrastructure/Hncore.Infrastructure/EventBus/ActionEventHandler.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Events -{ - internal class ActionEventHandler : IEventHandler where TEventData : IEventData - { - public Action Action { get; private set; } - - public virtual bool Ansyc { get; set; } - - public ActionEventHandler(Action handler) - { - Action = handler; - } - public void HandleEvent(TEventData eventData) - { - Action(eventData); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Events +{ + internal class ActionEventHandler : IEventHandler where TEventData : IEventData + { + public Action Action { get; private set; } + + public virtual bool Ansyc { get; set; } + + public ActionEventHandler(Action handler) + { + Action = handler; + } + public void HandleEvent(TEventData eventData) + { + Action(eventData); + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EventBus/EventBus.cs b/Infrastructure/Hncore.Infrastructure/EventBus/EventBus.cs index 0c4ef4c..1f66587 100644 --- a/Infrastructure/Hncore.Infrastructure/EventBus/EventBus.cs +++ b/Infrastructure/Hncore.Infrastructure/EventBus/EventBus.cs @@ -1,150 +1,150 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Events -{ - /// - /// 事件总线 - /// - public class EventBus - { - private static EventBus _eventBus = null; - - public static EventBus Default - { - get { return _eventBus ?? (_eventBus = new EventBus()); } - } - - /// - /// 定义线程安全集合 - /// - private readonly ConcurrentDictionary> _eventAndHandlerMapping; - - public EventBus() - { - _eventAndHandlerMapping = new ConcurrentDictionary>(); - MapEventToHandler(); - } - - /// - ///通过反射,将事件源与事件处理绑定 - /// - private void MapEventToHandler() - { - // Assembly assembly = Assembly.GetEntryAssembly(); - - var allAssembly = AppDomain.CurrentDomain.GetAssemblies().Where(item => item.FullName.Contains("Microkj.")); - if (!allAssembly.Any()) - { - return; - } - - foreach (var assembly in allAssembly) - { - foreach (var type in assembly.GetTypes()) - { - if (!type.IsGenericType && typeof(IEventHandler).IsAssignableFrom(type)) //判断当前类型是否实现了IEventHandler接口 - { - Type handlerInterface = type.GetInterface("IEventHandler`1"); //获取该类实现的泛型接口 - if (handlerInterface != null) - { - Type eventDataType = handlerInterface.GetGenericArguments()[0]; // 获取泛型接口指定的参数类型 - - if (_eventAndHandlerMapping.ContainsKey(eventDataType)) - { - List handlerTypes = _eventAndHandlerMapping[eventDataType]; - handlerTypes.Add(Activator.CreateInstance(type) as IEventHandler); - _eventAndHandlerMapping[eventDataType] = handlerTypes; - } - else - { - var handlerTypes = new List - { - Activator.CreateInstance(type) as IEventHandler - }; - _eventAndHandlerMapping[eventDataType] = handlerTypes; - } - } - } - } - } - } - - /// - /// 手动绑定事件源与事件处理 - /// - /// - /// - public void Register(IEventHandler eventHandler) - { - if (_eventAndHandlerMapping.Keys.Contains(typeof (TEventData))) - { - List handlerTypes = _eventAndHandlerMapping[typeof (TEventData)]; - if (!handlerTypes.Contains(eventHandler)) - { - handlerTypes.Add(eventHandler); - _eventAndHandlerMapping[typeof (TEventData)] = handlerTypes; - } - } - else - { - _eventAndHandlerMapping.GetOrAdd(typeof (TEventData), (type) => new List()) - .Add(eventHandler); - } - } - - - public void Register(Action action) where TEventData : IEventData - { - var actionHandler = new ActionEventHandler(action); - Register(actionHandler); - } - - /// - /// 手动解除事件源与事件处理的绑定 - /// - /// - /// - public void UnRegister(Type eventHandler) - { - List handlerTypes = _eventAndHandlerMapping[typeof (TEventData)]; - - _eventAndHandlerMapping.GetOrAdd(typeof (TEventData), (type) => new List()) - .RemoveAll(t => t.GetType() == eventHandler); - } - - /// - /// 根据事件源触发绑定的事件处理 - /// - /// - /// - public static void Publish(TEventData eventData) where TEventData : IEventData - { - List handlers = Default._eventAndHandlerMapping[typeof (TEventData)]; - - if (handlers != null && handlers.Count > 0) - { - foreach (var handler in handlers) - { - var eventHandler = handler as IEventHandler; - if (eventHandler.Ansyc) - { - Task.Run(() => - { - eventHandler.HandleEvent(eventData); - }); - } - else - { - eventHandler.HandleEvent(eventData); - } - } - } - } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Events +{ + /// + /// 事件总线 + /// + public class EventBus + { + private static EventBus _eventBus = null; + + public static EventBus Default + { + get { return _eventBus ?? (_eventBus = new EventBus()); } + } + + /// + /// 定义线程安全集合 + /// + private readonly ConcurrentDictionary> _eventAndHandlerMapping; + + public EventBus() + { + _eventAndHandlerMapping = new ConcurrentDictionary>(); + MapEventToHandler(); + } + + /// + ///通过反射,将事件源与事件处理绑定 + /// + private void MapEventToHandler() + { + // Assembly assembly = Assembly.GetEntryAssembly(); + + var allAssembly = AppDomain.CurrentDomain.GetAssemblies().Where(item => item.FullName.Contains("Microkj.")); + if (!allAssembly.Any()) + { + return; + } + + foreach (var assembly in allAssembly) + { + foreach (var type in assembly.GetTypes()) + { + if (!type.IsGenericType && typeof(IEventHandler).IsAssignableFrom(type)) //判断当前类型是否实现了IEventHandler接口 + { + Type handlerInterface = type.GetInterface("IEventHandler`1"); //获取该类实现的泛型接口 + if (handlerInterface != null) + { + Type eventDataType = handlerInterface.GetGenericArguments()[0]; // 获取泛型接口指定的参数类型 + + if (_eventAndHandlerMapping.ContainsKey(eventDataType)) + { + List handlerTypes = _eventAndHandlerMapping[eventDataType]; + handlerTypes.Add(Activator.CreateInstance(type) as IEventHandler); + _eventAndHandlerMapping[eventDataType] = handlerTypes; + } + else + { + var handlerTypes = new List + { + Activator.CreateInstance(type) as IEventHandler + }; + _eventAndHandlerMapping[eventDataType] = handlerTypes; + } + } + } + } + } + } + + /// + /// 手动绑定事件源与事件处理 + /// + /// + /// + public void Register(IEventHandler eventHandler) + { + if (_eventAndHandlerMapping.Keys.Contains(typeof (TEventData))) + { + List handlerTypes = _eventAndHandlerMapping[typeof (TEventData)]; + if (!handlerTypes.Contains(eventHandler)) + { + handlerTypes.Add(eventHandler); + _eventAndHandlerMapping[typeof (TEventData)] = handlerTypes; + } + } + else + { + _eventAndHandlerMapping.GetOrAdd(typeof (TEventData), (type) => new List()) + .Add(eventHandler); + } + } + + + public void Register(Action action) where TEventData : IEventData + { + var actionHandler = new ActionEventHandler(action); + Register(actionHandler); + } + + /// + /// 手动解除事件源与事件处理的绑定 + /// + /// + /// + public void UnRegister(Type eventHandler) + { + List handlerTypes = _eventAndHandlerMapping[typeof (TEventData)]; + + _eventAndHandlerMapping.GetOrAdd(typeof (TEventData), (type) => new List()) + .RemoveAll(t => t.GetType() == eventHandler); + } + + /// + /// 根据事件源触发绑定的事件处理 + /// + /// + /// + public static void Publish(TEventData eventData) where TEventData : IEventData + { + List handlers = Default._eventAndHandlerMapping[typeof (TEventData)]; + + if (handlers != null && handlers.Count > 0) + { + foreach (var handler in handlers) + { + var eventHandler = handler as IEventHandler; + if (eventHandler.Ansyc) + { + Task.Run(() => + { + eventHandler.HandleEvent(eventData); + }); + } + else + { + eventHandler.HandleEvent(eventData); + } + } + } + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EventBus/EventData.cs b/Infrastructure/Hncore.Infrastructure/EventBus/EventData.cs index 6141ba5..f86b570 100644 --- a/Infrastructure/Hncore.Infrastructure/EventBus/EventData.cs +++ b/Infrastructure/Hncore.Infrastructure/EventBus/EventData.cs @@ -1,39 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Events -{ - /// - /// 事件源:描述事件信息,用于参数传递 - /// - public class EventData : IEventData where TData:class - { - /// - /// 事件发生的时间 - /// - public DateTime EventTime { get; set; } - - /// - /// 触发事件的对象 - /// - public TData EventSource { get; set; } - - object IEventData.EventSource - { - get - { - return this.EventSource as TData; - } - - set { this.EventSource =(TData) value; } - } - - public EventData() - { - EventTime = DateTime.Now; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Events +{ + /// + /// 事件源:描述事件信息,用于参数传递 + /// + public class EventData : IEventData where TData:class + { + /// + /// 事件发生的时间 + /// + public DateTime EventTime { get; set; } + + /// + /// 触发事件的对象 + /// + public TData EventSource { get; set; } + + object IEventData.EventSource + { + get + { + return this.EventSource as TData; + } + + set { this.EventSource =(TData) value; } + } + + public EventData() + { + EventTime = DateTime.Now; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EventBus/IEventBus.cs b/Infrastructure/Hncore.Infrastructure/EventBus/IEventBus.cs index 18cb20c..f0556db 100644 --- a/Infrastructure/Hncore.Infrastructure/EventBus/IEventBus.cs +++ b/Infrastructure/Hncore.Infrastructure/EventBus/IEventBus.cs @@ -1,19 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Events -{ - public interface IEventBus - { - void Register(IEventHandler eventHandler); - - void Register(Action action) where TEventData : IEventData; - - void UnRegister(Type eventHandler); - - // void Trigger(TEventData eventData) where TEventData : IEventData; - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Events +{ + public interface IEventBus + { + void Register(IEventHandler eventHandler); + + void Register(Action action) where TEventData : IEventData; + + void UnRegister(Type eventHandler); + + // void Trigger(TEventData eventData) where TEventData : IEventData; + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EventBus/IEventData.cs b/Infrastructure/Hncore.Infrastructure/EventBus/IEventData.cs index 0e0a12b..5f3a3cf 100644 --- a/Infrastructure/Hncore.Infrastructure/EventBus/IEventData.cs +++ b/Infrastructure/Hncore.Infrastructure/EventBus/IEventData.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Events -{ - /// - /// 定义事件源接口,所有的事件源都要实现该接口 - /// - public interface IEventData - { - /// - /// 事件发生的时间 - /// - DateTime EventTime { get; set; } - - /// - /// 触发事件的对象 - /// - Object EventSource { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Events +{ + /// + /// 定义事件源接口,所有的事件源都要实现该接口 + /// + public interface IEventData + { + /// + /// 事件发生的时间 + /// + DateTime EventTime { get; set; } + + /// + /// 触发事件的对象 + /// + Object EventSource { get; set; } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/EventBus/IEventHandler.cs b/Infrastructure/Hncore.Infrastructure/EventBus/IEventHandler.cs index bc296f9..9044bb1 100644 --- a/Infrastructure/Hncore.Infrastructure/EventBus/IEventHandler.cs +++ b/Infrastructure/Hncore.Infrastructure/EventBus/IEventHandler.cs @@ -1,29 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Events -{ - /// - /// 定义事件处理器公共接口,所有的事件处理都要实现该接口 - /// - public interface IEventHandler - { - } - - /// - /// 泛型事件处理器接口 - /// - /// - public interface IEventHandler : IEventHandler where TEventData : IEventData - { - bool Ansyc { get; set; } - /// - /// 事件处理器实现该方法来处理事件 - /// - /// - void HandleEvent(TEventData eventData); - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Events +{ + /// + /// 定义事件处理器公共接口,所有的事件处理都要实现该接口 + /// + public interface IEventHandler + { + } + + /// + /// 泛型事件处理器接口 + /// + /// + public interface IEventHandler : IEventHandler where TEventData : IEventData + { + bool Ansyc { get; set; } + /// + /// 事件处理器实现该方法来处理事件 + /// + /// + void HandleEvent(TEventData eventData); + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Extension/AssemblyExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/AssemblyExtension.cs index 1b36781..c557ae1 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/AssemblyExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/AssemblyExtension.cs @@ -1,21 +1,21 @@ -using System; -using System.Reflection; - -namespace Hncore.Infrastructure.Extension -{ - /// - /// 程序集扩展类 - /// - public static class AssemblyExtension - { - /// - ///得到程序集友好名字 - /// - /// - /// - public static string GetFriendName(this Assembly asm) - { - return asm.ManifestModule?.Name?.TrimEnd(".dll".ToCharArray()); - } - } +using System; +using System.Reflection; + +namespace Hncore.Infrastructure.Extension +{ + /// + /// 程序集扩展类 + /// + public static class AssemblyExtension + { + /// + ///得到程序集友好名字 + /// + /// + /// + public static string GetFriendName(this Assembly asm) + { + return asm.ManifestModule?.Name?.TrimEnd(".dll".ToCharArray()); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/BoolExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/BoolExtension.cs index bd7c500..64894a3 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/BoolExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/BoolExtension.cs @@ -1,60 +1,60 @@ -using System; - -namespace Hncore.Infrastructure.Extension -{ - /// - /// bool类型扩展 - /// - public static class BoolExtension - { - /// - /// 转为bool - /// - /// - /// - public static bool ToBool(this bool? para) - { - if (para == null) - { - return false; - } - - return Convert.ToBoolean(para); - } - - - /// - /// 转为bool - /// - /// - /// - public static bool ToBool(this object obj) - { - if (obj == null) - { - return false; - } - - bool.TryParse(obj.ToString(), out var para); - - return para; - } - - public static bool ToBool(this sbyte? obj) - { - if (obj == null) - { - return false; - } - - sbyte num = (sbyte) obj; - - if (num == 1) - { - return true; - } - - return false; - } - } +using System; + +namespace Hncore.Infrastructure.Extension +{ + /// + /// bool类型扩展 + /// + public static class BoolExtension + { + /// + /// 转为bool + /// + /// + /// + public static bool ToBool(this bool? para) + { + if (para == null) + { + return false; + } + + return Convert.ToBoolean(para); + } + + + /// + /// 转为bool + /// + /// + /// + public static bool ToBool(this object obj) + { + if (obj == null) + { + return false; + } + + bool.TryParse(obj.ToString(), out var para); + + return para; + } + + public static bool ToBool(this sbyte? obj) + { + if (obj == null) + { + return false; + } + + sbyte num = (sbyte) obj; + + if (num == 1) + { + return true; + } + + return false; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/DateTimeExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/DateTimeExtension.cs index 36bae3b..568d568 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/DateTimeExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/DateTimeExtension.cs @@ -1,177 +1,177 @@ -using System; - -namespace Hncore.Infrastructure.Extension -{ - public static class DateTimeExtension - { - public static string Format(this DateTime time, string format = "yyyy-MM-dd") - { - return time.ToString(format); - } - - - private static DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - public static long CurrentTimeMillis() - { - return (long) ((DateTime.UtcNow - Jan1st1970).TotalMilliseconds); - } - - public static long CurrentTimeMillis(DateTime time) - { - return (long) ((time.ToUniversalTime() - Jan1st1970).TotalMilliseconds); - } - - public static string Format(this DateTime? dateTime, string format = "yyyy-MM-dd") - { - return Convert.ToDateTime(dateTime).ToString(format); - } - - public static DateTime ToDateTime(this DateTime? dateTime, DateTime defaultTime) - { - if (dateTime != null) - { - return Convert.ToDateTime(dateTime); - } - - return defaultTime; - } - - public static bool Between(this DateTime time, DateTime beginTime, DateTime endTime) - { - return time >= beginTime && time <= endTime; - } - - /// - /// 取得某月的第一天 - /// - /// 要取得月份第一天的时间 - /// - public static DateTime FirstDayOfMonth(this DateTime datetime) - { - return datetime.AddDays(1 - datetime.Day); - } - - /**/ - /// - /// 取得某月的最后一天 - /// - /// 要取得月份最后一天的时间 - /// - public static DateTime LastDayOfMonth(this DateTime datetime) - { - return datetime.AddDays(1 - datetime.Day).AddMonths(1).AddDays(-1); - } - - /**/ - /// - /// 取得上个月第一天 - /// - /// 要取得上个月第一天的当前时间 - /// - public static DateTime FirstDayOfPreviousMonth(this DateTime datetime) - { - return datetime.AddDays(1 - datetime.Day).AddMonths(-1); - } - - /**/ - /// - /// 取得上个月的最后一天 - /// - /// 要取得上个月最后一天的当前时间 - /// - public static DateTime LastDayOfPrdviousMonth(this DateTime datetime) - { - return datetime.AddDays(1 - datetime.Day).AddDays(-1); - } - - /// - /// 获取时间的Unix时间戳 - /// - /// 时间对象 - /// Unix时间戳 - /// - public static long GetUnixTimeStamp(this DateTime tm) - { - long result = (tm.ToUniversalTime().Ticks - 621355968000000000) / 10000000; - return result; - } - - /// - /// 从Uninx转换时间 - /// - /// 时间对象 - /// 时间戳 - /// 新时间对象 - /// - public static DateTime LoadFromUnixTimeStamp(this DateTime tm,double timeStamp) - { - DateTime startTime=TimeZoneInfo.ConvertTime(new System.DateTime(1970, 1, 1),TimeZoneInfo.Local); - DateTime result=startTime.AddSeconds(timeStamp); - return result; - } - - public static DateTime LoadFromUnixTimeStamp(this long timeStamp) - { - DateTime startTime = TimeZoneInfo.ConvertTime(new System.DateTime(1970, 1, 1), TimeZoneInfo.Local); - DateTime result = startTime.AddSeconds(timeStamp); - return result; - } - - /// - /// 时间戳转换成时间 - /// - /// 时间戳 - /// 是否毫秒级,true毫秒级(默认值) - /// 是否输出本地时间,true本地时间(默认值) - /// - public static DateTime? LoadFromUnixTimeStamp(this string timestamp) - { - if (long.TryParse(timestamp, out long ts)) - { - return ts.LoadFromUnixTimeStamp(); - } - return null; - } - - /// - /// 1970 到现在的秒数 - /// - /// - /// - public static int TimestampFrom19700101(this DateTime time) - { - return (int)(time - new DateTime(1970, 01, 01)).TotalSeconds; - } - - - #region 获取日期、明天 - public static DateTime Date(this DateTime? time) - { - return Convert.ToDateTime(time).Date; - } - public static DateTime NextDate(this DateTime? time) - { - return Convert.ToDateTime(time).Date.AddDays(1); - } - public static DateTime Date(this DateTime time) - { - return time.Date; - } - public static DateTime NextDate(this DateTime time) - { - return time.Date.AddDays(1); - } - #endregion - - public static DateTime Begin(this DateTime time) - { - return new DateTime(time.Year, time.Month, time.Day); - } - - public static DateTime End(this DateTime time) - { - return new DateTime(time.Year, time.Month, time.Day,23,59,59); - } - } +using System; + +namespace Hncore.Infrastructure.Extension +{ + public static class DateTimeExtension + { + public static string Format(this DateTime time, string format = "yyyy-MM-dd") + { + return time.ToString(format); + } + + + private static DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + public static long CurrentTimeMillis() + { + return (long) ((DateTime.UtcNow - Jan1st1970).TotalMilliseconds); + } + + public static long CurrentTimeMillis(DateTime time) + { + return (long) ((time.ToUniversalTime() - Jan1st1970).TotalMilliseconds); + } + + public static string Format(this DateTime? dateTime, string format = "yyyy-MM-dd") + { + return Convert.ToDateTime(dateTime).ToString(format); + } + + public static DateTime ToDateTime(this DateTime? dateTime, DateTime defaultTime) + { + if (dateTime != null) + { + return Convert.ToDateTime(dateTime); + } + + return defaultTime; + } + + public static bool Between(this DateTime time, DateTime beginTime, DateTime endTime) + { + return time >= beginTime && time <= endTime; + } + + /// + /// 取得某月的第一天 + /// + /// 要取得月份第一天的时间 + /// + public static DateTime FirstDayOfMonth(this DateTime datetime) + { + return datetime.AddDays(1 - datetime.Day); + } + + /**/ + /// + /// 取得某月的最后一天 + /// + /// 要取得月份最后一天的时间 + /// + public static DateTime LastDayOfMonth(this DateTime datetime) + { + return datetime.AddDays(1 - datetime.Day).AddMonths(1).AddDays(-1); + } + + /**/ + /// + /// 取得上个月第一天 + /// + /// 要取得上个月第一天的当前时间 + /// + public static DateTime FirstDayOfPreviousMonth(this DateTime datetime) + { + return datetime.AddDays(1 - datetime.Day).AddMonths(-1); + } + + /**/ + /// + /// 取得上个月的最后一天 + /// + /// 要取得上个月最后一天的当前时间 + /// + public static DateTime LastDayOfPrdviousMonth(this DateTime datetime) + { + return datetime.AddDays(1 - datetime.Day).AddDays(-1); + } + + /// + /// 获取时间的Unix时间戳 + /// + /// 时间对象 + /// Unix时间戳 + /// + public static long GetUnixTimeStamp(this DateTime tm) + { + long result = (tm.ToUniversalTime().Ticks - 621355968000000000) / 10000000; + return result; + } + + /// + /// 从Uninx转换时间 + /// + /// 时间对象 + /// 时间戳 + /// 新时间对象 + /// + public static DateTime LoadFromUnixTimeStamp(this DateTime tm,double timeStamp) + { + DateTime startTime=TimeZoneInfo.ConvertTime(new System.DateTime(1970, 1, 1),TimeZoneInfo.Local); + DateTime result=startTime.AddSeconds(timeStamp); + return result; + } + + public static DateTime LoadFromUnixTimeStamp(this long timeStamp) + { + DateTime startTime = TimeZoneInfo.ConvertTime(new System.DateTime(1970, 1, 1), TimeZoneInfo.Local); + DateTime result = startTime.AddSeconds(timeStamp); + return result; + } + + /// + /// 时间戳转换成时间 + /// + /// 时间戳 + /// 是否毫秒级,true毫秒级(默认值) + /// 是否输出本地时间,true本地时间(默认值) + /// + public static DateTime? LoadFromUnixTimeStamp(this string timestamp) + { + if (long.TryParse(timestamp, out long ts)) + { + return ts.LoadFromUnixTimeStamp(); + } + return null; + } + + /// + /// 1970 到现在的秒数 + /// + /// + /// + public static int TimestampFrom19700101(this DateTime time) + { + return (int)(time - new DateTime(1970, 01, 01)).TotalSeconds; + } + + + #region 获取日期、明天 + public static DateTime Date(this DateTime? time) + { + return Convert.ToDateTime(time).Date; + } + public static DateTime NextDate(this DateTime? time) + { + return Convert.ToDateTime(time).Date.AddDays(1); + } + public static DateTime Date(this DateTime time) + { + return time.Date; + } + public static DateTime NextDate(this DateTime time) + { + return time.Date.AddDays(1); + } + #endregion + + public static DateTime Begin(this DateTime time) + { + return new DateTime(time.Year, time.Month, time.Day); + } + + public static DateTime End(this DateTime time) + { + return new DateTime(time.Year, time.Month, time.Day,23,59,59); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/DbDataReaderExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/DbDataReaderExtension.cs index c3d545d..4ffc33d 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/DbDataReaderExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/DbDataReaderExtension.cs @@ -1,24 +1,24 @@ -using System.Collections.Generic; -using System.Data.Common; -using System.Dynamic; - -namespace Hncore.Infrastructure.Extension -{ - public static class DbDataReaderExtension - { - public static IDictionary GetDataRow(this DbDataReader dataReader) - { - var dataRow = new ExpandoObject() as IDictionary; - - for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++) - { - dataRow.Add( - dataReader.GetName(iFiled), - dataReader.IsDBNull(iFiled) ? "" : dataReader[iFiled] - ); - } - - return dataRow; - } - } +using System.Collections.Generic; +using System.Data.Common; +using System.Dynamic; + +namespace Hncore.Infrastructure.Extension +{ + public static class DbDataReaderExtension + { + public static IDictionary GetDataRow(this DbDataReader dataReader) + { + var dataRow = new ExpandoObject() as IDictionary; + + for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++) + { + dataRow.Add( + dataReader.GetName(iFiled), + dataReader.IsDBNull(iFiled) ? "" : dataReader[iFiled] + ); + } + + return dataRow; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/EnumExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/EnumExtension.cs index 428e469..09cff73 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/EnumExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/EnumExtension.cs @@ -1,149 +1,149 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Reflection; - -namespace Hncore.Infrastructure.Extension -{ - public static class EnumExtension - { - public static Dictionary ToDictionary() - { - Dictionary dic = new Dictionary(); - string namestr = ""; - foreach (var e in Enum.GetValues(typeof(T))) - { - namestr = ""; - - object[] objArrDisplay = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true);//Display - if (objArrDisplay.Any()) - { - var da = objArrDisplay[0] as DisplayAttribute; - namestr = da.Name; - } - else - { - object[] objArrDescription = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), true);//Description - namestr = objArrDescription.Any() ? (objArrDescription[0] as DescriptionAttribute).Description : e.ToString(); - } - int value = Convert.ToInt32(e); - - - - // string str = item.GetDescription(); - // int value = (int) item; - - dic.Add(value, namestr); - } - - return dic; - } - #region 获取枚举的相关信息的集合 - /// - /// 获取枚举的相关信息的集合 - /// - /// - /// - public static List EnumToList() - { - var list = new List(); - foreach (var e in Enum.GetValues(typeof(T))) - { - var m = new EnumInfo(); - object[] objArrDisplay = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true);//Display - if (objArrDisplay.Any()) - { - var da = objArrDisplay[0] as DisplayAttribute; - m.Name = da.Name; - } - else - { - object[] objArrDescription = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), true);//Description - m.Name = objArrDescription.Any() ? (objArrDescription[0] as DescriptionAttribute).Description : e.ToString(); - } - m.Value = Convert.ToInt32(e); - list.Add(m); - } - return list; - } - public class EnumInfo - { - public string Name { set; get; } - - public int Value { set; get; } - } - #endregion - public static string GetDisplayName(T enumValue) - { - object[] objArrDisplay = enumValue.GetType().GetField(enumValue.ToString()) - ?.GetCustomAttributes(typeof(DisplayAttribute), true);//Display - if (objArrDisplay!=null&&objArrDisplay.Any()) - { - var da = objArrDisplay[0] as DisplayAttribute; - return da?.Name; - } - return ""; - } - - /// - /// 获取枚举上通过DisplayName、Description或Display柱注的名称 - /// 优先级为DisplayName>Description>Display - /// - /// 枚举值 - /// 枚举名称 - /// - public static string GetEnumDisplayName(this Enum e) - { - try - { - Type t = e.GetType(); - FieldInfo fi = t.GetField(Enum.GetName(t, e)); - var dna = fi.GetCustomAttribute(); - if (dna != null) - return dna.DisplayName; - var da = fi.GetCustomAttribute(); - if (da != null) - return da.Description; - var d = fi.GetCustomAttribute(); - if (d != null) - return d.Name; - } - catch (Exception ex) - { - return "获取枚举"+e.GetType().FullName+"名称错误:"+ex.Message; - } - return ""; - } - - public static string ToHtmlSelectOptions() - { - var dic = ToDictionary(); - - string str = ""; - - foreach (var key in dic.Keys) - { - str += ""; - } - - return str; - } - - #region 判断值是否在枚举类型中存在 - - /// - /// 判断值是否在枚举中存在 - /// - /// 需要判断的参数 - /// 枚举类型 - /// - public static bool IsExist(this int enumValue, Type enumType) - { - return Enum.IsDefined(enumType, enumValue); - } - - #endregion - } +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; + +namespace Hncore.Infrastructure.Extension +{ + public static class EnumExtension + { + public static Dictionary ToDictionary() + { + Dictionary dic = new Dictionary(); + string namestr = ""; + foreach (var e in Enum.GetValues(typeof(T))) + { + namestr = ""; + + object[] objArrDisplay = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true);//Display + if (objArrDisplay.Any()) + { + var da = objArrDisplay[0] as DisplayAttribute; + namestr = da.Name; + } + else + { + object[] objArrDescription = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), true);//Description + namestr = objArrDescription.Any() ? (objArrDescription[0] as DescriptionAttribute).Description : e.ToString(); + } + int value = Convert.ToInt32(e); + + + + // string str = item.GetDescription(); + // int value = (int) item; + + dic.Add(value, namestr); + } + + return dic; + } + #region 获取枚举的相关信息的集合 + /// + /// 获取枚举的相关信息的集合 + /// + /// + /// + public static List EnumToList() + { + var list = new List(); + foreach (var e in Enum.GetValues(typeof(T))) + { + var m = new EnumInfo(); + object[] objArrDisplay = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true);//Display + if (objArrDisplay.Any()) + { + var da = objArrDisplay[0] as DisplayAttribute; + m.Name = da.Name; + } + else + { + object[] objArrDescription = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), true);//Description + m.Name = objArrDescription.Any() ? (objArrDescription[0] as DescriptionAttribute).Description : e.ToString(); + } + m.Value = Convert.ToInt32(e); + list.Add(m); + } + return list; + } + public class EnumInfo + { + public string Name { set; get; } + + public int Value { set; get; } + } + #endregion + public static string GetDisplayName(T enumValue) + { + object[] objArrDisplay = enumValue.GetType().GetField(enumValue.ToString()) + ?.GetCustomAttributes(typeof(DisplayAttribute), true);//Display + if (objArrDisplay!=null&&objArrDisplay.Any()) + { + var da = objArrDisplay[0] as DisplayAttribute; + return da?.Name; + } + return ""; + } + + /// + /// 获取枚举上通过DisplayName、Description或Display柱注的名称 + /// 优先级为DisplayName>Description>Display + /// + /// 枚举值 + /// 枚举名称 + /// + public static string GetEnumDisplayName(this Enum e) + { + try + { + Type t = e.GetType(); + FieldInfo fi = t.GetField(Enum.GetName(t, e)); + var dna = fi.GetCustomAttribute(); + if (dna != null) + return dna.DisplayName; + var da = fi.GetCustomAttribute(); + if (da != null) + return da.Description; + var d = fi.GetCustomAttribute(); + if (d != null) + return d.Name; + } + catch (Exception ex) + { + return "获取枚举"+e.GetType().FullName+"名称错误:"+ex.Message; + } + return ""; + } + + public static string ToHtmlSelectOptions() + { + var dic = ToDictionary(); + + string str = ""; + + foreach (var key in dic.Keys) + { + str += ""; + } + + return str; + } + + #region 判断值是否在枚举类型中存在 + + /// + /// 判断值是否在枚举中存在 + /// + /// 需要判断的参数 + /// 枚举类型 + /// + public static bool IsExist(this int enumValue, Type enumType) + { + return Enum.IsDefined(enumType, enumValue); + } + + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/ExceptionExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/ExceptionExtension.cs index 91579cc..9008fe4 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/ExceptionExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/ExceptionExtension.cs @@ -1,25 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using TinyPinyin.Core; - -namespace Hncore.Infrastructure.Extension -{ - public static class ExceptionExtension - { - public static string GetInfo(this Exception ex) - { - var info = $"S:{ex.Source},M:{ex.Message},ST:{ex.StackTrace}-----"; - if (ex.InnerException != null) - { - info += ex.InnerException.GetInfo(); - } - return info; - } - - } +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; +using TinyPinyin.Core; + +namespace Hncore.Infrastructure.Extension +{ + public static class ExceptionExtension + { + public static string GetInfo(this Exception ex) + { + var info = $"S:{ex.Source},M:{ex.Message},ST:{ex.StackTrace}-----"; + if (ex.InnerException != null) + { + info += ex.InnerException.GetInfo(); + } + return info; + } + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/HttpClientExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/HttpClientExtension.cs index 3e94f7f..81a94a8 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/HttpClientExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/HttpClientExtension.cs @@ -1,94 +1,94 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; -using Hncore.Infrastructure.Serializer; - -namespace Hncore.Infrastructure.Extension -{ - public static class HttpClientFactoryExtension - { - public static HttpClient CreateClient(this IHttpClientFactory factory, TimeSpan timeOut) - { - var client = factory.CreateClient(); - - client.Timeout = timeOut; - - return client; - } - } - - public static class HttpClientExtension - { - /// - /// post请求,ContentType:application/json - /// - /// - /// - /// - /// - public static async Task PostAsJson(this HttpClient httpClient, string path, object data, - Encoding encoding = null) - { - if (encoding == null) - { - encoding = Encoding.UTF8; - } - - string content = ""; - - if (data is string s) - { - content = s; - } - else - { - content = data.ToJson(); - } - - HttpContent httpContent = new StringContent(content, encoding); - - httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - - return await httpClient.PostAsync(path, httpContent); - } - - /// - /// post请求,ContentType:application/json - /// - /// - /// - /// - /// - public static async Task PostAsJsonGetString(this HttpClient httpClient, string path, object data, - Encoding encoding = null) - { - var res = await httpClient.PostAsJson(path, data, encoding); - - return await res.Content.ReadAsStringAsync(); - } - - public static async Task PostAsForm(this HttpClient httpClient, string path, IEnumerable> data, - Encoding encoding = null) - { - if (encoding == null) - { - encoding = Encoding.UTF8; - } - - HttpContent httpContent = new FormUrlEncodedContent(data); - - return await httpClient.PostAsync(path, httpContent); - } - public static async Task PostAsFormGetString(this HttpClient httpClient, string path, IEnumerable> data, -Encoding encoding = null) - { - var resp=await httpClient.PostAsForm(path, data, encoding); - - return await resp.Content.ReadAsStringAsync(); - } - } +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Hncore.Infrastructure.Serializer; + +namespace Hncore.Infrastructure.Extension +{ + public static class HttpClientFactoryExtension + { + public static HttpClient CreateClient(this IHttpClientFactory factory, TimeSpan timeOut) + { + var client = factory.CreateClient(); + + client.Timeout = timeOut; + + return client; + } + } + + public static class HttpClientExtension + { + /// + /// post请求,ContentType:application/json + /// + /// + /// + /// + /// + public static async Task PostAsJson(this HttpClient httpClient, string path, object data, + Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + string content = ""; + + if (data is string s) + { + content = s; + } + else + { + content = data.ToJson(); + } + + HttpContent httpContent = new StringContent(content, encoding); + + httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + + return await httpClient.PostAsync(path, httpContent); + } + + /// + /// post请求,ContentType:application/json + /// + /// + /// + /// + /// + public static async Task PostAsJsonGetString(this HttpClient httpClient, string path, object data, + Encoding encoding = null) + { + var res = await httpClient.PostAsJson(path, data, encoding); + + return await res.Content.ReadAsStringAsync(); + } + + public static async Task PostAsForm(this HttpClient httpClient, string path, IEnumerable> data, + Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + HttpContent httpContent = new FormUrlEncodedContent(data); + + return await httpClient.PostAsync(path, httpContent); + } + public static async Task PostAsFormGetString(this HttpClient httpClient, string path, IEnumerable> data, +Encoding encoding = null) + { + var resp=await httpClient.PostAsForm(path, data, encoding); + + return await resp.Content.ReadAsStringAsync(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/ListExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/ListExtension.cs index bd277f4..ecdf4fc 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/ListExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/ListExtension.cs @@ -1,126 +1,126 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Hncore.Infrastructure.Extension -{ - public static class ListExtension - { - public static bool NullOrEmpty(this IList list) - { - if (list == null) - { - return true; - } - - if (!list.Any()) - { - return true; - } - return false; - } - - #region 转换几个中所有元素的类型 - /// - /// 转换几个中所有元素的类型 - /// - /// - /// - /// - public static List ConvertListType(this List list) - { - if (list == null) - { - return null; - } - List newlist = new List(); - foreach (T t in list) - { - newlist.Add((TO)Convert.ChangeType(t, typeof(TO))); - } - return newlist; - } - #endregion - - /// - /// 添加 - /// - /// - /// - /// - /// - public static List AddNew(this List list, T item) - { - list.Add(item); - return list; - } - - /// - /// 添加多个集合 - /// - /// - /// - /// - /// - public static List AddNewMult(this List list, List item) - { - list.AddRange(item); - return list; - } - - /// - /// 移除 - /// - /// - /// - /// - /// - public static List RemoveNew(this List list, T item) - { - list.Remove(item); - return list; - } - - #region 按条件移除 - - /// - /// 移除单条符合条件的数据 - /// - /// - /// 条件 - /// - /// - public static List RemoveNew(this List list, Func condtion) - { - List listTemp = list; - var item = listTemp.FirstOrDefault(condtion); - if (item != null) - { - listTemp.Remove(item); - } - return list; - } - - /// - /// 移除多条满足条件 - /// - /// - /// 条件 - /// - /// - public static List RemoveMultNew(this List list, Func condtion) - { - List listTemp = list; - var items = listTemp.Where(condtion).ToList() ?? new List(); - foreach (var item in items) - { - listTemp.Remove(item); - } - return list; - } - - #endregion - - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Hncore.Infrastructure.Extension +{ + public static class ListExtension + { + public static bool NullOrEmpty(this IList list) + { + if (list == null) + { + return true; + } + + if (!list.Any()) + { + return true; + } + return false; + } + + #region 转换几个中所有元素的类型 + /// + /// 转换几个中所有元素的类型 + /// + /// + /// + /// + public static List ConvertListType(this List list) + { + if (list == null) + { + return null; + } + List newlist = new List(); + foreach (T t in list) + { + newlist.Add((TO)Convert.ChangeType(t, typeof(TO))); + } + return newlist; + } + #endregion + + /// + /// 添加 + /// + /// + /// + /// + /// + public static List AddNew(this List list, T item) + { + list.Add(item); + return list; + } + + /// + /// 添加多个集合 + /// + /// + /// + /// + /// + public static List AddNewMult(this List list, List item) + { + list.AddRange(item); + return list; + } + + /// + /// 移除 + /// + /// + /// + /// + /// + public static List RemoveNew(this List list, T item) + { + list.Remove(item); + return list; + } + + #region 按条件移除 + + /// + /// 移除单条符合条件的数据 + /// + /// + /// 条件 + /// + /// + public static List RemoveNew(this List list, Func condtion) + { + List listTemp = list; + var item = listTemp.FirstOrDefault(condtion); + if (item != null) + { + listTemp.Remove(item); + } + return list; + } + + /// + /// 移除多条满足条件 + /// + /// + /// 条件 + /// + /// + public static List RemoveMultNew(this List list, Func condtion) + { + List listTemp = list; + var items = listTemp.Where(condtion).ToList() ?? new List(); + foreach (var item in items) + { + listTemp.Remove(item); + } + return list; + } + + #endregion + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/ListForEachExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/ListForEachExtension.cs index 770df28..cbf4278 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/ListForEachExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/ListForEachExtension.cs @@ -1,19 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Extension -{ - public static class ListForEachExtension - { - public static async Task ForEachAsync(this IEnumerable list, Func func) - { - foreach (T value in list) - { - await func(value); - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Extension +{ + public static class ListForEachExtension + { + public static async Task ForEachAsync(this IEnumerable list, Func func) + { + foreach (T value in list) + { + await func(value); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/NumberExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/NumberExtension.cs index 5fe694d..8d13a31 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/NumberExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/NumberExtension.cs @@ -1,63 +1,63 @@ -using System; -using Newtonsoft.Json.Linq; - -namespace Hncore.Infrastructure.Extension -{ - public static class NumberExtension - { - public static int ToInt(this int? num) - { - if (num == null) - { - return 0; - } - - return Convert.ToInt32(num); - } - - public static int ToInt(this JToken obj) - { - if (obj == null) - { - return 0; - } - - int.TryParse(obj.ToString(), out var num); - - return num; - } - - public static decimal ToDecimal(this decimal? num) - { - if (num == null) - { - - return 0; - } - - return Convert.ToDecimal(num); - } - - public static long ToLong(this long? num) - { - if (num == null) - { - return 0; - } - - return (long) num; - } - - public static int ToInt(this bool flag) - { - if (flag) - { - return 1; - } - else - { - return 0; - } - } - } +using System; +using Newtonsoft.Json.Linq; + +namespace Hncore.Infrastructure.Extension +{ + public static class NumberExtension + { + public static int ToInt(this int? num) + { + if (num == null) + { + return 0; + } + + return Convert.ToInt32(num); + } + + public static int ToInt(this JToken obj) + { + if (obj == null) + { + return 0; + } + + int.TryParse(obj.ToString(), out var num); + + return num; + } + + public static decimal ToDecimal(this decimal? num) + { + if (num == null) + { + + return 0; + } + + return Convert.ToDecimal(num); + } + + public static long ToLong(this long? num) + { + if (num == null) + { + return 0; + } + + return (long) num; + } + + public static int ToInt(this bool flag) + { + if (flag) + { + return 1; + } + else + { + return 0; + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/ObjectExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/ObjectExtension.cs index 17227b4..4a168fc 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/ObjectExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/ObjectExtension.cs @@ -1,144 +1,144 @@ -using Nelibur.ObjectMapper; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Dynamic; -using System.Linq; -using System.Threading; - -namespace Hncore.Infrastructure.Extension -{ - public static class ObjectExtension - { - /// - /// 将对象[主要是匿名对象]转换为dynamic - /// - public static dynamic ToDynamic(this object obj, decimal defaultVal) - { - decimal result; - if (obj != null) - if (decimal.TryParse(obj.ToString(), out result)) - return result; - else - return defaultVal; - return defaultVal; - } - - // This extension method is broken out so you can use a similar pattern with - // other MetaData elements in the future. This is your base method for each. - public static T GetAttribute(this object value) where T : Attribute - { - var type = value.GetType(); - var memberInfo = type.GetMember(value.ToString()); - if (!memberInfo.Any()) - { - return null; - } - - var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false); - return (T) attributes[0]; - } - - // This method creates a specific call to the above method, requesting the - // Description MetaData attribute. - public static string GetDescription(this object value) - { - var desAttribute = value.GetAttribute(); - if (desAttribute != null) - { - return desAttribute.Description; - } - - var displayAttribute = value.GetAttribute(); - if (displayAttribute != null) - { - return displayAttribute.Name ?? displayAttribute.Description; - } - - return ""; - } - - #region 得到枚举字典(key对应枚举的值,value对应枚举的注释) - - /// - /// 得到枚举字典(key对应枚举的值,value对应枚举的注释) - /// - /// - /// - public static Dictionary ToDescriptionDictionary() - { - Array values = Enum.GetValues(typeof(TEnum)); - Dictionary nums = new Dictionary(); - foreach (Enum value in values) - { - nums.Add(value, GetDescription(value)); - } - - return nums; - } - - #endregion - - #region 实体中string属性执行Trim() - /// - /// 对象string属性执行Trim() - /// - /// - /// - /// - public static T ObjectStrTrim(this T t) where T:new () - { - if (t != null) - { - foreach (var pi in t.GetType().GetProperties()) - { - if (pi.PropertyType.Equals(typeof(string)) && pi.GetValue(t, null) != null)//判断属性的类型是不是String - { - pi.SetValue(t, pi.GetValue(t, null).ToString().Trim(), null);//给泛型的属性赋值 - } - } - } - return t; - } - #endregion - - #region - public static T save(this T obj,string key) where T:class,new() - { - Dictionary dic = c.Value ?? new Dictionary(); - dic.Add(key,obj); - c.Value = dic; - return obj; - } - public static AsyncLocal> c=new AsyncLocal>(); - #endregion - - #region 对象转换 - - public static T MapTo(this Object model) - { - var productDto = TinyMapper.Map(model); - - return productDto; - } - public static IEnumerable MapsTo(this Object model) - { - var generic = model.GetType().GetGenericTypeDefinition(); - - if (generic == typeof(List<>)) - { - return TinyMapper.Map>(model); - } - if (generic == typeof(Collection<>)) - { - return TinyMapper.Map>(model); - } - - throw new Exception("不合法的转换"); - } - - #endregion - } +using Nelibur.ObjectMapper; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Dynamic; +using System.Linq; +using System.Threading; + +namespace Hncore.Infrastructure.Extension +{ + public static class ObjectExtension + { + /// + /// 将对象[主要是匿名对象]转换为dynamic + /// + public static dynamic ToDynamic(this object obj, decimal defaultVal) + { + decimal result; + if (obj != null) + if (decimal.TryParse(obj.ToString(), out result)) + return result; + else + return defaultVal; + return defaultVal; + } + + // This extension method is broken out so you can use a similar pattern with + // other MetaData elements in the future. This is your base method for each. + public static T GetAttribute(this object value) where T : Attribute + { + var type = value.GetType(); + var memberInfo = type.GetMember(value.ToString()); + if (!memberInfo.Any()) + { + return null; + } + + var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false); + return (T) attributes[0]; + } + + // This method creates a specific call to the above method, requesting the + // Description MetaData attribute. + public static string GetDescription(this object value) + { + var desAttribute = value.GetAttribute(); + if (desAttribute != null) + { + return desAttribute.Description; + } + + var displayAttribute = value.GetAttribute(); + if (displayAttribute != null) + { + return displayAttribute.Name ?? displayAttribute.Description; + } + + return ""; + } + + #region 得到枚举字典(key对应枚举的值,value对应枚举的注释) + + /// + /// 得到枚举字典(key对应枚举的值,value对应枚举的注释) + /// + /// + /// + public static Dictionary ToDescriptionDictionary() + { + Array values = Enum.GetValues(typeof(TEnum)); + Dictionary nums = new Dictionary(); + foreach (Enum value in values) + { + nums.Add(value, GetDescription(value)); + } + + return nums; + } + + #endregion + + #region 实体中string属性执行Trim() + /// + /// 对象string属性执行Trim() + /// + /// + /// + /// + public static T ObjectStrTrim(this T t) where T:new () + { + if (t != null) + { + foreach (var pi in t.GetType().GetProperties()) + { + if (pi.PropertyType.Equals(typeof(string)) && pi.GetValue(t, null) != null)//判断属性的类型是不是String + { + pi.SetValue(t, pi.GetValue(t, null).ToString().Trim(), null);//给泛型的属性赋值 + } + } + } + return t; + } + #endregion + + #region + public static T save(this T obj,string key) where T:class,new() + { + Dictionary dic = c.Value ?? new Dictionary(); + dic.Add(key,obj); + c.Value = dic; + return obj; + } + public static AsyncLocal> c=new AsyncLocal>(); + #endregion + + #region 对象转换 + + public static T MapTo(this Object model) + { + var productDto = TinyMapper.Map(model); + + return productDto; + } + public static IEnumerable MapsTo(this Object model) + { + var generic = model.GetType().GetGenericTypeDefinition(); + + if (generic == typeof(List<>)) + { + return TinyMapper.Map>(model); + } + if (generic == typeof(Collection<>)) + { + return TinyMapper.Map>(model); + } + + throw new Exception("不合法的转换"); + } + + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/RequestExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/RequestExtension.cs index 10c5293..e76de67 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/RequestExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/RequestExtension.cs @@ -1,46 +1,46 @@ - - -using Microsoft.AspNetCore.Http; -using System; -using System.Linq; - -namespace Hncore.Infrastructure.Extension -{ - - public static class RequestExtension - { - public static string Get(this HttpRequest request, string key) - { - if (request.Query.ContainsKey(key)) - { - return request.Query[key]; - } - return ""; - } - public static int GetInt(this HttpRequest request, string key) - { - if (request.Query.ContainsKey(key)) - { - return Convert.ToInt32(request.Query[key]); - } - return 0; - } - - public static string GetUrl(this HttpRequest request, bool full=true) - { - if (full) - { - return $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"; - } - return $"{request.Path}{request.QueryString}"; - } - - public static string Remove(this HttpRequest request, string key) - { - var q = request.Query.Where(m => !m.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)); - var kvs = q.Select(m => $"{m.Key}={m.Value}"); - return string.Join("&", kvs); - - } - } + + +using Microsoft.AspNetCore.Http; +using System; +using System.Linq; + +namespace Hncore.Infrastructure.Extension +{ + + public static class RequestExtension + { + public static string Get(this HttpRequest request, string key) + { + if (request.Query.ContainsKey(key)) + { + return request.Query[key]; + } + return ""; + } + public static int GetInt(this HttpRequest request, string key) + { + if (request.Query.ContainsKey(key)) + { + return Convert.ToInt32(request.Query[key]); + } + return 0; + } + + public static string GetUrl(this HttpRequest request, bool full=true) + { + if (full) + { + return $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"; + } + return $"{request.Path}{request.QueryString}"; + } + + public static string Remove(this HttpRequest request, string key) + { + var q = request.Query.Where(m => !m.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)); + var kvs = q.Select(m => $"{m.Key}={m.Value}"); + return string.Join("&", kvs); + + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/StreamExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/StreamExtension.cs index d3b16e1..617f45d 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/StreamExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/StreamExtension.cs @@ -1,21 +1,21 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Web; - -namespace Hncore.Infrastructure.Extension -{ - public static class StreamExtension - { - public async static Task ReadAsStringAsync(this Stream stream) - { - var reader = new StreamReader(stream); - return await reader.ReadToEndAsync(); - } - } +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Web; + +namespace Hncore.Infrastructure.Extension +{ + public static class StreamExtension + { + public async static Task ReadAsStringAsync(this Stream stream) + { + var reader = new StreamReader(stream); + return await reader.ReadToEndAsync(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Extension/StringExtension.cs b/Infrastructure/Hncore.Infrastructure/Extension/StringExtension.cs index 7755a0b..610335e 100644 --- a/Infrastructure/Hncore.Infrastructure/Extension/StringExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Extension/StringExtension.cs @@ -1,779 +1,779 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using TinyPinyin.Core; - -namespace Hncore.Infrastructure.Extension -{ - public static class StringExtension - { - #region Url、Html编码 - - [DebuggerStepThrough] - public static string UrlEncode(this string target) - { - return HttpUtility.UrlEncode(target); - } - - [DebuggerStepThrough] - public static string UrlEncode(this string target, Encoding encoding) - { - return HttpUtility.UrlEncode(target, encoding); - } - - [DebuggerStepThrough] - public static string UrlDecode(this string target) - { - return HttpUtility.UrlDecode(target); - } - - [DebuggerStepThrough] - public static string UrlDecode(this string target, Encoding encoding) - { - return HttpUtility.UrlDecode(target, encoding); - } - - [DebuggerStepThrough] - public static string AttributeEncode(this string target) - { - return HttpUtility.HtmlAttributeEncode(target); - } - - [DebuggerStepThrough] - public static string HtmlEncode(this string target) - { - return HttpUtility.HtmlEncode(target); - } - - [DebuggerStepThrough] - public static string HtmlDecode(this string target) - { - return HttpUtility.HtmlDecode(target); - } - - #endregion - - #region Unicode编码 - - /// - /// 汉字转换为Unicode编码 - /// - /// 要编码的汉字字符串 - /// Unicode编码的的字符串 - public static string ToUnicode(this string str) - { - if (string.IsNullOrEmpty(str)) - { - return str; - } - - StringBuilder unicode = new StringBuilder(); - - foreach (char chr in str) - { - // Get the integral value of the character. - int value = Convert.ToInt32(chr); - // Convert the decimal value to a hexadecimal value in string form. - string hexOutput = String.Format("{0:x}", value); - - unicode.Append("\\u" + hexOutput); - } - - return unicode.ToString(); - } - - /// - /// 将Unicode编码转换为汉字字符串 - /// - /// Unicode编码字符串 - /// 汉字字符串 - public static string FromUnicode(this string unicode) - { - if (string.IsNullOrEmpty(unicode)) - { - return unicode; - } - - StringBuilder str = new StringBuilder(); - - unicode = unicode.Replace("\\u", "_"); - - string[] hex = unicode.Split('_'); - - for (int i = 1; i < hex.Length; i++) - { - int data = Convert.ToInt32(hex[i].ToString(), 16); - - str.Append((char) data); - } - - return str.ToString(); - } - - #endregion - - #region 清除脚本 - - /// - /// 清除脚本 - /// - /// - /// - public static string NoHTML(this string Htmlstring) - { - //删除脚本 - Htmlstring = Regex.Replace(Htmlstring, @"]*?>.*?", "", RegexOptions.IgnoreCase); - //删除HTML - Htmlstring = Regex.Replace(Htmlstring, @"<.*?>|&.{4,5}", "", RegexOptions.IgnoreCase); - Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "", RegexOptions.IgnoreCase); - Htmlstring = Regex.Replace(Htmlstring, @"([\r\n])[\s]+", "", RegexOptions.IgnoreCase); - Htmlstring = Regex.Replace(Htmlstring, @"-->", "", RegexOptions.IgnoreCase); - Htmlstring = Regex.Replace(Htmlstring, @"", "", RegexOptions.IgnoreCase); + Htmlstring = Regex.Replace(Htmlstring, @" - netstandard2.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - - + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/Infrastructure/Hncore.Infrastructure/IOC/IDependency.cs b/Infrastructure/Hncore.Infrastructure/IOC/IDependency.cs index c73054c..6ed28d6 100644 --- a/Infrastructure/Hncore.Infrastructure/IOC/IDependency.cs +++ b/Infrastructure/Hncore.Infrastructure/IOC/IDependency.cs @@ -1,7 +1,7 @@ -namespace Hncore.Infrastructure.IOC -{ - public interface IDependency - { - - } +namespace Hncore.Infrastructure.IOC +{ + public interface IDependency + { + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/IOC/IPerRequest.cs b/Infrastructure/Hncore.Infrastructure/IOC/IPerRequest.cs index a783bef..27e0607 100644 --- a/Infrastructure/Hncore.Infrastructure/IOC/IPerRequest.cs +++ b/Infrastructure/Hncore.Infrastructure/IOC/IPerRequest.cs @@ -1,7 +1,7 @@ -namespace Hncore.Infrastructure.IOC -{ - public interface IPerRequest - { - - } +namespace Hncore.Infrastructure.IOC +{ + public interface IPerRequest + { + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/IOC/ISingleInstance.cs b/Infrastructure/Hncore.Infrastructure/IOC/ISingleInstance.cs index 60c18ce..3f1bc51 100644 --- a/Infrastructure/Hncore.Infrastructure/IOC/ISingleInstance.cs +++ b/Infrastructure/Hncore.Infrastructure/IOC/ISingleInstance.cs @@ -1,7 +1,7 @@ -namespace Hncore.Infrastructure.IOC -{ - public interface ISingleInstance - { - - } +namespace Hncore.Infrastructure.IOC +{ + public interface ISingleInstance + { + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Mqtt/MQTTClient.cs b/Infrastructure/Hncore.Infrastructure/Mqtt/MQTTClient.cs index 05ec815..64ddf02 100644 --- a/Infrastructure/Hncore.Infrastructure/Mqtt/MQTTClient.cs +++ b/Infrastructure/Hncore.Infrastructure/Mqtt/MQTTClient.cs @@ -1,151 +1,151 @@ -using System; -using System.Collections.Concurrent; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Hncore.Infrastructure.Common; -using MQTTnet; -using MQTTnet.Client; -using Polly; - -namespace Hncore.Infrastructure.Mqtt -{ - public class MQTTClient : IDisposable - { - //实例 ID,购买后从控制台获取 - string _instanceId = "post-cn-v0h12xbue09"; - - //此处填写购买得到的 MQTT 接入点域名 - string _brokerUrl = "post-cn-v0h12xbue09.mqtt.aliyuncs.com"; - - //此处填写阿里云帐号 AccessKey - string _accessKey = "LTAIaJpHI68JfX2c"; - - //此处填写阿里云帐号 SecretKey - string _secretKey = "f17za6FRggVzwlSqzFHl8GndQ59SGV"; - - //此处填写客户端 ClientId,需要保证全局唯一,其中前缀部分即 GroupId 需要先在 MQ 控制台创建 - string _clientId = "GID_DOOR@@@MA_" + Guid.NewGuid(); - - private IMqttClient _mqttClient; - - private SemaphoreSlim _lock = new SemaphoreSlim(1, 1); - - private ConcurrentDictionary> cmdMap = new ConcurrentDictionary>(); - - public MQTTClient() - { - _mqttClient = new MqttFactory().CreateMqttClient(); - - _mqttClient.Disconnected += async (s, e) => - { - await Task.Delay(TimeSpan.FromSeconds(2)); - await Conn(); - }; - _mqttClient.ApplicationMessageReceived += (s, e) => - { - var topic = e.ApplicationMessage.Topic.TrimEnd('/'); - var data = Encoding.UTF8.GetString(e.ApplicationMessage.Payload??new byte[0]); - Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); - Console.WriteLine($"+ Topic = {topic}"); - Console.WriteLine($"+ Payload = {data}"); - Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); - Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); - Console.WriteLine(); - if (cmdMap.ContainsKey(topic)) - { - try - { - cmdMap[topic](data); - } - catch (Exception ex) - { - LogHelper.Error($"Mqtt:{topic}", ex.Message); - } - } - }; - } - - private async Task Conn() - { - if (!_mqttClient.IsConnected) - { - await _lock.WaitAsync(); - - try - { - int i = 0; - - await Policy.Handle() - .OrResult(res => !res.IsSessionPresent) - .RetryAsync(10) - .ExecuteAsync(async () => - { - if (!_mqttClient.IsConnected) - { - i++; - - try - { - string userName = "Signature|" + _accessKey + "|" + _instanceId; - string passWord = HMACSHA1(_secretKey, _clientId); - - var options = new MqttClientOptionsBuilder() - .WithClientId(_clientId) - .WithTcpServer(_brokerUrl) - .WithCredentials(userName, passWord) - .WithCleanSession() - .Build(); - - return await _mqttClient.ConnectAsync(options); - } - catch (Exception e) - { - LogHelper.Error($"mqtt连接失败,第{i}次连接", e); - throw; - } - } - - return new MqttClientConnectResult(true); - }); - } - finally - { - _lock.Release(); - } - } - } - - public async Task PublishAsync(string topic, string payload) - { - await Conn(); - - await _mqttClient.PublishAsync(topic, payload); - } - - public async Task SubscribeAsync(string topic, Action action) - { - await Conn(); - var option = new TopicFilterBuilder().WithTopic(topic).Build(); - await _mqttClient.SubscribeAsync(option); - cmdMap[topic] = action; - } - - public static string HMACSHA1(string key, string dataToSign) - { - Byte[] secretBytes = UTF8Encoding.UTF8.GetBytes(key); - HMACSHA1 hmac = new HMACSHA1(secretBytes); - Byte[] dataBytes = UTF8Encoding.UTF8.GetBytes(dataToSign); - Byte[] calcHash = hmac.ComputeHash(dataBytes); - String calcHashString = Convert.ToBase64String(calcHash); - return calcHashString; - } - - public async void Dispose() - { - await _mqttClient.DisconnectAsync(); - _mqttClient?.Dispose(); - } - } +using System; +using System.Collections.Concurrent; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Hncore.Infrastructure.Common; +using MQTTnet; +using MQTTnet.Client; +using Polly; + +namespace Hncore.Infrastructure.Mqtt +{ + public class MQTTClient : IDisposable + { + //实例 ID,购买后从控制台获取 + string _instanceId = "post-cn-v0h12xbue09"; + + //此处填写购买得到的 MQTT 接入点域名 + string _brokerUrl = "post-cn-v0h12xbue09.mqtt.aliyuncs.com"; + + //此处填写阿里云帐号 AccessKey + string _accessKey = "LTAIaJpHI68JfX2c"; + + //此处填写阿里云帐号 SecretKey + string _secretKey = "f17za6FRggVzwlSqzFHl8GndQ59SGV"; + + //此处填写客户端 ClientId,需要保证全局唯一,其中前缀部分即 GroupId 需要先在 MQ 控制台创建 + string _clientId = "GID_DOOR@@@MA_" + Guid.NewGuid(); + + private IMqttClient _mqttClient; + + private SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + + private ConcurrentDictionary> cmdMap = new ConcurrentDictionary>(); + + public MQTTClient() + { + _mqttClient = new MqttFactory().CreateMqttClient(); + + _mqttClient.Disconnected += async (s, e) => + { + await Task.Delay(TimeSpan.FromSeconds(2)); + await Conn(); + }; + _mqttClient.ApplicationMessageReceived += (s, e) => + { + var topic = e.ApplicationMessage.Topic.TrimEnd('/'); + var data = Encoding.UTF8.GetString(e.ApplicationMessage.Payload??new byte[0]); + Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); + Console.WriteLine($"+ Topic = {topic}"); + Console.WriteLine($"+ Payload = {data}"); + Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); + Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); + Console.WriteLine(); + if (cmdMap.ContainsKey(topic)) + { + try + { + cmdMap[topic](data); + } + catch (Exception ex) + { + LogHelper.Error($"Mqtt:{topic}", ex.Message); + } + } + }; + } + + private async Task Conn() + { + if (!_mqttClient.IsConnected) + { + await _lock.WaitAsync(); + + try + { + int i = 0; + + await Policy.Handle() + .OrResult(res => !res.IsSessionPresent) + .RetryAsync(10) + .ExecuteAsync(async () => + { + if (!_mqttClient.IsConnected) + { + i++; + + try + { + string userName = "Signature|" + _accessKey + "|" + _instanceId; + string passWord = HMACSHA1(_secretKey, _clientId); + + var options = new MqttClientOptionsBuilder() + .WithClientId(_clientId) + .WithTcpServer(_brokerUrl) + .WithCredentials(userName, passWord) + .WithCleanSession() + .Build(); + + return await _mqttClient.ConnectAsync(options); + } + catch (Exception e) + { + LogHelper.Error($"mqtt连接失败,第{i}次连接", e); + throw; + } + } + + return new MqttClientConnectResult(true); + }); + } + finally + { + _lock.Release(); + } + } + } + + public async Task PublishAsync(string topic, string payload) + { + await Conn(); + + await _mqttClient.PublishAsync(topic, payload); + } + + public async Task SubscribeAsync(string topic, Action action) + { + await Conn(); + var option = new TopicFilterBuilder().WithTopic(topic).Build(); + await _mqttClient.SubscribeAsync(option); + cmdMap[topic] = action; + } + + public static string HMACSHA1(string key, string dataToSign) + { + Byte[] secretBytes = UTF8Encoding.UTF8.GetBytes(key); + HMACSHA1 hmac = new HMACSHA1(secretBytes); + Byte[] dataBytes = UTF8Encoding.UTF8.GetBytes(dataToSign); + Byte[] calcHash = hmac.ComputeHash(dataBytes); + String calcHashString = Convert.ToBase64String(calcHash); + return calcHashString; + } + + public async void Dispose() + { + await _mqttClient.DisconnectAsync(); + _mqttClient?.Dispose(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/OpenApi/Application.cs b/Infrastructure/Hncore.Infrastructure/OpenApi/Application.cs index 9a5f7da..0022eff 100644 --- a/Infrastructure/Hncore.Infrastructure/OpenApi/Application.cs +++ b/Infrastructure/Hncore.Infrastructure/OpenApi/Application.cs @@ -1,26 +1,26 @@ -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.OpenApi -{ - /// - /// 接入的应用 - /// - public class Application - { - /// - /// 应用唯一标识 - /// - public string AppId { get; set; } = ""; - - /// - /// 应用密钥 - /// - public string AppKey { get; set; } = ""; - - /// - /// 是否启用 - /// - public bool Enable { get; set; } = true; - - } +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.OpenApi +{ + /// + /// 接入的应用 + /// + public class Application + { + /// + /// 应用唯一标识 + /// + public string AppId { get; set; } = ""; + + /// + /// 应用密钥 + /// + public string AppKey { get; set; } = ""; + + /// + /// 是否启用 + /// + public bool Enable { get; set; } = true; + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiAuthAttribute.cs b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiAuthAttribute.cs index 6d66448..94bd610 100644 --- a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiAuthAttribute.cs +++ b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiAuthAttribute.cs @@ -1,71 +1,71 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.AspNetCore.Mvc.Filters; -using Hncore.Infrastructure.Core.Web; - -namespace Hncore.Infrastructure.OpenApi -{ - public class OpenApiAuthAttribute : TypeFilterAttribute - { - public OpenApiAuthAttribute() : base(typeof(OpenApiAuthFilter)) - { - Order = -9997; - } - } - - public class OpenApiAuthFilter : IAsyncAuthorizationFilter - { - public async Task OnAuthorizationAsync(AuthorizationFilterContext context) - { - if (context.Filters.Any(item => item is IAllowAnonymousFilter)) - { - context.HttpContext.Items["AllowAnonymous"] = true; - return; - } - - context.HttpContext.Items["OpenApi"] = true; - - var body = await context.HttpContext.Request.ReadBodyAsStringAsync(); - - var requestBase = body.FromJsonTo(); - - if (requestBase.Timestamp==null) - { - OpenApiException.Throw(OpenApiReturnCode.Error,"缺少timestamp参数"); - } - - if (!requestBase.Sign.Has()) - { - OpenApiException.Throw(OpenApiReturnCode.Error,"缺少sign参数"); - } - - if (!requestBase.AppId.Has()) - { - OpenApiException.Throw(OpenApiReturnCode.Error,"缺少appid参数"); - } - - var application = await RedisHelper.HGetAsync("OpenApi:Application", requestBase.AppId); - - context.HttpContext.Items["OpenApiAppKey"] = application.AppKey; - - if (!application.Enable) - { - OpenApiException.Throw(OpenApiReturnCode.Unauthorized); - } - - if (DateTimeHelper.ToUnixTimestamp(DateTime.Now) - requestBase.Timestamp > 60) - { - OpenApiException.Throw(OpenApiReturnCode.TimeStampExpired); - } - - requestBase.CheckSign(application.AppKey); - } - - } +using System; +using System.Linq; +using System.Threading.Tasks; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; +using Hncore.Infrastructure.Core.Web; + +namespace Hncore.Infrastructure.OpenApi +{ + public class OpenApiAuthAttribute : TypeFilterAttribute + { + public OpenApiAuthAttribute() : base(typeof(OpenApiAuthFilter)) + { + Order = -9997; + } + } + + public class OpenApiAuthFilter : IAsyncAuthorizationFilter + { + public async Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + if (context.Filters.Any(item => item is IAllowAnonymousFilter)) + { + context.HttpContext.Items["AllowAnonymous"] = true; + return; + } + + context.HttpContext.Items["OpenApi"] = true; + + var body = await context.HttpContext.Request.ReadBodyAsStringAsync(); + + var requestBase = body.FromJsonTo(); + + if (requestBase.Timestamp==null) + { + OpenApiException.Throw(OpenApiReturnCode.Error,"缺少timestamp参数"); + } + + if (!requestBase.Sign.Has()) + { + OpenApiException.Throw(OpenApiReturnCode.Error,"缺少sign参数"); + } + + if (!requestBase.AppId.Has()) + { + OpenApiException.Throw(OpenApiReturnCode.Error,"缺少appid参数"); + } + + var application = await RedisHelper.HGetAsync("OpenApi:Application", requestBase.AppId); + + context.HttpContext.Items["OpenApiAppKey"] = application.AppKey; + + if (!application.Enable) + { + OpenApiException.Throw(OpenApiReturnCode.Unauthorized); + } + + if (DateTimeHelper.ToUnixTimestamp(DateTime.Now) - requestBase.Timestamp > 60) + { + OpenApiException.Throw(OpenApiReturnCode.TimeStampExpired); + } + + requestBase.CheckSign(application.AppKey); + } + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiException.cs b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiException.cs index 452fd50..b7564a9 100644 --- a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiException.cs +++ b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiException.cs @@ -1,28 +1,28 @@ -using System; - -namespace Hncore.Infrastructure.OpenApi -{ - public class OpenApiException: Exception - { - public OpenApiReturnCode Code { get; } = OpenApiReturnCode.InternalError; - - public OpenApiException(string message) : base(message) - { - } - - public OpenApiException(OpenApiReturnCode code, string message = "") : base(message) - { - Code = code; - } - - public static void Throw(string message = "") - { - throw new OpenApiException(message); - } - - public static void Throw(OpenApiReturnCode code, string message = "") - { - throw new OpenApiException(code, message); - } - } +using System; + +namespace Hncore.Infrastructure.OpenApi +{ + public class OpenApiException: Exception + { + public OpenApiReturnCode Code { get; } = OpenApiReturnCode.InternalError; + + public OpenApiException(string message) : base(message) + { + } + + public OpenApiException(OpenApiReturnCode code, string message = "") : base(message) + { + Code = code; + } + + public static void Throw(string message = "") + { + throw new OpenApiException(message); + } + + public static void Throw(OpenApiReturnCode code, string message = "") + { + throw new OpenApiException(code, message); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiRequestBase.cs b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiRequestBase.cs index 6655a3f..42a0357 100644 --- a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiRequestBase.cs +++ b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiRequestBase.cs @@ -1,30 +1,30 @@ -using System; -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Hncore.Infrastructure.OpenApi -{ - public class OpenApiRequestBase - { - [JsonProperty("appid")] - public string AppId { get; set; } - - [JsonProperty("timestamp")] - public long? Timestamp { get; set; } - - [JsonProperty("sign")] - public string Sign { get; set; } - - public void CheckSign(string key) - { - var sign = OpenApiSignUtil.CreateSign(this.Timestamp.ToLong(), key); - - if (!String.Equals(sign, Sign, StringComparison.CurrentCultureIgnoreCase)) - { - OpenApiException.Throw(OpenApiReturnCode.SignError); - } - } - } +using System; +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Hncore.Infrastructure.OpenApi +{ + public class OpenApiRequestBase + { + [JsonProperty("appid")] + public string AppId { get; set; } + + [JsonProperty("timestamp")] + public long? Timestamp { get; set; } + + [JsonProperty("sign")] + public string Sign { get; set; } + + public void CheckSign(string key) + { + var sign = OpenApiSignUtil.CreateSign(this.Timestamp.ToLong(), key); + + if (!String.Equals(sign, Sign, StringComparison.CurrentCultureIgnoreCase)) + { + OpenApiException.Throw(OpenApiReturnCode.SignError); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiResult.cs b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiResult.cs index 7538c45..90a6e3a 100644 --- a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiResult.cs +++ b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiResult.cs @@ -1,111 +1,111 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; - -namespace Hncore.Infrastructure.OpenApi -{ - public class OpenApiResult where T : class, new() - { - [JsonProperty("code")] public OpenApiReturnCode Code { get; private set; } - - [JsonProperty("message")] public string Message { get; private set; } = ""; - - [JsonProperty("timestamp")] public long Timestamp { get; set; } - - [JsonProperty("sign")] public string Sign { get; set; } - - [JsonProperty("data")] public T Data { get; set; } = new T(); - - private static readonly Dictionary Dic; - - static OpenApiResult() - { - Dic = ObjectExtension.ToDescriptionDictionary(); - } - - - public OpenApiResult(OpenApiReturnCode code = OpenApiReturnCode.Success, string message = "") - { - Code = code; - - if (string.IsNullOrEmpty(message) && Dic.ContainsKey(Code)) - { - Message = Dic[Code]; - } - else - { - Message = message; - } - - this.Timestamp = DateTimeHelper.ToUnixTimestamp(DateTime.Now); - } - - public void CreateSign(string key) - { - this.Sign = OpenApiSignUtil.CreateSign(this.Timestamp, key); - } - - public OpenApiResult CreateSign(HttpContext httpContext) - { - var key = httpContext.Items["OpenApiAppKey"].ToString(); - - this.Sign = OpenApiSignUtil.CreateSign(this.Timestamp, key); - - return this; - } - } - - public class OpenApiResult : OpenApiResult - { - public OpenApiResult(OpenApiReturnCode code = OpenApiReturnCode.Success, string message = "") : base(code, - message) - { - } - - public new OpenApiResult CreateSign(HttpContext httpContext) - { - var key = httpContext.Items["OpenApiAppKey"].ToString(); - - this.Sign = OpenApiSignUtil.CreateSign(this.Timestamp, key); - - return this; - } - } - - public enum OpenApiReturnCode - { - /// - /// 成功 - /// - [Description("成功")] Success = 10000, - - /// - /// 验签失败 - /// - [Description("未授权")] Unauthorized = 40001, - - /// - /// 验签失败 - /// - [Description("验签失败")] SignError = 40002, - - /// - /// 时间戳过期 - /// - [Description("时间戳过期")] TimeStampExpired = 40003, - - /// - /// 内部错误 - /// - [Description("内部错误")] InternalError = 50000, - - /// - /// 处理失败 - /// - [Description("处理失败")] Error = 500001 - } +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; + +namespace Hncore.Infrastructure.OpenApi +{ + public class OpenApiResult where T : class, new() + { + [JsonProperty("code")] public OpenApiReturnCode Code { get; private set; } + + [JsonProperty("message")] public string Message { get; private set; } = ""; + + [JsonProperty("timestamp")] public long Timestamp { get; set; } + + [JsonProperty("sign")] public string Sign { get; set; } + + [JsonProperty("data")] public T Data { get; set; } = new T(); + + private static readonly Dictionary Dic; + + static OpenApiResult() + { + Dic = ObjectExtension.ToDescriptionDictionary(); + } + + + public OpenApiResult(OpenApiReturnCode code = OpenApiReturnCode.Success, string message = "") + { + Code = code; + + if (string.IsNullOrEmpty(message) && Dic.ContainsKey(Code)) + { + Message = Dic[Code]; + } + else + { + Message = message; + } + + this.Timestamp = DateTimeHelper.ToUnixTimestamp(DateTime.Now); + } + + public void CreateSign(string key) + { + this.Sign = OpenApiSignUtil.CreateSign(this.Timestamp, key); + } + + public OpenApiResult CreateSign(HttpContext httpContext) + { + var key = httpContext.Items["OpenApiAppKey"].ToString(); + + this.Sign = OpenApiSignUtil.CreateSign(this.Timestamp, key); + + return this; + } + } + + public class OpenApiResult : OpenApiResult + { + public OpenApiResult(OpenApiReturnCode code = OpenApiReturnCode.Success, string message = "") : base(code, + message) + { + } + + public new OpenApiResult CreateSign(HttpContext httpContext) + { + var key = httpContext.Items["OpenApiAppKey"].ToString(); + + this.Sign = OpenApiSignUtil.CreateSign(this.Timestamp, key); + + return this; + } + } + + public enum OpenApiReturnCode + { + /// + /// 成功 + /// + [Description("成功")] Success = 10000, + + /// + /// 验签失败 + /// + [Description("未授权")] Unauthorized = 40001, + + /// + /// 验签失败 + /// + [Description("验签失败")] SignError = 40002, + + /// + /// 时间戳过期 + /// + [Description("时间戳过期")] TimeStampExpired = 40003, + + /// + /// 内部错误 + /// + [Description("内部错误")] InternalError = 50000, + + /// + /// 处理失败 + /// + [Description("处理失败")] Error = 500001 + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiSignUtil.cs b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiSignUtil.cs index 4b23567..72f7ecc 100644 --- a/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiSignUtil.cs +++ b/Infrastructure/Hncore.Infrastructure/OpenApi/OpenApiSignUtil.cs @@ -1,15 +1,15 @@ -using System.Collections.Generic; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Data; -using Newtonsoft.Json.Linq; - -namespace Hncore.Infrastructure.OpenApi -{ - public static class OpenApiSignUtil - { - public static string CreateSign(long timestamp, string key) - { - return SecurityHelper.GetMd5Hash(timestamp.ToString() + key); - } - } +using System.Collections.Generic; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Data; +using Newtonsoft.Json.Linq; + +namespace Hncore.Infrastructure.OpenApi +{ + public static class OpenApiSignUtil + { + public static string CreateSign(long timestamp, string key) + { + return SecurityHelper.GetMd5Hash(timestamp.ToString() + key); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/OperationLog/OperationLog.cs b/Infrastructure/Hncore.Infrastructure/OperationLog/OperationLog.cs index 2a31f57..fbf76b8 100644 --- a/Infrastructure/Hncore.Infrastructure/OperationLog/OperationLog.cs +++ b/Infrastructure/Hncore.Infrastructure/OperationLog/OperationLog.cs @@ -1,156 +1,156 @@ -using System; -using System.Threading.Tasks; -using Dapper; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Serializer; -using MySql.Data.MySqlClient; -using Newtonsoft.Json; - -namespace Hncore.Infrastructure.OperationLog -{ - public class OperationLog - { - /// - /// 物业id - /// - [JsonProperty("owner_id")] - public int OwnerId { get; set; } - - /// - /// 创建时间 - /// - [JsonProperty("createtime")] - public DateTime CreateTime { get; set; } = DateTime.Now; - - /// - /// 更新时间 - /// - [JsonProperty("updatetime")] - public DateTime UpdateTime { get; set; } = DateTime.Now; - - /// - /// 删除标记 - /// - [JsonProperty("deletetag")] - public int DeleteTag { get; set; } = 0; - - /// - /// 创建人id - /// - [JsonProperty("creatorid")] - public int CreatorId { get; set; } - - /// - /// 更新人id - /// - [JsonProperty("updatorid")] - public int UpdatorId { get; set; } - - /// - /// 操作目标id - /// - [JsonProperty("targetid")] - public int TargetId { get; set; } - - /// - /// 操作人名 - /// - [JsonProperty("operator")] - public string Operator { get; set; } - - /// - /// 操作说明 - /// - [JsonProperty("opdesc")] - public string OpDesc { get; set; } - - /// - /// 操作前 - /// - [JsonProperty("beforeop")] - public string BeforeOp { get; set; } - - /// - /// 操作后 - /// - [JsonProperty("afterop")] - public string AfterOp { get; set; } - - /// - /// 操作对象所在小区编码 - /// - [JsonProperty("projectcode")] - public int ProjectCode { get; set; } - - /// - /// 小区名称 - /// - [JsonProperty("estatename")] - public string EstateName { get; set; } - - /// - /// 操作权限编码 - /// - [JsonProperty("permissioncode")] - public string PermissionCode { get; set; } - - /// - /// 操作权限标签 - /// - [JsonProperty("permissionlabel")] - public string PermissionLabel { get; set; } - - /// - /// 操作菜单编码 - /// - [JsonProperty("optype")] - public int OpType { get; set; } - - /// - /// 操作菜单名称 - /// - [JsonProperty("optypename")] - public string OpTypeName { get; set; } - - public void Write() - { - try - { - OperationLogStorage.WriteLog(this); - } - catch (Exception e) - { - LogHelper.Error("写操作日志失败", e); - } - } - public void WriteAsync() - { - Task.Run(()=> Write()); - } - } - - internal class OperationLogStorage - { - private static string _devConn = - "Server=rm-bp12e1533udh1827azo.mysql.rds.aliyuncs.com;Database=etor_property_test;User=etor_test;Password=etor_test!QAZ2wsx;Convert Zero Datetime=True;"; - - private static string _proConn = - "Server=rm-bp1z48e1qz15k7q9qo.mysql.rds.aliyuncs.com;Database=etor_property_pro;User=sadmin;Password=!QAZ2wsx;Convert Zero Datetime=True;"; - - private static string _insertSql = @"INSERT INTO etor_property_operationlog -( -owner_id, createtime, updatetime, deletetag, creatorid, updatorid, targetid, operator, opdesc, beforeop, afterop, projectcode, estatename, permissioncode, permissionlabel, optype, optypename -) -VALUES (@OwnerId,@CreateTime,@UpdateTime,@DeleteTag,@CreatorId,@UpdatorId,@TargetId,@Operator,@OpDesc,@BeforeOp,@AfterOp,@ProjectCode,@EstateName,@PermissionCode,@PermissionLabel,@OpType,@OpTypeName);"; - - public static void WriteLog(OperationLog log) - { - string connString = EnvironmentVariableHelper.IsAspNetCoreProduction ? _proConn : _devConn; - - using (var conn = new MySqlConnection(connString)) - { - conn.Execute(_insertSql, log); - } - } - } +using System; +using System.Threading.Tasks; +using Dapper; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Serializer; +using MySql.Data.MySqlClient; +using Newtonsoft.Json; + +namespace Hncore.Infrastructure.OperationLog +{ + public class OperationLog + { + /// + /// 物业id + /// + [JsonProperty("owner_id")] + public int OwnerId { get; set; } + + /// + /// 创建时间 + /// + [JsonProperty("createtime")] + public DateTime CreateTime { get; set; } = DateTime.Now; + + /// + /// 更新时间 + /// + [JsonProperty("updatetime")] + public DateTime UpdateTime { get; set; } = DateTime.Now; + + /// + /// 删除标记 + /// + [JsonProperty("deletetag")] + public int DeleteTag { get; set; } = 0; + + /// + /// 创建人id + /// + [JsonProperty("creatorid")] + public int CreatorId { get; set; } + + /// + /// 更新人id + /// + [JsonProperty("updatorid")] + public int UpdatorId { get; set; } + + /// + /// 操作目标id + /// + [JsonProperty("targetid")] + public int TargetId { get; set; } + + /// + /// 操作人名 + /// + [JsonProperty("operator")] + public string Operator { get; set; } + + /// + /// 操作说明 + /// + [JsonProperty("opdesc")] + public string OpDesc { get; set; } + + /// + /// 操作前 + /// + [JsonProperty("beforeop")] + public string BeforeOp { get; set; } + + /// + /// 操作后 + /// + [JsonProperty("afterop")] + public string AfterOp { get; set; } + + /// + /// 操作对象所在小区编码 + /// + [JsonProperty("projectcode")] + public int ProjectCode { get; set; } + + /// + /// 小区名称 + /// + [JsonProperty("estatename")] + public string EstateName { get; set; } + + /// + /// 操作权限编码 + /// + [JsonProperty("permissioncode")] + public string PermissionCode { get; set; } + + /// + /// 操作权限标签 + /// + [JsonProperty("permissionlabel")] + public string PermissionLabel { get; set; } + + /// + /// 操作菜单编码 + /// + [JsonProperty("optype")] + public int OpType { get; set; } + + /// + /// 操作菜单名称 + /// + [JsonProperty("optypename")] + public string OpTypeName { get; set; } + + public void Write() + { + try + { + OperationLogStorage.WriteLog(this); + } + catch (Exception e) + { + LogHelper.Error("写操作日志失败", e); + } + } + public void WriteAsync() + { + Task.Run(()=> Write()); + } + } + + internal class OperationLogStorage + { + private static string _devConn = + "Server=rm-bp12e1533udh1827azo.mysql.rds.aliyuncs.com;Database=etor_property_test;User=etor_test;Password=etor_test!QAZ2wsx;Convert Zero Datetime=True;"; + + private static string _proConn = + "Server=rm-bp1z48e1qz15k7q9qo.mysql.rds.aliyuncs.com;Database=etor_property_pro;User=sadmin;Password=!QAZ2wsx;Convert Zero Datetime=True;"; + + private static string _insertSql = @"INSERT INTO etor_property_operationlog +( +owner_id, createtime, updatetime, deletetag, creatorid, updatorid, targetid, operator, opdesc, beforeop, afterop, projectcode, estatename, permissioncode, permissionlabel, optype, optypename +) +VALUES (@OwnerId,@CreateTime,@UpdateTime,@DeleteTag,@CreatorId,@UpdatorId,@TargetId,@Operator,@OpDesc,@BeforeOp,@AfterOp,@ProjectCode,@EstateName,@PermissionCode,@PermissionLabel,@OpType,@OpTypeName);"; + + public static void WriteLog(OperationLog log) + { + string connString = EnvironmentVariableHelper.IsAspNetCoreProduction ? _proConn : _devConn; + + using (var conn = new MySqlConnection(connString)) + { + conn.Execute(_insertSql, log); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/SMS/AliSmsService.cs b/Infrastructure/Hncore.Infrastructure/SMS/AliSmsService.cs index 74f6452..070b857 100644 --- a/Infrastructure/Hncore.Infrastructure/SMS/AliSmsService.cs +++ b/Infrastructure/Hncore.Infrastructure/SMS/AliSmsService.cs @@ -1,46 +1,46 @@ -using Aliyun.Acs.Core; -using Aliyun.Acs.Core.Exceptions; -using Aliyun.Acs.Core.Http; -using Aliyun.Acs.Core.Profile; -using Hncore.Infrastructure.Serializer; -using System; -using System.Collections.Generic; -namespace Hncore.Infrastructure.SMS -{ - /// - /// 发送短信 - /// - public class AliSmsService - { - public static bool Send(string TemplateCode,object TemplateParam, string SignName="", params string[] PhoneNumbers) - { - IClientProfile profile = DefaultProfile.GetProfile("cn-hangzhou", "LTAI4FmSkDSwFuXeLxsDB3jB", "r8FfRmoeWcCJyZSqqkQP2G3dKPPl2N"); - DefaultAcsClient client = new DefaultAcsClient(profile); - CommonRequest request = new CommonRequest(); - request.Method = MethodType.POST; - request.Domain = "dysmsapi.aliyuncs.com"; - request.Version = "2017-05-25"; - request.Action = "SendSms"; - // request.Protocol = ProtocolType.HTTP; - request.AddQueryParameters("PhoneNumbers", string.Join(",", PhoneNumbers)); - request.AddQueryParameters("SignName", SignName); - request.AddQueryParameters("TemplateCode", TemplateCode); - request.AddQueryParameters("TemplateParam", TemplateParam.ToJson()); - try - { - CommonResponse response = client.GetCommonResponse(request); - Console.WriteLine(System.Text.Encoding.Default.GetString(response.HttpResponse.Content)); - return true; - } - catch (ServerException e) - { - Console.WriteLine(e); - } - catch (ClientException e) - { - Console.WriteLine(e); - } - return false; - } - } -} +using Aliyun.Acs.Core; +using Aliyun.Acs.Core.Exceptions; +using Aliyun.Acs.Core.Http; +using Aliyun.Acs.Core.Profile; +using Hncore.Infrastructure.Serializer; +using System; +using System.Collections.Generic; +namespace Hncore.Infrastructure.SMS +{ + /// + /// 发送短信 + /// + public class AliSmsService + { + public static bool Send(string TemplateCode,object TemplateParam, string SignName="", params string[] PhoneNumbers) + { + IClientProfile profile = DefaultProfile.GetProfile("cn-hangzhou", "LTAI4FmSkDSwFuXeLxsDB3jB", "r8FfRmoeWcCJyZSqqkQP2G3dKPPl2N"); + DefaultAcsClient client = new DefaultAcsClient(profile); + CommonRequest request = new CommonRequest(); + request.Method = MethodType.POST; + request.Domain = "dysmsapi.aliyuncs.com"; + request.Version = "2017-05-25"; + request.Action = "SendSms"; + // request.Protocol = ProtocolType.HTTP; + request.AddQueryParameters("PhoneNumbers", string.Join(",", PhoneNumbers)); + request.AddQueryParameters("SignName", SignName); + request.AddQueryParameters("TemplateCode", TemplateCode); + request.AddQueryParameters("TemplateParam", TemplateParam.ToJson()); + try + { + CommonResponse response = client.GetCommonResponse(request); + Console.WriteLine(System.Text.Encoding.Default.GetString(response.HttpResponse.Content)); + return true; + } + catch (ServerException e) + { + Console.WriteLine(e); + } + catch (ClientException e) + { + Console.WriteLine(e); + } + return false; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/SMS/SendSMSService.cs b/Infrastructure/Hncore.Infrastructure/SMS/SendSMSService.cs index e864966..e272b0d 100644 --- a/Infrastructure/Hncore.Infrastructure/SMS/SendSMSService.cs +++ b/Infrastructure/Hncore.Infrastructure/SMS/SendSMSService.cs @@ -1,135 +1,135 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Serializer; -using Newtonsoft.Json; - -namespace Hncore.Infrastructure.SMS -{ - /// - /// 发送短信 - /// - public class SendSMSService - { - private static string smsApi = "http://dysmsapi.aliyuncs.com"; - private static string smsAuthorization = "Basic cGJsOjEyMzQ1NmFh"; - private static string smsAppId = smsAppId = "weiyuwuye"; - - - - /// - /// 发送短信 - /// - /// 发送内容 - /// 多个手机号逗号分隔 - /// - public async static Task SendSMS(string Content, string mobile) - { - if (string.IsNullOrEmpty(Content) || string.IsNullOrEmpty(mobile)) - { - throw new ArgumentException("SendSMS参数错误"); - } - mobile = mobile.Replace(';', ','); - var postData = new SMSData() { - Mobile=mobile, - SmsType=4, - Content=Content - }; - Dictionary headers = new Dictionary(); - headers.Add("Authorization", smsAuthorization); - headers.Add("AppId", smsAppId); - var res= await HttpPostAsync(smsApi, JsonConvert.SerializeObject(postData), "application/json", 30, headers); - // return response.Data; {"Code":"100000","Message":"发送成功","Data":null} - return JsonConvert.DeserializeObject(res) ; - - } - - /// - /// post请求 - /// - /// - /// - /// - /// - /// - /// - public static string HttpPost(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) - { - postData = postData ?? ""; - using (HttpClient client = new HttpClient()) - { - if (headers != null) - { - foreach (var header in headers) - client.DefaultRequestHeaders.Add(header.Key, header.Value); - } - using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8)) - { - if (contentType != null) - httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType); - - HttpResponseMessage response = client.PostAsync(url, httpContent).Result; - return response.Content.ReadAsStringAsync().Result; - } - } - } - - - public static async Task HttpPostAsync(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) - { - postData = postData ?? ""; - using (HttpClient client = new HttpClient()) - { - client.Timeout = new TimeSpan(0, 0, timeOut); - if (headers != null) - { - foreach (var header in headers) - client.DefaultRequestHeaders.Add(header.Key, header.Value); - } - using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8)) - { - if (contentType != null) - httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType); - - HttpResponseMessage response = await client.PostAsync(url, httpContent); - return await response.Content.ReadAsStringAsync(); - } - } - } - - public class APIResponse - { - /// - /// 业务状态码 - /// - public string Code { get; set; } - - /// - /// 业务消息,如:操作失败消息 - /// - public string Message { get; set; } - - /// - /// 业务实体数据 - /// - public T Data { get; set; } - } - - public class SMSData - { - public string Mobile { get; set; } - public string Content { get; set; } - public int SmsType { get; set; } - } - public class SMSDataResponse - { - public int Code { get; set; } - public string Message { get; set; } - public object Data { get; set; } - } - } -} +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Serializer; +using Newtonsoft.Json; + +namespace Hncore.Infrastructure.SMS +{ + /// + /// 发送短信 + /// + public class SendSMSService + { + private static string smsApi = "http://dysmsapi.aliyuncs.com"; + private static string smsAuthorization = "Basic cGJsOjEyMzQ1NmFh"; + private static string smsAppId = smsAppId = "weiyuwuye"; + + + + /// + /// 发送短信 + /// + /// 发送内容 + /// 多个手机号逗号分隔 + /// + public async static Task SendSMS(string Content, string mobile) + { + if (string.IsNullOrEmpty(Content) || string.IsNullOrEmpty(mobile)) + { + throw new ArgumentException("SendSMS参数错误"); + } + mobile = mobile.Replace(';', ','); + var postData = new SMSData() { + Mobile=mobile, + SmsType=4, + Content=Content + }; + Dictionary headers = new Dictionary(); + headers.Add("Authorization", smsAuthorization); + headers.Add("AppId", smsAppId); + var res= await HttpPostAsync(smsApi, JsonConvert.SerializeObject(postData), "application/json", 30, headers); + // return response.Data; {"Code":"100000","Message":"发送成功","Data":null} + return JsonConvert.DeserializeObject(res) ; + + } + + /// + /// post请求 + /// + /// + /// + /// + /// + /// + /// + public static string HttpPost(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) + { + postData = postData ?? ""; + using (HttpClient client = new HttpClient()) + { + if (headers != null) + { + foreach (var header in headers) + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8)) + { + if (contentType != null) + httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType); + + HttpResponseMessage response = client.PostAsync(url, httpContent).Result; + return response.Content.ReadAsStringAsync().Result; + } + } + } + + + public static async Task HttpPostAsync(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) + { + postData = postData ?? ""; + using (HttpClient client = new HttpClient()) + { + client.Timeout = new TimeSpan(0, 0, timeOut); + if (headers != null) + { + foreach (var header in headers) + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8)) + { + if (contentType != null) + httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType); + + HttpResponseMessage response = await client.PostAsync(url, httpContent); + return await response.Content.ReadAsStringAsync(); + } + } + } + + public class APIResponse + { + /// + /// 业务状态码 + /// + public string Code { get; set; } + + /// + /// 业务消息,如:操作失败消息 + /// + public string Message { get; set; } + + /// + /// 业务实体数据 + /// + public T Data { get; set; } + } + + public class SMSData + { + public string Mobile { get; set; } + public string Content { get; set; } + public int SmsType { get; set; } + } + public class SMSDataResponse + { + public int Code { get; set; } + public string Message { get; set; } + public object Data { get; set; } + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Serializer/JsonNetSetting.cs b/Infrastructure/Hncore.Infrastructure/Serializer/JsonNetSetting.cs index fc970f5..a817d9f 100644 --- a/Infrastructure/Hncore.Infrastructure/Serializer/JsonNetSetting.cs +++ b/Infrastructure/Hncore.Infrastructure/Serializer/JsonNetSetting.cs @@ -1,74 +1,74 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Hncore.Infrastructure.Common; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Hncore.Infrastructure.Serializer -{ - public class NullToEmptyStringResolver : DefaultContractResolver - { - protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) - { - return type.GetProperties() - .Select(p => - { - var jp = base.CreateProperty(p, memberSerialization); - jp.ValueProvider = new NullToEmptyStringValueProvider(p); - return jp; - }).ToList(); - } - } - - public class NullToEmptyStringValueProvider : IValueProvider - { - PropertyInfo _MemberInfo; - - public NullToEmptyStringValueProvider(PropertyInfo memberInfo) - { - _MemberInfo = memberInfo; - } - - public object GetValue(object target) - { - object result = _MemberInfo.GetValue(target); - if (_MemberInfo.PropertyType == typeof(string) && result == null) - { - result = ""; - } - else if ((_MemberInfo.PropertyType == typeof(DateTime) || _MemberInfo.PropertyType == typeof(DateTime?)) && - result != null) - { - if (result.ToString() == "0001/1/1 0:00:00" || result.ToString() == "1000/1/1 0:00:00") - { - result = DateTimeHelper.SqlMinTime; - } - - DateTime time = Convert.ToDateTime(result); - - if (time == DateTimeHelper.SqlMaxTime || time == DateTimeHelper.SqlMinTime || time == DateTime.MaxValue || - time == DateTime.MinValue) - { - result = ""; - } - } - else if (_MemberInfo.PropertyType.Name == "List`1" && result == null) - { - result = new List(); - } - else if (_MemberInfo.PropertyType.Name == "Object" && result == null) - { - result = new { }; - } - - return result; - } - - public void SetValue(object target, object value) - { - _MemberInfo.SetValue(target, value); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Hncore.Infrastructure.Common; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Hncore.Infrastructure.Serializer +{ + public class NullToEmptyStringResolver : DefaultContractResolver + { + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + return type.GetProperties() + .Select(p => + { + var jp = base.CreateProperty(p, memberSerialization); + jp.ValueProvider = new NullToEmptyStringValueProvider(p); + return jp; + }).ToList(); + } + } + + public class NullToEmptyStringValueProvider : IValueProvider + { + PropertyInfo _MemberInfo; + + public NullToEmptyStringValueProvider(PropertyInfo memberInfo) + { + _MemberInfo = memberInfo; + } + + public object GetValue(object target) + { + object result = _MemberInfo.GetValue(target); + if (_MemberInfo.PropertyType == typeof(string) && result == null) + { + result = ""; + } + else if ((_MemberInfo.PropertyType == typeof(DateTime) || _MemberInfo.PropertyType == typeof(DateTime?)) && + result != null) + { + if (result.ToString() == "0001/1/1 0:00:00" || result.ToString() == "1000/1/1 0:00:00") + { + result = DateTimeHelper.SqlMinTime; + } + + DateTime time = Convert.ToDateTime(result); + + if (time == DateTimeHelper.SqlMaxTime || time == DateTimeHelper.SqlMinTime || time == DateTime.MaxValue || + time == DateTime.MinValue) + { + result = ""; + } + } + else if (_MemberInfo.PropertyType.Name == "List`1" && result == null) + { + result = new List(); + } + else if (_MemberInfo.PropertyType.Name == "Object" && result == null) + { + result = new { }; + } + + return result; + } + + public void SetValue(object target, object value) + { + _MemberInfo.SetValue(target, value); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Serializer/ObjectExtension.cs b/Infrastructure/Hncore.Infrastructure/Serializer/ObjectExtension.cs index 875bd58..8208a73 100644 --- a/Infrastructure/Hncore.Infrastructure/Serializer/ObjectExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Serializer/ObjectExtension.cs @@ -1,185 +1,185 @@ -using System; -using System.ComponentModel; -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; -using Hncore.Infrastructure.Common; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Hncore.Infrastructure.Serializer -{ - public static class ObjectExtension - { - #region 序列化为二进制 - - /// - /// 序列化位二进制 - /// - /// 要序列化的对象 - /// 字节数组 - public static byte[] SerializeBinary(this object request) - { - using (MemoryStream memStream = new MemoryStream()) - { - BinaryFormatter serializer = new BinaryFormatter(); - serializer.Serialize(memStream, request); - return memStream.GetBuffer(); - } - } - - #endregion - - #region 二进制反序列化 - - /// - /// 二进制反序列化 - /// - /// 字节数组 - /// 得到的对象 - public static T DeserializeBinary(this byte[] buf) where T : class, new() - { - if (buf == null) - { - return default(T); - } - - using (MemoryStream memStream = new MemoryStream(buf)) - { - memStream.Position = 0; - BinaryFormatter deserializer = new BinaryFormatter(); - T info = (T) deserializer.Deserialize(memStream); - memStream.Close(); - return info; - } - } - - #endregion - - #region Json序列化 - - /// - /// Json序列化 - /// - public static string ToJson(this object item, bool format = false) - { - using (StringWriter sw = new StringWriter()) - { - JsonSerializer serializer = JsonSerializer.Create( - new JsonSerializerSettings - { - DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, - - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - - //NullValueHandling = NullValueHandling.Ignore, - - DateFormatString = "yyyy-MM-dd HH:mm:ss" - } - ); - - JsonWriter jsonWriter; - if (format) - { - jsonWriter = new JsonTextWriter(sw) - { - Formatting = Formatting.Indented, - Indentation = 4, - IndentChar = ' ' - }; - } - else - { - jsonWriter = new JsonTextWriter(sw); - } - - using (jsonWriter) - { - serializer.Serialize(jsonWriter, item); - } - - return sw.ToString(); - } - } - - #endregion - - #region Json反序列化 - - /// - /// Json反序列化 - /// - public static T FromJsonTo(this string jsonString) - { - try - { - if (!string.IsNullOrWhiteSpace(jsonString)) - { - T t = JsonConvert.DeserializeObject(jsonString); - return t; - } - else - { - return default(T); - } - } - catch (Exception ex) - { - LogHelper.Error($"Json反序列化出错", $"待反序列化的json字符串为{jsonString},错误信息:{ex}"); - return default(T); - } - } - - public static T FromJsonToOrDefault(this string str) - { - try - { - if (string.IsNullOrEmpty(str) || str == "[]" || str == "{}") - { - return default(T); - } - else - { - return JsonConvert.DeserializeObject(str); - } - } - catch (Exception ex) - { - LogHelper.Error($"Json反序列化出错", $"待反序列化的json字符串为{str},错误信息:{ex}"); - return default(T); - } - } - - #endregion - - #region 获取json字符串中的属性值 - /// - /// 获取json字符串中的属性值 - /// - /// json字符串 - /// "result:data:name" - /// - public static string JsonItemValue(this string str, string key) - { - var defaultValue = ""; - if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(key)) return defaultValue; - var JObject = JsonConvert.DeserializeObject(str) as JToken; - var res = GetJsonItem(JObject, key.Split(':'), 0) ?? defaultValue; - return res.ToString(); - } - - private static JToken GetJsonItem(JToken JToken, string[] arr, int index) - { - if (JToken == null || index == arr.Length) - { - return JToken; - } - else - { - JToken = JToken[arr[index]]; - index++; - } - return GetJsonItem(JToken, arr, index); - } - #endregion - } +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using Hncore.Infrastructure.Common; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Hncore.Infrastructure.Serializer +{ + public static class ObjectExtension + { + #region 序列化为二进制 + + /// + /// 序列化位二进制 + /// + /// 要序列化的对象 + /// 字节数组 + public static byte[] SerializeBinary(this object request) + { + using (MemoryStream memStream = new MemoryStream()) + { + BinaryFormatter serializer = new BinaryFormatter(); + serializer.Serialize(memStream, request); + return memStream.GetBuffer(); + } + } + + #endregion + + #region 二进制反序列化 + + /// + /// 二进制反序列化 + /// + /// 字节数组 + /// 得到的对象 + public static T DeserializeBinary(this byte[] buf) where T : class, new() + { + if (buf == null) + { + return default(T); + } + + using (MemoryStream memStream = new MemoryStream(buf)) + { + memStream.Position = 0; + BinaryFormatter deserializer = new BinaryFormatter(); + T info = (T) deserializer.Deserialize(memStream); + memStream.Close(); + return info; + } + } + + #endregion + + #region Json序列化 + + /// + /// Json序列化 + /// + public static string ToJson(this object item, bool format = false) + { + using (StringWriter sw = new StringWriter()) + { + JsonSerializer serializer = JsonSerializer.Create( + new JsonSerializerSettings + { + DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, + + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + + //NullValueHandling = NullValueHandling.Ignore, + + DateFormatString = "yyyy-MM-dd HH:mm:ss" + } + ); + + JsonWriter jsonWriter; + if (format) + { + jsonWriter = new JsonTextWriter(sw) + { + Formatting = Formatting.Indented, + Indentation = 4, + IndentChar = ' ' + }; + } + else + { + jsonWriter = new JsonTextWriter(sw); + } + + using (jsonWriter) + { + serializer.Serialize(jsonWriter, item); + } + + return sw.ToString(); + } + } + + #endregion + + #region Json反序列化 + + /// + /// Json反序列化 + /// + public static T FromJsonTo(this string jsonString) + { + try + { + if (!string.IsNullOrWhiteSpace(jsonString)) + { + T t = JsonConvert.DeserializeObject(jsonString); + return t; + } + else + { + return default(T); + } + } + catch (Exception ex) + { + LogHelper.Error($"Json反序列化出错", $"待反序列化的json字符串为{jsonString},错误信息:{ex}"); + return default(T); + } + } + + public static T FromJsonToOrDefault(this string str) + { + try + { + if (string.IsNullOrEmpty(str) || str == "[]" || str == "{}") + { + return default(T); + } + else + { + return JsonConvert.DeserializeObject(str); + } + } + catch (Exception ex) + { + LogHelper.Error($"Json反序列化出错", $"待反序列化的json字符串为{str},错误信息:{ex}"); + return default(T); + } + } + + #endregion + + #region 获取json字符串中的属性值 + /// + /// 获取json字符串中的属性值 + /// + /// json字符串 + /// "result:data:name" + /// + public static string JsonItemValue(this string str, string key) + { + var defaultValue = ""; + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(key)) return defaultValue; + var JObject = JsonConvert.DeserializeObject(str) as JToken; + var res = GetJsonItem(JObject, key.Split(':'), 0) ?? defaultValue; + return res.ToString(); + } + + private static JToken GetJsonItem(JToken JToken, string[] arr, int index) + { + if (JToken == null || index == arr.Length) + { + return JToken; + } + else + { + JToken = JToken[arr[index]]; + index++; + } + return GetJsonItem(JToken, arr, index); + } + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Serializer/XML.cs b/Infrastructure/Hncore.Infrastructure/Serializer/XML.cs index e1fcac0..90031d9 100644 --- a/Infrastructure/Hncore.Infrastructure/Serializer/XML.cs +++ b/Infrastructure/Hncore.Infrastructure/Serializer/XML.cs @@ -1,54 +1,54 @@ -using System.IO; -using System.Xml.Serialization; - -namespace Hncore.Infrastructure.Serializer -{ - public class XML - { - #region 将C#数据实体转化为xml数据 - - /// - /// 将C#数据实体转化为xml数据 - /// - /// 要转化的数据实体 - /// xml格式字符串 - public static string XmlSerialize(T obj) - { - using (MemoryStream stream = new MemoryStream()) - { - XmlSerializer xml = new XmlSerializer(typeof(T)); - - //序列化对象 - xml.Serialize(stream, obj); - - stream.Position = 0; - using (StreamReader sr = new StreamReader(stream)) - { - string str = sr.ReadToEnd(); - - return str; - } - } - } - - #endregion - - #region 将xml数据转化为C#数据实体 - - /// - /// 将xml数据转化为C#数据实体 - /// - /// 符合xml格式的字符串 - /// T类型的对象 - public static T XmlDeserialize(string xml) - { - XmlSerializer xmldes = new XmlSerializer(typeof(T)); - using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(xml.ToCharArray()))) - { - return (T)xmldes.Deserialize(stream); - } - } - - #endregion - } +using System.IO; +using System.Xml.Serialization; + +namespace Hncore.Infrastructure.Serializer +{ + public class XML + { + #region 将C#数据实体转化为xml数据 + + /// + /// 将C#数据实体转化为xml数据 + /// + /// 要转化的数据实体 + /// xml格式字符串 + public static string XmlSerialize(T obj) + { + using (MemoryStream stream = new MemoryStream()) + { + XmlSerializer xml = new XmlSerializer(typeof(T)); + + //序列化对象 + xml.Serialize(stream, obj); + + stream.Position = 0; + using (StreamReader sr = new StreamReader(stream)) + { + string str = sr.ReadToEnd(); + + return str; + } + } + } + + #endregion + + #region 将xml数据转化为C#数据实体 + + /// + /// 将xml数据转化为C#数据实体 + /// + /// 符合xml格式的字符串 + /// T类型的对象 + public static T XmlDeserialize(string xml) + { + XmlSerializer xmldes = new XmlSerializer(typeof(T)); + using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(xml.ToCharArray()))) + { + return (T)xmldes.Deserialize(stream); + } + } + + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Service/IServiceCollectionExtension.cs b/Infrastructure/Hncore.Infrastructure/Service/IServiceCollectionExtension.cs index 3cb4095..fedf0d2 100644 --- a/Infrastructure/Hncore.Infrastructure/Service/IServiceCollectionExtension.cs +++ b/Infrastructure/Hncore.Infrastructure/Service/IServiceCollectionExtension.cs @@ -1,14 +1,14 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Hncore.Infrastructure.Service -{ - public static class IServiceCollectionExtension - { - public static void AddServiceClient(this IServiceCollection service, string baseUrl) - { - ServiceHttpClient._BaseUrl = baseUrl; - - service.AddSingleton(); - } - } +using Microsoft.Extensions.DependencyInjection; + +namespace Hncore.Infrastructure.Service +{ + public static class IServiceCollectionExtension + { + public static void AddServiceClient(this IServiceCollection service, string baseUrl) + { + ServiceHttpClient._BaseUrl = baseUrl; + + service.AddSingleton(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Service/ServiceBase.cs b/Infrastructure/Hncore.Infrastructure/Service/ServiceBase.cs index 9620151..3286f31 100644 --- a/Infrastructure/Hncore.Infrastructure/Service/ServiceBase.cs +++ b/Infrastructure/Hncore.Infrastructure/Service/ServiceBase.cs @@ -1,205 +1,205 @@ -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.DDD; -using Hncore.Infrastructure.EF; -using Hncore.Infrastructure.EntitiesExtension; -using Hncore.Infrastructure.WebApi; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Service -{ - - public interface IFindService - { - - } - - public class ServiceBase : IFindService where TEntity : class - { - IHttpContextAccessor m_HttpContextAccessor; - - public HttpContext HttpContext - { - get => m_HttpContextAccessor.HttpContext; - } - public ManageUserInfo ManagerInfo - { - get => HttpContext.Request.GetManageUserInfo(); - } - protected DbContextBase m_DbContextBase { get; set; } - public ServiceBase(DbContextBase dbContext, IHttpContextAccessor httpContextAccessor) - { - m_DbContextBase = dbContext; - m_HttpContextAccessor = httpContextAccessor; - } - - - public virtual async Task GetById(object id) - { - return await m_DbContextBase.Set().FindByIdAsync(id); - } - - public virtual async Task Add(TEntity entity, bool autoSave = true) - { - var ret = await m_DbContextBase.Set().AddAsync(entity); - if (autoSave) - await m_DbContextBase.SaveChangesAsync(); - return ret.Entity; - } - public virtual async Task Adds(IEnumerable entitys, bool autoSave = true) - { - await m_DbContextBase.Set().AddRangeAsync(entitys); - if (autoSave) - await m_DbContextBase.SaveChangesAsync(); - } - public virtual async Task DeleteById(object id, bool autoSave = true) - { - var entity = await this.GetById(id); - return await Delete(entity, autoSave); - } - - public virtual async Task Delete(TEntity entity, bool autoSave = true) - { - //if (entity is ITenant) - //{ - // var tenantId = HttpContext.Request.GetManageUserInfo()?.TenantId; - // if (tenantId.HasValue && tenantId > 0 && (entity as ITenant).TenantId != tenantId) - // { - // return false; - // } - //} - - if (entity is ISoftDelete) - { - (entity as ISoftDelete).DeleteTag = 1; - m_DbContextBase.Set().Update(entity); - } - else - { - m_DbContextBase.Set().Remove(entity); - } - if (autoSave) - await m_DbContextBase.SaveChangesAsync(); - return true; - } - - public virtual async Task Deletes(IEnumerable entitys, bool autoSave = true) - { - if (entitys == null || entitys.Count() == 0) - return false; - - var entity = entitys.FirstOrDefault(); - - if (entity is ISoftDelete) - { - foreach(var item in entitys) { (entity as ISoftDelete).DeleteTag = 1; } - m_DbContextBase.Set().UpdateRange(entity); - } - else - { - m_DbContextBase.Set().RemoveRange(entity); - } - if (autoSave) - await m_DbContextBase.SaveChangesAsync(); - return true; - } - - - public virtual async Task Update(TEntity entity, bool autoSave = true) - { - //if (entity is ITenant) - //{ - // var tenantId = HttpContext.Request.GetManageUserInfo()?.TenantId; - // if (tenantId.HasValue && tenantId > 0 && (entity as ITenant).TenantId != tenantId) - // { - // return false; - // } - //} - m_DbContextBase.Set().Update(entity); - if (autoSave) - return (await m_DbContextBase.SaveChangesAsync()) > 0; - return true; - } - - public virtual async Task Update(IEnumerable list, bool autoSave = true) - { - m_DbContextBase.Set().UpdateRange(list); - if (autoSave) - await m_DbContextBase.SaveChangesAsync(); - return true; - } - public virtual IQueryable Query(bool noTracking = false) - { - var ret = m_DbContextBase.Set().AsQueryable(); - if (noTracking) - { - ret = ret.AsNoTracking(); - } - return ret; - } - public async virtual Task> GetAll(bool noTracking = false) - { - var ret = m_DbContextBase.Set().AsQueryable(); - if (noTracking) - { - ret = ret.AsNoTracking(); - } - return await ret.ToListAsync(); - } - public virtual IQueryable Query(Expression> exp=null, bool noTracking = false) - { - var ret = m_DbContextBase.Set().AsQueryable(); - if (exp != null) - { - ret = ret.Where(exp); - } - if (noTracking) - { - ret = ret.AsNoTracking(); - } - return ret; - } - public virtual async Task> Page(int page, int limit, Expression> exp = null, bool noTracking = false) - { - var ret = await Query(exp, noTracking).ListPagerAsync(limit, page, true); - return ret; - } - - public virtual async Task> PageDesc(int page, int limit, Expression> exp = null, bool noTracking = false, Expression> order = null) - { - - var query = Query(exp, noTracking); - if (order != null) - query = query.OrderByDescending(order); - - var ret = await query.ListPagerAsync(limit, page, true); - return ret; - } - - public virtual async Task> PageAsc(int page, int limit, Expression> exp = null, bool noTracking = false, Expression> order = null) - { - - var query = Query(exp, noTracking); - if (order != null) - query = query.OrderBy(order); - - var ret = await query.ListPagerAsync(limit, page, true); - return ret; - } - - public virtual bool Exist(Expression> expr) - { - return this.Query(expr).Count() > 0; - } - - public async Task Save() - { - return (await m_DbContextBase.SaveChangesAsync()) > 0; - } - } -} +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.DDD; +using Hncore.Infrastructure.EF; +using Hncore.Infrastructure.EntitiesExtension; +using Hncore.Infrastructure.WebApi; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Service +{ + + public interface IFindService + { + + } + + public class ServiceBase : IFindService where TEntity : class + { + IHttpContextAccessor m_HttpContextAccessor; + + public HttpContext HttpContext + { + get => m_HttpContextAccessor.HttpContext; + } + public ManageUserInfo ManagerInfo + { + get => HttpContext.Request.GetManageUserInfo(); + } + protected DbContextBase m_DbContextBase { get; set; } + public ServiceBase(DbContextBase dbContext, IHttpContextAccessor httpContextAccessor) + { + m_DbContextBase = dbContext; + m_HttpContextAccessor = httpContextAccessor; + } + + + public virtual async Task GetById(object id) + { + return await m_DbContextBase.Set().FindByIdAsync(id); + } + + public virtual async Task Add(TEntity entity, bool autoSave = true) + { + var ret = await m_DbContextBase.Set().AddAsync(entity); + if (autoSave) + await m_DbContextBase.SaveChangesAsync(); + return ret.Entity; + } + public virtual async Task Adds(IEnumerable entitys, bool autoSave = true) + { + await m_DbContextBase.Set().AddRangeAsync(entitys); + if (autoSave) + await m_DbContextBase.SaveChangesAsync(); + } + public virtual async Task DeleteById(object id, bool autoSave = true) + { + var entity = await this.GetById(id); + return await Delete(entity, autoSave); + } + + public virtual async Task Delete(TEntity entity, bool autoSave = true) + { + //if (entity is ITenant) + //{ + // var tenantId = HttpContext.Request.GetManageUserInfo()?.TenantId; + // if (tenantId.HasValue && tenantId > 0 && (entity as ITenant).TenantId != tenantId) + // { + // return false; + // } + //} + + if (entity is ISoftDelete) + { + (entity as ISoftDelete).DeleteTag = 1; + m_DbContextBase.Set().Update(entity); + } + else + { + m_DbContextBase.Set().Remove(entity); + } + if (autoSave) + await m_DbContextBase.SaveChangesAsync(); + return true; + } + + public virtual async Task Deletes(IEnumerable entitys, bool autoSave = true) + { + if (entitys == null || entitys.Count() == 0) + return false; + + var entity = entitys.FirstOrDefault(); + + if (entity is ISoftDelete) + { + foreach(var item in entitys) { (entity as ISoftDelete).DeleteTag = 1; } + m_DbContextBase.Set().UpdateRange(entity); + } + else + { + m_DbContextBase.Set().RemoveRange(entity); + } + if (autoSave) + await m_DbContextBase.SaveChangesAsync(); + return true; + } + + + public virtual async Task Update(TEntity entity, bool autoSave = true) + { + //if (entity is ITenant) + //{ + // var tenantId = HttpContext.Request.GetManageUserInfo()?.TenantId; + // if (tenantId.HasValue && tenantId > 0 && (entity as ITenant).TenantId != tenantId) + // { + // return false; + // } + //} + m_DbContextBase.Set().Update(entity); + if (autoSave) + return (await m_DbContextBase.SaveChangesAsync()) > 0; + return true; + } + + public virtual async Task Update(IEnumerable list, bool autoSave = true) + { + m_DbContextBase.Set().UpdateRange(list); + if (autoSave) + await m_DbContextBase.SaveChangesAsync(); + return true; + } + public virtual IQueryable Query(bool noTracking = false) + { + var ret = m_DbContextBase.Set().AsQueryable(); + if (noTracking) + { + ret = ret.AsNoTracking(); + } + return ret; + } + public async virtual Task> GetAll(bool noTracking = false) + { + var ret = m_DbContextBase.Set().AsQueryable(); + if (noTracking) + { + ret = ret.AsNoTracking(); + } + return await ret.ToListAsync(); + } + public virtual IQueryable Query(Expression> exp=null, bool noTracking = false) + { + var ret = m_DbContextBase.Set().AsQueryable(); + if (exp != null) + { + ret = ret.Where(exp); + } + if (noTracking) + { + ret = ret.AsNoTracking(); + } + return ret; + } + public virtual async Task> Page(int page, int limit, Expression> exp = null, bool noTracking = false) + { + var ret = await Query(exp, noTracking).ListPagerAsync(limit, page, true); + return ret; + } + + public virtual async Task> PageDesc(int page, int limit, Expression> exp = null, bool noTracking = false, Expression> order = null) + { + + var query = Query(exp, noTracking); + if (order != null) + query = query.OrderByDescending(order); + + var ret = await query.ListPagerAsync(limit, page, true); + return ret; + } + + public virtual async Task> PageAsc(int page, int limit, Expression> exp = null, bool noTracking = false, Expression> order = null) + { + + var query = Query(exp, noTracking); + if (order != null) + query = query.OrderBy(order); + + var ret = await query.ListPagerAsync(limit, page, true); + return ret; + } + + public virtual bool Exist(Expression> expr) + { + return this.Query(expr).Count() > 0; + } + + public async Task Save() + { + return (await m_DbContextBase.SaveChangesAsync()) > 0; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Service/ServiceHttpClient.cs b/Infrastructure/Hncore.Infrastructure/Service/ServiceHttpClient.cs index e4d27fc..fa368d9 100644 --- a/Infrastructure/Hncore.Infrastructure/Service/ServiceHttpClient.cs +++ b/Infrastructure/Hncore.Infrastructure/Service/ServiceHttpClient.cs @@ -1,34 +1,34 @@ -using Hncore.Infrastructure.WebApi; -using System.Net.Http; - -namespace Hncore.Infrastructure.Service -{ - public class ServiceHttpClient - { - private IHttpClientFactory _httpClientFactory; - - internal static string _BaseUrl = ""; - - public string BaseUrl => _BaseUrl; - - public ServiceHttpClient(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - public HttpClient CreateHttpClient() - { - var client = _httpClientFactory.CreateClient(); - client.BaseAddress = new System.Uri(_BaseUrl); - return client; - } - - public HttpClient CreateInternalClient() - { - var client = _httpClientFactory.CreateInternalAuthClient(); - client.BaseAddress = new System.Uri(_BaseUrl); - return client; - - } - } +using Hncore.Infrastructure.WebApi; +using System.Net.Http; + +namespace Hncore.Infrastructure.Service +{ + public class ServiceHttpClient + { + private IHttpClientFactory _httpClientFactory; + + internal static string _BaseUrl = ""; + + public string BaseUrl => _BaseUrl; + + public ServiceHttpClient(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public HttpClient CreateHttpClient() + { + var client = _httpClientFactory.CreateClient(); + client.BaseAddress = new System.Uri(_BaseUrl); + return client; + } + + public HttpClient CreateInternalClient() + { + var client = _httpClientFactory.CreateInternalAuthClient(); + client.BaseAddress = new System.Uri(_BaseUrl); + return client; + + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/Service/ServiceIOCExt.cs b/Infrastructure/Hncore.Infrastructure/Service/ServiceIOCExt.cs index 9694df4..5a476fb 100644 --- a/Infrastructure/Hncore.Infrastructure/Service/ServiceIOCExt.cs +++ b/Infrastructure/Hncore.Infrastructure/Service/ServiceIOCExt.cs @@ -1,26 +1,26 @@ -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Linq; -using System.Reflection; - -namespace Hncore.Infrastructure.Service -{ - public static class ServiceIOCExt - { - public static IServiceCollection AutoAddService(this IServiceCollection service, Type fromType = null) - { - if (fromType == null) - fromType = typeof(IFindService); - - var types = Assembly.GetCallingAssembly().GetTypes(); - - types = types.Where(m => fromType.IsAssignableFrom(m)).ToArray(); - - foreach (var type in types) - { - service.AddScoped(type); - } - return service; - } - } -} +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Linq; +using System.Reflection; + +namespace Hncore.Infrastructure.Service +{ + public static class ServiceIOCExt + { + public static IServiceCollection AutoAddService(this IServiceCollection service, Type fromType = null) + { + if (fromType == null) + fromType = typeof(IFindService); + + var types = Assembly.GetCallingAssembly().GetTypes(); + + types = types.Where(m => fromType.IsAssignableFrom(m)).ToArray(); + + foreach (var type in types) + { + service.AddScoped(type); + } + return service; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Tree/DataNode.cs b/Infrastructure/Hncore.Infrastructure/Tree/DataNode.cs index 5c44ac9..cb524b9 100644 --- a/Infrastructure/Hncore.Infrastructure/Tree/DataNode.cs +++ b/Infrastructure/Hncore.Infrastructure/Tree/DataNode.cs @@ -1,74 +1,74 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Tree -{ - public class DataNode - { - public DataNode(TData data) - { - this.Data = data; - } - - public DataNode Parent { get; set; } - public TData Data { get; set; } - - public List> Children { get; set; } = new List>(); - - public bool IsLeaf { get { return this.Children.Count()==0; } } - - public void Traverse(Action> act) - { - act(this); - this.Children.ForEach(item => - { - item.Traverse(act); - }); - } - - public string GetFullPath(Func func, string separator = ".") - { - var parent = this.Parent; - var names = new List { func(this.Data) }; - while (parent != null) - { - names.Add(func(parent.Data)); - parent = parent.Parent; - } - names.Reverse(); - return string.Join(separator, names).TrimStart(separator.ToArray()); - } - - public DataNode SortAsc(Func exp) - { - if (this.Children.Count > 0) - { - Func, Tkey> iierExp = m => exp(m.Data); - this.Children.OrderBy(iierExp); - this.Children.ForEach(item => - { - item.SortAsc(exp); - }); - } - return this; - } - - public DataNode SortDesc(Func exp) - { - if (this.Children.Count > 0) - { - Func, Tkey> iierExp = m => exp(m.Data); - this.Children.OrderByDescending(iierExp); - this.Children.ForEach(item => - { - item.SortDesc(exp); - }); - } - - return this; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Tree +{ + public class DataNode + { + public DataNode(TData data) + { + this.Data = data; + } + + public DataNode Parent { get; set; } + public TData Data { get; set; } + + public List> Children { get; set; } = new List>(); + + public bool IsLeaf { get { return this.Children.Count()==0; } } + + public void Traverse(Action> act) + { + act(this); + this.Children.ForEach(item => + { + item.Traverse(act); + }); + } + + public string GetFullPath(Func func, string separator = ".") + { + var parent = this.Parent; + var names = new List { func(this.Data) }; + while (parent != null) + { + names.Add(func(parent.Data)); + parent = parent.Parent; + } + names.Reverse(); + return string.Join(separator, names).TrimStart(separator.ToArray()); + } + + public DataNode SortAsc(Func exp) + { + if (this.Children.Count > 0) + { + Func, Tkey> iierExp = m => exp(m.Data); + this.Children.OrderBy(iierExp); + this.Children.ForEach(item => + { + item.SortAsc(exp); + }); + } + return this; + } + + public DataNode SortDesc(Func exp) + { + if (this.Children.Count > 0) + { + Func, Tkey> iierExp = m => exp(m.Data); + this.Children.OrderByDescending(iierExp); + this.Children.ForEach(item => + { + item.SortDesc(exp); + }); + } + + return this; + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/Tree/DataTree.cs b/Infrastructure/Hncore.Infrastructure/Tree/DataTree.cs index 8d3bb83..8e41ad4 100644 --- a/Infrastructure/Hncore.Infrastructure/Tree/DataTree.cs +++ b/Infrastructure/Hncore.Infrastructure/Tree/DataTree.cs @@ -1,60 +1,60 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Hncore.Infrastructure.Tree -{ - public class DataTree - { - /// - /// - /// - /// 得到第一级节点条件 - /// 得到孩子得条件 - public static DataNode Load(IEnumerable datas, Func topPredicate, Func childPredicate) where TData : new() - { - var root = new DataNode(new TData() { }); - if (datas == null || datas.Count() == 0) - return root; - var tops = datas.Where(topPredicate); - - foreach (var p in tops) - { - LoadChildren(datas, root, p, childPredicate); - } - return root; - } - private static void LoadChildren(IEnumerable datas,DataNode topNode, TData p, Func childPredicate) - { - var pNode = new DataNode(p); - pNode.Parent = topNode; - topNode.Children.Add(pNode); - - var childDatas = datas.Where(item=>childPredicate(p,item)) ; - - if (childDatas.Count() > 0) - { - foreach (var childData in childDatas) - { - LoadChildren(datas,pNode, childData, childPredicate); - } - } - } - - - public static void Traverse(DataNode rootNode, Action> act, bool root = true) - { - if (root) - { - rootNode.Traverse(act); - } - else - { - rootNode.Children.ForEach(item => - { - item.Traverse(act); - }); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Hncore.Infrastructure.Tree +{ + public class DataTree + { + /// + /// + /// + /// 得到第一级节点条件 + /// 得到孩子得条件 + public static DataNode Load(IEnumerable datas, Func topPredicate, Func childPredicate) where TData : new() + { + var root = new DataNode(new TData() { }); + if (datas == null || datas.Count() == 0) + return root; + var tops = datas.Where(topPredicate); + + foreach (var p in tops) + { + LoadChildren(datas, root, p, childPredicate); + } + return root; + } + private static void LoadChildren(IEnumerable datas,DataNode topNode, TData p, Func childPredicate) + { + var pNode = new DataNode(p); + pNode.Parent = topNode; + topNode.Children.Add(pNode); + + var childDatas = datas.Where(item=>childPredicate(p,item)) ; + + if (childDatas.Count() > 0) + { + foreach (var childData in childDatas) + { + LoadChildren(datas,pNode, childData, childPredicate); + } + } + } + + + public static void Traverse(DataNode rootNode, Action> act, bool root = true) + { + if (root) + { + rootNode.Traverse(act); + } + else + { + rootNode.Children.ForEach(item => + { + item.Traverse(act); + }); + } + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/CheckController.cs b/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/CheckController.cs index 9dfa160..8761555 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/CheckController.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/CheckController.cs @@ -1,16 +1,16 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Hncore.Infrastructure.WebApi -{ - [Route("/check")] - public class CheckController: ControllerBase - { - [HttpGet] - [AllowAnonymous] - public IActionResult Get() - { - return Ok("ok"); - } - } +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Hncore.Infrastructure.WebApi +{ + [Route("/check")] + public class CheckController: ControllerBase + { + [HttpGet] + [AllowAnonymous] + public IActionResult Get() + { + return Ok("ok"); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/EtorControllerBase.cs b/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/EtorControllerBase.cs index 0b0c2ac..496d4ee 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/EtorControllerBase.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/EtorControllerBase.cs @@ -1,124 +1,124 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using Hncore.Infrastructure.EF; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Abstractions; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.AspNetCore.Mvc.ViewEngines; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Routing; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; - -namespace Hncore.Infrastructure.WebApi -{ - [ApiController] - public class HncoreControllerBase : ControllerBase - { - protected ApiResult Success() - { - return new ApiResult(ResultCode.C_SUCCESS, ""); - } - - protected ApiResult Success(T data, string message = "") - { - return new ApiResult(ResultCode.C_SUCCESS, message) {Data = data}; - } - - protected ApiResult Error(string message = "") - { - return new ApiResult(ResultCode.C_UNKNOWN_ERROR, message); - } - - protected ApiResult Error(ResultCode code, string message = "") - { - return new ApiResult(code, message); - } - - protected ApiResult UofCommit(string message = "") - { - RepositoryDbContext.SaveChanges(); - - return Success(message); - } - - protected ApiResult UofCommit(Func func, string message = "") - { - RepositoryDbContext.SaveChanges(); - - return Success(func(), message); - } - - protected async Task UofCommitAsync(string message = "") - { - await RepositoryDbContext.SaveChangesAsync(); - - return Success(message); - } - - protected async Task UofCommitAsync(IDbContextTransaction trans, string message = "") - { - await RepositoryDbContext.SaveChangesAsync(); - - trans.Commit(); - - return Success(message); - } - - protected async Task UofCommitAsync(Func func, string message = "") - { - await RepositoryDbContext.SaveChangesAsync(); - - return Success(func(), message); - } - - - protected DbContext RepositoryDbContext => - Request.HttpContext - .RequestServices - .GetService() - .DbContext; - - protected async Task RenderViewToStringAsync(string viewName, object model = null) - { - using (var sw = new StringWriter()) - { - var actionContext = new ActionContext(HttpContext, new RouteData(), new ActionDescriptor()); - - var viewResult = HttpContext.RequestServices.GetService() - .FindView(actionContext, viewName, false); - - if (viewResult.View == null) - { - throw new ArgumentNullException($"未找到视图{viewName}"); - } - - var viewDictionary = - new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) - { - Model = model - }; - - var viewContext = new ViewContext( - actionContext, - viewResult.View, - viewDictionary, - new TempDataDictionary(actionContext.HttpContext, - HttpContext.RequestServices.GetService()), - sw, - new HtmlHelperOptions() - ); - - await viewResult.View.RenderAsync(viewContext); - - return sw.ToString().HtmlDecode(); - } - } - } +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Hncore.Infrastructure.EF; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; + +namespace Hncore.Infrastructure.WebApi +{ + [ApiController] + public class HncoreControllerBase : ControllerBase + { + protected ApiResult Success() + { + return new ApiResult(ResultCode.C_SUCCESS, ""); + } + + protected ApiResult Success(T data, string message = "") + { + return new ApiResult(ResultCode.C_SUCCESS, message) {Data = data}; + } + + protected ApiResult Error(string message = "") + { + return new ApiResult(ResultCode.C_UNKNOWN_ERROR, message); + } + + protected ApiResult Error(ResultCode code, string message = "") + { + return new ApiResult(code, message); + } + + protected ApiResult UofCommit(string message = "") + { + RepositoryDbContext.SaveChanges(); + + return Success(message); + } + + protected ApiResult UofCommit(Func func, string message = "") + { + RepositoryDbContext.SaveChanges(); + + return Success(func(), message); + } + + protected async Task UofCommitAsync(string message = "") + { + await RepositoryDbContext.SaveChangesAsync(); + + return Success(message); + } + + protected async Task UofCommitAsync(IDbContextTransaction trans, string message = "") + { + await RepositoryDbContext.SaveChangesAsync(); + + trans.Commit(); + + return Success(message); + } + + protected async Task UofCommitAsync(Func func, string message = "") + { + await RepositoryDbContext.SaveChangesAsync(); + + return Success(func(), message); + } + + + protected DbContext RepositoryDbContext => + Request.HttpContext + .RequestServices + .GetService() + .DbContext; + + protected async Task RenderViewToStringAsync(string viewName, object model = null) + { + using (var sw = new StringWriter()) + { + var actionContext = new ActionContext(HttpContext, new RouteData(), new ActionDescriptor()); + + var viewResult = HttpContext.RequestServices.GetService() + .FindView(actionContext, viewName, false); + + if (viewResult.View == null) + { + throw new ArgumentNullException($"未找到视图{viewName}"); + } + + var viewDictionary = + new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) + { + Model = model + }; + + var viewContext = new ViewContext( + actionContext, + viewResult.View, + viewDictionary, + new TempDataDictionary(actionContext.HttpContext, + HttpContext.RequestServices.GetService()), + sw, + new HtmlHelperOptions() + ); + + await viewResult.View.RenderAsync(viewContext); + + return sw.ToString().HtmlDecode(); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/PodHookController.cs b/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/PodHookController.cs index 04ce1aa..671cc1a 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/PodHookController.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/CommonController/PodHookController.cs @@ -1,35 +1,35 @@ -using System; -using System.Threading.Tasks; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Common.DingTalk; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Hncore.Infrastructure.WebApi -{ - [Route("/pod/[action]")] - public class PodHookController : ControllerBase - { - [HttpGet, AllowAnonymous] - public async Task PreStop() - { - LogHelper.Warn("应用即将退出"); - - if (EnvironmentVariableHelper.IsAspNetCoreProduction) - { - await DingTalkHelper.SendMessage(new MarkDownModel() - { - markdown = new markdown() - { - title = "应用即将退出", - text = "### 应用即将退出\n\nhostname:" + EnvironmentVariableHelper.HostName + "\n\n" + - DateTime.Now.Format("yyyy-MM-dd HH:mm:ss") - } - }); - } - - return Ok(); - } - } +using System; +using System.Threading.Tasks; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Common.DingTalk; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Hncore.Infrastructure.WebApi +{ + [Route("/pod/[action]")] + public class PodHookController : ControllerBase + { + [HttpGet, AllowAnonymous] + public async Task PreStop() + { + LogHelper.Warn("应用即将退出"); + + if (EnvironmentVariableHelper.IsAspNetCoreProduction) + { + await DingTalkHelper.SendMessage(new MarkDownModel() + { + markdown = new markdown() + { + title = "应用即将退出", + text = "### 应用即将退出\n\nhostname:" + EnvironmentVariableHelper.HostName + "\n\n" + + DateTime.Now.Format("yyyy-MM-dd HH:mm:ss") + } + }); + } + + return Ok(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/DTO/ApiResult.cs b/Infrastructure/Hncore.Infrastructure/WebApi/DTO/ApiResult.cs index bb4b2d5..62246b3 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/DTO/ApiResult.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/DTO/ApiResult.cs @@ -1,274 +1,274 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.Extension; -using Newtonsoft.Json; - -namespace Hncore.Infrastructure.WebApi -{ - - public class ApiResult - { - public ApiResult() - { - - } - public ApiResult(object data):this(ResultCode.C_SUCCESS,"") - { - Data = data; - } - public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = "") - { - Code = code; - Message = message; - } - [JsonProperty("Code")] public ResultCode Code { get; private set; } - - [JsonProperty("Message")] public string Message { get; private set; } = ""; - - [JsonProperty("Data")] public virtual object Data { get; set; } - - } - - public class ApiResult: ApiResult where T : class, new() - { - [JsonProperty("Data")] public new T Data { get; set; } - - - private static readonly Dictionary Dic; - - static ApiResult() - { - Dic = ObjectExtension.ToDescriptionDictionary(); - } - public ApiResult() - { - - } - public ApiResult(T data) : this(ResultCode.C_SUCCESS, "") - { - Data = data; - } - - public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = "") : base(code, message) - { - } - - //public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = ""):base(code,message) - //{ - // Code = code; - - // if (string.IsNullOrEmpty(message) && Dic.ContainsKey(Code)) - // { - // Message = Dic[Code]; - // } - // else - // { - // Message = message; - // } - //} - } - - //public class ApiResult : ApiResult - //{ - - // public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = "") : base(code, message) - // { - // } - //} - - public class ApiResultPaged : ApiResult where T : class, new() - { - [JsonProperty("TotalCount")] public int TotalCount { get; set; } - - public ApiResultPaged(ResultCode code = ResultCode.C_SUCCESS, string message = "") : base(code, message) - { - } - } - - public static class PageDataExt - { - public static ApiResultPaged> ToApiResult(this PageData pageData) where T : class, new() - { - return new ApiResultPaged>() - { - TotalCount = pageData.RowCount, - Data = pageData.List - }; - } - - public static ApiResultPaged> ToApiResult(this PageData pageData) where T2 : class, new() - { - return new ApiResultPaged>() - { - TotalCount = pageData.RowCount, - Data = pageData.List.MapsTo().ToList() - }; - } - } - - public enum ResultCode - { - /// - /// 未知错误 - /// - [Description("服务正在更新中,请稍后再试")] C_UNKNOWN_ERROR = 0, - - /// - /// 成功 - /// - [Description("成功")] C_SUCCESS = 10000, - - /// - /// 验证码 - /// - [Description("验证码错误")] C_VERIFY_CODE_ERROR = 10001, - - /// - /// 参数 - /// - [Description("服务正在更新中,请稍后再试")] C_PARAM_ERROR = 10002, - - /// - /// 登录名 - /// - [Description("登录名错误")] C_LONGIN_NAME_ERROR = 10003, - - /// - /// 密码 - /// - [Description("密码错误")] C_PASSWORD_ERROR = 10004, - - /// - /// 无效操作 - /// - [Description("非法操作")] C_INVALID_ERROR = 10005, - - /// - /// 文件 - /// - [Description("文件错误")] C_FILE_ERROR = 10006, - - /// - /// 已存在错误 - /// - [Description("资源已存在错误")] C_ALREADY_EXISTS_ERROR = 10007, - - /// - /// 资源无法访问:不是资源的拥有者 - /// - [Description("不是资源的拥有者,资源无法访问")] C_OWNER_ERROR = 10008, - - /// - /// 资源不存在 - /// - [Description("资源不存在")] C_NOT_EXISTS_ERROR = 10009, - - /// - /// 新建角色出错 - /// - [Description("创建角色出错")] C_ROLE_CREATE_ERROR = 10010, - - /// - /// 新建权限出错 - /// - [Description("新建权限错误")] C_PERMISSION_CREATE_ERROR = 10011, - - /// - /// 绑定角色和权限出错 - /// - [Description("绑定角色和权限出错")] C_ROLE_PERMISSION_CREATE_ERROR = 10012, - - /// - /// 服务器繁忙,请稍后再试! - /// - [Description("服务器繁忙")] C_Server_Is_Busy = 10013, - - /// - /// 访问被禁止 - /// - [Description("禁止访问")] C_Access_Forbidden = 10014, - - /// - /// 非法操作 - /// - [Description("非法操作")] C_Illegal_Operation = 10015, - - /// - /// 无效的openID - /// - [Description("OpenID无效")] C_OPENID_ERROR = 10016, - - /// - /// 返回错误,但无需理会 - /// - [Description("可忽略的错误")] C_IGNORE_ERROR = 10017, - - /// - /// 用户信息错误 - /// - [Description("用户信息错误")] C_USERINFO_ERROR = 10018, - - /// - /// 用户需要认证 - /// - [Description("用户需要认证")] C_USER_SELECT_ERROR = 10019, - - /// - /// 过期 - /// - [Description("超时错误")] C_TIMEOUT_ERROR = 10020, - - /// - /// 手机和验证码不匹配 - /// - [Description("手机和验证码不匹配")] C_PHONE_CODE_ERROR = 10021, - - /// - /// 微信没有选择楼 - /// - [Description("微信没有选择楼")] C_WX_UNIT_UNSELECT_ERROR = 10022, - - /// - /// 黑名单错误 - /// - [Description("黑名单错误")] C_BLACKLIST_ERROR = 10023, - - /// - /// 支付失败 - /// - [Description("支付失败")] C_PAY_FAIL = 10024, - - /// - /// 重复支付 - /// - [Description("重复支付")] RepeatPay= 10025, - - /// - /// 重定向 - /// - [Description("重定向")] C_REDIRECT_URL = 100302, - - [Description("用户重定向")] C_USER_REDIRECT_URL = 900302, - - - [Description("人脸已经存在")] C_FACEKEY_EXIST_ERROR = 900303, - - [Description("人脸角度不正确")] C_FACE_ANGLE_ERROR = 900304, - - [Description("退款失败")] C_PAY_Refund = 900305, - - /// - /// 用户支付中 - /// - [Description("用户支付中")] C_USERPAYING = 900306, - - - [Description("审核中")] C_VISITOR_CHECKING = 11001, - [Description("已过期")] C_VISITOR_OUTTIME = 11002, - [Description("未到期")] C_VISITOR_NOTYETDUE = 11003, - - } +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.Extension; +using Newtonsoft.Json; + +namespace Hncore.Infrastructure.WebApi +{ + + public class ApiResult + { + public ApiResult() + { + + } + public ApiResult(object data):this(ResultCode.C_SUCCESS,"") + { + Data = data; + } + public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = "") + { + Code = code; + Message = message; + } + [JsonProperty("Code")] public ResultCode Code { get; private set; } + + [JsonProperty("Message")] public string Message { get; private set; } = ""; + + [JsonProperty("Data")] public virtual object Data { get; set; } + + } + + public class ApiResult: ApiResult where T : class, new() + { + [JsonProperty("Data")] public new T Data { get; set; } + + + private static readonly Dictionary Dic; + + static ApiResult() + { + Dic = ObjectExtension.ToDescriptionDictionary(); + } + public ApiResult() + { + + } + public ApiResult(T data) : this(ResultCode.C_SUCCESS, "") + { + Data = data; + } + + public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = "") : base(code, message) + { + } + + //public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = ""):base(code,message) + //{ + // Code = code; + + // if (string.IsNullOrEmpty(message) && Dic.ContainsKey(Code)) + // { + // Message = Dic[Code]; + // } + // else + // { + // Message = message; + // } + //} + } + + //public class ApiResult : ApiResult + //{ + + // public ApiResult(ResultCode code = ResultCode.C_SUCCESS, string message = "") : base(code, message) + // { + // } + //} + + public class ApiResultPaged : ApiResult where T : class, new() + { + [JsonProperty("TotalCount")] public int TotalCount { get; set; } + + public ApiResultPaged(ResultCode code = ResultCode.C_SUCCESS, string message = "") : base(code, message) + { + } + } + + public static class PageDataExt + { + public static ApiResultPaged> ToApiResult(this PageData pageData) where T : class, new() + { + return new ApiResultPaged>() + { + TotalCount = pageData.RowCount, + Data = pageData.List + }; + } + + public static ApiResultPaged> ToApiResult(this PageData pageData) where T2 : class, new() + { + return new ApiResultPaged>() + { + TotalCount = pageData.RowCount, + Data = pageData.List.MapsTo().ToList() + }; + } + } + + public enum ResultCode + { + /// + /// 未知错误 + /// + [Description("服务正在更新中,请稍后再试")] C_UNKNOWN_ERROR = 0, + + /// + /// 成功 + /// + [Description("成功")] C_SUCCESS = 10000, + + /// + /// 验证码 + /// + [Description("验证码错误")] C_VERIFY_CODE_ERROR = 10001, + + /// + /// 参数 + /// + [Description("服务正在更新中,请稍后再试")] C_PARAM_ERROR = 10002, + + /// + /// 登录名 + /// + [Description("登录名错误")] C_LONGIN_NAME_ERROR = 10003, + + /// + /// 密码 + /// + [Description("密码错误")] C_PASSWORD_ERROR = 10004, + + /// + /// 无效操作 + /// + [Description("非法操作")] C_INVALID_ERROR = 10005, + + /// + /// 文件 + /// + [Description("文件错误")] C_FILE_ERROR = 10006, + + /// + /// 已存在错误 + /// + [Description("资源已存在错误")] C_ALREADY_EXISTS_ERROR = 10007, + + /// + /// 资源无法访问:不是资源的拥有者 + /// + [Description("不是资源的拥有者,资源无法访问")] C_OWNER_ERROR = 10008, + + /// + /// 资源不存在 + /// + [Description("资源不存在")] C_NOT_EXISTS_ERROR = 10009, + + /// + /// 新建角色出错 + /// + [Description("创建角色出错")] C_ROLE_CREATE_ERROR = 10010, + + /// + /// 新建权限出错 + /// + [Description("新建权限错误")] C_PERMISSION_CREATE_ERROR = 10011, + + /// + /// 绑定角色和权限出错 + /// + [Description("绑定角色和权限出错")] C_ROLE_PERMISSION_CREATE_ERROR = 10012, + + /// + /// 服务器繁忙,请稍后再试! + /// + [Description("服务器繁忙")] C_Server_Is_Busy = 10013, + + /// + /// 访问被禁止 + /// + [Description("禁止访问")] C_Access_Forbidden = 10014, + + /// + /// 非法操作 + /// + [Description("非法操作")] C_Illegal_Operation = 10015, + + /// + /// 无效的openID + /// + [Description("OpenID无效")] C_OPENID_ERROR = 10016, + + /// + /// 返回错误,但无需理会 + /// + [Description("可忽略的错误")] C_IGNORE_ERROR = 10017, + + /// + /// 用户信息错误 + /// + [Description("用户信息错误")] C_USERINFO_ERROR = 10018, + + /// + /// 用户需要认证 + /// + [Description("用户需要认证")] C_USER_SELECT_ERROR = 10019, + + /// + /// 过期 + /// + [Description("超时错误")] C_TIMEOUT_ERROR = 10020, + + /// + /// 手机和验证码不匹配 + /// + [Description("手机和验证码不匹配")] C_PHONE_CODE_ERROR = 10021, + + /// + /// 微信没有选择楼 + /// + [Description("微信没有选择楼")] C_WX_UNIT_UNSELECT_ERROR = 10022, + + /// + /// 黑名单错误 + /// + [Description("黑名单错误")] C_BLACKLIST_ERROR = 10023, + + /// + /// 支付失败 + /// + [Description("支付失败")] C_PAY_FAIL = 10024, + + /// + /// 重复支付 + /// + [Description("重复支付")] RepeatPay= 10025, + + /// + /// 重定向 + /// + [Description("重定向")] C_REDIRECT_URL = 100302, + + [Description("用户重定向")] C_USER_REDIRECT_URL = 900302, + + + [Description("人脸已经存在")] C_FACEKEY_EXIST_ERROR = 900303, + + [Description("人脸角度不正确")] C_FACE_ANGLE_ERROR = 900304, + + [Description("退款失败")] C_PAY_Refund = 900305, + + /// + /// 用户支付中 + /// + [Description("用户支付中")] C_USERPAYING = 900306, + + + [Description("审核中")] C_VISITOR_CHECKING = 11001, + [Description("已过期")] C_VISITOR_OUTTIME = 11002, + [Description("未到期")] C_VISITOR_NOTYETDUE = 11003, + + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/DTO/EtorRequestBase.cs b/Infrastructure/Hncore.Infrastructure/WebApi/DTO/EtorRequestBase.cs index 13677ce..82e1309 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/DTO/EtorRequestBase.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/DTO/EtorRequestBase.cs @@ -1,126 +1,126 @@ -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; - -namespace Hncore.Infrastructure.WebApi -{ - /// - /// 请求顶级父类 - /// - public class RequestBase - { - [JsonProperty("TenantId")] - [FromQuery(Name = "TenantId")] - public int? __tenantId { get; set; } - - /// - /// 隶属物业数据库ID - /// - [JsonIgnore] - public int TenantId - { - get => __tenantId.ToInt(); - set => __tenantId = value; - } - - [JsonProperty("OperaterId")] - [FromQuery(Name = "OperaterId")] - public int? __operaterId { get; set; } - - /// - /// 当前操作员数据库ID - /// - [JsonIgnore] - public int OperaterId - { - get => __operaterId.ToInt(); - set => __operaterId = value; - } - - [JsonProperty("ProjectCode")] - [FromQuery(Name = "ProjectCode")] - public int? __projectCode { get; set; } - - /// - /// 隶属项目编码 - /// - [JsonIgnore] - public int ProjectCode - { - get => __projectCode.ToInt(); - set => __projectCode = value; - } - } - - /// - /// 泛型请求父类(主要用于在请求时携带数据) - /// - /// 携带的数据类型 - public class RequestBase : RequestBase - { - /// - /// 请求携带的数据对象 - /// - public T Data { get; set; } - } - - /// - /// 分页请求父类(主要用于分页请求操作) - /// - /// - public class PageRequestBase : RequestBase - { - [JsonProperty("PageIndex")] - [FromQuery(Name = "PageIndex")] - public int? __pageIndex { get; set; } = 1; - - /// - /// 当前页码 - /// - [JsonIgnore] - public int PageIndex - { - get => __pageIndex.ToInt(); - set => __pageIndex = value; - } - - [JsonProperty("PageSize")] - [FromQuery(Name = "PageSize")] - public int? __pageSize { get; set; } = 50; - - /// - /// 每页条目数 - /// - [JsonIgnore] - public int PageSize - { - get => __pageSize.ToInt(); - set => __pageSize = value; - } - - public string KeyWord { get; set; } - } - - /// - /// 泛型分页请求父类(在分页请求的基础之上携带数据) - /// - /// 携带的数据类型 - public class PageRequestBase : PageRequestBase - { - /// - /// 请求携带的数据对象 - /// - public T Data { get; set; } - } - - /// - /// 主键ID查询请求类(主要用于根据一个主键ID查询单条数据的情况) - /// - public class QueryByIdRequest : RequestBase - { - /// - /// 记录的数据库主键ID - /// - public int Id { get; set; } - } +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; + +namespace Hncore.Infrastructure.WebApi +{ + /// + /// 请求顶级父类 + /// + public class RequestBase + { + [JsonProperty("TenantId")] + [FromQuery(Name = "TenantId")] + public int? __tenantId { get; set; } + + /// + /// 隶属物业数据库ID + /// + [JsonIgnore] + public int TenantId + { + get => __tenantId.ToInt(); + set => __tenantId = value; + } + + [JsonProperty("OperaterId")] + [FromQuery(Name = "OperaterId")] + public int? __operaterId { get; set; } + + /// + /// 当前操作员数据库ID + /// + [JsonIgnore] + public int OperaterId + { + get => __operaterId.ToInt(); + set => __operaterId = value; + } + + [JsonProperty("ProjectCode")] + [FromQuery(Name = "ProjectCode")] + public int? __projectCode { get; set; } + + /// + /// 隶属项目编码 + /// + [JsonIgnore] + public int ProjectCode + { + get => __projectCode.ToInt(); + set => __projectCode = value; + } + } + + /// + /// 泛型请求父类(主要用于在请求时携带数据) + /// + /// 携带的数据类型 + public class RequestBase : RequestBase + { + /// + /// 请求携带的数据对象 + /// + public T Data { get; set; } + } + + /// + /// 分页请求父类(主要用于分页请求操作) + /// + /// + public class PageRequestBase : RequestBase + { + [JsonProperty("PageIndex")] + [FromQuery(Name = "PageIndex")] + public int? __pageIndex { get; set; } = 1; + + /// + /// 当前页码 + /// + [JsonIgnore] + public int PageIndex + { + get => __pageIndex.ToInt(); + set => __pageIndex = value; + } + + [JsonProperty("PageSize")] + [FromQuery(Name = "PageSize")] + public int? __pageSize { get; set; } = 50; + + /// + /// 每页条目数 + /// + [JsonIgnore] + public int PageSize + { + get => __pageSize.ToInt(); + set => __pageSize = value; + } + + public string KeyWord { get; set; } + } + + /// + /// 泛型分页请求父类(在分页请求的基础之上携带数据) + /// + /// 携带的数据类型 + public class PageRequestBase : PageRequestBase + { + /// + /// 请求携带的数据对象 + /// + public T Data { get; set; } + } + + /// + /// 主键ID查询请求类(主要用于根据一个主键ID查询单条数据的情况) + /// + public class QueryByIdRequest : RequestBase + { + /// + /// 记录的数据库主键ID + /// + public int Id { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/EtorJwtValidator.cs b/Infrastructure/Hncore.Infrastructure/WebApi/EtorJwtValidator.cs index 5365339..79ccc51 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/EtorJwtValidator.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/EtorJwtValidator.cs @@ -1,219 +1,219 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using JWT; - -namespace Hncore.Infrastructure.WebApi -{ - public class ValidatorOption - { - public bool ValidateLifetime = true; - } - - public sealed class EtorJwtValidator : IJwtValidator - { - private readonly IJsonSerializer _jsonSerializer; - private readonly IDateTimeProvider _dateTimeProvider; - - private readonly ValidatorOption _option; - - /// - /// Creates an instance of - /// - /// The Json Serializer - /// The DateTime Provider - public EtorJwtValidator(IJsonSerializer jsonSerializer, IDateTimeProvider dateTimeProvider,ValidatorOption option) - { - _jsonSerializer = jsonSerializer; - _dateTimeProvider = dateTimeProvider; - _option = option; - } - - /// - /// - /// - public void Validate(string payloadJson, string decodedCrypto, string decodedSignature) - { - var ex = GetValidationException(payloadJson, decodedCrypto, decodedSignature); - if (ex != null) - throw ex; - } - - /// - /// - /// - public void Validate(string payloadJson, string decodedCrypto, string[] decodedSignatures) - { - var ex = GetValidationException(payloadJson, decodedCrypto, decodedSignatures); - if (ex != null) - throw ex; - } - - /// - /// Given the JWT, verifies its signature correctness without throwing an exception but returning it instead - /// - /// >An arbitrary payload (already serialized to JSON) - /// Decoded body - /// Decoded signature - /// Validation exception, if any - /// True if exception is JWT is valid and exception is null, otherwise false - public bool TryValidate(string payloadJson, string decodedCrypto, string decodedSignature, out Exception ex) - { - ex = GetValidationException(payloadJson, decodedCrypto, decodedSignature); - return ex is null; - } - - /// - /// Given the JWT, verifies its signatures correctness without throwing an exception but returning it instead - /// - /// >An arbitrary payload (already serialized to JSON) - /// Decoded body - /// Decoded signatures - /// Validation exception, if any - /// True if exception is JWT is valid and exception is null, otherwise false - public bool TryValidate(string payloadJson, string decodedCrypto, string[] decodedSignature, out Exception ex) - { - ex = GetValidationException(payloadJson, decodedCrypto, decodedSignature); - return ex is null; - } - - private Exception GetValidationException(string payloadJson, string decodedCrypto, string decodedSignature) - { - if (String.IsNullOrWhiteSpace(payloadJson)) - return new ArgumentException(nameof(payloadJson)); - - if (String.IsNullOrWhiteSpace(decodedCrypto)) - return new ArgumentException(nameof(decodedCrypto)); - - if (String.IsNullOrWhiteSpace(decodedSignature)) - return new ArgumentException(nameof(decodedSignature)); - - if (!CompareCryptoWithSignature(decodedCrypto, decodedSignature)) - return new SignatureVerificationException(decodedCrypto, decodedSignature); - - return GetValidationException(payloadJson); - } - - private Exception GetValidationException(string payloadJson, string decodedCrypto, string[] decodedSignatures) - { - if (String.IsNullOrWhiteSpace(payloadJson)) - return new ArgumentException(nameof(payloadJson)); - - if (String.IsNullOrWhiteSpace(decodedCrypto)) - return new ArgumentException(nameof(decodedCrypto)); - - if (AreAllDecodedSignaturesNullOrWhiteSpace(decodedSignatures)) - return new ArgumentException(nameof(decodedSignatures)); - - if (!IsAnySignatureValid(decodedCrypto, decodedSignatures)) - return new SignatureVerificationException(decodedCrypto, decodedSignatures); - - return GetValidationException(payloadJson); - } - - private Exception GetValidationException(string payloadJson) - { - if (!_option.ValidateLifetime) - { - return null; - } - - var payloadData = _jsonSerializer.Deserialize>(payloadJson); - - var now = _dateTimeProvider.GetNow(); - var secondsSinceEpoch = UnixEpoch.GetSecondsSince(now); - - return ValidateExpClaim(payloadData, secondsSinceEpoch) ?? ValidateNbfClaim(payloadData, secondsSinceEpoch); - } - - private static bool AreAllDecodedSignaturesNullOrWhiteSpace(string[] decodedSignatures) => - decodedSignatures.All(sgn => String.IsNullOrWhiteSpace(sgn)); - - private static bool IsAnySignatureValid(string decodedCrypto, string[] decodedSignatures) => - decodedSignatures.Any(decodedSignature => CompareCryptoWithSignature(decodedCrypto, decodedSignature)); - - /// In the future this method can be opened for extension so made protected virtual - private static bool CompareCryptoWithSignature(string decodedCrypto, string decodedSignature) - { - if (decodedCrypto.Length != decodedSignature.Length) - return false; - - var decodedCryptoBytes = Encoding.UTF8.GetBytes(decodedCrypto); - var decodedSignatureBytes = Encoding.UTF8.GetBytes(decodedSignature); - - byte result = 0; - for (var i = 0; i < decodedCrypto.Length; i++) - { - result |= (byte) (decodedCryptoBytes[i] ^ decodedSignatureBytes[i]); - } - - return result == 0; - } - - /// - /// Verifies the 'exp' claim. - /// - /// See https://tools.ietf.org/html/rfc7515#section-4.1.4 - /// - /// - private static Exception ValidateExpClaim(IDictionary payloadData, double secondsSinceEpoch) - { - - if (!payloadData.TryGetValue("exp", out var expObj)) - return null; - - if (expObj is null) - return new SignatureVerificationException("Claim 'exp' must be a number."); - - double expValue; - try - { - expValue = Convert.ToDouble(expObj); - } - catch - { - return new SignatureVerificationException("Claim 'exp' must be a number."); - } - - if (secondsSinceEpoch >= expValue) - { - return new TokenExpiredException("Token has expired."); - } - - return null; - } - - /// - /// Verifies the 'nbf' claim. - /// - /// See https://tools.ietf.org/html/rfc7515#section-4.1.5 - /// - private static Exception ValidateNbfClaim(IReadOnlyDictionary payloadData, - double secondsSinceEpoch) - { - if (!payloadData.TryGetValue("nbf", out var nbfObj)) - return null; - - if (nbfObj is null) - return new SignatureVerificationException("Claim 'nbf' must be a number."); - - double nbfValue; - try - { - nbfValue = Convert.ToDouble(nbfObj); - } - catch - { - return new SignatureVerificationException("Claim 'nbf' must be a number."); - } - - if (secondsSinceEpoch < nbfValue) - { - return new SignatureVerificationException("Token is not yet valid."); - } - - return null; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using JWT; + +namespace Hncore.Infrastructure.WebApi +{ + public class ValidatorOption + { + public bool ValidateLifetime = true; + } + + public sealed class EtorJwtValidator : IJwtValidator + { + private readonly IJsonSerializer _jsonSerializer; + private readonly IDateTimeProvider _dateTimeProvider; + + private readonly ValidatorOption _option; + + /// + /// Creates an instance of + /// + /// The Json Serializer + /// The DateTime Provider + public EtorJwtValidator(IJsonSerializer jsonSerializer, IDateTimeProvider dateTimeProvider,ValidatorOption option) + { + _jsonSerializer = jsonSerializer; + _dateTimeProvider = dateTimeProvider; + _option = option; + } + + /// + /// + /// + public void Validate(string payloadJson, string decodedCrypto, string decodedSignature) + { + var ex = GetValidationException(payloadJson, decodedCrypto, decodedSignature); + if (ex != null) + throw ex; + } + + /// + /// + /// + public void Validate(string payloadJson, string decodedCrypto, string[] decodedSignatures) + { + var ex = GetValidationException(payloadJson, decodedCrypto, decodedSignatures); + if (ex != null) + throw ex; + } + + /// + /// Given the JWT, verifies its signature correctness without throwing an exception but returning it instead + /// + /// >An arbitrary payload (already serialized to JSON) + /// Decoded body + /// Decoded signature + /// Validation exception, if any + /// True if exception is JWT is valid and exception is null, otherwise false + public bool TryValidate(string payloadJson, string decodedCrypto, string decodedSignature, out Exception ex) + { + ex = GetValidationException(payloadJson, decodedCrypto, decodedSignature); + return ex is null; + } + + /// + /// Given the JWT, verifies its signatures correctness without throwing an exception but returning it instead + /// + /// >An arbitrary payload (already serialized to JSON) + /// Decoded body + /// Decoded signatures + /// Validation exception, if any + /// True if exception is JWT is valid and exception is null, otherwise false + public bool TryValidate(string payloadJson, string decodedCrypto, string[] decodedSignature, out Exception ex) + { + ex = GetValidationException(payloadJson, decodedCrypto, decodedSignature); + return ex is null; + } + + private Exception GetValidationException(string payloadJson, string decodedCrypto, string decodedSignature) + { + if (String.IsNullOrWhiteSpace(payloadJson)) + return new ArgumentException(nameof(payloadJson)); + + if (String.IsNullOrWhiteSpace(decodedCrypto)) + return new ArgumentException(nameof(decodedCrypto)); + + if (String.IsNullOrWhiteSpace(decodedSignature)) + return new ArgumentException(nameof(decodedSignature)); + + if (!CompareCryptoWithSignature(decodedCrypto, decodedSignature)) + return new SignatureVerificationException(decodedCrypto, decodedSignature); + + return GetValidationException(payloadJson); + } + + private Exception GetValidationException(string payloadJson, string decodedCrypto, string[] decodedSignatures) + { + if (String.IsNullOrWhiteSpace(payloadJson)) + return new ArgumentException(nameof(payloadJson)); + + if (String.IsNullOrWhiteSpace(decodedCrypto)) + return new ArgumentException(nameof(decodedCrypto)); + + if (AreAllDecodedSignaturesNullOrWhiteSpace(decodedSignatures)) + return new ArgumentException(nameof(decodedSignatures)); + + if (!IsAnySignatureValid(decodedCrypto, decodedSignatures)) + return new SignatureVerificationException(decodedCrypto, decodedSignatures); + + return GetValidationException(payloadJson); + } + + private Exception GetValidationException(string payloadJson) + { + if (!_option.ValidateLifetime) + { + return null; + } + + var payloadData = _jsonSerializer.Deserialize>(payloadJson); + + var now = _dateTimeProvider.GetNow(); + var secondsSinceEpoch = UnixEpoch.GetSecondsSince(now); + + return ValidateExpClaim(payloadData, secondsSinceEpoch) ?? ValidateNbfClaim(payloadData, secondsSinceEpoch); + } + + private static bool AreAllDecodedSignaturesNullOrWhiteSpace(string[] decodedSignatures) => + decodedSignatures.All(sgn => String.IsNullOrWhiteSpace(sgn)); + + private static bool IsAnySignatureValid(string decodedCrypto, string[] decodedSignatures) => + decodedSignatures.Any(decodedSignature => CompareCryptoWithSignature(decodedCrypto, decodedSignature)); + + /// In the future this method can be opened for extension so made protected virtual + private static bool CompareCryptoWithSignature(string decodedCrypto, string decodedSignature) + { + if (decodedCrypto.Length != decodedSignature.Length) + return false; + + var decodedCryptoBytes = Encoding.UTF8.GetBytes(decodedCrypto); + var decodedSignatureBytes = Encoding.UTF8.GetBytes(decodedSignature); + + byte result = 0; + for (var i = 0; i < decodedCrypto.Length; i++) + { + result |= (byte) (decodedCryptoBytes[i] ^ decodedSignatureBytes[i]); + } + + return result == 0; + } + + /// + /// Verifies the 'exp' claim. + /// + /// See https://tools.ietf.org/html/rfc7515#section-4.1.4 + /// + /// + private static Exception ValidateExpClaim(IDictionary payloadData, double secondsSinceEpoch) + { + + if (!payloadData.TryGetValue("exp", out var expObj)) + return null; + + if (expObj is null) + return new SignatureVerificationException("Claim 'exp' must be a number."); + + double expValue; + try + { + expValue = Convert.ToDouble(expObj); + } + catch + { + return new SignatureVerificationException("Claim 'exp' must be a number."); + } + + if (secondsSinceEpoch >= expValue) + { + return new TokenExpiredException("Token has expired."); + } + + return null; + } + + /// + /// Verifies the 'nbf' claim. + /// + /// See https://tools.ietf.org/html/rfc7515#section-4.1.5 + /// + private static Exception ValidateNbfClaim(IReadOnlyDictionary payloadData, + double secondsSinceEpoch) + { + if (!payloadData.TryGetValue("nbf", out var nbfObj)) + return null; + + if (nbfObj is null) + return new SignatureVerificationException("Claim 'nbf' must be a number."); + + double nbfValue; + try + { + nbfValue = Convert.ToDouble(nbfObj); + } + catch + { + return new SignatureVerificationException("Claim 'nbf' must be a number."); + } + + if (secondsSinceEpoch < nbfValue) + { + return new SignatureVerificationException("Token is not yet valid."); + } + + return null; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/AuthBase.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/AuthBase.cs index 04a5b44..b42028c 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/AuthBase.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/AuthBase.cs @@ -1,50 +1,50 @@ -using System; -using Hncore.Infrastructure.Common; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace Hncore.Infrastructure.WebApi -{ - public abstract class AuthBase : Attribute, IAuthorizationFilter, IResourceFilter - { - public abstract void OnAuthorization(AuthorizationFilterContext context); - - public void OnResourceExecuting(ResourceExecutingContext context) - { - if (!context.HasPassed() && !context.AllowAnonymous()) - { - context.Reject(); - } - } - - public void OnResourceExecuted(ResourceExecutedContext context) - { - } - - /// - /// 内部接口签名 - /// - /// - /// - /// - public static string CreateInternalApiSign(long timestamp, string randomstr) - { - string secret = - "1CD985F202645678FF1CE16BC14BCB9E.74562B91A1E851E9CEC9DA8BCE313DFE.EA6B2EFBDD4255A9F1B3BBC6399B58F4"; - - return SecurityHelper.GetMd5Hash($"{timestamp}{randomstr}{secret}"); - } - - /// - /// 创建第三方开放接口签名 - /// - /// - /// - /// - /// - public static string CreateOpenApiSign(long timestamp, string randomstr, string appKey) - { - return SecurityHelper.GetMd5Hash($"{timestamp}{randomstr}{appKey}"); - } - } +using System; +using Hncore.Infrastructure.Common; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Hncore.Infrastructure.WebApi +{ + public abstract class AuthBase : Attribute, IAuthorizationFilter, IResourceFilter + { + public abstract void OnAuthorization(AuthorizationFilterContext context); + + public void OnResourceExecuting(ResourceExecutingContext context) + { + if (!context.HasPassed() && !context.AllowAnonymous()) + { + context.Reject(); + } + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } + + /// + /// 内部接口签名 + /// + /// + /// + /// + public static string CreateInternalApiSign(long timestamp, string randomstr) + { + string secret = + "1CD985F202645678FF1CE16BC14BCB9E.74562B91A1E851E9CEC9DA8BCE313DFE.EA6B2EFBDD4255A9F1B3BBC6399B58F4"; + + return SecurityHelper.GetMd5Hash($"{timestamp}{randomstr}{secret}"); + } + + /// + /// 创建第三方开放接口签名 + /// + /// + /// + /// + /// + public static string CreateOpenApiSign(long timestamp, string randomstr, string appKey) + { + return SecurityHelper.GetMd5Hash($"{timestamp}{randomstr}{appKey}"); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/HttpContextExt.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/HttpContextExt.cs index 31f4300..daf9e15 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/HttpContextExt.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/HttpContextExt.cs @@ -1,180 +1,180 @@ -using System.Linq; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace Hncore.Infrastructure.WebApi -{ - public static class HttpContextExt - { - /// - /// 是否允许匿名访问 - /// - /// - /// - public static bool AllowAnonymous(this AuthorizationFilterContext context) - { - if (context.HttpContext.Items.ContainsKey("AllowAnonymous") - && context.HttpContext.Items["AllowAnonymous"].ToBool()) - { - return true; - } - - if (context.Filters.Any(item => item is IAllowAnonymousFilter)) - { - context.HttpContext.Items["AllowAnonymous"] = true; - return true; - } - - return false; - } - - /// - /// 是否允许匿名访问 - /// - /// - /// - public static bool AllowAnonymous(this ResourceExecutingContext context) - { - if (context.HttpContext.Items.ContainsKey("AllowAnonymous") - && context.HttpContext.Items["AllowAnonymous"].ToBool()) - { - return true; - } - - if (context.Filters.Any(item => item is IAllowAnonymousFilter)) - { - context.HttpContext.Items["AllowAnonymous"] = true; - return true; - } - - return false; - } - - /// - /// 是否已通过验证 - /// - /// - public static bool HasPassed(this AuthorizationFilterContext context) - { - if (context.HttpContext.Items.ContainsKey("AuthPassed") && context.HttpContext.Items["AuthPassed"].ToBool()) - { - return true; - } - - return false; - } - - /// - /// 是否已通过验证 - /// - /// - public static bool HasPassed(this ResourceExecutingContext context) - { - if (context.HttpContext.Items.ContainsKey("AuthPassed") && context.HttpContext.Items["AuthPassed"].ToBool()) - { - return true; - } - - return false; - } - - /// - /// 通过验证 - /// - /// - /// 过滤器名称 - /// - public static void SetPassed(this AuthorizationFilterContext context, string filterName) - { - context.HttpContext.Items["AuthPassed"] = true; - context.HttpContext.Items["AuthPassedFilterName"] = filterName; - } - - /// - /// 拒绝通过 - /// - /// - /// 拒绝原因 - public static void Reject(this AuthorizationFilterContext context, string reason = "") - { - context.HttpContext.Response.StatusCode = 401; - context.Result = new JsonResult(new ApiResult(ResultCode.C_Access_Forbidden, reason)); - } - - /// - /// 拒绝通过并跳转 - /// - /// - /// - public static void RejectToRedirect(this AuthorizationFilterContext context, string url = "") - { - //context.HttpContext.Response.StatusCode = 401; - context.Result = new RedirectResult(url); - } - - /// - /// 拒绝通过 - /// - /// - /// 拒绝原因 - public static void Reject(this ResourceExecutingContext context, string reason = "") - { - context.HttpContext.Response.StatusCode = 401; - context.Result = new JsonResult(new ApiResult(ResultCode.C_Access_Forbidden, reason)); - } - - - /// - /// 是否包含内部调用验证信息 - /// - /// - /// - public static bool HasInternalApiAuthInfo(this AuthorizationFilterContext context) - { - if (!context.HttpContext.Request.Headers.ContainsKey("timestamp") - || !context.HttpContext.Request.Headers.ContainsKey("randomstr") - || !context.HttpContext.Request.Headers.ContainsKey("internalsign")) - { - return false; - } - - return true; - } - - /// - /// 是否包含第三方开放验证信息 - /// - /// - /// - public static bool HasOpenApiAuthInfo(this AuthorizationFilterContext context) - { - if (!context.HttpContext.Request.Headers.ContainsKey("timestamp") - || !context.HttpContext.Request.Headers.ContainsKey("randomstr") - || !context.HttpContext.Request.Headers.ContainsKey("appid") - || !context.HttpContext.Request.Headers.ContainsKey("sign")) - { - return false; - } - - return true; - } - - /// - /// 是否包含token验证信息 - /// - /// - /// - public static bool HasTokenAuthInfo(this AuthorizationFilterContext context) - { - if (!context.HttpContext.Request.Headers.ContainsKey("token")&&!context.HttpContext.Request.Cookies.ContainsKey("token")) - { - return false; - } - - return true; - } - } +using System.Linq; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Hncore.Infrastructure.WebApi +{ + public static class HttpContextExt + { + /// + /// 是否允许匿名访问 + /// + /// + /// + public static bool AllowAnonymous(this AuthorizationFilterContext context) + { + if (context.HttpContext.Items.ContainsKey("AllowAnonymous") + && context.HttpContext.Items["AllowAnonymous"].ToBool()) + { + return true; + } + + if (context.Filters.Any(item => item is IAllowAnonymousFilter)) + { + context.HttpContext.Items["AllowAnonymous"] = true; + return true; + } + + return false; + } + + /// + /// 是否允许匿名访问 + /// + /// + /// + public static bool AllowAnonymous(this ResourceExecutingContext context) + { + if (context.HttpContext.Items.ContainsKey("AllowAnonymous") + && context.HttpContext.Items["AllowAnonymous"].ToBool()) + { + return true; + } + + if (context.Filters.Any(item => item is IAllowAnonymousFilter)) + { + context.HttpContext.Items["AllowAnonymous"] = true; + return true; + } + + return false; + } + + /// + /// 是否已通过验证 + /// + /// + public static bool HasPassed(this AuthorizationFilterContext context) + { + if (context.HttpContext.Items.ContainsKey("AuthPassed") && context.HttpContext.Items["AuthPassed"].ToBool()) + { + return true; + } + + return false; + } + + /// + /// 是否已通过验证 + /// + /// + public static bool HasPassed(this ResourceExecutingContext context) + { + if (context.HttpContext.Items.ContainsKey("AuthPassed") && context.HttpContext.Items["AuthPassed"].ToBool()) + { + return true; + } + + return false; + } + + /// + /// 通过验证 + /// + /// + /// 过滤器名称 + /// + public static void SetPassed(this AuthorizationFilterContext context, string filterName) + { + context.HttpContext.Items["AuthPassed"] = true; + context.HttpContext.Items["AuthPassedFilterName"] = filterName; + } + + /// + /// 拒绝通过 + /// + /// + /// 拒绝原因 + public static void Reject(this AuthorizationFilterContext context, string reason = "") + { + context.HttpContext.Response.StatusCode = 401; + context.Result = new JsonResult(new ApiResult(ResultCode.C_Access_Forbidden, reason)); + } + + /// + /// 拒绝通过并跳转 + /// + /// + /// + public static void RejectToRedirect(this AuthorizationFilterContext context, string url = "") + { + //context.HttpContext.Response.StatusCode = 401; + context.Result = new RedirectResult(url); + } + + /// + /// 拒绝通过 + /// + /// + /// 拒绝原因 + public static void Reject(this ResourceExecutingContext context, string reason = "") + { + context.HttpContext.Response.StatusCode = 401; + context.Result = new JsonResult(new ApiResult(ResultCode.C_Access_Forbidden, reason)); + } + + + /// + /// 是否包含内部调用验证信息 + /// + /// + /// + public static bool HasInternalApiAuthInfo(this AuthorizationFilterContext context) + { + if (!context.HttpContext.Request.Headers.ContainsKey("timestamp") + || !context.HttpContext.Request.Headers.ContainsKey("randomstr") + || !context.HttpContext.Request.Headers.ContainsKey("internalsign")) + { + return false; + } + + return true; + } + + /// + /// 是否包含第三方开放验证信息 + /// + /// + /// + public static bool HasOpenApiAuthInfo(this AuthorizationFilterContext context) + { + if (!context.HttpContext.Request.Headers.ContainsKey("timestamp") + || !context.HttpContext.Request.Headers.ContainsKey("randomstr") + || !context.HttpContext.Request.Headers.ContainsKey("appid") + || !context.HttpContext.Request.Headers.ContainsKey("sign")) + { + return false; + } + + return true; + } + + /// + /// 是否包含token验证信息 + /// + /// + /// + public static bool HasTokenAuthInfo(this AuthorizationFilterContext context) + { + if (!context.HttpContext.Request.Headers.ContainsKey("token")&&!context.HttpContext.Request.Cookies.ContainsKey("token")) + { + return false; + } + + return true; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/InternalApiAuthAttribute.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/InternalApiAuthAttribute.cs index 3cabb43..85ffe68 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/InternalApiAuthAttribute.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/InternalApiAuthAttribute.cs @@ -1,70 +1,70 @@ -using System; -using System.Linq; -using System.Net.Http; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace Hncore.Infrastructure.WebApi -{ - public class InternalApiAuthAttribute : AuthBase,IOrderedFilter - { - public int Order =>0; - - public override void OnAuthorization(AuthorizationFilterContext context) - { - - if (context.AllowAnonymous() - || context.HasPassed() - || !context.HasInternalApiAuthInfo()) - { - return; - } - - long.TryParse(context.HttpContext.Request.Headers["timestamp"], out long timestamp); - string randomstr = context.HttpContext.Request.Headers["randomstr"]; - string sign = context.HttpContext.Request.Headers["internalsign"]; - - if (EnvironmentVariableHelper.IsAspNetCoreProduction) - { - long secondDiff = DateTimeHelper.ToUnixTimestamp(DateTime.Now) - timestamp; - - if (secondDiff > 600 || secondDiff < -600) - { - context.Reject("时间戳已过期"); - } - } - - - if (!String.Equals(sign, AuthBase.CreateInternalApiSign(timestamp, randomstr) - , StringComparison.CurrentCultureIgnoreCase)) - { - context.Reject("签名错误"); - } - - context.SetPassed("InternalApiAuth"); - } - } - - public static class InternalApiAuthExt - { - public static HttpClient CreateInternalAuthClient(this IHttpClientFactory httpClientFactory) - { - var httpclient = httpClientFactory.CreateClient(TimeSpan.FromSeconds(10)); - - long timestamp = DateTimeHelper.ToUnixTimestamp(DateTime.Now); - string randomstr = Guid.NewGuid().ToString(); - - var sign = AuthBase.CreateInternalApiSign(timestamp, randomstr); - - httpclient.DefaultRequestHeaders.Add("timestamp", timestamp.ToString()); - httpclient.DefaultRequestHeaders.Add("randomstr", randomstr); - httpclient.DefaultRequestHeaders.Add("internalsign", sign); - - return httpclient; - } - } +using System; +using System.Linq; +using System.Net.Http; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Hncore.Infrastructure.WebApi +{ + public class InternalApiAuthAttribute : AuthBase,IOrderedFilter + { + public int Order =>0; + + public override void OnAuthorization(AuthorizationFilterContext context) + { + + if (context.AllowAnonymous() + || context.HasPassed() + || !context.HasInternalApiAuthInfo()) + { + return; + } + + long.TryParse(context.HttpContext.Request.Headers["timestamp"], out long timestamp); + string randomstr = context.HttpContext.Request.Headers["randomstr"]; + string sign = context.HttpContext.Request.Headers["internalsign"]; + + if (EnvironmentVariableHelper.IsAspNetCoreProduction) + { + long secondDiff = DateTimeHelper.ToUnixTimestamp(DateTime.Now) - timestamp; + + if (secondDiff > 600 || secondDiff < -600) + { + context.Reject("时间戳已过期"); + } + } + + + if (!String.Equals(sign, AuthBase.CreateInternalApiSign(timestamp, randomstr) + , StringComparison.CurrentCultureIgnoreCase)) + { + context.Reject("签名错误"); + } + + context.SetPassed("InternalApiAuth"); + } + } + + public static class InternalApiAuthExt + { + public static HttpClient CreateInternalAuthClient(this IHttpClientFactory httpClientFactory) + { + var httpclient = httpClientFactory.CreateClient(TimeSpan.FromSeconds(10)); + + long timestamp = DateTimeHelper.ToUnixTimestamp(DateTime.Now); + string randomstr = Guid.NewGuid().ToString(); + + var sign = AuthBase.CreateInternalApiSign(timestamp, randomstr); + + httpclient.DefaultRequestHeaders.Add("timestamp", timestamp.ToString()); + httpclient.DefaultRequestHeaders.Add("randomstr", randomstr); + httpclient.DefaultRequestHeaders.Add("internalsign", sign); + + return httpclient; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/LimitQosAttribute.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/LimitQosAttribute.cs index da0a5a3..ef07bb3 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/LimitQosAttribute.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/LimitQosAttribute.cs @@ -1,88 +1,88 @@ -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Core.Web; -using Microsoft.AspNetCore.Mvc.Filters; -using System; -using System.Linq; - -namespace Hncore.Infrastructure.WebApi.Filter -{ - public enum LimitDimension - { - Ip, - TenantId, - AppId - } - public class LimitQosAttribute : AuthBase - { - private int _timeWindow; - private int _count; - private LimitDimension[] _dimensions; - - public LimitQosAttribute(int timeWindow, int count, params LimitDimension[] dimensions) - { - _timeWindow = timeWindow; - _count = count; - _dimensions = dimensions; - } - - public override void OnAuthorization(AuthorizationFilterContext context) - { - string url = context.HttpContext.Request.Path.ToString().ToLower(); - - string cacheKey = $"limitqos:{url}"; - - if (_dimensions.Contains(LimitDimension.Ip)) - { - string ip = context.HttpContext.GetUserIp(); - cacheKey += $":{ip}"; - } - - if (_dimensions.Contains(LimitDimension.TenantId)) - { - var mangeInfo = context.HttpContext.Request.GetManageUserInfo(); - - if (mangeInfo == null) - { - return; - } - - cacheKey += $":{mangeInfo.TenantId}"; - } - - if (_dimensions.Contains(LimitDimension.AppId)) - { - if (!context.HttpContext.Items.ContainsKey("OpenAppId")) - { - return; - } - - cacheKey += $":{context.HttpContext.Items["OpenAppId"]}"; - } - - var luaScript = $"return redis.call('CL.THROTTLE','{cacheKey}','{_count}','{_count}','{_timeWindow}','1')"; - - try - { - var res = RedisHelper.Eval(luaScript, cacheKey); - - if (res is Array) - { - var arr = res as Array; - - if (arr.Length == 5) - { - if (Convert.ToInt32(arr.GetValue(0)) != 0) - { - context.Reject($"超出{_timeWindow}秒{_count}次的请求限制"); - } - } - } - } - catch (Exception e) - { - LogHelper.Error("reids异常", e); - } - } - } - +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Core.Web; +using Microsoft.AspNetCore.Mvc.Filters; +using System; +using System.Linq; + +namespace Hncore.Infrastructure.WebApi.Filter +{ + public enum LimitDimension + { + Ip, + TenantId, + AppId + } + public class LimitQosAttribute : AuthBase + { + private int _timeWindow; + private int _count; + private LimitDimension[] _dimensions; + + public LimitQosAttribute(int timeWindow, int count, params LimitDimension[] dimensions) + { + _timeWindow = timeWindow; + _count = count; + _dimensions = dimensions; + } + + public override void OnAuthorization(AuthorizationFilterContext context) + { + string url = context.HttpContext.Request.Path.ToString().ToLower(); + + string cacheKey = $"limitqos:{url}"; + + if (_dimensions.Contains(LimitDimension.Ip)) + { + string ip = context.HttpContext.GetUserIp(); + cacheKey += $":{ip}"; + } + + if (_dimensions.Contains(LimitDimension.TenantId)) + { + var mangeInfo = context.HttpContext.Request.GetManageUserInfo(); + + if (mangeInfo == null) + { + return; + } + + cacheKey += $":{mangeInfo.TenantId}"; + } + + if (_dimensions.Contains(LimitDimension.AppId)) + { + if (!context.HttpContext.Items.ContainsKey("OpenAppId")) + { + return; + } + + cacheKey += $":{context.HttpContext.Items["OpenAppId"]}"; + } + + var luaScript = $"return redis.call('CL.THROTTLE','{cacheKey}','{_count}','{_count}','{_timeWindow}','1')"; + + try + { + var res = RedisHelper.Eval(luaScript, cacheKey); + + if (res is Array) + { + var arr = res as Array; + + if (arr.Length == 5) + { + if (Convert.ToInt32(arr.GetValue(0)) != 0) + { + context.Reject($"超出{_timeWindow}秒{_count}次的请求限制"); + } + } + } + } + catch (Exception e) + { + LogHelper.Error("reids异常", e); + } + } + } + } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/ManageAuthAttribute.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/ManageAuthAttribute.cs index ecb883f..225fded 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/ManageAuthAttribute.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/ManageAuthAttribute.cs @@ -1,148 +1,148 @@ -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using JWT; -using JWT.Serializers; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; -using System; -using System.Net.Http; - - -namespace Hncore.Infrastructure.WebApi -{ - public class ManageAuthAttribute : AuthBase, IOrderedFilter - { - public int Order =>1; - - public override void OnAuthorization(AuthorizationFilterContext context) - { - - if (context.AllowAnonymous() - || context.HasPassed() - || !context.HasTokenAuthInfo()) - { - return; - } - - if (context.HttpContext.Request.GetManageUserInfo() == null) - { - context.Reject(); - } - - context.SetPassed("ManageAuth"); - } - } - - public class ManageUserInfo - { - [JsonProperty("LoginName")] public string LoginName { get; set; } - - [JsonProperty("RoleName")] public string RoleName { get; set; } - - [JsonProperty("OperaterID")] public int OperaterId { get; set; } - - [JsonProperty("TenantId")] public int TenantId { get; set; } - - [JsonProperty("DataDomain")] public int StoreId { get; set; } - - [JsonProperty("exp")] public long ExpiredTimestamp { get; set; } - - [JsonProperty("iat")] public long IssueTimestamp { get; set; } - [JsonProperty("OpenId")] public string OpenId { get; set; } - } - - public static class HttpRequestExt - { - private static string _secret = "etor_yh_lzh_20f_2020_YES"; - - public static void SetManageUserInfo(this HttpRequest request, ManageUserInfo manageUserInfo) - { - request.HttpContext.Items["ManageUserInfo"] = manageUserInfo; - } - - public static ManageUserInfo GetManageUserInfo(this HttpRequest request) - { - if (!request.Headers.ContainsKey("token")) - { - return null; - } - - if (request.HttpContext.Items.ContainsKey("ManageUserInfo")) - { - return request.HttpContext.Items["ManageUserInfo"] as ManageUserInfo; - } - - string token = request.Headers["token"]; - - string storeId = request.Headers["sid"]; - - string payload = string.Empty; - - try - { - IJsonSerializer serializer = new JsonNetSerializer(); - IDateTimeProvider provider = new UtcDateTimeProvider(); - IJwtValidator validator = new JwtValidator(serializer, provider); - IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); - IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); - payload = decoder.Decode(token, _secret, verify: true); - - if (string.IsNullOrEmpty(payload)) - { - return null; - } - - try - { - ManageUserInfo manageUserInfo = payload.FromJsonTo(); - - if (manageUserInfo == null || manageUserInfo.TenantId == 0) - { - return null; - } - - if (manageUserInfo.IssueTimestamp == 0 - || DateTimeHelper.UnixTimeStampToDateTime(manageUserInfo.IssueTimestamp) < - DateTime.Now.AddHours(-4)) - { - return null; - } - - if(storeId.Has()) - { - manageUserInfo.StoreId = Convert.ToInt32(storeId); - } - - request.SetManageUserInfo(manageUserInfo); - - return manageUserInfo; - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - return null; - } - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - return null; - } - } - - public static HttpClient CreateManageAuthHttpClient(this HttpRequest request) - { - var httpclient = request.HttpContext.RequestServices - .GetService() - .CreateClient(TimeSpan.FromMinutes(1)); - - httpclient.DefaultRequestHeaders.Add("token", request.Headers["token"].ToString()); - httpclient.DefaultRequestHeaders.Add("sid", request.Headers["sid"].ToString()); - - return httpclient; - } - } +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using JWT; +using JWT.Serializers; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using System; +using System.Net.Http; + + +namespace Hncore.Infrastructure.WebApi +{ + public class ManageAuthAttribute : AuthBase, IOrderedFilter + { + public int Order =>1; + + public override void OnAuthorization(AuthorizationFilterContext context) + { + + if (context.AllowAnonymous() + || context.HasPassed() + || !context.HasTokenAuthInfo()) + { + return; + } + + if (context.HttpContext.Request.GetManageUserInfo() == null) + { + context.Reject(); + } + + context.SetPassed("ManageAuth"); + } + } + + public class ManageUserInfo + { + [JsonProperty("LoginName")] public string LoginName { get; set; } + + [JsonProperty("RoleName")] public string RoleName { get; set; } + + [JsonProperty("OperaterID")] public int OperaterId { get; set; } + + [JsonProperty("TenantId")] public int TenantId { get; set; } + + [JsonProperty("DataDomain")] public int StoreId { get; set; } + + [JsonProperty("exp")] public long ExpiredTimestamp { get; set; } + + [JsonProperty("iat")] public long IssueTimestamp { get; set; } + [JsonProperty("OpenId")] public string OpenId { get; set; } + } + + public static class HttpRequestExt + { + private static string _secret = "etor_yh_lzh_20f_2020_YES"; + + public static void SetManageUserInfo(this HttpRequest request, ManageUserInfo manageUserInfo) + { + request.HttpContext.Items["ManageUserInfo"] = manageUserInfo; + } + + public static ManageUserInfo GetManageUserInfo(this HttpRequest request) + { + if (!request.Headers.ContainsKey("token")) + { + return null; + } + + if (request.HttpContext.Items.ContainsKey("ManageUserInfo")) + { + return request.HttpContext.Items["ManageUserInfo"] as ManageUserInfo; + } + + string token = request.Headers["token"]; + + string storeId = request.Headers["sid"]; + + string payload = string.Empty; + + try + { + IJsonSerializer serializer = new JsonNetSerializer(); + IDateTimeProvider provider = new UtcDateTimeProvider(); + IJwtValidator validator = new JwtValidator(serializer, provider); + IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); + IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); + payload = decoder.Decode(token, _secret, verify: true); + + if (string.IsNullOrEmpty(payload)) + { + return null; + } + + try + { + ManageUserInfo manageUserInfo = payload.FromJsonTo(); + + if (manageUserInfo == null || manageUserInfo.TenantId == 0) + { + return null; + } + + if (manageUserInfo.IssueTimestamp == 0 + || DateTimeHelper.UnixTimeStampToDateTime(manageUserInfo.IssueTimestamp) < + DateTime.Now.AddHours(-4)) + { + return null; + } + + if(storeId.Has()) + { + manageUserInfo.StoreId = Convert.ToInt32(storeId); + } + + request.SetManageUserInfo(manageUserInfo); + + return manageUserInfo; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return null; + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return null; + } + } + + public static HttpClient CreateManageAuthHttpClient(this HttpRequest request) + { + var httpclient = request.HttpContext.RequestServices + .GetService() + .CreateClient(TimeSpan.FromMinutes(1)); + + httpclient.DefaultRequestHeaders.Add("token", request.Headers["token"].ToString()); + httpclient.DefaultRequestHeaders.Add("sid", request.Headers["sid"].ToString()); + + return httpclient; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/UserAuthAttribute.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/UserAuthAttribute.cs index 157bfaf..4281e3f 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/UserAuthAttribute.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/Auth/UserAuthAttribute.cs @@ -1,166 +1,166 @@ -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using JWT; -using JWT.Serializers; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using System; -using System.Net.Http; - - -namespace Hncore.Infrastructure.WebApi -{ - public class UserAuthAttribute : AuthBase, IOrderedFilter - { - public int Order => 0; - - public override void OnAuthorization(AuthorizationFilterContext context) - { - - if (context.HasPassed())//context.AllowAnonymous()|| - - { - return; - } - - if (context.HttpContext.Request.GetUserInfo() == null) - { - // context.Reject(); - context.HttpContext.Response.Cookies.Delete("token"); - context.HttpContext.Response.Cookies.Delete("userInfo"); - - var userAgent = context.HttpContext.Request.Headers[HeaderNames.UserAgent].ToString().ToLower(); - if (userAgent.IndexOf("micromessenger") == -1) - { - context.RejectToRedirect("/User/WebLogin"); - } - else - { - var url = context.HttpContext.Request.GetUrl().UrlEncode(); - context.RejectToRedirect($"/User/MP_GetUserInfo?appid=wx18e5b4f42773c3ec&callbakUrl={url}"); - } - } - - context.SetPassed("UserAuth"); - } - } - - public class AppUserInfo - { - [JsonProperty("LoginName")] public string LoginName { get; set; } - - [JsonProperty("Name")] public string Name { get; set; } - - [JsonProperty("RoleName")] public string RoleName { get; set; } - - [JsonProperty("UserId")] public int UserId { get; set; } - - [JsonProperty("TenantId")] public int TenantId { get; set; } - - [JsonProperty("DataDomain")] public int[] DataDomain { get; set; } - - [JsonProperty("exp")] public long ExpiredTimestamp { get; set; } - - [JsonProperty("iat")] public long IssueTimestamp { get; set; } - [JsonProperty("OpenId")] public string OpenId { get; set; } - [JsonProperty("AppType")] public string AppType { get; set; } - [JsonProperty("AppId")] public string AppId { get; set; } - [JsonProperty("StoreId")] public int StoreId { get; set; } - } - - public static class HttpRequestExt1 - { - private static string _secret = "hncore_yh_lzh_20f_2020_READY"; - - public static void SetUserInfo(this HttpRequest request, AppUserInfo manageUserInfo) - { - request.HttpContext.Items["UserInfo"] = manageUserInfo; - } - - public static AppUserInfo GetUserInfo(this HttpRequest request) - { - if (!request.Headers.ContainsKey("token")&& !request.Cookies.ContainsKey("token")) - { - return null; - } - - if (request.HttpContext.Items.ContainsKey("UserInfo")) - { - return request.HttpContext.Items["UserInfo"] as AppUserInfo; - } - - if(!request.Cookies.TryGetValue("token",out string token)) - { - token = request.Headers["token"]; - } - string payload = string.Empty; - - try - { - IJsonSerializer serializer = new JsonNetSerializer(); - IDateTimeProvider provider = new UtcDateTimeProvider(); - IJwtValidator validator = new JwtValidator(serializer, provider); - IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); - IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); - payload = decoder.Decode(token, _secret, verify: true); - - if (string.IsNullOrEmpty(payload)) - { - request.HttpContext.Response.Cookies.Delete("token"); - request.HttpContext.Response.Cookies.Delete("userInfo"); - return null; - } - - try - { - AppUserInfo manageUserInfo = payload.FromJsonTo(); - - if (manageUserInfo == null) - { - return null; - } - - if (manageUserInfo.IssueTimestamp == 0 - || DateTimeHelper.UnixTimeStampToDateTime(manageUserInfo.IssueTimestamp) < - DateTime.Now.AddHours(-4)) - { - return null; - } - - request.SetUserInfo(manageUserInfo); - - return manageUserInfo; - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - request.HttpContext.Response.Cookies.Delete("token"); - request.HttpContext.Response.Cookies.Delete("userInfo"); - return null; - } - } - catch(Exception ex) - { - Console.WriteLine(ex.Message); - request.HttpContext.Response.Cookies.Delete("token"); - request.HttpContext.Response.Cookies.Delete("userInfo"); - return null; - } - } - - public static HttpClient CreateManageAuthHttpClient(this HttpRequest request) - { - var httpclient = request.HttpContext.RequestServices - .GetService() - .CreateClient(TimeSpan.FromMinutes(1)); - - httpclient.DefaultRequestHeaders.Add("token", request.Headers["token"].ToString()); - - return httpclient; - } - } +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using JWT; +using JWT.Serializers; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using System; +using System.Net.Http; + + +namespace Hncore.Infrastructure.WebApi +{ + public class UserAuthAttribute : AuthBase, IOrderedFilter + { + public int Order => 0; + + public override void OnAuthorization(AuthorizationFilterContext context) + { + + if (context.HasPassed())//context.AllowAnonymous()|| + + { + return; + } + + if (context.HttpContext.Request.GetUserInfo() == null) + { + // context.Reject(); + context.HttpContext.Response.Cookies.Delete("token"); + context.HttpContext.Response.Cookies.Delete("userInfo"); + + var userAgent = context.HttpContext.Request.Headers[HeaderNames.UserAgent].ToString().ToLower(); + if (userAgent.IndexOf("micromessenger") == -1) + { + context.RejectToRedirect("/User/WebLogin"); + } + else + { + var url = context.HttpContext.Request.GetUrl().UrlEncode(); + context.RejectToRedirect($"/User/MP_GetUserInfo?appid=wx18e5b4f42773c3ec&callbakUrl={url}"); + } + } + + context.SetPassed("UserAuth"); + } + } + + public class AppUserInfo + { + [JsonProperty("LoginName")] public string LoginName { get; set; } + + [JsonProperty("Name")] public string Name { get; set; } + + [JsonProperty("RoleName")] public string RoleName { get; set; } + + [JsonProperty("UserId")] public int UserId { get; set; } + + [JsonProperty("TenantId")] public int TenantId { get; set; } + + [JsonProperty("DataDomain")] public int[] DataDomain { get; set; } + + [JsonProperty("exp")] public long ExpiredTimestamp { get; set; } + + [JsonProperty("iat")] public long IssueTimestamp { get; set; } + [JsonProperty("OpenId")] public string OpenId { get; set; } + [JsonProperty("AppType")] public string AppType { get; set; } + [JsonProperty("AppId")] public string AppId { get; set; } + [JsonProperty("StoreId")] public int StoreId { get; set; } + } + + public static class HttpRequestExt1 + { + private static string _secret = "hncore_yh_lzh_20f_2020_READY"; + + public static void SetUserInfo(this HttpRequest request, AppUserInfo manageUserInfo) + { + request.HttpContext.Items["UserInfo"] = manageUserInfo; + } + + public static AppUserInfo GetUserInfo(this HttpRequest request) + { + if (!request.Headers.ContainsKey("token")&& !request.Cookies.ContainsKey("token")) + { + return null; + } + + if (request.HttpContext.Items.ContainsKey("UserInfo")) + { + return request.HttpContext.Items["UserInfo"] as AppUserInfo; + } + + if(!request.Cookies.TryGetValue("token",out string token)) + { + token = request.Headers["token"]; + } + string payload = string.Empty; + + try + { + IJsonSerializer serializer = new JsonNetSerializer(); + IDateTimeProvider provider = new UtcDateTimeProvider(); + IJwtValidator validator = new JwtValidator(serializer, provider); + IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); + IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); + payload = decoder.Decode(token, _secret, verify: true); + + if (string.IsNullOrEmpty(payload)) + { + request.HttpContext.Response.Cookies.Delete("token"); + request.HttpContext.Response.Cookies.Delete("userInfo"); + return null; + } + + try + { + AppUserInfo manageUserInfo = payload.FromJsonTo(); + + if (manageUserInfo == null) + { + return null; + } + + if (manageUserInfo.IssueTimestamp == 0 + || DateTimeHelper.UnixTimeStampToDateTime(manageUserInfo.IssueTimestamp) < + DateTime.Now.AddHours(-4)) + { + return null; + } + + request.SetUserInfo(manageUserInfo); + + return manageUserInfo; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + request.HttpContext.Response.Cookies.Delete("token"); + request.HttpContext.Response.Cookies.Delete("userInfo"); + return null; + } + } + catch(Exception ex) + { + Console.WriteLine(ex.Message); + request.HttpContext.Response.Cookies.Delete("token"); + request.HttpContext.Response.Cookies.Delete("userInfo"); + return null; + } + } + + public static HttpClient CreateManageAuthHttpClient(this HttpRequest request) + { + var httpclient = request.HttpContext.RequestServices + .GetService() + .CreateClient(TimeSpan.FromMinutes(1)); + + httpclient.DefaultRequestHeaders.Add("token", request.Headers["token"].ToString()); + + return httpclient; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerAddEnumDescriptionsDocumentFilter.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerAddEnumDescriptionsDocumentFilter.cs index eee11d8..aaba937 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerAddEnumDescriptionsDocumentFilter.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerAddEnumDescriptionsDocumentFilter.cs @@ -1,106 +1,106 @@ -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Hncore.Infrastructure.WebApi.Filter -{ - public class SwaggerAddEnumDescriptionsDocumentFilter : IDocumentFilter - { - public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) - { - // add enum descriptions to result models - // 将枚举加到返回对象的描述中,json.definitions对象里的枚举 - foreach (KeyValuePair schemaDictionaryItem in swaggerDoc.Definitions) - { - Schema schema = schemaDictionaryItem.Value; - foreach (KeyValuePair propertyDictionaryItem in schema.Properties) - { - Schema property = propertyDictionaryItem.Value; - IList propertyEnums = property.Enum; - if (propertyEnums != null && propertyEnums.Count > 0) - { - property.Description += DescribeEnum(propertyEnums); - } - } - } - - // add enum descriptions to input parameters - if (swaggerDoc.Paths.Count > 0) - { - foreach (PathItem pathItem in swaggerDoc.Paths.Values) - { - DescribeEnumParameters(pathItem.Parameters); - // head, patch, options, delete left out - List possibleParameterisedOperations = new List { pathItem.Get, pathItem.Post, pathItem.Put }; - possibleParameterisedOperations.FindAll(x => x != null).ForEach(x => DescribeEnumParameters(x.Parameters)); - } - } - } - - private void DescribeEnumParameters(IList parameters) - { - if (parameters != null) - { - foreach (var param in parameters) - { - if (param.In == "path") - { - var nonParam = (NonBodyParameter)param; - IList paramEnums = nonParam.Enum; - if (paramEnums != null && paramEnums.Count > 0) - { - param.Description +=":"+ DescribeEnum(paramEnums); - } - } - if (param.In == "body") - { - var bodyParam = (BodyParameter)param; - Schema property = bodyParam.Schema; - IList propertyEnums = property.Enum; - if (propertyEnums != null && propertyEnums.Count > 0) - { - property.Description += ":" + DescribeEnum(propertyEnums); - } - } - if (param.In == "query") - { - var nonParam = (NonBodyParameter)param; - IList paramEnums = nonParam.Enum; - if (paramEnums != null && paramEnums.Count > 0) - { - param.Description += ":" + DescribeEnum(paramEnums); - } - } - } - } - } - /// - /// 枚举转换成值和描述 - /// - /// - /// - private string DescribeEnum(IList enums) - { - List enumDescriptions = new List(); - foreach (object item in enums) - { - var type = item.GetType(); - var objArr = type.GetField(item.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true); - if (objArr != null && objArr.Length > 0) - { - DisplayAttribute da = objArr[0] as DisplayAttribute; - enumDescriptions.Add($"{(int)item} {da.Name}"); - } - else - { - enumDescriptions.Add(string.Format("{0} = {1}", (int)item, Enum.GetName(item.GetType(), item))); - } - } - return string.Join(", ", enumDescriptions.ToArray()); - } - - } -} +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Hncore.Infrastructure.WebApi.Filter +{ + public class SwaggerAddEnumDescriptionsDocumentFilter : IDocumentFilter + { + public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) + { + // add enum descriptions to result models + // 将枚举加到返回对象的描述中,json.definitions对象里的枚举 + foreach (KeyValuePair schemaDictionaryItem in swaggerDoc.Definitions) + { + Schema schema = schemaDictionaryItem.Value; + foreach (KeyValuePair propertyDictionaryItem in schema.Properties) + { + Schema property = propertyDictionaryItem.Value; + IList propertyEnums = property.Enum; + if (propertyEnums != null && propertyEnums.Count > 0) + { + property.Description += DescribeEnum(propertyEnums); + } + } + } + + // add enum descriptions to input parameters + if (swaggerDoc.Paths.Count > 0) + { + foreach (PathItem pathItem in swaggerDoc.Paths.Values) + { + DescribeEnumParameters(pathItem.Parameters); + // head, patch, options, delete left out + List possibleParameterisedOperations = new List { pathItem.Get, pathItem.Post, pathItem.Put }; + possibleParameterisedOperations.FindAll(x => x != null).ForEach(x => DescribeEnumParameters(x.Parameters)); + } + } + } + + private void DescribeEnumParameters(IList parameters) + { + if (parameters != null) + { + foreach (var param in parameters) + { + if (param.In == "path") + { + var nonParam = (NonBodyParameter)param; + IList paramEnums = nonParam.Enum; + if (paramEnums != null && paramEnums.Count > 0) + { + param.Description +=":"+ DescribeEnum(paramEnums); + } + } + if (param.In == "body") + { + var bodyParam = (BodyParameter)param; + Schema property = bodyParam.Schema; + IList propertyEnums = property.Enum; + if (propertyEnums != null && propertyEnums.Count > 0) + { + property.Description += ":" + DescribeEnum(propertyEnums); + } + } + if (param.In == "query") + { + var nonParam = (NonBodyParameter)param; + IList paramEnums = nonParam.Enum; + if (paramEnums != null && paramEnums.Count > 0) + { + param.Description += ":" + DescribeEnum(paramEnums); + } + } + } + } + } + /// + /// 枚举转换成值和描述 + /// + /// + /// + private string DescribeEnum(IList enums) + { + List enumDescriptions = new List(); + foreach (object item in enums) + { + var type = item.GetType(); + var objArr = type.GetField(item.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true); + if (objArr != null && objArr.Length > 0) + { + DisplayAttribute da = objArr[0] as DisplayAttribute; + enumDescriptions.Add($"{(int)item} {da.Name}"); + } + else + { + enumDescriptions.Add(string.Format("{0} = {1}", (int)item, Enum.GetName(item.GetType(), item))); + } + } + return string.Join(", ", enumDescriptions.ToArray()); + } + + } +} diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerOperationFilter.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerOperationFilter.cs index ce2cddb..48f003a 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerOperationFilter.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/SwaggerOperationFilter.cs @@ -1,53 +1,53 @@ -using Microsoft.AspNetCore.JsonPatch.Operations; -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.ComponentModel.DataAnnotations; -using System.Linq; - -namespace Hncore.Infrastructure.WebApi.Filter -{ - public class SwaggerOperationFilter : IOperationFilter - { - /// - /// 应用过滤器 - /// - /// - /// - public void Apply(Swashbuckle.AspNetCore.Swagger.Operation operation, OperationFilterContext context) - { - #region Swagger版本描述处理 - - foreach (var parameter in operation.Parameters.OfType()) - { - //var description = context.ApiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); - if (parameter.Name == "version") - { - parameter.Description = "填写版本号如:1、2"; - parameter.Default = context.ApiDescription.GroupName.Replace("v", ""); - } - - //if (parameter.Enum!=null&¶meter.Enum.Count > 0) - //{ - // string desc = ""; - // foreach(var item in parameter.Enum) - // { - // var type = item.GetType(); - // var objArr=type.GetField(item.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true); - // if (objArr != null && objArr.Length > 0) - // { - // DisplayAttribute da = objArr[0] as DisplayAttribute; - // desc += $"{item} {da.Name}"; - // } - // } - // parameter.Description = desc; - //} - } - - #endregion - - var auth = context.MethodInfo - .GetCustomAttributes(true) - .OfType(); - } - } +using Microsoft.AspNetCore.JsonPatch.Operations; +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; +using System.ComponentModel.DataAnnotations; +using System.Linq; + +namespace Hncore.Infrastructure.WebApi.Filter +{ + public class SwaggerOperationFilter : IOperationFilter + { + /// + /// 应用过滤器 + /// + /// + /// + public void Apply(Swashbuckle.AspNetCore.Swagger.Operation operation, OperationFilterContext context) + { + #region Swagger版本描述处理 + + foreach (var parameter in operation.Parameters.OfType()) + { + //var description = context.ApiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); + if (parameter.Name == "version") + { + parameter.Description = "填写版本号如:1、2"; + parameter.Default = context.ApiDescription.GroupName.Replace("v", ""); + } + + //if (parameter.Enum!=null&¶meter.Enum.Count > 0) + //{ + // string desc = ""; + // foreach(var item in parameter.Enum) + // { + // var type = item.GetType(); + // var objArr=type.GetField(item.ToString()).GetCustomAttributes(typeof(DisplayAttribute), true); + // if (objArr != null && objArr.Length > 0) + // { + // DisplayAttribute da = objArr[0] as DisplayAttribute; + // desc += $"{item} {da.Name}"; + // } + // } + // parameter.Description = desc; + //} + } + + #endregion + + var auth = context.MethodInfo + .GetCustomAttributes(true) + .OfType(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/ValidateModelAttribute.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/ValidateModelAttribute.cs index dacd7aa..617708d 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Filter/ValidateModelAttribute.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Filter/ValidateModelAttribute.cs @@ -1,46 +1,46 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using System; -using System.Collections.Generic; -using System.Net; -using System.Text; - -namespace Hncore.Infrastructure.WebApi.Filter -{ - public class ValidateModelAttribute : ActionFilterAttribute - { - /// - /// netcore 会自动判断context.ModelState.IsValid - /// 如果是false,自动返回BadRequestObjectResult - /// 需要在OnResultExecuting重写返回值 - /// - /// - public override void OnActionExecuting(ActionExecutingContext context) - { - //var httpContext = context.HttpContext; - //if (context.ModelState.IsValid == false) - //{ - // context.Result = new JsonResult(context.ModelState); - // //httpContext.Response = httpContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, context.ModelState); - //} - } - public override void OnResultExecuting(ResultExecutingContext context) - { - var httpContext = context.HttpContext; - if (context.ModelState.IsValid == false) - { - var message = new List(); ; - foreach(var item in context.ModelState.Values) - { - foreach (var error in item.Errors) - { - message.Add( error.ErrorMessage ); - } - } - var apiRes = new ApiResult(ResultCode.C_PARAM_ERROR, string.Join("|", message)); - context.Result = new JsonResult(apiRes); - //httpContext.Response = httpContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, context.ModelState); - } - } - } -} +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Hncore.Infrastructure.WebApi.Filter +{ + public class ValidateModelAttribute : ActionFilterAttribute + { + /// + /// netcore 会自动判断context.ModelState.IsValid + /// 如果是false,自动返回BadRequestObjectResult + /// 需要在OnResultExecuting重写返回值 + /// + /// + public override void OnActionExecuting(ActionExecutingContext context) + { + //var httpContext = context.HttpContext; + //if (context.ModelState.IsValid == false) + //{ + // context.Result = new JsonResult(context.ModelState); + // //httpContext.Response = httpContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, context.ModelState); + //} + } + public override void OnResultExecuting(ResultExecutingContext context) + { + var httpContext = context.HttpContext; + if (context.ModelState.IsValid == false) + { + var message = new List(); ; + foreach(var item in context.ModelState.Values) + { + foreach (var error in item.Errors) + { + message.Add( error.ErrorMessage ); + } + } + var apiRes = new ApiResult(ResultCode.C_PARAM_ERROR, string.Join("|", message)); + context.Result = new JsonResult(apiRes); + //httpContext.Response = httpContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, context.ModelState); + } + } + } +} diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/GlobalData.cs b/Infrastructure/Hncore.Infrastructure/WebApi/GlobalData.cs index 50e7078..cc8cbb8 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/GlobalData.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/GlobalData.cs @@ -1,7 +1,7 @@ -namespace Hncore.Infrastructure.WebApi -{ - internal class GlobalData - { - public static bool UseGlobalManageAuthFilter { get; set; } - } +namespace Hncore.Infrastructure.WebApi +{ + internal class GlobalData + { + public static bool UseGlobalManageAuthFilter { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/Middleware/ErrorHandlingMiddleware.cs b/Infrastructure/Hncore.Infrastructure/WebApi/Middleware/ErrorHandlingMiddleware.cs index d37f138..92313b5 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/Middleware/ErrorHandlingMiddleware.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/Middleware/ErrorHandlingMiddleware.cs @@ -1,103 +1,103 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Data; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.OpenApi; -using Hncore.Infrastructure.Serializer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Hncore.Infrastructure.Core.Web; - - -namespace Hncore.Infrastructure.WebApi -{ - /// - /// 统一错误异常处理中间件类 - /// - /// - public class ErrorHandlingMiddleware - { - private readonly RequestDelegate next; - - public ErrorHandlingMiddleware(RequestDelegate next) - { - this.next = next; - } - - public async Task Invoke(HttpContext context) - { - try - { - await next(context); - } - catch (Exception ex) - { - string requestMsg = "请求URL:" + context.Request.GetAbsoluteUri() + ""; - - requestMsg += "\nMethod:" + context.Request.Method + "\n"; - - if (context.Request.Method.ToLower() != "get") - { - var requestBody = await context.Request.ReadBodyAsStringAsync(); - requestMsg += "Body:\n" + requestBody + - "\n------------------------\n"; - } - else - { - requestMsg += "\n------------------------\n"; - } - - - await HandleExceptionAsync(context, ex, requestMsg); - } - } - - - private static Task HandleExceptionAsync(HttpContext context, Exception ex, - string requestMsg) - { - ResultCode code = ResultCode.C_UNKNOWN_ERROR; - string msg = ""; - - if (ex is BusinessException bex) - { - code = bex.Code; - msg = bex.Message; - - LogHelper.Error($"业务异常,{msg}", requestMsg + ex); - } - else - { - if (EnvironmentVariableHelper.IsAspNetCoreProduction) - { - msg = "系统繁忙,请稍后再试"; - } - else - { - msg = ex.Message; - } - - LogHelper.Error($"未知异常,{ex.Message}", requestMsg + ex); - } - - var data = new ApiResult(code, msg); - - var result = data.ToJson(); - - context.Response.ContentType = "application/json;charset=utf-8"; - - return context.Response.WriteAsync(result); - } - } - - public static class ErrorHandlingExtensions - { - public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder) - { - return builder.UseMiddleware(); - } - } +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Data; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.OpenApi; +using Hncore.Infrastructure.Serializer; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Hncore.Infrastructure.Core.Web; + + +namespace Hncore.Infrastructure.WebApi +{ + /// + /// 统一错误异常处理中间件类 + /// + /// + public class ErrorHandlingMiddleware + { + private readonly RequestDelegate next; + + public ErrorHandlingMiddleware(RequestDelegate next) + { + this.next = next; + } + + public async Task Invoke(HttpContext context) + { + try + { + await next(context); + } + catch (Exception ex) + { + string requestMsg = "请求URL:" + context.Request.GetAbsoluteUri() + ""; + + requestMsg += "\nMethod:" + context.Request.Method + "\n"; + + if (context.Request.Method.ToLower() != "get") + { + var requestBody = await context.Request.ReadBodyAsStringAsync(); + requestMsg += "Body:\n" + requestBody + + "\n------------------------\n"; + } + else + { + requestMsg += "\n------------------------\n"; + } + + + await HandleExceptionAsync(context, ex, requestMsg); + } + } + + + private static Task HandleExceptionAsync(HttpContext context, Exception ex, + string requestMsg) + { + ResultCode code = ResultCode.C_UNKNOWN_ERROR; + string msg = ""; + + if (ex is BusinessException bex) + { + code = bex.Code; + msg = bex.Message; + + LogHelper.Error($"业务异常,{msg}", requestMsg + ex); + } + else + { + if (EnvironmentVariableHelper.IsAspNetCoreProduction) + { + msg = "系统繁忙,请稍后再试"; + } + else + { + msg = ex.Message; + } + + LogHelper.Error($"未知异常,{ex.Message}", requestMsg + ex); + } + + var data = new ApiResult(code, msg); + + var result = data.ToJson(); + + context.Response.ContentType = "application/json;charset=utf-8"; + + return context.Response.WriteAsync(result); + } + } + + public static class ErrorHandlingExtensions + { + public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ApplicationBuilderExtend.cs b/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ApplicationBuilderExtend.cs index 092bc59..a4e4c16 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ApplicationBuilderExtend.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ApplicationBuilderExtend.cs @@ -1,76 +1,76 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Common.DingTalk; -using Hncore.Infrastructure.Extension; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Logging; -using NLog.Extensions.Logging; - -namespace Hncore.Infrastructure.WebApi -{ - /// - /// 应用程序构建器扩展类 - /// - /// - public static class ApplicationBuilderExtend - { - /// - /// 初始化应用程序构建器 - /// - /// 当前应用程序构建器对象 - /// 日志工厂对象 - /// 初始化后的应用程序构建器对象 - /// 应用程序生命周期对象 - /// - public static IApplicationBuilder Init(this IApplicationBuilder app, ILoggerFactory loggerFactory, - IApplicationLifetime applicationLifetime) - { - loggerFactory.AddNLog(); //启用Nlog日志插件 - - app.UseErrorHandling(); //添加统一错误异常处理中间件(一个自定义类) - - - - //启用Cors(跨域请求)支持(默认关闭状态) - - app.UseCors(builder => builder - .SetIsOriginAllowed(host => true) //允许所有来源 - .AllowAnyMethod() //允许任何请求方法(GET、POST、PUT、DELETE等) - .AllowAnyHeader() //允许任何请求头信息 - .AllowCredentials() //允许跨域凭据 - // .SetPreflightMaxAge(TimeSpan.FromDays(30)) //指定可以缓存预检请求的响应的时间为30天 - .WithExposedHeaders("X-Suggested-Filename", "set-user-token", "set-user") - ); - app.UseMvc(); //启用MVC - - - //向应用程序生命周期的“应用程序已完全启动”事件注册回调函数 - applicationLifetime.ApplicationStarted.Register(OnAppStarted); - - return app; - } - - /// - /// 应用程序完全启动完成处理回调函数 - /// - /// - private static void OnAppStarted() - { - //if (EnvironmentVariableHelper.IsAspNetCoreProduction) - //{ - // DingTalkHelper.SendMessage(new MarkDownModel() - // { - // markdown = new markdown() - // { - // title = "应用已启动", - // text = "### 应用已启动\n\nhostname:" + EnvironmentVariableHelper.HostName + "\n\n" + - // DateTime.Now.Format("yyyy-MM-dd HH:mm:ss") - // } - // }); - //} - } - } +using System; +using System.Threading; +using System.Threading.Tasks; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Common.DingTalk; +using Hncore.Infrastructure.Extension; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; + +namespace Hncore.Infrastructure.WebApi +{ + /// + /// 应用程序构建器扩展类 + /// + /// + public static class ApplicationBuilderExtend + { + /// + /// 初始化应用程序构建器 + /// + /// 当前应用程序构建器对象 + /// 日志工厂对象 + /// 初始化后的应用程序构建器对象 + /// 应用程序生命周期对象 + /// + public static IApplicationBuilder Init(this IApplicationBuilder app, ILoggerFactory loggerFactory, + IApplicationLifetime applicationLifetime) + { + loggerFactory.AddNLog(); //启用Nlog日志插件 + + app.UseErrorHandling(); //添加统一错误异常处理中间件(一个自定义类) + + + + //启用Cors(跨域请求)支持(默认关闭状态) + + app.UseCors(builder => builder + .SetIsOriginAllowed(host => true) //允许所有来源 + .AllowAnyMethod() //允许任何请求方法(GET、POST、PUT、DELETE等) + .AllowAnyHeader() //允许任何请求头信息 + .AllowCredentials() //允许跨域凭据 + // .SetPreflightMaxAge(TimeSpan.FromDays(30)) //指定可以缓存预检请求的响应的时间为30天 + .WithExposedHeaders("X-Suggested-Filename", "set-user-token", "set-user") + ); + app.UseMvc(); //启用MVC + + + //向应用程序生命周期的“应用程序已完全启动”事件注册回调函数 + applicationLifetime.ApplicationStarted.Register(OnAppStarted); + + return app; + } + + /// + /// 应用程序完全启动完成处理回调函数 + /// + /// + private static void OnAppStarted() + { + //if (EnvironmentVariableHelper.IsAspNetCoreProduction) + //{ + // DingTalkHelper.SendMessage(new MarkDownModel() + // { + // markdown = new markdown() + // { + // title = "应用已启动", + // text = "### 应用已启动\n\nhostname:" + EnvironmentVariableHelper.HostName + "\n\n" + + // DateTime.Now.Format("yyyy-MM-dd HH:mm:ss") + // } + // }); + //} + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/HostingEnvironmentExtend.cs b/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/HostingEnvironmentExtend.cs index e2c02c9..bb9136c 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/HostingEnvironmentExtend.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/HostingEnvironmentExtend.cs @@ -1,42 +1,42 @@ -using System; -using System.Runtime; -using Hncore.Infrastructure.Serializer; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; - -namespace Hncore.Infrastructure.WebApi -{ - public static class HostingEnvironmentExtend - { - public static IConfigurationRoot UseAppsettings(this IHostingEnvironment env) - { - Console.WriteLine("环境:" + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")); -#if DEBUG - Console.WriteLine("模式:DEBUG"); -#endif - -#if RELEASE - Console.WriteLine("模式:RELEASE"); -#endif - - Console.WriteLine("GC模式:" + new - { - IsServerGC = GCSettings.IsServerGC, - LargeObjectHeapCompactionMode = GCSettings.LargeObjectHeapCompactionMode.ToString(), - LatencyMode = GCSettings.LatencyMode.ToString() - }.ToJson(true)); - - System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); - - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false) - .AddEnvironmentVariables(); - - var config = builder.Build(); - - return config; - } - } +using System; +using System.Runtime; +using Hncore.Infrastructure.Serializer; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; + +namespace Hncore.Infrastructure.WebApi +{ + public static class HostingEnvironmentExtend + { + public static IConfigurationRoot UseAppsettings(this IHostingEnvironment env) + { + Console.WriteLine("环境:" + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")); +#if DEBUG + Console.WriteLine("模式:DEBUG"); +#endif + +#if RELEASE + Console.WriteLine("模式:RELEASE"); +#endif + + Console.WriteLine("GC模式:" + new + { + IsServerGC = GCSettings.IsServerGC, + LargeObjectHeapCompactionMode = GCSettings.LargeObjectHeapCompactionMode.ToString(), + LatencyMode = GCSettings.LatencyMode.ToString() + }.ToJson(true)); + + System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); + + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + return config; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ServiceCollectionExtend.cs b/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ServiceCollectionExtend.cs index 5180bb9..98bedcb 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ServiceCollectionExtend.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/StartupExtensions/ServiceCollectionExtend.cs @@ -1,113 +1,113 @@ -using System; -using Hncore.Infrastructure.Autofac; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; - -namespace Hncore.Infrastructure.WebApi -{ - /// - /// 服务集合对象(可以理解为内置的依赖注入容器)功能扩展类 - /// - /// - public static class ServiceCollectionExtend - { - /// - /// 通用初始化方法(被各个微服务项目所引用) - /// - /// 服务集合对象 - /// 配置信息对象 - /// .net core兼容版本 - /// 服务自定义选项对象 - /// 服务提供者对象 - /// - public static IServiceProvider Init(this IServiceCollection services, IConfiguration configuration, - CompatibilityVersion version, ServiceOption serviceOption = null) - { - //启用选项配置服务 - services.AddOptions(); - - //将配置信息对象以单例模式添加到服务集合中 - services.AddSingleton(configuration); - - // services.AddCors(); - var mvcbuilder = services - .AddMvc(options => - { - options.EnableEndpointRouting = false; //关闭终端点路由 - - if (serviceOption != null && serviceOption.UseGlobalManageAuthFilter) - { - //如果配置并传递了自定义选项中的全局授权过滤器,就将全局授权过滤器添加到MVC全局过滤器链中让其生效 - options.Filters.Add(new ManageAuthAttribute()); - - GlobalData.UseGlobalManageAuthFilter = true; - } - else - { - GlobalData.UseGlobalManageAuthFilter = false; - } - }) - .SetCompatibilityVersion(version) //设置.net core兼容版本号 - .AddJsonOptions(options => - { - //使用NewtonsoftJson插件,替换掉系统默认提供的JSON插件 - options.SerializerSettings.ContractResolver = - new Newtonsoft.Json.Serialization.DefaultContractResolver(); - - //定义日期格式化策略 - options.SerializerSettings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat; - - options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; - - //定义循环引用处理策略(就是要序列化的对象类A中引用了B,B引用了C……转了一圈儿之后又直接或间接引用回了A,就称为循环引用) - options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - - //序列化或反序列化时,可接受的日期时间字符串格式 - options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - - if (serviceOption != null && serviceOption.IgnoreJsonNullValue) - { - //如果配置并传递了自定义选项中的忽略空值选项,就在操作JSON时忽略掉空值数据 - //忽略掉空值的意思就是如果对象中的某个属性值为null,它将不会出现在最终序列化后的JSON字符串中 - options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - } - }); - - services.AddApiVersioning(option => - { - //设置API版本信息 - option.ReportApiVersions = true; //在向客户端响应的响应头中显示受支持的API版本信息 - option.AssumeDefaultVersionWhenUnspecified = true; //如果客户端未提供并指定要调用的API版本,就以下方的默认版本为准 - option.DefaultApiVersion = new ApiVersion(1, 0); //默认版本号 - }); - - //启用HTTP请求上下文访问器(用来访问类似传统MVC中的那个HttpContext对象) - services.AddHttpContextAccessor(); - - - - - //构建并返回服务提供对象(在Build方法中主要用Autofac插件替换掉了默认的依赖注入插件,并做一些自动扫描配置) - return new MvcAutoRegister().Build(services, mvcbuilder); - } - } - - /// - /// 服务自定义配置类(承载一些自定义配置) - /// - /// - public class ServiceOption - { - /// - /// 是否启用全局授权过滤器(默认关闭) - /// - public bool UseGlobalManageAuthFilter { get; set; } = false; - - /// - /// 是否在生成的JSON字符串中忽略掉为null的属性或字段(默认为false,意思就是就算字段为null也会出现在最终序列化后的JSON字符串中) - /// - public bool IgnoreJsonNullValue { get; set; } = false; - } +using System; +using Hncore.Infrastructure.Autofac; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; + +namespace Hncore.Infrastructure.WebApi +{ + /// + /// 服务集合对象(可以理解为内置的依赖注入容器)功能扩展类 + /// + /// + public static class ServiceCollectionExtend + { + /// + /// 通用初始化方法(被各个微服务项目所引用) + /// + /// 服务集合对象 + /// 配置信息对象 + /// .net core兼容版本 + /// 服务自定义选项对象 + /// 服务提供者对象 + /// + public static IServiceProvider Init(this IServiceCollection services, IConfiguration configuration, + CompatibilityVersion version, ServiceOption serviceOption = null) + { + //启用选项配置服务 + services.AddOptions(); + + //将配置信息对象以单例模式添加到服务集合中 + services.AddSingleton(configuration); + + // services.AddCors(); + var mvcbuilder = services + .AddMvc(options => + { + options.EnableEndpointRouting = false; //关闭终端点路由 + + if (serviceOption != null && serviceOption.UseGlobalManageAuthFilter) + { + //如果配置并传递了自定义选项中的全局授权过滤器,就将全局授权过滤器添加到MVC全局过滤器链中让其生效 + options.Filters.Add(new ManageAuthAttribute()); + + GlobalData.UseGlobalManageAuthFilter = true; + } + else + { + GlobalData.UseGlobalManageAuthFilter = false; + } + }) + .SetCompatibilityVersion(version) //设置.net core兼容版本号 + .AddJsonOptions(options => + { + //使用NewtonsoftJson插件,替换掉系统默认提供的JSON插件 + options.SerializerSettings.ContractResolver = + new Newtonsoft.Json.Serialization.DefaultContractResolver(); + + //定义日期格式化策略 + options.SerializerSettings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat; + + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; + + //定义循环引用处理策略(就是要序列化的对象类A中引用了B,B引用了C……转了一圈儿之后又直接或间接引用回了A,就称为循环引用) + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + //序列化或反序列化时,可接受的日期时间字符串格式 + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + + if (serviceOption != null && serviceOption.IgnoreJsonNullValue) + { + //如果配置并传递了自定义选项中的忽略空值选项,就在操作JSON时忽略掉空值数据 + //忽略掉空值的意思就是如果对象中的某个属性值为null,它将不会出现在最终序列化后的JSON字符串中 + options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + } + }); + + services.AddApiVersioning(option => + { + //设置API版本信息 + option.ReportApiVersions = true; //在向客户端响应的响应头中显示受支持的API版本信息 + option.AssumeDefaultVersionWhenUnspecified = true; //如果客户端未提供并指定要调用的API版本,就以下方的默认版本为准 + option.DefaultApiVersion = new ApiVersion(1, 0); //默认版本号 + }); + + //启用HTTP请求上下文访问器(用来访问类似传统MVC中的那个HttpContext对象) + services.AddHttpContextAccessor(); + + + + + //构建并返回服务提供对象(在Build方法中主要用Autofac插件替换掉了默认的依赖注入插件,并做一些自动扫描配置) + return new MvcAutoRegister().Build(services, mvcbuilder); + } + } + + /// + /// 服务自定义配置类(承载一些自定义配置) + /// + /// + public class ServiceOption + { + /// + /// 是否启用全局授权过滤器(默认关闭) + /// + public bool UseGlobalManageAuthFilter { get; set; } = false; + + /// + /// 是否在生成的JSON字符串中忽略掉为null的属性或字段(默认为false,意思就是就算字段为null也会出现在最终序列化后的JSON字符串中) + /// + public bool IgnoreJsonNullValue { get; set; } = false; + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/WebApi/WebRequest.cs b/Infrastructure/Hncore.Infrastructure/WebApi/WebRequest.cs index e637bd0..0f4ba61 100644 --- a/Infrastructure/Hncore.Infrastructure/WebApi/WebRequest.cs +++ b/Infrastructure/Hncore.Infrastructure/WebApi/WebRequest.cs @@ -1,71 +1,71 @@ -using Microsoft.AspNetCore.Http; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Infrastructure.Core.Web -{ - public static class HttpContextExtension - { - public static string GetUserIp(this HttpContext context) - { - var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); - if (string.IsNullOrEmpty(ip)) - { - ip = context.Connection.RemoteIpAddress.ToString(); - } - - return ip; - } - - public static string GetAbsoluteUri(this HttpRequest request) - { - return new StringBuilder() - .Append(request.Scheme) - .Append("://") - .Append(request.Host) - .Append(request.PathBase) - .Append(request.Path) - .Append(request.QueryString) - .ToString(); - } - - public static async Task ReadBodyAsStringAsync(this HttpRequest request) - { - request.EnableBuffering(); - - var requestReader = new StreamReader(request.Body); - - var requestBody = await requestReader.ReadToEndAsync(); - - request.Body.Position = 0; - - return requestBody; - } - } - - public static class IsLocalExtension - { - private const string NullIpAddress = "::1"; - - public static bool IsLocal(this HttpRequest req) - { - var connection = req.HttpContext.Connection; - if (connection.RemoteIpAddress.IsSet()) - { - return connection.LocalIpAddress.IsSet() - ? connection.RemoteIpAddress.Equals(connection.LocalIpAddress) - : IPAddress.IsLoopback(connection.RemoteIpAddress); - } - - return true; - } - - private static bool IsSet(this IPAddress address) - { - return address != null && address.ToString() != NullIpAddress; - } - } +using Microsoft.AspNetCore.Http; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Infrastructure.Core.Web +{ + public static class HttpContextExtension + { + public static string GetUserIp(this HttpContext context) + { + var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(ip)) + { + ip = context.Connection.RemoteIpAddress.ToString(); + } + + return ip; + } + + public static string GetAbsoluteUri(this HttpRequest request) + { + return new StringBuilder() + .Append(request.Scheme) + .Append("://") + .Append(request.Host) + .Append(request.PathBase) + .Append(request.Path) + .Append(request.QueryString) + .ToString(); + } + + public static async Task ReadBodyAsStringAsync(this HttpRequest request) + { + request.EnableBuffering(); + + var requestReader = new StreamReader(request.Body); + + var requestBody = await requestReader.ReadToEndAsync(); + + request.Body.Position = 0; + + return requestBody; + } + } + + public static class IsLocalExtension + { + private const string NullIpAddress = "::1"; + + public static bool IsLocal(this HttpRequest req) + { + var connection = req.HttpContext.Connection; + if (connection.RemoteIpAddress.IsSet()) + { + return connection.LocalIpAddress.IsSet() + ? connection.RemoteIpAddress.Equals(connection.LocalIpAddress) + : IPAddress.IsLoopback(connection.RemoteIpAddress); + } + + return true; + } + + private static bool IsSet(this IPAddress address) + { + return address != null && address.ToString() != NullIpAddress; + } + } } \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/nlog.config b/Infrastructure/Hncore.Infrastructure/nlog.config index 08d55a2..8ea310b 100644 --- a/Infrastructure/Hncore.Infrastructure/nlog.config +++ b/Infrastructure/Hncore.Infrastructure/nlog.config @@ -1,25 +1,25 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Infrastructure/Hncore.Infrastructure/xUnit/PriorityOrderer.cs b/Infrastructure/Hncore.Infrastructure/xUnit/PriorityOrderer.cs index 3ec7740..36397be 100644 --- a/Infrastructure/Hncore.Infrastructure/xUnit/PriorityOrderer.cs +++ b/Infrastructure/Hncore.Infrastructure/xUnit/PriorityOrderer.cs @@ -1,56 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Hncore.Infrastructure.xUnit -{ - public class PriorityOrderer : ITestCaseOrderer - { - public IEnumerable OrderTestCases(IEnumerable testCases) where TTestCase : ITestCase - { - var sortedMethods = new SortedDictionary>(); - - foreach (TTestCase testCase in testCases) - { - int priority = 0; - - foreach (IAttributeInfo attr in testCase.TestMethod.Method.GetCustomAttributes((typeof(TestPriorityAttribute).AssemblyQualifiedName))) - priority = attr.GetNamedArgument("Priority"); - - GetOrCreate(sortedMethods, priority).Add(testCase); - } - - foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority])) - { - list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name)); - foreach (TTestCase testCase in list) - yield return testCase; - } - } - - static TValue GetOrCreate(IDictionary dictionary, TKey key) where TValue : new() - { - TValue result; - - if (dictionary.TryGetValue(key, out result)) return result; - - result = new TValue(); - dictionary[key] = result; - - return result; - } - } - - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class TestPriorityAttribute : Attribute - { - public TestPriorityAttribute(int priority) - { - Priority = priority; - } - - public int Priority { get; private set; } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Hncore.Infrastructure.xUnit +{ + public class PriorityOrderer : ITestCaseOrderer + { + public IEnumerable OrderTestCases(IEnumerable testCases) where TTestCase : ITestCase + { + var sortedMethods = new SortedDictionary>(); + + foreach (TTestCase testCase in testCases) + { + int priority = 0; + + foreach (IAttributeInfo attr in testCase.TestMethod.Method.GetCustomAttributes((typeof(TestPriorityAttribute).AssemblyQualifiedName))) + priority = attr.GetNamedArgument("Priority"); + + GetOrCreate(sortedMethods, priority).Add(testCase); + } + + foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority])) + { + list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name)); + foreach (TTestCase testCase in list) + yield return testCase; + } + } + + static TValue GetOrCreate(IDictionary dictionary, TKey key) where TValue : new() + { + TValue result; + + if (dictionary.TryGetValue(key, out result)) return result; + + result = new TValue(); + dictionary[key] = result; + + return result; + } + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class TestPriorityAttribute : Attribute + { + public TestPriorityAttribute(int priority) + { + Priority = priority; + } + + public int Priority { get; private set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/Alipay.AopSdk.Core/DefaultAopClient.cs b/Infrastructure/ServiceClient/Alipay.AopSdk.Core/DefaultAopClient.cs index 47ea2cc..76e8bc3 100644 --- a/Infrastructure/ServiceClient/Alipay.AopSdk.Core/DefaultAopClient.cs +++ b/Infrastructure/ServiceClient/Alipay.AopSdk.Core/DefaultAopClient.cs @@ -1,649 +1,649 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using Alipay.AopSdk.Core.Parser; -using Alipay.AopSdk.Core.Util; -using Newtonsoft.Json; - -namespace Alipay.AopSdk.Core -{ - /// - /// - /// AOP客户端。 - /// - public class DefaultAopClient : IAopClient - { - public const string APP_ID = "app_id"; - public const string FORMAT = "format"; - public const string METHOD = "method"; - public const string TIMESTAMP = "timestamp"; - public const string VERSION = "version"; - public const string SIGN_TYPE = "sign_type"; - public const string ACCESS_TOKEN = "auth_token"; - public const string SIGN = "sign"; - public const string TERMINAL_TYPE = "terminal_type"; - public const string TERMINAL_INFO = "terminal_info"; - public const string PROD_CODE = "prod_code"; - public const string NOTIFY_URL = "notify_url"; - public const string CHARSET = "charset"; - public const string ENCRYPT_TYPE = "encrypt_type"; - public const string BIZ_CONTENT = "biz_content"; - public const string APP_AUTH_TOKEN = "app_auth_token"; - public const string RETURN_URL = "return_url"; - private readonly string alipayPublicKey; - private string charset; - private readonly string encyptKey; - private readonly string encyptType = "AES"; - private string format; - private string httpmethod; - private readonly bool keyFromFile; - - public string notify_url; - private readonly string privateKeyPem; - public string return_url; - private readonly string serverUrl; - private readonly string signType = "RSA2"; - - private string version; - - private readonly WebUtils webUtils; - - public string Version - { - get => version != null ? version : "1.0"; - set => version = value; - } - - public string Format - { - get => format != null ? format : "json"; - set => format = value; - } - - public string AppId { get; set; } - - #region IAopClient Members - - public T PageExecute(IAopRequest request) where T : AopResponse - { - return PageExecute(request, null, "POST"); - } - - public async Task PageExecuteAsync(IAopRequest request) where T : AopResponse - { - return await PageExecuteAsync(request, null, "POST"); - } - #endregion - - #region IAopClient Members - public T PageExecute (IAopRequest request, string accessToken, string reqMethod) where T : AopResponse - { - return AsyncHelper.RunSync(async () => await PageExecuteAsync(request, accessToken, reqMethod)); - } - - public async Task PageExecuteAsync(IAopRequest request, string accessToken, string reqMethod) where T : AopResponse - { - if (string.IsNullOrEmpty(charset)) - charset = "utf-8"; - - string apiVersion = null; - - if (!string.IsNullOrEmpty(request.GetApiVersion())) - apiVersion = request.GetApiVersion(); - else - apiVersion = Version; - - var txtParams = new AopDictionary(request.GetParameters()); - - // 序列化BizModel - txtParams = SerializeBizModel(txtParams, request); - - // 添加协议级请求参数 - //AopDictionary txtParams = new AopDictionary(request.GetParameters()); - txtParams.Add(METHOD, request.GetApiName()); - txtParams.Add(VERSION, apiVersion); - txtParams.Add(APP_ID, AppId); - txtParams.Add(FORMAT, format); - txtParams.Add(TIMESTAMP, DateTime.Now); - txtParams.Add(ACCESS_TOKEN, accessToken); - txtParams.Add(SIGN_TYPE, signType); - txtParams.Add(TERMINAL_TYPE, request.GetTerminalType()); - txtParams.Add(TERMINAL_INFO, request.GetTerminalInfo()); - txtParams.Add(PROD_CODE, request.GetProdCode()); - txtParams.Add(NOTIFY_URL, request.GetNotifyUrl()); - txtParams.Add(CHARSET, charset); - txtParams.Add(RETURN_URL, request.GetReturnUrl()); - //字典排序 - IDictionary sortedTxtParams = new SortedDictionary(txtParams); - txtParams = new AopDictionary(sortedTxtParams); - // 排序返回字典类型添加签名参数 - - var signContent =AlipaySignature.GetSignContent(txtParams); - - var path ="Logs/"+DateTime.Now.ToString("yyyyMMdd"); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - var file = path + "/alipay.log"; - File.AppendAllText(file, "pay body:" + signContent); - - txtParams.Add(SIGN, AopUtils.SignAopRequest(sortedTxtParams, privateKeyPem, charset, keyFromFile, signType)); - File.AppendAllText(file, "pay SIGN:" + txtParams[SIGN]); - // 是否需要上传文件 - string body; - - if (request is IAopUploadRequest) - { - var uRequest = (IAopUploadRequest) request; - var fileParams = AopUtils.CleanupDictionary(uRequest.GetFileParameters()); - body = await webUtils.DoPostAsync(serverUrl + "?" + CHARSET + "=" + charset, txtParams, fileParams, charset); - } - else - { - if (reqMethod.Equals("GET")) - { - //直接调用DoGet方法请求 - //body=webUtils .DoGet (this.serverUrl ,txtParams ,this.charset); - //拼接get请求的url - var tmpUrl = serverUrl; - if (txtParams != null && txtParams.Count > 0) - if (tmpUrl.Contains("?")) - tmpUrl = tmpUrl + "&" + WebUtils.BuildQuery(txtParams, charset); - else - tmpUrl = tmpUrl + "?" + WebUtils.BuildQuery(txtParams, charset); - body = tmpUrl; - } - else - { - //直接调用DoPost方法请求 - // body = webUtils.DoPost(this.serverUrl, txtParams, this.charset); - //输出post表单 - body = BuildHtmlRequest(txtParams, reqMethod, reqMethod); - } - } - - T rsp = null; - IAopParser parser = null; - if ("xml".Equals(format)) - { - parser = new AopXmlParser(); - rsp = parser.Parse(body, charset); - } - else - { - parser = new AopJsonParser(); - rsp = parser.Parse(body, charset); - } - - //验签 - // CheckResponseSign(request, rsp, parser, this.alipayPublicKey, this.charset); - return rsp; - } - - #endregion - - #region SDK Execute - - public T SdkExecute(IAopRequest request) where T : AopResponse - { - // 构造请求参数 - var requestParams = buildRequestParams(request, null, null); - - // 字典排序 - IDictionary sortedParams = new SortedDictionary(requestParams); - var sortedAopDic = new AopDictionary(sortedParams); - - // 参数签名 - var charset = string.IsNullOrEmpty(this.charset) ? "utf-8" : this.charset; - var signResult = AopUtils.SignAopRequest(sortedAopDic, privateKeyPem, charset, keyFromFile, signType); - - // 添加签名结果参数 - sortedAopDic.Add(SIGN, signResult); - - // 参数拼接 - var signedResult = WebUtils.BuildQuery(sortedAopDic, charset); - - // 构造结果 - var rsp = (T) Activator.CreateInstance(typeof(T)); - rsp.Body = signedResult; - return rsp; - } - - #endregion - - #region IAopClient Members - - public string BuildHtmlRequest(IDictionary sParaTemp, string strMethod, string strButtonValue) - { - //待请求参数数组 - IDictionary dicPara = new Dictionary(); - dicPara = sParaTemp; - - var sbHtml = new StringBuilder(); - //sbHtml.Append(""); - - sbHtml.Append("
"); - ; - foreach (var temp in dicPara) - sbHtml.Append(""); - - //submit按钮控件请不要含有name属性 - sbHtml.Append("
"); - // sbHtml.Append(""); - - //表单实现自动提交 - sbHtml.Append(""); - - return sbHtml.ToString(); - } - - #endregion - - #region Common Method - - private AopDictionary buildRequestParams(IAopRequest request, string accessToken, string appAuthToken) - where T : AopResponse - { - // 默认参数 - var oriParams = new AopDictionary(request.GetParameters()); - - // 序列化BizModel - var result = SerializeBizModel(oriParams, request); - - // 获取参数 - var charset = string.IsNullOrEmpty(this.charset) ? "utf-8" : this.charset; - var apiVersion = string.IsNullOrEmpty(request.GetApiVersion()) ? Version : request.GetApiVersion(); - - // 添加协议级请求参数,为空的参数后面会自动过滤,这里不做处理。 - result.Add(METHOD, request.GetApiName()); - result.Add(VERSION, apiVersion); - result.Add(APP_ID, AppId); - result.Add(FORMAT, format); - result.Add(TIMESTAMP, DateTime.Now); - result.Add(ACCESS_TOKEN, accessToken); - result.Add(SIGN_TYPE, signType); - result.Add(TERMINAL_TYPE, request.GetTerminalType()); - result.Add(TERMINAL_INFO, request.GetTerminalInfo()); - result.Add(PROD_CODE, request.GetProdCode()); - result.Add(NOTIFY_URL, request.GetNotifyUrl()); - result.Add(CHARSET, charset); - result.Add(RETURN_URL, request.GetReturnUrl()); - result.Add(APP_AUTH_TOKEN, appAuthToken); - - if (request.GetNeedEncrypt()) - { - if (string.IsNullOrEmpty(result[BIZ_CONTENT])) - throw new AopException("api request Fail ! The reason: encrypt request is not supported!"); - - if (string.IsNullOrEmpty(encyptKey) || string.IsNullOrEmpty(encyptType)) - throw new AopException("encryptType or encryptKey must not null!"); - - if (!"AES".Equals(encyptType)) - throw new AopException("api only support Aes!"); - - var encryptContent = AopUtils.AesEncrypt(encyptKey, result[BIZ_CONTENT], this.charset); - result.Remove(BIZ_CONTENT); - result.Add(BIZ_CONTENT, encryptContent); - result.Add(ENCRYPT_TYPE, encyptType); - } - - return result; - } - - #endregion - - #region DefaultAopClient Constructors - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem) - { - AppId = appId; - this.privateKeyPem = privateKeyPem; - this.serverUrl = serverUrl; - webUtils = new WebUtils(serverUrl); - } - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, bool keyFromFile) - { - AppId = appId; - this.privateKeyPem = privateKeyPem; - this.serverUrl = serverUrl; - this.keyFromFile = keyFromFile; - webUtils = new WebUtils(serverUrl); - } - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format) - { - AppId = appId; - this.privateKeyPem = privateKeyPem; - this.serverUrl = serverUrl; - this.format = format; - webUtils = new WebUtils(serverUrl); - } - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string charset) - : this(serverUrl, appId, privateKeyPem, format) - { - this.charset = charset; - } - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, - string signType) - : this(serverUrl, appId, privateKeyPem) - { - this.format = format; - this.version = version; - this.signType = signType; - } - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, - string signType, string alipayPulicKey) - : this(serverUrl, appId, privateKeyPem, format, version, signType) - { - alipayPublicKey = alipayPulicKey; - } - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, - string signType, string alipayPulicKey, string charset) - : this(serverUrl, appId, privateKeyPem, format, version, signType, alipayPulicKey) - { - this.charset = charset; - } - - // - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, - string signType, string alipayPulicKey, string charset, bool keyFromFile) - : this(serverUrl, appId, privateKeyPem, format, version, signType, alipayPulicKey) - { - this.keyFromFile = keyFromFile; - this.charset = charset; - } - - public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, - string signType, string alipayPulicKey, string charset, string encyptKey) - : this(serverUrl, appId, privateKeyPem, format, version, signType, alipayPulicKey, charset) - { - this.encyptKey = encyptKey; - encyptType = "AES"; - } - - #endregion - - #region IAopClient Members - - public T Execute(IAopRequest request) where T : AopResponse - { - return Execute(request, null); - } - - public T Execute(IAopRequest request, string accessToken) where T : AopResponse - { - return Execute(request, accessToken, null); - } - - public async Task ExecuteAsync(IAopRequest request) where T : AopResponse - { - return await ExecuteAsync(request, null); - } - - public async Task ExecuteAsync(IAopRequest request, string accessToken) where T : AopResponse - { - return await ExecuteAsync(request, accessToken, null); - } - - #endregion - - #region IAopClient Members - - public T Execute(IAopRequest request, string accessToken, string appAuthToken) where T : AopResponse - { - return AsyncHelper.RunSync(async () => await ExecuteAsync(request, accessToken, appAuthToken)); - } - - public async Task ExecuteAsync(IAopRequest request, string accessToken, string appAuthToken) where T : AopResponse - { - if (string.IsNullOrEmpty(charset)) - charset = "utf-8"; - - string apiVersion = null; - - if (!string.IsNullOrEmpty(request.GetApiVersion())) - apiVersion = request.GetApiVersion(); - else - apiVersion = Version; - - // 添加协议级请求参数 - var txtParams = new AopDictionary(request.GetParameters()); - - // 序列化BizModel - txtParams = SerializeBizModel(txtParams, request); - - txtParams.Add(METHOD, request.GetApiName()); - txtParams.Add(VERSION, apiVersion); - txtParams.Add(APP_ID, AppId); - txtParams.Add(FORMAT, format); - txtParams.Add(TIMESTAMP, DateTime.Now); - txtParams.Add(ACCESS_TOKEN, accessToken); - txtParams.Add(SIGN_TYPE, signType); - txtParams.Add(TERMINAL_TYPE, request.GetTerminalType()); - txtParams.Add(TERMINAL_INFO, request.GetTerminalInfo()); - txtParams.Add(PROD_CODE, request.GetProdCode()); - txtParams.Add(CHARSET, charset); - - - if (!string.IsNullOrEmpty(request.GetNotifyUrl())) - txtParams.Add(NOTIFY_URL, request.GetNotifyUrl()); - - if (!string.IsNullOrEmpty(appAuthToken)) - txtParams.Add(APP_AUTH_TOKEN, appAuthToken); - - - if (request.GetNeedEncrypt()) - { - if (string.IsNullOrEmpty(txtParams[BIZ_CONTENT])) - throw new AopException("api request Fail ! The reason: encrypt request is not supported!"); - - if (string.IsNullOrEmpty(encyptKey) || string.IsNullOrEmpty(encyptType)) - throw new AopException("encryptType or encryptKey must not null!"); - - if (!"AES".Equals(encyptType)) - throw new AopException("api only support Aes!"); - - var encryptContent = AopUtils.AesEncrypt(encyptKey, txtParams[BIZ_CONTENT], charset); - txtParams.Remove(BIZ_CONTENT); - txtParams.Add(BIZ_CONTENT, encryptContent); - txtParams.Add(ENCRYPT_TYPE, encyptType); - } - - // 添加签名参数 - txtParams.Add(SIGN, AopUtils.SignAopRequest(txtParams, privateKeyPem, charset, keyFromFile, signType)); - - - // 是否需要上传文件 - string body; - - - if (request is IAopUploadRequest) - { - var uRequest = (IAopUploadRequest)request; - var fileParams = AopUtils.CleanupDictionary(uRequest.GetFileParameters()); - body = await webUtils.DoPostAsync(serverUrl + "?" + CHARSET + "=" + charset, txtParams, fileParams, charset); - } - else - { - body = await webUtils.DoPostAsync(serverUrl + "?" + CHARSET + "=" + charset, txtParams, charset); - } - - T rsp = null; - IAopParser parser = null; - if ("xml".Equals(format)) - { - parser = new AopXmlParser(); - rsp = parser.Parse(body, charset); - } - else - { - parser = new AopJsonParser(); - rsp = parser.Parse(body, charset); - } - - var item = parseRespItem(request, body, parser, encyptKey, encyptType, charset); - rsp = parser.Parse(item.realContent, charset); - - CheckResponseSign(request, item.respContent, rsp.IsError, parser, alipayPublicKey, charset, signType, keyFromFile); - - return rsp; - } - - private static ResponseParseItem parseRespItem(IAopRequest request, string respBody, IAopParser parser, - string encryptKey, string encryptType, string charset) where T : AopResponse - { - string realContent = null; - - if (request.GetNeedEncrypt()) - realContent = parser.EncryptSourceData(request, respBody, encryptType, encryptKey, charset); - else - realContent = respBody; - - var item = new ResponseParseItem(); - item.realContent = realContent; - item.respContent = respBody; - - return item; - } - - public static void CheckResponseSign(IAopRequest request, string responseBody, bool isError, - IAopParser parser, string alipayPublicKey, string charset, string signType) where T : AopResponse - { - if (string.IsNullOrEmpty(alipayPublicKey) || string.IsNullOrEmpty(charset)) - return; - - var signItem = parser.GetSignItem(request, responseBody); - if (signItem == null) - throw new AopException("sign check fail: Body is Empty!"); - - if (!isError || - isError && !string.IsNullOrEmpty(signItem.Sign)) - { - var rsaCheckContent = - AlipaySignature.RSACheckContent(signItem.SignSourceDate, signItem.Sign, alipayPublicKey, charset, signType); - if (!rsaCheckContent) - if (!string.IsNullOrEmpty(signItem.SignSourceDate) && signItem.SignSourceDate.Contains("\\/")) - { - var srouceData = signItem.SignSourceDate.Replace("\\/", "/"); - var jsonCheck = AlipaySignature.RSACheckContent(srouceData, signItem.Sign, alipayPublicKey, charset, signType); - if (!jsonCheck) - throw new AopException( - "sign check fail: check Sign and Data Fail JSON also"); - } - else - { - throw new AopException( - "sign check fail: check Sign and Data Fail!"); - } - } - } - - public static void CheckResponseSign(IAopRequest request, string responseBody, bool isError, - IAopParser parser, string alipayPublicKey, string charset, string signType, bool keyFromFile) - where T : AopResponse - { - if (string.IsNullOrEmpty(alipayPublicKey) || string.IsNullOrEmpty(charset)) - return; - - var signItem = parser.GetSignItem(request, responseBody); - if (signItem == null) - throw new AopException("sign check fail: Body is Empty!"); - - if (!isError || - isError && !string.IsNullOrEmpty(signItem.Sign)) - { - var rsaCheckContent = AlipaySignature.RSACheckContent(signItem.SignSourceDate, signItem.Sign, alipayPublicKey, - charset, signType, keyFromFile); - if (!rsaCheckContent) - if (!string.IsNullOrEmpty(signItem.SignSourceDate) && signItem.SignSourceDate.Contains("\\/")) - { - var srouceData = signItem.SignSourceDate.Replace("\\/", "/"); - var jsonCheck = - AlipaySignature.RSACheckContent(srouceData, signItem.Sign, alipayPublicKey, charset, signType, keyFromFile); - if (!jsonCheck) - throw new AopException( - "sign check fail: check Sign and Data Fail JSON also"); - } - else - { - throw new AopException( - "sign check fail: check Sign and Data Fail!"); - } - } - } - - #endregion - - #region IAopClient Members - - public Dictionary FilterPara(SortedDictionary dicArrayPre) - { - var dicArray = new Dictionary(); - foreach (var temp in dicArrayPre) - if (temp.Key.ToLower() != "sign" && temp.Key.ToLower() != "sign_type" && temp.Value != "" && temp.Value != null) - dicArray.Add(temp.Key, temp.Value); - - return dicArray; - } - - public static string CreateLinkStringUrlencode(Dictionary dicArray, Encoding code) - { - var prestr = new StringBuilder(); - foreach (var temp in dicArray) - prestr.Append(temp.Key + "=" + HttpUtility.UrlEncode(temp.Value, code) + "&"); - - //去掉最後一個&字符 - var nLen = prestr.Length; - prestr.Remove(nLen - 1, 1); - - return prestr.ToString(); - } - - #endregion - - #region Model Serialize - - /// - /// - /// - /// - /// - /// - private AopDictionary SerializeBizModel(AopDictionary requestParams, IAopRequest request) where T : AopResponse - { - var result = requestParams; - var isBizContentEmpty = !requestParams.ContainsKey(BIZ_CONTENT) || string.IsNullOrEmpty(requestParams[BIZ_CONTENT]); - if (isBizContentEmpty && request.GetBizModel() != null) - { - var bizModel = request.GetBizModel(); - var content = Serialize(bizModel); - result.Add(BIZ_CONTENT, content); - } - return result; - } - - /// - /// AopObject序列化 - /// - /// - /// - private string Serialize(AopObject obj) - { - JsonSerializerSettings jsetting = new JsonSerializerSettings(); - jsetting.NullValueHandling = NullValueHandling.Ignore; - return JsonConvert.SerializeObject(obj, Formatting.None, jsetting); - } - - #endregion - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using Alipay.AopSdk.Core.Parser; +using Alipay.AopSdk.Core.Util; +using Newtonsoft.Json; + +namespace Alipay.AopSdk.Core +{ + /// + /// + /// AOP客户端。 + /// + public class DefaultAopClient : IAopClient + { + public const string APP_ID = "app_id"; + public const string FORMAT = "format"; + public const string METHOD = "method"; + public const string TIMESTAMP = "timestamp"; + public const string VERSION = "version"; + public const string SIGN_TYPE = "sign_type"; + public const string ACCESS_TOKEN = "auth_token"; + public const string SIGN = "sign"; + public const string TERMINAL_TYPE = "terminal_type"; + public const string TERMINAL_INFO = "terminal_info"; + public const string PROD_CODE = "prod_code"; + public const string NOTIFY_URL = "notify_url"; + public const string CHARSET = "charset"; + public const string ENCRYPT_TYPE = "encrypt_type"; + public const string BIZ_CONTENT = "biz_content"; + public const string APP_AUTH_TOKEN = "app_auth_token"; + public const string RETURN_URL = "return_url"; + private readonly string alipayPublicKey; + private string charset; + private readonly string encyptKey; + private readonly string encyptType = "AES"; + private string format; + private string httpmethod; + private readonly bool keyFromFile; + + public string notify_url; + private readonly string privateKeyPem; + public string return_url; + private readonly string serverUrl; + private readonly string signType = "RSA2"; + + private string version; + + private readonly WebUtils webUtils; + + public string Version + { + get => version != null ? version : "1.0"; + set => version = value; + } + + public string Format + { + get => format != null ? format : "json"; + set => format = value; + } + + public string AppId { get; set; } + + #region IAopClient Members + + public T PageExecute(IAopRequest request) where T : AopResponse + { + return PageExecute(request, null, "POST"); + } + + public async Task PageExecuteAsync(IAopRequest request) where T : AopResponse + { + return await PageExecuteAsync(request, null, "POST"); + } + #endregion + + #region IAopClient Members + public T PageExecute (IAopRequest request, string accessToken, string reqMethod) where T : AopResponse + { + return AsyncHelper.RunSync(async () => await PageExecuteAsync(request, accessToken, reqMethod)); + } + + public async Task PageExecuteAsync(IAopRequest request, string accessToken, string reqMethod) where T : AopResponse + { + if (string.IsNullOrEmpty(charset)) + charset = "utf-8"; + + string apiVersion = null; + + if (!string.IsNullOrEmpty(request.GetApiVersion())) + apiVersion = request.GetApiVersion(); + else + apiVersion = Version; + + var txtParams = new AopDictionary(request.GetParameters()); + + // 序列化BizModel + txtParams = SerializeBizModel(txtParams, request); + + // 添加协议级请求参数 + //AopDictionary txtParams = new AopDictionary(request.GetParameters()); + txtParams.Add(METHOD, request.GetApiName()); + txtParams.Add(VERSION, apiVersion); + txtParams.Add(APP_ID, AppId); + txtParams.Add(FORMAT, format); + txtParams.Add(TIMESTAMP, DateTime.Now); + txtParams.Add(ACCESS_TOKEN, accessToken); + txtParams.Add(SIGN_TYPE, signType); + txtParams.Add(TERMINAL_TYPE, request.GetTerminalType()); + txtParams.Add(TERMINAL_INFO, request.GetTerminalInfo()); + txtParams.Add(PROD_CODE, request.GetProdCode()); + txtParams.Add(NOTIFY_URL, request.GetNotifyUrl()); + txtParams.Add(CHARSET, charset); + txtParams.Add(RETURN_URL, request.GetReturnUrl()); + //字典排序 + IDictionary sortedTxtParams = new SortedDictionary(txtParams); + txtParams = new AopDictionary(sortedTxtParams); + // 排序返回字典类型添加签名参数 + + var signContent =AlipaySignature.GetSignContent(txtParams); + + var path ="Logs/"+DateTime.Now.ToString("yyyyMMdd"); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + var file = path + "/alipay.log"; + File.AppendAllText(file, "pay body:" + signContent); + + txtParams.Add(SIGN, AopUtils.SignAopRequest(sortedTxtParams, privateKeyPem, charset, keyFromFile, signType)); + File.AppendAllText(file, "pay SIGN:" + txtParams[SIGN]); + // 是否需要上传文件 + string body; + + if (request is IAopUploadRequest) + { + var uRequest = (IAopUploadRequest) request; + var fileParams = AopUtils.CleanupDictionary(uRequest.GetFileParameters()); + body = await webUtils.DoPostAsync(serverUrl + "?" + CHARSET + "=" + charset, txtParams, fileParams, charset); + } + else + { + if (reqMethod.Equals("GET")) + { + //直接调用DoGet方法请求 + //body=webUtils .DoGet (this.serverUrl ,txtParams ,this.charset); + //拼接get请求的url + var tmpUrl = serverUrl; + if (txtParams != null && txtParams.Count > 0) + if (tmpUrl.Contains("?")) + tmpUrl = tmpUrl + "&" + WebUtils.BuildQuery(txtParams, charset); + else + tmpUrl = tmpUrl + "?" + WebUtils.BuildQuery(txtParams, charset); + body = tmpUrl; + } + else + { + //直接调用DoPost方法请求 + // body = webUtils.DoPost(this.serverUrl, txtParams, this.charset); + //输出post表单 + body = BuildHtmlRequest(txtParams, reqMethod, reqMethod); + } + } + + T rsp = null; + IAopParser parser = null; + if ("xml".Equals(format)) + { + parser = new AopXmlParser(); + rsp = parser.Parse(body, charset); + } + else + { + parser = new AopJsonParser(); + rsp = parser.Parse(body, charset); + } + + //验签 + // CheckResponseSign(request, rsp, parser, this.alipayPublicKey, this.charset); + return rsp; + } + + #endregion + + #region SDK Execute + + public T SdkExecute(IAopRequest request) where T : AopResponse + { + // 构造请求参数 + var requestParams = buildRequestParams(request, null, null); + + // 字典排序 + IDictionary sortedParams = new SortedDictionary(requestParams); + var sortedAopDic = new AopDictionary(sortedParams); + + // 参数签名 + var charset = string.IsNullOrEmpty(this.charset) ? "utf-8" : this.charset; + var signResult = AopUtils.SignAopRequest(sortedAopDic, privateKeyPem, charset, keyFromFile, signType); + + // 添加签名结果参数 + sortedAopDic.Add(SIGN, signResult); + + // 参数拼接 + var signedResult = WebUtils.BuildQuery(sortedAopDic, charset); + + // 构造结果 + var rsp = (T) Activator.CreateInstance(typeof(T)); + rsp.Body = signedResult; + return rsp; + } + + #endregion + + #region IAopClient Members + + public string BuildHtmlRequest(IDictionary sParaTemp, string strMethod, string strButtonValue) + { + //待请求参数数组 + IDictionary dicPara = new Dictionary(); + dicPara = sParaTemp; + + var sbHtml = new StringBuilder(); + //sbHtml.Append(""); + + sbHtml.Append("
"); + ; + foreach (var temp in dicPara) + sbHtml.Append(""); + + //submit按钮控件请不要含有name属性 + sbHtml.Append("
"); + // sbHtml.Append(""); + + //表单实现自动提交 + sbHtml.Append(""); + + return sbHtml.ToString(); + } + + #endregion + + #region Common Method + + private AopDictionary buildRequestParams(IAopRequest request, string accessToken, string appAuthToken) + where T : AopResponse + { + // 默认参数 + var oriParams = new AopDictionary(request.GetParameters()); + + // 序列化BizModel + var result = SerializeBizModel(oriParams, request); + + // 获取参数 + var charset = string.IsNullOrEmpty(this.charset) ? "utf-8" : this.charset; + var apiVersion = string.IsNullOrEmpty(request.GetApiVersion()) ? Version : request.GetApiVersion(); + + // 添加协议级请求参数,为空的参数后面会自动过滤,这里不做处理。 + result.Add(METHOD, request.GetApiName()); + result.Add(VERSION, apiVersion); + result.Add(APP_ID, AppId); + result.Add(FORMAT, format); + result.Add(TIMESTAMP, DateTime.Now); + result.Add(ACCESS_TOKEN, accessToken); + result.Add(SIGN_TYPE, signType); + result.Add(TERMINAL_TYPE, request.GetTerminalType()); + result.Add(TERMINAL_INFO, request.GetTerminalInfo()); + result.Add(PROD_CODE, request.GetProdCode()); + result.Add(NOTIFY_URL, request.GetNotifyUrl()); + result.Add(CHARSET, charset); + result.Add(RETURN_URL, request.GetReturnUrl()); + result.Add(APP_AUTH_TOKEN, appAuthToken); + + if (request.GetNeedEncrypt()) + { + if (string.IsNullOrEmpty(result[BIZ_CONTENT])) + throw new AopException("api request Fail ! The reason: encrypt request is not supported!"); + + if (string.IsNullOrEmpty(encyptKey) || string.IsNullOrEmpty(encyptType)) + throw new AopException("encryptType or encryptKey must not null!"); + + if (!"AES".Equals(encyptType)) + throw new AopException("api only support Aes!"); + + var encryptContent = AopUtils.AesEncrypt(encyptKey, result[BIZ_CONTENT], this.charset); + result.Remove(BIZ_CONTENT); + result.Add(BIZ_CONTENT, encryptContent); + result.Add(ENCRYPT_TYPE, encyptType); + } + + return result; + } + + #endregion + + #region DefaultAopClient Constructors + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem) + { + AppId = appId; + this.privateKeyPem = privateKeyPem; + this.serverUrl = serverUrl; + webUtils = new WebUtils(serverUrl); + } + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, bool keyFromFile) + { + AppId = appId; + this.privateKeyPem = privateKeyPem; + this.serverUrl = serverUrl; + this.keyFromFile = keyFromFile; + webUtils = new WebUtils(serverUrl); + } + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format) + { + AppId = appId; + this.privateKeyPem = privateKeyPem; + this.serverUrl = serverUrl; + this.format = format; + webUtils = new WebUtils(serverUrl); + } + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string charset) + : this(serverUrl, appId, privateKeyPem, format) + { + this.charset = charset; + } + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, + string signType) + : this(serverUrl, appId, privateKeyPem) + { + this.format = format; + this.version = version; + this.signType = signType; + } + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, + string signType, string alipayPulicKey) + : this(serverUrl, appId, privateKeyPem, format, version, signType) + { + alipayPublicKey = alipayPulicKey; + } + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, + string signType, string alipayPulicKey, string charset) + : this(serverUrl, appId, privateKeyPem, format, version, signType, alipayPulicKey) + { + this.charset = charset; + } + + // + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, + string signType, string alipayPulicKey, string charset, bool keyFromFile) + : this(serverUrl, appId, privateKeyPem, format, version, signType, alipayPulicKey) + { + this.keyFromFile = keyFromFile; + this.charset = charset; + } + + public DefaultAopClient(string serverUrl, string appId, string privateKeyPem, string format, string version, + string signType, string alipayPulicKey, string charset, string encyptKey) + : this(serverUrl, appId, privateKeyPem, format, version, signType, alipayPulicKey, charset) + { + this.encyptKey = encyptKey; + encyptType = "AES"; + } + + #endregion + + #region IAopClient Members + + public T Execute(IAopRequest request) where T : AopResponse + { + return Execute(request, null); + } + + public T Execute(IAopRequest request, string accessToken) where T : AopResponse + { + return Execute(request, accessToken, null); + } + + public async Task ExecuteAsync(IAopRequest request) where T : AopResponse + { + return await ExecuteAsync(request, null); + } + + public async Task ExecuteAsync(IAopRequest request, string accessToken) where T : AopResponse + { + return await ExecuteAsync(request, accessToken, null); + } + + #endregion + + #region IAopClient Members + + public T Execute(IAopRequest request, string accessToken, string appAuthToken) where T : AopResponse + { + return AsyncHelper.RunSync(async () => await ExecuteAsync(request, accessToken, appAuthToken)); + } + + public async Task ExecuteAsync(IAopRequest request, string accessToken, string appAuthToken) where T : AopResponse + { + if (string.IsNullOrEmpty(charset)) + charset = "utf-8"; + + string apiVersion = null; + + if (!string.IsNullOrEmpty(request.GetApiVersion())) + apiVersion = request.GetApiVersion(); + else + apiVersion = Version; + + // 添加协议级请求参数 + var txtParams = new AopDictionary(request.GetParameters()); + + // 序列化BizModel + txtParams = SerializeBizModel(txtParams, request); + + txtParams.Add(METHOD, request.GetApiName()); + txtParams.Add(VERSION, apiVersion); + txtParams.Add(APP_ID, AppId); + txtParams.Add(FORMAT, format); + txtParams.Add(TIMESTAMP, DateTime.Now); + txtParams.Add(ACCESS_TOKEN, accessToken); + txtParams.Add(SIGN_TYPE, signType); + txtParams.Add(TERMINAL_TYPE, request.GetTerminalType()); + txtParams.Add(TERMINAL_INFO, request.GetTerminalInfo()); + txtParams.Add(PROD_CODE, request.GetProdCode()); + txtParams.Add(CHARSET, charset); + + + if (!string.IsNullOrEmpty(request.GetNotifyUrl())) + txtParams.Add(NOTIFY_URL, request.GetNotifyUrl()); + + if (!string.IsNullOrEmpty(appAuthToken)) + txtParams.Add(APP_AUTH_TOKEN, appAuthToken); + + + if (request.GetNeedEncrypt()) + { + if (string.IsNullOrEmpty(txtParams[BIZ_CONTENT])) + throw new AopException("api request Fail ! The reason: encrypt request is not supported!"); + + if (string.IsNullOrEmpty(encyptKey) || string.IsNullOrEmpty(encyptType)) + throw new AopException("encryptType or encryptKey must not null!"); + + if (!"AES".Equals(encyptType)) + throw new AopException("api only support Aes!"); + + var encryptContent = AopUtils.AesEncrypt(encyptKey, txtParams[BIZ_CONTENT], charset); + txtParams.Remove(BIZ_CONTENT); + txtParams.Add(BIZ_CONTENT, encryptContent); + txtParams.Add(ENCRYPT_TYPE, encyptType); + } + + // 添加签名参数 + txtParams.Add(SIGN, AopUtils.SignAopRequest(txtParams, privateKeyPem, charset, keyFromFile, signType)); + + + // 是否需要上传文件 + string body; + + + if (request is IAopUploadRequest) + { + var uRequest = (IAopUploadRequest)request; + var fileParams = AopUtils.CleanupDictionary(uRequest.GetFileParameters()); + body = await webUtils.DoPostAsync(serverUrl + "?" + CHARSET + "=" + charset, txtParams, fileParams, charset); + } + else + { + body = await webUtils.DoPostAsync(serverUrl + "?" + CHARSET + "=" + charset, txtParams, charset); + } + + T rsp = null; + IAopParser parser = null; + if ("xml".Equals(format)) + { + parser = new AopXmlParser(); + rsp = parser.Parse(body, charset); + } + else + { + parser = new AopJsonParser(); + rsp = parser.Parse(body, charset); + } + + var item = parseRespItem(request, body, parser, encyptKey, encyptType, charset); + rsp = parser.Parse(item.realContent, charset); + + CheckResponseSign(request, item.respContent, rsp.IsError, parser, alipayPublicKey, charset, signType, keyFromFile); + + return rsp; + } + + private static ResponseParseItem parseRespItem(IAopRequest request, string respBody, IAopParser parser, + string encryptKey, string encryptType, string charset) where T : AopResponse + { + string realContent = null; + + if (request.GetNeedEncrypt()) + realContent = parser.EncryptSourceData(request, respBody, encryptType, encryptKey, charset); + else + realContent = respBody; + + var item = new ResponseParseItem(); + item.realContent = realContent; + item.respContent = respBody; + + return item; + } + + public static void CheckResponseSign(IAopRequest request, string responseBody, bool isError, + IAopParser parser, string alipayPublicKey, string charset, string signType) where T : AopResponse + { + if (string.IsNullOrEmpty(alipayPublicKey) || string.IsNullOrEmpty(charset)) + return; + + var signItem = parser.GetSignItem(request, responseBody); + if (signItem == null) + throw new AopException("sign check fail: Body is Empty!"); + + if (!isError || + isError && !string.IsNullOrEmpty(signItem.Sign)) + { + var rsaCheckContent = + AlipaySignature.RSACheckContent(signItem.SignSourceDate, signItem.Sign, alipayPublicKey, charset, signType); + if (!rsaCheckContent) + if (!string.IsNullOrEmpty(signItem.SignSourceDate) && signItem.SignSourceDate.Contains("\\/")) + { + var srouceData = signItem.SignSourceDate.Replace("\\/", "/"); + var jsonCheck = AlipaySignature.RSACheckContent(srouceData, signItem.Sign, alipayPublicKey, charset, signType); + if (!jsonCheck) + throw new AopException( + "sign check fail: check Sign and Data Fail JSON also"); + } + else + { + throw new AopException( + "sign check fail: check Sign and Data Fail!"); + } + } + } + + public static void CheckResponseSign(IAopRequest request, string responseBody, bool isError, + IAopParser parser, string alipayPublicKey, string charset, string signType, bool keyFromFile) + where T : AopResponse + { + if (string.IsNullOrEmpty(alipayPublicKey) || string.IsNullOrEmpty(charset)) + return; + + var signItem = parser.GetSignItem(request, responseBody); + if (signItem == null) + throw new AopException("sign check fail: Body is Empty!"); + + if (!isError || + isError && !string.IsNullOrEmpty(signItem.Sign)) + { + var rsaCheckContent = AlipaySignature.RSACheckContent(signItem.SignSourceDate, signItem.Sign, alipayPublicKey, + charset, signType, keyFromFile); + if (!rsaCheckContent) + if (!string.IsNullOrEmpty(signItem.SignSourceDate) && signItem.SignSourceDate.Contains("\\/")) + { + var srouceData = signItem.SignSourceDate.Replace("\\/", "/"); + var jsonCheck = + AlipaySignature.RSACheckContent(srouceData, signItem.Sign, alipayPublicKey, charset, signType, keyFromFile); + if (!jsonCheck) + throw new AopException( + "sign check fail: check Sign and Data Fail JSON also"); + } + else + { + throw new AopException( + "sign check fail: check Sign and Data Fail!"); + } + } + } + + #endregion + + #region IAopClient Members + + public Dictionary FilterPara(SortedDictionary dicArrayPre) + { + var dicArray = new Dictionary(); + foreach (var temp in dicArrayPre) + if (temp.Key.ToLower() != "sign" && temp.Key.ToLower() != "sign_type" && temp.Value != "" && temp.Value != null) + dicArray.Add(temp.Key, temp.Value); + + return dicArray; + } + + public static string CreateLinkStringUrlencode(Dictionary dicArray, Encoding code) + { + var prestr = new StringBuilder(); + foreach (var temp in dicArray) + prestr.Append(temp.Key + "=" + HttpUtility.UrlEncode(temp.Value, code) + "&"); + + //去掉最後一個&字符 + var nLen = prestr.Length; + prestr.Remove(nLen - 1, 1); + + return prestr.ToString(); + } + + #endregion + + #region Model Serialize + + /// + /// + /// + /// + /// + /// + private AopDictionary SerializeBizModel(AopDictionary requestParams, IAopRequest request) where T : AopResponse + { + var result = requestParams; + var isBizContentEmpty = !requestParams.ContainsKey(BIZ_CONTENT) || string.IsNullOrEmpty(requestParams[BIZ_CONTENT]); + if (isBizContentEmpty && request.GetBizModel() != null) + { + var bizModel = request.GetBizModel(); + var content = Serialize(bizModel); + result.Add(BIZ_CONTENT, content); + } + return result; + } + + /// + /// AopObject序列化 + /// + /// + /// + private string Serialize(AopObject obj) + { + JsonSerializerSettings jsetting = new JsonSerializerSettings(); + jsetting.NullValueHandling = NullValueHandling.Ignore; + return JsonConvert.SerializeObject(obj, Formatting.None, jsetting); + } + + #endregion + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/AlipaySignature.cs b/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/AlipaySignature.cs index 4aac54e..a95d2e4 100644 --- a/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/AlipaySignature.cs +++ b/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/AlipaySignature.cs @@ -1,807 +1,807 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace Alipay.AopSdk.Core.Util -{ - public class AlipaySignature - { - /** 默认编码字符集 */ - private static readonly string DEFAULT_CHARSET = "utf-8"; - - public static string GetSignContent(IDictionary parameters) - { - // 第一步:把字典按Key的字母顺序排序 - IDictionary sortedParams = new SortedDictionary(parameters); - var dem = sortedParams.GetEnumerator(); - - // 第二步:把所有参数名和参数值串在一起 - var query = new StringBuilder(""); - while (dem.MoveNext()) - { - var key = dem.Current.Key; - var value = dem.Current.Value; - if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) - query.Append(key).Append("=").Append(value).Append("&"); - } - var content = query.ToString().Substring(0, query.Length - 1); - - return content; - } - - public static string RSASign(IDictionary parameters, string privateKeyPem, string charset, - string signType) - { - var signContent = GetSignContent(parameters); - - return RSASignCharSet(signContent, privateKeyPem, charset, signType); - } - - public static string RSASign(string data, string privateKeyPem, string charset, string signType) - { - return RSASignCharSet(data, privateKeyPem, charset, signType); - } - - ///* - public static string RSASign(IDictionary parameters, string privateKeyPem, string charset, - bool keyFromFile, string signType) - { - var signContent = GetSignContent(parameters); - - return RSASignCharSet(signContent, privateKeyPem, charset, keyFromFile, signType); - } - - public static string RSASign(string data, string privateKeyPem, string charset, string signType, bool keyFromFile) - { - return RSASignCharSet(data, privateKeyPem, charset, keyFromFile, signType); - } - - //*/ - public static string RSASignCharSet(string data, string privateKeyPem, string charset, string signType) - { - RSA rsaCsp = LoadCertificateFile(privateKeyPem, signType); - byte[] dataBytes = null; - if (string.IsNullOrEmpty(charset)) - dataBytes = Encoding.UTF8.GetBytes(data); - else - dataBytes = Encoding.GetEncoding(charset).GetBytes(data); - - var signatureBytes = rsaCsp.SignData(dataBytes, "RSA2".Equals(signType) ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); - - return Convert.ToBase64String(signatureBytes); - } - - - public static string RSASignCharSet(string data, string privateKeyPem, string charset, bool keyFromFile, - string signType) - { - byte[] signatureBytes = null; - try - { - RSA rsaCsp = null; - rsaCsp = keyFromFile ? LoadCertificateFile(privateKeyPem, signType) : LoadCertificateString(privateKeyPem, signType); - - byte[] dataBytes = null; - if (string.IsNullOrEmpty(charset)) - dataBytes = Encoding.UTF8.GetBytes(data); - else - dataBytes = Encoding.GetEncoding(charset).GetBytes(data); - if (null == rsaCsp) - throw new AopException("您使用的私钥格式错误,请检查RSA私钥配置" + ",charset = " + charset); - signatureBytes = rsaCsp.SignData(dataBytes, "RSA2".Equals(signType) ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); - } - catch (Exception ex) - { - throw new AopException($"您使用的私钥格式错误,请检查RSA私钥配置,charset = {charset},异常信息:{ex.Message}", ex); - } - return Convert.ToBase64String(signatureBytes); - } - - - public static bool RSACheckV1(IDictionary parameters, string publicKeyPem, string charset) - { - var sign = parameters["sign"]; - - parameters.Remove("sign"); - parameters.Remove("sign_type"); - var signContent = GetSignContent(parameters); - return RSACheckContent(signContent, sign, publicKeyPem, charset, "RSA"); - } - - public static bool RSACheckV1(IDictionary parameters, string publicKeyPem) - { - var sign = parameters["sign"]; - - parameters.Remove("sign"); - parameters.Remove("sign_type"); - var signContent = GetSignContent(parameters); - - return RSACheckContent(signContent, sign, publicKeyPem, DEFAULT_CHARSET, "RSA"); - } - - public static bool RSA2Check(IDictionary parameters, string publicKeyPem) - { - var sign = parameters["sign"]; - - parameters.Remove("sign"); - parameters.Remove("sign_type"); - var signContent = GetSignContent(parameters); - - return RSACheckContent(signContent, sign, publicKeyPem, DEFAULT_CHARSET, "RSA2"); - } - - public static bool RSACheckV1(IDictionary parameters, string publicKeyPem, string charset, - string signType, bool keyFromFile) - { - var sign = parameters["sign"]; - - parameters.Remove("sign"); - parameters.Remove("sign_type"); - var signContent = GetSignContent(parameters); - return RSACheckContent(signContent, sign, publicKeyPem, charset, signType, keyFromFile); - } - - public static bool RSACheckV2(IDictionary parameters, string publicKeyPem, string charset) - { - var sign = parameters["sign"]; - - parameters.Remove("sign"); - parameters.Remove("sign_type"); - var signContent = GetSignContent(parameters); - - return RSACheckContent(signContent, sign, publicKeyPem, charset, "RSA"); - } - - public static bool RSACheckV2(IDictionary parameters, string publicKeyPem, string charset, - string signType, bool keyFromFile) - { - var sign = parameters["sign"]; - parameters.Remove("sign"); - parameters.Remove("sign_type"); - var signContent = GetSignContent(parameters); - - return RSACheckContent(signContent, sign, publicKeyPem, charset, signType, keyFromFile); - } - - public static RSA CreateRsaProviderFromPublicKey(string publicKeyString,string signType) - { - // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" - byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; - byte[] seq = new byte[15]; - - var x509Key = Convert.FromBase64String(publicKeyString); - - // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ - using (MemoryStream mem = new MemoryStream(x509Key)) - { - using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading - { - byte bt = 0; - ushort twobytes = 0; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - seq = binr.ReadBytes(15); //read the Sequence OID - if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8203) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - bt = binr.ReadByte(); - if (bt != 0x00) //expect null byte next - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - byte lowbyte = 0x00; - byte highbyte = 0x00; - - if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) - lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus - else if (twobytes == 0x8202) - { - highbyte = binr.ReadByte(); //advance 2 bytes - lowbyte = binr.ReadByte(); - } - else - return null; - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order - int modsize = BitConverter.ToInt32(modint, 0); - - int firstbyte = binr.PeekChar(); - if (firstbyte == 0x00) - { //if first byte (highest order) of modulus is zero, don't include it - binr.ReadByte(); //skip this null byte - modsize -= 1; //reduce modulus buffer size by 1 - } - - byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes - - if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data - return null; - int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) - byte[] exponent = binr.ReadBytes(expbytes); - - // ------- create RSACryptoServiceProvider instance and initialize with public key ----- - var rsa = RSA.Create(); - rsa.KeySize = signType == "RSA" ? 1024 : 2048; - RSAParameters rsaKeyInfo = new RSAParameters(); - rsaKeyInfo.Modulus = modulus; - rsaKeyInfo.Exponent = exponent; - rsa.ImportParameters(rsaKeyInfo); - - return rsa; - } - - } - } - - private static bool CompareBytearrays(byte[] a, byte[] b) - { - if (a.Length != b.Length) - return false; - int i = 0; - foreach (byte c in a) - { - if (c != b[i]) - return false; - i++; - } - return true; - } - - public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, - string signType) - { - try - { - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - - var sPublicKeyPem = File.ReadAllText(publicKeyPem); - - var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, signType); - - if ("RSA2".Equals(signType)) - { - - - var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), - Convert.FromBase64String(sign),HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - return bVerifyResultOriginal; - } - else - { - - var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), - Convert.FromBase64String(sign), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); - return bVerifyResultOriginal; - } - } - catch - { - return false; - } - } - - public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, - string signType, bool keyFromFile) - { - try - { - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - - string sPublicKeyPem= publicKeyPem; - - if (keyFromFile) - { - sPublicKeyPem = File.ReadAllText(publicKeyPem); - } - var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, signType); - - if ("RSA2".Equals(signType)) - { - - - var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), - Convert.FromBase64String(sign), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - return bVerifyResultOriginal; - } - else - { - var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), - Convert.FromBase64String(sign), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); - return bVerifyResultOriginal; - } - } - catch - { - return false; - } - } - - public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, - bool keyFromFile) - { - try - { - string sPublicKeyPem= publicKeyPem; - if (keyFromFile) - { - sPublicKeyPem = File.ReadAllText(publicKeyPem); - } - var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, "RSA"); - var sha1 = new SHA1CryptoServiceProvider(); - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), - Convert.FromBase64String(sign),HashAlgorithmName.SHA1,RSASignaturePadding.Pkcs1); - return bVerifyResultOriginal; - } - catch (Exception ex) - { - var s = ex.Message; - return false; - } - } - - public static string CheckSignAndDecrypt(IDictionary parameters, string alipayPublicKey, - string cusPrivateKey, bool isCheckSign, - bool isDecrypt) - { - var charset = parameters["charset"]; - var bizContent = parameters["biz_content"]; - if (isCheckSign) - if (!RSACheckV2(parameters, alipayPublicKey, charset)) - throw new AopException("rsaCheck failure:rsaParams=" + parameters); - - if (isDecrypt) - return RSADecrypt(bizContent, cusPrivateKey, charset, "RSA"); - - return bizContent; - } - - public static string CheckSignAndDecrypt(IDictionary parameters, string alipayPublicKey, - string cusPrivateKey, bool isCheckSign, - bool isDecrypt, string signType, bool keyFromFile) - { - var charset = parameters["charset"]; - var bizContent = parameters["biz_content"]; - if (isCheckSign) - if (!RSACheckV2(parameters, alipayPublicKey, charset, signType, keyFromFile)) - throw new AopException("rsaCheck failure:rsaParams=" + parameters); - - if (isDecrypt) - return RSADecrypt(bizContent, cusPrivateKey, charset, signType, keyFromFile); - - return bizContent; - } - - public static string encryptAndSign(string bizContent, string alipayPublicKey, - string cusPrivateKey, string charset, bool isEncrypt, - bool isSign, string signType, bool keyFromFile) - { - var sb = new StringBuilder(); - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - sb.Append(""); - if (isEncrypt) - { -// 加密 - sb.Append(""); - var encrypted = RSAEncrypt(bizContent, alipayPublicKey, charset, keyFromFile); - sb.Append("" + encrypted + ""); - sb.Append("" + signType + ""); - if (isSign) - { - var sign = RSASign(encrypted, cusPrivateKey, charset, signType, keyFromFile); - sb.Append("" + sign + ""); - sb.Append("" + signType + ""); - } - sb.Append(""); - } - else if (isSign) - { -// 不加密,但需要签名 - sb.Append(""); - sb.Append("" + bizContent + ""); - var sign = RSASign(bizContent, cusPrivateKey, charset, signType, keyFromFile); - sb.Append("" + sign + ""); - sb.Append("" + signType + ""); - sb.Append(""); - } - else - { -// 不加密,不加签 - sb.Append(bizContent); - } - return sb.ToString(); - } - - public static string encryptAndSign(string bizContent, string alipayPublicKey, - string cusPrivateKey, string charset, bool isEncrypt, - bool isSign) - { - var sb = new StringBuilder(); - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - sb.Append(""); - if (isEncrypt) - { -// 加密 - sb.Append(""); - var encrypted = RSAEncrypt(bizContent, alipayPublicKey, charset); - sb.Append("" + encrypted + ""); - sb.Append("RSA"); - if (isSign) - { - var sign = RSASign(encrypted, cusPrivateKey, charset, "RSA"); - sb.Append("" + sign + ""); - sb.Append("RSA"); - } - sb.Append(""); - } - else if (isSign) - { -// 不加密,但需要签名 - sb.Append(""); - sb.Append("" + bizContent + ""); - var sign = RSASign(bizContent, cusPrivateKey, charset, "RSA"); - sb.Append("" + sign + ""); - sb.Append("RSA"); - sb.Append(""); - } - else - { -// 不加密,不加签 - sb.Append(bizContent); - } - return sb.ToString(); - } - - public static string RSAEncrypt(string content, string publicKeyPem, string charset) - { - try - { - var sPublicKeyPEM = File.ReadAllText(publicKeyPem); - var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPEM, "RSA"); - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - var data = Encoding.GetEncoding(charset).GetBytes(content); - var maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制 - if (data.Length <= maxBlockSize) - { - var cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1); - return Convert.ToBase64String(cipherbytes); - } - var plaiStream = new MemoryStream(data); - var crypStream = new MemoryStream(); - var buffer = new byte[maxBlockSize]; - var blockSize = plaiStream.Read(buffer, 0, maxBlockSize); - while (blockSize > 0) - { - var toEncrypt = new byte[blockSize]; - Array.Copy(buffer, 0, toEncrypt, 0, blockSize); - var cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1); - crypStream.Write(cryptograph, 0, cryptograph.Length); - blockSize = plaiStream.Read(buffer, 0, maxBlockSize); - } - - return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None); - } - catch (Exception ex) - { - throw new AopException("EncryptContent = " + content + ",charset = " + charset, ex); - } - } - - public static string RSAEncrypt(string content, string publicKeyPem, string charset, bool keyFromFile) - { - try - { - string sPublicKeyPEM= publicKeyPem; - if (keyFromFile) - { - sPublicKeyPEM = File.ReadAllText(publicKeyPem); - } - var rsa = CreateRsaProviderFromPublicKey(publicKeyPem, "RSA"); - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - var data = Encoding.GetEncoding(charset).GetBytes(content); - var maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制 - if (data.Length <= maxBlockSize) - { - var cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1); - return Convert.ToBase64String(cipherbytes); - } - var plaiStream = new MemoryStream(data); - var crypStream = new MemoryStream(); - var buffer = new byte[maxBlockSize]; - var blockSize = plaiStream.Read(buffer, 0, maxBlockSize); - while (blockSize > 0) - { - var toEncrypt = new byte[blockSize]; - Array.Copy(buffer, 0, toEncrypt, 0, blockSize); - var cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1); - crypStream.Write(cryptograph, 0, cryptograph.Length); - blockSize = plaiStream.Read(buffer, 0, maxBlockSize); - } - - return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None); - } - catch (Exception ex) - { - throw new AopException("EncryptContent = " + content + ",charset = " + charset, ex); - } - } - - public static string RSADecrypt(string content, string privateKeyPem, string charset, string signType) - { - try - { - var rsaCsp = LoadCertificateFile(privateKeyPem, signType); - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - var data = Convert.FromBase64String(content); - var maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制 - if (data.Length <= maxBlockSize) - { - var cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1); - return Encoding.GetEncoding(charset).GetString(cipherbytes); - } - var crypStream = new MemoryStream(data); - var plaiStream = new MemoryStream(); - var buffer = new byte[maxBlockSize]; - var blockSize = crypStream.Read(buffer, 0, maxBlockSize); - while (blockSize > 0) - { - var toDecrypt = new byte[blockSize]; - Array.Copy(buffer, 0, toDecrypt, 0, blockSize); - var cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1); - plaiStream.Write(cryptograph, 0, cryptograph.Length); - blockSize = crypStream.Read(buffer, 0, maxBlockSize); - } - - return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray()); - } - catch (Exception ex) - { - throw new AopException("DecryptContent = " + content + ",charset = " + charset, ex); - } - } - - public static string RSADecrypt(string content, string privateKeyPem, string charset, string signType, - bool keyFromFile) - { - try - { - RSA rsaCsp = null; - if (keyFromFile) - rsaCsp = LoadCertificateFile(privateKeyPem, signType); - else - rsaCsp = LoadCertificateString(privateKeyPem, signType); - if (string.IsNullOrEmpty(charset)) - charset = DEFAULT_CHARSET; - var data = Convert.FromBase64String(content); - var maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制 - if (data.Length <= maxBlockSize) - { - var cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1); - return Encoding.GetEncoding(charset).GetString(cipherbytes); - } - var crypStream = new MemoryStream(data); - var plaiStream = new MemoryStream(); - var buffer = new byte[maxBlockSize]; - var blockSize = crypStream.Read(buffer, 0, maxBlockSize); - while (blockSize > 0) - { - var toDecrypt = new byte[blockSize]; - Array.Copy(buffer, 0, toDecrypt, 0, blockSize); - var cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1); - plaiStream.Write(cryptograph, 0, cryptograph.Length); - blockSize = crypStream.Read(buffer, 0, maxBlockSize); - } - - return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray()); - } - catch (Exception ex) - { - throw new AopException("DecryptContent = " + content + ",charset = " + charset, ex); - } - } - - private static byte[] GetPem(string type, byte[] data) - { - var pem = Encoding.UTF8.GetString(data); - var header = string.Format("-----BEGIN {0}-----\\n", type); - var footer = string.Format("-----END {0}-----", type); - var start = pem.IndexOf(header) + header.Length; - var end = pem.IndexOf(footer, start); - var base64 = pem.Substring(start, end - start); - - return Convert.FromBase64String(base64); - } - - private static RSA LoadCertificateFile(string filename, string signType) - { - using (var fs = File.OpenRead(filename)) - { - var data = new byte[fs.Length]; - byte[] res = null; - fs.Read(data, 0, data.Length); - if (data[0] != 0x30) - res = GetPem("RSA PRIVATE KEY", data); - try - { - var rsa = DecodeRSAPrivateKey(res, signType); - return rsa; - } - catch (Exception ex) - { - } - return null; - } - } - - public static RSA LoadCertificateString(string strKey, string signType) - { - byte[] data = null; - //读取带 - //ata = Encoding.Default.GetBytes(strKey); - data = Convert.FromBase64String(strKey); - //data = GetPem("RSA PRIVATE KEY", data); - try - { - var rsa = DecodeRSAPrivateKey(data, signType); - return rsa; - } - catch (Exception ex) - { - throw new AopException("Alipay.AopSdk.Core.Util.AlipaySignature LoadCertificateString DecodeRSAPrivateKey Error", ex); - } - return null; - } - - private static RSA DecodeRSAPrivateKey(byte[] privkey, string signType) - { - byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; - - // --------- Set up stream to decode the asn.1 encoded RSA private key ------ - var mem = new MemoryStream(privkey); - var binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading - byte bt = 0; - ushort twobytes = 0; - var elems = 0; - try - { - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) //version number - return null; - bt = binr.ReadByte(); - if (bt != 0x00) - return null; - - - //------ all private key components are Integer sequences ---- - elems = GetIntegerSize(binr); - MODULUS = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - E = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - D = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - P = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - Q = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DP = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DQ = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - IQ = binr.ReadBytes(elems); - - - // ------- create RSACryptoServiceProvider instance and initialize with public key ----- - var CspParameters = new CspParameters(); - CspParameters.Flags = CspProviderFlags.UseMachineKeyStore; - - var bitLen = 1024; - if ("RSA2".Equals(signType)) - bitLen = 2048; - - var rsa = RSA.Create(); - rsa.KeySize = bitLen; - var rsAparams = new RSAParameters(); - rsAparams.Modulus = MODULUS; - rsAparams.Exponent = E; - rsAparams.D = D; - rsAparams.P = P; - rsAparams.Q = Q; - rsAparams.DP = DP; - rsAparams.DQ = DQ; - rsAparams.InverseQ = IQ; - rsa.ImportParameters(rsAparams); - return rsa; - } - catch (Exception ex) - { - return null; - } - finally - { - binr.Close(); - } - } - - private static int GetIntegerSize(BinaryReader binr) - { - byte bt = 0; - byte lowbyte = 0x00; - byte highbyte = 0x00; - var count = 0; - bt = binr.ReadByte(); - if (bt != 0x02) //expect integer - return 0; - bt = binr.ReadByte(); - - if (bt == 0x81) - { - count = binr.ReadByte(); // data size in next byte - } - else if (bt == 0x82) - { - highbyte = binr.ReadByte(); // data size in next 2 bytes - lowbyte = binr.ReadByte(); - byte[] modint = {lowbyte, highbyte, 0x00, 0x00}; - count = BitConverter.ToInt32(modint, 0); - } - else - { - count = bt; // we already have the data size - } - - while (binr.ReadByte() == 0x00) - //remove high order zeros in data - count -= 1; - binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte - return count; - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Alipay.AopSdk.Core.Util +{ + public class AlipaySignature + { + /** 默认编码字符集 */ + private static readonly string DEFAULT_CHARSET = "utf-8"; + + public static string GetSignContent(IDictionary parameters) + { + // 第一步:把字典按Key的字母顺序排序 + IDictionary sortedParams = new SortedDictionary(parameters); + var dem = sortedParams.GetEnumerator(); + + // 第二步:把所有参数名和参数值串在一起 + var query = new StringBuilder(""); + while (dem.MoveNext()) + { + var key = dem.Current.Key; + var value = dem.Current.Value; + if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) + query.Append(key).Append("=").Append(value).Append("&"); + } + var content = query.ToString().Substring(0, query.Length - 1); + + return content; + } + + public static string RSASign(IDictionary parameters, string privateKeyPem, string charset, + string signType) + { + var signContent = GetSignContent(parameters); + + return RSASignCharSet(signContent, privateKeyPem, charset, signType); + } + + public static string RSASign(string data, string privateKeyPem, string charset, string signType) + { + return RSASignCharSet(data, privateKeyPem, charset, signType); + } + + ///* + public static string RSASign(IDictionary parameters, string privateKeyPem, string charset, + bool keyFromFile, string signType) + { + var signContent = GetSignContent(parameters); + + return RSASignCharSet(signContent, privateKeyPem, charset, keyFromFile, signType); + } + + public static string RSASign(string data, string privateKeyPem, string charset, string signType, bool keyFromFile) + { + return RSASignCharSet(data, privateKeyPem, charset, keyFromFile, signType); + } + + //*/ + public static string RSASignCharSet(string data, string privateKeyPem, string charset, string signType) + { + RSA rsaCsp = LoadCertificateFile(privateKeyPem, signType); + byte[] dataBytes = null; + if (string.IsNullOrEmpty(charset)) + dataBytes = Encoding.UTF8.GetBytes(data); + else + dataBytes = Encoding.GetEncoding(charset).GetBytes(data); + + var signatureBytes = rsaCsp.SignData(dataBytes, "RSA2".Equals(signType) ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); + + return Convert.ToBase64String(signatureBytes); + } + + + public static string RSASignCharSet(string data, string privateKeyPem, string charset, bool keyFromFile, + string signType) + { + byte[] signatureBytes = null; + try + { + RSA rsaCsp = null; + rsaCsp = keyFromFile ? LoadCertificateFile(privateKeyPem, signType) : LoadCertificateString(privateKeyPem, signType); + + byte[] dataBytes = null; + if (string.IsNullOrEmpty(charset)) + dataBytes = Encoding.UTF8.GetBytes(data); + else + dataBytes = Encoding.GetEncoding(charset).GetBytes(data); + if (null == rsaCsp) + throw new AopException("您使用的私钥格式错误,请检查RSA私钥配置" + ",charset = " + charset); + signatureBytes = rsaCsp.SignData(dataBytes, "RSA2".Equals(signType) ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); + } + catch (Exception ex) + { + throw new AopException($"您使用的私钥格式错误,请检查RSA私钥配置,charset = {charset},异常信息:{ex.Message}", ex); + } + return Convert.ToBase64String(signatureBytes); + } + + + public static bool RSACheckV1(IDictionary parameters, string publicKeyPem, string charset) + { + var sign = parameters["sign"]; + + parameters.Remove("sign"); + parameters.Remove("sign_type"); + var signContent = GetSignContent(parameters); + return RSACheckContent(signContent, sign, publicKeyPem, charset, "RSA"); + } + + public static bool RSACheckV1(IDictionary parameters, string publicKeyPem) + { + var sign = parameters["sign"]; + + parameters.Remove("sign"); + parameters.Remove("sign_type"); + var signContent = GetSignContent(parameters); + + return RSACheckContent(signContent, sign, publicKeyPem, DEFAULT_CHARSET, "RSA"); + } + + public static bool RSA2Check(IDictionary parameters, string publicKeyPem) + { + var sign = parameters["sign"]; + + parameters.Remove("sign"); + parameters.Remove("sign_type"); + var signContent = GetSignContent(parameters); + + return RSACheckContent(signContent, sign, publicKeyPem, DEFAULT_CHARSET, "RSA2"); + } + + public static bool RSACheckV1(IDictionary parameters, string publicKeyPem, string charset, + string signType, bool keyFromFile) + { + var sign = parameters["sign"]; + + parameters.Remove("sign"); + parameters.Remove("sign_type"); + var signContent = GetSignContent(parameters); + return RSACheckContent(signContent, sign, publicKeyPem, charset, signType, keyFromFile); + } + + public static bool RSACheckV2(IDictionary parameters, string publicKeyPem, string charset) + { + var sign = parameters["sign"]; + + parameters.Remove("sign"); + parameters.Remove("sign_type"); + var signContent = GetSignContent(parameters); + + return RSACheckContent(signContent, sign, publicKeyPem, charset, "RSA"); + } + + public static bool RSACheckV2(IDictionary parameters, string publicKeyPem, string charset, + string signType, bool keyFromFile) + { + var sign = parameters["sign"]; + parameters.Remove("sign"); + parameters.Remove("sign_type"); + var signContent = GetSignContent(parameters); + + return RSACheckContent(signContent, sign, publicKeyPem, charset, signType, keyFromFile); + } + + public static RSA CreateRsaProviderFromPublicKey(string publicKeyString,string signType) + { + // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" + byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; + byte[] seq = new byte[15]; + + var x509Key = Convert.FromBase64String(publicKeyString); + + // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ + using (MemoryStream mem = new MemoryStream(x509Key)) + { + using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading + { + byte bt = 0; + ushort twobytes = 0; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + seq = binr.ReadBytes(15); //read the Sequence OID + if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8203) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + bt = binr.ReadByte(); + if (bt != 0x00) //expect null byte next + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + byte lowbyte = 0x00; + byte highbyte = 0x00; + + if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) + lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus + else if (twobytes == 0x8202) + { + highbyte = binr.ReadByte(); //advance 2 bytes + lowbyte = binr.ReadByte(); + } + else + return null; + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order + int modsize = BitConverter.ToInt32(modint, 0); + + int firstbyte = binr.PeekChar(); + if (firstbyte == 0x00) + { //if first byte (highest order) of modulus is zero, don't include it + binr.ReadByte(); //skip this null byte + modsize -= 1; //reduce modulus buffer size by 1 + } + + byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes + + if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data + return null; + int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) + byte[] exponent = binr.ReadBytes(expbytes); + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + var rsa = RSA.Create(); + rsa.KeySize = signType == "RSA" ? 1024 : 2048; + RSAParameters rsaKeyInfo = new RSAParameters(); + rsaKeyInfo.Modulus = modulus; + rsaKeyInfo.Exponent = exponent; + rsa.ImportParameters(rsaKeyInfo); + + return rsa; + } + + } + } + + private static bool CompareBytearrays(byte[] a, byte[] b) + { + if (a.Length != b.Length) + return false; + int i = 0; + foreach (byte c in a) + { + if (c != b[i]) + return false; + i++; + } + return true; + } + + public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, + string signType) + { + try + { + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + + var sPublicKeyPem = File.ReadAllText(publicKeyPem); + + var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, signType); + + if ("RSA2".Equals(signType)) + { + + + var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), + Convert.FromBase64String(sign),HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + return bVerifyResultOriginal; + } + else + { + + var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), + Convert.FromBase64String(sign), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); + return bVerifyResultOriginal; + } + } + catch + { + return false; + } + } + + public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, + string signType, bool keyFromFile) + { + try + { + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + + string sPublicKeyPem= publicKeyPem; + + if (keyFromFile) + { + sPublicKeyPem = File.ReadAllText(publicKeyPem); + } + var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, signType); + + if ("RSA2".Equals(signType)) + { + + + var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), + Convert.FromBase64String(sign), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + return bVerifyResultOriginal; + } + else + { + var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), + Convert.FromBase64String(sign), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); + return bVerifyResultOriginal; + } + } + catch + { + return false; + } + } + + public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, + bool keyFromFile) + { + try + { + string sPublicKeyPem= publicKeyPem; + if (keyFromFile) + { + sPublicKeyPem = File.ReadAllText(publicKeyPem); + } + var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, "RSA"); + var sha1 = new SHA1CryptoServiceProvider(); + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), + Convert.FromBase64String(sign),HashAlgorithmName.SHA1,RSASignaturePadding.Pkcs1); + return bVerifyResultOriginal; + } + catch (Exception ex) + { + var s = ex.Message; + return false; + } + } + + public static string CheckSignAndDecrypt(IDictionary parameters, string alipayPublicKey, + string cusPrivateKey, bool isCheckSign, + bool isDecrypt) + { + var charset = parameters["charset"]; + var bizContent = parameters["biz_content"]; + if (isCheckSign) + if (!RSACheckV2(parameters, alipayPublicKey, charset)) + throw new AopException("rsaCheck failure:rsaParams=" + parameters); + + if (isDecrypt) + return RSADecrypt(bizContent, cusPrivateKey, charset, "RSA"); + + return bizContent; + } + + public static string CheckSignAndDecrypt(IDictionary parameters, string alipayPublicKey, + string cusPrivateKey, bool isCheckSign, + bool isDecrypt, string signType, bool keyFromFile) + { + var charset = parameters["charset"]; + var bizContent = parameters["biz_content"]; + if (isCheckSign) + if (!RSACheckV2(parameters, alipayPublicKey, charset, signType, keyFromFile)) + throw new AopException("rsaCheck failure:rsaParams=" + parameters); + + if (isDecrypt) + return RSADecrypt(bizContent, cusPrivateKey, charset, signType, keyFromFile); + + return bizContent; + } + + public static string encryptAndSign(string bizContent, string alipayPublicKey, + string cusPrivateKey, string charset, bool isEncrypt, + bool isSign, string signType, bool keyFromFile) + { + var sb = new StringBuilder(); + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + sb.Append(""); + if (isEncrypt) + { +// 加密 + sb.Append(""); + var encrypted = RSAEncrypt(bizContent, alipayPublicKey, charset, keyFromFile); + sb.Append("" + encrypted + ""); + sb.Append("" + signType + ""); + if (isSign) + { + var sign = RSASign(encrypted, cusPrivateKey, charset, signType, keyFromFile); + sb.Append("" + sign + ""); + sb.Append("" + signType + ""); + } + sb.Append(""); + } + else if (isSign) + { +// 不加密,但需要签名 + sb.Append(""); + sb.Append("" + bizContent + ""); + var sign = RSASign(bizContent, cusPrivateKey, charset, signType, keyFromFile); + sb.Append("" + sign + ""); + sb.Append("" + signType + ""); + sb.Append(""); + } + else + { +// 不加密,不加签 + sb.Append(bizContent); + } + return sb.ToString(); + } + + public static string encryptAndSign(string bizContent, string alipayPublicKey, + string cusPrivateKey, string charset, bool isEncrypt, + bool isSign) + { + var sb = new StringBuilder(); + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + sb.Append(""); + if (isEncrypt) + { +// 加密 + sb.Append(""); + var encrypted = RSAEncrypt(bizContent, alipayPublicKey, charset); + sb.Append("" + encrypted + ""); + sb.Append("RSA"); + if (isSign) + { + var sign = RSASign(encrypted, cusPrivateKey, charset, "RSA"); + sb.Append("" + sign + ""); + sb.Append("RSA"); + } + sb.Append(""); + } + else if (isSign) + { +// 不加密,但需要签名 + sb.Append(""); + sb.Append("" + bizContent + ""); + var sign = RSASign(bizContent, cusPrivateKey, charset, "RSA"); + sb.Append("" + sign + ""); + sb.Append("RSA"); + sb.Append(""); + } + else + { +// 不加密,不加签 + sb.Append(bizContent); + } + return sb.ToString(); + } + + public static string RSAEncrypt(string content, string publicKeyPem, string charset) + { + try + { + var sPublicKeyPEM = File.ReadAllText(publicKeyPem); + var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPEM, "RSA"); + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + var data = Encoding.GetEncoding(charset).GetBytes(content); + var maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制 + if (data.Length <= maxBlockSize) + { + var cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1); + return Convert.ToBase64String(cipherbytes); + } + var plaiStream = new MemoryStream(data); + var crypStream = new MemoryStream(); + var buffer = new byte[maxBlockSize]; + var blockSize = plaiStream.Read(buffer, 0, maxBlockSize); + while (blockSize > 0) + { + var toEncrypt = new byte[blockSize]; + Array.Copy(buffer, 0, toEncrypt, 0, blockSize); + var cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1); + crypStream.Write(cryptograph, 0, cryptograph.Length); + blockSize = plaiStream.Read(buffer, 0, maxBlockSize); + } + + return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None); + } + catch (Exception ex) + { + throw new AopException("EncryptContent = " + content + ",charset = " + charset, ex); + } + } + + public static string RSAEncrypt(string content, string publicKeyPem, string charset, bool keyFromFile) + { + try + { + string sPublicKeyPEM= publicKeyPem; + if (keyFromFile) + { + sPublicKeyPEM = File.ReadAllText(publicKeyPem); + } + var rsa = CreateRsaProviderFromPublicKey(publicKeyPem, "RSA"); + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + var data = Encoding.GetEncoding(charset).GetBytes(content); + var maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制 + if (data.Length <= maxBlockSize) + { + var cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1); + return Convert.ToBase64String(cipherbytes); + } + var plaiStream = new MemoryStream(data); + var crypStream = new MemoryStream(); + var buffer = new byte[maxBlockSize]; + var blockSize = plaiStream.Read(buffer, 0, maxBlockSize); + while (blockSize > 0) + { + var toEncrypt = new byte[blockSize]; + Array.Copy(buffer, 0, toEncrypt, 0, blockSize); + var cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1); + crypStream.Write(cryptograph, 0, cryptograph.Length); + blockSize = plaiStream.Read(buffer, 0, maxBlockSize); + } + + return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None); + } + catch (Exception ex) + { + throw new AopException("EncryptContent = " + content + ",charset = " + charset, ex); + } + } + + public static string RSADecrypt(string content, string privateKeyPem, string charset, string signType) + { + try + { + var rsaCsp = LoadCertificateFile(privateKeyPem, signType); + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + var data = Convert.FromBase64String(content); + var maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制 + if (data.Length <= maxBlockSize) + { + var cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1); + return Encoding.GetEncoding(charset).GetString(cipherbytes); + } + var crypStream = new MemoryStream(data); + var plaiStream = new MemoryStream(); + var buffer = new byte[maxBlockSize]; + var blockSize = crypStream.Read(buffer, 0, maxBlockSize); + while (blockSize > 0) + { + var toDecrypt = new byte[blockSize]; + Array.Copy(buffer, 0, toDecrypt, 0, blockSize); + var cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1); + plaiStream.Write(cryptograph, 0, cryptograph.Length); + blockSize = crypStream.Read(buffer, 0, maxBlockSize); + } + + return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray()); + } + catch (Exception ex) + { + throw new AopException("DecryptContent = " + content + ",charset = " + charset, ex); + } + } + + public static string RSADecrypt(string content, string privateKeyPem, string charset, string signType, + bool keyFromFile) + { + try + { + RSA rsaCsp = null; + if (keyFromFile) + rsaCsp = LoadCertificateFile(privateKeyPem, signType); + else + rsaCsp = LoadCertificateString(privateKeyPem, signType); + if (string.IsNullOrEmpty(charset)) + charset = DEFAULT_CHARSET; + var data = Convert.FromBase64String(content); + var maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制 + if (data.Length <= maxBlockSize) + { + var cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1); + return Encoding.GetEncoding(charset).GetString(cipherbytes); + } + var crypStream = new MemoryStream(data); + var plaiStream = new MemoryStream(); + var buffer = new byte[maxBlockSize]; + var blockSize = crypStream.Read(buffer, 0, maxBlockSize); + while (blockSize > 0) + { + var toDecrypt = new byte[blockSize]; + Array.Copy(buffer, 0, toDecrypt, 0, blockSize); + var cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1); + plaiStream.Write(cryptograph, 0, cryptograph.Length); + blockSize = crypStream.Read(buffer, 0, maxBlockSize); + } + + return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray()); + } + catch (Exception ex) + { + throw new AopException("DecryptContent = " + content + ",charset = " + charset, ex); + } + } + + private static byte[] GetPem(string type, byte[] data) + { + var pem = Encoding.UTF8.GetString(data); + var header = string.Format("-----BEGIN {0}-----\\n", type); + var footer = string.Format("-----END {0}-----", type); + var start = pem.IndexOf(header) + header.Length; + var end = pem.IndexOf(footer, start); + var base64 = pem.Substring(start, end - start); + + return Convert.FromBase64String(base64); + } + + private static RSA LoadCertificateFile(string filename, string signType) + { + using (var fs = File.OpenRead(filename)) + { + var data = new byte[fs.Length]; + byte[] res = null; + fs.Read(data, 0, data.Length); + if (data[0] != 0x30) + res = GetPem("RSA PRIVATE KEY", data); + try + { + var rsa = DecodeRSAPrivateKey(res, signType); + return rsa; + } + catch (Exception ex) + { + } + return null; + } + } + + public static RSA LoadCertificateString(string strKey, string signType) + { + byte[] data = null; + //读取带 + //ata = Encoding.Default.GetBytes(strKey); + data = Convert.FromBase64String(strKey); + //data = GetPem("RSA PRIVATE KEY", data); + try + { + var rsa = DecodeRSAPrivateKey(data, signType); + return rsa; + } + catch (Exception ex) + { + throw new AopException("Alipay.AopSdk.Core.Util.AlipaySignature LoadCertificateString DecodeRSAPrivateKey Error", ex); + } + return null; + } + + private static RSA DecodeRSAPrivateKey(byte[] privkey, string signType) + { + byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; + + // --------- Set up stream to decode the asn.1 encoded RSA private key ------ + var mem = new MemoryStream(privkey); + var binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading + byte bt = 0; + ushort twobytes = 0; + var elems = 0; + try + { + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + return null; + bt = binr.ReadByte(); + if (bt != 0x00) + return null; + + + //------ all private key components are Integer sequences ---- + elems = GetIntegerSize(binr); + MODULUS = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + E = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + D = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + P = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + Q = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DP = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DQ = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + IQ = binr.ReadBytes(elems); + + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + var CspParameters = new CspParameters(); + CspParameters.Flags = CspProviderFlags.UseMachineKeyStore; + + var bitLen = 1024; + if ("RSA2".Equals(signType)) + bitLen = 2048; + + var rsa = RSA.Create(); + rsa.KeySize = bitLen; + var rsAparams = new RSAParameters(); + rsAparams.Modulus = MODULUS; + rsAparams.Exponent = E; + rsAparams.D = D; + rsAparams.P = P; + rsAparams.Q = Q; + rsAparams.DP = DP; + rsAparams.DQ = DQ; + rsAparams.InverseQ = IQ; + rsa.ImportParameters(rsAparams); + return rsa; + } + catch (Exception ex) + { + return null; + } + finally + { + binr.Close(); + } + } + + private static int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + byte lowbyte = 0x00; + byte highbyte = 0x00; + var count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) //expect integer + return 0; + bt = binr.ReadByte(); + + if (bt == 0x81) + { + count = binr.ReadByte(); // data size in next byte + } + else if (bt == 0x82) + { + highbyte = binr.ReadByte(); // data size in next 2 bytes + lowbyte = binr.ReadByte(); + byte[] modint = {lowbyte, highbyte, 0x00, 0x00}; + count = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; // we already have the data size + } + + while (binr.ReadByte() == 0x00) + //remove high order zeros in data + count -= 1; + binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte + return count; + } + } +} diff --git a/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/RSAHelper.cs b/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/RSAHelper.cs index 8ada138..7fb51af 100644 --- a/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/RSAHelper.cs +++ b/Infrastructure/ServiceClient/Alipay.AopSdk.Core/Util/RSAHelper.cs @@ -1,320 +1,320 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace RSATest -{ - /// - /// RSA加解密 使用OpenSSL的公钥加密/私钥解密 - /// - /// 公私钥请使用openssl生成 ssh-keygen -t rsa 命令生成的公钥私钥是不行的 - /// - /// 作者:李志强 - /// 时间:2017年10月30日15:50:14 - /// QQ:501232752 - /// - public class RSAHelper - { - private readonly RSA _privateKeyRsaProvider; - private readonly RSA _publicKeyRsaProvider; - private readonly HashAlgorithmName _hashAlgorithmName; - private readonly Encoding _encoding; - - /// - /// 实例化RSAHelper - /// - /// 加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048 - /// 编码类型 - /// 私钥 - /// 公钥 - public RSAHelper(RSAType rsaType, Encoding encoding, string privateKey, string publicKey = null) - { - _encoding = encoding; - if (!string.IsNullOrEmpty(privateKey)) - { - _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey); - } - - if (!string.IsNullOrEmpty(publicKey)) - { - _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey); - } - - _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256; - } - - #region 使用私钥签名 - - /// - /// 使用私钥签名 - /// - /// 原始数据 - /// - public string Sign(string data) - { - byte[] dataBytes = _encoding.GetBytes(data); - - var signatureBytes = _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); - - return Convert.ToBase64String(signatureBytes); - } - - #endregion - - #region 使用公钥验证签名 - - /// - /// 使用公钥验证签名 - /// - /// 原始数据 - /// 签名 - /// - public bool Verify(string data,string sign) - { - byte[] dataBytes = _encoding.GetBytes(data); - byte[] signBytes = Convert.FromBase64String(sign); - - var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); - - return verify; - } - - #endregion - - #region 解密 - - public string Decrypt(string cipherText) - { - if (_privateKeyRsaProvider == null) - { - throw new Exception("_privateKeyRsaProvider is null"); - } - return Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.Pkcs1)); - } - - #endregion - - #region 加密 - - public string Encrypt(string text) - { - if (_publicKeyRsaProvider == null) - { - throw new Exception("_publicKeyRsaProvider is null"); - } - return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.Pkcs1)); - } - - #endregion - - #region 使用私钥创建RSA实例 - - public RSA CreateRsaProviderFromPrivateKey(string privateKey) - { - var privateKeyBits = Convert.FromBase64String(privateKey); - - var rsa = RSA.Create(); - var rsaParameters = new RSAParameters(); - - using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))) - { - byte bt = 0; - ushort twobytes = 0; - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) - binr.ReadByte(); - else if (twobytes == 0x8230) - binr.ReadInt16(); - else - throw new Exception("Unexpected value read binr.ReadUInt16()"); - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) - throw new Exception("Unexpected version"); - - bt = binr.ReadByte(); - if (bt != 0x00) - throw new Exception("Unexpected value read binr.ReadByte()"); - - rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr)); - rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr)); - } - - rsa.ImportParameters(rsaParameters); - return rsa; - } - - #endregion - - #region 使用公钥创建RSA实例 - - public RSA CreateRsaProviderFromPublicKey(string publicKeyString) - { - // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" - byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; - byte[] seq = new byte[15]; - - var x509Key = Convert.FromBase64String(publicKeyString); - - // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ - using (MemoryStream mem = new MemoryStream(x509Key)) - { - using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading - { - byte bt = 0; - ushort twobytes = 0; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - seq = binr.ReadBytes(15); //read the Sequence OID - if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8203) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - bt = binr.ReadByte(); - if (bt != 0x00) //expect null byte next - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - byte lowbyte = 0x00; - byte highbyte = 0x00; - - if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) - lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus - else if (twobytes == 0x8202) - { - highbyte = binr.ReadByte(); //advance 2 bytes - lowbyte = binr.ReadByte(); - } - else - return null; - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order - int modsize = BitConverter.ToInt32(modint, 0); - - int firstbyte = binr.PeekChar(); - if (firstbyte == 0x00) - { //if first byte (highest order) of modulus is zero, don't include it - binr.ReadByte(); //skip this null byte - modsize -= 1; //reduce modulus buffer size by 1 - } - - byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes - - if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data - return null; - int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) - byte[] exponent = binr.ReadBytes(expbytes); - - // ------- create RSACryptoServiceProvider instance and initialize with public key ----- - var rsa = RSA.Create(); - RSAParameters rsaKeyInfo = new RSAParameters - { - Modulus = modulus, - Exponent = exponent - }; - rsa.ImportParameters(rsaKeyInfo); - - return rsa; - } - - } - } - - #endregion - - #region 导入密钥算法 - - private int GetIntegerSize(BinaryReader binr) - { - byte bt = 0; - int count = 0; - bt = binr.ReadByte(); - if (bt != 0x02) - return 0; - bt = binr.ReadByte(); - - if (bt == 0x81) - count = binr.ReadByte(); - else - if (bt == 0x82) - { - var highbyte = binr.ReadByte(); - var lowbyte = binr.ReadByte(); - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; - count = BitConverter.ToInt32(modint, 0); - } - else - { - count = bt; - } - - while (binr.ReadByte() == 0x00) - { - count -= 1; - } - binr.BaseStream.Seek(-1, SeekOrigin.Current); - return count; - } - - private bool CompareBytearrays(byte[] a, byte[] b) - { - if (a.Length != b.Length) - return false; - int i = 0; - foreach (byte c in a) - { - if (c != b[i]) - return false; - i++; - } - return true; - } - - #endregion - - } - - /// - /// RSA算法类型 - /// - public enum RSAType - { - /// - /// SHA1 - /// - RSA = 0, - /// - /// RSA2 密钥长度至少为2048 - /// SHA256 - /// - RSA2 - } +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace RSATest +{ + /// + /// RSA加解密 使用OpenSSL的公钥加密/私钥解密 + /// + /// 公私钥请使用openssl生成 ssh-keygen -t rsa 命令生成的公钥私钥是不行的 + /// + /// 作者:李志强 + /// 时间:2017年10月30日15:50:14 + /// QQ:501232752 + /// + public class RSAHelper + { + private readonly RSA _privateKeyRsaProvider; + private readonly RSA _publicKeyRsaProvider; + private readonly HashAlgorithmName _hashAlgorithmName; + private readonly Encoding _encoding; + + /// + /// 实例化RSAHelper + /// + /// 加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048 + /// 编码类型 + /// 私钥 + /// 公钥 + public RSAHelper(RSAType rsaType, Encoding encoding, string privateKey, string publicKey = null) + { + _encoding = encoding; + if (!string.IsNullOrEmpty(privateKey)) + { + _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey); + } + + if (!string.IsNullOrEmpty(publicKey)) + { + _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey); + } + + _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256; + } + + #region 使用私钥签名 + + /// + /// 使用私钥签名 + /// + /// 原始数据 + /// + public string Sign(string data) + { + byte[] dataBytes = _encoding.GetBytes(data); + + var signatureBytes = _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); + + return Convert.ToBase64String(signatureBytes); + } + + #endregion + + #region 使用公钥验证签名 + + /// + /// 使用公钥验证签名 + /// + /// 原始数据 + /// 签名 + /// + public bool Verify(string data,string sign) + { + byte[] dataBytes = _encoding.GetBytes(data); + byte[] signBytes = Convert.FromBase64String(sign); + + var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); + + return verify; + } + + #endregion + + #region 解密 + + public string Decrypt(string cipherText) + { + if (_privateKeyRsaProvider == null) + { + throw new Exception("_privateKeyRsaProvider is null"); + } + return Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.Pkcs1)); + } + + #endregion + + #region 加密 + + public string Encrypt(string text) + { + if (_publicKeyRsaProvider == null) + { + throw new Exception("_publicKeyRsaProvider is null"); + } + return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.Pkcs1)); + } + + #endregion + + #region 使用私钥创建RSA实例 + + public RSA CreateRsaProviderFromPrivateKey(string privateKey) + { + var privateKeyBits = Convert.FromBase64String(privateKey); + + var rsa = RSA.Create(); + var rsaParameters = new RSAParameters(); + + using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))) + { + byte bt = 0; + ushort twobytes = 0; + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) + binr.ReadByte(); + else if (twobytes == 0x8230) + binr.ReadInt16(); + else + throw new Exception("Unexpected value read binr.ReadUInt16()"); + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) + throw new Exception("Unexpected version"); + + bt = binr.ReadByte(); + if (bt != 0x00) + throw new Exception("Unexpected value read binr.ReadByte()"); + + rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr)); + } + + rsa.ImportParameters(rsaParameters); + return rsa; + } + + #endregion + + #region 使用公钥创建RSA实例 + + public RSA CreateRsaProviderFromPublicKey(string publicKeyString) + { + // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" + byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; + byte[] seq = new byte[15]; + + var x509Key = Convert.FromBase64String(publicKeyString); + + // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ + using (MemoryStream mem = new MemoryStream(x509Key)) + { + using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading + { + byte bt = 0; + ushort twobytes = 0; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + seq = binr.ReadBytes(15); //read the Sequence OID + if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8203) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + bt = binr.ReadByte(); + if (bt != 0x00) //expect null byte next + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + byte lowbyte = 0x00; + byte highbyte = 0x00; + + if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) + lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus + else if (twobytes == 0x8202) + { + highbyte = binr.ReadByte(); //advance 2 bytes + lowbyte = binr.ReadByte(); + } + else + return null; + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order + int modsize = BitConverter.ToInt32(modint, 0); + + int firstbyte = binr.PeekChar(); + if (firstbyte == 0x00) + { //if first byte (highest order) of modulus is zero, don't include it + binr.ReadByte(); //skip this null byte + modsize -= 1; //reduce modulus buffer size by 1 + } + + byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes + + if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data + return null; + int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) + byte[] exponent = binr.ReadBytes(expbytes); + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + var rsa = RSA.Create(); + RSAParameters rsaKeyInfo = new RSAParameters + { + Modulus = modulus, + Exponent = exponent + }; + rsa.ImportParameters(rsaKeyInfo); + + return rsa; + } + + } + } + + #endregion + + #region 导入密钥算法 + + private int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + int count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) + return 0; + bt = binr.ReadByte(); + + if (bt == 0x81) + count = binr.ReadByte(); + else + if (bt == 0x82) + { + var highbyte = binr.ReadByte(); + var lowbyte = binr.ReadByte(); + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; + count = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; + } + + while (binr.ReadByte() == 0x00) + { + count -= 1; + } + binr.BaseStream.Seek(-1, SeekOrigin.Current); + return count; + } + + private bool CompareBytearrays(byte[] a, byte[] b) + { + if (a.Length != b.Length) + return false; + int i = 0; + foreach (byte c in a) + { + if (c != b[i]) + return false; + i++; + } + return true; + } + + #endregion + + } + + /// + /// RSA算法类型 + /// + public enum RSAType + { + /// + /// SHA1 + /// + RSA = 0, + /// + /// RSA2 密钥长度至少为2048 + /// SHA256 + /// + RSA2 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/Alipay.AopSdk.Core/obj/Debug/netstandard2.0/Alipay.AopSdk.Core.csprojAssemblyReference.cache b/Infrastructure/ServiceClient/Alipay.AopSdk.Core/obj/Debug/netstandard2.0/Alipay.AopSdk.Core.csprojAssemblyReference.cache index 1e180eafb0369748b2ffda6eba9e941a3b87eb5c..e0a4104236d801e0ea739400b00f39c1989843cf 100644 GIT binary patch delta 13522 zcmZu%30zax(&r?(!VMt-35zIO>OwUjShWI)yA@p6R5n3~fQmv`Roub0DpsvJYFc+~ zwRNqERr}gk+G<-DT%NVA)wXtDwCn5hv#owJ=O)+Omv?_UH}}lUf9A}YbM85F*?C{H z^SFEv+QpStRJ8Mk;@+#754_!Zz94#ec{RbHF4JJ~ zsX&RXg-D<01Ycy-z@W}{fmC#`U$vb1L94C`VxxWF(*>cBK0si4_^gi|hWGFj3@C4d zVp9~H^VY$RC;AHh*wW0QMTy22xW^aiR3Yjr>=+`|^=ztiN)MxH@ z7{s-+!_jU=))77*Y$xA&dnfE;73}TOiG^bi=CBB;>0=inQCA8Ec|7_BZ!M2k6gDs9 z9f{Owp{UX6Of*M47NBD$IOuDKV|u;t7>d?FrgfT6*+j0}8Tv<7Vfb}$>$)hxbwL^I zWoJ9|k7*Al0vh0_=1{2{-6$@xkA<)p?q6$+H~f9WU*bK^ktp~*t$L9Lr8@#uVOL8a zs+}L}0rn@WU{jbL-ibCaGyIZZ_s_OfOfMTdy}F`|I}^*{5#hmFXCLCY(wp7GVO?-F zce6Ukz~W(`xt1khOq+yWXk-a?z7n?R`~rqlRaRHkmDiV7**W49m={&>w!aqkT{B4C z>x~Mv5ba%|tCg(w!3I}U{ovgBc9L9QY%b&pbF*E4_9ScyUCjES*Yj8+Wcb@*MDHLW z31ue3g#l)`8fIb1&^Ev>SU5fouy$0ry@q>XVf`d>-uWqJy$0 z3@ja=ma+`qyWX1iEEDA#Abm+Y2_TCbqV#FJ>Ce+=40>1QoUwYCZSKd$qRm;bH(X@n zco)y~4HL3a>IJaHKMgBywwIuC`1tI6-Ief2P)-kG08au??f)3<9^jPlY<{NtQa z0<>Y%67;W*riFGyoii~f;=(eOmGEfn>D*o@MX`DE@+6wFSQ&Si0 zG@7hzG7lEbrDFs2Yzq2S4N*D~R?O8)At;B|SO=RbC&GY_wX_}z(_{x}sfY|SunL}$ zZ#?eLDsh#V%j|F)$s}TQ;-4H zdmCU;;u83B@HBYSs$(eFB56f+=I9b0D@ni&PceJnec0)&Ir`$cLFmmNOBle()4`|=bjBH(1sPIg4`)aYU&}Ca z1MM?SFf-2&24$M~XiUm9!wVz)U`wXSyXo2ZHX4G@|#$pl@+y+bc@1K##F8 z-mM?a<+k$2N`5{+*2D+p@K`C}cgC8$Axz}`4jvc7eH}E;%tyF-oYa?HM9a`C)#H`oz2d_%&aG=`r5K z=SRkP>ABhYydR%wq`nz1xgA^}rB`x+G#d2QaK8xXS}5TZ6`Iub zK}L9{(5%L}2`z==`E$SoGw}9zDBhFodBri^zKO?S)v;oK)}DEJ`Qv}qE?VW~)dgxw!ohsPM|_)eETSp)zt`h^cwZ`XpiYBa-L={DC|0`$}L7 zO54C(69GGGQpSYIs8(#(j+s?gUsf5HTUjCYC+Go01|p$e(j^6=)gAwBCH@e2zb+dd zlxuOH^>9*`#9;*S6p`T`ILF*^jw*3RK;h&HLxk+#NYw5I7Z!9&7Do}~~sOeOyMleX=-)z{e69=V4*DyMV(2QXPCf#*~ZxiS=%)mfl!WuZUoC39AzYB<;?2h)iYQtPd(*!h%h=q*dV zh*Ck2&^^dsT#rA+4Ma8~VGuq&mgN#__5PHgzOu$vWVVxX3qG7~#UsL;$!UguGXE9i z2g8AG!LWRW6$b4t3Qv-G`;n)2=4IA&dwqFj8D!72g73{7csNs!N7{sLf$;ZE9rSIm zz20zS+1z|ysZ;Gt-DP+tJ zf=zQRux)l;hY@o7*C_8=-*U<(!sK~YICE)?Zj>x>M+NrlQ4l_#+^M)NL=-+5y9l*Y=1C%#fp>o?q@NLq=-5XI5 z-(+Dn_~}Ljlz)-}XRs9@0)E8SB8akfBD9cP*@Y0bmzPgvT}f4KMfpTHw9qP!l%JX= zrx@~O$q1B;gp4aeSPszk7bEdej3N?^1f|@@>(*axudSQ-;P%?;jM5W1O#G&-$O0L@ z3$_P1vrn!vK=z{kaP35%j!lpS&G5t82sFq2^fFdN5NrNfu5~ zL(QHJ)>M)iL1ZKn0Iz%9|WK5N%5T`!=}UXgKu)_Bx=`e7%V6e;TrKm1t z|Jn0CQ1`qA0xnEc3Dtc1`97L{)1YWsP$HjnoK$*rZMA#XRw=ty4f~d*``3_8t!%m$ z&MuF%)?uSqPh`3WfxR#Y zy1bR=&t}L{jVKiY{f~#h{54jv9xwK7ka>&Y^VPuyzVbJtn79;)7mQmQ3s1l(y_U_E zC0D5FwJC|NB+*q|G<01o{CYV(Y_2T07Ta9|Le(Q^TW5tid&j~Qhu+_{5aQ-0jx$#{ ztl<4>Zgiup{}SrAcMd2&u46r{vdRu)U$8>%obiT*vgB@*41|PhfzbX%E5u(b)IB5f z4yqx3W;gigMGL^piYFGyjAN)|bi$9N3ehloNmVH=Yi}S&{0EUYkuZ39eJsQsn4nuC z>$a*P;%WyNxdC5ZS0{8{DlI0mhBB)lGwdjZeYT6}Zwb**_xYwiCkDA8~7L>ecftg;$>_QuMoLPl8VXK%{`sE zD%0tP8iv$nV~NhSH~&NpA5V@-x=B#ChajhMrR<8&=DBU!Zy{?f_yT zew!7lOQyrQZ3a9Z#I=Qju-yuu_v-*pwWa2-lQn)p4ObRFs@#dck~6;%`5lR}hraF} zX_X%R2f8dQ)V(0X-&4bZk4&&+2fYJm2WG+-&U%^sPz`q*!eHo53l`LVX$Xe#vr^&4 z&PS=5HodZ}w!EYcd|t6a`ir@S&9bhahGtgA#t=A7yp6@aTViUNda9wu*|f3efDf!XHx#Ve4)^ zmgW-%2Wo7kWwr3(ZYz3?kC3@!l;KqwJsusya{J8)Xx?L`ZsV`7jt*A|yZ?K;@gz#} zh2MRApDjEI=)8z-tu|~B)(>a&94{h z4$GGEHLzk+XK7_BK&xUQ5-(r}Vp$HX+7tzb&B=c3h%9f@KL#i_b~$eL9e=&>ORjE87P5x1ceyu1N} z{T2^){-YT`1A#pE-H%>24P;0W_r>mUU!;ut91R5QNQ;|577LG3aHIt{ zwU@`hq{Di6ygnM%9k#HE(7is=^*({**Y!}+YJ_>iv|xMPird&pe(Zz{xLN~0o{NO= zBNjFRemd6?O3tSkK9JcjqP=L`MTKkl`gP`r6<^=@2&NgMbSGu8tr{p^W|RhLJF18; zA@PD2kH*4*Wh3!Tb6OUCMFZ74LSfu73!Kz0&$7Jy{0{xuGZ4cydcjQh=HLAPzHR%(lmX*|xt|~375WgYpZ;5>8VIbJuK#fs-@FtAx+^8s=6$`CS8z>x10NCx3Y0ite~RO5Fd` zKmw$f-Y2EovhRQ5W02E#UjL~rufz|aGs^13zmO&VP2>UTC{l#E8}d>bVggKUvO&^m z?MRjIXh%vrN>PmsqK1foM2UI-zT2Db|M0=>P3I%PtL53gU(0@rFzj?^tCo;_iRe7w zeshQWRS9Q6=@Lji9SKRB^su!eS@*4s>@UFRc^$0*gxa1+pa=Ai?$AFdp@UI6A2Q~J zz+3ZD;Q2PKDnx*lm7SA939AEO(kAul+K5{i6K}4)x)P}hmP)oEv`yKPPx6bYG$3-m4Leui9wDrM&MU!s3E;PWGVD389s2|-x;AK7%XzB2 zqD;zKu{YuNA=1|a{FFQRNhNrq0N<@BGW;%wH3`+F3fjsBZL*k5kQO3V58U_Mao<(q z4iuoep%#+=r4mCzq* z)QJ-ayogA#hlMTf7B(v_l)%(8MFy%(OW#ULQ9Ft%sQri3Rf}Z=IElz)57ZajQ5{Ou zsRCR$JPm&MSSw6J#bCgKVzN})sU$Qz64xbdnpU}^tyH4b2ryylIDxtl0cn%F47yVW@Xn@GuD4$o!|D((BX zB1_zc#0v^;#KQ6FvG{GFy)3!|IU!hr4gtSU=vYg?zHp@IjIzXD0TOlv!}(7v@bs>N z@L-wIf~u}}2ma+xT~$RHY`|b)`oi`d($63lid#uq;G#*<+y@Yi0u>>i1C9Fo{i{ z^px!3#Ac(oH}rK*X)aCWINKBZGKTZ%+nSO;PU@>Kov!FoVL^G-_aTBnU%P<4d;|hM z9}`FiTDrt{X)N(FOyqdMTkG6FEbQTx-f{~|I>Nsc*l=j3 z{JD*c!W+M|LC@bsc9(50|pbZ;1H)wlpMaVE1;x(SMs@<)1$A z`M=LP4yc*P0@g;e4#xIadgDf2dMr>4(Z7eVP-4T*|8!RwA_*XnzI!O$i-w*LLRa5= z9DewwDX^Q1-8Lui;n~=mnJfmH{tSUj_fGMy_vT)Vz0U)Pe}zbp7MG4Z7FPJ@y-*!} zoS;j^hN@xUXTd5%Dwn5k5lRaoxl_v)N@x?)ny~>o1cZj(!5`37UI5 z1NXY*8O_O6R78+%X9mmSA+Z`KC;{}9d6#tjzVxpT?c9HdU2r@US!f7Vz&*oynKUV*j8y#QPRJQ`l?Eh)-Q+$} zEnDg1r;crE7RtUPw&RKxKi*=G94cR@Prp*TkqXXAwcq)WINIq@4rhqU;N>{z$96fM z^+SZ+fB31qHMCS4eQ*uunsT>A4WBA{E^jor_;PIhHI9TNT#bVyzCQQUJv^|?j(5Vaa|x1WzxG^Hr(%hYL7@zn94l(LMbnWG$Uc%F zpORFL8qqqgQljIw5v}oGM}yeoj>{cclX0BOU_xuZ?0tRH`mk)?IbSNcN|{$kmO>pi zd{g2=9a!Shkh@#zNC-d|%eg%xRaTXOo~EIN+>sZ`E~!|9({HM7y8HcC2lf#ybB?QF z=p~QVG6`_G2jB_`aA7CreEY1IBsX|SZj?qsN0mvbH!oqC%C~CPX&J6#Cpb(XmGP7?A4st~BblLk zoRWXmY38B?KqWP$z$Ga^u1Nq?DO2)qH~~JB0I1-l6u2#U@QnmO^(rO*J05IySK?E} zNy-12_`wN`%0>Tc4P#Ui@(k=>5*`(Rlz0!gJ(XJElaGoyoG(v4LEw{5>sV!GyNpzw zQR?di^e2>a1F68G9J()@c?gp;P@s$&Z%!T_rwMFqHfl$-7v| zJ{2XD{GO8i-jY7m6_otG0^e39bH17C14=#>3tan3+Gx;!x(tIQi&OwmY7BL>^~4^h zN_;xgEBPZOk1`}aoz9i~EXTnoQ9noG(@|T=&*Oa61ZlkJpv?L5coj<#DUEu{$M&4D*1FobS0{L2pJs! zM2S13dh$KJIoz%e3;CBqkW9y1N;K{|MBWctN;K2 delta 13648 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 diff --git a/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoClient.csproj b/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoClient.csproj index 46f011d..071881e 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoClient.csproj +++ b/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoClient.csproj @@ -1,15 +1,15 @@ - - - - netcoreapp2.2 - - - - - - - - - - - + + + + netcoreapp2.2 + + + + + + + + + + + diff --git a/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoHttpClient.cs b/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoHttpClient.cs index 369579f..e6857ee 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoHttpClient.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/BaseInfoHttpClient.cs @@ -1,24 +1,24 @@ -using System.Net.Http; - -namespace ServiceClient -{ - public class BaseInfoHttpClient - { - private IHttpClientFactory _httpClientFactory; - - internal static string _BaseUrl = ""; - - public string BaseUrl => _BaseUrl; - - public BaseInfoHttpClient(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - public HttpClient CreateHttpClient() - { - return _httpClientFactory.CreateClient(); - } - - } +using System.Net.Http; + +namespace ServiceClient +{ + public class BaseInfoHttpClient + { + private IHttpClientFactory _httpClientFactory; + + internal static string _BaseUrl = ""; + + public string BaseUrl => _BaseUrl; + + public BaseInfoHttpClient(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public HttpClient CreateHttpClient() + { + return _httpClientFactory.CreateClient(); + } + + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/IServiceCollectionExtension.cs b/Infrastructure/ServiceClient/BaseInfoClient/IServiceCollectionExtension.cs index c7b4539..7850ec3 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/IServiceCollectionExtension.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/IServiceCollectionExtension.cs @@ -1,14 +1,14 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace ServiceClient -{ - public static class IServiceCollectionExtension - { - public static void AddBaseInfoClient(this IServiceCollection service, string baseUrl) - { - BaseInfoHttpClient._BaseUrl = baseUrl; - - service.AddSingleton(); - } - } +using Microsoft.Extensions.DependencyInjection; + +namespace ServiceClient +{ + public static class IServiceCollectionExtension + { + public static void AddBaseInfoClient(this IServiceCollection service, string baseUrl) + { + BaseInfoHttpClient._BaseUrl = baseUrl; + + service.AddSingleton(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/HouseholderItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/HouseholderItem.cs index 32f3920..cf4a91e 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/HouseholderItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/HouseholderItem.cs @@ -1,13 +1,13 @@ -using Newtonsoft.Json; - -namespace ServiceClient.Response.Householder -{ - public class HouseholderItem - { - [JsonProperty("ID")] public int UserId { get; set; } - - [JsonProperty("name")] public string Name { get; set; } = ""; - - [JsonProperty("mobile")] public string Mobile { get; set; } = ""; - } +using Newtonsoft.Json; + +namespace ServiceClient.Response.Householder +{ + public class HouseholderItem + { + [JsonProperty("ID")] public int UserId { get; set; } + + [JsonProperty("name")] public string Name { get; set; } = ""; + + [JsonProperty("mobile")] public string Mobile { get; set; } = ""; + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryAllHouseholdersByOwnerIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryAllHouseholdersByOwnerIdResponse.cs index d7a1cfe..c235a77 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryAllHouseholdersByOwnerIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryAllHouseholdersByOwnerIdResponse.cs @@ -1,56 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; -using ServiceClient.Response.Householder; - -namespace ServiceClient.Response.Householder -{ - - - public class QueryAllHouseholdersByOwnerIdResponse - { - [JsonProperty("userData")] - public List HouseholderItems { get; set; } = new List(); - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryAllHouseholdersByOwnerIdExtension - { - public static async Task> QueryAllHouseholdersByOwnerId(this BaseInfoHttpClient client - , int ownerId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBasePerson?OwnerID={ownerId}&Data.IsGetUser=true"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.HouseholderItems; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户信息失败"); - } - else - { - return new List(); - } - } - - return new List(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; +using ServiceClient.Response.Householder; + +namespace ServiceClient.Response.Householder +{ + + + public class QueryAllHouseholdersByOwnerIdResponse + { + [JsonProperty("userData")] + public List HouseholderItems { get; set; } = new List(); + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryAllHouseholdersByOwnerIdExtension + { + public static async Task> QueryAllHouseholdersByOwnerId(this BaseInfoHttpClient client + , int ownerId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBasePerson?OwnerID={ownerId}&Data.IsGetUser=true"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.HouseholderItems; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户信息失败"); + } + else + { + return new List(); + } + } + + return new List(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryHouseholderInfoByUserIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryHouseholderInfoByUserIdResponse.cs index ad48b18..ec23ee1 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryHouseholderInfoByUserIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Householder/QueryHouseholderInfoByUserIdResponse.cs @@ -1,54 +1,54 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using ServiceClient; -using ServiceClient.Response.Householder; - -namespace ServiceClient.Response.Householder -{ - public class QueryHouseholderInfoByUserIdResponse - { - [JsonProperty("userData")] public HouseholderItem HouseholderItem { get; set; } - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryHouseholderInfoByUserIdExtension - { - public static async Task QueryHouseholderInfoByUserId(this BaseInfoHttpClient client - , int ownerId, int userId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?OwnerID={ownerId}&Data.userId={userId}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.HouseholderItem; - } - catch (Exception e) - { - LogHelper.Error("根据UserId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户信息失败"); - } - else - { - return new HouseholderItem(); - } - } - - return new HouseholderItem(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using ServiceClient; +using ServiceClient.Response.Householder; + +namespace ServiceClient.Response.Householder +{ + public class QueryHouseholderInfoByUserIdResponse + { + [JsonProperty("userData")] public HouseholderItem HouseholderItem { get; set; } + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryHouseholderInfoByUserIdExtension + { + public static async Task QueryHouseholderInfoByUserId(this BaseInfoHttpClient client + , int ownerId, int userId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?OwnerID={ownerId}&Data.userId={userId}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.HouseholderItem; + } + catch (Exception e) + { + LogHelper.Error("根据UserId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户信息失败"); + } + else + { + return new HouseholderItem(); + } + } + + return new HouseholderItem(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/ManageItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/ManageItem.cs index 5ec7a8b..24aea74 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/ManageItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/ManageItem.cs @@ -1,130 +1,130 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace BaseInfoClient.Response.Manage -{ - public class ManageItem - { - /// - /// 管理员id - /// - public int Id { get; set; } - - /// - /// 创建时间 - /// - public DateTime CreateTime { get; set; } - - /// - /// 更新时间 - /// - public DateTime UpdateTime { get; set; } - - /// - /// 删除标记 - /// - public int DeleteTag { get; set; } - - /// - /// 所属物业ID - /// - public int OwnerId { get; set; } - - /// - /// 更新人ID - /// - public int UpdatorId { get; set; } - - /// - /// 创建人ID - /// - public int CreatorId { get; set; } - - /// - /// 管理员登录名[16 - /// - public string LoginCode { get; set; } - - /// - /// 登录密码[20] - /// - public string Password { get; set; } - - /// - /// 关联的内部人员id - /// - public int workerid { get; set; } - - /// - /// 管理员角色 - /// - public int roleid { get; set; } - - /// - /// 状态 - /// - public int state { get; set; } - - /// - /// 头像地址[30 - /// - public string photourl { get; set; } - - /// - /// 微信openid[50] - /// - public string wxopenid { get; set; } - - /// - /// 微信昵称 - /// - public string wxnickname { get; set; } - - /// - /// 微信头像 - /// - public string wximage { get; set; } - - - /// - /// 注册来源 - /// - public int source { get; set; } - /// - /// 系统ID - /// - public int systemid { get; set; } - /// - /// 管理员手机号 - /// - public string Phone { get; set; } - /// - /// 账号code - /// - public string managercode { get; set; } - - /// - /// 管理员姓名 - /// - public string RealName { get; set; } - /// - /// 电子邮箱 - /// - public string Email { get; set; } - - /// - /// 项目关联 - /// - public string ProjectContact { get; set; } - - public bool IsIntegrator { get; set; } - - /// - /// 是否主管理员权限 - /// - public bool Isroot { get; set; } - - } -} +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BaseInfoClient.Response.Manage +{ + public class ManageItem + { + /// + /// 管理员id + /// + public int Id { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 删除标记 + /// + public int DeleteTag { get; set; } + + /// + /// 所属物业ID + /// + public int OwnerId { get; set; } + + /// + /// 更新人ID + /// + public int UpdatorId { get; set; } + + /// + /// 创建人ID + /// + public int CreatorId { get; set; } + + /// + /// 管理员登录名[16 + /// + public string LoginCode { get; set; } + + /// + /// 登录密码[20] + /// + public string Password { get; set; } + + /// + /// 关联的内部人员id + /// + public int workerid { get; set; } + + /// + /// 管理员角色 + /// + public int roleid { get; set; } + + /// + /// 状态 + /// + public int state { get; set; } + + /// + /// 头像地址[30 + /// + public string photourl { get; set; } + + /// + /// 微信openid[50] + /// + public string wxopenid { get; set; } + + /// + /// 微信昵称 + /// + public string wxnickname { get; set; } + + /// + /// 微信头像 + /// + public string wximage { get; set; } + + + /// + /// 注册来源 + /// + public int source { get; set; } + /// + /// 系统ID + /// + public int systemid { get; set; } + /// + /// 管理员手机号 + /// + public string Phone { get; set; } + /// + /// 账号code + /// + public string managercode { get; set; } + + /// + /// 管理员姓名 + /// + public string RealName { get; set; } + /// + /// 电子邮箱 + /// + public string Email { get; set; } + + /// + /// 项目关联 + /// + public string ProjectContact { get; set; } + + public bool IsIntegrator { get; set; } + + /// + /// 是否主管理员权限 + /// + public bool Isroot { get; set; } + + } +} diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/PermissionProjectByManagerItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/PermissionProjectByManagerItem.cs index 1947c0e..bd2e3f6 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/PermissionProjectByManagerItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/PermissionProjectByManagerItem.cs @@ -1,33 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BaseInfoClient.Response.Manage -{ - public class PermissionProjectByManagerItem - { - /// - /// ID - /// - public int Id { get; set; } - - /// - /// 所属物业ID - /// - public int OwnerId { get; set; } - - /// - /// 管理员数据库ID - /// - public int ManagerId { get; set; } - - /// - /// 项目编码 - /// - public int ProjectCode { get; set; } - /// - /// 小区名称 - /// - public string ProjectName { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace BaseInfoClient.Response.Manage +{ + public class PermissionProjectByManagerItem + { + /// + /// ID + /// + public int Id { get; set; } + + /// + /// 所属物业ID + /// + public int OwnerId { get; set; } + + /// + /// 管理员数据库ID + /// + public int ManagerId { get; set; } + + /// + /// 项目编码 + /// + public int ProjectCode { get; set; } + /// + /// 小区名称 + /// + public string ProjectName { get; set; } + } +} diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryAllManageByOwnerIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryAllManageByOwnerIdResponse.cs index 6b0080f..c450f38 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryAllManageByOwnerIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryAllManageByOwnerIdResponse.cs @@ -1,48 +1,48 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using BaseInfoClient.Response.Manage; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; - -namespace BaseInfoClient.Response.Manage -{ - -} -namespace BaseInfoClient.Extension -{ - public static class QueryAllManageByOwnerIdResponse - { - public static async Task QueryManageById(this BaseInfoHttpClient client - , int Id, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/manage/v1/Manager/GetOneManage?Id={Id}"); - - var content = await response.Content.ReadAsStringAsync(); - var result = content.FromJsonTo>(); - return result.Data; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户信息失败"); - } - else - { - return new ManageItem(); - } - } - return new ManageItem(); - } - } -} +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using BaseInfoClient.Response.Manage; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; + +namespace BaseInfoClient.Response.Manage +{ + +} +namespace BaseInfoClient.Extension +{ + public static class QueryAllManageByOwnerIdResponse + { + public static async Task QueryManageById(this BaseInfoHttpClient client + , int Id, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/manage/v1/Manager/GetOneManage?Id={Id}"); + + var content = await response.Content.ReadAsStringAsync(); + var result = content.FromJsonTo>(); + return result.Data; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户信息失败"); + } + else + { + return new ManageItem(); + } + } + return new ManageItem(); + } + } +} diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryPermissionProjectByManagerIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryPermissionProjectByManagerIdResponse.cs index b3e1214..50ed5ec 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryPermissionProjectByManagerIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Manage/QueryPermissionProjectByManagerIdResponse.cs @@ -1,45 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using BaseInfoClient.Response.Manage; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; -using System.Text; - -namespace BaseInfoClient.Extension -{ - public static class QueryPermissionProjectByManagerIdResponse - { - public static async Task> QueryProjectCodeByManageId(this BaseInfoHttpClient client - , int Id, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/manage/v1/Manager/GetByManageId?ManagerId={Id}"); - - var content = await response.Content.ReadAsStringAsync(); - var result = content.FromJsonTo>>(); - return result.Data; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户信息失败"); - } - else - { - return new List(); - } - } - return new List(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using BaseInfoClient.Response.Manage; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; +using System.Text; + +namespace BaseInfoClient.Extension +{ + public static class QueryPermissionProjectByManagerIdResponse + { + public static async Task> QueryProjectCodeByManageId(this BaseInfoHttpClient client + , int Id, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/manage/v1/Manager/GetByManageId?ManagerId={Id}"); + + var content = await response.Content.ReadAsStringAsync(); + var result = content.FromJsonTo>>(); + return result.Data; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户信息失败"); + } + else + { + return new List(); + } + } + return new List(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/ProjectItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/ProjectItem.cs index 5fb554d..847ea9c 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/ProjectItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/ProjectItem.cs @@ -1,18 +1,18 @@ -using Newtonsoft.Json; - -namespace BaseInfoClient.Response.Project -{ - public class ProjectItem - { - [JsonProperty("projectCode")] public int ProjectCode { get; set; } - - [JsonProperty("name")] public string Name { get; set; } = ""; - /// - /// Сַ - /// - [JsonProperty("location")] public string Location { get; set; } = ""; - - - [JsonProperty("projectType")] public int projectType { get; set; } - } +using Newtonsoft.Json; + +namespace BaseInfoClient.Response.Project +{ + public class ProjectItem + { + [JsonProperty("projectCode")] public int ProjectCode { get; set; } + + [JsonProperty("name")] public string Name { get; set; } = ""; + /// + /// Сַ + /// + [JsonProperty("location")] public string Location { get; set; } = ""; + + + [JsonProperty("projectType")] public int projectType { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryAllProjectsByOwnerIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryAllProjectsByOwnerIdResponse.cs index 412ebe9..60f28e4 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryAllProjectsByOwnerIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryAllProjectsByOwnerIdResponse.cs @@ -1,55 +1,55 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using BaseInfoClient.Response.Project; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; - -namespace BaseInfoClient.Response.Project -{ - - - public class QueryAllProjectsByOwnerIdResponse - { - [JsonProperty("projectData")] public List ProjectItems { get; set; } = new List(); - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryAllProjectsByOwnerIdExtension - { - public static async Task> QueryAllProjectsByOwnerIdAsync(this BaseInfoHttpClient client - , int ownerId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBaseProject?OwnerID={ownerId}&Data.IsGetproject=true"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.ProjectItems; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取小区", e); - - if (throwException) - { - BusinessException.Throw("获取小区信息失败"); - } - else - { - return new List(); - } - } - - return new List(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using BaseInfoClient.Response.Project; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; + +namespace BaseInfoClient.Response.Project +{ + + + public class QueryAllProjectsByOwnerIdResponse + { + [JsonProperty("projectData")] public List ProjectItems { get; set; } = new List(); + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryAllProjectsByOwnerIdExtension + { + public static async Task> QueryAllProjectsByOwnerIdAsync(this BaseInfoHttpClient client + , int ownerId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBaseProject?OwnerID={ownerId}&Data.IsGetproject=true"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.ProjectItems; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取小区", e); + + if (throwException) + { + BusinessException.Throw("获取小区信息失败"); + } + else + { + return new List(); + } + } + + return new List(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryOneProjectByCodeResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryOneProjectByCodeResponse.cs index 266cd17..56291ce 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryOneProjectByCodeResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Project/QueryOneProjectByCodeResponse.cs @@ -1,53 +1,53 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using BaseInfoClient.Response.Project; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; - -namespace BaseInfoClient.Response.Project -{ - public class QueryOneProjectByCodeResponse - { - [JsonProperty("projectData")] public ProjectItem ProjectItem { get; set; } - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryProjectByCodeResponseExtension - { - public static async Task QueryOneProjectByCodeAsync(this BaseInfoHttpClient client - , int ownerId, int projectCode, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBaseProject?OwnerID={ownerId}&Data.projectCode={projectCode}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.ProjectItem; - } - catch (Exception e) - { - LogHelper.Error("根据ProjectCode获取小区", e); - - if (throwException) - { - BusinessException.Throw("获取小区信息失败"); - } - else - { - return new ProjectItem(); - } - } - - return new ProjectItem(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using BaseInfoClient.Response.Project; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; + +namespace BaseInfoClient.Response.Project +{ + public class QueryOneProjectByCodeResponse + { + [JsonProperty("projectData")] public ProjectItem ProjectItem { get; set; } + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryProjectByCodeResponseExtension + { + public static async Task QueryOneProjectByCodeAsync(this BaseInfoHttpClient client + , int ownerId, int projectCode, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBaseProject?OwnerID={ownerId}&Data.projectCode={projectCode}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.ProjectItem; + } + catch (Exception e) + { + LogHelper.Error("根据ProjectCode获取小区", e); + + if (throwException) + { + BusinessException.Throw("获取小区信息失败"); + } + else + { + return new ProjectItem(); + } + } + + return new ProjectItem(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/EmployeeItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/EmployeeItem.cs index c5ce277..86474ee 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/EmployeeItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/EmployeeItem.cs @@ -1,17 +1,17 @@ -using Newtonsoft.Json; - -namespace ServiceClient.Response.PropertyEmployee -{ - public class EmployeeItem - { - [JsonProperty("ID")] public int UserId { get; set; } - - [JsonProperty("mobile")] public string Mobile { get; set; } = ""; - - [JsonProperty("name")] public string Name { get; set; } = ""; - - [JsonProperty("projectCode")] public int ProjectCode { get; set; } - - [JsonProperty("isInternal")] public bool IsInternal { get; set; } - } +using Newtonsoft.Json; + +namespace ServiceClient.Response.PropertyEmployee +{ + public class EmployeeItem + { + [JsonProperty("ID")] public int UserId { get; set; } + + [JsonProperty("mobile")] public string Mobile { get; set; } = ""; + + [JsonProperty("name")] public string Name { get; set; } = ""; + + [JsonProperty("projectCode")] public int ProjectCode { get; set; } + + [JsonProperty("isInternal")] public bool IsInternal { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryAllEmployeesByOwnerIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryAllEmployeesByOwnerIdResponse.cs index 528373a..a5d68f3 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryAllEmployeesByOwnerIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryAllEmployeesByOwnerIdResponse.cs @@ -1,57 +1,57 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using ServiceClient; -using ServiceClient.Response.PropertyEmployee; -using ServiceClient.Response.User; - -namespace ServiceClient.Response.PropertyEmployee -{ - public class QueryAllEmployeesByOwnerIdResponse - { - [JsonProperty("staffData")] - public List EmployeeInfos { get; set; }=new List(); - } -} - - -namespace BaseInfoClient.Extension -{ - public static class QueryAllEmployeesByOwnerIdResponseExtension - { - public static async Task> QueryAllEmployeesByOwnerId(this BaseInfoHttpClient client - , int ownerId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBasePerson?Data.IsGetSaff=true&OwnerID={ownerId}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.EmployeeInfos; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取物业员工",e); - - if (throwException) - { - BusinessException.Throw("获取员工信息失败"); - } - else - { - return new List(); - } - } - - return new List(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using ServiceClient; +using ServiceClient.Response.PropertyEmployee; +using ServiceClient.Response.User; + +namespace ServiceClient.Response.PropertyEmployee +{ + public class QueryAllEmployeesByOwnerIdResponse + { + [JsonProperty("staffData")] + public List EmployeeInfos { get; set; }=new List(); + } +} + + +namespace BaseInfoClient.Extension +{ + public static class QueryAllEmployeesByOwnerIdResponseExtension + { + public static async Task> QueryAllEmployeesByOwnerId(this BaseInfoHttpClient client + , int ownerId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBasePerson?Data.IsGetSaff=true&OwnerID={ownerId}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.EmployeeInfos; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取物业员工",e); + + if (throwException) + { + BusinessException.Throw("获取员工信息失败"); + } + else + { + return new List(); + } + } + + return new List(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryEmployeeInfoByUserIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryEmployeeInfoByUserIdResponse.cs index 8615df8..9d1136e 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryEmployeeInfoByUserIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/PropertyEmployee/QueryEmployeeInfoByUserIdResponse.cs @@ -1,57 +1,57 @@ -using System; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; -using ServiceClient.Response.PropertyEmployee; -using ServiceClient.Response.User; - -namespace ServiceClient.Response.PropertyEmployee -{ - - - public class QueryEmployeeInfoByUserIdResponse - { - [JsonProperty("staffData")] public EmployeeItem UserInfo { get; set; } = new EmployeeItem(); - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryEmployeeInfoByUserIdResponseExtension - { - public static async Task QueryEmployeeInfoByUserId(this BaseInfoHttpClient client, int ownerId, - int userId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?Data.staffId={userId}&OwnerID={ownerId}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.UserInfo; - } - catch (Exception e) - { - LogHelper.Error("根据员工Id获取员工信息",e); - - if (throwException) - { - BusinessException.Throw("获取员工信息失败"); - } - else - { - return new EmployeeItem(); - } - } - - return new EmployeeItem(); - } - - - } +using System; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; +using ServiceClient.Response.PropertyEmployee; +using ServiceClient.Response.User; + +namespace ServiceClient.Response.PropertyEmployee +{ + + + public class QueryEmployeeInfoByUserIdResponse + { + [JsonProperty("staffData")] public EmployeeItem UserInfo { get; set; } = new EmployeeItem(); + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryEmployeeInfoByUserIdResponseExtension + { + public static async Task QueryEmployeeInfoByUserId(this BaseInfoHttpClient client, int ownerId, + int userId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?Data.staffId={userId}&OwnerID={ownerId}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.UserInfo; + } + catch (Exception e) + { + LogHelper.Error("根据员工Id获取员工信息",e); + + if (throwException) + { + BusinessException.Throw("获取员工信息失败"); + } + else + { + return new EmployeeItem(); + } + } + + return new EmployeeItem(); + } + + + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryAllHouseholderRoomsByOwnerIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryAllHouseholderRoomsByOwnerIdResponse.cs index a371eff..2aa17d0 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryAllHouseholderRoomsByOwnerIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryAllHouseholderRoomsByOwnerIdResponse.cs @@ -1,54 +1,54 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; -using ServiceClient.Response.Room; - -namespace ServiceClient.Response.Room -{ - public class QueryAllHouseholderRoomsByOwnerIdResponse - { - [JsonProperty("userToRoomData")] - public List UserRoomItems { get; set; } = new List(); - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryAllHouseholderRoomsByOwnerIdResponseExtension - { - public static async Task> QueryAllHouseholderRoomsByOwnerId(this BaseInfoHttpClient client - , int ownerId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBasePerson?Data.IsGetUserToRoom=true&OwnerID={ownerId}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.UserRoomItems; - } - catch (Exception e) - { - LogHelper.Error("根据UserId获取用户房间信息", e); - - if (throwException) - { - BusinessException.Throw("获取用户房间信息失败"); - } - else - { - return new List(); - } - } - - return new List(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; +using ServiceClient.Response.Room; + +namespace ServiceClient.Response.Room +{ + public class QueryAllHouseholderRoomsByOwnerIdResponse + { + [JsonProperty("userToRoomData")] + public List UserRoomItems { get; set; } = new List(); + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryAllHouseholderRoomsByOwnerIdResponseExtension + { + public static async Task> QueryAllHouseholderRoomsByOwnerId(this BaseInfoHttpClient client + , int ownerId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBasePerson?Data.IsGetUserToRoom=true&OwnerID={ownerId}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.UserRoomItems; + } + catch (Exception e) + { + LogHelper.Error("根据UserId获取用户房间信息", e); + + if (throwException) + { + BusinessException.Throw("获取用户房间信息失败"); + } + else + { + return new List(); + } + } + + return new List(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryHouseholderRoomsByUserIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryHouseholderRoomsByUserIdResponse.cs index 6a6eadf..8dbff63 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryHouseholderRoomsByUserIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/QueryHouseholderRoomsByUserIdResponse.cs @@ -1,57 +1,57 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using ServiceClient; -using ServiceClient.Response.Room; - -namespace ServiceClient.Response.Room -{ - - - public class QueryHouseholderRoomsByUserIdResponse - { - [JsonProperty("userToRoomData")] - public List UserRoomItems { get; set; } = new List(); - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryHouseholderRoomsByUserIdExtension - { - public static async Task> QueryHouseholderRoomsByUserId(this BaseInfoHttpClient client - , int ownerId, int userId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?Data.userToroomUserId={userId}&OwnerID={ownerId}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.UserRoomItems; - } - catch (Exception e) - { - LogHelper.Error("根据UserId获取用户房间信息", e); - - if (throwException) - { - BusinessException.Throw("获取用户房间信息失败"); - } - else - { - return new List(); - } - } - - return new List(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using ServiceClient; +using ServiceClient.Response.Room; + +namespace ServiceClient.Response.Room +{ + + + public class QueryHouseholderRoomsByUserIdResponse + { + [JsonProperty("userToRoomData")] + public List UserRoomItems { get; set; } = new List(); + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryHouseholderRoomsByUserIdExtension + { + public static async Task> QueryHouseholderRoomsByUserId(this BaseInfoHttpClient client + , int ownerId, int userId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?Data.userToroomUserId={userId}&OwnerID={ownerId}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.UserRoomItems; + } + catch (Exception e) + { + LogHelper.Error("根据UserId获取用户房间信息", e); + + if (throwException) + { + BusinessException.Throw("获取用户房间信息失败"); + } + else + { + return new List(); + } + } + + return new List(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/UserRoomItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/UserRoomItem.cs index 15ec962..dd0a4e7 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/UserRoomItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Room/UserRoomItem.cs @@ -1,25 +1,25 @@ -using Newtonsoft.Json; - -namespace ServiceClient.Response.Room -{ - public class UserRoomItem - { - [JsonProperty("userId")] public int UserId { get; set; } - - [JsonProperty("roomCode")] public string RoomCode { get; set; } = ""; - - [JsonProperty("deleteTag")] public int DeleteTag { get; set; } - - [JsonProperty("state")] public int State { get; set; } - - [JsonProperty("floor")] public int Floor { get; set; } - - [JsonProperty("roomNo")] public string RoomNo { get; set; } - - [JsonProperty("projectCode")] public int ProjectCode { get; set; } - - [JsonProperty("buildCode")] public string BuildCode { get; set; } - - [JsonProperty("unit")] public string Unit { get; set; } - } +using Newtonsoft.Json; + +namespace ServiceClient.Response.Room +{ + public class UserRoomItem + { + [JsonProperty("userId")] public int UserId { get; set; } + + [JsonProperty("roomCode")] public string RoomCode { get; set; } = ""; + + [JsonProperty("deleteTag")] public int DeleteTag { get; set; } + + [JsonProperty("state")] public int State { get; set; } + + [JsonProperty("floor")] public int Floor { get; set; } + + [JsonProperty("roomNo")] public string RoomNo { get; set; } + + [JsonProperty("projectCode")] public int ProjectCode { get; set; } + + [JsonProperty("buildCode")] public string BuildCode { get; set; } + + [JsonProperty("unit")] public string Unit { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisiterAndUserByWxOpenIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisiterAndUserByWxOpenIdResponse.cs index 87f35e5..98be058 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisiterAndUserByWxOpenIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisiterAndUserByWxOpenIdResponse.cs @@ -1,63 +1,63 @@ -using System; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using ServiceClient; -using ServiceClient.Response.Householder; -using ServiceClient.Response.User; - -namespace ServiceClient.Response.User -{ - public class QueryVisiterAndUserByWxOpenIdResponse - { - [JsonProperty("userData")] - public HouseholderItem HouseholderItem { get; set; } - - [JsonProperty("visterData")] - public VisterItem VisterItem { get; set; } - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryVisiterAndUserByWxOpenIdResponseExtension - { - public static async Task QueryVisiterAndUserByWxOpenId( - this BaseInfoHttpClient client, string openId,bool throwException = false) - { - try - { - var response = - await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetUserVisterByOpenID?openId={openId}"); - //var response = - // await client.CreateHttpClient().GetAsync( - // $"https://localhost:44324/api/baseinfo/v1/BaseData/GetUserVisterByOpenID?openId={openId}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data; - } - catch (Exception e) - { - LogHelper.Error("根据openId查询用户",e); - - if (throwException) - { - BusinessException.Throw("获取用户信息失败"); - } - else - { - return new QueryVisiterAndUserByWxOpenIdResponse(); - } - } - - return new QueryVisiterAndUserByWxOpenIdResponse(); - } - } - +using System; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using ServiceClient; +using ServiceClient.Response.Householder; +using ServiceClient.Response.User; + +namespace ServiceClient.Response.User +{ + public class QueryVisiterAndUserByWxOpenIdResponse + { + [JsonProperty("userData")] + public HouseholderItem HouseholderItem { get; set; } + + [JsonProperty("visterData")] + public VisterItem VisterItem { get; set; } + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryVisiterAndUserByWxOpenIdResponseExtension + { + public static async Task QueryVisiterAndUserByWxOpenId( + this BaseInfoHttpClient client, string openId,bool throwException = false) + { + try + { + var response = + await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetUserVisterByOpenID?openId={openId}"); + //var response = + // await client.CreateHttpClient().GetAsync( + // $"https://localhost:44324/api/baseinfo/v1/BaseData/GetUserVisterByOpenID?openId={openId}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data; + } + catch (Exception e) + { + LogHelper.Error("根据openId查询用户",e); + + if (throwException) + { + BusinessException.Throw("获取用户信息失败"); + } + else + { + return new QueryVisiterAndUserByWxOpenIdResponse(); + } + } + + return new QueryVisiterAndUserByWxOpenIdResponse(); + } + } + } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisterByVisterIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisterByVisterIdResponse.cs index ab96687..0eb2eef 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisterByVisterIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/User/QueryVisterByVisterIdResponse.cs @@ -1,53 +1,53 @@ -using System; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; -using ServiceClient.Response.User; - - -namespace ServiceClient.Response.User -{ - public class QueryVisterByVisterIdResponse - { - [JsonProperty("visterData")] public VisterItem VisterItem { get; set; } = new VisterItem(); - } -} - -namespace BaseInfoClient.Extension -{ - public static class QueryVisterByVisterIdResponseExtension - { - public static async Task QueryVisterByVisterId(this BaseInfoHttpClient client, int ownerId, - int visterId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?OwnerID={ownerId}&Data.visterId={visterId}"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.VisterItem; - } - catch (Exception e) - { - LogHelper.Error("根据访客Id查询访客信息",e); - - if (throwException) - { - BusinessException.Throw("获取访客信息失败"); - } - else - { - return new VisterItem(); - } - } - - return new VisterItem(); - } - } +using System; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; +using ServiceClient.Response.User; + + +namespace ServiceClient.Response.User +{ + public class QueryVisterByVisterIdResponse + { + [JsonProperty("visterData")] public VisterItem VisterItem { get; set; } = new VisterItem(); + } +} + +namespace BaseInfoClient.Extension +{ + public static class QueryVisterByVisterIdResponseExtension + { + public static async Task QueryVisterByVisterId(this BaseInfoHttpClient client, int ownerId, + int visterId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetOneBasePerson?OwnerID={ownerId}&Data.visterId={visterId}"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.VisterItem; + } + catch (Exception e) + { + LogHelper.Error("根据访客Id查询访客信息",e); + + if (throwException) + { + BusinessException.Throw("获取访客信息失败"); + } + else + { + return new VisterItem(); + } + } + + return new VisterItem(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/User/VisterItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/User/VisterItem.cs index 2fd1978..2eb2c02 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/User/VisterItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/User/VisterItem.cs @@ -1,97 +1,97 @@ -using System; -using Newtonsoft.Json; - -namespace ServiceClient.Response.User -{ - public class VisterItem - { - /// - /// 邀请人姓名 - /// - [JsonProperty("username")] - public string UserName { get; set; } - - /// - /// 访客姓名 - /// - [JsonProperty("visitorname")] - public string VisitorName { get; set; } - - /// - /// 访客电话 - /// - [JsonProperty("visitorphone")] - public string VisitorPhone { get; set; } - - /// - /// 20 前台登记 10 用户邀请 30 人脸访问 40员工端邀请 - /// - [JsonProperty("visitortype")] - public int VisitorType { get; set; } - - [JsonProperty("openid")] public string OpenId { get; set; } - - /// - /// 来访日期 - /// - [JsonProperty("entrytime")] - public DateTime EntryTime { get; set; } - - [JsonProperty("expirytime")] public DateTime ExpiryTime { get; set; } - - [JsonProperty("projectcode")] public int ProjectCode { get; set; } - - [JsonProperty("buildcode")] public string BuildCode { get; set; } - - [JsonProperty("unit")] public string Unit { get; set; } - - [JsonProperty("room")] public string Room { get; set; } - - [JsonProperty("reason")] public string Reason { get; set; } - - /// - /// 邀请函模板类型 - /// - [JsonProperty("templatetype")] - public int TemplateType { get; set; } - - /// - /// 10:申请中 20:申请通过 30: 黑名单用户 40:申请作废 - /// - [JsonProperty("stats")] - public int Stats { get; set; } - - /// - /// 访客接受时间 - /// - [JsonProperty("accepttime")] - public DateTime AcceptTime { get; set; } - - /// - /// 备注 - /// - [JsonProperty("bak")] - public string Bak { get; set; } - - /// - /// 微信头像 - /// - [JsonProperty("wxImgUrl")] - public string WxImgUrl { get; set; } - - /// - /// 人脸地址 - /// - [JsonProperty("faceUrl")] - public string FaceUrl { get; set; } - - /// - /// 访客人员ID - /// - [JsonProperty("userId")] - public System.Int32? UserId { get; set; } = 0; - - [JsonProperty("ID")] - public int Id { get; set; } - } +using System; +using Newtonsoft.Json; + +namespace ServiceClient.Response.User +{ + public class VisterItem + { + /// + /// 邀请人姓名 + /// + [JsonProperty("username")] + public string UserName { get; set; } + + /// + /// 访客姓名 + /// + [JsonProperty("visitorname")] + public string VisitorName { get; set; } + + /// + /// 访客电话 + /// + [JsonProperty("visitorphone")] + public string VisitorPhone { get; set; } + + /// + /// 20 前台登记 10 用户邀请 30 人脸访问 40员工端邀请 + /// + [JsonProperty("visitortype")] + public int VisitorType { get; set; } + + [JsonProperty("openid")] public string OpenId { get; set; } + + /// + /// 来访日期 + /// + [JsonProperty("entrytime")] + public DateTime EntryTime { get; set; } + + [JsonProperty("expirytime")] public DateTime ExpiryTime { get; set; } + + [JsonProperty("projectcode")] public int ProjectCode { get; set; } + + [JsonProperty("buildcode")] public string BuildCode { get; set; } + + [JsonProperty("unit")] public string Unit { get; set; } + + [JsonProperty("room")] public string Room { get; set; } + + [JsonProperty("reason")] public string Reason { get; set; } + + /// + /// 邀请函模板类型 + /// + [JsonProperty("templatetype")] + public int TemplateType { get; set; } + + /// + /// 10:申请中 20:申请通过 30: 黑名单用户 40:申请作废 + /// + [JsonProperty("stats")] + public int Stats { get; set; } + + /// + /// 访客接受时间 + /// + [JsonProperty("accepttime")] + public DateTime AcceptTime { get; set; } + + /// + /// 备注 + /// + [JsonProperty("bak")] + public string Bak { get; set; } + + /// + /// 微信头像 + /// + [JsonProperty("wxImgUrl")] + public string WxImgUrl { get; set; } + + /// + /// 人脸地址 + /// + [JsonProperty("faceUrl")] + public string FaceUrl { get; set; } + + /// + /// 访客人员ID + /// + [JsonProperty("userId")] + public System.Int32? UserId { get; set; } = 0; + + [JsonProperty("ID")] + public int Id { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/QueryVisitorByOpenidOrId.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/QueryVisitorByOpenidOrId.cs index 7298347..e59a762 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/QueryVisitorByOpenidOrId.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/QueryVisitorByOpenidOrId.cs @@ -1,145 +1,145 @@ -using BaseInfoClient.Response.Visitor; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using ServiceClient; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; - - -namespace BaseInfoClient.Extension -{ - public static class QueryVisitorByOpenidOrId - { - public static async Task> QueryVisitorByOpenId(this BaseInfoHttpClient client - , QueryMenJinVisitorRequest param, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/visitor/v1/Request/QueryByOpenId?OwnerId={param.OwnerId}&Data.OpenId={param.OpenId}"); - - var content = await response.Content.ReadAsStringAsync(); - var result = content.FromJsonTo>>(); - return result.Data; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户信息失败"); - } - else - { - return new List(); - } - } - return new List(); - } - - public static async Task QueryVisitorById(this BaseInfoHttpClient client - , QueryMenJinVisitorRequest param, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/visitor/v1/Request/QueryById?OwnerId={param.OwnerId}&Data.Id={param.Id}&Data.Type={param.Type}"); - - var content = await response.Content.ReadAsStringAsync(); - var result = content.FromJsonTo>(); - return result.Data; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户信息失败"); - } - else - { - return new VisitorResponseItem(); - } - } - return new VisitorResponseItem(); - } - - /// - /// 根据人脸ID获取访客信息 - /// - /// - /// - /// - /// - public static async Task QueryVisitorByFaceId(this BaseInfoHttpClient client - , int ownerId,string faceId, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/visitor/v1/Request/QueryByFaceId?OwnerId={ownerId}&Data.FaceId={faceId}"); - - var content = await response.Content.ReadAsStringAsync(); - var result = content.FromJsonTo>(); - return result.Data; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户信息失败"); - } - else - { - return new VisitorResponseItem(); - } - } - return new VisitorResponseItem(); - } - - - /// - /// 根据人脸ID获取访客信息 - /// - /// - /// - /// - /// - public static async Task> QueryVisitorProFace(this BaseInfoHttpClient client - , int projectCode, string buildCode,string unit,int operaterID, int ownerID, bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/doorway/v1/device/GetFacesClient?OperaterID={operaterID}&OwnerID={ownerID}&Data.ProjectCode={projectCode}&Data.BuildCode={buildCode}&Data.Unit={unit}&Data.UserType=0"); - - var content = await response.Content.ReadAsStringAsync(); - var result = content.FromJsonTo>>(); - return result.Data; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取住户", e); - - if (throwException) - { - BusinessException.Throw("获取住户设备信息失败"); - } - else - { - return new List(); - } - } - return new List(); - } - } -} +using BaseInfoClient.Response.Visitor; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using ServiceClient; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + + +namespace BaseInfoClient.Extension +{ + public static class QueryVisitorByOpenidOrId + { + public static async Task> QueryVisitorByOpenId(this BaseInfoHttpClient client + , QueryMenJinVisitorRequest param, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/visitor/v1/Request/QueryByOpenId?OwnerId={param.OwnerId}&Data.OpenId={param.OpenId}"); + + var content = await response.Content.ReadAsStringAsync(); + var result = content.FromJsonTo>>(); + return result.Data; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户信息失败"); + } + else + { + return new List(); + } + } + return new List(); + } + + public static async Task QueryVisitorById(this BaseInfoHttpClient client + , QueryMenJinVisitorRequest param, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/visitor/v1/Request/QueryById?OwnerId={param.OwnerId}&Data.Id={param.Id}&Data.Type={param.Type}"); + + var content = await response.Content.ReadAsStringAsync(); + var result = content.FromJsonTo>(); + return result.Data; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户信息失败"); + } + else + { + return new VisitorResponseItem(); + } + } + return new VisitorResponseItem(); + } + + /// + /// 根据人脸ID获取访客信息 + /// + /// + /// + /// + /// + public static async Task QueryVisitorByFaceId(this BaseInfoHttpClient client + , int ownerId,string faceId, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/visitor/v1/Request/QueryByFaceId?OwnerId={ownerId}&Data.FaceId={faceId}"); + + var content = await response.Content.ReadAsStringAsync(); + var result = content.FromJsonTo>(); + return result.Data; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户信息失败"); + } + else + { + return new VisitorResponseItem(); + } + } + return new VisitorResponseItem(); + } + + + /// + /// 根据人脸ID获取访客信息 + /// + /// + /// + /// + /// + public static async Task> QueryVisitorProFace(this BaseInfoHttpClient client + , int projectCode, string buildCode,string unit,int operaterID, int ownerID, bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/doorway/v1/device/GetFacesClient?OperaterID={operaterID}&OwnerID={ownerID}&Data.ProjectCode={projectCode}&Data.BuildCode={buildCode}&Data.Unit={unit}&Data.UserType=0"); + + var content = await response.Content.ReadAsStringAsync(); + var result = content.FromJsonTo>>(); + return result.Data; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取住户", e); + + if (throwException) + { + BusinessException.Throw("获取住户设备信息失败"); + } + else + { + return new List(); + } + } + return new List(); + } + } +} diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/VisitorItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/VisitorItem.cs index 632e1ec..4ec5093 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/VisitorItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/Visitor/VisitorItem.cs @@ -1,56 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BaseInfoClient.Response.Visitor -{ - public class VisitorResponseItem - { - public int Id { get; set; } - - public DateTime ExpiryTime { get; set; } - public DateTime EntryTime { get; set; } - public string Unit { get; set; } - public string BuildCode { get; set; } - public int ProjectCode { get; set; } - /// - /// 10邀约 20申请 - /// - public int Type { get; set; } - public string Reason { get; set; } - public string VisitorName { get; set; } - /// - /// 访客电话 - /// - public string VisitorPhone { get; set; } - public int CheckUserId { get; set; } - /// - /// 审核人姓名 - /// - public string CheckUserName { get; set; } - public string Room { get; set; } - /// - /// 状态 10 审核中 20 已通过 30 已拒绝 - /// - public int State { get; set; } - } - public class QueryMenJinVisitorRequest - { - public int Id { get; set; } - public string OpenId { get; set; } - public DateTime InTime { get; set; } - /// - /// 10 申请 20 邀约 - /// - public int Type { get; set; } - public int OwnerId { get; set; } - } - - public class QueryDeviceByBuildResponse - { - public string Ip { get; set; } - public string PassWord { get; set; } - public string DeviceNo { get; set; } - public int Port { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace BaseInfoClient.Response.Visitor +{ + public class VisitorResponseItem + { + public int Id { get; set; } + + public DateTime ExpiryTime { get; set; } + public DateTime EntryTime { get; set; } + public string Unit { get; set; } + public string BuildCode { get; set; } + public int ProjectCode { get; set; } + /// + /// 10邀约 20申请 + /// + public int Type { get; set; } + public string Reason { get; set; } + public string VisitorName { get; set; } + /// + /// 访客电话 + /// + public string VisitorPhone { get; set; } + public int CheckUserId { get; set; } + /// + /// 审核人姓名 + /// + public string CheckUserName { get; set; } + public string Room { get; set; } + /// + /// 状态 10 审核中 20 已通过 30 已拒绝 + /// + public int State { get; set; } + } + public class QueryMenJinVisitorRequest + { + public int Id { get; set; } + public string OpenId { get; set; } + public DateTime InTime { get; set; } + /// + /// 10 申请 20 邀约 + /// + public int Type { get; set; } + public int OwnerId { get; set; } + } + + public class QueryDeviceByBuildResponse + { + public string Ip { get; set; } + public string PassWord { get; set; } + public string DeviceNo { get; set; } + public int Port { get; set; } + } +} diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/BuildItem.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/BuildItem.cs index ed3d823..7ff4f89 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/BuildItem.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/BuildItem.cs @@ -1,13 +1,13 @@ -using Newtonsoft.Json; - -namespace BaseInfoClient.Response.Build -{ - public class BuildItem - { - [JsonProperty("projectCode")] public int ProjectCode { get; set; } - - [JsonProperty("buildCode")] public string BuildCode { get; set; } = ""; - - [JsonProperty("name")] public string Name { get; set; } = ""; - } +using Newtonsoft.Json; + +namespace BaseInfoClient.Response.Build +{ + public class BuildItem + { + [JsonProperty("projectCode")] public int ProjectCode { get; set; } + + [JsonProperty("buildCode")] public string BuildCode { get; set; } = ""; + + [JsonProperty("name")] public string Name { get; set; } = ""; + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/QueryAllBuildsByOwnerIdResponse.cs b/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/QueryAllBuildsByOwnerIdResponse.cs index 9d77178..bc0e4d9 100644 --- a/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/QueryAllBuildsByOwnerIdResponse.cs +++ b/Infrastructure/ServiceClient/BaseInfoClient/Response/`Build/QueryAllBuildsByOwnerIdResponse.cs @@ -1,55 +1,55 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using BaseInfoClient.Response.Build; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Data; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; -using Newtonsoft.Json; -using ServiceClient; - -namespace BaseInfoClient.Response.Build -{ - - - public class QueryAllBuildsByOwnerIdResponse - { - [JsonProperty("buildData")] public List BuildItems { get; set; } = new List(); - } -} - -namespace BaseInfoClient.Extension -{ - public static class QuerAllyBuildsByOwnerIdExtension - { - public static async Task> QueryAllBuildsByOwnerIdAsync(this BaseInfoHttpClient client, int ownerId, - bool throwException = false) - { - try - { - var response = await client.CreateHttpClient().GetAsync( - $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBaseProject?OwnerID={ownerId}&Data.IsGetBuild=true"); - - var content = await response.Content.ReadAsStringAsync(); - - return content.FromJsonTo>().Data.BuildItems; - } - catch (Exception e) - { - LogHelper.Error("根据OwnerId获取楼宇", e); - - if (throwException) - { - BusinessException.Throw("获取楼宇信息失败"); - } - else - { - return new List(); - } - } - - return new List(); - } - } +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using BaseInfoClient.Response.Build; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Data; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; +using Newtonsoft.Json; +using ServiceClient; + +namespace BaseInfoClient.Response.Build +{ + + + public class QueryAllBuildsByOwnerIdResponse + { + [JsonProperty("buildData")] public List BuildItems { get; set; } = new List(); + } +} + +namespace BaseInfoClient.Extension +{ + public static class QuerAllyBuildsByOwnerIdExtension + { + public static async Task> QueryAllBuildsByOwnerIdAsync(this BaseInfoHttpClient client, int ownerId, + bool throwException = false) + { + try + { + var response = await client.CreateHttpClient().GetAsync( + $"{client.BaseUrl}/api/baseinfo/v1/BaseData/GetAllBaseProject?OwnerID={ownerId}&Data.IsGetBuild=true"); + + var content = await response.Content.ReadAsStringAsync(); + + return content.FromJsonTo>().Data.BuildItems; + } + catch (Exception e) + { + LogHelper.Error("根据OwnerId获取楼宇", e); + + if (throwException) + { + BusinessException.Throw("获取楼宇信息失败"); + } + else + { + return new List(); + } + } + + return new List(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/DataBodyAttribute.cs b/Infrastructure/ServiceClient/MsgCenterClient/DataBodyAttribute.cs index f6a11ec..79e0f9d 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/DataBodyAttribute.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/DataBodyAttribute.cs @@ -1,14 +1,14 @@ -using System; - -namespace MsgCenterClient -{ - public class DataBodyAttribute : Attribute - { - public int Order { get; } - - public DataBodyAttribute(int order) - { - Order = order; - } - } +using System; + +namespace MsgCenterClient +{ + public class DataBodyAttribute : Attribute + { + public int Order { get; } + + public DataBodyAttribute(int order) + { + Order = order; + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/IServiceCollectionExtension.cs b/Infrastructure/ServiceClient/MsgCenterClient/IServiceCollectionExtension.cs index 39dd6fd..06b8b4f 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/IServiceCollectionExtension.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/IServiceCollectionExtension.cs @@ -1,14 +1,14 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace MsgCenterClient -{ - public static class IServiceCollectionExtension - { - public static void AddMsgCenterClient(this IServiceCollection service, string baseUrl) - { - MsgCenterHttpClient._BaseUrl = baseUrl; - - service.AddSingleton(); - } - } +using Microsoft.Extensions.DependencyInjection; + +namespace MsgCenterClient +{ + public static class IServiceCollectionExtension + { + public static void AddMsgCenterClient(this IServiceCollection service, string baseUrl) + { + MsgCenterHttpClient._BaseUrl = baseUrl; + + service.AddSingleton(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterClient.csproj b/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterClient.csproj index 7a39c51..16e0fe8 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterClient.csproj +++ b/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterClient.csproj @@ -1,15 +1,15 @@ - - - - netcoreapp2.2 - - - - - - - - - - - + + + + netcoreapp2.2 + + + + + + + + + + + diff --git a/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterHttpClient.cs b/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterHttpClient.cs index d9913d1..f3aa21b 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterHttpClient.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/MsgCenterHttpClient.cs @@ -1,79 +1,79 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using MsgCenterClient.WechatMpTplMsg; - -namespace MsgCenterClient -{ - public class MsgCenterHttpClient - { - private IHttpClientFactory _httpClientFactory; - - internal static string _BaseUrl = ""; - - public string BaseUrl => _BaseUrl; - - public MsgCenterHttpClient(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - private HttpClient CreateHttpClient() - { - return _httpClientFactory.CreateClient(); - } - - /// - /// 发送微信公众号模板消息 - /// - /// - /// - public async Task SendWechatMpTplMsg(MsgBase msgBase) - { - var body = msgBase.ToRequestObject(); - - try - { - var res = await CreateHttpClient() - .PostAsJsonGetString(BaseUrl + "/api/msgcenter/v1/msg/SendMPTplMessage", body); - - return res.FromJsonTo(); - } - catch (Exception e) - { - LogHelper.Error("发送微信公众号模板消息失败", e + "\n消息内容:\n" + body.ToJson(true)); - - return new ApiResult(ResultCode.C_UNKNOWN_ERROR); - } - } - - /// - /// 发送微信小程序模板消息 - /// - /// - /// - public async Task SendMiniAppTplMsg(MiniAppMsgBase msgBase) - { - var body = msgBase.ToRequestObject(); - - try - { - var res = await CreateHttpClient() - .PostAsJsonGetString(BaseUrl + "/api/msgcenter/v1/msg/SendMiniAppTplMessage", body); - - return res.FromJsonTo(); - } - catch (Exception e) - { - LogHelper.Error("发送小程序模板消息失败", e + "\n消息内容:\n" + body.ToJson(true)); - - return new ApiResult(ResultCode.C_UNKNOWN_ERROR); - } - } - - } +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using MsgCenterClient.WechatMpTplMsg; + +namespace MsgCenterClient +{ + public class MsgCenterHttpClient + { + private IHttpClientFactory _httpClientFactory; + + internal static string _BaseUrl = ""; + + public string BaseUrl => _BaseUrl; + + public MsgCenterHttpClient(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + private HttpClient CreateHttpClient() + { + return _httpClientFactory.CreateClient(); + } + + /// + /// 发送微信公众号模板消息 + /// + /// + /// + public async Task SendWechatMpTplMsg(MsgBase msgBase) + { + var body = msgBase.ToRequestObject(); + + try + { + var res = await CreateHttpClient() + .PostAsJsonGetString(BaseUrl + "/api/msgcenter/v1/msg/SendMPTplMessage", body); + + return res.FromJsonTo(); + } + catch (Exception e) + { + LogHelper.Error("发送微信公众号模板消息失败", e + "\n消息内容:\n" + body.ToJson(true)); + + return new ApiResult(ResultCode.C_UNKNOWN_ERROR); + } + } + + /// + /// 发送微信小程序模板消息 + /// + /// + /// + public async Task SendMiniAppTplMsg(MiniAppMsgBase msgBase) + { + var body = msgBase.ToRequestObject(); + + try + { + var res = await CreateHttpClient() + .PostAsJsonGetString(BaseUrl + "/api/msgcenter/v1/msg/SendMiniAppTplMessage", body); + + return res.FromJsonTo(); + } + catch (Exception e) + { + LogHelper.Error("发送小程序模板消息失败", e + "\n消息内容:\n" + body.ToJson(true)); + + return new ApiResult(ResultCode.C_UNKNOWN_ERROR); + } + } + + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/FangKeShenHeMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/FangKeShenHeMsg.cs index e5856cb..78e5bbc 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/FangKeShenHeMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/FangKeShenHeMsg.cs @@ -1,50 +1,50 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 访客审核通知 - /// 模板编号:ku4vArgTpMOvwBeKQpjj6iE1nhVJyHL9xeQUjkUv5sY - /// 公众号模板库模板标题:认证成功通知 - /// - public class FangKeShenHeMsg : MiniAppMsgBase - { - public FangKeShenHeMsg(int ownerId, string appId, string openId) - : base("ku4vArgTpMOvwBeKQpjj6iE1nhVJyHL9xeQUjkUv5sY", ownerId, appId, openId) - { - } - - /// - /// 审核结果 - /// - [DataBody(1)] - public DataItem ShenHeJieGuo { get; set; } - - /// - /// 受访单位 - /// - [DataBody(2)] - public DataItem ShouFangDanWei { get; set; } - - /// - /// 事由 - /// - [DataBody(3)] - public DataItem ShiYou { get; set; } - - /// - /// 访客姓名 - /// - [DataBody(4)] - public DataItem FangKeXingMing { get; set; } - - /// - /// 手机号 - /// - [DataBody(5)] - public DataItem ShouJi { get; set; } - /// - /// 备注 - /// - [DataBody(6)] - public DataItem BeiZhu { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 访客审核通知 + /// 模板编号:ku4vArgTpMOvwBeKQpjj6iE1nhVJyHL9xeQUjkUv5sY + /// 公众号模板库模板标题:认证成功通知 + /// + public class FangKeShenHeMsg : MiniAppMsgBase + { + public FangKeShenHeMsg(int ownerId, string appId, string openId) + : base("ku4vArgTpMOvwBeKQpjj6iE1nhVJyHL9xeQUjkUv5sY", ownerId, appId, openId) + { + } + + /// + /// 审核结果 + /// + [DataBody(1)] + public DataItem ShenHeJieGuo { get; set; } + + /// + /// 受访单位 + /// + [DataBody(2)] + public DataItem ShouFangDanWei { get; set; } + + /// + /// 事由 + /// + [DataBody(3)] + public DataItem ShiYou { get; set; } + + /// + /// 访客姓名 + /// + [DataBody(4)] + public DataItem FangKeXingMing { get; set; } + + /// + /// 手机号 + /// + [DataBody(5)] + public DataItem ShouJi { get; set; } + /// + /// 备注 + /// + [DataBody(6)] + public DataItem BeiZhu { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/MsgBase.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/MsgBase.cs index 8f10b2b..1dd9c43 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/MsgBase.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMiniAppTplMsg/MsgBase.cs @@ -1,102 +1,102 @@ -using System.Collections.Generic; -using System.Linq; - -namespace MsgCenterClient.WechatMpTplMsg -{ - public class MiniAppMsgBase - { - /// - /// - /// - /// 模板Id - /// 物业Id - /// 公众号AppId - /// 用户OpenId - public MiniAppMsgBase(string templateId, int ownerId, string appId, string openId) - { - TemplateId = templateId; - OwnerId = ownerId; - AppId = appId; - OpenId = openId; - } - - /// - /// 模板Id - /// - public string TemplateId { get; } - - /// - /// 物业Id - /// - public int OwnerId { get; } - - /// - /// 公众号AppId - /// - public string AppId { get; } - - /// - /// 用户openId - /// - public string OpenId { get; } - - /// - /// 跳转的小程序页面 - /// - public string Page { get; set; } - - /// - /// 表单id - /// - public string FormId { get; set; } - - /// - /// 强调的字,可以为空 - /// - public string EmphasisKeyword { get; set; } - - public object ToRequestObject() - { - SortedDictionary bodyDic = new SortedDictionary(); - - var type = GetType(); - var properties = type.GetProperties(); - - foreach (var property in properties) - { - var bodyAttr = property.GetCustomAttributes(typeof(DataBodyAttribute), false); - - if (!bodyAttr.Any()) - { - continue; - } - - int order = ((DataBodyAttribute) bodyAttr[0]).Order; - - DataItem value = property.GetValue(this, null) as DataItem; - - if (value == null) - { - value = new DataItem(); - } - - bodyDic[order] = value; - } - - return new - { - key = TemplateId, - OwnerId = OwnerId, - From = AppId, - To = OpenId, - Content = new - { - page=this.Page, - form_id=this.FormId, - emphasis_keyword=this.EmphasisKeyword, - items = bodyDic.Values.Select(t => new {value = t.Value, color = t.Color}).ToList() - } - }; - } - } +using System.Collections.Generic; +using System.Linq; + +namespace MsgCenterClient.WechatMpTplMsg +{ + public class MiniAppMsgBase + { + /// + /// + /// + /// 模板Id + /// 物业Id + /// 公众号AppId + /// 用户OpenId + public MiniAppMsgBase(string templateId, int ownerId, string appId, string openId) + { + TemplateId = templateId; + OwnerId = ownerId; + AppId = appId; + OpenId = openId; + } + + /// + /// 模板Id + /// + public string TemplateId { get; } + + /// + /// 物业Id + /// + public int OwnerId { get; } + + /// + /// 公众号AppId + /// + public string AppId { get; } + + /// + /// 用户openId + /// + public string OpenId { get; } + + /// + /// 跳转的小程序页面 + /// + public string Page { get; set; } + + /// + /// 表单id + /// + public string FormId { get; set; } + + /// + /// 强调的字,可以为空 + /// + public string EmphasisKeyword { get; set; } + + public object ToRequestObject() + { + SortedDictionary bodyDic = new SortedDictionary(); + + var type = GetType(); + var properties = type.GetProperties(); + + foreach (var property in properties) + { + var bodyAttr = property.GetCustomAttributes(typeof(DataBodyAttribute), false); + + if (!bodyAttr.Any()) + { + continue; + } + + int order = ((DataBodyAttribute) bodyAttr[0]).Order; + + DataItem value = property.GetValue(this, null) as DataItem; + + if (value == null) + { + value = new DataItem(); + } + + bodyDic[order] = value; + } + + return new + { + key = TemplateId, + OwnerId = OwnerId, + From = AppId, + To = OpenId, + Content = new + { + page=this.Page, + form_id=this.FormId, + emphasis_keyword=this.EmphasisKeyword, + items = bodyDic.Values.Select(t => new {value = t.Value, color = t.Color}).ToList() + } + }; + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/AssetDeductMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/AssetDeductMsg.cs index a115ff0..5d0d290 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/AssetDeductMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/AssetDeductMsg.cs @@ -1,68 +1,68 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 虚拟资产冲抵成功通知 - /// 模板编号:OPENTM417732701 - /// - public class AssetDeductSuccessMsg : MsgBase - { - public AssetDeductSuccessMsg(int ownerId, string appId, string openId) - : base("OPENTM417732701", ownerId, appId, openId) - { - } - - /// - /// 账单应缴总金额 - /// - [DataBody(1)] - public DataItem Amount { get; set; } - - /// - /// 当前余额(账单金额冲抵后结存金额) - /// - [DataBody(2)] - public DataItem BalanceAmount { get; set; } - - /// - /// 扣款时间(账单冲抵完成时间) - /// - [DataBody(3)] - public DataItem SuccessTime { get; set; } - - /// - /// 扣款方式(默认为:余额冲抵) - /// - [DataBody(4)] - public DataItem PayType { get; set; }=new DataItem(){Value = "余额冲抵"}; - } - - /// - /// 虚拟资产冲抵失败通知 - /// 模板编号:OPENTM414769357 - /// - public class AssetDeductFailMsg : MsgBase - { - public AssetDeductFailMsg(int ownerId, string appId, string openId) - : base("OPENTM414769357", ownerId, appId, openId) - { - } - - /// - /// 扣费时间(所有账单扣费失败时间) - /// - [DataBody(1)] - public DataItem FailTime { get; set; } - - /// - /// 扣费金额(所有冲抵失败账单的金额总和) - /// - [DataBody(2)] - public DataItem Amount { get; set; } - - /// - /// 账户余额(虚拟帐户中剩余金额) - /// - [DataBody(3)] - public DataItem BalanceAmount { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 虚拟资产冲抵成功通知 + /// 模板编号:OPENTM417732701 + /// + public class AssetDeductSuccessMsg : MsgBase + { + public AssetDeductSuccessMsg(int ownerId, string appId, string openId) + : base("OPENTM417732701", ownerId, appId, openId) + { + } + + /// + /// 账单应缴总金额 + /// + [DataBody(1)] + public DataItem Amount { get; set; } + + /// + /// 当前余额(账单金额冲抵后结存金额) + /// + [DataBody(2)] + public DataItem BalanceAmount { get; set; } + + /// + /// 扣款时间(账单冲抵完成时间) + /// + [DataBody(3)] + public DataItem SuccessTime { get; set; } + + /// + /// 扣款方式(默认为:余额冲抵) + /// + [DataBody(4)] + public DataItem PayType { get; set; }=new DataItem(){Value = "余额冲抵"}; + } + + /// + /// 虚拟资产冲抵失败通知 + /// 模板编号:OPENTM414769357 + /// + public class AssetDeductFailMsg : MsgBase + { + public AssetDeductFailMsg(int ownerId, string appId, string openId) + : base("OPENTM414769357", ownerId, appId, openId) + { + } + + /// + /// 扣费时间(所有账单扣费失败时间) + /// + [DataBody(1)] + public DataItem FailTime { get; set; } + + /// + /// 扣费金额(所有冲抵失败账单的金额总和) + /// + [DataBody(2)] + public DataItem Amount { get; set; } + + /// + /// 账户余额(虚拟帐户中剩余金额) + /// + [DataBody(3)] + public DataItem BalanceAmount { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeShiBaiMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeShiBaiMsg.cs index cf5c818..2b8e878 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeShiBaiMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeShiBaiMsg.cs @@ -1,27 +1,27 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 车辆审核不通过通知 - /// 模板编号:OPENTM408157154 - /// 公众号模板库模板标题:审核不过通知 - /// - public class CheLiangShenHeShiBaiMsg: MsgBase - { - public CheLiangShenHeShiBaiMsg(int ownerId, string appId, string openId) - : base("OPENTM408157154", ownerId, appId, openId) - { - } - - /// - /// 姓名 - /// - [DataBody(1)] - public DataItem XingMing { get; set; } - - /// - /// 手机号 - /// - [DataBody(2)] - public DataItem ShouJiHao { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 车辆审核不通过通知 + /// 模板编号:OPENTM408157154 + /// 公众号模板库模板标题:审核不过通知 + /// + public class CheLiangShenHeShiBaiMsg: MsgBase + { + public CheLiangShenHeShiBaiMsg(int ownerId, string appId, string openId) + : base("OPENTM408157154", ownerId, appId, openId) + { + } + + /// + /// 姓名 + /// + [DataBody(1)] + public DataItem XingMing { get; set; } + + /// + /// 手机号 + /// + [DataBody(2)] + public DataItem ShouJiHao { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTiJiaoMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTiJiaoMsg.cs index 43a7e28..d8e7572 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTiJiaoMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTiJiaoMsg.cs @@ -1,27 +1,27 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 车辆审核申请提交成功通知 - /// 模板编号:OPENTM411517700 - /// 公众号模板库模板标题:申请提交成功通知 - /// - public class CheLiangShenHeTiJiaoMsg: MsgBase - { - public CheLiangShenHeTiJiaoMsg(int ownerId, string appId, string openId) - : base("OPENTM411517700", ownerId, appId, openId) - { - } - - /// - /// 服务类型 - /// - [DataBody(1)] - public DataItem FuWuLeiXing { get; set; } - - /// - /// 提交时间 - /// - [DataBody(2)] - public DataItem TiJiaoShiJian { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 车辆审核申请提交成功通知 + /// 模板编号:OPENTM411517700 + /// 公众号模板库模板标题:申请提交成功通知 + /// + public class CheLiangShenHeTiJiaoMsg: MsgBase + { + public CheLiangShenHeTiJiaoMsg(int ownerId, string appId, string openId) + : base("OPENTM411517700", ownerId, appId, openId) + { + } + + /// + /// 服务类型 + /// + [DataBody(1)] + public DataItem FuWuLeiXing { get; set; } + + /// + /// 提交时间 + /// + [DataBody(2)] + public DataItem TiJiaoShiJian { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTongGuoMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTongGuoMsg.cs index 9cd56af..2122686 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTongGuoMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/CheLiangShenHeTongGuoMsg.cs @@ -1,39 +1,39 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 车辆审核通过通知 - /// 模板编号:OPENTM416664350 - /// 公众号模板库模板标题:审核通过提醒 - /// - public class CheLiangShenHeTongGuoMsg: MsgBase - { - public CheLiangShenHeTongGuoMsg( int ownerId, string appId, string openId) - : base("OPENTM416664350", ownerId, appId, openId) - { - } - - /// - /// 姓名 - /// - [DataBody(1)] - public DataItem XingMing { get; set; } - - /// - /// 手机号 - /// - [DataBody(2)] - public DataItem ShouJiHao { get; set; } - - /// - /// 车牌号 - /// - [DataBody(3)] - public DataItem ChePaiHao { get; set; } - - /// - /// 审核时间 - /// - [DataBody(4)] - public DataItem ShenHeShiJian { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 车辆审核通过通知 + /// 模板编号:OPENTM416664350 + /// 公众号模板库模板标题:审核通过提醒 + /// + public class CheLiangShenHeTongGuoMsg: MsgBase + { + public CheLiangShenHeTongGuoMsg( int ownerId, string appId, string openId) + : base("OPENTM416664350", ownerId, appId, openId) + { + } + + /// + /// 姓名 + /// + [DataBody(1)] + public DataItem XingMing { get; set; } + + /// + /// 手机号 + /// + [DataBody(2)] + public DataItem ShouJiHao { get; set; } + + /// + /// 车牌号 + /// + [DataBody(3)] + public DataItem ChePaiHao { get; set; } + + /// + /// 审核时间 + /// + [DataBody(4)] + public DataItem ShenHeShiJian { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/DataItem.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/DataItem.cs index aecce67..c00bd76 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/DataItem.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/DataItem.cs @@ -1,28 +1,28 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - public class DataItem - { - public string Value { get; set; } = ""; - - // - // 摘要: - // 16进制颜色代码,如:#FF0000 - public string Color { get; set; } = "#173177"; - - public DataItem() - { - - } - - public DataItem(string value) - { - Value = value; - } - - public DataItem(string value, string color) - { - Value = value; - Color = color; - } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + public class DataItem + { + public string Value { get; set; } = ""; + + // + // 摘要: + // 16进制颜色代码,如:#FF0000 + public string Color { get; set; } = "#173177"; + + public DataItem() + { + + } + + public DataItem(string value) + { + Value = value; + } + + public DataItem(string value, string color) + { + Value = value; + Color = color; + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangKeDengJiTongZhi.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangKeDengJiTongZhi.cs index d5fd7dc..e057ffb 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangKeDengJiTongZhi.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangKeDengJiTongZhi.cs @@ -1,36 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace MsgCenterClient.WechatMpTplMsg -{ - public class FangKeDengJiTongZhi : MsgBase - { - public FangKeDengJiTongZhi(int ownerId, string appId, string openId) - : base("OPENTM417110808", ownerId, appId, openId) - { - - } - - /// - /// 标题 - /// - [DataBody(1)] - public DataItem Name { get; set; } - /// - /// 标题 - /// - [DataBody(2)] - public DataItem Company { get; set; } - /// - /// 标题 - /// - [DataBody(3)] - public DataItem Reason { get; set; } - /// - /// 标题 - /// - [DataBody(4)] - public DataItem Time { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace MsgCenterClient.WechatMpTplMsg +{ + public class FangKeDengJiTongZhi : MsgBase + { + public FangKeDengJiTongZhi(int ownerId, string appId, string openId) + : base("OPENTM417110808", ownerId, appId, openId) + { + + } + + /// + /// 标题 + /// + [DataBody(1)] + public DataItem Name { get; set; } + /// + /// 标题 + /// + [DataBody(2)] + public DataItem Company { get; set; } + /// + /// 标题 + /// + [DataBody(3)] + public DataItem Reason { get; set; } + /// + /// 标题 + /// + [DataBody(4)] + public DataItem Time { get; set; } + } +} diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengChengGongMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengChengGongMsg.cs index 0c0a42e..9aecf60 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengChengGongMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengChengGongMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 房屋认证成功通知 - /// 模板编号:OPENTM403179452 - /// 公众号模板库模板标题:认证成功通知 - /// - public class FangWuRenZhengChengGongMsg : MsgBase - { - public FangWuRenZhengChengGongMsg(int ownerId, string appId, string openId) - : base("OPENTM403179452", ownerId, appId, openId) - { - } - - /// - /// 认证类型 - /// - [DataBody(1)] - public DataItem RenZhengLeiXing { get; set; } - - /// - /// 审核结果 - /// - [DataBody(2)] - public DataItem ShenHeJieGuo { get; set; } - - /// - /// 审核时间 - /// - [DataBody(3)] - public DataItem ShenHeShiJian { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 房屋认证成功通知 + /// 模板编号:OPENTM403179452 + /// 公众号模板库模板标题:认证成功通知 + /// + public class FangWuRenZhengChengGongMsg : MsgBase + { + public FangWuRenZhengChengGongMsg(int ownerId, string appId, string openId) + : base("OPENTM403179452", ownerId, appId, openId) + { + } + + /// + /// 认证类型 + /// + [DataBody(1)] + public DataItem RenZhengLeiXing { get; set; } + + /// + /// 审核结果 + /// + [DataBody(2)] + public DataItem ShenHeJieGuo { get; set; } + + /// + /// 审核时间 + /// + [DataBody(3)] + public DataItem ShenHeShiJian { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengShiBaiMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengShiBaiMsg.cs index 9726095..aa82651 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengShiBaiMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/FangWuRenZhengShiBaiMsg.cs @@ -1,39 +1,39 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 房屋认证失败通知 - /// 模板编号:OPENTM403179639 - /// 公众号模板库模板标题:认证失败通知 - /// - public class FangWuRenZhengShiBaiMsg: MsgBase - { - public FangWuRenZhengShiBaiMsg(int ownerId, string appId, string openId) - : base("OPENTM403179639", ownerId, appId, openId) - { - } - - /// - /// 认证类型 - /// - [DataBody(1)] - public DataItem RenZhengLeiXing { get; set; } - - /// - /// 审核结果 - /// - [DataBody(2)] - public DataItem ShenHeJieGuo { get; set; } - - /// - /// 审核时间 - /// - [DataBody(3)] - public DataItem ShenHeShiJian { get; set; } - - /// - /// 结果描述 - /// - [DataBody(4)] - public DataItem JieGuoMiaoShu { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 房屋认证失败通知 + /// 模板编号:OPENTM403179639 + /// 公众号模板库模板标题:认证失败通知 + /// + public class FangWuRenZhengShiBaiMsg: MsgBase + { + public FangWuRenZhengShiBaiMsg(int ownerId, string appId, string openId) + : base("OPENTM403179639", ownerId, appId, openId) + { + } + + /// + /// 认证类型 + /// + [DataBody(1)] + public DataItem RenZhengLeiXing { get; set; } + + /// + /// 审核结果 + /// + [DataBody(2)] + public DataItem ShenHeJieGuo { get; set; } + + /// + /// 审核时间 + /// + [DataBody(3)] + public DataItem ShenHeShiJian { get; set; } + + /// + /// 结果描述 + /// + [DataBody(4)] + public DataItem JieGuoMiaoShu { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/GongDanWanJieMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/GongDanWanJieMsg.cs index 3dca16e..84d95a4 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/GongDanWanJieMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/GongDanWanJieMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 工单处理完结 - /// 模板编号:OPENTM201820050 - /// 公众号模板库模板标题:工单进度通知 - /// - public class GongDanWanJieMsg: MsgBase - { - public GongDanWanJieMsg( int ownerId, string appId, string openId) - : base("OPENTM201820050", ownerId, appId, openId) - { - } - - /// - /// 工单号 - /// - [DataBody(1)] - public DataItem GongDanHao { get; set; } - - /// - /// 工单进度 - /// - [DataBody(2)] - public DataItem GongDanJinDu { get; set; } - - /// - /// 工单处理人 - /// - [DataBody(3)] - public DataItem GongDanChuLiRen { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 工单处理完结 + /// 模板编号:OPENTM201820050 + /// 公众号模板库模板标题:工单进度通知 + /// + public class GongDanWanJieMsg: MsgBase + { + public GongDanWanJieMsg( int ownerId, string appId, string openId) + : base("OPENTM201820050", ownerId, appId, openId) + { + } + + /// + /// 工单号 + /// + [DataBody(1)] + public DataItem GongDanHao { get; set; } + + /// + /// 工单进度 + /// + [DataBody(2)] + public DataItem GongDanJinDu { get; set; } + + /// + /// 工单处理人 + /// + [DataBody(3)] + public DataItem GongDanChuLiRen { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JiaoFeiChengGongMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JiaoFeiChengGongMsg.cs index c5eecea..2c81b8a 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JiaoFeiChengGongMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JiaoFeiChengGongMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 缴费成功通知 - /// 模板编号:OPENTM412117951 - /// 公众号模板库模板标题:缴费成功通知 - /// - public class JiaoFeiChengGongMsg : MsgBase - { - public JiaoFeiChengGongMsg(int ownerId, string appId, string openId) - : base("OPENTM412117951", ownerId, appId, openId) - { - } - - /// - /// 缴费时间 - /// - [DataBody(1)] - public DataItem JiaoFeiShiJian { get; set; } - - /// - /// 缴费方式 - /// - [DataBody(2)] - public DataItem JiaoFeiFangShi { get; set; } - - /// - /// 缴费金额 - /// - [DataBody(3)] - public DataItem JiaoFeiJinE { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 缴费成功通知 + /// 模板编号:OPENTM412117951 + /// 公众号模板库模板标题:缴费成功通知 + /// + public class JiaoFeiChengGongMsg : MsgBase + { + public JiaoFeiChengGongMsg(int ownerId, string appId, string openId) + : base("OPENTM412117951", ownerId, appId, openId) + { + } + + /// + /// 缴费时间 + /// + [DataBody(1)] + public DataItem JiaoFeiShiJian { get; set; } + + /// + /// 缴费方式 + /// + [DataBody(2)] + public DataItem JiaoFeiFangShi { get; set; } + + /// + /// 缴费金额 + /// + [DataBody(3)] + public DataItem JiaoFeiJinE { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JieDanMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JieDanMsg.cs index cb581cd..68ba4cb 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JieDanMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/JieDanMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 接单通知 - /// 模板编号:OPENTM201820050 - /// 公众号模板库模板标题:工单进度通知 - /// - public class JieDanMsg: MsgBase - { - public JieDanMsg(int ownerId, string appId, string openId) - : base("OPENTM201820050", ownerId, appId, openId) - { - } - - /// - /// 工单号 - /// - [DataBody(1)] - public DataItem GongDanHao { get; set; } - - /// - /// 工单进度 - /// - [DataBody(2)] - public DataItem GongDanJinDu { get; set; } - - /// - /// 工单处理人 - /// - [DataBody(3)] - public DataItem GongDanChuLiRen { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 接单通知 + /// 模板编号:OPENTM201820050 + /// 公众号模板库模板标题:工单进度通知 + /// + public class JieDanMsg: MsgBase + { + public JieDanMsg(int ownerId, string appId, string openId) + : base("OPENTM201820050", ownerId, appId, openId) + { + } + + /// + /// 工单号 + /// + [DataBody(1)] + public DataItem GongDanHao { get; set; } + + /// + /// 工单进度 + /// + [DataBody(2)] + public DataItem GongDanJinDu { get; set; } + + /// + /// 工单处理人 + /// + [DataBody(3)] + public DataItem GongDanChuLiRen { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/LinShiDaoFangMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/LinShiDaoFangMsg.cs index 6f49b19..674aed9 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/LinShiDaoFangMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/LinShiDaoFangMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 临时到访通知 - /// 模板编号:5NE7oojOE4jhUpv8bixYqWgfkqcOInVBTjb_lHWPrSw - /// 公众号模板库模板标题:物业管理通知 - /// - public class LinShiDaoFangMsg: MsgBase - { - public LinShiDaoFangMsg(int ownerId, string appId, string openId) - : base("5NE7oojOE4jhUpv8bixYqWgfkqcOInVBTjb_lHWPrSw", ownerId, appId, openId) - { - } - - /// - /// 标题 - /// - [DataBody(1)] - public DataItem BiaoTi { get; set; } - - /// - /// 发布时间 - /// - [DataBody(2)] - public DataItem FaBuShiJian { get; set; } - - /// - /// 内容 - /// - [DataBody(3)] - public DataItem NeiRong { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 临时到访通知 + /// 模板编号:5NE7oojOE4jhUpv8bixYqWgfkqcOInVBTjb_lHWPrSw + /// 公众号模板库模板标题:物业管理通知 + /// + public class LinShiDaoFangMsg: MsgBase + { + public LinShiDaoFangMsg(int ownerId, string appId, string openId) + : base("5NE7oojOE4jhUpv8bixYqWgfkqcOInVBTjb_lHWPrSw", ownerId, appId, openId) + { + } + + /// + /// 标题 + /// + [DataBody(1)] + public DataItem BiaoTi { get; set; } + + /// + /// 发布时间 + /// + [DataBody(2)] + public DataItem FaBuShiJian { get; set; } + + /// + /// 内容 + /// + [DataBody(3)] + public DataItem NeiRong { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MenJinYaoShiShouQuanMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MenJinYaoShiShouQuanMsg.cs index 2efdadc..aa1c528 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MenJinYaoShiShouQuanMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MenJinYaoShiShouQuanMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 门禁钥匙授权通知 - /// 模板编号:OPENTM408634701 - /// 公众号模板库模板标题:授权成功通知 - /// - public class MenJinYaoShiShouQuanMsg: MsgBase - { - public MenJinYaoShiShouQuanMsg( int ownerId, string appId, string openId) - : base("OPENTM408634701", ownerId, appId, openId) - { - } - - /// - /// 授权人 - /// - [DataBody(1)] - public DataItem ShouQunRen { get; set; } - - /// - /// 被授权人 - /// - [DataBody(2)] - public DataItem BeiShouQunRen { get; set; } - - /// - /// 授权状态 - /// - [DataBody(3)] - public DataItem ShouQuanZhuangTai { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 门禁钥匙授权通知 + /// 模板编号:OPENTM408634701 + /// 公众号模板库模板标题:授权成功通知 + /// + public class MenJinYaoShiShouQuanMsg: MsgBase + { + public MenJinYaoShiShouQuanMsg( int ownerId, string appId, string openId) + : base("OPENTM408634701", ownerId, appId, openId) + { + } + + /// + /// 授权人 + /// + [DataBody(1)] + public DataItem ShouQunRen { get; set; } + + /// + /// 被授权人 + /// + [DataBody(2)] + public DataItem BeiShouQunRen { get; set; } + + /// + /// 授权状态 + /// + [DataBody(3)] + public DataItem ShouQuanZhuangTai { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MsgBase.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MsgBase.cs index 4caf0d7..bb9276a 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MsgBase.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/MsgBase.cs @@ -1,117 +1,117 @@ -using System.Collections.Generic; -using System.Linq; - -namespace MsgCenterClient.WechatMpTplMsg -{ - public class MsgBase - { - /// - /// - /// - /// 模板Id - /// 物业Id - /// 公众号AppId - /// 用户OpenId - public MsgBase(string templateId, int ownerId, string appId, string openId) - { - TemplateId = templateId; - OwnerId = ownerId; - AppId = appId; - OpenId = openId; - } - - /// - /// 头部内容 - /// - public DataItem Head { get; set; } - - /// - /// 底部内容 - /// - public DataItem Foot { get; set; } - - /// - /// 模板Id - /// - public string TemplateId { get; } - - /// - /// 物业Id - /// - public int OwnerId { get; } - - /// - /// 公众号AppId - /// - public string AppId { get; } - - /// - /// 用户openId - /// - public string OpenId { get; } - - /// - /// 跳转的Url - /// - public string Url { get; set; } - - /// - /// 跳转的小程序的Appid,为空的话默认跳转h5指定的url - /// - public string MiniAppId { get; set; } - - - public object ToRequestObject() - { - SortedDictionary bodyDic = new SortedDictionary(); - - var type = GetType(); - var properties = type.GetProperties(); - - foreach (var property in properties) - { - var bodyAttr = property.GetCustomAttributes(typeof(DataBodyAttribute), false); - - if (!bodyAttr.Any()) - { - continue; - } - - int order = ((DataBodyAttribute) bodyAttr[0]).Order; - - DataItem value = property.GetValue(this, null) as DataItem; - - if (value == null) - { - value = new DataItem(); - } - - bodyDic[order] = value; - } - - return new - { - key = TemplateId, - Content = new - { - Url, - MiniAppId, - first = new - { - value = Head?.Value, - color = Head?.Color - }, - remark = new - { - value = Foot?.Value, - color = Foot?.Color - }, - items = bodyDic.Values.Select(t => new {value = t.Value, color = t.Color}).ToList() - }, - OwnerId = OwnerId, - From = AppId, - To = OpenId - }; - } - } +using System.Collections.Generic; +using System.Linq; + +namespace MsgCenterClient.WechatMpTplMsg +{ + public class MsgBase + { + /// + /// + /// + /// 模板Id + /// 物业Id + /// 公众号AppId + /// 用户OpenId + public MsgBase(string templateId, int ownerId, string appId, string openId) + { + TemplateId = templateId; + OwnerId = ownerId; + AppId = appId; + OpenId = openId; + } + + /// + /// 头部内容 + /// + public DataItem Head { get; set; } + + /// + /// 底部内容 + /// + public DataItem Foot { get; set; } + + /// + /// 模板Id + /// + public string TemplateId { get; } + + /// + /// 物业Id + /// + public int OwnerId { get; } + + /// + /// 公众号AppId + /// + public string AppId { get; } + + /// + /// 用户openId + /// + public string OpenId { get; } + + /// + /// 跳转的Url + /// + public string Url { get; set; } + + /// + /// 跳转的小程序的Appid,为空的话默认跳转h5指定的url + /// + public string MiniAppId { get; set; } + + + public object ToRequestObject() + { + SortedDictionary bodyDic = new SortedDictionary(); + + var type = GetType(); + var properties = type.GetProperties(); + + foreach (var property in properties) + { + var bodyAttr = property.GetCustomAttributes(typeof(DataBodyAttribute), false); + + if (!bodyAttr.Any()) + { + continue; + } + + int order = ((DataBodyAttribute) bodyAttr[0]).Order; + + DataItem value = property.GetValue(this, null) as DataItem; + + if (value == null) + { + value = new DataItem(); + } + + bodyDic[order] = value; + } + + return new + { + key = TemplateId, + Content = new + { + Url, + MiniAppId, + first = new + { + value = Head?.Value, + color = Head?.Color + }, + remark = new + { + value = Foot?.Value, + color = Foot?.Color + }, + items = bodyDic.Values.Select(t => new {value = t.Value, color = t.Color}).ToList() + }, + OwnerId = OwnerId, + From = AppId, + To = OpenId + }; + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/PaiGongMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/PaiGongMsg.cs index 599d795..7f5f346 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/PaiGongMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/PaiGongMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 派工通知 - /// 模板编号:OPENTM201820050 - /// 公众号模板库模板标题:工单进度通知 - /// - public class PaiGongMsg: MsgBase - { - public PaiGongMsg( int ownerId, string appId, string openId) - : base("OPENTM201820050", ownerId, appId, openId) - { - } - - /// - /// 工单号 - /// - [DataBody(1)] - public DataItem GongDanHao { get; set; } - - /// - /// 工单进度 - /// - [DataBody(2)] - public DataItem GongDanJinDu { get; set; } - - /// - /// 工单处理人 - /// - [DataBody(3)] - public DataItem GongDanChuLiRen { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 派工通知 + /// 模板编号:OPENTM201820050 + /// 公众号模板库模板标题:工单进度通知 + /// + public class PaiGongMsg: MsgBase + { + public PaiGongMsg( int ownerId, string appId, string openId) + : base("OPENTM201820050", ownerId, appId, openId) + { + } + + /// + /// 工单号 + /// + [DataBody(1)] + public DataItem GongDanHao { get; set; } + + /// + /// 工单进度 + /// + [DataBody(2)] + public DataItem GongDanJinDu { get; set; } + + /// + /// 工单处理人 + /// + [DataBody(3)] + public DataItem GongDanChuLiRen { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengChengGongMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengChengGongMsg.cs index 1a54557..66d4690 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengChengGongMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengChengGongMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 人脸钥匙认证成功通知 - /// 模板编号:OPENTM403179452 - /// 公众号模板库模板标题:认证成功通知 - /// - public class RenLianRenZhengChengGongMsg: MsgBase - { - public RenLianRenZhengChengGongMsg( int ownerId, string appId, string openId) - : base("OPENTM403179452", ownerId, appId, openId) - { - } - - /// - /// 认证类型 - /// - [DataBody(1)] - public DataItem RenZhengLeiXing { get; set; } - - /// - /// 审核结果 - /// - [DataBody(2)] - public DataItem ShenHeJieGuo { get; set; } - - /// - /// 审核时间 - /// - [DataBody(3)] - public DataItem ShenHeShiJian { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 人脸钥匙认证成功通知 + /// 模板编号:OPENTM403179452 + /// 公众号模板库模板标题:认证成功通知 + /// + public class RenLianRenZhengChengGongMsg: MsgBase + { + public RenLianRenZhengChengGongMsg( int ownerId, string appId, string openId) + : base("OPENTM403179452", ownerId, appId, openId) + { + } + + /// + /// 认证类型 + /// + [DataBody(1)] + public DataItem RenZhengLeiXing { get; set; } + + /// + /// 审核结果 + /// + [DataBody(2)] + public DataItem ShenHeJieGuo { get; set; } + + /// + /// 审核时间 + /// + [DataBody(3)] + public DataItem ShenHeShiJian { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengShiBaiMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengShiBaiMsg.cs index 82e0ee6..067e54d 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengShiBaiMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/RenLianRenZhengShiBaiMsg.cs @@ -1,39 +1,39 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 人脸钥匙认证失败通知 - /// 模板编号:OPENTM403179639 - /// 公众号模板库模板标题:认证失败通知 - /// - public class RenLianRenZhengShiBaiMsg: MsgBase - { - public RenLianRenZhengShiBaiMsg( int ownerId, string appId, string openId) - : base("OPENTM403179639", ownerId, appId, openId) - { - } - - /// - /// 认证类型 - /// - [DataBody(1)] - public DataItem RenZhengLeiXing { get; set; } - - /// - /// 审核结果 - /// - [DataBody(2)] - public DataItem ShenHeJieGuo { get; set; } - - /// - /// 审核时间 - /// - [DataBody(3)] - public DataItem ShenHeShiJian { get; set; } - - /// - /// 结果描述 - /// - [DataBody(4)] - public DataItem JieGuoMiaoShu { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 人脸钥匙认证失败通知 + /// 模板编号:OPENTM403179639 + /// 公众号模板库模板标题:认证失败通知 + /// + public class RenLianRenZhengShiBaiMsg: MsgBase + { + public RenLianRenZhengShiBaiMsg( int ownerId, string appId, string openId) + : base("OPENTM403179639", ownerId, appId, openId) + { + } + + /// + /// 认证类型 + /// + [DataBody(1)] + public DataItem RenZhengLeiXing { get; set; } + + /// + /// 审核结果 + /// + [DataBody(2)] + public DataItem ShenHeJieGuo { get; set; } + + /// + /// 审核时间 + /// + [DataBody(3)] + public DataItem ShenHeShiJian { get; set; } + + /// + /// 结果描述 + /// + [DataBody(4)] + public DataItem JieGuoMiaoShu { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ShouDongCuiJiaoMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ShouDongCuiJiaoMsg.cs index c1764c6..3b59075 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ShouDongCuiJiaoMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ShouDongCuiJiaoMsg.cs @@ -1,27 +1,27 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 催费通知(手动催缴) - /// 模板编号:OPENTM411227201 - /// 公众号模板库模板标题:待付账单通知 - /// - public class ShouDongCuiJiaoMsg : MsgBase - { - public ShouDongCuiJiaoMsg(int ownerId, string appId, string openId) - : base("OPENTM411227201", ownerId, appId, openId) - { - } - - /// - /// 账单金额 - /// - [DataBody(1)] - public DataItem ZhangDanJinE { get; set; } - - /// - /// 账单日期 - /// - [DataBody(2)] - public DataItem ZhangDanRiQi { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 催费通知(手动催缴) + /// 模板编号:OPENTM411227201 + /// 公众号模板库模板标题:待付账单通知 + /// + public class ShouDongCuiJiaoMsg : MsgBase + { + public ShouDongCuiJiaoMsg(int ownerId, string appId, string openId) + : base("OPENTM411227201", ownerId, appId, openId) + { + } + + /// + /// 账单金额 + /// + [DataBody(1)] + public DataItem ZhangDanJinE { get; set; } + + /// + /// 账单日期 + /// + [DataBody(2)] + public DataItem ZhangDanRiQi { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/YaoYueDaoFangMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/YaoYueDaoFangMsg.cs index 1776c71..e061672 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/YaoYueDaoFangMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/YaoYueDaoFangMsg.cs @@ -1,33 +1,33 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 邀请访客到访通知 - /// 模板编号:rKssp0BPmK-XmGXCbrh3f6e9NVpb75Zqzui8Hcx26dE - /// 公众号模板库模板标题:来访通知 - /// - public class YaoYueDaoFangMsg: MsgBase - { - public YaoYueDaoFangMsg(int ownerId, string appId, string openId) - : base("OPENTM408101810", ownerId, appId, openId) - { - } - - /// - /// 访客名称 - /// - [DataBody(1)] - public DataItem FangKeMingCheng { get; set; } - - /// - /// 访客电话 - /// - [DataBody(2)] - public DataItem FangKeDianHua { get; set; } - - /// - /// 来访时间 - /// - [DataBody(3)] - public DataItem LaiFangShiJian { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 邀请访客到访通知 + /// 模板编号:rKssp0BPmK-XmGXCbrh3f6e9NVpb75Zqzui8Hcx26dE + /// 公众号模板库模板标题:来访通知 + /// + public class YaoYueDaoFangMsg: MsgBase + { + public YaoYueDaoFangMsg(int ownerId, string appId, string openId) + : base("OPENTM408101810", ownerId, appId, openId) + { + } + + /// + /// 访客名称 + /// + [DataBody(1)] + public DataItem FangKeMingCheng { get; set; } + + /// + /// 访客电话 + /// + [DataBody(2)] + public DataItem FangKeDianHua { get; set; } + + /// + /// 来访时间 + /// + [DataBody(3)] + public DataItem LaiFangShiJian { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ZiDongCuiJiaoMsg.cs b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ZiDongCuiJiaoMsg.cs index 54d65e1..dad0b83 100644 --- a/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ZiDongCuiJiaoMsg.cs +++ b/Infrastructure/ServiceClient/MsgCenterClient/WechatMpTplMsg/ZiDongCuiJiaoMsg.cs @@ -1,27 +1,27 @@ -namespace MsgCenterClient.WechatMpTplMsg -{ - /// - /// 催费通知(自动催缴) - /// 模板编号:OPENTM411227201 - /// 公众号模板库模板标题:待付账单通知 - /// - public class ZiDongCuiJiaoMsg : MsgBase - { - public ZiDongCuiJiaoMsg(int ownerId, string appId, string openId) - : base("OPENTM411227201", ownerId, appId, openId) - { - } - - /// - /// 账单金额 - /// - [DataBody(1)] - public DataItem ZhangDanJinE { get; set; } - - /// - /// 账单日期 - /// - [DataBody(2)] - public DataItem ZhangDanRiQi { get; set; } - } +namespace MsgCenterClient.WechatMpTplMsg +{ + /// + /// 催费通知(自动催缴) + /// 模板编号:OPENTM411227201 + /// 公众号模板库模板标题:待付账单通知 + /// + public class ZiDongCuiJiaoMsg : MsgBase + { + public ZiDongCuiJiaoMsg(int ownerId, string appId, string openId) + : base("OPENTM411227201", ownerId, appId, openId) + { + } + + /// + /// 账单金额 + /// + [DataBody(1)] + public DataItem ZhangDanJinE { get; set; } + + /// + /// 账单日期 + /// + [DataBody(2)] + public DataItem ZhangDanRiQi { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/DaiFu.cs b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/DaiFu.cs index 7f9416f..a6c08da 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/DaiFu.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/DaiFu.cs @@ -1,26 +1,26 @@ -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using Hncore.Payment.Request; -using PaymentCenterClient; - -namespace Hncore.Payment.ClientExtension -{ - public static class DaiFu - { - /// - /// 单笔代付 - /// - /// - /// - /// - public static async Task SingleDaiFu(this PaymentCenterHttpClient client, SingleDaiFuRequest request) - { - var res = await client.CreateHttpClient() - .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/ZhongXin/DaiFu/SingleDaiFu", request); - - return res.FromJsonTo(); - } - } +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using Hncore.Payment.Request; +using PaymentCenterClient; + +namespace Hncore.Payment.ClientExtension +{ + public static class DaiFu + { + /// + /// 单笔代付 + /// + /// + /// + /// + public static async Task SingleDaiFu(this PaymentCenterHttpClient client, SingleDaiFuRequest request) + { + var res = await client.CreateHttpClient() + .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/ZhongXin/DaiFu/SingleDaiFu", request); + + return res.FromJsonTo(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/EPay.cs b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/EPay.cs index 574cf47..56dc653 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/EPay.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/EPay.cs @@ -1,27 +1,27 @@ -using Hncore.Payment.Request; -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using PaymentCenterClient; - -namespace Hncore.Payment.ClientExtension -{ - public static class EPay - { - /// - /// 创建Pos机订单(推送订单到Pos机) - /// - /// - /// - /// - public static async Task CreatePosOrder(this PaymentCenterHttpClient client, - EPayCreateOrderRequest request) - { - var res = await client.CreateHttpClient() - .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/EPay/PushOrder", request); - - return res.FromJsonTo(); - } - } +using Hncore.Payment.Request; +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using PaymentCenterClient; + +namespace Hncore.Payment.ClientExtension +{ + public static class EPay + { + /// + /// 创建Pos机订单(推送订单到Pos机) + /// + /// + /// + /// + public static async Task CreatePosOrder(this PaymentCenterHttpClient client, + EPayCreateOrderRequest request) + { + var res = await client.CreateHttpClient() + .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/EPay/PushOrder", request); + + return res.FromJsonTo(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/OffLinePay.cs b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/OffLinePay.cs index cf877c6..c3704c2 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/OffLinePay.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/OffLinePay.cs @@ -1,26 +1,26 @@ -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using Hncore.Payment.Request; -using PaymentCenterClient; - -namespace Hncore.Payment.ClientExtension -{ - /// - /// 线下支付 - /// - public static class OffLinePay - { - public static async Task CreateOffLinePaySuccessedRecord(this PaymentCenterHttpClient client, - CreateOffLinePaySuccessedRecordRequest request) - { - var res = await client.CreateHttpClient() - .PostAsJsonGetString( - $"{client.BaseUrl}api/paymentcenter/v1/Payment/CreateOffLinePaySuccessedRecord", - request); - - return res.FromJsonTo(); - } - } +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using Hncore.Payment.Request; +using PaymentCenterClient; + +namespace Hncore.Payment.ClientExtension +{ + /// + /// 线下支付 + /// + public static class OffLinePay + { + public static async Task CreateOffLinePaySuccessedRecord(this PaymentCenterHttpClient client, + CreateOffLinePaySuccessedRecordRequest request) + { + var res = await client.CreateHttpClient() + .PostAsJsonGetString( + $"{client.BaseUrl}api/paymentcenter/v1/Payment/CreateOffLinePaySuccessedRecord", + request); + + return res.FromJsonTo(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QrPay.cs b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QrPay.cs index 436bc78..a233d70 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QrPay.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QrPay.cs @@ -1,30 +1,30 @@ -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using Hncore.Payment.Request; -using Hncore.Payment.Response; -using PaymentCenterClient; - -namespace Hncore.Payment.ClientExtension -{ - public static class QrPay - { - /// - /// 扫码支付下单 - /// - /// - /// - /// - public static async Task> QrPayCreateOrder( - this PaymentCenterHttpClient client, - QrPayCreateOrderRequest request) - { - var res = await client.CreateHttpClient() - .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/QrPay/CreateOrder", - request); - - return res.FromJsonTo>(); - } - } +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using Hncore.Payment.Request; +using Hncore.Payment.Response; +using PaymentCenterClient; + +namespace Hncore.Payment.ClientExtension +{ + public static class QrPay + { + /// + /// 扫码支付下单 + /// + /// + /// + /// + public static async Task> QrPayCreateOrder( + this PaymentCenterHttpClient client, + QrPayCreateOrderRequest request) + { + var res = await client.CreateHttpClient() + .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/QrPay/CreateOrder", + request); + + return res.FromJsonTo>(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QueryOrder.cs b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QueryOrder.cs index 022dad5..b205bc1 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QueryOrder.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/QueryOrder.cs @@ -1,26 +1,26 @@ -using System.Threading.Tasks; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using Hncore.Payment.Response; -using PaymentCenterClient; - -namespace Hncore.Payment.ClientExtension -{ - public static class QueryOrderExtension - { - /// - /// 订单查询 - /// - /// - /// - /// - public static async Task> QueryOrder(this PaymentCenterHttpClient client, - string orderNo) - { - var res = await client.CreateHttpClient() - .GetStringAsync($"{client.BaseUrl}api/paymentcenter/v1/Payment/QueryOrder?orderNo={orderNo}"); - - return res.FromJsonTo>(); - } - } +using System.Threading.Tasks; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using Hncore.Payment.Response; +using PaymentCenterClient; + +namespace Hncore.Payment.ClientExtension +{ + public static class QueryOrderExtension + { + /// + /// 订单查询 + /// + /// + /// + /// + public static async Task> QueryOrder(this PaymentCenterHttpClient client, + string orderNo) + { + var res = await client.CreateHttpClient() + .GetStringAsync($"{client.BaseUrl}api/paymentcenter/v1/Payment/QueryOrder?orderNo={orderNo}"); + + return res.FromJsonTo>(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/SwipeCard.cs b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/SwipeCard.cs index 721d51b..d586a62 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/SwipeCard.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/SwipeCard.cs @@ -1,26 +1,26 @@ -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.WebApi; -using Hncore.Payment.Request; -using PaymentCenterClient; - -namespace Hncore.Payment.ClientExtension -{ - public static class SwipeCard - { - /// - /// 刷卡支付下单 - /// - /// - /// - /// - public static async Task SwipeCardCreateOrder(this PaymentCenterHttpClient client, SwipeCardCreateOrderRequest request) - { - var res = await client.CreateHttpClient() - .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/SwipeCard/CreateOrder", request); - - return res.FromJsonTo(); - } - } +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.WebApi; +using Hncore.Payment.Request; +using PaymentCenterClient; + +namespace Hncore.Payment.ClientExtension +{ + public static class SwipeCard + { + /// + /// 刷卡支付下单 + /// + /// + /// + /// + public static async Task SwipeCardCreateOrder(this PaymentCenterHttpClient client, SwipeCardCreateOrderRequest request) + { + var res = await client.CreateHttpClient() + .PostAsJsonGetString($"{client.BaseUrl}api/paymentcenter/v1/SwipeCard/CreateOrder", request); + + return res.FromJsonTo(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/WechatJsPay.cs b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/WechatJsPay.cs index 22cc5d8..b0f674b 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/WechatJsPay.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/ClientExtension/WechatJsPay.cs @@ -1,30 +1,30 @@ -using System.Threading.Tasks; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Infrastructure.Service; -using Hncore.Infrastructure.WebApi; -using Hncore.Payment.Request; -using Hncore.Payment.Response; -using PaymentCenterClient; - -namespace Hncore.Payment.ClientExtension -{ - public static class WechatJsPay - { - /// - /// 微信小程序、公众号支付下单 - /// - /// - /// - /// - public static async Task WechatJsPayCreateOrder(this ServiceHttpClient client, - WechatJsPayCreateOrderRequest request) - { - var res = await client.CreateInternalClient() - .PostAsJsonGetString($"{client.BaseUrl}/api/paymentcenter/v1/WechatJsPay/CreateOrder", - request); - //WechatJsPayCreateOrderResponse - return res.FromJsonTo(); - } - } +using System.Threading.Tasks; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Infrastructure.Service; +using Hncore.Infrastructure.WebApi; +using Hncore.Payment.Request; +using Hncore.Payment.Response; +using PaymentCenterClient; + +namespace Hncore.Payment.ClientExtension +{ + public static class WechatJsPay + { + /// + /// 微信小程序、公众号支付下单 + /// + /// + /// + /// + public static async Task WechatJsPayCreateOrder(this ServiceHttpClient client, + WechatJsPayCreateOrderRequest request) + { + var res = await client.CreateInternalClient() + .PostAsJsonGetString($"{client.BaseUrl}/api/paymentcenter/v1/WechatJsPay/CreateOrder", + request); + //WechatJsPayCreateOrderResponse + return res.FromJsonTo(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/OrderType.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/OrderType.cs index d6a64d0..bb0fce0 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/OrderType.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/OrderType.cs @@ -1,25 +1,25 @@ -namespace Hncore.Payment.Enum -{ - public enum OrderType - { - /// - /// 短信订单 - /// - SmsOrder = 0, - - /// - /// 产品订单 - /// - Product = 1, - - /// - /// 课程订单 - /// - Course = 2, - - /// - /// 课程套餐订单 - /// - CoursePackage =3, - } +namespace Hncore.Payment.Enum +{ + public enum OrderType + { + /// + /// 短信订单 + /// + SmsOrder = 0, + + /// + /// 产品订单 + /// + Product = 1, + + /// + /// 课程订单 + /// + Course = 2, + + /// + /// 课程套餐订单 + /// + CoursePackage =3, + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentChannel.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentChannel.cs index c918419..559fb55 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentChannel.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentChannel.cs @@ -1,15 +1,15 @@ -namespace Hncore.Payment.Enum -{ - public enum PaymentChannel - { - /// - /// 全付通 - /// - QuanFuTong = 0, - - /// - /// 汇旺财 - /// - WeiFuTong = 10 - } +namespace Hncore.Payment.Enum +{ + public enum PaymentChannel + { + /// + /// 全付通 + /// + QuanFuTong = 0, + + /// + /// 汇旺财 + /// + WeiFuTong = 10 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentMethod.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentMethod.cs index 02ee9e6..34f52f4 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentMethod.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentMethod.cs @@ -1,30 +1,30 @@ -namespace Hncore.Payment.Enum -{ - /// - /// 支付方式 - /// - public enum PaymentMethod - { - None = 0, - /// - /// 微信付款码支付 - /// - WechatSwipeCardPay = 1, - /// - /// 微信扫码支付 - /// - WechatQrPay = 2, - /// - /// 支付宝付款码支付 - /// - AliSwipeCardPay = 3, - /// - /// 支付宝扫码支付 - /// - AliQrPay = 4, - /// - /// 微信公众号支付 - /// - WechatJsAppPay = 5 - } +namespace Hncore.Payment.Enum +{ + /// + /// 支付方式 + /// + public enum PaymentMethod + { + None = 0, + /// + /// 微信付款码支付 + /// + WechatSwipeCardPay = 1, + /// + /// 微信扫码支付 + /// + WechatQrPay = 2, + /// + /// 支付宝付款码支付 + /// + AliSwipeCardPay = 3, + /// + /// 支付宝扫码支付 + /// + AliQrPay = 4, + /// + /// 微信公众号支付 + /// + WechatJsAppPay = 5 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentStatus.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentStatus.cs index 4204c71..c12de7a 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentStatus.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentStatus.cs @@ -1,35 +1,35 @@ -namespace Hncore.Payment.Enum -{ - public enum PaymentStatus - { - /// - /// 未支付 - /// - NotPay = 10, - - /// - /// 已支付 - /// - OkPay = 20, - - /// - /// 过期 - /// - Expire = 30, - - /// - /// 支付失败 - /// - Fail = 40, - - /// - /// 支付成功,回调失败 - /// - CallbackFail = 50, - - /// - /// 支付中 - /// - Paying = 60 - } +namespace Hncore.Payment.Enum +{ + public enum PaymentStatus + { + /// + /// 未支付 + /// + NotPay = 10, + + /// + /// 已支付 + /// + OkPay = 20, + + /// + /// 过期 + /// + Expire = 30, + + /// + /// 支付失败 + /// + Fail = 40, + + /// + /// 支付成功,回调失败 + /// + CallbackFail = 50, + + /// + /// 支付中 + /// + Paying = 60 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentType.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentType.cs index 1d42df0..c0fb6a8 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentType.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/PaymentType.cs @@ -1,72 +1,72 @@ -using System.ComponentModel; - -namespace Hncore.Payment.Enum -{ - public enum PaymentType - { - /// - /// 线下支付-现金 - /// - [Description("线下支付-现金")] OfflinePayCash = 10, - - /// - /// 线下支付-支票 - /// - [Description("线下支付-支票")] OfflinePayCheck = 20, - - /// - /// 线下支付-银行转账 - /// - [Description("线下支付-银行转账")] OfflinePayBank = 30, - - /// - /// 线下支付-pos机刷卡 - /// - [Description("线下支付-pos机刷卡")] OfflinePayPOS = 40, - - /// - /// 线下支付-支付宝直接转账 - /// - [Description("线下支付-支付宝直接转账")] OfflinePayAlipay = 50, - - /// - /// 线下支付-微信直接转账 - /// - [Description("线下支付-微信直接转账")] OfflinePayWechat = 60, - - /// - /// 线上支付-微信支付 - /// - [Description("线上支付-微信支付")] OnlinePayWechart = 70, - - /// - /// 线上支付-POS机储蓄卡刷卡 - /// - [Description("线上支付-POS机储蓄卡刷卡")] OnlinePosDeposit = 80, - - /// - /// 线上支付-POS机信用卡刷卡 - /// - [Description("线上支付-POS机信用卡刷卡")] OnlinePosCredit = 90, - - /// - /// 线上支付-支付宝 - /// - [Description("线上支付-支付宝")] OnlineAlipay = 100, - - /// - /// 停车劵免费 - /// - [Description("停车劵免费")] ParkTicketFree = 110, - - /// - /// 余额冲抵 - /// - [Description("余额冲抵")] BalanceOffset =120, - - /// - /// 其他支付方式 - /// - [Description("其他支付方式")] Other = 250 - } +using System.ComponentModel; + +namespace Hncore.Payment.Enum +{ + public enum PaymentType + { + /// + /// 线下支付-现金 + /// + [Description("线下支付-现金")] OfflinePayCash = 10, + + /// + /// 线下支付-支票 + /// + [Description("线下支付-支票")] OfflinePayCheck = 20, + + /// + /// 线下支付-银行转账 + /// + [Description("线下支付-银行转账")] OfflinePayBank = 30, + + /// + /// 线下支付-pos机刷卡 + /// + [Description("线下支付-pos机刷卡")] OfflinePayPOS = 40, + + /// + /// 线下支付-支付宝直接转账 + /// + [Description("线下支付-支付宝直接转账")] OfflinePayAlipay = 50, + + /// + /// 线下支付-微信直接转账 + /// + [Description("线下支付-微信直接转账")] OfflinePayWechat = 60, + + /// + /// 线上支付-微信支付 + /// + [Description("线上支付-微信支付")] OnlinePayWechart = 70, + + /// + /// 线上支付-POS机储蓄卡刷卡 + /// + [Description("线上支付-POS机储蓄卡刷卡")] OnlinePosDeposit = 80, + + /// + /// 线上支付-POS机信用卡刷卡 + /// + [Description("线上支付-POS机信用卡刷卡")] OnlinePosCredit = 90, + + /// + /// 线上支付-支付宝 + /// + [Description("线上支付-支付宝")] OnlineAlipay = 100, + + /// + /// 停车劵免费 + /// + [Description("停车劵免费")] ParkTicketFree = 110, + + /// + /// 余额冲抵 + /// + [Description("余额冲抵")] BalanceOffset =120, + + /// + /// 其他支付方式 + /// + [Description("其他支付方式")] Other = 250 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/ResultCode.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/ResultCode.cs index 08755fb..692bfb3 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Enum/ResultCode.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Enum/ResultCode.cs @@ -1,156 +1,156 @@ -using System.ComponentModel; - -namespace Hncore.Payment.Enum -{ - public enum ResultCode - { - /// - /// 未知错误 - /// - [Description("服务正在更新中,请稍后再试")] C_UNKNOWN_ERROR = 0, - - /// - /// 成功 - /// - [Description("成功")] C_SUCCESS = 10000, - - /// - /// 验证码 - /// - [Description("验证码错误")] C_VERIFY_CODE_ERROR = 10001, - - /// - /// 参数 - /// - [Description("服务正在更新中,请稍后再试")] C_PARAM_ERROR = 10002, - - /// - /// 登录名 - /// - [Description("登录名错误")] C_LONGIN_NAME_ERROR = 10003, - - /// - /// 密码 - /// - [Description("密码错误")] C_PASSWORD_ERROR = 10004, - - /// - /// 无效操作 - /// - [Description("非法操作")] C_INVALID_ERROR = 10005, - - /// - /// 文件 - /// - [Description("文件错误")] C_FILE_ERROR = 10006, - - /// - /// 已存在错误 - /// - [Description("资源已存在错误")] C_ALREADY_EXISTS_ERROR = 10007, - - /// - /// 资源无法访问:不是资源的拥有者 - /// - [Description("不是资源的拥有者,资源无法访问")] C_OWNER_ERROR = 10008, - - /// - /// 资源不存在 - /// - [Description("资源不存在")] C_NOT_EXISTS_ERROR = 10009, - - /// - /// 新建角色出错 - /// - [Description("创建角色出错")] C_ROLE_CREATE_ERROR = 10010, - - /// - /// 新建权限出错 - /// - [Description("新建权限错误")] C_PERMISSION_CREATE_ERROR = 10011, - - /// - /// 绑定角色和权限出错 - /// - [Description("绑定角色和权限出错")] C_ROLE_PERMISSION_CREATE_ERROR = 10012, - - /// - /// 服务器繁忙,请稍后再试! - /// - [Description("服务器繁忙")] C_Server_Is_Busy = 10013, - - /// - /// 访问被禁止 - /// - [Description("禁止访问")] C_Access_Forbidden = 10014, - - /// - /// 非法操作 - /// - [Description("非法操作")] C_Illegal_Operation = 10015, - - /// - /// 无效的openID - /// - [Description("OpenID无效")] C_OPENID_ERROR = 10016, - - /// - /// 返回错误,但无需理会 - /// - [Description("可忽略的错误")] C_IGNORE_ERROR = 10017, - - /// - /// 用户信息错误 - /// - [Description("用户信息错误")] C_USERINFO_ERROR = 10018, - - /// - /// 用户需要认证 - /// - [Description("用户需要认证")] C_USER_SELECT_ERROR = 10019, - - /// - /// 过期 - /// - [Description("超时错误")] C_TIMEOUT_ERROR = 10020, - - /// - /// 手机和验证码不匹配 - /// - [Description("手机和验证码不匹配")] C_PHONE_CODE_ERROR = 10021, - - /// - /// 微信没有选择楼 - /// - [Description("微信没有选择楼")] C_WX_UNIT_UNSELECT_ERROR = 10022, - - /// - /// 黑名单错误 - /// - [Description("黑名单错误")] C_BLACKLIST_ERROR = 10023, - - /// - /// 支付失败 - /// - [Description("支付失败")] C_PAY_FAIL = 10024, - - /// - /// 重定向 - /// - [Description("重定向")] C_REDIRECT_URL = 100302, - - [Description("用户重定向")] C_USER_REDIRECT_URL = 900302, - - - [Description("人脸已经存在")] C_FACEKEY_EXIST_ERROR = 900303, - - [Description("人脸角度不正确")] C_FACE_ANGLE_ERROR = 900304, - - [Description("退款失败")] C_PAY_Refund = 900305, - - /// - /// 用户支付中 - /// - [Description("用户支付中")] C_USERPAYING = 900306 - } +using System.ComponentModel; + +namespace Hncore.Payment.Enum +{ + public enum ResultCode + { + /// + /// 未知错误 + /// + [Description("服务正在更新中,请稍后再试")] C_UNKNOWN_ERROR = 0, + + /// + /// 成功 + /// + [Description("成功")] C_SUCCESS = 10000, + + /// + /// 验证码 + /// + [Description("验证码错误")] C_VERIFY_CODE_ERROR = 10001, + + /// + /// 参数 + /// + [Description("服务正在更新中,请稍后再试")] C_PARAM_ERROR = 10002, + + /// + /// 登录名 + /// + [Description("登录名错误")] C_LONGIN_NAME_ERROR = 10003, + + /// + /// 密码 + /// + [Description("密码错误")] C_PASSWORD_ERROR = 10004, + + /// + /// 无效操作 + /// + [Description("非法操作")] C_INVALID_ERROR = 10005, + + /// + /// 文件 + /// + [Description("文件错误")] C_FILE_ERROR = 10006, + + /// + /// 已存在错误 + /// + [Description("资源已存在错误")] C_ALREADY_EXISTS_ERROR = 10007, + + /// + /// 资源无法访问:不是资源的拥有者 + /// + [Description("不是资源的拥有者,资源无法访问")] C_OWNER_ERROR = 10008, + + /// + /// 资源不存在 + /// + [Description("资源不存在")] C_NOT_EXISTS_ERROR = 10009, + + /// + /// 新建角色出错 + /// + [Description("创建角色出错")] C_ROLE_CREATE_ERROR = 10010, + + /// + /// 新建权限出错 + /// + [Description("新建权限错误")] C_PERMISSION_CREATE_ERROR = 10011, + + /// + /// 绑定角色和权限出错 + /// + [Description("绑定角色和权限出错")] C_ROLE_PERMISSION_CREATE_ERROR = 10012, + + /// + /// 服务器繁忙,请稍后再试! + /// + [Description("服务器繁忙")] C_Server_Is_Busy = 10013, + + /// + /// 访问被禁止 + /// + [Description("禁止访问")] C_Access_Forbidden = 10014, + + /// + /// 非法操作 + /// + [Description("非法操作")] C_Illegal_Operation = 10015, + + /// + /// 无效的openID + /// + [Description("OpenID无效")] C_OPENID_ERROR = 10016, + + /// + /// 返回错误,但无需理会 + /// + [Description("可忽略的错误")] C_IGNORE_ERROR = 10017, + + /// + /// 用户信息错误 + /// + [Description("用户信息错误")] C_USERINFO_ERROR = 10018, + + /// + /// 用户需要认证 + /// + [Description("用户需要认证")] C_USER_SELECT_ERROR = 10019, + + /// + /// 过期 + /// + [Description("超时错误")] C_TIMEOUT_ERROR = 10020, + + /// + /// 手机和验证码不匹配 + /// + [Description("手机和验证码不匹配")] C_PHONE_CODE_ERROR = 10021, + + /// + /// 微信没有选择楼 + /// + [Description("微信没有选择楼")] C_WX_UNIT_UNSELECT_ERROR = 10022, + + /// + /// 黑名单错误 + /// + [Description("黑名单错误")] C_BLACKLIST_ERROR = 10023, + + /// + /// 支付失败 + /// + [Description("支付失败")] C_PAY_FAIL = 10024, + + /// + /// 重定向 + /// + [Description("重定向")] C_REDIRECT_URL = 100302, + + [Description("用户重定向")] C_USER_REDIRECT_URL = 900302, + + + [Description("人脸已经存在")] C_FACEKEY_EXIST_ERROR = 900303, + + [Description("人脸角度不正确")] C_FACE_ANGLE_ERROR = 900304, + + [Description("退款失败")] C_PAY_Refund = 900305, + + /// + /// 用户支付中 + /// + [Description("用户支付中")] C_USERPAYING = 900306 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/IServiceCollectionExtension.cs b/Infrastructure/ServiceClient/PaymentCenterClient/IServiceCollectionExtension.cs index 57eb28b..4cdf7be 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/IServiceCollectionExtension.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/IServiceCollectionExtension.cs @@ -1,21 +1,21 @@ -using Hncore.Infrastructure.Extension; -using Microsoft.Extensions.DependencyInjection; - -namespace PaymentCenterClient -{ - public static class IServiceCollectionExtension - { - public static void AddPaymentCenterClient(this IServiceCollection service, string baseUrl = "") - { - if (!baseUrl.Has()) - { - baseUrl = "http://paymentcenter/"; - } - - PaymentCenterHttpClient._BaseUrl = baseUrl; - - service.AddHttpClient(); - service.AddSingleton(); - } - } +using Hncore.Infrastructure.Extension; +using Microsoft.Extensions.DependencyInjection; + +namespace PaymentCenterClient +{ + public static class IServiceCollectionExtension + { + public static void AddPaymentCenterClient(this IServiceCollection service, string baseUrl = "") + { + if (!baseUrl.Has()) + { + baseUrl = "http://paymentcenter/"; + } + + PaymentCenterHttpClient._BaseUrl = baseUrl; + + service.AddHttpClient(); + service.AddSingleton(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterClient.csproj b/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterClient.csproj index ada64a8..40b424d 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterClient.csproj +++ b/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterClient.csproj @@ -1,13 +1,13 @@ - - - - netcoreapp2.2 - - - - - - - - - + + + + netcoreapp2.2 + + + + + + + + + diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterHttpClient.cs b/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterHttpClient.cs index b96c33e..0e75bad 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterHttpClient.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/PaymentCenterHttpClient.cs @@ -1,25 +1,25 @@ -using System.Net.Http; - -namespace PaymentCenterClient -{ - public class PaymentCenterHttpClient - { - private IHttpClientFactory _httpClientFactory; - - internal static string _BaseUrl = ""; - - public PaymentCenterHttpClient(){} - - public PaymentCenterHttpClient(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - public string BaseUrl => _BaseUrl; - - public HttpClient CreateHttpClient() - { - return _httpClientFactory.CreateClient("PaymentCenterClient"); - } - } +using System.Net.Http; + +namespace PaymentCenterClient +{ + public class PaymentCenterHttpClient + { + private IHttpClientFactory _httpClientFactory; + + internal static string _BaseUrl = ""; + + public PaymentCenterHttpClient(){} + + public PaymentCenterHttpClient(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public string BaseUrl => _BaseUrl; + + public HttpClient CreateHttpClient() + { + return _httpClientFactory.CreateClient("PaymentCenterClient"); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOffLinePaySuccessedRecordRequest.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOffLinePaySuccessedRecordRequest.cs index 78e3a8b..e7be8f8 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOffLinePaySuccessedRecordRequest.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOffLinePaySuccessedRecordRequest.cs @@ -1,58 +1,58 @@ -using System.Collections.Generic; -using Hncore.Payment.Enum; - -namespace Hncore.Payment.Request -{ - public class CreateOffLinePaySuccessedRecordRequest: PaymentRequestBase - { - /// - /// 总金额,单位:分 - /// - public int TotalFee { get; set; } - - /// - /// 商品描述 - /// - public string Body { get; set; } - - /// - /// 支付类型 - /// - public PaymentType PaymentType { get; set; } - - /// - /// 支付方式 - /// - public PaymentMethod PaymentMethod { get; set; } - - /// - /// 业务订单号 - /// - public string OrderId { get; set; } - - /// - /// 订单类型 - /// - public OrderType OrderType { get; set; } - - /// - /// 是否来自POS机 - /// - public bool FromPos { get; set; } - - /// - /// 回调地址 - /// - public string CallbackUrl { get; set; } - - /// - /// 业务方附加信息,回调时原样返回 - /// - public string Attach { get; set; } - - /// - /// 支付成功后要发送的mqtt消息 - /// - public List MqttMessages { get; set; } = new List(); - } +using System.Collections.Generic; +using Hncore.Payment.Enum; + +namespace Hncore.Payment.Request +{ + public class CreateOffLinePaySuccessedRecordRequest: PaymentRequestBase + { + /// + /// 总金额,单位:分 + /// + public int TotalFee { get; set; } + + /// + /// 商品描述 + /// + public string Body { get; set; } + + /// + /// 支付类型 + /// + public PaymentType PaymentType { get; set; } + + /// + /// 支付方式 + /// + public PaymentMethod PaymentMethod { get; set; } + + /// + /// 业务订单号 + /// + public string OrderId { get; set; } + + /// + /// 订单类型 + /// + public OrderType OrderType { get; set; } + + /// + /// 是否来自POS机 + /// + public bool FromPos { get; set; } + + /// + /// 回调地址 + /// + public string CallbackUrl { get; set; } + + /// + /// 业务方附加信息,回调时原样返回 + /// + public string Attach { get; set; } + + /// + /// 支付成功后要发送的mqtt消息 + /// + public List MqttMessages { get; set; } = new List(); + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOrderRequestBase.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOrderRequestBase.cs index 74295ab..8a2f7e2 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOrderRequestBase.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/CreateOrderRequestBase.cs @@ -1,37 +1,37 @@ -using Hncore.Payment.Enum; -using System.Collections.Generic; -namespace Hncore.Payment.Request -{ - public class CreateOrderRequestBase: PaymentRequestBase - { - /// - /// 业务方附加信息,回调时原样返回 - /// - public string Attach { get; set; } - - /// - /// 总金额,单位:分 - /// - public int TotalFee { get; set; } - - /// - /// 支付类型 - /// - public PaymentType PaymentType { get; set; } = PaymentType.OnlinePayWechart; - - /// - /// 回调地址 - /// - public string CallbackUrl { get; set; } - - /// - /// 商品描述 - /// - public string Body { get; set; } - - /// - /// 业务订单号 - /// - public string OrderId { get; set; } - } +using Hncore.Payment.Enum; +using System.Collections.Generic; +namespace Hncore.Payment.Request +{ + public class CreateOrderRequestBase: PaymentRequestBase + { + /// + /// 业务方附加信息,回调时原样返回 + /// + public string Attach { get; set; } + + /// + /// 总金额,单位:分 + /// + public int TotalFee { get; set; } + + /// + /// 支付类型 + /// + public PaymentType PaymentType { get; set; } = PaymentType.OnlinePayWechart; + + /// + /// 回调地址 + /// + public string CallbackUrl { get; set; } + + /// + /// 商品描述 + /// + public string Body { get; set; } + + /// + /// 业务订单号 + /// + public string OrderId { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/EPayCreateOrderRequest.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/EPayCreateOrderRequest.cs index a070875..4c10d4b 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/EPayCreateOrderRequest.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/EPayCreateOrderRequest.cs @@ -1,10 +1,10 @@ -namespace Hncore.Payment.Request -{ - /// - /// POS机推送订单请求 - /// - public class EPayCreateOrderRequest : CreateOrderRequestBase - { - - } -} +namespace Hncore.Payment.Request +{ + /// + /// POS机推送订单请求 + /// + public class EPayCreateOrderRequest : CreateOrderRequestBase + { + + } +} diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/MqttMessage.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/MqttMessage.cs index 12bc9ec..97bd1fe 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/MqttMessage.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/MqttMessage.cs @@ -1,9 +1,9 @@ -namespace Hncore.Payment.Request -{ - public class MqttMessage - { - public string Topic { get; set; } - - public string Payload { get; set; } - } +namespace Hncore.Payment.Request +{ + public class MqttMessage + { + public string Topic { get; set; } + + public string Payload { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/PaymentRequestBase.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/PaymentRequestBase.cs index fc99825..7ffd52f 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/PaymentRequestBase.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/PaymentRequestBase.cs @@ -1,12 +1,12 @@ -using Hncore.Payment.Enum; - -namespace Hncore.Payment.Request -{ - public class PaymentRequestBase - { - public int TenantId { get; set; } - - public int StoreId { get; set; } = 0; - - } +using Hncore.Payment.Enum; + +namespace Hncore.Payment.Request +{ + public class PaymentRequestBase + { + public int TenantId { get; set; } + + public int StoreId { get; set; } = 0; + + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/QrPayCreateOrderRequest.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/QrPayCreateOrderRequest.cs index b15281c..edaba2a 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/QrPayCreateOrderRequest.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/QrPayCreateOrderRequest.cs @@ -1,28 +1,28 @@ -namespace Hncore.Payment.Request -{ - /// - /// 扫码支付请求 - /// - public class QrPayCreateOrderRequest: CreateOrderRequestBase - { - /// - /// 目标平台,微信、支付宝 - /// - public TargetPlatform TargetPlatform { get; set; } - } - - /// - /// 目标平台,微信、支付宝 - /// - public enum TargetPlatform - { - /// - /// 微信 - /// - Wechat=1, - /// - /// 支付宝 - /// - Alipay=2 - } +namespace Hncore.Payment.Request +{ + /// + /// 扫码支付请求 + /// + public class QrPayCreateOrderRequest: CreateOrderRequestBase + { + /// + /// 目标平台,微信、支付宝 + /// + public TargetPlatform TargetPlatform { get; set; } + } + + /// + /// 目标平台,微信、支付宝 + /// + public enum TargetPlatform + { + /// + /// 微信 + /// + Wechat=1, + /// + /// 支付宝 + /// + Alipay=2 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/SingleDaiFuRequest.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/SingleDaiFuRequest.cs index 3d59345..ea4193d 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/SingleDaiFuRequest.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/SingleDaiFuRequest.cs @@ -1,33 +1,33 @@ -namespace Hncore.Payment.Request -{ - /// - /// 单笔代付请求 - /// - public class SingleDaiFuRequest : PaymentRequestBase - { - /// - /// 订单Id - /// - public string OrderId { get; set; } - - /// - /// 收款人姓名 - /// - public string AccName { get; set; } - - /// - /// 收款人账号 - /// - public string AccNo { get; set; } - - /// - /// 转账金额,单位:分 - /// - public int Amount { get; set; } - - /// - /// 用途 - /// - public string Purpose { get; set; } - } +namespace Hncore.Payment.Request +{ + /// + /// 单笔代付请求 + /// + public class SingleDaiFuRequest : PaymentRequestBase + { + /// + /// 订单Id + /// + public string OrderId { get; set; } + + /// + /// 收款人姓名 + /// + public string AccName { get; set; } + + /// + /// 收款人账号 + /// + public string AccNo { get; set; } + + /// + /// 转账金额,单位:分 + /// + public int Amount { get; set; } + + /// + /// 用途 + /// + public string Purpose { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/SwipeCardCreateOrderRequest.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/SwipeCardCreateOrderRequest.cs index b08e308..0a0e517 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/SwipeCardCreateOrderRequest.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/SwipeCardCreateOrderRequest.cs @@ -1,13 +1,13 @@ -namespace Hncore.Payment.Request -{ - /// - /// 刷卡支付请求 - /// - public class SwipeCardCreateOrderRequest : CreateOrderRequestBase - { - /// - /// 扫码支付授权码, 设备读取用户展示的条码或者二维码信息 - /// - public string AuthCode { get; set; } - } +namespace Hncore.Payment.Request +{ + /// + /// 刷卡支付请求 + /// + public class SwipeCardCreateOrderRequest : CreateOrderRequestBase + { + /// + /// 扫码支付授权码, 设备读取用户展示的条码或者二维码信息 + /// + public string AuthCode { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Request/WechatJsPayCreateOrderRequest.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Request/WechatJsPayCreateOrderRequest.cs index 0e792d7..b19fd5b 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Request/WechatJsPayCreateOrderRequest.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Request/WechatJsPayCreateOrderRequest.cs @@ -1,40 +1,40 @@ -namespace Hncore.Payment.Request -{ - /// - /// 微信小程序、公众号支付请求 - /// - public class WechatJsPayCreateOrderRequest: CreateOrderRequestBase - { - /// - /// 支付环境 - /// - public PayEnvironment PayEnvironment { get; set; } - - /// - /// 微信用户关注商家公众号的openid - /// - public string UserOpenId { get; set; } - - /// - /// 公众账号或小程序ID - /// - public string AppId { get; set; } - - public int UserId { get; set; } - } - - /// - /// 支付环境 - /// - public enum PayEnvironment - { - /// - /// 小程序支付 - /// - WeApp=1, - /// - /// 公众号支付 - /// - H5=2 - } +namespace Hncore.Payment.Request +{ + /// + /// 微信小程序、公众号支付请求 + /// + public class WechatJsPayCreateOrderRequest: CreateOrderRequestBase + { + /// + /// 支付环境 + /// + public PayEnvironment PayEnvironment { get; set; } + + /// + /// 微信用户关注商家公众号的openid + /// + public string UserOpenId { get; set; } + + /// + /// 公众账号或小程序ID + /// + public string AppId { get; set; } + + public int UserId { get; set; } + } + + /// + /// 支付环境 + /// + public enum PayEnvironment + { + /// + /// 小程序支付 + /// + WeApp=1, + /// + /// 公众号支付 + /// + H5=2 + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Response/QrPayCreateOrderResponse.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Response/QrPayCreateOrderResponse.cs index 2a92ad7..d386c8f 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Response/QrPayCreateOrderResponse.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Response/QrPayCreateOrderResponse.cs @@ -1,15 +1,15 @@ -namespace Hncore.Payment.Response -{ - public class QrPayCreateOrderResponse - { - /// - /// 商户可用此参数自定义去生成二维码后展示出来进行扫码支付 - /// - public string CodeUrl { get; set; } - - /// - /// 此参数的值即是根据code_url生成的可以扫码支付的二维码图片地址 - /// - public string CodeImgUrl { get; set; } - } +namespace Hncore.Payment.Response +{ + public class QrPayCreateOrderResponse + { + /// + /// 商户可用此参数自定义去生成二维码后展示出来进行扫码支付 + /// + public string CodeUrl { get; set; } + + /// + /// 此参数的值即是根据code_url生成的可以扫码支付的二维码图片地址 + /// + public string CodeImgUrl { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Response/QueryOrderResponse.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Response/QueryOrderResponse.cs index 50c06a8..80c808c 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Response/QueryOrderResponse.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Response/QueryOrderResponse.cs @@ -1,11 +1,11 @@ -using Hncore.Payment.Enum; - -namespace Hncore.Payment.Response -{ - public class QueryOrderResponse - { - public PaymentStatus PaymentStatus { get; set; } - public PaymentType PayType { get; set; } - public string PaymentMessage { get; set; } - } +using Hncore.Payment.Enum; + +namespace Hncore.Payment.Response +{ + public class QueryOrderResponse + { + public PaymentStatus PaymentStatus { get; set; } + public PaymentType PayType { get; set; } + public string PaymentMessage { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/Response/WechatJsPayCreateOrderResponse.cs b/Infrastructure/ServiceClient/PaymentCenterClient/Response/WechatJsPayCreateOrderResponse.cs index 683db92..77b1fa4 100644 --- a/Infrastructure/ServiceClient/PaymentCenterClient/Response/WechatJsPayCreateOrderResponse.cs +++ b/Infrastructure/ServiceClient/PaymentCenterClient/Response/WechatJsPayCreateOrderResponse.cs @@ -1,10 +1,10 @@ -namespace Hncore.Payment.Response -{ - public class WechatJsPayCreateOrderResponse - { - /// - /// 原生态js支付信息或小程序支付信息 - /// - public string PayInfo { get; set; } - } +namespace Hncore.Payment.Response +{ + public class WechatJsPayCreateOrderResponse + { + /// + /// 原生态js支付信息或小程序支付信息 + /// + public string PayInfo { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/PaymentCenterClient/obj/Debug/netcoreapp2.2/PaymentCenterClient.csprojAssemblyReference.cache b/Infrastructure/ServiceClient/PaymentCenterClient/obj/Debug/netcoreapp2.2/PaymentCenterClient.csprojAssemblyReference.cache index 0823c36da85a557e00000cc5315456930000c7c5..00f2ceab6e1e46ac9dab683e3c32dc1a022c5154 100644 GIT binary patch literal 360590 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 - } - } +using Microsoft.Extensions.DependencyInjection; + +namespace ScheduledTaskClient +{ + public static class IServiceCollectionExtension + { + public static void AddMsgCenterClient(this IServiceCollection service, string baseUrl="http://scheduledtask") + { + ScheduledTaskHttpClient._BaseUrl = baseUrl; + + service.AddSingleton(); + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledMessage.cs b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledMessage.cs index 0981fd3..a0f3afe 100644 --- a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledMessage.cs +++ b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledMessage.cs @@ -1,35 +1,35 @@ -using System; - -namespace ScheduledTaskClient -{ - /// - /// 计划消息 - /// - public class ScheduledMessage - { - /// - /// 消息名称 - /// - public string MessageName { get; set; } - - /// - /// 计划类型 - /// - public ScheduledType ScheduledType { get; set; } - - /// - /// 延迟计划延迟时间 - /// - public TimeSpan Delaye { get; set; } - - /// - /// 定期计划cron表达式 - /// - public string Cron { get; set; } - - /// - /// 消息内容 - /// - public string Content { get; set; } - } +using System; + +namespace ScheduledTaskClient +{ + /// + /// 计划消息 + /// + public class ScheduledMessage + { + /// + /// 消息名称 + /// + public string MessageName { get; set; } + + /// + /// 计划类型 + /// + public ScheduledType ScheduledType { get; set; } + + /// + /// 延迟计划延迟时间 + /// + public TimeSpan Delaye { get; set; } + + /// + /// 定期计划cron表达式 + /// + public string Cron { get; set; } + + /// + /// 消息内容 + /// + public string Content { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskClient.csproj b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskClient.csproj index 86706b0..76c0a6e 100644 --- a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskClient.csproj +++ b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskClient.csproj @@ -1,11 +1,11 @@ - - - - netcoreapp2.2 - - - - - - - + + + + netcoreapp2.2 + + + + + + + diff --git a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskHttpClient.cs b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskHttpClient.cs index 50a6a91..96c0d68 100644 --- a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskHttpClient.cs +++ b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledTaskHttpClient.cs @@ -1,50 +1,50 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Etor.Infrastructure.Common; -using Etor.Infrastructure.Extension; -using Etor.Infrastructure.Serializer; -using Etor.Infrastructure.WebApi; - -namespace ScheduledTaskClient -{ - public class ScheduledTaskHttpClient - { - private IHttpClientFactory _httpClientFactory; - - internal static string _BaseUrl = ""; - - public ScheduledTaskHttpClient(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - public string BaseUrl => _BaseUrl; - - private HttpClient CreateHttpClient() - { - return _httpClientFactory.CreateClient(); - } - - /// - /// 设置计划消息 - /// - /// - /// - public async Task SetScheduledMmessage(ScheduledMessage message) - { - try - { - var res = await CreateHttpClient() - .PostAsJsonGetString("/api/scheduledtask/v1/message/Set", message); - - return res.FromJsonTo(); - } - catch (Exception e) - { - LogHelper.Error("设置计划消息失败", $"{e}\n消息内容:\n{message.ToJson(true)}"); - return new ApiResult(ResultCode.C_UNKNOWN_ERROR); - } - } - } +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Etor.Infrastructure.Common; +using Etor.Infrastructure.Extension; +using Etor.Infrastructure.Serializer; +using Etor.Infrastructure.WebApi; + +namespace ScheduledTaskClient +{ + public class ScheduledTaskHttpClient + { + private IHttpClientFactory _httpClientFactory; + + internal static string _BaseUrl = ""; + + public ScheduledTaskHttpClient(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public string BaseUrl => _BaseUrl; + + private HttpClient CreateHttpClient() + { + return _httpClientFactory.CreateClient(); + } + + /// + /// 设置计划消息 + /// + /// + /// + public async Task SetScheduledMmessage(ScheduledMessage message) + { + try + { + var res = await CreateHttpClient() + .PostAsJsonGetString("/api/scheduledtask/v1/message/Set", message); + + return res.FromJsonTo(); + } + catch (Exception e) + { + LogHelper.Error("设置计划消息失败", $"{e}\n消息内容:\n{message.ToJson(true)}"); + return new ApiResult(ResultCode.C_UNKNOWN_ERROR); + } + } + } } \ No newline at end of file diff --git a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledType.cs b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledType.cs index 802bc5d..d105676 100644 --- a/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledType.cs +++ b/Infrastructure/ServiceClient/ScheduledTaskClient/ScheduledType.cs @@ -1,18 +1,18 @@ -namespace ScheduledTaskClient -{ - /// - /// 计划类型 - /// - public enum ScheduledType - { - /// - /// 延迟 - /// - Delayed = 1, - - /// - /// 定期 - /// - Recurring = 2 - } +namespace ScheduledTaskClient +{ + /// + /// 计划类型 + /// + public enum ScheduledType + { + /// + /// 延迟 + /// + Delayed = 1, + + /// + /// 定期 + /// + Recurring = 2 + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/ChannelType.cs b/Infrastructure/WxApi/ChannelType.cs index 40e7685..d2a93ef 100644 --- a/Infrastructure/WxApi/ChannelType.cs +++ b/Infrastructure/WxApi/ChannelType.cs @@ -1,20 +1,20 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Wx.Open.Enums -{ - public enum ChannelType - { - [Description("公众号")] - MP=1, - [Description("小程序")] - MiniApp=2, - [Description("短信")] - Sms=3, - [Description("小程序订阅消息")] - MiniAppSubscribe = 4, - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Wx.Open.Enums +{ + public enum ChannelType + { + [Description("公众号")] + MP=1, + [Description("小程序")] + MiniApp=2, + [Description("短信")] + Sms=3, + [Description("小程序订阅消息")] + MiniAppSubscribe = 4, + } +} diff --git a/Infrastructure/WxApi/Cryptography.cs b/Infrastructure/WxApi/Cryptography.cs index 43b9133..05b5f77 100644 --- a/Infrastructure/WxApi/Cryptography.cs +++ b/Infrastructure/WxApi/Cryptography.cs @@ -1,232 +1,232 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using System.IO; -using System.Net; -namespace Tencent -{ - class Cryptography - { - public static UInt32 HostToNetworkOrder(UInt32 inval) - { - UInt32 outval = 0; - for (int i = 0; i < 4; i++) - outval = (outval << 8) + ((inval >> (i * 8)) & 255); - return outval; - } - - public static Int32 HostToNetworkOrder(Int32 inval) - { - Int32 outval = 0; - for (int i = 0; i < 4; i++) - outval = (outval << 8) + ((inval >> (i * 8)) & 255); - return outval; - } - /// - /// 解密方法 - /// - /// 密文 - /// - /// - /// - public static string AES_decrypt(String Input, string EncodingAESKey, ref string appid) - { - byte[] Key; - Key = Convert.FromBase64String(EncodingAESKey + "="); - byte[] Iv = new byte[16]; - Array.Copy(Key, Iv, 16); - byte[] btmpMsg = AES_decrypt(Input, Iv, Key); - - int len = BitConverter.ToInt32(btmpMsg, 16); - len = IPAddress.NetworkToHostOrder(len); - - - byte[] bMsg = new byte[len]; - byte[] bAppid = new byte[btmpMsg.Length - 20 - len]; - Array.Copy(btmpMsg, 20, bMsg, 0, len); - Array.Copy(btmpMsg, 20+len , bAppid, 0, btmpMsg.Length - 20 - len); - string oriMsg = Encoding.UTF8.GetString(bMsg); - appid = Encoding.UTF8.GetString(bAppid); - - - return oriMsg; - } - - public static String AES_encrypt(String Input, string EncodingAESKey, string appid) - { - byte[] Key; - Key = Convert.FromBase64String(EncodingAESKey + "="); - byte[] Iv = new byte[16]; - Array.Copy(Key, Iv, 16); - string Randcode = CreateRandCode(16); - byte[] bRand = Encoding.UTF8.GetBytes(Randcode); - byte[] bAppid = Encoding.UTF8.GetBytes(appid); - byte[] btmpMsg = Encoding.UTF8.GetBytes(Input); - byte[] bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length)); - byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bAppid.Length + btmpMsg.Length]; - - Array.Copy(bRand, bMsg, bRand.Length); - Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length); - Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length); - Array.Copy(bAppid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bAppid.Length); - - return AES_encrypt(bMsg, Iv, Key); - - } - private static string CreateRandCode(int codeLen) - { - string codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z"; - if (codeLen == 0) - { - codeLen = 16; - } - string[] arr = codeSerial.Split(','); - string code = ""; - int randValue = -1; - Random rand = new Random(unchecked((int)DateTime.Now.Ticks)); - for (int i = 0; i < codeLen; i++) - { - randValue = rand.Next(0, arr.Length - 1); - code += arr[randValue]; - } - return code; - } - - private static String AES_encrypt(String Input, byte[] Iv, byte[] Key) - { - var aes = new RijndaelManaged(); - //秘钥的大小,以位为单位 - aes.KeySize = 256; - //支持的块大小 - aes.BlockSize = 128; - //填充模式 - aes.Padding = PaddingMode.PKCS7; - aes.Mode = CipherMode.CBC; - aes.Key = Key; - aes.IV = Iv; - var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); - byte[] xBuff = null; - - using (var ms = new MemoryStream()) - { - using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) - { - byte[] xXml = Encoding.UTF8.GetBytes(Input); - cs.Write(xXml, 0, xXml.Length); - } - xBuff = ms.ToArray(); - } - String Output = Convert.ToBase64String(xBuff); - return Output; - } - - private static String AES_encrypt(byte[] Input, byte[] Iv, byte[] Key) - { - var aes = new RijndaelManaged(); - //秘钥的大小,以位为单位 - aes.KeySize = 256; - //支持的块大小 - aes.BlockSize = 128; - //填充模式 - //aes.Padding = PaddingMode.PKCS7; - aes.Padding = PaddingMode.None; - aes.Mode = CipherMode.CBC; - aes.Key = Key; - aes.IV = Iv; - var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); - byte[] xBuff = null; - - #region 自己进行PKCS7补位,用系统自己带的不行 - byte[] msg = new byte[Input.Length + 32 - Input.Length % 32]; - Array.Copy(Input, msg, Input.Length); - byte[] pad = KCS7Encoder(Input.Length); - Array.Copy(pad, 0, msg, Input.Length, pad.Length); - #endregion - - #region 注释的也是一种方法,效果一样 - //ICryptoTransform transform = aes.CreateEncryptor(); - //byte[] xBuff = transform.TransformFinalBlock(msg, 0, msg.Length); - #endregion - - using (var ms = new MemoryStream()) - { - using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) - { - cs.Write(msg, 0, msg.Length); - } - xBuff = ms.ToArray(); - } - - String Output = Convert.ToBase64String(xBuff); - return Output; - } - - private static byte[] KCS7Encoder(int text_length) - { - int block_size = 32; - // 计算需要填充的位数 - int amount_to_pad = block_size - (text_length % block_size); - if (amount_to_pad == 0) - { - amount_to_pad = block_size; - } - // 获得补位所用的字符 - char pad_chr = chr(amount_to_pad); - string tmp = ""; - for (int index = 0; index < amount_to_pad; index++) - { - tmp += pad_chr; - } - return Encoding.UTF8.GetBytes(tmp); - } - /** - * 将数字转化成ASCII码对应的字符,用于对明文进行补码 - * - * @param a 需要转化的数字 - * @return 转化得到的字符 - */ - static char chr(int a) - { - - byte target = (byte)(a & 0xFF); - return (char)target; - } - private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key) - { - RijndaelManaged aes = new RijndaelManaged(); - aes.KeySize = 256; - aes.BlockSize = 128; - aes.Mode = CipherMode.CBC; - aes.Padding = PaddingMode.None; - aes.Key = Key; - aes.IV = Iv; - var decrypt = aes.CreateDecryptor(aes.Key, aes.IV); - byte[] xBuff = null; - using (var ms = new MemoryStream()) - { - using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write)) - { - byte[] xXml = Convert.FromBase64String(Input); - byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32]; - Array.Copy(xXml, msg, xXml.Length); - cs.Write(xXml, 0, xXml.Length); - } - xBuff = decode2(ms.ToArray()); - } - return xBuff; - } - private static byte[] decode2(byte[] decrypted) - { - int pad = (int)decrypted[decrypted.Length - 1]; - if (pad < 1 || pad > 32) - { - pad = 0; - } - byte[] res = new byte[decrypted.Length - pad]; - Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad); - return res; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Security.Cryptography; +using System.IO; +using System.Net; +namespace Tencent +{ + class Cryptography + { + public static UInt32 HostToNetworkOrder(UInt32 inval) + { + UInt32 outval = 0; + for (int i = 0; i < 4; i++) + outval = (outval << 8) + ((inval >> (i * 8)) & 255); + return outval; + } + + public static Int32 HostToNetworkOrder(Int32 inval) + { + Int32 outval = 0; + for (int i = 0; i < 4; i++) + outval = (outval << 8) + ((inval >> (i * 8)) & 255); + return outval; + } + /// + /// 解密方法 + /// + /// 密文 + /// + /// + /// + public static string AES_decrypt(String Input, string EncodingAESKey, ref string appid) + { + byte[] Key; + Key = Convert.FromBase64String(EncodingAESKey + "="); + byte[] Iv = new byte[16]; + Array.Copy(Key, Iv, 16); + byte[] btmpMsg = AES_decrypt(Input, Iv, Key); + + int len = BitConverter.ToInt32(btmpMsg, 16); + len = IPAddress.NetworkToHostOrder(len); + + + byte[] bMsg = new byte[len]; + byte[] bAppid = new byte[btmpMsg.Length - 20 - len]; + Array.Copy(btmpMsg, 20, bMsg, 0, len); + Array.Copy(btmpMsg, 20+len , bAppid, 0, btmpMsg.Length - 20 - len); + string oriMsg = Encoding.UTF8.GetString(bMsg); + appid = Encoding.UTF8.GetString(bAppid); + + + return oriMsg; + } + + public static String AES_encrypt(String Input, string EncodingAESKey, string appid) + { + byte[] Key; + Key = Convert.FromBase64String(EncodingAESKey + "="); + byte[] Iv = new byte[16]; + Array.Copy(Key, Iv, 16); + string Randcode = CreateRandCode(16); + byte[] bRand = Encoding.UTF8.GetBytes(Randcode); + byte[] bAppid = Encoding.UTF8.GetBytes(appid); + byte[] btmpMsg = Encoding.UTF8.GetBytes(Input); + byte[] bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length)); + byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bAppid.Length + btmpMsg.Length]; + + Array.Copy(bRand, bMsg, bRand.Length); + Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length); + Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length); + Array.Copy(bAppid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bAppid.Length); + + return AES_encrypt(bMsg, Iv, Key); + + } + private static string CreateRandCode(int codeLen) + { + string codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z"; + if (codeLen == 0) + { + codeLen = 16; + } + string[] arr = codeSerial.Split(','); + string code = ""; + int randValue = -1; + Random rand = new Random(unchecked((int)DateTime.Now.Ticks)); + for (int i = 0; i < codeLen; i++) + { + randValue = rand.Next(0, arr.Length - 1); + code += arr[randValue]; + } + return code; + } + + private static String AES_encrypt(String Input, byte[] Iv, byte[] Key) + { + var aes = new RijndaelManaged(); + //秘钥的大小,以位为单位 + aes.KeySize = 256; + //支持的块大小 + aes.BlockSize = 128; + //填充模式 + aes.Padding = PaddingMode.PKCS7; + aes.Mode = CipherMode.CBC; + aes.Key = Key; + aes.IV = Iv; + var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); + byte[] xBuff = null; + + using (var ms = new MemoryStream()) + { + using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) + { + byte[] xXml = Encoding.UTF8.GetBytes(Input); + cs.Write(xXml, 0, xXml.Length); + } + xBuff = ms.ToArray(); + } + String Output = Convert.ToBase64String(xBuff); + return Output; + } + + private static String AES_encrypt(byte[] Input, byte[] Iv, byte[] Key) + { + var aes = new RijndaelManaged(); + //秘钥的大小,以位为单位 + aes.KeySize = 256; + //支持的块大小 + aes.BlockSize = 128; + //填充模式 + //aes.Padding = PaddingMode.PKCS7; + aes.Padding = PaddingMode.None; + aes.Mode = CipherMode.CBC; + aes.Key = Key; + aes.IV = Iv; + var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); + byte[] xBuff = null; + + #region 自己进行PKCS7补位,用系统自己带的不行 + byte[] msg = new byte[Input.Length + 32 - Input.Length % 32]; + Array.Copy(Input, msg, Input.Length); + byte[] pad = KCS7Encoder(Input.Length); + Array.Copy(pad, 0, msg, Input.Length, pad.Length); + #endregion + + #region 注释的也是一种方法,效果一样 + //ICryptoTransform transform = aes.CreateEncryptor(); + //byte[] xBuff = transform.TransformFinalBlock(msg, 0, msg.Length); + #endregion + + using (var ms = new MemoryStream()) + { + using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) + { + cs.Write(msg, 0, msg.Length); + } + xBuff = ms.ToArray(); + } + + String Output = Convert.ToBase64String(xBuff); + return Output; + } + + private static byte[] KCS7Encoder(int text_length) + { + int block_size = 32; + // 计算需要填充的位数 + int amount_to_pad = block_size - (text_length % block_size); + if (amount_to_pad == 0) + { + amount_to_pad = block_size; + } + // 获得补位所用的字符 + char pad_chr = chr(amount_to_pad); + string tmp = ""; + for (int index = 0; index < amount_to_pad; index++) + { + tmp += pad_chr; + } + return Encoding.UTF8.GetBytes(tmp); + } + /** + * 将数字转化成ASCII码对应的字符,用于对明文进行补码 + * + * @param a 需要转化的数字 + * @return 转化得到的字符 + */ + static char chr(int a) + { + + byte target = (byte)(a & 0xFF); + return (char)target; + } + private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key) + { + RijndaelManaged aes = new RijndaelManaged(); + aes.KeySize = 256; + aes.BlockSize = 128; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.None; + aes.Key = Key; + aes.IV = Iv; + var decrypt = aes.CreateDecryptor(aes.Key, aes.IV); + byte[] xBuff = null; + using (var ms = new MemoryStream()) + { + using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write)) + { + byte[] xXml = Convert.FromBase64String(Input); + byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32]; + Array.Copy(xXml, msg, xXml.Length); + cs.Write(xXml, 0, xXml.Length); + } + xBuff = decode2(ms.ToArray()); + } + return xBuff; + } + private static byte[] decode2(byte[] decrypted) + { + int pad = (int)decrypted[decrypted.Length - 1]; + if (pad < 1 || pad > 32) + { + pad = 0; + } + byte[] res = new byte[decrypted.Length - pad]; + Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad); + return res; + } + } +} diff --git a/Infrastructure/WxApi/Enums.cs b/Infrastructure/WxApi/Enums.cs index ef5a859..3cd72be 100644 --- a/Infrastructure/WxApi/Enums.cs +++ b/Infrastructure/WxApi/Enums.cs @@ -1,309 +1,309 @@ -namespace Hncore.Wx.Open -{ - /// - /// 选项设置信息选项名称 - /// - public enum OptionName - { - /// - /// 地理位置上报选项 - /// 0 无上报 - /// 1 进入会话时上报 - /// 2 每5s上报 - /// - location_report, - /// - /// 语音识别开关选项 - /// 0 关闭语音识别 - /// 1 开启语音识别 - /// - voice_recognize, - /// - /// 客服开关选项 - /// 0 关闭多客服 - /// 1 开启多客服 - /// - customer_service - } - - /// - /// 公众号第三方平台推送消息类型 - /// - public enum RequestInfoType - { - - #region 授权消息和事件 - /// - /// 推送component_verify_ticket协议 - /// - component_verify_ticket, - /// - /// 推送取消授权通知 - /// - unauthorized, - /// - /// 更新授权 - /// - updateauthorized, - /// - /// 授权成功通知 - /// - authorized, - /// - /// 小程序注册审核事件推送 - /// - notify_third_fasteregister, - - #endregion - - #region 公众号交互的事件 - /// - /// 关注事件 - /// - event_subscribe, - /// - /// 取消关注事件 - /// - event_unsubscribe, - /// - /// 扫描关注 - /// - event_subscribe_qrscene, - /// - /// 扫描已关注的事件 - /// - event_SCAN, - /// - /// 上报地理位置 - /// - event_LOCATION, - /// - /// 自定义菜单事件 - /// - event_CLICK, - /// - /// 点击菜单跳转链接 - /// - event_VIEW, - #endregion - - #region 公众号交互消息 - /// - /// 文本消息 - /// - text, - /// - /// 图片消息 - /// - image, - /// - /// 语音消息 - /// - voice, - /// - /// 视频消息 - /// - video, - /// - /// 音乐消息 - /// - music, - /// - /// 图文消息 - /// - news, - - /// - /// 未知消息 - /// - none - #endregion - } - - /// - /// 应用授权作用域 - /// - public enum OAuthScope - { - /// - /// 不弹出授权页面,直接跳转,只能获取用户openid - /// - snsapi_base, - /// - /// 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 - /// - snsapi_userinfo, - /// - /// 网站应用授权登录 - /// - snsapi_login, - } - - /// - /// 授权方公众号类型 - /// - public enum ServiceType - { -#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 - 订阅号 = 0, - 由历史老帐号升级后的订阅号 = 1, - 服务号 = 2 -#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 - } - - /// - /// 授权方认证类型 - /// - public enum VerifyType - { -#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 - 未认证 = -1, - 微信认证 = 0, - 新浪微博认证 = 1, - 腾讯微博认证 = 2, - 已资质认证通过但还未通过名称认证 = 3, - 已资质认证通过还未通过名称认证但通过了新浪微博认证 = 4, - 已资质认证通过还未通过名称认证但通过了腾讯微博认证 = 5 -#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 - } - - /// - /// 公众号/小程序授权给开发者的权限集列表(1-15为公众号权限,17-19为小程序权限)。 - /// 请注意:1)该字段的返回不会考虑公众号是否具备该权限集的权限(因为可能部分具备),请根据公众号的帐号类型和认证情况,来判断公众号的接口权限。 - /// - public enum FuncscopeCategory - { -#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 - 消息管理权限 = 1, - 用户管理权限 = 2, - 帐号服务权限 = 3, - 网页服务权限 = 4, - 微信小店权限 = 5, - 微信多客服权限 = 6, - 群发与通知权限 = 7, - 微信卡券权限 = 8, - 微信扫一扫权限 = 9, - 微信连WIFI权限 = 10, - 素材管理权限 = 11, - 微信摇周边权限 = 12, - 微信门店权限 = 13, - 微信支付权限 = 14, - 自定义菜单权限 = 15, - 获取认证状态及信息 = 16, - 帐号管理权限_小程序 = 17, - 开发管理权限_小程序 = 18, - 客服消息管理权限_小程序 = 19, - 微信登录权限_小程序 = 20, - 数据分析权限_小程序 = 21, - 城市服务接口权限 = 22, - 广告管理权限 = 23, - 开放平台帐号管理权限 = 24, - 开放平台帐号管理权限_小程序 = 25, - 微信电子发票权限 = 26, - 快速注册小程序权限 = 27, - 小程序管理权限 = 33, - 微信卡路里权限 = 35 -#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 - } - - /// - /// 小程序“修改服务器地址”接口的action类型 - /// - public enum ModifyDomainAction - { - /// - /// 添加 - /// - add, - /// - /// 删除 - /// - delete, - /// - /// 覆盖 - /// - set, - /// - /// 获取 - /// - get - } - - /// - /// 小程序“线上代码的可见状态”接口的action类型 - /// - public enum ChangVisitStatusAction - { - open, - close - } - - /// - /// 帐号类型(1:订阅号,2:服务号,3:小程序) - /// - public enum AccountType - { - 订阅号 = 1, - 服务号 = 2, - 小程序 = 3 - } - - /// - /// 主体类型(1:企业) - /// - public enum PrincipalType - { - 企业 = 1 - } - - /// - /// 1:实名验证成功,2:实名验证中,3:实名验证失败 - /// - public enum RealNameStatus - { - 实名验证成功 = 1, - 实名验证中 = 2, - 实名验证失败 = 3 - } - - /// - /// 小程序昵称审核状态,1:审核中,2:审核失败,3:审核成功 - /// - public enum AuditStat - { - 审核中 = 1, - 审核失败 = 2, - 审核成功 = 3 - } - - /// - /// 小程序类目审核状态,1:审核中,2:审核失败,3:审核成功 - /// - public enum AuditStatus - { - 审核中 = 1, - 审核不通过 = 2, - 审核通过 = 3 - } - - /// - /// 要授权的帐号类型 - /// - public enum LoginAuthType - { - 默认, - 仅展示公众号 = 1, - 仅展示小程序 = 2, - 表示公众号和小程序都展示 = 3 - } - - /// - /// 企业代码类型 1:统一社会信用代码(18位) 2:组织机构代码(9位xxxxxxxx-x) 3:营业执照注册号(15位) - /// - public enum CodeType - { - 统一社会信用代码 =1, - 组织机构代码=2, - 营业执照注册号=3 - } -} +namespace Hncore.Wx.Open +{ + /// + /// 选项设置信息选项名称 + /// + public enum OptionName + { + /// + /// 地理位置上报选项 + /// 0 无上报 + /// 1 进入会话时上报 + /// 2 每5s上报 + /// + location_report, + /// + /// 语音识别开关选项 + /// 0 关闭语音识别 + /// 1 开启语音识别 + /// + voice_recognize, + /// + /// 客服开关选项 + /// 0 关闭多客服 + /// 1 开启多客服 + /// + customer_service + } + + /// + /// 公众号第三方平台推送消息类型 + /// + public enum RequestInfoType + { + + #region 授权消息和事件 + /// + /// 推送component_verify_ticket协议 + /// + component_verify_ticket, + /// + /// 推送取消授权通知 + /// + unauthorized, + /// + /// 更新授权 + /// + updateauthorized, + /// + /// 授权成功通知 + /// + authorized, + /// + /// 小程序注册审核事件推送 + /// + notify_third_fasteregister, + + #endregion + + #region 公众号交互的事件 + /// + /// 关注事件 + /// + event_subscribe, + /// + /// 取消关注事件 + /// + event_unsubscribe, + /// + /// 扫描关注 + /// + event_subscribe_qrscene, + /// + /// 扫描已关注的事件 + /// + event_SCAN, + /// + /// 上报地理位置 + /// + event_LOCATION, + /// + /// 自定义菜单事件 + /// + event_CLICK, + /// + /// 点击菜单跳转链接 + /// + event_VIEW, + #endregion + + #region 公众号交互消息 + /// + /// 文本消息 + /// + text, + /// + /// 图片消息 + /// + image, + /// + /// 语音消息 + /// + voice, + /// + /// 视频消息 + /// + video, + /// + /// 音乐消息 + /// + music, + /// + /// 图文消息 + /// + news, + + /// + /// 未知消息 + /// + none + #endregion + } + + /// + /// 应用授权作用域 + /// + public enum OAuthScope + { + /// + /// 不弹出授权页面,直接跳转,只能获取用户openid + /// + snsapi_base, + /// + /// 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 + /// + snsapi_userinfo, + /// + /// 网站应用授权登录 + /// + snsapi_login, + } + + /// + /// 授权方公众号类型 + /// + public enum ServiceType + { +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + 订阅号 = 0, + 由历史老帐号升级后的订阅号 = 1, + 服务号 = 2 +#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 + } + + /// + /// 授权方认证类型 + /// + public enum VerifyType + { +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + 未认证 = -1, + 微信认证 = 0, + 新浪微博认证 = 1, + 腾讯微博认证 = 2, + 已资质认证通过但还未通过名称认证 = 3, + 已资质认证通过还未通过名称认证但通过了新浪微博认证 = 4, + 已资质认证通过还未通过名称认证但通过了腾讯微博认证 = 5 +#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 + } + + /// + /// 公众号/小程序授权给开发者的权限集列表(1-15为公众号权限,17-19为小程序权限)。 + /// 请注意:1)该字段的返回不会考虑公众号是否具备该权限集的权限(因为可能部分具备),请根据公众号的帐号类型和认证情况,来判断公众号的接口权限。 + /// + public enum FuncscopeCategory + { +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + 消息管理权限 = 1, + 用户管理权限 = 2, + 帐号服务权限 = 3, + 网页服务权限 = 4, + 微信小店权限 = 5, + 微信多客服权限 = 6, + 群发与通知权限 = 7, + 微信卡券权限 = 8, + 微信扫一扫权限 = 9, + 微信连WIFI权限 = 10, + 素材管理权限 = 11, + 微信摇周边权限 = 12, + 微信门店权限 = 13, + 微信支付权限 = 14, + 自定义菜单权限 = 15, + 获取认证状态及信息 = 16, + 帐号管理权限_小程序 = 17, + 开发管理权限_小程序 = 18, + 客服消息管理权限_小程序 = 19, + 微信登录权限_小程序 = 20, + 数据分析权限_小程序 = 21, + 城市服务接口权限 = 22, + 广告管理权限 = 23, + 开放平台帐号管理权限 = 24, + 开放平台帐号管理权限_小程序 = 25, + 微信电子发票权限 = 26, + 快速注册小程序权限 = 27, + 小程序管理权限 = 33, + 微信卡路里权限 = 35 +#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 + } + + /// + /// 小程序“修改服务器地址”接口的action类型 + /// + public enum ModifyDomainAction + { + /// + /// 添加 + /// + add, + /// + /// 删除 + /// + delete, + /// + /// 覆盖 + /// + set, + /// + /// 获取 + /// + get + } + + /// + /// 小程序“线上代码的可见状态”接口的action类型 + /// + public enum ChangVisitStatusAction + { + open, + close + } + + /// + /// 帐号类型(1:订阅号,2:服务号,3:小程序) + /// + public enum AccountType + { + 订阅号 = 1, + 服务号 = 2, + 小程序 = 3 + } + + /// + /// 主体类型(1:企业) + /// + public enum PrincipalType + { + 企业 = 1 + } + + /// + /// 1:实名验证成功,2:实名验证中,3:实名验证失败 + /// + public enum RealNameStatus + { + 实名验证成功 = 1, + 实名验证中 = 2, + 实名验证失败 = 3 + } + + /// + /// 小程序昵称审核状态,1:审核中,2:审核失败,3:审核成功 + /// + public enum AuditStat + { + 审核中 = 1, + 审核失败 = 2, + 审核成功 = 3 + } + + /// + /// 小程序类目审核状态,1:审核中,2:审核失败,3:审核成功 + /// + public enum AuditStatus + { + 审核中 = 1, + 审核不通过 = 2, + 审核通过 = 3 + } + + /// + /// 要授权的帐号类型 + /// + public enum LoginAuthType + { + 默认, + 仅展示公众号 = 1, + 仅展示小程序 = 2, + 表示公众号和小程序都展示 = 3 + } + + /// + /// 企业代码类型 1:统一社会信用代码(18位) 2:组织机构代码(9位xxxxxxxx-x) 3:营业执照注册号(15位) + /// + public enum CodeType + { + 统一社会信用代码 =1, + 组织机构代码=2, + 营业执照注册号=3 + } +} diff --git a/Infrastructure/WxApi/MessageHandler.cs b/Infrastructure/WxApi/MessageHandler.cs index 79698b6..3c60cdc 100644 --- a/Infrastructure/WxApi/MessageHandler.cs +++ b/Infrastructure/WxApi/MessageHandler.cs @@ -1,12 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Wx.Open -{ - public class MessageHandler - { - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Wx.Open +{ + public class MessageHandler + { + + } +} diff --git a/Infrastructure/WxApi/Model/WxMenuModel.cs b/Infrastructure/WxApi/Model/WxMenuModel.cs index 95cdb5a..cf5b1cb 100644 --- a/Infrastructure/WxApi/Model/WxMenuModel.cs +++ b/Infrastructure/WxApi/Model/WxMenuModel.cs @@ -1,12 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Pass.MsgCenter.WxOpen.Model -{ - public class WxMenuModel - { - } - -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Pass.MsgCenter.WxOpen.Model +{ + public class WxMenuModel + { + } + +} diff --git a/Infrastructure/WxApi/Model/WxOpenModel.cs b/Infrastructure/WxApi/Model/WxOpenModel.cs index 7ce2aed..6297fae 100644 --- a/Infrastructure/WxApi/Model/WxOpenModel.cs +++ b/Infrastructure/WxApi/Model/WxOpenModel.cs @@ -1,98 +1,98 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hncore.Wx.Open -{ - - public class wechat_access_token - { - public string access_token { get; set; } - public int expires_in { get; set; } - public string refresh_token { get; set; } - public string openid { get; set; } - public string scope { get; set; } - public int errcode { get; set; } - public string errmsg { get; set; } - } - - public class authorizer_info - { - public string nick_name { get; set; } - public string head_img { get; set; } - public Service_Type_Info service_type_info { get; set; } - public Verify_Type_Info verify_type_info { get; set; } - public string user_name { get; set; } - public string alias { get; set; } - public string qrcode_url { get; set; } - public bussiness_info business_info { get; set; } - public int idc { get; set; } - public string principal_name { get; set; } - public string signature { get; set; } - } - - public class Service_Type_Info - { - public int id { get; set; } - } - - public class Verify_Type_Info - { - public int id { get; set; } - } - - public class bussiness_info - { - public int open_pay { get; set; } - public int open_shake { get; set; } - public int open_scan { get; set; } - public int open_card { get; set; } - public int open_store { get; set; } - } - /// - /// 授权信息数据模型 - /// - /// - public class authorization_info - { - /// - /// 授权方(物业公众号)的AppId - /// - public string authorizer_appid { get; set; } - /// - /// 授权方(物业公众号)的访问令牌AccessToken - /// - public string authorizer_access_token { get; set; } - /// - /// 授权方访问令牌的过期时间 - /// - public int expires_in { get; set; } - /// - /// 授权方刷新令牌,当访问令牌过期时,使用此刷新令牌重新获取新的访问令牌(此字段要保存) - /// - public string authorizer_refresh_token { get; set; } - /// - /// 授权给开发者的权限集列表,ID为1到26分别代表: 1、消息管理权限 2、用户管理权限 3、帐号服务权限 4、网页服务权限 5、微信小店权限 6、微信多客服权限 7、群发与通知权限 8、微信卡券权限 9、微信扫一扫权限 10、微信连WIFI权限 11、素材管理权限 12、微信摇周边权限 13、微信门店权限 15、自定义菜单权限 16、获取认证状态及信息 17、帐号管理权限(小程序) 18、开发管理与数据分析权限(小程序) 19、客服消息管理权限(小程序) 20、微信登录权限(小程序) 21、数据分析权限(小程序) 22、城市服务接口权限 23、广告管理权限 24、开放平台帐号管理权限 25、 开放平台帐号管理权限(小程序) 26、微信电子发票权限 41、搜索widget的权限 请注意: 1)该字段的返回不会考虑公众号是否具备该权限集的权限(因为可能部分具备),请根据公众号的帐号类型和认证情况,来判断公众号的接口权限。 - /// - public func_info[] func_info { get; set; } - } - - public class func_info - { - public funcscope_category funcscope_category { get; set; } - } - - public class funcscope_category - { - public int id { get; set; } - } - - - public class WechatThirdpartApplicationInfomation - { - public authorizer_info authorizier_info { get; set; } - public authorization_info authorization_info { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hncore.Wx.Open +{ + + public class wechat_access_token + { + public string access_token { get; set; } + public int expires_in { get; set; } + public string refresh_token { get; set; } + public string openid { get; set; } + public string scope { get; set; } + public int errcode { get; set; } + public string errmsg { get; set; } + } + + public class authorizer_info + { + public string nick_name { get; set; } + public string head_img { get; set; } + public Service_Type_Info service_type_info { get; set; } + public Verify_Type_Info verify_type_info { get; set; } + public string user_name { get; set; } + public string alias { get; set; } + public string qrcode_url { get; set; } + public bussiness_info business_info { get; set; } + public int idc { get; set; } + public string principal_name { get; set; } + public string signature { get; set; } + } + + public class Service_Type_Info + { + public int id { get; set; } + } + + public class Verify_Type_Info + { + public int id { get; set; } + } + + public class bussiness_info + { + public int open_pay { get; set; } + public int open_shake { get; set; } + public int open_scan { get; set; } + public int open_card { get; set; } + public int open_store { get; set; } + } + /// + /// 授权信息数据模型 + /// + /// + public class authorization_info + { + /// + /// 授权方(物业公众号)的AppId + /// + public string authorizer_appid { get; set; } + /// + /// 授权方(物业公众号)的访问令牌AccessToken + /// + public string authorizer_access_token { get; set; } + /// + /// 授权方访问令牌的过期时间 + /// + public int expires_in { get; set; } + /// + /// 授权方刷新令牌,当访问令牌过期时,使用此刷新令牌重新获取新的访问令牌(此字段要保存) + /// + public string authorizer_refresh_token { get; set; } + /// + /// 授权给开发者的权限集列表,ID为1到26分别代表: 1、消息管理权限 2、用户管理权限 3、帐号服务权限 4、网页服务权限 5、微信小店权限 6、微信多客服权限 7、群发与通知权限 8、微信卡券权限 9、微信扫一扫权限 10、微信连WIFI权限 11、素材管理权限 12、微信摇周边权限 13、微信门店权限 15、自定义菜单权限 16、获取认证状态及信息 17、帐号管理权限(小程序) 18、开发管理与数据分析权限(小程序) 19、客服消息管理权限(小程序) 20、微信登录权限(小程序) 21、数据分析权限(小程序) 22、城市服务接口权限 23、广告管理权限 24、开放平台帐号管理权限 25、 开放平台帐号管理权限(小程序) 26、微信电子发票权限 41、搜索widget的权限 请注意: 1)该字段的返回不会考虑公众号是否具备该权限集的权限(因为可能部分具备),请根据公众号的帐号类型和认证情况,来判断公众号的接口权限。 + /// + public func_info[] func_info { get; set; } + } + + public class func_info + { + public funcscope_category funcscope_category { get; set; } + } + + public class funcscope_category + { + public int id { get; set; } + } + + + public class WechatThirdpartApplicationInfomation + { + public authorizer_info authorizier_info { get; set; } + public authorization_info authorization_info { get; set; } + } +} diff --git a/Infrastructure/WxApi/Notice/InfoTypeHelper.cs b/Infrastructure/WxApi/Notice/InfoTypeHelper.cs index f7857aa..6c99583 100644 --- a/Infrastructure/WxApi/Notice/InfoTypeHelper.cs +++ b/Infrastructure/WxApi/Notice/InfoTypeHelper.cs @@ -1,56 +1,56 @@ -using System; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - public static class InfoTypeHelper - { - /// - /// 根据xml信息返回类型 - /// 授权信息返回的是 InfoType - /// 微信交互消息和事件返回的是 MsgType - /// - /// - /// - public static RequestInfoType GetRequestInfoType(XDocument doc) - { - var reqType = ""; - var typeNode = doc.Root.Element("InfoType"); - if (typeNode != null) - { - reqType = typeNode.Value; - } - else - { - typeNode = doc.Root.Element("MsgType"); - reqType = typeNode.Value; - if (reqType == "event") - { - reqType = "event_" + doc.Root.Element("Event").Value; - var eventKey = doc.Root.Element("EventKey"); - if (null != (eventKey) && eventKey.Value.StartsWith("qrscene_")) - { - reqType = "event_subscribe_qrscene"; - } - } - } - return GetRequestInfoType(reqType); - } - - /// - /// 根据xml信息,返回InfoType - /// - /// - public static RequestInfoType GetRequestInfoType(string str) - { - try - { - return (RequestInfoType)Enum.Parse(typeof(RequestInfoType), str, true); - } - catch - { - return RequestInfoType.none; - } - } - } -} +using System; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + public static class InfoTypeHelper + { + /// + /// 根据xml信息返回类型 + /// 授权信息返回的是 InfoType + /// 微信交互消息和事件返回的是 MsgType + /// + /// + /// + public static RequestInfoType GetRequestInfoType(XDocument doc) + { + var reqType = ""; + var typeNode = doc.Root.Element("InfoType"); + if (typeNode != null) + { + reqType = typeNode.Value; + } + else + { + typeNode = doc.Root.Element("MsgType"); + reqType = typeNode.Value; + if (reqType == "event") + { + reqType = "event_" + doc.Root.Element("Event").Value; + var eventKey = doc.Root.Element("EventKey"); + if (null != (eventKey) && eventKey.Value.StartsWith("qrscene_")) + { + reqType = "event_subscribe_qrscene"; + } + } + } + return GetRequestInfoType(reqType); + } + + /// + /// 根据xml信息,返回InfoType + /// + /// + public static RequestInfoType GetRequestInfoType(string str) + { + try + { + return (RequestInfoType)Enum.Parse(typeof(RequestInfoType), str, true); + } + catch + { + return RequestInfoType.none; + } + } + } +} diff --git a/Infrastructure/WxApi/Notice/MessageBase.cs b/Infrastructure/WxApi/Notice/MessageBase.cs index a540f0d..3af088f 100644 --- a/Infrastructure/WxApi/Notice/MessageBase.cs +++ b/Infrastructure/WxApi/Notice/MessageBase.cs @@ -1,16 +1,16 @@ -using System.Threading.Tasks; - -namespace Hncore.Wx.Open -{ - /// - /// 请求消息接口 - /// - public interface IMessageBase - { - string AppId { get; set; } - long CreateTime { get; set; } - RequestInfoType InfoType { get; } - Task Handler(); - - } -} +using System.Threading.Tasks; + +namespace Hncore.Wx.Open +{ + /// + /// 请求消息接口 + /// + public interface IMessageBase + { + string AppId { get; set; } + long CreateTime { get; set; } + RequestInfoType InfoType { get; } + Task Handler(); + + } +} diff --git a/Infrastructure/WxApi/Notice/MessageDefault.cs b/Infrastructure/WxApi/Notice/MessageDefault.cs index 34d76c8..437052e 100644 --- a/Infrastructure/WxApi/Notice/MessageDefault.cs +++ b/Infrastructure/WxApi/Notice/MessageDefault.cs @@ -1,38 +1,38 @@ -/* - - - - 123456789 - - - - - - */ -using System.Threading.Tasks; - -namespace Hncore.Wx.Open -{ - /// - /// 扫描二维码关注 - /// - public class MessageDefault : IMessageBase - { - public MessageDefault() - { - - } - public RequestInfoType InfoType - { - get { return RequestInfoType.none; } - } - - public string AppId { get; set; } - public long CreateTime { get; set; } - - public async Task Handler() - { - return true; - } - } -} +/* + + + + 123456789 + + + + + + */ +using System.Threading.Tasks; + +namespace Hncore.Wx.Open +{ + /// + /// 扫描二维码关注 + /// + public class MessageDefault : IMessageBase + { + public MessageDefault() + { + + } + public RequestInfoType InfoType + { + get { return RequestInfoType.none; } + } + + public string AppId { get; set; } + public long CreateTime { get; set; } + + public async Task Handler() + { + return true; + } + } +} diff --git a/Infrastructure/WxApi/Notice/MessageFactory.cs b/Infrastructure/WxApi/Notice/MessageFactory.cs index e592ffc..36b5ac1 100644 --- a/Infrastructure/WxApi/Notice/MessageFactory.cs +++ b/Infrastructure/WxApi/Notice/MessageFactory.cs @@ -1,95 +1,95 @@ -using Hncore.Infrastructure.Common; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.IO; -using System.Xml; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - /// - /// RequestMessage工厂 - /// - public static class MessageFactory - { - - public static IServiceCollection Service { get; set; } - /// - /// 获取XDocument转换后的IRequestMessageBase实例。 - /// 如果MsgType不存在,抛出UnknownRequestMsgTypeException异常 - /// - /// - public static IMessageBase GetRequestEntity(XDocument doc) - { - IMessageBase requestMessage = null; - RequestInfoType infoType; - - try - { - infoType = InfoTypeHelper.GetRequestInfoType(doc); - switch (infoType) - { - case RequestInfoType.component_verify_ticket: - requestMessage = new MessageComponentVerifyTicket(doc); - break; - case RequestInfoType.unauthorized: - requestMessage = new MessageUnauthorized(doc); - break; - case RequestInfoType.authorized: - requestMessage = new MessageAuthorized(doc); - break; - case RequestInfoType.updateauthorized: - requestMessage = new MessageUpdateAuthorized(doc); - break; - case RequestInfoType.notify_third_fasteregister: - requestMessage = new MessageThirdFasteRegister(doc); - break; - case RequestInfoType.event_subscribe_qrscene: - requestMessage = new MessageEventSubscribeQrscene(doc); - break; - case RequestInfoType.event_SCAN: - requestMessage = new MessageEventScan(doc); - break; - case RequestInfoType.event_subscribe: - requestMessage = new MessageEventSubscribe(doc); - break; - default: - return new MessageDefault(); - } - } - catch (Exception ex) - { - LogHelper.Error("MessageFactory 解析失败", ex.Message); - } - return requestMessage; - } - - - /// - /// 获取XDocument转换后的IRequestMessageBase实例。 - /// 如果MsgType不存在,抛出UnknownRequestMsgTypeException异常 - /// - /// - public static IMessageBase GetRequestEntity(string xml) - { - return GetRequestEntity(XDocument.Parse(xml)); - } - - - /// - /// 获取XDocument转换后的IRequestMessageBase实例。 - /// 如果MsgType不存在,抛出UnknownRequestMsgTypeException异常 - /// - /// 如Request.InputStream - /// - public static IMessageBase GetRequestEntity(Stream stream) - { - using (XmlReader xr = XmlReader.Create(stream)) - { - var doc = XDocument.Load(xr); - - return GetRequestEntity(doc); - } - } - } -} +using Hncore.Infrastructure.Common; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.IO; +using System.Xml; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + /// + /// RequestMessage工厂 + /// + public static class MessageFactory + { + + public static IServiceCollection Service { get; set; } + /// + /// 获取XDocument转换后的IRequestMessageBase实例。 + /// 如果MsgType不存在,抛出UnknownRequestMsgTypeException异常 + /// + /// + public static IMessageBase GetRequestEntity(XDocument doc) + { + IMessageBase requestMessage = null; + RequestInfoType infoType; + + try + { + infoType = InfoTypeHelper.GetRequestInfoType(doc); + switch (infoType) + { + case RequestInfoType.component_verify_ticket: + requestMessage = new MessageComponentVerifyTicket(doc); + break; + case RequestInfoType.unauthorized: + requestMessage = new MessageUnauthorized(doc); + break; + case RequestInfoType.authorized: + requestMessage = new MessageAuthorized(doc); + break; + case RequestInfoType.updateauthorized: + requestMessage = new MessageUpdateAuthorized(doc); + break; + case RequestInfoType.notify_third_fasteregister: + requestMessage = new MessageThirdFasteRegister(doc); + break; + case RequestInfoType.event_subscribe_qrscene: + requestMessage = new MessageEventSubscribeQrscene(doc); + break; + case RequestInfoType.event_SCAN: + requestMessage = new MessageEventScan(doc); + break; + case RequestInfoType.event_subscribe: + requestMessage = new MessageEventSubscribe(doc); + break; + default: + return new MessageDefault(); + } + } + catch (Exception ex) + { + LogHelper.Error("MessageFactory 解析失败", ex.Message); + } + return requestMessage; + } + + + /// + /// 获取XDocument转换后的IRequestMessageBase实例。 + /// 如果MsgType不存在,抛出UnknownRequestMsgTypeException异常 + /// + /// + public static IMessageBase GetRequestEntity(string xml) + { + return GetRequestEntity(XDocument.Parse(xml)); + } + + + /// + /// 获取XDocument转换后的IRequestMessageBase实例。 + /// 如果MsgType不存在,抛出UnknownRequestMsgTypeException异常 + /// + /// 如Request.InputStream + /// + public static IMessageBase GetRequestEntity(Stream stream) + { + using (XmlReader xr = XmlReader.Create(stream)) + { + var doc = XDocument.Load(xr); + + return GetRequestEntity(doc); + } + } + } +} diff --git a/Infrastructure/WxApi/Notice/MpMessage/MessageEventScan.cs b/Infrastructure/WxApi/Notice/MpMessage/MessageEventScan.cs index 35e9cfc..7e1ba60 100644 --- a/Infrastructure/WxApi/Notice/MpMessage/MessageEventScan.cs +++ b/Infrastructure/WxApi/Notice/MpMessage/MessageEventScan.cs @@ -1,60 +1,60 @@ -/* - - - - 123456789 - - - - - - */ -using Hncore.Pass.MsgCenter.Constant; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Xml.Linq; -using Hncore.Pass.MsgCenter.Util; -namespace Hncore.Wx.Open -{ - /// - /// 扫描二维码关注 - /// - public class MessageEventScan : MessageMPBase - { - public MessageEventScan(XDocument doc) : base(doc) - { - this.EventKey = doc.Root.Element("EventKey").Value; - this.Ticket = doc.Root.Element("Ticket").Value; - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.event_SCAN; } - } - - /// - /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值 - /// - public string EventKey { get; set; } - /// - /// 二维码的ticket,可用来换取二维码图片 - /// - public string Ticket { get; set; } - - public override async Task Handler() - { - ///参数形式 method?a=1&b=2 - var dataUrl = this.EventKey; - var model = UrlHelper.ParseUrl(dataUrl); - - if (model.Method == "addtag") - { - var tagid = model.Args["tagid"]; - - WxOpenApi.AddTag(this.AppId, new List() { this.FromUserName }, tagid); - } - return true; - } - } -} +/* + + + + 123456789 + + + + + + */ +using Hncore.Pass.MsgCenter.Constant; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; +using Hncore.Pass.MsgCenter.Util; +namespace Hncore.Wx.Open +{ + /// + /// 扫描二维码关注 + /// + public class MessageEventScan : MessageMPBase + { + public MessageEventScan(XDocument doc) : base(doc) + { + this.EventKey = doc.Root.Element("EventKey").Value; + this.Ticket = doc.Root.Element("Ticket").Value; + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.event_SCAN; } + } + + /// + /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值 + /// + public string EventKey { get; set; } + /// + /// 二维码的ticket,可用来换取二维码图片 + /// + public string Ticket { get; set; } + + public override async Task Handler() + { + ///参数形式 method?a=1&b=2 + var dataUrl = this.EventKey; + var model = UrlHelper.ParseUrl(dataUrl); + + if (model.Method == "addtag") + { + var tagid = model.Args["tagid"]; + + WxOpenApi.AddTag(this.AppId, new List() { this.FromUserName }, tagid); + } + return true; + } + } +} diff --git a/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribe.cs b/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribe.cs index 95d6b7d..7dc2bd6 100644 --- a/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribe.cs +++ b/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribe.cs @@ -1,38 +1,38 @@ -/* - - - - 123456789 - - - - */ -using Hncore.Infrastructure.Common; -using Microsoft.EntityFrameworkCore; -using System.Threading.Tasks; -using System.Xml.Linq; -namespace Hncore.Wx.Open -{ - /// - /// 关注 - /// - public class MessageEventSubscribe : MessageMPBase - { - public MessageEventSubscribe(XDocument doc) : base(doc) - { - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.event_subscribe; } - } - - public override async Task Handler() - { - LogHelper.Info("MessageEventSubscribe", $"AppId={this.AppId},openid={this.FromUserName}"); - var userInfo = await WxOpenApi.GetUserUnionIDinfo(this.AppId, this.FromUserName); - LogHelper.Info("MessageEventSubscribe_userInfo", $"AppId={this.AppId},unionid={ userInfo.unionid}"); - - return true; - } - } -} +/* + + + + 123456789 + + + + */ +using Hncore.Infrastructure.Common; +using Microsoft.EntityFrameworkCore; +using System.Threading.Tasks; +using System.Xml.Linq; +namespace Hncore.Wx.Open +{ + /// + /// 关注 + /// + public class MessageEventSubscribe : MessageMPBase + { + public MessageEventSubscribe(XDocument doc) : base(doc) + { + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.event_subscribe; } + } + + public override async Task Handler() + { + LogHelper.Info("MessageEventSubscribe", $"AppId={this.AppId},openid={this.FromUserName}"); + var userInfo = await WxOpenApi.GetUserUnionIDinfo(this.AppId, this.FromUserName); + LogHelper.Info("MessageEventSubscribe_userInfo", $"AppId={this.AppId},unionid={ userInfo.unionid}"); + + return true; + } + } +} diff --git a/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribeQrscene.cs b/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribeQrscene.cs index e4081f8..83533b1 100644 --- a/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribeQrscene.cs +++ b/Infrastructure/WxApi/Notice/MpMessage/MessageEventSubscribeQrscene.cs @@ -1,64 +1,64 @@ -/* - - - - 123456789 - - - - - - */ -using Hncore.Pass.MsgCenter.Util; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - /// - /// 扫描二维码关注 - /// - public class MessageEventSubscribeQrscene : MessageMPBase - { - public MessageEventSubscribeQrscene(XDocument doc) : base(doc) - { - this.EventKey = doc.Root.Element("EventKey").Value; - this.Ticket = doc.Root.Element("Ticket").Value; - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.event_subscribe_qrscene; } - } - - /// - /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值 - /// - public string EventKey { get; set; } - /// - /// 二维码的ticket,可用来换取二维码图片 - /// - public string Ticket { get; set; } - - public override async Task Handler() - { - ///参数形式 method?a=1&b=2 - var dataUrl = EventKey.TrimStart("qrscene_".ToCharArray()); - var model = UrlHelper.ParseUrl(dataUrl); - - if (model.Method == "addtag") - { - var tagid = model.Args["tagid"]; - - WxOpenApi.AddTag(this.AppId, new List() { this.FromUserName }, tagid); - } - - var userInfo = await WxOpenApi.GetUserUnionIDinfo(this.AppId, this.FromUserName); - - - return true; - } - } -} +/* + + + + 123456789 + + + + + + */ +using Hncore.Pass.MsgCenter.Util; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + /// + /// 扫描二维码关注 + /// + public class MessageEventSubscribeQrscene : MessageMPBase + { + public MessageEventSubscribeQrscene(XDocument doc) : base(doc) + { + this.EventKey = doc.Root.Element("EventKey").Value; + this.Ticket = doc.Root.Element("Ticket").Value; + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.event_subscribe_qrscene; } + } + + /// + /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值 + /// + public string EventKey { get; set; } + /// + /// 二维码的ticket,可用来换取二维码图片 + /// + public string Ticket { get; set; } + + public override async Task Handler() + { + ///参数形式 method?a=1&b=2 + var dataUrl = EventKey.TrimStart("qrscene_".ToCharArray()); + var model = UrlHelper.ParseUrl(dataUrl); + + if (model.Method == "addtag") + { + var tagid = model.Args["tagid"]; + + WxOpenApi.AddTag(this.AppId, new List() { this.FromUserName }, tagid); + } + + var userInfo = await WxOpenApi.GetUserUnionIDinfo(this.AppId, this.FromUserName); + + + return true; + } + } +} diff --git a/Infrastructure/WxApi/Notice/MpMessage/MessageMPBase.cs b/Infrastructure/WxApi/Notice/MpMessage/MessageMPBase.cs index 74732e8..d8b69fa 100644 --- a/Infrastructure/WxApi/Notice/MpMessage/MessageMPBase.cs +++ b/Infrastructure/WxApi/Notice/MpMessage/MessageMPBase.cs @@ -1,35 +1,35 @@ -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - - /// - /// 请求消息 - /// - public class MessageMPBase : IMessageBase - { - - public MessageMPBase(XDocument doc) - { - this.FromUserName = doc.Root.Element("FromUserName").Value; - this.ToUserName = doc.Root.Element("ToUserName").Value; - this.CreateTime = long.Parse(doc.Root.Element("CreateTime").Value); - } - public string AppId { get; set; } - public string FromUserName { get; set; } - public string ToUserName { get; set; } - public long CreateTime { get; set; } - public virtual RequestInfoType InfoType - { - get { return RequestInfoType.component_verify_ticket; } - } - - - public virtual async Task Handler() - { - return true; - } - - } -} +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + + /// + /// 请求消息 + /// + public class MessageMPBase : IMessageBase + { + + public MessageMPBase(XDocument doc) + { + this.FromUserName = doc.Root.Element("FromUserName").Value; + this.ToUserName = doc.Root.Element("ToUserName").Value; + this.CreateTime = long.Parse(doc.Root.Element("CreateTime").Value); + } + public string AppId { get; set; } + public string FromUserName { get; set; } + public string ToUserName { get; set; } + public long CreateTime { get; set; } + public virtual RequestInfoType InfoType + { + get { return RequestInfoType.component_verify_ticket; } + } + + + public virtual async Task Handler() + { + return true; + } + + } +} diff --git a/Infrastructure/WxApi/Notice/OpenMessage/MessageAuthorized.cs b/Infrastructure/WxApi/Notice/OpenMessage/MessageAuthorized.cs index 8d26d4a..eeb6545 100644 --- a/Infrastructure/WxApi/Notice/OpenMessage/MessageAuthorized.cs +++ b/Infrastructure/WxApi/Notice/OpenMessage/MessageAuthorized.cs @@ -1,45 +1,45 @@ -/* - - 第三方平台appid - 1413192760 - authorized - 公众号appid - 授权码(code) - 过期时间 - - */ -using System; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - /// - /// 授权成功通知 - /// - public class MessageAuthorized : MessageOpenBase - { - public MessageAuthorized(XDocument doc) : base(doc) - { - this.AuthorizerAppid = doc.Root.Element("AuthorizerAppid").Value; - this.AuthorizationCode = doc.Root.Element("AuthorizationCode").Value; - this.AuthorizationCodeExpiredTime = DateTimeOffset.Parse(doc.Root.Element("AuthorizationCodeExpiredTime").Value); - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.authorized; } - } - - /// - /// 公众号appid - /// - public string AuthorizerAppid { get; set; } - /// - /// 授权码(code) - /// - public string AuthorizationCode { get; set; } - /// - /// 过期时间 - /// - public DateTimeOffset AuthorizationCodeExpiredTime { get; set; } - } -} +/* + + 第三方平台appid + 1413192760 + authorized + 公众号appid + 授权码(code) + 过期时间 + + */ +using System; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + /// + /// 授权成功通知 + /// + public class MessageAuthorized : MessageOpenBase + { + public MessageAuthorized(XDocument doc) : base(doc) + { + this.AuthorizerAppid = doc.Root.Element("AuthorizerAppid").Value; + this.AuthorizationCode = doc.Root.Element("AuthorizationCode").Value; + this.AuthorizationCodeExpiredTime = DateTimeOffset.Parse(doc.Root.Element("AuthorizationCodeExpiredTime").Value); + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.authorized; } + } + + /// + /// 公众号appid + /// + public string AuthorizerAppid { get; set; } + /// + /// 授权码(code) + /// + public string AuthorizationCode { get; set; } + /// + /// 过期时间 + /// + public DateTimeOffset AuthorizationCodeExpiredTime { get; set; } + } +} diff --git a/Infrastructure/WxApi/Notice/OpenMessage/MessageComponentVerifyTicket.cs b/Infrastructure/WxApi/Notice/OpenMessage/MessageComponentVerifyTicket.cs index 580ea9e..2b45ab7 100644 --- a/Infrastructure/WxApi/Notice/OpenMessage/MessageComponentVerifyTicket.cs +++ b/Infrastructure/WxApi/Notice/OpenMessage/MessageComponentVerifyTicket.cs @@ -1,32 +1,32 @@ -using Hncore.Pass.MsgCenter.Constant; -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - -// -// -// 1413192605 -// -// -// - public class MessageComponentVerifyTicket : MessageOpenBase - { - public MessageComponentVerifyTicket(XDocument doc):base(doc) - { - this.ComponentVerifyTicket = doc.Root.Element("ComponentVerifyTicket").Value; - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.component_verify_ticket; } - } - public string ComponentVerifyTicket { get; set; } - - public override async Task Handler() - { - //把ticket存入redis中 - return await RedisHelper.SetAsync(ConstantConfig.Redis_Psipwechat_Ticket_Key, this.ComponentVerifyTicket); - } - } -} +using Hncore.Pass.MsgCenter.Constant; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + +// +// +// 1413192605 +// +// +// + public class MessageComponentVerifyTicket : MessageOpenBase + { + public MessageComponentVerifyTicket(XDocument doc):base(doc) + { + this.ComponentVerifyTicket = doc.Root.Element("ComponentVerifyTicket").Value; + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.component_verify_ticket; } + } + public string ComponentVerifyTicket { get; set; } + + public override async Task Handler() + { + //把ticket存入redis中 + return await RedisHelper.SetAsync(ConstantConfig.Redis_Psipwechat_Ticket_Key, this.ComponentVerifyTicket); + } + } +} diff --git a/Infrastructure/WxApi/Notice/OpenMessage/MessageOpenBase.cs b/Infrastructure/WxApi/Notice/OpenMessage/MessageOpenBase.cs index b43e696..878858d 100644 --- a/Infrastructure/WxApi/Notice/OpenMessage/MessageOpenBase.cs +++ b/Infrastructure/WxApi/Notice/OpenMessage/MessageOpenBase.cs @@ -1,28 +1,28 @@ -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - - /// - /// 请求消息 - /// - public class MessageOpenBase : IMessageBase - { - public MessageOpenBase(XDocument doc) - { - this.AppId = doc.Root.Element("AppId").Value; - this.CreateTime = long.Parse(doc.Root.Element("CreateTime").Value); - } - public string AppId { get; set; } - public long CreateTime { get; set; } - public virtual RequestInfoType InfoType - { - get { return RequestInfoType.component_verify_ticket; } - } - public virtual async Task Handler() - { - return true; - } - } -} +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + + /// + /// 请求消息 + /// + public class MessageOpenBase : IMessageBase + { + public MessageOpenBase(XDocument doc) + { + this.AppId = doc.Root.Element("AppId").Value; + this.CreateTime = long.Parse(doc.Root.Element("CreateTime").Value); + } + public string AppId { get; set; } + public long CreateTime { get; set; } + public virtual RequestInfoType InfoType + { + get { return RequestInfoType.component_verify_ticket; } + } + public virtual async Task Handler() + { + return true; + } + } +} diff --git a/Infrastructure/WxApi/Notice/OpenMessage/MessageThirdFasteRegister.cs b/Infrastructure/WxApi/Notice/OpenMessage/MessageThirdFasteRegister.cs index 57ab14b..2e1f426 100644 --- a/Infrastructure/WxApi/Notice/OpenMessage/MessageThirdFasteRegister.cs +++ b/Infrastructure/WxApi/Notice/OpenMessage/MessageThirdFasteRegister.cs @@ -1,70 +1,70 @@ -using System; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - /// - /// 注册审核事件推送 - /// - public class MessageThirdFasteRegister : MessageOpenBase - { - public MessageThirdFasteRegister(XDocument doc) : base(doc) - { - - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.notify_third_fasteregister; } - } - - /// - /// 创建小程序appid - /// - public string appid { get; set; } - - public string status { get; set; } - /// - /// 第三方授权码 - /// - public string auth_code { get; set; } - - public string msg { get; set; } - - /// - /// 注册时提交的资料 - /// - public info info {get;set;} - } - - /// - /// 注册时提交的资料信息 - /// - public class info - { - /// - /// 企业名称 - /// - public string name { get; set; } - - /// - /// 企业代码 - /// - public string code { get; set; } - /// - /// 企业代码类型 - /// - public CodeType code_type { get; set; } - /// - /// 法人微信号 - /// - public string legal_persona_wechat { get; set; } - /// - /// 法人姓名 - /// - public string legal_persona_name { get; set; } - /// - /// 第三方联系电话 - /// - public string component_phone { get; set; } - } -} +using System; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + /// + /// 注册审核事件推送 + /// + public class MessageThirdFasteRegister : MessageOpenBase + { + public MessageThirdFasteRegister(XDocument doc) : base(doc) + { + + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.notify_third_fasteregister; } + } + + /// + /// 创建小程序appid + /// + public string appid { get; set; } + + public string status { get; set; } + /// + /// 第三方授权码 + /// + public string auth_code { get; set; } + + public string msg { get; set; } + + /// + /// 注册时提交的资料 + /// + public info info {get;set;} + } + + /// + /// 注册时提交的资料信息 + /// + public class info + { + /// + /// 企业名称 + /// + public string name { get; set; } + + /// + /// 企业代码 + /// + public string code { get; set; } + /// + /// 企业代码类型 + /// + public CodeType code_type { get; set; } + /// + /// 法人微信号 + /// + public string legal_persona_wechat { get; set; } + /// + /// 法人姓名 + /// + public string legal_persona_name { get; set; } + /// + /// 第三方联系电话 + /// + public string component_phone { get; set; } + } +} diff --git a/Infrastructure/WxApi/Notice/OpenMessage/MessageUnauthorized.cs b/Infrastructure/WxApi/Notice/OpenMessage/MessageUnauthorized.cs index 64102ee..f254ae0 100644 --- a/Infrastructure/WxApi/Notice/OpenMessage/MessageUnauthorized.cs +++ b/Infrastructure/WxApi/Notice/OpenMessage/MessageUnauthorized.cs @@ -1,44 +1,44 @@ - -/* - - 第三方平台appid - 1413192760 - unauthorized - 公众号appid - -*/ -using Hncore.Infrastructure.Common; -using System; -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - public class MessageUnauthorized : MessageOpenBase - { - public MessageUnauthorized(XDocument doc) : base(doc) - { - this.AuthorizerAppid = doc.Root.Element("AuthorizerAppid").Value; - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.unauthorized; } - } - public string AuthorizerAppid { get; set; } - - public override async Task Handler() - { - if (!string.IsNullOrEmpty(this.AuthorizerAppid)) - { - try - { - } - catch(Exception ex) - { - LogHelper.Error("MessageUnauthorized",ex.Message); - } - } - return false; - } - } -} + +/* + + 第三方平台appid + 1413192760 + unauthorized + 公众号appid + +*/ +using Hncore.Infrastructure.Common; +using System; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + public class MessageUnauthorized : MessageOpenBase + { + public MessageUnauthorized(XDocument doc) : base(doc) + { + this.AuthorizerAppid = doc.Root.Element("AuthorizerAppid").Value; + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.unauthorized; } + } + public string AuthorizerAppid { get; set; } + + public override async Task Handler() + { + if (!string.IsNullOrEmpty(this.AuthorizerAppid)) + { + try + { + } + catch(Exception ex) + { + LogHelper.Error("MessageUnauthorized",ex.Message); + } + } + return false; + } + } +} diff --git a/Infrastructure/WxApi/Notice/OpenMessage/MessageUpdateAuthorized.cs b/Infrastructure/WxApi/Notice/OpenMessage/MessageUpdateAuthorized.cs index c1b95aa..2bdc757 100644 --- a/Infrastructure/WxApi/Notice/OpenMessage/MessageUpdateAuthorized.cs +++ b/Infrastructure/WxApi/Notice/OpenMessage/MessageUpdateAuthorized.cs @@ -1,36 +1,36 @@ - -using System; -using System.Xml.Linq; - -namespace Hncore.Wx.Open -{ - /// - /// 授权更新通知 - /// - public class MessageUpdateAuthorized : MessageOpenBase - { - public MessageUpdateAuthorized(XDocument doc) : base(doc) - { - this.AuthorizerAppid = doc.Root.Element("AuthorizerAppid").Value; - this.AuthorizationCode = doc.Root.Element("AuthorizationCode").Value; - this.AuthorizationCodeExpiredTime = DateTimeOffset.Parse(doc.Root.Element("AuthorizationCodeExpiredTime").Value); - } - public override RequestInfoType InfoType - { - get { return RequestInfoType.updateauthorized; } - } - - /// - /// 公众号appid - /// - public string AuthorizerAppid { get; set; } - /// - /// 授权码(code) - /// - public string AuthorizationCode { get; set; } - /// - /// 过期时间 - /// - public DateTimeOffset AuthorizationCodeExpiredTime { get; set; } - } -} + +using System; +using System.Xml.Linq; + +namespace Hncore.Wx.Open +{ + /// + /// 授权更新通知 + /// + public class MessageUpdateAuthorized : MessageOpenBase + { + public MessageUpdateAuthorized(XDocument doc) : base(doc) + { + this.AuthorizerAppid = doc.Root.Element("AuthorizerAppid").Value; + this.AuthorizationCode = doc.Root.Element("AuthorizationCode").Value; + this.AuthorizationCodeExpiredTime = DateTimeOffset.Parse(doc.Root.Element("AuthorizationCodeExpiredTime").Value); + } + public override RequestInfoType InfoType + { + get { return RequestInfoType.updateauthorized; } + } + + /// + /// 公众号appid + /// + public string AuthorizerAppid { get; set; } + /// + /// 授权码(code) + /// + public string AuthorizationCode { get; set; } + /// + /// 过期时间 + /// + public DateTimeOffset AuthorizationCodeExpiredTime { get; set; } + } +} diff --git a/Infrastructure/WxApi/Request/GetAuthenticationUrlRequest.cs b/Infrastructure/WxApi/Request/GetAuthenticationUrlRequest.cs index 62e3a2a..4159732 100644 --- a/Infrastructure/WxApi/Request/GetAuthenticationUrlRequest.cs +++ b/Infrastructure/WxApi/Request/GetAuthenticationUrlRequest.cs @@ -1,15 +1,15 @@ -namespace Etor.Wx.Open -{ - /// - /// 获取公众号授权连接的URL - /// - public class GetAuthenticationUrlRequest - { - public string component_appid { get; set; } - /// - /// 从来 - /// - public string pre_auth_code { get; set; } - public string redirect_uri { get; set; } - } +namespace Etor.Wx.Open +{ + /// + /// 获取公众号授权连接的URL + /// + public class GetAuthenticationUrlRequest + { + public string component_appid { get; set; } + /// + /// 从来 + /// + public string pre_auth_code { get; set; } + public string redirect_uri { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetAuthorizerClientUserOpenIdRequest.cs b/Infrastructure/WxApi/Request/GetAuthorizerClientUserOpenIdRequest.cs index 13437e0..04d9313 100644 --- a/Infrastructure/WxApi/Request/GetAuthorizerClientUserOpenIdRequest.cs +++ b/Infrastructure/WxApi/Request/GetAuthorizerClientUserOpenIdRequest.cs @@ -1,10 +1,10 @@ -namespace Etor.Wx.Open -{ - public class GetAuthorizerClientUserOpenIdRequest - { - public object AuthorizerAppId { get; set; } - public object CallbackCode { get; set; } - public object AppID { get; set; } - public object ComponentAccessToken { get; set; } - } +namespace Etor.Wx.Open +{ + public class GetAuthorizerClientUserOpenIdRequest + { + public object AuthorizerAppId { get; set; } + public object CallbackCode { get; set; } + public object AppID { get; set; } + public object ComponentAccessToken { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetAuthorizerInfoRequest.cs b/Infrastructure/WxApi/Request/GetAuthorizerInfoRequest.cs index 7ab68e3..2fef0be 100644 --- a/Infrastructure/WxApi/Request/GetAuthorizerInfoRequest.cs +++ b/Infrastructure/WxApi/Request/GetAuthorizerInfoRequest.cs @@ -1,17 +1,17 @@ -namespace Etor.Wx.Open -{ - - - //{"authorizer_info":{"nick_name":"etor生活","head_img":"http:\/\/wx.qlogo.cn\/mmopen\/nlGmMCWo3wjyEnPPhOv2ygZbyEfyHmQHURK1odOyqGTys5kpdwFCmFo4PV6j2lCtkIWzqCJJvy9Ml7LS1XqUY3wsYn5lHKRX\/0","service_type_info":{"id":2},"verify_type_info":{"id":0},"user_name":"gh_d2ce4e1abd93","alias":"","qrcode_url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/OdctpfnnOo6dEuSOQRFTK9V4ue1yxH7UGAZuKtJlN5M64Grm8tRWgSEnrYWIPlSUf2AaKwialhGWh4wLbHbZ1ng\/0","business_info":{"open_pay":0,"open_shake":0,"open_scan":0,"open_card":0,"open_store":0},"idc":1,"principal_name":"河南云拓智能科技有限公司","signature":"etor生活,围绕商业综合体为周边人群提供智慧、便捷化服务的生活助手"},"authorization_info":{"authorizer_appid":"wx5cf944c37bf234ee","authorizer_refresh_token":"refreshtoken@@@fxrfTkTrpHPEHMDjGFWvljuB_JFW4VPQY7kmkbjamXY","func_info":[{"funcscope_category":{"id":1}},{"funcscope_category":{"id":15}},{"funcscope_category":{"id":4}},{"funcscope_category":{"id":7}},{"funcscope_category":{"id":2}},{"funcscope_category":{"id":3}},{"funcscope_category":{"id":11}}]}} - - - - - - public class GetAuthorizerInfoRequest - { - public string component_access_token { get; set; } - public string component_appid { get; set; } - public string authorizer_appid { get; set; } - } +namespace Etor.Wx.Open +{ + + + //{"authorizer_info":{"nick_name":"etor生活","head_img":"http:\/\/wx.qlogo.cn\/mmopen\/nlGmMCWo3wjyEnPPhOv2ygZbyEfyHmQHURK1odOyqGTys5kpdwFCmFo4PV6j2lCtkIWzqCJJvy9Ml7LS1XqUY3wsYn5lHKRX\/0","service_type_info":{"id":2},"verify_type_info":{"id":0},"user_name":"gh_d2ce4e1abd93","alias":"","qrcode_url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/OdctpfnnOo6dEuSOQRFTK9V4ue1yxH7UGAZuKtJlN5M64Grm8tRWgSEnrYWIPlSUf2AaKwialhGWh4wLbHbZ1ng\/0","business_info":{"open_pay":0,"open_shake":0,"open_scan":0,"open_card":0,"open_store":0},"idc":1,"principal_name":"河南云拓智能科技有限公司","signature":"etor生活,围绕商业综合体为周边人群提供智慧、便捷化服务的生活助手"},"authorization_info":{"authorizer_appid":"wx5cf944c37bf234ee","authorizer_refresh_token":"refreshtoken@@@fxrfTkTrpHPEHMDjGFWvljuB_JFW4VPQY7kmkbjamXY","func_info":[{"funcscope_category":{"id":1}},{"funcscope_category":{"id":15}},{"funcscope_category":{"id":4}},{"funcscope_category":{"id":7}},{"funcscope_category":{"id":2}},{"funcscope_category":{"id":3}},{"funcscope_category":{"id":11}}]}} + + + + + + public class GetAuthorizerInfoRequest + { + public string component_access_token { get; set; } + public string component_appid { get; set; } + public string authorizer_appid { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetAuthorizerInvokeAccessTokenRequest.cs b/Infrastructure/WxApi/Request/GetAuthorizerInvokeAccessTokenRequest.cs index a67218a..3f7937e 100644 --- a/Infrastructure/WxApi/Request/GetAuthorizerInvokeAccessTokenRequest.cs +++ b/Infrastructure/WxApi/Request/GetAuthorizerInvokeAccessTokenRequest.cs @@ -1,25 +1,25 @@ -namespace Etor.Wx.Open -{ - /// - /// 获取代表的第三方App的api执行权限 - /// - public class GetAuthorizerInvokeAccessTokenRequest - { - /// - /// 第三方平台的访问令牌 - /// - public string component_access_token { get; set; } - /// - /// 第三方平台的AppId - /// - public string component_appid { get; set; } - /// - /// 授权码,在公众号授权后,发送到回调地址上 - /// 从拿到authorization_code - /// - public string authorization_code - { - get; set; - } - } +namespace Etor.Wx.Open +{ + /// + /// 获取代表的第三方App的api执行权限 + /// + public class GetAuthorizerInvokeAccessTokenRequest + { + /// + /// 第三方平台的访问令牌 + /// + public string component_access_token { get; set; } + /// + /// 第三方平台的AppId + /// + public string component_appid { get; set; } + /// + /// 授权码,在公众号授权后,发送到回调地址上 + /// 从拿到authorization_code + /// + public string authorization_code + { + get; set; + } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetComponentAccessTokenRequest.cs b/Infrastructure/WxApi/Request/GetComponentAccessTokenRequest.cs index 91d6d4d..ad8c3bb 100644 --- a/Infrastructure/WxApi/Request/GetComponentAccessTokenRequest.cs +++ b/Infrastructure/WxApi/Request/GetComponentAccessTokenRequest.cs @@ -1,17 +1,17 @@ -namespace Etor.Wx.Open -{ - /// - /// 获取第三方App的AccessToken - /// 使用 - /// - /// - public class GetComponentAccessTokenRequest - { - public string component_appid { get; set; } - public string component_appsecret { get; set; } - /// - /// 这个请求可以拿到 - /// - public string component_verify_ticket { get; set; } - } +namespace Etor.Wx.Open +{ + /// + /// 获取第三方App的AccessToken + /// 使用 + /// + /// + public class GetComponentAccessTokenRequest + { + public string component_appid { get; set; } + public string component_appsecret { get; set; } + /// + /// 这个请求可以拿到 + /// + public string component_verify_ticket { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetComponentVerifyTicketRequest.cs b/Infrastructure/WxApi/Request/GetComponentVerifyTicketRequest.cs index 047dc13..48b84d9 100644 --- a/Infrastructure/WxApi/Request/GetComponentVerifyTicketRequest.cs +++ b/Infrastructure/WxApi/Request/GetComponentVerifyTicketRequest.cs @@ -1,10 +1,10 @@ -namespace Etor.Wx.Open -{ - /// - /// 从微信推送的消息内拉取ticket - /// - public class GetComponentVerifyTicketRequest - { - public string TicketSourceServerUrl { get; set; } - } +namespace Etor.Wx.Open +{ + /// + /// 从微信推送的消息内拉取ticket + /// + public class GetComponentVerifyTicketRequest + { + public string TicketSourceServerUrl { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetPreAuthCodeRequest.cs b/Infrastructure/WxApi/Request/GetPreAuthCodeRequest.cs index 7a6fcbd..f6b3db3 100644 --- a/Infrastructure/WxApi/Request/GetPreAuthCodeRequest.cs +++ b/Infrastructure/WxApi/Request/GetPreAuthCodeRequest.cs @@ -1,11 +1,11 @@ -namespace Etor.Wx.Open -{ - public class GetPreAuthCodeRequest - { - /// - /// 拿到的 - /// - public string component_access_token { get; set; } - public string component_appid { get; set; } - } +namespace Etor.Wx.Open +{ + public class GetPreAuthCodeRequest + { + /// + /// 拿到的 + /// + public string component_access_token { get; set; } + public string component_appid { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetSavedAuthorizerInfoRequest.cs b/Infrastructure/WxApi/Request/GetSavedAuthorizerInfoRequest.cs index 018d37c..46d64a8 100644 --- a/Infrastructure/WxApi/Request/GetSavedAuthorizerInfoRequest.cs +++ b/Infrastructure/WxApi/Request/GetSavedAuthorizerInfoRequest.cs @@ -1,8 +1,8 @@ -namespace Etor.Wx.Open -{ - public class GetSavedAuthorizerInfoRequest - { - public string DomainUrl { get; set; } - public string AppId { get; set; } - } +namespace Etor.Wx.Open +{ + public class GetSavedAuthorizerInfoRequest + { + public string DomainUrl { get; set; } + public string AppId { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetWechatTicketRequest.cs b/Infrastructure/WxApi/Request/GetWechatTicketRequest.cs index 600d7ec..308cbf4 100644 --- a/Infrastructure/WxApi/Request/GetWechatTicketRequest.cs +++ b/Infrastructure/WxApi/Request/GetWechatTicketRequest.cs @@ -1,9 +1,9 @@ -namespace Etor.Wx.Open -{ - /// - /// 获取wechat推送的ticket - /// - public class GetWechatTicketRequest - { - } +namespace Etor.Wx.Open +{ + /// + /// 获取wechat推送的ticket + /// + public class GetWechatTicketRequest + { + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/GetWxJsConfigRequest.cs b/Infrastructure/WxApi/Request/GetWxJsConfigRequest.cs index b72c469..112a557 100644 --- a/Infrastructure/WxApi/Request/GetWxJsConfigRequest.cs +++ b/Infrastructure/WxApi/Request/GetWxJsConfigRequest.cs @@ -1,51 +1,51 @@ -using Hncore.Infrastructure.Extension; -using System; -using System.Collections.Generic; - -namespace Etor.Wx.Open -{ - public class jsapi_ticket - { - public static object get_locker = new object(); - public int errcode { get; set; } - public string errmsg { get; set; } - public string ticket { get; set; } - public int expires_in { get; set; } - public int created_timestamp { get; set; } - - public bool is_expired - { - get - { - var dt = (DateTime.Now.TimestampFrom19700101() - this.created_timestamp); - - return errcode != 0 || dt < 0 || dt > 6000; - } - } - } - public class wx_config - { - public bool debug { get; set; } - public string appId { get; set; } - public string timestamp { get; set; } - public string nonceStr { get; set; } - public string signature { get; set; } - public List jsApiList { get; set; } - } - - - /// - /// 获取wxjs配置请求 - /// - public class GetWxJsConfigRequest - { - /// - /// 公众号appid - /// - public string Appid { get; set; } - /// - /// 当前的请求地址 - /// - public string CurrentUrl { get; set; } - } +using Hncore.Infrastructure.Extension; +using System; +using System.Collections.Generic; + +namespace Etor.Wx.Open +{ + public class jsapi_ticket + { + public static object get_locker = new object(); + public int errcode { get; set; } + public string errmsg { get; set; } + public string ticket { get; set; } + public int expires_in { get; set; } + public int created_timestamp { get; set; } + + public bool is_expired + { + get + { + var dt = (DateTime.Now.TimestampFrom19700101() - this.created_timestamp); + + return errcode != 0 || dt < 0 || dt > 6000; + } + } + } + public class wx_config + { + public bool debug { get; set; } + public string appId { get; set; } + public string timestamp { get; set; } + public string nonceStr { get; set; } + public string signature { get; set; } + public List jsApiList { get; set; } + } + + + /// + /// 获取wxjs配置请求 + /// + public class GetWxJsConfigRequest + { + /// + /// 公众号appid + /// + public string Appid { get; set; } + /// + /// 当前的请求地址 + /// + public string CurrentUrl { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/RefreshAuthorizerInvokeAccessTokenRequest.cs b/Infrastructure/WxApi/Request/RefreshAuthorizerInvokeAccessTokenRequest.cs index 7ace0a4..6d58180 100644 --- a/Infrastructure/WxApi/Request/RefreshAuthorizerInvokeAccessTokenRequest.cs +++ b/Infrastructure/WxApi/Request/RefreshAuthorizerInvokeAccessTokenRequest.cs @@ -1,19 +1,19 @@ -namespace Etor.Wx.Open -{ - // { - //"component_appid":"appid_value", - //"authorizer_appid":"auth_appid_value", - //"authorizer_refresh_token":"refresh_token_value", - //} - - /// - /// 刷新从得到的AccessToken - /// - public class RefreshAuthorizerInvokeAccessTokenRequest - { - public string component_appid { get; set; } - public string authorizer_appid { get; set; } - public string authorizer_refresh_token { get; set; } - public string component_access_token { get; set; } - } +namespace Etor.Wx.Open +{ + // { + //"component_appid":"appid_value", + //"authorizer_appid":"auth_appid_value", + //"authorizer_refresh_token":"refresh_token_value", + //} + + /// + /// 刷新从得到的AccessToken + /// + public class RefreshAuthorizerInvokeAccessTokenRequest + { + public string component_appid { get; set; } + public string authorizer_appid { get; set; } + public string authorizer_refresh_token { get; set; } + public string component_access_token { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/SaveAuthorizerInfoRequest.cs b/Infrastructure/WxApi/Request/SaveAuthorizerInfoRequest.cs index 1b906b2..a81206d 100644 --- a/Infrastructure/WxApi/Request/SaveAuthorizerInfoRequest.cs +++ b/Infrastructure/WxApi/Request/SaveAuthorizerInfoRequest.cs @@ -1,9 +1,9 @@ -namespace Hncore.Wx.Open -{ - public class SaveAuthorizerInfoRequest - { - public string Domain { get; set; } - public authorizer_info AuthorizerInfo { get; set; } - public authorization_info AuthorizationInfo { get; set; } - } +namespace Hncore.Wx.Open +{ + public class SaveAuthorizerInfoRequest + { + public string Domain { get; set; } + public authorizer_info AuthorizerInfo { get; set; } + public authorization_info AuthorizationInfo { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/SaveWechatAppMenuRequest.cs b/Infrastructure/WxApi/Request/SaveWechatAppMenuRequest.cs index 2304515..7a75148 100644 --- a/Infrastructure/WxApi/Request/SaveWechatAppMenuRequest.cs +++ b/Infrastructure/WxApi/Request/SaveWechatAppMenuRequest.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; - -namespace Hncore.Wx.Open -{ - public class SaveWechatAppMenuRequest - { - } +using System.Collections.Generic; + +namespace Hncore.Wx.Open +{ + public class SaveWechatAppMenuRequest + { + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/UpdateWechatTicketRequest.cs b/Infrastructure/WxApi/Request/UpdateWechatTicketRequest.cs index 1dd0292..2a17695 100644 --- a/Infrastructure/WxApi/Request/UpdateWechatTicketRequest.cs +++ b/Infrastructure/WxApi/Request/UpdateWechatTicketRequest.cs @@ -1,10 +1,10 @@ -namespace Etor.Wx.Open -{ - /// - /// 更新微信推送的票据 - /// - public class UpdateWechatTicketRequest - { - public string NewTicket { get; set; } - } +namespace Etor.Wx.Open +{ + /// + /// 更新微信推送的票据 + /// + public class UpdateWechatTicketRequest + { + public string NewTicket { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Request/UploadWxImageMaterialRequest.cs b/Infrastructure/WxApi/Request/UploadWxImageMaterialRequest.cs index b5f9ba7..798cd91 100644 --- a/Infrastructure/WxApi/Request/UploadWxImageMaterialRequest.cs +++ b/Infrastructure/WxApi/Request/UploadWxImageMaterialRequest.cs @@ -1,38 +1,38 @@ -namespace Etor.Wx.Open -{ - public class UploadWxImageMaterialRequest - { - /// - /// 是否永久素材 - /// - public bool IsTemp { get; set; } - /// - /// image 图片 thumb 缩略图 - /// - public string MediaType { get; set; } - /// - /// 图片名称 - /// - public string imagename { get; set; } - /// - /// 分组id,默认0 - /// - public int groupid { get; set; } - - /// - /// 小区/写字楼编码 - /// - public string projectcode { get; set; } - - /// - /// 公众号appid - /// - public string appid { get; set; } - /// - ///图片地址 - /// - public string url { get; set; } - - - } -} +namespace Etor.Wx.Open +{ + public class UploadWxImageMaterialRequest + { + /// + /// 是否永久素材 + /// + public bool IsTemp { get; set; } + /// + /// image 图片 thumb 缩略图 + /// + public string MediaType { get; set; } + /// + /// 图片名称 + /// + public string imagename { get; set; } + /// + /// 分组id,默认0 + /// + public int groupid { get; set; } + + /// + /// 小区/写字楼编码 + /// + public string projectcode { get; set; } + + /// + /// 公众号appid + /// + public string appid { get; set; } + /// + ///图片地址 + /// + public string url { get; set; } + + + } +} diff --git a/Infrastructure/WxApi/Request/UploadWxMediaRequest.cs b/Infrastructure/WxApi/Request/UploadWxMediaRequest.cs index c1fac1a..34eca42 100644 --- a/Infrastructure/WxApi/Request/UploadWxMediaRequest.cs +++ b/Infrastructure/WxApi/Request/UploadWxMediaRequest.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; - -namespace Etor.Wx.Open -{ - public class UploadWxMediaRequest - { - - } -} +using System.Collections.Generic; + +namespace Etor.Wx.Open +{ + public class UploadWxMediaRequest + { + + } +} diff --git a/Infrastructure/WxApi/Request/WechatRequestBase.cs b/Infrastructure/WxApi/Request/WechatRequestBase.cs index f53b67e..6c1ccc9 100644 --- a/Infrastructure/WxApi/Request/WechatRequestBase.cs +++ b/Infrastructure/WxApi/Request/WechatRequestBase.cs @@ -1,16 +1,16 @@ -namespace Etor.Wx.Open -{ - public class WechatRequestBase - { - public int OwnerID { get; set; } - public int OperaterID { get; set; } - public int ProjectCode { get; set; } - protected string _url; - public string url { get { return _url; } } - public string access_token { get; set; } - } - public class WechatRequestBase : WechatRequestBase - { - public TData post_body { get; set; } - } -} +namespace Etor.Wx.Open +{ + public class WechatRequestBase + { + public int OwnerID { get; set; } + public int OperaterID { get; set; } + public int ProjectCode { get; set; } + protected string _url; + public string url { get { return _url; } } + public string access_token { get; set; } + } + public class WechatRequestBase : WechatRequestBase + { + public TData post_body { get; set; } + } +} diff --git a/Infrastructure/WxApi/Response/CreateQrcodeResponse.cs b/Infrastructure/WxApi/Response/CreateQrcodeResponse.cs index e05b028..591dc27 100644 --- a/Infrastructure/WxApi/Response/CreateQrcodeResponse.cs +++ b/Infrastructure/WxApi/Response/CreateQrcodeResponse.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; - -namespace Hncore.Wx.Open -{ - public class CreateQrcodeResponse : ResponseBase - { - public string ticket { get; set; } - public string url { get; set; } - } +using System.Collections.Generic; + +namespace Hncore.Wx.Open +{ + public class CreateQrcodeResponse : ResponseBase + { + public string ticket { get; set; } + public string url { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetAuthenticationUrlResponse.cs b/Infrastructure/WxApi/Response/GetAuthenticationUrlResponse.cs index 5a2b600..db0aafa 100644 --- a/Infrastructure/WxApi/Response/GetAuthenticationUrlResponse.cs +++ b/Infrastructure/WxApi/Response/GetAuthenticationUrlResponse.cs @@ -1,6 +1,6 @@ -namespace Hncore.Wx.Open -{ - public class GetAuthenticationUrlResponse : ResponseBase - { - } +namespace Hncore.Wx.Open +{ + public class GetAuthenticationUrlResponse : ResponseBase + { + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetAuthorizerClientUserOpenIdResponse.cs b/Infrastructure/WxApi/Response/GetAuthorizerClientUserOpenIdResponse.cs index d4d6630..c3e335e 100644 --- a/Infrastructure/WxApi/Response/GetAuthorizerClientUserOpenIdResponse.cs +++ b/Infrastructure/WxApi/Response/GetAuthorizerClientUserOpenIdResponse.cs @@ -1,7 +1,7 @@ -namespace Hncore.Wx.Open -{ - public class GetAuthorizerClientUserOpenIdResponse : ResponseBase - { - public wechat_access_token AccessToken { get; set; } - } +namespace Hncore.Wx.Open +{ + public class GetAuthorizerClientUserOpenIdResponse : ResponseBase + { + public wechat_access_token AccessToken { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetAuthorizerInfoResponse.cs b/Infrastructure/WxApi/Response/GetAuthorizerInfoResponse.cs index 363b5a4..3a12add 100644 --- a/Infrastructure/WxApi/Response/GetAuthorizerInfoResponse.cs +++ b/Infrastructure/WxApi/Response/GetAuthorizerInfoResponse.cs @@ -1,9 +1,9 @@ -namespace Hncore.Wx.Open -{ - public class GetAuthorizerInfoResponse : ResponseBase - { - public authorizer_info authorizer_info { get; set; } - public authorization_info authorization_info { get; set; } - public string content { get; set; } - } +namespace Hncore.Wx.Open +{ + public class GetAuthorizerInfoResponse : ResponseBase + { + public authorizer_info authorizer_info { get; set; } + public authorization_info authorization_info { get; set; } + public string content { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetAuthorizerInvokeAccessTokenResponse.cs b/Infrastructure/WxApi/Response/GetAuthorizerInvokeAccessTokenResponse.cs index 7c3b167..1788b6b 100644 --- a/Infrastructure/WxApi/Response/GetAuthorizerInvokeAccessTokenResponse.cs +++ b/Infrastructure/WxApi/Response/GetAuthorizerInvokeAccessTokenResponse.cs @@ -1,32 +1,32 @@ -namespace Hncore.Wx.Open -{ - // { - //"authorization_info": - //{ - //"authorizer_appid": "wxf8b4f85f3a794e77", - //"authorizer_access_token": "QXjUqNqfYVH0yBE1iI_7vuN_9gQbpjfK7hYwJ3P7xOa88a89-Aga5x1NMYJyB8G2yKt1KCl0nPC3W9GJzw0Zzq_dBxc8pxIGUNi_bFes0qM", - //"expires_in": 7200, - //"authorizer_refresh_token": "dTo-YCXPL4llX-u1W1pPpnp8Hgm4wpJtlR6iV0doKdY", - //"func_info": - // [ - //{"funcscope_category": {"id": 1}}, - //{"funcscope_category": {"id": 2}}, - //{"funcscope_category": {"id": 3}} - //] - //} - //} - - - /// - /// 授权码换取访问令牌结果类 - /// - /// - /// - public class GetAuthorizerInvokeAccessTokenResponse:ResponseBase - { - /// - /// 授权信息对象 - /// - public authorization_info authorization_info { get; set; } - } +namespace Hncore.Wx.Open +{ + // { + //"authorization_info": + //{ + //"authorizer_appid": "wxf8b4f85f3a794e77", + //"authorizer_access_token": "QXjUqNqfYVH0yBE1iI_7vuN_9gQbpjfK7hYwJ3P7xOa88a89-Aga5x1NMYJyB8G2yKt1KCl0nPC3W9GJzw0Zzq_dBxc8pxIGUNi_bFes0qM", + //"expires_in": 7200, + //"authorizer_refresh_token": "dTo-YCXPL4llX-u1W1pPpnp8Hgm4wpJtlR6iV0doKdY", + //"func_info": + // [ + //{"funcscope_category": {"id": 1}}, + //{"funcscope_category": {"id": 2}}, + //{"funcscope_category": {"id": 3}} + //] + //} + //} + + + /// + /// 授权码换取访问令牌结果类 + /// + /// + /// + public class GetAuthorizerInvokeAccessTokenResponse:ResponseBase + { + /// + /// 授权信息对象 + /// + public authorization_info authorization_info { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetComponentAccessTokenResponse.cs b/Infrastructure/WxApi/Response/GetComponentAccessTokenResponse.cs index 714117d..1017bb2 100644 --- a/Infrastructure/WxApi/Response/GetComponentAccessTokenResponse.cs +++ b/Infrastructure/WxApi/Response/GetComponentAccessTokenResponse.cs @@ -1,18 +1,18 @@ -namespace Hncore.Wx.Open -{ - - // { - //"component_access_token":"61W3mEpU66027wgNZ_MhGHNQDHnFATkDa9-2llqrMBjUwxRSNPbVsMmyD-yq8wZETSoE5NQgecigDrSHkPtIYA", - //"expires_in":7200 - //} - - - /// - /// - /// - public class GetComponentAccessTokenResponse : ResponseBase - { - public string component_access_token { get; set; } - - } +namespace Hncore.Wx.Open +{ + + // { + //"component_access_token":"61W3mEpU66027wgNZ_MhGHNQDHnFATkDa9-2llqrMBjUwxRSNPbVsMmyD-yq8wZETSoE5NQgecigDrSHkPtIYA", + //"expires_in":7200 + //} + + + /// + /// + /// + public class GetComponentAccessTokenResponse : ResponseBase + { + public string component_access_token { get; set; } + + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetComponentVerifyTicketResponse.cs b/Infrastructure/WxApi/Response/GetComponentVerifyTicketResponse.cs index d5928ae..d669101 100644 --- a/Infrastructure/WxApi/Response/GetComponentVerifyTicketResponse.cs +++ b/Infrastructure/WxApi/Response/GetComponentVerifyTicketResponse.cs @@ -1,10 +1,10 @@ -namespace Hncore.Wx.Open -{ - /// - /// - /// - public class GetComponentVerifyTicketResponse:ResponseBase - { - public string ComponentVerifyTicket { get; set; } - } +namespace Hncore.Wx.Open +{ + /// + /// + /// + public class GetComponentVerifyTicketResponse:ResponseBase + { + public string ComponentVerifyTicket { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetGetWebAccessTokenResponse.cs b/Infrastructure/WxApi/Response/GetGetWebAccessTokenResponse.cs index b8c8975..5704134 100644 --- a/Infrastructure/WxApi/Response/GetGetWebAccessTokenResponse.cs +++ b/Infrastructure/WxApi/Response/GetGetWebAccessTokenResponse.cs @@ -1,10 +1,10 @@ -namespace Hncore.Wx.Open -{ - public class GetGetWebAccessTokenResponse : ResponseBase - { - public string access_token { get; set; } - public string refresh_token { get; set; } - public string openid { get; set; } - public string scope { get; set; } - } +namespace Hncore.Wx.Open +{ + public class GetGetWebAccessTokenResponse : ResponseBase + { + public string access_token { get; set; } + public string refresh_token { get; set; } + public string openid { get; set; } + public string scope { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetJsApiTicketResponse.cs b/Infrastructure/WxApi/Response/GetJsApiTicketResponse.cs index a456c9a..f97a26d 100644 --- a/Infrastructure/WxApi/Response/GetJsApiTicketResponse.cs +++ b/Infrastructure/WxApi/Response/GetJsApiTicketResponse.cs @@ -1,13 +1,13 @@ -using Hncore.Wx.Open; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Pass.MsgCenter.WxOpen.Response -{ - public class GetJsApiTicketResponse : ResponseBase - { - public string ticket { get; set; } - } -} +using Hncore.Wx.Open; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Pass.MsgCenter.WxOpen.Response +{ + public class GetJsApiTicketResponse : ResponseBase + { + public string ticket { get; set; } + } +} diff --git a/Infrastructure/WxApi/Response/GetMpAccessTokenResponse.cs b/Infrastructure/WxApi/Response/GetMpAccessTokenResponse.cs index 330c97c..428f3c5 100644 --- a/Infrastructure/WxApi/Response/GetMpAccessTokenResponse.cs +++ b/Infrastructure/WxApi/Response/GetMpAccessTokenResponse.cs @@ -1,12 +1,12 @@ -namespace Hncore.Wx.Open -{ - - //{"access_token":"ACCESS_TOKEN","expires_in":7200} - /// - /// - /// - public class GetAccessTokenResponse : ResponseBase - { - public string access_token { get; set; } - } +namespace Hncore.Wx.Open +{ + + //{"access_token":"ACCESS_TOKEN","expires_in":7200} + /// + /// + /// + public class GetAccessTokenResponse : ResponseBase + { + public string access_token { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetPreAuthCodeResponse.cs b/Infrastructure/WxApi/Response/GetPreAuthCodeResponse.cs index 2f73579..0964104 100644 --- a/Infrastructure/WxApi/Response/GetPreAuthCodeResponse.cs +++ b/Infrastructure/WxApi/Response/GetPreAuthCodeResponse.cs @@ -1,17 +1,17 @@ -namespace Hncore.Wx.Open -{ - - // { - //"pre_auth_code":"Cx_Dk6qiBE0Dmx4EmlT3oRfArPvwSQ-oa3NL_fwHM7VI08r52wazoZX2Rhpz1dEw", - //"expires_in":600 - //} - - - /// - /// - /// - public class GetPreAuthCodeResponse:ResponseBase - { - public string pre_auth_code { get; set; } - } +namespace Hncore.Wx.Open +{ + + // { + //"pre_auth_code":"Cx_Dk6qiBE0Dmx4EmlT3oRfArPvwSQ-oa3NL_fwHM7VI08r52wazoZX2Rhpz1dEw", + //"expires_in":600 + //} + + + /// + /// + /// + public class GetPreAuthCodeResponse:ResponseBase + { + public string pre_auth_code { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetSavedAuthorizerInfoResponse.cs b/Infrastructure/WxApi/Response/GetSavedAuthorizerInfoResponse.cs index 4d827fc..af10dbc 100644 --- a/Infrastructure/WxApi/Response/GetSavedAuthorizerInfoResponse.cs +++ b/Infrastructure/WxApi/Response/GetSavedAuthorizerInfoResponse.cs @@ -1,6 +1,6 @@ -namespace Hncore.Wx.Open -{ - public class GetSavedAuthorizerInfoResponse:ResponseBase - { - } +namespace Hncore.Wx.Open +{ + public class GetSavedAuthorizerInfoResponse:ResponseBase + { + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetTagsResponse.cs b/Infrastructure/WxApi/Response/GetTagsResponse.cs index 3e10a58..857383b 100644 --- a/Infrastructure/WxApi/Response/GetTagsResponse.cs +++ b/Infrastructure/WxApi/Response/GetTagsResponse.cs @@ -1,18 +1,18 @@ -using System.Collections.Generic; - -namespace Hncore.Wx.Open -{ - public class GetTagsResponse : ResponseBase - { - public List tags { get; set; } - } - - public class TagItem - { - public int id { get; set; } - - public string name { get; set; } - - public int count { get; set; } - } +using System.Collections.Generic; + +namespace Hncore.Wx.Open +{ + public class GetTagsResponse : ResponseBase + { + public List tags { get; set; } + } + + public class TagItem + { + public int id { get; set; } + + public string name { get; set; } + + public int count { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetTemplateResponse.cs b/Infrastructure/WxApi/Response/GetTemplateResponse.cs index 92918c5..a4993c3 100644 --- a/Infrastructure/WxApi/Response/GetTemplateResponse.cs +++ b/Infrastructure/WxApi/Response/GetTemplateResponse.cs @@ -1,7 +1,7 @@ -namespace Hncore.Wx.Open -{ - public class GetTemplateResponse : ResponseBase - { - public string template_id { get; set; } - } +namespace Hncore.Wx.Open +{ + public class GetTemplateResponse : ResponseBase + { + public string template_id { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetTempleteListResponse.cs b/Infrastructure/WxApi/Response/GetTempleteListResponse.cs index 36d2442..2a49f54 100644 --- a/Infrastructure/WxApi/Response/GetTempleteListResponse.cs +++ b/Infrastructure/WxApi/Response/GetTempleteListResponse.cs @@ -1,20 +1,20 @@ -using System.Collections.Generic; - -namespace Hncore.Wx.Open -{ - public class GetTempleteListResponse : ResponseBase - { - public List template_list { get; set; } - } - - public class TempleteItem - { - public string template_id { get; set; } - public string title { get; set; } - public string primary_industry { get; set; } - public string deputy_industry { get; set; } - } - -} - - +using System.Collections.Generic; + +namespace Hncore.Wx.Open +{ + public class GetTempleteListResponse : ResponseBase + { + public List template_list { get; set; } + } + + public class TempleteItem + { + public string template_id { get; set; } + public string title { get; set; } + public string primary_industry { get; set; } + public string deputy_industry { get; set; } + } + +} + + diff --git a/Infrastructure/WxApi/Response/GetUserinfoResponse.cs b/Infrastructure/WxApi/Response/GetUserinfoResponse.cs index a2fb437..80e5d4a 100644 --- a/Infrastructure/WxApi/Response/GetUserinfoResponse.cs +++ b/Infrastructure/WxApi/Response/GetUserinfoResponse.cs @@ -1,17 +1,17 @@ -using System.Collections.Generic; - -namespace Hncore.Wx.Open -{ - public class GetUserinfoResponse : ResponseBase - { - public string openid { get; set; } - public string nickname { get; set; } - public string sex { get; set; } - public string province { get; set; } - public string city { get; set; } - public string country { get; set; } - public string headimgurl { get; set; } - public List privilege { get; set; } - public string unionid { get; set; } - } +using System.Collections.Generic; + +namespace Hncore.Wx.Open +{ + public class GetUserinfoResponse : ResponseBase + { + public string openid { get; set; } + public string nickname { get; set; } + public string sex { get; set; } + public string province { get; set; } + public string city { get; set; } + public string country { get; set; } + public string headimgurl { get; set; } + public List privilege { get; set; } + public string unionid { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/GetWechatTicketResponse.cs b/Infrastructure/WxApi/Response/GetWechatTicketResponse.cs index 8ec2cc5..0e666e5 100644 --- a/Infrastructure/WxApi/Response/GetWechatTicketResponse.cs +++ b/Infrastructure/WxApi/Response/GetWechatTicketResponse.cs @@ -1,10 +1,10 @@ -namespace Hncore.Wx.Open -{ - /// - /// - /// -public class GetWechatTicketResponse:ResponseBase -{ - public string Ticket { get; set; } -} +namespace Hncore.Wx.Open +{ + /// + /// + /// +public class GetWechatTicketResponse:ResponseBase +{ + public string Ticket { get; set; } +} } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/RefreshAuthorizerInvokeAccessTokenResponse.cs b/Infrastructure/WxApi/Response/RefreshAuthorizerInvokeAccessTokenResponse.cs index 0a45e81..bf57be5 100644 --- a/Infrastructure/WxApi/Response/RefreshAuthorizerInvokeAccessTokenResponse.cs +++ b/Infrastructure/WxApi/Response/RefreshAuthorizerInvokeAccessTokenResponse.cs @@ -1,17 +1,17 @@ -namespace Hncore.Wx.Open -{ - // { - //"authorizer_access_token": "aaUl5s6kAByLwgV0BhXNuIFFUqfrR8vTATsoSHukcIGqJgrc4KmMJ-JlKoC_-NKCLBvuU1cWPv4vDcLN8Z0pn5I45mpATruU0b51hzeT1f8", - //"expires_in": 7200, - //"authorizer_refresh_token": "BstnRqgTJBXb9N2aJq6L5hzfJwP406tpfahQeLNxX0w" - //} - - /// - /// - /// - public class GetAuthorizerAccessTokenResponse : ResponseBase - { - public string authorizer_access_token { get; set; } - public string authorizer_refresh_token { get; set; } - } +namespace Hncore.Wx.Open +{ + // { + //"authorizer_access_token": "aaUl5s6kAByLwgV0BhXNuIFFUqfrR8vTATsoSHukcIGqJgrc4KmMJ-JlKoC_-NKCLBvuU1cWPv4vDcLN8Z0pn5I45mpATruU0b51hzeT1f8", + //"expires_in": 7200, + //"authorizer_refresh_token": "BstnRqgTJBXb9N2aJq6L5hzfJwP406tpfahQeLNxX0w" + //} + + /// + /// + /// + public class GetAuthorizerAccessTokenResponse : ResponseBase + { + public string authorizer_access_token { get; set; } + public string authorizer_refresh_token { get; set; } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/SaveAuthorizerInfoResponse.cs b/Infrastructure/WxApi/Response/SaveAuthorizerInfoResponse.cs index 1f716e7..f86c257 100644 --- a/Infrastructure/WxApi/Response/SaveAuthorizerInfoResponse.cs +++ b/Infrastructure/WxApi/Response/SaveAuthorizerInfoResponse.cs @@ -1,7 +1,7 @@ -namespace Hncore.Wx.Open -{ - public class SaveAuthorizerInfoResponse : ResponseBase - { - - } +namespace Hncore.Wx.Open +{ + public class SaveAuthorizerInfoResponse : ResponseBase + { + + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/SaveWechatAppMenuResponse.cs b/Infrastructure/WxApi/Response/SaveWechatAppMenuResponse.cs index 887df57..bc94ece 100644 --- a/Infrastructure/WxApi/Response/SaveWechatAppMenuResponse.cs +++ b/Infrastructure/WxApi/Response/SaveWechatAppMenuResponse.cs @@ -1,7 +1,7 @@ -namespace Hncore.Wx.Open -{ - public class SaveWechatAppMenuResponse : ResponseBase - { - - } +namespace Hncore.Wx.Open +{ + public class SaveWechatAppMenuResponse : ResponseBase + { + + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/UpdateWechatTicketResponse.cs b/Infrastructure/WxApi/Response/UpdateWechatTicketResponse.cs index 3a1cff5..fd5152e 100644 --- a/Infrastructure/WxApi/Response/UpdateWechatTicketResponse.cs +++ b/Infrastructure/WxApi/Response/UpdateWechatTicketResponse.cs @@ -1,12 +1,12 @@ -namespace Hncore.Wx.Open -{ - /// - /// - /// - public class UpdateWechatTicketResponse : ResponseBase - { - public UpdateWechatTicketResponse() { - - } - } +namespace Hncore.Wx.Open +{ + /// + /// + /// + public class UpdateWechatTicketResponse : ResponseBase + { + public UpdateWechatTicketResponse() { + + } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Response/UploadMediaResponse.cs b/Infrastructure/WxApi/Response/UploadMediaResponse.cs index b1f972b..f20bf8a 100644 --- a/Infrastructure/WxApi/Response/UploadMediaResponse.cs +++ b/Infrastructure/WxApi/Response/UploadMediaResponse.cs @@ -1,20 +1,20 @@ -namespace Hncore.Wx.Open -{ - public class UploadMediaResponse:ResponseBase - { - public string type { get; set; } - public string media_id { get; set; } - public int created_at { get; set; } - } - public class UploadImageMediaResponse : ResponseBase - { - public string url { get; set; } - public string media_id { get; set; } - } - - - - - - -} +namespace Hncore.Wx.Open +{ + public class UploadMediaResponse:ResponseBase + { + public string type { get; set; } + public string media_id { get; set; } + public int created_at { get; set; } + } + public class UploadImageMediaResponse : ResponseBase + { + public string url { get; set; } + public string media_id { get; set; } + } + + + + + + +} diff --git a/Infrastructure/WxApi/Response/WechatNotificationResponses.cs b/Infrastructure/WxApi/Response/WechatNotificationResponses.cs index c79c524..d941a44 100644 --- a/Infrastructure/WxApi/Response/WechatNotificationResponses.cs +++ b/Infrastructure/WxApi/Response/WechatNotificationResponses.cs @@ -1,22 +1,22 @@ -using System.Collections.Generic; - -namespace Hncore.Wx.Open -{ - public class WechatNotificationTemplate - { - public string template_id { get; set; } - public string title { get; set; } - public string primary_industry { get; set; } - public string deputy_industry { get; set; } - public string content { get; set; } - public string example { get; set; } - } - public class GetAllPrivateTemplateResponse : ResponseBase - { - public List template_list { get; set; } - } - public class WechatAddNotificationTemplateResponse : ResponseBase - { - public string template_id { get; set; } - } -} +using System.Collections.Generic; + +namespace Hncore.Wx.Open +{ + public class WechatNotificationTemplate + { + public string template_id { get; set; } + public string title { get; set; } + public string primary_industry { get; set; } + public string deputy_industry { get; set; } + public string content { get; set; } + public string example { get; set; } + } + public class GetAllPrivateTemplateResponse : ResponseBase + { + public List template_list { get; set; } + } + public class WechatAddNotificationTemplateResponse : ResponseBase + { + public string template_id { get; set; } + } +} diff --git a/Infrastructure/WxApi/Response/WxResponseBase.cs b/Infrastructure/WxApi/Response/WxResponseBase.cs index 408be0d..04dc460 100644 --- a/Infrastructure/WxApi/Response/WxResponseBase.cs +++ b/Infrastructure/WxApi/Response/WxResponseBase.cs @@ -1,23 +1,23 @@ -using Hncore.Infrastructure.Extension; -using System; - -namespace Hncore.Wx.Open -{ - public class ResponseBase - { - public int errcode { get; set; } - public string errmsg { get; set; } - public int expires_in { get; set; } - public long create_from { get; set; } - public virtual bool need_to_refresh_token - { - get - { - var timespan = (DateTime.Now.TimestampFrom19700101() - create_from); - //此处不应该等于0 errcode!=0表明微信返回的token有错误 需要刷新 ==0是正确的 - //提前10分钟过期 - return errcode != 0 || timespan < 0 || timespan > expires_in-10*60; - } - } - } -} +using Hncore.Infrastructure.Extension; +using System; + +namespace Hncore.Wx.Open +{ + public class ResponseBase + { + public int errcode { get; set; } + public string errmsg { get; set; } + public int expires_in { get; set; } + public long create_from { get; set; } + public virtual bool need_to_refresh_token + { + get + { + var timespan = (DateTime.Now.TimestampFrom19700101() - create_from); + //此处不应该等于0 errcode!=0表明微信返回的token有错误 需要刷新 ==0是正确的 + //提前10分钟过期 + return errcode != 0 || timespan < 0 || timespan > expires_in-10*60; + } + } + } +} diff --git a/Infrastructure/WxApi/TemplateMessage/SendResult.cs b/Infrastructure/WxApi/TemplateMessage/SendResult.cs index 6bbf701..87df744 100644 --- a/Infrastructure/WxApi/TemplateMessage/SendResult.cs +++ b/Infrastructure/WxApi/TemplateMessage/SendResult.cs @@ -1,15 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Wx.Open -{ - public class SendResult - { - public int errcode { get; set; } - public string errmsg { get; set; } - public long msgid { get; set; } - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Wx.Open +{ + public class SendResult + { + public int errcode { get; set; } + public string errmsg { get; set; } + public long msgid { get; set; } + + } +} diff --git a/Infrastructure/WxApi/TemplateMessage/TemplateApi.cs b/Infrastructure/WxApi/TemplateMessage/TemplateApi.cs index fef54f8..7ffc85b 100644 --- a/Infrastructure/WxApi/TemplateMessage/TemplateApi.cs +++ b/Infrastructure/WxApi/TemplateMessage/TemplateApi.cs @@ -1,266 +1,266 @@ -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Pass.MsgCenter.Constant; -using Hncore.Pass.MsgCenter.Util; -using Hncore.Wx.Open.Enums; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Hncore.Wx.Open -{ - public static class TemplateApi - { - private static IHttpClientFactory _HttpClientFactory; - - public static void Init(IHttpClientFactory HttpClientFactory) - { - _HttpClientFactory = HttpClientFactory; - } - private static HttpClient GetHttpClient() - { - return _HttpClientFactory.CreateClient("WxOpen"); - } - /// - /// 开放平台或者公众平台或者小程序发送模板消息 - /// - /// 公众号appid - /// 模板id - /// openid ,号分割 - /// 数据 - /// - public static async Task SendTemplateMessageAsync(string appId, string to, ChannelType chennel, TemplateBaseModel tData) - { - var wxAppSecret = WxOpenApi.GetWxAppSecret(appId); - var access_token = ""; - if (!string.IsNullOrWhiteSpace(wxAppSecret))//公众平台或者小程序直接发送模板消息 - { - access_token = await WxOpenApi.GetAccessToken(appId, wxAppSecret); - } - else//开放平台代公众平台发送模板消息 - { - access_token = await WxOpenApi.GetAuthorizerAccessToken(appId); - } - - var tplId =await GetTemplateIdAsync(appId, tData.template_id); - - if (string.IsNullOrWhiteSpace(tplId)) - { - LogHelper.Error("没有找到对应的模板Id", $"appid={appId},tpl={tData.template_id}"); - return new SendResult() - { - errcode = 10001, - errmsg = "没有对应的模板" - }; - } - - tData.template_id = tplId; - var userList = to.Split(','); - foreach (var touser in userList) - { - tData.touser = touser; - var reqData = tData.ToData(); - if (chennel == ChannelType.MP) - { - mp_send(access_token, reqData); - } - else if (chennel == ChannelType.MiniApp) - { - miniapp_send(access_token, reqData); - } - } - return new SendResult(); - } - - /// - /// 发送小程序订阅消息 - /// - /// 小程序appid - /// 模板id - /// openid ,号分割 - /// 数据 - /// - public static async Task SendSubscribeMessageAsync(string appId, string to, TemplateBaseModel tData) - { - var wxAppSecret = WxOpenApi.GetWxAppSecret(appId); - - var access_token = await WxOpenApi.GetAccessToken(appId, wxAppSecret); - - var userList = to.Split(','); - foreach (var touser in userList) - { - tData.touser = touser; - var reqData = tData.ToData(); - miniapp_subscribe_send(access_token, reqData); - } - return new SendResult(); - } - - - /// - /// 根据短模板编号得到对应的模板id - /// - /// - /// - /// - public static async Task GetTemplateIdAsync(string appId, string short_tpl_id) - { - if (!short_tpl_id.StartsWith("OPENTM") && !short_tpl_id.StartsWith("TM")&& !short_tpl_id.StartsWith("ETOR")) - { - return short_tpl_id; - } - var key = string.Format(ConstantConfig.Redis_TempleteId_Key, appId, short_tpl_id); - var templete_id = await RedisHelper.GetAsync(key); - if (!string.IsNullOrWhiteSpace(templete_id)) - { - return templete_id; - } - - var access_token = await WxOpenApi.GetAuthorizerAccessToken(appId); - var ret = await get_tpl(access_token, short_tpl_id); - if (ret.errcode == 0) - { - await RedisHelper.SetAsync(key, ret.template_id); - return ret.template_id; - } - return ""; - } - - public static async Task mp_send(string access_token, object reqData) - { - if (string.IsNullOrWhiteSpace(access_token)) - { - return new SendResult() - { - errcode = 1000, - errmsg = "access_token 不存在" - }; - } - string urlFormat = $"cgi-bin/message/template/send?access_token={access_token}"; - LogHelper.Debug("mp_send_req", reqData.ToJson()); - var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); - var response = respJson.FromJsonToOrDefault(); - if (response.errcode > 0) - { - LogHelper.Error("mp_send_respJson", respJson); - return response; - } - return response; - } - public static async Task miniapp_send(string access_token, object reqData) - { - if (string.IsNullOrWhiteSpace(access_token)) - { - return new SendResult() - { - errcode = 1000, - errmsg = "access_token 不存在" - }; - } - string urlFormat = $"cgi-bin/message/wxopen/template/send?access_token={access_token}"; - LogHelper.Debug("mp_send_req", reqData.ToJson()); - var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); - var response = respJson.FromJsonToOrDefault(); - if (response.errcode > 0) - { - LogHelper.Error("miniapp_send_respJson", respJson); - return response; - } - return response; - } - - //小程序订阅消息 - public static async Task miniapp_subscribe_send(string access_token, object reqData) - { - if (string.IsNullOrWhiteSpace(access_token)) - { - return new SendResult() - { - errcode = 1000, - errmsg = "access_token 不存在" - }; - } - string urlFormat = $"cgi-bin/message/subscribe/send?access_token={access_token}"; - LogHelper.Debug("miniapp_subscribe_send", reqData.ToJson()); - var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); - var response = respJson.FromJsonToOrDefault(); - if (response.errcode > 0) - { - LogHelper.Error("miniapp_subscribe_send", respJson); - return response; - } - return response; - } - - - public static async Task uniform_send(string access_token, object reqData) - { - if (string.IsNullOrWhiteSpace(access_token)) - { - return new SendResult() - { - errcode = 1000, - errmsg = "access_token 不存在" - }; - } - - string urlFormat = $"cgi-bin/message/wxopen/template/uniform_send?access_token={access_token}"; - LogHelper.Debug("SendTemplateMessageAsync_Req", reqData.ToJson()); - var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); - var response = respJson.FromJsonToOrDefault(); - if (response.errcode > 0) - { - LogHelper.Debug("SendTemplateMessageAsync_respJson", respJson); - return response; - } - return new SendResult(); - } - - public static async Task get_tpl(string access_token, string short_tpl_id) - { - if (string.IsNullOrWhiteSpace(access_token)) - { - return new GetTemplateResponse() - { - errcode = 1000, - errmsg = "access_token 不存在" - }; - } - var reqData = new - { - template_id_short = short_tpl_id - }; - string urlFormat = $"cgi-bin/template/api_add_template?access_token={access_token}"; - LogHelper.Debug("get_tpl", reqData.ToJson()); - var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); - var response = respJson.FromJsonToOrDefault(); - if (response.errcode > 0) - { - LogHelper.Error("get_tpl_respJson", respJson); - return response; - } - return response; - } - - private static async Task get_tpl_list(string access_token) - { - if (string.IsNullOrWhiteSpace(access_token)) - { - return new GetTempleteListResponse() - { - errcode = 1000, - errmsg = "access_token 不存在" - }; - } - string urlFormat = $"cgi-bin/template/get_all_private_template?access_token={access_token}"; - var respJson = await GetHttpClient().GetStringAsync(urlFormat); - var response = respJson.FromJsonToOrDefault(); - if (response.errcode > 0) - { - LogHelper.Error("get_tpl_respJson", respJson); - return response; - } - return response; - } - } -} +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Pass.MsgCenter.Constant; +using Hncore.Pass.MsgCenter.Util; +using Hncore.Wx.Open.Enums; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Hncore.Wx.Open +{ + public static class TemplateApi + { + private static IHttpClientFactory _HttpClientFactory; + + public static void Init(IHttpClientFactory HttpClientFactory) + { + _HttpClientFactory = HttpClientFactory; + } + private static HttpClient GetHttpClient() + { + return _HttpClientFactory.CreateClient("WxOpen"); + } + /// + /// 开放平台或者公众平台或者小程序发送模板消息 + /// + /// 公众号appid + /// 模板id + /// openid ,号分割 + /// 数据 + /// + public static async Task SendTemplateMessageAsync(string appId, string to, ChannelType chennel, TemplateBaseModel tData) + { + var wxAppSecret = WxOpenApi.GetWxAppSecret(appId); + var access_token = ""; + if (!string.IsNullOrWhiteSpace(wxAppSecret))//公众平台或者小程序直接发送模板消息 + { + access_token = await WxOpenApi.GetAccessToken(appId, wxAppSecret); + } + else//开放平台代公众平台发送模板消息 + { + access_token = await WxOpenApi.GetAuthorizerAccessToken(appId); + } + + var tplId =await GetTemplateIdAsync(appId, tData.template_id); + + if (string.IsNullOrWhiteSpace(tplId)) + { + LogHelper.Error("没有找到对应的模板Id", $"appid={appId},tpl={tData.template_id}"); + return new SendResult() + { + errcode = 10001, + errmsg = "没有对应的模板" + }; + } + + tData.template_id = tplId; + var userList = to.Split(','); + foreach (var touser in userList) + { + tData.touser = touser; + var reqData = tData.ToData(); + if (chennel == ChannelType.MP) + { + mp_send(access_token, reqData); + } + else if (chennel == ChannelType.MiniApp) + { + miniapp_send(access_token, reqData); + } + } + return new SendResult(); + } + + /// + /// 发送小程序订阅消息 + /// + /// 小程序appid + /// 模板id + /// openid ,号分割 + /// 数据 + /// + public static async Task SendSubscribeMessageAsync(string appId, string to, TemplateBaseModel tData) + { + var wxAppSecret = WxOpenApi.GetWxAppSecret(appId); + + var access_token = await WxOpenApi.GetAccessToken(appId, wxAppSecret); + + var userList = to.Split(','); + foreach (var touser in userList) + { + tData.touser = touser; + var reqData = tData.ToData(); + miniapp_subscribe_send(access_token, reqData); + } + return new SendResult(); + } + + + /// + /// 根据短模板编号得到对应的模板id + /// + /// + /// + /// + public static async Task GetTemplateIdAsync(string appId, string short_tpl_id) + { + if (!short_tpl_id.StartsWith("OPENTM") && !short_tpl_id.StartsWith("TM")&& !short_tpl_id.StartsWith("ETOR")) + { + return short_tpl_id; + } + var key = string.Format(ConstantConfig.Redis_TempleteId_Key, appId, short_tpl_id); + var templete_id = await RedisHelper.GetAsync(key); + if (!string.IsNullOrWhiteSpace(templete_id)) + { + return templete_id; + } + + var access_token = await WxOpenApi.GetAuthorizerAccessToken(appId); + var ret = await get_tpl(access_token, short_tpl_id); + if (ret.errcode == 0) + { + await RedisHelper.SetAsync(key, ret.template_id); + return ret.template_id; + } + return ""; + } + + public static async Task mp_send(string access_token, object reqData) + { + if (string.IsNullOrWhiteSpace(access_token)) + { + return new SendResult() + { + errcode = 1000, + errmsg = "access_token 不存在" + }; + } + string urlFormat = $"cgi-bin/message/template/send?access_token={access_token}"; + LogHelper.Debug("mp_send_req", reqData.ToJson()); + var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); + var response = respJson.FromJsonToOrDefault(); + if (response.errcode > 0) + { + LogHelper.Error("mp_send_respJson", respJson); + return response; + } + return response; + } + public static async Task miniapp_send(string access_token, object reqData) + { + if (string.IsNullOrWhiteSpace(access_token)) + { + return new SendResult() + { + errcode = 1000, + errmsg = "access_token 不存在" + }; + } + string urlFormat = $"cgi-bin/message/wxopen/template/send?access_token={access_token}"; + LogHelper.Debug("mp_send_req", reqData.ToJson()); + var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); + var response = respJson.FromJsonToOrDefault(); + if (response.errcode > 0) + { + LogHelper.Error("miniapp_send_respJson", respJson); + return response; + } + return response; + } + + //小程序订阅消息 + public static async Task miniapp_subscribe_send(string access_token, object reqData) + { + if (string.IsNullOrWhiteSpace(access_token)) + { + return new SendResult() + { + errcode = 1000, + errmsg = "access_token 不存在" + }; + } + string urlFormat = $"cgi-bin/message/subscribe/send?access_token={access_token}"; + LogHelper.Debug("miniapp_subscribe_send", reqData.ToJson()); + var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); + var response = respJson.FromJsonToOrDefault(); + if (response.errcode > 0) + { + LogHelper.Error("miniapp_subscribe_send", respJson); + return response; + } + return response; + } + + + public static async Task uniform_send(string access_token, object reqData) + { + if (string.IsNullOrWhiteSpace(access_token)) + { + return new SendResult() + { + errcode = 1000, + errmsg = "access_token 不存在" + }; + } + + string urlFormat = $"cgi-bin/message/wxopen/template/uniform_send?access_token={access_token}"; + LogHelper.Debug("SendTemplateMessageAsync_Req", reqData.ToJson()); + var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); + var response = respJson.FromJsonToOrDefault(); + if (response.errcode > 0) + { + LogHelper.Debug("SendTemplateMessageAsync_respJson", respJson); + return response; + } + return new SendResult(); + } + + public static async Task get_tpl(string access_token, string short_tpl_id) + { + if (string.IsNullOrWhiteSpace(access_token)) + { + return new GetTemplateResponse() + { + errcode = 1000, + errmsg = "access_token 不存在" + }; + } + var reqData = new + { + template_id_short = short_tpl_id + }; + string urlFormat = $"cgi-bin/template/api_add_template?access_token={access_token}"; + LogHelper.Debug("get_tpl", reqData.ToJson()); + var respJson = await GetHttpClient().PostAsJsonGetString(urlFormat, reqData); + var response = respJson.FromJsonToOrDefault(); + if (response.errcode > 0) + { + LogHelper.Error("get_tpl_respJson", respJson); + return response; + } + return response; + } + + private static async Task get_tpl_list(string access_token) + { + if (string.IsNullOrWhiteSpace(access_token)) + { + return new GetTempleteListResponse() + { + errcode = 1000, + errmsg = "access_token 不存在" + }; + } + string urlFormat = $"cgi-bin/template/get_all_private_template?access_token={access_token}"; + var respJson = await GetHttpClient().GetStringAsync(urlFormat); + var response = respJson.FromJsonToOrDefault(); + if (response.errcode > 0) + { + LogHelper.Error("get_tpl_respJson", respJson); + return response; + } + return response; + } + } +} diff --git a/Infrastructure/WxApi/TemplateMessage/TemplateModel.cs b/Infrastructure/WxApi/TemplateMessage/TemplateModel.cs index 06553b6..159df3b 100644 --- a/Infrastructure/WxApi/TemplateMessage/TemplateModel.cs +++ b/Infrastructure/WxApi/TemplateMessage/TemplateModel.cs @@ -1,129 +1,129 @@ -using System; -using System.Collections.Generic; - -namespace Hncore.Wx.Open -{ - public class TemplateBaseModel - { - public string touser { get; set; } - public string template_id { get; set; } - public List Items { get; set; } = new List(); - - //{ - // "touser":"touser", - // "template_id": "TEMPLATE_ID", - // "data": { - // "keyword1": { - // "value": "339208499" - // }, - // "keyword2": { - // "value": "2015年01月05日 12:30" - // }, - // "keyword3": { - // "value": "腾讯微信总部" - // } , - // "keyword4": { - // "value": "广州市海珠区新港中路397号" - // } - // } - //} - public virtual Dictionary ToData() - { - var data = new Dictionary() { - { "touser",touser}, - {"template_id",template_id} - }; - var index = 1; - var dataItems = new Dictionary() { }; - Items.ForEach(m => - { - dataItems[$"keyword{index}"] = m; - index++; - }); - data["data"] = dataItems; - return data; - } - } - - public class TemplateMPModel: TemplateBaseModel - { - public string Url { get; set; } - public string MiniAppId { get; set; } - public TemplateDataItem first { get; set; } - public TemplateDataItem remark { get; set; } - public override Dictionary ToData() - { - var ret = base.ToData(); - var data = ret ["data"] as Dictionary; - if (first != null) data["first"] = first; - if (remark != null) data["remark"] = remark; - if (!string.IsNullOrWhiteSpace(Url)) ret["url"] = Url; - if (!string.IsNullOrWhiteSpace(MiniAppId)) ret["miniprogram"] = new - { - appid = MiniAppId, - pagepath = Url - }; - return ret; - } - } - - public class TemplateMiniAppModel : TemplateBaseModel - { - public string page { get; set; } - public string form_id { get; set; } - public string emphasis_keyword { get; set; } - public override Dictionary ToData() - { - var ret = base.ToData(); - if (!string.IsNullOrWhiteSpace(page)) ret["page"] = page; - if (!string.IsNullOrWhiteSpace(form_id)) ret["form_id"] = form_id; - if (!string.IsNullOrWhiteSpace(emphasis_keyword)) ret["emphasis_keyword"] = emphasis_keyword; - return ret; - } - } - - public class TemplateDataItem - { - public TemplateDataItem(string v, string c = "#173177") - { - value = v; - color = c; - } - - public string name { get; set; } - public string value { get; set; } - // - // 摘要: - // 16进制颜色代码,如:#FF0000 - public string color { get; set; } - } - - - /// - /// 小程序订阅消息 - /// - public class SubscribeMiniAppModel : TemplateBaseModel - { - public string page { get; set; } - public override Dictionary ToData() - { - var ret = base.ToData(); - - var data = new Dictionary() { - { "touser",touser}, - {"template_id",template_id} - }; - var index = 1; - var dataItems = new Dictionary() { }; - Items.ForEach(m => - { - var num = index.ToString().PadLeft(2, '0'); - dataItems[m.name] = new { value = m.value }; - index++; - }); - data["data"] = dataItems; - if (!string.IsNullOrWhiteSpace(page)) ret["page"] = page; - return data; - } - } -} +using System; +using System.Collections.Generic; + +namespace Hncore.Wx.Open +{ + public class TemplateBaseModel + { + public string touser { get; set; } + public string template_id { get; set; } + public List Items { get; set; } = new List(); + + //{ + // "touser":"touser", + // "template_id": "TEMPLATE_ID", + // "data": { + // "keyword1": { + // "value": "339208499" + // }, + // "keyword2": { + // "value": "2015年01月05日 12:30" + // }, + // "keyword3": { + // "value": "腾讯微信总部" + // } , + // "keyword4": { + // "value": "广州市海珠区新港中路397号" + // } + // } + //} + public virtual Dictionary ToData() + { + var data = new Dictionary() { + { "touser",touser}, + {"template_id",template_id} + }; + var index = 1; + var dataItems = new Dictionary() { }; + Items.ForEach(m => + { + dataItems[$"keyword{index}"] = m; + index++; + }); + data["data"] = dataItems; + return data; + } + } + + public class TemplateMPModel: TemplateBaseModel + { + public string Url { get; set; } + public string MiniAppId { get; set; } + public TemplateDataItem first { get; set; } + public TemplateDataItem remark { get; set; } + public override Dictionary ToData() + { + var ret = base.ToData(); + var data = ret ["data"] as Dictionary; + if (first != null) data["first"] = first; + if (remark != null) data["remark"] = remark; + if (!string.IsNullOrWhiteSpace(Url)) ret["url"] = Url; + if (!string.IsNullOrWhiteSpace(MiniAppId)) ret["miniprogram"] = new + { + appid = MiniAppId, + pagepath = Url + }; + return ret; + } + } + + public class TemplateMiniAppModel : TemplateBaseModel + { + public string page { get; set; } + public string form_id { get; set; } + public string emphasis_keyword { get; set; } + public override Dictionary ToData() + { + var ret = base.ToData(); + if (!string.IsNullOrWhiteSpace(page)) ret["page"] = page; + if (!string.IsNullOrWhiteSpace(form_id)) ret["form_id"] = form_id; + if (!string.IsNullOrWhiteSpace(emphasis_keyword)) ret["emphasis_keyword"] = emphasis_keyword; + return ret; + } + } + + public class TemplateDataItem + { + public TemplateDataItem(string v, string c = "#173177") + { + value = v; + color = c; + } + + public string name { get; set; } + public string value { get; set; } + // + // 摘要: + // 16进制颜色代码,如:#FF0000 + public string color { get; set; } + } + + + /// + /// 小程序订阅消息 + /// + public class SubscribeMiniAppModel : TemplateBaseModel + { + public string page { get; set; } + public override Dictionary ToData() + { + var ret = base.ToData(); + + var data = new Dictionary() { + { "touser",touser}, + {"template_id",template_id} + }; + var index = 1; + var dataItems = new Dictionary() { }; + Items.ForEach(m => + { + var num = index.ToString().PadLeft(2, '0'); + dataItems[m.name] = new { value = m.value }; + index++; + }); + data["data"] = dataItems; + if (!string.IsNullOrWhiteSpace(page)) ret["page"] = page; + return data; + } + } +} diff --git a/Infrastructure/WxApi/Util/Constant.cs b/Infrastructure/WxApi/Util/Constant.cs index 72e4d8b..bb9c611 100644 --- a/Infrastructure/WxApi/Util/Constant.cs +++ b/Infrastructure/WxApi/Util/Constant.cs @@ -1,106 +1,106 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Pass.MsgCenter.Constant -{ - public class ConstantConfig - { - /// - /// 微信开放平台定期推送的ticket 标识 - /// - public const string Redis_Psipwechat_Ticket_Key = "psipwechat_ticket"; - - /// - /// 微信开放平台AccessToken 标识 - /// - public const string Redis_ComponentAccessToken_Key = "WEIXIN:{0}_wx_component_token.json"; - - /// - /// 微信开放平台accessToken 标识{authorizer_appid} - /// - public const string Redis_AuthorizerAccessToken_Key = "WEIXIN:wx_authorizer_token_{0}"; - - /// - /// 微信开放平台refresh_authorizer_token 标识{authorizer_appid} - /// - public const string Redis_Refresh_AuthorizerAccessToken_Key = "WEIXIN:wx_refresh_authorizer_token_{0}"; - - - /// - /// 微信公众平台 mp_access_token 标识{appid} - /// - public const string Redis_MP_AccessToken_Key = "WEIXIN:wx_mp_access_token_{0}"; - - - - /// - /// 微信公众平台 mp_access_token 标识{openid} - /// - public const string Redis_MP_GetUserinfoByWebAccessToken_Key = "WEIXIN:userinfo_by_web_accesstoken_{0}"; - - /// - /// 微信公众平台 mp_access_token 标识{openid} - /// - public const string Redis_MP_GetUserUnionIDInfo_Key = "WEIXIN:userinfo_uniono_info_{0}"; - - /// - /// 微信公众平台 mp_access_token 标识{openid} - /// - public const string Redis_MP_GetUserOpenIdInfo_Key = "WEIXIN:userinfo_openid_info_{0}"; - - - - /// - /// 公众号模板key {appid}_{tplId} - /// - public const string Redis_TempleteId_Key = "WEIXIN:TempleteId_{0}_{1}"; - - /// - /// 公众号的基本信息{公众号appid} - /// - public const string Redis_MP_Info_Key = "WEIXIN:mp_info_{0}"; - - - /// - /// 发送消息公众号模板消息标识 - /// - public const string RabbitMQ_Send_Message_Key = "msgcenter_send_msg_{0}"; - /// - /// 发送消息公众号模板消息标识 - /// - public const string RabbitMQ_Send_Message_MP_Key = "msgcenter_send_msg_1"; - - /// - /// 送消息小程序模板消息标识 - /// - public const string RabbitMQ_Send_Message_MiniApp_Key = "msgcenter_send_msg_2"; - - /// - /// 送消息短信模板消息标识 - /// - public const string RabbitMQ_Send_Message_Sms_Key = "msgcenter_send_msg_3"; - - /// - /// 送消小程序订阅消息标识 - /// - public const string RabbitMQ_Send_Message_Subscribe_Key = "msgcenter_send_msg_4"; - - - public const string RabbitMQ_Send_Message_Test_Key = "msgcenter_send_msg_4"; - - - /// - /// 发email消息 - /// - public const string RabbitMQ_Send_Message_Email = "Etor.PSIP.Msgcenter.SendEmail"; - - /// - /// 公众号模板key {uuid} - /// - public const string Redis_WxScanWait_Key = "WEIXIN:WxScanWait_{0}"; - - public const string Redis_MP_Js_Ticket_Key = "WEIXIN:mp_js_ticket_{0}"; - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Pass.MsgCenter.Constant +{ + public class ConstantConfig + { + /// + /// 微信开放平台定期推送的ticket 标识 + /// + public const string Redis_Psipwechat_Ticket_Key = "psipwechat_ticket"; + + /// + /// 微信开放平台AccessToken 标识 + /// + public const string Redis_ComponentAccessToken_Key = "WEIXIN:{0}_wx_component_token.json"; + + /// + /// 微信开放平台accessToken 标识{authorizer_appid} + /// + public const string Redis_AuthorizerAccessToken_Key = "WEIXIN:wx_authorizer_token_{0}"; + + /// + /// 微信开放平台refresh_authorizer_token 标识{authorizer_appid} + /// + public const string Redis_Refresh_AuthorizerAccessToken_Key = "WEIXIN:wx_refresh_authorizer_token_{0}"; + + + /// + /// 微信公众平台 mp_access_token 标识{appid} + /// + public const string Redis_MP_AccessToken_Key = "WEIXIN:wx_mp_access_token_{0}"; + + + + /// + /// 微信公众平台 mp_access_token 标识{openid} + /// + public const string Redis_MP_GetUserinfoByWebAccessToken_Key = "WEIXIN:userinfo_by_web_accesstoken_{0}"; + + /// + /// 微信公众平台 mp_access_token 标识{openid} + /// + public const string Redis_MP_GetUserUnionIDInfo_Key = "WEIXIN:userinfo_uniono_info_{0}"; + + /// + /// 微信公众平台 mp_access_token 标识{openid} + /// + public const string Redis_MP_GetUserOpenIdInfo_Key = "WEIXIN:userinfo_openid_info_{0}"; + + + + /// + /// 公众号模板key {appid}_{tplId} + /// + public const string Redis_TempleteId_Key = "WEIXIN:TempleteId_{0}_{1}"; + + /// + /// 公众号的基本信息{公众号appid} + /// + public const string Redis_MP_Info_Key = "WEIXIN:mp_info_{0}"; + + + /// + /// 发送消息公众号模板消息标识 + /// + public const string RabbitMQ_Send_Message_Key = "msgcenter_send_msg_{0}"; + /// + /// 发送消息公众号模板消息标识 + /// + public const string RabbitMQ_Send_Message_MP_Key = "msgcenter_send_msg_1"; + + /// + /// 送消息小程序模板消息标识 + /// + public const string RabbitMQ_Send_Message_MiniApp_Key = "msgcenter_send_msg_2"; + + /// + /// 送消息短信模板消息标识 + /// + public const string RabbitMQ_Send_Message_Sms_Key = "msgcenter_send_msg_3"; + + /// + /// 送消小程序订阅消息标识 + /// + public const string RabbitMQ_Send_Message_Subscribe_Key = "msgcenter_send_msg_4"; + + + public const string RabbitMQ_Send_Message_Test_Key = "msgcenter_send_msg_4"; + + + /// + /// 发email消息 + /// + public const string RabbitMQ_Send_Message_Email = "Etor.PSIP.Msgcenter.SendEmail"; + + /// + /// 公众号模板key {uuid} + /// + public const string Redis_WxScanWait_Key = "WEIXIN:WxScanWait_{0}"; + + public const string Redis_MP_Js_Ticket_Key = "WEIXIN:mp_js_ticket_{0}"; + } +} diff --git a/Infrastructure/WxApi/Util/HostContext.cs b/Infrastructure/WxApi/Util/HostContext.cs index dca9235..cbdaad2 100644 --- a/Infrastructure/WxApi/Util/HostContext.cs +++ b/Infrastructure/WxApi/Util/HostContext.cs @@ -1,18 +1,18 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Hncore.Pass.MsgCenter.Util -{ - public class HostContext - { - public static IConfiguration Configuration { get; set; } - - public static IHttpClientFactory HttpClientFactory { get; set; } - } -} +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Hncore.Pass.MsgCenter.Util +{ + public class HostContext + { + public static IConfiguration Configuration { get; set; } + + public static IHttpClientFactory HttpClientFactory { get; set; } + } +} diff --git a/Infrastructure/WxApi/Util/ServiceContext.cs b/Infrastructure/WxApi/Util/ServiceContext.cs index 86756a7..0982413 100644 --- a/Infrastructure/WxApi/Util/ServiceContext.cs +++ b/Infrastructure/WxApi/Util/ServiceContext.cs @@ -1,33 +1,33 @@ -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace Hncore.Pass.MsgCenter.Util -{ - public class ServiceContext - { - private static IServiceProvider _serviceProvider; - public static void Initialize(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - /// - /// 构建实例 - /// - /// - /// - public static T Resolve() where T : class - { - return _serviceProvider.GetService(); - } - /// - /// 构建类型 - /// - /// - /// - public static object Resolve(Type type) - { - return _serviceProvider.GetService(type); - } - } +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace Hncore.Pass.MsgCenter.Util +{ + public class ServiceContext + { + private static IServiceProvider _serviceProvider; + public static void Initialize(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + /// + /// 构建实例 + /// + /// + /// + public static T Resolve() where T : class + { + return _serviceProvider.GetService(); + } + /// + /// 构建类型 + /// + /// + /// + public static object Resolve(Type type) + { + return _serviceProvider.GetService(type); + } + } } \ No newline at end of file diff --git a/Infrastructure/WxApi/Util/UrlHelper.cs b/Infrastructure/WxApi/Util/UrlHelper.cs index b25a406..dc2c685 100644 --- a/Infrastructure/WxApi/Util/UrlHelper.cs +++ b/Infrastructure/WxApi/Util/UrlHelper.cs @@ -1,47 +1,47 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Hncore.Pass.MsgCenter.Util -{ - public class UrlHelper - { - public static UrlMethodModel ParseUrl(string data) - { - var index = data.IndexOf('/'); - if (index != -1) - { - data = data.Substring(index + 1); - } - UrlMethodModel model = new UrlMethodModel(); - var token = data.Split('?'); - if (token.Length > 0) - { - model.Method = token[0]; - } - if (token.Length > 1) - { - var kvs = token[1].Split('&'); - foreach (var item in kvs) - { - var kv = item.Split('='); - if (kv.Length > 1) - { - var key = kv[0].ToLower(); - var value = kv[1]; - model.Args[key] = value; - } - } - } - return model; - } - } - - public class UrlMethodModel - { - public string Method { get; set; } = ""; - - public Dictionary Args { get; set; } = new Dictionary(); - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Hncore.Pass.MsgCenter.Util +{ + public class UrlHelper + { + public static UrlMethodModel ParseUrl(string data) + { + var index = data.IndexOf('/'); + if (index != -1) + { + data = data.Substring(index + 1); + } + UrlMethodModel model = new UrlMethodModel(); + var token = data.Split('?'); + if (token.Length > 0) + { + model.Method = token[0]; + } + if (token.Length > 1) + { + var kvs = token[1].Split('&'); + foreach (var item in kvs) + { + var kv = item.Split('='); + if (kv.Length > 1) + { + var key = kv[0].ToLower(); + var value = kv[1]; + model.Args[key] = value; + } + } + } + return model; + } + } + + public class UrlMethodModel + { + public string Method { get; set; } = ""; + + public Dictionary Args { get; set; } = new Dictionary(); + } +} diff --git a/Infrastructure/WxApi/WxApi.csproj b/Infrastructure/WxApi/WxApi.csproj index 83ea249..fa98788 100644 --- a/Infrastructure/WxApi/WxApi.csproj +++ b/Infrastructure/WxApi/WxApi.csproj @@ -1,15 +1,15 @@ - - - - netstandard2.0 - - - - - - - - - - - + + + + netstandard2.0 + + + + + + + + + + + diff --git a/Infrastructure/WxApi/WxApiExt.cs b/Infrastructure/WxApi/WxApiExt.cs index d75995e..153f1cb 100644 --- a/Infrastructure/WxApi/WxApiExt.cs +++ b/Infrastructure/WxApi/WxApiExt.cs @@ -1,30 +1,30 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Net.Http; - -namespace Hncore.Wx.Open -{ - public static class WxApiExt - { - - public static void AddWxApi(this IServiceCollection services) - { - services.AddHttpClient("WxOpen", client => - { - client.BaseAddress = new Uri("https://api.weixin.qq.com/"); - client.Timeout = TimeSpan.FromSeconds(10); - }); - } - - public static void UseWxApi(this IApplicationBuilder app) - { - var serviceProvider = app.ApplicationServices; - var configuration = serviceProvider.GetService(); - var httpFactory = serviceProvider.GetService(); - WxOpenApi.Init(configuration, httpFactory); - TemplateApi.Init(httpFactory); - } - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Net.Http; + +namespace Hncore.Wx.Open +{ + public static class WxApiExt + { + + public static void AddWxApi(this IServiceCollection services) + { + services.AddHttpClient("WxOpen", client => + { + client.BaseAddress = new Uri("https://api.weixin.qq.com/"); + client.Timeout = TimeSpan.FromSeconds(10); + }); + } + + public static void UseWxApi(this IApplicationBuilder app) + { + var serviceProvider = app.ApplicationServices; + var configuration = serviceProvider.GetService(); + var httpFactory = serviceProvider.GetService(); + WxOpenApi.Init(configuration, httpFactory); + TemplateApi.Init(httpFactory); + } + } +} diff --git a/Infrastructure/WxApi/WxOpenApi.cs b/Infrastructure/WxApi/WxOpenApi.cs index 9953aa2..ec1e7c7 100644 --- a/Infrastructure/WxApi/WxOpenApi.cs +++ b/Infrastructure/WxApi/WxOpenApi.cs @@ -1,759 +1,759 @@ -using Hncore.Infrastructure.Common; -using Hncore.Infrastructure.Extension; -using Hncore.Infrastructure.Serializer; -using Hncore.Pass.MsgCenter.Constant; -using Hncore.Pass.MsgCenter.Util; -using Hncore.Pass.MsgCenter.WxOpen.Response; -using Microsoft.EntityFrameworkCore.Internal; -using Microsoft.Extensions.Configuration; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using System.Web; - -namespace Hncore.Wx.Open -{ - public class WxOpenApi - { - private static readonly AsyncLock _mutex1 = new AsyncLock(); - private static readonly AsyncLock _mutex2 = new AsyncLock(); - private static readonly AsyncLock _mutex4 = new AsyncLock(); - - private static Dictionary WxAppMap = new Dictionary(); - - private static IHttpClientFactory _HttpClientFactory; - - public static void Init(IConfiguration config, IHttpClientFactory HttpClientFactory) - { - _HttpClientFactory = HttpClientFactory; - var key = config[$"WxApps:AppID"]; - WxAppMap[key] = config[$"WxApps:AppSecret"]; - //var WxMpApps = config.GetSection("WxApps"); - //var nodes = WxMpApps.GetChildren(); - //for (var i = 0; i < nodes.Count(); i++) - //{ - // var key = config[$"WxApps:{i}:AppID"]; - // var value = config[$"WxApps:{i}:AppSecret"]; - // if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) - // WxAppMap[key] = value; - //} - } - - public static string GetWxAppSecret(string appid) - { - if (WxAppMap.ContainsKey(appid)) - return WxAppMap[appid]; - else return ""; - } - - private static HttpClient GetHttpClient() - { - return _HttpClientFactory.CreateClient("WxOpen"); - } - public static async Task GetComponentTicket() - { - return await RedisHelper.GetAsync(ConstantConfig.Redis_Psipwechat_Ticket_Key); - } - - public static async Task SetComponentTicket(string ticket) - { - return await RedisHelper.SetAsync(ConstantConfig.Redis_Psipwechat_Ticket_Key, ticket); - } - - /// - /// 开放平台:得到开放平台的accessToken - /// - /// - public static async Task GetComponentAccessToken() - { - var component_appid = HostContext.Configuration["WxOpen:AppID"]; - var key =string.Format(ConstantConfig.Redis_ComponentAccessToken_Key, component_appid); - var tokenJson = await RedisHelper.GetAsync(key); - var token = tokenJson.FromJsonToOrDefault(); - if (token == null || token.need_to_refresh_token) - { - using (await _mutex1.LockAsync()) - { - tokenJson = await RedisHelper.GetAsync(key); - token = tokenJson.FromJsonToOrDefault(); - if (token != null && !token.need_to_refresh_token) - { - return token.component_access_token; - } - //重新去微信获取 - var ticket =await GetComponentTicket(); - token = await get_component_access_token(ticket); - if (token.errcode == 0) - { - await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); - return token.component_access_token; - } - else - { - return ""; - } - } - } - return token.component_access_token; - } - - /// - /// 开放平台:得到公众平台的AccessToken - /// - /// - public static async Task GetAuthorizerAccessToken(string authorizer_appid) - { - var key = string.Format(ConstantConfig.Redis_AuthorizerAccessToken_Key, authorizer_appid); - var refresh_key = string.Format(ConstantConfig.Redis_Refresh_AuthorizerAccessToken_Key, authorizer_appid); - var tokenJson = await RedisHelper.GetAsync(key); - var token = tokenJson.FromJsonToOrDefault(); - if (token == null || token.need_to_refresh_token) - { - using (await _mutex2.LockAsync()) - { - tokenJson = await RedisHelper.GetAsync(key); - token = tokenJson.FromJsonToOrDefault(); - if (token != null && !token.need_to_refresh_token) - { - return token.authorizer_access_token; - } - - var authorizer_refresh_token = await RedisHelper.GetAsync(refresh_key); - var component_access_token = await GetComponentAccessToken(); - token = await get_authorizer_access_token_by_refresh(authorizer_appid, authorizer_refresh_token, component_access_token); - if (token.errcode == 0) - { - await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); - await RedisHelper.SetAsync(refresh_key, token.authorizer_refresh_token,60*60*24*30); - return token.authorizer_access_token; - } - else - { - return ""; - } - } - } - return token.authorizer_access_token; - } - - public static async Task ReFreshAccessToken(string authorizer_appid, string component_access_token) - { - var key = string.Format(ConstantConfig.Redis_AuthorizerAccessToken_Key, authorizer_appid); - var refresh_key = string.Format(ConstantConfig.Redis_Refresh_AuthorizerAccessToken_Key, authorizer_appid); - var authorizer_refresh_token = await RedisHelper.GetAsync(refresh_key); - using (await _mutex4.LockAsync()) - { - var token = await get_authorizer_access_token_by_refresh(authorizer_appid, authorizer_refresh_token, component_access_token); - if (token.errcode == 0) - { - await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); - await RedisHelper.SetAsync(refresh_key, token.authorizer_refresh_token,60 * 60 * 24 * 30); - } - } - } - - /// - ///开放平台: 通过回调返回授权码的方式获得公众号的accesstoken - /// - /// - /// - public static async Task GetAuthorizerAccessTokenByCode(string authorization_code,Func> callback) - { - var component_access_token = await GetComponentAccessToken(); - var ret = await get_authorizer_access_token_by_callback(authorization_code, component_access_token); - var authorizer_appid = ret.authorization_info.authorizer_appid; - //存access_token的key - var key = string.Format(ConstantConfig.Redis_AuthorizerAccessToken_Key, authorizer_appid); - - //存refresh_access_token的key - var refresh_key = string.Format(ConstantConfig.Redis_Refresh_AuthorizerAccessToken_Key, authorizer_appid); - - var info = new GetAuthorizerAccessTokenResponse() - { - authorizer_access_token = ret.authorization_info.authorizer_access_token, - authorizer_refresh_token = ret.authorization_info.authorizer_refresh_token, - expires_in = ret.authorization_info.expires_in, - create_from = DateTime.Now.TimestampFrom19700101() - }; - - await RedisHelper.SetAsync(key, info.ToJson(), info.expires_in); - await RedisHelper.SetAsync(refresh_key, info.authorizer_refresh_token); - - if (callback != null) - { - return await callback(ret.authorization_info); - } - return info.authorizer_access_token; - } - - /// - ///开放平台: 得到开放平台的预授权码 - /// - /// - public static async Task GetPreAuthCode() - { - var component_access_token = await GetComponentAccessToken(); - var ret = await get_pre_auth_Code(component_access_token); - return ret.pre_auth_code; - } - - - /// - ///开放平台: 代公众号获取用户在公众号的OpenId,以及AccessToken 信息 - /// - /// - /// 结果对象,里边包含了授权令牌信息 - public static async Task GetAuthorizerClientUserOpenIdInfo(string appid, string code) - { - var component_access_token = await GetComponentAccessToken(); - var ret = await get_authorizer_user_openIdifno(appid, code, component_access_token); - return ret; - } - - /// - /// 开放平台:为公众平台授权的url - /// - /// - public static async Task GetAuthorizationUrl(string callback_url) - { - var appID = HostContext.Configuration["WxOpen:AppID"]; ; - var pre_auth_code = await GetPreAuthCode(); - var baseUrl = HostContext.Configuration["BaseInfoUrl"]; - callback_url = $"{baseUrl}/{callback_url}";//回调地址 - - callback_url = HttpUtility.UrlEncode(callback_url); - //开放平台授权地址 - var url = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?" + - $"component_appid={appID}" + - $"&pre_auth_code={pre_auth_code}" + - $"&redirect_uri={callback_url}"; - return url; - } - - private static readonly AsyncLock _mutex3 = new AsyncLock(); - /// - /// 【公众平台、小程序】:得到公众平台、小程序的accessToken - /// - /// - public static async Task GetAccessToken(string appid, string secret) - { - var key = string.Format(ConstantConfig.Redis_MP_AccessToken_Key, appid); - var tokenJson = await RedisHelper.GetAsync(key); - var token = tokenJson.FromJsonToOrDefault(); - if (token == null || token.need_to_refresh_token) - { - using (await _mutex3.LockAsync()) - { - tokenJson = await RedisHelper.GetAsync(key); - token = tokenJson.FromJsonToOrDefault(); - if (token != null && !token.need_to_refresh_token) - { - return token.access_token; - } - token = await get_access_token(appid, secret); - if (token.errcode == 0) - { - await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); - return token.access_token; - } - return ""; - } - } - return token.access_token; - } - /// - /// 公众平台,通过code换取网页授权access_token - /// - /// - /// - /// - public static async Task GetWebAccessToken(string appid, string code) - { - var secret = GetWxAppSecret(appid); - if (string.IsNullOrWhiteSpace(secret)) - { - LogHelper.Error("GetWebAccessToken", $"appid={appid} 没有配置"); - return null; - } - return await get_web_access_token(appid, secret, code); - - } - /// - /// 通过access_token和openid拉取用户信息 - /// - /// - /// - /// - public static async Task GetUserinfoByWebAccessToken(string access_token, string openid) - { - var key = string.Format(ConstantConfig.Redis_MP_GetUserinfoByWebAccessToken_Key, openid); - var userInfo = await RedisHelper.GetAsync(key); - - if (userInfo == null) - { - userInfo = await get_userinfo_by_web_access_token(access_token, openid); - if (userInfo.errcode == 0) - await RedisHelper.SetAsync(key, userInfo.ToJson(), 2 * 60 * 60); - } - return userInfo; - } - - /// - /// 开放平台获得获取用户基本信息(UnionID机制) - /// - /// - /// - /// - public static async Task GetUserUnionIDinfo(string appid, string openid) - { - var key = string.Format(ConstantConfig.Redis_MP_GetUserUnionIDInfo_Key, openid); - var userInfo = await RedisHelper.GetAsync(key); - - if (userInfo == null) - { - var access_token = await GetAuthorizerAccessToken(appid); - userInfo = await get_user_openid_info(access_token, openid); - if (userInfo.errcode == 0) - await RedisHelper.SetAsync(key, userInfo.ToJson(), 2 * 60 * 60); - } - return userInfo; - } - - /// - /// 公众平台获得获取用户基本信息() - /// - /// - /// - /// - public static async Task GetUserinfoByOpenId(string appid, string openid) - { - var key = string.Format(ConstantConfig.Redis_MP_GetUserOpenIdInfo_Key, openid); - var userInfo = await RedisHelper.GetAsync(key); - - if (userInfo == null) - { - var access_token = await GetAccessToken(appid,GetWxAppSecret(appid)); - userInfo = await get_user_openid_info(access_token, openid); - if (userInfo.errcode == 0) - await RedisHelper.SetAsync(key, userInfo.ToJson(), 2 * 60 * 60); - } - return userInfo; - } - - /// - /// 通过开放平台获得公众号的信息 - /// - /// - /// - /// - public static async Task GetMpInfo(string authorizer_appid) - { - var key = string.Format(ConstantConfig.Redis_MP_Info_Key, authorizer_appid); - var mpInfo = await RedisHelper.GetAsync(key); - - if (mpInfo == null) - { - var component_token = await GetComponentAccessToken(); - mpInfo = await get_mp_info(component_token, authorizer_appid); - if (mpInfo.errcode == 0) - await RedisHelper.SetAsync(key, mpInfo.ToJson(), 1 * 60 * 60); - } - return mpInfo.authorizer_info; - } - - /// - /// 为公众平台创建菜单 - /// - /// - /// - public static async Task CreateMPMenu(string access_token, object menu) - { - var ret = await create_mp_menu(access_token, menu); - return ret.errcode == 0; - } - - /// - /// 为公众平台粉丝添加标签 - /// - /// - /// - public static async Task AddTag(string appid, List openIds, string tagId) - { - var access_token = await GetAuthorizerAccessToken(appid); - await add_tag(access_token, openIds, int.Parse(tagId)); - } - - /// - /// 得到公众平台所有标签 - /// - /// - /// - public static async Task GetTags(string appid) - { - var access_token = await GetAuthorizerAccessToken(appid); - return await get_tags(access_token); - } - - /// - /// 得到公众平台jsapi ticket - /// - /// - /// - public static async Task GetJsTicket(string authorizer_appid) - { - var key = string.Format(ConstantConfig.Redis_MP_Js_Ticket_Key, authorizer_appid); - var ticketInfo = await RedisHelper.GetAsync(key); - if (ticketInfo == null) - { - var access_token = await GetAuthorizerAccessToken(authorizer_appid); - ticketInfo = await get_jsapi_ticket(access_token); - if (ticketInfo.errcode == 0) - await RedisHelper.SetAsync(key, ticketInfo.ToJson(), 1 * 60 * 60); - } - return ticketInfo; - } - - /// - /// 为公众平台创建二维码 - /// - /// - /// - public static async Task CreatePermanentQrcode(string appid, string scene_str) - { - var access_token = await GetAuthorizerAccessToken(appid); - return await create_permanent_qrcode(access_token, scene_str); - } - - #region 内部微信接口 - - /// - /// 开放平台:得到access_token - /// - /// - /// - private static async Task get_component_access_token(string ticket) - { - string url = "cgi-bin/component/api_component_token"; - - var reqData = new - { - component_appid = HostContext.Configuration["WxOpen:AppID"], - component_appsecret = HostContext.Configuration["WxOpen:AppSecret"], - component_verify_ticket = ticket, - }; - var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); - var ret = respData.FromJsonTo(); - if (ret.errcode > 0) - { - LogHelper.Error("get_component_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - ret.create_from = DateTime.Now.TimestampFrom19700101(); - LogHelper.Debug("get_component_access_token", respData); - return ret; - } - - - /// - ///开放平台:通过刷新的方式获得公众平台的accesstoken - /// - /// - /// - private static async Task get_authorizer_access_token_by_refresh(string authorizer_appid, string authorizer_refresh_token,string component_access_token) - { - string url = $"cgi-bin/component/api_authorizer_token?component_access_token={component_access_token}"; - var reqData = new - { - component_appid = HostContext.Configuration["WxOpen:AppID"], - authorizer_appid, - authorizer_refresh_token, - }; - - var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_authorizer_access_token_by_refresh", $"errcode={ret.errcode},errmsg={ret.errmsg},authorizer_appid={authorizer_appid}"); - } - ret.create_from = DateTime.Now.TimestampFrom19700101(); - LogHelper.Debug("get_authorizer_access_token_by_callback", respData); - return ret; - } - - /// - ///开放平台: 通过授权码回调方式获得公众平台的accesstoken - /// - /// - /// - private static async Task get_authorizer_access_token_by_callback(string authorization_code,string component_access_token) - { - string url = $"cgi-bin/component/api_query_auth?component_access_token={component_access_token}"; - var reqData = new - { - component_appid = HostContext.Configuration["WxOpen:AppID"], - authorization_code, - }; - var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_authorizer_access_token_by_callback", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - ret.create_from = DateTime.Now.TimestampFrom19700101(); - LogHelper.Debug("get_authorizer_access_token_by_callback", respData); - return ret; - } - - /// - ///开放平台: 获取开放平台预授权代码 用于生成授权url - /// - private static async Task get_pre_auth_Code(string component_access_token) - { - string url = $"cgi-bin/component/api_create_preauthcode?component_access_token={component_access_token}"; - var reqData = new - { - component_appid = HostContext.Configuration["WxOpen:AppID"], - }; - - var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_pre_auth_Code", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - ret.create_from = DateTime.Now.TimestampFrom19700101(); - LogHelper.Debug("get_pre_auth_Code", respData); - return ret; - } - - /// - /// 开放平台:获得公众平台用户openid信息 - /// - /// - /// - /// - /// - private static async Task get_authorizer_user_openIdifno(string appid, string code, string component_access_token) - { - LogHelper.Debug("get_authorizer_user_openIdifno_params", $"{appid},{code}"); - var component_appid = HostContext.Configuration["WxOpen:AppID"]; - string url = $"sns/oauth2/component/access_token?appid={appid}&code={code}&grant_type=authorization_code&component_appid={component_appid}&component_access_token={component_access_token}"; - - LogHelper.Debug("get_authorizer_user_openIdifno-url", url); - var resp = await GetHttpClient().GetStringAsync(url); - var respData = resp.FromJsonToOrDefault(); - LogHelper.Debug("get_authorizer_user_openIdifno", resp); - return respData; - } - - /// - ///公众平台,小程序: 直接获得公众平台,小程序的accesstoken - /// - /// - /// - private static async Task get_access_token(string appid, string secret) - { - string url = $"cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}"; - - var respData = await GetHttpClient().GetStringAsync(url); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_mp_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - ret.create_from = DateTime.Now.TimestampFrom19700101(); - LogHelper.Debug("get_mp_access_token", respData); - return ret; - } - - /// - ///公众平台,通过code换取网页授权access_token - ///这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同 - ///https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 - /// - /// - /// - private static async Task get_web_access_token(string appid, string secret,string code) - { - string url = $"sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code"; - var respData = await GetHttpClient().GetStringAsync(url); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_web_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - ret.create_from = DateTime.Now.TimestampFrom19700101(); - LogHelper.Debug("get_web_access_token", respData); - return ret; - } - - /// - /// 如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。 - /// https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 - /// - /// 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 - /// - /// - private static async Task get_userinfo_by_web_access_token(string access_token, string openid) - { - string url = $"sns/userinfo?access_token={access_token}&openid={openid}&lang=zh_CN"; - var respData = await GetHttpClient().GetStringAsync(url); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_userinfo_by_web_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("get_userinfo_by_web_access_token", respData); - return ret; - } - - private static async Task get_user_openid_info(string access_token, string openid) - { - string url = $"cgi-bin/user/info?access_token={access_token}&openid={openid}&lang=zh_CN"; - var respData = await GetHttpClient().GetStringAsync(url); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_user_openid_info", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("get_user_openid_info", respData); - return ret; - } - - - /// - /// 通过开放平台得到公众号的信息,主要是 (authorizer_info) - /// - /// - /// - /// - private static async Task get_mp_info(string component_token, string authorizer_appid) - { - string url = $"cgi-bin/component/api_get_authorizer_info?component_access_token={component_token}"; - - var reqData = new - { - authorizer_appid, - component_appid = HostContext.Configuration["WxOpen:AppID"], - }; - var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_mp_info_resp", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("get_mp_info_resp", respData); - return ret; - } - - /// - /// 为公众平台创建菜单 - /// - /// - /// - /// - private static async Task create_mp_menu(string access_token, object menu) - { - string url = $"cgi-bin/menu/create?access_token={access_token}"; - var respData = await GetHttpClient().PostAsJsonGetString(url, menu); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("create_mp_menu", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("create_mp_menu", respData); - return ret; - } - - /// - /// 为公众号粉丝打标签 - /// - /// - /// - /// - private static async Task add_tag(string access_token, List openIds,int tagId) - { - string url = $"cgi-bin/tags/members/batchtagging?access_token={access_token}"; - - var reqData = new - { - openid_list = openIds, - tagid = tagId - }; - var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("add_tag", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("add_tag", respData); - return ret; - } - - /// - /// 创建公众号二维码 - /// - /// - /// - /// - private static async Task get_tags(string access_token) - { - string url = $"cgi-bin/tags/get?access_token={access_token}"; - var respData = await GetHttpClient().GetStringAsync(url); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_tags", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("get_tags", respData); - return ret; - } - - /// - /// 创建公众号二维码 - /// - /// - /// - /// - private static async Task create_permanent_qrcode(string access_token,string scene_str) - { - string url = $"cgi-bin/qrcode/create?access_token={access_token}"; - //{ "action_name": "QR_LIMIT_STR_SCENE", "action_info": { "scene": { "scene_str": "test"} } } - var reqData = new - { - action_name = "QR_LIMIT_STR_SCENE", - action_info = new - { - scene = new - { - scene_str - } - } - }; - var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("create_permanent_qrcode", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("create_permanent_qrcode", respData); - return ret; - } - - public static async Task get_jsapi_ticket(string access_token) - { - string url = $"cgi-bin/ticket/getticket?access_token={access_token}&type=jsapi"; - var respData = await GetHttpClient().GetStringAsync(url); - var ret = respData.FromJsonToOrDefault(); - if (ret.errcode > 0) - { - LogHelper.Error("get_jsapi_ticket", $"errcode={ret.errcode},errmsg={ret.errmsg}"); - } - LogHelper.Debug("get_jsapi_ticket", respData); - return ret; - } - - #endregion - } -} +using Hncore.Infrastructure.Common; +using Hncore.Infrastructure.Extension; +using Hncore.Infrastructure.Serializer; +using Hncore.Pass.MsgCenter.Constant; +using Hncore.Pass.MsgCenter.Util; +using Hncore.Pass.MsgCenter.WxOpen.Response; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web; + +namespace Hncore.Wx.Open +{ + public class WxOpenApi + { + private static readonly AsyncLock _mutex1 = new AsyncLock(); + private static readonly AsyncLock _mutex2 = new AsyncLock(); + private static readonly AsyncLock _mutex4 = new AsyncLock(); + + private static Dictionary WxAppMap = new Dictionary(); + + private static IHttpClientFactory _HttpClientFactory; + + public static void Init(IConfiguration config, IHttpClientFactory HttpClientFactory) + { + _HttpClientFactory = HttpClientFactory; + var key = config[$"WxApps:AppID"]; + WxAppMap[key] = config[$"WxApps:AppSecret"]; + //var WxMpApps = config.GetSection("WxApps"); + //var nodes = WxMpApps.GetChildren(); + //for (var i = 0; i < nodes.Count(); i++) + //{ + // var key = config[$"WxApps:{i}:AppID"]; + // var value = config[$"WxApps:{i}:AppSecret"]; + // if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) + // WxAppMap[key] = value; + //} + } + + public static string GetWxAppSecret(string appid) + { + if (WxAppMap.ContainsKey(appid)) + return WxAppMap[appid]; + else return ""; + } + + private static HttpClient GetHttpClient() + { + return _HttpClientFactory.CreateClient("WxOpen"); + } + public static async Task GetComponentTicket() + { + return await RedisHelper.GetAsync(ConstantConfig.Redis_Psipwechat_Ticket_Key); + } + + public static async Task SetComponentTicket(string ticket) + { + return await RedisHelper.SetAsync(ConstantConfig.Redis_Psipwechat_Ticket_Key, ticket); + } + + /// + /// 开放平台:得到开放平台的accessToken + /// + /// + public static async Task GetComponentAccessToken() + { + var component_appid = HostContext.Configuration["WxOpen:AppID"]; + var key =string.Format(ConstantConfig.Redis_ComponentAccessToken_Key, component_appid); + var tokenJson = await RedisHelper.GetAsync(key); + var token = tokenJson.FromJsonToOrDefault(); + if (token == null || token.need_to_refresh_token) + { + using (await _mutex1.LockAsync()) + { + tokenJson = await RedisHelper.GetAsync(key); + token = tokenJson.FromJsonToOrDefault(); + if (token != null && !token.need_to_refresh_token) + { + return token.component_access_token; + } + //重新去微信获取 + var ticket =await GetComponentTicket(); + token = await get_component_access_token(ticket); + if (token.errcode == 0) + { + await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); + return token.component_access_token; + } + else + { + return ""; + } + } + } + return token.component_access_token; + } + + /// + /// 开放平台:得到公众平台的AccessToken + /// + /// + public static async Task GetAuthorizerAccessToken(string authorizer_appid) + { + var key = string.Format(ConstantConfig.Redis_AuthorizerAccessToken_Key, authorizer_appid); + var refresh_key = string.Format(ConstantConfig.Redis_Refresh_AuthorizerAccessToken_Key, authorizer_appid); + var tokenJson = await RedisHelper.GetAsync(key); + var token = tokenJson.FromJsonToOrDefault(); + if (token == null || token.need_to_refresh_token) + { + using (await _mutex2.LockAsync()) + { + tokenJson = await RedisHelper.GetAsync(key); + token = tokenJson.FromJsonToOrDefault(); + if (token != null && !token.need_to_refresh_token) + { + return token.authorizer_access_token; + } + + var authorizer_refresh_token = await RedisHelper.GetAsync(refresh_key); + var component_access_token = await GetComponentAccessToken(); + token = await get_authorizer_access_token_by_refresh(authorizer_appid, authorizer_refresh_token, component_access_token); + if (token.errcode == 0) + { + await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); + await RedisHelper.SetAsync(refresh_key, token.authorizer_refresh_token,60*60*24*30); + return token.authorizer_access_token; + } + else + { + return ""; + } + } + } + return token.authorizer_access_token; + } + + public static async Task ReFreshAccessToken(string authorizer_appid, string component_access_token) + { + var key = string.Format(ConstantConfig.Redis_AuthorizerAccessToken_Key, authorizer_appid); + var refresh_key = string.Format(ConstantConfig.Redis_Refresh_AuthorizerAccessToken_Key, authorizer_appid); + var authorizer_refresh_token = await RedisHelper.GetAsync(refresh_key); + using (await _mutex4.LockAsync()) + { + var token = await get_authorizer_access_token_by_refresh(authorizer_appid, authorizer_refresh_token, component_access_token); + if (token.errcode == 0) + { + await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); + await RedisHelper.SetAsync(refresh_key, token.authorizer_refresh_token,60 * 60 * 24 * 30); + } + } + } + + /// + ///开放平台: 通过回调返回授权码的方式获得公众号的accesstoken + /// + /// + /// + public static async Task GetAuthorizerAccessTokenByCode(string authorization_code,Func> callback) + { + var component_access_token = await GetComponentAccessToken(); + var ret = await get_authorizer_access_token_by_callback(authorization_code, component_access_token); + var authorizer_appid = ret.authorization_info.authorizer_appid; + //存access_token的key + var key = string.Format(ConstantConfig.Redis_AuthorizerAccessToken_Key, authorizer_appid); + + //存refresh_access_token的key + var refresh_key = string.Format(ConstantConfig.Redis_Refresh_AuthorizerAccessToken_Key, authorizer_appid); + + var info = new GetAuthorizerAccessTokenResponse() + { + authorizer_access_token = ret.authorization_info.authorizer_access_token, + authorizer_refresh_token = ret.authorization_info.authorizer_refresh_token, + expires_in = ret.authorization_info.expires_in, + create_from = DateTime.Now.TimestampFrom19700101() + }; + + await RedisHelper.SetAsync(key, info.ToJson(), info.expires_in); + await RedisHelper.SetAsync(refresh_key, info.authorizer_refresh_token); + + if (callback != null) + { + return await callback(ret.authorization_info); + } + return info.authorizer_access_token; + } + + /// + ///开放平台: 得到开放平台的预授权码 + /// + /// + public static async Task GetPreAuthCode() + { + var component_access_token = await GetComponentAccessToken(); + var ret = await get_pre_auth_Code(component_access_token); + return ret.pre_auth_code; + } + + + /// + ///开放平台: 代公众号获取用户在公众号的OpenId,以及AccessToken 信息 + /// + /// + /// 结果对象,里边包含了授权令牌信息 + public static async Task GetAuthorizerClientUserOpenIdInfo(string appid, string code) + { + var component_access_token = await GetComponentAccessToken(); + var ret = await get_authorizer_user_openIdifno(appid, code, component_access_token); + return ret; + } + + /// + /// 开放平台:为公众平台授权的url + /// + /// + public static async Task GetAuthorizationUrl(string callback_url) + { + var appID = HostContext.Configuration["WxOpen:AppID"]; ; + var pre_auth_code = await GetPreAuthCode(); + var baseUrl = HostContext.Configuration["BaseInfoUrl"]; + callback_url = $"{baseUrl}/{callback_url}";//回调地址 + + callback_url = HttpUtility.UrlEncode(callback_url); + //开放平台授权地址 + var url = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?" + + $"component_appid={appID}" + + $"&pre_auth_code={pre_auth_code}" + + $"&redirect_uri={callback_url}"; + return url; + } + + private static readonly AsyncLock _mutex3 = new AsyncLock(); + /// + /// 【公众平台、小程序】:得到公众平台、小程序的accessToken + /// + /// + public static async Task GetAccessToken(string appid, string secret) + { + var key = string.Format(ConstantConfig.Redis_MP_AccessToken_Key, appid); + var tokenJson = await RedisHelper.GetAsync(key); + var token = tokenJson.FromJsonToOrDefault(); + if (token == null || token.need_to_refresh_token) + { + using (await _mutex3.LockAsync()) + { + tokenJson = await RedisHelper.GetAsync(key); + token = tokenJson.FromJsonToOrDefault(); + if (token != null && !token.need_to_refresh_token) + { + return token.access_token; + } + token = await get_access_token(appid, secret); + if (token.errcode == 0) + { + await RedisHelper.SetAsync(key, token.ToJson(), token.expires_in); + return token.access_token; + } + return ""; + } + } + return token.access_token; + } + /// + /// 公众平台,通过code换取网页授权access_token + /// + /// + /// + /// + public static async Task GetWebAccessToken(string appid, string code) + { + var secret = GetWxAppSecret(appid); + if (string.IsNullOrWhiteSpace(secret)) + { + LogHelper.Error("GetWebAccessToken", $"appid={appid} 没有配置"); + return null; + } + return await get_web_access_token(appid, secret, code); + + } + /// + /// 通过access_token和openid拉取用户信息 + /// + /// + /// + /// + public static async Task GetUserinfoByWebAccessToken(string access_token, string openid) + { + var key = string.Format(ConstantConfig.Redis_MP_GetUserinfoByWebAccessToken_Key, openid); + var userInfo = await RedisHelper.GetAsync(key); + + if (userInfo == null) + { + userInfo = await get_userinfo_by_web_access_token(access_token, openid); + if (userInfo.errcode == 0) + await RedisHelper.SetAsync(key, userInfo.ToJson(), 2 * 60 * 60); + } + return userInfo; + } + + /// + /// 开放平台获得获取用户基本信息(UnionID机制) + /// + /// + /// + /// + public static async Task GetUserUnionIDinfo(string appid, string openid) + { + var key = string.Format(ConstantConfig.Redis_MP_GetUserUnionIDInfo_Key, openid); + var userInfo = await RedisHelper.GetAsync(key); + + if (userInfo == null) + { + var access_token = await GetAuthorizerAccessToken(appid); + userInfo = await get_user_openid_info(access_token, openid); + if (userInfo.errcode == 0) + await RedisHelper.SetAsync(key, userInfo.ToJson(), 2 * 60 * 60); + } + return userInfo; + } + + /// + /// 公众平台获得获取用户基本信息() + /// + /// + /// + /// + public static async Task GetUserinfoByOpenId(string appid, string openid) + { + var key = string.Format(ConstantConfig.Redis_MP_GetUserOpenIdInfo_Key, openid); + var userInfo = await RedisHelper.GetAsync(key); + + if (userInfo == null) + { + var access_token = await GetAccessToken(appid,GetWxAppSecret(appid)); + userInfo = await get_user_openid_info(access_token, openid); + if (userInfo.errcode == 0) + await RedisHelper.SetAsync(key, userInfo.ToJson(), 2 * 60 * 60); + } + return userInfo; + } + + /// + /// 通过开放平台获得公众号的信息 + /// + /// + /// + /// + public static async Task GetMpInfo(string authorizer_appid) + { + var key = string.Format(ConstantConfig.Redis_MP_Info_Key, authorizer_appid); + var mpInfo = await RedisHelper.GetAsync(key); + + if (mpInfo == null) + { + var component_token = await GetComponentAccessToken(); + mpInfo = await get_mp_info(component_token, authorizer_appid); + if (mpInfo.errcode == 0) + await RedisHelper.SetAsync(key, mpInfo.ToJson(), 1 * 60 * 60); + } + return mpInfo.authorizer_info; + } + + /// + /// 为公众平台创建菜单 + /// + /// + /// + public static async Task CreateMPMenu(string access_token, object menu) + { + var ret = await create_mp_menu(access_token, menu); + return ret.errcode == 0; + } + + /// + /// 为公众平台粉丝添加标签 + /// + /// + /// + public static async Task AddTag(string appid, List openIds, string tagId) + { + var access_token = await GetAuthorizerAccessToken(appid); + await add_tag(access_token, openIds, int.Parse(tagId)); + } + + /// + /// 得到公众平台所有标签 + /// + /// + /// + public static async Task GetTags(string appid) + { + var access_token = await GetAuthorizerAccessToken(appid); + return await get_tags(access_token); + } + + /// + /// 得到公众平台jsapi ticket + /// + /// + /// + public static async Task GetJsTicket(string authorizer_appid) + { + var key = string.Format(ConstantConfig.Redis_MP_Js_Ticket_Key, authorizer_appid); + var ticketInfo = await RedisHelper.GetAsync(key); + if (ticketInfo == null) + { + var access_token = await GetAuthorizerAccessToken(authorizer_appid); + ticketInfo = await get_jsapi_ticket(access_token); + if (ticketInfo.errcode == 0) + await RedisHelper.SetAsync(key, ticketInfo.ToJson(), 1 * 60 * 60); + } + return ticketInfo; + } + + /// + /// 为公众平台创建二维码 + /// + /// + /// + public static async Task CreatePermanentQrcode(string appid, string scene_str) + { + var access_token = await GetAuthorizerAccessToken(appid); + return await create_permanent_qrcode(access_token, scene_str); + } + + #region 内部微信接口 + + /// + /// 开放平台:得到access_token + /// + /// + /// + private static async Task get_component_access_token(string ticket) + { + string url = "cgi-bin/component/api_component_token"; + + var reqData = new + { + component_appid = HostContext.Configuration["WxOpen:AppID"], + component_appsecret = HostContext.Configuration["WxOpen:AppSecret"], + component_verify_ticket = ticket, + }; + var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); + var ret = respData.FromJsonTo(); + if (ret.errcode > 0) + { + LogHelper.Error("get_component_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + ret.create_from = DateTime.Now.TimestampFrom19700101(); + LogHelper.Debug("get_component_access_token", respData); + return ret; + } + + + /// + ///开放平台:通过刷新的方式获得公众平台的accesstoken + /// + /// + /// + private static async Task get_authorizer_access_token_by_refresh(string authorizer_appid, string authorizer_refresh_token,string component_access_token) + { + string url = $"cgi-bin/component/api_authorizer_token?component_access_token={component_access_token}"; + var reqData = new + { + component_appid = HostContext.Configuration["WxOpen:AppID"], + authorizer_appid, + authorizer_refresh_token, + }; + + var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_authorizer_access_token_by_refresh", $"errcode={ret.errcode},errmsg={ret.errmsg},authorizer_appid={authorizer_appid}"); + } + ret.create_from = DateTime.Now.TimestampFrom19700101(); + LogHelper.Debug("get_authorizer_access_token_by_callback", respData); + return ret; + } + + /// + ///开放平台: 通过授权码回调方式获得公众平台的accesstoken + /// + /// + /// + private static async Task get_authorizer_access_token_by_callback(string authorization_code,string component_access_token) + { + string url = $"cgi-bin/component/api_query_auth?component_access_token={component_access_token}"; + var reqData = new + { + component_appid = HostContext.Configuration["WxOpen:AppID"], + authorization_code, + }; + var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_authorizer_access_token_by_callback", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + ret.create_from = DateTime.Now.TimestampFrom19700101(); + LogHelper.Debug("get_authorizer_access_token_by_callback", respData); + return ret; + } + + /// + ///开放平台: 获取开放平台预授权代码 用于生成授权url + /// + private static async Task get_pre_auth_Code(string component_access_token) + { + string url = $"cgi-bin/component/api_create_preauthcode?component_access_token={component_access_token}"; + var reqData = new + { + component_appid = HostContext.Configuration["WxOpen:AppID"], + }; + + var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_pre_auth_Code", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + ret.create_from = DateTime.Now.TimestampFrom19700101(); + LogHelper.Debug("get_pre_auth_Code", respData); + return ret; + } + + /// + /// 开放平台:获得公众平台用户openid信息 + /// + /// + /// + /// + /// + private static async Task get_authorizer_user_openIdifno(string appid, string code, string component_access_token) + { + LogHelper.Debug("get_authorizer_user_openIdifno_params", $"{appid},{code}"); + var component_appid = HostContext.Configuration["WxOpen:AppID"]; + string url = $"sns/oauth2/component/access_token?appid={appid}&code={code}&grant_type=authorization_code&component_appid={component_appid}&component_access_token={component_access_token}"; + + LogHelper.Debug("get_authorizer_user_openIdifno-url", url); + var resp = await GetHttpClient().GetStringAsync(url); + var respData = resp.FromJsonToOrDefault(); + LogHelper.Debug("get_authorizer_user_openIdifno", resp); + return respData; + } + + /// + ///公众平台,小程序: 直接获得公众平台,小程序的accesstoken + /// + /// + /// + private static async Task get_access_token(string appid, string secret) + { + string url = $"cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}"; + + var respData = await GetHttpClient().GetStringAsync(url); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_mp_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + ret.create_from = DateTime.Now.TimestampFrom19700101(); + LogHelper.Debug("get_mp_access_token", respData); + return ret; + } + + /// + ///公众平台,通过code换取网页授权access_token + ///这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同 + ///https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 + /// + /// + /// + private static async Task get_web_access_token(string appid, string secret,string code) + { + string url = $"sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code"; + var respData = await GetHttpClient().GetStringAsync(url); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_web_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + ret.create_from = DateTime.Now.TimestampFrom19700101(); + LogHelper.Debug("get_web_access_token", respData); + return ret; + } + + /// + /// 如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。 + /// https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 + /// + /// 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 + /// + /// + private static async Task get_userinfo_by_web_access_token(string access_token, string openid) + { + string url = $"sns/userinfo?access_token={access_token}&openid={openid}&lang=zh_CN"; + var respData = await GetHttpClient().GetStringAsync(url); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_userinfo_by_web_access_token", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("get_userinfo_by_web_access_token", respData); + return ret; + } + + private static async Task get_user_openid_info(string access_token, string openid) + { + string url = $"cgi-bin/user/info?access_token={access_token}&openid={openid}&lang=zh_CN"; + var respData = await GetHttpClient().GetStringAsync(url); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_user_openid_info", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("get_user_openid_info", respData); + return ret; + } + + + /// + /// 通过开放平台得到公众号的信息,主要是 (authorizer_info) + /// + /// + /// + /// + private static async Task get_mp_info(string component_token, string authorizer_appid) + { + string url = $"cgi-bin/component/api_get_authorizer_info?component_access_token={component_token}"; + + var reqData = new + { + authorizer_appid, + component_appid = HostContext.Configuration["WxOpen:AppID"], + }; + var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_mp_info_resp", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("get_mp_info_resp", respData); + return ret; + } + + /// + /// 为公众平台创建菜单 + /// + /// + /// + /// + private static async Task create_mp_menu(string access_token, object menu) + { + string url = $"cgi-bin/menu/create?access_token={access_token}"; + var respData = await GetHttpClient().PostAsJsonGetString(url, menu); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("create_mp_menu", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("create_mp_menu", respData); + return ret; + } + + /// + /// 为公众号粉丝打标签 + /// + /// + /// + /// + private static async Task add_tag(string access_token, List openIds,int tagId) + { + string url = $"cgi-bin/tags/members/batchtagging?access_token={access_token}"; + + var reqData = new + { + openid_list = openIds, + tagid = tagId + }; + var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("add_tag", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("add_tag", respData); + return ret; + } + + /// + /// 创建公众号二维码 + /// + /// + /// + /// + private static async Task get_tags(string access_token) + { + string url = $"cgi-bin/tags/get?access_token={access_token}"; + var respData = await GetHttpClient().GetStringAsync(url); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_tags", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("get_tags", respData); + return ret; + } + + /// + /// 创建公众号二维码 + /// + /// + /// + /// + private static async Task create_permanent_qrcode(string access_token,string scene_str) + { + string url = $"cgi-bin/qrcode/create?access_token={access_token}"; + //{ "action_name": "QR_LIMIT_STR_SCENE", "action_info": { "scene": { "scene_str": "test"} } } + var reqData = new + { + action_name = "QR_LIMIT_STR_SCENE", + action_info = new + { + scene = new + { + scene_str + } + } + }; + var respData = await GetHttpClient().PostAsJsonGetString(url, reqData); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("create_permanent_qrcode", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("create_permanent_qrcode", respData); + return ret; + } + + public static async Task get_jsapi_ticket(string access_token) + { + string url = $"cgi-bin/ticket/getticket?access_token={access_token}&type=jsapi"; + var respData = await GetHttpClient().GetStringAsync(url); + var ret = respData.FromJsonToOrDefault(); + if (ret.errcode > 0) + { + LogHelper.Error("get_jsapi_ticket", $"errcode={ret.errcode},errmsg={ret.errmsg}"); + } + LogHelper.Debug("get_jsapi_ticket", respData); + return ret; + } + + #endregion + } +} diff --git a/Infrastructure/WxApi/WxOpenCrypt.cs b/Infrastructure/WxApi/WxOpenCrypt.cs index 5bea97c..8f7264f 100644 --- a/Infrastructure/WxApi/WxOpenCrypt.cs +++ b/Infrastructure/WxApi/WxOpenCrypt.cs @@ -1,222 +1,222 @@ -using System; -using System.Collections; -//using System.Web; -using System.Security.Cryptography; -using System.Text; -using System.Xml; -using Tencent; -//-40001 : 签名验证错误 -//-40002 : xml解析失败 -//-40003 : sha加密生成签名失败 -//-40004 : AESKey 非法 -//-40005 : appid 校验错误 -//-40006 : AES 加密失败 -//-40007 : AES 解密失败 -//-40008 : 解密后得到的buffer非法 -//-40009 : base64加密异常 -//-40010 : base64解密异常 -namespace Hncore.Wx.Open -{ - public class WxOpenCrypt - { - string m_sToken; - string m_sEncodingAESKey; - string m_sAppID; - enum WXBizMsgCryptErrorCode - { - WXBizMsgCrypt_OK = 0, - WXBizMsgCrypt_ValidateSignature_Error = -40001, - WXBizMsgCrypt_ParseXml_Error = -40002, - WXBizMsgCrypt_ComputeSignature_Error = -40003, - WXBizMsgCrypt_IllegalAesKey = -40004, - WXBizMsgCrypt_ValidateAppid_Error = -40005, - WXBizMsgCrypt_EncryptAES_Error = -40006, - WXBizMsgCrypt_DecryptAES_Error = -40007, - WXBizMsgCrypt_IllegalBuffer = -40008, - WXBizMsgCrypt_EncodeBase64_Error = -40009, - WXBizMsgCrypt_DecodeBase64_Error = -40010 - }; - - //构造函数 - // @param sToken: 公众平台上,开发者设置的Token - // @param sEncodingAESKey: 公众平台上,开发者设置的EncodingAESKey - // @param sAppID: 公众帐号的appid - public WxOpenCrypt(string sToken, string sEncodingAESKey, string sAppID) - { - m_sToken = sToken; - m_sAppID = sAppID; - m_sEncodingAESKey = sEncodingAESKey; - } - - - // 检验消息的真实性,并且获取解密后的明文 - // @param sMsgSignature: 签名串,对应URL参数的msg_signature - // @param sTimeStamp: 时间戳,对应URL参数的timestamp - // @param sNonce: 随机串,对应URL参数的nonce - // @param sPostData: 密文,对应POST请求的数据 - // @param sMsg: 解密后的原文,当return返回0时有效 - // @return: 成功0,失败返回对应的错误码 - public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg) - { - if (m_sEncodingAESKey.Length!=43) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; - } - XmlDocument doc = new XmlDocument(); - XmlNode root; - string sEncryptMsg; - try - { - doc.LoadXml(sPostData); - root = doc.FirstChild; - sEncryptMsg = root["Encrypt"].InnerText; - } - catch (Exception) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error; - } - //verify signature - int ret = 0; - ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature); - if (ret != 0) - return ret; - //decrypt - string cpid = ""; - try - { - sMsg= Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid); - // sMsg = SecurityHelper.Decrypt(sEncryptMsg, m_sEncodingAESKey); - } - catch (FormatException) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error; - } - catch (Exception) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error; - } - if (cpid != m_sAppID) - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateAppid_Error; - return 0; - } - - //将企业号回复用户的消息加密打包 - // @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串 - // @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp - // @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce - // @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串, - // 当return返回0时有效 - // return:成功0,失败返回对应的错误码 - public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg) - { - if (m_sEncodingAESKey.Length!=43) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; - } - string raw = ""; - try - { - raw= Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sAppID); - //raw = SecurityHelper.AESEncrypt(sReplyMsg, m_sEncodingAESKey); - } - catch (Exception) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error; - } - string MsgSigature = ""; - int ret = 0; - ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature); - if (0 != ret) - return ret; - sEncryptMsg = ""; - - string EncryptLabelHead = ""; - string MsgSigLabelHead = ""; - string TimeStampLabelHead = ""; - string NonceLabelHead = ""; - sEncryptMsg = sEncryptMsg + "" + EncryptLabelHead + raw + EncryptLabelTail; - sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail; - sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail; - sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail; - sEncryptMsg += ""; - return 0; - } - - public class DictionarySort : System.Collections.IComparer - { - public int Compare(object oLeft, object oRight) - { - string sLeft = oLeft as string; - string sRight = oRight as string; - int iLeftLength = sLeft.Length; - int iRightLength = sRight.Length; - int index = 0; - while (index < iLeftLength && index < iRightLength) - { - if (sLeft[index] < sRight[index]) - return -1; - else if (sLeft[index] > sRight[index]) - return 1; - else - index++; - } - return iLeftLength - iRightLength; - - } - } - //Verify Signature - public static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture) - { - string hash = ""; - int ret = 0; - ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash); - if (ret != 0) - return ret; - //System.Console.WriteLine(hash); - if (hash == sSigture) - return 0; - else - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error; - } - } - - public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt ,ref string sMsgSignature) - { - ArrayList AL = new ArrayList(); - AL.Add(sToken); - AL.Add(sTimeStamp); - AL.Add(sNonce); - AL.Add(sMsgEncrypt); - AL.Sort(new DictionarySort()); - string raw = ""; - for (int i = 0; i < AL.Count; ++i) - { - raw += AL[i]; - } - - SHA1 sha; - ASCIIEncoding enc; - string hash = ""; - try - { - sha = new SHA1CryptoServiceProvider(); - enc = new ASCIIEncoding(); - byte[] dataToHash = enc.GetBytes(raw); - byte[] dataHashed = sha.ComputeHash(dataToHash); - hash = BitConverter.ToString(dataHashed).Replace("-", ""); - hash = hash.ToLower(); - } - catch (Exception) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error; - } - sMsgSignature = hash; - return 0; - } - } -} +using System; +using System.Collections; +//using System.Web; +using System.Security.Cryptography; +using System.Text; +using System.Xml; +using Tencent; +//-40001 : 签名验证错误 +//-40002 : xml解析失败 +//-40003 : sha加密生成签名失败 +//-40004 : AESKey 非法 +//-40005 : appid 校验错误 +//-40006 : AES 加密失败 +//-40007 : AES 解密失败 +//-40008 : 解密后得到的buffer非法 +//-40009 : base64加密异常 +//-40010 : base64解密异常 +namespace Hncore.Wx.Open +{ + public class WxOpenCrypt + { + string m_sToken; + string m_sEncodingAESKey; + string m_sAppID; + enum WXBizMsgCryptErrorCode + { + WXBizMsgCrypt_OK = 0, + WXBizMsgCrypt_ValidateSignature_Error = -40001, + WXBizMsgCrypt_ParseXml_Error = -40002, + WXBizMsgCrypt_ComputeSignature_Error = -40003, + WXBizMsgCrypt_IllegalAesKey = -40004, + WXBizMsgCrypt_ValidateAppid_Error = -40005, + WXBizMsgCrypt_EncryptAES_Error = -40006, + WXBizMsgCrypt_DecryptAES_Error = -40007, + WXBizMsgCrypt_IllegalBuffer = -40008, + WXBizMsgCrypt_EncodeBase64_Error = -40009, + WXBizMsgCrypt_DecodeBase64_Error = -40010 + }; + + //构造函数 + // @param sToken: 公众平台上,开发者设置的Token + // @param sEncodingAESKey: 公众平台上,开发者设置的EncodingAESKey + // @param sAppID: 公众帐号的appid + public WxOpenCrypt(string sToken, string sEncodingAESKey, string sAppID) + { + m_sToken = sToken; + m_sAppID = sAppID; + m_sEncodingAESKey = sEncodingAESKey; + } + + + // 检验消息的真实性,并且获取解密后的明文 + // @param sMsgSignature: 签名串,对应URL参数的msg_signature + // @param sTimeStamp: 时间戳,对应URL参数的timestamp + // @param sNonce: 随机串,对应URL参数的nonce + // @param sPostData: 密文,对应POST请求的数据 + // @param sMsg: 解密后的原文,当return返回0时有效 + // @return: 成功0,失败返回对应的错误码 + public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg) + { + if (m_sEncodingAESKey.Length!=43) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; + } + XmlDocument doc = new XmlDocument(); + XmlNode root; + string sEncryptMsg; + try + { + doc.LoadXml(sPostData); + root = doc.FirstChild; + sEncryptMsg = root["Encrypt"].InnerText; + } + catch (Exception) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error; + } + //verify signature + int ret = 0; + ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature); + if (ret != 0) + return ret; + //decrypt + string cpid = ""; + try + { + sMsg= Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid); + // sMsg = SecurityHelper.Decrypt(sEncryptMsg, m_sEncodingAESKey); + } + catch (FormatException) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error; + } + catch (Exception) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error; + } + if (cpid != m_sAppID) + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateAppid_Error; + return 0; + } + + //将企业号回复用户的消息加密打包 + // @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串 + // @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp + // @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce + // @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串, + // 当return返回0时有效 + // return:成功0,失败返回对应的错误码 + public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg) + { + if (m_sEncodingAESKey.Length!=43) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; + } + string raw = ""; + try + { + raw= Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sAppID); + //raw = SecurityHelper.AESEncrypt(sReplyMsg, m_sEncodingAESKey); + } + catch (Exception) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error; + } + string MsgSigature = ""; + int ret = 0; + ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature); + if (0 != ret) + return ret; + sEncryptMsg = ""; + + string EncryptLabelHead = ""; + string MsgSigLabelHead = ""; + string TimeStampLabelHead = ""; + string NonceLabelHead = ""; + sEncryptMsg = sEncryptMsg + "" + EncryptLabelHead + raw + EncryptLabelTail; + sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail; + sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail; + sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail; + sEncryptMsg += ""; + return 0; + } + + public class DictionarySort : System.Collections.IComparer + { + public int Compare(object oLeft, object oRight) + { + string sLeft = oLeft as string; + string sRight = oRight as string; + int iLeftLength = sLeft.Length; + int iRightLength = sRight.Length; + int index = 0; + while (index < iLeftLength && index < iRightLength) + { + if (sLeft[index] < sRight[index]) + return -1; + else if (sLeft[index] > sRight[index]) + return 1; + else + index++; + } + return iLeftLength - iRightLength; + + } + } + //Verify Signature + public static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture) + { + string hash = ""; + int ret = 0; + ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash); + if (ret != 0) + return ret; + //System.Console.WriteLine(hash); + if (hash == sSigture) + return 0; + else + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error; + } + } + + public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt ,ref string sMsgSignature) + { + ArrayList AL = new ArrayList(); + AL.Add(sToken); + AL.Add(sTimeStamp); + AL.Add(sNonce); + AL.Add(sMsgEncrypt); + AL.Sort(new DictionarySort()); + string raw = ""; + for (int i = 0; i < AL.Count; ++i) + { + raw += AL[i]; + } + + SHA1 sha; + ASCIIEncoding enc; + string hash = ""; + try + { + sha = new SHA1CryptoServiceProvider(); + enc = new ASCIIEncoding(); + byte[] dataToHash = enc.GetBytes(raw); + byte[] dataHashed = sha.ComputeHash(dataToHash); + hash = BitConverter.ToString(dataHashed).Replace("-", ""); + hash = hash.ToLower(); + } + catch (Exception) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error; + } + sMsgSignature = hash; + return 0; + } + } +} diff --git a/Infrastructure/WxApi/obj/Debug/netstandard2.0/WxApi.csprojAssemblyReference.cache b/Infrastructure/WxApi/obj/Debug/netstandard2.0/WxApi.csprojAssemblyReference.cache index 9ee691e695c05f1dce6d358dbee9da533d804504..8a8826abd97151d85c4911a2a58096a7f13b29c8 100644 GIT binary patch literal 261457 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 literal 278636 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 3 { - - if utf8Len(line) > 0 { - - if i == 4 && strings.HasPrefix(line, messageChars) { - line = slice(line, 9, -1) - } - - if log.Level == "ERROR" && - (strings.HasPrefix(strings.TrimSpace(line), sysLogStartChars1) || - strings.HasPrefix(strings.TrimSpace(line), sysLogStartChars2) || - strings.HasPrefix(strings.TrimSpace(line), sysLogStartChars3)) { - - continue - } - } - - log.Message += line + "\n" - } - - } - - return -} - -func slice(str string, start int, end int) string { - - str2 := []rune(str) - - len := len(str2) - - if start < 0 || start > len { - start = 0 - } - if end < 0 || end > len { - end = len - } - - return string(str2[start:end]) -} - -func utf8Len(str string) int { - return utf8.RuneCountInString(str) -} +package logparse + +import ( + "encoding/json" + "fmt" + "strings" + "unicode/utf8" +) + +type filebeat struct { + Message string `json:"message"` +} + +type Log struct { + Time string + Title string + Message string + Level string + App string +} + +var lineSplitChar = "\n" +var leftMiddleBracketsChar = "[" +var rightMiddleBracketsChar = "]" +var assemblyChars = "Assembly" +var titleChars = "Title" +var messageChars = "Message :" +var sysLogStartChars1 = "at lambda_method(Closure , Object )" +var sysLogStartChars2 = "at Microsoft.Extensions." +var sysLogStartChars3 = "at Microsoft.AspNetCore" + +func LogParseStart(parse <-chan *string, db chan<- *Log) { + + for fileBeatStr := range parse { + + jsonobj := filebeat{} + + err := json.Unmarshal([]byte(*fileBeatStr), &jsonobj) + + if err != nil { + println(err) + } + + log, err := parseLog(&jsonobj.Message) + + if err != nil { + fmt.Println(err) + } else { + db <- log + } + + } +} + +func parseLog(logChars *string) (log *Log, err error) { + + defer func() { + if p := recover(); p != nil { + err = fmt.Errorf("解析日志失败:%v", p) + } + }() + + log = &Log{} + + lines := strings.Split(*logChars, lineSplitChar) + + for index, line := range lines { + + i := index + 1 + + if i == 1 && strings.HasPrefix(line, leftMiddleBracketsChar) { + + timeEndIndex := strings.Index(line, rightMiddleBracketsChar) + + log.Level = slice(line, timeEndIndex+2, -1) + log.Time = slice(line, strings.Index(line, leftMiddleBracketsChar)+1, timeEndIndex-1) + } + + if i == 2 && strings.HasPrefix(line, assemblyChars) { + + log.App = strings.TrimSpace(slice(line, 9, -1)) + } + + if i == 3 && strings.HasPrefix(line, titleChars) { + log.Title = slice(line, 7, -1) + } + + if i > 3 { + + if utf8Len(line) > 0 { + + if i == 4 && strings.HasPrefix(line, messageChars) { + line = slice(line, 9, -1) + } + + if log.Level == "ERROR" && + (strings.HasPrefix(strings.TrimSpace(line), sysLogStartChars1) || + strings.HasPrefix(strings.TrimSpace(line), sysLogStartChars2) || + strings.HasPrefix(strings.TrimSpace(line), sysLogStartChars3)) { + + continue + } + } + + log.Message += line + "\n" + } + + } + + return +} + +func slice(str string, start int, end int) string { + + str2 := []rune(str) + + len := len(str2) + + if start < 0 || start > len { + start = 0 + } + if end < 0 || end > len { + end = len + } + + return string(str2[start:end]) +} + +func utf8Len(str string) int { + return utf8.RuneCountInString(str) +} diff --git a/Infrastructure/log_storage/logstorage/logstorage.go b/Infrastructure/log_storage/logstorage/logstorage.go index c422eff..bd1c316 100644 --- a/Infrastructure/log_storage/logstorage/logstorage.go +++ b/Infrastructure/log_storage/logstorage/logstorage.go @@ -1,29 +1,29 @@ -package logstorage - -import ( - "database/sql" - "fmt" - "log_storage/logparse" -) - -func PgsqlStorageStart(logchan <-chan *logparse.Log, dbconn string) { - - db, err := sql.Open("postgres", dbconn) - - if err != nil { - panic("pgsql连接失败") - } - - for log := range logchan { - - sql := "INSERT INTO psiplog (time,title,message,level,app) VALUES($1,$2,$3,$4,$5);" - - _, err := db.Exec(sql, log.Time, log.Title, log.Message, log.Level, log.App) - - if err != nil { - fmt.Println(err) - } else { - fmt.Println("入库一条数据") - } - } -} +package logstorage + +import ( + "database/sql" + "fmt" + "log_storage/logparse" +) + +func PgsqlStorageStart(logchan <-chan *logparse.Log, dbconn string) { + + db, err := sql.Open("postgres", dbconn) + + if err != nil { + panic("pgsql连接失败") + } + + for log := range logchan { + + sql := "INSERT INTO psiplog (time,title,message,level,app) VALUES($1,$2,$3,$4,$5);" + + _, err := db.Exec(sql, log.Time, log.Title, log.Message, log.Level, log.App) + + if err != nil { + fmt.Println(err) + } else { + fmt.Println("入库一条数据") + } + } +} diff --git a/Infrastructure/log_storage/main.go b/Infrastructure/log_storage/main.go index 89300a8..7120a85 100644 --- a/Infrastructure/log_storage/main.go +++ b/Infrastructure/log_storage/main.go @@ -1,40 +1,40 @@ -package main - -import ( - "flag" - "log_storage/logparse" - "log_storage/logstorage" - logreids "log_storage/redis" - "strconv" - - "github.com/go-redis/redis" - _ "github.com/lib/pq" -) - -func main() { - - redisAddr := flag.String("RedisAddr", "", "reids server ip地址") - redisPort := flag.Int("RedisPort", 6379, "redis端口号") - redisPassword := flag.String("RedisPwd", "", "redis密码") - pgsqlConn := flag.String("PgsqlConn", "", "pgsql连接字符串(host=192.168.1.245 port=5432 user=postgres password=123456 dbname=log)") - - flag.Parse() - - redisClient := redis.NewClient(&redis.Options{ - Addr: *redisAddr + ":" + strconv.Itoa(*redisPort), - Password: *redisPassword, - DB: 14, - }) - - parseChan := make(chan *string) - dbChan := make(chan *logparse.Log) - - go logstorage.PgsqlStorageStart(dbChan, *pgsqlConn) - go logparse.LogParseStart(parseChan, dbChan) - go logreids.RedisPullStart(parseChan, redisClient) - - waiting := make(chan interface{}) - - <-waiting - -} +package main + +import ( + "flag" + "log_storage/logparse" + "log_storage/logstorage" + logreids "log_storage/redis" + "strconv" + + "github.com/go-redis/redis" + _ "github.com/lib/pq" +) + +func main() { + + redisAddr := flag.String("RedisAddr", "", "reids server ip地址") + redisPort := flag.Int("RedisPort", 6379, "redis端口号") + redisPassword := flag.String("RedisPwd", "", "redis密码") + pgsqlConn := flag.String("PgsqlConn", "", "pgsql连接字符串(host=192.168.1.245 port=5432 user=postgres password=123456 dbname=log)") + + flag.Parse() + + redisClient := redis.NewClient(&redis.Options{ + Addr: *redisAddr + ":" + strconv.Itoa(*redisPort), + Password: *redisPassword, + DB: 14, + }) + + parseChan := make(chan *string) + dbChan := make(chan *logparse.Log) + + go logstorage.PgsqlStorageStart(dbChan, *pgsqlConn) + go logparse.LogParseStart(parseChan, dbChan) + go logreids.RedisPullStart(parseChan, redisClient) + + waiting := make(chan interface{}) + + <-waiting + +} diff --git a/Infrastructure/log_storage/redis/redispull.go b/Infrastructure/log_storage/redis/redispull.go index cd35fae..2df6faf 100644 --- a/Infrastructure/log_storage/redis/redispull.go +++ b/Infrastructure/log_storage/redis/redispull.go @@ -1,32 +1,32 @@ -package redis - -import ( - "fmt" - "github.com/go-redis/redis" - "time" -) - -func RedisPullStart(parse chan<- *string, client *redis.Client) { - - pong, err := client.Ping().Result() - - if err != nil { - panic("redis连接失败") - } - - fmt.Println(pong, err) - - for { - - vals, err := client.BLPop(0, "filebeat").Result() - - if err != nil { - fmt.Println(err) - time.Sleep(time.Second * 10) - } - - if len(vals) == 2 { - parse <- &vals[1] - } - } -} +package redis + +import ( + "fmt" + "github.com/go-redis/redis" + "time" +) + +func RedisPullStart(parse chan<- *string, client *redis.Client) { + + pong, err := client.Ping().Result() + + if err != nil { + panic("redis连接失败") + } + + fmt.Println(pong, err) + + for { + + vals, err := client.BLPop(0, "filebeat").Result() + + if err != nil { + fmt.Println(err) + time.Sleep(time.Second * 10) + } + + if len(vals) == 2 { + parse <- &vals[1] + } + } +} diff --git a/Infrastructure/log_storage/run.sh b/Infrastructure/log_storage/run.sh index 3d4eff6..f71525c 100644 --- a/Infrastructure/log_storage/run.sh +++ b/Infrastructure/log_storage/run.sh @@ -1,5 +1,5 @@ -#!/bin/bash -exec $(dirname "$0")/log_storage \ --RedisAddr=192.168.1.245 \ --RedisPwd=123456 \ +#!/bin/bash +exec $(dirname "$0")/log_storage \ +-RedisAddr=192.168.1.245 \ +-RedisPwd=123456 \ -PgsqlConn="host=192.168.1.245 port=5432 user=postgres password=123456 dbname=log sslmode=disable" \ No newline at end of file