Archived
基线:鉴权会话+工作组+工作台三区修复+编排工作区+产品设计文档
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 触发器输入装配(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).toEqual({ kind: 'missing', 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('缺必填即 missing,列出缺哪几个', () => {
|
||||
const r = assembleInput(registration(), { rfqRef: 'RFQ-1' }, ['customerCode', 'rfqRef'], noExpr);
|
||||
expect(r).toEqual({ kind: 'missing', fields: ['customerCode'] });
|
||||
});
|
||||
});
|
||||
@@ -50,7 +50,15 @@ export function assembleInput(
|
||||
required: readonly string[],
|
||||
evaluateExpression: ExpressionEvaluator,
|
||||
): AssemblyResult {
|
||||
const data: Record<string, unknown> = {};
|
||||
/*
|
||||
* 登记触发的入参**就是登记人填的表单**(OS 3.1:「按调用接口 schema 填写输入,
|
||||
* 表单由 schema 自动渲染」)—— 那份表单已按调用接口成形,不需要再装配一遍。
|
||||
* 所以这里以 scope 打底,mapping(若声明了)再覆盖其上。
|
||||
*
|
||||
* 事件 / 周期触发不打底:它们的 scope 是事件载荷,与调用接口是两套字段名,
|
||||
* 直接铺进去会把无关字段塞给流程包 —— 那正是 mapping 存在的理由。
|
||||
*/
|
||||
const data: Record<string, unknown> = config.spec.kind === 'registration' ? { ...scope } : {};
|
||||
for (const mapping of config.mapping) {
|
||||
switch (mapping.source.from) {
|
||||
case 'constant':
|
||||
|
||||
@@ -86,7 +86,13 @@ export function createTriggerService(deps: TriggerServiceDeps): TriggerService {
|
||||
async fireByRegistration(triggerId, by, data) {
|
||||
const config = await deps.store.get(triggerId);
|
||||
if (config === undefined || !config.enabled) return { triggerId, skipped: 'disabled' };
|
||||
return fire(config, { kind: 'registration', ref: `registration:${by}`, by }, data, config.name);
|
||||
/*
|
||||
* 主题优先取登记人填的 `subject` —— 拿触发器名当主题的话,
|
||||
* 同一个登记入口起的所有单主题全一样,清单上分不出哪条是哪条。
|
||||
*/
|
||||
const filled = data['subject'];
|
||||
const subject = typeof filled === 'string' && filled.trim() !== '' ? filled.trim() : config.name;
|
||||
return fire(config, { kind: 'registration', ref: `registration:${by}`, by }, data, subject);
|
||||
},
|
||||
|
||||
/** 周期扫描:日历型按表达式到点;数据日历型逐行起单 */
|
||||
|
||||
Reference in New Issue
Block a user