feat(mysql): 添加MySQL数据库支持
- 在requirements.txt中添加aiomysql依赖 - 在config.toml中添加MySQL配置块 - 新增MySQLModel配置模型 - 实现MySQLManager单例管理器 - 更新Config类以支持MySQL配置加载 - 在__init__.py中导出mysql_manager - 改进ConfigError异常处理
This commit is contained in:
21
config.toml
21
config.toml
@@ -21,13 +21,28 @@ permission_denied_message = "权限不足,需要 {permission_name} 权限"
|
||||
# Redis 配置
|
||||
[redis]
|
||||
# Redis 主机地址
|
||||
host = "101.36.126.55"
|
||||
host = "114.66.61.199"
|
||||
# Redis 端口
|
||||
port = 6379
|
||||
port = 37080
|
||||
# Redis 数据库编号
|
||||
db = 0
|
||||
# Redis 密码
|
||||
password = "redis_5fCmnE"
|
||||
password = "redis_n7Ke76"
|
||||
|
||||
# MySQL 配置
|
||||
[mysql]
|
||||
# MySQL 主机地址
|
||||
host = "114.66.61.199"
|
||||
# MySQL 端口
|
||||
port = 42398
|
||||
# MySQL 用户名
|
||||
user = "neobot"
|
||||
# MySQL 密码
|
||||
password = "neobot"
|
||||
# MySQL 数据库名称
|
||||
db = "neobot"
|
||||
|
||||
|
||||
|
||||
# Docker 配置
|
||||
[docker]
|
||||
|
||||
@@ -7,7 +7,7 @@ from pathlib import Path
|
||||
|
||||
import tomllib
|
||||
from pydantic import ValidationError
|
||||
from .config_models import ConfigModel, NapCatWSModel, BotModel, RedisModel, DockerModel, ImageManagerModel
|
||||
from .config_models import ConfigModel, NapCatWSModel, BotModel, RedisModel, DockerModel, ImageManagerModel, MySQLModel
|
||||
from .utils.logger import ModuleLogger
|
||||
from .utils.exceptions import ConfigError, ConfigNotFoundError, ConfigValidationError
|
||||
|
||||
@@ -59,9 +59,9 @@ class Config:
|
||||
error_details.append(error_msg)
|
||||
|
||||
validation_error = ConfigValidationError(
|
||||
message="配置验证失败",
|
||||
original_error=e
|
||||
message="配置验证失败"
|
||||
)
|
||||
validation_error.original_error = e
|
||||
|
||||
self.logger.error("配置验证失败,请检查 `config.toml` 文件中的以下错误:")
|
||||
for detail in error_details:
|
||||
@@ -71,17 +71,17 @@ class Config:
|
||||
raise validation_error
|
||||
except tomllib.TOMLDecodeError as e:
|
||||
error = ConfigError(
|
||||
message=f"TOML解析错误: {str(e)}",
|
||||
original_error=e
|
||||
message=f"TOML解析错误: {str(e)}"
|
||||
)
|
||||
error.original_error = e
|
||||
self.logger.error(f"加载配置文件时发生TOML解析错误: {error.message}")
|
||||
self.logger.log_custom_exception(error)
|
||||
raise error
|
||||
except Exception as e:
|
||||
error = ConfigError(
|
||||
message=f"加载配置文件时发生未知错误: {str(e)}",
|
||||
original_error=e
|
||||
message=f"加载配置文件时发生未知错误: {str(e)}"
|
||||
)
|
||||
error.original_error = e
|
||||
self.logger.exception(f"加载配置文件时发生未知错误: {error.message}")
|
||||
self.logger.log_custom_exception(error)
|
||||
raise error
|
||||
@@ -108,6 +108,13 @@ class Config:
|
||||
"""
|
||||
return self._model.redis
|
||||
|
||||
@property
|
||||
def mysql(self) -> MySQLModel:
|
||||
"""
|
||||
获取 MySQL 配置
|
||||
"""
|
||||
return self._model.mysql
|
||||
|
||||
@property
|
||||
def docker(self) -> DockerModel:
|
||||
"""
|
||||
|
||||
@@ -36,6 +36,18 @@ class RedisModel(BaseModel):
|
||||
password: str
|
||||
|
||||
|
||||
class MySQLModel(BaseModel):
|
||||
"""
|
||||
对应 `config.toml` 中的 `[mysql]` 配置块。
|
||||
"""
|
||||
host: str
|
||||
port: int
|
||||
user: str
|
||||
password: str
|
||||
db: str
|
||||
charset: str = "utf8mb4"
|
||||
|
||||
|
||||
class DockerModel(BaseModel):
|
||||
"""
|
||||
对应 `config.toml` 中的 `[docker]` 配置块。
|
||||
@@ -64,6 +76,7 @@ class ConfigModel(BaseModel):
|
||||
napcat_ws: NapCatWSModel
|
||||
bot: BotModel
|
||||
redis: RedisModel
|
||||
mysql: MySQLModel
|
||||
docker: DockerModel
|
||||
image_manager: ImageManagerModel
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from .command_manager import matcher as command_manager
|
||||
from .permission_manager import PermissionManager
|
||||
from .plugin_manager import PluginManager
|
||||
from .redis_manager import RedisManager
|
||||
from .mysql_manager import MySQLManager
|
||||
from .browser_manager import BrowserManager
|
||||
from .image_manager import ImageManager
|
||||
|
||||
@@ -26,6 +27,9 @@ plugin_manager = PluginManager(command_manager)
|
||||
# Redis 管理器
|
||||
redis_manager = RedisManager()
|
||||
|
||||
# MySQL 管理器
|
||||
mysql_manager = MySQLManager()
|
||||
|
||||
# 浏览器管理器
|
||||
browser_manager = BrowserManager()
|
||||
|
||||
@@ -38,6 +42,7 @@ __all__ = [
|
||||
"matcher",
|
||||
"plugin_manager",
|
||||
"redis_manager",
|
||||
"mysql_manager",
|
||||
"browser_manager",
|
||||
"image_manager",
|
||||
]
|
||||
|
||||
148
core/managers/mysql_manager.py
Normal file
148
core/managers/mysql_manager.py
Normal file
@@ -0,0 +1,148 @@
|
||||
import aiomysql
|
||||
from ..config_loader import global_config as config
|
||||
from ..utils.logger import logger
|
||||
from ..utils.singleton import Singleton
|
||||
|
||||
|
||||
class MySQLManager(Singleton):
|
||||
"""
|
||||
MySQL 数据库连接管理器(异步单例)
|
||||
"""
|
||||
_pool = None
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化 MySQL 管理器
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
async def initialize(self):
|
||||
"""
|
||||
异步初始化 MySQL 连接池并进行健康检查
|
||||
"""
|
||||
if self._pool is None:
|
||||
try:
|
||||
mysql_config = config.mysql
|
||||
host = mysql_config.host
|
||||
port = mysql_config.port
|
||||
user = mysql_config.user
|
||||
password = mysql_config.password
|
||||
db = mysql_config.db
|
||||
charset = mysql_config.charset
|
||||
|
||||
logger.info(f"正在尝试连接 MySQL: {host}:{port}, DB: {db}")
|
||||
|
||||
self._pool = await aiomysql.create_pool(
|
||||
host=host,
|
||||
port=port,
|
||||
user=user,
|
||||
password=password,
|
||||
db=db,
|
||||
charset=charset,
|
||||
autocommit=False,
|
||||
maxsize=10,
|
||||
minsize=1
|
||||
)
|
||||
|
||||
async with self._pool.acquire() as conn:
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute("SELECT 1")
|
||||
result = await cur.fetchone()
|
||||
if result and result[0] == 1:
|
||||
logger.success("MySQL 连接成功!")
|
||||
else:
|
||||
logger.error("MySQL 连接失败: 健康检查失败")
|
||||
except Exception as e:
|
||||
logger.exception(f"MySQL 初始化时发生未知错误: {e}")
|
||||
self._pool = None
|
||||
|
||||
@property
|
||||
def pool(self):
|
||||
"""
|
||||
获取 MySQL 连接池实例
|
||||
"""
|
||||
if self._pool is None:
|
||||
raise ConnectionError("MySQL 未初始化或连接失败,请先调用 initialize()")
|
||||
return self._pool
|
||||
|
||||
async def execute(self, sql: str, args: tuple = None):
|
||||
"""
|
||||
执行 SQL 语句(用于 INSERT、UPDATE、DELETE)
|
||||
|
||||
Args:
|
||||
sql: SQL 语句
|
||||
args: 参数元组
|
||||
|
||||
Returns:
|
||||
影响的行数
|
||||
"""
|
||||
async with self._pool.acquire() as conn:
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute(sql, args)
|
||||
await conn.commit()
|
||||
return cur.rowcount
|
||||
|
||||
async def fetchone(self, sql: str, args: tuple = None):
|
||||
"""
|
||||
查询单条记录
|
||||
|
||||
Args:
|
||||
sql: SQL 语句
|
||||
args: 参数元组
|
||||
|
||||
Returns:
|
||||
单条记录字典
|
||||
"""
|
||||
async with self._pool.acquire() as conn:
|
||||
async with conn.cursor(aiomysql.DictCursor) as cur:
|
||||
await cur.execute(sql, args)
|
||||
return await cur.fetchone()
|
||||
|
||||
async def fetchall(self, sql: str, args: tuple = None):
|
||||
"""
|
||||
查询多条记录
|
||||
|
||||
Args:
|
||||
sql: SQL 语句
|
||||
args: 参数元组
|
||||
|
||||
Returns:
|
||||
记录列表
|
||||
"""
|
||||
async with self._pool.acquire() as conn:
|
||||
async with conn.cursor(aiomysql.DictCursor) as cur:
|
||||
await cur.execute(sql, args)
|
||||
return await cur.fetchall()
|
||||
|
||||
async def begin_transaction(self):
|
||||
"""
|
||||
开始事务
|
||||
|
||||
Returns:
|
||||
事务连接对象
|
||||
"""
|
||||
conn = await self._pool.acquire()
|
||||
return conn
|
||||
|
||||
async def commit_transaction(self, conn):
|
||||
"""
|
||||
提交事务
|
||||
|
||||
Args:
|
||||
conn: 事务连接对象
|
||||
"""
|
||||
await conn.commit()
|
||||
await self._pool.release(conn)
|
||||
|
||||
async def rollback_transaction(self, conn):
|
||||
"""
|
||||
回滚事务
|
||||
|
||||
Args:
|
||||
conn: 事务连接对象
|
||||
"""
|
||||
await conn.rollback()
|
||||
await self._pool.release(conn)
|
||||
|
||||
|
||||
mysql_manager = MySQLManager()
|
||||
@@ -83,14 +83,15 @@ class ConfigError(Exception):
|
||||
配置相关错误的基类。
|
||||
|
||||
Args:
|
||||
section: 配置部分名称
|
||||
key: 配置项名称
|
||||
message: 错误消息
|
||||
section: 配置部分名称(可选)
|
||||
key: 配置项名称(可选)
|
||||
message: 错误消息(可选)
|
||||
"""
|
||||
def __init__(self, section=None, key=None, message=None):
|
||||
self.section = section
|
||||
self.key = key
|
||||
self.message = message
|
||||
self.original_error = None
|
||||
|
||||
if section and key and message:
|
||||
super().__init__(f"配置错误 [{section}.{key}]: {message}")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
aiohappyeyeballs==2.6.1
|
||||
aiohttp==3.13.3
|
||||
aiomysql==0.2.0
|
||||
aiosignal==1.4.0
|
||||
annotated-types==0.7.0
|
||||
anyio==4.12.1
|
||||
|
||||
Reference in New Issue
Block a user