SpringBoot宠物诊所管理系统毕业设计:从架构到落地实践
2026/9/20 18:33:46 网站建设 项目流程

简介:基于Spring Boot的小型哺乳类宠物诊所管理系统毕业论文,适合软件工程、计算机科学与技术等专业的学生用于毕业设计参考或课题研究。论文针对传统宠物诊所手工记录、纸质档案导致的管理效率低下问题,系统阐述了基于Spring Boot框架和MySQL数据库的设计方案,涵盖宠物档案、医生排班、预约挂号、宠物病历、药品管理等核心模块,并给出了从需求分析、总体设计到具体实现与测试的完整流程。压缩包内共1个doc文件,大小8.49MB,包含中英文摘要、目录及后续详细章节,结构清晰,便于直接查阅和二次修改。目前已有69人学习下载。阅读该文档可以掌握Spring Boot在管理信息系统中的落地方法,理解数据库表设计与业务模块划分思路,对完成同类毕业论文或开发宠物诊所管理系统有实际参考价值。 我见过太多人一上来就埋头写代码,结果系统做完了,论文憋不出来;或者论文写了一大半,发现系统功能对不上。这篇就先把“怎么想”和“怎么做”串起来,再给关键代码和SQL,保证你能稳稳落地。

1. 内容整体设计与思路拆解

1.1 为什么选SpringBoot做毕设,而不选SSH或SSM

先说结论:SpringBoot不是“炒作产物”,它是目前中小型管理系统最合适的主选框架,尤其适合宠物诊所这类业务连贯、表结构清晰、接口模式固定的项目。

  • 自动配置极大降低装配成本:SSM时代你要写一堆XML去声明SqlSessionFactory、事务管理器、视图解析器,SpringBoot把这一坨全包了,我们只需要引入spring-boot-starter-webspring-boot-starter-jdbc(或mybatis-spring-boot-starter),再配一个application.yml就能跑起来。
  • 内嵌Tomcat,少了环境配置的坑:毕业设计要答辩演示,最怕“我本地能跑,你机器上起不来”。SpringBoot内嵌Tomcat后,打包成单个Jar就能启动,评审老师用IDEA打开或直接java -jar都能跑,环境一致性带来的麻烦直接被干掉。
  • 生态成熟,资料多:你在网上搜到的教程、面试题、踩坑帖,八成以上都是SpringBoot语境,这意味着你遇到问题更容易找到答案。对于一个毕设项目来说,可维护性和可求助性就是生命线。

一套典型的SpringBoot + MyBatis-Plus + MySQL + Vue(可选)架构,既能满足“前后端分离”的亮点,又不至于为了分离而分离。如果你的论文要求必须有前端页面,我建议用Thymeleaf模板引擎或直接Vue脚手架都行,但别把精力全耗在前后端交互的花活上,后文我会说怎么把握这个度。

1.2 小型哺乳类宠物诊所的“特殊性”在哪

很多人会把宠物诊所做成“简化版医院管理系统”,一上来就是挂号、收费、药房,结果做得四不像。小型哺乳类宠物(常见的就是兔、龙猫、仓鼠、豚鼠、刺猬、蜜袋鼯等)和猫狗医院有个明显区别:

  1. 体重基数小,剂量敏感:比如兔子的抗生素剂量是按“mg/kg”精细计算的,猫狗的常规用药量不经过换算直接给兔子用会出事。系统里最好单独维护“体重记录”和“剂量换算”字段,治疗单上要有体重作为辅助信息。
  2. _species字段必须单独建表或枚举:不同物种的生理指标参考范围差异极大,没必要在病例表里堆字符串,应该设计species表,记录科属、参考体温、参考心率等基础指标。
  3. 疫苗和驱虫计划差异大:兔子每年接种疫苗的种类和猫狗完全不同,仓鼠和龙猫甚至没有常规疫苗。所以“疫苗管理”模块不能写死,必须做成可配置的“疫苗种类 + 适用物种”的关联表。

