core: admin can remove contestant before contest start (#1196)

This commit is contained in:
Baoshuo authored and GitHub committed 2026-08-03 12:42:33 +08:00
1 parent 52f811cf98
commit f29971a30d
11 files changed
+50 -3

No files matched your search

+1
View File
@@ -685,6 +685,7 @@ The user is too lazy to leave something here...: 설명이 없습니다... (´
There are no contests...: 대회 없음
There is no homework so far ╰(*°▽°*)╯: 과제 없음 ╰(*°▽°*)╯
This contest is not live.: 대회가 실시간이 아닙니다.
This contest has already started.: 대회가 이미 시작되었습니다.
This email was sent by {0} automatically, and please do not reply directly.: '{0}으로 이메일을 보냈습니다.'
This homework is not open and you cannot view problems.: 과제를 열지 않았기에 문제를 볼 수 없습니다.
This homework is not open.: 과제가 아직 닫혀 있습니다.
+1
View File
@@ -811,6 +811,7 @@ theme: 主题
There are no contests...: 没有比赛…
There is no homework so far ╰(*°▽°*)╯: 目前还没有作业 ╰(*°▽°*)╯
This contest is not live.: 比赛没有开始。
This contest has already started.: 比赛已开始。
This email was sent by {0} automatically, and please do not reply directly.: 这封邮件由 {0} 自动发送,请勿直接回复。
This homework is not open and you cannot view problems.: 该作业还未到开放时间,您无法查看作业题目。
This homework is not open.: 该作业还未开放递交。
+1
View File
@@ -808,6 +808,7 @@ theme: 主題
There are no contests...: 沒有比賽…
There is no homework so far ╰(*°▽°*)╯: 目前沒有功課 ╰(*°▽°*)╯
This contest is not live.: 比賽沒有開始。
This contest has already started.: 比賽已開始。
This email was sent by {0} automatically, and please do not reply directly.: 這封郵件由 {0} 自動傳送,請勿直接回復。
This homework is not open and you cannot view problems.: 功課尚未開放,不能檢視題目。
This homework is not open.: 功課尚未開放遞交。
+1
View File
@@ -35,6 +35,7 @@ export const ContestNotAttendedError = Err('ContestNotAttendedError', ForbiddenE
export const RequireProError = Err('RequireProError', ForbiddenError, 'RequireProError');
export const ContestAlreadyAttendedError = Err('ContestAlreadyAttendedError', ForbiddenError, "You've already attended this contest.");
export const ContestNotLiveError = Err('ContestNotLiveError', ForbiddenError, 'This contest is not live.');
export const ContestAlreadyStartedError = Err('ContestAlreadyStartedError', ForbiddenError, 'This contest has already started.');
export const ContestNotEndedError = Err('ContestNotEndedError', ForbiddenError, 'This contest is not ended.');
export const ContestScoreboardHiddenError = Err('ContestScoreboardHiddenError', ForbiddenError, 'Contest scoreboard is not visible.');
export const TrainingAlreadyEnrollError = Err('TrainingAlreadyEnrollError', ForbiddenError, "You've already enrolled this training.");
+12 -2
View File
@@ -10,8 +10,8 @@ import {
} from '@hydrooj/utils/lib/utils';
import { Context, Service } from '../context';
import {
BadRequestError, ContestNotAttendedError, ContestNotEndedError, ContestNotFoundError, ContestNotLiveError,
ContestScoreboardHiddenError, FileLimitExceededError, FileUploadError,
BadRequestError, ContestAlreadyStartedError, ContestNotAttendedError, ContestNotEndedError, ContestNotFoundError,
ContestNotLiveError, ContestScoreboardHiddenError, FileLimitExceededError, FileUploadError,
InvalidTokenError, MethodNotAllowedError, NotAssignedError, NotFoundError, PermissionError, ValidationError,
} from '../error';
import { ContestStatusDoc, FileInfo, ScoreboardConfig, Tdoc } from '../interface';
@@ -761,6 +761,16 @@ export class ContestUserHandler extends ContestManagementBaseHandler {
await contest.setStatus(domainId, tid, uid, null, { endAt: '' });
this.back();
}
@param('tid', Types.ObjectId)
@param('uid', Types.Int)
async postRemoveUser(domainId: string, tid: ObjectId, uid: number) {
if (!contest.isNotStarted(this.tdoc)) throw new ContestAlreadyStartedError();
const tsdoc = await contest.getStatus(domainId, tid, uid);
if (!tsdoc?.attend) throw new ContestNotAttendedError(uid);
await contest.cancelAttend(domainId, tid, uid);
this.back();
}
}
export class ContestBalloonHandler extends ContestManagementBaseHandler {
+7
View File
@@ -939,6 +939,12 @@ export async function attend(domainId: string, tid: ObjectId, uid: number, paylo
return {};
}
export async function cancelAttend(domainId: string, tid: ObjectId, uid: number) {
await document.deleteMultiStatus(domainId, document.TYPE_CONTEST, { docId: tid, uid });
await document.inc(domainId, document.TYPE_CONTEST, tid, 'attend', -1);
return {};
}
export function getMultiStatus(domainId: string, query: any) {
return document.getMultiStatus(domainId, document.TYPE_CONTEST, query);
}
@@ -1148,6 +1154,7 @@ global.Hydro.model.contest = {
getListStatus,
getMultiStatus,
attend,
cancelAttend,
edit,
del,
get,