Skip to content
页面信息
📝 描述Hydro 插件系统的事件总线,所有可监听事件定义在 EventMap 接口中

EventMap (事件总线)

Hydro 插件系统的事件总线。所有可监听事件定义在 EventMap 接口中,通过 cordis 框架的事件机制分发。

Source: packages/hydrooj/src/service/bus.ts

ts
import { Events } from 'hydrooj'; // re-exported as Events
import type { EventMap } from 'hydrooj'; // original type name

使用模式

ctx.on() — 订阅事件

注册事件监听器,返回一个 Disposable 函数,调用即可取消订阅。

ts
const dispose = ctx.on('problem/add', (doc, docId) => {
    console.log('New problem added:', docId);
});
// 取消订阅
dispose();

ctx.emit() — 同步触发

同步触发事件,所有监听器按注册顺序执行。

ts
ctx.emit('record/change', rdoc, $set, $push, body);

ctx.parallel() — 并发触发

触发事件,所有监听器并发执行,返回 Promise 在全部完成后 resolve。

ts
await ctx.parallel('app/ready');

ctx.broadcast() — 跨进程广播

跨集群广播事件(PM2 或 MongoDB 总线)。每个节点收到后以 parallel 方式执行。

ts
ctx.broadcast('record/judge', rdoc, updated, pdoc, updater);

App Lifecycle

EventSignatureDescription
app/listen() => voidHTTP 服务器开始监听端口时触发。
app/started() => void应用启动完成时触发。
app/ready() => VoidReturn所有插件加载完毕、应用就绪时触发。用于异步初始化。
app/exit() => VoidReturn应用即将关闭时触发。用于清理资源。
app/before-reload(entries: Set<string>) => VoidReturn热重载发生前触发,entries 为即将重载的插件路径集合。
app/reload(entries: Set<string>) => VoidReturn热重载完成后触发。

File Watch

EventSignatureDescription
app/watch/change(path: string) => VoidReturn监视的文件内容发生变更时触发。
app/watch/unlink(path: string) => VoidReturn监视的文件被删除时触发。

Database

EventSignatureDescription
database/connect(db: Db) => void数据库连接建立后触发,传入 Db 实例。
database/config() => VoidReturn数据库配置加载时触发。

System

EventSignatureDescription
system/setting(args: Record<string, any>) => VoidReturn系统设置变更时触发。
system/setting-loaded() => VoidReturn系统设置加载完毕时触发。
bus/broadcast(event: keyof EventMap, payload: any, trace?: string) => VoidReturn跨进程广播消息到达时触发。一般不直接监听,由 ctx.broadcast() 内部使用。
monitor/update(type: 'server' | 'judge', $set: any) => VoidReturn监控数据更新时触发。
monitor/collect(info: any) => VoidReturn收集监控信息时触发。
api/update() => voidAPI 路由更新时触发。

Scheduled Task

EventSignatureDescription
task/daily() => void每日定时任务触发。
task/daily/finish(pref: Record<string, number>) => void每日任务完成后触发,传入执行偏好统计。

Subscription (WebSocket)

EventSignatureDescription
subscription/init(h: ConnectionHandler<Context>, privileged: boolean) => VoidReturnWebSocket 订阅初始化时触发。
subscription/subscribe(channel: string, user: User, metadata: Record<string, string>) => VoidReturn用户订阅某频道时触发。
subscription/enable(channel: string, h: ConnectionHandler<Context>, privileged: boolean, onDispose: (disposable: () => void) => void) => VoidReturn订阅频道激活时触发;通过 onDispose 注册清理回调。

User

EventSignatureDescription
user/message(uid: number[], mdoc: Omit<MessageDoc, 'to'>) => void用户收到消息时触发。
user/get(udoc: User) => void用户信息被查询时触发。
user/delcache(content: string | true) => void用户缓存失效时触发。
user/import/parse(payload: any) => VoidReturn用户导入解析阶段触发。
user/import/create(uid: number, udoc: any) => VoidReturn用户导入创建阶段触发。

Domain

EventSignatureDescription
domain/create(ddoc: DomainDoc) => VoidReturn域创建后触发。
domain/before-get(query: Filter<DomainDoc>) => VoidReturn域查询前触发,可修改查询条件。
domain/get(ddoc: DomainDoc) => VoidReturn域查询后触发。
domain/before-update(domainId: string, $set: Partial<DomainDoc>) => VoidReturn域更新前触发,可拦截或修改更新内容。
domain/update(domainId: string, $set: Partial<DomainDoc>, ddoc: DomainDoc) => VoidReturn域更新后触发。
domain/delete(domainId: string) => VoidReturn域删除后触发。
domain/delete-cache(domainId: string) => VoidReturn域缓存失效时触发。

