ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue大健康养老公寓管理系统:从业务建模到前后端部署的完整实战

SpringBoot+Vue大健康养老公寓管理系统:从业务建模到前后端部署的完整实战 作为一名带过不少毕设、也接手过若干“半成品”项目的开发者每次看到“SpringBoot Vue 管理系统”这类组合第一反应不是“又是个老掉牙的CRUD”而是——这玩意儿做好了是真能体现一个学生的工程素养。尤其这次标题里带上了“大健康养老公寓管理系统”这就不是单纯做个增删改查交差了事了它背后牵扯到“医养结合”的业务逻辑、角色权限、健康档案、床位管理、订单流转这些东西业务复杂度上来了含金量也就上来了。这篇东西我打算讲点实在的不整那些虚头巴脑的“项目介绍”。我会从项目整体设计思路、后端SpringBoot核心模块拆分、前端Vue业务交互、SQL脚本设计要点、接口文档规范这几个维度把一套完整的大健康养老公寓管理系统到底是怎么从零搭起来的关键难点在哪怎么避坑一次性说清楚。不管你是在校生准备拿它当毕设还是刚入行的前端想搞懂Java Web项目怎么上手接活这篇都能给你点参考。1. 项目整体设计与思路拆解1.1 核心需求解析养老公寓管理系统到底在管什么先说一个很多人容易踩的坑拿到“养老公寓管理系统”这个题目第一反应就是把“老人信息表”建好搞几个增删改查页面完事了。这种项目在答辩的时候基本撑不过三分钟因为评委随便问一句“那你这个系统怎么处理老人入住退房的费用结算”你就哑火了。真正的大健康养老公寓管理系统业务核心是“医养结合”。它至少得覆盖几个模块入住管理老人入住登记、床位分配、退房办理。健康管理老人基本信息、体检记录、慢病跟踪、用药提醒。生活服务订餐、保洁、活动报名、家属探访预约。费用管理入住费用、护理费、餐饮费、额外服务费的核算与缴纳。系统管理管理员、护理员、老人家属、医生等不同角色的权限分配。这四个模块加一起才算勉强摸到了“大健康”的边。如果能力允许再往上加可以加“健康数据大屏”可视化展示公寓内老人的健康趋势、“异常报警”心率/血压异常自动推送给家属这些属于加分项但如果基础模块还没做好别急着铺开。1.2 技术选型SpringBoot Vue 为什么是这个组合技术上选SpringBoot Vue在当前这个时间点看几乎是Java Web毕设的最优解没有之一。后端用SpringBoot好处有三个。第一它内置了Tomcat不用单独部署一个jar包跑起来部署成本极低。第二SpringBoot的自动配置会让你少写大量配置文件你不需要再像SSHSpring Struts Hibernate时代那样堆一堆XML。第三SpringBoot生态太成熟了整合MyBatis-Plus、Shiro/JWT、Redis、EasyExcel这些工具都有现成starter能用。前端选Vue尤其Vue 2或Vue 3配Element UI/Element Plus是因为它学习曲线相对平缓组件化开发思路很清晰而且电商后台、管理系统这类界面90%都是表格表单弹窗图表的组合Vue这种数据驱动的框架写起来非常顺手。你要是选React对于毕设来说反而容易在状态管理上多花心思绕远路。前后端分离意味着你开发时可以开两个端口前端8080后端8081通过代理转发解决跨域。这种架构不光是当下企业的标准姿势更关键的是你答辩的时候可以正大光明地说“我把前后端完全解耦符合现代Web开发的主流实践”——这句话在很多评委那里是加分项。1.3 单体还是微服务毕设千万别上头这里我要泼一盆冷水。很多人一看“大健康”“养老公寓”这几个字脑子一热就想去上微服务搞什么注册中心、网关、配置中心、分布式事务……等你真正把Eureka Gateway Nacos Seata这一套拉起来光是本机内存就吃紧更别提你还要在里面写业务代码。毕设的规模和周期摆在那里单体应用 模块化代码结构绝对够用。你可以把代码按feature分包auth模块负责登录鉴权elder模块负责老人与健康档案room模块负责床位管理order模块负责服务订单fee模块负责费用流水。模块之间通过service层调用解耦这样即便以后想拆微服务也有个良好的基础但现阶段没必要真拆。2. 后端SpringBoot核心细节与实操要点2.1 项目目录结构别再把代码写成大杂烩后端代码结构这一块很多同学喜欢把所有类都塞进com.example.demo下面包名也是随便起的。等代码量上去了找东西找到怀疑人生。我习惯用的标准的“Controller → Service → ServiceImpl → MapperDAO → Entity”分层结构在此基础上加两层com.yanglao ├── common // 通用类统一返回结果、全局异常处理、常量定义 │ ├── Result.java │ ├── ResultCode.java │ └── GlobalExceptionHandler.java ├── config // 配置类MyBatis-Plus、CORS、拦截器、Swagger │ ├── CorsConfig.java │ ├── MybatisPlusConfig.java │ └── SwaggerConfig.java ├── controller // 接口层接收请求、参数校验、返回结果 ├── service // 业务接口 │ └── impl // 业务实现 ├── mapper // 数据访问层配合MyBatis-Plus ├── entity // 数据库实体对应表 ├── dto // 前端传输对象接收参数用的别直接用实体接收 ├── vo // 视图对象返回给前端的字段 └── utils // 工具类JWT工具、日期处理等这个结构的好处是每一层职责单一出了问题知道去哪找。比如前端传参格式不对看DTOSQL执行异常看Mapper业务逻辑不对看ServiceImpl——这种“哪里坏了修哪里”的体验能给你省下大把掉头发的机会。2.2 登录鉴权方案JWT还是Session养老管理系统涉及老人隐私数据和费用数据权限控制绝对不能没有。常见方案有两种Session配合拦截器和JWT配合Spring Security或Shiro。我建议用JWT 拦截器的方式理由很现实前后端分离项目前端可能部署在一台服务器后端在另一台Session在跨域场景下维护成本比较高。JWT把用户ID、角色这些信息加密放到Token里前端请求时在Header带上Authorization: Bearer token后端拦截器一解析就知道用户是谁、能干什么天然适合分布式和前后端分离。具体实现上分三步登录成功后用io.jsonwebtoken.JwtUtil生成Token有效期建议设24小时过期让前端跳回登录页。写一个JwtInterceptor注册进拦截器链放行登录接口、静态资源其余/api/**请求全部校验Token。在HandlerMethod里注入用户信息时做一个基于角色的权限注解RequireRole(ADMIN)灵活控制接口访问。这套做完你说出去就是“我实现了基于JWT的无状态认证方案”——含金量直接不一样。顺带提醒JWT的密钥不要太简单别用123456这种被扫描出来容易出问题。2.3 统一返回格式与全局异常处理写接口最容易出现的情况有的接口出错返回null有的返回false有的直接抛异常前端拿到数据一脸懵还得各种状态判断。好的做法是全项目统一返回JSON结构{ code: 200, message: 操作成功, data: { } }code是业务状态码200成功401未登录403无权限500服务器异常message是提示信息data放业务数据。Controller所有接口的返回值全是ResultT配合全局异常处理器RestControllerAdvice代码里只需要专注业务逻辑不用到处写try-catch。这里有个细节值得注意业务校验失败比如床位已被占用应该抛出自定义业务异常BizException(B1002, 该床位已被占用)由全局异常处理器捕获并转换为对应code返回而不是在Controller里返回一个带错误信息的Result。你还记得那个让你“缝缝补补式”写异常处理的时期吗“满屏try-catch result.setMsg”的日子体验太糟糕。2.4 体检健康模块的实现要点大健康养老公寓的“健康”不是空头概念。健康档案模块建议设计成“一人多档”模式一个老人有一条主档案姓名、身份证、床位号、紧急联系人下面挂多条健康记录身高、体重、血压、心率、既往病史、用药情况。这里用MyBatis-Plus的分页查询PageElderHealthRecord 多条件组合查询按老人姓名、护理等级、记录日期范围筛选。前端表格用分页组件绑定接口参数就是pageNum、pageSize、elderName、dateRange这几个代码简洁明了。重点提醒健康数据涉及隐私查看敏感字段比如既往病史的接口上进在RequireRole(DOCTOR)以上权限家属端只能看自己绑定老人的数据这也就是为什么“老人-家属”关系表必须提前设计好。3. 前端Vue接入与交互实现3.1 前端工程搭建与代理转发前端这边我比较推荐用Vue CLI如果是Vue 2项目或者ViteVue 3项目来初始化。Element UI / Element Plus是租住管理类项目里的干活主力。几个关键依赖装好vue-router路由、axiosHTTP、pinia或vuex状态管理、echarts可视化图表。开发环境最大的坑是跨域。前后端分离下前端localhost:8080去请求localhost:8081/api浏览器的同源策略会把请求拦下。常用的解法是“代理转发”在前端项目的vue.config.js里加module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true, pathRewrite: { ^/api: /api } } } } }这样前端请求/api/login实际上会被转发到后端的http://localhost:8081/api/login整个开发过程中前端是无感知的axios的baseURL直接设置成/api就行。生产环境部署的时候前端打包成静态文件丢到Nginx再配一个反向代理转发到后端服务同一个套路。3.2 路由权限控制与菜单动态生成系统里有管理员、护理员、医生、家属四种角色不同角色看到的菜单应该不一样。为了做角色化菜单前端路由不能写死。我当时是把菜单项和权限字段绑定的在前端维护一份“菜单-角色映射表”路由守卫里根据当前用户的角色过滤。整体思路就是存储在Pinia/Vuex的用户信息里放一个roles数组路由的meta上也标一个roles数组所以比大小匹配一下就知道能不能访问。这一点比硬编码不同角色的路由要优雅得多体现了一个前端工程师的良苦用心。3.3 表格表单弹窗的组合套路管理系统的前端开发其实就是一个重复劳动比较多的活套路非常固定列表页搜索栏几个筛选条件 表格 分页 操作列。新增/编辑弹出Dialog里面放Form表单校验规则用rules声明。删除操作确认弹窗ElMessageBox.confirm调接口后刷新列表。状态切换比如启用/禁用、已入住/已退房用switch组件直接调状态接口。这些写多了会发现通用的“一个页面”就是“一个列表组件 一个表单组件 若干接口调用”所以一定要把重复代码抽出来。比如axios统一封装拦截器请求时自动带Token响应时统一处理401跳登录页、500弹错误提示。另外有个小技巧表格里的状态字段0/1/2这种前端别直接显示数字用字典翻译。比如“护理等级”的1/2/3级对应“自理/半自理/全护理”在数据格式化为dictMap后再渲染。这样做的好处是后续如果状态枚举变了只改前端一处的映射就够。3.4 健康数据可视化的落地加了可视化模块系统大屏的观感会很强势。用Echarts画折线图血压趋势、饼图护理等级占比、柱状图各区域入住率数据来源就是前文说的健康记录接口。这里需要注意Echarts初始化要在DOM渲染完成后所以要在mounted里初始化或者使用nextTick。当tab切换或弹窗内嵌图表时还要注意容器的宽高有没有获取到不然会出现图表只占一坨小格子的问题。解决方案是组件里放置图表容器的父元素设置好固定高度并且图表加载前调用this.$nextTick(() this.chart.resize())。这套搞定视觉效果直接上一个档次。答辩演示时先点一遍大屏再进业务页面走一遍流程老师看的兴趣能多一分。4. SQL脚本数据库设计与初始化数据4.1 表结构设计原则SQL脚本是整个项目的根基但往往最被忽视。“能跑就行”的心态最容易在数据库这里埋大雷。设计原则有三条表主键统一用bigint自增或雪花ID不要用varchar存主键。性能差异在数据量小的时候看不出来一旦数据量大麻烦全来了。所有表要有create_time、update_time、deleted逻辑删除三个字段。这一个习惯能让你的代码少写无数个“硬删除后关联数据变成孤儿”的bug。外键约束能不用就不用线上环境基本都不加但逻辑外键有对应关系关系要在代码和设计文档中说明白。比如老人表elder_infoCREATE TABLE elder_info ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, elder_no varchar(32) NOT NULL COMMENT 老人编号, name varchar(64) NOT NULL COMMENT 姓名, gender tinyint DEFAULT NULL COMMENT 性别:1男 2女, birthday date DEFAULT NULL COMMENT 出生日期, id_card varchar(18) DEFAULT NULL COMMENT 身份证号, phone varchar(20) DEFAULT NULL COMMENT 联系电话, bed_id bigint DEFAULT NULL COMMENT 关联床位ID, care_level tinyint DEFAULT 1 COMMENT 护理等级:1自理 2半自理 3全护理, status tinyint DEFAULT 1 COMMENT 状态:1在住 2退房 3待入住, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted tinyint DEFAULT 0 COMMENT 逻辑删除:0未删 1已删, PRIMARY KEY (id), KEY idx_bed_id (bed_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT老人信息表;注意聊天中用到的字段名要见名知义加注释很重要。后面不管是写代码、写接口文档还是验收答辩都能省很多口舌。4.2 关联表设计老人-家属关系、床位-费用关系养老公寓系统里“老人-家属”是一对多一个老人可有多个家属还是多对多家属关联多位老人一般建议设计成多对多关系表因为现实里确实有一位家属绑定两位老人的情况比如儿子给爸妈同时办入住。CREATE TABLE elder_family ( id bigint NOT NULL AUTO_INCREMENT, elder_id bigint NOT NULL COMMENT 老人ID, family_id bigint NOT NULL COMMENT 家属用户ID, relation varchar(20) DEFAULT NULL COMMENT 关系:儿子/女儿/配偶, is_primary tinyint DEFAULT 0 COMMENT 是否主联系人, PRIMARY KEY (id), UNIQUE KEY uk_elder_family (elder_id, family_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;还有“床位-费用”关系这里建议做一个单独的fee_rule费用规则表把护理等级、房型、餐食标准映射到每日单价。订单生成或者按月结算时直接按规则计算而不是把单价硬编码在业务代码里——否则后期改价格你得改代码、重新部署而用规则表的话动不动在管理后台改个数字就行。4.3 初始化数据的必要性要给系统正常启动跑起来至少需要这些初始化数据管理员账号建议admin/123456首次登录强制改密几个测试角色用户护理员、医生、家属各一个基础字典数据性别、护理等级、床位状态、订单状态等10条左右的演示数据老人档案、床位信息、健康记录、费用流水初始化的SQL脚本我习惯放在一个独立sql/目录下分成01_schema.sql建表、02_data.sql初始化数据。这样不管是别人拿到你项目还是你换电脑重新部署两步就能把数据库拉起来。5. 接口文档与联调经验5.1 接口文档应该包含什么接口文档是很多同学容易忽略但又是决定项目“专业感”上限的点。不要求你把所有接口都写得很细但核心接口至少要有下面这些信息接口路径比如POST /api/elder/page请求参数字段名、类型、是否必填、含义、示例值响应结果code、message、data结构data里数组还是对象鉴权要求哪些接口需要Token哪些开放分页参数pageNum/pageSize怎么传返回里total放哪我建议用Swagger2注解来生成接口文档Api、ApiOperation、ApiModelProperty挂上Springfox依赖后启动项目访问/swagger-ui.html就能看到在线文档。这个展示方式在答辩时特别加分——导师看了会说“这项目做得很规范”。5.2 前后端联调的几个小经验联调是开发中最容易出现“互相甩锅”的阶段。这里分享几条经验接口返回数据格式的对齐最好一开始用postman定义好并给前端开发一份简化版的接口约定文档不然前端总会拿不到你data里的那个字段来问你。时间字段的统一格式很关键Java后端如果不做处理可能会返回时间戳秒/毫秒前端格式化非常麻烦。建议全局配置Jackson的日期格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8字段命名一定要统一Java用驼峰elderName前端也用驼峰访问。除非你数据库字段就是下划线且不开启驼峰映射否则很容易因为大小写不一致导致前端迟迟拿不到数据。5.3 接口幂等性与事务处理在做费用结算、订单创建这类接口时要考虑“重复提交”的问题。比如家属端缴纳费用时如果前端没做好防重用户连点两次按钮就会产生两条扣费流水。解决方案可以在后端加防重令牌Token/Idempotent Key或者利用数据库唯一索引做约束比如订单号唯一。带着这个思路去回答该项目的核心业务问题时会让人瞬间觉得你不只写了个CRUD是真的在做设计。事务处理方面在Service实现类上直接加Transactional(rollbackFor Exception.class)保证操作多张表时的一致性。比如退房操作要同时更新老人状态、释放床位、结算最后一份账单这三步必须在一个事务里。如果没加事务或者只加了默认配置中间某步抛出异常就会造成“床位释放了但老人还显示在住”这种经典脏数据。6. 常见问题与排查技巧实录6.1 跨域问题导出的“请求发送失败”现象前端axios调用后端接口控制台报Access to XMLHttpRequest at http://localhost:8081/api/login from origin http://localhost:8080 has been blocked by CORS policy。排查思路确认后端CORS配置是否生效看是否用了CrossOrigin还是全局配置。如果是全局配置注意allowedOrigins不要写*某些浏览器下*加allowCredentials(true)会直接报错。确认是否用了代理转发如果前端已经配置了proxy那你直接请求/api/login就好别再走全路径。看后端日志有没有收到请求收到说明是响应头没加没收到说明请求根本没到达后端是代理配置或端口问题。最常见的问题是把后端端口写错成8080和前端发起了冲突然后CORS报错误导排查方向。建议直接用vue.config.js的proxy解决开发环境几乎不碰CORS。生产环境的Nginx配置里加location /api/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }6.2 SpringBoot版本太高导致的依赖兼容问题我见过不少同学新建项目时直接选最新版SpringBoot比如3.x然后发现MyBatis-Plus、Swagger这些starter的兼容库还没跟上启动直接报错。网上很多教程还是基于SpringBoot 2.x的照抄也能报错一堆。解决方案毕设项目不要用最高版本。SpringBoot 2.7.x JDK8 MyBatis-Plus 3.5.x Swagger 3.0.x这套组合经过大量项目验证非常稳定。如果你是跟着某篇教程做的就先看教程的版本保持一致。6.3 Vue打包后布局异常问题本地开发好好的npm run build部署到Nginx后刷新页面出现404或样式丢失、图标不显示的经典问题。原因基本都是路径配置不对。解决方案在vue.config.js里设置publicPath: ./相对路径。路由模式尽量用hashcreateWebHashHistory不要用history模式。如果非要用historyNginx需要配置try_files $uri $uri/ /index.html;来避免404。还有一个容易踩的坑打包后Element-UI的字体文件丢失大概率是publicPath配置问题把资源引到绝对路径了。把publicPath设成相对路径后基本解决。6.4 接口文档中分页参数命名不一致前端传的pageNum从0开始还是从1开始后端返回的pageNum和total字段与前端期望对不上这类问题在联调时最容易消磨耐心。统一的约定是pageNum从1开始pageSize默认10返回结构是{ total: 100, records: [] }。前端拿到total后用el-pagination的total属性绑定。注意MyBatis-Plus分页插件的IPage里字段名是total和records前端就按这个结构用不要你手动再包一层。如果你要自定义返回VO那就要在接口文档里明确说明结构。7. 项目部署从本地到线上7.1 后端打包与启动SpringBoot项目用Maven打包IDEA右侧Maven面板→Lifecycle→package会生成一个jar包。注意application.yml里配置的数据库连接、文件上传路径要改成你测试服务器的环境。启动命令java -jar yanglao-admin.jar --spring.profiles.activeprod如果你配置了多环境配置文件application-dev.yml / application-prod.yml启动时指定profile即可。没有配置多环境的话直接改yml里的MySQL密码和端口重新打包也行。7.2 前端打包与Nginx配置前端npm run build生成dist目录上传到服务器Nginx的html目录下。Nginx配置可以参考上面说的跨域那段。再加一个gzip压缩会有更好的优化效果server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8081; } }转后端服务起来后访问你的域名地址正常能打开前端页面并登录就算部署成功。7.3 常见启动失败排查后端启动失败常见三处数据库连不上查看yml端口、用户名、密码是否正确加上时区参数characterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai。端口被占用报Port 8081 was already in use用netstat -ano | findstr 8081Windows或lsof -i:8081Linux找占用进程干掉或者改yml端口。Redis连接失败如果你在项目中用了Redis做缓存或Token存储注意服务器上Redis必须允许外部访问或至少设置正确的密码否则启动时连接超时。8. 答辩与项目优化的几个加分建议8.1 演示顺序安排答辩演示不要上来就讲代码结构评委没耐心看。建议按这个顺序走先展示登录页 不同角色登录后看到的不同菜单体现权限设计。快速走一遍老人信息录入、入住院办理的完整流程体现业务闭环。打开健康数据大屏展示可视化图表视觉冲击力。展示Swagger接口文档页面体现接口规范。8.2 代码整洁度检查答辩时最怕被抽查代码后发现包名乱起、没有注释、论底层逻辑完全通不过。几个硬性指标包名统一用com.xxx.yanglao不要出现默认包。Service接口和实现类分开别把所有逻辑都堆在Controller里。Controller方法上要有注释说明接口用途。SQL脚本有注释表字段有注释。8.3 可扩展方向如果你想在已有基础上再加点亮点可以考虑消息通知当老人健康指标异常后端主动向家属端推送提醒用WebSocket或极光推送。报表导出把费用统计、健康趋势报表导出为ExcelEasyExcel实现。大屏实时刷新定时器每10秒拉一次数据图表动态更新。但记住这些是“锦上添花”先把主流程的稳定性保住。你代码再花哨一运行就报错前面全白搭。在这个项目的整个开发过程里我最大的体会是这种管理系统类的毕设难点从来不在某个技术点本身而在你“能不能把所有环节串起来”。数据库表设计要考虑业务后端接口要考虑前端怎么拿数据前端页面要接得上接口文档的返回结构部署时还要处理跨域、路径这些问题。任何一个环节脱节都会让整个系统看起来“差点意思”。如果你正在做这个项目先把数据库表建好再把后端核心接口调通最后去写前端页面——这个顺序别乱。乱的话调试成本会翻倍。最后分享一个小经验项目里凡是自己调试了很久才解决的问题都记下来搞成一个“问题排查文档”。答辩的时候如果老师问“你遇到的最大困难是什么”直接从里面挑一个讲比干巴巴地说“我遇到了很多bug”要有说服力得多。这个习惯放到工作中更是受益一辈子。
返回列表