配置管理优化:解决团队成员配置冲突问题

- 添加项目根目录.gitignore统一管理忽略规则
- 创建数据库配置文件模板和示例便于团队协作
- 优化ConfigManager支持多层配置加载优先级
- 移除冗余的子目录.gitignore文件
- 完善配置文档说明团队协作流程
pull/8/head
123 1 week ago
parent 1bfbbd5122
commit 41d2933249

49
.gitignore vendored

@ -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,99 @@
# 配置文件说明 (Configuration Guide)
## 团队协作配置管理 (Team Collaboration Configuration Management)
为了避免团队成员间的配置冲突,本项目采用以下配置管理策略:
### 1. 个人配置文件 (Personal Configuration Files)
**database.ini** - 个人数据库配置文件(已添加到 .gitignore
- 此文件不会被 Git 跟踪,每个开发者可以有自己的配置
- 从模板文件复制并修改为你的个人设置
### 2. 配置文件模板 (Configuration Templates)
**database.ini.template** - 配置文件模板
- 复制此文件为 `database.ini` 并根据你的环境修改
- 包含所有必要的配置项和说明
**database.ini.example** - 配置示例文件
- 展示常见的配置选项和格式
- 可作为参考来设置你的个人配置
### 3. 配置加载优先级 (Configuration Loading Priority)
系统按以下优先级加载配置:
1. **环境变量** (Environment Variables) - 最高优先级
- `BES_DB_HOST` - 数据库主机地址
- `BES_DB_PORT` - 数据库端口
- `BES_DB_NAME` - 数据库名称
- `BES_DB_USER` - 数据库用户名
- `BES_DB_PASSWORD` - 数据库密码
2. **配置文件** (Configuration File) - 中等优先级
- `database.ini` - 个人配置文件
3. **默认值** (Default Values) - 最低优先级
- 代码中定义的默认配置
### 4. 快速开始 (Quick Start)
#### 方法一:使用配置文件 (Using Configuration File)
```bash
# 1. 复制模板文件
cp database.ini.template database.ini
# 2. 编辑配置文件
nano database.ini
# 3. 修改密码和其他配置项
```
#### 方法二:使用环境变量 (Using Environment Variables)
```bash
# 设置环境变量
export BES_DB_HOST=localhost
export BES_DB_PORT=3306
export BES_DB_NAME=Client
export BES_DB_USER=root
export BES_DB_PASSWORD=your_password_here
# 运行应用程序
./BattlefieldExplorationSystem
```
#### 方法三:永久环境变量 (Permanent Environment Variables)
```bash
# 添加到 ~/.bashrc 或 ~/.zshrc
echo 'export BES_DB_PASSWORD=your_password_here' >> ~/.bashrc
source ~/.bashrc
```
### 5. 注意事项 (Important Notes)
- **不要提交个人配置文件**`database.ini` 已添加到 `.gitignore`
- **不要在代码中硬编码密码**:使用环境变量或配置文件
- **模板文件可以更新**:团队可以更新 `.template``.example` 文件
- **向后兼容性**:现有代码仍然可以工作,但建议迁移到新的配置方式
### 6. 故障排除 (Troubleshooting)
#### 数据库连接失败
1. 检查数据库服务是否运行
2. 验证配置文件中的参数是否正确
3. 确认环境变量是否设置正确
4. 查看应用程序日志中的详细错误信息
#### 配置不生效
1. 检查配置文件是否存在于正确位置
2. 验证环境变量是否正确设置
3. 重启应用程序以加载新配置
4. 检查配置文件格式是否正确
### 7. 团队协作建议 (Team Collaboration Recommendations)
- 使用环境变量进行敏感信息管理
- 定期更新配置文件模板
- 在团队文档中记录配置要求
- 为新成员提供快速配置指南

@ -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,40 @@
# 战场探索系统数据库配置文件模板
# Database Configuration Template for BattlefieldExplorationSystem
#
# 复制此文件为 database.ini 并根据你的环境修改配置
# Copy this file to database.ini and modify the configuration for your environment
[Database]
# 数据库服务器地址 (Database server host)
host=localhost
# 数据库端口 (Database port)
port=3306
# 数据库名称 (Database name)
databaseName=Client
# 数据库用户名 (Database username)
username=root
# 数据库密码 (Database password)
# 请修改为你的MySQL密码 (Please change to your MySQL password)
password=your_password_here
# 连接超时时间,单位毫秒 (Connection timeout in milliseconds)
connectionTimeout=30000
# 数据库驱动名称 (Database driver name)
driverName=QMYSQL
# 配置说明:
# 1. 如果你的MySQL用户名不是root请修改username字段
# 2. 请务必修改password字段为你的MySQL密码
# 3. 如果你的MySQL运行在不同的主机或端口请修改host和port字段
# 4. 如果你使用不同的数据库名称请修改databaseName字段
#
# Configuration Notes:
# 1. If your MySQL username is not 'root', modify the username field
# 2. You MUST modify the password field to your MySQL password
# 3. If your MySQL runs on different host or port, modify host and port fields
# 4. If you use different database name, modify databaseName field

@ -0,0 +1,157 @@
/**
* @file DatabaseConfig.h
* @brief
* @author BattlefieldExplorationSystem Team
* @date 2024-01-01
* @version 2.0
*/
#ifndef DATABASECONFIG_H
#define DATABASECONFIG_H
#include <QString>
#include <QSettings>
/**
* @struct DatabaseConnectionInfo
* @brief
*/
struct DatabaseConnectionInfo
{
QString hostName; ///< 主机名
int port; ///< 端口
QString databaseName; ///< 数据库名
QString username; ///< 用户名
QString password; ///< 密码
};
/**
* @class DatabaseConfig
* @brief
*
*
*
*/
class DatabaseConfig
{
public:
/**
* @brief
* @return DatabaseConfig
*/
static DatabaseConfig* getInstance();
/**
* @brief
* @return
*/
QString getHost() const;
/**
* @brief
* @return
*/
int getPort() const;
/**
* @brief
* @return
*/
QString getDatabaseName() const;
/**
* @brief
* @return
*/
QString getUsername() const;
/**
* @brief
* @return
*/
QString getPassword() const;
/**
* @brief
* @return
*/
int getConnectTimeout() const;
/**
* @brief
* @return
*/
int getMaxConnections() const;
/**
* @brief
* @param host
* @param port
* @param dbName
* @param username
* @param password
*/
void setDatabaseConfig(const QString& host, int port, const QString& dbName,
const QString& username, const QString& password);
/**
* @brief
* @param configPath
* @return
*/
bool loadConfig(const QString& configPath = "");
/**
* @brief
* @param configPath
* @return
*/
bool saveConfig(const QString& configPath = "");
/**
* @brief
* @return
*/
DatabaseConnectionInfo getConnectionInfo() const;
private:
/**
* @brief
*/
DatabaseConfig();
/**
* @brief
*/
~DatabaseConfig();
/**
* @brief
*/
DatabaseConfig(const DatabaseConfig&) = delete;
/**
* @brief
*/
DatabaseConfig& operator=(const DatabaseConfig&) = delete;
/**
* @brief
*/
void initDefaultConfig();
private:
static DatabaseConfig* m_instance; ///< 单例实例
QString m_host; ///< 数据库主机地址
int m_port; ///< 数据库端口
QString m_databaseName; ///< 数据库名称
QString m_username; ///< 数据库用户名
QString m_password; ///< 数据库密码
int m_connectTimeout; ///< 连接超时时间
int m_maxConnections; ///< 最大连接数
QSettings* m_settings; ///< 配置文件管理器
};
#endif // DATABASECONFIG_H

@ -0,0 +1,183 @@
/**
* @file DatabaseHelper.h
* @brief
* @author BattlefieldExplorationSystem Team
* @date 2024-01-01
* @version 2.0
*/
#ifndef DATABASEHELPER_H
#define DATABASEHELPER_H
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QString>
#include <QMutex>
#include <QMap>
/**
* @class DatabaseHelper
* @brief
*
*
* 线
*/
class DatabaseHelper
{
public:
/**
* @brief
* @return DatabaseHelper
*/
static DatabaseHelper* getInstance();
/**
* @brief
* @param connectionName
* @return QSqlDatabase
*/
static QSqlDatabase createConnection(const QString& connectionName = "");
/**
* @brief
* @param connectionName
* @return QSqlDatabase
*/
static QSqlDatabase createTempConnection(const QString& connectionName);
/**
* @brief
* @param connectionName
*/
static void closeConnection(const QString& connectionName);
/**
* @brief
*/
static void closeAllConnections();
/**
* @brief
* @param connectionName
* @return
*/
static bool isConnectionValid(const QString& connectionName);
/**
* @brief SQL
* @param query SQL
* @param connectionName
* @return
*/
static QSqlQuery executeQuery(const QString& query, const QString& connectionName = "");
/**
* @brief
* @param connectionName
* @return
*/
static bool beginTransaction(const QString& connectionName = "");
/**
* @brief
* @param connectionName
* @return
*/
static bool commitTransaction(const QString& connectionName = "");
/**
* @brief
* @param connectionName
* @return
*/
static bool rollbackTransaction(const QString& connectionName = "");
/**
* @brief
* @return
*/
static bool initializeDatabase();
/**
* @brief
* @return
*/
static bool createTables();
/**
* @brief
* @param connectionName
* @return
*/
static QString getLastError(const QString& connectionName = "");
/**
* @brief
* @return
*/
static bool testConnection();
private:
/**
* @brief
*/
DatabaseHelper();
/**
* @brief
*/
~DatabaseHelper();
/**
* @brief
*/
DatabaseHelper(const DatabaseHelper&) = delete;
/**
* @brief
*/
DatabaseHelper& operator=(const DatabaseHelper&) = delete;
/**
* @brief
* @return
*/
static QString generateConnectionName();
/**
* @brief
* @param db
* @return
*/
static bool configureConnection(QSqlDatabase& db);
/**
* @brief
* @param db
* @return
*/
static bool createDevicesTable(QSqlDatabase& db);
/**
* @brief
* @param db
* @return
*/
static bool createOperationLogsTable(QSqlDatabase& db);
/**
* @brief
* @param db
* @return
*/
static bool createSystemConfigTable(QSqlDatabase& db);
private:
static DatabaseHelper* m_instance; ///< 单例实例
static QMutex m_mutex; ///< 线程安全锁
static QMap<QString, QSqlDatabase> m_connections; ///< 连接池
static int m_connectionCounter; ///< 连接计数器
};
#endif // DATABASEHELPER_H

@ -0,0 +1,350 @@
/**
* @file DatabaseManager.h
* @brief - RAII
* @author BattlefieldExplorationSystem Team
* @date 2024-01-01
* @version 2.0
*
*
* - RAII
* -
* - 线
* -
* -
*
* @note 访
* @since 2.0
*/
#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H
// C++标准库头文件
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
// Qt头文件
#include <QObject>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QString>
#include <QTimer>
#include <QVariant>
// 前向声明
class ConfigManager;
/**
* @class DatabaseConnection
* @brief RAII
*
*
*
*
* -
* -
* -
* -
*
* @since 2.0
*/
class DatabaseConnection
{
public:
/**
* @brief -
* @param connectionName
* @param autoReconnect
*/
explicit DatabaseConnection(const QString& connectionName, bool autoReconnect = true);
/**
* @brief -
*/
~DatabaseConnection();
// 禁用拷贝构造和拷贝赋值
DatabaseConnection(const DatabaseConnection&) = delete;
DatabaseConnection& operator=(const DatabaseConnection&) = delete;
// 支持移动语义
DatabaseConnection(DatabaseConnection&& other) noexcept;
DatabaseConnection& operator=(DatabaseConnection&& other) noexcept;
/**
* @brief
* @return bool
*/
bool isValid() const;
/**
* @brief
* @return QSqlDatabase&
* @throws std::runtime_error
*/
QSqlDatabase& database();
/**
* @brief
* @return const QSqlDatabase&
* @throws std::runtime_error
*/
const QSqlDatabase& database() const;
/**
* @brief SQL
* @param sql SQL
* @return std::unique_ptr<QSqlQuery>
*/
std::unique_ptr<QSqlQuery> executeQuery(const QString& sql);
/**
* @brief
* @param sql SQL
* @param bindings
* @return std::unique_ptr<QSqlQuery>
*/
std::unique_ptr<QSqlQuery> executePreparedQuery(const QString& sql,
const QVariantList& bindings = QVariantList());
/**
* @brief
* @return bool
*/
bool beginTransaction();
/**
* @brief
* @return bool
*/
bool commitTransaction();
/**
* @brief
* @return bool
*/
bool rollbackTransaction();
/**
* @brief
* @return QSqlError
*/
QSqlError lastError() const;
private:
/**
* @brief
* @return bool
*/
bool reconnect();
private:
QString m_connectionName; ///< 连接名称
QSqlDatabase m_database; ///< 数据库对象
bool m_autoReconnect; ///< 是否自动重连
bool m_isValid; ///< 连接状态
};
/**
* @class DatabaseManager
* @brief
*
* 线访
*
*
* -
* -
* -
* -
*
* @note
* @since 2.0
*/
class DatabaseManager : public QObject
{
Q_OBJECT
public:
/**
* @brief
* @return DatabaseManager&
*/
static DatabaseManager& getInstance();
/**
* @brief
*/
~DatabaseManager() override;
// 禁用拷贝和移动
DatabaseManager(const DatabaseManager&) = delete;
DatabaseManager& operator=(const DatabaseManager&) = delete;
DatabaseManager(DatabaseManager&&) = delete;
DatabaseManager& operator=(DatabaseManager&&) = delete;
/**
* @brief
* @param maxConnections
* @return bool
*/
bool initialize(int maxConnections = 10);
/**
* @brief
* @param connectionName
* @return std::unique_ptr<DatabaseConnection>
*/
std::unique_ptr<DatabaseConnection> getConnection(const QString& connectionName = QString());
/**
* @brief
* @return bool
*/
bool testConnection();
/**
* @brief
* @return QString
*/
QString getPoolStatistics() const;
/**
* @brief
*/
void closeAllConnections();
/**
* @brief
* @return bool
*/
bool isDatabaseAvailable() const;
public slots:
/**
* @brief
*/
void performHealthCheck();
/**
* @brief
*/
void cleanupIdleConnections();
signals:
/**
* @brief
* @param available
*/
void databaseAvailabilityChanged(bool available);
/**
* @brief
* @param activeConnections
* @param totalConnections
*/
void poolStatusChanged(int activeConnections, int totalConnections);
private:
/**
* @brief
*/
explicit DatabaseManager(QObject* parent = nullptr);
/**
* @brief
* @param connectionName
* @return QSqlDatabase
*/
QSqlDatabase createConnection(const QString& connectionName);
/**
* @brief
* @return QString
*/
QString generateConnectionName();
/**
* @brief
*/
void startHealthCheckTimer();
private:
static std::unique_ptr<DatabaseManager> m_instance; ///< 单例实例
static std::mutex m_instanceMutex; ///< 实例互斥锁
mutable std::mutex m_mutex; ///< 访问互斥锁
std::vector<QString> m_availableConnections; ///< 可用连接池
std::vector<QString> m_activeConnections; ///< 活跃连接池
int m_maxConnections; ///< 最大连接数
int m_connectionCounter; ///< 连接计数器
bool m_initialized; ///< 初始化状态
bool m_databaseAvailable; ///< 数据库可用状态
std::unique_ptr<QTimer> m_healthCheckTimer; ///< 健康检查定时器
ConfigManager* m_configManager; ///< 配置管理器
// 统计信息
mutable std::mutex m_statsMutex; ///< 统计信息互斥锁
int m_totalConnectionsCreated; ///< 总创建连接数
int m_totalQueriesExecuted; ///< 总执行查询数
int m_failedConnections; ///< 失败连接数
};
/**
* @class DatabaseTransaction
* @brief RAII
*
*
*
* @since 2.0
*/
class DatabaseTransaction
{
public:
/**
* @brief -
* @param connection
*/
explicit DatabaseTransaction(DatabaseConnection& connection);
/**
* @brief -
*/
~DatabaseTransaction();
// 禁用拷贝和移动
DatabaseTransaction(const DatabaseTransaction&) = delete;
DatabaseTransaction& operator=(const DatabaseTransaction&) = delete;
DatabaseTransaction(DatabaseTransaction&&) = delete;
DatabaseTransaction& operator=(DatabaseTransaction&&) = delete;
/**
* @brief
* @return bool
*/
bool commit();
/**
* @brief
* @return bool
*/
bool rollback();
/**
* @brief
* @return bool
*/
bool isActive() const;
private:
DatabaseConnection& m_connection; ///< 数据库连接引用
bool m_committed; ///< 是否已提交
bool m_active; ///< 事务是否活跃
};
#endif // DATABASEMANAGER_H

@ -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;
}

