资讯动态

SpringBoot+Vue农事管理系统:从数据库设计到部署实战

发布时间:2026/10/14 6:10:43 来源:尧图企业网站定制
接手这个项目之前我在帮某个小型农场做信息化的需求梳理。农场主给我的第一句话是“地里的记录都在Excel里不同的人在填填完也没人看。去年种了什么品种、每亩用了多少肥、哪块地产量高根本说不清楚。”这就是做农事管理系统最真实的出发点——不是炫技而是把农业生产里的地块、农事、农资、人员、记录这些散落的信息整理成一套可查询、可统计、可追溯的数据闭环。这篇文章把完整的项目拆开讲业务需求怎么提炼成功能模块数据库表怎么设计才能支撑农事作业闭环后端用SpringBootMyBatis怎么写核心接口前端用Vue怎么把页面串起来最后是本地跑通源码的完整步骤和我在开发过程中踩过的坑。适合正在做毕业设计、想拿完整项目练手的开发者也适合准备给中小型农场做信息化的小团队参考。1. 农事管理系统到底要管什么业务需求拆解很多同学拿到这类题目第一反应是上网抄一套通用后台管理模板把用户管理、角色管理、菜单管理做出来就觉得完事了。这恰恰是本末倒置。农事管理系统的核心不是“管理用户”而是“管理农事”。用户管理只是支撑系统运转的骨架地块、农事任务、农资库存、农事记录才是血肉。1.1 地块档案一切农事数据的锚点农事管理的基本单位是“地块”。在真实的农场里一块地有编号、面积、土壤类型、种植作物、负责人、地理位置等属性。所有后续的播种、施肥、打药、灌溉、采收记录都要挂到地块上才能回答“这块地今年种了什么、花了多少成本、产出如何”。项目里我设计了“地块档案管理”模块字段包括地块编号、地块名称、面积、土壤类型、当前作物、状态空闲/种植中/休耕、负责人、备注。这里最关键的一点是地块编号必须唯一且稳定它是农事记录外键关联的基础。如果地块可以随意删除历史记录就会失去锚点所以地块模块一般做逻辑删除不物理删除。1.2 农事任务与农事记录计划到执行的双层结构农场日常工作不是“想做就做”而是按农时安排。播种、施肥、打药、灌溉、采收都有时间窗口。我把农事拆成两层农事任务计划和农事记录执行。农事任务是计划层包含任务类型播种、施肥、打药、灌溉、采收、除草等、关联地块、计划开始时间、计划结束时间、负责人、任务状态待执行/执行中/已完成/已取消、任务描述。农事记录是执行层由执行人提交包含实际执行时间、实际用量、用药用肥明细、操作人、现场情况说明。为什么要拆两层因为农场的考核和管理需要对比“计划 vs 实际”。比如计划在3月20日前完成施肥实际拖到3月28日系统里就能看出偏差。如果不拆分只有一条记录很难做这种对比分析。我在设计时让“新建农事记录”可以从某个农事任务一键生成自动带出地块、类型、负责人执行人只要补充实际数据即可。1.3 农资库存与出入库闭环里的成本账农资管理如果只做“进出库流水”那只是一个记账工具。我在项目里把它做成了带预警的库存闭环。农资档案包括种子、化肥、农药、农机具等属性有物资名称、类别、规格、单位、库存数量、预警阈值、供应商、单价。出入库单记录每一次变动入库单采购入库、退库入库、出库单农事领用、损耗出库。最关键的设计是出库单可以选择关联农事任务或农事记录。这样到月底就能算出来“这块地这个月领了多少化肥花在哪个任务上”从而实现按地块、按任务维度的成本归集。我还加了一个低库存预警逻辑当库存数量低于预警阈值时系统自动标记主页看板会提示需要采购。这一条在实际使用中农户非常认可因为以前都是用到地里才发现药不够了临时采购往往耽误农时。1.4 环境记录与统计报表给决策提供依据环境数据温度、湿度、降雨量对农业生产有直接影响。系统里保留了一个环境记录表可以手动录入也可以为将来接入物联网传感器留接口。模块不复杂就是按日期和地块记录环境指标配合可视化图表展示。统计报表是系统的价值出口。我做了三个核心报表按地块统计全年农事记录数量与产量趋势、按农资类型统计使用成本、按人员统计任务完成率。这些数据从农事记录表和出入库表聚合而来用SQL的GROUP BY就能完成但页面展示上用了ECharts折线图和柱状图让农场管理者一眼能看到问题地块和成本大户。2. 技术选型的底层逻辑为什么是SpringBootVueMyBatis网上关于技术选型的讨论很多但对于这个项目SpringBoot Vue MyBatis MySQL的组合不是随便选的它非常适合“中小型管理信息系统”这个定位。2.1 后端SpringBoot 2.7 MyBatisSpringBoot是目前Java后端开发成本最低的框架内嵌Tomcat简化了Spring的XML配置Maven一键打包项目的启动入口就是一个main方法。对做毕业设计或者小团队交付来说SpringBoot能显著降低部署和运维的复杂度。MyBatis和SpringBoot的配合很成熟。我选择MyBatis而不是JPA原因有两点第一农事管理系统的查询条件复杂多变——按地块查、按时间范围查、按任务类型查、按状态查还可能多个条件组合。MyBatis的XML里可以手写动态SQL用if标签灵活拼接条件比JPA的Specification更直观可控。第二报表统计需要写多表JOIN和GROUP BY聚合查询这种SQL在MyBatis里就是直接写XML改起来方便执行计划也可控。如果你不太熟悉MyBatis的动态SQL建议花半小时过一遍where、set、foreach、if这几个标签。这个项目90%的查询接口都要用到它们。2.2 前端Vue 2 Element UI前端选择Vue 2是基于生态成熟度的考虑。Vue 2 Element UI后台管理组件的组合在大量项目中得到验证网上的解决方案多遇到问题容易查到资料。Vue的响应式数据绑定非常适合表单类和列表类页面数据变化自动驱动视图更新不需要手动操作DOM。Element UI提供了表格、表单、日期选择器、对话框、消息提示这些后台系统高频组件直接降低页面开发量。比如农事记录列表页用el-table绑定数据加上分页组件几行代码就出来了。我建议如果你自己做项目且不熟悉前端先别急着上Vue 3和TypeScript虽然它们更新但Vue 2 JS的学习曲线更平缓先把业务跑通更重要。等到整体架构熟练了再迁移到Vue 3也不迟。2.3 前后端分离的结构设计项目采用前后端分离结构后端只提供RESTful API前端通过axios调用。开发环境用Vue CLI的devServer代理解决跨域生产环境用Nginx反向代理。这样前后端可以独立开发、独立部署代码维护也清晰。整体结构分为三层前端是Vue项目包含登录页、首页看板、地块管理、农事任务、农事记录、农资库存、统计报表、系统管理这几个页面后端是SpringBoot单体应用按controller、service、mapper分层数据库用MySQL 8.0字符集utf8mb4。单体应用对这个量级的系统完全够用不需要微服务。3. 数据库设计五张核心表的关系与建表细节数据库设计是整个项目中最重要、也最容易被低估的环节。表结构设计得好后面写Mapper和页面都会顺手设计得不好后期改表会连带改动大量代码痛苦不堪。3.1 用户与角色别在用户表上堆字段用户模块我不做复杂的RBAC权限模型只做用户表和角色字段。农场系统用户量本来就不大角色无非是管理员、场长、技术员、操作工几种。在user表里用一个role字段区分即可不需要role表、user_role表、menu_role表那套复杂设计那会显著增加工作量。user表字段id、username、passwordBCrypt加密存储、real_name、phone、role、status、create_time。登录时用JWT生成token前端把token存在localStorage每次请求头带Authorization。后端写一个拦截器校验token解析用户信息后放行。3.2 地块、农事任务、农事记录、农资、出入库五张核心表这五张表是系统的业务主干。我列出简化版字段方便你理解关系表名核心字段说明farmland地块id, code, name, area, soil_type, current_crop, status, manager_id业务锚点farm_task农事任务id, task_type, farmland_id, plan_start, plan_end, assignee_id, status, description计划层farm_record农事记录id, task_id, farmland_id, record_type, execute_time, operator_id, detail, pesticide_info执行层task_id可空material农资档案id, name, category, spec, unit, stock, warn_threshold, supplier, price库存主数据material_io出入库单id, io_type, material_id, quantity, related_task_id, create_time, creator_id, remark库存流水farm_record里冗余了farmland_id为什么不只通过task_id去关联因为农事记录允许不挂任务直接录比如临时发现病虫害马上打药不一定要先建一条计划任务。冗余字段可以避免每次查询都要JOIN任务表提高列表页性能。这是典型的“以空间换效率”设计在报表统计时很有用。3.3 建表SQL的几个容易忽略的细节第一所有表的主键用BIGINT自增不要用UUID字符串做主键BIGINT在索引和JOIN性能上更好。第二时间字段统一用datetime不要用timestamp避免2038年问题和时区换算困扰。第三字符集统一utf8mb4排序规则utf8mb4_general_ci这样中文、emoji、特殊符号都能存。第四金额和面积字段用DECIMAL不要用FLOAT/DOUBLE避免精度误差。比如农药单价12.35元float可能存成12.349999。外键我建议只在逻辑层面关联不建物理外键约束。物理外键在插入、删除时会带来额外的锁和校验开销而且在分页、批量操作时容易出问题。维护数据一致性靠service层事务控制性能更好灵活性更高。初始化SQL脚本里我会预置一个管理员账号admin/admin123和一些演示地块、农资数据方便第一次启动就能看到页面有内容。4. 后端实现登录鉴权与农事任务闭环4.1 JWT登录流程与拦截器登录接口接收用户名和密码查user表用BCryptPasswordEncoder匹配密码匹配成功则生成JWT。JWT的payload里放userId和username设置24小时有效期。我习惯在JWT里不放敏感信息密码、手机号这些坚决不往token里塞因为JWT的payload是Base64编码等于明文。拦截器继承HandlerInterceptor重写preHandle方法从request头取Authorization去掉“Bearer ”前缀后解析token。解析失败或过期直接返回401。配置类里用registry.addInterceptor注册拦截器并放行登录接口、静态资源、跨域预检请求OPTIONS。这一步如果不放行OPTIONS前端调用接口时会一直报跨域错误后面前端章节会细说。4.2 农事任务的CRUD与状态流转农事任务模块是后端写得最多的部分。列表接口用动态SQL传入taskType、status、farmlandId、时间范围等条件mapper XML里用where加if拼接。分页用PageHelper插件一行startPage就可以自动生成limit语句。新增任务的逻辑比较简单直接insert。比较关键的是状态流转。我把状态机定义为待执行任务创建后默认状态执行中负责人开始作业后更新已完成执行人提交农事记录后自动变为已完成已取消管理员手动取消提交农事记录时后端会执行一个事务方法插入farm_record然后更新对应farm_task状态为已完成。这样才能保证“记录有了任务状态也变了”这两个操作要么都成功要么都失败。4.3 农资出入库的事务控制出入库是另一个涉及事务的关键点。出库单的保存逻辑是先插入material_io记录再更新material表库存数量stock stock - 数量。如果插入流水成功但库存更新失败数据就不一致了所以两个操作必须在一个事务里。我还在service层加了库存不足校验出库前查询当前库存如果小于出库数量直接抛出业务异常事务回滚。采购入库的逻辑类似只是数量做加法。这里补充一个我的实现技巧用Spring的Transactional注解时注意只有RuntimeException和Error才会触发默认回滚受检异常不会。所以自定义业务异常要继承RuntimeException。我项目里定义了一个BusinessExceptionservice层直接throw new BusinessException(库存不足)就能触发回滚。5. 前端实现列表、表单、看板的落地细节5.1 项目初始化与请求封装前端用Vue CLI创建项目安装vue-router、axios、element-ui、echarts。页面结构上整体布局用Element UI的el-container左侧是侧边菜单右侧是内容区顶部是用户信息。axios需要封装一下统一处理baseURL、超时时间、token携带和响应拦截。我在request.js里做了三件事请求拦截器从localStorage取token并加到请求头响应拦截器判断后端返回的codecode为200时直接返回数据code为401时跳转登录页code为500时用Element UI的Message提示错误信息。这样封装之后每个页面的接口调用代码就很简洁不再需要重复处理错误。5.2 农事日历与任务列表页任务列表页是所有页面里最复杂的。顶部是查询条件区任务类型下拉、状态下拉、日期范围、地块选择中间是el-table展示任务列表操作列有“开始执行”“查看”“提交记录”按钮。新增和编辑共用同一个弹窗表单提交时校验必填项。这里有个前端小技巧编辑弹窗打开时要用深拷贝把行数据复制给表单对象不能直接赋值引用否则表单里改了没点保存表格数据已经被改了。农事日历是我给这个项目加的亮点页面。用echarts的日历坐标系按日期展示当月任务数量颜色深浅代表任务密度。配合一个表格点日历日期就能看到当天的具体任务列表。做起来并不复杂api返回的数据按日期分组即可但使用体验比纯表格直观很多。5.3 统计看板的图表页面统计报表页面分三个Tab地块产量趋势折线图、农资成本分布柱状图、人员任务完成率饼图。数据接口都在后端写好了聚合SQL前端只需要接收标准结构用echarts的setOption渲染。ECharts的选项配置比较多我建议把每个图表的option封装成独立的函数接收数据返回配置不要在组件里写一长串option后期维护会很难受。图表容器一定要设定width和height否则初始化时会是0像素图表不显示这是新手常踩的坑。6. 从源码到运行本地部署完整步骤拿到完整源码之后怎么在本地跑起来我按后端和前端两部分说明跟着做一遍基本就能启动成功。6.1 环境准备清单软件版本建议说明JDK1.8及以上项目基于JDK8开发11也能跑Maven3.6用于后端依赖管理和打包MySQL8.05.7也可以但建议8.0Node14/16Vue CLI 5要求Node版本不能太低IDEIntelliJ IDEA后端用IDEA前端可以用IDEA或VSCode第一步创建一个空的MySQL数据库执行项目里提供的init.sql脚本会自动建库建表并写入演示数据。注意MySQL 8.0的默认认证插件是caching_sha2_password如果连接报错可以在创建用户时指定mysql_native_password或者升级mysql-connector-java版本。6.2 后端配置与启动修改application.yml把数据库地址、用户名、密码改成你的本地配置。我建议单独建一个专用账号不要用root避免误操作。启动后端之前确认三件事Maven依赖是否下载完整、数据库是否已初始化、端口8080是否被占用。如果8080被占用在application.yml里改server.port。启动成功后浏览器访问http://localhost:8080配合swagger或者直接访问一个测试接口能返回JSON说明后端OK。6.3 前端启动与代理配置前端项目在IDE里打开终端执行npm install安装依赖。这一步在国内经常卡在下载可以先用nrm把npm源切换到国内镜像源速度提升明显。安装完成后执行npm run serve默认启动在8080端口如果和后端冲突config目录下的index.js里改port。关键点是vue.config.js里配置devServer代理module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端请求/api/login会被代理到后端的http://localhost:8080/api/login规避跨域问题。注意后端的Controller统一以/api开头否则代理路径还要加rewrite逻辑。启动完成后浏览器访问http://localhost:3000看到登录页就说明环境跑通了。7. 踩坑清单这几个问题我排查了最久项目开发过程中我遇到了不少问题挑几个影响最大、最容易复现的记录下来这些在官方文档里往往不会直接写明。7.1 跨域与拦截器放行冲突前端调用后端接口报跨域第一反应是配置CorsFilter。配置之后预检请求OPTIONS通过了但带token的正式请求还是失败。原因在于拦截器在CORS处理之前就把OPTIONS请求拦截了还没到CorsFilter就返回了401。解决办法有两个一是拦截器里统一放行OPTIONS方法代码很简单if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; }二是SpringBoot的CorsMvcConfigurer和拦截器注册顺序配合。我建议两个都做稳一点。这个问题排查了我一个下午因为控制台报的是CORS错误但实际根因是拦截器表面现象很容易误导。7.2 MyBatis下划线转驼峰没生效数据库字段是create_time实体类是createTime查询返回的createTime一直为null。查了很久发现application.yml里缺了配置mybatis: configuration: map-underscore-to-camel-case: true没有这个配置MyBatis查询结果映射时不会自动把下划线转驼峰。需要注意的是在application.yml里写这个配置如果用mybatis-plus可能写法不同它是mybatis-plus.configuration.map-underscore-to-camel-case。这个细节很容易踩。7.3 时间格式化时差问题农事记录列表页的时间比数据库实际时间少了8小时。原因有两层MySQL连接URL里的serverTimezone没设置或者设置成了UTC加上fastjson在序列化LocalDateTime时默认格式化的时区不对。我在连接URL里加了serverTimezoneAsia/Shanghai并在application.yml里统一配置jackson的时间格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai实体类的时间字段我用LocalDateTime配合JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。这样前后端显示的时间就一致了。7.4 库存扣减的并发问题测试的时候用两个账号同时对同一农资出库库存从10变成了8而不是预期的6。这是典型的并发覆盖问题两个请求都查到库存10各自扣2后写回8最后一次写覆盖了前一次。解决方法是出库SQL用原子更新UPDATE material SET stock stock - #{quantity} WHERE id #{materialId} AND stock #{quantity}affected rows为0说明库存不足抛出业务异常。这样不需要用悲观锁也不需要引入分布式锁一个SQL就能解决并发场景下的库存扣减。这是我在实际项目中验证过的方案对这个量级的系统足够用。写在最后的个人建议项目全部做完之后我把源码里的注释、文档和SQL重新梳理了一遍。一个很重要的体会是农事管理系统这类项目业务逻辑并不复杂真正花时间的反而是数据库设计和那些不起眼的细节配置。如果让我重做一次我会在项目开始时先花一周时间和最终用户聊业务流程把地块怎么分、农资怎么管、任务怎么派这些问题彻底搞清楚再动手写代码。业务模型对了技术实现是水到渠成的事。另外这个项目最大的扩展空间在于数据接入层——环境传感器、气象接口、智能水肥控制这些都可以作为独立的数据源接入现有系统。做毕业设计的同学可以考虑在报表模块加一两个这样的数据来源展示出来的效果会比纯增删改查亮眼很多。最后提醒一句一定要自己动手把代码敲一遍源码看一眼和亲手写一遍掌握程度完全是两回事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