Skip to content

Commit

Permalink
feat: complete competition event for solution module
Browse files Browse the repository at this point in the history
dreamerblue committed May 4, 2024
1 parent 3e75b46 commit d79711e
Showing 11 changed files with 240 additions and 25 deletions.
6 changes: 5 additions & 1 deletion src/app/solution/solution.contract.ts
Original file line number Diff line number Diff line change
@@ -644,6 +644,10 @@ const solutionContract = {
judgeInfoId: { type: 'number' },
solutionId: { type: 'number' },
judgerId: { type: 'string' },
userId: { type: 'number' },
problemId: { type: 'number' },
contestId: { type: 'number' },
competitionId: { type: 'number' },
data: {
anyOf: [
{
@@ -684,7 +688,7 @@ const solutionContract = {
},
eventTimestampUs: { type: 'number' },
},
additionalProperties: false,
additionalProperties: true,
required: ['judgeInfoId', 'solutionId', 'judgerId', 'data', 'eventTimestampUs'],
} as defContract.ContractSchema,
};
14 changes: 13 additions & 1 deletion src/app/solution/solution.controller.ts
Original file line number Diff line number Diff line change
@@ -507,6 +507,7 @@ export default class SolutionController {
user: {
userId: sess.userId,
},
competition: competitionId ? { competitionId } : undefined,
language,
code,
});
@@ -532,6 +533,7 @@ export default class SolutionController {
solutionId: newId,
problemId,
userId: sess.userId,
judgeInfoId,
});
}
} catch (e) {
@@ -635,6 +637,7 @@ export default class SolutionController {
user: {
userId: s.userId,
},
competition: s.competitionId ? { competitionId: s.competitionId } : undefined,
language: this.utils.judger.convertOJLanguageToRiver(s.language) || '',
code: codeMap.get(s.solutionId) || '',
}),
@@ -668,9 +671,16 @@ export default class SolutionController {
async [routesBe.callbackJudge.i](ctx: Context): Promise<void> {
const req = ctx.request.body as ICallbackJudgeReq;
const { judgeInfoId, solutionId, judgerId, data, eventTimestampUs } = req;
const redundant = this.lodash.pick(req, ['userId', 'problemId', 'contestId', 'competitionId']);
switch (data.type) {
case 'start': {
await this.service.updateJudgeStart(judgeInfoId, solutionId, judgerId, eventTimestampUs);
await this.service.updateJudgeStart(
judgeInfoId,
solutionId,
judgerId,
eventTimestampUs,
redundant,
);
break;
}
case 'progress': {
@@ -679,6 +689,7 @@ export default class SolutionController {
solutionId,
judgerId,
eventTimestampUs,
redundant,
data.current,
data.total,
);
@@ -690,6 +701,7 @@ export default class SolutionController {
solutionId,
judgerId,
eventTimestampUs,
redundant,
data.resultType,
data.detail,
);
7 changes: 7 additions & 0 deletions src/app/solution/solution.interface.ts
Original file line number Diff line number Diff line change
@@ -226,6 +226,13 @@ export interface IMSolutionJudgeStatus {
total?: number; // 总测试点数量
}

export interface IMSolutionJudgeRedundantData {
userId?: number;
problemId?: number;
contestId?: number;
competitionId?: number;
}

//#region service.getList
export interface IMSolutionServiceGetListOpt {
solutionId?: ISolutionModel['solutionId'];
186 changes: 174 additions & 12 deletions src/app/solution/solution.service.ts
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@ import {
IMSolutionServiceGetLiteSolutionSliceOpt,
IMSolutionServiceGetLiteSolutionSliceRes,
IMSolutionServiceLiteSolution,
IMSolutionJudgeRedundantData,
} from './solution.interface';
import { Op, QueryTypes, fn as sequelizeFn, col as sequelizeCol } from 'sequelize';
import { IUtils } from '@/utils';
@@ -74,6 +75,8 @@ import { ICompetitionModel } from '../competition/competition.interface';
import { IUserModel } from '../user/user.interface';
import { IProblemModel } from '../problem/problem.interface';
import microtime from 'microtime';
import { CCompetitionEventService } from '../competition/competitionEvent.service';
import { ECompetitionEvent } from '../competition/competition.enum';

const httpAgent = new http.Agent({ keepAlive: true });
const axiosSocketBrideInstance = Axios.create({
@@ -160,6 +163,9 @@ export default class SolutionService {
@inject()
competitionService: CCompetitionService;

@inject()
competitionEventService: CCompetitionEventService;

@inject()
utils: IUtils;

@@ -1573,16 +1579,44 @@ export default class SolutionService {
return res === 0;
}

async updateStaleJudgeInfoAsCancelled(judgeInfoId: number) {
if (!judgeInfoId) {
return false;
}
const res = await this.judgeInfoModel.update(
{
result: ESolutionResult.CNL,
},
{
where: {
judgeInfoId,
result: {
[Op.in]: [ESolutionResult.RPD, ESolutionResult.JG],
},
},
},
);
return res[0] > 0;
}

async updateJudgeStart(
judgeInfoId: number,
solutionId: number,
judgerId: string,
eventTimestampUs: number,
redundant: IMSolutionJudgeRedundantData,
): Promise<void> {
if (await this.checkIsJudgeStale(judgeInfoId, solutionId)) {
await this.delSolutionJudgeStatus(judgeInfoId);
this.ctx.logger.info(
`[updateJudgeStart] stale judge (solutionId=${solutionId}, judgeInfoId=${judgeInfoId}), skip`,
);
await Promise.all([
this.updateStaleJudgeInfoAsCancelled(judgeInfoId),
this.delSolutionJudgeStatus(judgeInfoId),
]);
return;
}

await this.model.update(
{
result: ESolutionResult.RPD,
@@ -1605,20 +1639,40 @@ export default class SolutionService {
eventTimestampUs,
});
this.pushJudgeStatus(solutionId, [solutionId, 0, ESolutionResult.JG]);
if (redundant.competitionId) {
this.competitionEventService.event(redundant.competitionId, ECompetitionEvent.JudgeStart, {
detail: {
judgerId,
ts: eventTimestampUs,
},
solutionId,
judgeInfoId,
userId: redundant.userId,
problemId: redundant.problemId,
});
}
}

async updateJudgeProgress(
judgeInfoId: number,
solutionId: number,
judgerId: string,
eventTimestampUs: number,
redundant: IMSolutionJudgeRedundantData,
current: number,
total: number,
): Promise<void> {
if (await this.checkIsJudgeStale(judgeInfoId, solutionId)) {
await this.delSolutionJudgeStatus(judgeInfoId);
this.ctx.logger.info(
`[updateJudgeProgress] stale judge (solutionId=${solutionId}, judgeInfoId=${judgeInfoId}), skip`,
);
await Promise.all([
this.updateStaleJudgeInfoAsCancelled(judgeInfoId),
this.delSolutionJudgeStatus(judgeInfoId),
]);
return;
}

const currentJudgeStatus = await this.getSolutionJudgeStatus(judgeInfoId);
if (currentJudgeStatus && currentJudgeStatus.eventTimestampUs > eventTimestampUs) {
return;
@@ -1632,22 +1686,46 @@ export default class SolutionService {
total,
});
this.pushJudgeStatus(solutionId, [solutionId, 0, ESolutionResult.JG, current, total]);
if (redundant.competitionId) {
this.competitionEventService.event(redundant.competitionId, ECompetitionEvent.JudgeProgress, {
detail: {
current,
total,
judgerId,
ts: eventTimestampUs,
},
solutionId,
judgeInfoId,
userId: redundant.userId,
problemId: redundant.problemId,
});
}
}

async updateJudgeFinish(
judgeInfoId: number,
solutionId: number,
judgerId: string,
eventTimestampUs: number,
redundant: IMSolutionJudgeRedundantData,
resultType: 'CompileError' | 'SystemError' | 'Done',
detail: any,
): Promise<void> {
if (await this.checkIsJudgeStale(judgeInfoId, solutionId)) {
this.ctx.logger.info(
`[updateJudgeFinish] stale judge (solutionId=${solutionId}, judgeInfoId=${judgeInfoId}), skip`,
);
await Promise.all([
this.updateStaleJudgeInfoAsCancelled(judgeInfoId),
this.delSolutionJudgeStatus(judgeInfoId),
]);
return;
}

let result: ESolutionResult = ESolutionResult.SE;
switch (resultType) {
case 'CompileError': {
const result = ESolutionResult.CE;
result = ESolutionResult.CE;
const { compileInfo } = detail;
await Promise.all([
this.updateJudgeInfo(judgeInfoId, {
@@ -1664,11 +1742,30 @@ export default class SolutionService {
this.clearDetailCache(solutionId),
]);
this.pushJudgeStatus(solutionId, [solutionId, 1, result]);
// 推送比赛事件
if (redundant.competitionId) {
this.competitionEventService.event(
redundant.competitionId,
ECompetitionEvent.JudgeFinish,
{
detail: {
resultType,
result,
judgerId,
ts: eventTimestampUs,
},
solutionId,
judgeInfoId,
userId: redundant.userId,
problemId: redundant.problemId,
},
);
}
break;
}
case 'SystemError': {
this.ctx.logger.error('[updateJudgeFinish] SystemError:', detail);
const result = ESolutionResult.SE;
result = ESolutionResult.SE;
await Promise.all([
this.updateJudgeInfo(judgeInfoId, {
result,
@@ -1684,17 +1781,30 @@ export default class SolutionService {
this.clearDetailCache(solutionId),
]);
this.pushJudgeStatus(solutionId, [solutionId, 1, result]);
// 推送比赛事件
if (redundant.competitionId) {
this.competitionEventService.event(
redundant.competitionId,
ECompetitionEvent.JudgeFinish,
{
detail: {
resultType,
result,
judgerId,
ts: eventTimestampUs,
},
solutionId,
judgeInfoId,
userId: redundant.userId,
problemId: redundant.problemId,
},
);
}
break;
}
case 'Done': {
const {
result,
maxTimeUsed,
maxMemoryUsed,
lastCaseNumber,
totalCaseNumber,
cases,
} = detail;
const { maxTimeUsed, maxMemoryUsed, lastCaseNumber, totalCaseNumber, cases } = detail;
result = detail.result;
// 更新评测信息
await Promise.all([
await this.updateJudgeInfo(judgeInfoId, {
@@ -1727,6 +1837,30 @@ export default class SolutionService {
]);
// 推送完成状态
this.pushJudgeStatus(solutionId, [solutionId, 1, result, lastCaseNumber, totalCaseNumber]);
// 推送比赛事件
if (redundant.competitionId) {
this.competitionEventService.event(
redundant.competitionId,
ECompetitionEvent.JudgeFinish,
{
detail: {
resultType,
result,
maxTimeUsed,
maxMemoryUsed,
lastCaseNumber,
totalCaseNumber,
cases,
judgerId,
ts: eventTimestampUs,
},
solutionId,
judgeInfoId,
userId: redundant.userId,
problemId: redundant.problemId,
},
);
}
// 设置异步定时任务来更新计数
this.model
.findOne({
@@ -1753,6 +1887,34 @@ export default class SolutionService {
break;
}
}

if (redundant.competitionId) {
const hasPreviousJudgeInfo =
(await this.judgeInfoModel.count({
where: {
solutionId,
judgeInfoId: {
[Op.lt]: judgeInfoId,
},
result: {
[Op.ne]: ESolutionResult.CNL,
},
},
})) > 0;
const solutionResultEvent = hasPreviousJudgeInfo
? ECompetitionEvent.SolutionResultChange
: ECompetitionEvent.SolutionResultSettle;
this.competitionEventService.event(redundant.competitionId, solutionResultEvent, {
solutionId,
judgeInfoId,
detail: {
resultType,
result,
},
userId: redundant.userId,
problemId: redundant.problemId,
});
}
await this.delSolutionJudgeStatus(judgeInfoId);
}

2 changes: 1 addition & 1 deletion src/common
Loading

0 comments on commit d79711e

Please sign in to comment.