132 lines
7.8 KiB
Markdown
132 lines
7.8 KiB
Markdown
---
|
||
name: pbl_blueprint
|
||
description: PBL 蓝图聚合根与子对象、版本、模板(M1a)。蓝图 CRUD/树/fork、7 类子对象泛化契约、版本 change_delta、模板实例化与离线兜底。所有读写 tenant_id 强制打头,缺失即 fail-closed 拒绝。
|
||
---
|
||
|
||
# pbl_blueprint 模块技能
|
||
|
||
## 1. 模块定位
|
||
`pbl_blueprint` 是 PBL(Project-Based Learning)平台的**蓝图域聚合根模块**,负责:
|
||
- 蓝图主表 `pbl_blueprint` 的 CRUD / 列表 / 树 / fork
|
||
- 7 类子对象的**泛化契约**(一套 CRUD 覆盖 7 张表)
|
||
- 版本快照 `pbl_blueprint_version`(含 `change_delta` 增量、diff、rollback)
|
||
- 模板 `pbl_blueprint_template` / `pbl_blueprint_template_item`(实例化 + 离线兜底)
|
||
|
||
模块是 **Python 包**(无 app.py / 无端口 / 无 Dockerfile),通过 `load_pbl_blueprint(env)` 挂到应用 `apps/pbls`。
|
||
|
||
## 2. 目录结构
|
||
```
|
||
modules/pbl_blueprint/
|
||
├── pyproject.toml
|
||
├── scripts/load_path.py # RBAC 路径注册(API/页面/表权限)
|
||
└── pbl_blueprint/ # 包目录 = 模块名(禁 src/)
|
||
├── __init__.py # 导出 load_pbl_blueprint
|
||
├── init.py # 装配入口 + env 注册 + 契约字典
|
||
├── db.py # 库名解析 / sqlor 获取 / 异常体系 / 建表
|
||
├── blueprint_crud.py # 蓝图 CRUD + 树 + fork + 版本契约
|
||
├── subobjects.py # 7 类子对象泛化 CRUD
|
||
├── templates.py # 模板 CRUD + 实例化 + 离线兜底
|
||
├── api.py # HTTP API 层 + API_PATHS + HANDLERS
|
||
├── models/*.json # 表定义(四段式 summary/fields/indexes/codes)
|
||
├── json/*.json # CRUD 定义(根键 tblname + params.editable/browserfields)
|
||
├── sql/pbl_blueprint.core.sql # 核心 4 表 DDL
|
||
├── sql/pbl_blueprint.subobjects.sql # 子对象 7 表 DDL
|
||
├── init/data.json # 初始化数据(内置模板)
|
||
└── wwwroot/index.ui # 前端页面
|
||
```
|
||
|
||
## 3. 数据表(11 张)
|
||
核心 4 张:`pbl_blueprint`、`pbl_blueprint_version`、`pbl_blueprint_template`、`pbl_blueprint_template_item`
|
||
子对象 7 张:`pbl_blueprint_task`、`pbl_blueprint_mission`、`pbl_blueprint_role`、
|
||
`pbl_blueprint_learning_goal`、`pbl_blueprint_evidence_spec`、`pbl_blueprint_artifact_spec`、
|
||
`pbl_blueprint_reflection_spec`
|
||
|
||
统一约定:
|
||
- 主键 `id` = varchar(32),32 位无横线 UUID(`db.new_id()`)
|
||
- `tenant_id` varchar(32) NOT NULL,**每个索引首列**
|
||
- 软删除 `deleted` tinyint(0 正常 / 1 已删除)
|
||
- 结构化字段用 `json` 类型(`spec` / `config` / `snapshot` / `change_delta` / `rule` / `permissions`)
|
||
- 审计五件套 `created_by/created_at/updated_by/updated_at/deleted`
|
||
|
||
## 4. 子对象泛化契约(重点)
|
||
7 类子对象共用一套函数,`obj_type` 分派到表:
|
||
|
||
| obj_type | 表 | 编码前缀 | 特有字段 |
|
||
|---|---|---|---|
|
||
| task | pbl_blueprint_task | TASK | parent_id |
|
||
| mission | pbl_blueprint_mission | MISSION | task_id(必填), unlock_condition |
|
||
| role | pbl_blueprint_role | ROLE | task_id, permissions |
|
||
| learning_goal | pbl_blueprint_learning_goal | GOAL | task_id, dimension, weight |
|
||
| evidence_spec | pbl_blueprint_evidence_spec | EVI | task_id, mission_id, learning_goal_id, evidence_type, collect_mode, rule, weight |
|
||
| artifact_spec | pbl_blueprint_artifact_spec | ART | task_id, mission_id, artifact_type, accept_criteria, required, weight |
|
||
| reflection_spec | pbl_blueprint_reflection_spec | REF | task_id, mission_id, trigger_point, reflection_type, rubric_ref, weight |
|
||
|
||
```python
|
||
from pbl_blueprint import load_pbl_blueprint
|
||
contract = load_pbl_blueprint(env)
|
||
contract['create_subobject'](tenant_id, 'mission', {'blueprint_id': bp_id, 'task_id': t_id, 'name': '关卡1'}, env=env)
|
||
contract['list_subobjects'](tenant_id, 'task', bp_id, env=env)
|
||
contract['batch_upsert_subobjects'](tenant_id, 'task', bp_id, rows, env=env) # Designer Agent 整树写入
|
||
```
|
||
|
||
## 5. 蓝图契约
|
||
```python
|
||
create_blueprint(tenant_id, data, env, operator) # 自动生成 code PBL-{yyyyMMdd}-{seq4}
|
||
update_blueprint(tenant_id, blueprint_id, data, ...) # 状态流转校验 STATUS_FLOW
|
||
delete_blueprint(tenant_id, blueprint_id, ...) # 软删(连带子对象+版本)
|
||
get_blueprint(tenant_id, blueprint_id, with_children) # with_children=True 附带 7 类子树
|
||
list_blueprints(tenant_id, filters, page, page_size) # 分页 + keyword/status/subject 过滤
|
||
get_blueprint_tree(tenant_id, blueprint_id) # {obj_type: [rows]}
|
||
fork_blueprint(tenant_id, blueprint_id, new_name, target_tenant_id) # 跨租户复制,ID 重映射
|
||
```
|
||
状态机:`draft → validating → validated → compiling → compiled → published → archived`(非法流转抛 `PblValidationError`)。
|
||
|
||
## 6. 版本契约
|
||
```python
|
||
save_version(tenant_id, blueprint_id, remark) # 存全量 snapshot + 计算 change_delta,主表 version_no 自增
|
||
list_versions(tenant_id, blueprint_id, page, page_size)
|
||
diff_versions(tenant_id, blueprint_id, version_a, version_b) # {added, updated, removed}
|
||
rollback_version(tenant_id, blueprint_id, version_no) # 先自动存当前版本再回滚
|
||
```
|
||
`change_delta` 结构:`{"added":[{"obj_type","id","name"}], "updated":[{"obj_type","id","fields"}], "removed":[...]}`
|
||
|
||
## 7. 模板与离线兜底
|
||
```python
|
||
create_template(tenant_id, {'name':..., 'items':[{'obj_type':'task','ref_key':'T1','name':...}, ...]})
|
||
instantiate_template(tenant_id, template_id, name, overrides) # ref_key/parent_ref 解析为真实 ID,use_count 自增
|
||
build_offline_fallback_blueprint(tenant_id, name) # Agent/网络不可用时的最小可用蓝图(9 条子对象)
|
||
```
|
||
模板条目用 `ref_key` / `parent_ref` 做**模板内相对引用**,实例化时按 `SUBOBJECT_TYPES` 顺序解析为真实 ID(父先于子)。
|
||
|
||
## 8. tenant_id 强制打头(铁律)
|
||
- 每个契约函数第一入参 `tenant_id`,内部 `db.require_tenant()` 校验
|
||
- 缺失/空/非字符串 → 抛 `PblTenantRequired`(code=`PBL_TENANT_REQUIRED`),**fail-closed 不放行**
|
||
- 所有 where 条件由 `_where(tenant_id, extra)` 构造,`tenant_id=` 永远在最前
|
||
- API 层 `_tid(params)` 从入参或 `context.tenant_id` 取,取不到直接失败
|
||
- 平台内置模板 `tenant_id='platform'`,`list_templates(include_platform=True)` 才并入
|
||
|
||
## 9. 异常码
|
||
| code | 含义 |
|
||
|---|---|
|
||
| PBL_TENANT_REQUIRED | 租户上下文缺失 |
|
||
| PBL_VALIDATION_ERROR | 入参校验失败 |
|
||
| PBL_NOT_FOUND / PBL_BLUEPRINT_NOT_FOUND / PBL_SUBOBJECT_NOT_FOUND / PBL_VERSION_NOT_FOUND / PBL_TEMPLATE_NOT_FOUND | 记录不存在 |
|
||
| PBL_UNKNOWN_OBJ_TYPE | 未知子对象类型 |
|
||
| PBL_DB_UNAVAILABLE | sqlor 不可用 |
|
||
|
||
## 10. 数据访问约束
|
||
- 库名:`ServerEnv().get_module_dbname('pbl_blueprint')`,**禁止硬编码 DBNAME**
|
||
- SQL 只用 sqlor 标准 API:`sor.C / sor.U / sor.D / sor.R / sor.sqlExe`,禁编造 save/list/insert
|
||
- 建表:`db.ensure_tables()` 幂等执行 `sql/*.sql`(`CREATE TABLE IF NOT EXISTS`)
|
||
|
||
## 11. API 路径(25 个,见 api.py API_PATHS)
|
||
前缀 `/pbl_blueprint/`,分四组:`blueprint/*`(7)、`version/*`(4)、`subobject/*`(7)、`template/*`(7)。
|
||
RBAC 注册:`python -m pbl_blueprint.scripts.load_path`(幂等,注册 API + 页面 + 表权限共 39 条路径)。
|
||
|
||
## 12. 下游模块依赖
|
||
- `pbl_validation`(M2)读 `get_blueprint_tree` 做 14 维校验,回写 `quality_level`
|
||
- `pbl_compiler`(M3)读蓝图树生成 Game Definition
|
||
- `pbl_evidence`(M5)按 `evidence_spec` / `artifact_spec` 采集
|
||
- `pbl_assessment`(M6)按 `learning_goal.weight` / `reflection_spec.rubric_ref` 评分
|
||
- `pbl_agent_runtime`(M4)用 `batch_upsert_subobjects` 写 Designer 产出,fail-closed 裁决
|