Flowable實(shí)戰(zhàn)(五)表單和流程變量

一撵摆、流程變量

??流程實(shí)例按步驟執(zhí)行時(shí),需要保存并使用一些數(shù)據(jù)囚灼,在Flowable中骆膝,這些數(shù)據(jù)稱為變量(variable)。

??流程實(shí)例可以持有變量灶体,稱作流程變量(process variables)阅签。

??為了使用效率,F(xiàn)lowable將變量分為兩種:運(yùn)行時(shí)變量赃春、歷史變量愉择。

1.1 運(yùn)行時(shí)變量

??流程實(shí)例運(yùn)行時(shí)的變量,存入act_ru_variable表中织中。在流程實(shí)例運(yùn)行結(jié)束時(shí)锥涕,此實(shí)例的變量在表中刪除。

??在流程實(shí)例創(chuàng)建及啟動時(shí)狭吼,可設(shè)置流程變量层坠。所有的startProcessInstanceXXX方法都有一個(gè)可選參數(shù)用于設(shè)置變量。例如刁笙,在RuntimeService中:

    ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);

??也可以在流程執(zhí)行中加入變量破花。例如,(RuntimeService):

    void setVariable(String executionId, String variableName, Object value);
    void setVariableLocal(String executionId, String variableName, Object value);
    void setVariables(String executionId, Map<String, ? extends Object> variables);
    void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);

??讀取變量方法(請注意TaskService中有類似的方法疲吸。這意味著任務(wù)與執(zhí)行一樣座每,可以持有局部變量,其生存期為任務(wù)持續(xù)的時(shí)間摘悴。)

    Map<String, Object> getVariables(String executionId);
    Map<String, Object> getVariablesLocal(String executionId);
    Map<String, Object> getVariables(String executionId, Collection<String> variableNames);
    Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames);
    Object getVariable(String executionId, String variableName);
    <T> T getVariable(String executionId, String variableName, Class<T> variableClass);

注意:由于流程實(shí)例結(jié)束時(shí)峭梳,對應(yīng)在運(yùn)行時(shí)表的數(shù)據(jù)跟著被刪除。所以蹂喻,查詢一個(gè)已經(jīng)完結(jié)流程實(shí)例的變量葱椭,只能在歷史變量表中查找。

1.2 歷史變量

??歷史變量口四,存入act_hi_varinst表中孵运。在流程啟動時(shí),流程變量會同時(shí)存入歷史變量表中蔓彩;在流程結(jié)束時(shí)治笨,歷史表中的變量仍然存在〕嘟溃可理解為“永久代”的流程變量旷赖。

??獲取已完成的、id為’XXX’的流程實(shí)例中探膊,所有的HistoricVariableInstances(歷史變量實(shí)例)杠愧,并以變量名排序。

    historyService.createHistoricVariableInstanceQuery()
      .processInstanceId("XXX")
      .orderByVariableName.desc()
      .list();

二逞壁、表單

??在實(shí)際業(yè)務(wù)中流济,流程伴隨著各種各樣的表單锐锣,F(xiàn)lowable引擎將表單數(shù)據(jù)統(tǒng)一作為流程變量存入變量表中。所以绳瘟,對于Flowable引擎雕憔,可以完全獨(dú)立于表單運(yùn)行,因?yàn)榭梢杂昧鞒套兞刻娲韱螖?shù)據(jù)糖声。

??但一般的斤彼,我們需要結(jié)構(gòu)化的數(shù)據(jù),表單仍然是我們推薦的用法蘸泻。

??表單定義有兩種方法琉苇,內(nèi)置表單和外部表單。

2.1 內(nèi)置表單

??以請假為例悦施,XML內(nèi)容:

<process id="leave" name="請假流程-內(nèi)置表單">
    <startEvent id="start">
        <extensionElements>
            <flowable:formProperty id="startDate" name="請假開始事件" type="date" 
              datePattern="dd-MMM-yyyy" required="true" readable="true" writeable="true"/>
            <flowable:formProperty id="endDate" name="請假結(jié)束事件" type="date" 
              datePattern="dd-MMM-yyyy" required="true" readable="true" writeable="true"/>
            <flowable:formProperty id="reason" name="請假原因" type="string" 
              required="true" readable="true" writeable="true"/>
            <flowable:formProperty id="leaveType" type="enum" name="請假類型">
              <flowable:value id="personalLeave" name="事假" />
              <flowable:value id="annualLeave" name="年假" />
            </flowable:formProperty>
        </extensionElements>
    </startEvent>
