#feature: update AI log analysis

This commit is contained in:
2026-02-11 11:00:32 +08:00
parent ddd0f531fd
commit 53bca7d609
18 changed files with 688 additions and 262 deletions

View File

@@ -1,133 +1,106 @@
<template>
<div class="p-6">
<div class="p-4">
<!-- 页面标题 -->
<div class="mb-6">
<h1 class="text-2xl font-bold text-gray-900">消息同步</h1>
<p class="text-gray-600 mt-2">批量输入消息ID从crmslave数据库查询并同步到agent服务</p>
<div class="mb-3 flex items-center justify-between">
<div>
<h1 class="text-lg font-bold text-gray-900">消息同步</h1>
<p class="text-xs text-gray-500 mt-0.5">输入消息ID通过Mono服务重新消费并分发消息</p>
</div>
<button
@click="testConnection"
:disabled="loading.test"
class="px-3 py-1.5 text-xs bg-gray-100 text-gray-600 rounded hover:bg-gray-200 disabled:opacity-50 flex items-center"
>
<svg v-if="loading.test" class="animate-spin -ml-0.5 mr-1.5 h-3 w-3" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
测试连接
</button>
</div>
<!-- 错误信息 -->
<div v-if="error" class="bg-red-50 border border-red-200 rounded px-3 py-2 mb-3 flex items-start text-sm">
<svg class="w-4 h-4 text-red-400 mr-1.5 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span class="text-red-700">{{ error }}</span>
</div>
<!-- 输入区域 -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
<h2 class="text-xl font-semibold text-gray-700 mb-4">消息ID输入</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
消息ID列表 (每行一个ID)
</label>
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 mb-3">
<div class="flex gap-4">
<div class="flex-1">
<div class="flex items-center justify-between mb-1.5">
<label class="text-xs font-medium text-gray-600">消息ID每行一个</label>
<span class="text-xs text-gray-400">{{ messageIdsList.length }} </span>
</div>
<textarea
v-model="messageIdsText"
rows="8"
class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入消息ID每行一个&#10;例如:&#10;af7e5ca7-2779-0e9e-93d1-68c79ceffd9033&#10;bf8f6db8-3880-1f0f-a4e2-79d8adf00144"
rows="6"
class="w-full border border-gray-300 rounded px-2.5 py-1.5 text-sm font-mono focus:ring-1 focus:ring-blue-500 focus:border-blue-500 resize-none"
placeholder="af7e5ca7-2779-0e9e-93d1-68c79ceffd9033&#10;bf8f6db8-3880-1f0f-a4e2-79d8adf00144"
></textarea>
<div class="text-sm text-gray-500 mt-1">
{{ messageIdsList.length }} 个消息ID
</div>
</div>
<div class="flex space-x-4">
<div class="flex flex-col gap-2 pt-6">
<button
@click="queryMessages"
:disabled="loading.query || messageIdsList.length === 0"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
class="px-4 py-1.5 text-sm bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center whitespace-nowrap"
>
<svg v-if="loading.query" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<svg v-if="loading.query" class="animate-spin -ml-0.5 mr-1.5 h-3.5 w-3.5 text-white" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
查询消息
</button>
<button
@click="syncMessages"
:disabled="loading.sync || !queryResults || messageIdsList.length === 0"
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
:disabled="loading.sync || messageIdsList.length === 0"
class="px-4 py-1.5 text-sm bg-green-600 text-white rounded hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center whitespace-nowrap"
>
<svg v-if="loading.sync" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<svg v-if="loading.sync" class="animate-spin -ml-0.5 mr-1.5 h-3.5 w-3.5 text-white" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
执行同步
</button>
<button
@click="testConnection"
:disabled="loading.test"
class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
>
<svg v-if="loading.test" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
测试连接
</button>
</div>
</div>
</div>
<!-- 错误信息 -->
<div v-if="error" class="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
<div class="flex">
<svg class="w-5 h-5 text-red-400 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="text-sm font-medium text-red-800">错误</h3>
<p class="text-sm text-red-700 mt-1">{{ error }}</p>
</div>
</div>
</div>
<!-- 查询结果 -->
<div v-if="queryResults" class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
<h2 class="text-xl font-semibold text-gray-700 mb-4">查询结果</h2>
<!-- 统计信息 -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<div class="bg-blue-50 rounded-lg p-4">
<div class="text-2xl font-bold text-blue-600">{{ queryResults.stats.total_requested }}</div>
<div class="text-sm text-blue-600">请求总数</div>
</div>
<div class="bg-green-50 rounded-lg p-4">
<div class="text-2xl font-bold text-green-600">{{ queryResults.stats.total_found }}</div>
<div class="text-sm text-green-600">找到记录</div>
</div>
<div class="bg-red-50 rounded-lg p-4">
<div class="text-2xl font-bold text-red-600">{{ queryResults.stats.total_missing }}</div>
<div class="text-sm text-red-600">缺失记录</div>
</div>
<div class="bg-purple-50 rounded-lg p-4">
<div class="text-2xl font-bold text-purple-600">{{ Object.keys(queryResults.stats.event_types).length }}</div>
<div class="text-sm text-purple-600">事件类型</div>
<div v-if="queryResults" class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 mb-3">
<div class="flex items-center justify-between mb-3">
<h2 class="text-sm font-semibold text-gray-700">查询结果</h2>
<div class="flex gap-4 text-xs">
<span class="text-blue-600">请求 <b>{{ queryResults.stats.total_requested }}</b></span>
<span class="text-green-600">找到 <b>{{ queryResults.stats.total_found }}</b></span>
<span class="text-red-600">缺失 <b>{{ queryResults.stats.total_missing }}</b></span>
<span class="text-purple-600">类型 <b>{{ Object.keys(queryResults.stats.event_types).length }}</b></span>
</div>
</div>
<!-- 消息列表 -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">消息ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">事件类型</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">跟踪ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">时间</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
<table class="min-w-full text-sm">
<thead>
<tr class="border-b border-gray-200 text-xs text-gray-500">
<th class="text-left py-2 pr-3 font-medium">消息ID</th>
<th class="text-left py-2 pr-3 font-medium">事件类型</th>
<th class="text-left py-2 pr-3 font-medium">跟踪ID</th>
<th class="text-left py-2 pr-3 font-medium">时间</th>
<th class="text-left py-2 font-medium w-12"></th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="message in queryResults.messages" :key="message.msg_id">
<td class="px-6 py-4 whitespace-nowrap text-sm font-mono text-gray-900">{{ message.msg_id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ message.event_type }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-mono text-gray-500">{{ message.trace_id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ formatTimestamp(message.timestamp) }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<button
@click="showMessageDetail(message)"
class="text-blue-600 hover:text-blue-900"
>
查看详情
</button>
<tbody class="divide-y divide-gray-100">
<tr v-for="message in queryResults.messages" :key="message.msg_id" class="hover:bg-gray-50">
<td class="py-1.5 pr-3 font-mono text-xs text-gray-900">{{ message.msg_id }}</td>
<td class="py-1.5 pr-3 text-gray-700">{{ message.event_type }}</td>
<td class="py-1.5 pr-3 font-mono text-xs text-gray-400">{{ message.trace_id }}</td>
<td class="py-1.5 pr-3 text-xs text-gray-500 whitespace-nowrap">{{ formatTimestamp(message.timestamp) }}</td>
<td class="py-1.5">
<button @click="showMessageDetail(message)" class="text-blue-500 hover:text-blue-700 text-xs">详情</button>
</td>
</tr>
</tbody>
@@ -136,57 +109,39 @@
</div>
<!-- 同步结果 -->
<div v-if="syncResults" class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h2 class="text-xl font-semibold text-gray-700 mb-4">同步结果</h2>
<!-- 同步统计 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="bg-blue-50 rounded-lg p-4">
<div class="text-2xl font-bold text-blue-600">{{ syncResults.summary.total }}</div>
<div class="text-sm text-blue-600">总计</div>
</div>
<div class="bg-green-50 rounded-lg p-4">
<div class="text-2xl font-bold text-green-600">{{ syncResults.summary.success }}</div>
<div class="text-sm text-green-600">成功</div>
</div>
<div class="bg-red-50 rounded-lg p-4">
<div class="text-2xl font-bold text-red-600">{{ syncResults.summary.failure }}</div>
<div class="text-sm text-red-600">失败</div>
<div v-if="syncResults" class="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<div class="flex items-center justify-between mb-3">
<h2 class="text-sm font-semibold text-gray-700">同步结果</h2>
<div class="flex gap-4 text-xs">
<span class="text-blue-600">总计 <b>{{ syncResults.summary.total }}</b></span>
<span class="text-green-600">成功 <b>{{ syncResults.summary.success }}</b></span>
<span class="text-red-600">失败 <b>{{ syncResults.summary.failure }}</b></span>
</div>
</div>
<!-- 同步结果列表 -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">消息ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">响应</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
<table class="min-w-full text-sm">
<thead>
<tr class="border-b border-gray-200 text-xs text-gray-500">
<th class="text-left py-2 pr-3 font-medium">消息ID</th>
<th class="text-left py-2 pr-3 font-medium w-16">状态</th>
<th class="text-left py-2 pr-3 font-medium">响应</th>
<th class="text-left py-2 font-medium w-12"></th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="result in syncResults.results" :key="result.msg_id">
<td class="px-6 py-4 whitespace-nowrap text-sm font-mono text-gray-900">{{ result.msg_id }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span v-if="result.success" class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
成功
</span>
<span v-else class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
失败
</span>
<tbody class="divide-y divide-gray-100">
<tr v-for="result in syncResults.results" :key="result.msg_id" class="hover:bg-gray-50">
<td class="py-1.5 pr-3 font-mono text-xs text-gray-900">{{ result.msg_id }}</td>
<td class="py-1.5 pr-3">
<span v-if="result.success" class="inline-block px-1.5 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700">成功</span>
<span v-else class="inline-block px-1.5 py-0.5 rounded text-xs font-medium bg-red-100 text-red-700">失败</span>
</td>
<td class="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">
{{ result.success ? '同步成功' : result.error }}
<td class="py-1.5 pr-3 text-xs text-gray-500 max-w-md truncate">
{{ result.success ? '消息消费成功' : result.error }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<button
@click="showSyncDetail(result)"
class="text-blue-600 hover:text-blue-900"
>
查看详情
</button>
<td class="py-1.5">
<button @click="showSyncDetail(result)" class="text-blue-500 hover:text-blue-700 text-xs">详情</button>
</td>
</tr>
</tbody>
@@ -195,20 +150,18 @@
</div>
<!-- 详情模态框 -->
<div v-if="showDetailModal" class="fixed inset-0 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-11/12 md:w-3/4 lg:w-1/2 shadow-lg rounded-md bg-white">
<div class="mt-3">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">详细信息</h3>
<button @click="closeDetailModal" class="text-gray-400 hover:text-gray-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="max-h-96 overflow-y-auto">
<pre class="bg-gray-100 p-4 rounded-lg text-sm overflow-x-auto">{{ JSON.stringify(selectedDetail, null, 2) }}</pre>
</div>
<div v-if="showDetailModal" class="fixed inset-0 overflow-y-auto h-full w-full z-50" @click.self="closeDetailModal">
<div class="relative top-16 mx-auto p-4 border w-11/12 md:w-2/3 lg:w-1/2 shadow-lg rounded-lg bg-white">
<div class="flex justify-between items-center mb-3">
<h3 class="text-sm font-semibold text-gray-900">详细信息</h3>
<button @click="closeDetailModal" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="max-h-[70vh] overflow-y-auto">
<pre class="bg-gray-50 p-3 rounded text-xs font-mono overflow-x-auto leading-relaxed">{{ JSON.stringify(selectedDetail, null, 2) }}</pre>
</div>
</div>
</div>