Browse Source

Merge pull request #7 from colinin/2.9

合并堆积的2.9分支提交
pull/21/head
cKey 6 years ago
committed by GitHub
parent
commit
c4405dc6b2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 141
      aspnet-core/configuration/AuthServer.Host/appsettings.Development.json
  2. 110
      aspnet-core/configuration/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json
  3. 102
      aspnet-core/configuration/LINGYUN.ApiGateway.Host/appsettings.Development.json
  4. 91
      aspnet-core/configuration/LINGYUN.ApiGateway.HttpApi.Host/appsettings.Development.json
  5. 133
      aspnet-core/configuration/LINGYUN.Platform.HttpApi.Host/appsettings.Development.json
  6. 2
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/PhoneNumberRegisterDto.cs
  7. 28
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/WeChatRegisterDto.cs
  8. 4
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs
  9. 1
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj
  10. 6
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs
  11. 34
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs
  12. 3
      aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/en.json
  13. 3
      aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json
  14. 4
      aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/Microsoft/AspNetCore/Identity/PhoneNumberUserValidator.cs
  15. 17
      aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/AccountController.cs
  16. 7
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN.ApiGateway.Application.Contracts.csproj
  17. 26
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/AggregateReRouteAppService.cs
  18. 15
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/GlobalConfigurationAppService.cs
  19. 23
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/ReRouteAppService.cs
  20. 7
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain.Shared/LINGYUN.ApiGateway.Domain.Shared.csproj
  21. 9
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain.Shared/LINGYUN/ApiGateway/EventBus/ApigatewayConfigChangeEventData.cs
  22. 1
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN.ApiGateway.Domain.csproj
  23. 2
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.EntityFrameworkCore/LINGYUN/ApiGateway/EntityFrameworkCore/ApiGatewayDbContextModelCreatingExtensions.cs
  24. 7
      aspnet-core/modules/apigateway/LINGYUN.ApiGateway.HttpApi.Client/LINGYUN.ApiGateway.HttpApi.Client.csproj
  25. 19
      aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN.Abp.Aliyun.Authorization.csproj
  26. 15
      aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN/Abp/Aliyun/Authorization/AbpAliyunAuthorizationModule.cs
  27. 14
      aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN/Abp/Aliyun/Authorization/AbpAliyunOptions.cs
  28. 21
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN.Abp.BackgroundJobs.Hangfire.csproj
  29. 34
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs
  30. 34
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs
  31. 46
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs
  32. 35
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/IBackgroundJobManagerExtensions.cs
  33. 78
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/Volo/Abp/BackgroundJobs/CronGenerator.cs
  34. 19
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs/LINGYUN.Abp.BackgroundJobs.csproj
  35. 43
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs/LINGYUN/Abp/BackgroundJobs/RetryAsyncBackgroundJobArgs.cs
  36. 80
      aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs/LINGYUN/Abp/BackgroundJobs/RetryAsyncBackgroundJobBase.cs
  37. 25
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN.Abp.BlobStoring.Aliyun.csproj
  38. 35
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs
  39. 25
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobContainerConfigurationExtensions.cs
  40. 148
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs
  41. 62
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs
  42. 22
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs
  43. 24
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNameCalculator.cs
  44. 9
      aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/IAliyunBlobNameCalculator.cs
  45. 2
      aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj
  46. 22
      aspnet-core/modules/common/LINGYUN.Abp.Hangfire.MySqlStorage/LINGYUN.Abp.Hangfire.Storage.MySql.csproj
  47. 38
      aspnet-core/modules/common/LINGYUN.Abp.Hangfire.MySqlStorage/LINGYUN/Abp/Hangfire/Storage/MySql/AbpHangfireMySqlStorageModule.cs
  48. 20
      aspnet-core/modules/common/LINGYUN.Abp.Hangfire.Storage.SqlServer/LINGYUN.Abp.Hangfire.Storage.SqlServer.csproj
  49. 38
      aspnet-core/modules/common/LINGYUN.Abp.Hangfire.Storage.SqlServer/LINGYUN/Abp/Hangfire/Storage/SqlServer/AbpHangfireSqlServerStorageModule.cs
  50. 8
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/Class1.cs
  51. 14
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj
  52. 44
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs
  53. 9
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/Localization/WeChatValidator/en.json
  54. 9
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/Localization/WeChatValidator/zh-Hans.json
  55. 78
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatSignatureMiddleware.cs
  56. 16
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatSignatureOptions.cs
  57. 106
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs
  58. 21
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatValidatorConsts.cs
  59. 22
      aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Builder/IdentityServerApplicationBuilderExtensions.cs
  60. 8
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/AbpNotificationsSignalRModule.cs
  61. 4
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs
  62. 30
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs
  63. 21
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.WeApp.csproj
  64. 30
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpNotificationsWeChatWeAppModule.cs
  65. 22
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpWeChatWeAppNotificationOptions.cs
  66. 9
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/IWeChatWeAppNotificationSender.cs
  67. 84
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs
  68. 108
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationSender.cs
  69. 103
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppSendNotificationData.cs
  70. 2
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN.Abp.Notifications.csproj
  71. 33
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/AbpNotificationModule.cs
  72. 16
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/AbpNotificationOptions.cs
  73. 9
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDefinitionContext.cs
  74. 15
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDefinitionManager.cs
  75. 7
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDefinitionProvider.cs
  76. 28
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDispatcher.cs
  77. 12
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProvider.cs
  78. 9
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProviderManager.cs
  79. 11
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublisher.cs
  80. 12
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs
  81. 81
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationSubscriptionManager.cs
  82. 198
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs
  83. 70
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/NotificationSubscriptionManager.cs
  84. 78
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationData.cs
  85. 83
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinition.cs
  86. 33
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinitionContext.cs
  87. 75
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs
  88. 9
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinitionProvider.cs
  89. 38
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationEventData.cs
  90. 41
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationInfo.cs
  91. 18
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationLifetime.cs
  92. 14
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationName.cs
  93. 15
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationNameNormalizer.cs
  94. 34
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishJob.cs
  95. 20
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishJobArgs.cs
  96. 49
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs
  97. 33
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProviderManager.cs
  98. 1
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationSubscriptionInfo.cs
  99. 16
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/UserIdentifier.cs
  100. 4
      aspnet-core/modules/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN.Abp.Sms.Aliyun.csproj

141
aspnet-core/configuration/AuthServer.Host/appsettings.Development.json

@ -0,0 +1,141 @@
{
"App": {
"SelfUrl": "http://localhost:44385/",
"CorsOrigins": "http://localhost:4200,http://localhost:9528,http://127.0.0.1:63898"
},
"AppSelfUrl": "http://localhost:44385/",
"ConnectionStrings": {
"Default": "Server=127.0.0.1;Database=IdentityServer;User Id=root;Password=123456",
"AbpIdentity": "Server=127.0.0.1;Database=IdentityServer;User Id=root;Password=123456",
"AbpIdentityServer": "Server=127.0.0.1;Database=IdentityServer;User Id=root;Password=123456",
"AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpFeatureManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456"
},
"Identity": {
"Password": {
"RequiredLength": 6,
"RequiredUniqueChars": 0,
"RequireNonAlphanumeric": false,
"RequireLowercase": false,
"RequireUppercase": false,
"RequireDigit": false
},
"Lockout": {
"AllowedForNewUsers": false,
"LockoutDuration": 5,
"MaxFailedAccessAttempts": 5
},
"SignIn": {
"RequireConfirmedEmail": false,
"RequireConfirmedPhoneNumber": false
}
},
"CAP": {
"EventBus": {
"DefaultGroup": "AuthServer",
"Version": "v1",
"FailedRetryInterval": 300,
"FailedRetryCount": 10
},
"RabbitMQ": {
"HostName": "127.0.0.1",
"Port": 5672,
"UserName": "admin",
"Password": "admin",
"ExchangeName": "LINGYUN.AbpApplication",
"VirtualHost": "multi.service.test"
}
},
"RedisCache": {
"ConnectString": "127.0.0.1",
"RedisPrefix": "AuthServer"
},
"AuthServer": {
"Authority": "http://localhost:44385/",
"ApiName": "auth-service"
},
"WeChat": {
"Auth": {
"AppId": "微信AppId",
"AppSecret": "微信AppSecret"
},
"Signature": {
"RequestPath": "微信开发者中心填写的验证地址",
"Token": "微信开发者中心填写的Token"
}
},
"IdentityServer": {
"Clients": {
"AuthManagement": {
"ClientId": "auth-management",
"RootUrl": "http://localhost:44313/"
},
"AuthVueAdmin": {
"ClientId": "vue-admin-element"
},
"AuthApiGateway": {
"ClientId": "apigateway-host-client"
}
}
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"System": "Warning",
"Microsoft": "Warning",
"DotNetCore": "Information"
}
},
"Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId" ],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/Debug-.log",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Info-.log",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Warn-.log",
"restrictedToMinimumLevel": "Warning",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Error-.log",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Fatal-.log",
"restrictedToMinimumLevel": "Fatal",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
}
]
}
}

110
aspnet-core/configuration/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json

@ -0,0 +1,110 @@
{
"ConnectionStrings": {
"Default": "Server=127.0.0.1;Database=Messages;User Id=root;Password=123456",
"MessageService": "Server=127.0.0.1;Database=Messages;User Id=root;Password=123456",
"AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456"
},
"RedisCache": {
"RedisPrefix": "Platform_Test_Cache",
"ConnectString": "127.0.0.1"
},
"AuthServer": {
"Authority": "http://localhost:44385/",
"ApiName": "auth-service"
},
"Hangfire": {
"MySql": {
"Connection": "Server=127.0.0.1;Database=Messages;User Id=root;Password=123456;Allow User Variables=true",
"TablePrefix": "AppHangfire"
}
},
"WeChat": {
"Auth": {
"AppId": "你自己的微信AppId",
"AppSecret": "你自己的微信AppSecret"
}
},
"Notifications": {
"WeChat": {
"WeApp": {
"DefaultWeAppState": "formal"
}
}
},
"CAP": {
"EventBus": {
"DefaultGroup": "MessageService",
"Version": "v1",
"FailedRetryInterval": 300,
"FailedRetryCount": 10
},
"RabbitMQ": {
"HostName": "127.0.0.1",
"Port": 5672,
"UserName": "admin",
"Password": "admin",
"ExchangeName": "LINGYUN.AbpApplication",
"VirtualHost": "multi.service.test"
}
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft.EntityFrameworkCore": "Debug",
"System": "Warning",
"Microsoft": "Warning"
}
},
"Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId" ],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/Debug-.log",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Info-.log",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Warn-.log",
"restrictedToMinimumLevel": "Warning",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Error-.log",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Fatal-.log",
"restrictedToMinimumLevel": "Fatal",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
}
]
}
}

102
aspnet-core/configuration/LINGYUN.ApiGateway.Host/appsettings.Development.json

@ -0,0 +1,102 @@
{
"ApiGateway": {
"AppId": "TEST-APP"//OcelotAPI,
},
"RemoteServices": {
"ApiGateway": {
"BaseUrl": "http://localhost:30001/",//Ocelot Api,Volo.Abp.HttpClient
"IdentityClient": "apigateway-host-client"
}
},
"IdentityClients": {//OcelotAPI,Volo.Abp.HttpClient.IdentityModel
"apigateway-host-client": {
"Authority": "http://localhost:44385",
"RequireHttps": false,
"GrantType": "client_credentials",
"ClientId": "apigateway-host-client",
"Scope": "apigateway-service",
"ClientSecret": "1q2w3e*",
"UserName": "ocelotHost",
"UserPassword": "Ocelot1."
}
},
"EnabledDynamicOcelot": true,
"CAP": {
"EventBus": {
"DefaultGroup": "ApiGateway-Host",
"Version": "v1",
"FailedRetryInterval": 300,
"FailedRetryCount": 10
},
"RabbitMQ": {
"HostName": "127.0.0.1",
"Port": 5672,
"UserName": "admin",
"Password": "admin",
"ExchangeName": "LINGYUN.ApiGateway",
"VirtualHost": "multi.service.test"
}
},
"AuthServer": {
"Host": "http://localhost:44385/",//IdentityServer
"ApiName": "apigateway-service",//IdentityServerApiName
"ApiSecret": "defj98734htgrb90365D23"//IdentityServerApiSecret
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"System": "Warning",
"Microsoft": "Warning"
}
},
"Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId" ],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/Debug-.log",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Info-.log",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Warn-.log",
"restrictedToMinimumLevel": "Warning",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Error-.log",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Fatal-.log",
"restrictedToMinimumLevel": "Fatal",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
}
]
}
}

91
aspnet-core/configuration/LINGYUN.ApiGateway.HttpApi.Host/appsettings.Development.json

@ -0,0 +1,91 @@
{
"ConnectionStrings": {
"Default": "Server=127.0.0.1;Database=ApiGateway;User Id=root;Password=123456",
"ApiGateway": "Server=127.0.0.1;Database=ApiGateway;User Id=root;Password=123456",
"AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456"
},
"RedisCache": {
"RedisPrefix": "ApiGateway_Test_Cache",
"ConnectString": "127.0.0.1"
},
"CAP": {
"EventBus": {
"DefaultGroup": "ApiGateway-Admin",
"Version": "v1",
"FailedRetryInterval": 300,
"FailedRetryCount": 10
},
"RabbitMQ": {
"HostName": "127.0.0.1",
"Port": 5672,
"UserName": "admin",
"Password": "admin",
"ExchangeName": "LINGYUN.ApiGateway",
"VirtualHost": "multi.service.test"
}
},
"AuthServer": {
"Host": "http://localhost:44385/",
"ApiName": "apigateway-service",
"ApiSecret": "defj98734htgrb90365D23"
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"System": "Warning",
"Microsoft": "Warning"
}
},
"Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId" ],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/Debug-.log",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Info-.log",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Warn-.log",
"restrictedToMinimumLevel": "Warning",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Error-.log",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Fatal-.log",
"restrictedToMinimumLevel": "Fatal",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
}
]
}
}

133
aspnet-core/configuration/LINGYUN.Platform.HttpApi.Host/appsettings.Development.json

