using DS.Module.Core; using DS.Module.Core.Helpers; 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.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.Sys.Entity; using DS.WMS.Core.TaskPlat.Dtos; using DS.WMS.Core.TaskPlat.Entity; using DS.WMS.Core.TaskPlat.Interface; using Masuit.Tools; using Masuit.Tools.Systems; using Microsoft.Extensions.DependencyInjection; using SqlSugar; namespace DS.WMS.Core.Op.Method.TaskInteraction { /// /// 任务交互服务 /// public abstract class TaskService : ServiceBase, ITaskService { const long PERMISSION_ID = 1815294400855674880; static readonly TaskBaseTypeEnum[] AuditTaskTypes = [TaskBaseTypeEnum.WAIT_ORDER_AUDIT, TaskBaseTypeEnum.RETURN_CABIN]; static readonly Dictionary TypeMappings = new() { { TaskBaseTypeEnum.WAIT_ORDER_AUDIT, AuditType.SeaExport }, { TaskBaseTypeEnum.RETURN_CABIN, AuditType.SeaExportReturn } }; /// /// 任务管理服务 /// protected ITaskManageService ManagerService { get; private set; } /// /// 日志服务 /// protected ITaskLogService LogService { get; private set; } /// /// 工作流服务 /// protected Lazy FlowService { get; private set; } /// /// 工作流服务 /// protected Lazy ActionService { get; private set; } /// /// 初始化 /// /// public TaskService(IServiceProvider provider) : base(provider) { ManagerService = provider.GetRequiredService(); LogService = provider.GetRequiredService(); FlowService = new Lazy(provider.GetRequiredService()); ActionService = new Lazy(provider.GetRequiredService()); } /// /// 获取给定任务的下一任务类型 /// /// 任务信息 /// internal static TaskBaseTypeEnum? GetNextType(BusinessTask current) { if (current.TaskType == TaskBaseTypeEnum.NOT_SPECIFIED || current.TaskType == TaskBaseTypeEnum.WAIT_CHECKOUT_BILL) //流程的最后一步 return null; int currentTypeVal = (int)current.TaskType; if (currentTypeVal >= 300) //300开始的枚举值为可选服务项目,不存在前后关联性 return null; return (TaskBaseTypeEnum)(currentTypeVal + 1); } /// /// 获取业务的任务信息 /// /// 业务ID /// 业务类型 /// 任务类型 /// public async Task>> GetTasks(long id, BusinessType businessType, params TaskBaseTypeEnum[] types) { var list = await TenantDb.Queryable().Where(x => x.BusinessId == id && 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 /// 业务类型 /// 任务类型 /// public async Task> Exists(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); } /// /// 确保任务交互模块已授权 /// /// protected virtual async Task EnsureModuleAuthorized() { long tid = long.Parse(User.TenantId); var authStr = await Db.Queryable().Where(x => x.PermissionId == PERMISSION_ID && x.TenantId == tid && SqlFunc.Subqueryable().Where(spt => spt.PermissionId == x.PermissionId).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) { 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 || task.TaskStatus == TaskStatusEnum.Create) { await TenantDb.Ado.BeginTranAsync(); try { var req = new TaskUpdateRequest { AutoCreateNext = false, BusinessId = request.BusinessId, BusinessType = request.BusinessType, TaskStatus = TaskStatusEnum.Create, TaskTypeName = request.TaskTypeName }; //重置任务为待处理 var result = await SetTaskStatusAsync(req, false); if (!result.Succeeded) return DataResult.Failed(result.Message, result.MultiCode); //创建&启动工作流 var result2 = await CreateAndStartWorkflow(task, false); if (!result2.Succeeded) return result2; 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, "重新审批"); await TenantDb.Ado.CommitTranAsync(); return result2; } catch (Exception ex) { await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskStatusNotSupported)); } /// /// 任务审核 /// /// /// public async Task AuditAsync(TaskAuditRequest request) { long id = request.Ids[0]; var task = await GetQuery(id, request.BusinessType.GetValueOrDefault(), request.TaskType).FirstAsync(); if (task == null) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData)); if (task.TaskStatus == TaskStatusEnum.Complete) { return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCompleted)); } else if (task.TaskStatus != TaskStatusEnum.Create && task.TaskStatus != TaskStatusEnum.Pending) { return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskAuditStatusError)); } if (task.FlowId == null) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FlowNotFound)); var result = FlowService.Value.AuditFlowInstance(new FlowAuditInfo { AuditNote = request.Remark, Status = request.Result, Instance = await Db.Queryable().FirstAsync(x => x.Id == task.FlowId.Value) }); var flow = await Db.Queryable().Where(x => x.Id == task.FlowId.Value).Select(x => new FlowInstance { FlowStatus = x.FlowStatus, MakerList = x.MakerList, }).FirstAsync(); result.Data = new { flow.IsCompleted, flow.FlowStatus }; return result; } /// /// 创建关联任务 /// /// /// 是否使用事务 /// public async Task CreateTaskAsync(TaskCreationRequest request, bool useTransaction = true) { if (!await EnsureModuleAuthorized()) return DataResult.SuccessedWithDesc(nameof(MultiLanguageConst.ModuleUnauthorized)); var configList = await TenantDb.Queryable().Where(x => x.TaskType == request.TaskType && x.IsSkip) .Select(x => new { x.SourceName, x.MatchCondition }).ToListAsync(); if (configList.Count > 0) { var biz = ActionService.Value.GetBusinessData(request.BusinessId, request.BusinessType); if (biz != null) { //循环匹配 var logEntity = new BusinessTaskLog { ActionType = ActionType.Create, BusinessId = request.BusinessId, BusinessType = request.BusinessType, TaskType = request.TaskType, CreateBy = long.Parse(User.UserId), CreateTime = DateTime.Now, Remark = "符合设定条件,已跳过任务的创建" }; await LogService.WriteLogAsync(logEntity); return new DataResult(ResultCode.Success, logEntity.Remark); } } var task = await GetQuery(request.BusinessId, request.BusinessType, request.TaskType).FirstAsync(); if (task != null && task.TaskStatus != TaskStatusEnum.Cancel) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskExists)); long tenatId = long.Parse(User.TenantId); string tenatName = Db.Queryable().Where(x => x.Id == tenatId).Select(x => x.Name).First(); var 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) { //根据配置获取默认接收人 info.Main.RecvUserInfoList = await GetRecvUsers(request.BusinessId, request.BusinessType, request.TaskType); if (info.Main.RecvUserInfoList == null || info.Main.RecvUserInfoList.Count == 0) { if (AuditTaskTypes.Contains(request.TaskType)) { info.Main.RecvUserInfoList = await GetRecvUsers(long.Parse(User.UserId)); } else { return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskReceiverNotFound)); } } } else { info.Main.RecvUserInfoList = await GetRecvUsers(request.RecvUserIdList); } if (info.Main.TaskTitle.IsNullOrEmpty()) { var biz = await TenantDb.Queryable().Select(x => new { x.Id, x.CustomerNo, x.MBLNO, x.Vessel, x.Voyno, x.ETD, }).FirstAsync(x => x.Id == request.BusinessId); info.Main.TaskDesp = info.Main.TaskTitle = $"【{request.TaskType.GetDescription()}】{biz?.CustomerNo} {biz?.Vessel} {biz?.Voyno} ETD:{biz?.ETD?.ToString("yyyy-MM-dd")}"; } if (useTransaction) await TenantDb.Ado.BeginTranAsync(); try { DataResult 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)), CreateBy = long.Parse(User.UserId), CreateTime = DateTime.Now }; task.NextType = GetNextType(task); await TenantDb.Insertable(task).ExecuteCommandAsync(); //审核任务需创建工作流 if (AuditTaskTypes.Contains(request.TaskType)) { result = await CreateAndStartWorkflow(task); if (!result.Succeeded) return result; } result = await OnTaskCreated(task); if (!result.Succeeded) return result; //记录日志 await LogService.WriteLogAsync(task); if (useTransaction) await TenantDb.Ado.CommitTranAsync(); return result; } catch (Exception ex) { if (useTransaction) await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } /// /// 创建并启动审批工作流 /// /// /// 同时变更任务执行人 /// protected internal async Task CreateAndStartWorkflow(BusinessTask task, bool changeMarker = true) { var auditType = TypeMappings[task.TaskType]; var template = await FindTemplateAsync(auditType); 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(); result = FlowService.Value.StartFlowInstance(instance.Id.ToString()); instance = result.Data as FlowInstance; if (result.Succeeded && changeMarker) { string[] ids = FlowInstanceService.GetMarkers(instance); //变更任务接收人为所有审批执行人 var users = await GetRecvUsers(ids.Select(long.Parse).ToArray()); result = await ManagerService.TransferTask(task.BusinessId, task.TaskType, users); if (result.Succeeded) { task.RecvUsers = string.Join(",", ids); await TenantDb.Updateable(task).UpdateColumns(x => x.RecvUsers).ExecuteCommandAsync(); } } } return result; } /// /// 当任务创建时调用 /// /// /// protected virtual Task OnTaskCreated(BusinessTask task) { return Task.FromResult(DataResult.Success); } /// /// 设置任务状态 /// /// /// 是否使用事务 /// public async Task> SetTaskStatusAsync(TaskUpdateRequest request, bool useTransaction = true) { if (!await EnsureModuleAuthorized()) return DataResult.SuccessMsg(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.ModuleUnauthorized)), 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.EmptyData)); if (task.TaskStatus == TaskStatusEnum.Complete) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCompleted)); if (task.TaskStatus == TaskStatusEnum.Cancel) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskCancelled)); var result = await ManagerService.SetTaskStatus(request.BusinessId, request.TaskType, request.TaskStatus, DateTime.Now); if (!result.Succeeded) return DataResult.Failed(result.Message, result.MultiCode); //触发任务状态变更通知 if (task.TaskStatus != request.TaskStatus) await OnTaskStatusChanged(request); task.RejectReason = request.RejectReason; //更新当前任务状态 task.TaskStatus = request.TaskStatus; if (AuditTaskTypes.Contains(task.TaskType)) task.FlowId = null; await TenantDb.Updateable(task).UpdateColumns(x => new { x.TaskStatus, x.FlowId, x.RejectReason }).ExecuteCommandAsync(); if (task.TaskStatus == TaskStatusEnum.Complete) { //若存在下一任务,则继续创建 if (task.NextType.HasValue && request.AutoCreateNext) { var req = new TaskCreationRequest { BusinessId = request.BusinessId, BusinessType = request.BusinessType, TaskTypeName = task.NextType.ToString(), RecvUserIdList = task.RecvUserIdArray }; 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(); ActionService.Value.TriggerAction(task); return DataResult.Success(task.TaskStatus == TaskStatusEnum.Complete ? GetNextType(task) : null); } 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; } /// /// 通知审批执行人变更 /// /// 回调信息 /// /// 为null时引发 public virtual async Task MarkerChangedAsync(MarkerChangedCallback callback) { ArgumentNullException.ThrowIfNull(callback, nameof(callback)); long userId = long.Parse(User.UserId); var users = await GetRecvUsers(userId); var dt = DateTime.Now; var taskType = TypeMappings.Where(x => x.Value == callback.Type.GetValueOrDefault()).Select(x => x.Key).FirstOrDefault(); await ManagerService.SetTaskUserStatus( callback.BusinessId, taskType, 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 = taskType, RecvUsers = users.Count > 0 ? users[0].RecvUserName : null }); } /// /// 审批完成回调更新 /// /// 回调信息 /// /// 为null时引发 public virtual async Task UpdateBusinessAsync(FlowCallback callback) { ArgumentNullException.ThrowIfNull(callback, nameof(callback)); var taskType = TypeMappings.Where(x => x.Value == callback.Type.GetValueOrDefault()).Select(x => x.Key).FirstOrDefault(); var req = new TaskUpdateRequest { BusinessId = callback.BusinessId, BusinessType = callback.BusinessType.Value, TaskTypeName = taskType.ToString(), TaskStatus = callback.FlowStatus == FlowStatusEnum.Approve ? TaskStatusEnum.Complete : TaskStatusEnum.Pending, AutoCreateNext = false //审批完成后需根据业务需要自定义任务类型,因此设置为不自动创建下一任务 }; //根据审批结果更新任务状态 await SetTaskStatusAsync(req); long userId = long.Parse(User.UserId); var users = await GetRecvUsers(userId); string remark = "终审完成,审批结果为:" + callback.FlowStatus.GetDescription(); if (callback.FlowStatus == FlowStatusEnum.Reject) { var task = await GetQuery(callback.BusinessId, callback.BusinessType.Value, taskType).FirstAsync(); //创建驳回任务以进行通知 await CreateTaskAsync(new TaskCreationRequest { BusinessId = callback.BusinessId, BusinessType = callback.BusinessType.GetValueOrDefault(), TaskTypeName = (taskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT ? TaskBaseTypeEnum.ORDER_AUDIT_REJECTED : TaskBaseTypeEnum.RETURN_CABIN_REJECTED).ToString(), RecvUserIdList = [task.CreateBy] //通知任务发起人 }); remark += ";驳回理由:" + callback.RejectReason; } //记录日志 await LogService.WriteLogAsync(new BusinessTaskLog { ActionType = ActionType.Audit, AuditStatus = callback.FlowStatus, BusinessId = callback.BusinessId, BusinessType = callback.BusinessType.Value, CreateBy = userId, CreateTime = DateTime.Now, TaskStatus = req.TaskStatus, TaskType = req.TaskType, RecvUsers = users.Count > 0 ? users[0].RecvUserName : null, Remark = remark }); } /// /// 获取指定类型的业务关联任务 /// /// 业务ID /// 业务类型 /// 任务类型 /// protected internal ISugarQueryable GetQuery(long id, BusinessType businessType, TaskBaseTypeEnum taskType) { return TenantDb.Queryable().Where(x => x.BusinessId == id && x.BusinessType == businessType && x.TaskType == taskType); } /// /// 获取任务接收用户列表 /// /// 业务ID /// 业务类型 /// 任务类型 /// protected internal async Task?> GetRecvUsers(long id, BusinessType businessType, TaskBaseTypeEnum taskType) { string typeCode = taskType.ToString(); var allocations = await TenantDb.Queryable().Where(x => x.TaskTypeCode == typeCode) .Select(x => new { x.CarrierId, x.IsAllotCustomerService, //客服 x.IsAllotOperator, //操作 x.IsAllotSale, //销售 x.IsAllotVouchingClerk //单证 }).ToListAsync(); long? carrierId = null; switch (businessType) { case BusinessType.OceanShippingExport: carrierId = await TenantDb.Queryable().Where(x => x.Id == id).Select(x => x.CarrierId).FirstAsync(); break; } var allocation = allocations.Find(x => x.CarrierId == carrierId); //首先使用船公司匹配 if (allocation == null) allocation = allocations.Find(x => x.CarrierId == null); //使用默认值匹配 if (allocation == null) return null; var expr = Expressionable.Create(); if (allocation.IsAllotCustomerService) { expr = expr.Or(x => x.IsCustomerService); } if (allocation.IsAllotOperator) { expr = expr.Or(x => x.IsOperator); } if (allocation.IsAllotSale) { expr = expr.Or(x => x.IsSale); } if (allocation.IsAllotVouchingClerk) { expr = expr.Or(x => x.IsVouchingClerk); } var condition = expr.ToExpression(); if (condition.IsNullOrEmpty()) return null; return await Db.Queryable().Where(condition).Select( x => new RecvUserInfo { RecvUserId = x.Id, RecvUserName = x.UserName }).ToListAsync(); } /// /// 获取任务接收用户列表 /// /// 用户ID /// protected internal async Task> GetRecvUsers(params long[] ids) { return await Db.Queryable().Where(x => ids.Contains(x.Id)).Select( x => new RecvUserInfo { RecvUserId = x.Id, RecvUserName = x.UserName }).ToListAsync(); } /// /// 删除任务 /// /// /// /// /// public async Task Delete(long id, BusinessType businessType, params TaskBaseTypeEnum[] taskTypes) { if (!await EnsureModuleAuthorized()) return DataResult.SuccessedWithDesc(nameof(MultiLanguageConst.ModuleUnauthorized)); string[]? typeNames = taskTypes?.Select(x => x.ToString()).ToArray(); await TenantDb.Ado.BeginTranAsync(); try { 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(); 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(); AuditType[] auditTypes = [AuditType.SeaExport, AuditType.SeaExportReturn]; await Db.Deleteable().Where(x => x.BusinessId == id && x.BusinessType == businessType && auditTypes.Contains(x.Type.Value)) .ExecuteCommandAsync(); await TenantDb.Deleteable().Where(x => x.BusinessId == id && x.BusinessType == businessType) .WhereIF(typeNames != null && typeNames.Length > 0, x => taskTypes.Contains(x.TaskType)) .ExecuteCommandAsync(); 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 = "删除任务" + (typeNames != null && typeNames.Length > 0 ? $"【{string.Join(",", typeNames)}】" : string.Empty) }); 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)); } } } }