Skip to content
页面信息
📝 描述评测记录模型,提供提交创建、评测任务分发、结果更新和重测
📥 导入import { RecordModel } from 'hydrooj'

RecordModel

评测记录模型,提供提交创建、评测任务分发、结果更新、重测/重置和提交统计。

RecordModel 是一个纯静态类。所有方法直接在类上调用(如 RecordModel.get(...))。


类型导出

RecordDoc

主要记录文档类型。在 packages/hydrooj/src/interface.ts 中定义为基于 RecordPayload(来自 @hydrooj/common)的映射类型:

typescript
type RecordDoc = {
    [K in keyof RecordPayload]: K extends 'hackTarget' | 'contest' ? ObjectId : RecordPayload[K];
} & {
    _id: ObjectId;
    notify?: boolean;
};

RecordPayload(继承 RecordJudgeInfo)中的关键字段:

字段类型说明
domainIdstring记录所属的域
pidnumber题目 ID
uidnumber提交者用户 ID
langstring提交语言
codestring提交的源代码
statusnumber评测状态(来自 STATUS 枚举)
scorenumber总分
timenumber总用时(毫秒)
memorynumber总内存(KB)
rejudgedboolean是否为重测
progressnumber?评测进度百分比
sourcestring?来源标识符
contestObjectId?比赛 ID(或预测试/生成的哨兵值)
inputstring | string[]?预测试输入数据
hackTargetObjectId?Hack 提交的目标记录 ID
filesRecord<string, string>?附加文件
judgeTexts(string | JudgeMessage)[]评测输出消息
compilerTextsstring[]编译器输出消息
testCasesRequired<TestCase>[]每个测试点的结果
judgernumber评测者用户 ID
judgeAtDate评测时间戳
subtasksRecord<number, SubtaskResult>?子任务结果

RecordStatDoc

存储在 record.stat 集合中的统计文档,用于唯一提交/通过提交跟踪:

字段类型说明
_idObjectId与记录的 _id 相同
domainIdstring域 ID
pidnumber题目 ID
uidnumber用户 ID
timenumber用时
memorynumber内存
lengthnumber代码长度
langstring语言

RecordHistoryDoc

记录重置重测时归档到 record.history 中的评测结果:

字段类型说明
_idObjectId历史条目 ID
ridObjectId原始记录 ID
(继承自 RecordJudgeInfo)scoretimememorystatusjudgeTextscompilerTextstestCasessubtasksjudgerjudgeAt

JudgeMeta

传递给评测任务的元数据:

字段类型说明
problemOwnernumber题目拥有者 UID
hackRejudge?stringHack 重测标识符
rejudge?boolean | 'controlled'重测模式
type?string评测类型提示

属性

