Files
eUi/include/sql/qSqlBase.h
T

692 lines
24 KiB
C++

#include <qfile.h>
#include <string>
#include <unordered_map>
#include <mutex>
#include <qdatetime.h>
#include <memory>
#include <qtextstream.h>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
#include <QtSql/QSqlRecord>
#include <QVariant>
#include <QDebug>
#include <QString>
#include <QList>
#include <QMap>
#include <qcoreapplication.h>
#include <qdir.h>
#include <qtimer.h>
#include <chrono>
#include <qobject.h>
#include <generalJsonParsing.hpp>
#include <iostream>
#include <fstream>
#include <qelapsedtimer.h>
#include <QQueue>
class SQLiteDB {
public:
SQLiteDB(const QString& dbPath, const QString& dbName)
: m_dbName(dbName), m_dbPath(dbPath) {
connect();
}
~SQLiteDB() { disconnect(); };
// 执行无返回值的SQL语句(如插入、删除、更新)
bool execute(const QString& queryStr, const QVariantList& args = {}) {
QSqlQuery query;
query.prepare(queryStr);
for (int i = 0; i < args.size(); ++i) {
query.bindValue(i, args[i]);
}
if (!query.exec()) {
qCritical() << "SQL error:" << query.lastError().text();
qCritical() << "Query:" << queryStr;
return false;
}
return true;
}
// 查询数据,返回结果集
QList<QMap<QString, QVariant>> query(const QString& queryStr, const QVariantList& args = {}) {
QList<QMap<QString, QVariant>> results;
QSqlQuery query;
query.prepare(queryStr);
for (int i = 0; i < args.size(); ++i) {
query.bindValue(i, args[i]);
}
if (!query.exec()) {
qCritical() << "SQL error:" << query.lastError().text();
qCritical() << "Query:" << queryStr;
return results;
}
while (query.next()) {
QMap<QString, QVariant> row;
for (int i = 0; i < query.record().count(); ++i) {
row.insert(query.record().fieldName(i), query.value(i));
}
results.append(row);
}
return results;
}
// 检查数据库是否已连接
bool isConnected() const {
return m_db.isOpen();
}
////////////////BASE//////////////////////
bool createTable(const QString& tableName, const QList<std::pair<QString, QString>>& columns) {
if (columns.empty()) {
qDebug() << "Create table failed: columns are empty.";
return false;
}
QStringList columnDefinitions;
for (const auto& pair : columns) {
columnDefinitions << QString("%1 %2").arg(pair.first).arg(pair.second);
}
// 确保表名被双引号包围,处理特殊字符的表名
QString sql = QString("CREATE TABLE IF NOT EXISTS \"%1\" (%2)")
.arg(tableName)
.arg(columnDefinitions.join(", "));
QSqlQuery query(m_db);
bool res = query.exec(sql);
return res;
}
std::vector<QString> getTables() {
std::vector<QString> tables;
// 查询所有表名,排除sqlite系统表
QString sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';";
QSqlQuery query(m_db);
if (!query.exec(sql)) {
qDebug() << "getTables failed:" << query.lastError().text();
return tables;
}
while (query.next()) {
tables.push_back(query.value(0).toString());
}
return tables;
}
bool dropTable(const QString& tableName) {
QString sql = QString("DROP TABLE IF EXISTS %1").arg(tableName);
return execute(sql);
}
bool addColumn(const QString& tableName, const QString& columnName, const QString& columnType) {
// 检查列名和类型是否有效
if (columnName.isEmpty() || columnType.isEmpty()) {
qDebug() << "Add column failed: column name or type is empty.";
return false;
}
// 构造 SQL 语句
QString sql = QString("ALTER TABLE \"%1\" ADD COLUMN \"%2\" %3").arg(tableName, columnName, columnType);
QSqlQuery query(m_db);
query.prepare(sql);
// 执行 SQL 语句
if (!query.exec()) {
qDebug() << "Add column failed:" << query.lastError().text();
return false;
}
qDebug() << "Column" << columnName << "added to table" << tableName;
return true;
}
bool tableExists(const QString& tableName) {
// 使用 sqlite_master 查询表是否存在
QString sql = QString("SELECT name FROM sqlite_master WHERE type='table' AND name='%1';").arg(tableName);
QList<QMap<QString, QVariant>> result = query(sql);
return !result.isEmpty(); // 如果结果不为空,则表示表存在
}
bool _insert(const QString& tableName,
const std::map<QString, QVariant>& data) {
// 如果数据为空,返回 false
if (data.empty()) {
qDebug() << "Insert failed: data is empty.";
return false;
}
// 如果表名为空,返回 false
if (tableName.isEmpty()) {
qDebug() << "Insert failed: tableName is empty.";
return false;
}
QStringList columns, values;
for (const auto& pair : data) {
columns << pair.first;
// 直接插入数据(明文传输),需要确保数据已经适当转义
values << "'" + pair.second.toString().replace("'", "''") + "'"; // 处理引号
}
// 构造 SQL 查询语句(不使用绑定参数)
QString sql = QString("INSERT INTO \"%1\" (%2) VALUES (%3)")
.arg(tableName) // 使用双引号包围表名,确保兼容特殊字符
.arg(columns.join(", "))
.arg(values.join(", "));
qDebug() << "Generated SQL: " << sql;
QSqlQuery query(m_db);
// 检查数据库连接
if (!m_db.isOpen()) {
qDebug() << "Database is not open!";
return false;
}
// 执行查询并检查错误
if (!query.exec(sql)) {
qDebug() << "Insert failed:" << query.lastError().text();
return false;
}
return true;
}
bool _update(const QString& tableName,
const std::map<QString, QVariant>& data,
const std::map<QString, QVariant>& conditions) {
if (data.empty() || conditions.empty()) {
qDebug() << "Update failed: data or conditions are empty.";
return false;
}
QStringList setClauses;
for (const auto& pair : data) {
QString value = pair.second.toString().replace("'", "''"); // 防止单引号注入
setClauses << QString("%1 = '%2'").arg(pair.first).arg(value);
}
QStringList whereClauses;
for (const auto& pair : conditions) {
QString conditionValue = pair.second.toString().replace("'", "''");
whereClauses << QString("%1 = '%2'").arg(pair.first).arg(conditionValue);
}
// 构造 SQL 查询语句(不使用绑定参数)
QString sql = QString("UPDATE \"%1\" SET %2 WHERE %3")
.arg(tableName)
.arg(setClauses.join(", "))
.arg(whereClauses.join(" AND "));
qDebug() << "Generated SQL: " << sql;
QSqlQuery query(m_db);
// 检查数据库连接
if (!m_db.isOpen()) {
qDebug() << "Database is not open!";
return false;
}
// 执行查询并检查错误
if (!query.exec(sql)) {
qDebug() << "Update failed:" << query.lastError().text();
return false;
}
return true;
}
bool _delete(const QString& tableName,
const std::map<QString, QVariant>& conditions) {
if (conditions.empty()) {
qDebug() << "Delete failed: conditions are empty.";
return false;
}
QStringList whereClauses;
for (const auto& pair : conditions) {
whereClauses << QString("%1 = :%2").arg(pair.first).arg(pair.first + "_condition");
}
QString sql = QString("DELETE FROM \"%1\" WHERE %2")
.arg(tableName)
.arg(whereClauses.join(" AND "));
QSqlQuery query(m_db);
query.prepare(sql);
// 绑定条件参数
for (const auto& pair : conditions) {
query.bindValue(":" + pair.first + "_condition", QVariant::fromValue(pair.second));
}
if (!query.exec()) {
qDebug() << "Delete failed:" << query.lastError().text();
return false;
}
return true;
}
std::vector<std::map<QString, QVariant>> _search(const QString& tableName,
const QStringList& columns,
const std::map<QString, QVariant>& conditions = {}) {
// 如果没有指定列名,则选择所有列
QStringList selectColumns = columns.isEmpty() ? QStringList("*") : columns;
// 构建基本的 SQL 查询
QString sql = QString("SELECT %1 FROM \"%2\"").arg(selectColumns.join(", "), tableName);
if (!conditions.empty()) {
QStringList whereClauses;
for (const auto& pair : conditions) {
// 直接拼接条件,确保值已经处理好以防 SQL 注入
QString value = pair.second.toString().replace("'", "''"); // 防止单引号注入
whereClauses << QString("%1 = '%2'").arg(pair.first).arg(value);
}
sql += " WHERE " + whereClauses.join(" AND ");
}
qDebug() << "Generated SQL: " << sql;
QSqlQuery query(m_db);
// 检查数据库连接
if (!m_db.isOpen()) {
qDebug() << "Database is not open!";
return {};
}
// 执行查询并检查错误
if (!query.exec(sql)) {
qDebug() << "Search failed:" << query.lastError().text();
return {};
}
std::vector<std::map<QString, QVariant>> results;
while (query.next()) {
std::map<QString, QVariant> row;
for (int i = 0; i < query.record().count(); ++i) {
row[query.record().fieldName(i)] = query.value(i);
}
results.push_back(row);
}
return results;
}
///////////////////EX//FUNC/////////////////
QList<QMap<QString, QVariant>> queryTabelData(const QString& tableName) {
QString sql = QString("SELECT * FROM \"%1\"").arg(tableName);
return query(sql);
}
bool exportTableToCSV(const QString& tableName, const QString& filePath, std::mutex& _mutex) {
using namespace std::chrono; // 便于使用 chrono 相关功能
// 记录开始时间
auto start = high_resolution_clock::now();
// 查询表中的所有数据
QString csvFileName = filePath;
QString sql = QString("SELECT * FROM \"%1\"").arg(tableName);
QSqlQuery query(m_db);
if (!query.exec(sql)) {
qCritical() << "Query execution failed: " << query.lastError().text();
return false;
}
// 如果查询没有返回数据,返回 false
if (!query.next()) {
qDebug() << "No data found in table" << tableName;
return false;
}
// 打开文件进行写入
QFile file(csvFileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qCritical() << "Failed to open file" << csvFileName;
return false;
}
QTextStream out(&file);
// 获取列名顺序
QSqlRecord record = query.record();
QStringList columns;
for (int i = 0; i < record.count(); ++i) {
columns << record.fieldName(i); // 保证列名按查询结果顺序提取
}
// 写入表头(列名)
out << columns.join(",") << "\n"; // 表头以逗号分隔
// 写入数据行
do {
QStringList values;
for (const auto& column : columns) {
values << query.value(column).toString(); // 获取每列的值,保持列名顺序
}
out << values.join(",") << "\n"; // 每行数据以逗号分隔
} while (query.next()); // 循环处理所有数据行
file.close(); // 关闭文件
// 记录结束时间
auto end = high_resolution_clock::now();
// 计算耗时
auto duration = duration_cast<milliseconds>(end - start);
qDebug() << csvFileName << "EXPORT CSV COST: " << duration.count() << "ms";
return true;
}
private:
bool connect() {
// 检查数据库文件是否存在
if (!QFile::exists(m_dbPath + m_dbName + ".db")) {
QDir().mkpath(m_dbPath);
qDebug() << "Database file does not exist. Creating a new database:" << m_dbName;
}
QCoreApplication::addLibraryPath("./");
// 添加 SQLite 数据库驱动
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName(m_dbPath + m_dbName + ".db");
// 打开数据库
if (!m_db.open()) {
qCritical() << "Failed to open or create database:" << m_db.lastError().text();
return false;
}
qDebug() << "Connected to SQLite database:" << m_dbPath + m_dbName;
createTable("setting",
{
{"id","INTEGER PRIMARY KEY AUTOINCREMENT"} ,
});
return true;
}
// 关闭数据库连接
void disconnect() {
if (m_db.isOpen()) {
m_db.close();
qDebug() << "Database closed.";
}
}
QString m_dbName;
QString m_dbPath;
QSqlDatabase m_db;
};
class CsvWriter
{
public:
CsvWriter(SQLiteDB* database_ptr = nullptr)
{
try
{
m_json = readJsonFromPath("./sqldrivers/sqliteCfg.json");
for (auto& data : m_json["数据列表"]) {
db_InitColumns.append({ QString::fromStdString(data),"TEXT" });
}
for (auto& data : m_json["链接到UI表格数据列表"].items()) {
csvAndChartLink[QString::fromStdString(data.key())] = QString::fromStdString(data.value());
}
if (m_json["新增数据列表"].size() > 0) {
for (auto& data : m_json["新增数据列表"]) {
db_newCol.append({ QString::fromStdString(data),"TEXT" });
}
m_json["新增数据列表"].clear();
saveJsonFromPath("./sqldrivers/sqliteCfg.json", m_json);
}
for (auto& data : m_json["判断NG依据数据列表"]) {
isNgDepends.push_back(QString::fromStdString(data));
}
}
catch (const std::exception&)
{
std::cerr << "readJsonFromPath error" << std::endl;
}
init(database_ptr);
}
~CsvWriter()
{
//stopAutoExport();
}
std::map<QString, QString>csvAndChartLink;
bool addtData(const QString& snCode, const std::map<QString, QVariant>& data) {
try {
if (snCode.isEmpty()) {
qWarning() << "addtData: snCode is empty.";
return false;
}
if (data.empty()) {
qWarning() << "addtData: data is empty.";
return false;
}
std::lock_guard<std::mutex> locker(_mutex);
QString currentProcessCsvName = getCurrentProcessCsvName();
if (currentProcessCsvName != currentProcessCsvNameCache) {
if (!_db->createTable(currentProcessCsvName, db_InitColumns)) {
qWarning() << "addtData: Failed to create table:" << currentProcessCsvName;
return false;
}
currentProcessCsvNameCache = currentProcessCsvName;
}
// 获取当前表的所有列名
QSqlQuery colQuery;
QString colSql = QString("PRAGMA table_info(\"%1\")").arg(currentProcessCsvName);
QList<QString> tableColumns;
if (colQuery.exec(colSql)) {
while (colQuery.next()) {
tableColumns.append(colQuery.value(1).toString());
}
}
// 检查 data 中的 key 是否在表结构中,不在则新增列
for (const auto& kv : data) {
if (!tableColumns.contains(kv.first)) {
// 默认新增为 TEXT 类型
if (!_db->addColumn(currentProcessCsvName, kv.first, "TEXT")) {
qWarning() << "addtData: Failed to add column:" << kv.first << "to table:" << currentProcessCsvName;
// 可选择 return false 或继续
}
}
}
auto res = _db->_search(currentProcessCsvName, {}, { {"条码",snCode} });
QString nowTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); // 精度提升到毫秒
bool opResult = false;
if (res.empty()) {
auto lastRes = _db->_search(getLastProcessCsvName(), {}, { {"条码",snCode} });
if (lastRes.empty()) {
// 插入新数据
std::map<QString, QVariant> insertData = data;
insertData["条码"] = snCode;
insertData["创建时间"] = nowTime;
insertData["修改时间"] = nowTime;
opResult = _db->_insert(currentProcessCsvName, insertData);
if (!opResult) {
qWarning() << "addtData: Insert failed for table:" << currentProcessCsvName << "snCode:" << snCode;
}
} else {
// 更新过去的数据
std::map<QString, QVariant> updateData = data;
updateData["修改时间"] = nowTime;
opResult = _db->_update(getLastProcessCsvName(), updateData, { {"条码",snCode} });
if (!opResult) {
qWarning() << "addtData: Update failed for table:" << getLastProcessCsvName() << "snCode:" << snCode;
}
}
} else {
// 更新当前数据
std::map<QString, QVariant> updateData = data;
updateData["修改时间"] = nowTime;
opResult = _db->_update(getCurrentProcessCsvName(), updateData, { {"条码",snCode} });
if (!opResult) {
qWarning() << "addtData: Update failed for table:" << getCurrentProcessCsvName() << "snCode:" << snCode;
}
}
if (timer.elapsed() > m_intervalMs) {
exportCsv();
timer.start();
}
return opResult;
} catch (const std::exception& ex) {
qCritical() << "addtData: Exception caught:" << ex.what();
return false;
} catch (...) {
qCritical() << "addtData: Unknown exception caught.";
return false;
}
}
bool isNewData(const QString& snCode) {
std::lock_guard<std::mutex> locker(_mutex);
auto res = _db->_search(getCurrentProcessCsvName(), {}, { {"条码",snCode} });
return res.empty();
}
bool isNgData(const QString& snCode) {
std::lock_guard<std::mutex> locker(_mutex);
auto res = _db->_search(getCurrentProcessCsvName(), {}, { {"条码",snCode} });
if (res.empty() == true)
return false;
for (auto& data : isNgDepends) {
if (res[0][data].toString() == "NG")
return true;
}
return false;
}
private:
std::vector<QString> isNgDepends;
QQueue<std::pair< QString, std::map<QString, QVariant>> > addDataCacheQueue;
std::mutex _mutex;
SQLiteDB* _db;
int m_intervalMs = 10000;
QElapsedTimer timer;
QString currentProcessCsvNameCache;
nlohmann::json m_json;
QList<std::pair<QString, QString>> db_newCol = {};
QList<std::pair<QString, QString>> db_InitColumns = {
{"条码", "TEXT NOT NULL PRIMARY KEY"},
{"创建时间", "TEXT"},
{"修改时间", "TEXT"},
{"CT", "TEXT"},
};
//void run()
//{
// init();
// while (m_running == true) {
// QThread::msleep(m_intervalMs);
// while (addDataCacheQueue.empty() == false)
// {
// auto data = addDataCacheQueue.dequeue();
// addtData(data.first, data.second);
// }
// exportCsv();
// }
// delete _db;
//}
void init(SQLiteDB* database_ptr = nullptr){
std::lock_guard<std::mutex> locker(_mutex);
if (database_ptr == nullptr)
_db = new SQLiteDB("./sqlDatabse/", "database");
else
_db = database_ptr;
if (!_db->createTable(getLastProcessCsvName(), db_InitColumns)) {
qDebug() << "Failed to create table " << getLastProcessCsvName();
}
currentProcessCsvNameCache = getCurrentProcessCsvName();
if (!_db->createTable(currentProcessCsvNameCache, db_InitColumns)) {
qDebug() << "Failed to create table " << currentProcessCsvNameCache;
}
for (auto& data : db_newCol) {
_db->addColumn(getLastProcessCsvName(), data.first, data.second);
_db->addColumn(getCurrentProcessCsvName(), data.first, data.second);
}
timer.start();
}
QString getCurrentProcessCsvName() {
// 获取当前时间
QDateTime currentDateTime = QDateTime::currentDateTime();
// 获取当前小时和分钟
int hour = currentDateTime.time().hour();
int minute = currentDateTime.time().minute();
// 判断是否在白班时间范围(8:30到20:30)
bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30));
// 判断是否是前一天的夜班
bool isDayBefore = hour <= 8 || (hour == 8 && minute <= 29);
// 获取当前日期(yyyy-MM-dd格式)
QString dateStr;
if (isDayBefore == true)
dateStr = currentDateTime.addDays(-1).toString("yyyy-MM-dd");
else
dateStr = currentDateTime.toString("yyyy-MM-dd");
// 根据时间段返回班次信息
if (isDayShift) {
return (dateStr + "白班");
}
else {
return (dateStr + "夜班");
}
}
QString getLastProcessCsvName() {
// 获取当前时间
QDateTime currentDateTime = QDateTime::currentDateTime();
// 获取当前小时和分钟
int hour = currentDateTime.time().hour();
int minute = currentDateTime.time().minute();
// 判断是否在白班时间范围(8:30到20:30)
bool isDayShift = (hour == 8 && minute >= 30) && (hour == 20 && minute <= 30);
// 判断是否为上一天
bool isDayBefore = hour == 20 && minute <= 30;
// 获取上一班的日期
QDateTime lastShiftDateTime;
if (isDayBefore) {
// 当前是白班,上一班是前一天的夜班
lastShiftDateTime = currentDateTime.addDays(-1);
}
else
lastShiftDateTime = currentDateTime;
// 获取上一班的日期(yyyy-MM-dd格式)
QString dateStr = lastShiftDateTime.toString("yyyy-MM-dd");
// 根据时间段返回班次信息
if (isDayShift) {
return (dateStr + "夜班"); // 上一班是夜班
}
else {
return (dateStr + "白班"); // 上一班是白班
}
}
void exportCsv(){
//std::lock_guard<std::mutex> locker(_mutex);
QString name = getLastProcessCsvName();
_db->exportTableToCSV(name, "./csv/"+ name +".csv", _mutex);
name = getCurrentProcessCsvName();
_db->exportTableToCSV(name, "./csv/" + name + ".csv", _mutex);
QString currentProcessCsvName = getCurrentProcessCsvName();
if (currentProcessCsvName != currentProcessCsvNameCache)
if (!_db->createTable(currentProcessCsvName, db_InitColumns)) {
return;
}
else
currentProcessCsvNameCache = currentProcessCsvName;
}
};
#pragma once