@ -0,0 +1,133 @@
{
"ConnectionStrings": {
"Default": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpIdentity": "Server=127.0.0.1;Database=IdentityServer;User Id=root;Password=123456",
"AbpIdentityServer": "Server=127.0.0.1;Database=IdentityServer;User Id=root;Password=123456",
"AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456"
},
"Location": {
"Baidu": {
"AccessKey": "你自己的百度地图WebAPI Key",
"ExtensionsRoad": true,
"ExtensionsTown": true,
"ExtensionsPoi": "1",
"VisableErrorToClient": true
}
},
"Aliyun": {
"Sms": {
"RegionId": "cn-hangzhou",
"Domain": "dysmsapi.aliyuncs.com",
"Version": "2017-05-25",
"AccessKeyId": "你自己的阿里云Sms服务Key",
"AccessKeySecret": "你自己的阿里云Sms服务KeySecret",
"DefaultSignName": "你自己的阿里云Sms服务签名",
"DefaultTemplateCode": "你自己的阿里云Sms服务模板",
"DeveloperPhoneNumber": "你自己的手机号码,用于开发模式统一接收短信的手机号",
"VisableErrorToClient": true
}
},
"Identity": {
"Password": {
"RequiredLength": 6,
"RequiredUniqueChars": 0,
"RequireNonAlphanumeric": false,
"RequireLowercase": false,
"RequireUppercase": false,
"RequireDigit": false
},
"Lockout": {
"AllowedForNewUsers": false,
"LockoutDuration": 5,
"MaxFailedAccessAttempts": 5
},
"SignIn": {
"RequireConfirmedEmail": false,
"RequireConfirmedPhoneNumber": false
}
},
"CAP": {
"EventBus": {
"DefaultGroup": "Platform",
"Version": "v1",
"FailedRetryInterval": 300,
"FailedRetryCount": 10
},
"RabbitMQ": {
"HostName": "127.0.0.1",
"Port": 5672,
"UserName": "admin",
"Password": "admin",
"ExchangeName": "LINGYUN.AbpApplication",
"VirtualHost": "multi.service.test"
}
},
"RedisCache": {
"RedisPrefix": "Platform_Test_Cache",
"ConnectString": "127.0.0.1"
},
"AuthServer": {
"Authority": "http://localhost:44385/",
"ApiName": "auth-service"
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft.EntityFrameworkCore": "Debug",
"System": "Warning",
"Microsoft": "Warning"
}
},
"Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId" ],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/Debug-.log",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Info-.log",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Warn-.log",
"restrictedToMinimumLevel": "Warning",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Error-.log",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Fatal-.log",
"restrictedToMinimumLevel": "Fatal",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
}
]
}
}

2
aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/RegisterVerifyDto.cs → aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/PhoneNumberRegisterDto.cs

@ -4,7 +4,7 @@ using Volo.Abp.Identity;
namespace LINGYUN.Abp.Account
{
public class RegisterVerifyDto
public class PhoneNumberRegisterDto
{
[Required]
[Phone]

28
aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/WeChatRegisterDto.cs

@ -0,0 +1,28 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Auditing;
using Volo.Abp.Identity;
namespace LINGYUN.Abp.Account
{
public class WeChatRegisterDto
{
[Required]
public string Code { get; set; }
[DisableAuditing]
[DataType(DataType.Password)]
[Required]
[StringLength(IdentityUserConsts.MaxPasswordLength)]
public string Password { get; set; }
[StringLength(IdentityUserConsts.MaxNameLength)]
public string Name { get; set; }
[StringLength(IdentityUserConsts.MaxUserNameLength)]
public string UserName { get; set; }
[EmailAddress]
[StringLength(IdentityUserConsts.MaxEmailLength)]
public string EmailAddress { get; set; }
}
}

4
aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs

@ -6,7 +6,9 @@ namespace LINGYUN.Abp.Account
{
public interface IAccountAppService : IApplicationService
{
Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input);
Task<IdentityUserDto> RegisterAsync(PhoneNumberRegisterDto input);
Task<IdentityUserDto> RegisterAsync(WeChatRegisterDto input);
Task ResetPasswordAsync(PasswordResetDto passwordReset);

1
aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj

@ -11,6 +11,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\LINGYUN.Abp.WeChat.Authorization\LINGYUN.Abp.WeChat.Authorization.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.Account.Application.Contracts\LINGYUN.Abp.Account.Application.Contracts.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.Account.Domain\LINGYUN.Abp.Account.Domain.csproj" />
</ItemGroup>

6
aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs

@ -1,11 +1,13 @@
using Volo.Abp.Modularity;
using LINGYUN.Abp.WeChat.Authorization;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Account
{
[DependsOn(
typeof(AbpAccountDomainModule),
typeof(Volo.Abp.Account.AbpAccountApplicationModule),
typeof(AbpAccountApplicationContractsModule))]
typeof(AbpAccountApplicationContractsModule),
typeof(AbpWeChatAuthorizationModule))]
public class AbpAccountApplicationModule : AbpModule
{

34
aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity;
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Threading.Tasks;
@ -8,7 +9,6 @@ using Volo.Abp.Caching;
using Volo.Abp.Identity;
using Volo.Abp.Settings;
using Volo.Abp.Sms;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.Account
{
@ -17,6 +17,8 @@ namespace LINGYUN.Abp.Account
/// </summary>
public class AccountAppService : ApplicationService, IAccountAppService
{
private IWeChatOpenIdFinder _weChatOpenIdFinder;
protected IWeChatOpenIdFinder WeChatOpenIdFinder => LazyGetRequiredService(ref _weChatOpenIdFinder);
protected ISmsSender SmsSender { get; }
protected IdentityUserManager UserManager { get; }
protected IdentityUserStore UserStore { get; }
@ -39,6 +41,32 @@ namespace LINGYUN.Abp.Account
PhoneNumberTokenProvider = phoneNumberTokenProvider;
LocalizationResource = typeof(Localization.AccountResource);
}
public virtual async Task<IdentityUserDto> RegisterAsync(WeChatRegisterDto input)
{
await CheckSelfRegistrationAsync();
var wehchatOpenId = await WeChatOpenIdFinder.FindAsync(input.Code);
var user = await UserManager.FindByLoginAsync("WeChat", wehchatOpenId.OpenId);
if (user == null)
{
var userName = input.UserName ?? wehchatOpenId.OpenId;
var userEmail = input.EmailAddress ?? $"{userName}@{new Random().Next(1000, 99999)}.com";//如果邮件地址不验证,随意写入一个
user = new IdentityUser(GuidGenerator.Create(), userName, userEmail, CurrentTenant.Id)
{
Name = input.Name ?? userName
};
(await UserManager.CreateAsync(user, input.Password)).CheckErrors();
(await UserManager.AddDefaultRolesAsync(user)).CheckErrors();
var userLogin = new UserLoginInfo("WeChat", wehchatOpenId.OpenId, "微信认证登录");
(await UserManager.AddLoginAsync(user, userLogin)).CheckErrors();
}
return ObjectMapper.Map<IdentityUser, IdentityUserDto>(user);
}
/// <summary>
/// 用户注册
/// </summary>
@ -49,7 +77,7 @@ namespace LINGYUN.Abp.Account
/// 如果没有此手机号的缓存记录或验证码不匹配,抛出验证码无效的异常
/// 用户注册成功,清除缓存的验证码记录
/// </remarks>
public virtual async Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input)
public virtual async Task<IdentityUserDto> RegisterAsync(PhoneNumberRegisterDto input)
{
var phoneVerifyCacheKey = NormalizeCacheKey(input.PhoneNumber);

3
aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/en.json

@ -14,6 +14,7 @@
"Description:PhoneVerifyCodeExpiration": "The valid time for the user to send SMS verification code, unit m, default 3m",
"RequiredEmailAddress": "Email address required",
"InvalidPhoneNumber": "Invalid phone number",
"DuplicatePhoneNumber": "The phone number {0} has been registered!"
"DuplicatePhoneNumber": "The phone number {0} has been registered!",
"DuplicateWeChat": "The wechat has been registered!"
}
}

3
aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json

@ -14,6 +14,7 @@
"Description:PhoneVerifyCodeExpiration": "用户发送短信验证码的有效时长,单位m,默认3m",
"RequiredEmailAddress": "邮件地址必须输入",
"InvalidPhoneNumber": "手机号无效",
"DuplicatePhoneNumber": "手机号已经注册过!"
"DuplicatePhoneNumber": "手机号已经注册过!",
"DuplicateWeChat": "微信号已经注册过!"
}
}

4
aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/Microsoft/AspNetCore/Identity/PhoneNumberUserValidator.cs

@ -35,7 +35,9 @@ namespace Microsoft.AspNetCore.Identity
var phoneNumber = await manager.GetPhoneNumberAsync(user);
if (phoneNumber.IsNullOrWhiteSpace())
{
throw new UserFriendlyException(_stringLocalizer["InvalidPhoneNumber"].Value, "InvalidPhoneNumber");
return;
// 如果用户没有手机号,不验证
//throw new UserFriendlyException(_stringLocalizer["InvalidPhoneNumber"].Value, "InvalidPhoneNumber");
}
var phoneNumberHasRegisted = await _identityUserRepository.PhoneNumberHasRegistedAsync(phoneNumber);

17
aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/AccountController.cs

@ -9,7 +9,7 @@ namespace LINGYUN.Abp.Account
{
[RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)]
[Area("account")]
[Route("api/account/phone")]
[Route("api/account")]
public class AccountController : AbpController, IAccountAppService
{
protected IAccountAppService AccountAppService { get; }
@ -20,21 +20,28 @@ namespace LINGYUN.Abp.Account
}
[HttpPost]
[Route("register")]
public virtual async Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input)
[Route("wechat/register")]
public virtual async Task<IdentityUserDto> RegisterAsync(WeChatRegisterDto input)
{
return await AccountAppService.RegisterAsync(input);
}
[HttpPost]
[Route("verify")]
[Route("phone/register")]
public virtual async Task<IdentityUserDto> RegisterAsync(PhoneNumberRegisterDto input)
{
return await AccountAppService.RegisterAsync(input);
}
[HttpPost]
[Route("phone/verify")]
public virtual async Task VerifyPhoneNumberAsync(VerifyDto input)
{
await AccountAppService.VerifyPhoneNumberAsync(input);
}
[HttpPut]
[Route("reset-password")]
[Route("phone/reset-password")]
public virtual async Task ResetPasswordAsync(PasswordResetDto passwordReset)
{
await AccountAppService.ResetPasswordAsync(passwordReset);

7
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN.ApiGateway.Application.Contracts.csproj

@ -3,6 +3,13 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>

26
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/AggregateReRouteAppService.cs

@ -1,11 +1,10 @@
using DotNetCore.CAP;
using LINGYUN.ApiGateway.EventBus;
using LINGYUN.ApiGateway.Snowflake;
using LINGYUN.ApiGateway.EventBus;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.EventBus.Distributed;
namespace LINGYUN.ApiGateway.Ocelot
{
@ -16,14 +15,14 @@ namespace LINGYUN.ApiGateway.Ocelot
[Authorize(ApiGatewayPermissions.AggregateRoute.Default)]
public class AggregateReRouteAppService : ApiGatewayApplicationServiceBase, IAggregateReRouteAppService
{
private readonly ICapPublisher _eventPublisher;
private IDistributedEventBus _eventBus;
protected IDistributedEventBus DistributedEventBus => LazyGetRequiredService(ref _eventBus);
private readonly IAggregateReRouteRepository _aggregateReRouteRepository;
public AggregateReRouteAppService(
ICapPublisher eventPublisher,
IAggregateReRouteRepository aggregateReRouteRepository)
{
_eventPublisher = eventPublisher;
_aggregateReRouteRepository = aggregateReRouteRepository;
}
@ -75,8 +74,7 @@ namespace LINGYUN.ApiGateway.Ocelot
}
aggregateRoute = await _aggregateReRouteRepository.InsertAsync(aggregateRoute);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName,
new ApigatewayConfigChangeCommand("AggregateRoute", "Create"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(aggregateRoute.AppId, "AggregateRoute", "Create"));
return ObjectMapper.Map<AggregateReRoute, AggregateReRouteDto>(aggregateRoute);
}
@ -106,8 +104,7 @@ namespace LINGYUN.ApiGateway.Ocelot
aggregateRoute = await _aggregateReRouteRepository.UpdateAsync(aggregateRoute, true);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName,
new ApigatewayConfigChangeCommand("AggregateRoute", "Update"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(aggregateRoute.AppId, "AggregateRoute", "Update"));
return ObjectMapper.Map<AggregateReRoute, AggregateReRouteDto>(aggregateRoute);
}
@ -119,8 +116,7 @@ namespace LINGYUN.ApiGateway.Ocelot
var aggregateRoute = await _aggregateReRouteRepository.GetByRouteIdAsync(routeId);
await _aggregateReRouteRepository.DeleteAsync(aggregateRoute);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName,
new ApigatewayConfigChangeCommand("AggregateRoute", "Delete"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(aggregateRoute.AppId, "AggregateRoute", "Delete"));
}
[Authorize(ApiGatewayPermissions.AggregateRoute.ManageRouteConfig)]
@ -135,8 +131,7 @@ namespace LINGYUN.ApiGateway.Ocelot
await _aggregateReRouteRepository.UpdateAsync(aggregateRoute);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName,
new ApigatewayConfigChangeCommand("AggregateRoute", "AddRouteConfig"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(aggregateRoute.AppId, "AggregateRoute", "AddRouteConfig"));
return ObjectMapper.Map<AggregateReRouteConfig, AggregateReRouteConfigDto>(aggregateRouteConfig);
}
@ -150,8 +145,7 @@ namespace LINGYUN.ApiGateway.Ocelot
await _aggregateReRouteRepository.UpdateAsync(aggregateRoute);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName,
new ApigatewayConfigChangeCommand("AggregateRoute", "DeleteRouteConfig"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(aggregateRoute.AppId, "AggregateRoute", "DeleteRouteConfig"));
}
}
}

15
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/GlobalConfigurationAppService.cs

