测试台改仪表台:启动输入换成一封信;卡片化触发器与大模型;清掉 ⓘ 说教条

**测试台**
  · 启动输入不再是「调用参数」表 —— 这条流真实的起法就是客户来信,
    界面上就该是一封信(发件人 / 收件人 / 主题 / 正文 / BOM 附件)。
    服务端拿**真通道的抽键正则** + **真触发器的装配映射**推出调用入参:
    测试台自己不解析邮件,否则试通的是测试台而不是这条流。
    抽不出必填就如实报「哪个字段没命中、通道的正则要什么」,不静默起空单。
  · 一屏两大块:左选流,右两节 —— 01 输入配置、02 节点指派。
  · 起单钮只剩一枚(原来页头一枚、RUN 卡里一枚)。
  · 改暗色仪表台:调色板集中在 .g-tb/.g-rig 的一组变量上,
    TRACE 那张树 + 甘特的结构一行没动,只是纸变成了面板。

**触发器 / 大模型** 列表改卡片:一条规则、一个实例都要一眼看全
(何时起 · 起哪条流 · 开没开 / 谁家的 · 什么型号 · 密钥绑没绑)。
触发器卡是 div 不是 button —— 卡里还坐着「改规则」那枚真按钮。

**同类卡等高**:底栏钉在卡底、说明恒占两行、卡头一行截断。
实测同一货架内 13 张工具卡、6 张活动卡、3 张模型卡各自完全等高。

**删掉 19 条 ⓘ 说教横幅**(「只列有生效版的包…」那一类)。
留下的是三种:确认带里的影响说明(点了动作才出现,是决策依据)、
warn/ok 的真反馈、以及「X 不存在」这类空态。

删掉入站邮件演练整屏 —— 它验的那两步已经并进测试台的起单路径了。

新增一条接缝测试:通道抽出来的键叫 keys.customerCode,触发器映射也得照这名字取;
哪头改了名字单还是能起,只是入参悄悄变空、流走到 G1 才莫名其妙地停。

四闸绿;契约快照随本提交入库。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-29 10:42:01 +08:00
co-authored by Claude Opus 5
parent 331fd25922
commit 72f90f293b
19 changed files with 526 additions and 838 deletions
@@ -39,8 +39,8 @@ import {
Seg,
St,
} from '@web-shared/gov/primitives.tsx';
import { Ico } from '@web-shared/Ico.tsx';
import type { Nav } from '@web-shared/gov/gov-route.ts';
import { InboundDrill } from './inbound.tsx';
import { useGovObjects, useGovPackages, useGovTriggers } from '@web-shared/gov/gov-api.ts';
import { domainShort } from '@web-shared/gov/domains.ts';
@@ -88,14 +88,10 @@ export function TriggerRulesHome({ nav }: { nav: Nav }) {
return (
<QueryGate query={q}>
<>
<Note>
调起规则决定<b>什么时候起一单</b>:周期 / 事件 / 登记三类。触发器与流程包平级——不入包结构、
不随包版本,改规则不动任何版本。<b>启停不在这里</b>,那是运营中心的事。
</Note>
{triggers.length === 0 ? (
<Clear>还没有调起规则 —— 配一条,这条流就能被自动或人工调起来。</Clear>
) : (
<NList>
<div className="g-shelf">
{triggers.map((t) =>
editing === t.id ? (
<TriggerForm
@@ -105,37 +101,57 @@ export function TriggerRulesHome({ nav }: { nav: Nav }) {
onCancel={() => setEditing(undefined)}
/>
) : (
<NRow
/*
* 卡而不是行:一条规则要一眼看全「何时起 · 起哪条流 · 开没开」,
* 行只放得下前两样。卡是 div 不是 button —— 卡里还坐着「改规则」那枚真按钮。
*/
<div
key={t.id}
tone={t.enabled ? 'brand' : 'mute'}
ico="zap"
title={t.name}
sub={ruleText(t)}
cols={[
{ k: '类别', v: <Chip tone="brand">{TRIGGER_KIND_LABEL[triggerKindOf(t.spec)]}</Chip> },
{ k: '调起', v: t.processPackageId },
{ k: '装配', v: `${t.mapping.length} 条` },
]}
right={
className={'g-tile t-' + (t.enabled ? 'brand' : 'mute')}
role="button"
tabIndex={0}
onClick={() => nav.push({ k: 'trigger', id: t.id })}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
nav.push({ k: 'trigger', id: t.id });
}
}}
>
<span className="g-tile-h">
<span className="g-tile-i">
<Ico name="zap" s={16} />
</span>
<span className="g-tile-t">
<b>{t.name}</b>
<em>{t.processPackageId}</em>
</span>
<Chip tone="brand">{TRIGGER_KIND_LABEL[triggerKindOf(t.spec)]}</Chip>
</span>
<span className="g-tile-d">{ruleText(t)}</span>
<span className="g-tile-m">
<Chip ico="arrow-left-right">装配 {t.mapping.length} 条</Chip>
</span>
<span className="g-tile-f">
<St tone={t.enabled ? 'ok' : 'mute'} ico={t.enabled ? 'circle-check' : 'power'}>
{t.enabled ? '起单中' : '已停用'}
</St>
<button
className="g-btn"
className="g-btn r"
onClick={(e) => {
// 行本身也可点(进详情)—— 按钮要拦住冒泡,否则点「改规则」会跳走
// 卡本身也可点(进详情)—— 按钮要拦住冒泡,否则点「改规则」会跳走
e.stopPropagation();
setEditing(t.id);
}}
>
改规则
</button>
}
onClick={() => nav.push({ k: 'trigger', id: t.id })}
/>
</span>
</div>
),
)}
</NList>
</div>
)}
{/* 定完就地验:来一封真信,看这些规则里哪条接住 */}
<InboundDrill nav={nav} />
</>
</QueryGate>
);
@@ -240,9 +256,6 @@ function TriggerForm({
<Field k="类别" hint="周期 / 事件 / 登记三类">
<Seg value={draft.spec.kind} options={SPEC_KINDS.map((s) => ({ k: s.k, label: s.label }))} onPick={setSpecKind} />
</Field>
{draft.spec.kind === 'registration' && (
<Note>登记型无参:人在工作台按调用接口的 schema 填表提交,即起一单。</Note>
)}
{draft.spec.kind === 'event' && (
<>
<Field k="事件类型">
@@ -422,7 +435,6 @@ export function TriggerOpsTab({ nav }: { nav: Nav }) {
))}
</NList>
)}
<Note>逐条启停在触发器详情页做 —— 开关是治理动作,要走影响确认带,不做列表上的顺手一点。</Note>
{toggle.isError && <Note tone="warn">切换失败,请重试。</Note>}
</>
</QueryGate>