同步操作将从 mengtree/工作流引擎 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
演示环境为dev分支打包
http://39.101.74.14:8083/index.html
演示对接业务系统de地址
http://39.101.74.14:8183/index.html
账号 admin 密码 123qwe
对接演示 git: https://gitee.com/mengtree/workflow-engine-docking
该流程引擎完全开源免费,致力于打造与平台组织架构无关、高扩展的工作流引擎。 通过自定义用户选择器和条件处理器实现既有业务的组织架构关联和审批过程处理。
工作流项目有很多,各有特点,而本项目的特点就是解耦。将审批过程中的条件选择与人员派发抽象出来,用户可以根据自身的组织架构特点进行自定义人员与条件的配置,而无需更改逻辑代码。只需要简单的实现条件和人员选择自定义接口即可。
传统方式,流程审批跟业务系统耦合在一起,特别是审批过程的表单数据判定以及人员(组织)的选择。
本项目的思想就是将审批过程从业务系统分离。基本思路为:不管是耦合在业务系统的审批还是分离的审批,最终都是更新表单的状态。
在下载完源码之后,通过几个简单的操作即可运行起项目:
在 WorkFlowCoreFrameworkModule 的 PreConfigureServices 方法配置指定启动需要的数据库类型。默认可选 LocalMemory 和 Mysql, LocalMemory 是内存sqlite,无需建库,直接运行体验。
public override void PreConfigureServices(ServiceConfigurationContext context)
{
WorkFlowCoreFrameworkOptions.DbType = FrameworkConfigOrmType.LocalMemory;
}
如果选择的是 MySql :
配置数据库字符串链接。如果是Mysql,还需要配置上 版本。建议 8.0 以上。 在
"ConnectionStrings": {
"Default": "${ConnectionStrings_Default}|HLPexHv+EZ1OHtlrnL2oQLRNTMuY5C1pXgEKuqBqgjaQVWczq9OMn5PErX0cHGUKz7ABrPJZHhI2l4OsnpG7pBt3LAkCvWYfEku7MyTgWjwqVgXq7T6KIMzEdFugrolAFVLC8AChraWFael7QKbJaWUlHEo8mSu1i5wcl+iiFenu8mVYGiIm7+0B0VIhtKbEsBcvvbXhBhrqUsuKXmg9iQ==",
"DefaultVersion": "8.0.26",
"SecretPath": "C:\\workflowcore\\secret.config",
"IsEncrypt": "true",
"Secret": "${ConnectionStrings_Secret}|"
}
设置 Host 为启动项。
初始化迁移脚本(如果数据库不是Mysql,默认是Mysql则不需要这一步)。
初始化数据表。通过 vs 的 工具=>NuGet包管理器=>程序包管理器控制台 ,默认项目 选择 WorkFlowCore.Framework,输入 update-database 命令回车
***注:当前系统使用的 orm 为 ef,如需要自定义实现,则重写 repository ***
在介绍使用之前,简单介绍下代码结构。 整个项目包含四个部分:
条件处理器
用户选择器
业务平台自动登录流程引擎平台
业务平台调用流程平台接口发起审批
流程审批状态同步示意图
流程引擎单独运行没有什么意义,一般需要结合具体的组织架构和业务进行应用。所以需要根据场景与组织系统进行对接。本系统设计初衷就是为了将引擎核心与业务分离,只需要简单的接口实现既可以跟既有组织架构关联。
实现自定义仓储。如果需要使用其它的orm,则需要实现自己的数据库仓储。在实现数据库仓储时,应相应的实现该仓储的工作单元。总之参考默认实现,实现一套自己的仓储。
自定义用户选择器。通过实现 IUserSelector 接口实现自己的用户选择器。
[UserSelector("按用户选择","从所有用户选择")]
public class UserSelectorB : IUserSelector
{
public List<Selection> GetSelections()
{
return UserList.Users.Select(u => new Selection { Id = u.Id, Name = u.Name }).ToList();
}
public List<User> GetUsers(SelectorInput input)
{
var result = new List<User>();
switch (input.SelectionId)
{
default:
result.Add(new User { Id = input.SelectionId, Name = UserList.GetUserById(input.SelectionId).Name });
break;
}
return result;
}
}
用户选择器说明:
用户选择器还可以实现 IDefaultUserSelector 以实现”默认选择器“
namespace WorkFlowCore.UserSelectors
{
public interface IDefaultUserSelector: IUserSelector
{
List<string> GetDefaultSelectionIds();
}
}
自定义条件处理器。通过实现 ICondition 接口,可自定义条件处理器。条件处理时,将通过工作流表单信息、当前审批人信息、以及条件参数等信息进行判断是否满足某当前处理器所指定的条件。只需要返回是否满足。
[Condition("条件处理器A")]
public class ConditionA : ICondition
{
public bool CanAccept(ConditionInput input)
{
try
{
//简单的表达式解析
var keyvalue = input.Expression.Split('=');
JObject jObject = JObject.Parse(input.WorkTask.FormData);
var token = jObject.SelectToken(keyvalue[0]);
var value = token.Value<string>();
return value.Equals(keyvalue[1]);
}
catch (Exception)
{
return false;
}
}
}
条件处理器说:
创建流程设计。新建一个工作流设计,该流程设计包括各个节点、人员、条件 等的配置信息,主要为一下结构:
创建工作流实例。设计好流程步骤后。就可以创建相应的审批实例,接着进行审批各项操作。
当前流程引擎有自己完整的一套工作方式,并不适合进行拆解源码融合到其它系统,最适合的方式就是以分布式的方式进行部署。通过分布式部署的情况下,与业务系统的对接则通过接口进行。
该步骤是设计我们的流程节点该怎么走。
在流程设计页面,点击新增,输入流程名称,点击确定,即可创建一条流程设计记录,新增成功后自动进入到流程设计页面。
也可以通过点击列表的编辑按钮,进入到设计页面。 在设计页面,左边是可拖拽的节点,中间的节点布局,右边是属性编辑区。
从左边拖拽节点到中间进行布局设计,开始-普通-结束,这是最简单的流程设计。
节点连线,鼠标放到布局区节点的 左边图标,鼠标会变成 十字 ,点击拖拽,放到目标节点,放开鼠标,就自动连接上了。
点击右上角的 保存 按钮,即可保存流程设计。 这样一个简单的流程就完成了。
子流程的逻辑相对麻烦,驳回可能会有问题,如果有此类需求,优先建议将节点都平铺出来,类似这种:
点击节点,在右侧可以编辑该节点的属性。
注意:配置完的节点信息,一定要点击配置面版右下角的 确定 按钮保存,否则配置信息调整无效。
节点显示名称
审批人员的配置确定这个节点由谁来审批。
一个节点可以增加多个配置项。 可以通过自定义人员选择器来增加人员的选择类型(详见扩展点)
如果勾选了该选项,当匹配到多个人时,将等到所有人都审批完成才会往下走。(有点类似会签,但是会签的等待上游的所有节点审批,但每个节点都有可能匹配到多个人,特别时按职位匹配时)
退回节点用于手动指定审批驳回时,驳回到哪个节点上。默认情况下,无需指定退回节点,驳回时将自动回溯到上一级的节点。
点击 添加节点 增加一个退回节点配置
选择节点。指定驳回时要返回的是哪个节点。由于节点处理没那么智能,这里的节点一定要选择其上游的节点,否则流程可能会有问题。
回退条件。配置驳回时,验证是否退回到指定的节点。只有满足条件时,才能退回。默认不指定时,将无条件回退到指定的节点。退回条件配置信息:
条件类型。该选项指定要用什么处理逻辑来做判断。l条件类型可以通过自定义条件处理器扩展(详见扩展点)。
参数。提供参数给处理逻辑解析,不同的条件类型需要的参数可能不一样,除此之外,在后台处理时,还会结合表单数据,流程信息等信息进行解析,具体处理方式以条件类型为准。
比如,条件类型 选择 布尔处理器,参数 传 true 则,标识,这个条件是满足的,但这种方式是写死的。
如果使用 表单条件处理器,参数传 value(请假天数)>3(请假天数 为json表单字段),则匹配审批表单的请假天数 大于 3,才会满足。
再次强调,使用什么参数格式,得看条件类型的规范,
点击连接节点的线条,可以配置线条的属性。线条主要配置的是条件处理,其确定了这条线是否走的通。
注意:配置完的节点信息,一定要点击配置面版右下角的 确定 按钮保存,否则配置信息调整无效。
点击线条,右侧将切换为线条属性配置。
线条名称 将显示在设计图的线条上,通常,如果该线条配置了条件,可以将条件作为名称。
通过配置条件,来决定该连线是否能联通。
在节点审批时,将判断下游节点的连线,哪条线连的通就派发到连接的节点。相当于某些流程设计里的判断节点。’ 这里的思路是直接赋予线条条件,连线能不能打通,就看条件满不满足。
对于子流程的线条配置,如果一个子流程节点有多个子流程,则应该只能让其满足一条分支,否则流程会有问题
参考 节点配置--退回节点--回退条件。
附条件处理器的解析基本流程:
数据来源:审批时,对条件进行判断时,有两个来源,一个是一开始填写,发起后就不能更改的 表单数据,一个是在每一个人审批都可以有自己步骤的表单数据 这里称为审批表单(通常不需要,看实际情况)。他们在条件解析 时是通过不同的字段传入的: 而条件解析传入的表达式将作为公式解析,用来判断表单的数据是否满足表达式的公式。
数据解析。不同的条件处理器解析不同,这里以 Json条件处理器为例(2023-5-1 新增),该处理器支持通过 "${属性key}" 的方式来解析变量,比如,表单的数据是这样 :"{days:12}",则通过 ${days} 即可取到 12,如果我们的表达式 写成 "${days}>10",那这个条件就会解析为 12>10 , 显然就会返回 true。具体怎么解析还得看条件处理器怎么支持。
既然数据是通过 属性key 来获取数据,那我们的表单里也要有这个key才行。
如果表单数据是直接在输入框编辑,就直接指定属性名称,比如,
那如果是通过动态表单配置的表单,则一定要手动指定控件的属性名称:
通过点击编辑面版的右上角 流程信息 可以查看当前设计的 json 信息,还可以直接更新 json 信息以实现编辑,通常需要复制一个流程时,可以通过该功能。
点击右上角的保存按钮保存流程设计。
这一步不是必须。如果你的系统有自己的表单,这里则用不上。在这里,是为了让演示环境更完整以及方便,才增加了动态表单模块
流程表单和流程设计不是必须关联的,在demo平台上的表单设计只是提供一种应用的参考方式。可以但不限于。可以理解为,这里的表单只是为了将表单数据进行可视化,可操作化
切换到 流程表单 功能菜单,点击新增,创建一个与指定流程设计版本的表单。
新建的表单需要通过列表页,点击编辑进入到编辑页面,在编辑页面编辑表单。 与流程设计类型,表单设计也分三个区域,左侧控件区,中间是布局区,右侧是控件属性区。 通过拖拽左边的控件到中间编辑区进行控件布局。
选中布局区域的控件,通过其下的功能按钮可以进行简单的控件编辑。
单、多选。比如 checkbox,select,radiobox 等,对于这些,特别提供了 选项属性,选项属性是一组数组,指定选择值和名称两个属性填写。
表格table。表格需要配置每一列的数据,每一列又可以是不同的数据类型,所以表格的属性相当于嵌套了一层控件
点击预览可以查看生成的表单效果。
与流程设计类似,流程表单也可以进行版本管理,一个流程设计可以设计多版表单,但最终数据呈现以激活的版本为准。
在 流程审批--我发起的-新建 ,即可创建和发起审批。
响应格式
{
"code": "string", 编号 success|fail
"msg": "string", 对应编号的 成功|失败
"data": //接口业务
}
部分标准实体返回:
"modifiedTime": "2023-01-08T02:43:38.640Z", //更新时间
"modifiedUserId": "string", //更新人id
"createdUserId": "string", //
"creationTime": "2023-01-08T02:43:38.640Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"deletedUserId": "string",
"deletedTime": "2023-01-08T02:43:38.640Z",
接口为 非标准 restful (method 遵循)
在流程设计服务里,主要涉及设计和版本的管理。 设计内容则为流程的设计信息,包括节点,连线,人员,条件以及前端其它设计代码管理。 版本则为流程的版本管理,服务的设计为,一个流程可以有多个版本,随着业务的调整可以不断的调整流程,通过版本管理,可以保证现有流程与新流程区分开来,使旧流程不受新流程影响。
这里的创建很简单,这里实际上是将设计信息的创建统一放到更新操作,一来为了简化新增逻辑,统一在更新处理,二来也是配合前端的交互进行的设计。
该操作返回流程基本信息(这时候设计信息还是为空)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
"workflowNo": "string",//流程编号
"name": "string",//流程名称
"activeVersion": 0, //当前使用版本
"description": "string" //描述
......
}
该接口用于保存流程设计信息,当流程版本有多个时,每个版本单独存储,每个版本有自己的设计id。
name:流程设计名称
des: 描述
drawingInfo: 前端设计绘制信息,考虑到有些前端流程框架的绘制数据可能比较复杂,为便于回显,可以直接将绘制信息记录起来,需要回显可直接读取
versionDescription: 版本描述。
workflowId: 流程唯一标识。 由于一个流程可以有多个版本,所以唯一标识 由 流程id 和 版本确定
{
versionId: 0 //版本号
id: //流程设计id 为 CreateWorkFlow 接口返回的 id
}
workflowNodes: 流程节点。系统的设计思路为节点负责人员选择,连线负责条件判断。一个节点除了有基本的节点信息,还包括 用户选择器(userSelector 用于在流程流转过程中,解析审批人员)。在这里,节点信息允许括驳回节点指定,即可根据不同的条件判定驳回场景下,应流转到哪个节点(补充一点,在当前系统,不管是拒绝还是驳回,都会构建一条记录信息,而不是“删除”上一步操作,所以每一步都是“下一步”)。
流程节点数据格式如下:
[
{
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6 //节点唯一标识,应由前端生成
name: string //节点名称
nodeType: 0 //节点类型
drawingInfo: string //节点绘制信息。预留字段,如果节点的绘制数据较复杂,可以考虑直接保存绘制信息,便于回显
isWaitingAllUser: true //是否等待所有用户(处理)。
userSelectors: //用户选择器,指定通过哪个选择器来解析审批人员
[
{
selectorId: string // 选择器id
selectorName: string //选择器名称
selections: //选项值
[
{
id: string //选项值id
name: string //选项名称
}
]
parameter: string //参数,指定参数将作为选择器使用(参数是否有用,具体看选择器的实现,在后台解析审批人员时,会传递该参数)
description: string //描述
handleType: 0 //处理类型。分为 只读(类似 抄送,只能看但不能处理)和处理。
}
]
rejectNodes: //驳回(拒绝)回退的节点配置
[
{
conditions: //条件
[
{
conditionId: string //条件id
conditionName: string //条件名称
parameter: string //参数
description: string // 描述
}
]
nodeId: 3fa85f64-5717-4562-b3fc-2c963f66afa6 //退回的节点id
nodeName: string // 退回的节点名称
}
]
}
]
其中:
/// <summary>
/// 开始
/// </summary>
Begin,
/// <summary>
/// 结束
/// </summary>
End,
/// <summary>
/// 普通
/// </summary>
Judge,
/// <summary>
/// 会签,所有指向该节点的节点都要审批完成后到达
/// </summary>
Sign
isWaitingAllUser : 一个节点可能会分配到多个人审批,通常同一个节点有其中一个人处理后,即可往下一个节点,但是,如果勾选等待所有用户审批,那这个节点就相当会签节点,所有人都通过才会走下一步。
userSelectors : 用户选择器集合。用户选择器用于在流转到节点时,解析目标审批人员。用户选择器信息主要包括几个信息:哪个选择器,选了哪个选项,额外参数,人员处理方式。
用户选择器通过 实现 IUserSelector 接口自定义用户选择
[UserSelector("按用户选择","从所有用户选择")]
public class UserSelectorB : IUserSelector
{
//获取选项值
public List<Selection> GetSelections()
{
return UserList.Users.Select(u => new Selection { Id = u.Id, Name = u.Name }).ToList();
}
//根据选项和参数获取用户(审批人员)
public List<User> GetUsers(SelectorInput input)
{
var result = new List<User>();
switch (input.SelectionId)
{
default:
result.Add(new User { Id = input.SelectionId, Name = UserList.GetUserById(input.SelectionId).Name });
break;
}
return result;
}
}
/api/WorkFlow/GetAllUserSelectors 前端通过该接口获取所有的用户选择器。默认返回的是 实现了 IUserSelector 接口的类型的基本信息,selectorId 为 类型(在文档这里是 UserSelectorB)全称,selectorName 为 UserSelectorAttribute 的第一个参数:
[UserSelector("按用户选择","从所有用户选择")]
public class UserSelectorB : IUserSelector
[
{
"id": "string", //选择器id(类名全称)
"name": "string", //选择器名称 UserSelectorAttribute 的第一个参数
"description": "string" //选择器描述 UserSelectorAttribute 的第二个参数
}
]
/api/WorkFlow/GetUserSelectionsOfUserSelector 通过该接口根据用户选择器id获取该选择器的选项 。
[
{
"id": "string", //选项id
"name": "string" 选项名称
}
]
rejectNodes :拒绝(驳回、回退、回滚)节点配置。通过该配置,根据条件“回到”指定的节点,在必要情况下可以直接退到指定的节点,默认情况下,不配置时,则按原路返回。该配置主要有两项内容:条件,目标节点
[Condition("条件处理器A")]
public class ConditionA : ICondition
{
public bool CanAccept(ConditionInput input)
{
try
{
//简单的表达式解析
var keyvalue = input.Expression.Split('=');
JObject jObject = JObject.Parse(input.WorkTask.FormData);
var token = jObject.SelectToken(keyvalue[0]);
var value = token.Value<string>();
return value.Equals(keyvalue[1]);
}
catch (Exception)
{
return false;
}
}
}
[Condition("条件处理器A")]
public class ConditionA : ICondition
[
{
"id": "string", //条件处理器id(类名全称)
"name": "string", //条件处理器名称 ConditionAttribute 的第一个参数
"description": "string" //条件处理器描述 ConditionAttribute 的第二个参数
}
]
workflowLines : 节点连线信息。该属性记录节点间的连线关系(从哪个节点到哪个节点)以及通行(条件是否满足)条件。
/api/WorkFlow/UpdateWorkflowActiveVersion 用户切换当前激活(应用)的是哪个版本的流程
workflowId :流程id
activeVersion :激活版本(versionId)
/api/WorkFlow/GetAllWorkflowVersions 通过该接口根据流程id获取所有的流程和版本信息(一个流程有多个版本)
[
{
"workflowId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
"versionNo": 0, //流程版本
"description": "string", //版本描述
"modifiedTime": "2023-01-07T13:40:38.791Z",
"creationTime": "2023-01-07T13:40:38.791Z"
}
]
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
"workflowNo": "string", //流程编号
"name": "string", //流程名称
"activeVersion": 0, //当前应用版本
"description": "string" //描述
}
]
versionId 版本号
id 工作流id
返回详细信息
{
"code": "string",
"msg": "string",
"data": {
"modifiedTime": "2023-01-07T13:48:42.386Z",
"modifiedUserId": "string",
"createdUserId": "string",
"creationTime": "2023-01-07T13:48:42.386Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"deletedUserId": "string",
"deletedTime": "2023-01-07T13:48:42.386Z",
"deleted": true,
"workflowId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"versionNo": 0,
"drawingInfo": "string",
"description": "string",
"nodeMaps": [], //节点映射。连线和节点的映射信息集合。
"allNodes": [] //所有节点
}
}
其中
[
{
"mapType": 0,
"fromNode": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"workflowId": {
"versionId": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"name": "string",
"nodeType": 0,
"drawingInfo": "string",
"isWaitingAllUser": true,
"userSelectors": [
{
"selectorId": "string",
"selectorName": "string",
"selections": [
{
"id": "string",
"name": "string"
}
],
"parameter": "string",
"description": "string",
"handleType": 0
}
],
"rejectNodes": [
{
"conditions": [
{
"conditionId": "string",
"conditionName": "string",
"parameter": "string",
"description": "string"
}
],
"nodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"nodeName": "string"
}
]
},
"toNode": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"workflowId": {
"versionId": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"name": "string",
"nodeType": 0,
"drawingInfo": "string",
"isWaitingAllUser": true,
"userSelectors": [
{
"selectorId": "string",
"selectorName": "string",
"selections": [
{
"id": "string",
"name": "string"
}
],
"parameter": "string",
"description": "string",
"handleType": 0
}
],
"rejectNodes": [
{
"conditions": [
{
"conditionId": "string",
"conditionName": "string",
"parameter": "string",
"description": "string"
}
],
"nodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"nodeName": "string"
}
]
},
"conditions": [
{
"conditionId": "string",
"conditionName": "string",
"parameter": "string",
"description": "string"
}
]
}
]
{
"workflowId":
{
"versionId": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"name": "string",
"formData": "string",
"entityFullName": "string",
"entityKeyValue": "string",
"createdUserId": "string"
}
*workflowId :流程id :原始流程id + 版本号。通过接口 /api/WorkFlow/GetAllWorkflows 获取所有的流程设计。返回流程设计基本信息。workflowId.versionId 对应 GetAllWorkflows 结果集里的 activeVersion。 即这里要创建审批,只需要知道某个流程的以及当前应用的版本(不需要知道所有版本,原则上发起审批的人不需要知道这些)。
name 审批名称(标题)。
formData 表单数据。这个参数是核心。表单数据可以是平台的任意格式,相应的 userSelector 和 condition 在处理过程也会传递表单数据共解析和判断。
entityFullName,entityKeyValue 外部系统表单类型全称和唯一标识。这两个参数主要是为了便于外部系统自己的分类和查询。在系统表单处需要查询流程信息时,借助这两个数据可以精确查询。
createdUserId 创建用户id。为系统平台的用户id,也可以不传,前提是自定义实现 IWorkflowSession 接口解析当前请求的用户信息。
IWorkflowSession 的抽象在 WorkFlowCore.Authorization 。在WorkFlowCore.Framework 实现自定义的 session,并在 WorkFlowCoreFrameworkService 替换现有的注册:
services.Replace(new ServiceDescriptor(typeof(IWorkflowSession),typeof(DefaultSession), ServiceLifetime.Scoped));
返回基本流程信息:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"deleted": true,
"workflowId": {
"versionId": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"name": "string",
"formData": "string",
"entityFullName": "string",
"entityKeyValue": "string",
"workTaskStatus": 0,
"isProcessed": true,
"isPending": true,
"isProcessing": true,
"isSimulation": true
}
其中有几个的数据:
workTaskStatus 审批状态
{
/// <summary>
/// 待处理
/// </summary>
Pending,
/// <summary>
/// 处理中
/// </summary>
Processing,
/// <summary>
/// 已完成
/// </summary>
Processed
}
isProcessed/isPending/isProcessing 为 workTaskStatus 的常用判断值
isSimulation 是否是模拟流程。 该属性不在前端传递,而是通过 创建模拟流程接口 /api/WorkFlow/CreateSimulationWorkTask 创建的任务。其参数与 CreateWorkTask 一致。
{
"worktaskId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
worktaskId 流程审批id。
返回发起审批后的处理待审批记录(每一条记录对应 每一个审批用户):
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"workTaskId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"workStepType": 0,
"fromNodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fromNodeName": "string",
"nodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"nodeName": "string",
"handleUser": {
"id": "string",
"name": "string"
},
"isHandled": true,
"handleType": 0,
"comment": "string",
"resourceIds": "string",
"isRead": true,
"readTime": "2023-01-08T02:43:38.627Z",
"handlerTime": "2023-01-08T02:43:38.627Z",
"groupId": "string",
"preStepGroupId": "string",
"formData": "string",
"fromForwardStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
]
其中:
workStepType 处理类型。与 流程设计(/api/WorkFlow/UpdateWorkflow )中,节点 Node 配置中的 handleType对应: 0 //处理类型。分为 只读(类似 抄送,只能看但不能处理)和处理。
/// <summary>
/// 处理(正常需要处理的情况)
/// </summary>
Handle,
/// <summary>
/// 只读(抄送情况下)
/// </summary>
ReadOnly,
fromNodeId,fromNodeName 来源节点。即是从哪个节点流转到当前的(新发起的节点来源节点为 开始节点)。
nodeId,nodeName 当前审批节点。
handleUser 当前审批人。
*isHandled 是否已经处理(workStepType 为”处理“的情况)
handleType 处理方式:
{
/// <summary>
/// 通过
/// </summary>
Pass,
/// <summary>
/// 拒绝
/// </summary>
Reject,
/// <summary>
/// 撤回
/// </summary>
Withdraw,
/// <summary>
/// 转发(转给某人审批)
/// </summary>
Forward,
/// <summary>
/// 未处理(在会签节点其它节点驳回,但是部分未处理的则更新为该状态)
/// </summary>
UnWork
}
*handlerTime 处理时间
comment 批文。
resourceIds 附件id。这里设计为只记录附件id。每一次审批都可以根据情况上传附件(附件由平台自己管理附件信息)。通常多个附件英文逗号隔开,但具体什么存和解析,由接入平台定义。
isRead,readTime 是否已读,以及已读时间。(目前这两个状态仅在执行”撤回(/api/WorkFlow/WithdrawProve)“操作时,才标记已读,设计考虑在用户查看审批明细时,也应该标记已读)
groupId 分组id。分组id 在业务上没意义,在流程控制上便于”撤回“和”驳回“。设计上,每一次的节点流转都是一个分组,一个节点可能会根据条件流转到一个或者多个节点上,而这些在同一个节点审批流转的审批就归属为同一组。
preStepGroupId 前一组id
formData 表单数据。这里审批的表单数据不是发起是的表单数据。下面审批接口(/api/WorkFlow/PassProve)会提到。每次审批也都可以有自己的表单数据,当存在这些数据并且 用户选择器 和 条件处理器 都会应用这些数据进行解析时,也会影响到流程的流转。考虑到的这个场景是应用到每次审批都有可能更新表单的情况(具体在审批过程能否编辑数据,这个得看实现情况)。
comment 批文。
*fromForwardStepId 来源步骤id。如果是转发才会有值。即标记是哪个审批记录转发过来的。
{
"comment": "string",
"stepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"resourceIds": "string"
}
comment 批文
stepId 待审批记录id
resourceIds 附件id(前面提到过)。
返回发起审批后的处理待审批记录(与 发起审批 /api/WorkFlow/StartWorkTask 接口返回一致):
转发审批为转发当前审批操作转给另一个人进行审批。与其他审批处理操作相似,但多出一个传参。
{
"comment": "string", //批文
"stepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //待审批记录id
"resourceIds": "string", //附件id
"userSelectors":
[
{
"selectorId": "string",
"selectorName": "string",
"selections": [
{
"id": "string",
"name": "string"
}
],
"parameter": "string",
"description": "string",
"handleType": 0
}
]
}
参考 分页获取用户待处理审批 /api/WorkFlow/GetUnHandledWorkTasksOfUser
目前已实现的接口只能保证基本的流程运作,实际上应用过程可能还有更多的场景没考虑到。由于能力有限,不可能做到面面俱到,特别是对于查询接口,这就需要根据业务自己实现。
系统实现了一套插件管理逻辑,目的是为了某些插件(扩展点)可以配置和复用。 管理员登录可以对插件进行管理和配置
插件相关流程主要有:注册、配置、实例化。
通过 PluginManager.RegisterPluginType(Type 插件类型 ,string 插件类型名称) 注册插件类型,这一步实际上就是注册抽象类(接口、基类),定义是属于某个类型的插件,比如用户选择器就是一个插件类型。 在插件管理,表现为插件大类:
在讲注册插件实例之前,先说清楚插件的实现。(ps:插件系统不支持依赖注入)
插件的实现,就是接口的实现或者是继承基类的子类。比如实现了用户选择器的接口 IUserSelector 的实现类:DefaultUserSelector、UserSelectorDefaultHttp。
ConfigureContext configureContext;
public Task Configure(ConfigureContext configureContext)
{
this.configureContext = configureContext;
return Task.CompletedTask;
}
通过 PluginManager.Register(new ManifestWithConfig(string className, string name, string description,string entry,string version,Assembly assembly)); 这一步注册插件类型抽象的实现(或者子类),比如用户选择器就有按角色,按用户等方式获取,对应的就是插件类型的实现。其中:
除了手动在系统内注册,也支持注册外部插件(兼容之前的插件,参考下边用户选择器,条件处理器的插件注册),系统会自动根据所注册的插件类型(抽象),遍历插件目录下实现了对应抽象的插件类型,并载入。
Configure<PluginOptions>(options =>
{
options.BasePath = Path.Combine(AppContext.BaseDirectory, "Plugins");
});
插件包
--src
----XXXX_Entry.dll //导出的入口dll(可能会多个)
--manifest.json //描述文件
{
"Entry": "UserSelector_PluginDemo.dll", //入口插件格式,插件载入时,将以这个为入口载入(必填)
"Version": "1.0",
"ClassName":"插件全类型名称",
"Name":"中文名称",
"Description":"类型描述"
}
对于实现了 IConfigurable 接口的插件,在插件配置管理则允许实现配置。具体的配置格式要求由插件自定义。平台默认对 key=value 格式自动解析。
每个插件配置有唯一的id(初始化时,自动创建一个配置信息),对于可配置插件,同一个插件实现可以针对应用场景进行多个配置。比如:
插件的实例化只能由插件自行管理,暂时没法使用框架的依赖注入。 实例化支持指定配置id实例化,或者实现某个插件抽象类型的所有实例
pluginManager.Resolve<ICondition>(conditionId);
pluginManager.ResolveAll<IUserSelector>();
通过自定义实现 IUserSelector 接口实现所需的用户选择器。
新建自定义类库
引入项目依赖 WorkFlowCore,或者手动引入dll (WorkFlowCore.Common.dll,WorkFlowCore.dll)
实现自定义逻辑。并创建对应的 manifest.json
{
"Entry": "Condition_PluginDemo.dll", //入口文件dll
"Version": "1.0" //版本号(目前没应用到,预留)
}
编译生成。并将生成的文件按如下结构按如下格式复制到 Host发布目录下 Plugins\UserSelectors
重启(启动) Host 服务
目前插件的自定义实现类暂时还无法通过ioc 注入参数。除非插件自己维护 ServiceProvider。
通过自定义实现 ICondition 接口实现所需的用户选择器。
自定义实现 ICustomizationVerifyExtension 接口自定义认证扩展。接口返回基本的认证信息。该插件用于自定义验证身份信息,通过扩展可以实现第三方平台的认证。
public VerifyOutput Verify(VerifyInput input)
public class VerifyOutput
{
public bool IsValid { get; set; }
public AuthorizationUser User { get; set; }
public Claim[] Claims { get; set; }
}
身份认证插件的认证逻辑示意图
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。