克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
LGPL-3.0

演示地址

演示环境为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

QQ交流群 :558038638

简介

该流程引擎完全开源免费,致力于打造与平台组织架构无关、高扩展的工作流引擎。 通过自定义用户选择器和条件处理器实现既有业务的组织架构关联和审批过程处理。

项目特点

工作流项目有很多,各有特点,而本项目的特点就是解耦。将审批过程中的条件选择与人员派发抽象出来,用户可以根据自身的组织架构特点进行自定义人员与条件的配置,而无需更改逻辑代码。只需要简单的实现条件和人员选择自定义接口即可。

传统做法

传统方式,流程审批跟业务系统耦合在一起,特别是审批过程的表单数据判定以及人员(组织)的选择。 Alt text

本项目的做法

本项目的思想就是将审批过程从业务系统分离。基本思路为:不管是耦合在业务系统的审批还是分离的审批,最终都是更新表单的状态。 Alt text

快速运行

在下载完源码之后,通过几个简单的操作即可运行起项目:

  • 在 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}|"
          }
      
      • 通过 ${变量名称}|默认值 的格式配置参数,可以通过配置环境变量,控制台变量启动时指定配置,不指定时,取默认值。这里的连接字符串默认进行了加密处理。*** Mode:CBC ,Padding:PKCS7 ***
      • IsEncrypt 设为空值 时,可配置明文连接字符串
      • SecretPath 只是用于本地密钥地址。便于本地解密。实际运行时可以通过 指定 ConnectionStrings_Secret 环境变量指定解密密钥。
    • 设置 Host 为启动项。

    • 初始化迁移脚本(如果数据库不是Mysql,默认是Mysql则不需要这一步)。

      • 删除 WorkFlowCore.Framework 下的 Migrations 文件夹。
      • 通过 vs 的 工具=>NuGet包管理器=>程序包管理器控制台 ,默认项目 选择 WorkFlowCore.Framework,输入 add-migration init 命令回车
    • 初始化数据表。通过 vs 的 工具=>NuGet包管理器=>程序包管理器控制台 ,默认项目 选择 WorkFlowCore.Framework,输入 update-database 命令回车

    • ***注:当前系统使用的 orm 为 ef,如需要自定义实现,则重写 repository ***

使用介绍

业务模型

  • 工作流和业务组织领域的关系
  • Alt text
  • 流程引擎依赖业务系统的业务数据、组织 结构数据。
  • Alt text

源码结构

在介绍使用之前,简单介绍下代码结构。 整个项目包含四个部分:

  • 1.WorkFlowCore 是领域核心,所有的业务逻辑在这里面封装交互。在这个部分里,有几个模块:
    • Authorization 身份认证,里面是获取当前用户信息的抽象接口。因为项目是独立与用户的其它系统,当进行接口(或其它形式)访问时,需要获取当前登录信息时,就需要通过身份接口获取。用户根据实际情况做具体的实现。
    • EventData 事件消息模型(发布者发布的数据对象)
    • IRepositories 抽象仓储。在系统设计时,考虑到不同的使用场景下,具体的数据库是不确定的,所以引入仓储,便于扩展。(默认提供了EF 的扩展)
    • UserSelectors 用户选择器。特色模块之一。为用户选择的抽象管理。实际使用时,是需要实现相应的用户选择接口接口扩展用户选择器。后面会详细介绍。
    • Conditions 条件处理器。特色模块之一。通过自定义条件处理器,可以为用户提供丰富的处理选项和逻辑。后面会详细介绍。
    • Workflows 流程设计模块,后面会介绍。
    • WorkTasks 流程实例模块,后面会介绍。
    • Plugins 插件管理模块。
  • WorkFlowCore.Framework 领域核心抽象的实现。在这里实现具体用仓储用哪个数据库,有什么用户选择器和条件处理器等待
  • WorkFlowCore.Host 接口
  • WorkFlowCore.Test 单元测试。

条件处理器和用户选择器插件示意

  • 条件处理器 Alt text

  • 用户选择器 Alt text

