fix(bugcollect): 对齐前后端接口契约
- bugcollect.ts: issues/events 请求参数重映射(startDate/endDate→from/to,eventName→name,page 0-based+1→backend 1-based) - BugCollectPageResult: content/totalElements → items/total(匹配后端 PageResult<T>) - BugCollectIssue: 字段对齐 IssueResponse(id:number,isResolved,移除 affectedUsers/status) - BugCollectEventItem: 字段对齐 IssueEventResponse(message/stack/createdAt,移除 eventName/timestamp/properties) - BugCollectIssueRanking: 同步 isResolved,移除 affectedUsers - 所有视图表格列更新为实际存在的字段 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
9cc6a53256
当前提交
3676241f6a
@ -11,53 +11,65 @@ export interface BugCollectOverview {
|
|||||||
topIssues: { id: string; title: string; type: string; count: number }[]
|
topIssues: { id: string; title: string; type: string; count: number }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches backend IssueResponse
|
||||||
export interface BugCollectIssue {
|
export interface BugCollectIssue {
|
||||||
id: string
|
id: number
|
||||||
title: string
|
appKey: string
|
||||||
|
fingerprint?: string
|
||||||
type: string
|
type: string
|
||||||
platform: string
|
title: string
|
||||||
count: number
|
|
||||||
affectedUsers: number
|
|
||||||
lastSeenAt: string
|
|
||||||
firstSeenAt: string
|
firstSeenAt: string
|
||||||
status: string
|
lastSeenAt: string
|
||||||
|
count: number
|
||||||
|
isResolved: boolean
|
||||||
|
platform: string
|
||||||
|
appVersion?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches backend IssueResponse (with events embedded)
|
||||||
export interface BugCollectIssueDetail {
|
export interface BugCollectIssueDetail {
|
||||||
id: string
|
id: number
|
||||||
title: string
|
appKey: string
|
||||||
|
fingerprint?: string
|
||||||
type: string
|
type: string
|
||||||
platform: string
|
title: string
|
||||||
appVersion: string
|
|
||||||
osVersion: string
|
|
||||||
deviceModel: string
|
|
||||||
count: number
|
|
||||||
affectedUsers: number
|
|
||||||
firstSeenAt: string
|
firstSeenAt: string
|
||||||
lastSeenAt: string
|
lastSeenAt: string
|
||||||
status: string
|
count: number
|
||||||
stackTrace: string
|
isResolved: boolean
|
||||||
sourceContext?: { line: number; content: string; highlight: boolean }[]
|
platform: string
|
||||||
recentEvents: BugCollectEventItem[]
|
appVersion?: string
|
||||||
|
events?: BugCollectEventItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches backend IssueEventResponse
|
||||||
export interface BugCollectEventItem {
|
export interface BugCollectEventItem {
|
||||||
id: string
|
id: number
|
||||||
eventName: string
|
issueId?: number
|
||||||
userId: string
|
appKey?: string
|
||||||
timestamp: string
|
userId?: string
|
||||||
properties: Record<string, unknown>
|
sessionId?: string
|
||||||
|
message?: string
|
||||||
|
stack?: string
|
||||||
|
stackSymbolicated?: string
|
||||||
|
metadata?: string
|
||||||
|
platform?: string
|
||||||
|
appVersion?: string
|
||||||
|
createdAt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches backend IssueResponse used for rankings
|
||||||
export interface BugCollectIssueRanking {
|
export interface BugCollectIssueRanking {
|
||||||
id: string
|
id: number
|
||||||
title: string
|
title: string
|
||||||
type: string
|
type: string
|
||||||
platform: string
|
platform: string
|
||||||
count: number
|
count: number
|
||||||
affectedUsers: number
|
isResolved: boolean
|
||||||
riskScore?: number
|
firstSeenAt: string
|
||||||
lastSeenAt: string
|
lastSeenAt: string
|
||||||
|
appVersion?: string
|
||||||
|
riskScore?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BugCollectFunnelStep {
|
export interface BugCollectFunnelStep {
|
||||||
@ -76,12 +88,12 @@ export interface BugCollectWebhook {
|
|||||||
updatedAt: string
|
updatedAt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches backend PageResult<T> (items/total, not content/totalElements)
|
||||||
export interface BugCollectPageResult<T> {
|
export interface BugCollectPageResult<T> {
|
||||||
content: T[]
|
items: T[]
|
||||||
page: number
|
page: number
|
||||||
size: number
|
size: number
|
||||||
totalElements: number
|
total: number
|
||||||
totalPages: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── API ─────────────────────────────────────────────────────────────────────
|
// ── API ─────────────────────────────────────────────────────────────────────
|
||||||
@ -91,7 +103,7 @@ export const bugCollectApi = {
|
|||||||
overview: (appKey: string) =>
|
overview: (appKey: string) =>
|
||||||
client.get<{ data: BugCollectOverview }>('/bugcollect/v1/overview', { params: { appKey } }),
|
client.get<{ data: BugCollectOverview }>('/bugcollect/v1/overview', { params: { appKey } }),
|
||||||
|
|
||||||
// Issues
|
// Issues — page is 1-based (backend convention); startDate/endDate mapped to from/to
|
||||||
issues(appKey: string, params: {
|
issues(appKey: string, params: {
|
||||||
type?: string
|
type?: string
|
||||||
platform?: string
|
platform?: string
|
||||||
@ -100,7 +112,10 @@ export const bugCollectApi = {
|
|||||||
page?: number
|
page?: number
|
||||||
size?: number
|
size?: number
|
||||||
}) {
|
}) {
|
||||||
return client.get<{ data: BugCollectPageResult<BugCollectIssue> }>('/bugcollect/v1/issues', { params: { appKey, ...params } })
|
const { startDate, endDate, page, ...rest } = params
|
||||||
|
return client.get<{ data: BugCollectPageResult<BugCollectIssue> }>('/bugcollect/v1/issues', {
|
||||||
|
params: { appKey, ...rest, from: startDate, to: endDate, page: (page ?? 0) + 1 },
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
issueDetail(id: string) {
|
issueDetail(id: string) {
|
||||||
@ -116,7 +131,7 @@ export const bugCollectApi = {
|
|||||||
return client.get<{ data: BugCollectIssueRanking[] }>('/bugcollect/v1/issues/rankings/risk', { params: { appKey } })
|
return client.get<{ data: BugCollectIssueRanking[] }>('/bugcollect/v1/issues/rankings/risk', { params: { appKey } })
|
||||||
},
|
},
|
||||||
|
|
||||||
// Events
|
// Events — eventName→name, startDate/endDate→from/to, page is 0-based from caller → +1 for backend
|
||||||
events(appKey: string, params: {
|
events(appKey: string, params: {
|
||||||
eventName?: string
|
eventName?: string
|
||||||
userId?: string
|
userId?: string
|
||||||
@ -125,7 +140,10 @@ export const bugCollectApi = {
|
|||||||
page?: number
|
page?: number
|
||||||
size?: number
|
size?: number
|
||||||
}) {
|
}) {
|
||||||
return client.get<{ data: BugCollectPageResult<BugCollectEventItem> }>('/bugcollect/v1/events', { params: { appKey, ...params } })
|
const { eventName, startDate, endDate, page, ...rest } = params
|
||||||
|
return client.get<{ data: BugCollectPageResult<BugCollectEventItem> }>('/bugcollect/v1/events', {
|
||||||
|
params: { appKey, ...rest, name: eventName, from: startDate, to: endDate, page: (page ?? 0) + 1 },
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Funnel
|
// Funnel
|
||||||
|
|||||||
@ -58,20 +58,22 @@
|
|||||||
|
|
||||||
<div class="table-wrap">
|
<div class="table-wrap">
|
||||||
<el-table :data="events" v-loading="loading" border stripe>
|
<el-table :data="events" v-loading="loading" border stripe>
|
||||||
<el-table-column prop="eventName" label="事件名" width="180" />
|
<el-table-column prop="platform" label="平台" width="100" />
|
||||||
<el-table-column prop="userId" label="用户 ID" width="200" show-overflow-tooltip />
|
<el-table-column prop="appVersion" label="版本" width="110" />
|
||||||
<el-table-column prop="timestamp" label="时间" width="170" sortable>
|
<el-table-column prop="userId" label="用户 ID" width="180" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="message" label="错误信息" min-width="260" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="createdAt" label="时间" width="170" sortable>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="time-text">{{ formatTime(row.timestamp) }}</span>
|
<span class="time-text">{{ formatTime(row.createdAt) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="属性" min-width="300">
|
<el-table-column label="堆栈" width="90" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-popover trigger="click" :width="420">
|
<el-popover trigger="click" :width="600">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button link type="primary" size="small">查看属性</el-button>
|
<el-button link type="primary" size="small" :disabled="!row.stack">查看</el-button>
|
||||||
</template>
|
</template>
|
||||||
<pre class="props-json">{{ JSON.stringify(row.properties, null, 2) }}</pre>
|
<pre class="props-json">{{ row.stackSymbolicated || row.stack }}</pre>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@ -137,8 +139,8 @@ async function loadData() {
|
|||||||
size: pageSize.value,
|
size: pageSize.value,
|
||||||
})
|
})
|
||||||
const data = res.data.data
|
const data = res.data.data
|
||||||
events.value = data.content ?? []
|
events.value = data.items ?? []
|
||||||
total.value = data.totalElements ?? 0
|
total.value = data.total ?? 0
|
||||||
} catch {
|
} catch {
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|||||||
@ -67,7 +67,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="count" label="次数" width="100" sortable />
|
<el-table-column prop="count" label="次数" width="100" sortable />
|
||||||
<el-table-column prop="affectedUsers" label="影响用户" width="110" sortable />
|
<el-table-column prop="isResolved" label="状态" width="90"><template #default="{ row }"><el-tag size="small" :type="row.isResolved ? 'success' : 'danger'">{{ row.isResolved ? '已解决' : '未解决' }}</el-tag></template></el-table-column>
|
||||||
<el-table-column prop="platform" label="平台" width="110" />
|
<el-table-column prop="platform" label="平台" width="110" />
|
||||||
<el-table-column prop="lastSeenAt" label="最后出现" width="170" sortable>
|
<el-table-column prop="lastSeenAt" label="最后出现" width="170" sortable>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@ -146,8 +146,8 @@ async function loadData() {
|
|||||||
size: pageSize.value,
|
size: pageSize.value,
|
||||||
})
|
})
|
||||||
const data = res.data.data
|
const data = res.data.data
|
||||||
issues.value = data.content ?? []
|
issues.value = data.items ?? []
|
||||||
total.value = data.totalElements ?? 0
|
total.value = data.total ?? 0
|
||||||
} catch {
|
} catch {
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="platform" label="平台" width="110" />
|
<el-table-column prop="platform" label="平台" width="110" />
|
||||||
<el-table-column prop="count" label="次数" width="100" sortable />
|
<el-table-column prop="count" label="次数" width="100" sortable />
|
||||||
<el-table-column prop="affectedUsers" label="影响用户" width="110" sortable />
|
<el-table-column prop="isResolved" label="状态" width="90"><template #default="{ row }"><el-tag size="small" :type="row.isResolved ? 'success' : 'danger'">{{ row.isResolved ? '已解决' : '未解决' }}</el-tag></template></el-table-column>
|
||||||
<el-table-column prop="lastSeenAt" label="最后出现" width="170">
|
<el-table-column prop="lastSeenAt" label="最后出现" width="170">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="time-text">{{ formatTime(row.lastSeenAt) }}</span>
|
<span class="time-text">{{ formatTime(row.lastSeenAt) }}</span>
|
||||||
|
|||||||
@ -46,7 +46,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="count" label="次数" width="100" sortable />
|
<el-table-column prop="count" label="次数" width="100" sortable />
|
||||||
<el-table-column prop="affectedUsers" label="影响用户" width="110" sortable />
|
<el-table-column prop="isResolved" label="状态" width="90"><template #default="{ row }"><el-tag size="small" :type="row.isResolved ? 'success' : 'danger'">{{ row.isResolved ? '已解决' : '未解决' }}</el-tag></template></el-table-column>
|
||||||
<el-table-column prop="lastSeenAt" label="最后出现" width="170">
|
<el-table-column prop="lastSeenAt" label="最后出现" width="170">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="time-text">{{ formatTime(row.lastSeenAt) }}</span>
|
<span class="time-text">{{ formatTime(row.lastSeenAt) }}</span>
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户