This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-MVP/api-report/apps/api/src/modules/trigger/port.d.ts
T
ChaiHaoandClaude Opus 5 f9b2c07731 三面拆分⑬:触发确认链路 + 运营中心「工作流」区 —— 设计只管设计,运营管细节
**触发确认链路**(用户口述五步的第 4/5 步):
  接收邮件 → 分类识别 → 命中 RFQ 事件 → ④(可选,运营勾)系统消息到指定人
  工作台(带邮件内容)→ ⑤ 工作台点「确认起单」,流程才起。
  · 命中不等于必起:勾了「需人工确认」,装配好的入参存成待确认单
    (sys.trigger_pending,migration 0019),ConfirmDesk 端口把带来件内容的
    系统消息送指定人(eventRef 挂票号,消息列表就地贴确认状态与那枚钮)。
  · 先认领后起单:consumed_at 空才认得到 —— 两人同时点、一人点两次,都只起一条流;
    起单失败退回认领,不留死票。确认人记进 source.by,留痕可查。
  · 登记触发不走确认(登记那一下本身就是人在确认);勾了没指定人 = 配置错误入死信。
  · 4 条单测 + 全链实测:投信 → triggersHit=1 且零直起 → AI 工作台收到确认消息
    → 确认起单 → 二次确认幂等回同一流号 → source.by = AI。

**运营中心重排**(区名即运行时:工作流是流程包的运行时,任务是活动的运行时):
  · 新「工作流」区:在途看板(从任务区迁入)+ 运行配置 —— 每包的触发器
    起单开关 / 确认打勾 / 确认人,和节点负责人(sys.node_owner)。
  · 负责人三层:显式指派 > 运营配置 > 轮值 —— 合并点在 process.start,
    复用既有派发链一行不改。
  · 任务区瘦身成活动运行时(任务/审批/需处置);知识库区改名「数据」
    (与设计中心的知识库解开同名);系统区只剩通道实例。
  · 设计中心触发器详情页摘掉启停按钮 —— 设计只管设计,启停/确认全在运营。
  · 旧深链有改嫁:tasks/board → runs/board,tasks/trg → runs/conf。

reset-runs 补 sys.trigger_pending(待确认单是运行痕 —— 和上回漏 trace_span 同类洞)。
邮件分类 Agent(第 ② 步)现由抽键正则 + S-FEEL 代位,升级路径记进 debt.md。
产品设计.md 运营章、trigger/process 两份 CONTRACT.md 同步。

五闸绿;契约快照随本提交入库。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 13:09:16 +08:00

91 lines
3.2 KiB
TypeScript

/**
* module: trigger —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
*/
import type { TriggerConfig } from '@contracts';
import type { PendingFire } from './contract.ts';
export type Clock = {
now(): Date;
};
export type IdGenerator = {
next(): string;
};
export type TriggerStore = {
list(): Promise<TriggerConfig[]>;
get(id: string): Promise<TriggerConfig | undefined>;
upsert(config: TriggerConfig): Promise<TriggerConfig>;
setEnabled(id: string, enabled: boolean): Promise<void>;
/** 运营口:确认配置 —— 设计面的 upsert 不碰这两列 */
setConfirm(id: string, confirm: {
required: boolean;
staffId?: string;
}): Promise<void>;
remove(id: string): Promise<void>;
/** 幂等:同一键至多起一单 */
claim(key: string): Promise<boolean>;
lastFiredAt(id: string): Promise<Date | undefined>;
markFired(id: string, at: Date): Promise<void>;
createPending(pending: PendingFire): Promise<void>;
getPending(id: string): Promise<PendingFire | undefined>;
pendingByIds(ids: readonly string[]): Promise<PendingFire[]>;
/** 认领:consumed_at 空才认得到 —— 两个人同时点确认,只有一个赢,绝不起两条流 */
claimPending(id: string, at: Date): Promise<boolean>;
/** 起单成功后把流号写回;起单失败时用 releasePending 把认领退回去 */
setPendingWorkflow(id: string, workflowId: string): Promise<void>;
releasePending(id: string): Promise<void>;
};
/**
* 确认台 —— app 注入 `message`(L2):命中但要人点头时,
* 把带来件内容的系统消息送到指定人的工作台。
*/
export type ConfirmDesk = {
request(input: {
pendingId: string;
staffId: string;
triggerName: string;
subject: string;
preview: Readonly<Record<string, string>>;
}): Promise<void>;
};
/** 起单 —— app 注入 `process`(L7 同层?否:trigger 在 process 之上同层,经 app 装配) */
export type ProcessGateway = {
start(input: {
packageId: string;
subject: string;
source: {
kind: 'periodic' | 'event' | 'registration';
ref: string;
by?: string;
};
data: Record<string, unknown>;
}): Promise<{
id: string;
}>;
/** 调用接口的必填字段 —— 装配缺项即起单失败 */
requiredInputs(packageId: string): Promise<string[]>;
};
export type Sfeel = {
condition(expression: string, scope: Readonly<Record<string, unknown>>): boolean;
expression(expression: string, scope: Readonly<Record<string, unknown>>): unknown;
};
/** 死信 —— app 注入 `message`(L2):起单失败入死信 */
export type DeadLetterSink = {
report(input: {
triggerId: string;
reason: string;
detail: Record<string, unknown>;
}): Promise<void>;
};
/** 数据日历型要读台账 —— app 注入 `ledger`(L1) */
export type LedgerScanner = {
scan(input: {
domain: string;
object: string;
dateField: string;
filter?: string;
}): Promise<Array<{
key: Record<string, string | number>;
date: string;
row: Record<string, unknown>;
}>>;
};