fastbee/docs/knowledge-base/05-data-bridge-integration.md
2026-05-25 09:10:32 +08:00

664 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 数据桥接对接文档
本文面向需要和 岩安测控 对接的第三方系统,覆盖两类能力:
1. 第三方系统查询设备及其物模型。
2. 第三方系统获取设备上报数据,包括 岩安测控 主动推送和第三方主动查询。
本文基于当前仓库已有接口和数据桥接实现整理。默认服务根地址记为 `{BASE_URL}`,例如 `http://1.116.124.85:8080`。
## 对接方式总览
```mermaid
flowchart LR
Device[设备] --> Protocol[MQTT/TCP/HTTP/CoAP 等接入]
Protocol --> 岩安测控[岩安测控 数据处理]
岩安测控 --> Store[(MySQL/TDengine)]
岩安测控 --> Rule[数据流脚本/规则引擎]
Rule --> PushHTTP[主动推送 HTTP]
Rule --> PushMQTT[主动桥接 MQTT]
Rule --> PushDB[写入第三方数据库]
Third[第三方系统] --> API[REST API 主动查询]
API --> 岩安测控
```
| 场景 | 推荐方式 | 说明 |
| --- | --- | --- |
| 获取设备清单 | REST API 主动查询 | 查询 `iot_device` 设备列表或按设备编号查询详情。 |
| 获取设备物模型 | REST API 主动查询 | 查询产品级/设备级物模型,包含属性、事件、服务、计算模型。 |
| 获取实时值 | REST API 主动查询 | 查询设备运行时缓存值。 |
| 获取历史上报数据 | REST API 主动查询 | 查询数据中心历史接口,支持按设备、时间范围、物模型标识符过滤。 |
| 上报数据实时同步给第三方 | 数据桥接主动推送 | 通过数据流脚本触发 HTTP、MQTT 或数据库桥接。 |
| 第三方系统周期同步 | REST API 定时拉取 | 第三方按时间窗口分页/分段查询历史数据。 |
## 认证
除安全配置中明确放行的匿名接口外,REST API 默认需要登录 Token。
### 登录获取 Token
```http
POST {BASE_URL}/login
Content-Type: application/json
{
"username": "admin",
"password": "admin123",
"code": "验证码",
"uuid": "验证码uuid"
}
```
成功响应:
```json
{
"code": 200,
"msg": "操作成功",
"token": "eyJhbGciOiJIUzUxMiJ9..."
}
```
后续请求头:
```http
Authorization: Bearer {token}
```
如果系统关闭验证码或使用内部服务账号,可按部署配置调整登录参数。第三方生产对接建议单独创建服务账号,并只授予以下最小权限:
| 能力 | 权限标识 |
| --- | --- |
| 设备查询 | `iot:device:list`, `iot:device:query` |
| 物模型查询 | `iot:model:list`, `iot:model:query` |
| 历史数据查询 | `dataCenter:history:list` |
| 数据分析查询 | `dataCenter:analysis:list` |
| 数据桥接配置 | `iot:bridge:list`, `iot:bridge:add`, `iot:bridge:edit`, `iot:bridge:query` |
| 数据流脚本配置 | `iot:script:list`, `iot:script:add`, `iot:script:edit`, `iot:script:query` |
## 通用响应
普通接口返回 `AjaxResult`:
```json
{
"code": 200,
"msg": "操作成功",
"data": {}
}
```
分页接口返回 `TableDataInfo`:
```json
{
"code": 200,
"msg": "查询成功",
"total": 100,
"rows": []
}
```
常见约定:
- `code=200` 表示成功。
- 设备编号 `serialNumber` 在上报和查询中通常会转为大写处理,第三方建议统一传大写。
- 时间字段建议使用 `yyyy-MM-dd HH:mm:ss`。
## 查询设备
### 设备分页列表
```http
GET {BASE_URL}/iot/device/list?pageNum=1&pageSize=20&deviceName=xxx&serialNumber=ABC001&productId=1
Authorization: Bearer {token}
```
说明:
- Controller:`DeviceController.list`
- 权限:`iot:device:list`
- 返回:分页设备列表。
- 常用过滤字段来自 `Device` 对象,实际可用字段包括 `deviceId`、`deviceName`、`serialNumber`、`productId`、`tenantId`、`deptId`、`status` 等。
### 设备简短列表
适合第三方做设备下拉或轻量同步。
```http
GET {BASE_URL}/iot/device/shortList?pageNum=1&pageSize=100&productId=1
Authorization: Bearer {token}
```
### 查询所有设备简短列表
```http
GET {BASE_URL}/iot/device/all
Authorization: Bearer {token}
```
### 按设备 ID 查询详情
```http
GET {BASE_URL}/iot/device/{deviceId}
Authorization: Bearer {token}
```
### 按设备编号查询详情
```http
GET {BASE_URL}/iot/device/getDeviceBySerialNumber/{serialNumber}
Authorization: Bearer {token}
```
示例:
```http
GET {BASE_URL}/iot/device/getDeviceBySerialNumber/ABC001
Authorization: Bearer {token}
```
## 查询设备物模型
岩安测控 的物模型主要由 `productId + identifier` 定义。设备级计算物模型或特殊覆盖会结合 `serialNumber` 处理。
### 按产品 ID 和设备编号查询物模型
推荐第三方使用该接口,它会从缓存中合并产品级物模型和设备级处理结果。
```http
GET {BASE_URL}/iot/model/listByProductIdAndSerialNumber?productId=1&serialNumber=ABC001
Authorization: Bearer {token}
```
说明:
- Controller:`ThingsModelController.listByProductIdAndSerialNumber`
- 权限:`iot:model:list`
- 返回:`ThingsModelDTO` 列表。
典型返回项字段:
| 字段 | 含义 |
| --- | --- |
| `modelId` | 物模型 ID。 |
| `identifier` | 物模型标识符,历史数据查询和推送数据中使用该字段作为变量名。 |
| `modelName` | 物模型名称。 |
| `type` | 物模型类型,通常表示属性/功能/事件等。 |
| `datatype` | 数据类型定义,例如 number、string、bool、enum 等。 |
| `specs` | 枚举、单位、取值范围等扩展规格。 |
| `modelOrder` | 排序。 |
### 查询设备变量概况
```http
GET {BASE_URL}/iot/device/listThingsModel?deviceId=100&pageNum=1&pageSize=50
Authorization: Bearer {token}
```
可选过滤:
- `modelName`:按物模型名称模糊过滤。
- `type`:按物模型类型过滤。
### 查询产品物模型列表
```http
GET {BASE_URL}/iot/model/list?pageNum=1&pageSize=100&productId=1
Authorization: Bearer {token}
```
### 获取缓存 JSON 物模型
```http
GET {BASE_URL}/iot/model/cache/{productId}
Authorization: Bearer {token}
```
适用于需要拿完整 TSL JSON 的场景。
## 查询实时数据
### 查询设备运行时状态
```http
GET {BASE_URL}/iot/runtime/running?serialNumber=ABC001
Authorization: Bearer {token}
```
说明:
- Controller:`DeviceRuntimeController.runState`
- 返回:物模型当前值列表。
- 数据来源:运行时缓存/物模型缓存,不适合替代历史审计。
典型返回:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"id": "temperature",
"name": "温度",
"value": "26.5",
"type": 1,
"datatype": {
"type": "decimal",
"unit": "℃"
}
}
]
}
```
## 第三方主动查询设备上报数据
### 推荐接口:数据中心历史查询
```http
POST {BASE_URL}/data/center/deviceHistory
Authorization: Bearer {token}
Content-Type: application/json
{
"deviceId": 100,
"serialNumber": "ABC001",
"beginTime": "2026-05-24 00:00:00",
"endTime": "2026-05-24 23:59:59",
"identifierList": [
{ "identifier": "temperature", "type": 1 },
{ "identifier": "humidity", "type": 1 }
]
}
```
说明:
- Controller:`DataCenterController.deviceHistory`
- 权限:`dataCenter:history:list`
- `serialNumber` 必填。
- `identifierList` 建议传入,避免一次拉取全部变量导致响应过大。
- `type` 与物模型类型保持一致,常见属性数据为 `1`,以实际物模型返回值为准。
典型返回结构:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"2026-05-24 10:00:00": [
{ "temperature": "26.5" },
{ "humidity": "60" }
]
},
{
"2026-05-24 10:01:00": [
{ "temperature": "26.7" },
{ "humidity": "59" }
]
}
]
}
```
### 设备日志列表
适合按原始日志粒度查询。
```http
GET {BASE_URL}/iot/deviceLog/list?pageNum=1&pageSize=50&serialNumber=ABC001&beginTime=2026-05-24 00:00:00&endTime=2026-05-24 23:59:59
Authorization: Bearer {token}
```
说明:
- Controller:`DeviceLogController.list`
- 权限:`iot:device:list`
- 返回:分页日志行,字段来自 `DeviceLog`。
### 设备历史数据 GET 查询
```http
GET {BASE_URL}/iot/deviceLog/history?serialNumber=ABC001&beginTime=2026-05-24 00:00:00&endTime=2026-05-24 23:59:59
Authorization: Bearer {token}
```
返回按 `identifier` 分组的历史数据,适合较简单的查询场景。
### 按时间聚合的历史数据
```http
GET {BASE_URL}/iot/deviceLog/historyGroupByCreateTime?pageNum=1&pageSize=100&serialNumber=ABC001&beginTime=2026-05-24 00:00:00&endTime=2026-05-24 23:59:59
Authorization: Bearer {token}
```
### 网关计算物模型历史
```http
POST {BASE_URL}/data/center/deviceHistoryCalculated
Authorization: Bearer {token}
Content-Type: application/json
{
"deviceId": 100,
"serialNumber": "GW001",
"beginTime": "2026-05-24 00:00:00",
"endTime": "2026-05-24 23:59:59",
"identifierList": [
{ "identifier": "avg_temperature", "type": 1 }
]
}
```
### 周期拉取建议
第三方主动查询历史数据时,建议使用滑动时间窗口:
1. 首次同步:按小时或天分段拉取。
2. 增量同步:每 1 至 5 分钟拉取一次 `[lastSyncTime, now - 10s]`。
3. 去重键:`serialNumber + identifier + time`。
4. 重试:失败窗口不要丢弃,下次继续从 `lastSuccessEndTime` 拉取。
5. 大设备量:先同步设备列表,再按设备分批拉取,避免单次请求过大。
## 岩安测控 主动推送给第三方系统
岩安测控 当前主动推送走“数据流脚本 + 数据桥接”机制。设备上报后,`RuleProcess.processRuleScript(...)` 会按产品和事件类型找到启用的数据流脚本,然后执行脚本动作:
| 脚本动作 | `scriptAction` | 桥接组件 | 用途 |
| --- | ---: | --- | --- |
| HTTP 推送 | 3 | `httpBridge` | 调用第三方 HTTP 接口。 |
| MQTT 桥接 | 4 | `mqttBridge` | 发布到第三方 MQTT Broker。 |
| 数据库存储 | 5 | `databaseBridge` | 写入第三方数据库。 |
设备上报事件类型:
| 事件 | `scriptEvent` | 说明 |
| --- | ---: | --- |
| 设备上报 | 1 | 设备属性/数据上报后触发。 |
| 平台下发 | 2 | 平台服务/指令下发前后触发。 |
| 设备上线 | 3 | 设备上线触发。 |
| 设备离线 | 4 | 设备离线触发。 |
| HTTP 桥接入口数据 | 5 | 第三方调用 `/bridge/get|post|put` 后触发。 |
| MQTT 桥接入口数据 | 6 | MQTT 输入桥接触发,按脚本配置处理。 |
### 主动 HTTP 推送配置
1. 新增数据桥接。
```http
POST {BASE_URL}/iot/bridge
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "推送到第三方业务系统",
"enable": "1",
"type": 3,
"direction": 2,
"route": "https://third.example.com/iot/report",
"configJson": "{\"method\":\"POST\",\"hostUrl\":\"https://third.example.com/iot/report\",\"requestHeaders\":\"{\\\"Authorization\\\":\\\"Bearer xxx\\\"}\",\"requestQuerys\":\"{}\",\"requestConfig\":\"\",\"requestBody\":\"\"}"
}
```
字段说明:
| 字段 | 说明 |
| --- | --- |
| `type=3` | HTTP 推送。 |
| `direction=2` | 输出方向。 |
| `route` | 展示/路由字段,实际 HTTP 地址以 `configJson.hostUrl` 为准。 |
| `configJson.method` | HTTP 方法,如 `POST`。 |
| `configJson.hostUrl` | 第三方接收地址。 |
| `configJson.requestHeaders` | 请求头 JSON 字符串。 |
| `configJson.requestQuerys` | Query 参数 JSON 字符串。 |
| `configJson.requestBody` | 自定义请求体模板;为空时默认使用脚本上下文 payload。 |
2. 新增数据流脚本。
下面脚本展示的是核心意图:取得 `MsgContext`,设置桥接 ID,必要时重写 `payload/topic`。不同 LiteFlow 脚本语言的上下文变量写法可能不同,落地时以当前环境脚本校验结果为准。
```http
POST {BASE_URL}/iot/script
Authorization: Bearer {token}
Content-Type: application/json
{
"applicationName": "岩安测控",
"scriptName": "设备上报转第三方HTTP",
"scriptType": "script",
"scriptLanguage": "groovy",
"scriptPurpose": 1,
"scriptEvent": 1,
"scriptAction": 3,
"productId": 1,
"enable": 1,
"bridgeId": 10,
"scriptData": "def cxt = _meta.getContextBean(com.岩安测控.ruleEngine.context.MsgContext.class); cxt.setPayload(cxt.getPayload()); cxt.setData('httpBridgeID', 10);"
}
```
脚本关键点:
- `scriptPurpose=1` 表示数据流。
- `scriptEvent=1` 表示设备上报触发。
- `scriptAction=3` 表示执行 HTTP 推送组件。
- `productId` 表示该产品下设备上报时触发。
- 脚本必须向上下文写入 `httpBridgeID`,值为桥接配置 `bridge.id`;HTTP 桥接组件会从上下文读取该 ID。
- 脚本可改写 `payload`,从而控制推送给第三方的报文。
### 推荐 HTTP 推送报文
如果第三方希望使用稳定格式,建议脚本把 payload 改造成如下 JSON:
```json
{
"event": "property.post",
"productId": 1,
"serialNumber": "ABC001",
"protocolCode": "ModbusRtu",
"reportTime": "2026-05-24 10:00:00",
"data": [
{
"identifier": "temperature",
"value": "26.5"
},
{
"identifier": "humidity",
"value": "60"
}
],
"rawPayload": "[{\"id\":\"temperature\",\"value\":\"26.5\"}]"
}
```
当前默认上下文 payload 通常是设备上报解析后的物模型数据 JSON,字段形态可能随协议解析结果变化。生产对接建议在脚本中统一出站格式,避免第三方直接依赖内部 payload。
### 主动 MQTT 桥接配置
1. 新增 MQTT 桥接。
```http
POST {BASE_URL}/iot/bridge
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "第三方MQTT桥接",
"enable": "1",
"type": 4,
"direction": 2,
"route": "third/iot/report",
"configJson": "{\"name\":\"third-mqtt\",\"hostUrl\":\"broker.example.com:1883\",\"username\":\"user\",\"password\":\"pass\",\"clientId\":\"岩安测控-bridge\"}"
}
```
2. 新增脚本。
```json
{
"applicationName": "岩安测控",
"scriptName": "设备上报转第三方MQTT",
"scriptType": "script",
"scriptLanguage": "groovy",
"scriptPurpose": 1,
"scriptEvent": 1,
"scriptAction": 4,
"productId": 1,
"enable": 1,
"bridgeId": 11,
"scriptData": "def cxt = _meta.getContextBean(com.岩安测控.ruleEngine.context.MsgContext.class); cxt.setTopic('third/iot/' + cxt.getSerialNumber() + '/property/post'); cxt.setData('mqttBridgeID', 11);"
}
```
说明:
- MQTT 桥接组件使用 `cxt.getTopic()` 作为发布主题。
- 脚本必须写入 `mqttBridgeID`;MQTT 桥接组件会从上下文读取该 ID。
- `payload` 默认透传,建议按第三方格式重写。
### 写入第三方数据库
1. 配置数据库桥接数据源。
数据库桥接组件读取 `MultipleDataSource`,字段包括:
| 字段 | 说明 |
| --- | --- |
| `databaseSource` | 数据库类型,支持 MySQL、SQLServer、Oracle、PostgreSQL、H2。 |
| `host` | 地址,包含主机和端口。 |
| `dataBaseName` | 数据库名。 |
| `userName` | 用户名。 |
| `password` | 密码。 |
| `sql` | 要执行的 SQL,可使用上下文占位符替换。 |
2. 新增数据流脚本,写入 `databaseBridgeID`。
```json
{
"scriptPurpose": 1,
"scriptEvent": 1,
"scriptAction": 5,
"productId": 1,
"enable": 1,
"scriptData": "def cxt = _meta.getContextBean(com.岩安测控.ruleEngine.context.MsgContext.class); cxt.setData('databaseBridgeID', 20);"
}
```
注意:数据库桥接当前通过拼接后的 SQL 执行,生产环境必须控制 SQL 模板来源和字段转义,避免把不可信 payload 直接拼入 SQL。
## 第三方调用 岩安测控 桥接入口
岩安测控 也提供了桥接入口,第三方可以把数据送入 岩安测控 的数据流脚本:
| 方法 | 地址 | 说明 |
| --- | --- | --- |
| GET | `{BASE_URL}/bridge/get` | Query/Header 进入上下文。 |
| POST | `{BASE_URL}/bridge/post` | Body 进入上下文 payload。 |
| PUT | `{BASE_URL}/bridge/put` | Body 进入上下文 payload。 |
这些入口会以:
```text
scriptPurpose=1
scriptEvent=5
route=/bridge/get|/bridge/post|/bridge/put
```
查询并执行数据流脚本。
安全注意:当前安全配置未默认放行 `/bridge/**`,因此仍需要 Token,除非部署侧自行放开。
## 推荐对接方案
### 方案 A:实时同步优先
适用:第三方需要尽快拿到设备上报数据。
1. 第三方提供 HTTP 接收地址或 MQTT Broker。
2. 岩安测控 配置 `bridge`。
3. 岩安测控 为对应产品配置数据流脚本。
4. 第三方收到推送后按 `serialNumber + identifier + reportTime` 入库去重。
5. 第三方定时调用历史查询接口补偿漏推窗口。
推荐组合:
- 实时:HTTP 推送或 MQTT 桥接。
- 补偿:`POST /data/center/deviceHistory`。
### 方案 B:第三方定时拉取
适用:第三方不开放公网回调地址,或只需要分钟级/小时级同步。
1. 调用 `/iot/device/list` 或 `/iot/device/shortList` 同步设备。
2. 调用 `/iot/model/listByProductIdAndSerialNumber` 同步物模型。
3. 按设备调用 `/data/center/deviceHistory` 拉取上报数据。
4. 使用滑动窗口和去重键保证幂等。
### 方案 C:第三方数据库落库
适用:第三方只提供数据库账号,不提供 API。
1. 配置数据库连接。
2. 为产品配置数据流脚本。
3. 数据库桥接执行 SQL 写入第三方库。
不建议把该方案作为首选,原因是数据库账号权限、SQL 模板安全和网络连通性风险更高。
## 字段映射建议
第三方侧建议建立如下最小模型。
### 设备表
| 字段 | 来源 |
| --- | --- |
| `device_id` | `iot_device.deviceId` |
| `serial_number` | `iot_device.serialNumber` |
| `device_name` | `iot_device.deviceName` |
| `product_id` | `iot_device.productId` |
| `status` | `iot_device.status` |
| `tenant_id` | `iot_device.tenantId` |
### 物模型表
| 字段 | 来源 |
| --- | --- |
| `model_id` | `ThingsModelDTO.modelId` |
| `product_id` | 查询入参或返回字段 |
| `identifier` | `ThingsModelDTO.identifier` |
| `name` | `ThingsModelDTO.modelName` |
| `type` | `ThingsModelDTO.type` |
| `datatype` | `ThingsModelDTO.datatype` |
### 设备数据表
| 字段 | 来源 |
| --- | --- |
| `serial_number` | 设备编号 |
| `product_id` | 设备所属产品 |
| `identifier` | 物模型标识符 |
| `value` | 上报值 |
| `report_time` | 历史接口返回的时间或推送报文时间 |
| `source` | `pull` 或 `push` |
| `raw_payload` | 原始推送/查询报文 |
## 幂等、重试和安全
- 推送接口必须幂等,建议第三方以 `serialNumber + identifier + reportTime` 去重。
- 岩安测控 主动推送失败当前主要依赖组件日志排查,建议第三方同时做主动拉取补偿。
- 第三方主动查询必须按时间窗口限制范围,避免一次拉取全量历史。
- HTTP 推送建议使用 HTTPS、固定 Token 或签名头。
- MQTT 桥接建议使用独立账号和最小 Topic 权限。
- 数据库桥接建议只授予 INSERT 权限,避免执行 DDL 或高危 SQL。
## 当前实现注意点
- `岩安测控-mq` 当前核心是 JVM 内本地阻塞队列,不是外部 RocketMQ 的强持久化链路。
- 主动 HTTP 推送的默认 payload 由规则脚本上下文决定,生产对接应在脚本中统一出站报文。
- 数据桥接脚本查询按 `productId + scriptPurpose + scriptEvent + enable` 等条件筛选;产品维度必须配置正确。
- 业务表关系大多没有数据库外键,第三方不要依赖数据库外键判断关联,应以 API 返回和业务字段为准。