You need to enable JavaScript to run this app.
导航

业务对象(item)数据接入(SaaS查看)

最近更新时间2023.08.04 11:51:01

首次发布时间2021.02.23 10:41:48

本文档仅针对SaaS版本,不针对SaaS云原生版本和私有化版本;
SaaS云原生版本和私有化版本接入请参考HTTP API文档中的「6.上报业务对象属性」模块。

1.概述

本文档涉及的上报和查询 API 接口,接口采用 Restful API 规范。

2.快速开始

2.1 开通功能

该功能默认可以使用,但是上报item数据需要使用ak/sk,您可以通过页面右下角的工单功能或者联系您的客户成功经理告诉我们您要使用业务对象(Item)功能。我们会将上报数据所需的 ak/sk 发送到您指定的邮箱。

您可以进入数据管理 > 业务对象点击创建业务对象按钮开始使用。

上图是我们的电商行业Demo,该 Demo 中“商品”就是我们预先定义好的业务对象。

2.2 配置属性

点击创建业务对象可以在弹框中对您想添加的业务对象以及它的属性进行配置。

目前我们对业务对象做了一些限制,具体如下:

  • 每个应用目前最多可创建10个业务对象;
  • 每个业务对象最多可创建50个属性;

2.3 上报数据

在完成了 item 对象的定义后,数据上报须分两部分进行:
1) 通过本文 API 上报 item 对象;
2) 在上报事件时进行关联;

2.3.1 上报 item 对象的属性

完成属性配置后,可以按下文中的(4.API用法)以及(6.鉴权)进行接口调用完成数据上报。注意数据类型一定正确,类型错误的数据会被丢弃。

2.3.2 在上报事件时进行关联(参考服务端埋点上报)

创建好的 item 对象需要与事件关联才可以使用。关联就是将某一个或多个具体业务对象的 id 值作为上报到事件的预置属性params.__item当中的过程。
下列示例代码演示了将 sku 和 order 两个创建好的业务对象与事件 new_order 进行关联的方式。__items 为事件预置属性,仅用于上报业务对象;
示例如下:

...
"event": "new_order",
"params": {
    "__items": "[{\"sku\":[{\"id\":\"sku_id_1\"},{\"id\":\"sku_id_2\"}]}]",
    ...
},
...

__item 是转为 stringjson 对象,其格式为:

[
    {"item_name_1": [
        {"id": "id_value_1"},
        {"id": "id_value_2"},
        ...
    ]}
]

item_name_1 就是在上一步中定义的业务对象名称
如果这里关联的业务对象名称没有被定义过,那么相应的数据就会被抛掉,不会入库;
如果 id 值没有找到对应的业务对象,也同样会被抛掉,不会入库,无法使用。

  • 注意:一个事件暂时仅支持一个item

2.4 进行分析

当已经完成数据上报,并且属性没有被禁用的情况下。就可以在属性筛选、分组等处使用这些属性了。
针对业务对象,我们还提供了业务对象分析功能,可以以业务对象为主体进行分析。

2.5 注意事项

  1. 该API使用QPS上限为500
  2. item_name 以及 item_id 需要满足规则:[a-zA-Z_0-9\\-/]+
3.鉴权

通过提供AccessKey/SecretKey的方式鉴权,简写为ak/sk,AccessKey是app请求的唯一标识,SecretKey是app的密钥,它们相当于用户名和密码。注册app之后就会生成一个AccessKey和SecretKey,请妥善保存。在所有请求的header中包括如下鉴权信息:

HeaderTypeDescriptionRequired
Authorizationstringapi鉴权使用(Global)TRUE
  • 及appSecret的生成可联系客户经理
  • 可以使用我们提供的sdk帮助鉴权
  • Authorization的生成工具见下文 示例代码-6.1
  • 生成Authorization code示例代码见下午 示例代码-6.2
4.API用法

4.1 域名

国内: https://analytics.volcengineapi.com
海外: https://analytics.byteplusapi.com

4.2 属性值初始化接口

Path: /dataprofile/openapi/v1/{app_id}/items/{item_name}/{item_id}?set_once=true
Method: PUT
Content-Type: application/json; charset=utf-8
Request-parameters:

ParameterTypeDescriptionRequired
app_idint64app_idTRUE
item_namestring属性名称TRUE
item_idstringitem idTRUE

Query-parameters:

ParameterTypeDescriptionRequired
set_onceboolean不存在则设置TRUE

Put body:

{
    "name":"price",
    "value":9.9
}

Request-example:

curl -X PUT -H 'Content-Type: application/json; charset=utf-8' -H 'Authorization: ******' -i https://analytics.volcengineapi.com/dataprofile/openapi/v1/341/items/your_item_name/185?set_once=true --data '{
        "name":"price","value":9.9
}'

Response-fields:

FieldTypeDescription
codeint32业务响应状态码
messagestring业务响应描述信息

Response-example:

{
        "code":2000,
        "message":"success"
}

4.3 单个属性值的更新操作接口

Path: /dataprofile/openapi/v1/{app_id}/items/{item_name}/{item_id}/attributes/{attribute}
Method: PUT
Content-Type: application/json; charset=utf-8
Request-parameters:

ParameterTypeDescriptionRequired
app_idint64app_idTRUE
item_namestringitem名称TRUE
item_idstringitem_idTRUE
attributestring需要修改的属性名称TRUE

Body:

{
    "operation":"INCREASE",
    "value":9
}

operation可选值见下文 4.6 Operation

Request-example:

curl -X PUT -H 'Content-Type: application/json; charset=utf-8' -H 'Authorization: ******'  -i https://analytics.volcengineapi.com/dataprofile/openapi/v1/384/items/your_item_name/185/attributes/storage --data '{
        "operation":"INCREASE","value":9
}'

Response-fields:

FieldTypeDescription
codeint32业务响应状态码
messagestring业务响应描述信息

Response-example:

{
        "code":2000,
        "message":"success"
}

4.4 多属性批量处理接口

Path: /dataprofile/openapi/v1/{app_id}/items/{item_name}/{item_id}/attributes
Method: PUT
Content-Type: application/json; charset=utf-8
Request-parameters:

ParameterTypeDescriptionRequired
app_idint64app_idTRUE
item_namestring注册的item的名称TRUE
item_idstringitem的唯一idTRUE

Body:

{
    "attributes": [
        {
          "name": "price",
          "value": 9,
          "operation": "SET"
        },
        {
          "name": "storage",
          "value": 900,
          "operation": "INCREASE"
        },
        {
          "name": "color",
          "operation": "UNSET"
        }
    ]
}

operation可选值见下文 4.6 Operation

Request-example:

curl -X PUT -H 'Content-Type: application/json; charset=utf-8' -H 'Authorization: ******'  -i https://analytics.volcengineapi.com/dataprofile/openapi/v1/541/items/your_item_name/185/attributes --data '{
        "attributes": [
        {
          "name": "price",
          "value": 9,
          "operation": "SET"
        },
        {
          "name": "storage",
          "value": 900,
          "operation": "INCREASE"
        },
        {
          "name": "color",
          "operation": "UNSET"
        }
    ]
}'

Response-fields:

FieldTypeDescription
codeint32业务响应状态码
messagestring业务响应描述信息

Response-example:

{
        "code":2000,
        "message":"success"
}

4.5 item 属性查询接口

Path: /dataprofile/openapi/v1/{app_id}/items/{item_name}/{item_id}
Method: GET
Content-Type: application/json; charset=utf-8
Request-parameters:

FieldTypeDescription
codeint32业务响应状态码
messagestring业务响应状态信息
dataobject属性值信息
data.appIdint64应用id
data.attributeslist具体信息
data.attributes.namestring属性名称
data.attributes.value根据注册类型而定值信息

Request-example:

curl -X GET -H 'Authorization: ******' -i https://analytics.volcengineapi.com/dataprofile/openapi/v1/760/items/your_item_name/185

Response-fields:

FieldTypeDescription
codeint32业务响应状态码
messagestring业务响应状态信息
dataobject属性值信息
data.appIdint64应用id
data.attributeslist具体信息
data.attributes.namestring属性名称
data.attributes.value根据注册类型而定值信息

