Skip to content
叶春华 edited this page Feb 21, 2019 · 17 revisions

Overview

  • InfoPlus Messenger System: Event-Driven Integration

What's Messenger?

  • “Messenger”是InfoPlus平台和第三方系统之间的事件驱动的互操作机制,简而言之,就是流程的钩子/回调。
  • 事件机制/Events涵盖流程处理的全生命期,包括从流程创建/结束/终止、各步骤办理/打印/草稿/撤回、界面的每个控件的变化/感知等。
  • 开发者可以在这些回调中控制界面显示、检查操作的合法性、以及实现对自身业务系统的数据存取和接口调用等。
  • 技术上,Messenger和InfoPlus平台之间可通过REST/WebService等不同方式进行交互,具体实现上并不限制技术框架,但建议上请参考/使用我们的InfoPlus SDK来简化开发过程。

如何使用?

  • 流程开发者可以在流程编辑器中定义Messenger程序的访问地址,并勾选要触发的事件,参考:
  • 开发者可在messenger中开发各种事件的处理程序,具体实现因SDK而异,请具体参考sdk的开源代码和实现。
  • 开发过程中需要的的数据模型,可通过IDE来生成,参考:

Events

EventType When 典型用例
INSTANCE_STARTING 流程发起时,发起成功前 检查是否可发起;赋予初始化数据
INSTANCE_STARTED 发起成功后 持久化流程流水号等信息;关联第三方数据实体
INSTANCE_COMPLETING 流程即将结束时 此事件无效,原理上InfoPlus无法准确取得,请勿使用
INSTANCE_COMPLETED 流程结束以后 持久化表单数据
INSTANCE_KILLED 流程被终止以后 通知用户;处理第三方相关的业务逻辑
INSTANCE_EXPIRING 流程级超时 超时时自动办理、终止、延时
INSTANCE_EXPIRED 流程超时后 做一些持久化的操作
INSTANCE_SAVING 管理员修改 合法性检查
INSTANCE_SAVED 管理员修改后 保持第三方数据一致性
INSTANCE_PRINTING 管理员打印 打印时数据预处理
INSTANCE_EXPORTING 管理员导出 导出时数据预处理
INSTANCE_RENDERING 欢迎页渲染 渲染前数据预处理
STEP_EXPIRING 步骤/实例即将超时 干预超时行为:延期/忽略/终止
STEP_EXPIRED 步骤/实例已超时 通知用户;处理第三方相关的业务逻辑
STEP_RENDERING 页面即将渲染时 修改表单界面数据;追加代码表内容配合Select使用
STEP_RENDERED 页面渲染成功后 暂不支持
STEP_PRINTING 用户打印 打印时数据预处理
STEP_EXPORTING 用户导出 导出时数据预处理
STEP_WITHDRAWING 撤回时 撤回时效验
ACTION_CLICKING 动作点击以后/选步骤之前 验证表单数据 注:请在ACTION_DOING做相同验证
ACTION_SAVING 即将保存草稿 验证表单数据给出错误提示
ACTION_SAVED 存草稿成功后
ACTION_DOING 步骤即将办理时 验证表单数据给出错误提示;更新表单数据
ACTION_DONE 步骤办理成功以后 处理第三方相关的业务逻辑
FIELD_CHANGING 表单字段变化 引起界面其他字段联动。 注:需配合字段的Event掩码配置一起使用
FIELD_SUGGESTING 外部代码表Suggest时 引起界面其他字段联动。 注:需配合字段的Event掩码一起使用
ECHO 系统保留

STEP_RENDERING/FIELD_CHANGING/FIELD_SUGGESTING 和数据持久化

  • 此类事件改变的数据,只是影响前端的展示,并不会真正持久化,持久化发生在后续的保存或提交时
  • RENDER事件经常被过度使用,但建议一些持久化的操作要提前
    • 需要持久化的数据如果可以在上一步结束时产生的,请移到ACTION_DOING中
    • 如果产生点有很多,可以使用SDK的封装来针对“达到此步”的事件写Messenger
  • 此类事件的数据经过前台,所以要注意数据泄漏和篡改
    • 引擎后台此类事件带过来的数据和用户手工输入的一视同仁,所以如果希望持久化必须有足够权限
    • 重复节如果权限不足,会报FORM_DATA_UNAUTHORIZED的异常;字段无权限会被简单的忽略
    • 在表单提交时,如有必要,需要对带过来的数据做合法性检查或者二次赋值

FIELD_CHANGING

数据结构

data

Java messenger

public InfoPlusResponse onFieldChanging(InfoPlusEvent e) {
    SampleForm form = e.toBean(SampleForm.class);
    Object changedObject = null;
    if ("fieldRepeat1".equals(e.getChangingField())) { //某重复字段发生改变
        //1. 改变本行的其他字段
        RepeatGroup g = e.getChangingObject(form);
        g.setRepeat2("I am fieldRepeat2");
        changedObject = g; //正确
        changedObject = form; //也正确,但是低效
        //2. 改变上级非重复字段
        form.setNoRepeat1("I am fieldNoRepeat1");
        changedObject = form;
        //3. 追加行
        RepeatGroup g = new RepeatGroup();
        g.setRepeat1("I am fieldRepeat1");
        g.setRepeat2("I am fieldRepeat2");
        form.getRepeatGroups().add(g);
        changedObject = form; //正确
        changedObject = g; //错误
        //4. 删除本行
        RepeatGroup g = e.getChangingObject(form);
        form.getRepeatGroups().remove(g);
        changedObject = form; //正确
        changedObject = g; //错误
        //5. 改变其他行的字段
        RepeatGroup firstRow = form.getRepeatGroups().get(0);
        firstRow.setRepeat1("I am fieldRepeat1");
        changedObject = firstRow; //正确
        changedObject = form; //也正确,但是低效
    }
    if (changedObject == null) return null;
    return new InfoPlusResponse(e, changedObject);
}

C# messenger

public class FieldChangeDemoMessenger : AbstractMessenger 
{
    public override InfoPlusResponse OnFieldChanging(InfoPlusEvent e)
    {
        FieldChangeDemo rootObject = InfoPlusEntity.Convert<FieldChangeDemo>(e);
        InfoPlusResponse response = new InfoPlusResponse();
    
        switch (e.Field)
        {
            case "fieldNoRepeat1":
                //改变本行
                FieldChangeDemo demo = e.LocateChangedObject<FieldChangeDemo>(rootObject);
                demo.NoRepeat2 = "fieldNoRepeat changed->" + demo.NoRepeat1;
                //追加重复行
                if (demo.RepeatGroup == null)
                {
                    demo.RepeatGroup = new List<FieldChangeDemoRepeatGroup>();
                }
                FieldChangeDemoRepeatGroup newRow  = new FieldChangeDemoRepeatGroup();
                newRow.Repeat1 = "new repeat1";
                newRow.Repeat2 = "new repeat2";
                demo.RepeatGroup.Add(newRow);
                response.FormData = e.ConvertChanged(demo);
                break;
            case "fieldRepeat1":
                //改变本行
                FieldChangeDemoRepeatGroup repeatGroup = e.LocateChangedObject<FieldChangeDemoRepeatGroup>(rootObject);
                repeatGroup.Repeat2 = "fieldRepeat1 changed->" + repeatGroup.Repeat1;
                response.FormData = e.ConvertChanged(repeatGroup);
                //改变上级字段
                FieldChangeDemo changedOjbect = e.LocateChangedObject<FieldChangeDemo>(rootObject);
                changedOjbect.NoRepeat2 = "fieldRepeat1 changed";
                response.FormData = e.ConvertChanged(changedOjbect);
    
                break;
            case "fieldInnerRepeat1":
                //改变本行
                FieldChangeDemoRepeatGroupInnerRepeatGroup innerRepeatRow =
                        e.LocateChangedObject<FieldChangeDemoRepeatGroupInnerRepeatGroup>(rootObject);
                innerRepeatRow.InnerRepeat2 = "fieldInnerRepeat1 changed->" + innerRepeatRow.InnerRepeat1;
                response.FormData = e.ConvertChanged(innerRepeatRow);
                //改变上级字段
                FieldChangeDemoRepeatGroupInnerRepeatGroup innerRepeatRow =
                        e.LocateChangedObject<FieldChangeDemoRepeatGroupInnerRepeatGroup>(rootObject);
                FieldChangeDemoRepeatGroup parentGroup = e.LocateChangedObject<FieldChangeDemoRepeatGroup>(rootObject);
                parentGroup.Repeat2 = "fieldInnerRepeat1 changed->" + innerRepeatRow.InnerRepeat1;
                response.FormData = e.ConvertChanged(parentGroup);
                //改变其他行字段
                FieldChangeDemoRepeatGroupInnerRepeatGroup innerRepeatRow =
                        e.LocateChangedObject<FieldChangeDemoRepeatGroupInnerRepeatGroup>(rootObject);
                FieldChangeDemoRepeatGroup parentGroup = e.LocateChangedObject<FieldChangeDemoRepeatGroup>(rootObject);
                FieldChangeDemoRepeatGroupInnerRepeatGroup firstRow = parentGroup.InnerRepeatGroup[0];
                firstRow.InnerRepeat2 = "fieldInnerRepeat1 changed->" + innerRepeatRow.InnerRepeat1;
                response.FormData = e.ConvertChanged(parentGroup);
                //改变根对象
                FieldChangeDemoRepeatGroupInnerRepeatGroup innerRepeatRow =
                        e.LocateChangedObject<FieldChangeDemoRepeatGroupInnerRepeatGroup>(rootObject);
                FieldChangeDemo demoRoot = e.LocateChangedObject<FieldChangeDemo>(rootObject);
                demoRoot.NoRepeat2 = "fieldNoRepeat changed->" + innerRepeatRow.InnerRepeat1;
                response.FormData = e.ConvertChanged(demoRoot);
                break;
        }
    
        return response;
    }
}

