feat: added add and delete featuries

This commit is contained in:
2026-01-19 16:55:03 +03:00
parent c7d921f774
commit 955395345e
4 changed files with 338 additions and 27 deletions

View File

@@ -1,10 +1,14 @@
#include "LicenseModel.h"
// Qt
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QUuid>
#include <algorithm>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
#include <QDebug>
#include <QtSql/QSqlRecord>
// Self
#include <qfile.h>
@@ -14,10 +18,19 @@
const static int COLUMN_COUNT = 9;
namespace {
quintptr licenseItemToInternalId(const LicenseModel::LicenseItem &item)
struct LoadResult
{
return reinterpret_cast<quintptr>(&item);
}
QList<LicenseModel::LicenseItem> data;
LicenseModel::Status status = LicenseModel::Status::Ok;
QString error;
};
struct DeleteResult
{
QList<QString> ids;
LicenseModel::Status status = LicenseModel::Status::Ok;
QString error;
};
}
LicenseModel::LicenseModel(QObject* parent)
@@ -43,11 +56,13 @@ LicenseModel::LicenseModel(QObject* parent)
m_status = Status::Ok;
}
}
if (m_db.open())
m_db.close();
}
LicenseModel::~LicenseModel()
{
m_db.close();
}
int LicenseModel::rowCount(const QModelIndex &parent) const
@@ -166,6 +181,8 @@ bool LicenseModel::checkTables()
}
if (!clientTableExist)
return false;
else
return true;
}
bool LicenseModel::prepareDatabase()
@@ -181,11 +198,11 @@ bool LicenseModel::prepareDatabase()
{
if (item.trimmed().isEmpty())
continue;
QSqlQuery query(item.trimmed() + ";", m_db);
if (!query.exec())
QSqlQuery query(m_db);
if (!query.exec(item.trimmed()))
{
m_status = Status::DbStructError;
qDebug() << item.trimmed() + ";" << query.lastError().text();
qDebug() << item.trimmed() << query.lastError().text();
m_errors.append(query.lastError().text());
return false;
}
@@ -196,28 +213,272 @@ bool LicenseModel::prepareDatabase()
void LicenseModel::addClient(const LicenseItem &item)
{
beginInsertRows({}, m_data.size() - 1, m_data.size() - 1);
m_data.append(item);
endInsertRows();
m_status = Status::Working;
emit statusChanged();
// handler thread finish
auto* watcher = new QFutureWatcher<LicenseItem>(this);
connect(watcher, &QFutureWatcher<LicenseItem>::finished, this, [this, watcher]() {
const int row = m_data.size();
beginInsertRows({}, row, row);
m_data.append(watcher->result());
endInsertRows();
m_status = Status::Ok;
emit statusChanged();
watcher->deleteLater();
});
// another thread lambda
watcher->setFuture(QtConcurrent::run([item]() {
const QString connectionName = QString("license_add_%1").arg(QUuid::createUuid().toString(QUuid::Id128));
LicenseItem output;
bool ok = false;
{
auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
db.setDatabaseName(DB_PATH);
if (!db.open())
qDebug() << "db not open";
else
{
QSqlQuery queryInsert(db);
queryInsert.prepare(QString(
"INSERT INTO clients(lastName, firstName, patronymic, phone, email, city, yourCompany, hardwareHash, comment) "
"VALUES (:lastName, :firstName, :patronymic, :phone, :email, :city, :yourCompany, :hardwareHash, :comment);"
));
queryInsert.bindValue(":lastName", item.lastName);
queryInsert.bindValue(":firstName", item.firstName);
queryInsert.bindValue(":patronymic", item.patronymic);
queryInsert.bindValue(":phone", item.phone);
queryInsert.bindValue(":email", item.email);
queryInsert.bindValue(":city", item.city);
queryInsert.bindValue(":yourCompany", item.yourCompany);
queryInsert.bindValue(":hardwareHash", item.hardwareHash);
queryInsert.bindValue(":comment", item.comment);
if (!queryInsert.exec())
qDebug() << "queryInsert error" << queryInsert.lastError();
else
{
QString itemId = QString::number(queryInsert.lastInsertId().toLongLong());
QSqlQuery querySelect(db);
querySelect.prepare(QString("SELECT * FROM clients WHERE id=:id;"));
querySelect.bindValue(":id", itemId);
if (!querySelect.exec())
qDebug() << "querySelect exec error" << querySelect.lastError();
else if (!querySelect.next())
qDebug() << "querySelect no rows for id" << itemId;
else
{
output.id = itemId;
output.lastName = querySelect.value("lastName").toString();
output.firstName = querySelect.value("firstName").toString();
output.patronymic = querySelect.value("patronymic").toString();
output.phone = querySelect.value("phone").toString();
output.email = querySelect.value("email").toString();
output.city = querySelect.value("city").toString();
output.yourCompany = querySelect.value("yourCompany").toString();
output.hardwareHash = querySelect.value("hardwareHash").toString();
output.comment = querySelect.value("comment").toString();
ok = true;
}
}
}
}
QSqlDatabase::removeDatabase(connectionName);
if (!ok)
return LicenseItem();
return output;
}));
}
void LicenseModel::deleteClient(int index)
void LicenseModel::deleteClient(const QList<int> &rows)
{
beginRemoveRows({}, index, index);
m_data.removeAt(index);
endRemoveRows();
m_status = Status::Working;
emit statusChanged();
QList<QString> ids;
ids.reserve(rows.size());
for (const auto row : rows)
{
if (row < 0 || row >= m_data.size())
continue;
const QString id = m_data[row].id;
if (!id.isEmpty())
ids.append(id);
}
auto* watcher = new QFutureWatcher<DeleteResult>(this);
connect(watcher, &QFutureWatcher<DeleteResult>::finished, this, [this, watcher]() {
const DeleteResult result = watcher->result();
if (result.status != Status::Ok)
{
m_status = result.status;
if (!result.error.isEmpty())
m_errors.append(result.error);
emit statusChanged();
watcher->deleteLater();
return;
}
QList<int> indices;
indices.reserve(result.ids.size());
for (const auto &id : result.ids)
{
for (int i = 0; i < m_data.size(); ++i)
{
if (m_data[i].id == id)
{
indices.append(i);
break;
}
}
}
std::sort(indices.begin(), indices.end(), std::greater<int>());
for (const auto row : indices)
{
if (row < 0 || row >= m_data.size())
continue;
beginRemoveRows({}, row, row);
m_data.removeAt(row);
endRemoveRows();
}
m_status = Status::Ok;
emit statusChanged();
watcher->deleteLater();
});
watcher->setFuture(QtConcurrent::run([ids]() {
DeleteResult result;
result.ids = ids;
if (ids.isEmpty())
return result;
const QString connectionName = QString("license_delete_%1")
.arg(QUuid::createUuid().toString(QUuid::Id128));
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
db.setDatabaseName(DB_PATH);
if (!db.open())
{
result.status = LicenseModel::Status::DbExistError;
result.error = "Database connection failed: " + db.lastError().text();
}
else
{
QStringList placeholders;
placeholders.reserve(ids.size());
for (int i = 0; i < ids.size(); ++i)
placeholders << "?";
QSqlQuery query(db);
query.prepare(QString("DELETE FROM clients WHERE id IN (%1);")
.arg(placeholders.join(',')));
for (const auto &id : ids)
query.addBindValue(id);
if (!query.exec())
{
result.status = LicenseModel::Status::DbStructError;
result.error = query.lastError().text();
}
}
}
QSqlDatabase::removeDatabase(connectionName);
return result;
}));
}
void LicenseModel::editClient(const LicenseItem &item, int index)
{
m_status = Status::Working;
emit statusChanged();
m_data[index] = item;
dataChanged(this->index(index, 0), this->index(index, COLUMN_COUNT));
dataChanged(this->index(index, 0), this->index(index, COLUMN_COUNT - 1));
}
void LicenseModel::updateModel()
{
beginResetModel();
endResetModel();
m_status = Status::Working;
emit statusChanged();
auto* watcher = new QFutureWatcher<LoadResult>(this);
connect(watcher, &QFutureWatcher<LoadResult>::finished, this, [this, watcher]() {
const LoadResult result = watcher->result();
if (result.status != Status::Ok)
{
m_status = result.status;
if (!result.error.isEmpty())
m_errors.append(result.error);
emit statusChanged();
watcher->deleteLater();
return;
}
beginResetModel();
m_data = result.data;
endResetModel();
m_status = Status::Ok;
emit statusChanged();
watcher->deleteLater();
});
watcher->setFuture(QtConcurrent::run([]() {
LoadResult result;
const QString connectionName = QString("license_load_%1")
.arg(QUuid::createUuid().toString(QUuid::Id128));
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
db.setDatabaseName(DB_PATH);
if (!db.open())
{
result.status = LicenseModel::Status::DbExistError;
result.error = "Database connection failed: " + db.lastError().text();
}
else
{
QSqlQuery query(db);
if (!query.exec(
"SELECT id, lastName, firstName, patronymic, phone, email, city, yourCompany, hardwareHash, comment "
"FROM clients ORDER BY id DESC;"
))
{
result.status = LicenseModel::Status::DbStructError;
result.error = query.lastError().text();
}
else
{
const int createdAtUtcIndex = query.record().indexOf("createdAtUtc");
while (query.next())
{
LicenseItem item;
item.id = query.value("id").toString();
item.lastName = query.value("lastName").toString();
item.firstName = query.value("firstName").toString();
item.patronymic = query.value("patronymic").toString();
item.phone = query.value("phone").toString();
item.email = query.value("email").toString();
item.city = query.value("city").toString();
item.yourCompany = query.value("yourCompany").toString();
item.hardwareHash = query.value("hardwareHash").toString();
item.comment = query.value("comment").toString();
if (createdAtUtcIndex >= 0)
item.createdAtUtc = query.value(createdAtUtcIndex).toString();
result.data.append(item);
}
}
}
}
QSqlDatabase::removeDatabase(connectionName);
return result;
}));
}
LicenseModel::LicenseItem LicenseModel::getItem(int index) const