1. 概述
1.1 应用场景
接口取数时,部分接口存在限制,每次分页请求都必须携带签名/Token,而签名/Token 无法通过接口直接获取,只能由用户手动生成。
当用户需要实现分页循环 +日期等多个维度的循环时,由于循环容器暂不支持嵌套,仅靠简单的循环容器无法满足需求。
本文案例,进行取数的接口信息如下:
1)请求参数
请求方式:GET

签名时需要用到页码参数,因此在做分页时无法简单用 FineDataLink 自带的分页取数,需要通过循环容器才能实现分页效果;另外,还需要循环遍历日期范围取数。
2)返回数据

1.2 实现思路

1)「参数列表」中先新建参数 pageNum 和 date_count。
pageNum:页数,先暂定 1,生成第一次循环的签名,会在后续步骤中对该参数重新赋值。
data_count:循环遍历的日期范围数量,作为循环容器的判断条件,当值大于 1 时,标识仍有日期范围的数据没取完,需要继续循环;先暂定1,保证开启第一次循环,会在后续步骤中对该参数重新赋值。
2)构建日期序列,输出日期范围,即多组 starttime 和 endtime 值,并保存到临时表中。
3)循环容器节点中:
按照 starttime 升序排列,取出第一行日期范围,即第一次循环用到的开始时间和结束时间,作为参数输出。
计算出签名,并作为参数输出。
接口取数,如果返回数据为空,进入下次循环,此时删除第二步中时间范围第一行数据,pageNum 重置为1,重新计算date_count 值;如果返回数据非空,从接口取数,如果第一页已取出全部数据,进入下次循环,如果未取出全部数据,pageNum+1,再次取数。
2. 操作步骤
2.1 设置静态参数
1)新建定时任务,进入定时任务。
2)点击「参数列表」,新增两个静态参数 pageNum,data_count,参数值设置为 1。如下图所示:
pageNum:页数,先暂定 1,生成第一次循环的签名,会在后续步骤中对该参数重新赋值。
data_count:循环遍历的日期范围数量,作为循环容器的判断条件,当值大于 1 时,标识仍有日期范围的数据没取完,需要继续循环;先暂定1,保证开启第一次循环,会在后续步骤中对该参数重新赋值。

2.2 遍历日期输出为参数
本文示例中,要取出 2010 年至 2024 年的数据;由于时间范围久,包含的数据量很大,直接一次性取数的话容易超时或者触发 fdl64m 取数限制,分批取数更加适合。
1)拖入「数据同步」节点,生成每次循环容器取数时使用的开始日期和结束日期值。如下图所示:
WITH RECURSIVE years AS (
SELECT 2010 AS y
UNION ALL
SELECT y + 1 FROM years WHERE y < 2024
)
SELECT
CONCAT(y, '-01-01') AS starttime,
CONCAT(y, '-12-31') AS endtime
FROM years
ORDER BY y;

2)将取出的数据写入到临时表中。如下图所示:

2.3 循环容器设置
拖入「循环容器」,循环方式选择「条件循环」,执行条件设置为${date_count}>0。如下图所示:
{date_count}:循环遍历的日期范围数量,大于 0 时,表示还有数据未取完。

2.4 指定循环的日期范围
1)「循环容器」节点内,拖入「参数赋值」节点,2.2 节输出的日期范围,按照 starttime 升序排列,取出第一个日期范围数据。
SELECT * FROM `Raine.Huang`.`日期list_tmp`
ORDER BY starttime ASC
LIMIT 1;
2)将 starttime、endtime输出为参数,便于后续从接口取出该时间范围的数据。如下图所示:
任务设计时,为便于后续节点预览数据,建议给这两个参数赋予调试值。

2.5 生成签名
从本文 1.1 节接口信息介绍中,我们了解到,签名需要当前时间戳、projectId、pageNum。


1)拖入「数据转换」节点,进入「数据转换」节点。
2)拖入「Spark SQL」节点,获取当前时间戳。
SELECT unix_millis(current_timestamp()) AS ts;

3)拖入「参数输出」节点,将当前时间戳输出为参数。如下图所示:

4)再拖入一个「数据转换」节点,进入「数据转换」节点。
5)拖入「Spark SQL」节点,计算签名。
SELECT
HMACMD5(
CONCAT(ts, 'fb8964fc-1150-4c21-8d4c-8badd32db135', 'pageNum'),
'c023481ee30d33a7c7223a7b6252b419',
'HEX'
) AS sign

点击「数据预览」:

6)拖入「参数输出」算子,将签名输出为参数:

2.6 判断本次时间范围内是否有数据
实际接口取数时,不是每个时间范围内都有数据,如果数据为空,接口本身不会报错,JSON 解析后会生成一行 Null 值数据同步到目标数据库中,如果目标表设置主键,会产生脏数据直接报错。
所以需要提前判断取数时间范围内使用当前页码,是否返回空数据(接口返回数据中,rowCount 是否为0)。

1)拖入「参数赋值」节点,调用接口取数:

将返回值展开为二维表:

点击「数据预览」,如下图所示:

2)将 rowCount 输出为参数:

3)拖入「条件分支」节点,「条件分支」节点后拖入「数据转换」节点和「SQL脚本」节点。
若 rowCount>0,执行「数据转换」节点进行接口取数。
若 rowCount为0,执行「SQL脚本」节点执行下次循环。

2.7 接口取数以及下次循环准备

2.7.1 rowCount为0
若取数时间范围内使用当前页码返回数据为空,可进入下次循环。
1)日期范围临时表中(本文 2.2 节),按照 starttime 升序排列,删除第一条时间范围数据,下次循环使用第二条时间范围取数。
DELETE FROM `Raine.Huang`.`日期list_tmp`
ORDER BY starttime ASC
LIMIT 1;

2)拖入「参数赋值」节点,起始页码置为 1,并输出为同名参数 pageNum(值覆盖 2.1 节同名静态参数)。
容器内参数优先级最高,优先级大于其他所有容器外参数和内置的参数。

3)拖入「参数赋值」节点,计算 2.2 节临时表中,日期范围的数量,输出为参数 date_count(值覆盖 2.1 节同名静态参数,该参数作为循环容器节点的执行条件判断)。
容器内参数优先级最高,优先级大于其他所有容器外参数和内置的参数。
若 date_count >0,表示仍存在某个时间范围,未进行取数,需要进入下一次循环。

2.7.2 rowCount >0

1)进入「数据转换」节点,拖入「API输入」算子,进行接口取数:

将返回值展开为二维表:

点击「数据预览」,如下图所示:

2)拖入「JSON解析」算子,解析数据:

3)拖入「DB表输出」算子,将解析后的数据输出到目标表中。

4)从本文 1.1 节接口信息中,我们了解到,调用接口后,返回的rowCount,即每页固定条数为 1000,若小于1000,表示数据已经取完,若等于 1000,表示本页取数未取完,需要在下一页进行取数。
所以,拖入「条件分支」节点,如果 rowCount<1000,进入 2.7.1 节流程,即「条件分支」节点后接入「SQL脚本」节点。

如果 rowCount=1000,拖入「参数赋值」节点,给 pageNum 参数值加1后,重新再赋值给 pageNum 参数,从下一页开始取数。

