using Furion; using Furion.DependencyInjection; using Furion.DistributedIDGenerator; using Furion.DynamicApiController; using Furion.FriendlyException; using Furion.JsonSerialization; using Furion.RemoteRequest.Extensions; using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Myshipping.Application.Entity; using Myshipping.Application.Entity.DraftCompare; using Myshipping.Core; using Myshipping.Core.Entity; using Myshipping.Core.Service; using Org.BouncyCastle.Asn1.X9; using StackExchange.Profiling.Internal; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace Myshipping.Application { /// /// 请求下货纸比对 /// [ApiDescriptionSettings("Application", Name = "TaskShippingOrderCompare", Order = 9)] public class TaskShippingOrderCompareService : ITaskShippingOrderCompareService, IDynamicApiController, ITransient { private readonly ISysCacheService _cache; private readonly ILogger _logger; private readonly SqlSugarRepository _bookingOrderRepository; private readonly SqlSugarRepository _bookingOrderContaRepository; private readonly SqlSugarRepository _draftCompareFeedBackRecordRepository; //private readonly SqlSugarRepository _userRepository; private readonly IBookingValueAddedService _bookingValueAddedService; private readonly IBookingOrderService _bookingOrderService; public TaskShippingOrderCompareService(ISysCacheService cache, ILogger logger, SqlSugarRepository bookingOrderRepository, SqlSugarRepository bookingOrderContaRepository, IBookingValueAddedService bookingValueAddedService, IBookingOrderService bookingOrderService, SqlSugarRepository draftCompareFeedBackRecordRepository) { _cache = cache; _logger = logger; _bookingOrderRepository = bookingOrderRepository; _bookingOrderContaRepository = bookingOrderContaRepository; _bookingValueAddedService = bookingValueAddedService; _bookingOrderService = bookingOrderService; _draftCompareFeedBackRecordRepository = draftCompareFeedBackRecordRepository; } /// /// 执行下货纸比对 /// /// 订舱主键 /// 返回回执 [HttpGet("/TaskShippingOrderCompare/ExcuteShippingOrderCompare")] public async Task ExcuteShippingOrderCompareAsync(string bookingId) { string batchNo = IDGen.NextID().ToString(); _logger.LogInformation("批次={no}获取订舱数据请求规则 {id}", batchNo, bookingId); /* 处理逻辑 1、台账触发单票下货纸比对 2、调取订舱的详情。 3、对应请求报文。 4、请求比对接口。 5、返回回执。 */ return await InnerExcuteShippingOrderCompareAsync(bookingId); } #region 执行下货纸比对 /// /// 执行下货纸比对 /// /// 订舱主键 /// 比对模式(MANUAL-手工 AUTO-自动) /// 返回回执 private async Task InnerExcuteShippingOrderCompareAsync(string bookingId, string LstShipOrderCompareMode = "MANUAL") { string batchNo = IDGen.NextID().ToString(); _logger.LogInformation("批次={no}获取订舱数据请求规则 {id}", batchNo, bookingId); /* 处理逻辑 1、台账触发单票下货纸比对 2、调取订舱的详情。 3、对应请求报文。 4、请求比对接口。 5、返回回执。 */ TaskManageExcuteResultDto result = new TaskManageExcuteResultDto(); try { DateTime nowDate = DateTime.Now; long id = long.Parse(bookingId); var model = _bookingOrderRepository.AsQueryable().Filter(null, true) .First(a => a.Id == id && !a.IsDeleted && a.TenantId == UserManager.TENANT_ID); if (model == null) throw Oops.Oh($"订舱主键{bookingId}无法获取业务信息"); _logger.LogInformation("批次={no}获取订舱数据完成", batchNo); //附主信息 var mainInfo = model.Adapt(); mainInfo.BusiPKId = model.Id.ToString(); mainInfo.UserId = UserManager.UserId.ToString(); mainInfo.UserName = UserManager.Name; mainInfo.UserEmail = UserManager.Email; var contaList = await _bookingOrderContaRepository.AsQueryable().Filter(null, true) .Where(x => x.BILLID == model.Id && x.TenantId == UserManager.TENANT_ID).ToListAsync(); _logger.LogInformation("批次={no} 提取箱完成 数量={total}", batchNo, contaList.Count); if (contaList.Count > 0) { mainInfo.ContaList = contaList.Adapt>(); } var msgModel = GetMessageInfo(batchNo, mainInfo); _logger.LogInformation("批次={no} 对应请求报文完成 msg={msg}", batchNo, JSON.Serialize(msgModel)); DateTime bDate = DateTime.Now; var compareResult = await ExcuteCompare(msgModel); DateTime eDate = DateTime.Now; TimeSpan ts = eDate.Subtract(bDate); var timeDiff = ts.TotalMilliseconds; _logger.LogInformation("批次={no} 请求完成,耗时:{timeDiff}ms. 结果{msg}", batchNo, timeDiff, compareResult.succ ? "成功" : "失败"); if (compareResult == null) throw Oops.Oh($"订舱主键{bookingId}请求下货纸比对失败,返回为空"); var orderInfo = _bookingOrderRepository.AsQueryable().Filter(null, true) .First(x => x.Id == id && x.TenantId == UserManager.TENANT_ID); if (orderInfo != null) { var oldOrderInfo = orderInfo.Adapt(); orderInfo.LstShipOrderCompareDate = bDate; if (compareResult.succ) { orderInfo.LstShipOrderCompareMode = LstShipOrderCompareMode; /* isComplete标记当票是否满足截至要求 1、场站返回的记录里所有箱都有了返场日期。 2、或者当票的实际开船日期或者预计开船日期已到也可以截至。 */ bool isComplete = false; bool isBefore = false; //需要细分状态,返场前和返场后 if (compareResult.yardStatInfo != null) { var yardStatInfo = compareResult.yardStatInfo as YardStatInfo; //如果场站集装箱都有了返场时间,即认为下货纸自动任务结束 if (yardStatInfo.LstReturnYardDate.HasValue && yardStatInfo.ContaNum >= 1 && yardStatInfo.ContaNum == yardStatInfo.ExistsReturnYardDateCtnNum) { isComplete = true; } //判断 有箱号的条数跟总计箱数量不一致 认为是反场前比对 if ((yardStatInfo.ExistsCtnNo < yardStatInfo.ContaNum)) isBefore = true; } DateTime etd = DateTime.MinValue; if(model.ATD.HasValue) { etd = model.ATD.Value; } else if (model.ETD.HasValue) { etd = model.ETD.Value; } if (etd != DateTime.MinValue && etd <= DateTime.Now) isComplete = true; if (compareResult.extra.IsExistsDiff) { if(isComplete) { orderInfo.LstShipOrderCompareRlt = "DIFF"; orderInfo.LstShipOrderCompareRltName = "有差异"; } else { bool isBeforeEqual = false; var checkList = compareResult.extra.ShowDetailList.Where(b => (b.PCode == null || b.PCode == "") && b.IsDisplay && (b.FieldCode.ToUpper() != "PKGS" && b.FieldCode.ToUpper() != "KGS" && b.FieldCode.ToUpper() != "CBM")) .ToList(); if (!checkList.Any(t => t.IsDiff)) { isBeforeEqual = true; } if (isBefore) { if (isBeforeEqual) { orderInfo.LstShipOrderCompareRlt = "BEFORE_EQUAL"; orderInfo.LstShipOrderCompareRltName = "返场前比对正常"; } else { orderInfo.LstShipOrderCompareRlt = "BEFORE_DIFF"; orderInfo.LstShipOrderCompareRltName = "返场前比对有差异"; } } else { orderInfo.LstShipOrderCompareRlt = "DIFF_U"; orderInfo.LstShipOrderCompareRltName = "有差异未结束"; } } } else { if (isComplete) { orderInfo.LstShipOrderCompareRlt = "NO_DIFF"; orderInfo.LstShipOrderCompareRltName = "正常"; //比对成功后触发下货纸比对状态 var saveStatusRlt = await _bookingValueAddedService.SaveServiceStatus(new ModifyServiceProjectStatusDto { BookingId = long.Parse(bookingId), SourceType = TrackingSourceTypeEnum.AUTO, StatusCodes = new List { new ModifyServiceProjectStatusDetailDto { StatusCode = "XHZBDCHG" } } }); _logger.LogInformation("批次={no} 异步推送下货纸比对状态完成,结果={rlt}", batchNo, JSON.Serialize(saveStatusRlt)); } else { if (isBefore) { orderInfo.LstShipOrderCompareRlt = "BEFORE_EQUAL"; orderInfo.LstShipOrderCompareRltName = "返场前比对正常"; } else { orderInfo.LstShipOrderCompareRlt = "NO_DIFF_U"; orderInfo.LstShipOrderCompareRltName = "正常未结束"; //比对成功后触发下货纸比对状态 var saveStatusRlt = await _bookingValueAddedService.SaveServiceStatus(new ModifyServiceProjectStatusDto { BookingId = long.Parse(bookingId), SourceType = TrackingSourceTypeEnum.AUTO, StatusCodes = new List { new ModifyServiceProjectStatusDetailDto { StatusCode = "XHZBDCHG" } } }); _logger.LogInformation("批次={no} 异步推送下货纸比对状态完成,结果={rlt}", batchNo, JSON.Serialize(saveStatusRlt)); } } } } else { orderInfo.LstShipOrderCompareRlt = "NO_YARD"; orderInfo.LstShipOrderCompareRltName = "无动态"; } if (compareResult.extra != null) orderInfo.LstShipOrderCompareId = compareResult.extra.TaskCompareId; //更新 await _bookingOrderRepository.AsUpdateable(orderInfo).UpdateColumns(it => new { it.LstShipOrderCompareId, it.LstShipOrderCompareDate, it.LstShipOrderCompareRlt, it.LstShipOrderCompareRltName, it.LstShipOrderCompareMode, }).ExecuteCommandAsync(); // 记录日志 await _bookingOrderService.SaveLog(orderInfo, oldOrderInfo, "下货纸比对"); } result.succ = compareResult.succ; result.msg = compareResult.msg; result.extra = compareResult.extra; result.extra2 = compareResult.extra2; result.total = compareResult.total; result.yardStatInfo = compareResult.yardStatInfo; _logger.LogInformation("批次={no} 请求下货纸比对返回结果{msg}", batchNo, JSON.Serialize(compareResult)); _logger.LogInformation("批次={no} 返回结果{msg}", batchNo, JSON.Serialize(result)); } catch (Exception ex) { result.succ = false; result.msg = $"请求下货纸比对异常,{ex.Message}"; } return result; } #endregion /// /// 批量执行下货纸比对 /// /// 订舱主键组 /// 返回回执 [HttpPost("/TaskShippingOrderCompare/ExcuteShippingOrderCompareBatch")] public async Task ExcuteShippingOrderCompareBatchAsync([FromBody] string[] bookingIds) { TaskManageExcuteResultDto result = new TaskManageExcuteResultDto(); try { var ids = bookingIds.Select(a => long.Parse(a)).Distinct().ToArray(); var list = _bookingOrderRepository.AsQueryable().Where(t => ids.Contains(t.Id)).ToList(); if (list.Count != ids.Length) { var noRecord = string.Join(",", ids.GroupJoin(list, l => l, r => r.Id, (l, r) => { var currList = r.ToList(); if (r.Count() > 0) return 0; return l; }).Where(t => t > 0).ToArray()); throw Oops.Oh($"以下主键信息 {noRecord} 检索失败或者已作废过"); } //这里单票下货纸比对时,等待结果返回 if(ids.Length == 1) { result = await InnerExcuteShippingOrderCompareAsync(list.FirstOrDefault().Id.ToString()); } else { List> listOfTasks = new List>(); list.ForEach(entity => { listOfTasks.Add(InnerExcuteShippingOrderCompareAsync(entity.Id.ToString())); }); await Task.WhenAll(listOfTasks); result.succ = true; result.msg = "比对完成"; string rltMsg = string.Empty; result.rows = listOfTasks.Select(a => { var origId = a.Result.extra.OrigPKId; var order = list.FirstOrDefault(x => x.Id == long.Parse(origId)); if (a.Result.succ) { if (a.Result.total > 0) { return new { MblNo = order.MBLNO, Msg = "比对异常", IsDiff = true, rlt = a.Result.succ }; } return new { MblNo = order.MBLNO, Msg = "比对一致", IsDiff = false, rlt = a.Result.succ }; } return new { MblNo = order?.MBLNO, Msg = "比对一致", IsDiff = false, rlt = a.Result.succ }; }).ToList(); } } catch (Exception ex) { result.succ = false; result.msg = $"请求下货纸比对异常,{ex.Message}"; } return result; } /// /// 下货纸自动比对回写状态 /// /// 比对回写详情 /// 返回回执 [AllowAnonymous, HttpPost("/TaskShippingOrderCompare/AutoTaskShippingOrderCompareCallBack")] public async Task AutoTaskShippingOrderCompareCallBackAsync([FromBody] ShippingOrderCompareCallBackInfo model) { TaskManageExcuteResultDto result = new TaskManageExcuteResultDto(); try { if(string.IsNullOrWhiteSpace(model.reqBusiId)) throw Oops.Oh($"订舱主键{model.reqBusiId}不能为空"); if (string.IsNullOrWhiteSpace(model.compareId)) throw Oops.Oh($"比对ID{model.compareId}不能为空"); if (string.IsNullOrWhiteSpace(model.compareMode)) throw Oops.Oh($"下货纸比对方式不能为空{model.compareMode}不能为空"); if (string.IsNullOrWhiteSpace(model.compareRltCode)) throw Oops.Oh($"比对结果代码{model.compareRltCode}不能为空"); if (string.IsNullOrWhiteSpace(model.compareRltName)) throw Oops.Oh($"比对结果名称{model.compareId}不能为空"); var bookingOrder = await _bookingOrderRepository.AsQueryable() .FirstAsync(a => a.Id == long.Parse(model.reqBusiId)); if (bookingOrder == null) { throw Oops.Oh($"订舱信息检索失败,ID={model.reqBusiId}"); } var oldBookingOrder = bookingOrder.Adapt(); bookingOrder.LstShipOrderCompareId = model.compareId; bookingOrder.LstShipOrderCompareDate = model.compareDate; bookingOrder.LstShipOrderCompareRlt = model.compareRltCode; bookingOrder.LstShipOrderCompareRltName = model.compareRltName; bookingOrder.LstShipOrderCompareMode = model.compareMode; //更新 await _bookingOrderRepository.AsUpdateable(bookingOrder).UpdateColumns(it => new { it.LstShipOrderCompareId, it.LstShipOrderCompareDate, it.LstShipOrderCompareRlt, it.LstShipOrderCompareRltName, it.LstShipOrderCompareMode, }).ExecuteCommandAsync(); await _bookingOrderService.SaveLog(bookingOrder, oldBookingOrder, "下货纸自动比对回写状态"); result.succ = true; result.msg = "更新完成"; } catch (Exception ex) { result.succ = false; result.msg = $"下货纸自动比对回写状态失败,{ex.Message}"; } return result; } /// /// 生成请求规则报文 /// /// 批次号 /// 订舱主业务信息 /// 返回请求报文类 [NonAction] private TaskMessageInfoDto GetMessageInfo(string batchNo, TaskMessageMain mainInfo) { DateTime nowDate = DateTime.Now; TaskMessageInfoDto msgModel = new TaskMessageInfoDto(); msgModel.Head = new TaskMessageHead { GID = batchNo, MessageType = "SHIP_ORDER_COMPARE", SenderId = App.Configuration["RulesEngineSender"], SenderName = App.Configuration["RulesEngineSenderName"], ReceiverId = "RulesEngine", ReceiverName = "大简云规则引擎", Version = "1.0", RequestDate = nowDate.ToString("yyyy-MM-dd HH:mm:ss"), RequestAction = "Compare", }; msgModel.Main = mainInfo; return msgModel; } #region 请求下货纸比对 /// /// 请求下货纸比对 /// /// /// [NonAction] private async Task ExcuteCompare(TaskMessageInfoDto info) { TaskManageExcuteResultDto model = null; /* 1、读取配置文件中的规则引擎URL 2、填充请求的类,并生成JSON报文 3、POST请求接口,并记录回执。 4、返回信息。 */ var url = App.Configuration["ShippingOrderCompareUrl"]; try { var res = await url.OnClientCreating(client => { // client 为 HttpClient 对象 client.Timeout = TimeSpan.FromMinutes(15); // 设置超时时间 15分钟 }).SetHttpMethod(HttpMethod.Post) .SetBody(JSON.Serialize(info), "application/json") .SetContentEncoding(Encoding.UTF8) .PostAsync(); _logger.LogInformation("批次={no} 对应请求报文完成 res={res}", info.Head.GID, JSON.Serialize(res)); if (res.StatusCode == System.Net.HttpStatusCode.OK) { var userResult = await res.Content.ReadAsStringAsync(); model = JSON.Deserialize(userResult); } } catch (Exception ex) { //写日志 if (ex is HttpRequestException) throw Oops.Oh(10000002); } return model; } #endregion #region 自动执行下货纸比对 /// /// 自动执行下货纸比对 /// /// 订舱主键 /// 返回回执 [AllowAnonymous, HttpGet("/TaskShippingOrderCompare/ExcuteAutoShippingOrderCompare"), ApiUser(ApiCode = "ExcuteAutoShippingOrderCompare")] public async Task ExcuteAutoShippingOrderCompareAsync(string bookingId) { string batchNo = IDGen.NextID().ToString(); _logger.LogInformation("批次={no}获取订舱数据请求规则 {id}", batchNo, bookingId); /* 处理逻辑 1、台账触发单票下货纸比对 2、调取订舱的详情。 3、对应请求报文。 4、请求比对接口。 5、返回回执。 */ return await InnerExcuteShippingOrderCompareAsync(bookingId, "AUTO"); } #endregion /// /// 获取下货纸比对结果 /// /// 订舱主键 /// 返回回执 [HttpGet("/TaskShippingOrderCompare/GetShippingOrderCompareResult")] public async Task GetShippingOrderCompareResult(long bookingId) { string batchNo = IDGen.NextID().ToString(); TaskManageExcuteResultDto result = new TaskManageExcuteResultDto(); try { var model = _bookingOrderRepository.AsQueryable().Filter(null, true) .First(a => a.Id == bookingId && !a.IsDeleted && a.TenantId == UserManager.TENANT_ID); DateTime nowDate = DateTime.Now; TaskMessageInfoDto msgModel = new TaskMessageInfoDto { Head = new TaskMessageHead { GID = batchNo, MessageType = "DRAFT_COMPARE", SenderId = App.Configuration["RulesEngineSender"], SenderName = App.Configuration["RulesEngineSenderName"], ReceiverId = "RulesEngine", ReceiverName = "大简云规则引擎", Version = "1.0", RequestDate = nowDate.ToString("yyyy-MM-dd HH:mm:ss"), RequestAction = "Compare", }, Main = new TaskMessageMain { BusiPKId = bookingId.ToString(), TaskCompareId = model.LstShipOrderCompareId, } }; _logger.LogInformation($"开始请求查询 msg={JSON.Serialize(msgModel)}"); result = await GetCompareResult(msgModel); if (result != null && result.extra != null && !string.IsNullOrWhiteSpace(result.extra.TaskCompareId)) { var feedBack = _draftCompareFeedBackRecordRepository.AsQueryable().First(a => a.BOOKING_ID == bookingId && a.TASK_COMPARE_ID == result.extra.TaskCompareId); if (feedBack != null) { result.extra.ManualFeedBackResult = new FeedBackResult { OperNote = feedBack.NOTES, OperTime = feedBack.CreatedTime.Value, OperUser = feedBack.CreatedUserName, Reason = (feedBack.IS_OCR_ERROR ? "识别问题 " : "") + (feedBack.IS_EDIT_ERROR ? "录入问题 " : "") + (feedBack.IS_AGENT_ERROR ? "代理录入问题 " : "") }; } } } catch (Exception ex) { _logger.LogInformation("获取Draft比对结果异常,原因:{error}", ex.Message); throw Oops.Oh($"获取Draft比对结果异常,原因:{ex.Message}"); } return result; } #region 获取Draft比对结果 /// /// 获取Draft比对结果 /// /// 请求报文 /// 返回回执 [NonAction] private async Task GetCompareResult(TaskMessageInfoDto info) { TaskManageExcuteResultDto model = null; var url = App.Configuration["GetShippingOrderCompareUrl"]; try { var res = await url.SetHttpMethod(HttpMethod.Post) .SetBody(JSON.Serialize(info), "application/json") .SetContentEncoding(Encoding.UTF8) .PostAsync(); _logger.LogInformation("批次={no} 对应请求报文完成 res={res}", info.Head.GID, JSON.Serialize(res)); if (res.StatusCode == System.Net.HttpStatusCode.OK) { var userResult = await res.Content.ReadAsStringAsync(); _logger.LogInformation("对应请求报文 userResult={userResult}", userResult); model = JSON.Deserialize(userResult); } } catch (Exception ex) { //写日志 if (ex is HttpRequestException) throw Oops.Oh(10000002); } return model; } #endregion } }