ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于SpringBoot+Vue的畜牧养殖管理系统开发实战详解

基于SpringBoot+Vue的畜牧养殖管理系统开发实战详解 这两年我在农村信息化项目上跑了不少地方最深的一个体会是大部分养殖场真正缺的不是那种一上来就搞物联网、AI 识别的“高大上”平台而是一套能安安稳稳把每一头猪、每一只鸡的账算明白的 Web 管理系统。去年我参与开发了一套基于 SpringBoot Vue 的畜牧养殖管理系统同时覆盖养猪场和养鸡场两种业务场景从需求梳理到上线部署折腾了三个多月。今天把这套系统从设计思路、数据库建模到核心模块实现、再到前后端联调的坑完整拆开来写一写。如果你正准备做类似的毕设、课设或者手头接了养殖场信息化的外包项目这篇文章里的表格结构、接口设计思路和排错经验可以直接拿来参考。我尽量少讲虚的多讲实际能落地的细节。1. 这个系统到底要解决什么问题1.1 养殖场管理的四个核心痛点我在调研阶段跑过几个中小规模的养殖场发现不管养猪还是养鸡日常管理的痛点惊人地相似。首先是档案混乱很多场子还在用纸质本子记录猪只的耳标号、出生日期、免疫情况时间一长本子丢失或者字迹模糊数据基本就断了。其次是库存靠拍脑袋饲料、兽药到底还剩多少全凭库管员记忆经常出现断料或者过期药还在用的情况。再一个是免疫时间容易漏猪瘟疫苗、口蹄疫疫苗、鸡新城疫疫苗都有严格的免疫程序漏打一次后面就很麻烦。最后是收益算不清一批猪出栏之后饲料成本、兽药成本、人工成本、水电成本混在一起根本说不清楚这批到底是赚是亏。这些痛点归结起来就是数据没有结构化、流程没有标准化。而 Web 管理系统刚好能解决这些问题——把纸质记录变成数据库记录把人工记忆变成系统提醒把月底算账变成实时统计。1.2 为什么选择 SpringBoot Vue 这套组合技术选型的时候我认真比较过几个方案。用 PHP 写传统混编页面确实快但后期维护和二次开发比较痛苦用 Python Django 开发效率也高但说句实在话在中小型外包项目里SpringBoot 的生态和招人难度还是更有优势。前端为什么选 Vue 而不是 React核心原因有两个一是 Vue 的中文资料和社区活跃度非常高后续接手的开发者上手成本低二是 Vue 的双向数据绑定和指令系统对于表单密集型的后台管理系统来说开发效率确实高。SpringBoot Vue 前后端分离架构还有一个实际好处是便于多人协作。我这边一个人开发的时候感受不明显但如果是团队作战后端同学专注写接口前端同学专注写页面两边只需要约定好接口文档开发节奏完全不受彼此影响。而且前后端分离之后后端接口可以复用未来如果要出小程序端或者 App 端接口层基本不用动。1.3 系统价值和适用人群这套系统上线之后最直观的变化是养殖场老板打开手机或者电脑就能看到当前存栏量、今日饲料消耗、待免疫数量、本月销售额这些关键指标不用再打电话问场长。对于饲养员来说每天该给哪几栏猪做防疫、哪个料塔需要补料系统会自动推送提醒。对于技术员来说每一只猪从入场到出栏的全生命周期记录都在系统里追溯起来非常方便。适用人群非常明确一是农村中小规模养殖场和合作社二是做 Java 毕设或课设的学生三是接信息化外包项目的开发者。无论你是哪一类这篇文章里的模块划分思路和代码实现细节应该都能帮到你。2. 整体设计与技术架构2.1 功能模块怎么划分系统功能模块我按照养殖场的实际业务流程来划分整体分为六大模块系统管理用户登录、角色权限、操作日志养殖档案猪只档案、鸡群批次档案、圈舍管理、转栏记录物资管理饲料入库、饲料出库、兽药库存、库存预警防疫管理免疫计划、免疫记录、疾病诊疗记录、死淘记录销售管理销售记录、销售统计、客户信息数据看板存栏统计、饲料消耗趋势、销售收益分析、免疫完成率每个模块再往下拆就是具体的页面和接口。比如养殖档案模块猪只档案页面要有新增、编辑、删除、批量导入、按耳标号查询这些功能批次管理页面要能按批次查看当前存栏数、死亡数、转出数并且能一键生成本批次的免疫计划。模块划分有一个原则我特别想强调一定要从“使用者”的角度去切而不是从“技术”的角度去切。我当时第一版设计按“增删改查模块”来切结果做出来的菜单结构用户根本看不懂后来完全推倒按养殖业务场景重新划分用户接受度立刻高了很多。2.2 前后端分离的项目结构后端项目结构用 Maven 管理标准的多模块或单模块分层。单模块的话package 结构大致如下com.farm.management ├── config // 配置类跨域、拦截器、MyBatis-Plus配置 ├── controller // 接口层 ├── service // 业务层 ├── mapper // 数据访问层 ├── entity // 数据库实体 ├── dto // 前端交互对象 ├── vo // 视图对象 └── utils // 工具类前端项目基于 Vue CLI 创建也可以直接用 Vite 初始化目录结构同样按业务模块划分src ├── api // 按模块封装的请求接口 ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由配置 ├── store // 状态管理Vuex/Pinia ├── views // 页面组件按模块分文件夹 └── utils // 请求封装、日期格式化等这里我特别提醒一下前端 api 目录一定要按后端模块拆分文件不要所有请求都写在一个 js 里面。我当时图省事把接口全写在一个 api.js 里到后面几百个接口混在一起改一个接口要全局搜索半天后来花了一个下午才拆干净。2.3 技术栈选型的补充说明除了 SpringBoot 和 Vue 这两个核心框架选型上还有一些细节值得说一下。ORM 层我用的 MyBatis-Plus没用原生 MyBatis也没用 Spring Data JPA。MyBatis-Plus 的 BaseMapper 内置了常用的单表 CRUD 方法像分页查询、条件构造器 QueryWrapper 这些开发效率比原生 MyBatis 高很多同时保留了 XML 自定义 SQL 的灵活性复杂统计可以自己写 SQL。数据库用的 MySQL 5.7说实话够用了。认证方案用的 Sa-Token相比 Shiro 和 Spring SecuritySa-Token 的 API 设计更简单集成 SpringBoot 只需要加一个依赖登录、鉴权、退出就都能搞定适合这种业务复杂度不高但权限要求明确的系统。前端状态管理用的 Pinia相比 VuexPinia 的 TypeScript 支持更好语法也更简洁Vue 3 项目基本是标配了。还有一个容易被忽略的点前后端联调时的接口文档。我这边用的是 Apifox接口定义好之后自动生成文档前端同学直接在线调试省去了写 Word 文档的功夫也避免了接口改来改去文档忘更新的问题。3. 数据库设计养殖数据的核心载体3.1 核心表结构设计思路数据库是这类系统的重头戏表结构设计得好不好直接决定后期开发的效率。我在设计时有几条原则第一所有业务表必须有主键 id、create_time、update_time 三个基础字段第二删除一律用逻辑删除加一个 deleted 字段避免误删数据无法恢复第三凡是涉及金额、数量的字段统一用 DECIMAL 类型避免浮点数精度问题。系统核心表我整理了一下大概有十来张核心几张表的设计如下用户表sys_userid, username, password, real_name, role_id, farm_id, status, create_time养殖批次表breed_batchid, batch_no, type猪/鸡, breed_name, entry_date, entry_count, current_count, status, remark猪只档案表pig_infoid, batch_id, ear_tag_no, gender, birth_date, weight, pen_id, status, mother_tag, father_tag圈舍表pen_infoid, pen_no, type, capacity, current_count, manager_name饲料库存表feed_stockid, feed_name, spec, stock_quantity, unit, min_quantity, warning_quantity, update_time饲料出入库记录表feed_recordid, feed_id, typein/out, quantity, operator, record_time, remark免疫计划表immune_planid, batch_id, disease_name, vaccine_name, plan_date, execute_date, status, operator免疫记录表immune_recordid, plan_id, batch_id, execute_date, vaccine_name, batch_no, dosage, operator疾病诊疗记录表disease_recordid, batch_id, pig_id, disease_name, symptom, diagnosis, treatment, cost, record_date销售记录表sale_recordid, batch_id, type, customer_name, quantity, unit_price, total_amount, sale_date, remark死淘记录表dead_recordid, batch_id, type, quantity, reason, dead_date, operator3.2 关键字段设计细节有几个字段设计我觉得值得单独拿出来说因为这些都是我踩过坑之后总结出来的经验。首先是批次号 batch_no 的生成规则。批次号我用的是“类型缩写 年月日 四位流水号”比如 P202506120001 表示 2025 年 6 月 12 日第 1 个养猪批次C202506010001 表示养鸡批次。这个规则看起来简单但在后续统计报表、批次追溯的时候非常好用前端拿到批次号直接就能解析出时间和类型。然后是猪只档案的耳标号 ear_tag_no。这个字段必须是唯一索引因为耳标是猪只的“身份证”后续免疫记录、疾病记录都要关联到这个字段。耳标号规则建议跟栏舍编号联动比如 01020301 代表 1 号舍 2 号栏 3 号位 01 号猪这样一旦某个栏舍出现疫情可以通过耳标号快速圈定风险范围。再一个是饲料库存表里的 min_quantity 和 warning_quantity 字段。min_quantity 是理论最低库存warning_quantity 是触发预警的阈值。这两个字段很多人会混淆我的做法是min_quantity 根据日消耗量和采购周期自动计算warning_quantity 手动设置默认为 min_quantity 的 1.2 倍。这样系统每天定时任务跑一遍库存低于 warning_quantity 就提醒采购低于 min_quantity 就高亮告警。3.3 数据库索引与查询优化数据量上来之后如果不做索引优化报表查询会非常慢。养殖档案类表建议创建联合索引。比如猪只档案表(farm_id, status, batch_id) 建联合索引这样统计某个场当前存栏、某个批次存栏时走索引就很快。免疫记录表按 (batch_id, plan_date) 建索引销售记录表按 (sale_date, type) 建索引。需要注意索引不是越多越好每次插入和更新都会额外维护索引所以只给高频查询字段建索引就行。另外报表统计类的 SQL 尽量用日期聚合。比如按月统计销售收益SQL 大概是SELECT DATE_FORMAT(sale_date, %Y-%m) AS month, SUM(total_amount) AS total_amount FROM sale_record WHERE farm_id #{farmId} AND sale_date #{startDate} GROUP BY DATE_FORMAT(sale_date, %Y-%m) ORDER BY month;这种 SQL 在数据量几百条的时候感觉不到差别但一旦到了几十万条没有索引、没有日期范围过滤查询延迟会从毫秒级变成秒级甚至卡死。所以设计表的时候就必须提前想到统计报表的查询路径。4. 核心模块实现与实操细节4.1 登录认证与权限拦截系统采用 Sa-Token 做登录认证和权限控制。登录流程不算复杂前端把用户名密码提交到 /api/auth/login后端校验通过后调用 StpUtil.login(userId) 生成 Token返回给前端存到 localStorage。前端请求拦截器会在每次请求头里带上 Token后端通过 Sa-Token 拦截器统一校验。Sa-Token 集成 SpringBoot 非常省事核心配置就几行Configuration public class SaTokenConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SaInterceptor(handle - { StpUtil.checkLogin(); })) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/login, /api/auth/captcha); } }角色权限我用的是简单的 RBAC 模型分三个角色系统管理员、技术员、饲养员。系统管理员可以操作所有模块技术员可以管理防疫和养殖档案饲养员只能查看和执行日常任务。Sa-Token 自带的注解鉴权 SaCheckRole 可以直接加在 Controller 方法上前端再配合路由守卫控制页面显示基本就能覆盖需求。实际开发中有一个小坑前端路由守卫判断登录状态时不能只判断 localStorage 里有没有 Token。因为 Token 可能过期过期后服务端第一次请求会报未登录错误这时候前端要统一拦截 401 状态码跳转到登录页并清除本地 Token。我在 axios 响应拦截器里加了这段处理否则用户会看到一个莫名其妙的空白页。4.2 养殖档案管理实现养殖档案是整个系统的业务核心主要包括猪只档案管理和鸡群批次管理两类。猪只档案偏向个体管理适合种猪场和规模不太大的育肥场鸡群档案偏向批次管理因为鸡的数量大不可能一只一只建档只能按“日龄批次”来管。猪只档案的新增是典型的表单提交场景前端用 Vue 的双向绑定收集表单数据后端接收 DTO 之后校验字段然后插入数据库。批量导入功能我用的 EasyExcel 库提供一个 Excel 模板给养殖场下载填写之后上传后端读取每一行数据逐条校验入库校验出错的行会生成错误提示文件回传。这个功能在初次建档的时候特别实用几百头猪的数据不可能手工在页面上一条一条录。批次管理里有一个功能我觉得做得不错批次状态自动流转。批次状态设计为“在养中”“已售出”“已淘汰”“已清栏”。当本批次的销售记录累计数量等于入场数量时系统自动把批次状态改为“已清栏”同时把圈舍的当前存栏数清零。这个逻辑用 MyBatis-Plus 的 UpdateWrapper 就能实现不需要写复杂 SQL。圈舍管理模块看起来简单其实藏着一个逻辑要谨慎处理转栏操作。猪只从 A 栏转到 B 栏需要同时更新猪只档案的 pen_id、原圈舍的 current_count 减一、目标圈舍的 current_count 加一。这个操作必须是事务性的任何一步失败都要回滚。我当时用 Transactional 注解包住整个转栏方法并且在更新圈舍数量之前先做了一次校验避免目标圈舍超出容量。4.3 饲料库存预警的完整实现饲料库存模块是养殖场老板最看重的功能之一。实现逻辑分三层底层是出入库流水中间是库存汇总上层是预警提醒。出入库登记页面操作员选择饲料品种、填写出入库数量系统自动更新库存表。这里有一个细节出库数量不能大于当前库存前端要做校验后端接口也要做校验双重保险。我当时在 Service 层用 synchronized 关键字对同一 feedId 做了并发锁避免两个操作员同时出库导致库存变成负数。预警功能用 SpringBoot 自带的 Scheduled 定时任务实现每天上午八点跑一次Component public class StockWarningTask { Autowired private FeedStockMapper feedStockMapper; Scheduled(cron 0 0 8 * * ?) public void checkStockWarning() { ListFeedStock stockList feedStockMapper.selectList( new QueryWrapperFeedStock().apply(stock_quantity warning_quantity) ); // 生成预警记录同时通过站内信/短信通知管理员 for (FeedStock stock : stockList) { saveWarningRecord(stock); } } }这里我又加了一个优化预警记录表里每条记录都有 status 字段标记“待处理”和“已处理”。库存管理员看到预警后填写采购计划或者完成入库后可以手动把预警状态改为已处理。这样就不会出现同一个库存问题每天重复提醒的情况。4.4 免疫管理与疾病记录免疫管理是本系统里业务逻辑最复杂的模块。猪和鸡的免疫程序差异很大猪要防猪瘟、口蹄疫、蓝耳、圆环鸡要防新城疫、禽流感、法氏囊。我的设计思路是先维护一个“免疫程序模板表”针对不同养殖类型、不同日龄段配置好应免疫的疫苗种类和时间点然后根据批次入场日期自动生成该批次的免疫计划。举个实际例子猪场技术员在系统里配置了仔猪 21 日龄免疫猪瘟疫苗、28 日龄免疫蓝耳疫苗。新增一个 6 月 1 日入场的批次后系统自动在 6 月 22 日生成一条“猪瘟疫苗待免疫”计划在 6 月 29 日生成一条“蓝耳疫苗待免疫”计划。生成计划的 SQL 本质上就是把模板表和批次表做关联日期计算逻辑并不复杂但非常实用。免疫记录的录入采用“按计划执行”的交互模式。技术员每天打开“今日免疫任务”列表看到今天需要免疫的批次点击“执行”系统自动带出疫苗名称、计划日期、执行人信息技术员只需填写疫苗批号和实际免疫头数提交后计划状态变为“已完成”。疾病诊疗记录我专门做了一个统计功能按疾病名称分组统计近一个月的发病次数并且用饼图展示占比。这个统计对于养殖场了解本地疫情流行趋势很有帮助。如果一类疾病发病次数连续异常增高系统会提示技术员重点排查。4.5 销售统计与图表展示销售模块的统计报表用 ECharts 做可视化。前端引入 ECharts 之后从后端接口拿到统计数据渲染成折线图、柱状图和饼图。比较常用的三个维度按月销售金额趋势、按客户销售额排名、按品种销售占比。后端统计接口返回的数据结构要设计成前端容易消费的格式。比如按月销售金额趋势的接口返回值是一个包含月份列表和金额列表的对象{ months: [2025-01, 2025-02, 2025-03], amounts: [152000.00, 168500.00, 145200.00] }这里有一个经验不要直接返回数据库查询结果给前端。因为 SQL 查出来的是 List前端拿到的字段名是数据库列名往往和前端需要的格式对不上。我在后端专门建了统计 VO 类把数据组装成前端友好的结构再返回前后端联调的时候少了很多不必要的沟通和返工。4.6 扩展功能养殖场监控视频接入系统上线稳妥之后老板提了个新需求能不能在系统里直接看猪舍和鸡舍的实时监控画面。我研究了一下方案最终通过集成 Vue 播放器接入监控 RTSP 流转成的 HLS/M3U8 流地址。监控摄像头的 RTSP 流无法直接在浏览器播放需要通过流媒体服务转成 HLS 协议输出 m3u8 播放地址。前端页面里用 video 标签 配合 hls.js 库实现播放import Hls from hls.js; function initPlayer(videoElement, m3u8Url) { if (Hls.isSupported()) { const hls new Hls(); hls.loadSource(m3u8Url); hls.attachMedia(videoElement); hls.on(Hls.Events.MANIFEST_PARSED, () { videoElement.play(); }); } else if (videoElement.canPlayType(application/vnd.apple.mpegurl)) { // Safari 原生支持 HLS videoElement.src m3u8Url; videoElement.play(); } }这个功能的难点主要在网络和流媒体服务这一侧前端实现并不复杂。我在接入过程中踩过的坑是HLS 延迟比较大直播画面会比实际延迟十几秒跟视频监控厂商确认后换成了低延迟模式才稍微好一点。如果你是做毕设想加点亮点这个功能可以写进论文里但别指望生产环境完全靠它做实时监控它更适合做历史回放或者远程巡查。5. 前后端联调与部署的常见问题5.1 跨域问题怎么彻底解决前后端分离项目联调时第一个拦路虎必然是跨域。开发环境下前端跑在 8080 端口后端跑在 8081 端口前端请求后端接口会触发浏览器同源策略限制。解决方式有两种一种是后端配置全局跨域另一种是前端配置开发服务器代理。后端配置比较直接写一个配置类实现 WebMvcConfigurerConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }但我更推荐前端开发环境下用代理方式。在 vue.config.js 里配置 devServer.proxy让前端的 /api 请求代理到后端地址这样浏览器看到的请求是同源的不涉及跨域也便于以后部署时通过 Nginx 统一转发。生产环境部署时Nginx 配置把 /api 前缀的请求转发到后端服务前端静态文件托管在 Nginx 上完全避开跨域问题。5.2 日期时间格式统一问题日期时间格式的问题是前后端联调最容易出 bug 的地方。Java 后端使用 LocalDateTime默认序列化成 ISO 格式的字符串比如 “2025-06-12T08:30:00”中间那个 T 非常讨厌前端 new Date() 解析时行为不一致有的浏览器能解析有的不能。解决方案是在后端统一配置 Jackson 的日期格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8前端 axios 请求中所有参数里的日期字符串都用 YYYY-MM-DD 这类格式不要直接用 Date 对象提交。前端展示时用 dayjs 库做格式化避免各种时间格式化工具混用导致的时区偏移问题。我踩过一个真实的坑服务器时区是 UTC数据库存的时间比实际时间快了八个小时后来在 JDBC 连接串里加上 serverTimezoneAsia/Shanghai 才解决。5.3 报表统计性能优化系统上线两个月后销售数据积累到了几万条按月统计的接口响应时间从几十毫秒涨到了一秒多。排查下来发现是统计 SQL 里用了大量的子查询和函数计算没有走索引。优化的方式有三个方向。第一是把报表查询的时间范围参数化前端默认只查最近一年避免全量扫描。第二是针对高频统计的维度在业务表上建立冗余字段。第三如果数据量继续增长到百万级可以考虑创建汇总表每天定时任务把当天的销售、饲料消耗数据汇总到汇总表报表查询只查汇总表速度可以保持在毫秒级。这个方案我还没来得及实施但设计上已经预留了表结构的位置。5.4 打包部署遇到的那些坑后端打包成 jar 后用 systemd 托管运行前端打包成 dist 目录交给 Nginx 托管。这里有一个经典坑Vue Router 使用 history 模式时刷新页面会 404因为 Nginx 找不到对应的静态文件路径。需要在 Nginx 配置里加 try_fileslocation / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }另一个坑是前端打包后布局异常。我在 Vue 3 项目里遇到过几次本地开发正常打包部署到服务器上发现样式错乱、图片不显示。排查下来基本都是静态资源路径的问题。Vite 构建时需要配置 base// vite.config.js export default defineConfig({ base: ./, plugins: [vue()] })改成相对路径之后静态资源在子路径部署就不会出问题了。5.5 SpringBoot 版本选择与兼容性开发时有个教训值得分享SpringBoot 版本不是越新越好。刚开始我图新鲜用了 SpringBoot 3.x结果发现它基于 Jakarta EE很多第三方库和旧教程的依赖坐标还不兼容。比如 PageHelper、某些 MyBatis 插件版本都要额外适配折腾了一天半才把环境跑通。后来我把项目降到 SpringBoot 2.7.x所有问题迎刃而解。选择 SpringBoot 版本的核心原则是看生态兼容性。如果你的项目依赖了大量第三方库建议用主流稳定的 2.x 版本如果是全新项目且依赖较少可以直接上 3.x。对于养殖管理系统这种业务型项目稳定性和可维护性比追求新版本更有价值。同理JDK 版本也建议用 8 或者 11太高的版本反而可能遇到一些老库不兼容的问题。6. 一些实用的经验总结系统落地到现在整体运行稳定最让我有成就感的是养殖场老板现在每天早上打开手机看一眼数据看板就能知道昨日的饲料消耗和销售情况库房管理员不用再翻本子查库存技术员不会漏掉免疫日期。这套系统真正把养殖场的核心数据串起来了。最后分享一个后续可以扩展的方向。如果养殖场规模发展到几千头猪批次管理和个体管理会变得更加复杂可以考虑引入更细化的物联网设备比如智能料线、环境传感器把这些设备的数据也接入系统做一个真正的数字化养殖平台。但前提一定是先把基础的数据记录和管理流程做扎实否则底子不稳上层再漂亮也是空中楼阁。
返回列表