-- ============================================================================= -- pbl_blueprint M1b DDL —— 模板平台公共部分 / 子对象扩展 / 关联表判定 -- ============================================================================= -- 覆盖 4 张表(与 pbl_blueprint/models/m1b/*.json 四段式表模型一一对应): -- 1) pbl_blueprint_template 模板表(tenant_id NULL = 平台公共模板) -- 2) pbl_ext_field_def 子对象扩展字段定义表(元数据字典,fail-closed 校验依据) -- 3) pbl_subobject_ext 子对象扩展值表(EAV,一行一键值) -- 4) pbl_blueprint_ref 蓝图外部关联表(Q-OPEN-3 裁决物理落地) -- -- 设计铁律: -- * 全部 CREATE TABLE IF NOT EXISTS / CREATE INDEX 幂等,可重复执行(build.sh 与 -- m1b_init.ensure_m1b_tables 双入口均执行本文件); -- * 无 FOREIGN KEY(跨模块/可能跨库,有效性由 m1b_ref.resolve_refs() 主动判定); -- * 无 ENUM(一律 varchar + 字典 codes,见 models/m1b/*.json 的 codes 段); -- * 无 TIMESTAMP(一律 datetime,避免 2038 与隐式时区转换); -- * 租户列索引打头:所有业务索引首列为 tenant_key(= COALESCE(tenant_id,'__PLATFORM__')) -- 或 tenant_id;唯一键以 tenant_key 打头,规避 MariaDB 唯一索引不对 NULL 去重 -- 导致平台公共行(tenant_id IS NULL)可重复插入的问题; -- * Q-OPEN-3:本文件**不含**任何针对 world / scene / entity / script 等复用域基表的 -- CREATE / ALTER / UPDATE / DELETE 语句——蓝图对外部对象的引用单向落在 -- pbl_blueprint_ref,ref_mode 恒为 'readonly'。 -- ============================================================================= SET NAMES utf8mb4; -- ----------------------------------------------------------------------------- -- 1) pbl_blueprint_template —— PBL 蓝图模板表(M1b) -- tenant_id IS NULL 且 platform_flag='Y' → 平台公共模板,对全部租户可见 -- tenant_id 非空 → 租户私有模板 -- 模板升级 append 新版本行、不覆盖旧版本(Q-OPEN-9) -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_blueprint_template` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '物理主键 AUTO_INCREMENT', `tenant_key` varchar(32) NOT NULL COMMENT '= COALESCE(tenant_id,''__PLATFORM__'');索引打头列;应用层计算,不接受外部传入', `tenant_id` varchar(32) DEFAULT NULL COMMENT 'NULL = 平台公共模板(M1b 模板平台公共部分);非空 = 租户私有模板', `template_code` varchar(32) NOT NULL COMMENT '业务主键,pbl_common.gen_code(''TPL'', tenant_id) 生成', `template_version` int NOT NULL DEFAULT 1 COMMENT '模板版本,升级 append 新版本不覆盖旧行', `template_name` varchar(128) NOT NULL COMMENT '模板名称', `subject` varchar(32) DEFAULT NULL COMMENT '学科,字典 pbl_subject', `grade` varchar(16) DEFAULT NULL COMMENT '学段,字典 pbl_grade', `category` varchar(32) DEFAULT NULL COMMENT '模板分类,字典 pbl_tpl_category', `tpl_json` longtext NOT NULL COMMENT '7 类子对象数组 + 模板内临时 ID 父子引用', `tpl_hash` varchar(64) NOT NULL COMMENT 'sha256(canonical_json(tpl_json)),确定性实例化校验', `offline_flag` varchar(1) NOT NULL DEFAULT 'N' COMMENT 'Y=build.sh 种子落库的内置离线兜底模板', `platform_flag` varchar(1) NOT NULL DEFAULT 'N' COMMENT 'Y=平台公共模板(此时 tenant_id 必须为 NULL);字典 pbl_yes_no', `ref_policy` varchar(16) NOT NULL DEFAULT 'warn' COMMENT 'strict=引用缺失阻断回滚;warn=落库并标记 ref_unresolved(Q-OPEN-4);字典 pbl_ref_policy', `tpl_status` varchar(16) NOT NULL DEFAULT 'active' COMMENT '模板状态,字典 pbl_tpl_status', `usage_count` int NOT NULL DEFAULT 0 COMMENT '实例化次数(供 M10 分析,非权威计数)', `remark` varchar(512) DEFAULT NULL COMMENT '备注', `creator_id` varchar(32) DEFAULT NULL COMMENT '创建人 rbac 用户ID', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tpl_code_ver` (`tenant_key`, `template_code`, `template_version`), KEY `idx_tpl_status` (`tenant_key`, `tpl_status`, `subject`, `grade`), KEY `idx_tpl_offline` (`tenant_key`, `offline_flag`), KEY `idx_tpl_hash` (`tenant_key`, `tpl_hash`), KEY `idx_tpl_platform` (`platform_flag`, `tpl_status`), KEY `idx_tpl_tenant` (`tenant_id`, `tpl_status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='PBL 蓝图模板表(M1b,含平台公共模板 tenant_id NULL)'; -- ----------------------------------------------------------------------------- -- 2) pbl_ext_field_def —— PBL 子对象扩展字段定义表(M1b) -- 扩展字段元数据字典:某类子对象允许挂哪些扩展键、类型、必填、默认值、 -- 枚举候选、长度上限、正则校验。 -- m1b_subobject.set_ext / bulk_set_ext 写 pbl_subobject_ext 前必须先按本表 -- fail-closed 校验:未定义键 → PBL_EXT_FIELD_UNDEFINED; -- 类型/长度/枚举/正则不符 → PBL_EXT_VALUE_INVALID。 -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_ext_field_def` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '物理主键 AUTO_INCREMENT', `tenant_key` varchar(32) NOT NULL COMMENT '= COALESCE(tenant_id,''__PLATFORM__'');索引打头列;应用层计算', `tenant_id` varchar(32) DEFAULT NULL COMMENT 'NULL = 平台内置字段定义;非空 = 租户自有字段定义', `def_code` varchar(32) NOT NULL COMMENT '业务主键,pbl_common.gen_code(''EFD'', tenant_id) 生成', `subobject_type` varchar(32) NOT NULL COMMENT '字典 pbl_subobject_type 的 7 类之一;''*'' 表示全部类型通用', `field_key` varchar(64) NOT NULL COMMENT '写入 pbl_subobject_ext.ext_key 的键名;^[a-z][a-z0-9_]{0,63}$', `field_label` varchar(128) NOT NULL COMMENT '字段显示名', `value_type` varchar(16) NOT NULL DEFAULT 'string' COMMENT '字典 pbl_ext_value_type:string/int/float/bool/json/date/enum', `required_flag` varchar(1) NOT NULL DEFAULT 'N' COMMENT 'Y=保存该子对象扩展时此键必须出现且非空;字典 pbl_yes_no', `default_value` varchar(512) DEFAULT NULL COMMENT '调用方未传该键时按 value_type 强转后落库', `enum_options` text DEFAULT NULL COMMENT 'value_type=enum 时必填,JSON 数组字符串,如 ["easy","normal","hard"]', `max_length` int NOT NULL DEFAULT 0 COMMENT '0=不限制;>0 时对 string/json/enum 序列化长度做上限校验', `validate_regex` varchar(256) DEFAULT NULL COMMENT '非空时对 string 值做 re.fullmatch 校验,不符则 PBL_EXT_VALUE_INVALID', `platform_flag` varchar(1) NOT NULL DEFAULT 'N' COMMENT 'Y=平台内置定义(此时 tenant_id 必须为 NULL);字典 pbl_yes_no', `def_status` varchar(16) NOT NULL DEFAULT 'active' COMMENT '字典 pbl_ext_def_status;disabled 不参与校验且拒绝新值写入(历史值只读保留)', `sort_no` int NOT NULL DEFAULT 0 COMMENT '排序号', `remark` varchar(512) DEFAULT NULL COMMENT '备注', `creator_id` varchar(32) DEFAULT NULL COMMENT '创建人', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_def_code` (`tenant_key`, `def_code`), UNIQUE KEY `uk_def_type_key` (`tenant_key`, `subobject_type`, `field_key`), KEY `idx_def_lookup` (`tenant_key`, `def_status`, `subobject_type`, `sort_no`), KEY `idx_def_platform` (`platform_flag`, `def_status`), KEY `idx_def_tenant` (`tenant_id`, `subobject_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='PBL 子对象扩展字段定义表(M1b,扩展字段元数据字典)'; -- ----------------------------------------------------------------------------- -- 3) pbl_subobject_ext —— PBL 子对象扩展表(M1b,EAV) -- M1a 的 pbl_bp_subobject 契约已冻结,M1b 零改动 M1a 基表, -- 扩展属性一律外挂到本表(M1b-annex §7 对 M1a 在途代码零改动要求)。 -- owner_kind=blueprint → subobject_ref 取 M1a subobject_id; -- owner_kind=template → subobject_ref 取 tpl_json 内模板临时 ID, -- 实例化时由 m1b_subobject.remap_subobject_ref 重映射。 -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_subobject_ext` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '物理主键 AUTO_INCREMENT', `tenant_key` varchar(32) NOT NULL COMMENT '= COALESCE(tenant_id,''__PLATFORM__'');索引打头列;应用层计算', `tenant_id` varchar(32) DEFAULT NULL COMMENT 'NULL = 平台公共模板的子对象扩展;非空 = 租户数据', `ext_code` varchar(32) NOT NULL COMMENT '业务主键,pbl_common.gen_code(''SXE'', tenant_id) 生成', `owner_kind` varchar(16) NOT NULL COMMENT '字典 pbl_ext_owner_kind:blueprint / template', `owner_id` varchar(32) NOT NULL COMMENT 'owner_kind=blueprint 时为 blueprint_id;=template 时为 template_code', `owner_version` int NOT NULL DEFAULT 1 COMMENT 'blueprint→蓝图版本号;template→template_version;版本隔离避免跨版本串数据', `subobject_type` varchar(32) NOT NULL COMMENT '字典 pbl_subobject_type 的 7 类之一', `subobject_ref` varchar(64) NOT NULL COMMENT '非空归一列:blueprint→M1A subobject_id;template→模板内临时 ID。参与唯一键,杜绝 NULL 造成重复行', `subobject_id` varchar(32) DEFAULT NULL COMMENT '冗余列,owner_kind=blueprint 时与 subobject_ref 同值,便于按 M1A 子对象直查', `ext_key` varchar(64) NOT NULL COMMENT '必须在 pbl_ext_field_def 中存在且 def_status=active', `ext_value` longtext DEFAULT NULL COMMENT '统一字符串形态;json/bool/date 按 value_type 序列化', `value_type` varchar(16) NOT NULL DEFAULT 'string' COMMENT '写入时从 pbl_ext_field_def.value_type 快照,保证历史值可按当时类型解读;字典 pbl_ext_value_type', `field_def_id` bigint DEFAULT NULL COMMENT '指向 pbl_ext_field_def.id(逻辑关联,无 FK)', `sort_no` int NOT NULL DEFAULT 0 COMMENT '排序号', `creator_id` varchar(32) DEFAULT NULL COMMENT '创建人', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_ext_code` (`tenant_key`, `ext_code`), UNIQUE KEY `uk_ext_slot` (`tenant_key`, `owner_kind`, `owner_id`, `owner_version`, `subobject_type`, `subobject_ref`, `ext_key`), KEY `idx_ext_owner` (`tenant_key`, `owner_kind`, `owner_id`, `owner_version`, `subobject_type`), KEY `idx_ext_subobject` (`tenant_key`, `subobject_id`), KEY `idx_ext_key` (`tenant_key`, `ext_key`), KEY `idx_ext_tenant` (`tenant_id`, `owner_kind`, `owner_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='PBL 子对象扩展表(M1b,EAV 一行一键值)'; -- ----------------------------------------------------------------------------- -- 4) pbl_blueprint_ref —— PBL 蓝图外部关联表(M1b,Q-OPEN-3 裁决物理落地) -- 裁决结论:『不改 world 表』(含 scene / entity / script 等复用域基表一律 -- 零改动、零加列、零回写)。蓝图与复用域对象的全部关联单向落在本表: -- * ref_target_code 指向外部对象业务编码(逻辑关联,无 FOREIGN KEY); -- * ref_mode 恒为 'readonly',由 m1b_ref.assert_writable() 与 -- WRITE_PROTECTED_TABLES 双重闸门强制(任何针对 world/scene/entity/script -- 的 INSERT/UPDATE/DELETE 在 SQL 执行前被拦截并抛 PBL_DB_WRITE_PROTECTED); -- * resolve_refs() 仅做只读存在性校验并把结果写回本表 ref_status -- (resolved / unresolved / invalid),校验策略 strict/warn 由 -- pbl_blueprint_template.ref_policy 决定(Q-OPEN-4 阈值可配); -- * snapshot_json 保存解析时刻只读快照,避免运行期跨库联查。 -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_blueprint_ref` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '物理主键 AUTO_INCREMENT', `tenant_key` varchar(32) NOT NULL COMMENT '= COALESCE(tenant_id,''__PLATFORM__'');索引打头列;应用层计算', `tenant_id` varchar(32) DEFAULT NULL COMMENT 'NULL = 平台公共模板携带的关联;非空 = 租户蓝图关联', `ref_code` varchar(32) NOT NULL COMMENT '业务主键,pbl_common.gen_code(''BPR'', tenant_id) 生成', `owner_kind` varchar(16) NOT NULL DEFAULT 'blueprint' COMMENT '字典 pbl_ext_owner_kind:blueprint / template', `blueprint_id` varchar(32) NOT NULL COMMENT 'owner_kind=blueprint 时为 M1A blueprint_id;=template 时复用本列存 template_code(取值由 owner_kind 判定)', `subobject_id` varchar(32) DEFAULT NULL COMMENT '关联所属的 *_ref 类子对象ID;模板态存模板临时ID', `ref_kind` varchar(16) NOT NULL COMMENT '字典 pbl_ref_kind:world / scene / entity / script', `ref_target_code` varchar(64) NOT NULL COMMENT '复用域对象业务编码(只读引用,无 FK,不反查基表写操作)', `ref_target_id` varchar(64) DEFAULT NULL COMMENT '解析成功时回填的对方物理ID快照', `ref_mode` varchar(16) NOT NULL DEFAULT 'readonly' COMMENT '恒为 readonly(Q-OPEN-3:不改 world 表,只读引用);字典 pbl_ref_mode;写入非 readonly 值将被 m1b_ref 拒绝', `ref_status` varchar(16) NOT NULL DEFAULT 'unresolved' COMMENT '字典 pbl_ref_status:unresolved 待解析 / resolved 已解析 / invalid 目标不存在或不可用', `resolve_time` datetime DEFAULT NULL COMMENT '解析时间', `resolve_msg` varchar(512) DEFAULT NULL COMMENT 'invalid 时记录原因,供 M2 校验与教师端提示', `snapshot_json` text DEFAULT NULL COMMENT '解析时刻外部对象关键字段快照(JSON),运行期免跨库联查', `sort_no` int NOT NULL DEFAULT 0 COMMENT '排序号', `creator_id` varchar(32) DEFAULT NULL COMMENT '创建人', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_ref_code` (`tenant_key`, `ref_code`), UNIQUE KEY `uk_ref_slot` (`tenant_key`, `owner_kind`, `blueprint_id`, `ref_kind`, `ref_target_code`), KEY `idx_ref_blueprint` (`tenant_key`, `owner_kind`, `blueprint_id`, `ref_kind`), KEY `idx_ref_target` (`tenant_key`, `ref_kind`, `ref_target_code`), KEY `idx_ref_status` (`tenant_key`, `ref_status`), KEY `idx_ref_tenant` (`tenant_id`, `blueprint_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='PBL 蓝图外部关联表(M1b,Q-OPEN-3 不改 world 表的只读关联落地)'; -- ============================================================================= -- 幂等性说明:以上 4 段均为 CREATE TABLE IF NOT EXISTS,索引随表定义一并创建, -- 重复执行不报错、不产生重复索引。m1b_init.ensure_m1b_tables() 按 ';' 切分本文件 -- 逐条 sor.sqlExe(dbname, stmt) 执行(库名来自 ServerEnv().get_module_dbname, -- 本文件与本模块任何代码均不硬编码库名)。 -- =============================================================================