@activity-sdk(§8.2 / ADR-004): - 基座六件接口(契约 / 交互 / 记忆 / 数据 / 工具 / LLM)—— 活动看到的全部世界 - defineActivity() 的 ABI;本地开发宿主(mock 工具表 + 模拟工位 + 录制夹具), 宿主自身就守三规矩:裸调用拒发、视图含脚本拒推、监听无时限拒登记 activities/quote-intake —— 统一报价 A1 · RFQ 需求分析(flow): - 契约七件 / 步序声明五步(LLM 抽取 → 条件跳步补全 → 查历史价 → DMN 打标 → 验收放行) - 三分类规则 P-03 落 DMN 决策表(C 优先于 A,命中行留痕) - 台账声明两对象、监听时限必配、SLA 落 Harness depcruise 实测拦下活动包越界 import(activities → apps 双向隔离), 测试遂按边界拆分:活动侧只用 @sdk/@contracts/@sfeel 358 个测试通过,门禁五道全绿 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
/**
|
|
* 统一报价 A1 · RFQ 需求分析 —— 活动包代码入口(ADR-004:默认导出 defineActivity 的返回值)。
|
|
*
|
|
* 这是 **flow** 活动:下一步由步序声明给定,引擎照谱推进;
|
|
* 本文件承担声明装不下的那部分 —— 视图流控与纯工具(打标)。
|
|
* **零环境能力**:一切能力经基座六件注入。
|
|
*/
|
|
import { defineActivity } from '@sdk';
|
|
import type { ActivityBase, ActivityOutcome } from '@sdk';
|
|
|
|
const INTAKE = 'quote_intake.record';
|
|
const TYPE_TABLE = 'quote_intake.type_table';
|
|
|
|
export default defineActivity({
|
|
name: 'quote-intake',
|
|
paradigm: 'flow',
|
|
|
|
async run(base: ActivityBase): Promise<ActivityOutcome> {
|
|
const records = await base.ledger.read(INTAKE, { customer_code: String(base.contract.data['customerCode'] ?? '') });
|
|
const typed = await base.ledger.read(TYPE_TABLE);
|
|
|
|
// 视图流控:按局面推结构化块 —— 内容归活动,容器归系统
|
|
await base.interaction.pushView([
|
|
{
|
|
t: 'kv',
|
|
title: '受理概览',
|
|
items: [
|
|
{ k: '客户', v: String(base.contract.data['customerCode'] ?? '') },
|
|
{ k: '已归一', v: `${records.length} 条` },
|
|
{ k: '已打标', v: `${typed.length} 条` },
|
|
],
|
|
},
|
|
{
|
|
t: 'table',
|
|
title: '统一受理记录',
|
|
cols: ['PN', '厂牌', 'EAU', 'Type'],
|
|
rows: records.map((r) => [
|
|
String(r['pn'] ?? ''),
|
|
String(r['brand'] ?? ''),
|
|
String(r['eau'] ?? ''),
|
|
String(r['type_tag'] ?? '未打标'),
|
|
]),
|
|
},
|
|
]);
|
|
|
|
const untyped = records.filter((r) => r['type_tag'] === undefined || r['type_tag'] === null);
|
|
if (untyped.length > 0) {
|
|
await base.interaction.say(`还有 ${untyped.length} 条未打标,继续按三分类规则 P-03 判定。`, 'conclusion');
|
|
return { kind: 'await', reason: '等待打标步完成' };
|
|
}
|
|
|
|
await base.interaction.say(
|
|
`${records.length} 条已全部打标:` +
|
|
`A ${count(records, 'A')} 条直通定价,B ${count(records, 'B')} 条转寻源,C ${count(records, 'C')} 条转选型。`,
|
|
'criterion',
|
|
);
|
|
return { kind: 'submit', deliverable: 'intake-record', rationale: '逐条已打标,必交付物齐备' };
|
|
},
|
|
});
|
|
|
|
function count(rows: ReadonlyArray<Record<string, unknown>>, tag: string): number {
|
|
return rows.filter((r) => r['type_tag'] === tag).length;
|
|
}
|