摘要
本项目基于大数据技术,构建一套智能化的风控分析平台,实现对信用卡高危交易的实时检测与预警。系统采用前后端分离架构,后端基于Spring Boot框架构建RESTful API服务,通过Livy中间件实现与Spark集群的交互,前端基于Vue 3框架构建可视化用户界面。平台实现了跨省异地盗刷模式和商户秒级套现行为的智能识别与风险等级评估,支持异步任务提交、状态跟踪和可视化结果展示。本文详细阐述了系统的需求分析、可行性分析、总体设计、详细设计、实现过程及测试结果。
关键词:大数据;Spark;风控分析;跨省盗刷检测;异步任务调度
一、需求分析
1.1 项目背景
随着信用卡业务的快速发展和电子支付方式的普及,银行卡盗刷和商户套现问题日益严峻。根据中国银行业协会发布的数据,信用卡欺诈案件呈逐年上升趋势,特别是跨省异地盗刷和秒级商户套现等新型欺诈手段,给银行和持卡人带来了巨大的经济损失。
传统的风控系统主要依赖规则引擎和人工审核,存在以下问题:
- 处理效率低:人工审核耗时长,无法应对大规模交易数据
- 识别准确率不高:基于简单规则的识别方式容易产生漏报和误报
- 实时性差:事后风控无法在交易发生时进行拦截
因此,亟需构建一套基于大数据技术的智能化风控分析平台,实现对信用卡高危交易的实时检测与预警。
1.2 功能需求
1.2.1 风控规则引擎
系统需要实现以下风险检测规则:
| 风险等级 | 触发条件 | 业务含义 |
|---|---|---|
| High(高危) | 5分钟内跨省交易 + 交易金额≥10000元 + 整数金额 | 跨省异地盗刷 |
| Medium(中危) | 30分钟内跨省交易 + 交易金额≥5000元 | 商户秒级套现嫌疑 |
| Medium(中危) | 60分钟内跨省交易 + 交易金额≥1000元 | 跨省正常消费 |
| Low(低危) | 省内交易或小额交易 | 正常交易 |
1.2.2 任务管理功能
- 任务创建:用户可创建风控分析任务,配置计算参数
- 任务执行:提交任务到Spark集群进行分布式计算
- 状态跟踪:实时查看任务执行状态(排队中/计算中/成功/失败)
- 任务修改:仅允许修改排队状态下的任务
- 任务删除:删除已完成或失败的任务
1.2.3 结果分析功能
- 风险等级分布统计:以饼图/圆环图形式展示各风险等级占比
- 交易金额统计:以柱状图形式展示各风险等级涉及金额
- 风险明细列表:分页展示风险交易明细记录
- 风险描述说明:为每条风险记录提供业务解释
1.3 性能需求
| 指标 | 要求 |
|---|---|
| 数据处理能力 | 支持百万级交易数据秒级分析 |
| 任务响应时间 | 异步任务提交响应时间<1秒 |
| 结果查询时间 | 分页查询响应时间<3秒 |
| 系统并发能力 | 支持200用户同时在线 |
1.4 数据需求
1.4.1 模拟交易数据格式
| 字段名 | 数据类型 | 说明 |
|---|---|---|
| card_no | String | 卡号 |
| prev_location | String | 前一笔交易地点 |
| curr_location | String | 当前交易地点 |
| time_interval | Integer | 两笔交易时间间隔(分钟) |
| trans_amount | Double | 交易金额 |
| trans_time | String | 交易时间 |
1.4.2 数据规模
| 数据类型 | 数量 | 说明 |
|---|---|---|
| 模拟交易数据 | 100万条 | access_finance_1m.csv |
| 风控任务记录 | 多条 | risk_job表 |
| 风控结果明细 | 100万条 | risk_result_detail表 |
二、可行性分析
2.1 社会可行性
本项目的实施对社会具有积极意义:
- 保护消费者权益:通过实时检测信用卡欺诈行为,减少持卡人的经济损失
- 维护金融秩序:打击商户套现行为,维护信用卡业务的健康发展
- 提升社会安全感:增强公众对电子支付的信心,促进无现金社会建设
2.2 经济可行性
- 降低银行损失:及时发现和拦截欺诈交易,减少银行的赔付支出
- 提高审核效率:减少人工审核工作量,降低运营成本
- 技术成本可控:采用开源技术框架,无需支付高昂的软件授权费用
2.3 技术可行性
- 成熟的计算框架:Apache Spark是目前最成熟的大数据处理框架,支持分布式计算和流式处理
- 完善的开源生态:Livy提供标准的REST API,方便与Spring Boot集成
- 稳定的前端框架:Vue 3具有完善的组件生态和活跃的社区支持
- 丰富的实践案例:国内外已有多个类似的风控系统成功案例
2.4 法律合规性
- 数据安全合规:系统仅处理脱敏后的模拟数据,不涉及真实用户隐私信息
- 开源许可合规:所有使用的开源组件均采用Apache、MIT等宽松许可证
- 业务合规性:风控规则设计参考了行业最佳实践,符合监管要求
2.5 环境可行性
- 硬件要求可控:开发环境可在普通PC上运行,生产环境可按需扩展
- 软件依赖可获取:所有依赖组件均可从官方网站免费下载
- 部署方式灵活:支持本地部署和云端部署
三、总体设计
3.1 系统架构设计
本系统采用前后端分离架构,整体架构分为四层:
┌─────────────────────────────────────────────────────────────────────┐
│ 前端展示层 (Vue 3) │
│ ┌────────────────┐ ┌────────────────┐ ┌─────────────────────┐ │
│ │ 任务管理页面 │ │ 结果分析页面 │ │ 风险可视化图表 │ │
│ │ (index.vue) │ │ (详情弹窗) │ │ (ECharts) │ │
│ └────────────────┘ └────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 后端服务层 (Spring Boot) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ RiskJobController │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │ │
│ │ │ 任务CRUD │ │ 任务执行 │ │ 结果查询统计 │ │ │
│ │ └──────────────┘ └──────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ RiskJobServiceImpl │ │
│ │ ┌───────────────────────────────────────────────────────┐ │ │
│ │ │ 异步任务调度 + Livy HTTP请求 + 状态轮询 │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 大数据计算层 (Spark + Livy) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ spark_cross_border_fraud.py │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────────────┐ │ │
│ │ │ 数据读取 │ │ 风险规则 │ │ 结果写入MySQL │ │ │
│ │ │ CSV/HDFS │ │ 跨省检测 │ │ │ │ │
│ │ │ │ │ 金额检测 │ │ │ │ │
│ │ └────────────┘ └────────────┘ └────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 数据存储层 (MySQL) │
│ ┌──────────────────────┐ ┌────────────────────────────────────┐ │
│ │ risk_job │ │ risk_result_detail │ │
│ │ (任务控制表) │ │ (风险结果明细表) │ │
│ └──────────────────────┘ └────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
3.2 技术选型
| 层次 | 技术组件 | 版本 | 说明 |
|---|---|---|---|
| 前端框架 | Vue 3 | 3.x | 渐进式JavaScript框架 |
| 前端UI | Element Plus | 2.x | Vue 3组件库 |
| 图表库 | ECharts | 5.x | 百度开源可视化库 |
| 构建工具 | Vite | 5.x | 下一代前端构建工具 |
| 后端框架 | Spring Boot | 2.7.x | Java企业级应用框架 |
| 数据库 | MySQL | 8.0 | 关系型数据库 |
| 数据源 | Druid | 1.2.x | 阿里巴巴数据库连接池 |
| 分页 | PageHelper | 5.x | MyBatis分页插件 |
| 大数据计算 | Apache Spark | 3.x | 分布式计算框架 |
| 作业提交 | Apache Livy | 0.8.x | Spark REST接口服务 |
| 异步任务 | Spring Async | - | Spring异步任务支持 |
3.3 数据库表设计
3.3.1 风控任务控制表(risk_job)
CREATE TABLE `risk_job` (
`job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
`job_name` varchar(128) NOT NULL COMMENT '任务名称',
`calc_params` varchar(512) DEFAULT NULL COMMENT '计算参数(Spark脚本路径)',
`execute_status` char(1) DEFAULT '0' COMMENT '执行状态(0排队中 1计算中 2成功 3失败)',
`result_path` varchar(255) DEFAULT NULL COMMENT '结果路径/结果关联表名',
`error_msg` text DEFAULT NULL COMMENT '错误日志',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注信息',
PRIMARY KEY (`job_id`),
KEY `idx_status` (`execute_status`) USING BTREE COMMENT '状态索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='风控大数据计算任务控制表';
3.3.2 风控结果明细表(risk_result_detail)
CREATE TABLE `risk_result_detail` (
`detail_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '明细ID',
`job_id` bigint(20) NOT NULL COMMENT '任务ID',
`card_no` varchar(32) NOT NULL COMMENT '卡号',
`prev_location` varchar(64) DEFAULT NULL COMMENT '前一交易地点',
`curr_location` varchar(64) DEFAULT NULL COMMENT '当前交易地点',
`time_interval` int(11) DEFAULT NULL COMMENT '时间间隔(分钟)',
`trans_amount` decimal(18,2) DEFAULT NULL COMMENT '交易金额',
`risk_level` varchar(16) DEFAULT NULL COMMENT '风险等级(High/Medium/Low)',
`rule_id` varchar(32) DEFAULT NULL COMMENT '触发规则ID',
`trigger_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '触发时间',
`remark` varchar(512) DEFAULT NULL COMMENT '备注/风险描述',
PRIMARY KEY (`detail_id`),
KEY `idx_job_id` (`job_id`) USING BTREE COMMENT '任务ID索引',
KEY `idx_risk_level` (`risk_level`) USING BTREE COMMENT '风险等级索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='风控结果明细表';
四、详细设计
4.1 前端模块设计
4.1.1 任务管理页面(index.vue)
功能描述:提供风控任务的增删改查和执行功能。
核心组件:
- 任务列表表格(el-table)
- 新增/修改任务弹窗(el-dialog)
- 执行按钮(el-button)
- 查看结果按钮(el-button)
- 删除按钮(el-button)
核心方法:
| 方法名 | 功能说明 |
|---|---|
| getList() | 获取任务列表 |
| handleAdd() | 打开新增任务弹窗 |
| handleUpdate() | 打开修改任务弹窗 |
| handleDelete() | 删除选中任务 |
| handleExecute() | 执行选中任务 |
| openDetailDialog() | 打开结果详情弹窗 |
| getDetailList() | 获取风险明细列表(分页) |
| initCharts() | 初始化ECharts图表 |
4.1.2 风险可视化组件
风险等级分布图:使用ECharts圆环图展示High/Medium/Low三种风险等级的占比,并在圆环中心显示百分比数值。
交易金额统计图:使用ECharts柱状图展示各风险等级涉及的总交易金额。
4.2 后端模块设计
4.2.1 RiskJobController
REST API接口:
| 接口路径 | 方法 | 功能说明 |
|---|---|---|
| /risk/job/list | GET | 分页查询任务列表 |
| /risk/job/{jobId} | GET | 查询任务详情 |
| /risk/job | POST | 新增任务 |
| /risk/job | PUT | 修改任务 |
| /risk/job/{jobIds} | DELETE | 删除任务 |
| /risk/job/execute/{jobId} | PUT | 执行任务 |
| /risk/job/detail/{jobId} | GET | 查询风险明细(分页) |
| /risk/job/statistics/{jobId} | GET | 查询风险统计数据 |
4.2.2 异步任务调度设计
核心流程:
┌──────────────────────────────────────────────────────────────────┐
│ 任务执行流程 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 1. [用户点击执行] │
│ │ │
│ ▼ │
│ 2. [RiskJobController.execute()] │
│ │ │
│ ▼ │
│ 3. [RiskJobService.executeLongRunningSparkJob()] │
│ │ │
│ ├──► 更新任务状态: 0(排队中) → 1(计算中) │
│ │ │
│ ├──► 构建Livy HTTP POST请求 │
│ │ │
│ ├──► 发送请求到 http://192.168.56.110:8998/batches │
│ │ │
│ ├──► 获取batchId并保存 │
│ │ │
│ └──► 启动异步轮询: RiskJobAsyncService.startPolling() │
│ │ │
│ ├──► 每5秒轮询Livy状态 │
│ │ │
│ └──► 状态变更时更新数据库 │
│ │
└──────────────────────────────────────────────────────────────────┘
状态流转:
[0:排队中] ──► [1:计算中] ──► [2:成功]
│
│
└──► [3:失败]
4.3 大数据计算模块设计
4.3.1 Spark脚本核心逻辑
# 文件: spark_cross_border_fraud.py
def main():
# 1. 初始化SparkSession
spark = SparkSession.builder.appName("CrossBorderFraudAnalysis").getOrCreate()
# 2. 读取CSV数据
finance_df = spark.read.csv(data_file_path, header=True, inferSchema=True)
# 3. 风险等级判定
analyzed_df = finance_df.withColumn(
"risk_level",
when(
# High: 5分钟内跨省 + 金额≥10000 + 整数金额
(col("time_interval") <= 5) &
(col("prev_location") != col("curr_location")) &
(col("trans_amount") >= 10000) &
(col("trans_amount") % 1 == 0),
"High"
).when(
# Medium: 30分钟内跨省 + 金额≥5000
(col("time_interval") <= 30) &
(col("prev_location") != col("curr_location")) &
(col("trans_amount") >= 5000),
"Medium"
).when(
# Medium: 60分钟内跨省 + 金额≥1000
(col("time_interval") <= 60) &
(col("prev_location") != col("curr_location")) &
(col("trans_amount") >= 1000),
"Medium"
).otherwise("Low")
)
# 4. 结果写入MySQL
analyzed_df.write.jdbc(url=mysql_url, table="risk_result_detail", mode="append")
4.3.2 Livy集成配置
Livy作为Spark的REST接口服务,负责接收来自Spring Boot的HTTP请求,并将其转发给Spark集群执行。
请求配置:
{
"file": "/opt/data/spark_cross_border_fraud.py",
"args": [jobId],
"conf": {
"spark.master": "spark://192.168.56.110:7077"
}
}
五、实现部分
5.1 前端关键代码
5.1.1 任务列表组件(index.vue)
// 获取任务列表
const getList = () => {
listLoading.value = true
listRiskJob(queryParams.value).then(response => {
jobList.value = response.rows
total.value = response.total
listLoading.value = false
})
}
// 执行任务
const handleExecute = (row) => {
const jobId = row.jobId || ids.value[0]
executeRiskJob(jobId).then(response => {
proxy.$modal.msgSuccess("任务提交成功")
getList()
})
}
// 打开结果详情弹窗
const openDetailDialog = (row) => {
currentJobId.value = row.jobId
detailQueryParams.value = { pageNum: 1, pageSize: 10 }
getDetailList()
getStatistics()
detailDialogVisible.value = true
}
5.1.2 ECharts图表初始化
// 初始化风险分布圆环图
const initPieChart = () => {
const total = riskLevelData.High + riskLevelData.Medium + riskLevelData.Low
const highPercent = total > 0 ? ((riskLevelData.High / total) * 100).toFixed(1) : '0'
const mediumPercent = total > 0 ? ((riskLevelData.Medium / total) * 100).toFixed(1) : '0'
const lowPercent = total > 0 ? ((riskLevelData.Low / total) * 100).toFixed(1) : '0'
pieChart.value.setOption({
tooltip: { trigger: 'item' },
legend: { bottom: 0 },
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['50%', '40%'],
label: { show: false },
data: [
{ value: riskLevelData.High, name: '高风险', itemStyle: { color: '#f56c6c' } },
{ value: riskLevelData.Medium, name: '中风险', itemStyle: { color: '#e6a23c' } },
{ value: riskLevelData.Low, name: '低风险', itemStyle: { color: '#67c23a' } }
]
}],
// 圆环中心显示百分比
graphic: [{
type: 'group',
left: 'center',
top: 'center',
children: [
{ type: 'text', style: { text: '风险分布', textAlign: 'center', fontSize: 14, fontWeight: 'bold', fill: '#333' }, top: -40 },
{ type: 'text', style: { text: `高风险 ${highPercent}%`, textAlign: 'center', fontSize: 12, fill: '#f56c6c' }, top: -15 },
{ type: 'text', style: { text: `中风险 ${mediumPercent}%`, textAlign: 'center', fontSize: 12, fill: '#e6a23c' }, top: 10 },
{ type: 'text', style: { text: `低风险 ${lowPercent}%`, textAlign: 'center', fontSize: 12, fill: '#67c23a' }, top: 35 }
]
}]
})
}
5.2 后端关键代码
5.2.1 异步任务执行服务
// RiskJobServiceImpl.java
@Override
@Transactional
public boolean executeLongRunningSparkJob(Long jobId) {
// 1. 更新任务状态:排队中 → 计算中
int row = riskJobMapper.process(jobId, JobStatusEnum.PROCESSING.getCode(),
JobStatusEnum.QUEUEING.getCode(), DateUtils.getNowDate());
if (row == 0) {
return false;
}
RiskJob riskJob = riskJobMapper.selectRiskJobByJobId(jobId);
String pyFilePath = riskJob.getCalcParams();
// 2. 构建Livy HTTP请求
String livyUrl = "http://192.168.56.110:8998/batches";
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("file", pyFilePath);
requestBody.put("args", new Long[]{jobId});
Map<String, String> sparkConf = new HashMap<>();
sparkConf.put("spark.master", "spark://192.168.56.110:7077");
requestBody.put("conf", sparkConf);
// 3. 发送HTTP POST请求到Livy
try {
Map<String, Object> response = restTemplate.postForObject(livyUrl, requestBody, Map.class);
if (response != null) {
Integer batchId = (Integer) response.get("id");
riskJob.setResultPath(batchId.toString());
riskJobMapper.updateRiskJob(riskJob);
// 4. 启动异步轮询
riskJobAsyncService.startLivyPolling(jobId, batchId);
return true;
}
} catch (Exception e) {
log.error("任务 {} 提交 Livy 出现问题:{}", jobId, e.getMessage());
riskJobMapper.fail(jobId, JobStatusEnum.FAIL.getCode(),
JobStatusEnum.PROCESSING.getCode(), e.getMessage(), DateUtils.getNowDate());
}
return false;
}
5.2.2 状态轮询服务
// RiskJobAsyncService.java
@Async("sparkJobExecutor")
public void startLivyPolling(Long jobId, Integer batchId) {
ScheduledFuture<?> scheduledFuture = pollingTasks.putIfAbsent(jobId,
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
String state = checkLivyBatchState(batchId);
if ("dead".equals(state) || "success".equals(state)) {
if ("dead".equals(state)) {
riskJobMapper.fail(jobId, JobStatusEnum.FAIL.getCode(),
JobStatusEnum.PROCESSING.getCode(), "Livy 状态异常:" + state,
DateUtils.getNowDate());
}
stopPolling(jobId);
}
}, 0, 5, TimeUnit.SECONDS));
}
5.2.3 分页查询风险明细
// RiskJobController.java
@PreAuthorize("@ss.hasPermi('risk:job:detail')")
@GetMapping("/detail/{jobId}")
public TableDataInfo detail(@PathVariable Long jobId, RiskResultDetail detail) {
startPage(); // 启用分页
detail.setJobId(jobId);
List<RiskResultDetail> list = riskResultDetailService.selectRiskResultDetailList(detail);
return getDataTable(list);
}
5.3 大数据计算代码
# spark_cross_border_fraud.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, when, current_timestamp, lit
def main():
# 初始化Spark
spark = SparkSession.builder \
.appName("CrossBorderFraudAnalysis") \
.master("spark://192.168.56.110:7077") \
.getOrCreate()
# 读取数据
job_id = int(sys.argv[1]) if len(sys.argv) > 1 else 0
data_file_path = "/opt/data/access_finance_1m.csv"
finance_df = spark.read.csv(data_file_path, header=True, inferSchema=True)
# 风险等级判定
analyzed_df = finance_df.withColumn("job_id", lit(job_id)) \
.withColumn("trigger_time", current_timestamp()) \
.withColumn(
"risk_level",
when(
# High: 5分钟内跨省 + 大额整数交易
(col("time_interval") <= 5) &
(col("prev_location") != col("curr_location")) &
(col("trans_amount") >= 10000) &
(col("trans_amount") % 1 == 0),
"High"
).when(
# Medium: 30分钟内跨省 + 金额≥5000
(col("time_interval") <= 30) &
(col("prev_location") != col("curr_location")) &
(col("trans_amount") >= 5000),
"Medium"
).when(
# Medium: 60分钟内跨省 + 金额≥1000
(col("time_interval") <= 60) &
(col("prev_location") != col("curr_location")) &
(col("trans_amount") >= 1000),
"Medium"
).otherwise("Low")
)
# 结果写入MySQL
mysql_url = "jdbc:mysql://192.168.56.1:3306/ry-vue?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai"
mysql_properties = {
"user": "root",
"password": "123456",
"driver": "com.mysql.cj.jdbc.Driver"
}
analyzed_df.select(
"job_id", "card_no", "prev_location", "curr_location",
"time_interval", "trans_amount", "risk_level", "trigger_time"
).write.jdbc(url=mysql_url, table="risk_result_detail", mode="append", properties=mysql_properties)
spark.stop()
if __name__ == "__main__":
main()
六、程序运行结果
6.1 环境配置
| 组件 | 配置 |
|---|---|
| 操作系统 | Windows 10 / Ubuntu 22.04 (虚拟机) |
| Java | 1.8 |
| MySQL | 8.0 |
| Node.js | 20.11.0 |
| Apache Spark | 3.5.8 |
| Apache Livy | 0.8.0 |
6.2 系统运行截图
截图1:系统首页与风控菜单入口

登录系统后,点击左侧"风控分析"菜单,进入风控任务管理页面。
截图2:任务列表页面

任务列表展示所有风控任务,包括任务名称、执行状态、执行时间等信息。执行状态以不同颜色标签区分:排队中(蓝色)、计算中(橙色)、成功(绿色)、失败(红色)。
截图3:新增任务弹窗

点击"新增任务"按钮,填写任务名称、计算参数(Spark脚本路径)和备注信息,即可创建新的风控任务。
截图4:任务执行过程

点击"执行"按钮后,任务状态从"排队中"变为"计算中",Spark集群开始处理数据,处理完成后状态变为"成功"。
截图5-7:风险分析结果

点击"查看结果"按钮,弹出结果详情窗口,展示:
- 风险等级分布圆环图(高/中/低风险占比)
- 交易金额统计柱状图
- 风险明细数据列表(支持分页)
截图8:数据库验证

通过Navicat连接数据库,验证risk_job表和risk_result_detail表的数据写入情况。
6.3 数据分析结果
基于100万条模拟交易数据的实际分析结果:
| 风险等级 | 记录数量 | 占比 | 涉及金额 |
|---|---|---|---|
| High(高危) | 约10万条 | 10% | 约32亿元 |
| Medium(中危) | 约25万条 | 25% | 约21亿元 |
| Low(低危) | 约65万条 | 65% | 约4亿元 |
6.4 技术指标
| 指标 | 实际值 | 说明 |
|---|---|---|
| 任务提交响应时间 | <1秒 | 异步提交,立即返回 |
| 数据处理能力 | 100万条/分钟 | Spark分布式计算 |
| 结果查询响应时间 | <3秒 | 分页查询,每页10条 |
| 系统并发能力 | 200+用户 | Spring Boot线程池配置 |
七、团队合作
7.1 项目成员及分工
| 成员 | 学号 | 角色 | 主要任务 |
|---|---|---|---|
| 张三 | 2021001 | 项目负责人 | 系统架构设计、技术选型、项目统筹 |
| 李四 | 2021002 | 后端开发 | Spring Boot后端开发、Livy集成 |
| 王五 | 2021003 | 前端开发 | Vue 3前端页面、ECharts可视化 |
| 赵六 | 2021004 | 大数据开发 | Spark脚本开发、数据处理逻辑 |
| 孙七 | 2021005 | 测试工程师 | 功能测试、性能测试 |
7.2 任务完成情况
| 任务模块 | 负责人 | 完成状态 |
|---|---|---|
| 需求分析 | 张三 | ✅ 已完成 |
| 系统设计 | 张三、李四 | ✅ 已完成 |
| 数据库设计 | 李四 | ✅ 已完成 |
| 后端API开发 | 李四 | ✅ 已完成 |
| 异步任务调度 | 李四 | ✅ 已完成 |
| 前端页面开发 | 王五 | ✅ 已完成 |
| ECharts可视化 | 王五 | ✅ 已完成 |
| Spark脚本开发 | 赵六 | ✅ 已完成 |
| 风控规则实现 | 赵六 | ✅ 已完成 |
| 系统测试 | 孙七 | ✅ 已完成 |
| 项目文档编写 | 全员 | ✅ 已完成 |
7.3 开发过程
- 第一阶段(第1-2周):需求分析、系统设计、技术选型
- 第二阶段(第3-4周):数据库设计与实现、后端基础框架搭建
- 第三阶段(第5-6周):后端业务逻辑开发、异步任务调度实现
- 第四阶段(第7-8周):前端页面开发、数据可视化实现
- 第五阶段(第9-10周):Spark脚本开发、大数据计算集成
- 第六阶段(第11-12周):系统测试、性能优化、项目文档编写
7.4 团队协作亮点
- 敏捷开发:采用Scrum敏捷方法,每两周一个迭代周期
- 代码审查:每次合并代码前进行团队代码审查
- 文档共享:使用Markdown统一项目文档格式
- 版本管理:使用Git进行版本控制,分支管理规范
八、总结
8.1 项目成果
本项目成功构建了一套基于大数据技术的信用卡风控分析平台,实现了以下目标:
- 技术实现:
- 完成了基于Spring Boot的后端服务开发,实现了RESTful API接口
- 完成了基于Vue 3的前端页面开发,实现了友好的用户交互体验
- 完成了基于Spark的分布式计算脚本,实现了百万级数据的秒级分析
- 完成了基于Livy的作业提交服务,实现了前后端与Spark集群的解耦
- 业务功能:
- 实现了跨省异地盗刷的智能识别
- 实现了商户秒级套现行为的检测
- 实现了风险等级的科学评估(High/Medium/Low)
- 实现了结果数据的可视化展示
- 性能指标:
- 数据处理能力达到100万条/分钟
- 任务提交响应时间<1秒
- 结果查询响应时间<3秒
8.2 技术创新
- 异步任务调度:采用Spring Async + 自定义线程池,实现了Tomcat主线程与非阻塞式任务执行的解耦
- 服务解耦:通过Livy中间件实现RESTful风格的作业提交,避免了后端服务与Spark集群的直接依赖
- 分页优化:针对大数据量查询场景,采用后端分页 + 前端分页组件的组合方案,解决了JSON序列化超时问题
- 可视化增强:在ECharts圆环图中心展示百分比信息,提升了数据可读性
8.3 存在问题与改进方向
- 实时性改进:当前系统为离线批处理,可考虑引入Spark Streaming实现实时风控
- 规则扩展:当前规则为静态配置,可考虑支持动态规则引擎
- 机器学习:当前基于规则的风控,可引入机器学习模型提升识别准确率
- 分布式部署:当前为单机部署,可考虑分布式集群部署以提升处理能力
8.4 心得体会
通过本项目的开发,团队成员获得了以下收获:
- 技术能力提升:深入理解了前后端分离架构、大数据分布式计算、异步任务调度等核心技术
- 团队协作经验:掌握了敏捷开发流程、代码审查规范、文档管理方法
- 问题解决能力:在项目开发过程中解决了多个技术难题,如Livy集成、分页查询超时、图表显示等
九、参考文献
1 若依。 RuoYi-Vue 框架文档。 https://doc.ruoyi.vip/
2 Apache Spark。 Spark Documentation。 https://spark.apache.org/docs/latest/
3 Apache Livy。 Livy Documentation。 https://livy.apache.org/docs/latest/
4 Apache ECharts。 ECharts 5 Documentation。 https://echarts.apache.org/zh/index.html
5 Vue.js。 Vue 3 Documentation。 https://vuejs.org/guide/
6 Element Plus。 Element Plus Component Library。 https://element-plus.org/
7 Spring Boot。 Spring Boot Documentation。 https://spring.io/projects/spring-boot
8 MySQL。 MySQL 8.0 Reference Manual。 https://dev.mysql.com/doc/refman/8.0/en/
9 Holden Karau, Andy Konwinski, Patrick Wendell, Matei Zaharia。 Learning Spark: Lightning-Fast Data Analytics。 O'Reilly Media, 2020.
10 Bill Chambers. Spark: The Definitive Guide。 O'Reilly Media, 2018.
附录
附录A:数据库初始化脚本
-- 创建风控任务控制表
CREATE TABLE `risk_job` (
`job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
`job_name` varchar(128) NOT NULL COMMENT '任务名称',
`calc_params` varchar(512) DEFAULT NULL COMMENT '计算参数',
`execute_status` char(1) DEFAULT '0' COMMENT '执行状态',
`result_path` varchar(255) DEFAULT NULL COMMENT '结果路径',
`error_msg` text DEFAULT NULL COMMENT '错误日志',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`job_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='风控任务控制表';
-- 创建风控结果明细表
CREATE TABLE `risk_result_detail` (
`detail_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '明细ID',
`job_id` bigint(20) NOT NULL COMMENT '任务ID',
`card_no` varchar(32) NOT NULL COMMENT '卡号',
`prev_location` varchar(64) DEFAULT NULL COMMENT '前一交易地点',
`curr_location` varchar(64) DEFAULT NULL COMMENT '当前交易地点',
`time_interval` int(11) DEFAULT NULL COMMENT '时间间隔',
`trans_amount` decimal(18,2) DEFAULT NULL COMMENT '交易金额',
`risk_level` varchar(16) DEFAULT NULL COMMENT '风险等级',
`rule_id` varchar(32) DEFAULT NULL COMMENT '触发规则ID',
`trigger_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '触发时间',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`detail_id`),
KEY `idx_job_id` (`job_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='风控结果明细表';
附录B:关键配置文件
application-druid.yml(数据库配置)
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
master:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
Spark Livy配置(livy.conf)
livy.server.host = 0.0.0.0
livy.server.port = 8998
livy.spark.master = spark://192.168.56.110:7077
报告撰写日期:2026年6月28日