完善文档与前端迁移,补充开源协议

This commit is contained in:
2026-04-17 19:48:13 +08:00
parent 466fa50aa8
commit 31916e68a6
94 changed files with 7019 additions and 480 deletions

86
docs/PLAN.md Normal file
View File

@@ -0,0 +1,86 @@
# QQuiz Execution Plan
更新时间2026-04-17
## 目标
把当前项目推进到可以持续开发和稳定验收的状态,重点落实:
1. 默认 SQLite兼容 MySQL
2. 前端界面简洁、可直接操作、少说明文字
3. 用户管理达到可上市产品的基础要求
4. push 后 GitHub 自动构建镜像
5. 逐步完成旧 Vite 前端到新 Next 前端的替换
## 已完成的代码级工作
- 默认 Docker 拓扑已切到 SQLite
- MySQL 兼容拓扑已拆到 `docker-compose.mysql.yml`
- 新前端容器已接入并替换 Docker 默认前端
- 管理员用户管理已经接入真实接口
- 管理员设置页已经接入真实配置接口
- 侧边栏选中态 bug 已修复
- 新前端色彩已收敛为更简洁的产品风格
## 当前代码扫描后的主要问题
### 后端
1. 数据库迁移体系仍然不完整
2. `LLMService` 仍存在启动期副作用
3. 文档解析任务仍依赖进程内后台任务
4. 题库导入并发与去重约束还没彻底补完
5. 管理模块还缺用户状态、审计、批量能力
### 前端
1. Exam detail / Question / Mistake / Quiz 仍是占位或半占位页面
2. 部分页面仍有旧迁移骨架内容,需要继续清理
3. 确认类交互还没统一替换为更正式的对话框方案
4. 视觉层还需要统一列表、表单、状态、分页组件
### 部署与文档
1. Compose 仍缺 dev/prod 分离
2. 文档体系仍需把运行方式统一到 SQLite 默认 + MySQL 兼容
3. CI 只有主干镜像构建,还缺 PR 验证与 smoke test
## 后续执行顺序
### 第一阶段:后端稳定性
1. Alembic 基线迁移
2. 去掉 `create_all` 正式职责
3. 去掉 LLM import side effect
4. 统一事务边界
5. 补用户状态字段与审计日志模型
### 第二阶段:前端业务页
1. 接通 `exams/[examId]`
2. 接通 `questions`
3. 接通 `mistakes`
4. 接通 `quiz/[examId]`
5. 接通 `mistake-quiz`
### 第三阶段:用户管理产品化
1. 用户状态管理
2. 审计日志
3. 批量操作
4. 更完整的密码与安全策略
### 第四阶段:工程化
1. Compose dev/prod 分离
2. PR workflow
3. SQLite/MySQL 双栈 smoke
4. 文档统一
## 前端视觉要求
1. 主色:深蓝,作为动作与选中态
2. 背景:浅灰蓝,不用大面积高饱和装饰
3. 卡片:白底、细边框、轻阴影
4. 状态色:成功绿、警告橙、错误红
5. 页面信息结构:标题、数据、动作优先,减少解释文字

92
docs/TASKS.md Normal file
View File

