|
|
using Furion;
|
|
|
using Furion.DependencyInjection;
|
|
|
using Furion.DistributedIDGenerator;
|
|
|
using Furion.DynamicApiController;
|
|
|
using Furion.EventBus;
|
|
|
using Furion.Extensions;
|
|
|
using Furion.FriendlyException;
|
|
|
using Furion.JsonSerialization;
|
|
|
using Mapster;
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
using Myshipping.Application.ConfigOption;
|
|
|
using Myshipping.Application.Entity;
|
|
|
using Myshipping.Application.Event;
|
|
|
using Myshipping.Application.Helper;
|
|
|
using Myshipping.Application.Service.BookingOrder.Dto;
|
|
|
using Myshipping.Application.Service.BookingSlot.Dto;
|
|
|
using Myshipping.Core;
|
|
|
using Myshipping.Core.Service;
|
|
|
using SqlSugar;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.ComponentModel;
|
|
|
using System.IO;
|
|
|
using System.Linq;
|
|
|
using System.Net.Http;
|
|
|
using System.Net.Http.Headers;
|
|
|
using System.Text;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
using Yitter.IdGenerator;
|
|
|
|
|
|
namespace Myshipping.Application
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 订舱舱位
|
|
|
/// </summary>
|
|
|
[ApiDescriptionSettings("Application", Name = "BookingSlot", Order = 1)]
|
|
|
public class BookingSlotService : IDynamicApiController, ITransient, IBookingSlotService
|
|
|
{
|
|
|
private readonly SqlSugarRepository<BookingSlotBase> _repBase;
|
|
|
private readonly SqlSugarRepository<BookingSlotCtn> _repCtn;
|
|
|
private readonly SqlSugarRepository<BookingSlotStock> _repStock;
|
|
|
private readonly SqlSugarRepository<BookingSlotAllocation> _repAllocation;
|
|
|
private readonly SqlSugarRepository<BookingLabelAllocation> _repLabelAllocation;
|
|
|
private readonly SqlSugarRepository<BookingSlotAllocationCtn> _repAllocationCtn;
|
|
|
private readonly SqlSugarRepository<BookingFile> _bookingFileRepository;
|
|
|
private readonly SqlSugarRepository<BookingSlotCompare> _bookingSlotCompareRepository;
|
|
|
private readonly SqlSugarRepository<BookingOrderContact> _bookingOrderContactRepository;
|
|
|
private readonly SqlSugarRepository<BookingOrder> _repBookingOrder;
|
|
|
private readonly SqlSugarRepository<BookingLog> _repBookingLog;
|
|
|
private readonly SqlSugarRepository<BookingLogDetail> _repBookingLogDetail;
|
|
|
private readonly SqlSugarRepository<BookingFile> _bookingfile;
|
|
|
|
|
|
private readonly ILogger<BookingSlotService> _logger;
|
|
|
private readonly ISysCacheService _cache;
|
|
|
|
|
|
private readonly IEventPublisher _publisher;
|
|
|
private readonly INamedServiceProvider<IBookingOrderService> _namedBookingOrderServiceProvider;
|
|
|
private readonly IDjyCustomerService _djyCustomerService;
|
|
|
private readonly IBookingValueAddedService _bookingValueAddedService;
|
|
|
|
|
|
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";
|
|
|
|
|
|
const string CONST_BC_MODIFY_FILE_CODE = "bc_modify";
|
|
|
const string CONST_BC_MODIFY_FILE_NAME = "Booking Amendment";
|
|
|
|
|
|
const string CONST_BC_MODIFY_NOTICE_FILE_CODE = "bc_moidfynotice";
|
|
|
const string CONST_BC_MODIFY_NOTICE_FILE_NAME = "Booking Amendment Notice";
|
|
|
|
|
|
public BookingSlotService(SqlSugarRepository<BookingSlotBase> repBase,
|
|
|
SqlSugarRepository<BookingSlotCtn> repCtn,
|
|
|
SqlSugarRepository<BookingSlotStock> repStock,
|
|
|
SqlSugarRepository<BookingLog> repBookingLog,
|
|
|
SqlSugarRepository<BookingLogDetail> repBookingLogDetail,
|
|
|
SqlSugarRepository<BookingFile> bookingfile,
|
|
|
ILogger<BookingSlotService> logger,
|
|
|
ISysCacheService cache,
|
|
|
IEventPublisher publisher,
|
|
|
IDjyCustomerService djyCustomerService,
|
|
|
SqlSugarRepository<BookingSlotAllocation> repAllocation,
|
|
|
SqlSugarRepository<BookingSlotAllocationCtn> repAllocationCtn,
|
|
|
SqlSugarRepository<BookingFile> bookingFileRepository,
|
|
|
SqlSugarRepository<BookingSlotCompare> bookingSlotCompareRepository,
|
|
|
SqlSugarRepository<BookingOrderContact> bookingOrderContactRepository,
|
|
|
INamedServiceProvider<IBookingOrderService> namedBookingOrderServiceProvider,
|
|
|
IBookingValueAddedService bookingValueAddedService,
|
|
|
SqlSugarRepository<BookingOrder> repBookingOrder,
|
|
|
SqlSugarRepository<BookingLabelAllocation> repLabelAllocation)
|
|
|
{
|
|
|
_repBase = repBase;
|
|
|
_repCtn = repCtn;
|
|
|
_repStock = repStock;
|
|
|
_repBookingLog = repBookingLog;
|
|
|
_repBookingLogDetail = repBookingLogDetail;
|
|
|
_repAllocation = repAllocation;
|
|
|
_repAllocationCtn = repAllocationCtn;
|
|
|
_djyCustomerService = djyCustomerService;
|
|
|
|
|
|
_logger = logger;
|
|
|
_cache = cache;
|
|
|
|
|
|
_publisher = publisher;
|
|
|
_bookingfile = bookingfile;
|
|
|
_bookingFileRepository = bookingFileRepository;
|
|
|
_bookingSlotCompareRepository = bookingSlotCompareRepository;
|
|
|
_namedBookingOrderServiceProvider = namedBookingOrderServiceProvider;
|
|
|
_bookingOrderContactRepository = bookingOrderContactRepository;
|
|
|
_bookingValueAddedService = bookingValueAddedService;
|
|
|
_repBookingOrder = repBookingOrder;
|
|
|
_repLabelAllocation = repLabelAllocation;
|
|
|
}
|
|
|
|
|
|
#region 舱位
|
|
|
|
|
|
/// <summary>
|
|
|
/// 保存订舱舱位
|
|
|
/// </summary>
|
|
|
/// <param name="input"></param>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/save")]
|
|
|
public async Task<BookingSlotBaseSaveOutput> Save(BookingSlotBaseSaveInput input)
|
|
|
{
|
|
|
BookingSlotBase model = null;
|
|
|
if (input.Id > 0) //修改
|
|
|
{
|
|
|
var c = _repBase.AsQueryable().Where(x => x.SLOT_BOOKING_NO == input.SLOT_BOOKING_NO && input.Id != input.Id).Count();
|
|
|
if (c > 0)
|
|
|
{
|
|
|
throw Oops.Bah("订舱编号已存在");
|
|
|
}
|
|
|
|
|
|
model = _repBase.FirstOrDefault(x => x.Id == input.Id);
|
|
|
var oldObj = model.Adapt<BookingSlotBaseSaveInput>();
|
|
|
|
|
|
input.Adapt(model);
|
|
|
|
|
|
// 1.判断新的舱位信息的7个库存统计维度是否发生变化
|
|
|
// 2.如果有变化,则需要更新旧的库存信息
|
|
|
bool isNeedUpdateOldStock = false;
|
|
|
if (oldObj.VESSEL != model.VESSEL
|
|
|
|| oldObj.VOYNO != model.VOYNO
|
|
|
|| oldObj.BOOKING_SLOT_TYPE != model.BOOKING_SLOT_TYPE
|
|
|
|| oldObj.CARRIERID != model.CARRIERID
|
|
|
|| oldObj.PORTLOADID != model.PORTLOADID
|
|
|
|| oldObj.PORTDISCHARGEID != model.PORTDISCHARGEID)
|
|
|
{
|
|
|
isNeedUpdateOldStock = true;
|
|
|
}
|
|
|
await _repBase.UpdateAsync(model);
|
|
|
|
|
|
if (isNeedUpdateOldStock)
|
|
|
{
|
|
|
//更新库存
|
|
|
await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel
|
|
|
{
|
|
|
BOOKING_SLOT_TYPE = oldObj.BOOKING_SLOT_TYPE,
|
|
|
CARRIERID = oldObj.CARRIERID,
|
|
|
CONTRACT_NO = oldObj.CONTRACT_NO,
|
|
|
VESSEL = oldObj.VESSEL,
|
|
|
VOYNO = oldObj.VOYNO,
|
|
|
PORTLOADID = oldObj.PORTLOADID,
|
|
|
PORTDISCHARGEID = oldObj.PORTDISCHARGEID,
|
|
|
TenantId = model.TenantId
|
|
|
}));
|
|
|
}
|
|
|
await _repCtn.DeleteAsync(x => x.SLOT_ID == model.Id);
|
|
|
if (input.CtnList != null)
|
|
|
{
|
|
|
foreach (var ctn in input.CtnList)
|
|
|
{
|
|
|
var newCtn = ctn.Adapt<BookingSlotCtn>();
|
|
|
newCtn.SLOT_ID = model.Id;
|
|
|
await _repCtn.InsertAsync(newCtn);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
#region 关联订舱信息修改
|
|
|
if (input.BookingSlotSaleInfoList != null)
|
|
|
{
|
|
|
var bookingOrderService = App.GetService<IBookingOrderService>();
|
|
|
foreach (var item in input.BookingSlotSaleInfoList)
|
|
|
{
|
|
|
if (!item.UpdateFlag)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
var allocation = await _repAllocation.FirstOrDefaultAsync(x => x.Id == item.Id);
|
|
|
if (allocation == null)
|
|
|
{
|
|
|
throw Oops.Oh($"保存失败,原因:更新关联订舱时未查询到订舱(关联表Id:{item.Id})");
|
|
|
}
|
|
|
|
|
|
// 更新关联表
|
|
|
item.Adapt(allocation);
|
|
|
_repAllocation.CurrentBeginTran();
|
|
|
try
|
|
|
{
|
|
|
await _repAllocation.AsUpdateable(allocation).UpdateColumns(x => new
|
|
|
{
|
|
|
x.CUSTOMERID,
|
|
|
x.CUSTOMERNAME,
|
|
|
x.CUSTSERVICE,
|
|
|
x.CUSTSERVICEID,
|
|
|
x.SALE,
|
|
|
x.SALEID,
|
|
|
x.SHIPPER,
|
|
|
x.SALE_TIME,
|
|
|
x.GOODSNAME,
|
|
|
x.SELLING_PRICE,
|
|
|
x.UpdatedUserId,
|
|
|
x.UpdatedUserName,
|
|
|
x.UpdatedTime,
|
|
|
}).ExecuteCommandAsync();
|
|
|
|
|
|
// 更新订舱表
|
|
|
var bookingOrder = await _repBookingOrder.FirstOrDefaultAsync(x => x.Id == allocation.BOOKING_ID);
|
|
|
var oldBookingOrder = bookingOrder.Adapt<BookingOrder>();
|
|
|
if (bookingOrder != null)
|
|
|
{
|
|
|
bookingOrder.CUSTOMERID = allocation.CUSTOMERID;
|
|
|
bookingOrder.CUSTOMERNAME = allocation.CUSTOMERNAME;
|
|
|
bookingOrder.CUSTSERVICE = allocation.CUSTSERVICE;
|
|
|
bookingOrder.CUSTSERVICEID = allocation.CUSTSERVICEID;
|
|
|
bookingOrder.SALE = allocation.SALE;
|
|
|
bookingOrder.SALEID = allocation.SALEID;
|
|
|
|
|
|
await _repBookingOrder.AsUpdateable(bookingOrder).UpdateColumns(x => new
|
|
|
{
|
|
|
x.CUSTOMERID,
|
|
|
x.CUSTOMERNAME,
|
|
|
x.CUSTSERVICE,
|
|
|
x.CUSTSERVICEID,
|
|
|
x.SALE,
|
|
|
x.SALEID,
|
|
|
x.UpdatedUserId,
|
|
|
x.UpdatedUserName,
|
|
|
x.UpdatedTime,
|
|
|
}).ExecuteCommandAsync();
|
|
|
|
|
|
Parallel.For(0, 1, (n) =>
|
|
|
{
|
|
|
bookingOrderService.SaveLog(bookingOrder, oldBookingOrder, "舱位关联更新");
|
|
|
|
|
|
// 推送东胜
|
|
|
bookingOrderService.SendBookingOrder(new long[] { allocation.BOOKING_ID });
|
|
|
});
|
|
|
}
|
|
|
_repAllocation.CurrentCommitTran();
|
|
|
}
|
|
|
catch (Exception)
|
|
|
{
|
|
|
_repAllocation.CurrentRollbackTran();
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
Parallel.For(0, 1, (n) =>
|
|
|
{
|
|
|
_ = InsLog("Update", model.Id, typeof(BookingSlotBaseSaveInput), oldObj, input,
|
|
|
nameof(BookingSlotBaseApiSaveDto.CtnList),
|
|
|
nameof(BookingSlotBaseApiSaveDto.BookingSlotSaleInfoList));
|
|
|
});
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
var c = _repBase.AsQueryable().Where(x => x.SLOT_BOOKING_NO == input.SLOT_BOOKING_NO).Count();
|
|
|
if (c > 0)
|
|
|
{
|
|
|
throw Oops.Bah("订舱编号已存在");
|
|
|
}
|
|
|
|
|
|
model = input.Adapt<BookingSlotBase>();
|
|
|
await _repBase.InsertAsync(model);
|
|
|
foreach (var ctn in input.CtnList)
|
|
|
{
|
|
|
var newCtn = ctn.Adapt<BookingSlotCtn>();
|
|
|
newCtn.SLOT_ID = model.Id;
|
|
|
await _repCtn.InsertAsync(newCtn);
|
|
|
}
|
|
|
|
|
|
await InsLog("Add", model.Id, "新增舱位");
|
|
|
}
|
|
|
|
|
|
//更新库存
|
|
|
await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel
|
|
|
{
|
|
|
BOOKING_SLOT_TYPE = model.BOOKING_SLOT_TYPE,
|
|
|
CARRIERID = model.CARRIERID,
|
|
|
CONTRACT_NO = model.CONTRACT_NO,
|
|
|
VESSEL = model.VESSEL,
|
|
|
VOYNO = model.VOYNO,
|
|
|
PORTLOADID = model.PORTLOADID,
|
|
|
PORTDISCHARGEID = model.PORTDISCHARGEID,
|
|
|
TenantId = model.TenantId
|
|
|
}));
|
|
|
|
|
|
return await Detail(model.Id);
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 直接在台账页数据列表上进行保存
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/SaveDataInList")]
|
|
|
public async Task<BookingSlotBaseSaveOutput> SaveDataInList(BookingSlotBaseSaveDataInListInputDto input)
|
|
|
{
|
|
|
long slotBaseId = 0;
|
|
|
if (input.SlotBase != null)
|
|
|
{
|
|
|
slotBaseId = input.SlotBase.GetLongValue("Id");
|
|
|
if (slotBaseId == 0)
|
|
|
{
|
|
|
throw Oops.Bah("修改舱位信息需要提供参数:Id");
|
|
|
}
|
|
|
|
|
|
var model = await _repBase.FirstOrDefaultAsync(x => x.Id == slotBaseId);
|
|
|
if (model == null)
|
|
|
{
|
|
|
throw Oops.Bah("未查询到舱位信息,请刷新页面");
|
|
|
}
|
|
|
|
|
|
var type = typeof(BookingSlotBase);
|
|
|
var properties = type.GetProperties();
|
|
|
List<string> updateColumns = new(input.SlotBase.Count + 3)
|
|
|
{
|
|
|
"UpdatedTime","UpdatedUserId","UpdatedUserName"
|
|
|
};
|
|
|
|
|
|
foreach (var item in input.SlotBase)
|
|
|
{
|
|
|
if (item.Key == "Id")
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
updateColumns.Add(item.Key);
|
|
|
var property = properties.FirstOrDefault(x => x.Name.Equals(item.Key, StringComparison.OrdinalIgnoreCase))
|
|
|
?? throw Oops.Bah($"参数有误,该属性不存在:{item.Key}");
|
|
|
if (property.PropertyType == typeof(string) || property.PropertyType == typeof(string))
|
|
|
{
|
|
|
property.SetValue(model, input.SlotBase.GetStringValue(item.Key));
|
|
|
}
|
|
|
else if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
|
|
|
{
|
|
|
property.SetValue(model, input.SlotBase.GetIntValue(item.Key));
|
|
|
}
|
|
|
else if (property.PropertyType == typeof(long) || property.PropertyType == typeof(long?))
|
|
|
{
|
|
|
property.SetValue(model, input.SlotBase.GetLongValue(item.Key));
|
|
|
}
|
|
|
else if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
|
|
|
{
|
|
|
property.SetValue(model, input.SlotBase.GetDateTimeValue(item.Key));
|
|
|
}
|
|
|
else if (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?))
|
|
|
{
|
|
|
property.SetValue(model, input.SlotBase.GetBooleanValue(item.Key));
|
|
|
}
|
|
|
else if (property.PropertyType == typeof(decimal) || property.PropertyType == typeof(decimal?))
|
|
|
{
|
|
|
property.SetValue(model, input.SlotBase.GetDecimalValue(item.Key));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
throw new Exception($"需要维护此类型:{property.PropertyType},请联系管理员");
|
|
|
}
|
|
|
}
|
|
|
await _repBase.AsUpdateable(model).UpdateColumns(updateColumns.ToArray()).ExecuteCommandAsync();
|
|
|
}
|
|
|
if (input.SlotSaleInfo != null)
|
|
|
{
|
|
|
var allocationId = input.SlotSaleInfo.GetLongValue("Id");
|
|
|
if (allocationId == 0)
|
|
|
{
|
|
|
throw Oops.Bah("修改订舱信息需要提供参数:Id");
|
|
|
}
|
|
|
|
|
|
var model = await _repAllocation.FirstOrDefaultAsync(x => x.Id == allocationId);
|
|
|
if (model == null)
|
|
|
{
|
|
|
throw Oops.Bah("未查询到关联的订舱信息,请刷新页面");
|
|
|
}
|
|
|
slotBaseId = model.BOOKING_SLOT_ID;
|
|
|
|
|
|
var update1 = _repAllocation.Context.Updateable<BookingSlotAllocation>();
|
|
|
var update2 = _repBookingOrder.Context.Updateable<BookingOrder>();
|
|
|
foreach (var item in input.SlotSaleInfo)
|
|
|
{
|
|
|
if (item.Key == "Id")
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
if (item.Key.Equals("CUSTOMERNAME", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
var value = input.SlotSaleInfo.GetStringValue(item.Key);
|
|
|
update1.SetColumns(x => x.CUSTOMERNAME == value);
|
|
|
update2.SetColumns(x => x.CUSTOMERNAME == value);
|
|
|
}
|
|
|
else if (item.Key.Equals("CUSTOMERID", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
var value = input.SlotSaleInfo.GetLongValue(item.Key);
|
|
|
update1.SetColumns(x => x.CUSTOMERID == value);
|
|
|
update2.SetColumns(x => x.CUSTOMERID == value);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
throw Oops.Bah($"需要维护此字段:{item.Key},请联系管理员");
|
|
|
}
|
|
|
}
|
|
|
try
|
|
|
{
|
|
|
_repAllocation.CurrentBeginTran();
|
|
|
await update1.Where(x => x.Id == model.Id).ExecuteCommandAsync();
|
|
|
await update2.Where(x => x.Id == model.BOOKING_ID).ExecuteCommandAsync();
|
|
|
_repAllocation.CurrentCommitTran();
|
|
|
|
|
|
var bookingOrderService = App.GetService<IBookingOrderService>();
|
|
|
await bookingOrderService.SendBookingOrder(new long[] { model.BOOKING_ID });
|
|
|
}
|
|
|
catch (Exception)
|
|
|
{
|
|
|
_repAllocation.CurrentRollbackTran();
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return await Detail(slotBaseId);
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 获取订舱舱位
|
|
|
/// </summary>
|
|
|
/// <param name="id"></param>
|
|
|
/// <returns></returns>
|
|
|
[HttpGet("/BookingSlot/detail")]
|
|
|
public async Task<BookingSlotBaseSaveOutput> Detail(long id)
|
|
|
{
|
|
|
var slotBase = await _repBase.FirstOrDefaultAsync(u => u.Id == id);
|
|
|
var ctns = await _repCtn.Where(x => x.SLOT_ID == id).ToListAsync();
|
|
|
|
|
|
var rtn = slotBase.Adapt<BookingSlotBaseSaveOutput>();
|
|
|
rtn.CtnList = ctns.Adapt<List<BookingSlotCtnSaveInput>>();
|
|
|
|
|
|
List<BookingLogDto> list = new List<BookingLogDto>();
|
|
|
var main = await _repBookingLog.AsQueryable().Where(u => u.BookingId == slotBase.Id).ToListAsync();
|
|
|
var mailidlist = main.Select(x => x.Id).ToList();
|
|
|
list = main.Adapt<List<BookingLogDto>>();
|
|
|
if (list != null)
|
|
|
{
|
|
|
var bookinglogdetail = await _repBookingLogDetail.AsQueryable().Where(x => mailidlist.Contains(x.PId)).ToListAsync();
|
|
|
foreach (var item in list)
|
|
|
{
|
|
|
var details = bookinglogdetail.Where(x => x.PId == item.Id).ToList();
|
|
|
item.details = details;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
rtn.LogList = list;
|
|
|
|
|
|
// 赋值关联的订舱列表
|
|
|
// 查询舱位绑定的销售信息,赋值到舱位对象中
|
|
|
List<BookingSlotSaleInfoDto> saleInfoList = await _repAllocation.AsQueryable()
|
|
|
.Where(x => x.BOOKING_SLOT_ID == id)
|
|
|
.Select(x => new BookingSlotSaleInfoDto
|
|
|
{
|
|
|
Id = x.Id,
|
|
|
BOOKING_ID = x.BOOKING_ID,
|
|
|
BOOKING_SLOT_ID = x.BOOKING_SLOT_ID,
|
|
|
CUSTOMERID = x.CUSTOMERID,
|
|
|
CUSTOMERNAME = x.CUSTOMERNAME,
|
|
|
CUSTSERVICEID = x.CUSTSERVICEID,
|
|
|
CUSTSERVICE = x.CUSTSERVICE,
|
|
|
SALEID = x.SALEID,
|
|
|
SALE = x.SALE,
|
|
|
SALE_TIME = x.SALE_TIME,
|
|
|
SHIPPER = x.SHIPPER,
|
|
|
GOODSNAME = x.GOODSNAME,
|
|
|
SELLING_PRICE = x.SELLING_PRICE
|
|
|
}).ToListAsync();
|
|
|
rtn.BookingSlotSaleInfoList = saleInfoList;
|
|
|
|
|
|
return rtn;
|
|
|
}
|
|
|
|
|
|
#region 对外接口
|
|
|
/// <summary>
|
|
|
/// 舱位接收保存、取消接口
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/ApiReceive"), AllowAnonymous, ApiUser(ApiCode = "BCTaskManage")]
|
|
|
public async Task<TaskManageOrderResultDto> ApiReceive(string jsonData, IFormFile file = null, IFormFile modifyFile = null)
|
|
|
{
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
try
|
|
|
{
|
|
|
BookingSlotBaseApiDto dto = JSON.Deserialize<BookingSlotBaseApiDto>(jsonData);
|
|
|
|
|
|
DynameFileInfo bcFile = null;
|
|
|
DynameFileInfo bcNoticeFile = null;
|
|
|
|
|
|
if (file != null)
|
|
|
{
|
|
|
bcFile = new DynameFileInfo
|
|
|
{
|
|
|
FileBytes = file.ToByteArray(),
|
|
|
FileName = file.FileName
|
|
|
};
|
|
|
}
|
|
|
|
|
|
if (modifyFile != null)
|
|
|
{
|
|
|
bcNoticeFile = new DynameFileInfo
|
|
|
{
|
|
|
FileBytes = modifyFile.ToByteArray(),
|
|
|
FileName = modifyFile.FileName
|
|
|
};
|
|
|
}
|
|
|
|
|
|
var id = InnerApiReceive(dto, bcFile, bcNoticeFile).GetAwaiter().GetResult();
|
|
|
|
|
|
result.succ = true;
|
|
|
result.msg = "成功";
|
|
|
result.ext = id;
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
result.succ = false;
|
|
|
result.msg = $"失败,原因:{ex.Message}";
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
|
/// 舱位接收保存、取消接口
|
|
|
/// </summary>
|
|
|
/// <param name="dto"></param>
|
|
|
/// <param name="file"></param>
|
|
|
/// <param name="modifyFile"></param>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/InnerApiReceive")]
|
|
|
public async Task<long> InnerApiReceive(BookingSlotBaseApiDto dto, DynameFileInfo file = null, DynameFileInfo modifyFile = null)
|
|
|
{
|
|
|
long id = 0;
|
|
|
|
|
|
//接口方法直接调用save、delete等方法会报错,可能因为非token授权登录导致,故重写一遍保存、删除代码
|
|
|
if (dto.OpType == "add" || dto.OpType == "update" || dto.OpType == "del" || dto.OpType == "cancellation")
|
|
|
{
|
|
|
//翻译船公司
|
|
|
if (!string.IsNullOrWhiteSpace(dto.DataObj.CARRIERID) && string.IsNullOrWhiteSpace(dto.DataObj.CARRIER))
|
|
|
{
|
|
|
var carrierInfo = _cache.GetAllCodeCarrier().GetAwaiter().GetResult()
|
|
|
.Where(t => t.Code.Equals(dto.DataObj.CARRIERID, StringComparison.OrdinalIgnoreCase)
|
|
|
|| t.EnName.Equals(dto.DataObj.CARRIERID, StringComparison.OrdinalIgnoreCase)
|
|
|
|| t.CnName.Equals(dto.DataObj.CARRIERID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
|
|
|
|
|
if (carrierInfo != null)
|
|
|
{
|
|
|
dto.DataObj.CARRIER = carrierInfo.CnName?.Trim();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//翻译箱型代码
|
|
|
if (dto.DataObj.CtnList != null && dto.DataObj.CtnList.Count > 0 &&
|
|
|
dto.DataObj.CtnList.Any(t => string.IsNullOrWhiteSpace(t.CTNCODE)))
|
|
|
{
|
|
|
var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList();
|
|
|
|
|
|
dto.DataObj.CtnList.ForEach(t =>
|
|
|
{
|
|
|
if (!string.IsNullOrWhiteSpace(t.CTNALL) && string.IsNullOrWhiteSpace(t.CTNCODE))
|
|
|
{
|
|
|
var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) &&
|
|
|
a.Name.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
if (ctnCode != null)
|
|
|
t.CTNCODE = ctnCode?.Code;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 解析收货地,得到装货港名称及五字码
|
|
|
if (!string.IsNullOrWhiteSpace(dto.DataObj.PLACERECEIPT))
|
|
|
{
|
|
|
var portEnName = dto.DataObj.PLACERECEIPT.Split(',')[0]?.Trim();
|
|
|
if (!string.IsNullOrWhiteSpace(portEnName))
|
|
|
{
|
|
|
var portInfo = (await _cache.GetAllCodePortLoad()).FirstOrDefault(x => x.EnName.Equals(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null)
|
|
|
{
|
|
|
dto.DataObj.PORTLOAD = portInfo.EnName;
|
|
|
dto.DataObj.PORTLOADID = portInfo.EdiCode;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
_logger.LogInformation("通过收货地城市名称未匹配到港口信息,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
_logger.LogInformation("收货地分割后得到的城市名称为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
_logger.LogInformation("收货地为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO);
|
|
|
}
|
|
|
// 解析交货地,得到为卸货港名称及五字码, 以及国家信息
|
|
|
if (!string.IsNullOrWhiteSpace(dto.DataObj.PLACEDELIVERY))
|
|
|
{
|
|
|
var portEnName = dto.DataObj.PLACEDELIVERY.Split(',')[0]?.Trim();
|
|
|
if (!string.IsNullOrWhiteSpace(portEnName))
|
|
|
{
|
|
|
var portInfo = (await _cache.GetAllCodePort()).FirstOrDefault(x => x.EnName.Equals(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null)
|
|
|
{
|
|
|
dto.DataObj.PORTDISCHARGE = portInfo.EnName;
|
|
|
dto.DataObj.PORTDISCHARGEID = portInfo.EdiCode;
|
|
|
dto.DataObj.PORTDISCHARGE_COUNTRY = portInfo.Country;
|
|
|
dto.DataObj.PORTDISCHARGE_COUNTRY_CODE = portInfo.CountryCode;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
_logger.LogInformation("通过交货地城市名称未匹配到港口信息,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
_logger.LogInformation("交货地分割后得到的城市名称为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
_logger.LogInformation("交货地为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO);
|
|
|
}
|
|
|
|
|
|
BookingSlotBase model = null;
|
|
|
if (dto.OpType == "add")
|
|
|
{
|
|
|
var c = _repBase.AsQueryable().Filter(null, true).Where(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == dto.DataObj.SLOT_BOOKING_NO).Count();
|
|
|
if (c > 0)
|
|
|
{
|
|
|
throw Oops.Bah("订舱编号已存在");
|
|
|
}
|
|
|
|
|
|
model = dto.DataObj.Adapt<BookingSlotBase>();
|
|
|
await _repBase.InsertAsync(model);
|
|
|
|
|
|
id = model.Id;
|
|
|
|
|
|
foreach (var ctn in dto.DataObj.CtnList)
|
|
|
{
|
|
|
var newCtn = ctn.Adapt<BookingSlotCtn>();
|
|
|
newCtn.SLOT_ID = model.Id;
|
|
|
await _repCtn.InsertAsync(newCtn);
|
|
|
}
|
|
|
|
|
|
await InsLog("Add", model.Id, "新增舱位");
|
|
|
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
|
//处理附件
|
|
|
if (file != null)
|
|
|
{
|
|
|
_logger.LogInformation($"请求文件名:{file.FileName}");
|
|
|
|
|
|
var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), file.FileBytes, batchNo, file.FileName, "bcfiles");
|
|
|
|
|
|
_logger.LogInformation($"保存文件路径:{fileFullPath}");
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(fileFullPath))
|
|
|
{
|
|
|
//将格式单附件写入订舱的附件
|
|
|
SaveEDIFile(id, fileFullPath, file.FileName, UserManager.TENANT_ID,
|
|
|
CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (modifyFile != null)
|
|
|
{
|
|
|
_logger.LogInformation($"请求文件名(变更文件):{modifyFile.FileName}");
|
|
|
|
|
|
var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), modifyFile.FileBytes, batchNo, modifyFile.FileName, "bcnoticefiles");
|
|
|
|
|
|
_logger.LogInformation($"保存文件路径(变更文件):{fileFullPath}");
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(fileFullPath))
|
|
|
{
|
|
|
//将格式单附件写入订舱的附件
|
|
|
SaveEDIFile(id, fileFullPath, modifyFile.FileName, UserManager.TENANT_ID,
|
|
|
CONST_BC_NOTICE_FILE_CODE, CONST_BC_NOTICE_FILE_NAME).GetAwaiter();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (dto.OpType == "update")
|
|
|
{
|
|
|
model = await _repBase.AsQueryable().Filter(null, true).FirstAsync(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == dto.DataObj.SLOT_BOOKING_NO);
|
|
|
if (model == null)
|
|
|
{
|
|
|
throw Oops.Bah($"未找到订舱编号为 {dto.DataObj.SLOT_BOOKING_NO} 的数据");
|
|
|
}
|
|
|
|
|
|
id = model.Id;
|
|
|
|
|
|
//生成待比对详情
|
|
|
TaskBCInfoDto bcSrcDto = model.Adapt<TaskBCInfoDto>();
|
|
|
TaskBCInfoDto bcTargetDto = dto.DataObj.Adapt<TaskBCInfoDto>();
|
|
|
|
|
|
//提取箱信息
|
|
|
var ctnList = _repCtn.AsQueryable().Filter(null, true)
|
|
|
.Where(x => x.IsDeleted == false && x.TenantId.Value == UserManager.TENANT_ID
|
|
|
&& x.SLOT_ID == model.Id).ToList();
|
|
|
|
|
|
if (ctnList != null)
|
|
|
{
|
|
|
bcSrcDto.CtnList = ctnList.GroupBy(x => x.CTNALL)
|
|
|
.Select(x =>
|
|
|
{
|
|
|
return new TaskBCCTNInfoDto
|
|
|
{
|
|
|
CtnALL = x.Key,
|
|
|
CTNNUM = x.ToList()
|
|
|
.Sum(a => a.CTNNUM)
|
|
|
};
|
|
|
}).ToList();
|
|
|
}
|
|
|
|
|
|
if (dto.DataObj.CtnList != null && dto.DataObj.CtnList.Count > 0)
|
|
|
{
|
|
|
bcTargetDto.CtnList = dto.DataObj.CtnList.GroupBy(x => x.CTNALL)
|
|
|
.Select(x =>
|
|
|
{
|
|
|
return new TaskBCCTNInfoDto
|
|
|
{
|
|
|
CtnALL = x.Key,
|
|
|
CTNNUM = x.ToList()
|
|
|
.Sum(a => a.CTNNUM)
|
|
|
};
|
|
|
}).ToList();
|
|
|
}
|
|
|
|
|
|
var oldObj = model.Adapt<BookingSlotBaseApiSaveDto>();
|
|
|
|
|
|
dto.DataObj.Adapt(model);
|
|
|
|
|
|
// 1.判断新的舱位信息的7个库存统计维度是否发生变化
|
|
|
// 2.如果有变化,则需要更新旧的库存信息
|
|
|
bool isNeedUpdateOldStock = false;
|
|
|
if (oldObj.VESSEL != model.VESSEL
|
|
|
|| oldObj.VOYNO != model.VOYNO
|
|
|
|| oldObj.BOOKING_SLOT_TYPE != model.BOOKING_SLOT_TYPE
|
|
|
|| oldObj.CARRIERID != model.CARRIERID
|
|
|
|| oldObj.PORTLOADID != model.PORTLOADID
|
|
|
|| oldObj.PORTDISCHARGEID != model.PORTDISCHARGEID)
|
|
|
{
|
|
|
isNeedUpdateOldStock = true;
|
|
|
}
|
|
|
await _repBase.UpdateAsync(model);
|
|
|
|
|
|
if (isNeedUpdateOldStock)
|
|
|
{
|
|
|
//更新库存
|
|
|
await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel
|
|
|
{
|
|
|
BOOKING_SLOT_TYPE = oldObj.BOOKING_SLOT_TYPE,
|
|
|
CARRIERID = oldObj.CARRIERID,
|
|
|
CONTRACT_NO = oldObj.CONTRACT_NO,
|
|
|
VESSEL = oldObj.VESSEL,
|
|
|
VOYNO = oldObj.VOYNO,
|
|
|
PORTLOADID = oldObj.PORTLOADID,
|
|
|
PORTDISCHARGEID = oldObj.PORTDISCHARGEID,
|
|
|
TenantId = model.TenantId
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
await _repCtn.DeleteAsync(x => x.SLOT_ID == model.Id);
|
|
|
foreach (var ctn in dto.DataObj.CtnList)
|
|
|
{
|
|
|
var newCtn = ctn.Adapt<BookingSlotCtn>();
|
|
|
newCtn.SLOT_ID = model.Id;
|
|
|
await _repCtn.InsertAsync(newCtn);
|
|
|
}
|
|
|
|
|
|
await InsLog("Update", model.Id, typeof(BookingSlotBaseApiSaveDto), oldObj, dto.DataObj, nameof(BookingSlotBaseApiSaveDto.CtnList), nameof(BookingSlotBaseApiSaveDto.BookingSlotSaleInfoList));
|
|
|
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
|
//处理附件
|
|
|
if (file != null)
|
|
|
{
|
|
|
_logger.LogInformation($"请求文件名:{file.FileName}");
|
|
|
|
|
|
var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), file.FileBytes, batchNo, file.FileName, "bcmoidfyfiles");
|
|
|
|
|
|
_logger.LogInformation($"保存文件路径:{fileFullPath}");
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(fileFullPath))
|
|
|
{
|
|
|
//将格式单附件写入订舱的附件
|
|
|
SaveEDIFile(id, fileFullPath, file.FileName, UserManager.TENANT_ID,
|
|
|
CONST_BC_MODIFY_FILE_CODE, CONST_BC_MODIFY_FILE_NAME).GetAwaiter();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (modifyFile != null)
|
|
|
{
|
|
|
_logger.LogInformation($"请求文件名(变更文件):{modifyFile.FileName}");
|
|
|
|
|
|
var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), modifyFile.FileBytes, batchNo, modifyFile.FileName, "bcmoidfynoticefiles");
|
|
|
|
|
|
_logger.LogInformation($"保存文件路径(变更文件):{fileFullPath}");
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(fileFullPath))
|
|
|
{
|
|
|
//将格式单附件写入订舱的附件
|
|
|
SaveEDIFile(id, fileFullPath, modifyFile.FileName, UserManager.TENANT_ID,
|
|
|
CONST_BC_MODIFY_NOTICE_FILE_CODE, CONST_BC_MODIFY_NOTICE_FILE_NAME).GetAwaiter();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//一般更新数据指的是Booking Amendment,需要与舱位进行数据比对
|
|
|
await PushCompareBCInfo(bcSrcDto, bcTargetDto, id, dto.BatchNo);
|
|
|
}
|
|
|
else if (dto.OpType == "del")
|
|
|
{
|
|
|
var slotNO = dto.DataObj.SLOT_BOOKING_NO;
|
|
|
model = await _repBase.AsQueryable().Filter(null, true).FirstAsync(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == slotNO);
|
|
|
if (model == null)
|
|
|
{
|
|
|
throw Oops.Bah($"未找到订舱编号为 {slotNO} 的数据");
|
|
|
}
|
|
|
|
|
|
id = model.Id;
|
|
|
|
|
|
model.IsDeleted = true;
|
|
|
await _repBase.UpdateAsync(model);
|
|
|
|
|
|
var ctns = await _repCtn.AsQueryable().Filter(null, true).Where(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_ID == model.Id).ToListAsync();
|
|
|
foreach (var ctn in ctns)
|
|
|
{
|
|
|
ctn.IsDeleted = true;
|
|
|
await _repCtn.UpdateAsync(ctn);
|
|
|
}
|
|
|
|
|
|
await InsLog("Del", model.Id, "取消舱位");
|
|
|
}
|
|
|
else if (dto.OpType == "cancellation")
|
|
|
{
|
|
|
// 更新标志
|
|
|
var slotNO = dto.DataObj.SLOT_BOOKING_NO;
|
|
|
model = await _repBase.AsQueryable().Filter(null, true).FirstAsync(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == slotNO);
|
|
|
if (model == null)
|
|
|
{
|
|
|
throw Oops.Bah($"未找到订舱编号为 {slotNO} 的数据");
|
|
|
}
|
|
|
|
|
|
id = model.Id;
|
|
|
model.IS_CANCELLATION = true;
|
|
|
model.CANCELLATION_DATE = DateTime.Now;
|
|
|
await _repBase.UpdateAsync(model);
|
|
|
|
|
|
// 删除该舱位相关的订舱关联关系
|
|
|
var slotList = await _repAllocation.AsQueryable().Where(a => a.BOOKING_SLOT_ID == id).ToListAsync();
|
|
|
var slotIdList = slotList.Select(s => s.Id);
|
|
|
|
|
|
await _repAllocation.Context.Updateable<BookingSlotAllocation>()
|
|
|
.SetColumns(a => a.IsDeleted == true)
|
|
|
.SetColumns(a => a.UpdatedTime == DateTime.Now)
|
|
|
.SetColumns(a => a.UpdatedUserId == UserManager.UserId)
|
|
|
.SetColumns(a => a.UpdatedUserName == UserManager.Name)
|
|
|
.Where(a => slotIdList.Contains(a.Id))
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
await _repAllocationCtn.Context.Updateable<BookingSlotAllocationCtn>()
|
|
|
.SetColumns(a => a.IsDeleted == true)
|
|
|
.SetColumns(a => a.UpdatedTime == DateTime.Now)
|
|
|
.SetColumns(a => a.UpdatedUserId == UserManager.UserId)
|
|
|
.SetColumns(a => a.UpdatedUserName == UserManager.Name)
|
|
|
.Where(a => slotIdList.Contains(a.SLOT_ALLOC_ID))
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
await InsLog("Cancellation", model.Id, "取消舱位");
|
|
|
}
|
|
|
|
|
|
//更新库存
|
|
|
await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel
|
|
|
{
|
|
|
BOOKING_SLOT_TYPE = model.BOOKING_SLOT_TYPE,
|
|
|
CARRIERID = model.CARRIERID,
|
|
|
CONTRACT_NO = model.CONTRACT_NO,
|
|
|
VESSEL = model.VESSEL,
|
|
|
VOYNO = model.VOYNO,
|
|
|
PORTLOADID = model.PORTLOADID,
|
|
|
PORTDISCHARGEID = model.PORTDISCHARGEID,
|
|
|
TenantId = model.TenantId
|
|
|
}));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
throw Oops.Bah("操作类型参数有误");
|
|
|
}
|
|
|
|
|
|
return id;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 插入日志(仅显示一条文本信息)
|
|
|
/// </summary>
|
|
|
/// <param name="type"></param>
|
|
|
/// <param name="slotId"></param>
|
|
|
/// <param name="status"></param>
|
|
|
/// <returns></returns>
|
|
|
[NonAction]
|
|
|
private async Task InsLog(string type, long slotId, string status)
|
|
|
{
|
|
|
var bid = await _repBookingLog.InsertReturnSnowflakeIdAsync(new BookingLog
|
|
|
{
|
|
|
Type = type,
|
|
|
BookingId = slotId,
|
|
|
TenantId = Convert.ToInt64(UserManager.TENANT_ID),
|
|
|
CreatedTime = DateTime.Now,
|
|
|
CreatedUserId = UserManager.UserId,
|
|
|
CreatedUserName = UserManager.Name,
|
|
|
Module = "Slot"
|
|
|
});
|
|
|
|
|
|
if (!string.IsNullOrEmpty(status))
|
|
|
{
|
|
|
await _repBookingLogDetail.InsertReturnSnowflakeIdAsync(new BookingLogDetail
|
|
|
{
|
|
|
PId = bid,
|
|
|
Field = "",
|
|
|
OldValue = "",
|
|
|
NewValue = status,
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 插入日志(比对修改内容)
|
|
|
/// </summary>
|
|
|
/// <param name="type"></param>
|
|
|
/// <param name="id"></param>
|
|
|
/// <param name="objType"></param>
|
|
|
/// <param name="objOld"></param>
|
|
|
/// <param name="objNew"></param>
|
|
|
/// <param name="excepProp"></param>
|
|
|
/// <returns></returns>
|
|
|
[NonAction]
|
|
|
private async Task InsLog(string type, long id, Type objType, object objOld, object objNew, params string[] excepProp)
|
|
|
{
|
|
|
var bid = await _repBookingLog.InsertReturnSnowflakeIdAsync(new BookingLog
|
|
|
{
|
|
|
Type = type,
|
|
|
BookingId = id,
|
|
|
TenantId = Convert.ToInt64(UserManager.TENANT_ID),
|
|
|
CreatedTime = DateTime.Now,
|
|
|
CreatedUserId = UserManager.UserId,
|
|
|
CreatedUserName = UserManager.Name,
|
|
|
Module = "Slot"
|
|
|
});
|
|
|
List<BookingLogDetail> waitInsertLogList = new();
|
|
|
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(objType))
|
|
|
{
|
|
|
if (excepProp.Contains(descriptor.Name))
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
var oldValue = descriptor.GetValue(objOld)?.ToString();
|
|
|
var newValue = descriptor.GetValue(objNew)?.ToString();
|
|
|
|
|
|
if (oldValue != newValue)
|
|
|
{
|
|
|
var fieldName = descriptor.Name;
|
|
|
if (!string.IsNullOrWhiteSpace(descriptor.Description))
|
|
|
{
|
|
|
//fieldName = descriptor.Description;
|
|
|
continue;
|
|
|
}
|
|
|
waitInsertLogList.Add(new BookingLogDetail
|
|
|
{
|
|
|
PId = bid,
|
|
|
Field = fieldName,
|
|
|
OldValue = oldValue switch { "True" => "是", "False" => "否", _ => oldValue },
|
|
|
NewValue = newValue switch { "True" => "是", "False" => "否", _ => newValue },
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
if (waitInsertLogList.Any())
|
|
|
{
|
|
|
await _repBookingLogDetail.InsertAsync(waitInsertLogList);
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 库存
|
|
|
/// <summary>
|
|
|
/// 库存查询
|
|
|
/// </summary>
|
|
|
/// <param name="input"></param>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/pageStock")]
|
|
|
public async Task<dynamic> PageStock(BookingSlotStockPageInput input)
|
|
|
{
|
|
|
var entities = await _repStock.AsQueryable()
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.VESSEL), u => u.VESSEL.Contains(input.VESSEL))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.VOYNO), u => u.VOYNO.Contains(input.VOYNO))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.CARRIER), u => u.CARRIER.Contains(input.CARRIER))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.CTN_STAT), u => u.CTN_STAT.Contains(input.CTN_STAT))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.PORTLOADID), u => u.PORTLOADID == input.PORTLOADID)
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.PORTDISCHARGEID), u => u.PORTDISCHARGEID == input.PORTDISCHARGEID)
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.PORTDISCHARGE_COUNTRY_CODE), u => u.PORTDISCHARGE_COUNTRY_CODE == input.PORTDISCHARGE_COUNTRY_CODE)
|
|
|
.WhereIF(input.ETD_START.HasValue, u => u.ETD >= input.ETD_START.Value)
|
|
|
.WhereIF(input.ETD_END.HasValue, u => u.ETD < input.ETD_END.Value.AddDays(1))
|
|
|
.WhereIF(input.ETA_START.HasValue, u => u.ETA >= input.ETA_START.Value)
|
|
|
.WhereIF(input.ETA_END.HasValue, u => u.ETA < input.ETA_END.Value.AddDays(1))
|
|
|
.ToPagedListAsync(input.PageNo, input.PageSize);
|
|
|
|
|
|
var result = entities.Adapt<SqlSugarPagedList<BookingSlotStockListOutput>>();
|
|
|
|
|
|
return result.XnPagedResult();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 刷新库存统计
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/refreshStock")]
|
|
|
public async Task RefreshStock(BookingSlotStockUpdateModel input)
|
|
|
{
|
|
|
input.TenantId = UserManager.TENANT_ID;
|
|
|
//更新库存
|
|
|
await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", input));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 重新计算某租户下面所有的库存
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/refreshAllStock")]
|
|
|
public async Task RefreshAllStock([FromBody] long telentId)
|
|
|
{
|
|
|
var logger = App.GetService<ILogger<BookingSlotStockSubscriber>>();
|
|
|
BookingSlotStockSubscriber subscriber = new BookingSlotStockSubscriber(logger);
|
|
|
var group = await _repBase.AsQueryable().Filter(null, true)
|
|
|
.Where(x => x.TenantId == telentId && x.IsDeleted == false)
|
|
|
.GroupBy(x => new
|
|
|
{
|
|
|
x.VESSEL,
|
|
|
x.VOYNO,
|
|
|
x.CARRIERID,
|
|
|
x.CONTRACT_NO,
|
|
|
x.BOOKING_SLOT_TYPE,
|
|
|
x.PORTLOADID,
|
|
|
x.PORTDISCHARGEID,
|
|
|
x.TenantId
|
|
|
}).Select(x => new
|
|
|
{
|
|
|
x.VESSEL,
|
|
|
x.VOYNO,
|
|
|
x.CARRIERID,
|
|
|
x.CONTRACT_NO,
|
|
|
x.BOOKING_SLOT_TYPE,
|
|
|
x.PORTLOADID,
|
|
|
x.PORTDISCHARGEID,
|
|
|
x.TenantId
|
|
|
}).ToListAsync();
|
|
|
foreach (var item in group)
|
|
|
{
|
|
|
BookingSlotStockUpdateModel model = item.Adapt<BookingSlotStockUpdateModel>();
|
|
|
await subscriber.BookingSlotStock(model);
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 舱位引入
|
|
|
/// <summary>
|
|
|
/// 查询可用的舱位及箱子
|
|
|
/// </summary>
|
|
|
[HttpGet("/BookingSlot/getAvailableSlots")]
|
|
|
public async Task<SqlSugarPagedList<BookingSlotBaseWithCtnDto>> GetAvailableSlots([FromQuery] BookingSlotBaseDto input, [FromQuery] PageWithTotal pageInfo)
|
|
|
{
|
|
|
var result = await GetAvailableSlots(input, null, pageInfo);
|
|
|
SqlSugarPagedList<BookingSlotBaseWithCtnDto> pageResult = new()
|
|
|
{
|
|
|
PageIndex = pageInfo.PageNo,
|
|
|
PageSize = pageInfo.PageSize,
|
|
|
TotalCount = pageInfo.Total,
|
|
|
Items = result,
|
|
|
};
|
|
|
return pageResult;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 查询可用的舱位及箱子列表
|
|
|
/// </summary>
|
|
|
/// <param name="slotInput">筛选条件1:舱位信息、箱型</param>
|
|
|
/// <param name="slotIdListInput">筛选条件2:舱位主键列表</param>
|
|
|
/// <param name="pageInfo">筛选条件3:分页</param>
|
|
|
/// <returns>可用的舱位列表(含可用的箱子列表)</returns>
|
|
|
[NonAction]
|
|
|
public async Task<List<BookingSlotBaseWithCtnDto>> GetAvailableSlots(BookingSlotBaseDto slotInput = null,
|
|
|
List<long> slotIdListInput = null,
|
|
|
PageWithTotal pageInfo = null)
|
|
|
{
|
|
|
slotInput ??= new();
|
|
|
slotIdListInput ??= new();
|
|
|
|
|
|
string[] ctnCodeList = null;
|
|
|
if (!string.IsNullOrEmpty(slotInput.CTN_STAT))
|
|
|
{
|
|
|
ctnCodeList = slotInput.CTN_STAT.Split(',');
|
|
|
}
|
|
|
|
|
|
// 1. 【舱位基础表】与【箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【总的箱量】,作为queryable1
|
|
|
var queryable1 = _repBase.Context.Queryable<BookingSlotBase, BookingSlotCtn>((bas, ctn) => bas.Id == ctn.SLOT_ID)
|
|
|
.Where(bas => bas.IS_CANCELLATION == false)
|
|
|
.WhereIF(!string.IsNullOrEmpty(slotInput.SLOT_BOOKING_NO), bas => bas.SLOT_BOOKING_NO == slotInput.SLOT_BOOKING_NO)
|
|
|
.WhereIF(!string.IsNullOrEmpty(slotInput.PORTLOAD), bas => bas.PORTLOAD.Contains(slotInput.PORTLOAD))
|
|
|
.WhereIF(!string.IsNullOrEmpty(slotInput.PORTDISCHARGE), bas => bas.PORTLOAD.Contains(slotInput.PORTLOAD))
|
|
|
.WhereIF(!string.IsNullOrEmpty(slotInput.VESSEL), bas => bas.VESSEL.Contains(slotInput.VESSEL))
|
|
|
.WhereIF(!string.IsNullOrEmpty(slotInput.VOYNO), bas => bas.VOYNO.Contains(slotInput.VOYNO))
|
|
|
.WhereIF(!string.IsNullOrEmpty(slotInput.CARRIAGE_TYPE), bas => bas.CARRIAGE_TYPE == slotInput.CARRIAGE_TYPE)
|
|
|
.WhereIF(!string.IsNullOrEmpty(slotInput.BOOKING_SLOT_TYPE), bas => bas.BOOKING_SLOT_TYPE == slotInput.BOOKING_SLOT_TYPE)
|
|
|
.WhereIF(ctnCodeList != null, (bas, ctn) => ctnCodeList.Contains(ctn.CTNCODE))
|
|
|
.WhereIF(slotIdListInput.Any(), (bas) => slotIdListInput.Contains(bas.Id))
|
|
|
.GroupBy((bas, ctn) => new
|
|
|
{
|
|
|
bas.Id,
|
|
|
ctn.CTNCODE
|
|
|
})
|
|
|
.Select((bas, ctn) => new
|
|
|
{
|
|
|
id = bas.Id,
|
|
|
ctnCode = ctn.CTNCODE,
|
|
|
numAll = SqlFunc.AggregateSum(ctn.CTNNUM)
|
|
|
})
|
|
|
.MergeTable();
|
|
|
|
|
|
// 2. 【已引入舱位表】与【已使用的箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【已使用的箱量】,作为queryable2
|
|
|
var queryable2 = _repBase.Context.Queryable<BookingSlotAllocation, BookingSlotAllocationCtn>((alc, ctn) => alc.Id == ctn.SLOT_ALLOC_ID)
|
|
|
.GroupBy((alc, ctn) => new
|
|
|
{
|
|
|
alc.BOOKING_SLOT_ID,
|
|
|
ctn.CTNCODE
|
|
|
})
|
|
|
.Select((alc, ctn) => new
|
|
|
{
|
|
|
id = alc.BOOKING_SLOT_ID,
|
|
|
ctnCode = ctn.CTNCODE,
|
|
|
numUse = SqlFunc.AggregateSum(ctn.CTNNUM)
|
|
|
})
|
|
|
.MergeTable();
|
|
|
|
|
|
// 3. 将queryable1 左连接 queryable2,使用【总的箱量】减去【已使用的箱量】,得到【剩余的箱量】,添加【剩余的箱量】> 0 的条件,作为queryable3
|
|
|
var queryable3 = queryable1.LeftJoin(queryable2, (q1, q2) => q1.id == q2.id && q1.ctnCode == q2.ctnCode)
|
|
|
.Select((q1, q2) => new
|
|
|
{
|
|
|
q1.id,
|
|
|
q1.ctnCode,
|
|
|
numResidue = SqlFunc.IsNull(q1.numAll - q2.numUse, q1.numAll)
|
|
|
})
|
|
|
.MergeTable()
|
|
|
.Where(r => r.numResidue > 0);
|
|
|
|
|
|
// 4. 执行ToList(),得到可用的【舱位主键】、【箱型】、【箱量】列表
|
|
|
RefAsync<int> total = 0;
|
|
|
var canUselist = pageInfo == null
|
|
|
? await queryable3.ToListAsync()
|
|
|
: await queryable3.ToPageListAsync(pageInfo.PageNo, pageInfo.PageSize, total);
|
|
|
|
|
|
if (pageInfo != null)
|
|
|
{
|
|
|
pageInfo.Total = total;
|
|
|
}
|
|
|
|
|
|
// 查询舱位列表
|
|
|
var baseIdList = canUselist.Select(c => c.id);
|
|
|
List<BookingSlotBase> baseList = await _repBase.AsQueryable()
|
|
|
.Where(u => baseIdList.Contains(u.Id))
|
|
|
.ToListAsync();
|
|
|
|
|
|
List<Core.Entity.CodeCtn> ctnCodeCache = await _cache.GetAllCodeCtn();
|
|
|
|
|
|
// 构建结果
|
|
|
List<BookingSlotBaseWithCtnDto> result = baseList.Adapt<List<BookingSlotBaseWithCtnDto>>();
|
|
|
foreach (var item in result)
|
|
|
{
|
|
|
var ctnList = canUselist.Where(c => c.id == item.Id).ToList();
|
|
|
if (ctnList?.Any() == true)
|
|
|
{
|
|
|
item.CtnList = ctnList.Select(c => new BookingSlotCtnDto()
|
|
|
{
|
|
|
CTNCODE = c.ctnCode,
|
|
|
CTNNUM = c.numResidue,
|
|
|
CTNALL = ctnCodeCache.FirstOrDefault(e => e.Code == c.ctnCode)?.Name ?? throw new Exception($"舱位信息中存在未收录的箱型:{c.ctnCode},需要在箱型字典中补充"),
|
|
|
}).ToList();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 检查指定订舱记录,是否可以引入舱位列表
|
|
|
/// </summary>
|
|
|
/// <param name="slots">待引入的舱位列表</param>
|
|
|
/// <param name="bookingOrderId">待关联的订舱记录</param>
|
|
|
/// <returns>isExists:指定订舱记录是否已经引入过舱位数据,isEnough:现有舱位及箱子是否满足需求,message:提示信息</returns>
|
|
|
[NonAction]
|
|
|
public async Task<(bool isExists, bool isEnough, string message)> CheckImportSlots(List<BookingSlotBaseWithCtnDto> slots, long bookingOrderId)
|
|
|
{
|
|
|
slots ??= new List<BookingSlotBaseWithCtnDto>();
|
|
|
|
|
|
// 判断是否已存在引用关系
|
|
|
if (bookingOrderId != 0 && await _repAllocation.IsExistsAsync(a => a.BOOKING_ID == bookingOrderId))
|
|
|
{
|
|
|
return (true, false, $"订舱主键{bookingOrderId}已引用舱位");
|
|
|
}
|
|
|
var slotIdList = slots.Select(s => s.Id).ToList();
|
|
|
|
|
|
// 查询可用舱位及箱子列表
|
|
|
var latestSlotList = await GetAvailableSlots(null, slotIdList);
|
|
|
|
|
|
// 判断余量是否满足需求
|
|
|
foreach (var inSlotItem in slots)
|
|
|
{
|
|
|
var latestSlot = latestSlotList.FirstOrDefault(b => b.Id == inSlotItem.Id);
|
|
|
if (latestSlot == null)
|
|
|
{
|
|
|
return (false, false, $"订舱编号为{inSlotItem.SLOT_BOOKING_NO}的舱位已被占用或取消,请重新引入");
|
|
|
}
|
|
|
if (inSlotItem.CtnList?.Any() == false)
|
|
|
{
|
|
|
return (false, false, $"每个舱位至少选择一个箱子,订舱编号:{inSlotItem.SLOT_BOOKING_NO}");
|
|
|
}
|
|
|
foreach (var inCtnItem in inSlotItem.CtnList)
|
|
|
{
|
|
|
var latestCtn = latestSlot.CtnList.FirstOrDefault(c => c.CTNCODE == inCtnItem.CTNCODE);
|
|
|
if (latestCtn == null)
|
|
|
{
|
|
|
return (false, false, $"订舱编号为{latestSlot.SLOT_BOOKING_NO}的舱位中,箱型为{inCtnItem.CTNALL}的箱子已被占用或取消,请重新引入");
|
|
|
}
|
|
|
if (latestCtn.CTNNUM < inCtnItem.CTNNUM)
|
|
|
{
|
|
|
return (false, false, $"订舱编号为{latestSlot.SLOT_BOOKING_NO}的舱位中,箱型为{inCtnItem.CTNALL}的箱子当前剩余{latestCtn.CTNNUM}个,少于所需的{inCtnItem.CTNNUM}个,请重新引入");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return (false, true, $"可以引入");
|
|
|
}
|
|
|
public static object ImportLockObj = new object();
|
|
|
|
|
|
/// <summary>
|
|
|
/// 为指定订舱记录引入舱位信息
|
|
|
/// </summary>
|
|
|
/// <param name="slots">待引入的舱位列表</param>
|
|
|
/// <param name="bookingOrderId">待关联的订舱记录</param>
|
|
|
/// <param name="isCheck">是否进行剩余量检查</param>
|
|
|
/// <returns>isSuccess:检查(余量及已引用检查)是否成功通过,message:提示信息</returns>
|
|
|
[NonAction]
|
|
|
public async Task<(bool isSuccess, string message)> ImportSlots(List<BookingSlotBaseWithCtnDto> slots, long bookingOrderId, bool isCheck)
|
|
|
{
|
|
|
slots ??= new List<BookingSlotBaseWithCtnDto>();
|
|
|
|
|
|
Monitor.Enter(ImportLockObj);
|
|
|
try
|
|
|
{
|
|
|
if (isCheck)
|
|
|
{
|
|
|
(bool isExists, bool isEnough, string message) checkResult = await CheckImportSlots(slots, bookingOrderId);
|
|
|
|
|
|
if (checkResult.isExists || !checkResult.isEnough)
|
|
|
return (false, checkResult.message);
|
|
|
}
|
|
|
|
|
|
var slotIdList = slots.Select(s => s.Id).ToList();
|
|
|
List<BookingSlotBase> latestSlotList = await _repBase.AsQueryable().Where(b => slotIdList.Contains(b.Id)).ToListAsync();
|
|
|
foreach (var inSlotItem in slots)
|
|
|
{
|
|
|
var latestSlot = latestSlotList.First(b => b.Id == inSlotItem.Id);
|
|
|
|
|
|
var config = new TypeAdapterConfig();
|
|
|
config.ForType<BookingSlotBase, BookingSlotAllocation>()
|
|
|
.Ignore(dest => dest.CreatedTime)
|
|
|
.Ignore(dest => dest.UpdatedTime)
|
|
|
.Ignore(dest => dest.CreatedUserId)
|
|
|
.Ignore(dest => dest.UpdatedUserId)
|
|
|
.Ignore(dest => dest.CreatedUserName)
|
|
|
.Ignore(dest => dest.UpdatedUserName)
|
|
|
.Ignore(dest => dest.TenantId)
|
|
|
.Ignore(dest => dest.TenantName);
|
|
|
|
|
|
var newSlotAllocation = latestSlot.Adapt<BookingSlotAllocation>(config);
|
|
|
newSlotAllocation.Id = 0;
|
|
|
newSlotAllocation.BOOKING_SLOT_ID = latestSlot.Id;
|
|
|
newSlotAllocation.BOOKING_ID = bookingOrderId;
|
|
|
newSlotAllocation.ALLO_BILL_NO = latestSlot.SLOT_BOOKING_NO;
|
|
|
newSlotAllocation.FINAL_BILL_NO = latestSlot.SLOT_BOOKING_NO;
|
|
|
await _repAllocation.InsertAsync(newSlotAllocation);
|
|
|
|
|
|
var insertCtnList = inSlotItem.CtnList.Select(c => new BookingSlotAllocationCtn()
|
|
|
{
|
|
|
SLOT_ALLOC_ID = newSlotAllocation.Id,
|
|
|
CTNCODE = c.CTNCODE,
|
|
|
CTNALL = c.CTNALL,
|
|
|
CTNNUM = c.CTNNUM
|
|
|
});
|
|
|
await _repAllocationCtn.InsertAsync(insertCtnList);
|
|
|
|
|
|
// 更新库存
|
|
|
await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new Event.BookingSlotStockUpdateModel
|
|
|
{
|
|
|
BOOKING_SLOT_TYPE = latestSlot.BOOKING_SLOT_TYPE,
|
|
|
CARRIERID = latestSlot.CARRIERID,
|
|
|
CONTRACT_NO = latestSlot.CONTRACT_NO,
|
|
|
VESSEL = latestSlot.VESSEL,
|
|
|
VOYNO = latestSlot.VOYNO,
|
|
|
PORTLOADID = latestSlot.PORTLOADID,
|
|
|
PORTDISCHARGEID = latestSlot.PORTDISCHARGEID,
|
|
|
TenantId = latestSlot.TenantId,
|
|
|
}));
|
|
|
}
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
Monitor.Exit(ImportLockObj);
|
|
|
}
|
|
|
|
|
|
return (true, "引入成功");
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 获取附件
|
|
|
/// <summary>
|
|
|
/// 获取附件
|
|
|
/// </summary>
|
|
|
/// <param name="id">舱位主键</param>
|
|
|
/// <returns>返回附件列表</returns>
|
|
|
[HttpGet("/BookingSlot/GetFile")]
|
|
|
public async Task<List<BookingFile>> GetFile(long id)
|
|
|
{
|
|
|
var list = await _bookingfile.AsQueryable().Filter(null, true)
|
|
|
.Where(u => u.BookingId == id && u.Moudle == "BookingSlot").ToListAsync();
|
|
|
|
|
|
return list;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 舱位
|
|
|
/// <summary>
|
|
|
/// 分页查询订舱舱位
|
|
|
/// </summary>
|
|
|
[HttpPost("/BookingSlot/page")]
|
|
|
public async Task<dynamic> Page(BookingSlotBasePageInput input)
|
|
|
{
|
|
|
ISugarQueryable<BookingSlotBase> select = null;
|
|
|
|
|
|
// 箱型筛选
|
|
|
string[] ctnCodeArr = null;
|
|
|
if (!string.IsNullOrEmpty(input.CTN_STAT))
|
|
|
{
|
|
|
ctnCodeArr = input.CTN_STAT.Split(',');
|
|
|
select = _repBase.AsQueryable().InnerJoin<BookingSlotCtn>((u, c) => u.Id == c.SLOT_ID);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
select = _repBase.AsQueryable();
|
|
|
}
|
|
|
|
|
|
select = select.WhereIF(!string.IsNullOrEmpty(input.SLOT_BOOKING_NO), u => u.SLOT_BOOKING_NO.Contains(input.SLOT_BOOKING_NO))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.SLOT_BOOKING_NO), u => u.SLOT_BOOKING_NO.Contains(input.SLOT_BOOKING_NO))
|
|
|
.WhereIF(input.STATUS == 1, u => !u.IS_CANCELLATION)
|
|
|
.WhereIF(input.STATUS == 2, u => u.IS_CANCELLATION)
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.VESSEL), u => u.VESSEL.Contains(input.VESSEL))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.VOYNO), u => u.VOYNO.Contains(input.VOYNO))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.PORTLOAD), u => u.PORTLOAD.Contains(input.PORTLOAD))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.PORTDISCHARGE), u => u.PORTDISCHARGE.Contains(input.PORTDISCHARGE))
|
|
|
|
|
|
.WhereIF(input.ETD_START.HasValue, u => u.ETD >= input.ETD_START.Value)
|
|
|
.WhereIF(input.ETD_END.HasValue, u => u.ETD < input.ETD_END.Value.AddDays(1))
|
|
|
.WhereIF(input.ETA_START.HasValue, u => u.ETA >= input.ETA_START.Value)
|
|
|
.WhereIF(input.ETA_END.HasValue, u => u.ETA < input.ETA_END.Value.AddDays(1))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.CreatedUserName), u => u.CreatedUserName.Contains(input.CreatedUserName))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.UpdatedUserName), u => u.UpdatedUserName.Contains(input.UpdatedUserName))
|
|
|
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.CARRIER), u => u.CARRIER.Contains(input.CARRIER))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.CONTRACT_NO), u => u.CONTRACT_NO.Contains(input.CONTRACT_NO))
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.SI_RLT_STAT), u => u.SI_RLT_STAT == input.SI_RLT_STAT)
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.VGM_RLT_STAT), u => u.VGM_RLT_STAT == input.VGM_RLT_STAT)
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.CARRIAGE_TYPE), u => u.CARRIAGE_TYPE == input.CARRIAGE_TYPE)
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.BOOKING_SLOT_TYPE), u => u.BOOKING_SLOT_TYPE == input.BOOKING_SLOT_TYPE)
|
|
|
|
|
|
.WhereIF(!string.IsNullOrEmpty(input.LANENAME), u => u.LANENAME.Contains(input.LANENAME))
|
|
|
.WhereIF(input.WEEK_AT != null, u => u.WEEK_AT == input.WEEK_AT)
|
|
|
|
|
|
// 标签筛选
|
|
|
.WhereIF(input.LabelIdArray != null && input.LabelIdArray.Length > 0,
|
|
|
u => SqlFunc.Subqueryable<BookingLabelAllocation>()
|
|
|
.Where(x => x.BusinessId == u.Id && input.LabelIdArray.Contains(x.LabelId))
|
|
|
.Any());
|
|
|
|
|
|
if (ctnCodeArr != null && ctnCodeArr.Length > 0)
|
|
|
{
|
|
|
var tempSelect = select as ISugarQueryable<BookingSlotBase, BookingSlotCtn>;
|
|
|
tempSelect.Where((u, c) => ctnCodeArr.Contains(c.CTNCODE));
|
|
|
}
|
|
|
//var sql = select.OrderByDescending(u => u.CreatedTime).ToSqlString();
|
|
|
|
|
|
var entities = await select.OrderByDescending(u => u.CreatedTime)
|
|
|
.ToPagedListAsync(input.PageNo, input.PageSize);
|
|
|
|
|
|
var result = entities.Adapt<SqlSugarPagedList<BookingSlotBaseListOutput>>();
|
|
|
|
|
|
// 查询舱位绑定的销售信息,赋值到舱位对象中
|
|
|
var slotIds = result.Items.Select(x => x.Id);
|
|
|
if (slotIds.Any())
|
|
|
{
|
|
|
List<BookingSlotSaleInfoDto> allocationInfoList = await _repAllocation.AsQueryable()
|
|
|
.Where(x => slotIds.Contains(x.BOOKING_SLOT_ID))
|
|
|
.Select(x => new BookingSlotSaleInfoDto
|
|
|
{
|
|
|
Id = x.Id,
|
|
|
BOOKING_ID = x.BOOKING_ID,
|
|
|
BOOKING_SLOT_ID = x.BOOKING_SLOT_ID,
|
|
|
CUSTOMERID = x.CUSTOMERID,
|
|
|
CUSTOMERNAME = x.CUSTOMERNAME,
|
|
|
CUSTSERVICEID = x.CUSTSERVICEID,
|
|
|
CUSTSERVICE = x.CUSTSERVICE,
|
|
|
SALEID = x.SALEID,
|
|
|
SALE = x.SALE,
|
|
|
SALE_TIME = x.SALE_TIME,
|
|
|
SHIPPER = x.SHIPPER,
|
|
|
GOODSNAME = x.GOODSNAME,
|
|
|
SELLING_PRICE = x.SELLING_PRICE
|
|
|
}).ToListAsync();
|
|
|
|
|
|
var group = allocationInfoList.GroupBy(x => x.BOOKING_SLOT_ID);
|
|
|
foreach (var item in group)
|
|
|
{
|
|
|
var slot = result.Items.FirstOrDefault(x => x.Id == item.Key);
|
|
|
if (slot != null)
|
|
|
{
|
|
|
slot.BookingSlotSaleInfoList = item.ToList();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return result.XnPagedResult();
|
|
|
}
|
|
|
#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 推送BC变更比对
|
|
|
/// <summary>
|
|
|
/// 推送BC变更比对
|
|
|
/// </summary>
|
|
|
/// <param name="bcSrcDto">原舱位详情</param>
|
|
|
/// <param name="bcTargetDto">变更后舱位详情</param>
|
|
|
/// <param name="slotId">舱位主键</param>
|
|
|
/// <param name="reqBatchNo">请求批次号用来区分对应的哪个批次任务</param>
|
|
|
/// <returns></returns>
|
|
|
[NonAction]
|
|
|
public async Task PushCompareBCInfo(TaskBCInfoDto bcSrcDto, TaskBCInfoDto bcTargetDto, long slotId, string reqBatchNo)
|
|
|
{
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
|
DateTime bDate = DateTime.Now;
|
|
|
|
|
|
var compareResult = await ExcuteCompare(bcSrcDto, bcTargetDto);
|
|
|
|
|
|
DateTime eDate = DateTime.Now;
|
|
|
TimeSpan ts = eDate.Subtract(bDate);
|
|
|
var timeDiff = ts.TotalMilliseconds;
|
|
|
|
|
|
if (compareResult != null)
|
|
|
{
|
|
|
_logger.LogInformation("批次={no} 请求完成,耗时:{timeDiff}ms. 结果{msg}", batchNo, timeDiff, compareResult.succ ? "成功" : "失败");
|
|
|
}
|
|
|
|
|
|
if (compareResult != null)
|
|
|
{
|
|
|
DateTime nowDate = DateTime.Now;
|
|
|
|
|
|
var hisInfo = _bookingSlotCompareRepository.AsQueryable().Filter(null, true).First(a => a.COMPARE_BATCHNO == reqBatchNo);
|
|
|
|
|
|
if (hisInfo == null)
|
|
|
{
|
|
|
BookingSlotCompare entity = new BookingSlotCompare
|
|
|
{
|
|
|
SLOT_ID = slotId,
|
|
|
COMPARE_BATCHNO = reqBatchNo,
|
|
|
COMPARE_DIFF_NUM = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0,
|
|
|
CreatedTime = nowDate,
|
|
|
UpdatedTime = nowDate,
|
|
|
CreatedUserId = UserManager.UserId,
|
|
|
CreatedUserName = UserManager.Name,
|
|
|
UpdatedUserId = UserManager.UserId,
|
|
|
UpdatedUserName = UserManager.Name,
|
|
|
COMPARE_TYPE = "BC_MODIFY",
|
|
|
COMPARE_RLT = JSON.Serialize(compareResult.extra.ShowDetailList),
|
|
|
};
|
|
|
|
|
|
await _bookingSlotCompareRepository.InsertAsync(entity);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
hisInfo.COMPARE_DIFF_NUM = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0;
|
|
|
hisInfo.UpdatedTime = nowDate;
|
|
|
hisInfo.UpdatedUserId = UserManager.UserId;
|
|
|
hisInfo.UpdatedUserName = UserManager.Name;
|
|
|
|
|
|
hisInfo.COMPARE_RLT = JSON.Serialize(compareResult.extra.ShowDetailList);
|
|
|
|
|
|
await _bookingSlotCompareRepository.AsUpdateable(hisInfo).UpdateColumns(it =>
|
|
|
new
|
|
|
{
|
|
|
it.COMPARE_DIFF_NUM,
|
|
|
it.COMPARE_RLT,
|
|
|
it.UpdatedTime,
|
|
|
it.UpdatedUserId,
|
|
|
it.UpdatedUserName
|
|
|
}).ExecuteCommandAsync();
|
|
|
}
|
|
|
//throw Oops.Oh($"舱位主键{slotId}请求BC比对失败,返回为空");
|
|
|
|
|
|
if (compareResult.extra.ShowDetailList == null || compareResult.extra.ShowDetailList.Count == 0)
|
|
|
{
|
|
|
new EmailNoticeHelper().SendEmailNotice($"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,比对结果为0", $"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,比对结果为0", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
new EmailNoticeHelper().SendEmailNotice($"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,未获取到比对结果", $"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,未获取到比对结果", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList());
|
|
|
}
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 请求BC比对
|
|
|
/// <summary>
|
|
|
/// 请求BC比对
|
|
|
/// </summary>
|
|
|
/// <param name="bcSrcDto">BC详情</param>
|
|
|
/// <param name="bcTargetDto">BC变更后详情</param>
|
|
|
/// <returns>返回回执</returns>
|
|
|
[NonAction]
|
|
|
public async Task<TaskManageExcuteResultDto> ExcuteCompare(TaskBCInfoDto bcSrcDto, TaskBCInfoDto bcTargetDto)
|
|
|
{
|
|
|
TaskManageExcuteResultDto model = null;
|
|
|
/*
|
|
|
1、读取配置文件中的规则引擎URL
|
|
|
2、填充请求的类,并生成JSON报文
|
|
|
3、POST请求接口,并记录回执。
|
|
|
4、返回信息。
|
|
|
*/
|
|
|
|
|
|
var url = App.Configuration["BCCompareUrl"];
|
|
|
|
|
|
using (var httpClient = new HttpClient())
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
using (var reduceAttach = new MultipartFormDataContent())
|
|
|
{
|
|
|
var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(JSON.Serialize(bcSrcDto)));
|
|
|
|
|
|
dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data")
|
|
|
{
|
|
|
Name = "srcJson"
|
|
|
};
|
|
|
|
|
|
reduceAttach.Add(dataContent);
|
|
|
|
|
|
var dataContent2 = new ByteArrayContent(Encoding.UTF8.GetBytes(JSON.Serialize(bcTargetDto)));
|
|
|
|
|
|
dataContent2.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data")
|
|
|
{
|
|
|
Name = "destJson"
|
|
|
};
|
|
|
|
|
|
reduceAttach.Add(dataContent2);
|
|
|
|
|
|
//请求
|
|
|
var response = httpClient.PostAsync(url, reduceAttach).Result;
|
|
|
|
|
|
var result = response.Content.ReadAsStringAsync().Result;
|
|
|
|
|
|
model = JSON.Deserialize<TaskManageExcuteResultDto>(result);
|
|
|
|
|
|
_logger.LogInformation($"推送BC比返回结果:{JSON.Serialize(model)}");
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_logger.LogInformation("推送BC比对异常,原因:{error}", ex.Message);
|
|
|
|
|
|
// throw Oops.Oh($"推送BC比对异常,原因:{ex.Message}");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return model;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 获取舱位变更比对结果
|
|
|
/// <summary>
|
|
|
/// 获取舱位变更比对结果
|
|
|
/// </summary>
|
|
|
/// <param name="id">舱位主键</param>
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
/// <returns>返回舱位变更比对结果</returns>
|
|
|
[HttpGet("/BookingSlot/GetSlotCompareResult")]
|
|
|
public async Task<List<CompareResultDetailInfo>> GetSlotCompareResult([FromQuery] long id, [FromQuery] string batchNo)
|
|
|
{
|
|
|
var compareInfo = await _bookingSlotCompareRepository.AsQueryable().Filter(null, true)
|
|
|
.FirstAsync(t => t.SLOT_ID == id && t.COMPARE_BATCHNO == batchNo);
|
|
|
|
|
|
if (compareInfo == null)
|
|
|
{
|
|
|
throw Oops.Oh($"舱位变更比对结果不存在");
|
|
|
}
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(compareInfo.COMPARE_RLT))
|
|
|
{
|
|
|
throw Oops.Oh($"获取舱位变更比对结果错误,比对内容不存在");
|
|
|
}
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(compareInfo.COMPARE_RLT))
|
|
|
{
|
|
|
return JSON.Deserialize<List<CompareResultDetailInfo>>(compareInfo.COMPARE_RLT);
|
|
|
}
|
|
|
|
|
|
return new List<CompareResultDetailInfo>();
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 导入舱位
|
|
|
/// <summary>
|
|
|
/// 导入舱位
|
|
|
/// </summary>
|
|
|
/// <param name="file">导入舱位文件</param>
|
|
|
/// <returns>返回回执</returns>
|
|
|
[HttpPost("/BookingSlot/ImportSlotFromFile")]
|
|
|
public async Task<TaskManageOrderResultDto> ImportSlotFromFile(IFormFile file)
|
|
|
{
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
try
|
|
|
{
|
|
|
/*
|
|
|
1、只支持Excel导入
|
|
|
2、
|
|
|
*/
|
|
|
result.succ = true;
|
|
|
result.msg = "导入成功";
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_logger.LogError($"导入舱位异常,原因:{ex.Message}");
|
|
|
|
|
|
result.succ = false;
|
|
|
result.msg = $"导入舱位异常,原因:{ex.Message}";
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 生成订舱订单
|
|
|
/// <summary>
|
|
|
/// 生成订舱订单
|
|
|
/// </summary>
|
|
|
/// <param name="model">生成订舱订单请求</param>
|
|
|
/// <returns>返回回执</returns>
|
|
|
[HttpPost("/BookingSlot/CreateBookingOrder")]
|
|
|
public async Task<TaskManageOrderResultDto> CreateBookingOrder(BookingGenerateDto model)
|
|
|
{
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
if (!model.SlotId.HasValue || model.SlotId.Value < 1)
|
|
|
throw Oops.Oh($"舱位ID不能为空");
|
|
|
|
|
|
var slotInfo = await _repBase.AsQueryable()
|
|
|
.FirstAsync(a => a.Id == model.SlotId.Value);
|
|
|
|
|
|
if (slotInfo == null)
|
|
|
throw Oops.Oh($"舱位信息不存在或已作废");
|
|
|
|
|
|
// 判断是否已存在引用关系
|
|
|
if (_repAllocation.IsExists(a => a.BOOKING_SLOT_ID == slotInfo.Id
|
|
|
&& a.BOOKING_ID > 0 && a.IsDeleted == false))
|
|
|
{
|
|
|
throw Oops.Oh($"舱位已有对应的订舱订单,不能重复执行");
|
|
|
}
|
|
|
|
|
|
var ctnList = _repCtn.AsQueryable().Where(a => a.SLOT_ID == slotInfo.Id && a.IsDeleted == false)
|
|
|
.ToList();
|
|
|
|
|
|
var fileList = await _bookingfile.AsQueryable().Filter(null, true)
|
|
|
.Where(u => u.BookingId == slotInfo.Id && u.Moudle == "BookingSlot").ToListAsync();
|
|
|
|
|
|
var bookingOrderId = await GenerateBookingOrder(slotInfo, ctnList, fileList, model);
|
|
|
|
|
|
result.succ = true;
|
|
|
result.msg = "成功";
|
|
|
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 生成订舱
|
|
|
/// <summary>
|
|
|
/// 生成订舱
|
|
|
/// </summary>
|
|
|
/// <param name="bookingSlotBase">舱位详情</param>
|
|
|
/// <param name="bookingSlotCtnList">舱位集装箱列表</param>
|
|
|
/// <param name="bookingSlotFileList">舱位附件列表</param>
|
|
|
/// <param name="generateModel">订舱请求详情</param>
|
|
|
/// <returns>返回订舱ID</returns>
|
|
|
private async Task<long> GenerateBookingOrder(BookingSlotBase bookingSlotBase, List<BookingSlotCtn> bookingSlotCtnList,
|
|
|
List<BookingFile> bookingSlotFileList,
|
|
|
BookingGenerateDto generateModel)
|
|
|
{
|
|
|
long id = 0;
|
|
|
|
|
|
try
|
|
|
{
|
|
|
/*
|
|
|
1、新增订舱
|
|
|
2、推送服务项目
|
|
|
3、推送附件
|
|
|
*/
|
|
|
|
|
|
var carrierInfo = _cache.GetAllCodeCarrier().GetAwaiter().GetResult()
|
|
|
.Where(t => t.Code.Equals(bookingSlotBase.CARRIERID, StringComparison.OrdinalIgnoreCase)
|
|
|
|| t.EnName.Equals(bookingSlotBase.CARRIERID, StringComparison.OrdinalIgnoreCase)
|
|
|
|| t.CnName.Equals(bookingSlotBase.CARRIERID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
|
|
|
|
|
SaveBookingOrderInput bkModel = new SaveBookingOrderInput
|
|
|
{
|
|
|
CUSTOMERID = generateModel.CustomerId,
|
|
|
CUSTOMERNAME = generateModel.CustomerName,
|
|
|
CARRIERID = carrierInfo.Code?.Trim(),
|
|
|
CARRIER = carrierInfo.CnName?.Trim(),
|
|
|
MBLNO = bookingSlotBase.SLOT_BOOKING_NO.Trim(),
|
|
|
CONTRACTNO = !string.IsNullOrWhiteSpace(bookingSlotBase.CONTRACT_NO) ? bookingSlotBase.CONTRACT_NO : "",
|
|
|
VESSEL = bookingSlotBase.VESSEL.ToUpper().Trim(),
|
|
|
VOYNO = bookingSlotBase.VOYNO.ToUpper().Trim(),
|
|
|
VOYNOINNER = bookingSlotBase.VOYNO.ToUpper().Trim(),
|
|
|
ETD = bookingSlotBase.ETD,
|
|
|
ETA = bookingSlotBase.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 = bookingSlotBase.VGM_SUBMISSION_CUT_DATE,
|
|
|
CLOSINGDATE = bookingSlotBase.CY_CUT_DATE,
|
|
|
CLOSEDOCDATE = bookingSlotBase.SI_CUT_DATE,
|
|
|
CUSTSERVICEID = generateModel.CustServiceId.HasValue ? generateModel.CustServiceId.Value.ToString() : null,
|
|
|
CUSTSERVICE = generateModel.CustServiceName,
|
|
|
ctnInputs = new List<BookingCtnDto>()
|
|
|
};
|
|
|
|
|
|
var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList();
|
|
|
|
|
|
if (bookingSlotCtnList.Count > 0)
|
|
|
{
|
|
|
bookingSlotCtnList.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
|
|
|
};
|
|
|
|
|
|
bkModel.ctnInputs.Add(ctn);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
var bookingOrderService = _namedBookingOrderServiceProvider.GetService<ITransient>(nameof(BookingOrderService));
|
|
|
var bkRlt = await bookingOrderService.Save(bkModel);
|
|
|
|
|
|
id = bkRlt.Id;
|
|
|
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
|
if (id > 0)
|
|
|
{
|
|
|
List<BookingSlotBaseWithCtnDto> slots = new List<BookingSlotBaseWithCtnDto>();
|
|
|
|
|
|
//检索舱位信息
|
|
|
var slotInfo = Detail(bookingSlotBase.Id).GetAwaiter().GetResult();
|
|
|
|
|
|
BookingSlotBaseWithCtnDto baseInfo = slotInfo.Adapt<BookingSlotBaseWithCtnDto>();
|
|
|
baseInfo.Id = bookingSlotBase.Id;
|
|
|
baseInfo.CtnList = slotInfo.CtnList.Adapt<List<BookingSlotCtnDto>>();
|
|
|
|
|
|
slots.Add(baseInfo);
|
|
|
|
|
|
//对应订舱和舱位关系
|
|
|
var allocRlt = await ImportSlots(slots, id, false);
|
|
|
|
|
|
//这里如果指定了委托单位的邮件联系人,则推送订舱联系人
|
|
|
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)}");
|
|
|
}
|
|
|
|
|
|
var opt = App.GetOptions<BookingAttachOptions>();
|
|
|
var dirAbs = opt.basePath;
|
|
|
if (string.IsNullOrEmpty(dirAbs))
|
|
|
{
|
|
|
dirAbs = App.WebHostEnvironment.WebRootPath;
|
|
|
}
|
|
|
|
|
|
if (bookingSlotFileList.Any(a => a.TypeCode.Equals("bc", StringComparison.OrdinalIgnoreCase)))
|
|
|
{
|
|
|
var file = bookingSlotFileList.OrderByDescending(a => a.CreatedTime)
|
|
|
.FirstOrDefault(a => a.TypeCode.Equals("bc", StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
var fileFullPath = Path.Combine(dirAbs, file.FilePath);
|
|
|
|
|
|
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, file.TenantId.Value,
|
|
|
CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (bookingSlotFileList.Any(a => a.TypeCode.Equals("bc_notice", StringComparison.OrdinalIgnoreCase)))
|
|
|
{
|
|
|
var file = bookingSlotFileList.OrderByDescending(a => a.CreatedTime)
|
|
|
.FirstOrDefault(a => a.TypeCode.Equals("bc_notice", StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
var fileFullPath = Path.Combine(dirAbs, file.FilePath);
|
|
|
|
|
|
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, file.TenantId.Value,
|
|
|
CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
_logger.LogInformation($"MBLNO:{bookingSlotBase.SLOT_BOOKING_NO} 生成订舱订单成功 id={id}");
|
|
|
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_logger.LogError($"MBLNO:{bookingSlotBase.SLOT_BOOKING_NO} 生成订舱订单异常,原因:{ex.Message}");
|
|
|
}
|
|
|
|
|
|
|
|
|
return id;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 检索舱位对应的订舱订单(BY 舱位主键)
|
|
|
/// <summary>
|
|
|
/// 检索舱位对应的订舱订单(BY 舱位主键)
|
|
|
/// </summary>
|
|
|
/// <param name="id"></param>
|
|
|
/// <returns>返回回执</returns>
|
|
|
[HttpGet("/BookingSlot/SearchBookingSlotWithOrderById")]
|
|
|
public async Task<BookingSlotWithOrderDto> SearchBookingSlotWithOrderById(long id)
|
|
|
{
|
|
|
BookingSlotWithOrderDto dto = null;
|
|
|
|
|
|
var slotInfo = await _repBase.AsQueryable().FirstAsync(a => a.Id == id);
|
|
|
|
|
|
if (slotInfo == null)
|
|
|
{
|
|
|
_logger.LogInformation($"id={id} 获取舱位失败,舱位不存在或已作废");
|
|
|
|
|
|
return dto;
|
|
|
}
|
|
|
|
|
|
var list = _repAllocation.AsQueryable().Where(a => a.BOOKING_SLOT_ID == id).ToList();
|
|
|
|
|
|
dto = new BookingSlotWithOrderDto
|
|
|
{
|
|
|
BookingSlotId = slotInfo.Id,
|
|
|
};
|
|
|
|
|
|
if (list.Count > 0)
|
|
|
{
|
|
|
dto.HasBookingOrder = true;
|
|
|
dto.BookingOrderList = list.Select(x => x.Id).ToList();
|
|
|
}
|
|
|
|
|
|
return dto;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 检索舱位对应的订舱订单(BY 订舱编号)
|
|
|
/// <summary>
|
|
|
/// 检索舱位对应的订舱订单(BY 订舱编号)
|
|
|
/// </summary>
|
|
|
/// <param name="slotBookingNo">订舱编号</param>
|
|
|
/// <param name="tenantId">租户ID</param>
|
|
|
/// <returns>返回回执</returns>
|
|
|
[HttpGet("/BookingSlot/SearchBookingSlotWithOrderByNo")]
|
|
|
public async Task<BookingSlotWithOrderDto> SearchBookingSlotWithOrderByNo(string slotBookingNo, long tenantId)
|
|
|
{
|
|
|
BookingSlotWithOrderDto dto = null;
|
|
|
|
|
|
var slotInfo = await _repBase.AsQueryable().Filter(null, true).FirstAsync(a => a.SLOT_BOOKING_NO == slotBookingNo && a.TenantId == tenantId && a.IsDeleted == false);
|
|
|
|
|
|
if (slotInfo == null)
|
|
|
{
|
|
|
_logger.LogInformation($"slotBookingNo={slotBookingNo} 获取舱位失败,舱位不存在或已作废");
|
|
|
|
|
|
return dto;
|
|
|
}
|
|
|
|
|
|
var list = _repAllocation.AsQueryable().Filter(null, true).Where(a => a.BOOKING_SLOT_ID == slotInfo.Id && a.TenantId == tenantId && a.IsDeleted == false).ToList();
|
|
|
|
|
|
dto = new BookingSlotWithOrderDto
|
|
|
{
|
|
|
BookingSlotId = slotInfo.Id,
|
|
|
};
|
|
|
|
|
|
if (list.Count > 0)
|
|
|
{
|
|
|
dto.HasBookingOrder = true;
|
|
|
dto.BookingOrderList = list.Select(x => x.BOOKING_ID).ToList();
|
|
|
}
|
|
|
|
|
|
return dto;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
|
/// 校验是否可以生成订舱订单
|
|
|
/// </summary>
|
|
|
/// <param name="id">舱位主键</param>
|
|
|
/// <returns></returns>
|
|
|
[HttpGet("/BookingSlot/ValidateCreateBookingOrder")]
|
|
|
public async Task<TaskManageOrderResultDto> ValidateCreateBookingOrder(long id)
|
|
|
{
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
var slotInfo = await _repBase.AsQueryable().FirstAsync(a => a.Id == id);
|
|
|
|
|
|
if (slotInfo == null)
|
|
|
{
|
|
|
throw Oops.Oh($"获取舱位失败,舱位不存在或已作废");
|
|
|
}
|
|
|
|
|
|
//if(so)
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 批量发送邮件提醒(发送客户)
|
|
|
/// </summary>
|
|
|
/// <param name="model">舱位批量发送邮件请求</param>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost("/BookingSlot/SendEmail")]
|
|
|
public async Task<TaskManageOrderResultDto> SendEmail(BookingSlotSendEmailDto model)
|
|
|
{
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class DynameFileInfo
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 文件名称
|
|
|
/// </summary>
|
|
|
public string FileName { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 文件二进制流
|
|
|
/// </summary>
|
|
|
public byte[] FileBytes { get; set; }
|
|
|
}
|
|
|
}
|