|
|
using DS.Module.Core;
|
|
|
using DS.Module.Core.Condition;
|
|
|
using DS.Module.Core.Constants;
|
|
|
using DS.Module.Core.Data;
|
|
|
using DS.Module.Core.Extensions;
|
|
|
using DS.Module.DjyServiceStatus;
|
|
|
using DS.Module.SqlSugar;
|
|
|
using DS.Module.UserModule;
|
|
|
using DS.WMS.Core.Code.Dtos;
|
|
|
using DS.WMS.Core.Flow.Entity;
|
|
|
using DS.WMS.Core.Map.Dtos;
|
|
|
using DS.WMS.Core.Op.Dtos;
|
|
|
using DS.WMS.Core.Op.Entity;
|
|
|
using DS.WMS.Core.Op.Entity.TaskInteraction;
|
|
|
using DS.WMS.Core.Op.Interface;
|
|
|
using DS.WMS.Core.Sys.Entity;
|
|
|
using DS.WMS.Core.TaskPlat.Dtos;
|
|
|
using DS.WMS.Core.TaskPlat.Entity;
|
|
|
using DS.WMS.Core.TaskPlat.Interface;
|
|
|
using Microsoft.AspNetCore.Hosting;
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
using Newtonsoft.Json;
|
|
|
using SqlSugar;
|
|
|
using System.Linq.Expressions;
|
|
|
using System.Runtime.InteropServices;
|
|
|
using System.Text;
|
|
|
using System.Web;
|
|
|
|
|
|
namespace DS.WMS.Core.TaskPlat.Method
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 任务模块业务类的基类,封装了一些常用的方法
|
|
|
/// </summary>
|
|
|
public class TaskManageBaseService<T> : ITaskManageBaseService
|
|
|
{
|
|
|
// 实例化时构建
|
|
|
protected readonly IUser user;
|
|
|
protected readonly ILogger<T> logger;
|
|
|
protected readonly ISaasDbService saasDbService;
|
|
|
protected readonly IServiceProvider serviceProvider;
|
|
|
protected readonly IWebHostEnvironment environment;
|
|
|
protected readonly ISqlSugarClient db;
|
|
|
|
|
|
// 按需构建
|
|
|
protected Lazy<ITaskAllocationService> allocationService;
|
|
|
protected Lazy<ISeaExportCommonService> seaExportCommonService;
|
|
|
protected Lazy<IDjyServiceStatusService> djyServiceStatusService;
|
|
|
|
|
|
public TaskManageBaseService(IUser user,
|
|
|
ILogger<T> logger,
|
|
|
ISaasDbService saasDbService,
|
|
|
IServiceProvider serviceProvider,
|
|
|
IWebHostEnvironment environment)
|
|
|
{
|
|
|
this.user = user;
|
|
|
this.logger = logger;
|
|
|
this.saasDbService = saasDbService;
|
|
|
this.serviceProvider = serviceProvider;
|
|
|
this.environment = environment;
|
|
|
|
|
|
db = serviceProvider.GetRequiredService<ISqlSugarClient>();
|
|
|
|
|
|
allocationService = new Lazy<ITaskAllocationService>(serviceProvider.GetRequiredService<ITaskAllocationService>());
|
|
|
seaExportCommonService = new Lazy<ISeaExportCommonService>(serviceProvider.GetRequiredService<ISeaExportCommonService>());
|
|
|
djyServiceStatusService = new Lazy<IDjyServiceStatusService>(serviceProvider.GetRequiredService<IDjyServiceStatusService>());
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 更新任务主表状态
|
|
|
/// </summary>
|
|
|
/// <param name="taskIds">任务主键数组</param>
|
|
|
/// <param name="columns">需要更新状态的列</param>
|
|
|
public async Task SetTaskStatus(long[] taskIds, params Expression<Func<TaskBaseInfo, bool>>[] columns)
|
|
|
{
|
|
|
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
|
|
|
//任务不考虑OrgId,这里去掉
|
|
|
tenantDb.QueryFilter.Clear<IOrgId>();
|
|
|
|
|
|
var updateable = tenantDb.Updateable<TaskBaseInfo>();
|
|
|
foreach (var item in columns)
|
|
|
{
|
|
|
updateable.SetColumns(item);
|
|
|
}
|
|
|
updateable.SetColumns(x => x.UpdateBy == long.Parse(user.UserId))
|
|
|
.SetColumns(x => x.UpdateTime == DateTime.Now)
|
|
|
.SetColumns(x => x.UpdateUserName == user.UserName);
|
|
|
|
|
|
await updateable.Where(x => taskIds.Contains(x.Id))
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
// 后面可以记录日志
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 设置任务处理人
|
|
|
/// </summary>
|
|
|
/// <param name="taskIds">任务主键数组</param>
|
|
|
/// <param name="userInfo">人员信息列表</param>
|
|
|
public async Task SetTaskOwner(long[] taskIds, List<RecvUserInfo> userInfo)
|
|
|
{
|
|
|
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
|
|
|
//任务不考虑OrgId,这里去掉
|
|
|
tenantDb.QueryFilter.Clear<IOrgId>();
|
|
|
|
|
|
try
|
|
|
{
|
|
|
var taskList = await tenantDb.Queryable<TaskBaseInfo>().Where(x => taskIds.Contains(x.Id)).ToListAsync(x => new
|
|
|
{
|
|
|
x.Id,
|
|
|
x.STATUS,
|
|
|
x.STATUS_NAME,
|
|
|
x.TASK_TYPE,
|
|
|
x.OUT_BS_NO,
|
|
|
x.TASK_SOURCE
|
|
|
});
|
|
|
|
|
|
var taskIdList = taskList.Select(x => x.Id).ToList();
|
|
|
var taskTypeList = taskList.Where(x => x.TASK_SOURCE == TaskSourceEnum.WORK_FLOW.ToString() && x.OUT_BS_NO != null).Select(x => x.TASK_TYPE).Distinct().ToList();
|
|
|
|
|
|
var taskCompleteStatusCodeList = taskTypeList.Count > 0 ? await tenantDb.Queryable<TaskFlowModule>()
|
|
|
.Where(x => x.ModuleType == 2 && taskTypeList.Contains(x.TaskType))
|
|
|
.Select(x => new { x.TaskType, x.CompletedBusinessStatusCode })
|
|
|
.ToListAsync()
|
|
|
: [];
|
|
|
|
|
|
var userIdList = userInfo.Select(x => x.RecvUserId).Distinct().ToList();
|
|
|
var userWithOrgMap = await db.Queryable<SysUser>().Where(x => userIdList.Contains(x.Id)).Select(x => new { x.Id, x.DefaultOrgId }).ToListAsync();
|
|
|
|
|
|
List<TaskBaseAllocation> allocationList = new();
|
|
|
foreach (var item in taskList)
|
|
|
{
|
|
|
var config = taskCompleteStatusCodeList.FirstOrDefault(x => x.TaskType == item.TASK_TYPE);
|
|
|
|
|
|
var allots = userInfo.Select(x => new TaskBaseAllocation
|
|
|
{
|
|
|
TaskId = item.Id,
|
|
|
UserId = x.RecvUserId,
|
|
|
UserName = x.RecvUserName,
|
|
|
Status = item.STATUS,
|
|
|
StatusName = item.STATUS_NAME,
|
|
|
StatusTime = DateTime.Now,
|
|
|
BusinessId = item.OUT_BS_NO,
|
|
|
GoodStatusCode = config?.CompletedBusinessStatusCode,
|
|
|
OrgId = userWithOrgMap.FirstOrDefault(m => m.Id == x.RecvUserId)?.DefaultOrgId ?? 0
|
|
|
});
|
|
|
allocationList.AddRange(allots);
|
|
|
}
|
|
|
// 用于更新工作流的任务
|
|
|
var userIdStr = string.Join(',', userInfo.Select(x => x.RecvUserId));
|
|
|
var businessIdList = taskList.Where(x => x.OUT_BS_NO != null && x.OUT_BS_NO != 0).Select(x => x.OUT_BS_NO).Distinct().ToList();
|
|
|
|
|
|
//await tenantDb.Ado.BeginTranAsync();
|
|
|
await tenantDb.AsTenant().BeginTranAsync();
|
|
|
|
|
|
var idList = await tenantDb.Queryable<TaskBaseAllocation>().Where(x => taskIdList.Contains(x.TaskId)).Select(x => x.Id).ToListAsync();
|
|
|
await tenantDb.Deleteable<TaskBaseAllocation>(x => idList.Contains(x.Id)).ExecuteCommandAsync();
|
|
|
|
|
|
await tenantDb.Insertable(allocationList).ExecuteCommandAsync();
|
|
|
|
|
|
await tenantDb.Updateable<TaskBaseInfo>()
|
|
|
.SetColumns(x => x.IS_PUBLIC == 0)
|
|
|
.Where(x => taskIdList.Contains(x.Id))
|
|
|
.ExecuteCommandAsync();
|
|
|
foreach (var item in taskList)
|
|
|
{
|
|
|
if (item.OUT_BS_NO is not null or 0 && Enum.TryParse(typeof(TaskBaseTypeEnum), item.TASK_TYPE, out object? taskType))
|
|
|
{
|
|
|
await tenantDb.Updateable<BusinessTask>()
|
|
|
.SetColumns(x => x.RecvUsers == userIdStr)
|
|
|
.Where(x => x.BusinessId == item.OUT_BS_NO && x.TaskType == (TaskBaseTypeEnum)taskType)
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
var id = await db.Queryable<FlowInstance>()
|
|
|
.ClearFilter(typeof(ITenantId))
|
|
|
.Where(y => y.BusinessId == item.OUT_BS_NO && y.AuditType == (TaskBaseTypeEnum)taskType)
|
|
|
.OrderByDescending(y => y.Id)
|
|
|
.Select(y => y.Id)
|
|
|
.FirstAsync();
|
|
|
if (id != 0)
|
|
|
{
|
|
|
await db.Updateable<FlowInstance>()
|
|
|
.SetColumns(x => x.MakerList == userIdStr)
|
|
|
.Where(x => x.Id == id)
|
|
|
.ExecuteCommandAsync();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//await tenantDb.Ado.CommitTranAsync();
|
|
|
await tenantDb.AsTenant().CommitTranAsync();
|
|
|
}
|
|
|
catch (Exception)
|
|
|
{
|
|
|
//await tenantDb.Ado.RollbackTranAsync();
|
|
|
await tenantDb.AsTenant().RollbackTranAsync();
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#region Tools
|
|
|
|
|
|
/// <summary>
|
|
|
/// 保存文件并返回文件完整路径
|
|
|
/// </summary>
|
|
|
/// <param name="fileDictKey">追加文件夹</param>
|
|
|
/// <param name="fileBytes">文件二进制流</param>
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
/// <param name="fileNameNoSuffix">无拓展名的文件名</param>
|
|
|
/// <param name="printFileType">文件类型</param>
|
|
|
/// <param name="attachFileType">附件类型 bcfiles-BC文件 sofile-订舱附件</param>
|
|
|
/// <returns>返回文件完整路径</returns>
|
|
|
protected async Task<string> SaveFile(string fileDictKey, byte[] fileBytes, string batchNo, string fileNameNoSuffix,
|
|
|
PrintFileTypeEnum printFileType, string attachFileType = "sofiles")
|
|
|
{
|
|
|
// 获取文件存盘时保存的物理文件名:fileName
|
|
|
var fileType = string.Empty;
|
|
|
if (printFileType == PrintFileTypeEnum.PDF)
|
|
|
{
|
|
|
fileType = ".pdf";
|
|
|
}
|
|
|
else if (printFileType == PrintFileTypeEnum.XLSX)
|
|
|
{
|
|
|
fileType = ".xlsx";
|
|
|
}
|
|
|
else if (printFileType == PrintFileTypeEnum.DOCX)
|
|
|
{
|
|
|
fileType = ".docx";
|
|
|
}
|
|
|
else if (printFileType == PrintFileTypeEnum.XLS)
|
|
|
{
|
|
|
fileType = ".xls";
|
|
|
}
|
|
|
else if (printFileType == PrintFileTypeEnum.DOC)
|
|
|
{
|
|
|
fileType = ".doc";
|
|
|
}
|
|
|
var fileName = $"{fileNameNoSuffix}{fileType}"; // 文件存盘时保存的物理文件名
|
|
|
|
|
|
|
|
|
// 获取文件存库时保存的相对路径:fileInfoRelativePathWithName
|
|
|
var fileInfoRelativeDic = string.Empty;
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(attachFileType))
|
|
|
fileInfoRelativeDic = Path.Combine(fileInfoRelativeDic, attachFileType);
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(fileDictKey))
|
|
|
fileInfoRelativeDic = Path.Combine(fileInfoRelativeDic, fileDictKey);
|
|
|
|
|
|
// 文件存库时保存的相对路径
|
|
|
var fileInfoRelativePathWithName = Path.Combine(fileInfoRelativeDic, fileName); // 文件存库时保存的相对路径
|
|
|
|
|
|
|
|
|
|
|
|
// 获取文件存盘所需的绝对路径:absFilePath
|
|
|
var absFilePath = string.Empty;
|
|
|
var baseDicConfig = AppSetting.app(new string[] { "FileSettings", "BasePath" });
|
|
|
var relativeDicConfig = AppSetting.app(new string[] { "FileSettings", "RelativePath" });
|
|
|
|
|
|
if (string.IsNullOrEmpty(baseDicConfig))
|
|
|
{
|
|
|
absFilePath = Path.Combine(environment.WebRootPath ?? "", relativeDicConfig, fileInfoRelativeDic);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
absFilePath = Path.Combine(baseDicConfig, relativeDicConfig, fileInfoRelativeDic);
|
|
|
}
|
|
|
|
|
|
if (!Directory.Exists(absFilePath))
|
|
|
Directory.CreateDirectory(absFilePath);
|
|
|
|
|
|
absFilePath = Path.Combine(absFilePath, fileName);
|
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
{
|
|
|
fileInfoRelativePathWithName = fileInfoRelativePathWithName.Replace("\\", "/");
|
|
|
absFilePath = absFilePath.Replace("\\", "/");
|
|
|
}
|
|
|
await File.WriteAllBytesAsync(absFilePath, fileBytes);
|
|
|
|
|
|
return fileInfoRelativePathWithName;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取文件类型
|
|
|
/// </summary>
|
|
|
/// <param name="fileName">文件完成路径</param>
|
|
|
/// <returns>返回文件类型枚举</returns>
|
|
|
protected PrintFileTypeEnum GetFileType(string fileName)
|
|
|
{
|
|
|
PrintFileTypeEnum fileType = PrintFileTypeEnum.PDF;
|
|
|
|
|
|
switch (Path.GetExtension(fileName).ToLower())
|
|
|
{
|
|
|
case ".pdf":
|
|
|
fileType = PrintFileTypeEnum.PDF;
|
|
|
break;
|
|
|
case ".xlsx":
|
|
|
fileType = PrintFileTypeEnum.XLSX;
|
|
|
break;
|
|
|
case ".docx":
|
|
|
fileType = PrintFileTypeEnum.DOCX;
|
|
|
break;
|
|
|
case ".xls":
|
|
|
fileType = PrintFileTypeEnum.XLS;
|
|
|
break;
|
|
|
case ".doc":
|
|
|
fileType = PrintFileTypeEnum.DOC;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
return fileType;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
|
/// 根据任务ID获取附件信息
|
|
|
/// </summary>
|
|
|
/// <param name="taskId">任务Id</param>
|
|
|
/// <param name="fileCategory">附件分类代码</param>
|
|
|
public async Task<(string fileFullPath, string fileName)> GetTaskFileInfo(long taskId, string fileCategory)
|
|
|
{
|
|
|
var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
|
|
|
//任务不考虑OrgId,这里去掉
|
|
|
tenantDb.QueryFilter.Clear<IOrgId>();
|
|
|
|
|
|
var bcTaskInfo = await tenantDb.Queryable<TaskBaseInfo>().Where(u => u.Id == taskId).FirstAsync();
|
|
|
if (bcTaskInfo == null)
|
|
|
{
|
|
|
throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DataQueryNoData)));
|
|
|
}
|
|
|
|
|
|
TaskFileCategoryEnum fileCategoryEnum = TaskFileCategoryEnum.NONE;
|
|
|
|
|
|
System.Enum.TryParse(fileCategory, out fileCategoryEnum);
|
|
|
|
|
|
if (fileCategoryEnum == TaskFileCategoryEnum.NONE)
|
|
|
{
|
|
|
// 附件分类代码错误,请提供正确的分类代码
|
|
|
throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskFileCategoryError)));
|
|
|
}
|
|
|
|
|
|
string name = fileCategoryEnum.ToString();
|
|
|
|
|
|
var fileInfo = await tenantDb.Queryable<TaskFileInfo>().Where(u => u.TASK_PKID == taskId && u.FILE_CATEGORY == name).OrderByDescending(x => x.Id).FirstAsync();
|
|
|
|
|
|
if (fileInfo == null)
|
|
|
{
|
|
|
// 附件分类代码错误,请提供正确的分类代码
|
|
|
throw new Exception(
|
|
|
string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskFileEmpty)), taskId)
|
|
|
);
|
|
|
}
|
|
|
var baseDicConfig = AppSetting.app(new string[] { "FileSettings", "BasePath" });
|
|
|
var relativeDicConfig = AppSetting.app(new string[] { "FileSettings", "RelativePath" });
|
|
|
|
|
|
string absFilePath;
|
|
|
if (string.IsNullOrEmpty(baseDicConfig))
|
|
|
{
|
|
|
absFilePath = Path.Combine(environment.WebRootPath ?? "", relativeDicConfig, fileInfo.FILE_PATH);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
absFilePath = Path.Combine(baseDicConfig, relativeDicConfig, fileInfo.FILE_PATH);
|
|
|
}
|
|
|
if (!File.Exists(absFilePath))
|
|
|
{
|
|
|
logger.LogError("根据任务主键获取文件信息失败:文件不存在,fileFullPath={fileFullPath}", absFilePath);
|
|
|
//任务主键{0} 附件下载请求失败,请确认文件是否存在
|
|
|
throw new Exception(
|
|
|
string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskFileNotExists)), taskId)
|
|
|
);
|
|
|
}
|
|
|
|
|
|
var fileName = HttpUtility.UrlEncode(fileInfo.FILE_NAME, Encoding.GetEncoding("UTF-8"))!;
|
|
|
return (absFilePath, fileName);
|
|
|
//return (new FileStream(fileFullPath, FileMode.Open), fileName);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 根据订单及配置,将所有或指定的公共任务匹配到个人
|
|
|
/// </summary>
|
|
|
/// <param name="taskIdList">任务Id列表(当传入时,则只匹配列表中指定的任务)</param>
|
|
|
/// <returns>涉及当前登陆人的匹配结果</returns>
|
|
|
public async Task<DataResult<MatchTaskResultDto>> MatchTask(List<long>? taskIdList = null)
|
|
|
{
|
|
|
/*
|
|
|
*
|
|
|
* 测试库测试用Sql:
|
|
|
SELECT t.SplitOrMergeFlag,t.OperatorId,t.OperatorName,t.Doc,t.DocName,t.CustomerService,t.CustomerServiceName,t.ForeignCustomerService,t.ForeignCustomerServiceName,t.Sale,t.SaleId,t. *
|
|
|
FROM `op_sea_export` t
|
|
|
where Deleted=0 and id In(1816649497120477184,1816779333432381440,1780891904372772864)
|
|
|
order by id desc
|
|
|
|
|
|
|
|
|
SELECT t.SplitOrMergeFlag,t.OperatorId,t.OperatorName,t.Doc,t.DocName,t.CustomerService,t.CustomerServiceName,t.ForeignCustomerService,t.ForeignCustomerServiceName,t.Sale,t.SaleId,t. *
|
|
|
FROM `op_sea_export` t
|
|
|
where Deleted=0 and id In(1813475270208917504,1813509723408961536,1816277472539447296)
|
|
|
order by id desc
|
|
|
*/
|
|
|
|
|
|
MatchTaskResultDto result = new MatchTaskResultDto();
|
|
|
|
|
|
var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
|
|
|
//任务不考虑OrgId,这里去掉
|
|
|
tenantDb.QueryFilter.Clear<IOrgId>();
|
|
|
|
|
|
var time = DateTime.Now.AddMonths(-3);
|
|
|
var taskList = await tenantDb.Queryable<TaskBaseInfo>()
|
|
|
.Where(x => x.IS_PUBLIC == 1
|
|
|
&& x.STATUS == TaskStatusEnum.Create.ToString()
|
|
|
&& !string.IsNullOrEmpty(x.MBL_NO)
|
|
|
&& x.CreateTime > time)
|
|
|
.WhereIF(taskIdList != null && taskIdList.Count > 0, x => taskIdList!.Contains(x.Id))
|
|
|
//.Where(x => x.Id == 1813475270208917504 || x.Id == 1816277472539447296 || x.Id == 1813509723408961536)
|
|
|
.ToListAsync(x => new TaskBaseInfo
|
|
|
{
|
|
|
Id = x.Id,
|
|
|
MBL_NO = x.MBL_NO,
|
|
|
TASK_TYPE = x.TASK_TYPE,
|
|
|
TASK_TYPE_NAME = x.TASK_TYPE_NAME,
|
|
|
//CARRIER_ID = x.CARRIER_ID,
|
|
|
});
|
|
|
logger.LogInformation($"MatchTask共查询出{taskList.Count}条待匹配的记录,taskList:{string.Join(',', taskList.Select(x => x.Id).Take(50))}");
|
|
|
|
|
|
var allotSetCache = await allocationService.Value.GetAllList();
|
|
|
|
|
|
if (!allotSetCache.Succeeded || (allotSetCache.Data?.Count ?? 0) == 0)
|
|
|
{
|
|
|
return DataResult<MatchTaskResultDto>.Success("操作成功!", result, MultiLanguageConst.DataUpdateSuccess);
|
|
|
}
|
|
|
|
|
|
var taskTypeStrList = taskList.Select(x => x.TASK_TYPE).Distinct().ToList();
|
|
|
var allotSetList = allotSetCache.Data.Where(x => taskTypeStrList.Contains(x.TaskTypeCode)).ToList();
|
|
|
if (allotSetList.Count == 0)
|
|
|
{
|
|
|
return DataResult<MatchTaskResultDto>.Success("操作成功!", result, MultiLanguageConst.DataUpdateSuccess);
|
|
|
}
|
|
|
|
|
|
// 需要查询的订单的提单号的集合,用于一次性将需要查询的订单查询出来;
|
|
|
// 后续如果需要查询舱位,可以再补充字段如List<string> waitQuerySlotWithMblnoList = new();
|
|
|
HashSet<string> waitQuerySeaExportWithMblnoList = new();
|
|
|
|
|
|
// 分配规则
|
|
|
Dictionary<string, List<TaskAllocationtSetDto>> allotTargetList = new();
|
|
|
|
|
|
foreach (var taskItem in taskList)
|
|
|
{
|
|
|
List<TaskAllocationtSetDto> taskItemAllotSetList = allotSetList.Where(x => x.TaskTypeCode == taskItem.TASK_TYPE).ToList();
|
|
|
|
|
|
foreach (var allotSetItem in taskItemAllotSetList)
|
|
|
{
|
|
|
// 这里先存下来,后面再统一查库,减少查询次数
|
|
|
// 如果配置的下面四种接收对象,则需要查询订单
|
|
|
if (allotSetItem.IsAllotCustomerService
|
|
|
|| allotSetItem.IsAllotSale
|
|
|
|| allotSetItem.IsAllotOperator
|
|
|
|| allotSetItem.IsAllotVouchingClerk)
|
|
|
{
|
|
|
waitQuerySeaExportWithMblnoList.Add(taskItem.MBL_NO!);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 查出涉及到的订单
|
|
|
var seaExportList = await tenantDb.Queryable<SeaExport>()
|
|
|
.Where(x => x.ParentId == 0
|
|
|
&& (
|
|
|
(x.SplitOrMergeFlag == 0 && waitQuerySeaExportWithMblnoList.Contains(x.MBLNO))
|
|
|
|| (x.SplitOrMergeFlag == 1 && waitQuerySeaExportWithMblnoList.Contains(x.BookingNo))
|
|
|
)).Select<SeaExportRes>().ToListAsync();
|
|
|
List<(TaskBaseInfo, List<RecvUserInfo>)> allotData = new();
|
|
|
foreach (var taskItem in taskList)
|
|
|
{
|
|
|
var recvUserList = new List<RecvUserInfo>();
|
|
|
|
|
|
List<TaskAllocationtSetDto> taskItemAllotSetList = allotSetList.Where(x => x.TaskTypeCode == taskItem.TASK_TYPE).ToList();
|
|
|
|
|
|
var taskItemAllotList = new List<TaskAllocationtSetDto>();
|
|
|
foreach (var allotSetItem in taskItemAllotSetList)
|
|
|
{
|
|
|
// 验证条件
|
|
|
if (!string.IsNullOrEmpty(allotSetItem.Condition))
|
|
|
{
|
|
|
var contitionContent = JsonConvert.DeserializeObject<ConditionContent>(allotSetItem.Condition!)!;
|
|
|
|
|
|
TaskFlowDataContext dataContext = new();
|
|
|
|
|
|
if (allotSetItem.IsAllotCustomerService
|
|
|
|| allotSetItem.IsAllotSale
|
|
|
|| allotSetItem.IsAllotOperator
|
|
|
|| allotSetItem.IsAllotVouchingClerk)
|
|
|
{
|
|
|
var seaExport = seaExportList.FirstOrDefault(x => (x.SplitOrMergeFlag == 0 && taskItem.MBL_NO == x.MBLNO) || (x.SplitOrMergeFlag == 1 && taskItem.MBL_NO == x.BookingNo));
|
|
|
|
|
|
if (seaExport == null) continue; // 这里为了效率,设置为如果订单为空,则跳过;后面如果需要判断其他表,需要把这里注释掉
|
|
|
|
|
|
dataContext.Set(TaskFlowDataNameConst.Business, seaExport);
|
|
|
}
|
|
|
|
|
|
if (!ConditionHelper.IsPass(contitionContent, dataContext))
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if (allotSetItem.IsAllotCustomerService
|
|
|
|| allotSetItem.IsAllotSale
|
|
|
|| allotSetItem.IsAllotOperator
|
|
|
|| allotSetItem.IsAllotVouchingClerk)
|
|
|
{
|
|
|
var order = seaExportList.FirstOrDefault(x => (x.SplitOrMergeFlag == 0 && taskItem.MBL_NO == x.MBLNO) || (x.SplitOrMergeFlag == 1 && taskItem.MBL_NO == x.BookingNo));
|
|
|
if (order == null)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
/*
|
|
|
* 操作Operator=订单里的:OperatorId+OperatorName
|
|
|
* 单证VouchingClerk=订单里的:Doc+DocName
|
|
|
* 销售Sale=订单里的:SaleId+Sale
|
|
|
* 客服CustomerService=订单里的:CustomerService+CustomerServiceName / ForeignCustomerService+ForeignCustomerServiceName
|
|
|
*/
|
|
|
|
|
|
if (allotSetItem.IsAllotCustomerService)
|
|
|
{
|
|
|
if (order.CustomerService != 0 && !string.IsNullOrEmpty(order.CustomerServiceName))
|
|
|
{
|
|
|
recvUserList.Add(new RecvUserInfo(order.CustomerService, order.CustomerServiceName));
|
|
|
}
|
|
|
if (order.ForeignCustomerService != 0 && !string.IsNullOrEmpty(order.ForeignCustomerServiceName))
|
|
|
{
|
|
|
recvUserList.Add(new RecvUserInfo(order.ForeignCustomerService, order.ForeignCustomerServiceName));
|
|
|
}
|
|
|
}
|
|
|
if (allotSetItem.IsAllotOperator
|
|
|
&& order.OperatorId != 0 && !string.IsNullOrEmpty(order.OperatorName))
|
|
|
{
|
|
|
recvUserList.Add(new RecvUserInfo(order.OperatorId, order.OperatorName));
|
|
|
}
|
|
|
if (allotSetItem.IsAllotSale
|
|
|
&& order.SaleId != 0 && !string.IsNullOrEmpty(order.Sale))
|
|
|
{
|
|
|
recvUserList.Add(new RecvUserInfo(order.SaleId, order.Sale));
|
|
|
}
|
|
|
if (allotSetItem.IsAllotVouchingClerk
|
|
|
&& order.Doc != 0 && !string.IsNullOrEmpty(order.DocName))
|
|
|
{
|
|
|
recvUserList.Add(new RecvUserInfo(order.Doc, order.DocName));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (recvUserList.Count != 0)
|
|
|
{
|
|
|
recvUserList = recvUserList.DistinctBy(x => x.RecvUserId).ToList();
|
|
|
|
|
|
allotData.Add((taskItem, recvUserList));
|
|
|
await SetTaskOwner([taskItem.Id], recvUserList);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return DataResult<MatchTaskResultDto>.Success("操作成功!", result, MultiLanguageConst.DataUpdateSuccess);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 通过任务主表主键设置任务状态(任务台使用)
|
|
|
/// </summary>
|
|
|
/// <param name="taskBaseId">任务主表主键</param>
|
|
|
/// <param name="taskBaseTypeEnum">业务类型</param>
|
|
|
/// <param name="taskStatusEnum">业务状态</param>
|
|
|
/// <param name="statusTime">状态发生时间</param>
|
|
|
/// <param name="bsno">业务主键</param>
|
|
|
public async Task<DataResult> SetTaskStatus(long taskBaseId, TaskBaseTypeEnum taskBaseTypeEnum, TaskStatusEnum taskStatusEnum, DateTime? statusTime, long? bsno = null)
|
|
|
{
|
|
|
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
|
|
|
|
|
|
TaskBaseInfo taskInfo = await tenantDb.Queryable<TaskBaseInfo>().ClearFilter(typeof(IOrgId))
|
|
|
.Where(t => t.Id == taskBaseId)
|
|
|
.FirstAsync();
|
|
|
|
|
|
return await SetTaskStatus(taskInfo, taskStatusEnum, statusTime, bsno);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 通过任务主表对象设置任务状态()
|
|
|
/// </summary>
|
|
|
/// <param name="taskInfo">任务主表对象</param>
|
|
|
/// <param name="taskStatusEnum">业务状态</param>
|
|
|
/// <param name="statusTime">状态发生时间</param>
|
|
|
/// <param name="bsno">业务主键</param>
|
|
|
public async Task<DataResult> SetTaskStatus(TaskBaseInfo taskInfo, TaskStatusEnum taskStatusEnum, DateTime? statusTime, long? bsno = null)
|
|
|
{
|
|
|
if (taskInfo is null)
|
|
|
{
|
|
|
throw new ArgumentNullException(nameof(taskInfo));
|
|
|
}
|
|
|
|
|
|
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
|
|
|
|
|
|
// 修改任务的状态
|
|
|
taskInfo.STATUS = taskStatusEnum.ToString();
|
|
|
taskInfo.STATUS_NAME = taskStatusEnum.EnumDescription();
|
|
|
taskInfo.RealUserId = long.Parse(user.UserId);
|
|
|
taskInfo.RealUserName = user.UserName;
|
|
|
|
|
|
if (taskStatusEnum == TaskStatusEnum.Complete)
|
|
|
{
|
|
|
taskInfo.IS_COMPLETE = 1;
|
|
|
taskInfo.COMPLETE_DATE = statusTime;
|
|
|
}
|
|
|
|
|
|
|
|
|
// 任务状态为“完成”且来源为工作流时要做的工作:
|
|
|
if (taskStatusEnum == TaskStatusEnum.Complete && taskInfo.TASK_SOURCE == TaskSourceEnum.WORK_FLOW.ToString())
|
|
|
{
|
|
|
long? orderId = bsno;
|
|
|
if (orderId == null || orderId == 0)
|
|
|
{
|
|
|
if (long.TryParse(taskInfo.BOOK_ORDER_NO, out long temp))
|
|
|
{
|
|
|
orderId = temp;
|
|
|
}
|
|
|
}
|
|
|
if (orderId != null && orderId != 0)
|
|
|
{
|
|
|
string? statusCode = await tenantDb.Queryable<TaskFlowModule>().Where(x => x.ModuleType == 2 && x.TaskType == taskInfo.TASK_TYPE).Select(x => x.CompletedBusinessStatusCode).FirstAsync();
|
|
|
if (!string.IsNullOrEmpty(statusCode))
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
// 1.设置相关订单的业务状态
|
|
|
await seaExportCommonService.Value.SetGoodsStatus(statusCode, (long)orderId, tenantDb);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.LogError(ex, "任务完成时,设置订单业务状态的过程中发生异常,orderId={0},taskType={1}", orderId, taskInfo.TASK_TYPE);
|
|
|
throw;
|
|
|
}
|
|
|
|
|
|
try
|
|
|
{
|
|
|
// 2.设置货物状态为已完成
|
|
|
await djyServiceStatusService.Value.SaveServiceStatus(new EmbedServiceProjectStatusDto()
|
|
|
{
|
|
|
businessId = orderId.ToString()!,
|
|
|
SourceType = 1,
|
|
|
StatusCodes = [new() { StatusCode = statusCode }]
|
|
|
});
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.LogError(ex, "任务完成时,设置订单的货物状态时发生异常,orderId={0},taskType={1}", orderId, taskInfo.TASK_TYPE);
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
try
|
|
|
{
|
|
|
await tenantDb.Ado.BeginTranAsync();
|
|
|
|
|
|
await tenantDb.Updateable(taskInfo).UpdateColumns(x => new
|
|
|
{
|
|
|
x.UpdateBy,
|
|
|
x.UpdateTime,
|
|
|
x.UpdateUserName,
|
|
|
x.COMPLETE_DATE,
|
|
|
x.IS_COMPLETE,
|
|
|
x.STATUS,
|
|
|
x.STATUS_NAME,
|
|
|
x.RealUserId,
|
|
|
x.RealUserName
|
|
|
}).ExecuteCommandAsync();
|
|
|
|
|
|
var taskBaseAllocationList = await tenantDb.Queryable<TaskBaseAllocation>().Where(x => x.TaskId == taskInfo.Id).ToListAsync();
|
|
|
if (taskBaseAllocationList.Count != 0)
|
|
|
{
|
|
|
taskBaseAllocationList.ForEach(x =>
|
|
|
{
|
|
|
x.Status = taskStatusEnum.ToString();
|
|
|
x.StatusName = taskStatusEnum.EnumDescription();
|
|
|
x.StatusTime = statusTime;
|
|
|
});
|
|
|
|
|
|
await tenantDb.Updateable(taskBaseAllocationList).UpdateColumns(x => new
|
|
|
{
|
|
|
x.UpdateBy,
|
|
|
x.UpdateTime,
|
|
|
x.UpdateUserName,
|
|
|
x.Status,
|
|
|
x.StatusName,
|
|
|
x.StatusTime
|
|
|
}).ExecuteCommandAsync();
|
|
|
}
|
|
|
await tenantDb.Ado.CommitTranAsync();
|
|
|
}
|
|
|
catch (Exception)
|
|
|
{
|
|
|
await tenantDb.Ado.RollbackTranAsync();
|
|
|
throw;
|
|
|
}
|
|
|
|
|
|
return DataResult.Successed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DataUpdateSuccess)));
|
|
|
}
|
|
|
|
|
|
|
|
|
#region 根据收货地港口英文名解析出起始港对象
|
|
|
/// <summary>
|
|
|
/// 根据收货地港口英文名解析出起始港对象
|
|
|
/// </summary>
|
|
|
/// <param name="portEnName">收货地港口英文名</param>
|
|
|
/// <param name="cachePortLoad">起始港缓存</param>
|
|
|
/// <param name="cacheMapPortLoadFunc">起始港缓存映射</param>
|
|
|
/// <returns>起始港对象</returns>
|
|
|
protected async Task<DataResult<CodePortRes>> PlaceReceiptToPortload(string portEnName, List<CodePortRes> cachePortLoad, Func<Task<DataResult<List<MappingPortRes>>>> cacheMapPortLoadFunc)
|
|
|
{
|
|
|
CodePortRes portInfo = null;
|
|
|
|
|
|
if (string.IsNullOrEmpty(portEnName))
|
|
|
{
|
|
|
return DataResult<CodePortRes>.FailedData(portInfo);
|
|
|
}
|
|
|
|
|
|
// 匹配方式1:精准匹配
|
|
|
portInfo = cachePortLoad.FirstOrDefault(x => x.PortName.Equals(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
|
|
|
// 匹配方式2:起始模糊匹配
|
|
|
portInfo = cachePortLoad.FirstOrDefault(x => x.PortName.StartsWith(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
|
|
|
// 匹配方式3:完整模糊匹配
|
|
|
portInfo = cachePortLoad.FirstOrDefault(x => x.PortName.Contains(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
|
|
|
// 匹配方式4:精准映射匹配
|
|
|
var mapCachePortLoad = await cacheMapPortLoadFunc();
|
|
|
var map = mapCachePortLoad.Data.FirstOrDefault(x => x.Module == MappingModuleConst.RECEIPT_TO_PORTLOAD
|
|
|
&& x.MapName.Equals(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
if (map != null)
|
|
|
{
|
|
|
portInfo = cachePortLoad.FirstOrDefault(x => x.Id == map.LinkId);
|
|
|
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
}
|
|
|
|
|
|
return DataResult<CodePortRes>.FailedData(portInfo);
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 根据交货地港口英文名解析出目的港对象
|
|
|
/// <summary>
|
|
|
/// 根据交货地港口英文名解析出目的港对象
|
|
|
/// </summary>
|
|
|
/// <param name="portEnName">交货地港口英文名</param>
|
|
|
/// <param name="cachePort">目的港缓存</param>
|
|
|
/// <param name="cacheMapPortFunc">目的港缓存映射</param>
|
|
|
/// <returns>目的港对象</returns>
|
|
|
protected async Task<DataResult<CodePortRes>> PlaceDeliveryToPort(string portEnName, List<CodePortRes> cachePort, Func<Task<DataResult<List<MappingPortRes>>>> cacheMapPortFunc)
|
|
|
{
|
|
|
CodePortRes portInfo = null;
|
|
|
|
|
|
if (string.IsNullOrEmpty(portEnName))
|
|
|
{
|
|
|
return DataResult<CodePortRes>.FailedData(portInfo);
|
|
|
}
|
|
|
|
|
|
// 匹配方式1:精准匹配
|
|
|
portInfo = cachePort.FirstOrDefault(x => x.PortName.Equals(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
|
|
|
// 匹配方式2:起始模糊匹配
|
|
|
portInfo = cachePort.FirstOrDefault(x => x.PortName.StartsWith(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
|
|
|
// 匹配方式3:完整模糊匹配
|
|
|
portInfo = cachePort.FirstOrDefault(x => x.PortName.Contains(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
|
|
|
// 匹配方式4:精准映射匹配
|
|
|
var mapCachePort = await cacheMapPortFunc();
|
|
|
var map = mapCachePort.Data.FirstOrDefault(x => x.Module == MappingModuleConst.DELIVERY_TO_PORT
|
|
|
&& x.MapName.Equals(portEnName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
if (map != null)
|
|
|
{
|
|
|
portInfo = cachePort.FirstOrDefault(x => x.Id == map.LinkId);
|
|
|
|
|
|
if (portInfo != null) return DataResult<CodePortRes>.Success(portInfo);
|
|
|
}
|
|
|
|
|
|
return DataResult<CodePortRes>.FailedData(portInfo);
|
|
|
}
|
|
|
#endregion
|
|
|
}
|
|
|
}
|
|
|
|