powersir преди 1 година
родител
ревизия
f7ce5eb4c8
променени са 6 файла, в които са добавени 419 реда и са изтрити 1 реда
  1. +83
    -0
      src/models/api-log.data.ts
  2. +2
    -0
      src/models/index.ts
  3. +0
    -0
      src/pages/infra/log/api-access-log/index.tsx
  4. +292
    -0
      src/pages/infra/log/api-error-log/index.tsx
  5. +1
    -1
      src/pages/system/role/index.tsx
  6. +41
    -0
      src/request/service/api-log.ts

+ 83
- 0
src/models/api-log.data.ts Целия файл

@@ -0,0 +1,83 @@
export interface ApiAccessLogVO {
id: number
traceId: string
userId: number
userType: number
applicationName: string
requestMethod: string
requestParams: string
requestUrl: string
userIp: string
userAgent: string
beginTime: Date
endTIme: Date
duration: number
resultCode: number
resultMsg: string
createTime: Date
}

export interface ApiAccessLogPageReqVO extends PageParam {
userId?: number
userType?: number
applicationName?: string
requestUrl?: string
beginTime?: Date[]
duration?: number
resultCode?: number
}

export interface ApiAccessLogExportReqVO {
userId?: number
userType?: number
applicationName?: string
requestUrl?: string
beginTime?: Date[]
duration?: number
resultCode?: number
}

export interface ApiErrorLogVO {
id: number
traceId: string
userId: number
userType: number
applicationName: string
requestMethod: string
requestParams: string
requestUrl: string
userIp: string
userAgent: string
exceptionTime: Date
exceptionName: string
exceptionMessage: string
exceptionRootCauseMessage: string
exceptionStackTrace: string
exceptionClassName: string
exceptionFileName: string
exceptionMethodName: string
exceptionLineNumber: number
processUserId: number
processStatus: number
processTime: Date
resultCode: number
createTime: Date
}

export interface ApiErrorLogPageReqVO extends PageParam {
userId?: number
userType?: number
applicationName?: string
requestUrl?: string
exceptionTime?: Date[]
processStatus?: number
}

export interface ApiErrorLogExportReqVO {
userId?: number
userType?: number
applicationName?: string
requestUrl?: string
exceptionTime?: Date[]
processStatus: number
}

+ 2
- 0
src/models/index.ts Целия файл

@@ -11,6 +11,8 @@ export * from './logs.data.ts'
export * from './system-dict.data.ts'
export * from './error-code.data.ts'
export * from './data-source.data.ts'
export * from './redis.data.ts'
export * from './api-log.data.ts'

