更新时间:2025-08-05 03:07:04下载pdf
本文介绍查询对话记录的 API。
根据设备 ID、绑定角色类型和角色 ID,查询 AI 智能体对话记录。
GET: /v1.0/cloud/agent/ai/enterprise/chat/devices/{device_id}/history
参数名 | 类型 | 参数位置 | 是否必填 | 说明 |
---|---|---|---|---|
device_id | String | path | true | 设备 ID |
gmt_end | Long | query | true | 聊天信息的截止时间(时间戳),小于此时间的聊天信息会被返回 |
page_size | Integer | query | true | 每页数量,每页最多 20 条 |
参数名 | 类型 | 说明 |
---|---|---|
success | Boolean | 请求是否成功 |
error_code | String | 错误代码 |
error_msg | String | 错误信息 |
result | Object | 返回结果 |
result
说明
参数名 | 类型 | 说明 |
---|---|---|
data | String | 聊天信息(密文,需要进行解密) |
pv | String | 协议版本 |
sign | String | 防伪造校验的数据签名 |
t | Long | 接口返回时的系统时间 |
GET: /v1.0/cloud/agent/ai/enterprise/chat/devices/6c50600a0e15d1fb78h***/history?page_size=20&gmt_end=1753079987512
{
"error_msg": null,
"result": {
"data": "XpAnrVY93e***",
"pv": "1.0",
"sign": "e92b1036a7546f912058e07546ba66585ca2***",
"t": 1753770653706
},
"success": true,
"error_code": null
}
参考 错误码。
result
中 data
对象说明
参数名 | 类型 | 说明 |
---|---|---|
gmt_create | Long | 聊天时间(时间戳) |
answer | List | 回答内容列表 |
question | List | 提问内容列表 |
request_id | String | 聊天记录 ID |
role_info | Object | 角色信息。若角色被删除,则不会返回 |
Answer/Question 对象说明
参数名 | 类型 | 说明 |
---|---|---|
context | String | 消息内容 |
type | String | 消息类型 |
role_info
对象说明
参数名 | 类型 | 说明 |
---|---|---|
role_id | String | 角色 ID |
role_name | String | 角色名称 |
bind_role_type | Integer | 绑定角色类型:
|
解密后的数据示例
{
"data": [{
"answer": [{
"context": "你之前跟我说过***",
"type": "text"
}],
"gmt_create": 1753758926504,
"question": [{
"context": "我的***",
"type": "text"
}],
"request_id": "ccbda733-e3d2-4ee3-bb19-d201-***",
"role_info": {
"bind_role_type": 1,
"role_id": "20001",
"role_name": "测试角色"
}
}]
}
为了保障数据传输安全,接口返回的聊天记录采用 AES-GCM 认证加密模式 进行加密处理。您需对响应体中 data
字段进行解密后,方可获取原始数据。
属性 | 规格说明 |
---|---|
加密标准 | AES-GCM(Galois/Counter Mode) |
密钥来源 | 云开发项目配置的 Access Secret |
安全特性 | 数据完整性验证、真实性认证、抵抗选择密文攻击 |
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.util.Base64;
public class CryptoUtils {
private static final String AES_ALGO = "AES";
private static final String AES_GCM = "AES/GCM/NoPadding";
private static final int GCM_NONCE_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 16; // 认证标签长度
/**
* AES-GCM 解密实现
*
* @param data Base64 编码的密文字符串
* @param key 项目密钥,即 Access Secret
* @return 解密后的原始字符串
* @throws IllegalArgumentException 当输入参数不合法时抛出
*/
public static String decryptDataGCM(String data, String key)
throws GeneralSecurityException {
// 参数校验
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("密钥不能为空");
}
Key secretKey = new SecretKeySpec(key.getBytes(), AES_ALGO);
Cipher cipher = Cipher.getInstance(AES_GCM);
byte[] message = Base64.decodeBase64(data);
if (message.length < GCM_NONCE_LENGTH + GCM_TAG_LENGTH) {
throw new IllegalArgumentException();
}
GCMParameterSpec params = new GCMParameterSpec(GCM_TAG_LENGTH * 8, message, 0, GCM_NONCE_LENGTH);
cipher.init(Cipher.DECRYPT_MODE, secretKey, params);
byte[] decryptData = cipher.doFinal(message, GCM_NONCE_LENGTH, message.length - GCM_NONCE_LENGTH);
return new String(decryptData);
}
}
为了确保数据完整性与请求来源真实性,请按以下流程进行签名验证:
获取接口返回数据:从接口响应中提取以下关键字段。
sign
:接口返回的数字签名t
:UNIX 时间戳pv
:协议版本号data
:加密的数据负载本地重新生成签名:使用与接口相同的签名算法和密钥(Access Secret)执行签名计算。
String localSign = SignUtils.createCloudSign(data, pv, t, accessSecret);
签名比对验证:严格对比本地生成的签名与接口返回的 sign
值。
if (!localSign.equals(apiSign)) {
// 验证失败处理
}
验证结果 | 含义 | 处理措施 |
---|---|---|
一致 | 数据完整且来源可信 | 可安全使用数据 |
不一致 | 传输过程中数据被篡改或密钥不匹配 | 1. 丢弃当前数据 2. 检查密钥有效性 3. 触发安全告警机制 |
/**
* 生成数字签名(SHA-256)
*
* @param data 加密数据,需与请求内容一致
* @param pv 协议版本,例如 '1.0'
* @param time 时间戳,取自接口中返回的 t 参数
* @param secretKey 项目密钥,即 Access Secret
* @return 十六进制编码的签名值
*/
public static String createCloudSign(String data,
String pv,
long time,
String secretKey)
throws NoSuchAlgorithmException {
// 参数校验
if (secretKey == null || secretKey.isEmpty()) {
throw new IllegalArgumentException("secretKey 不能为空");
}
if (data == null || pv == null) {
throw new IllegalArgumentException("data 和 pv 参数不能为 null");
}
// 创建有序参数集(TreeMap 默认按照键升序排列)
TreeMap<String, String> params = new TreeMap<>();
params.put("t", String.valueOf(time));
params.put("data", data);
params.put("pv", pv);
// 构造签名字符串
StringBuilder sb = new StringBuilder(128);
for (Map.Entry<String, String> entry : params.entrySet()) {
String value = entry.getValue();
if (value != null && !value.trim().isEmpty()) {
sb.append(entry.getKey())
.append('=')
.append(value)
.append("||");
}
}
sb.append(secretKey);
// SHA-256 哈希计算
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
// 转换为十六进制
StringBuilder hexString = new StringBuilder(64);
for (byte b: hash) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
}
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