</process>

??使用方法:

    StartFormData FormService.getStartFormData(String processDefinitionId)

??或

    TaskFormData FormService.getTaskFormData(String taskId)

??內(nèi)置表單了解即可并扇,實(shí)際應(yīng)用更多的是使用外部表單。

2.2 外部表單

??根據(jù)表單文件自行渲染的任務(wù)表單抡诞,稱為外部表單穷蛹。

2.2.1 XML內(nèi)容

    <process id="leave" name="請假流程-內(nèi)置表單">
        <startEvent id="start" flowable:formKey="form1"></startEvent>
    </process>

注意:flowable:formKey="form1"中的"form1"對應(yīng)表單定義文件的"key"值。

2.2.2 表單定義

  • 表單定義文件的后綴為.form昼汗。
  • 表單的JSON定義以key肴熏、name和description開頭。
  • 表單引擎通過屬性key來辨別表單在整個(gè)表單引擎中的唯一身份顷窒。對于來源相同的同一個(gè)表單定義的版本系統(tǒng)也是基于屬性key運(yùn)作的蛙吏。
  • 第二部分是一個(gè)數(shù)組類型fields,表單定義的字段在這里闡明蹋肮。
  • 第三部分是可選的出刷,用來定義表單的結(jié)果outcomes璧疗。示例如下:
{
    "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"
        }
    ]
}

2.2.3 部署表單

??在springboot環(huán)境下坯辩,resources/forms目錄下任何.form后綴的表單定義文件都會被自動部署。

??例如崩侠,將2.2.2表單定義內(nèi)容保存為leave.form文件漆魔,放入resources/forms目錄下。

注意:實(shí)際應(yīng)用中却音,應(yīng)當(dāng)讓前端流程設(shè)計(jì)器生成指定格式的表單定義文件改抡,通過與前文提到的接口方式,更新部署流程定義及表單定義資源系瓢。

2.2.4 獲取及提交表單參數(shù)

??實(shí)際上阿纤,渲染表單所需的所有數(shù)據(jù)都組裝在下面兩個(gè)方法:

    StartFormData FormService.getStartFormData(String processDefinitionId)
    TaskFormdata FormService.getTaskFormData(String taskId)

??可以通過下面兩個(gè)方法提交表單參數(shù):

    ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties)
    void FormService.submitTaskFormData(String taskId, Map<String,String> properties)

??表單參數(shù)FormProperty的具體信息:

public interface FormProperty {
  /**
   * 在{@link FormService#submitStartFormData(String, java.util.Map)}
   * 或{@link FormService#submitTaskFormData(String, java.util.Map)}
   * 中提交參數(shù)時(shí)使用的key
   */
  String getId();

  /** 顯示標(biāo)簽 */
  String getName();

  /** 在本接口中定義的類型,例如{@link #TYPE_STRING} */
  FormType getType();

  /** 可選夷陋。這個(gè)參數(shù)需要顯示的值 */
  String getValue();

  /** 這個(gè)參數(shù)是否可以讀惹肥啊:在表單中顯示胰锌,并可通過
   * {@link FormService#getStartFormData(String)}
   * 與{@link FormService#getTaskFormData(String)}
   * 方法訪問。
   */
  boolean isReadable();

  /** 用戶提交表單時(shí)是否可以包含這個(gè)參數(shù)藐窄? */
  boolean isWritable();

  /** 輸入框中是否必填這個(gè)參數(shù) */
  boolean isRequired();
}

2.2.5 獲取及提交表單數(shù)據(jù)

??獲取指定流程實(shí)例的表單數(shù)據(jù)的方法:

    FormModel RuntimeService.getStartFormModel(String processDefinitionId, String processInstanceId);

