feat(黑名单): 新增恶意买家黑名单服务及相关功能
refactor(服务): 重构多个服务类并添加数据库表初始化方法 style(日志): 优化日志输出格式和内容 docs(任务概览): 更新恶意买家黑名单闭环任务状态 fix(ImageRecognitionService): 修复错误处理中的变量名错误 chore: 移除冗余代码并合并相似功能
This commit is contained in:
626
server/src/services/RiskAssessmentService.ts
Normal file
626
server/src/services/RiskAssessmentService.ts
Normal file
@@ -0,0 +1,626 @@
|
||||
import { logger } from '../utils/logger';
|
||||
import BlacklistDatabaseService from './BlacklistDatabaseService';
|
||||
import BlacklistService from './BlacklistService';
|
||||
|
||||
export interface BuyerBehavior {
|
||||
buyer_id: string;
|
||||
platform: string;
|
||||
platform_buyer_id: string;
|
||||
order_count: number;
|
||||
return_rate: number;
|
||||
chargeback_rate: number;
|
||||
complaint_rate: number;
|
||||
refund_rate: number;
|
||||
average_order_value: number;
|
||||
purchase_frequency: number;
|
||||
review_score: number;
|
||||
suspicious_behavior: boolean;
|
||||
abnormal_activity: boolean;
|
||||
location_mismatch: boolean;
|
||||
payment_issues: number;
|
||||
account_age_days: number;
|
||||
device_changes: number;
|
||||
ip_changes: number;
|
||||
velocity_score: number;
|
||||
risk_indicators: string[];
|
||||
last_purchase_date: Date;
|
||||
first_purchase_date: Date;
|
||||
}
|
||||
|
||||
export interface RiskAssessmentResult {
|
||||
buyer_id: string;
|
||||
platform: string;
|
||||
platform_buyer_id: string;
|
||||
risk_score: number;
|
||||
risk_level: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
||||
risk_factors: string[];
|
||||
recommendations: string[];
|
||||
assessment_date: Date;
|
||||
is_blacklisted: boolean;
|
||||
blacklist_reasons?: string[];
|
||||
confidence_score: number;
|
||||
}
|
||||
|
||||
export interface RiskRule {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
condition: string;
|
||||
weight: number;
|
||||
threshold: number;
|
||||
action: 'ALERT' | 'BLOCK' | 'MONITOR';
|
||||
enabled: boolean;
|
||||
created_by: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
}
|
||||
|
||||
export interface RiskStatistics {
|
||||
total_assessments: number;
|
||||
high_risk_count: number;
|
||||
medium_risk_count: number;
|
||||
low_risk_count: number;
|
||||
critical_risk_count: number;
|
||||
blacklist_conversion_rate: number;
|
||||
false_positives: number;
|
||||
false_negatives: number;
|
||||
by_platform: Record<string, number>;
|
||||
by_risk_level: Record<string, number>;
|
||||
average_risk_score: number;
|
||||
risk_trend: {
|
||||
date: string;
|
||||
average_score: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface RiskAssessmentRequest {
|
||||
tenant_id: string;
|
||||
shop_id: string;
|
||||
task_id?: string;
|
||||
trace_id: string;
|
||||
buyer_behavior: BuyerBehavior;
|
||||
include_blacklist_check: boolean;
|
||||
assessment_reason: string;
|
||||
}
|
||||
|
||||
export default class RiskAssessmentService {
|
||||
private static instance: RiskAssessmentService;
|
||||
private blacklistService: BlacklistService;
|
||||
|
||||
static getInstance(): RiskAssessmentService {
|
||||
if (!RiskAssessmentService.instance) {
|
||||
RiskAssessmentService.instance = new RiskAssessmentService();
|
||||
}
|
||||
return RiskAssessmentService.instance;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.blacklistService = BlacklistService.getInstance();
|
||||
}
|
||||
|
||||
async initTables(): Promise<void> {
|
||||
// 初始化风险规则表
|
||||
const hasRiskRulesTable = await db.schema.hasTable('cf_risk_rule');
|
||||
if (!hasRiskRulesTable) {
|
||||
logger.info('[RiskAssessmentService] Creating cf_risk_rule table...');
|
||||
await db.schema.createTable('cf_risk_rule', (table) => {
|
||||
table.string('id', 36).primary();
|
||||
table.string('name', 255).notNullable();
|
||||
table.text('description').notNullable();
|
||||
table.text('condition').notNullable();
|
||||
table.decimal('weight', 5, 2).notNullable();
|
||||
table.decimal('threshold', 5, 2).notNullable();
|
||||
table.enum('action', ['ALERT', 'BLOCK', 'MONITOR']).notNullable();
|
||||
table.boolean('enabled').notNullable().defaultTo(true);
|
||||
table.string('created_by', 64).notNullable();
|
||||
table.datetime('created_at').notNullable().defaultTo(db.fn.now());
|
||||
table.datetime('updated_at').notNullable().defaultTo(db.fn.now());
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化风险评估记录表
|
||||
const hasRiskAssessmentsTable = await db.schema.hasTable('cf_risk_assessment');
|
||||
if (!hasRiskAssessmentsTable) {
|
||||
logger.info('[RiskAssessmentService] Creating cf_risk_assessment table...');
|
||||
await db.schema.createTable('cf_risk_assessment', (table) => {
|
||||
table.string('id', 36).primary();
|
||||
table.string('tenant_id', 64).notNullable().index();
|
||||
table.string('shop_id', 64).notNullable();
|
||||
table.string('task_id', 36);
|
||||
table.string('trace_id', 64).notNullable();
|
||||
table.string('buyer_id', 64).notNullable();
|
||||
table.string('platform', 64).notNullable().index();
|
||||
table.string('platform_buyer_id', 64).notNullable().index();
|
||||
table.decimal('risk_score', 5, 2).notNullable().index();
|
||||
table.enum('risk_level', ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']).notNullable().index();
|
||||
table.json('risk_factors').notNullable();
|
||||
table.json('recommendations').notNullable();
|
||||
table.boolean('is_blacklisted').notNullable();
|
||||
table.json('blacklist_reasons');
|
||||
table.decimal('confidence_score', 5, 2).notNullable();
|
||||
table.text('assessment_reason').notNullable();
|
||||
table.json('buyer_behavior').notNullable();
|
||||
table.datetime('assessment_date').notNullable().defaultTo(db.fn.now());
|
||||
table.datetime('created_at').notNullable().defaultTo(db.fn.now());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async assessRisk(request: RiskAssessmentRequest): Promise<RiskAssessmentResult> {
|
||||
try {
|
||||
logger.info(`[RiskAssessmentService] Assessing risk for buyer: ${request.buyer_behavior.platform_buyer_id}, platform=${request.buyer_behavior.platform}, traceId=${request.trace_id}`);
|
||||
|
||||
// 计算基础风险分数
|
||||
let riskScore = this.calculateBaseRiskScore(request.buyer_behavior);
|
||||
|
||||
// 应用风险规则
|
||||
const { adjustedScore, riskFactors } = await this.applyRiskRules(request.buyer_behavior, riskScore);
|
||||
riskScore = adjustedScore;
|
||||
|
||||
// 检查黑名单状态
|
||||
let isBlacklisted = false;
|
||||
let blacklistReasons: string[] = [];
|
||||
|
||||
if (request.include_blacklist_check) {
|
||||
const blacklistCheck = await this.blacklistService.checkBlacklist(
|
||||
request.buyer_behavior.platform,
|
||||
request.buyer_behavior.platform_buyer_id,
|
||||
request.tenant_id
|
||||
);
|
||||
isBlacklisted = blacklistCheck.is_blacklisted;
|
||||
blacklistReasons = blacklistCheck.reasons || [];
|
||||
|
||||
// 如果在黑名单中,提高风险分数
|
||||
if (isBlacklisted) {
|
||||
riskScore = Math.min(100, riskScore + 30);
|
||||
riskFactors.push('Buyer is in blacklist');
|
||||
}
|
||||
}
|
||||
|
||||
// 确定风险等级
|
||||
const riskLevel = this.calculateRiskLevel(riskScore);
|
||||
|
||||
// 生成建议
|
||||
const recommendations = this.generateRecommendations(riskLevel, riskFactors, isBlacklisted);
|
||||
|
||||
// 计算置信度分数
|
||||
const confidenceScore = this.calculateConfidenceScore(request.buyer_behavior, riskFactors.length);
|
||||
|
||||
// 保存评估记录
|
||||
await this.saveAssessmentRecord({
|
||||
...request,
|
||||
risk_score: riskScore,
|
||||
risk_level: riskLevel,
|
||||
risk_factors: riskFactors,
|
||||
recommendations: recommendations,
|
||||
is_blacklisted: isBlacklisted,
|
||||
blacklist_reasons: blacklistReasons,
|
||||
confidence_score: confidenceScore
|
||||
});
|
||||
|
||||
const result: RiskAssessmentResult = {
|
||||
buyer_id: request.buyer_behavior.buyer_id,
|
||||
platform: request.buyer_behavior.platform,
|
||||
platform_buyer_id: request.buyer_behavior.platform_buyer_id,
|
||||
risk_score: riskScore,
|
||||
risk_level: riskLevel,
|
||||
risk_factors: riskFactors,
|
||||
recommendations: recommendations,
|
||||
assessment_date: new Date(),
|
||||
is_blacklisted: isBlacklisted,
|
||||
blacklist_reasons: blacklistReasons,
|
||||
confidence_score: confidenceScore
|
||||
};
|
||||
|
||||
logger.info(`[RiskAssessmentService] Risk assessment completed: score=${riskScore}, level=${riskLevel}, confidence=${confidenceScore}, traceId=${request.trace_id}`);
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
logger.error(`[RiskAssessmentService] Failed to assess risk: ${error.message}, traceId=${request.trace_id}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private calculateBaseRiskScore(behavior: BuyerBehavior): number {
|
||||
let score = 0;
|
||||
|
||||
// 退货率 (0-20分)
|
||||
score += Math.min(20, behavior.return_rate * 20);
|
||||
|
||||
// 退款率 (0-20分)
|
||||
score += Math.min(20, behavior.refund_rate * 20);
|
||||
|
||||
// 拒付率 (0-25分)
|
||||
score += Math.min(25, behavior.chargeback_rate * 25);
|
||||
|
||||
// 投诉率 (0-15分)
|
||||
score += Math.min(15, behavior.complaint_rate * 15);
|
||||
|
||||
// 可疑行为 (0-10分)
|
||||
if (behavior.suspicious_behavior) score += 10;
|
||||
|
||||
// 异常活动 (0-10分)
|
||||
if (behavior.abnormal_activity) score += 10;
|
||||
|
||||
// 位置不匹配 (0-8分)
|
||||
if (behavior.location_mismatch) score += 8;
|
||||
|
||||
// 支付问题 (0-5分 per issue)
|
||||
score += Math.min(15, behavior.payment_issues * 5);
|
||||
|
||||
// 设备变更 (0-5分 per change)
|
||||
score += Math.min(10, behavior.device_changes * 2);
|
||||
|
||||
// IP变更 (0-5分 per change)
|
||||
score += Math.min(10, behavior.ip_changes * 2);
|
||||
|
||||
// 速度评分 (0-10分)
|
||||
score += Math.min(10, behavior.velocity_score);
|
||||
|
||||
// 账户年龄 (新账户风险更高)
|
||||
if (behavior.account_age_days < 30) score += 10;
|
||||
else if (behavior.account_age_days < 90) score += 5;
|
||||
|
||||
return Math.min(100, score);
|
||||
}
|
||||
|
||||
private async applyRiskRules(behavior: BuyerBehavior, baseScore: number): Promise<{ adjustedScore: number; riskFactors: string[] }> {
|
||||
const riskFactors: string[] = [];
|
||||
let adjustedScore = baseScore;
|
||||
|
||||
// 获取启用的风险规则
|
||||
const rules = await db('cf_risk_rule').where({ enabled: true });
|
||||
|
||||
for (const rule of rules) {
|
||||
try {
|
||||
// 简单的规则评估逻辑
|
||||
if (this.evaluateRule(rule, behavior)) {
|
||||
adjustedScore = Math.min(100, adjustedScore + rule.weight);
|
||||
riskFactors.push(rule.name);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`[RiskAssessmentService] Failed to evaluate rule ${rule.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return { adjustedScore, riskFactors };
|
||||
}
|
||||
|
||||
private evaluateRule(rule: any, behavior: BuyerBehavior): boolean {
|
||||
// 简单的规则评估实现
|
||||
// 实际应用中可能需要更复杂的规则引擎
|
||||
switch (rule.name) {
|
||||
case 'High Return Rate':
|
||||
return behavior.return_rate > 0.3;
|
||||
case 'Chargeback History':
|
||||
return behavior.chargeback_rate > 0;
|
||||
case 'Suspicious Activity':
|
||||
return behavior.suspicious_behavior;
|
||||
case 'New Account':
|
||||
return behavior.account_age_days < 30;
|
||||
case 'Multiple Device Changes':
|
||||
return behavior.device_changes > 3;
|
||||
case 'Multiple IP Changes':
|
||||
return behavior.ip_changes > 3;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private calculateRiskLevel(score: number): 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' {
|
||||
if (score >= 80) return 'CRITICAL';
|
||||
if (score >= 60) return 'HIGH';
|
||||
if (score >= 30) return 'MEDIUM';
|
||||
return 'LOW';
|
||||
}
|
||||
|
||||
private generateRecommendations(riskLevel: string, riskFactors: string[], isBlacklisted: boolean): string[] {
|
||||
const recommendations: string[] = [];
|
||||
|
||||
switch (riskLevel) {
|
||||
case 'CRITICAL':
|
||||
recommendations.push('Block buyer immediately');
|
||||
recommendations.push('Add to blacklist');
|
||||
recommendations.push('Review all past transactions');
|
||||
recommendations.push('Consider legal action if fraud is suspected');
|
||||
break;
|
||||
case 'HIGH':
|
||||
recommendations.push('Place order on hold for manual review');
|
||||
recommendations.push('Request additional verification');
|
||||
recommendations.push('Limit order amount');
|
||||
recommendations.push('Monitor future transactions closely');
|
||||
break;
|
||||
case 'MEDIUM':
|
||||
recommendations.push('Monitor transaction closely');
|
||||
recommendations.push('Set lower order limits');
|
||||
recommendations.push('Require signature for delivery');
|
||||
break;
|
||||
case 'LOW':
|
||||
recommendations.push('Process order normally');
|
||||
recommendations.push('Continue monitoring');
|
||||
break;
|
||||
}
|
||||
|
||||
if (isBlacklisted) {
|
||||
recommendations.push('Review blacklist entry details');
|
||||
recommendations.push('Consider permanent block if repeat offender');
|
||||
}
|
||||
|
||||
if (riskFactors.includes('High Return Rate')) {
|
||||
recommendations.push('Consider restocking fees');
|
||||
recommendations.push('Review return policy compliance');
|
||||
}
|
||||
|
||||
if (riskFactors.includes('Chargeback History')) {
|
||||
recommendations.push('Use secure payment methods');
|
||||
recommendations.push('Keep detailed transaction records');
|
||||
}
|
||||
|
||||
return recommendations;
|
||||
}
|
||||
|
||||
private calculateConfidenceScore(behavior: BuyerBehavior, riskFactorCount: number): number {
|
||||
let confidence = 50; // 基础置信度
|
||||
|
||||
// 数据完整性提高置信度
|
||||
if (behavior.order_count > 0) confidence += 10;
|
||||
if (behavior.account_age_days > 30) confidence += 10;
|
||||
if (riskFactorCount > 0) confidence += 15;
|
||||
if (behavior.review_score > 0) confidence += 5;
|
||||
|
||||
// 数据不足降低置信度
|
||||
if (behavior.order_count === 0) confidence -= 20;
|
||||
if (behavior.account_age_days < 7) confidence -= 15;
|
||||
|
||||
return Math.max(10, Math.min(95, confidence));
|
||||
}
|
||||
|
||||
private async saveAssessmentRecord(data: any): Promise<void> {
|
||||
const id = uuidv4();
|
||||
await db('cf_risk_assessment').insert({
|
||||
id,
|
||||
tenant_id: data.tenant_id,
|
||||
shop_id: data.shop_id,
|
||||
task_id: data.task_id,
|
||||
trace_id: data.trace_id,
|
||||
buyer_id: data.buyer_behavior.buyer_id,
|
||||
platform: data.buyer_behavior.platform,
|
||||
platform_buyer_id: data.buyer_behavior.platform_buyer_id,
|
||||
risk_score: data.risk_score,
|
||||
risk_level: data.risk_level,
|
||||
risk_factors: data.risk_factors,
|
||||
recommendations: data.recommendations,
|
||||
is_blacklisted: data.is_blacklisted,
|
||||
blacklist_reasons: data.blacklist_reasons,
|
||||
confidence_score: data.confidence_score,
|
||||
assessment_reason: data.assessment_reason,
|
||||
buyer_behavior: data.buyer_behavior,
|
||||
assessment_date: new Date(),
|
||||
created_at: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
async getRiskAssessments(tenantId: string, filter?: {
|
||||
platform?: string;
|
||||
risk_level?: string;
|
||||
start_date?: Date;
|
||||
end_date?: Date;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<{
|
||||
assessments: any[];
|
||||
total: number;
|
||||
}> {
|
||||
try {
|
||||
let query = db('cf_risk_assessment').where({ tenant_id: tenantId });
|
||||
|
||||
if (filter) {
|
||||
if (filter.platform) {
|
||||
query = query.where({ platform: filter.platform });
|
||||
}
|
||||
if (filter.risk_level) {
|
||||
query = query.where({ risk_level: filter.risk_level });
|
||||
}
|
||||
if (filter.start_date) {
|
||||
query = query.where('assessment_date', '>=', filter.start_date);
|
||||
}
|
||||
if (filter.end_date) {
|
||||
query = query.where('assessment_date', '<=', filter.end_date);
|
||||
}
|
||||
}
|
||||
|
||||
const total = await query.count('id as count').first();
|
||||
const assessments = await query
|
||||
.orderBy('assessment_date', 'desc')
|
||||
.limit(filter?.limit || 100)
|
||||
.offset(filter?.offset || 0);
|
||||
|
||||
return {
|
||||
assessments,
|
||||
total: total ? parseInt(total.count as string) : 0
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.error(`[RiskAssessmentService] Failed to get risk assessments: ${error.message}`);
|
||||
return { assessments: [], total: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
async getRiskStatistics(tenantId: string, days: number = 30): Promise<RiskStatistics> {
|
||||
try {
|
||||
const startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() - days);
|
||||
|
||||
const totalAssessments = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const highRiskCount = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId, risk_level: 'HIGH' })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const mediumRiskCount = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId, risk_level: 'MEDIUM' })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const lowRiskCount = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId, risk_level: 'LOW' })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const criticalRiskCount = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId, risk_level: 'CRITICAL' })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const blacklistConversion = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId, is_blacklisted: true })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const byPlatform = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.select('platform', db.raw('count(*) as count'))
|
||||
.groupBy('platform');
|
||||
|
||||
const byRiskLevel = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.select('risk_level', db.raw('count(*) as count'))
|
||||
.groupBy('risk_level');
|
||||
|
||||
const averageScore = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId })
|
||||
.where('assessment_date', '>=', startDate)
|
||||
.avg('risk_score as avg_score')
|
||||
.first();
|
||||
|
||||
// 生成风险趋势数据
|
||||
const riskTrend = [];
|
||||
for (let i = days - 1; i >= 0; i--) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - i);
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
|
||||
const dayStart = new Date(date);
|
||||
dayStart.setHours(0, 0, 0, 0);
|
||||
|
||||
const dayEnd = new Date(date);
|
||||
dayEnd.setHours(23, 59, 59, 999);
|
||||
|
||||
const dayAverage = await db('cf_risk_assessment')
|
||||
.where({ tenant_id: tenantId })
|
||||
.whereBetween('assessment_date', [dayStart, dayEnd])
|
||||
.avg('risk_score as avg_score')
|
||||
.first();
|
||||
|
||||
riskTrend.push({
|
||||
date: dateStr,
|
||||
average_score: dayAverage?.avg_score ? parseFloat(dayAverage.avg_score as string) : 0
|
||||
});
|
||||
}
|
||||
|
||||
const total = totalAssessments ? parseInt(totalAssessments.count as string) : 0;
|
||||
const blacklistCount = blacklistConversion ? parseInt(blacklistConversion.count as string) : 0;
|
||||
|
||||
return {
|
||||
total_assessments: total,
|
||||
high_risk_count: highRiskCount ? parseInt(highRiskCount.count as string) : 0,
|
||||
medium_risk_count: mediumRiskCount ? parseInt(mediumRiskCount.count as string) : 0,
|
||||
low_risk_count: lowRiskCount ? parseInt(lowRiskCount.count as string) : 0,
|
||||
critical_risk_count: criticalRiskCount ? parseInt(criticalRiskCount.count as string) : 0,
|
||||
blacklist_conversion_rate: total > 0 ? (blacklistCount / total) * 100 : 0,
|
||||
false_positives: 0, // 需要额外逻辑计算
|
||||
false_negatives: 0, // 需要额外逻辑计算
|
||||
by_platform: byPlatform.reduce((acc, item) => {
|
||||
acc[item.platform] = parseInt(item.count as string);
|
||||
return acc;
|
||||
}, {} as Record<string, number>),
|
||||
by_risk_level: byRiskLevel.reduce((acc, item) => {
|
||||
acc[item.risk_level] = parseInt(item.count as string);
|
||||
return acc;
|
||||
}, {} as Record<string, number>),
|
||||
average_risk_score: averageScore?.avg_score ? parseFloat(averageScore.avg_score as string) : 0,
|
||||
risk_trend: riskTrend
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.error(`[RiskAssessmentService] Failed to get risk statistics: ${error.message}`);
|
||||
return {
|
||||
total_assessments: 0,
|
||||
high_risk_count: 0,
|
||||
medium_risk_count: 0,
|
||||
low_risk_count: 0,
|
||||
critical_risk_count: 0,
|
||||
blacklist_conversion_rate: 0,
|
||||
false_positives: 0,
|
||||
false_negatives: 0,
|
||||
by_platform: {},
|
||||
by_risk_level: {},
|
||||
average_risk_score: 0,
|
||||
risk_trend: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async createRiskRule(rule: Omit<RiskRule, 'id' | 'created_at' | 'updated_at'>): Promise<RiskRule> {
|
||||
try {
|
||||
const id = uuidv4();
|
||||
const now = new Date();
|
||||
|
||||
const newRule: RiskRule = {
|
||||
...rule,
|
||||
id,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
};
|
||||
|
||||
await db('cf_risk_rule').insert(newRule);
|
||||
logger.info(`[RiskAssessmentService] Risk rule created: id=${id}, name=${rule.name}`);
|
||||
return newRule;
|
||||
} catch (error: any) {
|
||||
logger.error(`[RiskAssessmentService] Failed to create risk rule: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async updateRiskRule(id: string, updates: Partial<RiskRule>): Promise<boolean> {
|
||||
try {
|
||||
const result = await db('cf_risk_rule')
|
||||
.where({ id })
|
||||
.update({
|
||||
...updates,
|
||||
updated_at: new Date()
|
||||
});
|
||||
|
||||
if (result > 0) {
|
||||
logger.info(`[RiskAssessmentService] Risk rule updated: id=${id}`);
|
||||
}
|
||||
return result > 0;
|
||||
} catch (error: any) {
|
||||
logger.error(`[RiskAssessmentService] Failed to update risk rule: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async getRiskRules(): Promise<RiskRule[]> {
|
||||
try {
|
||||
const rules = await db('cf_risk_rule').where({ enabled: true }).orderBy('weight', 'desc');
|
||||
return rules as RiskRule[];
|
||||
} catch (error: any) {
|
||||
logger.error(`[RiskAssessmentService] Failed to get risk rules: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导入必要的依赖
|
||||
import db from '../config/database';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
Reference in New Issue
Block a user