可视化流程应用仅提供基础的旅程画板编辑、旅程运行时的调度功能,业务步骤的能力由第三方应用以插件的形式向可视化流程应用提供。

# 步骤插件概述

步骤插件指由第三方应用按照指定规范向可视化流程应用注入的步骤配置,包括后端 Open Api 、前端组件。注入的步骤会展示在可视化流程的画板中供用户选择、拖拽、配置。配置页面也是由插件提供,通过前端技术进行集成。

  • 租户购买第三方应用后,可视化流程拉取所有购买应用的步骤插件配置,组成该租户所拥有的步骤插件列表。
  • 用户(即员工)进入可视化流程画板,可以在步骤列表中看到租户所拥有的步骤。

# 插件注册与发布

第三方应用 需要在 Engage admin 应用配置中注册步骤插件,才能提供更多的业务处理能力。插件配置规范如下:

字段 描述
target 插件注入的目标应用
target.application 目标应用 Id
target.type 插件适配的类型,object-step:作为旅程的起点步骤,生产客体、common-step:一般业务步骤
name 步骤名称
type 步骤类型,要求全局唯一
icon 步骤的icon
category 步骤种类,audience:客体步骤;branch:判断步骤;action:动作步骤
object-step-tabs 客体步骤默认有选择受众与导入受众两个 tab 页面,其中,选择受众展示的内容由插件控制,导入受众由旅程前端控制,默认选择受众与导入受众都显示。该配置允许开发者自定义 tab 页的展示效果,config-tab:只显示插件注入的配置页面,不显示导入客体 tab 页。import-tab:只显示导入客体 tab 页面
branch-type 当步骤种类是 branch 的时候,需要配置 branch-type 字段。single:步骤仅有两个分支, multi:步骤存在多个分支。
config-component 配置页面的微前端组件,格式为:<微前端名>.<组件名>
object-attribute 当步骤种类为客体步骤(audience),传入数组来声明输出的客体包含哪些属性,如 ['MobilePhone','customerId']
workflow-removed 是否在删除旅程时向对应应用发起回调,默认为 false,如果需要进行回调,应用需要是实现 /v1/workflow/remove open-api 来接收回调

注册完插件发布时,由 Engage 平台调用可视化流程的插件校验接口,对插件配置进行检查。

# 插件配置示例

客体步骤插件:

  # 插件目标
- target:
    # 目标应用 Id,固定为 workflow
    application: workflow 
    # 插件适配的类型,这里为客体步骤
    type: object-step
  # 步骤名称
  name: 受众人群
  # 步骤类型
  type: audience
  # 步骤的 Icon,URL
  icon: https://xx.com/xx.svg
  # 配置页面的组件,格式为:<微前端名>.<组件名>
  config-component: workflow-addons.audience-step-config
  object-attribute: ['MobilePhone','customerId']

一般步骤插件:

  # 插件目标
- target:
    # 目标应用 Id,固定为 workflow
    application: workflow 
    # 插件适配的类型,这里为客体步骤
    type: common-step
  # 步骤名称
  name: 发送短信
  # 步骤所属分类,有 action、branch
  category: action
  # 步骤类型
  type: send-sms
  # 步骤的 Icon,URL
  icon: https://xx.com/xx.svg
  # 配置页面的组件,格式为:<微前端名>.<组件名>
  config-component: workflow-addons.send-sms-step-config

# 后端集成

可视化流程应用与第三方应用通过如下接口进行服务端的通信

# 画板配置时后端通信流程

注:此处重点关注后端通信流程,所以将旅程前端与插件前端统一视为可视化流程画板。

当用户新建一个可视化流程后来到可视化流程画板页面:

  • 用户拖拽一个步骤进入画板,对步骤进行配置,点击保存。
  • 可视化流程前端调用可视化流程后端的保存接口。
  • 可视化流程后端将会像步骤对应的第三方应用:校验步骤接口发起请求,验证用户配置是否正确。
  • 如果第三方应用:校验步骤接口校验无误,可视化流程将会保存步骤至服务端,如果校验失败,将会提醒用户校验失败。
  • 重复上述过程,用户完成了一个可视化流程的设计。
  • 用户点击启动按钮。
  • 可视化流程前端向可视化流程后端发起启动请求,可视化流程将获取流程的所有步骤,向步骤对应的第三方应用:保存步骤接口发起请求,将步骤配置保存至第三方应用本地。
  • 可视化流程启动成功。

# 可视化流程运行时后端通信流程

可视化流程启动后,可视化流程引擎将会统筹客体在设计好的流程中流动的调度:

# 前端集成

在进行步骤配置时,很大一部分的配置项及其界面由第三方的前端组件来渲染。第三方前端组件需要跟可视化流程进行通信,包括配置变更、分支变更、资源变更等方面。 第三方前端组件需要实现的接口如下:

名称 位置 说明
value props 对象,双向绑定(v-model),步骤的配置信息
resources props 数组,双向绑定(.sync),全局资源信息
inputParams props 数组,双向绑定(.sync)步骤执行所需要的编程参数数组,允许为[]
outputParams props 数组,双向绑定(.sync)步骤执行后输出的编程参数数组,允许为[]
validate methods 函数,由可视化流程调用的配置校验函数
input event 配置变更
update:resources event 资源变更
error event 事件,对外实时传递校验结果

value 结构说明:

({
  name: '标题',
  branches: [
    {id: 'default', name: '默认'}, // 总是有这一个分支,无法删除,id 固定,总是第一个。name 可以修改。
    {id: 'branchxx', name: 'xxx'}, // 如果是多分支步骤,可以拥有多个 branch
  ],
  data: {
    // 公共参数,即可视化流程可能针对某些类型的步骤提供的通用配置项。这些参数由可视化流程渲染。
    wait: '5s', // 等待时间,一般只有多分支步骤存在。
    // 自定义参数,第三方步骤自己定义的配置项
    h5: {
      resourceId: '', // 表示这对应一个全局资源。当全局资源变更的时候,组件内要自行重新映射配置。
      id: 'xxx', name: '', cover: '',
    }
  }
});

resources 结构说明:

([
  {
    id: '1234', // 资源 Id,用于在步骤配置里进行绑定。全局唯一。可以用 type + 实际数据的 Id 组成。
    type: 'h5',  // 资源类型,必填
    name: '春节活动落地页', // 资源名称,必填
    remark: '宣传XXX产品', // 资源描述,可选
    cover: 'https://xx.com/1.jpg', // 资源封面,可选
    data: { // 资源的实际数据
      id: 'xxcddsf123sldf23',
      name: '春节活动落地页',
      cover: 'https://xx.com/1.jpg',
      createdAt: '2018-12-01 15:00:00+08:00',
      updatedAt: '2018-12-05 16:05:00+08:00',
    }
  }
])

inputParams 与 outputParams 结构说明:

({
  inputParams:[// 步骤执行所需要的编程参数
    {
     id:"1ozbzooXUJpQYcAyLNX5xT9N4rhFm08z",
     name:"题目一",
     stepId:"1"
    }
  ],
  outputParams:[ // 步骤执行后输出的编程参数
    {
      id:"group", 
      name:"人群分组",
      stepId:"1"
    }
  ],
});

示例:假设第三方应用(h5)要给 Workflow 提供一个步骤,叫“是否访问微页面”。该组件由 Vue 开发。

<template>
  <select :value="config.data.h5" @input="changeH5">
</template>
<script>
export default {
  name: 'is-h5-visited',
  props: {
    value: Object,
    resources: Object,
    inputParams: Array,
    outputParams: Array,
  },
  data(){
    return {
      config: {
        name: '',
        branches: [],
        data: {
          h5: {
            id: '',
          }
        }
        inputParams: [],
        outputParams: [],
      }
    }
  },
  methods: {
    validate(){ // 第三方组件要实现的校验方法,抛出异常表示校验失败
      if(!this.config.data.h5.id) {
        throw new Error('请选择 H5')
      }
    },
    changeH5(h5){
      if(!h5 || !h5.id){
        return this.$emit('error', new Error('请选择 H5'));
      }
      
      let resource = this.resources.find(r=>r.id == h5.id);
      if(!resource){
        resource = {id: 'h5'+h5.id, name: h5.name, cover: h5.cover, data: h5};
        this.$emit('update:resources', [...this.resources, resource])
      }
      this.config.data.h5 = {resourceId: resource.id, ...resource.data};
      this.$emit('input', this.config);
    }
  }
}
</script>
<style>
</style>

组件导出:

// src/main.js
import IsH5Visited from './components/is-h5-visited';

export {
  // 微前端三个钩子函数
  bootstrap(){
  },
  mount(){
  },
  unmount(){
  },
  // 导出的组件
  'is-h5-visited': IsH5Visited
}