flowable表單引擎作為一個(gè)獨(dú)立的模塊辆雾,也包括表單定義,部署等過程月劈。
1. API及與流程引擎的結(jié)合
如下圖所示度迂,表單引擎也有一個(gè)獨(dú)立的配置文件,配置類猜揪,配置引擎及三個(gè)Service.
在實(shí)際使用中我們也是將其配置嵌入到流程引擎的配置中惭墓,配置形式如下(省略了非相關(guān)配置):
<bean id="processEngineConfiguration"
class="org.flowable.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="databaseSchemaUpdate" value="true"/>
<property name="asyncExecutorActivate" value="false" />
<property name="configurators">
<list>
<bean class="org.flowable.form.engine.configurator.FormEngineConfigurator">
<property name="formEngineConfiguration">
<bean class="org.flowable.form.engine.impl.cfg.StandaloneFormEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</property>
</bean>
</list>
</property>
</bean>
<bean id="formRepositoryService" factory-bean="processEngine" factory-method="getFormEngineRepositoryService" />
在應(yīng)用重啟時(shí)數(shù)據(jù)庫中會(huì)增加act_fo_為前綴的六張數(shù)據(jù)庫表格:
act_fo_databasechangelog
: Liquibase用來跟蹤數(shù)據(jù)庫變量的
act_fo_databasechangeloglock
: Liquibase用來保證同一時(shí)刻只有一個(gè)Liquibase實(shí)例在運(yùn)行
act_fo_form_definition
:存儲(chǔ)表單定義的信息
act_fo_form_instance
:存儲(chǔ)用戶填充后表單實(shí)例信息
act_fo_form_deployment
:存儲(chǔ)表單部署元數(shù)據(jù)
act_fo_form_resource
:存儲(chǔ)表單定義的資源
2. 表單的定義
表單定義文件是以.form為后綴, 內(nèi)容格式為Json格式而姐。如下示例所示腊凶。
{
"key": "form1",
"name": "My first form",
"fields": [
{
"id": "input1",
"name": "Input1",
"type": "text",
"required": false,
"placeholder": "empty"
}
],
"outcomes": [
{
"id": "null",
"name": "Accept"
},
{
"id": "null",
"name": "Reject"
}
]
}
該文件的key屬性是其唯一性標(biāo)識(shí),表單引擎可以通過該key獲取到它毅人, 同時(shí)數(shù)據(jù)庫對(duì)相同key會(huì)維護(hù)不同的版本吭狡。第二部分是表單字段數(shù)組,第三部分是表單結(jié)果丈莺。每一個(gè)表單字段都有id,name和type屬性划煮。id屬性在同一個(gè)表單定義文件中必須唯一,當(dāng)用戶賦值時(shí)它會(huì)作為變量的名稱缔俄,在上例中弛秋,也就是會(huì)創(chuàng)建名稱為input1的變量,值由用戶填入俐载。同時(shí)表單結(jié)果也會(huì)以form_<form-identifier>_outcome
獲取得到蟹略,對(duì)于上例,用戶選擇的結(jié)果會(huì)賦值給form_form1_outcome
遏佣, 我們可以通過${form_form1_outcome == "Accept"}
表達(dá)式來驗(yàn)證表單結(jié)果是否為Accept挖炬。
表單字段的type屬性支持text, multi-line-text,integer,boolean,date等多種類型。
3. 表單的使用
首先在src/main/resources
文件夾下創(chuàng)建test.form
表單定義文件状婶,內(nèi)容如下:
{
"key": "form1",
"name": "請(qǐng)假流程",
"fields": [
{
"id": "startTime",
"name": "開始時(shí)間",
"type": "date",
"required": true,
"placeholder": "empty"
},
{
"id": "endTime",
"name": "結(jié)束時(shí)間",
"type": "date",
"required": true,
"placeholder": "empty"
},
{
"id": "reason",
"name": "請(qǐng)假原因",
"type": "text",
"required": true,
"placeholder": "empty"
}
]
}
然后創(chuàng)建test-form.bpmn20.xml
流程定義文件:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="holidayRequest" name="Holiday Request" isExecutable="true">
<startEvent id="startEvent" flowable:formKey="form1"/>
<sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>
<userTask id="approveTask" name="Approve or reject request" flowable:formKey="form1" flowable:candidateGroups="managers"/>
<sequenceFlow sourceRef="approveTask" targetRef="holidayApprovedTask"/>
<userTask id="holidayApprovedTask" name="Holiday approved" flowable:formKey="form1" flowable:assignee="employee"/>
<sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>
<endEvent id="approveEnd"/>
</process>
</definitions>
上面的開始事件和兩個(gè)用戶任務(wù)都帶有flowable:formKey
屬性意敛。
然后就可以創(chuàng)建測(cè)試類了,注意用runtimeService.startProcessInstanceWithForm
方法啟動(dòng)帶表單的流程膛虫,runtimeService.getStartFormModel
查詢流程啟動(dòng)時(shí)的表單信息; taskService.completeTaskWithForm
填充表單完成任務(wù)草姻,taskService.getTaskFormModel
查詢?nèi)蝿?wù)表單信息。具體測(cè)試類代碼如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class FormTest {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private FormRepositoryService formRepositoryService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 流程以及表單的部署
*/
@Test
public void deployTest(){
Deployment deployment = repositoryService.createDeployment()
.name("表單流程")
.addClasspathResource("flowable/test-form.bpmn20.xml")
.deploy();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().
deploymentId(deployment.getId())
.singleResult();
String processDefinitionId = processDefinition.getId();
FormDeployment formDeployment = formRepositoryService.createDeployment()
.name("definition-one")
.addClasspathResource("flowable/test.form")
.parentDeploymentId(deployment.getId())
.deploy();
FormDefinition formDefinition = formRepositoryService.createFormDefinitionQuery().deploymentId(formDeployment.getId()).singleResult();
String formDefinitionId = formDefinition.getId();
//啟動(dòng)實(shí)例并且設(shè)置表單的值
String outcome = "shareniu";
Map<String, Object> formProperties;
formProperties = new HashMap<>();
formProperties.put("reason", "家里有事");
formProperties.put("startTime", Dates.format(new Date(), Dates.Pattern.DATE));
formProperties.put("endTime", Dates.format(new Date(), Dates.Pattern.DATE));
String processInstanceName = "shareniu";
runtimeService.startProcessInstanceWithForm(processDefinitionId, outcome, formProperties, processInstanceName);
HistoricProcessInstanceEntity historicProcessInstanceEntity = (HistoricProcessInstanceEntity )historyService.createHistoricProcessInstanceQuery()
.processDefinitionId(processDefinitionId)
.singleResult();
String processInstanceId = historicProcessInstanceEntity.getProcessInstanceId();
//查詢表單信息
FormModel fm = runtimeService.getStartFormModel(processDefinitionId, processInstanceId);
System.out.println(fm.getId());
System.out.println(fm.getKey());
System.out.println(fm.getName());
System.out.println(fm.getOutcomeVariableName());
System.err.println(fm.getVersion());
List<FormField> fields = fm.getFields();
for (FormField ff : fields) {
System.out.println("######################");
System.out.println(ff.getId());
System.out.println(ff.getName());
System.out.println(ff.getType());
System.out.println(ff.getPlaceholder());
System.out.println(ff.getValue());
System.out.println("######################");
}
//查詢個(gè)人任務(wù)并填寫表單
Map<String, Object> formProperties2 = new HashMap<>();
formProperties2.put("reason", "家里有事2222");
formProperties2.put("startTime", Dates.format(new Date(), Dates.Pattern.DATE));
formProperties2.put("endTime", Dates.format(new Date(), Dates.Pattern.DATE));
formProperties2.put("days", "3");
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
String taskId = task.getId();
String outcome2="牛哥";
taskService.completeTaskWithForm(taskId, formDefinitionId, outcome2, formProperties2);
//獲取個(gè)人任務(wù)表單
FormModel taskFM = taskService.getTaskFormModel(taskId);
}
}