From be6fa211be0cdd0f0bf9b3f8c49d4c455f610cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=81=B0=E7=81=B0?= Date: Fri, 12 Aug 2022 01:57:18 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=94=AE=E5=90=8E=E5=AE=A1=E6=A0=B8=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E7=A7=AF=E5=88=86=E8=BF=94=E8=BF=98=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E7=9A=84=E9=98=9F=E5=88=97=E5=AE=9E=E7=8E=B0=EF=BC=8C?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=8E=9F=E6=9C=89=E7=A7=AF=E5=88=86=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E8=BF=94=E8=BF=98=E6=9C=BA=E5=88=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CoreCms.Net.Configuration/GlobalConstVars.cs | 5 + .../Config/RedisMessageQueueSetup.cs | 2 +- .../AfterSalesReviewForPointSubscribe.cs | 153 ++++++++++++++++++ .../Bill/CoreCmsBillAftersalesServices.cs | 29 ++-- CoreCms.Net.Web.Admin/appsettings.json | 2 +- .../wwwroot/lib/layuiAdmin/config.js | 2 +- CoreCms.Net.Web.WebApi/appsettings.json | 2 +- 7 files changed, 182 insertions(+), 13 deletions(-) create mode 100644 CoreCms.Net.RedisMQ/Subscribe/AfterSalesReviewForPointSubscribe.cs diff --git a/CoreCms.Net.Configuration/GlobalConstVars.cs b/CoreCms.Net.Configuration/GlobalConstVars.cs index 3a6f06f1..9eb6597b 100644 --- a/CoreCms.Net.Configuration/GlobalConstVars.cs +++ b/CoreCms.Net.Configuration/GlobalConstVars.cs @@ -380,6 +380,11 @@ namespace CoreCms.Net.Configuration /// public const string AfterSalesReview = "AfterSalesReview"; + /// + /// 售后审核通过后积分处理 + /// + public const string AfterSalesReviewForPoint = "AfterSalesReviewForPoint"; + /// /// 日志队列 diff --git a/CoreCms.Net.Core/Config/RedisMessageQueueSetup.cs b/CoreCms.Net.Core/Config/RedisMessageQueueSetup.cs index 8120ffdf..43a52fc9 100644 --- a/CoreCms.Net.Core/Config/RedisMessageQueueSetup.cs +++ b/CoreCms.Net.Core/Config/RedisMessageQueueSetup.cs @@ -32,7 +32,6 @@ namespace CoreCms.Net.Core.Config m.ConnectionString = AppSettingsConstVars.RedisConfigConnectionString; //对应的订阅者类,需要new一个实例对象,当然你也可以传参,比如日志对象 m.ListSubscribe = new List() { - typeof(DemoSubscribe), typeof(OrderAgentOrDistributionSubscribe), typeof(OrderAutomaticDeliverySubscribe), typeof(OrderFinishCommandSubscribe), @@ -45,6 +44,7 @@ namespace CoreCms.Net.Core.Config typeof(WeChatPayNoticeSubscribe), typeof(SendWxTemplateMessageSubscribe), typeof(AfterSalesReviewSubscribe), + typeof(AfterSalesReviewForPointSubscribe), }; //显示日志 m.ShowLog = false; diff --git a/CoreCms.Net.RedisMQ/Subscribe/AfterSalesReviewForPointSubscribe.cs b/CoreCms.Net.RedisMQ/Subscribe/AfterSalesReviewForPointSubscribe.cs new file mode 100644 index 00000000..2b058d1d --- /dev/null +++ b/CoreCms.Net.RedisMQ/Subscribe/AfterSalesReviewForPointSubscribe.cs @@ -0,0 +1,153 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using CoreCms.Net.Configuration; +using CoreCms.Net.IServices; +using CoreCms.Net.Loging; +using CoreCms.Net.Model.Entities; +using CoreCms.Net.Utility.Extensions; +using CoreCms.Net.Utility.Helper; +using InitQ.Abstractions; +using InitQ.Attributes; +using Microsoft.Extensions.DependencyInjection; +using SqlSugar; + +namespace CoreCms.Net.RedisMQ.Subscribe +{ + /// + /// 售后审核通过后对积分的返还处理 + /// + public class AfterSalesReviewForPointSubscribe : IRedisSubscribe + { + private readonly IServiceProvider _serviceProvider; + + public AfterSalesReviewForPointSubscribe(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + /// + /// 售后审核通过后对积分的返还处理 + /// + /// + /// + [Subscribe(RedisMessageQueueKey.AfterSalesReviewForPoint)] + + private async Task AfterSalesReviewForPointSubscribeCommand(string msg) + { + try + { + if (string.IsNullOrEmpty(msg)) + { + NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.RedisMessageQueue, "售后审核通过后积分处理", "审核单编号获取失败"); + return; + } + //使用服务驱动器取服务,防止autofac出现循环注入 + using var container = _serviceProvider.CreateScope(); + var _aftersalesServices = container.ServiceProvider.GetService(); + var _orderServices = container.ServiceProvider.GetService(); + var _aftersalesItemServices = container.ServiceProvider.GetService(); + var _productsServices = container.ServiceProvider.GetService(); + var _settingServices = container.ServiceProvider.GetService(); + var _userServices = container.ServiceProvider.GetService(); + var _userPointLogServices = container.ServiceProvider.GetService(); + + var info = await _aftersalesServices.QueryByClauseAsync(p => p.aftersalesId == msg, true); + if (info != null) + { + var order = await _orderServices.QueryByClauseAsync(p => p.orderId == info.orderId, true); + if (order == null) + { + NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.RedisMessageQueue, "售后审核通过后积分处理", "订单数据获取失败"); + return; + } + if (order.point == 0) + { + NLogUtil.WriteAll(NLog.LogLevel.Info, LogType.RedisMessageQueue, "售后审核通过后积分处理", "未使用积分无需处理"); + return; + } + var userModel = await _userServices.QueryByClauseAsync(p => p.id == order.userId, true); + if (userModel == null) + { + NLogUtil.WriteAll(NLog.LogLevel.Info, LogType.RedisMessageQueue, "售后审核通过后积分处理", "用户信息获取失败"); + return; + } + + //获取售后明细 + var aftersalesItems = await _aftersalesItemServices.QueryListByClauseAsync(p => p.aftersalesId == info.aftersalesId, p => p.id, OrderByType.Asc, true); + if (aftersalesItems == null) + { + NLogUtil.WriteAll(NLog.LogLevel.Info, LogType.RedisMessageQueue, "售后审核通过后积分处理", "售后明细获取失败"); + return; + } + //获取售后货品信息 + var productIds = aftersalesItems.Select(p => p.productId).ToList(); + var products = await _productsServices.QueryListByClauseAsync(p => productIds.Contains(p.id), p => p.id, OrderByType.Asc, true); + + var allConfigs = await _settingServices.GetConfigDictionaries(); + var pointExchangeModel = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.PointExchangeModel).ObjectToInt(); //积分模式 1全局2单品 + var pointDiscountedProportion = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.PointDiscountedProportion).ObjectToInt(); //积分折现比例 多少积分等于1元钱。 + //var ordersPointProportion = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.OrdersPointProportion).ObjectToInt(); //积分使用比例 全局模式下一个订单最多可以使用多少比例的积分 + decimal point = 0; + foreach (var p in aftersalesItems) + { + var aftersalesProduct = products.Find(x => x.id == p.productId); + if (aftersalesProduct == null) + { + continue; + }; + //如果是全局模式(根据比例来退还积分) + if (pointExchangeModel == 1) + { + //可能存在就是根本不是全积分抵扣,而是订单实际在不够积分的情况下,抵扣了多少金额。那么统一就根据订单的比例来计算,更加精准,(这里的总金额是实际支付金额,去掉了优惠) + var practicalProportion = Math.Round(order.pointMoney / order.orderAmount, 4); + + //获取货品金额*积分使用比例*数量*积分折现比例=积分抵扣的金额应该可以兑换的积分。 + point += aftersalesProduct.price * practicalProportion * p.nums * pointDiscountedProportion; + } + //如果是单品模式 + else if (pointDiscountedProportion == 2) + { + //单品模式只能是全积分抵扣或者全金额支付。所以直接按照扣掉的金额还原积分即可。 + point += aftersalesProduct.pointsDeduction * p.nums * pointDiscountedProportion; + } + } + + //为了计算比例情况下,增加精度,全局模式下用了4位小数。这里四色五入积分差异控制小点。 + var practicalPoint = Convert.ToInt32(point); + + var newPoint = practicalPoint + userModel.point; + + var pointLog = new CoreCmsUserPointLog(); + pointLog.userId = userModel.id; + pointLog.type = (int)GlobalEnumVars.UserPointSourceTypes.PointRefundReturn; + pointLog.num = practicalPoint; + pointLog.balance = newPoint; + pointLog.remarks = "售后单:" + info.aftersalesId + "退还积分"; + pointLog.createTime = DateTime.Now; + + var id = await _userPointLogServices.InsertAsync(pointLog); + if (id > 0) + { + await _userServices.UpdateAsync(p => new CoreCmsUser() { point = p.point + practicalPoint }, p => p.id == userModel.id); + } + NLogUtil.WriteAll(NLog.LogLevel.Info, LogType.RedisMessageQueue, "售后审核通过后积分处理", msg); + } + else + { + NLogUtil.WriteAll(NLog.LogLevel.Info, LogType.RedisMessageQueue, "售后审核通过后积分处理", "售后单查询失败"); + } + } + catch (Exception ex) + { + NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.RedisMessageQueue, "售后审核通过后积分处理", msg, ex); + throw; + } + await Task.CompletedTask; + } + + + + + } +} diff --git a/CoreCms.Net.Services/Bill/CoreCmsBillAftersalesServices.cs b/CoreCms.Net.Services/Bill/CoreCmsBillAftersalesServices.cs index 41852351..91616240 100644 --- a/CoreCms.Net.Services/Bill/CoreCmsBillAftersalesServices.cs +++ b/CoreCms.Net.Services/Bill/CoreCmsBillAftersalesServices.cs @@ -144,6 +144,7 @@ namespace CoreCms.Net.Services #endregion #region 统计用户的售后数量============================================ + /// /// 统计用户的售后数量 /// @@ -376,10 +377,19 @@ namespace CoreCms.Net.Services { if (type == (int)GlobalEnumVars.BillAftersalesIsReceive.Refund) { - var n = orderItem.nums - orderItem.sendNums - (orderItem.reshipNums - orderItem.reshipedNums); - if (n < nums) + //售后中未退换的数量(申请售后退还的,已经收到退还的,差额就是客户还需要退还的) + var reshipNum = orderItem.reshipNums - orderItem.reshipedNums; + + //如果未发货,但申请售后大于0 + if (orderItem.sendNums == 0 && nums > 0) { - jm.msg = orderItem.name + orderItem.addon + ",未发货商品,最多能退" + n + "个"; + jm.msg = orderItem.name + orderItem.addon + ",未发货商品,退货数量只能为0个"; + return jm; + } + //如果当前售后数量+未寄送的售后数量 > 已经发货数量 + if (reshipNum + nums > orderItem.sendNums) + { + jm.msg = orderItem.name + orderItem.addon + ",售后申请超过数量,最多能退" + (orderItem.sendNums - reshipNum - nums) + "个"; return jm; } } @@ -625,17 +635,16 @@ namespace CoreCms.Net.Services orderInfo.payStatus = (int)GlobalEnumVars.OrderPayStatus.Refunded; orderInfo.status = (int)GlobalEnumVars.OrderStatus.Complete; - //返还积分 - if (orderInfo.point > 0) - { - await _userPointLogServices.SetPoint(orderInfo.userId, orderInfo.point, (int)GlobalEnumVars.UserPointSourceTypes.PointRefundReturn, "售后退款:" + orderInfo.orderId + "返还积分"); - } + //返还积分(此模式会导致多次售后刷积分情况) + //if (orderInfo.point > 0) + //{ + // await _userPointLogServices.SetPoint(orderInfo.userId, orderInfo.point, (int)GlobalEnumVars.UserPointSourceTypes.PointRefundReturn, "售后退款:" + orderInfo.orderId + "返还积分"); + //} //返还优惠券 if (!string.IsNullOrEmpty(orderInfo.coupon)) { await couponServices.CancelReturnCoupon(orderInfo.coupon); } - } else { @@ -731,6 +740,8 @@ namespace CoreCms.Net.Services { //售后审核通过后处理 await _redisOperationRepository.ListLeftPushAsync(RedisMessageQueueKey.AfterSalesReview, aftersalesId); + //售后审核通过后积分退还机制 + await _redisOperationRepository.ListLeftPushAsync(RedisMessageQueueKey.AfterSalesReviewForPoint, aftersalesId); } } diff --git a/CoreCms.Net.Web.Admin/appsettings.json b/CoreCms.Net.Web.Admin/appsettings.json index a5653bea..ec46b86f 100644 --- a/CoreCms.Net.Web.Admin/appsettings.json +++ b/CoreCms.Net.Web.Admin/appsettings.json @@ -16,7 +16,7 @@ "AppConfig": { "AppUrl": "https://admin.demo.coreshop.cn/", //后端管理地址 "AppInterFaceUrl": "https://api.demo.coreshop.cn/", //接口请求地址 - "AppVersion": "CoreShopProfessional v0.5.4" + "AppVersion": "CoreShopProfessional v0.5.5" }, //redis为必须启动项,请保持redis为正常可用 "RedisConfig": { diff --git a/CoreCms.Net.Web.Admin/wwwroot/lib/layuiAdmin/config.js b/CoreCms.Net.Web.Admin/wwwroot/lib/layuiAdmin/config.js index c4b1056e..c6da0b94 100644 --- a/CoreCms.Net.Web.Admin/wwwroot/lib/layuiAdmin/config.js +++ b/CoreCms.Net.Web.Admin/wwwroot/lib/layuiAdmin/config.js @@ -12,7 +12,7 @@ layui.define(['laytpl', 'layer', 'element', 'util'], function (exports) { , pageTabs: false //是否开启页面选项卡功能。单页版不推荐开启 , name: '核心商城系统' - , version: 'CoreShopProfessional v0.5.4' + , version: 'CoreShopProfessional v0.5.5' , tableName: 'CoreCms' //本地存储表名 , MOD_NAME: 'admin' //模块事件名 diff --git a/CoreCms.Net.Web.WebApi/appsettings.json b/CoreCms.Net.Web.WebApi/appsettings.json index c0a96971..cdeed0a5 100644 --- a/CoreCms.Net.Web.WebApi/appsettings.json +++ b/CoreCms.Net.Web.WebApi/appsettings.json @@ -16,7 +16,7 @@ "AppConfig": { "AppUrl": "https://admin.demo.coreshop.cn/", //后端管理地址 "AppInterFaceUrl": "https://api.demo.coreshop.cn/", //接口请求地址 - "AppVersion": "CoreShopProfessional v0.5.4" + "AppVersion": "CoreShopProfessional v0.5.5" }, //redis为必须启动项,请保持redis为正常可用 "RedisConfig": {