Skip to content

计划执行记录

更新日期:2026-05-16

1. 工作基线

仓库路径当前分支工作区状态备注
/Users/moorefoss/Code/AUBB非 Git 仓库不适用根目录用于组织 serverwebdocs
server/main有未提交变更和新增文件后端代码、迁移、测试、脚本与 server/docs 文档均有本轮修改
web/main有未提交变更和新增文件前端 lint 修复、全局搜索、真实 E2E、课程公告校验、OpenAPI 生成类型和 web/docs 文档更新
docs/main有未提交变更和新增文件顶层文档同步与本轮交付报告

2. 已执行阶段

阶段状态关键输出
基线与技术栈核验已完成后端为 Spring Boot 4 / Maven / Java 25;前端为 Next.js 16 / React 19 / TypeScript;顶层文档站为 VitePress
后端编译与测试已完成已完成 Maven 编译、Spotless 与测试基线;本轮新增权限真实脚本回归后,权限真实 API 场景 22/22 通过
前端静态验证已完成已完成 lint、typecheck、unit test、build;真实后端 Playwright 最终全套 36/36 通过
默认 E2E已完成默认未设置 AUBB_E2E_REAL_BACKEND=1 时会跳过真实后端 smoke;真实模式已单独执行通过
V48 文档同步已完成当前文档已移除“成绩申诉、成绩发布快照、assignment 级成绩权重”为现行能力的表述,仅保留 V48 已移除说明
运行时 OpenAPI 刷新已完成使用 npx openapi-typescript http://127.0.0.1:18080/v3/api-docs -o src/shared/api/generated/openapi.ts 刷新前端生成类型
真实前后端联调已完成Docker 依赖、后端 18080、前端 3000 均启动成功;readiness、OpenAPI、登录、权限脚本、Playwright 和浏览器点验完成
顶层交付报告已完成本文件、全栈审计报告、按钮级 E2E 验证报告已补录真实联调证据

3. 本轮真实联调证据

验证项命令 / 操作结果
本地依赖cd server && docker compose up -dPostgreSQL、RabbitMQ、MinIO、Redis 健康,go-judge 运行;首次构建遇到 Debian 502,重试后成功
后端启动SERVER_PORT=18080 AUBB_MINIO_ENABLED=true ... bash ./mvnw spring-boot:run本地库已存在学校根节点 SCH-REALRUN,启用 bootstrap 且传入新 code 会失败;最终关闭 bootstrap、保留本地 JWT、启用本地 MinIO 后启动成功,Tomcat 监听 18080,Flyway 48 个迁移完成
后端 readinesscurl -fsS http://127.0.0.1:18080/actuator/health/readinessstatus=UP,最终响应包含 dbreadinessStateredisEnhancementminioStorage,bucket 为本地 aubb-assets
运行时 OpenAPIcurl -fsS http://127.0.0.1:18080/v3/api-docsOpenAPI 3.1.0,当前 124 个 path
登录探针POST /api/v1/auth/login管理员、教师、学生测试账号可登录并返回 access / refresh token
权限真实 APIbash scripts/api-tests/permission/run_permission_e2e.sh22 个真实 HTTP 权限场景全部通过,报告在 /tmp/aubb-permission-e2e
前端启动AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run dev -- --hostname 127.0.0.1 --port 3000Next dev server 就绪,页面通过 rewrite 连接真实后端
真实 PlaywrightAUBB_E2E_REAL_BACKEND=1 npm run test:e2e早期 smoke 基线通过;最终真实后端完整 Playwright 全套 36 passed
浏览器点验Playwright MCP 打开 http://127.0.0.1:3000登录失败/成功/退出、管理员导航与搜索、教师课程公告、学生作业/成绩、未授权跳转已验证

4. 关键修复清单

