feat: add daily tasks and fix /admin139 admin entry
这个提交包含在:
@@ -208,6 +208,47 @@ void InsertContestProblem(sqlite3* db,
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void InsertRedeemItem(sqlite3* db,
|
||||
const std::string& name,
|
||||
const std::string& description,
|
||||
const std::string& unit_label,
|
||||
int holiday_cost,
|
||||
int studyday_cost,
|
||||
int is_active,
|
||||
int is_global,
|
||||
int64_t created_by,
|
||||
int64_t created_at) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
const char* sql =
|
||||
"INSERT INTO redeem_items("
|
||||
"name,description,unit_label,holiday_cost,studyday_cost,is_active,is_global,created_by,created_at,updated_at"
|
||||
") VALUES(?,?,?,?,?,?,?,?,?,?)";
|
||||
ThrowSqlite(sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr), db,
|
||||
"prepare insert redeem_item");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_TRANSIENT), db,
|
||||
"bind redeem_item.name");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 2, description.c_str(), -1, SQLITE_TRANSIENT),
|
||||
db, "bind redeem_item.description");
|
||||
ThrowSqlite(sqlite3_bind_text(stmt, 3, unit_label.c_str(), -1, SQLITE_TRANSIENT),
|
||||
db, "bind redeem_item.unit_label");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 4, holiday_cost), db,
|
||||
"bind redeem_item.holiday_cost");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 5, studyday_cost), db,
|
||||
"bind redeem_item.studyday_cost");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 6, is_active), db,
|
||||
"bind redeem_item.is_active");
|
||||
ThrowSqlite(sqlite3_bind_int(stmt, 7, is_global), db,
|
||||
"bind redeem_item.is_global");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 8, created_by), db,
|
||||
"bind redeem_item.created_by");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 9, created_at), db,
|
||||
"bind redeem_item.created_at");
|
||||
ThrowSqlite(sqlite3_bind_int64(stmt, 10, created_at), db,
|
||||
"bind redeem_item.updated_at");
|
||||
ThrowSqlite(sqlite3_step(stmt), db, "insert redeem_item");
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SqliteDb SqliteDb::OpenFile(const std::string& path) {
|
||||
@@ -446,6 +487,71 @@ CREATE TABLE IF NOT EXISTS problem_solutions (
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS problem_solution_view_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
problem_id INTEGER NOT NULL,
|
||||
day_key TEXT NOT NULL,
|
||||
viewed_at INTEGER NOT NULL,
|
||||
charged INTEGER NOT NULL DEFAULT 0,
|
||||
cost INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(problem_id) REFERENCES problems(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS submission_feedback (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
submission_id INTEGER NOT NULL UNIQUE,
|
||||
feedback_md TEXT NOT NULL DEFAULT "",
|
||||
links_json TEXT NOT NULL DEFAULT "[]",
|
||||
model_name TEXT NOT NULL DEFAULT "",
|
||||
status TEXT NOT NULL DEFAULT "ready",
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(submission_id) REFERENCES submissions(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS redeem_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT "",
|
||||
unit_label TEXT NOT NULL DEFAULT "小时",
|
||||
holiday_cost INTEGER NOT NULL DEFAULT 5,
|
||||
studyday_cost INTEGER NOT NULL DEFAULT 25,
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
is_global INTEGER NOT NULL DEFAULT 1,
|
||||
created_by INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS redeem_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
item_id INTEGER NOT NULL,
|
||||
item_name TEXT NOT NULL,
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
day_type TEXT NOT NULL DEFAULT "studyday",
|
||||
unit_cost INTEGER NOT NULL DEFAULT 0,
|
||||
total_cost INTEGER NOT NULL DEFAULT 0,
|
||||
note TEXT NOT NULL DEFAULT "",
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(item_id) REFERENCES redeem_items(id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS daily_task_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
task_code TEXT NOT NULL,
|
||||
day_key TEXT NOT NULL,
|
||||
reward INTEGER NOT NULL DEFAULT 1,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE(user_id, task_code, day_key)
|
||||
);
|
||||
)SQL");
|
||||
|
||||
// Backward-compatible schema upgrades for existing deployments.
|
||||
@@ -494,6 +600,11 @@ CREATE INDEX IF NOT EXISTS idx_import_job_items_job_status ON import_job_items(j
|
||||
CREATE INDEX IF NOT EXISTS idx_problem_drafts_updated ON problem_drafts(updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_problem_solution_jobs_problem ON problem_solution_jobs(problem_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_problem_solutions_problem ON problem_solutions(problem_id, variant, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_solution_view_logs_user_problem ON problem_solution_view_logs(user_id, problem_id, viewed_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_solution_view_logs_user_day ON problem_solution_view_logs(user_id, day_key, viewed_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_items_active ON redeem_items(is_active, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_records_user_created ON redeem_records(user_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_daily_task_logs_user_day ON daily_task_logs(user_id, day_key, created_at DESC);
|
||||
)SQL");
|
||||
}
|
||||
|
||||
@@ -602,6 +713,20 @@ void SeedDemoData(SqliteDb& db) {
|
||||
if (contest_id && p1) InsertContestProblem(raw, *contest_id, *p1, 1);
|
||||
if (contest_id && p2) InsertContestProblem(raw, *contest_id, *p2, 2);
|
||||
}
|
||||
|
||||
if (CountRows(raw, "redeem_items") == 0) {
|
||||
InsertRedeemItem(
|
||||
raw,
|
||||
"私人玩游戏时间",
|
||||
"全局用户可兑换:假期 1 小时 5 Rating;学习日/非节假日 1 小时 25 Rating。",
|
||||
"小时",
|
||||
5,
|
||||
25,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
now);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace csp::db
|
||||
|
||||
在新工单中引用
屏蔽一个用户