#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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> query(const QString& queryStr, const QVariantList& args = {}) { QList> 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 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>& 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; } 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> result = query(sql); return !result.isEmpty(); // 如果结果不为空,则表示表存在 } bool _insert(const QString& tableName, const std::map& 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& data, const std::map& 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& 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> _search(const QString& tableName, const QStringList& columns, const std::map& 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> results; while (query.next()) { std::map 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> 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 + "/" + tableName + ".csv"; 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(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() { 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(); } ~CsvWriter() { //stopAutoExport(); } std::mapcsvAndChartLink; void addtData(const QString& snCode, const std::map& data) { std::lock_guard locker(_mutex); QString currentProcessCsvName = getCurrentProcessCsvName(); if (currentProcessCsvName != currentProcessCsvNameCache) if (_db->createTable(currentProcessCsvName, db_InitColumns)) currentProcessCsvNameCache = currentProcessCsvName; auto res = _db->_search(currentProcessCsvName, {}, { {"条码",snCode} }); if (res.empty() == true)//当前数据为空时查询过去的数据是否为空 { auto lastRes = _db->_search(getLastProcessCsvName(), {}, { {"条码",snCode} }); if (lastRes.empty() == true)//过去的数据也为空 则视作现在要插入的数据为全新的数据 { std::map insertData = data; insertData["条码"] = snCode; insertData["创建时间"] = QDateTime::currentDateTime().toString("hh:mm:ss"); insertData["修改时间"] = QDateTime::currentDateTime().toString("hh:mm:ss"); _db->_insert(currentProcessCsvName, insertData); } else//过去的数据不为空 更新到过去的数据上 { std::map updateData = data; updateData["修改时间"] = QDateTime::currentDateTime().toString("hh:mm:ss"); _db->_update(getLastProcessCsvName(), updateData, { {"条码",snCode} }); } } else//当前数据不为空 更新到当前数据上 { std::map updateData = data; updateData["修改时间"] = QDateTime::currentDateTime().toString("hh:mm:ss"); _db->_update(getCurrentProcessCsvName(), updateData, { {"条码",snCode} }); } if (timer.elapsed() > m_intervalMs) { exportCsv(); timer.start(); } } bool isNewData(const QString& snCode) { std::lock_guard locker(_mutex); auto res = _db->_search(getCurrentProcessCsvName(), {}, { {"条码",snCode} }); return res.empty(); } bool isNgData(const QString& snCode) { std::lock_guard 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 isNgDepends; QQueue> > addDataCacheQueue; std::mutex _mutex; SQLiteDB* _db; int m_intervalMs = 10000; QElapsedTimer timer; QString currentProcessCsvNameCache; nlohmann::json m_json; QList> db_newCol = {}; QList> 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 locker(_mutex); if(database_ptr == nullptr) _db = new SQLiteDB("./sqlDatabse/", "database"); 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 startAutoExport(int intervalMs = 10000) { // m_intervalMs = intervalMs; // m_running = true; // this->start(); //} //void stopAutoExport() { // m_running = false; // this->wait(); //} void exportCsv() { //std::lock_guard locker(_mutex); _db->exportTableToCSV(getLastProcessCsvName(), "./csv/", _mutex); _db->exportTableToCSV(getCurrentProcessCsvName(), "./csv/", _mutex); QString currentProcessCsvName = getCurrentProcessCsvName(); if (currentProcessCsvName != currentProcessCsvNameCache) if (!_db->createTable(currentProcessCsvName, db_InitColumns)) { return; } else currentProcessCsvNameCache = currentProcessCsvName; } }; #pragma once