十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue宠物医院管理系统:从表设计到前后端部署全解析

SpringBoot+Vue宠物医院管理系统:从表设计到前后端部署全解析 1. 项目全貌为什么选择SpringBootVue这套组合拳做宠物医院管理系统这个选题先明确一点这套系统本质上是一个典型的Web信息管理系统核心痛点是解决传统宠物医院靠纸质登记、Excel台账管理客户和宠物信息的低效问题。项目需要覆盖前台展示、预约挂号、就诊记录、药品库存、收费统计这些完整的业务流程所以当时选型时直接锁定了SpringBootVue这个组合。在目前的Java Web生态里这对组合基本属于“最优解”SpringBoot负责后端接口和业务逻辑Vue负责前端交互和页面渲染前后端完全分离开发效率高后期维护也不容易陷入“改一处崩一片”的泥潭。这个选题很适合做毕业设计或者自学练手原因有三第一业务场景清晰宠物医院的流程相对固定需求边界好把握不会被复杂业务逻辑拖垮第二技术栈主流SpringBoot和Vue都是招聘市场上高频出现的技能点做完这个项目写在简历上是实打实的加分项第三数据模型有代表性涉及到一对多、多对多等多种关系设计比如一个宠物主人可以养多只宠物、一只宠物可以多次就诊、一次就诊可以开多种药品这些都是经典的表关系设计题。需要先说明一下由于原始需求没有给出详细的业务细节和代码下文中的模块划分、表结构、接口设计等内容是我基于这类管理系统的常见需求补充的完整实现方案你可以直接当作项目开发的蓝本使用。说到B/S架构的选择现在很多老式诊所还停留在单机版管理软件甚至纸质台账的时代但B/S架构的优势非常明显不用安装客户端任何有浏览器的设备都能访问医生在诊室电脑上开完药方前台在另一台电脑马上就能看到收费信息这就是Web化的核心价值。同时SpringBoot内置Tomcat打包成Jar直接部署运维成本极低不会出现“部署一次配置三天”的尴尬局面。2. 业务需求拆解与功能模块规划2.1 核心角色与权限边界划分宠物医院管理系统表面上是对宠物信息的管理但本质上是对整个医院运营流程的信息化重构。我梳理需求的时候第一件事不是画页面而是先把医院的日常运营流程捋清楚。一个标准的宠物医院日常运转主要涉及三个角色前台接待人员、宠物医生、系统管理员。前台负责接待客户、登记宠物信息、安排预约挂号、办理缴费退费医生负责查看自己的预约列表、填写电子病历、开具处方、管理住院记录管理员负责维护系统基础数据比如药品字典、收费项目、员工账号、统计报表等。这三个角色的权限边界必须清晰否则会出现医生不小心删掉了收费记录、前台误改了药品库存这类低级事故。所以设计方案里权限部分不能只做页面隐藏更要在后端接口层做拦截校验。比如前端隐藏“药品库存管理”的菜单按钮只是第一层防线后端接口也要校验当前登录用户的角色防止懂点前端知识的用户直接调接口绕过限制。这个Design思路是很多毕设项目容易忽略的关键点权限控制必须是前后端双重校验只靠前端隐藏就等于把大门钥匙挂在门外。2.2 功能模块划分与前端页面地图整个系统按业务场景拆成了几个核心模块每个模块对应一组前端页面和一个后端Controller用户登录与注册模块包含账号密码登录、验证码校验、JWT身份认证前端对应登录页和注册页。首页数据看板模块展示今日预约数、今日就诊量、当前住院宠物数量、本周营业收入等统计卡片前端用ECharts绘制折线图和柱状图管理员和医生看到的统计维度不同。宠物信息管理模块宠物主人信息、宠物档案品种、年龄、疫苗情况、过敏史、宠物与主人的关联绑定。前端对应宠物列表页、新增/编辑弹窗、宠物详情页。预约挂号模块前台代预约或客户在线预约支持按医生排班时间选择号源系统自动校验号源是否冲突。前端对应预约日历页、号源选择组件、我的预约列表页。诊疗管理模块医生填写电子病历包含主诉、检查结果、诊断结论、医嘱、处方药品明细。前端对应就诊队列页、病历填写页、处方药选择组件。药品库存模块药品入库、出库、库存预警低于阈值自动标红、效期管理。前端对应药品列表页、入库记录页、库存预警弹窗。收费管理模块根据处方自动生成收费单支持现金、微信、支付宝等支付方式的记录收费流水关联挂号单和处方单。前端对应收费页、退费页、流水查询页。系统管理模块员工账号管理、角色分配、基础数据字典。前端对应员工列表页、角色分配弹窗。2.3 为什么这样规划模块更合理模块划分遵循了“高内聚、低耦合”的原则。比如处方和收费虽然业务上是前后关联的但设计成独立的表结构好处是收费失败时不会污染处方数据、统计营收时可以直接从收费流水表聚合不需要连表查询处方明细。另外预约和挂号这两个概念很多人容易混淆。我的设计里是这样区分的预约是提前约定一个时间段来看病状态是待就诊挂号是当天实际到店后确认就诊、分配医生和诊室状态变为就诊中。两者是递进关系而不是重叠关系。这样的话医生端看到的“今日就诊队列”就是挂号后的列表不会混入放了鸽子的预约记录实际使用体验会清爽很多。用生活化一点的说法这套模块划分相当于把医院的业务流切成了“进店前预约、进店时挂号登记、看诊中病历处方、看诊后收费取药”四个阶段每个阶段由对应的模块承接数据一环扣一环又彼此独立想改哪个环节都不会牵一发动全身。3. 数据库设计表结构是系统的地基3.1 核心数据表梳理数据库设计这部分我花的时间比写代码多得多。因为后续所有的接口逻辑、统计报表都是建立在表结构之上的表设计不合理后面写SQL的时候会想骂人。这个项目我最终设计了9张核心表覆盖了整个业务闭环user表用户信息表主键用户ID字段包括用户名、加密密码、真实姓名、手机号、角色类型1管理员、2医生、3前台使用BCrypt加密存储密码防止数据库泄露后密码明文暴露。pet_owner表宠物主人信息表关联user表额外记录住址、电子邮箱、备注信息。pet表宠物档案表关联owner表记录宠物名、品种、性别、出生日期、体重、绝育状态、过敏史、疫苗记录。doctor表医生信息表关联user表记录职称、所属科室、擅长领域、排班时间段使用JSON格式存储一周的排班规则。appointment表预约记录表关联pet表、doctor表记录预约日期、时间段、状态待就诊、已取消、已完成、预约来源。medical_record表电子病历表关联pet、doctor记录主诉、诊断结果、检查数据、医嘱、下次复诊建议。prescription表处方表关联medical_record记录总金额、开方时间、备注。prescription_item表处方明细表关联prescription和drug记录药品用量、频次、天数、小计金额。drug表药品字典表记录药品名、规格、生产厂家、库存数量、预警阈值、零售价、效期。payment表收费流水表关联prescription和appointment记录应收金额、实收金额、支付方式、收费状态、收费时间。3.2 关键表设计细节与为什么这么做有几个设计细节值得展开说说。第一密码字段一定要用BCrypt加密这是Spring Security自带的加密工具不是简单的MD5。MD5现在用彩虹表基本可以秒破而BCrypt会自动加盐并计算多轮哈希同样的密码每次加密结果都不同安全性完全不是一个等级。第二处方和处方明细拆成两张表这是一个经典的一对多设计。为什么不能把药品直接塞在prescription表里因为一条处方可能开3种药药品字段就会变成值列表无法满足后续“统计哪种药卖得最好”的报表需求。拆成明细表后每个明细行单独存储一种药要统计销量直接groupBy药品ID聚合效率极高。第三医生排班使用JSON格式存储。比如一位医生一周七天每天上午下午是否有号直接维护一个长度为7的数组每个元素又是一个包含上午号源数和下午号源数的对象。用关系型数据库的标准范式来看这不太“正规”但实际使用中排班规则就是这样一个固定结构的配置JSON反而最直观查询时直接解析这个字段即可不需要为排班规则专门建一套表。第四所有表都添加create_time和update_time字段使用MyBatis Plus的自动填充功能统一维护。这个习惯非常重要一旦后续需要排查数据问题或者做数据对账没有时间字段等于没有破案线索。3.3 数据初始化与模拟数据准备系统开发调试阶段最痛苦的事情是没有真实数据可用。我的做法是写一个CommandLineRunner接口的实现类在系统启动时自动执行数据初始化逻辑创建默认管理员账号admin/admin123、创建演示用医生和前台账号、插入一批药品字典数据、生成未来7天的医生排班。为什么强调这个设计因为你在开发前端页面的时候列表页、详情页、统计图表都需要数据支撑才能调样式和逻辑。没有数据就开发前端等于闭着眼睛开车。而且这还能顺带验证后端的批量插入逻辑是否正确一举两得。模拟数据要尽量贴近真实场景比如药品名称不要写什么“药品A”要写“阿莫西林克拉维酸钾片”这种真实宠物药这样页面效果看起来才真实可信。4. 后端SpringBoot项目搭建与核心实现4.1 项目初始化与依赖配置后端项目我用Spring Boot 2.7.18版本为什么不用最新的Spring Boot 3.x核心原因是兼容性问题3.x版本基于Jakarta EE很多老牌的第三方库比如一些工作流引擎、旧版MyBatis插件还没有完全适配而且JDK要求17以上。对于学习型项目和毕设来说Spring Boot 2.7 JDK 8 MyBatis Plus这套组合经过了大量项目的验证资料最多、踩坑案例也最全遇到问题一搜就有答案。创建项目时用IDEA的Spring Initializr勾选以下依赖Spring Web提供REST接口能力、MySQL Driver数据库驱动、Lombok简化实体类代码、Spring Validation参数校验。项目创建完成后手动引入MyBatis Plus和JWT相关依赖。MyBatis Plus相比原生MyBatis的最大优势就是单表CRUD操作不需要手写SQL它的BaseMapper接口已经把常用的增删改查封装好了对于这类业务逻辑并不复杂的系统来说开发效率能提升至少30%。4.2 统一返回结果与全局异常处理后端接口设计上必须定义统一的返回结构。我的ResponseResult类包含三个字段code状态码200表示成功500表示服务器异常401表示未认证、message提示信息、data业务数据。所有接口都返回这个结构前端Axios拦截器直接判断code是否为200简化了前端的错误处理逻辑。同时使用RestControllerAdvice注解实现全局异常处理。这个设计非常实用比如业务中遇到“库存不足”、“号源已被占满”这类情况可以直接抛出BusinessException并携带自定义错误信息全局异常处理器统一捕获后包装成ResponseResult返回给前端。如果不做全局异常处理就要在每个Controller里写try-catch代码会膨胀得非常难看而且容易遗漏。4.3 JWT登录认证与接口安全用户登录成功后后端生成JWT令牌返回给前端。JWT的结构包含三部分Header声明算法、Payload存放用户ID、用户名、角色、Signature签名验证。前端收到token后存储在localStorage中之后每个请求在HTTP请求头加上Authorization字段后端通过拦截器解析token识别当前用户。JWT相比传统的Session方案最大优势是服务器不需要存储会话状态天然支持横向扩展。但要注意一个安全隐患JWT默认是不加密的只是Base64编码所以千万不要在Payload中存放密码等敏感信息。我的做法是Payload中只存用户ID和角色需要用户其他信息时再根据ID查询数据库。拦截器配置有几个细节登录接口和注册接口必须放行不需要token否则用户永远无法登录校验token时如果发现过期或签名无效返回401状态码而不是200响应信息提示“登录状态已过期请重新登录”前端收到401后自动清除本地存储的token并跳转到登录页。还有一个容易被忽视的点CORS跨域配置要单独写一个配置类否则前端项目运行在8080端口、后端运行在9090端口时浏览器的同源策略会拦截所有请求。4.4 核心业务接口逻辑实现预约挂号接口是整个系统最核心的一个难点。用户提交预约请求时携带医生ID、预约日期、时间段。后端逻辑分几步执行先查询该医生在指定日期指定时间段是否还有剩余号源这里需要注意并发问题——两个客户同时在最后一刻预约同一个号都查询到“有号”然后都插入成功就会造成超卖。解决方案是使用数据库的悲观锁SELECT ... FOR UPDATE或者乐观锁版本号比对。在这个场景下我用的是数据库行级锁开启事务先锁住该排班记录再检查剩余号源数剩余大于0则扣减并插入预约记录否则抛出“号源已满”异常。挂号确认接口相对简单校验预约记录存在且状态为待就诊后更新状态为就诊中同时创建一个空的病历记录草稿医生后续在这个草稿上补充内容。就诊完成接口需要处理一个事务中包含多步操作的情况更新病历内容、生成处方单、批量插入处方明细、扣减药品库存、生成待收费流水。这五个操作要么全部成功要么全部回滚所以必须用Transactional注解包裹。特别注意库存扣减的细节扣减前要检查药品可用数量扣减时也要用乐观锁防止并发扣减成负数扣减后判断剩余库存是否低于预警阈值如果是则把库存状态标记为“需补货”。收费确认接口相对独立更新收费流水状态为已支付同时更新关联的预约记录状态为已完成。这里特意没有把收费和开方放在同一个事务里因为现实中客户可能开了药但暂不缴费或者分多次支付如果强行包在一个事务里开方后马上扣费失败会导致处方数据丢失不符合实际业务场景。4.5 统计报表接口的SQL写法首页看板需要展示一周每天的就诊量和营收数据这个时候MyBatis Plus的封装方法就不够用了需要手写SQL。用一条SQL配合日期函数就能搞定SELECT DATE(create_time) AS date, COUNT(*) AS visit_count, SUM(amount) AS revenue FROM payment WHERE create_time DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE(create_time)这条SQL的关键在于DATE_SUB函数计算7天前的日期作为起点然后按天分组聚合。前端拿到结果后遍历补全缺失的日期比如某天没有收入数据库不会返回该行的记录前端需要默认补0就可以直接传给ECharts生成图表了。5. 前端Vue项目从搭建到组件封装5.1 前端项目结构与路由设计前端我用的Vue 2 Vue Router 3 Element UI这套组合。为什么不用Vue 3和Element Plus考虑到这个项目的定位是学习和毕设Vue 2的Element UI组件库生态成熟网上资料多遇到问题好排查而且很多学校老师对Vue 2更熟悉。如果你是自学且时间充裕用Vue 3也没有问题核心逻辑是相通的。项目目录按功能模块划分api目录存放所有接口请求方法views目录存放页面组件router目录配置路由表store目录Vuex存放全局状态utils目录存放Axios封装、JWT工具类等公共方法。路由设计的核心点在于权限控制路由表的meta字段中配置requiresAuth是否需要登录和roles允许访问的角色数组。前端路由守卫在每次跳转前检查用户是否有权限访问目标路由router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else if (token to.meta.roles) { const userRole store.state.user.role if (!to.meta.roles.includes(userRole)) { next(/403) } } else { next() } })5.2 Axios封装与接口请求统一管理前端所有HTTP请求都应该走Axios实例而不是每个组件直接引入Axios。我的封装方式是这样的创建一个axios实例设置baseURL为/api然后在请求拦截器中添加token到请求头在响应拦截器中统一处理错误码service.interceptors.response.use( response { const res response.data if (res.code ! 200) { if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.message || 请求失败)) } return res.data }, error { return Promise.reject(error) } )拦截器的作用相当于给所有请求装了一层“安检门”不用每个接口都写重复的错误处理逻辑。后端返回401就强制跳登录页返回其他错误码就弹出Message提示框。5.3 Element UI常规组件与复杂交互实现考虑到热词中提到了“vue播放m3u8”这里顺带补充一个前端多媒体处理的扩展思路。虽然宠物医院管理系统本身不涉及视频播放但在做Web项目时难免会碰到直播回放、监控视频这类需求。宠物医院如果后续扩展“在线查看住院宠物监控”的功能就会用到m3u8视频流播放。m3u8本质是苹果推出的基于HTTP的流媒体传输协议将一个完整的视频拆分成无数个小的TS分片文件播放器逐个加载播放。前端播放m3u8流媒体通常使用video.js配合videojs-contrib-hls插件或者使用hls.js库。这不属于本项目的核心范围仅作为参考。回到系统本身Element UI的表格组件和表单组件是使用频率最高的。表格组件需要处理分页逻辑我的做法是前端维护pageNum和pageSize两个变量切换页码时重新调用接口获取数据。表单组件则配合el-dialog弹窗使用新增和编辑共用一个弹窗通过当前操作类型动态改变标题和提交地址。表单校验规则使用el-form自带的rules属性比如手机号必须11位且为数字药品库存不能为负数这些都在前端先拦一道减少无谓的请求。5.4 大文件上传与第三方组件集成宠物医院系统的一个加分项是支持宠物照片上传和病历附件上传。前端用Element UI的el-upload组件后端用Spring Boot的MultipartFile接收文件存储到服务器本地磁盘或者OSS对象存储中。如果使用本地存储要注意拼接文件访问路径时解决跨域问题同时限制上传文件大小比如单文件不能超过5MB否则一次性上传超大图片会阻塞后端线程池。登录验证码功能可以使用前端canvas手写绘制生成4位随机字符图片点击刷新验证码。当然更省事的方式是引入kaptcha或者easy-captcha这类第三方工具库后端生成图片并存储正确答案到Redis缓存key为UUIDvalue为验证码文本前端提交登录请求时携带UUID和用户输入的验证码后端比对通过才允许登录。6. 开发实测中的典型问题与排查方法6.1 Vue开发服务器的端口占坑问题前端开发服务器默认端口是8080但SpringBoot内置Tomcat也默认启动在8080端口这就会导致后启动的那个直接报端口占用错误。开发阶段我的解决方案是修改SpringBoot的配置文件把服务端口改成9090前端正常运行在8080然后在前端的Vue配置文件里配置代理转发把/api开头的请求都转发到9090端口。这样既解决端口冲突问题又规避了开发环境的跨域限制。6.2 后端返回时间格式错乱默认情况下SpringBoot返回的时间是一串数字类似“1700000000000”这种时间戳格式前端直接展示会让人一头雾水。解决方案是在application.yml中配置统一的日期格式化格式同时给实体类的时间字段添加JsonFormat注解。注意Java 8新增的LocalDateTime和旧版Date序列化行为不一样最好项目从开始就统一使用LocalDateTime避免混用。6.3 前端打包后的路由404错误开发环境一切正常但项目执行npm run build打包部署到服务器后刷新页面出现404。这个问题的根因是Vue Router的history模式前端路由是虚拟路径服务器上并没有对应的物理文件刷新时服务器去找路径对应的文件找不到就返回404。解决办法有两个方向第一修改nginx配置添加try_files指令让所有路径都回退到index.html第二直接把Vue Router改成hash模式URL上多个#号虽然不太好看但兼容性最好。我一般建议在开发环境用history模式调试正式部署用hash模式省心省力。6.4 MyBatis Plus的分页失效MyBatis Plus使用分页时很多人会忘记配置分页插件导致page对象的records字段返回null。这个问题排查思路很简单检查配置类中是否注入了PaginationInnerInterceptor。Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }另一个高频问题是多表关联查询时无法直接使用BaseMapper的分页方法因为复杂的连表查询需要手写mapper.xml。碰上这种情况注意Page参数要作为第一个参数传入自定义Mapper方法MyBatis Plus才能自动拦截SQL并拼接LIMIT语句。6.5 会话保持与token过期处理浏览器关闭后重新打开系统发现用户需要重新登录这是预期的设计行为因为token保存在localStorage中localStorage不会因为关闭标签页而清空。但用户如果手动清除浏览器缓存token就会丢失。这里有一个隐藏的坑前端很多请求是异步发的同一时间可能有多个请求都带着过期的token后端对每个请求都返回401前后端拦截器就会触发多次“跳转登录页”的逻辑。优化方案是在响应拦截器中加一个标志位如果已经处于跳转状态就不再重复跳转避免出现“无限跳转”的诡异现象。6.6 点图标显示出错时的排查顺序如果页面加载时出现空白或者部分图标显示为方框大概率是Element UI的图标字体文件没有被正确加载。检查顺序是先看浏览器控制台有没有404请求确认icon字体文件是否在打包产物中再检查引入方式是否正确Element UI的样式文件必须完整引入不能只引入组件不引入样式。这类静态资源问题九成都是打包配置问题优先排查publicPath的配置是否正确。7. 常用问题速查表为了方便快速定位问题我把这个项目从开发到部署的典型坑整理成了一张速查表问题现象根因分析解决方案前端请求后端接口提示跨域前后端端口不一致未配置CORS后端添加CorsFilter或者配置类也可在前端Vue配置代理转发登录接口一直返回401token未正确传递检查Axios请求拦截器是否添加了Authorization请求头表格数据展示正常但分页数据总是固定几行分页参数传递错误检查请求参数是否包含当前的pageNum和pageSize提交表单后数据显示正常但刷新页面就消失数据未成功写入数据库可能事务回滚查看后端控制台是否有异常日志检查事务注解和SQL执行情况库存减成负数并发超卖问题无锁机制改用数据库行锁或者乐观锁更新库存搜索功能无响应后端接口或前端条件拼接有误在浏览器开发者工具Network面板查看请求参数是否符合预期打包后图片和CSS路径404Vite或Webpack的publicPath配置错误设置正确的base或publicPath路径使用相对路径页面显示内存溢出一次性加载过多数据使用分页查询不要全表查询返回前端表格使用懒加载修改了后端代码但刷新页面没效果SpringBoot服务未重启或热部署未生效添加spring-boot-devtools依赖开启热部署修改了前端代码但浏览器表现不变浏览器缓存了旧版JS文件强制刷新或者打包时给文件名添加hash值来打破缓存8. 项目后续还能怎么扩大战果这个系统做完之后并不是一个终点因为宠物医院这个领域可挖的点真的不少。个人觉得有三个方向可以继续扩展每一个拿出来都能单独做成一个有意思的项目第一个方向是可视化大屏。现在的统计看板只是简单的卡片加图表如果要做成大屏展示用Vue DataV或者纯ECharts做大屏适配把今日接诊量、营收趋势、宠物品种分布、医生工作量排行全部放到一个屏幕上放在医院大厅或者管理层办公室视觉冲击力完全不一样。第二个方向是消息通知服务。预约成功、就诊提醒、疫苗到期提醒这些都是刚需场景。接入短信服务商API或者用WebSocket做站内信推送让客户体验上一个台阶。这涉及的技术点是消息队列或者定时任务技术含量比CRUD高不少。第三个方向是移动端适配。现在的管理端是给医院内部员工用的但如果要开放给宠物主人在手机上自助预约挂号、查看宠物病历和疫苗记录就得考虑做移动端H5或者小程序后端接口是通用的改造成本主要在移动端适配和接口权限调整。从长远来看这类管理系统的价值不在于技术有多炫酷而在于你真正理解了业务场景能用技术去解决实际的效率问题。做项目的过程本质上是拿真实世界的业务去破解技术难题的过程。学到了这套从需求分析、表结构设计、接口规划、前后端联调、部署上线的完整链路比单纯背面试题要扎实得多。做一个宠物医院管理系统看起来只是一个Web项目实际上你做的是把一家医院的门诊流程、药品管理、账目结算全部搬到线上这种将现实世界数字化的思维方式才是这个项目带给你最大的收获。
返回列表