fastbee/docs/knowledge-base/06-platform-integration-design.md
2026-07-31 05:53:20 +08:00

44 KiB
Raw Blame History

平台对接设计文档

本文档按当前项目代码反补,描述已落地的平台对接模块。相关实现主要位于:

  • fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform
  • fastbee-open-api/src/main/java/com/fastbee/controller/platform
  • yack-web/src/views/platform
  • sql/platform-integration.sql
  • sql/platform-dcp-init-data.sql

1. 背景与目标

FastBee 需要将设备上报数据推送到第三方监测平台,当前已落地 DCP 结构监测 SaaS。模块目标是把"平台、用户认证、接口、流程、设备绑定、字段映射、推送日志"配置化,降低后续接入新平台或调整 DCP 接口的代码改动。

当前实现采用两条主链路:

  1. 流程执行链路:用于用户初始化、Token 刷新、设备入网等多步骤调用。
  2. 实时推送链路:设备上报后异步命中平台设备绑定,由 DCP 适配器组装请求体并推送。

设计边界:

  • 流程编排只负责顺序 HTTP 调用、上下文传递、结果持久化。
  • 实时数据推送不走流程引擎,按设备绑定上的 push_interface_id、field_mapping、extra_mapping 直接组装请求。
  • 当前只有 DCP 适配器实现,平台路由按 platform_instance.code 分发,code=dcp 时进入 DcpPlatformAdapter。
  • 网关多子设备场景通过独立子表 platform_device_sub_mapping 管理,不再只依赖 extra_mapping。
  • Token/认证已从平台级(PlatformInstance)迁移到用户级(PlatformUserAuth),每个用户有独立的 Token、过期时间和项目权限。

2. 四种执行模式

2.1 流程编排

适用于多个接口必须顺序调用,且后续步骤依赖前序步骤返回值的场景。

流程 触发方式 典型步骤 结果落库
用户初始化 用户管理页点击初始化 dcp_auth -> dcp_get_user -> dcp_get_projects platform_user_auth.access_token、platform_user_auth.platform_user_id、platform_user_auth.platform_user_name、platform_user_auth.extra_config
Token 刷新 HTTP 调用前自动触发 dcp_auth platform_user_auth.access_token、platform_user_auth.token_expires_at
直连设备入网 设备绑定页点击入网 dcp_create_point -> 对应传感器绑定接口 主表 platform_point_id、platform_device_id、net_status
网关设备入网 设备绑定页点击入网 对每个 monitor 子设备逐个执行入网流程 子表 platform_point_id、platform_device_id、net_status;全部 monitor 成功后主表 net_status=1

入口代码:

  • PlatformUserAuthServiceImpl.initialize(Long userAuthId) - 用户初始化
  • PlatformDeviceMappingServiceImpl.onboard(Long mappingId) - 设备入网
  • PlatformFlowExecutor.execute(PlatformFlow flow, FlowContext context)

注意: 流程手动执行端点(POST /platform/flow/{id}/execute)已移除,流程执行只能通过业务入口(初始化/入网/Token 刷新)触发。

2.2 直接调用实时推送

设备上报后由 DeviceReportTrigger.onDeviceReport(serialNumber, payload) 异步触发:

  1. PlatformDeviceMappingMapper.selectBySerialNumber 同时匹配直连主设备和网关子设备。
  2. 过滤未启用的平台和绑定关系。
  3. 按平台 code 分发,目前 dcp 进入 DcpPlatformAdapter。
  4. 加载 PlatformUserAuth,传入适配器用于 Token 获取和字段映射。
  5. 适配器按推送接口的 request_schema 与设备绑定字段映射组装请求体。
  6. 检查并刷新 Token(从 userAuth 获取)。
  7. 发送 HTTP POST,写入 platform_push_log。

实时推送不经过 PlatformFlowExecutor,因此不会使用流程步骤的 param_mapping、result_key、result_save。

2.3 定时推送

在实时推送基础上,新增按时间间隔推送最新数据的模式。通过 platform_device_mapping.push_mode 字段控制:

push_mode 模式 行为
1(默认) 实时推送 设备上报后立即推送,沿用原有链路
2 定时推送 仅由调度器按 push_interval(分钟)推送,DeviceReportTrigger 跳过实时推送

调度器 PlatformPushScheduledJob 使用 Spring @Scheduled(fixedDelay = 300_000) 每 5 分钟检查一次:

  1. 查询所有 enable='1'、push_mode=2、push_interval > 0 的绑定。
  2. 加载绑定关联的 PlatformUserAuth,逐条判断 now - last_push_time >= push_interval * 60 * 1000;从未推送过的立即触发。
  3. 从 Redis 缓存读取最新数据,按直连/网关分别调用 DcpPlatformAdapter 推送。
  4. 推送成功后更新 last_push_time;失败时保留不变,下次继续尝试。

定时推送不经过 PlatformFlowExecutor,推送数据来自 Redis 缓存而非实时 payload,因此依赖设备数据及时更新到 Redis。

2.4 Token 自动刷新

流程执行器调用 ensureToken(FlowContext ctx):

  • 从 FlowContext.platformUserAuth 获取用户认证信息。
  • access_token 为空,或 token_expires_at 距当前时间不足 5 分钟时视为过期。
  • 自动查找同平台 flow_type=3 且 enable='1' 的 Token 刷新流程。
  • 刷新流程本身不注入 Authorization,避免递归。
  • 刷新成功后更新 platform_user_auth.access_token 和 token_expires_at(不再是 platform_instance)。

架构变更: Token 管理已从 PlatformInstance 迁移到 PlatformUserAuth。PlatformInstance.access_token / PlatformInstance.token_expires_at 字段不再使用,所有 Token 读写都通过 platform_user_auth 表。