属性类型说明
collCollection<RecordDoc>MongoDB 集合 record
collStatCollection<RecordStatDoc>MongoDB 集合 record.stat
collHistoryCollection<RecordHistoryDoc>MongoDB 集合 record.history
PROJECTION_LIST(keyof RecordDoc)[]列表视图中包含的字段(17 个字段)
STAT_QUERYobject统计查询的排序方式,每个键映射为 [降序排序, 升序排序] 二元组(timememorylengthdate
RECORD_PRETESTObjectId预测试记录的哨兵 ID(000...000
RECORD_GENERATEObjectId生成记录的哨兵 ID(000...001

方法

查找

get(_id: ObjectId): Promise<RecordDoc | null>

通过 ObjectId 获取单条记录。

参数类型默认值说明
_idObjectId记录 ID
返回值Promise<RecordDoc | null>

get(domainId: string, _id: ObjectId): Promise<RecordDoc | null>

通过 domainId 和 ObjectId 获取单条记录。若记录的 domainId 不匹配则返回 null

参数类型默认值说明
domainIdstring域 ID
_idObjectId记录 ID
返回值Promise<RecordDoc | null>

getMulti(domainId: string, query: any, options?: FindOptions): Cursor<RecordDoc>

查询多条记录。自动按 domainId 限定范围。返回 MongoDB 游标。

参数类型默认值说明
domainIdstring域 ID
queryanyMongoDB 查询过滤器
optionsFindOptions查询选项(排序、投影等)
返回值Cursor<RecordDoc>记录游标

getMultiStat(domainId: string, query: any, sortBy?: any): Cursor<RecordStatDoc>

查询多条统计文档。默认按 _id 降序排序。

参数类型默认值说明
domainIdstring域 ID
queryanyMongoDB 查询过滤器
sortByany排序方式
返回值Cursor<RecordStatDoc>统计文档游标

getList(domainId: string, rids: ObjectId[], fields?: (keyof RecordDoc)[]): Promise<Record<string, Partial<RecordDoc>>>

通过 ID 数组获取记录,返回以十六进制字符串 _id 为键的映射。对输入 ID 去重。可选择投影特定字段。

参数类型默认值说明
domainIdstring域 ID
ridsObjectId[]记录 ID 数组
fields(keyof RecordDoc)[]投影字段列表
返回值Promise<Record<string, Partial<RecordDoc>>>以十六进制 ID 为键的记录映射

count(domainId: string, query: any): Promise<number>

统计匹配查询的记录数,按 domainId 限定范围。

参数类型默认值说明
domainIdstring域 ID
queryanyMongoDB 查询过滤器
返回值Promise<number>匹配数量

统计

stat(domainId?: string): Promise<{ d5min, d1h, day, week, month, year, total }>

获取各时间窗口的提交计数:5 分钟、1 小时、1 天、1 周、1 月、1 年和总计。可按域限定范围。

使用 @ArgMethod 装饰器修饰。

参数类型默认值说明
domainIdstring域 ID(省略则统计全部)
返回值Promise<{ d5min, d1h, day, week, month, year, total }>各时间窗口计数

提交与评测

add(domainId: string, pid: number, uid: number, lang: string, code: string, addTask: boolean, args?: any): Promise<ObjectId>

创建新的评测记录。插入到 coll 中并可选分发评测任务。

参数类型默认值说明
domainIdstring域 ID
pidnumber题目 ID
uidnumber提交者 UID
langstring语言标识符
codestring源代码
addTaskboolean是否立即分发评测任务
args.type'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate''judge'提交类型
args.contestObjectId?比赛 ID
args.inputstring[]?预测试输入数据
args.filesRecord<string, string>?附加文件
args.hackTargetObjectId?Hack 的目标记录
args.notifyboolean?评测完成时是否发送通知
返回值Promise<ObjectId>插入记录的 ObjectId
typescript
// 标准提交
const rid = await RecordModel.add(
  'system',
  1001,
  session.uid,
  'cpp',
  '#include <bits/stdc++.h>\nint main() {}',
  true,
);

// 预测试提交
const pretestRid = await RecordModel.add(
  'system',
  1001,
  session.uid,
  'cpp',
  code,
  true,
  { type: 'pretest', input: ['1 2\n', '3 4\n'] },
);

// Hack 提交
const hackRid = await RecordModel.add(
  'system',
  1001,
  session.uid,
  'cpp',
  hackInput,
  true,
  { type: 'hack', hackTarget: targetRid, contest: tid },
);

judge(domainId: string, rids: MaybeArray<ObjectId> | RecordDoc, priority?: number, config?: ProblemConfigFile, meta?: Partial<JudgeMeta>): Promise<any>

提交一条或多条记录进行评测。解析题目(跟随引用),删除这些记录的已有任务,并创建新的评测任务。

参数类型默认值说明
domainIdstring域 ID
ridsMaybeArray<ObjectId> | RecordDoc记录 ID 或文档
prioritynumber0任务优先级
configProblemConfigFile{}覆盖评测配置
metaPartial<JudgeMeta>{}评测元数据
返回值Promise<any>
typescript
// 提交单条记录评测
await RecordModel.judge('system', rid);

// 重测多条记录(带自定义优先级和元数据)
await RecordModel.judge(
  'system',
  [rid1, rid2, rid3],
  1,
  {},
  { rejudge: true, problemOwner: uid },
);

// 使用自定义评测配置重测
await RecordModel.judge(
  'system',
  rid,
  0,
  { timeLimit: 5000, memoryLimit: 512 },
  { type: 'rejudge' },
);

submissionPriority(uid: number, base?: number): Promise<number>

计算用户的动态提交优先级。根据近期提交量和待处理任务降低优先级。用于限制高频提交者。

参数类型默认值说明
uidnumber用户 ID
basenumber基础优先级
返回值Promise<number>计算后的优先级

更新

update(domainId: string, _id: ObjectId | ObjectId[], $set?: any, $push?: any, $unset?: any, $inc?: any): Promise<RecordDoc | null>

更新单条或多条记录。接受 MongoDB 更新操作符($set$push$unset$inc)。当 _id 为数组时执行 updateMany 并返回 null;否则返回更新后的文档。

参数类型默认值说明
domainIdstring域 ID
_idObjectId | ObjectId[]记录 ID 或 ID 数组(数组时批量更新)
$setany要设置的字段
$pushany要追加的字段
$unsetany要删除的字段
$incany要增减的字段
返回值Promise<RecordDoc | null>更新后的文档(批量更新返回 null

updateMulti(domainId: string, $match: any, $set?: any, $push?: any, $unset?: any): Promise<number>

更新匹配过滤器的多条记录。返回修改的文档数量。

参数类型默认值说明
domainIdstring域 ID
$matchanyMongoDB 匹配过滤器
$setany要设置的字段
$pushany要追加的字段
$unsetany要删除的字段
返回值Promise<number>修改的文档数量

reset(domainId: string, rid: ObjectId | ObjectId[], isRejudge: boolean): Promise<RecordDoc | null>

重置一条或多条记录以进行重测。将当前评测结果归档到 record.history,将所有评测字段清除为默认值,并删除关联的统计条目和任务。

参数类型默认值说明
domainIdstring域 ID
ridObjectId | ObjectId[]记录 ID 或 ID 数组
isRejudgeboolean是否为重测(true 时标记 rejudged
返回值Promise<RecordDoc | null>重置后的文档(批量重置返回 null
typescript
// 重测单条记录
await RecordModel.reset('system', rid, true);
// 记录状态清除,旧结果归档到 record.history,随后可调用 judge 重新评测
await RecordModel.judge('system', rid, 0, {}, { rejudge: true });

// 批量重测某题目的所有记录
const rids = await RecordModel.getMulti('system', { pid: 1001 }).map(r => r._id).toArray();
await RecordModel.reset('system', rids, true);
await RecordModel.judge('system', rids);

事件

事件参数说明
record/changeRecordDoc创建新记录时广播(通过 add()
record/judgerdoc: RecordDoc, updated: boolean评测完成时触发;对通过的提交更新 record.stat 并发送通知