??提交表單數(shù)據(jù)的方法:

    // 附帶表單數(shù)據(jù)啟動流程實(shí)例
    ProcessInstance RuntimeService.startProcessInstanceWithForm(String processDefinitionId, String outcome, Map<String,Object> properties, String taskName);
    // 附帶表單數(shù)據(jù)完成任務(wù)
    void TaskService.completeTaskWithForm(String taskId, String formDefinitionId, String outcome, Map<String,Object> properties);

??表單數(shù)據(jù)實(shí)際存放在流程變量表资昧,所以,用流程變量的方法同樣可以獲取及提交表單數(shù)據(jù)荆忍。

2.3 表單類型字段

??表單支持以下類型字段

  • text: 文本字段
  • multi-line-text: 多行文本字段
  • integer: 文本字段格带,但是只允許數(shù)字類型的值
  • boolean: 復(fù)選框字段
  • date: 日期字段
  • dropdown: 選擇框字段,定義字段時(shí)可以設(shè)置選項(xiàng)的值
  • radio-buttons: 單選字段刹枉,定義字段時(shí)可以設(shè)置選項(xiàng)的值
  • people: 選擇框字段叽唱,可以選中用戶表里的一個(gè)用戶
  • functional-group: 選擇框字段,可以選中分組表里的一個(gè)組
  • upload: 上傳文件字段
  • expression: 一個(gè)標(biāo)簽微宝,在標(biāo)簽文本中你可以運(yùn)用JUEL表達(dá)式操作變量或其他動態(tài)的值

2.4 自定義表單字段類型

??在實(shí)際應(yīng)用中尔觉,F(xiàn)lowable提供的表單字段類型并不能完全滿足需求,往往我們需要自定義表單字段類型芥吟。

??所有自定義字段類型需要繼承一個(gè)表達(dá)類型抽象類“org.flowable.engine.form.AbstractFormType”侦铜。

??比如,定義一個(gè)"卡片"自定義類型:

public class CardFormType extends AbstractFormType {

    // 定義表單類型的標(biāo)識符
    @Override
    public String getName() {
        return "card";
    }

    // 把表單中的值轉(zhuǎn)換為實(shí)際的對象(實(shí)際處理邏輯根據(jù)具體業(yè)務(wù)而定)
    @Override
    public Object convertFormValueToModelValue(String propertyValue) {
        return propertyValue;
    }

    // 把實(shí)際對象的值轉(zhuǎn)換為表單中的值(實(shí)際處理邏輯根據(jù)具體業(yè)務(wù)而定)
    @Override
    public String convertModelValueToFormValue(Object modelValue) {
        return (String) modelValue;
    }
    
}

??新建配置類钟鸵,注冊自定義字段類型解析類

@Configuration
public class ApplicationConfig extends WebMvcConfigurerAdapter {
    @Bean
    public BeanPostProcessor activitiConfigurer() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof SpringProcessEngineConfiguration) {
                    List<AbstractFormType> customFormTypes = Arrays.<AbstractFormType>asList(new CardFormType());
                    ((SpringProcessEngineConfiguration)bean).setCustomFormTypes(customFormTypes);
                }
                return bean;
            }
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                return bean;
            }
        };
    }
}

2.5 自定義表單引擎

??Flowable支持自定義表單引擎以適應(yīng)各種場景钉稍。只需要實(shí)現(xiàn)接口org.flowable.engine.impl.form.FormEngine,然后在引擎中注冊自定義的表單引擎實(shí)現(xiàn)類即可棺耍。

public class MyFormEngine implements FormEngine {
    // 表單引擎的名稱
    @Override
    public String getName() {
        return "MyFormEngine";
    }

    // 實(shí)際處理邏輯根據(jù)具體業(yè)務(wù)而定
    @Override
    public Object renderStartForm(StartFormData startFormData) {
        return "MyStartData";
    }

    // 實(shí)際處理邏輯根據(jù)具體業(yè)務(wù)而定
    @Override
    public Object renderTaskForm(TaskFormData taskFormData) {
        return "MyTaskData";
    }
}

??注冊方法與自定義表單字段類型相似贡未,在配置類中加入以下語句:

    List<FormEngine> customFormEngines = Arrays.<FormEngine>asList(new MyFormEngine());
    ((SpringProcessEngineConfiguration)bean).setCustomFormEngines(customFormEngines);

