社区养老系统开发实战:Spring Boot与Vue3实现老人档案、工单派发与RBAC权限
简介这份资源是面向高校计算机专业学生与Java开发学习者的毕业设计完整文档围绕社区养老服务管理系统的设计与实现展开适合作为Spring Boot项目实战与论文写作的参考范本。压缩包内仅含1个doc文件约3.69MB内容涵盖研究背景与意义、技术选型、功能模块设计、RBAC权限管理、ER图与数据库建模及结论等章节结构完整、论述清晰。系统后端采用Spring Boot框架前端使用Vue3结合Java语言与MySQL数据库构建划分管理者、工作者、医护人员和普通用户四类角色并设计用户管理、健康管理、活动组织、紧急救援、家政服务等功能模块。目前已有360人学习下载读者可借此了解从需求分析到数据库建模的完整开发思路掌握基于角色的权限控制实现方式并参考其论文组织框架与写作规范为自身毕业设计选题与开发提供可借鉴的实践路径。1. 社区养老系统到底在管什么从一张床位台账说起很多团队做社区养老管理系统第一反应是把它当成老人信息增删改查结果上线三个月就被护理站弃用。真正跑得起来的系统管的是三件事老人档案与能力评估、服务工单的派发与核销、床位与补贴的台账对账。这三件事背后是民政口径的数据上报要求也是社区养老机构每天要面对的实操压力。这个标题指向的是一套基于 Spring Boot 的 B/S 管理系统前端常见做法是 Vue3 加后台管理模板数据库用 MySQL权限模型用 RBAC 把管理员、街道管理员、护理员、家属分成不同角色。它适合两类人一类是计算机专业做毕业设计或课程设计的学生需要一套能跑通、能答辩、能写论文的完整工程另一类是想切入养老信息化赛道的小团队需要一个能二次开发的地基。下面按需求怎么拆、库表怎么建、接口怎么写、权限怎么控、坑在哪的顺序讲透代码和参数都能直接抄。2. 需求拆解与库表设计把养老业务翻译成 12 张核心表2.1 先分清四类角色再谈功能清单社区养老系统的功能不是拍脑袋列的它由角色驱动。常见做法是先把角色定死再倒推每个角色能看到什么菜单、能调什么接口。角色核心诉求典型菜单系统管理员账号、角色、权限、字典维护用户管理、角色管理、菜单管理街道/机构管理员老人建档、工单派发、补贴审核老人档案、服务工单、补贴台账护理员接单、上门服务、拍照核销我的工单、服务记录、打卡家属查看老人状态、预约服务、评价老人详情、服务预约、评价反馈功能清单落到模块上一般是六块老人档案管理、能力评估、服务工单、床位管理、补贴台账、系统管理。这里要提醒一句能力评估不是可选项民政上报要求里有 ADL日常生活活动能力评分缺了这块系统在真实场景里就是半成品。2.2 12 张核心表的字段与关系库表设计决定了后面接口好不好写。我一般会先画 ER 关系再落 SQL。核心表如下-- 老人档案主表 CREATE TABLE elder ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(32) NOT NULL COMMENT 姓名, id_card VARCHAR(18) UNIQUE NOT NULL COMMENT 身份证号, gender TINYINT DEFAULT 0 COMMENT 0未知1男2女, birth_date DATE COMMENT 出生日期, address VARCHAR(255) COMMENT 居住地址, care_level TINYINT DEFAULT 0 COMMENT 护理等级0自理1半自理2不能自理, status TINYINT DEFAULT 1 COMMENT 1在住0离世2迁出, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_id_card (id_card) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT老人档案; -- 能力评估记录表 CREATE TABLE elder_assess ( id BIGINT PRIMARY KEY AUTO_INCREMENT, elder_id BIGINT NOT NULL, adl_score INT COMMENT ADL总分, assess_date DATE COMMENT 评估日期, assessor VARCHAR(32) COMMENT 评估人, remark VARCHAR(500), INDEX idx_elder (elder_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT能力评估; -- 服务工单表 CREATE TABLE service_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) UNIQUE NOT NULL COMMENT 工单号, elder_id BIGINT NOT NULL, service_type TINYINT COMMENT 1助餐2助浴3助医4助洁, nurse_id BIGINT COMMENT 护理员ID, plan_time DATETIME COMMENT 计划上门时间, finish_time DATETIME COMMENT 完成时间, status TINYINT DEFAULT 0 COMMENT 0待派单1已派单2已完成3已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_elder (elder_id), INDEX idx_nurse (nurse_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT服务工单;字段设计上有几个关键点。id_card加唯一索引防止重复建档care_level用 tinyint 而不是字符串方便统计工单表用order_no而不是自增 ID 对外暴露避免被遍历。status字段一定要在注释里写清每个值的含义否则半年后自己都看不懂。2.3 补贴台账与床位表为什么不能省很多毕设版本把补贴台账砍掉觉得是财务的事。但社区养老的补贴是按床位、按护理等级、按月核算的没有台账表系统就只是个通讯录。床位表要记录床位号、房间号、当前入住老人、床位状态补贴台账要记录老人、月份、补贴类型、金额、审核状态。这两张表和工单表通过elder_id关联形成老人—服务—补贴的闭环。建表时注意字符集统一用utf8mb4排序规则utf8mb4_general_ci。MySQL 5.7 和 8.0 在默认排序规则上有差异8.0 默认是utf8mb4_0900_ai_ci如果团队里有人用 5.7 有人用 8.0导出 SQL 时容易报排序规则不兼容统一指定utf8mb4_general_ci能省掉这类玄学问题。3. Spring Boot 后端落地从实体到接口的最小闭环3.1 项目分层与依赖选型后端用 Spring Boot 2.6.x 或 2.7.x 都行3.x 需要 JDK17如果学校机房还是 JDK8就老老实实用 2.7.x。持久层用 MyBatis-Plus比原生 MyBatis 少写一半 XML。权限用 Spring Security 加 JWT这是 RBAC 落地的标准组合。pom.xml关键依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency /dependencies版本号不要乱升。MyBatis-Plus 3.5.3.1 和 Spring Boot 2.7.x 是经过大量项目验证的组合升到 3.5.5 以上有时会和某些 Spring Boot 小版本冲突报NoSuchMethodError这类问题排查起来很费时间。3.2 老人档案接口一个完整的 CRUD 示例以老人档案为例走一遍 Controller、Service、Mapper 的完整链路。RestController RequestMapping(/api/elder) public class ElderController { Autowired private ElderService elderService; // 分页查询支持按姓名和护理等级过滤 GetMapping(/page) public ResultPageElder page( RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String name, RequestParam(required false) Integer careLevel) { PageElder page elderService.pageQuery(pageNum, pageSize, name, careLevel); return Result.success(page); } // 新增老人身份证号唯一校验在 Service 层做 PostMapping public Result? add(RequestBody Valid Elder elder) { elderService.saveElder(elder); return Result.success(); } }Service 层的唯一校验不能只靠数据库唯一索引因为索引报错信息对用户不友好。常见做法是先count查一次再插入同时数据库索引兜底防止并发下重复。public void saveElder(Elder elder) { Long count elderMapper.selectCount( new LambdaQueryWrapperElder().eq(Elder::getIdCard, elder.getIdCard())); if (count 0) { throw new BizException(该身份证号已建档); } elderMapper.insert(elder); }参数说明pageNum从 1 开始pageSize建议限制上限 100防止有人传 10000 把数据库拖垮。careLevel用Integer而不是int因为它是可选过滤条件null表示不过滤。3.3 工单派发与状态流转工单是系统里状态最多、最容易出 bug 的模块。状态流转必须用状态机思路不能随便 update。public void dispatch(Long orderId, Long nurseId) { ServiceOrder order orderMapper.selectById(orderId); if (order null) { throw new BizException(工单不存在); } if (order.getStatus() ! 0) { throw new BizException(只有待派单状态才能派发); } order.setNurseId(nurseId); order.setStatus(1); orderMapper.updateById(order); }这里的关键是每次状态变更前先校验当前状态而不是直接覆盖。护理员完成服务时同样要先判断状态是不是 1再改成 2 并写入finish_time。如果跳过校验会出现已取消的工单被完成这种脏数据对账时非常头疼。工单号生成建议用日期 随机数或雪花算法不要用自增 ID 拼字符串否则并发下容易重复。常见做法是SOyyyyMMdd 6 位随机数配合数据库唯一索引兜底。4. Vue3 前端与 RBAC 权限菜单和按钮怎么按角色渲染4.1 Vue3 项目结构与请求封装前端用 Vue3 加 Vite 加 Element Plus这是目前后台管理系统最省事的组合。目录结构按api、views、router、store、utils分层。请求封装用 axios统一处理 token 和错误码。// utils/request.js import axios from axios import { ElMessage } from element-plus import { useUserStore } from /store/user const service axios.create({ baseURL: import.meta.env.VITE_API_BASE, timeout: 10000 }) service.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers[Authorization] Bearer userStore.token } return config }) service.interceptors.response.use( res { if (res.data.code ! 200) { ElMessage.error(res.data.msg || 请求失败) return Promise.reject(res.data) } return res.data.data }, err { ElMessage.error(err.message) return Promise.reject(err) } ) export default servicebaseURL从环境变量读开发环境指向http://localhost:8080生产环境指向部署地址。token 放在请求头Authorization里后端 Security 过滤器解析。响应拦截器统一处理业务错误码避免每个页面都写一遍错误提示。4.2 动态路由与按钮级权限RBAC 落到前端分两层菜单级和按钮级。菜单级靠后端返回当前用户的菜单树前端动态注册路由按钮级靠自定义指令v-permission控制显隐。// 自定义指令按钮级权限 export const permission { mounted(el, binding) { const userStore useUserStore() const perms userStore.permissions || [] if (!perms.includes(binding.value)) { el.parentNode el.parentNode.removeChild(el) } } }用法是el-button v-permissionelder:add新增老人/el-button。权限标识建议用模块:操作的格式比如elder:add、order:dispatch、subsidy:audit和后端PreAuthorize里的字符串保持一致方便对照。动态路由的常见做法是登录后调/api/user/menus拿到菜单树前端用router.addRoute逐个注册。这里有个坑刷新页面后动态路由会丢失需要在路由守卫里判断如果 store 里没有菜单数据就重新拉一次否则会出现白屏。4.3 后端接口权限校验后端用 Spring Security 的方法级注解做校验配置类里开启EnableGlobalMethodSecurity(prePostEnabled true)。PreAuthorize(hasAuthority(order:dispatch)) PostMapping(/dispatch) public Result? dispatch(RequestBody DispatchDTO dto) { orderService.dispatch(dto.getOrderId(), dto.getNurseId()); return Result.success(); }权限字符串从数据库加载用户登录时查角色对应的权限列表塞进UserDetails的 authorities。这样改权限只需要改数据库不用重新发版。注意hasAuthority和hasRole的区别hasRole会自动加ROLE_前缀如果数据库里存的是order:dispatch这种不带前缀的就用hasAuthority。5. 避坑与排查上线前必须过的 5 道坎5.1 现象中文姓名查询查不到英文正常原因数据库连接 URL 没指定字符集或者表字符集是latin1。MySQL 5.7 默认字符集在部分安装方式下不是utf8mb4。解决连接 URL 加useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai建表时显式指定DEFAULT CHARSETutf8mb4。已经建好的表用ALTER TABLE elder CONVERT TO CHARACTER SET utf8mb4转换。5.2 现象登录后刷新页面跳回登录页原因token 存在 Pinia 里刷新后内存清空路由守卫判断未登录。解决token 同时存 localStoragestore 初始化时从 localStorage 读取。注意 localStorage 里的 token 要设过期时间或者后端 JWT 本身带exp前端解析后判断是否过期。5.3 现象工单列表分页总数不对原因MyBatis-Plus 分页插件没配置或者配置了但Page对象没传对。解决确认配置类里有MybatisPlusInterceptor并添加了PaginationInnerInterceptor且DbType指定为MYSQL。如果总数始终是 0检查是不是用了自定义 XML 查询但没写 count 语句。5.4 现象护理员能看到所有工单越权了原因查询工单时没按当前登录用户过滤只做了角色菜单控制没做数据级权限。解决在 Service 层根据当前用户角色拼接查询条件。护理员角色加nurse_id 当前用户ID街道管理员加street_id 当前用户街道。这类行级权限最容易漏测试时一定要用不同角色账号交叉验证。5.5 现象MySQL 8.0 连接报 SSL 错误原因MySQL 8.0 默认要求 SSL 连接而连接串没配 SSL 参数。解决连接 URL 加useSSLfalseallowPublicKeyRetrievaltrue。生产环境如果要求 SSL就配置证书不要图省事直接关掉。开发环境关掉没问题但要在配置注释里写清楚避免误带到生产。6. 让系统经得起答辩和真实使用的两个进阶技巧第一个技巧是给工单加操作日志。很多系统只记录最终状态出了问题查不到是谁改的。常见做法是建一张oper_log表记录操作人、操作类型、目标 ID、变更前后状态、时间。用 AOP 切面在 Service 方法上打注解自动写入不侵入业务代码。答辩时老师问你怎么保证数据可追溯这张表就是答案。OperLog(module 工单, type 派发) public void dispatch(Long orderId, Long nurseId) { ... }切面里从SecurityContextHolder拿当前用户从方法参数拿目标 ID变更前后状态可以在切面里查一次再对比。日志表数据量大了之后按月分表或者定期归档到历史表避免拖慢主库。第二个技巧是补贴台账的月度对账。不要等用户手动点生成台账而是用定时任务每月 1 号凌晨跑一次把上月已完成工单按老人和护理等级汇总生成待审核台账。用 Spring 的Scheduled就能做cron 表达式0 0 1 1 * ?。生成后发一条站内消息给街道管理员提醒审核。这样系统从被动记录变成主动驱动业务使用率会明显不一样。我自己踩过最深的坑是早期版本把能力评估做成了纯文本备注结果民政上报要结构化数据时全部返工。后来养成习惯凡是政策要求上报的字段一律结构化建表宁可多几张表也不要塞进一个remark字段里。希望帮到你。本文还有配套的精品资源点击获取