using System.Text; using System.Threading.Tasks; using DS.Module.Core; using DS.Module.Core.Data; using DS.Module.Core.Helpers; using DS.Module.DjyRulesEngine; using DS.WMS.Core.Fee.Dtos; using DS.WMS.Core.Fee.Entity; using DS.WMS.Core.Flow.Dtos; using DS.WMS.Core.Flow.Entity; using DS.WMS.Core.Flow.Interface; using DS.WMS.Core.Flow.Method; using DS.WMS.Core.Info.Interface; using DS.WMS.Core.Op.Dtos.TaskInteraction; using DS.WMS.Core.Op.Entity; using DS.WMS.Core.Op.Entity.TaskInteraction; using DS.WMS.Core.Op.Interface.TaskInteraction; using DS.WMS.Core.Op.Method.TaskInteraction.ActionSelector; using DS.WMS.Core.Sys.Entity; using DS.WMS.Core.TaskPlat; using DS.WMS.Core.TaskPlat.Dtos; using DS.WMS.Core.TaskPlat.Entity; using DS.WMS.Core.TaskPlat.Interface; using Mapster; using Masuit.Tools; using Masuit.Tools.Systems; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using SqlSugar; using static iText.StyledXmlParser.Jsoup.Select.Evaluator; namespace DS.WMS.Core.Op.Method.TaskInteraction { /// /// 任务交互服务 /// public class TaskService : ServiceBase, ITaskService { const long PERMISSION_ID = 1815294400855674880; /// /// 获取支持审核的任务类型 /// public static readonly TaskBaseTypeEnum[] AuditTaskTypes; /// /// 通过任务台创建,而非通过工作流创建的任务类型,需手动调用函数来创建驳回任务,且重新提交时不创建工作流 /// static readonly TaskBaseTypeEnum[] AuditTaskTypesManualReject; internal static readonly TaskBaseTypeEnum[] FeeTypes = [TaskBaseTypeEnum.FEE_AUDIT, TaskBaseTypeEnum.FEE_MODIFY_AUDIT, TaskBaseTypeEnum.FEE_DELETE_AUDIT]; static readonly ApiFox Api; static TaskService() { var dic = EnumExt.GetDictionary(typeof(TaskBaseTypeEnum)); var enumValues = dic.Where(x => x.Key != -1).GroupBy(x => Math.Abs(x.Key)).Select(x => new { Value = x.Key, Count = x.Count() }).Where(x => x.Count > 1); AuditTaskTypesManualReject = [TaskBaseTypeEnum.WAIT_SI]; // 这里把非工作流创建任务类型排除一下,防止虽然需要审核但是不需要创建工作流的任务创建工作流 AuditTaskTypes = enumValues.Select(x => (TaskBaseTypeEnum)Math.Abs(x.Value)).Distinct().Except(AuditTaskTypesManualReject).ToArray(); Api = new ApiFox(); } /// /// 确定任务类型是否为业务订单任务 /// /// 任务类型 /// public static bool IsOrderType(TaskBaseTypeEnum taskType) { int val = (int)taskType; return val >= 200 && val <= 399; } /// /// 返回审核任务类型所对应的驳回类型 /// /// 审核任务类型 /// public static TaskBaseTypeEnum? GetRejectedType(TaskBaseTypeEnum auditTaskType) { if (!AuditTaskTypes.Contains(auditTaskType)) return null; int negativeVal = -(int)auditTaskType; return (TaskBaseTypeEnum)negativeVal; } /// /// 任务管理服务 /// protected ITaskManageService ManagerService { get; private set; } /// /// 日志服务 /// protected ITaskLogService LogService { get; private set; } /// /// 任务分配 /// protected ITaskAllocationService TaskAllocationService { get; private set; } /// /// 工作流服务 /// protected Lazy FlowService { get; private set; } /// /// 动作服务 /// protected IActionManagerService ActionService { get; private set; } readonly Lazy ClientParamService; /// /// 规则库服务 /// protected Lazy RuleEngineService { get; private set; } readonly IConfiguration configuration; /// /// 初始化 /// /// public TaskService(IServiceProvider provider) : base(provider) { ManagerService = provider.GetRequiredService(); LogService = provider.GetRequiredService(); TaskAllocationService = provider.GetRequiredService(); ActionService = provider.GetRequiredService(); configuration = provider.GetRequiredService(); ClientParamService = new Lazy(provider.GetRequiredService()); FlowService = new Lazy(provider.GetRequiredService()); RuleEngineService = new Lazy(provider.GetRequiredService()); TenantDb.QueryFilter.Clear(); } /// /// 批量创建关联任务 /// /// /// 是否使用事务 /// public async Task CreateMultipleTaskAsync(TaskCreationRequest request, bool useTransaction = true) { var ids = request.Ids; ids ??= [request.BusinessId]; List strList = []; DataResult result; for (int i = 0; i < ids.Length; i++) { request.BusinessId = ids[i]; result = await CreateTaskAsync(request, useTransaction); if (!result.Succeeded) { if (result.Data == null) return result; strList.Add(result.Data.ToString()!); } } if (strList.Count == 0) return DataResult.Success; return DataResult.Failed(string.Format( MultiLanguageConst.GetDescription(MultiLanguageConst.OrderTaskSubmitted), string.Join("、", strList))); } /// /// 创建关联任务 /// /// /// 是否使用事务 /// public async Task CreateTaskAsync(TaskCreationRequest request, bool useTransaction = true) { if (request.BusinessType.HasValue) { bool isBizValid = false; switch (request.BusinessType.Value) { case BusinessType.OceanShippingExport: isBizValid = await TenantDb.Queryable().AnyAsync(x => x.Id == request.BusinessId); break; } if (!isBizValid) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Biz_Not_Valid)); } if (!await HasAuthorizedAsync()) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.ModuleUnauthorized)); if (request.HasCabin.GetValueOrDefault() && request.BusinessType.HasValue) { //如果为现舱,获取下一任务类型进行创建 var nextType = await GetNextTypeAsync(request.BusinessId, request.BusinessType.Value, request.TaskType, request.HasCabin); if (nextType.HasValue) request.TaskTypeName = nextType.Value.ToString(); } DataResult result = DataResult.Success; bool updateFlag = false; var task = await GetQuery(request.BusinessId, request.BusinessType, request.TaskType).FirstAsync(); if (task != null) { //可审核任务类型允许挂起或取消的任务重新提交 if (AuditTaskTypes.Contains(request.TaskType) && (task.TaskStatus == TaskStatusEnum.Pending || task.TaskStatus == TaskStatusEnum.Cancel)) { updateFlag = true; } else //其他任务类型返回已存在提示 { if (IsOrderType(request.TaskType)) { string custNO = await TenantDb.Queryable().Where(x => x.Id == request.BusinessId) .Select(x => x.CustomerNo).FirstAsync(); result = DataResult.Failed(string.Format( MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.OrderTaskSubmitted)) + request.TaskType.GetDescription(), custNO)); result.Data = custNO; return result; } return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskExists)); } } if (request.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT) { result = await CheckRulesAsync(request.BusinessId, request.BusinessType.Value, RuleEngineType.COMMON_ORDER_AUDIT); if (!result.Succeeded) return result; } if (useTransaction) await TenantDb.Ado.BeginTranAsync(); try { TaskManageOrderMessageInfo? info = null; if (task == null) { long tenatId = long.Parse(User.TenantId); string tenatName = Db.Queryable().Where(x => x.Id == tenatId).Select(x => x.Name).First(); info = new TaskManageOrderMessageInfo { Head = new TaskManageOrderMessageHeadInfo { GID = Guid.NewGuid().ToString(), BSNO = request.BusinessId, MessageType = "WORK_FLOW_TASK", SenderId = "WorkFlow", SenderName = "工作流平台", ReceiverId = "TaskManage", ReceiverName = "任务管理平台", Version = "1.0", RequestDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), RequestAction = "Add" }, Main = new TaskManageOrderMessageMainInfo { TaskType = request.TaskType, TaskSource = TaskSourceEnum.WORK_FLOW, TaskTitle = request.TaskTitle, TaskDesp = request.TaskDescription, TaskUserId = User.UserId, TaskUserName = User.UserName, TaskTenatId = tenatId, TaskTenatName = tenatName, IsCheckExistsByTaskType = true } }; if ((request.RecvUserIdList == null || request.RecvUserIdList.Length == 0) && IsOrderType(request.TaskType)) { //根据配置获取默认接收人 info.Main.RecvUserInfoList = await GetRecvUsersAsync(request.BusinessId, request.BusinessType, request.TaskType); } else if (AuditTaskTypes.Contains(request.TaskType)) { //审核任务默认为提交人,生成工作流后替换为工作流执行人 info.Main.RecvUserInfoList = await FillInUserInfoAsync(long.Parse(User.UserId)); } else { info.Main.RecvUserInfoList = await FillInUserInfoAsync(request.RecvUserIdList); } //仅为业务订单任务自动填充任务标题/描述 if (info.Main.TaskTitle.IsNullOrEmpty() && IsOrderType(request.TaskType)) { var biz = await TenantDb.Queryable().Select(x => new { x.Id, x.CustomerNo, x.MBLNO, x.Vessel, x.Voyno, x.ETD, x.DischargePortId, x.DischargePortCode, x.DischargePort, x.LoadPortId, x.LoadPortCode, x.LoadPort, x.CarrierId, x.Carrier, //x.BookingNo, //x.HBLNO, //x.CustomerNum, }).FirstAsync(x => x.Id == request.BusinessId); info.Main.TaskTitle = $"【{request.TaskType.GetDescription()}】{biz?.CustomerNo} {biz?.Vessel} {biz?.Voyno} ETD:{biz?.ETD?.ToString("yyyy-MM-dd")}"; info.Main.CustomerNo = biz?.CustomerNo; info.Main.MBlNo = biz?.MBLNO; info.Main.ETD = biz?.ETD; info.Main.PortDischarge = biz?.DischargePort; info.Main.PortDischargeCode = biz?.DischargePortCode; info.Main.PortDischargeId = biz?.DischargePortId; info.Main.PortLoad = biz?.LoadPort; info.Main.PortLoadId = biz?.LoadPortId; info.Main.PortLoadCode = biz?.LoadPortCode; info.Main.CarrierId = biz?.Carrier; info.Main.CarrierPK = biz?.CarrierId; //info.Main.HBLNO = biz?.HBLNO; //info.Main.BookingNo = biz?.BookingNo; //info.Main.CustomsNum = biz?.CustomerNum; } info.Main.TaskDesp = info.Main.TaskDesp ?? info.Main.TaskTitle; if (!FeeTypes.Contains(request.TaskType)) { result = await ManagerService.InitTaskJob(info); if (!result.Succeeded) return result; } task = new BusinessTask { BusinessId = request.BusinessId, BusinessType = request.BusinessType, TaskType = request.TaskType, TaskStatus = TaskStatusEnum.Create, RecvUsers = string.Join(',', info.Main.RecvUserInfoList.Select(x => x.RecvUserId)) ?? string.Empty, NextType = request.NextType, CreateBy = long.Parse(User.UserId), CreateTime = DateTime.Now }; if (IsOrderType(request.TaskType) && !task.NextType.HasValue) task.NextType = await GetNextTypeAsync(task); await TenantDb.Insertable(task).ExecuteCommandAsync(); result = await OnTaskCreated(task); if (!result.Succeeded) return result; //记录日志 await LogService.WriteLogAsync(task); } //审核任务需创建工作流 if (AuditTaskTypes.Contains(request.TaskType)) { result = await CreateAndStartWorkflow(task); if (!result.Succeeded) return result; if (updateFlag) { if (!FeeTypes.Contains(request.TaskType)) { result = await SetTaskStatusAsync(new TaskUpdateRequest { BusinessId = request.BusinessId, BusinessType = request.BusinessType, TaskTypeName = request.TaskTypeName, TaskStatus = TaskStatusEnum.Create }, false); if (!result.Succeeded) return result; } } else if (FeeTypes.Contains(request.TaskType)) //费用审核需拆分任务 { var instance = result.Data as FlowInstance; var ids = FlowInstanceService.GetMarkers(instance).Select(long.Parse).ToArray(); var recvUserList = await FillInUserInfoAsync(ids); var fee = await TenantDb.Queryable().Where(x => x.Id == request.BusinessId) .Select(x => new { x.Id, x.BusinessId, x.BusinessType }).FirstAsync(); foreach (var item in recvUserList) { var copiedInfo = info.DeepClone(); copiedInfo.Head.BSNO = fee.BusinessId; //替换为费用关联的订单ID copiedInfo.Main.RecvUserInfoList.Clear(); copiedInfo.Main.RecvUserInfoList.Add(item); copiedInfo.Main.ExtData = new { fee.BusinessType, FeeId = fee.Id }; result = await ManagerService.InitTaskJob(copiedInfo); if (!result.Succeeded) { await Db.Deleteable(instance).ExecuteCommandAsync(); return result; } } } } if (useTransaction) await TenantDb.Ado.CommitTranAsync(); return DataResult.Success; } catch (Exception ex) { if (useTransaction) await TenantDb.Ado.RollbackTranAsync(); if (result.Data is FlowInstance instance) await Db.Deleteable(instance).ExecuteCommandAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } /// /// 创建并启动审批工作流 /// /// /// 同时变更任务执行人 /// protected internal async Task CreateAndStartWorkflow(BusinessTask task, bool changeMarker = true) { var template = await FindTemplateAsync(task.TaskType); if (template == null) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TemplateNotFound)); var result = FlowService.Value.CreateFlowInstance(new CreateFlowInstanceReq { BusinessId = task.BusinessId, BusinessType = task.BusinessType, TemplateId = template.Id }); //创建并启动实例 if (result.Succeeded) { var instance = result.Data as FlowInstance; task.FlowId = instance!.Id; await TenantDb.Updateable(task).UpdateColumns(x => x.FlowId).ExecuteCommandAsync(); if (instance.FlowStatus == FlowStatusEnum.Approve) { await SetTaskStatusAsync(new TaskUpdateRequest { BusinessId = task.BusinessId, BusinessType = task.BusinessType, RejectReason = instance.Note, TaskTypeName = task.TaskType.ToString(), TaskStatus = TaskStatusEnum.Complete }, false); } else { result = FlowService.Value.StartFlowInstance(instance.Id.ToString()); instance = result.Data as FlowInstance; if (result.Succeeded && changeMarker && !FeeTypes.Contains(task.TaskType)) { string[] ids = FlowInstanceService.GetMarkers(instance); //变更任务接收人为所有审批执行人 var users = await FillInUserInfoAsync(ids.Select(long.Parse).ToArray()); result = await ManagerService.TransferTask(task.BusinessId, task.TaskType, users, TenantDb); if (result.Succeeded) { task.RecvUsers = string.Join(",", ids); await TenantDb.Updateable(task).UpdateColumns(x => x.RecvUsers).ExecuteCommandAsync(); } } } result.Data = instance; } return result; } /// /// 当任务创建时调用 /// /// /// protected virtual Task OnTaskCreated(BusinessTask task) { return Task.FromResult(DataResult.Success); } /// /// 设置任务状态 /// /// /// 是否使用事务 /// public async Task SetTaskStatusAsync(TaskUpdateRequest request, bool useTransaction = true) { if (!await HasAuthorizedAsync()) return DataResult.FailedWithDesc(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.ModuleUnauthorized))); if (useTransaction) await TenantDb.Ado.BeginTranAsync(); try { BusinessTask task = await GetQuery(request.BusinessId, request.BusinessType, request.TaskType).FirstAsync(); if (task == null) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskNotExists)); if (task.TaskStatus == TaskStatusEnum.Complete) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCompleted)); //if (task.TaskStatus == TaskStatusEnum.Cancel) // return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCancelled)); //触发任务状态变更通知 if (task.TaskStatus != request.TaskStatus) await OnTaskStatusChanged(request); task.RejectReason = request.RejectReason; //更新当前任务状态 task.TaskStatus = request.TaskStatus; await TenantDb.Updateable(task).UpdateColumns(x => new { x.TaskStatus, x.RejectReason }).ExecuteCommandAsync(); DataResult result; if (FeeTypes.Contains(request.TaskType)) { var biz = await TenantDb.Queryable().Where(x => x.Id == request.BusinessId) .Select(x => new { x.BusinessId, x.BusinessType }).FirstAsync(); var relativeTasks = await GetFeeTasks(biz.BusinessType, biz.BusinessId); var groups = relativeTasks.GroupBy(x => x.TaskStatus).Select(x => x.Key).ToList(); if (groups.Count == 1 && groups.All(x => x == request.TaskStatus)) { result = await ManagerService.SetTaskStatusWithBsno(request.BusinessId, request.TaskType, request.TaskStatus, DateTime.Now, request.TaskDesc, [.. task.RecvUserIdArray]); if (!result.Succeeded) return DataResult.Failed(result.Message, result.MultiCode); } } else { result = await ManagerService.SetTaskStatusWithBsno(request.BusinessId, request.TaskType, request.TaskStatus, DateTime.Now, request.TaskDesc); if (!result.Succeeded) return DataResult.Failed(result.Message, result.MultiCode); } if (task.TaskStatus == TaskStatusEnum.Complete && task.NextType.HasValue && request.AutoCreateNext) { //存在下一任务,则继续创建 var req = new TaskCreationRequest { BusinessId = request.BusinessId, BusinessType = request.BusinessType, TaskTypeName = task.NextType.Value.ToString() }; result = await CreateTaskAsync(req, false); if (!result.Succeeded) return DataResult.Failed("创建下一关联任务时返回错误:" + result.Message, result.MultiCode); } //记录日志 await LogService.WriteLogAsync(request); if (useTransaction) await TenantDb.Ado.CommitTranAsync(); if (task.TaskStatus == TaskStatusEnum.Complete)//目前限制任务完成才触发 { Dictionary? dic = null; if (task.NextType == TaskBaseTypeEnum.WAIT_BOOKING) { dic = []; var param = await ClientParamService.Value.GetParamAsync(task.BusinessId, BookingSelector.Booking_Route, (x, y) => x.CustomerId == y.ForwarderId); dic[TaskFlowDataNameConst.ClientParam] = param; } await ActionService.TriggerActionAsync(task, additionalData: dic); } return DataResult.Success; } catch (Exception ex) { if (useTransaction) await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } /// /// 当任务状态发生变化时调用 /// /// /// protected virtual Task OnTaskStatusChanged(TaskUpdateRequest request) { return Task.CompletedTask; } /// /// 获取业务的任务信息 /// /// 业务ID /// 业务类型 /// 任务类型 /// public async Task>> GetTasks(long bsId, BusinessType? businessType, params TaskBaseTypeEnum[] types) { var list = await TenantDb.Queryable().Where(x => x.BusinessId == bsId) .WhereIF(businessType.HasValue, x => x.BusinessType == businessType) .WhereIF(types != null && types.Length > 0, x => types.Contains(x.TaskType)) .Select().ToListAsync(); var result = DataResult>.Success(list); result.Count = list.Count; return result; } /// /// 获取指定业务的所有关联费用类任务 /// /// 业务类型 /// 业务ID /// protected internal async Task> GetFeeTasks(BusinessType bsType, params long[] bsIds) { return await TenantDb.Queryable() .InnerJoin((t, f) => t.BusinessId == f.Id) .Where((t, f) => bsIds.Contains(f.BusinessId) && f.BusinessType == bsType && FeeTypes.Contains(t.TaskType) && t.RecvUsers.Contains(User.UserId)) .Select((t, f) => new BusinessTask { Id = t.Id, BusinessId = t.BusinessId, TaskType = t.TaskType, TaskStatus = t.TaskStatus, RecvUsers = t.RecvUsers }).ToListAsync(); } /// /// 返回指定类型的任务是否已存在 /// /// 业务ID /// 业务类型 /// 任务类型 /// public async Task> ExistsAsync(long id, BusinessType businessType, TaskBaseTypeEnum type) { var value = await TenantDb.Queryable().AnyAsync(x => x.BusinessId == id && x.BusinessType == businessType && x.TaskType == type); return DataResult.Success(value); } /// /// 返回任务交互模块是否已授权 /// /// public virtual async Task HasAuthorizedAsync() { long tid = long.Parse(User.TenantId); var authStr = await Db.Queryable().ClearFilter(typeof(ITenantId)).Where(x => x.PermissionId == PERMISSION_ID && x.TenantId == tid && SqlFunc.Subqueryable().Where(spt => spt.PermissionId == x.PermissionId && x.TenantId == tid).Any()) .Select(x => x.AuthNum).FirstAsync(); if (authStr.IsNullOrEmpty()) return false; var appSecret = await Db.Queryable().Where(x => x.Id == tid).Select(x => x.AppSecret).FirstAsync(); return int.TryParse(EncrypteHelper.DecryptData(authStr, appSecret), out int authNum) && authNum > 0; } /// /// 重新发起任务审核 /// /// /// 是否使用事务 /// public async Task SubmitAuditAsync(TaskRequest request, bool useTransaction = true) { var task = await GetQuery(request.BusinessId, request.BusinessType, request.TaskType).FirstAsync(); if (task == null) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData)); if (task.TaskStatus == TaskStatusEnum.Complete) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCompleted)); if (task.TaskStatus == TaskStatusEnum.Pending) { DataResult result; if (task.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT) { result = await CheckRulesAsync(request.BusinessId, request.BusinessType.Value, RuleEngineType.COMMON_ORDER_AUDIT); if (!result.Succeeded) return result; } if (useTransaction) await TenantDb.Ado.BeginTranAsync(); try { var req = new TaskUpdateRequest { AutoCreateNext = false, BusinessId = request.BusinessId, BusinessType = request.BusinessType, TaskStatus = TaskStatusEnum.Create, TaskTypeName = request.TaskTypeName }; //重置任务为待处理 result = await SetTaskStatusAsync(req, false); if (!result.Succeeded) return DataResult.Failed(result.Message, result.MultiCode); //创建&启动工作流 DataResult result2; if (!AuditTaskTypesManualReject.Contains(request.TaskType)) { result2 = await CreateAndStartWorkflow(task, false); if (!result2.Succeeded) return result2; } else { result2 = DataResult.Successed("操作成功", MultiLanguageConst.OperationSuccess); } await TenantDb.Updateable().SetColumns(x => x.TaskStatus == TaskStatusEnum.Create) .Where(x => x.BusinessId == request.BusinessId && x.BusinessType == request.BusinessType && x.TaskType == request.TaskType) .ExecuteCommandAsync(); //记录日志 await LogService.WriteLogAsync(req, "重新审批"); if (useTransaction) await TenantDb.Ado.CommitTranAsync(); return result2; } catch (Exception ex) { if (useTransaction) await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskStatusNotSupported)); } /// /// 撤销审核任务 /// /// /// 是否使用事务 /// public async Task WithdrawAsync(TaskRequest request, bool useTransaction = true) { if (!AuditTaskTypes.Contains(request.TaskType)) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskTypeNotSupported)); var task = await GetQuery(request.BusinessId, request.BusinessType, request.TaskType).Select(x => new BusinessTask { Id = x.Id, TaskStatus = x.TaskStatus, FlowId = x.FlowId, RecvUsers = x.RecvUsers }).FirstAsync(); if (task == null) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData)); if (task.TaskStatus == TaskStatusEnum.Complete) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCompleted)); if (!task.FlowId.HasValue) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.NotInFlows)); DataResult result; DateTime dt = DateTime.Now; if (useTransaction) await TenantDb.Ado.BeginTranAsync(); try { if (FeeTypes.Contains(request.TaskType)) { //if (request.ExtraData is not FeeRecord fee) // return DataResult.Failed("缺少请求传递的费用数据"); //var relativeTasks = await GetFeeTasks(fee.BusinessType, fee.BusinessId); //if (relativeTasks.Count == 1 || relativeTasks.Where(x => x.BusinessId != request.BusinessId && x.TaskStatus != TaskStatusEnum.Complete).All(x => x.TaskStatus == TaskStatusEnum.Cancel)) //{ // result = await ManagerService.SetTaskStatusWithBsno(fee.BusinessId, request.TaskType, TaskStatusEnum.Cancel, dt, userIdList: [.. task.RecvUserIdArray]); // if (!result.Succeeded) // return result; //} } else { result = await ManagerService.SetTaskStatusWithBsno(request.BusinessId, request.TaskType, TaskStatusEnum.Cancel, dt); if (!result.Succeeded) return result; } result = await FlowService.Value.WithdrawAsync([task.FlowId.Value], "用户撤销审核"); if (!result.Succeeded) return result; await TenantDb.Updateable().Where(x => x.Id == task.Id).SetColumns( x => x.TaskStatus == TaskStatusEnum.Cancel).ExecuteCommandAsync(); await LogService.WriteLogAsync(new BusinessTaskLog { ActionType = ActionType.StatusChanged, AuditStatus = FlowStatusEnum.Draft, BusinessId = request.BusinessId, BusinessType = request.BusinessType, CreateBy = long.Parse(User.UserId), CreateTime = dt, TaskStatus = TaskStatusEnum.Cancel, TaskType = request.TaskType }); if (request.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT) { await TenantDb.Updateable().SetColumns(x => x.BusinessStatusName == null) .Where(x => x.Id == request.BusinessId).ExecuteCommandAsync(); } if (useTransaction) await TenantDb.Ado.CommitTranAsync(); return DataResult.Success; } catch (Exception ex) { if (useTransaction) await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } /// /// 任务审核 /// /// public async Task AuditAsync(TaskAuditRequest request) { var tasks = await TenantDb.Queryable().Where(x => x.TaskType == request.TaskType && request.Ids.Contains(x.BusinessId)) .WhereIF(request.BusinessType.HasValue, x => x.BusinessType == request.BusinessType.Value).ToListAsync(); if (tasks.Count == 0) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskNotExists)); if (tasks.Count != request.Ids.Length) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCountNotMatch)); if (tasks.Exists(x => x.TaskStatus == TaskStatusEnum.Complete)) { return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCompleted)); } else if (tasks.Exists(x => x.TaskStatus != TaskStatusEnum.Create && x.TaskStatus != TaskStatusEnum.Pending)) { return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskAuditStatusError)); } if (tasks.Exists(x => !AuditTaskTypesManualReject.Contains(x.TaskType) && x.FlowId == null)) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FlowNotFound)); //审核前判断是否为此任务的处理人 //var hasAuthor = await ManagerService.HasTaskHandleAuthorityWithBsno(tasks.Select(x => (x.BusinessId, x.TaskType))); //if (!hasAuthor.Succeeded && hasAuthor.Data?.Count > 0) //{ // if (tasks.Count == 1) // { // return DataResult.Failed(string.Format( // MultiLanguageConst.GetDescription(MultiLanguageConst.TaskNotHaveAuthorSingle), string.Join("、", hasAuthor.Data))); // } // else // { // return DataResult.Failed(string.Format( // MultiLanguageConst.GetDescription(MultiLanguageConst.TaskNotHaveAuthor), string.Join("、", hasAuthor.Data!))); // } //} DataResult result = DataResult.Success; var flowIds = tasks.Where(x => !AuditTaskTypesManualReject.Contains(x.TaskType)).Select(x => x.FlowId.Value); var flowInstances = await Db.Queryable().Where(x => flowIds.Contains(x.Id)).ToListAsync(); foreach (var instance in flowInstances) { //如果当前审批为终审,则调用规则库进行校验 if (request.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT && instance.GetMarkerList().Length == 1) { result = await CheckRulesAsync(instance.BusinessId, request.BusinessType.GetValueOrDefault(), RuleEngineType.COMMON_OCEAN_BOOKING); if (!result.Succeeded) return result; } result = FlowService.Value.AuditFlowInstance(new FlowAuditInfo { AuditNote = request.Remark, Status = request.Result, Instance = instance }); if (!result.Succeeded) return result; var req = new TaskUpdateRequest { BusinessId = instance.BusinessId, BusinessType = instance.BusinessType, RejectReason = request.Remark, TaskTypeName = request.TaskType.ToString(), TaskStatus = request.Result == 1 ? TaskStatusEnum.Complete : TaskStatusEnum.Pending, AutoCreateNext = true }; //根据审批结果更新任务状态 await SetTaskStatusAsync(req); if (instance.FlowStatus == FlowStatusEnum.Reject) result.Message = MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskRejected)); if (IsOrderType(request.TaskType)) result.Data = new { instance.IsCompleted, instance.FlowStatus }; } if (request.Result == 2) { var manualRejectTask = tasks.Where(x => AuditTaskTypesManualReject.Contains(x.TaskType)).ToList(); foreach (var item in manualRejectTask) { // 驳回工作流创建的任务时会自动通过http创建驳回任务,但是驳回非工作流创建的任务不会自动创建驳回任务,所以在下面手动调用 var req = new TaskUpdateRequest { BusinessId = item.BusinessId, BusinessType = request.BusinessType.GetValueOrDefault(), RejectReason = request.Remark, TaskTypeName = request.TaskType.ToString(), TaskStatus = request.Result == 1 ? TaskStatusEnum.Complete : TaskStatusEnum.Pending, AutoCreateNext = false }; //根据审批结果更新任务状态 await SetTaskStatusAsync(req); // 创建相应的驳回任务 await UpdateBusinessAsync(new FlowCallback() { AuditType = request.TaskType, BusinessId = item.BusinessId, BusinessType = request.BusinessType.GetValueOrDefault(), FlowStatus = FlowStatusEnum.Reject, InstanceId = 0, RejectReason = request.Remark, }); } } return result; } /// /// 通知审批执行人变更 /// /// 回调信息 /// /// 为null时引发 public virtual async Task MarkerChangedAsync(MarkerChangedCallback callback) { ArgumentNullException.ThrowIfNull(callback, nameof(callback)); long userId = long.Parse(User.UserId); var users = await FillInUserInfoAsync(userId); var dt = DateTime.Now; await ManagerService.SetTaskUserStatus( callback.BusinessId, callback.AuditType.GetValueOrDefault(), TaskStatusEnum.Complete, //callback.Status == FlowStatusEnum.Approve ? TaskStatusEnum.Complete : TaskStatusEnum.Pending dt, users); //记录日志 await LogService.WriteLogAsync(new BusinessTaskLog { ActionType = ActionType.Audit, AuditStatus = callback.Status, BusinessId = callback.BusinessId, BusinessType = callback.BusinessType.Value, CreateBy = userId, CreateTime = dt, TaskStatus = TaskStatusEnum.Complete, TaskType = callback.AuditType.GetValueOrDefault(), RecvUsers = users.Count > 0 ? users[0].RecvUserName : null }); } /// /// 审批完成回调更新 /// /// 回调信息 /// /// 为null时引发 public virtual async Task UpdateBusinessAsync(FlowCallback callback) { ArgumentNullException.ThrowIfNull(callback, nameof(callback)); if (callback.AuditType == null || callback.BusinessType == null) return; string remark = "终审完成,审批结果为:" + callback.FlowStatus.GetDescription(); if (callback.FlowStatus == FlowStatusEnum.Reject) { var task = await GetQuery(callback.BusinessId, callback.BusinessType, callback.AuditType.Value).FirstAsync(); var request = new TaskCreationRequest { BusinessId = callback.BusinessId, BusinessType = callback.BusinessType, TaskTypeName = GetRejectedType(callback.AuditType.Value).ToString(), RecvUserIdList = [task.CreateBy] //通知任务发起人 }; //创建驳回任务以进行通知 await CreateTaskAsync(request); //更新任务描述为驳回原因 await SetTaskBaseDescription(callback.BusinessId, request.TaskType, callback.RejectReason!); remark += ";驳回原因:" + callback.RejectReason; } long userId = long.Parse(User.UserId); var users = await FillInUserInfoAsync(userId); //记录日志 await LogService.WriteLogAsync(new BusinessTaskLog { ActionType = ActionType.Audit, AuditStatus = callback.FlowStatus, BusinessId = callback.BusinessId, BusinessType = callback.BusinessType, CreateBy = userId, CreateTime = DateTime.Now, TaskStatus = callback.FlowStatus == FlowStatusEnum.Approve ? TaskStatusEnum.Complete : TaskStatusEnum.Pending, TaskType = callback.AuditType.GetValueOrDefault(), RecvUsers = users.Count > 0 ? users[0].RecvUserName : null, Remark = remark }); } async Task CheckRulesAsync(long bsId, BusinessType businessType, RuleEngineType ruleType) { var rulesReq = new RuleEngineReq(); var order = await TenantDb.Queryable().Where(x => x.Id == bsId).FirstAsync(); rulesReq.Main.BusinessInfo = order.Adapt(); rulesReq.Main.ProjectCode = [ruleType.ToString()]; var ruleResult = await RuleEngineService.Value.ExecuteSeaExportAuditRulesAsync(rulesReq); if (string.Equals(ruleResult.Succ, bool.FalseString, StringComparison.OrdinalIgnoreCase)) { return DataResult.Failed(ruleResult.Msg); } else if (ruleResult.Extra.DetailList?.Count > 0) { StringBuilder sb = new StringBuilder(); foreach (var item in ruleResult?.Extra.DetailList) sb.Append(item.ResultName); return DataResult.Failed(sb.ToString()); } return DataResult.Success; } /// /// 获取给定任务的下一任务类型 /// /// 当前任务对象 /// protected async Task GetNextTypeAsync(BusinessTask current) { return await GetNextTypeAsync(current.BusinessId, current.BusinessType, current.TaskType); } /// /// 获取给定任务的下一任务类型 /// /// 业务ID /// 业务类型 /// 当前任务类型 /// 是否现舱 /// protected async Task GetNextTypeAsync(long bsId, BusinessType? businessType, TaskBaseTypeEnum currentType, bool? hasCabin = null) { object? order = null; if (businessType.HasValue) order = await ActionService.GetBusinessDataAsync(bsId, businessType.Value); TaskFlowRuner flowRuner = new(TenantDb, ServiceProvider); var dataContext = new TaskFlowDataContext( (TaskFlowDataNameConst.Business, order), (nameof(hasCabin), hasCabin.HasValue ? hasCabin.ToString() : string.Empty) ); return await flowRuner.GetWorkFlowNextConfigByTaskType(TaskBaseTypeEnum.WORK_FLOW_MAIN, dataContext, currentType); } /// /// 获取指定类型的业务关联任务 /// /// 业务ID /// 业务类型 /// 任务类型 /// protected internal ISugarQueryable GetQuery(long id, BusinessType? businessType, params TaskBaseTypeEnum[] taskTypes) { return TenantDb.Queryable().Where(x => x.BusinessId == id) .WhereIF(businessType.HasValue, x => x.BusinessType == businessType) .WhereIF(taskTypes != null && taskTypes.Length > 0, x => taskTypes.Contains(x.TaskType)); } /// /// 从任务配置中获取接收用户列表 /// /// 业务ID /// 业务类型 /// 任务类型 /// protected internal async Task> GetRecvUsersAsync(long id, BusinessType? businessType, TaskBaseTypeEnum taskType) { var dic = await GetRecvUsersAsync(id, businessType, [taskType]); return dic?.Count > 0 ? dic.FirstOrDefault().Value : []; } /// /// 从任务配置中获取接收用户列表 /// /// 业务ID /// 业务类型 /// 任务类型 /// protected internal async Task>> GetRecvUsersAsync(long id, BusinessType? businessType, List taskTypes) { object? biz = null; if (businessType.HasValue) biz = await ActionService.GetBusinessDataAsync(id, businessType.Value); var result = await TaskAllocationService.GetAllotUserBySeaExportId(taskTypes, id, new TaskFlowDataContext( (TaskFlowDataNameConst.Business, biz) )); return result.Succeeded ? result.Data : []; } internal async Task> FillInUserInfoAsync(params long[] ids) { return await Db.Queryable().Where(x => ids.Contains(x.Id)).Select( x => new RecvUserInfo { RecvUserId = x.Id, RecvUserName = x.UserName }).ToListAsync(); } /// /// 删除任务 /// /// 业务ID /// 业务类型 /// 备注 /// 任务类型,不指定任务类型则删除全部任务 /// public async Task DeleteAsync(long id, BusinessType? businessType, string? remark = null, params TaskBaseTypeEnum[] taskTypes) { if (!await HasAuthorizedAsync()) return DataResult.SuccessedWithDesc(nameof(MultiLanguageConst.ModuleUnauthorized)); string[]? typeNames = taskTypes?.Select(x => x.ToString()).ToArray(); int rows = 0; await TenantDb.Ado.BeginTranAsync(); try { rows = await Db.Deleteable().Where(x => x.BusinessId == id) .WhereIF(businessType.HasValue, x => x.BusinessType == businessType) .WhereIF(taskTypes != null && taskTypes.Length > 0, x => taskTypes.Contains(x.AuditType.Value)) .ExecuteCommandAsync(); rows += await TenantDb.Deleteable().Where(x => SqlFunc.Subqueryable().Where( y => x.TaskId == y.Id && y.TASK_SOURCE == "WORK_FLOW" && y.OUT_BS_NO == id) .WhereIF(typeNames != null && typeNames.Length > 0, x => typeNames.Contains(x.TASK_TYPE)) .Any()).ExecuteCommandAsync(); rows += await TenantDb.Deleteable().Where(x => x.TASK_SOURCE == "WORK_FLOW" && x.OUT_BS_NO == id) .WhereIF(typeNames != null && typeNames.Length > 0, x => typeNames.Contains(x.TASK_TYPE)) .ExecuteCommandAsync(); rows += await TenantDb.Deleteable().Where(x => x.BusinessId == id) .WhereIF(businessType.HasValue, x => x.BusinessType == businessType) .WhereIF(typeNames != null && typeNames.Length > 0, x => taskTypes.Contains(x.TaskType)) .ExecuteCommandAsync(); if (string.IsNullOrEmpty(remark)) remark = "删除任务" + (typeNames != null && typeNames.Length > 0 ? $"【{string.Join(",", typeNames)}】,受影响行数:" + rows : string.Empty); await LogService.WriteLogAsync(new BusinessTaskLog { BusinessId = id, BusinessType = businessType, ActionType = ActionType.Delete, CreateBy = long.Parse(User.UserId), CreateTime = DateTime.Now, TaskType = TaskBaseTypeEnum.NOT_SPECIFIED, Remark = remark }); await TenantDb.Ado.CommitTranAsync(); return DataResult.Success; } catch (Exception ex) { await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } /// /// 更新任务台描述 /// /// /// 业务ID /// 任务类型 /// 描述信息 public async Task SetTaskBaseDescription(long id, TaskBaseTypeEnum taskType, string description) { await ManagerService.SetTaskBaseInfoPropertyWithBsno(id, taskType, x => x.TASK_DESP == description); } } /// /// 校验规则类型 /// public enum RuleEngineType { /// /// 订单提交审核 /// COMMON_ORDER_AUDIT, /// /// 订舱 /// COMMON_OCEAN_BOOKING } }