diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0b2dbcc5 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/src/Client/.gitignore b/src/Client/.gitignore deleted file mode 100644 index 5c42a99f..00000000 --- a/src/Client/.gitignore +++ /dev/null @@ -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/ \ No newline at end of file diff --git a/src/Client/config/README.md b/src/Client/config/README.md new file mode 100644 index 00000000..745bb53f --- /dev/null +++ b/src/Client/config/README.md @@ -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) + +- 使用环境变量进行敏感信息管理 +- 定期更新配置文件模板 +- 在团队文档中记录配置要求 +- 为新成员提供快速配置指南 \ No newline at end of file diff --git a/src/Client/config/database.ini.example b/src/Client/config/database.ini.example new file mode 100644 index 00000000..1193693b --- /dev/null +++ b/src/Client/config/database.ini.example @@ -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 \ No newline at end of file diff --git a/src/Client/config/database.ini.template b/src/Client/config/database.ini.template new file mode 100644 index 00000000..b12d949f --- /dev/null +++ b/src/Client/config/database.ini.template @@ -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 \ No newline at end of file diff --git a/src/Client/include/core/database/DatabaseConfig.h b/src/Client/include/core/database/DatabaseConfig.h new file mode 100644 index 00000000..48d1d41d --- /dev/null +++ b/src/Client/include/core/database/DatabaseConfig.h @@ -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 +#include + +/** + * @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 \ No newline at end of file diff --git a/src/Client/include/core/database/DatabaseHelper.h b/src/Client/include/core/database/DatabaseHelper.h new file mode 100644 index 00000000..79e651be --- /dev/null +++ b/src/Client/include/core/database/DatabaseHelper.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 +#include +#include +#include +#include +#include + +/** + * @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 m_connections; ///< 连接池 + static int m_connectionCounter; ///< 连接计数器 +}; + +#endif // DATABASEHELPER_H \ No newline at end of file diff --git a/src/Client/include/core/database/DatabaseManager.h b/src/Client/include/core/database/DatabaseManager.h new file mode 100644 index 00000000..f4a1db0e --- /dev/null +++ b/src/Client/include/core/database/DatabaseManager.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 +#include +#include +#include +#include + +// Qt头文件 +#include +#include +#include +#include +#include +#include +#include + +// 前向声明 +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 查询对象 + */ + std::unique_ptr executeQuery(const QString& sql); + + /** + * @brief 执行预编译查询 + * @param sql SQL语句 + * @param bindings 绑定参数 + * @return std::unique_ptr 查询对象 + */ + std::unique_ptr 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 连接对象 + */ + std::unique_ptr 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 m_instance; ///< 单例实例 + static std::mutex m_instanceMutex; ///< 实例互斥锁 + + mutable std::mutex m_mutex; ///< 访问互斥锁 + std::vector m_availableConnections; ///< 可用连接池 + std::vector m_activeConnections; ///< 活跃连接池 + + int m_maxConnections; ///< 最大连接数 + int m_connectionCounter; ///< 连接计数器 + bool m_initialized; ///< 初始化状态 + bool m_databaseAvailable; ///< 数据库可用状态 + + std::unique_ptr 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 \ No newline at end of file diff --git a/src/Client/src/core/database/DatabaseConfig.cpp b/src/Client/src/core/database/DatabaseConfig.cpp new file mode 100644 index 00000000..999cdfa3 --- /dev/null +++ b/src/Client/src/core/database/DatabaseConfig.cpp @@ -0,0 +1,198 @@ +/** + * @file DatabaseConfig.cpp + * @brief 数据库配置类实现 + * @author BattlefieldExplorationSystem Team + * @date 2024-01-01 + * @version 2.0 + */ + +#include "core/database/DatabaseConfig.h" +#include +#include +#include +#include + +// 静态成员初始化 +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; +} \ No newline at end of file diff --git a/src/Client/src/core/database/DatabaseHelper.cpp b/src/Client/src/core/database/DatabaseHelper.cpp new file mode 100644 index 00000000..3de2a89f --- /dev/null +++ b/src/Client/src/core/database/DatabaseHelper.cpp @@ -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 +#include +#include +#include +#include + +// 静态成员初始化 +DatabaseHelper* DatabaseHelper::m_instance = nullptr; +QMutex DatabaseHelper::m_mutex; +QMap 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(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; +} \ No newline at end of file diff --git a/src/Client/src/core/database/DatabaseManager.cpp b/src/Client/src/core/database/DatabaseManager.cpp new file mode 100644 index 00000000..714f310d --- /dev/null +++ b/src/Client/src/core/database/DatabaseManager.cpp @@ -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 +#include + +// Qt头文件 +#include +#include +#include +#include +#include +#include + +// 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 DatabaseConnection::executeQuery(const QString& sql) +{ + auto query = std::make_unique(database()); + + if (!query->exec(sql)) { + qWarning() << "Query execution failed:" << query->lastError().text(); + qWarning() << "SQL:" << sql; + } + + return query; +} + +std::unique_ptr DatabaseConnection::executePreparedQuery(const QString& sql, + const QVariantList& bindings) +{ + auto query = std::make_unique(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::m_instance = nullptr; +std::mutex DatabaseManager::m_instanceMutex; + +DatabaseManager& DatabaseManager::getInstance() +{ + std::lock_guard 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 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 DatabaseManager::getConnection(const QString& connectionName) +{ + std::unique_ptr connection; + + { + std::lock_guard lock(m_mutex); + + QString actualConnectionName = connectionName.isEmpty() ? + generateConnectionName() : connectionName; + + // 检查是否超过最大连接数 + if (m_activeConnections.size() >= static_cast(m_maxConnections)) { + qWarning() << "Maximum connections reached:" << m_maxConnections; + return nullptr; + } + + connection = std::make_unique(actualConnectionName); + + if (connection->isValid()) { + m_activeConnections.push_back(actualConnectionName); + ++m_totalConnectionsCreated; + } else { + ++m_failedConnections; + return nullptr; + } + } + + // 发送状态更新信号 + emit poolStatusChanged(static_cast(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 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 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 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 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(std::distance(it, m_activeConnections.end())); + m_activeConnections.erase(it, m_activeConnections.end()); + qDebug() << "Cleaned up" << cleaned << "idle connections"; + + emit poolStatusChanged(static_cast(m_activeConnections.size()), m_maxConnections); + } +} + +QString DatabaseManager::generateConnectionName() +{ + return QString("connection_%1_%2") + .arg(reinterpret_cast(QThread::currentThreadId())) + .arg(++m_connectionCounter); +} + +void DatabaseManager::startHealthCheckTimer() +{ + m_healthCheckTimer = std::make_unique(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; +} \ No newline at end of file diff --git a/src/Client/src/utils/ConfigManager.cpp b/src/Client/src/utils/ConfigManager.cpp index d4f1f05d..7ff79846 100644 --- a/src/Client/src/utils/ConfigManager.cpp +++ b/src/Client/src/utils/ConfigManager.cpp @@ -159,9 +159,26 @@ QString ConfigManager::getDatabaseUser() const QString ConfigManager::getDatabasePassword() const { - // 直接返回项目固定密码,便于团队协作和项目交接 - qDebug() << "Using hardcoded database password for project handover"; - return QString("hzk200407140238"); + // 优先使用环境变量,然后是配置文件,最后是默认值 + // Priority: Environment variable > Configuration file > Default value + + // 1. 检查环境变量 + QString envPassword = qgetenv("BES_DB_PASSWORD"); + if (!envPassword.isEmpty()) { + qDebug() << "Using database password from environment variable"; + return envPassword; + } + + // 2. 从配置文件读取 + QString configPassword = getValue(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,