refactor(services): 重构服务文件结构,将服务按功能分类到不同目录
- 将服务文件按功能分类到core、ai、analytics、security等目录 - 修复logger导入路径问题,统一使用相对路径 - 更新相关文件的导入路径引用 - 添加新的批量操作组件导出文件 - 修复dashboard页面中的类型错误 - 添加dotenv依赖到package.json
This commit is contained in:
220
server/src/api/controllers/RiskControlController.ts
Normal file
220
server/src/api/controllers/RiskControlController.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* [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: '风险扫描失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user