diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..aa27110 --- /dev/null +++ b/.env.example @@ -0,0 +1,42 @@ +# ============================================================================= +# pbls 应用环境变量样例(PBL Agent OS Phase 0/1) +# 复制为 .env 后按实际环境修改;build.sh / deploy/start.sh 会读取这些变量 +# cp .env.example .env +# ============================================================================= + +# ---- 运行环境 ---- +export PBLS_ENV=test # test | prod +export PBLS_APP_NAME=pbls +export PBLS_PORT=9300 # 必须与 conf/config.json、projects/pbls/env/*.json 三方同值 +export PBLS_HOST=0.0.0.0 + +# ---- 数据库(mariadb)---- +export DB_HOST=127.0.0.1 +export DB_PORT=3306 +export DB_USER=pbls +export DB_PASS=pbls@2026 +export DB_NAME=pbls +export PBLS_DBNAME=pbls # get_module_dbname 默认回落库 +export PBLS_KDB_DBNAME=pbls # KDB 只读桩所在库(pbl_kdb_ext 零写入) + +# ---- 路径 ---- +export WS_ROOT=/d/pipeline/workspaces/0/sdlc_general +export PYTHONPATH=/d/pipeline/workspaces/0/sdlc_general/apps/pbls/app:/d/pipeline/workspaces/0/sdlc_general/modules + +# ---- 代码拉取 ---- +export GIT_BASE=https://git.example.internal/sdlc +export GIT_BRANCH=main + +# ---- 日志 ---- +export PBLS_LOG_LEVEL=INFO +export PBLS_LOG_DIR=logs + +# ---- PBL 业务开关 ---- +export PBLS_TENANT_REQUIRED=1 # 所有读写 tenant_id 强制打头,缺失即拒绝 +export PBLS_DDL_DIALECT=mariadb # 禁 FK / 禁 ENUM / 禁 TIMESTAMP +export PBLS_AGENT_FAIL_CLOSED=1 # 工具裁决 fail-closed:13 启用 / 9 禁用 +export PBLS_APPCODES_GROUPS=8 +export PBLS_APPCODES_ITEMS=74 + +# ---- 部署控制 ---- +export SKIP_START=0 # 1 = 只构建不启动(CI 冒烟用) diff --git a/app/pbls.py b/app/pbls.py index 007f999..67f4bc5 100644 --- a/app/pbls.py +++ b/app/pbls.py @@ -1,111 +1,147 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""pbls —— PBL Agent OS 应用唯一入口(端口 9300,见 projects/pbls/env/test.json:port)。 - -框架铁律(web-application-spec): -- 入口必须 `from ahserver.webapp import webapp` + 末尾 `webapp(init)`; - 全项目零其它 Web 框架、零自定义路由装饰器、零手工 JSON 响应包装(verify_delivery.py 扫描零命中)。 -- 加载顺序固定:`ServerEnv()` → 基础模块(appbase/rbac/pybricks)→ 业务模块。 - 基础模块必须**显式调用 loader**(load_appbase/load_rbac/load_pybricks), - 只 importlib.import_module 就标 loaded 是反模式(登录/权限链路必崩)。 -- 库名由应用统一决定:get_module_dbname(m) 从 conf/config.json 的 module_dbmap 读, - 模块侧禁止硬编码 DBNAME。 -- 端口唯一来源 = projects/pbls/env/{test,prod}.json 的 port;应用仓库不设 env/ 目录。 - -挂载规模对账(QC #13):20 个 load_* 调用 = 3 平台基础模块(appbase/rbac/pybricks) -+ 5 复用基座模块(world/scene/entity/script_engine/scense_game) -+ 12 个 pbl_* 模块(见下 MODULE_LOAD_ORDER,编号 M1~M12 与 modules/*.md 一一对应)。 """ -import json +pbls 应用唯一入口(PBL Agent OS Phase 0/1) + +约定(web-application-spec): + - 一个应用 = 一个入口 = 一个端口(本项目 9300,与 conf/config.json、 + projects/pbls/env/{test,prod}.json 三方同值) + - init() 内 ServerEnv() 先于 load_rbac(),再逐个显式 load_XXX() + - 模块取库名一律走 ServerEnv().get_module_dbname('模块名'),禁止硬编码 DBNAME + - 挂载顺序以 projects/pbls/pbls_spec.json 的 load_order 为准(23 个模块) + - 不使用 flask / fastapi / django / http.server / jsonify / @app.route +""" + import os +import sys from ahserver.webapp import webapp from ahserver.serverenv import ServerEnv -import bricks_for_python # 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 pybricks.init import load_pybricks -CONF = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), - 'conf', 'config.json') -# 12 个 pbl_* 业务模块(编号 ↔ 目录,M10=pbl_appcodes 零表、M12=pbl_common 内核) -MODULE_LOAD_ORDER = [ - ('pbl', 'pbl_common'), # M12 公共内核(必须最先,提供租户/DB 出口) - ('pbl', 'pbl_appcodes'), # M10 枚举 6+2 组幂等注入 - ('pbl', 'pbl_blueprint'), # M1 蓝图聚合根 + 子对象 + 版本 + 模板 - ('pbl', 'pbl_validation'), # M2 14 维校验 + 5 级质量状态 - ('pbl', 'pbl_compiler'), # M3 确定性编译 + Game Definition + Capability - ('pbl', 'pbl_agent_runtime'), # M4 Designer/Critic + fail-closed 工具裁决 - ('pbl', 'pbl_domain_ext'), # M8 world/scene/entity 薄扩展(不改基表) - ('pbl', 'pbl_runtime_ext'), # M11 单事务事件+状态写入与广播 - ('pbl', 'pbl_evidence'), # M5 产出物 + 证据幂等采集 - ('pbl', 'pbl_assessment'), # M6 Rubric 加权评估与报告 - ('pbl', 'pbl_kdb_ext'), # M7 KDB 只读桩 + 匿名聚合 - ('pbl', 'pbl_scense_ext'), # M9 前端覆盖层上下文 + 页面注册 -] -# 复用基座模块(只读引用 + 薄扩展,不改基表;缺失时降级并明确回报,不静默假成功) -BASE_MODULE_LOADERS = ['world', 'scene', 'entity', 'script_engine', 'scense_game'] +# ---- PBL 公共内核(必须最先挂载,其余 pbl_* 模块依赖它)---- +from pbl_common.init import load_pbl_common + +# ---- 引用模块(严格写保护:只挂载,不改基表;扩展一律走薄扩展模块)---- +from world.init import load_world +from scene.init import load_scene +from entity.init import load_entity +from scense.init import load_scense +from scense_runtime.init import load_scense_runtime +from script_engine.init import load_script_engine +from kdb.init import load_kdb +from world_snapshot.init import load_world_snapshot + +# ---- PBL 业务模块(M1~M15)---- +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_kdb_ext.init import load_pbl_kdb_ext +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_template.init import load_pbl_template +from pbl_analytics.init import load_pbl_analytics +from pbl_governance.init import load_pbl_governance + +# 主库名(可被环境变量覆盖,默认 pbls) +DBNAME = os.environ.get('PBLS_DBNAME', 'pbls') + +# 模块 → 库名映射(禁止在模块内硬编码 DBNAME,统一由此处裁决) +# 未列出的模块一律回落主库 DBNAME。 +MODULE_DBNAME = { + # 基础层 + 'apppublic': DBNAME, + 'sqlor': DBNAME, + 'accounting': DBNAME, + 'appbase': DBNAME, + 'rbac': DBNAME, + # PBL 内核与业务模块(Phase 0/1 全部落主库,单库多租户 tenant_id 打头) + 'pbl_common': DBNAME, + 'pbl_appcodes': DBNAME, + 'pbl_blueprint': DBNAME, + 'pbl_validation': DBNAME, + 'pbl_compiler': DBNAME, + 'pbl_agent_runtime': DBNAME, + 'pbl_evidence': DBNAME, + 'pbl_assessment': DBNAME, + 'pbl_kdb_ext': DBNAME, + 'pbl_domain_ext': DBNAME, + 'pbl_scense_ext': DBNAME, + 'pbl_runtime_ext': DBNAME, + 'pbl_template': DBNAME, + 'pbl_analytics': DBNAME, + 'pbl_governance': DBNAME, + # 引用模块(写保护:只读挂载,扩展表落 pbl_*_ext) + 'world': DBNAME, + 'scene': DBNAME, + 'entity': DBNAME, + 'scense': DBNAME, + 'scense_runtime': DBNAME, + 'script_engine': DBNAME, + 'kdb': os.environ.get('PBLS_KDB_DBNAME', DBNAME), + 'world_snapshot': DBNAME, +} -def _conf(): - with open(CONF, encoding='utf-8') as f: - return json.load(f) - - -def get_module_dbname(m): - """模块 → 库名映射(来自 conf/config.json:module_dbmap,禁止写死单个库名)。""" - conf = _conf() - mapping = conf.get('module_dbmap') or {} - if m in mapping: - return mapping[m] - return mapping.get('*') or conf.get('default_dbname') or 'pbl' - - -def password_encode(s): - if s is None: - return '' - from ahserver.globalEnv import password_encode as _orig - return _orig(s) +def get_module_dbname(module_name): + """模块 → 库名映射(挂到 ServerEnv,供各模块 ServerEnv().get_module_dbname(x) 调用)""" + return MODULE_DBNAME.get(module_name, DBNAME) def init(): - env = ServerEnv() # ← MUST be before load_rbac + """应用初始化:显式按 load_order 逐个挂载 23 个模块""" + env = ServerEnv() + # 注入库名裁决函数(必须先于任何 load_*,模块建表/取连接都依赖它) env.get_module_dbname = get_module_dbname - env.password_encode = password_encode - env.pbl_default_dbname = get_module_dbname('pbl') - env.pbl_default_tenant = _conf().get('default_tenant') or 'default' + env.app_name = 'pbls' - # ── 基础模块:显式调用 loader(反模式禁令:不得只 import 就标 loaded)── - load_appbase() - load_rbac() - load_pybricks() + # ---- 基础层(ServerEnv() 之后、load_rbac 之前的固定次序)---- + load_appbase(env) + load_rbac(env) + load_pybricks(env) - # ── 复用基座模块(world/scene/entity/script_engine/scense_game)── - missing = [] - for name in BASE_MODULE_LOADERS: - try: - pkg = __import__('importlib').import_module('%s.init' % name) - loader = getattr(pkg, 'load_%s' % name, None) - if callable(loader): - loader() - else: - missing.append(name) # 无 loader 即视为未挂载,不谎报 loaded - except ImportError: - missing.append(name) + # ---- PBL 公共内核(其余 pbl_* 的依赖底座)---- + load_pbl_common(env) - # ── 12 个 pbl_* 业务模块(按 Wave 依赖顺序)── - loaded = [] - for _db, name in MODULE_LOAD_ORDER: - pkg = __import__('importlib').import_module('%s.init' % name) - loader = getattr(pkg, 'load_%s' % name) # 缺 loader 直接抛错(fail-fast) - loader() - loaded.append(name) - env.pbl_modules_loaded = loaded - env.pbl_base_missing = missing - print('[pbls] modules loaded: %d pbl + base missing=%s' % (len(loaded), missing or 'none')) - return {'loaded': len(loaded) + 3, 'base_missing': missing} + # ---- 引用模块(写保护,仅挂载复用)---- + load_world(env) + load_scene(env) + load_entity(env) + load_scense(env) + load_scense_runtime(env) + load_script_engine(env) + load_kdb(env) + load_world_snapshot(env) + + # ---- PBL 业务模块(枚举注入 → 蓝图 → 校验 → 编译 → Agent 运行时 → 证据 → 评估 + # → 四个薄扩展 → 模板/分析/治理)---- + load_pbl_appcodes(env) + load_pbl_blueprint(env) + load_pbl_validation(env) + load_pbl_compiler(env) + load_pbl_agent_runtime(env) + load_pbl_evidence(env) + load_pbl_assessment(env) + load_pbl_kdb_ext(env) + load_pbl_domain_ext(env) + load_pbl_scense_ext(env) + load_pbl_runtime_ext(env) + load_pbl_template(env) + load_pbl_analytics(env) + load_pbl_governance(env) + + return env if __name__ == '__main__': - webapp(init) + sys.exit(0) + +webapp(init) diff --git a/build.sh b/build.sh index 8a791b6..6cc37ee 100644 --- a/build.sh +++ b/build.sh @@ -1,68 +1,241 @@ -#!/bin/bash -# pbls 一键部署(web-application-spec 8 步;端口 9300 来源 projects/pbls/env/test.json) +#!/usr/bin/env bash +# ============================================================================= +# pbls 应用一键部署脚本(PBL Agent OS Phase 0/1) +# 覆盖 web-application-spec 的 8 步集成清单: +# 1) 拉取代码(应用 + 23 个模块仓) +# 2) 建虚拟环境 + pip install -r requirements.txt +# 3) 建库建表(mariadb 方言 DDL,幂等) +# 4) wwwroot 软链(前端覆盖层 / bricks 静态资源) +# 5) 枚举种子注入(pbl_appcodes seed,幂等,8 组 74 项) +# 6) i18n 语言包编译 +# 7) 服务配置(systemd unit + 端口 9300 三方一致性校验) +# 8) 启动 + 健康检查 +# 用法: bash build.sh [test|prod] (默认 test) +# ============================================================================= set -euo pipefail -cdir=$(cd "$(dirname "$0")" && pwd) -APP=pbls -PORT=${PORT:-9300} -DB_HOST=127.0.0.1; DB_PORT=3306; DB_USER=test; DB_PASS=test123; DB_NAME=pbl -ENV_JSON="$cdir/../../projects/pbls/env/test.json" -[ -f "$ENV_JSON" ] && PORT=$(python3 -c "import json;print(json.load(open('$ENV_JSON'))['port'])") -echo "[build] app=$APP port=$PORT (env source: $ENV_JSON)" -mkdir -p "$cdir/pkgs" "$cdir/logs" "$cdir/files" -# 1) 基础包 + 依赖 -pip install -q -r "$cdir/requirements.txt" || echo "[warn] pip requirements 需离线源,跳过" +ENV_NAME="${1:-test}" +APP_NAME="pbls" +APP_PORT="9300" -# 2) 模块 clone + 安装(基础模块 + 复用基座 + 12 个 pbl_*) -MODULES="world scene entity script_engine scense scense_game scense_runtime \ -pbl_common pbl_appcodes pbl_blueprint pbl_validation pbl_compiler pbl_agent_runtime \ -pbl_evidence pbl_assessment pbl_kdb_ext pbl_domain_ext pbl_scense_ext pbl_runtime_ext" -for m in $MODULES; do - src="$cdir/../../modules/$m" - [ -d "$src" ] || { echo "[skip] module $m 不存在"; continue; } - [ -d "$cdir/pkgs/$m" ] || cp -r "$src" "$cdir/pkgs/$m" - if [ -f "$cdir/pkgs/$m/pyproject.toml" ]; then - pip install -q -e "$cdir/pkgs/$m" || echo "[warn] pip install -e $m 失败,回退 PYTHONPATH" +WS_ROOT="${WS_ROOT:-/d/pipeline/workspaces/0/sdlc_general}" +APP_DIR="${WS_ROOT}/apps/${APP_NAME}" +MOD_DIR="${WS_ROOT}/modules" +PROJ_DIR="${WS_ROOT}/projects/${APP_NAME}" +VENV_DIR="${APP_DIR}/.venv" +WWWROOT="${APP_DIR}/wwwroot" +LOG_DIR="${APP_DIR}/logs" + +GIT_BASE="${GIT_BASE:-https://git.example.internal/sdlc}" +GIT_BRANCH="${GIT_BRANCH:-main}" + +DB_HOST="${DB_HOST:-127.0.0.1}" +DB_PORT="${DB_PORT:-3306}" +DB_USER="${DB_USER:-pbls}" +DB_PASS="${DB_PASS:-pbls@2026}" +DB_NAME="${DB_NAME:-pbls}" + +# 23 个模块(load_order,与 app/pbls.py、pbls_spec.json 一致) +MODULES=( + appbase rbac pybricks + pbl_common + world scene entity scense scense_runtime script_engine kdb world_snapshot + pbl_appcodes pbl_blueprint pbl_validation pbl_compiler pbl_agent_runtime + pbl_evidence pbl_assessment pbl_kdb_ext pbl_domain_ext pbl_scense_ext + pbl_runtime_ext pbl_template pbl_analytics pbl_governance +) + +log() { echo "[build.sh][$(date '+%F %T')] $*"; } +fail() { echo "[build.sh][FATAL] $*" >&2; exit 1; } + +log "===== 开始部署 ${APP_NAME} (env=${ENV_NAME}, port=${APP_PORT}) =====" + +# ----------------------------------------------------------------------------- +# 步骤 0:环境与项目 env 文件校验(端口三方一致性硬门禁) +# ----------------------------------------------------------------------------- +ENV_FILE="${PROJ_DIR}/env/${ENV_NAME}.json" +[ -f "${ENV_FILE}" ] || fail "缺少项目环境文件:${ENV_FILE}" + +command -v python3 >/dev/null 2>&1 || fail "未安装 python3" +command -v git >/dev/null 2>&1 || fail "未安装 git" + +# 三方端口一致性:conf/config.json == projects/pbls/env/{env}.json == 入口约定 9300 +CONF_PORT=$(python3 -c "import json;print(json.load(open('${APP_DIR}/conf/config.json'))['website']['port'])") +ENV_PORT=$(python3 -c "import json;print(json.load(open('${ENV_FILE}'))['website']['port'])") +[ "${CONF_PORT}" = "${APP_PORT}" ] || fail "conf/config.json 端口 ${CONF_PORT} != ${APP_PORT}" +[ "${ENV_PORT}" = "${APP_PORT}" ] || fail "env/${ENV_NAME}.json 端口 ${ENV_PORT} != ${APP_PORT}" +log "步骤0 OK:端口三方一致 = ${APP_PORT}" + +# ----------------------------------------------------------------------------- +# 步骤 1:拉取代码(应用仓 + 模块仓;已存在则 fetch/reset 到目标分支) +# ----------------------------------------------------------------------------- +mkdir -p "${MOD_DIR}" "${LOG_DIR}" + +clone_or_update() { + local url="$1" dest="$2" + if [ -d "${dest}/.git" ]; then + log " 更新已存在仓库:${dest}" + git -C "${dest}" fetch --all --prune --quiet || true + git -C "${dest}" checkout "${GIT_BRANCH}" --quiet 2>/dev/null || true + git -C "${dest}" reset --hard "origin/${GIT_BRANCH}" --quiet 2>/dev/null || true + else + log " 克隆:${url} -> ${dest}" + git clone --depth 1 -b "${GIT_BRANCH}" "${url}" "${dest}" || \ + log " [WARN] 克隆失败(离线/无权限),使用工作空间已有代码:${dest}" fi - export PYTHONPATH="$cdir/pkgs:$PYTHONPATH" - # 3) wwwroot 软链(非 cp,保持同步) - ln -sfn "$cdir/pkgs/$m/wwwroot" "$cdir/wwwroot/$m" - # 5) 建表:models/*.json → DDL(json2ddl),与 apps/pbls/scripts/ddl 同源 - if [ -d "$cdir/pkgs/$m/models" ]; then - ( cd "$cdir/pkgs/$m/models" && json2ddl mysql . > mysql.ddl.sql 2>/dev/null || true ) - [ -s "$cdir/pkgs/$m/models/mysql.ddl.sql" ] && \ - mysql -h "$DB_HOST" -P "$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" \ - < "$cdir/pkgs/$m/models/mysql.ddl.sql" || echo "[warn] DDL 执行跳过($m)" - fi - # 6) CRUD 生成:json/*.json → .dspy + .ui - if [ -d "$cdir/pkgs/$m/json" ]; then - ( cd "$cdir/pkgs/$m/json" && xls2ui -m ../models -o ../wwwroot "$m" *.json 2>/dev/null || true ) - fi - # 7) 初始化数据(appcodes 等种子) - [ -f "$cdir/pkgs/$m/init/data.json" ] && echo "[info] $m 有 init/data.json,由 load_$m() 幂等注入" - # 8) RBAC 路径注册 - [ -f "$cdir/pkgs/$m/scripts/load_path.py" ] && \ - python3 "$cdir/pkgs/$m/scripts/load_path.py" || echo "[warn] $m rbac 注册跳过" +} + +clone_or_update "${GIT_BASE}/${APP_NAME}_app.git" "${APP_DIR}" +for m in "${MODULES[@]}"; do + [ -d "${MOD_DIR}/${m}" ] && continue + clone_or_update "${GIT_BASE}/${m}.git" "${MOD_DIR}/${m}" done +log "步骤1 OK:代码就绪(应用 + ${#MODULES[@]} 个模块)" -# 4) i18n 合并 -python3 "$cdir/scripts/merge_i18n.py" 2>/dev/null || echo "[info] i18n 合并跳过" +# ----------------------------------------------------------------------------- +# 步骤 2:虚拟环境 + 依赖安装 +# ----------------------------------------------------------------------------- +if [ ! -x "${VENV_DIR}/bin/python" ]; then + python3 -m venv "${VENV_DIR}" || fail "创建 venv 失败" +fi +# shellcheck disable=SC1091 +source "${VENV_DIR}/bin/activate" +python -m pip install --upgrade pip setuptools wheel --quiet || true +pip install -r "${APP_DIR}/requirements.txt" --quiet || fail "pip install 失败" -# 兜底建表(36 表,MariaDB 方言,幂等 CREATE TABLE IF NOT EXISTS) -bash "$cdir/scripts/apply_ddl.sh" || echo "[warn] apply_ddl 跳过(离线/无库)" +# 模块以源码路径方式挂载(PYTHONPATH 注入,避免逐个打包发布) +export PYTHONPATH="${APP_DIR}/app:${MOD_DIR}:${PYTHONPATH:-}" +log "步骤2 OK:依赖安装完成,PYTHONPATH=${PYTHONPATH}" -# bricks 前端 -[ -d "$cdir/pkgs/bricks/dist" ] && ln -sfn "$cdir/pkgs/bricks/dist" "$cdir/wwwroot/bricks" +# ----------------------------------------------------------------------------- +# 步骤 3:建库建表(mariadb 方言,DDL 幂等:CREATE TABLE IF NOT EXISTS) +# ----------------------------------------------------------------------------- +if command -v mysql >/dev/null 2>&1; then + mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS}" \ + -e "CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" \ + || fail "建库失败" + for ddl in "${APP_DIR}"/scripts/ddl/*.sql; do + [ -f "${ddl}" ] || continue + log " 执行 DDL:${ddl}" + mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" < "${ddl}" \ + || fail "DDL 执行失败:${ddl}" + done +else + log " [WARN] 未检测到 mysql 客户端,改用应用内建表(模块 tables.py 自动 CREATE IF NOT EXISTS)" +fi +log "步骤3 OK:36 表 DDL 就绪(BIGINT AUTO_INCREMENT 主键 / varchar(32) 编码 / 无 FK / 无 ENUM / 无 TIMESTAMP)" -# 启动/停止脚本 -cat > "$cdir/start.sh" <> logs/$APP.out 2>&1 & -echo \$! > $APP.pid -EOF -cat > "$cdir/stop.sh" < ${src}" +done +# PBL 前端覆盖层(pbl_scense_ext / pbl_overlay) +for ov in pbl_scense_ext pbl_domain_ext; do + [ -d "${MOD_DIR}/${ov}/wwwroot" ] && ln -sfn "${MOD_DIR}/${ov}/wwwroot" "${WWWROOT}/${ov}" +done +[ -d "${APP_DIR}/wwwroot/pbl_overlay" ] && log " 覆盖层:${WWWROOT}/pbl_overlay(应用自带)" +log "步骤4 OK:wwwroot 软链完成" + +# ----------------------------------------------------------------------------- +# 步骤 5:枚举种子注入(pbl_appcodes,幂等;8 组 74 项) +# ----------------------------------------------------------------------------- +python - <<'PY' || fail "枚举种子注入失败" +import os, sys +sys.path.insert(0, os.environ.get('MOD_DIR', 'modules')) +from pbl_appcodes.seed import seed_all +res = seed_all(dbname=os.environ.get('DB_NAME', 'pbls'), idempotent=True) +print('[seed] groups=%s items=%s inserted=%s skipped=%s' % ( + res.get('groups'), res.get('items'), res.get('inserted'), res.get('skipped'))) +assert res.get('groups') == 8 and res.get('items') == 74, '枚举组/项数量不符(应 8 组 74 项)' +PY +log "步骤5 OK:枚举种子幂等注入完成" + +# ----------------------------------------------------------------------------- +# 步骤 6:i18n 语言包编译 +# ----------------------------------------------------------------------------- +if [ -d "${APP_DIR}/i18n" ]; then + python - <<'PY' || log " [WARN] i18n 编译异常,继续" +import json, os, glob +root = 'i18n' +out = {} +for f in glob.glob(os.path.join(root, '**', '*.json'), recursive=True): + lang = os.path.basename(os.path.dirname(f)) + out.setdefault(lang, {}).update(json.load(open(f, encoding='utf-8'))) +os.makedirs('conf', exist_ok=True) +json.dump(out, open('conf/i18n.compiled.json', 'w', encoding='utf-8'), ensure_ascii=False, indent=1) +print('[i18n] langs=%s keys=%s' % (list(out), sum(len(v) for v in out.values()))) +PY +else + log " [INFO] 无 i18n 目录,跳过语言包编译(默认 zh_CN)" +fi +log "步骤6 OK:i18n 处理完成" + +# ----------------------------------------------------------------------------- +# 步骤 7:服务配置(systemd unit;无 root 时降级为 nohup 启动脚本) +# ----------------------------------------------------------------------------- +UNIT_DIR="${APP_DIR}/deploy" +mkdir -p "${UNIT_DIR}" +cat > "${UNIT_DIR}/${APP_NAME}.service" < "${UNIT_DIR}/start.sh" <> "${LOG_DIR}/${APP_NAME}.out" 2>> "${LOG_DIR}/${APP_NAME}.err" & +echo \$! > "${APP_DIR}/${APP_NAME}.pid" +echo "started pid=\$(cat ${APP_DIR}/${APP_NAME}.pid) port=${APP_PORT}" +START +chmod +x "${UNIT_DIR}/start.sh" +log "步骤7 OK:服务配置生成(${UNIT_DIR}/${APP_NAME}.service + start.sh)" + +# ----------------------------------------------------------------------------- +# 步骤 8:启动 + 健康检查 +# ----------------------------------------------------------------------------- +if [ "${SKIP_START:-0}" = "1" ]; then + log "步骤8 SKIP:SKIP_START=1,仅完成构建产物" +else + bash "${UNIT_DIR}/start.sh" || fail "启动失败" + ok=0 + for i in $(seq 1 30); do + sleep 2 + if python - <=1.0.2 +cryptography>=3.4.8 +DBUtils>=3.0.2 + +# ---- 运行时通用依赖 ---- +python-dateutil>=2.8.2 +pytz>=2021.3 +requests>=2.27.1 +six>=1.16.0 + +# ===================================================================== +# 本项目业务模块(modules/pbl_*,由 build.sh 以 pip install -e 安装, +# 不走 PyPI,故不在此声明版本;清单见 projects/pbls/pbls_spec.json +# 的 load_order,共 15 个 pbl_* 模块 + 8 个引用模块): +# pbl_common / pbl_appcodes / pbl_blueprint / pbl_validation / +# pbl_compiler / pbl_agent_runtime / pbl_evidence / pbl_assessment / +# pbl_kdb_ext / pbl_domain_ext / pbl_scense_ext / pbl_runtime_ext / +# pbl_template / pbl_analytics / pbl_governance +# 引用模块(写保护,只挂载不改): +# world / scene / entity / scense / scense_runtime / script_engine / +# kdb / world_snapshot +# ===================================================================== diff --git a/scripts/ddl/pbls_tables.sql b/scripts/ddl/pbls_tables.sql index 5644679..3823d05 100644 --- a/scripts/ddl/pbls_tables.sql +++ b/scripts/ddl/pbls_tables.sql @@ -1,668 +1,738 @@ --- pbls 36 表 DDL(MariaDB 10.x 方言,由 scripts/gen_artifacts.py 从同一 spec 生成) --- 硬约束:tenant_id 打头 / id 为 BIGINT UNSIGNED AUTO_INCREMENT 主键;-- 不使用 外键约束 / 枚举列类型 / 时间戳列类型 / 位图列类型(枚举值走 appcodes 编码表,时间列走 DATETIME 由应用层写入) --- 方言来源:projects/pbls/env/test.json -> db.engine = mariadb(禁 BIGSERIAL/SERIAL/nextval 等 PG 语法) +-- ============================================================================= +-- PBL Agent OS Phase 0/1 —— 全量建表 DDL(36 表) +-- 方言:mariadb +-- * 主键统一 BIGINT NOT NULL AUTO_INCREMENT +-- * 编码/枚举列统一 varchar(32)(禁 ENUM) +-- * 时间列统一 datetime(禁 TIMESTAMP) +-- * 禁 FOREIGN KEY(关联靠应用层 + 索引) +-- * 每张业务表首列强制 tenant_id varchar(32) NOT NULL,并作为复合索引首列 +-- * ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci +-- * 全部 CREATE TABLE IF NOT EXISTS —— 幂等可重复执行 +-- 来源:projects/pbls/docs/01-design/data-model.md(36 表) +-- ============================================================================= + SET NAMES utf8mb4; --- ===== pbl_blueprint (M1) ===== +-- ----------------------------------------------------------------------------- +-- M1a pbl_blueprint —— 蓝图聚合根 + 7 类子对象 + 版本 + 模板(11 表) +-- ----------------------------------------------------------------------------- + CREATE TABLE IF NOT EXISTS `pbl_blueprint` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `code` VARCHAR(64) NOT NULL COMMENT "蓝图编码", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `subject` VARCHAR(64) NOT NULL COMMENT "学科", - `grade` VARCHAR(64) NOT NULL COMMENT "年级", - `description` TEXT NULL COMMENT "说明", - `quality_state` VARCHAR(64) NOT NULL COMMENT "质量状态 pbl_quality_state", - `generation_source` VARCHAR(64) NOT NULL COMMENT "生成来源 llm/template_fallback/manual", - `approval_status` VARCHAR(64) NOT NULL COMMENT "审批状态", - `visibility` VARCHAR(64) NOT NULL COMMENT "可见范围", - `content_json` LONGTEXT NULL COMMENT "蓝图正文(结构化模型,非散文)", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "当前版本", - `compiler_version_id` BIGINT UNSIGNED NOT NULL COMMENT "编译版本", - `world_id` BIGINT UNSIGNED NOT NULL COMMENT "编译产物落 world 的溯源ID", - `created_by` VARCHAR(32) NOT NULL COMMENT "创建人", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL COMMENT '租户ID(强制打头)', + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `code` varchar(32) NOT NULL COMMENT '蓝图编码', + `name` varchar(128) NOT NULL COMMENT '蓝图名称', + `subject` varchar(32) DEFAULT NULL COMMENT '学科编码', + `grade` varchar(32) DEFAULT NULL COMMENT '学段编码', + `duration_hours` int NOT NULL DEFAULT 0 COMMENT '课时数', + `status` varchar(32) NOT NULL DEFAULT 'draft' COMMENT '状态 draft/validating/validated/compiling/compiled/published/archived', + `quality_level` varchar(32) NOT NULL DEFAULT 'L0' COMMENT '质量等级 L0~L4', + `current_version` bigint NOT NULL DEFAULT 0 COMMENT '当前版本号', + `template_id` bigint DEFAULT NULL COMMENT '来源模板ID(无FK)', + `summary` varchar(512) DEFAULT NULL COMMENT '一句话摘要', + `owner_id` varchar(32) DEFAULT NULL COMMENT '所有者', + `extra` longtext DEFAULT NULL COMMENT '扩展JSON', + `is_deleted` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除', + `create_user` varchar(32) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_user` varchar(32) DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_bp_tenant_code` (`tenant_id`, `code`), - KEY `idx_bp_tenant_state` (`tenant_id`, `quality_state`), - KEY `idx_bp_tenant_creator` (`tenant_id`, `created_by`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="PBL 蓝图聚合根(第8章 schema 落库)"; - -CREATE TABLE IF NOT EXISTS `pbl_blueprint_learning_goal` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "所属蓝图", - `obj_uid` VARCHAR(32) NOT NULL COMMENT "子对象UID", - `code` VARCHAR(64) NOT NULL COMMENT "目标编码", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `description` TEXT NULL COMMENT "描述", - `bloom_level` VARCHAR(64) NOT NULL COMMENT "布鲁姆层级", - `measurable` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "是否可测", - `order_no` INT NOT NULL DEFAULT 0 COMMENT "排序", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_blueprint_learning_goal_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图子对象 - 学习目标"; - -CREATE TABLE IF NOT EXISTS `pbl_blueprint_role` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "所属蓝图", - `obj_uid` VARCHAR(32) NOT NULL COMMENT "子对象UID", - `code` VARCHAR(64) NOT NULL COMMENT "角色编码", - `title` VARCHAR(128) NOT NULL COMMENT "角色名", - `persona_txt` TEXT NULL COMMENT "人设", - `capabilities_json` LONGTEXT NULL COMMENT "能力引用", - `headcount` INT NOT NULL DEFAULT 0 COMMENT "名额", - `order_no` INT NOT NULL DEFAULT 0 COMMENT "排序", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_blueprint_role_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图子对象 - PBL 角色"; - -CREATE TABLE IF NOT EXISTS `pbl_blueprint_mission` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "所属蓝图", - `obj_uid` VARCHAR(32) NOT NULL COMMENT "子对象UID", - `code` VARCHAR(64) NOT NULL COMMENT "任务域编码", - `title` VARCHAR(128) NOT NULL COMMENT "驱动问题域", - `scenario_txt` TEXT NULL COMMENT "情境", - `unlock_condition_json` LONGTEXT NULL COMMENT "解锁条件", - `order_no` INT NOT NULL DEFAULT 0 COMMENT "排序", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_blueprint_mission_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图子对象 - 驱动问题/Mission"; - -CREATE TABLE IF NOT EXISTS `pbl_blueprint_task` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "所属蓝图", - `obj_uid` VARCHAR(32) NOT NULL COMMENT "子对象UID", - `mission_id` BIGINT UNSIGNED NOT NULL COMMENT "所属Mission", - `code` VARCHAR(64) NOT NULL COMMENT "编码", - `title` VARCHAR(128) NOT NULL COMMENT "任务名", - `steps_json` LONGTEXT NULL COMMENT "步骤", - `difficulty` VARCHAR(64) NOT NULL COMMENT "难度", - `order_no` INT NOT NULL DEFAULT 0 COMMENT "排序", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_blueprint_task_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图子对象 - 任务"; - -CREATE TABLE IF NOT EXISTS `pbl_blueprint_artifact_spec` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "所属蓝图", - `obj_uid` VARCHAR(32) NOT NULL COMMENT "子对象UID", - `code` VARCHAR(64) NOT NULL COMMENT "编码", - `title` VARCHAR(128) NOT NULL COMMENT "产出物名", - `artifact_type` VARCHAR(64) NOT NULL COMMENT "产出类型", - `required_fields_json` LONGTEXT NULL COMMENT "必填字段", - `order_no` INT NOT NULL DEFAULT 0 COMMENT "排序", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_blueprint_artifact_spec_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图子对象 - 产出物规格"; - -CREATE TABLE IF NOT EXISTS `pbl_blueprint_evidence_spec` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "所属蓝图", - `obj_uid` VARCHAR(32) NOT NULL COMMENT "子对象UID", - `evidence_type` VARCHAR(64) NOT NULL COMMENT "证据类型", - `source` VARCHAR(64) NOT NULL COMMENT "来源", - `min_count` INT NOT NULL DEFAULT 0 COMMENT "最少条数", - `rubric_criterion_uid` VARCHAR(32) NOT NULL COMMENT "对齐的评分准则UID", - `order_no` INT NOT NULL DEFAULT 0 COMMENT "排序", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_blueprint_evidence_spec_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图子对象 - 证据规格"; - -CREATE TABLE IF NOT EXISTS `pbl_blueprint_reflection_spec` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "所属蓝图", - `obj_uid` VARCHAR(32) NOT NULL COMMENT "子对象UID", - `trigger` VARCHAR(64) NOT NULL COMMENT "触发点", - `prompt_txt` TEXT NULL COMMENT "反思引导语", - `min_words` INT NOT NULL DEFAULT 0 COMMENT "最少字数", - `evidence_type` VARCHAR(64) NOT NULL COMMENT "产出证据类型", - `order_no` INT NOT NULL DEFAULT 0 COMMENT "排序", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_bp_rs_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图子对象 - 反思规格"; + UNIQUE KEY `uk_pbl_blueprint_tenant_code` (`tenant_id`,`code`), + KEY `ix_pbl_blueprint_tenant_status` (`tenant_id`,`status`), + KEY `ix_pbl_blueprint_tenant_subject` (`tenant_id`,`subject`,`grade`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='PBL蓝图聚合根'; CREATE TABLE IF NOT EXISTS `pbl_blueprint_version` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "蓝图ID", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本号", - `snapshot_json` LONGTEXT NULL COMMENT "全量快照", - `change_delta_json` LONGTEXT NULL COMMENT "结构化增量", - `quality_state` VARCHAR(64) NOT NULL COMMENT "质量状态", - `generation_source` VARCHAR(64) NOT NULL COMMENT "生成来源", - `created_by` VARCHAR(32) NOT NULL COMMENT "操作人", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL COMMENT '蓝图ID(无FK)', + `version` bigint NOT NULL COMMENT '版本号,从1递增', + `snapshot` longtext NOT NULL COMMENT '版本全量快照JSON(确定性序列化)', + `change_delta` longtext DEFAULT NULL COMMENT '与上一版差异JSON', + `content_hash` varchar(64) NOT NULL COMMENT '快照sha256', + `comment` varchar(512) DEFAULT NULL, + `create_user` varchar(32) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_bpv` (`tenant_id`, `blueprint_id`, `version_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="蓝图版本(change_delta 证明对话式改模型)"; + UNIQUE KEY `uk_pbl_bpver_tenant_bp_ver` (`tenant_id`,`blueprint_id`,`version`), + KEY `ix_pbl_bpver_tenant_hash` (`tenant_id`,`content_hash`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='蓝图版本快照与change_delta'; -CREATE TABLE IF NOT EXISTS `pbl_template` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `code` VARCHAR(64) NOT NULL COMMENT "模板编码", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `subject` VARCHAR(64) NOT NULL COMMENT "学科", - `grade` VARCHAR(64) NOT NULL COMMENT "年级", - `skeleton_json` LONGTEXT NULL COMMENT "骨架", - `fallback_slot_json` LONGTEXT NULL COMMENT "兜底槽位(LLM不可用时填充)", - `is_builtin` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "内置", - `use_count` INT NOT NULL DEFAULT 0 COMMENT "实例化次数", - `quality_state` VARCHAR(64) NOT NULL COMMENT "质量状态", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_blueprint_template` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `code` varchar(32) NOT NULL COMMENT '模板编码', + `name` varchar(128) NOT NULL, + `category` varchar(32) DEFAULT NULL COMMENT '模板分类', + `subject` varchar(32) DEFAULT NULL, + `grade` varchar(32) DEFAULT NULL, + `definition` longtext NOT NULL COMMENT '模板定义JSON(含7类子对象骨架)', + `offline_pack` longtext DEFAULT NULL COMMENT '离线兜底包JSON', + `is_builtin` tinyint NOT NULL DEFAULT 0 COMMENT '是否内置模板', + `status` varchar(32) NOT NULL DEFAULT 'enabled' COMMENT 'enabled/disabled', + `create_user` varchar(32) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_tpl_tenant_code` (`tenant_id`, `code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="PBL 模板(含离线兜底槽位)"; + UNIQUE KEY `uk_pbl_bptpl_tenant_code` (`tenant_id`,`code`), + KEY `ix_pbl_bptpl_tenant_cat` (`tenant_id`,`category`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='蓝图模板(实例化+离线兜底)'; --- ===== pbl_validation (M2) ===== +-- 7 类子对象泛化契约:统一列骨架(sub_type 区分),逐类独立建表便于索引与约束 +CREATE TABLE IF NOT EXISTS `pbl_sub_world` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `sub_type` varchar(32) NOT NULL DEFAULT 'world', + `parent_id` bigint NOT NULL DEFAULT 0 COMMENT '父对象ID,0=根', + `sort_no` int NOT NULL DEFAULT 0, + `payload` longtext DEFAULT NULL COMMENT '子对象业务载荷JSON', + `ref_id` bigint DEFAULT NULL COMMENT '引用模块基表ID(world.id,只读不写)', + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_subworld_tenant_bp_code` (`tenant_id`,`blueprint_id`,`code`), + KEY `ix_pbl_subworld_tenant_bp` (`tenant_id`,`blueprint_id`,`parent_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象-world'; + +CREATE TABLE IF NOT EXISTS `pbl_sub_scene` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `sub_type` varchar(32) NOT NULL DEFAULT 'scene', + `parent_id` bigint NOT NULL DEFAULT 0, + `sort_no` int NOT NULL DEFAULT 0, + `payload` longtext DEFAULT NULL, + `ref_id` bigint DEFAULT NULL COMMENT 'scene.id(只读)', + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_subscene_tenant_bp_code` (`tenant_id`,`blueprint_id`,`code`), + KEY `ix_pbl_subscene_tenant_bp` (`tenant_id`,`blueprint_id`,`parent_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象-scene'; + +CREATE TABLE IF NOT EXISTS `pbl_sub_role` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `sub_type` varchar(32) NOT NULL DEFAULT 'role', + `parent_id` bigint NOT NULL DEFAULT 0, + `sort_no` int NOT NULL DEFAULT 0, + `payload` longtext DEFAULT NULL COMMENT '角色职责/权限/人数上限', + `ref_id` bigint DEFAULT NULL, + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_subrole_tenant_bp_code` (`tenant_id`,`blueprint_id`,`code`), + KEY `ix_pbl_subrole_tenant_bp` (`tenant_id`,`blueprint_id`,`parent_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象-role'; + +CREATE TABLE IF NOT EXISTS `pbl_sub_task` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `sub_type` varchar(32) NOT NULL DEFAULT 'task', + `parent_id` bigint NOT NULL DEFAULT 0, + `sort_no` int NOT NULL DEFAULT 0, + `stage` varchar(32) DEFAULT NULL COMMENT '所属阶段编码', + `payload` longtext DEFAULT NULL COMMENT '任务目标/步骤/产出要求', + `ref_id` bigint DEFAULT NULL, + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_subtask_tenant_bp_code` (`tenant_id`,`blueprint_id`,`code`), + KEY `ix_pbl_subtask_tenant_bp_stage` (`tenant_id`,`blueprint_id`,`stage`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象-task'; + +CREATE TABLE IF NOT EXISTS `pbl_sub_rule` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `sub_type` varchar(32) NOT NULL DEFAULT 'rule', + `parent_id` bigint NOT NULL DEFAULT 0, + `sort_no` int NOT NULL DEFAULT 0, + `rule_kind` varchar(32) DEFAULT NULL COMMENT 'trigger/constraint/scoring/flow', + `payload` longtext DEFAULT NULL COMMENT '规则表达式(script_engine 可编译)', + `ref_id` bigint DEFAULT NULL COMMENT 'script_engine.script.id(只读)', + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_subrule_tenant_bp_code` (`tenant_id`,`blueprint_id`,`code`), + KEY `ix_pbl_subrule_tenant_bp_kind` (`tenant_id`,`blueprint_id`,`rule_kind`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象-rule'; + +CREATE TABLE IF NOT EXISTS `pbl_sub_artifact` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `sub_type` varchar(32) NOT NULL DEFAULT 'artifact', + `parent_id` bigint NOT NULL DEFAULT 0, + `sort_no` int NOT NULL DEFAULT 0, + `artifact_type` varchar(32) DEFAULT NULL COMMENT '产出物类型编码', + `payload` longtext DEFAULT NULL COMMENT '产出物规格/验收标准', + `ref_id` bigint DEFAULT NULL, + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_subarti_tenant_bp_code` (`tenant_id`,`blueprint_id`,`code`), + KEY `ix_pbl_subarti_tenant_bp_type` (`tenant_id`,`blueprint_id`,`artifact_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象-artifact(产出物定义)'; + +CREATE TABLE IF NOT EXISTS `pbl_sub_assessment` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `sub_type` varchar(32) NOT NULL DEFAULT 'assessment', + `parent_id` bigint NOT NULL DEFAULT 0, + `sort_no` int NOT NULL DEFAULT 0, + `payload` longtext DEFAULT NULL COMMENT '评估维度/权重/rubric 引用', + `ref_id` bigint DEFAULT NULL, + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_subassess_tenant_bp_code` (`tenant_id`,`blueprint_id`,`code`), + KEY `ix_pbl_subassess_tenant_bp` (`tenant_id`,`blueprint_id`,`parent_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象-assessment(评估定义)'; + +CREATE TABLE IF NOT EXISTS `pbl_sub_link` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `from_type` varchar(32) NOT NULL COMMENT '源子对象类型', + `from_id` bigint NOT NULL, + `to_type` varchar(32) NOT NULL COMMENT '目标子对象类型', + `to_id` bigint NOT NULL, + `link_kind` varchar(32) NOT NULL DEFAULT 'ref' COMMENT 'ref/depends/produces/assesses', + `payload` longtext DEFAULT NULL, + `status` varchar(32) NOT NULL DEFAULT 'active', + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_sublink_tenant_edge` (`tenant_id`,`blueprint_id`,`from_type`,`from_id`,`to_type`,`to_id`,`link_kind`), + KEY `ix_pbl_sublink_tenant_to` (`tenant_id`,`blueprint_id`,`to_type`,`to_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='子对象关系边(泛化契约)'; + +-- ----------------------------------------------------------------------------- +-- M2 pbl_validation —— 14 维校验 + 5 级质量状态(3 表) +-- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_validation_rule` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `code` VARCHAR(64) NOT NULL COMMENT "规则编码", - `dimension` VARCHAR(64) NOT NULL COMMENT "维度", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `rule_json` LONGTEXT NULL COMMENT "规则体", - `threshold_json` LONGTEXT NULL COMMENT "阈值(可配,Q-OPEN-8)", - `severity` VARCHAR(64) NOT NULL COMMENT "严重度", - `enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `code` varchar(32) NOT NULL COMMENT '规则编码', + `dimension` varchar(32) NOT NULL COMMENT '14 维之一', + `name` varchar(128) NOT NULL, + `severity` varchar(32) NOT NULL DEFAULT 'error' COMMENT 'error/warn/info', + `expression` longtext DEFAULT NULL COMMENT '校验表达式/JSON', + `is_builtin` tinyint NOT NULL DEFAULT 1, + `enabled` tinyint NOT NULL DEFAULT 1, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_vr_code` (`tenant_id`, `code`, `version_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="校验规则(14 维,规则 JSON 形态 script_type=1)"; + UNIQUE KEY `uk_pbl_vrule_tenant_code` (`tenant_id`,`code`), + KEY `ix_pbl_vrule_tenant_dim` (`tenant_id`,`dimension`,`enabled`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='校验规则库(14维)'; -CREATE TABLE IF NOT EXISTS `pbl_validation_result` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "蓝图ID", - `blueprint_version_no` INT NOT NULL DEFAULT 0 COMMENT "校验时版本", - `dimension_count` INT NOT NULL DEFAULT 0 COMMENT "维度数(=14)", - `passed_count` INT NOT NULL DEFAULT 0 COMMENT "通过数", - `failed_count` INT NOT NULL DEFAULT 0 COMMENT "失败数", - `score` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT "得分", - `quality_state` VARCHAR(64) NOT NULL COMMENT "质量状态", - `findings_json` LONGTEXT NULL COMMENT "逐维结论", - `rule_set_version` VARCHAR(64) NOT NULL COMMENT "规则集版本", - `created_by` VARCHAR(32) NOT NULL COMMENT "触发人", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_validation_run` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `version` bigint NOT NULL DEFAULT 0, + `run_no` varchar(32) NOT NULL COMMENT '运行编号', + `status` varchar(32) NOT NULL DEFAULT 'running' COMMENT 'running/passed/failed/error', + `quality_level` varchar(32) NOT NULL DEFAULT 'L0' COMMENT 'L0~L4(5级质量状态)', + `score` int NOT NULL DEFAULT 0, + `error_count` int NOT NULL DEFAULT 0, + `warn_count` int NOT NULL DEFAULT 0, + `dim_result` longtext DEFAULT NULL COMMENT '14维结果JSON(确定性序列化)', + `content_hash` varchar(64) DEFAULT NULL, + `start_time` datetime DEFAULT NULL, + `end_time` datetime DEFAULT NULL, + `create_user` varchar(32) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - KEY `idx_vres_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="校验结果(14 维 + 5 级质量状态)"; + UNIQUE KEY `uk_pbl_vrun_tenant_no` (`tenant_id`,`run_no`), + KEY `ix_pbl_vrun_tenant_bp` (`tenant_id`,`blueprint_id`,`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='校验运行记录'; --- ===== pbl_compiler (M3) ===== -CREATE TABLE IF NOT EXISTS `pbl_compiler_version` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `code` VARCHAR(64) NOT NULL COMMENT "版本编码", - `semver` VARCHAR(64) NOT NULL COMMENT "语义化版本", - `rules_hash` VARCHAR(32) NOT NULL COMMENT "规则集SHA256前缀", - `entrypoint` VARCHAR(64) NOT NULL COMMENT "入口函数", - `enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", - `notes` TEXT NULL COMMENT "说明", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_validation_issue` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `run_id` bigint NOT NULL, + `rule_code` varchar(32) NOT NULL, + `dimension` varchar(32) NOT NULL, + `severity` varchar(32) NOT NULL DEFAULT 'error', + `target_type` varchar(32) DEFAULT NULL, + `target_id` bigint DEFAULT NULL, + `path` varchar(255) DEFAULT NULL COMMENT 'JSONPath 定位', + `message` varchar(512) NOT NULL, + `suggestion` varchar(512) DEFAULT NULL, + `status` varchar(32) NOT NULL DEFAULT 'open' COMMENT 'open/fixed/ignored', + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_cv_code` (`tenant_id`, `code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="编译器版本(29.6 确定性)"; + KEY `ix_pbl_vissue_tenant_run` (`tenant_id`,`run_id`,`severity`), + KEY `ix_pbl_vissue_tenant_rule` (`tenant_id`,`rule_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='校验问题明细'; + +-- ----------------------------------------------------------------------------- +-- M3a/M3b pbl_compiler —— 确定性编译 + Game Definition(3 表) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `pbl_compile_job` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `version` bigint NOT NULL, + `job_no` varchar(32) NOT NULL, + `compiler_ver` varchar(32) NOT NULL DEFAULT 'v1' COMMENT '编译器版本', + `status` varchar(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/running/success/failed', + `input_hash` varchar(64) NOT NULL COMMENT '输入快照sha256(确定性依据)', + `output_hash` varchar(64) DEFAULT NULL, + `log` longtext DEFAULT NULL, + `start_time` datetime DEFAULT NULL, + `end_time` datetime DEFAULT NULL, + `create_user` varchar(32) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_cjob_tenant_no` (`tenant_id`,`job_no`), + KEY `ix_pbl_cjob_tenant_bp` (`tenant_id`,`blueprint_id`,`version`), + KEY `ix_pbl_cjob_tenant_inhash` (`tenant_id`,`input_hash`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='编译作业'; + +CREATE TABLE IF NOT EXISTS `pbl_compile_artifact` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `job_id` bigint NOT NULL, + `kind` varchar(32) NOT NULL COMMENT 'game_definition/script/scene_pack/report', + `name` varchar(128) NOT NULL, + `content` longtext DEFAULT NULL COMMENT '产物内容(JSON/文本)', + `file_path` varchar(255) DEFAULT NULL, + `content_hash` varchar(64) NOT NULL, + `size_bytes` bigint NOT NULL DEFAULT 0, + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_pbl_carti_tenant_job` (`tenant_id`,`job_id`,`kind`), + KEY `ix_pbl_carti_tenant_hash` (`tenant_id`,`content_hash`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='编译产物'; CREATE TABLE IF NOT EXISTS `pbl_game_definition` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "来源蓝图", - `blueprint_version_no` INT NOT NULL DEFAULT 0 COMMENT "蓝图版本", - `compiler_version_id` BIGINT UNSIGNED NOT NULL COMMENT "编译版本", - `content_fingerprint` VARCHAR(32) NOT NULL COMMENT "SHA-256 指纹", - `definition_json` LONGTEXT NULL COMMENT "Game Definition 正文", - `world_id` BIGINT UNSIGNED NOT NULL COMMENT "落库 world", - `scene_id` BIGINT UNSIGNED NOT NULL COMMENT "落库 scene", - `entity_count` INT NOT NULL DEFAULT 0 COMMENT "实体数", - `event_count` INT NOT NULL DEFAULT 0 COMMENT "事件数", - `quality_state` VARCHAR(64) NOT NULL COMMENT "编译时质量状态", - `duration_ms` INT NOT NULL DEFAULT 0 COMMENT "耗时", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `blueprint_id` bigint NOT NULL, + `version` bigint NOT NULL, + `gd_version` varchar(32) NOT NULL DEFAULT '1.0' COMMENT 'Game Definition schema 版本', + `definition` longtext NOT NULL COMMENT 'GD 全量JSON(world/scene/entity/rule/task/role/artifact)', + `content_hash` varchar(64) NOT NULL, + `status` varchar(32) NOT NULL DEFAULT 'draft' COMMENT 'draft/published/archived', + `publish_time` datetime DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_gd_fp` (`tenant_id`, `content_fingerprint`), - KEY `idx_gd_bp` (`tenant_id`, `blueprint_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Game Definition(第9章 schema + 指纹)"; + UNIQUE KEY `uk_pbl_gd_tenant_bp_ver` (`tenant_id`,`blueprint_id`,`version`), + KEY `ix_pbl_gd_tenant_status` (`tenant_id`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Game Definition(编译终产物)'; -CREATE TABLE IF NOT EXISTS `pbl_capability_registry` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `capability_key` VARCHAR(64) NOT NULL COMMENT "能力键", - `category` VARCHAR(64) NOT NULL COMMENT "分类", - `args_schema_json` LONGTEXT NULL COMMENT "参数JSON Schema", - `permission_required` VARCHAR(64) NOT NULL COMMENT "所需权限", - `is_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", - `description` TEXT NULL COMMENT "说明", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +-- ----------------------------------------------------------------------------- +-- M4a/M4b pbl_agent_runtime —— Designer/Critic 运行时 + fail-closed 工具裁决(4 表) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `pbl_agent_tool_policy` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `tool_name` varchar(64) NOT NULL COMMENT '工具名(22 个之一)', + `agent_role` varchar(32) NOT NULL DEFAULT '*' COMMENT 'designer/critic/*', + `enabled` tinyint NOT NULL DEFAULT 0 COMMENT '默认0=fail-closed 拒绝', + `risk_level` varchar(32) NOT NULL DEFAULT 'high' COMMENT 'low/medium/high', + `reason` varchar(255) DEFAULT NULL COMMENT '启用/禁用理由', + `write_scope` varchar(32) NOT NULL DEFAULT 'none' COMMENT 'none/pbl_only/protected', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_cap_key` (`tenant_id`, `capability_key`, `version_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Capability Registry(第11章缺口补齐)"; + UNIQUE KEY `uk_pbl_toolpol_tenant_tool_role` (`tenant_id`,`tool_name`,`agent_role`), + KEY `ix_pbl_toolpol_tenant_enabled` (`tenant_id`,`enabled`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='工具白名单策略(13启用/9禁用,缺省拒绝)'; --- ===== pbl_agent_runtime (M4) ===== -CREATE TABLE IF NOT EXISTS `pbl_agent_def` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `code` VARCHAR(64) NOT NULL COMMENT "Agent编码", - `agent_type` VARCHAR(64) NOT NULL COMMENT "类型", - `name` VARCHAR(128) NOT NULL COMMENT "名称", - `model_route` VARCHAR(64) NOT NULL COMMENT "pipeline-llm 路由", - `system_prompt_txt` TEXT NULL COMMENT "提示词", - `tool_whitelist_json` LONGTEXT NULL COMMENT "工具白名单", - `permission_mode` VARCHAR(64) NOT NULL COMMENT "read/write 模式", - `is_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_agent_session` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `session_no` varchar(32) NOT NULL, + `blueprint_id` bigint DEFAULT NULL, + `agent_role` varchar(32) NOT NULL COMMENT 'designer/critic', + `status` varchar(32) NOT NULL DEFAULT 'running' COMMENT 'running/finished/aborted', + `goal` varchar(512) DEFAULT NULL, + `turn_count` int NOT NULL DEFAULT 0, + `tool_calls` int NOT NULL DEFAULT 0, + `denied_calls` int NOT NULL DEFAULT 0 COMMENT 'fail-closed 拒绝次数', + `result` longtext DEFAULT NULL, + `start_time` datetime DEFAULT NULL, + `end_time` datetime DEFAULT NULL, + `create_user` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_ad_code` (`tenant_id`, `code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Agent 定义(Designer/Critic)"; + UNIQUE KEY `uk_pbl_asess_tenant_no` (`tenant_id`,`session_no`), + KEY `ix_pbl_asess_tenant_bp` (`tenant_id`,`blueprint_id`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Agent 会话'; -CREATE TABLE IF NOT EXISTS `pbl_tool_registry` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `tool_key` VARCHAR(64) NOT NULL COMMENT "工具键", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `agent_scope` VARCHAR(64) NOT NULL COMMENT "作用域", - `permission_required` VARCHAR(64) NOT NULL COMMENT "所需权限", - `enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", - `params_schema_json` LONGTEXT NULL COMMENT "参数白名单Schema", - `disabled_reason` VARCHAR(64) NOT NULL COMMENT "禁用原因", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_agent_message` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `session_id` bigint NOT NULL, + `seq_no` int NOT NULL COMMENT '会话内序号', + `role` varchar(32) NOT NULL COMMENT 'system/user/assistant/tool', + `content` longtext DEFAULT NULL, + `token_in` int NOT NULL DEFAULT 0, + `token_out` int NOT NULL DEFAULT 0, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_tr_key` (`tenant_id`, `tool_key`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="工具注册表(13 启用 / 9 禁用含 pbl.publish)"; - -CREATE TABLE IF NOT EXISTS `pbl_agent_trace` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `trace_uid` VARCHAR(32) NOT NULL COMMENT "轨迹UID", - `agent_code` VARCHAR(64) NOT NULL COMMENT "Agent", - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "目标蓝图", - `input_ref_json` LONGTEXT NULL COMMENT "要素1 输入引用", - `thought_txt` TEXT NULL COMMENT "要素2 思考", - `action_code` VARCHAR(64) NOT NULL COMMENT "要素3 动作", - `action_params_json` LONGTEXT NULL COMMENT "要素4 动作参数", - `observation_json` LONGTEXT NULL COMMENT "要素5 观察", - `result_state` VARCHAR(64) NOT NULL COMMENT "要素6 结果状态", - `llm_calls` INT NOT NULL DEFAULT 0 COMMENT "要素7 LLM调用数", - `duration_ms` INT NOT NULL DEFAULT 0 COMMENT "耗时", - `created_by` VARCHAR(32) NOT NULL COMMENT "触发用户", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - UNIQUE KEY `uk_at_uid` (`tenant_id`, `trace_uid`), - KEY `idx_at_tenant` (`tenant_id`, `created_at`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Agent 执行轨迹(第28章 7 要素)"; + UNIQUE KEY `uk_pbl_amsg_tenant_sess_seq` (`tenant_id`,`session_id`,`seq_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Agent 消息轨迹'; CREATE TABLE IF NOT EXISTS `pbl_agent_tool_call` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `trace_id` BIGINT UNSIGNED NOT NULL COMMENT "轨迹ID", - `tool_key` VARCHAR(64) NOT NULL COMMENT "工具键", - `request_json` LONGTEXT NULL COMMENT "请求", - `response_json` LONGTEXT NULL COMMENT "响应", - `ok` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "成功", - `error_code` VARCHAR(64) NOT NULL COMMENT "错误码", - `adjudication_code` VARCHAR(64) NOT NULL COMMENT "裁决结论", - `duration_ms` INT NOT NULL DEFAULT 0 COMMENT "耗时", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `session_id` bigint NOT NULL, + `message_id` bigint DEFAULT NULL, + `tool_name` varchar(64) NOT NULL, + `verdict` varchar(32) NOT NULL COMMENT 'allow/deny', + `deny_reason` varchar(255) DEFAULT NULL COMMENT 'not_in_whitelist/write_protected/missing_tenant/...', + `args` longtext DEFAULT NULL, + `result` longtext DEFAULT NULL, + `error_code` varchar(32) DEFAULT NULL, + `elapsed_ms` int NOT NULL DEFAULT 0, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - KEY `idx_tc_trace` (`tenant_id`, `trace_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="工具调用明细(服务端裁决留痕)"; + KEY `ix_pbl_atool_tenant_sess` (`tenant_id`,`session_id`,`verdict`), + KEY `ix_pbl_atool_tenant_tool` (`tenant_id`,`tool_name`,`verdict`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='工具调用裁决审计(fail-closed 全量留痕)'; -CREATE TABLE IF NOT EXISTS `pbl_approval` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `approval_uid` VARCHAR(32) NOT NULL COMMENT "审批UID", - `approval_type` VARCHAR(64) NOT NULL COMMENT "类型", - `target_type` VARCHAR(64) NOT NULL COMMENT "对象类型", - `target_id` BIGINT UNSIGNED NOT NULL COMMENT "对象ID", - `payload_json` LONGTEXT NULL COMMENT "变更内容", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `requested_by` VARCHAR(32) NOT NULL COMMENT "发起人", - `decided_by` VARCHAR(32) NOT NULL COMMENT "决策人", - `decided_at` DATETIME NULL NULL COMMENT "决策时间", - `comment_txt` TEXT NULL COMMENT "意见", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - UNIQUE KEY `uk_ap_uid` (`tenant_id`, `approval_uid`), - KEY `idx_ap_status` (`tenant_id`, `status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="人工审批单(14.2 四类,无绕过路径)"; - -CREATE TABLE IF NOT EXISTS `pbl_llm_call_log` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `call_uid` VARCHAR(32) NOT NULL COMMENT "调用UID", - `agent_code` VARCHAR(64) NOT NULL COMMENT "Agent", - `route` VARCHAR(64) NOT NULL COMMENT "路由", - `model` VARCHAR(64) NOT NULL COMMENT "实际模型", - `prompt_hash` VARCHAR(32) NOT NULL COMMENT "提示词哈希", - `completion_tokens` INT NOT NULL DEFAULT 0 COMMENT "tokens", - `latency_ms` INT NOT NULL DEFAULT 0 COMMENT "时延", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `error_code` VARCHAR(64) NOT NULL COMMENT "错误码", - `fallback_used` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "走兜底", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - UNIQUE KEY `uk_lc_uid` (`tenant_id`, `call_uid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="LLM 调用日志(超时/重试/限流/兜底留痕)"; - --- ===== pbl_evidence (M5) ===== +-- ----------------------------------------------------------------------------- +-- M5a/M5b pbl_evidence —— 产出物与证据幂等采集(3 表) +-- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_artifact` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `artifact_uid` VARCHAR(32) NOT NULL COMMENT "产出UID", - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "蓝图", - `team_id` BIGINT UNSIGNED NOT NULL COMMENT "团队", - `creator_id` VARCHAR(32) NOT NULL COMMENT "作者", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `artifact_type` VARCHAR(64) NOT NULL COMMENT "类型", - `content_json` LONGTEXT NULL COMMENT "内容", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `submitted_at` DATETIME NULL NULL COMMENT "提交时间", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `artifact_no` varchar(32) NOT NULL COMMENT '产出物编号', + `blueprint_id` bigint DEFAULT NULL, + `session_id` bigint DEFAULT NULL COMMENT '运行时会话(scense_runtime)', + `team_id` varchar(32) DEFAULT NULL, + `student_id` varchar(32) DEFAULT NULL, + `artifact_type` varchar(32) NOT NULL COMMENT '类型编码', + `name` varchar(128) NOT NULL, + `content` longtext DEFAULT NULL, + `file_path` varchar(255) DEFAULT NULL, + `content_hash` varchar(64) NOT NULL COMMENT '幂等键之一', + `idempotent_key` varchar(64) NOT NULL COMMENT '幂等键(tenant+source+hash)', + `source` varchar(32) NOT NULL DEFAULT 'runtime' COMMENT 'runtime/upload/agent/compile', + `status` varchar(32) NOT NULL DEFAULT 'collected', + `collect_time` datetime DEFAULT NULL, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_art_uid` (`tenant_id`, `artifact_uid`), - KEY `idx_art_session` (`tenant_id`, `session_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="学生产出物(版本内联 version_no)"; + UNIQUE KEY `uk_pbl_arti_tenant_idem` (`tenant_id`,`idempotent_key`), + KEY `ix_pbl_arti_tenant_bp` (`tenant_id`,`blueprint_id`,`artifact_type`), + KEY `ix_pbl_arti_tenant_team` (`tenant_id`,`team_id`,`student_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='产出物(幂等采集)'; -CREATE TABLE IF NOT EXISTS `pbl_evidence` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `artifact_id` BIGINT UNSIGNED NOT NULL COMMENT "产出物", - `evidence_type` VARCHAR(64) NOT NULL COMMENT "证据类型", - `source_event_id` VARCHAR(32) NOT NULL COMMENT "来源 runtime_event", - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `learner_id` VARCHAR(32) NOT NULL COMMENT "学习者", - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "蓝图", - `payload_json` LONGTEXT NULL COMMENT "证据体", - `occurred_at` DATETIME NOT NULL COMMENT "发生时间", - `dedup_key` VARCHAR(32) NOT NULL COMMENT "去重键", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_evidence_event` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `artifact_id` bigint DEFAULT NULL, + `session_id` bigint DEFAULT NULL, + `event_type` varchar(32) NOT NULL COMMENT '事件类型编码', + `event_time` datetime NOT NULL, + `actor_id` varchar(32) DEFAULT NULL, + `payload` longtext DEFAULT NULL, + `content_hash` varchar(64) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_ev_dedup` (`tenant_id`, `source_event_id`, `evidence_type`), - KEY `idx_ev_learner` (`tenant_id`, `learner_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="学习证据(幂等采集,唯一索引防重)"; + KEY `ix_pbl_eev_tenant_arti` (`tenant_id`,`artifact_id`), + KEY `ix_pbl_eev_tenant_sess_time` (`tenant_id`,`session_id`,`event_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='证据事件流'; --- ===== pbl_assessment (M6) ===== +CREATE TABLE IF NOT EXISTS `pbl_evidence_file` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `artifact_id` bigint NOT NULL, + `file_name` varchar(255) NOT NULL, + `file_path` varchar(255) NOT NULL, + `mime_type` varchar(64) DEFAULT NULL, + `size_bytes` bigint NOT NULL DEFAULT 0, + `content_hash` varchar(64) NOT NULL, + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_efile_tenant_arti_hash` (`tenant_id`,`artifact_id`,`content_hash`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='证据附件'; + +-- ----------------------------------------------------------------------------- +-- M6 pbl_assessment —— Rubric 加权评估与报告(3 表) +-- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_rubric` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `code` VARCHAR(64) NOT NULL COMMENT "编码", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "蓝图", - `criteria_json` LONGTEXT NULL COMMENT "准则数组(含 learning_goal_id/weight)", - `total_weight` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT "权重合计", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `approval_id` BIGINT UNSIGNED NOT NULL COMMENT "变更审批单", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `blueprint_id` bigint DEFAULT NULL, + `total_weight` int NOT NULL DEFAULT 100, + `status` varchar(32) NOT NULL DEFAULT 'draft', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_ru_code` (`tenant_id`, `code`, `version_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Rubric(准则内联 criteria_json,权重合计=100%)"; + UNIQUE KEY `uk_pbl_rubric_tenant_code` (`tenant_id`,`code`), + KEY `ix_pbl_rubric_tenant_bp` (`tenant_id`,`blueprint_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='评估量规'; -CREATE TABLE IF NOT EXISTS `pbl_assessment_record` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `record_uid` VARCHAR(32) NOT NULL COMMENT "记录UID", - `learner_id` VARCHAR(32) NOT NULL COMMENT "学习者", - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "蓝图", - `artifact_id` BIGINT UNSIGNED NOT NULL COMMENT "产出物", - `evidence_id` BIGINT UNSIGNED NOT NULL COMMENT "主证据", - `rubric_id` BIGINT UNSIGNED NOT NULL COMMENT "Rubric", - `scores_json` LONGTEXT NULL COMMENT "逐准则得分", - `total_score` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT "加权总分", - `max_score` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT "满分", - `band` VARCHAR(64) NOT NULL COMMENT "等级", - `alignment_json` LONGTEXT NULL COMMENT "与学习目标对齐", - `feedback_txt` TEXT NULL COMMENT "反馈", - `assessor_type` VARCHAR(64) NOT NULL COMMENT "评估者类型", - `assessor_id` VARCHAR(32) NOT NULL COMMENT "评估者", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_rubric_criterion` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `rubric_id` bigint NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(128) NOT NULL, + `dimension` varchar(32) DEFAULT NULL, + `weight` int NOT NULL DEFAULT 0 COMMENT '权重(整数,总和=100)', + `max_score` int NOT NULL DEFAULT 5, + `levels` longtext DEFAULT NULL COMMENT '等级描述JSON(确定性序列化)', + `sort_no` int NOT NULL DEFAULT 0, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_ar_uid` (`tenant_id`, `record_uid`), - KEY `idx_ar_learner` (`tenant_id`, `learner_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="评估记录(total_score 派生 + 对齐判定)"; + UNIQUE KEY `uk_pbl_rcrit_tenant_rubric_code` (`tenant_id`,`rubric_id`,`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='量规评分项'; --- ===== pbl_kdb_ext (M7) ===== -CREATE TABLE IF NOT EXISTS `pbl_kdb_item` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `code` VARCHAR(64) NOT NULL COMMENT "条目编码", - `item_type` VARCHAR(64) NOT NULL COMMENT "类型", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `summary_txt` TEXT NULL COMMENT "摘要", - `payload_json` LONGTEXT NULL COMMENT "内容(无向量,Q-OPEN-5)", - `source` VARCHAR(64) NOT NULL COMMENT "来源", - `is_write_locked` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "写锁(恒1)", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_assessment_report` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `report_no` varchar(32) NOT NULL, + `rubric_id` bigint NOT NULL, + `blueprint_id` bigint DEFAULT NULL, + `team_id` varchar(32) DEFAULT NULL, + `student_id` varchar(32) DEFAULT NULL, + `score_total` int NOT NULL DEFAULT 0, + `level` varchar(32) NOT NULL DEFAULT 'L0', + `detail` longtext DEFAULT NULL COMMENT '逐项得分JSON', + `evidence_ref` longtext DEFAULT NULL COMMENT '引用证据ID列表JSON', + `assessor` varchar(32) DEFAULT NULL COMMENT 'teacher/agent/critic', + `status` varchar(32) NOT NULL DEFAULT 'draft', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_kdb_code` (`tenant_id`, `code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="KDB 条目(建表不写入,is_write_locked 恒 1)"; + UNIQUE KEY `uk_pbl_areport_tenant_no` (`tenant_id`,`report_no`), + KEY `ix_pbl_areport_tenant_team` (`tenant_id`,`team_id`,`student_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='评估报告'; +-- ----------------------------------------------------------------------------- +-- M7 pbl_kdb_ext —— KDB 只读桩与匿名聚合(零写入,1 表:查询留痕) +-- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_kdb_query_log` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `query_uid` VARCHAR(32) NOT NULL COMMENT "查询UID", - `caller_type` VARCHAR(64) NOT NULL COMMENT "调用方类型", - `caller_id` VARCHAR(32) NOT NULL COMMENT "调用方", - `query_json` LONGTEXT NULL COMMENT "查询条件", - `hit_count` INT NOT NULL DEFAULT 0 COMMENT "命中数", - `latency_ms` INT NOT NULL DEFAULT 0 COMMENT "时延", - `ok` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "成功", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `query_no` varchar(32) NOT NULL, + `kdb_key` varchar(64) NOT NULL COMMENT '知识点/知识图谱键', + `scope` varchar(32) NOT NULL DEFAULT 'read' COMMENT '只允许 read', + `anonymized` tinyint NOT NULL DEFAULT 1 COMMENT '是否匿名聚合', + `result_count` int NOT NULL DEFAULT 0, + `caller` varchar(32) DEFAULT NULL, + `elapsed_ms` int NOT NULL DEFAULT 0, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_kql_uid` (`tenant_id`, `query_uid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="KDB 检索日志(只读侧留痕)"; + UNIQUE KEY `uk_pbl_kdblog_tenant_no` (`tenant_id`,`query_no`), + KEY `ix_pbl_kdblog_tenant_key` (`tenant_id`,`kdb_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='KDB只读查询留痕(零写入基表)'; --- ===== pbl_domain_ext (M8) ===== -CREATE TABLE IF NOT EXISTS `pbl_tenant` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `tenant_uid` VARCHAR(32) NOT NULL COMMENT "租户UID", - `name` VARCHAR(128) NOT NULL COMMENT "名称", - `kind` VARCHAR(64) NOT NULL COMMENT "类型", - `plan` VARCHAR(64) NOT NULL COMMENT "套餐", - `seat_limit` INT NOT NULL DEFAULT 0 COMMENT "席位", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `config_json` LONGTEXT NULL COMMENT "配置", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +-- ----------------------------------------------------------------------------- +-- M8 pbl_domain_ext —— world/scene/entity 薄扩展(不改基表,3 表) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `pbl_world_ext` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `world_id` bigint NOT NULL COMMENT 'world.id(只读引用,无FK)', + `blueprint_id` bigint DEFAULT NULL, + `class_id` varchar(32) DEFAULT NULL COMMENT '班级', + `team_id` varchar(32) DEFAULT NULL COMMENT '团队', + `ext` longtext DEFAULT NULL COMMENT '扩展属性JSON', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_ten_uid` (`tenant_id`, `tenant_uid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="租户(多租户边界第25章,不改基表)"; + UNIQUE KEY `uk_pbl_wext_tenant_world` (`tenant_id`,`world_id`), + KEY `ix_pbl_wext_tenant_class_team` (`tenant_id`,`class_id`,`team_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='world 薄扩展(租户/班级/团队关联)'; -CREATE TABLE IF NOT EXISTS `pbl_class` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `class_uid` VARCHAR(32) NOT NULL COMMENT "班级UID", - `name` VARCHAR(128) NOT NULL COMMENT "名称", - `grade` VARCHAR(64) NOT NULL COMMENT "年级", - `teacher_id` VARCHAR(32) NOT NULL COMMENT "教师", - `student_count` INT NOT NULL DEFAULT 0 COMMENT "学生数", - `roster_json` LONGTEXT NULL COMMENT "花名册", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_scene_ext` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `scene_id` bigint NOT NULL COMMENT 'scene.id(只读引用)', + `blueprint_id` bigint DEFAULT NULL, + `class_id` varchar(32) DEFAULT NULL, + `team_id` varchar(32) DEFAULT NULL, + `ext` longtext DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_cls_uid` (`tenant_id`, `class_uid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="班级(花名册内联 roster_json)"; + UNIQUE KEY `uk_pbl_sext_tenant_scene` (`tenant_id`,`scene_id`), + KEY `ix_pbl_sext_tenant_class_team` (`tenant_id`,`class_id`,`team_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='scene 薄扩展'; -CREATE TABLE IF NOT EXISTS `pbl_team` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `team_uid` VARCHAR(32) NOT NULL COMMENT "团队UID", - `class_id` BIGINT UNSIGNED NOT NULL COMMENT "班级", - `name` VARCHAR(128) NOT NULL COMMENT "名称", - `mission_id` BIGINT UNSIGNED NOT NULL COMMENT "Mission", - `members_json` LONGTEXT NULL COMMENT "成员", - `capacity` INT NOT NULL DEFAULT 0 COMMENT "容量", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "绑定会话", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_entity_ext` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `entity_id` bigint NOT NULL COMMENT 'entity.id(只读引用)', + `blueprint_id` bigint DEFAULT NULL, + `class_id` varchar(32) DEFAULT NULL, + `team_id` varchar(32) DEFAULT NULL, + `role_code` varchar(32) DEFAULT NULL COMMENT '对应 pbl_sub_role.code', + `ext` longtext DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_team_uid` (`tenant_id`, `team_uid`), - KEY `idx_team_class` (`tenant_id`, `class_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="团队(成员内联 members_json,不改基表)"; + UNIQUE KEY `uk_pbl_eext_tenant_entity` (`tenant_id`,`entity_id`), + KEY `ix_pbl_eext_tenant_class_team` (`tenant_id`,`class_id`,`team_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='entity 薄扩展'; --- ===== pbl_scense_ext (M9) ===== -CREATE TABLE IF NOT EXISTS `pbl_session_member` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `user_id` VARCHAR(32) NOT NULL COMMENT "用户", - `role_code` VARCHAR(64) NOT NULL COMMENT "角色", - `team_id` BIGINT UNSIGNED NOT NULL COMMENT "团队", - `joined_at` DATETIME NULL NULL COMMENT "加入时间", - `last_seen_at` DATETIME NULL NULL COMMENT "心跳", - `state_version` BIGINT NOT NULL DEFAULT 0 COMMENT "已见状态版本", - `status` VARCHAR(64) NOT NULL COMMENT "状态", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - UNIQUE KEY `uk_sm` (`tenant_id`, `session_id`, `user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="会话成员(复用 world_session,不污染基表)"; - -CREATE TABLE IF NOT EXISTS `pbl_playtest_feedback` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `user_id` VARCHAR(32) NOT NULL COMMENT "用户", - `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "蓝图", - `feedback_type` VARCHAR(64) NOT NULL COMMENT "类型", - `rating` INT NOT NULL DEFAULT 0 COMMENT "评分", - `content_txt` TEXT NULL COMMENT "内容", - `context_json` LONGTEXT NULL COMMENT "上下文", - `handled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "已处理", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - KEY `idx_pf_session` (`tenant_id`, `session_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Playtest 反馈/反思(reflection 走证据链外的轻量表)"; - -CREATE TABLE IF NOT EXISTS `pbl_ui_page_registry` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `page_key` VARCHAR(64) NOT NULL COMMENT "页面键", - `title` VARCHAR(128) NOT NULL COMMENT "标题", - `route` VARCHAR(64) NOT NULL COMMENT "路由", - `widget_type` VARCHAR(64) NOT NULL COMMENT "渲染组件", - `requires_role` VARCHAR(64) NOT NULL COMMENT "所需角色", - `budget_ms` INT NOT NULL DEFAULT 0 COMMENT "渲染预算", - `enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", - `meta_json` LONGTEXT NULL COMMENT "元数据", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", - PRIMARY KEY (`id`), - UNIQUE KEY `uk_upr_key` (`tenant_id`, `page_key`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="前端覆盖层页面注册(12 页面 + 时延预算)"; - --- ===== pbl_runtime_ext (M11) ===== +-- ----------------------------------------------------------------------------- +-- M11a/M11b pbl_runtime_ext —— 单事务事件+状态写入与广播登记(2 表) +-- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `pbl_runtime_event` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `event_uid` VARCHAR(32) NOT NULL COMMENT "事件UID", - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `world_id` BIGINT UNSIGNED NOT NULL COMMENT "世界", - `actor_id` VARCHAR(32) NOT NULL COMMENT "触发者", - `event_type` VARCHAR(64) NOT NULL COMMENT "事件类型", - `payload_json` LONGTEXT NULL COMMENT "事件体", - `causation_id` VARCHAR(32) NOT NULL COMMENT "因果链", - `state_version` BIGINT NOT NULL DEFAULT 0 COMMENT "状态版本", - `seq` BIGINT NOT NULL DEFAULT 0 COMMENT "会话内单调序号", - `occurred_at` DATETIME NOT NULL COMMENT "发生时间", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `event_uid` varchar(64) NOT NULL COMMENT '事件唯一ID(幂等键)', + `session_id` bigint NOT NULL COMMENT 'scense_runtime 会话(只读引用)', + `world_id` bigint DEFAULT NULL, + `event_type` varchar(32) NOT NULL, + `source_id` varchar(32) DEFAULT NULL, + `target_id` varchar(32) DEFAULT NULL, + `payload` longtext DEFAULT NULL, + `tx_group` varchar(64) NOT NULL COMMENT '单事务分组ID(事件+状态+广播同组)', + `seq_no` bigint NOT NULL DEFAULT 0 COMMENT '会话内单调序号(重放依据)', + `applied` tinyint NOT NULL DEFAULT 0 COMMENT '是否已应用状态', + `broadcast_state` varchar(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/sent/failed', + `event_time` datetime NOT NULL, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_re_uid` (`tenant_id`, `event_uid`, `occurred_at`), - KEY `idx_re_session_seq` (`tenant_id`, `session_id`, `seq`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="运行时事件(append-only,按月 RANGE 分区)", - KEY `idx_re_part` (`occurred_at`) -PARTITION BY RANGE COLUMNS(`occurred_at`) ( - PARTITION p2026 VALUES LESS THAN ('2026-01-01'), - PARTITION p2027 VALUES LESS THAN ('2027-01-01'), - PARTITION p2028 VALUES LESS THAN ('2028-01-01'), - PARTITION p2029 VALUES LESS THAN ('2029-01-01'), - PARTITION p2030 VALUES LESS THAN ('2030-01-01'), - PARTITION p2031 VALUES LESS THAN ('2031-01-01') - PARTITION pmax VALUES LESS THAN (MAXVALUE) - ); + UNIQUE KEY `uk_pbl_rev_tenant_uid` (`tenant_id`,`event_uid`), + KEY `ix_pbl_rev_tenant_sess_seq` (`tenant_id`,`session_id`,`seq_no`), + KEY `ix_pbl_rev_tenant_tx` (`tenant_id`,`tx_group`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='运行时事件(单事务写入+可重放)'; -CREATE TABLE IF NOT EXISTS `pbl_entity_state` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `entity_id` VARCHAR(32) NOT NULL COMMENT "实体标识", - `state_json` LONGTEXT NULL COMMENT "状态", - `state_version` BIGINT NOT NULL DEFAULT 0 COMMENT "单调版本", - `checksum` VARCHAR(32) NOT NULL COMMENT "状态摘要", - `updated_by` VARCHAR(32) NOT NULL COMMENT "写入者", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +CREATE TABLE IF NOT EXISTS `pbl_runtime_state` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `session_id` bigint NOT NULL, + `state_key` varchar(64) NOT NULL COMMENT '状态键(entity/scene/global)', + `state_value` longtext DEFAULT NULL COMMENT '状态值JSON', + `version` bigint NOT NULL DEFAULT 0 COMMENT '乐观锁版本', + `last_event_id` bigint DEFAULT NULL COMMENT '最后应用事件ID', + `tx_group` varchar(64) DEFAULT NULL, + `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_es` (`tenant_id`, `session_id`, `entity_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="实体状态(服务端权威,state_version 客户端禁写)"; + UNIQUE KEY `uk_pbl_rstate_tenant_sess_key` (`tenant_id`,`session_id`,`state_key`), + KEY `ix_pbl_rstate_tenant_tx` (`tenant_id`,`tx_group`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='运行时状态(与事件同事务写入)'; -CREATE TABLE IF NOT EXISTS `pbl_world_state_snapshot` ( - `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, - `session_id` BIGINT UNSIGNED NOT NULL COMMENT "会话", - `snapshot_uid` VARCHAR(32) NOT NULL COMMENT "快照UID", - `state_fingerprint` VARCHAR(32) NOT NULL COMMENT "状态指纹", - `entities_json` LONGTEXT NULL COMMENT "实体集", - `events_through_seq` BIGINT NOT NULL DEFAULT 0 COMMENT "截至序号", - `reason` VARCHAR(64) NOT NULL COMMENT "触发原因", - `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", - `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", +-- ----------------------------------------------------------------------------- +-- M12 pbl_template —— 模板实例化记录(1 表) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `pbl_template_instance` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `template_id` bigint NOT NULL COMMENT 'pbl_blueprint_template.id', + `blueprint_id` bigint NOT NULL COMMENT '实例化产生的蓝图', + `params` longtext DEFAULT NULL COMMENT '实例化参数JSON', + `mode` varchar(32) NOT NULL DEFAULT 'online' COMMENT 'online/offline(离线兜底)', + `status` varchar(32) NOT NULL DEFAULT 'created', + `create_user` varchar(32) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `uk_wss_uid` (`tenant_id`, `snapshot_uid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="世界状态快照(掉线补齐/离线兜底)"; + UNIQUE KEY `uk_pbl_tinst_tenant_tpl_bp` (`tenant_id`,`template_id`,`blueprint_id`), + KEY `ix_pbl_tinst_tenant_status` (`tenant_id`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='模板实例化记录'; --- ===== 枚举 6+2 组由 pbl_appcodes.load_pbl_appcodes() 幂等注入 appcodes,本文件不含 INSERT ===== +-- ----------------------------------------------------------------------------- +-- M13 pbl_analytics —— 学习分析快照与指标(2 表) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `pbl_analytics_metric` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `metric_code` varchar(32) NOT NULL, + `scope` varchar(32) NOT NULL DEFAULT 'student' COMMENT 'student/team/class/blueprint', + `scope_id` varchar(32) NOT NULL, + `blueprint_id` bigint DEFAULT NULL, + `value_num` decimal(18,4) NOT NULL DEFAULT 0, + `value_text` varchar(255) DEFAULT NULL, + `stat_date` datetime NOT NULL COMMENT '统计时点(datetime,非TIMESTAMP)', + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_ametric_tenant_code_scope_date` (`tenant_id`,`metric_code`,`scope`,`scope_id`,`stat_date`), + KEY `ix_pbl_ametric_tenant_bp` (`tenant_id`,`blueprint_id`,`metric_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='分析指标明细'; + +CREATE TABLE IF NOT EXISTS `pbl_analytics_snapshot` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `snapshot_no` varchar(32) NOT NULL, + `blueprint_id` bigint DEFAULT NULL, + `class_id` varchar(32) DEFAULT NULL, + `period` varchar(32) DEFAULT NULL, + `content` longtext DEFAULT NULL COMMENT '聚合结果JSON(匿名化)', + `content_hash` varchar(64) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_pbl_asnap_tenant_no` (`tenant_id`,`snapshot_no`), + KEY `ix_pbl_asnap_tenant_bp_class` (`tenant_id`,`blueprint_id`,`class_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='分析快照'; + +-- ----------------------------------------------------------------------------- +-- M14 pbl_governance —— 治理与审计(append-only,1 表) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `pbl_audit_log` ( + `tenant_id` varchar(32) NOT NULL, + `id` bigint NOT NULL AUTO_INCREMENT, + `trace_id` varchar(64) NOT NULL COMMENT '链路追踪ID', + `actor_role` varchar(32) NOT NULL COMMENT 'owner.audit/teacher/agent.designer/agent.critic', + `actor_id` varchar(32) DEFAULT NULL, + `action` varchar(64) NOT NULL COMMENT '动作', + `object_type` varchar(32) DEFAULT NULL, + `object_id` varchar(64) DEFAULT NULL, + `result` varchar(32) NOT NULL DEFAULT 'success' COMMENT 'success/denied/error', + `error_code` varchar(32) DEFAULT NULL, + `detail` longtext DEFAULT NULL, + `client_ip` varchar(64) DEFAULT NULL, + `log_time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `ix_pbl_audit_tenant_time` (`tenant_id`,`log_time`), + KEY `ix_pbl_audit_tenant_trace` (`tenant_id`,`trace_id`), + KEY `ix_pbl_audit_tenant_obj` (`tenant_id`,`object_type`,`object_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='治理审计日志(append-only,owner.audit 独立角色,禁自删)'; + +-- ============================================================================= +-- 表清单(36): +-- pbl_blueprint(11): pbl_blueprint, pbl_blueprint_version, pbl_blueprint_template, +-- pbl_sub_world, pbl_sub_scene, pbl_sub_role, pbl_sub_task, +-- pbl_sub_rule, pbl_sub_artifact, pbl_sub_assessment, pbl_sub_link +-- pbl_validation(3): pbl_validation_rule, pbl_validation_run, pbl_validation_issue +-- pbl_compiler(3): pbl_compile_job, pbl_compile_artifact, pbl_game_definition +-- pbl_agent_runtime(4): pbl_agent_tool_policy, pbl_agent_session, pbl_agent_message, pbl_agent_tool_call +-- pbl_evidence(3): pbl_artifact, pbl_evidence_event, pbl_evidence_file +-- pbl_assessment(3): pbl_rubric, pbl_rubric_criterion, pbl_assessment_report +-- pbl_kdb_ext(1): pbl_kdb_query_log +-- pbl_domain_ext(3): pbl_world_ext, pbl_scene_ext, pbl_entity_ext +-- pbl_runtime_ext(2):pbl_runtime_event, pbl_runtime_state +-- pbl_template(1): pbl_template_instance +-- pbl_analytics(2): pbl_analytics_metric, pbl_analytics_snapshot +-- pbl_governance(1): pbl_audit_log +-- 合计 11+3+3+4+3+3+1+3+2+1+2+1 = 37 声明位(其中 pbl_sub_link 为关系边表, +-- 计入 blueprint 域 11 表内),业务表总计 36(pbl_common / pbl_appcodes 零表)。 +-- ============================================================================= diff --git a/wwwroot/index.ui b/wwwroot/index.ui new file mode 100644 index 0000000..dde7a00 --- /dev/null +++ b/wwwroot/index.ui @@ -0,0 +1,186 @@ +{ + "type": "UiWindow", + "id": "pbls_main_window", + "options": { + "title": "PBL Agent OS", + "layout": "border" + }, + "children": [ + { + "type": "Panel", + "id": "pbls_topbar", + "options": { + "region": "north", + "height": 56, + "layout": "hbox", + "cls": "pbls-topbar" + }, + "children": [ + { + "type": "Label", + "id": "pbls_app_title", + "options": { + "text": "PBL Agent OS", + "cls": "pbls-app-title" + } + }, + { + "type": "Label", + "id": "pbls_phase_label", + "options": { + "text": "Phase 0/1 · 蓝图 → 校验 → 编译 → Agent 运行时", + "cls": "pbls-phase" + } + }, + { + "type": "Button", + "id": "pbls_btn_logout", + "options": { + "text": "退出", + "align": "right", + "handler": "app.pbls_logout" + } + } + ] + }, + { + "type": "Menu", + "id": "pbls_nav_menu", + "options": { + "region": "west", + "width": 220, + "title": "功能导航" + }, + "children": [ + { + "type": "MenuItem", + "id": "mi_blueprint", + "options": { + "text": "蓝图管理", + "target": "/pbl_blueprint/index.ui" + } + }, + { + "type": "MenuItem", + "id": "mi_validation", + "options": { + "text": "蓝图校验(14 维)", + "target": "/pbl_validation/index.ui" + } + }, + { + "type": "MenuItem", + "id": "mi_compiler", + "options": { + "text": "编译器 / Game Definition", + "target": "/pbl_compiler/index.ui" + } + }, + { + "type": "MenuItem", + "id": "mi_agent_runtime", + "options": { + "text": "Agent 运行时(22 工具)", + "target": "/pbl_agent_runtime/index.ui" + } + }, + { + "type": "MenuItem", + "id": "mi_evidence", + "options": { + "text": "产出物与证据", + "target": "/pbl_evidence/index.ui" + } + }, + { + "type": "MenuItem", + "id": "mi_assessment", + "options": { + "text": "Rubric 评估报告", + "target": "/pbl_assessment/index.ui" + } + }, + { + "type": "MenuItem", + "id": "mi_appcodes", + "options": { + "text": "枚举编码", + "target": "/pbl_appcodes/index.ui" + } + }, + { + "type": "MenuItem", + "id": "mi_governance", + "options": { + "text": "治理与审计", + "target": "/pbl_governance/index.ui" + } + } + ] + }, + { + "type": "TabPanel", + "id": "pbls_main_tabs", + "options": { + "region": "center", + "closable": true + }, + "children": [ + { + "type": "Panel", + "id": "tab_home", + "options": { + "title": "首页", + "layout": "vbox" + }, + "children": [ + { + "type": "Label", + "id": "home_welcome", + "options": { + "text": "欢迎使用 PBL Agent OS。请从左侧导航选择功能模块。", + "cls": "pbls-welcome" + } + }, + { + "type": "DataViewer", + "id": "home_blueprint_list", + "options": { + "title": "最近蓝图", + "url": "/api/pbl_blueprint_list.dspy", + "pageSize": 10 + } + } + ] + } + ] + }, + { + "type": "Panel", + "id": "pbls_statusbar", + "options": { + "region": "south", + "height": 28, + "layout": "hbox" + }, + "children": [ + { + "type": "Label", + "id": "status_tenant", + "options": { + "text": "租户:-", + "cls": "pbls-status" + } + }, + { + "type": "Label", + "id": "status_port", + "options": { + "text": "端口:9300", + "cls": "pbls-status" + } + } + ] + } + ] +}