Archived
用户定的逻辑:「调用活动,输入数据可以不定或者为空;进入活动后受理归一,
信息不全派补全单。」把收敛点从门口挪进活动 —— 三道门口闸全撤:
· 触发装配:缺必填**不再入死信**(AssemblyResult 的 missing 改 partial,带上已装配的);
装到多少带多少照样起单。死信只留给真错误(勾了确认没指定人这类配置错误)。
· process.start:撤掉调用接口缺项闸 —— 从前缺一个字段整条流都起不来,
现实是客户来信本来就常缺客户码,拦在门外等于要求外界替系统把信息备齐。
· task.start(替代旧 AM 12.1「缺项不得启动」):缺必填的任务落成「等人」,
缺的字段做成**补全卡**(elicitation,标签取 schema 的 title)发到负责人的
任务对话流,并武装 human 监听(IntakeDesk 端口;不占活动的人卡预算)。
答复只收契约声明过的键,合并进 data;齐了或答「没法填」才转 running 开跑;
答一半就就缺的部分再派一张卡。等人期间谱一步不走、不扣步数。
· 测试台预检改成**信息**不是闸:灯灭不拦起单,文案直说
「抽不出的键由受理补全单找人收齐」。
5 条受理归一单测 + 全链实测:投一封只写「想问下工控板价格」的信(零键)→
流照起 → A1 落负责人工位、等人、补全卡在对话流 → 答卡(customerCode/rfqRef)→
任务醒、合并、谱跑 3 步到验收。修一处接线错误:监听 deadline 要 ISO-8601
时长(P2D)不是时刻,传错整个补全监听静默没装上。
trigger/task/process 三份 CONTRACT.md 与产品设计.md 同步(宽进纪律成文)。
五闸绿;契约快照随本提交入库。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
/**
|
|
* 触发器输入装配(OS 3.1)—— 三类触发的入参从哪来,这里是分界线。
|
|
*/
|
|
import { describe, expect, it } from 'vitest';
|
|
import type { TriggerConfig } from '@contracts';
|
|
import { triggerConfigSchema } from '@contracts';
|
|
import { assembleInput } from '../internal/domain/matching.ts';
|
|
|
|
const noExpr = () => undefined;
|
|
|
|
const registration = (mapping: TriggerConfig['mapping'] = []): TriggerConfig =>
|
|
triggerConfigSchema.parse({
|
|
id: 'trg-reg',
|
|
name: '人工登记起单',
|
|
processPackageId: 'unified-quote',
|
|
enabled: true,
|
|
spec: { kind: 'registration' },
|
|
mapping,
|
|
});
|
|
|
|
const event = (mapping: TriggerConfig['mapping'] = []): TriggerConfig =>
|
|
triggerConfigSchema.parse({
|
|
id: 'trg-evt',
|
|
name: '邮件到达起单',
|
|
processPackageId: 'unified-quote',
|
|
enabled: true,
|
|
spec: { kind: 'event', eventType: 'data', rule: 'true' },
|
|
mapping,
|
|
});
|
|
|
|
describe('assembleInput', () => {
|
|
it('登记触发:表单填的就是入参 —— 不声明 mapping 也能起单', () => {
|
|
// 登记表单由调用接口 schema 渲染,填出来的已经是合契约的形状,不必再装配
|
|
const r = assembleInput(registration(), { customerCode: 'C001', rfqRef: 'RFQ-1' }, ['customerCode', 'rfqRef'], noExpr);
|
|
expect(r).toEqual({ kind: 'ready', data: { customerCode: 'C001', rfqRef: 'RFQ-1' } });
|
|
});
|
|
|
|
it('登记触发:声明了 mapping 就覆盖在表单之上', () => {
|
|
const r = assembleInput(
|
|
registration([{ target: 'customerCode', source: { from: 'constant', value: 'C999' } }]),
|
|
{ customerCode: 'C001', rfqRef: 'RFQ-1' },
|
|
['customerCode'],
|
|
noExpr,
|
|
);
|
|
expect(r.kind === 'ready' && r.data['customerCode']).toBe('C999');
|
|
});
|
|
|
|
it('事件触发:scope 不打底 —— 事件载荷与调用接口是两套字段名', () => {
|
|
// 不声明 mapping 就装配不出必填字段,应入死信而不是把事件载荷整个塞给流程包
|
|
const r = assembleInput(event(), { subject: '询价', customerCode: 'C001' }, ['customerCode'], noExpr);
|
|
expect(r).toMatchObject({ kind: 'partial', fields: ['customerCode'] });
|
|
});
|
|
|
|
it('事件触发:mapping 声明了才装配得出来', () => {
|
|
const r = assembleInput(
|
|
event([{ target: 'customerCode', source: { from: 'initiation', path: 'customerCode' } }]),
|
|
{ customerCode: 'C001' },
|
|
['customerCode'],
|
|
noExpr,
|
|
);
|
|
expect(r).toEqual({ kind: 'ready', data: { customerCode: 'C001' } });
|
|
});
|
|
|
|
it('缺必填即 partial,列出缺哪几个 —— 但已装上的照带', () => {
|
|
const r = assembleInput(registration(), { rfqRef: 'RFQ-1' }, ['customerCode', 'rfqRef'], noExpr);
|
|
expect(r).toMatchObject({ kind: 'partial', fields: ['customerCode'] });
|
|
});
|
|
});
|