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/process/contract.d.ts
T
ChaiHaoandClaude Opus 5 710946e729 流程包台面重做:版本收进包内部,启停是运行时开关
按你定的六条做:

## ① 台面三页签:总览 · 流程包 · 触发器
「调起规则」改名触发器(键不动,深链不断)。

## ② 版本收在流程内部,不平铺台面
货架一流程一卡:名字 + 标识 + 「生效 vN / 已下线」+ 版本数。
从前一版本一张卡,三个版本占三格 —— 版本是流程的内部事,不该摊在台面上。

## ③ 详情页布局照你说的排
左 1/3 版本列表(恰一个生效,点谁看谁的图),右 2/3 流程图;
图下是节点属性(点画布或点节点条,就地填);再往下才是声明式契约细节
(边映射校验 · 调用接口 · 触发器)。属性区从右侧窄栏挪到图下整行。

## ④ 新建流程
「新建流程」→ 填名称与标识 → 建出一张「开始 → 结束」的最小图,v1 即刻生效,直接进详情。
最小图天然过闭合校验 —— 不闭合存不进来这条闸对新建同样成立。
标识收紧为小写字母开头、只许小写/数字/连字符(它进 URL 和触发器规则)。

## ⑤ 历史包袱文案清掉
「工作流不设负责人,责任只落在节点上:此处不声明岗位,起单时就解析不出任务负责人」
→「这个节点还没指定岗位 —— 不指定,起单时就不知道任务该派给谁」。
同类:「未声明 · 发布前须补齐」→「未指定 —— 保存前选一个岗位」;
「发布前须先安装并过跨包契约相容核对」→「先去活动库安装;保存时会核对契约相容」;
边映射页的「边不闭合不得发布」→「不得保存」。

## ⑥ 版本可激活/停用,一次只能激活一个,切换不动在途
运行时模型从「最新版生效」改为**显式生效指针**:
  · `setPackageState` 激活即独占(同流其余版本一并停用)
  · 停用生效版 = 整条流下线,新起单被拒(报「无生效版本 —— 去流程包里激活一个」)
  · 起单取生效版;在途工作流按启动时钉住的版本号读定义,启停对它们零影响
  · 保存新版仍即刻生效(旧生效版自动停用)—— 回退就是把生效切回旧版,不用改图
store 的 setPackageState 把列上与 definition(jsonb)里的两份 state 一起改(教训同 0012);
迁移 0013 把存量多版同 active 归一为「每条流只留最高版生效」。

实测:激活 v1 → v3 自动停用,新起单钉 v1;停用 v1(零生效)→ 起单 500 且明说原因;
在途 v1 工作流照跑;恢复激活 v3。UI:切版看图、只读提示、启停确认卡、新建一条龙全通。

typecheck ✓ lint ✓ depcruise 376 模块 ✓ 498 测试 ✓

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

76 lines
4.3 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 = {
/**
* 保存 —— **严格检查 + 即刻生效**,不闭合抛 `NotClosedError`。
* 恒产生新版本号(服务端算,不采信入参的 version);返回实际落的那一版。
* 落下来的新版**即为生效版**:同流的旧生效版随之停用(一条流恰一个生效版本)。
*/
savePackage(pkg: ProcessPackage): Promise<ProcessPackage>;
listPackages(): Promise<ProcessPackage[]>;
getPackage(id: string, version?: number): Promise<ProcessPackage | undefined>;
/**
* 版本启停 —— 运行时开关,**不改定义一个字**。
* 激活某版:同流其余版本一并停用(恰一个生效);停用生效版:整条流下线,新起单被拒。
* 在途工作流按启动时钉住的版本跑完,启停对它们零影响。
*/
setPackageState(id: string, version: number, state: 'active' | 'retired'): Promise<ProcessPackage[]>;
/** 画图即校验 —— 与前端画布同源;只算不存 */
validate(pkg: ProcessPackage): 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 declare class CallInterfaceViolationError extends Error {
readonly packageId: string;
readonly errors: readonly string[];
constructor(packageId: string, errors: readonly string[]);
}
/** 不闭合不得**保存** —— 没有发布这道门,闸就落在保存上 */
export declare class NotClosedError extends Error {
readonly report: ClosureReport;
constructor(report: ClosureReport);
}
export declare class PositionVacantError extends Error {
readonly position: string;
constructor(position: string);
}