1.概述

在使用Activiti时,对于表单的应用应该是大家讨论的最多的话题之一了。Activiti自带了两种模式的表单:内置表单和外置表单。
       内置表单在流程定义文件的XML中用activiti:formProperty属性来定义;外置表单则使用activiti:formkey="form/test.form"来定义,test.form是一份单独的表单文件。
       在流程发起时从request获取提交的表单数据,然后构建为map再通过formService.submitStartFormData(defId, map)提交表单数据到Activiti中保存。
       在实际的使用过程中,这两种模式的表单都不能很好的满足企业级表单应用的需求,例如:

  1. 表单只支持键值对的格式;
  2. 不支持在线可视化进行表单设计;
  3. 表单数据不能进行查询和统计分析;
  4. 表单数据和流程数据无法分库存储。

2.在线表单设计器

使用在线表单设计器设计时,流程与表单按照以下方式进行整合。

activiti 吧 vue 自定义表单与自定义流程 restful 接口实现 activiti集成自定义表单_自定义表单

2.1流程绑定表单

在进行流程配置时,通过在流程定义的XML中添加如下Element将表单与流程进行绑定。

<ext:globalForm>
  <ext:form formValue="bxbd" name="报销表单" 
  			parentFlowKey="local_" type="inner"/>
</ext:globalForm>
2.2表单数据的保存

       在发起流程和审批待办任务时,通过表单Key可以从数据库中获取表单的html内容从而展示为表单页面。在用户填写了表单数据以后,执行启动流程、审批任务的操作时,将表单数据以json格式提交到后台,后台将数据保存到数据库中,同时将这份数据的主键作为businessKey与该流程实例关联起来。
       通过流程启动事件的监听器来处理表单数据的保存及businessKey的关联。

@Service
@Transactional
public class BpmStartEventListener implements ApplicationListener<BpmStartEvent>, Ordered {
	@Resource
	BpmDefinitionAccessor bpmDefinitionAccessor;
	@Resource
	BpmProcessInstanceManager bpmProcessInstanceManager;
	@Resource
	BpmExeStackManager bpmExeStackManager;
	@Resource(name="defaultBpmFormService")
	BpmFormService bpmFormService;

	@Override
	public void onApplicationEvent(BpmStartEvent ev) {
		BpmStartModel model = (BpmStartModel) ev.getSource();
		//设置上下文。
		setBuinessKey(model);
		
		if (AopType.PREVIOUS.equals(model.getAopType())) {
			try {
				before(model);
			} catch (Exception e) {
				throw new WorkFlowException(e.getLocalizedMessage(), e);
			}
		} else {
			try {
				after(model);
			} catch (Exception e) {
				throw new WorkFlowException("", e);
			}
		}
	}
	
	private void before(BpmStartModel model) throws Exception {
		// 执行前置处理器
		executeHandler(model, true);
		// 添加业务表的中间数据库表
		addBusLink(model);
	}

	/**
	 * 执行节点处理器。
	 * 
	 * @param model
	 * @param isBefore
	 *            void
	 * @throws Exception 
	 */
	private void executeHandler(BpmStartModel model, boolean isBefore) throws Exception {
		BpmProcessInstance instance = model.getBpmProcessInstance();
		ActionCmd cmd = model.getCmd();
		// 获取发起节点获取不到则获取第一个节点。
		NodeProperties properties = getStartProperties(instance);
		BusDataUtil.executeHandler(properties, cmd, isBefore);
	}

	/**
	 * 添加关联数据
	 * @param model
	 * @throws Exception
	 */
	private void addBusLink(BpmStartModel model) throws Exception {
		ActionCmd cmd = model.getCmd();
		if (cmd instanceof ProcessInstCmd) {

			DefaultBpmProcessInstance inst = (DefaultBpmProcessInstance) model.getBpmProcessInstance();
			
			String dataMode=cmd.getDataMode();
			
			// 设置数据模式。
			inst.setDataMode(dataMode);
			if(StringUtil.isNotEmpty(cmd.getSysCode())){
				inst.setSysCode(cmd.getSysCode());
			}
			// bo数据处理
			if (ActionCmd.DATA_MODE_BO.equals(dataMode)) {
				BusDataUtil.handSaveBoData(model.getBpmProcessInstance(), cmd); 
			}
			// 键名 键值。
			else if (ActionCmd.DATA_MODE_PAIR.equals(dataMode)) {
				BusDataUtil.handExt(cmd);
			}
			//主键模式。
			else if (ActionCmd.DATA_MODE_PK.equals(dataMode)) {
				String pk=cmd.getBusinessKey();
				String pkInst=inst.getBizKey();
				if(StringUtil.isNotEmpty(pk) && StringUtil.isEmpty(pkInst)){
					inst.setBizKey(pk);
				}
			}
			
		}
	}

	private void after(BpmStartModel model) throws Exception {
		 
		// 执行后置处理器
		executeHandler(model, false);
		// 记录流程实例表单
		handleInstForm(model);
	}
	
	// 流程启动后, 记录每个节点的表单  pc端和手机端的表单, 当流程结束或者终止时, 删除相应记录
	private void handleInstForm(BpmStartModel model) throws Exception {
		BpmProcessInstance instance = model.getBpmProcessInstance();
		bpmFormService.handleInstForm(instance.getId(), instance.getProcDefId(),false);
	}

	@Override
	public int getOrder() {
		return 1;
	}
}
2.3审批时显示表单数据

在进行待办任务审批时,需要显示表单及表单数据,通过流程实例所关联的表单Key及businessKey分别查询表单html和对应的数据json,在前端将htmljson融合显示为审批内容。

if( FormCategory.INNER.value().equals(formModel.getType().value()) ){
	List<ObjectNode> boDatas = boDataService.getDataByInst(bpmProcessInstance);
	
	DefaultTaskFinishCmd cmd = new DefaultTaskFinishCmd();
	cmd.setVariables(this.getTaskVars(taskId,null));
	ContextThreadUtil.setActionCmd(cmd);
	// BO数据前置处理
	ObjectNode data =BoDataUtil.hanlerData(bpmProcessInstance,nodeId, boDatas);
	// BO数据
	taskDetailVo.setData(data);
	
	//获取意见
	ObjectNode opinionJson = boDataService.getFormOpinionJson(task.getProcInstId());
	taskDetailVo.setOpinionList(opinionJson);
	//流程定义的权限
	ObjectNode formRestParams=JsonUtil.getMapper().createObjectNode();
	formRestParams.put("formkey", formModel.getFormKey());
	formRestParams.put("flowKey", task.getProcDefKey());
	formRestParams.put("nodeId", formAndPermNodeId);
	formRestParams.put("parentFlowKey", topDefKey);
	String permission = formRestfulService.getPermission(formRestParams);
	taskDetailVo.setPermission(permission);
}

以上即是activiti整合自定义表单的大致思路,大家也可以访问以下地址试用EIP7系统来查看最终效果。