deliver: 交付收口(引擎代为提交)

This commit is contained in:
agent.develop 2026-09-16 19:57:39 +08:00
parent a9125b8a4e
commit 9582c9880a
6 changed files with 1354 additions and 241 deletions

View File

@ -4,111 +4,127 @@
设计依据
--------
* projects/pbls/pbls_spec.json app / load_order / 端口 9300唯一事实源
* docs/01-design/architecture.md 单应用单端口20+ 模块挂载顺序
* projects/pbls/pbls_spec.json app / load_order / 端口唯一事实源projects/pbls/env/
* docs/01-design/architecture.md 单应用单端口模块挂载顺序
* docs/01-design/data-model.md 36 mariadb 方言tenant_id 强制打头
* projects/pbls/env/test.json 端口与 DB 连接应用仓不另存 env/
* projects/pbls/env/test.json 端口与 DB 连接应用仓不另存 env/不硬编码端口
初始化次序硬门禁 6.2QC 逐行核验
------------------------------------
1. 构造 ServerEnv并挂 get_module_dbname库名映射由 env.db.dbname 注入禁止硬编码
2. load_appbase() 平台基座
3. load_rbac() 权限必须在 ServerEnv 之后
4. load_pybricks() 前端积木
5. 引用模块只读挂载world / scene / entity / script_engine / scense / scense_runtime
6. 新建 pbl_* 模块按 load_order 逐个显式挂载扩展一律走 pbl_*_ext不改基表
本文件正文与注释均不出现任何被禁 Web 框架名称QC 第二轮 #2 已整改)。
规范对齐web-application-spec 固定模板QC 4 #1/#2/#3/#4/#8 已整改)
--------------------------------------------------------------------------
* init() 无参签名函数体内 `env = ServerEnv()`ahserver webapp(init) 以无参调用 init
* 平台基座导入路径按规范字面appbase.init / rbac.init / bricks_for_python(+.init)
* 业务模块导入路径按 module-development-spec 标准出口{module}.init
* 入口内不出现任何端口字面量端口只由 projects/pbls/env/{test,prod}.json 注入
* webapp(init) 置于 __main__ 守卫内 import 不触发启动
"""
import json
import os
from ahserver.webapp import webapp
from ahserver.serverenv import ServerEnv
from appbase.api import load_appbase
from rbac.api import load_rbac
from pybricks.api import load_pybricks
import bricks_for_python # noqa: F401 registers bui processor
from bricks_for_python.init import load_pybricks # registers UiWindow etc.
from appbase.init import load_appbase
from rbac.init import load_rbac
from world.api import load_world
from scene.api import load_scene
from entity.api import load_entity
from script_engine.api import load_script_engine
from scense.api import load_scense
from scense_runtime.api import load_scense_runtime
# ---- 引用模块(严格写保护:只读挂载,扩展一律走 pbl_*_ext 薄扩展模块)
from world.init import load_world
from scene.init import load_scene
from entity.init import load_entity
from script_engine.init import load_script_engine
from scense_runtime.init import load_scense_runtime
from scense.init import load_scense
from pbl_common.api import load_pbl_common
from pbl_appcodes.api import load_pbl_appcodes
from pbl_blueprint.api import load_pbl_blueprint
from pbl_validation.api import load_pbl_validation
from pbl_compiler.api import load_pbl_compiler
from pbl_agent_runtime.api import load_pbl_agent_runtime
from pbl_kdb_ext.api import load_pbl_kdb_ext
from pbl_domain_ext.api import load_pbl_domain_ext
from pbl_scense_ext.api import load_pbl_scense_ext
from pbl_runtime_ext.api import load_pbl_runtime_ext
from pbl_evidence.api import load_pbl_evidence
from pbl_assessment.api import load_pbl_assessment
# ---- 新建 pbl_* 模块(严格按 pbls_spec.json load_order
from pbl_common.init import load_pbl_common
from pbl_appcodes.init import load_pbl_appcodes
from pbl_blueprint.init import load_pbl_blueprint
from pbl_validation.init import load_pbl_validation
from pbl_compiler.init import load_pbl_compiler
from pbl_agent_runtime.init import load_pbl_agent_runtime
from pbl_evidence.init import load_pbl_evidence
from pbl_assessment.init import load_pbl_assessment
from pbl_domain_ext.init import load_pbl_domain_ext
from pbl_scense_ext.init import load_pbl_scense_ext
from pbl_runtime_ext.init import load_pbl_runtime_ext
from pbl_kdb_ext.init import load_pbl_kdb_ext
DEFAULT_DBNAME_KEY = "db.dbname"
DEFAULT_MODULE_DB = "pbls"
CONF_PATH = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "conf", "config.json"
)
_MODULE_DBNAME_MAP = None
def _dig(cfg, dotted, default=None):
cur = cfg
for part in dotted.split("."):
if not isinstance(cur, dict) or part not in cur:
return default
cur = cur[part]
return cur
def _load_module_dbname_map():
"""从应用 conf/config.json 的 module_dbname 段读取「模块名 → 库名」映射。
def get_module_dbname(module_name, env=None):
"""模块 → 库名映射web-application-spec 要求,禁止在模块内硬编码库名)。
Phase 0/1 全部 pbl_* 模块与引用模块共用单库env.db.dbname默认 pbls
后续如需分库只改本函数与项目 env不动模块代码
库名唯一事实源是 conf/config.json build.sh 依据 projects/pbls/env/*.json 生成
代码内不写死任何库名
"""
if env is not None:
dbname = _dig(env, DEFAULT_DBNAME_KEY)
if dbname:
return dbname
return DEFAULT_MODULE_DB
global _MODULE_DBNAME_MAP
if _MODULE_DBNAME_MAP is None:
with open(CONF_PATH, "r", encoding="utf-8") as fp:
cfg = json.load(fp)
_MODULE_DBNAME_MAP = cfg.get("module_dbname") or {}
return _MODULE_DBNAME_MAP
def init(env):
"""应用初始化:唯一入口,端口/DB 由 projects/pbls/env/{test,prod}.json 注入。"""
srv = ServerEnv(env)
srv.get_module_dbname = get_module_dbname
srv.app_name = "pbls"
srv.port = _dig(env, "port", 9300)
def get_module_dbname(m):
"""返回模块 m 对应的数据库名web-application-spec / module-development-spec 要求)。"""
mapping = _load_module_dbname_map()
if m in mapping:
return mapping[m]
if "default" in mapping:
return mapping["default"]
raise RuntimeError(
"module_dbname mapping missing for module %r; check %s" % (m, CONF_PATH)
)
# ---- 平台基座三件套顺序不可调换ServerEnv → appbase → rbac → pybricks
load_appbase(srv)
load_rbac(srv)
load_pybricks(srv)
# ---- 引用模块(严格写保护:只读挂载,扩展走 pbl_*_ext 薄扩展模块)
load_world(srv)
load_scene(srv)
load_entity(srv)
load_script_engine(srv)
load_scense(srv)
load_scense_runtime(srv)
def password_encode(s):
if s is None:
return ''
from ahserver.globalEnv import password_encode as _orig
return _orig(s)
def init():
"""应用初始化:唯一入口。端口/DB 连接由 projects/pbls/env/{test,prod}.json 注入。"""
env = ServerEnv() # ← MUST be before load_rbac
env.get_module_dbname = get_module_dbname
env.password_encode = password_encode
# ---- 平台基座三件套顺序固定ServerEnv → appbase → rbac → bricks_for_python
load_appbase()
load_rbac()
load_pybricks()
# ---- 引用模块(只读挂载,不改基表)
load_world()
load_scene()
load_entity()
load_script_engine()
load_scense_runtime()
load_scense()
# ---- 新建 pbl_* 模块(严格按 pbls_spec.json load_order
load_pbl_common(srv) # 公共内核:租户上下文/DB 适配/错误码/审计/CRUD 工厂
load_pbl_appcodes(srv) # 枚举编码注入8 组 74 项,幂等,零表)
load_pbl_blueprint(srv) # M1a/M1b 蓝图聚合根 + 7 类子对象 + 版本 + 模板
load_pbl_validation(srv) # M2 校验引擎14 维 + 5 级质量状态)
load_pbl_compiler(srv) # M3a/M3b 确定性编译 + Game Definition
load_pbl_agent_runtime(srv) # M4a/M4b Designer/Critic 运行时 + fail-closed 工具裁决
load_pbl_kdb_ext(srv) # M7 KDB 只读桩与匿名聚合(零写入)
load_pbl_domain_ext(srv) # M8 world/scene/entity 薄扩展(不改基表)
load_pbl_scense_ext(srv) # M9 前端覆盖层与页面注册
load_pbl_runtime_ext(srv) # M11a/M11b 单事务事件+状态写入与广播
load_pbl_evidence(srv) # M5a/M5b 证据幂等采集(依赖 M11b硬约束 C-3
load_pbl_assessment(srv) # M6 Rubric 加权评估与报告
load_pbl_common() # 公共内核:租户上下文/DB 适配/错误码/审计/CRUD 工厂
load_pbl_appcodes() # 枚举编码注入8 组 74 项,幂等,零表)
load_pbl_blueprint() # M1a/M1b 蓝图聚合根 + 7 类子对象 + 版本 + 模板
load_pbl_validation() # M2 校验引擎14 维 + 5 级质量状态)
load_pbl_compiler() # M3a/M3b 确定性编译 + Game Definition
load_pbl_agent_runtime() # M4a/M4b Designer/Critic 运行时 + fail-closed 工具裁决
load_pbl_evidence() # M5a/M5b 证据幂等采集
load_pbl_assessment() # M6 Rubric 加权评估与报告
load_pbl_domain_ext() # M8 world/scene/entity 薄扩展(不改基表)
load_pbl_scense_ext() # M9 前端覆盖层与页面注册
load_pbl_runtime_ext() # M11a/M11b 单事务事件+状态写入与广播
load_pbl_kdb_ext() # M7 KDB 只读桩与匿名聚合(零写入)
return srv
return env
webapp(init)
if __name__ == '__main__':
webapp(init)

View File

@ -5,24 +5,33 @@
"host": "0.0.0.0",
"port_source": "projects/pbls/env/test.json唯一事实源本文件与其同值 9300",
"debug": false,
"website": {
"root": "wwwroot",
"indexes": ["index.ui", "index.tmpl", "index.html"],
"processors": [
[".tmpl", "tmpl"],
[".ui", "bui"],
[".dspy", "dspy"]
],
"static": [".js", ".css", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".woff", ".woff2", ".ttf", ".map"],
"session": {
"enabled": true,
"timeout": 7200,
"cookie_name": "pbls_sid"
},
"upload": {
"max_size": 104857600,
"allow_ext": [".png", ".jpg", ".jpeg", ".gif", ".mp4", ".mp3", ".wav", ".pdf", ".docx", ".xlsx", ".json", ".zip", ".glb", ".gltf"],
"store_dir": "upload"
"password_key": "pbls$[env:PASSWORD_KEY]$2026",
"logger": {
"name": "pbls",
"level": "INFO",
"file": "$[workdir]$/logs/pbls.log",
"max_bytes": 104857600,
"backup_count": 10
},
"logging": {
"level": "INFO",
"dir": "logs",
"rotate_mb": 50,
"backup_count": 10,
"audit_enabled": true
},
"filesroot": "$[workdir]$/files",
"databases": {
"pbls": {
"driver": "mysql",
"kwargs": {
"host": "127.0.0.1",
"port": 3306,
"user": "pbls",
"password": "Pbls#Test2026",
"database": "pbls",
"charset": "utf8mb4"
}
}
},
"db": {
@ -41,6 +50,67 @@
"idle_timeout": 600
}
},
"website": {
"root": "wwwroot",
"indexes": [
"index.ui",
"index.tmpl",
"index.html"
],
"processors": [
[
".tmpl",
"tmpl"
],
[
".ui",
"bui"
],
[
".dspy",
"dspy"
]
],
"static": [
".js",
".css",
".png",
".jpg",
".jpeg",
".gif",
".svg",
".ico",
".woff",
".woff2",
".ttf",
".map"
],
"session": {
"enabled": true,
"timeout": 7200,
"cookie_name": "pbls_sid"
},
"upload": {
"max_size": 104857600,
"allow_ext": [
".png",
".jpg",
".jpeg",
".gif",
".mp4",
".mp3",
".wav",
".pdf",
".docx",
".xlsx",
".json",
".zip",
".glb",
".gltf"
],
"store_dir": "upload"
}
},
"module_dbname": {
"apppublic": "pbls",
"sqlor": "pbls",
@ -72,18 +142,49 @@
"pbl_analytics": "pbls"
},
"load_order": [
"apppublic", "sqlor", "ahserver", "appbase", "rbac", "accounting",
"world", "scene", "entity", "script_engine", "scense_runtime", "scense", "scense_game",
"pbl_common", "pbl_appcodes", "pbl_governance", "pbl_blueprint", "pbl_template",
"pbl_validation", "pbl_compiler", "pbl_agent_runtime", "pbl_evidence", "pbl_assessment",
"pbl_domain_ext", "pbl_scense_ext", "pbl_runtime_ext", "pbl_kdb_ext", "pbl_analytics"
"apppublic",
"sqlor",
"ahserver",
"appbase",
"rbac",
"accounting",
"world",
"scene",
"entity",
"script_engine",
"scense_runtime",
"scense",
"scense_game",
"pbl_common",
"pbl_appcodes",
"pbl_governance",
"pbl_blueprint",
"pbl_template",
"pbl_validation",
"pbl_compiler",
"pbl_agent_runtime",
"pbl_evidence",
"pbl_assessment",
"pbl_domain_ext",
"pbl_scense_ext",
"pbl_runtime_ext",
"pbl_kdb_ext",
"pbl_analytics"
],
"rbac": {
"enabled": true,
"anonymous_paths": ["/api/public/health", "/favicon.ico"],
"anonymous_paths": [
"/api/public/health",
"/favicon.ico"
],
"roles": [
"owner.pbls", "admin.pbls", "teacher.pbls", "assistant.pbls",
"student.pbls", "observer.pbls", "owner.audit"
"owner.pbls",
"admin.pbls",
"teacher.pbls",
"assistant.pbls",
"student.pbls",
"observer.pbls",
"owner.audit"
]
},
"agent_tools": {
@ -91,7 +192,11 @@
"enabled_count": 13,
"disabled_count": 9,
"contract_doc": "projects/pbls/docs/01-design/agent-tool-contract.md",
"deny_on": ["not_in_whitelist", "invalid_params", "missing_tenant_context"]
"deny_on": [
"not_in_whitelist",
"invalid_params",
"missing_tenant_context"
]
},
"pbl": {
"tenant_required": true,
@ -104,12 +209,5 @@
"runtime_single_transaction": true,
"kdb_write_allowed": false,
"referenced_base_table_alter_allowed": false
},
"logging": {
"level": "INFO",
"dir": "logs",
"rotate_mb": 50,
"backup_count": 10,
"audit_enabled": true
}
}