系统不要贪大,核心在于挂号、接诊、开具治疗单、药品库存、收费记录这几个闭环。小型诊所通常没有住院部,所以床位管理、护工排班这类功能可以砍掉,省下的时间去打磨核心流程。

1.3 选题定位:学术价值和工程能力的平衡

毕业论文这东西,答辩老师看的不是你用了多少新潮技术,而是你能不能把一个小问题讲透。所以题目里如果只有“管理系统”四个字,很容易被打上“低难度、大路货”的标签。但如果你在论文里把**“Restful API设计 + 基于RBAC的权限控制 + 药品库存的乐观锁防超卖 + 多条件复合查询”**这几个点讲清楚,那技术深度就完全不一样了。

我的建议是,论文里的“技术路线”部分重点写三块:

  • 为什么用MyBatis-Plus而不是纯MyBatis?因为代码生成器能直接生成Entity/Mapper/Service/Controller,省下大量重复CRUD时间,又能保留自定义SQL的灵活性。
  • 为什么权限模型用简单的用户—角色—菜单三张表,不引入Spring Security?因为毕设评审更看重“你能说清楚访问控制的逻辑”。自己写拦截器(HandlerInterceptor)处理登录态和角色校验,反而更能展示你对Spring MVC生命周期的理解。
  • 为什么数据库字段都用is_deleted逻辑删除、用create_time/update_time自动填充?因为这是企业项目里的实际习惯,不是教科书里的Demo。把这些细节写进论文,是明显的加分项。

2. 核心细节解析与实操要点

2.1 技术栈选型和版本搭配

不要一上来就追最新版,SpringBoot 3.x虽然已经很成熟了,但很多第三方starter的兼容性仍需注意。稳妥起见,用SpringBoot 2.7.x,对应Java 8或11都能跑,MyBatis-Plus版本选3.5.x,MySQL用5.7或8.0都支持。

组件推荐版本说明
JDK1.8 或 11别用17,避免一些老依赖不兼容
SpringBoot2.7.182.7是3.x之前最后的稳定长线版本
MyBatis-Plus3.5.3自带分页插件、代码生成器
MySQL5.7 / 8.08.0注意时区配置
Druid1.2.20连接池,有监控页面,论文截图好看
Lombok1.18.30简化实体类代码
Hutool5.8.x工具类,写毕设省很多事

提示:如果你的机器上已经装好的是JDK17,那直接升到SpringBoot 3.1.5也可以,MyBatis-Plus要换成mybatis-plus-spring-boot3-starter,就是依赖名多一个-spring-boot3。别混用,混用启动必报错。

2.2 数据库设计的“三张核心表”思路

诊所系统的表不在多,而在关联清晰。我建议最少要有这几张表:user(登录用户)、role(角色)、menu(菜单权限)、pet_owner(宠物主人)、pet(宠物档案)、appointment(挂号/预约)、medical_record(病例/接诊记录)、prescription(治疗单/处方明细)、drug(药品)、stock_log(库存流水)、payment(收费记录)。

给出pet表的核心DLL,因为这是整个系统的门面:

CREATE TABLE `pet` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `owner_id` bigint(20) DEFAULT NULL COMMENT '主人ID', `name` varchar(50) NOT NULL COMMENT '宠物昵称', `species` varchar(30) NOT NULL COMMENT '物种:兔/龙猫/仓鼠/豚鼠...', `breed` varchar(50) DEFAULT NULL COMMENT '品种', `gender` tinyint(1) DEFAULT NULL COMMENT '0-公 1-母', `birthday` date DEFAULT NULL COMMENT '出生日期', `weight` decimal(5,2) DEFAULT NULL COMMENT '体重kg', `chip_number` varchar(30) DEFAULT NULL COMMENT '芯片/耳标号', `is_deleted` tinyint(1) DEFAULT '0', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_owner_id` (`owner_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='宠物档案表';

