news 2026/10/26 16:19:02

家政服务管理小程序毕业设计:微信小程序+SSM+Vue+MySQL联调全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
家政服务管理小程序毕业设计:微信小程序+SSM+Vue+MySQL联调全指南

简介:面向毕业设计与微信小程序开发学习者,这份家政服务管理小程序资源,基于微信小程序+SSM+Vue+MySql实现前后端分离,覆盖用户端家政预约与支付、阿姨端审核与评价查看、管理端家政公司/阿姨/服务项目/留言管理等多类核心业务,适合作为计算机专业毕业设计、课程设计或项目练手的完整参考。资源共1123个文件,压缩包约38.52MB,类型包括Java后台、SSM配置、Vue管理端页面、小程序wxml/wxss/js逻辑、数据库SQL脚本、毕业论文doc及视频演示mp4等,整体目录结构清晰,便于按模块查找和二次开发。已有228人学习下载。除可直接运行的工程源码外,还配套建表脚本、开发环境配置、前后端联调思路与答辩展示材料,能为从环境搭建到系统部署的整个过程提供一站式支撑,尤其适合缺少完整毕设工程与文档的开发者对照学习。

1. 家政服务管理小程序毕业设计:难点不在写代码,而在让五个进程各就各位

如果你正对着“家政服务管理小程序-毕业设计,基于微信小程序+SSM+Vue+MySql开发,前后端分离”这个标题找落地路径,先把风险值降下来:这套组合的难点从来不在“写代码”,而在让五个东西在同一台电脑上各就各位——微信开发者工具、后端Tomcat、MySQL实例、Vue后台管理端,以及它们之间的网络连接。标题里的技术栈是毕业设计最常见的配方:小程序给用户下单,Vue后台给管理员接单派单,SSM只提供JSON接口,MySQL负责收口状态。这就是典型的前后端分离项目实战,但比纯网页项目多了一道微信侧的联调门槛。本文面向两类人:一类是要在答辩前半个月把项目跑起来、把论文写出来的本科生;另一类是已经能写CRUD,但想搞明白这套演示闭环里哪些配置在拖后腿的开发初学者。

2. 架构先立住:微信小程序 + SSM + Vue 的职责边界,以及 MySQL 里那张订单表怎么建

2.1 前后端分离的真正边界:谁面向用户,谁面向管理员,谁只提供 JSON

很多第一次做前后端分离的人,会把“小程序里用 Vue 写页面”当成默认选项,这是标题最容易误导人的地方。实际这套组合里的 Vue 不是跑在小程序里的,而是用来做后台管理端的。小程序端负责用户能看到的那一面:浏览服务、下单、支付、查订单;Vue 管理端负责家政公司能看到的那一面:接单、派单、改状态。两者共用同一套 SSM 接口。微信小程序本身是原生框架,页面用 WXML + WXSS + JS 写,它和 Vue 的模板语法确实有点像,但运行环境完全不同,不要试图在小程序里 import Vue。Vue 后台是单独的 Web 工程,跑在浏览器里。

这一层边界想清楚,论文里的系统架构图也就顺了。三个端的关系可以概括成一张表,写论文或者做开题答辩时能直接讲明白:

端技术职责典型页面
用户端微信小程序原生浏览服务、下单、支付、评价首页服务列表、订单详情
管理端Vue + Element UI接单、派单、管理服务项目订单管理、服务分类管理
服务端SSM(Spring + SpringMVC + MyBatis)只返回 JSON,不渲染页面/api/order/create、/api/service/list

后端“只提供 JSON”这句话,是前后端分离的核心。 SSM 工程里不应该再有 JSP 或者 Thymeleaf 页面,Controller 方法统一加 @ResponseBody,返回结构是固定的 {code, message, data}。小程序端和 Vue 端拿到同样的结构,各自去渲染,这才是分离的意义。

2.2 为什么是 SSM 而不是 Spring Boot:论文好写、依赖直观、排查链路短

