You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4510 lines
199 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using DocumentFormat.OpenXml.Office2010.Excel;
using Furion;
using Furion.DependencyInjection;
using Furion.DistributedIDGenerator;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.JsonSerialization;
using Furion.RemoteRequest.Extensions;
using HtmlAgilityPack;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Myshipping.Application.ConfigOption;
using Myshipping.Application.Entity;
using Myshipping.Application.Enum;
using Myshipping.Application.Helper;
using Myshipping.Application.Service;
using Myshipping.Application.Service.BookingOrder;
using Myshipping.Application.Service.BookingOrder.Dto;
using Myshipping.Application.Service.BookingSlot.Dto;
using Myshipping.Core;
using Myshipping.Core.Entity;
using Myshipping.Core.Service;
using Newtonsoft.Json;
using Npoi.Mapper;
using NPOI.HPSF;
using NPOI.SS.Formula.Functions;
using NPOI.Util;
using NPOI.XWPF.UserModel;
using Org.BouncyCastle.Asn1.Tsp;
using Org.BouncyCastle.Utilities;
using RabbitMQ.Client;
using SqlSugar;
using StackExchange.Profiling.Internal;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using Yitter.IdGenerator;
namespace Myshipping.Application
{
/// <summary>
/// BC任务
/// </summary>
[ApiDescriptionSettings("Application", Name = "TaskManageBC", Order = 10)]
public class TaskManageBCService: ITaskManageBCService, IDynamicApiController, ITransient
{
private readonly ISysCacheService _cache;
private readonly ILogger<TaskManageBCService> _logger;
private readonly SqlSugarRepository<TaskBCInfo> _taskBCInfoRepository;
private readonly SqlSugarRepository<TaskBCCTNInfo> _taskBCCTNInfoRepository;
private readonly SqlSugarRepository<TaskBaseInfo> _taskBaseRepository;
private readonly SqlSugarRepository<TaskFileInfo> _taskFileRepository;
private readonly SqlSugarRepository<BookingOrder> _bookingOrderRepository;
private readonly SqlSugarRepository<BookingCtn> _bookingCtnRepository;
private readonly SqlSugarRepository<SysUser> _sysUserRepository;
private readonly SqlSugarRepository<BookingFile> _bookingFileRepository;
private readonly SqlSugarRepository<DjyUserMailAccount> _djyUserMailAccount;
private readonly SqlSugarRepository<BookingOrderContact> _bookingOrderContactRepository;
private readonly SqlSugarRepository<BookingSlotCompare> _bookingSlotCompareRepository;
private readonly SqlSugarRepository<BookingSlotBase> _bookingSlotBaseRepository;
private readonly SqlSugarRepository<BookingSlotCtn> _bookingSlotBaseCtnRepository;
private readonly SqlSugarRepository<BookingSlotAllocation> _bookingSlotAllocationRepository;
private readonly SqlSugarRepository<BookingSlotAllocationCtn> _bookingSlotAllocationCtnRepository;
private readonly SqlSugarRepository<BookingDeliveryRecord> _bookingDeliveryRecordRep;
private readonly SqlSugarRepository<BookingDeliveryRecordCtn> _bookingDeliveryRecordCtnRep;
private readonly IServiceWorkFlowBaseService _serviceWorkFlowBaseService;
private readonly IBookingOrderService _bookingOrderService;
private readonly IBookingSlotService _bookingSlotService;
private readonly IBookingValueAddedService _bookingValueAddedService;
private readonly IDjyCustomerService _djyCustomerService;
private readonly INamedServiceProvider<IBookingMSKAPIService> _namedBookingMSKAPIServiceProvider;
private readonly IDjyTenantParamService _djyTenantParamService;
private readonly ICommonDBService _commonDBService;
private readonly INamedServiceProvider<IBookingSlotService> _namedBookingSlotServiceProvider;
const string CONST_BC_FILE_CODE = "bc";
const string CONST_BC_FILE_NAME = "Booking Confirmation";
const string CONST_BC_NOTICE_FILE_CODE = "bc_notice";
const string CONST_BC_NOTICE_FILE_NAME = "Booking Confirmation Notice";
//租户BC默认转发KEY
const string CONST_BC_DEFAULT_PARAM = "BC_TASK_AUTO_TRANSMIT";
//租户AMENDMENT默认转发KEY
const string CONST_AMENDMENT_DEFAULT_PARAM = "AMENDMENT_TASK_AUTO_TRANSMIT";
//租户CANCELLATION默认转发KEY
const string CONST_CANCELLATION_DEFAULT_PARAM = "CANCELLATION_TASK_AUTO_TRANSMIT";
//租户AMENDMENT默认钉钉消息KEY
const string CONST_AMENDMENT_DING_DEFAULT_PARAM = "AMENDMENT_TASK_DING_NOTICE";
//租户CANCELLATION默认钉钉消息KEY
const string CONST_CANCELLATION_DING_DEFAULT_PARAM = "CANCELLATION_TASK_DING_NOTICE";
//租户默认参数管理BC任务或舱位生成订舱客户联系人必填
const string CONST_CREATE_BOOKING_NEED_CONTACT = "BC_TASK_OR_SLOT_BOOKING_NEED_CONTACT";
public TaskManageBCService(SqlSugarRepository<TaskBCInfo> taskBCInfoRepository,
SqlSugarRepository<TaskBaseInfo> taskBaseRepository,
SqlSugarRepository<TaskBCCTNInfo> taskBCCTNInfoRepository,
SqlSugarRepository<TaskFileInfo> taskFileRepository,
SqlSugarRepository<BookingOrder> bookingOrderRepository,
SqlSugarRepository<BookingCtn> bookingCtnRepository,
SqlSugarRepository<SysUser> sysUserRepository,
SqlSugarRepository<BookingFile> bookingFileRepository,
SqlSugarRepository<DjyUserMailAccount> djyUserMailAccount,
IServiceWorkFlowBaseService serviceWorkFlowBaseService,
IBookingOrderService bookingOrderService, ILogger<TaskManageBCService> logger,
IDjyCustomerService djyCustomerService,
IBookingSlotService bookingSlotService, ISysCacheService cache, IBookingValueAddedService bookingValueAddedService,
INamedServiceProvider<IBookingSlotService> namedBookingSlotServiceProvider,
INamedServiceProvider<IBookingMSKAPIService> namedBookingMSKAPIServiceProvider,
IDjyTenantParamService djyTenantParamService,
SqlSugarRepository<BookingSlotBase> bookingSlotBaseRepository,
SqlSugarRepository<BookingSlotCtn> bookingSlotBaseCtnRepository,
SqlSugarRepository<BookingSlotAllocation> bookingSlotAllocationRepository,
SqlSugarRepository<BookingSlotAllocationCtn> bookingSlotAllocationCtnRepository,
SqlSugarRepository<BookingOrderContact> bookingOrderContactRepository,
SqlSugarRepository<BookingSlotCompare> bookingSlotCompareRepository, ICommonDBService commonDBService)
{
_taskBaseRepository = taskBaseRepository;
_taskBCInfoRepository = taskBCInfoRepository;
_taskBCCTNInfoRepository = taskBCCTNInfoRepository;
_taskFileRepository = taskFileRepository;
_bookingOrderRepository = bookingOrderRepository;
_bookingCtnRepository = bookingCtnRepository;
_sysUserRepository = sysUserRepository;
_serviceWorkFlowBaseService = serviceWorkFlowBaseService;
_bookingOrderService = bookingOrderService;
_bookingSlotService = bookingSlotService;
_cache = cache;
_bookingValueAddedService = bookingValueAddedService;
_bookingFileRepository = bookingFileRepository;
_djyCustomerService = djyCustomerService;
_djyUserMailAccount = djyUserMailAccount;
_bookingOrderContactRepository = bookingOrderContactRepository;
_bookingSlotCompareRepository = bookingSlotCompareRepository;
_bookingSlotBaseRepository = bookingSlotBaseRepository;
_bookingSlotBaseCtnRepository = bookingSlotBaseCtnRepository;
_bookingSlotAllocationRepository = bookingSlotAllocationRepository;
_bookingSlotAllocationCtnRepository = bookingSlotAllocationCtnRepository;
_namedBookingMSKAPIServiceProvider = namedBookingMSKAPIServiceProvider;
_namedBookingSlotServiceProvider = namedBookingSlotServiceProvider;
_djyTenantParamService = djyTenantParamService;
_commonDBService = commonDBService;
_logger = logger;
}
#region 获取BC详情
/// <summary>
/// 获取BC详情
/// </summary>
/// <param name="pkId">BC主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/GetInfo")]
public async Task<TaskManageOrderResultDto> GetInfo(string pkId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
var bcOrder = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == pkId);
if (bcOrder == null)
throw Oops.Oh($"BC主键{pkId}无法获取业务信息");
var BCCtnList = _taskBCCTNInfoRepository.AsQueryable().Where(a => a.P_ID == pkId).ToList();
TaskBCShowBaseDto model = bcOrder.Adapt<TaskBCShowBaseDto>();
if (BCCtnList.Count > 0)
model.CtnList = BCCtnList.Adapt<List<TaskBCCTNInfoDto>>();
var fileList = _taskFileRepository.AsQueryable().Where(a => a.TASK_PKID == bcOrder.TASK_ID).ToList();
if (fileList.Count > 0)
model.FileList = fileList.Adapt<List<TaskFileDto>>();
result.succ = true;
result.ext = model;
//如果当前BC有对应记录则读取订舱详情
if (bcOrder.BOOKING_ORDER_ID.HasValue)
{
var bkOrder = await _bookingOrderRepository.AsQueryable().
FirstAsync(a => a.Id == bcOrder.BOOKING_ORDER_ID.Value);
if (bkOrder != null)
{
var showBKOrder = bkOrder.Adapt<BookingOrderOutput>();
var ctnList = await _bookingCtnRepository.AsQueryable().
Where(a => a.BILLID == bkOrder.Id).ToListAsync();
if (ctnList.Count > 0)
showBKOrder.ctnInputs = ctnList.Adapt<List<BookingCtnDto>>();
result.ext2 = showBKOrder;
}
}
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"获取BC详情异常原因{ex.Message}";
}
return result;
}
#endregion
#region 通过任务主键获取BC详情
/// <summary>
/// 通过任务主键获取BC详情
/// </summary>
/// <param name="taskPkId">BC任务主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/GetInfoByTaskId")]
public async Task<TaskManageOrderResultDto> GetInfoByTaskId(string taskPkId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
var taskBase = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskPkId);
if (taskBase == null)
throw Oops.Oh($"任务主键{taskPkId}无法获取业务信息");
var bcOrder = _taskBCInfoRepository.AsQueryable().First(a => a.TASK_ID == taskBase.PK_ID);
if (bcOrder == null)
throw Oops.Oh($"任务主键{taskPkId}无法获取BC业务信息");
var bcCtnList = _taskBCCTNInfoRepository.AsQueryable().Where(a => a.P_ID == bcOrder.PK_ID).ToList();
TaskBCShowBaseDto model = bcOrder.Adapt<TaskBCShowBaseDto>();
if (bcCtnList.Count > 0)
model.CtnList = bcCtnList.Adapt<List<TaskBCCTNInfoDto>>();
var fileList = _taskFileRepository.AsQueryable().Where(a => a.TASK_PKID == bcOrder.TASK_ID).ToList();
if (fileList.Count > 0)
model.FileList = fileList.Adapt<List<TaskFileDto>>();
model.taskStatus = taskBase.STATUS;
//生成关键信息
#region 生成关键信息
model.Keywords = new List<TaskBCShowBaseKeywordDto>();
if(bcOrder.CARRIAGE_TYPE == "DIRECT_SHIP")
{
model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"承运方式:{bcOrder.CARRIAGE_TYPE_NAME}", Background = "#FFFF80",Icon= "icon-yunshu1" });
}
else if (bcOrder.CARRIAGE_TYPE == "TRANSFER_SHIP")
{
model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"承运方式:{bcOrder.CARRIAGE_TYPE_NAME}", Background = "#CAF982", Icon = "icon-shuaxin" });
}
if (bcOrder.BOOKING_SLOT_TYPE == "CONTRACT_ORDER")
{
model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"订舱方式:{bcOrder.BOOKING_SLOT_TYPE_NAME}", Background = "#81D3F8", Icon = "icon-touzijilu" });
}
else if (bcOrder.BOOKING_SLOT_TYPE == "SPOT_ORDER")
{
model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"订舱方式:{bcOrder.BOOKING_SLOT_TYPE_NAME}", Background = "#FACD91", Icon = "icon-beizhu1" });
}
#endregion
result.succ = true;
result.ext = model;
//如果当前BC有对应记录则读取订舱详情
if (bcOrder.BOOKING_ORDER_ID.HasValue)
{
var bkOrder = await _bookingOrderRepository.AsQueryable().
FirstAsync(a => a.Id == bcOrder.BOOKING_ORDER_ID.Value);
if(bkOrder != null)
{
var showBKOrder = bkOrder.Adapt<BookingOrderOutput>();
var ctnList = await _bookingCtnRepository.AsQueryable().
Where(a => a.BILLID == bkOrder.Id).ToListAsync();
if (ctnList.Count > 0)
showBKOrder.ctnInputs = ctnList.Adapt<List<BookingCtnDto>>();
result.ext2 = showBKOrder;
}
}
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"获取BC详情异常原因{ex.Message}";
}
return result;
}
#endregion
#region 获取待处理的BC任务
/// <summary>
/// 获取待处理的BC任务来自邮件解析需要对应订舱系统会根据用户的订舱台账预配
/// </summary>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/GetToDoBCList")]
public async Task<TaskManageOrderResultDto> GetToDoBCList()
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
/*
1、优先匹配提单号一致的
2、判断船名航次一致的
*/
try
{
Dictionary<string, List<BookingOrder>> toDoListDict = new Dictionary<string, List<BookingOrder>>();
//获取所有待处理的BC任务
var taskList = await _taskBCInfoRepository.AsQueryable().InnerJoin<TaskBaseInfo>((a,b)=>a.TASK_ID == b.PK_ID)
.Where((a, b)=> !a.BOOKING_ORDER_ID.HasValue && b.STATUS == TaskStatusEnum.Create.ToString() && b.TASK_BASE_TYPE == TaskBaseTypeEnum.BC.ToString())
.Select((a,b)=>new { BC = a,TSK = b }).ToListAsync();
if (taskList.Count > 0)
{
taskList.ForEach(async tsk =>
{
var curList = await _bookingOrderRepository.AsQueryable()
.Where(a => a.VESSEL.Contains(tsk.BC.VESSEL) && a.VOYNO.Contains(tsk.BC.VOYNO) || a.MBLNO.Contains(tsk.BC.MBL_NO)
).ToListAsync();
if (curList.Count > 0)
{
toDoListDict.Add(tsk.BC.PK_ID, curList);
}
else
{
toDoListDict.Add(tsk.BC.PK_ID, new List<BookingOrder>());
}
});
}
//这里最后清洗一下对应的订舱数据,只保留一条符合的数据
if (toDoListDict.Count > 0)
{
List<Tuple<TaskBCInfoDto, BookingOrderBCTaskDto>> tupList = new List<Tuple<TaskBCInfoDto, BookingOrderBCTaskDto>>();
int num = 1;
int odNum = 1;
foreach (var kvp in toDoListDict)
{
var bcInfo = taskList.FirstOrDefault(a => a.BC.PK_ID == kvp.Key).BC.Adapt<TaskBCInfoDto>();
bcInfo.Indx = num;
if (kvp.Value.Count > 0)
{
var bookingOrder = kvp.Value.Select(a =>
{
if (a.MBLNO.Equals(bcInfo.MBLNo, StringComparison.OrdinalIgnoreCase))
{
return new { Sort = 90, OBJ = a };
}
else if (a.VESSEL.Equals(bcInfo.Vessel, StringComparison.OrdinalIgnoreCase)
&& a.VOYNO.Equals(bcInfo.VoyNo, StringComparison.OrdinalIgnoreCase))
{
return new { Sort = 80, OBJ = a };
}
return new { Sort = 1, OBJ = a };
}).OrderByDescending(a => a.Sort).FirstOrDefault().OBJ.Adapt<BookingOrderBCTaskDto>();
bookingOrder.Indx = odNum;
bookingOrder.BCIndx = num;
bcInfo.BKOrderIndx = odNum;
odNum++;
tupList.Add(new Tuple<TaskBCInfoDto, BookingOrderBCTaskDto>(
bcInfo,
bookingOrder
));
}
else
{
tupList.Add(new Tuple<TaskBCInfoDto, BookingOrderBCTaskDto>(
bcInfo,
null
));
}
num++;
}
result.ext = tupList.Select(a=>a.Item1).ToList();
result.ext2 = tupList.Select(a => a.Item2).ToList();
}
result.succ = true;
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"获取派车详情异常,原因:{ex.Message}";
}
return result;
}
#endregion
#region 任务ID下载附件
/// <summary>
/// 任务ID下载附件
/// </summary>
/// <param name="taskPKId">BC任务主键</param>
/// <param name="fileCategory">附件分类代码</param>
/// <returns>返回数据流</returns>
[HttpGet("/TaskManageBC/DownloadFile")]
public async Task<IActionResult> DownloadFile([FromQuery] string taskPKId, [FromQuery] string fileCategory = "BC")
{
var bcTaskInfo = await _taskBaseRepository.AsQueryable().FirstAsync(u => u.PK_ID == taskPKId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}无法获取业务信息");
}
TaskFileCategoryEnum fileCategoryEnum = TaskFileCategoryEnum.NONE;
System.Enum.TryParse(fileCategory, out fileCategoryEnum);
if (fileCategoryEnum == TaskFileCategoryEnum.NONE)
{
throw Oops.Oh($"附件分类代码错误,请提供正确的分类代码");
}
string name = fileCategoryEnum.ToString();
var fileInfo = await _taskFileRepository.AsQueryable().FirstAsync(u => u.TASK_PKID == taskPKId && u.FILE_CATEGORY == name);
if (fileInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}没有可下载的附件");
}
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
var fileFullPath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
if (!File.Exists(fileFullPath))
{
throw Oops.Oh($"任务主键{taskPKId} 附件下载请求失败,请确认文件是否存在");
}
var fileName = HttpUtility.UrlEncode(fileInfo.FILE_NAME, Encoding.GetEncoding("UTF-8"));
var result = new FileStreamResult(new FileStream(fileFullPath, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
return result;
}
#endregion
#region 检索订舱信息
/// <summary>
/// 检索订舱信息
/// </summary>
/// <param name="query">检索条件</param>
/// <returns>返回回执</returns>
[HttpPost("/TaskManageBC/QueryBookingOrderList")]
public async Task<TaskManageOrderResultDto> QueryBookingOrderList([FromBody] BookingOrderBCQuery query)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
bool isAvailable = false;
var queryWhere = _bookingOrderRepository.AsQueryable();
#region 查询条件
if (query.beginETD.HasValue || query.endETD.HasValue)
{
/*
起始结束时间间隔不能超过7天
*/
DateTime beginDate = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
DateTime endDate = beginDate;
if (query.beginETD.HasValue)
beginDate = query.beginETD.Value;
if (query.endETD.HasValue)
endDate = query.endETD.Value;
if (endDate > beginDate.AddDays(7))
throw Oops.Oh($"船期的日期范围不能超过7天");
endDate = endDate.AddDays(1);
queryWhere = queryWhere.Where(a => a.ETD >= beginDate && a.ETD < endDate);
isAvailable = true;
}
if (query.beginCreated.HasValue || query.endCreated.HasValue)
{
/*
起始结束时间间隔不能超过7天
*/
DateTime beginDate = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
DateTime endDate = beginDate;
if (query.beginCreated.HasValue)
beginDate = query.beginCreated.Value;
if (query.endCreated.HasValue)
endDate = query.endCreated.Value;
if (endDate > beginDate.AddDays(7))
throw Oops.Oh($"制单的日期范围不能超过7天");
endDate = endDate.AddDays(1);
queryWhere = queryWhere.Where(a => a.CreatedTime >= beginDate && a.CreatedTime < endDate);
isAvailable = true;
}
if (!string.IsNullOrWhiteSpace(query.mblNo))
{
queryWhere = queryWhere.Where(a => a.MBLNO.Contains(query.mblNo));
isAvailable = true;
}
if (!string.IsNullOrWhiteSpace(query.custNo))
{
queryWhere = queryWhere.Where(a => a.CUSTNO.Contains(query.custNo));
isAvailable = true;
}
if (!string.IsNullOrWhiteSpace(query.vessel))
{
queryWhere = queryWhere.Where(a => a.VESSEL.Contains(query.vessel));
isAvailable = true;
}
if (!string.IsNullOrWhiteSpace(query.voyno))
{
queryWhere = queryWhere.Where(a => a.VOYNO.Contains(query.voyno));
isAvailable = true;
}
#endregion
if (!isAvailable)
throw Oops.Oh($"查询条件不能为空");
var list = await queryWhere.OrderBy(a => a.CreatedTime)
.Take(query.topNum).ToListAsync();
var bkList = list.Adapt<List<BookingOrderBCTaskDto>>();
result.succ = true;
result.ext = bkList;
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"查询失败,原因:{ex.Message}";
}
return result;
}
#endregion
#region 转移任务(将任务指定给其他人)
/// <summary>
/// 转移任务(将任务指定给其他人)
/// </summary>
/// <param name="taskPKId">BC任务主键,若多条以逗号分隔</param>
/// <param name="userId">用户ID</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/TransferTask")]
public async Task<TaskManageOrderResultDto> TransferTask([FromQuery] string taskPKId, [FromQuery] long userId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
//判断用户信息有效性
if (userId < 0)
throw Oops.Oh($"指定用户ID不能为空");
var targetUserId = _sysUserRepository.AsQueryable().First(u => u.Id == userId);
if (targetUserId == null)
throw Oops.Oh($"指定用户不存在");
string[] strings = taskPKId.Split(',');
foreach (var item in strings)
{
var bcTaskInfo = await _taskBaseRepository.AsQueryable().FirstAsync(u => u.PK_ID == item);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{item}无法获取业务信息");
}
if (bcTaskInfo.IS_PUBLIC == 1)
{
bcTaskInfo.IS_PUBLIC = 0;
bcTaskInfo.RealUserId = targetUserId.Id;
bcTaskInfo.RealUserName = targetUserId.Name;
bcTaskInfo.UpdatedTime = DateTime.Now;
bcTaskInfo.UpdatedUserId = targetUserId.Id;
bcTaskInfo.UpdatedUserName = targetUserId.Name;
await _taskBaseRepository.AsUpdateable(bcTaskInfo).IgnoreColumns(it => new
{
it.TenantId,
it.CreatedTime,
it.IsDeleted,
it.TASK_NO,
it.TASK_TYPE,
it.TASK_SOURCE
}).ExecuteCommandAsync();
}
else
{
bcTaskInfo.RealUserId = targetUserId.Id;
bcTaskInfo.RealUserName = targetUserId.Name;
bcTaskInfo.UpdatedTime = DateTime.Now;
bcTaskInfo.UpdatedUserId = targetUserId.Id;
bcTaskInfo.UpdatedUserName = targetUserId.Name;
await _taskBaseRepository.AsUpdateable(bcTaskInfo).IgnoreColumns(it => new
{
it.TenantId,
it.CreatedTime,
it.IsDeleted,
it.TASK_NO,
it.TASK_TYPE,
it.TASK_SOURCE
}).ExecuteCommandAsync();
}
}
result.succ = true;
result.msg = "成功";
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"转移任务失败,原因:{ex.Message}";
}
return result;
}
#endregion
#region 生成订舱或舱位
/// <summary>
/// 生成订舱或舱位
/// </summary>
/// <param name="model">生成订舱或者舱位请求</param>
/// <returns>返回回执</returns>
[HttpPost("/TaskManageBC/CreateBookingAndSlot")]
public async Task<TaskManageOrderResultDto> CreateBookingAndSlot([FromBody] BookingOrSlotGenerateDto model)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
_logger.LogInformation($"接收生成订舱或舱位请求,参数{JSON.Serialize(model)}");
try
{
/*
1、GEN_BOOKING_SLOT-生成舱位和订舱
1生成舱位
2生成海运订舱
3更新舱位库存
4更新BC任务
2、GEN_BOOKING-只生成订舱
1生成海运订舱
4更新BC任务
3、GEN_SLOT-只生成舱位
1生成舱位
4更新BC任务
3、GEN_SLOT-只生成舱位
1查找订舱记录
3更新舱位库存
4更新BC任务
*/
if (string.IsNullOrWhiteSpace(model.BCTaskId))
throw Oops.Oh($"BC任务主键不能为空");
//生成方式GEN_BOOKING_SLOT-生成舱位和订舱;GEN_BOOKING-只生成订舱GEN_SLOT-只生成舱位GEN_EXIST_BOOKING-匹配指定的订舱)
if (string.IsNullOrWhiteSpace(model.GenerateMethod))
throw Oops.Oh($"生成方式不能为空,需要指定一种生成方式");
var bcTaskInfo = await _taskBaseRepository.AsQueryable().FirstAsync(u => u.PK_ID == model.BCTaskId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{model.BCTaskId}无法获取业务信息");
}
var bcOrder = _taskBCInfoRepository.AsQueryable().First(a => a.TASK_ID == bcTaskInfo.PK_ID);
if (bcOrder == null)
throw Oops.Oh($"任务主键{model.BCTaskId}无法获取BC业务信息");
var bcCtnList = _taskBCCTNInfoRepository.AsQueryable().Where(a => a.P_ID == bcOrder.PK_ID).ToList();
var fileList = _taskFileRepository.AsQueryable().Where(a => a.TASK_PKID == bcTaskInfo.PK_ID).ToList();
if (model.GenerateMethod != "UPD_BOOKING")
{
if (bcOrder.BOOKING_ORDER_ID.HasValue && bcOrder.BOOKING_ORDER_ID.Value > 0)
{
throw Oops.Oh($"当前BC任务已生成订舱订单不能重复生成");
}
}
if (model.GenerateMethod == "GEN_BOOKING_SLOT")
{
#region 推送舱位、推送订舱
//推送舱位
long bookingSlotId = 0;
if (bcOrder.BOOKING_SLOT_ID.HasValue && bcOrder.BOOKING_SLOT_ID.Value > 0)
{
//bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value;
throw Oops.Oh($"生成舱位失败,舱位已存在");
}
//这里增加委托单位联系人的校验,重新从后台拉取了委托单位相关联系人,如果比对不一致跳出异常终止执行
ValidateContact(model);
//触发生成舱位
bookingSlotId = await GenerateBookingSlot(bcOrder, bcCtnList, fileList);
_logger.LogInformation($"生成舱位完成bookingSlotId={bookingSlotId} taskid={bcOrder.TASK_ID}");
if(bookingSlotId < 1)
throw Oops.Oh($"生成舱位失败");
//推送订舱订单
var bookingOrderId = await GenerateBookingOrder(bcOrder, bcCtnList, fileList, model);
_logger.LogInformation($"生成订舱订单完成bookingOrderId={bookingOrderId} taskid={bcOrder.TASK_ID}");
if (bookingOrderId < 1)
throw Oops.Oh($"生成订舱订单失败");
List<BookingSlotBaseWithCtnDto> slots = new List<BookingSlotBaseWithCtnDto>();
//检索舱位信息
var slotInfo = _bookingSlotService.Detail(bookingSlotId).GetAwaiter().GetResult();
BookingSlotBaseWithCtnDto baseInfo = slotInfo.Adapt<BookingSlotBaseWithCtnDto>();
baseInfo.Id = bookingSlotId;
baseInfo.CtnList = slotInfo.CtnList.Adapt<List<BookingSlotCtnDto>>();
slots.Add(baseInfo);
//对应订舱和舱位关系
var allocRlt = await _bookingSlotService.ImportSlots(slots, bookingOrderId, false);
_logger.LogInformation($"生成订舱和舱位关系完成allocRlt={JSON.Serialize(allocRlt)} taskid={bcOrder.TASK_ID}");
if (!allocRlt.isSuccess)
{
throw Oops.Oh($"生成订舱和舱位关系失败");
}
var bcEntity = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == bcOrder.PK_ID);
if (bcEntity == null)
{
throw Oops.Oh($"未获取有效任务BC失败更新失败");
}
bcEntity.BOOKING_ORDER_ID = bookingOrderId;
bcEntity.BOOKING_SLOT_ID = bookingSlotId;
bcEntity.UpdatedTime = DateTime.Now;
bcEntity.UpdatedUserId = UserManager.UserId;
bcEntity.UpdatedUserName = UserManager.Name;
//更新任务BC
await _taskBCInfoRepository.AsUpdateable(bcEntity).UpdateColumns(it => new
{
it.BOOKING_ORDER_ID,
it.BOOKING_SLOT_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
var taskEntity = _taskBaseRepository.AsQueryable().First(u => u.PK_ID == bcEntity.TASK_ID);
if (taskEntity == null)
{
throw Oops.Oh($"未获取有效任务记录,更新失败");
}
#region 更新任务
//如果是公共任务,需要变成个人任务 RealUserId = 当前操作人
if (taskEntity.IS_PUBLIC == 1)
{
taskEntity.IS_PUBLIC = 0;
taskEntity.RealUserId = UserManager.UserId;
taskEntity.RealUserName = UserManager.Name;
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
taskEntity.IS_COMPLETE = 1;
taskEntity.COMPLETE_DATE = DateTime.Now;
taskEntity.COMPLETE_DEAL = "MANUAL";
taskEntity.COMPLETE_DEAL = "手工";
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.IS_PUBLIC,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName,
it.RealUserId,
it.RealUserName,
it.IS_COMPLETE,
it.COMPLETE_DATE,
it.COMPLETE_DEAL,
it.COMPLETE_DEAL_NAME
}).ExecuteCommandAsync();
}
else
{
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
taskEntity.IS_COMPLETE = 1;
taskEntity.COMPLETE_DATE = DateTime.Now;
taskEntity.COMPLETE_DEAL = "MANUAL";
taskEntity.COMPLETE_DEAL = "手工";
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName,
it.IS_COMPLETE,
it.COMPLETE_DATE,
it.COMPLETE_DEAL,
it.COMPLETE_DEAL_NAME
}).ExecuteCommandAsync();
}
#endregion
var currBCOrder = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == bcEntity.PK_ID);
if (currBCOrder != null && model.IsDirectSend)
{
//异步推送邮件
var mailRlt = await GenerateSendEmail(currBCOrder, bcTaskInfo,null, model.usePersonalEmailSend);
if (!mailRlt.succ)
{
throw Oops.Oh($"邮件推送失败,原因:{mailRlt.msg},可以任务编辑页面重新发送邮件");
}
}
#endregion
}
else if (model.GenerateMethod == "GEN_BOOKING")
{
#region 推送订舱
long bookingId = 0;
long bookingSlotId = 0;
if (bcOrder.BOOKING_ORDER_ID.HasValue && bcOrder.BOOKING_ORDER_ID.Value > 0)
{
//bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value;
throw Oops.Oh($"生成订舱订单失败,订舱订单已存在");
}
if (!bcOrder.BOOKING_SLOT_ID.HasValue || bcOrder.BOOKING_SLOT_ID.Value < 1)
{
//bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value;
throw Oops.Oh($"生成订舱订单失败,舱位未生成不能直接生成订舱订单");
}
bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value;
//这里增加委托单位联系人的校验,重新从后台拉取了委托单位相关联系人,如果比对不一致跳出异常终止执行
ValidateContact(model);
//推送订舱订单
var bookingOrderId = await GenerateBookingOrder(bcOrder, bcCtnList, fileList, model);
List<BookingSlotBaseWithCtnDto> slots = new List<BookingSlotBaseWithCtnDto>();
//检索舱位信息
var slotInfo = _bookingSlotService.Detail(bookingSlotId).GetAwaiter().GetResult();
BookingSlotBaseWithCtnDto baseInfo = slotInfo.Adapt<BookingSlotBaseWithCtnDto>();
baseInfo.Id = bookingSlotId;
baseInfo.CtnList = slotInfo.CtnList.Adapt<List<BookingSlotCtnDto>>();
slots.Add(baseInfo);
//对应订舱和舱位关系
var allocRlt = await _bookingSlotService.ImportSlots(slots, bookingOrderId, false);
_logger.LogInformation($"生成订舱和舱位关系完成allocRlt={JSON.Serialize(allocRlt)} taskid={bcOrder.TASK_ID}");
if (!allocRlt.isSuccess)
{
throw Oops.Oh($"生成订舱和舱位关系失败");
}
var bcEntity = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == bcOrder.PK_ID);
if (bcEntity == null)
{
throw Oops.Oh($"未获取有效任务BC失败更新失败");
}
bcEntity.BOOKING_ORDER_ID = bookingOrderId;
bcEntity.BOOKING_SLOT_ID = bookingSlotId;
bcEntity.UpdatedTime = DateTime.Now;
bcEntity.UpdatedUserId = UserManager.UserId;
bcEntity.UpdatedUserName = UserManager.Name;
//更新任务BC
await _taskBCInfoRepository.AsUpdateable(bcEntity).UpdateColumns(it => new
{
it.BOOKING_ORDER_ID,
it.BOOKING_SLOT_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
var taskEntity = _taskBaseRepository.AsQueryable().First(u => u.PK_ID == bcEntity.TASK_ID);
if (taskEntity == null)
{
throw Oops.Oh($"未获取有效任务记录,更新失败");
}
#region 更新任务
//如果是公共任务,需要变成个人任务 RealUserId = 当前操作人
if (taskEntity.IS_PUBLIC == 1)
{
taskEntity.IS_PUBLIC = 0;
taskEntity.RealUserId = UserManager.UserId;
taskEntity.RealUserName = UserManager.Name;
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
taskEntity.IS_COMPLETE = 1;
taskEntity.COMPLETE_DATE = DateTime.Now;
taskEntity.COMPLETE_DEAL = "MANUAL";
taskEntity.COMPLETE_DEAL = "手工";
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.IS_PUBLIC,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName,
it.RealUserId,
it.RealUserName,
it.IS_COMPLETE,
it.COMPLETE_DATE,
it.COMPLETE_DEAL,
it.COMPLETE_DEAL_NAME
}).ExecuteCommandAsync();
}
else
{
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
taskEntity.IS_COMPLETE = 1;
taskEntity.COMPLETE_DATE = DateTime.Now;
taskEntity.COMPLETE_DEAL = "MANUAL";
taskEntity.COMPLETE_DEAL = "手工";
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName,
it.IS_COMPLETE,
it.COMPLETE_DATE,
it.COMPLETE_DEAL,
it.COMPLETE_DEAL_NAME
}).ExecuteCommandAsync();
}
#endregion
var currBCOrder = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == bcEntity.PK_ID);
if (currBCOrder != null && model.IsDirectSend)
{
//异步推送邮件
var mailRlt = await GenerateSendEmail(currBCOrder, bcTaskInfo,null, model.usePersonalEmailSend);
if (!mailRlt.succ)
{
throw Oops.Oh($"邮件推送失败,原因:{mailRlt.msg},可以任务编辑页面重新发送邮件");
}
}
#endregion
}
else if (model.GenerateMethod == "GEN_SLOT")
{
long bookingSlotId = 0;
if (bcOrder.BOOKING_SLOT_ID.HasValue && bcOrder.BOOKING_SLOT_ID.Value > 0)
{
//bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value;
throw Oops.Oh($"生成舱位失败,舱位已存在");
}
//触发生成舱位
bookingSlotId = await GenerateBookingSlot(bcOrder, bcCtnList, fileList);
_logger.LogInformation($"生成舱位完成bookingSlotId={bookingSlotId} taskid={bcOrder.TASK_ID}");
if (bookingSlotId < 1)
throw Oops.Oh($"生成舱位失败");
var bcEntity = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == bcOrder.PK_ID);
if (bcEntity == null)
{
throw Oops.Oh($"未获取有效任务BC失败更新失败");
}
bcEntity.BOOKING_SLOT_ID = bookingSlotId;
bcEntity.UpdatedTime = DateTime.Now;
bcEntity.UpdatedUserId = UserManager.UserId;
bcEntity.UpdatedUserName = UserManager.Name;
//更新任务BC
await _taskBCInfoRepository.AsUpdateable(bcEntity).UpdateColumns(it => new
{
it.BOOKING_SLOT_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
var taskEntity = _taskBaseRepository.AsQueryable().First(u => u.PK_ID == bcEntity.TASK_ID);
if (taskEntity == null)
{
throw Oops.Oh($"未获取有效任务记录,更新失败");
}
#region 更新任务
//如果是公共任务,需要变成个人任务 RealUserId = 当前操作人
if (taskEntity.IS_PUBLIC == 1)
{
taskEntity.IS_PUBLIC = 0;
taskEntity.RealUserId = UserManager.UserId;
taskEntity.RealUserName = UserManager.Name;
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.IS_PUBLIC,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName,
it.RealUserId,
it.RealUserName
}).ExecuteCommandAsync();
}
else
{
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
#endregion
}
else if (model.GenerateMethod == "UPD_BOOKING")
{
//这里增加委托单位联系人的校验,重新从后台拉取了委托单位相关联系人,如果比对不一致跳出异常终止执行
ValidateContact(model);
//推送订舱订单
var bookingOrderId = await UpdateBookingOrder(bcOrder, bcCtnList, fileList, model);
var taskEntity = _taskBaseRepository.AsQueryable().First(u => u.PK_ID == bcOrder.TASK_ID);
if (taskEntity == null)
{
throw Oops.Oh($"未获取有效任务记录,更新失败");
}
#region 更新任务
//如果是公共任务,需要变成个人任务 RealUserId = 当前操作人
if (taskEntity.IS_PUBLIC == 1)
{
taskEntity.IS_PUBLIC = 0;
taskEntity.RealUserId = UserManager.UserId;
taskEntity.RealUserName = UserManager.Name;
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
taskEntity.IS_COMPLETE = 1;
taskEntity.COMPLETE_DATE = DateTime.Now;
taskEntity.COMPLETE_DEAL = "MANUAL";
taskEntity.COMPLETE_DEAL = "手工";
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.IS_PUBLIC,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName,
it.RealUserId,
it.RealUserName,
it.IS_COMPLETE,
it.COMPLETE_DATE,
it.COMPLETE_DEAL,
it.COMPLETE_DEAL_NAME
}).ExecuteCommandAsync();
}
else
{
taskEntity.UpdatedTime = DateTime.Now;
taskEntity.UpdatedUserId = UserManager.UserId;
taskEntity.UpdatedUserName = UserManager.Name;
taskEntity.IS_COMPLETE = 1;
taskEntity.COMPLETE_DATE = DateTime.Now;
taskEntity.COMPLETE_DEAL = "MANUAL";
taskEntity.COMPLETE_DEAL = "手工";
await _taskBaseRepository.AsUpdateable(taskEntity).UpdateColumns(it => new
{
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName,
it.IS_COMPLETE,
it.COMPLETE_DATE,
it.COMPLETE_DEAL,
it.COMPLETE_DEAL_NAME
}).ExecuteCommandAsync();
}
#endregion
var currBCOrder = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == bcOrder.PK_ID);
if (currBCOrder != null && model.IsDirectSend)
{
//异步推送邮件
var mailRlt = await GenerateSendEmail(currBCOrder, bcTaskInfo, null, model.usePersonalEmailSend);
if (!mailRlt.succ)
{
throw Oops.Oh($"邮件推送失败,原因:{mailRlt.msg},可以任务编辑页面重新发送邮件");
}
}
}
result.succ = true;
result.msg = "成功";
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"生成订舱或舱位失败,原因:{ex.Message}";
}
return result;
}
#endregion
#region 生成舱位
/// <summary>
/// 生成舱位
/// </summary>
/// <param name="taskBCInfo">BC任务详情</param>
/// <param name="taskBCCtnList">BC任务集装箱列表</param>
/// <param name="taskFileList">BC任务附件列表</param>
/// <returns>返回舱位ID</returns>
private async Task<long> GenerateBookingSlot(TaskBCInfo taskBCInfo, List<TaskBCCTNInfo> taskBCCtnList, List<TaskFileInfo> taskFileList,string opType = "add")
{
long id = 0;
try
{
var carrierInfo = _cache.GetAllCodeCarrier().GetAwaiter().GetResult()
.Where(t => t.Code.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)
|| t.EnName.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)
|| t.CnName.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
BookingSlotBaseApiDto slotModel = new BookingSlotBaseApiDto
{
DataObj = new BookingSlotBaseApiSaveDto
{
CARRIERID = taskBCInfo.CARRIERID,
CARRIER = carrierInfo.CnName?.Trim(),
SLOT_BOOKING_NO = taskBCInfo.MBL_NO,
BOOKING_PARTY = taskBCInfo.BOOKING_PARTY,
BOOKING_SLOT_TYPE = taskBCInfo.BOOKING_SLOT_TYPE,
BOOKING_SLOT_TYPE_NAME = taskBCInfo.BOOKING_SLOT_TYPE_NAME,
VESSEL = taskBCInfo.VESSEL,
VOYNO = taskBCInfo.VOYNO,
VGM_SUBMISSION_CUT_DATE = taskBCInfo.VGM_CUTOFF_TIME,
//WEEK_AT = taskBCInfo.WEEK_AT,
CARRIAGE_TYPE = taskBCInfo.CARRIAGE_TYPE,
CARRIAGE_TYPE_NAME = taskBCInfo.CARRIAGE_TYPE_NAME,
CONTRACT_NO = taskBCInfo.CONTRACTNO,
CTN_STAT = taskBCInfo.CTN_STAT,
CY_CUT_DATE = taskBCInfo.CY_CUTOFF_TIME,
DETENSION_FREE_DAYS = taskBCInfo.DETENSION_FREE_DAYS,
ETD = taskBCInfo.ETD,
ETA = taskBCInfo.ETA,
LANECODE = taskBCInfo.LANECODE,
LANENAME = taskBCInfo.LANENAME,
MANIFEST_CUT_DATE = taskBCInfo.MANIFEST_CUT_DATE,
MDGF_CUT_DATE = taskBCInfo.MDGF_CUT_DATE,
PLACEDELIVERY = taskBCInfo.PLACEDELIVERY,
PLACERECEIPT = taskBCInfo.PLACERECEIPT,
PORTDISCHARGE = taskBCInfo.PORTDISCHARGE,
PORTLOAD = taskBCInfo.PORTLOAD,
SI_CUT_DATE = taskBCInfo.SI_CUT_DATE,
TRANSFER_PORT_1 = taskBCInfo.TRANSFER_PORT_1,
TRANSFER_PORT_2 = taskBCInfo.TRANSFER_PORT_2,
CtnList = new List<BookingSlotCtnSaveInput>()
},
OpType = opType
};
if (int.TryParse(taskBCInfo.WEEK_AT, out int _weekat))
{
slotModel.DataObj.WEEK_AT = _weekat;
}
var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList();
if (taskBCCtnList.Count > 0)
{
taskBCCtnList.ForEach(t =>
{
var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) &&
a.Name.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase));
BookingSlotCtnSaveInput ctn = new BookingSlotCtnSaveInput
{
CTNCODE = ctnCode?.Code,
CTNALL = t.CTNALL,
CTNNUM = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1
};
slotModel.DataObj.CtnList.Add(ctn);
});
}
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
DynameFileInfo dynameFile = null;
DynameFileInfo dynameNoticeFile = null;
if (taskFileList.Any(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString()))
{
var fileInfo = taskFileList.FirstOrDefault(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString());
var fileFullPath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
dynameFile = new DynameFileInfo
{
FileBytes = File.ReadAllBytes(fileFullPath),
FileName = Path.GetFileName(fileFullPath)
};
}
if (taskFileList.Any(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC_NOTICE.ToString()))
{
var fileInfo = taskFileList.FirstOrDefault(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString());
var fileFullPath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
dynameNoticeFile = new DynameFileInfo
{
FileBytes = File.ReadAllBytes(fileFullPath),
FileName = Path.GetFileName(fileFullPath)
};
}
id = await _bookingSlotService.InnerApiReceive(slotModel, dynameFile, dynameNoticeFile);
}
catch (Exception ex)
{
_logger.LogError($"任务BC MBLNO:{taskBCInfo.MBL_NO} 生成舱位异常,原因:{ex.Message}");
throw Oops.Oh($"MBLNO:{taskBCInfo.MBL_NO} 生成舱位异常,原因:{ex.Message}");
}
return id;
}
#endregion
#region 生成订舱
/// <summary>
/// 生成订舱
/// </summary>
/// <param name="taskBCInfo">BC任务详情</param>
/// <param name="taskBCCtnList">BC任务集装箱列表</param>
/// <param name="taskFileList">BC任务附件列表</param>
/// <param name="generateModel">订舱请求详情</param>
/// <returns>返回订舱ID</returns>
private async Task<long> GenerateBookingOrder(TaskBCInfo taskBCInfo, List<TaskBCCTNInfo> taskBCCtnList, List<TaskFileInfo> taskFileList,
BookingOrSlotGenerateDto generateModel)
{
long id = 0;
try
{
/*
1、新增订舱
2、推送服务项目
3、推送附件
*/
var carrierInfo = _cache.GetAllCodeCarrier().GetAwaiter().GetResult()
.Where(t => t.Code.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)
|| t.EnName.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)
|| t.CnName.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
SaveBookingOrderInput bkModel = new SaveBookingOrderInput
{
CUSTOMERID = generateModel.CustomerId,
CUSTOMERNAME = generateModel.CustomerName,
CARRIERID = carrierInfo.Code?.Trim(),
CARRIER = carrierInfo.CnName?.Trim(),
MBLNO = taskBCInfo.MBL_NO.ToUpper().Trim(),
CONTRACTNO = !string.IsNullOrWhiteSpace(taskBCInfo.CONTRACTNO) ? taskBCInfo.CONTRACTNO : "",
VESSEL = taskBCInfo.VESSEL?.ToUpper().Trim(),
VOYNO = taskBCInfo.VOYNO?.ToUpper().Trim(),
VOYNOINNER = taskBCInfo.VOYNO?.ToUpper().Trim(),
ETD = taskBCInfo.ETD,
ETA = taskBCInfo.ETA,
SALEID = generateModel.SaleId.ToString(),
SALE = generateModel.SaleName,
OPID = generateModel.OpId.ToString(),
OP = generateModel.OpName,
DOCID = generateModel.DocId.ToString(),
DOC = generateModel.DocName,
ROUTEID = generateModel.RouteID.ToString(),
ROUTE = generateModel.Route,
CZRemark = generateModel.CZRemark,
ShenQingXiangShi = generateModel.ShenQingXiangShi,
LineManageID = generateModel.LineManageID.ToString(),
LineName = generateModel.LineManage,
CLOSEVGMDATE = taskBCInfo.VGM_CUTOFF_TIME,
CLOSINGDATE = taskBCInfo.CY_CUTOFF_TIME,
CLOSEDOCDATE = taskBCInfo.CUT_SINGLE_TIME,
CUSTSERVICEID = generateModel.CustServiceId.HasValue? generateModel.CustServiceId.Value.ToString(): null,
CUSTSERVICE = generateModel.CustServiceName,
ShippingMethod = "整箱",
ctnInputs = new List<BookingCtnDto>()
};
if(taskBCInfo.CARRIERID.Equals("CMA",StringComparison.OrdinalIgnoreCase))
{
if(!string.IsNullOrWhiteSpace(taskBCInfo.CARRIER_VOYNO))
{
bkModel.VOYNOINNER = taskBCInfo.CARRIER_VOYNO;
}
}
//处理自动对应场站
if (!string.IsNullOrWhiteSpace(taskBCInfo.TAKE_CTN_YARD))
{
var yardInfo = _cache.GetAllCodeYard().GetAwaiter().GetResult().FirstOrDefault(a => a.Code.Equals(taskBCInfo.TAKE_CTN_YARD));
if (yardInfo != null)
{
bkModel.YARDID = yardInfo.Code;
bkModel.YARD = yardInfo.Name;
}
}
//增加装货港、卸货港的对应
// 解析收货地,得到装货港名称及五字码
if (!string.IsNullOrWhiteSpace(taskBCInfo.PLACERECEIPT))
{
var portEnName = taskBCInfo.PLACERECEIPT.Split(',')[0]?.Trim();
//这里CMA的收货地全称放在了括号里面
if (taskBCInfo.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase))
{
if (!string.IsNullOrWhiteSpace(taskBCInfo.PLACERECEIPT))
taskBCInfo.PLACERECEIPT = taskBCInfo.PLACERECEIPT.Replace("", "(").Replace("", ")");
if (taskBCInfo.PLACERECEIPT.IndexOf("(") >= 0)
{
string currStr = Regex.Match(taskBCInfo.PLACERECEIPT, "(?<=\\().*(?=\\))").Value?.Trim();
portEnName = currStr.Split(',')[0]?.Trim();
}
}
if (!string.IsNullOrWhiteSpace(portEnName))
{
var cachePortLoad = await _cache.GetAllCodePortLoad();
var portInfo = await PlaceToPortHelper.PlaceReceiptToPortload(portEnName, cachePortLoad, () => _cache.GetAllMappingPortLoad());
if (portInfo == null)
{
_logger.LogInformation("通过收货地城市名称未匹配到港口信息,订舱编号:{MBL_NO}", taskBCInfo.MBL_NO);
}
else
{
bkModel.PORTLOAD = portInfo.EnName;
bkModel.PORTLOADID = portInfo.EdiCode;
bkModel.PLACERECEIPT = portInfo.EnName;
bkModel.PLACERECEIPTID = portInfo.EdiCode;
}
}
else
{
_logger.LogInformation("收货地分割后得到的城市名称为空,订舱编号:{MBL_NO}", taskBCInfo.MBL_NO);
}
}
else
{
_logger.LogInformation("收货地为空,订舱编号:{MBL_NO}", taskBCInfo.MBL_NO);
}
// 解析交货地,得到为卸货港名称及五字码, 以及国家信息
if (!string.IsNullOrWhiteSpace(taskBCInfo.PLACEDELIVERY))
{
var portEnName = taskBCInfo.PLACEDELIVERY.Split(',')[0]?.Trim();
//这里CMA的收货地全称放在了括号里面
if (taskBCInfo.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase))
{
if (!string.IsNullOrWhiteSpace(taskBCInfo.PLACEDELIVERY))
taskBCInfo.PLACEDELIVERY = taskBCInfo.PLACEDELIVERY.Replace("", "(").Replace("", ")");
if (taskBCInfo.PLACEDELIVERY.IndexOf("(") >= 0)
{
string currStr = Regex.Match(taskBCInfo.PLACEDELIVERY, "(?<=\\().*(?=\\))").Value?.Trim();
portEnName = currStr.Split(',')[0]?.Trim();
}
}
if (!string.IsNullOrWhiteSpace(portEnName))
{
var cachePort = await _cache.GetAllCodePort();
var portInfo = await PlaceToPortHelper.PlaceDeliveryToPort(portEnName, cachePort, () => _cache.GetAllMappingPort());
if (portInfo == null)
{
_logger.LogInformation("通过交货地城市名称未匹配到港口信息,订舱编号:{MBL_NO}", taskBCInfo.MBL_NO);
}
else
{
bkModel.PORTDISCHARGE = portInfo.EnName;
bkModel.PORTDISCHARGEID = portInfo.EdiCode;
bkModel.PLACEDELIVERY = portInfo.EnName;
bkModel.PLACEDELIVERYID = portInfo.EdiCode;
}
}
else
{
_logger.LogInformation("交货地分割后得到的城市名称为空,订舱编号:{MBL_NO}", taskBCInfo.MBL_NO);
}
}
else
{
_logger.LogInformation("交货地为空,订舱编号:{MBL_NO}", taskBCInfo.MBL_NO);
}
var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList();
if (taskBCCtnList.Count > 0)
{
taskBCCtnList.ForEach(t =>
{
var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) &&
a.Name.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase));
BookingCtnDto ctn = new BookingCtnDto
{
CTNCODE = ctnCode?.Code,
CTNALL = t.CTNALL,
CTNNUM = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1
};
bkModel.ctnInputs.Add(ctn);
});
}
bkModel.BookingSlotType = taskBCInfo.BOOKING_SLOT_TYPE;
bkModel.BookingSlotTypeName = taskBCInfo.BOOKING_SLOT_TYPE_NAME;
var bkRlt = await _bookingOrderService.Save(bkModel);
id = bkRlt.Id;
string batchNo = IDGen.NextID().ToString();
if (id > 0)
{
//这里如果指定了委托单位的邮件联系人,则推送订舱联系人
if (generateModel.CustomerContactList != null && generateModel.CustomerContactList.Count > 0)
{
var bookingContactList = _bookingOrderContactRepository.AsQueryable()
.Where(a => a.BookingId == id && a.IsDeleted == false).ToList();
var djyCustomerInfo = _djyCustomerService.Detail(new GetDjyCustomerInput { Id = generateModel.CustomerId.Value })
.GetAwaiter().GetResult();
Dictionary<long, Tuple<string, string>> finalContactDict = new Dictionary<long, Tuple<string, string>>();
generateModel.CustomerContactList.ForEach(contact =>
{
DjyCustomerContactOutput djyCustomerContactMan = null;
if (djyCustomerInfo.Contacts != null && djyCustomerInfo.Contacts.Count > 0)
{
djyCustomerContactMan = djyCustomerInfo.Contacts.FirstOrDefault(a =>
a.Id == contact.CustomerContactId);
}
finalContactDict.Add(contact.CustomerContactId, new Tuple<string, string>(djyCustomerContactMan?.Email, contact.CustomerContactName));
if (djyCustomerContactMan != null)
{
var bookingContact = bookingContactList
.FirstOrDefault(x => x.Email.Equals(djyCustomerContactMan.Email, StringComparison.OrdinalIgnoreCase));
if (bookingContact == null)
{
bookingContact = new BookingOrderContact
{
Name = djyCustomerContactMan.Name,
BookingId = id,
Email = djyCustomerContactMan.Email,
Remark = djyCustomerContactMan.Remark,
CreatedTime = DateTime.Now,
CreatedUserId = UserManager.UserId,
CreatedUserName = UserManager.Name
};
_bookingOrderContactRepository.Insert(bookingContact);
}
else
{
bookingContact.Name = djyCustomerContactMan.Name;
bookingContact.Email = djyCustomerContactMan.Email;
bookingContact.Remark = djyCustomerContactMan.Remark;
bookingContact.UpdatedTime = DateTime.Now;
bookingContact.UpdatedUserId = UserManager.UserId;
bookingContact.UpdatedUserName = UserManager.Name;
_bookingOrderContactRepository.AsUpdateable(bookingContact).UpdateColumns(it => new
{
it.Name,
it.Email,
it.Remark,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommand();
}
}
});
//这里对于入库后的联系人再做一下比对,如果有差异需要邮件推送消息
var checkbookingContactList = _bookingOrderContactRepository.AsQueryable()
.Where(a => a.BookingId == id && a.IsDeleted == false).ToList();
if (finalContactDict.Any(x => string.IsNullOrWhiteSpace(x.Value.Item1)))
{
//推送邮件提醒
new EmailNoticeHelper().SendEmailNotice($"MBLNO={taskBCInfo.MBL_NO} 生成订舱的联系人失败"
, $"MBLNO={taskBCInfo.MBL_NO} 生成订舱的联系人失败,有联系人没有关联到邮箱信息 {string.Join(",",finalContactDict.Where(x => string.IsNullOrWhiteSpace(x.Value.Item1)).ToArray())}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
}
var checkRlt = generateModel.CustomerContactList.GroupJoin(finalContactDict, l => l.CustomerContactId, r => r.Key,
(l, r) =>
{
var currArg = r.ToList();
if(currArg.Count > 0)
{
var currModel = currArg.FirstOrDefault();
if (string.IsNullOrWhiteSpace(currModel.Value.Item1))
{
return new { UName = currModel.Value.Item2, IsNoEmail = true };
}
return new { UName = currModel.Value.Item2, IsNoEmail = false };
}
return new { UName = l.CustomerContactName, IsNoEmail = true };
}).ToList();
if (checkRlt.Any(t => t.IsNoEmail))
{
//推送邮件提醒
new EmailNoticeHelper().SendEmailNotice($"MBLNO={taskBCInfo.MBL_NO} 生成订舱的联系人失败"
, $"MBLNO={taskBCInfo.MBL_NO} 生成订舱的联系人失败,有联系人没有关联到邮箱信息 {string.Join(",", checkRlt.Where(x => x.IsNoEmail).Select(x => x.UName).ToArray())}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
}
}
if (generateModel.ProjectList != null && generateModel.ProjectList.Count > 0)
{
ModifyServiceProjectDto projectDto = new ModifyServiceProjectDto
{
BookingId = id,
ProjectCodes = generateModel.ProjectList.Distinct().ToArray(),
};
//写入服务项目
var prjRlt = await _bookingValueAddedService.SaveServiceProject(projectDto);
_logger.LogInformation($"推送订舱的服务项目完成 id={id} rlt={JSON.Serialize(prjRlt)}");
}
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
taskFileList.ForEach(file =>
{
if (file.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString())
{
var fileFullPath = Path.Combine(dirAbs, file.FILE_PATH);
if (File.Exists(fileFullPath))
{
//如果确认文件读取成功
var bookFilePath = FileAttachHelper.MoveFile(id.ToString(), fileFullPath, batchNo
, false, null, true).GetAwaiter().GetResult();
//将格式单附件写入订舱的附件
SaveEDIFile(id, bookFilePath, new System.IO.FileInfo(bookFilePath).Name, taskBCInfo.TenantId.Value,
CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter();
}
}
else if (file.FILE_CATEGORY == TaskFileCategoryEnum.BC_NOTICE.ToString())
{
var fileFullPath = Path.Combine(dirAbs, file.FILE_PATH);
if (File.Exists(fileFullPath))
{
//如果确认文件读取成功
var bookFilePath = FileAttachHelper.MoveFile(id.ToString(), fileFullPath, batchNo
, false, "bcnoticefile",true).GetAwaiter().GetResult();
//将格式单附件写入订舱的附件
SaveEDIFile(id, bookFilePath, new System.IO.FileInfo(bookFilePath).Name, taskBCInfo.TenantId.Value,
CONST_BC_NOTICE_FILE_CODE, CONST_BC_NOTICE_FILE_NAME).GetAwaiter();
}
}
});
}
_logger.LogInformation($"任务BC MBLNO:{taskBCInfo.MBL_NO} 生成订舱成功 id={id}");
}
catch(Exception ex)
{
_logger.LogError($"任务BC MBLNO:{taskBCInfo.MBL_NO} 生成订舱异常,原因:{ex.Message}");
}
return id;
}
#endregion
#region 取消任务
/// <summary>
/// 取消任务
/// </summary>
/// <param name="taskPKId">BC任务主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/CancelTask")]
public async Task<TaskManageOrderResultDto> CancelTask([FromQuery] string taskPKId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
var bcTaskInfo = await _taskBaseRepository.AsQueryable().FirstAsync(u => u.PK_ID == taskPKId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}无法获取业务信息");
}
bcTaskInfo.IsDeleted = true;
bcTaskInfo.UpdatedTime = DateTime.Now;
bcTaskInfo.UpdatedUserId = UserManager.UserId;
bcTaskInfo.UpdatedUserName = UserManager.Name;
await _taskBaseRepository.AsUpdateable(bcTaskInfo).IgnoreColumns(it => new
{
it.TenantId,
it.CreatedTime,
it.CreatedUserId,
it.CreatedUserName,
it.IsDeleted,
it.TASK_NO,
it.TASK_TYPE,
it.TASK_SOURCE
}).ExecuteCommandAsync();
result.succ = true;
result.msg = "成功";
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"取消任务失败,原因:{ex.Message}";
}
return result;
}
#endregion
#region 获取服务项目列表
/// <summary>
/// 获取服务项目列表
/// </summary>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/GetProjectList")]
public async Task<List<ServiceProjectBaseDto>> GetProjectList()
{
List<ServiceProjectBaseDto> list = new List<ServiceProjectBaseDto>();
return await _serviceWorkFlowBaseService.GetEnableProjectList(UserManager.TENANT_ID.ToString());
}
#endregion
#region 异步写入附件表
/// <summary>
/// 异步写入附件表
/// </summary>
/// <param name="boookId">订舱ID</param>
/// <param name="FilePath">文件路径</param>
/// <param name="fileName">文件名</param>
/// <param name="tenantId">租户ID</param>
/// <param name="fileTypeCode">附件类型代码</param>
/// <param name="fileTypeName">附件类型名称</param>
/// <param name="moudle">附件模块代码</param>
/// <returns></returns>
[NonAction]
private async Task SaveEDIFile(long boookId, string FilePath, string fileName, long tenantId,
string fileTypeCode = "bc", string fileTypeName = "Booking Confirmation",string moudle = "BookingSlot")
{
/*
直接将附件信息写入附件表
*/
//EDI文件
var bookFile = new BookingFile
{
Id = YitIdHelper.NextId(),
FileName = fileName,
FilePath = FilePath,
TypeCode = fileTypeCode,
TypeName = fileTypeName,
BookingId = boookId,
TenantId = tenantId,
Moudle = moudle
};
await _bookingFileRepository.InsertAsync(bookFile);
}
#endregion
#region 生成并推送邮件
/// <summary>
/// 生成并推送邮件
/// </summary>
/// <param name="taskBCInfo">BC任务详情</param>
/// <param name="bcTaskInfo">主任务详情</param>
/// <param name="usePersonalEmailSend">是否默认使用用户个人邮箱发送</param>
/// <returns>返回回执</returns>
private async Task<TaskManageOrderResultDto> GenerateSendEmail(TaskBCInfo taskBCInfo, TaskBaseInfo bcTaskInfo, Nullable<long> mandatoryBookingId = null, bool usePersonalEmailSend = false)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
/*
1、提取邮件接收人、通过订舱的委托客户获取联系人信息提取联系人中备注是BCNotice的邮箱
2、提取当票订舱对应的操作人邮箱、通过订舱的委托客户获取操作OP的邮箱
3、读取用户邮箱配置主要提取显示名称BCNotice的邮箱用来作为公共邮箱来发送邮件。
4、读取邮件模板填充详情。
5、推送邮件给邮件接收人
*/
//读取订舱数据
BookingOrder bookingOrderEntity = null;
if(mandatoryBookingId.HasValue && mandatoryBookingId.Value> 0)
{
bookingOrderEntity = _bookingOrderRepository.AsQueryable().Filter(null, true)
.First(a => a.Id == mandatoryBookingId.Value);
}
else
{
bookingOrderEntity = _bookingOrderRepository.AsQueryable().Filter(null, true)
.First(a => a.Id == taskBCInfo.BOOKING_ORDER_ID);
}
var ctnList = _bookingCtnRepository.AsQueryable().Filter(null, true)
.Where(a => a.BILLID == mandatoryBookingId.Value).ToList();
if (bookingOrderEntity == null)
{
var checkInfo =_bookingOrderRepository.AsQueryable().Filter(null, true)
.First(a => a.MBLNO == taskBCInfo.MBL_NO && a.IsDeleted == false && (a.ParentId == null || a.ParentId == 0));
if(checkInfo != null)
{
throw Oops.Oh($"订舱详情获取失败用提单号能检索到但是没有ID关系需要人工看看问题");
}
else
{
throw Oops.Oh($"订舱详情获取失败,请确认订舱是否存在或已删除");
}
}
_logger.LogInformation($"获取订舱详情完成bookid={bookingOrderEntity.Id}");
if (!bookingOrderEntity.CUSTOMERID.HasValue || (bookingOrderEntity.CUSTOMERID.HasValue && bookingOrderEntity.CUSTOMERID.Value == 0))
{
throw Oops.Oh($"订舱的委托客户不能为空");
}
var djyCustomerInfo = _djyCustomerService.Detail(new GetDjyCustomerInput { Id = bookingOrderEntity.CUSTOMERID.Value })
.GetAwaiter().GetResult();
if (djyCustomerInfo == null)
{
throw Oops.Oh($"委托单位详情获取失败,请确认委托单位是否存在或已删除");
}
_logger.LogInformation($"获取委托单位详情完成djyCustomerId={djyCustomerInfo.Id}");
//DjyCustomerContactOutput djyCustomerContactMan = null;
//TO 邮件接收人
string toEmail = string.Empty;
//订舱OP的邮箱
string opEmail = string.Empty;
var bookingContactList = _bookingOrderContactRepository.AsQueryable().Filter(null, true)
.Where(a => a.BookingId == bookingOrderEntity.Id && a.IsDeleted == false).ToList();
if (bookingContactList == null || bookingContactList.Count == 0)
{
_logger.LogInformation($"当前订舱未指定的联系人toEmail={toEmail}");
}
toEmail = string.Join(";", bookingContactList.Select(x => x.Email.Trim()).Distinct().ToArray());
//获取操作OP的邮箱
if (!string.IsNullOrWhiteSpace(bookingOrderEntity.OPID))
{
var opId = long.Parse(bookingOrderEntity.OPID);
var opUser = _sysUserRepository.AsQueryable().Filter(null, true).First(a => a.Id == opId);
if (opUser != null && !string.IsNullOrWhiteSpace(opUser.Email))
{
opEmail = opUser.Email.Trim();
_logger.LogInformation($"获取操作OP的邮箱opEmail={opEmail} id={opId} name={opUser.Name}");
}
}
//提取当前公共邮箱的配置
DjyUserMailAccount publicMailAccount = null;
if(usePersonalEmailSend)
{
publicMailAccount = _djyUserMailAccount.AsQueryable().Filter(null, true).First(x => x.CreatedUserId == UserManager.UserId
&& x.SmtpPort > 0 && x.SmtpServer != null && x.SmtpServer != "");
}
else
{
//这个是公共邮箱配置
publicMailAccount = _djyUserMailAccount.AsQueryable().Filter(null, true).First(x => x.TenantId == UserManager.TENANT_ID && x.ShowName == "PublicSend"
&& x.SmtpPort > 0 && x.SmtpServer != null && x.SmtpServer != "");
}
if (publicMailAccount == null)
{
throw Oops.Oh($"提取公共邮箱配置失败请在用户邮箱账号管理增加配置显示名为PublicSend或者配置个人邮箱");
}
_logger.LogInformation($"提取当前公共邮箱的配置完成id={publicMailAccount.Id}");
string emailTitle = $"Booking Confirmation : {bookingOrderEntity.MBLNO}";
string filePath = string.Empty;
SysUser opUserInfo = null;
if (!string.IsNullOrWhiteSpace(bookingOrderEntity.OPID) && Regex.IsMatch(bookingOrderEntity.OPID, "[0-9]+"))
opUserInfo = _sysUserRepository.AsQueryable().Filter(null, true).First(u => u.Id == long.Parse(bookingOrderEntity.OPID));
if (taskBCInfo.BUSI_TYPE == "BookingAmendment")
{
emailTitle = $"【变更】Booking Amendment : {bookingOrderEntity.MBLNO}";
}
//读取邮件模板并填充数据
string emailHtml = string.Empty;
if (taskBCInfo.BUSI_TYPE == "BookingAmendment")
{
if (mandatoryBookingId.HasValue)
{
emailHtml = GenerateSendEmailHtmlAmendment(taskBCInfo, opUserInfo, UserManager.TENANT_NAME, bookingOrderEntity, ctnList).GetAwaiter().GetResult();
}
else
{
emailHtml = GenerateSendEmailHtmlAmendment(taskBCInfo, opUserInfo, UserManager.TENANT_NAME).GetAwaiter().GetResult();
}
}
else
{
emailHtml = GenerateSendEmailHtml(taskBCInfo, opUserInfo, UserManager.TENANT_NAME).GetAwaiter().GetResult();
}
_logger.LogInformation($"生成邮件BODY结果{emailHtml}");
TaskFileInfo fileInfo = null;
if (bcTaskInfo.TASK_BASE_TYPE == TaskBaseTypeEnum.BC.ToString())
{
fileInfo = _taskFileRepository.AsQueryable().Filter(null, true).Where(a => a.TASK_PKID == taskBCInfo.TASK_ID && a.FILE_CATEGORY.Contains("BC_NOTICE"))
.OrderByDescending(a => a.CreatedTime).First();
}
else if (bcTaskInfo.TASK_BASE_TYPE == TaskBaseTypeEnum.BC_MODIFY.ToString())
{
//CMA没有变更附件所以转发邮件时默认用原文件转发
if (bcTaskInfo.CARRIER_ID.Equals("CMA", StringComparison.OrdinalIgnoreCase))
{
fileInfo = _taskFileRepository.AsQueryable().Filter(null, true).Where(a => a.TASK_PKID == taskBCInfo.TASK_ID && a.FILE_CATEGORY.Contains("BC_MODIFY"))
.OrderByDescending(a => a.CreatedTime).First();
}
else
{
fileInfo = _taskFileRepository.AsQueryable().Filter(null, true).Where(a => a.TASK_PKID == taskBCInfo.TASK_ID && a.FILE_CATEGORY.Contains("BC_MODIFY_NOTICE"))
.OrderByDescending(a => a.CreatedTime).First();
}
}
if (fileInfo == null)
{
throw Oops.Oh($"提取变更文件失败,不能发送邮件");
}
_logger.LogInformation($"获取订舱附件地址,结果:{fileInfo.FILE_PATH}");
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
filePath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
if (mandatoryBookingId.HasValue)
{
//重新取BC文件
fileInfo = _taskFileRepository.AsQueryable().Filter(null, true).Where(a => a.TASK_PKID == taskBCInfo.TASK_ID && a.FILE_CATEGORY.Contains("BC_MODIFY"))
.OrderByDescending(a => a.CreatedTime).First();
filePath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
var newFilePath = await ReGenerateBAFile(bookingOrderEntity, ctnList, filePath);
if (!string.IsNullOrWhiteSpace(newFilePath))
filePath = Path.Combine(dirAbs, newFilePath);
SaveEDIFile(bookingOrderEntity.Id, filePath, new System.IO.FileInfo(filePath).Name, taskBCInfo.TenantId.Value,
"splitfile", "Booking Confirmation Split").GetAwaiter();
}
EmailApiUserDefinedDto emailApiUserDefinedDto = new EmailApiUserDefinedDto
{
SendTo = toEmail,
//CCTo = opEmail,
Title = emailTitle,
Body = emailHtml,
Account = publicMailAccount.MailAccount?.Trim(),
Password = publicMailAccount.Password?.Trim(),
Server = publicMailAccount.SmtpServer?.Trim(),
Port = publicMailAccount.SmtpPort.HasValue ? publicMailAccount.SmtpPort.Value : 465,
UseSSL = publicMailAccount.SmtpSSL.HasValue ? publicMailAccount.SmtpSSL.Value : true,
Attaches = new List<AttachesInfo>()
};
//如果配置了租户参数AUTO_TRANS_EMAIL_OP_CCTO-自动转发是否默认抄送操作=ENABLE发送邮件时自动抄送操作
DjyTenantParamValueOutput paramConfig = _djyTenantParamService.GetParaCodeWithValue(new[] { "AUTO_TRANS_EMAIL_OP_CCTO" }).GetAwaiter().GetResult().FirstOrDefault();
if (paramConfig != null && paramConfig.ParaValue.Equals("ENABLE", StringComparison.OrdinalIgnoreCase))
{
emailApiUserDefinedDto.CCTo = opEmail;
}
_logger.LogInformation($"生成请求邮件参数,结果:{JSON.Serialize(emailApiUserDefinedDto)}");
//推送邮件
var emailRlt = await PushEmail(emailApiUserDefinedDto, filePath);
_logger.LogInformation($"推送邮件完成,结果:{JSON.Serialize(emailRlt)}");
//var taskBcInfo = _taskBCInfoRepository.AsQueryable().Filter(null, true)
//.First(a => a.PK_ID == taskBCInfo.PK_ID);
if (emailRlt.succ)
{
result.succ = true;
result.msg = "成功";
}
else
{
result.succ = false;
result.msg = emailRlt.msg;
new EmailNoticeHelper().SendEmailNotice($"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败", $"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败,原因:{emailRlt.msg}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
}
}
catch (Exception ex)
{
_logger.LogInformation($"推送邮件失败,异常:{ex.Message}");
result.succ = false;
result.msg = $"推送邮件失败,{ex.Message}";
new EmailNoticeHelper().SendEmailNotice($"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败", $"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败,原因:{ex.Message}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
}
return result;
}
#endregion
#region 通过邮件模板生成HTML
/// <summary>
/// 通过邮件模板生成HTML
/// </summary>
/// <param name="taskBCInfo">BC任务详情</param>
/// <param name="opUserInfo">订舱OP详情</param>
/// <param name="tenantName">当前租户全称</param>
/// <returns>返回生成的HTML</returns>
public async Task<string> GenerateSendEmailHtml(TaskBCInfo taskBCInfo, SysUser opUserInfo, string tenantName)
{
string result = string.Empty;
/*
1、加载模板文件读取HTML
2、读取main、conta的tr行替换业务数据
3、返回HTML的文本信息。
*/
try
{
string templatePath = App.Configuration["EmailTemplateFilePath"];
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
templatePath = $"{dirAbs}{templatePath}\\BCEmailTemplate.html";
string baseHtml = File.ReadAllText(templatePath);
if (string.IsNullOrWhiteSpace(baseHtml))
throw Oops.Oh($"读取邮件模板失败");
if(opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Name))
{
baseHtml = baseHtml.Replace("#opname#", opUserInfo.Name);
}
else
{
baseHtml = baseHtml.Replace("#opname#", "操作");
}
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Email))
{
baseHtml = baseHtml.Replace("#opemail#", opUserInfo.Email);
}
else
{
baseHtml = baseHtml.Replace("#opemail#", "");
}
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Phone))
{
baseHtml = baseHtml.Replace("#optel#", opUserInfo.Phone);
}
else if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Tel))
{
baseHtml = baseHtml.Replace("#optel#", opUserInfo.Tel);
}
else
{
baseHtml = baseHtml.Replace("#optel#", "");
}
HtmlDocument html = new HtmlDocument();
html.LoadHtml(baseHtml);
HtmlNode baseTable = html.DocumentNode.SelectNodes("//table[@class='base-table']").FirstOrDefault();
if (baseTable == null)
throw Oops.Oh($"读取邮件模板格式错误定位base-table失败");
var baseTrList = baseTable.SelectNodes(".//tr");
foreach (var baseTr in baseTrList)
{
var tdList = baseTr.SelectNodes(".//td");
foreach (var baseTd in tdList)
{
if (baseTd.Attributes["class"].Value == "billno-val")
{
baseTd.InnerHtml = taskBCInfo.MBL_NO;
}
else if (baseTd.Attributes["class"].Value == "takebillno-val")
{
baseTd.InnerHtml = taskBCInfo.MBL_NO;
}
else if (baseTd.Attributes["class"].Value == "pol-val")
{
baseTd.InnerHtml = taskBCInfo.PLACERECEIPT;
}
else if (baseTd.Attributes["class"].Value == "pod-val")
{
baseTd.InnerHtml = taskBCInfo.PLACEDELIVERY;
}
else if (baseTd.Attributes["class"].Value == "ctn-val")
{
baseTd.InnerHtml = taskBCInfo.CTN_STAT;
}
else if (baseTd.Attributes["class"].Value == "etd-val")
{
if (taskBCInfo.ETD.HasValue)
{
baseTd.InnerHtml = taskBCInfo.ETD.Value.ToString("yyyy-MM-dd");
}
}
else if (baseTd.Attributes["class"].Value == "eta-val")
{
if (taskBCInfo.ETA.HasValue)
{
baseTd.InnerHtml = taskBCInfo.ETA.Value.ToString("yyyy-MM-dd");
}
}
}
}
var noreplyTr = html.DocumentNode.SelectNodes("//tr[@class='email-noreply']").FirstOrDefault();
if (noreplyTr != null)
{
var currTd = noreplyTr.SelectNodes(".//td").FirstOrDefault();
if(currTd != null)
{
var currPList = currTd.SelectNodes(".//p");
foreach (var baseP in currPList)
{
if (baseP.Attributes["class"].Value == "notice-comp-val")
{
baseP.InnerHtml = tenantName;
}
}
}
}
result = html.DocumentNode.OuterHtml;
}
catch (Exception ex)
{
_logger.LogInformation($"通过邮件模板生成HTML异常原因={ex.Message}");
throw ex;
}
return result;
}
#endregion
#region 通过邮件模板生成HTML
/// <summary>
/// 通过邮件模板生成HTML
/// </summary>
/// <param name="taskBCInfo">BC任务详情</param>
/// <param name="opUserInfo">订舱OP详情</param>
/// <param name="tenantName">当前租户全称</param>
/// <param name="bookingOrder">相关订单详情</param>
/// <param name="ctnList">相关订单的集装箱列表</param>
/// <returns>返回生成的HTML</returns>
public async Task<string> GenerateSendEmailHtmlAmendment(TaskBCInfo taskBCInfo, SysUser opUserInfo, string tenantName,
BookingOrder bookingOrder = null,List<BookingCtn> ctnList = null)
{
string result = string.Empty;
/*
1、加载模板文件读取HTML
2、读取main、conta的tr行替换业务数据
3、返回HTML的文本信息。
*/
try
{
string templatePath = App.Configuration["EmailTemplateFilePath"];
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
templatePath = $"{dirAbs}{templatePath}\\BCModifyEmailTemplate.html";
string baseHtml = File.ReadAllText(templatePath);
if (string.IsNullOrWhiteSpace(baseHtml))
throw Oops.Oh($"读取邮件模板失败");
List<CompareResultDetailInfo> compareList = GetCompareResult(taskBCInfo.TASK_ID).GetAwaiter().GetResult();
if (taskBCInfo.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase))
{
compareList = new List<CompareResultDetailInfo>();
}
else
{
if (compareList == null || compareList.Count == 0)
throw Oops.Oh($"读取变更数据失败,没有差异数据");
}
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Name))
{
baseHtml = baseHtml.Replace("#opname#", opUserInfo.Name);
}
else
{
baseHtml = baseHtml.Replace("#opname#", "操作");
}
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Email))
{
baseHtml = baseHtml.Replace("#opemail#", opUserInfo.Email);
}
else
{
baseHtml = baseHtml.Replace("#opemail#", "");
}
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Phone))
{
baseHtml = baseHtml.Replace("#optel#", opUserInfo.Phone);
}
else if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Tel))
{
baseHtml = baseHtml.Replace("#optel#", opUserInfo.Tel);
}
else
{
baseHtml = baseHtml.Replace("#optel#", "");
}
HtmlDocument html = new HtmlDocument();
html.LoadHtml(baseHtml);
HtmlNode baseTable = html.DocumentNode.SelectNodes("//table[@class='base-table']").FirstOrDefault();
if (baseTable == null)
throw Oops.Oh($"读取邮件模板格式错误定位base-table失败");
var baseTrList = baseTable.SelectNodes(".//tr");
foreach (var baseTr in baseTrList)
{
var tdList = baseTr.SelectNodes(".//td");
foreach (var baseTd in tdList)
{
if (baseTd.Attributes["class"].Value == "billno-val")
{
if (bookingOrder != null)
{
//如果是外港这里需要填正式的提单号
if (!bookingOrder.CUSTNO.Equals(bookingOrder.HBLNO))
{
if (!string.IsNullOrWhiteSpace(bookingOrder.PORTLOADID) && bookingOrder.PORTLOADID.Equals("CNTAO", StringComparison.OrdinalIgnoreCase))
{
baseTd.InnerHtml = bookingOrder.MBLNO;
}
else
{
baseTd.InnerHtml = bookingOrder.HBLNO;
}
}
else
{
baseTd.InnerHtml = bookingOrder.MBLNO;
}
}
else
{
baseTd.InnerHtml = taskBCInfo.MBL_NO;
}
}
else if (baseTd.Attributes["class"].Value == "takebillno-val")
{
if (bookingOrder != null)
{
//如果是外港这里需要填正式的提单号
if (!bookingOrder.CUSTNO.Equals(bookingOrder.HBLNO))
{
if (!string.IsNullOrWhiteSpace(bookingOrder.PORTLOADID) && bookingOrder.PORTLOADID.Equals("CNTAO", StringComparison.OrdinalIgnoreCase))
{
baseTd.InnerHtml = bookingOrder.HBLNO;
}
else
{
baseTd.InnerHtml = bookingOrder.MBLNO;
}
}
else
{
baseTd.InnerHtml = bookingOrder.MBLNO;
}
}
else
{
baseTd.InnerHtml = taskBCInfo.MBL_NO;
}
//baseTd.InnerHtml = taskBCInfo.MBL_NO;
}
else if (baseTd.Attributes["class"].Value == "pol-val")
{
baseTd.InnerHtml = taskBCInfo.PLACERECEIPT;
}
else if (baseTd.Attributes["class"].Value == "pod-val")
{
baseTd.InnerHtml = taskBCInfo.PLACEDELIVERY;
}
else if (baseTd.Attributes["class"].Value == "ctn-val")
{
if (ctnList != null && ctnList.Count > 0)
{
baseTd.InnerHtml = string.Join(",", ctnList.GroupBy(a => a.CTNALL).Select(a => $"{a.Key}*{a.Sum(b => b.CTNNUM.HasValue ? b.CTNNUM.Value : 0)}").ToArray());
}
else
{
baseTd.InnerHtml = taskBCInfo.CTN_STAT;
}
}
/*
else if (baseTd.Attributes["class"].Value == "etd-val")
{
if (taskBCInfo.ETD.HasValue)
{
baseTd.InnerHtml = taskBCInfo.ETD.Value.ToString("yyyy-MM-dd");
}
}
else if (baseTd.Attributes["class"].Value == "eta-val")
{
if (taskBCInfo.ETA.HasValue)
{
baseTd.InnerHtml = taskBCInfo.ETA.Value.ToString("yyyy-MM-dd");
}
}*/
}
}
bool isOnlyChangeOne = false;
List<string> nameList = new List<string>();
//船名
if (compareList.Any(x => x.FieldCode.Equals("vessel", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("vessel", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">船名 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("船名");
}
//航次号
if (compareList.Any(x => x.FieldCode.Equals("voyNo", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("voyNo", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">航次号 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("航次号");
}
//ETD
if (compareList.Any(x => x.FieldCode.Equals("ETD", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("ETD", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">ETD 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("ETD");
}
//ETA
if (compareList.Any(x => x.FieldCode.Equals("ETA", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("ETA", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">ETA 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("ETA");
}
//CustomSICutDate 客户样单截止日期
if (compareList.Any(x => x.FieldCode.Equals("CustomSICutDate", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("CustomSICutDate", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">样单截止日期 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("样单截止日期");
}
//CYCutoffTime 截港时间
if (compareList.Any(x => x.FieldCode.Equals("CYCutoffTime", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("CYCutoffTime", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">截港时间 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("截港时间");
}
//ManifestCutDate 舱单截止时间
if (compareList.Any(x => x.FieldCode.Equals("ManifestCutDate", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("ManifestCutDate", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">舱单截止时间 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("舱单截止时间");
}
//VGMCutoffTime MDGF提交截止时间
if (compareList.Any(x => x.FieldCode.Equals("VGMCutoffTime", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("VGMCutoffTime", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">截VGM时间 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("截VGM时间");
}
//MDGFCutDate MDGF提交截止时间
if (compareList.Any(x => x.FieldCode.Equals("MDGFCutDate", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("MDGFCutDate", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"30%\">MDGF提交截止时间 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("MDGF提交截止时间");
}
//PlaceReceipt 收货地
if (compareList.Any(x => x.FieldCode.Equals("PlaceReceipt", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("PlaceReceipt", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"20%\">收货地 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("收货地");
}
//PlaceDelivery 交货地
if (compareList.Any(x => x.FieldCode.Equals("PlaceDelivery", StringComparison.OrdinalIgnoreCase)))
{
var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("PlaceDelivery", StringComparison.OrdinalIgnoreCase)).TargetVal;
baseTable.AppendChild(HtmlNode.CreateNode("<tr><td class=\"ctn-label\" width=\"20%\">交货地 变更为:</td><td class=\"pod-val\" width=\"160\">" + name + "</td></tr>"));
nameList.Add("交货地");
}
var noreplyTr = html.DocumentNode.SelectNodes("//tr[@class='email-noreply']").FirstOrDefault();
if (noreplyTr != null)
{
var currTd = noreplyTr.SelectNodes(".//td").FirstOrDefault();
if (currTd != null)
{
var currPList = currTd.SelectNodes(".//p");
foreach (var baseP in currPList)
{
if (baseP.Attributes["class"].Value == "notice-comp-val")
{
baseP.InnerHtml = tenantName;
}
else if(baseP.Attributes["class"].Value == "dynamic-val")
{
if (nameList.Count > 0)
{
baseP.InnerHtml = $"请注意{(string.Join("", nameList.ToArray()))} 变更,请您按相关的更新安排操作,谢谢!";
}
}
}
}
}
result = html.DocumentNode.OuterHtml;
}
catch (Exception ex)
{
_logger.LogInformation($"通过邮件模板生成HTML异常原因={ex.Message}");
throw ex;
}
return result;
}
#endregion
#region 推送邮件
/// <summary>
/// 推送邮件
/// </summary>
/// <param name="emailApiUserDefinedDto">自定义邮件详情</param>
/// <param name="filePath">文件路径</param>
/// <returns>返回回执</returns>
private async Task<CommonWebApiResult> PushEmail(EmailApiUserDefinedDto emailApiUserDefinedDto, string filePath)
{
CommonWebApiResult result = new CommonWebApiResult { succ = true };
List<EmailApiUserDefinedDto> emailList = new List<EmailApiUserDefinedDto>();
var emailUrl = _cache.GetAllDictData().GetAwaiter().GetResult()
.FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == "email_api_url")?.Value;
if (emailUrl == null)
throw Oops.Bah("字典未配置 url_set->email_api_url 请联系管理员");
System.IO.FileStream file = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read);
int SplitSize = 5242880;//5M分片长度
int index = 1; //序号 第几片
long StartPosition = 5242880 * (index - 1);
long lastLens = file.Length - StartPosition;//真不知道怎么起命了,就这样吧
if (lastLens < 5242880)
{
SplitSize = (int)lastLens;
}
byte[] heByte = new byte[SplitSize];
file.Seek(StartPosition, SeekOrigin.Begin);
//第一个参数是 起始位置
file.Read(heByte, 0, SplitSize);
//第三个参数是 读取长度(剩余长度)
file.Close();
string base64Str = Convert.ToBase64String(heByte);
emailApiUserDefinedDto.Attaches.Add(new AttachesInfo
{
AttachName = Path.GetFileName(filePath).Replace("_MODIFY",""),
AttachContent = base64Str
});
emailList.Add(emailApiUserDefinedDto);
//string strJoin = System.IO.File.ReadAllText(filePath);
DateTime bDate = DateTime.Now;
HttpResponseMessage res = null;
try
{
res = await emailUrl.SetBody(emailList, "application/json").PostAsync();
}
catch (Exception ex)
{
_logger.LogInformation($"发送邮件异常:{ex.Message}");
}
DateTime eDate = DateTime.Now;
TimeSpan ts = eDate.Subtract(bDate);
var timeDiff = ts.TotalMilliseconds;
_logger.LogInformation($"邮件上传完成 上传文件大小:{heByte.Length} 用时:{timeDiff}ms.,");
_logger.LogInformation($"发送邮件返回:{JSON.Serialize(res)}");
if (res != null && res.StatusCode == System.Net.HttpStatusCode.OK)
{
var userResult = await res.Content.ReadAsStringAsync();
var respObj = JsonConvert.DeserializeAnonymousType(userResult, new
{
Success = false,
Message = string.Empty,
Code = -9999,
});
result.succ = respObj.Success;
result.msg = respObj.Message;
}
return result;
}
#endregion
#region 发送邮件
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="taskPKId">BC任务主键</param>
/// <param name="usePersonalEmailSend">是否默认使用用户个人邮箱发送</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/SendEmail")]
public async Task<TaskManageOrderResultDto> SendEmail(string taskPKId, Nullable<long> mandatoryBookingId = null, bool usePersonalEmailSend = false )
{
if (string.IsNullOrWhiteSpace(taskPKId))
throw Oops.Oh($"BC任务主键不能为空");
var bcTaskInfo = await _taskBaseRepository.AsQueryable().Filter(null,true).FirstAsync(u => u.PK_ID == taskPKId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}无法获取业务信息");
}
var bcOrder = _taskBCInfoRepository.AsQueryable().Filter(null, true).First(a => a.TASK_ID == bcTaskInfo.PK_ID);
if (bcOrder == null)
throw Oops.Oh($"任务主键{taskPKId}无法获取BC业务信息");
return await GenerateSendEmail(bcOrder, bcTaskInfo, mandatoryBookingId);
}
#endregion
#region 获取当前比对结果
/// <summary>
/// 获取当前比对结果
/// </summary>
/// <param name="taskPKId">BC任务主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/GetCompareResult")]
public async Task<List<CompareResultDetailInfo>> GetCompareResult(string taskPKId)
{
if (string.IsNullOrWhiteSpace(taskPKId))
throw Oops.Oh($"BC任务主键不能为空");
var bcTaskInfo = await _taskBaseRepository.AsQueryable().Filter(null,true).FirstAsync(u => u.PK_ID == taskPKId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}无法获取业务信息");
}
var bcOrder = _taskBCInfoRepository.AsQueryable().Filter(null, true).First(a => a.TASK_ID == bcTaskInfo.PK_ID);
if (bcOrder == null)
throw Oops.Oh($"任务主键{taskPKId}无法获取BC业务信息");
if(bcOrder.BOOKING_SLOT_ID.HasValue)
{
return await _bookingSlotService.GetSlotCompareResult(bcOrder.BOOKING_SLOT_ID.Value, bcOrder.BATCH_NO);
}
else
{
return new List<CompareResultDetailInfo>();
}
}
#endregion
#region 同步舱位变更
/// <summary>
/// 同步舱位变更
/// </summary>
/// <param name="taskPKId">BC任务主键</param>
/// <param name="tenantId">租户ID</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/SyncBookingSlotChange")]
public async Task<TaskManageOrderResultDto> SyncBookingSlotChange(string taskPKId, long tenantId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
/*
* Amendment
1、需要先匹配舱位如果没有舱位不能执行后续的步骤。
2、判断当前租户是否配置了自动转发客户邮件。
3、如果配置了自动转发需要判断订舱是否已有没有不能执行自动转发。
4、转发需要提取差异数据如果没有差异数据也不能转发邮件。
5、默认通过钉钉通知操作收到了变更通知。
* Cancellation
1、需要先匹配舱位如果没有舱位不能执行后续的步骤。
2、判断当前租户是否配置了自动转发客户邮件。
3、如果配置了自动转发需要判断订舱是否已有没有不能执行自动转发。
4、转发需要提取差异数据如果没有差异数据也不能转发邮件。
*/
try
{
var baseTask = _taskBaseRepository.AsQueryable().Filter(null, true).First(u => u.PK_ID == taskPKId && u.TenantId == tenantId);
if (baseTask == null)
throw Oops.Oh($"主任务获取失败,不存在或已作废");
var bcOrder = _taskBCInfoRepository.AsQueryable().Filter(null, true).First(a => a.TASK_ID == taskPKId && a.TenantId == tenantId);
if (bcOrder == null)
throw Oops.Oh($"BC任务获取失败不存在或已作废");
DjyTenantParamValueOutput paramConfig = null;
DjyTenantParamValueOutput dingdingConfig = null;
var paramList = _djyTenantParamService.GetParaCodeWithValue(new[] { CONST_AMENDMENT_DEFAULT_PARAM,
CONST_CANCELLATION_DEFAULT_PARAM,
CONST_AMENDMENT_DING_DEFAULT_PARAM,
CONST_CANCELLATION_DING_DEFAULT_PARAM }).GetAwaiter().GetResult();
if (baseTask.TASK_TYPE == TaskBaseTypeEnum.BC_MODIFY.ToString())
{
if (paramList != null && paramList.Count > 0)
{
paramConfig = paramList.FirstOrDefault(a => a.ParaCode.Equals(CONST_AMENDMENT_DEFAULT_PARAM, StringComparison.OrdinalIgnoreCase));
dingdingConfig = paramList.FirstOrDefault(a => a.ParaCode.Equals(CONST_AMENDMENT_DING_DEFAULT_PARAM, StringComparison.OrdinalIgnoreCase));
}
}
else if (baseTask.TASK_TYPE == TaskBaseTypeEnum.CANCELLATION.ToString())
{
if (paramList != null && paramList.Count > 0)
{
paramConfig = paramList.FirstOrDefault(a => a.ParaCode.Equals(CONST_CANCELLATION_DEFAULT_PARAM, StringComparison.OrdinalIgnoreCase));
dingdingConfig = paramList.FirstOrDefault(a => a.ParaCode.Equals(CONST_CANCELLATION_DING_DEFAULT_PARAM, StringComparison.OrdinalIgnoreCase));
}
}
//这个先取一下与订单的关系
var searchOrder = await _bookingOrderService.SearchOrderInfo(bcOrder.MBL_NO);
_logger.LogInformation($"taskPKId={taskPKId} 判断是否默认转发邮件 paramConfig={JSON.Serialize(paramConfig)}");
if (!bcOrder.BOOKING_SLOT_ID.HasValue || bcOrder.BOOKING_SLOT_ID.Value == 0)
{
var server = _namedBookingSlotServiceProvider.GetService<ITransient>(nameof(BookingSlotService));
var slotModel = server.SearchBookingSlotWithOrderByNo(bcOrder.MBL_NO, bcOrder.TenantId.Value).GetAwaiter().GetResult();
if (slotModel != null && slotModel.BookingSlotId > 0)
{
if (slotModel.HasBookingOrder)
{
bcOrder.BOOKING_SLOT_ID = slotModel.BookingSlotId;
bcOrder.BOOKING_ORDER_ID = slotModel.BookingOrderList.FirstOrDefault();
bcOrder.UpdatedTime = DateTime.Now;
bcOrder.UpdatedUserId = UserManager.UserId;
bcOrder.UpdatedUserName = UserManager.Name;
await _taskBCInfoRepository.AsUpdateable(bcOrder).UpdateColumns(it => new
{
it.BOOKING_SLOT_ID,
it.BOOKING_ORDER_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
else
{
bcOrder.BOOKING_SLOT_ID = slotModel.BookingSlotId;
bcOrder.UpdatedTime = DateTime.Now;
bcOrder.UpdatedUserId = UserManager.UserId;
bcOrder.UpdatedUserName = UserManager.Name;
await _taskBCInfoRepository.AsUpdateable(bcOrder).UpdateColumns(it => new
{
it.BOOKING_SLOT_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
if (baseTask != null)
{
if (baseTask.IS_PUBLIC == 1)
{
baseTask.IS_PUBLIC = 0;
baseTask.RealUserId = UserManager.UserId;
baseTask.RealUserName = UserManager.Name;
baseTask.UpdatedTime = DateTime.Now;
baseTask.UpdatedUserId = UserManager.UserId;
baseTask.UpdatedUserName = UserManager.Name;
//更新任务台,将当前任务变更为个人任务
await _taskBaseRepository.AsUpdateable(baseTask).UpdateColumns(it => new
{
it.IS_PUBLIC,
it.RealUserId,
it.RealUserName,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
}
}
else
{
_logger.LogInformation($"taskPKId={taskPKId} 无法获取舱位信息");
throw Oops.Oh($"无法获取舱位信息");
}
}
//如果是拆票需要更新所有的拆票订单并根据修改BA文件的单号和箱型自动转发给客户
if (searchOrder.currOrder != null && searchOrder.splitOrMergeFlag == 1)
{
var orderIdList = new List<long> { searchOrder.currOrder.Id };
if (searchOrder.otherOrderList.Count > 0)
orderIdList.AddRange(searchOrder.otherOrderList.Select(b => b.Id).ToList());
foreach (var bkid in orderIdList)
{
var currBCOrder = bcOrder.Adapt<TaskBCInfo>();
currBCOrder.BOOKING_ORDER_ID = bkid;
await UpdateBookingOrder(currBCOrder, taskPKId, true);
//如果值是ENABLE表示可以自动发送
if (paramConfig != null && paramConfig.ParaValue.Equals("ENABLE", StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation($"taskPKId={taskPKId} 准备发送邮件");
var emailRlt = await SendEmail(taskPKId, bkid);
_logger.LogInformation($"taskPKId={taskPKId} 发送邮件完成 结果={JSON.Serialize(emailRlt)}");
}
else
{
_logger.LogInformation($"taskPKId={taskPKId} 未配置自动发送邮件");
}
}
}
else
{
//去关联的订舱ID
if (bcOrder.BOOKING_ORDER_ID == null || bcOrder.BOOKING_ORDER_ID.Value == 0)
{
var server = _namedBookingSlotServiceProvider.GetService<ITransient>(nameof(BookingSlotService));
if (searchOrder.currOrder != null)
{
bcOrder.BOOKING_ORDER_ID = searchOrder.currOrder.Id;
bcOrder.UpdatedTime = DateTime.Now;
bcOrder.UpdatedUserId = UserManager.UserId;
bcOrder.UpdatedUserName = UserManager.Name;
await _taskBCInfoRepository.AsUpdateable(bcOrder).UpdateColumns(it => new
{
it.BOOKING_ORDER_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
else
{
var slotModel = server.SearchBookingSlotWithOrderByNo(bcOrder.MBL_NO, bcOrder.TenantId.Value).GetAwaiter().GetResult();
if (slotModel.HasBookingOrder)
{
bcOrder.BOOKING_ORDER_ID = slotModel.BookingOrderList.FirstOrDefault();
bcOrder.UpdatedTime = DateTime.Now;
bcOrder.UpdatedUserId = UserManager.UserId;
bcOrder.UpdatedUserName = UserManager.Name;
await _taskBCInfoRepository.AsUpdateable(bcOrder).UpdateColumns(it => new
{
it.BOOKING_ORDER_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
}
}
if (bcOrder.BOOKING_ORDER_ID != null && bcOrder.BOOKING_ORDER_ID.HasValue && bcOrder.BOOKING_ORDER_ID.Value > 0)
{
await UpdateBookingOrder(bcOrder, taskPKId);
}
else
{
_logger.LogInformation($"taskPKId={taskPKId} 更新订舱详情失败没有对应舱位ID");
}
//如果值是ENABLE表示可以自动发送
if (paramConfig != null && paramConfig.ParaValue.Equals("ENABLE", StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation($"taskPKId={taskPKId} 准备发送邮件");
var emailRlt = await SendEmail(taskPKId);
_logger.LogInformation($"taskPKId={taskPKId} 发送邮件完成 结果={JSON.Serialize(emailRlt)}");
}
else
{
_logger.LogInformation($"taskPKId={taskPKId} 未配置自动发送邮件");
}
}
//if (dingdingConfig != null && dingdingConfig.ParaValue.Equals("ENABLE", StringComparison.OrdinalIgnoreCase))
//{
// _logger.LogInformation($"taskPKId={taskPKId} 准备发送钉钉消息");
// var emailRlt = await SendEmail(taskPKId);
// _logger.LogInformation($"taskPKId={taskPKId} 发送钉钉消息完成 结果={JSON.Serialize(emailRlt)}");
//}
//else
//{
// _logger.LogInformation($"taskPKId={taskPKId} 未配置自动发送邮件");
//}
result.succ = true;
result.msg = "执行成功";
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"处理异常,原因:{ex.Message}";
_logger.LogInformation($"taskPKId={taskPKId} 处理异常,原因:{ex.Message}");
//new EmailNoticeHelper().SendEmailNotice($"taskPKId={taskPKId} 转发通知邮件失败", $"taskPKId={taskPKId} 转发通知邮件失败,原因:{ex.Message}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
}
return result;
}
#endregion
private async Task UpdateBookingOrder(TaskBCInfo bcOrder, string taskPKId, bool isAvoidUpdateCtn = false)
{
try
{
SaveBookingOrderInput bkModel = new SaveBookingOrderInput
{
Id = bcOrder.BOOKING_ORDER_ID.Value,
MBLNO = bcOrder.MBL_NO.ToUpper().Trim(),
CONTRACTNO = !string.IsNullOrWhiteSpace(bcOrder.CONTRACTNO) ? bcOrder.CONTRACTNO : "",
VESSEL = bcOrder.VESSEL.ToUpper().Trim(),
VOYNO = bcOrder.VOYNO.ToUpper().Trim(),
VOYNOINNER = bcOrder.VOYNO.ToUpper().Trim(),
ETD = bcOrder.ETD,
ETA = bcOrder.ETA,
CLOSEVGMDATE = bcOrder.VGM_CUTOFF_TIME,
CLOSINGDATE = bcOrder.CY_CUTOFF_TIME,
CLOSEDOCDATE = bcOrder.CUT_SINGLE_TIME,
ctnInputs = new List<BookingCtnDto>()
};
//处理自动对应场站
if (!string.IsNullOrWhiteSpace(bcOrder.TAKE_CTN_YARD))
{
var yardInfo = _cache.GetAllCodeYard().GetAwaiter().GetResult().FirstOrDefault(a => a.Code.Equals(bcOrder.TAKE_CTN_YARD));
if (yardInfo != null)
{
bkModel.YARDID = yardInfo.Code;
bkModel.YARD = yardInfo.Name;
}
}
//增加装货港、卸货港的对应
// 解析收货地,得到装货港名称及五字码
if (!string.IsNullOrWhiteSpace(bcOrder.PLACERECEIPT))
{
var portEnName = bcOrder.PLACERECEIPT.Split(',')[0]?.Trim();
//这里CMA的收货地全称放在了括号里面
if (bcOrder.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase))
{
if (!string.IsNullOrWhiteSpace(bcOrder.PLACERECEIPT))
bcOrder.PLACERECEIPT = bcOrder.PLACERECEIPT.Replace("", "(").Replace("", ")");
if (bcOrder.PLACERECEIPT.IndexOf("(") >= 0)
{
string currStr = Regex.Match(bcOrder.PLACERECEIPT, "(?<=\\().*(?=\\))").Value?.Trim();
portEnName = currStr.Split(',')[0]?.Trim();
}
}
if (!string.IsNullOrWhiteSpace(portEnName))
{
var cachePortLoad = await _cache.GetAllCodePortLoad();
var portInfo = await PlaceToPortHelper.PlaceReceiptToPortload(portEnName, cachePortLoad, () => _cache.GetAllMappingPortLoad());
if (portInfo == null)
{
_logger.LogInformation("通过收货地城市名称未匹配到港口信息,订舱编号:{MBL_NO}", bcOrder.MBL_NO);
}
else
{
bkModel.PORTLOAD = portInfo.EnName;
bkModel.PORTLOADID = portInfo.EdiCode;
bkModel.PLACERECEIPT = portInfo.EnName;
bkModel.PLACERECEIPTID = portInfo.EdiCode;
}
}
else
{
_logger.LogInformation("收货地分割后得到的城市名称为空,订舱编号:{MBL_NO}", bcOrder.MBL_NO);
}
}
else
{
_logger.LogInformation("收货地为空,订舱编号:{MBL_NO}", bcOrder.MBL_NO);
}
// 解析交货地,得到为卸货港名称及五字码, 以及国家信息
if (!string.IsNullOrWhiteSpace(bcOrder.PLACEDELIVERY))
{
var portEnName = bcOrder.PLACEDELIVERY.Split(',')[0]?.Trim();
//这里CMA的收货地全称放在了括号里面
if (bcOrder.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase))
{
if (!string.IsNullOrWhiteSpace(bcOrder.PLACEDELIVERY))
bcOrder.PLACEDELIVERY = bcOrder.PLACEDELIVERY.Replace("", "(").Replace("", ")");
if (bcOrder.PLACEDELIVERY.IndexOf("(") >= 0)
{
string currStr = Regex.Match(bcOrder.PLACEDELIVERY, "(?<=\\().*(?=\\))").Value?.Trim();
portEnName = currStr.Split(',')[0]?.Trim();
}
}
if (!string.IsNullOrWhiteSpace(portEnName))
{
var cachePort = await _cache.GetAllCodePort();
var portInfo = await PlaceToPortHelper.PlaceDeliveryToPort(portEnName, cachePort, () => _cache.GetAllMappingPort());
if (portInfo == null)
{
_logger.LogInformation("通过交货地城市名称未匹配到港口信息,订舱编号:{MBL_NO}", bcOrder.MBL_NO);
}
else
{
bkModel.PORTDISCHARGE = portInfo.EnName;
bkModel.PORTDISCHARGEID = portInfo.EdiCode;
bkModel.PLACEDELIVERY = portInfo.EnName;
bkModel.PLACEDELIVERYID = portInfo.EdiCode;
}
}
else
{
_logger.LogInformation("交货地分割后得到的城市名称为空,订舱编号:{MBL_NO}", bcOrder.MBL_NO);
}
}
else
{
_logger.LogInformation("交货地为空,订舱编号:{MBL_NO}", bcOrder.MBL_NO);
}
StringBuilder err = new StringBuilder();
if(string.IsNullOrWhiteSpace(bcOrder.MBL_NO))
{
err.Append("提单号为空");
}
if (bcOrder.BOOKING_SLOT_TYPE.Equals("CONTRACT_ORDER") && string.IsNullOrWhiteSpace(bcOrder.CONTRACTNO))
{
//err.Append("是合约订舱但是合约号为空");
new EmailNoticeHelper().SendEmailNotice($"更新订舱详情被终止 taskPKId={taskPKId} billno={bcOrder.MBL_NO} 是合约订舱但是合约号为空", $"taskPKId={taskPKId} billno={bcOrder.MBL_NO} 更新订舱详情被终止,原因:是合约订舱但是合约号为空,请尽快确认是否是识别问题", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
}
if (string.IsNullOrWhiteSpace(bcOrder.VESSEL))
{
err.Append("船名为空");
}
if (string.IsNullOrWhiteSpace(bcOrder.VOYNO))
{
err.Append("航次号为空");
}
if (!bcOrder.ETD.HasValue)
{
err.Append("ETD为空");
}
//if (!bcOrder.ETA.HasValue)
//{
// err.Append("ETA为空");
//}
var msgTxt = err.ToString();
if(msgTxt.Length > 0)
{
//如果提示内容不为空,终止更新发邮件提醒
new EmailNoticeHelper().SendEmailNotice($"更新订舱详情被终止 taskPKId={taskPKId} billno={bcOrder.MBL_NO} 更新订舱详情被终止", $"taskPKId={taskPKId} billno={bcOrder.MBL_NO} 更新订舱详情被终止,原因:{msgTxt},请尽快确认是否是识别问题", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
return;
}
bkModel.ChangedFields = new List<string>();
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.CONTRACTNO));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.VESSEL));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.VOYNO));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.VOYNOINNER));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.ETD));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.ETA));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.CLOSEVGMDATE));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.CLOSINGDATE));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.CLOSEDOCDATE));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PORTDISCHARGE));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PORTDISCHARGEID));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PLACEDELIVERY));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PLACEDELIVERYID));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PORTLOAD));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PORTLOADID));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PLACERECEIPT));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.PLACERECEIPTID));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.YARDID));
bkModel.ChangedFields.Add(nameof(SaveBookingOrderInput.YARD));
if (!isAvoidUpdateCtn)
{
var taskBCCtnList = _taskBCCTNInfoRepository.AsQueryable().Filter(null, true).Where(a => a.P_ID == bcOrder.PK_ID && a.TenantId == UserManager.TENANT_ID).ToList();
var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList();
if (taskBCCtnList.Count > 0)
{
taskBCCtnList.ForEach(t =>
{
var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) &&
a.Name.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase));
BookingCtnDto ctn = new BookingCtnDto
{
CTNCODE = ctnCode?.Code,
CTNALL = t.CTNALL,
CTNNUM = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1
};
bkModel.ctnInputs.Add(ctn);
});
}
}
else
{
var ctnList = _bookingCtnRepository.AsQueryable().Filter(null, true).Where(a => a.BILLID == bcOrder.BOOKING_ORDER_ID.Value).ToList();
bkModel.ctnInputs = ctnList.Select(b => b.Adapt<BookingCtnDto>()).ToList();
}
var bkRlt = await _bookingOrderService.Save(bkModel);
if (bkRlt.Id != null && bkRlt.Id > 0)
{
_logger.LogInformation($"taskPKId={taskPKId} 更新订舱详情完成");
}
else
{
_logger.LogInformation($"taskPKId={taskPKId} 更新订舱详情失败没有对应舱位ID");
}
}
catch (Exception ex)
{
_logger.LogInformation($"taskPKId={taskPKId} 更新订舱详情异常,原因:{ex.Message}");
new EmailNoticeHelper().SendEmailNotice($"taskPKId={taskPKId} billno={bcOrder.MBL_NO} 更新订舱详情异常", $"taskPKId={taskPKId} billno={bcOrder.MBL_NO} 更新订舱详情异常,原因:{ex.Message}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
}
}
#region 重试处理BC任务
/// <summary>
/// 重试处理BC任务
/// 对未匹配舱位、订舱订单的任务记录重新对应舱位、订舱订单
/// </summary>
/// <param name="taskPkId">BC任务主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/SearchAndConnectBookingInfo")]
public async Task<TaskManageOrderResultDto> SearchAndConnectBookingInfo(string taskPkId)
{
/*
只要解决BC变更、BC取消生成任务时未匹配到舱位、订舱订单需要后边人工来点击匹配上舱位和订舱订单。
1、BC变更重试匹配舱位有舱位触发舱位的变更方法。
2、BC取消重试匹配舱位有舱位触发舱位的取消方法。
*/
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
var taskBase = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskPkId);
if (taskBase == null)
throw Oops.Oh($"任务主键{taskPkId}无法获取业务信息");
var taskBCInfo = _taskBCInfoRepository.AsQueryable().First(a => a.TASK_ID == taskBase.PK_ID);
if (taskBCInfo == null)
throw Oops.Oh($"BC任务主键{taskPkId}无法获取业务信息");
string mblNo = taskBCInfo.MBL_NO.ToUpper().Trim();
var server = _namedBookingSlotServiceProvider.GetService<ITransient>(nameof(BookingSlotService));
//首先检索关联舱位
if (!taskBCInfo.BOOKING_SLOT_ID.HasValue)
{
//var server = _namedBookingSlotServiceProvider.GetService<ITransient>(nameof(BookingSlotService));
var slotInfo = await server.SearchBookingSlotWithOrderByNo(mblNo, taskBCInfo.TenantId.Value);
if (slotInfo == null)
throw Oops.Oh($"提单号{mblNo}未提取有效的舱位信息");
taskBCInfo.BOOKING_SLOT_ID = slotInfo.BookingSlotId;
taskBCInfo.UpdatedUserId = UserManager.UserId;
taskBCInfo.UpdatedUserName = UserManager.Name;
if (slotInfo.HasBookingOrder && !taskBCInfo.BOOKING_ORDER_ID.HasValue)
{
taskBCInfo.BOOKING_ORDER_ID = slotInfo.BookingOrderList.FirstOrDefault();
//更新任务BC
await _taskBCInfoRepository.AsUpdateable(taskBCInfo).UpdateColumns(it => new
{
it.BOOKING_SLOT_ID,
it.BOOKING_ORDER_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
else
{
//更新任务BC
await _taskBCInfoRepository.AsUpdateable(taskBCInfo).UpdateColumns(it => new
{
it.BOOKING_SLOT_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
_logger.LogInformation($"提单号{mblNo} 提取到舱位ID,更新完成");
}
//对应订舱订单
if (!taskBCInfo.BOOKING_ORDER_ID.HasValue)
{
var slotInfo = await server.SearchBookingSlotWithOrderByNo(mblNo, taskBCInfo.TenantId.Value);
if (slotInfo == null)
throw Oops.Oh($"提单号{mblNo}未提取有效的舱位信息");
if (slotInfo.HasBookingOrder && !taskBCInfo.BOOKING_ORDER_ID.HasValue)
{
taskBCInfo.BOOKING_ORDER_ID = slotInfo.BookingOrderList.FirstOrDefault();
taskBCInfo.UpdatedUserId = UserManager.UserId;
taskBCInfo.UpdatedUserName = UserManager.Name;
//更新任务BC
await _taskBCInfoRepository.AsUpdateable(taskBCInfo).UpdateColumns(it => new
{
it.BOOKING_ORDER_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
_logger.LogInformation($"提单号{mblNo} 提取到订舱订单ID,更新完成");
}
}
//取消舱位
if (taskBCInfo.BUSI_TYPE == TaskBusiTypeEnum.CANCELLATION.ToString())
{
BookingSlotBaseApiDto slotModel = new BookingSlotBaseApiDto
{
DataObj = new BookingSlotBaseApiSaveDto
{
CARRIERID = taskBCInfo.CARRIERID,
SLOT_BOOKING_NO = taskBCInfo.MBL_NO,
CtnList = new List<BookingSlotCtnSaveInput>()
},
OpType = "cancellation"
};
_logger.LogInformation($"提单号{mblNo} 推送舱位取消");
var pushRlt = await server.ApiReceive(JSON.Serialize(slotModel));
_logger.LogInformation($"提单号{mblNo} 推送舱位取消,结果={JSON.Serialize(pushRlt)}");
var service = _namedBookingMSKAPIServiceProvider
.GetService<ITransient>(nameof(BookingMSKAPIService));
var synRlt = await service.SyncBCInfo(taskBCInfo.MBL_NO, taskBCInfo.TenantId.Value, "Cancellation");
_logger.LogInformation($"入库完BC自动推送状态到API订舱列表 mblno={taskBCInfo.MBL_NO} synRlt={JSON.Serialize(synRlt)}");
}
else if (taskBCInfo.BUSI_TYPE == TaskBusiTypeEnum.BC_MODIFY.ToString())
{
BookingSlotBaseApiDto slotModel = new BookingSlotBaseApiDto
{
DataObj = new BookingSlotBaseApiSaveDto
{
CARRIERID = taskBCInfo.CARRIERID,
SLOT_BOOKING_NO = taskBCInfo.MBL_NO,
BOOKING_PARTY = taskBCInfo.BOOKING_PARTY,
BOOKING_SLOT_TYPE = taskBCInfo.BOOKING_SLOT_TYPE,
BOOKING_SLOT_TYPE_NAME = taskBCInfo.BOOKING_SLOT_TYPE_NAME,
VESSEL = taskBCInfo.VESSEL,
VOYNO = taskBCInfo.VOYNO,
VGM_SUBMISSION_CUT_DATE = taskBCInfo.VGM_CUTOFF_TIME,
//WEEK_AT = taskBCInfo.WEEK_AT,
CARRIAGE_TYPE = taskBCInfo.CARRIAGE_TYPE,
CARRIAGE_TYPE_NAME = taskBCInfo.CARRIAGE_TYPE_NAME,
CONTRACT_NO = taskBCInfo.CONTRACTNO,
CTN_STAT = taskBCInfo.CTN_STAT,
CY_CUT_DATE = taskBCInfo.CY_CUTOFF_TIME,
DETENSION_FREE_DAYS = taskBCInfo.DETENSION_FREE_DAYS,
ETD = taskBCInfo.ETD,
ETA = taskBCInfo.ETA,
LANECODE = taskBCInfo.LANECODE,
LANENAME = taskBCInfo.LANENAME,
MANIFEST_CUT_DATE = taskBCInfo.MANIFEST_CUT_DATE,
MDGF_CUT_DATE = taskBCInfo.MDGF_CUT_DATE,
PLACEDELIVERY = taskBCInfo.PLACEDELIVERY,
PLACERECEIPT = taskBCInfo.PLACERECEIPT,
PORTDISCHARGE = taskBCInfo.PORTDISCHARGE,
PORTLOAD = taskBCInfo.PORTLOAD,
SI_CUT_DATE = taskBCInfo.SI_CUT_DATE,
TRANSFER_PORT_1 = taskBCInfo.TRANSFER_PORT_1,
TRANSFER_PORT_2 = taskBCInfo.TRANSFER_PORT_2,
CtnList = new List<BookingSlotCtnSaveInput>()
},
OpType = "update"
};
if (int.TryParse(taskBCInfo.WEEK_AT, out int _weekat))
{
slotModel.DataObj.WEEK_AT = _weekat;
}
var ctnList = _taskBCCTNInfoRepository.AsQueryable().Where(a => a.P_ID == taskBCInfo.PK_ID).ToList();
if (ctnList.Count > 0)
{
ctnList.ForEach(t =>
{
BookingSlotCtnSaveInput ctn = new BookingSlotCtnSaveInput
{
CTNALL = t.CTNALL,
CTNNUM = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1
};
slotModel.DataObj.CtnList.Add(ctn);
});
}
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
var taskFileList = _taskFileRepository.AsQueryable().Where(a => a.TASK_PKID == taskBCInfo.TASK_ID).ToList();
DynameFileInfo dynameFile = null;
DynameFileInfo dynameNoticeFile = null;
if (taskFileList.Any(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString()))
{
var fileInfo = taskFileList.FirstOrDefault(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString());
var fileFullPath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
dynameFile = new DynameFileInfo
{
FileBytes = File.ReadAllBytes(fileFullPath),
FileName = Path.GetFileName(fileFullPath)
};
}
if (taskFileList.Any(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC_NOTICE.ToString()))
{
var fileInfo = taskFileList.FirstOrDefault(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString());
var fileFullPath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
dynameNoticeFile = new DynameFileInfo
{
FileBytes = File.ReadAllBytes(fileFullPath),
FileName = Path.GetFileName(fileFullPath)
};
}
_logger.LogInformation($"提单号{mblNo} 推送舱位更新");
var pushRlt = await server.InnerApiReceive(slotModel, dynameFile, dynameNoticeFile);
_logger.LogInformation($"提单号{mblNo} 推送舱位更新,结果={JSON.Serialize(pushRlt)}");
}
result.succ = true;
result.msg = "成功";
return result;
}
#endregion
#region 重新比对
/// <summary>
/// 重新比对
/// </summary>
/// <param name="taskPKId">BC任务主键</param>
/// <returns></returns>
[HttpGet("/TaskManageBC/ReCompareResult")]
public async Task ReCompareResult(string taskPKId)
{
List<CompareResultDetailInfo> list = new List<CompareResultDetailInfo>();
/*
1、判断舱位是否存在如果舱位不存在终止重新比对。
2、获取BookingConfirmation任务如果不存在终止。
3、调取BookingConfirmation文件识别结果与当前任务的文件识别重新比对。
4、更新到对应的舱位
*/
var baseTask = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskPKId);
if (baseTask == null)
{
throw Oops.Oh($"未提取有效的任务信息");
}
if (baseTask.TASK_TYPE != TaskBaseTypeEnum.BC_MODIFY.ToString())
{
throw Oops.Oh($"任务类型不是Amendment不能重新比对");
}
var bcTaskInfo = _taskBCInfoRepository.AsQueryable().First(a => a.TASK_ID == taskPKId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"未提取有效的BC任务信息");
}
long slotId = 0;
string reqBatchNo = bcTaskInfo.BATCH_NO;
var server = _namedBookingSlotServiceProvider.GetService<ITransient>(nameof(BookingSlotService));
//没有舱位先要匹配舱位,才能比对
if (!bcTaskInfo.BOOKING_SLOT_ID.HasValue)
{
var searchInfo = await server.SearchBookingSlotWithOrderByNo(bcTaskInfo.MBL_NO, bcTaskInfo.TenantId.Value);
if (searchInfo != null)
{
if (searchInfo.HasBookingOrder)
{
bcTaskInfo.BOOKING_SLOT_ID = searchInfo.BookingSlotId;
bcTaskInfo.BOOKING_ORDER_ID = searchInfo.BookingOrderList.FirstOrDefault();
bcTaskInfo.UpdatedTime = DateTime.Now;
bcTaskInfo.UpdatedUserId = UserManager.UserId;
bcTaskInfo.UpdatedUserName = UserManager.Name;
await _taskBCInfoRepository.AsUpdateable(bcTaskInfo).UpdateColumns(it => new
{
it.BOOKING_SLOT_ID,
it.BOOKING_ORDER_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
else
{
bcTaskInfo.BOOKING_SLOT_ID = searchInfo.BookingSlotId;
bcTaskInfo.UpdatedTime = DateTime.Now;
bcTaskInfo.UpdatedUserId = UserManager.UserId;
bcTaskInfo.UpdatedUserName = UserManager.Name;
await _taskBCInfoRepository.AsUpdateable(bcTaskInfo).UpdateColumns(it => new
{
it.BOOKING_SLOT_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
if (baseTask.IS_PUBLIC == 1)
{
baseTask.IS_PUBLIC = 0;
baseTask.RealUserId = UserManager.UserId;
baseTask.RealUserName = UserManager.Name;
baseTask.UpdatedTime = DateTime.Now;
baseTask.UpdatedUserId = UserManager.UserId;
baseTask.UpdatedUserName = UserManager.Name;
//更新任务台,将当前任务变更为个人任务
await _taskBaseRepository.AsUpdateable(baseTask).UpdateColumns(it => new {
it.IS_PUBLIC,
it.RealUserId,
it.RealUserName,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
}
}
slotId = bcTaskInfo.BOOKING_SLOT_ID.Value;
var bcInfo = _taskBaseRepository.AsQueryable().Filter(null, true).Where(a => a.MBL_NO == bcTaskInfo.MBL_NO && a.CARRIER_ID == bcTaskInfo.CARRIERID
&& a.TASK_TYPE == TaskBaseTypeEnum.BC.ToString() && a.IsDeleted == false && a.TenantId == UserManager.TENANT_ID).First();
if (bcInfo == null)
throw Oops.Oh($"未检索到对应的BC任务");
var modifyFile = _taskFileRepository.AsQueryable().First(a => a.TASK_PKID == taskPKId && a.FILE_CATEGORY == "BC_MODIFY");
if(modifyFile == null)
throw Oops.Oh($"未获取到当前任务的文件信息");
var bcFile = _taskFileRepository.AsQueryable().First(a => a.TASK_PKID == bcInfo.PK_ID && a.FILE_CATEGORY == "BC");
if (bcFile == null)
throw Oops.Oh($"未获取到当前任务对应BC的文件信息");
var modifyFileName = Path.GetFileName(modifyFile.FILE_PATH);
var bcFileName = Path.GetFileName(bcFile.FILE_PATH);
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
var modifyFileFullPath = Path.Combine(dirAbs, modifyFile.FILE_PATH);
if (!File.Exists(modifyFileFullPath))
{
throw Oops.Oh("Amendment 文件提取失败");
}
var bcFileFullPath = Path.Combine(dirAbs, bcFile.FILE_PATH);
if (!File.Exists(bcFileFullPath))
{
throw Oops.Oh("BC 文件提取失败");
}
TaskBCInfoReadDto modifyBCReadInfo = await GetBCReaderInfo(modifyFileFullPath, modifyFileName, UserManager.TENANT_ID, taskPKId, App.Configuration["BookingAmendReadWebApiUrl"]);
if (modifyBCReadInfo == null)
throw Oops.Oh($"重新读取BC变更文件详情失败");
TaskBCInfoReadDto BCReadInfo = await GetBCReaderInfo(bcFileFullPath, bcFileName, UserManager.TENANT_ID, taskPKId, App.Configuration["BCReadWebApiUrl"]);
if (BCReadInfo == null)
throw Oops.Oh($"重新读取BC文件详情失败");
TaskBCInfoDto bcSrcDto = BCReadInfo.Adapt<TaskBCInfoDto>();
TaskBCInfoDto bcTargetDto = modifyBCReadInfo.Adapt<TaskBCInfoDto>();
if (BCReadInfo.CtnList != null && BCReadInfo.CtnList.Count > 0)
{
bcSrcDto.CtnList = BCReadInfo.CtnList.GroupBy(x => x.CtnALL)
.Select(x =>
{
return new TaskBCCTNInfoDto
{
CtnALL = x.Key,
CTNNUM = x.ToList()
.Sum(a => a.CTNNUM)
};
}).ToList();
}
if (modifyBCReadInfo.CtnList != null && modifyBCReadInfo.CtnList.Count > 0)
{
bcTargetDto.CtnList = modifyBCReadInfo.CtnList.GroupBy(x => x.CtnALL)
.Select(x =>
{
return new TaskBCCTNInfoDto
{
CtnALL = x.Key,
CTNNUM = x.ToList()
.Sum(a => a.CTNNUM)
};
}).ToList();
}
_logger.LogInformation($"触发比对差异开始reqBatchNo={reqBatchNo}");
//比对差异
await server.PushCompareBCInfo(bcSrcDto, bcTargetDto, slotId, reqBatchNo);
_logger.LogInformation($"触发比对差异结束reqBatchNo={reqBatchNo}");
}
#endregion
#region 读BC详情详情
/// <summary>
/// 读BC详情详情
/// </summary>
/// <param name="attachFullName">文件完整路径</param>
/// <param name="fileName">文件名称</param>
/// <param name="tenantId">所属租户</param>
/// <param name="taskPKId">任务ID</param>
/// <param name="url">请求URL</param>
/// <returns></returns>
private async Task<TaskBCInfoReadDto> GetBCReaderInfo(string attachFullName, string fileName, long tenantId,string taskPKId,string url)
{
TaskBCInfoReadDto taskBCInfoReadDto = null;
try
{
DateTime nowDate = DateTime.Now;
EmailBCReadMessageInfo messageInfo = new EmailBCReadMessageInfo
{
Head = new TaskMessageHead
{
GID = IDGen.NextID().ToString(),
MessageType = "BOOKING_AMENDMENT",
SenderId = "DJY",
SenderName = "新大简云",
ReceiverId = "RulesEngine",
ReceiverName = "大简云规则引擎",
Version = "1.0",
RequestDate = nowDate.ToString("yyyy-MM-dd HH:mm:ss"),
RequestAction = "ReadFile",
},
Main = new EmailBCReadMessageMainInfo
{
TenantId = tenantId > 0 ? tenantId.ToString() : ""
}
};
NameValueCollection par = new NameValueCollection();
par.Add("jsonData", JSON.Serialize(messageInfo));
//解析BookingAmendment
var compareRlt = await ExcuteReadFile(par, url,new
{
file = "file",
fileName = fileName,
fileBytes = File.ReadAllBytes(attachFullName)
});
_logger.LogInformation($"读取BC附件详情 taskPKId={taskPKId}compareRlt={JSON.Serialize(compareRlt)}");
if (compareRlt.succ)
{
taskBCInfoReadDto = JSON.Deserialize<TaskBCInfoReadDto>(JSON.Serialize(compareRlt.extra));
}
}
catch (Exception ex)
{
_logger.LogError($"读取BC附件详情异常原因{ex.Message}");
}
return taskBCInfoReadDto;
}
#endregion
#region 请求BookingAmendment解析
/// <summary>
/// 请求BookingAmendment解析
/// </summary>
/// <param name="nameValueCollection">请求参数</param>
/// <param name="url">请求url</param>
/// <param name="fileInfo">文件</param>
/// <param name="contentType">请求类型</param>
/// <returns>返回回执</returns>
[NonAction]
private async Task<ParserReaderExcuteResultDto> ExcuteReadFile(NameValueCollection nameValueCollection, string url,dynamic fileInfo,
string contentType = "application/json")
{
ParserReaderExcuteResultDto model = null;
var result = string.Empty;
using (var httpClient = new HttpClient())
{
try
{
using (var reduceAttach = new MultipartFormDataContent())
{
string[] allKeys = nameValueCollection.AllKeys;
foreach (string key in allKeys)
{
var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(nameValueCollection[key]));
dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data")
{
Name = key
};
reduceAttach.Add(dataContent);
}
#region 文件参数
if (fileInfo != null)
{
var Content = new ByteArrayContent(fileInfo.fileBytes);
//Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
//{
// Name = fileInfo.file.ToString(),
// FileName = fileInfo.fileName.ToString(),
//};
Content.Headers.Add("Content-Type", contentType);
reduceAttach.Add(Content, fileInfo.file.ToString(), HttpUtility.UrlEncode(fileInfo.fileName.ToString()));
}
#endregion
//httpClient.DefaultRequestHeaders.Add("USER_KEY", App.Configuration["ApiUserKey"]);
//httpClient.DefaultRequestHeaders.Add("USER_SECRET", App.Configuration["ApiUserSecret"]);
//请求
var response = httpClient.PostAsync(url, reduceAttach).Result;
result = response.Content.ReadAsStringAsync().Result;
model = JSON.Deserialize<ParserReaderExcuteResultDto>(result);
}
}
catch (Exception ex)
{
_logger.LogInformation("请求读取BC附件详情读取详情异常原因{error}", ex.Message);
throw Oops.Oh($"请求读取BC附件详情读取详情异常原因{ex.Message}");
}
}
return model;
}
#endregion
#region 获取BC关联的订舱详情
/// <summary>
/// 获取BC关联的订舱详情
/// </summary>
/// <param name="taskPkId">BC任务主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageBC/GetBookingOrder")]
public async Task<TaskManageOrderResultDto> GetBookingOrder(string taskPkId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
var baseTask = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskPkId);
if (baseTask == null)
{
throw Oops.Oh($"未提取有效的任务信息");
}
var bcTaskInfo = _taskBCInfoRepository.AsQueryable().First(a => a.TASK_ID == taskPkId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"未提取有效的BC任务信息");
}
if (!bcTaskInfo.BOOKING_ORDER_ID.HasValue)
{
var server = _namedBookingSlotServiceProvider.GetService<ITransient>(nameof(BookingSlotService));
var slotModel = server.SearchBookingSlotWithOrderByNo(bcTaskInfo.MBL_NO, bcTaskInfo.TenantId.Value).GetAwaiter().GetResult();
if (slotModel != null && slotModel.BookingSlotId > 0)
{
if (slotModel.HasBookingOrder)
{
bcTaskInfo.BOOKING_ORDER_ID = slotModel.BookingOrderList.FirstOrDefault();
bcTaskInfo.UpdatedTime = DateTime.Now;
bcTaskInfo.UpdatedUserId = UserManager.UserId;
bcTaskInfo.UpdatedUserName = UserManager.Name;
await _taskBCInfoRepository.AsUpdateable(bcTaskInfo).UpdateColumns(it => new
{
it.BOOKING_ORDER_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
}
}
}
if (bcTaskInfo.BOOKING_ORDER_ID.HasValue)
{
var bkRlt = await _bookingOrderService.Get(bcTaskInfo.BOOKING_ORDER_ID.Value);
if (bkRlt != null)
{
var BookingSubmitResultModel = new BookingOrSlotGenerateDto
{
CustomerId = bkRlt.CUSTOMERID,
CustomerName = bkRlt.CUSTOMERNAME,
CustServiceId = !string.IsNullOrWhiteSpace(bkRlt.CUSTSERVICEID)? long.Parse(bkRlt.CUSTSERVICEID):null,
CustServiceName = bkRlt.CUSTSERVICE,
SaleId = !string.IsNullOrWhiteSpace(bkRlt.SALEID) ? long.Parse(bkRlt.SALEID) : null,
SaleName = bkRlt.SALE,
OpId = !string.IsNullOrWhiteSpace(bkRlt.OPID) ? long.Parse(bkRlt.OPID) : null,
OpName = bkRlt.OP,
DocId = !string.IsNullOrWhiteSpace(bkRlt.DOCID) ? long.Parse(bkRlt.DOCID) : null,
DocName = bkRlt.DOC,
RouteID = !string.IsNullOrWhiteSpace(bkRlt.ROUTEID) ? long.Parse(bkRlt.ROUTEID) : null,
Route = bkRlt.ROUTE,
LineManageID = !string.IsNullOrWhiteSpace(bkRlt.LineManageID) ? long.Parse(bkRlt.LineManageID) : null,
LineManage = bkRlt.LineManage
};
result.ext = BookingSubmitResultModel;
result.succ = true;
}
}
return result;
}
#endregion
#region 更新订舱
/// <summary>
/// 更新订舱
/// </summary>
/// <param name="taskBCInfo">BC任务详情</param>
/// <param name="taskBCCtnList">BC任务集装箱列表</param>
/// <param name="taskFileList">BC任务附件列表</param>
/// <param name="generateModel">订舱请求详情</param>
/// <returns>返回订舱ID</returns>
private async Task<long> UpdateBookingOrder(TaskBCInfo taskBCInfo, List<TaskBCCTNInfo> taskBCCtnList, List<TaskFileInfo> taskFileList,
BookingOrSlotGenerateDto generateModel)
{
long id = 0;
try
{
/*
1、新增订舱
2、推送服务项目
3、推送附件
*/
var carrierInfo = _cache.GetAllCodeCarrier().GetAwaiter().GetResult()
.Where(t => t.Code.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)
|| t.EnName.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)
|| t.CnName.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
SaveBookingOrderInput bkModel = new SaveBookingOrderInput
{
Id = taskBCInfo.BOOKING_ORDER_ID.Value,
CUSTOMERID = generateModel.CustomerId,
CUSTOMERNAME = generateModel.CustomerName,
CARRIERID = carrierInfo.Code?.Trim(),
CARRIER = carrierInfo.CnName?.Trim(),
MBLNO = taskBCInfo.MBL_NO.ToUpper().Trim(),
CONTRACTNO = !string.IsNullOrWhiteSpace(taskBCInfo.CONTRACTNO) ? taskBCInfo.CONTRACTNO : "",
VESSEL = taskBCInfo.VESSEL.ToUpper().Trim(),
VOYNO = taskBCInfo.VOYNO.ToUpper().Trim(),
VOYNOINNER = taskBCInfo.VOYNO.ToUpper().Trim(),
ETD = taskBCInfo.ETD,
ETA = taskBCInfo.ETA,
SALEID = generateModel.SaleId.ToString(),
SALE = generateModel.SaleName,
OPID = generateModel.OpId.ToString(),
OP = generateModel.OpName,
DOCID = generateModel.DocId.ToString(),
DOC = generateModel.DocName,
ROUTEID = generateModel.RouteID.ToString(),
ROUTE = generateModel.Route,
CZRemark = generateModel.CZRemark,
ShenQingXiangShi = generateModel.ShenQingXiangShi,
LineManageID = generateModel.LineManageID.ToString(),
LineName = generateModel.LineManage,
CLOSEVGMDATE = taskBCInfo.VGM_CUTOFF_TIME,
CLOSINGDATE = taskBCInfo.CY_CUTOFF_TIME,
CLOSEDOCDATE = taskBCInfo.CUT_SINGLE_TIME,
CUSTSERVICEID = generateModel.CustServiceId.HasValue ? generateModel.CustServiceId.Value.ToString() : null,
CUSTSERVICE = generateModel.CustServiceName,
ctnInputs = new List<BookingCtnDto>()
};
var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList();
if (taskBCCtnList.Count > 0)
{
taskBCCtnList.ForEach(t =>
{
var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) &&
a.Name.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase));
BookingCtnDto ctn = new BookingCtnDto
{
CTNCODE = ctnCode?.Code,
CTNALL = t.CTNALL,
CTNNUM = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1
};
bkModel.ctnInputs.Add(ctn);
});
}
bkModel.ChangedFields = new List<string>
{
nameof(SaveBookingOrderInput.CUSTOMERID),
nameof(SaveBookingOrderInput.CUSTOMERNAME),
nameof(SaveBookingOrderInput.CARRIERID),
nameof(SaveBookingOrderInput.CARRIER),
nameof(SaveBookingOrderInput.CONTRACTNO),
nameof(SaveBookingOrderInput.VESSEL),
nameof(SaveBookingOrderInput.VOYNO),
nameof(SaveBookingOrderInput.SALEID),
nameof(SaveBookingOrderInput.SALE),
nameof(SaveBookingOrderInput.OPID),
nameof(SaveBookingOrderInput.OP),
nameof(SaveBookingOrderInput.DOCID),
nameof(SaveBookingOrderInput.DOC),
nameof(SaveBookingOrderInput.ROUTEID),
nameof(SaveBookingOrderInput.ROUTE),
nameof(SaveBookingOrderInput.LineManageID),
nameof(SaveBookingOrderInput.LineName),
nameof(SaveBookingOrderInput.VOYNOINNER),
nameof(SaveBookingOrderInput.ROUTE),
nameof(SaveBookingOrderInput.ETD),
nameof(SaveBookingOrderInput.ETA),
nameof(SaveBookingOrderInput.CZRemark),
nameof(SaveBookingOrderInput.ShenQingXiangShi),
nameof(SaveBookingOrderInput.CLOSEVGMDATE),
nameof(SaveBookingOrderInput.CLOSINGDATE),
nameof(SaveBookingOrderInput.CUSTSERVICEID),
nameof(SaveBookingOrderInput.CUSTSERVICE),
};
var bkRlt = await _bookingOrderService.Save(bkModel);
id = bkRlt.Id;
string batchNo = IDGen.NextID().ToString();
if (id > 0)
{
var hisList = _bookingOrderContactRepository.AsQueryable().Where(a => a.BookingId == id && a.IsDeleted == false).ToList();
if (hisList.Count > 0)
{
//批量作废
hisList.ForEach(async a =>
{
a.IsDeleted = true;
a.UpdatedTime = DateTime.Now;
a.UpdatedUserId = UserManager.UserId;
a.UpdatedUserName = UserManager.Name;
await _bookingOrderContactRepository.UpdateAsync(a);
});
}
//这里如果指定了委托单位的邮件联系人,则推送订舱联系人
if (generateModel.CustomerContactList != null && generateModel.CustomerContactList.Count > 0)
{
var bookingContactList = _bookingOrderContactRepository.AsQueryable()
.Where(a => a.BookingId == id && !a.IsDeleted).ToList();
var djyCustomerInfo = _djyCustomerService.Detail(new GetDjyCustomerInput { Id = generateModel.CustomerId.Value })
.GetAwaiter().GetResult();
generateModel.CustomerContactList.ForEach(contact =>
{
DjyCustomerContactOutput djyCustomerContactMan = null;
if (djyCustomerInfo.Contacts != null && djyCustomerInfo.Contacts.Count > 0)
{
djyCustomerContactMan = djyCustomerInfo.Contacts.FirstOrDefault(a =>
a.Id == contact.CustomerContactId);
}
if (djyCustomerContactMan != null)
{
var bookingContact = bookingContactList
.FirstOrDefault(x => x.Email.Equals(djyCustomerContactMan.Email, StringComparison.OrdinalIgnoreCase));
if (bookingContact == null)
{
bookingContact = new BookingOrderContact
{
Name = djyCustomerContactMan.Name,
BookingId = id,
Email = djyCustomerContactMan.Email,
Remark = djyCustomerContactMan.Remark,
CreatedTime = DateTime.Now,
CreatedUserId = UserManager.UserId,
CreatedUserName = UserManager.Name
};
_bookingOrderContactRepository.Insert(bookingContact);
}
else
{
bookingContact.Name = djyCustomerContactMan.Name;
bookingContact.Email = djyCustomerContactMan.Email;
bookingContact.Remark = djyCustomerContactMan.Remark;
bookingContact.UpdatedTime = DateTime.Now;
bookingContact.UpdatedUserId = UserManager.UserId;
bookingContact.UpdatedUserName = UserManager.Name;
_bookingOrderContactRepository.AsUpdateable(bookingContact).UpdateColumns(it => new
{
it.Name,
it.Email,
it.Remark,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommand();
}
}
});
}
if (generateModel.ProjectList != null && generateModel.ProjectList.Count > 0)
{
ModifyServiceProjectDto projectDto = new ModifyServiceProjectDto
{
BookingId = id,
ProjectCodes = generateModel.ProjectList.Distinct().ToArray(),
};
//写入服务项目
var prjRlt = await _bookingValueAddedService.SaveServiceProject(projectDto);
_logger.LogInformation($"推送订舱的服务项目完成 id={id} rlt={JSON.Serialize(prjRlt)}");
}
}
_logger.LogInformation($"任务BC MBLNO:{taskBCInfo.MBL_NO} 生成订舱成功 id={id}");
}
catch (Exception ex)
{
_logger.LogError($"任务BC MBLNO:{taskBCInfo.MBL_NO} 生成订舱异常,原因:{ex.Message}");
}
return id;
}
#endregion
#region 校验生成订舱或舱位的联系人信息
/// <summary>
/// 校验生成订舱或舱位的联系人信息
/// </summary>
/// <param name="model">请求参数</param>
private void ValidateContact(BookingOrSlotGenerateDto model)
{
//校验前端提交的联系人是否是委托绑定的联系人
if (model.GenerateMethod == "GEN_BOOKING_SLOT" || model.GenerateMethod == "GEN_BOOKING" || model.GenerateMethod == "UPD_BOOKING")
{
DjyTenantParamValueOutput paramConfig = null;
var paramList = _djyTenantParamService.GetParaCodeWithValue(new[] { CONST_CREATE_BOOKING_NEED_CONTACT }).GetAwaiter().GetResult();
if (paramList != null && paramList.Count > 0)
{
paramConfig = paramList.FirstOrDefault(a => a.ParaCode.Equals(CONST_CREATE_BOOKING_NEED_CONTACT, StringComparison.OrdinalIgnoreCase));
}
if (model.CustomerContactList != null && model.CustomerContactList.Count > 0)
{
//取委托客户下面所有的联系人列表
var djyCustomerInfo = _djyCustomerService.Detail(new GetDjyCustomerInput { Id = model.CustomerId.Value })
.GetAwaiter().GetResult();
if (djyCustomerInfo == null)
{
_logger.LogInformation($"委托单位{model.CustomerName} 获取失败,委托单位不存在或已作废 taskid={model.BCTaskId}");
throw Oops.Oh($"委托单位{model.CustomerName} 获取失败,委托单位不存在或已作废");
}
if (djyCustomerInfo.Contacts == null && djyCustomerInfo.Contacts.Count < 1)
{
_logger.LogInformation($"委托单位{model.CustomerName} 获取相关联系人失败,委托单位相关联系人为空 taskid={model.BCTaskId}");
throw Oops.Oh($"委托单位{model.CustomerName} 获取相关联系人失败,委托单位相关联系人为空");
}
model.CustomerContactList.ForEach(contact =>
{
DjyCustomerContactOutput djyCustomerContactMan = djyCustomerInfo.Contacts
.FirstOrDefault(a => a.Id == contact.CustomerContactId);
if (djyCustomerContactMan == null)
{
_logger.LogInformation($"委托单位{model.CustomerName} 联系人 {contact.CustomerContactName}获取失败,联系人不存在或已作废 taskid={model.BCTaskId}");
throw Oops.Oh($"委托单位{model.CustomerName} 联系人 {contact.CustomerContactName}获取失败,联系人不存在或已作废");
}
});
}
else
{
if (paramConfig != null && paramConfig.ParaValue.Equals("ENABLE", StringComparison.OrdinalIgnoreCase))
{
throw Oops.Oh($"生成订舱时往来单位联系必填,请修改");
}
}
}
}
#endregion
#region 同步更新船名基础表
/// <summary>
/// 同步更新船名基础表
/// </summary>
/// <param name="taskPKId">BC任务主键</param>
/// <returns>返回回执</returns>
public async Task<TaskManageOrderResultDto> SyncDjyVesselInfo(string taskPKId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
var taskBase = _taskBaseRepository.AsQueryable().Filter(null,true).First(a => a.PK_ID == taskPKId);
if (taskBase == null)
throw Oops.Oh($"任务主键{taskPKId}无法获取业务信息");
var taskBCInfo = _taskBCInfoRepository.AsQueryable().Filter(null, true).First(a => a.TASK_ID == taskBase.PK_ID);
if (taskBCInfo == null)
throw Oops.Oh($"BC任务主键{taskPKId}无法获取业务信息");
if(!string.IsNullOrWhiteSpace(taskBCInfo.VESSEL))
{
var syncRlt = await _commonDBService.SyncVessel(new Core.Service.CommonDB.Dto.CodeVesselDto { Name = taskBCInfo.VESSEL });
if (string.IsNullOrWhiteSpace(syncRlt))
{
result.succ = true;
_logger.LogInformation($"任务BC MBLNO:{taskBCInfo.MBL_NO} 同步船名成功 新增船名{taskBCInfo.VESSEL}");
}
else
{
result.succ = false;
result.msg = syncRlt;
}
}
else
{
result.succ = false;
result.msg = "BC的船名为空";
}
}
catch(Exception ex)
{
result.succ = false;
result.msg = $"同步船名到船名基础表异常,原因:{ex.Message}";
}
return result;
}
#endregion
#region 重新获取生成BookingAmendment文件
/// <summary>
/// 重新获取生成BookingAmendment文件
/// </summary>
/// <param name="bookingOrder"></param>
/// <param name="ctnList"></param>
/// <param name="origFilePath"></param>
/// <returns></returns>
private async Task<string> ReGenerateBAFile(BookingOrder bookingOrder,List<BookingCtn> ctnList,string origFilePath)
{
DrawModifyBCConfig mConfig = new DrawModifyBCConfig {
drawTypeArg = new DrawModifyBCConfigTypeEnum[] { DrawModifyBCConfigTypeEnum.ChangeToSplitBillNo,DrawModifyBCConfigTypeEnum.SplitBC },
origBillNo = bookingOrder.CUSTNO,
newBillNo = bookingOrder.MBLNO,
newCtnNum = ctnList.Sum(a=> a.CTNNUM),
ctnAll = ctnList.FirstOrDefault().CTNALL
};
NameValueCollection modifyPar = new NameValueCollection();
modifyPar.Add("jsonMessage", JSON.Serialize(mConfig));
DynameFileInfo dynameFile = null;
var fileName = Path.GetFileName(origFilePath);
var fileBytes = File.ReadAllBytes(origFilePath);
if (fileBytes.Length > 0)
{
dynameFile = new DynameFileInfo
{
FileName = fileName,
FileBytes = fileBytes
};
}
string batchNo = IDGen.NextID().ToString();
byte[] modifyBytes = ModifyFile(modifyPar, dynameFile).GetAwaiter().GetResult();
var noExtensionFileName = Path.GetFileNameWithoutExtension(fileName);
return await FileAttachHelper.SaveFile(bookingOrder.Id.ToString(), modifyBytes, batchNo, noExtensionFileName,
GetFileType(origFilePath), "bcsplitnoticefiles");
}
#endregion
#region 获取文件类型
/// <summary>
/// 获取文件类型
/// </summary>
/// <param name="fileName">文件完成路径</param>
/// <returns>返回文件类型枚举</returns>
private PrintFileTypeEnum GetFileType(string fileName)
{
PrintFileTypeEnum fileType = PrintFileTypeEnum.PDF;
switch (Path.GetExtension(fileName).ToLower())
{
case ".pdf":
fileType = PrintFileTypeEnum.PDF;
break;
case ".xlsx":
fileType = PrintFileTypeEnum.XLSX;
break;
case ".docx":
fileType = PrintFileTypeEnum.DOCX;
break;
case ".xls":
fileType = PrintFileTypeEnum.XLS;
break;
case ".doc":
fileType = PrintFileTypeEnum.DOC;
break;
}
return fileType;
}
#endregion
#region 请求变更BookingAmendment后的文件
/// <summary>
/// 请求变更BookingAmendment后的文件
/// </summary>
/// <param name="nameValueCollection">请求参数</param>
/// <param name="fileInfo">文件</param>
/// <param name="contentType">请求类型</param>
/// <returns>返回回执</returns>
[NonAction]
private async Task<byte[]> ModifyFile(NameValueCollection nameValueCollection, dynamic fileInfo,
string contentType = "application/json")
{
//Stream ms = null;
byte[] bytes;
var result = string.Empty;
using (var httpClient = new HttpClient())
{
try
{
using (var reduceAttach = new MultipartFormDataContent())
{
string[] allKeys = nameValueCollection.AllKeys;
foreach (string key in allKeys)
{
var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(nameValueCollection[key]));
dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data")
{
Name = key
};
reduceAttach.Add(dataContent);
}
#region 文件参数
if (fileInfo != null)
{
var Content = new ByteArrayContent(fileInfo.FileBytes);
//Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
//{
// Name = fileInfo.file.ToString(),
// FileName = fileInfo.fileName.ToString(),
//};
Content.Headers.Add("Content-Type", contentType);
reduceAttach.Add(Content, "file", HttpUtility.UrlEncode(fileInfo.FileName.ToString()));
}
#endregion
//httpClient.DefaultRequestHeaders.Add("USER_KEY", App.Configuration["ApiUserKey"]);
//httpClient.DefaultRequestHeaders.Add("USER_SECRET", App.Configuration["ApiUserSecret"]);
//请求
var response = httpClient.PostAsync(App.Configuration["BookingAmendModifyWebApiUrl"], reduceAttach).Result;
bytes = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
}
}
catch (Exception ex)
{
_logger.LogInformation("请求自动变更BookingAmendment文件内容异常原因{error}", ex.Message);
throw Oops.Oh($"请求自动变更BookingAmendment文件内容异常原因{ex.Message}");
}
}
return bytes;
}
#endregion
}
/// <summary>
/// 自定义邮件
/// </summary>
public class EmailApiUserDefinedDto
{
/// <summary>
/// 接收邮箱
/// </summary>
public string SendTo { get; set; }
/// <summary>
/// 抄送
/// </summary>
public string CCTo { get; set; }
/// <summary>
/// 邮件标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 邮件正文
/// </summary>
public string Body { get; set; }
/// <summary>
/// 账号
/// </summary>
public string Account { get; set; }
/// <summary>
/// 密码(126邮箱需要在网站生成密文)
/// </summary>
public string Password { get; set; }
/// <summary>
/// SMTP服务器
/// </summary>
public string Server { get; set; }
/// <summary>
/// SMTP端口
/// </summary>
public int Port { get; set; }
/// <summary>
/// 发件SSL
/// </summary>
public bool UseSSL { get; set; }
/// <summary>
/// 附件
/// </summary>
public List<AttachesInfo> Attaches { get; set; }
}
}