尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot+Vue洗衣店订单管理系统:毕设项目完整解析

SpringBoot+Vue洗衣店订单管理系统:毕设项目完整解析 去年帮一个学弟把关毕设他拿来的题目就是基于SpringBootVue的洗衣店订单管理系统。说实话我当时第一反应是这题目是不是有点太朴素了——洗衣店听起来比电商、秒杀系统低了好几个档次。可陪他完整走完从需求梳理、数据库设计、前后端开发到部署答辩的全过程后我反而觉得这个选题是真的很聪明。洗衣店订单管理系统在业务复杂度上刚刚好它既有订单、会员、商品洗衣项目、支付这些标准的Java Web核心要素又不会像电商平台那样牵扯库存、物流、营销一堆让人头大的东西。用SpringBoot做后端、Vue做前端再加一套完整的SQL脚本和接口文档就是一个结构清晰、能讲清楚、也能扛住答辩提问的毕设项目。这篇博文我就把这个项目的完整拆解思路、表结构设计、接口文档写法、前后端联调流程和答辩经验一次性讲透想拿它做毕设或者课程设计的同学可以直接照着搭。1. 为什么洗衣店订单管理系统适合做Java Web毕设——需求与定位先说个观点毕设选题的好坏不看题目听起来多高大上而看它能不能让你在有限的时间里把完整的技术栈走通并且论文有得写、答辩有得讲。洗衣店订单管理系统正好卡在这个平衡点上。1.1 选题逻辑功能覆盖面与开发成本的双赢一个合格的Java Web毕设至少要覆盖六个维度增删改查、复杂查询、权限区分、状态流转、报表统计、文件或图片处理。你看洗衣店业务增删改查洗衣项目、会员信息、订单记录全是标准的CRUD。复杂查询订单列表要支持按状态、按时间范围、按手机号搜索这是典型的多条件分页查询。权限区分顾客端和管理端天然就是两种角色正好用JWT做登录鉴权和路由守卫。状态流转一个订单从待取衣到洗涤中再到待取衣/待配送最后已完成每一步都要有记录。报表统计店家需要知道本月营业额、各洗衣项目占比这就用到聚合查询和ECharts图表。这些知识点覆盖下来毕业论文的需求分析-系统设计-功能实现-系统测试四件套就全齐了。相比商城系统动辄十几个表、购物车、库存扣减、支付回调环环相扣洗衣店系统的业务边界非常清楚开发周期压在两到三周完全可行而且每一块都能讲出点技术含量。1.2 用户角色与核心业务流转这个系统我建议按两个端来设计对应两套Vue页面顾客端微信扫码进入的H5风格页面浏览洗衣项目、在线下单、查看订单状态、在线支付可做模拟支付、会员充值。管理端后台管理系统订单管理接单、状态更新、删单、洗衣项目管理上架/下架/改价、会员管理、数据统计看板。核心业务流转是一条很清晰的主线顾客下单选择洗衣项目、填写取衣地址和联系电话→ 系统生成订单状态为待支付 → 支付成功后状态变待取衣 → 门店揽收后状态变洗涤中 → 洗涤完成状态变待取衣或待配送→ 顾客取衣后状态变已完成。这条流转线就是整个项目的脊梁骨。数据库表设计、后端Controller、前端页面状态展示全都要围绕这条线来展开。答辩的时候老师一问你订单有哪些状态、每个状态谁去触发变更你能把这条线画清楚项目就成功了大半。2. 技术选型SpringBootVue这套组合到底好在哪很多同学选技术栈是跟风别人用什么我也用什么。但你要是问一句为什么还真不一定答得上来。这里我把这套组合的关键理由讲透答辩被问为什么不用SSM、为什么不用JSP的时候你也能对答如流。2.1 后端SpringBoot配置简化与生态完备SpringBoot的本质就是约定大于配置。它帮你把Spring MVC、Tomcat、Jackson这些组件的配置都自动搞定你只需要在application.yml里写数据库连接、端口号这些变化的部分。洗衣店系统这种场景后端需要的东西SpringBoot几乎是开箱即用spring-boot-starter-web内置TomcatController直接写RESTful接口。spring-boot-starter-validation参数校验下单时校验手机号格式、洗衣项目ID是否存在。MyBatis Plus单表CRUD几乎不用写SQL复杂的多条件分页查询用LambdaQueryWrapper就能拼出来。JWT BCrypt登录认证和密码加密。我自己一直推荐MyBatis Plus而不是纯MyBatis原因很简单毕设项目的时间是有限的把半天时间花在写单表CRUD的XML上不值当。MP的BaseMapper接口直接给了你selectById、insert、updateById这些现成方法你真正要写的SQL就只剩多表关联和统计报表那几条。2.2 前端Vue组件化开发与生态工具链Vue这套技术栈毕设里最常用的组合是Vue 2 Element UI Axios Vue Router Vuex/Pinia。也有同学用Vue 3 Element Plus区别不大核心逻辑一致看你熟悉哪个。这里重点说下Vue的环境配置很多同学第一步就卡住。你需要装的是Node.js建议LTS版本然后通过npm安装Vue CLI如果走Vite就装create-vite。装完之后用npm run serve启动开发服务器默认跑在8080端口。这里有个非常关键的配置——开发环境下前端请求后端的代理在vue.config.js里这样写module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } }意思很直白前端页面里所有以/api开头的请求都转发到后端9090端口这样就绕开了跨域问题。很多同学在这里踩坑——不配代理浏览器控制台报CORS错误然后跑去后端加CrossOrigin虽然也能解决但对毕设来说用代理是更规范的做法。2.3 数据库与辅助工具MySQL、接口文档与SQL脚本数据库直接选MySQL 5.7或8.08.0记得注意驱动版本要用com.mysql.cj.jdbc.Driver。配套的SQL脚本是这个项目交付物的灵魂——里面不仅要有建表语句还必须包括初始化数据。接口文档我推荐用Apifox或者Swagger二选一。Apifox的好处是又能写文档又能调试前端联调的时候直接看文档就能知道传什么参数Swagger则是SpringBoot集成简单加个依赖写几个注解浏览器访问/swagger-ui.html就能看到在线文档。对毕设来说二者都可以我的建议是接口文档这件事一定要做这是答辩时能直接展示的成果。3. 数据库设计订单系统的表结构决定业务上限数据库设计这一步我建议直接决定后面代码的好写程度。洗衣店订单系统的表数量不用贪多六张核心表加两张辅助表就非常完整了。3.1 核心表结构与关键字段先给出一套我在这个项目里实际用过的核心表设计供参考表名用途关键字段user用户表顾客id, username, password, phone, balance(余额), create_timeadmin管理员表id, username, password, avatar, rolelaundry_item洗衣项目表id, name, price, unit(计费单位), icon, status(上架/下架)orders订单主表id, order_no, user_id, status, total_amount, pay_type, address, phone, remark, create_time, update_timeorder_detail订单明细表id, order_id, item_id, item_name(冗余), price(快照), quantitymember_card会员卡表id, user_id, card_no, balance, level, recharge_totalrecharge_record充值记录表id, user_id, amount, give_amount, create_timeorder_status_log订单状态变更日志表id, order_id, from_status, to_status, operator, create_time这里有几个设计细节是答辩加分点第一个细节订单明细表为什么要冗余item_name和price。洗衣项目表里的价格是会变的今天干洗一件大衣80明天可能调价到90。如果用订单明细去关联洗衣项目表读价格历史订单的金额就会跟着变这在账目上是绝对不能发生的。所以下单那一刻必须把项目名称和价格快照到明细表里。第二个细节订单状态为什么要单独一张日志表。这是一个很多同学忽略但老师很看重的点。订单表里只有一个当前状态但你没法回答这个订单什么时候从待取衣变成了洗涤中。加一张order_status_log表每次状态变更都插入一行记录后面做统计每个流程平均耗时就非常容易。第三个细节会员余额与充值分开记录。用户表里有个balance字段存当前余额recharge_record表记录每一笔充值。每次充值时更新余额并插入一条充值记录这样逻辑清晰对账也方便。3.2 SQL脚本里的初始化数据设计SQL脚本不是只把表建出来就完事了INSERT语句的初始化数据同样重要。启动项目要有默认管理员账号比如admin/admin123密码存BCrypt加密后的值、几个测试用户、十条左右的洗衣项目数据、几条不同状态的测试订单。这里踩过一个坑提醒大家管理员密码在SQL里直接写明文虽然方便导入但答辩时老师如果问密码安全怎么保证就很尴尬。正确做法是先用一个Java测试类或在线BCrypt工具把admin123加密后的哈希值算出来再写进SQL脚本。展示给老师看的时候可以说数据库里存储的是加密哈希登录时用BCryptPasswordEncoder校验这是基础安全意识的体现。洗衣项目数据建议这样设计让后续统计图表有东西可展示INSERT INTO laundry_item (id, name, price, unit, status) VALUES (1, 常规水洗, 15.00, 件, 1), (2, 标准干洗, 30.00, 件, 1), (3, 羽绒服清洗, 45.00, 件, 1), (4, 西装熨烫, 25.00, 件, 1), (5, 窗帘清洗, 60.00, 条, 1);3.3 订单状态流转的代码约束状态流转看似简单但要防止用户乱传状态。比如一个已完成的订单不能改成待支付。我的做法是在后端写一个状态机校验工具类public class OrderStatusMachine { // 定义合法的状态转换 private static final MapInteger, SetInteger TRANSITIONS new HashMap(); static { TRANSITIONS.put(0, new HashSet(Arrays.asList(1))); // 待支付 - 待取衣 TRANSITIONS.put(1, new HashSet(Arrays.asList(2, 5))); // 待取衣 - 洗涤中 / 已取消 TRANSITIONS.put(2, new HashSet(Arrays.asList(3))); // 洗涤中 - 待取衣(洗完) TRANSITIONS.put(3, new HashSet(Arrays.asList(4))); // 待取衣 - 已完成 } public static boolean canChange(int from, int to) { return TRANSITIONS.getOrDefault(from, Collections.emptySet()).contains(to); } }每次更新订单状态时都先走一遍canChange校验非法跳转直接抛业务异常。这个小工具类在答辩演示时是一个很好的亮点——为了确保订单状态流转的安全性我封装了状态机校验。4. 从下单到结算核心功能模块与接口设计技术选型和表结构定下来之后最有含金量的就是接口设计了。接口设计的好坏直接影响前端开发的效率和项目的可维护性。4.1 统一返回结果与接口文档规范前后端分离的项目接口必须先约定好返回格式不然后端返回一个页面的数据、前端却按对象去解析就会出各种莫名其妙的错。我的做法是写一个统一的Result类public class ResultT { private Integer code; // 200成功 400业务错误 401未登录 private String message; // 提示信息 private T data; // 数据 }所有接口都返回这个格式。前端Axios封装里统一处理codecode 200就正常返回数据否则弹出message。这套规范最大的好处是业务错误和信息提示在前后端之间传递有统一口径。接口文档我建议按模块组织形成一份完整的接口文档.md或Apifox在线文档核心接口包括模块接口路径方法说明登录注册/api/user/loginPOST用户登录返回JWT登录注册/api/admin/loginPOST管理员登录洗衣项目/api/item/listGET查询上架洗衣项目洗衣项目/api/item/pageGET管理端分页查询洗衣项目/api/item/savePOST新增/修改洗衣项目订单模块/api/order/addPOST顾客下单订单模块/api/order/pageGET分页查询订单列表订单模块/api/order/statusPUT更新订单状态订单模块/api/order/detailGET订单详情会员模块/api/user/rechargePOST会员充值统计模块/api/stats/overviewGET营业额与订单量统计统计模块/api/stats/categoryGET洗衣项目占比统计4.2 订单模块核心接口的后端实现逻辑下单接口是整个系统的核心它的逻辑是这样的前端把洗衣项目ID列表和数量传过来后端遍历项目ID从laundry_item表查出单价乘以数量累加得到total_amount同时生成一个订单号。订单号生成是个容易糊弄但要讲清楚的点。用UUID当订单号虽然能用但不专业——真实的订单号要有人眼可读的格式。我建议这样生成String orderNo YD new SimpleDateFormat(yyyyMMddHHmmss).format(new Date()) String.format(%03d, new Random().nextInt(1000));生成出来长这样YD20250617153026042含义是YD 年月日时分秒 三位随机数。虽然理论上极低概率会重复但严谨起见还是要加个唯一索引兜底。答辩时你可以说这个格式方便人工识别和按时间检索。下单这里还有一个细节——事务处理。生成订单头、批量插入订单明细、扣减会员余额如果用余额支付这三步必须在一个Transactional方法里完成。否则一旦插入明细失败订单头就变成了一条没有内容的脏数据。我在项目里给下单方法加了Transactional(rollbackFor Exception.class)并且明确告诉前端下单成功后返回orderId前端拿到后才跳转支付页。4.3 前端路由设计与页面交互前端部分Vue Router的路由设计要对应两种角色两套页面。我的建议是把路由分成三类const routes [ { path: /login, component: Login }, { path: /shop, component: ShopLayout, children: [ { path: items, component: ItemList }, // 顾客端洗衣项目 { path: orders, component: OrderList }, // 顾客端我的订单 { path: cart, component: Cart } // 顾客端下单确认 ]}, { path: /admin, component: AdminLayout, meta: { role: admin }, children: [ { path: dashboard, component: Dashboard }, // 管理端数据看板 { path: order-manage, component: OrderManage }, { path: item-manage, component: ItemManage }, { path: member-manage, component: MemberManage } ]} ]路由守卫是必须写的router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { next(); } else if (!token) { next(/login); } else { next(); } });这里有个小细节要提醒前端路由守卫只是体验优化真正的鉴权必须在后端做。后端写一个拦截器或Spring SecurityJWT解析失败就返回401。否则别人直接拿Postman调你的接口绕过前端照样能查到数据答辩时被问到怎么保证安全就露馅了。5. 从源码到运行环境搭建、SQL导入与联调全流程拿到一套完整的源码SQL脚本接口文档很多同学第一步就卡在怎么跑起来上。我按实际可操作的步骤把整个流程捋一遍照着做就行。5.1 环境清单与准备工作跑起这个项目需要的东西如下工具版本建议用途JDK1.8或11后端运行环境Maven3.6以上后端依赖管理和构建MySQL5.7或8.0数据存储Node.js14 LTS或16 LTS前端构建环境IDEA2020以上后端开发IDEVSCode最新版前端开发IDENavicat任意版本数据库可视化导入工具Apifox最新版接口调试与文档管理提前提一句JDK版本和后端pom.xml里的SpringBoot版本要匹配。SpringBoot 2.x系列一般配JDK 8或11你如果装了JDK 17最好换成SpringBoot 2.7或升级到SpringBoot 3.x。这个不匹配的问题是出镜率极高的第一坑。5.2 后端启动三步走第一步导入数据库。用Navicat新建一个laundry_db数据库字符集选utf8mb4然后右键运行SQL文件把laundry.sql脚本导入进去。导入后检查一下表是否齐全、初始化数据是否正常重点确认admin表里有一条账号。第二步修改配置文件。打开application.yml把数据库用户名密码改成你自己的server: port: 9090 spring: datasource: url: jdbc:mysql://localhost:3306/laundry_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456第三步启动SpringBoot。在IDEA里打开项目等Maven把依赖下载完运行主类LaundryApplication.java。看到Started LaundryApplication in x.xxx seconds日志说明启动成功。然后用Apifox调一下/api/item/list接口验证后端是否正常。这里要再叮嘱一次启动报错先看日志的最后三行大部分问题都是端口被占用、数据库连不上、依赖没下载全这三类。5.3 前端启动与联调验证前端部分相对更折腾因为涉及npm下载速度等问题。步骤是# 在vue目录下执行 npm install # 安装依赖如果慢就配置淘宝镜像 npm run serve # 启动开发服务器默认localhost:8080启动完成后浏览器访问http://localhost:8080先用初始化的测试账号登录。如果页面能正常打开、图片能加载、接口返回数据说明前后端联调已经通了。联调过程中最常遇到的几个症状和对应解法整理成表格放在这里症状原因解法控制台报CORS错误前端代理没配好检查vue.config.js里proxy配置请求404后端接口路径拼错对比接口文档里的路径和Controller的RequestMapping请求401Token过期或没传检查Axios拦截器是否加Authorization头数据能查到但页面空白JS渲染出错按F12看Console具体报错多半是字段名对不上状态改了页面不刷新没重新拉取列表状态变更后重新调分页接口5.4 我在实际跑这个项目时踩过的三个坑坑一日期格式化跨域。后端返回的createTime格式是2025-06-17T15:26:30.00000:00前端表格里显示出来一长串很难看。解决方式是全局配置Jackson格式。在配置类里加一个BeanBean public Jackson2ObjectMapperBuilderCustomizer customizer() { return builder - builder.simpleDateFormat(yyyy-MM-dd HH:mm:ss); }坑二金额精度。数据库金额字段必须用DECIMAL(10,2)Java实体类用BigDecimal不要把价格定义成double或float。用double计算洗衣费用出现0.1 0.2 0.30000000000000004这种结果在财务场景里是不能接受的。BigDecimal做加法时记得用add方法而不是。坑三懒加载序列化问题。如果订单表和用户表用了MyBatis Plus的关联查询在JSON序列化时可能报懒加载异常。这个项目里我建议直接不用对象关联而是在OrderService里用两次查询手动组装VO对象代码看着多几行但逻辑清晰且不报错。6. 答辩前的加分准备与项目打磨方向项目能跑通只是及格线真正拉分的是代码可读性、细节处理和数据表现力。这部分的投入性价比非常高。6.1 代码规范与接口文档的整理策略答辩时老师会随机打开你的项目看代码结构和注释。这里有几个快速提升观感的地方包结构清晰controller/service/mapper/entity/dto/config/common一眼看过去就是规整的分层架构。Controller里不写业务逻辑一个典型的错误是Controller里直接查库。正确做法是Controller只做参数接收和结果返回业务逻辑全部放在Service层。这个习惯同样能在答辩时以为了降低耦合我把业务逻辑抽到了Service层一句话讲出来。常量与枚举代替魔法值订单状态不要到处写裸的0、1、2定义一个OrderStatusEnum代码可读性提升一大截。注释写为什么而非是什么比如订单号码那段注释写生成规则前缀时间戳随机数保证可读性和并发下的基本唯一性而不是写生成订单号。接口文档建议做成一份单独的PDF或Markdown文件放进项目交付物里包括每个接口的请求地址、请求参数、返回示例和错误码说明。答辩时可以直接打开文档向老师展示整个项目的接口规范都在这里前端开发时是严格按照这份文档对接的有兴趣的同学也可以把它作为前后端协作的模板来用。6.2 数据可视化与性能优化的进阶方向如果时间充裕给项目增加以下三个亮点能力答辩时效果立竿见影亮点一管理端数据看板。用ECharts在管理端首页放两张图表近7天营业额折线图、洗衣项目销售量占比饼图。后端对应两个聚合查询SQLSELECT DATE(create_time) AS day, SUM(total_amount) AS amount FROM orders WHERE status ! 0 AND create_time DATE_SUB(CURDATE(), INTERVAL 6 DAY) GROUP BY DATE(create_time);SELECT i.name AS itemName, SUM(od.quantity) AS cnt FROM order_detail od LEFT JOIN laundry_item i ON od.item_id i.id GROUP BY od.item_id ORDER BY cnt DESC;这两条SQL很有讲头——前者是分组聚合时间函数后者是表关联聚合排序都是面试和答辩喜欢问的SQL基础写在项目里比写在简历上更可信。亮点二缓存层。洗衣项目列表是高频读取、低频修改的数据非常适合缓存。最简单的方式是引入Spring CacheCacheable(cacheNames itemList, key all) public ListLaundryItem getOnSaleItems() { return itemMapper.selectList(...); }在Service方法上加一个注解就完事但能讲出热点数据缓存降低数据库压力这个技术点。注意项目启动后要记得在EnableCaching开启缓存。亮点三JWT过期处理。前端Axios响应拦截器里如果返回401先尝试用refreshToken刷新Token刷新成功就重放请求失败再跳转登录页。这一步做完认证这一块的内容就非常完整了。6.3 关于答辩演示与话术的几条实在建议最后聊几句答辩现场。演示的时候不要一上来就打开登录页那样浪费时间。我的建议是提前准备一套带数据的演示账号账号里预置几个不同状态的订单比如一个待支付、一个洗涤中、一个已完成演示时先展示数据库表和初始化数据再进入系统按业务流程顺序一步一步演示用户下单→查看订单状态变化→管理员接单→统计看板出现数据增长。这样整个演示过程就是一个闭环老师看着也会比较舒服比零散地点击查看自然得多。另外有个实用小技巧在SQL脚本里故意保留几条测试订单的create_time分散在不同日期这样图表展示时曲线图会有起伏变化。如果所有订单都是同一天创建的折线图就只有孤零零一个点看上去就特别没说服力。我当时帮学弟准备演示数据特意用DATE_SUB插了一周内每天2-3条订单演示图表出来效果好了不止一个档次。
返回列表