@yss123/cage-approval 0.3.0 → 0.3.1
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 +13 -0
- package/dist/bridge.js +8 -4
- package/dist/bridge.js.map +1 -1
- package/dist/deadlines.d.ts +29 -0
- package/dist/deadlines.js +72 -0
- package/dist/deadlines.js.map +1 -0
- package/dist/engine.d.ts +86 -10
- package/dist/engine.js +205 -116
- package/dist/engine.js.map +1 -1
- package/dist/execution.d.ts +41 -0
- package/dist/execution.js +85 -0
- package/dist/execution.js.map +1 -0
- package/dist/http.d.ts +2 -1
- package/dist/http.js +73 -12
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/openapi.d.ts +17 -0
- package/dist/openapi.js +71 -0
- package/dist/openapi.js.map +1 -0
- package/dist/operations-schema.d.ts +20 -0
- package/dist/operations-schema.js +20 -0
- package/dist/operations-schema.js.map +1 -0
- package/dist/operations.d.ts +118 -0
- package/dist/operations.js +349 -0
- package/dist/operations.js.map +1 -0
- package/dist/route.d.ts +59 -7
- package/dist/route.js +65 -27
- package/dist/route.js.map +1 -1
- package/dist/selectors.d.ts +8 -0
- package/dist/selectors.js +53 -0
- package/dist/selectors.js.map +1 -0
- package/dist/task-actions.d.ts +94 -0
- package/dist/task-actions.js +198 -0
- package/dist/task-actions.js.map +1 -0
- package/dist/task-stages.d.ts +17 -0
- package/dist/task-stages.js +47 -0
- package/dist/task-stages.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用审批运营运行时:worker 推进持久临期/超时提醒,HTTP/SDK 查询收件箱、本人申请和催办。
|
|
3
|
+
* 写操作统一按实例→步骤/任务加锁,接收人实时核对授权、范围与当前责任;站内提醒不会自动通过、驳回或调用外部消息通道。
|
|
4
|
+
* 本服务只使用宿主连接池;节点时限在数据库保存,跨进程靠实例锁与通知唯一键去重,停机后恢复只发送当前有效阶段。
|
|
5
|
+
*/
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { RUNTIME, MIGRATIONS, cageError, declError, loadUserGrants, userCtx } from '@yss123/cage-kernel';
|
|
8
|
+
import { sql } from 'kysely';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { approvalUrgeSchema } from './operations-schema.js';
|
|
11
|
+
import { authorizedTaskActor, recordTaskAction } from './task-actions.js';
|
|
12
|
+
import { resolveApprovers } from './selectors.js';
|
|
13
|
+
import { ACTIVE_STEP_STATUSES } from './execution.js';
|
|
14
|
+
const noticeOptionsSchema = z.strictObject({ after: z.string().regex(/^(0|[1-9]\d{0,18})$/)
|
|
15
|
+
.refine(value => BigInt(value) <= 9223372036854775807n).optional(),
|
|
16
|
+
limit: z.number().int().min(1).max(200).optional(), unreadOnly: z.boolean().optional() });
|
|
17
|
+
const services = new WeakMap();
|
|
18
|
+
/** 有效时间统一按 UTC 序列化;数据库 null 保持为空,避免把无时限误显示为 1970 年。 */
|
|
19
|
+
function timestamp(value) { return value == null ? null : new Date(value).toISOString(); }
|
|
20
|
+
/** 仅把权限拒绝当作不适合接收;数据库、事务和未知异常必须回滚交给 worker 重试。 */
|
|
21
|
+
function permissionDenied(error) {
|
|
22
|
+
return error instanceof Error && 'code' in error && ['FORBIDDEN', 'NOT_FOUND'].includes(String(error.code));
|
|
23
|
+
}
|
|
24
|
+
/** 当前实名用户重新加载角色与状态;传入的旧会话上下文不能让撤权/停用继续读取提醒。 */
|
|
25
|
+
async function currentActor(tx, ctx) {
|
|
26
|
+
if (ctx.actor?.kind !== 'user' || !ctx.userId)
|
|
27
|
+
throw cageError('FORBIDDEN');
|
|
28
|
+
const grants = await loadUserGrants(tx, ctx.userId, ctx.tenantId);
|
|
29
|
+
if (!grants)
|
|
30
|
+
throw cageError('FORBIDDEN');
|
|
31
|
+
return userCtx(grants);
|
|
32
|
+
}
|
|
33
|
+
/** 单据版本只读复核;原生审批不会为已变更业务对象继续发送可操作提醒,不直接修改其状态。 */
|
|
34
|
+
async function currentSubject(k, tx, inst) {
|
|
35
|
+
const binding = k.registry.approvalByProcess(inst.process)?.a;
|
|
36
|
+
if (!binding)
|
|
37
|
+
return false;
|
|
38
|
+
const row = await tx.selectFrom(`${binding.agg.table.schema}.${binding.agg.table.name}`).select('version')
|
|
39
|
+
.where('id', '=', inst.business_key).where('tenant_id', '=', inst.tenant_id).executeTakeFirst();
|
|
40
|
+
return !!row && (inst.subject_version === null || row.version === inst.subject_version);
|
|
41
|
+
}
|
|
42
|
+
/** 读取当前申请人可见性而不获取业务明文;字段身份取实例快照,权限和范围始终取当前组织数据。 */
|
|
43
|
+
async function visibleRequest(k, tx, inst, actor, checkVersion = true) {
|
|
44
|
+
const policy = inst.reminder_policy;
|
|
45
|
+
const binding = k.registry.approvalByProcess(inst.process)?.a;
|
|
46
|
+
if (!policy || inst.vars[policy.requesterField] !== actor.userId || !binding || !actor.capabilities.has(`${binding.agg.name}.read`))
|
|
47
|
+
return false;
|
|
48
|
+
if (checkVersion && !(await currentSubject(k, tx, inst)))
|
|
49
|
+
return false;
|
|
50
|
+
return !!(await k.read.tx(actor, read => read.selectFrom(`safe.${binding.agg.table.name}`).select('id')
|
|
51
|
+
.where('id', '=', inst.business_key).executeTakeFirst()));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 从当前节点 pending 票解析接收人,waiting 不催办;委派时受托人与原责任人均可接收。
|
|
55
|
+
* 未领候选按当前组织规则且排除同节点已参与者,与 claim 的独立审批票边界一致;每个目标还需实时批准/read/范围权限。
|
|
56
|
+
* 调用方持实例锁时,所有任务动作与此处串行;只读列表在事务内即时核验,不把历史接收人当作永久授权。
|
|
57
|
+
*/
|
|
58
|
+
async function recipients(k, tx, inst, onlyUserId, selection) {
|
|
59
|
+
if (inst.status !== 'running' || !(await currentSubject(k, tx, inst)))
|
|
60
|
+
return [];
|
|
61
|
+
const tasks = await tx.selectFrom('flow.approval_task as t')
|
|
62
|
+
.innerJoin('flow.approval_step as s', join => join.onRef('s.instance_id', '=', 't.instance_id').onRef('s.step', '=', 't.step'))
|
|
63
|
+
.selectAll('t').where('t.instance_id', '=', inst.id).where('t.tenant_id', '=', inst.tenant_id)
|
|
64
|
+
.where('s.status', 'in', [...ACTIVE_STEP_STATUSES])
|
|
65
|
+
.$if(selection?.step !== undefined, query => query.where('t.step', '=', selection.step)).orderBy('t.id').execute();
|
|
66
|
+
const result = [], authorized = new Map();
|
|
67
|
+
// 自动阶段只检查尚未投递的配对,每轮最多新增100条;后续扫描从唯一记录继续,不把上限当作丢弃尾部的规则。
|
|
68
|
+
// 角色解析仍会读取当前候选集合;本轮限制投递事务规模,不承诺大规模组织目录的分页性能。
|
|
69
|
+
const delivered = selection?.source ? await tx.selectFrom('flow.approval_notice').select(['task_id', 'recipient_id'])
|
|
70
|
+
.where('instance_id', '=', inst.id).where('source', '=', selection.source).execute() : [];
|
|
71
|
+
const existing = new Set(delivered.map(row => `${row.task_id}:${row.recipient_id}`));
|
|
72
|
+
const candidates = new Map();
|
|
73
|
+
for (const task of tasks) {
|
|
74
|
+
if (task.status !== 'pending')
|
|
75
|
+
continue;
|
|
76
|
+
const node = inst.steps[task.step];
|
|
77
|
+
let people;
|
|
78
|
+
if (task.assignee)
|
|
79
|
+
people = [...new Set([task.assignee, ...(task.delegation === 'pending' && task.owner_id ? [task.owner_id] : [])])];
|
|
80
|
+
else {
|
|
81
|
+
if (task.assignment !== 'candidate' || node.kind === 'ai' || node.assignment !== 'candidate')
|
|
82
|
+
continue;
|
|
83
|
+
if (!candidates.has(task.step))
|
|
84
|
+
candidates.set(task.step, await resolveApprovers(tx, node.approver, inst.vars, inst.tenant_id));
|
|
85
|
+
people = candidates.get(task.step).filter(id => !tasks.some(peer => peer.step === task.step && peer.id !== task.id && (peer.assignee === id || peer.owner_id === id)));
|
|
86
|
+
}
|
|
87
|
+
for (const userId of people) {
|
|
88
|
+
if (onlyUserId !== undefined && userId !== onlyUserId)
|
|
89
|
+
continue;
|
|
90
|
+
if (existing.has(`${task.id}:${userId}`))
|
|
91
|
+
continue;
|
|
92
|
+
if (!authorized.has(userId)) {
|
|
93
|
+
try {
|
|
94
|
+
await authorizedTaskActor(k, tx, inst, userId);
|
|
95
|
+
authorized.set(userId, true);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (!permissionDenied(error))
|
|
99
|
+
throw error;
|
|
100
|
+
authorized.set(userId, false);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (authorized.get(userId)) {
|
|
104
|
+
result.push({ taskId: task.id, userId, step: task.step });
|
|
105
|
+
if (selection && result.length >= selection.maxRecipients)
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 在持实例锁事务内按来源/任务/接收人插入;自动阶段使用固定 source,人工催办每次使用独立 UUID。
|
|
114
|
+
* 返回新增投递数,唯一冲突表示已送达,不重复写历史;任意写入失败会连同全部投递和冷却一起回滚。
|
|
115
|
+
*/
|
|
116
|
+
async function deliver(tx, inst, people, kind, source, actorId = null, reason = null) {
|
|
117
|
+
let count = 0;
|
|
118
|
+
for (const person of people) {
|
|
119
|
+
const row = await tx.insertInto('flow.approval_notice').values({ tenant_id: inst.tenant_id, instance_id: inst.id,
|
|
120
|
+
task_id: person.taskId, step: person.step, recipient_id: person.userId, kind, source, actor_id: actorId, reason })
|
|
121
|
+
.onConflict(conflict => conflict.columns(['instance_id', 'step', 'source', 'task_id', 'recipient_id']).doNothing())
|
|
122
|
+
.returning('id').executeTakeFirst();
|
|
123
|
+
if (row)
|
|
124
|
+
count++;
|
|
125
|
+
}
|
|
126
|
+
return count;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 原生审批的可选持久运营服务,公开方法重新校验内部调用输入,不依赖 HTTP 才执行校验。
|
|
130
|
+
* 实例锁与 SQL 唯一键支持多个 worker;本对象不持有额外连接、定时器或队列,close 无需关闭宿主资源。
|
|
131
|
+
*/
|
|
132
|
+
export class ApprovalOperationsService {
|
|
133
|
+
k;
|
|
134
|
+
constructor(k) {
|
|
135
|
+
this.k = k;
|
|
136
|
+
}
|
|
137
|
+
/** 仅扫描当前仍注册的原生流程;没有 ROUTE 的空应用启用运行时也不访问不存在的 flow 表。 */
|
|
138
|
+
processes() { return this.k.registry.extensions.filter(ext => ext.kind === 'ROUTE').map(ext => ext.value.process); }
|
|
139
|
+
/** 启动时验证新迁移可读;无原生路由时服务为空操作,不代替 migrate 自动建表。 */
|
|
140
|
+
async ready() {
|
|
141
|
+
if (!this.processes().length)
|
|
142
|
+
return;
|
|
143
|
+
await this.k.app.selectFrom('flow.approval_step').select(['due_at', 'next_notice_scan_at']).limit(0).execute();
|
|
144
|
+
}
|
|
145
|
+
/** 服务不创建独立资源,停机时由宿主 worker 等待在途事务并关闭宿主连接。 */
|
|
146
|
+
async close() { }
|
|
147
|
+
/**
|
|
148
|
+
* 每轮最多检查 25 个到期节点,实例 FOR UPDATE SKIP LOCKED 后再读节点与任务,保持既有锁序。
|
|
149
|
+
* 扫描时间持久后移一秒,已投递节点不会饿死后续节点;未找到当前合格人也保留后续补投机会。
|
|
150
|
+
* 超时优先于临期,停机跨过截止时不会同时补两阶段;新接收人下一轮补投,既有人由唯一键去重。
|
|
151
|
+
*/
|
|
152
|
+
async tick(signal) {
|
|
153
|
+
const processes = this.processes();
|
|
154
|
+
if (!processes.length || signal?.aborted)
|
|
155
|
+
return 0;
|
|
156
|
+
let delivered = 0;
|
|
157
|
+
for (let index = 0; index < 25 && !signal?.aborted; index++) {
|
|
158
|
+
const count = await this.k.app.transaction().execute(async (tx) => {
|
|
159
|
+
const inst = await tx.selectFrom('flow.approval_instance as i')
|
|
160
|
+
.innerJoin('flow.approval_step as s', 's.instance_id', 'i.id')
|
|
161
|
+
.selectAll('i').select('s.step as timer_step').where('i.process', 'in', processes).where('i.status', '=', 'running')
|
|
162
|
+
.where('s.status', '=', 'running').where('s.next_notice_scan_at', '<=', sql `now()`)
|
|
163
|
+
.orderBy('s.next_notice_scan_at').orderBy('i.id').forUpdate('i').skipLocked().limit(1).executeTakeFirst();
|
|
164
|
+
if (!inst)
|
|
165
|
+
return null;
|
|
166
|
+
const current = await tx.selectFrom('flow.approval_step').selectAll().where('instance_id', '=', inst.id)
|
|
167
|
+
.where('step', '=', inst.timer_step).forUpdate().executeTakeFirstOrThrow();
|
|
168
|
+
const clock = await sql `select clock_timestamp() as now`.execute(tx), now = new Date(clock.rows[0].now);
|
|
169
|
+
// 加锁等待期间节点可能推进;必须复核当前行,不能沿锁前关联结果把新步骤当成旧计时器处理。
|
|
170
|
+
if (current.status !== 'running' || current.due_at === null || current.next_notice_scan_at === null ||
|
|
171
|
+
new Date(current.next_notice_scan_at).getTime() > now.getTime() ||
|
|
172
|
+
new Date(current.remind_at ?? current.due_at).getTime() > now.getTime())
|
|
173
|
+
return 0;
|
|
174
|
+
const dueAt = new Date(current.due_at).getTime();
|
|
175
|
+
// 无效业务版本不会恢复,停止该节点后续扫描;状态仍由已有审批/领域命令流程负责。
|
|
176
|
+
if (!(await currentSubject(this.k, tx, inst))) {
|
|
177
|
+
await tx.updateTable('flow.approval_step').set({ next_notice_scan_at: null }).where('id', '=', current.id).execute();
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
await tx.updateTable('flow.approval_step').set({ next_notice_scan_at: new Date(now.getTime() + 1000) }).where('id', '=', current.id).execute();
|
|
181
|
+
const kind = dueAt <= now.getTime() ? 'overdue' : 'due_soon';
|
|
182
|
+
const count = await deliver(tx, inst, await recipients(this.k, tx, inst, undefined, { maxRecipients: 100, source: kind, step: current.step }), kind, kind);
|
|
183
|
+
if (count)
|
|
184
|
+
await recordTaskAction(tx, inst, null, null, 'reminded', null, { step: current.step, kind, recipientCount: count, dueAt: timestamp(current.due_at) });
|
|
185
|
+
return count;
|
|
186
|
+
});
|
|
187
|
+
if (count === null)
|
|
188
|
+
break;
|
|
189
|
+
delivered += count;
|
|
190
|
+
}
|
|
191
|
+
return delivered;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 本人收件箱按 bigint 游标递增分页,返回前重新核对当前任务接收关系与业务权限。
|
|
195
|
+
* 转出、候选被他人领取、任务结束、停用或撤权后对应内容不再可见;过滤不篡改原始通知或全局已读状态。
|
|
196
|
+
*/
|
|
197
|
+
async notices(ctx, options = {}) {
|
|
198
|
+
const parsed = noticeOptionsSchema.safeParse(options);
|
|
199
|
+
if (!parsed.success)
|
|
200
|
+
throw cageError('INVALID_INPUT');
|
|
201
|
+
return this.k.app.transaction().execute(async (tx) => {
|
|
202
|
+
const actor = await currentActor(tx, ctx), limit = parsed.data.limit ?? 50;
|
|
203
|
+
if (!this.processes().length)
|
|
204
|
+
return { items: [], nextCursor: null };
|
|
205
|
+
const rows = await tx.selectFrom('flow.approval_notice as n')
|
|
206
|
+
.innerJoin('flow.approval_instance as i', 'i.id', 'n.instance_id')
|
|
207
|
+
.innerJoin('flow.approval_step as s', join => join.onRef('s.instance_id', '=', 'i.id').onRef('s.step', '=', 'n.step'))
|
|
208
|
+
.select(['n.id', 'n.task_id', 'n.instance_id', 'n.step', 'n.kind', 'n.actor_id', 'n.reason', 'n.created_at', 'n.read_at', 's.due_at'])
|
|
209
|
+
.where('n.tenant_id', '=', actor.tenantId).where('i.tenant_id', '=', actor.tenantId).where('n.recipient_id', '=', actor.userId)
|
|
210
|
+
.where('n.id', '>', parsed.data.after ?? '0').where('i.status', '=', 'running').where('s.status', 'in', [...ACTIVE_STEP_STATUSES])
|
|
211
|
+
.$if(parsed.data.unreadOnly === true, query => query.where('n.read_at', 'is', null)).orderBy('n.id').limit(limit + 1).execute();
|
|
212
|
+
const items = [], cache = new Map();
|
|
213
|
+
for (const row of rows.slice(0, limit)) {
|
|
214
|
+
if (!cache.has(row.instance_id)) {
|
|
215
|
+
const inst = await tx.selectFrom('flow.approval_instance').selectAll().where('id', '=', row.instance_id)
|
|
216
|
+
.where('tenant_id', '=', actor.tenantId).executeTakeFirstOrThrow();
|
|
217
|
+
cache.set(row.instance_id, { inst, tasks: new Set((await recipients(this.k, tx, inst, actor.userId)).map(person => person.taskId)) });
|
|
218
|
+
}
|
|
219
|
+
const entry = cache.get(row.instance_id);
|
|
220
|
+
if (!entry.tasks.has(row.task_id))
|
|
221
|
+
continue;
|
|
222
|
+
items.push({ id: String(row.id), taskId: row.task_id, instanceId: row.instance_id, process: entry.inst.process,
|
|
223
|
+
aggregate: entry.inst.aggregate, businessKey: entry.inst.business_key, step: row.step, title: entry.inst.steps[row.step].title,
|
|
224
|
+
kind: row.kind, actorId: row.actor_id, reason: row.reason,
|
|
225
|
+
createdAt: timestamp(row.created_at), readAt: timestamp(row.read_at), dueAt: timestamp(row.due_at) });
|
|
226
|
+
}
|
|
227
|
+
return { items, nextCursor: rows.length > limit ? String(rows[limit - 1].id) : null };
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
/** 本人已读是幂等操作;先锁实例再核验接收资格,不能用他人 ID 或旧转办页面修改提醒。 */
|
|
231
|
+
async readNotice(id, ctx) {
|
|
232
|
+
if (!/^[1-9]\d{0,18}$/.test(id) || BigInt(id) > 9223372036854775807n)
|
|
233
|
+
throw cageError('INVALID_INPUT');
|
|
234
|
+
return this.k.app.transaction().execute(async (tx) => {
|
|
235
|
+
const actor = await currentActor(tx, ctx);
|
|
236
|
+
if (!this.processes().length)
|
|
237
|
+
throw cageError('NOT_FOUND');
|
|
238
|
+
const notice = await tx.selectFrom('flow.approval_notice').selectAll().where('id', '=', id).where('tenant_id', '=', actor.tenantId)
|
|
239
|
+
.where('recipient_id', '=', actor.userId).executeTakeFirst();
|
|
240
|
+
if (!notice)
|
|
241
|
+
throw cageError('NOT_FOUND');
|
|
242
|
+
const inst = await tx.selectFrom('flow.approval_instance').selectAll().where('id', '=', notice.instance_id)
|
|
243
|
+
.where('tenant_id', '=', actor.tenantId).forUpdate().executeTakeFirstOrThrow();
|
|
244
|
+
if (!(await recipients(this.k, tx, inst, actor.userId)).some(person => person.taskId === notice.task_id))
|
|
245
|
+
throw cageError('NOT_FOUND');
|
|
246
|
+
const row = await tx.updateTable('flow.approval_notice').set({ read_at: sql `coalesce(read_at, now())` }).where('id', '=', id)
|
|
247
|
+
.where('recipient_id', '=', actor.userId).returning('read_at').executeTakeFirstOrThrow();
|
|
248
|
+
return { id, readAt: timestamp(row.read_at) };
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/** 本人当前申请清单最多返回最近 200 项;当前 read/范围和提交版本均复核,旧实例不补造催办政策。 */
|
|
252
|
+
async requests(ctx) {
|
|
253
|
+
return this.k.app.transaction().execute(async (tx) => {
|
|
254
|
+
const actor = await currentActor(tx, ctx), processes = this.processes();
|
|
255
|
+
if (!processes.length)
|
|
256
|
+
return [];
|
|
257
|
+
const rows = await tx.selectFrom('flow.approval_instance as i')
|
|
258
|
+
.selectAll('i').where('i.tenant_id', '=', actor.tenantId).where('i.process', 'in', processes)
|
|
259
|
+
.where('i.status', '=', 'running').where(sql `i.vars ->> (i.reminder_policy ->> 'requesterField') = ${actor.userId}`)
|
|
260
|
+
.orderBy('i.created_at', 'desc').limit(200).execute();
|
|
261
|
+
const result = [];
|
|
262
|
+
for (const inst of rows)
|
|
263
|
+
if (await visibleRequest(this.k, tx, inst, actor)) {
|
|
264
|
+
const active = await tx.selectFrom('flow.approval_step').select(['step', 'due_at']).where('instance_id', '=', inst.id)
|
|
265
|
+
.where('status', 'in', [...ACTIVE_STEP_STATUSES]).orderBy('step').execute();
|
|
266
|
+
const activeSteps = active.map(row => ({ step: row.step, title: inst.steps[row.step].title, dueAt: timestamp(row.due_at),
|
|
267
|
+
branchPath: inst.steps[row.step].execution?.branchPath ?? [] }));
|
|
268
|
+
const deadlines = activeSteps.flatMap(row => row.dueAt ? [row.dueAt] : []).sort();
|
|
269
|
+
result.push({ id: inst.id, process: inst.process, aggregate: inst.aggregate, businessKey: inst.business_key,
|
|
270
|
+
currentStep: inst.current_step, currentSteps: activeSteps.map(row => row.step), activeSteps,
|
|
271
|
+
title: activeSteps.map(row => row.title).join(' / '), dueAt: deadlines[0] ?? null, nextUrgeAt: timestamp(inst.next_urge_at) });
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* 发起人催办当前实例/节点;锁内再次检查业务版本、本人字段、范围与冷却,收件人与说明不可由客户端越权指定。
|
|
278
|
+
* 一次成功催办独立来源去重,投递、不可覆盖动作历史与冷却同时提交;无可处理人工任务明确拒绝且不占冷却。
|
|
279
|
+
*/
|
|
280
|
+
async urge(instanceId, ctx, input) {
|
|
281
|
+
const parsed = approvalUrgeSchema.safeParse(input);
|
|
282
|
+
if (!z.uuid().safeParse(instanceId).success || !parsed.success)
|
|
283
|
+
throw cageError('INVALID_INPUT');
|
|
284
|
+
return this.k.app.transaction().execute(async (tx) => {
|
|
285
|
+
const actor = await currentActor(tx, ctx), processes = this.processes();
|
|
286
|
+
if (!processes.length)
|
|
287
|
+
throw cageError('NOT_FOUND');
|
|
288
|
+
const inst = await tx.selectFrom('flow.approval_instance').selectAll().where('id', '=', instanceId).where('tenant_id', '=', actor.tenantId)
|
|
289
|
+
.where('process', 'in', processes).forUpdate().executeTakeFirst();
|
|
290
|
+
if (!inst || !(await visibleRequest(this.k, tx, inst, actor, false)))
|
|
291
|
+
throw cageError('NOT_FOUND');
|
|
292
|
+
if (inst.status !== 'running')
|
|
293
|
+
throw cageError('INVALID_TRANSITION');
|
|
294
|
+
if (inst.current_step !== parsed.data.expectedStep)
|
|
295
|
+
throw cageError('CONFLICT', { reason: '审批节点已变化,请刷新后催办' });
|
|
296
|
+
// 兼容摘要可能在另一分支推进时保持不变;并行实例必须核对完整集合,避免旧页面催办新环节。
|
|
297
|
+
const active = await tx.selectFrom('flow.approval_step').select('step').where('instance_id', '=', inst.id)
|
|
298
|
+
.where('status', 'in', [...ACTIVE_STEP_STATUSES]).orderBy('step').execute();
|
|
299
|
+
const parallel = inst.steps.some(node => node.execution?.branchPath.length);
|
|
300
|
+
if (parallel && parsed.data.expectedSteps === undefined)
|
|
301
|
+
throw cageError('INVALID_INPUT', { reason: '并行催办必须携带 expectedSteps' });
|
|
302
|
+
if (parsed.data.expectedSteps && JSON.stringify(parsed.data.expectedSteps) !== JSON.stringify(active.map(row => row.step)))
|
|
303
|
+
throw cageError('CONFLICT', { reason: '并行审批环节已变化,请刷新后催办' });
|
|
304
|
+
if (!(await currentSubject(this.k, tx, inst)))
|
|
305
|
+
throw cageError('CONFLICT', { reason: '审批所针对的业务版本已变化,请刷新后处理' });
|
|
306
|
+
const clock = await sql `select clock_timestamp() as now`.execute(tx), now = new Date(clock.rows[0].now);
|
|
307
|
+
if (inst.next_urge_at && new Date(inst.next_urge_at).getTime() > now.getTime())
|
|
308
|
+
throw cageError('CONFLICT', { reason: '催办仍在冷却期内', nextUrgeAt: timestamp(inst.next_urge_at) });
|
|
309
|
+
const people = await recipients(this.k, tx, inst, undefined, { maxRecipients: 201 });
|
|
310
|
+
if (!people.length)
|
|
311
|
+
throw cageError('INVALID_TRANSITION', { reason: '当前节点没有可接收提醒的人工审批人' });
|
|
312
|
+
if (people.length > 200)
|
|
313
|
+
throw cageError('INVALID_TRANSITION', { reason: '单次人工催办最多支持200个任务接收人,请缩小当前节点的审批范围' });
|
|
314
|
+
const source = randomUUID(), recipientCount = await deliver(tx, inst, people, 'urge', source, actor.userId, parsed.data.reason);
|
|
315
|
+
const nextUrgeAt = new Date(now.getTime() + Math.round(inst.reminder_policy.cooldownMinutes * 60_000));
|
|
316
|
+
await tx.updateTable('flow.approval_instance').set({ next_urge_at: nextUrgeAt }).where('id', '=', inst.id).execute();
|
|
317
|
+
await recordTaskAction(tx, inst, null, actor.userId, 'urged', parsed.data.reason, { source, step: inst.current_step, steps: active.map(row => row.step), recipientCount, nextUrgeAt: nextUrgeAt.toISOString() });
|
|
318
|
+
return { recipientCount, nextUrgeAt: nextUrgeAt.toISOString() };
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* 取得当前内核唯一运营服务;只有 domain 显式导出 approvalOperations 才开放,不因安装包而隐式启用。
|
|
324
|
+
* HTTP 和 worker 复用同一弱缓存对象,但一致性由数据库事务保证,不能依赖这个内存缓存跨进程去重。
|
|
325
|
+
*/
|
|
326
|
+
export function approvalOperationsService(kernel) {
|
|
327
|
+
if (!kernel.registry.extensions.some(ext => ext.kind === 'RUNTIME' && ext.value.name === 'approval-operations'))
|
|
328
|
+
throw cageError('NOT_FOUND');
|
|
329
|
+
let service = services.get(kernel);
|
|
330
|
+
if (!service) {
|
|
331
|
+
service = new ApprovalOperationsService(kernel);
|
|
332
|
+
services.set(kernel, service);
|
|
333
|
+
}
|
|
334
|
+
return service;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* 独立 RUNTIME 声明启用审批运营;不能把同一对象同时标记 ROUTE,否则注册表只会识别其运行时身份。
|
|
338
|
+
* 迁移由原生路由统一追加,空应用可以启用本声明且保持无操作;不新增生产角色能力或默认授权。
|
|
339
|
+
*/
|
|
340
|
+
export function approvalOperations() {
|
|
341
|
+
return Object.freeze({ [RUNTIME]: true, name: 'approval-operations', spec: Object.freeze({}), capabilities: [],
|
|
342
|
+
[MIGRATIONS]: () => [], service: approvalOperationsService,
|
|
343
|
+
validate(registry) {
|
|
344
|
+
if (registry.extensions.filter(ext => ext.kind === 'RUNTIME' && ext.value.name === 'approval-operations').length !== 1)
|
|
345
|
+
throw declError('CAGE-APPR-006', '只能导出一个 approvalOperations 运行时');
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"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"}
|
package/dist/route.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 代码内封闭审批声明:组合子保存条件、选人、分配与任务政策,由注册表/CLI 抽取、引擎创建实例。
|
|
3
|
+
* 字段选人与条件进入 fieldsUsed 依赖;运行实例保存步骤快照,后来改代码不会改写在途规则。
|
|
4
|
+
* 这里不授予业务权限或执行任意脚本;新结构只追加迁移,历史 FLOW_DDL 内容保持不变。
|
|
5
|
+
*/
|
|
1
6
|
import { ROUTE, SYSTEM_DDL, MIGRATIONS, type Kernel, type Registry } from '@yss123/cage-kernel';
|
|
7
|
+
import { type ExecutableStep } from './execution.js';
|
|
2
8
|
import { InCodeEngine } from './engine.js';
|
|
3
9
|
import { type AiStep, type AiStepOptions } from './ai.js';
|
|
10
|
+
import { type TaskActionPolicy } from './task-actions.js';
|
|
11
|
+
import { type ApprovalSla, type ApprovalReminderPolicy } from './deadlines.js';
|
|
4
12
|
/** 封闭选人策略,仅决定候选执行人;被选中不代表拥有目标业务命令能力、数据范围或额度。 */
|
|
5
13
|
export type Approver = {
|
|
6
14
|
kind: 'deptLeader';
|
|
@@ -8,6 +16,9 @@ export type Approver = {
|
|
|
8
16
|
} | {
|
|
9
17
|
kind: 'role';
|
|
10
18
|
code: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'field';
|
|
21
|
+
field: string;
|
|
11
22
|
} | {
|
|
12
23
|
kind: 'user';
|
|
13
24
|
userId: string;
|
|
@@ -20,6 +31,8 @@ export declare const approver: {
|
|
|
20
31
|
role: (code: string) => Approver;
|
|
21
32
|
/** 指定用户 */
|
|
22
33
|
user: (userId: string) => Approver;
|
|
34
|
+
/** 从 routable 单据字段读取 UUID 或 UUID 数组;值随实例冻结,仍须业务授权,不接受任意 JS。 */
|
|
35
|
+
field: (field: string) => Approver;
|
|
23
36
|
};
|
|
24
37
|
/** 可静态分析的分支条件;field 应进入 approval.routable,运行时只读取流程变量。 */
|
|
25
38
|
export type Condition = {
|
|
@@ -43,43 +56,80 @@ export declare const lte: (field: string, value: number) => Condition;
|
|
|
43
56
|
export declare const eq: (field: string, value: string | number | boolean) => Condition;
|
|
44
57
|
export declare const ne: (field: string, value: string | number | boolean) => Condition;
|
|
45
58
|
export declare const isIn: (field: string, value: (string | number)[]) => Condition;
|
|
46
|
-
/**
|
|
59
|
+
/** 人工节点;direct 创建逐人审批票,candidate 创建共享待领取任务;追加票始终必签。 */
|
|
47
60
|
export interface Step {
|
|
48
61
|
kind: 'step';
|
|
49
62
|
title: string;
|
|
50
63
|
approver: Approver;
|
|
51
64
|
/** any:任意一人通过即可(或签);all:全部通过(会签) */
|
|
52
65
|
mode: 'any' | 'all';
|
|
66
|
+
/** direct(默认)逐人分配;candidate 建立一项未领取任务,只能搭配 any,领取时重新核对候选规则。 */
|
|
67
|
+
assignment?: 'direct' | 'candidate';
|
|
68
|
+
/** step 自动保存的新加签位置政策;旧实例缺省仅 parallel,升级不能自动开放挂起/后签行为。 */
|
|
69
|
+
readonly signPositions?: readonly ('parallel' | 'before' | 'after')[];
|
|
70
|
+
/** 可选任务流转政策;默认关闭。启动时随步骤保存,代码更新不改变在途实例。 */
|
|
71
|
+
actions?: Readonly<TaskActionPolicy>;
|
|
72
|
+
/** 节点总办理自然分钟时限,进入节点后持久固定;前后加签/转办不重置,不自动改变结论。 */
|
|
73
|
+
sla?: Readonly<ApprovalSla>;
|
|
53
74
|
}
|
|
54
75
|
/** 条件满足才展开内部步骤;未满足时跳过这一分支,不生成空待办。 */
|
|
55
76
|
export interface When {
|
|
56
77
|
kind: 'when';
|
|
57
78
|
cond: Condition;
|
|
58
|
-
steps:
|
|
79
|
+
steps: RouteNode[];
|
|
59
80
|
}
|
|
60
81
|
export type ApprovalStep = Step | AiStep;
|
|
61
|
-
|
|
82
|
+
/** 并行组中的具名顺序分支;只有所属 parallel 能启动它,不作为独立根节点执行。 */
|
|
83
|
+
export interface Branch {
|
|
84
|
+
/** 固定判别值,防止把分支误当作人工步骤。 */
|
|
85
|
+
kind: 'branch';
|
|
86
|
+
/** 同一并行组内唯一的可读名称,进入历史分支路径。 */
|
|
87
|
+
title: string;
|
|
88
|
+
/** 分支内部按声明顺序推进,可包含条件和嵌套并行组。 */
|
|
89
|
+
nodes: RouteNode[];
|
|
90
|
+
}
|
|
91
|
+
/** AND 并行组:各分支从共同入口启动,全部实际展开的分支完成才进入后续节点。 */
|
|
92
|
+
export interface Parallel {
|
|
93
|
+
kind: 'parallel';
|
|
94
|
+
/** 可读并行组标题,用于图谱、待办与历史定位。 */
|
|
95
|
+
title: string;
|
|
96
|
+
/** 2–16 个具名分支;名称在组内不能重复。 */
|
|
97
|
+
branches: Branch[];
|
|
98
|
+
}
|
|
99
|
+
/** 结构化流程节点;没有任意连线、循环或运行时重算条件的含义。 */
|
|
100
|
+
export type RouteNode = ApprovalStep | When | Parallel;
|
|
101
|
+
/** 创建具名分支,内部可顺序执行多步;只保存声明,结构预算在 route 构建时统一验证。 */
|
|
102
|
+
export declare function branch(title: string, ...nodes: RouteNode[]): Branch;
|
|
103
|
+
/** 声明 AND 分支与汇合;一条分支拒绝/退回时结束整轮,全部通过才继续,选人不授予权限。 */
|
|
104
|
+
export declare function parallel(title: string, ...branches: Branch[]): Parallel;
|
|
62
105
|
/**
|
|
63
106
|
* 同一个步骤原语支持人工和 AI 两类执行方式。AI 必须声明模型、要求、取证字段和人工接管规则,
|
|
64
107
|
* 正数预算在声明时检查;人工默认 any。此处只描述节点,不创建实例或执行模型请求。
|
|
65
108
|
*/
|
|
66
109
|
export declare function step(title: string, who: Approver, opts?: {
|
|
67
110
|
mode?: 'any' | 'all';
|
|
111
|
+
assignment?: 'direct' | 'candidate';
|
|
112
|
+
actions?: TaskActionPolicy;
|
|
113
|
+
sla?: ApprovalSla;
|
|
68
114
|
}): Step;
|
|
69
115
|
export declare function step(title: string, who: AiStepOptions): AiStep;
|
|
70
116
|
/** 将一组顺序步骤置于同一个条件下,条件求值发生在实例创建时。 */
|
|
71
|
-
export declare function when(cond: Condition, ...steps:
|
|
117
|
+
export declare function when(cond: Condition, ...steps: RouteNode[]): When;
|
|
72
118
|
/** 使用封闭运算符求值,不执行任意表达式;比较转换规则固定,业务阈值边界应另有回归用例。 */
|
|
73
119
|
export declare function evalCondition(c: Condition, vars: Record<string, unknown>): boolean;
|
|
74
|
-
/**
|
|
75
|
-
export declare function resolveSteps(nodes: readonly RouteNode[], vars: Record<string, unknown>):
|
|
120
|
+
/** 按提交变量展开节点并携带冻结的依赖;返回顺序是节点索引顺序,实际执行以 execution.dependsOn 为准。 */
|
|
121
|
+
export declare function resolveSteps(nodes: readonly RouteNode[], vars: Record<string, unknown>): ExecutableStep[];
|
|
76
122
|
/** 代码路由兼具可检查规格与引擎工厂;实例/待办表由迁移准备,注册路由本身不会访问数据库。 */
|
|
77
123
|
export interface Route {
|
|
78
124
|
readonly [ROUTE]: true;
|
|
79
125
|
readonly process: string;
|
|
80
126
|
readonly nodes: readonly RouteNode[];
|
|
127
|
+
/** 完整声明内容的 SHA256 指纹;实例保存该版本,发布新代码不会重算已有依赖或节点政策。 */
|
|
128
|
+
readonly definitionVersion: string;
|
|
81
129
|
/** 路由条件和审批人用到的字段(进入本体图的 uses 边) */
|
|
82
130
|
readonly fieldsUsed: readonly string[];
|
|
131
|
+
/** 显式申请人催办政策;随实例保存,升级不向既有无政策实例开放催办。 */
|
|
132
|
+
readonly reminders?: Readonly<ApprovalReminderPolicy>;
|
|
83
133
|
/** 代码内引擎(worker 与 HTTP 层使用) */
|
|
84
134
|
readonly engine: (k: Kernel) => InCodeEngine;
|
|
85
135
|
readonly [SYSTEM_DDL]: () => string;
|
|
@@ -94,7 +144,9 @@ export interface Route {
|
|
|
94
144
|
* 注册表完成后 validate 继续核对 AI 运行时、自动角色、退回命令和取证字段/查询,尽早暴露缺失声明。
|
|
95
145
|
* nodes 保存原有步骤顺序,业务实例创建时会冻结展开后的步骤,后续不会随代码分支重新求值。
|
|
96
146
|
*/
|
|
97
|
-
export declare function route(process: string, nodes: RouteNode[]
|
|
147
|
+
export declare function route(process: string, nodes: RouteNode[], options?: {
|
|
148
|
+
reminders?: ApprovalReminderPolicy;
|
|
149
|
+
}): Route;
|
|
98
150
|
/** 代码内引擎自己的表,放在 flow 模式;引擎没有业务表的写权限 */
|
|
99
151
|
/** 此 SQL 是已发布扩展 DDL 的内容摘要来源;新增字段通过独立 MIGRATIONS 追加,不能改写历史字符串。 */
|
|
100
152
|
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";
|