现在的位置: 首页 > 综合 > 正文

一个真正符合中国国情的工作流设计参考(包括PHP实现) .

2013年06月11日 ⁄ 综合 ⁄ 共 9600字 ⁄ 字号 评论关闭

开源的工作流很少有让人满意的,即便是国内用的比较多的jbpm,用起来也会觉得很便扭。再加上PHP中没有什么好用的工作流,于是干脆自己设计一个,设计的原则如下:

1 根据80/20原则,只使用wfmc模型中最符合自身应用的20%功能

2 充分吸收国内使用jbpm开发BOSS中遇到的问题,工作流引擎只负责参数的收集和流程的流转,具体和业务的控制,交给每个流程定制的控制类去实现。

3 表单采用简单的html+控制标签的方法实现

4 权限和模板引擎,以及其它辅助函数直接使用办公系统自带的框架

5 充分利用PHP语言的特点,流程设计是基于数据库的,程序上使用OO设计,但采用重对象的方法

6 不把可视化设计流程的工作交给最终客户,而且由设计时完成,因此不考虑流程版本更新的问题

一、工作流数据表设计

 

tbl_workflow_defination:工作流定义表

defination_id

流程id

 

defination_name

流程名称

 

defination_handler

流程处理辅助文件,每个工作流一个文件

自定义处理文件,及其对象。例如workflow-proporsal-handler.php,其中定义对象proposal

 

tbl_workflow_node:流程结点步骤表

node_id

结点id

 

defination_id

流程id

 

node_index

结点序号

结点的step

node_name

结点名称

 

node_type

结点类型

1人为决策,2自动处理(直接执行execute_function)3等待外部响应(例如外部WS触发),4分支,5汇总
6
结束结点(此结点执行时候自动终止进程)

init_function

流程初始函数

 

run_function

流程运行函数

 

save_function

流程保存函数

 

transit_function

流程流转函数

 

prev_node_index

前结点序号

例如1。开始结点没有

执行前,通过此来校验一下流程

next_node_index

后结点序号

例如[同意]3,[不同意]4。尾结点或要结束的结点没有,若没有,直接调用end

executor

执行角色,组,人

role[1,2] group[1,2] user[1,2],为空由运行时决定

execute_type

执行类型

0需所有人执行 1只需一人执行

remind

提醒

0不提醒 1邮件 2短信 3邮件和短信

field

可编辑的字段

name,content

max_day

最长时间()

 

 

tbl_workflow_process
:流程执行进程表

process_id

进程id

 

defination_id

流程id

 

process_desc

进程描述

显示在我的工作台中

context

上下文

存放上下文变量,例如业务表的id

current_node_index

当前结点序号

 

start_time

流程启动时间

如遇分支、汇合显示为:

1=》3,4=》3,5=》6

finish_time

流程完成时间

 

state

状态

1运行 2结束

start_user

发起人

发起人,用于显示自己的流程

 

tbl_workflow_thread
:流程执行线程表

thread_id

线程id

 

process_id

进程id

 

process_desc

进程描述

 

node_id

结点id

 

node_name

结点名称

 

executor

执行人

 

start_time

线程生成时间

 

receive_time

线程接收时间

 

finish_time

线程完成时间

 

max_time

结点规定的最长时间

 

state

状态

0未接收 1已接收 2已处理

 

二、常见流程

人工决策

领导传阅

部门领导审批

填写表单

结束

放弃

提交

同意

重填(退回)

不同意

完成

外部响应

发送支付信息

接收支付成功响应(外部WS触发该流程)

三、PHP设计

运行的函数由结点在设计时候决定,如果没有设定,就使用默认的函数。利用了PHP语言的以下特性

<?php
class Foo
{
    function Variable()
    {
        $name 'Bar';
        $this->$name(); // This calls the Bar() method
    }
    
    function Bar()
    {
        echo "This is Bar";
    }
}

