refactor: 清理后端冗余代码和未用依赖

This commit is contained in:
2026-05-19 15:46:31 +08:00
parent d30dd0da25
commit 3240027d48
11 changed files with 32 additions and 111 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ CapacityReport 用于每周导入 Excel/CSV 数据,按 `Configure.json` 与 `R
- 后端:FastAPI + Uvicorn + Supervisor - 后端:FastAPI + Uvicorn + Supervisor
- 前端:Vue 3 + TypeScript + Vite + Naive UI - 前端:Vue 3 + TypeScript + Vite + Naive UI
- 数据库:MySQL 8.0+,通过 PyMySQL + SQLAlchemy 访问 - 数据库:MySQL 8.0+,通过 PyMySQL 访问
- 数据处理:Pandas + OpenPyXL - 数据处理:Pandas + OpenPyXL
- 部署:Docker + Docker Compose - 部署:Docker + Docker Compose
+2 -25
View File
@@ -56,11 +56,8 @@ def _dataframe_from_table(db: DatabaseManager, table_name: str) -> pd.DataFrame:
@router.post("/api/database/test") @router.post("/api/database/test")
async def test_database(): async def test_database():
db = DatabaseManager(state.config) db = DatabaseManager(state.config)
try: success, message = db.test_connection()
success, message = db.test_connection() return {"success": success, "message": message}
return {"success": success, "message": message}
finally:
db.dispose()
@router.get("/api/database/info") @router.get("/api/database/info")
@@ -70,8 +67,6 @@ async def get_database_info():
return {"success": True, **db.get_server_info()} return {"success": True, **db.get_server_info()}
except Exception as exc: except Exception as exc:
return {"success": False, "error": str(exc)} return {"success": False, "error": str(exc)}
finally:
db.dispose()
@router.get("/api/database/tables") @router.get("/api/database/tables")
@@ -82,8 +77,6 @@ async def get_tables():
return {"tables": db.get_tables()} return {"tables": db.get_tables()}
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/database/table/info") @router.post("/api/database/table/info")
@@ -93,8 +86,6 @@ async def get_table_info(table_name: str = Body(..., embed=True)):
return db.get_table_info(table_name) return db.get_table_info(table_name)
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/database/table/data") @router.post("/api/database/table/data")
@@ -110,8 +101,6 @@ async def query_table_data(
return db.query_table(table_name, page, page_size, order_by=order_by, order_dir=order_dir) return db.query_table(table_name, page, page_size, order_by=order_by, order_dir=order_dir)
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/database/table/query") @router.post("/api/database/table/query")
@@ -135,8 +124,6 @@ async def query_table_with_filter(
) )
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/database/table/truncate") @router.post("/api/database/table/truncate")
@@ -147,8 +134,6 @@ async def truncate_table(table_name: str = Body(..., embed=True)):
return {"success": True, "message": f"表 {table_name} 已清空"} return {"success": True, "message": f"表 {table_name} 已清空"}
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/database/table/drop") @router.post("/api/database/table/drop")
@@ -159,8 +144,6 @@ async def drop_table(table_name: str = Body(..., embed=True)):
return {"success": True, "message": f"表 {table_name} 已删除"} return {"success": True, "message": f"表 {table_name} 已删除"}
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/database/table/drop-all") @router.post("/api/database/table/drop-all")
@@ -176,8 +159,6 @@ async def drop_all_tables():
} }
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/database/execute") @router.post("/api/database/execute")
@@ -192,8 +173,6 @@ async def execute_sql(sql: str = Body(..., embed=True)):
raise raise
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
@router.post("/api/download") @router.post("/api/download")
@@ -226,8 +205,6 @@ async def download_table(
raise raise
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
finally:
db.dispose()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename_prefix = requested_tables[0] if len(requested_tables) == 1 else "tables" filename_prefix = requested_tables[0] if len(requested_tables) == 1 else "tables"
+6 -10
View File
@@ -19,15 +19,12 @@ async def health_check():
try: try:
db_manager = DatabaseManager(state.config) db_manager = DatabaseManager(state.config)
try: server_info = db_manager.get_server_info()
server_info = db_manager.get_server_info() checks["database"] = {
checks["database"] = { "status": "ok",
"status": "ok", "version": server_info.get("version", "unknown"),
"version": server_info.get("version", "unknown"), "load_data_infile": server_info.get("load_data_infile", False),
"load_data_infile": server_info.get("load_data_infile", False), }
}
finally:
db_manager.dispose()
except Exception as exc: except Exception as exc:
checks["database"] = {"status": "error", "message": str(exc)} checks["database"] = {"status": "error", "message": str(exc)}
@@ -39,4 +36,3 @@ async def health_check():
"uptime_pid": os.getpid(), "uptime_pid": os.getpid(),
"checks": checks, "checks": checks,
} }
-2
View File
@@ -4,7 +4,6 @@ import hashlib
import hmac import hmac
import json import json
import time import time
from pathlib import Path
from typing import Optional from typing import Optional
from app.config import BASE_DIR from app.config import BASE_DIR
@@ -81,4 +80,3 @@ def _sign(header: str, payload: str) -> str:
hashlib.sha256, hashlib.sha256,
).digest() ).digest()
return base64.urlsafe_b64encode(raw).decode().rstrip("=") return base64.urlsafe_b64encode(raw).decode().rstrip("=")
+1 -48
View File
@@ -2,14 +2,9 @@
数据库连接与操作模块 - 性能优化版 数据库连接与操作模块 - 性能优化版
""" """
import pymysql import pymysql
from urllib.parse import quote
from contextlib import contextmanager from contextlib import contextmanager
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool
from app.config import AppConfig from app.config import AppConfig
@@ -18,33 +13,6 @@ class DatabaseManager:
def __init__(self, config: AppConfig): def __init__(self, config: AppConfig):
self.config = config self.config = config
self._engine: Optional[sqlalchemy.Engine] = None
@property
def engine(self) -> sqlalchemy.Engine:
"""获取 SQLAlchemy 引擎(带连接池)- 优化配置"""
if self._engine is None:
mysql = self.config.mysql
self._engine = create_engine(
f'mysql+pymysql://'
f'{quote(mysql.user)}:'
f'{quote(mysql.passwd)}@'
f'{quote(mysql.host)}:'
f'{mysql.port}/'
f'{quote(mysql.dbname)}?charset=utf8mb4',
poolclass=QueuePool,
pool_size=10, # 增大连接池
max_overflow=20, # 增大溢出连接
pool_pre_ping=True,
pool_recycle=3600,
echo=False,
# 性能优化参数
connect_args={
'local_infile': True, # 允许 LOAD DATA LOCAL
'autocommit': False,
}
)
return self._engine
@contextmanager @contextmanager
def get_connection(self): def get_connection(self):
@@ -230,15 +198,6 @@ class DatabaseManager:
"total_pages": (total + page_size - 1) // page_size "total_pages": (total + page_size - 1) // page_size
} }
def delete_rows(self, table_name: str, condition: str, params: List[Any]) -> int:
"""删除符合条件的行"""
with self.get_connection() as conn:
with conn.cursor() as cursor:
sql = f"DELETE FROM `{table_name}` WHERE {condition}"
cursor.execute(sql, params)
conn.commit()
return cursor.rowcount
def truncate_table(self, table_name: str) -> bool: def truncate_table(self, table_name: str) -> bool:
"""清空表""" """清空表"""
with self.get_connection() as conn: with self.get_connection() as conn:
@@ -272,7 +231,7 @@ class DatabaseManager:
try: try:
cursor.execute(f"DROP TABLE IF EXISTS `{table}`") cursor.execute(f"DROP TABLE IF EXISTS `{table}`")
dropped_tables.append(table) dropped_tables.append(table)
except Exception as e: except Exception:
# 记录错误但继续删除其他表 # 记录错误但继续删除其他表
pass pass
@@ -434,9 +393,3 @@ class DatabaseManager:
with conn.cursor() as cursor: with conn.cursor() as cursor:
cursor.execute(sql) cursor.execute(sql)
conn.commit() conn.commit()
def dispose(self):
"""释放连接池"""
if self._engine:
self._engine.dispose()
self._engine = None
+1 -1
View File
@@ -49,7 +49,7 @@ class HistoryManager:
"""加载历史记录""" """加载历史记录"""
try: try:
return json.loads(HISTORY_FILE.read_text(encoding='utf-8')) return json.loads(HISTORY_FILE.read_text(encoding='utf-8'))
except: except (OSError, json.JSONDecodeError):
return [] return []
def _save(self, records: List[Dict[str, Any]]): def _save(self, records: List[Dict[str, Any]]):
-2
View File
@@ -220,8 +220,6 @@ class DataProcessor:
finally: finally:
# 清理临时目录 # 清理临时目录
self._cleanup_temp_dir() self._cleanup_temp_dir()
# 释放数据库连接
self.db.dispose()
return self.results return self.results
+12 -19
View File
@@ -8,10 +8,12 @@ CapacityReport 统一构建脚本
import os import os
import sys import sys
import json import json
import platform
import shutil import shutil
import subprocess import subprocess
import re import re
import tarfile import tarfile
import traceback
from pathlib import Path from pathlib import Path
# ============================================================================ # ============================================================================
@@ -37,9 +39,7 @@ MYSQL_PORT = 3306
# 应用端口映射 # 应用端口映射
APP_PORT_HOST = 19081 APP_PORT_HOST = 19081
APP_PORT_CONTAINER = 9081
MYSQL_PORT_HOST = 13306 MYSQL_PORT_HOST = 13306
MYSQL_PORT_CONTAINER = 3306
# ============================================================================ # ============================================================================
# 脚本目录配置 # 脚本目录配置
@@ -51,14 +51,12 @@ DIST_DIR = PROJECT_ROOT / "dist"
BUILD_DIR = PROJECT_ROOT / "build" BUILD_DIR = PROJECT_ROOT / "build"
TEMP_BUILD_DIR = BUILD_DIR / "temp" TEMP_BUILD_DIR = BUILD_DIR / "temp"
# 颜色输出(Windows 支持)
import platform
if platform.system() == 'Windows': if platform.system() == 'Windows':
try: try:
import ctypes import ctypes
kernel32 = ctypes.windll.kernel32 kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
except: except OSError:
pass pass
class Colors: class Colors:
@@ -95,7 +93,7 @@ def run_cmd(cmd, check=True, capture_output=False):
text=True text=True
) )
return result return result
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError:
if check: if check:
print_error(f"命令执行失败: {' '.join(cmd)}") print_error(f"命令执行失败: {' '.join(cmd)}")
sys.exit(1) sys.exit(1)
@@ -198,7 +196,7 @@ def image_exists(image_name):
check=True check=True
) )
return image_name in result.stdout return image_name in result.stdout
except: except (subprocess.CalledProcessError, FileNotFoundError):
return False return False
def format_size(size_bytes): def format_size(size_bytes):
@@ -668,14 +666,10 @@ def build_full_package():
sys.exit(1) sys.exit(1)
# 检查 Docker Compose # 检查 Docker Compose
docker_compose_cmd = None
try: try:
subprocess.run(["docker", "compose", "version"], capture_output=True, check=True) subprocess.run(["docker", "compose", "version"], capture_output=True, check=True)
docker_compose_cmd = "docker compose" except (subprocess.CalledProcessError, FileNotFoundError):
except: if not check_command("docker-compose"):
if check_command("docker-compose"):
docker_compose_cmd = "docker-compose"
else:
print_error("未检测到 Docker Compose") print_error("未检测到 Docker Compose")
sys.exit(1) sys.exit(1)
@@ -689,14 +683,14 @@ def build_full_package():
if not image_exists(python_image): if not image_exists(python_image):
print_info(f"Python 镜像不存在,正在拉取: {python_image}") print_info(f"Python 镜像不存在,正在拉取: {python_image}")
run_cmd(["docker", "pull", python_image]) run_cmd(["docker", "pull", python_image])
print_info(f"Python 镜像拉取完成") print_info("Python 镜像拉取完成")
else: else:
print_info(f"Python 镜像已存在: {python_image}") print_info(f"Python 镜像已存在: {python_image}")
if not image_exists(mysql_image): if not image_exists(mysql_image):
print_info(f"MySQL 镜像不存在,正在拉取: {mysql_image}") print_info(f"MySQL 镜像不存在,正在拉取: {mysql_image}")
run_cmd(["docker", "pull", mysql_image]) run_cmd(["docker", "pull", mysql_image])
print_info(f"MySQL 镜像拉取完成") print_info("MySQL 镜像拉取完成")
else: else:
print_info(f"MySQL 镜像已存在: {mysql_image}") print_info(f"MySQL 镜像已存在: {mysql_image}")
@@ -728,7 +722,7 @@ def build_full_package():
if dockerignore_build.read_text() == dockerignore_root.read_text(): if dockerignore_build.read_text() == dockerignore_root.read_text():
dockerignore_root.unlink() dockerignore_root.unlink()
print_info("已清理临时 .dockerignore") print_info("已清理临时 .dockerignore")
except: except OSError:
pass pass
# 标记 MySQL 镜像(提取版本号) # 标记 MySQL 镜像(提取版本号)
@@ -844,7 +838,7 @@ def build_update_package():
if not image_exists(python_image): if not image_exists(python_image):
print_info(f"Python 镜像不存在,正在拉取: {python_image}") print_info(f"Python 镜像不存在,正在拉取: {python_image}")
run_cmd(["docker", "pull", python_image]) run_cmd(["docker", "pull", python_image])
print_info(f"Python 镜像拉取完成") print_info("Python 镜像拉取完成")
else: else:
print_info(f"Python 镜像已存在: {python_image}") print_info(f"Python 镜像已存在: {python_image}")
@@ -876,7 +870,7 @@ def build_update_package():
if dockerignore_build.read_text() == dockerignore_root.read_text(): if dockerignore_build.read_text() == dockerignore_root.read_text():
dockerignore_root.unlink() dockerignore_root.unlink()
print_info("已清理临时 .dockerignore") print_info("已清理临时 .dockerignore")
except: except OSError:
pass pass
# 导出镜像 # 导出镜像
@@ -992,6 +986,5 @@ if __name__ == "__main__":
sys.exit(1) sys.exit(1)
except Exception as e: except Exception as e:
print_error(f"构建失败: {e}") print_error(f"构建失败: {e}")
import traceback
traceback.print_exc() traceback.print_exc()
sys.exit(1) sys.exit(1)
+8 -1
View File
@@ -1,5 +1,12 @@
# 项目上下文记录 # 项目上下文记录
## 2026-05-19:清理后端冗余代码和未用依赖
- `app/database.py` 移除未使用的 SQLAlchemy 连接池、`engine` 属性、`dispose()` 空释放路径和未引用的 `delete_rows()`;数据库访问统一保留现有 PyMySQL 上下文连接。
- `app/api/routers/database.py`、`app/api/routers/health.py` 和 `app/processor.py` 同步去除无效 `dispose()` 调用,避免保留没有实际资源释放意义的样板代码。
- `requirements.txt`、`run.bat` 和 `README.md` 移除 SQLAlchemy 依赖和说明;`build/build.py` 清理无用端口常量、内联导入和宽泛异常捕获。
- 已清理本地 `.ruff_cache/` 与重复的 `ReportScript.sql.bak`;已执行 `.venv\Scripts\python.exe -m compileall app build`、`uvx ruff check .`、`uvx vulture app build --min-confidence 80` 和 `npm run build`,均通过。
## 2026-05-19:移除旧版 HTML 前端和双端口托管 ## 2026-05-19:移除旧版 HTML 前端和双端口托管
- 删除 `frontend_old/` 旧版 HTML/CSS/JS 前端及其本地 Monaco 资源,项目只保留 Vue 3 新前端。 - 删除 `frontend_old/` 旧版 HTML/CSS/JS 前端及其本地 Monaco 资源,项目只保留 Vue 3 新前端。
@@ -324,7 +331,7 @@
### 依赖与清理 ### 依赖与清理
- Python 依赖保留当前代码实际使用项:`fastapi`、`uvicorn[standard]`、`python-multipart`、`pymysql`、`sqlalchemy`、`cryptography`、`pandas`、`openpyxl`、`chardet`、`supervisor`。 - Python 依赖保留当前代码实际使用项:`fastapi`、`uvicorn[standard]`、`python-multipart`、`pymysql`、`cryptography`、`pandas`、`openpyxl`、`chardet`、`supervisor`。
- 已移除未使用的 `sqlparse`、`aiofiles`、`python-dateutil`。 - 已移除未使用的 `sqlparse`、`aiofiles`、`python-dateutil`。
- 旧静态目录、根目录打包产物、日志和 Python 编译缓存属于可清理产物,不应提交。 - 旧静态目录、根目录打包产物、日志和 Python 编译缓存属于可清理产物,不应提交。
-1
View File
@@ -5,7 +5,6 @@ python-multipart
# Database # Database
pymysql pymysql
sqlalchemy
cryptography cryptography
# Data processing # Data processing
+1 -1
View File
@@ -16,7 +16,7 @@ if not exist "%PYTHON_EXE%" (
exit /b 1 exit /b 1
) )
"%PYTHON_EXE%" -c "import fastapi, uvicorn, pandas, openpyxl, pymysql, sqlalchemy, chardet" >nul 2>&1 "%PYTHON_EXE%" -c "import fastapi, uvicorn, pandas, openpyxl, pymysql, chardet" >nul 2>&1
if errorlevel 1 ( if errorlevel 1 (
echo [INFO] Installing or repairing Python dependencies... echo [INFO] Installing or repairing Python dependencies...
uv pip install -r requirements.txt uv pip install -r requirements.txt