本指南面向希望为 Whimread 贡献代码或自部署的开发者。涵盖项目架构、开发环境搭建、扩展开发等主题。
Whimread 采用 monorepo 架构,包含两个主要模块:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Flutter App │ ←─→ │ FastAPI 后端 │ ←─→ │ PostgreSQL │
│ (novel_app) │ │ (backend) │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ ├── ComfyUI (图片/视频生成)
│ └── 客户端本地 AI(DSL Engine + Agent Chat)
│
└── SQLite 本地缓存(含 site_scripts / chapter_cache)
- 章节获取 → Flutter App → Headless WebView + 本地 JS 提取脚本 → 写入
chapter_cache表 - AI 功能 → Flutter App(DSL Engine 本地执行) → OpenAI 兼容 LLM API
- 场景插图 → Flutter App → FastAPI → ComfyUI → 图片下载/缓存
- 数据备份 → 客户端 → FastAPI
/api/backup/upload
| 工具 | 版本 | 用途 |
|---|---|---|
| Flutter SDK | 3.0+ | 移动应用开发 |
| Dart SDK | 3.0+ | Flutter 依赖 |
| Python | 3.11+ | 后端开发 |
| Docker | 20.10+ | 容器化部署 |
| Docker Compose | 2.0+ | 多服务编排 |
| PostgreSQL | 15+ | 后端数据库 |
| Git | 2.30+ | 版本控制 |
| Node.js | 18+ | API 代码生成工具 |
git clone https://github.com/yunkst/Whimread.git
cd Whimread
git remote add upstream https://github.com/yunkst/Whimread.git# 启动数据库和后端服务
docker-compose up -d postgres backend
# 查看日志
docker-compose logs -f backendcd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 安装依赖(含开发工具)
pip install -e ".[dev]"
# 启动开发服务器
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reloadcd backend
alembic upgrade head # 应用所有迁移
alembic revision --autogenerate -m "描述" # 生成新迁移cd novel_app
flutter pub get
# 生成 Riverpod Provider 代码
dart run build_runner build --delete-conflicting-outputs
# 运行应用(自动选择连接的设备)
flutter run
# 仅在特定平台运行
flutter run -d android
flutter run -d chrome后端启动后,运行以下命令重新生成 Dart API 客户端:
cd novel_app
dart run tool/generate_api.dartWhimread/
├── novel_app/ # Flutter 移动应用
├── backend/ # Python FastAPI 后端
├── docs/ # 项目文档
├── docker-compose.yml # Docker 编排
├── .github/ # GitHub Actions CI/CD
├── .env.example # 环境变量模板
├── README.md # 项目说明
├── CHANGELOG.md # 变更日志
├── CONTRIBUTING.md # 贡献指南
├── LICENSE # MIT 许可证
└── CLAUDE.md # AI 上下文文档
novel_app/
├── lib/
│ ├── main.dart # 应用入口
│ ├── core/ # 核心基础设施
│ │ ├── di/ # 依赖注入
│ │ ├── database/ # SQLite 数据库
│ │ ├── interfaces/ # 抽象接口
│ │ └── providers/ # Riverpod Provider(50+)
│ ├── controllers/ # 控制器层
│ ├── repositories/ # 数据仓库层
│ ├── services/ # 业务服务
│ │ ├── dsl_engine/ # DSL Engine 核心
│ │ ├── novel_agent/ # Agent Chat 引擎
│ │ ├── headless_webview_*.dart # Headless WebView 提取
│ │ ├── ocr_restore_service.dart # OCR 还原(PP-OCRv6)
│ │ └── api_service_*.dart # API 客户端
│ ├── screens/ # 页面组件
│ ├── widgets/ # 可复用组件
│ ├── dialogs/ # 对话框
│ ├── models/ # 数据模型
│ ├── utils/ # 工具函数
│ ├── mixins/ # 混入类
│ ├── extensions/ # 扩展方法
│ └── generated/ # OpenAPI 自动生成代码
├── assets/
│ ├── dsl/ # DSL 工作流定义(YAML)
│ └── images/ # 应用图标等
├── android/ # Android 平台
├── ios/ # iOS 平台
├── test/ # 测试代码
└── CLAUDE.md # 模块文档
backend/
├── app/
│ ├── main.py # FastAPI 应用入口
│ ├── config.py # 配置管理
│ ├── database.py # 数据库连接
│ ├── exceptions.py # 自定义异常
│ ├── logging_config.py # 日志配置
│ ├── constants.py # 常量定义
│ ├── api/
│ │ └── routes/ # 路由模块
│ │ ├── backup.py # 备份 API
│ │ ├── novel_sync.py # 小说同步
│ │ ├── text2img.py # 文生图(ComfyUI)
│ │ ├── image_to_video.py # 图生视频(ComfyUI)
│ │ └── logs.py # 日志查询/上报
│ ├── deps/ # FastAPI 依赖
│ ├── models/ # SQLAlchemy 模型
│ ├── schemas/ # Pydantic 模式
│ └── services/ # 业务服务
│ ├── comfyui_*.py # ComfyUI 客户端
│ ├── backup_*.py # 备份/恢复
│ └── *.py # 其他服务
├── alembic/ # 数据库迁移
├── tests/ # 测试
└── CLAUDE.md # 模块文档
Novel App 完全采用 Riverpod 进行状态管理:
// Service Provider 示例
@Riverpod(keepAlive: true)
ApiServiceWrapper apiServiceWrapper(Ref ref) {
return ApiServiceWrapper();
}
// State Notifier Provider 示例
@riverpod
class BookshelfState extends _$BookshelfState {
@override
Future<List<Bookshelf>> build() async {
final repo = ref.watch(bookshelfRepositoryProvider);
return repo.getAll();
}
}Provider 分类:
- Service Providers (20+) - 服务层单例
- Repository Providers - 数据仓库
- StateNotifierProviders (30+) - 业务状态
- FutureProvider / StreamProvider - 异步数据流
所有数据访问通过 Repository 层:
class BookshelfRepository extends BaseRepository {
Future<List<Bookshelf>> getAll();
Future<void> add(Bookshelf bookshelf);
Future<void> delete(int id);
}使用 OpenAPI 自动生成的 Dart 客户端,通过 Riverpod Provider 注入:
@riverpod
Future<List<Novel>> searchNovels(
Ref ref,
String keyword,
List<String> sites,
) async {
final api = ref.watch(apiServiceWrapperProvider);
return api.search(keyword: keyword, sites: sites);
}Whimread 不再依赖服务端爬虫:章节列表与正文由 Flutter 端 Headless WebView + 本地 JS 提取脚本(lib/services/headless_webview_*.dart + site_scripts 表)直接获取;对字体反爬站点(如番茄)走 OCR 还原(OcrRestoreService + 系统 OCR-JS 模板,PP-OCRv6)。
- 客户端实现:见 lib/services/headless_webview_*.dart
- 站点脚本:
site_scripts表(v39,含chapter_list_ocr/chapter_content_ocr独立列)
仅客户端本地缓存(chapter_cache 表),服务端 PostgreSQL 不再缓存章节内容(2026-07-08 已删 novel_cache_tasks / novel_chapters_cache / chapter_list_cache 三表)。
from app.services.comfyui_client import ComfyUIClient
client = ComfyUIClient(base_url="http://localhost:8188")
result = await client.text_to_image(
prompt="a beautiful landscape",
negative_prompt="low quality",
width=1024,
height=1024,
)Novel App 内置客户端 DSL 工作流引擎(与 Dify 解耦,本地 YAML 解析),无需后端协作即可执行 AI 工作流。
核心组件:
lib/services/dsl_engine/
├── dsl_parser.dart # YAML DSL 解析
├── graph_engine.dart # 工作流图执行
├── variable_pool.dart # 变量管理
├── llm_provider.dart # LLM 调用
├── dsl_executor.dart # 统一执行入口
├── dsl_engine_config.dart # 配置管理
└── workflow_nodes/ # 工作流节点类型
使用示例:
final executor = DslExecutor(
llmConfig: LlmConfig(
baseUrl: 'https://api.deepseek.com/v1',
apiKey: 'sk-xxx',
),
defaultModel: 'deepseek-chat',
);
// Streaming 执行
await executor.runStreaming(
inputs: {'chapter_content': '...'},
onData: (chunk) => print(chunk),
onError: (e) => print(e),
onDone: () => print('完成'),
);DSL 工作流定义(assets/dsl/creater.yml):
app:
name: 角色提取
description: 从小说章节提取角色信息
workflow:
graph:
nodes:
- id: start
type: start
- id: extract
type: llm
prompt: |
从以下小说内容中提取角色信息:
{{chapter_content}}
model: deepseek-chat
- id: end
type: end
edges:
- from: start
to: extract
- from: extract
to: end- 数据库文件:
novel_reader.db - 版本:v39
- 主要表:bookshelf, chapter_cache, novel_chapters, characters, character_relationships, scene_illustrations, outlines, chat_sessions, chat_scenes, site_scripts, llm_configs, agent_memories
- 通过 Riverpod Provider 访问
- ORM:SQLAlchemy 2.0+
- 迁移工具:Alembic(head:
20260708_drop_cache_tables) - 主要表:text2img_task, image_to_video_task, client_logs, backup_files, novel_sync_data
- 注:缓存类表(
novel_cache_tasks/novel_chapters_cache/chapter_list_cache)已于 2026-07-08 删除
Whimread 已不使用服务端爬虫。要支持新站点,在前端编写 site_script:
- 打开 APP → Agent Chat
- 让 Agent 访问目标站点,自动生成 chapter_list / chapter_content 提取脚本
- Agent 通过
save_script工具落库到site_scripts表
直接编辑 site_scripts 表中对应行的 chapter_list_script / chapter_content_script(JS 字符串)。
对 PUA 字体反爬站点(如番茄),在 site_scripts 表将 chapter_list_ocr / chapter_content_ocr 置为 1,并创建对应 OCR 提取器(详见根 CLAUDE.md 2026-07-15 OCR 条目)。
在 Agent Chat 中调用 read_chapter_content / list_chapters 工具验证提取结果。
数据库版本由 lib/core/database/database_version.dart 管理。修改表结构时:
- 更新 Bump DB version
- 实现
onUpgrade迁移逻辑 - 运行测试验证
cd backend
# 修改 SQLAlchemy 模型
# app/models/your_model.py
# 生成迁移
alembic revision --autogenerate -m "添加新字段"
# 检查生成的迁移文件
# alembic/versions/xxxx_add_new_field.py
# 应用迁移
alembic upgrade head
# 回滚(如果需要)
alembic downgrade -1cd backend
# 静态检查
ruff check .
pylint app/
mypy app/
# 格式化
ruff format .
isort .
# 测试
pytest # 全部测试
pytest tests/unit/ # 单元测试
pytest -k "test_crawler" # 匹配名称
pytest --cov=app tests/ # 覆盖率cd novel_app
# 静态分析
flutter analyze
# 格式化
flutter format lib/
# 代码生成
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch --delete-conflicting-outputs # 监听模式
# 测试
flutter test # 全部测试
flutter test test/unit/ # 单元测试
flutter test --coverage # 覆盖率建议在提交前运行:
# 后端
cd backend && ruff check . && mypy app/
# 前端
cd novel_app && flutter analyze && flutter test项目使用 GitHub Actions 自动化测试和发布。
.github/workflows/flutter-ci.yml- PR/Push 到 main 时运行 CI.github/workflows/flutter-release.yml- 推送 tag 时构建并发布 APK
- 检出代码
- 安装 Flutter SDK
- 安装依赖(
flutter pub get) - 生成 Riverpod 代码
- 静态分析(
flutter analyze) - 运行单元测试(
flutter test)
- 检出代码
- 安装 Flutter SDK
- 安装依赖
- 生成代码
- 构建 Release APK
- 上传到 GitHub Releases
# 1. 更新 pubspec.yaml
# version: 1.7.6+55
# 2. 提交
git add novel_app/pubspec.yaml
git commit -m "chore: 发布版本 1.7.6"
# 3. 创建 tag
git tag v1.7.6
git push origin master --tags
# 4. GitHub Actions 自动构建并发布完整环境变量参考 .env.example。核心变量(v2.0.x):
| 变量名 | 必需 | 用途 |
|---|---|---|
NOVEL_API_TOKEN |
✅ | API 认证 Token(X-API-TOKEN) |
DATABASE_URL |
✅ | PostgreSQL 连接字符串 |
COMFYUI_API_URL |
ComfyUI 服务(文生图/图生视频) | |
DEBUG |
❌ | 调试模式 |
CORS_ORIGINS |
❌ | CORS 允许的源 |
HTTP_PROXY / HTTPS_PROXY |
❌ | 网络代理 |
注:早期文档提到的
NOVEL_ENABLED_SITES/SECRET_KEY/LOG_LEVEL/MAX_UPLOAD_SIZE/UPLOAD_DIR/VIDEO_GENERATION_TIMEOUT/HOST/PORT字段已在 v2.0.x 移除(爬虫功能 2026-07-08 整体下线)。
- 项目主页 - https://github.com/yunkst/Whimread
- API 文档 - http://localhost:3800/docs
- 后端模块文档 - backend/CLAUDE.md
- 前端模块文档 - novel_app/CLAUDE.md
- 部署指南 - deployment.md
- 日志使用指南 - logging-guidelines.md
- 创建分支 -
git checkout -b feature/your-feature - 编写代码 - 遵循代码规范
- 添加测试 - 确保新功能有测试覆盖
- 本地验证 - 运行
flutter analyze和flutter test - 提交 - 遵循 Conventional Commits
- 推送并创建 PR - 详细描述变更内容
- 代码审查 - 等待维护者审查
- 合并 - 通过 CI 后合并到 master
详细请参考 贡献指南。