1 Commits

16 changed files with 340 additions and 190 deletions

2
.gitmodules vendored
View File

@@ -1,6 +1,6 @@
[submodule "redkbuild"]
path = redkbuild
url = git@gitea.redkit-lab.work:redkit-lab/redkbuild.git
url = ssh://git@gitea.redkit-lab.work:57322/redkit-lab/redkbuild.git
[submodule "redkitty"]
path = redkitty
url = git@gitea.redkit-lab.work:redkit-lab/redkitty.git

View File

@@ -15,7 +15,6 @@ Project {
"src/cpp-opds.qbs",
"src/database/database.qbs",
"src/model/model.qbs",
"src/restapi/restapi.qbs",
]
}

View File

@@ -21,7 +21,6 @@ PSApplication {
Depends { name: "restapi" }
Depends { name: "redkit_gen" }
Depends { name: "rdbase" }
Depends { name: "model" }
cpp.cxxLanguageVersion: "c++20"

View File

@@ -4,7 +4,7 @@
#ifndef AUTHOR_S_H
#define AUTHOR_S_H
#include "model_global.h"
#include "database_global.h"
#include <QString>
@@ -13,7 +13,7 @@
#include <odb/query.hxx>
#pragma db object
class MODEL_EXPORT Author_S
class DATABASE_EXPORT Author_S
{
public:
Author_S() = default;
@@ -45,7 +45,7 @@ private:
QString m_firstName;
QString m_lastName;
quint64 m_age;
quint8 m_age;
};
#pragma db view object(Author_S)

View File

@@ -4,7 +4,7 @@
#ifndef BOOK_S_H
#define BOOK_S_H
#include "model_global.h"
#include "database_global.h"
#include <QSharedPointer>
#include <QString>
@@ -14,7 +14,7 @@
#include "author_s.h"
#pragma db object
class MODEL_EXPORT Book_S
class DATABASE_EXPORT Book_S
{
public:
Book_S() = default;

View File

@@ -13,9 +13,87 @@
#include <memory> // std::unique_ptr
#include <string>
#include <filesystem>
#include <odb/database.hxx>
#include "database_utils.h"
#if defined(DATABASE_MYSQL)
#include <odb/mysql/database.hxx>
#elif defined(DATABASE_SQLITE)
#include <odb/connection.hxx>
#include <odb/schema-catalog.hxx>
#include <odb/sqlite/database.hxx>
#include <odb/transaction.hxx>
#elif defined(DATABASE_PGSQL)
#include <odb/pgsql/database.hxx>
#elif defined(DATABASE_ORACLE)
#include <odb/oracle/database.hxx>
#elif defined(DATABASE_MSSQL)
#include <odb/mssql/database.hxx>
#else
#error unknown database; did you forget to define the DATABASE_* macros?
#endif
#include <filesystem>
#include <random>
#include <rcore/smarttypes.h>
using oDBase = odb::sqlite::database;
using uDBase = U<oDBase>;
struct testData
{
QString first;
QString last;
int age;
};
// Функция для генерации случайной строки (имени или фамилии)
QString generate_random_string(const QVector<QString>& pool)
{
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, pool.size() - 1);
return pool[dis(gen)];
}
// Функция для генерации случайного года
int generate_random_year(int min_year = 1900, int max_year = 2020)
{
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(min_year, max_year);
return dis(gen);
}
QVector<testData> fillDB()
{
QVector<QString> first_names = {
"John", "Jane", "Alex", "Chris", "Robert", "Emily", "James", "Linda", "David", "Sarah",
"Michael", "Elizabeth", "Daniel", "Samantha", "William", "Olivia", "Ethan", "Sophia", "Joshua", "Charlotte",
"Daniel", "Grace", "Benjamin", "Isabella", "Matthew", "Victoria", "Henry", "Abigail", "Samuel", "Megan",
"Lucas", "Lily", "Andrew", "Madison", "Jackson", "Chloe", "Aiden", "Amelia", "Thomas", "Natalie",
"Ryan", "Zoe", "Jack", "Harper", "Elijah", "Ava", "Isaac", "Mia", "Caleb", "Ella"
};
QVector<QString> last_names = {
"Doe", "Smith", "Johnson", "Williams", "Jones", "Brown", "Davis", "Miller", "Wilson", "Moore",
"Taylor", "Anderson", "Thomas", "Jackson", "White", "Harris", "Martin", "Thompson", "Garcia", "Martinez",
"Roberts", "Clark", "Lewis", "Walker", "Young", "Allen", "King", "Wright", "Scott", "Adams",
"Baker", "Gonzalez", "Nelson", "Carter", "Mitchell", "Perez", "Robinson", "Hughes", "Flores", "Cook",
"Rogers", "Gutierrez", "Ramirez", "Diaz", "Perez", "Ross", "Sanders", "Price", "Howard", "Cooper"
};
QVector<testData> vecTest;
for (int i = 0; i < 50; ++i)
{
QString first_name = generate_random_string(first_names);
QString last_name = generate_random_string(last_names);
int birth_year = generate_random_year(1900, 2000);
vecTest.push_back({ first_name, last_name, birth_year });
}
return vecTest;
}
inline uDBase openDB(const std::string path_db)
{

View File

@@ -13,22 +13,37 @@ PSLibrary {
]
consoleApplication: true
Depends { name: "Qt"; submodules: [ "core", "network" ] }
Depends { name: "Qt"; submodules: [ "core", "sql", "network" ] }
Depends { name: "cpp" }
Depends { name: "odb.gen" }
Depends { name: "rdbase" }
Depends { name: "redkit_gen" }
redkit_gen.includeModules: ["JsonSerializer"]
odb.gen.databases: "sqlite"
cpp.cxxLanguageVersion: "c++17"
Group {
name: "cpp"
files: [
"**/*.h",
"**/*.hxx",
"**/*.cpp",
"databasemanager.*",
"*.cpp",
"*.h",
"*.hxx",
]
excludeFiles: odbs.files
}
Group {
id: odbs
name: "odb"
files: [
"author_s.h",
"book_s.h",
]
fileTags: ["hpp", "odbxx"]
}
cpp.dynamicLibraries: [

View File

@@ -1,57 +0,0 @@
#include "database_utils.h"
#include <random>
namespace
{
// Функция для генерации случайной строки (имени или фамилии)
QString generate_random_string(const QVector<QString>& pool)
{
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, pool.size() - 1);
return pool[dis(gen)];
}
// Функция для генерации случайного года
int generate_random_year(int min_year = 1900, int max_year = 2020)
{
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(min_year, max_year);
return dis(gen);
}
} // namespace
QVector<testData> fillAuthorDB()
{
QVector<QString> first_names = {
"John", "Jane", "Alex", "Chris", "Robert", "Emily", "James", "Linda", "David", "Sarah",
"Michael", "Elizabeth", "Daniel", "Samantha", "William", "Olivia", "Ethan", "Sophia", "Joshua", "Charlotte",
"Daniel", "Grace", "Benjamin", "Isabella", "Matthew", "Victoria", "Henry", "Abigail", "Samuel", "Megan",
"Lucas", "Lily", "Andrew", "Madison", "Jackson", "Chloe", "Aiden", "Amelia", "Thomas", "Natalie",
"Ryan", "Zoe", "Jack", "Harper", "Elijah", "Ava", "Isaac", "Mia", "Caleb", "Ella"
};
QVector<QString> last_names = {
"Doe", "Smith", "Johnson", "Williams", "Jones", "Brown", "Davis", "Miller", "Wilson", "Moore",
"Taylor", "Anderson", "Thomas", "Jackson", "White", "Harris", "Martin", "Thompson", "Garcia", "Martinez",
"Roberts", "Clark", "Lewis", "Walker", "Young", "Allen", "King", "Wright", "Scott", "Adams",
"Baker", "Gonzalez", "Nelson", "Carter", "Mitchell", "Perez", "Robinson", "Hughes", "Flores", "Cook",
"Rogers", "Gutierrez", "Ramirez", "Diaz", "Perez", "Ross", "Sanders", "Price", "Howard", "Cooper"
};
QVector<testData> vecTest;
for (int i = 0; i < 50; ++i)
{
QString first_name = generate_random_string(first_names);
QString last_name = generate_random_string(last_names);
int birth_year = generate_random_year(1900, 2000);
vecTest.push_back({ first_name, last_name, birth_year });
}
return vecTest;
}

View File

@@ -1,37 +0,0 @@
#include <QString>
#include <QVector>
#include <odb/database.hxx>
#if defined(DATABASE_MYSQL)
#include <odb/mysql/database.hxx>
#elif defined(DATABASE_SQLITE)
#include <odb/connection.hxx>
#include <odb/schema-catalog.hxx>
#include <odb/sqlite/database.hxx>
#include <odb/transaction.hxx>
#elif defined(DATABASE_PGSQL)
#include <odb/pgsql/database.hxx>
#elif defined(DATABASE_ORACLE)
#include <odb/oracle/database.hxx>
#elif defined(DATABASE_MSSQL)
#include <odb/mssql/database.hxx>
#else
#error unknown database; did you forget to define the DATABASE_* macros?
#endif
#include <rcore/smarttypes.h>
#include "database_global.h"
using oDBase = odb::sqlite::database;
using uDBase = U<oDBase>;
struct testData
{
QString first;
QString last;
int age;
};
QVector<testData> DATABASE_EXPORT fillAuthorDB();

View File

@@ -0,0 +1,118 @@
#include "databasemanager.h"
// Singleton instance
DatabaseManager& DatabaseManager::instance()
{
static DatabaseManager instance;
return instance;
}
// Конструктор: создаем подключение
DatabaseManager::DatabaseManager()
{
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("mydatabase.db");
if (!db.open())
{
qDebug() << "Ошибка открытия БД:" << db.lastError().text();
}
else
{
qDebug() << "База данных успешно открыта";
}
}
// Деструктор: закрываем подключение
DatabaseManager::~DatabaseManager()
{
if (db.isOpen())
{
db.close();
}
}
// Возвращает ссылку на объект БД
QSqlDatabase& DatabaseManager::database()
{
return db;
}
// Выполняет SQL-запрос (без параметров)
bool DatabaseManager::executeQuery(const QString& query)
{
QSqlQuery q;
if (!q.exec(query))
{
qDebug() << "Ошибка выполнения запроса:" << q.lastError().text();
return false;
}
return true;
}
// Выполняет подготовленный SQL-запрос с параметрами
bool DatabaseManager::executePreparedQuery(const QString& query, const QStringList& values)
{
QSqlQuery q;
q.prepare(query);
for (const auto& value : values)
q.addBindValue(value);
if (!q.exec())
{
qDebug() << "Ошибка выполнения подготовленного запроса:" << q.lastError().text();
return false;
}
return true;
}
QList<QVariantList> DatabaseManager::executeSelectQuery(const QString& query, const QStringList& values)
{
QSqlQuery q;
q.prepare(query);
for (const auto& value : values)
q.addBindValue(value);
if (!q.exec())
{
qDebug() << "Ошибка выполнения SELECT запроса:" << q.lastError().text();
return {};
}
QList<QVariantList> results;
while (q.next())
{
QVariantList row;
for (int i = 0; i < q.record().count(); ++i)
row.append(q.value(i));
results.append(row);
}
return results;
}
QList<QVariantList> DatabaseManager::executeSelect(const SelectBuilder& selectBuilder)
{
const auto queryString = selectBuilder.getSelect();
qDebug() << "Подготовленный запрос: " << queryString;
QSqlQuery q;
if (!q.prepare(queryString))
qDebug() << "Ошибка при подготовке запроса:" << q.lastError().text();
if (!q.exec())
qDebug() << "Ошибка при выполнении запроса:" << q.lastError().text();
QList<QVariantList> results;
while (q.next()) // Проходимся по строкам
{
QVariantList row;
for (int i = 0; i < q.record().count(); ++i) // Проходимся по столбцам
row.append(q.value(i));
results.append(row);
}
return results;
}

View File

@@ -0,0 +1,61 @@
#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H
#include "database_global.h"
#include <QDebug>
#include <QtSql>
struct SelectBuilder
{
QString tableName;
QStringList rows = { "*" };
QString where;
QString getSelect() const
{
QString allRows;
auto ri = rows.begin();
while (ri != rows.end())
{
allRows += (*ri++);
if (ri != rows.end())
allRows += ", ";
}
QString queryString = QString("SELECT %1").arg(allRows);
queryString += QString(" FROM %1").arg(tableName);
if (!where.isEmpty())
queryString += QString(" WHERE %1").arg(where);
queryString += ';';
return queryString;
}
};
class DATABASE_EXPORT DatabaseManager
{
public:
static DatabaseManager& instance(); // Singleton
QSqlDatabase& database();
bool executeQuery(const QString& query);
bool executePreparedQuery(const QString& query, const QStringList& values);
QList<QVariantList> executeSelectQuery(const QString& query, const QStringList& values = {});
QList<QVariantList> executeSelect(const SelectBuilder& selectBuilder);
private:
DatabaseManager();
~DatabaseManager();
QSqlDatabase db;
};
#endif // DATABASEMANAGER_H

View File

@@ -8,10 +8,10 @@
#include <restapi/restapiserver.h>
/* Опыты с odb */
#include <model/author_s-odb.hxx> // Должен быть здесь
#include <model/author_s.h>
#include <model/book_s-odb.hxx> // Должен быть здесь
#include <model/book_s.h>
#include <database/author_s-odb.hxx> // Должен быть здесь
#include <database/author_s.h>
#include <database/book_s-odb.hxx> // Должен быть здесь
#include <database/book_s.h>
#include <database/database.hxx> // create_database
#include <odb/database.hxx>
@@ -101,7 +101,7 @@ void fillBooksBD(uDBase& db)
{
odb::core::transaction t(db->begin());
for (auto& author : authors)
db->persist(author);
db->persist(author); // Используем get() для получения сырого указателя
t.commit();
}
@@ -129,23 +129,45 @@ int main(int argc, char* argv[])
uDBase db(openDB(dbPath));
// TODO Как-то нужно выполнять лишь раз
fillBooksBD(db);
// Сохранение дополнителньых авторов в базу данных
{
auto authors = fillAuthorDB();
odb::core::transaction t(db->begin());
for (auto& author : authors)
{
SH<Author_S> tempAuthor = SH<Author_S>::create(author.first, author.last, author.age);
db->persist(tempAuthor);
}
t.commit();
}
// fillBooksBD(db);
RestApiServer server(*db);
server.start(8080);
/* Опыты с odb */
// const std::string dbPath = "test_db.sqlite";
// std::vector<Author_S> authors;
// odb::core::transaction trans(db->begin());
// for (auto data : fillDB())
// {
// auto a = Author_S(data.first, data.last, data.age);
// db->persist(a);
// }
// trans.commit();
// try
// {
// odb::core::transaction tr(db->begin());
// odb::query<Author_S> query;
// odb::result<Author_S> result;
// odb::result<Author_S> r(db->query<Author_S>(odb::query<Author_S>::age >= 2000));
// for (const auto& author : r)
// {
// std::cout << "First: " << author.first() << ", Last: " << author.last() << ", Year: " << author.age() << std::endl;
// }
// tr.commit();
// }
// catch (const odb::exception& e)
// {
// std::cerr << "ODB error: " << e.what() << std::endl;
// return 1;
// }
// Set up code that uses the Qt event loop here.
// Call a.quit() or a.exit() to quit the application.
// A not very useful example would be including

View File

@@ -1,43 +0,0 @@
/*!
\qmltype cpp-opds
\inherits Project
\brief Описание
*/
PSLibrary {
cpp.defines: [
// You can make your code fail to compile if it uses deprecated APIs.
// In order to do so, uncomment the following line.
//"QT_DISABLE_DEPRECATED_BEFORE=0x060000" // disables all the APIs deprecated before Qt 6.0.0
"DATABASE_SQLITE",
"MODEL_LIBRARY"
]
consoleApplication: true
Depends { name: "Qt"; submodules: [ "core", "network" ] }
Depends { name: "cpp" }
Depends { name: "odb.gen" }
Depends { name: "rdbase" }
Depends { name: "redkit_gen" }
redkit_gen.includeModules: ["JsonSerializer"]
odb.gen.databases: "sqlite"
cpp.cxxLanguageVersion: "c++17"
Group {
id: odbs
name: "odb"
files: [
"**/*.h",
]
fileTags: ["hpp", "odbxx", "rgen"]
}
cpp.dynamicLibraries: [
"odb-sqlite",
"odb-qt",
"odb",
"sqlite3"
]
}

View File

@@ -1,12 +0,0 @@
#ifndef MODEL_GLOBAL_H
#define MODEL_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(MODEL_LIBRARY)
#define MODEL_EXPORT Q_DECL_EXPORT
#else
#define MODEL_EXPORT Q_DECL_IMPORT
#endif
#endif // MODEL_GLOBAL_H

View File

@@ -18,7 +18,6 @@ WPSLibrary {
Depends { name: "odb.gen" }
Depends { name: "redkit_gen" }
Depends { name: "rdbase" }
Depends { name: "model" }
cpp.cxxLanguageVersion: "c++20"

View File

@@ -5,10 +5,10 @@
#include <QJsonObject>
#include <QJsonValue>
#include <model/author_s-odb.hxx> // Должен быть здесь
#include <model/author_s.h>
#include <model/book_s-odb.hxx> // Должен быть здесь
#include <model/book_s.h>
#include <database/author_s-odb.hxx> // Должен быть здесь
#include <database/author_s.h>
#include <database/book_s-odb.hxx> // Должен быть здесь
#include <database/book_s.h>
#include <odb/core.hxx>
#include <odb/database.hxx>
@@ -27,8 +27,8 @@ void RestApiServer::start(quint16 port)
}
else
{
qDebug() << "REST API сервер запущен по адресу:";
qDebug() << QString("http://127.0.0.1:%1").arg(port);
QString addrs = "http://127.0.0.1:" + QString::number(this->serverPort());
qDebug() << "REST API сервер запущен по адресу:" << addrs;
}
}
@@ -59,7 +59,7 @@ void RestApiServer::handleRequest()
QByteArray RestApiServer::processRequest(const QString& request)
{
// qWarning() << request << "\n\n";
qWarning() << request << "\n\n";
if (request.startsWith("GET /books/author/"))
{
@@ -70,6 +70,11 @@ QByteArray RestApiServer::processRequest(const QString& request)
QString firstName = nameParts.size() > 0 ? nameParts[0] : "";
QString lastName = nameParts.size() > 1 ? nameParts[1] : "";
qWarning()
<< "author:" << author << "\n"
<< "firstName:" << firstName << "\n"
<< "lastName:" << lastName << "\n";
odb::transaction t(m_db.begin());
auto books = m_db.query<Book_S>(odb::query<Book_S>::author->id == ageReq);
@@ -97,6 +102,7 @@ QByteArray RestApiServer::processRequest(const QString& request)
odb::result<Book_S> res(m_db.query<Book_S>());
QJsonArray jArray;
for (auto it = res.begin(); it != res.end(); ++it)
{
const auto& book = *it;
@@ -109,6 +115,7 @@ QByteArray RestApiServer::processRequest(const QString& request)
jArray.push_back(j);
}
auto jDoc = QJsonDocument(jArray);
t.commit();
return "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" + jDoc.toJson(QJsonDocument::Indented);
@@ -120,6 +127,7 @@ QByteArray RestApiServer::processRequest(const QString& request)
odb::result<Author_S> res(m_db.query<Author_S>());
QJsonArray jArray;
for (auto it = res.begin(); it != res.end(); ++it)
{
const auto& author = *it;
@@ -132,8 +140,8 @@ QByteArray RestApiServer::processRequest(const QString& request)
jArray.push_back(j);
}
auto jDoc = QJsonDocument(jArray);
t.commit();
t.commit();
return "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" + jDoc.toJson(QJsonDocument::Indented);
}