using Furion; using Furion.EventBus; using Furion.FriendlyException; using Furion.RemoteRequest.Extensions; using Mapster; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Myshipping.Application.ConfigOption; using Myshipping.Application.Entity; using Myshipping.Application.Enum; using Myshipping.Application.Service.BookingOrder.Dto; using Myshipping.Core; using Myshipping.Core.Entity; using Myshipping.Core.Service; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Yitter.IdGenerator; namespace Myshipping.Application.Event { /// /// 订舱同步给客户订舱系统 /// public class BookingSyncSubscriber : IEventSubscriber { private IServiceProvider _services { get; } private readonly ILogger _logger; public BookingSyncSubscriber(IServiceProvider services, ILogger logger) { _services = services; _logger = logger; } #region 运营端事件 //发送订舱同步数据给客户订舱系统 [EventSubscribe("SendToCustomer:Book")] public async Task SendToCustomer(EventHandlerExecutingContext context) { _logger.LogInformation($"收到订舱同步客户订舱系统请求:{context.Source.Payload}"); var bookId = (long)context.Source.Payload; using var scope = _services.CreateScope(); var repoCutomerOrder = scope.ServiceProvider.GetRequiredService>(); var repoOrder = scope.ServiceProvider.GetRequiredService>(); var repoCtn = scope.ServiceProvider.GetRequiredService>(); var repoFile = scope.ServiceProvider.GetRequiredService>(); var repoStaLog = scope.ServiceProvider.GetRequiredService>(); var repoStaLogDetail = scope.ServiceProvider.GetRequiredService>(); var repoGoodsSta = scope.ServiceProvider.GetRequiredService>(); var repoGoodsStaCfg = scope.ServiceProvider.GetRequiredService>(); var repoLog = scope.ServiceProvider.GetRequiredService>(); var repoLogDetail = scope.ServiceProvider.GetRequiredService>(); var cacheService = scope.ServiceProvider.GetRequiredService(); var custOrder = await repoCutomerOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.BookingId == bookId); if (custOrder == null) //非客户订舱系统过来的数据 { _logger.LogInformation($"ID为 {bookId} 的数据并非来自客户订舱系统,不继续处理数据回推"); return; } if (string.IsNullOrEmpty(custOrder.FeedbackUrl)) { _logger.LogInformation($"ID为 {bookId} 的数据未提供回推接口URL,不继续处理数据回推"); return; } var order = await repoOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == bookId); var ctns = await repoCtn.AsQueryable().Filter(null, true).Where(x => x.BILLID == bookId).ToListAsync(); var files = await repoFile.AsQueryable().Filter(null, true).Where(x => x.BookingId == bookId).ToListAsync(); var staLogs = await repoStaLog.AsQueryable().Filter(null, true).Where(x => x.BookingId == bookId).ToListAsync(); var staLogDetails = await repoStaLogDetail.AsQueryable().Filter(null, true).Where(x => staLogs.Select(y => y.Id).Contains(x.PId)).ToListAsync(); var goodsLogs = await repoGoodsSta.AsQueryable().Filter(null, true).Where(x => x.bookingId == bookId).ToListAsync(); var goodsLogConfigs = await repoGoodsStaCfg.AsQueryable().Filter(null, true).Where(x => goodsLogs.Select(y => y.ConfigId).Contains(x.Id)).ToListAsync(); var bookLogs = await repoLog.AsQueryable().Filter(null, true).Where(x => x.BookingId == bookId).ToListAsync(); var bookLogDetails = await repoLogDetail.AsQueryable().Filter(null, true).Where(x => bookLogs.Select(y => y.Id).Contains(x.PId)).ToListAsync(); //把ID还原为客户订舱系统中的ID order.Id = Convert.ToInt64(order.BSNO); order.BSNO = null; ctns.ForEach(x => x.BILLID = order.Id); files.ForEach(x => x.BookingId = order.Id); staLogs.ForEach(x => x.BookingId = order.Id); goodsLogs.ForEach(x => x.bookingId = order.Id); //货运动态 var staLogSendList = staLogs.Adapt>(); staLogSendList.ForEach(x => { x.Details = staLogDetails.Where(y => y.PId == x.Id).Adapt>(); }); //修改日志 var bookLogSendList = bookLogs.Adapt>(); bookLogSendList.ForEach(x => { x.Details = bookLogDetails.Where(y => y.PId == x.Id).Adapt>(); }); //发送对象 var sendObj = new BookingCustomerRecDataFeedbackDto() { Order = order.Adapt(), Ctns = ctns.Adapt>(), Files = files.Adapt>(), StatusLogs = staLogSendList, BookingLogs = bookLogSendList }; ////文件内容 //var opt = App.GetOptions(); //var dirAbs = opt.basePath; //if (string.IsNullOrEmpty(dirAbs)) //{ // dirAbs = App.WebHostEnvironment.WebRootPath; //} //foreach (var file in files) //{ // var fileFullPath = Path.Combine(dirAbs, file.FilePath); // if (File.Exists(fileFullPath)) // { // sendObj.Files.First(x => x.Id == file.Id).FileContent = File.ReadAllBytes(fileFullPath); // } //} var feedbackObj = new BookingFeedbackDto(BookingFeedbackType.BookingAll.ToString()); feedbackObj.JsonContent = sendObj.ToJsonString(); //回推回执 _logger.LogInformation($"准备发送客户订舱数据同步:{feedbackObj.ToJsonString()},URL:{custOrder.FeedbackUrl}"); var rtn = await custOrder.FeedbackUrl .SetHeaders(new Dictionary { { CommonConst.API_USER_HEADER_KEY, custOrder.FeedbackKey}, { CommonConst.API_USER_HEADER_SECRET, custOrder.FeedbackSecret} }) .SetBody(feedbackObj, "application/x-www-form-urlencoded") .PostAsStringAsync(); _logger.LogInformation($"回推数据同步返回:{rtn}"); var jobjRtn = JObject.Parse(rtn); if (jobjRtn.GetIntValue("code") != 200) { throw Oops.Bah(jobjRtn.GetStringValue("message")); } } //推送货物状态到客户订舱系统 [EventSubscribe("SyncGoodStatus:Book")] public async Task SyncGoodStatus(EventHandlerExecutingContext context) { _logger.LogInformation($"收到推送货物状态到客户订舱系统:{context.Source.Payload}"); GoodsStatusSyncDto payload = context.Source.Payload as GoodsStatusSyncDto; using var scope = _services.CreateScope(); var repoCutomerOrder = scope.ServiceProvider.GetRequiredService>(); var repoOrder = scope.ServiceProvider.GetRequiredService>(); var cacheService = scope.ServiceProvider.GetRequiredService(); var custOrder = await repoCutomerOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.BookingId == payload.Id); if (custOrder == null) //非客户订舱系统过来的数据 { _logger.LogInformation($"ID为 {payload.Id} 的数据并非来自客户订舱系统,不继续处理数据回推"); return; } if (string.IsNullOrEmpty(custOrder.FeedbackUrl)) { _logger.LogInformation($"ID为 {custOrder.Id} 的数据未提供回推接口URL,不继续处理数据回推"); return; } var order = await repoOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == payload.Id); //把ID还原为客户订舱系统中的ID payload.Id = Convert.ToInt64(order.BSNO); var feedbackObj = new BookingFeedbackDto(BookingFeedbackType.GoodsStatus.ToString()); feedbackObj.JsonContent = payload.ToJsonString(); //回推回执 _logger.LogInformation($"准备发送货物状态数据同步:{feedbackObj.ToJsonString()},URL:{custOrder.FeedbackUrl}"); var rtn = await custOrder.FeedbackUrl .SetHeaders(new Dictionary { { CommonConst.API_USER_HEADER_KEY, custOrder.FeedbackKey}, { CommonConst.API_USER_HEADER_SECRET, custOrder.FeedbackSecret} }) .SetBody(feedbackObj, "application/x-www-form-urlencoded") .PostAsStringAsync(); _logger.LogInformation($"回推数据同步返回:{rtn}"); var jobjRtn = JObject.Parse(rtn); if (jobjRtn.GetIntValue("code") != 200) { throw Oops.Bah(jobjRtn.GetStringValue("message")); } } //运营端处理了修改服务项目的消息的回推消息 [EventSubscribe("Message:Process:Booking:ChangeServiceItem")] public async Task MessageProcessBookingChangeServiceItem(EventHandlerExecutingContext context) { _logger.LogInformation($"收到回推服务项目修改审核到客户订舱系统:{context.Source.Payload}"); var msgId = (long)context.Source.Payload; using var scope = _services.CreateScope(); var repMessage = scope.ServiceProvider.GetRequiredService>(); var repoCutomerOrder = scope.ServiceProvider.GetRequiredService>(); var repoOrder = scope.ServiceProvider.GetRequiredService>(); var repoServiceItem = scope.ServiceProvider.GetRequiredService>(); var cacheService = scope.ServiceProvider.GetRequiredService(); var msg = await repMessage.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == msgId); var extObj = JsonConvert.DeserializeObject(msg.ExtData); var custOrdId = extObj.CustomerOrderId; var custOrder = await repoCutomerOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == custOrdId); //修改服务项目 if (msg.ProcStatus == MessageProcessStatus.Accept.ToString()) { var serItm = cacheService.GetAllDictData().Result.FirstOrDefault(x => x.TypeCode == "booking_service_item" && x.Code == extObj.Dto.ServiceCode); var serv = await repoServiceItem.AsQueryable().Filter(null, true).FirstAsync(x => x.BookingId == extObj.OrderId && x.Code == extObj.Dto.ServiceCode); if (extObj.Dto.IsCancel) //取消服务项目 { await repoServiceItem.DeleteAsync(serv); } else //增加服务项目 { if (serv == null) { serv = new BookingServiceItem(); serv.Id = YitIdHelper.NextId(); serv.BookingId = extObj.OrderId; serv.Code = extObj.Dto.ServiceCode; serv.Value = serItm.Value; await repoServiceItem.InsertAsync(serv); } } } ChangeServiceItemResponseDto respDto = new ChangeServiceItemResponseDto(); respDto.Dto = extObj.Dto; respDto.AuditName = msg.ProcUser; respDto.IsReject = msg.ProcStatus == MessageProcessStatus.Reject.ToString(); respDto.ProcResult = msg.ProcResult; //回推回执 var feedbackObj = new BookingFeedbackDto(BookingFeedbackType.ServiceItemAudit.ToString()); feedbackObj.JsonContent = respDto.ToJsonString(); _logger.LogInformation($"准备回推服务项目修改审核到客户订舱系统:{feedbackObj.JsonContent},URL:{custOrder.FeedbackUrl}"); var rtn = await custOrder.FeedbackUrl .SetHeaders(new Dictionary { { CommonConst.API_USER_HEADER_KEY, custOrder.FeedbackKey}, { CommonConst.API_USER_HEADER_SECRET, custOrder.FeedbackSecret} }) .SetBody(feedbackObj, "application/x-www-form-urlencoded") .PostAsStringAsync(); _logger.LogInformation($"回推服务项目修改审核:{rtn}"); var jobjRtn = JObject.Parse(rtn); if (jobjRtn.GetIntValue("code") != 200) { throw Oops.Bah(jobjRtn.GetStringValue("message")); } } //运营端处理了单证补料的消息的回推消息 [EventSubscribe("Message:Process:Booking:DocSupplement")] public async Task MessageProcessBookingDocSupplement(EventHandlerExecutingContext context) { _logger.LogInformation($"收到回推单证补料审核到客户订舱系统:{context.Source.Payload}"); var msgId = (long)context.Source.Payload; using var scope = _services.CreateScope(); var repMessage = scope.ServiceProvider.GetRequiredService>(); var repoCutomerOrder = scope.ServiceProvider.GetRequiredService>(); var repoOrder = scope.ServiceProvider.GetRequiredService>(); var repoServiceItem = scope.ServiceProvider.GetRequiredService>(); var cacheService = scope.ServiceProvider.GetRequiredService(); var _repBookingfile = scope.ServiceProvider.GetRequiredService>(); var _repStatuslog = scope.ServiceProvider.GetRequiredService>(); var msg = await repMessage.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == msgId); var extObj = JsonConvert.DeserializeObject(msg.ExtData); var custOrder = await repoCutomerOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == extObj.CustomerOrderId); var order = await repoOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == extObj.OrderId); if (msg.ProcStatus == MessageProcessStatus.Accept.ToString()) { //进入订舱附件 foreach (var ff in extObj.Dto.FileList) { var fileSuffix = Path.GetFileName(ff.FileName); var id = YitIdHelper.NextId(); var newFile = new BookingFile { Id = id, FileName = ff.FileName, FilePath = ff.FilePath, TypeCode = ff.TypeCode, TypeName = ff.TypeName, BookingId = extObj.OrderId, TenantId = order.TenantId, TenantName = order.TenantName, }; await _repBookingfile.InsertAsync(newFile); ff.Id = id; //赋值id,回传给客户端 } } //记录日志 var staLog = new BookingStatusLog(); staLog.Status = $"审核{(msg.ProcStatus == MessageProcessStatus.Accept.ToString() ? "通过" : "驳回")}单证补料"; staLog.CreatedUserId = order.CreatedUserId; staLog.CreatedUserName = msg.ProcUser; staLog.CreatedTime = DateTime.Now; staLog.OpTime = DateTime.Now; staLog.BookingId = order.Id; staLog.Category = "doc_supplement"; staLog.TenantId = order.TenantId; _repStatuslog.Insert(staLog); DocSupplementResponseDto respDto = new DocSupplementResponseDto(); respDto.Dto = extObj.Dto; respDto.AuditName = msg.ProcUser; respDto.IsReject = msg.ProcStatus == MessageProcessStatus.Reject.ToString(); respDto.ProcResult = msg.ProcResult; //回推回执 var feedbackObj = new BookingFeedbackDto(BookingFeedbackType.DocSupplementAudit.ToString()); feedbackObj.JsonContent = respDto.ToJsonString(); _logger.LogInformation($"准备回推单证补料审核到客户订舱系统:{feedbackObj.JsonContent},URL:{custOrder.FeedbackUrl}"); var rtn = await custOrder.FeedbackUrl .SetHeaders(new Dictionary { { CommonConst.API_USER_HEADER_KEY, custOrder.FeedbackKey}, { CommonConst.API_USER_HEADER_SECRET, custOrder.FeedbackSecret} }) .SetBody(feedbackObj, "application/x-www-form-urlencoded") .PostAsStringAsync(); _logger.LogInformation($"回推单证补料审核:{rtn}"); var jobjRtn = JObject.Parse(rtn); if (jobjRtn.GetIntValue("code") != 200) { throw Oops.Bah(jobjRtn.GetStringValue("message")); } } //运营端手工选中或取消服务项目的回推消息 [EventSubscribe("ServiceItem:ChangeNotify")] public async Task BookingServiceItemChangeNotify(EventHandlerExecutingContext context) { _logger.LogInformation($"收到回推服务项目手工修改的通知到客户订舱系统:{context.Source.Payload}"); var dto = context.Source.Payload as DjyChangeServiceItemDto; using var scope = _services.CreateScope(); var repoCutomerOrder = scope.ServiceProvider.GetRequiredService>(); var repoOrder = scope.ServiceProvider.GetRequiredService>(); var repoServiceItem = scope.ServiceProvider.GetRequiredService>(); var cacheService = scope.ServiceProvider.GetRequiredService(); var bookId = Convert.ToInt64(dto.Id); var custOrder = await repoCutomerOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.BookingId == bookId); var order = await repoOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == bookId); dto.Id = order.BSNO; //将运营端系统的id转为客户系统的id //回推回执 var feedbackObj = new BookingFeedbackDto(BookingFeedbackType.ServiceItemChange.ToString()); feedbackObj.JsonContent = dto.ToJsonString(); _logger.LogInformation($"准备回推服务项目手工修改的通知到客户订舱系统:{feedbackObj.JsonContent},URL:{custOrder.FeedbackUrl}"); var rtn = await custOrder.FeedbackUrl .SetHeaders(new Dictionary { { CommonConst.API_USER_HEADER_KEY, custOrder.FeedbackKey}, { CommonConst.API_USER_HEADER_SECRET, custOrder.FeedbackSecret} }) .SetBody(feedbackObj, "application/x-www-form-urlencoded") .PostAsStringAsync(); _logger.LogInformation($"回推服务项目项目手工修改的通知:{rtn}"); var jobjRtn = JObject.Parse(rtn); if (jobjRtn.GetIntValue("code") != 200) { throw Oops.Bah(jobjRtn.GetStringValue("message")); } } #endregion #region 客户端事件 //推送服务项目变动到运营端 [EventSubscribe("ServiceItem:ChangeSubmit")] public async Task SendServiceItemChangeSubmit(EventHandlerExecutingContext context) { _logger.LogInformation($"收到推送服务项目变动到运营端订舱系统:{context.Source.Payload}"); var dto = context.Source.Payload as ChangeServiceItemDto; if (dto != null) { using var scope = _services.CreateScope(); var _cache = scope.ServiceProvider.GetRequiredService(); //当前系统的url var sysUrlCfg = _cache.GetAllSysConfig().Result.FirstOrDefault(x => x.Code == "SystemUrl"); var sysUrl = sysUrlCfg.Value; if (!sysUrl.EndsWith("/")) { sysUrl += "/"; } var recUrl = _cache.GetAllSysConfig().Result.FirstOrDefault(x => x.Code == "DjyBookingRequestReceiveUrl"); if (recUrl == null || string.IsNullOrEmpty(recUrl.Value)) { throw Oops.Bah("大简云接收订舱URL地址未配置,请联系管理员"); } var userId = _cache.GetAllSysConfig().Result.FirstOrDefault(x => x.Code == "DjyBookingReceiveUserId"); var userSecret = _cache.GetAllSysConfig().Result.FirstOrDefault(x => x.Code == "DjyBookingReceiveUserSecret"); if (userId == null || string.IsNullOrEmpty(userId.Value) || userSecret == null || string.IsNullOrEmpty(userSecret.Value)) { throw Oops.Bah("大简云接收订舱用户key和秘钥未配置,请联系管理员"); } //构建完整url var submitUrl = recUrl.Value; if (!submitUrl.EndsWith("/")) { submitUrl += "/"; } submitUrl += "BookingCustomerOrder/ChangeServiceItem"; _logger.LogInformation($"提交修改服务项目数据({submitUrl},{userId.Value},{userSecret.Value}):{JsonConvert.SerializeObject(dto)}"); var rtn = await submitUrl .SetHeaders(new Dictionary { { CommonConst.API_USER_HEADER_KEY, userId.Value}, { CommonConst.API_USER_HEADER_SECRET, userSecret.Value} }) .SetBody(dto) .PostAsStringAsync(); _logger.LogInformation($"返回数据:{rtn}"); var resultText = new StringBuilder(); var jobjRtn = JObject.Parse(rtn); if (jobjRtn.GetIntValue("code") != 200) { throw Oops.Bah(jobjRtn.GetStringValue("message")); } } } //推送货物状态通知给订阅人 [EventSubscribe("GoodsStatusSubscribeNotify:Book")] public async Task GoodsStatusSubscribeNotify(EventHandlerExecutingContext context) { _logger.LogInformation($"收到推送货物状态通知请求:{context.Source.Payload}"); dynamic payload = context.Source.Payload; long bookId = payload.BookingId; string statusCode = payload.StatusCode; string statusName = payload.StatusName; using var scope = _services.CreateScope(); var cache = scope.ServiceProvider.GetRequiredService(); var repoOrder = scope.ServiceProvider.GetRequiredService>(); var repoBookingGoodsStatusSubscribe = scope.ServiceProvider.GetRequiredService>(); var order = await repoOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == bookId); var subList = await repoBookingGoodsStatusSubscribe.AsQueryable().Filter(null, true).Where(x => x.IsDeleted == false && x.BookingId == bookId).ToListAsync(); foreach (var sub in subList) { if (sub.SendMail && !string.IsNullOrEmpty(sub.Email) && sub.StatusCode.Split(',').Contains(statusCode)) { var subject = $"大简云-货物通知:{order.MBLNO}---{order.VESSEL}/{order.VOYNO}{statusName}"; var body = $"尊敬的客户,您好:

您所订阅的提单号{order.MBLNO} ,船名航次{order.VESSEL}/{order.VOYNO}的业务{statusName}。

—— 此邮件为大简云平台自动发送,请勿回复。

"; var sendUrlCfg = cache.GetAllDictData().Result.FirstOrDefault(x => x.Code == "email_api_url"); if (sendUrlCfg == null) { _logger.LogError($"未配置邮件发送URL,推送货物状态通知邮件未能发送。订舱ID:{bookId},状态:{statusName}({statusCode}),邮箱:{sub.Email}"); } var mailJson = new dynamic[]{ new { SendTo = sub.Email, Title = subject, Body = body, SmtpConfig = "SERVICE" } }; var mailStr = mailJson.ToJsonString(); _logger.LogInformation($"准备推送货物状态邮件通知,JSON:{mailStr},订舱ID:{bookId},状态:{statusName}({statusCode}),邮箱:{sub.Email}"); var rtn = await sendUrlCfg.Value .SetBody(mailStr) .PostAsStringAsync(); _logger.LogError($"推送货物状态通知邮件发送返回:{rtn}。订舱ID:{bookId},状态:{statusName}({statusCode}),邮箱:{sub.Email}"); var jsonRtn = JObject.Parse(rtn); if (jsonRtn.GetBooleanValue("Success")) { _logger.LogInformation($"推送货物状态通知邮件发送成功。订舱ID:{bookId},状态:{statusName}({statusCode}),邮箱:{sub.Email}"); } else { _logger.LogError($"推送货物状态通知邮件发送失败:{jsonRtn.GetStringValue("Message")}。订舱ID:{bookId},状态:{statusName}({statusCode}),邮箱:{sub.Email}。"); } } } } #endregion } }