流程引擎与业务系统对接示意图

  • 业务平台自动登录流程引擎平台

    Alt text

  • 业务平台调用流程平台接口发起审批

    Alt text

  • 流程审批状态同步示意图

    Alt text

使用流程简介

流程引擎单独运行没有什么意义,一般需要结合具体的组织架构和业务进行应用。所以需要根据场景与组织系统进行对接。本系统设计初衷就是为了将引擎核心与业务分离,只需要简单的接口实现既可以跟既有组织架构关联。

  • 实现自定义仓储。如果需要使用其它的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;
        }
    }
    

    用户选择器说明:

    1. UserSelector 特性。该特性对选择器做指定一个名称和描述,用于在前端显示。如图所示:
    2. 类名全程将作为选择器唯一标识。
    3. GetSelections 方法 指定这个类型的选择器有哪些选项。选择可以是多种多样的。比如作为角色选择器时,选项返回角色列表。
    4. GetUsers 通过将通过选项标识获取实际的用户列表返回。比如角色选择器传入一个角色,将返回这个角色的所有成员。

    用户选择器还可以实现 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;
              }
          }
      }
    

    条件处理器说:

    1. Condition特性标记。 类似 用户选择器,Condition标记将提供条件名称和描述供前端查看。
    2. CanAccept 只有一个布尔返回值,将通过表达式、当前工作流信息、当前审批步骤 等信息去解析表达式,并得出结果。信息来源广,可以实现解析工作流表单信息,也可以实现通过sql或者其它各种形式的数据判断。同时解析手段也可以根据实际情况扩展,比如解析json、xml或者其它非标准结构的数据,都可以通过自定义实现处理器来 解析判断。
  • 创建流程设计。新建一个工作流设计,该流程设计包括各个节点、人员、条件 等的配置信息,主要为一下结构:

    • 节点。审批处理节点,或者说审批步骤。
      • 人员选择器。该节点由哪些人来审批或抄送给谁。
    • 线条。线条用于连接节点。有了线条串起各个节点,才能形成一个通路。
      • 条件处理器。线条配置条件处理器就可以在处理时判断能去到哪些节点。
  • 创建工作流实例。设计好流程步骤后。就可以创建相应的审批实例,接着进行审批各项操作。

部署方式

当前流程引擎有自己完整的一套工作方式,并不适合进行拆解源码融合到其它系统,最适合的方式就是以分布式的方式进行部署。通过分布式部署的情况下,与业务系统的对接则通过接口进行。

  1. 一般情况下,业务系统表单自己维护一个审批状态,业务系统通过调用流程系统接口发起一个审批。也可以自行管理一套表单和状态,只通过本引擎进行流转。
  2. 流程系统成功新建一个流程后,将发起一个审批开始的事件,通过订阅该事件进行回调更新业务表单的审批状态。
  3. 其它的诸如新建流程设计、流程审批记录等,可通过接口查询,或者自行实现相应的查询接口。

快速上手

创建流程设计

该步骤是设计我们的流程节点该怎么走。

简单流程

  • 在流程设计页面,点击新增,输入流程名称,点击确定,即可创建一条流程设计记录,新增成功后自动进入到流程设计页面。 新增

  • 也可以通过点击列表的编辑按钮,进入到设计页面。 编辑 在设计页面,左边是可拖拽的节点,中间的节点布局,右边是属性编辑区。 新增

  • 从左边拖拽节点到中间进行布局设计,开始-普通-结束,这是最简单的流程设计。 节点拖拽

  • 节点连线,鼠标放到布局区节点的 左边图标,鼠标会变成 十字 ,点击拖拽,放到目标节点,放开鼠标,就自动连接上了。 节点拖拽

  • 点击右上角的 保存 按钮,即可保存流程设计。 节点拖拽 这样一个简单的流程就完成了。

有分支的流程

  • 一个节点可以连接到几个下级节点,这样就构成了多分支流程。 多分支连接

