BetterWrite — 深圳中考英语作文 AI 辅导系统。面向教师、学生与学校管理员,提供作文任务管理、AI 智能批改、学情分析、错题本与教学资源等功能。
- Web: Next.js 16 (App Router) + React 19 + TypeScript + Tailwind CSS
- API: Hono + Lucia Auth + Zod
- Database: SQLite (libsql) + Drizzle ORM
- Cache / Rate-limiting: Redis
- Task Queue: 内置 worker(作文异步批改)
- Monorepo: pnpm workspaces + Turbo
- Deployment: Docker + Docker Compose
git clone https://github.com/your-org/BetterWrite.git
cd BetterWritepnpm installcp .env.example .env编辑 .env,至少配置以下项:
NEXTAUTH_SECRET:随机字符串,生产环境务必修改ENCRYPTION_KEY:64 位 hex 密钥(openssl rand -hex 32),用于加密存储 AI API 密钥
AI 供应商密钥不再通过环境变量配置,启动后使用超管账号登录后台「API 管理」页面统一配置,详见 AI 供应商配置。
pnpm db:migrate
pnpm db:seedpnpm dev访问 http://localhost:3000,使用[默认账号](#默认账号)登录。
推荐用于生产或演示环境,已包含 Web、Worker、Redis、自动迁移。
cp .env.production.example .env.production
# 编辑 .env.production,配置 NEXTAUTH_SECRET 与 AI Keydocker compose --env-file .env.production -f docker/docker-compose.yml up -d该命令会依次完成:
- 启动 Redis
- 运行数据库迁移(migrate 服务,一次性)
- 启动作文批改 Worker
- 启动 Next.js Web 服务
首次部署后,运行种子脚本创建默认管理员、教师、学生账号:
docker compose --env-file .env.production -f docker/docker-compose.yml run --rm seed种子脚本会写入固定邮箱,重复执行将因唯一约束失败,属于预期行为。
打开浏览器访问 http://localhost:3000(端口可通过 WEB_PORT 修改)。
# Web 服务
docker logs -f betterwrite-web
# Worker
docker logs -f betterwrite-workerdocker compose --env-file .env.production -f docker/docker-compose.yml down| 变量 | 说明 | 开发示例 | 生产示例 |
|---|---|---|---|
DATABASE_URL |
SQLite / libsql 连接地址 | file:./local.db |
file:/app/data/betterwrite.db |
DATABASE_AUTH_TOKEN |
远程 libsql 认证令牌(可选) | - | - |
REDIS_URL |
Redis 连接地址 | redis://localhost:6379 |
redis://redis:6379 |
NEXTAUTH_SECRET |
Lucia 会话加密密钥 | 任意字符串 | openssl rand -base64 32 |
NEXT_PUBLIC_API_URL |
前端 API 基地址 | http://localhost:3000 |
http://localhost:3000 |
ENCRYPTION_KEY |
AI 密钥加密存储密钥(64 位 hex,web/worker 必须一致) | openssl rand -hex 32 |
openssl rand -hex 32 |
EXPO_ACCESS_TOKEN |
推送通知令牌(可选) | - | - |
WEB_PORT |
Web 服务对外端口 | 3000 |
3000 |
AI 供应商的 API Key 不再使用环境变量,由超管后台统一管理。
AI API 密钥由超管在后台「API 管理」页面统一配置,加密(AES-256-GCM)存入数据库:
- 多提供商:支持 OpenAI、DeepSeek、Anthropic、Qwen 及自定义 OpenAI 兼容端点,可配置 Base URL、模型、maxTokens、温度、每分钟限流等参数。
- 优先级与回退:多个配置按优先级降序使用,主模型调用失败或触发限流时自动切换到备用模型。
- 模型路由:可为题意符合度、内容、语言、结构、综合评分等批改环节分别指定首选配置。
- 热更新:配置保存后通过 Redis 通知 worker 实时重载(另有 60s 轮询兑底),无需重启服务。
- 前置条件:web 与 worker 需配置相同的
ENCRYPTION_KEY;未配置任何提供商时,批改降级为模拟评分。
执行 pnpm db:seed 或 Docker seed 服务后,系统会创建以下账号(密码随机生成,详见命令行输出):
| 角色 | 邮箱 |
|---|---|
| 超级管理员 | superadmin@betterwrite.cn |
| 学校管理员 | admin@school.com |
| 教师 | teacher@school.com |
| 学生 | student@school.com |
生产环境务必修改默认密码或删除默认账号。
# 拉取最新代码
git pull origin main
# 重新构建并启动
docker compose --env-file .env.production -f docker/docker-compose.yml up -d --build数据库以 SQLite 文件形式存储在 Docker volume betterwrite_sqlite-data 中。
# 备份
docker run --rm -v betterwrite_sqlite-data:/data -v $(pwd):/backup alpine \
tar czf /backup/betterwrite-db-backup.tar.gz -C /data .
# 恢复(谨慎操作,会覆盖当前数据)
docker run --rm -v betterwrite_sqlite-data:/data -v $(pwd):/backup alpine \
tar xzf /backup/betterwrite-db-backup.tar.gz -C /data# 启动所有开发服务
pnpm dev
# 仅启动 Web
pnpm --filter @betterwrite/web dev
# 运行测试
pnpm test
# 代码检查
pnpm lint
pnpm typecheck
# 数据库迁移与种子
pnpm db:migrate
pnpm db:seed
pnpm db:studioBetterWrite/
├── apps/
│ ├── web/ # Next.js Web 应用
│ ├── worker/ # 作文批改 Worker
│ └── mobile/ # 移动端(Expo/React Native)
├── packages/
│ ├── db/ # Drizzle ORM 与数据库 schema
│ ├── ai/ # AI 批改引擎与 provider 路由
│ ├── shared/ # 共享类型与工具函数
│ ├── design-system/ # UI 组件与设计 token
│ └── tsconfig/ # 共享 TypeScript 配置
├── docker/ # Docker 镜像与 Compose 编排
├── docs/ # 架构与接口文档
└── .github/workflows/ # CI/CD
- 会话密钥: 修改
.env.production中的NEXTAUTH_SECRET,长度必须 ≥ 32 字符。生成命令:openssl rand -base64 32
- 加密密钥: 设置
ENCRYPTION_KEY(64 位 hex,openssl rand -hex 32),web 与 worker 必须一致。AI 供应商密钥部署后在超管后台「API 管理」页面配置,未配置时批改使用模拟评分。 - 默认账号: 首次启动后使用种子服务创建默认账号,生产环境务必修改默认密码或删除默认账号。
- HTTPS: 使用反向代理并配置 TLS 证书。项目已提供 Nginx 示例:
# 准备证书到 docker/nginx/certs/fullchain.pem 与 privkey.pem docker compose --env-file .env.production \ -f docker/docker-compose.yml \ -f docker/docker-compose.nginx.yml up -d - 备份: 启用 backup profile 定期创建一致性备份:
docker compose --env-file .env.production -f docker/docker-compose.yml --profile backup up -d
- 日志轮转: docker-compose.yml 已配置每个容器最多保留 3 个 100MB 日志文件,如需调整可修改
logging配置。 - CORS: 如需跨域访问 API,在
.env.production中设置CORS_ORIGIN(逗号分隔)。留空时禁止跨域请求,仅允许同源访问。 - 数据库迁移: migrate 服务内置 3 次重试,避免 SQLite 偶发锁冲突导致启动失败。
- Web / Worker 反复重启:检查
.env.production中NEXTAUTH_SECRET是否为空或长度不足 32,以及REDIS_URL是否可解析。 - 健康检查失败:Web 服务访问
/api/health会校验数据库、Redis 与任务队列;Worker 健康端点会校验数据库与 Redis 连通性。 - Worker 因 Redis 断连退出:当 Redis 长期不可达时,Worker 会主动退出,由 Docker
restart: unless-stopped自动重启恢复,属于正常行为。 - 备份文件损坏:Backup 服务已使用 SQLite 在线备份(
.backup命令),避免直接复制正在写入的数据库文件。 - 无真实 AI 批改:未配置 AI Key 时,系统会自动降级为模拟评分,并在日志中提示
No AI provider configured。 - SQLite 写入冲突:Docker Compose 默认使用单 SQLite 文件与单 Worker 实例,避免多进程同时写入;高并发场景请迁移至 libsql server / Turso。
MIT