Merge branch 'main' of https://bdgit.educoder.net/pp3xivymc/Software_Architecture
commit
ab677d5a12
@ -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
|
@ -0,0 +1,126 @@
|
||||
<execution>
|
||||
<constraint>
|
||||
## 学术项目界面约束
|
||||
- **评分时间限制**:界面需要在短时间内给老师留下深刻印象
|
||||
- **演示环境约束**:需要适应课堂投影和不同显示设备
|
||||
- **功能展示要求**:界面必须能清晰展现所有核心功能
|
||||
- **团队协作体现**:界面需要体现团队分工和技术整合
|
||||
- **文档配合约束**:界面设计需要与技术文档保持一致
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
## 学术界面强制标准
|
||||
- **功能完整性优先**:所有要求功能必须有对应界面入口
|
||||
- **专业性体现必须**:界面必须体现学生的技术水平
|
||||
- **演示友好性**:界面必须便于课堂演示和功能展示
|
||||
- **创新点突出**:必须有超出基本要求的设计亮点
|
||||
- **稳定性保证**:演示过程中不能出现界面错误
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
## 学术界面设计指南
|
||||
- **第一印象优化**:应用启动后的首屏要专业美观
|
||||
- **核心功能突出**:主要功能入口要显眼易找
|
||||
- **技术深度展现**:通过界面细节体现技术实力
|
||||
- **用户引导清晰**:操作流程要直观易懂
|
||||
- **错误处理完善**:异常情况要有友好提示
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
## 学术界面标准化流程
|
||||
|
||||
### 评分标准对齐检查
|
||||
```mermaid
|
||||
graph TD
|
||||
A[功能完整性检查] --> B[专业美观度评估]
|
||||
B --> C[用户体验测试]
|
||||
C --> D[技术深度体现]
|
||||
D --> E[创新亮点识别]
|
||||
E --> F[演示效果验证]
|
||||
F --> G{达到学术标准?}
|
||||
G -->|是| H[标准合格]
|
||||
G -->|否| I[针对性改进]
|
||||
I --> A
|
||||
```
|
||||
|
||||
### 老师评分视角模拟
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[应用启动] --> B[第一印象评分]
|
||||
B --> C[功能演示]
|
||||
C --> D[交互体验]
|
||||
D --> E[技术亮点]
|
||||
E --> F[整体评价]
|
||||
F --> G[最终评分]
|
||||
```
|
||||
|
||||
**评分关键节点:**
|
||||
1. **启动印象** (20%):应用启动速度和首屏效果
|
||||
2. **功能展示** (30%):核心功能的界面表现
|
||||
3. **交互体验** (25%):操作流程的流畅度
|
||||
4. **技术深度** (15%):界面体现的技术水平
|
||||
5. **创新亮点** (10%):超出预期的设计创新
|
||||
|
||||
### 差异化竞争策略
|
||||
```mermaid
|
||||
mindmap
|
||||
root((竞争优势))
|
||||
技术深度
|
||||
复杂QSS样式
|
||||
自定义控件
|
||||
高级布局技巧
|
||||
视觉设计
|
||||
现代化UI风格
|
||||
专业配色方案
|
||||
精致图标设计
|
||||
交互创新
|
||||
流畅动画效果
|
||||
智能操作引导
|
||||
个性化设置
|
||||
功能完整
|
||||
全面功能覆盖
|
||||
异常处理完善
|
||||
性能优化到位
|
||||
```
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
## 学术界面评分标准
|
||||
|
||||
### 功能完整性 (30分)
|
||||
- ✅ 所有要求功能都有界面入口 (10分)
|
||||
- ✅ 功能操作流程完整清晰 (10分)
|
||||
- ✅ 异常情况处理完善 (5分)
|
||||
- ✅ 界面与功能逻辑一致 (5分)
|
||||
|
||||
### 专业美观度 (25分)
|
||||
- ✅ 整体视觉设计专业 (8分)
|
||||
- ✅ 色彩搭配协调统一 (6分)
|
||||
- ✅ 控件样式现代美观 (6分)
|
||||
- ✅ 布局合理有序 (5分)
|
||||
|
||||
### 用户体验 (20分)
|
||||
- ✅ 操作流程直观简洁 (8分)
|
||||
- ✅ 界面响应及时准确 (6分)
|
||||
- ✅ 错误提示友好明确 (3分)
|
||||
- ✅ 学习成本低 (3分)
|
||||
|
||||
### 技术深度 (15分)
|
||||
- ✅ Qt技术运用熟练 (6分)
|
||||
- ✅ 代码结构清晰规范 (4分)
|
||||
- ✅ 性能优化到位 (3分)
|
||||
- ✅ 跨平台兼容性好 (2分)
|
||||
|
||||
### 创新亮点 (10分)
|
||||
- ✅ 有超出基本要求的设计 (4分)
|
||||
- ✅ 技术实现有创新性 (3分)
|
||||
- ✅ 用户体验有独特之处 (2分)
|
||||
- ✅ 整体方案有思考深度 (1分)
|
||||
|
||||
### 演示效果加分项
|
||||
- 🌟 界面启动给人惊艳感 (+2分)
|
||||
- 🌟 功能演示流畅无卡顿 (+2分)
|
||||
- 🌟 细节处理体现工匠精神 (+1分)
|
||||
- 🌟 整体方案体现团队协作 (+1分)
|
||||
</criteria>
|
||||
</execution>
|
@ -0,0 +1,266 @@
|
||||
# Qt 5.15 界面开发核心知识
|
||||
|
||||
## QSS样式表精通
|
||||
|
||||
### 基础语法结构
|
||||
```css
|
||||
/* 选择器语法 */
|
||||
QWidget { background-color: #f0f0f0; }
|
||||
QPushButton#myButton { color: blue; }
|
||||
QLabel[class="title"] { font-size: 18px; }
|
||||
|
||||
/* 伪状态选择器 */
|
||||
QPushButton:hover { background-color: #e0e0e0; }
|
||||
QPushButton:pressed { background-color: #d0d0d0; }
|
||||
QPushButton:disabled { color: gray; }
|
||||
```
|
||||
|
||||
### 现代化按钮样式
|
||||
```css
|
||||
QPushButton {
|
||||
background-color: #4CAF50;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: #3d8b40;
|
||||
}
|
||||
```
|
||||
|
||||
### 输入框美化
|
||||
```css
|
||||
QLineEdit {
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
QLineEdit:focus {
|
||||
border-color: #4CAF50;
|
||||
outline: none;
|
||||
}
|
||||
```
|
||||
|
||||
## 布局管理器精通
|
||||
|
||||
### QVBoxLayout 垂直布局
|
||||
```cpp
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(titleLabel);
|
||||
layout->addSpacing(10); // 添加间距
|
||||
layout->addWidget(contentWidget);
|
||||
layout->addStretch(); // 添加弹性空间
|
||||
layout->setContentsMargins(20, 20, 20, 20); // 设置边距
|
||||
```
|
||||
|
||||
### QGridLayout 网格布局
|
||||
```cpp
|
||||
QGridLayout *gridLayout = new QGridLayout;
|
||||
gridLayout->addWidget(label1, 0, 0);
|
||||
gridLayout->addWidget(lineEdit1, 0, 1);
|
||||
gridLayout->addWidget(label2, 1, 0);
|
||||
gridLayout->addWidget(lineEdit2, 1, 1);
|
||||
gridLayout->setColumnStretch(1, 1); // 第二列可拉伸
|
||||
```
|
||||
|
||||
### QHBoxLayout 水平布局
|
||||
```cpp
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(okButton);
|
||||
buttonLayout->addWidget(cancelButton);
|
||||
```
|
||||
|
||||
## 控件美化技巧
|
||||
|
||||
### QTableWidget 表格美化
|
||||
```css
|
||||
QTableWidget {
|
||||
gridline-color: #e0e0e0;
|
||||
background-color: white;
|
||||
alternate-background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
QTableWidget::item {
|
||||
padding: 8px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QTableWidget::item:selected {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
QHeaderView::section {
|
||||
background-color: #f5f5f5;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
font-weight: bold;
|
||||
}
|
||||
```
|
||||
|
||||
### QComboBox 下拉框美化
|
||||
```css
|
||||
QComboBox {
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
QComboBox::drop-down {
|
||||
border: none;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
QComboBox::down-arrow {
|
||||
image: url(:/icons/down-arrow.png);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
```
|
||||
|
||||
## 自定义控件开发
|
||||
|
||||
### 自定义按钮类
|
||||
```cpp
|
||||
class CustomButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CustomButton(const QString &text, QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void enterEvent(QEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
|
||||
private:
|
||||
bool m_hovered;
|
||||
QPropertyAnimation *m_animation;
|
||||
};
|
||||
```
|
||||
|
||||
### 渐变背景实现
|
||||
```cpp
|
||||
void CustomWidget::paintEvent(QPaintEvent *event) {
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QLinearGradient gradient(0, 0, 0, height());
|
||||
gradient.setColorAt(0, QColor(240, 240, 240));
|
||||
gradient.setColorAt(1, QColor(220, 220, 220));
|
||||
|
||||
painter.fillRect(rect(), gradient);
|
||||
}
|
||||
```
|
||||
|
||||
## 信号槽机制优化
|
||||
|
||||
### Lambda表达式连接
|
||||
```cpp
|
||||
connect(button, &QPushButton::clicked, [this]() {
|
||||
// 处理点击事件
|
||||
updateUI();
|
||||
});
|
||||
```
|
||||
|
||||
### 自定义信号定义
|
||||
```cpp
|
||||
class CustomWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void dataChanged(const QString &data);
|
||||
void statusUpdated(int status);
|
||||
|
||||
public slots:
|
||||
void onDataReceived(const QByteArray &data);
|
||||
void onStatusChanged(bool connected);
|
||||
};
|
||||
```
|
||||
|
||||
## 性能优化技巧
|
||||
|
||||
### 减少重绘次数
|
||||
```cpp
|
||||
// 批量更新时禁用重绘
|
||||
widget->setUpdatesEnabled(false);
|
||||
// 执行多个更新操作
|
||||
widget->setUpdatesEnabled(true);
|
||||
widget->update(); // 手动触发重绘
|
||||
```
|
||||
|
||||
### 使用样式表缓存
|
||||
```cpp
|
||||
// 在类初始化时设置样式表,避免重复设置
|
||||
static const QString buttonStyle =
|
||||
"QPushButton { background-color: #4CAF50; }";
|
||||
button->setStyleSheet(buttonStyle);
|
||||
```
|
||||
|
||||
## 响应式设计
|
||||
|
||||
### 自适应布局
|
||||
```cpp
|
||||
void MainWindow::resizeEvent(QResizeEvent *event) {
|
||||
QMainWindow::resizeEvent(event);
|
||||
|
||||
// 根据窗口大小调整布局
|
||||
if (width() < 800) {
|
||||
// 小屏幕布局
|
||||
switchToCompactLayout();
|
||||
} else {
|
||||
// 大屏幕布局
|
||||
switchToNormalLayout();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### DPI适配
|
||||
```cpp
|
||||
// 获取系统DPI缩放比例
|
||||
qreal dpiScale = qApp->devicePixelRatio();
|
||||
int scaledSize = static_cast<int>(16 * dpiScale);
|
||||
font.setPixelSize(scaledSize);
|
||||
```
|
||||
|
||||
## 国际化支持
|
||||
|
||||
### 文本国际化
|
||||
```cpp
|
||||
// 在代码中使用tr()函数
|
||||
button->setText(tr("确定"));
|
||||
label->setText(tr("用户名:"));
|
||||
|
||||
// 在.pro文件中添加
|
||||
TRANSLATIONS += app_zh_CN.ts app_en_US.ts
|
||||
```
|
||||
|
||||
## 调试和测试
|
||||
|
||||
### 样式表调试
|
||||
```cpp
|
||||
// 运行时修改样式表进行调试
|
||||
#ifdef QT_DEBUG
|
||||
widget->setStyleSheet("border: 1px solid red;"); // 调试边框
|
||||
#endif
|
||||
```
|
||||
|
||||
### 性能监控
|
||||
```cpp
|
||||
// 使用QElapsedTimer监控性能
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
// 执行耗时操作
|
||||
qDebug() << "操作耗时:" << timer.elapsed() << "ms";
|
||||
```
|
@ -0,0 +1,19 @@
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
@!thought://ui-design-thinking
|
||||
@!thought://academic-standards-awareness
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://qt-optimization-workflow
|
||||
@!execution://academic-ui-standards
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
@!knowledge://qt-ui-development
|
||||
@!knowledge://ui-ux-principles
|
||||
@!knowledge://academic-project-standards
|
||||
</knowledge>
|
||||
</role>
|
@ -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