Files
ruoyi-vue-pro/yudao-module-education

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 至少提供一个。解析规则:
    1. 如果同时提供两者,它们必须解析到同一个租户,否则拒绝请求
    2. 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

启动验证

  1. 确保 yudao.education.enabled=true
  2. 启动 yudao-server
  3. 访问 Swagger UI 查看 education 分组
  4. 调用 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-描述.sqlNNN 为三位递增序号)
  • 每个正向脚本应有对应的回滚脚本
  • schema 文件仅包含 DDLseed 文件仅包含 DML
  • 不修改项目根目录的 ruoyi-vue-pro.sql 巨量全量转储

前端状态

当前工作区未检出完整的前端源码。 yudao-ui/yudao-ui-admin-vue3/ 仅包含部分 MES 相关文件(src/api/mes/src/views/mes/),缺少 package.jsonrouter/store/config/ 等核心框架文件。

因此:

  • 管理后台教育菜单项:基础 SQL 仅注册了 system_menu 记录ID 6800-6801。租户解析与当前上下文属于学生端接口不创建虚假的后台权限菜单。前端无路由/页面组件可渲染,菜单在管理后台不会显示。
  • Student Web/H5 应用外壳:前端源码不存在,无法建立。

Student 端前端集成契约

前端就位后必须实现以下流程(不能伪造静态页面):

  1. 租户识别(登录前)

    • URL: GET /app-api/education/tenant/resolve
    • 从浏览器 window.location.host 获取 authority包含非默认端口传入 hostname 参数
    • 备用:支持手动输入 tenantName
    • 根据返回的 loginMethods 决定展示哪种登录方式PASSWORD/SMS
    • 获得 tenantId 后,在后续请求中通过 tenant-id header 传递
  2. 用户认证(复用 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
  3. 获取上下文(登录后)

    • URL: GET /app-api/education/context
    • 携带有效 Bearer Token + tenant-id header
    • 从响应获取 userIdtenantIdtenantName 用于页面展示
  4. 跨租户防护

    • 前端不应允许用户手动切换 tenant-id header
    • 后端通过 TenantSecurityWebFilter 拒绝认证用户的跨租户 header 操作

阻塞项:完整前端源码(含 router、store、package.json是上述前端集成的必要前提。一旦前端源码就位

  1. 在 Vue3 admin 的路由中添加 /education 路由项,绑定 Education 菜单组件
  2. 添加 src/api/education/ API 封装层(调用上述教育端点)
  3. Student Web/H5 端如需要独立入口,需新建对应前端项目

用户 APP - 题库目录Catalog

所有端点需要学生登录态Bearer TokenuserIdtenantId 由安全上下文派生,不接受客户端传参。 Scalar 代理层自动注入 x-tenant-id header来自 TenantContextHolder),前端不发起任何直达 Scalar 的请求。

架构边界

Browser → Controller(/education/catalog/*) → CatalogService → CatalogProvider → [Scalar]
                                              ↑ 内部 DTO/VO    ↑ Scalar DTO 仅此层
  • 业务层Controller/Service仅操作内部 Catalog VOCatalogRegionRespVO 等)
  • 集成层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、耗时和结果

前端集成提示

前端就位后,学生端学习首页应:

  1. 获取上下文(/education/context)确认登录态
  2. 调用 /education/catalog/regions 获取地区筛选器
  3. 根据地区调用 subjects / categories 获取科目分类
  4. 调用 content-entriescontent-nodes 构建目录树
  5. 叶子节点调用 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 不支持的题库数据源模式