diff --git a/frontend/src/stores/operation-log.ts b/frontend/src/stores/operation-log.ts new file mode 100644 index 0000000..8098c89 --- /dev/null +++ b/frontend/src/stores/operation-log.ts @@ -0,0 +1,93 @@ +import { api } from '@/utils/request' +import type { + PaginatedData, + OperationLogRecord, + OperationLogFilters, +} from '@/types/api' + +export type { OperationLogRecord } + +interface UserLookup { + id: number + username: string +} + +export const useOperationLogStore = defineStore('operationLog', () => { + const logs = ref([]) + const loading = ref(false) + const pagination = reactive({ + page: 1, + per_page: 20, + total: 0, + }) + const filters = reactive({ + user_id: undefined, + action: undefined, + target_type: undefined, + created_at_range: null, + }) + + const users = ref([]) + const userMap = computed( + () => new Map(users.value.map((u) => [u.id, u.username])), + ) + + async function loadLookups() { + try { + const data = await api.get>('/api/v1/users', { + per_page: 200, + }) + users.value = data.items + } catch (err: unknown) { + console.warn('加载用户列表失败', err) + } + } + + async function fetchLogs() { + loading.value = true + try { + const data = await api.get>( + '/api/v1/logs/operations', + { + page: pagination.page, + per_page: pagination.per_page, + user_id: filters.user_id || undefined, + action: filters.action || undefined, + target_type: filters.target_type || undefined, + created_at_from: filters.created_at_range?.[0] || undefined, + created_at_to: filters.created_at_range?.[1] || undefined, + }, + ) + logs.value = data.items + pagination.total = data.total + pagination.page = data.page + } catch (err: unknown) { + logs.value = [] + pagination.total = 0 + const msg = err instanceof Error ? err.message : '获取操作日志列表失败' + message.error(msg) + } finally { + loading.value = false + } + } + + function resetFilters() { + filters.user_id = undefined + filters.action = undefined + filters.target_type = undefined + filters.created_at_range = null + pagination.page = 1 + } + + return { + logs, + loading, + pagination, + filters, + users, + userMap, + loadLookups, + fetchLogs, + resetFilters, + } +}) diff --git a/frontend/src/stores/request-log.ts b/frontend/src/stores/request-log.ts new file mode 100644 index 0000000..3a62b2b --- /dev/null +++ b/frontend/src/stores/request-log.ts @@ -0,0 +1,96 @@ +import { api } from '@/utils/request' +import type { + PaginatedData, + RequestLogRecord, + RequestLogFilters, +} from '@/types/api' + +export type { RequestLogRecord } + +interface UserLookup { + id: number + username: string +} + +export const useRequestLogStore = defineStore('requestLog', () => { + const logs = ref([]) + const loading = ref(false) + const pagination = reactive({ + page: 1, + per_page: 20, + total: 0, + }) + const filters = reactive({ + user_id: undefined, + method: undefined, + path: '', + status_code: undefined, + created_at_range: null, + }) + + const users = ref([]) + const userMap = computed( + () => new Map(users.value.map((u) => [u.id, u.username])), + ) + + async function loadLookups() { + try { + const data = await api.get>('/api/v1/users', { + per_page: 200, + }) + users.value = data.items + } catch (err: unknown) { + console.warn('加载用户列表失败', err) + } + } + + async function fetchLogs() { + loading.value = true + try { + const data = await api.get>( + '/api/v1/logs/requests', + { + page: pagination.page, + per_page: pagination.per_page, + user_id: filters.user_id || undefined, + method: filters.method || undefined, + path: filters.path || undefined, + status_code: filters.status_code || undefined, + created_at_from: filters.created_at_range?.[0] || undefined, + created_at_to: filters.created_at_range?.[1] || undefined, + }, + ) + logs.value = data.items + pagination.total = data.total + pagination.page = data.page + } catch (err: unknown) { + logs.value = [] + pagination.total = 0 + const msg = err instanceof Error ? err.message : '获取请求日志列表失败' + message.error(msg) + } finally { + loading.value = false + } + } + + function resetFilters() { + filters.user_id = undefined + filters.method = undefined + filters.path = '' + filters.status_code = undefined + filters.created_at_range = null + pagination.page = 1 + } + + return { + logs, + loading, + pagination, + filters, + users, + userMap, + loadLookups, + fetchLogs, + resetFilters, + } +}) diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index 13cfd97..b52fab4 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -253,6 +253,57 @@ export interface DashboardBreakdownParams { data_type?: string } +/** ─── 请求日志 ─── */ + +export interface RequestLogRecord { + id: number + user_id: number | null + method: string + path: string + status_code: number + ip: string + response_code: number + duration_ms: number + created_at: string +} + +export interface RequestLogDetail extends RequestLogRecord { + user_agent: string | null + request_body: Record | null +} + +export interface RequestLogFilters { + user_id: number | undefined + method: string | undefined + path: string + status_code: number | undefined + created_at_range: [string, string] | null +} + +/** ─── 操作日志 ─── */ + +export interface OperationLogRecord { + id: number + user_id: number | null + action: string + target_type: string + target_id: number + description: string + ip: string + created_at: string +} + +export interface OperationLogDetail extends OperationLogRecord { + detail: Record | null +} + +export interface OperationLogFilters { + user_id: number | undefined + action: string | undefined + target_type: string | undefined + created_at_range: [string, string] | null +} + /** 业务异常 */ export class ApiError extends Error { code: number