$foo = new Foo();
$funcname "Variable";
$foo->$funcname();  // This calls $foo->Variable()

?>

使用前可以用method_exists来检查。

 

WorkflowService.php

  WorkflowService

    $defination

$process

$node

$thread

$input 用户输入的和流程有关的变量

list_defination()

{

}

init_process(defination_id)

{  global user;

取得$defination,得到业务的handler,例如WorkflowProposalHandler

   建立$process行记录

}

start_process()

调用WorkflowProposalHandler->start($process)//新建业务对象,并把业务类的参数例如proposal_id放到$process[‘context’]里面

   init_thread(1);  //默认调用第一个结点

}

 

list_ my_thread ()

{  global user;

}

 

init_thread(node_index)

{

  取得$node

  取得$process

  修改$process为运行到当前结点

  Switch($node[‘node_type’])

   Case 1: 人工决策

       建立$thread

       WorkflowProposalHandler-> init_function ($process,$node,$thread)

       发送提醒

Case 2: 自动处理

    建立$thread

    WorkflowProposalHandler-> init_function ($process,$node,$thread)

       调用run_thread(thread_id)

Case 3: 等待外部响应

    建立$thread

    WorkflowProposalHandler-> init_function ($process,$node,$thread)

Case 4: 分支

    取得所有分支的子结点

    init_thread(子结点)

Case 5: 汇总:

    取得所有前结点,如果所有前结点的Thread都结束了,调出下一结点

       调用init_thread(子结点)

Case 6: 结束:直接结束进程process

    end_process()

}

run_thread(thread_id)

{   

取得$node

取得$process

取得$thread

  Switch($node[‘node_type’])

   Case 1: 人工决策

       修改$thread为已接收

          WorkflowProposalHandler-> run_function ($process,$node,$thread)
显示表单

Case 2: 自动处理

    修改$thread为已接收

    $next_node_id=WorkflowProposalHandler-> run_function ($process,$node,$thread)

       调用transit_thread(thread_id, $next_node_id)

Case 3: 等待外部响应

    修改$thread为已接收

    $next_node_id=WorkflowProposalHandler-> run_function ($process,$node,$thread)

    transit_thread(thread_id, $next_node_id)

Case 4: 分支

Case 5: 汇总:

Case 6: 结束:

}

save_thread(thread_id)

{  //保存结点数据

取得$node

取得$process

取得$thread

  Switch($node[‘node_type’])

   Case 1: 人工决策

          WorkflowProposalHandler-> save_function ($process,$node,$thread)
保存表单

WorkflowProposalHandler-> run_function ($process,$node,$thread)
显示表单

Case 2: 自动处理

Case 3: 等待外部响应

Case 4: 分支

Case 5: 汇总:

Case 6: 结束:

}

transit_thread(thread_id, $next_node_id)

{ 取得$node

  取得$process

取得$thread

  Switch($node[‘node_type’])

   Case 1: 人工决策

      WorkflowProposalHandler->transit_function($process,$node,$thread,$next_node_id)  

          修改$thread为已完成

          If($next_node_id < $ cur_node_id) { //回退

删除所有大于$next_node_idThread

}

init_thread($next_node_id)

Case 2: 自动处理

修改$thread为已完成

           If($next_node_id < $ cur_node_id) { //回退

删除所有大于$next_node_idThread

}

       init _thread($next_node_id)

Case 3: 等待外部响应

    修改$thread为已完成

           If($next_node_id < $ cur_node_id) { //回退

删除所有大于$next_node_idThread

}

    init _thread($next_node_id)

Case 4: 分支

Case 5: 汇总:

Case 6: 结束:

 

}

 

end_process()

 

list_my_process

view_process

 

workflow_proposal_handler.php

WorkflowProposalHandler

  start()

  prepare_input()
准备用户输入变量,从$_POST收集

  init_function ()
