# 流程事件推送开发指南
# 功能介绍
七巧低码开发平台面向第三方系统提供了流程引擎数据的主动推送机制,可用于实现当流程发起、更新、节点流转、完成场景下的数据推送,满足集成场景需求。
# 使用教程
# 配置指南
# 配置事件推送
打开低码中心>>OpenAPI>>API管理>>推送管理
配置URL和Secret,并设置对应表单的触发类型
# URL有效性鉴权
POST http://example.com/qiqiao/hook?timestamp=1498586609
Headers:{
"Content-type":"application/json",
"X-Auth0-DeliverId":"31a2ae17-2661-4234-8d79-f62f3175fd75",
}
Body:{
"eventType":"URL_VERIFY",
"data": "随机字符串"
}
Response: {
"msg":"执行成功",
"code": 0,
"data": {
"token" : "zvmPTQAcEmxGbMoOLPjpoCPk/Oh096ZaORQ8o2iu8Cg="
}
校验流程如下:
第三方业务系统事件接口接收到body里面的一个随机字符串,使用上述设置的Secret进行加密并生成Base64的签名,直接响应返回;系统用同样条件生成的签名与第三方系统生成的签名一致时,表示URL校验通过;
# 响应规则
向对应目标服务器推送请求后,该服务器需在20秒内使用 2xx 作为响应的状态码进行响应,即认为数据推送成功。
一次推送重试最多5次,如果单次推送连续重试5次均失败,则该次推送失败。
# 开发注意点
为了保证每个对接的应用服务能够接受到推送数据时,不出现异常,所以请在接收到eventType参数的数据类型时,直接响应成功,不要响应为失败。
# 开发指南
# 推送方式
使用POST请求,在请求的内容如下:
POST
http://qiqiao.do1.com.cn/plus/cgi-bin/v1/qiqiao/hook?timestamp=1498586609
Headers:{
"Content-type":"application/json",
"X-Auth0-DeliverId":"31a2ae17-2661-4234-8d79-f62f3175fd75",
}
Body:{
"id": "8349253077296234501",
"eventType": "UPDATE_EVENT",
"applicationId": "6597b0a4d62af3357477f54b",
"formModelId": "6597b0acd62af3357477f54e",
"processModelId": "659e717fd62af3357477f565",
"nodeName": "人工任务",
"eventPushTime": 1706668846230,
"tenantId": "9134167922923ba09ad18872b2b1246e",
"processVersion": "2",
"data": "zvmPTQAcEmxGbMoOLPjpoCPk/Oh096ZaORQ8o2iu8Cg="
}
Response: {
"msg":"执行成功",
"code": 0,
"data": {}
}
加密结构
{
"id": "8349253077296234501",
"eventType": "UPDATE_EVENT",
"applicationId": "6597b0a4d62af3357477f54b",
"formModelId": "6597b0acd62af3357477f54e",
"processModelId": "659e717fd62af3357477f565",
"nodeName": "人工任务",
"eventPushTime": 1706668846230,
"tenantId": "9134167922923ba09ad18872b2b1246e",
"processVersion": "2",
"data": {
"formInstanceId": "8349252493180682240",
"processInstanceId": "8349253042936496128",
"executeUserName": "刘雪花",
"executeUserId": "2491c631357e75c6df90fa2f4388af15",
"executeTime": 1706668845447,
"status": "审批中"
}
}
参数说明:
字段 | 字段类型 | 字段说明 |
---|---|---|
formInstanceId | string | 表单实例ID |
processInstanceId | string | 流程实例ID |
executeUserName | string | 事件触发用户名称 |
executeUserId | string | 事件触发用户ID |
executeTime | Long | 事件触发时间 |
status | string | 流程当前状态 |
# 请求头包含字段说明
X-Auth0-DeliverId :消息推送Id,每次推送的id是唯一的,可以通过该字段完成请求去重,防止重复请求;
# 请求包体字段说明
参数名 | 是否必须 | 字段类型 | 参数说明 | 备注 |
---|---|---|---|---|
id | 是 | string | 全局唯一推送id | 与(X-Auth0-DeliverId)相同值 |
eventType | 是 | String | 事件类型 | 类型名称(类型标识): 发起事件:INITIATE_EVENT |
更新事件:UPDATE_EVENT
节点事件:NODE_EVENT
完成事件:COMPLETE_EVENT) |
| applicationId | 是 | string | 应用Id | 此处表示应用模型Id |
| formModelId | 是 | string | 表单模型id | 此处表示表单模型Id |
| processModelId | 是 | string | 流程模型id | 此处表示流程模型Id |
| nodeName | 否 | string | 流程节点名称 | 当前停留的流程节点,为空则流程结束 |
| eventPushTime | 是 | Long | 事件推送时间 | |
| tenantId | 是 | string | 租户ID | 也是(机构ID) |
| processVersion | 是 | string | 流程版本 | |
| data | 是 | string | 消息内容 | 内容进行对称加密后转换为base64的字符串 |
# 加密算法
# 1.2.1 AES对称加密
加密模式:ECB
填充:pkcs7
数据块:128位
密钥:用户设置的token字符串
字符集:utf8
# 加密内容输出方式
将加密后的内容通过base64字符串方式输出
# 加密算法示例
# 添加依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
# 加密工具类
public final class EncryptUtils {
/**
* 加密算法
*/
private static final String ENCRY_ALGORITHM = "AES";
/**
* 加密算法/加密模式/填充类型
* 采用AES加密,ECB加密模式,PKCS7Padding填充
*/
private static final String CIPHER_MODE = "AES/ECB/PKCS7Padding";
/**
* 设置加密字符集
* 采用 UTF-8 字符集
*/
private static final String CHARACTER = "UTF-8";
private EncryptUtils() {
}
/**
* AES加密
*
* @param content 加密内容
* @param encryptKey 加密密钥
* @return
*/
private static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
if (StringUtils.isEmpty(content)) {
log.error("The encrypted content must not be null!");
throw new EncryptContentNullException("The encrypted content must not be null!");
}
SecretKey key = getKey(encryptKey);
log.info("密钥:{}", cn.com.do1.qiqiao.webhook.encrypt.utils.Base64.encode(key.getEncoded()));
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(content.getBytes(CHARACTER));
}
/**
* 生成密钥
*
* @param strKey
* @return
*/
public static SecretKey getKey(String strKey) {
try {
KeyGenerator generator = KeyGenerator.getInstance(ENCRY_ALGORITHM);
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(strKey.getBytes());
generator.init(128, secureRandom);
return generator.generateKey();
} catch (Exception e) {
throw new RuntimeException(" 初始化密钥出现异常 ");
}
}
/**
* AES解密
*
* @param encryptBytes 待解密的byte[]
* @param decryptKey 解密密钥
* @return 解密后的String
* @throws Exception
*/
private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
SecretKey key = getKey(decryptKey);
log.info("密钥:{}", cn.com.do1.qiqiao.webhook.encrypt.utils.Base64.encode(key.getEncoded()));
// 添加加密模式
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(encryptBytes), CHARACTER);
}
/**
* base 64 encode
*
* @param bytes 待编码的byte[]
* @return 编码后的base 64 code
*/
private static String base64Encode(byte[] bytes) {
try {
log.info("加密后的内容:{}", bytes);
return new String(Base64.encode(bytes), CHARACTER);// new BASE64Encoder().encode(bytes);
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage());
}
return null;
}
/**
* base 64 decode
*
* @param base64Code 待解码的base 64 code
* @return 解码后的byte[]
* @throws Exception
*/
private static byte[] base64Decode(String base64Code) throws Exception {
return Base64.decode(base64Code.getBytes(CHARACTER));
}
/**
* AES加密为base 64 code
*
* @param content 待加密的内容
* @param encryptKey 加密密钥
* @return 加密后的base 64 code
* @throws Exception
*/
public static String aesEncrypt(String content, String encryptKey) throws Exception {
return base64Encode(aesEncryptToBytes(content, encryptKey));
}
/**
* 将base 64 code AES解密
*
* @param encryptStr 待解密的base 64 code
* @param decryptKey 解密密钥
* @return 解密后的string
* @throws Exception
*/
public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
return StringUtils.isBlank(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
}
}