@yss123/cage-approval 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,19 +1,74 @@
1
1
  # @yss123/cage-approval
2
2
 
3
- 审批适配接口、桥接与代码内封闭路由(可选)
3
+ Cage 的审批路由与执行包。用代码声明顺序步骤、条件分支、角色/部门负责人选人和会签,提供待办接口、审批历史与结论桥接,也支持 AI 审批步骤。通过、驳回和退回最终调用领域命令,审批引擎不直接修改业务表。
4
4
 
5
- Cage 是通过 TypeScript 声明业务、由框架统一执行权限和数据规则的中后台框架。此包是框架的一部分。
5
+ ## 安装
6
6
 
7
- ## 使用
7
+ 需要 Node.js 24+、PostgreSQL,以及同版本 Cage 框架。在应用 domain 包中安装:
8
8
 
9
9
  ```powershell
10
- pnpm add @yss123/cage-approval
10
+ pnpm add @yss123/cage-approval @yss123/cage-kernel
11
11
  ```
12
12
 
13
- 首次使用建议通过脚手架创建完整项目,所需依赖由模板统一配置。需要 Node.js 24 或更新版本。
13
+ 标准 `@yss123/cage-http` 装配审批接口,worker 消费审批启动/取消事件。只启动 API 不会完成后台事件消费。
14
14
 
