基于SpringBoot+Vue的动物园动物饲养管理系统设计与实现

发布时间:2026/10/11 3:48:48
基于SpringBoot+Vue的动物园动物饲养管理系统设计与实现 刚做完一个动物园动物饲养管理系统前后端分离架构SpringBoot 3.x Vue 3全家桶。这个项目是给某中型动物园做的既要管动物档案又要盯饲养流程还要处理饲料库存和健康记录一开始接手的时候觉得不就是个CRUD吗真做起来才发现坑不少。这篇就把整个设计和实现过程完整拆一遍从需求分析到数据库设计从权限控制到健康预警再到实际部署时踩过的几个坑全写出来。1. 项目整体设计与技术选型思路1.1 项目背景与核心需求解析先说说这个项目的实际背景。该动物园目前有哺乳类、鸟类、爬行类等共计两百多只动物分布在十多个展区。过去的管理方式基本靠纸质台账 Excel表格饲养员每天手写饲喂记录兽医做健康检查也是纸质登记月底汇总的时候非常痛苦而且历史数据基本没法追溯。举个例子某只动物出现食欲不振想查它过去一周的进食情况要翻好几个本子效率极低。管理方希望做一个系统核心需求归纳下来有这么几条动物档案电子化每只动物有唯一编号记录品种、性别、出生日期、来源、展区位置等基础信息。饲养任务日常化饲养员每天按任务清单执行饲喂、清洁、观察等工作并填写记录。健康管理闭环兽医定期体检、记录疾病与治疗情况系统能对异常指标做提醒。饲料库存联动饲料入库、出库、库存预警和饲喂记录形成数据关联。数据可视化管理层要能看到饲养任务完成率、饲料消耗趋势、动物健康状况统计。需求梳理清楚之后技术选型就比较明确了SpringBoot做后端接口服务Vue做前端管理界面MySQL存业务数据。这套组合的优点是生态成熟、招人容易、部署简单对动物园这种预算有限、运维力量也不强的单位来说是非常稳妥的选型。1.2 为什么选SpringBoot Vue 3而不是其他方案选型这件事我在项目里通常是按“团队熟悉度 生态成熟度 后续维护成本”三个维度来评估的而不是哪个技术新就选哪个。后端选SpringBoot 3.x原因是Spring Data JPA MyBatis双支持这套组合在中小型管理系统中非常成熟遇到复杂查询可以走MyBatis的XML简单CRUD直接用JPA就能搞定。Spring Security做登录认证和权限控制JWT无状态令牌适合前后端分离场景。生态里现成的工具类特别多分页、参数校验、全局异常处理、定时任务都有标准解法开发效率很高。前端选Vue 3 Element Plus原因是Vue 3的组合式API写业务逻辑比Vue 2的选项式更清爽逻辑复用度高。Element Plus的表格、表单、弹窗、树形控件基本覆盖了管理后台90%的场景。Pinia做状态管理比Vuex更轻量TypeScript支持也更好。有的团队喜欢用若依这类脚手架直接改但我觉得系统定制化程度高、业务逻辑复杂的时候从零搭建反而更容易掌控不必为了省初期工作量去适应别人定的框架约束后期改起来更痛苦。1.3 系统整体架构与功能模块划分系统采用经典的前后端分离架构前端通过HTTP接口调用后端服务后端分层为Controller、Service、Mapper三层。整体功能模块如下模块名称核心功能涉及角色系统管理用户管理、角色分配、菜单权限超级管理员动物档案动物基础信息、展区管理、生命状态变更管理员、饲养员饲养管理饲喂任务、饲喂记录、清洁记录饲养员健康管理体检记录、疾病治疗、健康预警兽医饲料管理饲料信息、入库、出库、库存预警库管员统计分析任务完成率、饲料消耗、健康统计图表管理员这个模块划分的出发点很简单每个业务域都有明确的责任人权限边界清晰操作流程形成闭环。举个例子饲养员填写的饲喂记录会自动扣减饲料库存库管员不用等月底盘点就能看到实时消耗这就是模块联动的价值。2. 数据库设计与核心表结构拆解2.1 动物档案表的字段设计动物档案是整个系统的数据根基表设计上既要有基础信息也要考虑状态流转和历史追溯。CREATE TABLE animal_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, animal_code VARCHAR(32) NOT NULL UNIQUE COMMENT 动物编号, animal_name VARCHAR(64) NOT NULL COMMENT 动物名称, species VARCHAR(64) COMMENT 物种, category VARCHAR(32) COMMENT 类别哺乳/鸟/爬行等, gender TINYINT COMMENT 性别0-未知 1-雄 2-雌, birth_date DATE COMMENT 出生日期, acquisition_type VARCHAR(16) COMMENT 来源繁殖/购入/救助/交换, acquisition_date DATE COMMENT 入园日期, enclosure_id BIGINT COMMENT 展区ID, health_status VARCHAR(16) COMMENT 健康状态健康/观察/治疗/隔离, life_status VARCHAR(16) COMMENT 生命状态存活/死亡/转出, is_sterilized TINYINT DEFAULT 0 COMMENT 是否绝育, remark VARCHAR(255) COMMENT 备注, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );字段设计上有几个容易被忽略的点animal_code必须唯一这是每只动物的“身份证号”后续所有业务记录都通过这个编号关联比直接用自增ID更规范也方便线下对应。health_status和life_status分开存是有讲究的。健康状态是可逆的动物生病治好了能恢复生命状态是不可逆的死亡或转出后这条档案就“锁定”了。两个状态混在一个字段里业务逻辑会非常混乱。acquisition_type我建议用字典表管理而不是直接写死枚举因为动物园之间经常会有动物交换后续可能新增“借展”这类的来源。2.2 饲养记录与饲料消耗的关联设计饲养这块是整个系统业务量最大的部分设计时最关键的点是“饲喂记录”和“饲料出库”的联动关系。CREATE TABLE feeding_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, animal_code VARCHAR(32) NOT NULL, feed_id BIGINT NOT NULL, feed_name VARCHAR(64), feed_weight DECIMAL(10,2) COMMENT 饲喂重量(kg), feed_time DATETIME NOT NULL, feeder_id BIGINT COMMENT 饲养员ID, feeder_name VARCHAR(32), remark VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP );这里我设计的是“前端先查饲料信息选中后提交”的方式后端接收到feed_id和feed_weight后在同一事务里做两件事插入feeding_record同时扣减饲料库存表对应记录。如果扣减后库存低于预警阈值自动生成一条补货提醒。事务控制是这个功能的关键。如果先插记录再扣库存中间任何一步失败都会造成数据不一致。SpringBoot里直接用Transactional注解就能搞定量子化操作两件事要么都成功要么都回滚。这里有个实际的业务细节不同动物的饲喂单位不一样大象按“千克”算小型鸟类可能按“克”算。字段类型我都用的是DECIMAL(10,2)单位统一在饲料字典表里维护展示的时候根据单位字段自动换算显示避免数据库里出现“0.05”这种不好理解的数据。2.3 健康档案与预警机制的表结构支撑健康模块我做了三张表体检记录表、疾病治疗表、健康指标阈值表。体检记录表的核心字段是体检日期、体重、体温、心跳、精神状态评价、兽医姓名和备注。每个字段对应一个健康指标的评分区间比如动物体温低于或高于正常范围系统自动标记为“异常”。为了让预警不写死在代码里我建了一张阈值配置表CREATE TABLE health_threshold ( id BIGINT PRIMARY KEY AUTO_INCREMENT, species VARCHAR(64) COMMENT 适用物种, metric_code VARCHAR(32) COMMENT 指标编码temperature/heart_rate/weight, min_value DECIMAL(8,2), max_value DECIMAL(8,2), level TINYINT COMMENT 1-注意 2-危险 );这样做的好处是兽医可以在系统里直接调整某个物种的体温正常范围不用改代码重新部署。比如夏天环境温度高某些爬行动物的活跃体温范围有变化直接在页面上改配置就行这种灵活性是写死枚举给不了的。3. 核心功能实现与实操过程记录3.1 用户认证与RBAC权限控制的落地权限控制这块用的是经典的RBAC模型用户-角色-菜单三级。用户表只管账号密码和基本信息角色表定义权限集合用户和角色做多对多关联。// JWT工具类核心方法 public String generateToken(Long userId, String username, ListString roles) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .claim(roles, roles) .setExpiration(new Date(System.currentTimeMillis() EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); }Spring Security的配置上我重写了SecurityFilterChain放行了登录接口和静态资源其余接口全部要求携带有效Token。不同角色的访问控制是通过自定义注解PreAuthorize(hasRole(ADMIN))加到Controller方法上实现的。实际开发中有个容易踩坑的地方JWT的密钥和过期时间如果写在代码里后续修改必须重新编译部署。我这里是放在了application.yml里通过ConfigurationProperties注入这样运维同学改配置重启就行不用动代码。过期时间设成了12小时白天上班基本不用重新登录安全性和便利性比较平衡。3.2 动物档案管理的核心接口实现动物档案管理模块的难点不在CRUD本身而在“唯一编号自动生成”和“状态变更的联动处理”。编号生成我用的是统一前缀 日期 流水号的方案public String generateAnimalCode(String category) { String prefix categoryMapper.getCodePrefix(category); String datePart LocalDate.now().format(DateTimeFormatter.ofPattern(yyyyMMdd)); int seq animalMapper.getDailySequence(prefix, datePart); return prefix datePart String.format(%03d, seq 1); }比如一只新入园的非洲象类别编码为ELE生成编号就是ELE20250608001。这种编号规则的好处是光看编号就能知道动物类别和入园时间既方便线下登记查找也方便系统内部索引。状态变更的联动处理是另一个关键点。动物从“正常”变为“隔离”系统要自动完成几件事更新动物表状态、给隔离展区负责人生成一个待办任务、在健康模块插入一条观察记录。这套逻辑必须放在同一个事务里否则就会出现“状态变了但饲养员不知道”的情况。前端页面上动物档案列表用了Element Plus的el-table组件分页、搜索、状态筛选都做出来了。编辑表单里展区和类别的下拉选项是从后端接口加载的不写死这样管理员在系统里新增了展区前端下拉选项刷新就能同步出来。3.3 饲养任务派发与饲喂记录填写流程饲养任务派发的思路是管理员按展区配置日常任务模板系统每天凌晨自动生成当天的任务清单分配到对应的饲养员账号下。任务模板表的核心字段是展区ID、任务类型、计划开始时间、计划结束时间、任务内容、指定饲养员ID。生成任务的定时任务我用的是Spring的Scheduled注解Scheduled(cron 0 30 5 * * ?) public void generateDailyTasks() { ListTaskTemplate templates taskTemplateMapper.findAll(); for (TaskTemplate template : templates) { DailyTask task new DailyTask(); task.setName(template.getName()); task.setEnclosureId(template.getEnclosureId()); task.setAssigneeId(template.getAssigneeId()); task.setPlanStartTime(template.getPlanStartTime()); task.setPlanEndTime(template.getPlanEndTime()); task.setStatus(TaskStatus.PENDING); dailyTaskMapper.insert(task); } }定时任务生成之后饲养员登录系统首页就是“我的今日任务”可以看到“未开始”“进行中”“已完成”“已逾期”四种状态。点击“开始”按钮后任务状态变为进行中填写完饲喂记录并提交后状态自动变为已完成。这个过程对饲养员的操作设计做了减法尽量一键操作减少填写负担。因为实际使用这套系统的人不是专业程序员很多饲养员年纪偏大在电脑上操作不熟练界面必须直观按钮必须大交互步骤必须少。我在系统里把表格默认的行高调大了字体也调大了就是为了让这些终端用户操作时不费劲。3.4 饲料出入库与库存预警实现饲料管理模块相对独立逻辑上更接近进销存系统。入库单、出库单、实时库存、预警记录四张表构成了核心闭环。入库操作相对简单填写供应商、饲料名称、数量、单价、入库日期提交后库存增加。出库操作有两种口径一种是从饲喂记录自动扣减的“消耗出库”另一种是饲养员手动填写的“领用出库”比如定期给展区补充一些零食饲料。库存预警的实现比较轻量我在库存表的每个饲料品种上配置了最低库存阈值。每次出库操作完成后检查当前库存是否低于阈值如果低于就插入一条预警记录同时在管理员的待办中心生成一条处理提示。public void deductStock(Long feedId, BigDecimal weight) { FeedStock stock feedStockMapper.selectByFeedId(feedId); stock.setCurrentStock(stock.getCurrentStock().subtract(weight)); feedStockMapper.updateById(stock); if (stock.getCurrentStock().compareTo(stock.getMinStock()) 0) { stockWarningMapper.insert(new StockWarning(feedId, stock.getCurrentStock())); } }这里有个细节比较用的是compareTo而不是直接因为BigDecimal的compareTo和equals行为不同。直接比较0.1和0.10equals返回false但compareTo返回0用compareTo比较数值大小才是正确的。这是我当年在一个财务系统里踩过的坑印象特别深。3.5 数据统计与可视化大屏的实践统计分析模块我做了两类东西一类是后台管理页面的ECharts图表另一类是园区大屏展示页。后台统计包括近30天饲养任务完成率趋势折线图各展区动物分布饼图饲料消耗TOP10柱状图健康异常动物滚动列表这些图表的数据来源是后端接口实时查询聚合出来的不额外建统计表。因为动物园的数据量级很小就算每天每条记录都查一遍数据库压力也完全可以承受。我写的SQL基本都是COUNT、SUM配合GROUP BY加上时间范围条件走索引毫秒级就能返回。大屏展示页用了Vue轮询的方式每30秒刷新一次数据展示当前在线任务量、待办预警数、园区总动物数等核心指标。大屏整体配色是深蓝底 绿色数据比较符合动物园绿色生态的主题。4. 常见问题与排查技巧实录4.1 跨域配置引发的前端联调阻塞前后端分离开发本地联调第一个遇到的就是跨域问题。前端跑在5173端口后端跑在8080端口浏览器直接拦截跨域请求。我的处理是在SpringBoot里写了一个统一的CORS配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(*) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }这里有个关键点容易踩坑前端如果开启了withCredentials携带Cookie后端就不能用allowedOrigins(*)必须使用allowedOriginPatterns(*)否则浏览器会直接拒绝响应。我当时卡了半小时最后打开浏览器控制台才发现的。不过要提醒的是这只是开发环境方便联调的方案。生产环境我建议还是用Nginx做反向代理前端请求/api前缀的统一转发到后端服务从根本上规避跨域问题性能和安全性都更好。4.2 JWT登录态失效导致的操作无响应系统上线后遇到过一个问题用户登录后长时间停留在某个页面没操作超过Token有效期后再点按钮接口返回401但前端没有统一处理这个状态页面看起来就是“点了没反应”。解决方法是加了一个axios的响应拦截器统一处理401状态码service.interceptors.response.use( response { return response.data; }, error { if (error.response error.response.status 401) { localStorage.removeItem(token); router.push(/login); ElMessage.error(登录已过期请重新登录); } return Promise.reject(error); } );这样处理之后用户再次操作时会被自动踢到登录页并收到提示体验就顺了。另外还有一个连带问题用户操作很频繁时每请求一次就要重新校验一次Token虽然性能影响不大但为了更好的体验我在前端做了请求拦截器给每个请求自动附带Authorization: Bearer xxx的Header后端只需要在Security过滤器里校验一次。这个组合用下来整体比较稳。4.3 事务失效问题饲料扣减和记录插入不同步有一次测试时发现饲喂记录已经插进去了但饲料库存没扣减。排查下来是事务注解写在了同一个类内部调用的方法上Spring事务切面默认只对外部调用生效内部自调用不走代理Transactional直接失效了。这个问题的本质是Spring AOP的代理机制。解决思路有两种一是把扣减库存的逻辑拆到另一个Service类中通过注入调用走代理二是用AopContext.currentProxy()显式获取当前代理对象来调用。我采用的是前者把库存操作单独封装到了StockService里逻辑也更清晰后面测试通过后就没再出过问题。另外提一句Transactional默认只在抛出RuntimeException时回滚如果方法里catch了异常没重新抛出事务也会失效。事务里尽量不做try-catch包裹让异常自然抛出由全局异常处理器统一收口。4.4 大数据量导出时的内存溢出风险系统上线一段时间后管理员提出了导出全年饲养记录的需求。当时第一个版本的实现是直接用select * from feeding_record查出所有数据再循环处理几万条数据直接导致内存飙升个别年份数据量大的时候甚至OutOfMemory。后来改成了Streaming查询的方式利用MyBatis的游标读取public void exportFeedingRecords(String year, OutputStream outputStream) { try (SqlSession sqlSession sqlSessionFactory.openSession()) { FeedingRecordMapper mapper sqlSession.getMapper(FeedingRecordMapper.class); CursorFeedingRecord cursor mapper.scanByYear(year); for (FeedingRecord record : cursor) { exportRow(record, outputStream); } } }游标方式不是一次性加载全部数据而是逐行从数据库读取内存占用只有原来的零头。十万条记录的导出从必现OOM变成稳定完成这个改动算是性价比很高的优化。4.5 前端菜单权限刷新后丢失登录后管理系统的基本流程是前端拿到用户信息和角色根据角色动态生成菜单。但我最开始是把菜单数据存在Vuex里一刷新页面Vuex状态清空菜单就丢了页面变成一片空白。后来改成了刷新时重新调用/api/user/info接口根据返回的角色动态重建菜单。这个方案说穿了就是“刷新后重新拉一次用户信息”但能把问题彻底解决。另外要注意的是路由守卫里要处理异步获取用户信息的阻塞避免菜单还没生成路由就跳转的情况。5. 系统部署与运维要点总结5.1 Linux服务器下单服务部署实践部署方案上我最终选择的是单机部署一台Linux服务器Nginx放前端静态文件并做反向代理后端Jar包通过systemd守护进程运行。前端打包用的是npm run build产物放在/opt/zoo/frontend/目录Nginx配置大致如下server { listen 80; server_name zoo.example.com; location / { root /opt/zoo/frontend; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里try_files配置必须要有否则Vue的history模式路由在刷新非首页路径时会报404。这个是前端路由模式的经典问题部署过Vue项目的人应该都不陌生。后端Jar包我用了systemd来管理配了Restartalways进程挂掉会自动拉起再配合一个简单的健康检查脚本每天凌晨检查一次接口可用性有问题就发告警提醒。5.2 MySQL备份与数据安全策略数据是系统的命脉备份策略必须提前定好。我配置了每天凌晨3点自动执行mysqldump全量备份保留最近7天的备份文件#!/bin/bash BACKUP_DIR/opt/backup/mysql DATE$(date %Y%m%d_%H%M%S) mysqldump -uzoo_user -pPassword zoo_db | gzip $BACKUP_DIR/zoo_db_$DATE.sql.gz find $BACKUP_DIR -name *.sql.gz -mtime 7 -delete备份文件我建议至少每月做一次异地拷贝或者同步到云存储防止服务器磁盘故障导致数据全丢。这个项目上线后的第二周就遇到过一次磁盘告警当时要不是提前配了备份清理和异地同步后果还真挺麻烦的。另外管理员账号开启了二步验证因为系统里包含了动物健康数据虽然不算特别敏感但兽医的治疗记录和饲料供应商信息还是应该受到保护能多一层验证就多一层安心。6. 项目复盘与二次开发扩展方向6.1 做得好的地方和遗留的问题复盘整个项目我认为做得比较好的点有三个需求梳理阶段花了足够的时间和园长、兽医、饲养员都做了面对面沟通所以模块划分和角色权限边界都比较清晰后期开发没有出现大范围推倒重来的情况。数据库设计时充分考虑了状态变更的联动性健康状态、生命状态、库存字段都预留了扩展空间。前端交互做了充分的简化考虑到终端用户是饲养员和兽医操作路径很短基本都能在两三步内完成核心操作。遗留的问题也有移动端适配没有做得很完善饲养员在园区里边走边用手机填记录的时候部分页面在小屏下显示不够友好。目前是让饲养员在电脑或平板上完成的但长期来看响应式适配或者做一个轻量移动端是后续值得投入的方向。6.2 二次开发的技术演进建议如果后续要继续扩展我建议优先考虑下面几个方向对接摄像头和RFID设备实现动物自动识别和定位。这样饲养员扫一下动物耳标就能弹出档案不用手动搜索编号效率提升会比较明显。引入物联网传感器做环境监测。展区的温度、湿度、空气质量实时采集超出舒适区间自动报警数据还能关联到动物健康档案里对饲养管理很有价值。引入简单的机器学习做异常检测。比如根据动物历史进食量预测正常范围某天进食量明显偏离时自动标记辅助兽医做健康判断。这个方向不一定多高大上但确实能解决实际痛点。最后再分享一个小技巧如果你也要做这类“看起来就是个管理系统”的项目前期沟通需求的时候记得多追问一句“你们现在最痛的是什么”用户往往会说出一个非常具体的场景这个场景就是系统里最值得打磨的核心功能。我这套系统的任务派发和库存联动就是通过这句话挖出来的成效也确实最明显。这个项目开发周期大概两个月中间改了不少需求但整体架构没有伤筋动骨。希望这篇能帮到正在做类似系统的朋友少走几个弯路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询