☰
SpringBoot+Vue家禽养殖系统源码:私有部署与二次开发实战
2026/10/18 14:26:51 网站建设 项目流程

简介:这是一套基于SpringBoot与Vue开发的家禽养殖管理系统源码,面向计算机专业学生、课程设计或毕业设计开发者,以及需要搭建养殖场信息化管理平台的初学者与中级开发者。系统围绕角色权限与业务流程展开,管理员负责账户创建、权限分配与注册申请审核,普通员工完成家禽信息、饲养记录、生长健康状态等日数据导入,物资管理员则处理饲料、药品、医疗器械的采购与库存监控,功能划分清晰,便于理解前后端分离架构与权限控制逻辑。资源包共449个文件,以72个Java后端源码、22个JavaScript脚本、22个XML配置、21个Vue组件为主,另含少量图片、JSON与SQL等辅助文件,压缩包约11.03MB,目录结构完整,适合直接导入运行与二次开发。目前已有176人学习下载,可作为课程实践、项目复现与功能扩展的参考案例,帮助读者快速掌握SpringBoot与Vue整合开发的核心流程。

1. 从一份家禽养殖系统源码说起:SpringBoot+Vue 能解决什么

去年帮一个做养殖设备的朋友看后台,他掏出手机给我展示某云厂商的 SaaS 管理界面,说一年服务费够买两台自动喂料机。我问他数据能不能导出来自己存,他愣了半天。这件事让我意识到,中小型养殖场对「数据自主」的需求其实很强烈,只是市面上要么是功能臃肿的 ERP,要么是只能看不能改的租用系统。这份基于 SpringBoot+Vue 的家禽养殖系统源码,恰好卡在中间——它把养殖批次、饲料投喂、环境监测、防疫记录这些核心环节做成了可私有部署的模块,前端用 Vue 做交互,后端 SpringBoot 提供接口,数据库层留了扩展位。适合谁?一是想拿现成架子做二次开发的团队,二是计算机专业做课程设计或毕业设计的学生,三是养殖场里懂点技术的场长,想自己搭一套内部用的记录工具。它不承诺替代专业环控设备,但能把「谁在什么时候喂了多少料、死了几只、打了什么疫苗」这类纸质台账电子化,并且数据留在自己手里。

2. 环境搭建与项目结构:从 clone 到跑通第一个接口

2.1 技术栈版本确认与依赖安装

拿到源码包后别急着mvn spring-boot:run,先看pom.xml和package.json里的版本约束。这类项目常见组合是 SpringBoot 2.7.x + Vue 2.6/2.7 + Element UI,或者 SpringBoot 3.x + Vue 3 + Element Plus。两者在依赖坐标和语法上有差异,混用会直接报Cannot resolve symbol 'javax'或Uncaught TypeError: Cannot read properties of undefined。我一般先确认三件事:JDK 版本(1.8 还是 17)、Node 版本(14 还是 18)、MySQL 版本(5.7 还是 8.0)。下面以 SpringBoot 2.7 + Vue 2 的常见组合为例,给出可复现的初始化步骤。

# 后端依赖安装,进入项目根目录下的 server 或 backend 文件夹 cd poultry-farm-system/backend # 确认 JDK 版本,SpringBoot 2.7 最低要求 JDK 8,推荐 11 或 17 java -version # 使用 Maven 拉取依赖,跳过测试可加快首次构建 mvn clean install -DskipTests # 如果公司内网有私服,在 settings.xml 里配好 mirror,否则会卡在下载
# 前端依赖安装,进入 web 或 frontend 文件夹 cd ../frontend # 确认 Node 版本,Vue 2 项目建议 Node 14 或 16,Node 18 可能报 OpenSSL 错误 node -v # 安装依赖,国内建议临时指定 registry 避免超时 npm install --registry=https://registry.npmmirror.com # 启动开发服务器 npm run serve

逻辑说明:后端mvn install会把项目打成 jar 并安装到本地仓库,同时下载 SpringBoot starter、MyBatis-Plus、Druid 等依赖。前端npm install根据package-lock.json还原 node_modules。参数上,-DskipTests跳过单元测试,首次搭建时能省几分钟;--registry指定镜像源,避免默认源在部分网络环境下超时。如果npm run serve报error:0308010C:digital envelope routines::unsupported,说明 Node 版本过高,降级到 16 或设置NODE_OPTIONS=--openssl-legacy-provider即可。

