From 2d7b1943cd77561954e11bae075575c29287ae51 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Tue, 16 Jun 2026 14:13:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=97=A5=E5=BF=97=E7=9B=91=E6=8E=A7?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=20=E2=80=94=208=20=E4=B8=AA=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 概览仪表盘(统计卡片 + 趋势图 + Top5) - 错误列表(分页 + 筛选) - 错误详情(符号化 stack + 源码上下文) - 事件流水(分页 + 筛选) - 漏斗分析(动态步骤 + 转化率) - Webhook 配置(CRUD) - 高频/高危排行 Co-Authored-By: Claude --- tenant-platform/src/api/log.ts | 146 +++++++++++++++ tenant-platform/src/router/index.ts | 33 ++++ .../src/views/layout/MainLayout.vue | 23 ++- .../src/views/log-monitor/LogEvents.vue | 150 ++++++++++++++++ .../src/views/log-monitor/LogFunnels.vue | 120 +++++++++++++ .../src/views/log-monitor/LogIssueDetail.vue | 159 ++++++++++++++++ .../src/views/log-monitor/LogIssues.vue | 151 ++++++++++++++++ .../src/views/log-monitor/LogOverview.vue | 169 ++++++++++++++++++ .../src/views/log-monitor/LogRankFreq.vue | 72 ++++++++ .../src/views/log-monitor/LogRankRisk.vue | 84 +++++++++ .../src/views/log-monitor/LogWebhooks.vue | 153 ++++++++++++++++ tenant-platform/vite.config.ts | 4 + 12 files changed, 1263 insertions(+), 1 deletion(-) create mode 100644 tenant-platform/src/api/log.ts create mode 100644 tenant-platform/src/views/log-monitor/LogEvents.vue create mode 100644 tenant-platform/src/views/log-monitor/LogFunnels.vue create mode 100644 tenant-platform/src/views/log-monitor/LogIssueDetail.vue create mode 100644 tenant-platform/src/views/log-monitor/LogIssues.vue create mode 100644 tenant-platform/src/views/log-monitor/LogOverview.vue create mode 100644 tenant-platform/src/views/log-monitor/LogRankFreq.vue create mode 100644 tenant-platform/src/views/log-monitor/LogRankRisk.vue create mode 100644 tenant-platform/src/views/log-monitor/LogWebhooks.vue diff --git a/tenant-platform/src/api/log.ts b/tenant-platform/src/api/log.ts new file mode 100644 index 0000000..0bcd9b7 --- /dev/null +++ b/tenant-platform/src/api/log.ts @@ -0,0 +1,146 @@ +import client from './client' + +// ── Types ─────────────────────────────────────────────────────────────────── + +export interface LogOverview { + totalIssues: number + todayNewIssues: number + affectedUsers: number + crashRate: number + crashRateTrend: { date: string; rate: number }[] + topIssues: { id: string; title: string; type: string; count: number }[] +} + +export interface LogIssue { + id: string + title: string + type: string + platform: string + count: number + affectedUsers: number + lastSeenAt: string + firstSeenAt: string + status: string +} + +export interface LogIssueDetail { + id: string + title: string + type: string + platform: string + appVersion: string + osVersion: string + deviceModel: string + count: number + affectedUsers: number + firstSeenAt: string + lastSeenAt: string + status: string + stackTrace: string + sourceContext?: { line: number; content: string; highlight: boolean }[] + recentEvents: LogEventItem[] +} + +export interface LogEventItem { + id: string + eventName: string + userId: string + timestamp: string + properties: Record +} + +export interface LogIssueRanking { + id: string + title: string + type: string + platform: string + count: number + affectedUsers: number + riskScore?: number + lastSeenAt: string +} + +export interface LogFunnelStep { + eventName: string + count: number + conversionRate: number +} + +export interface LogWebhook { + id: string + url: string + eventTypes: string[] + cooldownSeconds: number + enabled: boolean + createdAt: string + updatedAt: string +} + +export interface LogPageResult { + content: T[] + page: number + size: number + totalElements: number + totalPages: number +} + +// ── API ───────────────────────────────────────────────────────────────────── + +export const logApi = { + // Overview + overview: () => client.get<{ data: LogOverview }>('/log/v1/overview'), + + // Issues + issues(params: { + type?: string + platform?: string + startDate?: string + endDate?: string + page?: number + size?: number + }) { + return client.get<{ data: LogPageResult }>('/log/v1/issues', { params }) + }, + + issueDetail(id: string) { + return client.get<{ data: LogIssueDetail }>(`/log/v1/issues/${id}`) + }, + + // Rankings + frequencyRanking() { + return client.get<{ data: LogIssueRanking[] }>('/log/v1/issues/rankings/frequency') + }, + + riskRanking() { + return client.get<{ data: LogIssueRanking[] }>('/log/v1/issues/rankings/risk') + }, + + // Events + events(params: { + eventName?: string + userId?: string + startDate?: string + endDate?: string + page?: number + size?: number + }) { + return client.get<{ data: LogPageResult }>('/log/v1/events', { params }) + }, + + // Funnel + funnel(steps: string[]) { + return client.get<{ data: LogFunnelStep[] }>('/log/v1/events/funnel', { + params: { steps: steps.join(',') }, + }) + }, + + // Webhooks + webhooks: { + list: () => client.get<{ data: LogWebhook[] }>('/log/v1/webhooks'), + create: (data: Omit) => + client.post<{ data: LogWebhook }>('/log/v1/webhooks', data), + update: (id: string, data: Partial>) => + client.put<{ data: LogWebhook }>(`/log/v1/webhooks/${id}`, data), + delete: (id: string) => client.delete(`/log/v1/webhooks/${id}`), + }, +} diff --git a/tenant-platform/src/router/index.ts b/tenant-platform/src/router/index.ts index 6b62328..98e73fd 100644 --- a/tenant-platform/src/router/index.ts +++ b/tenant-platform/src/router/index.ts @@ -113,6 +113,39 @@ const router = createRouter({ path: 'accounts', component: () => import('@/views/accounts/SubAccountView.vue'), }, + // ── 日志监控 ──────────────────────────────────────────────── + { + path: 'log/overview', + component: () => import('@/views/log-monitor/LogOverview.vue'), + }, + { + path: 'log/issues', + component: () => import('@/views/log-monitor/LogIssues.vue'), + }, + { + path: 'log/issues/:id', + component: () => import('@/views/log-monitor/LogIssueDetail.vue'), + }, + { + path: 'log/events', + component: () => import('@/views/log-monitor/LogEvents.vue'), + }, + { + path: 'log/funnels', + component: () => import('@/views/log-monitor/LogFunnels.vue'), + }, + { + path: 'log/webhooks', + component: () => import('@/views/log-monitor/LogWebhooks.vue'), + }, + { + path: 'log/rank/freq', + component: () => import('@/views/log-monitor/LogRankFreq.vue'), + }, + { + path: 'log/rank/risk', + component: () => import('@/views/log-monitor/LogRankRisk.vue'), + }, ], }, ], diff --git a/tenant-platform/src/views/layout/MainLayout.vue b/tenant-platform/src/views/layout/MainLayout.vue index 8de3e99..df07d8c 100644 --- a/tenant-platform/src/views/layout/MainLayout.vue +++ b/tenant-platform/src/views/layout/MainLayout.vue @@ -29,6 +29,16 @@ 数据库管理 操作日志 + + + 概览 + 错误列表 + 事件流水 + 漏斗分析 + 高频排行 + 高危排行 + Webhook + 子账号管理 @@ -69,6 +79,16 @@ 数据库管理 操作日志 + + + 概览 + 错误列表 + 事件流水 + 漏斗分析 + 高频排行 + 高危排行 + Webhook + 子账号管理 @@ -116,7 +136,7 @@ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue' import { useAuthStore } from '@/stores/auth' import { useRoute, useRouter } from 'vue-router' -import { Bell, ChatDotRound, Coin, Document, Grid, Key, List, Lock, Menu, Monitor, Odometer, Setting, Upload, User } from '@element-plus/icons-vue' +import { AlertTriangle, Bell, ChatDotRound, Coin, DataLine, Document, Filter, Grid, Key, Link, List, Lock, Menu, Monitor, Odometer, Setting, Sort, Upload, User, Warning } from '@element-plus/icons-vue' import { getDeploymentStatus } from '@/api/system' const auth = useAuthStore() @@ -130,6 +150,7 @@ const openedMenus = computed(() => { const menus: string[] = [] if (route.path.startsWith('/services/')) menus.push('services') if (['/system-logs', '/database', '/operation-logs'].includes(route.path)) menus.push('ops') + if (route.path.startsWith('/log/')) menus.push('log-monitor') return menus }) diff --git a/tenant-platform/src/views/log-monitor/LogEvents.vue b/tenant-platform/src/views/log-monitor/LogEvents.vue new file mode 100644 index 0000000..41d8312 --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogEvents.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/tenant-platform/src/views/log-monitor/LogFunnels.vue b/tenant-platform/src/views/log-monitor/LogFunnels.vue new file mode 100644 index 0000000..1ed8366 --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogFunnels.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/tenant-platform/src/views/log-monitor/LogIssueDetail.vue b/tenant-platform/src/views/log-monitor/LogIssueDetail.vue new file mode 100644 index 0000000..73adf67 --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogIssueDetail.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/tenant-platform/src/views/log-monitor/LogIssues.vue b/tenant-platform/src/views/log-monitor/LogIssues.vue new file mode 100644 index 0000000..41b3ced --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogIssues.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/tenant-platform/src/views/log-monitor/LogOverview.vue b/tenant-platform/src/views/log-monitor/LogOverview.vue new file mode 100644 index 0000000..b31b1a1 --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogOverview.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/tenant-platform/src/views/log-monitor/LogRankFreq.vue b/tenant-platform/src/views/log-monitor/LogRankFreq.vue new file mode 100644 index 0000000..60221bb --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogRankFreq.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/tenant-platform/src/views/log-monitor/LogRankRisk.vue b/tenant-platform/src/views/log-monitor/LogRankRisk.vue new file mode 100644 index 0000000..ed09c5a --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogRankRisk.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/tenant-platform/src/views/log-monitor/LogWebhooks.vue b/tenant-platform/src/views/log-monitor/LogWebhooks.vue new file mode 100644 index 0000000..9c85965 --- /dev/null +++ b/tenant-platform/src/views/log-monitor/LogWebhooks.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/tenant-platform/vite.config.ts b/tenant-platform/vite.config.ts index 1cb2618..d05ff28 100644 --- a/tenant-platform/vite.config.ts +++ b/tenant-platform/vite.config.ts @@ -42,6 +42,10 @@ export default defineConfig(({ mode }) => { server: { port: 5173, proxy: { + '/api/log': { + target: 'http://127.0.0.1:8087', + changeOrigin: true, + }, '/api/license': { target: 'http://127.0.0.1:8085', changeOrigin: true,