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
}
}