工作流Activiti框架中表單的使用!詳細解析內建表單和外接表單的渲染

攻城獅Chova發表於2021-06-07

Activiti中的表單

  • Activiti提供了一種方便而且靈活的方式在業務流程中以手工方式新增表單
  • 對錶單的支援有2種方式:
    • 通過表單屬性對內建表單進行渲染
    • 通過表單屬性對外接表單進行渲染

表單屬性

  • 業務流程相關聯的所有資訊:
    • 包含自身的流程變數
    • 通過流程變數的引用
  • Activiti支援儲存複雜的Java物件作為流程變數:
    • 序列化物件
    • Jpa實體物件
    • 整個XML文件作為字串
  • 使用者是在啟動一個流程和完成使用者任務時,與流程進行互動
  • 表單需要某個UI技術渲染之後才能夠與使用者進行互動
  • 為了能夠使用不同UI技術變得容易,流程定義包含一個對流程變數中複雜的Java型別物件到一個propertiesMap<String,String> 型別的轉換邏輯
  • 使用Activiti API的方法檢視公開的屬性資訊.然後,任意UI技術都能夠在這些屬性上面構建一個表單.該屬性專門為流程變數提供了一個檢視. 表單所需要顯示的屬性可以返回值FormData中獲取:
StartFormData FormService.getStartFormData(String processDefinitionId)

或者

TaskFormdata FormService.getTaskFormData(String taskId)
  • 在預設情況下,內建的表單引擎遇到這些變數就像對待流程變數一樣.如果任務表單屬性和流程變數是一對一的關係,那麼任務表單屬性就不需要進行申明瞭:
<startEvent id="start" />
  • 當執行到開始事件時,所有的流程變數都是可用的,但是
formService.getStartFormData(String processDefinitionId).getFormProperties()

會是一個空值,因為沒有定義一個具體的對映

  • 表單中所有被提交的屬性都將會作為流程變數被儲存在Activiti使用的資料庫中. 這意味著在一個表單中新新增一個簡單的input輸入欄位,也會作為一個新的變數被儲存
  • 屬性來自於流程變數,但是不一定非要作為流程變數儲存:
    • 一個流程變數可能是JPA實體如類Address.在某種UI技術中使用的表單屬性StreetName可能會關聯到一個表示式 #{address.street}
  • 使用者提交的表單屬性應該作為流程變數進行儲存
  • 使用UEL值表示式將其作為流程變數的一個巢狀屬性進行儲存
  • 提交的表單屬性預設的行為是作為流程變數進行儲存,除非一個 formProperty 申明瞭其他的規則
  • 型別轉換也可以應用於表單資料和流程變數之間的處理:
<userTask id="task">
  <extensionElements>
    <activiti:formProperty id="room" />
    <activiti:formProperty id="duration" type="long"/>
    <activiti:formProperty id="speaker" variable="SpeakerName" writable="false" />
    <activiti:formProperty id="street" expression="#{address.street}" required="true" />
  </extensionElements>
</userTask>
  • 表單屬性room將會被對映為String型別流程變數room
  • 表單屬性duration將會被對映為java.lang.Long型別流程變數duration
  • 表單屬性speaker將會被對映為流程變數SpeakerName:
    • writable="false" 只能夠在TaskFormData物件中使用.如果屬性speaker提交,將會丟擲一個ActivitiException的異常
    • readable="false" 該屬性就會在FormData進行排除,但是在提交後仍然會對其進行處理
  • 表單屬性street將會對映為Java Bean address的屬性street作為String型別的流程變數:
    • 當提交的表單屬性並沒有提供並且required="true" 時,那麼就會丟擲一個異常
  • 表單資料也可以作為FormData的一部分提供型別後設資料.該FormData可以從以下方法的返回值中獲取:
StartFormData FormService.getStartFormData(String processDefinitionId)
TaskFormdata FormService.getTaskFormData(String taskId)
  • 表單屬性型別:
    • string: org.activiti.engine.impl.form.StringFormType
    • long: org.activiti.engine.impl.form.LongFormType
    • enum: org.activiti.engine.impl.form.EnumFormType
    • date: org.activiti.engine.impl.form.DateFormType
    • boolean: org.activiti.engine.impl.form.BooleanFormType
  • 對於宣告每一個表單屬性,FormProperty資訊可以通過以下方式獲取:
