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

发布时间:2026/9/28 12:19:32
社区智慧养老监护平台:SpringBoot+Vue前后端分离实战 做社区智慧养老监护管理平台系统最忌讳的就是把它当成一个普通的后台管理系统去写。老人档案、健康数据、护工排班、家属通知、紧急告警看起来都是增删改查但真正落地的时候你会发现这里面的角色权限流转、告警状态机、数据时序关系任何一个环节没理清楚后面全是要返工的重灾区。这套SpringBootVueMyBatisMySQL前后端分离的完整项目是我在社区级养老场景下逐步打磨出来的。它不只是简单地把老人信息做个列表维护而是把“监护”这个核心动作拆成了可落地的业务闭环设备采集健康数据、系统自动生成异常告警、护工接单处理、家属同步收到消息通知。文章里我会从数据库设计、后端接口实现、前端页面对接一直讲到本地启动部署和那些绕不开的坑代码和配置直接可以抄作业如果你是刚接触前后端分离项目的新手这套流程也足够带你完整走一遍真实项目的落地路径。1. 项目核心智慧养老监护管理平台到底在解决什么问题1.1 场景痛点与系统定位社区智慧养老和机构养老最大的区别在于“分散”二字。老人不住在同一栋楼里而是分布在社区各个小区、各个楼栋有的白天由家属照护、晚上独居。传统养老机构的集中式看护模式根本套不上去。所以做这套平台系统定位很清晰以社区为网格、以老人档案为主线、以健康监护为核心、以告警联勤为纽带。先说痛点。社区养老场景下最常见的几个问题老人基础信息散落在纸质档案或Excel里换一个网格员就得重新交接一遍数据对不上。老人在家里的健康状况完全是黑盒没有设备接入、没有数据采集家属只能靠打电话确认。遇到紧急情况跌倒、心率异常、长时间未活动老人可能无法主动呼救等发现时往往已经错过最佳处置时间。护工上门服务缺乏记录和反馈服务做没做、做得好不好管理层无法量化考核。这套系统要解决的就是上面这几件事。技术形态上选前后端分离原因也很实际管理后台需要给社区工作人员、护工、老人家属分别使用有的在PC端操作有的更习惯在手机浏览器里访问前后端分离后用同一套后端接口支撑多个前端入口后续再做小程序、App也方便不用重新写业务逻辑。1.2 角色权限与业务模块设计先看角色设计。这套系统里我划分了四种角色每种角色看到的界面和能操作的接口是严格区分的角色核心职责主要操作权限管理员系统配置与全面监管老人档案管理、护工排班、数据总览、角色账号管理护工上门照护执行查看照护任务、录入上门记录、处理告警、查看老人健康数据家属远程关注老人状态查看老人健康数据、接收告警通知、反馈意见老人本人使用终端设备查看个人健康报告、主动发起求助这里有个容易踩的坑很多人在做前后端分离项目时权限控制只做了“页面隐藏”也就是根据角色去控制菜单显示但后端的接口没有做对应的拦截校验。这在真实系统里是很危险的因为接口一旦暴露任何人都可以直接调接口拿到隐私数据。我在这套项目里用的是Spring Boot拦截器加自定义注解的方式在Controller层做二次校验后面会详细说实现。业务模块上按功能域划分成六个模块档案中心老人基础信息、家属绑定、社区网格归属、既往病史。健康监护心率、血压、血氧、体温等数据的实时展示与历史趋势图。告警中心异常数据触发告警、老人主动求助、告警处理流转。照护任务护工排班、上门任务派发、服务记录回传。消息中心告警通知、任务提醒、系统公告。数据看板社区老人总数、今日健康数据、待处理告警、设备在线率等统计。这么拆分的逻辑是每个模块都对应一个真实的业务角色和一套完整的业务流程数据有来有回、状态有流转而不是孤立的CRUD页面。做完整项目练手或者直接改造成商用项目的时候这个骨架都是经得起推敲的。你在复现这套项目时建议按这个模块顺序去理解代码不要一上来就扎进某个页面的细节里先建立全局地图再逐个点位突破效率会高很多。2. 前后端分离架构设计与技术选型解析2.1 为什么是这套技术栈SpringBootVueMyBatisMySQL的选型逻辑选型这件事我不想说得太玄就一句话这套组合在国内社区养老类中小型项目中是投入产出比最稳的。后端SpringBoot生态成熟、上手成本低最关键的是它对周边组件的包容度非常高后面要接消息推送、定时任务、文件存储都有现成的starter能用前端Vue组件化开发对后台管理系统这类强表单、强表格的场景非常友好Element UI这类组件库基本把管理页面80%的重复工作包了MyBatis做数据层SQL是自己写的复杂联表查询和统计报表可控性比全自动ORM强太多MySQL做存储社区级项目的数据量根本达不到性能瓶颈单库单表完全够用运维成本也很低。数据库选型这里多说一句。我见过不少人在MySQL和PostgreSQL之间纠结半天其实没必要。MySQL在Windows和Linux上都有非常成熟的图形化工具链和安装教程排查问题的时候搜索引擎一搜一大把对新手极其友好。这套项目里我用的是MySQL 5.7如果你用8.0除了连接驱动和认证方式需要注意Navicat连接8.0经常报caching_sha2_password相关错误业务SQL基本可以无缝迁移。至于有人问要不要换MyBatis-Plus我理解这个趋势但在这套项目里用原生MyBatis是有意为之的因为MyBatis的XML映射文件能让你清楚地看到每一条SQL理解分页插件、缓存、动态SQL这些底层机制是怎么回事。用MyBatis-Plus写起来确实快但如果你连原生XML都没写过直接用Plus很容易变成“只知道调用方法、不懂SQL”的状态对长期成长不利。2.2 前端工程结构与核心依赖前端工程我用的Vue CLI创建整体结构如下frontend/ ├── public/ ├── src/ │ ├── api/ # 所有后端接口请求的统一封装 │ ├── assets/ # 静态资源 │ ├── components/ # 公共组件 │ ├── router/ # 路由配置 │ ├── store/ # 全局状态管理 │ ├── views/ # 页面按模块目录组织 │ ├── utils/ # 工具函数request.js等 │ ├── App.vue │ └── main.js ├── package.json └── vue.config.js依赖选型上核心的三个axiosHTTP请求、vue-router路由、element-ui组件库。状态管理这版我用的是Vuex虽然现在新项目用Pinia更多但这套项目要兼容Vue 2生态Vuex是最稳的选择。还有一个特别容易被忽视的依赖是给图表用的echarts健康数据趋势图、数据看板里的统计图表都要靠它记得在package.json里加上不要等到页面开发到一半才发现没装。前端和后端的联调方式我强烈建议在vue.config.js里配置devServer代理而不是在前端代码里直接写后端IP端口。理由很简单后端接口地址写死在axios里每次环境切换都要改代码重新打包用代理的话前端所有请求都走相对路径/apidevServer自动转发到后端服务器部署阶段再把同样的规则搬到Nginx里配置一次到处复用。组件的设计上我的经验是不要把页面级的逻辑写进公共组件里。表格、弹窗、搜索表单这些做成通用组件没问题但具体到某个业务的接口调用全部放到views层或者api层。这样做的直接好处是当某个页面的业务逻辑变了你只需要改对应的views页面不会动到公共组件而影响其他页面。2.3 后端分层架构与MyBatis关键配置后端的包结构我按社区项目通用的分层来组织src/main/java/com/community/care/ ├── common/ # 通用返回结果、异常处理、工具类 ├── config/ # 配置类跨域、拦截器、分页插件 ├── controller/ # 接口层 ├── service/ # 业务层接口实现 ├── mapper/ # MyBatis映射接口 ├── entity/ # 数据库实体 └── utils/ # 工具类分层的原则是“controller只管收参数、service只管业务、mapper只管SQL”。很多新手喜欢把业务逻辑直接写在Controller里图省事结果接口一多、逻辑一复杂代码就开始不可控。尤其是智慧养老这类系统告警处理、任务派发这种多个数据表联动的操作如果不走Service层做事务控制出现一半成功一半失败的数据不一致问题排查起来非常痛苦。MyBatis在Spring Boot里接入主要有两个关键配置需要理解清楚。第一个是分页插件。后台管理页面几乎每个列表都要分页手写LIMIT还要自count很烦人。我用的是PageHelper配置方式很简单在配置类里加一个Bean即可Bean public PageInterceptor pageInterceptor() { PageInterceptor interceptor new PageInterceptor(); Properties props new Properties(); props.setProperty(helperDialect, mysql); props.setProperty(reasonable, true); interceptor.setProperties(props); return interceptor; }reasonable设为true的好处是当前端传过来的页码小于1或者超过总页数时PageHelper会自动修正到第一页或最后一页不会直接返回空数据。这一点在列表页面的体验上很重要搜索结果翻到最后一页删除几条数据后刷新页码超界是常态。第二个是MyBatis的二级缓存。默认情况下MyBatis一级缓存是SqlSession级别的在Spring整合的环境下实际上作用有限。二级缓存要启用需要满足三个条件在Mapper XML里加cache标签、实体类实现Serializable接口、全局配置cache-enabledtrue。但这里我必须提醒社区养老系统的健康数据和告警数据是强实时、强一致性要求的缓存会导致查询结果滞后我在这套项目里是关闭二级缓存的。缓存这个东西不是配置了就能提升性能要看业务场景用错地方反而制造诡异bug。3. 核心功能实操从数据库表到接口再到页面的完整链路3.1 数据库表结构设计与关键字段说明数据库设计是一个项目的地基我先给出这套系统的核心表结构和设计思路。完整的SQL脚本在项目db目录下这里摘出最关键的三张表来讲。-- 用户表 CREATE TABLE sys_user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(100) NOT NULL COMMENT BCrypt加密密码, role varchar(20) NOT NULL COMMENT 角色ADMIN/CAREGIVER/FAMILY/ELDER, real_name varchar(50) DEFAULT NULL, phone varchar(20) DEFAULT NULL, status tinyint(1) DEFAULT 1 COMMENT 账号状态, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;用户表是整个系统的权限基础。密码字段我用的BCrypt加密存储不要用明文。这一点看似基础但真实项目里真的见过不少密码直接存数据库的一旦数据泄露就是塌方事故。角色字段用字符串枚举而不是单独的关联表社区级系统角色就四种枚举字符串在代码里可读性更高查列表时少一次join。-- 老人档案表 CREATE TABLE elder_info ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) DEFAULT NULL COMMENT 绑定登录账号, name varchar(50) NOT NULL, gender tinyint(1) DEFAULT NULL COMMENT 1男 0女, birthday date DEFAULT NULL, address varchar(200) DEFAULT NULL COMMENT 详细住址, community_id int(11) DEFAULT NULL COMMENT 所属社区网格, emergency_contact varchar(50) DEFAULT NULL COMMENT 紧急联系人, emergency_phone varchar(20) DEFAULT NULL, health_status varchar(200) DEFAULT NULL COMMENT 既往病史、健康备注, room_no varchar(20) DEFAULT NULL COMMENT 楼栋房号, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;老人档案表里我特别加了emergency_contact和emergency_phone这是养老场景的业务刚需紧急联系人字段在告警联动时会被直接调用。健康数据表的设计一定要注意数据粒度。我见过有人把心率、血压、血氧拆成三张表然后把设备实时上报的数据按秒记录一个月不到单表就是上千万行。社区级智慧养老项目的合理粒度是分钟级而且要按老人维度做数据冗余设计查询的时候才不用天天大表join。-- 健康数据表 CREATE TABLE health_record ( id bigint(20) NOT NULL AUTO_INCREMENT, elder_id int(11) NOT NULL, heart_rate int(11) DEFAULT NULL COMMENT 心率, blood_pressure_high int(11) DEFAULT NULL COMMENT 收缩压, blood_pressure_low int(11) DEFAULT NULL COMMENT 舒张压, blood_oxygen int(11) DEFAULT NULL COMMENT 血氧饱和度, temperature decimal(4,1) DEFAULT NULL COMMENT 体温, record_time datetime NOT NULL COMMENT 采集时间, PRIMARY KEY (id), KEY idx_elder_time (elder_id, record_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;联合索引idx_elder_time这个设计是故意的查询老人某段时间内的健康数据是最高频的SQL联合索引让这类查询直接走索引覆盖性能会好很多。新人最容易犯的错是只把主键当成索引等数据量上来后接口响应从几十毫秒变成几秒才知道索引的重要性。3.2 后端核心接口实现登录鉴权、健康上报、告警联动先看登录鉴权。前后端分离项目的鉴权我用的方案是JWT Token配合Spring Boot拦截器做全局校验。流程是用户提交用户名密码后端校验成功后返回一个签名后的token前端把token存到localStorage里后续每个请求在axios拦截器里自动带上Authorization请求头。JWT的工具类核心代码public String generateToken(User user) { return Jwts.builder() .setSubject(user.getUsername()) .claim(role, user.getRole()) .claim(userId, user.getId()) .setExpiration(new Date(System.currentTimeMillis() 3600_000 * 24)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }拦截器校验就是取请求头里的token用同一个secretKey去解析解析失败直接返回401。这里要特别提醒一个安全细节token的过期时间不能设置太长24小时是我的底线社区养老系统涉及老人隐私数据token一旦泄露被利用的后果很严重。如果怕用户反复登录体验差可以后续升级refresh token机制但不能为了体验牺牲安全。再看告警联动这个核心业务。当健康数据出现异常比如心率超过140或低于50时系统要自动生成一条告警记录同时把告警推送给家属和护工。这段业务逻辑看起来不长但里面有几个关键设计要考虑清楚Service public class AlarmServiceImpl implements AlarmService { Override Transactional(rollbackFor Exception.class) public void handleAbnormalHealth(HealthRecord record) { // 1. 判断是否异常 if (!isAbnormal(record)) { return; } // 2. 生成告警记录 Alarm alarm new Alarm(); alarm.setElderId(record.getElderId()); alarm.setAlarmType(determineAlarmType(record)); alarm.setAlarmTime(new Date()); alarm.setStatus(PENDING); alarmMapper.insert(alarm); // 3. 发送通知先落库再异步推送 notifyService.notifyFamilyAndCaregiver(alarm); } }注意步骤2和3之间我加了Transactional。为什么要事务因为生成告警记录和通知发送是两个步骤如果告警记录写库成功、通知推送却因为网络问题失败事务回滚后两个操作都会撤销避免出现“告警里有记录但家属没收到”这种最要命的数据不一致。异步推送的通知建议走Spring的Async注解或者消息队列不要让用户请求线程死等第三方推送接口返回。前端页面对接时健康数据页我用的是Echarts的折线图接口返回按时间排序的数据数组前端直接映射成serie。这里有个实践技巧后端接口返回时间字段建议格式化好再给前端比如统一返回yyyy-MM-dd HH:mm:ss格式的字符串不要把Date对象直接序列化因为Java的Date序列化成什么格式受jackson配置影响前后端各管一段容易把时间格式搞乱。3.3 前端页面与接口对接的实操细节前端的核心页面有三块列表页、详情页、数据看板。我先说列表页的通用套路。后台管理系统的列表页几乎都是“表格搜索条件分页”三件套。Element UI的el-table配合el-pagination再加上搜索表单一个页面的骨架就出来了。关键在axios封装我统一在request.js里做了三件事请求拦截器带头token、响应拦截器统一处理错误码、超时时间设为15秒。import axios from axios; const service axios.create({ baseURL: /api, timeout: 15000 }); service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] token; } return config; }); service.interceptors.response.use( response { const res response.data; if (res.code ! 200) { Message.error(res.msg || 请求失败); return Promise.reject(new Error(res.msg)); } return res; }, error { if (error.response error.response.status 401) { router.push(/login); } Message.error(网络异常请重试); return Promise.reject(error); } );这一层封装是前后端分离项目的命脉。没有这层统一处理每个页面都要自己写错误提示、自己判断token过期代码量翻倍还容易漏。我看到很多新手项目把axios直接用得飞起看到报错只能打开控制台看半天问题就出在这。vue-router的路由配置上有两个细节要注意。导航守卫里我判断了token存在才允许进入首页否则重定向到登录页同时用路由的meta字段标记页面所需角色在守卫里校验。另外一个实操细节是菜单栏选中的高亮状态要和当前路由绑定用el-menu的default-active绑定$route.path即可不要自己维护一个复杂的激活状态对象省心又不会出错。数据看板页是这套系统里最能体现“前后端分离”优势的部分。后端只负责返回统计数据比如今日健康状况、告警趋势、设备在线率前端用Echarts渲染成图表某一块图表要调整样式时只需要改前端代码后端接口纹丝不动。联调阶段这样的解耦能节约大量沟通成本。4. 从零到一本地部署与项目启动全流程4.1 环境准备与版本选择部署这套系统前先把环境理清楚。我的建议版本组合如下组件推荐版本说明JDK1.8 或 11项目用的是Spring Boot 2.xJDK 8完全够用不要一上来就用JDK 17Maven3.6后端依赖管理Node.js14.x 或 16.x前端构建太新的版本反而有兼容问题MySQL5.7 或 8.0需要注意8.0的认证插件问题Nginx1.18生产环境静态资源托管与反向代理这里为什么会把版本单独拿出来说因为这个项目的“部署教程”环节90%的新手卡在环境上。大部分人不是被代码难倒的而是JDK装成最新的17、Node装成20.x、MySQL装成8.0还开着默认认证三样东西叠在一起报错信息根本对不上号。做技术选型的时候就要有版本克制意识够用就好不要追新。MySQL安装这里多说一句。Windows上安装MySQL 5.7建议用zip包解压方式而不是MSI安装包zip方式干净、路径可控、出问题容易清理。解压后在my.ini里配置basedir和datadir然后以管理员身份执行mysqld --initialize-insecure初始化再执行mysqld --install注册Windows服务最后net start mysql启动。整个过程大概10分钟比MSI向导那种黑盒安装更好排查。如果你是在Linux服务器上部署用包管理器安装也行只是记得装完执行systemctl enable mysqld否则服务器重启后MySQL不会自动拉起。4.2 后端启动步骤与配置细节后端项目的启动核心在application.yml的配置。我先说数据库连接这部分spring: datasource: url: jdbc:mysql://localhost:3306/community_care?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8serverTimezoneAsia/Shanghai这个参数是必填的MySQL 8.0之后如果URL里不指定服务器时区启动连接的时候直接报时区错误。这也是为什么我前面反复强调“部署过程要把配置细节讲透”一个时区参数就能卡掉一半的人。jackson的date-format和time-zone也建议一起配上保证后端返回给前端的时间字符串格式统一。初始化数据库的步骤也很简单。用Navicat或者命令行执行项目提供的SQL脚本mysql -uroot -p db/community_care.sql脚本里我包含了建库、建表、初始账号数据。执行完可以检查一下sys_user表里是否已经有数据默认管理员账号是admin密码是admin123这边提醒你登录后第一时间改密码。后端启动命令在项目根目录执行mvn clean package -DskipTests java -jar target/community-care-1.0.0.jar第一次打包Maven会下载大量依赖需要几分钟是正常现象。启动成功后看到Started CommunityCareApplication的日志后端就在8080端口跑起来了。如果想指定端口在启动命令后面加--server.port9999即可或者直接在yml里配置。这里还有个生产环境常用的技巧用nohup或者systemd把jar包做成后台服务避免关闭终端进程就死掉。用systemd管理Spring Boot进程是比较规范的方案写一个service unit文件指定ExecStart为java -jar /path/to/your.jar再配置Restartalways这样进程崩溃也会自动拉起运维省心很多。4.3 前端启动步骤与代理配置前端启动分开发环境和生产环境两种。开发环境本地联调阶段在frontend目录下执行npm install npm run serve依赖安装阶段会遇到一个经典问题npm install安装到一半卡住或者安装超时。解决方案是把npm源切到国内镜像源npm config set registry https://registry.npmmirror.com这是我统一建议所有新项目的操作不是因为它多高级而是因为这个坑几乎每个人都会踩一遍不如提前解决。npm run serve启动成功后默认端口是8080访问http://localhost:8080。但这里有个冲突要注意后端jar占用了8080端口Vue开发服务器也默认8080两个不能同时占。解决方案是在vue.config.js里把前端端口改成8081同时配置代理转发module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };生产环境部署前端执行npm run build生成dist目录。这一步很多人会忽略的一个操作生产环境前端的请求地址是打死的不能像开发环境那样走devServer代理。所以我把axios的baseURL做成环境变量控制开发环境用相对路径/api走代理生产环境直接指向后端服务器域名https://your-domain.com/api。如果你的前后端最终部署在同一台服务器上我推荐用Nginx托管把dist目录做成静态站点同时把/api路径反向代理到后端端口这样一个域名的访问入口既解决跨域问题又省去了单独部署前端服务器的成本。Nginx核心配置如下server { listen 80; server_name your-domain.com; root /var/www/community-care/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }这段配置里try_files那行是前端单页应用路由模式的关键不加这一行刷新页面就会出现404。因为前端路由是history模式浏览器刷新某个子路径时Nginx先去找磁盘上有这个文件找不到就落到/index.html由前端路由接管。5. 部署与联调中的常见问题排查实录5.1 MySQL连接报错排查实录error 2002、Access denied、认证插件MySQL相关问题在整套系统的部署环节中出现的频率是最高的。我挑几个典型来说。第一个是ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock。这个问题在Mac和Linux上特别常见本质是MySQL服务根本没启动或者client连接时找不到socket文件。解决办法先用service mysql status确认服务状态如果没启动就service mysql start。如果是socket文件路径问题就在my.cnf里检查socket路径或者在连接命令里用-h 127.0.0.1 -P 3306走TCP连接而不是socket连接。第二个是Spring Boot项目启动时报Access denied for user rootlocalhost。这是典型的账号权限或者密码错误。排查思路是先确认你在命令行能正常连接MySQL排除服务本身问题再看yml里的用户名密码是否匹配。这里有个很容易忽略的点yml文件里的密码如果含有特殊字符比如#、!、建议用单引号括起来否则yaml解析会截断密码导致认证失败。第三个是MySQL 8.0的认证插件问题。用Navicat连接MySQL 8.0时提示caching_sha2_password这是因为8.0默认的认证插件是caching_sha2_password而Navicat旧版本不支持。解决办法在SQL里把用户的认证方式改成mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;如果是Linux上装的MySQL 8.0还要提醒一句数据目录初始化方式变了用mysqld --initialize会生成临时密码日志文件里查看登录后必须立刻ALTER USER改掉否则后续所有连接都进不来。5.2 前后端联调中的跨域与请求问题前后端分离项目跨域问题几乎是绕不开的。先说一个最容易迷惑人的点开发环境配了devServer代理后前端页面看到的是相对路径请求网络请求头里的Host是localhost:8081前端实际的服务器地址是localhost:8080后端这种情况下浏览器根本不会有跨域报错因为请求是同源的——浏览器只看到前端自己发出的同源请求代理转发发生在开发服务器内部。但如果你的部署方案是前端静态资源放在一个域名、后端接口放在另一个端口或者开发时前端直接访问http://localhost:8080/api/...这种跨源请求那就必须要后端开启CORS。后端配置跨域用Spring Boot的全局配置即可Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }配置完成后如果前端请求还是报跨域我建议先清理一下浏览器缓存因为跨域判断有时候会受浏览器缓存影响。另外记住一点加了这个配置后如果同时还在后端用拦截器校验token一定要在拦截器里放行OPTIONS预检请求否则浏览器预检直接404跨域问题依旧。联调时还有个高频问题前端请求不到后端打开控制台看到404或者500。404一般是路径不对500一般是后端代码报错。这时候别急着改代码先在浏览器控制台看一下请求实际发到了哪个地址再用Postman直接在后端端口试一遍很快就定位是前端代理没配好还是后端接口本身就报错。我经常见人浪费一上午在改代码最后发现是代理target写错了一个字符。5.3 MyBatis与业务逻辑的常见坑MyBatis这块我遇到并解决过的问题里有三个最值得拿出来分享。第一个是XML映射文件中resultType和resultMap的使用混乱。单表查询用resultType最简单字段名和实体类属性名一一对应记得在Spring Boot配置里开启map-underscore-to-camel-case: true这样数据库下划线字段自动映射到驼峰属性。但是多表联查、特别是查询结果里包含统计字段的时候一定要用resultMap显式映射。我在健康数据统计接口里就吃过这个亏联表查出两个重名字段resultType直接不知道映射给谁返回的全是null。第二个是分页插件PageHelper的使用方式问题。PageHelper.startPage(pageNum, pageSize)之后必须紧跟第一个查询方法中间不能插入其他查询或耗时操作。有些人写了startPage后还去查了别的表分页结果就错乱到别的查询上了。排错方法把PageHelper.startPage和target查询连在一起写中间不夹任何东西包括日志输出和循环。第三个是事务的生效范围。Transactional默认只对RuntimeException和Error回滚对checked exception不回滚。健康上报接口里如果写了try-catch把异常吞掉事务是不会回滚的。我自己习惯的写法是在catch里主动抛出RuntimeException这样才能保证整个告警流程的一致性和完整性。这套项目里还有个容易踩的地方是时间查询。健康数据按时间段过滤的接口前端传过来的是字符串后端直接拿字符串去和datetime字段比较很多时候会因为格式不匹配查不到数据。我在Service层强制做了一步转换把前端传的yyyy-MM-dd转成当天的00:00:00到23:59:59的LocalDateTime范围再传给Mapper这样无论前端传的是日期还是时间查询逻辑都稳。另外如果你有反编译已有SpringBoot项目源码的需求我的经验是用IDEA打开jar包之后配合反编译插件比如FernFlower把class还原成java但这套项目本身提供了完整源码建议大家直接基于源码学习不要从反编译开始走弯路。这套项目从架构到代码我始终围绕着一个原则让数据在正确的时机、通过正确的路径到达正确的人手里。健康数据按时序存入MySQL告警在异常出现的第一时间生成并推送护工处理结果回写形成闭环家属随时能看到老人状态。智能养老听起来高大上落到系统设计上其实就是把信息流和事件流梳理得足够干净。在实现过程中我最深的体会是完整的前后端分离项目难点从来不在某个单一技术点而在于整个链路的打通。数据库建表、MyBatis映射、Spring Boot接口、Vue页面、Nginx部署任何一个环节断了整个项目就跑不起来。建议你拿到源码后不要急着全改先按文章里的部署步骤完整跑一遍确认系统能跑通、能看到数据再开始改造业务逻辑这样你踩的坑会少很多。最后留个实惠的小技巧数据库脚本里我放了演示数据包含三个角色的账号和几十条健康记录你可以在项目跑起来后直接切换账号体验不同角色的界面差异这对理解整套权限设计特别有帮助。