十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue+MyBatis实战:垃圾分类管理系统设计与实现

SpringBoot+Vue+MyBatis实战:垃圾分类管理系统设计与实现 手头上这套“基于SpringBootVue的城市垃圾分类管理系统”是我最近整理完的一套完整练手项目Java后端 MySQL存储 MyBatis做持久层前端用Vue搭界面。代码跑通之后我觉得这套东西对两类人特别有价值一类是准备做毕业设计的在校生另一类是刚学完Java基础、想看看真实项目长什么样的自学者。为什么这么说因为这个系统麻雀虽小五脏俱全有用户端和管理端两个角色有垃圾分类查询、预约回收、积分兑换这些实际业务场景还涉及登录鉴权、分页查询、文件上传、前后端联调这些日常开发必用的技术点。你把这套东西吃透出去面Java岗位别人问SpringBoot怎么组织项目、MyBatis怎么用、Vue怎么调后端接口你都能答得上来。接下来我把这套系统的设计和实现完整拆开讲。1. 项目整体设计与技术选型思路1.1 为什么是SpringBoot Vue MySQL MyBatis这套组合很多新手选技术栈的时候容易纠结其实你只要想明白一件事技术选型不是越新越好、越多越好而是要看项目规模和团队能力。这套系统我选这四样东西核心逻辑就三条。第一SpringBoot是目前Java后端开发事实上的标准框架。它比传统Spring MVC省掉了大量XML配置内嵌了Tomcat打一个jar包就能跑起来。对于这种中小型管理系统你不需要分布式、不需要微服务一个SpringBoot应用把接口写好就够了。从学习角度讲SpringBoot也是你从“会Java语法”到“能干活”之间最短的桥梁。第二MyBatis作为持久层框架最大的优势是SQL你自己写、自己控。这话听起来像缺点但实际在业务系统里反而是优点。比如垃圾分类查询需要根据垃圾名称模糊匹配、按分类类型筛选甚至多个条件组合MyBatis的动态SQL写起来非常直观。相比Hibernate那种全自动ORMMyBatis更容易排查问题也更容易做SQL性能优化。第三MySQL没什么好说的开源、免费、稳定中小型项目首选。最关键是网上资料多出了问题一搜就有答案。第四Vue这套前端方案在国内管理后台领域已经是事实标准。Vue本身上手曲线平缓配合Element UI组件库不需要你懂多少JavaScript高级特性就能搭出像样的管理界面。而且Vue的生态很成熟路由、状态管理、HTTP请求都有成熟的配套方案。这套组合选下来本质上就是当前国内中小型Web项目最经典的范式。你跑通这个项目等于把国内绝大多数公司里日常开发的流程走了一遍价值就在这里。1.2 功能模块划分用户端和管理端各做什么垃圾分类管理系统核心角色是两类普通用户居民和管理员。我设计功能的时候坚持一个原则——每个模块必须能回答一个真实的业务问题。用户端要解决的是这些问题我不知道某个垃圾属于哪一类怎么办所以要有“垃圾分类查询”支持按名称搜索和按分类浏览。我有些可回收物想处理掉怎么办所以要有“预约回收”用户提交预约单填写垃圾类型、大概重量、上门时间。我参与分类回收能有什么激励所以要有“积分中心”预约回收完成可以获得积分积分可以兑换小礼品。另外还配了“留言反馈”和“新闻公告”前者让用户能联系管理员后者用来发布垃圾分类政策动态。管理端要解决的是管理效率问题管理员登录后需要维护垃圾信息库——增删改查各类垃圾的名称、分类、描述、处理方式。这是整个系统最核心的数据资产。用户提交的预约回收单要有人处理所以要有“回收订单管理”管理员查看订单列表、标记完成状态。用户积分可能出现异常所以要有“积分记录管理”可以查看和调整。另外还有公告发布、用户管理、留言审核这些基础功能。我见过很多新手做管理系统容易犯一个毛病功能堆得很多但每个都是凑数的闭环走不通。比如做了积分功能但用户不知道积分怎么来的、怎么花的这就是没闭环。我这套系统的设计思路是每个用户端功能都有对应的管理端功能接住每个业务动作都有状态流转这样才能算一个完整可用的系统。1.3 数据库表设计七张表怎么撑起整个业务数据库设计是整套系统的地基。我用了七张表不算多但每张表的字段都经过斟酌尽量不冗余、不缺失。用户表user是系统的第一张表字段包含id、用户名、密码、昵称、手机号、头像、积分余额、角色user/admin、创建时间。密码我用了MD5加盐存储这个后面详细讲。角色字段决定了登录后跳转用户端还是管理端。垃圾信息表garbage是第二张核心表字段有id、垃圾名称、分类类型可回收/厨余/有害/其他、分类描述、处理方式、图片地址。这里要注意分类描述和处理方式是两回事描述是说这个垃圾是什么处理方式是说它应该怎么被处理。比如废电池描述是“充电电池、纽扣电池等”处理方式是“投放至有害垃圾收集容器交由专业单位处理”。预约回收表recycle_order是业务流转的关键字段有id、用户id、垃圾类型、预估重量、预约时间、上门地址、备注、状态待处理/已完成/已取消、创建时间。状态字段是整个业务闭环的核心前端列表根据状态显示不同的操作按钮后台根据状态筛选订单。积分记录表points_record记录积分变动的每一笔流水谁、什么时间、因为什么回收/兑换/管理员调整、变动了多少积分、剩余多少。这样设计是为了可追溯用户投诉积分不对的时候管理员能查流水。积分商品表goods和兑换记录表exchange_record支撑积分兑换功能。商品表有名称、所需积分、库存、图片、描述兑换记录表把用户和商品关联起来记录兑换时间和状态。公告表notice比较简单标题、内容、发布时间。前台首页拉最新一条展示后台可以维护多条。另外还有一张留言表message用户提交、管理员可以回复。这七张表的关系链很清晰用户产生回收订单和兑换记录分类垃圾信息和公告由管理员维护。外键我没有在数据库层面强制约束而是在代码里控制的理由是这种体量的系统用逻辑外键更灵活避免删除数据时被外键卡住。2. 后端SpringBoot MyBatis核心实现拆解2.1 项目初始化和分层目录结构搭建这套系统后端我用的开发工具是IntelliJ IDEASpringBoot版本选的2.7.x。这里有个很重要的提醒不要用SpringBoot 3.x因为3.x基于JDK 17而且javax包改成了jakarta很多教程和第三方工具还不兼容对新手来说坑太多。2.7.x稳定、教程多、各种踩坑记录都能搜到等你有经验了再去玩3.x不迟。创建项目时在Spring Initializr里勾选这几个依赖Spring Web、MyBatis Framework、MySQL Driver、Lombok。其中Lombok我强烈推荐它通过注解自动生成getter/setter/构造方法让实体类干净很多。不过要记得在IDEA里安装Lombok插件并且开启Annotation Processing否则编译报错找不到方法。后端代码我按标准的三层架构分包com.city.garbage ├── controller # 接收前端请求返回结果 ├── service # 业务逻辑层处理具体的业务规则 ├── dao # MyBatis的Mapper接口对应一条条SQL ├── entity # 实体类和数据库表字段映射 ├── common # 公共类统一返回结果、分页参数等 ├── config # 配置类拦截器、跨域等 └── utils # 工具类MD5加密、JWT工具等很多新手写项目喜欢把代码全堆在controller里几行代码搞定一个接口。但用了这种分层结构之后每个类的职责非常清楚controller只负责参数接收和结果返回service负责业务逻辑dao只管数据存取。这么做的好处是比如后面我要加“预约回收后自动加积分”这个业务规则我只需要在service层加几行代码就行controller和dao都不用动。2.2 登录鉴权MD5加盐 拦截器管理系统的登录鉴权我先说结论用Session 拦截器就够了不需要上JWT。为什么JWT适合前后端完全分离、需要跨域认证的场景但如果你用Vue开发服务器端渲染时直接跟着项目的方案走Session方案要简单得多而且不依赖额外的配置。登录的流程是这样的前端把用户名和密码传给后端后端用MD5加盐的方式校验密码。密码明文存数据库是大忌直接哈希存储也不够安全因为彩虹表可以反查。我的做法是初始化用户的时候用“原始密码 固定盐值”拼接再取MD5这样即使用户设置的密码很简单存储的也是复杂的一串。登录成功后把用户id、用户名、角色存进Session。同时我写了一个LoginInterceptor拦截器在config里注册时指定拦截路径。需要放行的路径包括登录接口、注册接口、垃圾查询接口、公告查询接口因为这些是用户未登录就能看的。需要拦截的路径是所有/admin/开头的管理端接口、预约回收、积分兑换这些操作类接口。拦截器的核心逻辑只有几行从Session里拿用户信息拿不到就返回401状态码前端拿到这个状态码就跳转到登录页。这里有个细节值得提醒跨域和Session是天然冲突的因为浏览器跨域请求默认不带Cookie。所以我在前端用代理方式解决跨域下面会讲后端就不需要额外配置CORSSession就正常工作。如果你非要用CORS跨域那就必须配置allowCredentials(true)而且前端axios要设置withCredentials: true少一个都不行。2.3 统一返回结果类的设计前后端联调的时候最头疼的问题之一是接口返回格式不统一。有的接口返回{“code”:200, “data”:[]}有的直接返回一个数组前端代码写得越多越乱。我从一开始就定了一个统一返回类Result所有接口都返回这个格式public class ResultT { private Integer code; // 200成功500失败401未登录 private String msg; // 提示信息 private T data; // 数据 // 静态方法成功带数据、成功不带数据、失败带消息 }controller里所有接口的返回值类型都是Result成功就调用Result.success(data)失败就调用Result.error(“参数错误”)。这样前端只需要做一次统一的响应处理根据code判断业务成功还是失败再根据msg弹提示data里取数据。这个习惯建议你从一开始就养成它是专业开发的一个小规范但对协作效率的提升非常明显。2.4 MyBatis映射与动态SQL的实战玩法MyBatis的使用是整个后端的核心我重点说两个点。第一Mapper接口和XML文件的对应关系。我在resources目录下建了一个和DAO包路径一致的mapper目录里面放XML文件。MyBatis扫描的时候会按照XML里的namespace找到对应的Mapper接口接口方法名和XML里的id一一对应。XML写SQL有个好处是复杂SQL不用拼Java字符串改SQL不用重新编译有些环境支持热部署。但要注意一个强迫症级的问题工程打包时resources下的XML文件路径必须和Java接口的包名保持一致否则启动就报Invalid bound statement。第二动态SQL是这个系统查询功能的关键。比如垃圾查询支持按垃圾名称模糊搜索、按分类类型筛选。这两个条件可能同时用也可能只用其中一个还可能都不用查全部。传统的做法是Java里拼SQL各种if判断再加字符串拼接极易出错。MyBatis的写法就优雅很多select idselectList resultTypecom.city.garbage.entity.Garbage select * from garbage where if testname ! null and name ! and name like concat(%, #{name}, %) /if if testtype ! null and type ! and type #{type} /if /where /select标签会自动处理“如果前面没有条件就把第一个and去掉”的逻辑。加一个条件就加一个 清晰直接。新手看这个写法一开始觉得别扭用熟了之后你会觉得离不开它。分页我用了PageHelper插件这是MyBatis生态里最常用的分页插件。用法是在查询前调用PageHelper.startPage(pageNum, pageSize)紧接着的第一次查询会自动拼接limit语句。这一步操作极其简单但需要注意PageHelper只对紧随其后的第一条SQL生效。你如果在中间插了别的数据库操作分页就错乱了。还一个坑是PageHelper对嵌套查询或多表关联的count语句偶尔会生成错误遇到这种情况把分页查询拆开先用查询条件count一下再limit取数据就行。3. 前端Vue Element UI实现要点3.1 项目创建和目录组织前端我用Vue 2 Element UI的方案。我知道Vue 3已经出很久了但Element UI的生态、教程数量、坑的解决方案目前对新手最友好而且这套系统的前端代码量不大用Vue 2和Vue 3的差别主要体现在的API风格上业务逻辑是一样的。你能跑通Vue 2项目具备的是组件化开发和前后端交互的通用能力迁移到Vue 3的成本很低。创建项目我用的是Vue CLIvue create garbage-frontend然后进入项目目录安装依赖npm install element-ui axios vue-router3注意vue-router要装3.x版本因为Vue 2不兼容vue-router 4。项目目录结构我分成了几块src ├── api # 每个模块的接口请求封装 ├── assets # 静态资源 ├── components # 公共组件 ├── router # 路由配置 ├── views # 页面组件 ├── App.vue # 根组件 └── main.js # 入口文件views下面再按用户端和管理端分子目录。这个结构的好处是找页面代码特别快。用户说“垃圾查询页面报错了”你直接进views/user/search.vue不用翻半天。3.2 页面与路由实现用户端我设计了5个页面首页展示公告和分类指引、垃圾分类查询页、预约回收页、积分商城页、个人中心页。管理端我设计了6个页面登录后的工作台、垃圾信息管理、回收订单处理、积分管理、公告管理、留言管理。路由的配置比较简单用到的基本是普通路由和嵌套路由const routes [ { path: /, component: Home }, { path: /search, component: Search }, { path: /recycle, component: Recycle, meta: { requiresAuth: true } }, // ... ]这里一个关键点是路由守卫。meta.requiresAuth标记需要登录才能访问的页面。在router.beforeEach里检查如果要访问的页面需要登录先判断本地有没有token登录成功我除了存Session还会在localStorage存一个token字符串方便前端判断登录态没有就跳转到登录页。这个逻辑很简单但你整个系统的权限控制就靠它撑起来。3.3 axios二次封装与前端联调前端请求后端接口我用axios但绝不是直接每个页面里调用axios.get那样代码会非常散。我做了二次封装统一配置baseURL、超时时间和请求拦截器。核心代码思路// api/request.js import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: /api, // 通过代理转发解决跨域 timeout: 10000 }) // 请求拦截器每次请求带上token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[token] token } return config }) // 响应拦截器统一处理错误码 service.interceptors.response.use( response { const res response.data if (res.code 401) { // 未登录跳转登录页 router.push(/login) } return res }, error { Message.error(网络请求失败) return Promise.reject(error) } )这里最关键的配置是baseURL设为/api然后利用开发服务器的代理把请求转发到后端的8080端口。在vue.config.js里配置module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }这个代理的作用是浏览器请求前端的8081端口的/api/logindevServer把它转发到后端的8080端口的/login。因为浏览器看到的请求是同源的就不会触发跨域限制。这个方案比后端配CORS省心得多尤其对Session方案来说几乎是最优解。3.4 Element UI表单处理和列表渲染Element UI的表格和表单组件是管理后台开发的主力。垃圾信息管理页就是典型代表一个搜索区名称输入框、类型下拉框、搜索按钮、一个表格区展示垃圾数据操作列含编辑和删除按钮、一个弹窗新增和编辑共用。这里有两个小坑值得说一下。第一个是表单校验Element UI的rules校验规则注意触发方式用blur输入框失焦时校验而不是change每次输入都校验不然每敲一个字母都会弹一次错误提示。第二个是编辑回显编辑时el-dialog里绑定的form对象要用深拷贝比如Object.assign({}, row)不然弹窗里的修改会直接改掉表格那行数据用户取消弹窗时发现表格数据已经变了。4. 常见问题与排查技巧实录4.1 数据库连接报错排查这套系统跑起来最常见的坑第一个就是数据库连接。我用的是MySQL 8.x版本驱动依赖需要用com.mysql.cj.jdbc.DriverURL里必须带时区参数jdbc:mysql://localhost:3306/garbage?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf-8少一个serverTimezone启动就会报The server time zone value的错不关SSL会有个大警告characterEncoding不写utf-8查询中文就是乱码。这三个是一套的建议直接背下来。建库的时候也要注意我用的是utf8mb4字符集比utf8多支持了emoji和一些生僻字。如果建库时选错了字符集后面表里存中文就会出现??这样的问号。改起来很麻烦不如一开始就选对。4.2 MyBatis映射踩坑记录这里我把调试中遇到的高频异常列成一张速查表方便你以后报错时快速定位。异常现象根本原因解决方案Invalid bound statement (not found)XML文件路径和Mapper接口包名不对应检查resources目录下的XML文件路径是否和DAO接口包名完全一致Mapped Statements collection does not contain valuenamespace写错或者id写错对照Mapper接口全限定名和namespace逐一核对TooManyResultsException查询出一个列表但接口方法返回类型写成实体类把返回类型改成List实体类查询结果全为null数据库列名是下划线风格实体类属性是驼峰风格没有开启驼峰映射application.yml加配置mybatis.configuration.map-underscore-to-camel-case: true插入时主键没有自动回填数据库自增主键但XML里没有配置useGeneratedKeysinsert时加useGeneratedKeystrue keyPropertyid尤其最后那个驼峰映射问题最容易踩。数据库字段习惯用create_timeJava属性习惯用createTime如果不开启map-underscore-to-camel-caseMyBatis默认是匹配不上的结果就是查出来全是null。这个配置加在application.yml的mybatis节点下就行。4.3 前后端联调时跨域和路径问题前端如果出现“Request failed with status code 404”先别急着怀疑后端接口没写对检查三件事第一请求路径是不是通过代理正确转发到了后端第二后端controller里的RequestMappg路径是不是和前端请求路径一致第三是不是忘了给Controller加RequestBody来接收JSON参数。跨域问题如果出现“Access-Control-Allow-Origin”相关的报错先确认是否走了代理。如果你发现network里请求地址是http://localhost:8080/xxx而不是/api/xxx说明代理没生效baseURL配置有问题。如果确定走了代理还报跨域那是因为target地址写错了代理转发到了不存在的服务上。404还有一个容易被忽略的点SpringBoot的路径匹配规则。SpringBoot 2.6版本之后默认的PathPatternMatcher对两个连续斜杠的路径//user会直接拒掉我在这上面花过半天时间。如果前端请求路径拼接时不小心写了双斜杠就会出现莫名其妙的404。排查方式也很简单看浏览器network里的完整URL有//就说明是这里的问题。4.4 项目启动与打包部署的常见坑本地开发调试IDEA控制台报“Port 8080 was already in use”说明8080端口被别的程序占了。排查方法Mac/Linux用lsof -i :8080Windows用netstat -ano | findstr 8080。找到PID之后再确认是否可杀如果是一个你已经不用的Java进程直接kill掉就行。或者你嫌麻烦直接把后端的server.port改成一个不常用的端口比如8081前端代理的target也要对应改掉。打包部署的时候前端先npm run build生成dist目录然后把这个dist目录里的文件复制到SpringBoot项目的src/main/resources/static下重新打包后端。这样打出来的jar包自带页面文件别人访问你的服务直接就是完整系统。这个操作很实用很多毕设演示都这么干不用额外部署nginx。5. 项目的扩展方向与二次开发建议把这套系统跑通只是第一步真正让你和别人拉开差距的是你会不会在这个基础上做扩展。第一个值得加的功能是Redis缓存。垃圾分类查询是一个读多写少的接口非常适合缓存。在SpringBoot里集成Redis其实不复杂引入spring-boot-starter-data-redis依赖配置连接信息然后在你觉得性能需要优化的接口上先查缓存、再查数据库。Redis的引入还会引出一个关键问题缓存和数据库的一致性怎么保证。你在简历或者论文里写“引入了Redis缓存机制”面试官大概率会追问这个点自己去搜一下“缓存穿透、缓存击穿、缓存雪崩”这三个词了解清楚再写上去。第二个可以扩展的方向是图片上传。现在的垃圾信息表已经有图片字段但管理端是直接填URL。你可以做一个本地上传接口把图片保存到服务器某个目录返回访问路径前端用Element UI的Upload组件对接。这样管理端维护垃圾信息的时候可以直接上传图片系统显得更完整。第三个方向是把垃圾分类做成“拍照识别文字检索”双通道。当然拍照识别需要接入图像识别接口或者自己训练模型这个对毕设来说工作量大。但你可以做一个简化版前端放一个图片上传控件用户传一张垃圾照片后端调用某个图像识别API返回一个候选分类结果用户自己确认。这样做出来的系统既有亮点又不会太难实现。第四个方向是移动端适配。现在这套系统的页面是按桌面浏览器设计的手机上看会有点挤。你可以利用Vue的响应式特性或者干脆用uni-app重写一个移动端版本后台接口完全复用只换前端壳。很多学校对毕设的要求是“系统移动端”这个扩展方向就直接命中了。写在最后的体会我整理这套项目最大的感受是一个能跑通、前后端闭环的系统远比一堆零散的知识点有价值。你在教程里学的SpringBoot怎么启动、Vue的生命周期、MyBatis的动态SQL单独学都懂但把它们组合成一个系统的时候才会发现原来还有跨域、Session、路径匹配、打包路径这堆破事儿等着你。这不是坏事——你每踩一个坑对Web开发的理解就深一层。如果你把这套系统的每一个模块都自己动手敲一遍遇到报错自己排查跑通之后自己总结我敢说你比那些只看视频不敲代码的人强出好几个身位。后面你想往深了走无论是改造成微服务架构、周末接个外包、还是拿去面试谈项目经验这套项目都能当你的起点。
返回列表