mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: allow user to manually end contest early (#1147)
This commit is contained in:
1 parent
d8a5716a22
commit
125c4b2388
9 files changed
+99
-40
No files matched your search
@@ -93,14 +93,17 @@ export class ContestDetailBaseHandler extends Handler {
|
||||
}
|
||||
}
|
||||
if (this.tdoc.duration && this.tsdoc?.startAt) {
|
||||
const endAt = moment(this.tsdoc.startAt).add(this.tdoc.duration, 'hours').toDate();
|
||||
this.tsdoc.endAt = endAt < this.tdoc.endAt ? endAt : this.tdoc.endAt;
|
||||
this.tsdoc.endAt = moment.min([
|
||||
moment(this.tsdoc.startAt).add(this.tdoc.duration, 'hours'),
|
||||
moment(this.tdoc.endAt),
|
||||
...(this.tsdoc.endAt ? [moment(this.tsdoc.endAt)] : []),
|
||||
]).toDate();
|
||||
}
|
||||
}
|
||||
|
||||
tsdocAsPublic() {
|
||||
if (!this.tsdoc) return null;
|
||||
return pick(this.tsdoc, ['attend', 'subscribe', 'startAt', ...(this.tdoc.duration ? ['endAt'] : [])]);
|
||||
return pick(this.tsdoc, ['attend', 'subscribe', 'startAt', ...(this.tdoc.duration || this.tsdoc.endAt ? ['endAt'] : [])]);
|
||||
}
|
||||
|
||||
@param('tid', Types.ObjectId, true)
|
||||
@@ -173,7 +176,7 @@ export class ContestDetailHandler extends ContestDetailBaseHandler {
|
||||
@param('code', Types.String, true)
|
||||
async postAttend(domainId: string, tid: ObjectId, code = '') {
|
||||
this.checkPerm(PERM.PERM_ATTEND_CONTEST);
|
||||
if (contest.isDone(this.tdoc)) throw new ContestNotLiveError(tid);
|
||||
if (contest.isDone(this.tdoc)) throw new ContestNotLiveError(domainId, tid);
|
||||
if (this.tdoc._code && code !== this.tdoc._code) throw new InvalidTokenError('Contest Invitation', code);
|
||||
await contest.attend(domainId, tid, this.user._id, { subscribe: 1 });
|
||||
this.back();
|
||||
@@ -186,6 +189,15 @@ export class ContestDetailHandler extends ContestDetailBaseHandler {
|
||||
await contest.setStatus(domainId, tid, this.user._id, { subscribe: subscribe ? 1 : 0 });
|
||||
this.back();
|
||||
}
|
||||
|
||||
@param('tid', Types.ObjectId)
|
||||
async postEarlyEnd(domainId: string, tid: ObjectId) {
|
||||
if (this.tdoc.rule === 'homework') throw new ContestNotFoundError(domainId, tid);
|
||||
if (!this.tsdoc?.attend) throw new ContestNotAttendedError(domainId, tid);
|
||||
if (!contest.isOngoing(this.tdoc, this.tsdoc)) throw new ContestNotLiveError(domainId, tid);
|
||||
await contest.setStatus(domainId, tid, this.user._id, { endAt: new Date() });
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
|
||||
export class ContestPrintHandler extends ContestDetailBaseHandler {
|
||||
@@ -698,10 +710,16 @@ export class ContestUserHandler extends ContestManagementBaseHandler {
|
||||
@param('tid', Types.ObjectId)
|
||||
async get(domainId: string, tid: ObjectId) {
|
||||
const tsdocs = await contest.getMultiStatus(domainId, { docId: tid }).project({
|
||||
uid: 1, attend: 1, startAt: 1, unrank: 1,
|
||||
uid: 1, attend: 1, startAt: 1, unrank: 1, endAt: 1,
|
||||
}).toArray();
|
||||
for (const tsdoc of tsdocs) {
|
||||
tsdoc.endAt = (this.tdoc.duration && tsdoc.startAt) ? moment(tsdoc.startAt).add(this.tdoc.duration, 'hours').toDate() : null;
|
||||
if (this.tdoc.duration && tsdoc.startAt) {
|
||||
tsdoc.endAt = moment.min([
|
||||
moment(tsdoc.startAt).add(this.tdoc.duration, 'hours'),
|
||||
moment(this.tdoc.endAt),
|
||||
...(tsdoc.endAt ? [moment(tsdoc.endAt)] : []),
|
||||
]).toDate();
|
||||
}
|
||||
}
|
||||
const udict = await user.getListForRender(
|
||||
domainId, [this.tdoc.owner, ...tsdocs.map((i) => i.uid)],
|
||||
@@ -728,6 +746,20 @@ export class ContestUserHandler extends ContestManagementBaseHandler {
|
||||
await contest.setStatus(domainId, tid, uid, { unrank: !tsdoc.unrank });
|
||||
this.back();
|
||||
}
|
||||
|
||||
@param('tid', Types.ObjectId)
|
||||
@param('uid', Types.PositiveInt)
|
||||
async postResume(domainId: string, tid: ObjectId, uid: number) {
|
||||
const tsdoc = await contest.getStatus(domainId, tid, uid);
|
||||
if (!tsdoc?.attend) throw new ContestNotAttendedError(uid);
|
||||
if (this.tdoc.endAt <= new Date()) throw new ContestNotLiveError(domainId, tid);
|
||||
if (this.tdoc.duration && tsdoc.startAt) {
|
||||
const durationEnd = moment(tsdoc.startAt).add(this.tdoc.duration, 'hours').toDate();
|
||||
if (durationEnd <= new Date()) throw new ContestNotLiveError(domainId, tid);
|
||||
}
|
||||
await contest.setStatus(domainId, tid, uid, null, { endAt: '' });
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
|
||||
export class ContestBalloonHandler extends ContestManagementBaseHandler {
|
||||
|
||||
@@ -62,12 +62,14 @@ export function isNotStarted(tdoc: Tdoc) {
|
||||
|
||||
export function isOngoing(tdoc: Tdoc, tsdoc?: any) {
|
||||
const now = new Date();
|
||||
if (tsdoc?.endAt && tsdoc.endAt <= now) return false;
|
||||
if (tsdoc && tdoc.duration && tsdoc.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return false;
|
||||
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
||||
}
|
||||
|
||||
export function isDone(tdoc: Tdoc, tsdoc?: any) {
|
||||
if (tdoc.endAt <= new Date()) return true;
|
||||
if (tsdoc?.endAt && tsdoc.endAt <= new Date()) return true;
|
||||
if (tsdoc && tdoc.duration && tsdoc.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -940,8 +942,8 @@ export function getMultiStatus(domainId: string, query: any) {
|
||||
return document.getMultiStatus(domainId, document.TYPE_CONTEST, query);
|
||||
}
|
||||
|
||||
export function setStatus(domainId: string, tid: ObjectId, uid: number, $set: any) {
|
||||
return document.setStatus(domainId, document.TYPE_CONTEST, tid, uid, $set);
|
||||
export function setStatus(domainId: string, tid: ObjectId, uid: number, $set?: any, $unset?: any) {
|
||||
return document.setStatus(domainId, document.TYPE_CONTEST, tid, uid, $set, $unset);
|
||||
}
|
||||
|
||||
export function count(domainId: string, query: any) {
|
||||
|
||||
@@ -295,11 +295,16 @@ export function getMultiStatusWithoutDomain<K extends keyof DocStatusType>(
|
||||
|
||||
export async function setStatus<K extends keyof DocStatusType>(
|
||||
domainId: string, docType: K, docId: DocStatusType[K]['docId'], uid: number,
|
||||
args: UpdateFilter<DocStatusType[K]>['$set'], returnDocument: 'before' | 'after' = 'after',
|
||||
$set: UpdateFilter<DocStatusType[K]>['$set'] | null,
|
||||
$unset?: UpdateFilter<DocStatusType[K]>['$unset'] | null,
|
||||
returnDocument: 'before' | 'after' = 'after',
|
||||
): Promise<DocStatusType[K]> {
|
||||
const op: UpdateFilter<DocStatusType[K]> = {};
|
||||
if ($set && Object.keys($set).length) op.$set = $set;
|
||||
if ($unset && Object.keys($unset).length) op.$unset = $unset;
|
||||
return await collStatus.findOneAndUpdate(
|
||||
{ domainId, docType, docId, uid },
|
||||
{ $set: args },
|
||||
op,
|
||||
{
|
||||
upsert: true,
|
||||
returnDocument,
|
||||
|
||||
@@ -74,7 +74,7 @@ class SolutionModel {
|
||||
if (!doc) throw new SolutionNotFoundError(domainId, psid);
|
||||
const before = await document.setStatus(
|
||||
domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid,
|
||||
{ vote: value }, 'before',
|
||||
{ vote: value }, null, 'before',
|
||||
);
|
||||
let inc = value;
|
||||
if (before?.vote) inc -= before.vote;
|
||||
|
||||
Reference in new issue
Block a user