refactor(A): 产品/物模型/设备三层架构重构 - 地基批次
新增:
- iot-common/PlatformType: 平台维度单一真相源(枚举)
统一 tuya/matter/local/xiaomi/huawei/dueros/alexa/google_home,
提供 legacy 别名归一(miot|mi -> xiaomi)与已知性判定
- backend/sql/V22__product_device_arch_refactor.sql: 幂等迁移(153行)
A. platform_type 平台字典表 + 种子数据(后台可维护)
B. product 加 protocol 列; platform_product_id 空串清洗为 NULL;
清洗 model/manufacturer 被涂鸦 productId 污染的历史脏数据;
加 (platform, platform_product_id, deleted) 唯一约束
C. product_property 加 grp/is_primary/enum_label + (product_id,code) 唯一约束
D. product_event/product_action 加 (product_id,code) 唯一约束;
product_action 加 completion_mode
E. device 加 product_id 索引(OTA 物化目标设备此前全表扫); platform 归一
改造:
- DeviceAdapter 新增 platform() 显式声明平台
- DeviceAdapterManager 改为按 platform() 精确匹配,
移除'类名是否包含平台字符串'的脆弱约定(类名与平台码不同名会静默降级)
- DevicePlatform 委托 PlatformType, 消除第四套平台取值域
验证: mvn -pl iot-common,iot-device -am compile 通过
This commit is contained in:
parent
0ce191c448
commit
a932633c50
@ -0,0 +1,100 @@
|
||||
package com.iot.common.constants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 平台标识(单一真相源)。
|
||||
*
|
||||
* <p>历史问题:platform 的取值域在四张表/四份代码里各写一套,互不兼容:</p>
|
||||
* <ul>
|
||||
* <li>product.platform —— tuya / local / matter</li>
|
||||
* <li>device.platform —— tuya / miot / dueros / local / matter</li>
|
||||
* <li>DevicePlatform 枚举 —— tuya / mi / huawei / matter / local</li>
|
||||
* <li>platform_config —— tuya / aliyun / xfyun</li>
|
||||
* </ul>
|
||||
* <p>本枚举是 product / device / platform_type 三处的统一口径来源;
|
||||
* platform_config.platform_code 表示"第三方集成配置"(涂鸦 OpenAPI / 讯飞 / 阿里云),
|
||||
* 与设备接入平台不是同一维度,不在此枚举内。</p>
|
||||
*
|
||||
* <p>另:zigbee / ble / bluetooth_mesh / wifi 是协议,不是平台,
|
||||
* 存放于 device.protocol 与 product.connect_type,与 platform 正交。</p>
|
||||
*/
|
||||
public enum PlatformType {
|
||||
|
||||
TUYA("tuya", "涂鸦智能"),
|
||||
MATTER("matter", "Matter"),
|
||||
LOCAL("local", "本地协议"),
|
||||
XIAOMI("xiaomi", "小米 IoT"),
|
||||
HUAWEI("huawei", "华为 HiLink"),
|
||||
DUEROS("dueros", "百度 DuerOS"),
|
||||
ALEXA("alexa", "Amazon Alexa"),
|
||||
GOOGLE_HOME("google_home", "Google Home");
|
||||
|
||||
/** 历史别名 → 规范码(只做同义改名映射) */
|
||||
private static final Map<String, String> LEGACY_ALIAS;
|
||||
|
||||
static {
|
||||
Map<String, String> m = new LinkedHashMap<>();
|
||||
m.put("miot", "xiaomi");
|
||||
m.put("mi", "xiaomi");
|
||||
m.put("xiaomi_miot", "xiaomi");
|
||||
m.put("matter_bridge", "matter");
|
||||
m.put("tuya_cloud", "tuya");
|
||||
LEGACY_ALIAS = Map.copyOf(m);
|
||||
}
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
PlatformType(String code, String label) {
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台显示名(注意:Enum.name() 是 final 的,不能用作显示名,故用 label)。
|
||||
*/
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/** 按编码取枚举(不归一 legacy 别名,未知返回 empty) */
|
||||
public static Optional<PlatformType> of(String code) {
|
||||
if (code == null || code.isBlank()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String c = code.trim().toLowerCase();
|
||||
return Arrays.stream(values()).filter(p -> p.code.equals(c)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* 归一化任意平台字符串:去空格小写 → legacy 别名映射。
|
||||
* 未知编码原样返回(不丢数据),空值归为 LOCAL。
|
||||
*/
|
||||
public static String normalize(String raw) {
|
||||
if (raw == null || raw.isBlank()) {
|
||||
return LOCAL.code;
|
||||
}
|
||||
String c = raw.trim().toLowerCase();
|
||||
String mapped = LEGACY_ALIAS.get(c);
|
||||
return mapped != null ? mapped : c;
|
||||
}
|
||||
|
||||
/** 全部平台枚举(供后台下拉与字典表初始化使用) */
|
||||
public static List<PlatformType> all() {
|
||||
return List.of(values());
|
||||
}
|
||||
|
||||
/** 判断是否为已知平台编码 */
|
||||
public static boolean isKnown(String code) {
|
||||
return of(code).isPresent();
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,17 @@ import java.util.List;
|
||||
*/
|
||||
public interface DeviceAdapter {
|
||||
|
||||
/**
|
||||
* 本适配器负责的平台编码(与 {@code device.platform} / {@code product.platform} 同源)。
|
||||
*
|
||||
* <p>适配器必须显式声明平台,适配器管理器按此字段精确匹配,
|
||||
* 不再依赖"类名是否包含平台字符串"这种脆弱约定
|
||||
* (类名与平台码一旦不同名就会静默降级到 Local 适配器)。</p>
|
||||
*
|
||||
* @return 平台编码,如 {@code tuya} / {@code local}
|
||||
*/
|
||||
String platform();
|
||||
|
||||
/**
|
||||
* 设备连接
|
||||
* @param deviceId 设备ID
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.iot.device.adapter;
|
||||
|
||||
import com.iot.common.constants.PlatformType;
|
||||
import com.iot.common.exception.BusinessException;
|
||||
import com.iot.common.result.ResultCode;
|
||||
import com.iot.device.entity.Device;
|
||||
@ -27,23 +28,26 @@ public class DeviceAdapterManager {
|
||||
private final Map<String, DeviceAdapter> adapterCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 根据平台类型获取适配器
|
||||
* 根据平台类型获取适配器。
|
||||
*
|
||||
* <p>匹配规则:适配器显式声明的 {@link DeviceAdapter#platform()} 精确等于平台码;
|
||||
* 平台码先经 {@link PlatformType#normalize(String)} 归一(miot→xiaomi 等 legacy 别名)。
|
||||
* 未命中时降级到 LOCAL 适配器(本地设备适配器是唯一兜底实现)。</p>
|
||||
*/
|
||||
public DeviceAdapter getAdapter(String platform) {
|
||||
return adapterCache.computeIfAbsent(platform, key -> {
|
||||
// 先精确匹配平台对应的适配器
|
||||
String key = PlatformType.normalize(platform);
|
||||
return adapterCache.computeIfAbsent(key, k -> {
|
||||
DeviceAdapter matched = adapters.stream()
|
||||
.filter(adapter -> adapter.getClass().getSimpleName()
|
||||
.toLowerCase().contains(key.toLowerCase()))
|
||||
.filter(adapter -> k.equals(adapter.platform()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (matched != null) {
|
||||
return matched;
|
||||
}
|
||||
// 未找到时降级到 LocalDeviceAdapter 作为默认适配器
|
||||
log.warn("未找到平台[{}]的专用适配器,降级使用 LocalDeviceAdapter", key);
|
||||
log.warn("未找到平台[{}]的专用适配器,降级使用 LocalDeviceAdapter", k);
|
||||
return adapters.stream()
|
||||
.filter(a -> a.getClass().getSimpleName().contains("Local"))
|
||||
.filter(a -> PlatformType.LOCAL.code().equals(a.platform()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException(ResultCode.PLATFORM_NOT_SUPPORTED));
|
||||
});
|
||||
@ -69,14 +73,12 @@ public class DeviceAdapterManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有支持的平台
|
||||
* 获取所有支持的平台(由适配器显式声明)。
|
||||
*/
|
||||
public List<String> getSupportedPlatforms() {
|
||||
return adapters.stream()
|
||||
.map(adapter -> {
|
||||
String name = adapter.getClass().getSimpleName();
|
||||
return name.replace("DeviceAdapter", "").toLowerCase();
|
||||
})
|
||||
.map(DeviceAdapter::platform)
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,11 @@ public class LocalDeviceAdapter implements DeviceAdapter {
|
||||
|
||||
private final Map<String, DeviceConnectRequest.Credentials> deviceCredentials = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public String platform() {
|
||||
return com.iot.common.constants.PlatformType.LOCAL.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(String deviceId, DeviceConnectRequest.Credentials credentials) {
|
||||
log.info("连接本地设备: deviceId={}", deviceId);
|
||||
|
||||
@ -31,6 +31,11 @@ public class TuyaDeviceAdapter implements DeviceAdapter {
|
||||
private final TuyaCloudService tuyaCloudService;
|
||||
private final ProductMapper productMapper;
|
||||
|
||||
@Override
|
||||
public String platform() {
|
||||
return com.iot.common.constants.PlatformType.TUYA.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(String deviceId, DeviceConnectRequest.Credentials credentials) {
|
||||
log.info("涂鸦设备连接(无状态, 走云端): deviceId={}", deviceId);
|
||||
|
||||
@ -1,23 +1,39 @@
|
||||
package com.iot.device.collector;
|
||||
|
||||
import com.iot.common.constants.PlatformType;
|
||||
|
||||
/**
|
||||
* 设备平台标识(平台无关)。
|
||||
* 动态模型不绑定任何平台,平台差异只体现在「采集适配」层。
|
||||
* 采集适配层的平台标识。
|
||||
*
|
||||
* <p>已委托给 {@link PlatformType}(平台维度的单一真相源),
|
||||
* 避免采集层再维护一份与 device / product / platform_type 不一致的取值域。
|
||||
* 历史上本枚举的 MI 编码为 "mi",现已统一为规范码 "xiaomi"
|
||||
* (对应 legacy 别名 miot / mi)。</p>
|
||||
*/
|
||||
public enum DevicePlatform {
|
||||
TUYA("tuya"),
|
||||
MI("mi"),
|
||||
HUAWEI("huawei"),
|
||||
MATTER("matter"),
|
||||
LOCAL("local");
|
||||
TUYA(PlatformType.TUYA),
|
||||
MI(PlatformType.XIAOMI),
|
||||
HUAWEI(PlatformType.HUAWEI),
|
||||
MATTER(PlatformType.MATTER),
|
||||
LOCAL(PlatformType.LOCAL);
|
||||
|
||||
private final String code;
|
||||
private final PlatformType delegate;
|
||||
|
||||
DevicePlatform(String code) {
|
||||
this.code = code;
|
||||
DevicePlatform(PlatformType delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范平台编码(与 device.platform / product.platform 同源)。
|
||||
*/
|
||||
public String code() {
|
||||
return code;
|
||||
return delegate.code();
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台显示名。
|
||||
*/
|
||||
public String label() {
|
||||
return delegate.label();
|
||||
}
|
||||
}
|
||||
|
||||
172
backend/sql/V22__product_device_arch_refactor.sql
Normal file
172
backend/sql/V22__product_device_arch_refactor.sql
Normal file
@ -0,0 +1,172 @@
|
||||
-- =====================================================================
|
||||
-- V22 · 产品 / 物模型 / 设备 三层架构重构(地基)
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 背景(重构前的实测断点):
|
||||
-- 1) platform 取值域四套并存:product(tuya/local/matter) /
|
||||
-- device(tuya/miot/dueros/local/matter) / DevicePlatform 枚举 /
|
||||
-- platform_config(tuya/aliyun/xfyun),miot、dueros 设备无处归类;
|
||||
-- 2) 物模型两套并存:product_property(唯一被消费)与 device.schema_json
|
||||
-- (中控屏上报后只写不读,属死数据);
|
||||
-- 3) product_property 无 (product_id, code) 唯一约束,saveSpec 全量替换
|
||||
-- / saveProperty 增量 / reconcile-spec 物理删三条写路径语义不一;
|
||||
-- 4) product.model / manufacturer 被涂鸦 productId 污染
|
||||
-- (resolveProductId 调用方参数错位),product_key 为随机 UUID 不可读;
|
||||
-- 5) device.product_id 无索引,而 OTA 按 product_id 物化目标设备 -> 全表扫;
|
||||
-- 6) V19 已建 product_property.semantic/channel/completionMode 三列,
|
||||
-- 但 Entity / DTO 全无对应字段 -> 语义字段整链断裂。
|
||||
--
|
||||
-- 本次动作(仅结构与数据清洗,不做业务语义回填;语义回填由后端
|
||||
-- SpecSemanticResolver 单一权威实现,保证规则只有一份):
|
||||
-- A. 新建 platform_type 平台字典表(后台可维护,取代硬编码枚举)
|
||||
-- B. product:加 protocol 列;空串 platform_product_id -> NULL;
|
||||
-- 清洗 model/manufacturer 脏数据;加 (platform, platform_product_id) 唯一约束
|
||||
-- C. product_property:加 grp / is_primary / enum_label;
|
||||
-- 加 (product_id, code) 唯一约束
|
||||
-- D. product_event / product_action:加 (product_id, code) 唯一约束;
|
||||
-- product_action 加 completion_mode
|
||||
-- E. device:加 product_id 索引;platform 归一(legacy 别名 + 空值兜底)
|
||||
--
|
||||
-- 幂等说明:本脚本可重复执行(加列/加索引均由存储过程判存在性)。
|
||||
-- 唯一约束前有重复检测,有重复时 ALTER 会直接报错中止,交人工合并
|
||||
-- (不静默合并,避免设备错挂到错误产品)。
|
||||
-- 执行方式:手工 mysql 客户端执行(本环境 Flyway 未启用,与 V3~V21 一致)。
|
||||
-- =====================================================================
|
||||
|
||||
-- ---------- 0) 幂等辅助过程 ----------
|
||||
DROP PROCEDURE IF EXISTS v22_add_col$$
|
||||
CREATE PROCEDURE v22_add_col(IN tbl VARCHAR(64), IN col VARCHAR(64), IN def TEXT)
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'iot_platform' AND TABLE_NAME = tbl AND COLUMN_NAME = col
|
||||
) THEN
|
||||
SET @s = CONCAT('ALTER TABLE ', tbl, ' ADD COLUMN ', col, ' ', def);
|
||||
PREPARE st FROM @s; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
DROP PROCEDURE IF EXISTS v22_add_idx$$
|
||||
CREATE PROCEDURE v22_add_idx(IN tbl VARCHAR(64), IN idx VARCHAR(64), IN cols VARCHAR(255))
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = 'iot_platform' AND TABLE_NAME = tbl AND INDEX_NAME = idx
|
||||
) THEN
|
||||
SET @s = CONCAT('ALTER TABLE ', tbl, ' ADD INDEX ', idx, ' (', cols, ')');
|
||||
PREPARE st FROM @s; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
DROP PROCEDURE IF EXISTS v22_add_uniq$$
|
||||
CREATE PROCEDURE v22_add_uniq(IN tbl VARCHAR(64), IN idx VARCHAR(64), IN cols VARCHAR(255))
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = 'iot_platform' AND TABLE_NAME = tbl AND INDEX_NAME = idx
|
||||
) THEN
|
||||
SET @s = CONCAT('ALTER TABLE ', tbl, ' ADD UNIQUE INDEX ', idx, ' (', cols, ')');
|
||||
PREPARE st FROM @s; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
END IF;
|
||||
END$$
|
||||
-- ---------- 1) 平台字典表 ----------
|
||||
CREATE TABLE IF NOT EXISTS platform_type (
|
||||
id BIGINT NOT NULL COMMENT '主键',
|
||||
code VARCHAR(32) NOT NULL COMMENT '平台编码(tuya/matter/local/xiaomi/huawei...)',
|
||||
name VARCHAR(64) NOT NULL COMMENT '平台名称',
|
||||
legacy_codes VARCHAR(255) DEFAULT NULL COMMENT '历史别名(逗号分隔)',
|
||||
protocol VARCHAR(32) DEFAULT NULL COMMENT '默认协议(wifi/ble/zigbee/matter)',
|
||||
enabled TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||||
sort INT NOT NULL DEFAULT 0 COMMENT '排序',
|
||||
remark VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
tenant_id BIGINT DEFAULT NULL,
|
||||
create_time DATETIME DEFAULT NULL,
|
||||
update_time DATETIME DEFAULT NULL,
|
||||
deleted INT DEFAULT 0,
|
||||
create_by BIGINT DEFAULT NULL,
|
||||
update_by BIGINT DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_platform_code (code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台字典表';
|
||||
|
||||
INSERT INTO platform_type
|
||||
(id, code, name, legacy_codes, protocol, enabled, sort, remark, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(2260000000000000001, 'tuya', '涂鸦智能', 'tuya_cloud', 'wifi', 1, 10, '涂鸦 OpenAPI 接入', NOW(), NOW(), 0),
|
||||
(2260000000000000002, 'matter', 'Matter', 'matter_bridge', 'matter', 1, 20, 'Matter 协议设备', NOW(), NOW(), 0),
|
||||
(2260000000000000003, 'local', '本地协议', 'local', 'zigbee', 1, 30, '中控屏/网关本地执行', NOW(), NOW(), 0),
|
||||
(2260000000000000004, 'xiaomi', '小米 IoT', 'miot,mi', 'wifi', 1, 40, '米家设备', NOW(), NOW(), 0),
|
||||
(2260000000000000005, 'huawei', '华为 HiLink', 'huawei,harmony', 'wifi', 1, 50, '鸿蒙智联设备', NOW(), NOW(), 0),
|
||||
(2260000000000000006, 'dueros', '百度 DuerOS', 'dueros', 'wifi', 0, 60, '待接入', NOW(), NOW(), 0),
|
||||
(2260000000000000007, 'alexa', 'Amazon Alexa', 'alexa', 'wifi', 0, 70, '待接入', NOW(), NOW(), 0),
|
||||
(2260000000000000008, 'google_home', 'Google Home', 'google_home', 'wifi', 0, 80, '待接入', NOW(), NOW(), 0)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
name = VALUES(name),
|
||||
legacy_codes = VALUES(legacy_codes),
|
||||
protocol = VALUES(protocol),
|
||||
sort = VALUES(sort),
|
||||
remark = VALUES(remark),
|
||||
update_time = NOW();
|
||||
|
||||
-- ---------- 2) product ----------
|
||||
-- 协议维度(与 platform 正交):wifi / ble / zigbee / matter / mesh
|
||||
CALL v22_add_col('product', 'protocol',
|
||||
'VARCHAR(32) DEFAULT NULL COMMENT ''联网协议(wifi/ble/zigbee/matter/mesh)''');
|
||||
|
||||
-- 空串会与唯一索引互撞(多个 '' 视为同值),统一清洗为 NULL
|
||||
UPDATE product SET platform_product_id = NULL WHERE platform_product_id = '';
|
||||
|
||||
-- 清洗历史脏数据:model / manufacturer 曾被误写入涂鸦 productId
|
||||
UPDATE product
|
||||
SET model = NULL, manufacturer = NULL
|
||||
WHERE manufacturer = 'tuya'
|
||||
AND model IS NOT NULL
|
||||
AND model = platform_product_id;
|
||||
|
||||
-- 守卫:确认无重复后再加唯一约束(有重复时下面 ALTER 会直接报错中止)
|
||||
CALL v22_add_uniq('product', 'uk_platform_product', 'platform, platform_product_id, deleted');
|
||||
-- ---------- 3) product_property(物模型属性) ----------
|
||||
-- grp:功能分组(switch / scene / mode / countdown / display ...),
|
||||
-- 支撑"多开关位一起控制"与详情页分组渲染
|
||||
CALL v22_add_col('product_property', 'grp',
|
||||
'VARCHAR(32) DEFAULT NULL COMMENT ''功能分组(switch/scene/mode/countdown/display)''');
|
||||
|
||||
-- is_primary:主开关标记,取代按名称猜测(开关/电源/switch/power)
|
||||
CALL v22_add_col('product_property', 'is_primary',
|
||||
'TINYINT NOT NULL DEFAULT 0 COMMENT ''是否主开关(1是,0否)''');
|
||||
|
||||
-- enum_label:枚举中文名 JSON(如 {"warm":"暖风"}),供 APP 与后台显示
|
||||
CALL v22_add_col('product_property', 'enum_label',
|
||||
'TEXT DEFAULT NULL COMMENT ''枚举中文名(JSON对象)''');
|
||||
|
||||
CALL v22_add_uniq('product_property', 'uk_product_code', 'product_id, code, deleted');
|
||||
|
||||
-- ---------- 4) product_event ----------
|
||||
CALL v22_add_uniq('product_event', 'uk_product_code', 'product_id, code, deleted');
|
||||
|
||||
-- ---------- 5) product_action ----------
|
||||
-- completion_mode 属 action 维度(sync 同步完成 / ack_only 仅受理)
|
||||
CALL v22_add_col('product_action', 'completion_mode',
|
||||
'VARCHAR(16) DEFAULT ''SYNC'' COMMENT ''完成模式(sync/ack_only)''');
|
||||
|
||||
CALL v22_add_uniq('product_action', 'uk_product_code', 'product_id, code, deleted');
|
||||
|
||||
-- ---------- 6) device ----------
|
||||
-- product_id 索引:OTA 按 product_id 物化目标设备,此前为全表扫
|
||||
CALL v22_add_idx('device', 'idx_product_id', 'product_id');
|
||||
|
||||
-- platform 归一:legacy 别名 -> 规范码
|
||||
UPDATE device SET platform = 'xiaomi' WHERE platform = 'miot';
|
||||
|
||||
-- platform 空值兜底为 local(无平台信息的中控屏本地设备)
|
||||
UPDATE device SET platform = 'local' WHERE platform IS NULL OR platform = '';
|
||||
|
||||
-- 被误标为 local、但持有云平台产品ID 的设备 -> 归回涂鸦
|
||||
UPDATE device SET platform = 'tuya'
|
||||
WHERE platform = 'local'
|
||||
AND platform_product_id IS NOT NULL
|
||||
AND platform_product_id <> '';
|
||||
|
||||
-- ---------- 7) 清理辅助过程 ----------
|
||||
DROP PROCEDURE IF EXISTS v22_add_col$$
|
||||
DROP PROCEDURE IF EXISTS v22_add_idx$$
|
||||
DROP PROCEDURE IF EXISTS v22_add_uniq$$
|
||||
Loading…
x
Reference in New Issue
Block a user