企业主数据是用来描述企业核心业务实体的数据,例如供应商、员工、产品、物料、设备等;它是具有高业务价值的、可以在企业内跨越各个业务部门被重复使用的数据,并且存在于多个异构的应用中。数字工厂提供统一的工厂主数据的云数据定义和数据管理功能,第三方应用系统可以通过API查询主数据的元数据和数据实例或者采用订阅方式获得元数据和主数据变更通知。

例如设备运维的第三方应用需要用到数字工厂的设备类型、设备种类和生产设备三种设备主数据,首先通过元数据查询接口获得所有元数据信息,然后根据设备类型、设备种类和生产设备的元数据定义,去订阅这三类主数据,一旦企业的业务用户在数字工厂中对主数据进行了操作,新采购了一台设备,创建了对应的生产设备主数据,将通过接口通知设备运维的第三方应用,应用就可以对新的设备进行运维操作。主数据集成的说明请参看主数据集成接口文档

元数据、主数据和主数据记录

简单来说,每一种主数据都对应一张物理表,对应的元数据就是这张物理表的描述信息。上面提到的供应商、员工、产品、物料、设备这些都是相应主数据(物理表)的业务描述。

系统内建主数据

1、通用的内建主数据

系统包含了7种通用的内建主数据:人员(Employee),物料组(MaterialGroup),物料类型(MaterialType),物料(Material),生产设备类型(EquipmentType),生产设备型号(EquipmentModel),生产设备(Equipment)。7种内建主数据的属性定义如下表:

人员

中文名,工号(PK),手机号码(可空),入职时间(可空),备注(可空)

物料组

名称,编码(PK),简码(可空),描述(可空)

物料类型

名称,编码(PK),简码(可空),所属物料组,描述(可空),工艺路径(可空)

物料

名称,编码(PK),简码(可空),所属物料类型,描述(可空)

设备类型

名称,编码(PK),简码(可空),描述(可空)

设备型号

名称,编码(PK),简码(可空),所属设备类型,描述(可空)

生产设备

名称,固定资产编号(PK),简码(可空),所属设备型号,物联网设备,使用状态(1-新建;2-使用中;3-已停用;4-已报废) , 描述(可空)

扩展说明:用户可以对内建主数据的属性进行扩展,增加新的属性,也可以增加新的主数据类型。

2、行业特定的内建主数据

行业内建主数据,是通过对通用内建主数据扩展了属性和表格来实现。

对于一个具体的行业,通过行业模板的方式,增加更多特定于本行业的主数据。以增加系统的实用性。例如

当前支持:家电行业。国家标准分类号为 38-电气机械和器材制造业。支持如下数据:

人员,物料组,物料类型,物料,计量单位,计量单位转换,生产设备类型,生产设备型号,生产设备,模具,特料包装规格,供应商,客户。

人员

物料组

物料类型

物料

计量单位

计量单位转换

设备类型

设备型号

生产设备

模具

物料包装规格

供应商

客户

主数据集成概述

租户概述:租户用于确定一个企业账户。一个企业账户购买数字工厂或三方应用后,可以给企业的多个员工使用。通常,不同员工可能对应不同角色。访问时需要根据员身份进行鉴权。

对应用来说,租户与租户之间要进行区分和隔离。对于独占式应用,租户与租户间是通过不同的应用实例进行物理隔离。对于共享式应用,租户与租户之间通过逻辑隔离。

集成需要做的事,

如果应用有菜单集成到数字工厂,先确定免登方式;

确定调用的参数,以区分api调用哪个租户相关,以及确定,与某一租户的哪一个具体员工相关。

以下demo代码都是基于github的openApiSDK

https://github.com/aliyun/iotx-api-gateway-client.git

1、查询

查询接口有三个,分,别是查询元数据、查询元数据属性列表、查询主数据记录

(1)查询元数据

这个接口的作用在于:得到系统支持所有主数据的描述信息(即元数据),包括,id, name, 描述,是否支持多版本,是否内建主数据等。内建主数据不支持删除对应的主数据表。

数据id会在其它所有的接口中用到,是系统内识别主数据表的标识信息。

不指定id,可以查询系统里所有元数据的列表,也可以指定id,查询一种具体的元数据。

入参列表

出参示例

{
  "code": 200,
  "message": "success",
  "localizedMsg": null,
  "data": {
    "digitalFactoryMasterDataDTOList": [{
        "id": 541,
        "name": "人员",
        "description": "人员主数据",
        "multiVersion": 0,
        "builtin": 1,
        "gmtCreate": "2019-02-22T02:25:29.000Z",
        "gmtModified": "2019-02-22T02:25:29.000Z"
      }]
  }
}

java demo

import com.alibaba.cloudapi.sdk.model.ApiResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.iotx.api.client.IoTApiClientBuilderParams;
import com.aliyun.iotx.api.client.IoTApiRequest;
import com.aliyun.iotx.api.client.SyncApiClient;
import org.apache.commons.collections4.CollectionUtils;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

public class DemoApplication {

    private static String postBody(String path, IoTApiRequest request, Map<String, String> headers) {
        String appKey = "";
        String appSecret = "";

        IoTApiClientBuilderParams ioTApiClientBuilderParams = new IoTApiClientBuilderParams();

        ioTApiClientBuilderParams.setAppKey(appKey);
        ioTApiClientBuilderParams.setAppSecret(appSecret);

        SyncApiClient syncApiClient = new SyncApiClient(ioTApiClientBuilderParams);

        try {
            //设置请求参数域名、path、request ,如果使用HTTPS,设置为true
            ApiResponse response = syncApiClient.postBody("api.link.aliyun.com", path, request, true, headers);
            String responseString = new String(response.getBody(), "UTF-8");
            System.out.println("response code = " + response.getCode() + " response = " + responseString);
            return responseString;
        } catch (UnsupportedEncodingException uee) {
            System.out.println(uee.getMessage());
            uee.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        IoTApiRequest request = new IoTApiRequest();
        //设置协议版本号
        request.setVersion("1.0");
        String uuid = UUID.randomUUID().toString();
        String id = uuid.replace("-", "");
        //设置请求ID
        request.setId(id);
        System.out.println("id = " + id);
        //设置API版本号
        request.setApiVer("1.0.1");

        Map<String, String> headers = new HashMap<>();

        String path = "/industry/metadata/query";
        String metaDataListString = postBody(path, request, headers);
        JSONObject jsonData = JSON.parseObject(metaDataListString);

        int code = jsonData.getInteger("code");
        if (code == 200) {
            JSONObject data = jsonData.getJSONObject("data");
            JSONArray dtoList = data.getJSONArray("digitalFactoryMasterDataDTOList");
            List<JSONObject> targetDTOList = dtoList.stream().filter(dto -> ((JSONObject)dto).getString("name").equals("人员")).map(dto -> (JSONObject)dto).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(targetDTOList)) {
                System.out.print("人员元数据id " + targetDTOList.get(0).getLong("id"));
            }
        }
 }
}
		

(2)查询主数据属性列表

该接口的作用在于获取具体某个主数据的属性信息,包括属性类型、属性长度/大小的限制。

dataId必须传入,表示查询哪个主数据的属性信息。

id选填,查询具体的某一个属性。

以人员主数据为例来说明

入参

参数名 描述
id 属性的id
dataId 所属元数据的id
pageId 分页查询时的页号
pageSize 分页查询时的页大小
appId 应用的实例id(uuid)
employeeId 阿里云账号的子账号id

出参示例

