新增后台错误报告查看器

增加错误报告列表、详情和状态筛选页面。

支持管理员受控下载诊断包并接入错误报告 Tab 权限。

补充前端 API 类型、客户端请求和详情弹窗样式。
This commit is contained in:
2026-09-01 10:11:43 +08:00
parent 5a4ac48575
commit 4671c5fb52
7 changed files with 308 additions and 0 deletions
+70
View File
@@ -271,6 +271,59 @@ export function listAdminTrackingEventKeys(token: string) {
);
}
export function listAdminErrorReports(
token: string,
query: {
status?: string;
fingerprint?: string;
source?: string;
limit?: number;
} = {},
) {
return request<import('./adminApiTypes').AdminErrorReportListResponse>(
`/admin/api/error-reports${buildErrorReportQueryString(query)}`,
{ token },
);
}
export function getAdminErrorReport(token: string, batchId: string) {
return request<import('./adminApiTypes').AdminErrorReportDetail>(
`/admin/api/error-reports/${encodeURIComponent(batchId)}`,
{ token },
);
}
export function updateAdminErrorReport(
token: string,
batchId: string,
payload: { status: string; note?: string },
) {
return request<import('./adminApiTypes').AdminErrorReportDetail>(
`/admin/api/error-reports/${encodeURIComponent(batchId)}`,
{ method: 'PATCH', token, body: payload },
);
}
export function getAdminErrorReportDownloadUrl(batchId: string) {
return `/admin/api/error-reports/${encodeURIComponent(batchId)}/download`;
}
export async function downloadAdminErrorReport(token: string, batchId: string) {
const response = await fetch(
buildRequestUrl(getAdminErrorReportDownloadUrl(batchId)),
{
headers: { Authorization: `Bearer ${token}` },
},
);
if (!response.ok) {
throw new AdminApiError({
message: `下载失败(${response.status})`,
status: response.status,
});
}
return response.blob();
}
export function getAdminFeatureGateConfig(token: string) {
return request<AdminFeatureGateConfigResponse>('/admin/api/feature-gates', {
token,
@@ -806,6 +859,23 @@ function buildQueryString(query: AdminTrackingEventListQuery) {
return queryString ? `?${queryString}` : '';
}
function buildErrorReportQueryString(query: {
status?: string;
fingerprint?: string;
source?: string;
limit?: number;
}) {
const params = new URLSearchParams();
appendQueryParam(params, 'status', query.status);
appendQueryParam(params, 'fingerprint', query.fingerprint);
appendQueryParam(params, 'source', query.source);
if (typeof query.limit === 'number' && Number.isFinite(query.limit)) {
params.set('limit', String(query.limit));
}
const queryString = params.toString();
return queryString ? `?${queryString}` : '';
}
function buildAdminRechargeOrderListQuery(query: AdminRechargeOrderListQuery) {
const params = new URLSearchParams();
appendQueryParam(params, 'orderId', query.orderId);