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.
BookingHeChuan/Myshipping.Application/Service/BookingSlot/BookingSlotService.cs

1634 lines
72 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 Furion;
using Furion.DependencyInjection;
using Furion.DistributedIDGenerator;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.Extensions;
using Furion.FriendlyException;
using Furion.JsonSerialization;
using Furion.RemoteRequest.Extensions;
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.Service.BookingOrder.Dto;
using Myshipping.Application.Service.BookingSlot.Dto;
using Myshipping.Core;
using Myshipping.Core.Service;
using MySqlX.XDevAPI.Common;
using NPOI.XWPF.UserModel;
using Org.BouncyCastle.Asn1.Tsp;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing.Imaging;
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 System.Web;
using Yitter.IdGenerator;
using static Aliyun.OSS.Model.CreateSelectObjectMetaInputFormatModel;
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<BookingSlotAllocationCtn> _repAllocationCtn;
private readonly SqlSugarRepository<BookingFile> _bookingFileRepository;
private readonly SqlSugarRepository<BookingSlotCompare> _bookingSlotCompareRepository;
private readonly SqlSugarRepository<BookingOrderContact> _bookingOrderContactRepository;
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)
{
_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;
}
#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);
await _repBase.UpdateAsync(model);
await _repCtn.DeleteAsync(x => x.SLOT_ID == model.Id);
foreach (var ctn in input.CtnList)
{
var newCtn = ctn.Adapt<BookingSlotCtn>();
newCtn.SLOT_ID = model.Id;
await _repCtn.InsertAsync(newCtn);
}
await InsLog("Update", model.Id, typeof(BookingSlotBaseSaveInput), oldObj, input, "CtnList");
}
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,
PLACERECEIPT = model.PLACERECEIPT,
PLACEDELIVERY = model.PLACEDELIVERY
}));
return await Detail(model.Id);
}
/// <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;
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;
}
});
}
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);
await _repBase.UpdateAsync(model);
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, "CtnList");
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,
PLACERECEIPT = model.PLACERECEIPT,
PLACEDELIVERY = model.PLACEDELIVERY
}));
}
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"
});
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(objType))
{
if (excepProp.Contains(descriptor.Name))
{
continue;
}
var compResult = false;
var oldValue = descriptor.GetValue(objOld);
var newValue = descriptor.GetValue(objNew);
if (oldValue != null && newValue != null)
{
if (descriptor.PropertyType == typeof(string))
{
compResult = oldValue.ToString() == newValue.ToString();
}
else if (descriptor.PropertyType == typeof(DateTime) || descriptor.PropertyType == typeof(DateTime?))
{
compResult = Convert.ToDateTime(oldValue) == Convert.ToDateTime(newValue);
}
else if (descriptor.PropertyType == typeof(decimal) || descriptor.PropertyType == typeof(float) || descriptor.PropertyType == typeof(double)
|| descriptor.PropertyType == typeof(decimal?) || descriptor.PropertyType == typeof(float?) || descriptor.PropertyType == typeof(double?))
{
compResult = Convert.ToDecimal(oldValue) == Convert.ToDecimal(newValue);
}
else if (descriptor.PropertyType == typeof(int) || descriptor.PropertyType == typeof(long)
|| descriptor.PropertyType == typeof(int?) || descriptor.PropertyType == typeof(long?))
{
compResult = Convert.ToInt64(oldValue) == Convert.ToInt64(newValue);
}
}
else
{
compResult = oldValue == newValue;
}
if (!compResult)
{
var fieldName = descriptor.Name;
if (!string.IsNullOrWhiteSpace(descriptor.Description))
{
fieldName = descriptor.Description;
}
await _repBookingLogDetail.InsertReturnSnowflakeIdAsync(new BookingLogDetail
{
PId = bid,
Field = fieldName,
OldValue = $"{oldValue}",
NewValue = $"{newValue}",
});
}
}
}
#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(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)
{
//更新库存
await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", input));
}
#endregion
#region 舱位引入
/// <summary>
/// 查询可用的舱位及箱子
/// </summary>
[HttpGet("/BookingSlot/getAvailableSlots")]
public async Task<List<BookingSlotBaseWithCtnDto>> GetAvailableSlots([FromQuery] BookingSlotBaseDto input)
{
return await GetAvailableSlots(input, null);
}
/// <summary>
/// 查询可用的舱位及箱子列表
/// </summary>
/// <param name="slotInput">筛选条件1舱位信息、箱型</param>
/// <param name="slotIdListInput">筛选条件2舱位主键列表</param>
/// <returns>可用的舱位列表(含可用的箱子列表)</returns>
[NonAction]
public async Task<List<BookingSlotBaseWithCtnDto>> GetAvailableSlots(BookingSlotBaseDto slotInput = null, List<long> slotIdListInput = 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.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(),得到可用的【舱位主键】、【箱型】、【箱量】列表
var canUselist = await queryable3.ToListAsync();
// 查询舱位列表
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,
PLACERECEIPT = latestSlot.PLACERECEIPT,
PLACEDELIVERY = latestSlot.PLACEDELIVERY
}));
}
}
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>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/BookingSlot/page")]
public async Task<dynamic> Page(BookingSlotBasePageInput input)
{
var entities = await _repBase.AsQueryable()
.WhereIF(!string.IsNullOrEmpty(input.SLOT_BOOKING_NO), u => u.SLOT_BOOKING_NO.Contains(input.SLOT_BOOKING_NO))
.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.PORTLOAD.Contains(input.PORTLOAD))
.WhereIF(!string.IsNullOrEmpty(input.CARRIER), u => u.CARRIER.Contains(input.CARRIER))
.WhereIF(!string.IsNullOrEmpty(input.LANENAME), u => u.LANENAME.Contains(input.LANENAME))
.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.CTN_STAT), u => u.CTN_STAT.Contains(input.CTN_STAT))
.WhereIF(!string.IsNullOrEmpty(input.VGM_RLT_STAT), u => u.VGM_RLT_STAT == input.VGM_RLT_STAT)
.WhereIF(!string.IsNullOrEmpty(input.SI_RLT_STAT), u => u.SI_RLT_STAT == input.SI_RLT_STAT)
.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<BookingSlotBaseListOutput>>();
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;
_logger.LogInformation("批次={no} 请求完成,耗时:{timeDiff}ms. 结果{msg}", batchNo, timeDiff, compareResult.succ ? "成功" : "失败");
if (compareResult == null)
throw Oops.Oh($"舱位主键{slotId}请求BC比对失败返回为空");
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();
}
}
#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);
}
}
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)
{
//这里如果指定了委托单位的邮件联系人,则推送订舱联系人
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);
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).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; }
}
}