☰
SpringBoot+Vue智慧养老系统:健康数据闭环设计与实战
2026/10/27 6:59:13 网站建设 项目流程

简介:本资源是一套面向计算机专业本科生的Java毕业设计实战项目,基于Spring Boot后端与Vue前端构建,聚焦智慧社区居家养老健康服务场景,解决老年人健康管理、医养协同与家属联动等现实需求。压缩包共878个文件,含128个Java核心业务类、69个Vue组件页面、158个JS交互逻辑及162个SVG图标资源,辅以MySQL建表SQL、YML配置、HTML模板与CSS样式文件,完整覆盖前后端分离开发全链路;包体大小39.68MB,结构清晰,便于按模块(如老人管理、医生预约、档案系统)快速定位学习。已有140人下载学习,提供可直接运行的代码工程、配套毕业论文文档及多级目录组织规范,包含build/run/install三阶段批处理脚本,显著降低环境搭建门槛,适合毕设选题参考、全栈开发实践与养老信息化课题拓展。

1. 这不是又一个“管理后台”:SpringBoot + Vue 做智慧社区居家养老系统,为什么必须把健康数据流闭环跑通?

你手头这个「基于 SpringBoot 的智慧社区居家养老健康管理系统」毕业设计,绝不是套个 AdminLTE 模板、增删改查几个表就能交差的项目。它的真实战场在:老人晨起血压异常却无人知晓、跌倒报警信号卡在网关没进数据库、家属App里看到的血糖值比实际晚了4小时、社区护士翻着Excel表格手动核对巡访记录……这些不是玄学,是健康数据链路上真实存在的断点。本系统要解决的,是让血压计、智能手环、跌倒检测垫、用药提醒盒这些硬件产生的原始数据,经过 SpringBoot 后端清洗、校验、分级告警,再通过 Vue 前端实现家属端实时看板、护理端工单派发、社区端统计报表——核心不在“有页面”,而在“数据从设备到决策者手里不丢、不错、不慢”。适合正在做毕设、想拿高分又怕答辩被问“你这系统真能用吗”的同学;也适合刚入职想快速吃透 SpringBoot+Vue 全栈数据流的同学。别急着 clone 代码,先搞懂这条健康数据链怎么立住。


2. 后端选型不是堆技术:为什么用 SpringBoot 而不是 Spring Cloud,MyBatis-Plus 怎么避免养老数据的“脏读”

2.1 为什么放弃微服务,坚持单体 SpringBoot?——养老场景的三个硬约束

很多同学一上来就想上 Nacos + Gateway + Feign,结果毕设答辩被问:“你这个系统部署在社区服务中心一台 4C8G 的旧服务器上,跑得动 5 个微服务实例吗?” 真实场景里,智慧社区养老系统往往部署在区级政务云或本地机房,资源有限、运维能力弱、升级频率低。SpringBoot 单体架构在这里反而是更稳的选择:

  • 部署极简:打包成一个healthcare.jar,java -jar healthcare.jar --spring.profiles.active=prod一行命令启动,运维人员不用记docker-compose up -d里哪几个服务要先拉起来;
  • 事务强一致:老人建档、健康档案创建、首次评估报告生成,这三个操作必须原子性完成。微服务跨库事务靠 Saga 或 TCC 太重,而 SpringBoot + MyBatis-Plus 的@Transactional直接搞定;
  • 调试可追溯:当家属App显示“心率异常”但后台日志没记录时,单体应用里 grep 一条 traceId 就能串起 Controller → Service → Mapper 全链路,不用在 Zipkin 里跳 3 个服务面板。

提示:如果你学校明确要求“体现微服务思想”,可在后期用 SpringBoot 的@ConditionalOnProperty做模块开关(如health-service.enabled=true),为未来拆分留接口,但毕设阶段不实际启用。

2.2 MyBatis-Plus 不是“自动 CRUD 工具”,而是养老数据的“校验守门员”

养老数据最怕什么?不是字段空,而是逻辑错:比如blood_pressure_systolic=190(高压190)却标记为health_status='normal';fall_detection_time是未来时间;medication_reminder_time比elderly_birth_date还早……这些脏数据进库,前端图表再漂亮也是毒图。MyBatis-Plus 的@TableField和自定义MetaObjectHandler是第一道防线:

// 实体类 HealthRecord.java @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; // 自定义填充处理器:确保血压值在合理范围(50~260 mmHg) @Component public class HealthDataMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { Object systolic = getFieldValByName("bloodPressureSystolic", metaObject); if (systolic instanceof Integer && ((Integer) systolic) > 260) { throw new IllegalArgumentException("收缩压不能超过260mmHg,请检查设备校准"); } // 其他字段校验... this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 赋值 } }

这段代码的意义在于:它把业务规则(血压阈值)写死在实体层,而不是等前端传过来再校验。哪怕有人绕过 Vue 前端直接调/api/record/save接口,也会在 MyBatis-Plus 插入前被拦住。这是毕业设计里最容易被忽略、但答辩时最能体现“工程思维”的细节。

2.3 数据库设计:别只建 user、admin、record 三张表,养老系统的 4 类核心实体关系

很多同学的 ER 图只有 3 张表,结果答辩被问:“老人用药提醒怎么关联到具体药品?巡访记录怎么知道是哪个护士执行的?跌倒事件怎么和定位坐标绑定?” 正确的最小核心实体应包含:

实体名关键字段关系说明为什么必须存在
elderly(老人)id, name, id_card, birth_date, emergency_contact, community_id主表,其他表外键引用社区管理基础单元,所有健康行为归属主体
health_record(健康记录)id, elderly_id, record_type(血压/血糖/心率), value, unit, record_time, device_id一对多(一个老人多条记录)原始数据载体,record_type 用枚举而非字符串,防脏数据
medication_plan(用药计划)id, elderly_id, drug_name, dosage, frequency, start_time, end_time一对多支持定时提醒,frequency 字段存DAILY/BIWEEKLY/AS_NEEDED枚举
nursing_visit(护理巡访)id, elderly_id, nurse_id, visit_time, status(已执行/已取消), remark一对多关联nurse表,status 必须有业务状态机,不能只用 0/1

注意:device_id字段不要存设备 MAC 地址(易伪造),而应存设备注册时由后端生成的唯一device_code(如DEV-2024-00123),并在device表中维护设备型号、所属老人、最后在线时间。这样当某台血压计数据异常时,能快速定位是否设备故障而非老人身体问题。


3. 前端不是“套模板”:Vue 如何把养老数据变成家属看得懂、护士用得顺的界面

3.1 Vue 项目结构:为什么必须拆出health-monitor和nursing-task两个独立模块?

很多同学用 Vue CLI 一键生成后,所有组件全塞在src/views下:ElderlyList.vue、RecordForm.vue、ReportChart.vue……结果答辩时被问:“家属和社区护士登录后看到的首页完全不同,你是怎么隔离权限和路由的?” 正确做法是按角色域拆分:

src/ ├── views/ │ ├── health-monitor/ # 家属端:健康看板、预警推送、历史趋势 │ │ ├── Dashboard.vue # 首页:今日异常指标卡片 + 折线图 │ │ ├── AlertList.vue # 实时告警列表(带一键拨打紧急联系人) │ │ └── TrendChart.vue # ECharts 封装组件,支持按周/月切换 │ └── nursing-task/ # 护士端:工单池、巡访打卡、健康评估 │ ├── TaskPool.vue # 待处理工单(含优先级颜色标识) │ ├── VisitForm.vue # 巡访表单(带 GPS 定位按钮、照片上传) │ └── Assessment.vue # 标准化评估量表(ADL、MMSE 等) ├── router/index.js # 路由守卫按角色加载不同模块 └── store/modules/ # 状态管理按模块划分

关键在router/index.js的动态导入:

// 动态路由:根据用户角色加载对应模块 const routes = [ { path: '/dashboard', component: () => import('@/views/health-monitor/Dashboard.vue'), meta: { roles: ['family'] } // 只有家属角色能访问 }, { path: '/task-pool', component: () => import('@/views/nursing-task/TaskPool.vue'), meta: { roles: ['nurse', 'admin'] } } ] // 路由守卫:登录后根据 token 中的 role 字段跳转 router.beforeEach((to, from, next) => { const role = localStorage.getItem('userRole') if (to.meta.roles && !to.meta.roles.includes(role)) { next('/403') // 权限不足 } else { next() } })

这种结构让答辩时你能清晰回答:“家属看不到护士的工单池,护士打不开家属的血糖趋势图——不是靠前端隐藏按钮,而是路由层彻底隔离。”

3.2 健康数据可视化:ECharts 不是贴代码,而是用dataset解耦数据与配置