@@ -0,0 +1,92 @@
# QQuiz Task Checklist
更新时间2026-04-17
## P0 运行基线
- [x] 默认 Docker 拓扑切回 SQLite
- [x] 保留 MySQL 兼容 Compose 覆盖文件
- [x] 前后端容器可启动并完成最小探活
- [x] GitHub Actions 改成 push 后自动构建 backend/frontend 镜像
- [ ] 补开发/生产分离 Compose
- [ ] 补 PR 级别 build/smoke workflow
- [ ] 清理根目录 Docker 文档漂移
## P1 后端稳定性
- [x] 管理员配置接口忽略打码后的密钥回写
- [x] 用户列表返回改为强类型
- [x] 用户列表统计去掉 N+1 查询
- [x] 最后一个管理员保护
- [x] 管理员密码重置接口
- [ ] 去掉启动期 `create_all` 作为正式迁移方式
- [ ] 建 Alembic 初始迁移
- [ ] 去掉 `LLMService` import side effect
- [ ] 收敛事务边界
- [ ] 修 ingestion 并发与唯一约束
- [ ] 规范健康检查和错误模型
## P2 用户管理
- [x] 用户搜索
- [x] 创建用户
- [x] 编辑用户
- [x] 重置密码
- [x] 删除用户
- [ ] 用户状态字段(启用/禁用/锁定)
- [ ] 审计日志
- [ ] 批量操作
- [ ] 密码强度与重置流程优化
- [ ] 默认管理员保护策略文档化
## P3 新前端基础层
- [x] Next.js App Router 骨架
- [x] BFF 登录/登出/`/me` 代理
- [x] 同源 API 代理
- [x] SSE 代理入口
- [x] 移除旧前端 ESA 人机验证
- [ ] 中间件与服务端守卫完善
- [ ] 错误页/空状态统一
- [ ] URL 状态策略统一
## P4 页面迁移
### 已接入真实数据
- [x] Dashboard
- [x] Exams list
- [x] Exam detail
- [x] Questions list
- [x] Mistakes list
- [x] Quiz player
- [x] Mistake quiz
- [x] Admin user management
- [x] Admin settings
### 待继续
- [ ] 上传/进度/失败重试链路
## P5 前端视觉与交互
- [x] 侧边栏选中态修复
- [x] 新前端配色收敛为更简洁的产品风格
- [x] 去掉大段迁移说明文案
- [ ] 统一表格、表单、按钮、状态徽标
- [ ] 清理页面中的占位内容
- [ ] 替换 `window.confirm` 为统一对话框
- [ ] 移动端布局细化
## P6 测试与验收
- [x] 旧前端构建通过
- [x] 新前端构建通过
- [x] Docker 最小登录链路验证
- [x] 管理员配置、用户管理、上传解析、题目、错题、刷题链路验证
- [x] 管理员与普通用户登录验证
- [x] PowerShell smoke 脚本固化全流程验证
- [ ] 后端集成测试
- [ ] 前端 E2E 烟测
- [ ] SQLite / MySQL 双栈验证
- [ ] 用户管理回归用例

View File

@@ -0,0 +1,70 @@
# QQuiz Architecture Audit
## Scope
This document records the current system shape and the approved target
direction for the ongoing refactor.
Audit date: 2026-04-17
## Current Architecture
### Backend
- Runtime: FastAPI + SQLAlchemy async
- Database access: direct ORM session injection per request
- Task execution: in-process `BackgroundTasks`
- Progress streaming: in-memory `ProgressService`
- Schema management: mixed `create_all()` and Alembic placeholders
### Frontend
- Runtime: React 18 + Vite SPA
- Routing: `react-router-dom`
- Auth state: client-only `localStorage` token + context
- API transport: axios interceptor with browser redirects
- Styling: Tailwind CSS with page-local utility classes
### Deployment
- `docker-compose.yml`: development-oriented split stack
- `docker-compose-single.yml`: monolith container with SQLite
- `Dockerfile`: FastAPI serves the built SPA as static assets
## Target Architecture
### Backend
- Keep FastAPI as the system API boundary
- Move heavy router logic into typed services
- Use Alembic as the only schema migration path
- Introduce durable ingestion execution semantics
- Replace implicit transaction patterns with explicit service-level boundaries
### Frontend
- New app in `web/`
- Stack: Next.js App Router + TypeScript + Tailwind + shadcn/ui
- Auth: `HttpOnly` session cookie mediated by Next route handlers
- Data fetching: `fetch` wrappers for server/client usage
- Streaming: Next proxy route for exam progress SSE
### Deployment
- Split deployment becomes the primary production shape
- Monolith mode remains secondary compatibility mode
- Development and production Compose files must be separated
## Core Constraints
1. Do not overwrite existing uncommitted user changes in the legacy frontend.
2. Keep the legacy `frontend/` app available until the new `web/` app reaches functional parity.
3. Preserve backend API contracts where possible during the frontend migration.
4. Fix deployment/documentation drift before treating new frontend work as production-ready.
## Immediate Workstreams
1. Remove abandoned ESA captcha wiring from the legacy frontend.
2. Write audit documents and freeze the migration backlog.
3. Scaffold the new `web/` frontend without disturbing the legacy app.
4. Fix first-order deployment issues such as health checks and documented mount paths.

View File