Response

{
    // true表示取消当前事件,等同于抛出异常,告知前台无需继续,用于服务端验证
    cancel : {boolean},

    // 仅当cancel时有效,作为告知前台的提示信息
    // 提示信息支持显示网页链接,语法为[显示的文字](http://xxx.xx.com)
    prompt: {string},

    // 同上,但作为"详细信息"显示,不支持显示网页链接
    detail: {string},

    // 表单数据,具体格式可参见[FormData]词条,可用于:
    // 1.STARTING:给出初始化数据
    // 2.CHANGING:自动在界面上填充相关字段
    // 3.RENDERING:临时改变显示内容,不持久化
    // 4.CLICKING:如果后续为判断节点,可改变流程走向,不持久化
    // 5.DOING/SAVING:改变持久化的表单数据
    formData: {string:object},

    // 返回的代码表数据,这用于:
    // 1.FIELD_SUGGESTING:仅第一个有效,当前感知的内容
    // 2.STEP_RENDERING:初始化表单需要的外部代码表,主要用于select显示
    // 3.FIELD_CHANGING:同上
    codes: [{
        name: {string},
        total: {int},
        items: [
            codeId: {string},
            codeName: {string},
            description: {string},
            parentId: {string},
            itemIndex: {int},            // 代码表项的排序号,升序
            attributes: {string:string}  // 额外的属性,可用于公式样式等
        ]
    }],

    // 返回一个定时器,仅作用于STEP或INSTANCE的EXPIRING事件
    // 在EXPIRING之后,Messenger可以告知引擎下面四件事情之一:忽略/延时/终止/执行
    // 注意:如果timer为空,则使用工作流配置的默认选项,否则以timer为准
    timer: {
        // 时间间隔,有3种情况:
        // 0:不作处理,忽略此事
        // -1:终止当前流程
        // 正整数:延时的秒数
        interval: {integer},  

        // 自动执行的action的code,注意:
        // * 仅当STEP_EXPIRING有效
        // * interval为-1时将忽略action,直接终止
        // * 只能使用系统用户办理(执行),此步骤必须配置为"系统用户可办"
        // * 执行时几时发生的,如执行不成功依然可以设置interval忽略或者延时
        action: {string}
    }
}
Clone this wiki locally