feat: expand platform management, admin controls, and learning workflows
这个提交包含在:
@@ -4,11 +4,16 @@
|
||||
#include "csp/domain/enum_strings.h"
|
||||
#include "csp/domain/json.h"
|
||||
#include "csp/services/contest_service.h"
|
||||
#include "csp/services/problem_service.h"
|
||||
#include "csp/services/solution_access_service.h"
|
||||
#include "csp/services/submission_feedback_service.h"
|
||||
#include "csp/services/submission_service.h"
|
||||
#include "http_auth.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
@@ -49,6 +54,32 @@ std::optional<int64_t> ParseOptionalInt64(const std::string& s) {
|
||||
return std::stoll(s);
|
||||
}
|
||||
|
||||
bool ParseBoolLike(const std::string& s, bool default_value) {
|
||||
if (s.empty()) return default_value;
|
||||
std::string v = s;
|
||||
std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) {
|
||||
return static_cast<char>(std::tolower(c));
|
||||
});
|
||||
if (v == "1" || v == "true" || v == "yes" || v == "on") return true;
|
||||
if (v == "0" || v == "false" || v == "no" || v == "off") return false;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
Json::Value ParseLinksArray(const std::string& links_json) {
|
||||
Json::CharReaderBuilder builder;
|
||||
Json::Value parsed;
|
||||
std::string errs;
|
||||
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||
if (!reader->parse(links_json.data(),
|
||||
links_json.data() + links_json.size(),
|
||||
&parsed,
|
||||
&errs) ||
|
||||
!parsed.isArray()) {
|
||||
return Json::Value(Json::arrayValue);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SubmissionController::submitProblem(
|
||||
@@ -153,7 +184,91 @@ void SubmissionController::getSubmission(
|
||||
cb(JsonError(drogon::k404NotFound, "submission not found"));
|
||||
return;
|
||||
}
|
||||
cb(JsonOk(domain::ToJson(*s)));
|
||||
Json::Value payload = domain::ToJson(*s);
|
||||
payload["code"] = s->code;
|
||||
|
||||
services::SolutionAccessService access_svc(csp::AppState::Instance().db());
|
||||
const auto stats =
|
||||
access_svc.QueryUserProblemViewStats(s->user_id, s->problem_id);
|
||||
payload["has_viewed_answer"] = stats.has_viewed;
|
||||
payload["answer_view_count"] = stats.total_views;
|
||||
payload["answer_view_total_cost"] = stats.total_cost;
|
||||
if (stats.last_viewed_at.has_value()) {
|
||||
payload["last_answer_view_at"] = Json::Int64(*stats.last_viewed_at);
|
||||
} else {
|
||||
payload["last_answer_view_at"] = Json::nullValue;
|
||||
}
|
||||
|
||||
services::SubmissionFeedbackService feedback_svc(csp::AppState::Instance().db());
|
||||
if (const auto feedback = feedback_svc.GetBySubmissionId(s->id);
|
||||
feedback.has_value()) {
|
||||
Json::Value analysis;
|
||||
analysis["feedback_md"] = feedback->feedback_md;
|
||||
analysis["links"] = ParseLinksArray(feedback->links_json);
|
||||
analysis["model_name"] = feedback->model_name;
|
||||
analysis["status"] = feedback->status;
|
||||
analysis["created_at"] = Json::Int64(feedback->created_at);
|
||||
analysis["updated_at"] = Json::Int64(feedback->updated_at);
|
||||
payload["analysis"] = analysis;
|
||||
} else {
|
||||
payload["analysis"] = Json::nullValue;
|
||||
}
|
||||
|
||||
cb(JsonOk(payload));
|
||||
} catch (const std::exception& e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void SubmissionController::analyzeSubmission(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t submission_id) {
|
||||
try {
|
||||
std::string auth_error;
|
||||
if (!GetAuthedUserId(req, auth_error).has_value()) {
|
||||
cb(JsonError(drogon::k401Unauthorized, auth_error));
|
||||
return;
|
||||
}
|
||||
|
||||
bool force_refresh = ParseBoolLike(req->getParameter("refresh"), false);
|
||||
if (const auto json = req->getJsonObject();
|
||||
json && (*json).isMember("refresh")) {
|
||||
force_refresh = (*json)["refresh"].asBool();
|
||||
}
|
||||
|
||||
services::SubmissionService submission_svc(csp::AppState::Instance().db());
|
||||
const auto submission = submission_svc.GetById(submission_id);
|
||||
if (!submission.has_value()) {
|
||||
cb(JsonError(drogon::k404NotFound, "submission not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
services::ProblemService problem_svc(csp::AppState::Instance().db());
|
||||
const auto problem = problem_svc.GetById(submission->problem_id);
|
||||
if (!problem.has_value()) {
|
||||
cb(JsonError(drogon::k404NotFound, "problem not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
services::SubmissionFeedbackService feedback_svc(csp::AppState::Instance().db());
|
||||
const auto feedback =
|
||||
feedback_svc.GenerateAndSave(*submission, *problem, force_refresh);
|
||||
|
||||
Json::Value payload;
|
||||
payload["submission_id"] = Json::Int64(feedback.submission_id);
|
||||
payload["feedback_md"] = feedback.feedback_md;
|
||||
payload["links"] = ParseLinksArray(feedback.links_json);
|
||||
payload["model_name"] = feedback.model_name;
|
||||
payload["status"] = feedback.status;
|
||||
payload["created_at"] = Json::Int64(feedback.created_at);
|
||||
payload["updated_at"] = Json::Int64(feedback.updated_at);
|
||||
payload["refresh"] = force_refresh;
|
||||
cb(JsonOk(payload));
|
||||
} catch (const std::invalid_argument&) {
|
||||
cb(JsonError(drogon::k400BadRequest, "invalid request field"));
|
||||
} catch (const std::runtime_error& e) {
|
||||
cb(JsonError(drogon::k400BadRequest, e.what()));
|
||||
} catch (const std::exception& e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户