有会签的流程

  • 多个节点连线到会签节点,实现会签(等待所有人都处理完才到下一步)。 会签连接

有子流程的流程

  • 使用子流程节点,可以实现等待子流程节点审批完成后,主流程才会到下一个节点(慎用子流程)。子流程节点 和 子节点节点 是配合使用,一个子流程必须用子节点节点做起始节点,子节点用于标记这里子流程的开始节点。一个子流程可以有多个子节点节点,可以通过条件配置指定走哪个子流程(后面会讲条件配置) 子流程 多个子流程分支的情况(当然,这个不建议,如果需要有多个子流程分支,则应该在外层就指定多个子流程节点,在外层先进行判断): 子流程

子流程的逻辑相对麻烦,驳回可能会有问题,如果有此类需求,优先建议将节点都平铺出来,类似这种: 子流程

节点属性

点击节点,在右侧可以编辑该节点的属性。

注意:配置完的节点信息,一定要点击配置面版右下角的 确定 按钮保存,否则配置信息调整无效。

名称

节点显示名称

审批人员

审批人员的配置确定这个节点由谁来审批。

一个节点可以增加多个配置项。 可以通过自定义人员选择器来增加人员的选择类型(详见扩展点)

  • 点击 添加人员,会新增一个人员配置项 添加人员,其中,
    • 人员类型。人员类型指定了这个人按什么样的方式或者分类获取。比如,按角色,按职位等
    • 人员项。分类明细,指定具体的类型,比如,类型按角色,则这个人员项则选择具体的某个角色。 人员选择
    • 参数。这个为额外的配置项,具体作用和用法由人员选择器决定。
    • 审批、抄送。审批,则该配置项匹配的人员将可以审批,抄送,则该选项匹配的人员只能查看,不能审批。
    • 描述。自定义备注。

等待所有人审批

如果勾选了该选项,当匹配到多个人时,将等到所有人都审批完成才会往下走。(有点类似会签,但是会签的等待上游的所有节点审批,但每个节点都有可能匹配到多个人,特别时按职位匹配时)

退回节点

退回节点用于手动指定审批驳回时,驳回到哪个节点上。默认情况下,无需指定退回节点,驳回时将自动回溯到上一级的节点。

  • 点击 添加节点 增加一个退回节点配置 人员选择

  • 选择节点。指定驳回时要返回的是哪个节点。由于节点处理没那么智能,这里的节点一定要选择其上游的节点,否则流程可能会有问题。

  • 回退条件。配置驳回时,验证是否退回到指定的节点。只有满足条件时,才能退回。默认不指定时,将无条件回退到指定的节点。退回条件配置信息:

    • 条件类型。该选项指定要用什么处理逻辑来做判断。l条件类型可以通过自定义条件处理器扩展(详见扩展点)。

    • 参数。提供参数给处理逻辑解析,不同的条件类型需要的参数可能不一样,除此之外,在后台处理时,还会结合表单数据,流程信息等信息进行解析,具体处理方式以条件类型为准。

      比如,条件类型 选择 布尔处理器,参数 传 true 则,标识,这个条件是满足的,但这种方式是写死的。

      如果使用 表单条件处理器,参数传 value(请假天数)>3(请假天数 为json表单字段),则匹配审批表单的请假天数 大于 3,才会满足。

      再次强调,使用什么参数格式,得看条件类型的规范,

线条属性

点击连接节点的线条,可以配置线条的属性。线条主要配置的是条件处理,其确定了这条线是否走的通。

注意:配置完的节点信息,一定要点击配置面版右下角的 确定 按钮保存,否则配置信息调整无效。

点击线条,右侧将切换为线条属性配置。 线条配置

线条名称

线条名称 将显示在设计图的线条上,通常,如果该线条配置了条件,可以将条件作为名称。 条件名称

添加条件

通过配置条件,来决定该连线是否能联通。

在节点审批时,将判断下游节点的连线,哪条线连的通就派发到连接的节点。相当于某些流程设计里的判断节点。’ 这里的思路是直接赋予线条条件,连线能不能打通,就看条件满不满足。

