Files
ojnext/src/admin/api.ts
2026-07-04 03:50:43 -06:00

526 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import http from "utils/http"
import { toProblemListItem } from "admin/transforms"
import type {
AdminProblem,
Announcement,
AnnouncementEdit,
BlankContest,
BlankProblem,
Contest,
Exercise,
ExerciseType,
Server,
SQLDisplay,
TestcaseUploadedReturns,
Tutorial,
User,
WebsiteConfig,
} from "utils/types"
export function getBaseInfo() {
return http.get("admin/dashboard_info")
}
export function randomUser10(classroom: string) {
return http.get("admin/random_user", { params: { classroom } })
}
export async function getProblemList(
offset = 0,
limit = 10,
keyword: string,
author?: string,
contestID?: string,
) {
const endpoint = !!contestID ? "admin/contest/problem" : "admin/problem"
const res = await http.get<{ results: AdminProblem[]; total: number }>(
endpoint,
{
params: {
paging: true,
offset,
limit,
keyword,
author,
contest_id: contestID,
},
},
)
return {
results: res.data.results.map(toProblemListItem),
total: res.data.total,
}
}
export function deleteProblem(id: number) {
return http.delete("admin/problem", { params: { id } })
}
export function deleteContestProblem(id: number) {
return http.delete("admin/contest/problem", { params: { id } })
}
export function editProblem(problem: AdminProblem | BlankProblem) {
return http.put("admin/problem", problem)
}
export function toggleProblemVisible(problemID: number) {
return http.put("admin/problem/visible", { id: problemID })
}
export function generateFlowchartFromPythonCode(python: string) {
return http.post("admin/problem/flowchart", { python })
}
export function editContestProblem(problem: AdminProblem | BlankProblem) {
return http.put("admin/contest/problem", problem)
}
export function getProblem(id: string | number) {
return http.get<AdminProblem>("admin/problem", { params: { id } })
}
export function getContestProblem(id: number) {
return http.get("admin/contest/problem", { params: { id } })
}
// 用户列表
export function getUserList(
offset = 0,
limit = 10,
type = "",
keyword: string,
orderBy = "",
) {
return http.get("admin/user", {
params: { paging: true, offset, limit, keyword, type, order_by: orderBy },
})
}
// 编辑用户
export function editUser(user: User) {
return http.put("admin/user", user)
}
// 重置用户密码
export function resetPassword(userID: number) {
return http.post("admin/reset_password", { id: userID })
}
// 导入用户
export function importUsers(users: string[][]) {
return http.post("admin/user", { users })
}
// 批量删除用户
export function deleteUsers(userIDs: number[]) {
return http.delete("admin/user", { params: { id: userIDs.join(",") } })
}
export function getContestList(offset = 0, limit = 10, keyword: string) {
return http.get("admin/contest", {
params: { paging: true, offset, limit, keyword },
})
}
// 上传图片
export async function uploadImage(file: File): Promise<string> {
const form = new window.FormData()
form.append("image", file)
// 该端点不走 { error, data } 信封,直接返回上传结果
const res = (await http.post("admin/upload_image", form, {
headers: { "content-type": "multipart/form-data" },
})) as unknown as { success: boolean; file_path: string; msg: "Success" }
return res.success ? res.file_path : ""
}
// 上传测试用例SQL 题的压缩包是 1.sql..N.sql每个文件一个测试点的建表+数据脚本)
export function uploadTestcases(file: File, options: { sql?: boolean } = {}) {
const form = new window.FormData()
form.append("file", file)
if (options.sql) {
form.append("sql", "1")
}
return http.post<TestcaseUploadedReturns>("admin/test_case", form, {
headers: { "content-type": "multipart/form-data" },
})
}
// SQL 题测试点预览:后端跑一遍初始化脚本+标准答案,返回数据表和期望结果展示数据
export function previewSQLTestcase(data: {
init_sql: string
ref_sql: string
mode: "query" | "modify"
}) {
return http.post<SQLDisplay>("admin/sql_test_case_preview", data)
}
// 回显已上传的 SQL 测试点脚本内容(按 1.sql, 2.sql... 排序)
export function getSQLTestcaseScripts(problemId: number) {
return http.get<{ name: string; content: string }[]>(
"admin/sql_test_case_scripts",
{ params: { problem_id: problemId } },
)
}
export function createProblem(problem: BlankProblem) {
return http.post("admin/problem", problem)
}
export function createContestProblem(problem: BlankProblem) {
return http.post("admin/contest/problem", problem)
}
export function createContest(contest: BlankContest) {
return http.post("admin/contest", contest)
}
export function editContest(contest: Contest | BlankContest) {
return http.put("admin/contest", contest)
}
export function cloneContest(contest_id: number) {
return http.post("admin/contest/clone", { contest_id })
}
export function getContest(id: string) {
return http.get<Contest & { password: string }>("admin/contest", {
params: { id },
})
}
export function addProblemForContest(
contestID: string,
problemID: number,
displayID: string,
) {
return http.post("admin/contest/add_problem_from_public", {
contest_id: contestID,
problem_id: problemID,
display_id: displayID,
})
}
export function getWebsite() {
return http.get<WebsiteConfig>("admin/website")
}
export function editWebsite(data: WebsiteConfig) {
return http.post("admin/website", data)
}
export function listInvalidTestcases() {
return http.get("admin/prune_test_case")
}
export function pruneInvalidTestcases(id?: string) {
return http.delete("admin/prune_test_case", { params: { id } })
}
export function getJudgeServer() {
return http.get<{ token: string; servers: Server[] }>("admin/judge_server")
}
export function deleteJudgeServer(hostname: string) {
return http.delete("admin/judge_server", { params: { hostname } })
}
export function getAnnouncementList(offset = 0, limit = 10) {
return http.get("admin/announcement", {
params: { paging: true, offset, limit },
})
}
export function getAnnouncement(id: number) {
return http.get<Announcement>("admin/announcement", { params: { id } })
}
export function deleteAnnouncement(id: number) {
return http.delete("admin/announcement", { params: { id } })
}
export function editAnnouncement(announcement: AnnouncementEdit) {
return http.put("admin/announcement", announcement)
}
export function createAnnouncement(announcement: AnnouncementEdit) {
return http.post("admin/announcement", announcement)
}
export function getCommentList(offset = 0, limit = 10, problem: string) {
return http.get("admin/comment", {
params: { offset, limit, problem },
})
}
export function deleteComment(id: number) {
return http.delete("admin/comment", { params: { id } })
}
export async function getTutorialList() {
const res = await http.get<Tutorial[]>("admin/tutorial")
return res.data
}
export async function getTutorial(id: number) {
const res = await http.get<Tutorial>("admin/tutorial", { params: { id } })
return res.data
}
export async function createTutorial(data: Partial<Tutorial>) {
const res = await http.post<Tutorial>("admin/tutorial", data)
return res.data
}
export async function updateTutorial(data: Partial<Tutorial>) {
const res = await http.put("admin/tutorial", data)
return res.data
}
export function deleteTutorial(id: number) {
return http.delete("admin/tutorial", { params: { id } })
}
export function setTutorialVisibility(id: number, is_public: boolean) {
return http.put("admin/tutorial/visibility", { id, is_public })
}
export async function getAdminExercises(tutorialId: number) {
const res = await http.get<Exercise[]>("admin/exercise", {
params: { tutorial_id: tutorialId },
})
return res.data
}
export async function createExercise(data: {
tutorial_id: number
type: ExerciseType
data: object
order: number
}) {
const res = await http.post<Exercise>("admin/exercise", data)
return res.data
}
export async function updateExercise(data: {
id: number
type: ExerciseType
data: object
order: number
}) {
const res = await http.put("admin/exercise", data)
return res.data as Exercise
}
export function deleteExercise(id: number) {
return http.delete("admin/exercise", { params: { id } })
}
// 将竞赛题目转为公开题目
export function makeProblemPublic(id: number, display_id: string) {
return http.post("admin/contest_problem/make_public", {
id,
display_id,
})
}
// 比赛辅助检查
export function getACMHelperList(contest_id: number) {
return http.get("admin/contest/acm_helper", {
params: { contest_id },
})
}
export function updateACMHelperChecked(
contest_id: number,
rank_id: number,
problem_id: string,
checked: boolean,
) {
return http.put("admin/contest/acm_helper", {
contest_id,
rank_id,
problem_id,
checked,
})
}
// 题单管理 API
export function getProblemSetList(
offset = 0,
limit = 10,
keyword = "",
difficulty = "",
status = "",
) {
return http.get("admin/problemset", {
params: {
offset,
limit,
keyword,
difficulty,
status,
},
})
}
export function getProblemSetDetail(id: number) {
return http.get(`admin/problemset/${id}`)
}
export function createProblemSet(data: {
title: string
description: string
difficulty: string
status: string
end_time?: Date | null
}) {
return http.post("admin/problemset", data)
}
export function editProblemSet(data: {
id: number
title?: string
description?: string
difficulty?: string
status?: string
end_time?: Date | null
visible?: boolean
}) {
return http.put("admin/problemset", data)
}
export function deleteProblemSet(id: number) {
return http.delete("admin/problemset", { params: { id } })
}
export function toggleProblemSetVisible(id: number) {
return http.put("admin/problemset/visible", { id })
}
export function updateProblemSetStatus(id: number, status: string) {
return http.put("admin/problemset/status", { id, status })
}
// 题单题目管理 API
export function getProblemSetProblems(problemSetId: number) {
return http.get(`admin/problemset/${problemSetId}/problems`)
}
export function addProblemToSet(
problemSetId: number,
data: {
problem_id: string
order?: number
is_required?: boolean
score?: number
hint?: string
},
) {
return http.post(`admin/problemset/${problemSetId}/problems`, data)
}
export function editProblemInSet(
problemSetId: number,
problemSetProblemId: number,
data: {
order?: number
is_required?: boolean
score?: number
hint?: string
},
) {
return http.put(
`admin/problemset/${problemSetId}/problems/${problemSetProblemId}`,
data,
)
}
export function removeProblemFromSet(
problemSetId: number,
problemSetProblemId: number,
) {
return http.delete(
`admin/problemset/${problemSetId}/problems/${problemSetProblemId}`,
)
}
// 题单奖章管理 API
export function getProblemSetBadges(problemSetId: number) {
return http.get(`admin/problemset/${problemSetId}/badges`)
}
export function createProblemSetBadge(
problemSetId: number,
data: {
name: string
description: string
icon: string
condition_type: string
condition_value: number
level?: number
},
) {
return http.post(`admin/problemset/${problemSetId}/badges`, data)
}
export function editProblemSetBadge(
problemSetId: number,
badgeId: number,
data: {
name?: string
description?: string
icon?: string
condition_type?: string
condition_value?: number
level?: number
},
) {
return http.put(`admin/problemset/${problemSetId}/badges/${badgeId}`, data)
}
export function deleteProblemSetBadge(problemSetId: number, badgeId: number) {
return http.delete(`admin/problemset/${problemSetId}/badges/${badgeId}`)
}
// 题单进度管理 API
export function getProblemSetProgress(problemSetId: number) {
return http.get(`admin/problemset/${problemSetId}/progress`)
}
export function removeUserFromProblemSet(problemSetId: number, userId: number) {
return http.delete(`admin/problemset/${problemSetId}/progress/${userId}`)
}
// 学生卡点分析
export function getStuckProblems() {
return http.get("admin/problem/stuck")
}
export function getTopACTrend(params: {
since_year: number
until_year: number
min_per_year: number
}) {
return http.get("admin/problem/top_ac_trend", { params })
}
// AI 学习分析报告
export function getAIReportList(offset = 0, limit = 10, username = "") {
return http.get("admin/ai/reports", {
params: { paging: true, offset, limit, username: username || undefined },
})
}
export function getAIReportDetail(id: number) {
return http.get("admin/ai/reports", { params: { id } })
}
export function pinAIReport(id: number) {
return http.post("admin/ai/reports", { id })
}
export function getPinnedAIReports() {
return http.get("admin/ai/reports", { params: { pinned_only: "true" } })
}