# 一、功能场景

  1. 客户期望自己全部由定义自己流程按钮功能,比如加签
  2. 在流程按钮提交前做一些前置交互,比如客户期望在点击按钮后弹出一个流程预测的弹框,在预测完后点击确定再触发流程的功能。
应用客户及业务组 需求场景 应用场景
中建 平铺选人加签后需要在提交动作之前做校验 行为执行之前校验
中海 流程提交前进行流程预测 行为执行之前校验
内部公文组 流程提交之后需要跳转页面 行为执行之后校验

支持的功能场景: ● 支持流程行为之前、行为执行之后做一些第三方的客开能力。 ● 支持完全客开bpm流程类按钮,完全劫持bpm操作按钮执行效果。 ● 支持获取bpm流程的相关信息 ● 目前仅支持3.16及以后的版本

支持版本:3.16.0及以上

# 二、应用场景

针对客户需求,流程操作存在如下应用场景

流程操作场景 是否存在多个劫持情况
行为执行之前 存在
行为执行之后 存在
完全拦截 不存在(完全拦截就纯粹完全是客户定制开发了,这种场景多个拦截会被完全拦截替换)

多个注册拦截行为可能会存在如下问题:

  1. 多个行为拦截的先后顺序问题。
  2. 行为拦截校验后的后续执行问题。

为了解决上述问题,所以实现了如下的客开技术方案

# 三、客开技术方案

支持的功能场景: ● 支持流程行为之前、行为执行之后做一些第三方的客开能力。 ● 支持完全客开bpm流程类按钮,完全劫持bpm操作按钮执行效果。 ● 支持获取bpm流程的相关信息 ● 目前仅支持3.16及以后的版本 整体大致实现客开的代码如下:

import { eventEmit } from '@seeyon/global';
type Dispatch = ({ type, payload }: {
    type: TField;
    payload: unknown;
}) => unknown
 
type BpmSdk = {
    countersignInfo: CountersignInfo;
    setFormExtraParam: (key: string, value: any) => void;
    syncSignInfo: (countersignParam: SyncSignParams) => void;
    getBpmState: (key?: string) => any;
    setBpmState: Dispatch;
}
 
interface EventParams {
  /** 需要自定义的流程行为 */
  actions: string[];
  /** 执行校验 **/
  func: (bpmAction: BpmAction, bpmSdk: BpmSdk ) => Promise<any>;
  type: 'AFTER_ACTION' | 'BEFORE_ACTION';
  /** 执行顺序*/
  order: number;
}
 
eventEmit(eventName, {
    // 具体需要拦截的流程行为
    actions: ['NODE_ADD'],
    func: (bpmAction, bpmSdk) => {
        return new Promise((resolve)=> resolve('success'));
    },
    type: 'BEFORE_ACTION', // 必传
    order: 1,  // 校验顺序
});

● actions 具体需要的注册进来的拦击行为,见code表 ● func 具体需要执行的校验方法,当返回的Promise是resolve状态就会往后执行,reject状态就不会往后执行了 ● type 指定行为执行的类型,行为执行之前:BEFORE_ACTION,行为执行之后:AFTER_ACTION; ● order: 可选,注册多个的校验行为时,根据传递的执行顺序来执行,不传就按注册的属性来传递。

# 四、业务系统内部扩展

BPM流程行为由第三方实现,实现客开功能,比如加签。

# 1. 行为执行之前拦截

如果需要再继续使用bpm的能力,则执行promise函数中的resolve方法继续执行。如果内部行为执行之前校验失败则执行promise函数中的reject函数,则后续不会再执行。

行为执行之前的大致执行顺序如下: 1747273781682.jpg

import { eventEmit, BpmPageContext } from '@seeyon/global';
  
type Dispatch = ({ type, payload }: {
    type: TField;
    payload: unknown;
}) => unknown
 
type BpmSdk = {
    countersignInfo: CountersignInfo;
    setFormExtraParam: (key: string, value: any) => void;
    syncSignInfo: (countersignParam: SyncSignParams) => void;
    getBpmState: (key?: string) => any;
    setBpmState: Dispatch;
}
 
 
interface EventParams {
  /** 需要自定义的流程行为 */
  actions: string[];
  /** 执行校验 **/
  func: (bpmAction: BpmAction, bpmSdk: BpmSdk ) => Promise<any>;
  type: 'AFTER_ACTION' | 'BEFORE_ACTION';
  /** 执行顺序*/
  order: number;
}

let count = 0;

const Demo = ()=> {
  const { bpmEventId, store } = useContext(BpmPageContext);
  
  useEffect(() => {
    const eventName = `CUSTOM_ACTION_${bpmEventId}`;
    eventEmit(eventName, {
      // 具体需要拦截的流程行为
      actions: ['NODE_ADD'],
      func: (bpmAction, bpmSdk) => {
        if(count > 2) {
          return new Promise((resolve)=> resolve('success'));
        }
        count++;
        return new Promise((resolve, reject)=> reject('reject'));
      },
      type: 'BEFORE_ACTION', // 必传
      order: 1,  // 校验顺序
    });
  }, [bpmEventId]);
}

# 2. 完全拦截流程行为

完全拦截流程行为可以由行为执行之前的方案去实现,完全拦截则可以通过返回函数reject来实现完全客开的功能。

import { useContext, useEffect } from 'react'
import { eventEmit, BpmPageContext } from '@seeyon/global'
 