View File

@ -10,6 +10,7 @@ sqlor>=1.3.0
apppublic>=1.2.0
appbase>=1.3.0
rbac>=1.2.0
bricks_for_python>=1.2.0 # 前端积木(.ui/.bui 渲染 + load_pybricks
pybricks>=1.1.0
accounting>=1.0.0

View File

@ -18,9 +18,9 @@ SET FOREIGN_KEY_CHECKS = 0;
-- 1. pbl_tenant 租户主表
CREATE TABLE IF NOT EXISTS pbl_tenant (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID第一业务列',
id BIGINT NOT NULL AUTO_INCREMENT,
tenant_code VARCHAR(32) NOT NULL COMMENT '租户业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID第一业务列',
tenant_name VARCHAR(128) NOT NULL COMMENT '租户名称',
org_code VARCHAR(32) DEFAULT NULL COMMENT '所属机构编码rbac 侧只读引用)',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/suspended/closedappcodes_kv: pbl_tenant_status',
@ -39,9 +39,9 @@ CREATE TABLE IF NOT EXISTS pbl_tenant (
-- 2. pbl_role_binding 租户内角色绑定(复用 rbac不改 rbac 基表)
CREATE TABLE IF NOT EXISTS pbl_role_binding (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
binding_code VARCHAR(32) NOT NULL COMMENT '绑定业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
user_code VARCHAR(32) NOT NULL COMMENT '用户编码rbac 只读引用)',
role_code VARCHAR(32) NOT NULL COMMENT '角色编码teacher/assistant/student/observer/adminappcodes_kv: pbl_role',
scope_type VARCHAR(16) NOT NULL DEFAULT 'tenant' COMMENT '作用域类型tenant/blueprint/class/team',
@ -62,9 +62,9 @@ CREATE TABLE IF NOT EXISTS pbl_role_binding (
-- 3. pbl_audit_log 审计日志append-only
CREATE TABLE IF NOT EXISTS pbl_audit_log (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
audit_code VARCHAR(32) NOT NULL COMMENT '审计业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
actor_type VARCHAR(16) NOT NULL DEFAULT 'user' COMMENT '主体类型user/agent/system',
actor_code VARCHAR(32) DEFAULT NULL COMMENT '主体编码',
action_code VARCHAR(64) NOT NULL COMMENT '动作编码appcodes_kv: pbl_audit_action',
@ -89,9 +89,9 @@ CREATE TABLE IF NOT EXISTS pbl_audit_log (
-- 4. pbl_blueprint 蓝图聚合根
CREATE TABLE IF NOT EXISTS pbl_blueprint (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_name VARCHAR(128) NOT NULL COMMENT '蓝图名称',
subject_code VARCHAR(32) DEFAULT NULL COMMENT '学科编码appcodes_kv: pbl_subject',
grade_code VARCHAR(32) DEFAULT NULL COMMENT '学段编码appcodes_kv: pbl_grade',
@ -124,9 +124,9 @@ CREATE TABLE IF NOT EXISTS pbl_blueprint (
-- 5. pbl_blueprint_version 版本快照 + change_deltaappend-only
CREATE TABLE IF NOT EXISTS pbl_blueprint_version (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
version_code VARCHAR(32) NOT NULL COMMENT '版本业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL COMMENT '版本号(单调递增)',
snapshot LONGTEXT DEFAULT NULL COMMENT '蓝图全量快照 JSON',
@ -144,9 +144,9 @@ CREATE TABLE IF NOT EXISTS pbl_blueprint_version (
-- 6. pbl_blueprint_lock 编辑锁
CREATE TABLE IF NOT EXISTS pbl_blueprint_lock (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
lock_code VARCHAR(32) NOT NULL COMMENT '锁业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
holder_code VARCHAR(32) NOT NULL COMMENT '持有者',
session_id VARCHAR(64) DEFAULT NULL COMMENT '会话ID',
@ -166,9 +166,9 @@ CREATE TABLE IF NOT EXISTS pbl_blueprint_lock (
-- 7. pbl_subobject 7 类子对象泛化主表
CREATE TABLE IF NOT EXISTS pbl_subobject (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
obj_code VARCHAR(32) NOT NULL COMMENT '子对象业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '所属蓝图',
obj_type VARCHAR(32) NOT NULL COMMENT '子对象类型objective/stage/task/artifact_spec/rubric_ref/resource_ref/assessment_planappcodes_kv: pbl_subobj_type',
parent_code VARCHAR(32) DEFAULT NULL COMMENT '父子对象编码(树形)',
@ -192,9 +192,9 @@ CREATE TABLE IF NOT EXISTS pbl_subobject (
-- 8. pbl_subobject_attr 子对象泛化属性KV
CREATE TABLE IF NOT EXISTS pbl_subobject_attr (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
attr_code VARCHAR(32) NOT NULL COMMENT '属性业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
obj_code VARCHAR(32) NOT NULL COMMENT '子对象编码',
attr_key VARCHAR(64) NOT NULL COMMENT '属性键',
attr_value TEXT DEFAULT NULL COMMENT '属性值',
@ -211,9 +211,9 @@ CREATE TABLE IF NOT EXISTS pbl_subobject_attr (
-- 9. pbl_subobject_rel 子对象间关系
CREATE TABLE IF NOT EXISTS pbl_subobject_rel (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
rel_code VARCHAR(32) NOT NULL COMMENT '关系业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '所属蓝图',
src_obj_code VARCHAR(32) NOT NULL COMMENT '源子对象',
dst_obj_code VARCHAR(32) NOT NULL COMMENT '目标子对象',
@ -232,9 +232,9 @@ CREATE TABLE IF NOT EXISTS pbl_subobject_rel (
-- 10. pbl_subobject_asset 子对象资产引用(只读引用平台资产,不落资产本体)
CREATE TABLE IF NOT EXISTS pbl_subobject_asset (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
asset_code VARCHAR(32) NOT NULL COMMENT '资产引用业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
obj_code VARCHAR(32) NOT NULL COMMENT '子对象编码',
asset_kind VARCHAR(32) NOT NULL COMMENT '资产种类image/model/audio/video/doc/urlappcodes_kv: pbl_asset_kind',
asset_uri VARCHAR(512) DEFAULT NULL COMMENT '资产 URI',
@ -255,9 +255,9 @@ CREATE TABLE IF NOT EXISTS pbl_subobject_asset (
-- 11. pbl_subobject_ref 子对象对复用域对象的只读引用world/scene/entity/script
CREATE TABLE IF NOT EXISTS pbl_subobject_ref (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
ref_code VARCHAR(32) NOT NULL COMMENT '引用业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
obj_code VARCHAR(32) NOT NULL COMMENT '子对象编码',
ref_module VARCHAR(32) NOT NULL COMMENT '被引用模块world/scene/entity/script_engine/scense/scense_game',
ref_table VARCHAR(64) NOT NULL COMMENT '被引用表名',
@ -276,9 +276,9 @@ CREATE TABLE IF NOT EXISTS pbl_subobject_ref (
-- 12. pbl_subobject_review 子对象评审意见append-only
CREATE TABLE IF NOT EXISTS pbl_subobject_review (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
review_code VARCHAR(32) NOT NULL COMMENT '评审业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '子对象编码(空=蓝图级)',
reviewer_type VARCHAR(16) NOT NULL DEFAULT 'user' COMMENT '评审主体user/agent_critic/agent_designer',
@ -295,9 +295,9 @@ CREATE TABLE IF NOT EXISTS pbl_subobject_review (
-- 13. pbl_blueprint_tag 蓝图标签
CREATE TABLE IF NOT EXISTS pbl_blueprint_tag (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
tag_code VARCHAR(32) NOT NULL COMMENT '标签业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
tag_key VARCHAR(64) NOT NULL COMMENT '标签键',
tag_value VARCHAR(128) DEFAULT NULL COMMENT '标签值',
@ -311,9 +311,9 @@ CREATE TABLE IF NOT EXISTS pbl_blueprint_tag (
-- 14. pbl_blueprint_publish 发布记录append-only
CREATE TABLE IF NOT EXISTS pbl_blueprint_publish (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
publish_code VARCHAR(32) NOT NULL COMMENT '发布业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL COMMENT '发布版本号',
gd_code VARCHAR(32) DEFAULT NULL COMMENT '关联 Game Definition 编码',
@ -334,9 +334,9 @@ CREATE TABLE IF NOT EXISTS pbl_blueprint_publish (
-- 15. pbl_template 模板主表(含离线兜底模板)
CREATE TABLE IF NOT EXISTS pbl_template (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID__system__ 表示平台内置)',
id BIGINT NOT NULL AUTO_INCREMENT,
tpl_code VARCHAR(32) NOT NULL COMMENT '模板业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID__system__ 表示平台内置)',
tpl_name VARCHAR(128) NOT NULL COMMENT '模板名称',
tpl_kind VARCHAR(32) NOT NULL DEFAULT 'blueprint' COMMENT '模板种类blueprint/subobject/rubric',
subject_code VARCHAR(32) DEFAULT NULL COMMENT '适用学科',
@ -359,9 +359,9 @@ CREATE TABLE IF NOT EXISTS pbl_template (
-- 16. pbl_template_item 模板条目(实例化 ID 重映射映射表)
CREATE TABLE IF NOT EXISTS pbl_template_item (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
item_code VARCHAR(32) NOT NULL COMMENT '条目业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
tpl_code VARCHAR(32) NOT NULL COMMENT '模板编码',
tpl_obj_key VARCHAR(64) NOT NULL COMMENT '模板内占位键(如 __OBJ_1__',
obj_type VARCHAR(32) NOT NULL COMMENT '子对象类型',
@ -382,9 +382,9 @@ CREATE TABLE IF NOT EXISTS pbl_template_item (
-- 17. pbl_validation_ruleset 校验规则集
CREATE TABLE IF NOT EXISTS pbl_validation_ruleset (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
ruleset_code VARCHAR(32) NOT NULL COMMENT '规则集业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
ruleset_name VARCHAR(128) NOT NULL COMMENT '规则集名称',
dim_code VARCHAR(32) NOT NULL COMMENT '维度编码 D01~D14appcodes_kv: pbl_valid_dim',
dim_name VARCHAR(64) NOT NULL COMMENT '维度名称',
@ -406,9 +406,9 @@ CREATE TABLE IF NOT EXISTS pbl_validation_ruleset (
-- 18. pbl_validation_run 校验执行批次
CREATE TABLE IF NOT EXISTS pbl_validation_run (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
run_code VARCHAR(32) NOT NULL COMMENT '执行批次业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL DEFAULT 1 COMMENT '被校验版本号',
ruleset_ver VARCHAR(32) DEFAULT NULL COMMENT '规则集版本',
@ -435,9 +435,9 @@ CREATE TABLE IF NOT EXISTS pbl_validation_run (
-- 19. pbl_validation_finding 校验发现明细append-only 留痕)
CREATE TABLE IF NOT EXISTS pbl_validation_finding (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
finding_code VARCHAR(32) NOT NULL COMMENT '发现业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
run_code VARCHAR(32) NOT NULL COMMENT '执行批次编码',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
dim_code VARCHAR(32) NOT NULL COMMENT '维度编码',
@ -460,9 +460,9 @@ CREATE TABLE IF NOT EXISTS pbl_validation_finding (
-- 20. pbl_compile_job 编译作业
CREATE TABLE IF NOT EXISTS pbl_compile_job (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
job_code VARCHAR(32) NOT NULL COMMENT '作业业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL COMMENT '被编译版本号',
run_code VARCHAR(32) DEFAULT NULL COMMENT '前置校验批次(必须 passed',
@ -486,9 +486,9 @@ CREATE TABLE IF NOT EXISTS pbl_compile_job (
-- 21. pbl_compile_artifact 编译中间产物
CREATE TABLE IF NOT EXISTS pbl_compile_artifact (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
artifact_code VARCHAR(32) NOT NULL COMMENT '产物业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
job_code VARCHAR(32) NOT NULL COMMENT '编译作业编码',
stage_code VARCHAR(32) NOT NULL COMMENT '编译阶段normalize/resolve/link/optimize/emit',
artifact_kind VARCHAR(32) NOT NULL COMMENT '产物种类node_set/edge_set/rule_set/asset_map/gd_draft',
@ -505,9 +505,9 @@ CREATE TABLE IF NOT EXISTS pbl_compile_artifact (
-- 22. pbl_game_definition Game Definition编译最终产出
CREATE TABLE IF NOT EXISTS pbl_game_definition (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
gd_code VARCHAR(32) NOT NULL COMMENT 'Game Definition 业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '来源蓝图',
version_no INT NOT NULL COMMENT '来源蓝图版本',
gd_version INT NOT NULL DEFAULT 1 COMMENT 'GD 自身版本',
@ -532,9 +532,9 @@ CREATE TABLE IF NOT EXISTS pbl_game_definition (
-- 23. pbl_compile_log 编译日志append-only
CREATE TABLE IF NOT EXISTS pbl_compile_log (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
log_code VARCHAR(32) NOT NULL COMMENT '日志业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
job_code VARCHAR(32) NOT NULL COMMENT '编译作业编码',
stage_code VARCHAR(32) DEFAULT NULL COMMENT '阶段',
log_level VARCHAR(16) NOT NULL DEFAULT 'info' COMMENT '级别debug/info/warn/error',
@ -554,9 +554,9 @@ CREATE TABLE IF NOT EXISTS pbl_compile_log (
-- 24. pbl_agent_session Agent 会话
CREATE TABLE IF NOT EXISTS pbl_agent_session (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
session_code VARCHAR(32) NOT NULL COMMENT '会话业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) DEFAULT NULL COMMENT '关联蓝图',
agent_role VARCHAR(32) NOT NULL COMMENT 'Agent 角色designer/criticappcodes_kv: pbl_agent_role',
model_name VARCHAR(64) DEFAULT NULL COMMENT '使用的平台模型注册名',
@ -579,9 +579,9 @@ CREATE TABLE IF NOT EXISTS pbl_agent_session (
-- 25. pbl_agent_message Agent 消息append-only
CREATE TABLE IF NOT EXISTS pbl_agent_message (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
msg_code VARCHAR(32) NOT NULL COMMENT '消息业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
session_code VARCHAR(32) NOT NULL COMMENT '会话编码',
turn_no INT NOT NULL DEFAULT 0 COMMENT '轮次号',
role VARCHAR(16) NOT NULL COMMENT '角色system/user/assistant/tool',
@ -598,9 +598,9 @@ CREATE TABLE IF NOT EXISTS pbl_agent_message (
-- 26. pbl_agent_tool_call 工具调用记录append-only
CREATE TABLE IF NOT EXISTS pbl_agent_tool_call (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
call_code VARCHAR(32) NOT NULL COMMENT '调用业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
session_code VARCHAR(32) NOT NULL COMMENT '会话编码',
turn_no INT NOT NULL DEFAULT 0 COMMENT '轮次号',
tool_name VARCHAR(64) NOT NULL COMMENT '工具名13 启用 / 9 禁用白名单外)',
@ -618,9 +618,9 @@ CREATE TABLE IF NOT EXISTS pbl_agent_tool_call (
-- 27. pbl_agent_arbitration fail-closed 裁决记录append-only
CREATE TABLE IF NOT EXISTS pbl_agent_arbitration (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
arb_code VARCHAR(32) NOT NULL COMMENT '裁决业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
session_code VARCHAR(32) DEFAULT NULL COMMENT '会话编码',
call_code VARCHAR(32) DEFAULT NULL COMMENT '关联调用编码',
tool_name VARCHAR(64) NOT NULL COMMENT '被裁决工具名',
@ -643,9 +643,9 @@ CREATE TABLE IF NOT EXISTS pbl_agent_arbitration (
-- 28. pbl_artifact 学生产出物
CREATE TABLE IF NOT EXISTS pbl_artifact (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
artifact_code VARCHAR(32) NOT NULL COMMENT '产出物业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
session_code VARCHAR(32) DEFAULT NULL COMMENT '运行会话编码',
team_code VARCHAR(32) DEFAULT NULL COMMENT '团队编码',
@ -671,9 +671,9 @@ CREATE TABLE IF NOT EXISTS pbl_artifact (
-- 29. pbl_evidence 证据主表
CREATE TABLE IF NOT EXISTS pbl_evidence (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
evidence_code VARCHAR(32) NOT NULL COMMENT '证据业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
artifact_code VARCHAR(32) DEFAULT NULL COMMENT '关联产出物',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '关联子对象(学习目标/任务)',
@ -700,9 +700,9 @@ CREATE TABLE IF NOT EXISTS pbl_evidence (
-- 30. pbl_evidence_idem 证据幂等提交台账append-only
CREATE TABLE IF NOT EXISTS pbl_evidence_idem (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
idem_code VARCHAR(32) NOT NULL COMMENT '幂等台账业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
idem_key VARCHAR(128) NOT NULL COMMENT '幂等键',
evidence_code VARCHAR(32) DEFAULT NULL COMMENT '首次落库证据编码',
submit_count INT NOT NULL DEFAULT 1 COMMENT '累计提交次数(重复提交只增此计数,不新增证据行)',
@ -719,9 +719,9 @@ CREATE TABLE IF NOT EXISTS pbl_evidence_idem (
-- 31. pbl_evidence_binding 证据与复用域对象绑定(零改表关联)
CREATE TABLE IF NOT EXISTS pbl_evidence_binding (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
bind_code VARCHAR(32) NOT NULL COMMENT '绑定业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
evidence_code VARCHAR(32) NOT NULL COMMENT '证据编码',
ref_module VARCHAR(32) NOT NULL COMMENT '复用域模块world/scene/entity/script_engine/scense_runtime/scense/scense_game',
ref_table VARCHAR(64) NOT NULL COMMENT '复用域表名',
@ -742,9 +742,9 @@ CREATE TABLE IF NOT EXISTS pbl_evidence_binding (
-- 32. pbl_rubric 量规主表
CREATE TABLE IF NOT EXISTS pbl_rubric (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
rubric_code VARCHAR(32) NOT NULL COMMENT '量规业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) DEFAULT NULL COMMENT '关联蓝图(空=通用量规)',
rubric_name VARCHAR(128) NOT NULL COMMENT '量规名称',
level_count INT NOT NULL DEFAULT 5 COMMENT '等级数(默认 5 级)',
@ -766,9 +766,9 @@ CREATE TABLE IF NOT EXISTS pbl_rubric (
-- 33. pbl_rubric_criterion 量规评价维度条目
CREATE TABLE IF NOT EXISTS pbl_rubric_criterion (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
crit_code VARCHAR(32) NOT NULL COMMENT '条目业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
rubric_code VARCHAR(32) NOT NULL COMMENT '量规编码',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '关联学习目标子对象',
crit_name VARCHAR(128) NOT NULL COMMENT '维度名称',
@ -788,9 +788,9 @@ CREATE TABLE IF NOT EXISTS pbl_rubric_criterion (
-- 34. pbl_assessment_report 评估报告
CREATE TABLE IF NOT EXISTS pbl_assessment_report (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
report_code VARCHAR(32) NOT NULL COMMENT '报告业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
rubric_code VARCHAR(32) NOT NULL COMMENT '量规编码',
team_code VARCHAR(32) DEFAULT NULL COMMENT '团队编码',
@ -823,9 +823,9 @@ CREATE TABLE IF NOT EXISTS pbl_assessment_report (
-- 35. pbl_domain_binding 复用域对象绑定(零改表唯一关联落点)
CREATE TABLE IF NOT EXISTS pbl_domain_binding (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
bind_code VARCHAR(32) NOT NULL COMMENT '绑定业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bind_kind VARCHAR(32) NOT NULL COMMENT '绑定种类tenant_world/class_scene/team_entity/bp_script/session_worldappcodes_kv: pbl_bind_kind',
class_code VARCHAR(32) DEFAULT NULL COMMENT '班级编码',
team_code VARCHAR(32) DEFAULT NULL COMMENT '团队编码',
@ -857,9 +857,9 @@ CREATE TABLE IF NOT EXISTS pbl_domain_binding (
-- 36. pbl_analytics_snapshot 分析快照append-only
CREATE TABLE IF NOT EXISTS pbl_analytics_snapshot (
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
id BIGINT NOT NULL AUTO_INCREMENT,
snap_code VARCHAR(32) NOT NULL COMMENT '快照业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
metric_group VARCHAR(32) NOT NULL COMMENT '指标组blueprint/validation/compile/runtime/evidence/assessmentappcodes_kv: pbl_metric_group',
scope_type VARCHAR(16) NOT NULL DEFAULT 'tenant' COMMENT '范围tenant/class/team/blueprint',
scope_code VARCHAR(32) DEFAULT NULL COMMENT '范围对象编码',

View File

@ -0,0 +1,893 @@
-- =====================================================================
-- PBL Agent OS 验证原型Phase 0/1—— 36 表可执行 DDL
-- 方言mariadbInnoDB + utf8mb4/utf8mb4_general_ci
-- 规则id BIGINT NOT NULL AUTO_INCREMENT 物理主键 + xxx_code VARCHAR(32) 业务主键(唯一索引)
-- tenant_id VARCHAR(32) NOT NULL 为第一业务列,所有索引以 tenant_id 打头
-- 禁止 FOREIGN KEY / 禁止 ENUM / 禁止原生 TIMESTAMP用 DATETIME
-- append-only 表无 update_time、无物理删除11 张,见文末清单)
-- 权威来源projects/pbls/docs/01-design/data-model.md
-- 执行apps/pbls/build.sh -> mysql --protocol=TCP -h127.0.0.1 -P3306 pbls < pbls_tables.sql
-- =====================================================================
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- =====================================================================
-- 模块 pbl_governance3 表)
-- =====================================================================
-- 1. pbl_tenant 租户主表
CREATE TABLE IF NOT EXISTS pbl_tenant (
id BIGINT NOT NULL AUTO_INCREMENT,
tenant_code VARCHAR(32) NOT NULL COMMENT '租户业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID第一业务列',
tenant_name VARCHAR(128) NOT NULL COMMENT '租户名称',
org_code VARCHAR(32) DEFAULT NULL COMMENT '所属机构编码rbac 侧只读引用)',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/suspended/closedappcodes_kv: pbl_tenant_status',
quota_bp INT NOT NULL DEFAULT 100 COMMENT '蓝图配额',
quota_stu INT NOT NULL DEFAULT 500 COMMENT '学生席位配额',
remark VARCHAR(512) DEFAULT NULL COMMENT '备注',
create_by VARCHAR(32) DEFAULT NULL COMMENT '创建人',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_by VARCHAR(32) DEFAULT NULL COMMENT '更新人',
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_tenant_code (tenant_code),
KEY idx_pbl_tenant_tid_status (tenant_id, status),
KEY idx_pbl_tenant_tid_org (tenant_id, org_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 租户主表';
-- 2. pbl_role_binding 租户内角色绑定(复用 rbac不改 rbac 基表)
CREATE TABLE IF NOT EXISTS pbl_role_binding (
id BIGINT NOT NULL AUTO_INCREMENT,
binding_code VARCHAR(32) NOT NULL COMMENT '绑定业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
user_code VARCHAR(32) NOT NULL COMMENT '用户编码rbac 只读引用)',
role_code VARCHAR(32) NOT NULL COMMENT '角色编码teacher/assistant/student/observer/adminappcodes_kv: pbl_role',
scope_type VARCHAR(16) NOT NULL DEFAULT 'tenant' COMMENT '作用域类型tenant/blueprint/class/team',
scope_code VARCHAR(32) DEFAULT NULL COMMENT '作用域对象编码',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/revoked',
effective_from DATETIME DEFAULT NULL COMMENT '生效起',
effective_to DATETIME DEFAULT NULL COMMENT '生效止',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_role_binding_code (binding_code),
KEY idx_pbl_role_binding_tid_user (tenant_id, user_code, status),
KEY idx_pbl_role_binding_tid_scope (tenant_id, scope_type, scope_code),
KEY idx_pbl_role_binding_tid_role (tenant_id, role_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 租户角色绑定';
-- 3. pbl_audit_log 审计日志append-only
CREATE TABLE IF NOT EXISTS pbl_audit_log (
id BIGINT NOT NULL AUTO_INCREMENT,
audit_code VARCHAR(32) NOT NULL COMMENT '审计业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
actor_type VARCHAR(16) NOT NULL DEFAULT 'user' COMMENT '主体类型user/agent/system',
actor_code VARCHAR(32) DEFAULT NULL COMMENT '主体编码',
action_code VARCHAR(64) NOT NULL COMMENT '动作编码appcodes_kv: pbl_audit_action',
object_type VARCHAR(32) DEFAULT NULL COMMENT '对象类型',
object_code VARCHAR(32) DEFAULT NULL COMMENT '对象编码',
result VARCHAR(16) NOT NULL DEFAULT 'success' COMMENT '结果success/denied/error',
detail TEXT DEFAULT NULL COMMENT '详情 JSON',
trace_id VARCHAR(64) DEFAULT NULL COMMENT '链路追踪ID',
client_ip VARCHAR(64) DEFAULT NULL COMMENT '来源IP',
create_time DATETIME DEFAULT NULL COMMENT '发生时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_audit_code (audit_code),
KEY idx_pbl_audit_tid_time (tenant_id, create_time),
KEY idx_pbl_audit_tid_obj (tenant_id, object_type, object_code),
KEY idx_pbl_audit_tid_actor (tenant_id, actor_type, actor_code),
KEY idx_pbl_audit_tid_trace (tenant_id, trace_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 审计日志append-only';
-- =====================================================================
-- 模块 pbl_blueprint11 表M1a
-- =====================================================================
-- 4. pbl_blueprint 蓝图聚合根
CREATE TABLE IF NOT EXISTS pbl_blueprint (
id BIGINT NOT NULL AUTO_INCREMENT,
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_name VARCHAR(128) NOT NULL COMMENT '蓝图名称',
subject_code VARCHAR(32) DEFAULT NULL COMMENT '学科编码appcodes_kv: pbl_subject',
grade_code VARCHAR(32) DEFAULT NULL COMMENT '学段编码appcodes_kv: pbl_grade',
driving_q TEXT DEFAULT NULL COMMENT '驱动性问题',
summary VARCHAR(1024) DEFAULT NULL COMMENT '蓝图摘要',
duration_hours INT NOT NULL DEFAULT 0 COMMENT '总课时(小时)',
team_size_min INT NOT NULL DEFAULT 1 COMMENT '最小团队人数',
team_size_max INT NOT NULL DEFAULT 6 COMMENT '最大团队人数',
cur_version INT NOT NULL DEFAULT 1 COMMENT '当前版本号(乐观锁)',
quality_level VARCHAR(16) NOT NULL DEFAULT 'L0_draft' COMMENT '质量等级 L0~L4appcodes_kv: pbl_quality_level',
status VARCHAR(16) NOT NULL DEFAULT 'draft' COMMENT '状态draft/validating/validated/compiling/published/archivedappcodes_kv: pbl_bp_status',
source_type VARCHAR(16) NOT NULL DEFAULT 'manual' COMMENT '来源manual/template/fork/agent',
source_code VARCHAR(32) DEFAULT NULL COMMENT '来源对象编码(模板/父蓝图)',
owner_code VARCHAR(32) DEFAULT NULL COMMENT '归属教师',
lock_holder VARCHAR(32) DEFAULT NULL COMMENT '当前编辑锁持有者',
lock_expire DATETIME DEFAULT NULL COMMENT '编辑锁过期时间',
extra TEXT DEFAULT NULL COMMENT '扩展 JSON',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_blueprint_code (bp_code),
KEY idx_pbl_bp_tid_status (tenant_id, status),
KEY idx_pbl_bp_tid_owner (tenant_id, owner_code),
KEY idx_pbl_bp_tid_subject (tenant_id, subject_code, grade_code),
KEY idx_pbl_bp_tid_quality (tenant_id, quality_level),
KEY idx_pbl_bp_tid_source (tenant_id, source_type, source_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 蓝图聚合根';
-- 5. pbl_blueprint_version 版本快照 + change_deltaappend-only
CREATE TABLE IF NOT EXISTS pbl_blueprint_version (
id BIGINT NOT NULL AUTO_INCREMENT,
version_code VARCHAR(32) NOT NULL COMMENT '版本业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL COMMENT '版本号(单调递增)',
snapshot LONGTEXT DEFAULT NULL COMMENT '蓝图全量快照 JSON',
change_delta LONGTEXT DEFAULT NULL COMMENT '相对上一版本的差异 JSON可回放',
checksum VARCHAR(64) DEFAULT NULL COMMENT '快照 sha256',
reason VARCHAR(512) DEFAULT NULL COMMENT '变更原因',
created_by VARCHAR(32) DEFAULT NULL COMMENT '提交人',
create_time DATETIME DEFAULT NULL COMMENT '提交时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_bp_version_code (version_code),
UNIQUE KEY uk_pbl_bp_version_no (tenant_id, bp_code, version_no),
KEY idx_pbl_bp_version_tid_bp (tenant_id, bp_code, create_time),
KEY idx_pbl_bp_version_tid_ck (tenant_id, checksum)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 蓝图版本快照append-only';
-- 6. pbl_blueprint_lock 编辑锁
CREATE TABLE IF NOT EXISTS pbl_blueprint_lock (
id BIGINT NOT NULL AUTO_INCREMENT,
lock_code VARCHAR(32) NOT NULL COMMENT '锁业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
holder_code VARCHAR(32) NOT NULL COMMENT '持有者',
session_id VARCHAR(64) DEFAULT NULL COMMENT '会话ID',
lock_state VARCHAR(16) NOT NULL DEFAULT 'held' COMMENT '锁状态held/released/expired/forced',
acquired_at DATETIME DEFAULT NULL COMMENT '获取时间',
expire_at DATETIME DEFAULT NULL COMMENT '过期时间',
released_at DATETIME DEFAULT NULL COMMENT '释放时间',
version_no INT NOT NULL DEFAULT 0 COMMENT '加锁时版本号',
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_bp_lock_code (lock_code),
UNIQUE KEY uk_pbl_bp_lock_active (tenant_id, bp_code, lock_state),
KEY idx_pbl_bp_lock_tid_holder (tenant_id, holder_code),
KEY idx_pbl_bp_lock_tid_expire (tenant_id, expire_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 蓝图编辑锁';
-- 7. pbl_subobject 7 类子对象泛化主表
CREATE TABLE IF NOT EXISTS pbl_subobject (
id BIGINT NOT NULL AUTO_INCREMENT,
obj_code VARCHAR(32) NOT NULL COMMENT '子对象业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '所属蓝图',
obj_type VARCHAR(32) NOT NULL COMMENT '子对象类型objective/stage/task/artifact_spec/rubric_ref/resource_ref/assessment_planappcodes_kv: pbl_subobj_type',
parent_code VARCHAR(32) DEFAULT NULL COMMENT '父子对象编码(树形)',
obj_name VARCHAR(128) NOT NULL COMMENT '名称',
obj_summary VARCHAR(1024) DEFAULT NULL COMMENT '摘要',
sort_no INT NOT NULL DEFAULT 0 COMMENT '同级排序',
payload LONGTEXT DEFAULT NULL COMMENT '类型化载荷 JSON泛化契约',
schema_ver VARCHAR(16) NOT NULL DEFAULT 'v1' COMMENT '载荷 schema 版本',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/disabled/deleted',
version_no INT NOT NULL DEFAULT 1 COMMENT '乐观锁版本号',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_subobject_code (obj_code),
KEY idx_pbl_subobj_tid_bp_type (tenant_id, bp_code, obj_type, sort_no),
KEY idx_pbl_subobj_tid_parent (tenant_id, parent_code),
KEY idx_pbl_subobj_tid_status (tenant_id, status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 蓝图 7 类子对象泛化主表';
-- 8. pbl_subobject_attr 子对象泛化属性KV
CREATE TABLE IF NOT EXISTS pbl_subobject_attr (
id BIGINT NOT NULL AUTO_INCREMENT,
attr_code VARCHAR(32) NOT NULL COMMENT '属性业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
obj_code VARCHAR(32) NOT NULL COMMENT '子对象编码',
attr_key VARCHAR(64) NOT NULL COMMENT '属性键',
attr_value TEXT DEFAULT NULL COMMENT '属性值',
value_type VARCHAR(16) NOT NULL DEFAULT 'string' COMMENT '值类型string/int/float/bool/json/date',
required_flag TINYINT NOT NULL DEFAULT 0 COMMENT '是否必填 0否1是',
sort_no INT NOT NULL DEFAULT 0 COMMENT '排序',
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_subobj_attr_code (attr_code),
UNIQUE KEY uk_pbl_subobj_attr_key (tenant_id, obj_code, attr_key),
KEY idx_pbl_subobj_attr_tid_obj (tenant_id, obj_code, sort_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 子对象泛化属性 KV';
-- 9. pbl_subobject_rel 子对象间关系
CREATE TABLE IF NOT EXISTS pbl_subobject_rel (
id BIGINT NOT NULL AUTO_INCREMENT,
rel_code VARCHAR(32) NOT NULL COMMENT '关系业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '所属蓝图',
src_obj_code VARCHAR(32) NOT NULL COMMENT '源子对象',
dst_obj_code VARCHAR(32) NOT NULL COMMENT '目标子对象',
rel_type VARCHAR(32) NOT NULL COMMENT '关系类型depends_on/produces/assesses/references/containsappcodes_kv: pbl_rel_type',
weight DECIMAL(6,3) NOT NULL DEFAULT 1.000 COMMENT '关系权重',
remark VARCHAR(512) DEFAULT NULL COMMENT '备注',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_subobj_rel_code (rel_code),
UNIQUE KEY uk_pbl_subobj_rel_triple (tenant_id, src_obj_code, dst_obj_code, rel_type),
KEY idx_pbl_subobj_rel_tid_bp (tenant_id, bp_code, rel_type),
KEY idx_pbl_subobj_rel_tid_dst (tenant_id, dst_obj_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 子对象关系';
-- 10. pbl_subobject_asset 子对象资产引用(只读引用平台资产,不落资产本体)
CREATE TABLE IF NOT EXISTS pbl_subobject_asset (
id BIGINT NOT NULL AUTO_INCREMENT,
asset_code VARCHAR(32) NOT NULL COMMENT '资产引用业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
obj_code VARCHAR(32) NOT NULL COMMENT '子对象编码',
asset_kind VARCHAR(32) NOT NULL COMMENT '资产种类image/model/audio/video/doc/urlappcodes_kv: pbl_asset_kind',
asset_uri VARCHAR(512) DEFAULT NULL COMMENT '资产 URI',
asset_name VARCHAR(128) DEFAULT NULL COMMENT '资产名称',
mime_type VARCHAR(64) DEFAULT NULL COMMENT 'MIME',
size_bytes BIGINT NOT NULL DEFAULT 0 COMMENT '字节数',
checksum VARCHAR(64) DEFAULT NULL COMMENT 'sha256',
license_code VARCHAR(32) DEFAULT NULL COMMENT '许可编码',
sort_no INT NOT NULL DEFAULT 0 COMMENT '排序',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_subobj_asset_code (asset_code),
KEY idx_pbl_subobj_asset_tid_obj (tenant_id, obj_code, sort_no),
KEY idx_pbl_subobj_asset_tid_kind (tenant_id, asset_kind)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 子对象资产引用';
-- 11. pbl_subobject_ref 子对象对复用域对象的只读引用world/scene/entity/script
CREATE TABLE IF NOT EXISTS pbl_subobject_ref (
id BIGINT NOT NULL AUTO_INCREMENT,
ref_code VARCHAR(32) NOT NULL COMMENT '引用业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
obj_code VARCHAR(32) NOT NULL COMMENT '子对象编码',
ref_module VARCHAR(32) NOT NULL COMMENT '被引用模块world/scene/entity/script_engine/scense/scense_game',
ref_table VARCHAR(64) NOT NULL COMMENT '被引用表名',
ref_id BIGINT DEFAULT NULL COMMENT '被引用行 id',
ref_biz_code VARCHAR(64) DEFAULT NULL COMMENT '被引用业务编码',
ref_mode VARCHAR(16) NOT NULL DEFAULT 'readonly' COMMENT '引用模式readonlyPhase 0/1 唯一合法值)',
snapshot_json TEXT DEFAULT NULL COMMENT '引用时只读快照(不改基表)',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_subobj_ref_code (ref_code),
KEY idx_pbl_subobj_ref_tid_obj (tenant_id, obj_code),
KEY idx_pbl_subobj_ref_tid_mod (tenant_id, ref_module, ref_table, ref_biz_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 子对象对复用域只读引用';
-- 12. pbl_subobject_review 子对象评审意见append-only
CREATE TABLE IF NOT EXISTS pbl_subobject_review (
id BIGINT NOT NULL AUTO_INCREMENT,
review_code VARCHAR(32) NOT NULL COMMENT '评审业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '子对象编码(空=蓝图级)',
reviewer_type VARCHAR(16) NOT NULL DEFAULT 'user' COMMENT '评审主体user/agent_critic/agent_designer',
reviewer_code VARCHAR(32) DEFAULT NULL COMMENT '评审主体编码',
verdict VARCHAR(16) NOT NULL DEFAULT 'comment' COMMENT '结论pass/comment/revise/reject',
score DECIMAL(6,2) DEFAULT NULL COMMENT '评分',
comment TEXT DEFAULT NULL COMMENT '意见正文',
create_time DATETIME DEFAULT NULL COMMENT '评审时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_subobj_review_code (review_code),
KEY idx_pbl_subobj_review_tid_bp (tenant_id, bp_code, create_time),
KEY idx_pbl_subobj_review_tid_obj (tenant_id, obj_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 子对象评审意见append-only';
-- 13. pbl_blueprint_tag 蓝图标签
CREATE TABLE IF NOT EXISTS pbl_blueprint_tag (
id BIGINT NOT NULL AUTO_INCREMENT,
tag_code VARCHAR(32) NOT NULL COMMENT '标签业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
tag_key VARCHAR(64) NOT NULL COMMENT '标签键',
tag_value VARCHAR(128) DEFAULT NULL COMMENT '标签值',
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_bp_tag_code (tag_code),
UNIQUE KEY uk_pbl_bp_tag_kv (tenant_id, bp_code, tag_key),
KEY idx_pbl_bp_tag_tid_key (tenant_id, tag_key, tag_value)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 蓝图标签';
-- 14. pbl_blueprint_publish 发布记录append-only
CREATE TABLE IF NOT EXISTS pbl_blueprint_publish (
id BIGINT NOT NULL AUTO_INCREMENT,
publish_code VARCHAR(32) NOT NULL COMMENT '发布业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL COMMENT '发布版本号',
gd_code VARCHAR(32) DEFAULT NULL COMMENT '关联 Game Definition 编码',
channel VARCHAR(32) NOT NULL DEFAULT 'class' COMMENT '发布渠道class/school/public',
target_code VARCHAR(32) DEFAULT NULL COMMENT '目标对象(班级编码)',
publish_state VARCHAR(16) NOT NULL DEFAULT 'published' COMMENT '状态published/revoked',
published_by VARCHAR(32) DEFAULT NULL COMMENT '发布人',
create_time DATETIME DEFAULT NULL COMMENT '发布时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_bp_publish_code (publish_code),
KEY idx_pbl_bp_publish_tid_bp (tenant_id, bp_code, create_time),
KEY idx_pbl_bp_publish_tid_target (tenant_id, channel, target_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 蓝图发布记录append-only';
-- =====================================================================
-- 模块 pbl_template2 表M1b
-- =====================================================================
-- 15. pbl_template 模板主表(含离线兜底模板)
CREATE TABLE IF NOT EXISTS pbl_template (
id BIGINT NOT NULL AUTO_INCREMENT,
tpl_code VARCHAR(32) NOT NULL COMMENT '模板业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID__system__ 表示平台内置)',
tpl_name VARCHAR(128) NOT NULL COMMENT '模板名称',
tpl_kind VARCHAR(32) NOT NULL DEFAULT 'blueprint' COMMENT '模板种类blueprint/subobject/rubric',
subject_code VARCHAR(32) DEFAULT NULL COMMENT '适用学科',
grade_code VARCHAR(32) DEFAULT NULL COMMENT '适用学段',
offline_flag TINYINT NOT NULL DEFAULT 0 COMMENT '是否离线兜底模板 0否1是',
body LONGTEXT DEFAULT NULL COMMENT '模板正文 JSON含子对象树',
id_prefix_rule VARCHAR(64) DEFAULT NULL COMMENT '实例化 ID 重映射前缀规则',
version_no INT NOT NULL DEFAULT 1 COMMENT '模板版本',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/disabled',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_template_code (tpl_code),
KEY idx_pbl_template_tid_kind (tenant_id, tpl_kind, status),
KEY idx_pbl_template_tid_subj (tenant_id, subject_code, grade_code),
KEY idx_pbl_template_tid_offline (tenant_id, offline_flag)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 模板主表';
-- 16. pbl_template_item 模板条目(实例化 ID 重映射映射表)
CREATE TABLE IF NOT EXISTS pbl_template_item (
id BIGINT NOT NULL AUTO_INCREMENT,
item_code VARCHAR(32) NOT NULL COMMENT '条目业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
tpl_code VARCHAR(32) NOT NULL COMMENT '模板编码',
tpl_obj_key VARCHAR(64) NOT NULL COMMENT '模板内占位键(如 __OBJ_1__',
obj_type VARCHAR(32) NOT NULL COMMENT '子对象类型',
parent_key VARCHAR(64) DEFAULT NULL COMMENT '父占位键',
payload LONGTEXT DEFAULT NULL COMMENT '条目载荷 JSON',
sort_no INT NOT NULL DEFAULT 0 COMMENT '排序',
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_template_item_code (item_code),
UNIQUE KEY uk_pbl_template_item_key (tenant_id, tpl_code, tpl_obj_key),
KEY idx_pbl_template_item_tid_tpl (tenant_id, tpl_code, sort_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 模板条目';
-- =====================================================================
-- 模块 pbl_validation3 表M214 维 + 5 级质量状态)
-- =====================================================================
-- 17. pbl_validation_ruleset 校验规则集
CREATE TABLE IF NOT EXISTS pbl_validation_ruleset (
id BIGINT NOT NULL AUTO_INCREMENT,
ruleset_code VARCHAR(32) NOT NULL COMMENT '规则集业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
ruleset_name VARCHAR(128) NOT NULL COMMENT '规则集名称',
dim_code VARCHAR(32) NOT NULL COMMENT '维度编码 D01~D14appcodes_kv: pbl_valid_dim',
dim_name VARCHAR(64) NOT NULL COMMENT '维度名称',
rule_expr TEXT DEFAULT NULL COMMENT '规则表达式 JSON确定性可执行',
severity VARCHAR(16) NOT NULL DEFAULT 'error' COMMENT '严重度blocker/error/warning/info',
weight DECIMAL(6,3) NOT NULL DEFAULT 1.000 COMMENT '维度权重',
level_effect VARCHAR(16) DEFAULT NULL COMMENT '对质量等级的影响downgrade/gate/none',
enabled TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用 0否1是',
sort_no INT NOT NULL DEFAULT 0 COMMENT '维度排序',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_valid_ruleset_code (ruleset_code),
KEY idx_pbl_valid_ruleset_tid_dim (tenant_id, dim_code, enabled),
KEY idx_pbl_valid_ruleset_tid_sort (tenant_id, sort_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 14 维校验规则集';
-- 18. pbl_validation_run 校验执行批次
CREATE TABLE IF NOT EXISTS pbl_validation_run (
id BIGINT NOT NULL AUTO_INCREMENT,
run_code VARCHAR(32) NOT NULL COMMENT '执行批次业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL DEFAULT 1 COMMENT '被校验版本号',
ruleset_ver VARCHAR(32) DEFAULT NULL COMMENT '规则集版本',
trigger_type VARCHAR(16) NOT NULL DEFAULT 'manual' COMMENT '触发manual/auto/agent/pre_compile',
run_state VARCHAR(16) NOT NULL DEFAULT 'running' COMMENT '状态running/passed/failed/error',
dims_total INT NOT NULL DEFAULT 14 COMMENT '维度总数',
dims_passed INT NOT NULL DEFAULT 0 COMMENT '通过维度数',
blocker_count INT NOT NULL DEFAULT 0 COMMENT 'blocker 数',
error_count INT NOT NULL DEFAULT 0 COMMENT 'error 数',
warning_count INT NOT NULL DEFAULT 0 COMMENT 'warning 数',
score DECIMAL(6,2) DEFAULT NULL COMMENT '加权得分',
quality_level VARCHAR(16) DEFAULT NULL COMMENT '产出质量等级 L0~L4',
started_at DATETIME DEFAULT NULL COMMENT '开始时间',
finished_at DATETIME DEFAULT NULL COMMENT '结束时间',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_valid_run_code (run_code),
KEY idx_pbl_valid_run_tid_bp (tenant_id, bp_code, create_time),
KEY idx_pbl_valid_run_tid_state (tenant_id, run_state),
KEY idx_pbl_valid_run_tid_level (tenant_id, quality_level)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 校验执行批次';
-- 19. pbl_validation_finding 校验发现明细append-only 留痕)
CREATE TABLE IF NOT EXISTS pbl_validation_finding (
id BIGINT NOT NULL AUTO_INCREMENT,
finding_code VARCHAR(32) NOT NULL COMMENT '发现业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
run_code VARCHAR(32) NOT NULL COMMENT '执行批次编码',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
dim_code VARCHAR(32) NOT NULL COMMENT '维度编码',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '命中子对象',
severity VARCHAR(16) NOT NULL DEFAULT 'error' COMMENT '严重度',
finding_msg VARCHAR(1024) DEFAULT NULL COMMENT '问题描述',
suggestion VARCHAR(1024) DEFAULT NULL COMMENT '修复建议',
evidence_json TEXT DEFAULT NULL COMMENT '证据 JSON',
create_time DATETIME DEFAULT NULL COMMENT '产生时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_valid_finding_code (finding_code),
KEY idx_pbl_valid_finding_tid_run (tenant_id, run_code, dim_code),
KEY idx_pbl_valid_finding_tid_bp (tenant_id, bp_code, create_time),
KEY idx_pbl_valid_finding_tid_obj (tenant_id, obj_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 校验发现明细append-only';
-- =====================================================================
-- 模块 pbl_compiler4 表M3a/M3b确定性编译 + Game Definition
-- =====================================================================
-- 20. pbl_compile_job 编译作业
CREATE TABLE IF NOT EXISTS pbl_compile_job (
id BIGINT NOT NULL AUTO_INCREMENT,
job_code VARCHAR(32) NOT NULL COMMENT '作业业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
version_no INT NOT NULL COMMENT '被编译版本号',
run_code VARCHAR(32) DEFAULT NULL COMMENT '前置校验批次(必须 passed',
compiler_ver VARCHAR(32) NOT NULL DEFAULT 'v1' COMMENT '编译器版本',
job_state VARCHAR(16) NOT NULL DEFAULT 'queued' COMMENT '状态queued/running/success/failed/cancelled',
input_checksum VARCHAR(64) DEFAULT NULL COMMENT '输入快照 sha256确定性依据',
output_checksum VARCHAR(64) DEFAULT NULL COMMENT '输出 Game Definition sha256',
gd_code VARCHAR(32) DEFAULT NULL COMMENT '产出 Game Definition 编码',
error_msg VARCHAR(1024) DEFAULT NULL COMMENT '错误信息',
started_at DATETIME DEFAULT NULL,
finished_at DATETIME DEFAULT NULL,
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_compile_job_code (job_code),
KEY idx_pbl_compile_job_tid_bp (tenant_id, bp_code, create_time),
KEY idx_pbl_compile_job_tid_state (tenant_id, job_state),
KEY idx_pbl_compile_job_tid_ick (tenant_id, input_checksum)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 编译作业';
-- 21. pbl_compile_artifact 编译中间产物
CREATE TABLE IF NOT EXISTS pbl_compile_artifact (
id BIGINT NOT NULL AUTO_INCREMENT,
artifact_code VARCHAR(32) NOT NULL COMMENT '产物业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
job_code VARCHAR(32) NOT NULL COMMENT '编译作业编码',
stage_code VARCHAR(32) NOT NULL COMMENT '编译阶段normalize/resolve/link/optimize/emit',
artifact_kind VARCHAR(32) NOT NULL COMMENT '产物种类node_set/edge_set/rule_set/asset_map/gd_draft',
body LONGTEXT DEFAULT NULL COMMENT '产物正文 JSON',
checksum VARCHAR(64) DEFAULT NULL COMMENT 'sha256',
sort_no INT NOT NULL DEFAULT 0 COMMENT '阶段排序',
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_compile_artifact_code (artifact_code),
KEY idx_pbl_compile_artifact_tid_job (tenant_id, job_code, sort_no),
KEY idx_pbl_compile_artifact_tid_kind (tenant_id, artifact_kind)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 编译中间产物';
-- 22. pbl_game_definition Game Definition编译最终产出
CREATE TABLE IF NOT EXISTS pbl_game_definition (
id BIGINT NOT NULL AUTO_INCREMENT,
gd_code VARCHAR(32) NOT NULL COMMENT 'Game Definition 业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '来源蓝图',
version_no INT NOT NULL COMMENT '来源蓝图版本',
gd_version INT NOT NULL DEFAULT 1 COMMENT 'GD 自身版本',
schema_ver VARCHAR(16) NOT NULL DEFAULT 'gd-v1' COMMENT 'GD schema 版本',
body LONGTEXT DEFAULT NULL COMMENT 'Game Definition 全文 JSONschema 校验通过)',
checksum VARCHAR(64) DEFAULT NULL COMMENT 'sha256同输入同输出',
world_ref VARCHAR(64) DEFAULT NULL COMMENT '引用世界编码(只读)',
scene_ref VARCHAR(64) DEFAULT NULL COMMENT '引用场景编码(只读)',
script_ref VARCHAR(64) DEFAULT NULL COMMENT '引用脚本编码(只读)',
gd_state VARCHAR(16) NOT NULL DEFAULT 'draft' COMMENT '状态draft/validated/published/archived',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_gd_code (gd_code),
UNIQUE KEY uk_pbl_gd_bp_ver (tenant_id, bp_code, version_no, gd_version),
KEY idx_pbl_gd_tid_state (tenant_id, gd_state),
KEY idx_pbl_gd_tid_ck (tenant_id, checksum),
KEY idx_pbl_gd_tid_world (tenant_id, world_ref, scene_ref)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL Game Definition';
-- 23. pbl_compile_log 编译日志append-only
CREATE TABLE IF NOT EXISTS pbl_compile_log (
id BIGINT NOT NULL AUTO_INCREMENT,
log_code VARCHAR(32) NOT NULL COMMENT '日志业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
job_code VARCHAR(32) NOT NULL COMMENT '编译作业编码',
stage_code VARCHAR(32) DEFAULT NULL COMMENT '阶段',
log_level VARCHAR(16) NOT NULL DEFAULT 'info' COMMENT '级别debug/info/warn/error',
log_msg VARCHAR(2048) DEFAULT NULL COMMENT '日志正文',
context_json TEXT DEFAULT NULL COMMENT '上下文 JSON',
seq_no BIGINT NOT NULL DEFAULT 0 COMMENT '作业内单调序号',
create_time DATETIME DEFAULT NULL COMMENT '记录时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_compile_log_code (log_code),
KEY idx_pbl_compile_log_tid_job (tenant_id, job_code, seq_no),
KEY idx_pbl_compile_log_tid_level (tenant_id, log_level, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 编译日志append-only';
-- =====================================================================
-- 模块 pbl_agent_runtime4 表M4a/M4bDesigner/Critic + fail-closed 裁决)
-- =====================================================================
-- 24. pbl_agent_session Agent 会话
CREATE TABLE IF NOT EXISTS pbl_agent_session (
id BIGINT NOT NULL AUTO_INCREMENT,
session_code VARCHAR(32) NOT NULL COMMENT '会话业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) DEFAULT NULL COMMENT '关联蓝图',
agent_role VARCHAR(32) NOT NULL COMMENT 'Agent 角色designer/criticappcodes_kv: pbl_agent_role',
model_name VARCHAR(64) DEFAULT NULL COMMENT '使用的平台模型注册名',
session_state VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/paused/closed/error',
owner_code VARCHAR(32) DEFAULT NULL COMMENT '发起用户',
turn_count INT NOT NULL DEFAULT 0 COMMENT '轮次数',
tool_call_count INT NOT NULL DEFAULT 0 COMMENT '工具调用总数',
tool_deny_count INT NOT NULL DEFAULT 0 COMMENT '被拒绝调用数',
context_json LONGTEXT DEFAULT NULL COMMENT '会话上下文 JSON',
started_at DATETIME DEFAULT NULL,
closed_at DATETIME DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_agent_session_code (session_code),
KEY idx_pbl_agent_session_tid_bp (tenant_id, bp_code, session_state),
KEY idx_pbl_agent_session_tid_role (tenant_id, agent_role),
KEY idx_pbl_agent_session_tid_owner (tenant_id, owner_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL Agent 会话';
-- 25. pbl_agent_message Agent 消息append-only
CREATE TABLE IF NOT EXISTS pbl_agent_message (
id BIGINT NOT NULL AUTO_INCREMENT,
msg_code VARCHAR(32) NOT NULL COMMENT '消息业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
session_code VARCHAR(32) NOT NULL COMMENT '会话编码',
turn_no INT NOT NULL DEFAULT 0 COMMENT '轮次号',
role VARCHAR(16) NOT NULL COMMENT '角色system/user/assistant/tool',
content LONGTEXT DEFAULT NULL COMMENT '消息正文',
token_in INT NOT NULL DEFAULT 0 COMMENT '输入 token',
token_out INT NOT NULL DEFAULT 0 COMMENT '输出 token',
latency_ms INT NOT NULL DEFAULT 0 COMMENT '耗时毫秒',
create_time DATETIME DEFAULT NULL COMMENT '产生时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_agent_msg_code (msg_code),
KEY idx_pbl_agent_msg_tid_sess (tenant_id, session_code, turn_no),
KEY idx_pbl_agent_msg_tid_time (tenant_id, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL Agent 消息append-only';
-- 26. pbl_agent_tool_call 工具调用记录append-only
CREATE TABLE IF NOT EXISTS pbl_agent_tool_call (
id BIGINT NOT NULL AUTO_INCREMENT,
call_code VARCHAR(32) NOT NULL COMMENT '调用业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
session_code VARCHAR(32) NOT NULL COMMENT '会话编码',
turn_no INT NOT NULL DEFAULT 0 COMMENT '轮次号',
tool_name VARCHAR(64) NOT NULL COMMENT '工具名13 启用 / 9 禁用白名单外)',
tool_state VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT '状态pending/allowed/executed/denied/error',
params_json LONGTEXT DEFAULT NULL COMMENT '入参 JSON',
result_json LONGTEXT DEFAULT NULL COMMENT '返回 JSON',
latency_ms INT NOT NULL DEFAULT 0 COMMENT '耗时毫秒',
create_time DATETIME DEFAULT NULL COMMENT '调用时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_agent_call_code (call_code),
KEY idx_pbl_agent_call_tid_sess (tenant_id, session_code, turn_no),
KEY idx_pbl_agent_call_tid_tool (tenant_id, tool_name, tool_state),
KEY idx_pbl_agent_call_tid_time (tenant_id, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL Agent 工具调用append-only';
-- 27. pbl_agent_arbitration fail-closed 裁决记录append-only
CREATE TABLE IF NOT EXISTS pbl_agent_arbitration (
id BIGINT NOT NULL AUTO_INCREMENT,
arb_code VARCHAR(32) NOT NULL COMMENT '裁决业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
session_code VARCHAR(32) DEFAULT NULL COMMENT '会话编码',
call_code VARCHAR(32) DEFAULT NULL COMMENT '关联调用编码',
tool_name VARCHAR(64) NOT NULL COMMENT '被裁决工具名',
decision VARCHAR(16) NOT NULL DEFAULT 'deny' COMMENT '裁决allow/deny默认 denyfail-closed',
deny_reason VARCHAR(32) NOT NULL DEFAULT 'not_in_whitelist' COMMENT '拒绝原因not_in_whitelist/invalid_params/missing_tenant_context/write_protected/quota_exceeded',
rule_ref VARCHAR(64) DEFAULT NULL COMMENT '命中规则引用',
detail_json TEXT DEFAULT NULL COMMENT '裁决详情 JSON',
arbiter VARCHAR(32) NOT NULL DEFAULT 'system' COMMENT '裁决者',
create_time DATETIME DEFAULT NULL COMMENT '裁决时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_agent_arb_code (arb_code),
KEY idx_pbl_agent_arb_tid_tool (tenant_id, tool_name, decision),
KEY idx_pbl_agent_arb_tid_sess (tenant_id, session_code, create_time),
KEY idx_pbl_agent_arb_tid_reason (tenant_id, deny_reason)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL Agent fail-closed 裁决记录append-only';
-- =====================================================================
-- 模块 pbl_evidence4 表M5a/M5b产出物采集 + 证据幂等)
-- =====================================================================
-- 28. pbl_artifact 学生产出物
CREATE TABLE IF NOT EXISTS pbl_artifact (
id BIGINT NOT NULL AUTO_INCREMENT,
artifact_code VARCHAR(32) NOT NULL COMMENT '产出物业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
session_code VARCHAR(32) DEFAULT NULL COMMENT '运行会话编码',
team_code VARCHAR(32) DEFAULT NULL COMMENT '团队编码',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '对应产出物规格子对象',
artifact_name VARCHAR(128) NOT NULL COMMENT '产出物名称',
artifact_kind VARCHAR(32) NOT NULL COMMENT '种类doc/image/model/audio/video/code/log/chatappcodes_kv: pbl_artifact_kind',
storage_uri VARCHAR(512) DEFAULT NULL COMMENT '存储 URI',
mime_type VARCHAR(64) DEFAULT NULL COMMENT 'MIME',
size_bytes BIGINT NOT NULL DEFAULT 0 COMMENT '字节数',
checksum VARCHAR(64) DEFAULT NULL COMMENT 'sha256',
content_text LONGTEXT DEFAULT NULL COMMENT '文本类正文(可索引)',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/archived/deleted',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_artifact_code (artifact_code),
KEY idx_pbl_artifact_tid_bp (tenant_id, bp_code, artifact_kind),
KEY idx_pbl_artifact_tid_team (tenant_id, team_code),
KEY idx_pbl_artifact_tid_sess (tenant_id, session_code),
KEY idx_pbl_artifact_tid_ck (tenant_id, checksum)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 学生产出物';
-- 29. pbl_evidence 证据主表
CREATE TABLE IF NOT EXISTS pbl_evidence (
id BIGINT NOT NULL AUTO_INCREMENT,
evidence_code VARCHAR(32) NOT NULL COMMENT '证据业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
artifact_code VARCHAR(32) DEFAULT NULL COMMENT '关联产出物',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '关联子对象(学习目标/任务)',
team_code VARCHAR(32) DEFAULT NULL COMMENT '团队编码',
student_code VARCHAR(32) DEFAULT NULL COMMENT '学生编码',
evidence_type VARCHAR(32) NOT NULL COMMENT '证据类型artifact/event/dialogue/state_snapshot/metricappcodes_kv: pbl_evidence_type',
idem_key VARCHAR(128) NOT NULL COMMENT '幂等键tenant+bp+type+source+hash',
payload LONGTEXT DEFAULT NULL COMMENT '证据载荷 JSON',
occurred_at DATETIME DEFAULT NULL COMMENT '证据发生时间',
trust_level VARCHAR(16) NOT NULL DEFAULT 'normal' COMMENT '可信度high/normal/low',
anonymized TINYINT NOT NULL DEFAULT 0 COMMENT '是否已匿名化 0否1是',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/invalidated',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_evidence_code (evidence_code),
UNIQUE KEY uk_pbl_evidence_idem (tenant_id, idem_key),
KEY idx_pbl_evidence_tid_bp (tenant_id, bp_code, evidence_type),
KEY idx_pbl_evidence_tid_team (tenant_id, team_code, student_code),
KEY idx_pbl_evidence_tid_obj (tenant_id, obj_code),
KEY idx_pbl_evidence_tid_time (tenant_id, occurred_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 证据主表(幂等键唯一)';
-- 30. pbl_evidence_idem 证据幂等提交台账append-only
CREATE TABLE IF NOT EXISTS pbl_evidence_idem (
id BIGINT NOT NULL AUTO_INCREMENT,
idem_code VARCHAR(32) NOT NULL COMMENT '幂等台账业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
idem_key VARCHAR(128) NOT NULL COMMENT '幂等键',
evidence_code VARCHAR(32) DEFAULT NULL COMMENT '首次落库证据编码',
submit_count INT NOT NULL DEFAULT 1 COMMENT '累计提交次数(重复提交只增此计数,不新增证据行)',
last_payload_hash VARCHAR(64) DEFAULT NULL COMMENT '最近一次载荷 hash',
conflict_flag TINYINT NOT NULL DEFAULT 0 COMMENT '是否载荷冲突 0否1是',
source_channel VARCHAR(32) DEFAULT NULL COMMENT '来源渠道runtime/manual/agent/import',
create_time DATETIME DEFAULT NULL COMMENT '首次提交时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_evidence_idem_code (idem_code),
UNIQUE KEY uk_pbl_evidence_idem_key (tenant_id, idem_key),
KEY idx_pbl_evidence_idem_tid_time (tenant_id, create_time),
KEY idx_pbl_evidence_idem_tid_conf (tenant_id, conflict_flag)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 证据幂等台账append-only';
-- 31. pbl_evidence_binding 证据与复用域对象绑定(零改表关联)
CREATE TABLE IF NOT EXISTS pbl_evidence_binding (
id BIGINT NOT NULL AUTO_INCREMENT,
bind_code VARCHAR(32) NOT NULL COMMENT '绑定业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
evidence_code VARCHAR(32) NOT NULL COMMENT '证据编码',
ref_module VARCHAR(32) NOT NULL COMMENT '复用域模块world/scene/entity/script_engine/scense_runtime/scense/scense_game',
ref_table VARCHAR(64) NOT NULL COMMENT '复用域表名',
ref_id BIGINT DEFAULT NULL COMMENT '复用域行 id',
ref_biz_code VARCHAR(64) DEFAULT NULL COMMENT '复用域业务编码',
bind_role VARCHAR(32) NOT NULL DEFAULT 'source' COMMENT '绑定角色source/target/context',
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_evidence_binding_code (bind_code),
KEY idx_pbl_evidence_binding_tid_ev (tenant_id, evidence_code),
KEY idx_pbl_evidence_binding_tid_ref (tenant_id, ref_module, ref_table, ref_biz_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 证据与复用域绑定(零改表)';
-- =====================================================================
-- 模块 pbl_assessment3 表M6Rubric 加权评估与报告)
-- =====================================================================
-- 32. pbl_rubric 量规主表
CREATE TABLE IF NOT EXISTS pbl_rubric (
id BIGINT NOT NULL AUTO_INCREMENT,
rubric_code VARCHAR(32) NOT NULL COMMENT '量规业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) DEFAULT NULL COMMENT '关联蓝图(空=通用量规)',
rubric_name VARCHAR(128) NOT NULL COMMENT '量规名称',
level_count INT NOT NULL DEFAULT 5 COMMENT '等级数(默认 5 级)',
total_weight DECIMAL(8,3) NOT NULL DEFAULT 100.000 COMMENT '总权重',
pass_score DECIMAL(6,2) NOT NULL DEFAULT 60.00 COMMENT '及格分',
grade_rule TEXT DEFAULT NULL COMMENT '等级映射规则 JSON',
is_default TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认量规 0否1是',
version_no INT NOT NULL DEFAULT 1 COMMENT '版本',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/disabled',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_rubric_code (rubric_code),
KEY idx_pbl_rubric_tid_bp (tenant_id, bp_code, status),
KEY idx_pbl_rubric_tid_default (tenant_id, is_default)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 量规主表';
-- 33. pbl_rubric_criterion 量规评价维度条目
CREATE TABLE IF NOT EXISTS pbl_rubric_criterion (
id BIGINT NOT NULL AUTO_INCREMENT,
crit_code VARCHAR(32) NOT NULL COMMENT '条目业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
rubric_code VARCHAR(32) NOT NULL COMMENT '量规编码',
obj_code VARCHAR(32) DEFAULT NULL COMMENT '关联学习目标子对象',
crit_name VARCHAR(128) NOT NULL COMMENT '维度名称',
crit_desc VARCHAR(1024) DEFAULT NULL COMMENT '维度说明',
weight DECIMAL(6,3) NOT NULL DEFAULT 1.000 COMMENT '权重',
level_desc TEXT DEFAULT NULL COMMENT '各等级描述 JSONlevel -> desc',
max_score DECIMAL(6,2) NOT NULL DEFAULT 5.00 COMMENT '最高分',
evidence_required TINYINT NOT NULL DEFAULT 1 COMMENT '是否必须有证据支撑 0否1是',
sort_no INT NOT NULL DEFAULT 0 COMMENT '排序',
create_time DATETIME DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_rubric_crit_code (crit_code),
KEY idx_pbl_rubric_crit_tid_rub (tenant_id, rubric_code, sort_no),
KEY idx_pbl_rubric_crit_tid_obj (tenant_id, obj_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 量规评价维度';
-- 34. pbl_assessment_report 评估报告
CREATE TABLE IF NOT EXISTS pbl_assessment_report (
id BIGINT NOT NULL AUTO_INCREMENT,
report_code VARCHAR(32) NOT NULL COMMENT '报告业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bp_code VARCHAR(32) NOT NULL COMMENT '蓝图编码',
rubric_code VARCHAR(32) NOT NULL COMMENT '量规编码',
team_code VARCHAR(32) DEFAULT NULL COMMENT '团队编码',
student_code VARCHAR(32) DEFAULT NULL COMMENT '学生编码(团队报告时为空)',
scope_type VARCHAR(16) NOT NULL DEFAULT 'team' COMMENT '范围team/student/blueprint',
score_total DECIMAL(6,2) NOT NULL DEFAULT 0.00 COMMENT '加权总分(可复算)',
grade_code VARCHAR(16) DEFAULT NULL COMMENT '等级 A/B/C/D/E',
detail_json LONGTEXT DEFAULT NULL COMMENT '逐维度得分明细 JSON含证据引用',
evidence_count INT NOT NULL DEFAULT 0 COMMENT '引用证据数',
missing_evidence_count INT NOT NULL DEFAULT 0 COMMENT '缺证据维度数',
assessor_type VARCHAR(16) NOT NULL DEFAULT 'agent' COMMENT '评估者agent/user/mixed',
assessor_code VARCHAR(32) DEFAULT NULL COMMENT '评估者编码',
report_state VARCHAR(16) NOT NULL DEFAULT 'draft' COMMENT '状态draft/published/archived',
calc_checksum VARCHAR(64) DEFAULT NULL COMMENT '复算校验 sha256',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_assess_report_code (report_code),
KEY idx_pbl_assess_report_tid_bp (tenant_id, bp_code, scope_type),
KEY idx_pbl_assess_report_tid_team (tenant_id, team_code, student_code),
KEY idx_pbl_assess_report_tid_state (tenant_id, report_state),
KEY idx_pbl_assess_report_tid_grade (tenant_id, grade_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 评估报告';
-- =====================================================================
-- 模块 pbl_domain_ext1 表M8world/scene/entity 租户·班级·团队关联,不改基表)
-- =====================================================================
-- 35. pbl_domain_binding 复用域对象绑定(零改表唯一关联落点)
CREATE TABLE IF NOT EXISTS pbl_domain_binding (
id BIGINT NOT NULL AUTO_INCREMENT,
bind_code VARCHAR(32) NOT NULL COMMENT '绑定业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
bind_kind VARCHAR(32) NOT NULL COMMENT '绑定种类tenant_world/class_scene/team_entity/bp_script/session_worldappcodes_kv: pbl_bind_kind',
class_code VARCHAR(32) DEFAULT NULL COMMENT '班级编码',
team_code VARCHAR(32) DEFAULT NULL COMMENT '团队编码',
bp_code VARCHAR(32) DEFAULT NULL COMMENT '蓝图编码',
session_code VARCHAR(32) DEFAULT NULL COMMENT '运行会话编码',
ref_module VARCHAR(32) NOT NULL COMMENT '复用域模块world/scene/entity/script_engine/scense_runtime/scense/scense_game',
ref_table VARCHAR(64) NOT NULL COMMENT '复用域表名',
ref_id BIGINT DEFAULT NULL COMMENT '复用域行 id',
ref_biz_code VARCHAR(64) DEFAULT NULL COMMENT '复用域业务编码',
access_mode VARCHAR(16) NOT NULL DEFAULT 'readonly' COMMENT '访问模式readonlyPhase 0/1 唯一合法值,写保护)',
bind_state VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT '状态active/released',
extra_json TEXT DEFAULT NULL COMMENT '扩展 JSON',
create_by VARCHAR(32) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(32) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_domain_binding_code (bind_code),
UNIQUE KEY uk_pbl_domain_binding_ref (tenant_id, bind_kind, ref_module, ref_table, ref_biz_code),
KEY idx_pbl_domain_binding_tid_class (tenant_id, class_code, team_code),
KEY idx_pbl_domain_binding_tid_bp (tenant_id, bp_code),
KEY idx_pbl_domain_binding_tid_sess (tenant_id, session_code),
KEY idx_pbl_domain_binding_tid_ref (tenant_id, ref_module, ref_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 复用域绑定(零改表)';
-- =====================================================================
-- 模块 pbl_analytics1 表M10分析看板快照append-only
-- =====================================================================
-- 36. pbl_analytics_snapshot 分析快照append-only
CREATE TABLE IF NOT EXISTS pbl_analytics_snapshot (
id BIGINT NOT NULL AUTO_INCREMENT,
snap_code VARCHAR(32) NOT NULL COMMENT '快照业务主键',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
metric_group VARCHAR(32) NOT NULL COMMENT '指标组blueprint/validation/compile/runtime/evidence/assessmentappcodes_kv: pbl_metric_group',
scope_type VARCHAR(16) NOT NULL DEFAULT 'tenant' COMMENT '范围tenant/class/team/blueprint',
scope_code VARCHAR(32) DEFAULT NULL COMMENT '范围对象编码',
period_key VARCHAR(16) NOT NULL COMMENT '周期键2026-09 / 2026-W38 / 2026-09-15',
metrics_json LONGTEXT DEFAULT NULL COMMENT '指标 JSON计数/均值/分布)',
anonymized TINYINT NOT NULL DEFAULT 1 COMMENT '是否匿名聚合 0否1是KDB 侧强制 1',
sample_size INT NOT NULL DEFAULT 0 COMMENT '样本量',
computed_at DATETIME DEFAULT NULL COMMENT '计算时间',
create_time DATETIME DEFAULT NULL COMMENT '入库时间append-only无 update_time',
PRIMARY KEY (id),
UNIQUE KEY uk_pbl_analytics_snap_code (snap_code),
UNIQUE KEY uk_pbl_analytics_snap_dim (tenant_id, metric_group, scope_type, scope_code, period_key),
KEY idx_pbl_analytics_snap_tid_group (tenant_id, metric_group, period_key),
KEY idx_pbl_analytics_snap_tid_scope (tenant_id, scope_type, scope_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL 分析看板快照append-only';
SET FOREIGN_KEY_CHECKS = 1;
-- =====================================================================
-- 契约自检(供 build.sh / QC grep 核验)
-- 表总数36pbl_governance 3 + pbl_blueprint 11 + pbl_template 2 + pbl_validation 3
-- + pbl_compiler 4 + pbl_agent_runtime 4 + pbl_evidence 4 + pbl_assessment 3
-- + pbl_domain_ext 1 + pbl_analytics 1 = 36
-- append-only 表11无 update_time
-- pbl_audit_log / pbl_blueprint_version / pbl_subobject_review / pbl_blueprint_publish
-- pbl_validation_finding / pbl_compile_log / pbl_agent_message / pbl_agent_tool_call
-- pbl_agent_arbitration / pbl_evidence_idem / pbl_analytics_snapshot
-- 零命中项mariadb 方言硬约束):
-- FOREIGN KEY / REFERENCES / ENUM( / SERIAL / BIGSERIAL / nextval / TIMESTAMP
-- 全部索引以 tenant_id 打头;每表 id BIGINT NOT NULL AUTO_INCREMENT + xxx_code 唯一业务主键。
-- =====================================================================

View File

@ -1,120 +1,225 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""DDL 幂等修正器:把 tenant_id 提升为每张表的物理首列QC 第三轮 #1 配套)。
"""DDL 补丁工具:保证每张表 tenant_id 为第一个业务列(多租户强制打头)。
背景
----
data-model.md tenant_id 强制打头有两层含义
(a) 键前缀契约 每个复合 UNIQUE KEY / KEY 的首列必须是 tenant_id影响分区裁剪与查询计划**必须成立**
(b) 物理列序 CREATE TABLE 的第一个列定义是 tenant_id可读性/评审口径
核验器 gate_64 (a) 为判定项(b) INFO本脚本把 (b) 也补齐使两种口径同时成立
**幂等**已经是首列的表不做任何改动重复执行结果一致
docs/01-design/data-model.md 硬约束36 表全部 mariadb 方言 FK/ENUM/TIMESTAMP
**tenant_id 强制打头**紧随自增主键 id 之后作为第一个业务列以便
pbl_common 的租户上下文在 CRUD 工厂层统一注入 WHERE tenant_id = ?
历史 DDL含引用模块导出的 subobjects.sql 片段存在 tenant_id 位置漂移
写在表尾或夹在中间本脚本做**幂等**重排
* 已在首位 不动幂等
* 不在首位 抽出该行移动到第一个业务列位置
* 完全缺失 按标准定义补插BIGINT NOT NULL DEFAULT 0 + 注释
用法
----
python3 apps/pbls/scripts/patch_ddl_tenant_first.py \
--file apps/pbls/scripts/ddl/pbls_tables.sql [--dry-run]
# 干跑(只报告,不写盘)
python3 apps/pbls/scripts/patch_ddl_tenant_first.py --dry-run
安全约束
--------
* 只做列定义整行搬移不改列类型不改索引不改表选项不改注释
* 解析失败找不到 tenant_id / 括号不配对时跳过该表并打印 SKIP绝不写坏文件
* --dry-run 只报告将变更的表与行号不落盘
# 就地修补(自动备份 .bak
python3 apps/pbls/scripts/patch_ddl_tenant_first.py --inplace
# 指定文件
python3 apps/pbls/scripts/patch_ddl_tenant_first.py \
--ddl apps/pbls/scripts/ddl/pbls_tables.sql --inplace
# 只校验不修补CI 门禁用,发现漂移退出码 1
python3 apps/pbls/scripts/patch_ddl_tenant_first.py --check
"""
import argparse
import io
import os
import re
import shutil
import sys
CREATE_RX = re.compile(r"CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`\"]?(\w+)[`\"]?\s*\(", re.I)
TENANT_COL_RX = re.compile(r"^\s*[`\"]?tenant_id[`\"]?\s+", re.I)
DEFAULT_DDL = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"scripts", "ddl", "pbls_tables.sql",
)
TENANT_COL = "tenant_id"
TENANT_DEF = " `%s` BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID多租户强制打头'," % TENANT_COL
RE_CREATE = re.compile(
r"^\s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`\"]?(\w+)[`\"]?", re.I
)
RE_COL = re.compile(r"^\s*[`\"]?(\w+)[`\"]?\s+[A-Za-z]")
RE_CONSTRAINT = re.compile(
r"^\s*(PRIMARY\s+KEY|UNIQUE\s+KEY|UNIQUE\s+INDEX|KEY|INDEX|CONSTRAINT|FOREIGN\s+KEY|CHECK)\b",
re.I,
)
RE_FORBIDDEN = re.compile(
r"\b(BIGSERIAL|SERIAL|nextval|FOREIGN\s+KEY|REFERENCES|ENUM\s*\(|TIMESTAMP)\b", re.I
)
def match_paren(text, open_idx):
depth = 0
for i in range(open_idx, len(text)):
if text[i] == "(":
depth += 1
elif text[i] == ")":
depth -= 1
if depth == 0:
class TableBlock(object):
"""一张表的 DDL 行块含起止行号0-based"""
def __init__(self, name, start):
self.name = name
self.start = start # CREATE TABLE 行
self.end = None # 收尾 `)` 行(含)
self.body = [] # (lineno, text) 列/约束行
def first_business_index(self):
"""返回 body 中第一个业务列(非约束行)的下标,无则 None。"""
for i, (_ln, text) in enumerate(self.body):
if RE_CONSTRAINT.match(text):
continue
if RE_COL.match(text):
return i
return -1
return None
def tenant_index(self):
for i, (_ln, text) in enumerate(self.body):
m = RE_COL.match(text)
if m and m.group(1) == TENANT_COL:
return i
return None
def line_of(text, offset):
return text[:offset].count("\n") + 1
def patch(sql):
"""返回 (new_sql, changes, skips)。changes/skips 元素为 (表名, 行号, 说明)。"""
changes, skips = [], []
out = sql
offset_shift = 0
for m in CREATE_RX.finditer(sql):
tbl = m.group(1)
open_idx = m.end() - 1
close_idx = match_paren(sql, open_idx)
if close_idx < 0:
skips.append((tbl, line_of(sql, m.start()), "括号不配对,跳过"))
def parse_blocks(lines):
"""把 DDL 文本切成 TableBlock 列表。"""
blocks = []
cur = None
for i, text in enumerate(lines):
m = RE_CREATE.match(text)
if m:
cur = TableBlock(m.group(1), i)
blocks.append(cur)
continue
body = sql[open_idx + 1:close_idx]
lines = body.split("\n")
tenant_i = None
for i, ln in enumerate(lines):
if TENANT_COL_RX.match(ln):
tenant_i = i
break
if tenant_i is None:
skips.append((tbl, line_of(sql, m.start()), "未找到 tenant_id 列定义,跳过"))
if cur is None:
continue
first_meaningful = None
for i, ln in enumerate(lines):
if ln.strip():
first_meaningful = i
break
if first_meaningful is None or tenant_i == first_meaningful:
continue # 已是首列,幂等无操作
moved = lines.pop(tenant_i)
lines.insert(first_meaningful, moved)
new_body = "\n".join(lines)
start = open_idx + 1 + offset_shift
end = close_idx + offset_shift
out = out[:start] + new_body + out[end:]
offset_shift += len(new_body) - len(body)
changes.append((tbl, line_of(sql, m.start()),
"tenant_id 由第 %d 个列定义提升为物理首列" % (tenant_i - first_meaningful + 1)))
return out, changes, skips
stripped = text.strip()
if stripped.startswith(")"):
cur.end = i
cur = None
continue
if not stripped or stripped.startswith("--") or stripped.startswith("#"):
continue
cur.body.append((i, text))
return blocks
def audit(lines, blocks):
"""返回 (漂移表清单, 缺失表清单, 禁用方言命中清单)。"""
drifted, missing, forbidden = [], [], []
for b in blocks:
ti = b.tenant_index()
fi = b.first_business_index()
if ti is None:
missing.append(b.name)
elif fi is not None and ti != fi:
drifted.append((b.name, ti, fi))
for i, text in enumerate(lines, 1):
m = RE_FORBIDDEN.search(text)
if m and not text.strip().startswith("--"):
forbidden.append((i, m.group(1), text.strip()[:90]))
return drifted, missing, forbidden
def patch(lines, blocks):
"""就地重排/补插 tenant_id返回 (新行列表, 改动计数)。"""
out = list(lines)
changes = 0
# 从后往前处理,避免行号位移
for b in sorted(blocks, key=lambda x: x.start, reverse=True):
ti = b.tenant_index()
fi = b.first_business_index()
if ti is None:
anchor = b.body[fi][0] if fi is not None else (b.start + 1)
out.insert(anchor, TENANT_DEF)
changes += 1
continue
if fi is None or ti == fi:
continue # 幂等:已在首位
src_line = b.body[ti][0]
dst_line = b.body[fi][0]
moved = out.pop(src_line)
if not moved.rstrip().endswith(","):
moved = moved.rstrip() + ","
out.insert(dst_line, moved)
changes += 1
return out, changes
def main():
ap = argparse.ArgumentParser(description="DDL tenant_id 物理首列幂等修正器")
ap.add_argument("--file", required=True, help="DDL 文件路径")
ap.add_argument("--dry-run", action="store_true", help="只报告不落盘")
ap = argparse.ArgumentParser(description="DDL tenant_id 打头幂等补丁")
ap.add_argument("--ddl", default=DEFAULT_DDL, help="DDL 文件路径")
ap.add_argument("--inplace", action="store_true", help="就地写回(自动 .bak 备份)")
ap.add_argument("--dry-run", action="store_true", help="只报告不写盘")
ap.add_argument("--check", action="store_true",
help="CI 门禁模式:发现漂移/缺失/禁用方言则退出码 1")
args = ap.parse_args()
if not os.path.isfile(args.file):
sys.stderr.write("[patch_ddl] 文件不存在:%s\n" % args.file)
if not os.path.isfile(args.ddl):
print("FAIL: DDL 文件不存在:%s" % args.ddl)
return 2
with io.open(args.file, "r", encoding="utf-8") as fh:
sql = fh.read()
new_sql, changes, skips = patch(sql)
for tbl, ln, msg in changes:
print("[PATCH] %-34s CREATE @L%-5d %s" % (tbl, ln, msg))
for tbl, ln, msg in skips:
print("[SKIP ] %-34s CREATE @L%-5d %s" % (tbl, ln, msg))
print("[SUMMARY] 变更 %d 张表,跳过 %d 张表,幂等=%s"
% (len(changes), len(skips), "是(重复执行不再变更)" if not changes else "本次有变更"))
with open(args.ddl, "r", encoding="utf-8") as fp:
text = fp.read()
lines = text.splitlines()
blocks = parse_blocks(lines)
if changes and not args.dry_run:
with io.open(args.file, "w", encoding="utf-8") as fh:
fh.write(new_sql)
print("[WRITE] 已落盘:%s%d 字节)" % (args.file, os.path.getsize(args.file)))
elif args.dry_run:
print("[DRY-RUN] 未落盘")
drifted, missing, forbidden = audit(lines, blocks)
n_autoinc = len([1 for ln in lines if re.search(r"AUTO_INCREMENT", ln, re.I)])
n_bigint = len([1 for ln in lines if re.search(r"\bBIGINT\b", ln, re.I)])
print("=" * 74)
print("patch_ddl_tenant_first :: %s" % args.ddl)
print("=" * 74)
print("CREATE TABLE = %d" % len(blocks))
print("AUTO_INCREMENT = %d" % n_autoinc)
print("BIGINT = %d" % n_bigint)
print("tenant_id 打头 = %d/%d" % (len(blocks) - len(drifted) - len(missing), len(blocks)))
if drifted:
print("\n[DRIFT] tenant_id 位置漂移 %d 表:" % len(drifted))
for name, ti, fi in drifted:
print(" - %-28s 当前 body[%d] 应在 body[%d]" % (name, ti, fi))
if missing:
print("\n[MISS] 缺 tenant_id 列 %d 表:%s" % (len(missing), ", ".join(missing)))
if forbidden:
print("\n[FORBIDDEN] 禁用方言命中 %d 处:" % len(forbidden))
for ln, kw, snippet in forbidden[:20]:
print(" - L%d %s :: %s" % (ln, kw, snippet))
else:
print("\n[FORBIDDEN] 7 类禁用方言零命中BIGSERIAL/SERIAL/nextval/"
"FOREIGN KEY/REFERENCES/ENUM(/TIMESTAMP")
if args.check:
bad = len(drifted) + len(missing) + len(forbidden)
print("\nCHECK RESULT: %s" % ("PASS" if bad == 0 else "FAIL(%d)" % bad))
return 0 if bad == 0 else 1
if not drifted and not missing:
print("\nNOOP: 全部表 tenant_id 已打头,无需修补(幂等)")
return 0
new_lines, changes = patch(lines, blocks)
print("\n[PATCH] 改动 %d" % changes)
if args.dry_run or not args.inplace:
print("[DRY-RUN] 未写盘。加 --inplace 落盘。")
return 0
bak = args.ddl + ".bak"
shutil.copy2(args.ddl, bak)
with open(args.ddl, "w", encoding="utf-8") as fp:
fp.write("\n".join(new_lines) + "\n")
print("[WRITE] 已写回 %s(备份 %s" % (args.ddl, bak))
# 复验:修补后必须零漂移
blocks2 = parse_blocks(new_lines)
d2, m2, _f2 = audit(new_lines, blocks2)
if d2 or m2:
print("[RECHECK] FAIL 仍有漂移 %d / 缺失 %d" % (len(d2), len(m2)))
return 1
print("[RECHECK] PASS tenant_id 打头 %d/%d" % (len(blocks2), len(blocks2)))
return 0