@ -1,29 +1,28 @@
using DotNetCore.CAP;
using LINGYUN.ApiGateway.EventBus;
using LINGYUN.ApiGateway.EventBus;
using LINGYUN.ApiGateway.Snowflake;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.EventBus.Distributed;
namespace LINGYUN.ApiGateway.Ocelot
{
[Authorize(ApiGatewayPermissions.Global.Default)]
public class GlobalConfigurationAppService : ApiGatewayApplicationServiceBase, IGlobalConfigurationAppService
{
private IDistributedEventBus _eventBus;
protected IDistributedEventBus DistributedEventBus => LazyGetRequiredService(ref _eventBus);
private readonly IRouteGroupChecker _routeGroupChecker;
private readonly IGlobalConfigRepository _globalConfigRepository;
private readonly ISnowflakeIdGenerator _snowflakeIdGenerator;
private readonly ICapPublisher _eventPublisher;
public GlobalConfigurationAppService(
ICapPublisher eventPublisher,
IRouteGroupChecker routeGroupChecker,
ISnowflakeIdGenerator snowflakeIdGenerator,
IGlobalConfigRepository globalConfigRepository
)
{
_eventPublisher = eventPublisher;
_routeGroupChecker = routeGroupChecker;
_snowflakeIdGenerator = snowflakeIdGenerator;
_globalConfigRepository = globalConfigRepository;
@ -56,7 +55,7 @@ namespace LINGYUN.ApiGateway.Ocelot
globalConfiguration = await _globalConfigRepository.InsertAsync(globalConfiguration, true);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, new ApigatewayConfigChangeCommand("Global", "Create"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(globalConfiguration.AppId, "Global", "Create"));
return ObjectMapper.Map<GlobalConfiguration, GlobalConfigurationDto>(globalConfiguration);
}
@ -75,7 +74,7 @@ namespace LINGYUN.ApiGateway.Ocelot
globalConfiguration = await _globalConfigRepository.UpdateAsync(globalConfiguration, true);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, new ApigatewayConfigChangeCommand("Global", "Modify"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(globalConfiguration.AppId, "Global", "Modify"));
return ObjectMapper.Map<GlobalConfiguration, GlobalConfigurationDto>(globalConfiguration);
}

23
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/ReRouteAppService.cs

@ -1,27 +1,26 @@
using DotNetCore.CAP;
using LINGYUN.ApiGateway.EventBus;
using LINGYUN.ApiGateway.EventBus;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.EventBus.Distributed;
namespace LINGYUN.ApiGateway.Ocelot
{
[Authorize(ApiGatewayPermissions.Route.Default)]
public class ReRouteAppService : ApiGatewayApplicationServiceBase, IReRouteAppService
{
private IDistributedEventBus _eventBus;
protected IDistributedEventBus DistributedEventBus => LazyGetRequiredService(ref _eventBus);
private readonly IRouteGroupChecker _routeGroupChecker;
private readonly IReRouteRepository _reRouteRepository;
private readonly ICapPublisher _eventPublisher;
public ReRouteAppService(
ICapPublisher eventPublisher,
IRouteGroupChecker routeGroupChecker,
IReRouteRepository reRouteRepository
)
{
_eventPublisher = eventPublisher;
_routeGroupChecker = routeGroupChecker;
_reRouteRepository = reRouteRepository;
}
@ -37,7 +36,7 @@ namespace LINGYUN.ApiGateway.Ocelot
var reRouteDto = ObjectMapper.Map<ReRoute, ReRouteDto>(reRoute);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, new ApigatewayConfigChangeCommand("ReRoute", "Create"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(reRoute.AppId, "ReRoute", "Create"));
return reRouteDto;
}
@ -74,7 +73,7 @@ namespace LINGYUN.ApiGateway.Ocelot
var reRouteDto = ObjectMapper.Map<ReRoute, ReRouteDto>(reRoute);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, new ApigatewayConfigChangeCommand("ReRoute", "Modify"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(reRoute.AppId, "ReRoute", "Modify"));
return reRouteDto;
}
@ -116,9 +115,11 @@ namespace LINGYUN.ApiGateway.Ocelot
[Authorize(ApiGatewayPermissions.Route.Delete)]
public async Task DeleteAsync(ReRouteGetByIdInputDto routeGetById)
{
await _reRouteRepository.DeleteAsync(x => x.ReRouteId.Equals(routeGetById.RouteId));
var reRoute = await _reRouteRepository.GetByReRouteIdAsync(routeGetById.RouteId);
await _reRouteRepository.DeleteAsync(reRoute);
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, new ApigatewayConfigChangeCommand("ReRoute", "Delete"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(reRoute.AppId, "ReRoute", "Delete"));
}
[Authorize(ApiGatewayPermissions.Route.Delete)]
@ -128,7 +129,7 @@ namespace LINGYUN.ApiGateway.Ocelot
await _reRouteRepository.DeleteAsync(x => x.AppId.Equals(routeGetByAppId.AppId));
await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, new ApigatewayConfigChangeCommand("ReRoute", "Clean"));
await DistributedEventBus.PublishAsync(new ApigatewayConfigChangeEventData(routeGetByAppId.AppId, "ReRoute", "Clean"));
}
protected virtual void ApplyReRouteOptions(ReRoute reRoute, ReRouteDtoBase routeDto)

7
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain.Shared/LINGYUN.ApiGateway.Domain.Shared.csproj

@ -3,6 +3,13 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>

9
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain.Shared/LINGYUN/ApiGateway/EventBus/ApigatewayConfigChangeCommand.cs → aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain.Shared/LINGYUN/ApiGateway/EventBus/ApigatewayConfigChangeEventData.cs

@ -2,19 +2,20 @@
namespace LINGYUN.ApiGateway.EventBus
{
public class ApigatewayConfigChangeCommand
public class ApigatewayConfigChangeEventData
{
public const string EventName = nameof(ApigatewayConfigChangeCommand);
public DateTime DateTime { get; set; }
public string AppId { get; set; }
public string Method { get; set; }
public string Object { get; set; }
protected ApigatewayConfigChangeCommand()
protected ApigatewayConfigChangeEventData()
{
}
public ApigatewayConfigChangeCommand(string @object, string @method)
public ApigatewayConfigChangeEventData(string appId, string @object, string @method)
{
AppId = appId;
DateTime = DateTime.Now;
Object = @object;
Method = @method;

1
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN.ApiGateway.Domain.csproj

@ -6,7 +6,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetCore.CAP" Version="3.0.3" />
<PackageReference Include="Volo.Abp.Caching" Version="2.9.0" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="2.9.0" />
</ItemGroup>

2
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.EntityFrameworkCore/LINGYUN/ApiGateway/EntityFrameworkCore/ApiGatewayDbContextModelCreatingExtensions.cs

@ -304,7 +304,7 @@ namespace LINGYUN.ApiGateway.EntityFrameworkCore
e.Property(x => x.DownstreamHostAndPorts).HasMaxLength(1000);
e.Property(x => x.DelegatingHandlers).HasMaxLength(1000);
e.HasIndex(i => new { i.DownstreamPathTemplate, i.UpstreamPathTemplate }).IsUnique();
e.HasIndex(i => new { i.AppId, i.DownstreamPathTemplate, i.UpstreamPathTemplate }).IsUnique();
e.ConfigureConcurrencyStamp();
e.ConfigureExtraProperties();

7
aspnet-core/modules/apigateway/LINGYUN.ApiGateway.HttpApi.Client/LINGYUN.ApiGateway.HttpApi.Client.csproj

@ -3,6 +3,13 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>

19
aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN.Abp.Aliyun.Authorization.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Core" Version="2.9.0" />
</ItemGroup>
</Project>

15
aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN/Abp/Aliyun/Authorization/AbpAliyunAuthorizationModule.cs

@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Aliyun.Authorization
{
public class AbpAliyunAuthorizationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpAliyunOptions>(configuration.GetSection("Aliyun:Auth"));
}
}
}

14
aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN/Abp/Aliyun/Authorization/AbpAliyunOptions.cs

@ -0,0 +1,14 @@
namespace LINGYUN.Abp.Aliyun.Authorization
{
public class AbpAliyunOptions
{
/// <summary>
/// 访问标识
/// </summary>
public string AccessKeyId { get; set; }
/// <summary>
/// 访问密钥
/// </summary>
public string AccessKeySecret { get; set; }
}
}

21
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN.Abp.BackgroundJobs.Hangfire.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
<Company />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.HangFire" Version="2.9.0" />
<PackageReference Include="Volo.Abp.BackgroundJobs" Version="2.9.0" />
</ItemGroup>
</Project>

34
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs

@ -0,0 +1,34 @@
using Hangfire;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using Volo.Abp;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Hangfire;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.BackgroundJobs.Hangfire
{
[DependsOn(
typeof(AbpBackgroundJobsAbstractionsModule),
typeof(AbpHangfireModule)
)]
public class AbpBackgroundJobsHangfireModule : AbpModule
{
public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpBackgroundJobOptions>>().Value;
if (!options.IsJobExecutionEnabled)
{
var hangfireOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value;
hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer;
}
}
private BackgroundJobServer CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider)
{
serviceProvider.GetRequiredService<JobStorage>();
return null;
}
}
}

34
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs

@ -0,0 +1,34 @@
using Hangfire;
using System;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundJobs.Hangfire
{
[Dependency(ReplaceServices = true)]
public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDependency
{
public virtual Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
TimeSpan? delay = null)
{
if (!delay.HasValue)
{
return Task.FromResult(
BackgroundJob.Enqueue<HangfireJobExecutionAdapter<TArgs>>(
adapter => adapter.Execute(args)
)
);
}
else
{
return Task.FromResult(
BackgroundJob.Schedule<HangfireJobExecutionAdapter<TArgs>>(
adapter => adapter.Execute(args),
delay.Value
)
);
}
}
}
}

46
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs

@ -0,0 +1,46 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.BackgroundJobs.Hangfire
{
public class HangfireJobExecutionAdapter<TArgs>
{
protected AbpBackgroundJobOptions Options { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IBackgroundJobExecuter JobExecuter { get; }
public HangfireJobExecutionAdapter(
IOptions<AbpBackgroundJobOptions> options,
IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory)
{
JobExecuter = jobExecuter;
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
}
public void Execute(TArgs args)
{
if (!Options.IsJobExecutionEnabled)
{
throw new AbpException(
"Background job execution is disabled. " +
"This method should not be called! " +
"If you want to enable the background job execution, " +
$"set {nameof(AbpBackgroundJobOptions)}.{nameof(AbpBackgroundJobOptions.IsJobExecutionEnabled)} to true! " +
"If you've intentionally disabled job execution and this seems a bug, please report it."
);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var jobType = Options.GetJob(typeof(TArgs)).JobType;
var context = new JobExecutionContext(scope.ServiceProvider, jobType, args);
AsyncHelper.RunSync(() => JobExecuter.ExecuteAsync(context));
}
}
}
}

35
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/LINGYUN/Abp/BackgroundJobs/Hangfire/IBackgroundJobManagerExtensions.cs

@ -0,0 +1,35 @@
using Hangfire;
using JetBrains.Annotations;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.BackgroundJobs;
namespace LINGYUN.Abp.BackgroundJobs.Hangfire
{
public static class IBackgroundJobManagerExtensions
{
/// <summary>
/// 后台作业进入周期性队列
/// </summary>
/// <typeparam name="TArgs">作业参数类型</typeparam>
/// <param name="backgroundJobManager">后台作业管理器</param>
/// <param name="cron">Cron表达式</param>
/// <param name="args">作业参数</param>
/// <returns></returns>
public static Task EnqueueAsync<TArgs>(
this IBackgroundJobManager backgroundJobManager,
[NotNull] string cron,
TArgs args
)
{
Check.NotNullOrWhiteSpace(cron, nameof(cron));
Check.NotNull(args, nameof(args));
var jobName = BackgroundJobNameAttribute.GetName<TArgs>();
RecurringJob.AddOrUpdate<HangfireJobExecutionAdapter<TArgs>>(jobName, adapter => adapter.Execute(args), cron);
return Task.CompletedTask;
}
}
}

78
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs.Hangfire/Volo/Abp/BackgroundJobs/CronGenerator.cs

@ -0,0 +1,78 @@
using Hangfire;
using System;
namespace Volo.Abp.BackgroundJobs
{
public class CronGenerator
{
/// <summary>
/// 周期性为分钟的任务
/// </summary>
/// <param name="interval">执行周期的间隔,默认为每分钟一次</param>
/// <returns></returns>
public static string Minute(int interval = 1)
{
return $"1 0/{interval} * * * ? ";
}
/// <summary>
/// 周期性为小时的任务
/// </summary>
/// <param name="minute">第几分钟开始,默认为第一分钟</param>
/// <param name="interval">执行周期的间隔,默认为每小时一次</param>
/// <returns></returns>
public static string Hour(int minute = 1, int interval = 1)
{
return $"1 {minute} 0/ {interval} * * ? ";
}
/// <summary>
/// 周期性为天的任务
/// </summary>
/// <param name="hour">第几小时开始,默认从1点开始</param>
/// <param name="minute">第几分钟开始,默认从第1分钟开始</param>
/// <param name="interval">执行周期的间隔,默认为每天一次</param>
/// <returns></returns>
public static string Day(int hour = 1, int minute = 1, int interval = 1)
{
return $"1 {minute} {hour} 1/ {interval} * ? ";
}
/// <summary>
/// 周期性为周的任务
/// </summary>
/// <param name="dayOfWeek">星期几开始,默认从星期一点开始</param>
/// <param name="hour">第几小时开始,默认从1点开始</param>
/// <param name="minute">第几分钟开始,默认从第1分钟开始</param>
/// <returns></returns>
public static string Week(DayOfWeek dayOfWeek = DayOfWeek.Monday, int hour = 1, int minute = 1)
{
return Cron.Weekly(dayOfWeek, hour, minute);
}
/// <summary>
/// 周期性为月的任务
/// </summary>
/// <param name="day">几号开始,默认从一号开始</param>
/// <param name="hour">第几小时开始,默认从1点开始</param>
/// <param name="minute">第几分钟开始,默认从第1分钟开始</param>
/// <returns></returns>
public static string Month(int day = 1, int hour = 1, int minute = 1)
{
return Cron.Monthly(day, hour, minute);
}
/// <summary>
/// 周期性为年的任务
/// </summary>
/// <param name="month">几月开始,默认从一月开始</param>
/// <param name="day">几号开始,默认从一号开始</param>
/// <param name="hour">第几小时开始,默认从1点开始</param>
/// <param name="minute">第几分钟开始,默认从第1分钟开始</param>
/// <returns></returns>
public static string Year(int month = 1, int day = 1, int hour = 1, int minute = 1)
{
return Cron.Yearly(month, day, hour, minute);
}
}
}

19
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs/LINGYUN.Abp.BackgroundJobs.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BackgroundJobs" Version="2.9.0" />
</ItemGroup>
</Project>

43
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs/LINGYUN/Abp/BackgroundJobs/RetryAsyncBackgroundJobArgs.cs

@ -0,0 +1,43 @@
namespace LINGYUN.Abp.BackgroundJobs
{
public class RetryAsyncBackgroundJobArgs<TArgs>
{
/// <summary>
/// 重试次数
/// </summary>
public int RetryCount { get; set; } = 0;
/// <summary>
/// 重试间隔(毫秒)
/// 默认 300000ms = 5min
/// </summary>
public double RetryIntervalMillisecond { get; set; } = 300000d;
/// <summary>
/// 最大重试次数
/// 默认 20
/// </summary>
public int MaxRetryCount { get; set; } = 20;
/// <summary>
/// 作业参数
/// </summary>
public TArgs JobArgs { get; set; }
public RetryAsyncBackgroundJobArgs()
{
}
public RetryAsyncBackgroundJobArgs(TArgs jobArgs)
{
JobArgs = jobArgs;
}
public RetryAsyncBackgroundJobArgs(TArgs jobArgs, int retryCount = 0, double interval = 300000d, int maxRetryCount = 20)
{
JobArgs = jobArgs;
RetryCount = retryCount;
RetryIntervalMillisecond = interval;
MaxRetryCount = maxRetryCount;
}
}
}

80
aspnet-core/modules/common/LINGYUN.Abp.BackgroundJobs/LINGYUN/Abp/BackgroundJobs/RetryAsyncBackgroundJobBase.cs

@ -0,0 +1,80 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
namespace LINGYUN.Abp.BackgroundJobs
{
public abstract class RetryAsyncBackgroundJobBase<TArgs> : IAsyncBackgroundJob<RetryAsyncBackgroundJobArgs<TArgs>>
{
public ILogger<RetryAsyncBackgroundJobBase<TArgs>> Logger { get; set; }
protected IBackgroundJobManager BackgroundJobManager { get; }
protected RetryAsyncBackgroundJobBase(
IBackgroundJobManager backgroundJobManager)
{
BackgroundJobManager = backgroundJobManager;
Logger = NullLogger<RetryAsyncBackgroundJobBase<TArgs>>.Instance;
}
public async Task ExecuteAsync(RetryAsyncBackgroundJobArgs<TArgs> args)
{
if (args.RetryCount > args.MaxRetryCount)
{
Logger.LogWarning("Job has failed and the maximum number of retries has been reached. The failure callback is about to enter");
// 任务执行失败次数已达上限,调用用户定义回调,并不再执行
await OnJobExecuteFailedAsync(args.JobArgs);
return;
}
try
{
// 执行任务
await ExecuteAsync(args.JobArgs, args.RetryCount);
// 执行完成后回调
await OnJobExecuteCompletedAsync(args.JobArgs);
}
catch(Exception ex)
{
Logger.LogWarning("Job execution has failed and a retry is imminent");
Logger.LogWarning("Job running error:{0}", ex.Message);
// 每次重试 间隔时间增加1.1倍
var retryInterval = args.RetryIntervalMillisecond * 1.1;
var retryJobArgs = new RetryAsyncBackgroundJobArgs<TArgs>(args.JobArgs,
args.RetryCount + 1, retryInterval, args.MaxRetryCount);
Logger.LogDebug("Job task is queued for the next execution");
// 计算优先级
BackgroundJobPriority priority = BackgroundJobPriority.Normal;
if (args.RetryCount <= (args.MaxRetryCount / 2) &&
args.RetryCount > (args.MaxRetryCount / 3))
{
priority = BackgroundJobPriority.BelowNormal;
}
else if (args.RetryCount > (args.MaxRetryCount / 1.5))
{
priority = BackgroundJobPriority.Low;
}
// 延迟入队,等待下一次运行
await BackgroundJobManager.EnqueueAsync(retryJobArgs, priority, delay: TimeSpan.FromMilliseconds(retryInterval));
}
}
protected abstract Task ExecuteAsync(TArgs args, int retryCount);
protected virtual Task OnJobExecuteFailedAsync(TArgs args)
{
return Task.CompletedTask;
}
protected virtual Task OnJobExecuteCompletedAsync(TArgs args)
{
return Task.CompletedTask;
}
}
}

25
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN.Abp.BlobStoring.Aliyun.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
<Description>阿里云Oss对象存储Abp集成</Description>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.10.0" />
<PackageReference Include="Volo.Abp.BlobStoring" Version="2.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Aliyun.Authorization\LINGYUN.Abp.Aliyun.Authorization.csproj" />
</ItemGroup>
</Project>

35
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs

@ -0,0 +1,35 @@
using LINGYUN.Abp.Aliyun.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using Volo.Abp.BlobStoring;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.BlobStoring.Aliyun
{
[DependsOn(
typeof(AbpBlobStoringModule),
typeof(AbpAliyunAuthorizationModule))]
public class AbpBlobStoringAliyunModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpBlobStoringOptions>(options =>
{
context.Services.ExecutePreConfiguredActions(options);
options.Containers.ConfigureAll((containerName, containerConfiguration) =>
{
containerConfiguration.UseAliyun(aliyun =>
{
aliyun.BucketName = configuration[AliyunBlobProviderConfigurationNames.BucketName] ?? "";
aliyun.CreateBucketIfNotExists = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists).Get<bool>();
aliyun.CreateBucketReferer = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketReferer).Get<List<string>>();
aliyun.Endpoint = configuration[AliyunBlobProviderConfigurationNames.Endpoint];
});
});
});
}
}
}

25
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobContainerConfigurationExtensions.cs

@ -0,0 +1,25 @@
using System;
using Volo.Abp.BlobStoring;
namespace LINGYUN.Abp.BlobStoring.Aliyun
{
public static class AliyunBlobContainerConfigurationExtensions
{
public static AliyunBlobProviderConfiguration GetAliyunConfiguration(
this BlobContainerConfiguration containerConfiguration)
{
return new AliyunBlobProviderConfiguration(containerConfiguration);
}
public static BlobContainerConfiguration UseAliyun(
this BlobContainerConfiguration containerConfiguration,
Action<AliyunBlobProviderConfiguration> aliyunConfigureAction)
{
containerConfiguration.ProviderType = typeof(AliyunBlobProvider);
aliyunConfigureAction(new AliyunBlobProviderConfiguration(containerConfiguration));
return containerConfiguration;
}
}
}

148
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs

@ -0,0 +1,148 @@
using Aliyun.OSS;
using LINGYUN.Abp.Aliyun.Authorization;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BlobStoring.Aliyun
{
public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
{
protected AbpAliyunOptions Options { get; }
protected IAliyunBlobNameCalculator AliyunBlobNameCalculator { get; }
public AliyunBlobProvider(
IOptions<AbpAliyunOptions> options,
IAliyunBlobNameCalculator aliyunBlobNameCalculator)
{
Options = options.Value;
AliyunBlobNameCalculator = aliyunBlobNameCalculator;
}
public override async Task<bool> DeleteAsync(BlobProviderDeleteArgs args)
{
var blobName = AliyunBlobNameCalculator.Calculate(args);
if (await BlobExistsAsync(args, blobName))
{
var ossClient = GetOssClient(args);
return ossClient.DeleteObject(GetBucketName(args), blobName).DeleteMarker;
}
return false;
}
public override async Task<bool> ExistsAsync(BlobProviderExistsArgs args)
{
var blobName = AliyunBlobNameCalculator.Calculate(args);
return await BlobExistsAsync(args, blobName);
}
public override async Task<Stream> GetOrNullAsync(BlobProviderGetArgs args)
{
var blobName = AliyunBlobNameCalculator.Calculate(args);
if (!await BlobExistsAsync(args, blobName))
{
return null;
}
var ossClient = GetOssClient(args);
var ossObject = ossClient.GetObject(GetBucketName(args), blobName);
// 返回原始结果才会调用 Stream.ReadAsync();
return ossObject.Content;
}
public override async Task SaveAsync(BlobProviderSaveArgs args)
{
var blobName = AliyunBlobNameCalculator.Calculate(args);
var configuration = args.Configuration.GetAliyunConfiguration();
if (!args.OverrideExisting && await BlobExistsAsync(args, blobName))
{
throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the bucketName '{GetBucketName(args)}'! Set {nameof(args.OverrideExisting)} if it should be overwritten.");
}
if (configuration.CreateBucketIfNotExists)
{
await CreateBucketIfNotExists(args, configuration.CreateBucketReferer);
}
var bucketName = GetBucketName(args);
var ossClient = GetOssClient(args);
if (args.OverrideExisting && await BlobExistsAsync(args, blobName))
{
ossClient.DeleteObject(bucketName, blobName);
}
ossClient.PutObject(bucketName, blobName, args.BlobStream);
}
protected virtual OssClient GetOssClient(BlobProviderArgs args)
{
var configuration = args.Configuration.GetAliyunConfiguration();
var ossClient = new OssClient(configuration.Endpoint, Options.AccessKeyId, Options.AccessKeySecret);
return ossClient;
}
protected virtual async Task CreateBucketIfNotExists(BlobProviderArgs args, IList<string> refererList = null)
{
if (! await BucketExistsAsync(args))
{
var ossClient = GetOssClient(args);
var bucketName = GetBucketName(args);
var request = new CreateBucketRequest(bucketName)
{
//设置存储空间访问权限ACL。
ACL = CannedAccessControlList.PublicReadWrite,
//设置数据容灾类型。
DataRedundancyType = DataRedundancyType.ZRS
};
ossClient.CreateBucket(request);
if (refererList != null && refererList.Count > 0)
{
var srq = new SetBucketRefererRequest(bucketName, refererList);
ossClient.SetBucketReferer(srq);
}
}
}
private async Task<bool> BlobExistsAsync(BlobProviderArgs args, string blobName)
{
var ossClient = GetOssClient(args);
var bucketExists = await BucketExistsAsync(args);
if (bucketExists)
{
var objectExists = ossClient.DoesObjectExist(GetBucketName(args), blobName);
return objectExists;
}
return false;
}
private Task<bool> BucketExistsAsync(BlobProviderArgs args)
{
var ossClient = GetOssClient(args);
var bucketExists = ossClient.DoesBucketExist(GetBucketName(args));
return Task.FromResult(bucketExists);
}
private static string GetBucketName(BlobProviderArgs args)
{
var configuration = args.Configuration.GetAliyunConfiguration();
return configuration.BucketName.IsNullOrWhiteSpace()
? args.ContainerName
: configuration.BucketName;
}
}
}

62
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs

@ -0,0 +1,62 @@
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.BlobStoring;
namespace LINGYUN.Abp.BlobStoring.Aliyun
{
public class AliyunBlobProviderConfiguration
{
/// <summary>
/// 数据中心
/// </summary>
/// <remarks>
/// 详见 https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.14.417cd47eLc9LHc#concept-zt4-cvy-5db
/// </remarks>
public string Endpoint
{
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.Endpoint);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint, Check.NotNullOrWhiteSpace(value, nameof(value)));
}
/// <summary>
/// 命名空间
/// </summary>
public string BucketName
{
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.BucketName);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.BucketName, Check.NotNullOrWhiteSpace(value, nameof(value)));
}
/// <summary>
/// 命名空间不存在是否创建
/// </summary>
public bool CreateBucketIfNotExists
{
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists, false);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists, value);
}
/// <summary>
/// 创建命名空间时防盗链列表
/// </summary>
public List<string> CreateBucketReferer
{
get => _containerConfiguration.GetConfiguration<List<string>>(AliyunBlobProviderConfigurationNames.CreateBucketReferer);
set
{
if (value == null)
{
_containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.CreateBucketReferer, new List<string>());
}
else
{
_containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.CreateBucketReferer, value);
}
}
}
private readonly BlobContainerConfiguration _containerConfiguration;
public AliyunBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration)
{
_containerConfiguration = containerConfiguration;
}
}
}

22
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs

@ -0,0 +1,22 @@
namespace LINGYUN.Abp.BlobStoring.Aliyun
{
public static class AliyunBlobProviderConfigurationNames
{
/// <summary>
/// 数据中心
/// </summary>
public const string Endpoint = "Aliyun:OSS:Endpoint";
/// <summary>
/// 命名空间
/// </summary>
public const string BucketName = "Aliyun:OSS:BucketName";
/// <summary>
/// 命名空间不存在是否创建
/// </summary>
public const string CreateBucketIfNotExists = "Aliyun:OSS:CreateBucketIfNotExists";
/// <summary>
/// 创建命名空间时防盗链列表
/// </summary>
public const string CreateBucketReferer = "Aliyun:OSS:CreateBucketReferer";
}
}

24
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNameCalculator.cs

@ -0,0 +1,24 @@
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BlobStoring.Aliyun
{
public class DefaultAliyunBlobNameCalculator : IAliyunBlobNameCalculator, ITransientDependency
{
protected ICurrentTenant CurrentTenant { get; }
public DefaultAliyunBlobNameCalculator(
ICurrentTenant currentTenant)
{
CurrentTenant = currentTenant;
}
public string Calculate(BlobProviderArgs args)
{
return CurrentTenant.Id == null
? $"host/{args.BlobName}"
: $"tenants/{CurrentTenant.Id.Value:D}/{args.BlobName}";
}
}
}

9
aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/IAliyunBlobNameCalculator.cs

@ -0,0 +1,9 @@
using Volo.Abp.BlobStoring;
namespace LINGYUN.Abp.BlobStoring.Aliyun
{
public interface IAliyunBlobNameCalculator
{
string Calculate(BlobProviderArgs args);
}
}

2
aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN.Abp.EventBus.CAP.csproj

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetCore.CAP" Version="3.0.3" />
<PackageReference Include="DotNetCore.CAP" Version="3.0.4" />
<PackageReference Include="Volo.Abp.BackgroundWorkers" Version="2.9.0" />
<PackageReference Include="Volo.Abp.EventBus" Version="2.9.0" />
</ItemGroup>

22
aspnet-core/modules/common/LINGYUN.Abp.Hangfire.MySqlStorage/LINGYUN.Abp.Hangfire.Storage.MySql.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Company>LINGYUN</Company>
<Authors>LINGYUN</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Hangfire.MySql.Core" Version="2.2.5" />
<PackageReference Include="Volo.Abp.HangFire" Version="2.9.0" />
</ItemGroup>
</Project>

38
aspnet-core/modules/common/LINGYUN.Abp.Hangfire.MySqlStorage/LINGYUN/Abp/Hangfire/Storage/MySql/AbpHangfireMySqlStorageModule.cs

@ -0,0 +1,38 @@
using Hangfire;
using Hangfire.MySql.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Hangfire;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Hangfire.Storage.MySql
{
[DependsOn(typeof(AbpHangfireModule))]
public class AbpHangfireMySqlStorageModule : AbpModule
{
private MySqlStorage _jobStorage;
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var mysqlStorageOptions = new MySqlStorageOptions();
configuration.GetSection("Hangfire:MySql").Bind(mysqlStorageOptions);
var hangfireMySqlConfiguration = configuration.GetSection("Hangfire:MySql:Connection");
var hangfireMySqlCon = hangfireMySqlConfiguration.Exists()
? hangfireMySqlConfiguration.Value : configuration.GetConnectionString("Default");
_jobStorage = new MySqlStorage(hangfireMySqlCon, mysqlStorageOptions);
context.Services.AddSingleton<JobStorage, MySqlStorage>(fac =>
{
return _jobStorage;
});
context.Services.AddHangfire(config =>
{
config.UseStorage(_jobStorage);
});
}
}
}

20
aspnet-core/modules/common/LINGYUN.Abp.Hangfire.Storage.SqlServer/LINGYUN.Abp.Hangfire.Storage.SqlServer.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Authors>LINGYUN</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HangFire.SqlServer" Version="1.7.11" />
<PackageReference Include="Volo.Abp.HangFire" Version="2.9.0" />
</ItemGroup>
</Project>

38
aspnet-core/modules/common/LINGYUN.Abp.Hangfire.Storage.SqlServer/LINGYUN/Abp/Hangfire/Storage/SqlServer/AbpHangfireSqlServerStorageModule.cs

@ -0,0 +1,38 @@
using Hangfire;
using Hangfire.SqlServer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Hangfire;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Hangfire.Storage.SqlServer
{
[DependsOn(typeof(AbpHangfireModule))]
public class AbpHangfireSqlServerStorageModule : AbpModule
{
private SqlServerStorage _jobStorage;
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var sqlserverStorageOptions = new SqlServerStorageOptions();
configuration.GetSection("Hangfire:SqlServer").Bind(sqlserverStorageOptions);
var hangfireSqlServerConfiguration = configuration.GetSection("Hangfire:SqlServer:Connection");
var hangfireSqlServerCon = hangfireSqlServerConfiguration.Exists()
? hangfireSqlServerConfiguration.Value : configuration.GetConnectionString("Default");
_jobStorage = new SqlServerStorage(hangfireSqlServerCon, sqlserverStorageOptions);
context.Services.AddSingleton<JobStorage, SqlServerStorage>(fac =>
{
return _jobStorage;
});
context.Services.AddHangfire(config =>
{
config.UseStorage(_jobStorage);
});
}
}
}

8
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/Class1.cs

@ -1,8 +0,0 @@
using System;
namespace LINGYUN.Abp.IdentityServer.WeChatValidator
{
public class Class1
{
}
}

14
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj

@ -5,8 +5,22 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\IdentityServer\Localization\WeChatValidator\en.json" />
<None Remove="LINGYUN\Abp\IdentityServer\Localization\WeChatValidator\zh-Hans.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\IdentityServer\Localization\WeChatValidator\en.json" />
<EmbeddedResource Include="LINGYUN\Abp\IdentityServer\Localization\WeChatValidator\zh-Hans.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.IdentityServer.Domain" Version="2.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\modules\common\LINGYUN.Abp.WeChat.Authorization\LINGYUN.Abp.WeChat.Authorization.csproj" />
</ItemGroup>
</Project>

44
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs

@ -0,0 +1,44 @@
using LINGYUN.Abp.IdentityServer.WeChatValidator;
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.IdentityServer;
using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.IdentityServer
{
[DependsOn(
typeof(AbpWeChatAuthorizationModule),
typeof(AbpIdentityServerDomainModule))]
public class AbpIdentityServerWeChatValidatorModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IIdentityServerBuilder>(builder =>
{
builder.AddExtensionGrantValidator<WeChatTokenGrantValidator>();
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<WeChatSignatureOptions>(configuration.GetSection("WeChat:Signature"));
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpIdentityServerWeChatValidatorModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<AbpIdentityServerResource>()
.AddVirtualJson("/LINGYUN/Abp/IdentityServer/Localization/WeChatValidator");
});
}
}
}

9
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/Localization/WeChatValidator/en.json

@ -0,0 +1,9 @@
{
"culture": "en",
"texts": {
"InvalidGrant:GrantTypeInvalid": "The type of authorization that is not allowed!",
"InvalidGrant:WeChatTokenInvalid": "WeChat authentication failed!",
"InvalidGrant:WeChatCodeNotFound": "The code obtained when WeChat is logged in is empty or does not exist!",
"InvalidGrant:WeChatNotRegister": "User WeChat account not registed!"
}
}

9
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/Localization/WeChatValidator/zh-Hans.json

@ -0,0 +1,9 @@
{
"culture": "zh-Hans",
"texts": {
"InvalidGrant:GrantTypeInvalid": "不被允许的授权类型!",
"InvalidGrant:WeChatTokenInvalid": "微信认证失败!",
"InvalidGrant:WeChatCodeNotFound": "微信登录时获取的 code 为空或不存在!",
"InvalidGrant:WeChatNotRegister": "用户微信账号未绑定!"
}
}

78
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatSignatureMiddleware.cs

@ -0,0 +1,78 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.IdentityServer
{
public class WeChatSignatureMiddleware : IMiddleware, ITransientDependency
{
protected WeChatSignatureOptions Options { get; }
public WeChatSignatureMiddleware(IOptions<WeChatSignatureOptions> options)
{
Options = options.Value;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.Request.Path.HasValue)
{
var requestPath = context.Request.Path.Value;
// 访问地址是否与定义的地址匹配
if (requestPath.Equals(Options.RequestPath))
{
var timestamp = context.Request.Query["timestamp"];
var nonce = context.Request.Query["nonce"];
var signature = context.Request.Query["signature"];
var echostr = context.Request.Query["echostr"];
// 验证消息合法性
var check = CheckWeChatSignature(Options.Token, timestamp, nonce, signature);
if (check)
{
// 验证通过需要把微信服务器传递的字符原封不动传回
await context.Response.WriteAsync(echostr);
return;
}
// 微信消息验证不通过
throw new AbpException("Invalid wechat signature");
}
}
// 不属于微信的消息进入下一个中间件
await next(context);
}
protected bool CheckWeChatSignature(string token, string timestamp, string nonce, string signature)
{
var al = new ArrayList
{
token,
timestamp,
nonce
};
// step1 排序
al.Sort();
string signatureStr = string.Empty;
// step2 拼接
for (int i = 0; i < al.Count; i++)
{
signatureStr += al[i];
}
// step3 SHA1加密
using var sha1 = new SHA1CryptoServiceProvider();
byte[] bytes_in = Encoding.ASCII.GetBytes(signatureStr);
byte[] bytes_out = sha1.ComputeHash(bytes_in);
string result = BitConverter.ToString(bytes_out).Replace("-", "");
// step4 比对
if (result.Equals(signature, StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
return false;
}
}
}

16
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatSignatureOptions.cs

@ -0,0 +1,16 @@
namespace LINGYUN.Abp.IdentityServer
{
public class WeChatSignatureOptions
{
/// <summary>
/// 微信服务器请求路径
/// 填写在微信开发者中心配置的地址
/// </summary>
public string RequestPath { get; set; }
/// <summary>
/// 微信服务器请求token
/// 填写在微信开发者中心配置的token
/// </summary>
public string Token { get; set; }
}
}

106
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs

@ -0,0 +1,106 @@
using IdentityModel;
using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Validation;
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Security.Claims;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace LINGYUN.Abp.IdentityServer.WeChatValidator
{
public class WeChatTokenGrantValidator : IExtensionGrantValidator
{
protected ILogger<WeChatTokenGrantValidator> Logger { get; }
protected AbpWeChatOptions Options { get; }
protected IHttpClientFactory HttpClientFactory{ get; }
protected IEventService EventService { get; }
protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; }
protected IIdentityUserRepository UserRepository { get; }
protected UserManager<IdentityUser> UserManager { get; }
protected SignInManager<IdentityUser> SignInManager { get; }
protected IStringLocalizer<AbpIdentityServerResource> Localizer { get; }
protected PhoneNumberTokenProvider<IdentityUser> PhoneNumberTokenProvider { get; }
public WeChatTokenGrantValidator(
IEventService eventService,
IWeChatOpenIdFinder weChatOpenIdFinder,
IHttpClientFactory httpClientFactory,
UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository,
SignInManager<IdentityUser> signInManager,
IStringLocalizer<AbpIdentityServerResource> stringLocalizer,
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider,
IOptions<AbpWeChatOptions> options,
ILogger<WeChatTokenGrantValidator> logger)
{
Logger = logger;
Options = options.Value;
EventService = eventService;
UserManager = userManager;
SignInManager = signInManager;
Localizer = stringLocalizer;
UserRepository = userRepository;
WeChatOpenIdFinder = weChatOpenIdFinder;
HttpClientFactory = httpClientFactory;
PhoneNumberTokenProvider = phoneNumberTokenProvider;
}
public string GrantType => WeChatValidatorConsts.WeChatValidatorGrantTypeName;
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var raw = context.Request.Raw;
var credential = raw.Get(OidcConstants.TokenRequest.GrantType);
if (credential == null || !credential.Equals(GrantType))
{
Logger.LogWarning("Invalid grant type: not allowed");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:GrantTypeInvalid"]);
return;
}
var wechatCode = raw.Get(WeChatValidatorConsts.WeChatValidatorTokenName);
if (wechatCode.IsNullOrWhiteSpace() || wechatCode.IsNullOrWhiteSpace())
{
Logger.LogWarning("Invalid grant type: wechat code not found");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatCodeNotFound"]);
return;
}
var whchatOpenId = await WeChatOpenIdFinder.FindAsync(wechatCode);
var currentUser = await UserManager.FindByLoginAsync("WeChat", whchatOpenId.OpenId);
if(currentUser == null)
{
Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", whchatOpenId.OpenId);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatNotRegister"]);
return;
}
var sub = await UserManager.GetUserIdAsync(currentUser);
var additionalClaims = new List<Claim>();
if (currentUser.TenantId.HasValue)
{
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString()));
}
additionalClaims.Add(new Claim(WeChatValidatorConsts.ClaimTypes.OpenId, whchatOpenId.OpenId));
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, whchatOpenId.OpenId, null));
context.Result = new GrantValidationResult(sub,
WeChatValidatorConsts.AuthenticationMethods.BasedWeChatAuthentication, additionalClaims.ToArray());
}
}
}

21
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatValidatorConsts.cs

@ -0,0 +1,21 @@
namespace LINGYUN.Abp.IdentityServer.WeChatValidator
{
public class WeChatValidatorConsts
{
public const string WeChatValidatorClientName = "WeChatValidator";
public const string WeChatValidatorGrantTypeName = "wechat";
public const string WeChatValidatorTokenName = "code";
public class ClaimTypes
{
public const string OpenId = "wx-openid";
}
public class AuthenticationMethods
{
public const string BasedWeChatAuthentication = "wca";
}
}
}

22
aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Builder/IdentityServerApplicationBuilderExtensions.cs

@ -0,0 +1,22 @@
using LINGYUN.Abp.IdentityServer;
namespace Microsoft.AspNetCore.Builder
{
public static class IdentityServerApplicationBuilderExtensions
{
/// <summary>
/// 启用中间件可以处理微信服务器消息
/// 用于验证消息是否来自于微信服务器
/// </summary>
/// <param name="builder"></param>
/// <remarks>
/// 也可以用Controller的形式来实现
/// </remarks>
/// <returns></returns>
public static IApplicationBuilder UseWeChatSignature(this IApplicationBuilder builder)
{
builder.UseMiddleware<WeChatSignatureMiddleware>();
return builder;
}
}
}

8
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/AbpNotificationsSignalRModule.cs

@ -10,6 +10,12 @@ namespace LINGYUN.Abp.Notifications.SignalR
typeof(AbpAspNetCoreSignalRModule))]
public class AbpNotificationsSignalRModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpNotificationOptions>(options =>
{
options.PublishProviders.Add<SignalRNotificationPublishProvider>();
});
}
}
}

4
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs

@ -22,9 +22,9 @@ namespace LINGYUN.Abp.Notifications.SignalR.Hubs
}
[HubMethodName("ChangeState")]
public virtual async Task ChangeStateAsync(long id, NotificationReadState readState = NotificationReadState.Read)
public virtual async Task ChangeStateAsync(string id, NotificationReadState readState = NotificationReadState.Read)
{
await NotificationStore.ChangeUserNotificationReadStateAsync(CurrentTenant.Id, CurrentUser.GetId(), id, readState);
await NotificationStore.ChangeUserNotificationReadStateAsync(CurrentTenant.Id, CurrentUser.GetId(), long.Parse(id), readState);
}

30
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublisher.cs → aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs

@ -2,54 +2,58 @@
using LINGYUN.Abp.RealTime.Client;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Notifications.SignalR
{
public class SignalRNotificationPublisher : INotificationPublisher, ISingletonDependency
public class SignalRNotificationPublishProvider : NotificationPublishProvider
{
public ILogger<SignalRNotificationPublisher> Logger { protected get; set; }
public const string ProviderName = "SignalR";
public override string Name => ProviderName;
private readonly IOnlineClientManager _onlineClientManager;
private readonly IHubContext<NotificationsHub> _hubContext;
public SignalRNotificationPublisher(
public SignalRNotificationPublishProvider(
IOnlineClientManager onlineClientManager,
IHubContext<NotificationsHub> hubContext)
IHubContext<NotificationsHub> hubContext,
IServiceProvider serviceProvider)
: base(serviceProvider)
{
_hubContext = hubContext;
_onlineClientManager = onlineClientManager;
Logger = NullLogger<SignalRNotificationPublisher>.Instance;
}
public async Task PublishAsync(NotificationInfo notification, IEnumerable<Guid> userIds)
public override async Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers)
{
foreach(var userId in userIds)
// 返回标准数据给前端
notification.Data = NotificationData.ToStandardData(notification.Data);
foreach (var identifier in identifiers)
{
var onlineClientContext = new OnlineClientContext(notification.TenantId, userId);
Logger.LogDebug($"Find online client with user {identifier.UserId} - {identifier.UserName}");
var onlineClientContext = new OnlineClientContext(notification.TenantId, identifier.UserId);
var onlineClients = _onlineClientManager.GetAllByContext(onlineClientContext);
foreach (var onlineClient in onlineClients)
{
try
{
Logger.LogDebug($"Find online client {onlineClient.UserId} - {onlineClient.ConnectionId}");
var signalRClient = _hubContext.Clients.Client(onlineClient.ConnectionId);
if (signalRClient == null)
{
Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " with connectionId " + onlineClient.ConnectionId + " from SignalR hub!");
continue;
}
Logger.LogDebug($"Found a singalr client, begin senging notifications");
await signalRClient.SendAsync("getNotification", notification);
}
catch (Exception ex)
{
Logger.LogWarning("Could not send notifications to user: {0}", userId);
Logger.LogWarning("Could not send notifications to user: {0}", identifier.UserId);
Logger.LogWarning("Send to user notifications error: {0}", ex.Message);
}
}

21
aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.WeApp.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.9.0</Version>
<Description>通知接口的微信小程序发布者实现</Description>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>D:\LocalNuget</OutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WeChat.Authorization\LINGYUN.Abp.WeChat.Authorization.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" />
</ItemGroup>
</Project>

30
aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpNotificationsWeChatWeAppModule.cs

@ -0,0 +1,30 @@
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Polly;
using System;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Notifications.WeChat.WeApp
{
[DependsOn(
typeof(AbpWeChatAuthorizationModule),
typeof(AbpNotificationModule))]
public class AbpNotificationsWeChatWeAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpWeChatWeAppNotificationOptions>(configuration.GetSection("Notifications:WeChat:WeApp"));
// TODO:是否有必要启用重试机制?
context.Services.AddHttpClient(WeChatWeAppNotificationSender.SendNotificationClientName)
.AddTransientHttpErrorPolicy(builder =>
builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i))));
Configure<AbpNotificationOptions>(options =>
{
options.PublishProviders.Add<WeChatWeAppNotificationPublishProvider>();
});
}
}
}

22
aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpWeChatWeAppNotificationOptions.cs

@ -0,0 +1,22 @@
namespace LINGYUN.Abp.Notifications.WeChat.WeApp
{
public class AbpWeChatWeAppNotificationOptions
{
/// <summary>
/// 默认消息头部标记
/// </summary>
public string DefaultMsgPrefix { get; set; } = "[wx]";
/// <summary>
/// 默认小程序模板
/// </summary>
public string DefaultTemplateId { get; set; }
/// <summary>
/// 默认跳转小程序类型
/// </summary>
public string DefaultWeAppState { get; set; } = "developer";
/// <summary>
/// 默认小程序语言
/// </summary>
public string DefaultWeAppLanguage { get; set; } = "zh_CN";
}
}

9
aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/IWeChatWeAppNotificationSender.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications.WeChat.WeApp
{
public interface IWeChatWeAppNotificationSender
{
Task SendAsync(WeChatWeAppSendNotificationData notificationData);
}
}

84
aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs

@ -0,0 +1,84 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications.WeChat.WeApp
{
/// <summary>
/// 微信小程序消息推送提供者
/// </summary>
public class WeChatWeAppNotificationPublishProvider : NotificationPublishProvider
{
public override string Name => "WeChat.WeApp";
protected IWeChatWeAppNotificationSender NotificationSender { get; }
protected AbpWeChatWeAppNotificationOptions Options { get; }
public WeChatWeAppNotificationPublishProvider(
IServiceProvider serviceProvider,
IWeChatWeAppNotificationSender notificationSender,
IOptions<AbpWeChatWeAppNotificationOptions> options)
: base(serviceProvider)
{
Options = options.Value;
NotificationSender = notificationSender;
}
public override async Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers)
{
// step1 默认微信openid绑定的就是username,
// 如果不是,需要自行处理openid获取逻辑
// step2 调用微信消息推送接口
foreach (var identifier in identifiers)
{
await SendWeChatTemplateMessagAsync(notification, identifier);
}
}
protected virtual async Task SendWeChatTemplateMessagAsync(NotificationInfo notification, UserIdentifier identifier)
{
var templateId = GetOrDefaultTemplateId(notification.Data);
Logger.LogDebug($"Get wechat weapp template id: {templateId}");
var redirect = GetOrDefault(notification.Data, "RedirectPage", null);
Logger.LogDebug($"Get wechat weapp redirect page: {redirect ?? "null"}");
var weAppState = GetOrDefault(notification.Data, "WeAppState", Options.DefaultWeAppState);
Logger.LogDebug($"Get wechat weapp state: {weAppState ?? null}");
var weAppLang = GetOrDefault(notification.Data, "WeAppLanguage", Options.DefaultWeAppLanguage);
Logger.LogDebug($"Get wechat weapp language: {weAppLang ?? null}");
var weChatWeAppNotificationData = new WeChatWeAppSendNotificationData(identifier.UserName,
templateId, redirect, weAppState, weAppLang);
// 写入模板数据
weChatWeAppNotificationData.WriteStandardData(NotificationData.ToStandardData(Options.DefaultMsgPrefix, notification.Data));
// weChatWeAppNotificationData.WriteData(Options.DefaultMsgPrefix, notification.Data.Properties);
Logger.LogDebug($"Sending wechat weapp notification: {notification.Name}");
// 发送小程序订阅消息
await NotificationSender.SendAsync(weChatWeAppNotificationData);
}
protected string GetOrDefaultTemplateId(NotificationData data)
{
return GetOrDefault(data, "TemplateId", Options.DefaultTemplateId);
}
protected string GetOrDefault(NotificationData data, string key, string defaultValue)
{
if (data.Properties.TryGetValue(key, out object value))
{
// 取得了数据就删除对应键值
// data.Properties.Remove(key);
return value.ToString();
}
return defaultValue;
}
}
}

108
aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationSender.cs

@ -0,0 +1,108 @@
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace LINGYUN.Abp.Notifications.WeChat.WeApp
{
public class WeChatWeAppNotificationSender : IWeChatWeAppNotificationSender, ITransientDependency
{
public const string SendNotificationClientName = "WeChatWeAppSendNotificationClient";
public ILogger<WeChatWeAppNotificationSender> Logger { get; set; }
protected IHttpClientFactory HttpClientFactory { get; }
protected IJsonSerializer JsonSerializer { get; }
protected IWeChatTokenProvider WeChatTokenProvider { get; }
public WeChatWeAppNotificationSender(
IJsonSerializer jsonSerializer,
IHttpClientFactory httpClientFactory,
IWeChatTokenProvider weChatTokenProvider)
{
JsonSerializer = jsonSerializer;
HttpClientFactory = httpClientFactory;
WeChatTokenProvider = weChatTokenProvider;
Logger = NullLogger<WeChatWeAppNotificationSender>.Instance;
}
public virtual async Task SendAsync(WeChatWeAppSendNotificationData notificationData)
{
var weChatToken = await WeChatTokenProvider.GetTokenAsync();
var requestParamters = new Dictionary<string, string>
{
{ "access_token", weChatToken.AccessToken }
};
var weChatSendNotificationUrl = "https://api.weixin.qq.com";
var weChatSendNotificationPath = "/cgi-bin/message/subscribe/send";
var requestUrl = BuildRequestUrl(weChatSendNotificationUrl, weChatSendNotificationPath, requestParamters);
var responseContent = await MakeRequestAndGetResultAsync(requestUrl, notificationData);
var weChatSenNotificationResponse = JsonSerializer.Deserialize<WeChatSendNotificationResponse>(responseContent);
if (!weChatSenNotificationResponse.IsSuccessed)
{
Logger.LogWarning("Send wechat we app subscribe message failed");
Logger.LogWarning($"Error code: {weChatSenNotificationResponse.ErrorCode}, message: {weChatSenNotificationResponse.ErrorMessage}");
}
// 失败是否抛出异常
// weChatSenNotificationResponse.ThrowIfNotSuccess();
}
protected virtual async Task<string> MakeRequestAndGetResultAsync(string url, WeChatWeAppSendNotificationData notificationData)
{
var client = HttpClientFactory.CreateClient(SendNotificationClientName);
var sendDataContent = JsonSerializer.Serialize(notificationData);
var requestContent = new StringContent(sendDataContent);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = requestContent
};
var response = await client.SendAsync(requestMessage);
if (!response.IsSuccessStatusCode)
{
throw new AbpException($"WeChat send subscribe message http request service returns error! HttpStatusCode: {response.StatusCode}, ReasonPhrase: {response.ReasonPhrase}");
}
var resultContent = await response.Content.ReadAsStringAsync();
return resultContent;
}
protected virtual string BuildRequestUrl(string uri, string path, IDictionary<string, string> paramters)
{
var requestUrlBuilder = new StringBuilder(128);
requestUrlBuilder.Append(uri);
requestUrlBuilder.Append(path).Append("?");
foreach (var paramter in paramters)
{
requestUrlBuilder.AppendFormat("{0}={1}", paramter.Key, paramter.Value);
requestUrlBuilder.Append("&");
}
requestUrlBuilder.Remove(requestUrlBuilder.Length - 1, 1);
return requestUrlBuilder.ToString();
}
}
public class WeChatSendNotificationResponse
{
[JsonProperty("errcode")]
public int ErrorCode { get; set; }
[JsonProperty("errmsg")]
public string ErrorMessage { get; set; }
public bool IsSuccessed => ErrorCode == 0;
public void ThrowIfNotSuccess()
{
if (ErrorCode != 0)
{
throw new AbpException($"Send wechat weapp notification error:{ErrorMessage}");
}
}
}
}

103
aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppSendNotificationData.cs

@ -0,0 +1,103 @@
#pragma warning disable IDE1006 // 禁止编译器提示
using System.Collections.Generic;
namespace LINGYUN.Abp.Notifications.WeChat.WeApp
{
public class WeChatWeAppSendNotificationData
{
/// <summary>
/// 接收者(用户)的 openid
/// </summary>
public string touser { get; set; }
/// <summary>
/// 所需下发的订阅模板id
/// </summary>
public string template_id { get; set; }
/// <summary>
/// 点击模板卡片后的跳转页面,仅限本小程序内的页面。
/// 支持带参数,(示例index?foo=bar)。
/// 该字段不填则模板无跳转
/// </summary>
public string page { get; set; }
/// <summary>
/// 跳转小程序类型:
/// developer为开发版;trial为体验版;formal为正式版;
/// 默认为正式版
/// </summary>
public string miniprogram_state { get; set; }
/// <summary>
/// 进入小程序查看”的语言类型,
/// 支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),
/// 默认为zh_CN
/// </summary>
public string lang { get; set; }
/// <summary>
/// 模板内容,
/// 格式形如 { "key1": { "value": any }, "key2": { "value": any } }
/// </summary>
public Dictionary<string, WeChatNotificationData> data { get; set; }
public WeChatWeAppSendNotificationData() { }
public WeChatWeAppSendNotificationData(string openId, string templateId, string redirectPage = "",
string state = "formal", string miniLang = "zh_CN")
{
touser = openId;
template_id = templateId;
page = redirectPage;
miniprogram_state = state;
lang = miniLang;
data = new Dictionary<string, WeChatNotificationData>();
}
/// <summary>
/// 写入标准数据
/// </summary>
/// <param name="writeData"></param>
/// <returns></returns>
public WeChatWeAppSendNotificationData WriteStandardData(NotificationData writeData)
{
foreach (var kv in writeData.Properties)
{
if (!data.ContainsKey(kv.Key))
{
data.Add(kv.Key, new WeChatNotificationData(kv.Value));
}
}
return this;
}
public WeChatWeAppSendNotificationData WriteData(string prefix, string key, object value)
{
// 只截取符合标记的数据
if (key.StartsWith(prefix))
{
key = key.Replace(prefix, "");
if (!data.ContainsKey(key))
{
data.Add(key, new WeChatNotificationData(value));
}
}
return this;
}
public WeChatWeAppSendNotificationData WriteData(string prefix, IDictionary<string, object> setData)
{
foreach(var kv in setData)
{
WriteData(prefix, kv.Key, kv.Value);
}
return this;
}
}
public class WeChatNotificationData
{
public object Value { get; }
public WeChatNotificationData(object value)
{
Value = value;
}
}
}
#pragma warning restore IDE1006

2
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN.Abp.Notifications.csproj

@ -13,7 +13,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EventBus" Version="2.9.0" />
<PackageReference Include="Volo.Abp.Json" Version="2.9.0" />
<PackageReference Include="Volo.Abp.BackgroundJobs" Version="2.9.0" />
</ItemGroup>
</Project>

33
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/AbpNotificationModule.cs