对于子流程的线条配置,如果一个子流程节点有多个子流程,则应该只能让其满足一条分支,否则流程会有问题

参考 节点配置--退回节点--回退条件。

附条件处理器的解析基本流程:

  • 数据来源:审批时,对条件进行判断时,有两个来源,一个是一开始填写,发起后就不能更改的 表单数据,一个是在每一个人审批都可以有自己步骤的表单数据 这里称为审批表单(通常不需要,看实际情况)。他们在条件解析 时是通过不同的字段传入的: 条件名称 而条件解析传入的表达式将作为公式解析,用来判断表单的数据是否满足表达式的公式。 条件名称

  • 数据解析。不同的条件处理器解析不同,这里以 Json条件处理器为例(2023-5-1 新增),该处理器支持通过 "${属性key}" 的方式来解析变量,比如,表单的数据是这样 :"{days:12}",则通过 ${days} 即可取到 12,如果我们的表达式 写成 "${days}>10",那这个条件就会解析为 12>10 , 显然就会返回 true。具体怎么解析还得看条件处理器怎么支持。

    既然数据是通过 属性key 来获取数据,那我们的表单里也要有这个key才行。

    如果表单数据是直接在输入框编辑,就直接指定属性名称,比如, 条件名称

    那如果是通过动态表单配置的表单,则一定要手动指定控件的属性名称: 条件名称

流程信息

通过点击编辑面版的右上角 流程信息 可以查看当前设计的 json 信息,还可以直接更新 json 信息以实现编辑,通常需要复制一个流程时,可以通过该功能。 条件名称

保存

点击右上角的保存按钮保存流程设计。 保存

  • 描述。这个是这个流程的描述
  • 版本。一个流程设计可以有多个版本,通过多版本控制,可以实现在业务演变过程中,调整流程,并且保证已经审批中的流程正常。
  • 版本描述。针对这个版本的描述。

列表功能

列表功能

  • 查看。快速查看设计图
  • 编辑。进入设计页面编辑设计图。
  • 版本切换。一个流程设计随着业务变化会有多个版本,该版本切换确定新建的流程使用哪个版本。
  • 模拟。该功能提供流程设计的模拟验证,以确保调整的流程是否正常。
    • 点击模拟会进入到流程模拟页面 模拟 在模拟页面,可以手动编辑json表单数据,然后指定人员进行审批。
    • 是否模拟。该勾选项如果不勾选,将当作一条正常的流程发起审批。仅为了测试,默认勾选。
    • 审批。模拟流程发起后,可以切换当前用户吗,如果下一个步骤是他审批的,将会看到审批表单。 列表功能 审批可以进行直接审批或者转发 列表功能 列表功能
      • 表单数据。该参数适用于每次审批可能会更改表单数据的场景。该场景可能发生在作业流控制,即在某些节点可以编辑数据。
      • 批语。
      • 其它(在模拟页面没有附件等上传,但实际应用过程,可以在不同的审批不同提交附件文件等)
      • 转发即转给另一个人处理,转发的人员也是通过人员选择器选择,详细参考节点配置的人员选择。

配置流程表单

这一步不是必须。如果你的系统有自己的表单,这里则用不上。在这里,是为了让演示环境更完整以及方便,才增加了动态表单模块

流程表单和流程设计不是必须关联的,在demo平台上的表单设计只是提供一种应用的参考方式。可以但不限于。可以理解为,这里的表单只是为了将表单数据进行可视化,可操作化

表单创建

切换到 流程表单 功能菜单,点击新增,创建一个与指定流程设计版本的表单。 创建

表单编辑

新建的表单需要通过列表页,点击编辑进入到编辑页面,在编辑页面编辑表单。 创建 与流程设计类型,表单设计也分三个区域,左侧控件区,中间是布局区,右侧是控件属性区。 通过拖拽左边的控件到中间编辑区进行控件布局。 推拽 布局

控件编辑

选中布局区域的控件,通过其下的功能按钮可以进行简单的控件编辑。

