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.

519 lines
21 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.Fee.Dtos;
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.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;
/// <summary>
/// 任务管理服务
/// </summary>
protected ITaskManageService ManagerService { 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>();
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.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>
/// <returns></returns>
public async Task<DataResult<List<BusinessTaskDto>>> GetTasks(long id, BusinessType businessType)
{
var list = await TenantDb.Queryable<BusinessTask>().Where(
x => x.BusinessId == id && x.BusinessType == businessType).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()
{
//if (!await Db.Queryable<SysPermissionTenant>().AnyAsync(x => x.PermissionId == PERMISSION_ID))
// return false;
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, TaskBaseTypeEnum.WAIT_ORDER_AUDIT).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)
{
//创建&启动工作流
var result = await Create_StartWorkflow(task);
if (!result.Succeeded)
return result;
if (task.TaskStatus != TaskStatusEnum.Create)
{
await TenantDb.Updateable<BusinessTask>().SetColumns(x => x.TaskStatus == TaskStatusEnum.Create)
.Where(x => x.BusinessId == request.BusinessId &&
x.BusinessType == request.BusinessType && x.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT)
.ExecuteCommandAsync();
}
return result;
}
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskStatusNotSupported));
}
/// <summary>
/// 任务审核
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResult> AuditAsync(AuditRequest request)
{
long id = request.Ids[0];
var task = await GetQuery(id, request.BusinessType.Value, TaskBaseTypeEnum.WAIT_ORDER_AUDIT).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
{
x.FlowStatus,
x.MakerList,
}).FirstAsync();
result.Data = (string.IsNullOrEmpty(flow.MakerList) || flow.MakerList == "-1") && flow.FlowStatus == FlowStatusEnum.Approve;
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
}
};
if (request.RecvUserIdList == null || request.RecvUserIdList.Length == 0)
{
info.Main.RecvUserInfoList = [new RecvUserInfo { RecvUserId = long.Parse(User.UserId), RecvUserName = User.UserName }];
}
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.MBLNO,
x.Vessel,
x.Voyno,
x.ETD,
}).FirstAsync(x => x.Id == request.BusinessId);
info.Main.TaskDesp = info.Main.TaskTitle = $"{request.TaskType.GetDescription()} {biz.Vessel} {biz.Voyno} ETD:{biz.ETD?.ToString("yyyy-MM-dd")} BLNo:{biz.MBLNO}";
}
if (useTransaction)
await TenantDb.Ado.BeginTranAsync();
try
{
var 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 (task.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT)
{
result = await Create_StartWorkflow(task);
if (!result.Succeeded)
return result;
}
result = await OnTaskCreated(task);
if (!result.Succeeded)
return result;
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>
/// <returns></returns>
protected internal async Task<DataResult> Create_StartWorkflow(BusinessTask task)
{
var template = await FindTemplateAsync(AuditType.SeaExport);
if (template == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TemplateNotFound));
var result = FlowService.Value.CreateFlowInstance(new CreateFlowInstanceReq
{
BusinessId = task.BusinessId,
BusinessType = BusinessType.OceanShippingExport,
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)
{
string[] ids = FlowInstanceService.GetNextMarkers(instance);
//变更任务接收人为审批执行人
await UpdateReceiverAsync(new MakerChangedCallback
{
BusinessId = task.BusinessId,
BusinessType = task.BusinessType,
InstanceId = instance.Id,
Type = instance.Type,
NextUserId = ids.Select(long.Parse).ToArray()
});
}
}
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 EnsureModuleAuthorized())
return DataResult.SuccessedWithDesc(nameof(MultiLanguageConst.ModuleUnauthorized));
if (useTransaction)
await TenantDb.Ado.BeginTranAsync();
try
{
var result = await ManagerService.SetTaskStatus(request.BusinessId, request.TaskType, request.TaskStatus, DateTime.Now);
if (!result.Succeeded)
return result;
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));
//触发任务状态变更通知
if (task.TaskStatus != request.TaskStatus)
await OnTaskStatusChanged(request);
//更新当前任务状态
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 }).ExecuteCommandAsync();
if (task.TaskStatus == TaskStatusEnum.Complete)
{
//若存在下一任务,则继续创建
if (task.NextType.HasValue && request.AutoCreateNext)
{
var req = new TaskCreationRequest
{
BusinessId = request.BusinessId,
BusinessType = request.BusinessType,
TaskType = task.NextType.Value,
RecvUserIdList = task.RecvUserIdArray,
};
await CreateTaskAsync(req, false);
}
}
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>
/// <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 UpdateReceiverAsync(MakerChangedCallback callback)
{
ArgumentNullException.ThrowIfNull(callback, nameof(callback));
if (callback.NextUserId == null)
return;
var users = await GetRecvUsers(callback.NextUserId);
var result = await ManagerService.TransferTask(callback.BusinessId, TaskBaseTypeEnum.WAIT_ORDER_AUDIT, users);
if (!result.Succeeded)
{
await new ApplicationException($"任务API({nameof(ManagerService.TransferTask)})调用失败:" + result.Message).LogAsync(Db);
return;
}
string recvUser = string.Join(',', callback.NextUserId);
await TenantDb.Updateable<BusinessTask>().Where(x => x.BusinessId == callback.BusinessId &&
x.BusinessType == callback.BusinessType && x.TaskType == TaskBaseTypeEnum.WAIT_ORDER_AUDIT)
.SetColumns(x => x.RecvUsers == recvUser)
.ExecuteCommandAsync();
}
/// <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));
//根据审批结果更新任务状态
await SetTaskStatusAsync(new TaskUpdateRequest
{
BusinessId = callback.BusinessId,
BusinessType = callback.BusinessType.Value,
TaskType = TaskBaseTypeEnum.WAIT_ORDER_AUDIT,
TaskStatus = callback.FlowStatus == FlowStatusEnum.Approve ? TaskStatusEnum.Complete : TaskStatusEnum.Pending,
AutoCreateNext = callback.FlowStatus == FlowStatusEnum.Approve
});
if (callback.FlowStatus == FlowStatusEnum.Reject)
{
//创建审单驳回任务以进行通知
var task = await GetQuery(callback.BusinessId, callback.BusinessType.Value, TaskBaseTypeEnum.WAIT_ORDER_AUDIT).FirstAsync();
await CreateTaskAsync(new TaskCreationRequest
{
BusinessId = callback.BusinessId,
BusinessType = callback.BusinessType.GetValueOrDefault(),
TaskType = TaskBaseTypeEnum.ORDER_AUDIT_REJECTED,
RecvUserIdList = [task.CreateBy] //通知任务发起人
});
}
}
/// <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="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();
}
}
}