feat: rebuild CSP practice workflow, UX and automation
这个提交包含在:
@@ -0,0 +1,146 @@
|
||||
#include "csp/controllers/me_controller.h"
|
||||
|
||||
#include "csp/app_state.h"
|
||||
#include "csp/domain/json.h"
|
||||
#include "csp/services/user_service.h"
|
||||
#include "csp/services/wrong_book_service.h"
|
||||
#include "http_auth.h"
|
||||
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace csp::controllers {
|
||||
|
||||
namespace {
|
||||
|
||||
drogon::HttpResponsePtr JsonError(drogon::HttpStatusCode code,
|
||||
const std::string& msg) {
|
||||
Json::Value j;
|
||||
j["ok"] = false;
|
||||
j["error"] = msg;
|
||||
auto resp = drogon::HttpResponse::newHttpJsonResponse(j);
|
||||
resp->setStatusCode(code);
|
||||
return resp;
|
||||
}
|
||||
|
||||
drogon::HttpResponsePtr JsonOk(const Json::Value& data) {
|
||||
Json::Value j;
|
||||
j["ok"] = true;
|
||||
j["data"] = data;
|
||||
auto resp = drogon::HttpResponse::newHttpJsonResponse(j);
|
||||
resp->setStatusCode(drogon::k200OK);
|
||||
return resp;
|
||||
}
|
||||
|
||||
std::optional<int64_t> RequireAuth(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>& cb) {
|
||||
std::string auth_error;
|
||||
const auto user_id = GetAuthedUserId(req, auth_error);
|
||||
if (!user_id.has_value()) {
|
||||
cb(JsonError(drogon::k401Unauthorized, auth_error));
|
||||
return std::nullopt;
|
||||
}
|
||||
return user_id;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MeController::profile(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb) {
|
||||
try {
|
||||
const auto user_id = RequireAuth(req, cb);
|
||||
if (!user_id.has_value()) return;
|
||||
|
||||
services::UserService users(csp::AppState::Instance().db());
|
||||
const auto user = users.GetById(*user_id);
|
||||
if (!user.has_value()) {
|
||||
cb(JsonError(drogon::k404NotFound, "user not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
cb(JsonOk(domain::ToPublicJson(*user)));
|
||||
} catch (const std::exception& e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MeController::listWrongBook(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb) {
|
||||
try {
|
||||
const auto user_id = RequireAuth(req, cb);
|
||||
if (!user_id.has_value()) return;
|
||||
|
||||
services::WrongBookService wrong_book(csp::AppState::Instance().db());
|
||||
const auto rows = wrong_book.ListByUser(*user_id);
|
||||
|
||||
Json::Value arr(Json::arrayValue);
|
||||
for (const auto& row : rows) {
|
||||
Json::Value item = domain::ToJson(row.item);
|
||||
item["problem_title"] = row.problem_title;
|
||||
arr.append(item);
|
||||
}
|
||||
|
||||
cb(JsonOk(arr));
|
||||
} catch (const std::exception& e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MeController::upsertWrongBookNote(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id) {
|
||||
try {
|
||||
const auto user_id = RequireAuth(req, cb);
|
||||
if (!user_id.has_value()) return;
|
||||
|
||||
const auto json = req->getJsonObject();
|
||||
if (!json) {
|
||||
cb(JsonError(drogon::k400BadRequest, "body must be json"));
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string note = (*json).get("note", "").asString();
|
||||
if (note.size() > 4000) {
|
||||
cb(JsonError(drogon::k400BadRequest, "note too long"));
|
||||
return;
|
||||
}
|
||||
|
||||
services::WrongBookService wrong_book(csp::AppState::Instance().db());
|
||||
wrong_book.UpsertNote(*user_id, problem_id, note);
|
||||
|
||||
Json::Value data;
|
||||
data["user_id"] = Json::Int64(*user_id);
|
||||
data["problem_id"] = Json::Int64(problem_id);
|
||||
data["note"] = note;
|
||||
cb(JsonOk(data));
|
||||
} catch (const std::exception& e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MeController::deleteWrongBookItem(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
|
||||
int64_t problem_id) {
|
||||
try {
|
||||
const auto user_id = RequireAuth(req, cb);
|
||||
if (!user_id.has_value()) return;
|
||||
|
||||
services::WrongBookService wrong_book(csp::AppState::Instance().db());
|
||||
wrong_book.Remove(*user_id, problem_id);
|
||||
|
||||
Json::Value data;
|
||||
data["user_id"] = Json::Int64(*user_id);
|
||||
data["problem_id"] = Json::Int64(problem_id);
|
||||
data["deleted"] = true;
|
||||
cb(JsonOk(data));
|
||||
} catch (const std::exception& e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace csp::controllers
|
||||
在新工单中引用
屏蔽一个用户