feat: Minecraft theme overhaul, fix points bug, add history
这个提交包含在:
@@ -4,6 +4,7 @@
|
||||
#include "csp/domain/json.h"
|
||||
#include "csp/services/daily_task_service.h"
|
||||
#include "csp/services/redeem_service.h"
|
||||
#include "csp/services/solution_access_service.h"
|
||||
#include "csp/services/user_service.h"
|
||||
#include "csp/services/wrong_book_service.h"
|
||||
#include "http_auth.h"
|
||||
@@ -19,7 +20,7 @@ namespace csp::controllers {
|
||||
namespace {
|
||||
|
||||
drogon::HttpResponsePtr JsonError(drogon::HttpStatusCode code,
|
||||
const std::string& msg) {
|
||||
const std::string &msg) {
|
||||
Json::Value j;
|
||||
j["ok"] = false;
|
||||
j["error"] = msg;
|
||||
@@ -28,7 +29,7 @@ drogon::HttpResponsePtr JsonError(drogon::HttpStatusCode code,
|
||||
return resp;
|
||||
}
|
||||
|
||||
drogon::HttpResponsePtr JsonOk(const Json::Value& data) {
|
||||
drogon::HttpResponsePtr JsonOk(const Json::Value &data) {
|
||||
Json::Value j;
|
||||
j["ok"] = true;
|
||||
j["data"] = data;
|
||||
@@ -37,8 +38,9 @@ drogon::HttpResponsePtr JsonOk(const Json::Value& data) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
std::optional<int64_t> RequireAuth(const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>& cb) {
|
||||
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()) {
|
||||
@@ -48,23 +50,23 @@ std::optional<int64_t> RequireAuth(const drogon::HttpRequestPtr& req,
|
||||
return user_id;
|
||||
}
|
||||
|
||||
int ParseClampedInt(const std::string& s,
|
||||
int default_value,
|
||||
int min_value,
|
||||
int ParseClampedInt(const std::string &s, int default_value, int min_value,
|
||||
int max_value) {
|
||||
if (s.empty()) return default_value;
|
||||
if (s.empty())
|
||||
return default_value;
|
||||
const int value = std::stoi(s);
|
||||
return std::max(min_value, std::min(max_value, value));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
void MeController::profile(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb) {
|
||||
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;
|
||||
if (!user_id.has_value())
|
||||
return;
|
||||
|
||||
services::UserService users(csp::AppState::Instance().db());
|
||||
const auto user = users.GetById(*user_id);
|
||||
@@ -74,22 +76,23 @@ void MeController::profile(
|
||||
}
|
||||
|
||||
cb(JsonOk(domain::ToPublicJson(*user)));
|
||||
} catch (const std::exception& e) {
|
||||
} catch (const std::exception &e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MeController::listRedeemItems(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb) {
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&cb) {
|
||||
try {
|
||||
if (!RequireAuth(req, cb).has_value()) return;
|
||||
if (!RequireAuth(req, cb).has_value())
|
||||
return;
|
||||
|
||||
services::RedeemService redeem(csp::AppState::Instance().db());
|
||||
const auto items = redeem.ListItems(false);
|
||||
|
||||
Json::Value arr(Json::arrayValue);
|
||||
for (const auto& item : items) {
|
||||
for (const auto &item : items) {
|
||||
Json::Value j;
|
||||
j["id"] = Json::Int64(item.id);
|
||||
j["name"] = item.name;
|
||||
@@ -104,24 +107,25 @@ void MeController::listRedeemItems(
|
||||
arr.append(j);
|
||||
}
|
||||
cb(JsonOk(arr));
|
||||
} catch (const std::exception& e) {
|
||||
} catch (const std::exception &e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MeController::listRedeemRecords(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb) {
|
||||
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;
|
||||
if (!user_id.has_value())
|
||||
return;
|
||||
|
||||
const int limit = ParseClampedInt(req->getParameter("limit"), 100, 1, 500);
|
||||
services::RedeemService redeem(csp::AppState::Instance().db());
|
||||
const auto rows = redeem.ListRecordsByUser(*user_id, limit);
|
||||
|
||||
Json::Value arr(Json::arrayValue);
|
||||
for (const auto& row : rows) {
|
||||
for (const auto &row : rows) {
|
||||
Json::Value j;
|
||||
j["id"] = Json::Int64(row.id);
|
||||
j["user_id"] = Json::Int64(row.user_id);
|
||||
@@ -136,21 +140,22 @@ void MeController::listRedeemRecords(
|
||||
arr.append(j);
|
||||
}
|
||||
cb(JsonOk(arr));
|
||||
} catch (const std::invalid_argument&) {
|
||||
} catch (const std::invalid_argument &) {
|
||||
cb(JsonError(drogon::k400BadRequest, "invalid query parameter"));
|
||||
} catch (const std::out_of_range&) {
|
||||
} catch (const std::out_of_range &) {
|
||||
cb(JsonError(drogon::k400BadRequest, "query parameter out of range"));
|
||||
} catch (const std::exception& e) {
|
||||
} catch (const std::exception &e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MeController::createRedeemRecord(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb) {
|
||||
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;
|
||||
if (!user_id.has_value())
|
||||
return;
|
||||
|
||||
const auto json = req->getJsonObject();
|
||||
if (!json) {
|
||||
@@ -186,19 +191,20 @@ void MeController::createRedeemRecord(
|
||||
j["rating_after"] = user->rating;
|
||||
}
|
||||
cb(JsonOk(j));
|
||||
} catch (const std::runtime_error& e) {
|
||||
} catch (const std::runtime_error &e) {
|
||||
cb(JsonError(drogon::k400BadRequest, e.what()));
|
||||
} catch (const std::exception& e) {
|
||||
} catch (const std::exception &e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MeController::listDailyTasks(
|
||||
const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& cb) {
|
||||
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;
|
||||
if (!user_id.has_value())
|
||||
return;
|
||||
|
||||
services::DailyTaskService tasks(csp::AppState::Instance().db());
|
||||
const auto rows = tasks.ListTodayTasks(*user_id);
|
||||
@@ -206,7 +212,7 @@ void MeController::listDailyTasks(
|
||||
Json::Value arr(Json::arrayValue);
|
||||
int total_reward = 0;
|
||||
int gained_reward = 0;
|
||||
for (const auto& row : rows) {
|
||||
for (const auto &row : rows) {
|
||||
Json::Value j;
|
||||
j["code"] = row.code;
|
||||
j["title"] = row.title;
|
||||
@@ -229,41 +235,43 @@ void MeController::listDailyTasks(
|
||||
out["gained_reward"] = gained_reward;
|
||||
out["tasks"] = arr;
|
||||
cb(JsonOk(out));
|
||||
} catch (const std::exception& e) {
|
||||
} 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) {
|
||||
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;
|
||||
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) {
|
||||
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) {
|
||||
} 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,
|
||||
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;
|
||||
if (!user_id.has_value())
|
||||
return;
|
||||
|
||||
const auto json = req->getJsonObject();
|
||||
if (!json) {
|
||||
@@ -285,18 +293,19 @@ void MeController::upsertWrongBookNote(
|
||||
data["problem_id"] = Json::Int64(problem_id);
|
||||
data["note"] = note;
|
||||
cb(JsonOk(data));
|
||||
} catch (const std::exception& e) {
|
||||
} 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,
|
||||
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;
|
||||
if (!user_id.has_value())
|
||||
return;
|
||||
|
||||
services::WrongBookService wrong_book(csp::AppState::Instance().db());
|
||||
wrong_book.Remove(*user_id, problem_id);
|
||||
@@ -306,9 +315,35 @@ void MeController::deleteWrongBookItem(
|
||||
data["problem_id"] = Json::Int64(problem_id);
|
||||
data["deleted"] = true;
|
||||
cb(JsonOk(data));
|
||||
} catch (const std::exception& e) {
|
||||
} catch (const std::exception &e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace csp::controllers
|
||||
void MeController::listRatingHistory(
|
||||
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;
|
||||
|
||||
const int limit = ParseClampedInt(req->getParameter("limit"), 100, 1, 500);
|
||||
services::SolutionAccessService access_svc(csp::AppState::Instance().db());
|
||||
const auto rows = access_svc.ListRatingHistory(*user_id, limit);
|
||||
|
||||
Json::Value arr(Json::arrayValue);
|
||||
for (const auto &row : rows) {
|
||||
Json::Value j;
|
||||
j["type"] = row.type;
|
||||
j["created_at"] = Json::Int64(row.created_at);
|
||||
j["change"] = row.change;
|
||||
j["note"] = row.note;
|
||||
arr.append(j);
|
||||
}
|
||||
cb(JsonOk(arr));
|
||||
} catch (const std::exception &e) {
|
||||
cb(JsonError(drogon::k500InternalServerError, e.what()));
|
||||
}
|
||||
}
|
||||
} // namespace csp::controllers
|
||||
|
||||
在新工单中引用
屏蔽一个用户