线程建立后调用的默认函数,当流程的执行者由程序生成时,在此函数内更改$threadexecutor,例如直接赋值user[2]

run_function ()
线程运行化时候调用的默认函数

save_function ()
保存运行信息

  transit_function ()
执行流转

  sendmail 其它结点调用函数

 

workflow.php

 switch(op)

   case list_defination

        参数:无

WorkflowService->list_defination()

case start_process :
启动

       参数:defination_id

       WorkflowService->init_process(defination_id)

WorkflowService->start_process()

   case list_ my_thread :
待处理的列表

       WorkflowService->list_ my_thread()

   case run_thread :

       参数:thread_id

       WorkflowService->run_thread(thread_id)

case save_thread :

    参数:thread_id

    input收集起来(所有的变量以 f_
开头),赋给WorkflowServiceInput,另外还要获得thread_id

    WorkflowService->save_thread(thread_id)

   case transit_thread :

   参数:thread_id

input收集起来,赋给WorkflowServiceInput,另外还要获得thread_id

$next_node_id =
得到用户选择的下一结点id

WorkflowService-> transit _thread(thread_id$next_node_id)

 

   case list_my_process:
所有我发起的流程

case list_all_process:
所有我发起的流程

case view_process :

 

在其它程序中初始化流程

    1先自行建立好业务表单

2WorkflowService->init_process(defination_id)

3把建好的业务表单的ID放在processcontext里面

4WorkflowService->init_thread(1)

WorkflowService->transit_thread(12)
通过手动调用把前面的流程过掉

外部服务继续流转流程(只用于自动流程)

1 input收集起来,赋给WorkflowServiceInput,另外还要获得thread_id

2 WorkflowService->run_thread(thread_id)

顺便把和daniel的聊天也附在下面:

♂蒓玥♀ 12:09:25
execute_type的作用是表示这个流程由多少人完成,例如对于会签,就需要大家一起完成。如果是客户报修,只要有一个客服接收了这次报修,流程就继续
 
是的,我写这个文的目的,就是想说明,自己完全可以开发一个符合实际需要的流程引擎。如果总是用那些现成的引擎,常常会束手束脚
 
-=Daniel=- 12:11:39
是的啊, 我知道, 但是我认为execute_type本身就是为了executer存在的. executer又是来自外部的, 比如用户管理模块啊什么的. 外部就可以生成一个executer的id给流程使用了, 为什么这里还需要描述这个呢. 比如说外部生成一个id, 这个id是描述一个人, 那就是一个人的操作, 如果是一个组, 那就是一个组的操作. 在流程内部标明这个id的含义有什么作用呢?

♂蒓玥♀ 12:11:46
remind就是一个提醒功能,设计的时候,自己定义那些结点需要提醒,把他放进去,其实是为了利用空间换时间的原理,减少定义在其他地方的时候浪费读取的时间
 
-=Daniel=- 12:13:01
其实我的意思是remind可能有很多种, 不可能在流程内部给定义完. 如果留接口而不是由流程给定, 可能会更加全面点.

♂蒓玥♀ 12:14:07
我的executor是这样定义的,可以定义user[1]表示id为1的用户,group[1]表示组id为1,role[1]表示角色1,这几个组合是可以并的,最终根据定义把thread分到所有的用户,但是execute_type的含义是,只要有一个人执行了这个thread,我根据设定判断,如果不需要所有人做,就把其他的thread结束

 
恩,我把所有业务的类都交给handler,例如一个提案流程
就是workflow-proposal-handler类,里面定义所有结点需要的处理方法,当然也可以发送短信
 
我把一次完整的流程看作process,其中的每一条分支,都是thread
所有的thread都是可以直接由外部调用的,这比jpbm这些要好多了
 
