Archived
**字段只声明,不做任何要求**(用户定):编排层不再碰字段 ——
· 激活闸只剩两面:结构闭合(dangling-node / multi-default-edge)与活动装载
(unknown-activity);字段类的检查(同名不同型、容忍分支被必填依赖)全部退役。
· 节点属性卡的「数据接口」从「推来源」改成**纯声明**:照活动契约列出吃什么、吐什么,
不标必填、不推来源、不提补全 —— 缺了怎么补是**活动内实现**的事,不在这一层现形。
· 字段类型支持数组:统一写成 `T[]`(如 `any[]`),出口口的字段也带上类型
(原来只有字段名)。实测 A2 的 benchmark 口显示「rows any[] · needSourcing boolean」。
**详情页改三栏**:左窄轨版本(132px) | 中流程图 | 右节点属性。
· 版本条一版一行(v几 · 状态 · 点/边计数),动作只在**看着的那一版**上展开 ——
窄栏放不下每行一排钮,也不该让人误点别版。
· 删掉「激活 · 双判据」整卡:没选节点时,属性栏就地列出还差哪几条(点一条定位过去);
两面都齐时盖一枚小检验章,不再占一整张卡。
· 删掉节点拣选行 —— 直接在图上点节点。
· 属性卡用包壳绝对定位:行高由中栏的图定,属性长了在卡内滚,不把图拉长
(实测卡高 467、明细内容 1039、可滚,图框不受影响)。窄轨里属性小卡改单列竖排。
· 顺带校正:上一轮那条「图框下沿对齐左列底栏顶线」的 20px 留白,
三栏改版后左栏已无底栏,没有对齐对象了 —— 改回四边等距(18px)。
**版本可删可改名**(新):
· 删:生效版不可删(先停用,ActiveVersionError → 409)、在途钉版不可删
(那些单要按它跑完,PinnedVersionError → 409);删掉正看的那版会落到剩下的最新版,
一版不剩就回货架。
· 改名:名字是**这条流的身份**,一次改及于所有版本 —— 版本列表上只显示 v1/v2…,
若只改一版的名,列表上什么也看不见,人会以为没生效。
· 5 条单测守两道闸与改名口径;实测走查:改名落库 → 改回;生效版删 409、停用版删 200。
process CONTRACT.md 与产品设计.md 同步。五闸绿。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
108 lines
6.1 KiB
TypeScript
108 lines
6.1 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>;
|
|
/**
|
|
* 节点指派 `{ 节点id → 员工id }`(可选)—— 起单时把某几个节点定向给指定账号。
|
|
* 不传即照旧:员工组轮值 / 全员轮值。指的人不在岗时退回轮值,单不能没人接。
|
|
*/
|
|
readonly assignments?: Record<string, string>;
|
|
};
|
|
export type ProcessService = {
|
|
/**
|
|
* 保存 —— **编辑即其身**:pkg.version 已存在则就地写回该版(号不变)。
|
|
* 两道闸:在途钉版抛 PinnedVersionError;写回生效版须闭合,否则 NotClosedError。
|
|
* `asNew` 或版本不存在(新流程首版)才产生新版本号(服务端算,不生效)。
|
|
*/
|
|
savePackage(pkg: ProcessPackage, opts?: {
|
|
asNew?: boolean;
|
|
}): Promise<ProcessPackage>;
|
|
listPackages(): Promise<ProcessPackage[]>;
|
|
getPackage(id: string, version?: number): Promise<ProcessPackage | undefined>;
|
|
/**
|
|
* 版本启停 —— 运行时开关,**不改定义一个字**。
|
|
* 激活先过闭合校验(不闭合抛 `NotClosedError`),过了即独占:同流其余版本一并停用;
|
|
* 停用生效版:整条流下线,新起单被拒。在途按启动时钉住的版本跑完,启停零影响。
|
|
*/
|
|
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[]>;
|
|
/** 删一版:生效版与在途钉版都不许删(见 `ActiveVersionError` / `PinnedVersionError`) */
|
|
deleteVersion(id: string, version: number): Promise<ProcessPackage[]>;
|
|
/** 改名:名字是这条流的身份,一次改及于所有版本 */
|
|
rename(id: string, name: string): Promise<ProcessPackage[]>;
|
|
nodeOwners(packageId: string): Promise<Record<string, string>>;
|
|
setNodeOwner(packageId: string, nodeId: string, staffId: string | undefined): Promise<void>;
|
|
/** 任务终态到达:完成即继续流转;非完成即走声明的失败走向 */
|
|
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 ActiveVersionError extends Error {
|
|
readonly id: string;
|
|
readonly version: number;
|
|
constructor(id: string, version: number);
|
|
}
|
|
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 StaleVersionError extends Error {
|
|
readonly packageId: string;
|
|
readonly version: number;
|
|
constructor(packageId: string, version: number);
|
|
}
|
|
/** 在途钉版:该版本有未完结的工作流按 (包, 版本) 钉着定义 —— 定义不可变,改动请新建一版 */
|
|
export declare class PinnedVersionError extends Error {
|
|
readonly packageId: string;
|
|
readonly version: number;
|
|
readonly inFlight: number;
|
|
constructor(packageId: string, version: number, inFlight: number);
|
|
}
|
|
export declare class PositionVacantError extends Error {
|
|
readonly position: string;
|
|
constructor(position: string);
|
|
}
|