2.2 数据库初始化与配置修改

源码包里通常有sql文件夹,里面是建表语句和初始数据。常见坑是直接导入后启动报Table 'xxx' doesn't exist,原因是数据库名和配置文件里的不一致。我一般先建库再导表,字符集用utf8mb4,排序规则utf8mb4_general_ci,避免中文乱码。

-- 创建数据库,名称与 application.yml 中 url 的库名保持一致 CREATE DATABASE poultry_farm DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 切换到该库 USE poultry_farm; -- 导入源码包中的 schema.sql 和 data.sql,注意执行顺序 SOURCE /path/to/sql/schema.sql; SOURCE /path/to/sql/data.sql;
# application.yml 中需要改动的核心配置 spring: datasource: url: jdbc:mysql://localhost:3306/poultry_farm?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver # 如果项目用了 Redis 做缓存,确认 redis 服务已启动 redis: host: localhost port: 6379 database: 0

逻辑说明:serverTimezone=Asia/Shanghai不加的话,MySQL 8 驱动会报时区错误。characterEncoding=utf8配合库的utf8mb4能存 emoji 和生僻字。Redis 配置看项目是否启用,如果启动报Unable to connect to Redis,要么启动 Redis 服务,要么在配置里把相关依赖注释掉。改完配置后重启后端,访问http://localhost:8080/doc.html或/swagger-ui.html看接口文档是否正常渲染,这是验证后端跑通的最快方式。

3. 核心业务模块拆解:养殖批次、饲料投喂与防疫记录

3.1 养殖批次管理的数据模型与接口调用

家禽养殖系统的核心实体是「批次」(Batch),一个批次对应一批同品种、同日龄的家禽,从入栏到出栏或淘汰。表结构通常包含batch_id、breed(品种)、entry_date(入栏日期)、initial_count(初始数量)、current_count(当前存栏)、status(在养/已出栏/已淘汰)。后端用 MyBatis-Plus 做 CRUD,前端用 Element UI 的表格和表单渲染。下面看一个典型的批次新增接口。

// BatchController.java 片段,接收前端 JSON 并写入数据库 @RestController @RequestMapping("/api/batch") public class BatchController { @Autowired private BatchService batchService; @PostMapping("/add") public Result addBatch(@RequestBody @Valid BatchDTO batchDTO) { // 校验入栏日期不能晚于当前日期 if (batchDTO.getEntryDate().isAfter(LocalDate.now())) { return Result.fail("入栏日期不能是未来日期"); } // 初始数量必须大于零 if (batchDTO.getInitialCount() <= 0) { return Result.fail("初始数量必须大于零"); } return batchService.saveBatch(batchDTO); } }

逻辑说明:@Valid触发 JSR-303 校验,BatchDTO里用@NotNull、@Min注解约束字段。entryDate的未来日期校验是业务规则,防止误操作。Result是统一返回包装类,包含code、msg、data。前端调用时用 axios 发 POST 请求,注意Content-Type: application/json,并且后端如果配了跨域,确认@CrossOrigin或全局 CORS 配置生效,否则浏览器控制台会报Access-Control-Allow-Origin。

// 前端 api/batch.js 中的请求封装 import request from '@/utils/request' export function addBatch(data) { return request({ url: '/api/batch/add', method: 'post', data: data }) }

参数说明:data对象包含breed、entryDate、initialCount等字段,日期格式用yyyy-MM-dd字符串,后端用@JsonFormat(pattern = "yyyy-MM-dd")反序列化。如果前端传的是时间戳,后端要加@DateTimeFormat或改用LocalDateTime。常见翻车点是日期格式不匹配导致JSON parse error,看后端日志里Cannot deserialize value of type java.time.LocalDate就能定位。

3.2 饲料投喂记录与库存联动

投喂模块不只是记一笔「今天喂了 50 公斤」,它通常和饲料库存表联动:投喂时扣减库存,库存低于阈值触发提醒。表设计上,feed_record表存投喂记录,feed_stock表存当前库存,feed_threshold存预警值。后端在保存投喂记录时用事务保证扣减和插入同时成功或同时回滚。