-=Daniel=- 12:17:02
我不同意, 我认为executer不管一个人完成或者多个人完成, 都是只有有其中一个人完成就立即判定为执行了流程. 如果一个流程需要多个人有次序的完成, 那么就说明这个流程不够详细, 需要再细分.

 
♂蒓玥♀ 12:18:08
哦,我先定义了两种情况:0 所有人处理,就是会签
1 只需一个人处理,就是客服模式,
这两个是最长用的模式
 
-=Daniel=- 12:18:53
哦, 这可能是你定义流程的模式跟我的有一定的差别, 或者是我们的客户不同类型吧.

-=Daniel=- 12:19:51
对了, 有一种流程你怎么处理的, 比如一个流程中有一个步骤, 它按A这条路走是对的, 按B走也是可以的, 只要走了子流程中的任意一个流程, 都可以.

♂蒓玥♀ 12:20:48
就是分支啊,随便用户选哪一条路径

-=Daniel=- 12:22:35
是啊, 我这边在这个分支上遇到了意外, 因为现在要做事务功能, 加上了分支很麻烦.

♂蒓玥♀ 12:23:03
哦,我的这个客户本身就是用SAP的,他们一个子公司要上提案系统,是基于Web的。
 
♂蒓玥♀ 12:24:26
你的事务处理具体想干什么?
 
-=Daniel=- 12:26:47
事务嘛, 最主要的就是回溯啊, 原先在纪录的保存上做的都是当前状态的值, 回溯就是返回到上一个值, 要求处理分支的回溯, 那就要求将当前的操作本身也当作一个值, 进行回溯.

♂蒓玥♀ 12:27:15
我的回退作为流程分支处理
 
-=Daniel=- 12:27:50
如果一个事务跨越了一个分支倒可以, 问题就是怕事务从分支的一部分开始, 到另外一个分支的某一部分结束, 那就会弄的很乱.

-=Daniel=- 12:28:39
我看了, 这是一个方法, 就是所有的节点都有一个回退功能, 做起来比较烦.

♂蒓玥♀ 12:29:39
这个问题我想过,包括所有结点的回退,我的结论是:纯粹图论上的功能对实际业务没有任何作用,只要在流程设计的时候设计好,不要有这些问题就行了
 
♂蒓玥♀ 12:30:18
对于银行这种单位,流程里面有很多自动划帐的结点,能让人随便回退么
 

分享到:
查看评论
8楼
iamXiaMi
2008-10-28 09:39发表
[回复]
[引用]

[举报]
基于php的workflow能做引擎?~

那如何解决工作流的设计呢?php~~貌似不太现实吧?~

7楼
make6b
2008-01-27 00:16发表
[回复]
[引用]

[举报]
不懂L
http://www.pvpsale.com
6楼
border1
2007-10-18 11:23发表
[回复]
[引用]

[举报]
开源的基于ajax的可视化自定义web表单工具, 在: http://my5155.meibu.com
5楼
gzaqq
2007-10-17 19:56发表
[回复]
[引用]

[举报]
我用oracle的bpm做过一个项目,也培训过IBM的,觉得都有点不合中国的国情,我觉得他们的优势主要是jdeveloper
4楼
tanzu
2007-10-17 12:02发表
[回复]
[引用]

[举报]
我觉得platform的发言很可笑, 难道一定要有什么大公司出的东西做引擎才算是工作流吗?
3楼
tanzu
2007-10-17 12:00发表
[回复]
[引用]

[举报]
楼主很不错了, 至少能够将自己的东西提供大家讨论, 可能在面上不够详细, 但是这个东西至少是个完整的工作流工具了. 想提几个建议, execute_type,remind 这几个东西可以说完全不必要存在, 因为我认为这些东西应该与工作流本身无关, executer本身就是一个id, 决定了由哪些操作者来执行了, 外部的用户机制来抉择这个id的判断就够了. 还有remind也显得很突然, 我认为放在这里也没有必要, 我倒认为应该加一个ajax或者rpc的接口来获取或者判断当前的状态或者工作流的位置, 这样这里的remind工作就可以由外部去处理了.
另外一个提议是内部应该有工作流程的输出, 可以是一个log, 因为在这个中间做事务似乎有点麻烦, 但是至少有一个输出结果可以供外部接口使用.
2楼
platform
2007-10-16 18:23发表
[回复]
[引用]

