🎨 使用Python包管理和模块访问

🎨 重构数据库结构, 使用集中式服务定位器与数据库交互
🎨 更改部分代码逻辑, 减小内存消耗
This commit is contained in:
2025-11-30 16:45:13 +08:00
parent b4d3baebe6
commit a6691595e5
29 changed files with 1356 additions and 885 deletions
+171 -57
View File
@@ -1,60 +1,188 @@
from abc import ABC
from contextlib import asynccontextmanager
import os
from pathlib import Path
import re
from typing import Any
from typing import Any, ClassVar
import aiosqlite
from zhenxun.services.log import logger
from ...utils.config import g_sDBFilePath, g_sDBPath
from ...utils import config
from .dbServiceLocator import g_pDBlocator
class CSqlManager:
class CSqlManager(ABC):
"""
数据库管理器基类
所有具体的数据管理器都应该继承这个类
"""
m_pDB: ClassVar[aiosqlite.Connection | None] = None
def __init__(self):
dbPath = Path(g_sDBPath)
if dbPath and not dbPath.exists():
os.makedirs(dbPath, exist_ok=True)
self.m_sTableName: str | None = None
self.m_bInitialized: bool = False
@classmethod
async def cleanup(cls):
if hasattr(cls, "m_pDB") and cls.m_pDB:
await cls.m_pDB.close()
self.m_pDBPath = Path(config.g_sDBPath)
if self.m_pDBPath and not self.m_pDBPath.exists():
os.makedirs(self.m_pDBPath, exist_ok=True)
@classmethod
async def init(cls) -> bool:
try:
cls.m_pDB = await aiosqlite.connect(g_sDBFilePath)
cls.m_pDB.row_factory = aiosqlite.Row
return True
if cls.m_pDB is None:
cls.m_pDB = await aiosqlite.connect(config.g_sDBFilePath)
cls.m_pDB.row_factory = aiosqlite.Row
logger.info("真寻农场数据库连接初始化成功")
return True
else:
logger.info("真寻农场数据库连接已存在,跳过初始化")
return True
except Exception as e:
logger.debug("真寻农场初始化总数据库失败", e=e)
logger.error(f"真寻农场数据库连接初始化失败: {e}")
return False
@classmethod
async def cleanup(cls):
if cls.m_pDB:
await cls.m_pDB.close()
cls.m_pDB = None
logger.info("真寻农场数据库连接已关闭")
@classmethod
def getDB(cls) -> aiosqlite.Connection:
if cls.m_pDB is None:
raise RuntimeError("数据库连接未初始化,请先调用 init_global_db()")
return cls.m_pDB
@classmethod
def getPlantManager(cls) -> Any:
return g_pDBlocator.getPlantManager()
@classmethod
def getUserManager(cls) -> Any:
return g_pDBlocator.getUserManager()
@classmethod
def getUserSoilManager(cls) -> Any:
return g_pDBlocator.getUserSoilManager()
@classmethod
def getUserSeedManager(cls) -> Any:
return g_pDBlocator.getUserSeedManager()
@classmethod
def getUserItemManager(cls) -> Any:
return g_pDBlocator.getUserItemManager()
@classmethod
def getUserStealManager(cls) -> Any:
return g_pDBlocator.getUserStealManager()
@classmethod
def getUserSignManager(cls) -> Any:
return g_pDBlocator.getUserSignManager()
@classmethod
def getUserPlantCountManager(cls) -> Any:
return g_pDBlocator.getUserPlantCountManager()
def isInitialized(self) -> bool:
"""检查是否已初始化"""
return self.m_bInitialized
def setInitialized(self) -> None:
"""标记为已初始化"""
self.m_bInitialized = True
@classmethod
@asynccontextmanager
async def _transaction(cls):
await cls.m_pDB.execute("BEGIN;")
await cls.getDB().execute("BEGIN;")
try:
yield
except:
await cls.m_pDB.execute("ROLLBACK;")
await cls.getDB().execute("ROLLBACK;")
raise
else:
await cls.m_pDB.execute("COMMIT;")
await cls.getDB().execute("COMMIT;")
@classmethod
async def getTableInfo(cls, tableName: str) -> list:
if not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", tableName):
raise ValueError(f"Illegal table name: {tableName}")
try:
cursor = await cls.m_pDB.execute(f'PRAGMA table_info("{tableName}")')
cursor = await cls.getDB().execute(f'PRAGMA table_info("{tableName}")')
rows = await cursor.fetchall()
return [{"name": row[1], "type": row[2]} for row in rows]
except aiosqlite.Error:
return []
# @classmethod
# async def ensureTableSchema(cls, tableName: str, columns: dict) -> bool:
# """由AI生成
# 创建表或为已存在表添加缺失字段。
# 返回 True 表示有变更(创建或新增列),False 则无操作
# Args:
# tableName (str): 表名
# columns (dict): 字典
# Returns:
# bool:
# """
# info = await cls.getTableInfo(tableName)
# existing = {col["name"]: col["type"].upper() for col in info}
# desired = {k: v.upper() for k, v in columns.items() if k != "PRIMARY KEY"}
# primaryKey = columns.get("PRIMARY KEY", "")
# if not existing:
# colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
# if primaryKey:
# colsDef += f", PRIMARY KEY {primaryKey}"
# await cls.getDB().execute(f'CREATE TABLE "{tableName}" ({colsDef});')
# return True
# toAdd = [k for k in desired if k not in existing]
# toRemove = [k for k in existing if k not in desired]
# typeMismatch = [
# k for k in desired if k in existing and existing[k] != desired[k]
# ]
# if toAdd and not toRemove and not typeMismatch:
# for col in toAdd:
# await cls.getDB().execute(
# f'ALTER TABLE "{tableName}" ADD COLUMN "{col}" {columns[col]}'
# )
# return True
# async with cls._transaction():
# tmpTable = f"{tableName}_new"
# colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
# if primaryKey:
# colsDef += f", PRIMARY KEY {primaryKey}"
# await cls.getDB().execute(f'CREATE TABLE "{tmpTable}" ({colsDef});')
# commonCols = [k for k in desired if k in existing]
# if commonCols:
# colsStr = ", ".join(f'"{c}"' for c in commonCols)
# sql = (
# f'INSERT INTO "{tmpTable}" ({colsStr}) '
# f"SELECT {colsStr} "
# f'FROM "{tableName}";'
# )
# await cls.getDB().execute(sql)
# await cls.getDB().execute(f'DROP TABLE "{tableName}";')
# await cls.getDB().execute(
# f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";'
# )
# return True
@classmethod
async def ensureTableSchema(cls, tableName: str, columns: dict) -> bool:
"""由AI生成
@@ -62,13 +190,12 @@ class CSqlManager:
返回 True 表示有变更(创建或新增列),False 则无操作
Args:
tableName (_type_): 表名
columns (_type_): 字典
tableName (str): 表名
columns (dict): 字典
Returns:
_type_: _description_
bool:
"""
info = await cls.getTableInfo(tableName)
existing = {col["name"]: col["type"].upper() for col in info}
desired = {k: v.upper() for k, v in columns.items() if k != "PRIMARY KEY"}
@@ -78,7 +205,7 @@ class CSqlManager:
colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
if primaryKey:
colsDef += f", PRIMARY KEY {primaryKey}"
await cls.m_pDB.execute(f'CREATE TABLE "{tableName}" ({colsDef});')
await cls.getDB().execute(f'CREATE TABLE "{tableName}" ({colsDef});')
return True
toAdd = [k for k in desired if k not in existing]
@@ -87,36 +214,23 @@ class CSqlManager:
k for k in desired if k in existing and existing[k] != desired[k]
]
if toAdd and not toRemove and not typeMismatch:
if toRemove or typeMismatch:
return False
if toAdd:
for col in toAdd:
await cls.m_pDB.execute(
f'ALTER TABLE "{tableName}" ADD COLUMN "{col}" {columns[col]}'
)
try:
await cls.getDB().execute(
f'ALTER TABLE "{tableName}" ADD COLUMN "{col}" {columns[col]}'
)
except aiosqlite.OperationalError as e:
if "duplicate column name" in str(e).lower():
continue
else:
raise
return True
async with cls._transaction():
tmpTable = f"{tableName}_new"
colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
if primaryKey:
colsDef += f", PRIMARY KEY {primaryKey}"
await cls.m_pDB.execute(f'CREATE TABLE "{tmpTable}" ({colsDef});')
commonCols = [k for k in desired if k in existing]
if commonCols:
colsStr = ", ".join(f'"{c}"' for c in commonCols)
sql = (
f'INSERT INTO "{tmpTable}" ({colsStr}) '
f"SELECT {colsStr} "
f'FROM "{tableName}";'
)
await cls.m_pDB.execute(sql)
await cls.m_pDB.execute(f'DROP TABLE "{tableName}";')
await cls.m_pDB.execute(
f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";'
)
return True
return False
@classmethod
async def executeDB(cls, command: str) -> bool:
@@ -133,7 +247,7 @@ class CSqlManager:
try:
async with cls._transaction():
await cls.m_pDB.execute(command)
await cls.getDB().execute(command)
return True
except Exception:
return False
@@ -162,7 +276,7 @@ class CSqlManager:
sql = f'INSERT INTO "{tableName}" ({columns}) VALUES ({placeholders})'
async with cls._transaction():
await cls.m_pDB.execute(sql, values)
await cls.getDB().execute(sql, values)
return True
except Exception as e:
logger.debug("真寻农场插入数据失败!", e=e)
@@ -191,7 +305,7 @@ class CSqlManager:
sql = f'INSERT INTO "{tableName}" ({columns}) VALUES ({placeholders})'
async with cls._transaction():
await cls.m_pDB.executemany(
await cls.getDB().executemany(
sql, [list(data.values()) for data in data_list]
)
return True
@@ -255,7 +369,7 @@ class CSqlManager:
if limit:
sql += f" LIMIT {limit}"
cursor = await cls.m_pDB.execute(sql, params)
cursor = await cls.getDB().execute(sql, params)
rows = await cursor.fetchall()
# 转换为字典列表
@@ -304,7 +418,7 @@ class CSqlManager:
sql = f'UPDATE "{tableName}" SET {", ".join(set_conditions)} WHERE {" AND ".join(where_conditions)}'
async with cls._transaction():
cursor = await cls.m_pDB.execute(sql, params)
cursor = await cls.getDB().execute(sql, params)
# 检查是否影响了行
return cursor.rowcount > 0
except Exception as e:
@@ -337,7 +451,7 @@ class CSqlManager:
sql = f'DELETE FROM "{tableName}" WHERE {" AND ".join(where_conditions)}'
async with cls._transaction():
cursor = await cls.m_pDB.execute(sql, params)
cursor = await cls.getDB().execute(sql, params)
# 检查是否影响了行
return cursor.rowcount > 0
except Exception as e:
@@ -388,7 +502,7 @@ class CSqlManager:
sql += " WHERE " + " AND ".join(where_conditions)
cursor = await cls.m_pDB.execute(sql, params)
cursor = await cls.getDB().execute(sql, params)
row = await cursor.fetchone()
return row["count"] if row else 0
except Exception as e: