云联一号 3 days ago
parent
commit
d21e87a598

+ 41 - 0
src/api/workflow/lobster-dashboard.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function getTenantDashboard(companyId) {
+  return request({ url: '/api/lobster/admin/dashboard/' + companyId, method: 'get' })
+}
+
+export function getEvolutionMetrics(companyId) {
+  return request({ url: '/api/lobster/admin/evolution/metrics/' + companyId, method: 'get' })
+}
+
+export function triggerScheduler() {
+  return request({ url: '/api/lobster/admin/scheduler/trigger', method: 'post' })
+}
+
+export function queryPayStatus(orderNo, gateway) {
+  return request({ url: '/api/lobster/admin/pay/status', method: 'get', params: { orderNo, gateway } })
+}
+
+export function getActiveAbTests(companyId) {
+  return request({ url: '/api/lobster/admin/ab-test/active/' + companyId, method: 'get' })
+}
+
+export function applyAbTest(companyId, testId) {
+  return request({ url: '/api/lobster/admin/ab-test/apply/' + companyId + '/' + testId, method: 'post' })
+}
+
+export function triggerLearningCycle(companyId) {
+  return request({ url: '/api/lobster/admin/learning/trigger/' + companyId, method: 'post' })
+}
+
+export function getLearningResults(companyId) {
+  return request({ url: '/api/lobster/admin/learning/results/' + companyId, method: 'get' })
+}
+
+export function applyLearningResult(companyId, resultId) {
+  return request({ url: '/api/lobster/admin/learning/apply/' + companyId + '/' + resultId, method: 'post' })
+}
+
+export function setOptimizationConfig(companyId, params) {
+  return request({ url: '/api/lobster/admin/optimization/config/' + companyId, method: 'post', params })
+}

+ 16 - 0
src/api/workflow/lobster.js

@@ -83,6 +83,22 @@ export function getAvailableChannels() {
   return request({ url: '/workflow/lobster/engine/channels', method: 'get' })
 }
 
+export function getNodeCapabilities() {
+  return request({ url: '/workflow/lobster/engine/node-capabilities', method: 'get' })
+}
+
+export function runIntegrationTests() {
+  return request({ url: '/workflow/lobster/engine/integration-test/run', method: 'post' })
+}
+
+export function testNodeTypes() {
+  return request({ url: '/workflow/lobster/engine/integration-test/node-types', method: 'get' })
+}
+
+export function testDynamicExecutor() {
+  return request({ url: '/workflow/lobster/engine/integration-test/dynamic-executor', method: 'get' })
+}
+
 // ======== AI生成工作流 ========
 export function aiGenerate(data) {
   return request({ url: '/workflow/lobster/ai-generator/generate', method: 'post', data })

+ 48 - 0
src/router/index.js

@@ -684,6 +684,54 @@ export const constantRoutes = [
         component: () => import('@/views/lobster/sensitive-words/index'),
         name: 'LobsterSensitiveWords',
         meta: { title: '敏感词库', icon: 'warning' }
+      },
+      {
+        path: 'dashboard',
+        component: () => import('@/views/lobster/dashboard/index'),
+        name: 'LobsterDashboard',
+        meta: { title: '引擎看板', icon: 'dashboard' }
+      },
+      {
+        path: 'evolution',
+        component: () => import('@/views/lobster/evolution/index'),
+        name: 'LobsterEvolution',
+        meta: { title: 'AI进化引擎', icon: 'guide' }
+      },
+      {
+        path: 'node-capabilities',
+        component: () => import('@/views/lobster/node-capabilities/index'),
+        name: 'LobsterNodeCapabilities',
+        meta: { title: '节点能力矩阵', icon: 'tree-table' }
+      },
+      {
+        path: 'ab-test',
+        component: () => import('@/views/lobster/ab-test/index'),
+        name: 'LobsterAbTest',
+        meta: { title: 'A/B测试', icon: 'chart' }
+      },
+      {
+        path: 'learning-results',
+        component: () => import('@/views/lobster/learning-results/index'),
+        name: 'LobsterLearningResults',
+        meta: { title: '学习结果', icon: 'education' }
+      },
+      {
+        path: 'pay-manage',
+        component: () => import('@/views/lobster/pay-manage/index'),
+        name: 'LobsterPayManage',
+        meta: { title: '支付管理', icon: 'money' }
+      },
+      {
+        path: 'channel-config',
+        component: () => import('@/views/lobster/channel-config/index'),
+        name: 'LobsterChannelConfig',
+        meta: { title: '渠道配置', icon: 'connection' }
+      },
+      {
+        path: 'integration-test',
+        component: () => import('@/views/lobster/integration-test/index'),
+        name: 'LobsterIntegrationTest',
+        meta: { title: '集成测试', icon: 'bug' }
       }
     ]
   },

+ 30 - 0
src/views/lobster/ab-test/index.vue

@@ -0,0 +1,30 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-button type="primary" plain icon="el-icon-refresh" size="mini" @click="getList">刷新</el-button></el-col></el-row>
+    <el-table border v-loading="loading" :data="list">
+      <el-table-column prop="id" label="测试ID" width="80"/>
+      <el-table-column prop="testName" label="测试名称"/>
+      <el-table-column prop="nodeCode" label="节点编码" width="120"/>
+      <el-table-column prop="originalMessage" label="原始话术" show-overflow-tooltip/>
+      <el-table-column prop="variantMessage" label="变体话术" show-overflow-tooltip/>
+      <el-table-column prop="sampleCount" label="样本量" width="80"/>
+      <el-table-column prop="controlRate" label="对照转化率" width="100"/>
+      <el-table-column prop="variantRate" label="变体转化率" width="100"/>
+      <el-table-column prop="winner" label="胜出" width="80"><template slot-scope="s"><el-tag v-if="s.row.winner" type="success" size="small">{{ s.row.winner }}</el-tag></template></el-table-column>
+      <el-table-column label="操作" width="120"><template slot-scope="s"><el-button v-if="s.row.winner==='variant'" size="mini" type="primary" @click="apply(s.row)">应用变体</el-button></template></el-table-column>
+    </el-table>
+  </div>
+</template>
+<script>
+import { getActiveAbTests, applyAbTest } from '@/api/workflow/lobster-dashboard'
+export default {
+  data() { return { list:[], loading:false } },
+  created() { this.getList() },
+  methods: {
+    async getList() { this.loading=true; try { const res = await getActiveAbTests(0); this.list = (res.data||res).tests || [] } catch(e){} this.loading=false },
+    async apply(row) {
+      try { await applyAbTest(0, row.id); this.$message.success('已应用'); this.getList() } catch(e) { this.$message.error('应用失败') }
+    }
+  }
+}
+</script>

+ 43 - 0
src/views/lobster/channel-config/index.vue

@@ -0,0 +1,43 @@
+<template>
+  <div class="app-container">
+    <el-table border :data="channels" v-loading="loading">
+      <el-table-column prop="channelType" label="渠道编码" width="140"/>
+      <el-table-column prop="displayName" label="渠道名称"/>
+      <el-table-column prop="available" label="可用" width="80">
+        <template slot-scope="s"><el-tag :type="s.row.available?'success':'info'" size="small">{{ s.row.available?'可用':'不可用' }}</el-tag></template>
+      </el-table-column>
+      <el-table-column prop="enabled" label="启用" width="80">
+        <template slot-scope="s"><el-tag :type="s.row.enabled?'success':'danger'" size="small">{{ s.row.enabled?'已启用':'已禁用' }}</el-tag></template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+<script>
+import { getAvailableChannels } from '@/api/workflow/lobster'
+const DISPLAY_NAMES = {
+  WX: '微信客服', QW: '企微助手', IM: 'APP内IM', SMS: '短信', EMAIL: '邮件',
+  TMALL: '天猫', JD: '京东', DOUYIN_DM: '抖音私信', DOUYIN_EC: '抖音电商',
+  KUAISHOU_DM: '快手私信', XIAOHONGSHU_DM: '小红书', LINE: 'Line',
+  TELEGRAM: 'Telegram', APP_IM: 'APP私信', WHATSAPP: 'WhatsApp'
+}
+export default {
+  data() { return { channels: [], loading: false } },
+  created() { this.load() },
+  methods: {
+    async load() {
+      this.loading = true
+      try {
+        const res = await getAvailableChannels()
+        const data = res.data || {}
+        this.channels = Object.keys(data).map(k => ({
+          channelType: k,
+          displayName: DISPLAY_NAMES[k] || k,
+          available: data[k].available,
+          enabled: data[k].available
+        }))
+      } catch (e) { /* ignore */ }
+      this.loading = false
+    }
+  }
+}
+</script>

+ 60 - 0
src/views/lobster/dashboard/index.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="16">
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value">{{ dash.activeInstances24h || 0 }}</div><div class="stat-label">24h活跃实例</div></div></el-card></el-col>
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value">{{ dash.messages7d || 0 }}</div><div class="stat-label">7天消息量</div></div></el-card></el-col>
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value" style="color:#E6A23C">{{ deadLetterBacklog }}</div><div class="stat-label">死信积压</div></div></el-card></el-col>
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value" style="color:#67C23A">{{ heartbeats }}</div><div class="stat-label">引擎在线</div></div></el-card></el-col>
+    </el-row>
+
+    <el-row :gutter="16" style="margin-top:16px">
+      <el-col :span="12">
+        <el-card shadow="hover"><div slot="header"><span>转换漏斗</span></div>
+          <div v-for="(v,k) in funnel" :key="k" style="margin-bottom:12px">
+            <div style="display:flex;justify-content:space-between"><span>{{ k }}</span><span style="font-weight:bold">{{ v }}</span></div>
+            <el-progress :percentage="Math.min(100, v/(funnel.conversations||1)*100)" :show-text="false" :stroke-width="8" />
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="12">
+        <el-card shadow="hover"><div slot="header"><span>渠道分布</span></div>
+          <el-table border :data="channelData" size="small"><el-table-column prop="channel_type" label="渠道"/><el-table-column prop="cnt" label="会话数"/></el-table>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-card shadow="hover" style="margin-top:16px"><div slot="header"><span>知识库使用排行</span></div>
+      <el-table border :data="topKnowledge" size="small"><el-table-column prop="knowledge_title" label="知识条目"/><el-table-column prop="cnt" label="引用次数" width="100"/></el-table>
+    </el-card>
+
+    <el-card shadow="hover" style="margin-top:16px"><div slot="header"><span>心跳任务状态</span></div>
+      <el-table border :data="heartbeatList" size="small"><el-table-column prop="task_key" label="任务"/><el-table-column prop="status" label="状态"/><el-table-column prop="create_time" label="时间" width="160"/></el-table>
+    </el-card>
+  </div>
+</template>
+<script>
+import { getTenantDashboard } from '@/api/workflow/lobster-dashboard'
+export default {
+  data() { return { dash:{}, funnel:{}, channelData:[], topKnowledge:[], heartbeatList:[], deadLetterBacklog:0, heartbeats:0 } },
+  created() { this.load() },
+  methods: {
+    async load() {
+      try {
+        const res = await getTenantDashboard(this.$store.getters.companyId || 0)
+        this.dash = res.data || res
+        this.funnel = this.dash.conversionFunnel || {}
+        this.channelData = (this.dash.channelDistribution || []).map(r=>({channel_type:r.channel_type||r.channelType,cnt:r.cnt||r.count}))
+        this.topKnowledge = (this.dash.topKnowledge || []).slice(0,10)
+        this.heartbeatList = this.dash.heartbeatStatus || []
+        this.deadLetterBacklog = this.dash.deadLetterBacklog || 0
+        this.heartbeats = this.heartbeatList.length
+      } catch(e) { this.$message.error('加载失败') }
+    }
+  }
+}
+</script>
+<style scoped>
+.stat-card { text-align:center; padding:10px 0 }
+.stat-value { font-size:32px; font-weight:bold; color:#409EFF }
+.stat-label { font-size:13px; color:#909399; margin-top:4px }
+</style>

+ 69 - 0
src/views/lobster/evolution/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="16" class="mb8">
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value">{{ metrics.totalInteractions || 0 }}</div><div class="stat-label">总交互次数</div></div></el-card></el-col>
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value" style="color:#67C23A">{{ metrics.appliedSuggestions || 0 }}</div><div class="stat-label">已应用优化</div></div></el-card></el-col>
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value" style="color:#E6A23C">{{ metrics.pendingSuggestions || 0 }}</div><div class="stat-label">待审核建议</div></div></el-card></el-col>
+      <el-col :span="6"><el-card shadow="hover"><div class="stat-card"><div class="stat-value">{{ metrics.avgQualityScore || '-' }}</div><div class="stat-label">平均质量分</div></div></el-card></el-col>
+    </el-row>
+
+    <el-form :inline="true" class="mb8">
+      <el-form-item label="工作流ID">
+        <el-input v-model="workflowId" placeholder="输入工作流ID" size="small" style="width:160px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-data-analysis" size="mini" @click="handleAnalyze">触发进化分析</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="loadMetrics">刷新指标</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-card shadow="hover" v-if="suggestion">
+      <div slot="header"><span>最新进化建议</span></div>
+      <el-descriptions :column="2" border size="small">
+        <el-descriptions-item label="建议ID">{{ suggestion.id || suggestion.suggestionId }}</el-descriptions-item>
+        <el-descriptions-item label="置信度">{{ suggestion.confidence }}</el-descriptions-item>
+        <el-descriptions-item label="节点" :span="2">{{ suggestion.nodeCode }}</el-descriptions-item>
+        <el-descriptions-item label="原因" :span="2">{{ suggestion.reason }}</el-descriptions-item>
+        <el-descriptions-item label="优化内容" :span="2">{{ suggestion.suggestedContent }}</el-descriptions-item>
+      </el-descriptions>
+      <div style="margin-top:12px">
+        <el-button type="success" size="mini" @click="handleApply" :disabled="!(suggestion.id || suggestion.suggestionId)">应用建议</el-button>
+      </div>
+    </el-card>
+  </div>
+</template>
+<script>
+import { getEvolutionMetrics, analyzeEvolution, applyEvolution } from '@/api/workflow/lobster'
+export default {
+  name: 'LobsterEvolution',
+  data() {
+    return { metrics: {}, workflowId: '', suggestion: null }
+  },
+  created() { this.loadMetrics() },
+  methods: {
+    loadMetrics() {
+      getEvolutionMetrics().then(res => { this.metrics = res.data || {} }).catch(() => {})
+    },
+    handleAnalyze() {
+      if (!this.workflowId) { this.$message.warning('请输入工作流ID'); return }
+      analyzeEvolution(this.workflowId).then(res => {
+        this.suggestion = res.data || null
+        this.$message.success('分析完成')
+        this.loadMetrics()
+      })
+    },
+    handleApply() {
+      if (!this.suggestion || !(this.suggestion.id || this.suggestion.suggestionId)) return
+      applyEvolution(this.suggestion.id || this.suggestion.suggestionId).then(() => {
+        this.$message.success('建议已应用')
+        this.loadMetrics()
+      })
+    }
+  }
+}
+</script>
+<style scoped>
+.stat-card { text-align:center; padding:10px 0 }
+.stat-value { font-size:32px; font-weight:bold; color:#409EFF }
+.stat-label { font-size:13px; color:#909399; margin-top:4px }
+</style>

+ 42 - 0
src/views/lobster/integration-test/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5"><el-button type="primary" icon="el-icon-video-play" size="mini" :loading="running" @click="runAll">运行全部测试</el-button></el-col>
+      <el-col :span="1.5"><el-button plain icon="el-icon-cpu" size="mini" @click="runModule('node-types')">节点类型</el-button></el-col>
+      <el-col :span="1.5"><el-button plain icon="el-icon-connection" size="mini" @click="runModule('dynamic-executor')">动态执行器</el-button></el-col>
+    </el-row>
+
+    <el-alert v-if="summary.message" :title="summary.message" :type="summary.passed ? 'success' : 'error'" show-icon class="mb8" />
+
+    <el-table border :data="details" size="small">
+      <el-table-column prop="line" label="测试结果" show-overflow-tooltip />
+    </el-table>
+  </div>
+</template>
+<script>
+import { runIntegrationTests, testNodeTypes, testDynamicExecutor } from '@/api/workflow/lobster'
+export default {
+  name: 'LobsterIntegrationTest',
+  data() { return { running: false, summary: {}, details: [] } },
+  methods: {
+    showResult(data) {
+      this.summary = data || {}
+      const lines = (data && data.details) || []
+      this.details = lines.map(line => ({ line }))
+    },
+    runAll() {
+      this.running = true
+      runIntegrationTests().then(res => { this.showResult(res.data); this.$message.success('测试完成') })
+        .finally(() => { this.running = false })
+    },
+    runModule(type) {
+      const fn = type === 'node-types' ? testNodeTypes : testDynamicExecutor
+      fn().then(res => {
+        const r = res.data || {}
+        this.summary = { passed: r.passed, message: r.message }
+        this.details = (r.details || []).map(line => ({ line }))
+      })
+    }
+  }
+}
+</script>

+ 51 - 0
src/views/lobster/learning-results/index.vue

@@ -0,0 +1,51 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-button type="primary" icon="el-icon-video-play" size="mini" @click="trigger">触发学习周期</el-button></el-col></el-row>
+    <el-table border v-loading="loading" :data="list">
+      <el-table-column prop="title" label="发现模式" show-overflow-tooltip/>
+      <el-table-column prop="learningType" label="类型" width="90"/>
+      <el-table-column prop="description" label="证据" show-overflow-tooltip/>
+      <el-table-column prop="confidence" label="置信度" width="80"/>
+      <el-table-column prop="status" label="状态" width="80"><template slot-scope="s"><el-tag :type="s.row.status==='discovered'?'warning':'success'" size="small">{{ s.row.status }}</el-tag></template></el-table-column>
+      <el-table-column label="操作" width="100"><template slot-scope="s"><el-button v-if="s.row.status==='discovered'" size="mini" type="primary" @click="apply(s.row)">应用</el-button></template></el-table-column>
+    </el-table>
+  </div>
+</template>
+<script>
+import { triggerLearningCycle, getLearningResults, applyLearningResult } from '@/api/workflow/lobster-dashboard'
+export default {
+  data() { return { list:[], loading:false } },
+  computed: {
+    companyId() {
+      return this.$store.getters.companyId || (this.$store.state.user && this.$store.state.user.companyId) || 0
+    }
+  },
+  created() { this.getList() },
+  methods: {
+    async getList() {
+      this.loading = true
+      try {
+        const res = await getLearningResults(this.companyId)
+        const data = res.data || res
+        this.list = data.results || []
+      } catch (e) { /* ignore */ }
+      this.loading = false
+    },
+    async trigger() {
+      try {
+        const res = await triggerLearningCycle(this.companyId)
+        const data = res.data || res
+        this.$message.success(data.summary || '已触发')
+        this.getList()
+      } catch (e) { /* ignore */ }
+    },
+    async apply(row) {
+      try {
+        await applyLearningResult(this.companyId, row.id)
+        this.$message.success('已应用')
+        this.getList()
+      } catch (e) { /* ignore */ }
+    }
+  }
+}
+</script>

+ 77 - 0
src/views/lobster/node-capabilities/index.vue

@@ -0,0 +1,77 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="16" class="mb8">
+      <el-col :span="4"><el-card shadow="hover"><div class="stat-card"><div class="stat-value">{{ summary.totalTypes || 0 }}</div><div class="stat-label">节点类型总数</div></div></el-card></el-col>
+      <el-col :span="4"><el-card shadow="hover"><div class="stat-card"><div class="stat-value" style="color:#67C23A">{{ summary.fullCount || 0 }}</div><div class="stat-label">FULL 完整</div></div></el-card></el-col>
+      <el-col :span="4"><el-card shadow="hover"><div class="stat-card"><div class="stat-value" style="color:#E6A23C">{{ summary.partialCount || 0 }}</div><div class="stat-label">PARTIAL</div></div></el-card></el-col>
+      <el-col :span="4"><el-card shadow="hover"><div class="stat-card"><div class="stat-value">{{ summary.avgMaturityStars || '-' }}</div><div class="stat-label">平均成熟度</div></div></el-card></el-col>
+      <el-col :span="4"><el-card shadow="hover"><div class="stat-card"><div class="stat-value">{{ registeredCount }}</div><div class="stat-label">已注册Handler</div></div></el-card></el-col>
+      <el-col :span="4"><el-card shadow="hover"><div class="stat-card"><div class="stat-value" style="color:#F56C6C">{{ gapCount }}</div><div class="stat-label">未注册缺口</div></div></el-card></el-col>
+    </el-row>
+
+    <el-form :inline="true" class="mb8">
+      <el-form-item label="状态">
+        <el-select v-model="filterStatus" placeholder="全部" clearable size="small" @change="applyFilter">
+          <el-option label="FULL" value="FULL" /><el-option label="PARTIAL" value="PARTIAL" />
+          <el-option label="STUB" value="STUB" /><el-option label="NONE" value="NONE" />
+        </el-select>
+      </el-form-item>
+      <el-form-item><el-button icon="el-icon-refresh" size="mini" @click="load">刷新</el-button></el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="filteredList" size="small">
+      <el-table-column prop="code" label="编码" width="70" />
+      <el-table-column prop="codeName" label="标识" width="140" />
+      <el-table-column prop="name" label="名称" width="120" />
+      <el-table-column prop="maturityStars" label="成熟度" width="100">
+        <template slot-scope="s"><el-rate :value="s.row.maturityStars" disabled /></template>
+      </el-table-column>
+      <el-table-column prop="status" label="状态" width="90">
+        <template slot-scope="s"><el-tag :type="statusType(s.row.status)" size="mini">{{ s.row.status }}</el-tag></template>
+      </el-table-column>
+      <el-table-column prop="handlerRegistered" label="Handler" width="90">
+        <template slot-scope="s"><el-tag :type="s.row.handlerRegistered?'success':'danger'" size="mini">{{ s.row.handlerRegistered?'已注册':'缺失' }}</el-tag></template>
+      </el-table-column>
+      <el-table-column prop="handler" label="处理器" show-overflow-tooltip />
+      <el-table-column prop="gapNote" label="备注" show-overflow-tooltip />
+    </el-table>
+  </div>
+</template>
+<script>
+import { getNodeCapabilities } from '@/api/workflow/lobster'
+export default {
+  name: 'LobsterNodeCapabilities',
+  data() {
+    return { loading: false, list: [], summary: {}, registeredCount: 0, gapCount: 0, filterStatus: '' }
+  },
+  computed: {
+    filteredList() {
+      if (!this.filterStatus) return this.list
+      return this.list.filter(r => r.status === this.filterStatus)
+    }
+  },
+  created() { this.load() },
+  methods: {
+    load() {
+      this.loading = true
+      getNodeCapabilities().then(res => {
+        const d = res.data || {}
+        this.summary = d.summary || {}
+        this.list = d.capabilities || []
+        this.registeredCount = d.registeredHandlerCount || 0
+        this.gapCount = (d.gaps || []).length
+        this.loading = false
+      }).catch(() => { this.loading = false })
+    },
+    applyFilter() {},
+    statusType(s) {
+      return s === 'FULL' ? 'success' : s === 'PARTIAL' ? 'warning' : 'info'
+    }
+  }
+}
+</script>
+<style scoped>
+.stat-card { text-align:center; padding:10px 0 }
+.stat-value { font-size:28px; font-weight:bold; color:#409EFF }
+.stat-label { font-size:12px; color:#909399; margin-top:4px }
+</style>

+ 8 - 2
src/views/lobster/optimization/index.vue

@@ -50,7 +50,7 @@
 </template>
 <script>
 import { listAllCompanies } from '@/api/workflow/lobster-admin'
-import { listPendingAudit, batchAuditOptimization, auditSingleOptimization, getOptimizationStats } from '@/api/workflow/lobster'
+import { listPendingAudit, batchAuditOptimization, auditSingleOptimization, getOptimizationStats, analyzeEvolution } from '@/api/workflow/lobster'
 export default {
   name: 'LobsterOptimization',
   data() {
@@ -74,7 +74,13 @@ export default {
       let items = this.ids.map(id => ({ optimizationId: id, approved, remark: '' }))
       batchAuditOptimization({ auditItems: items, auditRemark: approved?'批量通过':'批量驳回' }).then(() => { this.$message.success('批量审核完成'); this.getList(); this.getStats() })
     },
-    handleAnalyze() { this.$message.info('请输入用户ID触发分析') }
+    handleAnalyze() {
+      if (!this.queryParams.workflowId) { this.$message.warning('请输入工作流ID'); return }
+      analyzeEvolution(this.queryParams.workflowId).then(() => {
+        this.$message.success('进化分析已触发')
+        this.getList(); this.getStats()
+      })
+    }
   }
 }
 </script>

+ 28 - 0
src/views/lobster/pay-manage/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <div class="app-container">
+    <el-form :model="q" inline><el-form-item label="订单号"><el-input v-model="q.orderNo" size="small"/></el-form-item>
+    <el-form-item label="网关"><el-select v-model="q.gateway" size="small"><el-option label="微信" value="wechat"/><el-option label="支付宝" value="alipay"/><el-option label="Mock" value="mock"/></el-select></el-form-item>
+    <el-form-item><el-button icon="el-icon-search" size="small" @click="search">查询</el-button></el-form-item></el-form>
+    <el-table border :data="results" v-loading="loading">
+      <el-table-column prop="orderNo" label="订单号" width="200"/>
+      <el-table-column prop="gateway" label="支付网关" width="100"/>
+      <el-table-column prop="status" label="状态" width="100"><template slot-scope="s"><el-tag :type="s.row.status==='PAID'?'success':s.row.status==='CREATED'?'warning':'info'" size="small">{{ s.row.status }}</el-tag></template></el-table-column>
+      <el-table-column prop="transactionId" label="交易号" width="160"/>
+      <el-table-column prop="payUrl" label="支付链接" show-overflow-tooltip/>
+    </el-table>
+  </div>
+</template>
+<script>
+import { queryPayStatus } from '@/api/workflow/lobster-dashboard'
+export default {
+  data() { return { q:{orderNo:'',gateway:'wechat'}, results:[], loading:false } },
+  methods: {
+    async search() {
+      if(!this.q.orderNo) return this.$message.warning('请输入订单号')
+      this.loading=true
+      try { const res = await queryPayStatus(this.q.orderNo, this.q.gateway); this.results=[res.data||res] } catch(e){}
+      this.loading=false
+    }
+  }
+}
+</script>