// FeedService.java 中的投喂事务方法 @Transactional(rollbackFor = Exception.class) public Result feed(FeedDTO feedDTO) { // 查询当前库存 FeedStock stock = feedStockMapper.selectById(feedDTO.getFeedId()); if (stock.getQuantity() < feedDTO.getAmount()) { return Result.fail("库存不足,当前剩余:" + stock.getQuantity()); } // 扣减库存 stock.setQuantity(stock.getQuantity() - feedDTO.getAmount()); feedStockMapper.updateById(stock); // 插入投喂记录 FeedRecord record = new FeedRecord(); record.setBatchId(feedDTO.getBatchId()); record.setFeedId(feedDTO.getFeedId()); record.setAmount(feedDTO.getAmount()); record.setFeedTime(LocalDateTime.now()); feedRecordMapper.insert(record); // 检查是否低于阈值,低于则写预警表 if (stock.getQuantity() < stock.getThreshold()) { alertMapper.insert(new Alert("饲料库存不足", stock.getFeedName())); } return Result.success("投喂成功"); }

逻辑说明:@Transactional保证扣库存和插记录原子性,rollbackFor = Exception.class确保受检异常也回滚。库存不足时直接返回失败,不进入扣减逻辑。预警写入用独立表,前端轮询或 WebSocket 推送。参数上,amount单位统一为公斤,前端输入时做数字校验,避免负数或超大值。如果并发投喂同一批饲料,selectById可能读到旧库存,常见做法是加乐观锁版本号或select ... for update,源码里如果没做,高并发场景下会出现超卖,这是需要留意的边界。

3.3 防疫记录与批次状态流转

防疫记录关联批次和疫苗类型,记录接种日期、剂量、操作人。批次状态从「在养」到「已出栏」的流转通常由出栏操作触发,出栏时校验该批次是否有未完成的防疫计划。这部分逻辑在前端表现为按钮的禁用状态,后端做二次校验。

// 出栏操作的状态校验 public Result sellOut(Long batchId) { Batch batch = batchMapper.selectById(batchId); if (!"在养".equals(batch.getStatus())) { return Result.fail("该批次不在在养状态,无法出栏"); } // 检查是否有未完成的防疫记录 Long pendingCount = vaccineMapper.selectCount( new QueryWrapper<Vaccine>().eq("batch_id", batchId).eq("status", "待接种") ); if (pendingCount > 0) { return Result.fail("存在未完成的防疫计划,请先处理"); } batch.setStatus("已出栏"); batch.setExitDate(LocalDate.now()); batchMapper.updateById(batch); return Result.success("出栏成功"); }

逻辑说明:状态机用字符串枚举,简单直接但容易拼写错误,常见改进是定义常量类或枚举。selectCount返回待接种数量,大于零则阻止出栏。参数上batchId从路径或请求体获取,前端出栏按钮根据status和pendingCount动态置灰。如果源码里没做这个校验,出栏后防疫记录还挂着,数据就乱了,这是业务逻辑上的一个坑。

4. 前后端联调与权限控制:避开跨域和 Token 失效的坑

4.1 跨域配置与请求拦截器

前后端分离项目本地开发时,前端跑在localhost:8081,后端跑在localhost:8080,浏览器同源策略会拦截请求。常见做法是后端配 CORS 或前端配 proxy。后端配 CORS 要注意allowCredentials(true)时allowedOrigins不能为*,否则报When allowCredentials is true, allowedOrigins cannot contain the special value "*"。

// 全局 CORS 配置类 @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") // 用 patterns 替代 origins .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

逻辑说明:allowedOriginPatterns是 Spring 5.3 之后引入的,解决allowCredentials与通配符冲突。maxAge(3600)让预检请求缓存一小时,减少 OPTIONS 请求次数。前端如果用 proxy,在vue.config.js里配devServer.proxy,把/api转发到后端,这样浏览器看到的是同源请求,不需要后端改 CORS。两种方式选一种即可,同时用可能重复添加响应头导致Access-Control-Allow-Origin出现两次,浏览器照样拦截。

4.2 JWT Token 校验与前端路由守卫

登录后后端返回 token,前端存 localStorage 或 sessionStorage,每次请求在 header 里带Authorization: Bearer xxx。后端拦截器校验 token 有效性,过期返回 401。前端 axios 响应拦截器捕获 401 后跳转登录页。常见坑是 token 过期后用户正在填表单,跳转登录导致数据丢失,改进做法是弹窗提示重新登录并保留草稿。

// 前端 request.js 响应拦截器 service.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { // token 过期,清除本地存储并跳转登录 localStorage.removeItem('token') router.push('/login') Message.error('登录已过期,请重新登录') } return Promise.reject(error) } )

