198 lines
5.2 KiB
C++
198 lines
5.2 KiB
C++
#include "services/UserService.h"
|
|
|
|
#include "repositories/Sql.h"
|
|
|
|
#include <drogon/drogon.h>
|
|
|
|
#include <algorithm>
|
|
|
|
namespace visione::services
|
|
{
|
|
namespace
|
|
{
|
|
Json::Value failure(const std::string &code)
|
|
{
|
|
Json::Value body;
|
|
body["ok"] = false;
|
|
body["error"] = code;
|
|
return body;
|
|
}
|
|
|
|
Json::Value success()
|
|
{
|
|
Json::Value body;
|
|
body["ok"] = true;
|
|
return body;
|
|
}
|
|
|
|
int boundedPage(const std::optional<std::string> &value)
|
|
{
|
|
if (!value)
|
|
{
|
|
return 1;
|
|
}
|
|
try
|
|
{
|
|
return std::max(1, std::stoi(*value));
|
|
}
|
|
catch (...)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int boundedPerPage(const std::optional<std::string> &value)
|
|
{
|
|
if (!value)
|
|
{
|
|
return 50;
|
|
}
|
|
try
|
|
{
|
|
return std::clamp(std::stoi(*value), 1, 100);
|
|
}
|
|
catch (...)
|
|
{
|
|
return 50;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
Json::Value UserService::listNotifications(
|
|
const auth::UserContext &user,
|
|
const std::optional<std::string> &page,
|
|
const std::optional<std::string> &perPage)
|
|
{
|
|
const auto pageValue = boundedPage(page);
|
|
const auto perPageValue = boundedPerPage(perPage);
|
|
const auto offset = (pageValue - 1) * perPageValue;
|
|
|
|
auto response = success();
|
|
response["page"] = pageValue;
|
|
response["per_page"] = perPageValue;
|
|
response["notifications"] = repositories::resultToJson(
|
|
drogon::app().getDbClient()->execSqlSync(
|
|
"SELECT id, type, title, body, report_id, read_at, created_at "
|
|
"FROM notifications WHERE user_id = ? "
|
|
"ORDER BY created_at DESC LIMIT ? OFFSET ?",
|
|
user.id,
|
|
perPageValue,
|
|
offset));
|
|
return response;
|
|
}
|
|
|
|
Json::Value UserService::markNotificationRead(const auth::UserContext &user,
|
|
long long notificationId)
|
|
{
|
|
auto db = drogon::app().getDbClient();
|
|
const auto rows = db->execSqlSync(
|
|
"SELECT id FROM notifications WHERE id = ? AND user_id = ?",
|
|
notificationId,
|
|
user.id);
|
|
if (rows.empty())
|
|
{
|
|
return failure("notification_not_found");
|
|
}
|
|
db->execSqlSync(
|
|
"UPDATE notifications SET read_at = COALESCE(read_at, NOW()) WHERE id = ?",
|
|
notificationId);
|
|
return success();
|
|
}
|
|
|
|
Json::Value UserService::rewards(const auth::UserContext &user,
|
|
const std::optional<std::string> &page,
|
|
const std::optional<std::string> &perPage)
|
|
{
|
|
const auto pageValue = boundedPage(page);
|
|
const auto perPageValue = boundedPerPage(perPage);
|
|
const auto offset = (pageValue - 1) * perPageValue;
|
|
|
|
auto db = drogon::app().getDbClient();
|
|
const auto userRows = db->execSqlSync(
|
|
"SELECT points FROM users WHERE id = ?",
|
|
user.id);
|
|
|
|
auto response = success();
|
|
response["points"] = userRows.empty() ? 0 : userRows[0]["points"].as<int>();
|
|
response["page"] = pageValue;
|
|
response["per_page"] = perPageValue;
|
|
response["reward_events"] = repositories::resultToJson(db->execSqlSync(
|
|
"SELECT id, report_id, points, reason, created_at "
|
|
"FROM reward_events WHERE user_id = ? "
|
|
"ORDER BY created_at DESC LIMIT ? OFFSET ?",
|
|
user.id,
|
|
perPageValue,
|
|
offset));
|
|
return response;
|
|
}
|
|
|
|
void UserService::createNotification(long long userId,
|
|
const std::string &type,
|
|
const std::string &title,
|
|
const std::string &body,
|
|
std::optional<long long> reportId)
|
|
{
|
|
auto db = drogon::app().getDbClient();
|
|
if (reportId)
|
|
{
|
|
db->execSqlSync(
|
|
"INSERT INTO notifications (user_id, type, title, body, report_id) "
|
|
"VALUES (?, ?, ?, ?, ?)",
|
|
userId,
|
|
type,
|
|
title,
|
|
body,
|
|
*reportId);
|
|
}
|
|
else
|
|
{
|
|
db->execSqlSync(
|
|
"INSERT INTO notifications (user_id, type, title, body) "
|
|
"VALUES (?, ?, ?, ?)",
|
|
userId,
|
|
type,
|
|
title,
|
|
body);
|
|
}
|
|
}
|
|
|
|
void UserService::awardReportOwner(long long reportId,
|
|
const std::string &reason,
|
|
int points)
|
|
{
|
|
auto db = drogon::app().getDbClient();
|
|
const auto reportRows = db->execSqlSync(
|
|
"SELECT user_id FROM reports WHERE id = ?",
|
|
reportId);
|
|
if (reportRows.empty())
|
|
{
|
|
return;
|
|
}
|
|
const auto userId = reportRows[0]["user_id"].as<long long>();
|
|
const auto existing = db->execSqlSync(
|
|
"SELECT id FROM reward_events WHERE report_id = ? AND reason = ?",
|
|
reportId,
|
|
reason);
|
|
if (!existing.empty())
|
|
{
|
|
return;
|
|
}
|
|
db->execSqlSync(
|
|
"INSERT INTO reward_events (user_id, report_id, points, reason) "
|
|
"VALUES (?, ?, ?, ?)",
|
|
userId,
|
|
reportId,
|
|
points,
|
|
reason);
|
|
db->execSqlSync(
|
|
"UPDATE users SET points = points + ? WHERE id = ?",
|
|
points,
|
|
userId);
|
|
createNotification(userId,
|
|
"reward_awarded",
|
|
"Points awarded",
|
|
"Your resolved report earned points.",
|
|
reportId);
|
|
}
|
|
} // namespace visione::services
|