using Furion; using Furion.DataEncryption; using Furion.DependencyInjection; using Furion.DynamicApiController; using Furion.FriendlyException; using Furion.UnifyResult; using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Myshipping.Application.Entity; using Myshipping.Application.Service.ExpressDelivery.Dto; using Myshipping.Application.Service.ExpressDelivery.Dto.SF; using Myshipping.Core; using Myshipping.Core.Attributes; using Myshipping.Core.Service; using Newtonsoft.Json; using SqlSugar; using StackExchange.Profiling.Internal; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Threading.Tasks; namespace Myshipping.Application { /// /// 快递订单服务 /// [ApiDescriptionSettings("Application", Name = "ExpressDelivery", Description = "快递订单服务", Order = 20)] [Route("/ExpressDelivery")] public class ExpressDeliveryService : IExpressDeliveryService, IDynamicApiController, ITransient { private const string STATUS_ASSOCIATION = "关联快递订单"; private const string STATUS_DISASSOCIATION = "取消关联快递订单"; private const string STATUS_DELETE = "关联快递订单已删除"; private const string STATUS_SEND = "快递已下单"; private const string STATUS_CANCEL = "快递已消单"; /// /// 保存面单文件的文件夹名称 /// private const string WAY_BILL_DIRECTORY = "WaybillFile"; private readonly ILogger _logger; private readonly ISysCacheService _cache; private readonly SqlSugarRepository _bookingStatuslogRep; private readonly SqlSugarRepository _bookingOrderRep; private readonly SqlSugarRepository _orderRep; private readonly SqlSugarRepository _businessRep; private readonly SqlSugarRepository _statusRep; private readonly SqlSugarRepository _addressRep; private readonly SqlSugarRepository _feeRep; private readonly INamedServiceProvider _deliverySendServiceProvider; public ExpressDeliveryService(ILogger logger, ISysCacheService cache, SqlSugarRepository bookingStatuslogRep, SqlSugarRepository bookingOrderRep, SqlSugarRepository orderRep, SqlSugarRepository businessRep, SqlSugarRepository statusRep, SqlSugarRepository templeteRep, INamedServiceProvider deliverySendServiceProvider, SqlSugarRepository feeRep) { _logger = logger; _cache = cache; _orderRep = orderRep; _businessRep = businessRep; _statusRep = statusRep; _bookingStatuslogRep = bookingStatuslogRep; _bookingOrderRep = bookingOrderRep; _addressRep = templeteRep; _deliverySendServiceProvider = deliverySendServiceProvider; _feeRep = feeRep; } /// /// 分页查询快递主表 /// /// /// [HttpGet("Page")] public async Task> Page([FromQuery] ExpressDeliveryInput input) { var entities = await _orderRep.AsQueryable().Filter(null, true) .Where(x => x.TenantId == UserManager.TENANT_ID && x.IsDeleted == false) .WhereIF(!string.IsNullOrWhiteSpace(input.KDNO), u => u.KDNO.Contains(input.KDNO)) .WhereIF(!string.IsNullOrWhiteSpace(input.SJCompany), u => u.SJCompany.Contains(input.SJCompany)) .WhereIF(!string.IsNullOrWhiteSpace(input.SJPeople), u => u.SJPeople.Contains(input.SJPeople)) .WhereIF(!string.IsNullOrWhiteSpace(input.SJTel), u => u.SJTel.Contains(input.SJTel)) .WhereIF(!string.IsNullOrWhiteSpace(input.FJCompany), u => u.FJCompany.Contains(input.FJCompany)) .WhereIF(!string.IsNullOrWhiteSpace(input.FJPeople), u => u.FJPeople.Contains(input.FJPeople)) .WhereIF(!string.IsNullOrWhiteSpace(input.FJTel), u => u.FJTel.Contains(input.FJTel)) .WhereIF(!string.IsNullOrWhiteSpace(input.VESSEL), u => u.VESSEL.Contains(input.VESSEL)) .WhereIF(!string.IsNullOrWhiteSpace(input.VOYNO), u => u.VOYNO.Contains(input.VOYNO)) .WhereIF(input.BDate != null, u => u.Date >= input.BDate) .WhereIF(input.EDate != null, u => u.Date <= input.EDate) .OrderBy(PageInputOrder.OrderBuilder(input.SortField, input.DescSort)) .ToPagedListAsync(input.PageNo, input.PageSize); var list = entities.Adapt>(); foreach (var item in list.Items) { item.Business = await _businessRep.AsQueryable().Filter(null, true).Where(x => x.PId == item.Id).ToListAsync(); } return list; } /// /// 获取详情 /// /// /// [HttpPost("Get")] public async Task Get(long Id) { ExpressDeliveryDto ordOut = new ExpressDeliveryDto(); var main = await _orderRep.FirstOrDefaultAsync(u => u.Id == Id); if (main != null) { ordOut = main.Adapt(); var business = await _businessRep.AsQueryable().Where(x => x.PId == Id).ToListAsync(); ordOut.Business = business; var feeList = await _feeRep.AsQueryable().Where(x => x.OrderId == Id).ToListAsync(); ordOut.FeeList = feeList; } return ordOut; } /// /// 保存并返回数据 /// /// [HttpPost("Save")] [JoinValidateMessge] public async Task Save(ExpressDeliveryDto input) { var entity = input.Adapt(); string logGuid = Guid.NewGuid().ToString(); _logger.LogInformation($"快递订单保存操作开始,{logGuid}"); if (input.Id == 0) { entity.CurrentStateCode = "DJY02"; entity.CurrentStateDesc = "已保存"; _logger.LogInformation($"快递订单新增操作开始,{logGuid}"); await _orderRep.InsertAsync(entity); if (input.Business != null && input.Business.Count > 0) { #region 保存关联的业务信息 _logger.LogInformation($"快递订单新增关联的业务信息,{logGuid}"); input.Business.ForEach(b => b.PId = entity.Id); await _businessRep.InsertAsync(input.Business); #endregion #region 向与快递关联的订舱列表中添加动态 List mblNoList = input.Business.Select(b => b.MBLNO).ToList(); await InsertNewBookingStatusWithMblnoList(mblNoList, STATUS_ASSOCIATION); #endregion } // 添加快递动态 _logger.LogInformation($"快递订单新增快递动态,{logGuid}"); var status = new ExpressDeliveryStatus() { OrderId = entity.Id, MailNo = "", StatusCode = entity.CurrentStateCode, StatusDesc = entity.CurrentStateDesc }; await _statusRep.InsertAsync(status); } else { var oldOrder = _orderRep.FirstOrDefault(x => x.Id == input.Id); if (oldOrder.CurrentStateCode != "DJY02") { throw Oops.Bah("修改失败,原因:下单过的快递单无法修改信息"); } _logger.LogInformation($"快递订单修改操作开始,{logGuid}"); await _orderRep.AsUpdateable(entity).IgnoreColumns(it => new { it.CurrentStateCode, it.CurrentStateDesc, it.TenantId, it.CreatedTime, it.CreatedUserId, it.CreatedUserName, }).ExecuteCommandAsync(); #region 向与快递关联的订舱列表中添加动态,并新增或更新业务信息 List businessList = await _businessRep.AsQueryable().Where(x => x.PId == input.Id).ToListAsync(); if (businessList.Count == 0) { if (input.Business?.Count != 0) { // 直接得到:要关联快递的提单号列表 List mblNoList = input.Business.Select(b => b.MBLNO).ToList(); await InsertNewBookingStatusWithMblnoList(mblNoList, STATUS_ASSOCIATION); // 直接得到:要新增的业务信息 input.Business.ForEach(b => b.PId = entity.Id); await _businessRep.InsertAsync(input.Business); } } else { if (input.Business?.Count == 0) { // 直接得到:要取消关联快递的提单号列表 List mblNoList = businessList.Select(b => b.MBLNO).ToList(); await InsertNewBookingStatusWithMblnoList(mblNoList, STATUS_DISASSOCIATION); // 直接得到:要删除的业务信息 await _businessRep.DeleteAsync(x => x.PId == entity.Id); } else { // 分析对比,判断哪些关联,哪些取消关联 List oldMblnoList = businessList.Select(b => b.MBLNO).ToList(); List newMblnoList = input.Business.Select(b => b.MBLNO).ToList(); // 新增关联的提单号列表: List addMblnoList = newMblnoList.Except(oldMblnoList).ToList(); await InsertNewBookingStatusWithMblnoList(addMblnoList, STATUS_ASSOCIATION); // 取消关联的提单号列表 List cancelMblnoList = oldMblnoList.Except(newMblnoList).ToList(); await InsertNewBookingStatusWithMblnoList(cancelMblnoList, STATUS_DISASSOCIATION); if (addMblnoList.Count != 0 || cancelMblnoList.Count != 0) { // 先删除,再新增业务信息 await _businessRep.DeleteAsync(x => x.PId == entity.Id); input.Business.ForEach(b => b.PId = entity.Id); await _businessRep.InsertAsync(input.Business); } } } #endregion if (input.IsSending) { await SendBooking(entity); } } return await Get(entity.Id); } /// /// 删除单据 /// /// /// [SqlSugarUnitOfWork] [HttpPost("Delete")] public async Task Delete(string Ids) { var arr = Ids.Split(","); if (arr.Length > 0) { foreach (var ar in arr) { long Id = Convert.ToInt64(ar); ExpressDeliveryOrder order = await _orderRep.AsQueryable(e => e.Id == Id).FirstAsync() ?? throw Oops.Bah("未找到快递订单"); // 添加快递动态 var status = new ExpressDeliveryStatus() { OrderId = Id, MailNo = order.KDNO, StatusCode = "DJY05", StatusDesc = "快递单已删除" }; await _statusRep.InsertAsync(status); // 更新快递订单表 await _orderRep.UpdateAsync(x => x.Id == Id, x => new ExpressDeliveryOrder { IsDeleted = true, CurrentStateCode = status.StatusCode, CurrentStateDesc = status.StatusDesc }); // 快递关联的订舱添加动态 List mblnoList = await _businessRep.AsQueryable(x => x.PId == Id).Select(b => b.MBLNO).ToListAsync(); await InsertNewBookingStatusWithMblnoList(mblnoList, STATUS_DELETE); // 删除快递关联的业务表数据 await _businessRep.DeleteAsync(x => x.PId == Id); } } else { throw Oops.Bah("请上传正确参数"); } } /// /// 发送快递 /// /// [HttpPost("SendBooking")] [NonAction] public async Task SendBooking(ExpressDeliveryOrder order) { if (order == null) { throw Oops.Bah("请选择正确数据!"); } if (order.CurrentStateCode == "DJY01") { throw Oops.Bah("下单失败,原因:尚未保存"); } if (order.CurrentStateCode != "DJY02") { throw Oops.Bah("下单失败,原因:重复下单"); } var Id = order.Id; IDeliverySend deliverySend = GetDeliverySend(order.KDCompany); string waybillNo = await deliverySend.CreateOrder(order); string statusDesc = $"{STATUS_SEND}(单号:{waybillNo})"; // 不需要等待的任务,并且不会因为异常导致当前请求终止 _ = Task.Run(async () => { #region 添加快递动态 try { var status = new ExpressDeliveryStatus() { OrderId = Id, MailNo = waybillNo, StatusCode = "DJY03", StatusDesc = statusDesc }; await _statusRep.InsertAsync(status); } catch (Exception ex) { _logger.LogError(ex, "调用快递接口下单后,添加快递动态的过程中发生异常"); } #endregion #region 为与快递关联的订舱添加动态 try { List mblnoList = await _businessRep.AsQueryable(b => b.PId == Id).Select(b => b.MBLNO).ToListAsync(); _ = InsertNewBookingStatusWithMblnoList(mblnoList, statusDesc); } catch (Exception ex) { _logger.LogError(ex, "调用快递接口下单后,为与快递关联的订舱添加动态的过程中发生异常"); } #endregion #region 维护地址簿 try { if (!await _addressRep.IsExistsAsync(a => a.People == order.SJPeople && a.Tel == order.SJTel)) { await _addressRep.InsertAsync(new ExpressDeliveryAddress() { Address = order.SJAddress, City = order.SJCity, Company = order.SJCompany, People = order.SJPeople, Province = order.SJProvince, ProvinceId = order.SJProvinceId, Tel = order.SJTel, Type = 1 }); } if (!await _addressRep.IsExistsAsync(a => a.People == order.FJPeople && a.Tel == order.FJTel)) { await _addressRep.InsertAsync(new ExpressDeliveryAddress() { Address = order.FJAddress, City = order.FJCity, Company = order.FJCompany, People = order.FJPeople, Province = order.FJProvince, ProvinceId = order.FJProvinceId, Tel = order.FJTel, Type = 2 }); } } catch (Exception ex) { _logger.LogError(ex, "调用快递接口下单后,维护地址簿的过程中发生异常"); } #endregion }); // 需要等待的任务 #region 下载面单PDF,保存到本地,不会因异常终止当前请求 string filePath = null; try { var dic = Path.Combine(App.WebHostEnvironment.WebRootPath, WAY_BILL_DIRECTORY); if (!Directory.Exists(dic)) { Directory.CreateDirectory(dic); } var fileName = $"{waybillNo}_{order.Id}.pdf"; filePath = Path.Combine(dic, fileName); await deliverySend.DownloadWaybillFile(waybillNo, filePath); } catch (Exception ex) { _logger.LogError(ex, "调用下单接口后,下载面单的过程中发生异常"); } #endregion #region 向快递订单表反写快递单号与状态 ExpressDeliveryOrder updateOrder = new ExpressDeliveryOrder() { Id = Id, KDNO = waybillNo, CurrentStateCode = "DJY03", CurrentStateDesc = statusDesc, WaybillFilePath = filePath }; await _orderRep.AsUpdateable(updateOrder) .UpdateColumns(o => new { o.KDNO, o.CurrentStateCode, o.CurrentStateDesc, o.WaybillFilePath }) .ExecuteCommandAsync(); #endregion UnifyContext.Fill(new { KDNO = waybillNo, currentStateCode = "DJY03", currentStateDesc = statusDesc }); return "下单成功!"; } /// /// 取消快递下单 /// /// /// [HttpPost("CancelBooking")] public async Task CancelBooking(long Id) { var order = _orderRep.FirstOrDefault(x => x.Id == Id) ?? throw Oops.Bah("请选择正确数据!"); if (order.CurrentStateCode is "DJY01" or "DJY02") { throw Oops.Bah("消单失败,原因:尚未下单,无需消单"); } if (order.CurrentStateCode is "DJY04") { throw Oops.Bah("消单失败,原因:已消单,无需再次消单"); } IDeliverySend deliverySend = GetDeliverySend(order.KDCompany); await deliverySend.CancelOrder(order.Id.ToString()); #region 添加快递动态 var status = new ExpressDeliveryStatus() { OrderId = Id, MailNo = order.KDNO, StatusCode = "DJY04", StatusDesc = STATUS_CANCEL }; await _statusRep.InsertAsync(status); #endregion #region 为与快递关联的订舱添加动态 List mblnoList = await _businessRep.AsQueryable(b => b.PId == Id).Select(b => b.MBLNO).ToListAsync(); await InsertNewBookingStatusWithMblnoList(mblnoList, STATUS_CANCEL); #endregion #region 更新快递订单状态 ExpressDeliveryOrder updateOrder = new ExpressDeliveryOrder() { Id = Id, CurrentStateCode = status.StatusCode, CurrentStateDesc = status.StatusDesc, }; await _orderRep.AsUpdateable(updateOrder) .UpdateColumns(o => new { o.CurrentStateCode, o.CurrentStateDesc }) .ExecuteCommandAsync(); #endregion return "取消成功!"; } /// /// 打印面单(根据快递订单Id读取相应的面单文件并返回) /// /// Jwt令牌 /// 快递订单Id /// [HttpGet("PrintWaybill")] [NonUnify] [AllowAnonymous] public async Task PrintWaybill([FromQuery][Required] string authorization, [FromQuery][Required] long orderId) { var validateResult = JWTEncryption.Validate(authorization); if (!validateResult.IsValid) { return new BadPageResult(403) { Title = "接口鉴权未通过", Description = null, }; //return new UnauthorizedObjectResult("接口鉴权未通过"); } var order = await _orderRep.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == orderId); if (order == null) { return new BadPageResult(400) { Title = "请选择正确快递单", Description = null, }; } if (order.CurrentStateCode is "DJY01" or "DJY02") { return new BadPageResult(400) { Title = "请先保存并下单后,再打印面单", Description = null, }; } if (string.IsNullOrEmpty(order.KDNO)) { return new BadPageResult(500) { Title = "未知异常:快递单号为空", Description = null, }; } try { var dic = Path.Combine(App.WebHostEnvironment.WebRootPath, WAY_BILL_DIRECTORY); var filePath = Path.Combine(dic, order.WaybillFilePath); if (!File.Exists(filePath)) { _logger.LogInformation("快递面单文件不存在,尝试重新下载..."); IDeliverySend deliverySend = GetDeliverySend(order.KDCompany); await deliverySend.DownloadWaybillFile(order.KDNO, filePath); } var data = File.ReadAllBytes(filePath); FileContentResult result = new FileContentResult(data, "application/pdf"); return result; } catch (Exception ex) { _logger.LogError(ex, "打印面单的过程中发生未知异常"); return new BadPageResult(500) { Title = "打印失败", Description = ex.Message, }; } } /// /// 根据提单号列表,向与之快递关联的订舱动态中,添加快递动态 /// /// 需要添加快递动态的提单号列表 /// 快递动态描述 /// 日志标志 [NonAction] private async Task InsertNewBookingStatusWithMblnoList(List mblnoList, string status, string logGuid = "") { if (mblnoList == null || mblnoList.Count == 0) { _logger.LogInformation($"快递订单关联订舱增加动态,{logGuid},mblNoList为空,return"); return; } _logger.LogInformation($"快递订单关联订舱增加动态,{logGuid},mblNoList:{string.Join(',', mblnoList)}"); List filteredList = mblnoList.Where(m => !string.IsNullOrWhiteSpace(m)).ToList(); // 如果提单号为空,则不进行处理 if (filteredList == null || filteredList.Count == 0) { return; } var bookingIdMapMblnoList = await _bookingOrderRep.AsQueryable(o => o.ParentId == 0 && filteredList.Contains(o.MBLNO)) .Select(o => new { o.Id, o.MBLNO }) .ToListAsync(); var bookingStatusLogList = new List(); foreach (string mblNo in filteredList) { var bookingOrderIdListTemp = bookingIdMapMblnoList.Where(o => o.MBLNO == mblNo); if (bookingOrderIdListTemp == null) { _logger.LogInformation($"根据提单号{mblNo}查询订舱记录主键结果为空,{logGuid}"); continue; } else if (bookingOrderIdListTemp.Count() > 1) { _logger.LogInformation($"根据提单号{mblNo}查询订舱记录主键时查出多条记录,ids:{string.Join(',', bookingOrderIdListTemp.Select(b => b.Id).ToArray())},{logGuid}"); continue; } long? bookingOrderId = bookingOrderIdListTemp.First().Id; _logger.LogInformation($"根据提单号{mblNo}查询订舱记录主键为{bookingOrderId},{logGuid}"); bookingStatusLogList.Add(new BookingStatusLog() { BookingId = bookingOrderId, Status = status, Category = "kuaidi", OpTime = DateTime.Now, MBLNO = mblNo }); } if (bookingStatusLogList.Count > 0) { await _bookingStatuslogRep.InsertAsync(bookingStatusLogList); } } /// /// 根据不同的快递公司,解析出不同的实现类 /// /// /// private IDeliverySend GetDeliverySend(string kdCompany) { return _deliverySendServiceProvider.GetService(kdCompany switch { "sf" => nameof(SFDeliverySend), "jd" => throw Oops.Oh("京东快递以后会对接的"), "ems" => throw Oops.Oh("EMS快递以后会对接的"), _ => throw Oops.Oh("未知快递公司") }); } #region 顺丰推送接口 /// /// 接收顺丰推送的路由动态 /// [AllowAnonymous] [NonUnify] [HttpPost("ReceiveSfRoute")] public async Task ReceiveSfRoute(SFReceiveRouteDto orderStatus) { try { string logGuid = Guid.NewGuid().ToString(); _logger.LogInformation($"接收到顺丰路由动态,{logGuid},入参:{orderStatus?.ToJsonString()}"); var expressDeliveryStatusList = new List(); foreach (WaybillRoute item in orderStatus.Body.WaybillRoute) { expressDeliveryStatusList.Add(new ExpressDeliveryStatus() { OrderId = long.Parse(item.Orderid), AcceptAddress = item.AcceptAddress, AcceptTime = string.IsNullOrEmpty(item.AcceptTime) ? null : DateTime.Parse(item.AcceptTime), ErrorCode = item.ReasonCode, ErrorDesc = item.ReasonName, MailNo = item.Mailno, StatusId = item.Id, StatusCode = item.OpCode, StatusDesc = item.Remark }); } IEnumerable> statusGroupList = expressDeliveryStatusList.GroupBy(e => e.OrderId); foreach (IGrouping item in statusGroupList) { long? orderId = item.Key; ExpressDeliveryOrder order = await _orderRep.AsQueryable(e => e.Id == orderId) .Filter(null, true) .FirstAsync(); if (order == null) { _logger.LogInformation($"未找到相应的快递订单,{logGuid},Id:{orderId}"); continue; } // 因为此方法为匿名方法,所以需要手动设置一下租户Id expressDeliveryStatusList.Where(s => s.OrderId == orderId).ToList().ForEach(e => e.TenantId = order.TenantId); #region 更新快递订单当前的状态 _logger.LogInformation($"当前快递订单的状态,{logGuid},Code:{order.CurrentStateCode},Desc:{order.CurrentStateDesc}"); ExpressDeliveryStatus lastStatus = item.MaxBy(e => e.StatusId); Lazy> waitUpdate = new(() => _orderRep.Context.Updateable()); if (order.CurrentStateCode != lastStatus.StatusCode) { waitUpdate.Value.SetColumns(o => o.CurrentStateCode == lastStatus.StatusCode); } if (order.CurrentStateDesc != lastStatus.StatusDesc) { waitUpdate.Value.SetColumns(o => o.CurrentStateDesc == lastStatus.StatusDesc); } if (waitUpdate.IsValueCreated) { _logger.LogInformation($"快递订单的状态更新为,{logGuid},Code:{lastStatus.StatusCode},Desc:{lastStatus.StatusDesc}"); await waitUpdate.Value.Where(e => e.Id == orderId).ExecuteCommandAsync(); } #endregion #region 向与快递关联的订舱列表中添加动态(如果一票快递关联了多条订舱,则每条订舱都要添加动态) List mblNoList = await _businessRep.AsQueryable(b => b.PId == orderId && b.TenantId == order.TenantId).Filter(null, true).Select(b => b.MBLNO).ToListAsync(); List filteredList = mblNoList.Where(m => !string.IsNullOrWhiteSpace(m)).ToList(); if (filteredList == null) { _logger.LogInformation($"根据快递订单主键查询关联业务表的提单号,查询结果为空,{logGuid}"); } else { _logger.LogInformation($"根据快递订单主键查询关联业务表的提单号,共查出以下记录,MblNos:{string.Join(',', filteredList)},{logGuid}"); var bookingIdMapMblnoList = await _bookingOrderRep.AsQueryable(o => o.ParentId == 0 && o.TenantId == order.TenantId && filteredList.Contains(o.MBLNO)) .Filter(null, true) .Select(o => new { o.Id, o.MBLNO }) .ToListAsync(); var bookingStatusLogList = new List(); foreach (string mblnoItem in filteredList) { var bookingOrderIdListTemp = bookingIdMapMblnoList.Where(o => o.MBLNO == mblnoItem); if (bookingOrderIdListTemp == null) { _logger.LogInformation($"根据提单号{mblnoItem}查询订舱记录主键结果为空,{logGuid}"); continue; } else if (bookingOrderIdListTemp.Count() > 1) { _logger.LogInformation($"根据提单号{mblnoItem}查询订舱记录主键时查出多条记录,ids:{string.Join(',', bookingOrderIdListTemp.Select(b => b.Id).ToArray())},{logGuid}"); continue; } long? bookingOrderId = bookingOrderIdListTemp.First().Id; _logger.LogInformation($"根据提单号{mblnoItem}查询订舱记录主键为{bookingOrderId},{logGuid}"); foreach (ExpressDeliveryStatus statusItem in item) { bookingStatusLogList.Add(new BookingStatusLog() { BookingId = bookingOrderId, Status = statusItem.StatusDesc, Category = "kuaidi", OpTime = DateTime.Now, MBLNO = bookingOrderIdListTemp.First().MBLNO, TenantId = order.TenantId }); } } await _bookingStatuslogRep.InsertAsync(bookingStatusLogList); } #endregion } #region 保存快递动态 // 如果租户Id不为null或0,说明接收到的快递动态能找到相应的快递订单,然后再进行动态新增操作 expressDeliveryStatusList = expressDeliveryStatusList.Where(e => e.TenantId != null && e.TenantId != 0).ToList(); if (expressDeliveryStatusList != null && expressDeliveryStatusList.Count > 0) { await _statusRep.InsertAsync(expressDeliveryStatusList); } #endregion return new { return_code = "0000", return_msg = "成功" }; } catch (Exception ex) { _logger.LogError(ex, "接收顺丰推送的路由状态时发生异常"); return new { return_code = "1000", return_msg = ex.Message }; } } /// /// 接收顺丰推送的订单动态 /// [AllowAnonymous] [NonUnify] [HttpPost("ReceiveSfOrderState")] public async Task ReceiveSfOrderState(ReceiveSfOrderStateDto receive) { try { string logGuid = Guid.NewGuid().ToString(); _logger.LogInformation($"接收到顺丰快递动态,{logGuid},入参:{receive?.ToJsonString()}"); var expressDeliveryStatusList = new List(); foreach (ReceiveSfOrderStateDto.OrderState item in receive.orderState) { expressDeliveryStatusList.Add(new ExpressDeliveryStatus() { OrderId = long.Parse(item.orderNo), AcceptAddress = "", AcceptTime = null, ErrorCode = "", ErrorDesc = "", MailNo = item.waybillNo, StatusId = receive.requestId, StatusCode = item.orderStateCode, StatusDesc = item.orderStateDesc }); } IEnumerable> statusGroupList = expressDeliveryStatusList.GroupBy(e => e.OrderId); foreach (IGrouping item in statusGroupList) { long? orderId = item.Key; ExpressDeliveryOrder order = await _orderRep.AsQueryable(e => e.Id == orderId) .Filter(null, true) .FirstAsync(); if (order == null) { _logger.LogInformation($"未找到相应的快递订单,{logGuid},Id:{orderId}"); continue; } // 因为此方法为匿名方法,所以需要手动设置一下租户Id expressDeliveryStatusList.Where(s => s.OrderId == orderId).ToList().ForEach(e => e.TenantId = order.TenantId); #region 更新快递订单当前的状态 _logger.LogInformation($"当前快递订单的状态,{logGuid},Code:{order.CurrentStateCode},Desc:{order.CurrentStateDesc}"); ExpressDeliveryStatus lastStatus = item.MaxBy(e => e.StatusId); Lazy> waitUpdate = new(() => _orderRep.Context.Updateable()); if (order.CurrentStateCode != lastStatus.StatusCode) { waitUpdate.Value.SetColumns(o => o.CurrentStateCode == lastStatus.StatusCode); } if (order.CurrentStateDesc != lastStatus.StatusDesc) { waitUpdate.Value.SetColumns(o => o.CurrentStateDesc == lastStatus.StatusDesc); } if (waitUpdate.IsValueCreated) { _logger.LogInformation($"快递订单的状态更新为,{logGuid},Code:{lastStatus.StatusCode},Desc:{lastStatus.StatusDesc}"); await waitUpdate.Value.Where(e => e.Id == orderId).ExecuteCommandAsync(); } #endregion #region 向与快递关联的订舱列表中添加动态(如果一票快递关联了多条订舱,则每条订舱都要添加动态) List mblNoList = await _businessRep.AsQueryable(b => b.PId == orderId && b.TenantId == order.TenantId).Filter(null, true).Select(b => b.MBLNO).ToListAsync(); List filteredList = mblNoList.Where(m => !string.IsNullOrWhiteSpace(m)).ToList(); if (filteredList == null) { _logger.LogInformation($"根据快递订单主键查询关联业务表的提单号,查询结果为空,{logGuid}"); } else { _logger.LogInformation($"根据快递订单主键查询关联业务表的提单号,共查出以下记录,MblNos:{string.Join(',', filteredList)},{logGuid}"); var bookingIdMapMblnoList = await _bookingOrderRep.AsQueryable(o => o.ParentId == 0 && o.TenantId == order.TenantId && filteredList.Contains(o.MBLNO)) .Filter(null, true) .Select(o => new { o.Id, o.MBLNO }) .ToListAsync(); var bookingStatusLogList = new List(); foreach (string mblnoItem in filteredList) { var bookingOrderIdListTemp = bookingIdMapMblnoList.Where(o => o.MBLNO == mblnoItem); if (bookingOrderIdListTemp == null) { _logger.LogInformation($"根据提单号{mblnoItem}查询订舱记录主键结果为空,{logGuid}"); continue; } else if (bookingOrderIdListTemp.Count() > 1) { _logger.LogInformation($"根据提单号{mblnoItem}查询订舱记录主键时查出多条记录,ids:{string.Join(',', bookingOrderIdListTemp.Select(b => b.Id).ToArray())},{logGuid}"); continue; } long? bookingOrderId = bookingOrderIdListTemp.First().Id; _logger.LogInformation($"根据提单号{mblnoItem}查询订舱记录主键为{bookingOrderId},{logGuid}"); foreach (ExpressDeliveryStatus statusItem in item) { bookingStatusLogList.Add(new BookingStatusLog() { BookingId = bookingOrderId, Status = statusItem.StatusDesc, Category = "kuaidi", OpTime = DateTime.Now, MBLNO = bookingOrderIdListTemp.First().MBLNO, TenantId = order.TenantId }); } } await _bookingStatuslogRep.InsertAsync(bookingStatusLogList); } #endregion } #region 保存快递动态 // 如果租户Id不为null或0,说明接收到的快递动态能找到相应的快递订单,然后再进行动态新增操作 expressDeliveryStatusList = expressDeliveryStatusList.Where(e => e.TenantId != null && e.TenantId != 0).ToList(); if (expressDeliveryStatusList != null && expressDeliveryStatusList.Count > 0) { await _statusRep.InsertAsync(expressDeliveryStatusList); } #endregion return new { success = "true", code = "0", msg = "" }; } catch (Exception ex) { _logger.LogError(ex, "接收顺丰推送的订单状态时发生异常"); return new { success = "false", code = "0", msg = ex.Message }; } } /// /// 接收顺丰推送的费用信息 /// [AllowAnonymous] [NonUnify] [HttpPost("ReceiveSfFee")] [HttpGet("ReceiveSfFee")] public async Task ReceiveSfFee([FromForm] ReceiveSfFeeDto receive) { try { string logGuid = Guid.NewGuid().ToString(); _logger.LogInformation($"接收到顺丰快递费用信息,{logGuid},入参:{receive?.ToJsonString()}"); ContentModel content = JsonConvert.DeserializeObject(receive.content); long orderId = long.Parse(content.orderNo); ExpressDeliveryOrder order = await _orderRep.AsQueryable(e => e.Id == orderId) .Filter(null, true) .FirstAsync(); if (order == null) { _logger.LogInformation($"未找到相应的快递订单,{logGuid},Id:{orderId}"); return new { code = "200", partnerCode = "", service = "", msgData = "" }; } // 保存计费重量 var feeWeight = float.Parse(content.meterageWeightQty); await _orderRep.Context.Updateable() .SetColumns(o => o.FeeWeight == feeWeight + "(kg)") .Where(e => e.Id == orderId) .ExecuteCommandAsync(); List feeList = new(content.feeList.Count); // 保存费用信息 foreach (Fee item in content.feeList) { feeList.Add(new ExpressDeliveryFee() { Account = item.customerAcctCode, FeeAmount = item.feeAmt, FeeTypeCode = item.feeTypeCode, FeeTypeName = item.feeTypeCode switch { "1" => "主运费", "2" => "其他费用", "3" => "保费", "4" => "代收货款服务费", "5" => "代收货款", _ => "未知费用类型" }, KDNO = item.waybillNo, OrderId = orderId, SettleAccountsTypeCode = item.settlementTypeCode }); } // 因为此方法为匿名方法,所以需要手动设置一下租户Id feeList.Where(s => s.OrderId == orderId).ToList().ForEach(e => e.TenantId = order.TenantId); await _feeRep.InsertAsync(feeList); return new { code = "200", partnerCode = "", service = "", msgData = "" }; } catch (Exception ex) { _logger.LogError(ex, "接收顺丰推送的费用信息时发生异常"); return new { code = "400", partnerCode = "", service = "", msgData = "" }; } } #endregion #region 地址簿相关 /// /// 获取快递地址簿列表 /// [HttpGet("GetAddressList")] public async Task> GetAddressList() { var modelList = await _addressRep.ToListAsync(e => true, e => e.Type, OrderByType.Desc); var result = modelList.Adapt>(); return result; } /// /// 删除快递地址簿 /// [HttpPost("DeleteAddress")] public async Task DeleteAddress([Required] string ids) { long[] idArr = ids.Split(',').Adapt(); await _addressRep.DeleteAsync(a => idArr.Contains(a.Id)); } /// /// 新增或修改地址簿 /// [HttpPost("SaveOrUpdateAddress")] public async Task SaveOrUpdateAddress(ExpressDeliveryAddressDto input) { if (input == null) return; var inputModel = input.Adapt(); if (inputModel.Id == 0) { await _addressRep.InsertAsync(inputModel); } else { await _addressRep.AsUpdateable(inputModel) .IgnoreColumns(a => a.TenantId) .ExecuteCommandAsync(); } } #endregion } }