温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片!
一、 项目背景与意义
随着现代农业向精细化、智能化方向发展,传统的小香葱种植管理模式在数据记录、生长监控、成本核算和销售追溯等方面面临诸多挑战。种植户通常依赖纸质记录或简单的电子表格,导致数据分散、易丢失、难以分析,无法为科学决策提供有效支持。
本项目旨在设计并实现一个基于SpringBoot的小香葱种植管理系统,其核心意义在于:
- 提升管理效率:将种植计划、农事记录、投入品管理、采收销售等环节数字化,实现一站式管理,减少人工重复劳动。
- 实现数据驱动决策:通过系统化记录生长环境、施肥用药、产量等数据,为优化种植方案、预测产量、控制成本提供数据依据。
- 保障农产品安全与可追溯:建立完整的农事操作档案,实现从种植到销售的全流程追溯,提升产品信誉与市场竞争力。
- 技术实践价值:作为一个典型的SpringBoot全栈项目,它涵盖了企业级应用开发的核心流程,包括需求分析、架构设计、数据库建模、后端API开发、前端交互等,具有很好的学习与参考价值。
二、 技术栈选型
本项目采用前后端分离架构,后端基于SpringBoot生态,前端使用Vue.js,具体技术选型如下:
1. 后端技术栈
- 核心框架:Spring Boot 2.7.x (提供快速启动和自动配置)
- Web框架:Spring MVC
- 数据持久层:MyBatis-Plus (简化CRUD操作)
- 数据库:MySQL 8.0 (关系型数据库,存储业务数据)
- 数据库连接池:HikariCP (高性能连接池)
- 权限控制:Spring Security + JWT (实现用户认证与接口鉴权)
- API文档:Knife4j (集成Swagger,生成在线API文档)
- 项目构建:Maven
- 其他工具:Lombok (简化JavaBean代码), Hutool (Java工具包)
2. 前端技术栈
- 核心框架:Vue 3.x (组合式API)
- 构建工具:Vite (快速开发与构建)
- UI组件库:Element Plus (基于Vue 3的桌面端组件库)
- 状态管理:Pinia (Vue官方推荐的状态管理库)
- 路由:Vue Router 4.x
- HTTP客户端:Axios
- 图表:ECharts (用于数据可视化展示)
3. 开发与部署
- 版本控制:Git
- 后端部署:打包为Jar,通过Docker容器或直接使用Java命令运行。
- 前端部署:构建为静态资源,部署至Nginx或对象存储。
三、 系统核心功能模块设计
系统主要面向种植基地管理员、技术员和销售员,核心功能模块如下:
- 1. 基地与地块管理:维护种植基地、大棚、地块的基本信息。
- 2. 种植计划管理:制定小香葱的种植批次、品种、计划面积、预计周期等。
- 3. 农事操作管理:记录播种、灌溉、施肥、病虫害防治等日常操作,支持图片上传。
- 4. 投入品管理:管理种子、肥料、农药等农资的入库、出库与库存。
- 5. 环境数据监测(可选集成):对接传感器,记录温度、湿度、光照等数据。
- 6. 采收与销售管理:记录采收批次、重量、等级,以及销售客户、价格、订单信息。
- 7. 数据统计与报表:提供产量统计、成本分析、农事日历等可视化报表。
- 8. 系统管理:用户、角色、权限、操作日志管理。
四、 数据库核心表设计
以下是几个核心业务表的结构设计:
-- 种植批次表 CREATE TABLE `planting_batch` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `batch_no` varchar(50) NOT NULL COMMENT '批次编号', `variety` varchar(100) DEFAULT NULL COMMENT '品种', `plot_id` bigint DEFAULT NULL COMMENT '种植地块ID', `plant_area` decimal(10,2) DEFAULT NULL COMMENT '种植面积(亩)', `plan_plant_date` date DEFAULT NULL COMMENT '计划种植日期', `actual_plant_date` date DEFAULT NULL COMMENT '实际种植日期', `status` tinyint DEFAULT '1' COMMENT '状态(1:种植中, 2:已采收)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_batch_no` (`batch_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='种植批次表'; -- 农事记录表 CREATE TABLE `farming_record` ( `id` bigint NOT NULL AUTO_INCREMENT, `batch_id` bigint NOT NULL COMMENT '关联种植批次ID', `operation_type` tinyint NOT NULL COMMENT '操作类型(1:播种,2:灌溉,3:施肥,4:施药,5:除草,6:其他)', `operation_date` date NOT NULL COMMENT '操作日期', `description` text COMMENT '操作描述', `operator` varchar(50) DEFAULT NULL COMMENT '操作人', `material_used` varchar(500) DEFAULT NULL COMMENT '使用农资', `image_urls` varchar(1000) DEFAULT NULL COMMENT '现场图片(多张,逗号分隔)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_batch_id` (`batch_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='农事记录表'; -- 采收记录表 CREATE TABLE `harvest_record` ( `id` bigint NOT NULL AUTO_INCREMENT, `batch_id` bigint NOT NULL, `harvest_date` date NOT NULL, `harvest_weight` decimal(10,2) NOT NULL COMMENT '采收重量(公斤)', `grade` varchar(20) DEFAULT NULL COMMENT '等级', `storage_location` varchar(200) DEFAULT NULL COMMENT '存放位置', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_batch_id` (`batch_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采收记录表';五、 核心代码实现示例
1. 后端实体类与Mapper (使用MyBatis-Plus)
import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDate; import java.time.LocalDateTime; @Data @TableName("planting_batch") public class PlantingBatch { @TableId(type = IdType.AUTO) private Long id; private String batchNo; private String variety; private Long plotId; private Double plantArea; private LocalDate planPlantDate; private LocalDate actualPlantDate; private Integer status; @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; }import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper public interface PlantingBatchMapper extends BaseMapper<PlantingBatch> { // MyBatis-Plus已提供基础CRUD方法 }2. Service层与业务逻辑
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.time.LocalDate; @Service public class PlantingBatchServiceImpl extends ServiceImpl<PlantingBatchMapper, PlantingBatch> implements PlantingBatchService { @Override public boolean startBatch(PlantingBatchDTO dto) { // 1. 校验数据 if (dto.getPlanPlantDate().isBefore(LocalDate.now())) { throw new BusinessException("计划种植日期不能早于今天"); } // 2. 生成批次号 String batchNo = "PB" + System.currentTimeMillis(); // 3. DTO转Entity并保存 PlantingBatch batch = new PlantingBatch(); BeanUtils.copyProperties(dto, batch); batch.setBatchNo(batchNo); batch.setStatus(1); // 状态:种植中 return this.save(batch); } @Override public PageResult<PlantingBatchVO> queryPage(PlantingBatchQuery query) { // 构建查询条件 LambdaQueryWrapper<PlantingBatch> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(query.getBatchNo()), PlantingBatch::getBatchNo, query.getBatchNo()) .eq(query.getStatus() != null, PlantingBatch::getStatus, query.getStatus()) .orderByDesc(PlantingBatch::getCreateTime); // 分页查询 Page<PlantingBatch> page = this.page(new Page<>(query.getPageNum(), query.getPageSize()), wrapper); // 转换为VO列表 List<PlantingBatchVO> voList = page.getRecords().stream() .map(this::convertToVO) .collect(Collectors.toList()); return new PageResult<>(page.getTotal(), voList); } private PlantingBatchVO convertToVO(PlantingBatch batch) { // 转换逻辑,可关联查询地块名称等 PlantingBatchVO vo = new PlantingBatchVO(); BeanUtils.copyProperties(batch, vo); return vo; } }3. Controller层REST API
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @RestController @RequestMapping("/api/planting-batch") @Api(tags = "种植批次管理") public class PlantingBatchController { @Autowired private PlantingBatchService plantingBatchService; @PostMapping @ApiOperation("创建种植批次") public R<String> create(@RequestBody @Valid PlantingBatchDTO dto) { boolean success = plantingBatchService.startBatch(dto); return success ? R.ok("创建成功") : R.fail("创建失败"); } @GetMapping("/page") @ApiOperation("分页查询种植批次") public R<PageResult<PlantingBatchVO>> page(PlantingBatchQuery query) { PageResult<PlantingBatchVO> pageResult = plantingBatchService.queryPage(query); return R.ok(pageResult); } @PutMapping("/{id}/harvest") @ApiOperation("标记批次已采收") public R<String> markHarvested(@PathVariable Long id) { // 业务逻辑:更新状态,并可能创建采收记录 boolean updated = plantingBatchService.markAsHarvested(id); return updated ? R.ok("操作成功") : R.fail("操作失败"); } }4. 前端Vue组件示例 (查询页面)
<template> <div class="planting-batch-page"> <el-card> <!-- 查询条件 --> <el-form :model="queryParams" inline> <el-form-item label="批次号"> <el-input v-model="queryParams.batchNo" placeholder="请输入批次号" clearable /> </el-form-item> <el-form-item label="状态"> <el-select v-model="queryParams.status" placeholder="请选择" clearable> <el-option label="种植中" :value="1" /> <el-option label="已采收" :value="2" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery">查询</el-button> <el-button @click="resetQuery">重置</el-button> </el-form-item> </el-form> <!-- 操作按钮 --> <div style="margin-bottom: 20px;"> <el-button type="primary" @click="handleCreate">新建批次</el-button> </div> <!-- 数据表格 --> <el-table :data="tableData" border> <el-table-column prop="batchNo" label="批次号" /> <el-table-column prop="variety" label="品种" /> <el-table-column prop="plantArea" label="面积(亩)" /> <el-table-column prop="planPlantDate" label="计划种植日" /> <el-table-column prop="status" label="状态"> <template #default="scope"> <el-tag :type="scope.row.status === 1 ? 'success' : 'info'"> {{ scope.row.status === 1 ? '种植中' : '已采收' }} </el-tag> </template> </el-table-column> <el-table-column label="操作" width="200"> <template #default="scope"> <el-button size="small" @click="handleView(scope.row)">查看</el-button> <el-button size="small" type="warning" @click="handleHarvest(scope.row)" v-if="scope.row.status === 1">标记采收</el-button> </template> </el-table-column> </el-table> <!-- 分页 --> <div class="pagination-container"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.pageNum" :page-sizes="[10, 20, 50]" :page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> </div> </el-card> </div> </template> <script setup> import { ref, onMounted } from 'vue' import { getPlantingBatchPage, markHarvested } from '@/api/plantingBatch' const queryParams = ref({ batchNo: '', status: null, pageNum: 1, pageSize: 10 }) const tableData = ref([]) const total = ref(0) // 加载数据 const loadData = async () => { const res = await getPlantingBatchPage(queryParams.value) if (res.code === 200) { tableData.value = res.data.list total.value = res.data.total } } // 查询 const handleQuery = () => { queryParams.value.pageNum = 1 loadData() } // 标记采收 const handleHarvest = async (row) => { try { await ElMessageBox.confirm('确认标记该批次已采收?', '提示', { type: 'warning' }) const res = await markHarvested(row.id) if (res.code === 200) { ElMessage.success(res.msg) loadData() // 刷新列表 } } catch (error) { // 用户取消 } } onMounted(() => { loadData() }) </script>六、 总结与展望
本文详细阐述了基于SpringBoot的小香葱种植管理系统的设计背景、技术选型、功能模块、数据库设计以及核心代码实现。该系统通过将传统种植业务数字化,能够有效提升管理效率与决策科学性。
后续可扩展方向:
- 物联网集成:接入温湿度