TrainingModel
训练计划(课程)模型,用于管理 DAG 结构的训练计划、用户注册和进度跟踪。
与 TaskModel 不同,TrainingModel 是一个纯模块,导出函数而非类。所有函数直接调用(如 TrainingModel.add(...))。
类型导出
TrainingNode
interface TrainingNode {
_id: number;
title: string;
requireNids: number[];
pids: number[];
}TrainingDoc
interface TrainingDoc extends Omit<Tdoc, 'docType'> {
docType: 40; // document.TYPE_TRAINING
description: string;
pin?: number;
dag: TrainingNode[];
}引用自 packages/hydrooj/src/interface.ts。
方法
增删改查
add(domainId: string, title: string, content: string, owner: number, dag?: TrainingNode[], description?: string, pin?: number): Promise<ObjectId>
创建一条新训练计划。默认值:dag=[]、description=''、pin=0。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
title | string | — | 训练计划标题 |
content | string | — | 训练计划内容 |
owner | number | — | 创建者 UID |
dag | TrainingNode[] | [] | DAG 节点数组 |
description | string | '' | 描述文本 |
pin | number | 0 | 置顶权重 |
| 返回值 | Promise<ObjectId> | 新训练计划的 ObjectId |
edit(domainId: string, tid: ObjectId, $set: Partial<TrainingDoc>): Promise<TrainingDoc>
更新匹配给定部分文档的训练计划字段。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
tid | ObjectId | — | 训练计划 ID |
$set | Partial<TrainingDoc> | — | 要更新的字段 |
| 返回值 | Promise<TrainingDoc> | 更新后的训练计划文档 |
del(domainId: string, tid: ObjectId): Promise<[void, void]>
删除训练计划及其所有关联的用户状态。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
tid | ObjectId | — | 训练计划 ID |
| 返回值 | Promise<[void, void]> |
get(domainId: string, tid: ObjectId): Promise<TrainingDoc>
通过 ID 获取单条训练计划。未找到则抛出 TrainingNotFoundError。同时将 DAG 中的 pids 值规范化为整数。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
tid | ObjectId | — | 训练计划 ID |
| 返回值 | Promise<TrainingDoc> |
getMulti(domainId: string, query?: Filter<TrainingDoc>): FindCursor<TrainingDoc>
返回匹配查询的训练计划游标,按 pin 降序然后 _id 降序排列。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
query | Filter<TrainingDoc> | — | 查询条件 |
| 返回值 | FindCursor<TrainingDoc> |
getList(domainId: string, tids: ObjectId[]): Promise<Record<string, TrainingDoc>>
通过 ID 获取多条训练计划,返回以 docId.toString() 为键的映射。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
tids | ObjectId[] | — | 训练计划 ID 数组 |
| 返回值 | Promise<Record<string, TrainingDoc>> |
count(domainId: string, query: Filter<TrainingDoc>): Promise<number>
返回匹配给定查询的训练计划数量。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
query | Filter<TrainingDoc> | — | 查询条件 |
| 返回值 | Promise<number> |
注册与状态
enroll(domainId: string, tid: ObjectId, uid: number): Promise<any>
将用户注册到训练计划中。若已注册则抛出 TrainingAlreadyEnrollError。递增训练计划的 attend 计数器。返回 document.inc 的结果。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
tid | ObjectId | — | 训练计划 ID |
uid | number | — | 用户 UID |
| 返回值 | Promise<any> | document.inc 的返回值 |
// 注册用户到训练计划
const newCount = await TrainingModel.enroll(
'system', // domainId
trainingId, // tid
12345, // uid
);
// 若用户已注册,抛出 TrainingAlreadyEnrollErrorgetStatus(domainId: string, tid: ObjectId, uid: number): Promise<TrainingStatusDoc | null>
获取指定用户在特定训练计划中的状态。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
tid | ObjectId | — | 训练计划 ID |
uid | number | — | 用户 UID |
| 返回值 | Promise<TrainingStatusDoc | null> |
getMultiStatus(domainId: string, query: Filter<TrainingDoc>): FindCursor<TrainingStatusDoc>
返回匹配给定查询的训练计划状态游标。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
query | Filter<TrainingDoc> | — | 查询条件 |
| 返回值 | FindCursor<TrainingStatusDoc> |
getListStatus(domainId: string, uid: number, tids: ObjectId[]): Promise<Record<string, TrainingStatusDoc>>
获取指定用户在多条训练计划中的状态,返回以 docId 为键的映射。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
uid | number | — | 用户 UID |
tids | ObjectId[] | — | 训练计划 ID 数组 |
| 返回值 | Promise<Record<string, TrainingStatusDoc>> |
setStatus(domainId: string, tid: ObjectId, uid: number, $set: any): Promise<void>
设置(覆盖)用户在特定训练计划中的状态字段。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
domainId | string | — | 域上下文 |
tid | ObjectId | — | 训练计划 ID |
uid | number | — | 用户 UID |
$set | any | — | 要设置的状态字段 |
| 返回值 | Promise<void> |
DAG 辅助函数
用于评估训练计划 DAG 中节点完成状态的工具函数。
getPids(dag: TrainingNode[]): number[]
提取 DAG 中所有节点的唯一父 ID(pids)集合。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dag | TrainingNode[] | — | DAG 节点数组 |
| 返回值 | number[] | 唯一 pids 集合 |
isDone(node: TrainingNode, doneNids: Set<number>, donePids: Set<number>): boolean
若节点的必需节点 ID 和父题目 ID 均已满足,返回 true。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
node | TrainingNode | — | DAG 节点 |
doneNids | Set<number> | — | 已完成的节点 ID 集合 |
donePids | Set<number> | — | 已完成的题目 ID 集合 |
| 返回值 | boolean |
isProgress(node: TrainingNode, doneNids: Set<number>, donePids: Set<number>, progPids: Set<number>): boolean
若节点的要求已满足但部分父题目未完成,且至少一个父题目已完成或正在进行中,返回 true。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
node | TrainingNode | — | DAG 节点 |
doneNids | Set<number> | — | 已完成的节点 ID 集合 |
donePids | Set<number> | — | 已完成的题目 ID 集合 |
progPids | Set<number> | — | 进行中的题目 ID 集合 |
| 返回值 | boolean |
isOpen(node: TrainingNode, doneNids: Set<number>, donePids: Set<number>, progPids: Set<number>): boolean
若节点的要求已满足,且无父题目已完成或正在进行中,返回 true。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
node | TrainingNode | — | DAG 节点 |
doneNids | Set<number> | — | 已完成的节点 ID 集合 |
donePids | Set<number> | — | 已完成的题目 ID 集合 |
progPids | Set<number> | — | 进行中的题目 ID 集合 |
| 返回值 | boolean |
isInvalid(node: TrainingNode, doneNids: Set<number>): boolean
若节点的必需节点 ID 未全部满足(即前置条件未达成),返回 true。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
node | TrainingNode | — | DAG 节点 |
doneNids | Set<number> | — | 已完成的节点 ID 集合 |
| 返回值 | boolean |
备注
- 训练计划是一种文档类型模型(
TYPE_TRAINING = 40)。增删改查和状态操作委托给共享的document模块。 enroll是原子操作——使用setIfNotStatus防止重复注册,冲突时抛出TrainingAlreadyEnrollError。get会规范化 DAG 的pids值(将数字字符串解析为整数)以保持向后兼容。- DAG 辅助函数(
isDone、isProgress、isOpen、isInvalid)接受Set<number>和number[]——内部会自动转换为Set。