This commit is contained in:
2026-08-13 11:57:44 +08:00
parent 7beca3f985
commit 835ac0ad12
87 changed files with 3721 additions and 295 deletions
@@ -0,0 +1,44 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { http } from '@/shared/api/http'
import { getPlatformAuditLog, listPlatformAuditLogs } from './platformAudit'
vi.mock('@/shared/api/http', () => ({
http: { get: vi.fn() },
unwrapApiResponse: <T>(response: { data: T }) => response.data,
}))
describe('platform audit api', () => {
beforeEach(() => vi.clearAllMocks())
it('queries the selected customer and preserves long ids as strings', async () => {
vi.mocked(http.get).mockResolvedValue({ data: { records: [{ id: '2082813911786024961', accountId: '8', operatorTenantId: '0', userId: '9', resourceId: '8', eventId: 'evt', eventType: 'PLATFORM_TENANT_UPDATE', resourceType: 'TENANT', description: '编辑客户', username: 'admin', appCode: 'ADMIN', clientType: 'WEB', requestId: null, traceId: null, serviceName: 'iam-service', success: true, errorCode: null, errorMessage: null, occurredAt: '2026-08-13T01:00:00Z', createdAt: '2026-08-13T09:00:00' }], page: 1, size: 20, total: 1, pages: 1 } })
const result = await listPlatformAuditLogs({ accountId: '8', page: 1, size: 20 })
expect(http.get).toHaveBeenCalledWith('/platform/v1/audit/logs', { params: { accountId: '8', page: 1, size: 20 } })
expect(result.records[0].id).toBe('2082813911786024961')
})
it('maps audit filters and omits empty query values', async () => {
vi.mocked(http.get).mockResolvedValue({ data: { records: [], page: 1, size: 20, total: 0, pages: 0 } })
await listPlatformAuditLogs({
accountId: '8', page: 1, size: 20, keyword: 'admin', success: false,
occurredFrom: '2026-08-01T00:00:00.000Z', occurredTo: '2026-08-13T23:59:59.999Z',
eventType: '', resourceType: undefined,
})
expect(http.get).toHaveBeenCalledWith('/platform/v1/audit/logs', { params: {
accountId: '8', page: 1, size: 20, keyword: 'admin', success: false,
occurredFrom: '2026-08-01T00:00:00.000Z', occurredTo: '2026-08-13T23:59:59.999Z',
} })
})
it('loads one audit detail', async () => {
vi.mocked(http.get).mockResolvedValue({ data: { id: '1', accountId: '8', operatorTenantId: '0', userId: '9', resourceId: '8', eventId: 'evt', eventType: 'PLATFORM_TENANT_UPDATE', resourceType: 'TENANT', description: '编辑客户', username: 'admin', appCode: 'ADMIN', clientType: 'WEB', requestId: null, traceId: null, serviceName: 'iam-service', success: true, errorCode: null, errorMessage: null, occurredAt: '2026-08-13T01:00:00Z', createdAt: '2026-08-13T09:00:00', httpMethod: 'PUT', requestUri: '/tenants/8', clientIp: '127.0.0.1', durationMs: 12, beforeData: '{}', afterData: '{"name":"new"}' } })
const result = await getPlatformAuditLog('1')
expect(http.get).toHaveBeenCalledWith('/platform/v1/audit/logs/1')
expect(result.afterData).toBe('{"name":"new"}')
})
})
+106
View File
@@ -0,0 +1,106 @@
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
export interface PlatformAuditLogListItem {
id: string
eventId: string
eventType: string
resourceType: string
resourceId: string | null
description: string | null
accountId: string
operatorTenantId: string
userId: string
username: string | null
appCode: string | null
clientType: string | null
requestId: string | null
traceId: string | null
serviceName: string | null
success: boolean
errorCode: string | null
errorMessage: string | null
occurredAt: string
createdAt: string
}
export interface PlatformAuditLogDetail extends PlatformAuditLogListItem {
httpMethod: string | null
requestUri: string | null
clientIp: string | null
durationMs: number | null
beforeData: string | null
afterData: string | null
}
export interface PlatformAuditLogPage {
records: PlatformAuditLogListItem[]
page: number
size: number
total: number
pages: number
}
export interface PlatformAuditLogQuery {
accountId: string
page?: number
size?: number
operatorUserId?: string
eventType?: string
resourceType?: string
resourceId?: string
success?: boolean
occurredFrom?: string
occurredTo?: string
keyword?: string
}
type RawId = string | number | null
type RawListItem = Omit<PlatformAuditLogListItem, 'id' | 'accountId' | 'operatorTenantId' | 'userId' | 'resourceId'> & {
id: RawId
accountId: RawId
operatorTenantId: RawId
userId: RawId
resourceId: RawId
}
type RawDetail = Omit<PlatformAuditLogDetail, keyof PlatformAuditLogListItem> & RawListItem
type RawPage = Omit<PlatformAuditLogPage, 'records' | 'page' | 'size' | 'total' | 'pages'> & {
records?: RawListItem[]
page: number | string
size: number | string
total: number | string
pages?: number | string
}
const basePath = '/platform/v1/audit/logs'
const id = (value: RawId) => value == null ? '' : String(value)
function normalizeListItem(item: RawListItem): PlatformAuditLogListItem {
return {
...item,
id: id(item.id),
accountId: id(item.accountId),
operatorTenantId: id(item.operatorTenantId),
userId: id(item.userId),
resourceId: item.resourceId == null ? null : String(item.resourceId),
}
}
export async function listPlatformAuditLogs(query: PlatformAuditLogQuery) {
const params = Object.fromEntries(Object.entries(query).filter(([, value]) => value !== '' && value !== null && value !== undefined))
const response = await http.get<ApiEnvelope<RawPage>, ApiEnvelope<RawPage>>(basePath, { params })
const data = unwrapApiResponse(response)
const size = Number(data.size || query.size || 20)
const total = Number(data.total || 0)
return {
records: (data.records || []).map(normalizeListItem),
page: Number(data.page || query.page || 1),
size,
total,
pages: Number(data.pages ?? (total > 0 ? Math.ceil(total / size) : 0)),
} satisfies PlatformAuditLogPage
}
export async function getPlatformAuditLog(idValue: string) {
const response = await http.get<ApiEnvelope<RawDetail>, ApiEnvelope<RawDetail>>(`${basePath}/${encodeURIComponent(idValue)}`)
return normalizeListItem(unwrapApiResponse(response)) as PlatformAuditLogDetail
}