@ -1,14 +1,47 @@
using LINGYUN.Abp.Notifications.Internal;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Notifications
{
[DependsOn(
typeof(AbpBackgroundJobsModule),
typeof(AbpJsonModule))]
public class AbpNotificationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<INotificationDispatcher, DefaultNotificationDispatcher>();
}
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
{
if (typeof(INotificationDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
services.Configure<AbpNotificationOptions>(options =>
{
services.ExecutePreConfiguredActions(options);
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
}
}

16
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/AbpNotificationOptions.cs

@ -0,0 +1,16 @@
using Volo.Abp.Collections;
namespace LINGYUN.Abp.Notifications
{
public class AbpNotificationOptions
{
public ITypeList<INotificationDefinitionProvider> DefinitionProviders { get; }
public ITypeList<INotificationPublishProvider> PublishProviders { get; }
public AbpNotificationOptions()
{
PublishProviders = new TypeList<INotificationPublishProvider>();
DefinitionProviders = new TypeList<INotificationDefinitionProvider>();
}
}
}

9
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDefinitionContext.cs

@ -0,0 +1,9 @@
namespace LINGYUN.Abp.Notifications
{
public interface INotificationDefinitionContext
{
NotificationDefinition GetOrNull(string category);
void Add(params NotificationDefinition[] definitions);
}
}

15
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDefinitionManager.cs

@ -0,0 +1,15 @@
using JetBrains.Annotations;
using System.Collections.Generic;
namespace LINGYUN.Abp.Notifications
{
public interface INotificationDefinitionManager
{
[NotNull]
NotificationDefinition Get([NotNull] string category);
IReadOnlyList<NotificationDefinition> GetAll();
NotificationDefinition GetOrNull(string category);
}
}

7
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDefinitionProvider.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Abp.Notifications
{
public interface INotificationDefinitionProvider
{
void Define(INotificationDefinitionContext context);
}
}

28
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDispatcher.cs

@ -1,9 +1,33 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications
{
/// <summary>
/// 通知发送者接口
/// </summary>
public interface INotificationDispatcher
{
Task DispatcheAsync(NotificationInfo notification);
/// <summary>
/// 发送通知
/// </summary>
/// <param name="notificationName">通知名称</param>
/// <param name="data">数据</param>
/// <param name="tenantId">租户</param>
/// <param name="notificationSeverity">级别</param>
/// <returns></returns>
Task DispatchAsync(NotificationName notificationName, NotificationData data, Guid? tenantId = null,
NotificationSeverity notificationSeverity = NotificationSeverity.Info);
/// <summary>
/// 发送通知事件
/// </summary>
/// <param name="notificationName">通知名称</param>
/// <param name="data">数据</param>
/// <param name="tenantId">租户</param>
/// <param name="notificationSeverity">级别</param>
/// <returns></returns>
Task DispatchEventAsync(NotificationName notificationName, NotificationData data, Guid? tenantId = null,
NotificationSeverity notificationSeverity = NotificationSeverity.Info);
}
}

12
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProvider.cs

@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications
{
public interface INotificationPublishProvider
{
string Name { get; }
Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers);
}
}

9
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProviderManager.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.Notifications
{
public interface INotificationPublishProviderManager
{
List<INotificationPublishProvider> Providers { get; }
}
}

11
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublisher.cs

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications
{
public interface INotificationPublisher
{
Task PublishAsync(NotificationInfo notification, IEnumerable<Guid> userIds);
}
}

12
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs

@ -6,22 +6,30 @@ namespace LINGYUN.Abp.Notifications
{
public interface INotificationStore
{
Task InsertUserSubscriptionAsync(Guid? tenantId, Guid userId, string notificationName);
Task InsertUserSubscriptionAsync(Guid? tenantId, UserIdentifier identifier, string notificationName);
Task InsertUserSubscriptionAsync(Guid? tenantId, IEnumerable<Guid> userIds, string notificationName);
Task InsertUserSubscriptionAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName);
Task DeleteUserSubscriptionAsync(Guid? tenantId, Guid userId, string notificationName);
Task DeleteAllUserSubscriptionAsync(Guid? tenantId, string notificationName);
Task DeleteUserSubscriptionAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName);
Task<List<NotificationSubscriptionInfo>> GetSubscriptionsAsync(Guid? tenantId, string notificationName);
Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, Guid userId);
Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, string userName);
Task<bool> IsSubscribedAsync(Guid? tenantId, Guid userId, string notificationName);
Task InsertNotificationAsync(NotificationInfo notification);
Task DeleteNotificationAsync(NotificationInfo notification);
Task DeleteNotificationAsync(int batchCount);
Task InsertUserNotificationAsync(NotificationInfo notification, Guid userId);
Task InsertUserNotificationsAsync(NotificationInfo notification, IEnumerable<Guid> userIds);

81
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationSubscriptionManager.cs

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications
{
/// <summary>
/// 通知订阅管理器
/// </summary>
public interface INotificationSubscriptionManager
{
/// <summary>
/// 是否已订阅
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="userId">用户标识</param>
/// <param name="notificationName">通知名称</param>
/// <returns></returns>
Task<bool> IsSubscribedAsync(Guid? tenantId, Guid userId, string notificationName);
/// <summary>
/// 订阅通知
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="identifier">用户标识</param>
/// <param name="notificationName">通知名称</param>
/// <returns></returns>
Task SubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName);
/// <summary>
/// 订阅通知
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="identifiers">用户标识列表</param>
/// <param name="notificationName">通知名称</param>
/// <returns></returns>
Task SubscribeAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName);
/// <summary>
/// 取消所有用户订阅
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="notificationName">通知名称</param>
/// <returns></returns>
Task UnsubscribeAllAsync(Guid? tenantId, string notificationName);
/// <summary>
/// 取消订阅
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="identifier">用户标识</param>
/// <param name="notificationName">通知名称</param>
/// <returns></returns>
Task UnsubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName);
/// <summary>
/// 取消订阅
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="identifiers">用户标识列表</param>
/// <param name="notificationName">通知名称</param>
/// <returns></returns>
Task UnsubscribeAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName);
/// <summary>
/// 获取通知被订阅用户列表
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="notificationName">通知名称</param>
/// <returns></returns>
Task<List<NotificationSubscriptionInfo>> GetSubscriptionsAsync(Guid? tenantId, string notificationName);
/// <summary>
/// 获取用户订阅列表
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="userId">用户标识</param>
/// <returns></returns>
Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, Guid userId);
/// <summary>
/// 获取用户订阅列表
/// </summary>
/// <param name="tenantId">租户</param>
/// <param name="userName">用户名</param>
/// <returns></returns>
Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, string userName);
}
}

198
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs

@ -1,34 +1,210 @@
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.EventBus.Distributed;
namespace LINGYUN.Abp.Notifications.Internal
{
/// <summary>
/// Implements <see cref="INotificationDispatcher"/>.
/// </summary>
internal class DefaultNotificationDispatcher : INotificationDispatcher
{
/// <summary>
/// Reference to <see cref="ILogger<DefaultNotificationDispatcher>"/>.
/// </summary>
public ILogger<DefaultNotificationDispatcher> Logger { get; set; }
/// <summary>
/// Reference to <see cref="IDistributedEventBus"/>.
/// </summary>
public IDistributedEventBus DistributedEventBus { get; set; }
/// <summary>
/// Reference to <see cref="IBackgroundJobManager"/>.
/// </summary>
private readonly IBackgroundJobManager _backgroundJobManager;
/// <summary>
/// Reference to <see cref="INotificationStore"/>.
/// </summary>
private readonly INotificationStore _notificationStore;
private readonly INotificationPublisher _notificationPublisher;
/// <summary>
/// Reference to <see cref="INotificationDefinitionManager"/>.
/// </summary>
private readonly INotificationDefinitionManager _notificationDefinitionManager;
/// <summary>
/// Reference to <see cref="INotificationPublishProviderManager"/>.
/// </summary>
private readonly INotificationPublishProviderManager _notificationPublishProviderManager;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultNotificationDispatcher"/> class.
/// </summary>
public DefaultNotificationDispatcher(
IBackgroundJobManager backgroundJobManager,
INotificationStore notificationStore,
INotificationPublisher notificationPublisher)
INotificationDefinitionManager notificationDefinitionManager,
INotificationPublishProviderManager notificationPublishProviderManager)
{
_backgroundJobManager = backgroundJobManager;
_notificationStore = notificationStore;
_notificationPublisher = notificationPublisher;
_notificationDefinitionManager = notificationDefinitionManager;
_notificationPublishProviderManager = notificationPublishProviderManager;
DistributedEventBus = NullDistributedEventBus.Instance;
Logger = NullLogger<DefaultNotificationDispatcher>.Instance;
}
/// <summary>
/// 发送通知
/// </summary>
/// <param name="notificationName">通知名称</param>
/// <param name="data">通知数据</param>
/// <param name="tenantId">租户</param>
/// <param name="notificationSeverity">级别</param>
/// <returns></returns>
public virtual async Task DispatchAsync(NotificationName notificationName, NotificationData data, Guid? tenantId = null,
NotificationSeverity notificationSeverity = NotificationSeverity.Info)
{
// 获取自定义的通知
var defineNotification = _notificationDefinitionManager.Get(notificationName.CateGory);
//// 没有定义的通知,应该也要能发布、订阅,
//// 比如订单之类的,是以订单编号为通知名称,这是动态的,没法自定义
//if(defineNotification == null)
//{
// defineNotification = new NotificationDefinition(notificationName.CateGory);
//}
var notificationInfo = new NotificationInfo
{
CateGory = notificationName.CateGory,
Name = notificationName.Name,
CreationTime = DateTime.Now,
NotificationSeverity = notificationSeverity,
Lifetime = defineNotification.NotificationLifetime,
NotificationType = defineNotification.NotificationType,
TenantId = tenantId,
Data = data
};
public async Task DispatcheAsync(NotificationInfo notification)
var providers = Enumerable
.Reverse(_notificationPublishProviderManager.Providers);
if (defineNotification.Providers.Any())
{
providers = providers.Where(p => defineNotification.Providers.Contains(p.Name));
}
await PublishFromProvidersAsync(providers, notificationInfo);
if (notificationInfo.Lifetime == NotificationLifetime.OnlyOne)
{
// 一次性通知在发送完成后就取消用户订阅
await _notificationStore.DeleteAllUserSubscriptionAsync(notificationInfo.TenantId,
notificationInfo.Name);
}
}
/// <summary>
/// 发送通知事件
/// </summary>
/// <param name="notificationName"></param>
/// <param name="data"></param>
/// <param name="tenantId"></param>
/// <param name="notificationSeverity"></param>
/// <returns></returns>
public virtual async Task DispatchEventAsync(NotificationName notificationName, NotificationData data, Guid? tenantId = null,
NotificationSeverity notificationSeverity = NotificationSeverity.Info)
{
// 获取自定义的通知
var defineNotification = _notificationDefinitionManager.Get(notificationName.CateGory);
var notificationEventData = new NotificationEventData
{
CateGory = notificationName.CateGory,
Name = notificationName.Name,
CreationTime = DateTime.Now,
NotificationSeverity = notificationSeverity,
Lifetime = defineNotification.NotificationLifetime,
NotificationType = defineNotification.NotificationType,
TenantId = tenantId,
Data = data
};
// 发布分布式通知事件,让消息中心统一处理
await DistributedEventBus.PublishAsync(notificationEventData);
}
/// <summary>
/// 指定提供者发布通知
/// </summary>
/// <param name="providers">提供者列表</param>
/// <param name="notificationInfo">通知信息</param>
/// <returns></returns>
protected async Task PublishFromProvidersAsync(IEnumerable<INotificationPublishProvider> providers,
NotificationInfo notificationInfo)
{
Logger.LogDebug($"Persistent notification {notificationInfo.Name}");
// 持久化通知
await _notificationStore.InsertNotificationAsync(notification);
await _notificationStore.InsertNotificationAsync(notificationInfo);
Logger.LogDebug($"Gets a list of user subscriptions {notificationInfo.Name}");
// 获取用户订阅列表
var userSubscriptions = await _notificationStore.GetSubscriptionsAsync(notification.TenantId, notification.Name);
var userSubscriptions = await _notificationStore.GetSubscriptionsAsync(notificationInfo.TenantId, notificationInfo.Name);
Logger.LogDebug($"Persistent user notifications {notificationInfo.Name}");
// 持久化用户通知
var subscriptionUserIds = userSubscriptions.Select(us => us.UserId);
await _notificationStore.InsertUserNotificationsAsync(notification, subscriptionUserIds);
var subscriptionUserIdentifiers = userSubscriptions.Select(us => new UserIdentifier(us.UserId, us.UserName));
await _notificationStore.InsertUserNotificationsAsync(notificationInfo,
subscriptionUserIdentifiers.Select(u => u.UserId));
// 发布通知
foreach (var provider in providers)
{
await PublishAsync(provider, notificationInfo, subscriptionUserIdentifiers);
}
// TODO: 需要计算队列大小,根据情况是否需要并行发布消息
//Parallel.ForEach(providers, async (provider) =>
//{
// await PublishAsync(provider, notificationInfo, subscriptionUserIdentifiers);
//});
}
/// <summary>
/// 发布通知
/// </summary>
/// <param name="provider">通知发布者</param>
/// <param name="notificationInfo">通知信息</param>
/// <param name="subscriptionUserIdentifiers">订阅用户列表</param>
/// <returns></returns>
protected async Task PublishAsync(INotificationPublishProvider provider, NotificationInfo notificationInfo,
IEnumerable<UserIdentifier> subscriptionUserIdentifiers)
{
try
{
Logger.LogDebug($"Sending notification with provider {provider.Name}");
// 发布
await provider.PublishAsync(notificationInfo, subscriptionUserIdentifiers);
Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful");
}
catch (Exception ex)
{
Logger.LogWarning($"Send notification error with provider {provider.Name}");
Logger.LogWarning($"Error message:{ex.Message}");
Logger.LogTrace(ex, $"Send notification error with provider { provider.Name}");
// 发布用户通知
await _notificationPublisher.PublishAsync(notification, subscriptionUserIds);
Logger.LogDebug($"Send notification error, notification {notificationInfo.Name} entry queue");
// 发送失败的消息进入后台队列
await _backgroundJobManager.EnqueueAsync(
new NotificationPublishJobArgs(notificationInfo.GetId(),
provider.GetType().AssemblyQualifiedName,
subscriptionUserIdentifiers.ToList(),
notificationInfo.TenantId));
}
}
}
}

70
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/NotificationSubscriptionManager.cs

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Notifications.Internal
{
internal class NotificationSubscriptionManager : INotificationSubscriptionManager, ITransientDependency
{
private readonly INotificationStore _store;
public NotificationSubscriptionManager(
INotificationStore store)
{
_store = store;
}
public virtual async Task<List<NotificationSubscriptionInfo>> GetSubscriptionsAsync(Guid? tenantId, string notificationName)
{
return await _store.GetSubscriptionsAsync(tenantId, notificationName);
}
public virtual async Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, Guid userId)
{
return await _store.GetUserSubscriptionsAsync(tenantId, userId);
}
public virtual async Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, string userName)
{
return await _store.GetUserSubscriptionsAsync(tenantId, userName);
}
public virtual async Task<bool> IsSubscribedAsync(Guid? tenantId, Guid userId, string notificationName)
{
return await _store.IsSubscribedAsync(tenantId, userId, notificationName);
}
public virtual async Task SubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName)
{
if (await IsSubscribedAsync(tenantId, identifier.UserId, notificationName))
{
return;
}
await _store.InsertUserSubscriptionAsync(tenantId, identifier, notificationName);
}
public virtual async Task SubscribeAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName)
{
foreach(var identifier in identifiers)
{
await SubscribeAsync(tenantId, identifier, notificationName);
}
}
public virtual async Task UnsubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName)
{
await _store.DeleteUserSubscriptionAsync(tenantId, identifier.UserId, notificationName);
}
public virtual async Task UnsubscribeAllAsync(Guid? tenantId, string notificationName)
{
await _store.DeleteAllUserSubscriptionAsync(tenantId, notificationName);
}
public virtual async Task UnsubscribeAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName)
{
await _store.DeleteUserSubscriptionAsync(tenantId, identifiers, notificationName);
}
}
}

