import { useEffect, useState, lazy, Suspense } from 'react'
import { Layout, Card, Tabs, Button, Space, Typography, Tag, message, Spin, Modal, Select, Menu } from 'antd'
import { ReloadOutlined, ThunderboltOutlined, BranchesOutlined, UserOutlined } from '@ant-design/icons'
import { api } from './api'
import type { SceneSchema } from './types'
import { SceneForm } from './engine/SceneForm'
import { TracePanel, ProvenancePanel, ModelViewer, OrdersEventsPanel } from './panels'
// 重量级视图(模型编辑器 + 三张可视化图,含 cytoscape/xyflow/bpmn-js)按需懒加载,
// 让默认「场景运行台」的首屏包保持轻量。
const ModelEditor = lazy(() => import('./model/ModelEditor').then((m) => ({ default: m.ModelEditor })))
const OntologyCanvas = lazy(() => import('./viz/OntologyCanvas').then((m) => ({ default: m.OntologyCanvas })))
const LazyFallback = () => (
)
const { Header, Sider, Content } = Layout
const { Title, Text, Paragraph } = Typography
type View = 'runner' | 'editor' | 'er' | 'network' | 'process'
const VIEW_ITEMS = [
{ key: 'runner', label: '场景运行台' },
{ key: 'editor', label: '模型编辑器' },
{ key: 'er', label: '数据结构图' },
{ key: 'network', label: '本体网络图' },
{ key: 'process', label: '场景流程图' },
]
export default function App() {
const [view, setView] = useState('runner')
const [scenes, setScenes] = useState([])
const [sceneId, setSceneId] = useState('') // 初始不硬编码,待场景列表返回后取首个可渲染场景
const [schema, setSchema] = useState(null)
const [loading, setLoading] = useState(false)
const [result, setResult] = useState(null)
const [refreshKey, setRefreshKey] = useState(0)
const [formKey, setFormKey] = useState(0)
// 演示态主体(M5 principal):随手切换以观察权限"通过/拒绝"分支;管理员方可热重载/保存模型/新增客户
const [principal, setPrincipal] = useState('backend_admin')
// 拉取场景列表(首个可渲染场景兜底选中;已有选择则保留,以便热重载后停留在当前场景)
const loadScenes = () =>
api.scenes().then((list) => {
setScenes(list)
setSceneId((cur) => cur || list.find((s: any) => s.hasTemplate)?.sceneId || '')
}).catch(() => message.error('无法连接引擎(8080),请先启动后端'))
useEffect(() => { loadScenes() }, [])
useEffect(() => {
if (!sceneId) return
setLoading(true); setResult(null)
api.sceneSchema(sceneId).then(setSchema).catch((e) => message.error('加载场景失败:' + e.message)).finally(() => setLoading(false))
}, [sceneId, formKey])
// 模型(含热重载/保存)变化后:重拉场景列表 + 重挂当前表单,让改动即时反映
const afterModelChange = async () => {
await loadScenes()
setFormKey((k) => k + 1)
}
async function reloadModels() {
try {
const r = await api.reload(principal)
message.success(r.message)
await afterModelChange()
} catch (e: any) { message.error('热重载失败:' + e.message) }
}
const onExecuted = (r: any) => { setResult(r); if (r.success) setRefreshKey((k) => k + 1) }
return (
本体驱动 DDD+EDA · 元数据引擎
{view === 'runner' && (
业务场景(M4)
{scenes.map((s) => {
const active = sceneId === s.sceneId
return (
setSceneId(s.sceneId)}
style={{
padding: '10px 12px', marginBottom: 8, borderRadius: 8, cursor: 'pointer',
background: active ? '#e6f4ff' : '#fff',
border: `1px solid ${active ? '#91caff' : '#f0f0f0'}`, transition: 'all .2s',
}}
>
{s.sceneName}
{s.hasTemplate ? s.templateName : '无表单 · 直接运行 flowSteps'}
)
})}
业务场景均由 M8 模板动态渲染并端到端执行。「模型编辑器」可视化查看/编辑本体;
「数据结构图 / 本体网络图 / 场景流程图」由 M0–M8 本体动态合成渲染。
{schema?.sceneName || '加载中'}{sceneId}}>
{schema?.template ?
: schema ?
: null}
},
{ key: 'prov', label: '溯源/规则', children: schema ? : null },
{ key: 'orders', label: '数据/事件', children: },
{ key: 'model', label: '模型查看器', children: },
]} />
)}
{view === 'editor' && (
}>
)}
{(view === 'er' || view === 'network' || view === 'process') && (
}>
)}
)
}
/**
* 无表单场景运行器:用于未绑定 M8 模板的场景(如"演示:全部 stepType",其字段为合成变量、非 M1 领域字段)。
* 直接以空 payload 调用同一执行接口,引擎会逐条"通用解释"M4 flowSteps,产出可读执行轨迹(见右侧「执行追踪」)。
*/
function RunOnlyScene({ schema, onExecuted, principal }:
{ schema: SceneSchema; onExecuted: (r: any) => void; principal: string }) {
const [running, setRunning] = useState(false)
async function run() {
setRunning(true)
try {
const r = await api.execute(schema.sceneId, { master: {}, details: {} }, principal)
onExecuted(r)
if (r.success) message.success('场景执行完成,见右侧「执行追踪」')
else message.error(`${r.stage} · ${r.message}`)
} catch (e: any) {
message.error('执行失败:' + e.message)
} finally {
setRunning(false)
}
}
return (
场景 {schema.sceneId}
无 M8 表单模板
权限 {schema.permissionBind?.join(',')}
该场景的字段为演示用合成变量(非 M1 领域字段),故不绑定 M8 表单模板。点下方按钮,引擎会以空 payload
逐条通用解释 M4 flowSteps —— 起止 / 数据与派生 / 控制流(condition·switch·while·parallel)
/ Saga 可靠性(retry·compensate·timer)/ EDA(emitEvent·waitEvent)/ 人工任务,产出可读执行轨迹。
)
}
function HelpButton() {
const [open, setOpen] = useState(false)
return (
<>
setOpen(false)} footer={null} width={720} title="如何验证:改了模型文件,操作定义同时改变">
本 Demo 的核心是元数据驱动:前端表单与后端行为都由 models/*.yaml 实时解释,改模型即改行为,无需改任何代码。试试:
- 进入「模型编辑器 · 源码编辑」,选 M8-front-schema.yaml 给订单主信息卡片加/删一个绑定 M1 字段的组件 → 保存并热重载 → 回「场景运行台」,表单立即多/少一个字段。
- 编辑 MetaRule-business.yaml:把 single_order_max_amount 从 50000 改为 100 → 保存 → 普通用户下单超 100 即被 REJECT。
- 编辑 M1-domain.yaml:把某字段 type 由 string 改为 int → 保存 → 组件输入类型随之改变;「数据结构图」同步更新。
- 「本体网络图 / 场景流程图」亦由本体动态合成,改模型后点视图内「刷新」即见变化。
保存需管理员主体(右上切换为「后台管理员」)。
>
)
}