文件 / 模块问题修复方式验证
server/scripts/api-tests/permission/e2e_permission_realrun.py后端在课程任课/成员权限变更后会撤销受影响用户旧会话,脚本继续复用旧 U-TA1 token 导致真实 API 脚本在成员列表接口收到 401fixture 收尾刷新受课程权限授予影响的教师、助教、学生账号 tokene2e_permission_realrun_test.py 通过;真实权限 API 22/22 通过
web/src/app/(teacher)/teacher/courses/[offeringId]/announcements/page.tsx教师公告空标题/正文点击“发布”会静默无反馈改为真实 <form>,标题和正文添加 required 与可访问标签,编辑弹窗同样走 submit 校验新增 Playwright 用例;早期 smoke 通过;浏览器点验返回原生必填提示
web/src/app/(admin)/admin/users/page.tsxweb/src/shared/ui/file-upload-field.tsx用户管理顶部“批量导入”按钮没有触发隐藏文件选择器FileUploadField 增加 triggerRef,用户页顶部按钮通过 ref 打开 CSV 文件选择器file-upload-field.test.tsx 通过;真实 filechooser E2E 通过
web/src/shared/api/download/index.tsweb/src/tests/unit/api/download.contract.test.ts下载按钮只通过 API 拉取 blob,没有创建浏览器下载downloadBinary 解析文件名、创建 object URL、点击隐藏 anchor 并释放 URL,同时返回 blob 元数据新增单测 1/1 通过;完整前端单元测试 11 个文件 / 24 个测试通过
web/src/app/(teacher)/teacher/courses/[offeringId]/resources/page.tsxweb/src/tests/e2e/teacher-course.spec.ts课程资源 E2E 首次失败,后端未启用对象存储时上传 API 返回 COURSE_RESOURCE_STORAGE_DISABLED;资源标题字段也缺少稳定 label 关联补资源页 label/id;E2E 等待上传 POST 并断言响应 OK;后端联调启动启用本地 MinIO资源上传、改名、下载、删除目标 E2E 1/1 通过;full-course 再次覆盖教师/学生下载
web/src/tests/e2e/auth.spec.ts退出登录菜单项真实 role 为 menuitem,测试按 button 查询导致误报失败改用 getByRole("menuitem", { name: "退出登录" })单用例通过;完整真实 E2E 通过
web/src/shared/api/generated/openapi.ts前端生成类型落后于运行时后端契约从真实后端 /v3/api-docs 重新生成生成命令通过;后续 typecheck/build 覆盖
web/src/app/(student)/student/assignments/page.tsxweb/src/app/(student)/student/courses/[classId]/page.tsxweb/src/app/(student)/student/labs/page.tsx运行时 OpenAPI 中 MyCourseClassView 不包含 features,学生端仍读取不存在字段导致 typecheck 失败去除学生端对不存在 features 字段的依赖,入口按当前 /me/courses 合约默认可见,内容与权限仍由后端接口控制npm run typechecknpm run build、真实 E2E smoke 和 full suites 覆盖
server/src/test/java/com/aubb/server/integration/AuthzOpenApiAccessRegistry.javaOpenAPI 权限登记表仍包含已删除成绩申诉 / 发布批次路径,且缺少部分 PUT/DELETE 路径移除旧路径,补齐学期、课程、公告、资源等当前路径权限映射后端全量测试基线通过
server/src/test/java/com/aubb/server/integration/*IntegrationTests.java固定 2026 时间窗和旧申诉通知导致测试漂移改为动态时间窗,旧申诉通知改为当前实验报告通知后端全量测试基线通过
web/src/shared/ui/layout/topbar.tsx顶栏全局搜索输入可输入但缺少有效跳转新增全局搜索路由匹配,Enter 跳到首个可访问目标前端单元测试和真实浏览器点验通过
web/docs/backend/**server/docs/**docs/**多处把 V48 已删除能力写成当前能力更新 API、数据模型、权限、产品规格和顶层文档残留扫描仅剩“V48 已删除/不再提供”的说明性命中
web/playwright.config.ts、旧 smoke specs、web/src/tests/e2e/real-backend.ts全量真实 E2E 并行使用共享真实账号时触发登录限流,30 秒预算下出现超时真实后端模式使用 1 worker 和 90 秒测试预算;非认证 smoke 改为 API session 注入;UI 登录 helper 仅在明确 429 时等待重试最终 npm run test:e2e 全套 36 passed

5. 当前运行状态

服务地址状态
后端 Spring Boothttp://127.0.0.1:18080本轮验证时运行中;最终收尾后停止
前端 Next dev serverhttp://127.0.0.1:3000本轮验证时运行中;最终收尾后停止
PostgreSQL / RabbitMQ / MinIO / Redis / go-judgeDocker compose本轮验证时运行中;最终收尾后执行 docker compose down,未删除数据卷

6. 注意事项

  • 本轮只使用本地 Docker、测试账号和本地 dummy 环境变量;未连接生产数据库、生产服务或付费外部 API。
  • 报告中只记录 token 是否存在,不记录 token、cookie、JWT、私钥或真实密钥值。
  • 课程公告和课程资源已做浏览器级创建-验证-清理闭环;批量导入文件提交、发布成绩、实验评阅等写入较重操作未全部提交,其真实写入闭环仍主要由 API 权限脚本和后端集成测试覆盖。
  • 课程资源上传/下载真实 E2E 需要后端以本地 MinIO 启动;若 AUBB_MINIO_ENABLED=false,上传 API 会按设计返回 COURSE_RESOURCE_STORAGE_DISABLED
  • 真实前后端 E2E 必须用 AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 启动 Next dev server;缺少该变量时 /api/v1/auth/login 会由 Next 返回 404,不代表后端登录失败。

7. 本轮按 plan.md 重新执行记录(2026-05-16 19:20 CST)

Task 1:建立当前工作基线

检查项结果
当前目录/Users/moorefoss/Code/AUBB
Git 仓库发现根目录不是 Git 仓库;仅发现 ./server/.git./web/.git./docs/.git
server/main...origin/main,最近提交 510897f chore: sync audit fixes and verification,开始时 git status --short 无未提交条目
web/main...origin/main,最近提交 2c6969b feat: align frontend integration and e2e coverage,开始时 git status --short 无未提交条目
docs/main...origin/main,最近提交 261de6a docs: add full stack audit reports,开始时 git status --short 无未提交条目
后端命令入口./mvnw -v 成功;Maven 3.9.14,Java 25.0.3
前端命令入口npm run 确认 devbuildlinttypechecktesttest:e2e
文档命令入口npm run 确认 docs:devdocs:builddocs:preview

本轮验证范围:按根目录 plan.md Task 1 至 Task 11 执行,覆盖 GAP-AUTH-01GAP-ADMIN-01GAP-ADMIN-10GAP-COURSE-01GAP-COURSE-06GAP-ASSIGN-01GAP-ASSIGN-07GAP-GRADE-01GAP-GRADE-03GAP-LAB-01GAP-LAB-03GAP-NOTIF-01GAP-NOTIF-02GAP-DOWNLOAD-01GAP-NAV-01GAP-RESP-01

Task 2:从代码重新生成全量功能矩阵

检查项命令结果
前端页面rg --files web/src/app | sortfind web/src/app -name page.tsx45 个 page.tsx;未发现 plan.md 1.3 之外的新增业务路由
后端 Controllerrg --files server/src/main/java/com/aubb/server/modules | rg '/api/.*Controller\.java$'33 个 Controller;覆盖认证、治理、课程、作业、提交、判题、成绩、实验、通知
API 注解/SSErg -n '@(Get|Post|Put|Patch|Delete|Request)Mapping|TEXT_EVENT_STREAM|SseEmitter' server/src/main/java/com/aubb/server/modules确认通知 SSE 路径 GET /api/v1/me/notifications/stream,并确认各 Controller 的 HTTP 映射
前端 API 封装find src/features -path '*/api/*' -type f -maxdepth 5 | sort20 个 API 封装文件;均可映射到后端 Controller 和页面入口
导航入口web/src/shared/routing/nav-config.ts管理员 9 个、教师 6 个、学生 6 个侧边栏入口

输出文件:已更新 docs/06-testing-and-ops/full-stack-audit-report.md 第 14 节,补入本轮代码扫描矩阵、API 封装映射和 GAP 基线。

Task 3:准备真实本地联调环境

检查项命令 / 操作结果
Docker 依赖cd server && docker compose up -d postgres rabbitmq minio redis go-judge && docker compose psPostgreSQL、RabbitMQ、MinIO、Redis、go-judge 均处于运行状态;PostgreSQL/RabbitMQ/MinIO/Redis health 为 healthy,go-judge 监听本地 5050
后端本地环境使用 SERVER_PORTSPRING_DATASOURCE_*SPRING_RABBITMQ_*AUBB_JWT_SECRETAUBB_MINIO_*AUBB_GO_JUDGE_*AUBB_REDIS_*AUBB_JUDGE_QUEUE_*AUBB_BOOTSTRAP_ENABLED=false仅使用本地 Docker 依赖和本地变量;报告不记录变量值;未连接生产服务
后端启动bash ./mvnw spring-boot:run,监听 http://127.0.0.1:18080Spring Boot 启动成功,Flyway 48 个迁移已是最新;修复平台配置 nullable 字段后重启为 session 69196
后端 readinesscurl -fsS http://127.0.0.1:18080/actuator/health/readinessstatus=UP;components 为 dbgoJudgejudgeQueueminioStoragereadinessStateredisEnhancement
运行时 OpenAPIcurl -fsS http://127.0.0.1:18080/v3/api-docs可访问;用于后续全量 E2E 和最终回归验证
前端启动既有 Next dev server session 99032http://127.0.0.1:3000/login/login 返回 HTTP 200;前端代理指向本地后端
前置修复验证cd server && ./mvnw -Dtest=PlatformGovernanceApiIntegrationTests#managesLivePlatformConfigAndWritesAuditTrail test首次新增 nullable 字段清空断言为红态;修复 PlatformConfigEntity nullable 更新策略后该用例通过

Task 3 输出:真实本地后端、真实前端与 Docker 依赖均已就绪,可进入 Task 4 至 Task 9 的 Playwright Chromium 全量验证。

Task 4:建立全量 Playwright 真实验证工程

检查项结果
E2E helperweb/src/tests/e2e/real-backend.ts 已扩展 API 登录、cookie 注入、唯一名、API 断言、下载断言、multipart、coverage 记录;只读取环境变量,不打印密码、token、cookie 或 JWT
fixtureweb/src/tests/e2e/full-fixtures.ts 通过 API 准备管理员、教师、学生和课程上下文;为避免历史数据状态影响,创建新的 E2E-* active 学生并加入 A1 班
full suites新增 full-admin.spec.tsfull-course.spec.tsfull-assignment-judge.spec.tsfull-grading-lab-notification.spec.tsfull-navigation-permission.spec.ts;保留原 smoke specs
中间修复验证full-admin.spec.ts 5/5 通过;full-course.spec.ts 5/5 通过;full-assignment-judge.spec.ts 最终 3/3 通过;full-grading-lab-notification.spec.ts 最终 3/3 通过;full-navigation-permission.spec.ts 最终 3/3 通过

Task 4 输出:真实 E2E 工程已从 smoke 扩展为管理员、课程、作业判题、成绩实验通知、导航权限响应式五个 full suites。

Task 5:管理端全量真实验证

GAP结果证据
GAP-AUTH-01已真实验证refresh、revoke、无 token 401、管理员撤销临时用户会话后旧 access token 失效
GAP-ADMIN-01已真实验证平台配置读取、保存、重新读取、恢复原值闭环;修复 nullable 字段无法清空问题
GAP-ADMIN-02部分验证组织架构页面打开,子组织创建后出现在树中;根 SCHOOL 创建按后端策略返回 400/409;无删除 API,记录残留
GAP-ADMIN-03GAP-ADMIN-04GAP-ADMIN-05已真实验证用户创建、搜索、详情、身份/profile/组织关系更新、禁用启用、撤销会话、CSV 成功/失败行导入
GAP-ADMIN-06GAP-ADMIN-07GAP-ADMIN-08GAP-ADMIN-09GAP-ADMIN-10已真实验证学期、课程模板、开课创建筛选编辑详情;审计日志筛选;权限解释允许/拒绝/错误输入

执行命令:cd web && AUBB_E2E_REAL_BACKEND=1 ... npx playwright test src/tests/e2e/full-admin.spec.ts --reporter=list --project=chromium,结果 5 passed。省略号为本地测试账号密码环境变量,未记录值。

Task 6:课程、成员、讨论、题库、判题环境全量验证

GAP结果证据
GAP-COURSE-01GAP-COURSE-02已真实验证教学班创建、功能开关、批量成员成功/失败、CSV 导入、成员禁用恢复、转班
GAP-COURSE-03GAP-DOWNLOAD-01已真实验证教师上传/改名/下载/删除课程资源,学生侧列表和下载同一资源
GAP-COURSE-04已真实验证教师发帖/回复、学生回复、锁定后学生回复被拒绝、最后解锁
GAP-COURSE-05已真实验证单选、多选、简答、文件上传、编程题创建,题库筛选、编辑、归档
GAP-COURSE-06已真实验证判题环境创建、编辑、归档

执行命令:cd web && AUBB_E2E_REAL_BACKEND=1 ... npx playwright test src/tests/e2e/full-course.spec.ts --reporter=list --project=chromium,结果 5 passed

Task 7:作业、提交、编程、判题全量验证

GAP结果证据
GAP-ASSIGN-01GAP-ASSIGN-02已真实验证教师创建结构化作业、编辑基础信息、替换试卷、发布、学生可见、关闭后提交被拒绝
GAP-ASSIGN-03GAP-ASSIGN-04GAP-DOWNLOAD-01已真实验证学生选择/简答/文件/编程答案提交、提交历史、提交详情、学生和教师附件下载
GAP-ASSIGN-05GAP-ASSIGN-06GAP-ASSIGN-07已真实验证编程工作区文件操作/保存/修订/恢复/重置、样例运行、正式判题、学生/教师报告下载、教师手动评分和重判

执行命令:cd web && AUBB_E2E_REAL_BACKEND=1 ... npx playwright test src/tests/e2e/full-assignment-judge.spec.ts --reporter=list --project=chromium,最终结果 3 passed。中间修复:教师提交详情页访问前切换浏览器会话到教师;页面断言改为真实稳定内容“答案内容”。

Task 8:成绩、实验、通知全量验证

GAP结果证据
GAP-GRADE-01GAP-GRADE-02GAP-GRADE-03GAP-DOWNLOAD-01已真实验证教师成绩册按开课/班级查询、报告、导出、人工评分、批量调分、模板下载、CSV 导入、发布;学生成绩查询和导出
GAP-LAB-01GAP-LAB-02GAP-LAB-03GAP-DOWNLOAD-01已真实验证实验创建/编辑/发布/关闭,学生附件上传保存提交读取下载,教师报告列表详情下载评阅发布,学生可见评语
GAP-NOTIF-01GAP-NOTIF-02已真实验证通知列表、未读数、单条已读、全部已读;SSE connectednotification 事件

执行命令:cd web && AUBB_E2E_REAL_BACKEND=1 AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 ... npx playwright test src/tests/e2e/full-grading-lab-notification.spec.ts --reporter=list --project=chromium,最终结果 3 passed。中间修复:SSE 探针直连本地后端 AUBB_SERVER_ORIGIN,为 reader.read() 增加 deadline,并修正重复触发 publish 的测试状态;学生实验页面跳转前切换浏览器会话到学生。

Task 9:导航、下载、响应式、权限矩阵

GAP结果证据
GAP-NAV-01已真实验证三角色侧边栏全部导航、概览快捷入口、顶栏搜索、用户菜单打开;学生/教师越权页面回退到默认首页或无权限/登录,后端无 token 401、无角色 403
GAP-RESP-01已真实验证管理、教师、学生关键页在 1280x800390x844 下主按钮或移动导航菜单可见可点击
GAP-DOWNLOAD-01已真实验证已在 Task 6-8 覆盖课程资源、提交附件、判题报告、成绩导出、成绩导入模板、实验附件等下载 API 的 OK 和非空响应

执行命令:cd web && AUBB_E2E_REAL_BACKEND=1 ... npx playwright test src/tests/e2e/full-navigation-permission.spec.ts --reporter=list --project=chromium,最终结果 3 passed。中间修复:登录后先进入角色首页再遍历侧边栏;同名快捷入口冲突时限定 navigation 区域;按钮文案按当前 UI 使用“新增用户”;权限断言允许当前实现回退到角色默认首页。

Task 10:后端/API/文档回归验证

步骤命令结果
后端脚本单测cd server && python3 scripts/api-tests/permission/e2e_permission_realrun_test.pyRan 1 testOK
后端权限真实 APIcd server && BASE_URL=http://127.0.0.1:18080 bash scripts/api-tests/permission/run_permission_e2e.shcaseCount=22passedCount=22failedCount=0status=passed
后端全量 verifycd server && bash ./mvnw verifyBUILD SUCCESS;Tests run: 318,Failures: 0,Errors: 0,Skipped: 0
前端 typecheckcd web && npm run typecheck通过
前端 lintcd web && npm run lint通过,0 errors
前端单元测试cd web && npm run test11 个测试文件、24 个测试通过
前端真实后端 E2Ecd web && AUBB_E2E_REAL_BACKEND=1 AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 ... npm run test:e2e完整套件 36 passed;中间曾因共享账号限流和 30 秒预算失败,修复测试基础设施后全量重跑通过
前端生产构建cd web && AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run build通过;Next.js 编译、TypeScript、静态页面生成均成功
文档构建cd docs && npm run docs:build通过;VitePress build complete

Task 10 额外修复:web/playwright.config.ts 在真实后端模式下使用 1 worker 和 90 秒测试预算;旧 smoke specs 中非认证目的的页面访问改为 loginAsApi 注入 session;real-backend.ts 的 UI 登录 helper 只在页面明确返回 429 限流提示时等待并重试。该调整避免全量真实 E2E 使用共享测试账号时误触登录限流。

Task 11:收尾、服务关闭、提交建议

检查项命令 / 操作结果
停止前端终止本地 Next dev 进程已停止;curl --max-time 2 -sS http://127.0.0.1:3000/login 返回连接失败
停止后端终止本地 Maven/Spring Boot 进程已停止;curl --max-time 2 -sS http://127.0.0.1:18080/actuator/health/readiness 返回连接失败
停止 Docker 依赖cd server && docker compose downPostgreSQL、RabbitMQ、MinIO、Redis、go-judge 容器和网络已移除;未删除数据卷
Compose 状态cd server && docker compose ps仅输出表头,无运行服务

Task 11 Git 摘要

仓库状态diff stat
server/main...origin/main,6 个已修改文件6 files changed, 89 insertions(+), 8 deletions(-)
web/main...origin/main,7 个已修改文件,6 个新增 full E2E 文件未跟踪tracked diff 为 7 files changed, 385 insertions(+), 82 deletions(-);新增文件为 full-admin.spec.tsfull-assignment-judge.spec.tsfull-course.spec.tsfull-fixtures.tsfull-grading-lab-notification.spec.tsfull-navigation-permission.spec.ts
docs/main...origin/main,3 个已修改报告文件3 files changed, 338 insertions(+), 100 deletions(-)

8. 2026-05-17 继续执行审计

本段记录上下文恢复后的实际状态校验。此前 Task 11 的 Git 摘要记录的是提交前状态;本段以当前工作区命令输出为准。

检查项当前证据
当前提交:server/32aea14 fix(authz): 补强真实权限验证修复
当前提交:web/f9b0245 test(e2e): 补全真实权限场景
当前提交:docs/7105a31 docs(testing): 记录真实权限验证
剩余 dirty:server/M Dockerfile,属于本轮开始时已记录的既有 dirty 文件,未纳入本次功能提交
剩余 dirty:web/M next.config.tsM src/tests/e2e/full-fixtures.ts,属于本轮开始时已记录的既有 dirty 文件,未纳入本次功能提交
剩余 dirty:docs/追加本段前为 clean;追加本段后仅包含本审计记录
运行时 OpenAPI 快照服务停止前检查为 3.1.0 124
服务停止docker compose down 后残留 appjudge-worker 占用 server_default,随后执行 docker compose stop app judge-worker && docker compose rm -f app judge-worker && docker compose down;未删除 volumes
最终服务状态docker compose ps -a 仅输出表头;127.0.0.1:18080127.0.0.1:3000 均连接失败
当前阻塞恢复后的当前 shell、zsh -lclaunchctl 均看不到四个 AUBB_E2E_*_PASSWORD 变量,因此不能重新运行真实登录基线或 full regression

本段不记录密码、token、cookie、JWT、私钥或真实连接串。

2026-05-17 继续执行补充

在 E2E 密码变量仍缺失、真实服务不得启动的状态下,补充执行不依赖真实后端的前端静态验证:

命令结果
cd web && npm run typecheck通过,退出码 0
cd web && npm run lint通过,退出码 0

该验证仅说明当前 web/ 剩余 dirty 文件未造成 TypeScript 或 ESLint 失败;不能替代真实后端、真实依赖和真实浏览器 E2E 门禁。

2026-05-17 后端静态门禁补充

命令结果
cd server && bash ./mvnw spotless:checkBUILD SUCCESS;Spotless 检查 531 个 Java 文件,0 个文件需要修改

该验证不依赖 E2E 密码变量;真实服务启动、后端 full regression 和真实浏览器 E2E 仍等待四个 AUBB_E2E_*_PASSWORD 变量进入当前执行环境。

2026-05-17 后端与前端单元回归补充

命令结果
cd server && bash ./mvnw verifyBUILD SUCCESS;Tests run: 318,Failures: 0,Errors: 0,Skipped: 0
cd web && npm run test11 个测试文件、24 个测试通过

mvn verify 使用 Testcontainers 隔离依赖,结束后已检查 docker ps 无运行容器、docker compose ps -a 仅输出表头。该补充仍不替代 plan.md 要求的真实本地服务、三角色登录基线和真实浏览器 E2E;这些仍因四个 AUBB_E2E_*_PASSWORD 变量缺失而阻塞。

2026-05-17 前端构建补充

命令结果
cd web && AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run build通过;Next.js 16.2.4 Turbopack 编译、TypeScript、30 个静态页面生成和路由优化均完成

该构建验证不启动本地后端;真实 API 代理、三角色登录和真实浏览器 E2E 仍需在四个 E2E 密码变量可用后重新执行。

2026-05-17 E2E Mock 与权限脚本静态补充

命令结果
`cd web && rg -n "\b(pagecontext
cd server && python3 scripts/api-tests/permission/e2e_permission_realrun_test.pyRan 1 testOK

该补充仅覆盖静态 mock 扫描和权限脚本单元测试;真实权限 HTTP 脚本仍需本地后端 127.0.0.1:18080 与 E2E 密码变量可用后重新执行。

2026-05-17 剩余 Dirty 文件归因补充

仓库文件只读归因
server/Dockerfile当前 diff 移除 BuildKit cache mount,并在 runtime stage 安装 curlserver/compose.yamlapp healthcheck 使用 curl -fsS http://localhost:8080/actuator/health/readiness。该文件在本轮开始时已经 dirty,未纳入本轮提交,不能在未确认归属前提交或回滚
web/next.config.ts当前 diff 增加 allowedDevOrigins: ["127.0.0.1"],与本计划固定使用 127.0.0.1:3000 的本地真实浏览器验证相关。该文件在本轮开始时已经 dirty,未纳入本轮提交
web/src/tests/e2e/full-fixtures.ts当前 diff 将全量用户列表查询改为按用户名分页查询,并把 userId(...) 改为 async;这可降低历史 E2E 数据膨胀导致固定 pageSize=100 找不到账号的风险。该文件在本轮开始时已经 dirty,未纳入本轮提交

上述归因不改变文件状态;剩余 dirty 文件仍需后续由用户或明确任务确认后再决定提交、保留或回滚。

2026-05-17 Dockerfile 构建尝试

命令结果
cd server && docker build -f Dockerfile -t aubb-server:dirty-check .未完成;构建在拉取/解析 maven:3.9.11-eclipse-temurin-25eclipse-temurin:25-jre 基础镜像 metadata 阶段超过 2 分钟无进展

该构建是为验证剩余 dirty server/Dockerfile 的可构建性而启动的临时检查;未运行容器、未删除 volumes。随后只终止了该 docker build/buildx 验证进程,并确认 docker ps 无运行容器。该项不能作为 Dockerfile 构建通过证据。

2026-05-17 Compose 静态配置补充

命令结果
cd server && docker compose config --quiet通过,退出码 0
cd server && docker compose --profile app config --quiet通过,退出码 0

该验证只检查 server/compose.yaml 可解析性,不创建或启动容器;真实依赖、后端 readiness 和 OpenAPI 仍需 E2E 密码变量可用后重新启动验证。

2026-05-17 Dockerfile 静态检查补充

命令结果
cd server && docker image inspect maven:3.9.11-eclipse-temurin-25 eclipse-temurin:25-jre基础镜像本地不存在
cd server && docker build --check --progress=plain -f Dockerfile .未完成;同样卡在 Docker Hub 基础镜像 metadata 阶段,随后终止该临时验证进程

因此当前仍没有 server/Dockerfile 构建通过证据;已知限制是基础镜像未缓存且 registry metadata 请求无进展。

2026-05-17 Diff 格式检查补充

命令结果
cd server && git diff --check通过,退出码 0
cd web && git diff --check通过,退出码 0
cd docs && git diff --check通过,退出码 0

该检查仅覆盖当前工作区 diff 的空白和补丁格式问题;不替代真实服务或浏览器验证。

2026-05-17 恢复后阻塞复核

检查项当前证据
server/ dirtyM Dockerfile,仍为本轮开始时已记录的既有 dirty 文件
web/ dirtyM next.config.tsM src/tests/e2e/full-fixtures.ts,仍为本轮开始时已记录的既有 dirty 文件
docs/ dirty追加本段前为 clean;追加本段后仅包含本审计记录
E2E 密码变量AUBB_E2E_ADMIN_PASSWORDAUBB_E2E_TEACHER_PASSWORDAUBB_E2E_STUDENT_PASSWORDAUBB_E2E_TEMP_USER_PASSWORD 均为 MISSING

本次只执行变量存在性检查,没有打印任何密码值。由于四个真实 E2E 密码变量仍不可见,继续遵守计划硬门禁:不启动 127.0.0.1:18080 后端、不启动 127.0.0.1:3000 前端、不启动 Docker 依赖、不运行真实浏览器 E2E。

2026-05-17 E2E 变量来源排查

检查项当前证据
当前 shell四个 AUBB_E2E_*_PASSWORD 变量均为 ENV_MISSING
launchctl getenv四个变量均为 LAUNCHCTL_MISSING
常见 shell 配置$HOME/.zshenv$HOME/.zprofile$HOME/.zshrc$HOME/.profile$HOME/.bash_profile$HOME/.bashrc 中未检出这四个变量名

本次只检查变量是否存在或变量名是否出现,没有读取、打印或落盘任何密码值。当前证据说明阻塞不是单纯的 launchctl 到 shell 传播问题;在本执行环境可见范围内,四个真实 E2E 密码变量尚未配置。

2026-05-17 E2E 环境加载入口排查

检查项当前证据
仓库内变量名来源plan.md、本执行日志、web/src/tests/e2e/README.mdweb/src/tests/e2e/real-backend.ts 命中四个密码变量名
本地 env 模板server/deploy/.env.uat.example.env.staging.example.env.production.example 存在,但未作为 E2E 密码变量模板
自动加载入口根目录、server/web/docs/ 下未发现 .envrc、Justfile、项目级 Makefile 或 mise 配置
E2E README明确真实密码必须从环境变量提供,默认账号标识为 U-SA1U-TA1U-ST1

本次仍只记录路径和变量名命中情况,没有读取或打印任何密码值。当前可见仓库内容没有提供替代的本地加载机制;真实服务启动链路仍等待四个 AUBB_E2E_*_PASSWORD 进入当前执行环境。

2026-05-17 Playwright 环境加载确认

检查项当前证据
web/package.jsontest:e2e 直接执行 playwright test
web/playwright.config.ts通过 process.env.AUBB_E2E_REAL_BACKENDprocess.env.PLAYWRIGHT_TEST_BASE_URL 读取环境
dotenv / loadEnvpackage.jsonplaywright.config.tsweb/src/tests/e2e 中未发现 dotenv 或自定义 env 文件加载逻辑
E2E 密码读取web/src/tests/e2e/real-backend.tsrequiredEnv(...) 直接从 process.env 读取四个密码变量

因此真实 E2E 运行前,四个 AUBB_E2E_*_PASSWORD 必须已经存在于当前命令环境;当前仓库代码不会自动从本地文件加载它们。

2026-05-17 基线 Step 2 阻塞复核

检查项当前证据
当前 shellAUBB_E2E_ADMIN_PASSWORDAUBB_E2E_TEACHER_PASSWORDAUBB_E2E_STUDENT_PASSWORDAUBB_E2E_TEMP_USER_PASSWORD 均为 MISSING;检查命令退出码 1
launchctl getenv 判断对存在变量名和不存在变量名均返回退出码 0,因此不能用退出码判断变量存在性;捕获输出并只判断是否为空后,四个 AUBB_E2E_*_PASSWORD 变量仍为 MISSING
server/ dirtyM Dockerfile,为本轮恢复时已存在改动,未回滚、未提交
web/ dirtyM next.config.tsM src/tests/e2e/full-fixtures.ts,为本轮恢复时已存在改动,未回滚、未提交
docs/ dirtyM 06-testing-and-ops/plan-execution-log.md,来自本阻塞记录
服务状态127.0.0.1:18080127.0.0.1:3000NOT_LISTENINGdocker ps 无运行容器

本次只检查变量是否存在,没有打印、写入或提交任何密码值。由于 plan.md 的真实环境启动与基线 Step 2 仍未满足,继续暂停真实执行:不启动 Docker 依赖、不启动后端 127.0.0.1:18080、不启动前端 127.0.0.1:3000、不运行真实 Playwright E2E。该阻塞不能作为完成证据,目标未达到生产上线门禁。

2026-05-17 当前会话 Step 2 阻塞复核

检查项当前证据
执行计划与约束已重新读取 plan.mdserver/AGENTS.mdweb/AGENTS.md,继续使用 superpowers:executing-plans;当前仍停在真实环境启动与基线 Step 2
子仓库 dirtyserver/: M Dockerfileweb/: M next.config.tsM src/tests/e2e/full-fixtures.tsdocs/: M 06-testing-and-ops/plan-execution-log.md
E2E 密码变量AUBB_E2E_ADMIN_PASSWORDAUBB_E2E_TEACHER_PASSWORDAUBB_E2E_STUDENT_PASSWORDAUBB_E2E_TEMP_USER_PASSWORD 均为 MISSING;检查命令退出码 1
服务状态127.0.0.1:18080127.0.0.1:3000 均无监听;docker ps 无运行容器

本次只检查变量存在性,没有打印、写入或提交任何密码值。由于四个真实 E2E 密码变量仍未进入当前命令环境,继续按硬门禁暂停:不启动 Docker 依赖、不启动后端、不启动前端、不运行真实 Playwright E2E。

2026-05-17 文档构建补充验证

命令结果
cd docs && npm run docs:build通过;最新复跑 VitePress build complete in 2.21s

该验证只证明当前 docs/ 文档站仍可构建,不能替代真实依赖、真实后端、真实前端和真实浏览器 E2E 门禁。

2026-05-17 前端静态与单元回归复跑

命令结果
cd web && npm run lint通过,退出码 0
cd web && npm run typecheck通过,退出码 0
cd web && npm run test通过;Vitest 11 passed test files,24 passed tests
cd web && AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run build通过;Next.js 编译、TypeScript、30 个静态页面生成均成功

该验证只覆盖前端静态检查与单元测试;真实后端 Playwright、真实依赖和三角色登录基线仍等待 E2E 密码变量进入当前命令环境。

2026-05-17 后端回归复跑

命令结果
cd server && python3 scripts/api-tests/permission/e2e_permission_realrun_test.py通过;Ran 1 testOK
cd server && bash ./mvnw spotless:checkBUILD SUCCESS;Spotless 检查 531 个 Java 文件,0 个文件需要修改
cd server && bash ./mvnw verifyBUILD SUCCESS;Tests run: 318,Failures: 0,Errors: 0,Skipped: 0

mvn verify 使用 Testcontainers 启动了临时 PostgreSQL、MinIO、RabbitMQ、go-judge 与 Ryuk 容器;命令结束后等待 Ryuk 自动清理,docker ps 仅输出表头。127.0.0.1:18080127.0.0.1:3000 仍无监听。本段验证不包含真实本地前端、三角色登录基线或 Playwright E2E。

2026-05-17 工作区 diff 质量检查

仓库命令结果
server/git diff --check通过,退出码 0
web/git diff --check通过,退出码 0
docs/git diff --check通过,退出码 0

2026-05-17 Playwright mock 禁用静态复核

命令结果
`cd web && rg -n "page\.route\(context\.route\(
`cd web && rg -n "mockmsw
cd web && npx playwright test --list --project=chromium可发现 10 个 E2E spec 文件、36 个测试标题

该复核只证明当前 E2E 源码未使用 Playwright 网络拦截或明显 mock 关键字;真实后端 E2E 仍需在 E2E 密码变量注入后连接本地 127.0.0.1:18080127.0.0.1:3000 重跑。

2026-05-17 dirty 文件敏感词计数复核

仓库命令范围结果
server/对当前 dirty 文件运行敏感关键词计数,不打印匹配行无命中
web/对当前 dirty 文件运行敏感关键词计数,不打印匹配行src/tests/e2e/full-fixtures.ts 有 3 处关键词命中
docs/对当前 dirty 文件运行敏感关键词计数,不打印匹配行当前执行日志有 42 处关键词命中

该复核只输出文件名和计数,不打印、写入或提交任何密码、token、cookie、JWT、私钥或真实连接串。命中来自测试变量名、认证术语和“不要记录敏感值”的执行记录;真实 E2E 密码变量仍未注入当前命令环境。

2026-05-17 当前恢复轮补充复核

检查项命令 / 证据结果
文档构建cd docs && npm run docs:build通过;VitePress build complete in 2.24s
前端生产构建cd web && AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run build通过;Next.js 编译、TypeScript、30 个静态页面生成均成功
工作区状态git status --short --branch 分别在 server/web/docs/ 执行server/ 仍仅 M Dockerfileweb/ 仍仅 M next.config.tsM src/tests/e2e/full-fixtures.tsdocs/ 在追加本段前为 clean
服务状态curl --max-time 2 -fsS 检查 127.0.0.1:18080127.0.0.1:3000后端和前端均不可访问;本轮未启动 Docker、后端、前端或 Playwright

本段是阻塞期间可执行的非账号、非真实服务门禁补充。真实依赖、真实后端、真实前端、三角色登录基线和 AUBB_E2E_REAL_BACKEND=1 npm run test:e2e 仍必须等待用户确认 环境变量已注入 后再执行;当前 goal 不能标记完成。

2026-05-17 本轮 plan.md 重新执行记录

Phase 0:重新读取计划与初始 Git 状态

检查项当前证据
计划来源已完整读取根目录 /Users/moorefoss/Code/AUBB/plan.md,共 785 行;本轮以该文件的 0-5 阶段、19 个任务和完成门禁为主规范
仓库边界根目录不是 Git 仓库;find . -maxdepth 3 -name .git -type d 仅发现 ./server/.git./web/.git./docs/.git
server/ 初始状态main...origin/main [ahead 1]M Dockerfile;当前 HEAD 32aea14
web/ 初始状态main...origin/main [ahead 1]M next.config.tsD plan.mdM src/tests/e2e/full-fixtures.tsD todo.md;当前 HEAD f9b0245
docs/ 初始状态main...origin/main [ahead 18];本轮修改前 git status --short --branch 无未提交条目;当前 HEAD 3683991
旧报告处理2026-05-16 和更早执行记录仅作为线索;本轮结论必须来自当前命令、当前代码和当前运行结果

Phase 1:结构、技术栈、命令与测试资产重新核验

核验项命令 / 文件当前事实
后端规范server/AGENTS.md默认中文;修改前记录 Git 状态;完成前必须有真实验证证据;commit summary 使用中文
前端规范web/AGENTS.mdNext.js 16 项目;UI 改动需遵守现有设计约束;真实问题优先系统化排查和验证
后端技术栈server/pom.xml./mvnw -vSpring Boot 4.0.5、Java 25.0.3、Maven 3.9.14、MyBatis-Plus、Flyway、PostgreSQL、RabbitMQ、Redis、MinIO、go-judge
前端技术栈web/package.jsonNext.js 16.2.4、React 19.2.4、TypeScript、Tailwind、Vitest、Playwright
文档技术栈docs/package.jsonVitePress;命令为 docs:devdocs:builddocs:preview
依赖服务server/compose.yamlPostgreSQL、RabbitMQ、MinIO、Redis、go-judge;可选 app/judge-worker profile
后端配置server/src/main/resources/application.yamlreadiness 包含 dbredisEnhancementminioStoragegoJudgejudgeQueue;JWT secret 必须来自环境变量
Playwright 配置web/playwright.config.tsAUBB_E2E_REAL_BACKEND=1workers=1、单测超时 90_000,默认 baseURL 为 http://127.0.0.1:3000
E2E 说明web/src/tests/e2e/README.md真实 E2E 连接本地后端 18080、前端 3000 和本地 Docker 依赖;报告中不得记录 token/cookie/JWT/密码值
fixture 密码策略根目录 plan.md Step 2本轮按计划使用本地固定 fixture 用户 U-SA1U-TA1U-ST1,测试密码固定为计划内本地值;不再沿用旧执行记录中“等待外部密码变量”的阻塞判断

Phase 2:当前代码事实扫描

清单命令当前结果
前端业务页面find src/app -name page.tsx | sort45 个 page.tsx,覆盖公共/认证、管理员、教师、学生四类路由
后端 Controllerrg --files src/main/java/com/aubb/server/modules | rg '/api/.*Controller\\.java$'33 个 Controller
前端 API 封装find src/features -path '*/api/*' -type f | sort20 个领域 API 文件
后端测试资产find src/test/java/com/aubb/server -type f -name '*Test*.java' -o -name '*Tests.java'覆盖 auth、权限、课程、作业、提交、判题、成绩、实验、通知、Redis、MinIO、go-judge 等集成/单元测试
前端测试资产find src/tests -type f | sort10 个 E2E spec/fixture/helper 文件,11 个 unit/contract 测试文件
控件源码线索`rg -n "Buttonbutton

下一步

启动真实 Docker 依赖、真实后端 127.0.0.1:18080 和真实前端 127.0.0.1:3000,然后执行 readiness、OpenAPI、权限 fixture 和三角色登录基线。所有敏感值只在命令环境中使用,不写入报告。

Phase 3:真实依赖、后端、前端与登录基线

验证项命令 / 操作当前结果
端口预检lsof -nP -iTCP:18080 -sTCP:LISTENlsof -nP -iTCP:3000 -sTCP:LISTEN启动前均无监听
Docker 依赖启动cd server && docker compose up -d postgres rabbitmq minio redis go-judge && docker compose psPostgreSQL、RabbitMQ、MinIO、Redis 均 healthy,go-judge running
后端第一次启动plan.md 使用 AUBB_BOOTSTRAP_SCHOOL_CODE=S1 启动失败;根因是本地持久化数据库已有唯一学校根节点 SCH-REALRUN,bootstrap 保护逻辑拒绝改为 S1
根因复核查询 org_unitsusers;读取 PlatformBootstrapApplicationServiceorg_units 现有根节点为 SCH-REALRUN;当配置 code 与现有根节点不一致时会抛出 IllegalStateException;没有清库或删除数据
后端第二次启动保持本地 Docker 依赖,改用现有根节点 code SCH-REALRUN 启动 bootstrap成功;Tomcat 监听 18080,Flyway 48 个迁移已验证,bootstrap 完成且未重复创建根节点
后端 readinesscurl -fsS http://127.0.0.1:18080/actuator/health/readinessstatus=UP;components 包含 dbgoJudgejudgeQueueminioStoragereadinessStateredisEnhancement
OpenAPIcurl -fsS http://127.0.0.1:18080/v3/api-docs | python3 -c ...OpenAPI 3.1.0124 个 paths
权限 fixtureBASE_URL=http://127.0.0.1:18080 ADMIN_USERNAME=U-SA1 ADMIN_PASSWORD=... bash scripts/api-tests/permission/run_permission_e2e.sh通过;caseCount=22passedCount=22failedCount=0;报告只记录变量名,不记录密码值
前端启动AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run dev -- --hostname 127.0.0.1 --port 3000Next.js 16.2.4 dev server 就绪,监听 127.0.0.1:3000
登录页探针curl -fsS -o /tmp/aubb-login.html -w '%{http_code} ...' http://127.0.0.1:3000/loginHTTP 200
三角色登录基线AUBB_E2E_REAL_BACKEND=1 ... npx playwright test src/tests/e2e/auth.spec.ts --project=chromiumChromium 5 个真实后端用例全部通过;管理员、教师、学生登录、退出、学生访问管理员页面负例通过

偏差说明:plan.md 指定 bootstrap school code 为 S1,但当前本地 PostgreSQL volume 已有 SCH-REALRUN 学校根节点。为遵守“不覆盖用户未提交变更/不破坏本地数据”,本轮没有清空 volume,而是按当前数据库事实改用既有根节点 code 启动 bootstrap;该偏差将在最终报告中列为本地环境偏差。

Phase 4:全量清单、契约和按钮级 full suites

验证项命令 / 证据当前结果
OpenAPI 路径枚举从当前 http://127.0.0.1:18080/v3/api-docs 保存 /tmp/aubb-openapi.json 后枚举OpenAPI 3.1.0124 paths;覆盖 admin、auth、me、teacher、actuator 路径族
Playwright mock 静态复核`rg -n "page\.route\(context\.route\(
Playwright 测试发现npx playwright test --list --project=chromium10 个 spec 文件,36 个测试标题
管理端 full suiteAUBB_E2E_REAL_BACKEND=1 ... npx playwright test src/tests/e2e/full-admin.spec.ts --project=chromium5 passed;覆盖 auth refresh/revoke、平台配置、组织、用户治理/CSV、学期/模板/开课、审计、权限解释
课程 full suite... npx playwright test src/tests/e2e/full-course.spec.ts --project=chromium5 passed;覆盖教学班、成员、功能开关、资源上传下载、讨论锁定、题库、判题环境
作业/提交/判题 full suite... npx playwright test src/tests/e2e/full-assignment-judge.spec.ts --project=chromium3 passed;覆盖作业生命周期、学生提交、附件下载、workspace、样例运行、正式判题、教师评分和重判
成绩/实验/通知 full suite... npx playwright test src/tests/e2e/full-grading-lab-notification.spec.ts --project=chromium3 passed;覆盖成绩导出/导入/发布、实验附件/评阅、通知已读
导航/权限/响应式 full suite... npx playwright test src/tests/e2e/full-navigation-permission.spec.ts --project=chromium3 passed;覆盖三角色导航、顶栏搜索、用户菜单、路由守卫、401/403、桌面和移动关键操作

当前 full suite 小计:19 passed。下一步运行完整真实后端 Playwright 套件 npm run test:e2e,同时覆盖 smoke specs。

Phase 5:完整门禁与收尾结果

验证项命令 / 证据当前结果
完整真实后端 Playwrightcd web && AUBB_E2E_REAL_BACKEND=1 AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 PLAYWRIGHT_TEST_BASE_URL=http://127.0.0.1:3000 ... npm run test:e2e36 passed,约 2.4 分钟;覆盖 10 个 E2E spec;报告只记录账号变量名,不记录密码值
前端 lintcd web && npm run lint通过,退出码 0
前端 typecheckcd web && npm run typecheck通过,退出码 0
前端 unit/contractcd web && npm run test11 个 test files / 24 tests 全部通过
前端生产构建cd web && AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run build通过;Next.js 编译、TypeScript、30 个静态页面生成均成功
后端 permission realruncd server && scripts/api-tests/permission/run_permission_e2e.shcaseCount=22passedCount=22failedCount=0
后端格式门禁cd server && bash ./mvnw spotless:checkBUILD SUCCESS
后端全量 verifycd server && bash ./mvnw verifyBUILD SUCCESS;Tests run: 318,Failures: 0,Errors: 0,Skipped: 0;总耗时 05:24 min
文档构建cd docs && npm run docs:build通过;VitePress build 完成

本轮真实环境偏差:plan.md 的示例 bootstrap school code 为 S1,但当前本地 PostgreSQL volume 已存在唯一学校根节点 SCH-REALRUN。本轮未清库、未删除 volume,而是按现有根节点 code 启动后端;这是为了避免破坏本地持久化数据。

Phase 6:本地服务清理

项目当前结果
后端 18080spring-boot:run 会话已退出;收尾时 Maven BUILD SUCCESScurl --max-time 2 http://127.0.0.1:18080/actuator/health/readiness 连接失败
前端 3000Next dev server 会话已退出;curl --max-time 2 http://127.0.0.1:3000/login 连接失败
Docker composecd server && docker compose down 已执行;docker compose ps 无运行服务;未删除 volumes

Phase 7:最终工作区复核

复核项当前结果
server/ diff checkgit diff --check 通过;当前 main...origin/main [ahead 1],保留 Dockerfile 中容器 healthcheck 所需的 curl 安装,恢复 BuildKit cache 行
web/ diff checkgit diff --check 通过;当前 main...origin/main [ahead 1],保留 next.config.ts 的本地 dev origin 配置和 src/tests/e2e/full-fixtures.ts 的用户分页查询修正;已恢复不必要删除的 plan.mdtodo.md
docs/ diff checkgit diff --check 通过;当前 main...origin/main [ahead 18],本轮追加三份报告并清理过期收尾记录
文档构建cd docs && npm run docs:build 通过;VitePress build 完成
敏感词文件级复核docs/ 当前 dirty 文件只输出文件名和计数;命中来自 password/token/cookie/JWT 等审计说明和环境变量名,未打印或写入任何真实凭据值

10. 2026-05-17 作业结构化答题闭环补齐

本段记录作业功能专项收敛,目标是补齐教师组卷、学生结构化作答、自动评分、人工评分和真实浏览器 E2E 中缺失的题型与交互证据。

后端变更

项目当前结果
填空题题型新增 FILL_BLANKAssignmentQuestionType,并追加 Flyway 迁移 V49__fill_blank_question_type.sql
组卷校验StructuredQuestionSupport 要求填空题无选项且必须配置 referenceAnswer
自动评分SubmissionAnswerApplicationService 对填空题执行 trim 后精确匹配;大小写保持敏感;命中得满分,否则 0;反馈为“填空题自动判分完成”
API 安全学生侧作业详情不暴露填空题 referenceAnswer
集成测试StructuredAssignmentIntegrationTests#fillBlankQuestionUsesTrimmedExactMatchAutoScoring 覆盖题库创建、发布作业、学生提交、trim 命中、错误答案 0 分和参考答案不可见

前端变更

项目当前结果
学生作业详情从仅依赖作业列表数据改为读取 /api/v1/me/assignments/{assignmentId},按结构化试卷渲染专用控件
专用答题控件单选 radio、多选 checkbox、填空输入框、Markdown 简答编辑/预览、文件题格式/大小/数量提示与上传、编程题跳转在线 IDE
草稿与提交非编程题按题目自动写入 localStorage,刷新后恢复;提交前做必填校验;提交时自动附加当前编程工作区快照
教师组卷PaperEditor 支持选择填空题、配置参考答案,并补齐文件上传题限制配置
Multipart 上传postForm 不再手工固定 multipart Content-Type,改由浏览器/运行时写入 boundary
类型与缓存OpenAPI 生成类型、assignment API、query key 与 hooks 已同步 FILL_BLANK 和学生作业详情接口

真实验证证据

验证项结果
后端专项集成测试StructuredAssignmentIntegrationTests12 passed
前端结构化单元测试src/tests/unit/submission/structured-answer-form.test.tsxstructured-submission.test.tsapi/http-form.contract.test.ts6 个测试通过
前端静态门禁npm run typechecknpm run lint 均通过
前端生产构建AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run build 通过
作业真实后端 E2E已执行完整 npm run test:e2e36 passed;其中 full-assignment-judge.spec.ts 覆盖学生通过真实 UI 完成单选、多选、填空、Markdown 简答、文件题提交、草稿恢复、提交历史、提交详情、附件下载、编程工作区、样例运行、正式判题、教师评分和重判

提交前复核说明

项目当前结果
当前 shell E2E 变量AUBB_E2E_ADMIN_PASSWORDAUBB_E2E_TEACHER_PASSWORDAUBB_E2E_STUDENT_PASSWORDAUBB_E2E_ASSISTANT_PASSWORD 当前未设置
后端专项验证cd server && bash ./mvnw -Dtest=StructuredAssignmentIntegrationTests test 通过;Tests run: 12,Failures: 0,Errors: 0,Skipped: 0
后端全量验证cd server && bash ./mvnw verify 通过;Tests run: 319,Failures: 0,Errors: 0,Skipped: 0;V49 迁移在 Testcontainers schema 中被多次校验
前端静态验证cd web && npm run typecheckcd web && npm run lint 均通过,退出码 0
前端单元验证cd web && npm run test 通过;14 个 test files / 30 tests 全部通过
前端构建验证cd web && AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npm run build 通过;Next.js 编译、TypeScript、30 个静态页面生成均完成
文档验证cd docs && npm run docs:build 通过;git diff --check 通过
当前处理策略当前 shell 未重跑真实浏览器 E2E;真实浏览器 E2E 以本段已有执行证据记录,不伪造重跑结果
残余风险文件上传题的超大文件真实上传、浏览器 file chooser 交互和成绩发布后的所有学生展示路径仍依赖更长时间的真实浏览器扩展套件持续覆盖

11. 2026-05-18 WebIDE 真实全流程验证

本段记录 WebIDE 专项真实闭环执行。目标是使用本地 Docker 依赖、后端 127.0.0.1:18080、前端 127.0.0.1:3000 和真实浏览器完成教师创建/发布编程题作业、学生 WebIDE 编辑保存、历史查看、样例运行和正式提交判题。全程未使用 Playwright route mock 或 MSW;API 仅用于 fixture 准备与后验核验。

环境与数据

项目当前结果
环境加载scripts/aubb-env.sh 已兼容 zsh sourced 场景;zsh -lc 'source scripts/aubb-env.sh && load_aubb_env ...' 可正确解析 /Users/moorefoss/Code/AUBB 并加载 E2E 变量
健康检查just healthcheck-strict 通过;确认 Docker 工具、env/e2e.env、后端 18080、前端 3000、后端 readiness/OpenAPI 与前端登录页可用
fixture 数据使用现有真实后端 helper 创建动态 E2E 学生/教学班/作业;教师端 UI 新增 Python3 判题环境配置;保留本地 E2E-* 残留记录

修复项

问题修复
zsh 下 load_aubb_env 无法加载变量scripts/aubb-env.sh 不再只依赖 BASH_SOURCE[0];zsh sourced 时使用 zsh source path 计算工作区根目录
教师创建结构化编程题作业返回 400创建/编辑页默认不再提交空的 assignment 级 judgeConfig;空白 judgeConfig JSON 解析为 undefined,避免与结构化 paper 冲突
datetime-local 原始值导致后端反序列化失败创建/编辑页统一将 datetime-local 转为 ISO datetime 后提交
PaperEditor 切换编程题会回退为单选题题型切换改为一次性更新 question 对象,并补单元测试覆盖
教师端缺少编程题关键 UI 配置字段PaperEditor 补齐样例试运行、样例输入/期望输出、源码文件约束、Python3、入口文件、测试用例、模板文件等配置
学生 WebIDE 题目内容偶发不显示工作区页面改为读取 /api/v1/me/assignments/{assignmentId} 详情,而不是依赖作业列表摘要
判题环境表格分页导致新增项不可见E2E 仍通过 UI 新增配置,保存后用真实后端列表做后验确认

真实验证证据

验证项结果
WebIDE 真实闭环cd web && source ../scripts/aubb-env.sh && load_aubb_env && AUBB_E2E_REAL_BACKEND=1 PLAYWRIGHT_TEST_BASE_URL=http://127.0.0.1:3000 AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 npx playwright test src/tests/e2e/webide-real-flow.spec.ts --project=chromium --reporter=list 通过;1 passed
作业回归真实 E2E同环境执行 npx playwright test src/tests/e2e/full-assignment-judge.spec.ts --project=chromium --reporter=list 通过;3 passed
前端 lintcd web && npm run lint 通过,退出码 0
前端 typecheckcd web && npm run typecheck 通过,退出码 0
前端目标单测cd web && npm test -- src/tests/unit/assignment/assignment-form.test.ts src/tests/unit/api/mappers.contract.test.ts src/tests/unit/assignment/paper-editor.test.tsx 通过;3 个 test files / 10 tests
后端目标测试cd server && bash ./mvnw -Dtest=JwtTokenServiceTests,AuthApiIntegrationTests test 通过;Tests run: 14,Failures: 0,Errors: 0,Skipped: 0
权限脚本语法cd server && python3 -m py_compile scripts/api-tests/permission/e2e_permission_realrun.py 通过

已验证用户流程

流程证据
教师判题环境配置真实页面填写 profileCode、配置名称、Python3、runCommand 并点击“新增配置”;toast 成功后真实后端列表确认 profile 存在
教师创建并发布编程题作业真实页面选择课程/教学班,填写标题、说明、时间、提交次数,添加编程题、样例、测试用例、模板文件,点击“创建作业”,随后在作业列表点击发布
学生进入 WebIDE真实页面打开 /student/assignments/{assignmentId}/workspace/{questionId},确认题干、文件浏览器、Monaco、状态栏和 main.py
编辑与保存通过页面 Monaco model 更新编辑器内容,点击真实“保存”按钮;后端 workspace 轮询确认 main.py 持久化为新代码
历史与重置点击“历史”并确认历史版本按钮可见;点击“重置”打开弹窗后取消,未破坏最终提交
样例运行在“运行结果”填写样例输入/期望输出,点击“运行自测”;真实 go-judge 返回 SUCCEEDED / ACCEPTED,stdout 为 3
正式提交点击“提交”并确认弹窗;跳转提交详情页,真实 submission 与 judge job 完成,判题结果为 ACCEPTED 且得分等于满分

残余说明

项目当前结果
密钥处理日志仅记录环境变量名和命令形态,未记录任何密码、token 或 cookie 值
本地数据本轮按计划创建 E2E-* 残留业务记录,未清理本地数据库
Monaco 交互真实浏览器中仍通过页面编辑器状态修改代码并点击真实“保存”;为避免 Monaco 隐藏 textarea/逐字符输入抖动,测试使用 Monaco model 一次性设值

12. 2026-05-19 组织架构与课程成员作用域真实验证

本段记录学校层级组织架构专项真实闭环执行。目标是在本地 Docker 依赖、后端 127.0.0.1:18080、前端 127.0.0.1:3000 和真实 Chromium 中,使用前端界面完成学院、学期、课程模板、跨学院开课、教学班和课程成员作用域验证。全程未使用 Playwright route mock 或 MSW;API 仅用于登录、动态测试用户准备和后验断言。

修复项

问题修复
开课管理缺少共同管理学院输入管理端开课表单新增“共同管理学院”多选,并提交 secondaryCollegeUnitIds
开课详情缺少跨学院与成员范围信息开课详情展示主开课学院、共同管理学院和授课团队作用域说明
成员管理只暴露部分角色成员页支持 INSTRUCTOROFFERING_TACLASS_INSTRUCTORTASTUDENT 五类角色
成员角色与教学班绑定规则不清晰整课角色禁用并清空教学班;班级角色和学生必须选择教学班,并在前端显示校验错误
班级下拉缺少唯一编码且长选项会覆盖按钮教学班选项显示“名称 (编码)”,并给成员表单网格和 select 加入收缩约束
列表新增后依赖第一页可见真实 E2E 改为等待 UI POST 成功,再按唯一编码调用真实 API 回查
datetime-local 直接提交导致后端反序列化失败开课创建/编辑提交前将本地 datetime-local 值转换为 ISO datetime
无参数 query key 失效无法刷新活跃列表queryKeys.admin.*() 支持返回列表前缀 key,用于 mutation 后刷新匹配查询

真实验证证据

验证项结果
环境门禁just healthcheck-strict 通过;确认 Docker、本地后端 18080、本地前端 3000、readiness、OpenAPI 与登录页可用
组织架构真实 E2Ecd web && set -a; source ../env/e2e.env; set +a; AUBB_E2E_REAL_BACKEND=1 AUBB_SERVER_ORIGIN=http://127.0.0.1:18080 PLAYWRIGHT_TEST_BASE_URL=http://127.0.0.1:3000 npx playwright test src/tests/e2e/full-organization-structure.spec.ts --project=chromium 通过;1 passed (11.6s)
前端静态门禁cd web && npm run typecheckcd web && npm run lint 均通过
前端目标单测cd web && npm test -- src/tests/unit/admin/course-offering-form.test.ts src/tests/unit/api/mappers.contract.test.ts src/tests/unit/api/query-keys.contract.test.ts src/tests/unit/course/member-role-binding.test.ts 通过;4 个 test files / 13 tests
后端全量回归cd server && bash ./mvnw test 通过;Tests run: 320,Failures: 0,Errors: 0,Skipped: 0
工作区总门禁just verify 通过;后端 320 tests / 0 failures / 0 errors,Web lint/typecheck 通过,Docs build 通过

已验证用户流程

流程证据
管理员创建组织与教学基础数据真实页面创建两个学院、学期、课程模板和跨学院开课;开课详情确认主学院、共同管理学院和初始主讲教师
教师创建教学班初始主讲教师登录真实前端,在课程工作台创建两个教学班
教师成员管理真实成员页添加整课教师、整课助教、班级教师、班级助教和学生,并用后端成员列表确认角色和教学班绑定
前端校验学生未选教学班时前端阻止添加并显示“当前角色必须选择教学班”;整课教师切换后教学班下拉禁用并清空
后端边界真实 API 验证缺班级、整课角色误带班级、跨开课教学班绑定、重复成员均返回明确失败原因;跨开课教学班返回“教学班不属于当前课程”
作用域验证整课教师可管理两个教学班,整课助教可读不可改;班级教师只看见并管理本班,班级助教只读本班,学生不能访问教师班级接口

残余说明

项目当前结果
密钥处理日志仅记录环境变量名和命令形态,未记录任何密码、token、cookie 或 JWT 值
本地数据本轮创建 E2E-* 学院、学期、课程模板、开课、教学班和临时用户;当前无通用删除 API,数据作为审计残留保留

13. 2026-06-08 空 stdin 与 STANDARD_IO 输出比较修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-008BUG-20260607-015 的后端阶段修复。目标是让无输入编程题成为合法教学场景,并让样例运行与正式 STANDARD_IO 判题共用默认宽松输出比较。

行为口径

项目新行为
编程题测试用例 stdinText作业创建和题库题目接口接受空字符串;请求体传入 null 或省略时在服务端归一化为空字符串
样例运行 stdinText自定义样例运行允许 stdinText: "",不会回退到“缺少标准输入”错误
题目样例输入sampleStdinText 可为空字符串,服务端持久化为空输入
STANDARD_IO 输出比较样例运行和正式判题统一规范化 CRLF/LF,忽略行尾空白、末尾空行和最终换行差异
期望输出expectedStdout 仍需提供非空文本,避免无断言的测试点误判通过

修复范围

模块修复
AssignmentTeacherController / QuestionBankTeacherController移除结构化编程题 ProgrammingJudgeCaseRequest.stdinText@NotBlank 拦截,并在 DTO 转换时把 null 归一化为 ""
StructuredQuestionSupport结构化编程题校验不再要求测试用例输入非空,只保留期望输出和分值校验
AssignmentPaperApplicationService / QuestionBankApplicationService持久化配置前归一化 sampleStdinTextjudgeCases[].stdinText,保证数据库 JSON 中无输入题稳定保存为空字符串
ProgrammingSampleRunApplicationService自定义运行通过字段是否出现判断模式,空字符串输入不再被 StringUtils.hasText 当作缺失
JudgeExecutionService移除样例运行空 stdin 拒绝;toCaseOutcome 使用宽松 STANDARD_IO 输出比较

验证证据

验证项结果
RED: 正式判题空 stdin新增 StructuredProgrammingJudgeIntegrationTests#standardIoJudgeAcceptsEmptyStdinAndFinalNewlineDifference 后,修复前失败于 POST /assignments 返回 400 MethodArgumentNotValidException
目标后端回归cd server && bash ./mvnw -Dtest='StructuredAssignmentIntegrationTests#teacherCreatesProgrammingAssignmentWithEmptyJudgeStdin+teacherCreatesQuestionBankProgrammingQuestionWithEmptyJudgeStdin,ProgrammingWorkspaceIntegrationTests#sampleRunAcceptsOnlyFinalNewlineDifference,StructuredProgrammingJudgeIntegrationTests#standardIoJudgeAcceptsEmptyStdinAndFinalNewlineDifference' test 通过;Tests run: 4,Failures: 0,Errors: 0,Skipped: 0
相关集成回归cd server && bash ./mvnw -Dtest='StructuredAssignmentIntegrationTests,ProgrammingWorkspaceIntegrationTests,StructuredProgrammingJudgeIntegrationTests' test 通过;Tests run: 43,Failures: 0,Errors: 0,Skipped: 0

残余说明

项目当前结果
真实浏览器证据本阶段先完成后端 RED-GREEN;仍需在最终业务闭环中用 Playwright MCP 复测作业创建、样例运行和正式提交
UI diff 可读性本次修复了默认判定语义;样例结果差异说明的前端可读性仍属于后续 WebIDE 体验阶段

14. 2026-06-08 作业列表总分摘要修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-009 的后端契约修复。目标是让教师端和学生端作业列表稳定展示结构化作业总分,同时避免列表接口暴露完整题面、答案裁剪细节或隐藏判题配置。

行为口径

项目新行为
教师作业列表GET /api/v1/teacher/course-offerings/{offeringId}/assignments 的结构化作业列表项返回 paper.sectionCountpaper.questionCountpaper.totalScore
学生作业列表GET /api/v1/me/assignments 的结构化作业列表项返回相同 paper 摘要,前端可直接用 paper.totalScore 展示总分
列表载荷边界列表响应的 paper.sections 固定为空数组;完整题面、选项、答案裁剪和编程题判题配置仍只从详情接口读取

修复范围

模块修复
AssignmentPaperApplicationService新增 loadPaperSummary(assignmentId),按结构化试卷节数、题数和节总分计算列表摘要
AssignmentApplicationService教师列表和学生当前作业列表从 paper=null 改为返回试卷摘要
StructuredAssignmentIntegrationTests新增教师端和学生端列表断言,覆盖 paper.totalScorepaper.questionCountpaper.sections=[]

验证证据

验证项结果
RED: 列表缺少总分摘要新增 StructuredAssignmentIntegrationTests#assignmentListsIncludeStructuredPaperScoreSummary 后,修复前失败于教师作业列表 $.items[0].papernull
目标后端回归cd server && bash ./mvnw -Dtest='StructuredAssignmentIntegrationTests#assignmentListsIncludeStructuredPaperScoreSummary' test 通过;Tests run: 1,Failures: 0,Errors: 0,Skipped: 0
相关集成回归cd server && bash ./mvnw -Dtest='AssignmentIntegrationTests,StructuredAssignmentIntegrationTests' test 通过;Tests run: 22,Failures: 0,Errors: 0,Skipped: 0

残余说明

项目当前结果
真实浏览器证据本阶段先完成后端 RED-GREEN 和文档同步;仍需在最终业务闭环中用 Playwright MCP 复测教师端和学生端作业列表不再显示“暂无”

15. 2026-06-08 成员表班级展示与添加结果清理

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-002BUG-20260607-004 的前端阶段复核与补强。目标是让成员表稳定展示教学班信息,并避免“添加成员”弹窗在继续录入下一名成员时保留上一轮成功 / 失败结果。

行为口径

项目新行为
成员表教学班列成员列表使用 className 优先、classCode 兜底展示教学班;无班级绑定时显示“未绑定”
成员写操作权限班级助教只保留本班成员读取视图,不展示添加、导入、停用、转班操作;主讲教师和整课助教保留成员写操作
添加成员结果添加成员弹窗在用户 ID、成员角色或教学班发生变化时调用 mutation reset,清理上一轮“添加成功 / 失败”结果
成员列表刷新添加、导入、状态变更、转班 mutation 成功后继续 invalidate queryKeys.course.members(offeringId),刷新当前成员查询

修复范围

模块修复
CourseMembersPage将添加成员字段变化接入 addMembersMutation.reset(),保留已有成员列表 invalidate 逻辑
AddMemberDialog增加 onFieldChange 回调,并在用户 ID、角色、教学班变化时触发
teacher-members-page.test.tsx增加回归测试,先验证旧添加结果可见,再通过真实 combobox 交互断言字段变化会清理旧结果

验证证据

验证项结果
RED: 修改添加成员输入不清理旧结果新增 CourseMembersPage clears previous add result when editing add member input 后,修复前失败;resetAddMembers 期望调用 3 次、实际 0 次
目标前端单测cd web && npm test -- src/tests/unit/course/teacher-members-page.test.tsx 通过;Test Files: 1 passed,Tests: 6 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 59 passed,Tests: 154 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测成员页添加后继续录入、列表刷新和教学班列展示

16. 2026-06-08 判题环境新增弹窗表单复位

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-007 的前端阶段修复。目标是避免教师端判题环境“新增配置”弹窗二次打开时残留上一条未提交或已提交配置草稿。

行为口径

项目新行为
新增判题环境弹窗每次打开新增弹窗时重置为默认草稿:配置编码、配置名称、语言版本、说明、编译命令、运行命令为空,编程语言为 Python 3
编辑判题环境弹窗每次打开编辑弹窗时仍按当前选中配置重置表单,避免复用上一轮编辑或新增草稿
表单提交载荷本次不改变 SaveJudgeEnvironmentProfileRequest 契约和字段映射,只修正弹窗打开时的表单生命周期

修复范围

模块修复
JudgeEnvironmentProfileDialogreact-hook-formopen=true 时使用当前 profile 对应的默认值执行 form.reset(defaultValues)
teacher-judge-environments-page.test.tsx增加回归测试,填入 C++ 草稿并取消后重新打开新增弹窗,断言所有文本字段清空且语言回到 Python 3

验证证据

验证项结果
RED: 新增弹窗二次打开残留上一条草稿新增 JudgeEnvironmentsPage resets create draft values when reopening the dialog 后,修复前失败于 配置编码 收到 cpp-stale
目标前端单测cd web && npm test -- src/tests/unit/course/teacher-judge-environments-page.test.tsx 通过;Test Files: 1 passed,Tests: 5 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 59 passed,Tests: 155 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测判题环境新增弹窗连续创建 / 取消 / 再打开不残留旧值

17. 2026-06-08 学生端课程状态文案修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-010 的前端阶段修复。目标是避免学生端课程卡片直接展示教师 / 管理侧的“草稿”状态,造成“课程看似未开放但已能访问作业”的理解冲突。

行为口径

项目新行为
学生课程状态 DRAFT学生工作台和“我的课表”不再显示“草稿”,改显示“可学习”,表示该课程已经通过学生可见接口进入学习范围
学生课程状态 PUBLISHED显示“可学习”,与学生可访问课程 / 作业的操作状态一致
教师 / 管理侧状态保留原有 DRAFT -> 草稿 映射,不改变教师创建、编辑、发布开课的内部状态语义
后端契约本次不改变 /api/v1/me/courses 返回的 status 字段,只在学生端视图模型做展示裁剪

修复范围

模块修复
course-view.ts新增 mapStudentCourseStatusTextmapToStudentCourseViewModel,将学生可见状态与教师 / 管理状态分离
student/page.tsx学生工作台课程卡片使用学生态状态文案,不再使用本地教师态 DRAFT -> 草稿 字典
student/courses/page.tsx我的课表使用 mapToStudentCourseViewModel,与工作台保持一致
mappers.contract.test.ts / student-home-page.test.tsx / student-courses-page.test.tsx增加回归测试,覆盖 mapper、学生工作台和我的课表三个入口

验证证据

验证项结果
RED: 学生课程卡片暴露教师侧草稿状态新增回归测试后,修复前失败:mapper 缺少 mapToStudentCourseViewModel,学生工作台卡片收到 草稿 而不是 可学习
目标前端单测cd web && npm test -- src/tests/unit/api/mappers.contract.test.ts src/tests/unit/course/student-home-page.test.tsx src/tests/unit/course/student-courses-page.test.tsx 通过;Test Files: 3 passed,Tests: 12 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 61 passed,Tests: 158 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测学生工作台、我的课表和作业列表的状态理解一致性

18. 2026-06-08 学生课程详情任务入口修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-011 的前端阶段修复。目标是让学生从课程详情页可以直接进入当前课程 / 教学班的作业和实验,而不是只看到公告、资源和讨论。

行为口径

项目新行为
课程详情任务入口课程详情页顶部新增“课程任务”区,提供课程作业、实验项目、课程资源和课程讨论四个稳定入口
课程作业入口若能从 /api/v1/me/courses 找到当前教学班所属开课,则链接到 /student/assignments?offeringId={offeringId}
实验项目入口链接到 /student/labs?classId={classId},直接进入当前教学班实验范围
资源 / 讨论入口使用页内锚点跳转到当前课程详情页的资源和讨论区域
数据契约本次不新增后端聚合接口,复用现有学生课程、作业、实验列表接口和查询参数

修复范围

模块修复
student/courses/[classId]/page.tsx新增课程任务入口区,并通过 useMyCoursesQuery 将当前 classId 解析为 offeringId
student/assignments/page.tsx读取 offeringId 查询参数并传给 useMyAssignmentsQuery
use-my-courses-query.ts我的作业查询 key 纳入查询参数,避免不同课程过滤结果共享缓存
student/labs/page.tsx读取 classId 查询参数,并作为初始教学班实验范围
student-course-detail-page.test.tsx / student-assignments-page.test.tsx / student-labs-page.test.tsx增加回归测试,覆盖课程详情入口、作业过滤参数和实验初始教学班参数

验证证据

验证项结果
RED: 学生课程详情缺少作业 / 实验入口新增回归测试后,修复前失败于找不到“课程任务”标题;作业页查询参数为 undefined;实验页初始查询教学班为默认 111 而非 URL 中的 496
目标前端单测cd web && npm test -- src/tests/unit/course/student-course-detail-page.test.tsx src/tests/unit/assignment/student-assignments-page.test.tsx src/tests/unit/lab/student-labs-page.test.tsx 通过;Test Files: 3 passed,Tests: 5 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 161 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测学生从课程详情进入当前课程作业和当前教学班实验

19. 2026-06-08 文件上传题附件文件名展示修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-012 的前端阶段修复。目标是让文件上传题在学生答题页和提交详情题目区域展示原始文件名、大小与下载入口,避免只显示“附件 #id”或“无文本答案”。

行为口径

项目新行为
学生答题页上传后文件上传题上传成功后,题目卡片立即显示 originalFilename 和文件大小;提交答案仍只提交 artifactIds
学生提交详情文件上传题答案卡片按 answers[].artifactIds 关联 artifacts[],在题目区域展示文件名、大小和下载按钮,不再显示“无文本答案”
教师提交详情教师批改视图同样在文件上传题答案卡片内展示文件名、大小和下载按钮,保持师生视图口径一致
数据契约本次不改变后端请求 / 响应契约;复用上传响应中的 SubmissionArtifactView 和提交详情中的 artifacts[]

修复范围

模块修复
structured-answer-utils.ts / structured-answer-form.tsx答题草稿增加附件元数据;上传和移除附件时同步维护 artifactIdsartifacts
structured-answer-controls.tsx文件上传控件优先显示原始文件名和大小,只有缺少元数据时才兜底显示 附件 #id
submission-answer-artifacts.tsx新增提交详情题目内附件展示组件,按题展示文件名、大小和下载入口
学生 / 教师提交详情页基于提交详情 artifacts[] 建索引,并在答案卡片中渲染文件上传题附件
structured-answer-form.test.tsx / student-submission-detail-page.test.tsx / teacher-submission-detail-page.test.tsx增加回归测试,覆盖上传后显示文件名、学生提交详情按题显示附件、教师提交详情按题显示附件

验证证据

验证项结果
RED: 文件上传题只显示附件编号 / 无文本答案新增回归测试后,修复前失败:上传后找不到 resource-bl-readme.txt;学生提交详情文件题卡片收到 报告附件文件上传10 分无文本答案
目标前端单测cd web && npm test -- src/tests/unit/submission/structured-answer-form.test.tsx src/tests/unit/submission/student-submission-detail-page.test.tsx src/tests/unit/submission/teacher-submission-detail-page.test.tsx 通过;Test Files: 3 passed,Tests: 9 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 163 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测学生真实文件上传、提交后学生 / 教师提交详情均按题显示文件名

20. 2026-06-08 WebIDE 初始入口文件展示修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-013 的前端阶段修复。目标是在后端工作区返回 files=[] 但提供 entryFilePath=main.py 时,文件树与编辑器同时展示入口文件,避免左侧文件浏览器为空。

行为口径

项目新行为
工作区 files 非空文件树继续使用后端返回的文件列表
工作区 files=[] 且有 entryFilePath前端生成一个展示用入口文件,路径为 entryFilePath,内容使用 codeText 或空字符串
编辑器与文件树一致性activeFilePath、打开标签、文件树和状态栏都以同一入口文件为准
保存请求保存时仍通过 mergeWorkspaceFileContent 生成包含当前入口文件内容的 files,不会只停留在展示层

修复范围

模块修复
workspace-files.ts新增 getWorkspaceDisplayFiles(workspace),集中处理入口文件兜底
use-workspace-file-session.ts文件树和状态栏文件计数改用展示文件列表,保持与编辑器入口一致
workspace-files.test.ts / programming-workspace-page.test.tsx增加回归测试,覆盖 helper 和页面文件浏览器在 files=[] 时显示 main.py

验证证据

验证项结果
RED: WebIDE 文件树缺少入口文件新增回归测试后,修复前失败于 getWorkspaceDisplayFiles is not a function;页面层在 files=[] 时无法从文件浏览器找到 main.py
目标前端单测cd web && npm test -- src/tests/unit/submission/workspace-files.test.ts src/tests/unit/submission/programming-workspace-page.test.tsx 通过;Test Files: 2 passed,Tests: 10 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 165 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测学生首次进入 WebIDE 时文件树、标签和编辑器均显示入口文件

21. 2026-06-08 WebIDE Monaco 保存值一致性复核

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-014 的当前树复核。目标是证明 WebIDE 保存、样例运行和“保存并返回”不依赖 React onChange 的异步交付,而是读取 Monaco model 当前值,避免可见代码与保存请求不一致时持久化空代码。

行为口径

项目新行为
Monaco model 变化编辑器 onMount 后订阅 onDidChangeModelContent,直接用 editor.getValue() 同步当前活动文件内容
手动保存点击“保存”时优先读取 editorRef.current.getValue(),并将该值写入 codeText 与当前文件内容
样例运行点击“运行”时同样读取 Monaco 当前值,样例请求的 codeTextfiles[] 使用同一份内容
保存并返回IDE 内“保存并返回”只保存当前编程题工作区,并返回作业详情页,不调用整份作业提交接口

复核范围

模块复核结果
ProgrammingWorkspacePagegetCurrentEditorContent() 优先从 Monaco editor 实例读取当前 model 值;保存、运行和确认保存均传入该值
useProgrammingWorkspaceControllerhandleSave / handleRun / handleSubmit 在传入内容与本地活动文件内容不一致时先同步本地状态,再构造请求 payload
programming-workspace-page.test.tsx已有回归测试覆盖 React onChange 不交付时,保存仍使用编辑器 model 当前值

验证证据

验证项结果
目标前端单测cd web && npm test -- src/tests/unit/submission/programming-workspace-page.test.tsx 通过;Test Files: 1 passed,Tests: 6 passed
关键断言saves the current Monaco model content even when React onChange is not delivered 断言保存 payload 的 codeTextfiles[0].content 均为编辑器当前内容 print(42)\n

残余说明

项目当前结果
真实浏览器证据本阶段完成当前树代码路径和单测复核;仍需在最终业务闭环中用 Playwright MCP 通过真实键盘输入、点击保存并核验请求 payload 与 Monaco model 一致

22. 2026-06-08 WebIDE 单文件题文件创建入口收敛

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-016 的前端阶段修复。目标是在编程题禁用多文件时,不再向学生暴露新建文件 / 新建目录入口,并在 hook 层阻止误触路径产生本地乐观文件,避免后端返回 PROGRAMMING_MULTIPLE_FILES_DISABLED 后 UI 与服务端状态发散。

行为口径

项目新行为
allowMultipleFiles=false 或缺省文件浏览器不展示根目录“创建文件或目录”按钮;目录项菜单也不展示“新建文件 / 新建目录”
allowMultipleFiles=true文件浏览器继续展示创建入口,支持多文件题创建文件和目录
误触保护useWorkspaceFileSessioncanCreateFiles=false 时直接忽略创建文件 / 目录操作,不调用后端 operations,也不写入本地乐观状态
可访问性文件创建和单文件操作图标按钮补充可访问名称,避免真实浏览器审计只能看到匿名按钮

修复范围

模块修复
useProgrammingWorkspaceController根据题目配置 question.config.allowMultipleFiles 计算 canCreateFiles,传入文件会话和页面组件
useWorkspaceFileSession新增 canCreateFiles guard,禁止单文件题通过 handler 创建本地文件或目录
WorkspaceLeftPanel / FileTree / FileTreeItemcanCreateFiles 透传到文件树;单文件题隐藏根目录和目录内创建入口;图标按钮补充 aria-label
programming-workspace-page.test.tsx增加回归测试,覆盖单文件题隐藏创建入口、多文件题保留创建入口

验证证据

验证项结果
RED: 单文件题仍暴露文件创建入口新增回归测试后,修复前失败:无法按可访问名称找到“创建文件或目录”,且角色列表仍出现匿名文件操作按钮
目标前端单测cd web && npm test -- src/tests/unit/submission/programming-workspace-page.test.tsx 通过;Test Files: 1 passed,Tests: 8 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 167 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
后端拒绝后端现有 PROGRAMMING_MULTIPLE_FILES_DISABLED 仍作为最终保护;前端本轮阻断常规 UI 入口和本地乐观状态发散
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测单文件题文件浏览器没有创建入口、多文件题创建入口可用

23. 2026-06-08 WebIDE 历史版本侧栏布局修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-017 的前端阶段修复。目标是让历史版本侧栏在桌面工作区中保持稳定宽度,不被 Monaco 编辑区挤压到约 98px,同时保留可滚动内容和清楚的恢复操作。

行为口径

项目新行为
桌面布局历史版本侧栏使用 shrink-0lg:w-80lg:min-w-80,在主编辑区旁保持稳定宽度
移动 / 窄屏布局侧栏在纵向布局中使用全宽和 max-h-[420px],避免覆盖编辑器或无限撑高页面
可滚动内容版本列表和预览区域继续使用 flex-1 overflow-y-auto,长历史列表可滚动
可访问性侧栏使用 role="complementary"aria-label="历史版本",便于浏览器审计和辅助技术定位

修复范围

模块修复
workspace-revision-sidebar.tsx根容器从普通 div 改为语义化 aside,补稳定宽度、禁止收缩、移动端最大高度和边框方向
programming-workspace-page.test.tsx增加回归测试,覆盖历史侧栏可定位、具备稳定宽度类并仍展示标题

验证证据

验证项结果
RED: 历史侧栏缺少稳定布局 landmark新增回归测试后,修复前失败于无法找到 role="complementary" 且名称为“历史版本”的侧栏
目标前端单测cd web && npm test -- src/tests/unit/submission/programming-workspace-page.test.tsx 通过;Test Files: 1 passed,Tests: 9 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 168 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测学生打开历史版本侧栏时宽度稳定、列表可滚动、恢复按钮可见

24. 2026-06-08 WebIDE 保存本题返回语义复核

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-018 的当前树复核。目标是确认 IDE 内操作不再调用整份结构化作业提交接口,避免只发送编程题答案导致 SUBMISSION_ANSWER_SET_INVALID,并让 UI 文案明确整份作业提交只能回到作业详情页完成。

行为口径

项目新行为
工具栏主按钮原 IDE 内“提交”改为“保存并返回”
确认弹窗标题为“保存本题”,说明“保存当前编程题代码并返回作业详情页;整份作业提交请在作业详情页完成。”
请求语义确认后调用编程工作区保存 mutation,revision message 为“保存本题后返回作业”,不调用 POST /submissions 整份作业提交接口
成功跳转保存成功后返回 /student/assignments/{assignmentId},由作业详情页负责整份作业提交

复核范围

模块复核结果
useProgrammingWorkspaceController当前生产代码不再引入或调用 useCreateSubmissionMutationhandleSubmit 使用 saveMutation.mutate(buildSavePayload(...))
ProgrammingWorkspacePage工具栏 submitLabel="保存并返回";确认弹窗标题、描述和按钮文案均为保存当前题后返回
programming-workspace-page.test.tsx回归测试断言 createSubmissionMutate 未被调用,保存 payload 包含当前代码和“保存本题后返回作业”,成功回调跳转作业详情页

验证证据

验证项结果
目标前端单测cd web && npm test -- src/tests/unit/submission/programming-workspace-page.test.tsx 通过;Test Files: 1 passed,Tests: 9 passed
关键断言saves the current programming question and returns to assignment detail instead of submitting the whole assignment 断言整份作业提交 mutation 未调用,保存成功后跳转 /student/assignments/439

残余说明

项目当前结果
真实浏览器证据本阶段完成当前树代码路径和单测复核;仍需在最终业务闭环中用 Playwright MCP 复测 IDE 内点击“保存并返回”只发工作区保存请求,并返回作业详情页后再完成整份作业提交

25. 2026-06-08 编程判题得分同步复核

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-019 的当前树复核。目标是确认结构化编程题评测完成后,提交详情分题得分、提交总分、待判题计数和成绩册聚合使用同一份 submission_answers 得分状态,不再出现 judge job 已 ACCEPTED 20/20 但提交详情仍为 20/60、编程题显示 0 分的情况。

行为口径

项目新行为
提交详情分题编程题进入 PROGRAMMING_JUDGEDPROGRAMMING_JUDGE_FAILED 终态后,即使人工分未发布,也会展示自动评测写回的 autoScorefinalScore 和反馈
提交总分scoreSummary 在隐藏人工分时仍包含客观题即时分和已完成编程评测自动分
待处理计数编程题评测完成后 pendingProgrammingCount=0;人工题仍保留 pendingManualCount
学生成绩可见性发布前仍隐藏非客观题人工评分和人工反馈;编程自动评测分作为即时反馈可见

复核范围

模块复核结果
SubmissionAnswerApplicationServiceshouldRevealAnswerResult 允许客观题和终态编程评测结果在 revealNonObjectiveScores=false 时进入答案视图和分数摘要
StructuredProgrammingJudgeIntegrationTests混合题集成测试覆盖客观题 20 分、编程题判题完成 20 分、人工题待批改,提交详情刷新后总分为 40 分
SubmissionAnswerApplicationServiceTests单元测试覆盖未发布人工分时终态编程评测结果仍展示,且分数摘要包含客观题 + 编程题自动分

验证证据

验证项结果
后端目标单测cd server && bash ./mvnw -Dtest=SubmissionAnswerApplicationServiceTests test 通过;Tests run: 4,Failures: 0,Errors: 0
后端目标集成测试cd server && bash ./mvnw -Dtest=StructuredProgrammingJudgeIntegrationTests test 通过;Tests run: 13,Failures: 0,Errors: 0
关键断言混合题提交初始 scoreSummary.finalScore=20pendingProgrammingCount=1;判题完成后学生提交详情 answers[1].finalScore=20scoreSummary.autoScoredScore=40scoreSummary.finalScore=40pendingProgrammingCount=0pendingManualCount=1

残余说明

项目当前结果
真实浏览器证据本阶段完成后端当前树单元 / 集成复核;仍需在最终业务闭环中用 Playwright MCP 复测学生正式提交后等待判题完成并刷新提交详情,确认页面显示的题目分和总分同步

26. 2026-06-08 提交详情按题型展示修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-020 的前端阶段修复。目标是让学生和教师 / 助教提交详情按题型展示答案,不再把客观题、文件题和编程题统一退化为“无文本答案”,并让编程题代码以保留换行的代码块展示。

行为口径

项目新行为
单选 / 多选题目卡片展示“已选选项”和选项 key 列表,如 AA, B
文件上传题题目卡片继续展示原始文件名、大小和下载入口;正文区域显示“附件见本题文件列表”,不再显示“无文本答案”
编程题题目卡片展示语言、入口文件和源码代码块;代码块使用 whitespace-pre 保留换行
简答 / 填空继续按文本答案展示,并保留原有换行
视图一致性学生提交详情和教师 / 助教提交详情共用同一个答案正文组件,避免展示口径分叉

修复范围

模块修复
submission-answer-body.tsx新增按题型渲染的答案正文组件,覆盖客观题、文件题、编程题和文本题
学生提交详情页SubmissionAnswerBody 替换统一 `answerText
教师提交详情页同步使用 SubmissionAnswerBody,保留批改和重判控件
student-submission-detail-page.test.tsx / teacher-submission-detail-page.test.tsx增加回归测试,覆盖客观题选项、文件题附件、编程题语言 / 入口文件 / 代码块换行展示

验证证据

验证项结果
RED: 提交详情统一退化为无文本答案新增回归测试后,修复前学生卡片收到 单选题单选题10 分无文本答案;教师卡片收到 单选题单选题无文本答案...,缺少“已选选项”
目标前端单测cd web && npm test -- src/tests/unit/submission/student-submission-detail-page.test.tsx src/tests/unit/submission/teacher-submission-detail-page.test.tsx 通过;Test Files: 2 passed,Tests: 8 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 170 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段先完成前端 TDD 和静态 / 单元 / 构建门禁;仍需在最终业务闭环中用 Playwright MCP 复测学生、教师和整课助教提交详情的客观题、文件题、编程题展示

27. 2026-06-08 整课助教成绩操作权限与 403 反馈修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-021 的当前树补强。目标是确认整课助教在开课范围内不再遇到重判、成绩册导出、批量调分和发布成绩 403,同时保留班级助教不能发布整课成绩的负例,并补齐成绩册导出失败时的可见前端反馈。

行为口径

项目新行为
整课助教权限OFFERING_TA 在本开课范围内可执行教师侧重判、成绩册导出、批量调分、成绩发布 / 重新发布等操作
班级助教边界TA 仍只能在授权教学班内查看 / 批改,不可发布整课成绩,不可越权到整课成绩导出或成员操作
前端错误反馈成绩册导出这类非 mutation 下载请求失败时,页面通过 toast 展示后端错误消息,例如“当前用户无权导出成绩册”
审计成绩册导出记录 GRADE_EXPORT allow / deny;成绩发布同时记录业务发布日志 ASSIGNMENT_GRADES_PUBLISHED 和敏感授权日志 GRADE_PUBLISH allow / deny

修复范围

模块修复 / 补强
GradingIntegrationTests新增接口级回归:整课助教导出成绩册、批量调分、发布成绩均为 2xx;班级助教发布整课成绩为 403;断言导出和发布审计
teacher/grading/gradebook/page.tsxhandleExport 改为 async,捕获 downloadOfferingGradebook 异常并通过 normalizeApiError + toast.error 展示
teacher-gradebook-page.test.tsx新增 RED/GREEN 测试,修复前 toast.error 调用次数为 0;修复后导出失败展示后端错误
authenticated-shell.test.tsx补显式 cleanup(),避免并发全量单测中 React 调度在 jsdom teardown 后触发 window is not defined 未处理异常

验证证据

验证项结果
RED: 导出失败无反馈新增前端测试后,修复前 toast.error 调用次数为 0,复现旧报告中导出 403 无 UI 反馈残口
前端目标单测cd web && npm test -- src/tests/unit/grading/teacher-gradebook-page.test.tsx 通过;Test Files: 1 passed,Tests: 4 passed
后端目标集成测试cd server && bash ./mvnw -Dtest=GradingIntegrationTests#offeringTaCanExportBatchAdjustAndPublishGradesWhileClassTaCannotPublishOfferingGrades test 通过;Tests run: 1,Failures: 0,Errors: 0
后端权限组合回归cd server && bash ./mvnw -Dtest=CourseSystemIntegrationTests#offeringTaShouldHaveTeacherSidePermissionsAndClassTaShouldStayScoped,JudgeIntegrationTests#offeringTaCanRequeueJudgeWithTeacherSidePermissionsAndAllowedAuditShouldBeRecorded,GradingIntegrationTests#offeringTaCanExportBatchAdjustAndPublishGradesWhileClassTaCannotPublishOfferingGrades test 通过;Tests run: 3,Failures: 0,Errors: 0
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 171 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段完成接口级和前端单元 / 构建证据;仍需在最终业务闭环中用 Playwright MCP 以整课助教身份实际点击提交详情重判、成绩册导出、批量调分、发布 / 重新发布,并确认页面反馈

28. 2026-06-08 开课主讲教师候选加载失败修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-001 的当前树修复与复核。目标是解除管理员新增开课时 GET /api/v1/admin/users?identityType=TEACHER&pageSize=100 因教师用户缺少 primaryOrgUnitId 返回 500 的阻塞,并让前端在教师候选接口失败时显示可重试失败态,而不是伪装成“暂无选项”。

行为口径

项目新行为
后端教师候选查询教师用户存在教师身份但 primaryOrgUnitId=null 时,管理员用户列表查询不再触发 NPE;教师候选仍可返回给开课创建页
前端失败态主讲教师候选接口失败时,新增开课弹窗展示“主讲教师候选加载失败”并提供“重试加载主讲教师候选”按钮
前端空态接口成功但候选为空时展示“暂无教师候选”,不再使用会掩盖接口失败的通用“暂无可选项”

修复范围

模块修复 / 补强
UserAdministrationApplicationService已在当前树兼容 primaryOrgUnitId=null 的教师用户候选查询,避免构建组织信息时触发 NPE
PlatformGovernanceApiIntegrationTests覆盖无主组织教师候选查询返回 200 的回归场景
admin/course-offerings/page.tsxuseUsersQueryisLoadingerrorrefetch 传入新增开课弹窗
course-offering-create-dialog.tsx主讲教师候选接口失败时展示 role="alert" 的可见失败态与重试按钮;加载与空候选使用专用文案
course-offerings-page.test.tsx增加失败态回归测试,断言旧的“暂无可选项”不再出现,点击重试会调用 refetch

验证证据

验证项结果
RED: 教师候选接口失败被显示为空态新增前端测试后,修复前无法找到“主讲教师候选加载失败”,弹窗仍退化为通用空态
后端目标集成测试cd server && bash ./mvnw -Dtest=PlatformGovernanceApiIntegrationTests#listsTeacherCandidatesWhenPrimaryOrganizationIsMissing test 通过;Tests run: 1,Failures: 0,Errors: 0
前端目标单测cd web && npm test -- src/tests/unit/admin/course-offerings-page.test.tsx 通过;Test Files: 1 passed,Tests: 5 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端全量单测cd web && npm test 通过;Test Files: 62 passed,Tests: 172 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 30/30 静态页面生成完成

残余说明

项目当前结果
真实浏览器证据本阶段完成后端集成测试和前端单元 / 构建证据;仍需在最终业务闭环中用 Playwright MCP 以管理员身份打开新增开课弹窗,确认教师候选可加载,且通过拦截或后端错误场景确认失败态可见并可重试

29. 2026-06-08 班级助教课程聚合与写入口收敛复核

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-003 的当前树补强。目标是确认班级助教不会在 /api/v1/me/courses 中被升格为整课助教,授权教学班不会丢失,并且教师端工作台 / 成员页不会向班级助教展示整课写操作。

行为口径

项目新行为
课程聚合角色班级助教成员只返回 roles:["TA"],不会被映射为 OFFERING_TA
班级范围/api/v1/me/coursesclasses 保留授权教学班 ID、编码、名称和该班角色
缓存边界助教先登录形成空课程缓存后,被教师加入班级再重新登录,成员添加会驱逐 myCoursesSummary 缓存并返回最新班级范围
后端权限班级助教只能读取授权教学班成员和班级,不能修改班级功能,不能添加成员
前端入口班级助教课程工作台隐藏新增教学班入口并禁用班级功能开关;成员页隐藏添加、导入、停用、转班等写操作

补强范围

模块修复 / 补强
CourseSystemIntegrationTests新增 classTaCourseSummaryKeepsClassScopedRoleAfterMembershipCacheEviction,覆盖空缓存 -> 加入班级助教 -> 重新登录后 /me/courses 返回 TA 和授权班级
CourseSystemIntegrationTests 既有回归复跑混合学生 / 班级助教身份、班级助教只读授权教学班、无班级过滤成员列表仅返回本班成员等场景
teacher-course-dashboard-page.test.tsx复跑班级助教隐藏新增教学班、禁用班级功能写开关的前端回归
teacher-members-page.test.tsx复跑班级助教成员页只读提示和写操作隐藏的前端回归

验证证据

验证项结果
后端新增目标测试cd server && bash ./mvnw -Dtest=CourseSystemIntegrationTests#classTaCourseSummaryKeepsClassScopedRoleAfterMembershipCacheEviction test 通过;Tests run: 1,Failures: 0,Errors: 0
后端权限组合回归cd server && bash ./mvnw -Dtest=CourseSystemIntegrationTests#taCanBeStudentInOneClassAndTaInAnotherButPermissionIsLimited,CourseSystemIntegrationTests#classTaCanReadOnlyOwnedTeachingClasses,CourseSystemIntegrationTests#classTaMemberListingWithoutClassFilterReturnsOnlyOwnedClassMembers,CourseSystemIntegrationTests#classTaCourseSummaryKeepsClassScopedRoleAfterMembershipCacheEviction test 通过;Tests run: 4,Failures: 0,Errors: 0
后端全量测试cd server && bash ./mvnw test 通过;Tests run: 333,Failures: 0,Errors: 0,Skipped: 0
前端目标单测cd web && npm test -- src/tests/unit/course/teacher-course-dashboard-page.test.tsx src/tests/unit/course/teacher-members-page.test.tsx 通过;Test Files: 2 passed,Tests: 11 passed
文档构建cd docs && npm run docs:build 通过;存在既有 VitePress chunk-size warning

残余说明

项目当前结果
真实浏览器证据本阶段完成接口级和前端单元证据;仍需在最终业务闭环中用 Playwright MCP 以班级助教身份真实登录,确认课程卡角色为班级助教、教学班数量正确、工作台无新增教学班入口、成员页无写操作按钮

30. 2026-06-08 缺档案课程账号登录引导修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-005 的当前树修复。目标是让已拥有课程角色绑定但缺少教师 / 学生学术档案的账号,登录后进入明确的档案补齐引导,而不是停留在根路径跳转或退化为泛化 Unauthorized。

行为口径

项目新行为
登录用户快照POST /api/v1/auth/loginGET /api/v1/auth/meAuthenticatedUserView 返回 groupBindings,包含 sourcetemplateCodescopeTypescopeRefId,供前端识别 role binding 来源的课程身份
JWT 边界access token 仍保持紧凑,不在 JWT 中携带完整 groupBindings 或完整权限点明细
默认落点academicProfile 且拥有教师 / 助教 / 学生课程身份的账号,默认首页为 /profile-required
前端引导/profile-required 显示“需要补全学术档案”,说明当前账号已有课程身份但缺少教师或学生档案,并提示联系管理员补全
退出路径引导页提供“退出并重新登录”,先撤销 / 清理当前会话,再跳转 /login,避免保留 cookie 后从登录页再次回到引导页

修复范围

模块修复 / 补强
AuthenticatedUserView新增 groupBindings 字段,来源于 AuthenticatedUserPrincipal.getGroupBindings()
CourseSystemIntegrationTests新增 loginResponseExposesCourseRoleBindingsForProfileCompletionGuidance,覆盖课程成员 role binding 用户缺档案登录响应包含 offering-ta binding
role-codes.tsgroupBindings.templateCode 派生前端角色码,并判断缺档案课程身份是否需要补齐引导
login-redirect.ts、session store / cookies统一使用 getAuthenticatedUserHomePath,确保登录后、刷新后和根路径跳转都落到 /profile-required
RoleGuard / AuthenticatedShell手动访问教师 / 学生 / 共享认证页时优先导向缺档案引导,不再展示泛化权限不足
profile-required 页面新增缺档案说明页和退出重登操作
real-backend.tsAPI 注入登录态的 E2E helper 同步识别缺档案课程身份,写入 /profile-required home path

验证证据

验证项结果
RED: 前端缺少缺档案落点函数 / 页面新增 auth 单测后,修复前失败于 needsAcademicProfileCompletion is not a function 和无法解析 /profile-required/page
RED: 后端登录快照缺少 role binding 明细新增后端测试后,修复前失败于 No value at JSON path "$.user.groupBindings[0].templateCode"
后端目标集成测试cd server && bash ./mvnw -Dtest=CourseSystemIntegrationTests#loginResponseExposesCourseRoleBindingsForProfileCompletionGuidance test 通过;Tests run: 1,Failures: 0,Errors: 0
后端角色绑定组合回归cd server && bash ./mvnw -Dtest=CourseSystemIntegrationTests#loginAfterOfferingCreationShouldUseInstructorRoleBindingSnapshot,CourseSystemIntegrationTests#offeringCreationShouldInvalidateExistingInstructorSessions,CourseSystemIntegrationTests#loginAfterClassMemberAddedShouldUseStudentRoleBindingSnapshot,CourseSystemIntegrationTests#loginResponseExposesCourseRoleBindingsForProfileCompletionGuidance test 通过;Tests run: 4,Failures: 0,Errors: 0
前端 auth 单测cd web && npm test -- src/tests/unit/auth 通过;Test Files: 5 passed,Tests: 16 passed
前端静态门禁cd web && npm run lintcd web && npm run typecheck 均通过
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 31/31 静态页面生成完成,包含 /profile-required

残余说明

项目当前结果
真实浏览器证据本阶段完成 API 级、单元和构建证据;仍需在最终业务闭环中用 Playwright MCP 创建或复用缺档案课程账号真实登录,确认落到 /profile-required,并在补齐档案后进入教师 / 学生工作区

31. 2026-06-08 题库题型专属字段补齐

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-006 的前端阶段修复。目标是让教师在题库新增 / 编辑弹窗中能够配置后端必需的题型专属字段,并能在弹窗内看到后端校验错误,而不是只提交基础字段后收到无处展示的 400。

行为口径

项目新行为
单选 / 多选题库弹窗展示选项与正确答案配置;提交时写入 options[].optionKey/content/correct
填空 / 简答填空题展示必填参考答案;简答题展示可选参考答案 / 评分说明;提交时写入 config.referenceAnswer
文件上传弹窗展示文件数量上限、文件大小上限和允许文件类型;提交时写入 config.maxFileCount/maxFileSizeMb/acceptedExtensions
编程题弹窗展示支持语言、入口文件、模板代码、样例输入输出、隐藏测试输入输出和隐藏测试分值;提交时写入 config.supportedLanguages/templateEntryFilePath/templateFiles/judgeCases
后端错误create / update mutation 的错误消息会在题库弹窗内以错误区展示,并保留草稿

修复范围

模块修复 / 补强
question-bank-question-dialog.tsx接入题型专属字段、前端校验和 options/config payload 构造,并展示 mutation 错误
question-bank-type-fields.tsx 等组件拆分选择题、文件题、编程题、参考答案字段,避免题库 Dialog 文件继续膨胀
question-bank-type-fields.ts集中维护题型草稿初始化、编辑回填、payload 构造和字段级错误
question-bank-page.test.tsx新增单选、文件、编程题 payload 回归,以及弹窗内展示后端校验错误回归
docs/05-api/assignments-api.md补充题库创建 / 更新请求中不同题型的专属字段要求

验证证据

验证项结果
RED: 弹窗缺少题型专属字段和错误区新增题库页单测后,修复前失败于无法找到 选项 A文件数量上限入口文件role="alert"
前端目标单测cd web && npm test -- src/tests/unit/assignment/question-bank-page.test.tsx 通过;Test Files: 1 passed,Tests: 6 passed
前端 assignment 单测cd web && npm test -- src/tests/unit/assignment 通过;Test Files: 8 passed,Tests: 22 passed
前端静态门禁cd web && npm run typecheckcd web && npm run lint 均通过

残余说明

项目当前结果
真实浏览器证据本阶段完成前端单元和静态门禁;仍需在最终业务闭环中用 Playwright MCP 在教师题库页真实创建单选、文件上传和编程题,确认弹窗字段、后端请求和错误反馈均可用

32. 2026-06-08 成绩发布状态与重新发布语义修复

本段记录 business-loop-playwright-mcp-report-2026-06-07.mdBUG-20260607-022 的当前树修复。目标是让教师侧成绩册在成绩发布后展示明确的已发布状态、发布时间、发布人和学生可见范围,并把后续操作切换为“重新发布成绩”语义。

行为口径

项目新行为
发布状态选中已发布作业后,成绩发布与调分面板展示“已发布”、发布时间、发布人 ID
学生可见范围面板根据统计报告展示 学生可见 已发布人数/适用人数
重新发布已发布作业按钮文案改为“重新发布成绩”;确认弹窗说明学生会看到最新成绩和反馈,并写入重新发布审计
后端契约教师侧成绩册 assignmentColumns[] 增加 gradePublishedAtgradePublishedByUserId,与已有 gradePublished 一起提供刷新后的发布元数据

修复范围

模块修复 / 补强
GradebookPageView.AssignmentColumnView增加成绩发布时间和发布人字段
GradebookQuerySql / GradebookQueryRepository / GradebookApplicationServiceassignments.grade_published_at / grade_published_by_user_id 回填作业列发布元数据
teacher/grading/gradebook/page.tsx按选中作业合并成绩册列、统计报告和刚发布 mutation 结果,派生当前发布状态
gradebook-actions-panel.tsx展示已发布状态卡、学生可见范围和重新发布按钮
teacher-gradebook-page.test.tsx增加发布后 UI 语义回归
docs/05-api/grading-api.md / server/docs/product-specs/grading-system.md / server/docs/stable-api.md同步成绩发布、重新发布和成绩册发布元数据契约

验证证据

验证项结果
RED: 发布后 UI 仍缺少状态和重新发布语义新增前端测试后,修复前失败于找不到“已发布”
前端目标单测cd web && npm test -- src/tests/unit/grading/teacher-gradebook-page.test.tsx 通过;Test Files: 1 passed,Tests: 5 passed
后端目标集成测试cd server && bash ./mvnw -Dtest=GradebookIntegrationTests#teacherReadsOfferingGradebookUsingLatestSubmissionAndApplicableAssignments test 通过;Tests run: 1,Failures: 0,Errors: 0
后端重新发布审计回归cd server && bash ./mvnw -Dtest=GradingIntegrationTests#repeatedPublishingCreatesNewSnapshotBatchWithoutResettingInitialPublication test 通过;Tests run: 1,Failures: 0,Errors: 0
后端全量测试cd server && bash ./mvnw test 通过;Tests run: 334,Failures: 0,Errors: 0,Skipped: 0
前端 grading 单测cd web && npm test -- src/tests/unit/grading 通过;Test Files: 1 passed,Tests: 5 passed
前端静态门禁cd web && npm run typecheckcd web && npm run lint 均通过
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 31/31 静态页面生成完成
文档构建cd docs && npm run docs:build 通过;存在既有 VitePress chunk-size warning
Diff 检查git diff --checkserver/web/docs/ 均通过

残余说明

项目当前结果
后端重新发布审计GradingIntegrationTests#repeatedPublishingCreatesNewSnapshotBatchWithoutResettingInitialPublication 已覆盖 initialPublication=false、审计条数和 metrics
真实浏览器证据本阶段完成 API / 单元 / 静态证据;仍需在最终业务闭环中用 Playwright MCP 以教师或整课助教身份实际发布和重新发布成绩,确认页面显示“已发布”“重新发布成绩”和学生侧可见结果

33. 2026-06-08 教学内容 Markdown 安全渲染修复

本段记录 goal.md 中“作业、题目、实验、公告、资源说明等说明性内容必须支持 Markdown”的前端阶段修复。目标是在不改变后端原文保存契约的前提下,让主要教学内容阅读区使用统一安全 Markdown 渲染,并移除旧的手写 HTML 预览路径。

行为口径

项目新行为
原文保存后端继续保存作业题干、公告、讨论、实验说明和实验报告正文的原始 Markdown,不返回预渲染 HTML
安全渲染前端统一使用 MarkdownContent;支持标题、列表、引用、表格、代码块、行内代码和链接;默认忽略原始 HTML 并限制链接协议
作业 / 题目学生答题页题干、简答预览、提交详情文本答案和 WebIDE 题面改用安全 Markdown
课程内容教师 / 学生公告正文、教师 / 学生讨论帖子正文改用安全 Markdown
实验内容教师 / 学生实验说明和教师端实验报告正文改用安全 Markdown

修复范围

模块修复 / 补强
shared/ui/markdown-content.tsx新增统一 Markdown/GFM 渲染组件,禁用原始 HTML,限制链接协议并统一表格 / 代码块滚动样式
structured-answer-controls.tsx / structured-answer-utils.ts简答预览从手写 HTML 字符串替换为安全 Markdown 组件
submission-answer-body.tsx / workspace-left-panel.tsx提交详情文本答案和 WebIDE 题面改为 Markdown 阅读区
公告、讨论、实验页面组件公告正文、讨论帖子、实验说明和实验报告正文改为 Markdown 阅读区
package.json / package-lock.json新增 react-markdownremark-gfm

验证证据

验证项结果
RED: 作业题干 / 简答预览仍是纯文本或手写简化预览新增结构化答题测试后,修复前失败于找不到 Markdown 标题“背景”
RED: 公告正文仍是纯文本新增公告测试后,修复前正文只显示 Markdown 源文本,无法找到表格 / 链接
作业 Markdown 单测cd web && npm test -- src/tests/unit/submission/structured-answer-form.test.tsx 通过;Test Files: 1 passed,Tests: 3 passed
公告 Markdown 单测cd web && npm test -- src/tests/unit/course/teacher-announcements-page.test.tsx 通过;Test Files: 1 passed,Tests: 3 passed
讨论 / 实验 Markdown 单测cd web && npm test -- src/tests/unit/course/teacher-discussion-detail-page.test.tsx src/tests/unit/course/student-discussion-detail-page.test.tsx src/tests/unit/lab/teacher-labs-page.test.tsx 通过;Test Files: 3 passed,Tests: 9 passed
Markdown 安全链接单测cd web && npm test -- src/tests/unit/shared/markdown-content.test.tsx ... 通过;Test Files: 6 passed,Tests: 16 passed
前端类型检查cd web && npm run typecheck 通过
前端静态门禁cd web && npm run lint 通过
前端完整单测cd web && npm test 通过;Test Files: 64 passed,Tests: 189 passed
前端生产构建cd web && npm run build 通过;Next.js 编译、TypeScript 和 31/31 静态页面生成完成
文档构建cd docs && npm run docs:build 通过;存在既有 VitePress chunk-size warning
Diff 检查git diff --checkserver/web/docs/ 均通过

残余说明

项目当前结果
资源说明第 35 段已补齐课程资源 description 字段、上传 / 编辑入口和师生列表安全 Markdown 渲染;本段原“无独立资源说明字段”风险已关闭
真实浏览器证据公告、讨论、实验、作业题干已在第 34 段完成 Playwright MCP 证据;课程资源说明的真实浏览器证据补充见第 35 段

34. 2026-06-08 真实 Playwright MCP 回归证据补充

本段记录在本地真实前后端 http://127.0.0.1:3000 / http://127.0.0.1:18080 上,通过 Playwright MCP 页面操作补齐的最终浏览器证据。本轮使用 just seed-fixtures 刷新权限 / E2E 基线数据后,以 U-TA1 教师账号、临时学生 mcp-mq4ajfav-student 和管理员 U-SA1 做页面与接口后验。

环境与数据

项目结果
环境健康检查just healthcheck 通过;后端 readiness、OpenAPI、前端登录页均可访问
Fixture 刷新just seed-fixtures 通过;caseCount=22passedCount=22failedCount=0
本轮开课offeringId=2,课程 数据结构 2025 秋,A1 教学班 classA1Id=2
本轮数据前缀MCP-mq4ajfav
本轮对象公告 3,讨论 3,实验 3,Markdown 作业 11,成绩发布作业 12,成绩提交 8,答案 11
截图目录docs/06-testing-and-ops/artifacts/business-loop-2026-06-08/,该目录为本地忽略证据包,不提交到 git

Playwright MCP 页面证据

验证项页面 / 证据结果
公告 Markdown 安全渲染/teacher/courses/2/announcementsbusiness-loop-2026-06-08-markdown-announcement.png页面显示 MCP-mq4ajfav Markdown公告;DOM 中存在 Markdown 标题、列表、GFM 表格和 https://example.com 安全链接;javascript: 链接未保留,script 标签数为 0window.__AUBB_MARKDOWN_XSS=false
讨论 Markdown 安全渲染/teacher/courses/2/discussions/3business-loop-2026-06-08-markdown-discussion.png讨论详情显示 Markdown 标题、列表、表格和安全链接;原始 <script> 不可见且未执行
学生实验说明 Markdown/student/labs?classId=2business-loop-2026-06-08-markdown-student-lab.png临时学生可见 MCP-mq4ajfav Markdown实验;实验说明渲染出标题、列表、表格和安全链接;危险链接与脚本被清理
学生作业题干 Markdown/student/assignments/11business-loop-2026-06-08-markdown-student-assignment.png作业 MCP-mq4ajfav Markdown作业 中题干渲染 Markdown 标题、列表、表格和安全链接;危险链接数为 0,脚本未进入 DOM
教师首次发布成绩/teacher/grading/gradebook?offeringId=2&assignmentId=12business-loop-2026-06-08-gradebook-published.png页面真实点击“发布成绩”并确认后,成绩册面板显示“已发布”、发布时间、发布人 ID、学生可见范围,按钮切换为“重新发布成绩”;成绩册接口中作业列 gradePublished=true
教师重新发布成绩同一成绩册页面页面真实点击“重新发布成绩”,确认弹窗文案说明“学生将看到最新成绩和反馈”且“写入重新发布审计”;确认后面板继续显示重新发布语义
学生成绩可见/student/grades?offeringId=2business-loop-2026-06-08-student-grade-visible.png临时学生选择 数据结构 2025 秋 后,作业行 MCP-mq4ajfav 成绩发布回归 显示 8 / 10 和“已发布”,导出成绩按钮可用

接口后验

验证项结果
成绩册发布元数据教师侧 /api/v1/teacher/course-offerings/2/gradebookassignmentColumns[] 对作业 12 返回 gradePublished=true
学生侧成绩临时学生 /api/v1/me/course-offerings/2/gradebook 能找到作业 12,且 gradePublished=true
重新发布审计管理员 /api/v1/admin/audit-logs?page=1&pageSize=50 中同一作业可见两条 GRADE_PUBLISH 审计,metadata 的 initialPublication 分别为 truefalse

残余说明

项目当前结果
资源说明第 35 段已补齐课程资源说明 Markdown 字段和真实浏览器验证入口,本轮原剩余风险已关闭
截图包Playwright MCP 截图已保存到本地 ignored artifacts 目录;正式 git 提交仅包含本执行日志

35. 2026-06-08 课程资源说明 Markdown 修复

本段记录对第 33、34 段遗留的“资源说明无独立字段”缺口的补齐。新口径为:课程资源说明作为可选原始 Markdown 保存到后端,上传和编辑资源时均可维护;教师和学生资源列表读取同一 description 字段,并通过统一安全 Markdown 组件渲染。

行为口径

项目新行为
后端存储course_resources.description 保存原始 Markdown;空白说明归一化为 null,非空最长 5000 字
上传资源POST /api/v1/teacher/course-offerings/{offeringId}/resources multipart 接受可选 description
更新资源PUT /api/v1/teacher/course-resources/{resourceId} 接受 title 和可选 description,用于同时维护资源标题与说明
资源列表教师侧和学生侧 CourseResourceView 返回可选 description,不返回预渲染 HTML
前端录入教师上传资源、编辑资源弹窗新增“资源说明” textarea,支持输入 Markdown
前端展示教师资源表格和学生课程资源列表在标题 / 文件名下方渲染资源说明;危险链接不可点击,原始 HTML 不执行

修复范围

仓库文件 / 范围
serverCourseResourceApplicationServiceCourseResourceTeacherControllerCourseResourceViewCourseResourceEntityV51__course_resource_description_markdown.sqlCourseResourceIntegrationTests
web课程资源 API 类型、资源上传 / 编辑表单、教师资源页、学生课程详情资源列表、资源页单测
docsserver/docs/stable-api.mdserver/docs/product-specs/course-system.mdserver/docs/generated/db-schema.mddocs/05-api/courses-api.mddocs/04-development/database.mddocs/04-development/frontend-design.md

验证证据

验证项结果
RED: 后端资源视图无 description新增 CourseResourceIntegrationTests#resourceDescriptionIsStoredAndReturnedAsRawMarkdown 后,修复前失败于 No value at JSON path "$.description"
后端定向用例cd server && bash ./mvnw -Dtest=CourseResourceIntegrationTests#resourceDescriptionIsStoredAndReturnedAsRawMarkdown test 通过;Tests run: 1
后端资源集成用例cd server && bash ./mvnw -Dtest=CourseResourceIntegrationTests test 通过;Tests run: 3
RED: 前端资源说明缺失新增教师 / 学生资源说明单测后,修复前分别失败于找不到 Markdown 标题“使用说明”“下载说明”和 label“资源说明”
前端定向用例cd web && npm test -- src/tests/unit/course/teacher-resources-page.test.tsx src/tests/unit/course/student-course-detail-page.test.tsx 通过;Test Files: 2 passed,Tests: 7 passed
前端类型检查cd web && npm run typecheck 通过
前端静态门禁cd web && npm run lint 通过
前端完整单元cd web && npm test 通过;Test Files: 64 passed,Tests: 192 passed
前端构建cd web && npm run build 通过;Next.js 生产构建完成,生成 31 个静态页面
文档构建cd docs && npm run docs:build 通过;VitePress build complete,存在既有 chunk size warning
运行时契约重启本地真实后端后抽样 /v3/api-docsCourseResourceViewUpdateResourceRequest 和上传参数均包含 description
Fixture 刷新just seed-fixtures 通过;caseCount=22passedCount=22
Playwright MCP 教师侧http://127.0.0.1:3000/teacher/courses/2/resources 以教师会话验证资源 E2E-MCP-资源说明-mq4e4g2g:初始说明可见;编辑弹窗保存后,资源表格渲染 Markdown heading、列表、表格;javascript: 链接没有生成可点击 anchor
Playwright MCP 学生侧http://127.0.0.1:3000/student/courses/10 以学生会话验证同一资源:课程资源区显示相同 heading、列表和表格;危险链接文本可见但没有可点击 anchor
验证资源清理本轮浏览器验证资源 id=3 已通过教师资源删除 API 清理,返回 204

后续验证

项目状态
Playwright MCP已补齐课程资源说明真实浏览器证据;教师侧上传 / 编辑入口和师生列表安全 Markdown 渲染缺口关闭
just verify通过;后端 bash ./mvnw test:Tests run 335,Failures 0,Errors 0,Skipped 0;前端 lint / typecheck 通过;文档构建通过,存在既有 VitePress chunk size warning
just verify-full通过;后端 bash ./mvnw verify:Tests run 335,Failures 0,Errors 0,Skipped 0BUILD SUCCESS;前端 lint / typecheck / unit / build 通过,unit 为 Test Files 64 passed、Tests 192 passed,Next.js 构建生成 31 个静态页面;文档构建通过,存在既有 VitePress chunk size warning
分仓库提交serverb045ea3 fix(course): 支持资源说明Markdownweb7095034 fix(course): 安全渲染资源说明docs:本段文档证据随本轮提交