# Education Pilot 验收与回滚手册 ## 1. 范围 本文覆盖学生核心学习闭环后端的 Pilot 发布、验证、监控和应用回滚。完整 Student Web/H5 源码当前不在本工作区,因此浏览器 E2E、桌面/H5 截图和前端构建验收仍是明确阻塞项,不能以 HTTP 或单元测试替代。 ## 2. Pilot 配置 ```yaml yudao: education: enabled: true catalog-read-enabled: true practice-write-enabled: true pilot-tenant-ids: [] catalog-mode: JAVA_READ scalar: enabled: false owner: exit-date: ``` 要求: - `pilot-tenant-ids` 在 Pilot 环境必须显式配置,不能使用空列表。 - Pilot 默认以 `JAVA_READ` 启动,只依赖目标 PostgreSQL;不得启动旧 NestJS API、旧 Worker、Supabase 或旧资产扫描服务作为前置条件。 - 仅在有明确负责人、告警、故障策略和退出日期的兼容窗口内切换 `SCALAR_READ`。Scalar token 只能通过密钥管理或环境变量注入。 - 发布前调用管理端 `/admin-api/education/capability` 和 `/admin-api/education/operations/health`;后者必须显示 `java-read-postgresql=UP`、`scalar-catalog=NOT_SELECTED`。 ## 3. 发布步骤 1. 备份 Education 相关表,并记录应用版本与 `flyway_schema_history`。 2. 由数据库管理员预置彼此独立的 Flyway owner LOGIN role 与 runtime LOGIN role。Flyway role 拥有目标 schema;runtime role 只获得业务表所需权限,且不得拥有、继承所有者角色或写入 `education_question_lifecycle_transition_token`、`education_content_node_lifecycle_transition_token`、`education_question_collection_lifecycle_transition_token`、`education_question_collection_membership_token`。角色/密码不由 migration 创建。 3. 显式注入 `FLYWAY_USER`、`FLYWAY_PASSWORD`(不得回退到 master datasource 账号),使用 Server 配置的 PostgreSQL Flyway 执行 migrate 和 validate,检查版本、脚本、checksum、success 以及四张 token 表 owner 均为 Flyway role。 4. 以 runtime datasource 账号验证四张 token 表均无 INSERT/UPDATE/DELETE/TRUNCATE,随后启动应用;同角色、继承 owner 或可写授权会导致 Education 启动检查 fail closed。 5. 先以 `catalog-read-enabled=false`、`practice-write-enabled=false` 部署应用。 6. 验证 System、Infra、Member 基础 smoke。 7. 仅对 Pilot 租户开启题库读取,完成 JAVA_READ 只读 smoke,并运行 `tools/education-target-smoke/java-read-readiness.sh`。 8. 对 Pilot 租户开启练习写入,完成会话、答案、交卷、报告、错题和收藏 smoke。 9. 若部署 Worker 或 Scanner,先确认其持续写入 `education_operational_component`,且 `/admin-api/education/operations/health` 无 `DOWN` 组件和未处理死信。 10. 观察错误率、延迟和数据库写入后再扩大租户列表。 ## 4. Smoke 清单 ### 基础与身份 - [ ] 非 Pilot 租户访问题库和练习写入被拒绝。 - [ ] Pilot 租户可完成 tenant resolve、Member 登录、refresh、logout 和 Education context。 - [ ] 错误 `tenant-id` 被租户安全过滤器拒绝。 ### 核心闭环 - [ ] 目录及题目只经 RuoYi API 返回,响应不含答案或解析。 - [ ] 创建练习后刷新可恢复相同会话、题序和已保存答案。 - [ ] 相同答案幂等键重试返回首次结果;旧版本和旧序号被拒绝。 - [ ] 交卷只生成一个报告,交卷后答案不可修改。 - [ ] 错题投影、错题复习和收藏操作仅对当前学生可见。 ### 隔离 - [ ] tenant A / student A 不能读取或修改 tenant A / student B 的记录。 - [ ] tenant A 不能读取或修改 tenant B 的记录,即使资源 ID 被猜中。 - [ ] 对 session、report、wrong question、favorite 分别留存拒绝结果证据。 ## 5. 故障与回滚 ### JAVA_READ / PostgreSQL 故障 1. 设置 `catalog-read-enabled=false`,停止新的目录和题目读取;不得静默切回 Scalar。 2. 保持 `enabled=true`,使已有会话、报告、错题和收藏仍可访问。 3. 检查 `/admin-api/education/operations/health` 的 `java-read-postgresql` 结果和 Flyway 历史。 4. 如需冻结新写入,再设置 `practice-write-enabled=false`。 5. 通过应用回滚或更高版本 Flyway 前滚修复,不执行 `flyway clean` 或手工回滚 SQL。 ### Scalar 兼容窗口故障 仅当部署明确选择 `SCALAR_READ` 时适用: 1. 设置 `catalog-read-enabled=false`,停止新的 Scalar 读取。 2. 保持 `enabled=true`,使已有会话、报告、错题和收藏仍可访问。 3. 如需冻结新写入,再设置 `practice-write-enabled=false`。 4. 验证 Education PostgreSQL 表行数和历史查询均未减少。 ### 练习写入熔断 设置 `practice-write-enabled=false` 后: - 新建练习、保存答案和交卷必须被拒绝; - 当前会话恢复、指定会话读取、报告和报告历史仍应可读; - 不执行清理、归档或 rollback SQL。 ### 应用回滚 1. 将应用回滚到上一已验证版本。 2. 保留所有 Education 表和数据,不执行 `flyway clean`、手工删除或任何 `*-rollback.sql`。 3. 若旧版本与新 schema 不兼容,保持功能关闭并通过更高版本 Flyway migration 前滚修复;不得通过删表恢复服务。 4. 重新验证 Member 登录、System 租户和 Infra 日志功能。 > 历史 `*-rollback.sql` 是数据销毁工具且不属于当前交付机制,不是常规应用版本回滚步骤。 ## 6. 可观测性 发布窗口至少观察: - `/admin-api/education/operations/health` 的必需依赖、Worker/Scanner 心跳和 open dead-letter 数; - `education_operational_component` 的 `last_heartbeat_at`、最后成功/失败和 backlog; - `education_dead_letter` 仅保留负载指纹与脱敏错误分类,不得保存业务 payload、凭据或个人数据; - Scalar 兼容模式请求成功率、4xx/5xx/timeout、P95/P99 延迟; - 练习创建成功/冲突数; - 答案保存成功、幂等重放、版本冲突和旧序号拒绝数; - 交卷成功、并发冲突和事务失败数; - Pilot 租户拒绝数; - JVM、数据库连接池、HTTP 错误率和接口延迟。 Scalar 日志只能记录脱敏路径、tenant ID、上游 request ID、状态、耗时和错误分类;不得记录 Authorization、Scalar token、学生答案、正确答案或完整响应体。RuoYi access/error log 中的 trace ID 用于关联入口请求;验收时需保存一条从入口日志到 Scalar request ID 的关联证据。 ### Worker、Scanner 与死信处置 1. Worker/Scanner 每次心跳使用固定 `component_key` upsert;部署实例变化写入 `instance_id`。 2. 心跳状态只能为 `STARTING/UP/DEGRADED/DOWN`,`detail` 必须脱敏且有界。 3. 重试耗尽后写入 `education_dead_letter`;同一租户、组件、workload 只允许一个 OPEN 记录。 4. 排障后先把原 OPEN 记录标记为 `REQUEUED` 并填写 `resolved_at`/`resolution_note`,再通过所属业务服务重入队;禁止直接修改业务结果或把原 payload 写入死信表。 5. 未部署对应 Worker/Scanner 时不得伪造 UP 心跳;能力清单应保持未交付状态。 ## 7. 验证命令 ```bash mvn -pl yudao-module-education -am test mvn -pl yudao-server -am package -DskipTests ``` 前端源码归位后还必须执行其 lint、类型检查、测试、生产构建及浏览器 E2E。 ## 8. 已知限制 - 当前工作区缺少完整 Student Web/H5 前端源码。 - 尚不能在本仓库完成浏览器 Network 无直连 Scalar 断言。 - 尚不能完成桌面和 H5 视觉截图对比。 - 真实 Scalar smoke 依赖部署环境、固定上游版本和有效只读凭据。 - Pilot 租户列表属于部署配置,修改后需要按配置刷新机制重新加载或重启应用。