jbpm的智能选择审批人及人工任务分配的实现思路

简介:

一、jbpm中审批人的概念


在工作流中每一个人工任务的办理者被称作审批人,在原生的jbpm定义中,我们可以看到assignee和assignmentHandler这两个标签。

1
2
3
4
5
6
7
8
< task  name = "review"  g = "96,16,127,52" >  
     < assignment-handler  class = "org.jbpm.examples.task.assignmenthandler.AssignTask" >  
       < field  name = "assignee" >  
         < string  value = "chao.gao"  />  
       </ field >  
     </ assignment-handler >  
     < transition  to = "wait"  />  
   </ task >

assignee如果是单用户,可以是字符串;如果是多用户,则是列表或数组;另外也可以使用类似jsp中的正则表达式,进行动态审批人的指定

如果有assignee,而没有分配处理器标签,则引擎自动将该人工任务分配给所有assignee。如有assignmentHandler会按照默认或定制的分配处理器制定的规则进行审批人的智能筛选以及对任务进行动态的指派。

在会签人方案一文中,提到了关于使用assignmentHandler生成会签子任务的实例,在决策里也提到了DefaultDecisionHandler,jbpm使用监听器模式对不同的处理类进行调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**  
      * @author Tom Baeyens  
      */  
     public  class  AssignTask  implements  AssignmentHandler {   
          
       private  static  final  long  serialVersionUID = 1L;   
       
       String assignee;   
       
       public  void  assign(Assignable assignable, OpenExecution execution) {   
         assignable.setAssignee(assignee);   
       }   
     }

以上是jbpm审批人的原生概念以及如何将指定或动态指定的用户与人工任务相绑定的理论知识。

下面阐述我们的审批人选择以及分配任务的思路

二、智能选择审批人

  审批人设置表结构

wKioL1TcQijD8TvnAAIRX6WIKV4987.jpg


    数据列表如下

wKiom1TcQj2y4m74AAUDqSVCszA605.jpg

  页面设置:

wKiom1TcXYPicv0YAAF46tJqBao488.jpg