同学常犯的错误:把 ECharts 配置写死在options里,改个颜色都要重写整个对象。养老系统里,家属可能要看“近7天血压趋势”,护士要看“本月各楼栋跌倒事件分布”,同一图表组件需复用。正确做法是用dataset分离数据源与视觉配置:

<!-- TrendChart.vue --> <template> <div ref="chartRef" style="width: 100%; height: 400px;"></div> </template> <script> import * as echarts from 'echarts' export default { props: { // 数据源:由父组件传入,格式统一为 { date: '2024-05-01', systolic: 138, diastolic: 82 } records: { type: Array, required: true }, chartType: { type: String, default: 'line' // 'line' or 'bar' } }, mounted() { this.initChart() }, methods: { initChart() { const chart = echarts.init(this.$refs.chartRef) const option = { dataset: { source: this.records.map(r => [r.date, r.systolic, r.diastolic]) // 数据驱动 }, xAxis: { type: 'category' }, yAxis: { type: 'value' }, series: [ { type: this.chartType, encode: { x: 0, y: 1 }, name: '收缩压' }, { type: this.chartType, encode: { x: 0, y: 2 }, name: '舒张压' } ], tooltip: { trigger: 'axis' } } chart.setOption(option) } } } </script>

这样,父组件只需传records数组,图表自动适配——数据变了,图表不用改一行配置。答辩时演示“切换查看血糖/血压/心率”,只需换数据源,评委立刻明白你理解了数据驱动的本质。

3.3 实时告警推送:用 WebSocket 而不是轮询,但必须加心跳保活

家属端需要“血压超标立刻弹窗”,如果用setInterval(() => axios.get('/api/alerts'), 3000),每秒3次请求,100个家属同时在线就是300 QPS,SpringBoot 默认 Tomcat 线程池直接打满。必须用 WebSocket:

// SpringBoot 后端:WebSocket 配置 @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(alertWebSocketHandler(), "/ws/alert") .setAllowedOrigins("*"); // 毕设可放开,生产需指定域名 } } // 告警处理器:按老人ID分组推送 @Component public class AlertWebSocketHandler extends TextWebSocketHandler { private final Map<String, Set<WebSocketSession>> sessions = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { String elderlyId = session.getUri().getQuery().split("=")[1]; // /ws/alert?elderlyId=1001 sessions.computeIfAbsent(elderlyId, k -> ConcurrentHashMap.newKeySet()).add(session); } // 业务层调用此方法推送告警 public void sendAlert(String elderlyId, String alertMsg) { Set<WebSocketSession> sessionsForElderly = sessions.get(elderlyId); if (sessionsForElderly != null) { sessionsForElderly.forEach(session -> { try { session.sendMessage(new TextMessage(alertMsg)); } catch (IOException e) { sessionsForElderly.remove(session); // 连接断开则清理 } }); } } }
// Vue 前端:建立连接并监听 export default { data() { return { ws: null, elderlyId: '1001' // 从路由参数或 Vuex 获取 } }, mounted() { this.initWebSocket() }, methods: { initWebSocket() { this.ws = new WebSocket(`ws://localhost:8080/ws/alert?elderlyId=${this.elderlyId}`) this.ws.onmessage = (event) => { const alert = JSON.parse(event.data) this.$message.warning(`⚠️ ${alert.title}: ${alert.content}`) // Element UI 提示 } // 心跳保活:每30秒发一次ping,防止Nginx超时断连 setInterval(() => { if (this.ws.readyState === WebSocket.OPEN) { this.ws.send('ping') } }, 30000) } } }

注意:WebSocket 在 SpringBoot 中默认使用 Tomcat 内置容器,无需额外引入 Netty。但务必加心跳机制——否则 Nginx 默认 60 秒无活动就断连,家属端会突然收不到告警。


4. 数据打通才是灵魂:SpringBoot 如何把硬件上报、人工录入、第三方平台数据统一归一化

4.1 硬件数据接入:不是“接收 JSON”,而是构建设备协议解析中间件

毕业设计里常写@PostMapping("/device/data")直接接收设备 POST,但真实设备协议五花八门:有的用 HTTP POST 发{ "devId":"A123", "type":"bp", "data":"138/82" },有的用 MQTT 上报topic: device/A123/bp payload: {"s":138,"d":82},还有的走 Modbus TCP……直接在 Controller 层写 if-else 判断type,代码会迅速腐烂。正确做法是抽象协议解析器:

// 协议解析器接口 public interface DeviceProtocolParser { DeviceData parse(String rawPayload, String deviceId); } // HTTP 协议解析器实现 @Component("httpProtocolParser") public class HttpProtocolParser implements DeviceProtocolParser { @Override public DeviceData parse(String rawPayload, String deviceId) { JSONObject json = JSON.parseObject(rawPayload); DeviceData data = new DeviceData(); data.setDeviceId(deviceId); data.setDataType(json.getString("type")); // bp, glucose, heart_rate switch (json.getString("type")) { case "bp": String[] bp = json.getString("data").split("/"); data.setValue1(Integer.parseInt(bp[0])); // 收缩压 data.setValue2(Integer.parseInt(bp[1])); // 舒张压 break; case "glucose": data.setValue1(json.getInteger("value")); // 血糖值 break; } return data; } } // Controller 统一入口 @PostMapping("/device/data") public Result<?> receiveDeviceData(@RequestBody String payload, @RequestHeader("X-Device-ID") String deviceId, @RequestHeader("X-Protocol") String protocol) { DeviceProtocolParser parser = parserMap.get(protocol); // 从 Spring 容器取对应解析器 DeviceData parsed = parser.parse(payload, deviceId); healthRecordService.save(parsed); // 统一存入健康记录表 return Result.success(); }

这样,新增一种设备协议(如蓝牙手环的 BLE GATT 协议),只需新增一个@Component实现类,Controller 一行不动。答辩时展示“我们预留了协议扩展点”,比“我写了3种设备解析”更有架构感。

4.2 人工录入数据校验:用 JSR-303 注解 + 自定义 ConstraintValidator 做业务级验证

家属在 App 里手动录入老人“今日食欲:尚可”,护士录入“巡访结论:轻度认知障碍”,这类文本数据不能只用@NotBlank,必须结合业务规则。例如“认知评估得分”必须在 0~30 之间,“营养评分”必须是预设枚举:

// 自定义枚举约束 @Target({ FIELD }) @Retention(RUNTIME) @Constraint(validatedBy = NutritionScoreValidator.class) public @interface ValidNutritionScore { String message() default "营养评分必须是 LOW/MEDIUM/HIGH"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } // 对应验证器 public class NutritionScoreValidator implements ConstraintValidator<ValidNutritionScore, String> { private static final Set<String> VALID_SCORES = Set.of("LOW", "MEDIUM", "HIGH"); @Override public boolean isValid(String value, ConstraintValidatorContext context) { return value != null && VALID_SCORES.contains(value.toUpperCase()); } } // 实体类使用 public class NursingVisit { @ValidNutritionScore private String nutritionScore; // 前端传 "low" 或 "LOW" 都能通过 @Min(value = 0, message = "认知评估得分不能小于0") @Max(value = 30, message = "认知评估得分不能大于30") private Integer cognitiveScore; }

提示:@Valid注解要加在 Controller 方法参数上,且 SpringBoot 需开启验证@Validated,否则注解不生效。这是毕设里最容易漏掉、导致“明明写了校验却没用”的坑。

4.3 第三方平台对接:用 FeignClient 封装卫健委健康档案查询接口(模拟)

很多学校要求“对接区域健康平台”,但实际没有测试环境。你可以用 Feign 模拟调用,重点展示对接思路:

// 定义远程服务接口 @FeignClient(name = "health-platform", url = "https://mock-api.health.gov.cn") public interface HealthPlatformClient { @GetMapping("/v1/elderly/{idCard}/records") List<HealthRecordDto> getRecordsByCard(@PathVariable("idCard") String idCard); } // 业务层调用(带降级) @Service public class ElderlyService { @Autowired private HealthPlatformClient platformClient; public List<HealthRecord> getExternalRecords(String idCard) { try { List<HealthRecordDto> dtos = platformClient.getRecordsByCard(idCard); return dtos.stream().map(this::convertToEntity).collect(Collectors.toList()); } catch (FeignException e) { log.warn("调取卫健委平台失败,返回空列表", e); return Collections.emptyList(); // 降级策略 } } }

关键点在于:你展示了标准对接姿势(Feign)、容错处理(try-catch 降级)、数据转换(DTO→Entity),而不是写个RestTemplate硬编码 URL。评委知道这是可落地的方案。


5. 避坑指南:答辩老师最爱问的 4 个致命问题,以及血泪换来的解决方案

5.1 现象:Vue 打包后静态资源 404,尤其favicon.ico和index.html路径错乱

原因:Vue CLI 默认打包到dist/目录,但 SpringBoot 的static目录要求资源放在static/下,且index.html必须在根路径。若直接把dist/整个复制到src/main/resources/static/,会导致index.html里引用的js/app.xxx.js路径为/js/app.xxx.js,而 SpringBoot 默认静态资源路径是/static/。
解决:修改vue.config.js,让打包输出路径匹配 SpringBoot:

// vue.config.js module.exports = { outputDir: '../backend/src/main/resources/static', // 直接输出到 SpringBoot 的 static 目录 publicPath: './', // 相对路径,使 js/css 引用为 ./js/app.xxx.js assetsDir: 'js' // 静态资源放 js/ 目录下,避免和 static/ 根目录混杂 }

然后npm run build,生成的文件自动落到 SpringBoot 项目里,mvn clean package打包后healthcare.jar内部static/结构即为可用。

5.2 现象:SpringBoot 启动报错Failed to configure a DataSource,但项目根本不用数据库

原因:SpringBoot 2.0+ 默认启用DataSourceAutoConfiguration,只要 classpath 有spring-jdbc或mybatis-spring-boot-starter,就会尝试初始化数据源。而你的application.yml里没配spring.datasource.*,就炸了。
解决:在@SpringBootApplication注解中排除自动配置:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class }) public class HealthcareApplication { public static void main(String[] args) { SpringApplication.run(HealthcareApplication.class, args); } }

注意:如果用了 MyBatis-Plus,必须保留DataSourceAutoConfiguration,此时必须配spring.datasource.url(哪怕只是 H2 内存库用于开发)。

5.3 现象:Vue 路由刷新后 404,比如访问/dashboard直接报错

原因:Vue Router 默认用history模式,依赖 HTML5 History API,但 SpringBoot 内嵌 Tomcat 不知道/dashboard是前端路由,会当成后端接口去匹配,找不到就 404。
解决:在 SpringBoot 中添加一个兜底控制器,把所有非 API 请求都指向index.html:

@Controller public class SpaController { @GetMapping({"/{path:^(?!api|actuator|swagger|webjars).*$}/**", "/"}) public String redirect() { return "forward:/index.html"; // 所有非 /api 开头的请求都返回 index.html } }

这样/dashboard、/task-pool刷新时,SpringBoot 返回index.html,Vue Router 再接管路由渲染。

5.4 现象:家属端看到的血压数据比护士端慢 2 小时,时间戳全乱了

原因:设备上报用的是设备本地时间(如老人家里手机时区为 GMT+8),后端服务器时区却是 UTC,LocalDateTime.now()存库后变成 UTC 时间,前端再用new Date()渲染,就差 8 小时。
解决:全链路统一用 UTC 存储,前端按浏览器时区显示:

  • SpringBoot 配置application.yml:
    spring: jackson: time-zone: GMT+0 # 序列化 JSON 时用 UTC
  • MyBatis-Plus 实体类用@TableField(fill = FieldFill.INSERT)填充LocalDateTime.now(ZoneId.of("UTC"));
  • Vue 中显示时用dayjs(record.recordTime).local().format('YYYY-MM-DD HH:mm'),自动转成本地时区。
    这样无论设备在哪、服务器在哪,数据时间基准统一,显示准确。

6. 让答辩老师眼前一亮的 3 个实战技巧:从“能跑”到“真能用”的最后一公里

6.1 把“健康预警”做成可配置规则引擎,而不是写死 if-else

答辩时如果只说“血压>180就告警”,老师会问:“那糖尿病老人血糖控制目标是 4.4~7.0 mmol/L,高血压老人是 130/80 mmHg,你怎么区分?” 真正的做法是把预警规则抽成数据库配置:

CREATE TABLE alert_rule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, elderly_type VARCHAR(20) NOT NULL COMMENT '老人类型:HYPERTENSION/DIABETES/ALZHEIMER', metric VARCHAR(30) NOT NULL COMMENT '指标:blood_pressure_systolic, blood_glucose', operator VARCHAR(10) NOT NULL COMMENT '操作符:GT/LT/GTE/LTE/BETWEEN', threshold_value1 DECIMAL(10,2), threshold_value2 DECIMAL(10,2), severity TINYINT NOT NULL COMMENT '严重等级:1-低危 2-中危 3-高危', enabled BOOLEAN DEFAULT TRUE ); -- 示例数据:高血压老人收缩压 >140 mmHg 触发中危告警 INSERT INTO alert_rule VALUES (1, 'HYPERTENSION', 'blood_pressure_systolic', 'GT', 140.00, NULL, 2, TRUE);

后端用策略模式加载规则:

@Service public class AlertRuleService { @Autowired private AlertRuleMapper ruleMapper; public List<AlertRule> getRulesByElderlyType(String elderlyType) { return ruleMapper.selectList(new QueryWrapper<AlertRule>().eq("elderly_type", elderlyType).eq("enabled", true)); } } // 告警触发逻辑 public void checkAndAlert(HealthRecord record) { List<AlertRule> rules = alertRuleService.getRulesByElderlyType(record.getElderlyType()); for (AlertRule rule : rules) { if (rule.match(record)) { // match() 方法封装 GT/LT/BETWEEN 判断逻辑 alertSender.send(record.getElderlyId(), rule.getSeverity(), rule.getMessage()); } } }

这样,新增一种老人类型(如“术后康复”),只需在数据库插几条规则,代码零改动。答辩时打开数据库截图,比讲一百行 if-else 更有说服力。

6.2 用 Actuator + Prometheus 暴露关键指标,证明系统“可运维”

毕设常被质疑:“你这系统上线后怎么知道它是不是在跑?” 加入 SpringBoot Actuator,暴露健康检查、内存、HTTP 调用量:

<!-- pom.xml --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
# application.yml management: endpoints: web: exposure: include: health,metrics,prometheus,loggers endpoint: health: show-details: when_authorized

然后用 Prometheus 抓取/actuator/prometheus端点(毕设可本地运行docker run -p 9090:9090 prom/prometheus),配置简单规则:

# prometheus.yml scrape_configs: - job_name: 'healthcare' static_configs: - targets: ['host.docker.internal:8080'] # SpringBoot 应用地址

在 Prometheus Web UI 输入http_server_requests_seconds_count{uri="/api/record/save"},就能看到每秒健康记录上报量。答辩时截图展示“过去1小时接口 QPS 稳定在 12,无 5xx 错误”,直接证明系统健壮性。

6.3 给论文加一个“真实数据模拟器”,让演示不再假大空

同学演示总用INSERT INTO elderly VALUES (1,'张三','110101195001011234',...),评委一眼看出是编的。用 Java Faker 库生成逼真测试数据:

// 测试数据生成工具类 @Component public class TestDataGenerator { private final Faker faker = new Faker(new Locale("zh-CN")); public Elderly generateElderly() { Elderly e = new Elderly(); e.setName(faker.name().fullName()); e.setIdCard(faker.idNumber().valid()); // 生成符合中国身份证规则的号码 e.setBirthDate(faker.date().birthday(70, 90)); // 70~90岁 e.setEmergencyContact(faker.phoneNumber().phoneNumber()); return e; } public HealthRecord generateBloodPressureRecord(Long elderlyId) { HealthRecord r = new HealthRecord(); r.setElderlyId(elderlyId); r.setRecordType("blood_pressure"); r.setValue1(faker.number().numberBetween(110, 160)); // 收缩压 r.setValue2(faker.number().numberBetween(60, 100)); // 舒张压 r.setRecordTime(faker.date().past(7, TimeUnit.DAYS)); // 过去7天内 return r; } }

在@PostConstruct方法里批量插入:

@PostConstruct public void initTestData() { if (profile.equals("dev")) { for (int i = 0; i < 50; i++) { Elderly e = testDataGenerator.generateElderly(); elderlyService.save(e); for (int j = 0; j < 30; j++) { HealthRecord r = testDataGenerator.generateBloodPressureRecord(e.getId()); healthRecordService.save(r); } } } }

这样启动项目后,自动生成 50 位老人、每人 30 条血压记录,家属端看板、护士工单池全是真实分布的数据——演示时打开图表,评委看到的是“某栋楼老人血压异常率 12.3%”,而不是“共100条数据,其中12条异常”。这种细节,让答辩老师觉得你真的想过“这系统要是真上线,会是什么样”。

我带过十几届毕设,见过太多同学卡在“功能做完但讲不出价值”。其实就一句话:别让代码只是代码,让它成为解决真实问题的证据链——设备协议解析是证据,规则引擎是证据,Actuator 指标是证据,Faker 数据是证据。答辩不是考试,是向老师证明:你写的每一行,都踩在养老场景的泥巴里。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询