3. 数据库设计(8 张表)

DDL 以 sql/platform-integration.sql 为准。

3.1 platform_instance 平台实体

保存第三方平台实例和认证模板。

关键字段:

字段 说明
name 平台名称
code 平台唯一标识,当前 DCP 为 dcp,有唯一索引
base_url 平台基础 URL
auth_type 认证类型:1=密码Token、2=ApiKey、3=OAuth2
auth_config 认证配置模板 JSON,新增用户时可复制;例如 userName、password、clientId、scope
extra_config 平台级业务参数,如站点编码
enable 是否启用
tenant_id 租户 ID

废弃字段: access_token、token_expires_at、init_status 为历史遗留,实际不再使用。Token 和初始化状态现在存储在 platform_user_auth 表。

架构说明: 一个平台可以对应多个用户认证(platform_user_auth),每个用户有独立的 Token、项目权限。platform_instance.auth_config 仅作为新增用户时的默认模板。

3.2 platform_user_auth 平台用户认证

支持一个平台对应多个用户认证,每个用户有独立的 Token 和项目权限。

关键字段:

字段 说明
platform_id 平台实例 ID
platform_user_id 平台侧用户 ID(如 DCP 的 userId),初始化流程执行后回填,允许为空
platform_user_name 平台侧用户名,初始化流程执行后回填
auth_config 用户认证凭证 JSON,例如 {userName, password, clientId, scope}
access_token 该用户的 Token
token_expires_at Token 过期时间
init_status 初始化状态:0=未初始化、1=已初始化
extra_config 用户级配置,如项目信息等
enable 是否启用

使用流程:

  1. 新增用户认证:仅输入认证配置(用户名/密码/clientId/scope)
  2. 用户记录处于 init_status=0,platform_user_id 为空
  3. 执行初始化流程:
    • 用认证配置获取 Token
    • 用 Token 获取 userId 和用户名
    • 自动回填 platform_user_id 和 platform_user_name
    • 标记 init_status=1
  4. 设备绑定时选择已初始化的用户,再获取项目列表

唯一约束: uk_platform_user (platform_id, platform_user_id),注意 platform_user_id 允许为空(未初始化时)。

3.3 platform_interface 接口定义

保存第三方 HTTP 接口定义。

关键字段:

字段 说明
platform_id 所属平台
name / code 接口名称与接口标识
method GET、POST、PUT、DELETE
path 接口路径,支持 {userId} 形式路径变量
content_type 默认 application/json,认证接口可用 application/x-www-form-urlencoded
request_schema 入参字段定义
response_schema 出参字段定义,支持按路径抽取
sort_order 排序

request_schema 在流程执行器中支持的 source:

source 含义
fixed 固定值,取 value
uuid 每次请求生成新的 UUID
auth 优先从 platform_user_auth.auth_config 取值,fallback 到 platform_instance.auth_config
extra 优先从 platform_user_auth.extra_config 取值,fallback 到 platform_instance.extra_config
context 从 FlowContext 按 contextKey 取值
device 从 PlatformDeviceMapping / 当前网关子设备上下文取 platform_device_id、platform_point_id、serial_number、device_name、gateway_serial_number、monitor_serial_number
thingsModel 从流程上下文 payload 按物模型标识取值,通常用于手动流程或扩展流程
ts 从流程上下文 payload 取时间戳
onboard_config 入网参数,直接取接口 request_schema 或步骤覆盖配置中的 value,不再读取子映射表 onboard_config
subDeviceSensorIds 收集子设备 sensorId 列表
cascadedSubDeviceSensorIds 串联子设备入网:从当前 monitor 到末端 monitor 收集 sensorId,并按末端到当前倒序返回
cascadedSubDeviceDepths 串联子设备入网:从配置的顺序数组中截取当前 monitor 到末端 monitor,并按末端到当前倒序返回
referenceSubDeviceSensorIdPairs 收集 [基准点sensorId, 监测点sensorId] 配对列表,可能展开为多次请求

onboard_config 是设备入网专用的可配置入参。接口管理页或流程步骤覆盖页直接维护 value:

{"name":"sensorDepths","type":"array","source":"onboard_config","value":[1.7,3.4,5.1,6.8]}
{"name":"holeDepth","type":"number","source":"onboard_config","value":6.8}

网关多 monitor 入网时,PlatformDeviceMappingServiceImpl 按 PlatformDeviceSubMappingMapper.selectByParentId 返回顺序筛选 monitor,并把当前下标写入 FlowContext.currentMonitorIndex。流程执行器解析 onboard_config 时:

  • value 不是数组:每个子设备使用同一个值。
  • value 是数组:按当前 monitor 下标取对应元素。
  • schema type=array 且取到单值:最终请求体会包装为单元素数组,例如 1.7 -> [1.7]。
  • 数组长度小于待入网 monitor 数量:直接抛错,避免错绑。

实时推送适配器中支持的 source:

source 含义
fixed 固定值
device 从绑定关系取平台设备/测点/序列号
auth 优先从 platform_user_auth 认证 JSON 取值
extra 优先从 platform_user_auth 扩展 JSON 取值
thingsModel / identifier 按 field_mapping 或 itemFieldMappings 从上报 payload 取物模型值
ts 从物模型值的 __ts 派生时间,缺省为当前时间
context 优先从 payload 的 contextKey 取值;reportTime/epoch 会按时间字段处理
subDeviceId 数组元素内的子设备平台 ID,如 DCP sensorId

response_schema.path 支持对象路径、数组下标路径,例如 id、[0].id、$.data.id。

