Moved the implementation of filters, sorting, searching to the DBMS

This commit is contained in:
Илья 2025-01-20 19:44:05 +04:00
parent 0345c38fb6
commit 385805414e
8 changed files with 206 additions and 234 deletions

View File

@ -33,11 +33,6 @@ qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES += \
data/requestDeviceModels.sql \
data/requestDevices.sql \
data/updateDevice.sql
HEADERS += \
apiserver.h \
models/baseentity.h \

View File

@ -1,10 +0,0 @@
SELECT
dm.*,
dt.name AS device_type_name,
m.name AS manufacturer_name
FROM
device_model dm
JOIN
device_type dt ON dm.fk_id_type = dt.id
JOIN
manufacturer m ON dm.fk_id_manuf = m.id;

View File

@ -1,14 +0,0 @@
SELECT
d.*,
l.name AS location_name,
TRIM(CONCAT(e.last_name, ' ', e.first_name, ' ', e.patronymic)) AS employee_full_name,
e.fk_id_department AS department_id,
dep.name AS department_name
FROM
device d
JOIN
location l ON d.fk_id_location = l.id
LEFT JOIN
employee e ON d.fk_id_employee = e.id
LEFT JOIN
department dep ON e.fk_id_department = dep.id;

View File

@ -1,12 +0,0 @@
UPDATE device
SET serial_number = '%1',
purchase_date = '%2',
price = '%3',
warranty_expire_date = '%4',
is_working = '%5',
further_information = '%6',
fk_id_location = '%7',
fk_id_employee = '%8',
fk_id_model = '%9',
is_liked = '%10'
WHERE id = '%11';

View File

@ -7,8 +7,15 @@ DeviceLogic::DeviceLogic(ServiceLoadDB *serviceDB)
QMap<int, Device> DeviceLogic::getAll()
{
_db->loadDeviceModels();
return _db->loadDevices();
QMap<int, Device> devicesMap;
auto devices =_db->loadDevices(-1, EntityType::All, "", FilterParams(), "");
for (const Device &device : devices) {
devicesMap.insert(device.id(), device);
}
return devicesMap;
}
QList<Device> DeviceLogic::getAllByParameters(
@ -18,12 +25,7 @@ QList<Device> DeviceLogic::getAllByParameters(
const QString &sortOrder
)
{
_db->loadDeviceModels();
auto devices = _db->loadDevices();
auto filteredDevices = filterByEntity(devices, entityId, entityType);
filteredDevices = searchDevices(filteredDevices, searchText);
filteredDevices = applyFilters(filteredDevices, filterParams);
return sortDevices(filteredDevices, sortOrder);
return _db->loadDevices(entityId, entityType, searchText, filterParams, sortOrder);
}
bool DeviceLogic::updateDevice(const Device &device)
@ -42,114 +44,3 @@ bool DeviceLogic::updateDevice(const Device &device)
}
return _db->updateDevice(device);
}
QMap<int, Device> DeviceLogic::filterByEntity(const QMap<int, Device> &devices, int entityId, EntityType entityType)
{
QMap<int, Device> result;
if (entityId == -1 || entityType == EntityType::All)
return devices;
for (auto &device : devices) {
switch (entityType) {
case EntityType::DeviceTypes:
if (device.deviceModel().idType() == entityId)
result.insert(device.id(), device);
break;
case EntityType::Locations:
if (device.idLocation() == entityId)
result.insert(device.id(), device);
break;
case EntityType::Departments:
if (device.idDepartment() == entityId)
result.insert(device.id(), device);
break;
case EntityType::Manufacturers:
if (device.deviceModel().idManuf() == entityId)
result.insert(device.id(), device);
break;
case EntityType::DeviceModels:
if (device.deviceModel().id() == entityId)
result.insert(device.id(), device);
break;
default: break;
}
}
return result;
}
QMap<int, Device> DeviceLogic::searchDevices(const QMap<int, Device> &devices, const QString &searchText)
{
if (searchText.isEmpty())
return devices;
QMap<int, Device> result;
for (auto &device : devices) {
if (device.deviceModel().name().contains(searchText, Qt::CaseInsensitive) ||
device.deviceModel().nameType().contains(searchText, Qt::CaseInsensitive) ||
device.serialNumber().contains(searchText, Qt::CaseInsensitive)) {
result.insert(device.id(), device);
}
}
return result;
}
QMap<int, Device> DeviceLogic::applyFilters(const QMap<int, Device> &devices, const FilterParams &filterParams)
{
if (!filterParams.apllyFilters())
return devices;
QMap<int, Device> result;
if (filterParams.disregardState())
{
for (auto &device : devices) {
if (device.price() < filterParams.priceFrom() || device.price() > filterParams.priceTo())
continue;
result.insert(device.id(), device);
}
}
else
{
for (auto &device : devices) {
if (device.isWorking() != filterParams.isWorking())
continue;
if (device.price() < filterParams.priceFrom() || device.price() > filterParams.priceTo())
continue;
result.insert(device.id(), device);
}
}
return result;
}
QList<Device> DeviceLogic::sortDevices(QMap<int, Device> &devices, const QString &sortOrder)
{
QList<Device> deviceList = devices.values();
if (sortOrder == "Сначала новые") {
std::sort(deviceList.begin(), deviceList.end(), [](const Device &a, const Device &b) {
return a.purchaseDate() > b.purchaseDate();
});
}
if (sortOrder == "Сначала старые") {
std::sort(deviceList.begin(), deviceList.end(), [](const Device &a, const Device &b) {
return a.purchaseDate() < b.purchaseDate();
});
}
if (sortOrder == "Сначала дешевые") {
std::sort(deviceList.begin(), deviceList.end(), [](const Device &a, const Device &b) {
return a.price() < b.price();
});
}
if (sortOrder == "Сначала дорогие") {
std::sort(deviceList.begin(), deviceList.end(), [](const Device &a, const Device &b) {
return a.price() > b.price();
});
}
if (sortOrder == "Сначала с лайком") {
std::sort(deviceList.begin(), deviceList.end(), [](const Device &a, const Device &b) {
return a.isLiked() > b.isLiked();
});
}
return deviceList;
}

