feat: M1-04 content safety — reports + violations management tabs
All checks were successful
Deploy Admin Frontend / build-and-deploy (push) Successful in 10s

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
WangDL 2026-05-24 10:52:39 +08:00
parent bf79178546
commit d71bb5f4d8

View File

@ -17,6 +17,8 @@ function CSPage() {
const { data: words } = useQuery({ queryKey: ['safety', 'words'], queryFn: (): Promise<any> => api.get('/admin-api/content-safety/words') })
const { data: checks } = useQuery({ queryKey: ['safety', 'checks'], queryFn: (): Promise<any> => api.get('/admin-api/content-safety/checks') })
const { data: reports } = useQuery({ queryKey: ['safety', 'reports'], queryFn: (): Promise<any> => api.get('/admin-api/content-safety/reports') })
const { data: violations } = useQuery({ queryKey: ['safety', 'violations'], queryFn: (): Promise<any> => api.get('/admin-api/content-safety/violations') })
const addWord = async () => {
await api.post('/admin-api/content-safety/words', { word: newWord, category, riskLevel: risk })
@ -29,6 +31,24 @@ function CSPage() {
onOk: async () => { await api.delete(`/admin-api/content-safety/words/${id}`); qc.invalidateQueries({ queryKey: ['safety'] }) },
})
const handleReport = (id: string, action: string) => modal.confirm({
title: action === 'confirmed' ? '确认违规' : '驳回举报',
content: action === 'confirmed' ? '确认后将创建违规记录' : '驳回后将关闭此举报',
onOk: async () => {
await api.post(`/admin-api/content-safety/reports/${id}/handle`, { action, note: `Admin ${action}` })
message.success('已处理'); qc.invalidateQueries({ queryKey: ['safety'] })
},
})
const applyPenalty = (id: string, penalty: string) => modal.confirm({
title: '应用处罚',
content: `确认对违规记录应用「${penalty}」处罚?`,
onOk: async () => {
await api.post(`/admin-api/content-safety/violations/${id}/penalty`, { penalty })
message.success('处罚已应用'); qc.invalidateQueries({ queryKey: ['safety'] })
},
})
const wordCols = [
{ title: '词汇', dataIndex: 'word', width: 150 },
{ title: '分类', dataIndex: 'category', width: 100 },
@ -46,6 +66,35 @@ function CSPage() {
{ title: '结果', dataIndex: 'result', width: 80, render: (v: string) => <Tag color={v==='passed'?'green':v==='blocked'?'red':'gold'}>{v}</Tag> },
]
const reportCols = [
{ title: '时间', dataIndex: 'createdAt', width: 130, render: (d: string) => dayjs(d).format('MM-DD HH:mm') },
{ title: '类型', dataIndex: 'targetType', width: 110 },
{ title: '目标ID', dataIndex: 'targetId', width: 120, ellipsis: true },
{ title: '原因', dataIndex: 'reason', ellipsis: true },
{ title: '状态', dataIndex: 'status', width: 80, render: (v: string) => v === 'pending' ? <Tag color="gold"></Tag> : v === 'confirmed' ? <Tag color="red"></Tag> : <Tag></Tag> },
{ title: '操作', width: 160, render: (_: any, r: any) => r.status === 'pending' ? (
<Space size="small">
<Button type="link" size="small" onClick={() => handleReport(r.id, 'confirmed')}></Button>
<Button type="link" size="small" danger onClick={() => handleReport(r.id, 'dismissed')}></Button>
</Space>
) : <Text type="secondary">-</Text> },
]
const violationCols = [
{ title: '时间', dataIndex: 'createdAt', width: 130, render: (d: string) => dayjs(d).format('MM-DD HH:mm') },
{ title: '用户', dataIndex: 'userId', width: 140, ellipsis: true },
{ title: '类型', dataIndex: 'contentType', width: 100 },
{ title: '内容', dataIndex: 'content', ellipsis: true, render: (v: string) => <Text style={{ fontSize: 12 }}>{v?.slice(0, 60)}</Text> },
{ title: '风险', dataIndex: 'riskLevel', width: 70, render: (v: string) => <Tag color={v==='critical'?'red':v==='high'?'orange':'blue'}>{v}</Tag> },
{ title: '处罚', dataIndex: 'penalty', width: 80, render: (v: string) => v === 'none' ? <Tag></Tag> : <Tag color="red">{v}</Tag> },
{ title: '操作', width: 120, render: (_: any, r: any) => r.penalty === 'none' ? (
<Space size="small">
<Button type="link" size="small" onClick={() => applyPenalty(r.id, 'warning')}></Button>
<Button type="link" size="small" danger onClick={() => applyPenalty(r.id, 'restrict')}></Button>
</Space>
) : <Text type="secondary">{r.penalty}</Text> },
]
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
@ -59,6 +108,8 @@ function CSPage() {
<Tabs items={[
{ key: 'words', label: '敏感词库', children: <Table dataSource={words || []} columns={wordCols} rowKey="id" pagination={false} size="small" /> },
{ key: 'checks', label: '审核记录', children: <Table dataSource={checks || []} columns={checkCols} rowKey="id" pagination={{ pageSize: 20 }} size="small" /> },
{ key: 'reports', label: '举报管理', children: <Table dataSource={reports || []} columns={reportCols} rowKey="id" pagination={{ pageSize: 20 }} size="small" /> },
{ key: 'violations', label: '违规记录', children: <Table dataSource={violations || []} columns={violationCols} rowKey="id" pagination={{ pageSize: 20 }} size="small" /> },
]} />
<Modal title="新增敏感词" open={addOpen} onOk={addWord} onCancel={() => setAddOpen(false)} okText="添加">