一般情况下,商用工作流都提供基于组织架构、汇报关系、角色的审批人选择方法,OA中也提供了相应功能。基于组织架构上下级关系,主要有当前部门、当前部门之上级部门、当前部门上级部门之上级部门,基于组织架构的层级关系,主要有一级部门、二级部门、三级部门、子公司等。以上两种规则,除当前部门外还可以通过表单数据指定部门的编码。基于组织架构的成员,可以将本架构内所有成员全部纳入审批人列表。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
private  Set<String> getAssigneeByAssignSetting(FlowRunTime flowRunTimeInfo,
             UserAssignEntity assignSetting) {
         Set<String> candidateUserIdList =  new  HashSet<String>();
 
         if ( "Y" .equals(assignSetting.getIsForm())){
             String fieldValue = getFiledValueByAssignSetting(assignSetting, flowRunTimeInfo);
             if (StringUtils.isNotEmpty(fieldValue) && assignSetting.getFieldName().contains( "," )){
                 String[] staffIds = fieldValue.split( "[,]" );
                 for (String staffId : staffIds){
                     if (StringUtils.isNotEmpty(staffId)){
                         StaffinfoEntity staffinfoEntity = staffInfoService.queryStaffInfoByStaffId(staffId);
                         if (staffinfoEntity !=  null  && StringUtils.isNotEmpty(staffinfoEntity.getStaffCode())){
                             candidateUserIdList.add(staffinfoEntity.getStaffCode());
                         } else  {
                             throw  new  UserAssignException( "通过表单选人出现错误,请检查!" );
                         }
                     }
                 }
             } else   {
                 StaffinfoEntity staffinfoEntity = staffInfoService.queryStaffInfoByStaffId(fieldValue);
                 if (StringUtils.isEmpty(fieldValue)){
                     throw  new  UserAssignException( "通过表单选人出现错误,请检查!" );
                 }
                  OrgEntity org = orgService.queryOrgByCode(fieldValue);
                  if (! "ShanghaiHQ" .equals(fieldValue)){
                      List<HrbpEntity> hrbpEntityList = hrbpService.queryHrbpByDqCode(fieldValue);
                      if (!CollectionUtils.isEmpty(hrbpEntityList)){
                          for (HrbpEntity hrbp : hrbpEntityList){
                              String staffCode = staffInfoService.queryStaffByStaffId(hrbp.getHrbpId()).getStaffCode();
                              if (StringUtils.isNotEmpty(staffCode) && !candidateUserIdList.contains(staffCode)){
                                  candidateUserIdList.add(staffCode);
                              }                       
                          }
                      }
                  }
                 if (staffinfoEntity !=  null ){
                     if  (UserAssignConstants.USERASSIGN_LEADER_OF_LASTSTEP.equals(assignSetting.getChooseRule()) || 
                         UserAssignConstants.USERASSIGN_LEADER_OF_STARTASSIGNEE.equals(assignSetting.getChooseRule())) {
                         String directleaderCode = staffInfoService.queryLeader(fieldValue).getDirectLeaderCode();
                         if (StringUtils.isNotEmpty(directleaderCode)){
                         StaffinfoEntity directLeader = staffInfoService.queryStaffInfoByStaffId(directleaderCode);
                             if  (directLeader !=  null ) {
                                 candidateUserIdList.add(directLeader.getStaffCode());  
                             }
                        
                     } else  if (UserAssignConstants.USERASSIGN_LEADER_OF_LEADER.equals(assignSetting.getChooseRule())){
                         String indirectleaderCode = staffInfoService.queryLeader(fieldValue).getLeapfrogLeaderCode();  
                         StaffinfoEntity indirectLeader = staffInfoService.queryStaffInfoByStaffId(indirectleaderCode);
                         if (indirectLeader !=  null ){
                             candidateUserIdList.add(indirectLeader.getStaffCode());
                         }
                     else  if (UserAssignConstants.USERASSIGN_LEADER_OF_LEADER_OF_LEADER.equals(assignSetting.getChooseRule())){
                         String indirectleaderCode = staffInfoService.queryLeader(fieldValue).getLeapfrogLeaderCode();   //越级主管
                         if (StringUtils.isNotEmpty(indirectleaderCode)){
                             StaffinfoEntity indirectLeader = staffInfoService.queryStaffInfoByStaffId(indirectleaderCode);
                             if (indirectLeader !=  null ){
                                 String inIndirectLeaderStaffId = staffInfoService.queryLeader(indirectLeader.getStaffId()).getDirectLeaderCode();
                                 if (StringUtils.isNotEmpty(inIndirectLeaderStaffId)){
                                     StaffinfoEntity inIndirectLeader = staffInfoService.queryStaffInfoByStaffId(inIndirectLeaderStaffId);
                                     if (inIndirectLeader !=  null ){
                                         candidateUserIdList.add(inIndirectLeader.getStaffCode());
                                     }
                                 }
                             }
                         }
                     }
                     if (UserAssignConstants.USERASSIGN_STARTER.equals(assignSetting.getChooseRule())){
                             candidateUserIdList.add(staffinfoEntity.getStaffCode());
                     }
       
                 }
                if (org !=  null ){
                     if (UserAssignConstants.USERASSIGN_LEADER_OF_ORG.equals(assignSetting.getChooseRule())){ //当前部门的领导:
                         if  (org !=  null  && !StringUtils.isBlank(org.getManagerCode())) {
                             String managerCode = org.getManagerCode();
                             candidateUserIdList.add(managerCode);
                         }
                     else  if (UserAssignConstants.USERASSIGN_LEADER_OF_LEADER_OF_ORG.equals(assignSetting.getChooseRule())){ //申请部门的上级领导:
                         if  (org !=  null  && !StringUtils.isBlank(org.getParentCode())) {
                             OrgEntity orgParent  = orgService.queryOrgByCode(org.getParentCode());
                             if  (orgParent!= null  && !StringUtils.isBlank(orgParent.getManagerCode())) {
                                 String managerCode = orgParent.getManagerCode();
                                 candidateUserIdList.add(managerCode);
                             }
                         }
                     else  if (UserAssignConstants.USERASSIGN_LEADER_OF_LEADER_OF_LEADER_OF_ORG.equals(assignSetting.getChooseRule())){ //申请部门的上级之上级领导:
                          if  (org !=  null  && !StringUtils.isBlank(org.getParentCode())) {
                              OrgEntity orgtwo  = orgService.queryOrgByCode(org.getParentCode());
                              if  (orgtwo!= null  && !StringUtils.isBlank(orgtwo.getManagerCode())) {
                                  OrgEntity orgParent = orgService.queryOrgByCode(orgtwo.getParentCode());
                                  if  (orgParent!= null  && !StringUtils.isBlank(orgParent.getManagerCode())) {
                                      String managerCode = orgParent.getManagerCode();
                                      candidateUserIdList.add(managerCode);
                                 }
                              }
                          }
                     else  if (UserAssignConstants.USERASSIGN_LEADER_OF_PLATFORM_OR_COMPANY.equals(assignSetting.getChooseRule())){ //申请部门的子公司领导:子公司领导
                         OrgEntity orgPlatform = orgService.queryPlatformEntityByDepCode(fieldValue);
                         if  (orgPlatform !=  null  && !StringUtils.isBlank(orgPlatform.getManagerCode())) {
                             String managerCode = orgPlatform.getManagerCode();
                             candidateUserIdList.add(managerCode);
                         }
                         
                     else  if (UserAssignConstants.USERASSIGN_LEADER_OF_THIRD_ORG.equals(assignSetting.getChooseRule())){ //三级部门
                         
                         if  (OrgConstants.LEVEL_THIRD_ORG.equals(org.getBranchOrg())) {
                             if  (StringUtils.isNotEmpty(org.getManagerCode())) {
                                 String managerCode = org.getManagerCode();
                                 candidateUserIdList.add(managerCode);
                             }
                         else  if (OrgConstants.LEVEL_SECOND_ORG.equals(org.getBranchOrg()) || OrgConstants.LEVEL_FIRST_ORG.equals(org.getBranchOrg()) 
                                 || OrgConstants.LEVEL_PLATFORM.equals(org.getBranchOrg())){
 
                             if (StringUtils.isNotEmpty(org.getManagerCode())){
                                 candidateUserIdList.add(org.getManagerCode());
                             }
                             
                         } //如果是一级部门却要找三级部门,只能提示找不到;
                    
                     else  if (UserAssignConstants.USERASSIGN_LEADER_OF_SECOND_ORG.equals(assignSetting.getChooseRule())){ //二级部门
                             if (OrgConstants.LEVEL_SECOND_ORG.equals(org.getBranchOrg())){
                                 String managerCode = org.getManagerCode();
                                 if (!StringUtils.isBlank(managerCode)){
                                     candidateUserIdList.add(managerCode);
                                 }
                             } else  if (OrgConstants.LEVEL_THIRD_ORG.equals(org.getBranchOrg())){
                                 if  (!StringUtils.isBlank(org.getParentCode())) {
                                     OrgEntity orgParent  = orgService.queryOrgByCode(org.getParentCode());
                                    if  (orgParent!= null  && !StringUtils.isBlank(orgParent.getManagerCode())) {
                                        String managerCode = orgParent.getManagerCode();
                                        candidateUserIdList.add(managerCode);
                                      }
                                  }
                             else  if (OrgConstants.LEVEL_FIRST_ORG.equals(org.getBranchOrg()) || OrgConstants.LEVEL_PLATFORM.equals(org.getBranchOrg())){
                                 if (StringUtils.isNotEmpty(org.getManagerCode())){
                                     candidateUserIdList.add(org.getManagerCode());
                                 }
                             }
                     else  if (UserAssignConstants.USERASSIGN_LEADER_OF_FIRST_ORG.equals(assignSetting.getChooseRule())){ //一级部门
                             if (OrgConstants.LEVEL_FIRST_ORG.equals(org.getBranchOrg())){
                                 String managerCode = org.getManagerCode();
                                 if (!StringUtils.isBlank(managerCode)){
                                     candidateUserIdList.add(managerCode);
                                 }
                             } else  if (OrgConstants.LEVEL_SECOND_ORG.equals(org.getBranchOrg())){
                                 if  (!StringUtils.isBlank(org.getParentCode())) {
                                     OrgEntity orgParent  = orgService.queryOrgByCode(org.getParentCode());
                                    if  (orgParent!= null  && !StringUtils.isBlank(orgParent.getManagerCode())) {
                                        String managerCode = orgParent.getManagerCode();
                                        candidateUserIdList.add(managerCode);
                                      }
                                  }
                             else  if (OrgConstants.LEVEL_THIRD_ORG.equals(org.getBranchOrg())){
                                 if  (!StringUtils.isBlank(org.getParentCode())) {
                                     OrgEntity orgParent  = orgService.queryOrgByCode(org.getParentCode());
                                     if  (orgParent !=  null  && !StringUtils.isBlank(orgParent.getParentCode())) {
                                         OrgEntity orgParentPa  = orgService.queryOrgByCode(orgParent.getParentCode());
                                    if  (orgParentPa!= null  && !StringUtils.isBlank(orgParentPa.getManagerCode())) {
                                        String managerCode = orgParentPa.getManagerCode();
                                        candidateUserIdList.add(managerCode);
                                      }
                                  }
                                 }
                             else  if (OrgConstants.LEVEL_PLATFORM.equals(org.getBranchOrg())){
                                 if (StringUtils.isNotEmpty(org.getManagerCode())){
                                     candidateUserIdList.add(org.getManagerCode());
                                 }
                            
                   
                }
          }
     }

根据汇报关系,目前有发起人本人,发起人直接主管、越级主管、第二越级主管。同样,除发起人外,还可以通过表单数据指定的工号或用户名找出其汇报关系。

基于角色,同基于组织架构的成员规则相同,即将该角色下的所有成员加入审批人列表

当部门和角色均全选时,将按照其交集不空,输出交集;否则输出全集的逻辑进一步筛选。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  if (candidateUserIdList.isEmpty()){
         if  (assignSetting !=  null ){
             //获取直接指定的用户
             if  (StringUtils.isNotEmpty(assignSetting.getUserId())) {
                 String[] userIds = assignSetting.getUserId().split( "[,]" );
                 for  (String user : userIds) { 
                     candidateUserIdList.add(user);
                 }
             }
             
             //通过角色和部门选择用户
             Set<String> roleAndDeptSet = getRoleAndDept(assignSetting);
             if (!CollectionUtils.isEmpty(roleAndDeptSet)){
                  candidateUserIdList.addAll(roleAndDeptSet);
             }
             String startAssignee =  "" ;
             if (StringUtils.isNotEmpty(flowRunTimeInfo.getProcessExecutionId())){
                 startAssignee = processExecutionService.queryById(flowRunTimeInfo.getProcessExecutionId()).getCreateUserCode();
             else  {
                 startAssignee = OAUserContext.getUserCode();
             }
 
             HashSet<String> allUser = chooseCandidateUser(startAssignee, assignSetting, candidateUserIdList);
             if (!CollectionUtils.isEmpty(allUser)){
                 candidateUserIdList = allUser;
             }
         }
     }

如汇报关系与组织架构的基准并非来自发起人或发起人所属组织时,isForm(即来自表单)需勾选且需填写字段名称。该字段名称所代表的用户或组织即作为智能选人规则的基准。目前情况下,如表单字段值有多个,则只代表直接输出字段所指定的用户,如果无法筛选出用户,则审批人列表显示为空。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   private  String getFiledValueByAssignSetting(UserAssignEntity assignSetting, FlowRunTime flowRunTime) {
         String fieldName_ = assignSetting.getFieldName();
         String[] fieldNames = fieldName_.split( "[,]" );
         String formData = flowRunTime.getFormData();
         String formDefineId = flowRunTime.getFormDefineId();
         String fieldValue =  "" ;
         for (String fieldName : fieldNames){
             List<Map<String, Object>> filedValuelist = formDataService.resolveFormData(formDefineId, formData);
             if (filedValuelist !=  null  && filedValuelist.size() >  0 ){
                 Map<String, String> valueMap =  new  HashMap<String, String>();
                 for (Map<String, Object> map : filedValuelist){
                     for (String key : map.keySet()){
                         if (FormDefineConstants.FORMDEFINE_MAP.equals(key)){
                             valueMap = (Map<String, String>)map.get(key);
                         }
                     }
                 }
                 if (!valueMap.isEmpty()){
                     String value = valueMap.get(fieldName);
                     if (StringUtils.isNotEmpty(value)){
                         if ( "" .equals(fieldValue) ){
                             fieldValue = value;
                         else  {
                             fieldValue = fieldValue +  ","  + value;
                         }  
                     }
                 }
             else  {
                 throw  new  UserAssignException(UserAssignConstants.USERASSIGN_EXCEPTION_FORM_ERROR);
             }
         }
         return  fieldValue;
     }

另外,我们还提供了直接指定审批人的方法,对一些特殊无法通过以上逻辑筛选,但审批人又固定的即可直接指定审批人。

三、对智能选人规则及任务分配的展望

通过以上分析也可以看出,审批人可以在流程运行期间通过某些规则直接指定,所以可以将其称为建模期(或定义期)指派。即通过流程定义进行建模的期间,直接为流程中的活动指派参与者的集合。而在运行期间才能指定审批人(3.1、3.2)或进行分配任务(3.3、3.4、3.5)的则被称为运行期规则。

3.1同前驱

  顾名思义,就指的是在流程的某个环节的办理人,与当前流程当前环节的某个前驱节眯是同一个人,这也与工作流“保持熟悉(Retain familiar)”原则相切合。

同前驱的选人规则目前无法通过建模期指派实现,比如以下场景:

1.招聘需求有“编制管理员审核”(审批候选人可能有多个)以及“招聘管理员签收”环节,其中要求“招聘管理员签收”环节必须与其前驱节点“编制管理员审核”确定;

2.档案借阅的借出环节与还进环节也要求必须是同一个人。

目前我们已经就这种需求形成两种方案:一种是杜提出的在前驱中后置将某代表其同后继的隐藏工号字段填充,后继则使用表单字段选人将审批人选出;一种是我提出的用同前驱节点及execution id遍历历史任务表,将前驱节点历史审批人拿出做为后继节点审批人。

3.2基于历史、基于能力、随机或循环

    以上的三种选人规则,是一种自学习自组织的选人方法。如通过以上选人得出的任选审批人为多个,那么可以使用以上规则筛选为一个或将多人按以规则排序。

  基于历史的是一种笼统的概念,又可以细分为当前任务最少、完成历史任务最多等规则;基于能力则可细分为当前列表中职务最大、层级最高、办理任务耗时最少等;随机则是指列表元素的先后顺序或指定用户是随机的,循环是指列表元素的先后顺序或指定是循环呈现的。

3.3委托\代理、移交\转交

委托是一种运行期间的智能选人模式。对于一个人工任务,在当前活动的参与者出差或休假期间,会用到委托功能。针对委托目前形成的方案是委托模块(委托表除用作工作流选人外,还可能用作),委托表结构如下:

委托人在休假期间新建一个委托,标识委托人、委托时间以及委托的流程(有一些重要流程可能不允许委托)。在每次审批人选出后需要遍历委托表中的有效数据,如当前审批人存在有效委托,则将审批人替换为被委托人,显示名为“被委托人(委托人代理)”,向委托人和被委托人均发送邮件,同时在我的工作中增加“代理工作”模块,将我被代理审批的工作展示出来。

另外的一个委托实现方案来自《流程的永恒之道》一书,即通过另外一个工作流请假单或工作代理单的方式将代理数据写入。代理数据等同于上文中的委托数据。

关于移交\转交:在当前审批者可以使用工作流功能,但由于职责变化或其他原因,将本任务直接转与其他用户。移交\转交功能是下文强行更改审批人的特例。

某用户将属于自己的且正在执行的任务移交给其他人执行。移交模式与委托模式的区别在于,委托是在事前,而移交模式是在事后。

3.4加签

  对于会签节点,存在这样一种可能,会签的目前某参与者或者管理员认为有必要临时将某用户加入会签人列表并为其下发会签任务。加签的实现方案即是:将当前子任务的父任务取出,基于父任务生成一个新的子任务,然后将该子任务分配给该临时用户,并发送提醒邮件。

3.5强行更改审批人

用于某些原因(如当前审批人离职或休假),在运行期间需要将某审批单改分配给另外的用户。逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  /** 
     
      * @author chao.gao
      * @date 2014-12-26 上午9:43:40
      * @see com.gaochao.oa.module.bpm.workflow.api.server.service.IProcessActivityService#changePeople(com.gaochao.oa.module.bpm.workflow.api.shared.dto.FlowRunTime)
      * @param flowRunTime
      * @return
      */
     @Override
     public  boolean  changePeople(FlowRunTime flowRunTime,TaskVo task) {
         if (StringUtils.isNotEmpty(flowRunTime.getCurrentUser())){
             ProcessExecutionEntity processExecutionEntity = processExecutionService.queryProcessExecutionByPkValue(flowRunTime.getPkValue());
             
             List<Task> list = jbpmOperatorService.getTasksByProcessInstanceId(processExecutionEntity.getProcessInstanceId()); 
             processExecutionService.changePeople(flowRunTime);         
 
             MailSettingEntity mailSetting = mailSettingService.queryMailSetting();              
 
             Map<String, List<MailblackwhitelistEntity>> mailBlackWhiteListMap = mailblackwhitelistService.queryMailBlackWhiteList();
             //设置流程处理ID
             flowRunTime.setProcessExecutionId(processExecutionEntity.getId());
             
             for ( int  i= 0 ;i<list.size();i++){
                 Task task1 = list.get(i);
                 task.setTaskId(task1.getId());
                 processTaskService.changePeople(task);
                 //设置任务Id
                 flowRunTime.setNextTaskId(task1.getId());
                 //经办人
                 final  String assign = flowRunTime.getCurrentUser();
                 jbpmOperatorService.assignTask(task1.getId(), assign);
                         
                 //发送邮件
                 EmailUtil.sendMail2Proxy(assign, flowRunTime, mailSetting, mailBlackWhiteListMap);
             }
         } else {
             throw  new  ProcessActivityException( "没有选择人员" );
         }
         
         return  true ;
     }







     本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1614081,如需转载请自行联系原作者



相关文章
|
6月前
22activiti - 流程管理定义(查询流程状态)
22activiti - 流程管理定义(查询流程状态)
52 0
|
测试技术 项目管理 前端开发
互联网项目管理流程(SOP)总结
无规矩不成方圆。 项目角色 产品经理(PM) 后台开发(RD) 前端开发(FE) 系统测试(QA) 项目周期 主要的环节包括 :需求评审、项目开发、提测、系统测试、发布上线等 序号 环节 主R(responsible) S(support) ...
4702 0
|
3天前
|
JavaScript Java 测试技术
基于Java的物流配送人员车辆调度管理系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的物流配送人员车辆调度管理系统的设计与实现(源码+lw+部署文档+讲解等)
9 0
|
6月前
|
监控
【技术小组日常管理工作、进度检验与管理、任务分配的工作经验之谈】
【技术小组日常管理工作、进度检验与管理、任务分配的工作经验之谈】
|
3月前
|
监控 测试技术 调度
测试管理流程有哪些?
测试管理流程有哪些?
|
10月前
CMMI流程规范—项目风险管理流程及输出物
CMMI流程规范—项目风险管理流程及输出物
181 0
|
8月前
使用的是哪个版本的机器人流程自动化(RPA)
使用的是哪个版本的机器人流程自动化(RPA)
56 1
|
人工智能 运维 JavaScript
RPA-机器人流程自动化
RPA-机器人流程自动化讲解RPA基础知识、技术框架和功能、工具应用、软件机器人的创建和实施等内容。
1346 0
RPA-机器人流程自动化
|
Java 数据库连接
JBPM学习(一):实现一个简单的工作流例子全过程
本文主要讲实现一个简单的工作流例子全过程
281 0
JBPM学习(一):实现一个简单的工作流例子全过程
|
存储 API 数据库
工作流中的流程追溯!Activiti框架的历史组件详细解析
本篇文章详细说明了工作流Activiti框架中的历史组件,这个组件用于捕获发生在进程执行中的信息并对这些信息进行永久保存,在流程实例运行完成后,这些数据依旧保存在数据库中。工作流Activiti框架中提供了对历史数据的查询方法。最后介绍了历史组件的相关配置以及使用历史组件中的数据进行流程审计。通过对历史组件的学习,可以方便地对工作流的流程进行追溯。
520 0
工作流中的流程追溯!Activiti框架的历史组件详细解析