78
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationData.cs

@ -45,5 +45,83 @@ namespace LINGYUN.Abp.Notifications
{
_properties = new Dictionary<string, object>();
}
/// <summary>
/// 写入标准数据
/// </summary>
/// <param name="title">标题</param>
/// <param name="message">内容</param>
/// <param name="createTime">创建时间</param>
/// <param name="formUser">来源用户</param>
/// <returns></returns>
public NotificationData WriteStandardData(string title, string message, DateTime createTime, string formUser)
{
TrySetData("title", title);
TrySetData("message", message);
TrySetData("formUser", formUser);
TrySetData("createTime", createTime);
return this;
}
/// <summary>
/// 写入标准数据
/// </summary>
/// <param name="prefix">数据前缀</param>
/// <param name="key">标识</param>
/// <param name="value">数据内容</param>
/// <returns></returns>
public NotificationData WriteStandardData(string prefix, string key, object value)
{
TrySetData(string.Concat(prefix, key), value);
return this;
}
/// <summary>
/// 转换为标准数据
/// </summary>
/// <param name="sourceData">原始数据</param>
/// <returns></returns>
public static NotificationData ToStandardData(NotificationData sourceData)
{
var data = new NotificationData();
data.TrySetData("title", sourceData.TryGetData("title"));
data.TrySetData("message", sourceData.TryGetData("message"));
data.TrySetData("formUser", sourceData.TryGetData("formUser"));
data.TrySetData("createTime", sourceData.TryGetData("createTime"));
return data;
}
/// <summary>
/// 转换为标准数据
/// </summary>
/// <param name="prefix">数据前缀</param>
/// <param name="sourceData">原始数据</param>
/// <returns></returns>
public static NotificationData ToStandardData(string prefix, NotificationData sourceData)
{
var data = ToStandardData(sourceData);
foreach(var property in sourceData.Properties)
{
if (property.Key.StartsWith(prefix))
{
var key = property.Key.Replace(prefix, "");
data.TrySetData(key, property.Value);
}
}
return data;
}
public object TryGetData(string key)
{
if (Properties.TryGetValue(key, out object value))
{
return value;
}
return null;
}
public void TrySetData(string key, object value)
{
if (value != null && !Properties.ContainsKey(key))
{
Properties[key] = value;
}
}
}
}

83
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinition.cs

@ -0,0 +1,83 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.Localization;
/*
*
* ,,,Catalog
* Prefix
*
*/
namespace LINGYUN.Abp.Notifications
{
public class NotificationDefinition
{
/// <summary>
/// 通知类目
/// </summary>
[NotNull]
public string CateGory { get; set; }
/// <summary>
/// 通知显示名称
/// </summary>
[NotNull]
public ILocalizableString DisplayName
{
get => _displayName;
set => _displayName = Check.NotNull(value, nameof(value));
}
private ILocalizableString _displayName;
/// <summary>
/// 通知说明
/// </summary>
[CanBeNull]
public ILocalizableString Description { get; set; }
/// <summary>
/// 允许客户端显示订阅
/// </summary>
public bool AllowSubscriptionToClients { get; set; }
/// <summary>
/// 存活类型
/// </summary>
public NotificationLifetime NotificationLifetime { get; set; }
/// <summary>
/// 通知类型
/// </summary>
public NotificationType NotificationType { get; set; }
/// <summary>
/// 通知提供者
/// </summary>
public List<string> Providers { get; }
public NotificationDefinition(
string category,
ILocalizableString displayName = null,
ILocalizableString description = null,
NotificationType notificationType = NotificationType.Application,
NotificationLifetime lifetime = NotificationLifetime.Persistent,
bool allowSubscriptionToClients = false)
{
CateGory = category;
DisplayName = displayName ?? new FixedLocalizableString(category);
Description = description;
NotificationLifetime = lifetime;
NotificationType = notificationType;
AllowSubscriptionToClients = allowSubscriptionToClients;
Providers = new List<string>();
}
public virtual NotificationDefinition WithProviders(params string[] providers)
{
if (!providers.IsNullOrEmpty())
{
Providers.AddRange(providers);
}
return this;
}
}
}

33
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinitionContext.cs

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
namespace LINGYUN.Abp.Notifications
{
public class NotificationDefinitionContext : INotificationDefinitionContext
{
protected Dictionary<string, NotificationDefinition> Notifications { get; }
public NotificationDefinitionContext(Dictionary<string, NotificationDefinition> notifications)
{
Notifications = notifications;
}
public void Add(params NotificationDefinition[] definitions)
{
if (definitions.IsNullOrEmpty())
{
return;
}
foreach (var definition in definitions)
{
Notifications[definition.CateGory] = definition;
}
}
public NotificationDefinition GetOrNull(string category)
{
return Notifications.GetOrDefault(category);
}
}
}

75
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs

@ -0,0 +1,75 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Notifications
{
public class NotificationDefinitionManager : INotificationDefinitionManager, ISingletonDependency
{
protected Lazy<IDictionary<string, NotificationDefinition>> NotificationDefinitions { get; }
protected AbpNotificationOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
public NotificationDefinitionManager(
IOptions<AbpNotificationOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
NotificationDefinitions = new Lazy<IDictionary<string, NotificationDefinition>>(CreateNotificationDefinitions, true);
}
public virtual NotificationDefinition Get([NotNull] string category)
{
Check.NotNull(category, nameof(category));
var notification = GetOrNull(category);
if (notification == null)
{
throw new AbpException("Undefined notification category: " + category);
}
return notification;
}
public virtual IReadOnlyList<NotificationDefinition> GetAll()
{
return NotificationDefinitions.Value.Values.ToImmutableList();
}
public virtual NotificationDefinition GetOrNull(string category)
{
return NotificationDefinitions.Value.GetOrDefault(category);
}
protected virtual IDictionary<string, NotificationDefinition> CreateNotificationDefinitions()
{
var notifications = new Dictionary<string, NotificationDefinition>();
using (var scope = ServiceProvider.CreateScope())
{
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as INotificationDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.Define(new NotificationDefinitionContext(notifications));
}
}
return notifications;
}
}
}

9
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationDefinitionProvider.cs

@ -0,0 +1,9 @@
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Notifications
{
public abstract class NotificationDefinitionProvider : INotificationDefinitionProvider, ITransientDependency
{
public abstract void Define(INotificationDefinitionContext context);
}
}

38
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationEventData.cs

@ -0,0 +1,38 @@
using System;
namespace LINGYUN.Abp.Notifications
{
public class NotificationEventData
{
public Guid? TenantId { get; set; }
public string CateGory { get; set; }
public string Name { get; set; }
public string Id { get; set; }
public NotificationData Data { get; set; }
public DateTime CreationTime { get; set; }
public NotificationLifetime Lifetime { get; set; }
public NotificationType NotificationType { get; set; }
public NotificationSeverity NotificationSeverity { get; set; }
public NotificationEventData()
{
}
public NotificationInfo ToNotificationInfo()
{
return new NotificationInfo
{
NotificationSeverity = NotificationSeverity,
CreationTime = CreationTime,
Data = Data,
Id = Id,
Name = Name,
CateGory = CateGory,
NotificationType = NotificationType,
Lifetime = Lifetime,
TenantId = TenantId
};
}
}
}

41
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationInfo.cs

@ -1,5 +1,4 @@
using Newtonsoft.Json;
using System;
using System;
namespace LINGYUN.Abp.Notifications
{
@ -7,19 +6,53 @@ namespace LINGYUN.Abp.Notifications
{
public Guid? TenantId { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(HexLongConverter))]
public long Id { get; set; }
public string CateGory { get; set; }
public string Id { get; set; }
public NotificationData Data { get; set; }
public DateTime CreationTime { get; set; }
public NotificationLifetime Lifetime { get; set; }
public NotificationType NotificationType { get; set; }
public NotificationSeverity NotificationSeverity { get; set; }
public NotificationInfo()
{
Data = new NotificationData();
Lifetime = NotificationLifetime.Persistent;
NotificationType = NotificationType.Application;
NotificationSeverity = NotificationSeverity.Info;
CreationTime = DateTime.Now;
}
public long SetId(long id)
{
if (Id.IsNullOrWhiteSpace())
{
Id = id.ToString();
return id;
}
return GetId();
}
public long GetId()
{
return long.Parse(Id);
}
public NotificationEventData ToNotificationEventData()
{
return new NotificationEventData
{
NotificationSeverity = NotificationSeverity,
CreationTime = CreationTime,
Data = Data,
Id = Id,
Name = Name,
CateGory = CateGory,
Lifetime = Lifetime,
NotificationType = NotificationType,
TenantId = TenantId
};
}
}
}

18
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationLifetime.cs

@ -0,0 +1,18 @@
namespace LINGYUN.Abp.Notifications
{
/// <summary>
/// 通知存活时间
/// 发送之后取消用户订阅,类似于微信小程序
/// </summary>
public enum NotificationLifetime
{
/// <summary>
/// 持久化
/// </summary>
Persistent = 0,
/// <summary>
/// 一次性
/// </summary>
OnlyOne = 1
}
}

14
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationName.cs

@ -0,0 +1,14 @@
namespace LINGYUN.Abp.Notifications
{
public class NotificationName
{
public string CateGory { get; }
public string Name { get; }
public NotificationName(string cateGory, string name)
{
Name = name;
CateGory = cateGory;
}
}
}

15
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationNameNormalizer.cs

@ -0,0 +1,15 @@
namespace LINGYUN.Abp.Notifications
{
public static class NotificationNameNormalizer
{
public static NotificationName NormalizerName(string name)
{
return new NotificationName(name, name);
}
public static NotificationName NormalizerName(string category, string name)
{
var notifyName = string.Concat(category, ":", name);
return new NotificationName(category, notifyName);
}
}
}

34
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishJob.cs

@ -0,0 +1,34 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Notifications
{
public class NotificationPublishJob : AsyncBackgroundJob<NotificationPublishJobArgs>, ITransientDependency
{
protected INotificationStore Store { get; }
protected IServiceProvider ServiceProvider { get; }
public NotificationPublishJob(
INotificationStore store,
IServiceProvider serviceProvider)
{
Store = store;
ServiceProvider = serviceProvider;
}
public override async Task ExecuteAsync(NotificationPublishJobArgs args)
{
var providerType = Type.GetType(args.ProviderType);
if (ServiceProvider.GetRequiredService(providerType) is INotificationPublishProvider publishProvider)
{
var notification = await Store.GetNotificationOrNullAsync(args.TenantId, args.NotificationId);
await publishProvider.PublishAsync(notification, args.UserIdentifiers);
}
}
}
}

20
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishJobArgs.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
namespace LINGYUN.Abp.Notifications
{
public class NotificationPublishJobArgs
{
public Guid? TenantId { get; set; }
public long NotificationId { get; set; }
public string ProviderType { get; set; }
public List<UserIdentifier> UserIdentifiers { get; set; }
public NotificationPublishJobArgs(long id, string providerType, List<UserIdentifier> userIdentifiers, Guid? tenantId = null )
{
NotificationId = id;
ProviderType = providerType;
UserIdentifiers = userIdentifiers;
TenantId = tenantId;
}
}
}

49
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs

@ -0,0 +1,49 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Notifications
{
public abstract class NotificationPublishProvider : INotificationPublishProvider, ITransientDependency
{
public abstract string Name { get; }
protected IServiceProvider ServiceProvider { get; }
protected readonly object ServiceProviderLock = new object();
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory;
protected ILogger Logger => _lazyLogger.Value;
private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
protected TService LazyGetRequiredService<TService>(ref TService reference)
{
if (reference == null)
{
lock (ServiceProviderLock)
{
if (reference == null)
{
reference = ServiceProvider.GetRequiredService<TService>();
}
}
}
return reference;
}
protected NotificationPublishProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public abstract Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers);
}
}

33
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProviderManager.cs

@ -0,0 +1,33 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Notifications
{
public class NotificationPublishProviderManager : INotificationPublishProviderManager, ISingletonDependency
{
public List<INotificationPublishProvider> Providers => _lazyProviders.Value;
protected AbpNotificationOptions Options { get; }
private readonly Lazy<List<INotificationPublishProvider>> _lazyProviders;
public NotificationPublishProviderManager(
IServiceProvider serviceProvider,
IOptions<AbpNotificationOptions> options)
{
Options = options.Value;
_lazyProviders = new Lazy<List<INotificationPublishProvider>>(
() => Options
.PublishProviders
.Select(type => serviceProvider.GetRequiredService(type) as INotificationPublishProvider)
.ToList(),
true
);
}
}
}

1
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationSubscriptionInfo.cs

@ -6,6 +6,7 @@ namespace LINGYUN.Abp.Notifications
{
public Guid? TenantId { get; set; }
public Guid UserId { get; set; }
public string UserName { get; set; }
public string NotificationName { get; set; }
}
}

16
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/UserIdentifier.cs

@ -0,0 +1,16 @@
using System;
namespace LINGYUN.Abp.Notifications
{
public class UserIdentifier
{
public Guid UserId { get; set; }
public string UserName { get; set; }
public UserIdentifier(Guid userId, string userName)
{
UserId = userId;
UserName = userName;
}
}
}

4
aspnet-core/modules/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN.Abp.Sms.Aliyun.csproj

@ -30,4 +30,8 @@
<PackageReference Include="Volo.Abp.Sms" Version="2.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Aliyun.Authorization\LINGYUN.Abp.Aliyun.Authorization.csproj" />
</ItemGroup>
</Project>

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

Loading…
Cancel
Save