按当前的主流,新项目几乎都直接上 Spring Boot,但毕业设计题目列的是 SSM,这其实不是坏事。SSM 的配置是显式的:web.xml、spring-mvc.xml、mybatis-config.xml 各管一摊,论文里“系统实现”那一章可以拆得很细,每层一个 XML 文件讲一段,结构越清晰反而越好写。相比之下 Spring Boot 大量自动配置,跑起来快,但论文里能写的东西少,答辩被问“自动配置怎么生效的”时容易卡壳。

SSM 的代价是初始化链条长。从 Tomcat 启动到一次请求进来,经历 listener 加载 Spring 容器、DispatcherServlet 初始化、HandlerMapping 匹配、Controller 执行、Service 注入、Mapper 代理生成、连接池取连接。任何一环配错,现象都是 404 或者 500,而且报错日志往往指向 Tomcat 而不是你的代码,新手容易在这上面耗半天。我的建议是:如果题目允许换 Spring Boot,换掉能省一天时间;如果题目锁死 SSM,就用 Maven 的 war 包方式部署,不要用 IDE 内置 Tomcat 的“热部署”方式,那会掩盖很多配置问题。

2.3 家政业务的数据底座:六张核心表与订单状态机的状态点

家政服务管理小程序的数据模型比普通电商多一个维度:服务不是实物商品,它绑定了时间、地点和服务人员。所以订单表不能只存商品 ID 和数量,还要存预约时间、服务地址和状态流转。常规做法是拆成六张表:用户表 user、服务分类表 category、服务项目表 item、订单表 order_info、地址表 address、评价表 comment。其中 order_info 是核心,它决定了整个演示闭环讲不讲得通。

订单状态机建议只设五个状态点:0 待支付、1 待接单、2 服务中、3 待评价、4 已完成、5 已取消。状态流是 0→1→2→3→4,取消只能从 0 或 1 发生,2 之后不允许取消。这个限制要在 Service 层做校验,不能靠前端按钮隐藏来防。因为小程序端请求可以直接被构造,绕过了界面就是绕过校验。状态机设计好了,后续做后端接口、做演示预置数据都顺。

2.4 建库建表 SQL 和两条容易忽略的 MySQL 参数

数据库命名直接用 home_service_db,表名字段名统一小写加下划线,避免不同 OS 下大小写问题。下面是订单表和用户表的建表语句,字段数量按毕设最小可用集设计,不用过度设计:

CREATE DATABASE IF NOT EXISTS home_service_db DEFAULT CHARACTER SET utf8mb4; USE home_service_db; CREATE TABLE `user` ( `id` INT NOT NULL AUTO_INCREMENT, `openid` VARCHAR(64) NOT NULL COMMENT '微信openid,唯一', `nickname` VARCHAR(50) DEFAULT '', `phone` VARCHAR(20) DEFAULT '', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `order_info` ( `id` INT NOT NULL AUTO_INCREMENT, `order_no` VARCHAR(32) NOT NULL COMMENT '业务订单号', `user_id` INT NOT NULL, `item_id` INT NOT NULL, `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待支付 1待接单 2服务中 3待评价 4已完成 5已取消', `appoint_time` DATETIME DEFAULT NULL COMMENT '预约上门时间', `address` VARCHAR(255) DEFAULT NULL, `amount` DECIMAL(10,2) DEFAULT 0.00, `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_user_id` (`user_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

字段注释直接写进 DDL,后面写论文画表格时能少绕弯路。order_no 用业务唯一键而不是 id,是为了演示时给用户看“订单号”而不是自增主键,更贴近真实。status 用 TINYINT 存数字,不要存字符串,这样状态机比较大小、扩展状态点都方便。注意 openid 必须加唯一索引,一个微信号只能对应一个账号。

建完库之后有两处 MySQL 参数值得提前调整。第一是 sql_mode,MySQL 5.7 默认带 ONLY_FULL_GROUP_BY,执行 GROUP BY 查询时如果 select 的字段不在 group by 里就会报错,很多网上抄来的统计 SQL 直接跑不通,可以在 my.ini 里把 sql_mode 改掉,或者用 ANY_VALUE() 包一下字段。第二是 wait_timeout,默认 8 小时,Wi-Fi 环境下 MySQL 如果被系统休眠挂起,第一次请求会卡十几秒才重连。这也是后面联调阶段最常见的翻车现场之一,提前把连接池和 wait_timeout 的关系弄清楚,能省很多排查时间。

3. 微信小程序端:从 app.json 到可下单首页的最小闭环

3.1 项目骨架与底部 TabBar:先让页面能翻得过去

小程序端的第一步不是写页面,而是把 project.config.json 和 app.json 配明白。直接用微信开发者工具新建一个“小程序”项目,AppID 可以先用测试号,等你要真机预览时再换自己注册的 AppID。app.json 里 pages 数组的第一项就是启动页,这里容易踩坑:如果想默认进首页,就要把 index 放最前面,而不是按目录顺序排。

{ "pages": [ "pages/index/index", "pages/category/category", "pages/order/order", "pages/user/user" ], "window": { "navigationBarTitleText": "家政服务", "navigationBarBackgroundColor": "#1296db", "navigationBarTextStyle": "white" }, "tabBar": { "color": "#999999", "selectedColor": "#1296db", "list": [ { "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/category/category", "text": "分类" }, { "pagePath": "pages/order/order", "text": "订单" }, { "pagePath": "pages/user/user", "text": "我的" } ] } }

这段配置里两个要点。tabBar 的 pagePath 必须同时存在于 pages 数组里,否则开发者工具会直接报错。navigationBarTextStyle 只支持 black 和 white 两个值,写其他颜色会被忽略。如果你想让首页有搜索框、需要自定义顶部导航,就要在对应页面的 json 里单独配 "navigationStyle": "custom",这个我们放在 3.3 节讲。先按默认导航跑通骨架,是最稳妥的顺序。

3.2 登录获取手机号:getPhoneNumber 的 code 只能用一次

微信小程序登录获取手机号是整套演示里最容易紧张的一环,因为后端接口有没有写好你暂时还没法验证,而前端这里一旦写错,日志又不容易看明白。小程序登录涉及两个不同的 code:wx.login 返回的 code 用来换 openid,这是账号体系的基础;getPhoneNumber 按钮返回的 code 用来换手机号,它是一次性的,同一个 code 只能调一次后端接口,重复调用必然报错。

// pages/user/user.js Page({ onLoad() { this.login(); }, login() { wx.login({ success: (res) => { if (res.code) { wx.request({ url: 'http://127.0.0.1:8080/api/user/login', method: 'POST', data: { code: res.code }, success: (resp) => { if (resp.data.code === 200) { wx.setStorageSync('token', resp.data.data.token); } } }); } } }); }, onGetPhone(e) { // e.detail.code 只能用一次 wx.request({ url: 'http://127.0.0.1:8080/api/user/phone', method: 'POST', data: { code: e.detail.code }, success: (resp) => { if (resp.data.code === 200) { wx.setStorageSync('phone', resp.data.data.phone); } } }); } });

这里的顺序是:先用 wx.login 建立会话、拿 token,用户点“微信一键登录”按钮时才去拿手机号。不要把换手机号放在 onLoad 里自动执行,因为 getPhoneNumber 必须由用户点击按钮触发,否则 detail.code 拿不到。注意 request 的 url 里现在写的是 127.0.0.1,这只是让逻辑先跑通,后面真机预览时必须改成局域网 IP,否则手机会连不到你电脑上的 Tomcat,这一点第 5 章专门讲。

3.3 顶部导航栏高度与安全区:自定义导航的第一道坎

一旦页面要做顶部搜索框或者希望导航栏跟品牌色更融合,你就要把“微信小程序顶部导航栏高度”这件事算明白,否则做出来的页面在刘海屏上会直接撞进状态栏。默认导航栏是系统帮你算好的,自定义导航栏则要自己处理状态栏高度和胶囊按钮位置。胶囊按钮是右上角那个圆形胶囊,它和小程序页面内容之间的相对位置在不同手机上不一样,所以必须动态获取。

// utils/navbar.js function getNavbarHeight() { const menu = wx.getMenuButtonBoundingClientRect(); const sys = wx.getSystemInfoSync(); const statusBarHeight = sys.statusBarHeight || 44; // 胶囊按钮垂直居中,上下留白距离相等 const marginTop = menu.top - statusBarHeight; const navHeight = statusBarHeight + menu.height + marginTop * 2; return { statusBarHeight, navHeight, menu }; } module.exports = { getNavbarHeight };

计算逻辑的直观理解是:导航栏整体高度 = 状态栏高度 + 胶囊按钮上下留白 + 胶囊按钮高度。因为胶囊按钮在导航栏里是垂直居中的,所以它上方留白等于下方留白,于是 menu.top - statusBarHeight 就是上方留白,乘 2 把上下都算上。这套公式在 iPhone X 以后的全面屏和普通屏上都适用。如果页面是自定义导航,推荐在页面的 data 里初始化这套值,然后给导航 view 设置 padding-top: statusBarHeight 和 height: navHeight,再在页面 json 里确认开的是 "navigationStyle": "custom",顺序不能反,先配 json 再写代码。

3.4 首页服务列表与下单:把 wx.request 封装成全项目唯一入口

小程序端如果每个页面都直接写 wx.request,后面换 baseURL、加 token 就要全局搜着改。常见做法是在 utils/request.js 里封装一个 Promise 版的请求函数,所有页面都用它。封装之后,后端返回的 {code, message, data} 结构也统一在这里解开,页面代码只需要关心业务数据。

// utils/request.js const BASE_URL = 'http://127.0.0.1:8080/api'; function request(url, method, data) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json' }, success(res) { if (res.statusCode === 200 && res.data.code === 200) { resolve(res.data.data); } else { reject(res.data); } }, fail(err) { reject(err); } }); }); } module.exports = { request, BASE_URL };

首页拿到服务列表渲染,核心就是 onLoad 里调一次 GET 接口,然后在 WXML 里用 wx:for 渲染卡片。下单则是在用户点“立即预约”时,把 itemId、appointTime、address 三个字段 POST 给后端。这里有三件事必须做:appointTime 不要传字符串就完事,要统一格式为 "yyyy-MM-dd HH:mm:ss",否则后端解析 DateTime 会直接 400;address 要么让用户从地址表选、要么让用户手动填,不能在测试时写死,否则订单详情页演示出来很假;POST 的 header 里 Content-Type 必须是 application/json,因为第四章后端接口用 @RequestBody 接收,如果漏了会报“Required request body is missing”。首页能渲染出服务列表、点击能调到下单页,小程序端的“最小闭环”就算打通了。

4. 后端 SSM 接口工程:把家政订单做成能测试的 REST API

4.1 SSM 工程怎么搭:依赖、web.xml 与三层代码的顺序

后端工程建议用 Maven 打成 war 包,结构是标准的 Controller-Service-Mapper 三层。Controller 负责参数接收和结果包装,Service 负责业务规则和事务,Mapper 只跟数据库打交道。刚入门的人最容易把三者混在一起:Controller 里写业务判断、Mapper 里写复杂逻辑、Service 形同虚设,这样的代码跑起来确实能用,但论文里没法写成“三层架构”,答辩也会被问倒。

pom.xml 里核心依赖就五个:spring-webmvc、mybatis、mybatis-spring、mysql-connector-java、druid。jackson-databind 也要加进去,它是 SpringMVC 把 Controller 返回值序列化成 JSON 的关键,很多人漏了它导致接口返回的不是 JSON 而是字符串。数据库连接池用 Druid,配置里至少包含 initialSize、maxActive、minIdle 三个参数,测试时 minIdle 不要设 0,否则空闲久了 MySQL 断开后要重新建连接,就是之前提过的“第一请求卡十几秒”。

web.xml 里要注册两个核心组件:ContextLoaderListener 负责启动 Spring 容器,DispatcherServlet 负责拦截所有 /api 请求。spring-mvc.xml 里开<mvc:annotation-driven />和组件扫描,mybatis-config.xml 里配置驼峰映射:<setting name="mapUnderscoreToCamelCase" value="true"/>。这个 setting 很关键,它让数据库里 update_time 自动映射成 Java 属性的 updateTime,不然你得在每个 resultMap 里手写映射。三层结构里 Service 接口和实现类可以分开写也可以写成一个类,毕设建议分开,论文里能多画一张接口图。

4.2 统一返回 Result :让 Vue 和小程序端都不再各自解析

接口返回值一定要做统一包装,否则小程序端要根据每个接口的怪癖去处理数据。这个包装类所有 Controller 都用,所以它要写成泛型,并且提供静态工厂方法。约定 code 200 表示成功,400 表示参数错误,401 表示未登录,500 表示系统异常。实际业务异常用 code 400 + message 描述,不要把异常堆栈直接抛给前端。

public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> ok(T data) { Result<T> r = new Result<>(); r.code = 200; r.message = "success"; r.data = data; return r; } public static <T> Result<T> error(String message) { Result<T> r = new Result<>(); r.code = 400; r.message = message; return r; } // getter / setter 省略 }

这个问题有一个边界要注意:Result.ok 传入 null 时,JSON 序列化结果是"data": null还是直接不包含 data 字段,取决于 Jackson 的配置。小程序端的 request.js 里我们取了 res.data.data,如果 data 字段缺失会得到 undefined,页面容易报错。所以要么在 spring-mvc.xml 里配置 Jacksoninclude: ALWAYS,要么约定后端返回 null 也包装成data: null。我一般会直接配置 Jackson 的 NON_NULL 为默认不忽略,这样数据更可控。

4.3 订单状态机:Service 层加事务,Mapper 层只做数据搬运

订单创建接口是整套系统的核心演示点。Controller 层接收前端 POST 的 JSON 体,Service 层先校验参数,然后生成订单号、插入订单记录,最后返回订单号给前端。这里最容易犯的错是:Controller 里直接调用 mapper 插入数据。一旦后面要加“下单同时扣库存”“下单同时生成支付流水”这类多表操作,没有 Service 层兜底事务,就会出现一边成功一边失败的脏数据。

@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Transactional(rollbackFor = Exception.class) @Override public String createOrder(OrderCreateDTO dto) { // 1. 校验必填参数 if (dto.getItemId() == null || dto.getAppointTime() == null) { throw new BusinessException("服务项目和预约时间不能为空"); } // 2. 生成唯一订单号:时间戳 + 随机数 String orderNo = "H" + System.currentTimeMillis() + String.valueOf((int) (Math.random() * 900) + 100); // 3. 插入订单,状态默认为 0 待支付 OrderDO order = new OrderDO(); order.setOrderNo(orderNo); order.setItemId(dto.getItemId()); order.setAppointTime(dto.getAppointTime()); order.setAddress(dto.getAddress()); order.setStatus(0); orderMapper.insert(order); return orderNo; } }

@Transactional(rollbackFor = Exception.class) 必须写 rollbackFor。Spring 默认只在抛出 RuntimeException 时回滚事务,如果你业务里抛的是受检异常,比如 IOException,事务不会回滚,数据就悄悄写进去了。这是事务注解里最经典的坑。状态流转时也建议加一个“期望状态”条件,比如把订单从 0 改成 1,update 语句要带 AND status = 0,这样并发情况下两个请求同时操作同一单,只有一个能成功,相当于用行级锁做乐观控制。这里不用上升到复杂分布式理论,但能让答辩老师看到你有并发意识。

4.4 MyBatis 动态 SQL:排序字段与更常见的边界

MyBatis 的 XML 是这一章里篇幅最大、最容易写错的地方。订单列表是个高频接口,Vue 后台和小程序端都要用,区别在于后台要分页、要支持按状态筛选,小程序端只查当前用户的订单。两个接口可以共用同一个 Mapper 方法,用动态 SQL 控制 WHERE 条件。这里有个小白必踩的坑:前端传排序字段时,ORDER BY 后面不能直接用#{},因为预编译占位符只能用在值的位置,用在 ORDER BY 或表名上会直接报错。

<select id="pageList" resultType="com.demo.entity.OrderDO"> SELECT id, order_no, user_id, item_id, status, appoint_time, address, amount, create_time, update_time FROM order_info <where> <if test="userId != null"> AND user_id = #{userId} </if> <if test="status != null"> AND status = #{status} </if> </where> <choose> <when test="orderBy == 'amount'"> ORDER BY amount ${orderDir} </when> <otherwise> ORDER BY create_time DESC </otherwise> </choose> LIMIT #{offset}, #{pageSize} </select>

注意这里 orderDir 用的是${},因为方向 asc/desc 不是预编译占位符能处理的。用了${}就有 SQL 注入面,所以必须在 Java 层做白名单校验,只允许传 asc 或 desc,其他一律按默认处理。排序字段本身也要白名单,字段名不允许前端随便传。另外 MySQL 的 LIMIT 分页建议至少给 user_id 建索引,否则订单量上来之后深分页会越来越慢,这属于 MySQL 排序和分页的基础素养,答辩时被问“订单多了怎么办”可以用这个顶住。

5. 联调与避坑:前后端拧成 demo 前必须处理的五处翻车现场

5.1 本地联调最小配置:域名校验、局域网 IP 与 Tomcat 端口

前后端都写完,进入联调阶段时,很多人会在这里耗掉比写代码更多的时间。先明确一个事实:小程序开发工具里跑通,不等于手机里能跑通。开发工具里可以用 127.0.0.1 直连本机 Tomcat,因为工具就跑在你电脑上;手机预览时,手机访问不到你电脑的 127.0.0.1,必须把 BASE_URL 换成电脑在局域网里的 IP。比如电脑 IP 是 192.168.1.10,那小程序端 request.js 里的 BASE_URL 就要改成http://192.168.1.10:8080/api,并且保证手机和电脑在同一个 Wi-Fi 下。

第二个拦路虎就是微信的域名校验。开发工具里默认会校验 request 合法域名,本地调试时要打开“不校验合法域名”开关:详情 → 本地设置 → 勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。这个开关只对开发工具和“预览”模式生效,正式上线必须配 HTTPS 域名,但毕业设计演示到预览这一步就够。手机上预览时还要在微信开发者工具的“真机调试”模式下操作,不要直接扫“预览”二维码期待所有功能都正常。

# 确认 Tomcat 已经启动并监听 8080 端口 netstat -ano | findstr :8080 # 如果是 Linux / macOS lsof -i :8080

端口检查是联调第一动作。Tomcat 默认 8080,如果本机 8080 被其他进程占用了,要在 tomcat/conf/server.xml 里改 Connector 端口,改成 8081 或 9090,然后同步修改前端 BASE_URL。之前见过一个同学本机 8080 被某个服务占着,Tomcat 启动日志滚动得飞快,但 curl 接口却连不上,他花了一晚上查代码,最后发现是端口冲突,这种玄学问题值得先检查。

5.2 五条踩坑记录:现象、原因、解决,按这个顺序排查

第一坑:小程序请求后端,开发工具 console 报“request:fail”。现象是请求没发出或立刻失败,Network 面板里看不到任何响应。原因是 url 写成了 127.0.0.1 但开发者工具运行环境里 127.0.0.1 指向工具自身,不是你的电脑;也可能是“不校验合法域名”没打开。解决:确认开关打开,BASE_URL 改为电脑局域网 IP,再不行就用开发者工具自带的“清缓存 → 清除网络缓存”重试。

第二坑:自定义导航栏在 iPhone 上文字“撞进”状态栏。现象是导航标题顶部被刘海挡住一部分。原因是导航栏高度写死成了 44px,没有计算状态栏高度和胶囊按钮位置。解决:用 3.3 节那段 getNavbarHeight 动态计算,navigationStyle 必须设置为 custom,并且页面内容的最外层 view 加上充足的 padding-top。

第三坑:第一次请求接口卡了十几秒才返回,之后又正常。现象是 Tomcat 日志里能看到请求进来,但响应延迟很大。原因是 MySQL wait_timeout 8 小时把空闲连接断了,Druid 连接池里的旧连接未剔除,第一次取连接时还拿着废连接去查库,失败重试后才重建。解决:Druid 配置里加 testWhileIdle=true,validationQuery=SELECT 1,timeBetweenEvictionRunsMillis 设为 60000,让连接池定期探活。这就是绝大多数“系统偶尔慢一次”的根源。

第四坑:下单成功,但订单列表查不到刚下的单。现象是订单详情页能看到,列表页没有。原因是 Service 方法加了事务但没提交,或者 MyBatis 二级缓存没刷新;更多时候是查询接口 state=status 只查了某个状态,而下单默认 status=0,列表页筛选条件默认查了 status=4。解决:先在 Navicat 里看 order_info 表数据是否存在,存在就是查询条件问题,不存在查事务。把数据和代码分开排查,别一上来就改代码。

第五坑:更新接口把 phone 字段更新成 NULL。现象是用户改了昵称,结果手机号没了。原因是 MyBatis 的 update 语句用了<set>里固定<if test="phone != null">,但前端把 phone 传成了空字符串,字符串不等于 null 从而进入更新分支。解决:判断条件改成phone != null and phone != '',同时前端提交前做一次 trim。这个坑在 Vue 后台的表单提交场景里尤其常见,因为 Element UI 的表单空值默认是空字符串而不是 null。

5.3 如果 MySQL 装在 Docker 或远程 Windows:连接串里的三点改法

有些同学的数据库不是本机装的,而是 Docker 容器里的 MySQL,或者实验室远程 Windows 机器上装的 MySQL。这两种场景下,SSM 的 JDBC 连接串写法有讲究。

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://192.168.1.20:3306/home_service_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456

第一,url 里的主机 IP 要改成 MySQL 所在机器的局域网 IP,不能再写 localhost。第二,Docker 容器里的 MySQL 必须做端口映射,启动容器时加-p 3306:3306,否则宿主机访问不到容器里的 3306。第三,MySQL 8.0 的驱动类名是 com.mysql.cj.jdbc.Driver,跟 5.7 的 com.mysql.jdbc.Driver 不一样,如果用 8.0 却写老驱动名,会直接 ClassNotFoundException。另外 serverTimezone=Asia/Shanghai 建议保留,不当时区可能相差八小时,订单里显示的时间全是错的。这些细节看起来不起眼,却是联调阶段最常见的返工来源。

6. 给答辩加分的验证技巧:用预置数据把订单全链路跑出节奏

演示最忌讳的是现场从零开始造数据,那会暴露太多不可控因素。正确做法是在答辩前准备好一组覆盖所有状态节点的预置数据,演示时按状态流一步一步点过去。建议准备四个订单:一个待支付、一个待接单、一个服务中、一个已完成,每个订单的金额、服务项目、预约时间都要逼真,比如“3月15日 14:00 日常保洁 129.00”。预置数据可以用 SQL 脚本一次插进去:

INSERT INTO order_info (order_no, user_id, item_id, status, appoint_time, address, amount) VALUES ('H20240615001', 1, 2, 0, '2024-06-16 14:00:00', '幸福小区 8-201', 129.00), ('H20240615002', 1, 3, 1, '2024-06-16 15:00:00', '阳光花园 3-502', 89.00), ('H20240615003', 1, 5, 3, '2024-06-17 09:00:00', '滨江公寓 2-1101', 199.00);

演示时的操作顺序也很重要。先在小程序端走一遍完整的用户视角:登录 → 首页浏览服务 → 选一个服务下单 → 看到订单状态变成待支付 → 点击支付逻辑(模拟) → 状态变成待接单。然后切到 Vue 后台,管理员看到待接单订单 → 接单 → 状态变服务中 → 再改已完成。期间把开发者工具的 Network 面板留在屏幕上,指一下每个请求的响应时间,让答辩老师看到前端确实在调后端接口,而不是各演各的。最后用一个已完成订单落到“评价”页面,把评价接口也演示一遍,整个业务闭环就完整了。

我的个人习惯是演示前一晚一定重新做一次“清空整个流程”的演练:清小程序缓存、重启 Tomcat、用 SQL 把订单状态重置回初始值,再全流程走一遍。因为开发过程中一定会在代码里写死过一些 token、写死过某个订单号,这些黑匣子只在当时的上下文里好用,清缓存之后立刻就暴露。第一次录演示视频时,我就因为忘了关开发者工具的“不校验合法域名”开关,导致手机端预览后所有请求全部失败,这血泪体验至今记得。提前把预置数据、状态节点、Network 面板都准备好,现场就不会慌。希望这篇能帮你的毕业设计少走几段弯路,把真实的联调细节做好,答辩时心里有底。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 16:16:58

差分进化优化随机森林:MATLAB多分类源码实战

简介&#xff1a;这份资源提供差分进化算法&#xff08;DE&#xff09;优化随机森林&#xff08;RF&#xff09;的MATLAB多分类实现&#xff0c;面向具备一定机器学习基础、希望深入理解超参数优化与集成学习的研究者与开发者。核心思路是用DE的种群变异、交叉与选择机制&#…

作者头像 李华
网站建设 2026/10/26 16:14:40

e2e profile1 深度解析:端到端测试配置档案与工程实践

1. 当“e2e”不再只是三个字母&#xff1a;从热词到落地场景的拆解 第一次看到“e2e”这个词&#xff0c;很多人脑子里蹦出来的可能是“端到端”三个字的缩写&#xff0c;然后就没有然后了。但如果你最近在技术社区、产品群或者招聘 JD 里频繁刷到它&#xff0c;尤其是搭配着“…

作者头像 李华
网站建设 2026/10/26 16:13:48

OpenMontage全景拼接实战:从图像配准到自动化出图

搞全景拼接这件事&#xff0c;我前前后后试过不少路子&#xff1a;手工在图像编辑软件里一层一层对齐、用全景相机拍完再导出、还写过一大堆特征点匹配的脚本自己拼。OpenMontage 是其中非常特别的一个开源项目——它把“特征提取、图像配准、投影变换、边缘融合”整条流水线全…

作者头像 李华
网站建设 2026/10/26 16:13:27

端到端测试实战:从设计到CI集成的完整指南

1. 从“e2e”说起&#xff1a;端到端测试到底在测什么第一次看到“e2e”这个词&#xff0c;很多人会以为是某个新框架的缩写&#xff0c;或者某个工具的名字。其实它是end-to-end的缩写&#xff0c;中文叫“端到端”。在软件研发语境里&#xff0c;它指的是一种测试层级——从用…

作者头像 李华
网站建设 2026/10/26 16:12:54

基于Java单体架构的养老管理系统:Spring Boot与MyBatis实践解析

简介&#xff1a;中州养老系统设计源码采用Java开发&#xff0c;定位为养老服务信息化平台&#xff0c;围绕老年人日常生活与健康管理需求&#xff0c;提供信息管理、服务预约、健康监测等能力&#xff0c;适合Java开发者、毕业设计或养老类项目学习者参考。压缩包共115个文件&…

作者头像 李华
网站建设 2026/10/26 16:12:07

端到端测试实战:Playwright选型、profile1配置与CI集成

1. 从“e2e”这个标题说起&#xff1a;它到底指什么 第一次看到“e2e”这三个字符&#xff0c;很多人会愣一下。它不像“Docker”“Kubernetes”那样一眼能看出领域&#xff0c;也不像“React”“Vue”那样自带技术栈标签。但如果你在软件工程、测试开发或者质量保障这条线上待…

作者头像 李华