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.

1287 lines
57 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 System.Text;
using DS.Module.Core;
using DS.Module.Core.Data;
using DS.Module.Core.Helpers;
using DS.Module.DjyRulesEngine;
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.Entity;
using DS.WMS.Core.Sys.Entity;
using DS.WMS.Core.TaskInteraction.Dtos;
using DS.WMS.Core.TaskInteraction.Entity;
using DS.WMS.Core.TaskInteraction.Interface;
using DS.WMS.Core.TaskInteraction.Method.ActionSelector;
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;
namespace DS.WMS.Core.TaskInteraction.Method
{
/// <summary>
/// 任务交互服务
/// </summary>
public class TaskService : ServiceBase, ITaskService
{
const long PERMISSION_ID = 1815294400855674880;
/// <summary>
/// 获取支持审核的任务类型
/// </summary>
public static readonly TaskBaseTypeEnum[] AuditTaskTypes;
/// <summary>
/// 通过任务台创建,而非通过工作流创建的任务类型,需手动调用函数来创建驳回任务,且重新提交时不创建工作流
/// </summary>
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 = typeof(TaskBaseTypeEnum).GetDictionary();
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();
}
/// <summary>
/// 确定任务类型是否为业务订单任务
/// </summary>
/// <param name="taskType">任务类型</param>
/// <returns></returns>
public static bool IsOrderType(TaskBaseTypeEnum taskType)
{
int val = (int)taskType;
return val >= 200 && val <= 399;
}
/// <summary>
/// 返回审核任务类型所对应的驳回类型
/// </summary>
/// <param name="auditTaskType">审核任务类型</param>
/// <returns></returns>
public static TaskBaseTypeEnum? GetRejectedType(TaskBaseTypeEnum auditTaskType)
{
if (!AuditTaskTypes.Contains(auditTaskType))
return null;
int negativeVal = -(int)auditTaskType;
return (TaskBaseTypeEnum)negativeVal;
}
/// <summary>
/// 任务管理服务
/// </summary>
protected ITaskManageService ManagerService { get; private set; }
/// <summary>
/// 日志服务
/// </summary>
protected ITaskLogService LogService { get; private set; }
/// <summary>
/// 任务分配
/// </summary>
protected ITaskAllocationService TaskAllocationService { get; private set; }
/// <summary>
/// 工作流服务
/// </summary>
protected Lazy<IClientFlowInstanceService> FlowService { get; private set; }
/// <summary>
/// 动作服务
/// </summary>
protected IActionManagerService ActionService { get; private set; }
readonly Lazy<IClientParamService> ClientParamService;
/// <summary>
/// 规则库服务
/// </summary>
protected Lazy<IRuleEngineService> RuleEngineService { get; private set; }
readonly IConfiguration configuration;
/// <summary>
/// 初始化
/// </summary>
/// <param name="provider"></param>
public TaskService(IServiceProvider provider) : base(provider)
{
ManagerService = provider.GetRequiredService<ITaskManageService>();
LogService = provider.GetRequiredService<ITaskLogService>();
TaskAllocationService = provider.GetRequiredService<ITaskAllocationService>();
ActionService = provider.GetRequiredService<IActionManagerService>();
configuration = provider.GetRequiredService<IConfiguration>();
ClientParamService = new Lazy<IClientParamService>(provider.GetRequiredService<IClientParamService>());
FlowService = new Lazy<IClientFlowInstanceService>(provider.GetRequiredService<IClientFlowInstanceService>());
RuleEngineService = new Lazy<IRuleEngineService>(provider.GetRequiredService<IRuleEngineService>());
TenantDb.QueryFilter.Clear<IOrgId>();
}
/// <summary>
/// 批量创建关联任务
/// </summary>
/// <param name="request"></param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> CreateMultipleTaskAsync(TaskCreationRequest request, bool useTransaction = true)
{
var ids = request.Ids;
ids ??= [request.BusinessId];
List<string> 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)));
}
/// <summary>
/// 创建关联任务
/// </summary>
/// <param name="request"></param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> CreateTaskAsync(TaskCreationRequest request, bool useTransaction = true)
{
if (request.BusinessType.HasValue)
{
bool isBizValid = false;
switch (request.BusinessType.Value)
{
case BusinessType.OceanShippingExport:
isBizValid = await TenantDb.Queryable<SeaExport>().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<SeaExport>().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<SysTenant>().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<SeaExport>().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<FeeRecord>().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));
}
}
/// <summary>
/// 创建并启动审批工作流
/// </summary>
/// <param name="task"></param>
/// <param name="changeMarker">同时变更任务执行人</param>
/// <returns></returns>
protected internal async Task<DataResult> 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;
}
/// <summary>
/// 当任务创建时调用
/// </summary>
/// <param name="task"></param>
/// <returns></returns>
protected virtual Task<DataResult> OnTaskCreated(BusinessTask task)
{
return Task.FromResult(DataResult.Success);
}
/// <summary>
/// 设置任务状态
/// </summary>
/// <param name="request"></param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> 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<FeeRecord>().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<string, object>? dic = null;
if (task.NextType == TaskBaseTypeEnum.WAIT_BOOKING)
{
dic = [];
var param = await ClientParamService.Value.GetParamAsync<string>(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));
}
}
/// <summary>
/// 当任务状态发生变化时调用
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
protected virtual Task OnTaskStatusChanged(TaskUpdateRequest request)
{
return Task.CompletedTask;
}
/// <summary>
/// 获取业务的任务信息
/// </summary>
/// <param name="bsId">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="types">任务类型</param>
/// <returns></returns>
public async Task<DataResult<List<BusinessTaskDto>>> GetTasks(long bsId, BusinessType? businessType, params TaskBaseTypeEnum[] types)
{
var list = await TenantDb.Queryable<BusinessTask>().Where(x => x.BusinessId == bsId)
.WhereIF(businessType.HasValue, x => x.BusinessType == businessType)
.WhereIF(types != null && types.Length > 0, x => types.Contains(x.TaskType))
.Select<BusinessTaskDto>().ToListAsync();
var result = DataResult<List<BusinessTaskDto>>.Success(list);
result.Count = list.Count;
return result;
}
/// <summary>
/// 获取指定业务的所有关联费用类任务
/// </summary>
/// <param name="bsType">业务类型</param>
/// <param name="bsIds">业务ID</param>
/// <returns></returns>
protected internal async Task<List<BusinessTask>> GetFeeTasks(BusinessType bsType, params long[] bsIds)
{
return await TenantDb.Queryable<BusinessTask>()
.InnerJoin<FeeRecord>((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();
}
/// <summary>
/// 返回指定类型的任务是否已存在
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="type">任务类型</param>
/// <returns></returns>
public async Task<DataResult<bool>> ExistsAsync(long id, BusinessType businessType, TaskBaseTypeEnum type)
{
var value = await TenantDb.Queryable<BusinessTask>().AnyAsync(x =>
x.BusinessId == id && x.BusinessType == businessType && x.TaskType == type);
return DataResult<bool>.Success(value);
}
/// <summary>
/// 返回任务交互模块是否已授权
/// </summary>
/// <returns></returns>
public virtual async Task<bool> HasAuthorizedAsync()
{
long tid = long.Parse(User.TenantId);
var authStr = await Db.Queryable<SysTenantPermissionAuth>().ClearFilter(typeof(ITenantId)).Where(x => x.PermissionId == PERMISSION_ID && x.TenantId == tid &&
SqlFunc.Subqueryable<SysPermissionTenant>().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<SysTenant>().Where(x => x.Id == tid).Select(x => x.AppSecret).FirstAsync();
return int.TryParse(EncrypteHelper.DecryptData(authStr, appSecret), out int authNum) && authNum > 0;
}
/// <summary>
/// 重新发起任务审核
/// </summary>
/// <param name="request"></param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> 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<BusinessTask>().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));
}
/// <summary>
/// 撤销审核任务
/// </summary>
/// <param name="request"></param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> 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<BusinessTask>().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<SeaExport>().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));
}
}
/// <summary>
/// 任务审核
/// </summary>
/// <param name="request"></param>
public async Task<DataResult> AuditAsync(TaskAuditRequest request)
{
var tasks = await TenantDb.Queryable<BusinessTask>().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<FlowInstance>().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;
}
/// <summary>
/// 通知审批执行人变更
/// </summary>
/// <param name="callback">回调信息</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"><paramref name="callback"/>为null时引发</exception>
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
});
}
/// <summary>
/// 审批完成回调更新
/// </summary>
/// <param name="callback">回调信息</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"><paramref name="callback"/>为null时引发</exception>
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<DataResult> CheckRulesAsync(long bsId, BusinessType businessType, RuleEngineType ruleType)
{
var rulesReq = new RuleEngineReq();
var order = await TenantDb.Queryable<SeaExport>().Where(x => x.Id == bsId).FirstAsync();
rulesReq.Main.BusinessInfo = order.Adapt<RulesEngineOrderBookingMainBusinessInfo>();
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;
}
/// <summary>
/// 获取给定任务的下一任务类型
/// </summary>
/// <param name="current">当前任务对象</param>
/// <returns></returns>
protected async Task<TaskBaseTypeEnum?> GetNextTypeAsync(BusinessTask current)
{
return await GetNextTypeAsync(current.BusinessId, current.BusinessType, current.TaskType);
}
/// <summary>
/// 获取给定任务的下一任务类型
/// </summary>
/// <param name="bsId">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="currentType">当前任务类型</param>
/// <param name="hasCabin">是否现舱</param>
/// <returns></returns>
protected async Task<TaskBaseTypeEnum?> 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);
}
/// <summary>
/// 获取指定类型的业务关联任务
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="taskTypes">任务类型</param>
/// <returns></returns>
protected internal ISugarQueryable<BusinessTask> GetQuery(long id, BusinessType? businessType, params TaskBaseTypeEnum[] taskTypes)
{
return TenantDb.Queryable<BusinessTask>().Where(x => x.BusinessId == id)
.WhereIF(businessType.HasValue, x => x.BusinessType == businessType)
.WhereIF(taskTypes != null && taskTypes.Length > 0, x => taskTypes.Contains(x.TaskType));
}
/// <summary>
/// 从任务配置中获取接收用户列表
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="taskType">任务类型</param>
/// <returns></returns>
protected internal async Task<List<RecvUserInfo>> GetRecvUsersAsync(long id, BusinessType? businessType, TaskBaseTypeEnum taskType)
{
var dic = await GetRecvUsersAsync(id, businessType, [taskType]);
return dic?.Count > 0 ? dic.FirstOrDefault().Value : [];
}
/// <summary>
/// 从任务配置中获取接收用户列表
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="taskTypes">任务类型</param>
/// <returns></returns>
protected internal async Task<Dictionary<TaskBaseTypeEnum, List<RecvUserInfo>>> GetRecvUsersAsync(long id, BusinessType? businessType, List<TaskBaseTypeEnum> 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<List<RecvUserInfo>> FillInUserInfoAsync(params long[] ids)
{
return await Db.Queryable<SysUser>().Where(x => ids.Contains(x.Id)).Select(
x => new RecvUserInfo { RecvUserId = x.Id, RecvUserName = x.UserName }).ToListAsync();
}
/// <summary>
/// 删除任务
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="remark">备注</param>
/// <param name="taskTypes">任务类型,不指定任务类型则删除全部任务</param>
/// <returns></returns>
public async Task<DataResult> 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<FlowInstance>().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<TaskBaseAllocation>().Where(x => SqlFunc.Subqueryable<TaskBaseInfo>().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<TaskBaseInfo>().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<BusinessTask>().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));
}
}
/// <summary>
/// 更新任务台描述
/// </summary>
/// <returns></returns>
/// <param name="id">业务ID</param>
/// <param name="taskType">任务类型</param>
/// <param name="description">描述信息</param>
public async Task SetTaskBaseDescription(long id, TaskBaseTypeEnum taskType, string description)
{
await ManagerService.SetTaskBaseInfoPropertyWithBsno(id, taskType, x => x.TASK_DESP == description);
}
}
/// <summary>
/// 校验规则类型
/// </summary>
public enum RuleEngineType
{
/// <summary>
/// 订单提交审核
/// </summary>
COMMON_ORDER_AUDIT,
/// <summary>
/// 订舱
/// </summary>
COMMON_OCEAN_BOOKING
}
}