-
Notifications
You must be signed in to change notification settings - Fork 9
Messenger
叶春华 edited this page Feb 21, 2019
·
17 revisions
- InfoPlus Messenger System: Event-Driven Integration
- “Messenger”是InfoPlus平台和第三方系统之间的事件驱动的互操作机制,简而言之,就是流程的钩子/回调。
- 事件机制/Events涵盖流程处理的全生命期,包括从流程创建/结束/终止、各步骤办理/打印/草稿/撤回、界面的每个控件的变化/感知等。
- 开发者可以在这些回调中控制界面显示、检查操作的合法性、以及实现对自身业务系统的数据存取和接口调用等。
- 技术上,Messenger和InfoPlus平台之间可通过REST/WebService等不同方式进行交互,具体实现上并不限制技术框架,但建议上请参考/使用我们的InfoPlus SDK来简化开发过程。
- 流程开发者可以在流程编辑器中定义Messenger程序的访问地址,并勾选要触发的事件,参考:
- 开发者可在messenger中开发各种事件的处理程序,具体实现因SDK而异,请具体参考sdk的开源代码和实现。
- 开发过程中需要的的数据模型,可通过IDE来生成,参考:
| 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 | 系统保留 | 无 |
- 此类事件改变的数据,只是影响前端的展示,并不会真正持久化,持久化发生在后续的保存或提交时
- RENDER事件经常被过度使用,但建议一些持久化的操作要提前
- 需要持久化的数据如果可以在上一步结束时产生的,请移到ACTION_DOING中
- 如果产生点有很多,可以使用SDK的封装来针对“达到此步”的事件写Messenger
- 此类事件的数据经过前台,所以要注意数据泄漏和篡改
- 引擎后台此类事件带过来的数据和用户手工输入的一视同仁,所以如果希望持久化必须有足够权限
- 重复节如果权限不足,会报FORM_DATA_UNAUTHORIZED的异常;字段无权限会被简单的忽略
- 在表单提交时,如有必要,需要对带过来的数据做合法性检查或者二次赋值

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);
}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;
}
}{
// 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}
}
}