Archived
**触发确认链路**(用户口述五步的第 4/5 步):
接收邮件 → 分类识别 → 命中 RFQ 事件 → ④(可选,运营勾)系统消息到指定人
工作台(带邮件内容)→ ⑤ 工作台点「确认起单」,流程才起。
· 命中不等于必起:勾了「需人工确认」,装配好的入参存成待确认单
(sys.trigger_pending,migration 0019),ConfirmDesk 端口把带来件内容的
系统消息送指定人(eventRef 挂票号,消息列表就地贴确认状态与那枚钮)。
· 先认领后起单:consumed_at 空才认得到 —— 两人同时点、一人点两次,都只起一条流;
起单失败退回认领,不留死票。确认人记进 source.by,留痕可查。
· 登记触发不走确认(登记那一下本身就是人在确认);勾了没指定人 = 配置错误入死信。
· 4 条单测 + 全链实测:投信 → triggersHit=1 且零直起 → AI 工作台收到确认消息
→ 确认起单 → 二次确认幂等回同一流号 → source.by = AI。
**运营中心重排**(区名即运行时:工作流是流程包的运行时,任务是活动的运行时):
· 新「工作流」区:在途看板(从任务区迁入)+ 运行配置 —— 每包的触发器
起单开关 / 确认打勾 / 确认人,和节点负责人(sys.node_owner)。
· 负责人三层:显式指派 > 运营配置 > 轮值 —— 合并点在 process.start,
复用既有派发链一行不改。
· 任务区瘦身成活动运行时(任务/审批/需处置);知识库区改名「数据」
(与设计中心的知识库解开同名);系统区只剩通道实例。
· 设计中心触发器详情页摘掉启停按钮 —— 设计只管设计,启停/确认全在运营。
· 旧深链有改嫁:tasks/board → runs/board,tasks/trg → runs/conf。
reset-runs 补 sys.trigger_pending(待确认单是运行痕 —— 和上回漏 trace_span 同类洞)。
邮件分类 Agent(第 ② 步)现由抽键正则 + S-FEEL 代位,升级路径记进 debt.md。
产品设计.md 运营章、trigger/process 两份 CONTRACT.md 同步。
五闸绿;契约快照随本提交入库。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
77 lines
3.1 KiB
TypeScript
77 lines
3.1 KiB
TypeScript
/**
|
|
* process 的 sys schema 表(编码规范 §7 / ADR-002)。
|
|
* 流程包定义与版本 + 工作流实例 + 节点实例(重开产生新实例,旧实例不再入算)。
|
|
*/
|
|
import { boolean, index, integer, jsonb, primaryKey, text, timestamp } from 'drizzle-orm/pg-core';
|
|
import { sys } from '@shared/db.ts';
|
|
|
|
export const processPackageTable = sys.table(
|
|
'process_package',
|
|
{
|
|
id: text('id').notNull(),
|
|
version: integer('version').notNull(),
|
|
name: text('name').notNull(),
|
|
state: text('state').notNull().default('draft'),
|
|
definition: jsonb('definition').notNull(),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
publishedAt: timestamp('published_at', { withTimezone: true }),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.id, t.version] }), index('process_package_state_idx').on(t.state)],
|
|
);
|
|
|
|
export const workflowTable = sys.table(
|
|
'workflow',
|
|
{
|
|
id: text('id').primaryKey(),
|
|
packageId: text('package_id').notNull(),
|
|
packageVersion: integer('package_version').notNull(),
|
|
subject: text('subject').notNull(),
|
|
state: text('state').notNull().default('running'),
|
|
/** 凡单必有来源 */
|
|
source: jsonb('source').notNull(),
|
|
initiation: jsonb('initiation').notNull(),
|
|
/** 工作组绑定快照(ADR-011)—— 起单时装载,之后改组不影响这条单 */
|
|
workGroup: jsonb('work_group'),
|
|
/** 节点指派:{ 节点id → 员工id } —— 测试台定向;生产起单不传,照旧轮值 */
|
|
assignments: jsonb('assignments'),
|
|
ports: jsonb('ports').notNull().default({}),
|
|
vars: jsonb('vars').notNull().default({}),
|
|
startedAt: timestamp('started_at', { withTimezone: true }).notNull().defaultNow(),
|
|
finishedAt: timestamp('finished_at', { withTimezone: true }),
|
|
},
|
|
(t) => [index('workflow_state_idx').on(t.state, t.startedAt)],
|
|
);
|
|
|
|
export const nodeInstanceTable = sys.table(
|
|
'node_instance',
|
|
{
|
|
workflowId: text('workflow_id').notNull(),
|
|
nodeId: text('node_id').notNull(),
|
|
instance: integer('instance').notNull(),
|
|
running: boolean('running').notNull().default(false),
|
|
waiting: boolean('waiting').notNull().default(false),
|
|
activated: boolean('activated').notNull().default(true),
|
|
terminal: text('terminal'),
|
|
failureDigested: boolean('failure_digested'),
|
|
taskId: text('task_id'),
|
|
approvalId: text('approval_id'),
|
|
assigneeId: text('assignee_id'),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
},
|
|
(t) => [
|
|
primaryKey({ columns: [t.workflowId, t.nodeId, t.instance] }),
|
|
index('node_instance_task_idx').on(t.taskId),
|
|
],
|
|
);
|
|
|
|
/**
|
|
* 节点负责人(**运营配置**,2026-07-30):流程包的哪个节点归谁。
|
|
* 起单时垫在显式指派之下、轮值之上 —— 显式指派 > 这里 > 员工组轮值 > 全员轮值。
|
|
* 不随包版本:它说的是「这家企业现在这活谁管」,不是流程长什么样。
|
|
*/
|
|
export const nodeOwnerTable = sys.table('node_owner', {
|
|
packageId: text('package_id').notNull(),
|
|
nodeId: text('node_id').notNull(),
|
|
staffId: text('staff_id').notNull(),
|
|
});
|