注意这几点:

  • weight不能用int,必须decimal。
  • species必须单独能扩展,如果哪一天诊所开始接诊雪貂,不用改Java代码,只改字典。
  • 逻辑删除字段是MyBatis-Plus的全局配置项,有了is_deleted,所有诗人的delete操作自动变成update,这个在论文里一定提。

然后medical_record表:

CREATE TABLE `medical_record` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `appointment_id` bigint(20) DEFAULT NULL, `pet_id` bigint(20) NOT NULL, `vet_id` bigint(20) NOT NULL COMMENT '接诊医生ID', `weight_at_visit` decimal(5,2) DEFAULT NULL COMMENT '就诊时体重kg', `temperature` decimal(4,1) DEFAULT NULL COMMENT '体温℃', `symptom` varchar(500) DEFAULT NULL COMMENT '主诉/症状描述', `diagnosis` varchar(500) DEFAULT NULL COMMENT '诊断结果', `advice` varchar(500) DEFAULT NULL COMMENT '医嘱/护理建议', `is_deleted` tinyint(1) DEFAULT '0', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_pet_id` (`pet_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='接诊记录表';

这里需要单独解释一下:一个小型诊所,患者体重的历史变化趋势是最重要的数据之一。医生给兔豚鼠看诊时,通常要对比上次体重,如果体重掉得快,问题就严重。所以weight_at_visit单独存,不引用pet.weight。这个细节就是在论文里体现“你懂业务”的好素材。

2.3 接口设计原则:别堆CRUD,要有业务含义

很多人的Controller长成这样:

@GetMapping("/list") public Result list() {...}

这种写法虽然也没错,但答辩的时候很难讲出花来。我建议把接口设计成“业务动作”而不是“数据操作”:

操作接口路径方法说明
新客挂号/api/appointment/bookPOST创建挂号记录,可选绑定宠物ID
接诊开单/api/medical-record/createPOST创建病历 + 可选择关联处方
药品入库/api/drug/stock-inPOST写库存流水,自动更新库存
收费结算/api/payment/payPOST创建收费记录,可打印小票
宠物档案查询/api/pet/profile/{petId}GET返回宠物信息+历史就诊摘要

这样的好处是论文的第4章“系统模块设计”特别容易写,每个模块对应一个业务动作,功能点清晰可见。另外,接口的路径动词尽量用book/create/stock-in/pay这种,不要全是save/delete/update,否则看起来就是个增删改查生成器,技术含量被低估。

3. 实操过程与核心环节实现

3.1 快速初始化项目:三分钟让页面能访问

我习惯先跑通一个“空转系统”,再往里填业务代码。以下是关键步骤,照着做不会有坑。

第一步:去Spring Initializr(https://start.spring.io/),选Maven、Java 8、SpringBoot 2.7.18,依赖勾选Spring WebMySQL DriverLombok。如果你不想用在线生成,IDEA自带的Spring Initializr也行。

第二步:修改pom.xml,加MyBatis-Plus和Druid依赖:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.20</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.18</version> </dependency>

第三步:配置application.yml,最容易被忽略的是MySQL8的时区,不配会报错:

server: port: 8080 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/pet_clinic?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: yourpassword mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto logic-delete-field: is_deleted logic-delete-value: 1 logic-not-delete-value: 0

这段配置里有一个关键点:logic-delete-field是全局逻辑删除。之后你的所有@TableLogic注解的字段都自动生效,不用在每张表手动写条件了。

第四步:写一个测试Controller,启动项目访问http://localhost:8080/api/health,看到“OK”就说明环境没问题。这时候再开始用代码生成器生成所有CRUD代码。

3.2 用MyBatis-Plus代码生成器,少写50%的重复代码

这里直接给一份我常用的生成器核心代码,你需要改的是url和表名:

public class CodeGenerator { public static void main(String[] args) { FastAutoGenerator.create("jdbc:mysql://localhost:3306/pet_clinic?serverTimezone=Asia/Shanghai", "root", "yourpassword") .globalConfig(builder -> builder.author("你的名字") .outputDir("D:/pet-clinic/src/main/java") .dateType(DateType.ONLY_DATE)) .packageConfig(builder -> builder.parent("com.example.petclinic") .entity("entity") .mapper("mapper") .service("service") .serviceImpl("service.impl") .controller("controller")) .strategyConfig(builder -> builder.addInclude("pet", "pet_owner", "appointment", "medical_record", "prescription", "drug", "stock_log", "payment") .entityBuilder().enableLombok() .controllerBuilder().enableRestStyle()) .execute(); } }

生成完,直接得到一个完整可跑的CRUD。但用之前必须处理好三处:

  1. Controller:所有生成的Controller继承一个BaseController,统一返回Result<T>Result类自己封装,包含codemsgdata三个字段。
  2. Service:在生成的IService基础上扩展自定义业务方法。比如“挂号”不能只save一个Appointment,还要判断这个宠物当天是否已有未完成的挂号,否则会造成重复挂号。
  3. 分页查询:MyBatis-Plus分页插件需要手动注入配置类,网上很多教程会漏掉这一步,漏了selectPage永远返回全量数据。

分页插件核心配置如下,复制进一个MybatisPlusConfig.java

@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }

3.3 登录和权限:用拦截器实现,讲得清楚又不引入重框架

不推荐在毕设里硬塞Spring Security或Sa-Token,除非你有十足把握讲明白里面的过滤器链和认证流程。更务实的做法是自定义注解 + HandlerInterceptor。

先定义一个@RequirePermission注解:

@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RequirePermission { String value(); // 例如 "appointment:book" }

然后在WebMvcConfigurer里注册拦截器:

@Component public class AuthInterceptor implements HandlerInterceptor { @Autowired private UserService userService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) handler; RequirePermission annotation = handlerMethod.getMethodAnnotation(RequirePermission.class); if (annotation == null) { annotation = handlerMethod.getBeanType().getAnnotation(RequirePermission.class); } if (annotation == null) { return true; // 无权限标注的接口直接放行 } // 从request header中获取token(可以是JWT或UUID) String token = request.getHeader("Authorization"); if (StrUtil.isBlank(token)) { throw new BizException(401, "未登录"); } // 从Redis或数据库查出用户信息和角色权限 // 判断用户是否拥有annotation.value()对应的权限,没有则抛403 return true; } }

这个方案好在哪?答辩时老师问你“权限怎么实现的”,你直接画一张图:请求进来 → 拦截器前置处理 → 取出token → 查权限 → 放行/拒绝。他很难再追问到底层,因为你讲的本身就是Spring MVC的实现机制。

3.4 核心业务闭环:挂号到收费怎么串起来

整个流程应该有状态流转,不能各表各管各的。我用一张表来说明:

步骤操作涉及表状态变更
1登记宠物主人和宠物pet_owner/pet创建档案
2预约挂号appointment状态:待就诊
3接诊medical_record状态:就诊中
4开处方prescription记录药物明细,扣减库存
5收费payment状态:已收费
6离院appointment状态:已完成

这里最容易被坑的是第4步“扣减库存”。如果不加锁,两个医生同时给两只兔子开同一盒药,库存就可能变负数。解决方案很简单:更新SQL加库存条件。

@Override @Transactional(rollbackFor = Exception.class) public boolean createPrescription(PrescriptionDTO dto) { // 1. 校验药品存在且未删除 Drug drug = drugMapper.selectById(dto.getDrugId()); if (drug == null || drug.getIsDeleted() == 1) { throw new BizException("药品不存在"); } // 2. 扣库存:乐观锁 where stock >= quantity int rows = drugMapper.reduceStock(dto.getDrugId(), dto.getQuantity()); if (rows == 0) { throw new BizException("库存不足"); } // 3. 写入处方明细 // 4. 写库存流水(stock_log) return true; }

reduceStock对应的Mapper方法:

@Update("UPDATE drug SET stock = stock - #{quantity} WHERE id = #{drugId} AND stock >= #{quantity}") int reduceStock(@Param("drugId") Long drugId, @Param("quantity") Integer quantity);

这个扣库存方案比SELECT ... FOR UPDATE更轻,又能在并发下保证不超卖。论文里可以写“采用乐观锁机制实现库存一致性”,虽然也是常见方案,但能把实现细节讲到SQL级别,就很有说服力了。

4. 常见问题与排查技巧实录

4.1 数据库连接报错“Public Key Retrieval is not allowed”

我用的是MySQL 8.0,第一次连数据库就报了这个。解决办法就是在JDBC URL末尾加&allowPublicKeyRetrieval=true,同时把useSSL=false加上。很多教程不讲这个,导致新手卡在连库环节半小时起步。

4.2 MyBatis-Plus自动填充的create_time不生效

这是重灾区。实体类里的字段要加@TableField(fill = FieldFill.INSERT),同时写一个MetaObjectHandler的Bean,不然MyBatis-Plus根本不知道什么时候填值。

@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }

如果数据库表里字段名是create_time,实体里是createTime,还要检查mapUnderscoreToCamelCase是否为true(MyBatis-Plus默认开启),否则映射不上。

4.3 有个表死活查不出数据,但SQL没问题

经常是逻辑删除字段的问题。如果你表里字段叫deleted,但全局配置写的是is_deleted,那MyBatis-Plus自动拼的SQL会是WHERE deleted=0,可你表里根本没有deleted字段,直接报错或查空。一定要让全局配置的表字段名和实体注解字段对应。

另外,如果某张表不需要逻辑删除,就在实体类上不用加@TableLogic,这样生成的SQL不会带逻辑删除条件。

4.4 前端Vue打包后刷新404

如果你用了Vue Router的history模式,把前端打包成dist后扔到SpringBoot的resources/static下,刷新某个内部路由页面会404。解决方法有两个:一是在后端写个转发规则,把非API的请求都转发到index.html;二是路由改成hash模式,最省事。我建议直接用hash模式,虽然URL多个#,但论文演示和部署都少很多麻烦。

4.5 SpringBoot启动秒退,没有任何日志

大概率是端口被占。用netstat -ano | findstr 8080查一下,把占用进程kill掉,或者改端口。

还有一种少见但典型的情况:自定义的ApplicationListener@Component里抛了异常,但启动日志被logback配置吞了。最保险的办法是先在application.yml里把日志级别设成debug,看完整启动日志再定位。

5. 部署、演示和论文材料的“三重配合”

很多人的毕设输在临门一脚:系统功能没问题,论文内容也对,但答辩演示时“卡住了”。结合我带过项目的经验,额外提几个用在演示环节的细节:

  1. 准备一套演示数据:不要只塞几条测试数据,至少要有5只宠物、2个医生、10条就诊记录、若干药品库存。这样演示“主人查宠物历史病历”时,界面才不空。
  2. 数据库设计文档整理成表格放进论文附录:表名、字段名、类型、说明列清楚,这既是论文内容的一部分,也是答辩时老师必翻的一页。
  3. 启动时先启动MySQL,再启动SpringBoot,最后确认http://localhost:8080能不能打开。我在实际演练中发现,最容易出的岔子不是代码,而是数据库没启动或密码改了没同步到yml。

如果你还有富余时间,给系统加一个banner.txt,启动控制台打出一个宠物爪印的ASCII图案。这是很小但很有辨识度的彩蛋,答辩时如果老师注意到,气氛会松弛很多。SpringBoot默认会读取classpath:banner.txt,不用任何额外配置。

说到底,选题落到“小型哺乳类宠物诊所”这个场景,优势在于业务足够小而完整,又能体现你对细分领域需求的观察。做一个能流畅演示、论文能自圆其说、答辩敢被追问的系统,比堆砌十个无用的功能模块有价值得多。照着上面的架构和细节走,你离这个目标应该不远了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询