feat(education): add tenant question placement

This commit is contained in:
2026-07-30 22:28:08 +08:00
parent 34bc1fe41e
commit 2a40cbd69e
21 changed files with 813 additions and 44 deletions

View File

@@ -4,7 +4,7 @@
## 当前状态
此模块提供教育业务功能骨架、题库目录浏览、题目预览与练习闭环,以及 `JAVA_READ` 下的租户题目草稿、发布和归档 tracer bullet。
此模块提供教育业务功能骨架、题库目录浏览、题目预览与练习闭环,以及 `JAVA_READ` 下的租户题目草稿、目录放置、发布和归档 tracer bullet。
**已实现**
- 模块骨架与包结构
@@ -16,10 +16,10 @@
- 练习配置预览端点 (见下方 Practice API) — 学生端已认证
- 答案保存端点 (见下方 Answer API) — 幂等保存,安全重试
- 题目安全过滤(答案/解析绝不暴露到前端)
- 管理端题目创作、发布和归档端点 — 仅在 `JAVA_READ` 下接受写入
- 管理端题目创作、目录放置、发布和归档端点 — 仅在 `JAVA_READ` 下接受写入
- 独立的功能开关配置 + Scalar 数据源配置
- 错误码常量(通用 + 租户 + Catalog/Scalar + 题目/练习)
- System RBAC 权限注解及 V4080 条件种子(`education:capability``education:question:author``education:question:publish``education:question:archive`);迁移不自动向任何角色授权
- System RBAC 权限注解及 V4080/V4090 条件种子(`education:capability``education:question:author``education:question:classify``education:question:publish``education:question:archive`);迁移不自动向任何角色授权
## 功能配置
@@ -48,6 +48,7 @@ yudao:
- `practice-write-enabled=false`:拒绝新建练习、保存答案和交卷;会话恢复、报告与历史查询保持可用。
- `pilot-tenant-ids`:非空时仅允许列表内租户使用题库和练习写入能力。
- 应用回滚只回滚应用版本或开关;不得执行 `*-rollback.sql`。SQL 回滚脚本仅用于明确的数据销毁场景。
- 回滚到 V4090 之前的应用版本前,必须先切离 `JAVA_READ` 以停止原生题目写入;没有可用替代读取权威时应关闭 Education。V4090 Schema 保留并通过后续更高版本向前修正。
## API
@@ -75,11 +76,12 @@ GET /admin-api/education/capability
### 管理后台 - 题目创作与发布
这些端点只管理当前框架租户的 `TENANT_OWNED` 题目,不提供 PUBLIC/platform-curator 写入口。客户端不能提交 `tenantId``scope`、生命周期状态或 actor新题固定以 `DRAFT/false` 创建,并且只允许 `DRAFT → PUBLISHED → ARCHIVED`
这些端点只管理当前框架租户的 `TENANT_OWNED` 题目,不提供 PUBLIC/platform-curator 写入口。客户端不能提交 `tenantId``scope`、生命周期状态或 actor新题固定以 `DRAFT/false` 创建,放置到可用 Content Node 后才可发布,并且只允许 `DRAFT → PUBLISHED → ARCHIVED`
| 端点 | 权限 | 说明 |
|------|------|------|
| `POST /admin-api/education/questions/drafts` | `education:question:author` | 创建当前租户草稿,返回题目 ID |
| `PUT /admin-api/education/questions/{id}/placement` | `education:question:classify` | 将草稿放置或重新放置到允许的 Content Node返回放置版本 |
| `PUT /admin-api/education/questions/{id}/publish` | `education:question:publish` | 发布当前租户草稿 |
| `PUT /admin-api/education/questions/{id}/archive` | `education:question:archive` | 归档当前租户已发布题目 |
@@ -100,6 +102,17 @@ GET /admin-api/education/capability
}
```
目录放置请求示例:
```json
{
"nodeId": 200,
"expectedPlacementVersion": 0
}
```
放置目标必须是当前租户可读的 PUBLIC 或同租户 Content Node并且处于 active、非 hidden、selectable 状态。放置使用独立乐观版本;发布后不可重新放置。学生按 node 查询时会在同一条 PostgreSQL 查询中再次检查节点可用性。
`correctAnswer``explanation``analysis` 是服务端受保护内容;学生端仍只返回下文列出的安全题目投影。发布和归档采用 tenant/scope/expected-state CAS并在同一事务追加生命周期审计。`SCALAR_READ` 或其他不支持的 provider mode 返回 `1_005_003_070`,不会查询或修改题目。
| 错误码 | 说明 |
@@ -108,6 +121,9 @@ GET /admin-api/education/capability
| `1_005_003_071` | 题目不存在或无权管理 |
| `1_005_003_072` | 状态已变化或生命周期转换非法 |
| `1_005_003_073` | 题目内容不完整或不安全,不能发布 |
| `1_005_003_074` | 归类目标不存在或不可用 |
| `1_005_003_075` | 归类版本或题目状态已变化 |
| `1_005_003_076` | 题目尚未归类,不能发布 |
### 用户 APP - 教育租户识别
@@ -221,7 +237,8 @@ yudao-module-education/src/main/resources/db/migration/education/
- `V4030` 创建或接管 Practice 核心闭环表,并在存在旧答案/交卷幂等表时向统一 `education_idempotency` 回填数据。
- `V4070` 对 19 条目录引用边执行历史预校验并安装写入守卫,同时将 11 张目录表的 `tenant_id/scope` 设为插入后不可变,阻止 PUBLIC→租户、跨租户以及父节点归属变更造成的非法关系。
- `V4080` 增加第 20 条目录引用边 `education_question_version.question_id → education_question.id`,并为版本表增加第 12 个 ownership/scope 守卫;版本归属必须与题目完全一致。该迁移还建立 draft-first 生命周期、不可变题目版本、同事务追加式审计及历史已发布内容的 fail-closed 预检。
- V4080 在平台 `system_menu` 已存在时条件写入 Education 权限种子;固定 ID 已被不同 permission 占用时迁移失败,且迁移不会向角色写入授权关系
- `V4090` 增加 Question Placement 乐观版本、PUBLIC 管理拒绝、发布后放置冻结、发布前可用节点约束,以及 `education:question:classify` 权限种子
- V4080/V4090 在平台 `system_menu` 已存在时条件写入 Education 权限种子;固定 ID 已被不同 permission 占用时迁移失败,且迁移不会向角色写入授权关系。
-`education_answer_idempotency``education_submit_idempotency` 在首次接管时保留,后续清理必须使用更高版本的独立向前 migration。
- `sql/postgresql/education/` 是手工初始化/设计历史,`sql/mysql/education/` 是过时归档;两者都不是运行时交付入口。
- 禁止使用 `flyway clean``*-rollback.sql` 回退共享环境。应用回滚后如有 Schema 兼容问题,通过更高版本向前修复。
@@ -242,7 +259,7 @@ find yudao-module-education/target/classes/db/migration/education -type f -print
**当前工作区未检出完整的前端源码。** `yudao-ui/yudao-ui-admin-vue3/` 仅包含部分 MES 相关文件(`src/api/mes/``src/views/mes/`),缺少 `package.json``router/``store/``config/` 等核心框架文件。
因此:
- **管理后台教育菜单项**V4080 已条件写入 Education 权限/菜单记录,但不分配角色;前端仍无路由或页面组件,因此不声明已有可操作的可见教育页面。
- **管理后台教育菜单项**V4080/V4090 已条件写入 Education 权限/菜单记录,但不分配角色;前端仍无路由或页面组件,因此不声明已有可操作的可见教育页面。
- **Student Web/H5 应用外壳**:前端源码不存在,无法建立。
### Student 端前端集成契约