年初接手了一套疗养院的业务系统改造对方机构还在用Excel表格维护老人档案、床位和收费记录每个月月底核算账单要折腾好几天。后来我基于Spring Boot做了一套管理系统后端用Spring Boot前端配Vue数据库选MySQL整套源码跑通之后直接扔到对方服务器上到现在用了三个多月稳定性和效率都很能打。这篇文章把整个项目从功能拆分、表结构设计到运行踩坑的过程完整记录下来适合准备做管理系统开发、或者正在折腾此类系统的同学直接参考。先说明一点这不是一个炫技型项目没有微服务、没有分布式、没有复杂的算法设计。它就是一套典型的前后端分离信息管理系统价值在于把业务梳理清楚、把代码写规矩、把坑提前填好。你拿到手之后改改配置就能跑跑起来之后照着业务继续往深了做也不费劲。1. 为什么要单独搞一套疗养院系统而不是直接套用通用后勤系统1.1 疗养院场景和普通酒店、医院管理差在哪不少第一次接触这个领域的人会有个误区疗养院不就是酒店住宿 简单医疗吗拿一套通用的公寓管理系统改一改不就行了实际上手之后你会发现完全不是一回事。疗养院的日常管理核心是人而且是很特殊的一批人。他们不是短期住客也不是普通门诊病人而是长期驻留、需要持续观察健康状况、每天产生各类康养服务消耗的群体。这意味着系统的业务主线至少要覆盖三条入住周期管理、健康照护过程管理、按日累计的费用核算。这三条线彼此交织比如一位老人每次身体指标的记录都会影响护理等级护理等级一变每天的费用也跟着变。这种动态联动通用系统很难处理。普通公寓系统只关心房间有没有人住、租金交没交医院HIS系统关心的是门诊挂号、医嘱、划价收费。疗养院系统夹在中间既要有房间和床位管理又要有健康档案和照护记录还要把长期住宿产生的费用按天算清楚、按月出账单。这种业务定位本身就决定了它需要一套独立的建模思路。1.2 我按哪几条主线来拆需求这套系统上手之前我先和机构的一线工作人员聊了两次把日常流程走了一遍最后确定了四个核心模块组接待入住与退住、健康档案与照护执行、护理排班与任务分配、费用计费与报表核销。外围再补充系统管理模块用来管账号、角色和权限。这四个模块组不是拍脑袋分的它们分别对应机构里真实存在的岗位角色。前台接待管入住和访客护士站管档案和照护计划护工组管排班和任务执行财务月底要跑账单。每个角色打开系统看到的页面、能操作的功能都不同这就是后面权限设计的基本出发点。1.3 功能边界要提前定死否则开发会失控做这类系统最怕需求蔓延。比如有人提出要加在线问诊家属视频探视智能硬件心率接入听起来很美好但第一版做进去必然拖垮进度。我当时和对方明确约定第一版只做人工信息录入与流程管理不做硬件对接不做实时消息推送不做复杂图表分析。所有扩展功能留到二期的接口空位。这个边界现在已经写进代码里了。比如健康指标记录表预留了一个source_type字段将来如果接智能手环数据来源标记成设备即可费用表明细里预留了exception_flag将来处理减免、折扣也不会改表结构。前期边界越清晰后期改起来越舒服。2. 技术栈选型为什么这套组合最省心模块和表是怎么设计的2.1 三个核心组件选择的理由后端用Spring Boot这个没什么悬念企业级管理系统的标配生态成熟、招人容易、出了问题网上一搜一大把解决方案。我选的是Spring Boot 2.7.x版本配合JDK 8稳定优先。安全认证用Spring Security加JWT持久层用MyBatis-Plus因为单表CRUD它能自动生成大部分方法我只需要手写那些复杂的统计SQL。前端用Vue 2.6加Element UI。可能有朋友会问现在Vue 3都用起来了为什么还守着Vue 2原因很实在Element UI的表格、表单、弹窗组件非常成熟网上现成的管理后台模板也多改起来快。如果从零搭Vue 3加Element Plus组件API变了、表单校验方式也变了开发量会明显增加。对于快速交付、稳定好用为目标的系统Vue 2完全够。这不是技术落后是工程选型的现实考量。数据库用MySQL 5.7单机部署日活几十人数据量撑死几十万条完全不担心性能。如果后面真有高并发诉求加一层Redis缓存就够了但现在完全没必要。2.2 核心表设计的几个关键点表结构是这个系统的灵魂字段名可以改但表关系错了后面全是坑。我总共设计了20多张表按业务域可以分成四组人员与组织sys_user登录账号、sys_role角色、sys_user_role账号角色关联、resident疗养人员档案床位与空间building楼栋、room房间、bed床位健康照护health_record健康指标记录、nursing_plan照护计划、nursing_task照护任务执行、medication_record用药记录运营计费visit_record访客记录、shift_record排班记录、charge_item收费项目、resident_account在住账户、charge_detail每日计费明细有几个设计细节值得专门说。resident表把人员信息和入住状态分开设计。人员基本表里存姓名、证件号、家属联系方式、既往病史入住状态则用单独字段stay_status控制值分别是0待入住、1在住、2已退住、3转院。这样设计的直接好处是退住之后人员档案不会跟着消失财务核对历史数据随时能查到这个人当时住哪床、哪位护工负责。床位和房间不合成一张表。为什么因为疗养院里一个房间常见两张床甚至三张床但两张床的朝向、床位费、有无护栏这些细节可能不同。床是独立收费单位房间只是空间归属。凡是涉及收费的地方我全部落在bed这一层从根上避免了算一间房还是算一张床的混乱。费用相关的表设计我前后改了三版最终采用收费项目配置表 每日流水明细的模式。charge_item定义每一类收费项的日单价比如一级护理60元/天餐费30元/天单人间床位费80元/天系统每天晚上跑批按疗养人员当前状态和所在床位自动生成当天的charge_detail记录。月底要出账单直接按人汇总这个表的记录即可每一条都有日期、项目、金额、生成来源没有一笔是拍脑袋算出来的。2.3 后端项目结构参考拿到源码之后你会发现包结构基本如下建议二次开发也保持这个习惯com.example.convalescent ├── common // 通用类统一返回、异常处理、常量 ├── config // 配置跨域、安全、MyBatis-Plus分页 ├── controller // 接口层 ├── service // 业务逻辑层 ├── mapper // 数据访问层 ├── entity // 数据库实体 ├── dto // 前端交互对象 ├── vo // 视图返回对象 ├── util // 工具类 └── security // JWT相关entity里的类和数据库表一一对应dto和vo存在的意义是不要直接把数据库实体暴露给前端。比如查询入住列表时前端需要显示房间号、床位号、护理等级、负责人但不需要id_card和medical_history这些敏感字段所以我单独定义了一个ResidentListItemVO只返回必要字段。这个小习惯能少掉非常多返回了不该返回的数据的安全问题。3. 把核心流程串起来入住、照护、排班、费用这套链路怎么走3.1 入住登记到底要做几步入住登记不是简单地点一个新增人员。在系统里前台操作员的完整流程是这样的先录入疗养人员的基本信息包括姓名、证件、紧急联系人、过往病史再选择楼栋、楼层、房间、床位床位要多条件筛选——只有显示空闲状态的才能选然后根据评估结果设置护理等级这个等级会直接影响照护计划和后续计费。整个登记过程在界面上看起来是一个多步骤表单但后端实际上分了四步事务操作插入人员档案、占用床位、初始化在住账户、生成首日费用记录。这四步必须放在同一个Transactional里任何一步失败都要整体回滚否则很可能出现人登记成功了但床还是空闲的这种脏数据。这一步我在源码里写得很清楚针对它做了完整的单元测试因为它是整个系统中出错概率最高的入口。无风险的小技巧是入住完成后立即在主页面刷新床位状态让前台人员养成确认的习惯。3.2 健康档案与照护计划的联动逻辑疗养人员的健康管理是这个系统里人情味最重的模块。每天护士会录入体温、血压、血糖、精神状态这几个核心指标数据进入health_record表。系统不会自动做诊断判断但会在连续指标异常时给出页面级别的提示方便护士关注。照护计划表nursing_plan则记录每位在住人员的护理要求比如每2小时翻身一次三餐后测血糖每周二次康复训练。这些计划项不会孤零零存在它们会生成到对应护工的每日任务清单里护工完成任务后逐项勾选。管理者在报表页面能看到今天的护理任务完成率是多少。这个模块设计上有一个容易忽略的细节所有健康记录都只做追加不做修改。业务上不存在把昨天的血压从130改成120这种场景如果录错了正确做法是重新录入一条并将旧记录标记为作废。这样虽然会导致表数据量偏大但每一份健康数据都可追溯真出现纠纷时这是一份说得清楚的证据链。3.3 排班模块比想象中更简单也比想象中更实用排班功能很多管理系统里都有但疗养院的排班有两个特点一是班次稳定基本就是白班、夜班、休息三态循环二是排班结果直接关联到护工的任务分配范围。我没有做复杂的自动排班算法就做了一个简单实用的按人设置周模板一键生成下周排班表的功能。排班生成后系统会在每天凌晨根据值班人员名单把当前在住疗养人员按房间楼栋分配到对应的责任护工名下。这样一来护工登录系统看到的任务列表天然就是今天该我管的那几个人不用自己满屏找。这个设计逻辑简单但实际使用反馈非常好因为一线人员最怕花时间找到底谁负责谁。3.4 每日计费跑批和退住结算的算法解释计费是财务最关心的部分也是代码里注释最多的地方。系统里维护了一个定时任务每天凌晨2点执行逻辑分三步查出所有stay_status 1的在住人员对每个人根据当前床位所属的收费项、护理等级对应的护理费、固定餐费各生成一条charge_detail累计到对应resident_account的当月应缴金额字段中。退住结算的核心算法是按天确认边界。假设某位老人6月15日上午10点办理退住系统中退住日期记录为6月15日最后一天的费用仍然收取因为床位当天无法再安排其他人入住。如果退住时间在上午8点前且机构有明确的半日规则则通过配置项settle_type切换为半日计费。这种细节在需求讨论阶段就要聊明白写进代码就是charge_detail里每条记录都带上精确的生成时间和业务日期。月底对账时财务只需要打印月度费用汇总报表按人展示上月结余、本月新增费用、已收款、应收余额四个数字。再也不需要对着Excel手工拉公式了。4. 权限设计不同角色看到的东西不能一样这一点必须较真4.1 四种角色和对应的数据范围这套系统的账号一共分四类系统管理员、医生/护士、护工、前台/财务。如果再细分还有只读性质的领导查看看板但第一版没做统一合进管理员角色。角色不同不仅操作按钮不一样连数据范围都完全不同。这块我用了最简单的方案sys_role表里有一个data_scope字段取值为ALL全部数据、DEPARTMENT本部门数据、SELF仅自己相关数据。比如医生角色配DEPARTMENT只能看到自己负责的疗养区护工配SELF任务列表只显示分配给自己的那部分。查询的时候MyBatis-Plus的拦截器会根据当前登录用户的data_scope自动拼接SQL条件。这么做比纯靠前端隐藏按钮靠谱得多。前端隐藏只是体验层面的保护真正的数据隔离必须落在SQL层面。否则我只要绕过页面直接调接口就能看到别人的健康档案这对医疗数据来说是不可接受的安全事故。4.2 JWT登录态令牌怎么发、怎么验、怎么续期登录接口的逻辑比较常规核对用户名密码校验通过后生成一个JWT令牌返回前端。这个令牌里我封了三个信息userId、userName、roleKey。前端拿到后存到localStorage每次请求在Authorization头里带上。服务端的校验链路是自定义的JwtAuthenticationFilter拦截所有/api/**请求解析请求头里的令牌如果令牌有效就把用户信息放进SecurityContext如果无效或过期直接返回401。然后用Spring Security的PreAuthorize(hasRole(ADMIN))在Controller方法上做接口级权限控制比如删除床位这个操作只允许管理员调用。令牌有效期我设置的是8小时实际用下来勉强够一天。系统里没有做自动续期思路是前端在每次请求收到401后跳回登录页重新登录。对于内部管理系统来说这个体验可以接受。如果想更顺滑可以在后端加一个刷新令牌接口但为了控制复杂度第一版我没加。4.3 参数校验和防注入服务器要当不信任的边界所有前端传来的数据后端一律默认不可信。实体类上加了JSR 303校验注解比如姓名不能为空、手机号必须符合格式、金额必须大于零Controller层通过Validated统一触发校验。SQL注入方面MyBatis-Plus的QueryWrapper本身用#{}预编译风险低。真正的隐患出在用字符串拼接SQL的统计报表里。我在代码审查时专门定了一条规矩任何动态排序字段必须走白名单校验。比如报表允许按姓名、入住日期、费用排序前端传来的排序关键字如果不在白名单里直接拒绝执行。这一条能挡住大多数人为构造的注入尝试。5. 拿到源码之后的运行全流程每一步都给你标清楚5.1 环境版本对齐是第一道门槛想直接运行最怕的就是版本不对齐。这套系统的后端用的是JDK 1.8Maven 3.6Spring Boot 2.7.14MySQL 5.7前端用的是Node.js 14.18npm 6.14Vue 2.6.14Element UI 2.15如果你本机装的是JDK 17直接跑Spring Boot 2.7大概率会报Unsupported class file major version这不是项目代码的问题是JDK版本太高。解决方式很简单要么把JDK切回1.8要么把pom.xml里的Spring Boot版本升级到2.7以上并同步调整依赖。我建议保持JDK 8因为整套配置都是按这个版本调试好的何必给自己找麻烦。5.2 初始化数据库不是简单执行一个SQL文件那么简单源码里带了一个sql/init.sql文件里面包含建库命令、全部建表语句和初始数据。执行之前要注意两点。第一字符集统一用utf8mb4否则带生僻字的人名、特殊字符可能存不进去。建库语句默认是CREATE DATABASE convalescent_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;第二初始数据里预置了一个管理员账号和几个演示用的角色、菜单。项目完成后第一件事就是改掉这个默认密码。很多系统出安全问题都是从默认口令开始的这是老生常谈的教训。5.3 后端配置文件和启动步骤把数据库初始化好之后打开后端的application.yml把数据源改成你自己的地址、账号、密码server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/convalescent_db?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0然后在项目根目录执行mvn clean package -DskipTests java -jar target/convalescent-admin.jar看到Started Application in XX seconds就说明后端起来了。建议启动后先访问http://localhost:8080/api/auth/login用管理员账号测试一下登录接口能否正常返回Token这一步能快速确认后端和数据库连通性。5.4 前端启动和代理联调前端的src/utils/request.js里封装了Axios实例基础URL指向一个代理地址开发环境下由Vue CLI的代理转发到后端baseURL: /apivue.config.js里的代理配置大概是这样devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这里有个非常容易踩的坑后端context-path已经设置了/api前端代理指向后端时如果再用/api前缀就要让代理把路径原样转发不要再重写。如果代理配置里多了一个pathRewrite: {^/api: }请求到了后端就会变成/api/api/xxx直接404。这个问题能坑掉一半的新手。前端启动命令npm install npm run serve如果npm install特别慢把镜像切到国内源再装一般一分钟内能装完。浏览器打开http://localhost:8081输入初始账号密码登录即可。5.5 如果本地跑不起来先从这三个方向定位我见过很多人卡在启动环节实际九成是这三个原因端口被占用。后端8080前端8081都可能有其他程序占用尤其前端调试工具经常占用8080。后端改端口改application.yml里的server.port前端改端口改vue.config.js里的port。前端代理里的target地址记得同步修改。MySQL时区不匹配。连接串里不写serverTimezoneAsia/Shanghai有时候会报The server time zone value错误。解决方案就是连接串带上这个参数同时确保系统时间正常。Maven依赖下载不下来。公司内网环境或者网络受限时依赖可能拉取失败。在maven settings.xml里配置阿里云镜像即可解决。6. 二开扩展方向与踩过的坑复盘6.1 第一张要做好的成本账不是所有功能都要做进系统这套系统交付后对方机构提了很多后续需求但在评审时我都做了排序。真正值得做的几个扩展点我列一下排在后面的直接砍掉。首推做的是统计看板。管理层每天要看的无非就那几个数字当前在住人数、今日新增入住、退住人数、床位使用率、本月应收费用。实现上不需要引入任何报表引擎后端写一个聚合查询接口前端用几个统计卡片加折线图就能搞定。这是投入产出比最高的一项。第二是做消息提醒。当疗养人员的费用余额低于阈值或者健康指标连续异常时通过短信或微信服务号通知家属。这个需要引入第三方消息服务功能本身不复杂但涉及费用和资质第二期再考虑。第三才是硬件对接。智能手环、血压计的数据接入听着很高端但实际部署要考虑设备成本、网络连接稳定性、数据准确率。在机构还没有完成基础设备升级前上了也是摆设。6.2 实际运行中遇到过的三个真问题这套系统上线过程中最有价值的收获反而来自几个意料之外的bug。第一个是定时任务重复执行。最初用Spring自带的Scheduled做每日计费跑批后来因为某次手动触发定时器同一天生成了两遍计费明细。修复方案是引一条执行记录表每次跑批前先检查当天是否已经成功执行过成功则直接跳过。这个幂等检查在财务系统里迟早会遇到越早做好越好。第二个是导出Excel中文乱码。报表导出我用的阿里开源的EasyExcel在本地一切正常部署到服务器后导出的文件名和表头出现乱码。排查了半天发现是Linux服务器的默认编码不是UTF-8。解决方案是在代码里显式指定导出文件名的编码而不是依赖服务器环境变量。这个坑希望你不要遇到遇到了至少知道往这个方向查。第三个是逻辑删除和唯一索引冲突。系统对账户、人员记录做逻辑删除后为了保留历史痕迹同一身份证号再次入住时插入了新的档案记录但身份证号字段上有个全局唯一索引导致新记录插入失败。解决方案是唯一索引改成联合唯一id_card deleted_flag。这个在数据库设计初期就要想清楚否则上线后改库非常被动。6.3 关于源码学习的最后一点建议如果你拿到的这套源码是用来学习的我的建议是不要从Controller开始读而是从数据库表结构和service层开始。表结构承载了业务数据模型service层承载了业务流程判断Controller不过是薄薄的接口转发层。把这两层读懂整个系统的脉络就通了。如果是为了快速交付项目那就先跑起来再对着功能清单逐个验证然后把报表和权限这两块吃透。这两块是最能体现系统价值的地方也是最容易在答辩或验收时被深入追问的部分。这套系统不是什么前沿架构但它是我一手搭起来、真实跑过几个月的作品。对我个人来说最大的收获不是写了几千行代码而是明白了一个管理系统能不能真正落地不在于用了多新的技术而在于业务逻辑有没有掰扯明白。后续我还在持续迭代这套系统下一阶段会优先补上数据大屏和家属端小程序。如果你也在搞类似的系统欢迎多交流踩过的坑已经帮你踩完了剩下的路会好走很多。
阅读完成 · 觉得有帮助?