const PageDemo = () => {
  const { bpmEventId } = useContext(BpmPageContext);
 
  useEffect(() => {
    const eventName = `CUSTOM_ACTION_${bpmEventId}`;
    eventEmit(eventName, {
      // 具体拦截的流程行为
      actions: ['SUBMIT'],
      func: (bpmAction, bpmSdk) => {
        // bpmAction,当前流程操作的行为信息,如加签标识等
        // 第三方自定义扩展行为
        return new Promise((resolve, reject)=> reject("自定义完全拦截"))
      },
      order: 1,
    });
  }, [bpmEventId]);
};

在传递的func函数中执行客户自己的业务逻辑。

# 3. 行为执行之后拦截

行为执行之后的拦截如果存在校验失败或拦截不往后执行,会影响页面关闭逻辑,需要客开自行根据校验情况来resolve,如果不resolve则需要第三方自行控制关闭页面逻辑。

行为执行之后的执行顺序如下: 1747274170871.jpg


import { eventEmit, BpmPageContext } from '@seeyon/global';
 
type Dispatch = ({ type, payload }: {
    type: TField;
    payload: unknown;
}) => unknown
 
type BpmSdk = {
    countersignInfo: CountersignInfo;
    setFormExtraParam: (key: string, value: any) => void;
    syncSignInfo: (countersignParam: SyncSignParams) => void;
    getBpmState: (key?: string) => any;
    setBpmState: Dispatch;
}
 
interface EventParams {
  /** 需要自定义的流程行为 */
  actions: string[];
  /** 执行校验 **/
  func: (bpmAction: BpmAction, bpmSdk: BpmSdk ) => Promise<any>;
  type: 'AFTER_ACTION' | 'BEFORE_ACTION';
  /** 执行顺序*/
  order: number;
}
 
const Demo = ()=> {
  const { bpmEventId, store } = useContext(BpmPageContext);
  
  useEffect(() => {
    const eventName = `CUSTOM_ACTION_${bpmEventId}`;
    eventEmit(eventName, {
      // 具体需要拦截的流程行为
      actions: ['NODE_ADD'],
      func: (bpmAction, bpmSdk) => {
        // 如果函数reject掉,则不会再往后执行
        return new Promise((resolve, reject)=> resolve('success'));
      },
      type: 'AFTER_ACTION', // 必传
      order: 1,  // 校验顺序
    });
  }, [bpmEventId]);
}

# 五、业务系统外部扩展

第三方插入一段js到index.html中,然后想客开bpm的能力。则可以通过以下方式来实现。

# 1. 行为执行之前拦截

const fn = (event)=>{
    const msgData = event.data;
    if(msgData.type === 'SUB_PAGE_MESSAGE') {
    // 流程相关信息
    const { bpmEventId } = msgData || {};
    // 自定义流程行为事件名称
    const eventName = `CUSTOM_ACTION_${bpmEventId}`;
    // 根据业务方需求自定义拦截流程行为
    window.SeeyonGlobal.eventEmit(eventName, {
        // 具体拦截的流程行为
        actions: ['NODE_ADD'],
        type: 'BEFORE_ACTION', // 必传
        order: 1,  // 校验顺序
        func: (bpmAction, bpmSdk) => {
            return new Promise((resolve)=> resolve('success'));
        },
     });
   }
};
 
window.addEventListener('message', fn);

# 2. 行为执行之前拦截

const fn = (event)=>{
    const msgData = event.data;
    if(msgData.type === 'SUB_PAGE_MESSAGE') {
    // 流程相关信息
    const { bpmEventId } = msgData || {};
    // 自定义流程行为事件名称
    const eventName = `CUSTOM_ACTION_${bpmEventId}`;
    // 根据业务方需求自定义拦截流程行为
    window.SeeyonGlobal.eventEmit(eventName, {
        // 具体拦截的流程行为
        actions: ['NODE_ADD'],
        type: 'BEFORE_ACTION', // 必传
        order: 1,  // 校验顺序
        func: (bpmAction, bpmSdk) => {
            return new Promise((resolve, reject)=> reject('自定义行为'));
        },
     });
   }
};
 
window.addEventListener('message', fn);

# 3. 行为执行之后拦截

const fn = (event)=>{
    const msgData = event.data;
    if(msgData.type === 'SUB_PAGE_MESSAGE') {
    // 流程相关信息
    const { bpmEventId } = msgData || {};
    // 自定义流程行为事件名称
    const eventName = `CUSTOM_ACTION_${bpmEventId}`;
    // 根据业务方需求自定义拦截流程行为
    window.SeeyonGlobal.eventEmit(eventName, {
        // 具体拦截的流程行为
        actions: ['NODE_ADD'],
        type: 'AFTER_ACTION', // 必传
        order: 1,  // 校验顺序
        func: (bpmAction, bpmSdk) => {
            return new Promise((resolve)=> resolve('success'));
        },
     });
   }
};
 
window.addEventListener('message', fn);

# 六、流程行为code表

上面拦截行为中的actions取值,参考code表。

操作类型 (Code) 描述 (Description)
SEND 发送
SUBMIT 提交
READ 已阅
AGREE 同意
DIS_AGREE 不同意
TEMPORARY 暂存
DRAFT_HANDLE 保存
BACK 回退
UNDO 撤销
TERMINATION 终止
TRANSFER 移交
AFFAIR_CONSULT 咨询
RETRIEVE 取回
NOTICE 知会
GIVE 会签
PROCESS_SHOW 查看流程图
DELETE_DRAFT 删除待发
MULTI_COOPERATE 多人填写
PAUSE 挂起
TRACK 关注
编撰人:wensl、xuxyyf