Files
doorcontrol/base/RuoYi-Vue-master/sql/lock_v2.sql
T
zfjia 271e4ca77c chore: 导入门锁迭代开发工程基线
包含 RuoYi-Vue 后端(ruoyi-iot 门禁模块)、RuoYi-Vue2 前端、设计文档与升级脚本。
忽略构建产物(target/node_modules/dist)、IDE 配置、运行日志与数据库转储。
2026-11-01 19:44:40 +08:00

1026 lines
82 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =========================================================================
-- 公寓人脸门禁系统(智能门锁)业务表 —— MySQL v2.0 基线
--
-- 库名:iotlocks(与 application-druid.yml 中 master.url 保持一致)
-- 前置:先执行同目录 ry_20260417.sql(若依基础表)
-- 版本:v2.0 / 2026-10-30
-- 依据:doc/门锁系统架构设计.md §4.1 / §5.1 / §6 / §8.2 / §9.2
--
-- ⚠️ 本文件是【全新安装】的基线脚本,建表前会 drop 同名表。
-- 已在运行的 v1 库请勿直接执行,改用文末【附录 A:存量升级】。
--
-- 数据分域:
-- 设备域 lock_group → lock_gateway ⇄ lock_device_gateway ⇄ lock_device
-- (一锁可登记到多个网关,归属关系是多对多)
-- 人员域 lock_person → lock_biometric(生物特征)/ lock_credential(凭证)
-- 授权域 lock_grant = 凭证 × 门锁 × 有效期 × 下发状态
-- 编排域 lock_command
-- 事件域 lock_access_log
-- 运维域 lock_mqtt_log / lock_service_config / lock_protocol_profile
--
-- 与 v1(sql/lock.sql,9 张表)的差异:
-- 1) lock_face 拆为 lock_person + lock_biometric,基线不再创建 lock_face;
-- lock_auth 拆为 lock_grant + lock_credential,基线不再创建 lock_auth。
-- 两张旧表在存量库中保留至数据迁移完成,本脚本不负责删除。
-- 2) lock_gateway.group_id 改为非空:网关必须先归入分区才能下发,
-- 否则 topic 无法拼装(分区号是主题第 2 段)。
-- 3) lock_command 删除 target_code,改用 route_scope + route_code:
-- 「投递到哪个主题」与「业务对象是谁」是两个概念,不能共用一个字段。
-- 状态码重排,新增「2 网关已接收」,与两段式应答对齐。
-- 4) lock_mqtt_log 删除 group_code / gateway_sn,改用 route_scope + route_code。
-- 5) 开锁方式、开门结果只保留设备码(2密码/3卡片/4指纹/5二维码/7人脸,
-- 结果码 1-6),废弃自造编码 01-06;平台生成的远程开锁固定为 9。
-- 6) 新增 lock_device_gateway:一把锁可登记到多个网关,每个网关各自维护
-- locklist,两个网关按信号强度自行择优。lock_device.gateway_id 改为
-- primary_gateway_id(选路结果缓存,事实源是关联表);
-- lock_device.group_id 冗余列删除,分区由选中的网关决定。
-- 7) 菜单未随 v2 调整(仍为人脸/授权口径),待人员域前后端改造时统一。
--
-- 规范(与 ry_20260417.sql 一致):
-- 1) 主键统一 bigint(20) auto_increment;关联列统一 bigint(20),不建物理外键。
-- 2) 状态/标志位统一 char(1) 或 char(2);时间统一 datetime。
-- 3) 表与列注释使用 comment;索引统一 idx_ / uk_ 前缀。
-- 4) 脚本可重复执行:建表前 drop table if exists,种子数据先 delete 再 insert。
--
-- 表清单(13 张):
-- 1. lock_group 分区
-- 2. lock_gateway 网关
-- 3. lock_device 门锁
-- 4. lock_device_gateway 门锁-网关归属(多对多)
-- 5. lock_person 人员
-- 6. lock_biometric 生物特征
-- 7. lock_credential 凭证
-- 8. lock_grant 授权
-- 9. lock_access_log 通行记录
-- 10. lock_command 下行命令
-- 11. lock_mqtt_log MQTT 报文留痕
-- 12. lock_service_config 服务参数配置
-- 13. lock_protocol_profile 协议配置(多厂家/多信封)
-- =========================================================================
set names utf8mb4;
-- =========================================================================
-- 1、分区表
-- 业务含义:分区是三重身份的统一载体——组织维度(数据权限)、
-- 网络维度(LoRa 路由域,即 MQTT 主题第 2 段的 group_code)、
-- 下发维度(组播 topic 粒度)。三者必须由同一行数据承载。
-- =========================================================================
drop table if exists lock_group;
create table lock_group (
group_id bigint(20) not null auto_increment comment '分区ID',
group_code varchar(32) not null comment '分区编号(MQTT主题第2段,FFFA+4B,全局唯一,一经占用不再复用)',
group_name varchar(64) not null comment '分区名称',
parent_id bigint(20) default 0 comment '上级分区ID(0为顶级,支持 园区→楼栋→楼层 树形)',
ancestors varchar(500) default '' comment '祖级列表(逗号分隔,如 0,100,101)',
zone_type char(1) default '4' comment '分区类型(1园区 2楼栋 3楼层 4其他,字典 lock_zone_type)',
dept_id bigint(20) default null comment '所属部门ID(用于数据权限)',
manager varchar(64) default null comment '负责人',
phone varchar(20) default null comment '联系电话',
address varchar(255) default null comment '地址',
config_id bigint(20) default null comment '关联MQTT服务器配置ID(lock_service_config.config_id,空表示用全局默认)',
lock_capacity int(11) default 0 comment '设计门锁容量(容量规划与超限告警用,0表示不限)',
status char(1) default '0' comment '状态(0正常 1停用)',
order_num int(4) default 0 comment '显示顺序',
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 (group_id),
unique key uk_lock_group_code (group_code),
key idx_lock_group_parent (parent_id),
key idx_lock_group_dept (dept_id),
key idx_lock_group_status (status)
) engine=innodb comment = '分区表';
-- =========================================================================
-- 2、网关表
-- 业务含义:LoRa 汇聚节点,下挂 ≤16 台门锁;所属分区必填,决定网关级
-- 主题(ydlink/{gatewaySn}/thing/...)与组播主题(ydlink/{groupCode}/...)。
-- gateway_mac 用于 onoff_line 报文反查(该报文 devId 是 12 位 hex MAC)。
-- =========================================================================
drop table if exists lock_gateway;
create table lock_gateway (
gateway_id bigint(20) not null auto_increment comment '网关ID',
gateway_sn varchar(32) not null comment '网关SN(8位hex,MQTT主题第2段,全局唯一)',
gateway_mac varchar(32) default null comment '网关MAC(12位hex,onoff_line 报文 devId,唯一)',
gateway_name varchar(64) default null comment '网关名称',
group_id bigint(20) not null comment '所属分区ID(lock_group.group_id,必填,决定下发主题)',
gateway_ip varchar(64) default null comment '网关IP',
subnet_mask varchar(64) default null comment '子网掩码',
default_gateway varchar(64) default null comment '默认网关',
gateway_password varchar(256) default null comment '网关密码(AES-GCM 密文,下发时解密为明文)',
config_id bigint(20) default null comment '关联MQTT服务器配置ID',
protocol_profile_id bigint(20) default null comment '协议配置ID(lock_protocol_profile.profile_id)',
lora_config varchar(1024) default null comment 'LORA参数配置(JSON)',
mqtt_config varchar(1024) default null comment '网关侧MQTT参数(JSON)',
max_device int(11) default 16 comment '最大下挂门锁数(协议上限16)',
device_count int(11) default 0 comment '关联门锁数(lock_device_gateway 计数,上限 max_device)',
config_version int(11) default 0 comment '配置版本号(每次下发+1,用于比对网关侧是否已同步)',
sync_status char(1) default '0' comment '配置同步状态(0未同步 1已同步 2同步失败)',
online_status char(1) default '1' comment '在线状态(0在线 1离线 2未知,字典 lock_online_status)',
last_online_time datetime comment '最后在线时间',
last_sync_time datetime comment '最后配置同步时间',
firmware_ver varchar(32) default null comment '网关固件版本',
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 (gateway_id),
unique key uk_lock_gateway_sn (gateway_sn),
unique key uk_lock_gateway_mac (gateway_mac),
key idx_lock_gateway_group (group_id),
key idx_lock_gateway_online (online_status),
key idx_lock_gateway_sync (sync_status)
) engine=innodb comment = '网关表';
-- =========================================================================
-- 3、门锁表
-- 业务含义:门锁终端。归属关系是【多对多】:一把锁可以登记到多个网关,
-- 每个网关各自维护自己的 locklist,两个网关按与锁之间的信号强度择优执行。
-- 归属的事实源在 lock_device_gateway;本表的 primary_gateway_id 只是
-- 选路结果缓存(决定下发主题),不是事实源。
-- 协议字段映射:device_sn -> devId,device_password -> devPsw,
-- dev_type -> devType,dev_ver -> devVer。
-- device_mac 是 opencloselock 请求 params[4] 的必填值(12位hex),
-- 缺它远程开锁组包不合法。
-- =========================================================================
drop table if exists lock_device;
create table lock_device (
device_id bigint(20) not null auto_increment comment '门锁ID',
device_sn varchar(32) not null comment '门锁SN(8位hex,对应协议devId,全局唯一)',
device_mac varchar(32) default null comment '门锁MAC(12位hex,opencloselock params[4])',
device_name varchar(64) default null comment '门锁名称',
primary_gateway_id bigint(20) default null comment '当前主控网关ID(选路结果缓存,事实源为 lock_device_gateway,为空表示未登记到任何网关)',
device_password varchar(256) default null comment '门锁密码(AES-GCM 密文,对应协议devPsw,下发时解密)',
dev_type varchar(8) default null comment '设备类型(对应协议devType)',
dev_ver varchar(16) default null comment '设备固件版本(对应协议devVer)',
face_algo_version varchar(32) default null comment '人脸算法版本(特征提取 algorithm_version 入参,每把锁各自维护)',
protocol_profile_id bigint(20) default null comment '协议配置ID(lock_protocol_profile.profile_id)',
capacity_face int(11) default 0 comment '锁内人脸容量(0表示未知,不做校验)',
capacity_pwd int(11) default 0 comment '锁内密码容量(0表示未知)',
capacity_card int(11) default 0 comment '锁内卡片容量(0表示未知)',
room_no varchar(64) default null comment '分配房间号',
bind_time datetime comment '首次登记到网关的时间(明细见 lock_device_gateway.bind_time)',
last_sync_time datetime comment '最后时间同步时间',
signal_strength varchar(8) default null comment '信号强度RSSI(dBm,op=lockrssi 上报,如 -42)',
battery_level int(11) default null comment '剩余电量(0-100,255表示常电/未知)',
door_state char(1) default null comment '门状态(0关 1开 空未知,状态位bit1)',
deadbolt_state char(1) default null comment '反锁/取电状态(0否 1是 空未知,状态位bit2)',
door_sensor_state char(1) default null comment '门磁状态(0/1,状态位bit3,含义依现场定义)',
raw_state varchar(64) default null comment '最近一次状态位原值(便于协议变更后回溯解析)',
last_status_time datetime comment '最后状态上报时间',
online_status char(1) default '1' comment '在线状态(0在线 1离线 2未知,字典 lock_online_status)',
last_online_time datetime comment '最后在线时间',
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 (device_id),
unique key uk_lock_device_sn (device_sn),
key idx_lock_device_mac (device_mac),
key idx_lock_device_primary_gw (primary_gateway_id),
key idx_lock_device_room (room_no),
key idx_lock_device_online (online_status)
) engine=innodb comment = '门锁表';
-- =========================================================================
-- 4、门锁-网关归属表(多对多)
-- 业务含义:一把门锁登记到了哪些网关上,以及每个网关视角下该锁的信号与
-- 绑定状态。
--
-- 为什么需要独立成表:厂家协议里 locklist 是「网关的从设备清单」,绑定
-- 动作本身就是 per-gateway 的——locklist(add) 必须在每个期望为该锁服务的
-- 网关上各下发一次。同一把锁被两个网关覆盖时,两个网关依据与锁之间的
-- 信号强度自行择优执行,平台只做两件事:记录每条上报来自哪个网关、
-- 在需要平台选路时选出主控网关。
--
-- 选路规则(应用层实现,本表只提供数据):
-- 1) 同分区多网关:LoRa 侧自行择优,平台不改路由,只按上报刷新
-- signal_strength / online_status / last_signal_time;
-- 2) 跨分区多网关:按「在线优先 → 信号强度最大 → 上报时间最新」选一个,
-- 回写 lock_device.primary_gateway_id,由该网关的分区决定下发主题;
-- 3) 主控网关离线时降级到次优网关;全部离线则命令置 PENDING 等待上线。
--
-- 解除归属请用物理删除:本表可由网关的 locklist 回执重建,不存在需要保留的
-- 业务历史;若走 del_flag 逻辑删除,会被 uk_lock_device_gateway 挡住重新绑定。
-- =========================================================================
drop table if exists lock_device_gateway;
create table 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 = '门锁-网关归属表';
-- =========================================================================
-- 5、人员表
-- 业务含义:人员主数据,「是谁」与「凭什么开门」解耦。
-- person_no 是协议中的人员/人脸 ID,8 位定长(如 AA000003),
-- 一经下发到设备不可变更(设备侧用它索引特征与权限)。
-- =========================================================================
drop table if exists lock_person;
create table lock_person (
person_id bigint(20) not null auto_increment comment '人员ID',
person_no varchar(16) not null comment '人员编号(协议人脸/人员ID,8位定长,如 AA000003,全局唯一且不可变更)',
person_name varchar(64) default null comment '姓名',
person_type char(1) default '0' comment '人员类型(0住户 1员工 2访客 3其他,字典 lock_person_type)',
dept_id bigint(20) default null comment '所属部门ID(数据权限)',
sys_user_id bigint(20) default null comment '关联若依账号ID(员工自助场景,可为空)',
id_card_no varchar(256) default null comment '证件号(AES-GCM 密文)',
phone varchar(20) default null comment '联系电话',
valid_from datetime comment '人员有效期起(早于凭证有效期时以人员为准)',
valid_to datetime comment '人员有效期止(空表示永久)',
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 (person_id),
unique key uk_lock_person_no (person_no),
key idx_lock_person_name (person_name),
key idx_lock_person_dept (dept_id),
key idx_lock_person_sys_user (sys_user_id)
) engine=innodb comment = '人员表';
-- =========================================================================
-- 6、生物特征表
-- 业务含义:一人 × 一种生物特征 × 一个算法版本 = 一份特征。
-- 协议要求特征值与门锁算法版本匹配;同一人员在算法版本不同的门锁上
-- 需要多份特征,故 algo_version 参与唯一键。
-- 存的是「未注入人员ID的原始特征」的 AES-GCM 密文;下发时才注入 person_no。
-- =========================================================================
drop table if exists lock_biometric;
create table lock_biometric (
bio_id bigint(20) not null auto_increment comment '特征ID',
person_id bigint(20) not null comment '人员ID(lock_person.person_id)',
bio_type char(1) not null default '1' comment '特征类型(1人脸 2指纹,字典 lock_bio_type)',
algo_version varchar(32) not null default '' comment '提取时使用的算法版本(来自 lock_device.face_algo_version)',
photo_url varchar(512) default null comment '原图URL(仅人脸,对象存储/文件服务)',
photo_md5 varchar(32) default null comment '原图指纹(去重与变更检测)',
feature_data mediumtext comment '特征值(AES-GCM 密文,Base64密文)',
feature_md5 varchar(32) default null comment '特征指纹(变更检测与幂等下发)',
feature_size int(11) default 0 comment '特征字节数',
quality_score int(11) default null comment '质量分(提取服务返回0-100)',
extract_status char(1) default '0' comment '提取状态(0未提取 1成功 2失败,字典 lock_extract_status)',
extract_time datetime comment '提取时间',
extract_msg varchar(512) default null comment '提取失败原因',
is_primary char(1) default '0' comment '是否主特征(0否 1是,一人多脸时取主)',
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 (bio_id),
unique key uk_lock_biometric_person_algo (person_id, bio_type, algo_version),
key idx_lock_biometric_person (person_id),
key idx_lock_biometric_extract (extract_status)
) engine=innodb comment = '生物特征表';
-- =========================================================================
-- 7、凭证表
-- 业务含义:「凭什么开门」——密码 / IC卡 / 指纹 / 人脸引用。
-- 密码必须可逆加密:协议要求明文下发,不能做 hash。
-- cred_fingerprint 为 HMAC-SHA256,只用于查重,不落明文。
-- =========================================================================
drop table if exists lock_credential;
create table lock_credential (
cred_id bigint(20) not null auto_increment comment '凭证ID',
person_id bigint(20) not null comment '人员ID(lock_person.person_id)',
cred_type char(2) not null comment '凭证类型(01人脸 02密码 03指纹 04IC卡,字典 lock_cred_type)',
cred_value_cipher mediumtext comment '凭证内容(AES-GCM 密文:密码/卡号/指纹模板/特征引用)',
cred_fingerprint varchar(64) default null comment '凭证指纹(HMAC-SHA256,查重用,不落明文)',
cred_seq varchar(64) default null comment '协议侧凭证编号(卡片ID/密码/绑定编号,需可读)',
begin_time datetime comment '凭证有效期起(设备侧校验)',
end_time datetime comment '凭证有效期止(空表示永久)',
max_use int(11) default 0 comment '最大使用次数(0表示不限,对应 icardonce 一类限次卡)',
used_count int(11) default 0 comment '已使用次数',
freeze_flag char(1) default '0' comment '冻结标志(0正常 1冻结,对应 freeze 动作)',
source char(1) default '0' comment '来源(0平台生成 1设备学习 2导入,字典 lock_cred_source)',
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 (cred_id),
unique key uk_lock_credential_fp (person_id, cred_type, cred_fingerprint),
key idx_lock_credential_person (person_id),
key idx_lock_credential_type (cred_type),
key idx_lock_credential_seq (cred_seq)
) engine=innodb comment = '凭证表';
-- =========================================================================
-- 8、授权表
-- 业务含义:凭证 × 门锁 × 有效期,并跟踪下发到设备的同步状态。
-- 协议对应:ydlink/{groupCode}/thing/data/batch 的 params(add/delete)。
-- 唯一约束保证「同人同锁同类型凭证」只有一条授权。
-- =========================================================================
drop table if exists lock_grant;
create table lock_grant (
grant_id bigint(20) not null auto_increment comment '授权ID',
grant_no varchar(64) not null comment '授权编号(业务唯一,建议 GRANT+日期+序号)',
person_id bigint(20) not null comment '人员ID',
person_no varchar(16) default null comment '冗余人员编号(协议组包直接用)',
cred_id bigint(20) not null comment '凭证ID(lock_credential.cred_id)',
cred_type char(2) not null comment '冗余凭证类型(01人脸 02密码 03指纹 04IC卡)',
device_id bigint(20) not null comment '目标门锁ID',
device_sn varchar(32) default null comment '冗余门锁SN',
gateway_id bigint(20) default null comment '冗余网关ID',
group_id bigint(20) default null comment '冗余分区ID(决定下发主题)',
grant_mode char(1) default '1' comment '授权方式(1常驻 2时段 3一次性,字典 lock_grant_mode)',
begin_time datetime comment '授权生效窗口起(下发到设备)',
end_time datetime comment '授权生效窗口止(空表示永久)',
sync_status char(1) default '0' comment '下发状态(0待下发 1已下发 2成功 3失败 4已撤销 5已过期,字典 lock_sync_status)',
sync_msg varchar(512) default null comment '下发结果说明',
retry_count int(11) default 0 comment '下发重试次数',
last_sync_time datetime comment '最后下发/响应时间',
last_command_no varchar(64) default null comment '最近一次下发命令号(双向追溯用)',
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 (grant_id),
unique key uk_lock_grant_no (grant_no),
unique key uk_lock_grant_person_device (person_id, device_id, cred_type),
key idx_lock_grant_person (person_id),
key idx_lock_grant_cred (cred_id),
key idx_lock_grant_device (device_id),
key idx_lock_grant_group (group_id),
key idx_lock_grant_sync (sync_status),
key idx_lock_grant_command (last_command_no)
) engine=innodb comment = '授权表';
-- =========================================================================
-- 9、通行记录表
-- 业务含义:设备通过 event/trigger(op=lockrecord)上报的开门记录。
-- 高频只追加表,无 del_flag / update_*。
--
-- 【幂等】设备上报的 seq 常为 "0" 或空,不能做唯一键;
-- 去重靠业务键 (device_sn, open_time, open_type, 凭证) 先查后插 +
-- Redis SETNX 抑制并发重复。
--
-- 【分区】当前不做分区(容量假设未经实测)。当单表超过千万行时按
-- open_time 月度 RANGE 分区,注意 MySQL 要求分区列必须包含在每个唯一键中,
-- 届时主键需改为 (log_id, open_time)。
-- =========================================================================
drop table if exists lock_access_log;
create table lock_access_log (
log_id bigint(20) not null auto_increment comment '日志ID',
device_id bigint(20) default null comment '门锁ID(解析 device_sn 得到,未登记时为空)',
device_sn varchar(32) not null comment '门锁SN',
gateway_id bigint(20) default null comment '上报网关ID',
gateway_sn varchar(32) default null comment '上报网关SN',
group_id bigint(20) default null comment '所属分区ID',
person_id bigint(20) default null comment '人员ID(凭证反查得到)',
person_no varchar(16) default null comment '人员编号',
person_name varchar(64) default null comment '冗余人员姓名',
cred_id bigint(20) default null comment '凭证ID(凭证反查得到)',
cred_type char(2) default null comment '凭证类型(01人脸 02密码 03指纹 04IC卡)',
card_no varchar(32) default null comment '卡号(卡片开门时的脱敏凭证号)',
open_type varchar(8) default null comment '开门方式(设备码:2密码 3卡片 4指纹 5二维码 7人脸,字典 lock_open_type)',
open_result varchar(8) default null comment '开门结果(1成功 2通用错误 3密码错 4卡错 5传感器 6人脸错,字典 lock_open_result)',
open_dir varchar(8) default null comment '开/关方向(V1为空;V2:1开 2关)',
open_time datetime comment '开门时间(设备上报时间)',
recv_time datetime comment '服务端接收时间',
seq varchar(32) default null comment '报文序号(仅留痕,不用于唯一约束)',
protocol_ver char(1) default '1' comment '记录协议版本(1或2,决定 params[4] 语义)',
raw_params text comment '原始params数组(JSON,便于协议变更后重放解析)',
create_time datetime comment '创建时间',
remark varchar(500) default null comment '备注',
primary key (log_id),
key idx_lock_access_log_device (device_sn),
key idx_lock_access_log_person (person_no),
key idx_lock_access_log_time (open_time),
key idx_lock_access_log_idem (device_sn, open_time, open_type),
key idx_lock_access_log_group (group_id),
key idx_lock_access_log_seq (seq)
) engine=innodb comment = '通行记录表';
-- =========================================================================
-- 10、下行命令表
-- 业务含义:所有平台→设备的指令留痕,支撑回执匹配、重试补偿、对账与审计。
--
-- 【路由与身份分离】route_scope/route_code 决定报文发到哪个主题;
-- target_type/target_id 说明业务对象是谁。二者不可混用(v1 用 target_code
-- 一个字段承担两种语义,导致按锁反查主题要绕三道推导)。
--
-- 【状态机】0待发送 → 1已发送 → 2网关已接收 → 3成功/4失败;5超时;6已取消。
-- 收到终态码(0/11/12)即置终态,不强制等到网关回执 10。
--
-- 【幂等】idempotency_key 不建唯一索引:同一业务在业务上允许合法重发
-- (如授权覆盖下发),唯一性由 Redis SETNX 在 5 秒窗口内保证,
-- DB 索引仅用于排查重复下发。
-- =========================================================================
drop table if exists lock_command;
create table lock_command (
command_id bigint(20) not null auto_increment comment '命令ID',
command_no varchar(64) not null comment '命令编号(对应协议seq,全局唯一,分布式生成)',
command_type char(4) not null comment '命令类型(字典 lock_command_type,0001-0020)',
route_scope char(1) not null comment '路由范围(1分组 2网关,字典 lock_route_scope)',
route_code varchar(32) not null comment '路由编号(分区号 group_code 或网关SN,决定下发主题)',
target_type char(1) default null comment '业务目标类型(1网关 2门锁 3分组,字典 lock_target_type)',
target_id bigint(20) default null comment '业务目标ID(网关ID/门锁ID/分区ID)',
biz_type varchar(16) default null comment '业务类型(GRANT/DEVICE/GATEWAY/GROUP,解释 biz_id 的含义)',
biz_id bigint(20) default null comment '业务ID(如授权下发时指向 lock_grant.grant_id)',
topic varchar(255) default null comment '下发主题',
payload mediumtext comment '下发报文(特征值等敏感字段需掩码)',
req_time datetime comment '下发时间',
resp_topic varchar(255) default null comment '响应主题',
resp_payload mediumtext comment '响应报文(敏感字段需掩码)',
resp_time datetime comment '响应时间',
command_status char(1) default '0' comment '命令状态(0待发送 1已发送 2网关已接收 3成功 4失败 5超时 6已取消,字典 lock_command_status)',
result_code varchar(8) default null comment '归一后的结果码(0成功 10网关已接收 11设备忙 12设备不存在)',
gateway_ack char(1) default '0' comment '是否收到网关回执10(0否 1是)',
device_ack char(1) default '0' comment '是否收到门锁终态(0否 1是)',
device_result char(1) default null comment '门锁终态结果(0失败 1成功 空未知)',
retry_count int(11) default 0 comment '已重试次数',
max_retry int(11) default 3 comment '最大重试次数',
next_retry_time datetime comment '下次重试时间(补偿任务扫描,必须早于 timeout_at)',
timeout_at datetime comment '超时截止时间(按命令类型差异化)',
idempotency_key varchar(64) default null comment '幂等键(hash(命令类型,目标,业务,报文指纹),5秒窗口内去重)',
error_msg varchar(512) default null comment '失败原因',
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 (command_id),
unique key uk_lock_command_no (command_no),
key idx_lock_command_status (command_status),
key idx_lock_command_timeout (command_status, timeout_at),
key idx_lock_command_retry (command_status, next_retry_time),
key idx_lock_command_route (route_scope, route_code),
key idx_lock_command_biz (biz_type, biz_id),
key idx_lock_command_idem (idempotency_key),
key idx_lock_command_time (req_time)
) engine=innodb comment = '下行命令表';
-- =========================================================================
-- 11、MQTT 报文留痕表
-- 上下行原始报文留痕,用于协议联调、问题回溯与审计。
-- payload 入库前必须掩码(特征 Base64 / 密码 / 卡号一律替换为长度+MD5)。
-- route_scope/route_code 取代 v1 的 group_code/gateway_sn:
-- 主题第 2 段既可能是分区号也可能是网关SN,一个字段无法表达。
-- =========================================================================
drop table if exists lock_mqtt_log;
create table lock_mqtt_log (
log_id bigint(20) not null auto_increment comment '日志ID',
direction char(1) not null comment '方向(1上行设备→平台 2下行平台→设备)',
topic varchar(255) not null comment 'MQTT主题',
route_scope char(1) default null comment '主题第2段类型(1分组 2网关)',
route_code varchar(32) default null comment '主题第2段取值(分区号或网关SN)',
device_sn varchar(32) default null comment '报文中的设备标识(devId,可能是锁SN/网关SN/网关MAC)',
op varchar(32) default null comment '操作类型(协议op)',
mode varchar(8) default null comment '方向标识(s2d服务端→设备 d2s设备→服务端)',
ac varchar(8) default null comment '动作类型(协议ac:req请求 resp应答)',
seq varchar(32) default null comment '报文序号(协议seq)',
protocol_ver char(1) default null comment '业务协议版本(1或2,来自 protocalVer/devVer)',
envelope_ver char(1) default null comment '信封形态(1完整V1 2精简V2 3无信封,字典 lock_envelope_ver)',
qos smallint default 1 comment '服务质量',
retained char(1) default '0' comment '是否保留消息(0否 1是)',
payload mediumtext comment '报文原文(敏感字段掩码后)',
chksum varchar(32) default null comment '报文校验和(协议未定义算法,仅留痕)',
verify_status char(1) default '0' comment '校验结果(0未校验 1通过 2失败)',
handle_status char(1) default '0' comment '处理状态(0待处理 1成功 2失败 3已忽略,字典 lock_handle_status)',
handle_msg varchar(512) default null comment '处理结果说明',
cost_time int(11) default 0 comment '处理耗时(毫秒)',
recv_time datetime comment '接收/发送时间',
create_time datetime comment '创建时间',
primary key (log_id),
key idx_lock_mqtt_log_topic (topic),
key idx_lock_mqtt_log_device (device_sn),
key idx_lock_mqtt_log_route (route_scope, route_code),
key idx_lock_mqtt_log_time (recv_time),
key idx_lock_mqtt_log_handle (handle_status),
key idx_lock_mqtt_log_seq (seq),
key idx_lock_mqtt_log_direct (direction)
) engine=innodb comment = 'MQTT报文留痕表';
-- =========================================================================
-- 12、服务参数配置表
-- 承载“系统参数”中的 MQTT 服务器配置、人脸特征提取服务配置、
-- 平台参数与协议策略。结构化分类字段 + JSON 值的折中方案,
-- 新增配置项无需改表,前端按 config_type 渲染不同表单。
--
-- config_value JSON 约定:
-- config_type=01(MQTT服务器)
-- {"brokerUrl":"tcp://127.0.0.1:1883","clientId":"","username":"","password":"",
-- "defaultQos":1,"cleanSession":true,"keepAlive":60,"connectionTimeout":30}
-- config_type=02(人脸特征服务)
-- POST multipart/form-data,表单字段 file 为图片文件
-- 返回 {"functionSuccess":true,"resultSuccess":true,"resultData":"特征base64","msg":"..."}
-- {"endpoint":"http://192.168.1.202:8802/...","timeout":15000,"qualityThreshold":60}
-- config_type=03(平台参数)
-- {"accessLogKeepDays":180,"mqttLogLevel":"error","commandTimeout":10,"maxRetry":3}
-- config_type=04(协议策略)
-- {"protocolVer":"1","commandTimeout":10,"pushRatePerSecond":5,
-- "retryBaseSeconds":30,"maskFields":["feature","password","cardNo"]}
-- =========================================================================
drop table if exists lock_service_config;
create table lock_service_config (
config_id bigint(20) not null auto_increment comment '配置ID',
config_name varchar(100) not null comment '配置名称',
config_type char(2) not null comment '配置类型(01MQTT服务器 02人脸特征服务 03平台参数 04协议策略,字典 lock_config_type)',
config_value text comment '配置内容(JSON)',
group_id bigint(20) default null comment '归属分区ID(为空表示全局默认)',
is_default char(1) default '1' comment '是否默认(0是 1否)',
enabled char(1) default '0' comment '是否启用(0启用 1停用)',
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 (config_id),
unique key uk_lock_service_config_name (config_name, config_type),
key idx_lock_service_config_type (config_type)
) engine=innodb comment = '服务参数配置表';
-- =========================================================================
-- 13、协议配置表
-- 把「厂家差异」收敛成数据:主题前缀、资源段、信封版本、结果码映射、
-- 参数模板都由本表驱动,避免 ydlink/thing 之类的前缀散落在代码里。
-- param_templates 示例:
-- {"password.add":{"requestSize":7,"resultIndex":2},
-- "password.delete":{"requestSize":5,"resultIndex":2},
-- "opencloselock":{"requestSize":-1,"resultIndex":5}}
-- =========================================================================
drop table if exists lock_protocol_profile;
create table lock_protocol_profile (
profile_id bigint(20) not null auto_increment comment '协议配置ID',
profile_code varchar(32) not null comment '配置编码(如 ydlink-v1,全局唯一)',
profile_name varchar(64) not null comment '配置名称',
vendor varchar(32) not null comment '厂家标识(如 ydlink)',
topic_prefix varchar(32) not null default 'ydlink' comment '主题前缀(第1段)',
resource_segment varchar(32) not null default 'thing' comment '资源段(第3段)',
envelope_ver char(1) default '1' comment '默认信封(1完整V1 2精简V2 3无信封,字典 lock_envelope_ver)',
protocol_ver char(1) default '1' comment '默认业务协议版本(1或2)',
result_code_map text comment '结果码映射(JSON:设备码→内部分类)',
param_templates text comment '参数模板(JSON:op[.子命令]→长度/结果码下标/字段名)',
enabled char(1) default '0' comment '是否启用(0启用 1停用)',
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 (profile_id),
unique key uk_lock_protocol_profile_code (profile_code),
key idx_lock_protocol_profile_vendor (vendor)
) engine=innodb comment = '协议配置表';
-- =========================================================================
-- 14、字典初始化
-- 先按 dict_type 清理,保证脚本可重复执行。
-- 取值口径以 doc/门锁系统架构设计.md §9.4 为准。
-- =========================================================================
delete from sys_dict_data where dict_type in
('lock_online_status','lock_open_type','lock_open_result','lock_cred_type',
'lock_command_type','lock_command_status','lock_sync_status','lock_handle_status',
'lock_config_type','lock_extract_status','lock_person_type','lock_target_type',
'lock_zone_type','lock_bio_type','lock_grant_mode','lock_cred_source',
'lock_route_scope','lock_envelope_ver','lock_bind_status');
delete from sys_dict_type where dict_type in
('lock_online_status','lock_open_type','lock_open_result','lock_cred_type',
'lock_command_type','lock_command_status','lock_sync_status','lock_handle_status',
'lock_config_type','lock_extract_status','lock_person_type','lock_target_type',
'lock_zone_type','lock_bio_type','lock_grant_mode','lock_cred_source',
'lock_route_scope','lock_envelope_ver','lock_bind_status');
insert into sys_dict_type (dict_id, dict_name, dict_type, status, create_by, create_time, remark) values
(100, '门禁设备在线状态', 'lock_online_status', '0', 'admin', CURRENT_TIMESTAMP, '网关/门锁在线状态'),
(101, '门禁开门方式', 'lock_open_type', '0', 'admin', CURRENT_TIMESTAMP, '只保留设备码,废弃自造编码'),
(102, '门禁开门结果', 'lock_open_result', '0', 'admin', CURRENT_TIMESTAMP, '设备记录结果码 1-6'),
(103, '门禁凭证类型', 'lock_cred_type', '0', 'admin', CURRENT_TIMESTAMP, '人脸/密码/指纹/IC卡'),
(104, '门禁命令类型', 'lock_command_type', '0', 'admin', CURRENT_TIMESTAMP, '下行命令类型'),
(105, '门禁命令状态', 'lock_command_status', '0', 'admin', CURRENT_TIMESTAMP, '与两段式应答对齐'),
(106, '门禁授权同步状态', 'lock_sync_status', '0', 'admin', CURRENT_TIMESTAMP, '授权下发同步状态'),
(107, '门禁报文处理状态', 'lock_handle_status', '0', 'admin', CURRENT_TIMESTAMP, 'MQTT报文处理状态'),
(108, '门禁服务配置类型', 'lock_config_type', '0', 'admin', CURRENT_TIMESTAMP, '服务参数配置类型'),
(109, '人脸特征提取状态', 'lock_extract_status', '0', 'admin', CURRENT_TIMESTAMP, '特征提取状态'),
(110, '门禁人员类型', 'lock_person_type', '0', 'admin', CURRENT_TIMESTAMP, '人员类型'),
(111, '门禁命令目标类型', 'lock_target_type', '0', 'admin', CURRENT_TIMESTAMP, '命令业务目标类型'),
(112, '门禁分区类型', 'lock_zone_type', '0', 'admin', CURRENT_TIMESTAMP, '园区/楼栋/楼层'),
(113, '生物特征类型', 'lock_bio_type', '0', 'admin', CURRENT_TIMESTAMP, '人脸/指纹'),
(114, '授权方式', 'lock_grant_mode', '0', 'admin', CURRENT_TIMESTAMP, '常驻/时段/一次性'),
(115, '凭证来源', 'lock_cred_source', '0', 'admin', CURRENT_TIMESTAMP, '平台生成/设备学习/导入'),
(116, '命令路由范围', 'lock_route_scope', '0', 'admin', CURRENT_TIMESTAMP, '分组/网关'),
(117, '协议信封形态', 'lock_envelope_ver', '0', 'admin', CURRENT_TIMESTAMP, '完整V1/精简V2/无信封'),
(118, '门锁绑定状态', 'lock_bind_status', '0', 'admin', CURRENT_TIMESTAMP, 'locklist 下发状态');
insert into sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) values
-- 在线状态
(100, 1, '在线', '0', 'lock_online_status', '', 'success', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(101, 2, '离线', '1', 'lock_online_status', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(102, 3, '未知', '2', 'lock_online_status', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 开门方式(设备记录 params[2] 原值)
(110, 1, '密码', '2', 'lock_open_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '设备码'),
(111, 2, 'IC卡', '3', 'lock_open_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '设备码'),
(112, 3, '指纹', '4', 'lock_open_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '设备码'),
(113, 4, '二维码', '5', 'lock_open_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '设备码'),
(114, 5, '人脸', '7', 'lock_open_type', '', 'success', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '设备码,实测值'),
(115, 6, '远程开锁', '9', 'lock_open_type', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '平台生成,非设备上报'),
-- 开门结果(设备记录结果码)
(120, 1, '成功', '1', 'lock_open_result', '', 'success', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(121, 2, '通用错误', '2', 'lock_open_result', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(122, 3, '密码错误', '3', 'lock_open_result', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(123, 4, '卡片错误', '4', 'lock_open_result', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(124, 5, '传感器事件','5','lock_open_result', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(125, 6, '人脸错误', '6', 'lock_open_result', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 凭证类型
(130, 1, '人脸', '01', 'lock_cred_type', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, 'op=facefeature'),
(131, 2, '密码', '02', 'lock_cred_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'op=password'),
(132, 3, '指纹', '03', 'lock_cred_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'op=finger'),
(133, 4, 'IC卡', '04', 'lock_cred_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'op=iccard'),
-- 命令类型(与架构设计 §5.5 一一对应)
(140, 1, '绑定门锁', '0001', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'locklist(add) / property/set'),
(141, 2, '解绑门锁', '0002', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'locklist(clear/add) / property/set'),
(142, 3, '同步门锁时间', '0003', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(systime) / property/set'),
(143, 4, '远程开锁', '0004', 'lock_command_type', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'opencloselock(1) / action/execute'),
(144, 5, '远程关锁', '0005', 'lock_command_type', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'opencloselock(2) / action/execute'),
(145, 6, '重启网关', '0006', 'lock_command_type', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(reset) / property/set'),
(146, 7, '修改网络参数', '0007', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(server/bind) / property/set'),
(147, 8, '修改LORA参数', '0008', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(lorainfo) / property/set'),
(148, 9, '修改MQTT参数', '0009', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(server) / property/set'),
(149, 10, '保存配置', '0010', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'locklist+config 组合,无独立 op'),
(150, 11, '授权下发', '0011', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'facefeature/password/iccard/finger(add) / data/batch'),
(151, 12, '清除授权', '0012', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '同上(delete) / data/batch'),
(152, 13, '清除门锁', '0013', 'lock_command_type', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(reset) / property/set'),
(153, 14, '恢复默认参数', '0014', 'lock_command_type', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(defaultpara) / property/set'),
(154, 15, '配置断电延时', '0015', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'config(poweroffdelay) / property/set'),
(155, 16, '远程语音播报', '0016', 'lock_command_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'voice(play) / action/execute'),
(156, 17, '查询从设备状态', '0017', 'lock_command_type', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'getslaveinfo / data/batch'),
(157, 18, '读取开门记录', '0018', 'lock_command_type', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'getrecord(readrecord) / data/batch'),
(158, 19, '读取权限列表', '0019', 'lock_command_type', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'getkey(readkey) / data/batch,对账用'),
(159, 20, '读网关信息', '0020', 'lock_command_type', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'getgwinfo / property/set,发现用'),
-- 命令状态
(160, 1, '待发送', '0', 'lock_command_status', '', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, 'PENDING'),
(161, 2, '已发送', '1', 'lock_command_status', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'PUBLISHED'),
(162, 3, '网关已接收', '2', 'lock_command_status', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'GATEWAY_ACK,结果码10,中间态'),
(163, 4, '成功', '3', 'lock_command_status', '', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '结果码0'),
(164, 5, '失败', '4', 'lock_command_status', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '结果码11/12'),
(165, 6, '超时', '5', 'lock_command_status', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '超过 timeout_at'),
(166, 7, '已取消', '6', 'lock_command_status', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '用户撤回/目标已解绑'),
-- 授权同步状态
(170, 1, '待下发', '0', 'lock_sync_status', '', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(171, 2, '已下发', '1', 'lock_sync_status', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '命令已发出未终态'),
(172, 3, '成功', '2', 'lock_sync_status', '', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(173, 4, '失败', '3', 'lock_sync_status', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(174, 5, '已撤销', '4', 'lock_sync_status', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '设备侧已删除'),
(175, 6, '已过期', '5', 'lock_sync_status', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '超过 end_time'),
-- 报文处理状态
(180, 1, '待处理', '0', 'lock_handle_status', '', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(181, 2, '成功', '1', 'lock_handle_status', '', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(182, 3, '失败', '2', 'lock_handle_status', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '含参数模板校验不通过'),
(183, 4, '已忽略', '3', 'lock_handle_status', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 服务配置类型
(190, 1, 'MQTT服务器', '01', 'lock_config_type', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(191, 2, '人脸特征服务', '02', 'lock_config_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(192, 3, '平台参数', '03', 'lock_config_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(193, 4, '协议策略', '04', 'lock_config_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '重试/限流/掩码'),
-- 特征提取状态
(200, 1, '未提取', '0', 'lock_extract_status', '', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(201, 2, '成功', '1', 'lock_extract_status', '', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(202, 3, '失败', '2', 'lock_extract_status', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 人员类型
(210, 1, '住户', '0', 'lock_person_type', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(211, 2, '员工', '1', 'lock_person_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(212, 3, '访客', '2', 'lock_person_type', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(213, 4, '其他', '3', 'lock_person_type', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 命令目标类型
(220, 1, '网关', '1', 'lock_target_type', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(221, 2, '门锁', '2', 'lock_target_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(222, 3, '分区', '3', 'lock_target_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 分区类型
(230, 1, '园区', '1', 'lock_zone_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(231, 2, '楼栋', '2', 'lock_zone_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(232, 3, '楼层', '3', 'lock_zone_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(233, 4, '其他', '4', 'lock_zone_type', '', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 生物特征类型
(240, 1, '人脸', '1', 'lock_bio_type', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(241, 2, '指纹', '2', 'lock_bio_type', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 授权方式
(250, 1, '常驻', '1', 'lock_grant_mode', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(251, 2, '时段', '2', 'lock_grant_mode', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
(252, 3, '一次性', '3', 'lock_grant_mode', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 凭证来源
(260, 1, '平台生成', '0', 'lock_cred_source', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(261, 2, '设备学习', '1', 'lock_cred_source', '', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'learn'),
(262, 3, '导入', '2', 'lock_cred_source', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, ''),
-- 命令路由范围
(270, 1, '分组', '1', 'lock_route_scope', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '主题第2段为分区号'),
(271, 2, '网关', '2', 'lock_route_scope', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '主题第2段为网关SN'),
-- 协议信封形态
(280, 1, '完整信封V1', '1', 'lock_envelope_ver', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '含 ac/mode/chksum'),
(281, 2, '精简信封V2', '2', 'lock_envelope_ver', '', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '含 devVer/protocalVer'),
(282, 3, '无信封', '3', 'lock_envelope_ver', '', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '仅 devId/seq/op/params'),
-- 门锁绑定状态(locklist 下发状态)
(290, 1, '待下发', '0', 'lock_bind_status', '', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, ''),
(291, 2, '已下发', '1', 'lock_bind_status', '', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '网关已接收 locklist'),
(292, 3, '下发失败', '2', 'lock_bind_status', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '');
-- =========================================================================
-- 15、协议配置种子数据
-- 单厂家阶段只有 ydlink 一条;新增厂家时插一行即可,不新增配置项。
-- =========================================================================
delete from lock_protocol_profile where profile_code = 'ydlink-v1';
insert into lock_protocol_profile
(profile_id, profile_code, profile_name, vendor, topic_prefix, resource_segment,
envelope_ver, protocol_ver, result_code_map, param_templates, enabled, status,
create_by, create_time, remark)
values
(1, 'ydlink-v1', 'ydlink 厂家协议(V1/V2 并存)', 'ydlink', 'ydlink', 'thing',
'1', '1',
'{"0":"SUCCESS","10":"GATEWAY_RECEIVED","11":"BUSY","12":"NOT_FOUND"}',
'{"password.add":{"requestSize":7,"resultIndex":2},"password.delete":{"requestSize":5,"resultIndex":2},"iccard.add":{"requestSize":7,"resultIndex":2},"iccard.delete":{"requestSize":5,"resultIndex":2},"facefeature.add":{"requestSize":8,"resultIndex":2},"facefeature.delete":{"requestSize":4,"resultIndex":2},"locklist":{"requestSize":-1,"resultIndex":1},"config":{"requestSize":-1,"resultIndex":1},"opencloselock":{"requestSize":-1,"resultIndex":5}}',
'0', '0', 'admin', CURRENT_TIMESTAMP, '参数模板随协议整理 §6 维护,改配置不改代码');
-- =========================================================================
-- 16、菜单初始化
-- 沿用 v1 菜单(人脸/授权口径),待人员域前后端改造时统一为
-- 人员/凭证/授权三模块。此处不改动,避免与现有控制器权限不一致。
-- =========================================================================
delete from sys_menu where perms like 'lock:%';
delete from sys_menu where menu_name = '门禁管理' and parent_id = 0;
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) values
(2000, '门禁管理', 0, 4, 'iot', null, '', '', 1, 0, 'M', '0', '0', '', 'lock', 'admin', CURRENT_TIMESTAMP, '公寓人脸门禁系统目录');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) values
(2010, '公寓分组', 2000, 1, 'group', 'iot/group/index', '', '', 1, 0, 'C', '0', '0', 'lock:group:list', 'tree', 'admin', CURRENT_TIMESTAMP, '公寓分组菜单'),
(2020, '门锁网关', 2000, 2, 'gateway', 'iot/gateway/index', '', '', 1, 0, 'C', '0', '0', 'lock:gateway:list', 'server', 'admin', CURRENT_TIMESTAMP, '门锁网关菜单'),
(2030, '智能门锁', 2000, 3, 'device', 'iot/device/index', '', '', 1, 0, 'C', '0', '0', 'lock:device:list', 'lock', 'admin', CURRENT_TIMESTAMP, '智能门锁菜单'),
(2040, '人脸数据', 2000, 4, 'face', 'iot/face/index', '', '', 1, 0, 'C', '0', '0', 'lock:face:list', 'peoples', 'admin', CURRENT_TIMESTAMP, '人脸数据菜单'),
(2050, '授权管理', 2000, 5, 'auth', 'iot/auth/index', '', '', 1, 0, 'C', '0', '0', 'lock:auth:list', 'lock', 'admin', CURRENT_TIMESTAMP, '授权管理菜单'),
(2060, '通行记录', 2000, 6, 'accesslog', 'iot/accesslog/index','', '', 1, 0, 'C', '0', '0', 'lock:accesslog:list','list', 'admin', CURRENT_TIMESTAMP, '通行记录菜单'),
(2070, '命令记录', 2000, 7, 'command', 'iot/command/index', '', '', 1, 0, 'C', '0', '0', 'lock:command:list', 'job', 'admin', CURRENT_TIMESTAMP, '命令记录菜单'),
(2080, '报文日志', 2000, 8, 'mqttlog', 'iot/mqttlog/index', '', '', 1, 0, 'C', '0', '0', 'lock:mqttlog:list', 'log', 'admin', CURRENT_TIMESTAMP, '报文日志菜单'),
(2090, '系统参数', 2000, 9, 'config', 'iot/config/index', '', '', 1, 0, 'C', '0', '0', 'lock:config:list', 'edit', 'admin', CURRENT_TIMESTAMP, '系统参数菜单');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) values
(2011, '分组查询', 2010, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:group:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2012, '分组新增', 2010, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:group:add', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2013, '分组修改', 2010, 3, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:group:edit', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2014, '分组删除', 2010, 4, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:group:remove', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2015, '分组导出', 2010, 5, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:group:export', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2021, '网关查询', 2020, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2022, '网关新增', 2020, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:add', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2023, '网关修改', 2020, 3, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:edit', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2024, '网关删除', 2020, 4, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:remove', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2025, '网关导出', 2020, 5, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:export', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2026, '网关配置下发', 2020, 6, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:config', '#', 'admin', CURRENT_TIMESTAMP, '修改网络/LORA/MQTT参数与保存配置'),
(2027, '网关重启', 2020, 7, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:reboot', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2028, '网关绑定门锁', 2020, 8, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:gateway:bind', '#', 'admin', CURRENT_TIMESTAMP, '绑定/清除门锁'),
(2031, '门锁查询', 2030, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2032, '门锁新增', 2030, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:add', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2033, '门锁修改', 2030, 3, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:edit', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2034, '门锁删除', 2030, 4, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:remove', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2035, '门锁导出', 2030, 5, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:export', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2036, '绑定/解绑网关',2030, 6, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:bind', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2037, '时间同步', 2030, 7, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:sync', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2038, '远程开锁', 2030, 8, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:device:open', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2041, '人脸查询', 2040, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:face:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2042, '人脸新增', 2040, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:face:add', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2043, '人脸修改', 2040, 3, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:face:edit', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2044, '人脸删除', 2040, 4, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:face:remove', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2045, '人脸导出', 2040, 5, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:face:export', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2046, '特征提取', 2040, 6, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:face:extract', '#', 'admin', CURRENT_TIMESTAMP, '调用人脸特征提取服务'),
(2051, '授权查询', 2050, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:auth:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2052, '授权新增', 2050, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:auth:add', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2053, '授权修改', 2050, 3, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:auth:edit', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2054, '授权删除', 2050, 4, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:auth:remove', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2055, '授权导出', 2050, 5, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:auth:export', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2056, '授权下发', 2050, 6, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:auth:push', '#', 'admin', CURRENT_TIMESTAMP, '向设备下发授权'),
(2057, '清除授权', 2050, 7, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:auth:clear', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2061, '记录查询', 2060, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:accesslog:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2062, '记录导出', 2060, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:accesslog:export', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2071, '命令查询', 2070, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:command:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2072, '命令重发', 2070, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:command:retry', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2081, '报文查询', 2080, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:mqttlog:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2091, '参数查询', 2090, 1, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:config:query', '#', 'admin', CURRENT_TIMESTAMP, ''),
(2092, '参数修改', 2090, 2, '', null, '', '', 1, 0, 'F', '0', '0', 'lock:config:edit', '#', 'admin', CURRENT_TIMESTAMP, '');
-- =========================================================================
-- 脚本结束
-- =========================================================================
-- =========================================================================
-- 附录 A:存量升级(从 v1 的 9 张表到 v2 的 13 张表)
--
-- ⚠️ 仅在已上线的 v1 库执行,且【不要】与上面的基线部分放在一起跑。
-- 使用方式:整段复制出来,逐节执行,每节之间核对行数。
-- 执行前务必:mysqldump -u root -p iotlocks > iotlocks_before_v2.sql
--
-- 顺序:A1 补列 → A1.5 归属表(建表+回填)→ A2 其余新增表(复制基线 5/6/7/13 节)
-- → A3 人员迁移 → A4 特征迁移 → A5 凭证迁移(需应用侧加密)
-- → A6 授权迁移 → A7 命令/报文列迁移 → A8 旧表保留与最终下线
-- =========================================================================
-- A1 设备域补列(lock_group / lock_gateway / lock_device)
-- alter table lock_group
-- add column parent_id bigint(20) default 0 comment '上级分区ID(0为顶级)' after group_name,
-- add column ancestors varchar(500) default '' comment '祖级列表' after parent_id,
-- add column zone_type char(1) default '4' comment '分区类型(1园区 2楼栋 3楼层 4其他)' after ancestors,
-- add column lock_capacity int(11) default 0 comment '设计门锁容量' after config_id;
--
-- -- 先补齐空 group_id 再改非空,否则会失败;补不上的说明网关没入分区,需人工处理
-- -- select gateway_id, gateway_sn from lock_gateway where group_id is null;
-- alter table lock_gateway
-- add column gateway_mac varchar(32) default null comment '网关MAC(12位hex)' after gateway_sn,
-- add column protocol_profile_id bigint(20) default null comment '协议配置ID' after config_id,
-- add column max_device int(11) default 16 comment '最大下挂门锁数' after device_count,
-- modify column group_id bigint(20) not null comment '所属分区ID(必填)',
-- add unique key uk_lock_gateway_mac (gateway_mac);
--
-- alter table lock_device
-- add column device_mac varchar(32) default null comment '门锁MAC(12位hex)' after device_sn,
-- add column protocol_profile_id bigint(20) default null comment '协议配置ID' after face_algo_version,
-- add column capacity_face int(11) default 0 comment '人脸容量' after protocol_profile_id,
-- add column capacity_pwd int(11) default 0 comment '密码容量' after capacity_face,
-- add column capacity_card int(11) default 0 comment '卡片容量' after capacity_pwd,
-- add column door_state char(1) default null comment '门状态' after battery_level,
-- add column deadbolt_state char(1) default null comment '反锁/取电状态' after door_state,
-- add column door_sensor_state char(1) default null comment '门磁状态' after deadbolt_state,
-- add column raw_state varchar(64) default null comment '状态位原值' after door_sensor_state,
-- add unique key uk_lock_device_mac (device_mac);
--
-- -- 冗余列在 A1.5 关系表回填完成、代码切换到关系表之后再删(见 A8)
-- -- alter table lock_device drop key idx_lock_device_group, drop column group_id;
-- -- 密码列扩长(AES-GCM 密文比明文长)
-- alter table lock_gateway modify column gateway_password varchar(256) default null comment '网关密码(密文)';
-- alter table lock_device modify column device_password varchar(256) default null comment '门锁密码(密文)';
-- A1.5 多网关归属:v1 的 lock_device.gateway_id 只能表达单一归属,
-- 先建关联表并回填,再把 gateway_id 改名为 primary_gateway_id(选路缓存)。
-- 回填后每个锁只有一条关系;现场若已存在「一锁多网关」,由实施人员按实际
-- locklist 补录,不要依赖自动推导。
-- -- 建表:复制基线第 4 节
-- insert into lock_device_gateway
-- (device_id, gateway_id, bind_status, bind_time, online_status, status, del_flag,
-- create_by, create_time, remark)
-- select d.device_id, d.gateway_id, '1', d.bind_time, '2', '0', '0',
-- 'migrate', now(), 'v1 lock_device.gateway_id 迁移'
-- from lock_device d
-- where d.gateway_id is not null;
-- -- 核对:两边行数应相等
-- -- select (select count(*) from lock_device where gateway_id is not null) as v1_cnt,
-- -- (select count(*) from lock_device_gateway) as v2_cnt;
-- alter table lock_device
-- change column gateway_id primary_gateway_id bigint(20) default null
-- comment '当前主控网关ID(选路缓存,事实源为 lock_device_gateway)';
-- alter table lock_device rename index idx_lock_device_gateway to idx_lock_device_primary_gw;
-- -- 网关侧计数同步:device_count 由 lock_device_gateway 重算
-- update lock_gateway g
-- set device_count = (select count(*) from lock_device_gateway r where r.gateway_id = g.gateway_id);
-- A2 其余新增表:lock_person / lock_biometric / lock_credential / lock_protocol_profile
-- 执行基线第 5、6、7、13 节的 create table 语句(这三节的 drop 只针对自身表名)。
-- A3 人员迁移:lock_face 中的人员维度按 person_no 去重
-- insert into lock_person (person_no, person_name, person_type, phone, status, del_flag,
-- create_by, create_time, update_by, update_time, remark)
-- select f.person_no, max(f.person_name), max(f.person_type), max(f.phone),
-- max(f.status), max(f.del_flag), 'migrate', now(), 'migrate', now(), 'v1 lock_face 迁移'
-- from lock_face f
-- where f.person_no is not null and f.person_no <> ''
-- group by f.person_no;
-- -- 核对:应等于 lock_face 中不同的 person_no 数量
-- -- select count(distinct person_no) from lock_face;
-- A4 特征迁移:一人一版本一份(同一 person_no 多行时按 feature_algo_version 取最新)
-- feature_data 此时仍是明文 Base64,迁移后由应用侧批量重加密并改写该列。
-- insert into lock_biometric (person_id, bio_type, algo_version, photo_url, photo_md5,
-- feature_data, feature_md5, feature_size, extract_status,
-- extract_time, extract_msg, is_primary, status, del_flag,
-- create_by, create_time, remark)
-- select p.person_id, '1', ifnull(f.feature_algo_version, ''), f.photo_url, f.photo_md5,
-- f.feature_data, f.feature_md5, ifnull(f.feature_size, 0), f.extract_status,
-- f.extract_time, f.extract_msg, '1', f.status, f.del_flag,
-- 'migrate', now(), 'v1 lock_face 迁移,待重加密'
-- from lock_face f
-- join lock_person p on p.person_no = f.person_no
-- where f.face_id = (select max(x.face_id) from lock_face x where x.person_no = f.person_no);
-- A5 凭证迁移(lock_auth.auth_value → lock_credential)
-- 密码/卡号必须由应用侧加密后再写 cred_value_cipher,SQL 侧不做明文搬运。
-- 建议:先只迁人脸与指纹(值为特征引用),密码/卡走一次性迁移工具。
-- A6 授权迁移(lock_auth 关系部分 → lock_grant)
-- insert into lock_grant (grant_no, person_id, person_no, cred_id, cred_type,
-- device_id, device_sn, gateway_id, group_id, grant_mode,
-- begin_time, end_time, sync_status, sync_msg, retry_count,
-- last_sync_time, status, del_flag, create_by, create_time, remark)
-- select a.auth_no, p.person_id, a.person_no,
-- c.cred_id, a.auth_type,
-- a.device_id, a.device_sn, a.gateway_id, a.group_id, '1',
-- a.begin_time, a.end_time, a.sync_status, a.sync_msg, ifnull(a.retry_count, 0),
-- a.sync_time, a.status, a.del_flag, 'migrate', now(), 'v1 lock_auth 迁移'
-- from lock_auth a
-- join lock_person p on p.person_no = a.person_no
-- join lock_credential c on c.person_id = p.person_id and c.cred_type = a.auth_type
-- where a.auth_type in ('01', '03');
-- -- 注意:sync_status 编码在 v2 有变化(4 由"已清除"改为"已撤销"、新增 5 已过期),
-- -- 4 的历史数据按 4 保留即可,语义接近。
-- A7 命令与报文表改列
-- alter table lock_command
-- add column route_scope char(1) default null comment '路由范围(1分组 2网关)' after command_type,
-- add column route_code varchar(32) default null comment '路由编号(分区号/网关SN)' after route_scope,
-- add column biz_type varchar(16) default null comment '业务类型' after target_id,
-- add column result_code varchar(8) default null comment '归一结果码' after command_status,
-- add column timeout_at datetime comment '超时截止时间' after next_retry_time,
-- add column idempotency_key varchar(64) default null comment '幂等键' after timeout_at,
-- add key idx_lock_command_timeout (command_status, timeout_at),
-- add key idx_lock_command_route (route_scope, route_code),
-- add key idx_lock_command_idem (idempotency_key);
-- -- 回填路由:网关类命令取 target_code;门锁类命令按 lock_device → lock_gateway → lock_group 推导
-- update lock_command c
-- set route_scope = if(c.target_type = '1', '2', '1'),
-- route_code = if(c.target_type = '1', c.target_code,
-- (select g.group_code from lock_device d
-- join lock_gateway gw on gw.gateway_id = d.gateway_id
-- join lock_group g on g.group_id = gw.group_id
-- where d.device_id = c.target_id))
-- where c.route_code is null;
-- -- 回填 biz_type:0011/0012 → GRANT,0001/0002/0013 → DEVICE,0006-0010 → GATEWAY
-- -- 命令状态重排(2 成功→3、3 失败→4、4 超时→5、5 取消→6),必须从大到小改,避免撞值
-- update lock_command set command_status = '6' where command_status = '5';
-- update lock_command set command_status = '5' where command_status = '4';
-- update lock_command set command_status = '4' where command_status = '3';
-- update lock_command set command_status = '3' where command_status = '2';
--
-- alter table lock_mqtt_log
-- add column route_scope char(1) default null comment '主题第2段类型(1分组 2网关)' after topic,
-- add column route_code varchar(32) default null comment '主题第2段取值' after route_scope,
-- add column protocol_ver char(1) default null comment '业务协议版本' after seq,
-- add column envelope_ver char(1) default null comment '信封形态' after protocol_ver;
-- update lock_mqtt_log set route_code = ifnull(group_code, gateway_sn),
-- route_scope = if(group_code is not null, '1', '2')
-- where route_code is null;
-- -- 列删除放在代码切换之后,避免旧版本回滚时缺列
-- -- alter table lock_mqtt_log drop column group_code, drop column gateway_sn;
-- A8 旧表下线(至少保留一个版本周期,确认迁移数据与线上一致后执行)
-- rename table lock_face to lock_face_bak_20261030;
-- rename table lock_auth to lock_auth_bak_20261030;
-- -- 观察期结束后:
-- -- drop table lock_face_bak_20261030;
-- -- drop table lock_auth_bak_20261030;