重构: 迁移后端代码到 app 目录,前端移动到 WebUI,添加完整测试套件

主要变更:
- 后端代码从根目录迁移到 app/ 目录
- 前端代码从 frontend/ 重命名为 WebUI/
- 更新所有导入路径以适配新结构
- 提取公共 API 响应函数到 app/api/common.py
- 精简验证器服务代码
- 更新启动脚本和文档

测试:
- 新增完整测试套件 (tests/)
- 单元测试: 模型、仓库层
- 集成测试: 覆盖所有 22+ API 端点
- E2E 测试: 4个完整工作流场景
- 添加 pytest 配置和测试运行脚本
This commit is contained in:
祀梦
2026-04-04 13:32:36 +08:00
parent df3cc87f88
commit 38bd66128b
109 changed files with 2017 additions and 548 deletions

142
DESIGN.md
View File

@@ -309,77 +309,87 @@ Store 只负责:
```
ProxyPool/
├── api/ # FastAPI 入口和路由
│ ├── __init__.py
│ ├── main.py # 应用工厂
│ ├── lifespan.py # 生命周期管理
│ ├── deps.py # 依赖注入
│ ├── errors.py # 统一异常
│ └── routes/
│ ├── __init__.py
│ ├── proxies.py
│ ├── plugins.py
│ ├── scheduler.py
│ └── settings.py
├── main.py # 项目入口
├── requirements.txt # Python 依赖
├── .env.example # 环境变量示例
├── core/ # 基础设施
│ ├── __init__.py
│ ├── config.py # Pydantic Settings
│ ├── log.py # 日志
│ ├── db.py # 数据库连接池/上下文
│ └── exceptions.py # 业务异常
├── models/ # 数据模型
│ ├── __init__.py
│ ├── schemas.py # Pydantic 模型
│ └── domain.py # 领域模型ProxyRaw, PluginInfo 等)
├── repositories/ # 数据访问层
│ ├── __init__.py
│ └── proxy_repo.py # ProxyRepository
├── services/ # 业务逻辑层
│ ├── __init__.py
│ ├── proxy_service.py
│ ├── plugin_service.py
│ ├── scheduler_service.py
│ └── validator_service.py
├── core/ # 任务与插件系统
│ ├── plugin_system/
├── app/ # 后端代码
│ ├── api/ # FastAPI 入口和路由
│ │ ├── __init__.py
│ │ ├── base.py # BaseCrawlerPlugin
│ │ ── registry.py # 插件注册中心
└── tasks/
│ │ ├── main.py # 应用工厂
│ │ ── lifespan.py # 生命周期管理
│ ├── deps.py # 依赖注入
│ │ ├── errors.py # 统一异常
│ │ └── routes/
│ │ ├── __init__.py
│ │ ├── proxies.py
│ │ ├── plugins.py
│ │ ├── scheduler.py
│ │ └── settings.py
│ │
│ ├── core/ # 基础设施
│ │ ├── __init__.py
│ │ ├── config.py # Pydantic Settings
│ │ ├── log.py # 日志
│ │ ├── db.py # 数据库连接池/上下文
│ │ ├── exceptions.py # 业务异常
│ │ ├── plugin_system/ # 插件系统
│ │ │ ├── __init__.py
│ │ │ ├── base.py # BaseCrawlerPlugin
│ │ │ └── registry.py # 插件注册中心
│ │ └── tasks/ # 任务队列
│ │ ├── __init__.py
│ │ └── queue.py # ValidationQueue
│ │
│ ├── models/ # 数据模型
│ │ ├── __init__.py
│ │ ├── schemas.py # Pydantic 模型
│ │ └── domain.py # 领域模型
│ │
│ ├── repositories/ # 数据访问层
│ │ ├── __init__.py
│ │ ├── proxy_repo.py
│ │ ├── settings_repo.py
│ │ └── task_repo.py
│ │
│ ├── services/ # 业务逻辑层
│ │ ├── __init__.py
│ │ ├── proxy_service.py
│ │ ├── plugin_service.py
│ │ ├── scheduler_service.py
│ │ └── validator_service.py
│ │
│ └── plugins/ # 爬虫插件
│ ├── __init__.py
│ ├── queue.py # ValidationQueue
── workers.py # Worker Pool
│ ├── base.py # 通用抓取基类
── fate0.py
│ ├── kuaidaili.py
│ ├── ip3366.py
│ ├── ip89.py
│ ├── speedx.py
│ ├── yundaili.py
│ ├── proxylist_download.py
│ └── proxyscrape.py
├── plugins/ # 爬虫插件
│ ├── __init__.py
│ ├── base.py # 通用抓取基类HTTP 请求封装
│ ├── fate0.py
│ ├── proxylist_download.py
└── ...
├── frontend/ # Vue3 前端
── src/
├── services/ # 新增
│ ├── stores/
│ ├── api/
│ └── ...
├── WebUI/ # Vue3 前端
│ ├── src/
│ ├── api/ # API 封装
│ ├── stores/ # Pinia 状态管理
│ ├── views/ # 页面组件
│ ├── router/ # 路由配置
│ ├── components/ # 通用组件
└── style.css # 全局样式
── index.html
└── package.json
├── tests/ # 测试目录
│ ├── conftest.py
│ ├── unit/
│ └── integration/
├── script/
├── data/
├── db/
├── logs/
├── requirements.txt
├── .env.example
├── script/ # 启动脚本
├── db/ # 数据存储
├── logs/ # 日志文件
└── DESIGN.md # 本文档
```
@@ -426,11 +436,11 @@ ProxyPool/
假设要添加一个名为 `mynewsource` 的爬虫:
**Step 1**: 创建文件 `plugins/mynewsource.py`
**Step 1**: 创建文件 `app/plugins/mynewsource.py`
```python
from core.plugin_system import BaseCrawlerPlugin, ProxyRaw
from plugins.base import BaseHTTPPlugin # 可选:如果基于 HTTP 爬取
from app.core.plugin_system import BaseCrawlerPlugin, ProxyRaw
from app.plugins.base import BaseHTTPPlugin # 可选:如果基于 HTTP 爬取
class MyNewSourcePlugin(BaseHTTPPlugin):
name = "mynewsource"
@@ -450,11 +460,11 @@ class MyNewSourcePlugin(BaseHTTPPlugin):
return results
```
**Step 2**: 在 `plugins/__init__.py` 中注册
**Step 2**: 在 `app/plugins/__init__.py` 中注册
```python
from .mynewsource import MyNewSourcePlugin
from core.plugin_system import registry
from app.core.plugin_system import registry
registry.register(MyNewSourcePlugin)
```