@ -159,9 +159,26 @@ QString ConfigManager::getDatabaseUser() const
QString ConfigManager::getDatabasePassword() const QString ConfigManager::getDatabasePassword() const
{ {
// 直接返回项目固定密码,便于团队协作和项目交接 // 优先使用环境变量,然后是配置文件,最后是默认值
qDebug() << "Using hardcoded database password for project handover"; // Priority: Environment variable > Configuration file > Default value
return QString("hzk200407140238");
// 1. 检查环境变量
QString envPassword = qgetenv("BES_DB_PASSWORD");
if (!envPassword.isEmpty()) {
qDebug() << "Using database password from environment variable";
return envPassword;
}
// 2. 从配置文件读取
QString configPassword = getValue<QString>(KEY_DB_PASSWORD, "");
if (!configPassword.isEmpty()) {
qDebug() << "Using database password from configuration file";
return decryptString(configPassword);
}
// 3. 使用默认值(仅用于向后兼容)
qDebug() << "Using default database password (consider setting BES_DB_PASSWORD environment variable)";
return QString("your_password_here");
} }
void ConfigManager::setDatabaseConfig(const QString& host, int port, const QString& database, void ConfigManager::setDatabaseConfig(const QString& host, int port, const QString& database,

Loading…
Cancel
Save