3.4 platform_flow 流程定义

关键字段:

字段 说明
platform_id 所属平台
name / code 流程名称与标识
flow_type 1=平台初始化、2=设备入网、3=Token刷新
sensor_type 入网流程适用传感器类型,如 inclino、deep_inclino、stress、hyeto、static_level
enable 是否启用

3.5 platform_flow_step 流程步骤

关键字段:

字段 说明
flow_id 所属流程
step_order 执行顺序
interface_id 关联接口
param_mapping 覆盖接口默认入参来源
result_key 步骤结果写入上下文的 key
result_save 将响应字段持久化到用户认证或设备绑定

result_save 支持:

  • platform_user_auth.access_token - 用户级 Token
  • platform_user_auth.token_expires_at - 值按 expires_in 秒数转换为 Date
  • platform_user_auth.platform_user_id - 平台侧用户 ID
  • platform_user_auth.platform_user_name - 平台侧用户名
  • extra_config.xxx - 用户级配置
  • device_mapping.platform_device_id
  • device_mapping.platform_point_id

注意: result_save 已从写 platform_instance.* 改为写 platform_user_auth.*。配置中的 platform_instance.access_token 等旧值应更新为 platform_user_auth.access_token。

3.6 platform_device_mapping 设备绑定主表

保存 FastBee 设备与第三方平台的绑定关系。

关键字段:

字段 说明
platform_id 所属平台
platform_user_auth_id 关联用户认证 ID,选择已初始化的用户
platform_project_id 平台侧项目 ID,用户选择后存储
platform_project_name 平台侧项目名称
device_id / serial_number FastBee 设备 ID 与序列号
device_name 推送给平台的设备名称,默认取平台设备名称,可手动修改
platform_device_id 平台侧 deviceId,直连设备入网后回填
platform_point_id 平台侧 pointId,直连设备入网后回填
sensor_type 传感器类型,来自 platform_sensor_type 字典;当前包含 inclino、deep_inclino、stress、hyeto、static_level
device_type 0=直连设备、1=网关设备
push_interface_id 推送接口 ID,用于动态字段映射和推送路径
field_mapping 标量字段映射,格式如 [{identifier, platformField, transform}]
extra_mapping 数组字段、静力水准、上报间隔等扩展配置
net_status 0=未入网、1=已入网
enable 是否启用
push_mode 推送模式:1=实时、2=定时,默认 1
push_interval 定时推送间隔(分钟),0 表示不自动推送
last_push_time 最近推送时间,由定时调度器更新

级联选择流程: 新增设备绑定时,先选择平台 → 加载已初始化用户列表 → 选择用户 → 加载项目列表 → 选择项目。

3.7 platform_device_sub_mapping 子设备映射表

用于网关多子设备场景,尤其是静力水准和深部位移。

关键字段:

字段 说明
parent_mapping_id 主绑定 ID
sub_serial_number 子设备序列号
sub_device_name 子设备名称
device_role reference=基准点、monitor=监测点
platform_device_id 平台分配给该监测点的 deviceId
platform_point_id 平台分配给该监测点的 pointId
onboard_config 子设备平台标识 JSON,例如 sensorId、refSensorId、refSerialNumber;普通绑定接口参数不放这里
net_status 子设备入网状态

唯一约束:parent_mapping_id + sub_serial_number。

3.8 platform_push_log 推送日志

记录流程步骤调用、实时数据推送和失败信息。

关键字段:

字段 说明
platform_id 平台 ID
flow_id / step_order 流程调用时记录流程与步骤
device_id / serial_number 设备维度
interface_code 接口标识
request_url / request_body 请求信息
response_code / response_body 响应信息
status 0=进行中、1=成功、2=失败
error_msg 错误信息
idempotency_key 幂等键,当前表结构保留
log_type 1=数据推送、2=操作日志
retry_count 重试次数
cost_ms 耗时

4. 后端代码结构

fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/
├── domain/
│   ├── PlatformInstance
│   ├── PlatformUserAuth              # 平台用户认证,Token/认证从这里获取
│   ├── PlatformInterface
│   ├── PlatformFlow
│   ├── PlatformFlowStep
│   ├── PlatformDeviceMapping
│   ├── PlatformDeviceSubMapping
│   └── PlatformPushLog
├── mapper/
│   ├── PlatformInstanceMapper
│   ├── PlatformUserAuthMapper        # 用户认证 Mapper
│   ├── PlatformInterfaceMapper
│   ├── PlatformFlowMapper
│   ├── PlatformFlowStepMapper
│   ├── PlatformDeviceMappingMapper
│   ├── PlatformDeviceSubMappingMapper
│   └── PlatformPushLogMapper
├── service/
│   ├── IPlatformInstanceService       # 仅保留 selectList
│   ├── IPlatformUserAuthService       # 用户认证(新增,接管初始化/项目/Token)
│   ├── IPlatformFlowService           # 移除 executeManually
│   ├── IPlatformDeviceMappingService
│   ├── IPlatformDeviceSubMappingService
│   ├── IPlatformPushLogService
│   └── impl/
├── flow/
│   ├── PlatformFlowExecutor           # Token 从 userAuth 获取
│   └── FlowContext                    # platformUserAuth 为必填字段
├── adapter/
│   └── dcp/
│       ├── DcpPlatformAdapter         # 所有推送方法增加 userAuth 参数
│       └── StaticLevelBufferService
├── trigger/
│   └── DeviceReportTrigger            # 加载 userAuth 传入适配器
└── job/
    └── PlatformPushScheduledJob       # 加载 userAuth 传入适配器
