-- ============================================================================== -- 脚本名称: 008_external_resource_operation.sql -- 业务模块: DMS/外部物联网集成 - 外部资源映射与操作流水持久化模型 (R.3B Wave 1) -- 适用数据库: KingbaseES V8 / V9 (PostgreSQL 方言兼容) -- 安全编号: 008 (001-003 基线, 004-005 R.2分支占用, 006 Wave0, 007 保留禁止执行) -- 幂等性设计: 支持重复安全执行,纯增量结构,不含破坏性语句 -- ============================================================================== -- 1. 创建外部资源映射表 train_external_resource_mapping CREATE TABLE IF NOT EXISTS train_external_resource_mapping ( mapping_id BIGSERIAL PRIMARY KEY, dorm_id VARCHAR(32) NOT NULL, vendor_code VARCHAR(32) NOT NULL DEFAULT 'DMS', local_resource_type VARCHAR(32) NOT NULL, external_resource_type VARCHAR(32) NOT NULL, external_resource_id VARCHAR(64) NOT NULL, external_parent_id VARCHAR(64), external_hardware_id VARCHAR(64), external_project_id VARCHAR(64), is_primary SMALLINT NOT NULL DEFAULT 1, mapping_status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', last_sync_time TIMESTAMP, create_by VARCHAR(64) DEFAULT '', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64) DEFAULT '', update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), CONSTRAINT fk_ext_res_map_dorm FOREIGN KEY (dorm_id) REFERENCES train_dormitory_info(dorm_id) ); COMMENT ON TABLE train_external_resource_mapping IS '外部资源映射关联表(支持一房多锁与多供应商适配)'; COMMENT ON COLUMN train_external_resource_mapping.mapping_id IS '映射ID(主键)'; COMMENT ON COLUMN train_external_resource_mapping.dorm_id IS '本地宿舍ID(外键)'; COMMENT ON COLUMN train_external_resource_mapping.vendor_code IS '外部供应商标识(如 DMS)'; COMMENT ON COLUMN train_external_resource_mapping.local_resource_type IS '本地资源类型(DORM_ROOM, DOOR_LOCK)'; COMMENT ON COLUMN train_external_resource_mapping.external_resource_type IS '外部资源类型(ROOM, DOOR_LOCK)'; COMMENT ON COLUMN train_external_resource_mapping.external_resource_id IS '外部系统资源全局主键(如 placeId, lockId)'; COMMENT ON COLUMN train_external_resource_mapping.external_parent_id IS '外部父级资源标识(如 building/floor placeId)'; COMMENT ON COLUMN train_external_resource_mapping.external_hardware_id IS '外部硬件标识/SN(如门锁 hardwareId)'; COMMENT ON COLUMN train_external_resource_mapping.external_project_id IS '外部项目/租户站点ID(DMS projectId)'; COMMENT ON COLUMN train_external_resource_mapping.is_primary IS '是否主门锁/主资源(1是, 0否)'; COMMENT ON COLUMN train_external_resource_mapping.mapping_status IS '映射状态(ACTIVE有效, INACTIVE失效)'; COMMENT ON COLUMN train_external_resource_mapping.last_sync_time IS '最近同步元数据时间'; -- 1.1 约束与索引:同一宿舍同一厂商最多1个 ACTIVE 房间映射 (局部唯一索引) CREATE UNIQUE INDEX IF NOT EXISTS uq_ext_res_map_active_room ON train_external_resource_mapping (vendor_code, dorm_id) WHERE external_resource_type = 'ROOM' AND mapping_status = 'ACTIVE'; -- 1.2 约束与索引:同一外部资源在同一厂商下不能同时 ACTIVE 绑定到多个本地房间 (局部唯一索引) CREATE UNIQUE INDEX IF NOT EXISTS uq_ext_res_map_active_ext_res ON train_external_resource_mapping (vendor_code, external_resource_type, external_resource_id) WHERE mapping_status = 'ACTIVE'; -- 1.3 常规外键与查询索引 CREATE INDEX IF NOT EXISTS idx_ext_res_map_dorm ON train_external_resource_mapping (dorm_id); CREATE INDEX IF NOT EXISTS idx_ext_res_map_ext_res ON train_external_resource_mapping (vendor_code, external_resource_type, external_resource_id); -- 2. 创建外部操作流水表 train_external_operation CREATE TABLE IF NOT EXISTS train_external_operation ( operation_id BIGSERIAL PRIMARY KEY, door_auth_id BIGINT NOT NULL, business_type VARCHAR(32) NOT NULL DEFAULT 'DOOR_AUTH', vendor_code VARCHAR(32) NOT NULL DEFAULT 'DMS', operation_type VARCHAR(32) NOT NULL, operation_status VARCHAR(20) NOT NULL DEFAULT 'CREATED', idempotency_key VARCHAR(128) NOT NULL, external_operation_id VARCHAR(64), external_request_type INTEGER, external_hardware_id VARCHAR(64), external_key_id VARCHAR(64), attempt_count INTEGER NOT NULL DEFAULT 0, next_poll_time TIMESTAMP, requested_at TIMESTAMP, completed_at TIMESTAMP, last_error_code VARCHAR(64), last_error_message VARCHAR(500), create_by VARCHAR(64) DEFAULT '', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64) DEFAULT '', update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), CONSTRAINT fk_ext_op_door_auth FOREIGN KEY (door_auth_id) REFERENCES train_dorm_auth(auth_id) ); COMMENT ON TABLE train_external_operation IS '外部设备操作流水与异步轮询对账表(中立模型,无明文敏感数据)'; COMMENT ON COLUMN train_external_operation.operation_id IS '本地操作流水ID(主键)'; COMMENT ON COLUMN train_external_operation.door_auth_id IS '关联门禁授权主记录ID(外键)'; COMMENT ON COLUMN train_external_operation.business_type IS '业务类型(默认 DOOR_AUTH)'; COMMENT ON COLUMN train_external_operation.vendor_code IS '外部供应商标识(如 DMS)'; COMMENT ON COLUMN train_external_operation.operation_type IS '操作类型(GRANT下发, REVOKE注销, STATUS_QUERY查询)'; COMMENT ON COLUMN train_external_operation.operation_status IS '操作状态(CREATED, REQUESTED, ACCEPTED, PENDING, RETRY_WAIT, SUCCESS, FAILED, TIMEOUT)'; COMMENT ON COLUMN train_external_operation.idempotency_key IS '本地全局幂等键(防重复调用)'; COMMENT ON COLUMN train_external_operation.external_operation_id IS '外部返回的操作流水ID(如 DMS operationId,初始为NULL)'; COMMENT ON COLUMN train_external_operation.external_request_type IS '外部请求类型数字码(如 1下发密码, 3删除密码)'; COMMENT ON COLUMN train_external_operation.external_hardware_id IS '外部硬件标识/SN'; COMMENT ON COLUMN train_external_operation.external_key_id IS '外部凭证钥匙ID(如 DMS keyId)'; COMMENT ON COLUMN train_external_operation.attempt_count IS '尝试/轮询次数'; COMMENT ON COLUMN train_external_operation.next_poll_time IS '下次轮询执行时间戳'; COMMENT ON COLUMN train_external_operation.requested_at IS '向外部发出请求的时间'; COMMENT ON COLUMN train_external_operation.completed_at IS '操作终态达成时间'; COMMENT ON COLUMN train_external_operation.last_error_code IS '最后一次错误编码'; COMMENT ON COLUMN train_external_operation.last_error_message IS '最后一次错误描述'; -- 2.1 约束与索引:幂等键全局唯一约束 CREATE UNIQUE INDEX IF NOT EXISTS uq_ext_op_idempotency ON train_external_operation (idempotency_key); -- 2.2 约束与索引:门禁授权外键索引与外部操作ID索引 CREATE INDEX IF NOT EXISTS idx_ext_op_door_auth ON train_external_operation (door_auth_id); CREATE INDEX IF NOT EXISTS idx_ext_op_ext_id ON train_external_operation (vendor_code, external_operation_id); -- 2.3 约束与索引:待轮询操作快速检索组合索引 CREATE INDEX IF NOT EXISTS idx_ext_op_poll ON train_external_operation (operation_status, next_poll_time);