forked from ChaiHao/BPOS-MVP
- routing domain:选边(XOR 恰取一条 + 默认分支兜底;AND 全走)、边映射装配(四类来源 + S-FEEL 转换)、AND 汇合回齐(容忍分支不阻汇合)、审批点驳回走向声明式 - process-service:起单 → 调度节点 → 负责人四策略解析(结果与依据留痕)→ 活动起单 / 审批点卡工位 / 终点收束按来源回执;任务终态到达继续流转或走失败走向四选一; 审批驳回按声明回指定节点重开新实例;状态每次变动后重算聚合投影 - adapter:流程包(id × version)、工作流实例、节点实例三表持久 —— 崩溃后可续跑 ADR 待补项登记为 debt M5-1:§6.1 的 Mastra workflow 编译器作为可替换 adapter 后补, 当前由自持确定性状态机承担,语义等价、usecase 不动 325 个测试通过,门禁五道全绿 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
106 lines
3.6 KiB
TypeScript
106 lines
3.6 KiB
TypeScript
/**
|
|
* module: process —— 对外类型(编码规范 §10.1;语义出处 OS 03)。
|
|
*
|
|
* 流程层:流程包定义与版本、**边映射闭合校验**、工作流实例
|
|
* (网关 / 审批点 / 失败走向 / 负责人四策略 / 聚合)。
|
|
*
|
|
* M5 进行中:domain 已落(闭合校验 / 四策略解析 / 状态聚合),
|
|
* 用例与 Mastra 工作流编译随后(编码规范 §6.1)。
|
|
*/
|
|
import type {
|
|
ClosureReport,
|
|
ClosureViolation,
|
|
OwnerSpec,
|
|
ProcessNode,
|
|
ProcessPackage,
|
|
TerminalState,
|
|
WorkflowState,
|
|
} from '@contracts';
|
|
import type { ActivityContractFace, ClosureInput } from './internal/domain/closure.ts';
|
|
import type { Candidate, NodeInstance, ResolveContext, Resolution } from './internal/domain/owner-resolution.ts';
|
|
import type { AssembleScope, Dispatch, RejectRoute } from './internal/domain/routing.ts';
|
|
import type { WorkflowRecord } from './port.ts';
|
|
|
|
export type {
|
|
ActivityContractFace,
|
|
AssembleScope,
|
|
Candidate,
|
|
ClosureInput,
|
|
Dispatch,
|
|
RejectRoute,
|
|
WorkflowRecord,
|
|
ClosureReport,
|
|
ClosureViolation,
|
|
NodeInstance,
|
|
OwnerSpec,
|
|
ProcessPackage,
|
|
ProcessNode,
|
|
ResolveContext,
|
|
Resolution,
|
|
TerminalState,
|
|
WorkflowState,
|
|
};
|
|
|
|
/** 起单入参 —— 触发器与上游系统一律经**调用接口**调起(OS 03) */
|
|
export type StartWorkflowInput = {
|
|
readonly packageId: string;
|
|
readonly packageVersion?: number;
|
|
readonly subject: string;
|
|
/** 凡单必有来源:触发器即来源;纪要登记起单的,登记人即来源 */
|
|
readonly source: { kind: 'periodic' | 'event' | 'registration'; ref: string; by?: string };
|
|
readonly data: Record<string, unknown>;
|
|
};
|
|
|
|
export type ProcessService = {
|
|
savePackage(pkg: ProcessPackage): Promise<ProcessPackage>;
|
|
listPackages(): Promise<ProcessPackage[]>;
|
|
getPackage(id: string, version?: number): Promise<ProcessPackage | undefined>;
|
|
/** 画图即校验 —— 与前端画布同源 */
|
|
validate(pkg: ProcessPackage): Promise<ClosureReport>;
|
|
/** 不闭合不得发布 */
|
|
publish(id: string, version: number): Promise<ClosureReport>;
|
|
|
|
start(input: StartWorkflowInput): Promise<WorkflowRecord>;
|
|
getWorkflow(id: string): Promise<WorkflowRecord>;
|
|
listWorkflows(limit?: number): Promise<WorkflowRecord[]>;
|
|
instancesOf(workflowId: string): Promise<NodeInstance[]>;
|
|
|
|
/** 任务终态到达:完成即继续流转;非完成即走声明的失败走向 */
|
|
onTaskTerminal(
|
|
taskId: string,
|
|
terminal: TerminalState,
|
|
ports: Record<string, Record<string, unknown>>,
|
|
): Promise<WorkflowState | undefined>;
|
|
applyFailure(
|
|
workflowId: string,
|
|
nodeId: string,
|
|
instance: number,
|
|
node: Extract<ProcessNode, { kind: 'activity' }>,
|
|
terminal: TerminalState,
|
|
): Promise<WorkflowState>;
|
|
/** 审批裁决:通过继续;驳回按声明走向回指定节点**重开新实例** */
|
|
onApprovalVerdict(
|
|
workflowId: string,
|
|
nodeId: string,
|
|
verdict: 'pass' | 'reject',
|
|
payload: Record<string, unknown>,
|
|
): Promise<WorkflowState>;
|
|
/** 状态是聚合投影 —— 每次变动后重算 */
|
|
refreshState(workflowId: string): Promise<WorkflowState>;
|
|
terminate(workflowId: string, reason: string): Promise<WorkflowState>;
|
|
};
|
|
|
|
export class NotPublishableError extends Error {
|
|
constructor(readonly report: ClosureReport) {
|
|
super(`流程包不闭合,不得发布:${report.violations.map((v) => v.message).join('; ')}`);
|
|
this.name = 'NotPublishableError';
|
|
}
|
|
}
|
|
|
|
export class PositionVacantError extends Error {
|
|
constructor(readonly position: string) {
|
|
super(`岗位空缺,无法解析负责人:${position} —— 按节点失败走向处置,或转治理台待指派`);
|
|
this.name = 'PositionVacantError';
|
|
}
|
|
}
|