fastbee-open-api/src/main/java/com/fastbee/controller/platform/
├── PlatformInstanceController       # /platform/instance(移除 initialize、getProjects)
├── PlatformUserAuthController       # /platform/user-auth(新增,接管初始化/项目/用户管理)
├── PlatformInterfaceController      # /platform/interface
├── PlatformFlowController           # /platform/flow(移除 execute 端点)
├── PlatformDeviceMappingController  # /platform/device-mapping
└── PlatformPushLogController        # /platform/push-log

5. 核心逻辑

5.1 流程执行器

PlatformFlowExecutor 的执行顺序:

execute(flow, context)
  查询 flow 下所有 step,按 step_order 顺序执行
  对每个 step:
    1. 读取关联 platform_interface
    2. 合并 request_schema 与 step.param_mapping
    3. 按 source 解析参数(优先从 userAuth 获取,fallback 到 platform.authConfig)
    4. 若参数中存在 list-of-lists,展开为多批请求
    5. 每批请求刷新 uuid 参数
    6. 替换 path 中的 {变量}
    7. GET 参数放 query;form 接口编码为 x-www-form-urlencoded;其他接口发 JSON body
    8. 非认证步骤自动注入 Authorization: Bearer {token}(从 userAuth 获取)
    9. 执行 HTTP
    10. 按 response_schema 抽取响应,写入 FlowContext
    11. 按 result_save 持久化到 platform_user_auth 或 device_mapping
    12. 异步写 platform_push_log,log_type=2

FlowContext 构造: platformUserAuth 是必填字段,所有构造函数都需要传入。旧的 FlowContext(PlatformInstance) 构造函数已移除。

重要变更: persistResultSave 不再向 platform_instance 表写 Token/过期时间,而是写入 platform_user_auth 表。result_save 配置中的 saveTo 路径应使用 platform_user_auth.* 前缀而非 platform_instance.*。

auth 和 extra 参数来源优先级:

  • auth source:PlatformUserAuth.auth_config → PlatformInstance.auth_config
  • extra source:PlatformUserAuth.extra_config → PlatformInstance.extra_config

批量展开用于 referenceSubDeviceSensorIdPairs 这类参数。例如静力水准入网时,一个网关下多个 monitor 子设备会拆成多次绑定请求。

5.2 设备入网

入口:PlatformDeviceMappingServiceImpl.onboard。

直连设备:

  1. 根据设备绑定的 platformUserAuthId 查询用户认证信息。
  2. 查找同平台、flow_type=2、sensor_type 匹配且启用的流程。
  3. 创建 FlowContext(platform, mapping, userAuth)。
  4. 执行流程。
  5. 若主表已有 platform_device_id,将主绑定 net_status 改为 1。

网关设备:

  1. 读取 platform_device_sub_mapping。
  2. 筛选 device_role=monitor 且未入网的子设备。
  3. 对每个 monitor 创建独立 FlowContext,通过构造函数一次性传入所有上下文字段:
    • platformUserAuth = 设备绑定关联的用户认证
    • currentMonitorSerialNumber、currentMonitorDeviceName
    • currentMonitorIndex = monitor 在待入网 monitor 列表中的下标
    • subMappingOnboardConfig = monitor.onboard_config
  4. 执行同一个入网流程。若绑定接口 schema 使用 source=onboard_config 且 value 为数组,会按 currentMonitorIndex 拆出当前子设备的值。
  5. 从上下文 step1.id、step2.id 取 pointId/deviceId,写回当前 monitor 子表。
  6. 所有 monitor 都入网后,主绑定 net_status=1。
  7. 若部分成功部分失败,已成功子设备会保留结果,接口返回明确失败信息。

注意: 入网不再校验 platformProjectId 是否为空。网关子设备 FlowContext 现在通过构造函数一次性设置所有字段,不再使用 setXxx 逐个设置。

5.3 设备上报触发

DeviceReportTrigger.onDeviceReport 使用异步线程池 FastBeeConstant.TASK.PUSH_TO_THIRD_PLATFORM_ASYNC。

遍历匹配的 PlatformDeviceMapping 时,先检查 push_mode:

  • push_mode=1 或未配置:执行实时推送(原有行为)。
  • push_mode=2:跳过实时推送,仅由 PlatformPushScheduledJob 定时推送。

匹配逻辑在 PlatformDeviceMappingMapper.xml:

SELECT m.* FROM platform_device_mapping m
WHERE m.serial_number = #{serialNumber} AND m.enable = '1'
UNION
SELECT m.* FROM platform_device_mapping m
INNER JOIN platform_device_sub_mapping s ON s.parent_mapping_id = m.id
WHERE s.sub_serial_number = #{serialNumber} AND m.enable = '1'

分发规则:

  • 加载 PlatformUserAuth,未启用或无用户认证时跳过。
  • platform.code=dcp 且 device_type=0:调用 DcpPlatformAdapter.pushData(platform, userAuth, mapping, ...)。
  • platform.code=dcp 且 device_type=1:先在子表中按上报序列号找 PlatformDeviceSubMapping,找到后调用 pushGatewayData(platform, userAuth, mapping, subMapping, ...)。
  • 未知平台 code 只记录 warning,不推送。

5.4 定时推送调度器

PlatformPushScheduledJob 位于 com.fastbee.iot.platform.job,核心逻辑:

@Scheduled(fixedDelay = 300_000)
public void execute() {
    // 1. 查询所有启用的定时推送配置(pushMode=2)
    List<PlatformDeviceMapping> mappings = mappingService.list(
        Wrappers.<PlatformDeviceMapping>lambdaQuery()
            .eq(PlatformDeviceMapping::getEnable, "1")
            .eq(PlatformDeviceMapping::getPushMode, 2)
            .gt(PlatformDeviceMapping::getPushInterval, 0)
    );
    // 2. 遍历检查是否到达推送时间
    for (PlatformDeviceMapping mapping : mappings) {
        if (isDue(mapping, now)) {
            pushLatestData(mapping);
        }
    }
}

