242 lines
11 KiB
Markdown
242 lines
11 KiB
Markdown
# FEAT-20261009 课程两步式向导与教学资源关联 开发与验收文档
|
||||
|
|
|
|||
|
|
> **功能编号:** `FEAT-20261009-COURSE-WIZARD`
|
|||
|
|
> **关联模块:** `ISSUE-01-03 课程库基础资料管理`
|
|||
|
|
> **开发分支:** `feat/20261009-course-wizard`
|
|||
|
|
> **开发基线:** `55143af4a518222007d9b82dfbe0677a04f06150`
|
|||
|
|
> **API Contract:** `docs/api-v2/02_module_a_base_resource_api.md`
|
|||
|
|
> **数据库:** KingbaseES / Database V2
|
|||
|
|
> **实施日期:** 2026-10-09
|
|||
|
|
> **当前状态:** 代码开发完成,静态构建与自动化测试通过;待提交、推送、CI 与运行时验收
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 功能目标与范围
|
|||
|
|
|
|||
|
|
本功能将课程维护改造为两步式向导,并补齐课程与教学资源之间的真实关联能力:
|
|||
|
|
|
|||
|
|
1. 第一步维护课程编码、名称、类型、学时、教学目标、课程内容、状态与备注;
|
|||
|
|
2. 第二步查看已关联资源、关联资源库中的既有资源、上传新资源并自动关联、解除资源关联;
|
|||
|
|
3. 课程与资源使用规范化多对多关联表 `train_course_resource`,禁止在 `train_course` 中保存逗号分隔资源 ID;
|
|||
|
|
4. 复用既有课程、教学资源与通用文件上传能力,不引入新的文件存储协议;
|
|||
|
|
5. 不修改培训计划课程配置、排课及其他计划流程模块。
|
|||
|
|
|
|||
|
|
### 1.1 明确边界
|
|||
|
|
|
|||
|
|
- 解除关联只删除 `train_course_resource` 关系,不删除 `train_resource` 资源记录及实际文件;
|
|||
|
|
- 被课程引用的资源禁止直接删除,必须先解除课程关联;
|
|||
|
|
- 本功能不提供资源文件物理删除、资源版本管理、断点续传及大文件分片上传;
|
|||
|
|
- 本功能未执行真实生产数据库迁移或写入。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 用户操作流程
|
|||
|
|
|
|||
|
|
### 2.1 新增课程
|
|||
|
|
|
|||
|
|
1. 用户点击“新增”;
|
|||
|
|
2. 在“课程基本信息”步骤填写并保存课程;
|
|||
|
|
3. 后端返回已生成 `cId`,前端进入“教学资源”步骤;
|
|||
|
|
4. 用户可关联既有资源,或通过通用上传组件上传文件、创建资源并自动绑定;
|
|||
|
|
5. 用户点击“完成”关闭向导并刷新课程列表。
|
|||
|
|
|
|||
|
|
### 2.2 修改课程
|
|||
|
|
|
|||
|
|
1. 用户从列表进入“修改”;
|
|||
|
|
2. 页面加载课程详情及其真实资源列表;
|
|||
|
|
3. 用户可修改课程基本信息,也可进入第二步新增或解除资源关联;
|
|||
|
|
4. 解除关联不会删除资源库中的资源。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 数据模型与设计决策
|
|||
|
|
|
|||
|
|
### 3.1 规范化关联模型
|
|||
|
|
|
|||
|
|
课程与资源采用标准 N:M 模型:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
train_course (1) ──< train_course_resource >── (1) train_resource
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`train_course_resource` 核心字段:
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 说明 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `id` | `BIGINT IDENTITY` | 关联记录主键 |
|
|||
|
|
| `c_id` | `VARCHAR(32)` | 课程 ID,外键指向 `train_course.c_id` |
|
|||
|
|
| `resource_id` | `VARCHAR(32)` | 资源 ID,外键指向 `train_resource.resource_id` |
|
|||
|
|
| `sort_no` | `INTEGER` | 课程内资源排序号 |
|
|||
|
|
| `create_by` | `VARCHAR(64)` | 创建人 |
|
|||
|
|
| `create_time` | `TIMESTAMP` | 创建时间 |
|
|||
|
|
|
|||
|
|
唯一约束为 `(c_id, resource_id)`,防止同一资源被重复绑定到同一课程。
|
|||
|
|
|
|||
|
|
### 3.2 旧字段兼容迁移
|
|||
|
|
|
|||
|
|
迁移脚本兼容曾执行旧版 011 的环境:
|
|||
|
|
|
|||
|
|
1. 检测 `train_course.resource_id` 是否存在;
|
|||
|
|
2. 将逗号分隔的资源 ID 拆分后迁移到 `train_course_resource`;
|
|||
|
|
3. 忽略不存在或已逻辑删除的资源;
|
|||
|
|
4. 使用唯一约束避免重复迁移;
|
|||
|
|
5. 完成迁移后删除 `train_course.resource_id` 非规范字段。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. API 与权限
|
|||
|
|
|
|||
|
|
### 4.1 本次新增或调整的课程接口
|
|||
|
|
|
|||
|
|
| HTTP Method | Path | 权限 | 说明 |
|
|||
|
|
|:---:|---|---|---|
|
|||
|
|
| `GET` | `/train/course/{cId}` | `train:course:query` | 返回 `CourseDetailVO`,其中 `resources` 为真实关联资源列表 |
|
|||
|
|
| `POST` | `/train/course` | `train:course:add` | 新增课程并在 `data` 中返回包含生成后 `cId` 的课程对象 |
|
|||
|
|
| `POST` | `/train/course/{cId}/resources` | `train:course:edit` | 绑定资源;请求体为 `resourceId`、`sortNo` |
|
|||
|
|
| `DELETE` | `/train/course/{cId}/resources/{resourceId}` | `train:course:edit` | 解除课程资源关联,不删除资源本身 |
|
|||
|
|
|
|||
|
|
### 4.2 复用的资源接口
|
|||
|
|
|
|||
|
|
| HTTP Method | Path | 权限 | 用途 |
|
|||
|
|
|:---:|---|---|---|
|
|||
|
|
| `GET` | `/train/resource/list` | `train:resource:list` | 获取可关联资源 |
|
|||
|
|
| `POST` | `/train/resource` | `train:resource:edit` | 创建教学资源并返回生成后的资源对象 |
|
|||
|
|
| `POST` | `/common/upload` | 登录用户 | 复用通用上传组件上传资源文件 |
|
|||
|
|
|
|||
|
|
### 4.3 请求模型
|
|||
|
|
|
|||
|
|
`CourseResourceBindDTO`:
|
|||
|
|
|
|||
|
|
| 字段 | 校验 | 说明 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `resourceId` | `@NotBlank` | 必填资源 ID |
|
|||
|
|
| `sortNo` | `@Min(0)` | 可选排序号,默认 0 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 后端实现
|
|||
|
|
|
|||
|
|
| 层级 | 文件 | 实现内容 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| Controller | `TrainCourseController` | 返回新增课程对象;增加资源绑定、解绑端点 |
|
|||
|
|
| Controller | `TrainResourceController` | 新增资源后返回包含 `resourceId` 的资源对象 |
|
|||
|
|
| DTO | `CourseResourceBindDTO` | 绑定请求参数与校验 |
|
|||
|
|
| VO | `CourseDetailVO` | `resources` 调整为 `List<TrainResource>` |
|
|||
|
|
| Service | `TrainCourseResourceServiceImpl` | 校验课程和资源、幂等绑定、更新排序、解除关联 |
|
|||
|
|
| Service | `TrainCourseServiceImpl` | 查询课程详情时聚合真实资源列表 |
|
|||
|
|
| Service | `TrainResourceServiceImpl` | 删除前检查课程引用并给出友好提示 |
|
|||
|
|
| Mapper | `TrainCourseResourceMapper` | 关联查询、新增、更新、删除与引用计数 |
|
|||
|
|
| Mapper XML | `TrainCourseResourceMapper.xml` | 实现规范化关联 SQL,并过滤已逻辑删除资源 |
|
|||
|
|
| Entity/Mapper | `TrainCourse`、`TrainCourseMapper.xml` | 移除旧 `resourceId/resource_id` 逗号串字段 |
|
|||
|
|
|
|||
|
|
绑定操作的业务语义为幂等:若关联已存在,则更新 `sort_no`,不重复插入关联记录。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 前端实现
|
|||
|
|
|
|||
|
|
主要页面:`build_ui/src/views/train/course/index.vue`
|
|||
|
|
|
|||
|
|
实现内容:
|
|||
|
|
|
|||
|
|
1. 使用 `el-steps` 提供“课程基本信息 → 教学资源”两步流程;
|
|||
|
|
2. 新增课程时先保存课程并取得后端生成的 `cId`;
|
|||
|
|
3. 展示真实关联资源及其文件/链接;
|
|||
|
|
4. 支持搜索并关联资源库中尚未绑定的资源;
|
|||
|
|
5. 非外部链接资源复用 `file-upload` 组件;
|
|||
|
|
6. 外部链接校验必须以 `http://` 或 `https://` 开头;
|
|||
|
|
7. 上传资源后调用既有资源新增接口,再调用课程资源绑定接口;
|
|||
|
|
8. 支持解除关联,并明确提示资源本身不会被删除。
|
|||
|
|
|
|||
|
|
前端 API 文件 `build_ui/src/api/train/course.js` 新增:
|
|||
|
|
|
|||
|
|
- `bindCourseResource(cId, data)`;
|
|||
|
|
- `unbindCourseResource(cId, resourceId)`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 数据库迁移与回滚
|
|||
|
|
|
|||
|
|
### 7.1 正向迁移
|
|||
|
|
|
|||
|
|
脚本:`RuoYi-Vue/sql/train/v2/011_pilot_20261010_master_data_and_resource.sql`
|
|||
|
|
|
|||
|
|
- 幂等创建 `train_resource` 与 `train_course_resource`;
|
|||
|
|
- 创建课程资源唯一索引、课程索引与资源索引;
|
|||
|
|
- 创建课程、资源外键;
|
|||
|
|
- 迁移旧逗号 ID 后删除 `train_course.resource_id`;
|
|||
|
|
- 补齐资源类型字典、菜单与角色授权。
|
|||
|
|
|
|||
|
|
### 7.2 回滚
|
|||
|
|
|
|||
|
|
脚本:`RuoYi-Vue/sql/train/v2/rollback/011_pilot_20261010_master_data_and_resource_rollback.sql`
|
|||
|
|
|
|||
|
|
回滚脚本会删除资源菜单、字典、课程资源关联表与资源表,并回滚 011 中的主数据扩展列。该操作具有破坏性,只允许在确认无正式数据的回滚窗口执行。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. 自动化验证结果
|
|||
|
|
|
|||
|
|
| 验证项 | 结果 | 说明 |
|
|||
|
|
|---|:---:|---|
|
|||
|
|
| `ruoyi-train` 全量测试 | **PASS** | 393 tests,0 failures,0 errors,0 skipped |
|
|||
|
|
| 课程资源关联定向测试 | **PASS** | 3 tests,覆盖新增绑定、重复绑定幂等更新、资源不存在拦截 |
|
|||
|
|
| 后端业务模块编译 | **PASS** | `ruoyi-train` 编译成功 |
|
|||
|
|
| 前端 ESLint | **PASS** | 课程页面与课程 API 文件 0 errors |
|
|||
|
|
| 前端生产构建 | **PASS** | `npm run build:prod` 成功 |
|
|||
|
|
| 完整 `ruoyi-admin` 本地聚合构建 | **环境阻断** | 本机缺少非公开依赖 `com.kingbase:kingbase8:8.6.0`,不是本次代码编译错误 |
|
|||
|
|
| 数据库迁移实跑 | **PENDING** | 未连接目标 Kingbase 环境执行 011 |
|
|||
|
|
| 浏览器端到端验收 | **PENDING** | 需在迁移后的集成环境验证上传、绑定、解绑完整流程 |
|
|||
|
|
|
|||
|
|
前端构建存在一条仓库既有警告:`src/views/graph/antv/index2.vue` 使用的 `@antv/x6` 未导出 `Addon`。该警告与课程模块无关,未阻断构建。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Acceptance Criteria
|
|||
|
|
|
|||
|
|
| AC | 验收内容 | 当前状态 |
|
|||
|
|
|:---:|---|:---:|
|
|||
|
|
| AC-01 | 新增课程保存后可进入资源步骤,且获得有效 `cId` | **STATIC/AUTOMATED PASS** |
|
|||
|
|
| AC-02 | 课程详情返回真实关联资源列表 | **STATIC/AUTOMATED PASS** |
|
|||
|
|
| AC-03 | 可关联既有资源,同一资源不会重复绑定 | **AUTOMATED PASS** |
|
|||
|
|
| AC-04 | 可上传新资源并自动关联课程 | **BUILD PASS / RUNTIME PENDING** |
|
|||
|
|
| AC-05 | 解除关联不删除资源本身 | **STATIC PASS / RUNTIME PENDING** |
|
|||
|
|
| AC-06 | 被课程引用的资源禁止删除 | **STATIC PASS / RUNTIME PENDING** |
|
|||
|
|
| AC-07 | 数据库不再使用逗号分隔资源 ID | **MIGRATION STATIC PASS / RUNTIME PENDING** |
|
|||
|
|
| AC-08 | 不影响培训计划课程及排课流程 | **REGRESSION PASS** |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. 上线与验证顺序
|
|||
|
|
|
|||
|
|
1. 在备份后的集成数据库执行正向迁移 011;
|
|||
|
|
2. 核对 `train_course_resource` 表、唯一约束、外键、字典和菜单;
|
|||
|
|
3. 构建并部署后端、前端;
|
|||
|
|
4. 使用具备课程及资源权限的账号进行浏览器验收;
|
|||
|
|
5. 验证新增课程、修改课程、上传资源、关联既有资源、解除关联和资源删除保护;
|
|||
|
|
6. 核对操作日志与数据库关联记录;
|
|||
|
|
7. 验收通过后再合并到目标集成分支。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. Git 与交付状态
|
|||
|
|
|
|||
|
|
- 当前代码与本文档均位于本地分支 `feat/20261009-course-wizard`;
|
|||
|
|
- 当前改动尚未执行 `git commit`、`git push` 或创建合并请求;
|
|||
|
|
- 远端页面显示的 `Status: Failed` 对应基线提交 `55143af4` 的既有 CI Pipeline,不代表本地尚未提交的课程向导代码已经执行或失败;
|
|||
|
|
- 推荐先完成本地变更审查并按中文提交规范提交,再推送该功能分支触发新的 CI;
|
|||
|
|
- 新 Pipeline 全部通过后,再向 `integration/20261010-pilot` 创建合并请求。
|
|||
|
|
|
|||
|
|
建议提交信息:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
新增培训:实现课程两步式向导与教学资源关联
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 12. 已知事项
|
|||
|
|
|
|||
|
|
1. 本地完整管理端构建依赖授权的 Kingbase JDBC 驱动;CI Runner 必须先通过 `scripts/bootstrap/check-kingbase-jdbc.sh`;
|
|||
|
|
2. 当前远端失败 Pipeline 的具体失败 Job 和日志需在 GitLab Pipeline 页面查看,单凭分支页红色状态无法判断根因;
|
|||
|
|
3. 推送不会自动部署生产环境:当前 `.gitlab-ci.yml` 仅允许 `feat/cicd-pipeline` 执行 `deploy_production`,本功能分支只会运行后端、前端构建测试。
|