using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DS.Module.Core; using DS.Module.Core.Data; using DS.Module.Core.Helpers; using DS.Module.MQ; using DS.Module.SqlSugar; using DS.Module.UserModule; using DS.WMS.Core.Fee.Entity; using DS.WMS.Core.Flow.Dtos; using DS.WMS.Core.HangfireJob.Interface; using DS.WMS.Core.Info.Entity; using DS.WMS.Core.Op.Entity; using DS.WMS.Core.Sys.Entity; using DS.WMS.FeeBillRecvService.Dtos; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using NPOI.OpenXmlFormats.Wordprocessing; using Org.BouncyCastle.Ocsp; using RabbitMQ.Client; using RabbitMQ.Client.Events; using SqlSugar; namespace DS.WMS.FeeBillRecvService { public class RecvFeeBillWorker : BackgroundService { private readonly ILogger _logger; private IConnection mqConn; private IModel model; private readonly IServiceProvider _serviceProvider; private readonly ISqlSugarClient db; private readonly IUser user; private readonly ISaasDbService saasService; public RecvFeeBillWorker(IServiceProvider serviceProvider, ILogger logger) { _logger = logger; _serviceProvider = serviceProvider; db = _serviceProvider.GetRequiredService(); user = _serviceProvider.GetRequiredService(); saasService = _serviceProvider.GetRequiredService(); } protected override Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("启动账单 ExecuteAsync"); return Task.Run(() => { _logger.LogInformation("BookingAutoService ExecuteAsync RunTask"); //绑定队列 BindMQ(); }); } private void BindMQ() { //string ExchangeName = "billcenter.output.ds7new.444c19c1-0bf5-4709-a08b-c9859ca775e6"; //string QueueName = $"billcenter.output.ds7new.444c19c1-0bf5-4709-a08b-c9859ca775e6"; string ExchangeName = AppSetting.app(new string[] { "FeeSettings", "ExchangeName" }); string QueueName = AppSetting.app(new string[] { "FeeSettings", "QueueName" }); var mqUrl = AppSetting.app(new string[] { "FeeSettings", "MQUrl" }); ConnectionFactory factory = new ConnectionFactory(); //var repoSysCfg = _serviceScope.ServiceProvider.GetService>(); //var mqUrl = "amqp://dongsheng8bill:dongsheng8bill@47.104.207.5:12567/billcenter"; //var mqUrl = repoSysCfg.FirstOrDefault(x => x.Code == "DjyBookingAutoMQUrl")?.Value; if (string.IsNullOrEmpty(mqUrl)) { _logger.LogError($"接收订舱自动化消息推送所需MQUrl未配置"); } else { _logger.LogInformation($"准备连接订舱自动化消息队列:{mqUrl}"); factory.Uri = new Uri(mqUrl); mqConn = factory.CreateConnection("东胜8接收账单邮件解析"); model = mqConn.CreateModel(); model.ExchangeDeclare(ExchangeName, ExchangeType.Topic); model.QueueDeclare(QueueName, false, false, false, null); model.QueueBind(QueueName, ExchangeName, "*", null); var consumer = new EventingBasicConsumer(model); consumer.Received += (obj, arg) => { var body = arg.Body; var strBody = Encoding.UTF8.GetString(body.ToArray()); _logger.LogInformation($"收到订舱自动化消息队列:{strBody}"); DoWork(strBody); }; model.BasicConsume(QueueName, true, consumer); } } public override void Dispose() { base.Dispose(); //_serviceScope.Dispose(); if (mqConn != null && mqConn.IsOpen) mqConn.Close(); _logger.LogInformation("BookingAutoService Dispose"); } private void DoWork(string json) { /* 1、反串行化报文,转成对象 2、用提单号检索订单信息,如果匹配不到订单信息需要生成待办任务。 3、入库费用并写入订单的应付费用表 */ string msg = string.Empty; string gid = string.Empty; try { var model = GetInfo(json, out msg); _logger.LogInformation($"报文转换完成 GID={model.GID} BookingBill={model.BookingBill} 共【{model.DetailList.Count}】条"); //var tenantDb = saasService.GetBizJobDbScopeById(new JobDbInitConfig //{ // UserId = 1819549542463442944, // UserName = "东胜8测试", // TenantId = 1819549542425694208, // OrgId = 1819557001806614528 //}); var tenantDb = saasService.GetBizJobDbScopeExtById(new JobDbInitConfig { //UserId = long.Parse(AppSetting.app(new string[] { "FeeSettings", "UserId" })), //UserName = AppSetting.app(new string[] { "FeeSettings", "UserName" }), TenantId = long.Parse(AppSetting.app(new string[] { "FeeSettings", "TenantId" })), //OrgId = long.Parse(AppSetting.app(new string[] { "FeeSettings", "OrgId" })), }); _logger.LogInformation($"提取对应租户数据访问完成 GID={model.GID} BookingBill={model.BookingBill}"); string blno = model.BookingBill?.Trim(); gid = model.GID?.Trim(); _logger.LogInformation($"提取订单信息 GID={model.GID} BookingBill={model.BookingBill}"); var booking = tenantDb.Queryable().ClearFilter(typeof(IOrgId)).First(a => a.MBLNO == blno && a.Deleted == false && (a.IsRefund == null || a.IsRefund.Value == false) && (a.IsChangeETD == null || a.IsChangeETD.Value == false)); _logger.LogInformation($"提取订单完成 GID={model.GID} BookingBill={model.BookingBill} Order={JsonConvert.SerializeObject(booking)}"); if (booking != null && booking.IsFeeLocking.HasValue && booking.IsFeeLocking.Value) { SendCallBack(new SingleBillReceiveResult { ReceiveId = model.GID, Success = false, Reason = "订单费用已锁定" }); _logger.LogInformation($"提取订单完成 GID={model.GID} BookingBill={model.BookingBill} id={booking.Id}"); return; } if (booking != null) { var ctnList = tenantDb.Queryable().ClearFilter(typeof(IOrgId)).Where(a => a.BSNO == booking.Id.ToString() && a.Deleted == false).ToList(); DateTime etd = DateTime.MinValue; if (booking.ETD.HasValue) etd = booking.ETD.Value; DateTime nowDate = DateTime.Now; var custTypeDict = DS.Module.Core.EnumUtil.GetEnumDictionaryWithKey(typeof(CustomerTypeEnum)); var feeUnitDict = DS.Module.Core.EnumUtil.GetEnumDictionaryWithKey(typeof(FeeUnitEnum)); var codeCurrency = tenantDb.Queryable().ToList(); long tenantId = long.Parse(AppSetting.app(new string[] { "FeeSettings", "TenantId" })); var userInfo = db.Queryable().ClearFilter(typeof(ITenantId)).First(x => x.Id == booking.OperatorId && x.TenantId == tenantId); List errorMsgList = new List(); int start = 1; //需要先全部费用跟库匹配,存在不匹配的,终止整个入库 Dictionary> doResultDict = new Dictionary>(); var checkFeeArg = model.DetailList.Select(f=>f.CustSysName).Distinct().ToList(); string reason = string.Empty; bool isStop = false; bool isNoNotice = false; if (checkFeeArg.Any(x => string.IsNullOrWhiteSpace(x))) { isStop = true; reason = "费用名称不能为空"; //new EmailNoticeHelper().SendEmailNotice("") isNoNotice = true; } var queryFeeList = tenantDb.Queryable().Where(x => checkFeeArg.Contains(x.Name) && x.IsSea == true).ToList(); if(queryFeeList.Count == 0) { isStop = true; reason = $"费用名称不存在,{(string.Join(",", checkFeeArg))}"; isNoNotice = true; } else { var checkRlt = checkFeeArg.GroupJoin(queryFeeList, l => l, r => r.Name, (l, r) => { var currFee = r.FirstOrDefault(); if (currFee == null) return new { Succ = false, Obj = l }; return new { Succ = true, Obj = l }; }).ToList(); if (checkRlt.Any(b => !b.Succ)) { isStop = true; reason = "费用名称不存在," + string.Join(",", checkRlt.Where(b => !b.Succ).Select(b => b.Obj).ToArray()); isNoNotice = true; } } if (!isStop) { foreach (var fee in model.DetailList) { string qFee = fee.CustSysName?.Trim(); try { var feecode = tenantDb.Queryable().First(x => x.Name == qFee && x.IsSea == true); if (feecode == null) { } var newfee = new FeeRecord(); newfee.BusinessId = booking.Id; newfee.FeeId = feecode.Id; newfee.FeeName = feecode.Name; newfee.FeeCode = feecode.Code; newfee.TaxRate = feecode.TaxRate == null ? 0 : (decimal)feecode.TaxRate; newfee.BusinessType = BusinessType.OceanShippingExport; string custShortName = string.Empty; string custType = string.Empty; if (fee.CustSettleFor.IndexOf("#") >= 0) { var currArg = fee.CustSettleFor.Split(new char[] { '#' }); custShortName = currArg[0]?.Trim(); if (currArg.Length == 2) { custType = currArg.LastOrDefault().Trim(); } } else { custShortName = fee.CustSettleFor?.Trim(); } var customerInfo = tenantDb.Queryable().ClearFilter(typeof(ISharedOrgId)).First(x => x.ShortName == custShortName); newfee.CustomerId = customerInfo.Id; newfee.CustomerName = customerInfo.Description; if (!string.IsNullOrWhiteSpace(custType)) { var custTypeKey = custTypeDict.FirstOrDefault(x => x.Value == custType); newfee.CustomerType = custTypeKey.Key; newfee.CustomerTypeText = custTypeKey.Value; } newfee.FeeType = FeeType.Payable; //租箱月结明细.FeeType; var feeUnitKey = feeUnitDict.FirstOrDefault(x => x.Value == fee.CustFeeStandard?.Trim()); newfee.Unit = feeUnitKey.Key; newfee.UnitText = feeUnitKey.Value; var currCurrCode = codeCurrency.First(a => a.CodeName == fee.CustCurrency); newfee.Currency = currCurrCode.CodeName; decimal qty = 0; if (fee.CustFeeStandard.Equals("箱")) { var ctnSumList = ctnList.GroupBy(a => a.Ctn) .Select(a => new { Key = a.Key, Code = a.ToList().FirstOrDefault().CtnCode, Num = a.Sum(b => b.CtnNum.HasValue ? b.CtnNum.Value : 1) }).ToList(); if (ctnSumList.Count == 1) { newfee.Unit = ctnSumList.FirstOrDefault().Code; newfee.UnitText = ctnSumList.FirstOrDefault().Key; } if (!fee.Quantity.HasValue) { qty = ctnSumList.Sum(x => x.Num); } else { qty = fee.Quantity.Value; } } else if (fee.CustFeeStandard.Equals("票")) { qty = 1; } if (!fee.UnitPrice.HasValue) { if (fee.Amount.HasValue) { var price = fee.Amount.Value / qty; newfee.TaxUnitPrice = price; } else { newfee.TaxUnitPrice = 0; } } else { newfee.TaxUnitPrice = fee.UnitPrice.Value; } if (newfee.Currency.Equals("RMB", StringComparison.OrdinalIgnoreCase) || newfee.Currency.Equals("CNY", StringComparison.OrdinalIgnoreCase)) { newfee.ExchangeRate = 1; } else { if (etd != DateTime.MinValue) { var exchangeInfo = tenantDb.Queryable().ClearFilter(typeof(IOrgId)) .First(x => x.CurrencyCode == newfee.Currency && x.OrgId == booking.OrgId && x.StartDate.Value <= etd && x.EndDate.Value >= etd && x.LocalCurrency == "RMB"); if (exchangeInfo == null) { exchangeInfo = tenantDb.Queryable().ClearFilter(typeof(IOrgId)) .First(x => x.CurrencyCode == newfee.Currency && x.StartDate.Value <= etd && x.EndDate.Value >= etd && x.LocalCurrency == "RMB"); } if (exchangeInfo != null) { newfee.ExchangeRate = exchangeInfo.CRValue; } } } newfee.Quantity = qty; newfee.Amount = fee.Amount.HasValue ? fee.Amount.Value : 0; newfee.CreateTime = nowDate; newfee.CreateBy = userInfo.Id; newfee.CreateUserName = userInfo.UserName; newfee.UpdateTime = nowDate; //2024-11-08 按照董怡含要求,先不要默认审批通过,暂时关闭 //newfee.FeeStatus = FeeStatus.AuditPassed; newfee.Note = "大简云账单解析"; _logger.LogInformation($"写入账单开始 第【{start}】条 GID={model.GID} BookingBill={model.BookingBill} Order={JsonConvert.SerializeObject(newfee)}"); tenantDb.Insertable(newfee).ExecuteCommand(); _logger.LogInformation($"写入账单完成 第【{start}】条 GID={model.GID} BookingBill={model.BookingBill} Order={JsonConvert.SerializeObject(newfee)}"); //入库成功 doResultDict.Add(fee.GID, new Tuple(qFee, true)); } catch (Exception ex) { doResultDict.Add(fee.GID, new Tuple(qFee, false)); _logger.LogInformation($"明细写入产生异常,qFee={qFee} 原因:{ex.Message}"); } //callbackList.Add() start++; } //如果存在失败情况,回执失败 if (doResultDict.Count(b => !b.Value.Item2) > 0) { reason = "入库失败," + string.Join(",", doResultDict.Where(b => !b.Value.Item2).ToArray()); SendCallBack(new SingleBillReceiveResult { ReceiveId = model.GID, Success = false, Reason = reason }); } else { //全部成功,推送成功回执 SendCallBack(new SingleBillReceiveResult { ReceiveId = model.GID, Success = true, Reason = string.Empty }); } } else { if (!isNoNotice) { SendCallBack(new SingleBillReceiveResult { ReceiveId = model.GID, Success = false, Reason = reason }); } } } else { SendCallBack(new SingleBillReceiveResult { ReceiveId = model.GID, Success = false, Reason = "当前订单不存在" }); } } catch (Exception ex) { _logger.LogInformation($"产生异常,原因:{ex.Message}"); if (!string.IsNullOrWhiteSpace(gid)) { //SendCallBack(new SingleBillReceiveResult //{ // ReceiveId = gid, // Success = false, // Reason = "当前订单不存在" //}); } } } private void SendCallBack(SingleBillReceiveResult dto) { _logger.LogInformation($"开始准备发送回执MQ json={JsonConvert.SerializeObject(dto)}"); var mqRlt = PublishMessage(new MQPublishMessageReqDto { BusinessId = dto.ReceiveId, IsZip = false, json = JsonConvert.SerializeObject(dto), mqUri = AppSetting.app(new string[] { "FeeCallBacSettings", "MQUrl" }), mqExchangeName = AppSetting.app(new string[] { "FeeCallBacSettings", "ExchangeName" }), mqQueueName = AppSetting.app(new string[] { "FeeCallBacSettings", "QueueName" }) }).GetAwaiter().GetResult(); _logger.LogInformation($"发送回执MQ完成,结果 json={JsonConvert.SerializeObject(mqRlt)}"); } #region 发送MQ报文 /// /// 发送MQ报文 /// /// 请求参数 /// true-发送成功 false-发送失败 public async Task PublishMessage(MQPublishMessageReqDto request) { bool isException = false; string msg = string.Empty; try { ConnectionFactory factory = new ConnectionFactory(); factory.Uri = new Uri(request.mqUri); using (IConnection conn = factory.CreateConnection()) { try { IModel mqModel = conn.CreateModel(); mqModel.ExchangeDeclare(request.mqExchangeName, ExchangeType.Direct); //var queueName = $"{MqActionQueueName}.{(item.SubTenantId.HasValue && item.SubTenantId > 0 ? item.SubTenantId.Value : item.TenantId)}"; mqModel.QueueDeclare(request.mqQueueName, false, false, false, null); mqModel.QueueBind(request.mqQueueName, request.mqExchangeName, request.mqQueueName, null); byte[] messageBodyBytes = Encoding.UTF8.GetBytes(request.json); IBasicProperties props = mqModel.CreateBasicProperties(); props.DeliveryMode = 2; mqModel.BasicPublish(request.mqExchangeName, request.mqQueueName, props, messageBodyBytes); _logger.LogInformation($"Id:{request.BusinessId},订舱数据回推,已发送数据到消息队列【{request.mqUri}】,队列名称:【{request.mqQueueName}】"); } catch (Exception inne) { _logger.LogInformation($"Id:{request.BusinessId},推送MQ时发生异常,原因:{inne.Message}"); msg = $"Id:{request.BusinessId},推送MQ时发生异常,原因:{inne.Message}"; isException = true; } finally { conn.Close(); } } } catch (Exception ex) { _logger.LogInformation($"Id:{request.BusinessId},启动推送MQ时发生异常,原因:{ex.Message}"); msg = $"Id:{request.BusinessId},启动推送MQ时发生异常,原因:{ex.Message}"; isException = true; } if (isException) return msg; return string.Empty; } #endregion private FeeBillReadDto GetInfo(string json,out string msg) { FeeBillReadDto model = null; msg = string.Empty; try { model = JsonConvert.DeserializeObject(json); } catch(Exception ex) { msg = $"转换报文异常,原因:{ex.Message}"; } return model; } } }