?
会签的例子依然采用Nutz+ExtJS+JBPM来实现。
?
这里只讲讲会签的实现,其他细节可以参考这篇文章http://pangwu86.iteye.com/blog/1114082
?
#######################邪恶的分割线#######################
?
首先介绍下什么是会签
?
会签
?
会签是撰拟公文的过程中,主办单位主动与有关单位协商并核签的一种办文程序,一般当公文的内容涉及本单位的多个部门或与其他单位有关时,需要进行会签。会签根据对象的不同分为内部会签和外部会签。内部会签用于与本单位内部的各有关部门进行协商并核签;外部会签用于与外单位的有关部门进行协商并核签;二者的性质相同,但处理形式不同。
在管理系统中的会签流程,例如公司职员离职、大学生毕业离校都要在不同的部门去签字确认,这里去哪个部门签字没有顺序之分,但所有部门签字完毕后才可以离职或离校。
?
?
?
会签的情况会有很多中,根据复杂程度,一般可以分为单步会签(只有一个活动处理会签任务),以及多步会签(由多个任务组成的)
?
这里只介绍下常见的,也是业务中最常遇到的单步会签。
?
?
单步会签常见有4种情况:
?
?
- 一票否决制――参加会签的用户中任何一个人不同意,会签活动就会结束,进去会签否决,全部同意,则进入会签通过
- 一票通过制――与一票否决完全相反
- 按比例通过――等全部参加会签的用户提交任务后,根据会签意见,按照比例(比如少数服从多数)决定下面的转移
- 意见收集制――等全部参加会签的用户提交完意见后(这里就是一个收集意见的作用而已),会签结束,进入下一个节点
?
?
这里要说一下,在查找会签的资料时,yy269兄的http://yy629.iteye.com/blog/660701与phoenix.clt兄的http://phoenix-clt.iteye.com/blog/428242这两篇文章给了很大的启发,后面的实现也借鉴了他们的一些好的思想,需要的朋友可以去看一看。
?
?
下面的部分将会讲述如何实现一个动态会签(会签人数,人员,会签规则都可以自由设定)
?
好,接下来看一下今天举得这个申请经费的例子:
?
贴一下jpdl.xml文件
?
<?xml version="1.0" encoding="UTF-8"?> <process name="jingfeishenqing" xmlns="http://jbpm.org/4.4/jpdl"> <description><![CDATA[ 经费申请,大于30万需要老总们会签(采用一票否决制) ]]></description> <!-- 会签决策实现类 --> <variable name="calc.countersignCalculatorImpl" type="string"> <string value="org.nutz.jbpm.countersign.impl.AllAgreeSign" /> </variable> <!-- 以下三个参数只有按百分比策略才会用到,可以根据需要来设定 --> <!-- 最小同意数 --> <variable name="calc.minAgreeSize" type="int"> <int value="2"/> </variable> <!-- 最小同意比例(请设定一个在0-1之间的数字) --> <variable name="calc.minAgreePercent" type="float"> <float value="0.6"/> </variable> <!-- 是否使用按比例(true为按照比例,false为按照人数) --> <variable name="calc.userPercent" type="boolean"> <false/> </variable> <start g="81,8,48,48" name="start1"> <transition g="-60,13" name="申请经费" to="申请经费" /> </start> <task assignee="${employee}" form="countersign/applyfor.jsp" g="304,65,92,52" name="申请经费"> <transition name="金额判断" to="判断1" g="-69,-22" /> </task> <decision expr="#{money > 300000 ? '大于30万' : '小于等于30万'}" g="190,144,48,48" name="判断1"> <transition g="-39,14" name="大于30万" to="领导会签" /> <transition g="-80,-21" name="小于等于30万" to="财务部拨款" /> </decision> <task form="countersign/countersign.jsp" g="302,183,92,52" name="领导会签"> <assignment-handler class="org.nutz.jbpm.countersign.CountersignAssignment"> </assignment-handler> <transition g="-79,-11" name="会签结果判断" to="会签判断" /> </task> <decision expr="#{agree == true ? '同意' : '不同意'}" g="325,293,48,48" name="会签判断"> <transition g="-13,-26" name="同意" to="财务部拨款" /> <transition g="-50,-22" name="不同意" to="申请驳回" /> </decision> <task g="68,291,92,52" name="财务部拨款"> <transition g="-33,-10" name="拨款" to="拨款完毕" /> </task> <end g="91,400,48,48" name="拨款完毕" /> <end g="327,398,48,48" name="申请驳回" /> </process>?
可以说会签的难点就是如何在一个任务中,实现由多个人去完成。
?
JBPM4中提供了建立子任务的API,这一点就提供了一个思路.
?
那就是在进入这个任务后,给相应的会签人员建立对应的子任务,然后等待他们完成子任务后,该会签任务设定为完成,流程向下流转。
?
下面这段配置就是会签任务,可以看到,与普通task不同的是,添加一个assignment-handler属性,同时指定了一个java类
?
?
<task form="countersign/countersign.jsp" g="302,183,92,52" name="领导会签"> <assignment-handler class="org.nutz.jbpm.countersign.CountersignAssignment"> </assignment-handler> <transition g="-79,-11" name="会签结果判断" to="会签判断" /> </task>
?
这里的作用就是,在流程进入到这个节点时,会执行你指定的这个类
?
这个类的要求是,实现AssignmentHandler接口
?
?
public class CountersignAssignment implements AssignmentHandler
?
查看这个接口,会发现需呀实现一个assign方法
?
/** interface to delegate {@link Task} or {@link Swimlane} assignment. * * @author Tom Baeyens */ public interface AssignmentHandler extends Serializable { /** sets the actorId and candidates for the given task. */ void assign(Assignable assignable, OpenExecution execution) throws Exception; }
?
下面给出ME的一个实现:
?
public class CountersignAssignment implements AssignmentHandler { public void assign(Assignable assignable, OpenExecution execution) throws Exception { // 获得实例ID String pid = execution.getProcessInstance().getId(); // 获得当前的主任务 Task task = JbpmUtil.taskService.createTaskQuery().processInstanceId(pid) .activityName(execution.getName()).uniqueResult(); // 创建子任务 createSubTasks(task); // 获得会签决策(通过jpdl配置文件中配置的属性) String className = (String) JbpmUtil.taskService.getVariable(task.getId(), "calc.countersignCalculatorImpl"); // 通过会签决策工厂生产获得决策对象 CountersignCalculator calculator = CountersignCalculatorFactory .makeCountersignCalculator(className); // 如果是按比例通过的话,需要设定通过人数,或者比例 SetCalculatorVars(calculator, task.getId()); // TODO 这里用户采用一个模拟的方式,以后可以通过要整合的系统提供的API 获得用户名字列表 CountersignInfo countersignInfo = CountersignInfoFactory.makeCountersignInfo(getUsers(), calculator); // 记录当前的主任务与会签信息 Map<String, Object> vars = new HashMap<String, Object>(); vars.put("parentTaskId", task.getId()); vars.put("countersignInfo", countersignInfo); JbpmUtil.taskService.setVariables(task.getId(), vars); return; } private void SetCalculatorVars(CountersignCalculator calculator, String taskId) { // 只有按比例会签决策需要设定参数 if (calculator instanceof PercentAgreeSign) { // 获取参数 Object minAgreeSize = JbpmUtil.taskService.getVariable(taskId, "calc.minAgreeSize"); Object minAgreePercent = JbpmUtil.taskService.getVariable(taskId, "calc.minAgreePercent"); Object userPercent = JbpmUtil.taskService.getVariable(taskId, "calc.userPercent"); // 设定参数 PercentAgreeSign calc = (PercentAgreeSign) calculator; if (null != minAgreeSize) { calc.setMinAgreeSize((Integer) minAgreeSize); } if (null != minAgreePercent) { calc.setMinAgreePrecent((Float) minAgreePercent); } if (null != userPercent) { calc.setUserPercent((Boolean) userPercent); } } } private void createSubTasks(Task task) { // OpenTask才有createSubTask方法,Task接口是没有的 OpenTask oTask = (OpenTask) task; // 这个对象非常重要,没有它,通过子任务无法跟主任务获得联系 Execution execution = JbpmUtil.executionService.findExecutionById(task.getExecutionId()); // 获得所有的参与者 for (String assignee : getUsers()) { TaskImpl subTask = (TaskImpl) oTask.createSubTask(); subTask.setAssignee(assignee); subTask.setName(task.getName()); subTask.setFormResourceName(task.getFormResourceName()); // 这句话是关键 只有设定同样的实例 子任务才能获得主任务设定的变量 subTask.setExecution((ExecutionImpl) execution); JbpmUtil.taskService.addTaskParticipatingUser(task.getId(), assignee, Participation.CLIENT); } } private List<String> getUsers() { return CountersignAction.users; } }?
代码中注释解释的很详细了,这里就不再复述了,这段代码相关参数,可以参照上面的jpdl.xml中的变量设置。
?
下面介绍下上面代码中的CountersignInfo类与CountersignCalculator类
?
CountersignInfo
?
?
/** * 会签信息 * */ public interface CountersignInfo extends Serializable { /** * 用户下达会签结论 * * @param user * @param conclusion * @return */ public boolean sign(String user, Conclusion conclusion); /** * 获取会签人员列表 * * @return */ public List<String> getUsers(); /** * 获取会签会议结论 * * @return */ public Conclusion getConclusion(); /** * 是否全部人员都已签完 * * @return */ public boolean isAllSigned(); /** * 获取特定用户的会签结论 * * @param userId * @return */ public Conclusion getUserConclusion(String userId); /** * 获取所有用户的会签结论 * * @return */ public Map<String, Conclusion> getConclusions(); /** * 获取会签结论计算方式 * * @return */ public CountersignCalculator getConclusionCalculator(); }
?
可以通过这个API获得当前会签的所有相关信息。
?
CountersignCalculator
?
?
/** * 会签决策 * */ public interface CountersignCalculator extends Serializable { /** * 计算会签结果 * * @param info * @return */ public Conclusion calculate(CountersignInfo info); }
?
这里就只有一个方法,就是通过会签信息,计算出当前的会签结果,这个接口的实现类,你就可以在里面根据业务来进行实现,比如说实现上面提到的单步会签的常见的四种情况(后面会给出实现类)。
?
还有一个会签结果的枚举类,这个可以根据你的需要自己定制,这里给出ME的方案
?
Conclusion
?
?
/** * 会议结论定义 * */ public enum Conclusion implements Serializable { // 通过 AGREE, // 否决 DENY, // 弃权 ABSTAIN, // 继续(会签没有结束) CONTINUE; public static Conclusion getConclusion(String conclusion) { if (AGREE.toString().equals(conclusion)) { return Conclusion.AGREE; } else if (DENY.toString().equals(conclusion)) { return Conclusion.DENY; } else if (ABSTAIN.toString().equals(conclusion)) { return Conclusion.ABSTAIN; } else if (CONTINUE.toString().equals(conclusion)) { return Conclusion.CONTINUE; } return Conclusion.AGREE; } }
?
下面给出几个实现类,给大家做个参考:
?
首先是会签决策实现类:
?
一票否决制
?
?
/** * 一票否决制(全票通过制) * */ public class AllAgreeSign implements CountersignCalculator { public Conclusion calculate(CountersignInfo info) { // 是否有否决票 for (Entry<String, Conclusion> entry : info.getConclusions().entrySet()) { if (entry.getValue() == Conclusion.DENY) { // 一旦出现否决票,立刻作为否决处理 return Conclusion.DENY; } } if (info.isAllSigned()) { // 所有票投完了 return Conclusion.AGREE; } return Conclusion.CONTINUE; } public String toString() { return "采用一票否决制,只要有一票否决,则会签不通过。"; } }?
同理大家可以如法炮制一票通过制。
?
至于按比例,按人数是如何实现的,这里就不给出例子了,留给大家留一个思考的空间。
?
下面是会签信息的实现类:
?
默认会签信息实现
?
?
public class CountersignInfoDefaultImpl implements CountersignInfo { private List<String> users; private boolean isAllSigned = false; private Map<String, Conclusion> results = new HashMap<String, Conclusion>(); private CountersignCalculator countersignCalculator; public CountersignInfoDefaultImpl(List<String> users, CountersignCalculator countersignCalculator) { this.users = users; this.countersignCalculator = countersignCalculator; } /** * 用户提交决策 * * @param user * @param conclusion * @return */ public synchronized boolean sign(String user, Conclusion conclusion) { if (!users.contains(user)) { return false; } results.put(user, conclusion); if (results.size() == users.size()) { isAllSigned = true; } return true; } // *********接口实现*********** public List<String> getUsers() { // 不允许修改 return Collections.unmodifiableList(users); } public Conclusion getConclusion() { return countersignCalculator.calculate(this); } public boolean isAllSigned() { return isAllSigned; } public Conclusion getUserConclusion(String userId) { return results.get(userId); } public Map<String, Conclusion> getConclusions() { return Collections.unmodifiableMap(results); } public CountersignCalculator getConclusionCalculator() { return countersignCalculator; } }
?
最后说一下,在会签时,要做的一些处理,这里只给思路,源码就不放了。
?
- 根据当前任务(子任务)获得主任务
- 调用CountersignInfo的sign方法,进行会签,然后完成子任务。
- 调用CountersignInfo.getConclusion方法,判断是否会签结束
- 如果会签结束,完成主任务,流程继续向下流转
还有LZ在CountersignAssignment类中
private List<String> getUsers() { return CountersignAction.users; }的CountersignAction是怎么得到的
希望解惑,非常感谢!
还有LZ在CountersignAssignment类中
private List<String> getUsers() { return CountersignAction.users; }的CountersignAction是怎么得到的
希望解惑,非常感谢!
这里的getUsers()方法的意思是你可以根据你的系统特点,通过各种方式,拿到这个用户列表就好了
CountersignAction.users 实际上就是ME在CountersignAction这个类中定义的一个String列表
例如 List<String> users = new ArrayList<String>{'user1','user2'};
请看4楼
恩,没明白你想问什么问题
由于缺少完整代码,对于刚开始写会签的代码比较困难。谢谢
我的邮箱是lwei20000@163.com
上面的代码看了,但是一些个工厂类,工具类啥的没有,所以看不太清。
恩,没明白你想问什么问题
我参考了你的代码,之后在子流程提交这块我想获得一个监听提交的时候始终是失败的.