diff --git a/Server/CMakeLists.txt b/Server/CMakeLists.txt index 3ef736c..49f8c73 100644 --- a/Server/CMakeLists.txt +++ b/Server/CMakeLists.txt @@ -1,17 +1,6 @@ find_package(Boost COMPONENTS program_options json REQUIRED) -add_library(Database - Database.h Database.cpp -) - -target_include_directories(Database - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} -) - -target_link_libraries(Database - PUBLIC sqlite3 - PUBLIC Universal -) +add_subdirectory(Database) add_executable(Server main.cpp HttpSession.h HttpSession.cpp diff --git a/Server/Database.cpp b/Server/Database.cpp deleted file mode 100644 index 1c40030..0000000 --- a/Server/Database.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "Database.h" -#include "BoostLog.h" -#include -#include - -bool Database::open(const std::string &path) { - bool ret = true; - int result = sqlite3_open(path.c_str(), &m_sqlite3); - if (result != SQLITE_OK) { - ret = false; - LOG(error) << "open database failed."; - } - initialize(); - return ret; -} - -void Database::addTask(uint64_t createTime, const std::string &content, bool finished) { - std::ostringstream oss; - oss << "INSERT INTO tasks (create_time,content,finished) VALUES (" << createTime << ",\"" << content << "\"," - << finished << ");"; - auto sql = oss.str(); - int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, NULL); - if (result != SQLITE_OK) { - LOG(error) << "add task failed: " << sqlite3_errmsg(m_sqlite3) << ", sql: " << sql; - return; - } -} - -void Database::setTaskFinished(int id, bool finished, uint64_t finishedTime) { - std::ostringstream oss; - oss << "UPDATE tasks SET finished = " << finished << ", finished_time = " << finishedTime << " WHERE id = " << id; - auto sql = oss.str(); - int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, NULL); - if (result != SQLITE_OK) { - LOG(error) << "add task failed: " << sqlite3_errmsg(m_sqlite3) << ", sql: " << sql; - return; - } -} - -void Database::initialize() { - const char *sql = - "CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY AUTOINCREMENT, create_time INTEGER NOT NULL, " - "parent_id INTEGER, content VARCHAR(512) NOT NULL, finished BOLL, finished_time INTEGER);"; - int result = sqlite3_exec(m_sqlite3, sql, NULL, NULL, NULL); - if (result != SQLITE_OK) { - LOG(error) << "Failed to create table: " << sqlite3_errmsg(m_sqlite3); - return; - } -} - -Database::~Database() { - if (m_sqlite3 != nullptr) { - sqlite3_close(m_sqlite3); - m_sqlite3 = nullptr; - } -} diff --git a/Server/Database/CMakeLists.txt b/Server/Database/CMakeLists.txt new file mode 100644 index 0000000..4c74e86 --- /dev/null +++ b/Server/Database/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(Database + Database.h Database.cpp + Task.h Task.cpp +) + +target_include_directories(Database + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(Database + PUBLIC sqlite3 + PUBLIC Universal +) \ No newline at end of file diff --git a/Server/Database/Database.cpp b/Server/Database/Database.cpp new file mode 100644 index 0000000..0c915cc --- /dev/null +++ b/Server/Database/Database.cpp @@ -0,0 +1,107 @@ +#include "Database.h" +#include "BoostLog.h" +#include +#include + +bool Database::open(const std::string &path) { + bool ret = true; + int result = sqlite3_open(path.c_str(), &m_sqlite3); + if (result != SQLITE_OK) { + ret = false; + LOG(error) << "open database failed."; + } + initialize(); + return ret; +} + +static int selectTaskCallback(void *data, int argc, char **argv, char **columnName) { + auto tasks = reinterpret_cast(data); + Task task; + for (int i = 0; i < argc; i++) { + if (argv[i] == nullptr) continue; + if (strcmp(columnName[i], "id") == 0) { + task.id = std::atol(argv[i]); + } else if (strcmp(columnName[i], "create_time") == 0) { + task.createTime = std::atol(argv[i]); + } else if (strcmp(columnName[i], "content") == 0) { + task.content = argv[i]; + } else if (strcmp(columnName[i], "comment") == 0) { + task.comment = argv[i]; + } else if (strcmp(columnName[i], "finished") == 0) { + task.finished = std::atol(argv[i]); + } + } + tasks->push_back(task); + return 0; +} + +Tasks Database::tasks() { + Tasks ret; + char *error = nullptr; + if (sqlite3_exec(m_sqlite3, "select * from tasks", selectTaskCallback, &ret, &error) != SQLITE_OK) { + LOG(error) << "sqlite3_exec() failed: " << error << std::endl; + sqlite3_free(error); + } + return ret; +} + +bool Database::addTask(uint64_t createTime, const std::string &content, const std::string &comment, bool finished) { + bool ret = true; + std::ostringstream oss; + oss << "INSERT INTO tasks (create_time,content,comment,finished) VALUES (" << createTime << ",\"" << content + << "\",\"" << comment << "\"," << finished << ");"; + auto sql = oss.str(); + char *error = nullptr; + int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error); + if (result != SQLITE_OK) { + LOG(error) << "add task failed: " << error << ", sql: " << sql; + sqlite3_free(error); + ret = false; + } + return ret; +} + +bool Database::removeTask(int id) { + bool ret = true; + std::ostringstream oss; + oss << "DELETE FROM tasks WHERE id = " << id << ";"; + auto sql = oss.str(); + char *error = nullptr; + int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error); + if (result != SQLITE_OK) { + LOG(error) << "add task failed: " << error << ", sql: " << sql; + sqlite3_free(error); + ret = false; + } + return ret; +} + +void Database::setTaskFinished(int id, bool finished, uint64_t finishedTime) { + std::ostringstream oss; + oss << "UPDATE tasks SET finished = " << finished << ", finished_time = " << finishedTime << " WHERE id = " << id; + auto sql = oss.str(); + int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, NULL); + if (result != SQLITE_OK) { + LOG(error) << "add task failed: " << sqlite3_errmsg(m_sqlite3) << ", sql: " << sql; + return; + } +} + +void Database::initialize() { + const char *sql = + "CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY AUTOINCREMENT, create_time INTEGER NOT NULL, " + "parent_id INTEGER, content VARCHAR(512) NOT NULL, comment VARCHAR(512) NOT NULL, finished BOLL, finished_time " + "INTEGER);"; + int result = sqlite3_exec(m_sqlite3, sql, NULL, NULL, NULL); + if (result != SQLITE_OK) { + LOG(error) << "Failed to create table: " << sqlite3_errmsg(m_sqlite3); + return; + } +} + +Database::~Database() { + if (m_sqlite3 != nullptr) { + sqlite3_close(m_sqlite3); + m_sqlite3 = nullptr; + } +} diff --git a/Server/Database.h b/Server/Database/Database.h similarity index 66% rename from Server/Database.h rename to Server/Database/Database.h index c141357..3de8a64 100644 --- a/Server/Database.h +++ b/Server/Database/Database.h @@ -2,6 +2,7 @@ #define __DATABASE_H__ #include "Singleton.h" +#include "Task.h" #include struct sqlite3; @@ -12,7 +13,10 @@ class Database { public: ~Database(); bool open(const std::string &path); - void addTask(uint64_t createTime, const std::string &content, bool finished = false); + Tasks tasks(); + bool addTask(uint64_t createTime, const std::string &content, const std::string &comment = "", + bool finished = false); + bool removeTask(int id); void setTaskFinished(int id, bool finished, uint64_t finishedTime); protected: diff --git a/Server/Database/Task.cpp b/Server/Database/Task.cpp new file mode 100644 index 0000000..32223b0 --- /dev/null +++ b/Server/Database/Task.cpp @@ -0,0 +1,22 @@ +#include "Task.h" +#include +#include +#include + +namespace boost { +namespace json { +std::string serialize(const Tasks &tasks) { + boost::json::array ret; + for (auto &task : tasks) { + boost::json::object t; + t["id"] = task.id; + t["finished"] = task.finished; + t["createTime"] = task.createTime; + t["content"] = task.content; + t["comment"] = task.comment; + ret.push_back(std::move(t)); + } + return boost::json::serialize(ret); +} +} // namespace json +} // namespace boost \ No newline at end of file diff --git a/Server/Database/Task.h b/Server/Database/Task.h new file mode 100644 index 0000000..5f21182 --- /dev/null +++ b/Server/Database/Task.h @@ -0,0 +1,23 @@ +#ifndef __TASK_H__ +#define __TASK_H__ + +#include +#include + +class Task { +public: + int id = -1; + bool finished = false; + int32_t createTime = 0; + std::string content; + std::string comment; +}; +using Tasks = std::list; + +namespace boost { +namespace json { +std::string serialize(const Tasks &tasks); +} +} // namespace boost + +#endif // __TASK_H__ \ No newline at end of file diff --git a/Server/SharedState.cpp b/Server/SharedState.cpp index ffdff38..6757614 100644 --- a/Server/SharedState.cpp +++ b/Server/SharedState.cpp @@ -3,6 +3,7 @@ #include "ServiceLogic.h" #include "WeChatContext/CorporationContext.h" #include "WebsocketSession.h" +#include "Database.h" SharedState::SharedState(boost::asio::io_context &ioContext, std::string doc_root) : m_ioContext(ioContext), m_router{std::make_shared>()}, @@ -51,22 +52,44 @@ SharedState::SharedState(boost::asio::io_context &ioContext, std::string doc_roo m_router->insert("/api/v1/tasklist",[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { using namespace boost::beast; + auto database = Amass::Singleton::instance(); + auto tasks = database->tasks(); + http::response s{boost::beast::http::status::ok, request.version()}; s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::content_type, "application/json;charset=UTF-8"); s.keep_alive(request.keep_alive()); - s.body() = "[]"; + s.body() = boost::json::serialize(tasks); s.prepare_payload(); session.reply(std::move(s)); }); - m_router->insert("/api/v1/task/add",[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { + m_router->insert("/api/v1/task/add", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { using namespace boost::beast; - LOG(info)<<"add task: "<::instance(); + auto root = boost::json::parse(request.body()); + bool ret = database->addTask(root.at("createTime").as_int64(), std::string(root.at("content").as_string()), + std::string(root.at("comment").as_string())); boost::json::object reply; - reply["status"] = 0; + reply["status"] = ret ? 0 : -1; + http::response s{boost::beast::http::status::ok, request.version()}; + s.set(http::field::server, BOOST_BEAST_VERSION_STRING); + s.set(http::field::content_type, "application/json;charset=UTF-8"); + s.keep_alive(request.keep_alive()); + s.body() = boost::json::serialize(reply); + s.prepare_payload(); + session.reply(std::move(s)); + }); + m_router->insert("/api/v1/task/delete/{id}", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { + using namespace boost::beast; + LOG(info) << "delete task: " << matches.at("id"); + auto database = Amass::Singleton::instance(); + auto status = database->removeTask(std::stoi(matches.at("id")) ); + + boost::json::object reply; + reply["status"] = status? 0 : -1; http::response s{boost::beast::http::status::ok, request.version()}; s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::content_type, "application/json;charset=UTF-8"); diff --git a/UnitTest/DatabaseTest.cpp b/UnitTest/DatabaseTest.cpp index d19fe6d..f668138 100644 --- a/UnitTest/DatabaseTest.cpp +++ b/UnitTest/DatabaseTest.cpp @@ -11,7 +11,7 @@ BOOST_AUTO_TEST_CASE(DatabaseTest) { database.addTask(1234, "Hello"); - database.addTask(1234, "这是一个测试", true); + database.addTask(1234, "这是一个测试","", true); auto now = duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); database.setTaskFinished(1, true, now);