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.

667 lines
28 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using DS.Module.Core;
using DS.Module.Core.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
{
/// <summary>
/// 任务交互服务
/// </summary>
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<TaskBaseTypeEnum, AuditType> TypeMappings = new()
{
{ TaskBaseTypeEnum.WAIT_ORDER_AUDIT, AuditType.SeaExport },
{ TaskBaseTypeEnum.RETURN_CABIN, AuditType.SeaExportReturn }
};
/// <summary>
/// 任务管理服务
/// </summary>
protected ITaskManageService ManagerService { get; private set; }
/// <summary>
/// 日志服务
/// </summary>
protected ITaskLogService LogService { get; private set; }
/// <summary>
/// 工作流服务
/// </summary>
protected Lazy<IClientFlowInstanceService> FlowService { get; private set; }
/// <summary>
/// 初始化
/// </summary>
/// <param name="provider"></param>
public TaskService(IServiceProvider provider) : base(provider)
{
ManagerService = provider.GetRequiredService<ITaskManageService>();
LogService = provider.GetRequiredService<ITaskLogService>();
FlowService = new Lazy<IClientFlowInstanceService>(provider.GetRequiredService<IClientFlowInstanceService>());
}
/// <summary>
/// 获取给定任务的下一任务类型
/// </summary>
/// <param name="current">任务信息</param>
/// <returns></returns>
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);
}
/// <summary>
/// 获取业务的任务信息
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="types">任务类型</param>
/// <returns></returns>
public async Task<DataResult<List<BusinessTaskDto>>> GetTasks(long id, BusinessType businessType, params TaskBaseTypeEnum[] types)
{
var list = await TenantDb.Queryable<BusinessTask>().Where(x => x.BusinessId == id && 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>
/// <returns></returns>
protected virtual async Task<bool> EnsureModuleAuthorized()
{
long tid = long.Parse(User.TenantId);
var authStr = await Db.Queryable<SysTenantPermissionAuth>().Where(x => x.PermissionId == PERMISSION_ID && x.TenantId == tid &&
SqlFunc.Subqueryable<SysPermissionTenant>().Where(spt => spt.PermissionId == x.PermissionId).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>
/// <returns></returns>
public async Task<DataResult> 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<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, "重新审批");
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));
}
/// <summary>
/// 任务审核
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResult> 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<FlowInstance>().FirstAsync(x => x.Id == task.FlowId.Value)
});
var flow = await Db.Queryable<FlowInstance>().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;
}
/// <summary>
/// 创建关联任务
/// </summary>
/// <param name="request"></param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> CreateTaskAsync(TaskCreationRequest request, bool useTransaction = true)
{
if (!await EnsureModuleAuthorized())
return DataResult.SuccessedWithDesc(nameof(MultiLanguageConst.ModuleUnauthorized));
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<SysTenant>().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<SeaExport>().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));
}
}
/// <summary>
/// 创建并启动审批工作流
/// </summary>
/// <param name="task"></param>
/// <param name="changeMarker">同时变更任务执行人</param>
/// <returns></returns>
protected internal async Task<DataResult> 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;
}
/// <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<TaskBaseTypeEnum?>> SetTaskStatusAsync(TaskUpdateRequest request, bool useTransaction = true)
{
if (!await EnsureModuleAuthorized())
return DataResult<TaskBaseTypeEnum?>.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<TaskBaseTypeEnum?>.FailedWithDesc(nameof(MultiLanguageConst.EmptyData));
if (task.TaskStatus == TaskStatusEnum.Complete)
return DataResult<TaskBaseTypeEnum?>.FailedWithDesc(nameof(MultiLanguageConst.TaskCompleted));
if (task.TaskStatus == TaskStatusEnum.Cancel)
return DataResult<TaskBaseTypeEnum?>.FailedWithDesc(nameof(MultiLanguageConst.TaskCancelled));
var result = await ManagerService.SetTaskStatus(request.BusinessId, request.TaskType, request.TaskStatus, DateTime.Now);
if (!result.Succeeded)
return DataResult<TaskBaseTypeEnum?>.Failed(result.Message, result.MultiCode);
//触发任务状态变更通知
if (task.TaskStatus != request.TaskStatus)
await OnTaskStatusChanged(request);
task.RejectReason = request.RejectReason;
//更新当前任务状态
task.TaskStatus = request.TaskStatus;
if (task.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT)
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<TaskBaseTypeEnum?>.Failed("创建下一关联任务时返回错误:" + result.Message, result.MultiCode);
}
}
//记录日志
await LogService.WriteLogAsync(request);
if (useTransaction)
await TenantDb.Ado.CommitTranAsync();
return DataResult<TaskBaseTypeEnum?>.Success(task.TaskStatus == TaskStatusEnum.Complete ? GetNextType(task) : null);
}
catch (Exception ex)
{
if (useTransaction)
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult<TaskBaseTypeEnum?>.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="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 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
});
}
/// <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));
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
});
}
/// <summary>
/// 获取指定类型的业务关联任务
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="taskType">任务类型</param>
/// <returns></returns>
protected internal ISugarQueryable<BusinessTask> GetQuery(long id, BusinessType businessType, TaskBaseTypeEnum taskType)
{
return TenantDb.Queryable<BusinessTask>().Where(x =>
x.BusinessId == id && x.BusinessType == businessType && x.TaskType == taskType);
}
/// <summary>
/// 获取任务接收用户列表
/// </summary>
/// <param name="id">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="taskType">任务类型</param>
/// <returns></returns>
protected internal async Task<List<RecvUserInfo>?> GetRecvUsers(long id, BusinessType businessType, TaskBaseTypeEnum taskType)
{
string typeCode = taskType.ToString();
var allocations = await TenantDb.Queryable<TaskAllocationtSet>().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<SeaExport>().Where(x => x.Id == id).Select(x => x.CarrierId).FirstAsync();
break;
}
var allocation = allocations.Find(x => x.CarrierId == carrierId);
//未找到匹配值
if (allocation == null)
return null;
var expr = Expressionable.Create<SysUser>();
if (allocation.IsAllotCustomerService)
{
expr = expr.Or(x => x.IsCustomerService);
}
else if (allocation.IsAllotOperator)
{
expr = expr.Or(x => x.IsOperator);
}
else if (allocation.IsAllotSale)
{
expr = expr.Or(x => x.IsSale);
}
else if (allocation.IsAllotVouchingClerk)
{
expr = expr.Or(x => x.IsVouchingClerk);
}
var condition = expr.ToExpression();
if (condition.IsNullOrEmpty())
return null;
return await Db.Queryable<SysUser>().Where(condition).Select(
x => new RecvUserInfo { RecvUserId = x.Id, RecvUserName = x.UserName }).ToListAsync();
}
/// <summary>
/// 获取任务接收用户列表
/// </summary>
/// <param name="ids">用户ID</param>
/// <returns></returns>
protected internal async Task<List<RecvUserInfo>> GetRecvUsers(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();
}
}
}