稳定备份 更新顶盖逻辑

This commit is contained in:
2025-08-31 16:51:18 +08:00
parent aae60af637
commit 22e6be7afb
162 changed files with 4420 additions and 145154 deletions
+138 -128
View File
@@ -27,16 +27,23 @@
#include <quuid.h>
#include <global.h>
#include <qthread.h>
#include <qmutex.h>
class SQLiteDB {
public:
QSqlDatabase db;
// 新增:全局互斥锁,保护所有数据库操作
mutable std::mutex db_mutex;
SQLiteDB(const QString& dbPath)
: dbPath(dbPath){
: dbPath(dbPath) {
connect();
}
~SQLiteDB() { disconnect(); };
// 执行无返回值的SQL语句(如插入、删除、更新)
// 所有公有方法加锁保护
bool execute(const QString& queryStr, const QVariantList& args = {}) {
std::lock_guard<std::mutex> lock(db_mutex);
QSqlQuery query(db);
query.prepare(queryStr);
@@ -51,8 +58,9 @@ public:
}
return true;
}
// 查询数据,返回结果集
QList<QMap<QString, QVariant>> query(const QString& queryStr, const QVariantList& args = {}) {
std::lock_guard<std::mutex> lock(db_mutex);
QList<QMap<QString, QVariant>> results;
QSqlQuery query(db);
query.prepare(queryStr);
@@ -77,12 +85,9 @@ public:
return results;
}
// 检查数据库是否已连接
bool isConnected() const {
return db.isOpen();
}
////////////////BASE//////////////////////
bool createTable(const QString& tableName, const QList<std::pair<QString, QString>>& columns) {
std::lock_guard<std::mutex> lock(db_mutex);
if (columns.empty()) {
qDebug() << "Create table failed: columns are empty.";
return false;
@@ -93,7 +98,6 @@ public:
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(", "));
@@ -101,9 +105,10 @@ public:
bool res = query.exec(sql);
return res;
}
std::vector<QString> getTables() {
std::lock_guard<std::mutex> lock(db_mutex);
std::vector<QString> tables;
// 查询所有表名,排除sqlite系统表
QString sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';";
QSqlQuery query(db);
if (!query.exec(sql)) {
@@ -115,25 +120,25 @@ public:
}
return tables;
}
bool dropTable(const QString& tableName) {
std::lock_guard<std::mutex> lock(db_mutex);
QString sql = QString("DROP TABLE IF EXISTS %1").arg(tableName);
return execute(sql);
}
bool addColumn(const QString& tableName, const QString& columnName, const QString& columnType) {
// 检查列名和类型是否有效
std::lock_guard<std::mutex> lock(db_mutex);
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(db);
query.prepare(sql);
// 执行 SQL 语句
if (!query.exec()) {
qDebug() << "Add column failed:" << query.lastError().text();
return false;
@@ -143,24 +148,22 @@ public:
return true;
}
bool tableExists(const QString& tableName) {
// 使用 sqlite_master 查询表是否存在
std::lock_guard<std::mutex> lock(db_mutex);
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(); // 如果结果不为空,则表示表存在
return !result.isEmpty();
}
bool _insert(const QString& tableName,
const std::map<QString, QVariant>& data) {
// 如果数据为空,返回 false
std::lock_guard<std::mutex> lock(db_mutex);
if (data.empty()) {
qDebug() << "Insert failed: data is empty.";
return false;
}
// 如果表名为空,返回 false
if (tableName.isEmpty()) {
qDebug() << "Insert failed: tableName is empty.";
return false;
@@ -169,13 +172,11 @@ public:
QStringList columns, values;
for (const auto& pair : data) {
columns << pair.first;
// 直接插入数据(明文传输),需要确保数据已经适当转义
values << "'" + pair.second.toString().replace("'", "''") + "'"; // 处理引号
values << "'" + pair.second.toString().replace("'", "''") + "'";
}
// 构造 SQL 查询语句(不使用绑定参数)
QString sql = QString("INSERT INTO \"%1\" (%2) VALUES (%3)")
.arg(tableName) // 使用双引号包围表名,确保兼容特殊字符
.arg(tableName)
.arg(columns.join(", "))
.arg(values.join(", "));
@@ -183,13 +184,11 @@ public:
QSqlQuery query(db);
// 检查数据库连接
if (!db.isOpen()) {
qDebug() << "Database is not open!";
return false;
}
// 执行查询并检查错误
if (!query.exec(sql)) {
qDebug() << "Insert failed:" << query.lastError().text();
return false;
@@ -201,6 +200,7 @@ public:
bool _update(const QString& tableName,
const std::map<QString, QVariant>& data,
const std::map<QString, QVariant>& conditions) {
std::lock_guard<std::mutex> lock(db_mutex);
if (data.empty() || conditions.empty()) {
qDebug() << "Update failed: data or conditions are empty.";
return false;
@@ -208,7 +208,7 @@ public:
QStringList setClauses;
for (const auto& pair : data) {
QString value = pair.second.toString().replace("'", "''"); // 防止单引号注入
QString value = pair.second.toString().replace("'", "''");
setClauses << QString("%1 = '%2'").arg(pair.first).arg(value);
}
@@ -218,7 +218,6 @@ public:
whereClauses << QString("%1 = '%2'").arg(pair.first).arg(conditionValue);
}
// 构造 SQL 查询语句(不使用绑定参数)
QString sql = QString("UPDATE \"%1\" SET %2 WHERE %3")
.arg(tableName)
.arg(setClauses.join(", "))
@@ -228,13 +227,11 @@ public:
QSqlQuery query(db);
// 检查数据库连接
if (!db.isOpen()) {
qDebug() << "Database is not open!";
return false;
}
// 执行查询并检查错误
if (!query.exec(sql)) {
qDebug() << "Update failed:" << query.lastError().text();
return false;
@@ -243,9 +240,9 @@ public:
return true;
}
bool _delete(const QString& tableName,
const std::map<QString, QVariant>& conditions) {
std::lock_guard<std::mutex> lock(db_mutex);
if (conditions.empty()) {
qDebug() << "Delete failed: conditions are empty.";
return false;
@@ -263,7 +260,6 @@ public:
QSqlQuery query(db);
query.prepare(sql);
// 绑定条件参数
for (const auto& pair : conditions) {
query.bindValue(":" + pair.first + "_condition", QVariant::fromValue(pair.second));
}
@@ -279,17 +275,15 @@ public:
std::vector<std::map<QString, QVariant>> _search(const QString& tableName,
const QStringList& columns,
const std::map<QString, QVariant>& conditions = {}) {
// 如果没有指定列名,则选择所有列
std::lock_guard<std::mutex> lock(db_mutex);
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("'", "''"); // 防止单引号注入
QString value = pair.second.toString().replace("'", "''");
whereClauses << QString("%1 = '%2'").arg(pair.first).arg(value);
}
sql += " WHERE " + whereClauses.join(" AND ");
@@ -299,13 +293,11 @@ public:
QSqlQuery query(db);
// 检查数据库连接
if (!db.isOpen()) {
qDebug() << "Database is not open!";
return {};
}
// 执行查询并检查错误
if (!query.exec(sql)) {
qDebug() << "Search failed:" << query.lastError().text();
return {};
@@ -323,17 +315,17 @@ public:
return results;
}
///////////////////EX//FUNC/////////////////
QList<QMap<QString, QVariant>> queryTabelData(const QString& tableName) {
std::lock_guard<std::mutex> lock(db_mutex);
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 相关功能
// 记录开始时间
std::lock_guard<std::mutex> lock(db_mutex);
using namespace std::chrono;
auto start = high_resolution_clock::now();
// 查询表中的所有数据
QString csvFileName = filePath;
QString sql = QString("SELECT * FROM \"%1\"").arg(tableName);
QSqlQuery query(db);
@@ -343,13 +335,11 @@ public:
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;
@@ -358,62 +348,95 @@ public:
QTextStream out(&file);
// 获取列名顺序
QSqlRecord record = query.record();
QStringList columns;
for (int i = 0; i < record.count(); ++i) {
columns << record.fieldName(i); // 保证列名按查询结果顺序提取
columns << record.fieldName(i);
}
// 写入表头(列名)
out << columns.join(",") << "\n"; // 表头以逗号分隔
out << columns.join(",") << "\n";
// 写入数据行
do {
QStringList values;
for (const auto& column : columns) {
values << query.value(column).toString(); // 获取每列的值,保持列名顺序
values << query.value(column).toString();
}
out << values.join(",") << "\n"; // 每行数据以逗号分隔
} while (query.next()); // 循环处理所有数据行
out << values.join(",") << "\n";
} while (query.next());
file.close(); // 关闭文件
// 记录结束时间
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;
}
bool setWALMode(bool enableWAL = true, int busyTimeoutMs = 3000) {
std::lock_guard<std::mutex> lock(db_mutex);
if (!db.isOpen()) {
qWarning() << "Database is not open, cannot set WAL mode.";
return false;
}
QSqlQuery pragma(db);
QString mode = enableWAL ? "WAL" : "DELETE";
if (!pragma.exec(QString("PRAGMA journal_mode=%1;").arg(mode))) {
qWarning() << "Failed to set journal_mode:" << pragma.lastError().text();
return false;
}
qDebug() << "SQLite journal_mode set to" << mode;
if (enableWAL) {
pragma.exec("PRAGMA synchronous=NORMAL;");
}
pragma.exec(QString("PRAGMA busy_timeout=%1;").arg(busyTimeoutMs));
return true;
}
private:
bool connect() {
m_connectionName = QUuid::createUuid().toString();
// 检查数据库文件是否存在
if (!QFile::exists(dbPath)) {
QFileInfo fi(dbPath);
QDir().mkpath(fi.absolutePath());
qDebug() << "Database file does not exist. Creating a new database:" << dbPath;
}
db = QSqlDatabase::addDatabase("QSQLITE", m_connectionName);
db.setDatabaseName(dbPath);
// 打开数据库
if (!db.open()) {
qCritical() << "Failed to open or create database:" << db.lastError().text();
return false;
}
qDebug() << "Connected to SQLite database:" << dbPath;
{
QSqlQuery pragma(db);
if (!pragma.exec("PRAGMA journal_mode=WAL;")) {
qWarning() << "Failed to enable WAL mode:" << pragma.lastError().text();
}
else {
qDebug() << "SQLite WAL mode enabled.";
}
pragma.exec("PRAGMA synchronous=NORMAL;");
pragma.exec("PRAGMA busy_timeout=3000;");
}
createTable("setting",
{
{"id","INTEGER PRIMARY KEY AUTOINCREMENT"} ,
});
return true;
}
// 关闭数据库连接
void disconnect() {
QSqlDatabase::removeDatabase(m_connectionName);
if (db.isOpen()) {
@@ -421,13 +444,14 @@ private:
qDebug() << "Database closed.";
}
}
QString m_connectionName;
QString m_connectionName;
QString dbPath;
};
class CsvWriter : public QThread{
public:
CsvWriter(){
CsvWriter(SQLiteDB* dbIn):db(dbIn){
try{
m_json = readJsonFromPath("./sqldrivers/sqliteCfg.json");
for (auto& data : m_json["数据列表"]) {
@@ -456,8 +480,10 @@ public:
//stopAutoExport();
}
std::map<QString, QString>csvAndChartLink;
SQLiteDB* db;
void addData(QString snCode, std::map<QString, QVariant> data) {
if (snCode.isEmpty()) {
qWarning() << "addtData: snCode is empty.";
return;
@@ -466,16 +492,18 @@ public:
qWarning() << "addtData: data is empty.";
return;
}
QMutexLocker locker(&queueLock);
queue.enqueue(std::make_pair(snCode, data));
this->start();
}
bool isNewData(const QString& snCode) {
std::lock_guard<std::mutex> locker(_mutex);
std::lock_guard<std::mutex> locker(db->db_mutex);
auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码",snCode} });
return res.empty();
}
bool isNgData(const QString& snCode) {
std::lock_guard<std::mutex> locker(_mutex);
std::lock_guard<std::mutex> locker(db->db_mutex);
auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码",snCode} });
if (res.empty() == true)
return false;
@@ -485,63 +513,71 @@ public:
}
return false;
}
void run() override{
while (queue.size() > 0) {
queueLock.lock();
auto val = queue.dequeue();
queueLock.unlock();
try {
auto snCode = val.first;
auto data = val.second;
QString currentProcessCsvName = getCurrentProcessCsvName();
if (currentProcessCsvName != currentProcessCsvNameCache) {
std::lock_guard<std::mutex> locker(db->db_mutex);
if (!db->createTable(currentProcessCsvName, db_InitColumns)) {
qWarning() << "addtData: Failed to create table:" << currentProcessCsvName;
return;
}
currentProcessCsvNameCache = currentProcessCsvName;
}
// 获取当前表的所有列名
QSqlQuery colQuery(db->db);
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 或继续
{
std::lock_guard<std::mutex> locker(db->db_mutex);
QSqlQuery colQuery(db->db);
QString colSql = QString("PRAGMA table_info(\"%1\")").arg(currentProcessCsvName);
if (colQuery.exec(colSql)) {
while (colQuery.next()) {
tableColumns.append(colQuery.value(1).toString());
}
}
}
auto res = db->_search(currentProcessCsvName, {}, { {"条码",snCode} });
QString nowTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); // 精度提升到毫秒
for (const auto& kv : data) {
std::lock_guard<std::mutex> locker(db->db_mutex);
if (!tableColumns.contains(kv.first)) {
if (!db->addColumn(currentProcessCsvName, kv.first, "TEXT")) {
qWarning() << "addtData: Failed to add column:" << kv.first << "to table:" << currentProcessCsvName;
}
}
}
std::vector<std::map<QString, QVariant>> res;
{
std::lock_guard<std::mutex> locker(db->db_mutex);
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} });
std::vector<std::map<QString, QVariant>> lastRes;
{
std::lock_guard<std::mutex> locker(db->db_mutex);
lastRes = db->_search(getLastProcessCsvName(), {}, { {"条码",snCode} });
}
if (lastRes.empty()) {
// 插入新数据
std::map<QString, QVariant> insertData = data;
insertData["条码"] = snCode;
insertData["创建时间"] = nowTime;
insertData["修改时间"] = nowTime;
std::lock_guard<std::mutex> locker(db->db_mutex);
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;
std::lock_guard<std::mutex> locker(db->db_mutex);
opResult = db->_update(getLastProcessCsvName(), updateData, { {"条码",snCode} });
if (!opResult) {
qWarning() << "addtData: Update failed for table:" << getLastProcessCsvName() << "snCode:" << snCode;
@@ -549,19 +585,14 @@ public:
}
}
else {
// 更新当前数据
std::map<QString, QVariant> updateData = data;
updateData["修改时间"] = nowTime;
std::lock_guard<std::mutex> locker(db->db_mutex);
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();
//}
}
catch (const std::exception& ex) {
qCritical() << "addtData: Exception caught:" << ex.what();
@@ -569,29 +600,29 @@ public:
catch (...) {
qCritical() << "addtData: Unknown exception caught.";
}
}
}
private:
QMutex queueLock;
QQueue<std::pair<QString,std::map<QString, QVariant>>> queue;
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"},
{"条码", "TEXT NOT NULL PRIMARY KEY"},
{"创建时间", "TEXT"},
{"修改时间", "TEXT"},
{"CT", "TEXT"},
};
void init(){
std::lock_guard<std::mutex> locker(_mutex);
db = new SQLiteDB(globalDatabasePth);
if (!db->createTable(getLastProcessCsvName(), db_InitColumns)) {
qDebug() << "Failed to create table " << getLastProcessCsvName();
}
@@ -605,28 +636,18 @@ private:
}
timer.start();
}
QString getCurrentProcessCsvName() {
// 获取当前时间
QDateTime currentDateTime = QDateTime::currentDateTime();
// 获取当前小时和分钟
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 + "白班");
}
@@ -634,40 +655,29 @@ private:
return (dateStr + "夜班");
}
}
QString getLastProcessCsvName() {
// 获取当前时间
QDateTime currentDateTime = QDateTime::currentDateTime();
// 获取当前小时和分钟
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 + "夜班"); // 上一班是夜班
return (dateStr + "夜班");
}
else {
return (dateStr + "白班"); // 上一班是白班
return (dateStr + "白班");
}
}
void exportCsv(){
//std::lock_guard<std::mutex> locker(_mutex);
QString name = getLastProcessCsvName();
db->exportTableToCSV(name, "./csv/"+ name +".csv", _mutex);
name = getCurrentProcessCsvName();