ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

智慧养老系统开发实战:Spring Boot+Vue从架构到部署的避坑指南

智慧养老系统开发实战:Spring Boot+Vue从架构到部署的避坑指南 养老项目做多了就会发现真正难的不是技术选型而是你永远不知道客户会在哪一天突然提出“要支持国产数据库”“要能在电视大屏上实时看到老人位置”这种需求。这套智慧养老服务平台爱老助老系统前后端加起来也就踩了小半年的坑最后沉淀下来的技术栈非常明确后端 Spring Boot 单体架构前端 Vue 全家桶中间件按业务需要接入了 MinIO、ActiveMQ、MQTT、腾讯地图等。整体选型不算新潮但胜在稳定、好招人、好维护。无论你是拿它做毕业设计还是接了一个社区养老的定制项目这篇笔记都能帮你省掉不少试错时间。我尽量不写那种文档式的东西把真正要知道的原理、配置、坑都铺开讲尤其是前后端权限校验、动态路由、jar 反编译排查这类别人不常写的内容。如果你正准备从零搭一套这样的系统建议按文章顺序把架构思路理一遍再动手。1. 项目定位与架构推演这套系统为什么必须这么设计1.1 智慧养老平台到底要解决什么问题很多初次接触养老项目的人第一反应是“这不就是个管理系统吗”实际上完全不是。爱老助老系统的核心使用角色至少有五类老人本人、老人亲属、社区社工、平台运营管理员、上门服务人员。老人可能根本不会用APP真正高频操作的是亲属和社工这意味着前端的交互既要照顾亲属端的简洁又要给社工端足够的录入效率。业务模块就更细了我这边实际落地的有老人档案、健康监测数据、工单派发、探访记录、配餐订餐、辅具租赁、志愿者管理、紧急呼叫处理。这些模块并不是堆砌功能而是围绕“居家养老 社区支撑”这条主线串起来的。老人档案是地基健康数据和工单是日常紧急呼叫和预警是保命。所以项目从一开始就不是一个普通CRUD系统它必须具备几个特点数据权限要细社工只能看自己负责的老人、移动端要有微信或企业微信里打开、物联网设备要能对接血压计、跌倒报警器、运营后台要能处理大量工单。这几点直接决定了后面技术选型和数据库设计的方向。1.2 Spring Boot Vue 的组合优势在哪里先回答一个经常被问的问题为什么不选前后端不分离的 Thymeleaf 方案或者为什么不直接上微服务我的判断基于三条第一这套系统的业务体量在一个社区甚至一个区县级别单体能覆盖第二客户后续要扩展移动端、第三方大屏必须走接口化的前后端分离Vue 负责渲染Spring Boot 只出 API两侧都可以独立升级第三招人容易Spring Boot 和 Vue 是现在国内 Java 全栈开发者最熟悉的技术栈接手成本低。为什么不用微服务坦白讲我见过太多把这种项目拆成十几个服务的做法最后全是运维负担。单体 Spring Boot 只要内部按模块分包后续要拆微服务也完全来得及。前端选 Vue 而不是 React也有很现实的理由国内社区生态、中文文档、组件库Element Plus、Vant都非常成熟做后台和移动端 H5 都能一套技术栈覆盖。1.3 模块划分与数据库模型设计要点数据库设计决定这个项目能做到什么深度。我用的单库方案MySQL 为主后来配合国产化需求适配了金仓KingBase。核心表我逐个说老人档案表elder不要只存姓名、身份证、家属电话这种基础信息一定要冗余最近一次健康数据、当前负责社工ID、紧急联系人列表否则列表页每次都要关联查询。健康数据表health_record按月分表是必要的否则一年后血压血糖记录会膨胀得很厉害。工单表service_order设计时要带状态机字段从待派单、服务中、已完成到已评价后续做流转统计就靠它。任务调度表用于定时任务记录每个老人当天该做什么服务社工端按任务列表执行。我还特意加了一张“服务网格表”community_grid把社工与小区楼栋绑定。这样做有两个好处一是派单时可以按网格就近匹配二是数据权限天然就能通过社工所属网格过滤。这套设计在演示时特别加分客户一眼就能看出系统不是玩具。1.4 整体代码目录与协作规范后端用 Maven 多模块工程按业务分包而不是按层分包。我会在 springboot 主工程下拆出 common 公共模块、system 系统管理模块、elder 老人业务模块、order 工单模块、iot 设备对接模块。按层分包controller/service/mapper在项目变大后会非常痛苦一个业务改动往往要跳五六个包去找。按模块分包后每个模块内部自带 controller、service、mapper、entity改动集中新人接手也更快。前端 Vue 工程用 Vite 初始化src 下按 views、components、api、stores、router、directives 组织。api 目录里每个业务模块对应一个 js 文件统一封装 axios 实例。我在这里特别强调团队成员要遵守命名规范接口文件命名必须和数据库表一一对应不然两周后你自己都找不到接口。2. Spring Boot 后端的核心落地细节2.1 Spring Boot 版本选择与自动装配原理这里想聊一下“springboot版本太高”这个搜索热词因为我确实被坑过。前期图新功能用了 Spring Boot 3.x结果客户现场要用 JDK 8 老版本中间件一堆兼容性问题。后来我固定用 Spring Boot 2.7.x这是最后一个兼容 JDK 8 且能稳定对接大多数中间件的版本。不是越新越好对于这种交付型项目稳定压倒一切。很多人面试背了自动装配原理但实际开发中不太明白它在干嘛。简单说Spring Boot 启动时扫描META-INF/spring.factories或AutoConfiguration.imports里的自动配置类根据条件注解ConditionalOnClass、ConditionalOnProperty决定要不要加载某个配置。比如你引入了spring-boot-starter-data-redis并且 classpath 里有 RedisTemplate自动配置类就会创建连接工厂。理解了这套机制你就知道为什么有时候加了依赖但配置没生效——多半是少了某个条件类或者 yml 里的 key 写错了。2.2 认证授权与接口安全设计我没用 Spring Security理由很简单项目时间紧张Security 的过滤器链配置和学习成本对团队来说偏高。最终选了“JWT Redis 拦截器 自定义注解”这套轻量方案实现下来效果完全不差。登录成功之后签发 token把用户信息、角色集合、权限标识存进 Redis设置过期时间。前端每次请求在 header 带上 token后端拦截器校验通过后把用户信息放入 ThreadLocal。权限控制用自定义注解RequiresPermission(elder:add)加 Spring AOP 切面实现。如果接口没权限直接抛异常由全局异常处理器返回 403。这里有一个必须注意的点token 过期时不能只返回“未登录”给前端还应该在响应体里约定一个状态码比如 401前端 axios 拦截器拿到这个状态码后统一跳转登录页并把用户状态清空。这个约定不做好就会出现“明明没登录却卡在页面里”的体验问题。2.3 业务中间件接入MinIO、ActiveMQ、Mosquitto、HanLP文件存储我没有用 FastDFS选了 MinIO。原因是 MinIO 部署轻量、支持 Docker 一键启动并且 SDK 兼容 S3 协议。上传接口先把文件传到 MinIO再把返回的 URL 存到数据库前端拿到 URL 直接预览。MinIO 接入 Spring Boot 也比较简单主要就是配置endpoint、accessKey、secretKey、bucketName这几个参数然后注入MinioClient。唯一提醒一点私有 bucket 要生成预签名 URL 给前端临时访问别直接把 bucket 设成公开读否则文件地址被爬虫拿到就麻烦了。ActiveMQ 是我用来解耦短信通知和站内信的。比如创建工单后系统要同时通知社工、家属和管理员如果全写在业务代码里一旦短信接口超时整个请求就卡住了。引入 ActiveMQ 后业务方只管发消息到队列消费者异步处理通知。选择 ActiveMQ 而不是 RocketMQ 或 Kafka也是因为部署简单一个容器就能跑对于这类中小项目足够用了。Mosquitto 用来对接物联网设备。跌倒报警器、睡眠监测垫这类硬件大多走 MQTT 协议Spring Boot 端用 Eclipse Paho 客户端订阅主题收到设备消息后解析成健康记录并触发预警。这个环节比较隐蔽的问题是消息格式不统一不同厂商设备的 payload 完全不一样必须写一个适配层做协议转换否则每接入一个设备就改一次接收逻辑。HanLP 这个库我用来做工单文本的关键词抽取。社工提交走访记录时系统自动抽取“血压偏高”“独居”“需要助浴”这类标签回写到老人档案里形成需求画像。HanLP 在 Spring Boot 里以 jar 包方式引入即可分词速度快不依赖外部服务适合私有化部署。2.4 定时任务与健康预警规则定时任务是养老系统必不可少的环节。“每天上午九点提醒老人吃药”“每周给家属推送一次健康周报”“超过三天没有探访记录的自动生成待办”这些都是定时需求。我用的是 Spring 自带的Scheduled加 Quartz原因是前者简单后者能动态管理任务。健康预警我单独讲一下因为这块的判断逻辑直接决定系统生死。血压数据不是简单超过 140/90 就预警还要结合测量时间段、连续三次趋势、是否早晨峰时段来判断。我把预警规则设计成可配置的 JSON管理员可以在后台调整预警阈值和冷静期避免半夜因为一次异常测量就给家属连发七八条短信。这种“规则可视化”的设计客户非常认可因为它不需要改代码就能适应不同老人的健康基线。2.5 国产数据库适配与金仓读写分离项目交付时客户提出“要支持国产化”于是我们把 MySQL 适配到了金仓KingBase。金仓兼容 MySQL 的大部分语法但仍有不少差异比如自增主键、分页写法、函数名称都不同。最稳妥的做法是在 DAO 层全部使用 MyBatis-Plus 的通用方法不写或少写数据库方言重的内容这样大部分 SQL 可以一次兼容两套数据库。金仓读写分离配置我当时也研究了好一阵。它和 MySQL 的主从思路有点类似在数据源配置里设置主库和从库的连接信息再通过DataSource注解指定读操作走从库。注意读写分离不是银弹强一致性的写后读场景比如保存后立刻刷新列表必须强制走主库否则会出现“刚保存的数据查不到”的诡异 Bug。2.6 用反编译手段排查线上 Jar 问题“怎么将 spring boot jar 反编译成项目”这个话题热度一直很高我在实际排查问题时也确实经常这么干。场景一般是本地运行没毛病打出的 jar 包部署到服务器就报错。很多情况是编译后代码与源码不一致或者依赖 jar 包冲突。排查时先把项目 jar 解压再用反编译工具比如 JD-GUI 或 CFR打开BOOT-INF/classes下的 class 文件确认编译出来的代码是不是你本地最新的版本。如果配置没生效直接看反编译出的自动配置类比对ConditionalOnProperty的配置前缀能快速定位问题。这个技能看起来土但在没有源码运行环境的服务器上它就是程序员最后的侦察手段。3. Vue 前端与多端适配实战3.1 Vue 项目初始化与依赖环境配置Vue 这边我踩过的坑不比后端少。第一次用 Vite 创建项目时依赖装得很顺利结果运行后发现 Node 版本太低Vite 直接罢工。后来固定给团队成员统一用 Node 18 LTS。安装依赖用 npm 或 pnpm 都可以我推荐 pnpm因为它在多项目场景下磁盘占用低、安装速度快极大避免“装了个寂寞”的错觉。vue-devtools 是必装的调试插件很多新手不知道组件状态变了但页面没反应时打开 devtools 看一眼响应式数据就明白了。Vue 3 项目推荐的写法是组合式 APIsetup 语法但我团队里有成员对选项式更熟项目里就出现了两种写法混用。短期可以接受但新代码统一用组合式 API并把可复用的逻辑抽成自定义 hook比如useElderList、useOrderForm这样各页面之间才能共享逻辑。3.2 动态路由、菜单权限与状态管理这部分的关联逻辑我单独讲清楚。后端登录接口返回的 token 之外还会返回当前用户的角色和菜单权限列表。前端拿到之后做两件事第一把菜单数据渲染成侧边栏第二用 vue-router 的addRoute动态注册当前用户可访问的路由。有一个经典 Bug 是用户登录后动态路由已经加了但一刷新页面路由全都消失了。原因很简单刷新后 Pinia或 Vuex里的用户状态被清空动态路由没有重新注册。解决办法是在路由守卫里加一个逻辑如果状态里有用户信息但没有动态路由就重新调用“获取用户信息”接口然后重新addRoute再放行到目标页面。权限这块还有一个细化点按钮级别的权限用自定义指令v-permission控制无权限直接移除按钮而不是仅仅隐藏。状态管理我用的 Pinia它相比 Vuex 更简洁天然支持 TypeScript写起来也没有那么多模板代码。用户信息、token、菜单数据、动态路由标志位这些都放在 user store 里统一管理。3.3 腾讯地图、直播播放与企业微信 JS-SDK地图是智慧养老里的刚需。我们用的是腾讯地图 JavaScript API GL主要做两件事在后台大屏展示服务人员的位置以及在社工端展示老人住址的周边服务资源。调用时先在 index.html 里引入腾讯地图 SDK然后初始化地图实例用 marker 标记点位用 polyline 画服务路径。视频能力这里也说说“vue播放m3u8”是搜索热度很高的词。养老场景里家属希望实时查看老人在社区活动室的情况这就要接视频流。m3u8 直播流在浏览器里不能直接播放前端我用 hls.js 这个库来解析播放。注意组件销毁时要手动关闭播放实例否则页面切走一路狂奔的情况很容易把内存吃满。企业微信 JS-SDK 在我们项目里用来接收老人的紧急呼叫通知。使用 CDN 方式引入wecom/jssdk时需要保证版本不低于 2.3.2否则部分接口不可用。前端注册 JS-SDK 后后端会向企业微信后台请求签名前端拿到签名后调用agentConfig注入应用权限。这里比较坑的是签名 URL 必须是当前页面完整 URL不能带多余处理否则一直报 “invalid signature” 错误。3.4 Electron 打包桌面监测端很多客户会提一个要求“能不能把这个系统装在一台电视上开机自动启动看监控大屏”这时候把 Vue 项目直接扔进浏览器并不合适最好是打包成 Electron 桌面应用。做法是把 Vue 项目构建后的 dist 目录作为 Electron 的主页面加载主进程负责创建窗口、开启本地服务、处理系统托盘操作。主进程和渲染进程之间的通信必须走 IPCipcMain/ipcRenderer比如渲染进程请求主进程读取本地配置文件、获取设备状态主进程处理完再通过webContents.send把结果传回来。这里提醒一句别在渲染进程里直接使用 Node APIElectron 的 contextBridge 就是为隔离安全设计的图省事开nodeIntegration: true会埋下安全隐患。4. 联调、部署与配置管理4.1 前端代理与接口联调前后端分离开发时最烦的是联调阶段跨域问题。我前端本地开发用的是 Vite 代理在vite.config.js里配置 proxy把/api开头的请求转发到后端地址。这样浏览器始终认为请求是同源的不需要后端配置 CORS。// vite.config.js 关键片段 export default defineConfig({ server: { host: 0.0.0.0, port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } })这里有一个细节如果后端是网关或者加了 context-path代理的 rewrite 规则要对应调整否则会出现接口 404。联调阶段后端接口还没写完的部分前端用 mock 数据兜底我用的是 vite-plugin-mock路由配置好之后前后端互不阻塞。4.2 Docker Nginx 上线流程部署我是走容器化的。后端 Spring Boot 写 Dockerfile基础镜像用 openjdk:8-jre-alpine启动命令直接java -jar app.jar。前端 Vue 项目先npm run build构建 dist然后用 Nginx 镜像承载Nginx 配置里做两个关键点一是 location /api 反向代理到后端容器二是 history 模式路由需要配置try_files $uri $uri/ /index.html否则前端页面一刷新就 404。整个项目我用 docker-compose 编排服务分为 mysql、redis、minio、activemq、mosquitto、backend、frontend 几组。这里强烈建议把所有中间件的宿主机端口映射写死别用随机端口方便排查。后端配置文件里的server.port我见过有人用${random.int[8000,9000]}的随机端口做多实例测试但在 docker-compose 里如果端口映射固定随机端口会导致容内端口和外层映射不一致直接访问失败。记住线上环境禁止随机端口。4.3 多环境配置与打包优化Spring Boot 多环境用 application-dev.yml、application-prod.yml 区分启动时通过--spring.profiles.active指定。这里最实用的经验是“配置不写死”把数据库密码、第三方密钥都放到环境变量里docker-compose 的 environment 字段注入避免配置文件泄露。前端的多环境更麻烦一点Vite 本身支持.env.development和.env.production但要注意变量名必须以VITE_开头才会被暴露到客户端。接口地址、地图密钥、SDK 的 appId 这些都要放到环境变量里管理。5. 高频问题排查与避坑指南5.1 环境相关的高频问题我把过去半年遇到的高频问题整理成速查表按出现频率排序问题现象排查思路与解决办法前端依赖装不上pnpm install 卡住或报 ERESOLVE检查 Node 版本删除 node_modules 和 lockfile 重装国内环境建议配置镜像源后端 jar 启动报端口占用8080端口无法绑定先 lsof 查进程再改配置或用随机端口临时启动数据库时区报错插入时间比当前时间早8小时jdbc url 增加 serverTimezoneAsia/Shanghai前端刷新 404Nginx 下 F5 刷新页面白屏修改 Nginx 增加 try_files 配置上传文件后预览不了MinIO 文件已上传但浏览器拒绝访问检查 bucket 权限私有桶改用预签名 URL企业微信 JS-SDK 签名失败报 invalid signature确认签名 URL 与当前页面路径完全一致重新获取 access_token 与 ticket 并缓存5.2 业务侧容易忽略的问题业务侧的问题多和权限与数据边界有关。比如社工端只能看到自己网格内的老人但列表接口如果不带网格过滤条件就会被前端请求全部数据。这种越权问题不能用前端控制必须在后端校验数据权限。健康数据展示也有讲究。老人的血压数据是一天数次如果直接拉最近 100 条折线图展示前端页面会卡顿而且图像没有参考意义。我后来在前端做了聚合展示按天取平均、只展示最近七天和高低峰值同时支持缩放到原始数据。这个优化让家属端体验提升明显。工单状态流转是一个容易做乱的地方。我的方案是后端定义一个状态机用枚举保存所有合法流转路径比如“待派单”只能到“已接单”或“已取消”“已接单”只能到“服务中”。非法状态转换直接抛异常从源头上杜绝脏数据。5.3 一些必须写在最后的经验如果你正在计划启动类似的项目有几个建议我想单独拎出来第一先设计工单状态机和数据结构再写页面不要上来就画界面。很多毕设项目死在“页面做完发现表结构撑不起功能”。第二不要迷信新版本。锁死 Spring Boot 2.7 和稳定的 Vue 3 组合你能搜到大量别人的踩坑记录别拿项目给新版本当小白鼠。第三给前端和后端都做统一的异常与响应格式约定。我之前用固定返回体{ code, message, data }所有接口通用。没有这套约定联调阶段至少要多花一倍时间。最后再分享一个小技巧项目越到后期越要重视“演示路径”。养老系统给客户演示时不要从后台管理系统讲起要从“老人触发紧急呼叫 - 社工收到通知 - 工单生成 - 家属收到进度消息”这条完整链路讲配合地图和消息推送一起展示。技术再复杂能让客户看到业务闭环项目验收才真正稳。
返回列表