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.

997 lines
48 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 DS.Module.Core;
using DS.Module.Core.Condition;
using DS.Module.Core.Constants;
using DS.Module.Core.Data;
using DS.Module.Core.Enums.TaskPlat;
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.Interface;
using DS.WMS.Core.Sys.Entity;
using DS.WMS.Core.TaskInteraction.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 ILogger 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;
protected static readonly string createStatusStr = TaskStatusEnum.Create.ToString();
protected static readonly string pendingStatusStr = TaskStatusEnum.Pending.ToString();
protected static readonly string completeStatusStr = TaskStatusEnum.Complete.ToString();
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>());
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
logger = loggerFactory.CreateLogger("TaskFlowLogger");
}
/// <summary>
/// 更新任务主表状态
/// </summary>
/// <param name="taskIds">任务主键数组</param>
/// <param name="columns">需要更新状态的列</param>
public async Task SetTaskStatus(long[] taskIds, params Expression<Func<TaskBaseInfo, bool>>[] columns)
{
logger.LogInformation($"SetTaskStatus taskIds={string.Join(",", taskIds ?? [])}");
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)
{
logger.LogInformation($"SetTaskOwner taskIds={string.Join(",", taskIds ?? [])};userInfo={JsonConvert.SerializeObject(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));
//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))
{
var waitUpdateBusinessTaskIdList = await tenantDb.Queryable<BusinessTask>()
.Where(x => x.BusinessId == item.OUT_BS_NO && x.TaskType == (TaskBaseTypeEnum)taskType)
.Select(x => x.Id)
.ToListAsync();
if (waitUpdateBusinessTaskIdList.Count > 0)
{
await tenantDb.Updateable<BusinessTask>()
.SetColumns(x => x.RecvUsers == userIdStr)
.Where(x => waitUpdateBusinessTaskIdList.Contains(x.Id))
.ExecuteCommandAsync();
}
var waitUpdateFlowInstanceId = 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 (waitUpdateFlowInstanceId != 0)
{
await db.Updateable<FlowInstance>()
.SetColumns(x => x.MakerList == userIdStr)
.Where(x => x.Id == waitUpdateFlowInstanceId)
.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="taskStatusEnum">业务状态</param>
/// <param name="statusTime">状态发生时间</param>
/// <param name="bsno">业务主键</param>
public async Task<DataResult> SetTaskStatus(long taskBaseId, TaskStatusEnum taskStatusEnum, DateTime? statusTime, long? bsno = null)
{
logger.LogInformation($"SetTaskStatus taskBaseId={taskBaseId};taskStatusEnum={taskStatusEnum};statusTime={statusTime};bsno={bsno}");
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, false, bsno);
}
/// <summary>
/// 通过任务主表对象设置任务状态(通用)
/// </summary>
/// <param name="taskInfo">任务主表对象</param>
/// <param name="taskStatusEnum">业务状态</param>
/// <param name="statusTime">状态发生时间</param>
/// <param name="isCheckAllChildTaskComplete">完成任务前是否检查所有子任务是否完成</param>
/// <param name="bsno">业务主键</param>
/// <param name="taskDesc">任务描述</param>
public async Task<DataResult> SetTaskStatus(TaskBaseInfo taskInfo, TaskStatusEnum taskStatusEnum, DateTime? statusTime, bool isCheckAllChildTaskComplete, long? bsno = null, string? taskDesc = null)
{
if (taskInfo is null)
{
throw new ArgumentNullException(nameof(taskInfo));
}
logger.LogInformation($"SetTaskStatus 通用 taskInfo={JsonConvert.SerializeObject(taskInfo)};taskStatusEnum={taskStatusEnum};statusTime={statusTime};isCheckAllChildTaskComplete={isCheckAllChildTaskComplete};bsno={bsno};taskDesc={taskDesc}");
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
if (!taskInfo.IsChild && isCheckAllChildTaskComplete && taskStatusEnum == TaskStatusEnum.Complete)
{
if (await tenantDb.Queryable<TaskBaseInfo>()
.Where(x => x.IsChild == true && x.ParentTaskId == taskInfo.Id && (x.STATUS == createStatusStr || x.STATUS == pendingStatusStr))
.AnyAsync())
{
return DataResult.Failed("任务设置完成前,需要所有子任务非待处理中或挂起中");
}
}
// 修改任务的状态
string taskStatusStr = taskStatusEnum.ToString();
string taskStatusNameStr = taskStatusEnum.EnumDescription();
taskInfo.STATUS = taskStatusStr;
taskInfo.STATUS_NAME = taskStatusNameStr;
taskInfo.RealUserId = long.Parse(user.UserId);
taskInfo.RealUserName = user.UserName;
if (!string.IsNullOrWhiteSpace(taskDesc))
{
taskInfo.TASK_DESP = taskDesc;
}
if (taskStatusEnum == TaskStatusEnum.Complete)
{
taskInfo.IS_COMPLETE = 1;
taskInfo.COMPLETE_DATE = statusTime;
}
// 任务状态为“完成” && 来源为工作流 && 非子任务 时要做的工作:
if (taskStatusEnum == TaskStatusEnum.Complete
&& taskInfo.TASK_SOURCE == TaskSourceEnum.WORK_FLOW.ToString()
&& !taskInfo.IsChild)
{
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
{
var taskBaseAllocationList = await tenantDb.Queryable<TaskBaseAllocation>().Where(x => x.TaskId == taskInfo.Id && x.Status != taskStatusStr).ToListAsync();
List<long> waitUpdateChildTaskIdList = [];
if (!taskInfo.IsChild)
{
waitUpdateChildTaskIdList = await tenantDb.Queryable<TaskBaseInfo>()
.Where(x => x.IsChild == true && x.ParentTaskId == taskInfo.Id && x.STATUS != taskStatusStr)
.Select(x => x.Id)
.ToListAsync();
}
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
}).UpdateColumnsIF(!string.IsNullOrWhiteSpace(taskDesc), x => new
{
x.TASK_DESP
}).ExecuteCommandAsync();
// 更新主任务的任务接收人下的状态
if (taskBaseAllocationList.Count != 0)
{
taskBaseAllocationList.ForEach(x =>
{
x.Status = taskStatusStr;
x.StatusName = taskStatusNameStr;
x.StatusTime = statusTime;
});
await tenantDb.Updateable(taskBaseAllocationList).UpdateColumns(x => new
{
x.UpdateBy,
x.UpdateTime,
x.UpdateUserName,
x.Status,
x.StatusName,
x.StatusTime
}).ExecuteCommandAsync();
}
if (!taskInfo.IsChild && waitUpdateChildTaskIdList.Count > 0)
{
// 更新子任务的状态
await tenantDb.Updateable<TaskBaseInfo>()
.SetColumns(x => new TaskBaseInfo()
{
UpdateBy = long.Parse(user.UserId),
UpdateTime = DateTime.Now,
UpdateUserName = user.UserName,
IS_COMPLETE = taskInfo.IS_COMPLETE,
COMPLETE_DATE = taskInfo.COMPLETE_DATE,
STATUS = taskInfo.STATUS,
STATUS_NAME = taskInfo.STATUS_NAME,
RealUserId = taskInfo.RealUserId,
RealUserName = taskInfo.RealUserName,
})
.Where(x => waitUpdateChildTaskIdList.Contains(x.Id) && x.STATUS != taskStatusStr)
.ExecuteCommandAsync();
// 更新子任务的任务接收人下的状态
await tenantDb.Updateable<TaskBaseAllocation>()
.SetColumns(x => new TaskBaseAllocation()
{
UpdateBy = long.Parse(user.UserId),
UpdateTime = DateTime.Now,
UpdateUserName = user.UserName,
Status = taskInfo.STATUS,
StatusName = taskInfo.STATUS_NAME,
StatusTime = statusTime,
})
.Where(x => waitUpdateChildTaskIdList.Contains(x.TaskId) && x.Status != taskStatusStr)
.ExecuteCommandAsync();
}
await tenantDb.Ado.CommitTranAsync();
}
catch (Exception)
{
await tenantDb.Ado.RollbackTranAsync();
throw;
}
return DataResult.Successed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DataUpdateSuccess)));
}
/// <summary>
/// 是否具有指定任务列表的处理权限
/// </summary>
/// <returns>成功:全部具有处理权限;失败:存在不具有处理权限的任务,同时返回任务流水号列表</returns>
public async Task<DataResult<List<string>>> HasTaskHandleAuthorityWithBsno(IEnumerable<(long bsno, TaskBaseTypeEnum taskType)> businessTaskList)
{
if (businessTaskList == null || businessTaskList.Count() == 0)
{
return DataResult<List<string>>.Success([]);
}
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
var userId = long.Parse(user.UserId);
List<long> taskIdList = [];
foreach (var (bsno, taskType) in businessTaskList)
{
var taskTypeStr = taskType.ToString();
var taskId = await tenantDb.Queryable<TaskBaseInfo>().Where(x => x.OUT_BS_NO == bsno && x.TASK_TYPE == taskTypeStr && x.STATUS != TaskStatusEnum.Cancel.ToString()).Select(x => x.Id).FirstAsync();
if (taskId != 0)
{
taskIdList.Add(taskId);
}
}
return await HasTaskHandleAuthority(taskIdList);
}
/// <summary>
/// 是否具有指定任务列表的处理权限
/// </summary>
/// <returns>成功:全部具有处理权限;失败:存在不具有处理权限的任务,同时返回任务流水号列表</returns>
public async Task<DataResult<List<string>>> HasTaskHandleAuthority(IEnumerable<long> taskIdList)
{
if (taskIdList == null || taskIdList.Count() == 0)
{
return DataResult<List<string>>.Success([]);
}
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
var userId = long.Parse(user.UserId);
var taskList = await tenantDb.Queryable<TaskBaseInfo>().Where(x => taskIdList.Contains(x.Id) && x.IS_PUBLIC == 0).Select(x => new { x.Id, x.TASK_NO }).ToListAsync();
var newTaskIdList = taskList.Select(x => x.Id);
var hasAuthorityTaskIdList = await tenantDb.Queryable<TaskBaseAllocation>().Where(x => newTaskIdList.Contains(x.TaskId) && x.UserId == userId).Select(x => x.TaskId).ToListAsync();
var notHasAuthorityTaskIdList = newTaskIdList.Except(hasAuthorityTaskIdList);
if (notHasAuthorityTaskIdList.Any())
{
var taskNoList = taskList.Where(x => notHasAuthorityTaskIdList.Contains(x.Id)).Select(x => x.TASK_NO).ToList();
return DataResult<List<string>>.FailedData(taskNoList);
}
else
{
return DataResult<List<string>>.Success([]);
}
}
/// <summary>
/// 根据订单Id列表获取关联的任务流水号
/// </summary>
public async Task<DataResult<List<string>>> GetSeaExportAllotTaskNo(IEnumerable<long> seaExportIdList)
{
if (seaExportIdList == null || seaExportIdList.Count() == 0)
{
return DataResult<List<string>>.Success([]);
}
var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
var taskNoList = await tenantDb.Queryable<TaskBaseInfo>()
.Where(x => x.OUT_BS_NO != null && seaExportIdList.Contains((long)x.OUT_BS_NO) && x.STATUS == TaskStatusEnum.Create.ToString())
.Select(x => x.TASK_NO)
.ToListAsync();
return DataResult<List<string>>.Success(taskNoList);
}
#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
/// <summary>
/// 获取不创建任务的任务类型列表
/// </summary>
public async Task<List<TaskBaseTypeEnum>> GetNotCreateTaskTypeList(SqlSugarScopeProvider? tenantDb = null)
{
SqlSugarScopeProvider thisTenantDb = tenantDb ?? saasDbService.GetBizDbScopeById(user.TenantId);
var configTypeStr = TaskConfigTypeCodeEnum.NotCreateTask.ToString();
var notCreateTaskTypeList = await thisTenantDb.Queryable<TaskFlowTypeConfig>()
.Where(x => x.ConfigTypeCode == configTypeStr && x.ConfigValue == "1")
.Select(x => x.TaskType)
.ToListAsync();
List<TaskBaseTypeEnum> result = [];
var type = typeof(TaskBaseTypeEnum);
foreach (var taskType in notCreateTaskTypeList)
{
if (Enum.TryParse(type, taskType, out object? temp))
{
result.Add((TaskBaseTypeEnum)temp);
}
}
return result;
}
}
}