feat(education): add native question publication lifecycle

This commit is contained in:
2026-07-30 21:31:08 +08:00
parent 4db4a7d371
commit 34bc1fe41e
22 changed files with 2044 additions and 20 deletions

View File

@@ -4,7 +4,7 @@
## 当前状态
此模块提供教育业务功能骨架、题库目录浏览 tracer bullet,以及题目预览与练习配置预览
此模块提供教育业务功能骨架、题库目录浏览、题目预览与练习闭环,以及 `JAVA_READ` 下的租户题目草稿、发布和归档 tracer bullet。
**已实现**
- 模块骨架与包结构
@@ -16,9 +16,10 @@
- 练习配置预览端点 (见下方 Practice API) — 学生端已认证
- 答案保存端点 (见下方 Answer API) — 幂等保存,安全重试
- 题目安全过滤(答案/解析绝不暴露到前端)
- 管理端题目创作、发布和归档端点 — 仅在 `JAVA_READ` 下接受写入
- 独立的功能开关配置 + Scalar 数据源配置
- 错误码常量(通用 + 租户 + Catalog/Scalar + 题目/练习)
- 权限注解(`education:capability`);菜单种子尚未纳入正式 Flyway管理员授权需在后续产品决策后交付
- System RBAC 权限注解及 V4080 条件种子(`education:capability``education:question:author``education:question:publish``education:question:archive`);迁移不自动向任何角色授权
## 功能配置
@@ -34,9 +35,12 @@ yudao:
practice-write-enabled: true
# Pilot 灰度租户;空列表表示不限制,生产 Pilot 应显式配置目标租户 ID
pilot-tenant-ids: [1024]
# SCALAR_READ 是默认读取权威;只有 JAVA_READ 允许本地题目创作
catalog-mode: SCALAR_READ
```
`catalog-mode=SCALAR_READ` 时管理端题目写命令会在访问 Mapper 前失败关闭;要使用下述创作 API必须显式配置 `catalog-mode=JAVA_READ`,确保写入 PostgreSQL 的题目也是学生读取的数据源。
灰度与回滚约束:
- `enabled=false`:移除 Education HTTP 能力,不执行任何数据删除。
@@ -69,6 +73,42 @@ GET /admin-api/education/capability
}
```
### 管理后台 - 题目创作与发布
这些端点只管理当前框架租户的 `TENANT_OWNED` 题目,不提供 PUBLIC/platform-curator 写入口。客户端不能提交 `tenantId``scope`、生命周期状态或 actor新题固定以 `DRAFT/false` 创建,并且只允许 `DRAFT → PUBLISHED → ARCHIVED`
| 端点 | 权限 | 说明 |
|------|------|------|
| `POST /admin-api/education/questions/drafts` | `education:question:author` | 创建当前租户草稿,返回题目 ID |
| `PUT /admin-api/education/questions/{id}/publish` | `education:question:publish` | 发布当前租户草稿 |
| `PUT /admin-api/education/questions/{id}/archive` | `education:question:archive` | 归档当前租户已发布题目 |
创建草稿请求示例:
```json
{
"stem": "2 + 2 = ?",
"type": "choice",
"difficulty": "easy",
"options": [
{"label": "A", "content": "4", "order": 1.0},
{"label": "B", "content": "5", "order": 2.0}
],
"correctAnswer": "A",
"explanation": "基础加法",
"analysis": null
}
```
`correctAnswer``explanation``analysis` 是服务端受保护内容;学生端仍只返回下文列出的安全题目投影。发布和归档采用 tenant/scope/expected-state CAS并在同一事务追加生命周期审计。`SCALAR_READ` 或其他不支持的 provider mode 返回 `1_005_003_070`,不会查询或修改题目。
| 错误码 | 说明 |
|--------|------|
| `1_005_003_070` | 当前 provider mode 不支持本地题目创作 |
| `1_005_003_071` | 题目不存在或无权管理 |
| `1_005_003_072` | 状态已变化或生命周期转换非法 |
| `1_005_003_073` | 题目内容不完整或不安全,不能发布 |
### 用户 APP - 教育租户识别
```
@@ -180,6 +220,8 @@ yudao-module-education/src/main/resources/db/migration/education/
- `V4010``V4020` 是不可变迁移历史,不得修改。
- `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 占用时迁移失败,且迁移不会向角色写入授权关系。
-`education_answer_idempotency``education_submit_idempotency` 在首次接管时保留,后续清理必须使用更高版本的独立向前 migration。
- `sql/postgresql/education/` 是手工初始化/设计历史,`sql/mysql/education/` 是过时归档;两者都不是运行时交付入口。
- 禁止使用 `flyway clean``*-rollback.sql` 回退共享环境。应用回滚后如有 Schema 兼容问题,通过更高版本向前修复。
@@ -200,7 +242,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/` 等核心框架文件。
因此:
- **管理后台教育菜单项**当前仅保留 `education:capability` 权限契约,正式 Flyway 尚未写入菜单种子;前端无路由/页面组件可渲染,因此不声明已有可见教育菜单
- **管理后台教育菜单项**V4080 已条件写入 Education 权限/菜单记录,但不分配角色;前端无路由页面组件,因此不声明已有可操作的可见教育页面
- **Student Web/H5 应用外壳**:前端源码不存在,无法建立。
### Student 端前端集成契约