using DS.Module.Core;
using DS.Module.Core.Attributes;
using DS.Module.Core.Helpers;
using DS.Module.DjyServiceStatus;
using DS.Module.SqlSugar;
using DS.Module.UserModule;
using DS.WMS.Core.Code.Interface;
using DS.WMS.Core.Code.Method;
using DS.WMS.Core.Invoice.Dtos;
using DS.WMS.Core.Map.Interface;
using DS.WMS.Core.Map.Method;
using DS.WMS.Core.Op.Dtos;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.TaskPlat.Dtos;
using DS.WMS.Core.TaskPlat.Entity;
using DS.WMS.Core.TaskPlat.Interface;
using Mapster;
using Masuit.Tools.Systems;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NLog;
using NPOI.SS.Formula.Functions;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace DS.WMS.Core.TaskPlat.Method
{
///
///
///
public class TaskShippingOrderCompareService : ITaskShippingOrderCompareService
{
private readonly IServiceProvider _serviceProvider;
private readonly ISqlSugarClient db;
private readonly IUser user;
private readonly ISaasDbService saasService;
private readonly IDjyServiceStatusService _djyServiceStatusService;
private readonly IMappingYardService _mappingYardService;
private readonly IMappingCarrierService _mappingCarrierService;
private readonly ICodePortService _codePortService;
private readonly string shippingOrderCompareUrl;
private readonly string shippingOrderCompareResultUrl;
const string CONST_MAPPING_YARD_MODULE = "BillTrace";
const string CONST_MAPPING_CARRIER_MODULE = "CarrierBaseMapping";
private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger();
public TaskShippingOrderCompareService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
db = _serviceProvider.GetRequiredService();
user = _serviceProvider.GetRequiredService();
saasService = _serviceProvider.GetRequiredService();
_djyServiceStatusService = _serviceProvider.GetRequiredService();
_mappingYardService = _serviceProvider.GetRequiredService();
_mappingCarrierService = _serviceProvider.GetRequiredService();
_codePortService = _serviceProvider.GetRequiredService();
shippingOrderCompareUrl = AppSetting.app(new string[] { "ShippingOrderCompare", "Url" });
shippingOrderCompareResultUrl = AppSetting.app(new string[] { "ShippingOrderCompare", "ResultUrl" });
}
///
/// 执行下货纸比对
///
/// 订舱主键
/// 返回回执
public async Task ExcuteShippingOrderCompareAsync(string bookingId)
{
string batchNo = GuidHelper.GetSnowflakeId();
Logger.Log(NLog.LogLevel.Info, "批次={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 = GuidHelper.GetSnowflakeId();
Logger.Log(NLog.LogLevel.Info, "批次={no}获取订舱数据请求规则 {id}", batchNo, bookingId);
/*
处理逻辑
1、台账触发单票下货纸比对
2、调取订舱的详情。
3、对应请求报文。
4、请求比对接口。
5、返回回执。
*/
TaskManageExcuteResultDto result = new TaskManageExcuteResultDto();
var tenantDb = saasService.GetBizDbScopeById(user.TenantId);
try
{
DateTime nowDate = DateTime.Now;
long id = long.Parse(bookingId);
var model = tenantDb.Queryable()
.First(a => a.Id == id);
if (model == null)
throw new Exception($"订舱主键{bookingId}无法获取业务信息");
Logger.Log(NLog.LogLevel.Info, "批次={no}获取订舱数据完成", batchNo);
//附主信息
var mainInfo = model.Adapt();
//这里需要对场站进行映射翻译
var mapYardList = _mappingYardService.GetAllList().GetAwaiter().GetResult().Data;
if (mapYardList.Count > 0)
mapYardList = mapYardList.Where(a => a.Module.Equals(CONST_MAPPING_YARD_MODULE, StringComparison.OrdinalIgnoreCase)).ToList();
if (mapYardList.Any(a => a.LinkId == model.YardId))
{
mainInfo.YardCode = mapYardList.FirstOrDefault(a => a.LinkId == model.YardId).MapCode;
}
else
{
throw new Exception($"场站:{model.Yard} 没有对应的出口运踪映射代码");
}
var codePortList = _codePortService.GetAllList().GetAwaiter().GetResult().Data;
if (codePortList.Any(a => a.Id == model.LoadPortId))
mainInfo.PortLoadId = codePortList.FirstOrDefault(a => a.Id == model.LoadPortId).EdiCode;
if (codePortList.Any(a => a.Id == model.DischargePortId))
mainInfo.PortDischargeId = codePortList.FirstOrDefault(a => a.Id == model.DischargePortId).EdiCode;
if (codePortList.Any(a => a.Id == model.DeliveryPlaceId))
mainInfo.PlaceDeliveryId = codePortList.FirstOrDefault(a => a.Id == model.DeliveryPlaceId).EdiCode;
if (codePortList.Any(a => a.Id == model.DestinationId))
mainInfo.DestinationId = codePortList.FirstOrDefault(a => a.Id == model.DestinationId).EdiCode;
var mapCarrierList = _mappingCarrierService.GetAllList().GetAwaiter().GetResult().Data;
if (mapCarrierList.Count > 0)
mapCarrierList = mapCarrierList.Where(a => a.Module.Equals(CONST_MAPPING_CARRIER_MODULE, StringComparison.OrdinalIgnoreCase)).ToList();
if (mapYardList.Any(a => a.LinkId == model.CarrierId))
{
mainInfo.CarrierCode = mapYardList.FirstOrDefault(a => a.LinkId == model.CarrierId).MapCode;
}
mainInfo.BusiPKId = model.Id.ToString();
mainInfo.UserId = user.UserId;
mainInfo.UserName = user.UserName;
//mainInfo.UserEmail = user.;
var contaList = await tenantDb.Queryable()
.Where(x => long.Parse(x.BSNO) == model.Id).ToListAsync();
Logger.Log(NLog.LogLevel.Info, "批次={no} 提取箱完成 数量={total}", batchNo, contaList.Count);
if (contaList.Count > 0)
{
mainInfo.ContaList = contaList.Adapt>();
}
var msgModel = GetMessageInfo(batchNo, mainInfo);
Logger.Log(NLog.LogLevel.Info, "批次={no} 对应请求报文完成 msg={msg}", batchNo, JsonConvert.SerializeObject(msgModel));
DateTime bDate = DateTime.Now;
var compareResult = await ExcuteCompare(msgModel);
DateTime eDate = DateTime.Now;
TimeSpan ts = eDate.Subtract(bDate);
var timeDiff = ts.TotalMilliseconds;
Logger.Log(NLog.LogLevel.Info, "批次={no} 请求完成,耗时:{timeDiff}ms. 结果{msg}", batchNo, timeDiff, compareResult.succ ? "成功" : "失败");
if (compareResult == null)
throw new Exception($"订舱主键{bookingId}请求下货纸比对失败,返回为空");
var orderInfo = tenantDb.Queryable()
.First(x => x.Id == 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 pushModel = new EmbedServiceProjectStatusDto
{
businessId = bookingId,
SourceType = 1,
StatusCodes = new List {
new EmbedServiceProjectStatusDetailDto{
StatusCode = "XHZBDCHG"
}
}
};
var saveStatusRlt = await _djyServiceStatusService.SaveServiceStatus(pushModel);
Logger.Log(NLog.LogLevel.Info, "批次={no} 异步推送下货纸比对状态完成,结果={rlt}", batchNo, JsonConvert.SerializeObject(saveStatusRlt));
}
else
{
if (isBefore)
{
orderInfo.LstShipOrderCompareRlt = "BEFORE_EQUAL";
orderInfo.LstShipOrderCompareRltName = "返场前比对正常";
}
else
{
orderInfo.LstShipOrderCompareRlt = "NO_DIFF_U";
orderInfo.LstShipOrderCompareRltName = "正常未结束";
//比对成功后触发下货纸比对状态
var pushModel = new EmbedServiceProjectStatusDto
{
businessId = bookingId,
SourceType = 1,
StatusCodes = new List {
new EmbedServiceProjectStatusDetailDto{
StatusCode = "XHZBDCHG"
}
}
};
var saveStatusRlt = await _djyServiceStatusService.SaveServiceStatus(pushModel);
Logger.Log(NLog.LogLevel.Info, "批次={no} 异步推送下货纸比对状态完成,结果={rlt}", batchNo, JsonConvert.SerializeObject(saveStatusRlt));
}
}
}
}
else
{
orderInfo.LstShipOrderCompareRlt = "NO_YARD";
orderInfo.LstShipOrderCompareRltName = "无动态";
}
if (compareResult.extra != null)
orderInfo.LstShipOrderCompareId = compareResult.extra.TaskCompareId;
//更新
await tenantDb.Updateable(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.Log(NLog.LogLevel.Info, "批次={no} 请求下货纸比对返回结果{msg}", batchNo, JsonConvert.SerializeObject(compareResult));
Logger.Log(NLog.LogLevel.Info, "批次={no} 返回结果{msg}", batchNo, JsonConvert.SerializeObject(result));
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"请求下货纸比对异常,{ex.Message}";
}
return result;
}
#endregion
#region 生成请求规则报文
///
/// 生成请求规则报文
///
/// 批次号
/// 订舱主业务信息
/// 返回请求报文类
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 = AppSetting.app(new string[] { "ExcuteRuleService", "RulesEngineSender" }),
SenderName = AppSetting.app(new string[] { "ExcuteRuleService", "RulesEngineSenderName" }),
ReceiverId = "RulesEngine",
ReceiverName = "大简云规则引擎",
Version = "1.0",
RequestDate = nowDate.ToString("yyyy-MM-dd HH:mm:ss"),
RequestAction = "Compare",
};
msgModel.Main = mainInfo;
return msgModel;
}
#endregion
#region 请求下货纸比对
///
/// 请求下货纸比对
///
///
///
private async Task ExcuteCompare(TaskMessageInfoDto info)
{
TaskManageExcuteResultDto model = null;
/*
1、读取配置文件中的规则引擎URL
2、填充请求的类,并生成JSON报文
3、POST请求接口,并记录回执。
4、返回信息。
*/
try
{
//var res = await url.OnClientCreating(client => {
// // client 为 HttpClient 对象
// client.Timeout = TimeSpan.FromMinutes(15); // 设置超时时间 15分钟
//}).SetHttpMethod(HttpMethod.Post)
// .SetBody(JsonConvert.SerializeObject(info), "application/json")
// .SetContentEncoding(Encoding.UTF8)
//.PostAsync();
var jsonBody = JsonConvert.SerializeObject(info);
var res = RequestHelper.Post(jsonBody, shippingOrderCompareUrl);
Logger.Log(NLog.LogLevel.Info, string.Format("批次={0} 对应请求报文完成 res={1}", info.Head.GID, res));
if (!string.IsNullOrWhiteSpace(res))
{
//var userResult = await res.Content.ReadAsStringAsync();
model = JsonConvert.DeserializeObject(res);
}
}
catch (Exception ex)
{
//写日志
if (ex is HttpRequestException)
throw new Exception("请求失败");
}
return model;
}
#endregion
#region 请求下货纸比对结果
///
/// 请求下货纸比对结果
///
/// 订舱主键
///
///
public async Task GetShippingOrderCompareResult(long bookingId)
{
string batchNo = GuidHelper.GetSnowflakeId();
var tenantDb = saasService.GetBizDbScopeById(user.TenantId);
TaskManageExcuteResultDto result = new TaskManageExcuteResultDto();
try
{
var model = tenantDb.Queryable()
.First(a => a.Id == bookingId);
DateTime nowDate = DateTime.Now;
TaskMessageInfoDto msgModel = new TaskMessageInfoDto
{
Head = new TaskMessageHead
{
GID = batchNo,
MessageType = "DRAFT_COMPARE",
SenderId = AppSetting.app(new string[] { "ExcuteRuleService", "RulesEngineSender" }),
SenderName = AppSetting.app(new string[] { "ExcuteRuleService", "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.Log(NLog.LogLevel.Info, $"开始请求查询 msg={JsonConvert.SerializeObject(msgModel)}");
result = await GetCompareResult(msgModel);
if (result != null && result.extra != null && !string.IsNullOrWhiteSpace(result.extra.TaskCompareId))
{
var feedBack = tenantDb.Queryable().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.CreateTime,
OperUser = feedBack.CreateUserName,
Reason = (feedBack.IS_OCR_ERROR ? "识别问题 " : "") + (feedBack.IS_EDIT_ERROR ? "录入问题 " : "") + (feedBack.IS_AGENT_ERROR ? "代理录入问题 " : "")
};
}
}
}
catch (Exception ex)
{
Logger.Log(NLog.LogLevel.Info, "获取Draft比对结果异常,原因:{error}", ex.Message);
throw new Exception($"获取Draft比对结果异常,原因:{ex.Message}");
}
return result;
}
#endregion
#region 获取Draft比对结果
///
/// 获取Draft比对结果
///
/// 请求报文
/// 返回回执
private async Task GetCompareResult(TaskMessageInfoDto info)
{
TaskManageExcuteResultDto model = null;
try
{
//var res = await url.SetHttpMethod(HttpMethod.Post)
// .SetBody(JSON.Serialize(info), "application/json")
// .SetContentEncoding(Encoding.UTF8)
//.PostAsync();
var jsonBody = JsonConvert.SerializeObject(info);
var res = RequestHelper.Post(jsonBody, shippingOrderCompareResultUrl);
Logger.Log(NLog.LogLevel.Info, string.Format("批次={0} 对应请求报文完成 res={1}", info.Head.GID, res));
if (!string.IsNullOrWhiteSpace(res))
{
//var userResult = await res.Content.ReadAsStringAsync();
Logger.Log(NLog.LogLevel.Info, "对应请求报文 userResult={userResult}", res);
model = JsonConvert.DeserializeObject(res);
}
}
catch (Exception ex)
{
//写日志
if (ex is HttpRequestException)
throw new Exception("请求失败");
}
return model;
}
#endregion
public async Task ExcuteShippingOrderCompareBatchAsync([FromBody] string[] bookingIds)
{
TaskManageExcuteResultDto result = new TaskManageExcuteResultDto();
var tenantDb = saasService.GetBizDbScopeById(user.TenantId);
try
{
var ids = bookingIds.Select(a => long.Parse(a)).Distinct().ToArray();
var list = tenantDb.Queryable().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 new Exception($"以下主键信息 {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;
}
#region 下货纸自动比对回写状态
///
/// 下货纸自动比对回写状态
///
/// 比对回写详情
/// 返回回执
public async Task AutoTaskShippingOrderCompareCallBackAsync([FromBody] ShippingOrderCompareCallBackInfo model)
{
TaskManageExcuteResultDto result = new TaskManageExcuteResultDto();
var tenantDb = saasService.GetBizDbScopeById(user.TenantId);
try
{
if (string.IsNullOrWhiteSpace(model.reqBusiId))
throw new Exception($"订舱主键{model.reqBusiId}不能为空");
if (string.IsNullOrWhiteSpace(model.compareId))
throw new Exception($"比对ID{model.compareId}不能为空");
if (string.IsNullOrWhiteSpace(model.compareMode))
throw new Exception($"下货纸比对方式不能为空{model.compareMode}不能为空");
if (string.IsNullOrWhiteSpace(model.compareRltCode))
throw new Exception($"比对结果代码{model.compareRltCode}不能为空");
if (string.IsNullOrWhiteSpace(model.compareRltName))
throw new Exception($"比对结果名称{model.compareId}不能为空");
var bookingOrder = await tenantDb.Queryable()
.FirstAsync(a => a.Id == long.Parse(model.reqBusiId));
if (bookingOrder == null)
{
throw new Exception($"订舱信息检索失败,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 tenantDb.Updateable(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;
}
#endregion
#region 自动执行下货纸比对
///
/// 自动执行下货纸比对
///
/// 订舱主键
/// 返回回执
public async Task ExcuteAutoShippingOrderCompareAsync(string bookingId)
{
string batchNo = GuidHelper.GetSnowflakeId();
Logger.Log(NLog.LogLevel.Info, "批次={no}获取订舱数据请求规则 {id}", batchNo, bookingId);
/*
处理逻辑
1、台账触发单票下货纸比对
2、调取订舱的详情。
3、对应请求报文。
4、请求比对接口。
5、返回回执。
*/
return await InnerExcuteShippingOrderCompareAsync(bookingId, "AUTO");
}
#endregion
}
}