List<FormProperty> formService.getStartFormData(String processDefinitionId).getFormProperties()

或者

List<FormProperty> formService.getTaskFormData(String taskId).getFormProperties()

public interface FormProperty {
  /**

  the key used to submit the property in {@link FormService#submitStartFormData(String, java.util.Map)}
   * or {@link FormService#submitTaskFormData(String, java.util.Map)} */
  String getId();
  /** the display label */
  String getName();
  /** one of the types defined in this interface like e.g. {@link #TYPE_STRING} */
  FormType getType();
  /** optional value that should be used to display in this property */
  String getValue();
  /** is this property read to be displayed in the form and made accessible with the methods
   * {@link FormService#getStartFormData(String)} and {@link FormService#getTaskFormData(String)}. */
  boolean isReadable();
  /** is this property expected when a user submits the form? */
  boolean isWritable();
  /** is this property a required input field */
  boolean isRequired();
}
  • 示例:
<startEvent id="start">
  <extensionElements>
    <activiti:formProperty id="speaker"
      name="Speaker"
      variable="SpeakerName"
      type="string" />

    <activiti:formProperty id="start"
      type="date"
      datePattern="dd-MMM-yyyy" />

    <activiti:formProperty id="direction" type="enum">
      <activiti:value id="left" name="Go Left" />
      <activiti:value id="right" name="Go Right" />
      <activiti:value id="up" name="Go Up" />
      <activiti:value id="down" name="Go Down" />
    </activiti:formProperty>

  </extensionElements>
</startEvent>
  • 所有的表單屬性的資訊都是可以通過API進行訪問的:
    • formProperty.getType().getName(): 獲取型別的名稱
    • formProperty.getType().getInformation("datePattern"): 獲取日期的匹配方式
    • formProperty.getType().getInformation("values"): 可以獲取到列舉值
  • Activiti控制檯支援表單屬性並且可以根據表單定義對錶單進行渲染:
<startEvent ... >
  <extensionElements>
    <activiti:formProperty id="numberOfDays" name="Number of days" value="${numberOfDays}" type="long" required="true"/>
    <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" value="${startDate}" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
    <activiti:formProperty id="vacationMotivation" name="Motivation" value="${vacationMotivation}" type="string" />
  </extensionElements>
</userTask>

當使用Activiti控制檯時,會被渲染成流程的啟動表單

外接表單的渲染

  • Activiti中的API允許執行Activiti流程引擎之外的方式渲染任務表單,可以用自定義方式對任務表單進行渲染
  • 所有需要渲染的表單屬性進行裝配的服務方法有兩種:
    • StartFormData FormService.getStartFormData(String processDefinitionId)
    • TaskFormdata FormService.getTaskFormData(String taskId)
  • 表單屬性提交的兩種方式:
    • ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties)
    • void FormService.submitStartFormData(String taskId, Map<String,String> properties)
  • 可以將任何表單模版資源放進要部署的業務文件之中(如果想要按照流程的版本進行儲存).將會在部署中作為一種可用的資源
  • 獲取部署表單模版的方式有兩種:
    • String ProcessDefinition.getDeploymentId()
    • InputStream RepositoryService.getResourceAsStream(String deploymentId, String resourceName)
    • 這樣就可以獲取表單模版定義檔案,就可以在應用中渲染或者顯示錶單
  • 也可以使用該功能獲取任務表單之外的其他的部署資源用於其他的目的
  • 屬性 <userTask activiti:formKey="..." 暴露方式API:
    • String FormService.getStartFormData(String processDefinitionId).getFormKey()
    • String FormService.getTaskFormData(String taskId).getFormKey()
  • 可以使用這個儲存部署的模版中的全名(例如org/activiti/example/form/my-custom-form.xml) 但是這並不是必須的:
    • 可以在表單屬性中儲存一個通用的key,然後運用一種演算法或者換轉去得到你實際使用的模版
    • 當需要通過不同UI技術渲染不同的表單會更加方便:
      • 使用正常螢幕大小的web應用程式的表單
      • 移動手機小螢幕的表單
      • IM表單
      • email表單模版

相關文章