isDue 判断:push_interval 转换为毫秒后,若 nowMs - lastPushTime.getTime() >= intervalMs 则触发。lastPushTime 为空时立即触发。

pushLatestData 加载 PlatformUserAuth,从 Redis 缓存读取设备最新数据,按 device_type 分发:

  • 直连设备(device_type=0):调用 DcpPlatformAdapter.pushData(platform, userAuth, mapping, ...)。
  • 网关设备(device_type=1):遍历所有子设备,逐个从 Redis 读取并调用 DcpPlatformAdapter.pushGatewayData(platform, userAuth, mapping, subMapping, ...)。

推送完成后调用 IPlatformDeviceMappingService.updateLastPushTime 更新 last_push_time。

数据流:

设备上报数据 → Redis 缓存更新
                    ↓
┌──────────────────────────────────────┐
│  PlatformPushScheduledJob(每5分钟) │
│  push_mode=2 且达到时间间隔          │
│  → 加载 PlatformUserAuth             │
│  → 从 Redis 读最新数据               │
│  → 推送到平台(传入 userAuth)       │
│  → 更新 last_push_time               │
└──────────────────────────────────────┘

5.5 DCP 普通推送

DCP 实时推送必须在设备绑定中配置 push_interface_id:

  • 使用推送接口的 path、code、request_schema 组装并发送请求。
  • 未配置推送接口时,DcpPlatformAdapter.resolvePushPath 直接抛错,不再按 sensor_type 回退到内置路径。
  • resolveInterfaceCode 仍会在接口 code 为空时使用 dcp_push_{sensorType} 作为日志标识兜底。

请求体构建规则:

  • payload 支持 JSON 对象和数组两种格式。
  • 数组格式按 id 或 identifier 转成 {identifier: value},并把 ts 保存为 {identifier}__ts。
  • 有 request_schema 时按 schema 组装。
  • 若 schema 组装结果为空,或没有任何 payload 驱动字段,则回退到 legacy 组装。
  • 普通推送请求体最终是数组:[ {...} ]。

所有推送方法签名增加了 PlatformUserAuth userAuth 参数,用于 Token 获取:

// 变更后
dcpAdapter.pushData(platform, userAuth, mapping, reportSerialNumber, payload);
dcpAdapter.pushGatewayData(platform, userAuth, mapping, subMapping, reportSerialNumber, payload);

时间字段会统一转为带时区的 ISO-8601 毫秒格式,支持输入毫秒时间戳、秒时间戳、ISO 字符串、本地时间字符串。

5.6 网关与数组字段推送

网关推送入口:DcpPlatformAdapter.pushGatewayData。

当推送接口没有 type=array 字段时:

  • 按普通子设备推送处理。
  • deviceId 优先使用子表 platform_device_id。
  • 字段映射仍复用主表 field_mapping。

当推送接口包含 type=array 字段时:

  • 当前实现以静力水准为主要落地场景。
  • extra_mapping 可配置:
    • fieldName:数组字段名,默认 sensorData
    • itemFieldMappings:数组元素物模型字段映射
    • reportIntervalSec:缓存有效时间,默认 300 秒
  • 子设备 onboard_config.sensorId 作为数组元素的 sensorId。
  • monitor 子设备 platform_device_id 作为请求体顶层 deviceId。

静力水准缓存逻辑:

  1. 基准点 device_role=reference 上报时,只缓存当前 item,不立即推送。
  2. 监测点 device_role=monitor 上报时,查找其 onboard_config.refSerialNumber 对应的基准点缓存。
  3. 若基准点缓存新鲜,推送:
[
  {
    "deviceId": "监测点平台deviceId",
    "sensorData": [
      {"sensorId": "基准点sensorId", "height": 0.0, "temperature": 0.0, "epoch": "..."},
      {"sensorId": "监测点sensorId", "height": 0.0, "temperature": 0.0, "epoch": "..."}
    ],
    "ct": 1
  }
]
  1. 若监测点先到或基准点缓存过期,监测点数据会写入 Redis monitor 缓存。

缓存键:

  • static_level:ref:{platformId}:{fieldName}:{serialNumber}
  • static_level:monitor:{platformId}:{fieldName}:{serialNumber}

Redis TTL 为 reportIntervalSec * 2,读取时超过 reportIntervalSec * 1.5 视为过期。

5.7 深部位移整孔批量推送

深部位移等整孔场景可在推送接口上开启 is_batch_push=1,由 DcpPlatformAdapter.pushGatewayData 进入批量推送分支。该能力仍属于网关数组字段推送的一种特化形态,不单独走流程引擎。

核心行为:

  1. 查询当前绑定下所有 device_role=monitor 的子设备,顺序以 platform_device_sub_mapping 返回顺序为准(深部位移场景为浅到深)。
  2. 当前上报子设备使用本次 payload,其余子设备从 Redis 读取缓存;读取 Redis 时使用子设备真实 product_id + sub_serial_number,避免误用网关主设备产品 ID。
  3. 每个 monitor 组装为一个请求体对象,deviceId 使用该 monitor 入网后回填的 platform_device_id,xTotalBias、yTotalBias 等主体字段使用该 monitor 自身数据。
  4. 每个对象中的 sensorData 不是单点,而是从该 monitor 到末端 monitor 的后缀数组;深部位移中表现为"当前深度 + 更深点位",顺序保持从浅到深。
  5. 最浅节点对象包含全孔从浅到深的点位数据,最深节点对象只包含自己。
  6. 最终一次 HTTP 请求推送整个数组。

