feat: expand platform management, admin controls, and learning workflows
这个提交包含在:
@@ -5,6 +5,7 @@
|
||||
#include "csp/services/problem_service.h"
|
||||
#include "csp/services/problem_solution_runner.h"
|
||||
#include "csp/services/problem_workspace_service.h"
|
||||
#include "csp/services/solution_access_service.h"
|
||||
#include "http_auth.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -192,7 +193,7 @@ void ProblemController::saveDraft(
|
||||
}
|
||||
|
||||
void ProblemController::listSolutions(
|
||||
const drogon::HttpRequestPtr& /*req*/,
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id) {
|
||||
try {
|
||||
@@ -203,28 +204,72 @@ void ProblemController::listSolutions(
|
||||
}
|
||||
|
||||
const auto rows = svc.ListSolutions(problem_id);
|
||||
const bool has_solutions = !rows.empty();
|
||||
const auto latest_job = svc.GetLatestSolutionJob(problem_id);
|
||||
const std::string mode = req->getParameter("mode");
|
||||
const bool need_full = mode == "full";
|
||||
|
||||
Json::Value arr(Json::arrayValue);
|
||||
for (const auto& item : rows) {
|
||||
Json::Value j;
|
||||
j["id"] = Json::Int64(item.id);
|
||||
j["problem_id"] = Json::Int64(item.problem_id);
|
||||
j["variant"] = item.variant;
|
||||
j["title"] = item.title;
|
||||
j["idea_md"] = item.idea_md;
|
||||
j["explanation_md"] = item.explanation_md;
|
||||
j["code_cpp"] = item.code_cpp;
|
||||
j["complexity"] = item.complexity;
|
||||
j["tags_json"] = item.tags_json;
|
||||
j["source"] = item.source;
|
||||
j["created_at"] = Json::Int64(item.created_at);
|
||||
j["updated_at"] = Json::Int64(item.updated_at);
|
||||
arr.append(j);
|
||||
Json::Value access(Json::objectValue);
|
||||
access["required"] = true;
|
||||
access["daily_free_quota"] = 1;
|
||||
access["cost_after_free"] = 2;
|
||||
|
||||
if (need_full && has_solutions) {
|
||||
std::string auth_error;
|
||||
const auto user_id = GetAuthedUserId(req, auth_error);
|
||||
if (!user_id.has_value()) {
|
||||
cb(JsonError(drogon::k401Unauthorized, auth_error));
|
||||
return;
|
||||
}
|
||||
|
||||
services::SolutionAccessService access_svc(csp::AppState::Instance().db());
|
||||
const auto charge = access_svc.ConsumeSolutionView(*user_id, problem_id);
|
||||
if (!charge.granted) {
|
||||
cb(JsonError(drogon::k402PaymentRequired,
|
||||
"rating 不足:首次免费后每次查看答案需 2 分"));
|
||||
return;
|
||||
}
|
||||
|
||||
access["mode"] = "full";
|
||||
access["charged"] = charge.charged;
|
||||
access["daily_free"] = charge.daily_free;
|
||||
access["cost"] = charge.cost;
|
||||
access["day_key"] = charge.day_key;
|
||||
access["daily_used_count"] = charge.daily_used_count;
|
||||
access["rating_before"] = charge.rating_before;
|
||||
access["rating_after"] = charge.rating_after;
|
||||
access["viewed_at"] = Json::Int64(charge.viewed_at);
|
||||
|
||||
for (const auto& item : rows) {
|
||||
Json::Value j;
|
||||
j["id"] = Json::Int64(item.id);
|
||||
j["problem_id"] = Json::Int64(item.problem_id);
|
||||
j["variant"] = item.variant;
|
||||
j["title"] = item.title;
|
||||
j["idea_md"] = item.idea_md;
|
||||
j["explanation_md"] = item.explanation_md;
|
||||
j["code_cpp"] = item.code_cpp;
|
||||
j["complexity"] = item.complexity;
|
||||
j["tags_json"] = item.tags_json;
|
||||
j["source"] = item.source;
|
||||
j["created_at"] = Json::Int64(item.created_at);
|
||||
j["updated_at"] = Json::Int64(item.updated_at);
|
||||
arr.append(j);
|
||||
}
|
||||
} else {
|
||||
access["mode"] = "preview";
|
||||
access["charged"] = false;
|
||||
access["daily_free"] = false;
|
||||
access["cost"] = 0;
|
||||
access["daily_used_count"] = 0;
|
||||
}
|
||||
|
||||
Json::Value payload;
|
||||
payload["items"] = arr;
|
||||
payload["has_solutions"] = has_solutions;
|
||||
payload["answer_status"] = has_solutions ? "已有" : "待生成";
|
||||
payload["access"] = access;
|
||||
payload["runner_running"] =
|
||||
services::ProblemSolutionRunner::Instance().IsRunning(problem_id);
|
||||
if (latest_job.has_value()) {
|
||||
@@ -280,16 +325,20 @@ void ProblemController::generateSolutions(
|
||||
}
|
||||
|
||||
const int64_t job_id = svc.CreateSolutionJob(problem_id, *user_id, max_solutions);
|
||||
const bool started = services::ProblemSolutionRunner::Instance().TriggerAsync(
|
||||
auto& runner = services::ProblemSolutionRunner::Instance();
|
||||
const bool queued = runner.TriggerAsync(
|
||||
problem_id, job_id, max_solutions);
|
||||
if (!started) {
|
||||
cb(JsonError(drogon::k409Conflict, "solution generation is already running"));
|
||||
if (!queued) {
|
||||
cb(JsonError(drogon::k500InternalServerError,
|
||||
"solution generation queue is unavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
Json::Value payload;
|
||||
payload["queued"] = true;
|
||||
payload["started"] = true;
|
||||
payload["job_id"] = Json::Int64(job_id);
|
||||
payload["pending_jobs"] = Json::UInt64(runner.PendingCount());
|
||||
cb(JsonOk(payload));
|
||||
} catch (const std::exception& e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
|
||||
在新工单中引用
屏蔽一个用户