模型抽象
上文对工作流进行了一些基本的介绍,有了基础篇的内容,相信我们可以很容易抽象了一些实体模型:流程模型、开始节点模型、结束节点模型、任务节点模型、决策节点模型、分支节点模型、合并节点模型、边模型。
基础模型
我们把模型进行进一步抽象,可以得到基础模型,基础模型只包含两个属性,唯一编码和显示名称。
class BaseModel (object):
"""
基础模型
"""
__abstract__ = True
# 唯一编码
name = None
# 显示名称
displayName = None
模型行为
如果按照面向对象来看,流程模型本身除了属性外,还应该具有方法和行为,即节点和边所能做的事,这里用Action接口来定义。但是Python中没有接口,所以这里我们使用抽象类来定义。
class Action (object):
"""
节点行为接口
"""
__abstract__ = True
def execute(self, execution):
"""
执行行为
@param execution: 执行对象参数
"""
节点模型
流程模型由节点模型和边构成,节点的类型虽然很多,但是依然可以对其进行进一步抽象。节点都有自己通用性的行为,如前置拦截、后置拦截、日志处理等,除了通用性行为外,还可以根据子类不同的特性,实现exec抽象方法。在python中,实现抽象方法,需要使用abc模块,并且使用@abstractmethod装饰器。
from abc import ABC, abstractmethod
class NodeModel (ABC, BaseModel, Action):
"""
节点模型
"""
__abstract__ = True
# 输入边集合
inputs = []
# 输出边集合
outputs = []
# 前置拦截器-字符串,多个使用英文逗号分割
preInterceptors = None
# 后置拦截器-字符串,多个使用英文逗号分割
postInterceptors = None
@abstractmethod
def exec(self, execution):
"""
由子类自定义执行方法
@param execution: 执行对象参数
"""
def execute(self, execution):
"""
执行节点
@param execution: 执行对象参数
"""
self.exec(execution)
有了节点模型后,其他具体的节点模型就会显示相对简单。
开始节点模型
开始节点主要继承节点模型并实现其exec方法。
class StartModel(NodeModel):
"""
开始节点模型
"""
def exec(self, execution):
"""
执行节点
@param execution: 执行对象参数
"""
# 执行开始节点自定义执行逻辑
结束节点模型
结束节点主要继承节点模型并实现其exec方法。
class EndModel(NodeModel):
"""
开始节点模型
"""
def exec(self, execution):
"""
执行节点
@param execution: 执行对象参数
"""
# 执行结束节点自定义执行逻辑
任务节点模型
任务节点是流程中非常重要的节点,其拥有更多自己的属性。当然,下面只是把常用的属性罗列出来,后续也根据具体实现的业务,增加和删除其属性。
class TaskModel(NodeModel):
"""
任务节点模型
"""
form = None # 表单标识
assignee = None # 参与人
assignmentHandler = None # 参与人处理类
taskType = None # 任务类型(主办/协办)
performType = None # 参与类型(普通参与/会签参与)
reminderTime = None # 提醒时间
reminderRepeat = None # 重复提醒间隔
expireTime = None # 期待任务完成时间变量key
autoExecute = None # 到期是否自动执行Y/N
callback = None # 自动执行回调类
ext = {} # 自定义扩展属性
def exec(self, execution):
"""
执行节点
@param execution: 执行对象参数
"""
# 执行任务节点自定义执行逻辑
决策节点模型
决策节点也是一种特殊节点,其会有决策表达式,和决策处理类属性,主要用于动态计算下一个节点的走向。
class DecisionModel(NodeModel):
"""
决策节点模型
"""
def exec(self, execution):
"""
执行节点
@param execution: 执行对象参数
"""
# 执行决策节点自定义执行逻辑
分支节点模型
当用到并行流程的时候,该节点就派上用场了,这里只是先简单定义,后续也会根据具体的业务需要,扩展新的属性。
class ForkModel(NodeModel):
"""
分支节点模型
"""
def exec(self, execution):
"""
执行节点
@param execution: 执行对象参数
"""
# 执行分支节点自定义执行逻辑
合并节点模型
合并节点和分支节点是一起出来的,这里也是先按照最基础的定义。
class JoinModel(NodeModel):
"""
合并节点模型
"""
def exec(self, execution):
"""
执行节点
@param execution: 执行对象参数
"""
# 执行合并节点自定义执行逻辑
边模型
两节点连线,就构成一条边,所以边的关系属性就是source和target,边也有自己的执行方法。
class TransitionModel(BaseModel,Action):
source = None # 边源节点引用
target = None # 边目标节点引用
to = None # 目标节点名称
expr = None # 边表达式
enabled = False # 是否可执行
流程模型
有了上面的元素,那么流程模型就很容易得到如下定义:
class ProcessModel(BaseModel):
type = None # 流程定义分类
instanceUrl = None # 启动实例要填写的表单key
expireTime = None # 期待完成时间变量key
instanceNoClass = None # 实例编号生成器实现类
# 流程定义的所有节点
nodes = []
# 流程定义的所有任务节点
tasks = []
实例流程模型对象
from packages.engine.model.end_model import EndModel
from packages.engine.model.process_model import ProcessModel
from packages.engine.model.start_model import StartModel
import logging
from packages.engine.model.task_model import TaskModel
from packages.engine.model.transition_model import TransitionModel
class TestBuildProcess:
def test_build(self):
processModel = ProcessModel()
startModel = StartModel()
startModel.name = 'start'
startModel.displayName = '开始'
processModel.nodes.append(startModel)
t1 = TransitionModel()
t1.name = 't1'
t1.to = 'apply'
t1.source = startModel
startModel.outputs.append(t1)
applyTaskModel = TaskModel()
t1.target = applyTaskModel
applyTaskModel.name = 'apply'
applyTaskModel.displayName = '请假申请'
processModel.nodes.append(applyTaskModel)
processModel.tasks.append(applyTaskModel)
t2 = TransitionModel()
t2.name = 't2'
t2.to = 'deptApprove'
t2.source = 'applyTaskModel'
applyTaskModel.outputs.append(t2)
deptApproveTaskModel =TaskModel()
t2.target = deptApproveTaskModel
deptApproveTaskModel.name = 'deptApprove'
deptApproveTaskModel.displayName = '部门领导审批'
processModel.nodes.append(deptApproveTaskModel)
processModel.tasks.append(deptApproveTaskModel)
t3 = TransitionModel()
t3.name = 't3'
t3.to = 'end'
t3.source = deptApproveTaskModel
deptApproveTaskModel.outputs.append(t3)
endModel = EndModel()
t3.target = endModel
endModel.name = 'end'
endModel.displayName = '结束'
processModel.nodes.append(endModel)
for node in processModel.nodes:
logging.info(f"{node.name},{node.displayName}")
执行单元测试
pytest -q tests/test_build_process.py