finish basic http frame

This commit is contained in:
zhangyuheng 2023-12-21 17:52:03 +08:00
parent 6e0f2fc1e4
commit 4b6c822bf3
19 changed files with 559 additions and 18 deletions

View File

@ -12,10 +12,13 @@ FIND_PACKAGE(oatpp-postgresql REQUIRED)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src)
FILE(GLOB_RECURSE UTILS_SRC ${CMAKE_SOURCE_DIR}/src/utils/*.cpp)
FILE(GLOB_RECURSE SERVICE_SRC ${CMAKE_SOURCE_DIR}/src/service/*.cpp)
ADD_EXECUTABLE(QuickExamBackend
${CMAKE_SOURCE_DIR}/src/QuickExamBackend.cpp
${UTILS_SRC})
${UTILS_SRC}
${SERVICE_SRC}
)
TARGET_LINK_LIBRARIES(QuickExamBackend
PUBLIC oatpp::oatpp

View File

@ -9,10 +9,11 @@
"database": "quickexam"
},
"resource": "../resource",
"swagger_host": "http://localhost:8280",
"log": {
"save_path": "./logs",
"max_size_mb": 1,
"preserve_days": 30,
"debug": false
"debug": true
}
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <dto/basic/Response.h>
#include <oatpp/web/protocol/http/outgoing/ResponseFactory.hpp>
#include <oatpp/web/server/handler/ErrorHandler.hpp>
#include <utils/Logger.h>
namespace QuickExam {
class ErrorHandler : public oatpp::web::server::handler::ErrorHandler {
private:
typedef oatpp::web::protocol::http::outgoing::Response OutgoingResponse;
typedef oatpp::web::protocol::http::Status Status;
typedef oatpp::web::protocol::http::outgoing::ResponseFactory ResponseFactory;
private:
std::shared_ptr<oatpp::data::mapping::ObjectMapper> m_objectMapper;
public:
explicit ErrorHandler(const std::shared_ptr<oatpp::data::mapping::ObjectMapper> &objectMapper)
: m_objectMapper(objectMapper) {
LOGD("ErrorHandler", "ErrorHandler created");
}
std::shared_ptr<OutgoingResponse> handleError(const Status &status,
const oatpp::String &message,
const Headers &headers) override {
auto error = dto::basic::Response<oatpp::String>::createShared();
error->code = status.code;
error->message = message;
auto response = ResponseFactory::createResponse(Status::CODE_200, error, m_objectMapper);
for (const auto &pair : headers.getAll()) {
response->putHeader(pair.first.toString(), pair.second.toString());
}
return response;
}
};
} // namespace QuickExam

View File

@ -5,6 +5,7 @@
#pragma once
#include <components/ErrorHandler.h>
#include <dto/Configuration.h>
#include <oatpp/core/macro/component.hpp>
#include <oatpp/network/tcp/server/ConnectionProvider.hpp>
@ -42,8 +43,9 @@ public:
router); // get Router component
OATPP_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>,
objectMapper); // get ObjectMapper component
return oatpp::web::server::HttpConnectionHandler::createShared(router);
auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(router);
connectionHandler->setErrorHandler(std::make_shared<ErrorHandler>(objectMapper));
return connectionHandler;
}());
};
} // namespace QuickExam::component

View File

@ -24,7 +24,7 @@ public:
.setVersion("1.0")
.addServer("http://" + config->host + ":" + std::to_string(config->port),
"server on web server")
.addServer("http://localhost:" + std::to_string(config->port), "server on localhost");
.addServer(config->swagger_host, "server on localhost");
return builder.build();
}());

View File

@ -8,10 +8,12 @@
#include <dto/Question.h>
#include <dto/QuestionContent.h>
#include <dto/Tag.h>
#include <dto/response/Question.h>
#include <oatpp/core/macro/codegen.hpp>
#include <oatpp/core/macro/component.hpp>
#include <oatpp/parser/json/mapping/ObjectMapper.hpp>
#include <oatpp/web/server/api/ApiController.hpp>
#include <service/QuestionService.h>
#include OATPP_CODEGEN_BEGIN(ApiController)
@ -33,6 +35,7 @@ public:
private:
std::shared_ptr<ObjectMapper> objectMapper;
service::QuestionService question_service;
public:
ENDPOINT("POST", "/create", createQuestion, BODY_DTO(Object<dto::Question>, req_body)) {
@ -154,9 +157,37 @@ public:
info->addTag("Question");
info->summary = "Delete Content";
info->description = "Delete a content";
info->pathParams.add<Int32>("content_id");
info->pathParams.add<Int32>("content_id").description = "Content id";
info->pathParams.add<Int32>("content_id").required = true;
info->addResponse(Status::CODE_200, "Success, then re-get the question");
}
ENDPOINT("GET", "/get/{question_id}", getQuestionById, PATH(Int32, question_id)) {
return createDtoResponse(Status::CODE_200, question_service.getQuestionById(question_id));
}
ENDPOINT_INFO(getQuestionById) {
info->addTag("Question");
info->summary = "Get Question By Id";
info->description = "Get a question by id";
info->pathParams.add<Int32>("question_id").description = "Question id";
info->pathParams.add<Int32>("question_id").required = true;
info->addResponse<Object<dto::ResponseQuestion>>(Status::CODE_200, "application/json");
}
ENDPOINT("POST",
"/get/list/by/conditions",
getQuestions,
BODY_DTO(Object<dto::QuestionCondition>, req_body)) {
return createDtoResponse(Status::CODE_200,
question_service.getQuestionsByPageConditions(req_body));
}
ENDPOINT_INFO(getQuestions) {
info->addTag("Question");
info->summary = "Get Questions";
info->description = "Get questions by page and size";
info->addConsumes<Object<dto::QuestionCondition>>("application/json");
info->addResponse<Object<dto::ResponseQuestionPage>>(Status::CODE_200, "application/json");
}
};
} // namespace QuickExam::controller

View File

@ -35,7 +35,7 @@ public:
PREPARE(true),
PARAM(oatpp::Object<dto::db::AnswerContents>, a))
QUERY(getAnswerByQuestionId,
QUERY(getAnswersByQuestionId,
"SELECT "
"* "
"FROM qe_answer "

View File

@ -4,17 +4,25 @@
//
#pragma once
#include <dto/Question.h>
#include <dto/db/Question.h>
#include <oatpp-postgresql/orm.hpp>
#include <utils/Logger.h>
#include OATPP_CODEGEN_BEGIN(DbClient)
namespace QuickExam::doo {
class Question : public oatpp::orm::DbClient {
private:
std::shared_ptr<oatpp::orm::Executor> m_executor;
public:
explicit Question(const std::shared_ptr<oatpp::orm::Executor> &executor)
: oatpp::orm::DbClient(executor) {}
: oatpp::orm::DbClient(executor) {
m_executor = executor;
}
QUERY(insertQuestion,
"INSERT INTO qe_question "
@ -52,7 +60,7 @@ public:
"(DEFAULT, :q.tag_id, :q.question_id, :q.priority) "
"RETURNING id",
PREPARE(true),
PARAM(oatpp::Object<dto::db::QuestionTag>, q))
PARAM(oatpp::Object<dto::db::QuestionTags>, q))
QUERY(getQuestion,
"SELECT "
@ -152,6 +160,48 @@ public:
"WHERE id = :q.id",
PREPARE(true),
PARAM(oatpp::Object<dto::db::QuestionContents>, q))
QUERY(getQuestionsByTagId,
"SELECT "
"* "
"FROM qe_question "
"WHERE id IN (SELECT question_id FROM qe_question_tag WHERE tag_id = :tag_id)",
PREPARE(true),
PARAM(oatpp::Int32, tag_id))
DEFINE_CONDITION_FUNC(const oatpp::Object<dto::QuestionCondition> &query) {
std::string sql = "WHERE 1 = 1 ";
if (!query->search->empty()) {
sql += "AND ";
sql += "id IN (SELECT question_id FROM qe_question_content WHERE content LIKE '%" +
query->search + "%') ";
}
if (!query->types->empty()) {
sql += "AND ";
sql += "type IN (";
for (auto &type : *query->types) {
sql += std::to_string(type) + ", ";
}
sql.pop_back();
sql.pop_back();
sql += ") ";
}
if (!query->tag_ids->empty()) {
sql += "AND ";
sql += "id IN (SELECT question_id FROM qe_question_tag WHERE tag_id IN (";
for (auto &tag_id : *query->tag_ids) {
sql += std::to_string(tag_id) + ", ";
}
sql.pop_back();
sql.pop_back();
sql += ")) ";
}
return sql;
}
DEFINE_COUNT_QUERY("qe_question", dto::QuestionCondition)
DEFINE_PAGE_QUERY("qe_question", dto::QuestionCondition)
};
} // namespace QuickExam::doo

View File

@ -60,6 +60,12 @@ public:
"WHERE id = :t.id",
PREPARE(true),
PARAM(oatpp::Object<dto::db::Tag>, t))
QUERY(getAllTagIds,
"SELECT "
"id "
"FROM qe_tag",
PREPARE(true))
};
} // namespace QuickExam::doo

View File

@ -92,6 +92,11 @@ class Configuration : public oatpp::DTO {
info->description = "Resource path";
}
DTO_FIELD(String, swagger_host) = "localhost:8000";
DTO_FIELD_INFO(swagger_host) {
info->description = "Swagger host";
}
DTO_FIELD(Object<Log>, log) = Log::createShared();
DTO_FIELD_INFO(log) {
info->description = "Log configuration";

View File

@ -8,6 +8,7 @@
#include <dto/Answer.h>
#include <dto/QuestionContent.h>
#include <dto/Tag.h>
#include <dto/basic/Page.h>
#include <oatpp/core/Types.hpp>
#include <oatpp/core/macro/codegen.hpp>
@ -18,28 +19,45 @@ namespace QuickExam::dto {
class Question : public db::Question {
DTO_INIT(Question, db::Question)
DTO_FIELD(List<Object<QuestionContent>>,
question_contents) = List<Object<QuestionContent>>::createShared();
DTO_FIELD(List<Object<QuestionContent>>, question_contents);
DTO_FIELD_INFO(question_contents) {
info->description = "Question contents";
}
DTO_FIELD(List<Object<Question>>, sub_questions) = List<Object<Question>>::createShared();
DTO_FIELD(List<Object<Question>>, sub_questions);
DTO_FIELD_INFO(sub_questions) {
info->description = "Sub questions";
}
DTO_FIELD(List<Object<Answer>>, answers) = List<Object<Answer>>::createShared();
DTO_FIELD(List<Object<Answer>>, answers);
DTO_FIELD_INFO(answers) {
info->description = "Question answers";
}
DTO_FIELD(List<Object<Tag>>, tags) = List<Object<Tag>>::createShared();
DTO_FIELD(List<Object<Tag>>, tags);
DTO_FIELD_INFO(tags) {
info->description = "Question tags";
}
};
class QuestionCondition : public basic::Condition {
DTO_INIT(QuestionCondition, basic::Condition)
DTO_FIELD(List<Int32>, tag_ids) = List<Int32>::createShared();
DTO_FIELD_INFO(tag_ids) {
info->description = "Question tags (empty means all tags)";
}
DTO_FIELD(List<Int32>, types) = List<Int32>::createShared();
DTO_FIELD_INFO(types) {
info->description = "Question types (empty means all types)";
}
};
class QuestionPage : public basic::Page<oatpp::Object<Question>> {
DTO_INIT(QuestionPage, basic::Page<oatpp::Object<Question>>)
};
} // namespace QuickExam::dto
#include OATPP_CODEGEN_END(DTO)

View File

@ -5,6 +5,7 @@
#pragma once
#include <dto/basic/Page.h>
#include <dto/db/Tag.h>
#include <oatpp/core/Types.hpp>
#include <oatpp/core/macro/codegen.hpp>
@ -17,6 +18,14 @@ class Tag : public db::Tag {
DTO_INIT(Tag, db::Tag)
};
class TagCondition : public basic::Condition {
DTO_INIT(TagCondition, basic::Condition)
};
class TagPage : public basic::Page<oatpp::Object<Tag>> {
DTO_INIT(TagPage, basic::Page<oatpp::Object<Tag>>)
};
} // namespace QuickExam::dto
#include OATPP_CODEGEN_END(DTO)

View File

@ -29,6 +29,17 @@ ENUM(QuestionType,
VALUE(long_answer, 4, "long-answer"),
VALUE(file_upload, 5, "file-upload"))
static oatpp::List<oatpp::Int32> allQuestionTypes() {
auto list = oatpp::List<oatpp::Int32>::createShared();
list->push_back(0);
list->push_back(1);
list->push_back(2);
list->push_back(3);
list->push_back(4);
list->push_back(5);
return list;
}
ENUM(CandidateStatus,
v_int32,
VALUE(notified, 0, "notified"),

132
src/dto/basic/Page.h Normal file
View File

@ -0,0 +1,132 @@
//
// QuickExam
// Created on 2023/12/21.
//
#pragma once
#include <oatpp/core/Types.hpp>
#include <oatpp/core/macro/codegen.hpp>
#define DEFINE_CONDITION_FUNC static std::string conditionSqlString
#define DEFINE_COUNT_QUERY(TABLE_NAME, CONDITION_DTO) \
std::shared_ptr<oatpp::orm::QueryResult> getPageCountByConditions( \
const oatpp::Object<CONDITION_DTO> &query) { \
std::string sql = "SELECT "; \
sql += "COUNT(*) AS total_count "; \
sql += "FROM " TABLE_NAME " "; \
sql += conditionSqlString(query) + ";"; \
LOGD("QuestionDOO", "getQuestionsByConditionPageCount %s", sql.c_str()); \
return m_executor->execute(sql, {}); \
}
#define DEFINE_PAGE_QUERY(TABLE_NAME, CONDITION_DTO) \
std::shared_ptr<oatpp::orm::QueryResult> getPageByConditions( \
const oatpp::Object<CONDITION_DTO> &query) { \
std::string offset = std::to_string((query->page - 1) * query->page_size); \
std::string limit = std::to_string(query->page_size); \
std::string sql = "SELECT "; \
sql += "* "; \
sql += "FROM " TABLE_NAME " "; \
sql += conditionSqlString(query); \
sql += "ORDER BY " + query->sort_by + " " + query->sort_order + " "; \
sql += "OFFSET " + offset + " "; \
sql += "LIMIT " + limit + ";"; \
LOGD("QuestionDOO", "getQuestionsByConditionInPage %s", sql.c_str()); \
return m_executor->execute(sql, {}); \
}
#define GET_PAGE(DOO, CONDITION_DATA, PAGE_DATA, ITEM_DTO) \
{ \
auto DB_RES = (DOO)->getPageCountByConditions(CONDITION_DATA); \
ASSERT_DB(DB_RES); \
auto totals = \
DB_RES->fetch<oatpp::List<oatpp::Object<dto::basic::Page<Void>>>>()->front(); \
(PAGE_DATA)->total_count = totals->total_count; \
(PAGE_DATA)->total_page = totals->total_count % (CONDITION_DATA)->page_size == 0 ? \
totals->total_count / (CONDITION_DATA)->page_size : \
totals->total_count / (CONDITION_DATA)->page_size + 1; \
if ((CONDITION_DATA)->page > (PAGE_DATA)->total_page) { \
(CONDITION_DATA)->page = (PAGE_DATA)->total_page; \
} \
if ((CONDITION_DATA)->page <= 0) { \
(CONDITION_DATA)->page = 1; \
} \
DB_RES = (DOO)->getPageByConditions(CONDITION_DATA); \
ASSERT_DB(DB_RES); \
(PAGE_DATA)->items = DB_RES->fetch<oatpp::List<oatpp::Object<ITEM_DTO>>>(); \
}
#include OATPP_CODEGEN_BEGIN(DTO)
namespace QuickExam::dto::basic {
template <class L>
class Page : public oatpp::DTO {
DTO_INIT(Page, DTO)
DTO_FIELD(Int32, page) = 1;
DTO_FIELD_INFO(page) {
info->description = "Page number";
info->required = true;
}
DTO_FIELD(Int32, page_size) = 10;
DTO_FIELD_INFO(page_size) {
info->description = "Page size";
info->required = true;
}
DTO_FIELD(Int32, total_count) = 0;
DTO_FIELD_INFO(total_count) {
info->description = "Total number of records";
info->required = true;
}
DTO_FIELD(Int32, total_page) = 0;
DTO_FIELD_INFO(total_page) {
info->description = "Total number of pages";
info->required = true;
}
DTO_FIELD(List<L>, items) = List<L>::createShared();
DTO_FIELD_INFO(items) {
info->description = "Data list";
info->required = true;
}
};
class Condition : public oatpp::DTO {
DTO_INIT(Condition, DTO)
DTO_FIELD(Int32, page) = 1;
DTO_FIELD_INFO(page) {
info->description = "Page number";
info->required = true;
}
DTO_FIELD(Int32, page_size) = 10;
DTO_FIELD_INFO(page_size) {
info->description = "Page size";
info->required = true;
}
DTO_FIELD(String, sort_by) = "id";
DTO_FIELD_INFO(sort_by) {
info->description = "Sort by";
}
DTO_FIELD(String, sort_order) = "ASC";
DTO_FIELD_INFO(sort_order) {
info->description = "Sort order (ASC or DESC)";
}
DTO_FIELD(String, search) = "";
DTO_FIELD_INFO(search) {
info->description = "Search keyword";
}
};
} // namespace QuickExam::dto::basic
#include OATPP_CODEGEN_END(DTO)

68
src/dto/basic/Response.h Normal file
View File

@ -0,0 +1,68 @@
//
// QuickExam
// Created on 2023/12/21.
//
#pragma once
#include <dto/basic/Enmus.h>
#include <oatpp/core/Types.hpp>
#include <oatpp/core/macro/codegen.hpp>
#include <oatpp/web/protocol/http/Http.hpp>
#define ASSERT_DB(DB_RES) \
if (!(DB_RES)->isSuccess()) { \
LOGE("Database Error", "%s", (DB_RES)->getErrorMessage()->c_str()); \
using namespace oatpp::web::protocol::http; \
throw oatpp::web::protocol::http::HttpError(Status::CODE_500, \
(DB_RES)->getErrorMessage()->c_str()); \
}
#define ASSERT_HTTP(COND, MSG) \
if (!(COND)) { \
LOGE("Http Error", "%s", MSG); \
using namespace oatpp::web::protocol::http; \
throw oatpp::web::protocol::http::HttpError(Status::CODE_500, MSG); \
}
#define ASSERT_EXIST(COND, MSG) \
if (!(COND)) { \
LOGE("Not Found", "%s", MSG); \
using namespace oatpp::web::protocol::http; \
throw oatpp::web::protocol::http::HttpError(Status::CODE_404, MSG); \
}
#define RETURN_STATUS_SUCCESS(DTO) \
(DTO)->code = 200; \
(DTO)->message = "success"; \
return DTO;
#include OATPP_CODEGEN_BEGIN(DTO)
namespace QuickExam::dto::basic {
template <class T>
class Response : public oatpp::DTO {
DTO_INIT(Response, DTO)
DTO_FIELD(Int32, code) = 200;
DTO_FIELD_INFO(code) {
info->description = "Response code";
info->required = true;
}
DTO_FIELD(String, message) = "OK";
DTO_FIELD_INFO(message) {
info->description = "Response message";
info->required = true;
}
DTO_FIELD(T, data);
DTO_FIELD_INFO(data) {
info->description = "Response data";
}
};
} // namespace QuickExam::dto::basic
#include OATPP_CODEGEN_END(DTO)

View File

@ -92,8 +92,8 @@ class QuestionSubQuestions : public oatpp::DTO {
}
};
class QuestionTag : public oatpp::DTO {
DTO_INIT(QuestionTag, DTO)
class QuestionTags : public oatpp::DTO {
DTO_INIT(QuestionTags, DTO)
DTO_FIELD(Int32, id);
DTO_FIELD_INFO(id) {

View File

@ -0,0 +1,28 @@
//
// QuickExam
// Created on 2023/12/21.
//
#pragma once
#include <dto/Question.h>
#include <dto/basic/Enmus.h>
#include <dto/basic/Response.h>
#include <oatpp/core/Types.hpp>
#include <oatpp/core/macro/codegen.hpp>
#include OATPP_CODEGEN_BEGIN(DTO)
namespace QuickExam::dto {
class ResponseQuestion : public basic::Response<oatpp::Object<dto::Question>> {
DTO_INIT(ResponseQuestion, basic::Response<oatpp::Object<dto::Question>>)
};
class ResponseQuestionPage : public basic::Response<oatpp::Object<dto::QuestionPage>> {
DTO_INIT(ResponseQuestionPage, basic::Response<oatpp::Object<dto::QuestionPage>>)
};
} // namespace QuickExam::dto
#include OATPP_CODEGEN_END(DTO)

View File

@ -0,0 +1,94 @@
//
// QuickExam
// Created on 2023/12/21.
//
#include "QuestionService.h"
namespace QuickExam::service {
Object<dto::ResponseQuestionPage>
QuestionService::getQuestionsByPageConditions(const Object<dto::QuestionCondition> &conditions) {
auto page_info = dto::QuestionPage::createShared();
GET_PAGE(question_doo, conditions, page_info, dto::Question);
auto page_items = List<Object<dto::Question>>::createShared();
for (auto &question : *page_info->items) {
page_items->push_back(getQuestionDetails(question));
}
page_info->items = page_items;
auto response = dto::ResponseQuestionPage::createShared();
response->data = page_info;
RETURN_STATUS_SUCCESS(response);
}
Object<dto::Question> QuestionService::getQuestionDetails(const Object<dto::Question> &question) {
#define IF_DB_ERROR \
if (!db_res->isSuccess()) { \
LOGE("getQuestionDetails", "getQuestionContentsByQuestionId failed %s", \
db_res->getErrorMessage()->c_str()); \
return question; \
}
// init
question->sub_questions = List<Object<dto::Question>>::createShared();
question->answers = List<Object<dto::Answer>>::createShared();
question->tags = List<Object<dto::Tag>>::createShared();
question->question_contents = List<Object<dto::QuestionContent>>::createShared();
// get contents
auto db_res = question_doo->getQuestionContentsByQuestionId(question->id);
IF_DB_ERROR;
question->question_contents = db_res->fetch<List<Object<dto::QuestionContent>>>();
// get answers
db_res = answer_doo->getAnswersByQuestionId(question->id);
IF_DB_ERROR;
question->answers = db_res->fetch<List<Object<dto::Answer>>>();
for (auto &answer : *question->answers) {
db_res = answer_doo->getAnswerContentsByAnswerId(answer->id);
IF_DB_ERROR;
answer->answer_contents = db_res->fetch<List<Object<dto::AnswerContent>>>();
}
// get tags
db_res = question_doo->getQuestionTagsIdByQuestionId(question->id);
IF_DB_ERROR;
auto tags_id = db_res->fetch<List<Object<dto::db::QuestionTags>>>();
for (auto &tag_id : *tags_id) {
db_res = tag_doo->getTag(tag_id->tag_id);
IF_DB_ERROR;
auto tags = db_res->fetch<List<Object<dto::Tag>>>();
if (tags->size() != 1) {
LOGW("getQuestionDetails", "getTag failed %s", db_res->getErrorMessage()->c_str());
continue;
}
question->tags->push_back(tags->front());
}
// get sub questions
db_res = question_doo->getQuestionSubQuestionsIdByQuestionId(question->id);
IF_DB_ERROR;
auto sub_questions_id = db_res->fetch<List<Object<dto::db::QuestionSubQuestions>>>();
for (auto &sub_question_id : *sub_questions_id) {
db_res = question_doo->getQuestion(sub_question_id->sub_question_id);
IF_DB_ERROR;
auto sub_questions = db_res->fetch<List<Object<dto::Question>>>();
if (sub_questions->size() != 1) {
LOGW("getQuestionDetails", "getQuestion failed %s", db_res->getErrorMessage()->c_str());
continue;
}
auto sub_question = getQuestionDetails(sub_questions->front());
question->sub_questions->push_back(sub_question);
}
return question;
#undef IF_DB_ERROR
}
Object<dto::ResponseQuestion> QuestionService::getQuestionById(const Int32 &id) {
auto db_res = question_doo->getQuestion(id);
ASSERT_DB(db_res);
auto questions = db_res->fetch<List<Object<dto::Question>>>();
std::string msg = "Question id: " + std::to_string(id) + " not found";
ASSERT_EXIST(questions->size() == 1, msg.c_str());
auto data = getQuestionDetails(questions->front());
auto response = dto::ResponseQuestion::createShared();
response->data = data;
RETURN_STATUS_SUCCESS(response);
}
} // namespace QuickExam::service

View File

@ -0,0 +1,45 @@
//
// QuickExam
// Created on 2023/12/21.
//
#pragma once
#include <doo/Answer.h>
#include <doo/Question.h>
#include <doo/Tag.h>
#include <dto/Question.h>
#include <dto/basic/Enmus.h>
#include <dto/response/Question.h>
#include <oatpp/core/macro/component.hpp>
#include <oatpp/parser/json/mapping/ObjectMapper.hpp>
#include <utils/Logger.h>
namespace QuickExam::service {
using namespace oatpp;
class QuestionService {
private:
OATPP_COMPONENT(std::shared_ptr<doo::Question>, question_doo);
OATPP_COMPONENT(std::shared_ptr<doo::Tag>, tag_doo);
OATPP_COMPONENT(std::shared_ptr<doo::Answer>, answer_doo);
OATPP_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, m_objectMapper);
public:
QuestionService() = default;
~QuestionService() = default;
Object<dto::ResponseQuestionPage>
getQuestionsByPageConditions(const Object<dto::QuestionCondition> &page_conditions);
Object<dto::ResponseQuestion> getQuestionById(const Int32 &id);
private:
/**
* @brief Get the Question Details object
* @param question The question object
* @return Object<dto::Question> The question object
*/
Object<dto::Question> getQuestionDetails(const Object<dto::Question> &question);
};
} // namespace QuickExam::service