逻辑说明:response => response.data直接返回后端数据体,省去每次.data取值。401 处理里先清 token 再跳转,避免死循环。参数上,token 过期时间在后端JwtUtil里设置,常见 2 小时或 7 天,看安全要求。如果后端拦截器没放行登录接口和验证码接口,会出现登录请求也被拦截的尴尬,检查excludePathPatterns是否包含/api/auth/login。

5. 避坑与排查:部署和运行时的五个血泪教训

5.1 现象:启动报Port 8080 was already in use

原因:本机已有其他服务占用 8080,常见是之前没关掉的 Java 进程或 Tomcat。解决:lsof -i:8080找到 PID 后kill -9,或在application.yml里改server.port为 8081 等空闲端口。Windows 用netstat -ano | findstr 8080再taskkill /PID xxx /F。

5.2 现象:前端页面空白,控制台报Failed to load resource: 404

原因:前端打包后静态资源路径不对,或者后端没配静态资源映射。Vue 项目publicPath默认/,如果部署在子路径下要改成./或具体路径。解决:检查vue.config.js的publicPath,Nginx 部署时确认root和try_files配置指向index.html。

5.3 现象:数据库连接池报Communications link failure

原因:MySQL 服务没启动、防火墙拦截 3306、或连接超时时间太短。解决:先telnet localhost 3306确认端口通,再看 MySQL 是否允许远程连接(bind-address配置),连接池加connectionTimeout和validationQuery。Druid 配testWhileIdle: true能减少空闲连接被断开。

5.4 现象:中文乱码,页面显示????

原因:数据库字符集不是utf8mb4,或 JDBC URL 没加characterEncoding=utf8,或前端请求头Content-Type没带charset=utf-8。解决:建库时指定utf8mb4,JDBC URL 补全参数,后端server.servlet.encoding.charset=utf-8和force=true一起配。

5.5 现象:定时任务不执行或执行两次

原因:多实例部署时每个实例都跑定时任务,或者@Scheduled的 cron 表达式写错。解决:单机部署确认@EnableScheduling已加,cron 用在线工具验证;多实例用分布式锁或把定时任务抽到独立服务。常见错误是 cron 里*和?混用导致解析失败,启动日志会有Encountered invalid @Scheduled method提示。

6. 二次开发与数据导出:把系统改成自己场里的样子

源码跑通后,真正让它有用的是按自己场里的流程改。我一般先改三处:品种字典、栏舍编号规则、报表字段。品种字典在sys_dict表里,加几条dict_type='breed'的记录即可,前端下拉框自动读取。栏舍编号如果场里用「A区-01栋-02栏」这种格式,改house表的code字段长度和前端校验正则。报表字段最容易被忽略,默认的存栏报表可能只统计总数,但场里要按品种、按日龄分段看,这就需要在BatchMapper.xml里改 SQL,加GROUP BY breed, DATEDIFF(NOW(), entry_date)。

数据导出是另一个高频需求。系统自带 Excel 导出的话,通常用 EasyExcel 或 POI。如果没带,自己加一个 Controller 方法,查数据后写HttpServletResponse的OutputStream。注意导出大数据量时别一次性selectList全查出来,分页查或流式查,否则内存直接飙红。

// 简易 Excel 导出,使用 EasyExcel @GetMapping("/export") public void export(HttpServletResponse response) throws IOException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("养殖批次报表", "UTF-8"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); // 分页查询,每页 1000 条,避免全量加载 List<BatchExportVO> list = batchService.listForExport(); EasyExcel.write(response.getOutputStream(), BatchExportVO.class) .sheet("批次数据") .doWrite(list); }

逻辑说明:Content-Disposition里的文件名用URLEncoder编码,否则中文文件名在部分浏览器下乱码。EasyExcel.write直接写响应流,不落磁盘。参数上,BatchExportVO的字段用@ExcelProperty注解指定列名和顺序。如果数据量超过几万行,建议改成异步导出,前端轮询下载链接,避免请求超时。

验证二次开发是否成功,我习惯用「反向操作法」:新增一条批次,然后去数据库查是否写入;投喂一次,看库存是否扣减;出栏一个批次,确认防疫校验是否拦截。每一步都手动走一遍,比只看日志靠谱。从那以后我每次改完业务代码,都强制走一遍「新增-修改-删除-导出」四步,确认没有遗漏的关联更新。希望帮到你。

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

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

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

立即咨询