-- ========================================================================= -- 存量升级:D17 一锁多网关(v1 库) -- -- 背景:v1 的 lock_device 只有单个 gateway_id/group_id,同一把锁被两个网关 -- 登记时后写入覆盖前值。D17 新增 lock_device_gateway 作为归属事实源: -- 一个 (device_id, gateway_id) 一行,分别记录该网关视角下的信号、在线、 -- locklist 下发状态;lock_device.gateway_id/group_id 退化为「当前主控网关」 -- 缓存,下发前由 RouteResolver 按 R1-R4 实时选路。 -- -- 执行前置: -- 1) 停止应用(至少停止门禁业务的下发与上行消费); -- 2) 备份 lock_device / lock_gateway / sys_dict_type / sys_dict_data; -- 3) 先执行本脚本,再发布包含 D17 代码的版本。旧代码不认识新表,可继续 -- 运行;新代码在归属表为空时会拒绝下发,因此不要跳过回填。 -- -- 可重复执行:建表用 if not exists,回填用 insert ... on duplicate key update。 -- ========================================================================= set names utf8mb4; -- ========================================================================= -- 1、门锁-网关归属表 -- ========================================================================= create table if not exists lock_device_gateway ( rel_id bigint(20) not null auto_increment comment '归属关系ID', device_id bigint(20) not null comment '门锁ID(lock_device.device_id)', gateway_id bigint(20) not null comment '网关ID(lock_gateway.gateway_id)', signal_strength varchar(8) default null comment '该网关视角下的锁信号强度(dBm,如 -42,用于选路比较)', last_signal_time datetime comment '该网关最后一次上报本锁信号/状态的时间', online_status char(1) default '2' comment '该网关视角下的锁在线状态(0在线 1离线 2未知,字典 lock_online_status)', bind_status char(1) default '0' comment 'locklist 下发状态(0待下发 1已下发 2下发失败,字典 lock_bind_status)', bind_msg varchar(512) default null comment 'locklist 下发结果说明', bind_time datetime comment '首次登记到该网关的时间', last_sync_time datetime comment '最后一次 locklist 同步时间', status char(1) default '0' comment '状态(0正常 1停用)', del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (rel_id), unique key uk_lock_device_gateway (device_id, gateway_id), key idx_lock_dg_gateway (gateway_id), key idx_lock_dg_device (device_id), key idx_lock_dg_route (gateway_id, online_status, last_signal_time), key idx_lock_dg_bind (bind_status) ) engine=innodb comment = '门锁-网关归属表'; -- ========================================================================= -- 2、回填:存量「一锁一网关」关系转成归属记录 -- - 只回填网关仍存在的记录; -- - bind_status 记为 1(已下发):这些锁在旧模型下已经实际绑定并在用, -- 若不确定,可在回填后统一改为 0(待下发)并由后台重新下发 locklist; -- - 信号/在线取 lock_device 上的聚合值,作为该网关视角的初始值。 -- ========================================================================= insert into lock_device_gateway ( device_id, gateway_id, signal_strength, last_signal_time, online_status, bind_status, bind_time, last_sync_time, status, del_flag, create_by, create_time, remark ) select d.device_id, d.gateway_id, d.signal_strength, d.last_status_time, coalesce(d.online_status, '2'), '1', d.bind_time, d.bind_time, '0', '0', 'migrate', now(), 'D17 存量回填' from lock_device d join lock_gateway g on g.gateway_id = d.gateway_id where d.gateway_id is not null and d.del_flag = '0' and g.del_flag = '0' on duplicate key update status = '0', del_flag = '0', update_by = 'migrate', update_time = now(); -- ========================================================================= -- 3、校正主控网关缓存:group_id 以网关实际分组为准 -- 回填后把 lock_device.group_id 与网关分组对齐,避免旧数据中的冗余列失真。 -- ========================================================================= update lock_device d join lock_gateway g on g.gateway_id = d.gateway_id set d.group_id = g.group_id, d.update_time = now() where d.gateway_id is not null and (d.group_id is null or d.group_id <> g.group_id); -- ========================================================================= -- 4、字典:lock_bind_status(locklist 下发状态) -- ========================================================================= delete from sys_dict_data where dict_type = 'lock_bind_status'; delete from sys_dict_type where dict_type = 'lock_bind_status'; insert into sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) values ('门锁绑定状态', 'lock_bind_status', '0', 'admin', now(), 'locklist 下发状态'); insert into sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) values (1, '待下发', '0', 'lock_bind_status', '', 'info', 'Y', '0', 'admin', now(), ''), (2, '已下发', '1', 'lock_bind_status', '', 'success', 'N', '0', 'admin', now(), '网关已接收 locklist'), (3, '下发失败', '2', 'lock_bind_status', '', 'danger', 'N', '0', 'admin', now(), ''); -- ========================================================================= -- 5、执行后核对(只读,按需执行) -- ========================================================================= -- 5.1 归属记录数与已绑定门锁数应一致 -- select (select count(1) from lock_device_gateway where del_flag = '0') as rel_cnt, -- (select count(1) from lock_device where gateway_id is not null and del_flag = '0') as bound_device_cnt; -- -- 5.2 已绑定但缺归属记录的门锁(应为空;有值说明网关已删除或回填被跳过) -- select d.device_id, d.device_sn, d.gateway_id -- from lock_device d -- left join lock_device_gateway dg -- on dg.device_id = d.device_id and dg.gateway_id = d.gateway_id and dg.del_flag = '0' -- where d.gateway_id is not null and d.del_flag = '0' and dg.rel_id is null; -- -- 5.3 主控缓存与归属表不一致的门锁(下发前会由 RouteResolver 实时纠正) -- select d.device_id, d.device_sn, d.gateway_id, d.group_id, g.group_id as gateway_group_id -- from lock_device d -- left join lock_gateway g on g.gateway_id = d.gateway_id -- where d.gateway_id is not null and d.group_id <> g.group_id; -- -- 5.4 字典 -- select dict_type, count(1) from sys_dict_data -- where dict_type = 'lock_bind_status' group by dict_type; -- 期望 3 -- ========================================================================= -- 6、回滚 -- 新代码依赖归属表,不能只删表:先回退到不含 D17 的应用版本,再执行: -- delete from sys_dict_data where dict_type = 'lock_bind_status'; -- delete from sys_dict_type where dict_type = 'lock_bind_status'; -- drop table if exists lock_device_gateway; -- lock_device.gateway_id/group_id 未被本脚本改写(仅按网关分组校正了 -- group_id),回退后旧代码可直接按原字段工作。已执行的 group_id 校正如需 -- 还原,请使用备份。 -- =========================================================================