fix(J): 清理孤儿涂鸦设备与测试联动数据, 解绑时联动失效设备与AI档案
数据层(V24 迁移): 逻辑删除无涂鸦绑定记录的孤儿涂鸦设备及其AI档案; 物理删除测试阶段的联动规则、场景与场景执行记录(条件相同但动作矛盾的测试数据); 保留产品/物模型/家庭/成员/活动日志。幂等可重复执行。 代码层(根治): TuyaCloudService.unbindUser 解绑时联动逻辑删除该用户的涂鸦设备与AI档案, 避免解绑后设备仍在列表被联动规则反复命中而刷错误日志; 采用逻辑删除, 用户重新绑定同步时 restoreById 自动恢复。
This commit is contained in:
parent
4d8d38909d
commit
91d50eda91
@ -5,6 +5,7 @@ import com.iot.device.entity.AiDeviceProfile;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
/**
|
||||
* AI 设备控制档案 Mapper(iot-device 侧写入)
|
||||
@ -27,4 +28,20 @@ public interface AiDeviceProfileMapper extends BaseMapper<AiDeviceProfile> {
|
||||
+ "AND deleted = 0 LIMIT 1")
|
||||
AiDeviceProfile selectByProductAndOwner(@Param("productId") Long productId,
|
||||
@Param("ownerId") Long ownerId);
|
||||
|
||||
/**
|
||||
* 逻辑删除指定用户的 AI 档案(解绑涂鸦账号时联动失效)
|
||||
* <p>
|
||||
* 档案里的指令码(power_code 等)是按涂鸦 DP 识别的,设备失效后这些档案不再可用;
|
||||
* 保留会指向已失效设备,重新绑定后应由 AI 档案检测流程重新生成。
|
||||
* <p>
|
||||
* 按 owner_id 失效而非 JOIN device 比较 device_id:ai_device_profile.device_id 与
|
||||
* device.device_id 的排序规则不同(utf8mb4_unicode_ci vs utf8mb4_0900_ai_ci),
|
||||
* 跨表字符串比较会报 ERROR 1267 Illegal mix of collations。
|
||||
*
|
||||
* @return 被失效的档案数量
|
||||
*/
|
||||
@Update("UPDATE ai_device_profile SET deleted = 1, update_time = NOW() "
|
||||
+ "WHERE deleted = 0 AND owner_id = #{ownerId}")
|
||||
int invalidateProfilesByOwner(@Param("ownerId") Long ownerId);
|
||||
}
|
||||
|
||||
@ -44,4 +44,21 @@ public interface DeviceMapper extends BaseMapper<Device> {
|
||||
*/
|
||||
@Update("UPDATE device SET deleted = 0 WHERE id = #{id}")
|
||||
int restoreById(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 逻辑删除指定用户的全部涂鸦设备(解绑涂鸦账号时联动失效)
|
||||
* <p>
|
||||
* 背景:解绑涂鸦账号后,绑定记录被物理删除,但同步来的设备仍留在设备列表,
|
||||
* 形成"孤儿涂鸦设备"——设备无法再通过涂鸦云端控制,却仍会被联动规则命中,
|
||||
* 每条规则触发都会产生一次失败指令与错误日志。
|
||||
* <p>
|
||||
* 这里做逻辑删除(deleted=1)而非物理删除:device 表 uk_device_id 唯一索引不含
|
||||
* deleted,物理删除还会让 device_state / device_activity_log 等历史记录悬空;
|
||||
* 用户重新绑定并同步时,restoreById 会把记录自动恢复。
|
||||
*
|
||||
* @return 被失效的设备数量
|
||||
*/
|
||||
@Update("UPDATE device SET deleted = 1, update_time = NOW() "
|
||||
+ "WHERE deleted = 0 AND platform = 'tuya' AND owner_id = #{ownerId}")
|
||||
int invalidateTuyaDevicesByOwner(@Param("ownerId") Long ownerId);
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import com.iot.device.dto.tuya.*;
|
||||
import com.iot.device.entity.Device;
|
||||
import com.iot.device.entity.PlatformConfig;
|
||||
import com.iot.device.entity.TuyaUserBinding;
|
||||
import com.iot.device.mapper.AiDeviceProfileMapper;
|
||||
import com.iot.device.mapper.DeviceMapper;
|
||||
import com.iot.device.mapper.PlatformConfigMapper;
|
||||
import com.iot.device.mapper.TuyaUserBindingMapper;
|
||||
@ -45,6 +46,7 @@ public class TuyaCloudService {
|
||||
private final PlatformConfigMapper platformConfigMapper;
|
||||
private final TuyaUserBindingMapper tuyaUserBindingMapper;
|
||||
private final DeviceMapper deviceMapper;
|
||||
private final AiDeviceProfileMapper aiDeviceProfileMapper;
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@ -435,11 +437,20 @@ public class TuyaCloudService {
|
||||
|
||||
/**
|
||||
* 解绑涂鸦账号(物理删除,避免唯一索引冲突)
|
||||
* <p>
|
||||
* 同时联动失效该用户同步来的涂鸦设备与 AI 档案:
|
||||
* 解绑后这些设备已无法通过涂鸦云端控制,若继续留在设备列表并被联动规则命中,
|
||||
* 每次规则触发都会产生一条失败指令与错误日志(历史问题:孤儿涂鸦设备)。
|
||||
* 设备采用逻辑删除,重新绑定并同步时会被自动恢复。
|
||||
*/
|
||||
public void unbindUser(Long memberId) {
|
||||
// 先删除该用户所有绑定记录(防止有重复行导致唯一索引冲突)
|
||||
tuyaUserBindingMapper.deleteByMemberId(memberId);
|
||||
log.info("会员 {} 解绑了涂鸦账号(物理删除)", memberId);
|
||||
// 联动失效:涂鸦设备 + 指向它们的 AI 档案
|
||||
int invalidatedDevices = deviceMapper.invalidateTuyaDevicesByOwner(memberId);
|
||||
int invalidatedProfiles = aiDeviceProfileMapper.invalidateProfilesByOwner(memberId);
|
||||
log.info("会员 {} 解绑了涂鸦账号(物理删除),联动失效涂鸦设备 {} 个、AI 档案 {} 条",
|
||||
memberId, invalidatedDevices, invalidatedProfiles);
|
||||
}
|
||||
|
||||
// ==================== SMS验证码绑定(正式模式 - 调用涂鸦API) ====================
|
||||
|
||||
51
backend/sql/V24__cleanup_orphan_tuya_devices.sql
Normal file
51
backend/sql/V24__cleanup_orphan_tuya_devices.sql
Normal file
@ -0,0 +1,51 @@
|
||||
-- =====================================================================
|
||||
-- V24 · 清理测试阶段遗留的孤儿涂鸦设备与测试联动数据
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 背景:
|
||||
-- 1) unbindUser 早期只物理删除 tuya_user_binding 绑定记录,不处理已同步的设备,
|
||||
-- 于是产生"孤儿涂鸦设备":设备仍在设备列表,却已无涂鸦账号可控制。
|
||||
-- 定时场景每分钟命中的联动规则会持续对它们下发指令,全部失败并刷错误日志。
|
||||
-- (代码侧已修复:解绑时联动逻辑删除设备与 AI 档案,重新绑定同步时自动恢复)
|
||||
-- 2) 测试阶段遗留的联动规则与场景:条件全部相同(switch_1 == true)但动作互相矛盾
|
||||
-- (switch_1=true / switch_1=0 / {"1":"open"} 混用 dpId),会下发自相矛盾的指令。
|
||||
--
|
||||
-- 清理范围:
|
||||
-- - 逻辑删除孤儿涂鸦设备(owner 已无涂鸦绑定记录)
|
||||
-- - 逻辑删除指向这些设备的 AI 档案
|
||||
-- - 删除测试联动规则与测试场景(含每分钟 cron 的 bb 场景)
|
||||
-- 保留:产品与物模型(product / product_property / product_event / product_action),
|
||||
-- 家庭与成员、设备活动日志(历史审计)等。
|
||||
--
|
||||
-- 无存储过程、无 DELIMITER;幂等可重复执行。
|
||||
-- =====================================================================
|
||||
|
||||
-- ---------- 1) 逻辑删除孤儿涂鸦设备 ----------
|
||||
UPDATE device d
|
||||
SET d.deleted = 1,
|
||||
d.update_time = NOW()
|
||||
WHERE d.deleted = 0
|
||||
AND d.platform = 'tuya'
|
||||
AND d.owner_id > 0
|
||||
AND NOT EXISTS (SELECT 1 FROM tuya_user_binding b WHERE b.member_id = d.owner_id);
|
||||
|
||||
-- ---------- 2) 逻辑删除已解绑用户的 AI 档案 ----------
|
||||
-- 注意:这里按 owner_id 判定,而不是 JOIN device 比较 device_id——
|
||||
-- ai_device_profile.device_id 与 device.device_id 的排序规则不同
|
||||
-- (utf8mb4_unicode_ci vs utf8mb4_0900_ai_ci),跨表字符串比较会报
|
||||
-- ERROR 1267 Illegal mix of collations。owner_id 为数值列,无此问题。
|
||||
UPDATE ai_device_profile a
|
||||
SET a.deleted = 1,
|
||||
a.update_time = NOW()
|
||||
WHERE a.deleted = 0
|
||||
AND a.owner_id > 0
|
||||
AND NOT EXISTS (SELECT 1 FROM tuya_user_binding b WHERE b.member_id = a.owner_id);
|
||||
|
||||
-- ---------- 3) 删除测试联动规则 ----------
|
||||
DELETE FROM linkage_rule WHERE deleted = 0;
|
||||
|
||||
-- ---------- 4) 删除测试场景(bb 场景 cron 为每分钟执行) ----------
|
||||
DELETE FROM scene WHERE deleted = 0;
|
||||
|
||||
-- ---------- 5) 清理场景执行记录 ----------
|
||||
DELETE FROM scene_run;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user