尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

SpringBoot+Vue夕阳红公寓管理系统毕设全解析:从模块设计到前后端联调

发布时间:2026/9/26 18:02:35

资讯中心
01
ARTICLE

SpringBoot+Vue夕阳红公寓管理系统毕设全解析:从模块设计到前后端联调

SpringBoot+Vue夕阳红公寓管理系统毕设全解析:从模块设计到前后端联调
做毕设辅导这些年“SpringBootVue夕阳红公寓管理系统”是出现频率很高的一个选题。这套系统的业务半径不宽老人信息、房间床位、收费缴费、健康护理全都在一个园区管理场景里闭环做起来可控技术栈又是Java后端、MySQL数据库加Vue前端的经典组合成熟、资料多、换谁都能搭起来。也正因为常见能不能把“为什么这么设计”讲清楚就成了答辩拉开差距的关键。这篇文章我就按毕设的完整套路把这个项目从选题、模块设计、前后端落地到联调排错拆开来讲准备选这个题、或者正在写代码的同学都能直接用上。1. 选题的底层逻辑与技术栈选择1.1 为什么推荐养老公寓方向先说要避开的一个误区很多同学找毕设题目喜欢挑那种看着高大上的——又是微服务又是大数据结果开发一个月、调试三个月到最后答辩连自己写的代码都解释不流畅。而“夕阳红公寓管理系统”这种选题恰好在另一个方向业务模型非常成熟表结构怎么设计业内已经有大量现成参考你不需要从零发明业务但系统的大小又足够装下登录、权限、多角色、CRUD、报表统计、文件上传这些毕设必考的能力点。更深一层养老公寓的业务链条是自然连贯的老人要登记入住入住就牵扯到房间和床位分配住进来就要收费收完费还要有护理记录、健康档案最后退住还要结算——一个模块牵出另一个模块数据的流转本身就是一条完整的业务闭环。这条闭环对评委来说是最好讲的东西因为你可以从一张数据库表讲到另一张表把整个系统的“故事线”理清楚还不显得内容空洞。就拿查“在住老人”这个最简单的需求来说它可能涉及老人表、床位表、房间表、入住记录表四张表的关联这种数据串联关系是答辩时最自然的讲解素材。1.2 SpringBootVueMySQL为什么是“黄金组合”在毕设语境里技术栈选型核心考量三个字可控性。后端用SpringBoot是因为它把Spring那一堆XML配置全部收进了自动配置里一个main方法就能把内嵌Tomcat跑起来。对没有实际生产经验的本科生来说SpringBoot是“最容易跑通”的Java后端方案而且面试时“自动配置原理”“起步依赖”这些点也确实是高频考点属于学习性价比很高的框架。如果你手边还有“springboot面试题”这类资料你会发现大部分题目都能在这个项目里找到落脚点比如自动装配、starter机制、统一异常处理答辩前拿这些题过一遍代码比死记硬背八股文有效得多。前端用Vue本质是组件化开发模式让页面复用变得很直观。一个表格封装成组件换一份数据就能复用在老人列表、床位列表、缴费记录上开发体验比传统jQuery拼DOM强一个时代。Vue2的项目用Element UI组件库Vue3的项目用Element Plus毕设场景下我通常建议Vue2因为资料存量最大遇到报错一搜就有答案底子更好的同学可以上Vue3答辩口感更新。如果你是从零学Vue先花一天看“vue入门”的教程理解数据驱动、组件通信、路由这三个概念再动手写这个项目就不慌了。数据库选MySQL不用多说开源、免费、默认大学课程教的就是它。需要注意的第一坑是版本现在新装MySQL基本是8.0JDBC驱动要写com.mysql.cj.jdbc.Driver配置文件必须加serverTimezoneAsia/Shanghai这两行能拦住一半初始化失败的人。还有“mysql设置默认值为0”这种小问题建表时如果没给状态字段配默认值后续代码里就容易出现空指针后面的数据库章节我会专门讲。2. 核心功能模块拆解与业务设计2.1 模块清单与业务闭环先给一张模块总览表后面每个模块展开讲。模块核心数据面向角色关键动作老人档案管理老人基本信息、家属联系人前台/管理员新增、编辑、查询、入住状态迁移房间床位管理楼栋、房间、床位状态管理员房间新增、床位分配、状态变更入住退住管理入住记录、入住时间、退住结算前台/管理员入住登记、换房、退住费用缴费管理费用项、账单、缴费记录财务/管理员账单生成、缴费登记、欠费查询健康护理管理护理计划、护理记录、健康档案护理员记录新增、健康趋势查看系统管理用户、角色、菜单权限管理员用户分配角色、菜单授权这套模块之间不是孤立的。老人入院先在“老人档案”里建一条基本信息再在“房间床位管理”里选一个空闲床位随后“入住管理”生成一条入住记录之后每月“费用管理”根据护理等级生成账单护理员每天在“健康护理管理”写入护理内容。一张老人的信息卡片串起了全部业务表。实现的时候把这条主链路跑通项目就已经完成八成剩下的系统管理是围绕“谁能看什么、谁能点哪个按钮”展开的权限模型属于锦上添花的部分。2.2 老人档案与状态机老人档案表建议字段姓名、性别、身份证号、出生日期、联系电话、紧急联系人、关系、病史、过敏药物、入住时间、状态。这里有两个容易犯的错身份证号后端要做校验至少保证18位且格式合法涉及老人健康隐私的数据不要直接明文往页面上铺列表接口只返回必要的展示字段详情再单独查这是加分项。最该设计好的是“状态”。一个老人从入院到离开状态会在“登记未入住—在住—已退住”之间流转用一个status字段来记录。状态机逻辑虽然简单但所有业务统计都依赖它统计在住人数是status在住的数量查询空余床位也要先排除住在该床的未退住老人。所以凡是状态变更的操作都要放到一个统一的后端服务里处理不要前端传来什么状态就存什么状态否则后期统计一定会乱。我见过有同学在编辑老人信息时顺手把status改成“在住”结果房间分配还没录、费用单也没生成数据就漂在半空中直接导致统计报表对不上账。2.3 房间床位管理与入住退住房间和床位是父子关系建议拆两张表room放楼栋、楼层、房型、朝向bed放房间ID、床位号、状态。床位状态除了“空闲/占用”我还建议加一个“维修”状态否则保洁或维修期间床位既不能分配也不能删会很尴尬。页面展示时如果只显示“空闲”的床位下拉选项里就不会出现维修中的床位分配床位时误选的概率会大大降低。入住退住这块业务最核心的规则是“一个床位在同一时间只能住一个未退住老人”。实现上先通过床位状态筛选可分配床位入住成功后将床位状态置为占用退住时必须同时做三件事关闭入住记录、释放床位、结清剩余费用。这三件事要么全成功要么全失败所以必须在同一个事务里做。这里建议用一个Transactional注解把退住方法包起来一旦中间抛异常就整体回滚不会出现“床被释放了但费用还挂着”的脏数据。2.4 费用账单与健康护理费用这块是毕设答辩中评委最容易深挖的地方。系统里至少要有三类费用基础月费包含住宿和基本服务、护理费按护理等级分级、代收费用水电费。我建议设计一张费用项字典表一张账单表。账单在固定日期生成缴费后回写实缴金额和缴费时间欠费用户通过“缴费状态未缴”筛出来。数据库设计时记得把金额字段统一用DECIMAL(10,2)禁止用floatJava实体对应BigDecimal这是财务数据的铁规矩。答辩时如果评委问“为什么不直接在前端算金额”你回答“金额计算放在后端避免前端篡改同时保证账务口径一致”这个问题就稳了。健康护理模块能体现系统的人文关怀属性。每位老人可以有一条护理计划包含护理等级、护理频次、注意事项护理员每次执行护理动作后写一条护理记录内容包括体温、血压、血糖、精神状态、用药情况等。这部分的亮点在于“关联查询”从老人的维度查看所有护理记录的走势血压连续几天偏高系统给出提醒。实现不复杂就是按老人ID和时间范围筛选记录但讲出来很有画面感适合在答辩演示时专门点一下。3. 前端Vue落地实操3.1 环境搭建与工程骨架前端环境是三件事装Node.js、装npm、创建Vue工程。如果你手头正好是Vue2方案用vue create工程建议勾选Router和Vuex如果是Vue3用create-vite脚本初始化的速度更快。装依赖慢是另一个高频问题原因是npm默认源在境外解决办法一句话把registry切到国内镜像即可网上的“npm镜像设置”教程很多照着做就行。这里提醒一个细节node版本和vue-cli版本有对应关系老项目用新node经常报ERESOLVE依赖树错误别硬着头皮升级把node切到项目README要求的版本更省事。工程内部建议按这个结构分目录views放页面组件router放路由配置api放所有请求方法utils放axios封装和工具函数store放全局状态。很多同学把请求方法直接写在页面里结果每个页面几百行代码后期改接口没改全页面白屏半天查不出原因。提前把“所有HTTP请求都放在api目录下”这条纪律立住前后端联调能省大量时间。比如老人列表、床位列表、账单列表各自在api目录下对应一个module.js文件页面里只调用方法前后端接口一旦变更只需改一个文件。3.2 路由配置与权限控制前端这个系统建议分为两个区域登录页和登录后的主布局。主布局左侧是侧边菜单顶部是用户信息区内容区根据路由切换。路由可以分成静态路由和动态路由登录、404这种不需要权限的直接配死角色相关的菜单路由等用户登录以后通过接口把菜单列表拉回来再动态添加进路由表。这就是常说的动态权限菜单。这样设计的好处是不同角色登录后看到的菜单天然不同管理员能看到系统管理入口普通护理员只能看到护理相关页面。路由守卫要处理两件事一是未登录用户不管访问哪个页面都跳回登录页二是已登录但无权限的页面拦截提示。这里的常见坑是跳转死循环——路由守卫里判断到没权限又用router.push去跳一个同样被守卫拦截的页面就变成无限循环。解决思路是判断“目标路由是否在允许列表中”或者用window.location.href做一次硬跳转绕过守卫。另外动态路由要配合菜单权限一起做后端返回的菜单项里的component字段前端要映射到具体的Vue组件如果映射漏了登录进去就是白屏。我后面第6章会专门讲这个白屏排查思路。3.3 axios封装、拦截器与接口对接axios封装是前端工程化的基本功我建议的封装很实用这里给一个核心骨架// utils/request.js 核心部分 import axios from axios import { Message } from element-ui import router from /router import { getToken, removeToken } from /utils/auth const request axios.create({ baseURL: process.env.VUE_APP_BASE_URL || /api, timeout: 10000 }) request.interceptors.request.use(config { const token getToken() if (token) config.headers[Authorization] Bearer token return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.msg || 请求失败) if (res.code 401) { removeToken() router.push(/login) } return Promise.reject(new Error(res.msg)) } return res.data }, error { Message.error(error.message || 网络异常) return Promise.reject(error) } ) export default request这段代码解决了三件高频事每个请求自动带token、响应非200统一弹提示、登录过期自动回登录页。实际联调时后端返回结构一定要和这里保持一致我建议后端统一用{code, msg, data}三层结构前端就永远只取res.data这一层不用每个接口单独判断。很多同学前后端联调对不上八成是后端返回了data字段但前端取的是result或者错误码用的不是200而是0这类命名不一致的问题最好开工前先定一份接口规范文档把返回格式、分页字段名、日期格式全部固定下来。3.4 页面构造方法论与组件复用公寓管理系统的页面高度同构老人列表、床位列表、缴费列表本质都是“表格搜索栏分页”的组合新增和编辑用同一个弹窗里面放一个表单。抓住这个规律页面写起来就很快。以老人档案页为例搜索区放姓名、状态两个条件主区是el-table列包括姓名、性别、年龄、联系方式、入住状态、操作按钮操作按钮里的“编辑”“详情”打开同一个el-dialog里面放el-form。注意el-table的列需要固定宽度身份证号这种长字段不固定会把表格撑裂。分页用el-pagination绑定总条数和当前页切页时重新请求接口。每个页面都这样写系统里十来个管理页面一天能写完。组件复用还有一个技巧把“搜索栏表格分页”封装成一个通用列表组件页面只传入列配置和接口方法就能少写很多重复模板。但对毕设来说我不建议盲目封装过度封装太重了你可能解释不清答辩时被追问组件内部逻辑反而被动。适度的做法是把弹窗表单抽成一个子组件列表部分保留在页面里这样既减少重复又不会让代码结构太绕。4. 后端Spring Boot核心实现4.1 分层结构与请求链路后端代码结构建议controller/ 接口层 只做参数接收与结果返回 service/ 业务层 核心逻辑都在这 mapper/ 数据访问层 继承BaseMapper entity/ 数据库实体 dto/ 请求参数对象 vo/ 返回视图对象 common/ 统一返回、异常、常量 config/ 配置类 拦截器、跨域等 utils/ 工具类这个分层对应的请求链路是前端请求 → Controller接收 → Service处理业务 → Mapper查询数据库 → 数据返回实体 → Controller包装成Result → 返回前端。面试时把这条链路背出来相当于给答辩开了一个好头。写的时候注意Controller里不要写业务逻辑比如“查询在住老人并统计房间空余”这种动作就应该放到Service层Controller只负责接收参数、调Service、返回Result。这样代码结构清爽评委问“你这个接口的逻辑在哪”你直接指Service就行。4.2 MyBatis-Plus让CRUD效率翻倍如果所有表的增删改查都手动写SQL工作量非常可观。MyBatis-Plus把单表CRUD封装成了现成方法你的Mapper接口只需要继承BaseMapper 就能直接用selectById、selectPage这些方法写分页查询Mapper public interface OldPeopleMapper extends BaseMapperOldPeople { }复杂查询再配合LambdaQueryWrapper简洁且类型安全// 分页 条件查询 PageOldPeople page new Page(pageNum, pageSize); LambdaQueryWrapperOldPeople wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(name), OldPeople::getName, name) .eq(StringUtils.hasText(status), OldPeople::getStatus, status) .orderByDesc(OldPeople::getCreateTime); OldPeopleMapper.selectPage(page, wrapper);这里的链式写法有讲究like、eq方法第一个参数是个Boolean当条件不成立时这个条件会自动跳过。前端搜索框没填姓名时传一个空串这个条件就不拼接代码不用写一堆if判断。这是MyBatis-Plus最实用的特性之一答辩时可以讲给评委听。另外使用这种预编译的SQL写法本身就能防住SQL注入比自己拼字符串安全得多。4.3 JWT登录鉴权与拦截器实现登录模块是每个答辩老师都会打开看的实现时建议用JWT。流程是用户输入账号密码后端校验成功后用jwt工具类生成一个包含用户ID和用户名的token返回给前端前端存进localStorage后续请求带上token后端定义一个拦截器统一解析解析不到就返回401。生成token的核心思路是把用户唯一标识塞进claims里再设置一个过期时间用私钥签名。拦截器方面我习惯单独写一个AuthInterceptor类在preHandle方法里从请求头取Authorization去掉“Bearer ”前缀后用工具类解析解析失败直接返回JSON而不是抛出页面异常这样前端拦截器才能统一处理401跳转。拦截器注册要注意放行的路径必须包含/login和静态资源路径否则会出现“后端明明起来了前端一访问就401”的灵异事件。我习惯这样注册Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /error, /static/**); } }4.4 统一返回、全局异常与安全细节统一返回Result类长这样Data public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ... } public static T ResultT error(Integer code, String msg) { ... } }再配一个RestControllerAdvice注解的全局异常处理器把业务异常、参数校验异常、兜底异常分别catch住返回给前端统一的错误结构。这样前端拦截器里只需要判断res.code不等于200就能统一弹错误提示不会出现一堆try/catch。实体字段上的NotBlank、Email这类校验注解需要在Controller方法参数上加上Validated校验失败后全局异常处理器捕获MethodArgumentNotValidException把第一个报错信息返给前端体验比后端直接抛500好得多。安全方面有两个细节容易加分一个是XSS防护可以写一个全局过滤器对请求参数里的script标签做转义处理另一个是越权防护查询接口里根据当前登录用户的角色做数据范围限制比如普通护理员只能查看自己负责老人的护理记录管理员才能看全量数据。这里提一句如果你在网上搜过“springboot项目全局过滤器处理上传pdf文件时xss攻击”这类问题会发现过滤器里要注意Content-Type的判断避免误伤文件上传的二进制流。一个系统的“安全设计”在答辩里讲出这两点比堆砌几个名词管用得多。5. 数据库设计与查询优化5.1 核心表结构与字段规范建议核心表这样设计表名用途关键字段sys_user系统用户id, username, password, real_name, role_id, statussys_role角色id, role_name, role_keysys_menu菜单/权限id, parent_id, menu_name, path, component, permsold_people老人档案id, name, id_card, phone, status, emergency_contactroom房间id, building_no, floor, room_no, room_type, statusbed床位id, room_id, bed_no, statuscheck_in入住记录id, old_people_id, bed_id, check_in_date, check_out_datefee_record账单id, old_people_id, type, amount, status, create_datecare_record护理记录id, old_people_id, care_type, content, operator_idMySQL侧的几个基础设置值得注意数据库字符集用utf8mb4否则写入生僻字或者emoji会在插入时直接抛异常金额字段用DECIMAL(10,2)所有的主键统一用bigint自增不要搞UUID做主键索引效率差很多每个表建议都带上create_time、update_time两个审计字段。密码字段不要明文存用BCrypt加密后再入库就算数据库被拖库密码也不至于直接泄露答辩时这是能讲的安全点。5.2 多表联查的典型写法入住信息页面通常要显示老人姓名、楼栋号、房间号、床位号、入住时间这是典型的三表联查。SQL写出来就是SELECT c.id, o.name AS old_name, r.building_no, r.room_no, b.bed_no, c.check_in_date FROM check_in c JOIN old_people o ON c.old_people_id o.id JOIN bed b ON c.bed_id b.id JOIN room r ON b.room_id r.id WHERE c.status 1 ORDER BY c.check_in_date DESC;注意两点一是连表查询尽量用明确的别名答辩时你能说清楚每个JOIN的含义二是如果查询的数据量和并发都不大直接这种写法完全没问题不必硬上缓存、分库分表那是过度设计。真要说优化给外键字段和筛选字段加上普通索引就够了比如old_people表的status、check_in表的old_people_id和bed_id。这里再说一个很坑的细节连表字段的类型必须完全一致一个表用bigint一个表用varcharJOIN的时候索引会失效表数据量一大查询就慢得离谱。5.3 状态字段、默认值与数据一致性设计状态类字段要主动给默认值。比如bed表的status默认0表示为“空闲”fee_record的status默认0表示“未缴”对应到SQL建表语句就是DEFAULT 0。很多同学建表漏掉默认值结果service层每次new实体都得手动setStatus漏一处就出现空指针。这边答辩还可能追问“update语句怎么写才不掉条件”可以顺带复习一下UPDATE fee_record SET status 1, pay_time NOW() WHERE id ? AND status 0;带状态条件的更新可以防止重复缴费这种并发问题。两个用户同时点击缴费只有一个会把status从0改成1另一个因为status ! 0 更新不到数据。这个写法在答辩里讲出来算是一个很漂亮的细节。另外数据一致性还可以用事务来兜底退住结算、入住分配这些涉及多表变更的操作Service方法加上Transactional异常自动回滚这个前面已经提过属于必加项。6. 环境搭建、联调与排坑实录6.1 从零跑通项目的完整步骤拿到一套没有跑通过的源码第一步不是急着看懂代码而是先把环境立起来装JDK 1.8配置JAVA_HOME与PATH命令行输入java -version确认生效装MySQL 8按教程初始化root密码把数据库字符集设为utf8mb4用Navicat或命令行导入项目里的sql脚本创建数据库并导入表结构和测试数据后端项目打开application.yml改数据库连接、账号、密码启动SpringBoot看到Tomcat started on port 8080即成功前端项目npm install装依赖启动vue服务浏览器访问前端开发服务器地址能弹出登录页输入admin/123456走完全流程即大功告成。这套流程但凡卡住80%问题都出在前三步的版本匹配上而不是代码本身。比如JDK只装了JRE没装JDKMaven识别不到编译器比如MySQL连接串少写了useSSLfalse和serverTimezone启动直接报时区错误比如npm版本太高和vue-cli不兼容install到一半就崩。这些和业务代码都无关但会卡掉你好几天所以环境阶段多花半小时确认版本后面就顺了。6.2 前后端联调高频问题与解决方案现象原因解决思路启动报错driver not foundJDBC驱动版本与MySQL版本不匹配MySQL8用com.mysql.cj.jdbc.Driver5.7用com.mysql.jdbc.Driver后端能启动但前端访问不到前后端跨域或端口不一致前端配proxy代理或后端配CorsFilter登录后页面就跳回登录页token没带或后端401拦截了整个请求检查axios请求拦截器确认已从localStorage读取token并注入Header请求返回乱码编码不一致数据库链接加characterEncodingutf8后端接口返回之前统一设置UTF-8npm install报错ERESOLVE依赖树冲突常见于vue-cli4与新版node删除node_modules和package-lock.json重装或把node切到对应大版本分页数据不准pageNum和pageSize前后端约定不一致统一从1开始计数前端传pageNum后后端不做减一处理这里要特别说一下跨域问题。前端开发服务器在8080端口vue后端在8081端口浏览器会拦截不同源的请求最常见的错误提示是“Access-Control-Allow-Origin”。解决办法有两个一是后端加一个CorsFilter配置类把允许的域名和Header写好二是前端在vue.config.js里配置devServer.proxy把所有/api开头的请求转发到后端地址。毕设联调我推荐第二种因为上线部署时前端打包成静态文件后可以放在后端同域下跨域问题会自动消失dev代理只是开发期的过渡方案。6.3 几个典型的拦路坑记录印象最深的一次学生反馈“列表接口返回慢数据只有几十条但等了3秒”。我让他把SQL拿出来发现是外键id用了字符串类型JOIN时类型不一致索引全失效。这个问题提醒我外键字段的类型必须和主表主键完全一致bigint就是bigintvarchar就是varchar否则表数据量稍微涨一点就卡。另一类是前端路由权限引发的白屏用户登录后发现菜单是空的。排查顺序是——先看菜单接口是否返回数据再看动态路由有没有把菜单里的组件映射进路由最后看路由守卫是否在跳转前放行。这三个环节有一个断掉页面必然白屏或404而这个报错往往还不显眼地出现在控制台。所以注册菜单路由时建议后端把component字段直接返回一个前端能识别的组件路径字符串前端做一个统一映射能省掉大量调试时间。还有一个和MySQL相关的坑如果你用的是MySQL 8默认认证插件是caching_sha2_password老版本数据库工具连接时报错。解决方式是安装新版的Navicat或MySQL Workbench或者在MySQL里把用户的认证方式改成mysql_native_password但改成旧认证方式其实不是最优解不如直接升级客户端工具。这个细节很多人会忽略写在这里当个提醒。最后再聊几句经验这套项目我在辅导时反复讲的一个观点是毕设拿不拿优秀不在功能多少而在你有没有把“我为什么这样设计”讲到评委心坎上。你不需要给系统塞一堆用不上的中间件只要把老人档案这条主链路、再加上权限控制和安全防护这几点讲明白就是一个能打的项目。真到面试环节SpringBoot的自动配置、Vue的数据响应原理、MySQL的索引机制也都是从这套系统里能自然引出来的问题。准备答辩之前建议把这三个方向各准备一套两分钟的讲法然后再把后端启动日志里那几行关键的“Tomcat started”看熟——很多时候评委就是从你演示时的一句“这里需要注意端口占用”开始对你刮目相看的。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。