636 lines
23 KiB
C#
636 lines
23 KiB
C#
|
|
using Microsoft.AspNetCore.Http;
|
|||
|
|
using OpenAuth.App.BaseApp.Base;
|
|||
|
|
using OpenAuth.App.Interface;
|
|||
|
|
using OpenAuth.Repository;
|
|||
|
|
using OpenAuth.Repository.Domain;
|
|||
|
|
using SqlSugar;
|
|||
|
|
|
|||
|
|
namespace workflow;
|
|||
|
|
|
|||
|
|
public class WorkflowEngineApp : SqlSugarBaseApp<SysCategoryType, SugarDbContext>
|
|||
|
|
{
|
|||
|
|
private readonly ISqlSugarClient _sqlSugar;
|
|||
|
|
|
|||
|
|
|
|||
|
|
public WorkflowEngineApp(ISugarUnitOfWork<SugarDbContext> unitWork, ISimpleClient<SysCategoryType> repository,
|
|||
|
|
IAuth auth) : base(unitWork, repository, auth)
|
|||
|
|
{
|
|||
|
|
_sqlSugar = Repository.AsSugarClient();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region 1. 发起流程(完整实现,事务保障)
|
|||
|
|
|
|||
|
|
public long InitiateFlow(long userId, string userName, InitiateFlowRequest request)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(request.FlowCode) || string.IsNullOrEmpty(request.BusinessNo))
|
|||
|
|
throw new Exception("流程编码和业务编号不能为空");
|
|||
|
|
|
|||
|
|
var instanceId = 0L;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 1. 查询流程模板
|
|||
|
|
var template = _sqlSugar.Queryable<FlowTemplate>()
|
|||
|
|
.Where(t => t.FlowCode == request.FlowCode && t.IsEnabled)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (template == null)
|
|||
|
|
throw new Exception("流程模板不存在或未启用");
|
|||
|
|
|
|||
|
|
// 2. 查询开始节点
|
|||
|
|
var startNode = _sqlSugar.Queryable<FlowNode>()
|
|||
|
|
.Where(n => n.TemplateId == template.TemplateId && n.NodeType == "Start")
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (startNode == null)
|
|||
|
|
throw new Exception("流程开始节点不存在");
|
|||
|
|
|
|||
|
|
// 3. 插入流程实例(返回自增主键)
|
|||
|
|
var instance = new FlowInstance
|
|||
|
|
{
|
|||
|
|
TemplateId = template.TemplateId,
|
|||
|
|
FlowCode = template.FlowCode,
|
|||
|
|
BusinessNo = request.BusinessNo,
|
|||
|
|
Status = "Submitted",
|
|||
|
|
CurrentNodeId = startNode.NodeId,
|
|||
|
|
InitiatorId = userId,
|
|||
|
|
InitiatorName = userName,
|
|||
|
|
CreateTime = DateTime.Now
|
|||
|
|
};
|
|||
|
|
instanceId = _sqlSugar.Insertable(instance).ExecuteReturnIdentity();
|
|||
|
|
|
|||
|
|
// 4. 插入开始节点工作项(直接标记为已完成)
|
|||
|
|
var workitem = new FlowWorkitem
|
|||
|
|
{
|
|||
|
|
InstanceId = instanceId,
|
|||
|
|
NodeId = startNode.NodeId,
|
|||
|
|
NodeName = startNode.NodeName,
|
|||
|
|
HandlerId = userId,
|
|||
|
|
HandlerName = userName,
|
|||
|
|
Status = "Done",
|
|||
|
|
ReceiveTime = DateTime.Now,
|
|||
|
|
HandleTime = DateTime.Now,
|
|||
|
|
Comment = "流程发起成功"
|
|||
|
|
};
|
|||
|
|
_sqlSugar.Insertable(workitem).ExecuteCommand();
|
|||
|
|
|
|||
|
|
// 5. 保存流程变量(含附件)
|
|||
|
|
var attachments = SaveAttachments(request.Attachments);
|
|||
|
|
var variables = new List<FlowVariable>
|
|||
|
|
{
|
|||
|
|
new FlowVariable { InstanceId = instanceId, VarKey = "Title", VarValue = request.Title },
|
|||
|
|
new FlowVariable { InstanceId = instanceId, VarKey = "BusinessType", VarValue = request.BusinessType },
|
|||
|
|
new FlowVariable { InstanceId = instanceId, VarKey = "AttachmentPaths", VarValue = attachments },
|
|||
|
|
new FlowVariable
|
|||
|
|
{
|
|||
|
|
InstanceId = instanceId, VarKey = "AcceptTime",
|
|||
|
|
VarValue = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
_sqlSugar.Insertable(variables).ExecuteCommand();
|
|||
|
|
|
|||
|
|
// 6. 流转到下一个节点(区县→市局)
|
|||
|
|
FlowToNextNode(instanceId, startNode, userId, userName, "流程发起成功");
|
|||
|
|
|
|||
|
|
_sqlSugar.Ado.CommitTran();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_sqlSugar.Ado.RollbackTran();
|
|||
|
|
throw new Exception("发起流程失败:" + ex.Message);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return instanceId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 2. 处理工作项(中转/审核/汇总/退回,完整实现)
|
|||
|
|
|
|||
|
|
public bool HandleWorkitem(long userId, string userName, HandleWorkitemRequest request)
|
|||
|
|
{
|
|||
|
|
if (request.WorkitemId <= 0)
|
|||
|
|
throw new Exception("工作项ID无效");
|
|||
|
|
|
|||
|
|
_sqlSugar.Ado.BeginTran();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 1. 查询工作项(仅待办可处理)
|
|||
|
|
var workitem = _sqlSugar.Queryable<FlowWorkitem>()
|
|||
|
|
.Where(w => w.WorkitemId == request.WorkitemId && w.Status == "ToDo")
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (workitem == null)
|
|||
|
|
throw new Exception("工作项不存在或已处理");
|
|||
|
|
|
|||
|
|
// 2. 查询流程实例和当前节点
|
|||
|
|
var instance = _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == workitem.InstanceId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
var currentNode = _sqlSugar.Queryable<FlowNode>()
|
|||
|
|
.Where(n => n.NodeId == workitem.NodeId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (instance == null || currentNode == null)
|
|||
|
|
throw new Exception("流程实例或节点不存在");
|
|||
|
|
|
|||
|
|
// 3. 更新工作项为已完成
|
|||
|
|
workitem.Status = "Done";
|
|||
|
|
workitem.HandleTime = DateTime.Now;
|
|||
|
|
workitem.Comment = request.Comment ?? "处理完成";
|
|||
|
|
_sqlSugar.Updateable(workitem).ExecuteCommand();
|
|||
|
|
|
|||
|
|
// 4. 并行审核节点特殊处理(保存审核结果)
|
|||
|
|
if (currentNode.NodeType == "Parallel")
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(request.AuditResult) || string.IsNullOrEmpty(request.DeptName))
|
|||
|
|
throw new Exception("并行审核需提供审核结果和科室名称");
|
|||
|
|
|
|||
|
|
var parallelAudit = new FlowParallelAudit
|
|||
|
|
{
|
|||
|
|
InstanceId = instance.InstanceId,
|
|||
|
|
NodeId = currentNode.NodeId,
|
|||
|
|
DeptName = request.DeptName,
|
|||
|
|
AuditResult = request.AuditResult,
|
|||
|
|
AuditComment = request.Comment,
|
|||
|
|
AuditorId = userId,
|
|||
|
|
AuditorName = userName,
|
|||
|
|
AuditTime = DateTime.Now
|
|||
|
|
};
|
|||
|
|
_sqlSugar.Insertable(parallelAudit).ExecuteCommand();
|
|||
|
|
|
|||
|
|
// 5. 判断是否全部审核完成,更新流程状态
|
|||
|
|
if (SummarizeParallelAudit(instance.InstanceId))
|
|||
|
|
{
|
|||
|
|
instance.Status = "Summarized";
|
|||
|
|
_sqlSugar.Updateable(instance).ExecuteCommand();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 6. 流转到下一个节点(非结束节点)
|
|||
|
|
if (currentNode.NodeType != "End")
|
|||
|
|
{
|
|||
|
|
FlowToNextNode(instance.InstanceId, currentNode, userId, userName, workitem.Comment);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 结束节点:标记流程完成
|
|||
|
|
instance.Status = "Completed";
|
|||
|
|
instance.FinishTime = DateTime.Now;
|
|||
|
|
instance.CurrentNodeId = currentNode.NodeId;
|
|||
|
|
_sqlSugar.Updateable(instance).ExecuteCommand();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_sqlSugar.Ado.CommitTran();
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_sqlSugar.Ado.RollbackTran();
|
|||
|
|
throw new Exception("处理工作项失败:" + ex.Message);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 3. 汇总并行审核结果(判断是否全部通过)
|
|||
|
|
|
|||
|
|
public bool SummarizeParallelAudit(long instanceId)
|
|||
|
|
{
|
|||
|
|
if (instanceId <= 0)
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
// 1. 查询该流程所有并行审核结果
|
|||
|
|
var auditResults = _sqlSugar.Queryable<FlowParallelAudit>()
|
|||
|
|
.Where(a => a.InstanceId == instanceId)
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
if (!auditResults.Any())
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
// 2. 查询并行节点对应的角色用户数(判断是否全部审核)
|
|||
|
|
var parallelNode = _sqlSugar.Queryable<FlowNode>()
|
|||
|
|
.Where(n => n.NodeId == auditResults.First().NodeId && n.NodeType == "Parallel")
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
var sql =
|
|||
|
|
$"select u.* from sys_user u left join sys_userrole r on u.\"Id\" = r.\"UserId\" where r.\"RoleId\" = {parallelNode.RoleId}";
|
|||
|
|
var roleUserCount = _sqlSugar.SqlQueryable<SysUser>(sql).Count();
|
|||
|
|
// 3. 全部用户审核完成,且无驳回,返回true
|
|||
|
|
return auditResults.Count == roleUserCount && !auditResults.Any(a => a.AuditResult == "Reject");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 4. 流程查询(我的待办/我的已办,完整实现)
|
|||
|
|
|
|||
|
|
public List<FlowQueryResult> QueryMyToDo(long userId)
|
|||
|
|
{
|
|||
|
|
// 1. 查询用户待办工作项
|
|||
|
|
var toDoWorkitems = _sqlSugar.Queryable<FlowWorkitem>()
|
|||
|
|
.Where(w => w.HandlerId == userId && w.Status == "ToDo")
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
// 2. 组装返回结果
|
|||
|
|
var result = new List<FlowQueryResult>();
|
|||
|
|
foreach (var workitem in toDoWorkitems)
|
|||
|
|
{
|
|||
|
|
var instance = _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == workitem.InstanceId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (instance == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var title = _sqlSugar.Queryable<FlowVariable>() // InstanceId
|
|||
|
|
.Where(v => v.InstanceId == instance.InstanceId && v.VarKey == "Title")
|
|||
|
|
.First()?.VarValue ?? string.Empty;
|
|||
|
|
|
|||
|
|
result.Add(new FlowQueryResult
|
|||
|
|
{
|
|||
|
|
InstanceId = instance.InstanceId,
|
|||
|
|
BusinessNo = instance.BusinessNo,
|
|||
|
|
Title = title,
|
|||
|
|
NodeName = workitem.NodeName,
|
|||
|
|
Status = instance.Status,
|
|||
|
|
CreateTime = instance.CreateTime,
|
|||
|
|
InitiatorName = instance.InitiatorName
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public List<FlowQueryResult> QueryMyDone(long userId)
|
|||
|
|
{
|
|||
|
|
// 1. 查询用户已办工作项(去重流程实例)
|
|||
|
|
var doneInstanceIds = _sqlSugar.Queryable<FlowWorkitem>()
|
|||
|
|
.Where(w => w.HandlerId == userId && w.Status == "Done")
|
|||
|
|
.Select(w => w.InstanceId)
|
|||
|
|
.Distinct()
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
// 2. 组装返回结果
|
|||
|
|
var result = new List<FlowQueryResult>();
|
|||
|
|
foreach (var instanceId in doneInstanceIds)
|
|||
|
|
{
|
|||
|
|
var instance = _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == instanceId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (instance == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var title = _sqlSugar.Queryable<FlowVariable>()
|
|||
|
|
.Where(v => v.InstanceId == instance.InstanceId && v.VarKey == "Title")
|
|||
|
|
.First()?.VarValue ?? string.Empty;
|
|||
|
|
|
|||
|
|
var lastWorkitem = _sqlSugar.Queryable<FlowWorkitem>()
|
|||
|
|
.Where(w => w.InstanceId == instanceId && w.Status == "Done")
|
|||
|
|
.OrderByDescending(w => w.HandleTime)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
result.Add(new FlowQueryResult
|
|||
|
|
{
|
|||
|
|
InstanceId = instance.InstanceId,
|
|||
|
|
BusinessNo = instance.BusinessNo,
|
|||
|
|
Title = title,
|
|||
|
|
NodeName = lastWorkitem?.NodeName ?? string.Empty,
|
|||
|
|
Status = instance.Status,
|
|||
|
|
CreateTime = instance.CreateTime,
|
|||
|
|
InitiatorName = instance.InitiatorName
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 5. 辅助方法(流转节点/保存附件,完整实现)
|
|||
|
|
|
|||
|
|
public FlowInstance GetFlowInstanceDetail(long instanceId)
|
|||
|
|
{
|
|||
|
|
return _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == instanceId)
|
|||
|
|
.First();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 流转到下一个节点(核心流转逻辑)
|
|||
|
|
/// </summary>
|
|||
|
|
private void FlowToNextNode(long instanceId, FlowNode currentNode, long userId, string userName, string comment)
|
|||
|
|
{
|
|||
|
|
var nextNodeIds = currentNode.NextNodeIdList;
|
|||
|
|
if (!nextNodeIds.Any())
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var instance = _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == instanceId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (instance == null)
|
|||
|
|
throw new Exception("流程实例不存在");
|
|||
|
|
|
|||
|
|
foreach (var nextNodeId in nextNodeIds)
|
|||
|
|
{
|
|||
|
|
var nextNode = _sqlSugar.Queryable<FlowNode>()
|
|||
|
|
.Where(n => n.NodeId == nextNodeId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (nextNode == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
// 1. 更新流程实例当前节点和状态
|
|||
|
|
instance.CurrentNodeId = nextNode.NodeId;
|
|||
|
|
instance.Status = GetFlowStatusByNodeType(nextNode.NodeType);
|
|||
|
|
_sqlSugar.Updateable(instance).ExecuteCommand();
|
|||
|
|
|
|||
|
|
// 2. 创建下一步工作项
|
|||
|
|
var nextWorkitem = new FlowWorkitem
|
|||
|
|
{
|
|||
|
|
InstanceId = instanceId,
|
|||
|
|
NodeId = nextNode.NodeId,
|
|||
|
|
NodeName = nextNode.NodeName,
|
|||
|
|
HandlerId = nextNode.RoleId == 0 ? userId : GetRoleFirstUserId(nextNode.RoleId),
|
|||
|
|
HandlerName = nextNode.RoleId == 0 ? userName : GetRoleFirstUserName(nextNode.RoleId),
|
|||
|
|
Status = "ToDo",
|
|||
|
|
ReceiveTime = DateTime.Now,
|
|||
|
|
Comment = comment
|
|||
|
|
};
|
|||
|
|
_sqlSugar.Insertable(nextWorkitem).ExecuteCommand();
|
|||
|
|
|
|||
|
|
// 3. 分支节点特殊处理(自动判断,归档/退回)
|
|||
|
|
if (nextNode.NodeType == "Branch")
|
|||
|
|
{
|
|||
|
|
var isAllPass = SummarizeParallelAudit(instanceId);
|
|||
|
|
var targetNodeIds = nextNode.NextNodeIdList;
|
|||
|
|
if (targetNodeIds.Count < 2)
|
|||
|
|
throw new Exception("分支节点需配置2个下一步节点");
|
|||
|
|
|
|||
|
|
var targetNodeId = isAllPass ? targetNodeIds[0] : targetNodeIds[1];
|
|||
|
|
var targetNode = _sqlSugar.Queryable<FlowNode>()
|
|||
|
|
.Where(n => n.NodeId == targetNodeId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (targetNode == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
// 创建分支节点工作项
|
|||
|
|
var branchWorkitem = new FlowWorkitem
|
|||
|
|
{
|
|||
|
|
InstanceId = instanceId,
|
|||
|
|
NodeId = targetNode.NodeId,
|
|||
|
|
NodeName = targetNode.NodeName,
|
|||
|
|
HandlerId = userId,
|
|||
|
|
HandlerName = userName,
|
|||
|
|
Status = targetNode.NodeType == "End" ? "Done" : "ToDo",
|
|||
|
|
ReceiveTime = DateTime.Now,
|
|||
|
|
HandleTime = targetNode.NodeType == "End" ? DateTime.Now : null,
|
|||
|
|
Comment = isAllPass ? "全部审核通过,流程归档" : "部分审核不通过,退回区县修改"
|
|||
|
|
};
|
|||
|
|
_sqlSugar.Insertable(branchWorkitem).ExecuteCommand();
|
|||
|
|
|
|||
|
|
// 更新流程最终状态
|
|||
|
|
instance.CurrentNodeId = targetNode.NodeId;
|
|||
|
|
instance.Status = targetNode.NodeType == "End" ? "Completed" : "Rejected";
|
|||
|
|
instance.FinishTime = targetNode.NodeType == "End" ? DateTime.Now : null;
|
|||
|
|
_sqlSugar.Updateable(instance).ExecuteCommand();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 根据节点类型获取流程状态
|
|||
|
|
/// </summary>
|
|||
|
|
private string GetFlowStatusByNodeType(string nodeType)
|
|||
|
|
{
|
|||
|
|
return nodeType switch
|
|||
|
|
{
|
|||
|
|
"Common" => "Forwarded",
|
|||
|
|
"Parallel" => "Auditing",
|
|||
|
|
"Branch" => "Summarized",
|
|||
|
|
"End" => "Completed",
|
|||
|
|
_ => "Submitted"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 保存附件(简化实现,自动创建目录)
|
|||
|
|
/// </summary>
|
|||
|
|
private string SaveAttachments(List<IFormFile> attachments)
|
|||
|
|
{
|
|||
|
|
if (attachments == null || !attachments.Any())
|
|||
|
|
return string.Empty;
|
|||
|
|
|
|||
|
|
var attachmentPaths = new List<string>();
|
|||
|
|
var uploadDir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/Attachments");
|
|||
|
|
|
|||
|
|
// 创建附件目录(不存在则创建)
|
|||
|
|
if (!Directory.Exists(uploadDir))
|
|||
|
|
Directory.CreateDirectory(uploadDir);
|
|||
|
|
|
|||
|
|
foreach (var file in attachments)
|
|||
|
|
{
|
|||
|
|
if (file.Length <= 0)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
|
|||
|
|
var filePath = Path.Combine(uploadDir, fileName);
|
|||
|
|
|
|||
|
|
using (var stream = new FileStream(filePath, FileMode.Create))
|
|||
|
|
{
|
|||
|
|
file.CopyTo(stream);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
attachmentPaths.Add($"/Attachments/{fileName}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return string.Join(",", attachmentPaths);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取角色下第一个用户ID
|
|||
|
|
/// </summary>
|
|||
|
|
private long GetRoleFirstUserId(long roleId)
|
|||
|
|
{
|
|||
|
|
var sql =
|
|||
|
|
$"select u.* from sys_user u left join sys_userrole r on u.\"Id\" = r.\"UserId\" where r.\"RoleId\" = {roleId}";
|
|||
|
|
return _sqlSugar.SqlQueryable<SysUser>(sql).First().Id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取角色下第一个用户名称
|
|||
|
|
/// </summary>
|
|||
|
|
private string GetRoleFirstUserName(long roleId)
|
|||
|
|
{
|
|||
|
|
var sql =
|
|||
|
|
$"select u.* from sys_user u left join sys_userrole r on u.\"Id\" = r.\"UserId\" where r.\"RoleId\" = {roleId}";
|
|||
|
|
var user = _sqlSugar.SqlQueryable<SysUser>(sql).First();
|
|||
|
|
return user?.Name ?? string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 新增:个人维度 - 拟办查询与认领
|
|||
|
|
|
|||
|
|
public List<FlowQueryResult> QueryMyDraft(long userId)
|
|||
|
|
{
|
|||
|
|
// 1. 先获取当前用户的所有角色ID
|
|||
|
|
var userRoleIds = _sqlSugar.Queryable<SysUserRole>()
|
|||
|
|
.Where(ur => ur.UserId == userId)
|
|||
|
|
.Select(ur => ur.RoleId)
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
if (!userRoleIds.Any())
|
|||
|
|
return new List<FlowQueryResult>();
|
|||
|
|
|
|||
|
|
// 2. 查询当前用户角色对应的拟办工作项(HandlerId为null,状态为ToDo)
|
|||
|
|
var draftWorkitems = _sqlSugar.Queryable<FlowWorkitem, FlowNode>((w, n) => new JoinQueryInfos(
|
|||
|
|
JoinType.Inner, w.NodeId == n.NodeId))
|
|||
|
|
.Where((w, n) => w.HandlerId == null && w.Status == "ToDo" && userRoleIds.Contains(n.RoleId))
|
|||
|
|
.Select((w, n) => w)
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
// 3. 组装返回结果
|
|||
|
|
var result = new List<FlowQueryResult>();
|
|||
|
|
foreach (var workitem in draftWorkitems)
|
|||
|
|
{
|
|||
|
|
var instance = _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == workitem.InstanceId)
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (instance == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var title = _sqlSugar.Queryable<FlowVariable>()
|
|||
|
|
.Where(v => v.InstanceId == instance.InstanceId && v.VarKey == "Title")
|
|||
|
|
.First()?.VarValue ?? string.Empty;
|
|||
|
|
|
|||
|
|
result.Add(new FlowQueryResult
|
|||
|
|
{
|
|||
|
|
InstanceId = instance.InstanceId,
|
|||
|
|
BusinessNo = instance.BusinessNo,
|
|||
|
|
Title = title,
|
|||
|
|
NodeName = workitem.NodeName,
|
|||
|
|
Status = instance.Status,
|
|||
|
|
CreateTime = instance.CreateTime,
|
|||
|
|
InitiatorName = instance.InitiatorName
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool ClaimDraftWorkitem(long workitemId, long userId, string userName)
|
|||
|
|
{
|
|||
|
|
if (workitemId <= 0 || userId <= 0)
|
|||
|
|
throw new Exception("工作项ID和用户ID无效");
|
|||
|
|
|
|||
|
|
_sqlSugar.Ado.BeginTran();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 1. 查询拟办工作项(必须是未分配、待办状态)
|
|||
|
|
var workitem = _sqlSugar.Queryable<FlowWorkitem>()
|
|||
|
|
.Where(w => w.WorkitemId == workitemId && w.HandlerId == null && w.Status == "ToDo")
|
|||
|
|
.First();
|
|||
|
|
|
|||
|
|
if (workitem == null)
|
|||
|
|
throw new Exception("拟办工作项不存在或已被认领");
|
|||
|
|
|
|||
|
|
// 2. 更新工作项,分配给当前用户(转为待办)
|
|||
|
|
workitem.HandlerId = userId;
|
|||
|
|
workitem.HandlerName = userName;
|
|||
|
|
_sqlSugar.Updateable(workitem).ExecuteCommand();
|
|||
|
|
|
|||
|
|
_sqlSugar.Ado.CommitTran();
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_sqlSugar.Ado.RollbackTran();
|
|||
|
|
throw new Exception("认领拟办工作项失败:" + ex.Message);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 新增:流程维度 - 未办结/已完成查询
|
|||
|
|
|
|||
|
|
public List<FlowInstanceQueryResult> QueryUnfinishedFlows(long userId)
|
|||
|
|
{
|
|||
|
|
// 1. 查询当前用户有权限查看的未办结流程(Status != Completed)
|
|||
|
|
// 简化:此处查询所有未办结,实际可按用户角色/发起人过滤
|
|||
|
|
var unfinishedInstances = _sqlSugar.Queryable<FlowInstance, FlowTemplate>((i, t) => new JoinQueryInfos(
|
|||
|
|
JoinType.Inner, i.TemplateId == t.TemplateId))
|
|||
|
|
.Where((i, t) => i.Status != "Completed")
|
|||
|
|
.Select((i, t) => new FlowInstanceQueryResult
|
|||
|
|
{
|
|||
|
|
InstanceId = i.InstanceId,
|
|||
|
|
BusinessNo = i.BusinessNo,
|
|||
|
|
FlowName = t.FlowName,
|
|||
|
|
Status = i.Status,
|
|||
|
|
InitiatorName = i.InitiatorName,
|
|||
|
|
CreateTime = i.CreateTime,
|
|||
|
|
FinishTime = i.FinishTime
|
|||
|
|
})
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
// 2. 补充标题和当前节点名称
|
|||
|
|
foreach (var instance in unfinishedInstances)
|
|||
|
|
{
|
|||
|
|
// 补充标题
|
|||
|
|
instance.Title = _sqlSugar.Queryable<FlowVariable>()
|
|||
|
|
.Where(v => v.InstanceId == instance.InstanceId && v.VarKey == "Title")
|
|||
|
|
.First()?.VarValue ?? string.Empty;
|
|||
|
|
|
|||
|
|
// 补充当前节点名称
|
|||
|
|
instance.CurrentNodeName = _sqlSugar.Queryable<FlowNode>()
|
|||
|
|
.Where(n => n.NodeId == _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == instance.InstanceId)
|
|||
|
|
.Select(i => i.CurrentNodeId)
|
|||
|
|
.First())
|
|||
|
|
.First()?.NodeName ?? string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return unfinishedInstances;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public List<FlowInstanceQueryResult> QueryCompletedFlows(long userId)
|
|||
|
|
{
|
|||
|
|
// 1. 查询当前用户有权限查看的已完成流程(Status = Completed)
|
|||
|
|
var completedInstances = _sqlSugar.Queryable<FlowInstance, FlowTemplate>((i, t) => new JoinQueryInfos(
|
|||
|
|
JoinType.Inner, i.TemplateId == t.TemplateId))
|
|||
|
|
.Where((i, t) => i.Status == "Completed")
|
|||
|
|
.Select((i, t) => new FlowInstanceQueryResult
|
|||
|
|
{
|
|||
|
|
InstanceId = i.InstanceId,
|
|||
|
|
BusinessNo = i.BusinessNo,
|
|||
|
|
FlowName = t.FlowName,
|
|||
|
|
Status = i.Status,
|
|||
|
|
InitiatorName = i.InitiatorName,
|
|||
|
|
CreateTime = i.CreateTime,
|
|||
|
|
FinishTime = i.FinishTime
|
|||
|
|
})
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
// 2. 补充标题和当前节点名称
|
|||
|
|
foreach (var instance in completedInstances)
|
|||
|
|
{
|
|||
|
|
instance.Title = _sqlSugar.Queryable<FlowVariable>()
|
|||
|
|
.Where(v => v.InstanceId == instance.InstanceId && v.VarKey == "Title")
|
|||
|
|
.First()?.VarValue ?? string.Empty;
|
|||
|
|
|
|||
|
|
instance.CurrentNodeName = _sqlSugar.Queryable<FlowNode>()
|
|||
|
|
.Where(n => n.NodeId == _sqlSugar.Queryable<FlowInstance>()
|
|||
|
|
.Where(i => i.InstanceId == instance.InstanceId)
|
|||
|
|
.Select(i => i.CurrentNodeId)
|
|||
|
|
.First())
|
|||
|
|
.First()?.NodeName ?? string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return completedInstances;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|