工作流引擎之Activiti中的表单及应用详解

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

<br />

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

推荐阅读更多精彩内容