yudao-module-education
教育业务模块,提供课程、练习、题库、考试等教育业务功能。
当前状态
此模块提供教育业务功能骨架和题库目录浏览 tracer bullet。
已实现:
- 模块骨架与包结构
- 能力探测端点 (
/education/capability) - 租户识别端点 (
/education/tenant/resolve) — 学生端登录前使用 - 教育上下文端点 (
/education/context) — 学生端已认证状态 - 题库目录端点 (见下方 Catalog API) — 学生端已认证
- 独立的功能开关配置 + Scalar 数据源配置
- 错误码常量(通用 + 租户 + Catalog/Scalar)
- 权限与菜单种子数据
功能配置
在 application.yaml 或对应 profile 中配置:
API
管理后台 - 能力信息
GET /admin-api/education/capability
- 权限:
education:capability - 响应示例:
{
"code": 0,
"msg": "成功",
"data": {
"module": "education",
"enabled": true,
"version": "1.0.0",
"capabilities": ["shell", "catalog"]
}
}
用户 APP - 教育租户识别
GET /app-api/education/tenant/resolve?hostname=school.example.com
GET /app-api/education/tenant/resolve?tenantName=demo-school
GET /app-api/education/tenant/resolve?hostname=school.example.com&tenantName=demo-school
- 权限:无需认证(
@PermitAll) - 说明:通过主机名或租户名解析租户,返回学生端登录引导所需的基础字段。
hostname 和 tenantName 至少提供一个。解析规则:
- 如果同时提供两者,它们必须解析到同一个租户,否则拒绝请求
- hostname 经过标准化(保留端口并转为小写),先查
EducationProperties.hostnameTenantMap配置映射,再按system_tenant.websites的精确 authority 值查询
- 响应示例(成功):
{
"code": 0,
"msg": "成功",
"data": {
"tenantId": 1024,
"tenantName": "demo-school",
"displayName": "demo-school",
"status": "ACTIVE",
"loginMethods": ["PASSWORD", "SMS"]
}
}
- 错误响应:
| 错误码 | 说明 |
|---|---|
| 1_005_001_001 | 租户不存在 |
| 1_005_001_002 | 租户已被禁用 |
| 1_005_001_003 | 租户识别失败(hostname 格式不合法等) |
| 1_005_001_004 | 当前租户不可用(过期等) |
用户 APP - 教育当前上下文
GET /app-api/education/context
- 权限:需要认证(登录态)
- 说明:根据当前认证用户和租户上下文返回教育业务信息。不信任请求体中的 userId,一切从安全上下文和 TenantContext 派生。TenantSecurityWebFilter 前置完成租户校验,此处二次验证确保租户处于活跃状态。
- 响应示例:
{
"code": 0,
"msg": "成功",
"data": {
"userId": 1024,
"tenantId": 2048,
"tenantName": "demo-school",
"displayName": "demo-school"
}
}
错误码
| 错误码 | 说明 |
|---|---|
| 1_005_001_000 | 教育模块未启用 |
| 1_005_001_001 | 租户不存在 |
| 1_005_001_002 | 租户已被禁用 |
| 1_005_001_003 | 租户识别失败:{原因} |
| 1_005_001_004 | 当前租户不可用,请联系管理员 |
构建与运行
单独编译测试
# 编译 education 模块
mvn compile -pl yudao-module-education -am
# 运行 education 模块单元测试
mvn test -pl yudao-module-education -am
整体编译(含 server)
# 编译全量(member + education + system + infra + server)
mvn compile -pl yudao-server -am
# 打包(跳过测试加速)
mvn package -pl yudao-server -am -DskipTests
启动验证
- 确保
yudao.education.enabled=true - 启动
yudao-server - 访问 Swagger UI 查看
education分组 - 调用
GET /admin-api/education/capability
SQL 应用
# 应用基础种子数据(菜单 + 权限定义;执行后由管理员为目标租户角色授权)
mysql -u root -p ruoyi-vue-pro < sql/mysql/education/000-education-seed.sql
# 应用租户识别种子数据
mysql -u root -p ruoyi-vue-pro < sql/mysql/education/001-education-tenant-seed.sql
# 回滚
mysql -u root -p ruoyi-vue-pro < sql/mysql/education/000-education-rollback.sql
mysql -u root -p ruoyi-vue-pro < sql/mysql/education/001-education-tenant-rollback.sql
SQL 交付约定
- 所有 Education SQL 文件存放在
sql/mysql/education/目录下 - 文件命名:
NNN-描述.sql(NNN 为三位递增序号) - 每个正向脚本应有对应的回滚脚本
- schema 文件仅包含 DDL,seed 文件仅包含 DML
- 不修改项目根目录的
ruoyi-vue-pro.sql巨量全量转储
前端状态
当前工作区未检出完整的前端源码。 yudao-ui/yudao-ui-admin-vue3/ 仅包含部分 MES 相关文件(src/api/mes/、src/views/mes/),缺少 package.json、router/、store/、config/ 等核心框架文件。
因此:
- 管理后台教育菜单项:基础 SQL 仅注册了
system_menu记录(ID 6800-6801)。租户解析与当前上下文属于学生端接口,不创建虚假的后台权限菜单。前端无路由/页面组件可渲染,菜单在管理后台不会显示。 - Student Web/H5 应用外壳:前端源码不存在,无法建立。
Student 端前端集成契约
前端就位后必须实现以下流程(不能伪造静态页面):
-
租户识别(登录前)
- URL:
GET /app-api/education/tenant/resolve - 从浏览器
window.location.host获取 authority(包含非默认端口),传入hostname参数 - 备用:支持手动输入
tenantName - 根据返回的
loginMethods决定展示哪种登录方式(PASSWORD/SMS) - 获得
tenantId后,在后续请求中通过tenant-idheader 传递
- URL:
-
用户认证(复用 Member 模块)
- 密码登录:
POST /app-api/member/auth/login - 短信登录:
POST /app-api/member/auth/sms-login - 刷新令牌:
POST /app-api/member/auth/refresh-token - 登出:
POST /app-api/member/auth/logout - 所有请求携带
tenant-id: {tenantId}header
- 密码登录:
-
获取上下文(登录后)
- URL:
GET /app-api/education/context - 携带有效 Bearer Token +
tenant-idheader - 从响应获取
userId、tenantId、tenantName用于页面展示
- URL:
-
跨租户防护
- 前端不应允许用户手动切换
tenant-idheader - 后端通过
TenantSecurityWebFilter拒绝认证用户的跨租户 header 操作
- 前端不应允许用户手动切换
阻塞项:完整前端源码(含 router、store、package.json)是上述前端集成的必要前提。一旦前端源码就位,需:
- 在 Vue3 admin 的路由中添加
/education路由项,绑定 Education 菜单组件 - 添加
src/api/education/API 封装层(调用上述教育端点) - Student Web/H5 端如需要独立入口,需新建对应前端项目
用户 APP - 题库目录(Catalog)
所有端点需要学生登录态(Bearer Token)。userId 和 tenantId 由安全上下文派生,不接受客户端传参。
Scalar 代理层自动注入 x-tenant-id header(来自 TenantContextHolder),前端不发起任何直达 Scalar 的请求。
架构边界
Browser → Controller(/education/catalog/*) → CatalogService → CatalogProvider → [Scalar]
↑ 内部 DTO/VO ↑ Scalar DTO 仅此层
- 业务层(Controller/Service):仅操作内部 Catalog VO(
CatalogRegionRespVO等) - 集成层(Scalar DTO + ScalarCatalogProvider):封装 Scalar 协议差异,DTO 不泄露到上层
端点列表
| 端点 | 说明 | 参数 |
|---|---|---|
GET /app-api/education/catalog/regions |
查询可用地区 | 无 |
GET /app-api/education/catalog/categories |
查询题目分类 | subjectId (可选), nodeId (可选) |
GET /app-api/education/catalog/subjects |
查询科目目录 | regionId, schoolId, majorId, moduleId, type (均可选) |
GET /app-api/education/catalog/module-nodes |
查询模块导航节点 | regionId, moduleId, parentId (均可选) |
GET /app-api/education/catalog/content-entries |
查询内容入口 | regionId, entryType, includeHidden (均可选) |
GET /app-api/education/catalog/content-nodes |
查询内容导航节点 | entryId (必填), parentId, mode, includeInactive, markerType (可选) |
GET /app-api/education/catalog/question-collections |
查询可用题集 | regionId, entryId, nodeId, collectionType, limit (均可选) |
响应格式
所有成功响应返回 CommonResult<List<T>>:
{
"code": 0,
"msg": "成功",
"data": [
{"id": "uuid", "name": "全国", "order": 1, "active": true}
]
}
错误响应
| HTTP 状态 | 错误码 | 说明 |
|---|---|---|
| 401 | 1_016_000_002 | 未登录或会话过期 |
| 400 | 自定义 | 请求参数不合法 |
| 500 | 1_005_002_000 | 题库数据源未启用 |
| 500 | 1_005_002_001 | 上游题库服务异常 |
| 500 | 1_005_002_002 | 上游认证失败(配置问题) |
| 403 | 1_005_002_003 | 无权限访问上游资源 |
| 404 | 1_005_002_004 | 请求的题库资源不存在 |
| 409 | 1_005_002_005 | 资源状态冲突 |
| 429 | 1_005_002_006 | 请求过于频繁 |
| 500 | 1_005_002_007 | 上游超时 |
| 500 | 1_005_002_008 | 上游返回异常:{状态码} |
| 500 | 1_005_002_009 | 不支持的题库数据源模式 |
- 上游错误不会被转换为空列表或成功响应 — 每个上游非 2xx 均映射为明确的
ServiceException - 日志记录脱敏后的端点名、tenant、上游 requestId、耗时和结果
前端集成提示
前端就位后,学生端学习首页应:
- 获取上下文(
/education/context)确认登录态 - 调用
/education/catalog/regions获取地区筛选器 - 根据地区调用
subjects/categories获取科目分类 - 调用
content-entries→content-nodes构建目录树 - 叶子节点调用
question-collections获取题集摘要
当前阻塞:完整前端源码不存在,后端目录接口已就绪可通过 Swagger/curl 验证。
更新后的错误码
| 错误码 | 说明 |
|---|---|
| 1_005_001_000 | 教育模块未启用 |
| 1_005_001_001 | 租户不存在 |
| 1_005_001_002 | 租户已被禁用 |
| 1_005_001_003 | 租户识别失败:{原因} |
| 1_005_001_004 | 当前租户不可用 |
| 1_005_002_000 | 题库数据源未启用 |
| 1_005_002_001 | 上游题库服务异常 |
| 1_005_002_002 | 上游认证失败 |
| 1_005_002_003 | 无权限访问上游资源 |
| 1_005_002_004 | 题库资源不存在 |
| 1_005_002_005 | 资源状态冲突 |
| 1_005_002_006 | 请求过于频繁 |
| 1_005_002_007 | 上游超时 |
| 1_005_002_008 | 上游返回异常:{状态码} |
| 1_005_002_009 | 不支持的题库数据源模式 |