??使用方法:

    Object FormService.getRenderedStartForm(String processDefinitionId, "myFormEngine");
    Object FormService.getRenderedTaskForm(String taskId);

三、小結(jié)

??通過本篇蒙袍,我們了解到了表單和流程變量的具體使用俊卤,同樣的,在實(shí)際業(yè)務(wù)使用中害幅,還需要不少優(yōu)化消恍。比如,我們可以在formKey中保存通用的key以现,通過算法或轉(zhuǎn)換得到實(shí)際需要使用的表單模板狠怨,在普通屏幕尺寸的Web應(yīng)用中顯示一個(gè)表單,在手機(jī)等小屏幕中顯示另一個(gè)表單邑遏。還有下一篇將講到的“集成JPA”佣赖,進(jìn)一步對表單和流程變量的使用做出優(yōu)化。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末记盒,一起剝皮案震驚了整個(gè)濱河市憎蛤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纪吮,老刑警劉巖俩檬,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栏豺,死亡現(xiàn)場離奇詭異,居然都是意外死亡豆胸,警方通過查閱死者的電腦和手機(jī)奥洼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晚胡,“玉大人灵奖,你說我怎么就攤上這事」琅蹋” “怎么了瓷患?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長遣妥。 經(jīng)常有香客問我擅编,道長,這世上最難降的妖魔是什么箫踩? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任爱态,我火速辦了婚禮,結(jié)果婚禮上境钟,老公的妹妹穿的比我還像新娘锦担。我一直安慰自己,他們只是感情好慨削,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布洞渔。 她就那樣靜靜地躺著,像睡著了一般缚态。 火紅的嫁衣襯著肌膚如雪磁椒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天玫芦,我揣著相機(jī)與錄音浆熔,去河邊找鬼。 笑死姨俩,一個(gè)胖子當(dāng)著我的面吹牛蘸拔,可吹牛的內(nèi)容都是我干的师郑。 我是一名探鬼主播环葵,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宝冕!你這毒婦竟也來了张遭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤地梨,失蹤者是張志新(化名)和其女友劉穎菊卷,沒想到半個(gè)月后缔恳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡洁闰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年歉甚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扑眉。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纸泄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腰素,到底是詐尸還是另有隱情聘裁,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布弓千,位于F島的核電站衡便,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洋访。R本人自食惡果不足惜镣陕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姻政。 院中可真熱鬧茁彭,春花似錦、人聲如沸扶歪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽善镰。三九已至妹萨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炫欺,已是汗流浹背乎完。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留品洛,地道東北人树姨。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像桥状,于是被迫代替她去往敵國和親帽揪。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 最近公司使用Flowable開發(fā)了自己的OA系統(tǒng)辅斟,因此對Flowable的相關(guān)內(nèi)容進(jìn)行如下總結(jié) 一转晰、Flowabl...
    君臨天下夜未央閱讀 73,534評論 10 44
  • flowable表單引擎作為一個(gè)獨(dú)立的模塊,也包括表單定義,部署等過程查邢。 1. API及與流程引擎的結(jié)合 如下圖所...
    Acamy丶閱讀 31,112評論 3 10
  • 一蔗崎、流程審批后涉及到的表 當(dāng)流程全部走完后,act_ru_表的數(shù)據(jù)清空了扰藕,全部移到了act_hi_表 二缓苛、流程示例...
    無劍_君閱讀 5,284評論 0 5
  • 一、前言: 發(fā)現(xiàn)網(wǎng)上關(guān)于Flowable的資料基本都是淺嘗輒止邓深,對如何構(gòu)建一個(gè)企業(yè)級的流程應(yīng)用說明很少他嫡,所以寫個(gè)實(shí)...
    金楊杰閱讀 5,393評論 0 5
  • 一、數(shù)據(jù)模型設(shè)計(jì) 清單 通用數(shù)據(jù)庫 流程定義存儲表 身份數(shù)據(jù)表 運(yùn)行時(shí)流程數(shù)據(jù)表 歷史流程數(shù)據(jù)表 二庐完、表結(jié)構(gòu) 通用...
    無劍_君閱讀 3,092評論 0 4