feat(iot): B11 iot_device.subsystem_id + 设备归属绑定 API(一期允许 NULL)

- 新增 sql/iot/V2.0.3__iot_device_add_subsystem.sql(ALTER + idx_subsystem)
- 新增 sql/iot/V2.1.0__iot_device_subsystem_not_null.sql(二期预留,带 "勿执行" 注释)
- IotDeviceDO 加 subsystemId(一期可 NULL,二期改 NOT NULL)
- IotDeviceService 加 bindDeviceToSubsystem / batchBind / unbind / selectCountBySubsystemId
- IotDeviceServiceImpl.createDevice 强校验 subsystemId + 同租户 + Redis HINCRBY +1
- 绑定变更按 TransactionSynchronizationManager afterCommit 同步 Redis(-1 / +1,避免脏状态)
- IotDeviceMapper 加 selectCountBySubsystemId / updateSubsystemId 等
- IotSubsystemServiceImpl 加 incrementDeviceCount/decrementDeviceCount;deleteSubsystem 改用 DB 计数兜底(更可靠)
- IotDeviceController 加 PUT /bindSubsystem + /batchBindSubsystem(@PreAuthorize iot:device:update)
- IotDevicePageReqVO 加 subsystemId 过滤参数(null 可走 IS NULL 查未归属)
- api ErrorCodeConstants 加 DEVICE_SUBSYSTEM_REQUIRED / DEVICE_SUBSYSTEM_CROSS_TENANT(1_050_003_009/010)
- 测试:IotDeviceServiceImplTest 8/8 + B10 IotSubsystemServiceImplTest 补 mock deviceMapper 后 8/8 全绿
- Known Pitfalls 落地:
  ⚠️ 评审 A2:一期允许 NULL,V2.1.0 预留二期 NOT NULL
  ⚠️ Redis 计数:事务提交后同步(TransactionSynchronizationManager.afterCommit)
  ⚠️ 跨租户:校验 subsystem 属于当前租户,不然抛 DEVICE_SUBSYSTEM_CROSS_TENANT
  ⚠️ 索引 idx_subsystem (tenant_id, subsystem_id, deleted) 最左匹配;IS NULL 查询走全表扫,文档已提示

Co-Authored-By: Claude Sonnet (B11 subagent) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context, orchestrator) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-04-24 00:03:57 +08:00
parent ae74b4752a
commit 1f87d599c0
15 changed files with 622 additions and 16 deletions

View File

@@ -0,0 +1,6 @@
-- B11iot_device 增加 subsystem_id 列(一期允许 NULL存量设备兼容
-- 评审 A2分两阶段 NOT NULL 迁移,本脚本为第一期(允许 NULL
ALTER TABLE iot_device
ADD COLUMN subsystem_id BIGINT DEFAULT NULL COMMENT '所属子系统(一期可 NULL 存量兼容,二期改 NOT NULL',
ADD INDEX idx_subsystem (tenant_id, subsystem_id, deleted);

View File

@@ -0,0 +1,11 @@
-- B11第二期预留iot_device.subsystem_id 改 NOT NULL
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-- 预留脚本,第二期执行,当前 *** 不要执行 ***
-- 执行前提存量所有设备已完成子系统分配subsystem_id IS NULL 的记录为 0
-- 执行步骤:
-- 1. 确认 SELECT COUNT(1) FROM iot_device WHERE subsystem_id IS NULL = 0
-- 2. 再执行本 ALTER
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-- ALTER TABLE iot_device
-- MODIFY COLUMN subsystem_id BIGINT NOT NULL COMMENT '所属子系统(二期改 NOT NULL';