# yudao-module-education 教育业务模块,提供课程、练习、题库、考试等教育业务功能。 ## 当前状态:应用外壳 (Shell) 此模块目前处于**应用外壳**阶段,提供: - 模块骨架与包结构 - 能力探测端点 (`/education/capability`) - 租户识别端点 (`/education/tenant/resolve`) — 学生端登录前使用 - 教育上下文端点 (`/education/context`) — 学生端已认证状态 - 独立的功能开关配置 - 错误码常量 - 权限与菜单种子数据(角色授权由管理员按租户完成) - 增量 SQL 交付约定 无业务表,无虚假 CRUD。 ## 功能配置 在 `application.yaml` 或对应 profile 中配置: ```yaml yudao: education: enabled: true # 是否启用教育模块,默认 false version: 1.0.0 # 模块版本号 hostname-tenant-map: # authority 到租户名的精确映射(可选,键在读取时统一转为小写) "staging.school.com": "demo-school" # DNS 与 websites 不一致时使用 login-methods: [PASSWORD, SMS] # 当前部署全局启用的 Member 登录入口 ``` - `yudao.education.enabled=true`:启用模块(Controller 注册、Swagger 分组可见) ## API ### 管理后台 - 能力信息 ``` GET /admin-api/education/capability ``` - 权限:`education:capability` - 响应示例: ```json { "code": 0, "msg": "成功", "data": { "module": "education", "enabled": true, "version": "1.0.0", "capabilities": ["shell"] } } ``` ### 用户 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 值查询 - 响应示例(成功): ```json { "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 前置完成租户校验,此处二次验证确保租户处于活跃状态。 - 响应示例: ```json { "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 | 当前租户不可用,请联系管理员 | ## 构建与运行 ### 单独编译测试 ```bash # 编译 education 模块 mvn compile -pl yudao-module-education -am # 运行 education 模块单元测试 mvn test -pl yudao-module-education -am ``` ### 整体编译(含 server) ```bash # 编译全量(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 应用 ```bash # 应用基础种子数据(菜单 + 权限定义;执行后由管理员为目标租户角色授权) 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 端前端集成契约 前端就位后必须实现以下流程(不能伪造静态页面): 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 - 从响应获取 `userId`、`tenantId`、`tenantName` 用于页面展示 4. **跨租户防护** - 前端不应允许用户手动切换 `tenant-id` header - 后端通过 `TenantSecurityWebFilter` 拒绝认证用户的跨租户 header 操作 **阻塞项**:完整前端源码(含 router、store、package.json)是上述前端集成的必要前提。一旦前端源码就位,需: 1. 在 Vue3 admin 的路由中添加 `/education` 路由项,绑定 Education 菜单组件 2. 添加 `src/api/education/` API 封装层(调用上述教育端点) 3. Student Web/H5 端如需要独立入口,需新建对应前端项目