feat(iot): B10 iot_subsystem 表 + CRUD + Redis 设备计数聚合

- 新增 sql/iot/V2.0.2__iot_subsystem.sql(iot_project + iot_subsystem)
- 新增 server 模块 subsystem/ 下 DO + Mapper + Service + Controller + VO(7 端点)
- 新增 IotSubsystemDeviceCountRedisDAO(HINCRBY + rebuild + ApplicationReadyEvent 触发)
- api 模块 ErrorCodeConstants 新增子系统段(1-050-020-xxx)
- server 模块 RedisKeyConstants 新增 SUBSYSTEM_DEVCOUNT
- 测试:8 个单元用例全绿(mvn test IotSubsystemServiceImplTest)
- Known Pitfalls 落地:
  ⚠️ 评审 A4:UK(name, tenant_id, project_id, deleted) + 应用层 existsByNameAndProject 兜底 NULL
  ⚠️ 评审 A6:device-stats 走 Redis Hash,避免 GROUP BY
  ⚠️ 评审 A7:simple-list 权限码 iot:device:query,返回字段仅 id/name/code
  ⚠️ 删除校验:Redis 计数 > 0 抛 SUBSYSTEM_HAS_DEVICES
  ⚠️ Redis 重建:ApplicationReadyEvent + try/catch + log.warn 不阻塞启动

说明:iot_device 当前无 subsystem_id 列(rebuild 逻辑标 TODO B11),
待 B11 加列后启用 DB 重建查询。

Co-Authored-By: Claude Sonnet (B10 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-23 21:08:00 +08:00
parent 4614737d51
commit 6649e1abb6
18 changed files with 1295 additions and 0 deletions

View File

@@ -79,4 +79,10 @@ public interface ErrorCodeConstants {
// ========== IoT 告警记录 1-050-014-000 ==========
ErrorCode ALERT_RECORD_NOT_EXISTS = new ErrorCode(1_050_014_000, "IoT 告警记录不存在");
// ========== IoT 子系统 1-050-020-000 ==========
ErrorCode SUBSYSTEM_NOT_EXISTS = new ErrorCode(1_050_020_000, "子系统不存在");
ErrorCode SUBSYSTEM_NAME_DUPLICATE = new ErrorCode(1_050_020_001, "同项目下子系统名称已存在");
ErrorCode SUBSYSTEM_CODE_DUPLICATE = new ErrorCode(1_050_020_002, "同项目下子系统编码已存在");
ErrorCode SUBSYSTEM_HAS_DEVICES = new ErrorCode(1_050_020_003, "子系统下存在设备,不允许删除");
}