refactor: 清理后端冗余代码和未用依赖
This commit is contained in:
@@ -6,7 +6,7 @@ CapacityReport 用于每周导入 Excel/CSV 数据,按 `Configure.json` 与 `R
|
||||
|
||||
- 后端:FastAPI + Uvicorn + Supervisor
|
||||
- 前端:Vue 3 + TypeScript + Vite + Naive UI
|
||||
- 数据库:MySQL 8.0+,通过 PyMySQL + SQLAlchemy 访问
|
||||
- 数据库:MySQL 8.0+,通过 PyMySQL 访问
|
||||
- 数据处理:Pandas + OpenPyXL
|
||||
- 部署:Docker + Docker Compose
|
||||
|
||||
|
||||
@@ -56,11 +56,8 @@ def _dataframe_from_table(db: DatabaseManager, table_name: str) -> pd.DataFrame:
|
||||
@router.post("/api/database/test")
|
||||
async def test_database():
|
||||
db = DatabaseManager(state.config)
|
||||
try:
|
||||
success, message = db.test_connection()
|
||||
return {"success": success, "message": message}
|
||||
finally:
|
||||
db.dispose()
|
||||
success, message = db.test_connection()
|
||||
return {"success": success, "message": message}
|
||||
|
||||
|
||||
@router.get("/api/database/info")
|
||||
@@ -70,8 +67,6 @@ async def get_database_info():
|
||||
return {"success": True, **db.get_server_info()}
|
||||
except Exception as exc:
|
||||
return {"success": False, "error": str(exc)}
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@router.get("/api/database/tables")
|
||||
@@ -82,8 +77,6 @@ async def get_tables():
|
||||
return {"tables": db.get_tables()}
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@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)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@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)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@router.post("/api/database/table/query")
|
||||
@@ -135,8 +124,6 @@ async def query_table_with_filter(
|
||||
)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@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} 已清空"}
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@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} 已删除"}
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@router.post("/api/database/table/drop-all")
|
||||
@@ -176,8 +159,6 @@ async def drop_all_tables():
|
||||
}
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@router.post("/api/database/execute")
|
||||
@@ -192,8 +173,6 @@ async def execute_sql(sql: str = Body(..., embed=True)):
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
|
||||
@router.post("/api/download")
|
||||
@@ -226,8 +205,6 @@ async def download_table(
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
finally:
|
||||
db.dispose()
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename_prefix = requested_tables[0] if len(requested_tables) == 1 else "tables"
|
||||
|
||||
@@ -19,15 +19,12 @@ async def health_check():
|
||||
|
||||
try:
|
||||
db_manager = DatabaseManager(state.config)
|
||||
try:
|
||||
server_info = db_manager.get_server_info()
|
||||
checks["database"] = {
|
||||
"status": "ok",
|
||||
"version": server_info.get("version", "unknown"),
|
||||
"load_data_infile": server_info.get("load_data_infile", False),
|
||||
}
|
||||
finally:
|
||||
db_manager.dispose()
|
||||
server_info = db_manager.get_server_info()
|
||||
checks["database"] = {
|
||||
"status": "ok",
|
||||
"version": server_info.get("version", "unknown"),
|
||||
"load_data_infile": server_info.get("load_data_infile", False),
|
||||
}
|
||||
except Exception as exc:
|
||||
checks["database"] = {"status": "error", "message": str(exc)}
|
||||
|
||||
@@ -39,4 +36,3 @@ async def health_check():
|
||||
"uptime_pid": os.getpid(),
|
||||
"checks": checks,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from app.config import BASE_DIR
|
||||
@@ -81,4 +80,3 @@ def _sign(header: str, payload: str) -> str:
|
||||
hashlib.sha256,
|
||||
).digest()
|
||||
return base64.urlsafe_b64encode(raw).decode().rstrip("=")
|
||||
|
||||
|
||||
+1
-48
@@ -2,14 +2,9 @@
|
||||
数据库连接与操作模块 - 性能优化版
|
||||
"""
|
||||
import pymysql
|
||||
from urllib.parse import quote
|
||||
from contextlib import contextmanager
|
||||
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
|
||||
|
||||
|
||||
@@ -18,33 +13,6 @@ class DatabaseManager:
|
||||
|
||||
def __init__(self, config: AppConfig):
|
||||
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
|
||||
def get_connection(self):
|
||||
@@ -230,15 +198,6 @@ class DatabaseManager:
|
||||
"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:
|
||||
"""清空表"""
|
||||
with self.get_connection() as conn:
|
||||
@@ -272,7 +231,7 @@ class DatabaseManager:
|
||||
try:
|
||||
cursor.execute(f"DROP TABLE IF EXISTS `{table}`")
|
||||
dropped_tables.append(table)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
# 记录错误但继续删除其他表
|
||||
pass
|
||||
|
||||
@@ -434,9 +393,3 @@ class DatabaseManager:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(sql)
|
||||
conn.commit()
|
||||
|
||||
def dispose(self):
|
||||
"""释放连接池"""
|
||||
if self._engine:
|
||||
self._engine.dispose()
|
||||
self._engine = None
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ class HistoryManager:
|
||||
"""加载历史记录"""
|
||||
try:
|
||||
return json.loads(HISTORY_FILE.read_text(encoding='utf-8'))
|
||||
except:
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return []
|
||||
|
||||
def _save(self, records: List[Dict[str, Any]]):
|
||||
|
||||
@@ -220,8 +220,6 @@ class DataProcessor:
|
||||
finally:
|
||||
# 清理临时目录
|
||||
self._cleanup_temp_dir()
|
||||
# 释放数据库连接
|
||||
self.db.dispose()
|
||||
|
||||
return self.results
|
||||
|
||||
|
||||
+12
-19
@@ -8,10 +8,12 @@ CapacityReport 统一构建脚本
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import re
|
||||
import tarfile
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
|
||||
# ============================================================================
|
||||
@@ -37,9 +39,7 @@ MYSQL_PORT = 3306
|
||||
|
||||
# 应用端口映射
|
||||
APP_PORT_HOST = 19081
|
||||
APP_PORT_CONTAINER = 9081
|
||||
MYSQL_PORT_HOST = 13306
|
||||
MYSQL_PORT_CONTAINER = 3306
|
||||
|
||||
# ============================================================================
|
||||
# 脚本目录配置
|
||||
@@ -51,14 +51,12 @@ DIST_DIR = PROJECT_ROOT / "dist"
|
||||
BUILD_DIR = PROJECT_ROOT / "build"
|
||||
TEMP_BUILD_DIR = BUILD_DIR / "temp"
|
||||
|
||||
# 颜色输出(Windows 支持)
|
||||
import platform
|
||||
if platform.system() == 'Windows':
|
||||
try:
|
||||
import ctypes
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
||||
except:
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
class Colors:
|
||||
@@ -95,7 +93,7 @@ def run_cmd(cmd, check=True, capture_output=False):
|
||||
text=True
|
||||
)
|
||||
return result
|
||||
except subprocess.CalledProcessError as e:
|
||||
except subprocess.CalledProcessError:
|
||||
if check:
|
||||
print_error(f"命令执行失败: {' '.join(cmd)}")
|
||||
sys.exit(1)
|
||||
@@ -198,7 +196,7 @@ def image_exists(image_name):
|
||||
check=True
|
||||
)
|
||||
return image_name in result.stdout
|
||||
except:
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return False
|
||||
|
||||
def format_size(size_bytes):
|
||||
@@ -668,14 +666,10 @@ def build_full_package():
|
||||
sys.exit(1)
|
||||
|
||||
# 检查 Docker Compose
|
||||
docker_compose_cmd = None
|
||||
try:
|
||||
subprocess.run(["docker", "compose", "version"], capture_output=True, check=True)
|
||||
docker_compose_cmd = "docker compose"
|
||||
except:
|
||||
if check_command("docker-compose"):
|
||||
docker_compose_cmd = "docker-compose"
|
||||
else:
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
if not check_command("docker-compose"):
|
||||
print_error("未检测到 Docker Compose")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -689,14 +683,14 @@ def build_full_package():
|
||||
if not image_exists(python_image):
|
||||
print_info(f"Python 镜像不存在,正在拉取: {python_image}")
|
||||
run_cmd(["docker", "pull", python_image])
|
||||
print_info(f"Python 镜像拉取完成")
|
||||
print_info("Python 镜像拉取完成")
|
||||
else:
|
||||
print_info(f"Python 镜像已存在: {python_image}")
|
||||
|
||||
if not image_exists(mysql_image):
|
||||
print_info(f"MySQL 镜像不存在,正在拉取: {mysql_image}")
|
||||
run_cmd(["docker", "pull", mysql_image])
|
||||
print_info(f"MySQL 镜像拉取完成")
|
||||
print_info("MySQL 镜像拉取完成")
|
||||
else:
|
||||
print_info(f"MySQL 镜像已存在: {mysql_image}")
|
||||
|
||||
@@ -728,7 +722,7 @@ def build_full_package():
|
||||
if dockerignore_build.read_text() == dockerignore_root.read_text():
|
||||
dockerignore_root.unlink()
|
||||
print_info("已清理临时 .dockerignore")
|
||||
except:
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# 标记 MySQL 镜像(提取版本号)
|
||||
@@ -844,7 +838,7 @@ def build_update_package():
|
||||
if not image_exists(python_image):
|
||||
print_info(f"Python 镜像不存在,正在拉取: {python_image}")
|
||||
run_cmd(["docker", "pull", python_image])
|
||||
print_info(f"Python 镜像拉取完成")
|
||||
print_info("Python 镜像拉取完成")
|
||||
else:
|
||||
print_info(f"Python 镜像已存在: {python_image}")
|
||||
|
||||
@@ -876,7 +870,7 @@ def build_update_package():
|
||||
if dockerignore_build.read_text() == dockerignore_root.read_text():
|
||||
dockerignore_root.unlink()
|
||||
print_info("已清理临时 .dockerignore")
|
||||
except:
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# 导出镜像
|
||||
@@ -992,6 +986,5 @@ if __name__ == "__main__":
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print_error(f"构建失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
@@ -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 前端和双端口托管
|
||||
|
||||
- 删除 `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`。
|
||||
- 旧静态目录、根目录打包产物、日志和 Python 编译缓存属于可清理产物,不应提交。
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ python-multipart
|
||||
|
||||
# Database
|
||||
pymysql
|
||||
sqlalchemy
|
||||
cryptography
|
||||
|
||||
# Data processing
|
||||
|
||||
@@ -16,7 +16,7 @@ if not exist "%PYTHON_EXE%" (
|
||||
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 (
|
||||
echo [INFO] Installing or repairing Python dependencies...
|
||||
uv pip install -r requirements.txt
|
||||
|
||||
Reference in New Issue
Block a user