feat: rebuild CSP practice workflow, UX and automation
这个提交包含在:
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class ContestController : public drogon::HttpController<ContestController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(ContestController::list, "/api/v1/contests", drogon::Get);
|
||||
ADD_METHOD_TO(ContestController::getById, "/api/v1/contests/{1}", drogon::Get);
|
||||
ADD_METHOD_TO(ContestController::registerForContest, "/api/v1/contests/{1}/register", drogon::Post);
|
||||
ADD_METHOD_TO(ContestController::leaderboard, "/api/v1/contests/{1}/leaderboard", drogon::Get);
|
||||
METHOD_LIST_END
|
||||
|
||||
void list(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void getById(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t contest_id);
|
||||
|
||||
void registerForContest(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t contest_id);
|
||||
|
||||
void leaderboard(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t contest_id);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class ImportController : public drogon::HttpController<ImportController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(ImportController::latestJob, "/api/v1/import/jobs/latest", drogon::Get);
|
||||
ADD_METHOD_TO(ImportController::jobById, "/api/v1/import/jobs/{1}", drogon::Get);
|
||||
ADD_METHOD_TO(ImportController::jobItems, "/api/v1/import/jobs/{1}/items", drogon::Get);
|
||||
ADD_METHOD_TO(ImportController::runJob, "/api/v1/import/jobs/run", drogon::Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void latestJob(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void jobById(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t job_id);
|
||||
|
||||
void jobItems(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t job_id);
|
||||
|
||||
void runJob(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class KbController : public drogon::HttpController<KbController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(KbController::listArticles, "/api/v1/kb/articles", drogon::Get);
|
||||
ADD_METHOD_TO(KbController::getArticle, "/api/v1/kb/articles/{1}", drogon::Get);
|
||||
METHOD_LIST_END
|
||||
|
||||
void listArticles(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void getArticle(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
std::string slug);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class LeaderboardController : public drogon::HttpController<LeaderboardController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(LeaderboardController::global, "/api/v1/leaderboard/global", drogon::Get);
|
||||
METHOD_LIST_END
|
||||
|
||||
void global(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class MeController : public drogon::HttpController<MeController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(MeController::profile, "/api/v1/me", drogon::Get);
|
||||
ADD_METHOD_TO(MeController::listWrongBook, "/api/v1/me/wrong-book", drogon::Get);
|
||||
ADD_METHOD_TO(MeController::upsertWrongBookNote, "/api/v1/me/wrong-book/{1}", drogon::Patch);
|
||||
ADD_METHOD_TO(MeController::deleteWrongBookItem, "/api/v1/me/wrong-book/{1}", drogon::Delete);
|
||||
METHOD_LIST_END
|
||||
|
||||
void profile(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void listWrongBook(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void upsertWrongBookNote(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
|
||||
void deleteWrongBookItem(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class MetaController : public drogon::HttpController<MetaController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(MetaController::openapi, "/api/openapi.json", drogon::Get);
|
||||
ADD_METHOD_TO(MetaController::mcp, "/api/v1/mcp", drogon::Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void openapi(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void mcp(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class ProblemController : public drogon::HttpController<ProblemController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(ProblemController::list, "/api/v1/problems", drogon::Get);
|
||||
ADD_METHOD_TO(ProblemController::getById, "/api/v1/problems/{1}", drogon::Get);
|
||||
ADD_METHOD_TO(ProblemController::getDraft, "/api/v1/problems/{1}/draft", drogon::Get);
|
||||
ADD_METHOD_TO(ProblemController::saveDraft, "/api/v1/problems/{1}/draft", drogon::Put);
|
||||
ADD_METHOD_TO(ProblemController::listSolutions, "/api/v1/problems/{1}/solutions", drogon::Get);
|
||||
ADD_METHOD_TO(ProblemController::generateSolutions, "/api/v1/problems/{1}/solutions/generate", drogon::Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void list(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void getById(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
|
||||
void getDraft(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
|
||||
void saveDraft(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
|
||||
void listSolutions(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
|
||||
void generateSolutions(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class ProblemGenController : public drogon::HttpController<ProblemGenController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(ProblemGenController::status, "/api/v1/problem-gen/status", drogon::Get);
|
||||
ADD_METHOD_TO(ProblemGenController::run, "/api/v1/problem-gen/run", drogon::Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void status(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void run(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
class SubmissionController : public drogon::HttpController<SubmissionController> {
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(SubmissionController::submitProblem, "/api/v1/problems/{1}/submit", drogon::Post);
|
||||
ADD_METHOD_TO(SubmissionController::listSubmissions, "/api/v1/submissions", drogon::Get);
|
||||
ADD_METHOD_TO(SubmissionController::getSubmission, "/api/v1/submissions/{1}", drogon::Get);
|
||||
ADD_METHOD_TO(SubmissionController::runCpp, "/api/v1/run/cpp", drogon::Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void submitProblem(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id);
|
||||
|
||||
void listSubmissions(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
|
||||
void getSubmission(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t submission_id);
|
||||
|
||||
void runCpp(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb);
|
||||
};
|
||||
|
||||
} // namespace csp::controllers
|
||||
@@ -33,5 +33,6 @@ class SqliteDb {
|
||||
|
||||
// Apply SQL migrations in order. For now we ship a single init migration.
|
||||
void ApplyMigrations(SqliteDb& db);
|
||||
void SeedDemoData(SqliteDb& db);
|
||||
|
||||
} // namespace csp::db
|
||||
|
||||
@@ -33,6 +33,10 @@ struct Problem {
|
||||
std::string statement_md;
|
||||
int32_t difficulty = 1;
|
||||
std::string source;
|
||||
std::string statement_url;
|
||||
std::string llm_profile_json;
|
||||
std::string sample_input;
|
||||
std::string sample_output;
|
||||
int64_t created_at = 0;
|
||||
};
|
||||
|
||||
@@ -64,12 +68,15 @@ struct Submission {
|
||||
int64_t id = 0;
|
||||
int64_t user_id = 0;
|
||||
int64_t problem_id = 0;
|
||||
std::optional<int64_t> contest_id;
|
||||
Language language = Language::Cpp;
|
||||
std::string code;
|
||||
SubmissionStatus status = SubmissionStatus::Pending;
|
||||
int32_t score = 0;
|
||||
int32_t time_ms = 0;
|
||||
int32_t memory_kb = 0;
|
||||
std::string compile_log;
|
||||
std::string runtime_log;
|
||||
int64_t created_at = 0;
|
||||
};
|
||||
|
||||
@@ -81,4 +88,51 @@ struct WrongBookItem {
|
||||
int64_t updated_at = 0;
|
||||
};
|
||||
|
||||
struct Contest {
|
||||
int64_t id = 0;
|
||||
std::string title;
|
||||
int64_t starts_at = 0;
|
||||
int64_t ends_at = 0;
|
||||
std::string rule_json;
|
||||
};
|
||||
|
||||
struct ContestProblem {
|
||||
int64_t contest_id = 0;
|
||||
int64_t problem_id = 0;
|
||||
int32_t idx = 0;
|
||||
};
|
||||
|
||||
struct ContestRegistration {
|
||||
int64_t contest_id = 0;
|
||||
int64_t user_id = 0;
|
||||
int64_t registered_at = 0;
|
||||
};
|
||||
|
||||
struct KbArticle {
|
||||
int64_t id = 0;
|
||||
std::string slug;
|
||||
std::string title;
|
||||
std::string content_md;
|
||||
int64_t created_at = 0;
|
||||
};
|
||||
|
||||
struct KbArticleLink {
|
||||
int64_t article_id = 0;
|
||||
int64_t problem_id = 0;
|
||||
};
|
||||
|
||||
struct GlobalLeaderboardEntry {
|
||||
int64_t user_id = 0;
|
||||
std::string username;
|
||||
int32_t rating = 0;
|
||||
int64_t created_at = 0;
|
||||
};
|
||||
|
||||
struct ContestLeaderboardEntry {
|
||||
int64_t user_id = 0;
|
||||
std::string username;
|
||||
int32_t solved = 0;
|
||||
int64_t penalty_sec = 0;
|
||||
};
|
||||
|
||||
} // namespace csp::domain
|
||||
|
||||
@@ -14,5 +14,9 @@ Json::Value ToPublicJson(const User& u);
|
||||
Json::Value ToJson(const Problem& p);
|
||||
Json::Value ToJson(const Submission& s);
|
||||
Json::Value ToJson(const WrongBookItem& w);
|
||||
Json::Value ToJson(const Contest& c);
|
||||
Json::Value ToJson(const KbArticle& a);
|
||||
Json::Value ToJson(const GlobalLeaderboardEntry& e);
|
||||
Json::Value ToJson(const ContestLeaderboardEntry& e);
|
||||
|
||||
} // namespace csp::domain
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/domain/entities.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct ContestDetail {
|
||||
domain::Contest contest;
|
||||
std::vector<domain::Problem> problems;
|
||||
};
|
||||
|
||||
class ContestService {
|
||||
public:
|
||||
explicit ContestService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
std::vector<domain::Contest> ListContests();
|
||||
std::optional<domain::Contest> GetContest(int64_t contest_id);
|
||||
std::vector<domain::Problem> ListContestProblems(int64_t contest_id);
|
||||
|
||||
void Register(int64_t contest_id, int64_t user_id);
|
||||
bool IsRegistered(int64_t contest_id, int64_t user_id);
|
||||
bool ContainsProblem(int64_t contest_id, int64_t problem_id);
|
||||
bool IsRunning(int64_t contest_id);
|
||||
|
||||
std::vector<domain::ContestLeaderboardEntry> Leaderboard(int64_t contest_id);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct ImportRunOptions {
|
||||
bool clear_all_problems = false;
|
||||
};
|
||||
|
||||
class ImportRunner {
|
||||
public:
|
||||
static ImportRunner& Instance();
|
||||
|
||||
void Configure(std::string db_path);
|
||||
bool TriggerAsync(const std::string& trigger, const ImportRunOptions& options);
|
||||
void AutoStartIfEnabled();
|
||||
|
||||
bool IsRunning() const;
|
||||
std::string LastCommand() const;
|
||||
std::optional<int> LastExitCode() const;
|
||||
int64_t LastStartedAt() const;
|
||||
int64_t LastFinishedAt() const;
|
||||
|
||||
private:
|
||||
ImportRunner() = default;
|
||||
|
||||
std::string db_path_;
|
||||
mutable std::mutex mu_;
|
||||
bool running_ = false;
|
||||
std::string last_command_;
|
||||
std::optional<int> last_exit_code_;
|
||||
int64_t last_started_at_ = 0;
|
||||
int64_t last_finished_at_ = 0;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct ImportJob {
|
||||
int64_t id = 0;
|
||||
std::string status;
|
||||
std::string trigger;
|
||||
int32_t total_count = 0;
|
||||
int32_t processed_count = 0;
|
||||
int32_t success_count = 0;
|
||||
int32_t failed_count = 0;
|
||||
std::string options_json;
|
||||
std::string last_error;
|
||||
int64_t started_at = 0;
|
||||
std::optional<int64_t> finished_at;
|
||||
int64_t updated_at = 0;
|
||||
int64_t created_at = 0;
|
||||
};
|
||||
|
||||
struct ImportJobItem {
|
||||
int64_t id = 0;
|
||||
int64_t job_id = 0;
|
||||
std::string source_path;
|
||||
std::string status;
|
||||
std::string title;
|
||||
int32_t difficulty = 0;
|
||||
std::optional<int64_t> problem_id;
|
||||
std::string error_text;
|
||||
std::optional<int64_t> started_at;
|
||||
std::optional<int64_t> finished_at;
|
||||
int64_t updated_at = 0;
|
||||
int64_t created_at = 0;
|
||||
};
|
||||
|
||||
struct ImportJobItemQuery {
|
||||
std::string status;
|
||||
int page = 1;
|
||||
int page_size = 50;
|
||||
};
|
||||
|
||||
class ImportService {
|
||||
public:
|
||||
explicit ImportService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
std::optional<ImportJob> GetLatestJob();
|
||||
std::optional<ImportJob> GetById(int64_t job_id);
|
||||
std::vector<ImportJobItem> ListItems(int64_t job_id, const ImportJobItemQuery& query);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/domain/entities.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct KbArticleDetail {
|
||||
domain::KbArticle article;
|
||||
std::vector<std::pair<int64_t, std::string>> related_problems;
|
||||
};
|
||||
|
||||
class KbService {
|
||||
public:
|
||||
explicit KbService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
std::vector<domain::KbArticle> ListArticles();
|
||||
std::optional<KbArticleDetail> GetBySlug(const std::string& slug);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
class ProblemGenRunner {
|
||||
public:
|
||||
static ProblemGenRunner& Instance();
|
||||
|
||||
void Configure(std::string db_path);
|
||||
bool TriggerAsync(const std::string& trigger, int count = 1);
|
||||
void AutoStartIfEnabled();
|
||||
|
||||
bool IsRunning() const;
|
||||
std::string LastCommand() const;
|
||||
std::optional<int> LastExitCode() const;
|
||||
int64_t LastStartedAt() const;
|
||||
int64_t LastFinishedAt() const;
|
||||
|
||||
private:
|
||||
ProblemGenRunner() = default;
|
||||
|
||||
mutable std::mutex mu_;
|
||||
std::string db_path_;
|
||||
bool running_ = false;
|
||||
std::string last_command_;
|
||||
std::optional<int> last_exit_code_;
|
||||
int64_t last_started_at_ = 0;
|
||||
int64_t last_finished_at_ = 0;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/domain/entities.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct ProblemQuery {
|
||||
std::string q;
|
||||
std::string tag;
|
||||
std::vector<std::string> tags;
|
||||
std::string source_prefix;
|
||||
int difficulty = 0;
|
||||
int page = 1;
|
||||
int page_size = 20;
|
||||
std::string order_by = "id";
|
||||
std::string order = "asc";
|
||||
};
|
||||
|
||||
struct ProblemListResult {
|
||||
std::vector<domain::Problem> items;
|
||||
int total_count = 0;
|
||||
};
|
||||
|
||||
class ProblemService {
|
||||
public:
|
||||
explicit ProblemService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
ProblemListResult List(const ProblemQuery& query);
|
||||
std::optional<domain::Problem> GetById(int64_t id);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
class ProblemSolutionRunner {
|
||||
public:
|
||||
static ProblemSolutionRunner& Instance();
|
||||
|
||||
void Configure(std::string db_path);
|
||||
|
||||
bool TriggerAsync(int64_t problem_id, int64_t job_id, int max_solutions);
|
||||
bool IsRunning(int64_t problem_id) const;
|
||||
|
||||
private:
|
||||
ProblemSolutionRunner() = default;
|
||||
|
||||
std::string db_path_;
|
||||
mutable std::mutex mu_;
|
||||
std::set<int64_t> running_problem_ids_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct ProblemDraft {
|
||||
std::string language;
|
||||
std::string code;
|
||||
std::string stdin_text;
|
||||
int64_t updated_at = 0;
|
||||
};
|
||||
|
||||
struct ProblemSolution {
|
||||
int64_t id = 0;
|
||||
int64_t problem_id = 0;
|
||||
int variant = 1;
|
||||
std::string title;
|
||||
std::string idea_md;
|
||||
std::string explanation_md;
|
||||
std::string code_cpp;
|
||||
std::string complexity;
|
||||
std::string tags_json;
|
||||
std::string source;
|
||||
int64_t created_at = 0;
|
||||
int64_t updated_at = 0;
|
||||
};
|
||||
|
||||
struct ProblemSolutionJob {
|
||||
int64_t id = 0;
|
||||
int64_t problem_id = 0;
|
||||
std::string status;
|
||||
int progress = 0;
|
||||
std::string message;
|
||||
int64_t created_by = 0;
|
||||
int max_solutions = 3;
|
||||
int64_t created_at = 0;
|
||||
std::optional<int64_t> started_at;
|
||||
std::optional<int64_t> finished_at;
|
||||
int64_t updated_at = 0;
|
||||
};
|
||||
|
||||
class ProblemWorkspaceService {
|
||||
public:
|
||||
explicit ProblemWorkspaceService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
bool ProblemExists(int64_t problem_id);
|
||||
|
||||
void SaveDraft(int64_t user_id,
|
||||
int64_t problem_id,
|
||||
const std::string& language,
|
||||
const std::string& code,
|
||||
const std::string& stdin_text);
|
||||
std::optional<ProblemDraft> GetDraft(int64_t user_id, int64_t problem_id);
|
||||
|
||||
int64_t CreateSolutionJob(int64_t problem_id, int64_t created_by, int max_solutions);
|
||||
std::optional<ProblemSolutionJob> GetLatestSolutionJob(int64_t problem_id);
|
||||
std::vector<ProblemSolution> ListSolutions(int64_t problem_id);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/domain/entities.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct SubmissionCreateRequest {
|
||||
int64_t user_id = 0;
|
||||
int64_t problem_id = 0;
|
||||
std::optional<int64_t> contest_id;
|
||||
std::string language = "cpp";
|
||||
std::string code;
|
||||
};
|
||||
|
||||
struct RunOnlyResult {
|
||||
domain::SubmissionStatus status = domain::SubmissionStatus::Unknown;
|
||||
int time_ms = 0;
|
||||
std::string stdout_text;
|
||||
std::string stderr_text;
|
||||
std::string compile_log;
|
||||
};
|
||||
|
||||
class SubmissionService {
|
||||
public:
|
||||
explicit SubmissionService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
domain::Submission CreateAndJudge(const SubmissionCreateRequest& req);
|
||||
|
||||
std::vector<domain::Submission> List(std::optional<int64_t> user_id,
|
||||
std::optional<int64_t> problem_id,
|
||||
std::optional<int64_t> contest_id,
|
||||
int page,
|
||||
int page_size);
|
||||
std::optional<domain::Submission> GetById(int64_t id);
|
||||
|
||||
RunOnlyResult RunOnlyCpp(const std::string& code, const std::string& input);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/domain/entities.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
class UserService {
|
||||
public:
|
||||
explicit UserService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
std::optional<domain::User> GetById(int64_t id);
|
||||
std::vector<domain::GlobalLeaderboardEntry> GlobalLeaderboard(int limit = 100);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/domain/entities.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace csp::services {
|
||||
|
||||
struct WrongBookEntry {
|
||||
domain::WrongBookItem item;
|
||||
std::string problem_title;
|
||||
};
|
||||
|
||||
class WrongBookService {
|
||||
public:
|
||||
explicit WrongBookService(db::SqliteDb& db) : db_(db) {}
|
||||
|
||||
std::vector<WrongBookEntry> ListByUser(int64_t user_id);
|
||||
void UpsertNote(int64_t user_id, int64_t problem_id, const std::string& note);
|
||||
void UpsertBySubmission(int64_t user_id,
|
||||
int64_t problem_id,
|
||||
int64_t submission_id,
|
||||
const std::string& note);
|
||||
void Remove(int64_t user_id, int64_t problem_id);
|
||||
|
||||
private:
|
||||
db::SqliteDb& db_;
|
||||
};
|
||||
|
||||
} // namespace csp::services
|
||||
在新工单中引用
屏蔽一个用户