简介:基于SpringBoot+Vue构建的智能植物养护系统,是一份面向高校Java毕设及植物养护场景开发者的完整源码资料。系统前端使用Vue实现直观界面,后端依托SpringBoot处理数据与业务流程,覆盖植物信息展示、生长数据采集、养护计划自动生成等多用户协作模块,适合用于课程设计、毕业设计二次开发。压缩包共404个文件,约9.44MB,内含147个java后端源码、165个xml资源配置、43个jar依赖库,并附带sql数据库脚本、数据库设计文档与SpringBoot开发文档,结构清晰,便于导入开发环境直接运行学习。目前已有144人学习浏览。通过这套资源,使用者可快速掌握前后端分离项目的工程布局,理解从植物信息管理到养护规则推送的完整业务链路,也可基于现有代码扩展传感器数据接入或管理端功能,是一套体积小巧但体系完整的可运行项目参考。
1. 先搞清这个SpringBoot智能植物养护系统能解决什么
每到毕业设计选题季,Java 方向的学生十有八九是在各种管理系统里打转。同样是 SpringBoot、Vue、Java毕设这几件套,“智能植物养护系统”这个题目就显得有点意思:它不是又一个单纯增删改查的后台,而是把植物档案、环境传感器数据、浇水施肥提醒串成一条完整链路。这套资源最大的价值在于,你不用真的买传感器也能先把系统跑起来,包里内置的模拟数据负责让前后端全链路转起来。它适合两类人:一类是拿它做毕业设计的学生,想找一个结构完整、答辩好讲的项目;另一类是刚学完 SpringBoot 和 Vue、想练真实前后端联调的新手。只要你能上手 Java 基础,照着下文步骤走,一个下午就能把系统拉起来。
2. 技术选型:为什么是SpringBoot+Vue,而不是别的组合
2.1 后端:SpringBoot 版本差异与依赖选择
这套系统后端核心是 SpringBoot,MySQL 做持久化,MyBatis-Plus 操作数据库。先谈版本:SpringBoot 2.7.x 配 JDK 8 是目前兼容性最好的组合,3.x 系列虽然新,但强制要求 JDK 17,很多高校机房和教材环境还停留在 JDK 8,你在项目里贸然用了 3.x,答辩演示时换个机器跑不起来,血泪经验。除非你确认目标环境已经是 JDK 17,否则我建议锁在 2.7.x。
核心依赖长这样,pom.xml 里至少要保证下面几个关键片段存在:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> </dependencies>逻辑说明:spring-boot-starter-web提供内嵌 Tomcat 和 Spring MVC,处理接口请求全靠它;mybatis-spring-boot-starter负责把 Mapper 接口和 XML 绑定到数据库操作上,2.3.x 拿到了 SpringBoot 2.7 的官方兼容列表;mysql-connector-j是 MySQL 8 的官方驱动,如果你本地是 5.7,也可以换成mysql-connector-java:5.1.49系列,但统一用 8.x 驱动连 5.7 库也没问题。版本对齐这件事别看玄学,直接查spring-boot-starter-parent的依赖管理结果,大部分冲突都能提前避掉。
如果包里带了 MyBatis-Plus,通常会看到mybatis-plus-boot-starter的 3.5.x 版本,它对标 SpringBoot 2.x 基本没有坑,只要注意全局主键生成策略和数据库自增策略保持一致,这一节后面会专门讲。
2.2 前端:Vue2 还是 Vue3,按项目现状决定
这套系统的前端是 Vue,配合 Element UI 做页面组件。我拆过的不少学生项目里,前端还是 Vue2 + Element UI 的老组合,原因是 2019 到 2021 年间的教程和模板大量基于 Vue2,资源包里如果带的是这套,你就别手痒升级 Vue3。Vue3 的生态兼容性虽然已经很好,但 Element UI 本身不直接支持 Vue3,得换成 Element Plus,组件 API 和表单验证方式有差异,升级过程中冒出来的小问题足够消耗你一晚上。
如果你拿到的是 Vue3 + Element Plus 的版本,同样不要降级。原则只有一个:跟着包的现状走,别先动刀改造前端。前端的关键配置在vue.config.js里,主要做开发代理,防止联调时被跨域卡住:
// vue.config.js module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } } };逻辑说明:前端开发服务器跑在 8080,后端业务端口是 8081,浏览器直接向 8080 讨数据时,/api开头的请求会被 dev-server 转发到http://localhost:8081,后端响应后浏览器在开发者工具里看到的还是来自 8080 的响应,从而避开跨域限制。参数说明:port是前端端口,建议固定而不是用默认的随机端口,因为后端日志里打印的 CORS 信息和前端代理都要用它做参照;target必须跟application.yml中server.port保持一致;changeOrigin: true会让请求头里的 Host 字段改写为目标地址,很多后端框架会校验 Host,这个变量不改的话某些接口会返回 403。
2.3 为什么这样组合在毕设里吃香
这套选型并不是因为技术新颖,恰恰相反,它的优点就是“普通”。SpringBoot 的 REST 接口写法、Vue 的组件化、MyBatis-Plus 的BaseMapper通用方法,都是网上资料最多的技术点,出任何报错都能在三分钟内搜到解决方案。答辩时老师一眼能看懂系统架构,不会追着问冷门框架;项目结构也符合三层架构的习惯——controller收请求、service处理业务、mapper访问数据库,再加一个config包放 CORS、拦截器等配置,评审看你的代码结构就会觉得你是“按工程规范写的”,印象分先拿一点。反过来,如果你在毕设里用了冷门 ORM 或者奇怪的前端框架,答辩现场一旦被问“为什么用这个”,答不出所以然,反而丢分。
3. 核心业务与数据库设计:从植物档案到养护闭环
3.1 四个核心业务模块串起养护闭环
这套系统的业务逻辑不复杂,但胜在闭环完整。它围绕四个方面运转:
植物档案模块负责维护“家里有什么植物”的基本信息,包括植物名称、品种、习性说明、初始状态,以及最关键的一个字段:浇水周期。环境数据模块负责记录温度、湿度、光照强度这三类传感器数据,这是判断植物是否需要养护的依据。养护任务模块则根据预设规则和环境数据生成待办事件,比如“需要浇水”“需要施肥”“光照不足需要挪位置”。最后是系统管理模块,处理用户登录、密码修改这类基础操作。
业务流程也很好理解:管理员在页面上录入一盆绿萝,系统在plant表生成一条档案;模拟传感器定时向sensor_record表写入当前环境的温度和湿度;后端规则引擎读取“当某植物超过 N 天没浇水且空气湿度低于 30% 时,生成一条浇水任务”;用户在养护任务页面看到提醒,点“完成”后更新care_task表的状态。这套闭环用来做毕设,逻辑清晰,答辩时顺着数据流讲下来很清楚。
3.2 关键表设计与建表 SQL
数据库是整个系统的地基,设计稿通常落在四张表上。包里的 SQL 初始化脚本一般会包含建库、建表和测试数据,我这里提炼出最核心的四张表结构:
CREATE TABLE `sys_user` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `username` VARCHAR(50) NOT NULL COMMENT '登录名', `password` VARCHAR(100) NOT NULL COMMENT '加密后的密码', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表'; CREATE TABLE `plant` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '植物ID', `name` VARCHAR(50) NOT NULL COMMENT '植物名称', `variety` VARCHAR(50) COMMENT '品种', `water_cycle_days` INT NOT NULL DEFAULT 3 COMMENT '浇水周期(天)', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '健康状态 0正常 1缺水 2光照不足', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='植物档案表'; CREATE TABLE `sensor_record` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '记录ID', `plant_id` INT NOT NULL COMMENT '植物ID', `temperature` DECIMAL(5,2) COMMENT '温度(摄氏度)', `humidity` DECIMAL(5,2) COMMENT '湿度(百分比)', `light` DECIMAL(5,2) COMMENT '光照强度(lux)', `record_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '记录时间', PRIMARY KEY (`id`), KEY `idx_plant_time` (`plant_id`, `record_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='传感器环境记录表'; CREATE TABLE `care_task` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '任务ID', `plant_id` INT NOT NULL COMMENT '植物ID', `task_type` VARCHAR(20) NOT NULL COMMENT '任务类型: WATER/FERTILIZE/LIGHT', `content` VARCHAR(255) COMMENT '任务内容描述', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态 0待处理 1已完成', `due_time` DATETIME COMMENT '截止时间', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='养护任务表';说明几个关键点:一是字符集统一utf8mb4,特别是用户头像昵称这类字段可能存 Emoji,用utf8会在写入时直接报Incorrect string value错误;二是plant.status字段刻意做成数值类型,这样后续做统计时可以直接GROUP BY status出饼图数据;三是sensor_record表加了idx_plant_time复合索引,前端按植物展示时间曲线时,这个索引能让查询快很多,数据量大了也扛得住;四是care_task.status上的单列索引,是为了支撑待办列表的“只查状态为 0 的任务”这种高频查询。
3.3 接口设计:增删改查之外的业务接口
在纯管理系统里,接口就是save、delete、page这类通用方法,但植物养护系统多出来的几个接口,恰恰是答辩时值得展开讲的地方。以新增植物为例,Controller 层写法如下:
@RestController @RequestMapping("/api/plants") public class PlantController { @PostMapping public Result addPlant(@RequestBody Plant plant) { plant.setStatus(0); plantService.savePlant(plant); return Result.success("添加成功"); } @GetMapping("/{id}/todayTasks") public Result<List<CareTask>> todayTasks(@PathVariable Integer id) { return Result.success(careTaskService.listTodayTasks(id)); } }逻辑说明:新增植物是普通 POST 接口,核心在setStatus(0)这行,把新登记的植物默认标记为健康状态,后续才可能被传感器数据触发状态变更。todayTasks接口是这套系统的特色接口,一次请求把某盆植物今天所有待处理的养护任务返回给前端,前端就能在卡片页直接渲染“该浇水了”之类的警示条。参数说明:@PathVariable Integer id传的是植物 ID,在listTodayTasks内部会拿它关联care_task表,并拼上日期条件due_time BETWEEN curdate() AND curdate() + INTERVAL 1 DAY,如果包里没有这个接口,你自己按这个思路加一个也不难。
4. 本地运行起来:环境、配置、启动三连
4.1 环境准备:JDK、MySQL、Node 的版本对齐
拆这种项目包,第一件事不是看代码,而是先确认环境。这套系统的常见环境要求如下:JDK 8 或 JDK 17(视包内 SpringBoot 版本而定);Maven 3.6 以上;MySQL 5.7 或 8.0;Node.js 14 或 16(对应 Vue2 项目,Vue3 项目建议 Node 16 以上)。建议先跑一遍这三个命令确认:
java -version mvn -v node -vjava -version看 JDK 大版本,输出里带有1.8.0_xx就说明是 JDK 8,如果显示17.0.x那是 JDK 17。mvn -v会一并显示 Maven 版本和它使用的 Java 版本,需要确保 Maven 用的 JDK 和项目要求一致。node -v只确认 Node 存在,具体版本可以靠包里的package.json里engines字段判断,如果没有这个字段,就按 Vue CLI 4 和 Webpack 4 的老规矩来,Node 14 最稳。
我一般建议把这三个命令的输出版本截图存下来,后面出问题时先排除环境变量错乱,再查代码。
4.2 修改配置并初始化数据库
数据库建议先手动建一个空库,再执行包里的 SQL 脚本。如果你拿到的是整套源码,注意改掉application.yml里的数据源配置:
server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/plant_care?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true重点说明:serverTimezone=Asia/Shanghai这个参数不是可选项,缺了它,数据库连接时区默认取服务器时区,如果 MySQL 装在 Docker 里默认 UTC,你插入的时间会比北京时间早 8 个小时,属于经典翻车点。map-underscore-to-camel-case: true的作用是让数据库的create_time字段自动映射到 Java 的createTime属性,省去在 XML 里写一堆resultMap。
初始化数据库用命令行或客户端都行,命令方式最省事:
mysql -uroot -p plant_care < sql/init.sql这里注意执行顺序:先建库再导入表和数据,如果你直接导入且脚本里没写CREATE DATABASE,会报No database selected。导入完成后建议先查一下sys_user表,确认里面有一条测试账号数据,没有就手动插入一条,登录环节全靠它。
4.3 启动后端和前端,完成第一次联调
后端启动直接走 Maven:
cd backend mvn spring-boot:run看到Started Application in x.xxx seconds即可确认后端就绪。后端启动的后建议先访问http://localhost:8081/api/plants,虽然返回可能是空数组或空对象,但至少能确认端口和数据源没问题。如果端口起不来,先看有没有别的进程占用了 8081,Windows 上用netstat -ano | findstr 8081查进程号再结束任务即可。
前端启动分两步:
cd frontend npm install npm run servenpm install会按照package.json锁定依赖版本安装,第一次跑会花几分钟。常见问题是node-sass编译失败,如果你拿到的是老项目,里面装的node-sass对 Node 版本要求很苛刻,安装时可以换用npm install --registry=https://registry.npmmirror.com拉镜像源,但还是建议直接看报错再处理,Node 14 下通常能顺利过去。启动完成后浏览器访问http://localhost:8080,能看到登录页,输入包里初始化的账号密码(通常在 SQL 脚本注释里写着,比如 admin / 123456),能进首页差不多就算联调通过了。
5. 常见问题与避坑:跨域、时区、模拟数据三座山
5.1 前端调接口报跨域,报错看起来像是 CORS
现象:浏览器控制台报Access to XMLHttpRequest at 'http://localhost:8081/api/...' from origin 'http://localhost:8080' has been blocked by CORS policy,接口在网络面板里虽然返回 200,但前端拿不到响应数据。
原因:这是前后端端口不一致导致的典型跨域,要么后端加了全局 CORS 配置,同时前端又开了代理,等于同一个请求被两个机制各处理了一遍,响应头里出现多个Access-Control-Allow-Origin时浏览器直接拦截。要么就是代理没生效,请求还是直接打到了后端原生端口上。
解决:先确认vue.config.js里代理有没有配,后端config包下如果有一个CorsConfig类实现了WebMvcConfigurer,二选一就行。我习惯的做法是保留后端 CORS,关掉前端代理,这样部署到服务器后不用再改前端;如果你只在本地开发,保留前端代理、删掉后端 CORS 配置更简单。改动后两个服务都要重启,浏览器强刷缓存。
5.2 插入记录后时间相差 8 个小时
现象:前端列表显示“浇水时间”比当前时间早 8 小时,数据库里查出来的时间也不对。
原因:MySQL 连接串缺少serverTimezone=Asia/Shanghai,JDBC 驱动默认使用服务器时区,Docker 里装的 MySQL 通常默认 UTC,导致 DATETIME 字段写入和读取都存在时区转换偏差。
解决:按上一章给出的application.yml改连接串,把serverTimezone=Asia/Shanghai加进 URL。改完重启后端,再插一条数据验证。注意如果 MySQL 端字段类型是TIMESTAMP,它内部就会做时区转换,所以你也要确保 MySQL 本身的time_zone变量是对的,用SELECT NOW()看一眼,结果不对就再检查数据库容器环境变量里的TZ。
5.3 新增植物时主键冲突或主键为空
现象:表单提交后报Duplicate entry 'xxx' for key 'PRIMARY',或者新增成功后 ID 变成 0,后续关联的传感器记录全部指向错误植物。
原因:MyBatis-Plus 全局主键IdType和数据库自增策略对不上。如果你数据库表是AUTO_INCREMENT,实体里却用@TableId(type = IdType.ASSIGN_ID),系统会先生成一个雪花 ID 再插进去,撞上自增主键的概率虽然低,但一旦 MySQL 自增计数因为某个原因跳号,就会冲突。反过来如果表结构不写自增、实体类也没配主键,某些版本下会插入 0。
解决:实体类主键字段统一加@TableId(type = IdType.AUTO),同时保证建表 SQL 里对应字段是AUTO_INCREMENT。如果包里有多个实体,就全局搜一下@TableId注解,统一策略再重启。另外确认 MyBatis-Plus 全局配置id-type: auto和注解不冲突。
5.4 模拟数据不生成,传感器图表一直空白
现象:登录后进入环境数据页面,温度湿度曲线什么都没有,后端日志也看不到任何插入记录。
原因:大部分情况下是定时任务没启动。SpringBoot 里用@Scheduled注解做定时模拟数据写入时,必须要在启动类上添加@EnableScheduling注解,新手经常只写了子方法的注解,忘了在主类开总开关。另一种情况是模拟数据生成器的@Scheduled被@ConditionalOnProperty控制,配置里该项默认关闭。
解决:检查启动类上有没有@EnableScheduling,没有就加上。再看 application.yml 里有没有诸如simulate.enabled=true这样的配置项,没有就在application.yml里补上并确定值的拼写和配置文件完全一致。改完重启后端,等一分钟看日志,如果日志里没有任何“模拟数据写入”输出,就在模拟方法里临时加一行System.out.println,确认方法有没有被调用,逐步缩小排查范围。
6. 进阶验证:把模拟数据替换成真实传感器
模拟数据帮你把前后端链路跑通后,如果你想在毕设里加一点硬核的东西,可以尝试把数据源从定时模拟替换成真实传感器上报。最省事的方案是:后端保留现有的/api/sensor/records写入接口,前端不用改,只需要在定时任务里改动数据来源。原先的模拟任务大概长这样:
@Component public class SensorDataSimulator { @Scheduled(fixedRate = 5000) public void generate() { SensorRecord record = new SensorRecord(); record.setPlantId(1); record.setTemperature(25.0 + Math.random() * 5); record.setHumidity(40.0 + Math.random() * 20); record.setLight(800 + Math.random() * 200); sensorRecordService.save(record); } }参数说明:fixedRate = 5000表示每 5 秒执行一次,如果要改成真实传感器,把这个定时任务关掉,另写一个 POST 接口接收外部设备上报即可。真实场景里,用 ESP8266 或者树莓派加一个 DHT11 温湿度传感器,每 30 秒向/api/sensor/records发送一次 JSON 数据,后端不用动。
我一直习惯用一个笨办法验证整个逻辑:把模拟数据关掉,手工往sensor_record表里插一条湿度 25% 的记录,再刷新养护任务页面,如果能生成一条“浇水”任务,说明规则引擎和页面联动都没问题;如果没生成,优先查规则判断条件里的阈值和任务生成任务的 SQL 拼接。从那以后,我每次接手这类带模拟数据的项目,都会强制自己先关掉模拟源、手工构造一条边界数据,把全链路验证一遍再交给别人用。这个习惯帮我少踩了很多“演示时数据不刷新”的尴尬坑,也希望能帮你顺顺利利把系统跑起来。
本文还有配套的精品资源,点击获取