View File

@ -19,12 +19,6 @@ public:
);
bool updateDevice(const Device &device);
private:
QMap<int, Device> filterByEntity(const QMap<int, Device> &devices, int entityId, EntityType entityType);
QMap<int, Device> searchDevices(const QMap<int, Device> &devices, const QString &searchText);
QMap<int, Device> applyFilters(const QMap<int, Device> &devices, const FilterParams &filterParams);
QList<Device> sortDevices(QMap<int, Device> &devices, const QString &sortOrder);
private:
ServiceLoadDB *_db;
};

View File

@ -114,62 +114,158 @@ QMap<int, DeviceType> ServiceLoadDB::loadDeviceTypes()
}
QMap<int, DeviceModel> ServiceLoadDB::loadDeviceModels()
{
QFile file("../../data/requestDeviceModels.sql");
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "Не получилось открыть файл \"requestDeviceModels.sql\"";
return QMap<int, DeviceModel>();
}
{
QMap<int, DeviceModel> mapDeviceModels;
_dbInput = QString(file.readAll());
_dbInput = "SELECT "
"dm.*, "
"dt.name AS device_type_name, "
"m.name AS manufacturer_name "
"FROM device_model dm "
"JOIN device_type dt ON dm.fk_id_type = dt.id "
"JOIN manufacturer m ON dm.fk_id_manuf = m.id ";
if (!_query.exec(_dbInput)) {
qDebug() << "Ошибка запроса при получении информации о моделях устройств: " << _query.lastError().text();
return QMap<int, DeviceModel>();
return mapDeviceModels;
}
QSqlRecord rec;
while (_query.next()) {
rec = _query.record();
DeviceModel deviceModel;
int id = _query.value(rec.indexOf("id")).toInt();
deviceModel.setId(id);
deviceModel.setName(_query.value(rec.indexOf("name")).toString());
deviceModel.setDescription(getValueOrDefault<QString>(_query, rec, "description", QString("Описание отсутствует")));
deviceModel.setWorkEfficiency(_query.value(rec.indexOf("work_efficiency")).toInt());
deviceModel.setReliability(_query.value(rec.indexOf("reliability")).toInt());
deviceModel.setEnergyEfficiency(_query.value(rec.indexOf("energy_efficiency")).toInt());
deviceModel.setUserFriendliness(_query.value(rec.indexOf("user_friendliness")).toInt());
deviceModel.setDurability(_query.value(rec.indexOf("durability")).toInt());
deviceModel.setAestheticQualities(_query.value(rec.indexOf("aesthetic_qualities")).toInt());
deviceModel.setIdType(_query.value(rec.indexOf("fk_id_type")).toInt());
deviceModel.setNameType(_query.value(rec.indexOf("device_type_name")).toString());
deviceModel.setIdManuf(_query.value(rec.indexOf("fk_id_manuf")).toInt());
deviceModel.setNameManuf(_query.value(rec.indexOf("manufacturer_name")).toString());
deviceModel.setStructureElements(readStructureElements(id));
DeviceModel deviceModel = getModelObject(_query.value(rec.indexOf("id")).toInt(), _query.value(rec.indexOf("name")).toString(),
getValueOrDefault<QString>(_query, rec, "description", QString("Описание отсутствует")), _query.value(rec.indexOf("work_efficiency")).toInt(),
_query.value(rec.indexOf("reliability")).toInt(), _query.value(rec.indexOf("energy_efficiency")).toInt(), _query.value(rec.indexOf("user_friendliness")).toInt(),
_query.value(rec.indexOf("durability")).toInt(), _query.value(rec.indexOf("aesthetic_qualities")).toInt(), _query.value(rec.indexOf("fk_id_type")).toInt(),
_query.value(rec.indexOf("device_type_name")).toString(), _query.value(rec.indexOf("fk_id_manuf")).toInt(), _query.value(rec.indexOf("manufacturer_name")).toString());
_mapDeviceModels.insert(deviceModel.id(), deviceModel);
mapDeviceModels.insert(deviceModel.id(), deviceModel);
}
return _mapDeviceModels;
return mapDeviceModels;
}
QMap<int, Device> ServiceLoadDB::loadDevices()
QList<Device> ServiceLoadDB::loadDevices(int entityId, EntityType entityType,
QString searchText,
const FilterParams &filterParams,
const QString &sortOrder)
{
QMap<int, Device> mapDevices;
QList<Device> devices;
QFile file("../../data/requestDevices.sql");
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "Не получилось открыть файл \"requestDevices.sql\"";
return mapDevices;
QString baseQuery = "SELECT "
"d.*, "
"l.name AS location_name, "
"TRIM(CONCAT(e.last_name, ' ', e.first_name, ' ', e.patronymic)) AS employee_full_name, "
"e.fk_id_department AS department_id, "
"dep.name AS department_name, "
"dm.name AS model_name, "
"dm.description AS model_description, "
"dm.work_efficiency, dm.reliability, dm.energy_efficiency, "
"dm.user_friendliness, dm.durability, dm.aesthetic_qualities, "
"dm.fk_id_type AS device_type_id, "
"dt.name AS device_type_name, "
"dm.fk_id_manuf AS manufacturer_id, "
"m.name AS manufacturer_name "
"FROM device d "
"JOIN device_model dm ON d.fk_id_model = dm.id "
"JOIN manufacturer m ON dm.fk_id_manuf = m.id "
"JOIN device_type dt ON dm.fk_id_type = dt.id "
"JOIN location l ON d.fk_id_location = l.id "
"LEFT JOIN employee e ON d.fk_id_employee = e.id "
"LEFT JOIN department dep ON e.fk_id_department = dep.id ";
QStringList conditions;
QList<QVariant> params;
if (entityId != -1 && entityType != EntityType::All) {
switch (entityType) {
case EntityType::DeviceTypes:
conditions.append("d.fk_id_model IN (SELECT id FROM device_model WHERE fk_id_type = ?)");
params.append(entityId);
break;
case EntityType::Locations:
conditions.append("d.fk_id_location = ?");
params.append(entityId);
break;
case EntityType::Departments:
conditions.append("d.fk_id_employee IN (SELECT id FROM employee WHERE fk_id_department = ?)");
params.append(entityId);
break;
case EntityType::Manufacturers:
conditions.append("d.fk_id_model IN (SELECT id FROM device_model WHERE fk_id_manuf = ?)");
params.append(entityId);
break;
case EntityType::DeviceModels:
conditions.append("d.fk_id_model = ?");
params.append(entityId);
break;
default: break;
}
}
_dbInput = QString(file.readAll());
if (!searchText.isEmpty()) {
conditions.append("(d.serial_number ILIKE ? OR "
"d.fk_id_model IN (SELECT id FROM device_model WHERE name ILIKE ?) OR "
"d.fk_id_model IN ("
"SELECT dm.id "
"FROM device_model dm "
"JOIN device_type dt ON dm.fk_id_type = dt.id "
"WHERE dt.name ILIKE ?))");
params.append("%" + searchText + "%");
params.append("%" + searchText + "%");
params.append("%" + searchText + "%");
}
if (!_query.exec(_dbInput)) {
if (filterParams.apllyFilters()) {
if (filterParams.disregardState()) {
conditions.append("d.price BETWEEN ? AND ?");
params.append(filterParams.priceFrom());
params.append(filterParams.priceTo());
} else {
conditions.append("d.is_working = ? AND d.price BETWEEN ? AND ?");
params.append(filterParams.isWorking());
params.append(filterParams.priceFrom());
params.append(filterParams.priceTo());
}
}
if (!conditions.isEmpty()) {
baseQuery.append("WHERE " + conditions.join(" AND "));
}
if (!sortOrder.isEmpty()) {
if (sortOrder == "Сначала новые") {
baseQuery.append(" ORDER BY d.purchase_date DESC");
}
if (sortOrder == "Сначала старые") {
baseQuery.append(" ORDER BY d.purchase_date ASC");
}
if (sortOrder == "Сначала дешевые") {
baseQuery.append(" ORDER BY d.price ASC");
}
if (sortOrder == "Сначала дорогие") {
baseQuery.append(" ORDER BY d.price DESC");
}
if (sortOrder == "Сначала с лайком") {
baseQuery.append(" ORDER BY d.is_liked DESC");
}
}
_dbInput = baseQuery;
if (!_query.prepare(_dbInput)) {
qDebug() << "Ошибка подготовки запроса: " << _query.lastError().text();
return devices;
}
for (int i = 0; i < params.size(); i++) {
_query.bindValue(i, params[i]);
}
if (!_query.exec()) {
qDebug() << "Ошибка запроса при получении информации о устройствах: " << _query.lastError().text();
return mapDevices;
return devices;
}
QSqlRecord rec;
@ -177,14 +273,8 @@ QMap<int, Device> ServiceLoadDB::loadDevices()
rec = _query.record();
Device device;
int id = _query.value(rec.indexOf("id")).toInt();
int idModel = _query.value(rec.indexOf("fk_id_model")).toInt();
if (!_mapDeviceModels.contains(idModel)) {
qDebug() << "Не загружена модель устройства. Идентификатор устройства: " << id;
return QMap<int, Device>();
}
device.setId(id);
device.setId(_query.value(rec.indexOf("id")).toInt());
device.setSerialNumber(_query.value(rec.indexOf("serial_number")).toString());
device.setPurchaseDate(_query.value(rec.indexOf("purchase_date")).toDateTime());
device.setPrice(_query.value(rec.indexOf("price")).toDouble());
@ -197,38 +287,48 @@ QMap<int, Device> ServiceLoadDB::loadDevices()
device.setNameEmployee(getValueOrDefault<QString>(_query, rec, "employee_full_name", QString("Не назначен")));
device.setIdDepartment(getValueOrDefault<int>(_query, rec, "department_id", 0));
device.setNameDepartment(getValueOrDefault<QString>(_query, rec, "department_name", QString("Не относится к отделу")));
device.setDeviceModel(_mapDeviceModels[idModel]);
device.setIsLiked(_query.value(rec.indexOf("is_liked")).toBool());
device.setDeviceModel(getModelObject(_query.value(rec.indexOf("fk_id_model")).toInt(), _query.value(rec.indexOf("model_name")).toString(),
getValueOrDefault<QString>(_query, rec, "model_description", QString("Описание отсутствует")), _query.value(rec.indexOf("work_efficiency")).toInt(),
_query.value(rec.indexOf("reliability")).toInt(), _query.value(rec.indexOf("energy_efficiency")).toInt(), _query.value(rec.indexOf("user_friendliness")).toInt(),
_query.value(rec.indexOf("durability")).toInt(), _query.value(rec.indexOf("aesthetic_qualities")).toInt(), _query.value(rec.indexOf("device_type_id")).toInt(),
_query.value(rec.indexOf("device_type_name")).toString(), _query.value(rec.indexOf("manufacturer_id")).toInt(), _query.value(rec.indexOf("manufacturer_name")).toString()));
mapDevices.insert(device.id(), device);
devices.append(device);
}
return mapDevices;
return devices;
}
bool ServiceLoadDB::updateDevice(const Device &device)
{
QFile file("../../data/updateDevice.sql");
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "Не удалось открыть файл с запросом для обновления устройства";
return false;
}
_dbInput = "UPDATE device "
"SET serial_number = ?, "
"purchase_date = ?, "
"price = ?, "
"warranty_expire_date = ?, "
"is_working = ?, "
"further_information = ?, "
"fk_id_location = ?, "
"fk_id_employee = ?, "
"fk_id_model = ?, "
"is_liked = ? "
"WHERE id = ?";
QString db_input = QString(file.readAll());
_query.prepare(_dbInput);
_query.bindValue(0, device.serialNumber());
_query.bindValue(1, device.purchaseDate().toString("yyyy-MM-dd HH:mm:ss"));
_query.bindValue(2, device.price());
_query.bindValue(3, device.warrantyExpireDate().toString("yyyy-MM-dd HH:mm:ss"));
_query.bindValue(4, device.isWorking() ? "TRUE" : "FALSE");
_query.bindValue(5, device.furtherInformation());
_query.bindValue(6, device.idLocation());
_query.bindValue(7, device.idEmployee());
_query.bindValue(8, device.deviceModel().id());
_query.bindValue(9, device.isLiked() ? "TRUE" : "FALSE");
_query.bindValue(10, device.id());
db_input = db_input.arg(device.serialNumber())
.arg(device.purchaseDate().toString("yyyy-MM-dd HH:mm:ss"))
.arg(device.price())
.arg(device.warrantyExpireDate().toString("yyyy-MM-dd HH:mm:ss"))
.arg(device.isWorking() ? "TRUE" : "FALSE")
.arg(device.furtherInformation())
.arg(device.idLocation())
.arg(device.idEmployee())
.arg(device.deviceModel().id())
.arg(device.isLiked() ? "TRUE" : "FALSE")
.arg(device.id());
if (!_query.exec(db_input)) {
if (!_query.exec()) {
qDebug() << "Ошибка обновления устройства с id = " << device.id() << ": " << _query.lastError().text();
return false;
}
@ -266,6 +366,27 @@ QList<DeviceStructureElement> ServiceLoadDB::readStructureElements(int modelId)
return elements;
}
DeviceModel ServiceLoadDB::getModelObject(int id, QString name, QString description, int workEfficiency, int reliability, int energyEfficiency, int userFriendlines, int durability,
int aestheticQualities, int idType, QString nameType, int idManuf, QString nameManuf)
{
DeviceModel deviceModel;
deviceModel.setId(id);
deviceModel.setName(name);
deviceModel.setDescription(description);
deviceModel.setWorkEfficiency(workEfficiency);
deviceModel.setReliability(reliability);
deviceModel.setEnergyEfficiency(energyEfficiency);
deviceModel.setUserFriendliness(userFriendlines);
deviceModel.setDurability(durability);
deviceModel.setAestheticQualities(aestheticQualities);
deviceModel.setIdType(idType);
deviceModel.setNameType(nameType);
deviceModel.setIdManuf(idManuf);
deviceModel.setNameManuf(nameManuf);
deviceModel.setStructureElements(readStructureElements(id));
return deviceModel;
}
template<typename T>
T ServiceLoadDB::getValueOrDefault(const QSqlQuery &query, const QSqlRecord &record, const QString &fieldName, const T &defaultValue)
{

View File

@ -14,6 +14,8 @@
#include <QList>
#include <QFile>
#include "models/filterparams.h"
#include "models/enums/entitytype.h"
#include "models/types.h" // IWYU pragma: keep
class ServiceLoadDB : public QObject
@ -28,21 +30,26 @@ public:
QMap<int, Manufacturer> loadManufacturers();
QMap<int, DeviceType> loadDeviceTypes();
QMap<int, DeviceModel> loadDeviceModels();
QMap<int, Device> loadDevices();
QList<Device> loadDevices(int entityId, EntityType entityType,
QString searchText,
const FilterParams &filterParams,
const QString &sortOrder);
bool updateDevice(const Device &device);
private:
QList<DeviceStructureElement> readStructureElements(int modelId);
DeviceModel getModelObject(int id, QString name, QString description, int workEfficiency, int reliability, int energyEfficiency, int userFriendlines, int durability,
int aestheticQualities, int idType, QString nameType, int idManuf, QString nameManuf);
template <typename T>
T getValueOrDefault(const QSqlQuery &query, const QSqlRecord &record, const QString &fieldName, const T &defaultValue);
private:
QSqlDatabase _db;
QString _dbInput;
QSqlQuery _query;
QMap<int, DeviceModel> _mapDeviceModels;
QString _dbInput;
QSqlQuery _query;
};
#endif // SERVICELOADDB_H