控件编辑

  • 删除。从布局区域移除该控件
  • 复制。复制一个同样的控件(会生成新的控件id)
  • 编辑。点击编辑右侧将会出现该控件的属性面版,可直接编辑属性。 控件属性 不同的控件类型可编辑的控件属性也有所不同。通常有:
  • 宽度。宽度将表单的宽度分为24 个栅格,选择 6 个栅格表示该控件占据 1/4 的宽度。12 个栅格标识占据了 1/2 的宽度。
  • 字段。自动生成的字段名称。可以手动指定(但不可重复)。通常如果需要在流程中解析字段数据时,应该要手动指定字段名称。
  • 字段类型。也是控件类型,切换将更改控件样式和可配置的属性。
  • 默认值。指定默认值。

几种特殊的控件和属性

  • 单、多选。比如 checkbox,select,radiobox 等,对于这些,特别提供了 选项属性,选项属性是一组数组,指定选择值和名称两个属性填写。 控件属性

  • 表格table。表格需要配置每一列的数据,每一列又可以是不同的数据类型,所以表格的属性相当于嵌套了一层控件 控件属性

预览

点击预览可以查看生成的表单效果。

保存

与流程设计类似,流程表单也可以进行版本管理,一个流程设计可以设计多版表单,但最终数据呈现以激活的版本为准。

控件属性

发起审批

在 流程审批--我发起的-新建 ,即可创建和发起审批。

API

通用

  • 响应格式

    {
      "code": "string", 编号 success|fail
      "msg": "string", 对应编号的 成功|失败
      "data": //接口业务
    }
    
  • 部分标准实体返回:

    • 这些参数 顾名思义。其中 id 为该实体的数据库标识,有时也作业业务唯一标识。
        "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 遵循)

流程设计相关

在流程设计服务里,主要涉及设计和版本的管理。 设计内容则为流程的设计信息,包括节点,连线,人员,条件以及前端其它设计代码管理。 版本则为流程的版本管理,服务的设计为,一个流程可以有多个版本,随着业务的调整可以不断的调整流程,通过版本管理,可以保证现有流程与新流程区分开来,使旧流程不受新流程影响。

  • 创建流程(设计)/api/WorkFlow/CreateWorkFlow

    这里的创建很简单,这里实际上是将设计信息的创建统一放到更新操作,一来为了简化新增逻辑,统一在更新处理,二来也是配合前端的交互进行的设计。

    • name:流程设计名称
    • des :描述

    该操作返回流程基本信息(这时候设计信息还是为空)

    {
      
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
      "workflowNo": "string",//流程编号
      "name": "string",//流程名称
      "activeVersion": 0, //当前使用版本
      "description": "string" //描述
      ......
    }
    
    
  • 流程设计更新 /api/WorkFlow/UpdateWorkflow

    该接口用于保存流程设计信息,当流程版本有多个时,每个版本单独存储,每个版本有自己的设计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 // 退回的节点名称
              }
            ]
          }
        ]
      
      

      其中:

      • nodeType 节点类型。系统会自动从“开始”节点解析流程。 其中比较特别的类型是“会签”,使用会签节点的效果与 isWaitingAllUser 值为 true 的类似,但它是需要连接到它的节点审批通过才会进行下一个节点,它等待的不同来源节点审批完成。有以下枚举类型:
          /// <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获取该选择器的选项 。

          • userSelectorId 选择器id。
          • 返回选项集
          [
            {
              "id": "string", //选项id
              "name": "string" 选项名称
            }
          ]
          
      • rejectNodes :拒绝(驳回、回退、回滚)节点配置。通过该配置,根据条件“回到”指定的节点,在必要情况下可以直接退到指定的节点,默认情况下,不配置时,则按原路返回。该配置主要有两项内容:条件,目标节点

        • nodeId、nodeName 目标节点id 和名称,
        • conditions 条件处理器的配置集合,可配置多个条件,满足其一则跳转到指定节点。条件处理器信息相对简单,主要包含哪个条件处理器以及额外参数。
          • 通过实现 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;
                      }
                  }
              }
            
          • /api/WorkFlow/GetAllconditions 通过该接口可以获取所有的条件处理器信息。
             [Condition("条件处理器A")]
              public class ConditionA : ICondition
            
            
            • 返回基本的条件处理器信息集
              [
                {
                  "id": "string",  //条件处理器id(类名全称)
                  "name": "string", //条件处理器名称  ConditionAttribute 的第一个参数
                  "description": "string" //条件处理器描述  ConditionAttribute 的第二个参数
                }
              ]
            
            
    • workflowLines : 节点连线信息。该属性记录节点间的连线关系(从哪个节点到哪个节点)以及通行(条件是否满足)条件。

      • name: 条件名称。只用于显示
      • fromNodeId: 起始节点
      • toNodeId: 目标节点
      • drawingInfo: 绘制信息。也是冗余备用。万一需要存储前端设计信息,也便于回显
      • conditions :条件选择器信息,用于判断改连线流转是否满足条件,满足才能进行(同 用户选择器 rejectNodes 下的 conditions ),相当于一个“判断”+连线。
    • /api/WorkFlow/UpdateWorkflowActiveVersion 用户切换当前激活(应用)的是哪个版本的流程

      • workflowId :流程id

      • activeVersion :激活版本(versionId)

      • /api/WorkFlow/GetAllWorkflowVersions 通过该接口根据流程id获取所有的流程和版本信息(一个流程有多个版本)

        • workflowId 流程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"
            }
          ]
          
          
  • 获取所有的流程(设计)基本信息列表 ​/api​/WorkFlow​/GetAllWorkflows

    • 返回基本信息列表
        [
          {
            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
            "workflowNo": "string", //流程编号
            "name": "string", //流程名称
            "activeVersion": 0, //当前应用版本
            "description": "string" //描述
          }
        ]
      
      
  • 根据流程id和版本号获取详细的流程设计信息 /api/WorkFlow/GetWorkflowVersion

    • 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": [] //所有节点
        }
      }
      
      

      其中

      • nodeMaps 和 allNodes 数据差不多。对于 allNodes ,类似 接口 /api/WorkFlow/UpdateWorkflow 的参数。 nodeMaps 是 lines 和 nodes 关联的信息的明细,相当于把 连线line 里的 fromNodeId toNodeId 替换为 fromNode toNode ,对应 allNode 里相同nodeId的 node 节点:
        [
          {
            "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"
              }
            ]
          }
        ]
        

