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

EventMap (事件总线)

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

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

ts
import type { EventMap } from 'hydrooj';

注:EventsEventMap 的类型别名(context.tsexport { EventMap as Events }),并未从 hydrooj 导出,仅通过 declare module 'cordis' 的声明合并作为 cordis 的类型可用(如 import type { Events } from 'cordis')。


使用模式

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, privileged: boolean) => VoidReturnWebSocket 订阅初始化时触发。
subscription/subscribe(channel: string, user: User, metadata: Record<string, string>) => VoidReturn用户订阅某频道时触发。
subscription/enable(channel: string, h: ConnectionHandler, 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, messages: string[]) => VoidReturn用户导入解析阶段触发,可通过 messages 追加返回给前端的消息。
user/import/create(uid: number, udoc: any) => VoidReturn用户导入创建阶段触发。

Auth

EventSignatureDescription
auth/before-login(ctx: Handler, udoc: User) => VoidReturn用户登录认证前触发,可拦截或修改登录流程。
auth/login(ctx: Handler, udoc: User) => VoidReturn用户登录成功后触发。

Domain

EventSignatureDescription
domain/create(ddoc: DomainDoc) => VoidReturn域创建时触发(DB 插入前)。
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 | ConnectionHandler, args: any, data: any) => VoidReturn操作日志记录时触发。

内部广播机制

Hydro 使用三种广播路径(bus.ts 中的 apply 函数 + task.ts 中的事件转发):

  1. PM2 模式 — 集群部署时,通过 PM2 的 launchBus + BSON 序列化在进程间传递事件(bus.ts:106-118)。
  2. MongoDB 总线 — 广播事件写入 event 集合(带 ack 去重与 expire 过期),实例 0 通过 collEvent.watch() 变更流转发,无副本集时退化为轮询兜底(task.ts:142-190)。
  3. 单进程本地直调 — 非集群模式下,bus/broadcast 事件直接在本进程内调用 app.parallel(event, ...payload),仅本进程生效(bus.ts:119-122)。

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