动态数据源
通过本文你可以了解到卡片动态数据源的使用和常见问题
说明
在使用动态数据源之前,建议您已经完成了以下的准备工作:
- 了解卡片模板搭建及发布过程。
- 了解卡片平台创建卡片实例流程和开放接口创建卡片实例流程。
- 了解卡片平台投放卡片实例流程和开放接口投放卡片实例流程。
卡片中的数据来源
用户看到一张互动卡片的时候,卡片中的数据来源可以分为如下几类:
获取时机 | 更新方式 | |
---|---|---|
卡片公有数据(卡片所有接收人可见) | 卡片实例创建时 | 调用更新接口变更 |
卡片私有数据(卡片某个接收人可见) | 卡片实例创建时 | 调用更新接口变更 |
动态数据源数据 | 卡片在客户端渲染时 | 回调动态数据源提供方拉取时变更 |
动态数据源的适用场景
动态数据源主要在以下场景中使用:
- 部分卡片数据比较敏感,不能托管到卡片服务端(如发票金额、抬头等)
- 部分卡片数据需要根据场景千人千面, 私有数据不能满足诉求 (如查看员工OKR时根据权限隐藏等场景)
- 部分卡片数据需要定时拉取,如一些需要定期更新的报表(如每日销售额、业务趋势图等)
数据源交互流程
动态数据源接入方式
动态数据源的接入方式有两种,一种可以通过卡片搭建平台,在后台创建卡片实例时绑定数据源,一种可以通过开放接口接入的方式完成。
卡片搭建平台接入
如果卡片的创建是卡片平台创建卡片实例的方式完成,可以在卡片平台创建卡片实例 > 步骤二:创建卡片实例 > 完成数据配置实现动态数据源接入。
开放接口接入
我们以一张最简单的发票金额动态拉取为例,通过接口接入需要如下几个步骤:
-
调用服务端API-注册卡片回调地址,注册动态数据源的回调地址。
-
调用服务端API-创建卡片接口,实现动态数据源
openDynamicDataConfig
参数的配置。在开放接口创建卡片实例中,动态数据源相关配置参数
openDynamicDataConfig
,通过简单实例,解释对应参数的含义:说明
针对
openDynamicDataConfig
参数的说明:dynamicDataSourceConfigs
: 动态数据源配置列表,一个卡片实例中可以包含多个动态数据源配置dynamicDataSourceId
: 动态数据源的 ID,要和下文介绍的动态数据响应一一对应pullConfig
: 动态数据源获取配置,分为拉取策略和根据拉取策略的不同配置参数:pullStrategy
: 拉取策略ONCE
:仅获取一次INTERVAL
:定时获取,见「高级功能-定时拉取」
HTTP
POST /v1.0/card/instances HTTP/1.1
Host:api.dingtalk.com
x-acs-dingtalk-access-token:String
Content-Type:application/json
{
"userId" : "user123",
"userIdType": 1,
"cardTemplateId" : "abcd-1234",
"outTrackId" : "my-out-trarck-id",
"cardData" : {
"cardParamMap" : {
"title": "张三提交的报销单",
"type": "差旅费",
"reason": "出差费用",
"status": "未审批",
"amount": "" //需要通过动态数据源获取的数据的字段,可以为空
}
},
"openDynamicDataConfig":{
"dynamicDataSourceConfigs":[
{
"dynamicDataSourceId":"example_ds_1", //数据源id
"pullConfig": {
"pullStrategy": "ONCE" //只需要获取一次
}
}
]
}
}
-
实现动态数据拉取接口。
HTTP 请求格式实例:
当卡片进行动态数据源回调时,会向注册的 URL 地址发送一个 POST 请求,请求内容示例如下所示:
{
"type": "dynamicDataCallback", // 标识回调类型为动态数据源
"corpId": "corp1234", // 触发人的企业 ID
"userId": "user0", // 触发人的 userId
"outTrackId": "testOutTrackId", // 卡片 ID
"content": "{\"dynamicDataSourceQueryRequests\": [{\"dynamicDataSourceId\": \"example_ds_1\"}}"
}
说明
content:这是一个 Json String,解析后为如下样式:
"content": {
"dynamicDataSourceQueryRequests": [
{
"dynamicDataSourceId": "example_ds_1" // 请求动态数据源 ID
}
]
}
安全性校验:
为了提升回调接口的安全性,从钉钉侧发起的HTTP回调请求,支持开发者进行来源校验。
如注册卡片回调地址时提供了“卡片数据回调apiSecret”,则收到的HTTP请求Header中包含签名相关Header:
• x-ddpaas-signature-timestamp:签名时间戳
• x-ddpaas-signature:签名串
其中 <签名串> = calcSignature(apiSecret, <签名时间戳>),apiSecret是配置时指定的“卡片数据回调Secret”
接口提供方应使用如下方法计算签名并验证签名串是否正确以防未授权的调用:
public static String calcSignature(String apiSecret, long ts) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec key = new SecretKeySpec(apiSecret.getBytes(), "HmacSHA256");
mac.init(key);
return Base64.getEncoder()
.encodeToString(mac.doFinal(Long.toString(ts).getBytes()));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new GatewayException(ErrorCodeConstant.SYSTEM_ERROR,
"sign api secret failed", e);
}
}
HTTP 返回格式示例:
在处理完动态数据源回调请求后,需要返回回调响应,来更新卡片上的动态数据。响应内容示例如下所示:
{
"dataSourceQueryResponses": [
{
"data": "{\"amount\":\"1000元\"}", // 返回的动态数据,端上直接覆盖并渲染
"dynamicDataSourceId": "example_ds_1", // 动态数据源 ID
"dynamicDataValueType": "OBJECT" // 动态数据的类型,支持 STRING、ARRAY、OBJECT 等
}
]
}
至此,一个简单的拉取一次动态数据的动态卡片就已经创建并加载完成了。
动态数据拉取前 | 动态数据拉取后 |
---|---|
![]() | ![]() |
高级功能
定时拉取
在前面动态数据源的介绍和示例中,我们将 pullStrategy 设置为了 ONCE,这代表着卡片只会在第一次被用户查看的时候触发拉取动态数据源。
互动卡片支持对动态数据源的定时拉取策略。如果打算让卡片每 30 秒拉取 amount 的值,可以进行调用服务端API-创建卡片接口进行如下的配置:
HTTP
POST /v1.0/card/instances HTTP/1.1
Host:api.dingtalk.com
x-acs-dingtalk-access-token:String
Content-Type:application/json
{
"userId" : "user123",
"userIdType": 1,
"cardTemplateId" : "abcd-1234",
"outTrackId" : "my-out-trarck-id",
"cardData" : {
"cardParamMap" : {
"title": "张三提交的报销单",
"type": "差旅费",
"reason": "出差费用",
"status": "未审批",
"amount": "" //需要通过动态数据源获取的数据的字段,可以为空
}
},
"openDynamicDataConfig":{
"dynamicDataSourceConfigs":[
{
"dynamicDataSourceId":"example_ds_1", // 动态数据源 ID
"pullConfig": {
"pullStrategy": "INTERVAL", // 间隔拉取
"timeUnit": "SECONDS", // 拉取间隔时间的单位
"interval": "30" // 拉取间隔时间
}
}
]
}
}
说明
- timeUnit:拉取间隔时间的单位,支持 SECONDS、MINUTES、HOURS、DAYS
- interval:拉取间隔,如果 timeUnit 为 SECONDS 时,interval 最小值为 6,即拉取间隔最小为 6 秒
多数据源
如果卡片中有多个敏感数据,并且这些敏感数据可能归属于不同的服务。那么,我们可以选择在调用服务端API-创建卡片接口同时创建多个动态数据源。
HTTP
POST /v1.0/card/instances HTTP/1.1
Host:api.dingtalk.com
x-acs-dingtalk-access-token:String
Content-Type:application/json
{
"userId" : "zhangsan",
"userIdType": 1,
"cardTemplateId" : "exmample_template",
"outTrackId" : "my-out-trarck-id",
"cardData" : {
"cardParamMap" : {
"title": "张三提交的报销单",
"type": "差旅费",
"reason": "出差费用",
"status": "未审批",
"amount": "", //需要通过动态数据源获取的数据的字段,可以为空
"destination":"" //需要通过动态数据源获取的数据的字段,可以为空
}
},
"openDynamicDataConfig":{
"dynamicDataSourceConfigs":[
{
"dynamicDataSourceId":"example_ds_1", // 动态数据源 ID
"pullConfig": {
"pullStrategy": "INTERVAL", // 间隔拉取
"timeUnit": "SECONDS", // 拉取间隔时间的单位
"interval": "30" // 拉取间隔时间
}
},
{
"dynamicDataSourceId": "example_ds_2", // 动态数据源 ID
"pullConfig": {
"pullStrategy": "ONCE", // 只拉取一次
}
}
]
}
}
如上例所示,amount 和 destination 是两类不同的敏感数据,动态数据源 example_ds_1 会返回 amount 的数据,而动态数据源 example_ds_2 会返回 destination 的数据。
多个动态数据源配置下,回调请求如下所示:
{
"type": "dynamicDataCallback", // 标识回调类型为动态数据源
"corpId": "corpId", // 触发人的企业 ID
"userId": "zhangsan", // 触发人的 userId
"outTrackId": "my-out-trarck-id", // 卡片 ID
"content": "{\"dynamicDataSourceQueryRequests\": [{\"dynamicDataSourceId\": \"example_ds_1\"},{\"dynamicDataSourceId\": \"example_ds_2\" }]}"
}
说明
content:这是一个 Json String,解析后为如下样式:
"content": {
"dynamicDataSourceQueryRequests": [
{
"dynamicDataSourceId": "example_ds_1" // 请求动态数据源 ID
},
{
"dynamicDataSourceId": "example_ds_2" // 请求动态数据源 ID
}
]
}
回调响应如下所示:
{
"dataSourceQueryResponses": [
{
"data": "{\"amount\":\"1000元\"}", // 返回的动态数据
"dynamicDataSourceId": "example_ds_1", // 动态数据源 ID
"dynamicDataValueType": "OBJECT" // 动态数据的类型,支持 STRING、ARRAY、OBJECT 等
},
{
"data": "{\"destination\":\"杭州\"}", // 返回的动态数据
"dynamicDataSourceId": "example_ds_2", // 动态数据源 ID
"dynamicDataValueType": "OBJECT" // 动态数据的类型,支持 STRING、ARRAY、OBJECT 等
}
]
}