mirror of
https://github.com/Shu-Ying/main_plugin_farm.git
synced 2026-10-31 06:49:59 +08:00
🎨 使用Python包管理和模块访问
🎨 重构数据库结构, 使用集中式服务定位器与数据库交互 🎨 更改部分代码逻辑, 减小内存消耗
This commit is contained in:
+171
-57
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user