flowable表单引擎

flowable表单引擎作为一个独立的模块,也包括表单定义,部署等过程。

1. API及与流程引擎的结合

如下图所示,表单引擎也有一个独立的配置文件,配置类,配置引擎及三个Service.



在实际使用中我们也是将其配置嵌入到流程引擎的配置中,配置形式如下(省略了非相关配置):

    <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" />

在应用重启时数据库中会增加act_fo_为前缀的六张数据库表格:
act_fo_databasechangelog: Liquibase用来跟踪数据库变量的
act_fo_databasechangeloglock: Liquibase用来保证同一时刻只有一个Liquibase实例在运行
act_fo_form_definition:存储表单定义的信息
act_fo_form_instance:存储用户填充后表单实例信息
act_fo_form_deployment:存储表单部署元数据
act_fo_form_resource:存储表单定义的资源

2. 表单的定义

表单定义文件是以.form为后缀, 内容格式为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属性是其唯一性标识,表单引擎可以通过该key获取到它, 同时数据库对相同key会维护不同的版本。第二部分是表单字段数组,第三部分是表单结果。每一个表单字段都有id,name和type属性。id属性在同一个表单定义文件中必须唯一,当用户赋值时它会作为变量的名称,在上例中,也就是会创建名称为input1的变量,值由用户填入。同时表单结果也会以form_<form-identifier>_outcome获取得到,对于上例,用户选择的结果会赋值给form_form1_outcome, 我们可以通过${form_form1_outcome == "Accept"}表达式来验证表单结果是否为Accept。
表单字段的type属性支持text, multi-line-text,integer,boolean,date等多种类型。

3. 表单的使用

首先在src/main/resources文件夹下创建test.form表单定义文件,内容如下:

{
"key": "form1",
"name": "请假流程",
"fields": [
            {
            "id": "startTime",
            "name": "开始时间",
            "type": "date",
            "required": true,
            "placeholder": "empty"

            },
            {
            "id": "endTime",
            "name": "结束时间",
            "type": "date",
            "required": true,
            "placeholder": "empty"
            },
            {
            "id": "reason",
            "name": "请假原因",
            "type": "text",
            "required": true,
            "placeholder": "empty"
            }
    ]
}

然后创建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>

上面的开始事件和两个用户任务都带有flowable:formKey属性。
然后就可以创建测试类了,注意用runtimeService.startProcessInstanceWithForm方法启动带表单的流程,runtimeService.getStartFormModel查询流程启动时的表单信息; taskService.completeTaskWithForm填充表单完成任务,taskService.getTaskFormModel查询任务表单信息。具体测试类代码如下:

@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();



        //启动实例并且设置表单的值
        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("######################");

        }


        //查询个人任务并填写表单
        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);

        //获取个人任务表单
        FormModel taskFM = taskService.getTaskFormModel(taskId);
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342