[举报]
引擎呢???到底什么是工作流呢?
1楼
lizzz
2007-10-16 13:58发表
[回复]
[引用]

[举报]
这篇文章怎么上了首页!看来名字吓人就是有好处。我不知道是否真的了解什么是中国国情的工作流。首先我不完全赞同你的“6、不把可视化设计流程的工作交给最终客户,而且由设计时完成,因此不考虑流程版本更新的问题”。前半句我赞同,确实中国的工作流用户不可能自己去修改流程定义,但是后半句严重的偏离了中国国情。中国的国情是领导说今天这样,明天说那样,流程本身就具有太多的不确定性,因此即使不让用户修改,至少也得提供开发人员修改定义、更新历史数据的接口。
另外再说一下流程控制的问题,在你的文章中似乎没有涉及,灵活的流程控制才是真正的中国国情,中国人的条条框框很多,然而奇怪的是在流程处理的时候中国人却总是想冲破条条框框的限制,不按牌理出牌。
我是专业做工作流的,产品在多个大型银行的流程平台中使用。真实的感受是,想做满足中国人需求的工作流,太难了。

Re:
danny_xcz
2007-10-17 07:49发表
[回复]
[引用]

[举报]
谢谢你的回复,我不是不考虑流程版本的问题,而是流程的更新是由程序员完成的,就算是SAP的系统也是这样,升级前让客户先把没有执行的流程赶快执行完毕。其次我的流程提供了最灵活的用户接口,而且完全自定义,这是PHP天生的优势,我本来有一张很全的流程图,但是word的不好粘贴,我懒得弄了,里面包括了后退,分支等各种工作流。我这个工作流集成了SAP,JBPM,Shark,JBPM,通达OA等各个工作流的强处,避免了他们的缺点,最关键的是应用了PHP开发,所以非常的简单。&lt;br /&gt;流程引擎全部以PHP写成,如果你感兴趣,可以加我的QQ:88659854(请说明工作流)更进一步沟通

开源的工作流很少有让人满意的,即便是国内用的比较多的jbpm,用起来也会觉得很便扭。再加上PHP中没有什么好用的工作流,于是干脆自己设计一个,设计的原则如下:

1 根据80/20原则,只使用wfmc模型中最符合自身应用的20%功能

2 充分吸收国内使用jbpm开发BOSS中遇到的问题,工作流引擎只负责参数的收集和流程的流转,具体和业务的控制,交给每个流程定制的控制类去实现。

3 表单采用简单的html+控制标签的方法实现

4 权限和模板引擎,以及其它辅助函数直接使用办公系统自带的框架

5 充分利用PHP语言的特点,流程设计是基于数据库的,程序上使用OO设计,但采用重对象的方法

6 不把可视化设计流程的工作交给最终客户,而且由设计时完成,因此不考虑流程版本更新的问题

一、工作流数据表设计

 

tbl_workflow_defination:工作流定义表

defination_id

流程id

 

defination_name

流程名称

 

defination_handler

流程处理辅助文件,每个工作流一个文件

自定义处理文件,及其对象。例如workflow-proporsal-handler.php,其中定义对象proposal

 

tbl_workflow_node:流程结点步骤表

node_id

结点id

 

defination_id

流程id

 

node_index

结点序号

结点的step

node_name

结点名称

 

node_type

结点类型

1人为决策,2自动处理(直接执行execute_function)3等待外部响应(例如外部WS触发),4分支,5汇总
6
结束结点(此结点执行时候自动终止进程)

init_function

流程初始函数

 

run_function

流程运行函数

抱歉!评论已关闭.