diff --git a/README.md b/README.md index c525b452..5014f62e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ModuleShop完全免费并且开源 ### 项目结构 -![商品](https://gogs.circle.ac.cn/gogs/data/raw/master/images/devenv_2019-07-06_20-48-19.png) +![商品]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/devenv_2019-07-06_20-48-19.png) ## 后台前端 @@ -36,10 +36,10 @@ ModuleShop完全免费并且开源 ### 项目截图 -![商品](https://gogs.circle.ac.cn/gogs/data/raw/master/images/chrome_2019-07-06_20-59-32.png) -![商品](https://gogs.circle.ac.cn/gogs/data/raw/master/images/chrome_2019-07-06_20-59-21.png) -![订单](https://gogs.circle.ac.cn/gogs/data/raw/master/images/chrome_2019-07-06_20-59-55.png) -![首页显示配置](https://gogs.circle.ac.cn/gogs/data/raw/master/images/chrome_2019-07-06_21-00-04.png) +![商品]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/chrome_2019-07-06_20-59-32.png) +![商品]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/chrome_2019-07-06_20-59-21.png) +![订单]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/chrome_2019-07-06_20-59-55.png) +![首页显示配置]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/chrome_2019-07-06_21-00-04.png) ### 功能列表 @@ -57,25 +57,25 @@ ModuleShop完全免费并且开源 在线预览(天网商城): -![小程序码](https://gogs.circle.ac.cn/gogs/data/raw/master/images/shop_mp_8.jpg) +![小程序码]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/shop_mp_8.jpg) ### 项目截图 -![首页](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-06_21-05-55.png) +![首页]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-06_21-05-55.png) -![分类](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-06_21-06-27.png) +![分类]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-06_21-06-27.png) -![购物车](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-06_21-07-05.png) +![购物车]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-06_21-07-05.png) -![商品详情](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-07_01-42-31.png) +![商品详情]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-07_01-42-31.png) -![订单列表](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-07_01-32-17.png) +![订单列表]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-07_01-32-17.png) -![订单详情](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-06_21-07-35.png) +![订单详情]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-06_21-07-35.png) -![评价](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-07_01-41-17.png) +![评价]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-07_01-41-17.png) -![我的](https://gogs.circle.ac.cn/gogs/data/raw/master/images/wechatdevtools_2019-07-07_01-35-29.png) +![我的]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/wechatdevtools_2019-07-07_01-35-29.png) ### 功能列表 @@ -137,4 +137,4 @@ ModuleShop完全免费并且开源 [ModuleShop交流群](https://jq.qq.com/?_wv=1027&k=5AUnOw5 "ModuleShop交流群(863275860)")(863275860) -![ModuleShop交流群二维码](https://gogs.circle.ac.cn/gogs/data/raw/master/images/863275860_8_8.png) \ No newline at end of file +![ModuleShop交流群二维码]( https://gz-1253581958.cos.ap-guangzhou.myqcloud.com/data/images/863275860_8_8.png) \ No newline at end of file diff --git a/Shop.sln b/Shop.sln index 5a968d1c..ee6325b9 100644 --- a/Shop.sln +++ b/Shop.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.168 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0BC982E9-6A28-4C28-8AC9-0B4431064F9D}" EndProject @@ -35,12 +35,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shop.Module.ShoppingCart", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shop.Module.Core.MiniProgram", "src\Modules\Shop.Module.Core.MiniProgram\Shop.Module.Core.MiniProgram.csproj", "{A2469090-CF84-4B18-91F3-D1D61FA2D1B9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shop.Module.RabbitMQ", "src\Modules\Shop.Module.RabbitMQ\Shop.Module.RabbitMQ.csproj", "{1A4CEC3B-7F68-405E-955E-082BFC3BB26E}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MQ", "MQ", "{169A12D7-296F-46B0-9EEF-C3A576D05428}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shop.Module.MQ", "src\Modules\Shop.Module.MQ\Shop.Module.MQ.csproj", "{A52480D5-2473-439A-9C7D-1CF3865DD37C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shop.Module.Reviews", "src\Modules\Shop.Module.Reviews\Shop.Module.Reviews.csproj", "{547196CC-28BC-4893-9920-F34CF5719906}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage", "Storage", "{69D7DF55-97BF-42E9-A63E-65FB6C6B9E82}" @@ -168,14 +164,6 @@ Global {A2469090-CF84-4B18-91F3-D1D61FA2D1B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2469090-CF84-4B18-91F3-D1D61FA2D1B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2469090-CF84-4B18-91F3-D1D61FA2D1B9}.Release|Any CPU.Build.0 = Release|Any CPU - {1A4CEC3B-7F68-405E-955E-082BFC3BB26E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A4CEC3B-7F68-405E-955E-082BFC3BB26E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A4CEC3B-7F68-405E-955E-082BFC3BB26E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A4CEC3B-7F68-405E-955E-082BFC3BB26E}.Release|Any CPU.Build.0 = Release|Any CPU - {A52480D5-2473-439A-9C7D-1CF3865DD37C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A52480D5-2473-439A-9C7D-1CF3865DD37C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A52480D5-2473-439A-9C7D-1CF3865DD37C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A52480D5-2473-439A-9C7D-1CF3865DD37C}.Release|Any CPU.Build.0 = Release|Any CPU {547196CC-28BC-4893-9920-F34CF5719906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {547196CC-28BC-4893-9920-F34CF5719906}.Debug|Any CPU.Build.0 = Debug|Any CPU {547196CC-28BC-4893-9920-F34CF5719906}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -284,9 +272,7 @@ Global {DCE7B6B6-4617-4063-8E55-AA2B0F7C7BE2} = {C6E59042-550D-4E07-B339-5791E066AB8F} {C2ACBD35-964A-4E61-8BAD-784414BA36B9} = {39FAE76F-2C55-49B0-95D3-9321AB6CB6D3} {A2469090-CF84-4B18-91F3-D1D61FA2D1B9} = {9ABB371E-6E15-4FA8-AE40-785F67DD136A} - {1A4CEC3B-7F68-405E-955E-082BFC3BB26E} = {169A12D7-296F-46B0-9EEF-C3A576D05428} {169A12D7-296F-46B0-9EEF-C3A576D05428} = {39FAE76F-2C55-49B0-95D3-9321AB6CB6D3} - {A52480D5-2473-439A-9C7D-1CF3865DD37C} = {169A12D7-296F-46B0-9EEF-C3A576D05428} {547196CC-28BC-4893-9920-F34CF5719906} = {39FAE76F-2C55-49B0-95D3-9321AB6CB6D3} {69D7DF55-97BF-42E9-A63E-65FB6C6B9E82} = {39FAE76F-2C55-49B0-95D3-9321AB6CB6D3} {AC8FCA58-B8D9-434D-A93E-83F77A8E1555} = {69D7DF55-97BF-42E9-A63E-65FB6C6B9E82} diff --git a/docker-compose.dcproj b/docker-compose.dcproj index a66091bf..e7a5769b 100644 --- a/docker-compose.dcproj +++ b/docker-compose.dcproj @@ -20,8 +20,4 @@ - - - - \ No newline at end of file diff --git a/src/Modules/Shop.Module.Catalog.Abstractions/Shop.Module.Catalog.Abstractions.csproj b/src/Modules/Shop.Module.Catalog.Abstractions/Shop.Module.Catalog.Abstractions.csproj index b405a3d1..6b95af42 100644 --- a/src/Modules/Shop.Module.Catalog.Abstractions/Shop.Module.Catalog.Abstractions.csproj +++ b/src/Modules/Shop.Module.Catalog.Abstractions/Shop.Module.Catalog.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Catalog/ModuleInitializer.cs b/src/Modules/Shop.Module.Catalog/ModuleInitializer.cs index b714f3eb..1399c4a2 100644 --- a/src/Modules/Shop.Module.Catalog/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.Catalog/ModuleInitializer.cs @@ -21,7 +21,7 @@ public void ConfigureServices(IServiceCollection serviceCollection) serviceCollection.AddTransient, EntityViewedHandler>(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.Catalog/Services/CategoryService.cs b/src/Modules/Shop.Module.Catalog/Services/CategoryService.cs index 05e4db1b..460c83d9 100644 --- a/src/Modules/Shop.Module.Catalog/Services/CategoryService.cs +++ b/src/Modules/Shop.Module.Catalog/Services/CategoryService.cs @@ -94,13 +94,22 @@ public async Task>> List(StandardTabl Id = category.Id, IsPublished = category.IsPublished, IncludeInMenu = category.IncludeInMenu, - Name = IncludeParentName(all.FirstOrDefault(c => c.Id == category.Id)), + //Name = IncludeParentName(all.FirstOrDefault(c => c.Id == category.Id)), DisplayOrder = category.DisplayOrder, ParentId = category.ParentId, CreatedOn = category.CreatedOn, UpdatedOn = category.UpdatedOn }); + // TODO .NET CORE 3.1 + if (gridData?.List?.Count() > 0) + { + gridData.List.ToList().ForEach(c => + { + c.Name = IncludeParentName(all.FirstOrDefault(x => x.Id == c.Id)); + }); + } + return Result.Ok(gridData); } diff --git a/src/Modules/Shop.Module.Catalog/Services/ProductService.cs b/src/Modules/Shop.Module.Catalog/Services/ProductService.cs index d176ca70..6107fa18 100644 --- a/src/Modules/Shop.Module.Catalog/Services/ProductService.cs +++ b/src/Modules/Shop.Module.Catalog/Services/ProductService.cs @@ -11,7 +11,6 @@ using Shop.Module.Core.Abstractions.Models; using Shop.Module.Inventory.Abstractions.Entities; using Shop.Module.MQ.Abstractions.Data; -using Shop.Module.MQ.Abstractions.Models; using Shop.Module.MQ.Abstractions.Services; using Shop.Module.Orders.Abstractions.Entities; using System; @@ -152,7 +151,7 @@ public async Task GetGoodsByCache(int id) if (user != null) { - await _mqService.DirectSend(QueueKeys.ProductView, new ProductViewed() + await _mqService.Send(QueueKeys.ProductView, new ProductViewed() { UserId = user.Id, EntityId = result.Id, diff --git a/src/Modules/Shop.Module.Catalog/Shop.Module.Catalog.csproj b/src/Modules/Shop.Module.Catalog/Shop.Module.Catalog.csproj index 712c576b..6c0b1e41 100644 --- a/src/Modules/Shop.Module.Catalog/Shop.Module.Catalog.csproj +++ b/src/Modules/Shop.Module.Catalog/Shop.Module.Catalog.csproj @@ -1,14 +1,8 @@ - - netstandard2.0 - - - - - - - + + netcoreapp3.1 + @@ -19,7 +13,7 @@ - + diff --git a/src/Modules/Shop.Module.Core.Abstractions/Extensions/IWorkContext.cs b/src/Modules/Shop.Module.Core.Abstractions/Extensions/IWorkContext.cs index a7a33b4a..0541899d 100644 --- a/src/Modules/Shop.Module.Core.Abstractions/Extensions/IWorkContext.cs +++ b/src/Modules/Shop.Module.Core.Abstractions/Extensions/IWorkContext.cs @@ -10,5 +10,15 @@ public interface IWorkContext Task GetCurrentUserOrNullAsync(); Task GetCurrentOrThrowAsync(); + + /// + /// 验证令牌并自动续签 + /// + /// + /// + /// + /// + /// + bool ValidateToken(int userId, string token, out int statusCode, string path = ""); } } diff --git a/src/Modules/Shop.Module.Core.Abstractions/Services/IAccountService.cs b/src/Modules/Shop.Module.Core.Abstractions/Services/IAccountService.cs index d5905071..7dfb6740 100644 --- a/src/Modules/Shop.Module.Core.Abstractions/Services/IAccountService.cs +++ b/src/Modules/Shop.Module.Core.Abstractions/Services/IAccountService.cs @@ -6,6 +6,12 @@ namespace Shop.Module.Core.Abstractions.Services { public interface IAccountService { - Task LoginWithSignInCheck(User user); + /// + /// 验证并获取最后一条验证码 + /// + /// + /// + /// + Task ValidateGetLastSms(string phone, string captcha); } } diff --git a/src/Modules/Shop.Module.Core.Abstractions/Shop.Module.Core.Abstractions.csproj b/src/Modules/Shop.Module.Core.Abstractions/Shop.Module.Core.Abstractions.csproj index 67929b44..e6b7894c 100644 --- a/src/Modules/Shop.Module.Core.Abstractions/Shop.Module.Core.Abstractions.csproj +++ b/src/Modules/Shop.Module.Core.Abstractions/Shop.Module.Core.Abstractions.csproj @@ -1,16 +1,16 @@ - + - - netstandard2.0 - + + netcoreapp3.1 + - - - - + + + + - - - + + + diff --git a/src/Modules/Shop.Module.Core.Abstractions/ViewModels/UserQueryResult.cs b/src/Modules/Shop.Module.Core.Abstractions/ViewModels/UserQueryResult.cs index 97b52b59..ae5747fa 100644 --- a/src/Modules/Shop.Module.Core.Abstractions/ViewModels/UserQueryResult.cs +++ b/src/Modules/Shop.Module.Core.Abstractions/ViewModels/UserQueryResult.cs @@ -44,6 +44,6 @@ public class UserQueryResult public DateTime UpdatedOn { get; set; } - public IList RoleIds { get; set; } = new List(); + public IEnumerable RoleIds { get; set; } = new List(); } } diff --git a/src/Modules/Shop.Module.Core.MiniProgram/Controllers/MpPayApiController.cs b/src/Modules/Shop.Module.Core.MiniProgram/Controllers/MpPayApiController.cs index 46214644..de18e360 100644 --- a/src/Modules/Shop.Module.Core.MiniProgram/Controllers/MpPayApiController.cs +++ b/src/Modules/Shop.Module.Core.MiniProgram/Controllers/MpPayApiController.cs @@ -3,6 +3,8 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using Shop.Module.Core.Abstractions.Services; +using Shop.Module.Core.MiniProgram.Models; using Shop.Module.MQ.Abstractions.Data; using Shop.Module.MQ.Abstractions.Services; using Shop.Module.Orders.Abstractions.Events; @@ -20,15 +22,18 @@ public class MpPayApiController : ControllerBase private readonly IWeChatPayNotifyClient _client; private readonly IMQService _mqService; private readonly ILogger _logger; + private readonly IAppSettingService _appSettingService; public MpPayApiController( IWeChatPayNotifyClient client, IMQService mqService, - ILogger logger) + ILogger logger, + IAppSettingService appSettingService) { _client = client; _mqService = mqService; _logger = logger; + _appSettingService = appSettingService; } [AllowAnonymous] @@ -37,16 +42,25 @@ public async Task NotifyByOrderNo(string no) { try { - var notify = await _client.ExecuteAsync(Request); + var config = await _appSettingService.Get(); + var opt = new WeChatPayOptions() + { + AppId = config.AppId, + MchId = config.MchId, + Secret = config.AppSecret, + Key = config.Key + }; + + var notify = await _client.ExecuteAsync(Request, opt); if (notify.ReturnCode == "SUCCESS") { if (notify.ResultCode == "SUCCESS") { - await _mqService.DirectSend(QueueKeys.PaymentReceived, new PaymentReceived() + await _mqService.Send(QueueKeys.PaymentReceived, new PaymentReceived() { Note = "微信支付成功结果通知", OrderNo = no, - PaymentFeeAmount = int.Parse(notify.TotalFee) / 100M, + PaymentFeeAmount = notify.TotalFee / 100M, PaymentMethod = PaymentMethod.WeChat, PaymentOn = DateTime.ParseExact(notify.TimeEnd, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture) }); diff --git a/src/Modules/Shop.Module.Core.MiniProgram/ModuleInitializer.cs b/src/Modules/Shop.Module.Core.MiniProgram/ModuleInitializer.cs index c84f2d84..fb429a9c 100644 --- a/src/Modules/Shop.Module.Core.MiniProgram/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.Core.MiniProgram/ModuleInitializer.cs @@ -29,7 +29,7 @@ public void ConfigureServices(IServiceCollection services) }); services.AddScoped(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.Core.MiniProgram/Services/PaymentService.cs b/src/Modules/Shop.Module.Core.MiniProgram/Services/PaymentService.cs index ebd4f168..3f09336c 100644 --- a/src/Modules/Shop.Module.Core.MiniProgram/Services/PaymentService.cs +++ b/src/Modules/Shop.Module.Core.MiniProgram/Services/PaymentService.cs @@ -8,6 +8,8 @@ using Shop.Module.Payments.Abstractions.Models; using Shop.Module.Payments.Abstractions.Services; using System; +using System.Linq; +using System.Net; using System.Threading.Tasks; namespace Shop.Module.Core.MiniProgram.Services @@ -30,6 +32,10 @@ public PaymentService( public async Task GeneratePaymentOrder(PaymentOrderRequest request) { + var ip = Dns.GetHostEntry(Dns.GetHostName()) + .AddressList.FirstOrDefault(address => address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)?.ToString() + ?? "127.0.0.1"; + var apiHost = await _appSettingService.Get(ShopKeys.ApiHost); var wxRequest = new WeChatPayUnifiedOrderRequest { @@ -38,20 +44,32 @@ public async Task GeneratePaymentOrder(PaymentOrderReq TotalFee = Convert.ToInt32(request.TotalAmount * 100), OpenId = request.OpenId, TradeType = "JSAPI", - SpbillCreateIp = "127.0.0.1", + //SpbillCreateIp = "127.0.0.1", + SpBillCreateIp = ip, NotifyUrl = $"{apiHost.Trim('/')}/api/mp/pay/notify/{request.OrderNo}", }; - var response = await _client.ExecuteAsync(wxRequest); + + var config = await _appSettingService.Get(); + var opt = new WeChatPayOptions() + { + AppId = config.AppId, + MchId = config.MchId, + Secret = config.AppSecret, + Key = config.Key + }; + var response = await _client.ExecuteAsync(wxRequest, opt); + if (response?.ReturnCode == "SUCCESS" && response?.ResultCode == "SUCCESS") { - var req = new WeChatPayLiteAppCallPaymentRequest + var req = new WeChatPayAppSdkRequest { - Package = "prepay_id=" + response.PrepayId + PrepayId = response.PrepayId, }; // https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5 // 将参数(parameter)给 小程序前端 让他调起支付API - var parameter = await _client.ExecuteAsync(req); + var parameter = await _client.ExecuteAsync(req, opt); + var json = JsonConvert.SerializeObject(parameter); return JsonConvert.DeserializeObject(json); } diff --git a/src/Modules/Shop.Module.Core.MiniProgram/Shop.Module.Core.MiniProgram.csproj b/src/Modules/Shop.Module.Core.MiniProgram/Shop.Module.Core.MiniProgram.csproj index 440122e9..1f76ecda 100644 --- a/src/Modules/Shop.Module.Core.MiniProgram/Shop.Module.Core.MiniProgram.csproj +++ b/src/Modules/Shop.Module.Core.MiniProgram/Shop.Module.Core.MiniProgram.csproj @@ -1,11 +1,11 @@ - + - netcoreapp2.2 + netcoreapp3.1 - + diff --git a/src/Modules/Shop.Module.Core/Controllers/AccountApiController.cs b/src/Modules/Shop.Module.Core/Controllers/AccountApiController.cs index 6b82235d..129db222 100644 --- a/src/Modules/Shop.Module.Core/Controllers/AccountApiController.cs +++ b/src/Modules/Shop.Module.Core/Controllers/AccountApiController.cs @@ -7,14 +7,12 @@ using Shop.Infrastructure; using Shop.Infrastructure.Data; using Shop.Infrastructure.Helpers; -using Shop.Module.Core.Abstractions.Cache; using Shop.Module.Core.Abstractions.Data; using Shop.Module.Core.Abstractions.Entities; using Shop.Module.Core.Abstractions.Extensions; using Shop.Module.Core.Abstractions.Models; using Shop.Module.Core.Abstractions.Services; using Shop.Module.Core.Abstractions.ViewModels; -using Shop.Module.Core.Extensions; using Shop.Module.Schedule.Abstractions.Services; using System; using System.Collections.Generic; @@ -38,11 +36,10 @@ public class AccountApiController : ControllerBase private readonly IRepository _userRepository; private readonly ITokenService _tokenService; private readonly IWorkContext _workContext; - private readonly IStaticCacheManager _cacheManager; private readonly string _webHost; - private readonly ShopSignInManager _shopSignInManager; private readonly IJobService _jobService; private readonly IRepository _mediaRepository; + private readonly IAccountService _accountService; public AccountApiController( IRepository smsSendRepository, @@ -55,10 +52,9 @@ public AccountApiController( IRepository userRepository, ITokenService tokenService, IWorkContext workContext, - IStaticCacheManager cacheManager, - ShopSignInManager shopSignInManager, IJobService jobService, - IRepository mediaRepository) + IRepository mediaRepository, + IAccountService accountService) { _smsSendRepository = smsSendRepository; _userManager = userManager; @@ -70,10 +66,9 @@ public AccountApiController( _tokenService = tokenService; _workContext = workContext; _webHost = configuration.GetValue(ShopKeys.WebHost); - _cacheManager = cacheManager; - _shopSignInManager = shopSignInManager; _jobService = jobService; _mediaRepository = mediaRepository; + _accountService = accountService; } [HttpGet()] @@ -164,13 +159,16 @@ public async Task RegisterByPhone(RegisterByPhoneParam model) return Result.Fail("此手机号已被注册"); //5分钟内的验证码 - var sms = _smsSendRepository - .Query(c => c.PhoneNumber == model.Phone && c.IsSucceed && !c.IsUsed && c.TemplateType == SmsTemplateType.Captcha - && c.CreatedOn >= DateTime.Now.AddMinutes(-5)).OrderByDescending(c => c.CreatedOn).FirstOrDefault(); - if (sms == null) - return Result.Fail("验证码不存在或已失效,请重新获取验证码"); - if (sms.Value != model.Captcha) - return Result.Fail("验证码错误"); + //var sms = _smsSendRepository + // .Query(c => c.PhoneNumber == model.Phone && c.IsSucceed && !c.IsUsed && c.TemplateType == SmsTemplateType.Captcha + // && c.CreatedOn >= DateTime.Now.AddMinutes(-5)).OrderByDescending(c => c.CreatedOn).FirstOrDefault(); + //if (sms == null) + // return Result.Fail("验证码不存在或已失效,请重新获取验证码"); + //if (sms.Value != model.Captcha) + // return Result.Fail("验证码错误"); + + //5分钟内的验证码 + var sms = await _accountService.ValidateGetLastSms(model?.Phone, model?.Captcha); var user = new User { @@ -251,14 +249,7 @@ public async Task LoginPhone(LoginPhoneParam model, string returnUrl = n var phone = model.Phone; //5分钟内的验证码 - var sms = _smsSendRepository - .Query(c => c.PhoneNumber == phone && c.IsSucceed && !c.IsUsed && c.TemplateType == SmsTemplateType.Captcha - && c.CreatedOn >= DateTime.Now.AddMinutes(-5)).OrderByDescending(c => c.CreatedOn).FirstOrDefault(); - if (sms == null) - return Result.Fail("验证码不存在或已失效,请重新获取验证码"); - - if (sms.Value != model.Code) - return Result.Fail("验证码错误"); + var sms = await _accountService.ValidateGetLastSms(phone, model?.Code); //设置验证码被使用 sms.IsUsed = true; @@ -277,36 +268,78 @@ public async Task LoginPhone(LoginPhoneParam model, string returnUrl = n await _userManager.UpdateAsync(user); } - //var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); - //if (!userFactors.Any(c => c == nameof(model.Phone))) - // return Result.Fail("手机未验证,不允许用手机登录"); - //var isLockedOut = _userManager.IsLockedOutAsync(user); + var isLockedOut = await _userManager.IsLockedOutAsync(user); + if (isLockedOut) + { + throw new Exception("用户已锁定,请稍后重试"); + } - var signInResult = await _shopSignInManager.SignInCheck(user); - if (signInResult == null || signInResult.Succeeded) + if (!await _signInManager.CanSignInAsync(user)) { - //如果返回null,说明被允许登录 - //如果用手机登录且双因子=true时,则设置双因子=false - if (user.TwoFactorEnabled) - { - await _userManager.SetTwoFactorEnabledAsync(user, false); - } - var token = await _tokenService.GenerateAccessToken(user); - var loginResult = new LoginResult() - { - Token = token, - Avatar = user.AvatarUrl, - Email = user.Email, - Name = user.FullName, - Phone = user.PhoneNumber - }; - return Result.Ok(loginResult); + throw new Exception("用户不允许登录,请稍后重试"); } - else if (signInResult.IsLockedOut) + + // 如果手机没有验证,则自动验证 + if (!user.PhoneNumberConfirmed) { - return Result.Fail("用户已锁定,请稍后重试"); + user.PhoneNumberConfirmed = true; + await _userManager.UpdateAsync(user); + } + + // 如果用手机登录且双因子=true时,则设置双因子=false + if (user.TwoFactorEnabled) + { + await _userManager.SetTwoFactorEnabledAsync(user, false); } - return Result.Fail("用户登录失败,请稍后重试"); + + // 重置错误次数计数器 + var failedCount = await _userManager.GetAccessFailedCountAsync(user); + if (failedCount > 0) + { + await _userManager.ResetAccessFailedCountAsync(user); + } + + var token = await _tokenService.GenerateAccessToken(user); + var loginResult = new LoginResult() + { + Token = token, + Avatar = user.AvatarUrl, + Email = user.Email, + Name = user.FullName, + Phone = user.PhoneNumber + }; + return Result.Ok(loginResult); + + //var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); + //if (!userFactors.Any(c => c == nameof(model.Phone))) + // return Result.Fail("手机未验证,不允许用手机登录"); + //var isLockedOut = _userManager.IsLockedOutAsync(user); + + //var signInResult = await _shopSignInManager.SignInCheck(user); + //if (signInResult == null || signInResult.Succeeded) + //{ + // //如果返回null,说明被允许登录 + // //如果用手机登录且双因子=true时,则设置双因子=false + // if (user.TwoFactorEnabled) + // { + // await _userManager.SetTwoFactorEnabledAsync(user, false); + // } + // var token = await _tokenService.GenerateAccessToken(user); + // var loginResult = new LoginResult() + // { + // Token = token, + // Avatar = user.AvatarUrl, + // Email = user.Email, + // Name = user.FullName, + // Phone = user.PhoneNumber + // }; + // return Result.Ok(loginResult); + //} + //else if (signInResult.IsLockedOut) + //{ + // return Result.Fail("用户已锁定,请稍后重试"); + //} + //return Result.Fail("用户登录失败,请稍后重试"); } /// @@ -548,7 +581,7 @@ public async Task SendConfirmEmail() /// [HttpPut("confirm-email")] [AllowAnonymous] - public async Task ConfirmEmail([FromBody]ConfirmEmailParam param) + public async Task ConfirmEmail([FromBody] ConfirmEmailParam param) { var user = await _userManager.FindByIdAsync(param.UserId.ToString()); if (user == null) @@ -642,7 +675,7 @@ public async Task> ForgotPassword(string name) /// [HttpPost("forgot-password-email")] [AllowAnonymous] - public async Task ForgotPasswordSendEmail([FromBody]ResetPasswordPostParam param) + public async Task ForgotPasswordSendEmail([FromBody] ResetPasswordPostParam param) { var user = await _userManager.FindByNameAsync(param.UserName); if (user == null) @@ -669,7 +702,7 @@ await _jobService.Enqueue(() => _emailSender.SendEmailAsync(user.Email, "Reset P /// [HttpPut("reset-password-email")] [AllowAnonymous] - public async Task ResetPasswordByEmail([FromBody]ResetPasswordPutParam param) + public async Task ResetPasswordByEmail([FromBody] ResetPasswordPutParam param) { var user = await _userManager.FindByNameAsync(param.UserName); if (user == null) @@ -699,7 +732,7 @@ public async Task ResetPasswordByEmail([FromBody]ResetPasswordPutParam p /// [HttpPost("forgot-password-phone")] [AllowAnonymous] - public async Task ForgotPasswordSendPhone([FromBody]ResetPasswordPostParam param) + public async Task ForgotPasswordSendPhone([FromBody] ResetPasswordPostParam param) { var user = await _userManager.FindByNameAsync(param.UserName); if (user == null) @@ -723,7 +756,7 @@ public async Task ForgotPasswordSendPhone([FromBody]ResetPasswordPostPar /// [HttpPut("reset-password-phone")] [AllowAnonymous] - public async Task ResetPasswordByPhone([FromBody]ResetPasswordPutParam param) + public async Task ResetPasswordByPhone([FromBody] ResetPasswordPutParam param) { var user = await _userManager.FindByNameAsync(param.UserName); if (user == null) @@ -920,7 +953,7 @@ public async Task LogOff() return Result.Ok(); } - async Task SendEmailConfirmation(string email, int userId, string code) + private async Task SendEmailConfirmation(string email, int userId, string code) { if (string.IsNullOrEmpty(email)) return; @@ -929,4 +962,4 @@ async Task SendEmailConfirmation(string email, int userId, string code) await _jobService.Enqueue(() => _emailSender.SendEmailAsync(email, "Confirm your account", $"Please confirm your account by clicking this link: VERIFY", true)); } } -} +} \ No newline at end of file diff --git a/src/Modules/Shop.Module.Core/Controllers/UserApiController.cs b/src/Modules/Shop.Module.Core/Controllers/UserApiController.cs index d79004fb..80faf63e 100644 --- a/src/Modules/Shop.Module.Core/Controllers/UserApiController.cs +++ b/src/Modules/Shop.Module.Core/Controllers/UserApiController.cs @@ -109,7 +109,7 @@ public async Task>> List([FromBody]S LastLoginOn = user.LastLoginOn, PhoneNumber = user.PhoneNumber, UpdatedOn = user.UpdatedOn, - RoleIds = user.Roles.Select(c => c.RoleId).Distinct().OrderBy(c => c).ToList() + RoleIds = user.Roles.Select(c => c.RoleId) // .Distinct().OrderBy(c => c).ToList() }); return Result.Ok(result); } @@ -138,7 +138,7 @@ public async Task Get(int id) LastLoginOn = user.LastLoginOn, PhoneNumber = user.PhoneNumber, UpdatedOn = user.UpdatedOn, - RoleIds = user.Roles.Select(c => c.RoleId).Distinct().OrderBy(c => c).ToList() + RoleIds = user.Roles.Select(c => c.RoleId) // .Distinct().OrderBy(c => c).ToList() }; return Result.Ok(model); } diff --git a/src/Modules/Shop.Module.Core/Extensions/ShopSignInManager.cs b/src/Modules/Shop.Module.Core/Extensions/ShopSignInManager.cs deleted file mode 100644 index 8ca028df..00000000 --- a/src/Modules/Shop.Module.Core/Extensions/ShopSignInManager.cs +++ /dev/null @@ -1,41 +0,0 @@ -using MediatR; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Shop.Module.Core.Abstractions.Events; -using System.Threading.Tasks; - -namespace Shop.Module.Core.Extensions -{ - public class ShopSignInManager : SignInManager where TUser : class - { - private readonly IMediator _mediator; - - public ShopSignInManager( - UserManager userManager, - IHttpContextAccessor contextAccessor, - IUserClaimsPrincipalFactory claimsFactory, - IOptions optionsAccessor, - ILogger> logger, - IAuthenticationSchemeProvider schemes, - IMediator mediator) - : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes) - { - _mediator = mediator; - } - - public Task SignInCheck(TUser user) - { - return base.PreSignInCheck(user); - } - - public override async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null) - { - var userId = await UserManager.GetUserIdAsync(user); - await _mediator.Publish(new UserSignedIn { UserId = int.Parse(userId) }); - await base.SignInAsync(user, authenticationProperties, authenticationMethod); - } - } -} diff --git a/src/Modules/Shop.Module.Core/Extensions/WorkContext.cs b/src/Modules/Shop.Module.Core/Extensions/WorkContext.cs index a135ce79..0408d87a 100644 --- a/src/Modules/Shop.Module.Core/Extensions/WorkContext.cs +++ b/src/Modules/Shop.Module.Core/Extensions/WorkContext.cs @@ -3,10 +3,12 @@ using Microsoft.EntityFrameworkCore; using Shop.Infrastructure; using Shop.Infrastructure.Data; +using Shop.Module.Core.Abstractions.Cache; using Shop.Module.Core.Abstractions.Data; using Shop.Module.Core.Abstractions.Entities; using Shop.Module.Core.Abstractions.Extensions; using Shop.Module.Core.Abstractions.Models; +using Shop.Module.Core.Models.Cache; using System; using System.Linq; using System.Threading.Tasks; @@ -18,18 +20,24 @@ public class WorkContext : IWorkContext private const string UserGuidCookiesName = ShopKeys.UserGuidCookiesName; private User _currentUser; - private HttpContext _httpContext; - private UserManager _userManager; - private IRepository _userRepository; + private readonly HttpContext _httpContext; + private readonly UserManager _userManager; + private readonly IRepository _userRepository; + private readonly AuthenticationConfig _config; + private readonly IStaticCacheManager _cacheManager; public WorkContext( IHttpContextAccessor contextAccessor, UserManager userManager, - IRepository userRepository) + IRepository userRepository, + AuthenticationConfig config, + IStaticCacheManager cacheManager) { _userManager = userManager; _httpContext = contextAccessor.HttpContext; _userRepository = userRepository; + _config = config; + _cacheManager = cacheManager; } public async Task GetCurrentUserAsync() @@ -113,5 +121,68 @@ private void SetUserGuidCookies(Guid userGuid) IsEssential = true }); } + + /// + /// 验证令牌并自动续签 + /// + /// + /// + /// + /// + /// + public bool ValidateToken(int userId, string token, out int statusCode, string path = "") + { + statusCode = StatusCodes.Status200OK; + + if (userId <= 0 || string.IsNullOrWhiteSpace(token)) + return false; + + var _options = _config?.Jwt; + if (_options == null) + throw new ArgumentNullException(nameof(AuthenticationConfig)); + + var key = ShopKeys.UserJwtTokenPrefix + userId; + var currentUser = _cacheManager.Get(key); + if (currentUser != null && currentUser.Token.Equals(token, StringComparison.OrdinalIgnoreCase)) + { + var utcNow = DateTime.UtcNow; + var issuer = _options.Issuer; + var jwtKey = _options.Key; + var minutes = _options.AccessTokenDurationInMinutes; + + if (currentUser.TokenExpiresOnUtc != null && currentUser.TokenExpiresOnUtc < utcNow) + { + // 过期 + _cacheManager.Remove(key); + return false; + } + else if (minutes > 0 && currentUser.TokenExpiresOnUtc == null) + { + // 当调整配置时,访问时更新配置(无过期时间->有过期时间) + currentUser.TokenUpdatedOnUtc = utcNow; + currentUser.TokenExpiresOnUtc = utcNow.AddMinutes(minutes); + + _cacheManager.Set(key, currentUser, minutes); + } + else if (currentUser.TokenExpiresOnUtc != null && (utcNow - currentUser.TokenUpdatedOnUtc).TotalMinutes >= 1) + { + // 每分钟自动续签 + // 注意:默认jwt令牌不开启过期策略的 + currentUser.TokenUpdatedOnUtc = utcNow; + currentUser.TokenExpiresOnUtc = utcNow.AddMinutes(minutes); + + _cacheManager.Set(key, currentUser, minutes); + } + + return true; + } + else + { + // 令牌不存在或令牌不一致,返回 401 + statusCode = StatusCodes.Status401Unauthorized; + } + + return false; + } } } diff --git a/src/Modules/Shop.Module.Core/ModuleInitializer.cs b/src/Modules/Shop.Module.Core/ModuleInitializer.cs index 62dabd31..8e0dd00f 100644 --- a/src/Modules/Shop.Module.Core/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.Core/ModuleInitializer.cs @@ -50,8 +50,9 @@ public void ConfigureServices(IServiceCollection services) //serviceCollection.AddSingleton(); //serviceCollection.AddScoped(); - services.AddScoped>(); - services.AddScoped, ShopSignInManager>(); + services.AddScoped>(); + //services.AddScoped>(); + //services.AddScoped, ShopSignInManager>(); // used redis services.AddSingleton(); @@ -82,7 +83,7 @@ public void ConfigureServices(IServiceCollection services) } } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.Core/Services/AccountService.cs b/src/Modules/Shop.Module.Core/Services/AccountService.cs index a1b2a25e..746aeec4 100644 --- a/src/Modules/Shop.Module.Core/Services/AccountService.cs +++ b/src/Modules/Shop.Module.Core/Services/AccountService.cs @@ -1,50 +1,50 @@ -using Microsoft.Extensions.Logging; +using Microsoft.EntityFrameworkCore; +using Shop.Infrastructure.Data; using Shop.Module.Core.Abstractions.Entities; +using Shop.Module.Core.Abstractions.Models; using Shop.Module.Core.Abstractions.Services; -using Shop.Module.Core.Abstractions.ViewModels; -using Shop.Module.Core.Extensions; using System; +using System.Linq; using System.Threading.Tasks; namespace Shop.Module.Core.Services { public class AccountService : IAccountService { - private readonly ILogger _logger; - private readonly ITokenService _tokenService; - private readonly ShopSignInManager _shopSignInManager; + private readonly IRepository _smsSendRepository; - public AccountService( - ILoggerFactory loggerFactory, - ITokenService tokenService, - ShopSignInManager shopSignInManager) + public AccountService(IRepository smsSendRepository) { - _logger = loggerFactory.CreateLogger(); - _tokenService = tokenService; - _shopSignInManager = shopSignInManager; + _smsSendRepository = smsSendRepository; } - public async Task LoginWithSignInCheck(User user) + /// + /// 验证并获取最后一条验证码 + /// + /// + /// + /// + public async Task ValidateGetLastSms(string phone, string captcha) { - var signInResult = await _shopSignInManager.SignInCheck(user); - if (signInResult.IsLockedOut) + // 5分钟内的验证码 + var startOn = DateTime.Now.AddMinutes(-5); + var endOn = DateTime.Now; + + var sms = await _smsSendRepository.Query(c => c.PhoneNumber == phone && c.IsSucceed && c.TemplateType == SmsTemplateType.Captcha && c.CreatedOn <= endOn && c.CreatedOn >= startOn) + .OrderByDescending(c => c.CreatedOn) + .FirstOrDefaultAsync(); + + if (sms == null || sms.IsUsed) { - throw new Exception("用户已锁定,请稍后重试"); + throw new Exception("验证码不存在或已失效,请重新获取验证码"); } - else if (signInResult == null || signInResult.Succeeded) + + if (sms.Value != captcha) { - var token = await _tokenService.GenerateAccessToken(user); - var loginResult = new LoginResult() - { - Token = token, - Avatar = user.AvatarUrl, - Email = user.Email, - Name = user.FullName, - Phone = user.PhoneNumber - }; - return loginResult; + throw new Exception("验证码错误"); } - throw new Exception("用户登录失败,请稍后重试"); + + return sms; } } -} +} \ No newline at end of file diff --git a/src/Modules/Shop.Module.Core/Shop.Module.Core.csproj b/src/Modules/Shop.Module.Core/Shop.Module.Core.csproj index 42f3f0af..84032077 100644 --- a/src/Modules/Shop.Module.Core/Shop.Module.Core.csproj +++ b/src/Modules/Shop.Module.Core/Shop.Module.Core.csproj @@ -1,52 +1,22 @@ - + - netstandard2.0 + netcoreapp3.1 - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/src/Modules/Shop.Module.EmailSenderSmtp/ModuleInitializer.cs b/src/Modules/Shop.Module.EmailSenderSmtp/ModuleInitializer.cs index 3348d3c3..4f741ddc 100644 --- a/src/Modules/Shop.Module.EmailSenderSmtp/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.EmailSenderSmtp/ModuleInitializer.cs @@ -8,7 +8,7 @@ namespace Shop.Module.EmailSenderSmtp { public class ModuleInitializer : IModuleInitializer { - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.EmailSenderSmtp/Shop.Module.EmailSenderSmtp.csproj b/src/Modules/Shop.Module.EmailSenderSmtp/Shop.Module.EmailSenderSmtp.csproj index 46d8ce84..fbf65f79 100644 --- a/src/Modules/Shop.Module.EmailSenderSmtp/Shop.Module.EmailSenderSmtp.csproj +++ b/src/Modules/Shop.Module.EmailSenderSmtp/Shop.Module.EmailSenderSmtp.csproj @@ -1,12 +1,12 @@ - netstandard2.0 + netcoreapp3.1 - - + + diff --git a/src/Modules/Shop.Module.Feedback/Shop.Module.Feedbacks.csproj b/src/Modules/Shop.Module.Feedback/Shop.Module.Feedbacks.csproj index b50a7896..512f8809 100644 --- a/src/Modules/Shop.Module.Feedback/Shop.Module.Feedbacks.csproj +++ b/src/Modules/Shop.Module.Feedback/Shop.Module.Feedbacks.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Feedbacks.Abstractions/Shop.Module.Feedbacks.Abstractions.csproj b/src/Modules/Shop.Module.Feedbacks.Abstractions/Shop.Module.Feedbacks.Abstractions.csproj index 951ddaf3..a16e9bc6 100644 --- a/src/Modules/Shop.Module.Feedbacks.Abstractions/Shop.Module.Feedbacks.Abstractions.csproj +++ b/src/Modules/Shop.Module.Feedbacks.Abstractions/Shop.Module.Feedbacks.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Hangfire/ModuleInitializer.cs b/src/Modules/Shop.Module.Hangfire/ModuleInitializer.cs index 7b15ab1e..e9ab9d2c 100644 --- a/src/Modules/Shop.Module.Hangfire/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.Hangfire/ModuleInitializer.cs @@ -1,7 +1,7 @@ using Hangfire; using Hangfire.Dashboard.BasicAuthorization; using Hangfire.MemoryStorage; -using Hangfire.MySql; +using Hangfire.Redis; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -13,8 +13,6 @@ using Shop.Module.Hangfire.Services; using Shop.Module.Schedule.Abstractions.Services; using StackExchange.Redis; -using System; -using System.Transactions; namespace Shop.Module.Hangfire { @@ -26,45 +24,59 @@ public void ConfigureServices(IServiceCollection services) var json = configuration.GetRequiredService().GetValue(nameof(HangfireOptions)); var hangfireConfig = JsonConvert.DeserializeObject(json) ?? new HangfireOptions(); - switch (hangfireConfig.Provider) + //services.AddHangfire(config => + //{ + // config.UseRedisStorage(ConnectionMultiplexer.Connect(hangfireConfig.RedisHangfireConnection), new RedisStorageOptions() + // { + // Db = 1, + // Prefix = "shop:" + // }); + //}); + + services.AddHangfire(config => { - case ProviderType.MySql: - services.AddHangfire(config => - config.UseStorage(new MySqlStorage(hangfireConfig.MySqlHangfireConnection, - new MySqlStorageOptions - { - TransactionIsolationLevel = IsolationLevel.ReadCommitted, - QueuePollInterval = TimeSpan.FromSeconds(15), - JobExpirationCheckInterval = TimeSpan.FromHours(1), - CountersAggregateInterval = TimeSpan.FromMinutes(5), - PrepareSchemaIfNecessary = true, - DashboardJobListLimit = 50000, - TransactionTimeout = TimeSpan.FromMinutes(1), - TablesPrefix = "Hangfire" // TablesPrefix - prefix for the tables in database. Default is none - }))); - break; - case ProviderType.SqlServer: - services.AddHangfire(config => config.UseSqlServerStorage(hangfireConfig.SqlServerHangfireConnection)); - break; - case ProviderType.Redis: - services.AddHangfire(config => - { - config.UseRedisStorage(ConnectionMultiplexer.Connect(hangfireConfig.RedisHangfireConnection)); - }); - break; - case ProviderType.Memory: - services.AddHangfire(config => - { - config.UseMemoryStorage(); - }); - break; - default: - services.AddHangfire(config => - { - config.UseMemoryStorage(); - }); - break; - } + config.UseMemoryStorage(); + }); + + //switch (hangfireConfig.Provider) + //{ + // case ProviderType.MySql: + // services.AddHangfire(config => + // config.UseStorage(new MySqlStorage(hangfireConfig.MySqlHangfireConnection, + // new MySqlStorageOptions + // { + // TransactionIsolationLevel = IsolationLevel.ReadCommitted, + // QueuePollInterval = TimeSpan.FromSeconds(15), + // JobExpirationCheckInterval = TimeSpan.FromHours(1), + // CountersAggregateInterval = TimeSpan.FromMinutes(5), + // PrepareSchemaIfNecessary = true, + // DashboardJobListLimit = 50000, + // TransactionTimeout = TimeSpan.FromMinutes(1), + // TablesPrefix = "Hangfire" // TablesPrefix - prefix for the tables in database. Default is none + // }))); + // break; + // case ProviderType.SqlServer: + // services.AddHangfire(config => config.UseSqlServerStorage(hangfireConfig.SqlServerHangfireConnection)); + // break; + // case ProviderType.Redis: + // services.AddHangfire(config => + // { + // config.UseRedisStorage(ConnectionMultiplexer.Connect(hangfireConfig.RedisHangfireConnection)); + // }); + // break; + // case ProviderType.Memory: + // services.AddHangfire(config => + // { + // config.UseMemoryStorage(); + // }); + // break; + // default: + // services.AddHangfire(config => + // { + // config.UseMemoryStorage(); + // }); + // break; + //} //services.Configure(configuration.GetSection("Hangfire")); //services.Configure(options => @@ -80,7 +92,7 @@ public void ConfigureServices(IServiceCollection services) services.AddHostedService(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //var option = app.ApplicationServices.GetRequiredService>(); var json = app.ApplicationServices.GetRequiredService().GetValue(nameof(HangfireOptions)); diff --git a/src/Modules/Shop.Module.Hangfire/Shop.Module.Hangfire.csproj b/src/Modules/Shop.Module.Hangfire/Shop.Module.Hangfire.csproj index 223724de..c6cbc2f3 100644 --- a/src/Modules/Shop.Module.Hangfire/Shop.Module.Hangfire.csproj +++ b/src/Modules/Shop.Module.Hangfire/Shop.Module.Hangfire.csproj @@ -1,17 +1,16 @@ - + - netstandard2.0 + netcoreapp3.1 - - - - - - - + + + + + + diff --git a/src/Modules/Shop.Module.Inventory.Abstractions/Shop.Module.Inventory.Abstractions.csproj b/src/Modules/Shop.Module.Inventory.Abstractions/Shop.Module.Inventory.Abstractions.csproj index a07f5af0..790ebf72 100644 --- a/src/Modules/Shop.Module.Inventory.Abstractions/Shop.Module.Inventory.Abstractions.csproj +++ b/src/Modules/Shop.Module.Inventory.Abstractions/Shop.Module.Inventory.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Inventory/Shop.Module.Inventory.csproj b/src/Modules/Shop.Module.Inventory/Shop.Module.Inventory.csproj index 720555dc..34521d83 100644 --- a/src/Modules/Shop.Module.Inventory/Shop.Module.Inventory.csproj +++ b/src/Modules/Shop.Module.Inventory/Shop.Module.Inventory.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.MQ.Abstractions/Models/IMQMessage.cs b/src/Modules/Shop.Module.MQ.Abstractions/Models/IMQMessage.cs deleted file mode 100644 index fcc6ef7c..00000000 --- a/src/Modules/Shop.Module.MQ.Abstractions/Models/IMQMessage.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Shop.Module.MQ.Abstractions.Models -{ - public interface IMQMessage - { - string Queue { get; } - - T Value { get; } - } -} diff --git a/src/Modules/Shop.Module.MQ.Abstractions/Services/IMQService.cs b/src/Modules/Shop.Module.MQ.Abstractions/Services/IMQService.cs index 0af5ab2a..06dce34c 100644 --- a/src/Modules/Shop.Module.MQ.Abstractions/Services/IMQService.cs +++ b/src/Modules/Shop.Module.MQ.Abstractions/Services/IMQService.cs @@ -1,12 +1,9 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace Shop.Module.MQ.Abstractions.Services { public interface IMQService { - Task DirectSend(string queue, T message) where T : class; - - bool DirectReceive(string queue, Action callback, out string msg) where T : class; + Task Send(string queue, T message) where T : class; } } diff --git a/src/Modules/Shop.Module.MQ.Abstractions/Shop.Module.MQ.Abstractions.csproj b/src/Modules/Shop.Module.MQ.Abstractions/Shop.Module.MQ.Abstractions.csproj index 951ddaf3..a16e9bc6 100644 --- a/src/Modules/Shop.Module.MQ.Abstractions/Shop.Module.MQ.Abstractions.csproj +++ b/src/Modules/Shop.Module.MQ.Abstractions/Shop.Module.MQ.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.MQ/Shop.Module.MQ.csproj b/src/Modules/Shop.Module.MQ/Shop.Module.MQ.csproj deleted file mode 100644 index 9f5c4f4a..00000000 --- a/src/Modules/Shop.Module.MQ/Shop.Module.MQ.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - netstandard2.0 - - - diff --git a/src/Modules/Shop.Module.MassTransitMQ/MassTransitOptions.cs b/src/Modules/Shop.Module.MassTransitMQ/MassTransitOptions.cs new file mode 100644 index 00000000..c6e93305 --- /dev/null +++ b/src/Modules/Shop.Module.MassTransitMQ/MassTransitOptions.cs @@ -0,0 +1,13 @@ +namespace Shop.Module.MassTransitMQ +{ + public class MassTransitOptions + { + public string Host { get; set; } + + public ushort Port { get; set; } + + public string Username { get; set; } + + public string Password { get; set; } + } +} diff --git a/src/Modules/Shop.Module.MassTransitMQ/MemoryMQService.cs b/src/Modules/Shop.Module.MassTransitMQ/MemoryMQService.cs new file mode 100644 index 00000000..00b9275a --- /dev/null +++ b/src/Modules/Shop.Module.MassTransitMQ/MemoryMQService.cs @@ -0,0 +1,23 @@ +using MassTransit; +using Shop.Module.MQ.Abstractions.Services; +using System; +using System.Threading.Tasks; + +namespace Shop.Module.MassTransitMQ +{ + public class MemoryMQService : IMQService + { + private readonly IBusControl _busControl; + + public MemoryMQService(IBusControl busControl) + { + _busControl = busControl; + } + + public async Task Send(string queue, T message) where T : class + { + var sendEndpoint = await _busControl.GetSendEndpoint(new Uri($"loopback://localhost/{queue}")); + await sendEndpoint.Send(message); + } + } +} diff --git a/src/Modules/Shop.Module.MassTransitMQ/ModuleInitializer.cs b/src/Modules/Shop.Module.MassTransitMQ/ModuleInitializer.cs index 382ed685..b086beca 100644 --- a/src/Modules/Shop.Module.MassTransitMQ/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.MassTransitMQ/ModuleInitializer.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Shop.Infrastructure.Modules; using Shop.Module.MassTransitMQ.Services; using Shop.Module.MQ.Abstractions.Data; @@ -13,28 +14,6 @@ public class ModuleInitializer : IModuleInitializer { public void ConfigureServices(IServiceCollection services) { - //cfg.ReceiveEndpoint(QueueKeys.ProductView, e => - //{ - // e.Consumer(); - //}); - //cfg.ReceiveEndpoint(queue, e => - //{ - // e.Handler(async context => - // { - // await Task.Run(() => - // { - // callback(context.Message); - // }); - // }); - //}); - //cfg.ReceiveEndpoint(QueueKeys.ProductView, endpoint => - // { - // endpoint.Handler(async context => - // { - // await Console.Out.WriteLineAsync($"Received: {context.Message}"); - // }); - // }); - services.AddMassTransit(x => { x.AddConsumer(); @@ -42,37 +21,44 @@ public void ConfigureServices(IServiceCollection services) x.AddConsumer(); x.AddConsumer(); - x.AddBus(p => Bus.Factory.CreateUsingInMemory(cfg => + x.UsingInMemory((context, cfg) => { cfg.ReceiveEndpoint(QueueKeys.ProductView, e => { - e.ConfigureConsumer(p); + e.ConfigureConsumer(context); }); cfg.ReceiveEndpoint(QueueKeys.ReplyAutoApproved, e => { - e.ConfigureConsumer(p); + e.ConfigureConsumer(context); }); cfg.ReceiveEndpoint(QueueKeys.ReviewAutoApproved, e => { - e.ConfigureConsumer(p); + e.ConfigureConsumer(context); }); cfg.ReceiveEndpoint(QueueKeys.PaymentReceived, e => { - e.ConfigureConsumer(p); + e.ConfigureConsumer(context); }); - cfg.ConfigureEndpoints(p); - })); + cfg.ConfigureEndpoints(context); + }); + + //x.UsingRabbitMq((context, cfg) => + //{ + //}); }); - services.AddSingleton(); - services.AddSingleton(); + services.TryAddSingleton(); + + //services.TryAddSingleton(); + + services.AddMassTransitHostedService(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.MassTransitMQ/RabbitMQService.cs b/src/Modules/Shop.Module.MassTransitMQ/RabbitMQService.cs new file mode 100644 index 00000000..8e17ca7d --- /dev/null +++ b/src/Modules/Shop.Module.MassTransitMQ/RabbitMQService.cs @@ -0,0 +1,22 @@ +using MassTransit; +using Shop.Module.MQ.Abstractions.Services; +using System; +using System.Threading.Tasks; + +namespace Shop.Module.MassTransitMQ +{ + public class RabbitMQService : IMQService + { + private readonly IBusControl _busControl; + public RabbitMQService(IBusControl busControl) + { + _busControl = busControl; + } + + public async Task Send(string queue, T message) where T : class + { + var sendEndpoint = await _busControl.GetSendEndpoint(new Uri($"queue:{queue}")); + await sendEndpoint.Send(message); + } + } +} diff --git a/src/Modules/Shop.Module.MassTransitMQ/Services/MQHostedService.cs b/src/Modules/Shop.Module.MassTransitMQ/Services/MQHostedService.cs deleted file mode 100644 index a4ed52d5..00000000 --- a/src/Modules/Shop.Module.MassTransitMQ/Services/MQHostedService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using MassTransit; -using Microsoft.Extensions.Hosting; -using System.Threading; -using System.Threading.Tasks; - -namespace Shop.Module.MassTransitMQ.Services -{ - public class MQHostedService : IHostedService - { - private readonly IBusControl _busControl; - - public MQHostedService(IBusControl busControl) - { - _busControl = busControl; - } - - public Task StartAsync(CancellationToken cancellationToken) - { - return _busControl.StartAsync(cancellationToken); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - return _busControl.StopAsync(cancellationToken); - } - } -} diff --git a/src/Modules/Shop.Module.MassTransitMQ/Services/MQService.cs b/src/Modules/Shop.Module.MassTransitMQ/Services/MQService.cs deleted file mode 100644 index e6ffa90b..00000000 --- a/src/Modules/Shop.Module.MassTransitMQ/Services/MQService.cs +++ /dev/null @@ -1,54 +0,0 @@ -using MassTransit; -using Shop.Module.MQ.Abstractions.Services; -using System; -using System.Threading.Tasks; - -namespace Shop.Module.MassTransitMQ.Services -{ - public class MQService : IMQService - { - private readonly IBusControl _busControl; - - public MQService(IBusControl busControl) - { - _busControl = busControl; - } - - public bool DirectReceive(string queue, Action callback, out string msg) where T : class - { - throw new NotImplementedException(); - - //msg = string.Empty; - //try - //{ - // var handle = _busControl.ConnectConsumer(); - // //var busControl = Bus.Factory.CreateUsingInMemory(cfg => - // //{ - // // cfg.ReceiveEndpoint(queue, e => - // // { - // // e.Handler(async context => - // // { - // // await Task.Run(() => - // // { - // // callback(context.Message); - // // }); - // // }); - // // }); - // //}); - // //busControl.StartAsync(); - //} - //catch (Exception ex) - //{ - // msg = ex.ToString(); - // return false; - //} - //return true; - } - - public async Task DirectSend(string queue, T message) where T : class - { - var sendEndpoint = await _busControl.GetSendEndpoint(new Uri($"loopback://localhost/{queue}")); - await sendEndpoint.Send(message); - } - } -} diff --git a/src/Modules/Shop.Module.MassTransitMQ/Services/ProductViewMQConsumer.cs b/src/Modules/Shop.Module.MassTransitMQ/Services/ProductViewMQConsumer.cs index 4304afa4..1ea12cfc 100644 --- a/src/Modules/Shop.Module.MassTransitMQ/Services/ProductViewMQConsumer.cs +++ b/src/Modules/Shop.Module.MassTransitMQ/Services/ProductViewMQConsumer.cs @@ -3,8 +3,6 @@ using Microsoft.Extensions.Logging; using Shop.Module.Catalog.Abstractions.Events; using Shop.Module.Core.Abstractions.Events; -using Shop.Module.Core.Abstractions.Models; -using Shop.Module.MQ.Abstractions.Models; using System; using System.Threading.Tasks; diff --git a/src/Modules/Shop.Module.MassTransitMQ/Shop.Module.MassTransitMQ.csproj b/src/Modules/Shop.Module.MassTransitMQ/Shop.Module.MassTransitMQ.csproj index 5cb40984..94d5d7eb 100644 --- a/src/Modules/Shop.Module.MassTransitMQ/Shop.Module.MassTransitMQ.csproj +++ b/src/Modules/Shop.Module.MassTransitMQ/Shop.Module.MassTransitMQ.csproj @@ -1,12 +1,15 @@ - netstandard2.0 + netcoreapp3.1 - - + + + + + diff --git a/src/Modules/Shop.Module.Orders.Abstractions/Shop.Module.Orders.Abstractions.csproj b/src/Modules/Shop.Module.Orders.Abstractions/Shop.Module.Orders.Abstractions.csproj index ac178739..098d6f5a 100644 --- a/src/Modules/Shop.Module.Orders.Abstractions/Shop.Module.Orders.Abstractions.csproj +++ b/src/Modules/Shop.Module.Orders.Abstractions/Shop.Module.Orders.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Orders/Controllers/CustomerOrderApiController.cs b/src/Modules/Shop.Module.Orders/Controllers/CustomerOrderApiController.cs index 4f41bf32..46f06ca0 100644 --- a/src/Modules/Shop.Module.Orders/Controllers/CustomerOrderApiController.cs +++ b/src/Modules/Shop.Module.Orders/Controllers/CustomerOrderApiController.cs @@ -155,7 +155,7 @@ public async Task Get(int id) } [HttpPost("grid")] - public async Task>> List([FromBody]StandardTableParam param) + public async Task>> List([FromBody] StandardTableParam param) { var user = await _workContext.GetCurrentUserAsync(); var query = _orderRepository.Query() @@ -210,17 +210,28 @@ public async Task>> List([F ProductPrice = x.ProductPrice, Quantity = x.Quantity, ShippedQuantity = x.ShippedQuantity - }).Take(2), // 列表中最多显示2个商品 + }),// EF CORE 子查询列表 top bug, 不允许此操作.Take(2), // 列表中最多显示2个商品 ItemsTotal = c.OrderItems.Sum(x => x.Quantity), ItemsCount = c.OrderItems.Count, PaymentEndOn = c.PaymentEndOn, DeliveredEndOn = c.DeliveredEndOn }); + + if (result.List?.Count() > 0) + { + result.List.ToList().ForEach(c => + { + if (c.Items?.Count() > 2) + { + c.Items = c.Items.Take(2).ToList(); + } + }); + } return Result.Ok(result); } [HttpPut("{id:int:min(1)}/cancel")] - public async Task Cancel(int id, [FromBody]OrderCancelParam reason) + public async Task Cancel(int id, [FromBody] OrderCancelParam reason) { var user = await _workContext.GetCurrentOrThrowAsync(); var order = await _orderRepository.Query() diff --git a/src/Modules/Shop.Module.Orders/ModuleInitializer.cs b/src/Modules/Shop.Module.Orders/ModuleInitializer.cs index fde48578..f7836853 100644 --- a/src/Modules/Shop.Module.Orders/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.Orders/ModuleInitializer.cs @@ -1,5 +1,6 @@ using MediatR; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Shop.Infrastructure.Modules; using Shop.Module.Orders.Abstractions.Events; @@ -23,7 +24,7 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient, PaymentReceivedHandler>(); } - public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.Orders/Shop.Module.Orders.csproj b/src/Modules/Shop.Module.Orders/Shop.Module.Orders.csproj index 64d383bf..c9ebbd20 100644 --- a/src/Modules/Shop.Module.Orders/Shop.Module.Orders.csproj +++ b/src/Modules/Shop.Module.Orders/Shop.Module.Orders.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Payments.Abstractions/Shop.Module.Payments.Abstractions.csproj b/src/Modules/Shop.Module.Payments.Abstractions/Shop.Module.Payments.Abstractions.csproj index e190b5d0..bb4074bc 100644 --- a/src/Modules/Shop.Module.Payments.Abstractions/Shop.Module.Payments.Abstractions.csproj +++ b/src/Modules/Shop.Module.Payments.Abstractions/Shop.Module.Payments.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Payments.Payments/Shop.Module.Payments.Payments.csproj b/src/Modules/Shop.Module.Payments.Payments/Shop.Module.Payments.Payments.csproj index 9f5c4f4a..cb631906 100644 --- a/src/Modules/Shop.Module.Payments.Payments/Shop.Module.Payments.Payments.csproj +++ b/src/Modules/Shop.Module.Payments.Payments/Shop.Module.Payments.Payments.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.RabbitMQ/Data/MQCustomModelBuilder.cs b/src/Modules/Shop.Module.RabbitMQ/Data/MQCustomModelBuilder.cs deleted file mode 100644 index 2b1b5aa5..00000000 --- a/src/Modules/Shop.Module.RabbitMQ/Data/MQCustomModelBuilder.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; -using Shop.Infrastructure; -using Shop.Infrastructure.Data; -using Shop.Module.Core.Abstractions.Entities; -using Shop.Module.Core.Abstractions.Models; -using Shop.Module.RabbitMQ.Models; - -namespace Shop.Module.RabbitMQ.Data -{ - public class MQCustomModelBuilder : ICustomModelBuilder - { - public void Build(ModelBuilder modelBuilder) - { - var cfg = GlobalConfiguration.Configuration; - var config = new RabbitMQOptions() - { - ConnectionString = cfg[$"{nameof(RabbitMQOptions)}:{nameof(RabbitMQOptions.ConnectionString)}"] - }; - var json = JsonConvert.SerializeObject(config); - modelBuilder.Entity().HasData( - new AppSetting(nameof(RabbitMQOptions)) - { - Module = "RabbitMQ", - IsVisibleInCommonSettingPage = true, - Value = json, - FormatType = AppSettingFormatType.Json, - Type = typeof(RabbitMQOptions).AssemblyQualifiedName - } - ); - } - } -} diff --git a/src/Modules/Shop.Module.RabbitMQ/Models/RabbitMQOptions.cs b/src/Modules/Shop.Module.RabbitMQ/Models/RabbitMQOptions.cs deleted file mode 100644 index ced40f05..00000000 --- a/src/Modules/Shop.Module.RabbitMQ/Models/RabbitMQOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Shop.Module.RabbitMQ.Models -{ - public class RabbitMQOptions - { - public string ConnectionString { get; set; } - } -} diff --git a/src/Modules/Shop.Module.RabbitMQ/ModuleInitializer.cs b/src/Modules/Shop.Module.RabbitMQ/ModuleInitializer.cs deleted file mode 100644 index f868e27a..00000000 --- a/src/Modules/Shop.Module.RabbitMQ/ModuleInitializer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Shop.Infrastructure.Modules; -using Shop.Module.MQ.Abstractions.Services; -using Shop.Module.RabbitMQ.Services; - -namespace Shop.Module.RabbitMQ -{ - public class ModuleInitializer : IModuleInitializer - { - public void ConfigureServices(IServiceCollection services) - { - // services.AddTransient(); - // 实例化太耗时间,暂时使用单例 TODO - services.AddSingleton(); - - services.AddHostedService(); - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - } - } -} diff --git a/src/Modules/Shop.Module.RabbitMQ/Services/MQService.cs b/src/Modules/Shop.Module.RabbitMQ/Services/MQService.cs deleted file mode 100644 index d660214c..00000000 --- a/src/Modules/Shop.Module.RabbitMQ/Services/MQService.cs +++ /dev/null @@ -1,223 +0,0 @@ -using EasyNetQ; -using EasyNetQ.Topology; -using Shop.Module.Core.Abstractions.Services; -using Shop.Module.MQ.Abstractions.Services; -using Shop.Module.RabbitMQ.Models; -using System; -using System.Threading.Tasks; - -namespace Shop.Module.RabbitMQ.Services -{ - public class MQService : IMQService - { - private readonly IBus _bus; - public MQService(IAppSettingService appSettingService) - { - var options = appSettingService.Get().Result; - if (string.IsNullOrWhiteSpace(options?.ConnectionString)) - throw new ArgumentNullException(nameof(RabbitMQOptions)); - - _bus = RabbitHutch.CreateBus(options.ConnectionString); - } - - //-------------extens - - private IQueue CreateQueue(IAdvancedBus adbus, string queueName = "") - { - if (adbus == null) return null; - if (string.IsNullOrEmpty(queueName)) return adbus.QueueDeclare(); - return adbus.QueueDeclare(queueName); - } - - public void TopicSubscribe(string subscriptionId, Action callback, params string[] topics) where T : class - { - using (var bus = _bus) - { - bus.Subscribe(subscriptionId, callback, (config) => - { - foreach (var item in topics) - { - config.WithTopic(item); - } - }); - } - } - - public bool TopicPublish(string topic, T message, out string msg) where T : class - { - msg = string.Empty; - try - { - using (var bus = _bus) - { - bus.Publish(message, topic); - return true; - } - } - catch (Exception ex) - { - msg = ex.ToString(); - return false; - } - } - - public bool TopicSubscribe(T t, string topic, out string msg, string exChangeName = "topic_mq") where T : class - { - msg = string.Empty; - try - { - if (string.IsNullOrWhiteSpace(topic)) - throw new Exception("topic is empty."); - using (var bus = _bus) - { - var adbus = bus.Advanced; - //var queue = adbus.QueueDeclare("user.notice.zhangsan"); - var exchange = adbus.ExchangeDeclare(exChangeName, ExchangeType.Topic); - adbus.Publish(exchange, topic, false, new Message(t)); - return true; - } - } - catch (Exception ex) - { - msg = ex.ToString(); - return false; - } - } - - public bool FanoutPush(T t, out string msg, string exChangeName = "fanout_mq", string routingKey = "") where T : class - { - msg = string.Empty; - try - { - using (var bus = _bus) - { - var adbus = bus.Advanced; - var exchange = adbus.ExchangeDeclare(exChangeName, ExchangeType.Fanout); - adbus.Publish(exchange, routingKey, false, new Message(t)); - return true; - } - } - catch (Exception ex) - { - msg = ex.ToString(); - return false; - } - } - - public void FanoutConsume(Action handler, string exChangeName = "fanout_mq", string queueName = "fanout_queue_default", string routingKey = "") where T : class - { - var bus = _bus; - var adbus = bus.Advanced; - var exchange = adbus.ExchangeDeclare(exChangeName, ExchangeType.Fanout); - var queue = CreateQueue(adbus, queueName); - adbus.Bind(exchange, queue, routingKey); - adbus.Consume(queue, registration => - { - registration.Add((message, info) => - { - handler(message.Body); - }); - }); - } - - #region "direct" - /// - /// 消息发送(direct) - /// - /// 消息类型 - /// 发送到的队列 - /// 发送内容 - public async Task DirectSend(string queue, T message) where T : class - { - await _bus.SendAsync(queue, message); - } - - /// - /// 消息接收(direct) - /// - /// 消息类型 - /// 接收的队列 - /// 回调操作 - /// 错误信息 - /// - public bool DirectReceive(string queue, Action callback, out string msg) where T : class - { - msg = string.Empty; - try - { - _bus.Receive(queue, callback); - } - catch (Exception ex) - { - msg = ex.ToString(); - return false; - } - return true; - } - - /// - /// 消息发送 - /// - /// - /// - /// - /// - /// - /// - /// - public bool DirectPush(T t, out string msg, string exChangeName = "direct_mq", string routingKey = "direct_rout_default") where T : class - { - msg = string.Empty; - try - { - using (var bus = _bus) - { - var adbus = bus.Advanced; - var exchange = adbus.ExchangeDeclare(exChangeName, ExchangeType.Direct); - adbus.Publish(exchange, routingKey, false, new Message(t)); - return true; - } - } - catch (Exception ex) - { - msg = ex.ToString(); - return false; - } - } - /// - /// 消息接收 - /// - /// - /// 消息类型 - /// 回调 - /// 交换器名 - /// 队列名 - /// 路由名 - public bool DirectConsume(Action handler, out string msg, string exChangeName = "direct_mq", string queueName = "direct_queue_default", string routingKey = "direct_rout_default") where T : class - { - msg = string.Empty; - try - { - var bus = _bus; - var adbus = bus.Advanced; - var exchange = adbus.ExchangeDeclare(exChangeName, ExchangeType.Direct); - var queue = CreateQueue(adbus, queueName); - adbus.Bind(exchange, queue, routingKey); - adbus.Consume(queue, registration => - { - registration.Add((message, info) => - { - handler(message.Body); - }); - }); - } - catch (Exception ex) - { - msg = ex.ToString(); - return false; - } - return true; - } - #endregion - } -} diff --git a/src/Modules/Shop.Module.RabbitMQ/Services/ProductViewMQBackgroundService.cs b/src/Modules/Shop.Module.RabbitMQ/Services/ProductViewMQBackgroundService.cs deleted file mode 100644 index 881a712b..00000000 --- a/src/Modules/Shop.Module.RabbitMQ/Services/ProductViewMQBackgroundService.cs +++ /dev/null @@ -1,68 +0,0 @@ -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Shop.Module.Catalog.Abstractions.Events; -using Shop.Module.Core.Abstractions.Events; -using Shop.Module.Core.Abstractions.Models; -using Shop.Module.MQ.Abstractions.Data; -using Shop.Module.MQ.Abstractions.Services; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Shop.Module.RabbitMQ.Services -{ - public class ProductViewMQBackgroundService : BackgroundService - { - private readonly ILogger _logger; - private readonly IMQService _mqService; - private readonly IServiceProvider _serviceProvider; - - public ProductViewMQBackgroundService( - ILogger logger, - IMQService mqService, - IServiceProvider serviceProvider) - { - _logger = logger; - _mqService = mqService; - _serviceProvider = serviceProvider; - } - - protected async override Task ExecuteAsync(CancellationToken stoppingToken) - { - var success = _mqService.DirectReceive(QueueKeys.ProductView, async c => - { - try - { - if (stoppingToken.IsCancellationRequested) - throw new Exception("任务已取消"); - using (var scope = _serviceProvider.CreateScope()) - { - var mediator = scope.ServiceProvider.GetRequiredService(); - await mediator.Publish(new ProductViewed - { - EntityId = c.EntityId, - UserId = c.UserId, - EntityTypeWithId = EntityTypeWithId.Product - }); - } - } - catch (Exception ex) - { - _logger.LogError("消息处理失败", ex, c); - } - }, out string message); - if (!success) - { - _logger.LogError("消息接收异常", message); - } - await Task.CompletedTask; - - //while (!stoppingToken.IsCancellationRequested) - //{ - // await Task.Delay(TimeSpan.FromSeconds(3), stoppingToken); - //} - } - } -} diff --git a/src/Modules/Shop.Module.RabbitMQ/Services/ReplyAutoApprovedMQBackgroundService.cs b/src/Modules/Shop.Module.RabbitMQ/Services/ReplyAutoApprovedMQBackgroundService.cs deleted file mode 100644 index ce78620e..00000000 --- a/src/Modules/Shop.Module.RabbitMQ/Services/ReplyAutoApprovedMQBackgroundService.cs +++ /dev/null @@ -1,63 +0,0 @@ -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Shop.Module.MQ.Abstractions.Data; -using Shop.Module.MQ.Abstractions.Services; -using Shop.Module.Reviews.Abstractions.Events; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Shop.Module.RabbitMQ.Services -{ - public class ReplyAutoApprovedMQBackgroundService : BackgroundService - { - private readonly ILogger _logger; - private readonly IMQService _mqService; - private readonly IServiceProvider _serviceProvider; - //private readonly IMediator _mediator; //切记不可注入使用 - - public ReplyAutoApprovedMQBackgroundService( - ILogger logger, - IMQService mqService, - IServiceProvider serviceProvider) - { - _logger = logger; - _mqService = mqService; - _serviceProvider = serviceProvider; - } - - protected async override Task ExecuteAsync(CancellationToken stoppingToken) - { - var success = _mqService.DirectReceive(QueueKeys.ReplyAutoApproved, async c => - { - try - { - if (stoppingToken.IsCancellationRequested) - throw new Exception("canceled"); - using (var scope = _serviceProvider.CreateScope()) - { - var mediator = scope.ServiceProvider.GetRequiredService(); - await mediator.Publish(c); - } - } - catch (Exception ex) - { - _logger.LogError("消息处理失败", ex, c); - } - }, out string message); - if (!success) - { - _logger.LogError("消息接收异常", message); - } - await Task.CompletedTask; - - //while (!stoppingToken.IsCancellationRequested) - //{ - // await Task.Delay(TimeSpan.FromSeconds(3), stoppingToken); - //} - //_logger.LogInformation(nameof(ReplyAutoApprovedMQBackgroundService) + " stopping..."); - } - } -} diff --git a/src/Modules/Shop.Module.RabbitMQ/Shop.Module.RabbitMQ.csproj b/src/Modules/Shop.Module.RabbitMQ/Shop.Module.RabbitMQ.csproj deleted file mode 100644 index 7cca0308..00000000 --- a/src/Modules/Shop.Module.RabbitMQ/Shop.Module.RabbitMQ.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard2.0 - - - - - - - - - - - - - - - diff --git a/src/Modules/Shop.Module.Reviews.Abstractions/Shop.Module.Reviews.Abstractions.csproj b/src/Modules/Shop.Module.Reviews.Abstractions/Shop.Module.Reviews.Abstractions.csproj index 059a100f..2fec96d6 100644 --- a/src/Modules/Shop.Module.Reviews.Abstractions/Shop.Module.Reviews.Abstractions.csproj +++ b/src/Modules/Shop.Module.Reviews.Abstractions/Shop.Module.Reviews.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Reviews/Controllers/ReplyApiController.cs b/src/Modules/Shop.Module.Reviews/Controllers/ReplyApiController.cs index 8f81db09..39cf2235 100644 --- a/src/Modules/Shop.Module.Reviews/Controllers/ReplyApiController.cs +++ b/src/Modules/Shop.Module.Reviews/Controllers/ReplyApiController.cs @@ -78,7 +78,7 @@ public async Task Post([FromBody]ReplyAddParam param) var isAuto = await _appSettingService.Get(ReviewKeys.IsReplyAutoApproved); if (isAuto) { - await _mqService.DirectSend(QueueKeys.ReplyAutoApproved, new ReplyAutoApprovedEvent() + await _mqService.Send(QueueKeys.ReplyAutoApproved, new ReplyAutoApprovedEvent() { ReplyId = reply.Id }); diff --git a/src/Modules/Shop.Module.Reviews/Controllers/ReviewApiController.cs b/src/Modules/Shop.Module.Reviews/Controllers/ReviewApiController.cs index 2fb3b0d9..97d3585c 100644 --- a/src/Modules/Shop.Module.Reviews/Controllers/ReviewApiController.cs +++ b/src/Modules/Shop.Module.Reviews/Controllers/ReviewApiController.cs @@ -8,7 +8,6 @@ using Shop.Module.Core.Abstractions.Models; using Shop.Module.Core.Abstractions.Services; using Shop.Module.MQ.Abstractions.Data; -using Shop.Module.MQ.Abstractions.Models; using Shop.Module.MQ.Abstractions.Services; using Shop.Module.Orders.Abstractions.Entities; using Shop.Module.Orders.Abstractions.Models; @@ -16,6 +15,7 @@ using Shop.Module.Reviews.Abstractions.Entities; using Shop.Module.Reviews.Abstractions.Events; using Shop.Module.Reviews.Abstractions.Models; +using Shop.Module.Reviews.Abstractions.Services; using Shop.Module.Reviews.Abstractions.ViewModels; using System; using System.Linq; @@ -34,6 +34,7 @@ public class ReviewApiController : ControllerBase private readonly IRepository _orderRepository; private readonly IMQService _mqService; private readonly IAppSettingService _appSettingService; + private readonly IRepository _replyRepository; public ReviewApiController( IRepository reviewRepository, @@ -41,7 +42,8 @@ public ReviewApiController( IWorkContext workContext, IRepository orderRepository, IMQService mqService, - IAppSettingService appSettingService) + IAppSettingService appSettingService, + IRepository replyRepository) { _reviewRepository = reviewRepository; _supportRepository = supportRepository; @@ -49,10 +51,11 @@ public ReviewApiController( _orderRepository = orderRepository; _mqService = mqService; _appSettingService = appSettingService; + _replyRepository = replyRepository; } [HttpPost] - public async Task AddReview([FromBody]ReviewAddParam param) + public async Task AddReview([FromBody] ReviewAddParam param) { var user = await _workContext.GetCurrentOrThrowAsync(); var anyType = entityTypeIds.Any(c => c == param.EntityTypeId); @@ -124,7 +127,7 @@ public async Task AddReview([FromBody]ReviewAddParam param) var isAuto = await _appSettingService.Get(ReviewKeys.IsReviewAutoApproved); if (isAuto) { - await _mqService.DirectSend(QueueKeys.ReviewAutoApproved, new ReviewAutoApprovedEvent() + await _mqService.Send(QueueKeys.ReviewAutoApproved, new ReviewAutoApprovedEvent() { ReviewId = review.Id }); @@ -134,7 +137,7 @@ public async Task AddReview([FromBody]ReviewAddParam param) [HttpPost("info")] [AllowAnonymous] - public async Task Info([FromBody]ReviewInfoParam param) + public async Task Info([FromBody] ReviewInfoParam param) { var any = entityTypeIds.Any(c => c == param.EntityTypeId); if (!any) @@ -164,7 +167,7 @@ public async Task Info([FromBody]ReviewInfoParam param) [HttpPost("list")] [AllowAnonymous] - public async Task List([FromBody]ReviewListQueryParam param) + public async Task List([FromBody] ReviewListQueryParam param) { var any = entityTypeIds.Any(c => c == param.EntityTypeId); if (!any) @@ -190,8 +193,25 @@ public async Task List([FromBody]ReviewListQueryParam param) Avatar = c.User.AvatarUrl, ReplieCount = c.Replies.Where(x => x.Status == ReplyStatus.Approved).Count(), MediaUrls = c.Medias.OrderBy(x => x.DisplayOrder).Select(x => x.Media.Url), - Replies = c.Replies.Where(x => x.Status == ReplyStatus.Approved && x.ParentId == null) - .OrderByDescending(x => x.SupportCount).ThenByDescending(x => x.Id) + //Replies = c.Replies.Where(x => x.Status == ReplyStatus.Approved && x.ParentId == null) + //.OrderByDescending(x => x.SupportCount).ThenByDescending(x => x.Id) + //.Take(2).Select(x => new ReplyListResult() + //{ + // Id = x.Id, + // Comment = x.Comment, + // ReplierName = x.ReplierName, + // CreatedOn = x.CreatedOn, + // SupportCount = x.SupportCount + //}) + }).Take(param.Take).ToListAsync(); + + // bug todo 待优化 + result.ForEach(c => + { + if (c.ReplieCount > 0) + { + c.Replies = _replyRepository.Query(x => x.ReviewId == c.Id && x.Status == ReplyStatus.Approved && x.ParentId == null) + .OrderByDescending(x => x.SupportCount).ThenByDescending(x => x.Id) .Take(2).Select(x => new ReplyListResult() { Id = x.Id, @@ -199,14 +219,16 @@ public async Task List([FromBody]ReviewListQueryParam param) ReplierName = x.ReplierName, CreatedOn = x.CreatedOn, SupportCount = x.SupportCount - }) - }).Take(param.Take).ToListAsync(); + }).ToList(); + } + }); + return Result.Ok(result); } [HttpPost("grid")] [AllowAnonymous] - public async Task>> Grid([FromBody]StandardTableParam param) + public async Task>> Grid([FromBody] StandardTableParam param) { var search = param?.Search; if (search == null) @@ -254,17 +276,39 @@ public async Task>> Grid([FromBody] Avatar = c.User.AvatarUrl, ReplieCount = c.Replies.Where(x => x.Status == ReplyStatus.Approved).Count(), MediaUrls = c.Medias.OrderBy(x => x.DisplayOrder).Select(x => x.Media.Url), - Replies = c.Replies.Where(x => x.Status == ReplyStatus.Approved && x.ParentId == null) - .OrderByDescending(x => x.SupportCount).ThenByDescending(x => x.Id) - .Take(2).Select(x => new ReplyListResult() + //Replies = c.Replies.Where(x => x.Status == ReplyStatus.Approved && x.ParentId == null) + //.OrderByDescending(x => x.SupportCount).ThenByDescending(x => x.Id) + //.Take(2).Select(x => new ReplyListResult() + //{ + // Id = x.Id, + // Comment = x.Comment, + // ReplierName = x.ReplierName, + // CreatedOn = x.CreatedOn, + // SupportCount = x.SupportCount + //}) + }); + + if (result?.List?.Count() > 0) + { + // bug todo 待优化 + result.List.ToList().ForEach(c => + { + if (c.ReplieCount > 0) { - Id = x.Id, - Comment = x.Comment, - ReplierName = x.ReplierName, - CreatedOn = x.CreatedOn, - SupportCount = x.SupportCount - }) + c.Replies = _replyRepository.Query(x => x.ReviewId == c.Id && x.Status == ReplyStatus.Approved && x.ParentId == null) + .OrderByDescending(x => x.SupportCount).ThenByDescending(x => x.Id) + .Take(2).Select(x => new ReplyListResult() + { + Id = x.Id, + Comment = x.Comment, + ReplierName = x.ReplierName, + CreatedOn = x.CreatedOn, + SupportCount = x.SupportCount + }).ToList(); + } }); + } + return Result.Ok(result); } diff --git a/src/Modules/Shop.Module.Reviews/ModuleInitializer.cs b/src/Modules/Shop.Module.Reviews/ModuleInitializer.cs index 2e8750c4..a271e533 100644 --- a/src/Modules/Shop.Module.Reviews/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.Reviews/ModuleInitializer.cs @@ -23,7 +23,7 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient, ReviewAutoApprovedHandler>(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.Reviews/Shop.Module.Reviews.csproj b/src/Modules/Shop.Module.Reviews/Shop.Module.Reviews.csproj index 8e661cee..38a02f51 100644 --- a/src/Modules/Shop.Module.Reviews/Shop.Module.Reviews.csproj +++ b/src/Modules/Shop.Module.Reviews/Shop.Module.Reviews.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.SampleData/ModuleInitializer.cs b/src/Modules/Shop.Module.SampleData/ModuleInitializer.cs index 4d571313..482bf1c3 100644 --- a/src/Modules/Shop.Module.SampleData/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.SampleData/ModuleInitializer.cs @@ -16,7 +16,7 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.SampleData/Shop.Module.SampleData.csproj b/src/Modules/Shop.Module.SampleData/Shop.Module.SampleData.csproj index 63ef2a8e..2d554770 100644 --- a/src/Modules/Shop.Module.SampleData/Shop.Module.SampleData.csproj +++ b/src/Modules/Shop.Module.SampleData/Shop.Module.SampleData.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Schedule.Abstractions/Services/IJobService.cs b/src/Modules/Shop.Module.Schedule.Abstractions/Services/IJobService.cs index df67993d..4174ed13 100644 --- a/src/Modules/Shop.Module.Schedule.Abstractions/Services/IJobService.cs +++ b/src/Modules/Shop.Module.Schedule.Abstractions/Services/IJobService.cs @@ -32,4 +32,4 @@ public interface IJobService /// Task AddOrUpdate(string recurringJobId, Expression> methodCall, Func cronExpression); } -} +} \ No newline at end of file diff --git a/src/Modules/Shop.Module.Schedule.Abstractions/Shop.Module.Schedule.Abstractions.csproj b/src/Modules/Shop.Module.Schedule.Abstractions/Shop.Module.Schedule.Abstractions.csproj index 9f5c4f4a..cb631906 100644 --- a/src/Modules/Shop.Module.Schedule.Abstractions/Shop.Module.Schedule.Abstractions.csproj +++ b/src/Modules/Shop.Module.Schedule.Abstractions/Shop.Module.Schedule.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Schedule/Shop.Module.Schedule.csproj b/src/Modules/Shop.Module.Schedule/Shop.Module.Schedule.csproj index 7f8d6cba..3e423e40 100644 --- a/src/Modules/Shop.Module.Schedule/Shop.Module.Schedule.csproj +++ b/src/Modules/Shop.Module.Schedule/Shop.Module.Schedule.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Shipments.Abstractions/Shop.Module.Shipments.Abstractions.csproj b/src/Modules/Shop.Module.Shipments.Abstractions/Shop.Module.Shipments.Abstractions.csproj index 54cb50da..a4a256c7 100644 --- a/src/Modules/Shop.Module.Shipments.Abstractions/Shop.Module.Shipments.Abstractions.csproj +++ b/src/Modules/Shop.Module.Shipments.Abstractions/Shop.Module.Shipments.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Shipments/Shop.Module.Shipments.csproj b/src/Modules/Shop.Module.Shipments/Shop.Module.Shipments.csproj index 2209f5bc..f36dbc4b 100644 --- a/src/Modules/Shop.Module.Shipments/Shop.Module.Shipments.csproj +++ b/src/Modules/Shop.Module.Shipments/Shop.Module.Shipments.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Shipping.Abstractions/Shop.Module.Shipping.Abstractions.csproj b/src/Modules/Shop.Module.Shipping.Abstractions/Shop.Module.Shipping.Abstractions.csproj index 951ddaf3..a16e9bc6 100644 --- a/src/Modules/Shop.Module.Shipping.Abstractions/Shop.Module.Shipping.Abstractions.csproj +++ b/src/Modules/Shop.Module.Shipping.Abstractions/Shop.Module.Shipping.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.Shipping/Shop.Module.Shipping.csproj b/src/Modules/Shop.Module.Shipping/Shop.Module.Shipping.csproj index a120b6bd..2ec2369c 100644 --- a/src/Modules/Shop.Module.Shipping/Shop.Module.Shipping.csproj +++ b/src/Modules/Shop.Module.Shipping/Shop.Module.Shipping.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.ShoppingCart.Abstractions/Shop.Module.ShoppingCart.Abstractions.csproj b/src/Modules/Shop.Module.ShoppingCart.Abstractions/Shop.Module.ShoppingCart.Abstractions.csproj index a07f5af0..790ebf72 100644 --- a/src/Modules/Shop.Module.ShoppingCart.Abstractions/Shop.Module.ShoppingCart.Abstractions.csproj +++ b/src/Modules/Shop.Module.ShoppingCart.Abstractions/Shop.Module.ShoppingCart.Abstractions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.ShoppingCart/ModuleInitializer.cs b/src/Modules/Shop.Module.ShoppingCart/ModuleInitializer.cs index 98dcde0d..dacf7032 100644 --- a/src/Modules/Shop.Module.ShoppingCart/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.ShoppingCart/ModuleInitializer.cs @@ -18,7 +18,7 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient, UserSignedInHandler>(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.ShoppingCart/Shop.Module.ShoppingCart.csproj b/src/Modules/Shop.Module.ShoppingCart/Shop.Module.ShoppingCart.csproj index 24a5eb53..d34d645f 100644 --- a/src/Modules/Shop.Module.ShoppingCart/Shop.Module.ShoppingCart.csproj +++ b/src/Modules/Shop.Module.ShoppingCart/Shop.Module.ShoppingCart.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.SmsSenderAliyun/ModuleInitializer.cs b/src/Modules/Shop.Module.SmsSenderAliyun/ModuleInitializer.cs index 5f7a39dc..6b1913bf 100644 --- a/src/Modules/Shop.Module.SmsSenderAliyun/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.SmsSenderAliyun/ModuleInitializer.cs @@ -9,7 +9,7 @@ namespace Shop.Module.SmsSenderAliyun { public class ModuleInitializer : IModuleInitializer { - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.SmsSenderAliyun/Shop.Module.SmsSenderAliyun.csproj b/src/Modules/Shop.Module.SmsSenderAliyun/Shop.Module.SmsSenderAliyun.csproj index 5bbe75c8..f9720598 100644 --- a/src/Modules/Shop.Module.SmsSenderAliyun/Shop.Module.SmsSenderAliyun.csproj +++ b/src/Modules/Shop.Module.SmsSenderAliyun/Shop.Module.SmsSenderAliyun.csproj @@ -1,11 +1,11 @@ - + - netstandard2.0 + netcoreapp3.1 - + diff --git a/src/Modules/Shop.Module.StorageGitHub/ModuleInitializer.cs b/src/Modules/Shop.Module.StorageGitHub/ModuleInitializer.cs index d8573c15..b331be2c 100644 --- a/src/Modules/Shop.Module.StorageGitHub/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.StorageGitHub/ModuleInitializer.cs @@ -14,7 +14,7 @@ public void ConfigureServices(IServiceCollection serviceCollection) //serviceCollection.AddSingleton(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } } diff --git a/src/Modules/Shop.Module.StorageGitHub/Shop.Module.StorageGitHub.csproj b/src/Modules/Shop.Module.StorageGitHub/Shop.Module.StorageGitHub.csproj index 5b63c963..d595697b 100644 --- a/src/Modules/Shop.Module.StorageGitHub/Shop.Module.StorageGitHub.csproj +++ b/src/Modules/Shop.Module.StorageGitHub/Shop.Module.StorageGitHub.csproj @@ -1,12 +1,12 @@ - netstandard2.0 + netcoreapp3.1 - - + + diff --git a/src/Modules/Shop.Module.StorageLocal/ModuleInitializer.cs b/src/Modules/Shop.Module.StorageLocal/ModuleInitializer.cs index 94cb4ed0..adc9c296 100644 --- a/src/Modules/Shop.Module.StorageLocal/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.StorageLocal/ModuleInitializer.cs @@ -15,7 +15,7 @@ public void ConfigureServices(IServiceCollection serviceCollection) serviceCollection.AddScoped(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.StorageLocal/Shop.Module.StorageLocal.csproj b/src/Modules/Shop.Module.StorageLocal/Shop.Module.StorageLocal.csproj index 951ddaf3..a16e9bc6 100644 --- a/src/Modules/Shop.Module.StorageLocal/Shop.Module.StorageLocal.csproj +++ b/src/Modules/Shop.Module.StorageLocal/Shop.Module.StorageLocal.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netcoreapp3.1 diff --git a/src/Modules/Shop.Module.StorageSm/ModuleInitializer.cs b/src/Modules/Shop.Module.StorageSm/ModuleInitializer.cs index 11dfec7f..b05f04d0 100644 --- a/src/Modules/Shop.Module.StorageSm/ModuleInitializer.cs +++ b/src/Modules/Shop.Module.StorageSm/ModuleInitializer.cs @@ -15,7 +15,7 @@ public void ConfigureServices(IServiceCollection serviceCollection) serviceCollection.AddScoped(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } diff --git a/src/Modules/Shop.Module.StorageSm/Shop.Module.StorageSm.csproj b/src/Modules/Shop.Module.StorageSm/Shop.Module.StorageSm.csproj index bd8160dc..a7c9df2c 100644 --- a/src/Modules/Shop.Module.StorageSm/Shop.Module.StorageSm.csproj +++ b/src/Modules/Shop.Module.StorageSm/Shop.Module.StorageSm.csproj @@ -1,11 +1,11 @@ - netstandard2.0 + netcoreapp3.1 - + diff --git a/src/Shop.Infrastructure/AuthenticationConfig.cs b/src/Shop.Infrastructure/AuthenticationConfig.cs index bd363630..6e879e8c 100644 --- a/src/Shop.Infrastructure/AuthenticationConfig.cs +++ b/src/Shop.Infrastructure/AuthenticationConfig.cs @@ -4,13 +4,15 @@ public class AuthenticationConfig { public AuthenticationJwtConfig Jwt { get; set; } = new AuthenticationJwtConfig(); } + public class AuthenticationJwtConfig { public string Key { get; set; } public string Issuer { get; set; } + /// /// 提示:暂未开启JWT过期策略,此时间用于令牌生成过期时间及令牌续签过期时间 /// public int AccessTokenDurationInMinutes { get; set; } } -} +} \ No newline at end of file diff --git a/src/Shop.Infrastructure/Modules/IModuleInitializer.cs b/src/Shop.Infrastructure/Modules/IModuleInitializer.cs index a30e3af6..26ee3295 100644 --- a/src/Shop.Infrastructure/Modules/IModuleInitializer.cs +++ b/src/Shop.Infrastructure/Modules/IModuleInitializer.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; namespace Shop.Infrastructure.Modules { @@ -8,6 +8,6 @@ public interface IModuleInitializer { void ConfigureServices(IServiceCollection serviceCollection); - void Configure(IApplicationBuilder app, IHostingEnvironment env); + void Configure(IApplicationBuilder app, IWebHostEnvironment env); } } diff --git a/src/Shop.Infrastructure/Result.cs b/src/Shop.Infrastructure/Result.cs index 272e3b63..169d0584 100644 --- a/src/Shop.Infrastructure/Result.cs +++ b/src/Shop.Infrastructure/Result.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Mvc.ModelBinding; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; namespace Shop.Infrastructure @@ -21,18 +20,6 @@ public static Result Fail(string error) return new Result(false, error); } - public static Result Fail(ModelStateDictionary modelStates) - { - IEnumerable errors = null; - if (modelStates != null && !modelStates.IsValid) - { - errors = from modelState in modelStates.Values - from error in modelState?.Errors - select error.ErrorMessage; - } - return new Result(false, errors != null ? string.Join("\r\n", errors) : null); - } - public static Result Fail(TValue value, string error) { return new Result(value, false, error); diff --git a/src/Shop.Infrastructure/SequentialMediator.cs b/src/Shop.Infrastructure/SequentialMediator.cs deleted file mode 100644 index 2b9c702b..00000000 --- a/src/Shop.Infrastructure/SequentialMediator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using MediatR; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Shop.Infrastructure -{ - public class SequentialMediator : Mediator - { - public SequentialMediator(ServiceFactory serviceFactory) : base(serviceFactory) - { - } - - protected async override Task PublishCore(IEnumerable> allHandlers) - { - foreach (var handler in allHandlers) - { - await handler().ConfigureAwait(false); - } - } - } -} diff --git a/src/Shop.Infrastructure/Shop.Infrastructure.csproj b/src/Shop.Infrastructure/Shop.Infrastructure.csproj index 26bd4014..ecb5b6c6 100644 --- a/src/Shop.Infrastructure/Shop.Infrastructure.csproj +++ b/src/Shop.Infrastructure/Shop.Infrastructure.csproj @@ -1,15 +1,19 @@ - - netstandard2.0 - - - - - - - - - + + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/src/Shop.Infrastructure/Web/ModelBinders/InvariantDecimalModelBinder.cs b/src/Shop.Infrastructure/Web/ModelBinders/InvariantDecimalModelBinder.cs deleted file mode 100644 index 7e7bbb00..00000000 --- a/src/Shop.Infrastructure/Web/ModelBinders/InvariantDecimalModelBinder.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Globalization; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; -using Microsoft.Extensions.Logging; - -namespace Shop.Infrastructure.Web.ModelBinders -{ - public class InvariantDecimalModelBinder : IModelBinder - { - private readonly SimpleTypeModelBinder _baseBinder; - - public InvariantDecimalModelBinder(Type modelType, ILoggerFactory loggerFactory) - { - _baseBinder = new SimpleTypeModelBinder(modelType, loggerFactory); - } - - public Task BindModelAsync(ModelBindingContext bindingContext) - { - if (bindingContext == null) - { - throw new ArgumentNullException(nameof(bindingContext)); - } - - var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); - - if (valueProviderResult != ValueProviderResult.None) - { - bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); - - var valueAsString = valueProviderResult.FirstValue; - decimal result; - - if (decimal.TryParse(valueAsString, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out result)) - { - bindingContext.Result = ModelBindingResult.Success(result); - return Task.CompletedTask; - } - } - - // If we haven't handled it, then we'll let the base SimpleTypeModelBinder handle it - return _baseBinder.BindModelAsync(bindingContext); - } - } -} diff --git a/src/Shop.Infrastructure/Web/ModelBinders/InvariantDecimalModelBinderProvider.cs b/src/Shop.Infrastructure/Web/ModelBinders/InvariantDecimalModelBinderProvider.cs deleted file mode 100644 index 931debb3..00000000 --- a/src/Shop.Infrastructure/Web/ModelBinders/InvariantDecimalModelBinderProvider.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.Extensions.Logging; - -namespace Shop.Infrastructure.Web.ModelBinders -{ - public class InvariantDecimalModelBinderProvider : IModelBinderProvider - { - public IModelBinder GetBinder(ModelBinderProviderContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - - if (!context.Metadata.IsComplexType && (context.Metadata.ModelType == typeof(decimal) || context.Metadata.ModelType == typeof(decimal?))) - { - var loggerFactory = (ILoggerFactory)context.Services.GetService(typeof(ILoggerFactory)); - return new InvariantDecimalModelBinder(context.Metadata.ModelType, loggerFactory); - } - - return null; - } - } -} diff --git a/src/Shop.Infrastructure/Web/StandardTable/StandardTableExtension.cs b/src/Shop.Infrastructure/Web/StandardTable/StandardTableExtension.cs index 1c3786de..047b6e3c 100644 --- a/src/Shop.Infrastructure/Web/StandardTable/StandardTableExtension.cs +++ b/src/Shop.Infrastructure/Web/StandardTable/StandardTableExtension.cs @@ -120,5 +120,4 @@ public static async Task> ToStandardTableResultNoPr }; } } - -} +} \ No newline at end of file diff --git a/src/Shop.WebApi/Dockerfile b/src/Shop.WebApi/Dockerfile index 50a8efde..2a95ef75 100644 --- a/src/Shop.WebApi/Dockerfile +++ b/src/Shop.WebApi/Dockerfile @@ -1,9 +1,11 @@ -FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM microsoft/dotnet:2.2-sdk AS build +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build WORKDIR /src COPY ["src/Shop.WebApi/Shop.WebApi.csproj", "src/Shop.WebApi/"] COPY ["src/Modules/Shop.Module.Reviews/Shop.Module.Reviews.csproj", "src/Modules/Shop.Module.Reviews/"] @@ -25,14 +27,12 @@ COPY ["src/Modules/Shop.Module.Hangfire/Shop.Module.Hangfire.csproj", "src/Modul COPY ["src/Modules/Shop.Module.Shipments.Abstractions/Shop.Module.Shipments.Abstractions.csproj", "src/Modules/Shop.Module.Shipments.Abstractions/"] COPY ["src/Modules/Shop.Module.StorageGitHub/Shop.Module.StorageGitHub.csproj", "src/Modules/Shop.Module.StorageGitHub/"] COPY ["src/Modules/Shop.Module.EmailSenderSmtp/Shop.Module.EmailSenderSmtp.csproj", "src/Modules/Shop.Module.EmailSenderSmtp/"] -COPY ["src/Modules/Shop.Module.RabbitMQ/Shop.Module.RabbitMQ.csproj", "src/Modules/Shop.Module.RabbitMQ/"] COPY ["src/Modules/Shop.Module.ShoppingCart.Abstractions/Shop.Module.ShoppingCart.Abstractions.csproj", "src/Modules/Shop.Module.ShoppingCart.Abstractions/"] COPY ["src/Modules/Shop.Module.Feedback/Shop.Module.Feedbacks.csproj", "src/Modules/Shop.Module.Feedback/"] COPY ["src/Modules/Shop.Module.Feedbacks.Abstractions/Shop.Module.Feedbacks.Abstractions.csproj", "src/Modules/Shop.Module.Feedbacks.Abstractions/"] COPY ["src/Modules/Shop.Module.SmsSenderAliyun/Shop.Module.SmsSenderAliyun.csproj", "src/Modules/Shop.Module.SmsSenderAliyun/"] COPY ["src/Modules/Shop.Module.Shipping/Shop.Module.Shipping.csproj", "src/Modules/Shop.Module.Shipping/"] COPY ["src/Modules/Shop.Module.Payments.Payments/Shop.Module.Payments.Payments.csproj", "src/Modules/Shop.Module.Payments.Payments/"] -COPY ["src/Modules/Shop.Module.MQ/Shop.Module.MQ.csproj", "src/Modules/Shop.Module.MQ/"] COPY ["src/Modules/Shop.Module.MassTransitMQ/Shop.Module.MassTransitMQ.csproj", "src/Modules/Shop.Module.MassTransitMQ/"] COPY ["src/Modules/Shop.Module.ShoppingCart/Shop.Module.ShoppingCart.csproj", "src/Modules/Shop.Module.ShoppingCart/"] COPY ["src/Modules/Shop.Module.StorageSm/Shop.Module.StorageSm.csproj", "src/Modules/Shop.Module.StorageSm/"] @@ -44,12 +44,12 @@ COPY ["src/Modules/Shop.Module.Shipments/Shop.Module.Shipments.csproj", "src/Mod RUN dotnet restore "src/Shop.WebApi/Shop.WebApi.csproj" COPY . . WORKDIR "/src/src/Shop.WebApi" -RUN dotnet build "Shop.WebApi.csproj" -c Release -o /app +RUN dotnet build "Shop.WebApi.csproj" -c Release -o /app/build FROM build AS publish -RUN dotnet publish "Shop.WebApi.csproj" -c Release -o /app +RUN dotnet publish "Shop.WebApi.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app -COPY --from=publish /app . +COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "Shop.WebApi.dll"] \ No newline at end of file diff --git a/src/Shop.WebApi/Extensions/ApplicationBuilderExtensions.cs b/src/Shop.WebApi/Extensions/ApplicationBuilderExtensions.cs index 0e846157..6f11a796 100644 --- a/src/Shop.WebApi/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Shop.WebApi/Extensions/ApplicationBuilderExtensions.cs @@ -7,26 +7,19 @@ namespace Shop.WebApi.Extensions { public static class ApplicationBuilderExtensions { - public static void UseCustomizedConfigure(this IApplicationBuilder app, IHostingEnvironment env) + public static void UseCustomizedConfigure(this IApplicationBuilder app, IWebHostEnvironment env) { - //跨域 - app.UseCors(option => - { - option.AllowAnyHeader(); - option.AllowAnyMethod(); - option.AllowAnyOrigin(); - option.AllowCredentials(); - }); - - //JWT - app.UseAuthentication(); + ////跨域 + //app.UseCors(option => + //{ + // option.AllowAnyHeader(); + // option.AllowAnyMethod(); + // option.AllowAnyOrigin(); + // option.AllowCredentials(); + //}); - //Swagger - app.UseSwagger(); - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "Shop API V1"); - }); + ////JWT + //app.UseAuthentication(); //module var moduleInitializers = app.ApplicationServices.GetServices(); diff --git a/src/Shop.WebApi/Extensions/ServiceCollectionExtensions.cs b/src/Shop.WebApi/Extensions/ServiceCollectionExtensions.cs index 5a771969..336157f1 100644 --- a/src/Shop.WebApi/Extensions/ServiceCollectionExtensions.cs +++ b/src/Shop.WebApi/Extensions/ServiceCollectionExtensions.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; using Newtonsoft.Json; using Shop.Infrastructure; using Shop.Infrastructure.Data; @@ -17,7 +18,6 @@ using Shop.Module.Core.Extensions; using Shop.WebApi.Filters; using Shop.WebApi.Handlers; -using Swashbuckle.AspNetCore.Swagger; using System; using System.IO; using System.Linq; @@ -29,7 +29,7 @@ namespace Shop.WebApi.Extensions { public static class ServiceCollectionExtensions { - public static void AddCustomizedConfigureServices(this IServiceCollection services, IConfiguration configuration, IHostingEnvironment env) + public static void AddCustomizedConfigureServices(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment env) { // TODO // 本项目仅api,静态资源将不在存储,待优化配置 @@ -70,7 +70,7 @@ public static void AddCustomizedConfigureServices(this IServiceCollection servic options.Filters.Add(); options.Filters.Add(); }) - .AddJsonOptions(options => + .AddNewtonsoftJson(options => { // 忽略循环引用 options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; @@ -81,7 +81,7 @@ public static void AddCustomizedConfigureServices(this IServiceCollection servic // 设置输入/输出时间格式 options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; // json to datetime 2019-02-26T22:34:13.000Z -> 2019-02-27 06:34:13 options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + }).SetCompatibilityVersion(CompatibilityVersion.Version_3_0); // 验证错误会自动触发HTTP 400响应。禁止ModelState无效时自动返回错误,仅在api项目中配置。 services.Configure(options => @@ -117,13 +117,16 @@ public static void AddCustomizedConfigureServices(this IServiceCollection servic //options.SignIn.RequireConfirmedPhoneNumber = true; }); - services.AddScoped(p => p.GetService); - services.AddScoped(); + // mediatR + services.AddMediatR(AppDomain.CurrentDomain.GetAssemblies()); + + //services.AddScoped(p => p.GetService); + //services.AddScoped(); // swagger services.AddSwaggerGen(c => { - c.SwaggerDoc("v1", new Info { Title = "Shop API", Version = "v1" }); + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Shop Api", Version = "1.0.0" }); }); } diff --git a/src/Shop.WebApi/Filters/CustomActionFilterAttribute.cs b/src/Shop.WebApi/Filters/CustomActionFilterAttribute.cs index c70303bf..fad2cb57 100644 --- a/src/Shop.WebApi/Filters/CustomActionFilterAttribute.cs +++ b/src/Shop.WebApi/Filters/CustomActionFilterAttribute.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Shop.Infrastructure; +using System.Linq; namespace Shop.WebApi.Filters { @@ -12,98 +13,23 @@ public override void OnActionExecuting(ActionExecutingContext context) // 由于PermissionHandler暂时无法直接返回401,暂时调整为在方法执行前验证401 if (context.HttpContext.Response.StatusCode == StatusCodes.Status401Unauthorized) { - context.Result = new JsonResult(Result.Fail("请重新登录!")); + var result = Result.Fail("登录已失效,请重新登录"); + context.Result = new JsonResult(result); } else if (context.HttpContext.Response.StatusCode == StatusCodes.Status403Forbidden) { - context.Result = new JsonResult(Result.Fail("您无权限访问!")); + var result = Result.Fail("您无权限访问"); + context.Result = new JsonResult(result); } else { if (!context.ModelState.IsValid) { - // 验证错误 - context.Result = new JsonResult(Result.Fail(context.ModelState)); + var error = context.ModelState.Values.FirstOrDefault()?.Errors?.FirstOrDefault()?.ErrorMessage ?? "参数异常"; + context.Result = new JsonResult(Result.Fail(error)); } } base.OnActionExecuting(context); } - - //public void OnActionExecuting(ActionExecutingContext context) - //{ - // //var xx = _memoryCache.GetOrCreate(context.GetHashCode().ToString(), c => - // //{ - // // return Guid.NewGuid().ToString(); - // //}); - - // //既然 context.HttpContext.GetHashCode() 执行前中后,都是一样的,那么其实也可以使用其他缓存 - // //甚至直接使用 他自身的缓存 - - // //var logService = (IMongoLogService)context.HttpContext.RequestServices.GetService(typeof(IMongoLogService)); - // //var request = context.HttpContext.Request; - // //var model = new MongoApiLog - // //{ - // // Host = request.Host.Host, - // // Https = request.IsHttps, - // // Path = request.Path, - // // Port = request.Host.Port ?? 0, - // // Url = request.Host.Value + request.Path + (request.Method.Equals("get", StringComparison.OrdinalIgnoreCase) ? request.QueryString.ToString() : ""), - // // Mehod = request.Method, - // // Parameter = request.QueryString.ToString(), - // // SendTime = DateTime.Now, - // // Remark = "begin:" + context.HttpContext.GetHashCode().ToString() - // //}; - // //logService.DirectSend(MongoLogQueue.ApiLogQueue, model); - - // // if (!ModelState.IsValid) return BadRequest("参数错误!"); - // //if (context.ModelState.IsValid) return; - - // //var stopwach = new Stopwatch(); - // //stopwach.Start(); - // //context.HttpContext.Items.Add(Resources.StopwachKey, stopwach); - - // //var modelState = context.ModelState.FirstOrDefault(f => f.Value.Errors.Any()); - // //string errorMsg = modelState.Value.Errors.First().ErrorMessage; - // //throw new AppException(errorMsg); - //} - - //public void OnActionExecuted(ActionExecutedContext context) - //{ - // //var logService = (IMongoLogService)context.HttpContext.RequestServices.GetService(typeof(IMongoLogService)); - // //var request = context.HttpContext.Request; - // //var model = new MongoApiLog - // //{ - // // Host = request.Host.Host, - // // Https = request.IsHttps, - // // Path = request.Path, - // // Port = request.Host.Port ?? 0, - // // Url = request.Host.Value + request.Path + (request.Method.Equals("get", StringComparison.OrdinalIgnoreCase) ? request.QueryString.ToString() : ""), - // // Mehod = request.Method, - // // Parameter = request.QueryString.ToString(), - // // SendTime = DateTime.Now, - // // Remark = "over:" + context.HttpContext.GetHashCode().ToString() - // //}; - // //logService.DirectSend(MongoLogQueue.ApiLogQueue, model); - - // //var httpContext = context.HttpContext; - // //var stopwach = httpContext.Items[Resources.StopwachKey] as Stopwatch; - // //stopwach.Stop(); - // //var time = stopwach.Elapsed; - - // //if (time.TotalSeconds > 5) - // //{ - // // var factory = context.HttpContext.RequestServices.GetService(); - // // var logger = factory.CreateLogger(); - // // logger.LogWarning($"{context.ActionDescriptor.DisplayName}执行耗时:{time.ToString()}"); - // //} - // //上面的代码利用使用HttpContext传递一个Stopwach来计算action的执行时间,并在超过5秒时输出警告日志。 - - // // 注册方法与ExceptionFinter相同。找到系统根目录Startup.cs文件,修改ConfigureServices方法如下 - - // //services.AddMvc(options => - // //{ - // // options.Filters.Add(); - // //}); - //} } } diff --git a/src/Shop.WebApi/Filters/CustomExceptionFilterAttribute.cs b/src/Shop.WebApi/Filters/CustomExceptionFilterAttribute.cs index 4156ce0f..f8aed784 100644 --- a/src/Shop.WebApi/Filters/CustomExceptionFilterAttribute.cs +++ b/src/Shop.WebApi/Filters/CustomExceptionFilterAttribute.cs @@ -17,44 +17,6 @@ public override void OnException(ExceptionContext context) // context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; } base.OnException(context); - - //var logService = (IMongoLogService)context.HttpContext.RequestServices.GetService(typeof(IMongoLogService)); - //var request = context.HttpContext.Request; - //var model = new MongoExceptionLog - //{ - // Host = request.Host.Host, - // Parameter = request.QueryString.ToString(), - // SendTime = DateTime.Now, - // Exception = JsonConvert.SerializeObject(new - // { - // context.Exception.Message, - // context.Exception.Source, - // context.Exception.StackTrace - // }), - // Remark = "error:" + context.HttpContext.GetHashCode().ToString() - //}; - //logService.DirectSend(MongoLogQueue.ExceptionLogQueue, model); - - //var json = new JsonErrorResponse(); - ////这里面是自定义的操作记录日志 - //if (context.Exception.GetType() == typeof(UserOperationException)) - //{ - // json.Message = context.Exception.Message; - // if (_env.IsDevelopment()) - // { - // json.DevelopmentMessage = context.Exception.StackTrace;//堆栈信息 - // } - // context.Result = new BadRequestObjectResult(json);//返回异常数据 - //} - //else - //{ - // json.Message = "发生了未知内部错误"; - // if (_env.IsDevelopment()) - // { - // json.DevelopmentMessage = context.Exception.StackTrace;//堆栈信息 - // } - // context.Result = new InternalServerErrorObjectResult(json); - //} } } } diff --git a/src/Shop.WebApi/Handlers/PermissionHandler.cs b/src/Shop.WebApi/Handlers/PermissionHandler.cs index 1f32535b..b217bb1e 100644 --- a/src/Shop.WebApi/Handlers/PermissionHandler.cs +++ b/src/Shop.WebApi/Handlers/PermissionHandler.cs @@ -1,10 +1,8 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Filters; -using Shop.Infrastructure.Helpers; -using Shop.Module.Core.Abstractions.Services; -using System; +using Microsoft.AspNetCore.Routing; +using Shop.Module.Core.Abstractions.Extensions; using System.Security.Claims; using System.Threading.Tasks; @@ -12,10 +10,12 @@ namespace Shop.WebApi.Handlers { public class PermissionHandler : IAuthorizationHandler { - private readonly ITokenService _tokenService; - public PermissionHandler(ITokenService tokenService) + private readonly IWorkContext _workContext; + private readonly IHttpContextAccessor _httpContextAccessor; + public PermissionHandler(IWorkContext workContext, IHttpContextAccessor httpContextAccessor) { - _tokenService = tokenService; + _workContext = workContext; + _httpContextAccessor = httpContextAccessor; } public async Task HandleAsync(AuthorizationHandlerContext context) @@ -23,133 +23,57 @@ public async Task HandleAsync(AuthorizationHandlerContext context) var isAuthenticated = context.User?.Identity?.IsAuthenticated; if (isAuthenticated == true) { - // JWT 验证通过 - // 自定义验证规则、通过缓存处理JWT只能存在一个有效的 // 注意:令牌失效,需返回401。默认返回403 - if (context.Resource is AuthorizationFilterContext) + //if (context.Resource is AuthorizationFilterContext) + //{ + // var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext; + //} + + var httpContext = _httpContextAccessor?.HttpContext; + if (httpContext == null) + { + context.Fail(); + return; + } + + // https://stackoverflow.com/questions/51119926/jwt-authentication-usermanager-getuserasync-returns-null + // default the value of UserIdClaimType is ClaimTypes.NameIdentifier + var identityId = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); + string token = httpContext.Request.Headers["Authorization"]; + if (string.IsNullOrWhiteSpace(token)) + { + token = httpContext.Request.Query["access_token"]; + } + + if (string.IsNullOrWhiteSpace(identityId) || string.IsNullOrWhiteSpace(token) || !int.TryParse(identityId, out int userId) || userId <= 0) + { + httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; + } + else { - var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext; - if (httpContext == null) + string path; + if (httpContext.GetEndpoint() is RouteEndpoint endpoint && endpoint != null) { - context.Fail(); - return; + path = endpoint.RoutePattern.RawText; + } + else + { + path = httpContext.Request?.Path.Value; } - // https://stackoverflow.com/questions/51119926/jwt-authentication-usermanager-getuserasync-returns-null - // default the value of UserIdClaimType is ClaimTypes.NameIdentifier - var identityId = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); - string token = httpContext.Request.Headers["Authorization"]; - if (string.IsNullOrWhiteSpace(token)) - token = httpContext.Request.Query["access_token"]; - - // TODO 暂时放开控制,允许一个账号多处登录 + path = $"{httpContext.Request.Method}:/{path?.Trim().Trim('/')}"; - // 访问期间令牌自动续签(一次性令牌除外) - //if (string.IsNullOrWhiteSpace(identityId) || - // string.IsNullOrWhiteSpace(token) || - // !_tokenService.ValidateToken(identityId, token.TrimStart(JwtBearerDefaults.AuthenticationScheme).Trim())) - //{ - // httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; - //} + // 访问期间验证令牌并自动续签 + if (!_workContext.ValidateToken(userId, token.Substring($"{JwtBearerDefaults.AuthenticationScheme} ".Length).Trim(), out int statusCode, path)) + { + if (statusCode != StatusCodes.Status403Forbidden) + { + httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; + } + } } } await Task.CompletedTask; - - //var isAuthenticated = context.User?.Identity?.IsAuthenticated; - //if (isAuthenticated == true) - //{ - // // JWT 验证通过 - // // 自定义验证规则、通过缓存处理JWT只能存在一个有效的 - // // 注意:令牌失效,需返回401。默认返回403 - // if (context.Resource is AuthorizationFilterContext) - // { - // var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext; - // if (httpContext == null) - // { - // context.Fail(); - // return; - // } - - // var identityId = context.User.FindFirst(JwtRegisteredClaimNames.NameId)?.Value; - // if (string.IsNullOrWhiteSpace(identityId)) - // { - // //context.Fail(); - // //BUG,待修改 注意这里需要返回401,且不再继续执行,如果直接返回401并不会阻止方法调用 - // context.Fail(); - // httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; - // return; - // } - - // string token = httpContext.Request.Headers["Authorization"]; - // if (string.IsNullOrWhiteSpace(token)) - // token = httpContext.Request.Query["access_token"]; - // if (string.IsNullOrWhiteSpace(token)) - // { - // //context.Fail(); - // context.Fail(); - // httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; - // return; - // } - - // // 访问期间令牌自动续签(一次性令牌除外) - // if (!_tokenService.ValidateToken(identityId, token.TrimStart(JwtBearerDefaults.AuthenticationScheme).Trim())) - // { - // //context.Fail(); - // context.Fail(); - // httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; - // return; - // } - - // //var bearerAuthResult = await httpContext?.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); - // //var principal = new ClaimsPrincipal(); - // //if (bearerAuthResult?.Principal != null) - // //{ - // // principal.AddIdentities(bearerAuthResult.Principal.Identities); - // //} - - // //// 判断请求是否停止 - // //var handlerProvider = httpContext.RequestServices.GetRequiredService(); - // //var schemeProvider = httpContext.RequestServices.GetRequiredService(); - // //foreach (var scheme in await schemeProvider.GetRequestHandlerSchemesAsync()) - // //{ - // // var handler = await handlerProvider.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler; - // // if (handler != null && await handler.HandleRequestAsync()) - // // { - // // context.Fail(); - // // } - // //} - - // //// 请求Url - // //// var questUrl = httpContext.Request.Path.Value.ToLower(); - // //var defaultAuthenticate = await schemeProvider.GetDefaultAuthenticateSchemeAsync(); - // //if (defaultAuthenticate != null) - // //{ - // // var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name); - // // // result?.Principal不为空即登录成功 - // // if (result?.Principal != null) - // // { - // // httpContext.User = result.Principal; - - // // // 权限中是否存在请求的url - // // if (Requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0) - // // { - // // var name = httpContext.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType).Value; - // // //验证权限 - // // if (Requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() <= 0) - // // { - // // //无权限跳转到拒绝页面 - // // httpContext.Response.Redirect(requirement.DeniedAction); - // // } - // // } - // // } - // //} - - // //var pendingRequirements = context.PendingRequirements.ToList(); - // //foreach (var requirement in pendingRequirements) - // //{ - // //} - // } - //} } } } diff --git a/src/Shop.WebApi/Properties/PublishProfiles/FolderProfile.pubxml b/src/Shop.WebApi/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 3bd94109..00000000 --- a/src/Shop.WebApi/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - FileSystem - FileSystem - Release - Any CPU - - True - False - netcoreapp2.2 - 2059630e-5796-4a0c-9804-045a086fdc30 - false - <_IsPortable>true - bin\Debug\netcoreapp2.2\publish\ - False - - \ No newline at end of file diff --git a/src/Shop.WebApi/Shop.WebApi.csproj b/src/Shop.WebApi/Shop.WebApi.csproj index 03399d9c..19ce0038 100644 --- a/src/Shop.WebApi/Shop.WebApi.csproj +++ b/src/Shop.WebApi/Shop.WebApi.csproj @@ -1,64 +1,69 @@  - - netcoreapp2.2 - InProcess - Linux - 5651159f-07fa-4b32-a299-0b19259bdb6f - ..\..\docker-compose.dcproj - + + netcoreapp3.1 + InProcess + Linux + 5651159f-07fa-4b32-a299-0b19259bdb6f + ..\..\docker-compose.dcproj + ..\.. + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Shop.WebApi/Startup.cs b/src/Shop.WebApi/Startup.cs index f48af5eb..19a31909 100644 --- a/src/Shop.WebApi/Startup.cs +++ b/src/Shop.WebApi/Startup.cs @@ -2,16 +2,17 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Shop.WebApi.Extensions; namespace Shop.WebApi { public class Startup { - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IWebHostEnvironment _hostingEnvironment; private readonly IConfiguration _configuration; - public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment) + public Startup(IConfiguration configuration, IWebHostEnvironment hostingEnvironment) { _configuration = configuration; _hostingEnvironment = hostingEnvironment; @@ -22,15 +23,23 @@ public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironm // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddControllers(); + services.AddCustomizedConfigureServices(_configuration, _hostingEnvironment); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); + + app.UseSwagger(); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "Shop Api"); + }); } else { @@ -41,7 +50,28 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) app.UseCustomizedConfigure(env); //app.UseHttpsRedirection(); - app.UseMvc(); + + //app.UseRouting(); + + app.UseRouting(); + + // https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1 + // 指定AllowAnyOrigin和AllowCredentials是不安全的配置,可能会导致跨站点请求伪造。当使用两种方法配置应用程序时,CORS服务将返回无效的CORS响应。 + app.UseCors(option => + { + option.AllowAnyHeader(); + option.AllowAnyMethod(); + option.AllowAnyOrigin(); + //option.AllowCredentials(); + }); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); } } } diff --git a/src/Shop.WebApi/appsettings.json b/src/Shop.WebApi/appsettings.json index a555ff32..11c17638 100644 --- a/src/Shop.WebApi/appsettings.json +++ b/src/Shop.WebApi/appsettings.json @@ -12,7 +12,7 @@ "Logging": { "IncludeScopes": false, "LogLevel": { - "Default": "Information" + "Default": "Information" // Debug Information Warning Error } }, "Serilog": { diff --git a/src/Shop.WebApi/modules.json b/src/Shop.WebApi/modules.json index b537297c..f4473014 100644 --- a/src/Shop.WebApi/modules.json +++ b/src/Shop.WebApi/modules.json @@ -106,19 +106,10 @@ }, /* mq */ - { - "id": "Shop.Module.MQ", - "version": "1.0.0" - }, { "id": "Shop.Module.MQ.Abstractions", "version": "1.0.0" }, - // only one - //{ - // "id": "Shop.Module.RabbitMQ", - // "version": "1.0.0" - //}, { "id": "Shop.Module.MassTransitMQ", "version": "1.0.0" diff --git a/test/Shop.Module.Core.Tests/Shop.Module.Core.Tests.csproj b/test/Shop.Module.Core.Tests/Shop.Module.Core.Tests.csproj index 06429a12..4a3b8ec3 100644 --- a/test/Shop.Module.Core.Tests/Shop.Module.Core.Tests.csproj +++ b/test/Shop.Module.Core.Tests/Shop.Module.Core.Tests.csproj @@ -1,16 +1,19 @@ - netcoreapp2.2 + netcoreapp3.1 false - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive +