add log store and types
This commit is contained in:
@@ -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<OperationLogRecord[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
page: 1,
|
||||||
|
per_page: 20,
|
||||||
|
total: 0,
|
||||||
|
})
|
||||||
|
const filters = reactive<OperationLogFilters>({
|
||||||
|
user_id: undefined,
|
||||||
|
action: undefined,
|
||||||
|
target_type: undefined,
|
||||||
|
created_at_range: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const users = ref<UserLookup[]>([])
|
||||||
|
const userMap = computed(
|
||||||
|
() => new Map(users.value.map((u) => [u.id, u.username])),
|
||||||
|
)
|
||||||
|
|
||||||
|
async function loadLookups() {
|
||||||
|
try {
|
||||||
|
const data = await api.get<PaginatedData<UserLookup>>('/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<PaginatedData<OperationLogRecord>>(
|
||||||
|
'/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,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -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<RequestLogRecord[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
page: 1,
|
||||||
|
per_page: 20,
|
||||||
|
total: 0,
|
||||||
|
})
|
||||||
|
const filters = reactive<RequestLogFilters>({
|
||||||
|
user_id: undefined,
|
||||||
|
method: undefined,
|
||||||
|
path: '',
|
||||||
|
status_code: undefined,
|
||||||
|
created_at_range: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const users = ref<UserLookup[]>([])
|
||||||
|
const userMap = computed(
|
||||||
|
() => new Map(users.value.map((u) => [u.id, u.username])),
|
||||||
|
)
|
||||||
|
|
||||||
|
async function loadLookups() {
|
||||||
|
try {
|
||||||
|
const data = await api.get<PaginatedData<UserLookup>>('/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<PaginatedData<RequestLogRecord>>(
|
||||||
|
'/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,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -253,6 +253,57 @@ export interface DashboardBreakdownParams {
|
|||||||
data_type?: string
|
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<string, unknown> | 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<string, unknown> | 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 {
|
export class ApiError extends Error {
|
||||||
code: number
|
code: number
|
||||||
|
|||||||
Reference in New Issue
Block a user