资讯动态

Java+SpringBoot敬老院管理系统设计与实现

发布时间:2026/10/8 10:31:16 来源:尧图企业网站定制
又到了毕业设计的季节很多计算机专业的同学都在纠结选题。如果你正在找一个“难度适中、业务清晰、好答辩”的Web项目Java SpringBoot 实现的敬老院管理系统确实是个很划算的选择。这个题目听起来不大不小但里面装的东西一点都不少老人档案管理、护理记录、房间分配、费用收缴、亲属来访、员工排班甚至还能接一个简单的统计看板。业务场景足够真实技术栈又能把 Java、SpringBoot、MyBatis-Plus、MySQL 这些面试常问的东西全部串起来。这篇文章我按自己当年做这类管理系统的思路把整个项目的设计、核心代码、踩坑记录全部拆开讲一遍希望能给正在做毕设或者想练手 Web 开发的你一些能直接用的东西。要说清楚这个项目到底值不值得做得先弄明白一个问题敬老院管理系统本质上是什么往大了说它是一套典型的企业级信息管理系统MIS往小了说它就是“增删改查 权限 报表”的组合。但正因为业务本身不复杂你才有精力把代码质量、表结构设计、功能完整性做到位而不是被业务逻辑绕晕。这套系统的核心用户其实只有三类管理员院长/系统管理员、护工/护士、前台财务。每个角色关心的事情完全不一样这直接决定了你的功能模块和数据库表应该怎么设计。1. 项目定位与整体思路拆解1.1 这个系统真正在解决什么问题很多人一上来就闷头写代码写了一半才发现不对。做毕设也好做项目也罢第一步永远是搞清楚“给谁用、用在哪、解决什么痛点”。敬老院管理系统的关键矛盾点有三个。第一个矛盾是信息孤岛。老人的基本信息、家属联系方式、健康档案、用药记录、缴费记录如果都散落在纸质档案和 Excel 表里查找一份资料可能要在文件柜里翻半天。系统要解决的就是把这些分散的信息集中到一个平台里检索、更新、导出一气呵成。第二个矛盾是护理过程不可追溯。养老行业最敏感的就是“老人出事了说不清楚”。几点查房、几点喂药、当天精神状态怎么样、有没有异常情况这些必须留痕。所以护理记录模块不是简单记一笔而是要形成一条完整的时间线谁在什么时间做了什么操作系统里全部能查出来。这也是答辩时最容易被问到的亮点。第三个矛盾是费用管理混乱。入住费、护理费、伙食费、医疗费各种费用项目繁杂手工算账容易出错。系统里需要一张清晰的费用流水表每一笔钱都有来源、有去向、有经手人月底统计对账直接导出明细财务和领导都省心。1.2 为什么技术栈锁定 Java SpringBoot选技术栈这件事务实比炫技重要。市面上做管理系统的方案很多Python 的 Django/Flask、Node.js 的 Express、PHP 的 Laravel 都能做但作为计算机专业的毕设Java SpringBoot 依然是性价比最高的选择原因非常实际。第一Java 依然是国内企业级应用的主力语言你用这个技术栈做毕设面试的时候聊起来最自然。面试官看到 SpringBoot 项目不会觉得陌生问的问题也都有标准答案你准备起来有方向。第二SpringBoot 把繁琐的配置几乎清零了。早些年用 SSMSpring SpringMVC MyBatis搭环境光是 XML 配置文件就能写几十行现在 SpringBoot 通过自动配置和起步依赖一个注解加上几行配置就能跑起来。这意味着你能把精力放在业务实现上而不是跟配置死磕。第三生态太成熟了。MyBatis-Plus 操作数据库一条 SQL 都不用写Spring Data JPA 也能快速 CRUD前端随便配个 Thymeleaf 或者 Vue 都行部署打包一个 jar 文件就搞定。这些成熟的组件组合起来让一个人在一两个月内完成一个功能完整的系统完全可行。提示如果你 Java 基础不太好强烈建议选 MyBatis-Plus 而不是 JPA。MyBatis-Plus 的语法更接近你学过的 SQL 思维写起来直观排查问题也容易。JPA 的关联映射虽然省事但封装太狠报错的时候你根本看不懂它在干什么。2. 核心技术选型与模块规划2.1 技术栈逐项说明与版本选择我做这套系统时用的技术栈清单如下每一项都是经过实际检验的稳妥组合。技术组件选型方案选型理由后端框架SpringBoot 2.7.x稳定、教程多、兼容 JDK8/11网上遇到的坑几乎都有现成答案持久层框架MyBatis-Plus 3.5.x单表 CRUD 零 SQL内置分页插件还有代码生成器数据库MySQL 8.0主流、免费、功能够用鉴权方案JWT 拦截器无状态认证前后端分离也适用答辩好讲前端方案Thymeleaf 或 Vue 3 Element Plus看你的前端水平二选一后面细说构建工具Maven 3.8项目依赖管理标配开发工具IntelliJ IDEA Navicat不用多说效率神器关于版本这件事多说一句千万不要一上来就追最新的 SpringBoot 3.x。SpringBoot 3.x 强制要求 JDK17而且把javax.*改成了jakarta.*很多网上教程里的代码直接复制过来是编译不过的。毕设求稳用 SpringBoot 2.7.x JDK8 或者 JDK11遇到问题搜资料一搜一大把省下的时间多睡几觉不好吗。2.2 核心业务模块划分与数据表设计思路模块划分是系统的骨架我的习惯是先画思维导图把角色和功能理清楚再动手建表。敬老院管理系统我把它拆成了六个核心模块。系统管理模块员工账号管理、角色权限分配、登录日志。这个模块管的是“谁能进系统、进去能干什么”。老人档案模块老人基本信息、家属联系人、入住登记、退住办理、档案增减改查。这是整个系统的数据核心几乎所有模块都要跟它关联。住宿管理模块房间信息、床位分配、调房记录。房间有不同类型单人间、双人间、套间状态有“空闲、已入住、维修中”分配床位时要自动校验容量。护理管理模块护理等级设定、每日护理记录、用药提醒、健康体检数据。老人入住时会评估一个护理等级自理、半自理、全护理不同等级对应不同的护理内容和收费标准。费用管理模块费用项目配置、月度账单生成、缴费登记、欠费提醒。费用逻辑是系统根据入住时长和所选服务自动算钱财务只负责确认和登记收款。统计看板模块入住率统计、护理任务分布、月度营收趋势、老人年龄段分布。这些图表数据用 ECharts 展示非常出效果答辩演示时加分明显。数据库表我建议这样设计这是多张核心表的简化结构。admin_user管理员/员工表id、username、passwordBCrypt加密、real_name、role、phone、statuselder_info老人信息表id、name、gender、birthdate、id_card、health_status、care_level、room_id、check_in_date、statusfamily_contact家属联系表id、elder_id、name、relation、phone、addressroom_info房间信息表id、room_no、room_type、bed_count、used_count、status、floornursing_record护理记录表id、elder_id、staff_id、record_date、content、temperature、blood_pressure、remarkpayment_record缴费记录表id、elder_id、item_name、amount、pay_date、pay_type、operatormedication_plan用药计划表id、elder_id、drug_name、dosage、frequency、start_date、end_date这里特别注意一点表与表之间的关联字段只用id不要用业务字段比如身份证号来做外键这样既能保证灵活性也能避免误操作把关联数据改坏。3. 核心功能模块的实现细节3.1 登录鉴权与员工权限设计登录是一个系统的门面也是**答辩时老师几乎必问“你是怎么控制权限的”**的地方。我用的是 JWTJSON Web Token方案流程很清晰。用户提交用户名密码后端校验通过后签发一个 token前端把 token 存在本地之后每次请求都在请求头里带上。后端通过拦截器统一解析 token拿到当前用户的 id 和角色然后判断这个请求路径当前角色能不能访问。JWT 生成的核心代码其实不复杂我用的是io.jsonwebtoken这个库。public String createToken(Integer userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }拦截器解析 token 我就直接贴关键逻辑了。Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); try { Claims claims Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody(); request.setAttribute(userId, Integer.parseInt(claims.getSubject())); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { response.setStatus(401); return false; } } response.setStatus(401); return false; } }角色的权限控制我用一个简单的思路在配置类里把路径和管理员、普通员工的映射关系定义好比如/admin/**必须是管理员权限/nursing/**护工可以访问/finance/**财务才能访问。这里有个隐藏加分项密码不要明文存储。用BCryptPasswordEncoder加密后再存入数据库即使数据库泄露密码也不是裸的。答辩时老师说“你这个密码加密了没有”你直接说用了 BCrypt分分钟变成加分点。3.2 老人档案与入住管理实现老人信息是整个系统的核心实体设计的时候要考虑它和房间、家属、护理计划的关联关系。我用 MyBatis-Plus 建实体的时候会加上TableName、TableId字段就按数据库表一一对应。老人信息的录入和编辑不建议做成“一个表单搞定所有”因为老人的字段太多、太杂。我当时的做法是分步骤表单基本信息一步、健康信息一步、家属信息一步最后确认提交。这样用户体验好代码逻辑也清楚。前端用 Vue 的话就是el-steps组件然后用变量控制当前显示哪一步。入住管理有一个细节很容易漏老人入住时不仅要插入老人记录还要更新房间的已住人数同时生成一条入住状态变更历史。这三个操作涉及三张表必须放在同一个事务里。我当时就踩过这个坑老人入住成功了房间状态没更新前台看到空房间又安排了一个人进来直接搞出“一房两住”的乌龙。后来用Transactional把三个操作包到一起才解决。Transactional(rollbackFor Exception.class) public Long checkInElder(ElderInfo elder, Integer roomId) { // 1. 查询房间校验是否已满 RoomInfo room roomMapper.selectById(roomId); if (room.getUsedCount() room.getBedCount()) { throw new BusinessException(该房间已住满); } // 2. 插入老人信息 elderMapper.insert(elder); // 3. 更新房间已住人数 room.setUsedCount(room.getUsedCount() 1); roomMapper.updateById(room); // 4. 记录入住历史 checkinHistoryMapper.insert(new CheckinHistory(elder.getId(), roomId)); return elder.getId(); }3.3 护理记录和健康数据的处理护理记录这个模块业务上最敏感代码上却最直接——说白了就一个带多条件查询的 CRUD。但我想聊聊数据设计层面的一个优化老人每月可能有 30 条护理记录加上量血压、测体温一年下来数据量不小。如果每次列表页都全表扫描性能会有点难看。当时我用了 MyBatis-Plus 的分页插件再加上(elder_id, record_date)联合索引查询速度完全没问题。分页插件的配置很简单在配置类里加一个 bean 就行。Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }查询语句也不需要手写直接调 MyBatis-Plus 的Page方法。public PageNursingRecord getNursingRecords(int page, int size, String elderName, String date) { PageNursingRecord p new Page(page, size); LambdaQueryWrapperNursingRecord wrapper new LambdaQueryWrapper(); if (StringUtils.hasText(elderName)) { wrapper.like(NursingRecord::getElderName, elderName); } if (StringUtils.hasText(date)) { wrapper.eq(NursingRecord::getRecordDate, date); } wrapper.orderByDesc(NursingRecord::getRecordDate); return nursingRecordMapper.selectPage(p, wrapper); }需要注意的一点是时间字段的格式问题。MySQL 的datetime类型传到前端会变成2025-01-05T10:30:00这种格式跟页面上的2025-01-05 10:30:00对不上。这是前后端联调时最常遇到的问题。解决办法也很简单在实体类字段上加上注解或者配置一个全局的 Jackson 格式化。JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8) private LocalDateTime createTime;3.4 费用自动计算与统计看板费用模块是“看起来简单、做起来麻烦”的典型。麻烦在哪儿呢入住时间不一定是整月、护理等级中途会变、调房会导致费用标准变化这些都会让“一键生成月度账单”变得没那么简单。我当时的实现逻辑是每月 1 号跑一个定时任务遍历所有在住老人根据其当前护理等级和房间类型查对应的价目表然后计算当月应收金额并按天折算。如果月中调房或者升级护理就按比例切分。计算结果生成一条payment_record记录状态为“待缴费”。用 SpringBoot 的Scheduled注解就能轻松实现定时任务。Component public class BillGenerateTask { Scheduled(cron 0 0 2 1 * ?) // 每月1号凌晨2点执行 public void generateMonthlyBills() { ListElderInfo elders elderMapper.selectList( new LambdaQueryWrapperElderInfo().eq(ElderInfo::getStatus, 入住)); for (ElderInfo elder : elders) { // 根据护理等级和房间类型计算费用 BigDecimal amount calculateAmount(elder); paymentMapper.insert(new PaymentRecord(elder.getId(), amount, 月度费用, 待缴费)); } } }统计看板这块我推荐用 ECharts 来做图表。它支持按天、按月统计营收、入住率等数据前端配置一个折线图加一个饼图逼格瞬间上来。后端只需要提供一个聚合查询接口用 SQL 按时间分组统计即可也可以用 MyBatis-Plus 的 QueryWrapper 实现简单的分组查询。答辩演示的时候打开统计页面看到图表动起来老师第一印象就好。4. 从零搭建到运行完整实操过程4.1 环境版本选型与踩坑前置很多同学的第一个坑不在写代码而在环境搭建。我强烈建议你在动手之前先确定好版本矩阵然后一次性装齐不要边写边装。我这次用的版本组合是JDK 8、Maven 3.8.6、SpringBoot 2.7.18、MyBatis-Plus 3.5.3、MySQL 8.0.32。为什么选这些老版本我心里有数——这组合已经被无数人验证过了你踩的坑早有人替你踩平了搜解决方案一搜一大把。IDEA 创建项目时选择 Spring Initializr然后勾选 Web、MySQL Driver、MyBatis 这几个依赖再手动加上 MyBatis-Plus 的 starter 就行。注意 MyBatis-Plus 和 SpringBoot 版本要兼容我用的坐标是dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.2/version /dependencyapplication.yml的基本配置如下数据库连接串注意加上useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai否则中文乱码和时区错乱会折腾到你怀疑人生。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/elder_care?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto4.2 数据库初始化与测试数据准备数据库脚本不要手动一条条敲直接用 Navicat 建好库之后把建表语句和图解生成出来。这里我推荐一个效率技巧用 MyBatis-Plus 的代码生成器反向生成实体类、Mapper、Service、Controller。它可以根据数据库表直接生成全套代码省去机械劳动你集中精力改业务逻辑就行。要注意的是生成之后必须检查两件事第一连表查询的 DTO 不要用自动生成的实体类顶替最好单独建 VO 对象避免把不需要的字段暴露到前端第二逻辑删除字段比如deleted在实体上要加TableLogic这样 MyBatis-Plus 执行删除时自动转成UPDATE ... SET deleted 1而不是真的 DELETE数据安全性高一个档次。测试数据这块我当时写了一个简单的数据初始化类配置了一些常用的老人档案、房间信息、护理记录。答辩演示时你总不能现场往数据库里手工录数据吧。生成 20-30 条模拟数据后面写统计看板也有内容可展示。4.3 后端分层写法与统一返回格式后端代码我按经典的四层结构来写Controller接收请求、Service业务逻辑、Mapper数据访问、Entity/VO数据载体。这个结构是 Java 面试的基本盘也是答辩老师默认的期望结构。不管是查询还是新增统一返回格式会让前后端联调省不少事。我定义了一个 Result 类Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMessage(操作成功); r.setData(data); return r; } public static T ResultT error(String msg) { ResultT r new Result(); r.setCode(500); r.setMessage(msg); return r; } }Controller 层所有接口都返回Result?前端统一判断code 200再取数据。这样做的好处是错误处理集中化不用每个接口单独写一堆 if-else。4.4 前端方案的两种选择与打包部署前端我有两个方案给你参考。方案一服务端渲染用 Thymeleaf 模板。适合前端基础不太好的同学直接在 HTML 里写${}取后端数据表单提交用th:action不用解决跨域整个项目就是一个 jar 包部署非常省心。方案二前后端分离用 Vue 3 Vite Element Plus后端只提供 JSON API。这个方案的视觉效果和专业度明显更高页面美观、交互流畅答辩时很加分但你需要额外解决跨域问题后端配置CorsFilter即可部署时要把 Vue 构建出来的dist目录扔进 SpringBoot 的static目录下变成一个 jar 包运行。我的建议是如果时间有限、前端水平一般选方案一如果离答辩还有 3 周以上、想冲刺一个更好的呈现效果选方案二。两个方案我都在自己的机器上跑通了方案二的跨域配置其实也就几行代码。Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedHeader(*); config.addAllowedMethod(*); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }5. 常见问题与排查技巧实录5.1 高频故障速查表我把自己做项目时真实遇到过的、以及身边同学反复问过的问题整理成了一张速查表建议先收藏。问题现象根本原因解决方案启动报Failed to configure a DataSourceapplication.yml 里数据库连接配置有误或者依赖缺失检查 url、username、password确认引入 mysql-connector 依赖中文写入数据库变成??数据库编码不是 utf8mb4建库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci日期字段传到前端格式不对Jackson 序列化格式未配置全局配置spring.jackson.date-format或实体字段加JsonFormat接口返回 401JWT 过期或拦截器误拦截放行路径检查 token 有效期确认拦截器 exclude 了登录和静态资源路径分页数据不生效没有配置 MyBatis-Plus 分页插件添加PaginationInnerInterceptor前端请求跨域前后端端口不同未配置 CORS加 CorsFilter 或CrossOrigin修改数据库表后实体类报错实体字段与数据库字段不一致重新用代码生成器生成或手动同步字段打包后前端页面空白Vue 的 history 路由刷新 404改用 hash 路由或在后端配置 forward 到 index.html5.2 最容易让人崩溃的 SpringBoot 版本陷阱这个必须单独讲一下因为方向错了努力白费。SpringBoot 3.x 发布之后搜索引擎里排前面的博客、文章好多都是新版本的内容你一搜“SpringBoot 整合 MyBatis-Plus”搜出来的教程大概率是 3.x 版本的里面用的jakarta.*包名和 JDK17你拿 JDK8 一编译直接报package javax.servlet does not exist。这时候千万不要慌也不要顺手把 JDK 升级到 17而是回到官网或者查依赖版本对应关系把 SpringBoot 稳定在 2.7.x然后找一个明确标注支持 2.x 的教程。另外一个容易踩的坑是SpringCloud 和 SpringBoot 版本强绑定。如果你后续想加一点微服务的概念进项目里比如服务注册发现一定要先查清楚 SpringCloud 的版本对应的 SpringBoot 版本否则启动直接报IllegalArgumentException或者找不到配置类。毕设阶段我建议不加微服务除非老师明确要求否则 SpringBoot 单体应用完全够用。5.3 业务上的隐蔽逻辑 Bug这类 Bug 不会让程序报错但会让数据变得不合理属于“运行时逻辑错误”更难发现。我列三个典型的。第一个是并发入住同一房间。两个人同时在前台操作都看到 3 人间只剩 1 个空位同时录入结果入住人数变成 4。解决办法是给room_info表加一个带条件的更新操作UPDATE room_info SET used_count used_count 1 WHERE id ? AND used_count bed_count然后检查受影响行数如果为 0 就提示“房间已满”。第二个是退住时没有检查费用结清。老人退住之前必须把所有欠费交完否则账目就乱了。退住接口里要写一个校验逻辑存在未缴账单就禁止退住操作。第三个是删除家属联系人的级联问题。老人档案删除了家属表里的记录还残留着下次查老人的时候数据已经没了但家属变成了“孤儿数据”。在删除老人的方法上加上事务同时删除关联家属、护理计划或者设置TableLogic让它们随主记录一同“假删除”。6. 写在最后的经验体会花了几千字把这个项目从头到尾拆了一遍最后说几句掏心窝的经验。毕设项目不是越大越好而是越完整越好。一个能正常运行、功能闭环、代码清晰、答辩能讲出设计思路的敬老院管理系统在一众还没跑通的项目里已经是很能打的存在了。封装要适度不要为了“显得高级”硬上微服务、分布式、消息队列这些东西如果没有真实场景支撑答辩反而容易被问穿。做项目的时间分配也很重要。我见过太多人把 80% 的时间花在搭环境和调样式上最后没时间写核心业务逻辑。正确的节奏是花一周把环境和数据库搭好两周把后端核心接口写完一周做前端页面对接最后留一周专门处理各种边界情况和打磨演示流程。答辩时最重要的不是代码写得有多花哨而是你能打开系统把“登录-录入老人-分配房间-添加护理记录-生成账单-查看统计”这条主流程一气呵成地走下来把每一步的设计思路讲清楚。最后一个小技巧提前准备一份演示数据脚本把答辩用的账号、密码、典型业务场景都预设好。我当年就是靠着一份精心准备的演示脚本在系统被临时清库的情况下三分钟就把完整流程演完评委老师全程没有打断。如果你正在做类似的系统希望这篇文章能帮你少走几步弯路。遇到具体问题欢迎分享你的报错信息我们一起讨论。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