这次改造最初只是为了验证一种新设备能否接入现有平台。
设备可以连接通信服务,后台也能显示在线,但通过开放接口查询、下发和控制时,偶尔会出现“设备不存在”“签名失败”或者设备归属不匹配。
沿着开放接口、数据库、缓存、消息服务和硬件适配服务逐层排查后,我发现真正需要解决的不是某个品牌的协议兼容,而是开放 API 原有的权限模型。
旧模型的问题
旧逻辑把请求中的调用方 ID 直接作为设备归属 ID。
在设备数量少、组织层级简单时,这种做法能够工作。但系统实际存在多层组织关系:
- 上级组织持有开放 API 身份和密钥;
- 上级组织下面包含多个真实项目或小区;
- 设备实际归属于具体项目;
- 同一项目中可能同时存在内部设备和第三方接入设备;
- 不同调用方之间必须保持设备控制隔离。
如果继续让调用身份和设备归属使用同一个值,就只能给每个项目配置独立密钥,或者把设备错误地挂在上级组织节点。
前者会制造大量调用主体,后者会破坏原有的项目、住户、门禁和数据权限关系。
把三种身份拆开
最终方案将设备相关身份拆成三层。
调用身份
上级组织的 AppID 和 Secret 只负责:
- 请求身份认证;
- 签名验证;
- 回调地址配置;
- 确定允许访问的组织范围。
下级项目不需要配置独立密钥,也不会成为新的开放 API 调用方。
实际归属
设备仍然归属于真实项目或小区。
所有内部业务继续依赖真实归属,包括:
- 后台数据权限;
- 门禁关系;
- 用户授权;
- 设备日志;
- 访客流程;
- 业务统计。
开放 API 的改造不能破坏这些原有关系。
开放来源
设备增加独立的“开放来源”标识,用于记录它由哪个调用方创建和管理。
开放接口控制设备时,需要同时满足:
- 设备的开放来源与请求调用方一致;
- 设备当前项目仍位于调用方的有效下级范围;
- 设备序列号和品牌同时匹配;
- 设备已经审核通过;
- 设备没有过期或删除;
- 当前调用方的开放范围仍处于启用状态。
这样,即使多个调用方的设备处于同一个项目,它们之间也不能互相查询或控制。
内部设备没有开放来源,不会被第三方接口意外接管。
新增设备的两条路径
改造后,平台存在两种不同的新增方式。
通过开放 API 新增
通过开放 API 新增时,服务端记录调用方身份,并将设备置为待审核状态。
后台操作人员需要:
- 将设备分配到调用方下面的真实项目;
- 核对设备序列号、类型和品牌;
- 执行审核;
- 门禁设备继续完成门禁业务绑定。
审核通过后,系统自动生成初始有效期。
在设备仍停留于上级组织占位节点、被分配到范围外项目或者项目已经停用时,审核必须失败。
通过后台手工新增
后台手工新增的设备默认属于内部设备。
它可以正常参与平台内部管理,但不会自动绑定某个开放 API 调用方,因此不能直接通过第三方 AppID 查询和控制。
这条边界很重要。否则后台新增一台设备后,某个第三方调用方可能因为组织范围重合而自动取得控制权。
数据迁移
为了兼容历史设备,正式数据库进行了有回滚能力的迁移:
- 增加开放范围配置;
- 增加设备来源字段;
- 增加面向调用方、设备序列号、品牌和状态的联合索引;
- 建立设备归属和业务关系快照;
- 将历史占位设备迁入真实下级项目;
- 为已经明确来源的设备补充开放来源;
- 保持内部设备来源为空。
迁移完成后,继续检查:
- 设备归属是否位于合法范围;
- 门禁关系与设备归属是否一致;
- 是否存在跨调用方设备;
- 是否出现丢失、重复或无效设备;
- 迁移前后的数据数量是否一致。
消息服务器、设备主题、访问控制规则、硬件协议和固件均未修改。
生产环境里的两个问题
数据库字符序不一致
第一版在生产环境查询组织祖先路径时,遇到了不同字符序无法比较的问题。
测试环境没有暴露这个差异,正式签名查询直接返回失败。
最终在数据访问层显式统一比较双方的字符序,并补充契约测试:先让测试稳定复现失败,再验证修复结果。
这次再次说明,依赖字符串保存组织路径时,不能假设数据库、字段和连接的默认字符序永远一致。
服务停止不代表进程已经退出
正式切换时,服务管理工具已经执行停止操作,但实际 Java 进程仍然存在。
如果这时直接覆盖并重新启动,就可能出现:
- 端口仍被占用;
- 两个版本同时运行;
- 设备服务连接到旧实例;
- 数据迁移与应用版本不一致。
最终处理方式是同时核对:
- 服务状态;
- Java 进程;
- 进程命令行;
- 工作目录;
- HTTP 和 RPC 监听端口。
只有确认目标进程身份后,才完成旧进程退出、制品替换和新服务启动。
权限改造后的验证
发布前执行了定向单元测试、多模块构建和数据库契约检查。
正式环境继续验证了:
- 正确调用方可以查询自己的设备;
- 其他调用方查询同一设备会被拒绝;
- 待审核、过期、删除和范围外设备无法控制;
- 公网 HTTPS 接口能够正常访问;
- 用户、照片和卡号下发链路能够执行;
- 清空用户和远程控制请求能够送达设备服务;
- 相同签名不能在有效窗口内重复使用;
- 主应用、数据库、缓存和设备服务连接恢复正常;
- 消息服务器和在线设备连接未因主后端发布而重启。
API 成功不等于现场完成
设备接口返回成功,只能说明请求通过了平台和设备服务的处理,不能直接证明物理世界中的动作已经完成。
以下操作仍需要现场确认:
- 远程开门后,真实门体是否动作;
- 删除照片或卡号后,旧凭证是否确实失效;
- 人脸、卡号和二维码能否按预期通行;
- 设备主动上报是否生成内部记录;
- 第三方回调是否被对方系统正确接收。
第三方回调还存在一个常见细节:HTTP 状态码为 200 不一定代表业务成功。
如果平台约定返回 JSON 成功结构,而接收方只返回纯文本,平台仍可能将本次回调记录为失败。
发布后的“设备消失”
上线后还遇到一个看似与开放 API 有关的问题。
超级管理员能够看到设备,普通合作账号却看到空列表。
设备数据实际上没有丢失。原因是设备已经迁入真实下级项目,而合作账号的数据范围仍然是“仅本部门”。
此时出现了两套不同的权限结果:
- 开放 API 已经可以动态覆盖全部有效下级项目;
- 普通后台账号的数据范围仍然只有当前组织节点;
- 后台设备列表按登录账号的数据范围过滤,因此返回空数据。
处理方式不是迁回设备,也不是修改消息服务,而是将对应角色的数据范围调整为“本部门及以下”,再让账号重新登录刷新缓存。
这说明系统中至少存在三套相互独立的权限:
- 开放 API 调用权限;
- 后台菜单和按钮权限;
- 后台业务数据范围。
只完成其中一套权限改造,不能保证其他入口自动拥有相同行为。
最后的收获
这次真正完成的不是“多兼容一种设备”,而是把调用身份、设备实际归属和开放来源从一个概念中拆开。
最值得保留的结论是:
- 调用身份不应该同时承担设备物理归属;
- 上级组织可以授权下级范围,但设备必须保留真实项目;
- 开放来源必须单独记录,不能只依赖组织树判断;
- 内部设备和开放设备必须保持明确边界;
- 接口权限和后台数据权限需要分别验证;
- API 返回成功不能代替物理设备验收;
- 数据迁移必须同时准备快照、校验和回滚;
- 超级管理员可见而普通账号不可见时,应优先排查数据范围;
- 生产切换必须确认真实进程和端口,而不能只看服务管理状态。
当这些边界稳定以后,后续接入新设备时就不需要再设计一套特殊权限逻辑。
只要设备品牌配置、处理器和硬件服务同时可用,就可以复用同一套开放设备协议。
