"use client" import { useState, useEffect } from "react" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Input } from "@/components/ui/input" import { Switch } from "@/components/ui/switch" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Zap, Plus, Search, RefreshCw, Play, CheckCircle2, AlertTriangle, Settings, Trash2, Copy, Eye, Loader2, Filter, Shield, Sparkles, FileText, Database, } from "lucide-react" // 清洗规则接口 interface CleaningRule { id: string name: string description: string type: 'format' | 'dedup' | 'validate' | 'enrich' | 'mask' targetField: string config: { pattern?: string replacement?: string validation?: string enrichSource?: string maskType?: string } priority: number enabled: boolean createdAt: string lastRunAt?: string processedCount?: number } // 规则类型配置 const RULE_TYPES = { format: { label: '格式化', color: 'bg-blue-100 text-blue-700', icon: FileText, desc: '统一数据格式' }, dedup: { label: '去重', color: 'bg-green-100 text-green-700', icon: Copy, desc: '删除重复数据' }, validate: { label: '验证', color: 'bg-yellow-100 text-yellow-700', icon: Shield, desc: '校验数据有效性' }, enrich: { label: '补全', color: 'bg-purple-100 text-purple-700', icon: Sparkles, desc: '补充缺失字段' }, mask: { label: '脱敏', color: 'bg-red-100 text-red-700', icon: Eye, desc: '敏感数据脱敏' }, } export default function CleaningRulesPage() { const [rules, setRules] = useState([]) const [loading, setLoading] = useState(true) const [executing, setExecuting] = useState(null) const [showAddDialog, setShowAddDialog] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [stats, setStats] = useState({ totalRules: 0, enabledRules: 0, totalProcessed: 0 }) // 新规则表单 const [newRule, setNewRule] = useState({ name: '', description: '', type: 'format' as const, targetField: 'phone', pattern: '', replacement: '', validation: '' }) // 加载规则 useEffect(() => { loadRules() }, []) const loadRules = async () => { setLoading(true) try { const [rulesRes, statsRes] = await Promise.all([ fetch('/api/cleaning-rules'), fetch('/api/cleaning-rules?action=stats') ]) const rulesData = await rulesRes.json() const statsData = await statsRes.json() if (rulesData.success) setRules(rulesData.rules) if (statsData.success) setStats(statsData.stats) } catch (error) { console.error('加载清洗规则失败:', error) } finally { setLoading(false) } } // 执行单个规则 const executeRule = async (ruleId: string) => { setExecuting(ruleId) try { const res = await fetch('/api/cleaning-rules', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'execute', ruleId, limit: 1000 }) }) const data = await res.json() if (data.success) { alert(`执行完成: 处理 ${data.result.processed} 条,清洗 ${data.result.cleaned} 条`) loadRules() } } catch (error) { console.error('执行失败:', error) } finally { setExecuting(null) } } // 执行所有规则 const executeAllRules = async () => { setExecuting('all') try { const res = await fetch('/api/cleaning-rules', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'execute_all', limit: 100 }) }) const data = await res.json() if (data.success) { alert(`批量执行完成: 执行了 ${data.executed} 条规则`) loadRules() } } catch (error) { console.error('批量执行失败:', error) } finally { setExecuting(null) } } // 切换规则状态 const toggleRule = async (ruleId: string) => { try { await fetch('/api/cleaning-rules', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'toggle', ruleId }) }) setRules(rules.map(r => r.id === ruleId ? { ...r, enabled: !r.enabled } : r)) } catch (error) { console.error('切换失败:', error) } } // 添加规则 const handleAddRule = async () => { try { const res = await fetch('/api/cleaning-rules', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', rule: { name: newRule.name, description: newRule.description, type: newRule.type, targetField: newRule.targetField, config: { pattern: newRule.pattern, replacement: newRule.replacement, validation: newRule.validation } } }) }) const data = await res.json() if (data.success) { setShowAddDialog(false) loadRules() } } catch (error) { console.error('添加失败:', error) } } const formatNumber = (num: number): string => { if (num >= 1000000000) return `${(num / 1000000000).toFixed(2)}B` if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M` if (num >= 1000) return `${(num / 1000).toFixed(0)}K` return num.toLocaleString() } // 筛选规则 const filteredRules = rules.filter(rule => { if (searchQuery) { const query = searchQuery.toLowerCase() return rule.name.toLowerCase().includes(query) || rule.description.toLowerCase().includes(query) || rule.targetField.toLowerCase().includes(query) } return true }) return (
{/* 顶部标题 */}

清洗规则

配置数据清洗规则,实现数据格式化、去重、验证、补全和脱敏

{/* 统计卡片 */}

规则总数

{stats.totalRules}

已启用

{stats.enabledRules}

已处理数据

{formatNumber(stats.totalProcessed)}

规则类型

5种

{/* 搜索 */}
setSearchQuery(e.target.value)} className="pl-9 bg-white" />
{/* 规则列表 */} {loading ? (
加载清洗规则...
) : (
{filteredRules.map((rule, index) => { const typeConfig = RULE_TYPES[rule.type] const TypeIcon = typeConfig.icon return (
{index + 1}

{rule.name}

{typeConfig.label} {!rule.enabled && ( 已禁用 )}

{rule.description}

目标字段: {rule.targetField} {rule.lastRunAt && ( 上次执行: {rule.lastRunAt} )} {rule.processedCount !== undefined && ( 已处理: {formatNumber(rule.processedCount)} 条 )}
toggleRule(rule.id)} />
) })}
)} {/* 添加规则弹窗 */} 添加清洗规则 创建新的数据清洗规则,应用于数据接入过程
setNewRule({ ...newRule, name: e.target.value })} />