`
pangwu86
  • 浏览: 117763 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

使用Nutz+ExtJS+JBPM4.4实现会签

 
阅读更多

 

会签的例子依然采用Nutz+ExtJS+JBPM来实现。

 

这里只讲讲会签的实现,其他细节可以参考这篇文章http://pangwu86.iteye.com/blog/1114082

 

#######################邪恶的分割线#######################

 

首先介绍下什么是会签

 

会签

 

会签是撰拟公文的过程中,主办单位主动与有关单位协商并核签的一种办文程序,一般当公文的内容涉及本单位的多个部门或与其他单位有关时,需要进行会签。会签根据对象的不同分为内部会签和外部会签。内部会签用于与本单位内部的各有关部门进行协商并核签;外部会签用于与外单位的有关部门进行协商并核签;二者的性质相同,但处理形式不同。

在管理系统中的会签流程,例如公司职员离职、大学生毕业离校都要在不同的部门去签字确认,这里去哪个部门签字没有顺序之分,但所有部门签字完毕后才可以离职或离校。

 

 

 

会签的情况会有很多中,根据复杂程度,一般可以分为单步会签(只有一个活动处理会签任务),以及多步会签(由多个任务组成的)

 

这里只介绍下常见的,也是业务中最常遇到的单步会签。

 

 

单步会签常见有4种情况:

 

 

  1. 一票否决制——参加会签的用户中任何一个人不同意,会签活动就会结束,进去会签否决,全部同意,则进入会签通过
  2. 一票通过制——与一票否决完全相反
  3. 按比例通过——等全部参加会签的用户提交任务后,根据会签意见,按照比例(比如少数服从多数)决定下面的转移
  4. 意见收集制——等全部参加会签的用户提交完意见后(这里就是一个收集意见的作用而已),会签结束,进入下一个节点

 

 

这里要说一下,在查找会签的资料时,yy269兄的http://yy629.iteye.com/blog/660701phoenix.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;
	}

}

 

最后说一下,在会签时,要做的一些处理,这里只给思路,源码就不放了。

 

  1. 根据当前任务(子任务)获得主任务
  2. 调用CountersignInfo的sign方法,进行会签,然后完成子任务。
  3. 调用CountersignInfo.getConclusion方法,判断是否会签结束
  4. 如果会签结束,完成主任务,流程继续向下流转
4
4
分享到:
评论
9 楼 kk63643813 2014-07-24  
代码很经典,楼主你很强,代码思路也很好,求源码,谢谢171460127@qq.com
8 楼 lf84730258 2011-09-20  
pangwu86 写道
lf84730258 写道
代码很经典,楼主你很强,代码思路也很好.可是要想模拟你的代码了解JBPM和会签还是有难度.回个贴谢了.

恩,没明白你想问什么问题

我参考了你的代码,之后在子流程提交这块我想获得一个监听提交的时候始终是失败的.
7 楼 lwei20000 2011-09-14  
您好,最近正好做关于会签的工作,您可以把上面的完整代码发一份给我吗?
由于缺少完整代码,对于刚开始写会签的代码比较困难。谢谢

我的邮箱是lwei20000@163.com

上面的代码看了,但是一些个工厂类,工具类啥的没有,所以看不太清。
6 楼 pangwu86 2011-09-06  
lf84730258 写道
代码很经典,楼主你很强,代码思路也很好.可是要想模拟你的代码了解JBPM和会签还是有难度.回个贴谢了.

恩,没明白你想问什么问题
5 楼 pangwu86 2011-09-06  
zfh_1985 写道
同楼上的问,正好在做这一块,希望有机会交流一下,非常感谢。

请看4楼
4 楼 pangwu86 2011-09-06  
石头城 写道
LZ,非常感谢分享。还是有点不太明白,现在主要是关于如何从子类得到父类task,
还有LZ在CountersignAssignment类中
 private List<String> getUsers() {  
        return CountersignAction.users;  
    } 
的CountersignAction是怎么得到的
希望解惑,非常感谢!


这里的getUsers()方法的意思是你可以根据你的系统特点,通过各种方式,拿到这个用户列表就好了
CountersignAction.users 实际上就是ME在CountersignAction这个类中定义的一个String列表
例如 List<String> users = new ArrayList<String>{'user1','user2'};
3 楼 lf84730258 2011-08-30  
代码很经典,楼主你很强,代码思路也很好.可是要想模拟你的代码了解JBPM和会签还是有难度.回个贴谢了.
2 楼 zfh_1985 2011-08-27  
同楼上的问,正好在做这一块,希望有机会交流一下,非常感谢。
1 楼 石头城 2011-08-15  
LZ,非常感谢分享。还是有点不太明白,现在主要是关于如何从子类得到父类task,
还有LZ在CountersignAssignment类中
 private List<String> getUsers() {  
        return CountersignAction.users;  
    } 
的CountersignAction是怎么得到的
希望解惑,非常感谢!

相关推荐

    Nutz+ExtJS示例教程——后台Service实现

    **Nutz+ExtJS示例教程——后台Service实现** Nutz和ExtJS是两种不同的技术栈,它们在Web开发中有着各自的应用。Nutz是一款基于Java的轻量级框架,它提供了全面的Web开发解决方案,包括ORM、AOP、IOC等。而ExtJS则是...

    企业级应用项目,springmvc+nutz+redis+rabbitmq+quartz+shiro

    在本项目中,"企业级应用项目,springmvc+nutz+redis+rabbitmq+quartz+shiro",开发者采用了一系列高级技术构建了一个具备高可扩展性和低耦合度的系统,旨在提供一个适用于有一定Java基础的学习者进行实践和进阶的...

    国内技术强强联手之Nutz+KindEditor+LHGDialog+My97DatePicker

    标题中的“国内技术强强联手之Nutz+KindEditor+LHGDialog+My97DatePicker”揭示了四个关键的IT技术组件,它们在中国的技术社区中被广泛使用,并且经常一起集成到项目中以提供强大的功能。现在,我们将深入探讨这些...

    基于Nutz与ExtJs的快速开发

    4. **Nutz与ExtJs的整合**:讲解如何将Nutz作为后台服务,与ExtJs前端进行有效地通信,实现数据的动态加载和界面的实时更新。 5. **实战案例**:可能包含一些实际项目中的应用场景,如创建一个CRUD(创建、读取、...

    springboot+nutz+beetl整合工程

    在本项目中,`dao层`的实现将基于Nutz,通过其提供的注解和API来实现对数据库的操作,如增删改查等。 再来看Beetl,这是一款高性能的Java模板引擎,它的设计目标是提高开发效率,减少模板代码。Beetl语法简洁,支持...

    basecms(nutz+easyui)

    【basecms(nutz+easyui)】是一款基于Nutz框架和EasyUI前端库构建的CMS(内容管理系统)项目。此系统充分利用了EasyUI的组件化特性,为用户提供了一个直观、简洁且功能丰富的管理界面,而Nutz作为后端开发框架,确保...

    dbtoword.zip-springboot+POI+nutz+mysql导出word模板文件

    每次项目验收写文档是一个很凌乱的事情,作为一个程序员,应该是用技术解决问题。当然有很多工具也可以实现。比如PDman就可以导出word或者pdf文档。 这个案例主要是学习springboot...以及使用nutz poi第三方工具的使用

    SpringMVC+Nutz框架介绍.docx

    SpringMVC+Nutz框架介绍.docxSpringMVC+Nutz框架介绍.docxSpringMVC+Nutz框架介绍.docxSpringMVC+Nutz框架介绍.docxSpringMVC+Nutz框架介绍.docxSpringMVC+Nutz框架介绍.docxSpringMVC+Nutz框架介绍.docxSpringMVC+...

    SpringMVC+Nutz框架介绍.pdf

    SpringMVC+Nutz框架介绍.pdfSpringMVC+Nutz框架介绍.pdfSpringMVC+Nutz框架介绍.pdfSpringMVC+Nutz框架介绍.pdfSpringMVC+Nutz框架介绍.pdfSpringMVC+Nutz框架介绍.pdfSpringMVC+Nutz框架介绍.pdfSpringMVC+Nutz框架...

    数据表自动生成类代码

    可以自动生成数据库脚本,生成代码项目,数据库文档生成

    Java框架Nutz的基于jCasbin的权限管理插件.zip

    在Nutz+jCasbin的环境中,你可以根据业务需求定义各种条件,如用户属性、时间、地点等,来决定用户是否能执行特定操作,增强了权限控制的灵活性和适应性。 3. **策略定义**:jCasbin使用一种声明式的策略语言,称为...

    NutzWk是基于Nutz的Java开源企业级开发框架.rar

    NutzWk 2.0.x 试验版(不建议使用) NutzWk 1.0.x 传统版(velocity 支持IE6) 注:_velocity后缀为同时支持beetl和velocity两种模板引擎版本 QQ交流群: 68428921 在线演示地址 ====== 基于Nutz的开源企业级开发...

    nutz_redis集成依赖包

    7. **使用Nutz Redis API**:集成完成后,就可以在代码中使用Nutz Redis提供的API进行数据操作,例如: ```java import org.nutz.dao.Dao; import org.nutz.integration.jedis.JedisHolder; public class ...

    nutz 使用手册 nutz-1.a.33-manual.pdf

    ### Nutz 使用手册知识点概述 #### 一、Nutz 框架简介 - **背景与定位**:Nutz 是一款旨在提高 Java Web 开发效率的轻量级框架集。随着脚本语言在 Web 开发领域的兴起,Java 开发者面临开发效率低下等问题。Nutz ...

    nutz框架使用手册.zip

    在"nutz框架使用手册.zip"中,我们可以预期找到关于Nutz框架全面的学习资源,从基础概念到高级用法,帮助开发者从零开始掌握Nutz平台的开发技能。手册可能包含以下主要知识点: 1. **Nutz IOC**:Nutz IOC是Nutz...

    nutz代码生成器

    DAO接口则包含了如`selectById`、`insert`、`updateById`、`deleteById`等常见操作,这些方法的实现是基于Nutz的Ioc(依赖注入)和Aop(面向切面编程)功能,使得代码简洁而高效。 Nutz Codematic的压缩包文件"**...

    ztree与nutz简单使用

    《ztree与nutz在实际应用中的简单使用详解》 在IT行业中,高效地管理和操作数据是至关重要的。zTree和Nutz都是在这方面提供强大支持的工具。zTree是一款基于JavaScript的灵活、强大的树状菜单插件,而Nutz则是一个...

    使用Nutz[1.b.38]对数据库表的CRUD操作

    在本教程中,我们将深入探讨如何使用Nutz 1.b.38版本进行数据库表的CRUD操作。 首先,Nutz的核心组件Nutz DAO(Data Access Object)提供了与数据库交互的能力。通过简单的API,开发者可以轻松地实现对数据库的读写...

Global site tag (gtag.js) - Google Analytics