- 添加项目根目录.gitignore统一管理忽略规则 - 创建数据库配置文件模板和示例便于团队协作 - 优化ConfigManager支持多层配置加载优先级 - 移除冗余的子目录.gitignore文件 - 完善配置文档说明团队协作流程pull/8/head
parent
1bfbbd5122
commit
41d2933249
@ -0,0 +1,49 @@
|
|||||||
|
# 编译生成文件 (Build artifacts)
|
||||||
|
build/
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Makefile
|
||||||
|
|
||||||
|
# Qt临时文件 (Qt temporary files)
|
||||||
|
*.pro.user
|
||||||
|
*.pro.user.*
|
||||||
|
.qmake.stash
|
||||||
|
|
||||||
|
# 个人配置文件 (Personal configuration files)
|
||||||
|
# 忽略个人数据库配置,避免团队成员间的配置冲突
|
||||||
|
# Ignore personal database configuration to avoid conflicts between team members
|
||||||
|
src/Client/config/database.ini
|
||||||
|
|
||||||
|
# 环境配置文件 (Environment configuration files)
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# 日志文件 (Log files)
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# 临时文件 (Temporary files)
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*~
|
||||||
|
|
||||||
|
# IDE配置文件 (IDE configuration files)
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# 系统文件 (System files)
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# 备份文件 (Backup files)
|
||||||
|
*.bak
|
||||||
|
*.backup
|
@ -1,63 +0,0 @@
|
|||||||
# Qt/C++ build files
|
|
||||||
build/
|
|
||||||
bin/
|
|
||||||
*.o
|
|
||||||
*.obj
|
|
||||||
*.so
|
|
||||||
*.dll
|
|
||||||
*.dylib
|
|
||||||
*.a
|
|
||||||
*.lib
|
|
||||||
*.exe
|
|
||||||
|
|
||||||
# Qt specific files
|
|
||||||
moc_*.cpp
|
|
||||||
moc_*.h
|
|
||||||
qrc_*.cpp
|
|
||||||
ui_*.h
|
|
||||||
Makefile*
|
|
||||||
*.pro.user
|
|
||||||
*.pro.user.*
|
|
||||||
|
|
||||||
# IDE files
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.kate-swp
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*.tmp
|
|
||||||
*.temp
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Debug/Release directories
|
|
||||||
debug/
|
|
||||||
release/
|
|
||||||
Debug/
|
|
||||||
Release/
|
|
||||||
|
|
||||||
# CMake
|
|
||||||
CMakeCache.txt
|
|
||||||
CMakeFiles/
|
|
||||||
cmake_install.cmake
|
|
||||||
|
|
||||||
# Android build files
|
|
||||||
android/build/
|
|
||||||
android/.gradle/
|
|
||||||
android/local.properties
|
|
||||||
|
|
||||||
# Backup files
|
|
||||||
*.bak
|
|
||||||
*.backup
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Log files
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# Core dumps
|
|
||||||
core
|
|
||||||
core.*
|
|
||||||
|
|
||||||
# Documentation directory
|
|
||||||
doc/
|
|
@ -0,0 +1,47 @@
|
|||||||
|
# 战场探索系统数据库配置文件示例
|
||||||
|
# Database Configuration Example for BattlefieldExplorationSystem
|
||||||
|
#
|
||||||
|
# 这是一个示例配置文件,展示了常见的配置选项
|
||||||
|
# This is an example configuration file showing common configuration options
|
||||||
|
|
||||||
|
[Database]
|
||||||
|
# 数据库服务器地址 (Database server host)
|
||||||
|
host=localhost
|
||||||
|
|
||||||
|
# 数据库端口 (Database port)
|
||||||
|
port=3306
|
||||||
|
|
||||||
|
# 数据库名称 (Database name)
|
||||||
|
databaseName=Client
|
||||||
|
|
||||||
|
# 数据库用户名 (Database username)
|
||||||
|
username=root
|
||||||
|
|
||||||
|
# 数据库密码 (Database password)
|
||||||
|
password=example_password
|
||||||
|
|
||||||
|
# 连接超时时间,单位毫秒 (Connection timeout in milliseconds)
|
||||||
|
connectionTimeout=30000
|
||||||
|
|
||||||
|
# 数据库驱动名称 (Database driver name)
|
||||||
|
driverName=QMYSQL
|
||||||
|
|
||||||
|
# 其他常见配置示例:
|
||||||
|
# Other common configuration examples:
|
||||||
|
|
||||||
|
# 使用不同的数据库服务器
|
||||||
|
# Using different database server
|
||||||
|
# host=192.168.1.100
|
||||||
|
# port=3306
|
||||||
|
|
||||||
|
# 使用不同的数据库名称
|
||||||
|
# Using different database name
|
||||||
|
# databaseName=BattlefieldSystem
|
||||||
|
|
||||||
|
# 使用不同的用户名
|
||||||
|
# Using different username
|
||||||
|
# username=battlefield_user
|
||||||
|
|
||||||
|
# 更长的连接超时时间
|
||||||
|
# Longer connection timeout
|
||||||
|
# connectionTimeout=60000
|
@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* @file DatabaseConfig.cpp
|
||||||
|
* @brief 数据库配置类实现
|
||||||
|
* @author BattlefieldExplorationSystem Team
|
||||||
|
* @date 2024-01-01
|
||||||
|
* @version 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/database/DatabaseConfig.h"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
// 静态成员初始化
|
||||||
|
DatabaseConfig* DatabaseConfig::m_instance = nullptr;
|
||||||
|
|
||||||
|
DatabaseConfig* DatabaseConfig::getInstance()
|
||||||
|
{
|
||||||
|
if (m_instance == nullptr) {
|
||||||
|
m_instance = new DatabaseConfig();
|
||||||
|
}
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseConfig::DatabaseConfig()
|
||||||
|
: m_host("localhost")
|
||||||
|
, m_port(3306)
|
||||||
|
, m_databaseName("Client")
|
||||||
|
, m_username("root")
|
||||||
|
, m_password("hzk200407140238")
|
||||||
|
, m_connectTimeout(30)
|
||||||
|
, m_maxConnections(10)
|
||||||
|
, m_settings(nullptr)
|
||||||
|
{
|
||||||
|
initDefaultConfig();
|
||||||
|
loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseConfig::~DatabaseConfig()
|
||||||
|
{
|
||||||
|
if (m_settings) {
|
||||||
|
delete m_settings;
|
||||||
|
m_settings = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseConfig::initDefaultConfig()
|
||||||
|
{
|
||||||
|
// 初始化默认数据库配置
|
||||||
|
m_host = "localhost";
|
||||||
|
m_port = 3306;
|
||||||
|
m_databaseName = "Client";
|
||||||
|
m_username = "root";
|
||||||
|
m_password = "hzk200407140238";
|
||||||
|
m_connectTimeout = 30;
|
||||||
|
m_maxConnections = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseConfig::getHost() const
|
||||||
|
{
|
||||||
|
return m_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatabaseConfig::getPort() const
|
||||||
|
{
|
||||||
|
return m_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseConfig::getDatabaseName() const
|
||||||
|
{
|
||||||
|
return m_databaseName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseConfig::getUsername() const
|
||||||
|
{
|
||||||
|
return m_username;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseConfig::getPassword() const
|
||||||
|
{
|
||||||
|
return m_password;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatabaseConfig::getConnectTimeout() const
|
||||||
|
{
|
||||||
|
return m_connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatabaseConfig::getMaxConnections() const
|
||||||
|
{
|
||||||
|
return m_maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseConfig::setDatabaseConfig(const QString& host, int port, const QString& dbName,
|
||||||
|
const QString& username, const QString& password)
|
||||||
|
{
|
||||||
|
m_host = host;
|
||||||
|
m_port = port;
|
||||||
|
m_databaseName = dbName;
|
||||||
|
m_username = username;
|
||||||
|
m_password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseConfig::loadConfig(const QString& configPath)
|
||||||
|
{
|
||||||
|
QString configFile = configPath;
|
||||||
|
if (configFile.isEmpty()) {
|
||||||
|
// 使用默认配置文件路径
|
||||||
|
configFile = QCoreApplication::applicationDirPath() + "/config/database.ini";
|
||||||
|
|
||||||
|
// 如果应用程序目录没有配置文件,尝试用户配置目录
|
||||||
|
if (!QDir(configFile).exists()) {
|
||||||
|
QString userConfigDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
||||||
|
configFile = userConfigDir + "/BattlefieldExplorationSystem/database.ini";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_settings) {
|
||||||
|
delete m_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settings = new QSettings(configFile, QSettings::IniFormat);
|
||||||
|
|
||||||
|
if (!QDir(configFile).exists()) {
|
||||||
|
qDebug() << "Database config file not found, using defaults:" << configFile;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取数据库配置
|
||||||
|
m_settings->beginGroup("Database");
|
||||||
|
m_host = m_settings->value("host", m_host).toString();
|
||||||
|
m_port = m_settings->value("port", m_port).toInt();
|
||||||
|
m_databaseName = m_settings->value("databaseName", m_databaseName).toString();
|
||||||
|
m_username = m_settings->value("username", m_username).toString();
|
||||||
|
m_password = m_settings->value("password", m_password).toString();
|
||||||
|
m_settings->endGroup();
|
||||||
|
|
||||||
|
// 读取连接池配置
|
||||||
|
m_settings->beginGroup("ConnectionPool");
|
||||||
|
m_connectTimeout = m_settings->value("connectTimeout", m_connectTimeout).toInt();
|
||||||
|
m_maxConnections = m_settings->value("maxConnections", m_maxConnections).toInt();
|
||||||
|
m_settings->endGroup();
|
||||||
|
|
||||||
|
qDebug() << "Database config loaded from:" << configFile;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseConfig::saveConfig(const QString& configPath)
|
||||||
|
{
|
||||||
|
QString configFile = configPath;
|
||||||
|
if (configFile.isEmpty()) {
|
||||||
|
configFile = QCoreApplication::applicationDirPath() + "/config/database.ini";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保配置目录存在
|
||||||
|
QDir configDir = QFileInfo(configFile).absoluteDir();
|
||||||
|
if (!configDir.exists()) {
|
||||||
|
configDir.mkpath(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_settings) {
|
||||||
|
delete m_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settings = new QSettings(configFile, QSettings::IniFormat);
|
||||||
|
|
||||||
|
// 保存数据库配置
|
||||||
|
m_settings->beginGroup("Database");
|
||||||
|
m_settings->setValue("host", m_host);
|
||||||
|
m_settings->setValue("port", m_port);
|
||||||
|
m_settings->setValue("databaseName", m_databaseName);
|
||||||
|
m_settings->setValue("username", m_username);
|
||||||
|
m_settings->setValue("password", m_password);
|
||||||
|
m_settings->endGroup();
|
||||||
|
|
||||||
|
// 保存连接池配置
|
||||||
|
m_settings->beginGroup("ConnectionPool");
|
||||||
|
m_settings->setValue("connectTimeout", m_connectTimeout);
|
||||||
|
m_settings->setValue("maxConnections", m_maxConnections);
|
||||||
|
m_settings->endGroup();
|
||||||
|
|
||||||
|
m_settings->sync();
|
||||||
|
|
||||||
|
qDebug() << "Database config saved to:" << configFile;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseConnectionInfo DatabaseConfig::getConnectionInfo() const
|
||||||
|
{
|
||||||
|
DatabaseConnectionInfo info;
|
||||||
|
info.hostName = m_host;
|
||||||
|
info.port = m_port;
|
||||||
|
info.databaseName = m_databaseName;
|
||||||
|
info.username = m_username;
|
||||||
|
info.password = m_password;
|
||||||
|
return info;
|
||||||
|
}
|
@ -0,0 +1,348 @@
|
|||||||
|
/**
|
||||||
|
* @file DatabaseHelper.cpp
|
||||||
|
* @brief 数据库助手类实现
|
||||||
|
* @author BattlefieldExplorationSystem Team
|
||||||
|
* @date 2024-01-01
|
||||||
|
* @version 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/database/DatabaseHelper.h"
|
||||||
|
#include "core/database/DatabaseConfig.h"
|
||||||
|
#include <QSqlDriver>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
// 静态成员初始化
|
||||||
|
DatabaseHelper* DatabaseHelper::m_instance = nullptr;
|
||||||
|
QMutex DatabaseHelper::m_mutex;
|
||||||
|
QMap<QString, QSqlDatabase> DatabaseHelper::m_connections;
|
||||||
|
int DatabaseHelper::m_connectionCounter = 0;
|
||||||
|
|
||||||
|
DatabaseHelper* DatabaseHelper::getInstance()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (m_instance == nullptr) {
|
||||||
|
m_instance = new DatabaseHelper();
|
||||||
|
}
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseHelper::DatabaseHelper()
|
||||||
|
{
|
||||||
|
// 初始化数据库助手
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseHelper::~DatabaseHelper()
|
||||||
|
{
|
||||||
|
closeAllConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseHelper::generateConnectionName()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return QString("Connection_%1_%2").arg(++m_connectionCounter).arg(reinterpret_cast<quintptr>(QThread::currentThreadId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase DatabaseHelper::createConnection(const QString& connectionName)
|
||||||
|
{
|
||||||
|
QString connName = connectionName;
|
||||||
|
if (connName.isEmpty()) {
|
||||||
|
connName = generateConnectionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
// 如果连接已存在且有效,直接返回
|
||||||
|
if (m_connections.contains(connName)) {
|
||||||
|
QSqlDatabase existingDb = m_connections[connName];
|
||||||
|
if (existingDb.isValid() && existingDb.isOpen()) {
|
||||||
|
return existingDb;
|
||||||
|
} else {
|
||||||
|
// 移除无效连接
|
||||||
|
m_connections.remove(connName);
|
||||||
|
QSqlDatabase::removeDatabase(connName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新连接
|
||||||
|
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", connName);
|
||||||
|
|
||||||
|
if (!configureConnection(db)) {
|
||||||
|
qWarning() << "Failed to configure database connection:" << connName;
|
||||||
|
QSqlDatabase::removeDatabase(connName);
|
||||||
|
return QSqlDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!db.open()) {
|
||||||
|
qWarning() << "Failed to open database connection:" << connName << db.lastError().text();
|
||||||
|
QSqlDatabase::removeDatabase(connName);
|
||||||
|
return QSqlDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connections[connName] = db;
|
||||||
|
qDebug() << "Database connection created:" << connName;
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase DatabaseHelper::createTempConnection(const QString& connectionName)
|
||||||
|
{
|
||||||
|
QString tempConnName = connectionName + "_" + QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
|
return createConnection(tempConnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::configureConnection(QSqlDatabase& db)
|
||||||
|
{
|
||||||
|
DatabaseConfig* config = DatabaseConfig::getInstance();
|
||||||
|
|
||||||
|
db.setHostName(config->getHost());
|
||||||
|
db.setPort(config->getPort());
|
||||||
|
db.setDatabaseName(config->getDatabaseName());
|
||||||
|
db.setUserName(config->getUsername());
|
||||||
|
db.setPassword(config->getPassword());
|
||||||
|
|
||||||
|
// 设置连接选项
|
||||||
|
db.setConnectOptions("MYSQL_OPT_CONNECT_TIMEOUT=" + QString::number(config->getConnectTimeout()) +
|
||||||
|
";MYSQL_OPT_RECONNECT=1");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseHelper::closeConnection(const QString& connectionName)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
if (m_connections.contains(connectionName)) {
|
||||||
|
QSqlDatabase db = m_connections[connectionName];
|
||||||
|
if (db.isOpen()) {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
m_connections.remove(connectionName);
|
||||||
|
QSqlDatabase::removeDatabase(connectionName);
|
||||||
|
qDebug() << "Database connection closed:" << connectionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseHelper::closeAllConnections()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
QStringList connectionNames = m_connections.keys();
|
||||||
|
for (const QString& name : connectionNames) {
|
||||||
|
QSqlDatabase db = m_connections[name];
|
||||||
|
if (db.isOpen()) {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
QSqlDatabase::removeDatabase(name);
|
||||||
|
}
|
||||||
|
m_connections.clear();
|
||||||
|
qDebug() << "All database connections closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::isConnectionValid(const QString& connectionName)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
if (!m_connections.contains(connectionName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase db = m_connections[connectionName];
|
||||||
|
return db.isValid() && db.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery DatabaseHelper::executeQuery(const QString& query, const QString& connectionName)
|
||||||
|
{
|
||||||
|
QString connName = connectionName;
|
||||||
|
if (connName.isEmpty()) {
|
||||||
|
connName = generateConnectionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase db = createConnection(connName);
|
||||||
|
if (!db.isValid()) {
|
||||||
|
qWarning() << "Invalid database connection for query execution";
|
||||||
|
return QSqlQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery sqlQuery(db);
|
||||||
|
if (!sqlQuery.exec(query)) {
|
||||||
|
qWarning() << "Query execution failed:" << query << sqlQuery.lastError().text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqlQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::beginTransaction(const QString& connectionName)
|
||||||
|
{
|
||||||
|
if (!isConnectionValid(connectionName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase db = m_connections[connectionName];
|
||||||
|
return db.transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::commitTransaction(const QString& connectionName)
|
||||||
|
{
|
||||||
|
if (!isConnectionValid(connectionName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase db = m_connections[connectionName];
|
||||||
|
return db.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::rollbackTransaction(const QString& connectionName)
|
||||||
|
{
|
||||||
|
if (!isConnectionValid(connectionName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase db = m_connections[connectionName];
|
||||||
|
return db.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseHelper::getLastError(const QString& connectionName)
|
||||||
|
{
|
||||||
|
if (!isConnectionValid(connectionName)) {
|
||||||
|
return "Invalid connection";
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase db = m_connections[connectionName];
|
||||||
|
return db.lastError().text();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::testConnection()
|
||||||
|
{
|
||||||
|
QSqlDatabase testDb = createConnection("TestConnection");
|
||||||
|
bool success = testDb.isValid() && testDb.isOpen();
|
||||||
|
closeConnection("TestConnection");
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::initializeDatabase()
|
||||||
|
{
|
||||||
|
qDebug() << "Initializing database...";
|
||||||
|
|
||||||
|
if (!testConnection()) {
|
||||||
|
qWarning() << "Database connection test failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTables();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::createTables()
|
||||||
|
{
|
||||||
|
QSqlDatabase db = createConnection("InitConnection");
|
||||||
|
if (!db.isValid()) {
|
||||||
|
qWarning() << "Failed to create database connection for table creation";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
success &= createDevicesTable(db);
|
||||||
|
success &= createOperationLogsTable(db);
|
||||||
|
success &= createSystemConfigTable(db);
|
||||||
|
|
||||||
|
closeConnection("InitConnection");
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
qDebug() << "Database tables created successfully";
|
||||||
|
} else {
|
||||||
|
qWarning() << "Failed to create some database tables";
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::createDevicesTable(QSqlDatabase& db)
|
||||||
|
{
|
||||||
|
QSqlQuery query(db);
|
||||||
|
QString sql = R"(
|
||||||
|
CREATE TABLE IF NOT EXISTS devices (
|
||||||
|
id VARCHAR(50) PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
device_type ENUM('uav', 'dog') NOT NULL,
|
||||||
|
ip VARCHAR(15) NOT NULL,
|
||||||
|
port INT NOT NULL,
|
||||||
|
state INT NOT NULL DEFAULT 0,
|
||||||
|
longitude DOUBLE DEFAULT 0.0,
|
||||||
|
latitude DOUBLE DEFAULT 0.0,
|
||||||
|
signal_strength INT DEFAULT 0,
|
||||||
|
battery_level INT DEFAULT 100,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_device_type (device_type),
|
||||||
|
INDEX idx_state (state)
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
|
||||||
|
if (!query.exec(sql)) {
|
||||||
|
qWarning() << "Failed to create devices table:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::createOperationLogsTable(QSqlDatabase& db)
|
||||||
|
{
|
||||||
|
QSqlQuery query(db);
|
||||||
|
QString sql = R"(
|
||||||
|
CREATE TABLE IF NOT EXISTS device_operation_logs (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
device_id VARCHAR(50) NOT NULL,
|
||||||
|
device_type ENUM('uav', 'dog') NOT NULL,
|
||||||
|
operation VARCHAR(100) NOT NULL,
|
||||||
|
operation_result VARCHAR(20) NOT NULL,
|
||||||
|
operator VARCHAR(50) NOT NULL,
|
||||||
|
operation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_device_id (device_id),
|
||||||
|
INDEX idx_operation_time (operation_time)
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
|
||||||
|
if (!query.exec(sql)) {
|
||||||
|
qWarning() << "Failed to create operation logs table:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseHelper::createSystemConfigTable(QSqlDatabase& db)
|
||||||
|
{
|
||||||
|
QSqlQuery query(db);
|
||||||
|
QString sql = R"(
|
||||||
|
CREATE TABLE IF NOT EXISTS system_config (
|
||||||
|
config_key VARCHAR(100) PRIMARY KEY,
|
||||||
|
config_value TEXT,
|
||||||
|
description VARCHAR(255),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
|
||||||
|
if (!query.exec(sql)) {
|
||||||
|
qWarning() << "Failed to create system config table:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入默认配置
|
||||||
|
QString insertDefaults = R"(
|
||||||
|
INSERT IGNORE INTO system_config (config_key, config_value, description) VALUES
|
||||||
|
('map.default_center_lon', '116.4', '默认地图中心经度'),
|
||||||
|
('map.default_center_lat', '39.9', '默认地图中心纬度'),
|
||||||
|
('system.version', '2.0.0', '系统版本'),
|
||||||
|
('system.name', 'BattlefieldExplorationSystem', '系统名称')
|
||||||
|
)";
|
||||||
|
|
||||||
|
if (!query.exec(insertDefaults)) {
|
||||||
|
qWarning() << "Failed to insert default config:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,475 @@
|
|||||||
|
/**
|
||||||
|
* @file DatabaseManager.cpp
|
||||||
|
* @brief 数据库连接管理器实现
|
||||||
|
* @author BattlefieldExplorationSystem Team
|
||||||
|
* @date 2025-06-30
|
||||||
|
* @version 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/database/DatabaseManager.h"
|
||||||
|
#include "utils/ConfigManager.h"
|
||||||
|
|
||||||
|
// C++标准库头文件
|
||||||
|
#include <chrono>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
// Qt头文件
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
// DatabaseConnection 实现
|
||||||
|
|
||||||
|
DatabaseConnection::DatabaseConnection(const QString& connectionName, bool autoReconnect)
|
||||||
|
: m_connectionName(connectionName)
|
||||||
|
, m_autoReconnect(autoReconnect)
|
||||||
|
, m_isValid(false)
|
||||||
|
{
|
||||||
|
if (QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
m_database = QSqlDatabase::database(m_connectionName);
|
||||||
|
} else {
|
||||||
|
// 创建新连接
|
||||||
|
ConfigManager& config = ConfigManager::getInstance();
|
||||||
|
|
||||||
|
m_database = QSqlDatabase::addDatabase("QMYSQL", m_connectionName);
|
||||||
|
m_database.setHostName(config.getDatabaseHost());
|
||||||
|
m_database.setPort(config.getDatabasePort());
|
||||||
|
m_database.setDatabaseName(config.getDatabaseName());
|
||||||
|
m_database.setUserName(config.getDatabaseUser());
|
||||||
|
|
||||||
|
QString password = config.getDatabasePassword();
|
||||||
|
if (password.isEmpty()) {
|
||||||
|
qCritical() << "Database password not configured! Set BES_DB_PASSWORD environment variable.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_database.setPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试打开连接
|
||||||
|
m_isValid = m_database.open();
|
||||||
|
if (!m_isValid) {
|
||||||
|
qWarning() << "Failed to open database connection:" << m_database.lastError().text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseConnection::~DatabaseConnection()
|
||||||
|
{
|
||||||
|
if (m_database.isOpen()) {
|
||||||
|
m_database.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_connectionName.isEmpty() && QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
QSqlDatabase::removeDatabase(m_connectionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseConnection::DatabaseConnection(DatabaseConnection&& other) noexcept
|
||||||
|
: m_connectionName(std::move(other.m_connectionName))
|
||||||
|
, m_database(std::move(other.m_database))
|
||||||
|
, m_autoReconnect(other.m_autoReconnect)
|
||||||
|
, m_isValid(other.m_isValid)
|
||||||
|
{
|
||||||
|
other.m_isValid = false;
|
||||||
|
other.m_connectionName.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseConnection& DatabaseConnection::operator=(DatabaseConnection&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
// 清理当前资源
|
||||||
|
if (m_database.isOpen()) {
|
||||||
|
m_database.close();
|
||||||
|
}
|
||||||
|
if (!m_connectionName.isEmpty() && QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
QSqlDatabase::removeDatabase(m_connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动资源
|
||||||
|
m_connectionName = std::move(other.m_connectionName);
|
||||||
|
m_database = std::move(other.m_database);
|
||||||
|
m_autoReconnect = other.m_autoReconnect;
|
||||||
|
m_isValid = other.m_isValid;
|
||||||
|
|
||||||
|
other.m_isValid = false;
|
||||||
|
other.m_connectionName.clear();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseConnection::isValid() const
|
||||||
|
{
|
||||||
|
return m_isValid && m_database.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase& DatabaseConnection::database()
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
if (m_autoReconnect && !reconnect()) {
|
||||||
|
throw std::runtime_error("Database connection is not available and reconnection failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_database;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QSqlDatabase& DatabaseConnection::database() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
throw std::runtime_error("Database connection is not available");
|
||||||
|
}
|
||||||
|
return m_database;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<QSqlQuery> DatabaseConnection::executeQuery(const QString& sql)
|
||||||
|
{
|
||||||
|
auto query = std::make_unique<QSqlQuery>(database());
|
||||||
|
|
||||||
|
if (!query->exec(sql)) {
|
||||||
|
qWarning() << "Query execution failed:" << query->lastError().text();
|
||||||
|
qWarning() << "SQL:" << sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<QSqlQuery> DatabaseConnection::executePreparedQuery(const QString& sql,
|
||||||
|
const QVariantList& bindings)
|
||||||
|
{
|
||||||
|
auto query = std::make_unique<QSqlQuery>(database());
|
||||||
|
|
||||||
|
if (!query->prepare(sql)) {
|
||||||
|
qWarning() << "Query preparation failed:" << query->lastError().text();
|
||||||
|
qWarning() << "SQL:" << sql;
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定参数
|
||||||
|
for (const auto& binding : bindings) {
|
||||||
|
query->addBindValue(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query->exec()) {
|
||||||
|
qWarning() << "Prepared query execution failed:" << query->lastError().text();
|
||||||
|
qWarning() << "SQL:" << sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseConnection::beginTransaction()
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_database.transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseConnection::commitTransaction()
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_database.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseConnection::rollbackTransaction()
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_database.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlError DatabaseConnection::lastError() const
|
||||||
|
{
|
||||||
|
return m_database.lastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseConnection::reconnect()
|
||||||
|
{
|
||||||
|
if (m_database.isOpen()) {
|
||||||
|
m_database.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isValid = m_database.open();
|
||||||
|
if (!m_isValid) {
|
||||||
|
qWarning() << "Reconnection failed:" << m_database.lastError().text();
|
||||||
|
} else {
|
||||||
|
qDebug() << "Database reconnection successful for:" << m_connectionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseManager 实现
|
||||||
|
|
||||||
|
std::unique_ptr<DatabaseManager> DatabaseManager::m_instance = nullptr;
|
||||||
|
std::mutex DatabaseManager::m_instanceMutex;
|
||||||
|
|
||||||
|
DatabaseManager& DatabaseManager::getInstance()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_instanceMutex);
|
||||||
|
if (!m_instance) {
|
||||||
|
m_instance.reset(new DatabaseManager());
|
||||||
|
}
|
||||||
|
return *m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseManager::DatabaseManager(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_maxConnections(10)
|
||||||
|
, m_connectionCounter(0)
|
||||||
|
, m_initialized(false)
|
||||||
|
, m_databaseAvailable(false)
|
||||||
|
, m_configManager(&ConfigManager::getInstance())
|
||||||
|
, m_totalConnectionsCreated(0)
|
||||||
|
, m_totalQueriesExecuted(0)
|
||||||
|
, m_failedConnections(0)
|
||||||
|
{
|
||||||
|
// 私有构造函数
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseManager::~DatabaseManager()
|
||||||
|
{
|
||||||
|
closeAllConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::initialize(int maxConnections)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_initialized) {
|
||||||
|
qWarning() << "DatabaseManager already initialized";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_maxConnections = maxConnections;
|
||||||
|
|
||||||
|
// 确保配置管理器已初始化
|
||||||
|
if (!m_configManager->contains("database/host")) {
|
||||||
|
if (!m_configManager->initialize()) {
|
||||||
|
qCritical() << "Failed to initialize ConfigManager";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试初始连接
|
||||||
|
auto testConnection = getConnection("test_connection");
|
||||||
|
if (testConnection && testConnection->isValid()) {
|
||||||
|
m_databaseAvailable = true;
|
||||||
|
qDebug() << "Database is available";
|
||||||
|
} else {
|
||||||
|
m_databaseAvailable = false;
|
||||||
|
qWarning() << "Database is not available";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动健康检查
|
||||||
|
startHealthCheckTimer();
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
emit databaseAvailabilityChanged(m_databaseAvailable);
|
||||||
|
|
||||||
|
qDebug() << "DatabaseManager initialized with max connections:" << m_maxConnections;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<DatabaseConnection> DatabaseManager::getConnection(const QString& connectionName)
|
||||||
|
{
|
||||||
|
std::unique_ptr<DatabaseConnection> connection;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
QString actualConnectionName = connectionName.isEmpty() ?
|
||||||
|
generateConnectionName() : connectionName;
|
||||||
|
|
||||||
|
// 检查是否超过最大连接数
|
||||||
|
if (m_activeConnections.size() >= static_cast<size_t>(m_maxConnections)) {
|
||||||
|
qWarning() << "Maximum connections reached:" << m_maxConnections;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection = std::make_unique<DatabaseConnection>(actualConnectionName);
|
||||||
|
|
||||||
|
if (connection->isValid()) {
|
||||||
|
m_activeConnections.push_back(actualConnectionName);
|
||||||
|
++m_totalConnectionsCreated;
|
||||||
|
} else {
|
||||||
|
++m_failedConnections;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送状态更新信号
|
||||||
|
emit poolStatusChanged(static_cast<int>(m_activeConnections.size()), m_maxConnections);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::testConnection()
|
||||||
|
{
|
||||||
|
auto connection = getConnection("health_check");
|
||||||
|
if (!connection || !connection->isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto query = connection->executeQuery("SELECT 1");
|
||||||
|
return query && query->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseManager::getPoolStatistics() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_statsMutex);
|
||||||
|
|
||||||
|
return QString("DatabaseManager Statistics:\n"
|
||||||
|
"- Active connections: %1/%2\n"
|
||||||
|
"- Total connections created: %3\n"
|
||||||
|
"- Total queries executed: %4\n"
|
||||||
|
"- Failed connections: %5\n"
|
||||||
|
"- Database available: %6")
|
||||||
|
.arg(m_activeConnections.size())
|
||||||
|
.arg(m_maxConnections)
|
||||||
|
.arg(m_totalConnectionsCreated)
|
||||||
|
.arg(m_totalQueriesExecuted)
|
||||||
|
.arg(m_failedConnections)
|
||||||
|
.arg(m_databaseAvailable ? "Yes" : "No");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::closeAllConnections()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
// 移除所有活跃连接
|
||||||
|
for (const auto& connectionName : m_activeConnections) {
|
||||||
|
if (QSqlDatabase::contains(connectionName)) {
|
||||||
|
QSqlDatabase::removeDatabase(connectionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_activeConnections.clear();
|
||||||
|
m_availableConnections.clear();
|
||||||
|
|
||||||
|
qDebug() << "All database connections closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseManager::isDatabaseAvailable() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
return m_databaseAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::performHealthCheck()
|
||||||
|
{
|
||||||
|
bool wasAvailable = m_databaseAvailable;
|
||||||
|
m_databaseAvailable = testConnection();
|
||||||
|
|
||||||
|
if (wasAvailable != m_databaseAvailable) {
|
||||||
|
emit databaseAvailabilityChanged(m_databaseAvailable);
|
||||||
|
qDebug() << "Database availability changed to:" << m_databaseAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理失效的连接
|
||||||
|
cleanupIdleConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::cleanupIdleConnections()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
// 移除无效的连接名称
|
||||||
|
auto it = std::remove_if(m_activeConnections.begin(), m_activeConnections.end(),
|
||||||
|
[](const QString& connectionName) {
|
||||||
|
if (!QSqlDatabase::contains(connectionName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
QSqlDatabase db = QSqlDatabase::database(connectionName);
|
||||||
|
return !db.isOpen();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != m_activeConnections.end()) {
|
||||||
|
int cleaned = static_cast<int>(std::distance(it, m_activeConnections.end()));
|
||||||
|
m_activeConnections.erase(it, m_activeConnections.end());
|
||||||
|
qDebug() << "Cleaned up" << cleaned << "idle connections";
|
||||||
|
|
||||||
|
emit poolStatusChanged(static_cast<int>(m_activeConnections.size()), m_maxConnections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseManager::generateConnectionName()
|
||||||
|
{
|
||||||
|
return QString("connection_%1_%2")
|
||||||
|
.arg(reinterpret_cast<qintptr>(QThread::currentThreadId()))
|
||||||
|
.arg(++m_connectionCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::startHealthCheckTimer()
|
||||||
|
{
|
||||||
|
m_healthCheckTimer = std::make_unique<QTimer>(this);
|
||||||
|
connect(m_healthCheckTimer.get(), &QTimer::timeout,
|
||||||
|
this, &DatabaseManager::performHealthCheck);
|
||||||
|
|
||||||
|
// 每30秒检查一次
|
||||||
|
m_healthCheckTimer->start(30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseTransaction 实现
|
||||||
|
|
||||||
|
DatabaseTransaction::DatabaseTransaction(DatabaseConnection& connection)
|
||||||
|
: m_connection(connection)
|
||||||
|
, m_committed(false)
|
||||||
|
, m_active(false)
|
||||||
|
{
|
||||||
|
m_active = m_connection.beginTransaction();
|
||||||
|
if (!m_active) {
|
||||||
|
qWarning() << "Failed to begin transaction:" << m_connection.lastError().text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseTransaction::~DatabaseTransaction()
|
||||||
|
{
|
||||||
|
if (m_active && !m_committed) {
|
||||||
|
// 自动回滚未提交的事务
|
||||||
|
rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseTransaction::commit()
|
||||||
|
{
|
||||||
|
if (!m_active || m_committed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = m_connection.commitTransaction();
|
||||||
|
if (success) {
|
||||||
|
m_committed = true;
|
||||||
|
m_active = false;
|
||||||
|
} else {
|
||||||
|
qWarning() << "Failed to commit transaction:" << m_connection.lastError().text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseTransaction::rollback()
|
||||||
|
{
|
||||||
|
if (!m_active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = m_connection.rollbackTransaction();
|
||||||
|
m_active = false;
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
qWarning() << "Failed to rollback transaction:" << m_connection.lastError().text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseTransaction::isActive() const
|
||||||
|
{
|
||||||
|
return m_active;
|
||||||
|
}
|
Loading…
Reference in new issue