155 lines
4.4 KiB
TypeScript

/**
* 数据完整性定期检查 Edge Function
*
* 功能:
* 1. 定期运行数据一致性检查
* 2. 检测错误率是否超过阈值(万分之一)
* 3. 发送告警通知
* 4. 生成检查报告
*
* 触发方式:
* - 定时任务(建议每小时执行一次)
* - 手动调用
*/
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
interface CheckResult {
check_type: string;
total_records: number;
inconsistent_records: number;
error_rate: number;
status: 'PASS' | 'FAIL';
}
interface IntegrityReport {
timestamp: string;
checks: CheckResult[];
overall_error_rate: number;
overall_status: 'PASS' | 'FAIL';
target_threshold: number; // 万分比阈值
alerts: string[];
}
serve(async (req) => {
// 处理 CORS 预检请求
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders });
}
try {
// 创建 Supabase 客户端
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const supabase = createClient(supabaseUrl, supabaseServiceKey);
console.log('开始数据完整性检查...');
// 运行数据完整性检查
const { data: checkResults, error: checkError } = await supabase.rpc(
'run_data_integrity_check'
);
if (checkError) {
throw new Error(`数据检查失败: ${checkError.message}`);
}
// 构建报告
const report: IntegrityReport = {
timestamp: new Date().toISOString(),
checks: checkResults || [],
overall_error_rate: 0,
overall_status: 'PASS',
target_threshold: 1, // 万分之一
alerts: [],
};
// 计算总体错误率
let totalRecords = 0;
let totalInconsistent = 0;
for (const check of report.checks) {
totalRecords += check.total_records;
totalInconsistent += check.inconsistent_records;
// 检查单项是否超标
if (check.error_rate > report.target_threshold) {
report.alerts.push(
`⚠️ ${check.check_type} 错误率 ${check.error_rate.toFixed(2)}/万 超过阈值 ${report.target_threshold}/万`
);
}
}
report.overall_error_rate = totalRecords > 0
? (totalInconsistent / totalRecords) * 10000
: 0;
report.overall_status = report.overall_error_rate <= report.target_threshold
? 'PASS'
: 'FAIL';
// 如果总体错误率超标,添加告警
if (report.overall_status === 'FAIL') {
report.alerts.unshift(
`🚨 系统错误率 ${report.overall_error_rate.toFixed(2)}/万 超过目标阈值 ${report.target_threshold}/万`
);
}
// 记录检查结果到审计日志
await supabase.from('data_audit_logs').insert({
table_name: 'system_integrity_check',
record_id: '00000000-0000-0000-0000-000000000000', // 系统检查使用固定ID
operation: 'INSERT',
new_values: report,
validation_passed: report.overall_status === 'PASS',
error_message: report.overall_status === 'FAIL'
? report.alerts.join('; ')
: null,
});
// 如果有告警,可以发送通知(这里预留接口)
if (report.alerts.length > 0) {
console.warn('数据完整性告警:', report.alerts);
// TODO: 集成邮件/短信/钉钉等告警通知
// await sendAlertNotification(report);
}
// 返回报告
return new Response(
JSON.stringify({
success: true,
report,
message: report.overall_status === 'PASS'
? '✓ 数据完整性检查通过'
: '✗ 数据完整性检查发现异常',
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: report.overall_status === 'PASS' ? 200 : 500,
}
);
} catch (error) {
console.error('数据完整性检查异常:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return new Response(
JSON.stringify({
success: false,
error: errorMessage,
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 500,
}
);
}
});