大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Spark实时医疗健康数据监测与疾病预测系统(Java版本+可视化大屏+Kafka+SpringBoot+Vue3)

项目介绍
随着医疗信息化与物联网可穿戴设备的快速发展,医院与健康管理机构面临着体征数据规模大、产生速度快、分析时效性要求高的挑战。传统以事后统计为主的信息系统难以满足实时监测与早期预警需求。本文设计并实现了一套基于Spark的实时医疗健康数据监测与疾病预测系统,围绕“数据采集—消息传输—流式聚合—风险预测—可视化展示”的完整链路开展研究与开发。
系统后端采用Java 17与Spring Boot 3构建RESTful服务,结合Spring Security与JWT完成管理员身份认证;数据访问层使用MyBatis-Plus操作MySQL数据库db_health;实时事件通过Apache Kafka主题health_vitals进行解耦传输;计算层引入Apache Spark SQL与Spark MLlib,对按小时窗口聚合的风险指数序列进行线性回归预测,并计算RMSE、MAE、MAPE误差指标。前端采用Vue 3、Vite、Element Plus与ECharts实现管理后台与数据大屏,支持患者档案管理、体征记录查询、实时统计分析、疾病风险预测对比以及个人中心(头像上传、资料修改、密码修改)等功能。
测试结果表明,系统能够稳定完成模拟体征数据的持续接入与入库,实时统计与预测结果可在前端动态刷新,功能完整性与交互可用性满足本科毕业设计要求。本课题将大数据流处理、机器学习预测与Web工程实践相结合,对智慧医疗场景下的实时健康管理具有一定参考价值。
源码下载
链接: https://pan.baidu.com/s/1hNYZ5_11TXkswBhGGYhZHg?pwd=1234
提取码: 1234
系统展示




核心代码
package com.java1234.controller;
import com.java1234.common.PageResult;
import com.java1234.common.Result;
import com.java1234.dto.PatientCreateRequest;
import com.java1234.dto.PatientUpdateRequest;
import com.java1234.service.PatientService;
import com.java1234.vo.PatientVo;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* 患者管理控制器
*/
@RestController
@RequestMapping("/api/patient")
@RequiredArgsConstructor
public class PatientController {
private final PatientService patientService;
/**
* 分页查询患者列表
*/
@GetMapping("/list")
public Result<PageResult<PatientVo>> list(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String keyword) {
return Result.success(patientService.listPatients(page, size, keyword));
}
/**
* 新增患者
*/
@PostMapping("/create")
public Result<PatientVo> create(@RequestBody PatientCreateRequest request) {
return Result.success(patientService.createPatient(request));
}
/**
* 更新患者信息
*/
@PutMapping("/update/{patientId}")
public Result<PatientVo> update(@PathVariable Integer patientId,
@RequestBody PatientUpdateRequest request) {
return Result.success(patientService.updatePatient(patientId, request));
}
/**
* 删除患者
*/
@DeleteMapping("/delete/{patientId}")
public Result<Void> delete(@PathVariable Integer patientId) {
patientService.deletePatient(patientId);
return Result.success("删除成功");
}
/**
* 获取疾病类型列表
*/
@GetMapping("/diseases")
public Result<List<Map<String, Object>>> diseases() {
return Result.success(patientService.listDiseases());
}
}
<template>
<div class="page-container">
<div class="page-card">
<div class="page-title">患者管理</div>
<div style="margin-bottom:16px;display:flex;gap:12px">
<el-input v-model="keyword" placeholder="搜索患者姓名" clearable style="width:240px" @clear="loadData" @keyup.enter="loadData" />
<el-button type="primary" @click="loadData">搜索</el-button>
<el-button type="success" @click="openDialog()">新增患者</el-button>
</div>
<el-table :data="tableData" stripe border style="width:100%">
<el-table-column prop="id" label="ID" min-width="60" />
<el-table-column prop="name" label="姓名" min-width="100" />
<el-table-column prop="gender" label="性别" min-width="70" />
<el-table-column prop="age" label="年龄" min-width="70" />
<el-table-column prop="city" label="城市" min-width="100" />
<el-table-column prop="disease_name" label="疾病类型" min-width="120" />
<el-table-column prop="phone" label="联系电话" min-width="130" />
<el-table-column prop="create_time" label="创建时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.create_time) }}</template>
</el-table-column>
<el-table-column label="操作" min-width="160" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="openDialog(row)">编辑</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="margin-top:16px;justify-content:flex-end"
v-model:current-page="page"
v-model:page-size="size"
:total="total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
@change="loadData"
/>
</div>
<el-dialog v-model="dialogVisible" :title="editId ? '编辑患者' : '新增患者'" width="500px">
<el-form :model="form" label-width="90px">
<el-form-item label="姓名"><el-input v-model="form.name" /></el-form-item>
<el-form-item label="性别">
<el-select v-model="form.gender" style="width:100%">
<el-option label="男" value="男" /><el-option label="女" value="女" /><el-option label="未知" value="未知" />
</el-select>
</el-form-item>
<el-form-item label="年龄"><el-input-number v-model="form.age" :min="0" :max="120" style="width:100%" /></el-form-item>
<el-form-item label="城市"><el-input v-model="form.city" /></el-form-item>
<el-form-item label="疾病类型">
<el-select v-model="form.disease_id" style="width:100%">
<el-option v-for="d in diseases" :key="d.id" :label="d.name" :value="d.id" />
</el-select>
</el-form-item>
<el-form-item label="联系电话"><el-input v-model="form.phone" /></el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
/**
* 患者管理页面
*/
import { ref, reactive, onMounted } from 'vue'
import request from '@/utils/request'
import { formatDateTime } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
const tableData = ref([])
const diseases = ref([])
const page = ref(1)
const size = ref(10)
const total = ref(0)
const keyword = ref('')
const dialogVisible = ref(false)
const editId = ref(null)
const form = reactive({ name: '', gender: '未知', age: 0, city: '', disease_id: null, phone: '' })
async function loadData() {
const res = await request.get('/patient/list', { params: { page: page.value, size: size.value, keyword: keyword.value || undefined } })
tableData.value = res.data.items
total.value = res.data.total
}
async function loadDiseases() {
const res = await request.get('/patient/diseases')
diseases.value = res.data
}
function openDialog(row) {
editId.value = row?.id || null
form.name = row?.name || ''
form.gender = row?.gender || '未知'
form.age = row?.age || 0
form.city = row?.city || ''
form.disease_id = row?.disease_id || null
form.phone = row?.phone || ''
dialogVisible.value = true
}
async function handleSave() {
if (editId.value) {
await request.put(`/patient/update/${editId.value}`, form)
} else {
await request.post('/patient/create', form)
}
ElMessage.success('保存成功')
dialogVisible.value = false
loadData()
}
async function handleDelete(id) {
await ElMessageBox.confirm('确定删除该患者?', '提示', { type: 'warning' })
await request.delete(`/patient/delete/${id}`)
ElMessage.success('删除成功')
loadData()
}
onMounted(() => { loadData(); loadDiseases() })
</script>


43万+

被折叠的 条评论
为什么被折叠?