Response-example:

{
    "code": 2000,
    "message": "success",
    "data": {
        "appId": 182463,
        "attributes": [
            {
                "name": "storage",
                "value": 40
            },
            {
                "name": "price",
                "value": 22.66
            },
            {
                "name": "name",
                "value": "一次性医用口罩"
            },
            {
                "name": "color",
                "value": "green"
            }
        ]
    }
}

4.6 Operation

Operation说明
SET为属性设置一个值
SET_ONCE属性不存在则设置
UNSET删除一个属性
INCREASE对数值类型的属性执行累加操作
APPEND在list类型的属性值里插入一个值
REMOVE在list类型的属性值里删除一个值
5.错误码列表
Error codeDescription
2000success
4000parameter error
4010token authenticate fail
5010not supported request
5030request too frequency
5000something wrong, please retry later
6.示例代码

6.1 鉴权

6.1.1 Java (AuthUtil.class)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class AuthUtils {

    /**
     * @param ak                accessKey
     * @param sk                secretKey
     * @param expirationSeconds 过期时间,单位秒
     * @param method            方法,GET, POST, PUT
     * @param path           请求的path,非完整的url(不需要域名,有查询参数的也不需要拼接上去)
     * @param queryString       请求参数, param1=value1&param2=value2
     * @param body              请求的json体
     * @return
     */
    public static String sign(String ak, String sk, int expirationSeconds, String method, String path, String queryString, String body) {
        String cm = canonicalMethod(method);
        String cu = canonicalUrl(path);
        String cp = canonicalQueryString(queryString);
        String cb = canonicalBody(body);
        String text = cm + "\n" + cu + "\n" + cp + "\n" + cb;
        return doSign(ak, sk, expirationSeconds, text);
    }

    private static String canonicalMethod(String method) {
        return "HTTPMethod:" + method;
    }

    private static String canonicalUrl(String url) {
        return "CanonicalURI:" + url;
    }

    private static String canonicalQueryString(String queryString) {
        String res = "CanonicalQueryString:";
        if (queryString == null || queryString.isEmpty()) {
            return res;
        }
        return res + queryString;
    }

    private static String canonicalBody(String body) {
        String res = "CanonicalBody:";
        if (body == null) {
            return res;
        } else {
            return res + body;
        }
    }

    private static String doSign(String ak, String sk, int expiration, String text) {
        String signKeyInfo = "ak-v1/" + ak + "/" + (int) (System.currentTimeMillis() / 1000) + "/" + expiration;
        String signKey = sha256Hmac(signKeyInfo, sk);
        String signResult = sha256Hmac(text, signKey);
        return signKeyInfo + "/" + signResult;
    }

    private static String sha256Hmac(String message, String secret) {
        String hash = "";
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
            hash = byteArrayToHexString(bytes);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return hash;
    }

    private static String byteArrayToHexString(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().toLowerCase();
    }
}

6.1.2 Python (auth_util.py)

import time
import hashlib
import hmac

def _do_sign(ak, sk, expiration, text):
    sign_key_info = 'ak-v1/%s/%d/%d' % (ak, time.time(), expiration)
    sign_key = _sha256_hmac(sk, sign_key_info)
    sign_result = _sha256_hmac(sign_key, text)
    return '%s/%s' % (sign_key_info, sign_result)

def _canonical_method(method):
    return "HTTPMethod:" + method

def _canonical_url(url):
    return "CanonicalURI:" + url

def _canonical_query_string(query_string):
    res = 'CanonicalQueryString:'
    if not query_string:
        return res
    return res + query_string

def _canonical_body(body):
    res = "CanonicalBody:"
    if not body:
        return res
    return res + body

def _sha256_hmac(key, data):
    return hmac.new(str.encode(key, 'utf-8'), str.encode(data, 'utf-8'), hashlib.sha256).hexdigest()