{
  "code": 200,
  "data": {
    "propertyDTOList": [
      {
        "defaultValue": "",
        "builtin": 1,
        "display": false,
        "isUnique": 2,
        "propertyLimit": {
          "len": 64
        },
        "propertyCode": "name",
        "dataId": 652,
        "array": false,
        "propertyDesc": "中文名",
        "propertyType": "STRING",
        "isNull": 4,
        "propertyIndex": 3,
        "id": 1
      },
      {
        "defaultValue": "",
        "builtin": 1,
        "display": true,
        "isUnique": 1,
        "propertyLimit": {
          "len": 32
        },
        "propertyCode": "id",
        "dataId": 652,
        "array": false,
        "propertyDesc": "工号",
        "propertyType": "STRING",
        "isNull": 4,
        "propertyIndex": 4,
        "id": 2
      },
      {
        "defaultValue": "",
        "builtin": 1,
        "display": false,
        "isUnique": 2,
        "propertyLimit": {
          "len": 256
        },
        "propertyCode": "remark",
        "dataId": 652,
        "array": false,
        "propertyDesc": "备注",
        "propertyType": "STRING",
        "isNull": 3,
        "propertyIndex": 4,
        "id": 3
      },
      {
        "defaultValue": "",
        "builtin": 1,
        "display": false,
        "isUnique": 2,
        "propertyLimit": {
          "len": 32
        },
        "propertyCode": "phone",
        "dataId": 652,
        "array": false,
        "propertyDesc": "手机号码",
        "propertyType": "STRING",
        "isNull": 3,
        "propertyIndex": 5,
        "id": 4
      },
      {
        "defaultValue": "",
        "builtin": 1,
        "display": false,
        "isUnique": 2,
        "propertyLimit": {
          "len": 32
        },
        "propertyCode": "hired_date",
        "dataId": 652,
        "array": false,
        "propertyDesc": "入职时间",
        "propertyType": "DATE",
        "isNull": 3,
        "propertyIndex": 6,
        "id": 5
      }
    ]
  },
  "id": "787b121b3863478a8cde800142853dce"
}

java demo

import com.alibaba.cloudapi.sdk.model.ApiResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.iotx.api.client.IoTApiClientBuilderParams;
import com.aliyun.iotx.api.client.IoTApiRequest;
import com.aliyun.iotx.api.client.SyncApiClient;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

public class DemoApplication {

    private static String postBody(String path, IoTApiRequest request, Map<String, String> headers) {
        String appKey = "";
        String appSecret = "";

        IoTApiClientBuilderParams ioTApiClientBuilderParams = new IoTApiClientBuilderParams();

        ioTApiClientBuilderParams.setAppKey(appKey);
        ioTApiClientBuilderParams.setAppSecret(appSecret);

        SyncApiClient syncApiClient = new SyncApiClient(ioTApiClientBuilderParams);

        try {
            //设置请求参数域名、path、request ,如果使用HTTPS,设置为true
            ApiResponse response = syncApiClient.postBody("api.link.aliyun.com", path, request, true, headers);
            String responseString = new String(response.getBody(), "UTF-8");
            System.out.println("response code = " + response.getCode() + " response = " + responseString);
            return responseString;
        } catch (UnsupportedEncodingException uee) {
            System.out.println(uee.getMessage());
            uee.printStackTrace();
            return null;
        }
    }

    private static Long requestMetaDataId(String name, String appId) {
        IoTApiRequest request = new IoTApiRequest();
        //设置协议版本号
        request.setVersion("1.0");
        String uuid = UUID.randomUUID().toString();
        String id = uuid.replace("-", "");
        //设置请求ID
        request.setId(id);
        System.out.println("id = " + id);
        //设置API版本号
        request.setApiVer("1.0.1");

        request.putParam("pageId", 1);
        request.putParam("pageSize", 200);
        Map<String, String> headers = new HashMap<>();
        if (StringUtils.isNotEmpty(appId)) {
            request.putParam("appId", appId);
        }

        String path = "/industry/metadata/query";
        String metaDataListString = postBody(path, request, headers);
        JSONObject jsonData = JSON.parseObject(metaDataListString);

        int code = jsonData.getInteger("code");
        if (code == 200) {
            JSONObject data = jsonData.getJSONObject("data");
            JSONArray dtoList = data.getJSONArray("digitalFactoryMasterDataDTOList");
            List<JSONObject> targetDTOList = dtoList.stream().filter(dto -> ((JSONObject)dto).getString("name").equals(name)).map(dto -> (JSONObject)dto).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(targetDTOList)) {
                return targetDTOList.get(0).getLong("id");
            }
        }
        return null;
    }

    private static void requestPropertyList(Long dataId, String appId) {
        IoTApiRequest request = new IoTApiRequest();
        //设置协议版本号
        request.setVersion("1.0");
        String uuid = UUID.randomUUID().toString();
        String id = uuid.replace("-", "");
        //设置请求ID
        request.setId(id);
        System.out.println("id = " + id);
        //设置API版本号
        request.setApiVer("1.0.1");

        request.putParam("pageId", 1);
        request.putParam("pageSize", 200);
        Map<String, String> headers = new HashMap<>();


        String path = "/industry/metadata/property/query";
        request.putParam("dataId", dataId);
        if (StringUtils.isNotEmpty(appId)) {
            request.putParam("appId", appId);
        }

        String propertyListString = postBody(path, request, headers);
        System.out.println(propertyListString);
    }

    public static void main(String[] args) {
        Long employeeDataId = requestMetaDataId("人员", null);
        if (employeeDataId != null) {
            requestPropertyList(employeeDataId, null);
        }
    }
}
		

(3)查询主数据具体记录

该接口用于查询某个主数据的数据记录。

dataId必填,指明查询哪一个主数据的记录。

入参

参数名称 描述 -
dataId 元数据id 指明要查哪一个物理表
dapIds 数据记录的id。后台会给每一张主数据的物理表添加几个字段,其中一个是dap_id_,是一个自增的整型字段。该字段非空说明要查指定dap_id_的数据记录 后台最终执行的sqlselect * from tablewhere dap_id_ in (dapIds)
pageId 分页查询时的页号 -
pageSize 分页查询时的页大小 -
appId 应用实例id -
employeeId 阿里云账号的子账号id -
condition 查询条件 -

举例子说明condition的用法,仍然使用人员主数据

// 查询dap_id_为1,2,3的记录,dap_id_ IN (1, 2, 3)
Map<String, Object> condition = new HashMap<>(3);
condition.put("col", "dap_id_");
condition.put("op", "IN");
condition.put("value", Arrays.asList(1, 2, 3));
request.putParam("condition", Collections.singletonList(condition));

// 查询名字(name)为张三的记录, name = "张三"
Map<String, Object> condition = new HashMap<>(3);
condition.put("col", "name");
condition.put("op", "EQUAL");
condition.put("value", "张三");
request.putParam("condition", Collections.singletonList(condition));

// 查询工号(id)包含2019的记录,id LIKE "%2019%"
Map<String, Object> condition = new HashMap<>(3);
condition.put("col", "id");
condition.put("op", "LIKE");
condition.put("value", "2019");
request.putParam("condition", Collections.singletonList(condition));

// 查询工号包含2019并且名字中有三字的员工, id LIKE "%2019%" AND name LIKE "%三%"
Map<String, Object> subCondition1 = new HashMap<>(3);
subCondition1.put("col", "id");
subCondition1.put("op", "LIKE");
subCondition1.put("value", "2019");

Map<String, Object> subCondition2 = new HashMap<>(3);
subCondition1.put("col", "name");
subCondition1.put("op", "LIKE");
subCondition1.put("value", "三");

Map<String, Object> condition = new HashMap<>(2);
condition.put("op", "AND");
condition.put("condition", Arrays.asList(subCondition1, subCondition2));
request.putParam("condition", Collections.singletonList(condition));

出参

{
  "code": 200,
  "data": "{\"data\":[[\"工人1\",\"01\",\"工人1\",\"12312312301\",\"2019/07/02 00:00:00\",1,\"已发布\",0]],\1"nodes\":[\"name\",\"id\",\"remark\",\"phone\",\"hired_date\",\"dap_id_\",\"dap_status_\",\"dap_refcnt_\"],\"page\":{\"size\":1500,\"to\":1,\"total\":1}}",
  "id": "fd3d04805b6f4dd2a8e1376eabfe5b25"
}

其中data部分是个json字符串,包含的字段如下

字段名称 描述
nodes 物理表的属性列表
data 物理表的数据部分,是个二维数组,外层数组元素表示数据行,里层数据元素对应属性

上面的输出对应的表数据如下

1、写入主数据记录

主数据记录的来源有两种:

数字工厂控制台,通过页面新增主数据

通过api写入。只能用目标租户在入驻数字工厂时生成的appkey调用api写入主数据。这个appkey可以通过数字工厂的菜单:平台管理->阿里生态查到

(1)配置主数据来源

先创建被动接收模式的数据源,被动接收模式即是允许从open api写入主数据

创建被动接收模式的数据源之后,再将一种主数据关联到这个数据源,比如说人员

配置完成之后,人员主数据就不能通过页面修改,只能通过开放api修改

(2)api说明

入参

java demo

INSERT

import com.alibaba.cloudapi.sdk.model.ApiResponse;
import com.alibaba.fastjson.JSON;
import com.aliyun.iotx.api.client.IoTApiClientBuilderParams;
import com.aliyun.iotx.api.client.IoTApiRequest;
import com.aliyun.iotx.api.client.SyncApiClient;

import java.io.UnsupportedEncodingException;
import java.util.*;

public class InsertTest {
    public static void main(String[] args) {
        IoTApiRequest request = new IoTApiRequest();
        request.setVersion("1.0");
        String uuid = UUID.randomUUID().toString();
        String id = uuid.replace("-", "");
        request.setId(id);
        request.setApiVer("1.0.0");
        request.putParam("dataId", 1296L);
        request.putParam("action", "INSERT");
        String path = "/industry/masterdata/record/write";
        request.putParam("nodes", Arrays.asList("id", "name"));
        request.putParam("data", JSON.toJSONString(Arrays.asList(Arrays.asList(1, "张三"), Arrays.asList(2, "李四"))));
        IoTApiClientBuilderParams ioTApiClientBuilderParams = new IoTApiClientBuilderParams();

        ioTApiClientBuilderParams.setAppKey("");
        ioTApiClientBuilderParams.setAppSecret("");

        SyncApiClient syncApiClient = new SyncApiClient(ioTApiClientBuilderParams);
        try {
            //设置请求参数域名、path、request ,如果使用HTTPS,设置为true
            ApiResponse response = syncApiClient.postBody("api.link.aliyun.com", path, request, true, new HashMap<>());
            String responseString = new String(response.getBody(), "UTF-8");
            System.out.println("response code = " + response.getCode() + " response = " + responseString);

        } catch (UnsupportedEncodingException uee) {
            System.out.println(uee.getMessage());
            uee.printStackTrace();

        }
    }
}

MODIFY

import com.alibaba.cloudapi.sdk.model.ApiResponse;
import com.alibaba.fastjson.JSON;
import com.aliyun.iotx.api.client.IoTApiClientBuilderParams;
import com.aliyun.iotx.api.client.IoTApiRequest;
import com.aliyun.iotx.api.client.SyncApiClient;

import java.io.UnsupportedEncodingException;
import java.util.*;

public class UpdateTest {
    public static void main(String[] args) {
        IoTApiRequest request = new IoTApiRequest();
        request.setVersion("1.0");
        String uuid = UUID.randomUUID().toString();
        String id = uuid.replace("-", "");
        request.setId(id);
        request.setApiVer("1.0.0");
        request.putParam("dataId", 1296L);
        request.putParam("action", "MODIFY");
        String path = "/industry/masterdata/record/write";
        request.putParam("nodes", Collections.singletonList("phone"));
        request.putParam("data", JSON.toJSONString(Collections.singletonList("13101234567")));

        Map<String, Object> condition = new HashMap<>(3);
        condition.put("op", "EQUAL");
        condition.put("col", "name");
        condition.put("value", "张三");
        request.putParam("condition", Collections.singletonList(condition));

        IoTApiClientBuilderParams ioTApiClientBuilderParams = new IoTApiClientBuilderParams();

        ioTApiClientBuilderParams.setAppKey("");
        ioTApiClientBuilderParams.setAppSecret("");

        SyncApiClient syncApiClient = new SyncApiClient(ioTApiClientBuilderParams);
        try {
            //设置请求参数域名、path、request ,如果使用HTTPS,设置为true
            ApiResponse response = syncApiClient.postBody("api.link.aliyun.com", path, request, true, new HashMap<>());
            String responseString = new String(response.getBody(), "UTF-8");
            System.out.println("response code = " + response.getCode() + " response = " + responseString);
        } catch (UnsupportedEncodingException uee) {
            System.out.println(uee.getMessage());
            uee.printStackTrace();
        }
    }
}

ARCHIVE

import com.alibaba.cloudapi.sdk.model.ApiResponse;
import com.aliyun.iotx.api.client.IoTApiClientBuilderParams;
import com.aliyun.iotx.api.client.IoTApiRequest;
import com.aliyun.iotx.api.client.SyncApiClient;

import java.io.UnsupportedEncodingException;
import java.util.*;

public class ArchiveTest {
    public static void main(String[] args) {
        IoTApiRequest request = new IoTApiRequest();
        request.setVersion("1.0");
        String uuid = UUID.randomUUID().toString();
        String id = uuid.replace("-", "");
        request.setId(id);
        request.setApiVer("1.0.0");
        request.putParam("dataId", 1296L);
        request.putParam("action", "ARCHIVE");
        String path = "/industry/masterdata/record/write";

        Map<String, Object> condition = new HashMap<>(3);
        condition.put("op", "IN");
        condition.put("col", "id");
        condition.put("value", Arrays.asList(1, 2));
        request.putParam("condition", Collections.singletonList(condition));

        IoTApiClientBuilderParams ioTApiClientBuilderParams = new IoTApiClientBuilderParams();

        ioTApiClientBuilderParams.setAppKey("112345");
        ioTApiClientBuilderParams.setAppSecret("67890");

        SyncApiClient syncApiClient = new SyncApiClient(ioTApiClientBuilderParams);
        try {
            //设置请求参数域名、path、request ,如果使用HTTPS,设置为true
            ApiResponse response = syncApiClient.postBody("api.link.aliyun.com", path, request, true, new HashMap<>());
            String responseString = new String(response.getBody(), "UTF-8");
            System.out.println("response code = " + response.getCode() + " response = " + responseString);
        } catch (UnsupportedEncodingException uee) {
            System.out.println(uee.getMessage());
            uee.printStackTrace();
        }
    }
}

3、主数据消息通知格式

action分别对应创建/修改/删除/发布/归档的操作

nodes是实际数据表的属性,也是实际在界面上配置的属性编码。

dap_打头的属性是系统内部生成的

dap_id_是数据记录的编号

dap_status_是记录的状态,有待发布/已发布/已归档三个状态

对于多版本的主数据,还会有一个dap_row_version_的属性,记录版本号

data是一个二维数组,里面的每一个数组表示数据表中的一行数据,按顺序与nodes对应

如果某个属性是主数据类型,推送的消息中,会多一个属性,该属性的标识是原属性标识拼上“_key”;如果被关联的主数据是多版本的,还会多一个属性,该属性的标识是原属性标识拼上“_version”

例如,生产设备有一个所属设备型号的属性(equipment_model),推送的消息中,会多一个equipment_model_key的属性,其值为该所属设备型号唯一键的值;如果设备型号是多版本的,推送的消息中会多一个

equipment_model_version的属性,其值为所属设备型号的版本号的值。

{
  "appId": "xxxxx", // 对于SaaS应用,这个对应的是应用的实例id
  "serviceType": 1, // 对于主数据是固定的1
  "action": "INSERT"/"MODIFY"/"DELETE"/"PUBLISH"/"ARCHIVE",
  "data": [
    [
      "jdsi",
      "sdf",
      "",
      23,
      null,
      "使用中",
      "",
      "23",
      "待发布",
      "0"
    ]
  ],
  "dataId": 660,
  "masterDataName": "生产设备",
  "nodes": [
    "name",
    "asset_num",
    "brevity_code",
    "equipment_model",
    "iot_device_id",
    "usable_condition",
    "description",
    "dap_id_",
    "dap_status_",
    "dap_refcnt_"
  ]
}