15
- - [完整使用说明](https://github.com/shunshunyue/cage/blob/main/docs/使用说明.md)
16
- - [框架源码与范例](https://github.com/shunshunyue/cage)
17
- - [问题反馈](https://github.com/shunshunyue/cage/issues)
15
+ ## 将已有业务接到审批
18
16
 
19
- 许可证:[MIT](https://github.com/shunshunyue/cage/blob/main/LICENSE)。
17
+ 以下是采购模板可用的组合片段。`PurchaseOrder`、事件和结论命令来自业务文件;结论命令须声明正确的状态迁移、process 调用身份及业务硬约束。
18
+
19
+ ```ts
20
+ /** 将领域事件关联到审批生命周期;路由与绑定的 process 必须相同。 */
21
+ import { approval } from '@yss123/cage-kernel';
22
+ import { approver, gt, route, step, when } from '@yss123/cage-approval';
23
+ import {
24
+ PurchaseOrder, OrderSubmitted, OrderCancelled, approve, reject,
25
+ } from './purchase-order.js';
26
+
27
+ /** 提交启动、取消撤回;只允许金额和部门字段用于流程路由。 */
28
+ export const poApproval = approval(PurchaseOrder, {
29
+ process: 'po-approval',
30
+ start: OrderSubmitted,
31
+ cancelOn: [OrderCancelled],
32
+ routable: ['amount', 'deptId'],
33
+ onApproved: approve,
34
+ onRejected: reject,
35
+ });
36
+
37
+ /** 先部门负责人;金额严格大于 50000 元时,增加财务角色的一人审批。 */
38
+ export const poRoute = route('po-approval', [
39
+ step('部门经理审批', approver.deptLeader('deptId')),
40
+ when(
41
+ gt('amount', 50000),
42
+ step('财务审批', approver.role('finance'), { mode: 'any' }),
43
+ ),
44
+ ]);
45
+ ```
46
+
47
+ 将这两个声明经模块入口从 domain 根入口导出,运行 `pnpm exec cage check` 和 `pnpm gen`,审查迁移后执行 `pnpm migrate`。启动 API/worker,在系统管理中配置真实部门负责人、角色与命令权限;通过提交命令触发业务事件后,从 `/admin/` 的审批待办处理。
48
+
49
+ `routable` 是允许路由读取的字段清单,不是权限列表。被流程选为审批人也必须具备实际结论命令能力、数据范围及额度,并满足职责分离;路由选人不会自动授予这些权限。
50
+
51
+ ## 主要入口
52
+
53
+ | 入口 | 用途 |
54
+ | --- | --- |
55
+ | `route`、`step`、`when` | 声明步骤和条件;顺序执行,条件只读取允许路由的字段 |
56
+ | `approver` | 角色、部门负责人等选人规则 |
57
+ | `gt/gte/lt/lte/eq/ne/isIn` | 可被分析的条件表达式 |
58
+ | `InCodeEngine`、`TaskView` | 内置流程执行器及待办视图,通常由 HTTP/worker 装配 |
59
+ | `bindApproval`、`applyOutcome` | 适配器把审批结论桥接回领域命令 |
60
+ | `ApprovalAdapter` 等适配类型 | 对接外部审批实现时遵守的生命周期协议 |
61
+ | `approvalRoutes` | 受认证保护的 HTTP 审批接口装配 |
62
+ | `AiStepOptions`、`approvalRoleContext` | AI 节点配置与自动执行角色上下文 |
63
+
64
+ 领域绑定原语 `approval()` 从 `@yss123/cage-kernel` 导入;不要与本包的路由原语混淆。
65
+
66
+ AI 审批使用同一个 `step` 的 `kind: 'ai'` 选项,配置模型、材料范围、策略、handoff 和预算;结果为 `pass/return/manual`。需要 domain 启用 `@yss123/cage-ai`,退回需声明 `onReturned`,自动结论需显式配置 `automatedBy` 对应角色权限。模型失败不会被当成自动批准。
67
+
68
+ ## 文档
69
+
70
+ - [标准采购审批链路](https://github.com/shunshunyue/cage/blob/main/docs/ai/purchase-example.md)
71
+ - [AI 审批三态、转人工和授权](https://github.com/shunshunyue/cage/blob/main/docs/ai/intelligence.md)
72
+ - [采购审批完整源码](https://github.com/shunshunyue/cage/blob/main/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts)
73
+
74
+ 源码与反馈:[GitHub](https://github.com/shunshunyue/cage)。许可证:[MIT](https://github.com/shunshunyue/cage/blob/main/LICENSE)。
package/dist/ai.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * AI 审批的声明协议、受限角色上下文与前向迁移。模型循环在 cage-ai 中执行,
3
+ * 此模块不直接调用模型、不创建另一套流程;AI 结果仍进入 InCodeEngine 的当前步骤。
4
+ */
5
+ import { type Ctx, type Kernel } from '@yss123/cage-kernel';
6
+ import type { Approver } from './route.js';
7
+ /** AI 是步骤执行类型,handoff 仅指定本步骤的接管人,不嵌入第二条流程。 */
8
+ export interface AiStepOptions {
9
+ kind: 'ai';
10
+ /** 应用 AI 配置中的模型名,不在业务声明中保存供应商密钥。 */
11
+ model: string;
12
+ /** 节点审核政策;表达判定要求,不赋予超出自动角色的工具或数据权限。 */
13
+ instructions: string;
14
+ /** 允许取证的字段与已注册只读查询,字段须公开且实际访问仍受角色授权约束。 */
15
+ context: {
16
+ fields: readonly string[];
17
+ queries?: readonly string[];
18
+ };
19
+ /** 当前步骤转人工时的选人策略;不是另起一条无关审批流程。 */
20
+ handoff: Approver;
21
+ /** 单次执行预算:步骤数、工具调用数、token 数和毫秒超时,均应为正整数。 */
22
+ limits?: {
23
+ maxSteps?: number;
24
+ maxToolCalls?: number;
25
+ tokenBudget?: number;
26
+ timeoutMs?: number;
27
+ };
28
+ /** 模型/工具失败后的处理政策;故障不等同于正常通过。 */
29
+ onFailure?: 'manual' | 'failed';
30
+ }
31
+ export interface AiStep extends AiStepOptions {
32
+ title: string;
33
+ }
34
+ /** 三态审核结果;pass 只通过本节点,return 走领域退回命令,manual 生成本节点人工待办。 */
35
+ export interface AiDecision {
36
+ decision: 'pass' | 'return' | 'manual';
37
+ reason: string;
38
+ suggestions: string[];
39
+ evidence: {
40
+ source: string;
41
+ detail: string;
42
+ }[];
43
+ }
44
+ /** 角色授权只赋予流程服务主体;不借用用户身份,也不继承 systemCtx 的全部范围。 */
45
+ /**
46
+ * 每次从当前租户的 active 角色解析能力/范围/明文权限,不缓存旧授权、不借用真人身份。
47
+ * 角色缺失或未配置立即 FORBIDDEN;因为没有部门上下文,范围政策不能假定自动主体属于某个部门。
48
+ */
49
+ export declare function approvalRoleContext(kernel: Kernel, process: string, tenantId: string): Promise<Ctx>;
50
+ /** 追加迁移,保留原 FLOW_DDL 的已发布内容。 */
51
+ /** 节点记录保存针对的提交版本、run 归属、判定及可见性证据;同实例/步骤唯一,防重复占位。 */
52
+ export declare const APPROVAL_AI_MIGRATION: {
53
+ name: string;
54
+ sql: string;
55
+ };
package/dist/ai.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * AI 审批的声明协议、受限角色上下文与前向迁移。模型循环在 cage-ai 中执行,
3
+ * 此模块不直接调用模型、不创建另一套流程;AI 结果仍进入 InCodeEngine 的当前步骤。
4
+ */
5
+ import { cageError, mergeCaps, mergeScopes, unmaskFrom } from '@yss123/cage-kernel';
6
+ /** 角色授权只赋予流程服务主体;不借用用户身份,也不继承 systemCtx 的全部范围。 */
7
+ /**
8
+ * 每次从当前租户的 active 角色解析能力/范围/明文权限,不缓存旧授权、不借用真人身份。
9
+ * 角色缺失或未配置立即 FORBIDDEN;因为没有部门上下文,范围政策不能假定自动主体属于某个部门。
10
+ */
11
+ export async function approvalRoleContext(kernel, process, tenantId) {
12
+ const binding = kernel.registry.approvalByProcess(process)?.a;
13
+ if (!binding?.automatedBy)
14
+ throw cageError('FORBIDDEN', { reason: '审批未配置自动执行角色' });
15
+ const role = await kernel.app.selectFrom('sys.role').select(['capabilities', 'scopes']).where('tenant_id', '=', tenantId)
16
+ .where('code', '=', binding.automatedBy).where('status', '=', 'active').executeTakeFirst();
17
+ if (!role)
18
+ throw cageError('FORBIDDEN', { reason: '自动执行角色不存在或已禁用' });
19
+ const capabilities = mergeCaps([role]);
20
+ return { actor: { kind: 'process', id: `process:${process}:role:${binding.automatedBy}` }, userId: null, tenantId,
21
+ deptId: null, deptPath: null, capabilities, scopes: mergeScopes([role]), unmask: unmaskFrom(capabilities), source: 'process' };
22
+ }
23
+ /** 追加迁移,保留原 FLOW_DDL 的已发布内容。 */
24
+ /** 节点记录保存针对的提交版本、run 归属、判定及可见性证据;同实例/步骤唯一,防重复占位。 */
25
+ export const APPROVAL_AI_MIGRATION = {
26
+ name: 'cage:approval_ai_001',
27
+ sql: `
28
+ ALTER TABLE flow.approval_instance ADD COLUMN subject_version integer;
29
+ ALTER TABLE flow.approval_instance ADD COLUMN start_event_id bigint;
30
+ ALTER TABLE flow.approval_instance ADD COLUMN last_actor jsonb;
31
+ CREATE TABLE flow.approval_step (
32
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
33
+ tenant_id uuid NOT NULL,
34
+ instance_id uuid NOT NULL REFERENCES flow.approval_instance(id),
35
+ step integer NOT NULL,
36
+ kind text NOT NULL CHECK (kind IN ('human','ai')),
37
+ status text NOT NULL,
38
+ expected_version integer,
39
+ run_id uuid,
40
+ decision jsonb,
41
+ decision_visibility jsonb,
42
+ actor jsonb,
43
+ created_at timestamptz NOT NULL DEFAULT now(),
44
+ updated_at timestamptz NOT NULL DEFAULT now(),
45
+ UNIQUE(instance_id, step)
46
+ );
47
+ CREATE INDEX approval_step_queue_idx ON flow.approval_step(status, created_at) WHERE kind = 'ai';
48
+ GRANT SELECT, INSERT, UPDATE ON flow.approval_step TO cage_app;
49
+ `,
50
+ };
51
+ //# sourceMappingURL=ai.js.map
package/dist/ai.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.js","sourceRoot":"","sources":["../src/ai.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAyB,MAAM,qBAAqB,CAAC;AA4B3G,kDAAkD;AAClD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAc,EAAE,OAAe,EAAE,QAAgB;IACzF,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,WAAW;QAAE,MAAM,SAAS,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC;SACtH,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAC7F,IAAI,CAAC,IAAI;QAAE,MAAM,SAAS,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,OAAO,SAAS,OAAO,CAAC,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;QAC/G,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACnI,CAAC;AAED,gCAAgC;AAChC,oDAAoD;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,sBAAsB;IAC5B,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;CAsBN;CACA,CAAC"}
package/dist/bridge.d.ts CHANGED
@@ -1,6 +1,12 @@
1
- import type { Kernel } from '@yss123/cage-kernel';
1
+ import { type Kernel } from '@yss123/cage-kernel';
2
2
  import type { Transaction } from 'kysely';
3
3
  import type { ApprovalAdapter, ApprovalOutcome } from './types.js';
4
+ /**
5
+ * 从流程绑定选择通过/驳回/退回命令,再为结论构造可信 process 上下文。
6
+ * 人工结论实时核对最后审批人的命令能力与单据可见性;自动结论使用受限角色,不能继承系统全部范围。
7
+ * 传 tx 时与待办决定共同回滚,未传时命令自行开事务;expectedVersion 阻止迟到结论覆盖新版本。
8
+ */
4
9
  export declare function applyOutcome(k: Kernel, r: ApprovalOutcome, tx?: Transaction<any>): Promise<Record<string, any>>;
5
10
  /** 外部引擎(例如独立部署的 Warm-Flow、Flowable 服务):它的结论通过回调进入这里 */
11
+ /** 只登记结束回调,不创建外部流程或授予回调来源信任;适配器须自行完成其协议认证和投递可靠性。 */
6
12
  export declare function bindApproval(adapter: ApprovalAdapter, k: Kernel): void;
package/dist/bridge.js CHANGED
@@ -1,17 +1,34 @@
1
+ // 桥接:引擎的结论只能转成命令,以流程身份调用,走完整 12 关。引擎侧没有任何业务表的数据库权限。
2
+ import { cageError } from '@yss123/cage-kernel';
3
+ import { approvalRoleContext } from './ai.js';
4
+ /**
5
+ * 从流程绑定选择通过/驳回/退回命令,再为结论构造可信 process 上下文。
6
+ * 人工结论实时核对最后审批人的命令能力与单据可见性;自动结论使用受限角色,不能继承系统全部范围。
7
+ * 传 tx 时与待办决定共同回滚,未传时命令自行开事务;expectedVersion 阻止迟到结论覆盖新版本。
8
+ */
1
9
  export async function applyOutcome(k, r, tx) {
2
10
  const binding = k.registry.approvalByProcess(r.process);
3
11
  if (!binding)
4
12
  throw new Error(`[CAGE-APPR-004] 没有流程 ${r.process} 的审批绑定`);
5
- const cmd = r.outcome === 'approved' ? binding.a.onApproved : binding.a.onRejected;
6
- const ctx = k.processCtx({
7
- process: r.process,
8
- tenantId: r.tenantId,
9
- onBehalfOf: r.lastApprover,
10
- capabilities: [k.registry.capabilityOf(binding.a.onApproved), k.registry.capabilityOf(binding.a.onRejected)],
11
- });
12
- return tx ? k.executeIn(tx, cmd, { id: r.businessKey }, ctx, 0) : k.execute(cmd, { id: r.businessKey }, ctx);
13
+ const cmd = r.outcome === 'approved' ? binding.a.onApproved : r.outcome === 'returned' ? binding.a.onReturned : binding.a.onRejected;
14
+ if (!cmd)
15
+ throw new Error('[CAGE-APPR-006] 审批未绑定退回修改命令');
16
+ let ctx;
17
+ if (r.lastApprover) {
18
+ const user = await k.contextFor(r.lastApprover, r.tenantId);
19
+ if (!user?.capabilities.has(k.registry.capabilityOf(cmd)))
20
+ throw cageError('FORBIDDEN');
21
+ // 先核验真人对目标单据的权限;命令内声明的后台读依赖仍按已有流程身份完成业务校验。
22
+ await k.read.get(binding.a.agg, r.businessKey, user);
23
+ ctx = k.processCtx({ process: r.process, tenantId: r.tenantId, onBehalfOf: r.lastApprover, capabilities: [k.registry.capabilityOf(cmd)] });
24
+ }
25
+ else
26
+ ctx = await approvalRoleContext(k, r.process, r.tenantId);
27
+ const input = { id: r.businessKey, expectedVersion: r.expectedVersion };
28
+ return tx ? k.executeIn(tx, cmd, input, ctx, 0) : k.execute(cmd, input, ctx);
13
29
  }
14
30
  /** 外部引擎(例如独立部署的 Warm-Flow、Flowable 服务):它的结论通过回调进入这里 */
31
+ /** 只登记结束回调,不创建外部流程或授予回调来源信任;适配器须自行完成其协议认证和投递可靠性。 */
15
32
  export function bindApproval(adapter, k) {
16
33
  adapter.onCompleted(async (r) => {
17
34
  await applyOutcome(k, r);
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,CAAS,EAAE,CAAkB,EAAE,EAAqB;IACrF,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC;IACzE,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;IACnF,MAAM,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC;QACvB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU,EAAE,CAAC,CAAC,YAAY;QAC1B,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;KAC7G,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC;AAC/G,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,YAAY,CAAC,OAAwB,EAAE,CAAS;IAC9D,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,CAAS,EAAE,CAAkB,EAAE,EAAqB;IACrF,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC;IACzE,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;IACrI,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACzD,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAAE,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;QACxF,2CAA2C;QAC3C,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACrD,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,YAAY,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7I,CAAC;;QAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,eAAe,EAAE,CAAC,CAAC,eAAe,EAAE,CAAC;IACxE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;AAC/E,CAAC;AAED,uDAAuD;AACvD,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,OAAwB,EAAE,CAAS;IAC9D,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/engine.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { type Kernel } from '@yss123/cage-kernel';
1
+ import { type Kernel, type Ctx } from '@yss123/cage-kernel';
2
2
  import { type ApprovalEngine } from '@yss123/cage-kernel/worker';
3
3
  import { type Transaction } from 'kysely';
4
4
  import { type Route } from './route.js';
5
+ import { type AiDecision } from './ai.js';
6
+ /** 对外待办摘要;step 为 0 基索引,vars 仅来自 routable 白名单,不包含整份领域对象。 */
5
7
  export interface TaskView {
6
8
  id: string;
7
9
  instanceId: string;
@@ -14,21 +16,36 @@ export interface TaskView {
14
16
  vars: Record<string, unknown>;
15
17
  createdAt: string;
16
18
  }
19
+ /**
20
+ * 有限顺序路由的持久执行器:实例保存展开后的步骤,人工任务和 AI 节点共享同一推进/结束边界。
21
+ * 以实例行锁串行化决定,最后结论与领域命令共同事务提交;命令失败时本次审批也不能算完成。
22
+ */
17
23
  export declare class InCodeEngine implements ApprovalEngine {
18
24
  private readonly k;
19
25
  readonly route: Route;
20
26
  constructor(k: Kernel, route: Route);
27
+ /** 按显式用户、角色或部门负责人解析候选;部门无负责人向祖先查找,空结果由 openTasks 明确拒绝。 */
21
28
  private approversOf;
29
+ /** 登记当前节点与提交版本;AI 节点排队等待运行时领取,人工节点立即解析候选并创建待办。 */
22
30
  private openStep;
31
+ /** 候选去重后逐个建立待办;找不到人是配置错误,不能跳过步骤或默认审批通过。 */
32
+ private openTasks;
33
+ /**
34
+ * 在消费事务中创建新轮次:先取消旧执行者,再保存展开步骤和触发事件/提交版本。
35
+ * 事件版本已落后于当前对象时标 superseded 而不创建新待办,防止迟到提交事件审核过期内容。
36
+ */
23
37
  start(input: {
24
38
  process: string;
25
39
  businessKey: string;
26
40
  tenantId: string;
27
41
  vars: Record<string, unknown>;
28
42
  tx: Transaction<any>;
43
+ subjectVersion?: number;
44
+ eventId?: number;
29
45
  }): Promise<{
30
46
  instanceId: string;
31
47
  }>;
48
+ /** 关闭同租户/流程/对象的 running 轮次、未决待办和可运行 AI 节点,已完成历史保持可追溯。 */
32
49
  cancel(input: {
33
50
  process: string;
34
51
  businessKey: string;
@@ -37,9 +54,50 @@ export declare class InCodeEngine implements ApprovalEngine {
37
54
  }): Promise<void>;
38
55
  /** 我的待办 */
39
56
  tasksOf(userId: string, tenantId: string, status?: string): Promise<TaskView[]>;
57
+ /** 以业务对象读权限查看审批轮次;隐藏内部 policy、提示词、工具历史和运行凭据。 */
58
+ history(aggregate: string, businessKey: string, ctx: Ctx): Promise<{
59
+ id: any;
60
+ process: string;
61
+ status: any;
62
+ currentStep: any;
63
+ createdAt: string;
64
+ updatedAt: string;
65
+ steps: {
66
+ index: any;
67
+ kind: any;
68
+ status: any;
69
+ decision: any;
70
+ detailRestricted: boolean;
71
+ actor: any;
72
+ updatedAt: string;
73
+ }[];
74
+ }[]>;
40
75
  /** 审批人做出决定;流程结束时在同一事务里执行结论命令 */
41
- decide(taskId: string, userId: string, tenantId: string, outcome: 'approved' | 'rejected', comment?: string): Promise<{
76
+ decide(taskId: string, userId: string, tenantId: string, outcome: 'approved' | 'rejected' | 'returned', comment?: string): Promise<{
77
+ finished: boolean;
78
+ outcome?: string;
79
+ }>;
80
+ /** AI 助理的动作回执可与人工待办决定共用该事务,重试不重复审批。 */
81
+ /**
82
+ * 先按租户/受托人定位任务,再锁实例和任务并实时验证能力/单据可见性;过期待办不允许再次决定。
83
+ * 驳回/退回直接结束;all 等待同节点所有人通过,any 将其他待办标 skipped 后进入下一节点。
84
+ * 外层拥有事务提交权,可与 AI 助理动作回执共同提交;finished:false 仅表示流程还需后续步骤。
85
+ */
86
+ decideIn(tx: Transaction<any>, taskId: string, userId: string, tenantId: string, outcome: 'approved' | 'rejected' | 'returned', comment?: string): Promise<{
42
87
  finished: boolean;
43
88
  outcome?: string;
44
89
  }>;
90
+ /** 先执行带版本的领域结论命令,成功后才关闭剩余待办并写流程终态,保证两边不会各自成功。 */
91
+ private finish;
92
+ /** 将当前节点标通过并开启下一步;最后一步转 finish,因此 AI pass 也不必然立即批准整张单据。 */
93
+ private advance;
94
+ /** 只有仍然占据该节点的 run 能提交;调用方需在同一事务核验 AI run 租约。 */
95
+ /**
96
+ * 锁定实例、节点和业务对象,校验 run 仍占据当前节点且对象版本未变;旧轮次/旧 run 回执一律冲突。
97
+ * 保存判定时同时保存敏感明文/查询范围证据,history 据此决定后来读者能否查看理由。
98
+ * manual 仅开人工接管待办;return/ pass 分别进入共同 finish/advance,保持领域命令约束不变。
99
+ */
100
+ completeAi(tx: Transaction<any>, stepId: string, runId: string, decision: AiDecision, actor?: Ctx): Promise<{
101
+ finished: boolean;
102
+ }>;
45
103
  }
package/dist/engine.js CHANGED
@@ -1,9 +1,14 @@
1
1
  // 代码内封闭路由的引擎:流程实例和待办存在 flow 模式;结论通过桥接以流程身份调用命令。
2
2
  // 审批人做出最后一个决定时,决定和结论命令在同一个事务里提交:命令被拒(例如超出额度)时决定也不生效。
3
- import { cageError } from '@yss123/cage-kernel';
3
+ import { cageError, grantsFor, stableStringify } from '@yss123/cage-kernel';
4
4
  import { sql } from 'kysely';
5
5
  import { applyOutcome } from './bridge.js';
6
6
  import { resolveSteps } from './route.js';
7
+ import { approvalRoleContext } from './ai.js';
8
+ /**
9
+ * 有限顺序路由的持久执行器:实例保存展开后的步骤,人工任务和 AI 节点共享同一推进/结束边界。
10
+ * 以实例行锁串行化决定,最后结论与领域命令共同事务提交;命令失败时本次审批也不能算完成。
11
+ */
7
12
  export class InCodeEngine {
8
13
  k;
9
14
  route;
@@ -11,6 +16,7 @@ export class InCodeEngine {
11
16
  this.k = k;
12
17
  this.route = route;
13
18
  }
19
+ /** 按显式用户、角色或部门负责人解析候选;部门无负责人向祖先查找,空结果由 openTasks 明确拒绝。 */
14
20
  async approversOf(tx, a, vars, tenantId) {
15
21
  switch (a.kind) {
16
22
  case 'user':
@@ -50,40 +56,66 @@ export class InCodeEngine {
50
56
  return [];
51
57
  }
52
58
  }
53
- async openStep(tx, instanceId, tenantId, steps, index, vars) {
59
+ /** 登记当前节点与提交版本;AI 节点排队等待运行时领取,人工节点立即解析候选并创建待办。 */
60
+ async openStep(tx, instanceId, tenantId, steps, index, vars, version) {
54
61
  const s = steps[index];
55
- const who = await this.approversOf(tx, s.approver, vars, tenantId);
62
+ if (!s)
63
+ throw new Error('[CAGE-APPR-006] 审批路由未展开出任何步骤');
64
+ await tx.insertInto('flow.approval_step').values({ instance_id: instanceId, tenant_id: tenantId, step: index,
65
+ kind: s.kind === 'ai' ? 'ai' : 'human', status: s.kind === 'ai' ? 'queued' : 'running', expected_version: version ?? null }).execute();
66
+ if (s.kind === 'ai')
67
+ return;
68
+ await this.openTasks(tx, instanceId, tenantId, index, s.title, s.approver, vars);
69
+ }
70
+ /** 候选去重后逐个建立待办;找不到人是配置错误,不能跳过步骤或默认审批通过。 */
71
+ async openTasks(tx, instanceId, tenantId, index, title, selector, vars) {
72
+ const who = await this.approversOf(tx, selector, vars, tenantId);
56
73
  if (who.length === 0) {
57
- throw new Error(`[CAGE-APPR-007] 流程 ${this.route.process} 第 ${index + 1} 步「${s.title}」找不到审批人,请在 /admin 配置部门负责人或角色`);
74
+ throw new Error(`[CAGE-APPR-007] 流程 ${this.route.process} 第 ${index + 1} 步「${title}」找不到审批人,请在 /admin 配置部门负责人或角色`);
58
75
  }
59
76
  for (const assignee of new Set(who)) {
60
- await tx.insertInto('flow.approval_task').values({ instance_id: instanceId, tenant_id: tenantId, step: index, title: s.title, assignee }).execute();
77
+ await tx.insertInto('flow.approval_task').values({ instance_id: instanceId, tenant_id: tenantId, step: index, title, assignee }).execute();
61
78
  }
62
79
  }
80
+ /**
81
+ * 在消费事务中创建新轮次:先取消旧执行者,再保存展开步骤和触发事件/提交版本。
82
+ * 事件版本已落后于当前对象时标 superseded 而不创建新待办,防止迟到提交事件审核过期内容。
83
+ */
63
84
  async start(input) {
64
85
  const { tx } = input;
65
86
  const binding = this.k.registry.approvalByProcess(input.process);
66
87
  const steps = resolveSteps(this.route.nodes, input.vars);
67
- // 同一张单重新提交时,关闭旧的流程实例
68
- await tx.updateTable('flow.approval_instance').set({ status: 'superseded', updated_at: sql `now()` })
69
- .where('tenant_id', '=', input.tenantId).where('business_key', '=', input.businessKey).where('status', '=', 'running').execute();
88
+ // 重提关闭旧轮次的所有执行者;旧 AI 回执不能继续推进新流程。
89
+ await this.cancel({ ...input });
70
90
  const inst = await tx
71
91
  .insertInto('flow.approval_instance')
72
92
  .values({
73
93
  tenant_id: input.tenantId, process: input.process, aggregate: binding?.a.agg.name ?? '', business_key: input.businessKey,
74
94
  vars: JSON.stringify(input.vars), steps: JSON.stringify(steps),
95
+ subject_version: input.subjectVersion ?? null, start_event_id: input.eventId ?? null,
75
96
  })
76
97
  .returning('id')
77
98
  .executeTakeFirstOrThrow();
78
- await this.openStep(tx, inst.id, input.tenantId, steps, 0, input.vars);
99
+ if (input.subjectVersion && binding) {
100
+ const current = await tx.selectFrom(`${binding.a.agg.table.schema}.${binding.a.agg.table.name}`).select('version')
101
+ .where('id', '=', input.businessKey).where('tenant_id', '=', input.tenantId).executeTakeFirst();
102
+ if (!current || current.version !== input.subjectVersion) {
103
+ await tx.updateTable('flow.approval_instance').set({ status: 'superseded' }).where('id', '=', inst.id).execute();
104
+ return { instanceId: inst.id };
105
+ }
106
+ }
107
+ await this.openStep(tx, inst.id, input.tenantId, steps, 0, input.vars, input.subjectVersion);
79
108
  return { instanceId: inst.id };
80
109
  }
110
+ /** 关闭同租户/流程/对象的 running 轮次、未决待办和可运行 AI 节点,已完成历史保持可追溯。 */
81
111
  async cancel(input) {
82
112
  const insts = await input.tx.selectFrom('flow.approval_instance').select('id')
83
- .where('tenant_id', '=', input.tenantId).where('business_key', '=', input.businessKey).where('status', '=', 'running').execute();
113
+ .where('tenant_id', '=', input.tenantId).where('process', '=', input.process).where('business_key', '=', input.businessKey).where('status', '=', 'running').execute();
84
114
  for (const i of insts) {
85
115
  await input.tx.updateTable('flow.approval_instance').set({ status: 'cancelled', updated_at: sql `now()` }).where('id', '=', i.id).execute();
86
116
  await input.tx.updateTable('flow.approval_task').set({ status: 'cancelled' }).where('instance_id', '=', i.id).where('status', '=', 'pending').execute();
117
+ await input.tx.updateTable('flow.approval_step').set({ status: 'cancelled', updated_at: sql `now()` }).where('instance_id', '=', i.id)
118
+ .where('status', 'in', ['queued', 'running', 'waiting_manual', 'failed']).execute();
87
119
  }
88
120
  }
89
121
  /** 我的待办 */
@@ -95,6 +127,7 @@ export class InCodeEngine {
95
127
  .where('t.assignee', '=', userId)
96
128
  .where('t.tenant_id', '=', tenantId)
97
129
  .where('t.status', '=', status)
130
+ .$if(status === 'pending', qb => qb.where('i.status', '=', 'running'))
98
131
  .where('i.process', '=', this.route.process)
99
132
  .orderBy('t.created_at', 'desc')
100
133
  .limit(200)
@@ -104,47 +137,132 @@ export class InCodeEngine {
104
137
  step: r.step, title: r.title, status: r.status, vars: r.vars, createdAt: new Date(r.created_at).toISOString(),
105
138
  }));
106
139
  }
140
+ /** 以业务对象读权限查看审批轮次;隐藏内部 policy、提示词、工具历史和运行凭据。 */
141
+ async history(aggregate, businessKey, ctx) {
142
+ const binding = this.k.registry.approvalByProcess(this.route.process)?.a;
143
+ if (binding?.agg.name !== aggregate)
144
+ return [];
145
+ await this.k.read.get(aggregate, businessKey, ctx);
146
+ const instances = await this.k.app.selectFrom('flow.approval_instance').select(['id', 'status', 'current_step', 'created_at', 'updated_at'])
147
+ .where('tenant_id', '=', ctx.tenantId).where('process', '=', this.route.process).where('business_key', '=', businessKey).orderBy('created_at', 'desc').limit(50).execute();
148
+ const result = [];
149
+ for (const instance of instances) {
150
+ const steps = await this.k.app.selectFrom('flow.approval_step').select(['step', 'kind', 'status', 'decision', 'decision_visibility', 'actor', 'created_at', 'updated_at'])
151
+ .where('instance_id', '=', instance.id).orderBy('step').execute();
152
+ result.push({ id: instance.id, process: this.route.process, status: instance.status, currentStep: instance.current_step,
153
+ createdAt: new Date(instance.created_at).toISOString(), updatedAt: new Date(instance.updated_at).toISOString(),
154
+ steps: steps.map(s => {
155
+ // 生成的理由也可能包含关联查询或敏感明文,不能只按单据可见就向低权限读者披露。
156
+ const visibility = s.decision_visibility;
157
+ const allowed = !visibility || (visibility.unmask.every((kind) => ctx.unmask.includes(kind)) && visibility.queries.every((q) => ctx.capabilities.has(`${q.aggregate}.read`) && (grantsFor(ctx, q.aggregate).some(g => g.combinator === 'all') ||
158
+ (visibility.userId === ctx.userId && stableStringify(grantsFor(ctx, q.aggregate)) === stableStringify(q.scopes)))));
159
+ return { index: s.step, kind: s.kind, status: s.status, decision: allowed ? s.decision : null, detailRestricted: !allowed, actor: s.actor, updatedAt: new Date(s.updated_at).toISOString() };
160
+ }) });
161
+ }
162
+ return result;
163
+ }
107
164
  /** 审批人做出决定;流程结束时在同一事务里执行结论命令 */
108
165
  async decide(taskId, userId, tenantId, outcome, comment) {
109
- return this.k.app.transaction().execute(async (tx) => {
110
- const task = await tx.selectFrom('flow.approval_task').selectAll()
111
- .where('id', '=', taskId).where('tenant_id', '=', tenantId).forUpdate().executeTakeFirst();
112
- // 不是自己的待办一律 404,不泄露存在性
113
- if (!task || task.assignee !== userId)
114
- throw cageError('NOT_FOUND');
115
- if (task.status !== 'pending')
116
- throw cageError('INVALID_TRANSITION', { status: task.status });
117
- const inst = await tx.selectFrom('flow.approval_instance').selectAll().where('id', '=', task.instance_id).forUpdate().executeTakeFirstOrThrow();
118
- if (inst.status !== 'running')
119
- throw cageError('INVALID_TRANSITION', { status: inst.status });
120
- await tx.updateTable('flow.approval_task').set({ status: outcome, comment: comment ?? null, decided_at: sql `now()` }).where('id', '=', taskId).execute();
121
- const steps = inst.steps;
122
- const finish = async (result) => {
123
- await tx.updateTable('flow.approval_task').set({ status: 'skipped' }).where('instance_id', '=', inst.id).where('status', '=', 'pending').execute();
124
- await tx.updateTable('flow.approval_instance').set({ status: result, last_approver: userId, updated_at: sql `now()` }).where('id', '=', inst.id).execute();
125
- await applyOutcome(this.k, { process: inst.process, tenantId, businessKey: inst.business_key, outcome: result, lastApprover: userId }, tx);
126
- return { finished: true, outcome: result };
127
- };
128
- if (outcome === 'rejected')
129
- return finish('rejected');
130
- const s = steps[task.step];
131
- if (s.mode === 'all') {
132
- const left = await tx.selectFrom('flow.approval_task').select('id')
133
- .where('instance_id', '=', inst.id).where('step', '=', task.step).where('status', '=', 'pending').execute();
134
- if (left.length > 0)
135
- return { finished: false };
136
- }
137
- else {
138
- await tx.updateTable('flow.approval_task').set({ status: 'skipped' })
139
- .where('instance_id', '=', inst.id).where('step', '=', task.step).where('status', '=', 'pending').execute();
140
- }
141
- const nextIndex = task.step + 1;
142
- if (nextIndex >= steps.length)
143
- return finish('approved');
144
- await tx.updateTable('flow.approval_instance').set({ current_step: nextIndex, last_approver: userId, updated_at: sql `now()` }).where('id', '=', inst.id).execute();
145
- await this.openStep(tx, inst.id, tenantId, steps, nextIndex, inst.vars);
166
+ return this.k.app.transaction().execute(tx => this.decideIn(tx, taskId, userId, tenantId, outcome, comment));
167
+ }
168
+ /** AI 助理的动作回执可与人工待办决定共用该事务,重试不重复审批。 */
169
+ /**
170
+ * 先按租户/受托人定位任务,再锁实例和任务并实时验证能力/单据可见性;过期待办不允许再次决定。
171
+ * 驳回/退回直接结束;all 等待同节点所有人通过,any 将其他待办标 skipped 后进入下一节点。
172
+ * 外层拥有事务提交权,可与 AI 助理动作回执共同提交;finished:false 仅表示流程还需后续步骤。
173
+ */
174
+ async decideIn(tx, taskId, userId, tenantId, outcome, comment) {
175
+ const candidate = await tx.selectFrom('flow.approval_task').select(['instance_id', 'assignee']).where('id', '=', taskId).where('tenant_id', '=', tenantId).executeTakeFirst();
176
+ if (!candidate || candidate.assignee !== userId)
177
+ throw cageError('NOT_FOUND');
178
+ const inst = await tx.selectFrom('flow.approval_instance').selectAll().where('id', '=', candidate.instance_id).where('process', '=', this.route.process).forUpdate().executeTakeFirst();
179
+ if (!inst || inst.status !== 'running')
180
+ throw cageError('INVALID_TRANSITION');
181
+ const actor = await this.k.contextFor(userId, tenantId), binding = this.k.registry.approvalByProcess(inst.process).a;
182
+ const command = outcome === 'approved' ? binding.onApproved : outcome === 'returned' ? binding.onReturned : binding.onRejected;
183
+ if (!actor || !command || !actor.capabilities.has(this.k.registry.capabilityOf(command)))
184
+ throw cageError('FORBIDDEN');
185
+ await this.k.read.get(binding.agg, inst.business_key, actor);
186
+ const task = await tx.selectFrom('flow.approval_task').selectAll().where('id', '=', taskId).forUpdate().executeTakeFirst();
187
+ // 不是自己的待办一律 404,不泄露存在性
188
+ if (!task || task.assignee !== userId)
189
+ throw cageError('NOT_FOUND');
190
+ if (task.status !== 'pending')
191
+ throw cageError('INVALID_TRANSITION', { status: task.status });
192
+ if (task.step !== inst.current_step)
193
+ throw cageError('INVALID_TRANSITION');
194
+ await tx.updateTable('flow.approval_task').set({ status: outcome, comment: comment ?? null, decided_at: sql `now()` }).where('id', '=', taskId).execute();
195
+ const steps = inst.steps;
196
+ if (outcome !== 'approved')
197
+ return this.finish(tx, inst, outcome, userId);
198
+ const s = steps[task.step];
199
+ if (s.kind !== 'ai' && s.mode === 'all') {
200
+ const left = await tx.selectFrom('flow.approval_task').select('id')
201
+ .where('instance_id', '=', inst.id).where('step', '=', task.step).where('status', '=', 'pending').execute();
202
+ if (left.length > 0)
203
+ return { finished: false };
204
+ }
205
+ else {
206
+ await tx.updateTable('flow.approval_task').set({ status: 'skipped' })
207
+ .where('instance_id', '=', inst.id).where('step', '=', task.step).where('status', '=', 'pending').execute();
208
+ }
209
+ return this.advance(tx, inst, userId);
210
+ }
211
+ /** 先执行带版本的领域结论命令,成功后才关闭剩余待办并写流程终态,保证两边不会各自成功。 */
212
+ async finish(tx, inst, result, userId) {
213
+ await applyOutcome(this.k, { process: inst.process, tenantId: inst.tenant_id, businessKey: inst.business_key, outcome: result,
214
+ lastApprover: userId, expectedVersion: inst.subject_version ?? undefined }, tx);
215
+ await tx.updateTable('flow.approval_task').set({ status: 'skipped' }).where('instance_id', '=', inst.id).where('status', '=', 'pending').execute();
216
+ await tx.updateTable('flow.approval_step').set({ status: result, updated_at: sql `now()` }).where('instance_id', '=', inst.id).where('step', '=', inst.current_step).execute();
217
+ await tx.updateTable('flow.approval_instance').set({ status: result, last_approver: userId ?? null,
218
+ last_actor: JSON.stringify(userId ? { kind: 'user', id: userId } : { kind: 'process', id: inst.process }), updated_at: sql `now()` }).where('id', '=', inst.id).execute();
219
+ return { finished: true, outcome: result };
220
+ }
221
+ /** 将当前节点标通过并开启下一步;最后一步转 finish,因此 AI pass 也不必然立即批准整张单据。 */
222
+ async advance(tx, inst, userId) {
223
+ await tx.updateTable('flow.approval_step').set({ status: 'approved', updated_at: sql `now()` }).where('instance_id', '=', inst.id).where('step', '=', inst.current_step).execute();
224
+ const steps = inst.steps;
225
+ const next = inst.current_step + 1;
226
+ if (next >= steps.length)
227
+ return this.finish(tx, inst, 'approved', userId);
228
+ await tx.updateTable('flow.approval_instance').set({ current_step: next, last_approver: userId ?? null, updated_at: sql `now()` }).where('id', '=', inst.id).execute();
229
+ await this.openStep(tx, inst.id, inst.tenant_id, steps, next, inst.vars, inst.subject_version ?? undefined);
230
+ return { finished: false };
231
+ }
232
+ /** 只有仍然占据该节点的 run 能提交;调用方需在同一事务核验 AI run 租约。 */
233
+ /**
234
+ * 锁定实例、节点和业务对象,校验 run 仍占据当前节点且对象版本未变;旧轮次/旧 run 回执一律冲突。
235
+ * 保存判定时同时保存敏感明文/查询范围证据,history 据此决定后来读者能否查看理由。
236
+ * manual 仅开人工接管待办;return/ pass 分别进入共同 finish/advance,保持领域命令约束不变。
237
+ */
238
+ async completeAi(tx, stepId, runId, decision, actor) {
239
+ const ref = await tx.selectFrom('flow.approval_step').select('instance_id').where('id', '=', stepId).executeTakeFirstOrThrow();
240
+ const inst = await tx.selectFrom('flow.approval_instance').selectAll().where('id', '=', ref.instance_id).where('process', '=', this.route.process).forUpdate().executeTakeFirstOrThrow();
241
+ const current = await tx.selectFrom('flow.approval_step').selectAll().where('id', '=', stepId).forUpdate().executeTakeFirstOrThrow();
242
+ if (inst.status !== 'running' || current.step !== inst.current_step || current.run_id !== runId || !['running', 'queued'].includes(current.status))
243
+ throw cageError('CONFLICT');
244
+ const node = inst.steps[current.step];
245
+ if (node.kind !== 'ai')
246
+ throw cageError('INVALID_TRANSITION');
247
+ // 中间 AI 步骤同样检查提交版本,防止旧判定推进新内容;最后的命令仍进行版本校验。
248
+ const binding = this.k.registry.approvalByProcess(inst.process).a;
249
+ const subject = await tx.selectFrom(`${binding.agg.table.schema}.${binding.agg.table.name}`).select('version')
250
+ .where('id', '=', inst.business_key).where('tenant_id', '=', inst.tenant_id).forUpdate().executeTakeFirst();
251
+ if (!subject || (inst.subject_version !== null && subject.version !== inst.subject_version))
252
+ throw cageError('CONFLICT');
253
+ const who = actor?.actor ?? (decision.decision === 'manual' ? { kind: 'process', id: `process:${inst.process}:handoff` }
254
+ : (await approvalRoleContext(this.k, inst.process, inst.tenant_id)).actor);
255
+ const queryReads = [...new Set((node.context.queries ?? []).flatMap(name => this.k.registry.queries.find(q => q.name === name)?.reads.map(a => a.name) ?? []))];
256
+ const visibility = actor ? { userId: actor.userId, unmask: actor.unmask, queries: queryReads.map(aggregate => ({ aggregate, scopes: grantsFor(actor, aggregate) })) } : null;
257
+ await tx.updateTable('flow.approval_step').set({ decision: JSON.stringify(decision), decision_visibility: JSON.stringify(visibility), actor: JSON.stringify(who), updated_at: sql `now()` }).where('id', '=', stepId).execute();
258
+ if (decision.decision === 'manual') {
259
+ await this.openTasks(tx, inst.id, inst.tenant_id, current.step, node.title, node.handoff, inst.vars);
260
+ await tx.updateTable('flow.approval_step').set({ status: 'waiting_manual' }).where('id', '=', stepId).execute();
146
261
  return { finished: false };
147
- });
262
+ }
263
+ if (decision.decision === 'return')
264
+ return this.finish(tx, inst, 'returned');
265
+ return this.advance(tx, inst);
148
266
  }
149
267
  }
150
268
  //# sourceMappingURL=engine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { type Ctx, type Kernel } from '@yss123/cage-kernel';
2
2
  import type { FastifyInstance } from 'fastify';
3
3
  import type { InCodeEngine } from './engine.js';
4
+ /**
5
+ * 在宿主 Fastify 会话/CSRF 边界内注册我的待办、对象审批历史及三种人工决定接口。
6
+ * 路由只接受真人用户,历史先验证对象 read;决定由目标引擎再次核对租户、待办归属、当前步骤和能力。
7
+ * 多个引擎的待办按时间合并,comment 最多 1000 字;HTTP 层不直接推进流程或改写业务表。
8
+ */
4
9
  export declare function approvalRoutes(app: FastifyInstance, opts: {
5
10
  kernel: Kernel;
6
11
  engines: InCodeEngine[];
package/dist/http.js CHANGED
@@ -1,5 +1,10 @@
1
1
  // 待办接口:GET /api/approval/tasks、POST /api/approval/tasks/:id/approve|reject
2
2
  import { cageError } from '@yss123/cage-kernel';
3
+ /**
4
+ * 在宿主 Fastify 会话/CSRF 边界内注册我的待办、对象审批历史及三种人工决定接口。
5
+ * 路由只接受真人用户,历史先验证对象 read;决定由目标引擎再次核对租户、待办归属、当前步骤和能力。
6
+ * 多个引擎的待办按时间合并,comment 最多 1000 字;HTTP 层不直接推进流程或改写业务表。
7
+ */
3
8
  export function approvalRoutes(app, opts) {
4
9
  const user = (req) => {
5
10
  const ctx = opts.ctxOf(req);
@@ -13,7 +18,12 @@ export function approvalRoutes(app, opts) {
13
18
  const lists = await Promise.all(opts.engines.map((e) => e.tasksOf(ctx.userId, ctx.tenantId, status)));
14
19
  return lists.flat().sort((a, b) => b.createdAt.localeCompare(a.createdAt));
15
20
  });
16
- for (const outcome of ['approve', 'reject']) {
21
+ app.get('/api/approval/history/:aggregate/:id', async (req) => {
22
+ const ctx = user(req), params = req.params;
23
+ await opts.kernel.read.get(params.aggregate, params.id, ctx);
24
+ return (await Promise.all(opts.engines.map(engine => engine.history(params.aggregate, params.id, ctx)))).flat();
25
+ });
26
+ for (const outcome of ['approve', 'reject', 'return']) {
17
27
  app.post(`/api/approval/tasks/:id/${outcome}`, async (req) => {
18
28
  const ctx = user(req);
19
29
  const id = req.params.id;
@@ -24,7 +34,7 @@ export function approvalRoutes(app, opts) {
24
34
  const owns = await opts.kernel.app.selectFrom('flow.approval_task as t').innerJoin('flow.approval_instance as i', 'i.id', 't.instance_id')
25
35
  .select('t.id').where('t.id', '=', id).where('i.process', '=', e.route.process).executeTakeFirst();
26
36
  if (owns)
27
- return e.decide(id, ctx.userId, ctx.tenantId, outcome === 'approve' ? 'approved' : 'rejected', comment);
37
+ return e.decide(id, ctx.userId, ctx.tenantId, outcome === 'approve' ? 'approved' : outcome === 'return' ? 'returned' : 'rejected', comment);
28
38
  }
29
39
  throw cageError('NOT_FOUND');
30
40
  });
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAC;AAIvE,MAAM,UAAU,cAAc,CAAC,GAAoB,EAAE,IAA2E;IAC9H,MAAM,IAAI,GAAG,CAAC,GAAQ,EAAE,EAAE;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,MAAM,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC/F,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,MAAM,GAAI,GAAG,CAAC,KAAa,EAAE,MAAM,IAAI,SAAS,CAAC;QACvD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACvG,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,2BAA2B,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,MAAM,EAAE,GAAI,GAAG,CAAC,MAAc,CAAC,EAAY,CAAC;YAC5C,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,OAAQ,GAAG,CAAC,IAAY,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAE,GAAG,CAAC,IAAY,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9H,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,EAAE,eAAe,CAAC;qBACvI,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,CAAC;gBACrG,IAAI,IAAI;oBAAE,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,MAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACrH,CAAC;YACD,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,11 @@
1
+ /**
2
+ * 应用声明路由/绑定,HTTP 装配待办接口,worker 装配启动/取消消费者;三者使用同一流程标识。
3
+ * 结论必须经过桥接命令,不能因为引擎已经判断通过就绕过领域状态、权限、版本或不变量。
4
+ */
1
5
  export * from './types.js';
2
6
  export { applyOutcome, bindApproval } from './bridge.js';
3
7
  export { route, step, when, approver, gt, gte, lt, lte, eq, ne, isIn, evalCondition, resolveSteps, FLOW_DDL, type Route, type RouteNode, type Step, type When, type Approver, type Condition, } from './route.js';
4
8
  export { InCodeEngine, type TaskView } from './engine.js';
5
9
  export { approvalRoutes } from './http.js';
10
+ export { approvalRoleContext, type AiStep, type AiStepOptions, type AiDecision } from './ai.js';
11
+ export type { ApprovalStep } from './route.js';
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  // @yss123/cage-approval(可选):审批适配接口、桥接与代码内封闭路由。
2
+ /**
3
+ * 应用声明路由/绑定,HTTP 装配待办接口,worker 装配启动/取消消费者;三者使用同一流程标识。
4
+ * 结论必须经过桥接命令,不能因为引擎已经判断通过就绕过领域状态、权限、版本或不变量。
5
+ */
2
6
  export * from './types.js';
3
7
  export { applyOutcome, bindApproval } from './bridge.js';
4
8
  export { route, step, when, approver, gt, gte, lt, lte, eq, ne, isIn, evalCondition, resolveSteps, FLOW_DDL, } from './route.js';
5
9
  export { InCodeEngine } from './engine.js';
6
10
  export { approvalRoutes } from './http.js';
11
+ export { approvalRoleContext } from './ai.js';
7
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,GAEnG,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C;;;GAGG;AACH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,GAEnG,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAoD,MAAM,SAAS,CAAC"}
package/dist/route.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { ROUTE, SYSTEM_DDL, type Kernel } from '@yss123/cage-kernel';
1
+ import { ROUTE, SYSTEM_DDL, MIGRATIONS, type Kernel, type Registry } from '@yss123/cage-kernel';
2
2
  import { InCodeEngine } from './engine.js';
3
+ import { type AiStep, type AiStepOptions } from './ai.js';
4
+ /** 封闭选人策略,仅决定候选执行人;被选中不代表拥有目标业务命令能力、数据范围或额度。 */
3
5
  export type Approver = {
4
6
  kind: 'deptLeader';
5
7
  field: string;
@@ -19,6 +21,7 @@ export declare const approver: {
19
21
  /** 指定用户 */
20
22
  user: (userId: string) => Approver;
21
23
  };
24
+ /** 可静态分析的分支条件;field 应进入 approval.routable,运行时只读取流程变量。 */
22
25
  export type Condition = {
23
26
  op: 'gt' | 'gte' | 'lt' | 'lte';
24
27
  field: string;
@@ -32,6 +35,7 @@ export type Condition = {
32
35
  field: string;
33
36
  value: (string | number)[];
34
37
  };
38
+ /** 数值比较组合子将业务字段和阈值保留在声明中,金额字符串在 evalCondition 中转换比较。 */
35
39
  export declare const gt: (field: string, value: number) => Condition;
36
40
  export declare const gte: (field: string, value: number) => Condition;
37
41
  export declare const lt: (field: string, value: number) => Condition;
@@ -39,6 +43,7 @@ export declare const lte: (field: string, value: number) => Condition;
39
43
  export declare const eq: (field: string, value: string | number | boolean) => Condition;
40
44
  export declare const ne: (field: string, value: string | number | boolean) => Condition;
41
45
  export declare const isIn: (field: string, value: (string | number)[]) => Condition;
46
+ /** 人工节点;同一节点可生成多位候选人的待办,mode 决定通过后是否等待剩余人。 */
42
47
  export interface Step {
43
48
  kind: 'step';
44
49
  title: string;
@@ -46,19 +51,29 @@ export interface Step {
46
51
  /** any:任意一人通过即可(或签);all:全部通过(会签) */
47
52
  mode: 'any' | 'all';
48
53
  }
54
+ /** 条件满足才展开内部步骤;未满足时跳过这一分支,不生成空待办。 */
49
55
  export interface When {
50
56
  kind: 'when';
51
57
  cond: Condition;
52
- steps: Step[];
58
+ steps: ApprovalStep[];
53
59
  }
54
- export type RouteNode = Step | When;
60
+ export type ApprovalStep = Step | AiStep;
61
+ export type RouteNode = ApprovalStep | When;
62
+ /**
63
+ * 同一个步骤原语支持人工和 AI 两类执行方式。AI 必须声明模型、要求、取证字段和人工接管规则,
64
+ * 正数预算在声明时检查;人工默认 any。此处只描述节点,不创建实例或执行模型请求。
65
+ */
55
66
  export declare function step(title: string, who: Approver, opts?: {
56
67
  mode?: 'any' | 'all';
57
68
  }): Step;
58
- export declare function when(cond: Condition, ...steps: Step[]): When;
69
+ export declare function step(title: string, who: AiStepOptions): AiStep;
70
+ /** 将一组顺序步骤置于同一个条件下,条件求值发生在实例创建时。 */
71
+ export declare function when(cond: Condition, ...steps: ApprovalStep[]): When;
72
+ /** 使用封闭运算符求值,不执行任意表达式;比较转换规则固定,业务阈值边界应另有回归用例。 */
59
73
  export declare function evalCondition(c: Condition, vars: Record<string, unknown>): boolean;
60
74
  /** 按流程变量展开出实际要走的步骤 */
61
- export declare function resolveSteps(nodes: readonly RouteNode[], vars: Record<string, unknown>): Step[];
75
+ export declare function resolveSteps(nodes: readonly RouteNode[], vars: Record<string, unknown>): ApprovalStep[];
76
+ /** 代码路由兼具可检查规格与引擎工厂;实例/待办表由迁移准备,注册路由本身不会访问数据库。 */
62
77
  export interface Route {
63
78
  readonly [ROUTE]: true;
64
79
  readonly process: string;
@@ -68,7 +83,18 @@ export interface Route {
68
83
  /** 代码内引擎(worker 与 HTTP 层使用) */
69
84
  readonly engine: (k: Kernel) => InCodeEngine;
70
85
  readonly [SYSTEM_DDL]: () => string;
86
+ readonly [MIGRATIONS]: () => readonly {
87
+ name: string;
88
+ sql: string;
89
+ }[];
90
+ validate(registry: Registry): void;
71
91
  }
92
+ /**
93
+ * 构造流程声明并收集条件/部门选人字段,用于图谱依赖检查;按 kernel 与 process 缓存引擎实例。
94
+ * 注册表完成后 validate 继续核对 AI 运行时、自动角色、退回命令和取证字段/查询,尽早暴露缺失声明。
95
+ * nodes 保存原有步骤顺序,业务实例创建时会冻结展开后的步骤,后续不会随代码分支重新求值。
96
+ */
72
97
  export declare function route(process: string, nodes: RouteNode[]): Route;
73
98
  /** 代码内引擎自己的表,放在 flow 模式;引擎没有业务表的写权限 */
99
+ /** 此 SQL 是已发布扩展 DDL 的内容摘要来源;新增字段通过独立 MIGRATIONS 追加,不能改写历史字符串。 */
74
100
  export declare const FLOW_DDL = "\nCREATE TABLE IF NOT EXISTS flow.approval_instance (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL,\n process text NOT NULL,\n aggregate text NOT NULL,\n business_key uuid NOT NULL,\n vars jsonb NOT NULL,\n steps jsonb NOT NULL,\n current_step int NOT NULL DEFAULT 0,\n status text NOT NULL DEFAULT 'running',\n last_approver uuid,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS approval_instance_key_idx ON flow.approval_instance (tenant_id, business_key, status);\nCREATE TABLE IF NOT EXISTS flow.approval_task (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n instance_id uuid NOT NULL REFERENCES flow.approval_instance(id),\n tenant_id uuid NOT NULL,\n step int NOT NULL,\n title text NOT NULL,\n assignee uuid NOT NULL,\n status text NOT NULL DEFAULT 'pending',\n comment text,\n decided_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS approval_task_assignee_idx ON flow.approval_task (tenant_id, assignee, status);\nGRANT SELECT, INSERT, UPDATE ON flow.approval_instance, flow.approval_task TO cage_app;\n";
package/dist/route.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // 代码内封闭路由:一小组固定的组合子(step、when、gt、approver.*),和范围组合子一样只能从里面选,
2
2
  // 所以抽取器能读懂:改 amount 的口径时,影响分析会把这条路由列出来。
3
- import { ROUTE, SYSTEM_DDL, declError } from '@yss123/cage-kernel';
3
+ import { ROUTE, SYSTEM_DDL, MIGRATIONS, declError } from '@yss123/cage-kernel';
4
4
  import { InCodeEngine } from './engine.js';
5
+ import { APPROVAL_AI_MIGRATION } from './ai.js';
5
6
  /** 审批人组合子 */
6
7
  export const approver = {
7
8
  /** 单据某个部门字段对应部门的负责人 */
@@ -11,6 +12,7 @@ export const approver = {
11
12
  /** 指定用户 */
12
13
  user: (userId) => ({ kind: 'user', userId }),
13
14
  };
15
+ /** 数值比较组合子将业务字段和阈值保留在声明中,金额字符串在 evalCondition 中转换比较。 */
14
16
  export const gt = (field, value) => ({ op: 'gt', field, value });
15
17
  export const gte = (field, value) => ({ op: 'gte', field, value });
16
18
  export const lt = (field, value) => ({ op: 'lt', field, value });
@@ -19,11 +21,21 @@ export const eq = (field, value) => ({ op: 'eq', field, value });
19
21
  export const ne = (field, value) => ({ op: 'ne', field, value });
20
22
  export const isIn = (field, value) => ({ op: 'in', field, value });
21
23
  export function step(title, who, opts = {}) {
24
+ if (who.kind === 'ai') {
25
+ if (!who.model?.trim() || !who.instructions?.trim() || !who.handoff || !who.context?.fields.length)
26
+ throw declError('CAGE-APPR-006', 'AI 步骤需要模型、审核要求、上下文字段和接管规则');
27
+ for (const value of Object.values(who.limits ?? {}))
28
+ if (!Number.isSafeInteger(value) || value <= 0)
29
+ throw declError('CAGE-APPR-006', 'AI 步骤运行预算必须是正整数');
30
+ return Object.freeze({ ...who, title, context: { fields: [...who.context.fields], queries: [...(who.context.queries ?? [])] } });
31
+ }
22
32
  return { kind: 'step', title, approver: who, mode: opts.mode ?? 'any' };
23
33
  }
34
+ /** 将一组顺序步骤置于同一个条件下,条件求值发生在实例创建时。 */
24
35
  export function when(cond, ...steps) {
25
36
  return { kind: 'when', cond, steps };
26
37
  }
38
+ /** 使用封闭运算符求值,不执行任意表达式;比较转换规则固定,业务阈值边界应另有回归用例。 */
27
39
  export function evalCondition(c, vars) {
28
40
  const v = vars[c.field];
29
41
  switch (c.op) {
@@ -41,7 +53,7 @@ export function evalCondition(c, vars) {
41
53
  export function resolveSteps(nodes, vars) {
42
54
  const out = [];
43
55
  for (const n of nodes) {
44
- if (n.kind === 'step')
56
+ if (n.kind !== 'when')
45
57
  out.push(n);
46
58
  else if (evalCondition(n.cond, vars))
47
59
  out.push(...n.steps);
@@ -49,6 +61,11 @@ export function resolveSteps(nodes, vars) {
49
61
  return out;
50
62
  }
51
63
  const engines = new WeakMap();
64
+ /**
65
+ * 构造流程声明并收集条件/部门选人字段,用于图谱依赖检查;按 kernel 与 process 缓存引擎实例。
66
+ * 注册表完成后 validate 继续核对 AI 运行时、自动角色、退回命令和取证字段/查询,尽早暴露缺失声明。
67
+ * nodes 保存原有步骤顺序,业务实例创建时会冻结展开后的步骤,后续不会随代码分支重新求值。
68
+ */
52
69
  export function route(process, nodes) {
53
70
  if (!/^[a-z][a-z0-9-]*$/.test(process))
54
71
  throw declError('CAGE-APPR-005', `流程名 ${process} 必须是小写短横线`);
@@ -56,12 +73,14 @@ export function route(process, nodes) {
56
73
  throw declError('CAGE-APPR-006', `流程 ${process} 至少需要一个步骤`);
57
74
  const fields = new Set();
58
75
  for (const n of nodes) {
59
- const steps = n.kind === 'step' ? [n] : n.steps;
76
+ const steps = n.kind !== 'when' ? [n] : n.steps;
60
77
  if (n.kind === 'when')
61
78
  fields.add(n.cond.field);
62
- for (const s of steps)
63
- if (s.approver.kind === 'deptLeader')
64
- fields.add(s.approver.field);
79
+ for (const s of steps) {
80
+ const selector = s.kind === 'ai' ? s.handoff : s.approver;
81
+ if (selector.kind === 'deptLeader')
82
+ fields.add(selector.field);
83
+ }
65
84
  }
66
85
  const r = Object.freeze({
67
86
  [ROUTE]: true,
@@ -78,10 +97,30 @@ export function route(process, nodes) {
78
97
  return e;
79
98
  },
80
99
  [SYSTEM_DDL]: () => FLOW_DDL,
100
+ [MIGRATIONS]: () => [APPROVAL_AI_MIGRATION],
101
+ validate: (registry) => {
102
+ const aiSteps = nodes.flatMap(n => n.kind === 'when' ? n.steps : [n]).filter((s) => s.kind === 'ai');
103
+ if (!aiSteps.length)
104
+ return;
105
+ if (!registry.extensions.some(e => e.kind === 'RUNTIME' && e.value.name === 'ai'))
106
+ throw declError('CAGE-APPR-006', 'AI 审批必须启用 cage-ai 运行时');
107
+ const binding = registry.approvalByProcess(process)?.a;
108
+ if (!binding?.onReturned || !binding.automatedBy)
109
+ throw declError('CAGE-APPR-006', `AI 审批 ${process} 必须绑定 onReturned 和 automatedBy`);
110
+ for (const s of aiSteps) {
111
+ for (const field of s.context.fields)
112
+ if (!binding.agg.fields[field] || binding.agg.fields[field].isHidden)
113
+ throw declError('CAGE-APPR-001', `AI 上下文字段 ${field} 不存在或不可公开`);
114
+ for (const query of s.context.queries ?? [])
115
+ if (!registry.queries.some(q => q.name === query))
116
+ throw declError('CAGE-REG-001', `AI 步骤引用的查询 ${query} 未导出`);
117
+ }
118
+ },
81
119
  });
82
120
  return r;
83
121
  }
84
122
  /** 代码内引擎自己的表,放在 flow 模式;引擎没有业务表的写权限 */
123
+ /** 此 SQL 是已发布扩展 DDL 的内容摘要来源;新增字段通过独立 MIGRATIONS 追加,不能改写历史字符串。 */
85
124
  export const FLOW_DDL = `
86
125
  CREATE TABLE IF NOT EXISTS flow.approval_instance (
87
126
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
package/dist/route.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../src/route.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../src/route.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import type { ApprovalEngine } from '@yss123/cage-kernel/worker';
2
+ /** 引擎交给桥接层的业务结论;结果不会直接更新业务表,必须解析到绑定命令重新校验。 */
2
3
  export interface ApprovalOutcome {
3
4
  process: string;
4
5
  tenantId: string;
5
6
  /** 单据 id */
6
7
  businessKey: string;
7
- outcome: 'approved' | 'rejected';
8
+ outcome: 'approved' | 'rejected' | 'returned';
8
9
  /** 最后一位审批人:结论命令以流程身份执行,代表这个人(职责分离、额度按他判断) */
9
- lastApprover: string;
10
+ lastApprover?: string;
11
+ /** 审批所针对的单据提交版本;提供时命令拒绝把旧审批结论应用到已变化的对象。 */
12
+ expectedVersion?: number;
10
13
  }
14
+ /** 外部引擎适配约定;调用方负责可信回调来源,桥接层负责重新核对结论命令及真人权限。 */
11
15
  export interface ApprovalAdapter extends ApprovalEngine {
12
16
  /** 注册流程结束回调 */
13
17
  onCompleted(cb: (r: ApprovalOutcome) => Promise<void>): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yss123/cage-approval",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "审批适配接口、桥接与代码内封闭路由(可选)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "fastify": "^5.6.0",
19
19
  "kysely": "^0.28.5",
20
20
  "zod": "^4.1.5",
21
- "@yss123/cage-kernel": "0.2.0"
21
+ "@yss123/cage-kernel": "0.3.0"
22
22
  },
23
23
  "license": "MIT",
24
24
  "repository": {