def sign(ak, sk, expiration_seconds, method, uri_path, query_string, body):
    """
    :param ak:accessKey
    :param sk:secretKey
    :param expiration_seconds:过期时间,单位秒
    :param method:方法,GET, POST, PUT
    :param uri_path: 请求的path,非完整的url(不需要域名,有查询参数的也不需要拼接上去)
    :param query_string:请求参数, param1=value1&param2=value2
    :param body:请求的json体
    :return:
    """
    canonical_method = _canonical_method(method)
    canonical_url = _canonical_url(uri_path)
    canonical_param = _canonical_query_string(query_string)
    canonical_body = _canonical_body(body)
    text = '{}\n{}\n{}\n{}'.format(canonical_method, canonical_url, canonical_param, canonical_body)
    return _do_sign(ak, sk, expiration_seconds, text)

6.1.3 Js

const crypto = require('crypto');
function hash(ak, sk, expiration_seconds, method, path, queryString, body) {
    const timestamp = (+new Date() / 1000).toFixed(0);
    const signKeyInfo = `ak-v1/${ak}/${timestamp}/${expiration_seconds}`;
    const signKey = sha256HMAC(sk, signKeyInfo);
    const data = canonicalRequest(method, path, queryString, body);
    const signResult = sha256HMAC(signKey, data);
    return signKeyInfo + '/' + signResult;
}

function sha256HMAC(sk, data) {
    const hmac = crypto.createHmac('sha256', sk)
    return hmac.update(data).digest('hex')
}

function canonicalRequest(method, url, queryString, body) {
    let cm = canonicalMethod(method);
    let cu = canonicalUrl(url);
    let cp = canonicalQueryString(queryString);
    let cb = canonicalBody(body);
    return cm + '\n' + cu + '\n' + cp + '\n' + cb;
}

function canonicalMethod(method) {
    return 'HTTPMethod:' + method;
}

function canonicalUrl(url) {
    return 'CanonicalURI:' + url;
}

function canonicalQueryString(queryString) {
    let res = 'CanonicalQueryString:'
    if (!queryString) {
        return res;
    }
    return res + queryString;
}

function canonicalBody(body) {
    res = "CanonicalBody:"
    if (!body){
        return res;
    }
    return res + body;
}

6.2 Authorization

6.2.1 Java

public class AuthGenDemo {

    //分配的accessKey和secretKey
    private static String accessKey = "****";
    private static String secretKey = "****";
    // 单位秒
    private static Integer expirationSeconds = 300;

    public static void main(String[] args) {
        String method = "PUT";
        String host = "https://analytics.volcengineapi.com";
        // /dataprofile/openapi/v1/{appId}/items/{item_name}/{itemId}
        String path = "/dataprofile/openapi/v1/751/items/book/book01";

        // 请求参数,对于没有请求参数的接口,在生成authorization code的时候对应的参数传null即可
        HashMap<String, String> exampleQueryParams = new HashMap<>();
        exampleQueryParams.put("set_once", "true");
        String exampleQueryBodyJson = "{\"name\":\"name\",\"value\":\"zhangsan\"}";

        String authorization = AuthUtils.sign(accessKey, secretKey, expirationSeconds,
            method, path, exampleQueryParams, exampleQueryBodyJson);
        System.out.println("authorization: " + authorization);

        String url = host + path;
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", authorization);
        JSONObject result = HttpUtil.put(url, exampleQueryBodyJson, exampleQueryParams, headers);
        System.out.println(result);
    }
}

6.2.2 Python

import auth_util
import json
import requests

if __name__=="__main__":
    ak = "****"
    sk = "****"
    expiration_seconds = 300
    method = "PUT"
    host = "https://analytics.volcengineapi.com"
    # /dataprofile/openapi/v1/{appid}/items/{itemname}/{itemId}
    path = "/dataprofile/openapi/v1/751/items/book/book01"

    # 请求参数,对于没有请求参数的接口,在生成authorization code的时候对应的参数传null即可
    query_string = {"set_once": "true"}
    request_body = {
        "name": "name",
        "value": "zhangsan"
    }

    auth = auth_util.sign(ak, sk, expiration_seconds, method, path, query_string, json.dumps(request_body))
    print(auth)

    headers = {"Authorization": auth}
    url = host + path
    print(requests.put(url, data=json.dumps(request_body), params=query_string, headers=headers).json())