export interface ResponseDTO<T>{
code: number;


+ 0
- 0
src/pages/infra/log/api-access-log/index.tsx Целия файл


+ 292
- 0
src/pages/infra/log/api-error-log/index.tsx Целия файл

@@ -0,0 +1,292 @@
import React, { useState, useEffect, useRef } from 'react';
import { useSetState } from 'ahooks';
import { Space, Table, Button, Input, FloatButton, Divider, Tag, Card, Tooltip } from 'antd';
import type { TableColumnsType, InputRef } from 'antd';
import type { ColumnType, TableProps } from 'antd/es/table';
import { t } from '@/utils/i18n';
import { DownloadOutlined, SearchOutlined, ExclamationCircleFilled } from '@ant-design/icons';
import { antdUtils } from '@/utils/antd';
import { useRequest } from '@/hooks/use-request';
import { formatDate } from '@/utils/formatTime'
import apiLogService from '@/request/service/api-log';
import {
ApiErrorLogPageReqVO,
ApiErrorLogExportReqVO,
ApiErrorLogVO
} from '@/models';


type DataIndex = keyof ApiErrorLogVO;

const mapOperation = (operation: number) => {
if (operation === 1) {
return (<Tag color="purple">通知</Tag>)
} else if (operation === 2) {
return (<Tag color="purple">新增</Tag>)
} else if (operation === 3) {
return (<Tag color="yellow">修改</Tag>)
} else if (operation === 4) {
return (<Tag color="red">删除</Tag>)
} else if (operation === 5) {
return (<Tag color="blue">导出</Tag>)
} else {
return (<Tag color="red">未知({operation})</Tag>)
}
}

export default () => {
const [dataSource, setDataSource] = useState<ApiErrorLogVO[]>([]);

const { runAsync: getPageData } = useRequest(apiLogService.getApiErrorLogPageApi, { manual: true });
const { runAsync: exportOperateLog } = useRequest(apiLogService.exportApiErrorLogApi, { manual: true });
const { runAsync: updateApiErrorLogStatus } = useRequest(apiLogService.updateApiErrorLogPageApi, { manual: true });

const [searchState, setSearchState] = useSetState<ApiErrorLogPageReqVO>({
pageNo: 1,
pageSize: 10
});
const [total, setTotal] = useState(0)
const searchInput = useRef<InputRef>(null);
const [onSearching, setOnSearching] = useState(false);

const load = async () => {
console.log(searchState)
const [error, { code, msg, data }] = await getPageData(searchState);
setOnSearching(false);
if (error || code !== 0) {
antdUtils.message?.open({ type: 'error', content: msg ?? '操作失败' });
return
}
setTotal(data.total);
setDataSource(data.list);
};

const getColumnSearchProps = (dataIndex: DataIndex, placeholder: string): ColumnType<ApiErrorLogVO> => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters, close }) => (
<div style={{ padding: 8 }} onKeyDown={(e) => e.stopPropagation()}>
<Input.Search
ref={searchInput}
placeholder={placeholder}
value={selectedKeys[0]}
onChange={(e) => {
setSelectedKeys(e.target.value && e.target.value !== '' ? [e.target.value] : [])
}}
onSearch={(value) => {
if (value === '' && clearFilters) {
clearFilters!!()
}
confirm();
}}
onPressEnter={() => confirm()}
allowClear
style={{ marginBottom: 8, display: 'block' }}
enterButton="搜索"
size="middle"
loading={onSearching}
/>
</div>
),
filterIcon: (filtered: boolean) => (
<SearchOutlined style={{ color: filtered ? 'primaryColor' : undefined }} />
),
onFilterDropdownOpenChange: (visible) => {
if (visible) {
setTimeout(() => searchInput.current?.select(), 100);
}
},
});

const handleUpdateApiErrorLogStatus = async function (item: ApiErrorLogVO, status: number) {
antdUtils.modal?.confirm({
title: '温馨提示',
icon: <ExclamationCircleFilled />,
content: `确认标记为已${status === 1 ? '处理' : '忽略'}?`,
okText: '确定',
cancelText: '取消',
onOk() {
return new Promise(async (resolve) => {
const [error, { code, msg }] = await updateApiErrorLogStatus(item.id, status);
if (error || code !== 0) {
antdUtils.message?.open({ type: 'error', content: msg ?? '操作失败' })
} else {
antdUtils.message?.open({ type: 'success', content: '操作成功' })
}
await load();
resolve('')
}).catch(() => antdUtils.message?.open({
type: 'error',
content: '操作失败',
}));
},
onCancel() {
},
});
}

const columns: TableColumnsType<ApiErrorLogVO> = [
{
title: '日志编号',
dataIndex: 'id',
key: 'id',
align: 'center',
fixed: 'left',
width: 100,
},
{
title: '用户编号',
dataIndex: 'userId',
key: 'userId',
align: 'center',
width: 100,
...getColumnSearchProps('userId', "请输入用户编号")
},
{
title: '用户类型',
dataIndex: 'userType',
key: 'userType',
fixed: 'left',
filterSearch: true,
align: 'center',
width: 100,
render: (value: number) => {
if (value === 1) {
return <Tag color="purple">会员</Tag>
} else if (value === 2) {
return <Tag color="blue">管理员</Tag>
} else {
return <Tag color="red">未知</Tag>
}
}
},
{
title: '应用名',
dataIndex: 'applicationName',
key: 'applicationName',
align: 'center',
width: 120,
...getColumnSearchProps('applicationName', "请输入应用名")
},
{
title: '请求方法',
dataIndex: 'requestMethod',
key: 'requestMethod',
align: 'center',
width: 100,
},
{
title: '请求地址',
dataIndex: 'requestUrl',
key: 'requestUrl',
align: 'center',
width: 150,
...getColumnSearchProps('requestUrl', "请输入请求地址")
},
{
title: '异常时间',
dataIndex: 'exceptionTime',
key: 'exceptionTime',
align: 'center',
width: 150,
render: (value: number) => {
return formatDate(new Date(value), "YYYY-mm-dd HH:MM:SS")
}
},
{
title: '异常名',
dataIndex: 'exceptionName',
key: 'exceptionName',
align: 'center',
width: 150,
},
{
title: '处理状态',
dataIndex: 'processStatus',
key: 'processStatus',
align: 'center',
width: 150,
render: (value: number) => {
if (value === 0) {
return <Tag color="red">未处理</Tag>
} else if (value === 1) {
return <Tag color="purple">已处理</Tag>
} else if (value === 2) {
return <Tag color="blue">已忽略</Tag>
} else {
return <Tag color="purple">内置</Tag>
}
}
},
{
title: t("QkOmYwne" /* 操作 */),
key: 'action',
fixed: 'right',
align: 'center',
width: 250,
render: (value: ApiErrorLogVO, record) => (
<Space size="small" split={(<Divider type='vertical' />)}>
<a onClick={() => {

}}> 详情 </a>
{
(value.processStatus === 0 ? (<a onClick={() => {
handleUpdateApiErrorLogStatus(value, 1)
}}> 已处理 </a>) : null)
}
{
(value.processStatus === 0 ? (<a onClick={() => {
handleUpdateApiErrorLogStatus(value, 2)
}}> 已忽略 </a>) : null)
}
</Space>
),
},
];

const exportLogs = async () => {
await exportOperateLog()
}

useEffect(() => {
load();
}, [searchState]);

const onChange: TableProps<ApiErrorLogVO>['onChange'] = (pagination, filters, sorter, extra) => {
const state: ApiErrorLogPageReqVO = {
applicationName: filters.applicationName ? filters.applicationName[0] as string : undefined,
requestUrl: filters.requestUrl ? filters.requestUrl[0] as string : undefined,
userId: filters.userId ? parseInt(filters.userId[0] as string) : undefined,
pageNo: pagination.current,
pageSize: pagination.pageSize
}
setOnSearching(true);
setSearchState(state);
};

return (
<>
<div>
<Card className='mt-[4px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'>
<Table rowKey="id"
scroll={{ x: true }}
columns={columns}
dataSource={dataSource}
onChange={onChange}
className='bg-transparent'
pagination={{
position: ['bottomRight'],
current: searchState.pageNo,
pageSize: searchState.pageSize,
total
}}
/>
</Card>
<FloatButton
type='primary'
tooltip={<div>导出日志</div>}
icon={<DownloadOutlined />}
onClick={exportLogs}
/>
</div>
</>
);
};

+ 1
- 1
src/pages/system/role/index.tsx Целия файл

@@ -84,7 +84,7 @@ export default () => {
align: 'center',
width: 150,
render: (value: number) => {
return (value === 1 ? <Tag color="purple">内置</Tag> : <Tag color="blue">内置</Tag>)
return (value === 1 ? <Tag color="purple">内置</Tag> : <Tag color="blue">自定义</Tag>)
}
},
{


+ 41
- 0
src/request/service/api-log.ts Целия файл

@@ -0,0 +1,41 @@
import request from '@/request';
import {
ApiErrorLogPageReqVO,
ApiErrorLogExportReqVO,
ApiErrorLogVO,
ApiAccessLogPageReqVO,
ApiAccessLogExportReqVO,
ApiAccessLogVO,
PageData
} from '@/models';

const BASE_URL = '/admin-api/infra/api-error-log';
const BASE_ACCESS_URL = '/admin-api/infra/api-access-log'

export default {
// 查询列表API 访问日志
getApiErrorLogPageApi: (params: ApiErrorLogPageReqVO) => {
return request.get<PageData<ApiErrorLogVO>>(`${BASE_URL}/page`, { params })
},

// 更新 API 错误日志的处理状态
updateApiErrorLogPageApi: (id: number, processStatus: number) => {
return request.put(`${BASE_URL}/update-status?id=${id}&processStatus=${processStatus}`)
},

// 导出API 访问日志
exportApiErrorLogApi: (params: ApiErrorLogExportReqVO) => {
return request.get(`${BASE_URL}/export-excel`, { params, responseType: 'blob' })
},


// 查询列表API 访问日志
getApiAccessLogPageApi: (params: ApiAccessLogPageReqVO) => {
return request.get<PageData<ApiAccessLogVO>>(`${BASE_ACCESS_URL}/page`, { params })
},

// 导出API 访问日志
exportApiAccessLogApi: (params: ApiAccessLogExportReqVO) => {
return request.get(`${BASE_ACCESS_URL}/export-excel`, { params, responseType: 'blob' })
}
}

Loading…
Отказ
Запис