feat(audit): 审计日志保留期清理(后端自动清理 + 审计页配置) (#83)

审计日志新增可配置保留期:AuditLogRepository.DeleteBefore + AuditService 保留期监控(每 6h 读取 audit_retention_days,0/缺省=永久保留);审计页新增管理员保留天数配置控件。后端 go test、前端 tsc+vite 通过。
This commit is contained in:
Wu Qing
2026-05-27 08:37:34 +08:00
committed by GitHub
parent a0d1e66199
commit f807ce10e6
7 changed files with 209 additions and 1 deletions

View File

@@ -1,7 +1,10 @@
import { Button, DatePicker, Input, Message, PageHeader, Select, Space, Table, Tag, Typography } from '@arco-design/web-react'
import { Button, DatePicker, Input, InputNumber, Message, PageHeader, Select, Space, Table, Tag, Typography } from '@arco-design/web-react'
import type { ColumnProps } from '@arco-design/web-react/es/Table'
import { useCallback, useEffect, useState } from 'react'
import { exportAuditLogs, listAuditLogs } from '../../services/audit'
import { fetchSettings, updateSettings } from '../../services/system'
import { useAuthStore } from '../../stores/auth'
import { isAdmin } from '../../utils/permissions'
import type { AuditLog } from '../../types/audit'
import { formatDateTime } from '../../utils/format'
import { resolveErrorMessage } from '../../utils/error'
@@ -112,6 +115,9 @@ export function AuditLogsPage() {
const [dateRange, setDateRange] = useState<string[] | null>(null)
const [page, setPage] = useState(1)
const [exporting, setExporting] = useState(false)
const admin = isAdmin(useAuthStore((state) => state.user))
const [retentionDays, setRetentionDays] = useState(0)
const [savingRetention, setSavingRetention] = useState(false)
const fetchData = useCallback(async (currentPage: number) => {
setLoading(true)
@@ -139,6 +145,31 @@ export function AuditLogsPage() {
void fetchData(page)
}, [page, fetchData])
// 管理员加载当前审计日志保留期设置。
useEffect(() => {
if (!admin) return
void (async () => {
try {
const settings = await fetchSettings()
setRetentionDays(Number(settings.audit_retention_days ?? 0) || 0)
} catch {
/* 读取失败时保持默认 0永久保留不打断主流程 */
}
})()
}, [admin])
async function handleSaveRetention() {
setSavingRetention(true)
try {
await updateSettings({ audit_retention_days: String(retentionDays) })
Message.success(retentionDays > 0 ? `已设置:保留最近 ${retentionDays}` : '已设置:永久保留')
} catch (e) {
Message.error(resolveErrorMessage(e, '保存保留期失败'))
} finally {
setSavingRetention(false)
}
}
async function handleExport() {
setExporting(true)
try {
@@ -171,6 +202,27 @@ export function AuditLogsPage() {
style={{ paddingBottom: 0 }}
title="审计日志"
subTitle="记录系统中所有关键操作,保障数据操作链可溯源。支持高级筛选与 CSV 导出(最多 10000 行)。"
extra={
admin ? (
<Space>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
</Typography.Text>
<InputNumber
style={{ width: 150 }}
min={0}
max={3650}
value={retentionDays}
onChange={(value) => setRetentionDays(Number(value) || 0)}
suffix="天"
placeholder="0=永久"
/>
<Button type="primary" size="small" loading={savingRetention} onClick={() => void handleSaveRetention()}>
</Button>
</Space>
) : undefined
}
/>
{error ? <Typography.Text type="error">{error}</Typography.Text> : null}
<Space wrap>