前言:有关接口数据的接入,源端会给予我们一份接口说明文件,接着我们需依据对方提供的接口文档进行代码编写,以实现数据落表。
接入步骤大致可分为以下几步:
1. 依据对方提供的接口文档,明确接口地址、请求方式、传参信息以及参数格式等。
2. 借助编写代码,获取接口数据内容。
3. 按照获取到的接口数据内容来创建表。
4. 将数据写入表内。
5. 待下游验证数据无误后发布上线。
接口文档示例(由源端提供):
1.接口信息
接口地址 :/interPost/queryInfo
接口名称:相亲数据接口
提供者:张三
请求方:李四
交互频率:T+1
接口说明:用于相亲数据的接收
2.请求方式
http请求方式:post
参数格式:json
测试接口地址:20.177.6.225:1818
2.1.接口请求
1)字段说明:
字段名 字段类型 字段名称 是否必传 update_time varchar2(1000) 更新时间 是 name varchar2(1000) 名称 是 bush varchar2(1000) bush 是 houki varchar2(1000) houki 是 yomee varchar2(1000) yomee 是
2)请求示例
{
"startdate":"yyyy-MM-dd",//起始时间
"enddate":"yyyy-MM-dd",//截止时间
}
3)返回结果说明
参数 参数名称 类型 是否必填 参数说明 code 状态码 string 是 1-成功 0-失败 msg 提示信息 string 是 data 数据集合 List
4)返回结果示例
{
"msg": "操作成功",
"code": "1",
"data": "加密数据" //响应data为数据集合,会做加密,解密后为数组格式[{},{},{},...]
}3.附件-加解密工具类:
// 加密 public String aesEncrypt(String aesKey, String data) throws Exception { System.out.println("AES 加密前明文:" + data); byte[] input = data.getBytes("UTF-8"); byte[] keyBytes = aesKey.getBytes(); SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] cipherText = cipher.doFinal(input); return byte2hex(cipherText); }
// 解密 public String aesDecrypt(String aesKey, String data) throws Exception { byte[] input = hex2byte(data); byte[] keyBytes = aesKey.getBytes(); SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key); byte[] cipherText = cipher.doFinal(input); return new String(cipherText, "UTF-8"); }
public static void main(String[] args) { Map<String,Object> params = new HashMap<String,Object>(); params.put("startDate", "2024-06-20"); params.put("endDate", "2024-06-20"); params.put("pageIndex", "1"); params.put("pageSize", "50"); AesUtil aes = new AesUtil(); try { String postContext = aes.aesEncrypt("f33b3faf1e90a3f2", JSON.toJSONString(params)); System.out.println("加密后:" + postContext); String deData = aes.aesDecrypt("f33b3faf1e90a3f2", postContext); System.out.println("解密后:" + deData); } catch (Exception e) { e.printStackTrace(); } }
接口文档说明:这是一份关于相亲数据接口的文档,包括接口信息、请求方式、返回结果说明以及加解密工具类
1.接口信息部分提供了接口地址、名称、提供者、请求方、交互频率和接口说明等基本信息。
2.请求方式为 http 的 post 方式,参数格式为 json。测试接口地址为 20.177.6.225:1818。
3.接口请求部分,字段包括 update_time、name、bush、houki 和 yomee,其中 update_time 和 name 为必填项。请求示例给出了一个包含起始时间和截止时间的 json 数据。
4.返回结果说明中,状态码 code 为 1 表示成功,0 表示失败,同时还有提示信息 msg 和数据集合 data。返回结果示例展示了成功的情况,其中 data 为加密数据。
5.文档还提供了附件中的加解密工具类,包括 aesEncrypt 和 aesDecrypt 两个方法,用于对数据进行加密和解密。在 main 方法中,通过设置参数并调用加密和解密方法,展示了加密和解密的过程。
接口解密数据示例(这个返回数据源端有可能不会提供,需要我们自己调用后打印出来,再根据这个返回的数据来建表):
{"msg":"操作成功","code":"1","data":"[{"update_time":"20240703","name":"namevlue1","bush":"bushvlue1","houki":"houkivlue1","yomee":"yomeevlue1"},{"update_time":"20240704","name":"namevlue2","bush":"bushvlue2","houki":"houkivlue2","yomee":"yomeevlue2"},{"update_time":"20240705","name":"namevlue3","bush":"bushvlue3","houki":"houkivlue3","yomee":"yomeevlue3"}]}
接口数据说明:这段接口数据是一个 JSON 格式的字符串,包含了一个对象,其中包含了“msg”、“code”和“data”三个键值对。
“msg”键的值为“操作成功”,表示操作成功。
“code”键的值为“1”,表示操作的代码。
“data”键的值为一个 JSON 数组,其中包含了三个对象。每个对象都包含了“update_time”、“name”、“bush”、“houki”和“yomee”五个键值对。
第一步:代码编写
接入代码示例(接收方自己写):
import com.aliyun.odps.*;
import com.aliyun.odps.account.AliyunAccount;
import com.aliyun.odps.data.Record;
import com.aliyun.odps.data.RecordWriter;
import com.aliyun.odps.tunnel.TableTunnel;
import org.json.JSONArray;
import org.json.JSONObject;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.time.format.DateTimeFormatter;
import java.time.LocalDate;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.Key;
import java.text.SimpleDateFormat;
import java.util.*;
public class text2 {
private static String pt = "";
private static String accessId = "这里替换成阿里云的accessId";
private static String accessKey = "这里替换成阿里云的accessKey";
private static String endPoint = "api地址";
private static String connTunnel = "连接阿里云隧道的 URL";
private static String odpsTable = "table_bak";
public static void main(String[] args) throws OdpsException, IOException {
AliyunAccount account = new AliyunAccount(accessId, accessKey);
Odps odps = new Odps(account);
odps.setEndpoint(endPoint);
odps.setDefaultProject("odps_p");
// 通过tunnel 进行数据读写
TableTunnel tunnel = new TableTunnel(odps);
tunnel.setEndpoint(connTunnel);
// 分区空间设置
PartitionSpec partitionSpec = new PartitionSpec();
partitionSpec.set("pt", pt);
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String importdate = sdf.format(d.getTime() - 84600000L);
partitionSpec.set("pt", importdate);
String tableSchema = odps.getDefaultProject();
// 获取table
Table table = odps.tables().get(odpsTable);
// 获取字段列表
List<Column> columnList = table.getSchema().getColumns();
List<String> columnListStr = new ArrayList<>();
for (int i = 0; i < columnList.size(); i++) {
columnListStr.add(columnList.get(i).getName());
}
for (int i = 0; i < columnList.size(); ++i) {
columnListStr.add(((Column) columnList.get(i)).getName());
}
System.out.println("字段个数:" + columnList.size());
boolean partitionExists = table.hasPartition(partitionSpec); //检查分区是否存在
if (partitionExists) {
table.deletePartition(partitionSpec);
table.createPartition(partitionSpec);
} else {
table.createPartition(partitionSpec);
}
// 创建阿里云上传管道
TableTunnel.UploadSession uploadSession = tunnel.createUploadSession(tableSchema, odpsTable, partitionSpec);
// 管道写入对象
Record product = uploadSession.newRecord();
// 管道写入方法
RecordWriter writer = uploadSession.openBufferedWriter();
//创建时间参数对象,生成当前时间
Calendar calendar = Calendar.getInstance();
//拿到T-1的日期数据
calendar.add(Calendar.DAY_OF_MONTH, -1);
//创建日期格式化对象
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
//格式化日期对象
String queryDate = dateFormat.format(calendar.getTime());
// 设置结尾日期参数
DateTimeFormatter formatter = DateFormatter.ofPattern("yyyy-MM-dd");// 定义一个日期时间格式化器
LocalDate queryDateEnd1 = LocalDate.parse(queryDate, formatter);// 将字符串解析为LocalDate对象
LocalDate queryDateEnd = queryDateEnd1.plusDays(1);// 在queryDate基础上+1
String queryDateEndstr = queryDateEnd.format(formatter);// 日期转字符
System.out.println("数据时间周期:" + queryDate + "到" + queryDateEndstr);
// 接口地址
String interfaceUrl = "/interPost/queryInfo";
// 测试接口地址
String testInterfaceUrl = "http://20.177.6.225:1818" + interfaceUrl;
System.out.println("打印一下接口地址:" + testInterfaceUrl);
// 业务参数
Map<String, Object> arguments = new HashMap<>();
arguments.put("startdate",queryDate);//这里也可以把日期写死:arguments.put("startdate","2024-07-22");
arguments.put("enddate",queryDateEndstr);//这里也可以把日期写死:arguments.put("enddate","2024-07-23);
System.out.println("打印一下传参:" + arguments);
// 发送请求并获取响应
JSONObject response = sendPostRequest(testInterfaceUrl, arguments);
// 打印响应数据
System.out.println("接口响应数据:" + response.toString());
// 解密响应数据
String decryptedData = decryptResponse(response.getString("data"));
System.out.println("解密响应数据:" + decryptedData);
JSONArray jsonArray = new JSONArray(decryptedData);
for (int i = 0; a < jsonArray.length(); a++) {
ArrayList<String> inputlist = new ArrayList<>();
JSONObject item = jsonArray.getJSONObject(i);
inputlist.add(item.getString("update_time"));
inputlist.add(item.getString("name"));
inputlist.add(item.getString("bush"));
inputlist.add(item.getString("houki"));
inputlist.add(item.getString("yomee"));
// 将数据写入
// 写入的方式为获取表字段,获取数据一起写入
listData = new ArrayList<>();
listData.addAll(inputlist);
for (int j = 0; j < columnList.size(); ++j) {
Object listDesc = listData.get(j);
if (listDesc == null) {
listDesc = "";
}
product.setString(columnListStr.get(j), listDesc.toString());
}
writer.write(product);
}
if (writer != null) {
try {
System.out.println("写入成功");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
uploadSession.commit();
System.out.println("提交成功,id名字为 ");
System.out.println("全部提交成功");
System.exit(0);
}
/**
* 连接接口并获取数据
*
* @param arguments 请求参数
* @return 接口响应的JSON对象
*/
public static JSONObject sendPostRequest(String url,Map<String,Object> arguments) {
try {
// 创建 URL 对象
URL obj = new URL(url);
// 打开连接
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// 设置请求方法为 POST
con.setRequestMethod("POST");
//设置请求头
con.setRequestProperty("Content-Type","application/json");
// 将参数转换为 JSON 格式
JSONObject jsonarguments = new JSONObject(arguments);
// 发送请求体
con.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
out.write(jsonarguments.toString());
out.flush();
out.close();
// 读取响应
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine())!= null) {
response.append(inputLine);
}
in.close();
// 将响应转换为 JSONObject
JSONObject jsonResponse = new JSONObject(response.toString());
return jsonResponse;
} else {
System.out.println("请求失败,响应代码:" + responseCode);
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
// 解密逻辑,设解密工具类为 AesUtil
public static class AesUtil {
// 加密方法
public String AesUtilEncrypt(String AesUtilKey, String data) throws Exception {
System.out.println("加密前明文:" + data);
byte[] input = data.getBytes("UTF-8");
Key key = new SecretKeySpec(AesUtilKey.getBytes(),"AesUtil");
Cipher cipher = Cipher.getInstance("AesUtil/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,key);
byte[] encryptedText = cipher.doFinal(input);
return byte2hex(encryptedText);
}
// 解密方法
public String AesUtilDecrypt(String AesUtilKey, String data) throws Exception {
byte[] input = hex2byte(data);
Key key = new SecretKeySpec(AesUtilKey.getBytes(),"AesUtil");
Cipher cipher = Cipher.getInstance("AesUtil/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,key);
byte[] encryptedText = cipher.doFinal(input);
return new String(encryptedText,"UTF-8");
}
// 将字节数组转换为十六进制字符串
public static String byte2hex(byte[] b) {
StringBuilder hs = new StringBuilder();
String stmp;
for (int n = 0; b!= null && n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0XFF);
if (stmp.length() == 1) {
hs.append('0');
}
hs.append(stmp);
}
return hs.toString().toUpperCase();
}
// 将十六进制字符串转换为字节数组
public static byte[] hex2byte(String hexStr) {
if (hexStr == null || hexStr.length() == 0) {
return null;
}
byte[] bytes = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length(); i+= 2) {
String subStr = hexStr.substring(i, i + 2);
bytes[i / 2] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
}
public static String decryptResponse(String encryptedData) {
AesUtil AesUtil = new AesUtil();
try {
return AesUtil.AesUtilDecrypt("f33b3faf1e90a3f2", encryptedData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
代码说明:通过阿里云的 ODPS 服务进行数据的上传和处理,同时使用加密和解密工具类对响应数据进行加密和解密操作
1. 设置了一些阿里云的相关信息,包括访问 ID、访问密钥、端点、连接隧道的 URL 和 ODPS 表名等。同时,还创建了一个分区规格对象,并根据当前日期设置了分区的值。最后,获取了表对象,并打印了字段的个数。
1.1 定义了一些私有静态变量,包括阿里云的访问 ID、访问密钥、端点、连接隧道的 URL、ODPS 表名和分区名称。
1.2 在 main 方法中,创建了一个阿里云账户对象,并设置了端点和默认项目。
1.3 通过 TableTunnel 类创建了一个隧道对象,并设置了隧道的端点。
1.4 创建了一个分区规格对象,并设置了分区的键值对。
1.5 获取当前日期,并将其格式化为 yyyyMMdd 格式,用于设置分区的值。
1.6 根据 ODPS 表名获取表对象,并获取表的字段列表。
1.7 创建一个新的列表来存储字段名称。
1.8 检查分区是否存在,如果存在则先删除分区,然后再创建分区;如果不存在则直接创建分区。
1.9 打印odps表字段的个数。
2. 创建了一个阿里云上传管道,并通过上传会话对象和记录对象进行数据的写入。它还设置了日期参数,并将参数添加到业务参数的 Map 中。然后,发送 POST 请求获取响应,并打印出接口响应数据。接下来,解密响应数据并将其转换为 JSONArray 对象。最后,遍历 JSONArray 2.1 获取每个元素的字段值,并将其写入到记录对象中,最后提交上传会话。
2.2 通过 tunnel.createUploadSession 方法创建了一个上传会话对象 uploadSession,用于上传数据到指定的表和分区。
2.3 通过 uploadSession.newRecord 方法创建了一个记录对象 product,用于存储要上传的数据。
2.4 通过 uploadSession.openBufferedWriter 方法打开一个缓冲写入器对象 writer,用于将数据写入管道。
2.5 创建了一个日历对象 calendar,并使用 add 方法将日期向前推一天,获取到 T-1 的日期数据。
2.6 再创建一个日期格式化对象 dateFormat,用于将日期格式化为指定的字符串格式。
2.7 使用 format 方法将格式化后的日期字符串赋值给变量 queryDate。
2.8 使用日期时间格式化器 formatter 将 queryDate 解析为 LocalDate 对象 queryDateEnd1,并使用 plusDays 方法在 queryDateEnd1 的基础上加上一天,得到结束日期 queryDateEnd。
2.9 将结束日期格式化为字符串,并打印出数据时间周期。
3. sendPostRequest 方法,用于发送 POST 请求并获取接口的响应数据。它通过创建 URL 对象、打开连接、设置请求方法和请求头等操作,将请求参数转换为 JSON 格式并发送请求。然后,根据响应代码读取响应内容,并将其转换为 JSONObject 对象返回。
3.1 定义了接口地址和测试接口地址,并打印出测试接口地址。
3.2 创建了一个包含业务参数的 Map 对象,并将日期参数添加到其中。
3.3 发送 POST 请求并获取响应,将响应转换为 JSONObject 对象并打印。
3.4 解密响应数据。
3.5 将解密后的数据转换为 JSONArray 对象,并遍历其中的每个元素。
3.6 创建一个新的 ArrayList 对象 inputlist,用于存储每个元素的字段值。
3.7 获取表的字段列表。
3.8 将每个字段的值添加到 inputlist 中。
3.9 创建一个新的 ArrayList 对象 listData,用于存储要写入的数据。
3.10 将 inputlist 添加到 listData 中。
3.11 获取 Record 对象,并将 listData 中的数据设置到对应的字段中。
3.12 使用 RecordWriter 对象将数据写入管道。
3.13 关闭 RecordWriter 对象。
3.14 提交上传会话。
3.15 打印提交成功的消息,并退出程序。
4. AesUtil 类,包含了加密和解密的方法。其中,AesUtilEncrypt 方法用于对数据进行加密,AesUtilDecrypt 方法用于对加密数据进行解密。这两个方法都使用了指定的加密算法和密钥进行操作。
定义了一个名为 `sendPostRequest` 的方法,用于发送 `POST` 请求并获取接口的响应数据。
4.1 方法接受两个参数:`url` 表示接口的地址,`arguments` 表示请求的参数。
4.2 通过 `new URL(url)` 创建一个 `URL` 对象。
4.3 使用 `openConnection()` 方法打开一个 `HttpURLConnection` 连接。
4.4 设置请求方法为 `POST`。
4.5 设置请求头的 `Content-Type` 为 `application/json`,表示发送的请求体为 JSON 格式。
4.6 将请求参数转换为 `JSONObject` 对象。
4.7 使用 `getOutputStream()` 获取输出流,并将 JSON 数据写入输出流。
4.8 获取响应代码。
4.9 如果响应代码为 `200`(`HTTP_OK`),则读取响应内容。
4.10 使用 `BufferedReader` 逐行读取响应内容,并将其添加到 `response` 字符串构建器中。
4.11 关闭输入流。
4.12 将响应内容转换为 `JSONObject` 对象并返回。
4.13 如果响应代码不是 `200`,则打印请求失败的消息并返回 `null`。
4.14 在方法中处理了可能发生的 `IOException`,并打印异常信息。
5. decryptResponse 方法,用于解密响应数据。它创建了一个 AesUtil 对象,并使用指定的密钥进行解密操作。如果解密过程中发生异常,则打印异常信息并返回 null。
在实际使用中,可以根据需要修改密钥和加密算法,以满足具体的安全需求。
5.1 `AesUtil` 类:这是一个包含加密和解密方法的工具类。
- `AesUtilEncrypt` 方法:用于对数据进行加密。它接收两个参数:加密密钥 `AesUtilKey` 和要加密的数据 `data`。首先,将数据转换为字节数组,然后使用指定的加密算法和密钥初始化 `Cipher` 对象,最后进行加密操作并将加密后的字节数组转换为十六进制字符串返回。
- `AesUtilDecrypt` 方法:用于对加密数据进行解密。它接收两个参数:解密密钥 `AesUtilKey` 和要解密的十六进制字符串数据 `data`。首先,将十六进制字符串转换为字节数组,然后使用指定的解密算法和密钥初始化 `Cipher` 对象,最后进行解密操作并将解密后的字节数组转换为字符串返回。
- `byte2hex` 方法:用于将字节数组转换为十六进制字符串。它遍历字节数组,将每个字节转换为十六进制字符串并添加到字符串构建器中。
- `hex2byte` 方法:用于将十六进制字符串转换为字节数组。它遍历十六进制字符串,将每两个字符解析为一个十六进制数,并将其转换为字节添加到字节数组中。
5.2 `decryptResponse` 方法:这是一个静态方法,用于解密响应数据。它接收一个加密后的字符串 `encryptedData`,创建一个 `AesUtil` 对象,并使用指定的密钥进行解密操作。如果解密过程中发生异常,则打印异常信息并返回 `null`。
第二步:可先将写入表部分的代码先注释掉,执行获打印出解密后的接口数据后建表,再放开注释落表
写入表部分的代码:
for (int i = 0; a < jsonArray.length(); a++) {
ArrayList<String> inputlist = new ArrayList<>();
JSONObject item = jsonArray.getJSONObject(i);
inputlist.add(item.getString("update_time"));
inputlist.add(item.getString("name"));
inputlist.add(item.getString("bush"));
inputlist.add(item.getString("houki"));
inputlist.add(item.getString("yomee"));
// 将数据写入
// 写入的方式为获取表字段,获取数据一起写入
listData = new ArrayList<>();
listData.addAll(inputlist);for (int j = 0; j < columnList.size(); ++j) {
Object listDesc = listData.get(j);
if (listDesc == null) {
listDesc = "";
}product.setString(columnListStr.get(j), listDesc.toString());
}
writer.write(product);
}
建表语句:
CREATE TABLE IF NOT EXISTS odps_p.table_bak (
update_time STRING,
name STRING,
bush STRING,
houki STRING,
yomee STRING,
)
COMMENT '相亲数据'
PARTITIONED BY (pt STRING)
LIFECYCLE 36500;
第三步:阿里云创建shell节点调用jar包,具体操作需要请看(此篇文章的脚本不需要传参,直接 java -jar jar包名称.jar 调用即可):
阿里云设置shell节点调用jar包联动运维中心自定义日期传参,阿里云shell节点传参
最后输出的表结果示例:
update_time | name | bush | houki | yomee | pt |
20240701 | namevlue1 | bushvlue1 | houkivlue1 | yomeevlue1 | 20240707 |
20240702 | namevlue2 | bushvlue2 | houkivlue2 | yomeevlue2 | 20240707 |
20240703 | namevlue3 | bushvlue3 | houkivlue3 | yomeevlue3 | 20240707 |