feat: 完成源晶权限与经验系统并优化 me/admin 交互
这个提交包含在:
@@ -1,7 +1,12 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/services/auth_service.h"
|
||||
#include "csp/services/kb_service.h"
|
||||
#include "csp/services/user_service.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
TEST_CASE("kb service list/detail") {
|
||||
auto db = csp::db::SqliteDb::OpenMemory();
|
||||
@@ -16,3 +21,107 @@ TEST_CASE("kb service list/detail") {
|
||||
REQUIRE(detail.has_value());
|
||||
REQUIRE(detail->article.slug == rows.front().slug);
|
||||
}
|
||||
|
||||
TEST_CASE("kb skill claim requires prerequisites") {
|
||||
auto db = csp::db::SqliteDb::OpenMemory();
|
||||
csp::db::ApplyMigrations(db);
|
||||
csp::db::SeedDemoData(db);
|
||||
|
||||
csp::services::AuthService auth(db);
|
||||
const auto login = auth.Register("kb_pre_user", "password123");
|
||||
|
||||
csp::services::KbService svc(db);
|
||||
const auto detail = svc.GetBySlug("cpp14-skill-tree");
|
||||
REQUIRE(detail.has_value());
|
||||
REQUIRE(detail->article.id > 0);
|
||||
|
||||
bool prerequisite_throw = false;
|
||||
try {
|
||||
(void)svc.ClaimSkillPoint(login.user_id, detail->article.id, detail->article.slug,
|
||||
"cpp14-type-02");
|
||||
} catch (const std::runtime_error& e) {
|
||||
prerequisite_throw = true;
|
||||
REQUIRE(std::string(e.what()).find("prerequisite not completed") != std::string::npos);
|
||||
}
|
||||
REQUIRE(prerequisite_throw);
|
||||
|
||||
const auto first =
|
||||
svc.ClaimSkillPoint(login.user_id, detail->article.id, detail->article.slug,
|
||||
"cpp14-io-01");
|
||||
REQUIRE(first.claimed);
|
||||
REQUIRE(first.reward == 1);
|
||||
|
||||
const auto second =
|
||||
svc.ClaimSkillPoint(login.user_id, detail->article.id, detail->article.slug,
|
||||
"cpp14-type-02");
|
||||
REQUIRE(second.claimed);
|
||||
REQUIRE(second.reward == 1);
|
||||
|
||||
const auto second_again =
|
||||
svc.ClaimSkillPoint(login.user_id, detail->article.id, detail->article.slug,
|
||||
"cpp14-type-02");
|
||||
REQUIRE_FALSE(second_again.claimed);
|
||||
REQUIRE(second_again.reward == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("kb weekly tasks auto-generate and bonus awarded at 100 percent") {
|
||||
auto db = csp::db::SqliteDb::OpenMemory();
|
||||
csp::db::ApplyMigrations(db);
|
||||
csp::db::SeedDemoData(db);
|
||||
|
||||
csp::services::AuthService auth(db);
|
||||
const auto login = auth.Register("kb_weekly_user", "password123");
|
||||
|
||||
csp::services::KbService svc(db);
|
||||
csp::services::UserService users(db);
|
||||
|
||||
auto plan = svc.GetWeeklyPlan(login.user_id);
|
||||
REQUIRE_FALSE(plan.tasks.empty());
|
||||
REQUIRE(plan.tasks.size() <= 8);
|
||||
REQUIRE(plan.completion_percent == 0);
|
||||
|
||||
std::unordered_set<std::string> unlocked;
|
||||
for (const auto& task : plan.tasks) {
|
||||
for (const auto& pre : task.prerequisites) {
|
||||
REQUIRE(unlocked.count(pre) > 0);
|
||||
}
|
||||
unlocked.insert(task.knowledge_key);
|
||||
}
|
||||
|
||||
bool bonus_throw = false;
|
||||
try {
|
||||
(void)svc.ClaimWeeklyBonus(login.user_id);
|
||||
} catch (const std::runtime_error& e) {
|
||||
bonus_throw = true;
|
||||
REQUIRE(std::string(e.what()).find("100% completed") != std::string::npos);
|
||||
}
|
||||
REQUIRE(bonus_throw);
|
||||
|
||||
int weekly_reward_sum = 0;
|
||||
for (const auto& task : plan.tasks) {
|
||||
const auto claim = svc.ClaimSkillPoint(login.user_id, task.article_id,
|
||||
task.article_slug, task.knowledge_key);
|
||||
weekly_reward_sum += claim.reward;
|
||||
REQUIRE(claim.claimed);
|
||||
}
|
||||
|
||||
plan = svc.GetWeeklyPlan(login.user_id);
|
||||
REQUIRE(plan.completion_percent == 100);
|
||||
REQUIRE(plan.gained_reward == plan.total_reward);
|
||||
|
||||
const auto bonus = svc.ClaimWeeklyBonus(login.user_id);
|
||||
REQUIRE(bonus.claimed);
|
||||
REQUIRE(bonus.reward == plan.bonus_reward);
|
||||
REQUIRE(bonus.completion_percent == 100);
|
||||
REQUIRE(bonus.week_key == plan.week_key);
|
||||
|
||||
const auto user = users.GetById(login.user_id);
|
||||
REQUIRE(user.has_value());
|
||||
// Register auto-login grants +1 via login check-in task.
|
||||
REQUIRE(user->rating == 1 + weekly_reward_sum + plan.bonus_reward);
|
||||
|
||||
const auto bonus_again = svc.ClaimWeeklyBonus(login.user_id);
|
||||
REQUIRE_FALSE(bonus_again.claimed);
|
||||
REQUIRE(bonus_again.reward == 0);
|
||||
REQUIRE(bonus_again.rating_after == user->rating);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户