UFLO 2 : 18.2.设计报销流程模版

       报销流程模版如下图:

       报销人员填写完报销单,并提交给部门经理审批,部门经理审批通过后,再由部门经理指定二级部门经理进行进一步审批,然后,根据报销金额的大小自动决策,报销金额大约1000,由总经理审批,否则由财务一部和财务二部并发审批,最后审批结束。
其中,判断报销金额决策节点可以采用条件el表达式来实现决策功能,

       所有的任务节点的url属性配置为同一个业务表单页面,业务表单页面的开发,在后面会详细介绍。

  • 流程配置

       流程模版属性的名称建议尽量不要使用中文,流程模版分类ID同公司ID保持一致,否则将无法使用我们所部署的流程模版。
为了用户更好的了解自己提交的报销单的审批情况,报销单实体类需要添加一个标识状态的字段state,后面讲报销流程关联实体类时会详细介绍。报销审批是否通过,必须同时满足如下条件:

  1. 报销流程已经结束
  2. 所有审批节点审批都通过审批

       为了达到上述目的,我们可以通过流程事件来实现,因此,需要添加一个实现了ProcessEventHandler接口的实现类,实现类代码如下:

流程事件处理器类
@Component 
public class ReimbursementProcessEventHandler extends HibernateDao implements ProcessEventHandler { 

@Override 
public void start(ProcessInstance processInstance, Context context) { 

 } 

@Override 
public void end(ProcessInstance processInstance, Context context) { 
 Session session=this.getSessionFactory().openSession(); 
try { 
if(StringUtils.isNotEmpty(processInstance.getBusinessId())){ 
 Reimbursement reimbursement=(Reimbursement) session.load(Reimbursement.class, processInstance.getBusinessId()); 
if(State.process.equals(reimbursement.getState())){ 
 reimbursement.setState(State.pass); 
 } 
 } 

 } finally { 
 session.flush(); 
 session.close(); 
 } 

 } 

 }

       任务节点的配置       在流程结束事件的方法里,我们添加了如上代码,代码的逻辑是通过businessId获取相关实体,当各个审批节点都审批通过时,即状态为State.process,将实体的状态设置为审批通过。实体类后面会详细介绍。另外,需要将ReimbursementProcessEventHandler 注入spring容器。重启项目,接下来就可以为报销流程模版配置流程事件了。如下图所示:

       1)部门经理审批

       指定处理人的方式为指定Bean的方式,该方式需要添加一个实现AssignmentHandler接口的类,具体后台代码如下:

处理人处理器类
@Component 
public class DeptManagerAssignmentHandler implements AssignmentHandler { 

@Override 
public Collection<String> handle(TaskNode taskNode, 
 ProcessInstance processInstance, Context context) { 
 Collection<String> users=new ArrayList<String>(); 
 users.add("deptmanager"); 
return users; 
 } 

 }

       这种方式可以实现很复杂的处理人获取。部门经理审批,由申请人所在的部门经理处理,此处为了简单,代码中固定死了处理人,实际中,开发人员可以processInstance获取流程实例发起人,根据发起人获取发起人所在的部门的经理。

       2)二级部门经理审批

       指定处理人的方式为由上一任务节点处理人指定,也就是说由部门经理审批任务的处理人指定二级部门经理审批的处理人。如下图所示:

       上图页面将在后面业务表单页面那一节详细说明。

       3)财务一部审批

       指定处理人的方式为指定参与者,此方式需要添加一个实现了AssigneeProvider的实现类,并注入spring容器中,这样,在指定参与者选项中就可以看到自己定义的AssigneeProvider。接下来,我们将自定义一个AssigneeProvider,代码如下:

处理人提供者类
@Component 
public class AllUserAssigneeProvider implements AssigneeProvider { 
@Resource(name=IUserService.BEAN_ID) 
private IUserService userService; 
public boolean isTree() { 
return false; 
 } 

public String getName() { 
return "所有用户"; 
 } 

public void queryEntities(PageQuery<Entity> pageQuery, String parentId) { 
 Page<IUser> page=new Page<IUser>(pageQuery.getPageSize(),pageQuery.getPageIndex()); 
 String categoryId=EnvironmentUtils.getEnvironment().getCategoryId(); 
userService.loadPageUsers(page, categoryId, null); 
 Collection<IUser> users=page.getEntities(); 
if(users==null
users.size()==0){ 
return; 
 } 
 List<Entity> list=new ArrayList<Entity>(); 
for(IUser user:users){ 
 Entity entity=new Entity(user.getUsername(),user.getCname()); 
 list.add(entity); 
 } 
 pageQuery.setResult(list); 
 pageQuery.setRecordCount(page.getEntityCount()); 
 } 

public Collection<String> getUsers(String entityId,Context context,ProcessInstance processInstance) { 
 Collection<String> usernames=new ArrayList<String>(); 
 usernames.add(entityId); 
return usernames; 
 } 

@Override 
public boolean disable() { 
return false; 
 } 


 }

       代码说明:
       方法isTree是用来决定提供选择的数据是按二维表格来展现还是树形结构来展现,disable方法是用来控制此提供者是否可用。queryEntities是用来获取选择数据的方法,PageQuery参数是用来控制数据分页和数据的载体。parentId参数是在方法isTree返回true的情况下会使用到,通过parentId就可以取到所以对应的子节点。返回的数据都会由 com.bstek.uflo.process.assign.Entity类包裹。此类代码如下:

Entity类
public class Entity { 
private String id;//选择项标识 
private String name;//提高用户在选择的时候的可读性 
private boolean chosen=true;//控制选择项能否被选择 
public Entity(String id,String name){ 
this.id=id; 
this.name=name; 
 } 
public String getId() { 
return id; 
 } 
public void setId(String id) { 
this.id = id; 
 } 
public String getName() { 
return name; 
 } 
public void setName(String name) { 
this.name = name; 
 } 
public boolean isChosen() { 
return chosen; 
 } 
public void setChosen(boolean chosen) { 
this.chosen = chosen; 
 } 
 }

       其中,选择项,即Entity,可能包裹的是一个部门、岗位、群组或者只是一个简简单单的用户。Entity包裹的对象是什么,完全取决于开发人员给Entity对象id属性关联的值是什么。在本示例,我们的Entity对象包裹的是用户,所以Enitty的id给的是用户的关键字,即username。不管Entity包裹的对象是什么,最终需要通过getUsers方法将选择项,即Entity,影射为流程需要的用户的主键,如果Entity包裹的时部门,一般情况下,我们会把Entity影射为部门下的用户。通过Entity的id,即部门主键来获取部门下的用户。AllUserAssigneeProvider 类完成后,需要将它注入到spring容器中。然后重启项目,在流程设计器中,选择财务一部审批节点,选择指定参与者的方式,点击选择按钮,弹出如下对话框:

       从上图可以看出,我们刚才添加的提供者已经出现在列表当中,即"所有用户"。这里的名称是通过AllUserAssigneeProvider 类的如下方法提供的:

public String getName() { 
return "所有用户"; 
 }

       对于其他的提供者,是系统自带的。如果想禁用它们,可以通过在dorado-home目录下的configure.properties文件中添加相关的属性进行控制。如下图所示:

       选择"所有用户",单击"下一步",进入到选择处理人对话筐,如下图所示:

       选择"用户2",点击"完成",如下图所示

        3)财务二部审批

指定处理人的方式为EL表达式方式,EL表达式为"${assignee}",assignee为流程实例的一个变量,这个变量我们将在流程实例开启的时候设置。

  1. 总经理审批

       指定处理人方式同财务一部审批节点一样,任务类型设置为会签,如果给总经理审批指定了两个处理人,当任务到达此节点时,默认情况会产生两个会签任务,两个会签任务必须完成才能流向下一个流程节点,开发人员也可以改变会签任务节点的完成规则。如下图所示:

       在总经理审批节点的完成规则采用默认的"所用任务全部完成"。
       二级部门经理审批的处理人方式是由上一任务节点处理人指定,因此,在流程业务表单页面上需要一个按钮,通过这个按钮,部门经理审批任务处理人就可以为二级部门审批节点指定处理人。在这个示例里,流程业务表单页面是被所有任务节点所共享的,为了不让其他的节点任务看到这个按钮,在UFLO中,可以通过节点组件权限来配置,除了部门经理审批节点有此按钮外,其他节点都设置为隐藏,如下图所示:
       流程模版配置完成后,部署我们的流程模版,成功部署流程后可以在我们应用的如下格式的URL中看到部署成功的流程模版: com.bstek.uflo.console.view.ProcessMaintain.d,页面如下图:

       在这个流程监控与测试页面,可以测试新发布的流程模版,通过创建新流程按钮来开启一个流程实例,任务操作来领取、开始或者完成当前任务。需要注意的是,测试的时候需要在开启流程的时候添加两个流程实例变量money(报销金额)和assignee(财务二部审批节点处理人)。