@@ -0,0 +1,86 @@
# Backend Findings
## Critical Findings
### Schema lifecycle is unsafe
- App startup still calls `create_all()`
- Alembic metadata exists but the migration chain is effectively empty
- This prevents controlled upgrades and rollbacks
Files:
- `backend/main.py`
- `backend/database.py`
- `backend/alembic/versions/.gitkeep`
### Parsing tasks are not durable
- Document ingestion runs inside FastAPI `BackgroundTasks`
- Progress state lives in-process only
- Process restarts or horizontal scaling can strand exams in `pending` or `processing`
Files:
- `backend/routers/exam.py`
- `backend/services/progress_service.py`
### Transaction boundaries are inconsistent
- `get_db()` performs commit/rollback automatically
- Routers and background tasks also call `commit()` directly
- SSE endpoints keep a database dependency open for long-lived streams
Files:
- `backend/database.py`
- `backend/routers/exam.py`
## High-Priority Bugs
### Admin config can destroy secrets
- `GET /api/admin/config` masks API keys
- `PUT /api/admin/config` persists whatever the frontend sends back
- A round-trip save can replace the real secret with the masked placeholder
Files:
- `backend/routers/admin.py`
### LLM service has import-time side effects
- `LLMService()` is instantiated at module import time
- Missing environment variables can break startup before DB-backed config is loaded
Files:
- `backend/services/llm_service.py`
### Ingestion deduplication is race-prone
- No unique DB constraint on `(exam_id, content_hash)`
- Multiple append operations can race and insert duplicates
Files:
- `backend/models.py`
- `backend/routers/exam.py`
### Answer checking degrades incorrectly on infra failure
- Short-answer grading failures are converted into zero scores
- User mistake data can be polluted by provider outages or config errors
Files:
- `backend/services/llm_service.py`
- `backend/routers/question.py`
## Refactor Order
1. Replace runtime schema creation with Alembic-first migrations.
2. Move ingestion, config, and answer checking into service classes.
3. Introduce explicit transaction boundaries and idempotent ingestion rules.
4. Add durable task execution and real status/error semantics.
5. Add integration tests for config round-trips, ingestion races, and answer normalization.

View File

@@ -0,0 +1,49 @@
# Deployment Findings
## Current Problems
### Monolith persistence documentation is wrong
- Existing `docker run` examples mounted the wrong path
- SQLite and upload persistence must target `/app/data` and `/app/uploads`
### Monolith health check was broken
- `docker-compose-single.yml` used `curl`
- The image does not guarantee `curl` exists
- The health check has been switched to Python stdlib HTTP probing
### Split Compose is development-oriented
- Source mounts are enabled
- Backend runs with `uvicorn --reload`
- Frontend runs a dev server
- This is not a production deployment model
### Security posture is weak
- Compose contains hard-coded MySQL credentials
- MySQL is exposed on `3306`
- Environment guidance is inconsistent across README, Compose, and `.env.example`
## Approved Direction
1. Treat split deployment as the default production topology.
2. Keep monolith deployment as a compatibility target only.
3. Separate development assets from production assets.
4. Validate all release images with smoke checks before publishing.
## Backlog
### Short term
- Create `compose.dev.yml` and `compose.prod.yml`
- Remove dev-server assumptions from production documentation
- Add backend runtime dependencies explicitly to image builds
- Align README with actual mount paths and health checks
### Medium term
- Add PR build, typecheck, lint, and smoke-test workflows
- Publish separate images for API and Next web app
- Document rollback by image tag and Compose profile

View File

@@ -0,0 +1,70 @@
# Frontend Migration Plan
## Decision
The legacy Vite SPA remains in `frontend/` as a fallback.
The new frontend is being built in `web/` with:
- Next.js App Router
- TypeScript
- Tailwind CSS
- shadcn/ui component model
The abandoned ESA captcha integration has been removed from the legacy login page.
## Why a Rewrite Instead of an In-Place Port
The legacy frontend mixes too many browser-only assumptions into core runtime
boundaries:
- token storage in `localStorage`
- `window.location` redirects inside transport code
- client-only route protection
- SSE token passing in query strings
Those patterns do not map cleanly onto Next App Router and server-first auth.
## New Runtime Model
### Auth
- Login goes through Next route handlers
- Backend JWT is stored in an `HttpOnly` cookie
- Browser code never reads the raw token
### Data
- Server pages use server-side fetch helpers
- Client mutations use browser-side fetch helpers against Next proxy routes
- URL state is used for pagination and filters
### Streaming
- Browser connects to a same-origin Next progress route
- The route reads the session cookie and proxies backend SSE
- Backend URL tokens are hidden from the browser
## Directory Map
```text
web/
src/app/
src/components/
src/lib/
src/middleware.ts
```
## Migration Order
1. Auth shell, layouts, middleware, and proxy routes
2. Dashboard, exams list, questions list, and admin overview
3. Exam detail upload and progress streaming
4. Quiz and mistake-practice flows
5. Cutover, smoke testing, and legacy frontend retirement
## Non-Goals for This First Slice
- No immediate removal of the legacy `frontend/`
- No backend contract rewrite yet
- No server actions as the primary data mutation layer

BIN
docs/cover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB