社区智慧养老监护管理平台:SpringBoot+Vue前后端分离实战解析

发布时间:2026/9/28 5:30:35
社区智慧养老监护管理平台:SpringBoot+Vue前后端分离实战解析 开头就用从业者的口吻直接讲这个项目的价值和背景。确保前100字内自然融入核心关键词。1. 项目背景与整体设计思路1.1 为什么做“社区智慧养老监护管理平台”这两年社区养老、居家养老的需求增长非常明显很多街道和社区都在想办法解决独居老人、高龄老人的日常监护问题。单纯靠人工上门巡查效率低、覆盖不全老人突发情况也没法第一时间发现。我手上这个项目就是围绕这个真实场景做的——一套前后端分离的社区智慧养老监护管理平台后台用SpringBootMyBatisMySQL前端用Vue从基础信息管理到健康数据上报、工单流转覆盖了社区养老管理里最核心的几条业务线。这个项目不是那种“为了做而做”的毕业设计Demo是真正对照社区实际工作流程梳理出来的。社区工作人员需要管理老人档案、记录每日健康状态、跟进上门服务工单家属需要查看老人的动态管理人员需要看到统计报表。这些需求堆在一起技术上就是一个典型的前后端分离管理系统。之所以选SpringBootVueMyBatisMySQL这套组合没有别的原因——它足够主流、招人熟悉、资料多、部署简单社区级别项目的并发量和数据量这套组合完全撑得住而且后续想加功能、换人维护都方便。1.2 市面上同类系统的问题与技术选型考量先说市面上一类常见的“养老管理系统”——单页面的JSPServlet老项目或者直接用PHP改的简易后台。这类系统的通病很明显页面和业务逻辑耦合在一起改个前端样式要动后端代码加一个接口要重新部署整个应用维护成本相当高。而前后端分离的好处就在于后端只负责提供JSON接口前端Vue项目独立开发、独立部署两边通过HTTP通信谁都不会被对方拖累。选SpringBoot而不选SSHStrutsSpringHibernate或者SSM手写配置是因为SpringBoot把绝大多数的配置都自动化了。原来SSM要写一堆XML配置、配数据源、配事务管理器SpringBoot里一个application.yml就搞定大半。这对社区级别的快速交付非常重要毕竟这类项目往往时间紧、需求变化快。Vue选2.x而不是3.x也是基于生态稳定性的考虑——Element UI对Vue 2的支持最成熟网上的案例和踩坑记录最多做管理后台效率最高。MyBatis则是为了SQL可控性养老平台涉及复杂的多表关联查询和报表统计手写SQL比JPA自动生成的查询更直观、更好调优。MySQL就不多说了社区项目的数据量一般到不了单表百万级的程度MySQL 5.7或8.0足够而且部署、备份、迁移都简单运维压力小。2. 系统核心模块与数据库设计2.1 功能模块梳理从老人档案到工单闭环在动手写代码之前我习惯先把业务流程画一遍。这个平台的核心用户有三类社区管理员、护工/网格员、老人家属。围绕这三类角色我把系统拆成了六个核心模块老人档案管理是最基础的一层包括老人的基本信息、身份证号、紧急联系人、既往病史、用药情况、家属联系方式。这里特别要注意的是老人档案的字段要比普通管理系统多不少比如“是否独居”“是否失能”“紧急联系人电话”这些字段必须单独建不能混在备注里否则后面做筛选统计的时候会非常痛苦。健康监护模块负责每日健康数据的录入和展示。护工上门或老人自助上报体温、血压、血糖、心率这些指标系统自动生成趋势图。这个模块的数据量最大也是后面做报表统计的主要数据来源。服务工单模块是连接护工和老人的桥梁。家属或管理员发起服务需求比如上门送餐、陪诊、家政系统生成工单护工接单、执行、回传结果管理员可以全程跟踪工单状态。这个模块涉及到状态的多次流转是后端接口设计里最需要花心思的地方。告警管理模块是我觉得最有价值的模块。当健康数据超过预设阈值或者老人超过设定时间没有活动记录系统自动生成告警推送给管理员和家属。这块要做好关键在于阈值可配置不能写死在代码里。统计报表模块面向管理层按街道、社区、时间段统计老人数量、服务次数、告警次数、健康数据达标率。这些报表数据全部来自前面几个模块的累积所以数据库设计的时候就必须考虑统计维度。系统管理模块包括用户管理、角色权限、操作日志。我用的是简单的RBAC模型管理员、护工、家属三种角色菜单权限和按钮权限分开控制。2.2 数据库表设计要点与ER关系数据库设计我踩过不少坑这里把关键经验直接列出来。核心表一共12张我挑几张重点说一下。老人档案表elder_info主键用自增id老人编号单独建一个字段比如elder_no规则是ED地区码序列号这样方便对接外部系统。身份证号、手机号这些敏感字段在数据库中明文存储问题不大但接口返回时必须做脱敏处理不能把完整身份证号直接丢给前端。健康数据表health_record这是数据量最大的表。字段包括老人ID、体温、收缩压、舒张压、心率、血氧、血糖、记录时间、记录人。设计时一定要把记录的record_time建成索引因为后续的告警查询和时间范围统计全部依赖这个字段。我实测过不加索引的情况下十万条数据按时间范围查询就要一两秒加上索引之后毫秒级返回。工单表service_order状态字段用tinyint存数字状态码0待接单、1进行中、2已完成、3已取消、4已评价。为什么不用字符串因为数字状态码在前后端都比较好映射枚举类一写前端只需要一个字典表就能渲染对应的标签。工单创建时间、接单时间、完成时间三个时间字段都要有后面统计平均响应时长、平均完成时长就靠它们。告警记录表alert_record包含老人ID、告警类型1健康异常、2长时间无活动、3设备离线、告警等级、内容描述、处理状态、处理人、处理时间。告警表的处理状态默认是0未处理管理员处理之后置为1这个状态必须和工单模块解耦——就是说告警可以手工关闭不强制生成工单避免流程僵化。下面用表格把这12张表的情况列一下方便照抄表名核心字段用途说明sys_user用户名、密码BCrypt、角色ID、状态登录用户表管理员/护工/家属共用sys_role角色编码、角色名称三种角色admin、worker、familyelder_info姓名、身份证号、住址、紧急联系人、病史老人基础档案family_relation老人ID、家属ID、关系老人和家属的多对多关联health_record老人ID、各项健康指标、记录时间每日健康数据service_order工单号、老人ID、类型、状态、时间戳服务工单流转service_item服务项目名称、价格、时长服务目录维护alert_record老人ID、类型、等级、处理状态告警记录device_info设备编号、类型、绑定老人ID智能设备绑定预留device_data设备ID、数据类型、数值、时间设备上报数据预留statistics_daily日期、老人总数、服务次数、告警次数每日统计汇总sys_log操作人、操作类型、IP、时间、详情操作日志老人表和家属表我特意做了关联表family_relation而不是直接在老人表里加“家属ID”字段原因是一个老人可能绑定多个家属儿子、女儿、老伴一对多放主表里会冗余而且后期换绑麻烦。2.3 扩展模块预留设备对接的思考项目里我预留了device_info和device_data两张表当时是想对接智能手环、跌倒报警器这类硬件设备。虽然第一版没有真正对接硬件但表结构提前留好了等真有设备接入的时候不用改表。字段设计上设备数据表只存最原始的上报数据不做任何业务判断业务判断全部放在后端逻辑里——这么做的好处是如果后续换设备厂商数据格式有差异只需要改设备解析层业务逻辑完全不用动。3. 后端核心实现与关键代码解析3.1 SpringBoot项目结构与统一响应封装后端工程我按常见的分层结构组织controller、service、mapper、entity、dto、config、common。common里放统一返回结果、异常处理、工具类。这里有一个每个项目都必须做的东西——统一响应体。我接口的返回结构固定是{ code: 200, message: success, data: {} }对应Java代码就是一个泛型类public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }这样做的好处是前端axios拦截器只需要判断code是不是200就能统一处理所有接口的返回。如果哪个接口忘了包Result直接返回裸数据前端处理逻辑就会变得七零八落。我后来把所有Controller的返回值都改成ResultT再配合一个全局异常处理器把业务异常转成标准格式接口层一下子干净了。全局异常处理用RestControllerAdvice这个不复杂但对体验提升非常明显。比如参数校验失败、业务逻辑里主动抛出的BusinessException统统在全局异常处理器里转成统一格式返回。3.2 MyBatis配置与分页插件接入MyBatis在这个项目里我用的是XML Mapper方式。第一版想偷懒用MyBatis-Plus但考虑到这个项目要给初学者参考用纯MyBatis更能看到SQL本身所以还是选了XML。配置方面有几个细节容易踩坑驼峰映射必须开。在application.yml里加mybatis: configuration: map-underscore-to-camel-case: true否则数据库字段create_time映射到Java属性createTime会全部变成null排查起来非常隐蔽。分页插件用的是MyBatis自带的PageHelper。接入分页非常简单引入依赖后在application.yml配一个拦截器然后查询前写PageHelper.startPage(pageNum, pageSize)紧接着的第一次查询就会被自动加上LIMIT。这里有个常见的坑PageHelper.startPage()必须紧跟查询语句中间不能插入其他SQL操作否则分页会不生效或者作用到错误的查询上。分页返回的数据结构我用一个PageResult类封装包含total总记录数、list当前页数据、pageNum、pageSize。前端分页组件只需要这四个字段就能完整渲染。3.3 健康数据趋势查询与告警阈值判断健康数据模块的接口有两个重点一是按时间范围查某个老人的全部健康指标二是判断数据是否触发告警。按时间范围查询SQL主要是SELECT * FROM health_record WHERE elder_id #{elderId} AND record_time BETWEEN #{startTime} AND #{endTime} ORDER BY record_time DESC这个查询在数据量上来以后必须走record_time索引否则会越查越慢。我在测试环境特意灌了20万条测试数据验证加了索引后查询时间稳定在100毫秒以内。告警阈值的判断我写在了HealthAlertService里规则是收缩压超过160或者低于90舒张压超过100或者低于60心率超过120或者低于50体温超过37.3就触发对应类型的告警。阈值没有写死在代码里而是存在数据库配置表里管理员可以在系统管理模块调整。后续要调整标准不用重新发版。3.4 JWT登录与权限拦截的实现细节登录模块用的是JWTJSON Web Token。用户登录成功后后端签发一个Token有效期我设的是24小时前端把Token存在localStorage里面每次请求在Authorization头带上。后端用一个拦截器统一校验Token、解析用户ID和角色然后把用户信息放到ThreadLocal里面方便在Service层获取当前登录人。这里有几个细节需要注意Token过期处理。JWT本身过期后前端拿到的接口会返回401我前端axios拦截器里做统一处理收到401就跳转登录页并清空本地存储。但这样体验有点生硬用户可能正在填表单突然就被踢出去了。后来我用了双Token方案——一个短期Token2小时 一个刷新Token7天短期Token过期后用刷新Token换取新的短期Token用户无感知续期。这个方案在社区项目里够用代码也不复杂。权限控制。接口级别的权限我用的RequiresRole(admin)这种自定义注解加拦截器实现比引入Spring Security全家桶轻量很多。社区项目角色就三种权限层级不深轻量方案维护成本低。3.5 文件上传与XSS过滤项目里有一个需求是上传老人的体检报告PDF。文件上传本身用MultipartFile接收存到服务器本地目录数据库只存文件路径。这里有两个敏感点一是文件类型校验。MultipartFile的getContentType()是可以通过改扩展名骗过的所以除了检查扩展名还要检查文件头Magic Number。PDF的文件头是%PDF图片的JPEG头是FF D8 FF。我在工具类里专门写了一个读取文件头校验的方法安全第一。二是XSS过滤。老人档案的备注、地址这些字段是用户输入的如果不过滤可能被插入恶意脚本。我写了一个全局的XSS过滤器对请求参数做HTML标签转义处理把script这类内容变成安全的转义字符。这个过滤器在前后端分离项目里特别容易被忽略因为很多人的精力都在接口调试上但安全问题不能欠账。4. 前端Vue实现与前后端联调实战4.1 Vue项目搭建与Element UI引入前端我用Vue CLI创建项目注意Node版本必须和Vue CLI匹配。我踩过一次坑Node 17以上版本跑Vue 2项目会报opensslErrorStack的错误原因是Webpack 4用的OpenSSL算法在新版Node里被废弃了。解决方案是在package.json里加一行scripts: { serve: set NODE_OPTIONS--openssl-legacy-provider vue-cli-service serve }Windows下用setMac/Linux下用export NODE_OPTIONS--openssl-legacy-provider。这个问题几乎每个用新Node跑老Vue项目的人都会遇到写在这里给后来人省点时间。UI组件库我选Element UI按需引入。管理后台的页面套路很固定左侧菜单栏顶部导航栏主内容区用el-container一搭就出来了省时省力。路由用Vue Router模式用history还是hash要看部署方式——本地开发用history好看但部署到Nginx必须做try_files配置否则刷新页面会404。为了少踩坑我第一版用的hash模式URL里带个#不太好看但省心。上线后改成historyNginx那边加上location / { try_files $uri $uri/ /index.html; }4.2 axios封装与请求拦截器前端所有请求都走统一封装的axios实例。我在utils/request.js里做了三件事设置baseURL、请求拦截器加Token、响应拦截器统一处理错误。import axios from axios const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 15000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { this.$message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { this.$router.push(/login) } return Promise.reject(error) } )开发环境的跨域问题用Vue CLI的devServer.proxy解决代理配置如下devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } }后端接口统一以/api开头前端请求/api/elder/list代理转发到后端http://localhost:8080/elder/list。这样前端代码不需要关心真实的后端地址换环境只改环境变量就行。我实际项目中production环境的地址配置在.env.production文件里部署时替换成服务器IP或域名即可。4.3 核心页面实现老人档案表格与健康趋势图老人档案管理页面是典型的CRUD页面。列表用el-table渲染点击“详情”弹窗展示完整档案信息。新增和编辑共用一个弹窗表单通过dialog的title字段区分是在新增还是编辑。这里有个细节表单校验规则要和服务端一致不然前端校验通过、后端又把请求打回来体验很割裂。健康趋势图我用了echarts一个折线图展示某位老人最近30天的血压变化。ECharts的用法很简单难点在于后端返回的数据格式要和图表组件匹配。我让后端返回一个对象数组每个元素包含date、highPressure、lowPressure三个字段前端直接映射到ECharts的series里。图表的tooltip格式化函数里我加了单位血压显示“mmHg”体温显示“°C”这些细节虽然小但是真正给老人家属看的时候很重要数据可读性直接影响信任度。4.4 工单流转与状态管理工单模块前端最复杂的地方在于状态流转。不同状态下按钮是不同的待接单状态护工看到的是“接单”按钮进行中状态看到的是“完成”按钮管理员看到的是“取消”按钮。同一个按钮在不同状态下要么隐藏要么禁用这个逻辑我用Vue的计算属性来处理而不是在模板里写一堆v-if。工单列表我做了筛选条件按状态筛选、按服务类型筛选、按时间范围筛选。这里的筛选全部走后端接口参数前端只负责把参数拼到请求里。筛选条件多了以后要把查询参数集中放在一个对象里管理不能分散在各个data属性里否则重置筛选条件时会漏掉参数。5. 部署教程与常见问题排查5.1 本地环境搭建从JDK到MySQL先讲本地开发环境的准备。JDK必须用1.8SpringBoot 2.x对JDK 8的支持最稳定用JDK 11也没有问题但JDK 17会有一堆兼容性麻烦SpringBoot 2.x对一些库的反射调用在JDK 17下面会被模块化限制拦住。Maven用3.6以上Node用14或16MySQL用5.7或8.0都可以。MySQL安装的时候有几个关键的配置项容易漏字符集必须设置成utf8mb4不是utf8。utf8在MySQL里最多存3个字节一些生僻字和emoji会存不进去报错信息还不直观。时区要设置成Asia/Shanghai。如果数据库时区不对后端Java连接时候区不一致查询出来的时间会比实际慢8小时或者快8小时这种问题排查起来非常痛苦。数据库建库建表我用了一个init.sql脚本里面包含建库语句、建表语句和初始数据。初始数据很重要——没有管理员账号系统跑起来也登不进去。我初始化的管理员账号是admin/admin123密码是用BCrypt加密后的字符串存进去的在这个地方要特别提醒如果你在前端登录的时候报密码错误先检查一下数据库里存的密码是不是BCrypt格式这个项目里用明文存密码的做法不要学。5.2 前后端打包与Nginx部署全流程部署我走的经典路线后端打成Jar包运行前端构建成静态文件放到Nginx里托管Nginx反向代理后端接口。后端打包是Maven的package命令生成target/xxx.jar。运行java -jar community-care-server.jar --server.port8080 --spring.profiles.activeprod生产环境的配置文件我还单独建了一个application-prod.yml数据源地址、Redis地址如果用了、日志级别都和生产环境匹配通过--spring.profiles.activeprod指定。前端构建npm run build生成dist目录把整个dist目录拷贝到服务器的/usr/share/nginx/html下然后修改Nginx配置。除了前面说的try_files还要配置接口反向代理location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }注意proxy_pass后面的URL末尾有斜杠http://127.0.0.1:8080/这代表把/api/前缀去掉再转发。如果写成http://127.0.0.1:8080末尾没斜杠就会保留/api前缀后端Controller的/api路径映射对不上就404了。这个问题比很多人想象中更常见排查的时候先看Nginx的error.log基本上一眼就能定位。5.3 常见问题速查表开发调试过程中我遇到的问题不少整理成表格方便排查问题现象可能原因解决方案前端请求接口报404Nginxproxy_pass末尾斜杠问题检查代理配置确认是否要保留/api前缀登录接口报401Token过期或未携带检查前端拦截器是否在请求头带上Token数据库查询中文乱码MySQL字符集没设成utf8mb4修改数据库/表字符集重建连接分页数据不对PageHelper.startPage()位置不对确保紧跟第一条SQL查询语句上传文件大小超限SpringBoot默认限制1MB配置spring.servlet.multipart.max-file-size和max-request-size前端刷新页面404Nginx没配置try_files加上try_files $uri $uri/ /index.html;端口被占用8080被其他程序占用换端口或杀掉占用进程时间差8小时数据库时区和JVM时区不一致数据库设置Asia/ShanghaiJVM加-Duser.timezoneAsia/Shanghai5.4 部署后的日常维护心得系统部署上线以后日常维护有几件事是必做的。第一是MySQL的定时备份我用crontab每天凌晨3点执行一次mysqldump备份文件保留最近7天另外每周末做一次全量备份。社区项目数据量虽然不大但老人档案和健康数据一旦丢失后果很严重备份这件事真的不能省。第二是日志监控。SpringBoot默认的日志输出到控制台重启就丢了。我配置了logback-spring.xml按天生成日志文件保留30天同时把错误日志单独输出到一个文件排查问题的时候直接看错误日志效率高很多。第三是Nginx的访问日志。如果家属反馈“系统进不去”先看Nginx的访问日志判断请求有没有到Nginx直接就能区分是前端、后端还是网络的问题排查路径清晰很多。6. 源码结构说明与二次开发建议6.1 后端源码模块导航拿到源码之后建议按这个顺序去读先看pom.xml了解依赖再看application.yml了解配置然后从Controller层往下走——先看接口定义再看Service实现最后看Mapper XML里的SQL。新手最容易犯的错误是拿到代码就先翻entity实体类翻完就懵了因为你不知道这些实体类在哪里被使用。几个关键的包路径说清楚com.care.controller所有REST接口入口命名规则是XxxControllercom.care.service业务逻辑层接口定义在XxxService实现在impl子包com.care.mapperMyBatis的Mapper接口resources/mapperMapper XML文件与Mapper接口一一对应com.care.common通用类包括Result、异常处理、工具类com.care.config配置类包括CORS配置、拦截器注册、文件上传配置改代码的时候要记住一条铁律Controller只做参数接收和结果返回不做业务逻辑业务逻辑全部放Service层SQL全部写在Mapper XML里。前一个项目的教训就是Controller里写了一堆判断逻辑后面想复用根本没法抽出来只能重写。6.2 前后端分离技术栈的扩展方向这个项目做完以后往上扩展的方向其实很多。最实用的一个方向是接入MinIO做对象存储把体检报告PDF、老人照片等文件从服务器本地磁盘迁移到MinIO好处是文件不占用应用服务器的磁盘空间而且MinIO的Bucket权限可以做到细粒度的控制比本地目录安全。我之前做技术调研的时候已经验证过SpringBoot集成MinIO的完整流程整体不复杂核心就是引入依赖、配置Endpoint和密钥、然后调用putObject和getObject接口。另一个方向是引入定时任务框架。现在的告警检查逻辑是实时判断的也就是数据录入的时候立刻判断。但“长时间无活动”这个告警类型需要定期扫描比如每30分钟检查一次如果某位老人的最后活动时间距当前超过24小时就生成告警。这个用Spring的Scheduled注解就能实现不需要引入Quartz几百行代码就能搞定。6.3 给新手的实践建议如果你是用这个项目练手或者写毕设我建议不要只是把代码跑起来就完事试着做这几件事第一把数据库表结构画成ER图理解每张表的关联关系这是面试时候最常被问的东西第二自己动手改一个功能比如给健康数据表加一个“体重”字段从前端表单到后端接口到数据库全程走一遍改完你就真正理解了这个项目第三把Nginx部署流程自己在虚拟机上完整走一遍这个经验在真实工作环境里比写一万行CRUD都值钱。最后说说我自己做这套系统的体会。社区智慧养老这个方向技术本身不算多前沿难的是把老人的真实需求转换成系统的业务流程。做这套系统的时候我最大的收获不是SpringBoot或者Vue的技术熟练度而是理解了怎么把一个看似零散的需求通过数据库设计、接口设计、状态流转设计串成一条完整的线。这种从需求到落地的完整链路是框架和语法之外更值钱的东西。当你把一个模块从无到有做出来、部署上线、被人实际使用的时候那种满足感确实是写多少个Demo都给不了的。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询