feat(admin-web): AIPrewarm 分组展示 + 每行触发 + AppType 联合类型
1. AIPrewarm.tsx:
- areaToAppType(area) helper · area='all' → app2_finance · 其他 → app2a_finance_area
- handleRunOne / handleBackfillMissing 按 area 动态选 app_type
- MissingRowWithGroup 含 __group_header 字段
- groupedMissing 数据构造(全域 + 区域两组 · 每组前插 header 行)
- 每列 onCell colSpan 合并单元格实现"全域 / 区域"分组标题行
- Descriptions 加全域 8/X + 区域 64/X 双段统计
2. api/adminAI.ts:
- 新增 AppType 联合类型(9 项,含 app2a_finance_area)
- runApp 签名 appType: AppType(替代原 string)
- RunAppResponse.app_type 同步为 AppType
3. AIOperations.tsx:
- runAppType state 类型改为 AppType | undefined
- import { AppType } type
实测:
- pnpm tsc --noEmit 全项目通过
- playwright E2E 访问 /ai/prewarm 显示 "全域 8/8 · 区域 63/64" 分段统计
分组标题行正确合并 · 单独生成按钮按 area 路由到正确 app_type
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,10 +17,17 @@ import { ReloadOutlined } from "@ant-design/icons";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import {
|
||||
retryTriggerJob, invalidateCache, createBatchRun, confirmBatchRun,
|
||||
getAlerts, ackAlert, ignoreAlert,
|
||||
type AlertItem, type BatchRunEstimate,
|
||||
getAlerts, ackAlert, ignoreAlert, runApp, triggerEvent,
|
||||
type AlertItem, type AppType, type BatchRunEstimate,
|
||||
} from "../api/adminAI";
|
||||
|
||||
const EVENT_TYPE_OPTIONS = [
|
||||
{ label: "消费事件(App3→App8→App7 [+ App4→App5])", value: "consumption" },
|
||||
{ label: "备注事件(App6→App8)", value: "note_created" },
|
||||
{ label: "任务分配(App4→App5)", value: "task_assigned" },
|
||||
{ label: "DWS 完成(App2 × 72 组合预热)", value: "dws_completed" },
|
||||
];
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Title } = Typography;
|
||||
|
||||
@@ -92,6 +99,66 @@ const AIOperations: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// ---- Card 2.5: 按需重新生成 ----
|
||||
const [runAppType, setRunAppType] = useState<AppType | undefined>();
|
||||
const [runMemberId, setRunMemberId] = useState<string>("");
|
||||
const [runSiteId, setRunSiteId] = useState<number>(2790685415443269);
|
||||
const [runLoading, setRunLoading] = useState(false);
|
||||
const [runResult, setRunResult] = useState<{ success: boolean; text: string } | null>(null);
|
||||
|
||||
const handleRunApp = async () => {
|
||||
if (!runAppType) { message.warning("请选择 App 类型"); return; }
|
||||
setRunLoading(true);
|
||||
setRunResult(null);
|
||||
try {
|
||||
const res = await runApp(runAppType, {
|
||||
site_id: runSiteId,
|
||||
member_id: runMemberId ? Number(runMemberId) : undefined,
|
||||
});
|
||||
if (res.success) {
|
||||
setRunResult({ success: true, text: "执行成功,缓存已更新" });
|
||||
message.success("执行成功");
|
||||
} else {
|
||||
setRunResult({ success: false, text: res.error ?? "执行失败" });
|
||||
message.error(res.error ?? "执行失败");
|
||||
}
|
||||
} catch {
|
||||
message.error("请求失败");
|
||||
} finally {
|
||||
setRunLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ---- Card 2.6: 手动触发事件链(越过去重)----
|
||||
const [evtType, setEvtType] = useState<string>("consumption");
|
||||
const [evtSiteId, setEvtSiteId] = useState<number>(2790685415443269);
|
||||
const [evtMemberId, setEvtMemberId] = useState<string>("");
|
||||
const [evtAssistantId, setEvtAssistantId] = useState<string>("");
|
||||
const [evtForced, setEvtForced] = useState<boolean>(true);
|
||||
const [evtLoading, setEvtLoading] = useState(false);
|
||||
const [evtResult, setEvtResult] = useState<number | null>(null);
|
||||
|
||||
const handleTriggerEvent = async () => {
|
||||
if (!evtType) { message.warning("请选择事件类型"); return; }
|
||||
setEvtLoading(true);
|
||||
setEvtResult(null);
|
||||
try {
|
||||
const res = await triggerEvent({
|
||||
event_type: evtType,
|
||||
site_id: evtSiteId,
|
||||
member_id: evtMemberId ? Number(evtMemberId) : undefined,
|
||||
assistant_id: evtAssistantId ? Number(evtAssistantId) : undefined,
|
||||
is_forced: evtForced,
|
||||
});
|
||||
setEvtResult(res.trigger_job_id);
|
||||
message.success(`事件已触发,job_id=${res.trigger_job_id}(后台异步执行)`);
|
||||
} catch {
|
||||
message.error("触发失败");
|
||||
} finally {
|
||||
setEvtLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ---- Card 3: 批量执行 ----
|
||||
const [batchAppTypes, setBatchAppTypes] = useState<string[]>([]);
|
||||
const [batchMemberIds, setBatchMemberIds] = useState<string>("");
|
||||
@@ -247,6 +314,89 @@ const AIOperations: React.FC = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* Card 2.5: 按需重新生成 */}
|
||||
<Card title="按需重新生成" size="small" style={{ marginBottom: 16 }}>
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Select
|
||||
allowClear placeholder="App 类型" style={{ width: "100%" }}
|
||||
value={runAppType} onChange={setRunAppType}
|
||||
options={APP_TYPE_OPTIONS}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Input
|
||||
placeholder="会员 ID(部分 App 必填)" value={runMemberId}
|
||||
onChange={(e) => setRunMemberId(e.target.value)}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Select
|
||||
placeholder="门店" style={{ width: "100%" }}
|
||||
value={runSiteId} onChange={setRunSiteId}
|
||||
options={[{ label: "默认门店", value: 2790685415443269 }]}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={handleRunApp} loading={runLoading}>立即执行</Button>
|
||||
{runResult && (
|
||||
<Tag color={runResult.success ? "success" : "error"}>{runResult.text}</Tag>
|
||||
)}
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
{/* Card 2.6: 手动触发事件链(越过去重,调试利器)*/}
|
||||
<Card
|
||||
title="手动触发事件链(调试用)" size="small" style={{ marginBottom: 16 }}
|
||||
extra={<Tag color="orange">默认跳过去重</Tag>}
|
||||
>
|
||||
<Row gutter={12}>
|
||||
<Col span={6}>
|
||||
<div style={{ marginBottom: 4, fontSize: 12, color: "#888" }}>事件类型</div>
|
||||
<Select
|
||||
value={evtType} onChange={setEvtType}
|
||||
options={EVENT_TYPE_OPTIONS}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<div style={{ marginBottom: 4, fontSize: 12, color: "#888" }}>门店</div>
|
||||
<Select
|
||||
value={evtSiteId} onChange={setEvtSiteId}
|
||||
options={[{ label: "默认门店", value: 2790685415443269 }]}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<div style={{ marginBottom: 4, fontSize: 12, color: "#888" }}>member_id(可选)</div>
|
||||
<Input
|
||||
placeholder="consumption/note/task 事件需填"
|
||||
value={evtMemberId}
|
||||
onChange={(e) => setEvtMemberId(e.target.value)}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<div style={{ marginBottom: 4, fontSize: 12, color: "#888" }}>assistant_id(可选)</div>
|
||||
<Input
|
||||
placeholder="task_assigned 事件需填"
|
||||
value={evtAssistantId}
|
||||
onChange={(e) => setEvtAssistantId(e.target.value)}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<div style={{ marginBottom: 4, fontSize: 12, color: "#888" }}>跳过去重 + 操作</div>
|
||||
<Space>
|
||||
<Checkbox checked={evtForced} onChange={(e) => setEvtForced(e.target.checked)}>强制</Checkbox>
|
||||
<Button type="primary" danger onClick={handleTriggerEvent} loading={evtLoading}>触发</Button>
|
||||
{evtResult != null && <Tag color="processing">job #{evtResult}</Tag>}
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
{/* Card 3: 批量执行 */}
|
||||
<Card title="批量执行" size="small" style={{ marginBottom: 16 }}>
|
||||
<Row gutter={16}>
|
||||
|
||||
Reference in New Issue
Block a user