全面架构重构:建立分层架构与高度可扩展的插件系统

后端重构:
- 新增分层架构:API Routes -> Services -> Repositories -> Infrastructure
- 彻底移除全局单例,全面采用 FastAPI 依赖注入
- 新增 api/ 目录拆分路由(proxies, plugins, scheduler, settings, stats)
- 新增 services/ 业务逻辑层:ProxyService, PluginService, SchedulerService, ValidatorService, SettingsService
- 新增 repositories/ 数据访问层:ProxyRepository, SettingsRepository, PluginSettingsRepository
- 新增 models/ 层:Pydantic Schemas + Domain Models
- 重写 core/config.py:采用 Pydantic Settings 管理配置
- 新增 core/db.py:基于 asynccontextmanager 的连接管理,支持数据库迁移
- 新增 core/exceptions.py:统一业务异常体系

插件系统重构(核心):
- 新增 core/plugin_system/:BaseCrawlerPlugin + PluginRegistry
- 采用显式注册模式(装饰器 + plugins/__init__.py),类型安全、测试友好
- 新增 plugins/base.py:BaseHTTPPlugin 通用 HTTP 爬虫基类
- 迁移全部 7 个插件到新架构(fate0, proxylist_download, ip3366, ip89, kuaidaili, speedx, yundaili)
- 插件状态持久化到 plugin_settings 表

任务调度重构:
- 新增 core/tasks/queue.py:ValidationQueue + WorkerPool
- 解耦爬取与验证:爬虫只负责爬取,代理提交队列后由 Worker 异步验证
- 调度器定时从数据库拉取存量代理并分批投入验证队列

前端调整:
- 新增 frontend/src/services/ 层拆分 API 调用逻辑
- 调整 stores/ 和 views/ 使用 Service 层
- 保持 API 兼容性,页面无需大幅修改

其他:
- 新增 main.py 作为新入口
- 新增 DESIGN.md 架构设计文档
- 更新 requirements.txt 增加 pydantic-settings
This commit is contained in:
祀梦
2026-04-02 11:55:05 +08:00
parent a79f78b338
commit 209a744d94
56 changed files with 2891 additions and 2095 deletions

View File

@@ -0,0 +1,102 @@
"""设置数据访问层"""
import json
import aiosqlite
from typing import Optional, Dict, Any
from core.log import logger
DEFAULT_SETTINGS = {
"crawl_timeout": 30,
"validation_timeout": 10,
"max_retries": 3,
"default_concurrency": 50,
"min_proxy_score": 0,
"proxy_expiry_days": 7,
"auto_validate": True,
"validate_interval_minutes": 30,
}
class SettingsRepository:
"""系统设置 Repository"""
@staticmethod
async def get_all(db: aiosqlite.Connection) -> Dict[str, Any]:
settings = DEFAULT_SETTINGS.copy()
try:
async with db.execute("SELECT key, value FROM settings") as cursor:
rows = await cursor.fetchall()
for key, value in rows:
# 类型转换
default = DEFAULT_SETTINGS.get(key)
if isinstance(default, bool):
settings[key] = value.lower() == "true"
elif isinstance(default, int):
settings[key] = int(value)
else:
settings[key] = value
except Exception as e:
logger.error(f"get_all settings failed: {e}")
return settings
@staticmethod
async def save(db: aiosqlite.Connection, settings: Dict[str, Any]) -> bool:
try:
for key, value in settings.items():
await db.execute(
"""
INSERT INTO settings (key, value, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(key) DO UPDATE SET
value = excluded.value,
updated_at = CURRENT_TIMESTAMP
""",
(key, str(value)),
)
await db.commit()
return True
except Exception as e:
logger.error(f"save settings failed: {e}")
return False
class PluginSettingsRepository:
"""插件设置 Repository"""
@staticmethod
async def get_enabled(db: aiosqlite.Connection, plugin_id: str) -> Optional[bool]:
async with db.execute(
"SELECT enabled FROM plugin_settings WHERE plugin_id = ?", (plugin_id,)
) as cursor:
row = await cursor.fetchone()
if row:
return bool(row[0])
return None
@staticmethod
async def set_enabled(db: aiosqlite.Connection, plugin_id: str, enabled: bool) -> bool:
try:
await db.execute(
"""
INSERT INTO plugin_settings (plugin_id, enabled, created_at, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT(plugin_id) DO UPDATE SET
enabled = excluded.enabled,
updated_at = CURRENT_TIMESTAMP
""",
(plugin_id, int(enabled)),
)
await db.commit()
return True
except Exception as e:
logger.error(f"set_enabled failed for {plugin_id}: {e}")
return False
@staticmethod
async def list_all(db: aiosqlite.Connection) -> Dict[str, bool]:
result = {}
async with db.execute("SELECT plugin_id, enabled FROM plugin_settings") as cursor:
rows = await cursor.fetchall()
for plugin_id, enabled in rows:
result[plugin_id] = bool(enabled)
return result