M5(3/3): trigger —— 三类触发器与起单幂等

- matchesEvent:事件触发的命中判定(S-FEEL 规则;规则写坏按不命中处置,不让事件流停摆)
- assembleInput:字段映射装配;缺必填即起单失败入死信,不静默丢弃
- 幂等:同一事件对同一触发器至多起一单;数据日历型同一行同一日期只触发一次
- 周期两型:日历表达式到点扫描 + 数据日历型(日期字段 + 偏移 + 过滤)逐行起单
- 登记触发:人工起单的统一形态,登记人即来源
- 触发器与流程包平级:配置独立存放,增删启停不改流程包版本

340 个测试通过,门禁五道全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
JCAI Main Agent
2026-07-27 02:03:58 +08:00
co-authored by Claude Opus 5
parent a8f6eef610
commit 4ad901c452
16 changed files with 3768 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
/**
* module: trigger —— 对外类型(编码规范 §10.1;语义出处 OS 3.1)。
*
* 触发器三类,**独立配置存放**,判定与调用接口调起。
* 触发器与流程包平级:增删启停不改流程包的任何版本。
*/
import type { TriggerConfig, TriggerKind } from '@contracts';
import type { AssemblyResult, EventScope } from './internal/domain/matching.ts';
export type { AssemblyResult, EventScope, TriggerConfig, TriggerKind };
export declare class TriggerInputMissingError extends Error {
readonly triggerId: string;
readonly fields: readonly string[];
constructor(triggerId: string, fields: readonly string[]);
}
export type FireOutcome = {
readonly triggerId: string;
readonly workflowId?: string;
readonly skipped?: 'duplicate' | 'disabled' | 'input-missing';
readonly missing?: readonly string[];
};
export type TriggerService = {
list(): Promise<TriggerConfig[]>;
upsert(config: TriggerConfig): Promise<TriggerConfig>;
setEnabled(id: string, enabled: boolean): Promise<void>;
remove(id: string): Promise<void>;
/** 事件到达:命中判定 —— 供 `event` 的 `TriggerMatcher` 端口使用 */
match(scope: EventScope): Promise<string[]>;
/** 命中即起单;同一事件对同一触发器**至多起一单**(事件 ID 为幂等键) */
fireByEvent(triggerId: string, eventId: string, scope: EventScope): Promise<FireOutcome>;
/** 登记触发:人工起单的统一形态 —— 登记人即来源 */
fireByRegistration(triggerId: string, by: string, data: Record<string, unknown>): Promise<FireOutcome>;
/** 周期触发的到点扫描(由 pg-boss 定时调起) */
sweepPeriodic(now: Date): Promise<FireOutcome[]>;
};