多用户局域网考试练习平台,支持题库管理、考试模式、练习模式、背题模式、错题本、排行榜、个人看板、学情分析、知识补充和站内通知。
| 层 | 技术 |
|---|---|
| 前端 | Vue 3 + TypeScript + Pinia + Vue Router + Axios + Element Plus |
| 后端 | Python FastAPI + SQLAlchemy + Pydantic |
| 数据库 | PostgreSQL |
| 部署 | Docker Compose (Nginx + Uvicorn + PostgreSQL) |
| 包管理 | 前端 pnpm / 后端 uv |
copy .env.example .env
# 编辑 .env,填入随机的数据库密码、SECRET_KEY 和管理员初始密码
docker compose up -d --build
# 访问 http://localhost:4074
# 同局域网设备访问 http://<本机IP>:4074管理员账号固定为 admin,初始密码使用 .env 中的 ADMIN_PASSWORD;首次登录会强制修改密码。
JWT access/refresh token 带有服务端撤销版本;修改或重置密码后旧 token 会立即失效,强制改密用户只能先完成密码修改。
# 后端
cd backend
uv sync
uv run uvicorn app.main:app --reload --port 8000
# 前端(新终端)
cd frontend
pnpm install
pnpm run dev
# 访问 http://127.0.0.1:5173统计事实表迁移 026/027(含题干快照)的 STATISTICS_FACT_WRITES / STATISTICS_FACT_READS 默认关闭。
启用读取前请在隔离 PostgreSQL 数据库运行 scripts/backfill_answer_facts.py 并完成
新旧聚合对账;回滚顺序见 docs/DEVELOPMENT.md。
手动启动需要 PostgreSQL,并需在
.env或进程环境中提供SECRET_KEY、ADMIN_PASSWORD和DATABASE_URL。也可docker compose up -d db仅启动数据库容器。后端启动时会自动执行迁移并种子管理员。
# 1. 构建镜像 + 导出 tar + 打包源码(要求工作树干净;-NoBuild 会核验
# 现有镜像内烧录的 commit 必须等于 HEAD,否则拒绝导出陈旧镜像)
.\scripts\export-images.ps1
# 2. 包级演练(消费真实 migration_package:manifest/SHA-256 → load →
# image ID/baked commit 断言 → 临时 Compose 运行 release_check)
.\scripts\verify_release_package.ps1
# 3. 备份数据库
.\scripts\backup.ps1WSL/Linux 侧有完全等价的两条 POSIX 车道(同一 Schema v1,同一校验器),不必切到 Windows 打包:
# 1. 出包(默认拒绝脏工作树;--no-build 核验两个镜像的 baked commit 等于 HEAD)
./scripts/export-images.sh
# 2. 包级演练(先停栈,untag 后从交付 tar 重新 load;down 不带 -v,保留 pgdata 卷)
docker compose down
./scripts/verify_release_package.sh
# 3. 备份数据库
./scripts/backup.sh产物在 migration_package/ + 一个 .sql 备份文件,拷贝到目标离线机器。迁移包只包含 .env.example,不包含任何真实 .env 或数据库数据。出包脚本在写出生产候选 manifest 后会立即用共享校验器自检一遍,自检不通过即 FAIL-STOP。
发布安全更新(Review9/Review10 施工成果,2026-09-16):
--phase service是只读生产探针(未认证 401 → 管理员纯读登录 → release-identity 实例身份绑定 → 语义断言的只读列表),完整smoke-api.py业务链只能在恢复出的隔离副本上运行。backend 与 release-check 复用同一镜像 tag;release_check两个阶段都在任何数据库访问之前核验镜像内烧录的构建 commit(与--build-commit精确一致,development/unknown/非 40-hex 一律拒绝)。一键入口scripts/release.ps1/scripts/release.sh保持失败即停。历史施工记录见Review9AuditReport.md、Review10AuditReport.md。
发布安全更新(Review11 施工成果,2026-09-16):生产 runner 永不构建镜像——它只接受迁移包里 manifest 指定的、已
docker load的不可变镜像,并同时核验 image ID(docker image inspect {{.Id}})与镜像内 baked commit,之后全程--no-build/--pull never;-SkipBuild/--skip-build已移除(旧参数显式报错),开发构建不再属于发布入口。迁移包manifest.json升级为版本化 Schema v1(schema_version=1、固定四个 required artifacts、路径安全、production_candidate=true、backend 与 frontend 各自的 image 引用 + image ID + build commit、全量 SHA-256),由 Windows runner、Linux runner 与包级演练共用同一校验器;frontend 镜像也烧录/usr/share/nginx/html/release_build_commit.txt。流量门禁升级为先停 frontend,再以BACKEND_BIND_HOST=127.0.0.1启动 backend 做只读探针(宿主/LAN 的 API 端口在验收前不可达),探针通过后才用默认绑定重建 backend 并开放 frontend。只读探针的真实库零变化证据由scripts/verify_probe_zero_change.sh在一次性隔离 PostgreSQL 上产出(自建 network/容器/数据库 → 真实执行release_check --phase service→ 前后pg_dump与全表行数逐字节比对 → 清理)。最新判定见Review11AuditReport.md——系统仍不可发布,剩余阻断为 R11-S3 外部验收(灰度/回滚已于 2026-09-22 在重出的不可变包上复跑通过,见R11S3AcceptancePlan.md§9;剩余为线上 014 备份副本升级/恢复演练、Chromium/Edge/Chrome、混合容量与具名签字)。
以下命令全部在“内网目标机器”的 PowerShell 中执行。示例假设原项目目录为 C:/ExamPracticeSystem、迁移包目录为 C:/migration_package;如果实际目录不同,只修改前两行路径。
$ProjectDir = "C:/ExamPracticeSystem"
$PackageDir = "C:/migration_package"
$BackupDir = "C:/ExamPracticeSystem-backups"
Set-Location $ProjectDir
docker compose ps
docker volume ls | Select-String "pgdata"确认 docker compose ps 显示的是原来的项目,且能看到原来的 pgdata 卷。不要在开发机目录执行下面的升级命令,也不要为了迁移修改 Compose 项目名。
升级期间绝不允许应用容器带着旧库或半迁移的库继续服务。只保留数据库容器运行(备份和 release-check 都需要它):
docker compose stop backend frontend
docker compose psNew-Item -ItemType Directory -Force $BackupDir | Out-Null
$BackupPath = Join-Path $BackupDir ("before-upgrade_{0}.sql" -f (Get-Date -Format "yyyyMMdd_HHmmss"))
& (Join-Path $ProjectDir "scripts/backup.ps1") -OutputPath $BackupPath
$BackupFile = Get-Item $BackupPath -ErrorAction Stop
if ($BackupFile.Length -le 0) { throw "Database backup is empty: $BackupPath" }
$BackupFile | Select-Object FullName, Length必须确认最后一条命令能看到备份文件且文件大小不是 0。备份文件应放在项目目录之外,避免更新源码时误操作。
如果原系统已有 .env,不要修改它,更不要用开发机的 .env 覆盖它:
Test-Path (Join-Path $ProjectDir ".env")如果旧版本从未使用过 .env,先复制模板再编辑;数据库连接信息必须填写“原内网数据库”的实际值:
Copy-Item (Join-Path $PackageDir ".env.example") (Join-Path $ProjectDir ".env")
notepad (Join-Path $ProjectDir ".env")重点检查 POSTGRES_DB、POSTGRES_USER、POSTGRES_PASSWORD 和 DATABASE_URL 与原数据库一致;SECRET_KEY 必须至少 32 位,只有原密钥本身满足长度要求时才能沿用,否则请生成新值(这会使旧登录会话失效,但不会删除数据库数据)。ADMIN_PASSWORD 仅用于数据库中尚无管理员时创建初始管理员。不要把 .env.example 中的 replace-* 占位符直接用于生产环境。编辑完成后,在项目目录执行:
docker compose config --quiet该命令没有输出并返回成功,才继续下一步。
先按 manifest 校验包完整性(结构 + 全部 artifact 的 SHA-256),再做镜像加载:
python (Join-Path $ProjectDir "scripts/verify_release_manifest.py") (Join-Path $PackageDir "manifest.json")
docker load -i (Join-Path $PackageDir "postgres-16-alpine.tar")
docker load -i (Join-Path $PackageDir "exam-backend.tar")
docker load -i (Join-Path $PackageDir "exam-frontend.tar")校验器输出本次包的 build_commit/backend_image_id;任何 SHA-256 不匹配、worktree_dirty 包或缺失 artifact 都会直接失败。也可以用一键入口 .\scripts\release.ps1 -PackageDir $PackageDir,其内部在停机/备份之前执行同样的校验。
docker load 只导入镜像,不会删除或初始化 pgdata 数据卷。
源码归档不包含真实 .env,直接解压到原项目目录不会覆盖 .env:
tar -xf (Join-Path $PackageDir "exam-system-source.tar") -C $ProjectDir
Set-Location $ProjectDir
docker compose config --quiet源码解压后必须再次执行最终配置检查;旧版本的 Compose 文件可能不会读取新配置中的环境变量。
严禁在此步骤执行 docker compose up -d(一次性启动全部服务):新 backend 一启动就会在自己的 lifespan 里抢先迁移,发布检查就失去了“先验证再变更”的意义。必须只启动数据库:
docker compose up -d db
docker compose ps数据库侧的迁移(起点 014)、快照回填、facts 回填与新旧对账,必须通过失败即停的一次性 release-check 容器执行,而不是手动逐条命令、也不是进入 backend 容器补丁。先从数据库容器读取库名,再运行检查:
$DbName = (docker compose exec -T db printenv POSTGRES_DB)
$DbName = $DbName.Trim()
$BuildCommit = (python -c "import json;print(json.load(open(r'$PackageDir/manifest.json'))['build_commit'])")
$Report = "/reports/release_database_{0}.json" -f (Get-Date -Format "yyyyMMdd_HHmmss")
docker compose run --rm --no-deps --pull never release-check uv run python scripts/release_check.py `
--phase database `
--expect-database $DbName `
--confirm-backup-taken `
--build-commit $BuildCommit `
--report-file $Report回显 $DbName 确认它就是原业务库名;如果不是,先修正 .env 的 POSTGRES_DB / DATABASE_URL 再继续,绝不带糊弄的库名跑检查。
退出码只有三个:0 通过;1 某一步检查失败——迁移起点不是 014 属于这一类(prechecks 的步骤失败),迁移、回填、对账或报告写入失败同属此类,必须停止升级;3 目标被守卫拒绝——库名与 --expect-database 不一致(含 current_database() 服务端复核)、非 PostgreSQL 目标、缺少 --confirm-backup-taken,或镜像内烧录的构建 commit 与 --build-commit 不一致/非完整 40 位提交(该核验发生在任何数据库访问之前,报告以 expected_build_commit/actual_baked_commit 记录)。--report-file 的报告落在宿主机的 ./release-reports/ 目录(或 RELEASE_REPORTS_DIR 指定目录)。任一失败都不要“再跑一次碰运气”:先读报告中的失败步骤与有界诊断(重复会话 ID、未解析行 ID、对账差异),处置后从备份副本重新演练。
数据库阶段通过后先收回局域网入口,再以只绑回环的方式启动探针用的 backend(frontend 是局域网流量入口,验收通过前不启动;backend 的宿主端口在验收前也不对 LAN 开放):
docker compose stop frontend
$env:BACKEND_BIND_HOST = "127.0.0.1"
docker compose up -d --no-build backend
Remove-Item Env:BACKEND_BIND_HOST
docker compose ps
docker compose logs --tail=100 backend此时迁移已经完成,backend 启动时 lifespan 中的迁移只会幂等重放(已在目标版本则跳过)。服务就绪后运行严格只读的服务阶段探针:未认证请求必须 401 → 管理员登录(登录端点为纯读,不写任何字段)→ 读取 /about/release-identity 并把数据库名、Alembic revision、构建 commit 与期望值精确比对(任一不匹配退出 3 且零写入)→ 有界只读列表(含响应语义断言:health 必须 healthy、管理员身份、无截断响应)。探针绝不调用任何业务写接口;完整 smoke-api.py 链只允许在隔离副本上另行运行:
$DbName = (docker compose exec -T db printenv POSTGRES_DB).Trim()
$BuildCommit = "cf67f7a" # 必须是本次加载镜像对应的构建 commit(见迁移包 manifest.json)
$Report = "/reports/release_service_{0}.json" -f (Get-Date -Format "yyyyMMdd_HHmmss")
docker compose run --rm --no-deps --pull never release-check uv run python scripts/release_check.py `
--phase service `
--expect-database $DbName `
--build-commit $BuildCommit `
--base-url http://backend:8000/api/v1 `
--report-file $Report探针的就绪等待(Q50,2026-09-22):
docker compose up -d只保证容器被创建,应用可能仍在同步依赖与执行迁移(实测同一镜像冷启动约 13s、暖启动约 2s),紧随其后的探针会拿到Connection refused。因此api_reachable对传输层失败做有界重试:默认--wait-ready-seconds 60(0关闭),报告记录attempts。判据不变——可达的 API 仍必须回 401;一旦拿到任何 HTTP 响应立即判定,5xx/404属"已应答但不健康"不重试;等待期间不开放任何入口。
--build-commit 必填,取迁移包 manifest.json 的 build_commit,且与镜像内烧录值一致(缺失、非 40 位提交或不匹配退出 3)。docker compose run 没有 --no-build 标志,"禁止现场构建"由 --pull never(缺失镜像时直接失败,绝不拉取/构建)保证。退出码语义与数据库阶段相同。探针通过后才切流——backend 回到默认绑定,再开放前端(局域网流量入口):
docker compose up -d --no-build backend
docker compose up -d --no-build frontend之后用浏览器访问 http://<服务器IP>:4074 确认前端正常、登录可用,再把系统交回业务使用。
一键编排(推荐):以上 2~8 步可用
.\scripts\release.ps1 -PackageDir $PackageDir一键执行(manifest/双镜像身份校验 → 自动备份 → database 阶段 → 只绑回环启动 backend → service 阶段 → 通过后开放 backend/frontend),每一步都立即检查退出码,database 阶段失败绝不启动应用,service 阶段失败会把 backend 重新下线且 frontend 从未开放。生产 runner 不会构建镜像,只接受已docker load且与 manifest 记录的 image ID 一致的镜像。
先查看 docker compose logs backend 和 docker compose ps,保留备份文件,不要删除数据库卷。只有在确认需要回滚数据且已停止业务后,才由管理员单独执行 restore.ps1;恢复操作会覆盖数据库。
严禁执行 docker compose down -v、docker volume rm、docker system prune --volumes,也不要使用开发机 .env 或开发机的数据库备份覆盖内网数据库。
以下命令在 Ubuntu 内网服务器的原项目目录执行。示例路径可以按实际情况修改;如果 Docker 需要 root 权限,请统一在 Docker 命令前加 sudo,不要只对部分命令加 sudo。
set -euo pipefail
PROJECT_DIR=/opt/exam-practice-system
PACKAGE_DIR=/opt/migration_package
BACKUP_DIR=/opt/exam-practice-backups
cd "$PROJECT_DIR"
docker compose ps
docker volume ls --filter name=pgdata
# 停止业务写入,只保留数据库
docker compose stop backend frontend
docker compose ps
# 备份并校验非空
mkdir -p "$BACKUP_DIR"
BACKUP_PATH="$BACKUP_DIR/before-upgrade_$(date +%Y%m%d_%H%M%S).sql"
bash "$PROJECT_DIR/scripts/backup.sh" --output "$BACKUP_PATH"
test -s "$BACKUP_PATH"
ls -lh "$BACKUP_PATH"
# 旧版本没有 .env 时才执行;已有 .env 不要覆盖
if [ ! -f "$PROJECT_DIR/.env" ]; then
cp "$PACKAGE_DIR/.env.example" "$PROJECT_DIR/.env"
nano "$PROJECT_DIR/.env"
fi
docker load -i "$PACKAGE_DIR/postgres-16-alpine.tar"
docker load -i "$PACKAGE_DIR/exam-backend.tar"
docker load -i "$PACKAGE_DIR/exam-frontend.tar"
# manifest 强制校验(结构/worktree_dirty/全量 SHA-256;失败即停)
python3 "$PROJECT_DIR/scripts/verify_release_manifest.py" "$PACKAGE_DIR/manifest.json"
tar -xf "$PACKAGE_DIR/exam-system-source.tar" -C "$PROJECT_DIR"
cd "$PROJECT_DIR"
docker compose config --quiet
# 只启动数据库,绝不在检查前启动 backend/frontend
docker compose up -d db
# release-check 数据库阶段(一次性容器,报告落宿主 ./release-reports/)
DB_NAME="$(docker compose exec -T db printenv POSTGRES_DB | tr -d '\r')"
BUILD_COMMIT="$(python3 -c 'import json;print(json.load(open("'$PACKAGE_DIR'/manifest.json"))["build_commit"])')"
REPORT="/reports/release_database_$(date +%Y%m%d_%H%M%S).json"
docker compose run --rm --no-deps --pull never release-check uv run python scripts/release_check.py \
--phase database \
--expect-database "$DB_NAME" \
--confirm-backup-taken \
--build-commit "$BUILD_COMMIT" \
--report-file "$REPORT"
# 注意:不要在本块末尾用 `echo "... exit code: $?"` 收尾——它会把整段脚本的
# 退出码重置为 0,掩盖上游失败;`set -euo pipefail` 已保证任何一步失败立即中断。数据库阶段退出码为 0 才能继续。先收回局域网入口(frontend 无条件停止),再以只绑回环的方式启动探针用的 backend,完成严格只读的服务阶段探针(与 Windows 版一致:身份绑定 + 零业务写入 + 响应语义断言;--build-commit 取迁移包 manifest.json 的 build_commit),通过后才切流:
# 1) 探针前,局域网入口必须关闭(含"前端本来就在运行"的情况)
docker compose stop frontend
# 2) 探针 backend 只绑 127.0.0.1:宿主/LAN 的 8000 在验收前不可达
BACKEND_BIND_HOST=127.0.0.1 docker compose up -d --no-build backend
docker compose ps
docker compose logs --tail=100 backend
REPORT="/reports/release_service_$(date +%Y%m%d_%H%M%S).json"
BUILD_COMMIT="$(python3 -c 'import json;print(json.load(open("'$PACKAGE_DIR'/manifest.json"))["build_commit"])')"
docker compose run --rm --no-deps --pull never release-check uv run python scripts/release_check.py \
--phase service \
--expect-database "$DB_NAME" \
--build-commit "$BUILD_COMMIT" \
--base-url http://backend:8000/api/v1 \
--report-file "$REPORT"
# 3) 探针通过后才切流:backend 回到默认绑定,frontend 开放
docker compose up -d --no-build backend
docker compose up -d --no-build frontend服务阶段也通过后,用浏览器访问 http://<服务器IP>:4074 确认前端正常、登录可用。Linux 上推荐直接使用一键编排入口:bash scripts/release.sh --package-dir "$PACKAGE_DIR"(set -euo pipefail,每步失败即停;manifest 校验通过前不停机也不备份,service 探针通过前不开放任何局域网流量)。生产 runner 不会构建镜像,只接受已 docker load 且与 manifest 记录的 image ID 一致的镜像。
如果当前用户不在 docker 用户组,备份命令也要使用 sudo bash ...,并给每条 docker compose/docker load 命令加 sudo;不要只给最后的 up 命令加 sudo。
旧版本没有 .env 时,必须将 POSTGRES_DB、POSTGRES_USER、POSTGRES_PASSWORD 和 DATABASE_URL 填成原数据库的实际值;SECRET_KEY 至少 32 位,ADMIN_PASSWORD 只在数据库没有管理员时用于创建初始管理员。不要把模板占位符直接用于生产环境。源码解压后再执行一次 config,是为了让新 Compose 文件校验这些配置。
已有内网实例升级不执行 restore.sh;它只用于明确的数据恢复,并会覆盖数据库。升级失败时先查看日志并保留备份,不要删除 pgdata 卷。
# 1. 加载镜像
docker load -i migration_package/postgres-16-alpine.tar
docker load -i migration_package/exam-backend.tar
docker load -i migration_package/exam-frontend.tar
# 2. 解压源码
tar xf migration_package/exam-system-source.tar
# 3. 创建新部署配置(不要用于已有内网实例)
cp migration_package/.env.example .env
# Ubuntu 上编辑 .env,设置数据库密码、DATABASE_URL、SECRET_KEY 和 ADMIN_PASSWORD
nano .env
# 4. 启动服务(会自动建表+种子管理员)
docker compose up -d --no-build
# 5. 仅在明确要恢复一份备份时执行(会覆盖目标数据库)
bash ./scripts/restore.sh --input backup_20260528_120000.sql全新部署的空库由 backend 首次启动从零完成全部迁移,不适用
--phase database(该阶段要求迁移起点 014,是给已有内网实例升级用的)。全新部署只需在启动后按需自行业务验证。
访问 http://<服务器IP>:4074,管理员账号为 admin,初始密码取自 .env 的 ADMIN_PASSWORD,首次登录后必须修改。
# 备份
.\scripts\backup.ps1 # Docker 模式(默认)
.\scripts\backup.ps1 -Local # 本地 PostgreSQL
# 恢复(⚠ 覆盖全部数据)
.\scripts\restore.ps1 -InputPath backup.sql # Docker 模式
.\scripts\restore.ps1 -InputPath backup.sql -Local # 本地模式详见 scripts/backup.ps1 和 scripts/restore.ps1 的 -Help 信息。
.env是安全配置改造后加入的必需配置文件,用于避免仓库内固定数据库密码和 JWT 密钥。迁移已有内网系统时,始终沿用目标机原.env;migration_package/.env.example只供全新部署参考,不能覆盖原配置。
Ubuntu 的 Docker 部署使用 Shell 脚本,不使用 PowerShell:
cd /opt/exam-practice-system
BACKUP_PATH="/opt/exam-practice-backups/backup_$(date +%Y%m%d_%H%M%S).sql"
mkdir -p /opt/exam-practice-backups
bash ./scripts/backup.sh --output "$BACKUP_PATH"
test -s "$BACKUP_PATH"
ls -lh "$BACKUP_PATH"如果旧迁移包中的脚本出现 $'\r': command not found,说明脚本被 Windows 换行符污染。临时修复可以执行:
sed -i 's/\r$//' scripts/backup.sh scripts/restore.sh最新迁移包已通过 .gitattributes 固定 Shell 脚本使用 LF 换行,不应再出现该问题。
- 用户系统 — 注册/登录/登出,JWT 鉴权,管理员用户管理
- 题库管理 — 多题库、题目 CRUD、CSV/Excel/TXT 导入、列映射预览、管理员自定义题库顺序和默认题库
- 考试模式 — 题库与试卷分离,按题型规则随机抽题,限时答题、自动判分和结果回顾
- 练习模式 — 按题库/题型/顺序配置,逐题即时反馈并记录错题
- 背题模式 — 选择题库/题型/题数直接浏览题目、答案和解析,无需作答
- 答案解析 — 管理员建题时可填写解析,考试/练习/背题模式中展示
- 知识补充 — 用户提交知识点补充,管理员审批后更新解析
- 通知与公告 — 知识补充审批反馈、管理员站内通知和全局系统公告
- 错题本 — 自动收集错题、计数、筛选、查看题目详情、提交知识补充、重练和删除
- 排行榜 — 全局排名 + 组卷独立排名
- 个人看板 — 练习次数、正确率、平均分、最高分、题型正确率和得分历史
- 学情分析 — 整体统计、难题/高频错题分析、查看题目详情、提交知识补充、生成易错题题库
├── backend/ FastAPI 后端
│ ├── app/
│ │ ├── api/ # API 路由
│ │ ├── core/ # 配置、数据库、安全
│ │ ├── models/ # ORM 模型
│ │ └── schemas/ # 请求/响应模型
│ └── Dockerfile
├── frontend/ Vue 3 前端
│ ├── src/
│ │ ├── components/ # 可复用组件
│ │ ├── pages/ # 页面组件
│ │ ├── router/ # 路由配置
│ │ ├── store/ # 状态管理
│ │ └── services/ # HTTP 请求
│ └── Dockerfile
├── docs/ 开发文档
└── docker-compose.yml 容器编排
- 当前正式支持 Microsoft Edge 与 Google Chrome(Chromium);Firefox/Safari(WebKit) 暂不保证兼容。完整 GitHub CI 暂缓,发布前仍须执行本地/容器 release-check 与 Chromium 真实业务验收。
docs/DEVELOPMENT.md— 工程规范、部署、调试指南docs/requirements/ExamPracticeSystem.md— 业务需求、验收标准、进度和变更记录docs/requirements/Review6AcceptanceReport.md— 2026-09-16 第六轮验收及 AUD6 施工历史记录docs/requirements/Review7AcceptanceReport.md— 2026-09-16 第七轮审计与 R7-S0~S2 施工历史记录docs/requirements/Review8AuditReport.md— 2026-09-16 第八轮审查、发布车道交付缺口与最新上线判定docs/requirements/Review4ConstructionPlan.md— AC-REV4-1~28 定义、施工顺序和历史验收约束docs/requirements/OptimizationConstructionPlan.md— 2026-09-12 稳定性、性能、安全、考试与大题量专项施工计划docs/requirements/FutureImprovements.md— 待开发需求、已知问题和验收风险池AGENTS.md— AI 协作规范
文档职责约定:
- README 只保留项目简介、快速启动和文档入口。
docs/DEVELOPMENT.md维护工程事实:架构、运行方式、接口约定、质量门禁。docs/requirements/ExamPracticeSystem.md维护业务事实:功能范围、验收标准、当前状态和任务记录。
MIT