资讯动态

SpringBoot+Vue洗衣店订单管理系统开发详解

发布时间:2026/10/3 7:45:30 来源:尧图企业网站定制
1. 从洗衣店日常运营说起这个订单系统解决的是什么问题做毕设选这个题目之前我其实先想明白了一个问题一个洗衣店订单管理系统跟一个简单的登记表Excel到底区别在哪如果只是记流水账那真没必要做一整套系统。我去学校旁边的一家洗衣店蹲了半天和老板聊了聊发现真实场景远不是“记个名字、记个电话”那么简单。洗衣店的日常业务绕不开这些环节顾客送衣进来店员要登记衣物类型外套、衬衫、羽绒服、窗帘还是毛毯、登记洗涤要求干洗、水洗、熨烫、去渍、记录污渍情况、约定取衣时间然后衣服进入洗涤流程。洗涤完成后要通知顾客来取取衣时要核对手牌或者订单号同时计算费用。这还没完——大多数洗衣店还有会员制度顾客充了值就要记余额有些顾客一次送五六件衣服每一件洗涤价格不一样费用要分件算。到了月底老板还得知道这个月收入多少、哪个品类洗得多、哪个会员消费高。这一套东西如果用纸笔或者Excel管理最直接的问题是订单多了以后查找困难容易漏件、错件顾客来取衣服时报个名字要翻半天账单。而且洗衣店通常不只一个店员谁收的单、谁洗的、谁检查的每一步都要有人认账。这正是订单管理系统的核心价值把“送洗—洗涤—取衣—结算”整条链路数字化让每一件衣服的状态从进门到出门全程可追踪。所以这个系统做下来功能模块就很清晰了订单模块核心、会员模块、衣物管理、结算收费、统计报表。这不是为了堆功能才设计的而是洗衣店真实运转所必需的业务闭环。拿着这个逻辑去开题、去答辩老师问“你为什么要做这个系统”的时候你就能答得特别实——因为每一步都对应现实问题没有冗余。顺便说一句作为Java Web方向的毕设这个题目的好处在于技术栈足够主流SpringBoot Vue业务逻辑不复杂到失控但又涵盖了完整的前后端交互、数据库设计、接口文档编写正好适合用来检验整个学习阶段的成果。2. SpringBoot Vue选型的逻辑为什么这套组合是毕设项目的稳妥答案技术选型这块说实话我有过纠结。刚开始我甚至考虑过用纯JSP Servlet做因为课程里学的是那个也考虑过用一些更偏简单的框架。但最后定下来SpringBoot Vue理由很实在。2.1 后端选SpringBoot把配置的复杂度吃掉纯粹用Servlet JSP写这个项目不是不行但你会发现大量时间花在写web.xml、配过滤器、处理请求转发这些琐碎事情上真正写业务逻辑的时间反而没多少。SpringBoot的核心价值在于“约定优于配置”它通过自动装配把Spring生态里那些繁琐的Bean配置大部分接管掉了。你用IDEA新建一个Spring Initializr项目勾选Web、MyBatis、MySQL这些依赖生成出来的项目骨架就是能直接跑的剩下的精力可以全部放在写业务上。理解Spring Boot的自动装配原理对于答辩来说也是个加分项。简单说SpringBoot的启动类上有SpringBootApplication它组合了EnableAutoConfiguration框架会扫描META-INF/spring.factories里的配置类按条件注解ConditionalOnClass、ConditionalOnMissingBean决定哪些Bean要自动装载。你只需要关心自己写的业务Bean。就洗衣店这个项目来说大部分情况下我们只会用到Spring MVC层、事务管理、数据源配置这些基础能力SpringBoot默认配置已经完全够用。版本选择上是个很现实的坑——SpringBoot 3.x已经出了但是如果你的教程、你参考的项目代码都是基于2.x的贸然上3.x会遇到很多兼容问题最典型的就是javax包名变成了jakarta网上大量老代码直接报错。我做这个项目用的是SpringBoot 2.7.x稳定而且资料多踩坑成本低。这个细节后面部署章节我会单独提。2.2 前端选Vue组件化和路由管理让页面开发真正跑起来Vue这两年几乎成了Java Web毕设前端的事实标准主要原因不是它多炫酷而是它足够务实。你不需要会复杂的状态管理、不需要写TypeScript只要会基本的JavaScript HTML CSS很快就能上手。洗衣店这个项目前端页面大概需要这么几个登录页、订单管理页、会员管理页、衣物分类管理页、统计报表页。这些页面看起来不多但里面都有公共的部分——表格、弹窗、表单、状态标签。Vue的组件化正好解决这个问题把表格封装成一个组件把弹窗表单封装成一个组件不同页面引用同一套组件只传不同的数据。这就是组件复用的价值也是为什么Vue写这类后台管理系统特别顺手。Vue的核心机制包括响应式数据绑定、指令v-model、v-for、v-if、组件通信props和emit、路由管理Vue Router、状态管理Vuex或Pinia。在实际项目中路由这关是逃不掉的——后台系统的侧边栏菜单本质上就是一组路由的映射。你需要理解路由怎么嵌套、怎么配置子路由、怎么用路由守卫做登录校验。2.3 前后端分离还是合在一起这是每个毕设人都会遇到的问题SpringBoot做后端接口Vue做前端页面两者之间通过HTTP请求JSON格式通信这就是前后端分离。它的好处是职责清晰前端开发可以不用关心后端实现坏处是部署时要起两个服务稍微麻烦一点。对于毕设来说我建议一个灵活的做法开发时前后端分离跑SpringBoot占8080端口Vue的开发服务器占5173Vite默认或8081然后通过配置代理转发解决跨域演示和部署时把Vue打包后的静态资源放到SpringBoot的static目录下这样整个项目就是一个SpringBoot服务别人拿过去直接启动就能看到完整页面。这个合并的方案外面也叫“将Vue打包放进SpringBoot中”后面我会专门讲操作步骤。提示如果你在简历上写“前后端分离项目”面试官问你部署架构你就可以把上面这套开发和部署分离的思路讲出来比单纯说“就是分开的”要显得想得更深。3. 数据库设计SQL脚本里藏着最容易被答辩老师追问的三个细节这个项目的SQL脚本我前后重构了大概三版。第一版很天真就建了四五张表订单就一张主表。结果写着写着发现状态一多字段就乱了费用计算又开始糊最后回到数据库层面来改。数据库设计基本决定了这个项目能写到什么程度这块多花时间绝对是值得的。3.1 表结构怎么拆核心是订单主表和订单明细表洗衣店业务中一个重要场景是“顾客一次送洗多件衣物每件可能要求不同”。如果只在订单表里放一个“衣物名称”字段一个订单只能记一件衣服根本无法支撑这个高频场景。正确的做法是拆成订单主表orders和订单明细表order_items形成一对多关系。完整表清单大致是这样的表名用途关键字段sys_user系统用户管理员/店员username, password(md5加盐), real_name, rolemember会员信息name, phone, balance, level, create_timeclothing_category衣物分类及价格name, price, unit, remarkorders订单主表order_no, member_id, status, total_amount, deposit, created_by, remarkorder_item衣物明细一个订单多件衣物order_id, category_id, name, quantity, price, stain_desc, wash_type, statuswash_process洗涤流程记录order_item_id, process_node, operator, timepickup_log取衣记录order_id, pickup_code, take_user, timeorders和order_item是核心其他表围绕这两张表关联。这里多说一句为什么还要单独建一张wash_process表因为“衣服处于哪个环节”是需要被记录的——收衣、洗衣房、洗涤中、洗涤完成、质检、待取衣每一个节点谁操作的什么时间这是业务上很合理的需求也方便你做出一个时间线式的查看功能。哪怕只是简单记录数据模型上也站得住脚。3.2 订单状态用数字还是用字符状态码要设计成可扩展的订单状态这块我一开始用的是字符串比如PROCESSING、FINISHED后来改成了数字状态码。原因只有一个字符型状态在Java的枚举映射和前端显示时容易拼写不一致出bug而且数字状态码可以直接存tinyint查询效率更高、存储空间更小。我的状态设计如下状态码含义说明0待收衣已下单店员还没把衣物收进洗衣房1洗涤中衣物已进入洗涤流程2已完成洗涤完毕等待顾客取衣3已取衣顾客已完成取衣订单关闭-1已取消订单作废这里有个容易被毕设老师抓着问的问题“已完成”和“已取衣”为什么不合并成一个状态理由是门店的业务诉求——衣服洗完了放前台但顾客还没来拿这中间可能隔着好几天甚至存在超期保管的情况。如果不区分这两个状态报表统计“有多少衣服洗好还没被取走”这个维度就没了。这种细节恰恰能体现做系统的人对业务的理解。代码里用常量类或者枚举类去定义这些状态不要直接写在业务代码里。建议用枚举OrderStatusEnum包含code、desc两个字段写一个静态方法getByCode来根据数字获取状态描述这样前端下拉选项、后端判断逻辑都复用同一套定义。3.3 金额计算一分钱都不能差用Decimal不用Double洗衣店场景里有折扣、会员充值赠送、押金、多件衣服按件计价这些情况金额计算很容易出边界问题。double和float在Java里做货币运算会有精度损失比如0.10.2的结果不是0.3而是0.30000000000000004。做真实项目时这是不可接受的。解决办法是数据库金额字段用DECIMAL(10,2)Java实体类用BigDecimal运算时用BigDecimal的方法add、subtract、multiply、divide除法的时候指定保留两位小数和舍入模式。BigDecimal price new BigDecimal(35.00); BigDecimal quantity new BigDecimal(3); BigDecimal total price.multiply(quantity).setScale(2, RoundingMode.HALF_UP);还有初始化SQL里的测试数据金额也全部用带两位小数的字面量插入避免出现300这种没有小数点的数据后面做统计时还得转格式。这个细节看起来很小但答辩老师如果现场用SQL查一下订单金额总和发现因为类型问题小数点错位印象分直接就下来了。另外说一个经验订单号建议用时间戳加随机数的方案不要用数据库自增ID直接展示给顾客。因为一旦订单量大了别人可以通过订单号数字推测出你的日订单量这是信息泄漏。我用的方案是yyyyMMddHHmmss 4位随机数例如202506201530451237。这个逻辑放在后端生成不放在数据库里。4. 后端SpringBoot核心实现接口设计、状态流转与权限控制后端代码的结构组织我同样重构过。刚开始所有逻辑都写在Controller里结果一个Controller几百行改一个状态要翻半天。后来老老实实按Controller — Service — MapperDao三层分包每个层的职责搞清楚写起来一下就顺了。4.1 后端项目结构和统一返回体项目结构大致如下com.laundry.system ├── controller // 接口层只做参数接收和结果返回 │ ├── OrderController.java │ ├── MemberController.java │ └── CategoryController.java ├── service // 业务逻辑层编写核心业务 │ ├── OrderService.java │ └── impl/OrderServiceImpl.java ├── mapper // MyBatis的Mapper接口 │ ├── OrderMapper.java │ └── xml/OrderMapper.xml ├── entity // 数据库实体类 ├── common // 统一返回体、异常处理、工具类 │ ├── Result.java │ ├── GlobalExceptionHandler.java │ └── OrderStatusEnum.java └── config // 配置类CORS、拦截器、WebMvc配置统一返回体Result是所有接口的规范格式就是{ code, message, data }。这里说的统一不只是格式统一还有状态码统一。我定的规则如下codemessage含义200操作成功400参数错误401未登录或登录过期403权限不足500服务器内部错误对应在Result类里我写了一个静态的成功方法Result.success(data)和失败方法Result.error(code, message)Controller里所有接口都走这两个方法返回不会再出现有时候返回Map、有时候返回String这种混乱发挥。public class ResultT { private Integer code; private String message; private T data; // 省略构造函数 public static T ResultT success(T data) { return new Result(200, 操作成功, data); } public static T ResultT error(Integer code, String message) { return new Result(code, message, null); } }4.2 订单状态流转核心业务逻辑怎么写得清楚订单状态流转是整个项目的核心业务也是答辩时演示的重头戏。你需要保证状态的每一步推进是合法且有顺序的而不是前台随手一改就跳到任意状态。我的做法是在Service层写一个updateOrderStatus方法核心逻辑是查询订单当前状态判断当前状态是否允许变更到目标状态白名单判断校验通过则更新状态码并记录操作日志用Transactional保证状态变更与日志记录在同一事务里万一中途异常全部回滚。Transactional public void updateOrderStatus(Long orderId, Integer targetStatus, Long operatorId) { // 1.校验订单存在 Orders order orderMapper.selectById(orderId); if (order null) { throw new ServiceException(订单不存在); } // 2.校验状态流转合法性 this.validTransition(order.getStatus(), targetStatus); // 3.更新状态 order.setStatus(targetStatus); orderMapper.updateById(order); // 4.记录日志 this.recordOrderLog(orderId, order.getStatus(), targetStatus, operatorId); } private void validTransition(Integer current, Integer target) { SetInteger allowed TRANSITION_MAP.get(current); // 状态机映射 if (allowed null || !allowed.contains(target)) { throw new ServiceException(非法的状态流转); } }TRANSITION_MAP就是那张状态流转表例如0可以变到1、-11可以变到22可以变到3。把这种规则集中定义比在业务代码里写一串if (status 0 next 1)要清晰得多。4.3 登录鉴权记住Session还是用Token学生项目中最稳妥的登录方案是采用基于JWT的Token鉴权。项目本身的Spring Security配置过于复杂但完全不用的话接口就是裸奔状态。我的处理方式是登录成功后生成一个JWT Token把用户ID和角色信息放进去前端每次请求在Authorization请求头里带上Token后端写一个拦截器HandlerInterceptor解析Token并把用户ID存入ThreadLocal路由前缀为/api/admin/**的接口都走这个拦截器放行登录接口和静态资源。JWT技术栈本身比较成熟在Java生态中可以使用jjwt库具体步骤是dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency// 生成Token String token Jwts.builder() .setSubject(userId.toString()) .claim(username, username) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 7L * 24 * 3600 * 1000)) .signWith(secretKey, SignatureAlgorithm.HS256) .compact();这里提醒一下密钥不要写在代码里放到application.yml里通过ConfigurationProperties读取。虽然这只是个毕设项目但养成这个习惯能让你的代码专业度上一个台阶。4.4 Controller层的接口设计URL和参数要前后台一条心比如订单模块的接口如下方法路径功能GET/api/order/page分页查询订单列表GET/api/order/detail/{orderId}查询订单详情含明细POST/api/order/create创建订单含明细用JSON传参PUT/api/order/status/{orderId}修改订单状态DELETE/api/order/{orderId}逻辑删除订单分页这块我用的MyBatis-Plus的分页插件前端传current和size两个参数后端返回{ total, records }。如果自己写SQL分页注意用LIMIT #{offset}, #{size}。前端表格正好对应这套分页参数第几页、每页几条、总条数对接起来很直观。5. 前端Vue的实现从页面骨架到状态展示再到部署整合Vue部分我用的还是Vue2 Element UI这套组合因为资料最多、坑最少。Vue3 Element Plus当然也可以但对于毕设这种体量的项目选熟悉的技术更稳妥。以下把我在前端踩过的一些环节和关键实现点梳理出来照着做能少走弯路。5.1 用Vite还是vue-cli创建项目Vue2通常搭配vue-cliVue3搭配Vite两者都能跑。我的建议是如果你用Vue2直接用vue-cli创建项目如果用Vue3用Vite。原因很简单Vite对Vue3的支持更好启动速度快而Vue2的老项目用Vite可能需要额外的插件适配教程少、坑多。我实际用的是Vite Vue3。创建项目只需一条命令npm create vitelatest laundry-front -- --template vue我专门提这一点是因为很多教程还在让人vue create反应跟不上。5.2 路由配置和登录守卫每个页面的“门禁系统”Vue Router在后台管理系统里承担两个职能一是页面跳转二是路由级别的权限控制。这个项目里我用了“静态路由路由守卫”的方案登录需要的页面在一开始就配好未登录不允许访问。路由配置是这样的const routes [ { path: /login, component: Login, meta: { title: 登录 } }, { path: /, component: Layout, redirect: /dashboard, children: [ { path: dashboard, component: Dashboard, meta: { title: 首页 } }, { path: order, component: OrderList, meta: { title: 订单管理, requireAuth: true } }, { path: member, component: MemberList, meta: { title: 会员管理, requireAuth: true } }, { path: category, component: CategoryList, meta: { title: 衣物分类, requireAuth: true } } ] } ]登录守卫的实现关键就是router.beforeEachrouter.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requireAuth !token) { next(/login) } else { next() } })这里有个小模块功能把路由中的菜单和权限通过筛选接口读取并添加这就是“动态路由”的概念。如果你的毕设老师看重这块可以往这个方向实现用户登录后后端返回他能访问的路由列表前端用router.addRoute()动态添加。这个机制不复杂但对加分很有用。5.3 封装axios请求避免每个页面都重复写逻辑如果每个页面的请求都自己写一堆axios.get(...).then(...)代码会非常冗余。我把请求统一封装到了一个utils/request.js里面做三件事设置baseURL开发环境指向代理地址请求拦截器里自动添加Token到请求头响应拦截器里统一处理状态码如果是401就跳转登录如果是业务错误就弹Message提示。service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )封装之后页面里取数据就变成两行const res await getOrderList(params) state.list res.data.records5.4 订单表格和弹窗表单Vue插槽的价值在这里体现订单管理页的核心是一张表格——按状态筛选、按订单号搜索、分页。表格列里有订单号、会员电话、衣物明细、金额、状态、创建时间、操作按钮。状态那一列我不想显示数字0、1、2而是显示“待收衣”“洗涤中”“已完成”“已取衣”这样的标签。在Element Plus里这个功能用el-table的slot来实现不止插槽本身而是说当组件提供的基础渲染方式不能满足你的要求时插槽让你有完全的控制权。el-table-column label状态 width120 template #default{ row } el-tag :typestatusTag(row.status){{ statusText(row.status) }}/el-tag /template /el-table-columnstatusText和statusTag两个方法就是从后端拿到的状态码到界面文案、颜色的映射。我建议把它们放在一个独立的utils/orderStatus.js文件里避免写进组件内部。弹窗表单也是用的v-model控制显示el-form做表单校验。这里有个经验编辑和新建共用一个弹窗组件通过传入的不同row数据判断是新增还是修改减少重复代码。表单提交成功后记得调用父组件列表的刷新方法不然数据不会变。5.5 跨域问题开发环境代理解决别用后端随手加“跨域注解”糊弄开发阶段页面在5173端口后端在8080端口直接axios.get(http://localhost:8080/api/order/page)会被浏览器的同源策略拦截。网上很多教程让你在后端加一个CrossOrigin注解但那是治标不治本——它能解决开发环境的问题但你把前端打包部署进SpringBoot后一般就同源了。更规范的做法是在Vue的vite.config.js里配置代理server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端代码里直接写/api/order/page开发服务器会把请求转发到后端的8080浏览器角度只看到同源请求没有跨域问题。然后axios的baseURL设为/api即可。6. 接口文档和SQL脚本这两份“软资料”是拉开答辩差距的关键很多学生做完代码就开始摆烂接口文档和SQL脚本随便糊弄一下。但实际上这两份东西是答辩时老师判断“这个学生是真的做了项目还是照着视频敲的代码”的重要依据。我自己就是因为把这两块做扎实了答辩时老师翻了下文档就直接说“这个项目完成度可以”。6.1 接口文档怎么写字段、示例、错误码一个都不能少接口文档核心不在于格式多漂亮而在于信息完整。一个合格的接口文档至少要有这么几块接口描述这个接口是干什么的一句话说清楚请求URL和方法例如POST /api/order/create请求参数表参数名、类型、是否必填、说明写清楚嵌套结构响应示例完整的JSON返回体让对接的人一眼看到数据结构错误码说明这个接口在什么情况下会返回400、401、500提示信息分别是什么。举个例子创建订单的文档我大概是这样写的接口名称创建订单 请求方式POST 请求URL/api/order/create 请求参数JSON { memberId: 1, // 会员ID可空为空则按散客处理 items: [ { clothingName: 羽绒服, categoryId: 3, // 衣物分类ID quantity: 1, washType: 干洗, // 水洗/干洗/熨烫 stainDesc: 袖口有油渍 } ], remark: 尽量周三前洗完, deposit: 20.00 // 押金元 } 响应示例 { code: 200, message: 操作成功, data: { orderId: 120, orderNo: 202506201530451237 } } 错误码说明 400必填参数缺失如items为空 500数据库写入失败另外建议配合方案用Swagger/knife4j自动生成接口文档配合手写Markdown一起用。Swagger的优点是接口更新时自动同步缺点是字段说明不够详细。我当时是两种都保留了Swagger看结构手写文档看说明答辩时给老师演示Swagger UI页面效果很好。6.2 SQL脚本的细节字符集、外键、初始化数据SQL脚本能不能让别人开箱即用是衡量“完整项目源码”含金量的重要标准。第一字符集。脚本开头必须指定CREATE DATABASE IF NOT EXISTS laundry_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE laundry_db;这里用utf8mb4而不是utf8是为了兼容用户输入的生僻字和各种特殊符号。如果你用MySQL 8.x字符集默认就是utf8mb4但5.7版本还是需要显式指定的。第二外键和索引。理论上表之间应该有外键约束但实际上很多项目为了插入效率不建外键只建普通索引。毕设这里我建议加上外键约束因为它代表你懂关系型数据库设计的完整性。如果怕麻烦至少在order_item的order_id字段上建立一个普通索引ALTER TABLE order_item ADD INDEX idx_order_id (order_id);第三初始化数据。SQL脚本不只是建表还要把管理员账号、默认衣物分类、测试会员、几条典型订单示例数据都写进去让项目一导入就有内容可看。如果导入后是空库还得自己注册账号、建分类才能体验完整流程体验感大打折扣。下面这段是初始化数据的一个示例主要看风格-- 系统管理员账号初始密码123456 INSERT INTO sys_user (username, password, real_name, role, status) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 系统管理员, ADMIN, 1); -- 默认衣物分类及洗护价格 INSERT INTO clothing_category (name, price, unit, remark) VALUES (衬衫, 15.00, 件, 常规水洗), (外套, 25.00, 件, 常规洗涤), (羽绒服, 45.00, 件, 建议干洗), (连衣裙, 30.00, 条, 轻柔洗涤), (窗帘, 80.00, 幅, 大件洗涤);注意密码字段不要用明文我用的是MD5加密后的值。现实中更安全的做法是加盐哈希但毕设项目用MD5也能解释过去只要你不拿去生产环境用。7. 部署运行与踩坑记录从IDEA启动配置到Vue打包整合进SpringBoot这块内容是网上教程最零碎的部分。很多教程要么只讲开发环境启动要么只讲部署很少有把整条链路串起来、并把坑讲清楚的。我把自己完整跑通和演示的过程记录一下照着这个流程走基本不会卡住。7.1 IDEA里怎么配置SpringBoot启动项打开SpringBoot的启动类LaundryApplication.java在IDEA里点击运行按钮旁边的下拉选择“Edit Configurations”弹出配置窗口里确认以下几点Main class选com.laundry.system.LaundryApplicationVM options这个一般不用改但如果你需要覆盖配置文件里的端口可以填-Dserver.port8081Environment variables主要用于设置数据库连接信息比如SPRING_DATASOURCE_PASSWORDxxx生产环境建议这么干毕设直接写配置文件也行Working directory一般保持默认的$MODULE_WORKING_DIR$即可。配好之后点击Debug控制台出现SpringBoot那个标志性的Banner图案用在线Banner生成器定制一个学院风格的启动Banner也是个有意思的小细节然后看到“Tomcat started on port(s): 8080”就是成功启动了。7.2 MySQL连接和时区最常见的启动失败原因项目启动时报数据库连接错误90%是这几个原因时区问题连接URL里要加serverTimezoneAsia/Shanghai否则报The server time zone value错误驱动版本不匹配MySQL 8需要com.mysql.cj.jdbc.Driver如果你的驱动包是旧版写com.mysql.jdbc.Driver也能跑但会有警告建议统一用新版密码不匹配配置文件里的 username/password 要和本地MySQL实例对齐这个虽然基础但真的很容易被忽略数据库不存在SQL脚本没有先导入或者库名写错了。application.yml里我使用的配置大致如下server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/laundry_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0逻辑删除字段的统一配置也是为了让SQL脚本里的deleted字段真正生效查询自动带上deleted0条件删除变更新。这是MyBatis-Plus很小但是很实用的一个特性属于做项目的人都知道的“默认操作”。7.3 Vite构建出来的前端资源如何放进SpringBoot开发完之后要把前端构建产物合并到后端。过程不复杂但有几个坑。首先在项目根目录下执行npm run build构建完成后生产目录Vite默认是dist下会出现index.html和assets文件夹。把这两个文件/文件夹复制到SpringBoot的src/main/resources/static/目录下。然后访问http://localhost:8080/SpringBoot会自动把static/index.html作为首页返回。这就是“Vue打包放进SpringBoot”的完整过程。但这里有几个注意点路由模式需要调整。Vue如果用createWebHistory去掉#号的那种URL模式在SpringBoot里刷新子页面会出现404因为SpringBoot没有对应的路由映射。解决方法是让Vue改为createWebHashHistoryURL变成/#/order这种带#的格式刷新就不会404。这是一个非常经典的坑如果你发现打好的包刷新404第一时间检查这。如果刷新了还是404可以考虑在SpringBoot里加一个简单的控制器把未知路径转发到index.html不过毕设项目用hash路由一般就够了。接口路径问题。打包后前后端同源前端代码里的/api/order/page会直接请求到SpringBoot的/api/order/page不需要再改任何配置这也是我们提前把代理配好的原因。7.4 那些年毕设人都会遇到的“版本太高”问题这里说一个容易被忽略但概率很大的坑SpringBoot版本过高。如果你创建项目时选了SpringBoot 3.3以上同时又参考了基于2.x的教程会出现一堆莫名其妙的编译错误。最典型的就是下面这行import javax.servlet.*; // 报错原因就是我前面提到的SpringBoot 3.x把Java EE的javax命名空间换成了jakarta。你需要在代码里改成import jakarta.servlet.*;这只是无数兼容性问题的一个缩影。如果你不想花大量时间踩这些坑最务实的方案就是锁定SpringBoot 2.7.x这虽然不是最新版本但是它安全、稳定、资料丰富、几乎所有第三方库都兼容。我甚至可以直白地说对毕设而言稳定跑通比用最新版本重要得多。7.5 启动和演示流程现场答辩时的操作顺序最后分享一个答辩演示的操作顺序是我当时反复演练后总结出来的启动MySQL服务导入SQL脚本确保数据库可用启动SpringBoot后端控制台无异常用浏览器访问项目地址先走一遍登录管理员创建一个新订单并录入明细展示页面交互和状态变化演示订单状态逐级推进待收衣→洗涤中→已完成→已取衣查询订单列表展示筛选和分页功能打开会员管理演示查询和余额显示最后展示一份接口文档说明接口设计。这个顺序是沿着业务链路来的老师跟着这个链路走能直观地看到你系统里每一个模块是怎么配合的。如果你还有时间可以准备几条异常路径的演示比如订单状态跳转不合法时报的错误提示、未登录直接访问页面被守卫拦下的效果这些非常加分因为它说明你想到了边界情况。做完这个项目我最大的体会是毕设项目的意义不在于功能有多复杂而在于有没有一条完整且自洽的链路。从数据库设计到后端接口从接口文档到前端页面从开发环境到部署环境每个环节都能讲清楚“为什么这样做”这比堆砌再多花哨功能都更有说服力。如果正在做这个题目的同学能沿着这条链路走一遍你会发现答辩时真正让你有底气的不是代码行数而是你对自己做出来的每一块内容都心里有数。

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

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

免费获取报价 →
↑