From b77641f05905e69fe26be7fe9bbebde0015aa980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=80=E6=A2=A6?= <3501646051@qq.com> Date: Thu, 2 Apr 2026 12:26:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E9=9D=A2=E6=B8=85=E7=90=86=E5=86=97?= =?UTF-8?q?=E4=BD=99=E4=B8=8E=E8=BF=87=E5=BA=A6=E5=88=86=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端优化: - 合并 api/routes/stats.py 到 api/routes/proxies.py,统计接口变更为 /api/proxies/stats - 内联 services/settings_service.py:settings.py 和 scheduler.py 直接使用 SettingsRepository - 简化 repositories/proxy_repo.py:提取 _row_to_proxy 辅助函数,消除重复构造代码 - 更新 api/lifespan.py 和 api/deps.py,移除对 settings_service 的依赖 - 从 requirements.txt 移除 websockets 依赖(已废弃的 WebSocket 功能残留) 前端适配: - 更新 frontend/src/api/index.js:stats 接口路径同步为 /api/proxies/stats - 清理 api/index.js 中未使用的 createRequestConfig 和多余 JSDoc 注释 脚本优化: - 移除 script/stop.bat 末尾的 pause,避免自动化调用时挂起 --- api/deps.py | 5 --- api/lifespan.py | 10 +++--- api/routes/__init__.py | 3 +- api/routes/proxies.py | 20 +++++++++-- api/routes/scheduler.py | 24 +++++++------ api/routes/settings.py | 20 +++++------ api/routes/stats.py | 30 ---------------- frontend/src/api/index.js | 70 +++--------------------------------- repositories/proxy_repo.py | 58 +++++++++--------------------- requirements.txt | 1 - script/stop.bat | 2 -- services/settings_service.py | 19 ---------- 12 files changed, 68 insertions(+), 194 deletions(-) delete mode 100644 api/routes/stats.py delete mode 100644 services/settings_service.py diff --git a/api/deps.py b/api/deps.py index 4aa20f9..6b08e9e 100644 --- a/api/deps.py +++ b/api/deps.py @@ -2,7 +2,6 @@ from fastapi import Request from services.proxy_service import ProxyService from services.plugin_service import PluginService -from services.settings_service import SettingsService from services.scheduler_service import SchedulerService from services.validator_service import ValidatorService from repositories.proxy_repo import ProxyRepository @@ -18,10 +17,6 @@ def get_plugin_service() -> PluginService: return PluginService() -def get_settings_service() -> SettingsService: - return SettingsService() - - def get_scheduler_service(request: Request) -> SchedulerService: return request.app.state.scheduler_service diff --git a/api/lifespan.py b/api/lifespan.py index 358f859..7ab0bcd 100644 --- a/api/lifespan.py +++ b/api/lifespan.py @@ -1,10 +1,13 @@ """应用生命周期管理""" from contextlib import asynccontextmanager from fastapi import FastAPI -from core.db import init_db +from core.db import init_db, get_db from core.config import settings as app_settings from core.log import logger from api.deps import create_scheduler_service +from repositories.settings_repo import SettingsRepository + +settings_repo = SettingsRepository() @asynccontextmanager @@ -19,10 +22,9 @@ async def lifespan(app: FastAPI): app.state.validation_queue = scheduler_service.validation_queue # 加载设置并决定是否启动调度器 - from services.settings_service import SettingsService - settings_service = SettingsService() try: - settings = await settings_service.get_settings() + async with get_db() as db: + settings = await settings_repo.get_all(db) scheduler_service.interval_minutes = settings.get( "validate_interval_minutes", app_settings.validator_timeout ) diff --git a/api/routes/__init__.py b/api/routes/__init__.py index 969f8a3..67697cf 100644 --- a/api/routes/__init__.py +++ b/api/routes/__init__.py @@ -1,8 +1,7 @@ from fastapi import APIRouter -from . import stats, proxies, plugins, scheduler, settings +from . import proxies, plugins, scheduler, settings api_router = APIRouter() -api_router.include_router(stats.router) api_router.include_router(proxies.router) api_router.include_router(plugins.router) api_router.include_router(scheduler.router) diff --git a/api/routes/proxies.py b/api/routes/proxies.py index d803be0..57b5258 100644 --- a/api/routes/proxies.py +++ b/api/routes/proxies.py @@ -1,9 +1,11 @@ -"""代理相关路由""" +"""代理相关路由(含统计信息)""" from typing import Optional from fastapi import APIRouter, Depends, Query from services.proxy_service import ProxyService +from services.scheduler_service import SchedulerService from models.schemas import ProxyListRequest, BatchDeleteRequest -from api.deps import get_proxy_service +from api.deps import get_proxy_service, get_scheduler_service +from core.log import logger router = APIRouter(prefix="/api/proxies", tags=["proxies"]) @@ -16,6 +18,20 @@ def error_response(message: str, code: int = 500): return {"code": code, "message": message, "data": None} +@router.get("/stats") +async def get_stats( + proxy_service: ProxyService = Depends(get_proxy_service), + scheduler_service: SchedulerService = Depends(get_scheduler_service), +): + try: + stats = await proxy_service.get_stats() + stats["scheduler_running"] = scheduler_service.running + return success_response("获取统计信息成功", stats) + except Exception as e: + logger.error(f"Get stats failed: {e}") + return error_response("获取统计信息失败") + + @router.post("") async def list_proxies( request: ProxyListRequest, diff --git a/api/routes/scheduler.py b/api/routes/scheduler.py index c9fe46e..20f13a5 100644 --- a/api/routes/scheduler.py +++ b/api/routes/scheduler.py @@ -1,11 +1,13 @@ """调度器相关路由""" from fastapi import APIRouter, Depends from services.scheduler_service import SchedulerService -from services.settings_service import SettingsService +from repositories.settings_repo import SettingsRepository +from core.db import get_db from api.deps import get_scheduler_service from core.log import logger router = APIRouter(prefix="/api/scheduler", tags=["scheduler"]) +settings_repo = SettingsRepository() def success_response(message: str, data=None): @@ -25,11 +27,11 @@ async def start_scheduler( return success_response("验证调度器已在运行", {"running": True}) await scheduler.start() # 持久化设置 - settings_service = SettingsService() - settings = await settings_service.get_settings() - settings["auto_validate"] = True - from models.schemas import SettingsSchema - await settings_service.save_settings(SettingsSchema(**settings)) + async with get_db() as db: + settings = await settings_repo.get_all(db) + settings["auto_validate"] = True + from models.schemas import SettingsSchema + await settings_repo.save(db, SettingsSchema(**settings).model_dump()) return success_response("验证调度器已启动", {"running": True}) except Exception as e: logger.error(f"Start scheduler failed: {e}") @@ -45,11 +47,11 @@ async def stop_scheduler( return success_response("验证调度器未运行", {"running": False}) await scheduler.stop() # 持久化设置 - settings_service = SettingsService() - settings = await settings_service.get_settings() - settings["auto_validate"] = False - from models.schemas import SettingsSchema - await settings_service.save_settings(SettingsSchema(**settings)) + async with get_db() as db: + settings = await settings_repo.get_all(db) + settings["auto_validate"] = False + from models.schemas import SettingsSchema + await settings_repo.save(db, SettingsSchema(**settings).model_dump()) return success_response("验证调度器已停止", {"running": False}) except Exception as e: logger.error(f"Stop scheduler failed: {e}") diff --git a/api/routes/settings.py b/api/routes/settings.py index 9c538a9..3f60548 100644 --- a/api/routes/settings.py +++ b/api/routes/settings.py @@ -1,11 +1,12 @@ """设置相关路由""" -from fastapi import APIRouter, Depends -from services.settings_service import SettingsService +from fastapi import APIRouter +from core.db import get_db +from repositories.settings_repo import SettingsRepository from models.schemas import SettingsSchema -from api.deps import get_settings_service from core.log import logger router = APIRouter(prefix="/api/settings", tags=["settings"]) +settings_repo = SettingsRepository() def success_response(message: str, data=None): @@ -17,9 +18,10 @@ def error_response(message: str, code: int = 500): @router.get("") -async def get_settings(service: SettingsService = Depends(get_settings_service)): +async def get_settings(): try: - settings = await service.get_settings() + async with get_db() as db: + settings = await settings_repo.get_all(db) return success_response("获取设置成功", settings) except Exception as e: logger.error(f"Get settings failed: {e}") @@ -27,12 +29,10 @@ async def get_settings(service: SettingsService = Depends(get_settings_service)) @router.post("") -async def save_settings( - request: SettingsSchema, - service: SettingsService = Depends(get_settings_service), -): +async def save_settings(request: SettingsSchema): try: - success = await service.save_settings(request) + async with get_db() as db: + success = await settings_repo.save(db, request.model_dump()) if not success: return error_response("保存设置失败") return success_response("保存设置成功", request.model_dump()) diff --git a/api/routes/stats.py b/api/routes/stats.py deleted file mode 100644 index 0e44013..0000000 --- a/api/routes/stats.py +++ /dev/null @@ -1,30 +0,0 @@ -"""统计信息路由""" -from fastapi import APIRouter, Depends -from services.proxy_service import ProxyService -from services.scheduler_service import SchedulerService -from api.deps import get_proxy_service, get_scheduler_service -from core.log import logger - -router = APIRouter(prefix="/api/stats", tags=["stats"]) - - -def success_response(message: str, data=None): - return {"code": 200, "message": message, "data": data} - - -def error_response(message: str, code: int = 500): - return {"code": code, "message": message, "data": None} - - -@router.get("") -async def get_stats( - proxy_service: ProxyService = Depends(get_proxy_service), - scheduler_service: SchedulerService = Depends(get_scheduler_service), -): - try: - stats = await proxy_service.get_stats() - stats["scheduler_running"] = scheduler_service.running - return success_response("获取统计信息成功", stats) - except Exception as e: - logger.error(f"Get stats failed: {e}") - return error_response("获取统计信息失败") diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 1d18286..174edb2 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -43,11 +43,8 @@ api.interceptors.response.use( } ) -/** - * 清理请求参数,移除 null/undefined/空字符串 - * @param {object} params - * @returns {object} - */ +// ==================== API 模块 ==================== + function cleanParams(params) { const cleaned = {} Object.keys(params).forEach((key) => { @@ -59,52 +56,20 @@ function cleanParams(params) { return cleaned } -/** - * 生成请求配置,支持 AbortSignal - * @param {AbortSignal} [signal] - * @returns {object} - */ -function createRequestConfig(signal) { - return signal ? { signal } : {} -} - -// ==================== API 模块 ==================== - export const statsAPI = { - /** @returns {Promise>} */ - getStats: () => api.get('/api/stats') + getStats: () => api.get('/api/proxies/stats') } export const proxiesAPI = { - /** - * @param {object} params - * @param {AbortSignal} [signal] - * @returns {Promise>} - */ getProxies: (params, signal) => - api.post('/api/proxies', cleanParams(params), createRequestConfig(signal)), + api.post('/api/proxies', cleanParams(params), signal ? { signal } : {}), - /** - * @param {string} ip - * @param {number|string} port - * @returns {Promise>} - */ deleteProxy: (ip, port) => api.delete(`/api/proxies/${ip}/${port}`), - /** - * @param {Array<[string, number|string]>} proxies - * @returns {Promise>} - */ batchDeleteProxies: (proxies) => api.post('/api/proxies/batch-delete', { proxies }), - /** @returns {Promise>} */ cleanInvalidProxies: () => api.delete('/api/proxies/clean-invalid'), - /** - * @param {string} format - * @param {string|null} protocol - * @returns {Promise} - */ exportProxies: (format, protocol) => api.get(`/api/proxies/export/${format}`, { params: protocol ? { protocol } : {}, responseType: 'blob' @@ -112,48 +77,21 @@ export const proxiesAPI = { } export const pluginsAPI = { - /** @returns {Promise>} */ getPlugins: () => api.get('/api/plugins'), - - /** - * @param {string|number} pluginId - * @param {boolean} enabled - * @returns {Promise>} - */ togglePlugin: (pluginId, enabled) => api.put(`/api/plugins/${pluginId}/toggle`, { enabled }), - - /** - * @param {string|number} pluginId - * @returns {Promise>} - */ crawlPlugin: (pluginId) => api.post(`/api/plugins/${pluginId}/crawl`), - - /** @returns {Promise>} */ crawlAll: () => api.post('/api/plugins/crawl-all') } export const schedulerAPI = { - /** @returns {Promise>} */ start: () => api.post('/api/scheduler/start'), - - /** @returns {Promise>} */ stop: () => api.post('/api/scheduler/stop'), - - /** @returns {Promise>} */ validateNow: () => api.post('/api/scheduler/validate-now'), - - /** @returns {Promise>} */ getStatus: () => api.get('/api/scheduler/status') } export const settingsAPI = { - /** @returns {Promise>} */ getSettings: () => api.get('/api/settings'), - - /** - * @param {object} data - * @returns {Promise>} - */ saveSettings: (data) => api.post('/api/settings', data) } diff --git a/repositories/proxy_repo.py b/repositories/proxy_repo.py index 1d3299b..6a3c2ce 100644 --- a/repositories/proxy_repo.py +++ b/repositories/proxy_repo.py @@ -23,6 +23,18 @@ def _to_datetime(value: Union[str, datetime, None]) -> Optional[datetime]: return None +def _row_to_proxy(row: Tuple) -> Proxy: + return Proxy( + ip=row[0], + port=row[1], + protocol=row[2], + score=row[3], + response_time_ms=row[4], + last_check=_to_datetime(row[5]), + created_at=_to_datetime(row[6]), + ) + + class ProxyRepository: """代理 Repository""" @@ -125,15 +137,7 @@ class ProxyRepository: ) as cursor: row = await cursor.fetchone() if row: - return Proxy( - ip=row[0], - port=row[1], - protocol=row[2], - score=row[3], - response_time_ms=row[4], - last_check=_to_datetime(row[5]), - created_at=_to_datetime(row[6]), - ) + return _row_to_proxy(row) return None @staticmethod @@ -143,15 +147,7 @@ class ProxyRepository: ) as cursor: row = await cursor.fetchone() if row: - return Proxy( - ip=row[0], - port=row[1], - protocol=row[2], - score=row[3], - response_time_ms=row[4], - last_check=_to_datetime(row[5]), - created_at=_to_datetime(row[6]), - ) + return _row_to_proxy(row) return None @staticmethod @@ -170,18 +166,7 @@ class ProxyRepository: async with db.execute(query, params) as cursor: rows = await cursor.fetchall() - return [ - Proxy( - ip=row[0], - port=row[1], - protocol=row[2], - score=row[3], - response_time_ms=row[4], - last_check=_to_datetime(row[5]), - created_at=_to_datetime(row[6]), - ) - for row in rows - ] + return [_row_to_proxy(row) for row in rows] @staticmethod async def list_paginated( @@ -223,18 +208,7 @@ class ProxyRepository: params.extend([page_size, offset]) async with db.execute(data_query, params) as cursor: rows = await cursor.fetchall() - proxies = [ - Proxy( - ip=row[0], - port=row[1], - protocol=row[2], - score=row[3], - response_time_ms=row[4], - last_check=_to_datetime(row[5]), - created_at=_to_datetime(row[6]), - ) - for row in rows - ] + proxies = [_row_to_proxy(row) for row in rows] return proxies, total @staticmethod diff --git a/requirements.txt b/requirements.txt index 3d20dbe..a96639b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ fastapi==0.104.1 uvicorn[standard]==0.24.0 -websockets==12.0 aiosqlite==0.19.0 aiohttp==3.9.1 aiohttp-socks==0.9.1 diff --git a/script/stop.bat b/script/stop.bat index b65e393..232c6cf 100644 --- a/script/stop.bat +++ b/script/stop.bat @@ -37,5 +37,3 @@ echo. echo === Done === echo All services have been stopped. echo. - -pause diff --git a/services/settings_service.py b/services/settings_service.py deleted file mode 100644 index 39da888..0000000 --- a/services/settings_service.py +++ /dev/null @@ -1,19 +0,0 @@ -"""系统设置业务服务""" -from typing import Any, Dict -from core.db import get_db -from repositories.settings_repo import SettingsRepository -from models.schemas import SettingsSchema - - -class SettingsService: - def __init__(self): - self.repo = SettingsRepository() - - async def get_settings(self) -> Dict[str, Any]: - async with get_db() as db: - return await self.repo.get_all(db) - - async def save_settings(self, data: SettingsSchema) -> bool: - settings_dict = data.model_dump() - async with get_db() as db: - return await self.repo.save(db, settings_dict)