批量推送不再从"数据推送接口"的 request_schema 读取入网参数 sensorDepths。sensorDepths、holeDepth 等仍属于深部位移入网/绑定接口配置;数据推送接口只需配置实际推送字段,例如 deviceId、sensorData、累计偏移量等。

示例请求体结构(4 个串联点,从浅到深为 A/B/C/D):

[
  {
    "deviceId": "A平台deviceId",
    "xTotalBias": "A累计X偏移",
    "yTotalBias": "A累计Y偏移",
    "sensorData": ["A点数据", "B点数据", "C点数据", "D点数据"],
    "ct": 1
  },
  {
    "deviceId": "B平台deviceId",
    "xTotalBias": "B累计X偏移",
    "yTotalBias": "B累计Y偏移",
    "sensorData": ["B点数据", "C点数据", "D点数据"],
    "ct": 1
  },
  {
    "deviceId": "C平台deviceId",
    "xTotalBias": "C累计X偏移",
    "yTotalBias": "C累计Y偏移",
    "sensorData": ["C点数据", "D点数据"],
    "ct": 1
  },
  {
    "deviceId": "D平台deviceId",
    "xTotalBias": "D累计X偏移",
    "yTotalBias": "D累计Y偏移",
    "sensorData": ["D点数据"],
    "ct": 1
  }
]

配置与排查要点:

  • 在平台接口管理中,为需要整孔推送的"数据推送接口"开启批量推送,即 platform_interface.is_batch_push=1。
  • 推送接口 request_schema 必须包含实际数据推送字段;不要把入网接口的 sensorDepths 当作批量推送前置条件。
  • 子设备必须已入网,monitor 子表行必须有 platform_device_id。
  • Redis 中需要有各 monitor 子设备的最新物模型缓存;缺失数据的点位不会进入对应后缀 sensorData,若某 monitor 自身无数据则该 monitor 对象会被跳过。
  • 推送日志可在 platform_push_log 查看;成功日志会出现 [DCP] 批量推送完成。

5.8 Token 管理

架构变更: Token 管理已从平台级迁移到用户级。PlatformInstance 上的 access_token、token_expires_at 字段不再使用。

PlatformFlowExecutor.ensureToken(FlowContext ctx):

  • 从 ctx.getPlatformUserAuth() 获取用户认证信息。
  • access_token 为空,或 token_expires_at 距当前时间不足 5 分钟时视为过期。
  • 自动查找同平台 flow_type=3 且 enable='1' 的 Token 刷新流程。
  • 创建带有 platform 和 userAuth 的 FlowContext 执行刷新流程。
  • 刷新流程本身不注入 Authorization,避免递归。
  • 刷新成功后更新 platform_user_auth.access_token 和 token_expires_at。
  • Token 刷新流程最后步骤应配置 result_key=tokenStep,以便 refreshToken 方法提取 access_token 和 expires_in。

DcpPlatformAdapter.ensureToken(适配器层):

  • 读取 platformUserAuth.access_token 和 token_expires_at 判断是否过期。
  • 过期时查找 flow_type=3 流程并执行 flowExecutor.execute(tokenFlow, ctx)。
  • 刷新后重新从数据库读取最新 platform_user_auth 记录。
  • 返回 platformUserAuth.access_token。

5.9 日志与重试

  • 流程步骤日志:log_type=2。
  • 实时数据推送日志:log_type=1。
  • HTTP 成功时记录 URL、请求体、响应体、耗时。
  • HTTP 失败时记录 status=2 和 error_msg。
  • PlatformPushLogController 暴露 POST /platform/push-log/{id}/retry,重试逻辑在 IPlatformPushLogService.retry 实现中扩展。

6. 前端页面(yack-web)

菜单初始化见 sql/platform-menu.sql,页面位于 yack-web/src/views/platform。

6.1 平台管理

路径:yack-web/src/views/platform/instance/index.vue

后端接口:/platform/instance

主要能力:

  • 查询平台列表。
  • 新增、编辑、删除平台。
  • 平台详情接口会清空 access_token,避免前端拿到 Token。
  • initStatus 通过查询 platform_user_auth 表计算:存在有效用户则显示已初始化。

用户管理:

点击"用户"按钮打开用户管理弹窗,后端接口:/platform/user-auth

  • 查询用户认证列表:GET /platform/user-auth/list?platformId={id}
  • 新增用户:仅输入认证配置(用户名/密码/clientId/scope),platformUserId 和 platformUserName 为空
  • 初始化用户:POST /platform/user-auth/{id}/initialize
    • 执行初始化流程获取 Token
    • 从 Token 解析或调用接口获取 userId 和用户名
    • 自动回填 platformUserId 和 platformUserName
    • 标记 initStatus=1
  • 编辑用户:显示 platformUserId 和 platformUserName(只读)
  • 删除用户:DELETE /platform/user-auth/{ids}

用户初始化流程配置(flow_type=1)需要在 result_save 中指定:

[
  {"field": "access_token", "saveTo": "platform_user_auth.access_token"},
  {"field": "expires_in", "saveTo": "platform_user_auth.token_expires_at"},
  {"field": "userId", "saveTo": "platform_user_auth.platform_user_id"},
  {"field": "userName", "saveTo": "platform_user_auth.platform_user_name"}
]

注意: saveTo 应使用 platform_user_auth.* 前缀而非旧的 platform_instance.*。

6.2 接口定义

路径:yack-web/src/views/platform/components/InterfaceDrawer.vue、HttpRequestDesigner.vue。

后端接口:/platform/interface

主要能力:

  • 按平台维护接口。
  • 配置 method、path、content-type、请求 schema、响应 schema。
  • 请求 schema 用于流程执行和设备绑定页动态字段映射。
  • source=onboard_config 的取值配置编辑 value,支持填写 JSON 数组或数字/字符串。保存时前端会把 [1.7,3.4] 转成数组,把 6.8 转成数字。

6.3 流程编排

路径:yack-web/src/views/platform/flow/index.vue

后端接口:/platform/flow

主要能力:

  • 按平台查询流程。
  • 新增、编辑流程及步骤。
  • 查看流程详情时返回 {flow, steps}。
  • 步骤入参覆盖同样支持 source=onboard_config,语义与接口管理一致:配置 value,不配置子映射字段名。

注意: 流程手动执行按钮已移除,流程只能通过业务入口触发。

6.4 设备绑定

路径:yack-web/src/views/platform/device-mapping/index.vue

后端接口:/platform/device-mapping

主要能力:

  • 管理平台、用户、项目、设备、传感器类型、设备类型、推送接口、字段映射。
  • 级联选择(新增):
    • 选择平台 → 加载已初始化用户列表:GET /platform/user-auth/initialized/{platformId}
    • 选择用户 → 加载项目列表:GET /platform/user-auth/{id}/projects
    • 选择项目 → 回填 platformProjectId 和 platformProjectName
  • 入网:POST /platform/device-mapping/{id}/onboard。
  • 子设备映射:
    • 查询:GET /platform/device-mapping/{id}/sub-mappings
    • 全量同步:POST /platform/device-mapping/{id}/sub-mappings

网关设备必须配置子映射,monitor 子设备才会执行入网并获得平台 deviceId。

级联选择接口权限: 使用 hasAnyPermi 支持跨页面调用:

  • /platform/user-auth/initialized/{platformId} - 允许 platform:userAuth:list 或 platform:deviceMapping:*
  • /platform/user-auth/{id}/projects - 同上

6.5 推送日志

路径:yack-web/src/views/platform/push-log/index.vue

后端接口:/platform/push-log

主要能力:

  • 查询推送/操作日志。
  • 查看日志详情。
  • 手动重推入口:POST /platform/push-log/{id}/retry。

7. DCP 内置初始化数据

初始化 SQL:sql/platform-dcp-init-data.sql。

使用前需要先创建 DCP 平台实例:

SET @dcp_platform_id = (SELECT id FROM platform_instance WHERE code = 'dcp' LIMIT 1);

7.1 内置接口(12 个)

code 方法 路径 用途
dcp_auth POST /connect/token 获取 Token
dcp_get_user GET /api/app/user/user-id 获取当前用户
dcp_get_projects GET /api/app/project/by-user-id/{userId} 获取项目列表,默认取第一个
dcp_create_point POST /api/app/point 创建测点
dcp_bind_inclino POST /api/app/inclino-sensor 绑定倾角传感器
dcp_bind_stress POST /api/app/stress-sensor 绑定轴力计
dcp_bind_hyeto POST /api/app/hyeto-sensor 绑定雨量计
dcp_bind_static_level POST /api/app/static-level-device 绑定静力水准
dcp_push_inclino POST /api/app/inclino-data/many 推送倾角数据
dcp_push_stress POST /api/app/stress-data/many 推送轴力计数据
dcp_push_hyeto POST /api/app/hyeto-data/many 推送雨量计数据
dcp_push_static_level POST /api/app/static-level-data/many 推送静力水准数据

7.2 内置流程(6 个)

code flow_type sensor_type 步骤
dcp_init 1 - dcp_auth -> dcp_get_user -> dcp_get_projects
dcp_token_refresh 3 - dcp_auth
dcp_onboard_inclino 2 inclino dcp_create_point -> dcp_bind_inclino
dcp_onboard_stress 2 stress dcp_create_point -> dcp_bind_stress
dcp_onboard_hyeto 2 hyeto dcp_create_point -> dcp_bind_hyeto
dcp_onboard_static_level 2 static_level dcp_create_point -> dcp_bind_static_level

7.3 DCP 字段约定

  • 入网接口中的 moduleId、virtualId 使用 source=uuid,每次请求生成新 UUID。
  • 深部位移绑定接口的 sensorIds 使用 source=cascadedSubDeviceSensorIds,sensorDepths 使用 source=cascadedSubDeviceDepths。
  • 静力水准绑定接口的 sensorIds 使用 source=referenceSubDeviceSensorIdPairs。
  • 静力水准推送接口定义了数组字段 sensorData,其 itemSchema 包含 epoch、height、temperature、sensorId。
  • 轴力计和雨量计 DCP 文档要求累计值递增;当前代码未在适配器中实现"查询上次成功值并跳过非递增值"的通用校验,若目标平台严格校验,需要在推送前补充该逻辑或依赖平台返回失败日志。

7.4 深部位移计扩展配置

sql/platform-integration.sql 已在 platform_sensor_type 字典中维护 deep_inclino,前端传感器类型下拉也支持从字典读取和手动输入。sql/platform-dcp-init-data.sql 当前未内置深部位移计绑定接口和入网流程,需要在接口管理和流程编排中配置。

DCP 深部位移绑定接口:

项 值
code 建议 dcp_bind_deep_inclino
method POST
path /api/app/deep-inclino-device
content-type application/json

典型 request_schema:

[
  {"name":"projectId","type":"string","source":"extra","field":"projectId","required":true},
  {"name":"pointId","type":"string","source":"context","contextKey":"step1.id","required":true},
  {"name":"name","type":"string","source":"device","field":"serial_number","required":true},
  {"name":"moduleId","type":"string","source":"uuid","required":true},
  {"name":"virtualId","type":"string","source":"uuid","required":true},
  {"name":"sensorIds","type":"array","source":"cascadedSubDeviceSensorIds","required":true},
  {"name":"sensorDepths","type":"array","source":"cascadedSubDeviceDepths","value":[1.7,3.4,5.1,6.8],"required":true},
  {"name":"holeDepth","type":"number","source":"fixed","value":6.8,"required":true},
  {"name":"axisDirection","type":"string","source":"onboard_config","value":"X","required":false},
  {"name":"holeElevation","type":"number","source":"fixed","value":1900,"required":false},
  {"name":"zipDuring","type":"number","source":"onboard_config","value":0,"required":false},
  {"name":"frequency","type":"number","source":"onboard_config","value":2147483647,"required":false},
  {"name":"remark","type":"string","source":"onboard_config","value":"","required":false}
]

网关多子设备绑定深部位移计时,子映射表中的 monitor 顺序表示从浅到深。sensorDepths.value 也按浅到深配置完整孔深数组,例如 [1.7,3.4,5.1,6.8]。流程执行器会按当前 monitor 截取"当前节点 + 更深节点",再倒序成 DCP 要求的"最深到当前":

  • 第 1 个 monitor(1.7m):sensorDepths=[6.8,5.1,3.4,1.7],sensorIds=[6.8m sensorId,5.1m sensorId,3.4m sensorId,1.7m sensorId]。
  • 第 2 个 monitor(3.4m):sensorDepths=[6.8,5.1,3.4],sensorIds=[6.8m sensorId,5.1m sensorId,3.4m sensorId]。
  • 第 3 个 monitor(5.1m):sensorDepths=[6.8,5.1],sensorIds=[6.8m sensorId,5.1m sensorId]。
  • 第 4 个 monitor(6.8m):sensorDepths=[6.8],sensorIds=[6.8m sensorId]。

sensorIds 从每个子映射的 onboard_config.sensorId 读取;未配置时回退为子设备序列号。holeDepth 是整孔深度,配置为固定数字即可,不再按 monitor 拆分。

深度类字段按工程习惯使用正数,表示距孔口向下的距离;若需要平台侧高程,按 holeElevation - sensorDepth 计算,不通过负深度表达方向。


8. 关键复用点

资产 位置 用途
HttpClientFactory fastbee-plugs/fastbee-http 发起 HTTP 请求
HttpClientConfig fastbee-plugs/fastbee-http 构建请求 URL、method、headers、body/query
Forest HTTP com.dtflys.forest 底层 HTTP 执行
RuleProcess 调用方 IoT 上报处理链路 设备上报后触发 DeviceReportTrigger
Redis StaticLevelBufferService 静力水准基准点/监测点缓存
MyBatis-Plus ServiceImpl 平台 service 实现 常规 CRUD
iot_things_model 物模型表 前端字段映射可选项来源

9. 当前实现限制与注意事项

  • Token 迁移: Token/认证已从 PlatformInstance(平台级)迁移到 PlatformUserAuth(用户级)。旧的 result_save 配置如果使用了 platform_instance.* 前缀,需要更新为 platform_user_auth.*。PlatformInstance.access_token/token_expires_at 字段已废弃。
  • Service 简化: PlatformInstanceServiceImpl 仅保留 selectList;初始化、Token 管理、项目查询等功能已迁移到 PlatformUserAuthServiceImpl。
  • Controller 拆分: PlatformInstanceController 移除了 initialize 和 getProjects 端点,这些功能已迁移到 PlatformUserAuthController。
  • FlowContext 变更: platformUserAuth 现在是必填字段。旧的 FlowContext(PlatformInstance) 构造函数已移除。
  • 流程手动执行移除: POST /platform/flow/{id}/execute 端点已移除,流程只能通过业务入口(初始化/入网/Token 刷新)触发。
  • 入网校验变更: 入网时不再校验 platformProjectId 是否为空,改为校验 platformUserAuth 是否存在。
  • 当前适配器只有 DCP。新增平台需要新增 adapter,并在 DeviceReportTrigger.dispatchPush 中按平台 code 路由。
  • 文档中的"可视化配置"已覆盖平台、接口、流程、设备绑定、子映射和日志,但平台适配器路由仍需要代码扩展。
  • DCP 实时推送必须配置 push_interface_id,未配置时不会按传感器类型自动回退路径。
  • 实时推送只支持 POST JSON,流程执行支持 GET、form、JSON。
  • idempotency_key 字段已建表,但实时推送未生成幂等键。
  • PlatformUserAuthController.getInfo 会清空 access_token。
  • platform_device_mapping.platform_device_id 对网关设备不是主要数据来源;网关 monitor 的平台 deviceId 保存在 platform_device_sub_mapping.platform_device_id。
  • 静力水准 monitor 先到时会缓存 monitor 数据,但当前主推送分支是在 monitor 到达时读取基准点缓存;若要实现"基准点后到自动补推所有等待 monitor",需要在基准点上报分支补充扫描 monitor 缓存并推送。
  • 定时推送依赖 Redis 缓存数据,若设备数据未及时写入 Redis,定时推送可能推送过期数据或无数据可推。
  • 定时推送网关设备时会遍历所有子设备,大量子设备场景需关注性能和外部接口调用频率。
  • 定时推送当前使用 @Scheduled(fixedDelay),不支持 Cron 表达式;如需灵活调度需引入 Quartz。
  • 生产环境建议定时推送间隔不低于 5 分钟,避免频繁调用外部接口。