- 将服务文件按功能分类到core、ai、analytics、security等目录 - 修复logger导入路径问题,统一使用相对路径 - 更新相关文件的导入路径引用 - 添加新的批量操作组件导出文件 - 修复dashboard页面中的类型错误 - 添加dotenv依赖到package.json
221 lines
5.8 KiB
TypeScript
221 lines
5.8 KiB
TypeScript
/**
|
|
* [BE-CTL-018] 风控决策AI控制器
|
|
* 提供交易风控的AI介入RESTful API接口
|
|
* AI注意: 所有风控AI决策必须通过此控制器
|
|
*/
|
|
|
|
import { Request, Response } from 'express';
|
|
import { RiskRadarService } from '../../services/security/RiskRadarService';
|
|
import {
|
|
makeAIDecision,
|
|
DecisionType,
|
|
DecisionResult,
|
|
RiskLevel,
|
|
} from '../middleware/AIDecisionMiddleware';
|
|
import { logger } from '../../utils/logger';
|
|
|
|
export class RiskControlController {
|
|
/**
|
|
* POST /api/v1/risk/ai-decision
|
|
* 生成风控AI决策
|
|
*/
|
|
static async makeDecision(req: Request, res: Response) {
|
|
try {
|
|
const { tenantId, operatorId, productId, riskType, transactionData } = req.body;
|
|
|
|
if (!tenantId || !operatorId) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'MISSING_REQUIRED_FIELDS',
|
|
message: '缺少必要字段',
|
|
});
|
|
}
|
|
|
|
const aiDecision = makeAIDecision({
|
|
tenantId,
|
|
operatorId,
|
|
businessType: DecisionType.RISK_CONTROL,
|
|
operationType: riskType || 'TRANSACTION_RISK',
|
|
targetIds: productId ? [productId] : [],
|
|
customParams: {
|
|
riskType,
|
|
fraudScore: transactionData?.fraudScore || 0.3,
|
|
isHighRisk: (transactionData?.fraudScore || 0.3) > 0.7,
|
|
isObviousFraud: (transactionData?.fraudScore || 0.3) > 0.9,
|
|
amount: transactionData?.amount,
|
|
},
|
|
});
|
|
|
|
logger.info(`[RiskControlController] AI decision: ${aiDecision.requiredAction}, risk: ${aiDecision.riskLevel}`);
|
|
|
|
return res.json({
|
|
success: true,
|
|
data: aiDecision,
|
|
});
|
|
} catch (error) {
|
|
logger.error('[RiskControlController] makeDecision error:', error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: 'DECISION_ERROR',
|
|
message: '风控AI决策失败',
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/risk/assess
|
|
* 执行风险评估
|
|
*/
|
|
static async assessRisk(req: Request, res: Response) {
|
|
try {
|
|
const { tenantId, productId, riskType, severity, message, metadata } = req.body;
|
|
|
|
if (!tenantId || !productId || !riskType || !severity) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'MISSING_REQUIRED_FIELDS',
|
|
message: '缺少必要字段',
|
|
});
|
|
}
|
|
|
|
const aiDecision = makeAIDecision({
|
|
tenantId,
|
|
operatorId: 'system',
|
|
businessType: DecisionType.RISK_CONTROL,
|
|
operationType: 'RISK_ASSESS',
|
|
targetIds: [productId],
|
|
customParams: {
|
|
riskType,
|
|
severity,
|
|
metadata,
|
|
},
|
|
});
|
|
|
|
await RiskRadarService.recordRisk({
|
|
tenantId,
|
|
productId,
|
|
type: riskType,
|
|
severity,
|
|
message,
|
|
metadata,
|
|
}, `RISK-${Date.now()}`);
|
|
|
|
logger.info(`[RiskControlController] Risk recorded: ${productId}, severity: ${severity}`);
|
|
|
|
return res.json({
|
|
success: true,
|
|
data: {
|
|
aiDecision,
|
|
riskLevel: aiDecision.riskLevel,
|
|
requiredAction: aiDecision.requiredAction,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
logger.error('[RiskControlController] assessRisk error:', error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: 'ASSESS_ERROR',
|
|
message: '风险评估失败',
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/risk/block
|
|
* 拦截交易
|
|
*/
|
|
static async blockTransaction(req: Request, res: Response) {
|
|
try {
|
|
const { tenantId, productId, transactionId, reason } = req.body;
|
|
|
|
if (!tenantId || !productId || !transactionId) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'MISSING_REQUIRED_FIELDS',
|
|
message: '缺少必要字段',
|
|
});
|
|
}
|
|
|
|
const aiDecision = makeAIDecision({
|
|
tenantId,
|
|
operatorId: 'system',
|
|
businessType: DecisionType.RISK_CONTROL,
|
|
operationType: 'BLOCK_TRANSACTION',
|
|
targetIds: [productId, transactionId],
|
|
customParams: {
|
|
transactionId,
|
|
reason,
|
|
fraudScore: 0.95,
|
|
isObviousFraud: true,
|
|
},
|
|
});
|
|
|
|
if (aiDecision.riskLevel === RiskLevel.CRITICAL || aiDecision.riskLevel === RiskLevel.HIGH) {
|
|
logger.warn(`[RiskControlController] Transaction BLOCKED: ${transactionId}, reason: ${reason}`);
|
|
return res.status(200).json({
|
|
success: true,
|
|
data: {
|
|
action: 'BLOCKED',
|
|
transactionId,
|
|
reason,
|
|
aiDecision,
|
|
},
|
|
});
|
|
}
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
data: {
|
|
action: 'ALLOWED',
|
|
transactionId,
|
|
aiDecision,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
logger.error('[RiskControlController] blockTransaction error:', error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: 'BLOCK_ERROR',
|
|
message: '交易拦截失败',
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/risk/scan
|
|
* 执行风险扫描
|
|
*/
|
|
static async scanRisks(req: Request, res: Response) {
|
|
try {
|
|
const { tenantId } = req.body;
|
|
|
|
if (!tenantId) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'MISSING_TENANT_ID',
|
|
message: '缺少租户ID',
|
|
});
|
|
}
|
|
|
|
await RiskRadarService.performRiskScan(tenantId);
|
|
|
|
logger.info(`[RiskControlController] Risk scan completed for tenant: ${tenantId}`);
|
|
|
|
return res.json({
|
|
success: true,
|
|
data: {
|
|
message: '风险扫描完成',
|
|
tenantId,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
logger.error('[RiskControlController] scanRisks error:', error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: 'SCAN_ERROR',
|
|
message: '风险扫描失败',
|
|
});
|
|
}
|
|
}
|
|
}
|