feat(admin-web,backend): F1-5b Wave B UI-3 + UI-5 admin-web sandbox 透出补强 (W1)
UI-3 AIDashboard sandbox 提示 + today_calls 分组: - 后端 schemas/admin_ai.py DashboardResponse 加 today_live_calls / today_sandbox_calls 字段(默认 0,向后兼容) - 后端 services/ai/admin_service.py _get_range_stats SELECT 加 2 个 FILTER COUNT 表达式 - 前端 api/adminAI.ts DashboardResponse 类型补 2 字段 - 前端 pages/AIDashboard.tsx - 顶部加 sandbox Alert 提示条,选中 site sandbox 模式下显示业务日 + 实例 ID - today_calls 卡片下方加分组 Tag(实时 X / 沙箱 Y),feature flag 控制 - import fetchRuntimeContext + useEffect 拉 RuntimeContext - apps/admin-web/.env.example 新建,加 VITE_AI_RUNTIME_GROUPING=false 默认值说明 UI-5 AITriggerJobs runtime 列: - 后端 schemas/admin_ai.py TriggerJobItem 加 runtime_mode / sandbox_instance_id 可选字段 - 后端 admin_service.py list_trigger_jobs / get_trigger_job 各加 SELECT 列 - 前端 adminAI.ts TriggerJobItem 类型补 2 字段 - 前端 pages/AITriggerJobs.tsx 列表 columns 加运行模式 + 沙箱实例(同 UI-1 模式),详情 Modal 加 2 项(同 UI-2 模式) 双口径验证(Playwright + DB 直查): - UI-3 4a live: 选中默认门店,无 Alert,today_card 仅显示总数(flag off) - UI-3 4b sandbox=4-20: Alert 显示"沙箱 + 业务日 + sbx_…",today_calls=93(sandbox 当日) - UI-5 4a/4b: SQL INSERT 注入 walkthrough 测试行(id=9 live, id=10 sandbox),列表正确渲染 Tag + 短哈希 trend_7d 双线 / app_distribution 堆叠分布等更深入分组改造延后到 Wave C(§8.3 风险:破坏图表)。 审计: - docs/audit/changes/2026-05-05__wave1_f1_5b_ui3_aidashboard_sandbox.md - docs/audit/changes/2026-05-05__wave1_f1_5b_ui5_aitriggerjobs_runtime.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,10 +11,17 @@
|
||||
import React, { useEffect, useRef, useState, useCallback } from "react";
|
||||
import {
|
||||
Card, Row, Col, Statistic, Table, Tag, Badge, Progress,
|
||||
Select, Button, message, Typography, Space, DatePicker,
|
||||
Select, Button, message, Typography, Space, DatePicker, Alert,
|
||||
} from "antd";
|
||||
import { ReloadOutlined, WifiOutlined } from "@ant-design/icons";
|
||||
import type { Dayjs } from "dayjs";
|
||||
// F1-5b UI-3: sandbox 提示条复用 UI-4 同款 RuntimeContext fetch
|
||||
import { fetchRuntimeContext, type RuntimeContext } from "../api/runtimeContext";
|
||||
|
||||
// F1-5b UI-3: feature flag — today_calls 分组数字是否展示。
|
||||
// 默认 false(防止图表回归);开启时 today_calls 卡片下方显示 "实时 X / 沙箱 Y"。
|
||||
const RUNTIME_GROUPING_ENABLED =
|
||||
String(import.meta.env.VITE_AI_RUNTIME_GROUPING ?? "").toLowerCase() === "true";
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
@@ -105,6 +112,8 @@ const AIDashboard: React.FC = () => {
|
||||
const [wsStatus, setWsStatus] = useState<"connecting" | "connected" | "disconnected">("disconnected");
|
||||
const [realtimeAlerts, setRealtimeAlerts] = useState<AlertItem[]>([]);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
// F1-5b UI-3: 当前选中 site 的 RuntimeContext,用于顶部 sandbox 提示条
|
||||
const [runtimeCtx, setRuntimeCtx] = useState<RuntimeContext | null>(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -128,6 +137,19 @@ const AIDashboard: React.FC = () => {
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
// F1-5b UI-3: 拉取当前 site 的 RuntimeContext。siteId 未选时不显示提示条。
|
||||
useEffect(() => {
|
||||
if (siteId == null) {
|
||||
setRuntimeCtx(null);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
fetchRuntimeContext(siteId)
|
||||
.then((ctx) => { if (!cancelled) setRuntimeCtx(ctx); })
|
||||
.catch(() => { if (!cancelled) setRuntimeCtx(null); });
|
||||
return () => { cancelled = true; };
|
||||
}, [siteId]);
|
||||
|
||||
const statLabel = rangeDays === 0
|
||||
? (customRange ? `${customRange[0].format("MM-DD")} ~ ${customRange[1].format("MM-DD")}` : "指定日期")
|
||||
: (RANGE_LABEL[rangeDays] || "今日");
|
||||
@@ -198,10 +220,40 @@ const AIDashboard: React.FC = () => {
|
||||
</Space>
|
||||
</Row>
|
||||
|
||||
{/* F1-5b UI-3: sandbox 提示条 — 仅当选中 site 处于 sandbox 模式时显示 */}
|
||||
{runtimeCtx?.is_sandbox && (
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message={
|
||||
<Space size={8}>
|
||||
<Tag color="orange" style={{ margin: 0 }}>沙箱</Tag>
|
||||
<span>当前门店处于沙箱模式,数据基于业务日</span>
|
||||
<Tag color="default" style={{ fontFamily: "monospace" }}>{runtimeCtx.business_date}</Tag>
|
||||
{runtimeCtx.sandbox_instance_id && (
|
||||
<span style={{ fontSize: 12, color: "#888", fontFamily: "monospace" }}>
|
||||
实例 {runtimeCtx.sandbox_instance_id.slice(0, 12)}…
|
||||
</span>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 第一行:4 个统计卡片 */}
|
||||
<Row gutter={16} style={{ marginBottom: 16 }}>
|
||||
<Col span={6}>
|
||||
<Card><Statistic title={`${statLabel}调用次数`} value={data?.today_calls ?? 0} /></Card>
|
||||
<Card>
|
||||
<Statistic title={`${statLabel}调用次数`} value={data?.today_calls ?? 0} />
|
||||
{/* F1-5b UI-3: feature flag 控制 — today_calls 按 runtime 分组数字 */}
|
||||
{RUNTIME_GROUPING_ENABLED && data && (
|
||||
<div style={{ marginTop: 4, fontSize: 12, color: "#888" }}>
|
||||
<Tag color="blue" style={{ marginRight: 4 }}>实时 {data.today_live_calls}</Tag>
|
||||
<Tag color="orange" style={{ margin: 0 }}>沙箱 {data.today_sandbox_calls}</Tag>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card>
|
||||
|
||||
Reference in New Issue
Block a user