Document

EventSignatureDescription
document/add(doc: any) => VoidReturn文档创建后触发。
document/set<T extends keyof DocType>(domainId: string, docType: T, docId: DocType[T], $set: any, $unset: OnlyFieldsOfType<DocType[T], any, true | '' | 1>) => VoidReturn文档更新时触发。泛型参数 T 对应文档类型键。

Discussion

EventSignatureDescription
discussion/before-add(payload: Partial<DiscussionDoc>) => VoidReturn讨论创建前触发,可修改内容。
discussion/add(payload: Partial<DiscussionDoc>) => VoidReturn讨论创建后触发。

Problem

EventSignatureDescription
problem/before-add(domainId: string, content: string, owner: number, docId: number, doc: Partial<ProblemDoc>) => VoidReturn题目创建前触发。
problem/add(doc: Partial<ProblemDoc>, docId: number) => VoidReturn题目创建后触发。
problem/before-edit(doc: Partial<ProblemDoc>, $unset: OnlyFieldsOfType<ProblemDoc, any, true | '' | 1>) => VoidReturn题目编辑前触发。
problem/edit(doc: ProblemDoc) => VoidReturn题目编辑后触发。
problem/before-del(domainId: string, docId: number) => VoidReturn题目删除前触发。
problem/list(query: Filter<ProblemDoc>, handler: any, sort?: string[]) => VoidReturn题目列表查询时触发。
problem/get(doc: ProblemDoc, handler: any) => VoidReturn题目详情查询后触发。
problem/delete(domainId: string, docId: number) => VoidReturn题目删除后触发。
problem/addTestdata(domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => VoidReturn测试数据文件添加后触发。
problem/renameTestdata(domainId: string, docId: number, name: string, newName: string) => VoidReturn测试数据文件重命名后触发。
problem/delTestdata(domainId: string, docId: number, name: string[]) => VoidReturn测试数据文件删除后触发。
problem/addAdditionalFile(domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => VoidReturn附加文件添加后触发。
problem/renameAdditionalFile(domainId: string, docId: number, name: string, newName: string) => VoidReturn附加文件重命名后触发。
problem/delAdditionalFile(domainId: string, docId: number, name: string[]) => VoidReturn附加文件删除后触发。

Contest

EventSignatureDescription
contest/before-add(payload: Partial<Tdoc>) => VoidReturn比赛创建前触发。
contest/add(payload: Partial<Tdoc>, id: ObjectId) => VoidReturn比赛创建后触发。
contest/before-edit(tdoc: Tdoc, $set: Partial<Tdoc>) => VoidReturn比赛编辑前触发。
contest/edit(payload: Tdoc) => VoidReturn比赛编辑后触发。
contest/list(query: Filter<Tdoc>, handler: any) => VoidReturn比赛列表查询时触发。
contest/scoreboard(tdoc: Tdoc, rows: ScoreboardRow[], udict: BaseUserDict, pdict: ProblemDict) => VoidReturn比赛排行榜计算时触发,可修改排名结果。
contest/balloon(domainId: string, tid: ObjectId, bdoc: ContestBalloonDoc) => VoidReturn比赛气球(AC 提示)事件触发。
contest/del(domainId: string, tid: ObjectId) => VoidReturn比赛删除后触发。

Training

EventSignatureDescription
training/list(query: Filter<TrainingDoc>, handler: any) => VoidReturn训练计划列表查询时触发。
training/get(tdoc: TrainingDoc, handler: any) => VoidReturn训练计划详情查询后触发。

Record

EventSignatureDescription
record/change(rdoc: RecordDoc, $set?: any, $push?: any, body?: any) => void评测记录状态变更时同步触发。
record/judge(rdoc: RecordDoc, updated: boolean, pdoc?: ProblemDoc, updater?: any) => VoidReturn评测记录进入判题阶段时并发触发。

OpLog

EventSignatureDescription
oplog/log(type: string, handler: Handler<Context> | ConnectionHandler<Context>, args: any, data: any) => VoidReturn操作日志记录时触发。

内部广播机制

Hydro 使用两种跨进程广播实现(bus.ts 中的 apply 函数):

  1. PM2 模式 — 集群部署时,通过 PM2 的 launchBus + BSON 序列化在进程间传递事件。
  2. 进程内直调模式 — 单进程或无 PM2 时,bus/broadcast 事件直接在本进程内调用 app.parallel(event, ...payload)

插件开发者无需关心底层实现,统一使用 ctx.broadcast() 即可。