流程审批相关

  • 新建审批 /api/WorkFlow/CreateWorkTask

    •   {
          "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 表单数据。这个参数是核心。表单数据可以是平台的任意格式,相应的 userSelectorcondition 在处理过程也会传递表单数据共解析和判断。

    • 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/isProcessingworkTaskStatus 的常用判断值

      • isSimulation 是否是模拟流程。 该属性不在前端传递,而是通过 创建模拟流程接口 /api/WorkFlow/CreateSimulationWorkTask 创建的任务。其参数与 CreateWorkTask 一致。

  • 发起审批 /api/WorkFlow/StartWorkTask

  • {
      "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。如果是转发才会有值。即标记是哪个审批记录转发过来的。

  • 通过审批 /api/WorkFlow/PassProve

    • {
        "comment": "string",
        "stepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "resourceIds": "string"
      }
      
    • comment 批文

    • stepId 待审批记录id

    • resourceIds 附件id(前面提到过)。

    • 返回发起审批后的处理待审批记录(与 发起审批 /api/WorkFlow/StartWorkTask 接口返回一致):

  • 驳回审批 /api/WorkFlow/RejectProve (参考发起审批 /api/WorkFlow/StartWorkTask

  • 撤回审批 /api/WorkFlow/WithdrawProve (参考发起审批 /api/WorkFlow/ StartWorkTask

  • 转发审批 /api/WorkFlow/RejectProve

    转发审批为转发当前审批操作转给另一个人进行审批。与其他审批处理操作相似,但多出一个传参。

      {
        "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
          }
        ]
      }
    
    • userSelectors 用户选择器。此处转发操作也采用用户选择器进行解析,一来统一规范,二来可扩展性也 高。参考流程设计接口 (流程设计更新 /api/WorkFlow/UpdateWorkflow
  • 获取所有审批步骤 /api/WorkFlow/GetAllTaskStepsOfWorkTask

    • worktaskId 审批id。
    • 返回该审批流所有的审批记录。(与 发起审批 /api/WorkFlow/StartWorkTask 接口返回一致)。
  • 获取所有审批步骤(根据entity 信息) /api/WorkFlow/ GetAllTaskStepsOfWorkTaskByEntityInfo

    • entityFullName 外部分类全称。
    • entityKeyValue 外部表单标识。
    • (参考 新建审批 /api/WorkFlow/CreateWorkTask 里的)
  • 分页获取用户待处理审批 /api/WorkFlow/GetUnHandledWorkTasksOfUser

    • CurrentPage 当前页(默认从1开始)
    • MaxResultCount 分页大小
    • 返回流程审批集合.(属性参考 新建审批 /api/WorkFlow/CreateWorkTask)
  • 分页获取用户已处理审批 /api/WorkFlow/GetHandledWorkTasksOfUser

    参考 分页获取用户待处理审批 /api/WorkFlow/GetUnHandledWorkTasksOfUser

    • 这里的已处理包括自己发起的,也包括别人发起经由自己审批的。
  • 分页根据entity信息获取处理中的审批 ​/api​/WorkFlow​/ GetAllProcessingWorkTasksByEntityType

    • entityFullName 外部分类全称。
    • entityKeyValues 外部表单标识,多个之间通过英文逗号隔开。
    • 返回流程审批集合.(属性参考 新建审批 /api/WorkFlow/CreateWorkTask)
  • 分页获取用发起的审批 /api/WorkFlow/GetWorkTasksOfCreator

    • CurrentPage 当前页(默认从1开始)
    • MaxResultCount 分页大小
    • 返回流程审批集合.(属性参考 新建审批 /api/WorkFlow/CreateWorkTask)
  • 接口现状

  • 目前已实现的接口只能保证基本的流程运作,实际上应用过程可能还有更多的场景没考虑到。由于能力有限,不可能做到面面俱到,特别是对于查询接口,这就需要根据业务自己实现。

插件系统

系统实现了一套插件管理逻辑,目的是为了某些插件(扩展点)可以配置和复用。 管理员登录可以对插件进行管理和配置 Alt text

插件相关流程主要有:注册、配置、实例化。

注册

注册插件类型

通过 PluginManager.RegisterPluginType(Type 插件类型 ,string 插件类型名称) 注册插件类型,这一步实际上就是注册抽象类(接口、基类),定义是属于某个类型的插件,比如用户选择器就是一个插件类型。 在插件管理,表现为插件大类: Alt text

注册插件实例类型

在讲注册插件实例之前,先说清楚插件的实现。(ps:插件系统不支持依赖注入

插件的实现,就是接口的实现或者是继承基类的子类。比如实现了用户选择器的接口 IUserSelector 的实现类:DefaultUserSelector、UserSelectorDefaultHttp。

  • 可配置插件 。在实现接口或者继承基类的同时,同时实现 IConfigurable 接口,即可实现插件的可配置。
       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)); 这一步注册插件类型抽象的实现(或者子类),比如用户选择器就有按角色,按用户等方式获取,对应的就是插件类型的实现。其中:

  • className 为实现类的全类名,
  • name 为插件名称(比如 按用户)
  • description 描述
  • entry 插件入口,这个参数用于从外部载入插件的dll配置,在代理里配置时,这个参数可以忽略。
  • version 版本号(预留,暂时没区分),目前建议直接从类型名称上加版本。
  • assembly 插件所在的 程序集

除了手动在系统内注册,也支持注册外部插件(兼容之前的插件,参考下边用户选择器,条件处理器的插件注册),系统会自动根据所注册的插件类型(抽象),遍历插件目录下实现了对应抽象的插件类型,并载入。

  • 1 配置外部插件的插件目录,在 WorkFlowCoreModule ConfigureServices 方法里,配置插件所在的文件夹
     Configure<PluginOptions>(options =>
     {
         options.BasePath = Path.Combine(AppContext.BaseDirectory, "Plugins");
     });
    
  • 将实现的插件类库打包放在插件目录下(只要在目录下即可,可以嵌套,会自动遍历)。外部的插件实际上就是实现类生成的类库文件 + 插件描述文件 manifest.json 。插件的目录结构如下:
    • 插件包
      --src
      ----XXXX_Entry.dll  //导出的入口dll(可能会多个)
      --manifest.json  //描述文件
      
      
    • manifest.json 格式如下(已用户选择器为例)
        {
          "Entry": "UserSelector_PluginDemo.dll", //入口插件格式,插件载入时,将以这个为入口载入(必填)
          "Version": "1.0",
          "ClassName":"插件全类型名称",
          "Name":"中文名称",
          "Description":"类型描述"
        }
      
      

配置

对于实现了 IConfigurable 接口的插件,在插件配置管理则允许实现配置。具体的配置格式要求由插件自定义。平台默认对 key=value 格式自动解析。 Alt text

每个插件配置有唯一的id(初始化时,自动创建一个配置信息),对于可配置插件,同一个插件实现可以针对应用场景进行多个配置。比如: Alt text Alt text

实例化

插件的实例化只能由插件自行管理,暂时没法使用框架的依赖注入。 实例化支持指定配置id实例化,或者实现某个插件抽象类型的所有实例

pluginManager.Resolve<ICondition>(conditionId);
pluginManager.ResolveAll<IUserSelector>();

扩展点

用户选择器

通过自定义实现 IUserSelector 接口实现所需的用户选择器。

  • 实现插件(具体参考 Plugins/UserSelectors/UserSelector_PluginDemo)
    • 新建自定义类库

    • 引入项目依赖 WorkFlowCore,或者手动引入dll (WorkFlowCore.Common.dll,WorkFlowCore.dll)

    • 实现自定义逻辑。并创建对应的 manifest.json

      {
        "Entry": "Condition_PluginDemo.dll", //入口文件dll
        "Version": "1.0" //版本号(目前没应用到,预留)
      }
      
      
    • 编译生成。并将生成的文件按如下结构按如下格式复制到 Host发布目录下 Plugins\UserSelectors

    • 重启(启动) Host 服务

    • 目前插件的自定义实现类暂时还无法通过ioc 注入参数。除非插件自己维护 ServiceProvider。

条件处理器

通过自定义实现 ICondition 接口实现所需的用户选择器。

  • 实现插件(具体参考 Plugins/Conditions/UserSelector_PluginDemo)
  • 其他步骤类似用户选择器。只是生成后的插件导入到Host 的路径为 Plugins\Conditions

自定义身份认证

  • 自定义实现 ICustomizationVerifyExtension 接口自定义认证扩展。接口返回基本的认证信息。该插件用于自定义验证身份信息,通过扩展可以实现第三方平台的认证。

     public VerifyOutput Verify(VerifyInput input)
    
     public class VerifyOutput
      {
          public bool IsValid { get; set; }
          public AuthorizationUser User { get; set; }
          public Claim[] Claims { get; set; }
      }
    

    身份认证插件的认证逻辑示意图 Alt text

自定义流程审批状态同步

  • 自定义实现 ICustomTaskStateChangeEventEventHandler 接口自定义流程审批的状态变更通知。每次审批流程状态变化时调用。

Alt text

GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

简介

该流程引擎完全开源免费,致力于打造与平台组织架构无关、高扩展的工作流引擎。 通过自定义用户选择器和条件处理器实现既有业务的组织架构关联和审批过程处理。 展开 收起
LGPL-3.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化