简介:一套面向计算机专业毕业设计的家政服务管理小程序项目,基于微信小程序与SSM、Vue、MySQL开发,采用前后端分离架构,覆盖用户预约、在线支付、家政阿姨审核、管理员后台管理等核心流程,并提供用户端、阿姨端、管理端三类角色功能,适合课题设计、课程实训或答辩准备。资源包共1123个文件、约38.52MB,包含Java后端与SSM框架代码、Vue前端页面、微信小程序wxml/wxss/js逻辑、SQL数据库脚本,以及png/jpg界面素材和mp4演示视频,压缩包目录清晰,便于按模块快速查阅与定位。配套毕业论文、数据库脚本和视频演示,可对照源码梳理预约、支付、评价、留言等模块的实现思路,也能辅助快速搭建运行环境、定位关键代码,节省从零梳理项目的时间。目前已有228人学习下载,整套资料对完成毕业设计或理解前后端交互都较有参考价值。
1. 家政服务管理小程序是什么:一个毕设包要能从头跑到尾
如果你正在做毕业设计,看到“家政服务管理小程序”这个题目,大概率会下意识把它归为“又一个简单信息管理系统”。但实际上这个标题背后是一整套前后端分离的完整工程:微信小程序端给用户下单、Vue 管理端给管理员排单、SSM 后端负责接口与权限、MySQL 存所有业务数据,最后还要把源码、数据库脚本、毕业论文和视频演示一起交上去。它的难点恰恰不在单个技术点,而在于四个部分如何闭环。
我习惯把这类项目当成一次“小成本产线演练”:一个人同时充当产品、前端、后端、测试和文档工程师。这套方案能锻炼的能力很直接——数据库设计、接口约定、跨端联调、文档撰写,基本覆盖了真实项目中初级开发要遇到的绝大部分问题。适合三类人:正在做毕设但不知道一个完整系统如何串起来的学生;想用最短时间跑通“小程序 + 管理后台 + 后端接口”的初级开发者;以及需要给课程设计找可扩展模板的从业者。接下来我按自己复现这类项目的顺序,拆开讲清楚每一层怎么做、坑在哪、怎么验证。
2. SSM + Vue + 微信小程序三端分工:为什么这道题值得认真做
2.1 为什么是 SSM:从 Spring、SpringMVC、MyBatis 的职责说起
这个标题指定了 SSM,也就是 Spring + SpringMVC + MyBatis 的组合。很多学校在毕设要求里会写明“使用 SSM 框架”,理由很现实:它能覆盖 Java 后端开发里最核心的三个环节,而且面试时被问到的概率不低。
Spring 负责管理对象。家政平台里有用户、服务人员、订单、支付记录这些对象,它们之间的依赖关系如果全靠手动 new,改一个地方可能牵连十个地方。Spring 的 IoC 容器把对象的创建和注入接管过来,业务层只需要声明“我需要一个 OrderService”,容器就会在合适的时机把实现类交给你。控制反转这个概念听起来玄学,但在这类项目里它的体现非常朴素:你不在代码里写new OrderServiceImpl(),而是用@Autowired或者 XML 配置去声明依赖。
SpringMVC 负责接收 HTTP 请求。小程序端用户点“立即下单”,请求会走到后端的某个 Controller 方法。SpringMVC 通过@RequestMapping把 URL 映射到 Java 方法,再把前端传来的 JSON 自动绑定成对象。这个框架解决了“请求从哪来、参数怎么拿、结果怎么返回”的问题。前后端分离项目里,SpringMVC 还有一个关键配置是消息转换器,不然前端传来的 JSON 无法正确解析成 JavaBean,后端的返回对象也无法序列化成 JSON 回到前端。
MyBatis 是三者里最值得花时间的部分。它属于半自动 ORM,SQL 要自己写,但结果集映射交给框架。家政平台的订单表查询经常要带条件,比如“查询某个时间段内、某个员工的所有订单”,这类业务用 MyBatis 的动态 SQL 写起来非常顺手。而且 MyBatis 的 Mapper 接口和 XML 文件分离的组织方式,跟“先定 SQL 再写业务”的开发习惯天然匹配。理解 MyBatis 的关键是不要指望它帮你生成所有 SQL,它做的是把 Java 方法和 SQL 语句绑定起来,并处理参数和结果集。
2.2 三个端各管什么:用户端、管理端、后端接口的边界
家政服务管理程序的三端,业务上可以这样理解:微信小程序是“顾客的窗口”,Vue 管理端是“老板的收银台和调度台”,SSM 后端是“处理规则的大脑”,MySQL 是“记事的账本”。职责清楚分开,才能避免一个端里塞进所有功能。
微信小程序端只做客户端的事:展示服务项目、让用户选择服务人员和时间、下单、查看订单状态、提交评价。它不应该直接操作数据库,所有数据变更都通过 HTTP 请求走后端接口。小程序端的代码里不会出现jdbc或mybatis,只有wx.request和页面渲染。
Vue 管理端面向管理员,功能上和小程序端互补:管理员登录、查看订单列表、指派服务人员、上下架服务项目、审核用户评价。管理端跑在浏览器里,通过 Vue 的路由做页面跳转,通过 axios 发请求到后端。这里的核心是页面多、逻辑杂,但不需要考虑移动端适配。
SSM 后端把两端的数据操作统一收敛。后端提供 REST 风格接口,以/api/user、/api/order这类路径区分业务模块。它负责校验参数、鉴权(通过 Token 判断请求来自哪个用户、什么角色)、处理业务规则(比如订单取消后释放服务人员的时间段)、读写 MySQL。三端分离最直接的收益是:小程序端换掉不影响管理端;管理端想加功能只要后端接口还在;后端想优化 SQL 也不用动前端。
2.3 通信约定:接口路径、JSON 结构与 Token 校验
前后端分离的“分”靠的是接口拉开的。三端对接之前,第一步是把约定的文件写出来,哪怕只是几张纸。这个文件我一般会包含三个方面:接口路径清单、统一返回结构、错误码约定。
接口路径清单解决“前端该调谁”的问题。常见做法是按模块划分:/api/user/login、/api/order/list、/api/order/create、/api/employee/assign。路径不要带动词,每个接口只做一件事。这个清单不必写得很重,但要让写小程序的人看完知道自己调用后能拿到什么。
统一返回结构解决“前端怎么取数据”的问题。我常用的结构是:
{ "code": 200, "message": "success", "data": { "orderId": 1024, "status": 1 } }code 是业务状态码,200 代表成功,其他数字代表各种失败场景;message 是给前端直接弹窗展示的提示语;data 才是真正要渲染的数据。这个结构的好处是前端拿到响应后先判断 code 再取 data,不用去猜后端返回的是对象还是数组还是错误信息。
Token 校验解决“怎么知道你是谁”的问题。用户在小程序端登录后,后端返回一个 Token,小程序把这个 Token 存在缓存里,后续每个请求都放到 header 中。管理端同样有一套后台登录机制,用 JWT 或自定义签名都可以。这里要注意一个坑:如果 Token 只是放在前端请求里,后端所有接口都必须做拦截校验,而不是只对登录接口校验。SpringMVC 里实现一个 HandlerInterceptor,在preHandle里读 header 中的 Token,解析失败就返回 401。否则,任何知道接口路径的人都能直接绕开登录拿到数据。
3. MySQL 表结构先行:订单、员工、预约与评价的核心字段设计
3.1 用户、服务人员、服务项目:三条基础主数据怎么建
家政平台的基础数据有三条主线:谁在买服务(用户)、谁在提供服务(服务人员)、卖的是什么(服务项目)。这三张表设计得好,后面的订单和评价表就稳了。
用户表存小程序用户的身份信息。字段设计上最核心的是openid,微信生态里的唯一标识。nickname、avatar_url是可选的冗余字段,用于在管理端展示用户信息。表结构不需要太复杂,但要给一个create_time和status——前者用于统计新用户增长,后者用于冻结异常用户。
服务人员表比用户表多几个业务字段:real_name、phone、service_type(擅长哪类服务,如保洁、月嫂、维修)、id_card(实名认证用)、status(待审核/正常/停用)。这里有一个设计取舍:服务人员要不要关联用户表?常见做法是不关联,因为服务人员可能不是通过小程序注册的,而是管理员在后台录入的。如果后面想做一个“服务人员也能登录小程序查看自己接单”的功能,再单独加一个user_id字段做关联即可。
服务项目表是展示在小程序首页的数据源,字段至少包括:project_name、category、price、unit(次/小时)、cover_image、description、status。价格字段使用DECIMAL(10,2),不要用FLOAT,否则金额计算会出现 0.1 + 0.2 不等于 0.3 这类浮点误差。status用于上下架,小程序端只查询status = 1的项目。
3.2 订单表是核心:状态流转和冗余字段的取舍
订单表是整个家政平台的业务核心。用户端的“下单”、管理端的“派单”、财务结算、售后服务全都挂在订单上。设计订单表的时候,最忌讳的是只想着存当前的状态,而忽略状态是怎么变过来的。
订单表的字段可以分成四组。第一组是关联信息:user_id(谁下的单)、employee_id(指派给谁)、project_id(买的什么服务)。第二组是交易信息:price(下单时的价格)、pay_status(未支付/已支付/已退款)。第三组是服务信息:appoint_date、appoint_time(预约上门日期和时间段)、address(详细服务地址)、remark(用户备注)。第四组是状态信息:status和cancel_reason。
关于status的取值,我建议用一个枚举来管理,而不是在代码里散落魔法数字。
public enum OrderStatus { UNPAID(0, "待支付"), PENDING(1, "待派单"), ASSIGNED(2, "已派单"), IN_SERVICE(3, "服务中"), COMPLETED(4, "已完成"), CANCELLED(5, "已取消"), REFUNDED(6, "已退款"); }订单状态流转的原则是:任何一次status变更都要有业务前提。待支付订单才能取消;待派单才能指派员工;服务完成后才能评价。这些规则写在 Service 层,Controller 只是透传。
冗余字段的取舍是另一个常见纠结点。订单表要不要存employee_name和project_name?我的建议是存。虽然可以通过 join 查询拿名字,但订单列表页要高频展示这两个字段,冗余存储可以避免每次查询都连表。代价是如果员工改了名字,历史订单上的名字会保持旧值——这在会计和售后场景里其实是正确的行为,因为订单是快照。
3.3 预约与评价:附属表的边界
预约和评价都可以做成主表加附属表的结构,但这张附属表的边界要时刻提醒自己:它是订单的一种补充,不是独立业务。
预约表在某些方案里被设计成独立表,原因是“用户可以预约多个时段,等管理员确认”。但在家政服务场景,一次预约就是一个待确认的订单,预约字段加到订单表里更合理。我在订单表里已经有appoint_date和appoint_time两个字段,复用同一个状态机管理,就不会出现“预约已提交但订单表里没有记录”的尴尬。
评价表则是彻底的一对多:一个订单只可能有一条评价。字段包括order_id(唯一)、user_id、employee_id、rating(1-5 星)、content、reply(商家回复)、create_time。设计上要加唯一约束,保证同一个订单不能重复评价。评价数据在后端会用来算服务人员的平均评分,这个 SQL 写起来也很简单,就是 AVG 分组查询。
3.4 MySQL 初始化脚本的测试数据准备
毕业设计交付里数据库脚本是一等公民。初始化脚本不只是建表,还要提供可演示的测试数据。我见过很多脚本只建了空表,导致跑起来后前端没有任何内容可展示,整个系统看起来像没完成。
测试数据的准备有三个原则。一是主数据先造全:至少 5 个服务项目(覆盖保洁、维修、保姆、月嫂、家电清洗五类)、5 个服务人员、10 个用户。二是订单要有不同的状态分布,最好每个状态都有几条,这样管理端列表页能展示状态筛选的效果。三是时间字段要有梯度,比如订单的create_time分布在最近一个月内,方便演示“按日期范围查询”。
写 SQL 脚本时要用INSERT INTO ... VALUES (...)并带上显式 ID,不要依赖自增。显式写 ID 的原因是我需要预知user_id = 1是谁、project_id = 2是什么,然后在小程序端的演示时才能说出“这个用户今天下午 3 点预约了家电清洗”。演示时说不出业务数据,答辩印象分会差一截。
MySQL 8.0 和 5.7 在语法上有一个要注意的差异:8.0 默认字符集是 utf8mb4,而 5.7 需要手动指定。建表语句里统一加上DEFAULT CHARSET=utf8mb4,避免中文存入时出现乱码或字符截断问题。如果你是照着视频教程在 Windows 上一步步安装 MySQL,大概率装的是 8.0,这个细节可以帮你少踩一个隐形坑。
4. SSM 后端接口与 Vue 管理端联调:从 Controller 到页面渲染的落地路径
4.1 后端 Mapper 与 Service:先定 SQL 再说业务
SSM 项目有一个开发顺序问题:先写 Mapper 还是先写 Service?我的答案是先写 Mapper 的 SQL。原因很朴素:业务逻辑最后都落到数据查询上,SQL 写不出来的功能,Service 层写得再花哨也是空中楼阁。
以“订单列表页”为例。管理端需要展示字段有订单号、用户昵称、服务项目、服务人员、状态、下单时间。于是我在 OrderMapper.xml 里写查询:
<select id="selectOrderList" resultType="map"> SELECT o.id AS order_id, u.nickname AS user_name, p.project_name AS project_name, e.real_name AS employee_name, o.status, o.create_time FROM tb_order o LEFT JOIN tb_user u ON o.user_id = u.id LEFT JOIN tb_project p ON o.project_id = p.id LEFT JOIN tb_employee e ON o.employee_id = e.id <where> <if test="status != null"> AND o.status = #{status} </if> <if test="startTime != null"> AND o.create_time >= #{startTime} </if> </where> ORDER BY o.create_time DESC </select>这段 SQL 的逻辑说明:用 LEFT JOIN 关联四张表,取一次查出列表页需要的所有字段,避免 N+1 查询问题。<where>标签配合<if>实现动态条件,status 为空时不过滤,startTime 为空时忽略。注意>=是 XML 里转义后的大于等于号,直接写>=会导致 XML 解析失败——这是 MyBatis 新手必踩的坑。返回类型选map是因为这个查询结果是展示用的,不需要映射成固定实体。
Service 层在这一步关注的不是 SQL,而是状态机的校验。比如“指派员工”这个操作,Service 里要判断订单当前是否是待派单状态,已经被取消的订单不能指派:
@Service public class OrderServiceImpl implements OrderService { @Override @Transactional public void assignEmployee(Integer orderId, Integer employeeId) { Order order = orderMapper.selectById(orderId); if (order == null) { throw new BusinessException("订单不存在"); } if (order.getStatus() != OrderStatus.PENDING.getCode()) { throw new BusinessException("当前状态不允许指派员工"); } order.setEmployeeId(employeeId); order.setStatus(OrderStatus.ASSIGNED.getCode()); orderMapper.updateStatus(order); } }逻辑说明:@Transactional保证状态更新和员工绑定在同一事务里,中间出异常整体回滚。先查一次订单再校验状态,避免直接拿着前端传的 orderId 做更新——这是最基本的接口防错手段。
4.2 Controller 层与统一返回结构:让前端少做点判断
Controller 层在 SSM 项目里很容易写成“什么都往里塞”,但这里应该只做三件事:接收参数、调用 Service、返回统一结构。
@RestController @RequestMapping("/api/order") public class OrderController { @Autowired private OrderService orderService; @PostMapping("/create") public Result create(@RequestBody OrderCreateDTO dto) { Long orderId = orderService.createOrder(dto.getUserId(), dto.getProjectId(), dto.getAppointDate(), dto.getAppointTime(), dto.getAddress()); return Result.success(orderId); } @PostMapping("/assign") public Result assign(@RequestBody AssignDTO dto) { orderService.assignEmployee(dto.getOrderId(), dto.getEmployeeId()); return Result.success(null); } }逻辑说明:@PostMapping明确这是 POST 接口,对应前端wx.request和 axios 里的 POST 方法。@RequestBody把 JSON 请求体绑定到 DTO 对象,DTO 里可以加校验注解如@NotNull,省掉手动判断参数为空的代码。Result 是统一返回类,前端拿到code = 200之后取data渲染。
参数校验这里有个细节:wx.request默认的 content-type 是application/json。如果前端没设置这个 header,后端@RequestBody会解析失败,拿到一个空对象,然后因为 DTO 字段校验不通过返回错误。这种问题一般表现为“小程序端报 500,后端日志没异常”,排查时先看请求的 Content-Type 是不是 JSON。
4.3 Vue 管理端页面与 axios 对接:列表页、路由与跨域
Vue 管理端要对接后端接口,核心是封装一个统一的 axios 实例。我一般在src/utils/request.js里做全局配置,把 Token 自动加到请求头,把返回值里的 code 统一判断,前端代码就不需要在每个页面里重复写“取 data、判断 code”。
import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 10000 }) request.interceptors.request.use(config => { const token = localStorage.getItem('admin_token') if (token) { config.headers['Authorization'] = token } return config }) request.interceptors.response.use(res => { const result = res.data if (result.code !== 200) { this.$message.error(result.message) return Promise.reject(result) } return result.data }) export default request逻辑说明:baseURL 设为/api,配合 Vite 或 webpack 的代理配置,让开发环境的请求都发到本地 Java 后端端口,避免跨域报错。响应拦截器里判断 code,非 200 的统一弹错误提示,200 的直接返回 data,页面代码只需要关心业务数据。this.$message来自 Element UI,如果你用别的 UI 库,换成对应的消息组件即可。
路由是管理端另一个关键配置。管理端页面多,用 Vue Router 的懒加载拆包,避免首屏加载所有页面:
const routes = [ { path: '/login', component: () => import('@/views/Login.vue') }, { path: '/', component: () => import('@/layout/Index.vue'), children: [ { path: 'order', component: () => import('@/views/OrderList.vue') }, { path: 'employee', component: () => import('@/views/EmployeeList.vue') } ] } ]逻辑说明:() => import()是动态导入,Vue 在路由被访问时才加载该组件,首屏加载速度更快。Layout 组件做侧边栏导航,子路由对应的页面在 layout 的主区域渲染。这里有一个我在 vue 项目里踩过的坑:子路由配置时必须用相对路径order而不是/order,否则 Vue Router 会跳到根路径,结果页面白屏。
4.4 vue 安装与环境配置:本地开发代理和依赖管理的卡点
Vue 管理端从零跑起来,最让人头疼的不是 Vue 语法,而是环境。vite 或 vue-cli 的安装、依赖下载、Node 版本不匹配,每一步都可能卡半小时。我总结一个相对顺畅的路径。
Node 版本先确认。Vue 3 的项目要求 Node 16 以上,Vue 2 的项目要求 Node 14.18 以上。用node -v先看本机版本,版本太老直接升级,不要指望老版本能正常装依赖。
依赖安装时如果屡屡失败,常用做法是切换 npm 仓库到国内镜像。项目根目录新建.npmrc文件,写入registry=https://registry.npmmirror.com,然后重新执行npm install。装完依赖后跑npm run dev,看到本地服务启动。
前端代理配置写在项目配置文件里。如果是 Vite,在vite.config.js里加:
server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }这个配置的含义是:前端开发服务器收到/api开头的请求,转发到http://localhost:8080(你的 SSM 后端端口)。changeOrigin: true让后端收到的请求头里的 Host 变成后端的地址,避免某些后端框架校验来源 Host 导致 403。调试时看到管理端页面的请求状态是 304 或 200 而不是 404,说明代理生效了。
5. 微信小程序端最容易翻车的三个环节:登录、下单、导航栏适配
5.1 登录:从 wx.login 到自定义登录态的完整链路
微信小程序端登录是第一个必须梳理清楚的流程。它跟网页登录最大的区别是:小程序没有密码输入框,登录凭证来自微信的wx.login接口。调用后拿到一个临时 code,这个 code 有效期只有 5 分钟,用它换取 openid 的过程必须发生在后端。
正确的链路是:小程序先调wx.login拿到 code,然后调用自己的后端接口POST /api/user/login把 code 传过去。后端拿着 code 去微信的接口换 openid(这个过程需要 appid 和 secret),接着查用户表,如果 openid 不存在就自动注册一个新用户,最后给小程序返回一个自定义的 Token。小程序把 Token 存入wx.setStorageSync,后续所有请求在 header 里带上 Token。后端收到请求后从 Token 解析出用户身份,直接为这个用户提供数据,不再需要前端每次传 openid。
wx.login({ success: res => { wx.request({ url: 'http://localhost:8080/api/user/login', method: 'POST', data: { code: res.code }, success: (loginRes) => { const data = loginRes.data if (data.code === 200) { wx.setStorageSync('token', data.data.token) wx.setStorageSync('userInfo', data.data.userInfo) } } }) } })逻辑说明:wx.login的 code 是一次性的,不要缓存。后端拿到 Token 后,小程序存进缓存即可。这里有一个隐藏的坑:code 换 openid 的接口要用 HTTPS 调用。你在本地连微信接口没问题,但如果你把后端部署到普通 HTTP 服务器上,微信那边会拒绝回调。
5.2 手机号获取:为什么毕设里可能不需要真的拿到手机号
微信小程序“获取手机号”是热词里被搜得最多的一个能力,但也是最容易在毕设阶段卡住的一环。现实情况是:新版小程序获取手机号必须开通微信认证,而且要用button组件绑定open-type="getPhoneNumber"让用户主动点,不能静默获取。对于没有企业主体的个人开发者,这个功能根本拿不到完整手机号。
我建议毕设里用“手动填写手机号”的方式替代。在用户下单和注册页放一个input控件,让用户自己输入手机号,后端校验 11 位格式。这条路径在演示时更稳妥:点按钮授权可能出现弹窗失败或审核未通过的尴尬,手动输入则完全可控。
如果你的答辩老师一定会追问“为什么不接入微信一键获取手机号”,回答思路是:正经商业小程序需要企业主体认证,个人开发者毕设可以先用手输方式验证流程,等部署到有资质的账号上再替换。这个答复体现的是你懂业务边界,而不是技术不会。
5.3 顶部导航栏高度:自定义导航栏的适配公式
微信小程序的顶部导航栏高度适配问题,几乎每个做过原生小程序的人都会遇到一次。默认导航栏是微信渲染的,但是很多毕设项目的 UI 稿里,导航栏是自定义的,页面上方需要显示品牌色背景和自定义标题。这时候navigationStyle: custom可以设置成自定义,但状态栏(电量、时间那一条)和胶囊按钮(右上角的圆角按钮)的占位高度,必须自己算。
// app.js 里获取系统信息,存到全局 const systemInfo = wx.getSystemInfoSync() const capsule = wx.getMenuButtonBoundingClientRect() this.globalData.statusBarHeight = systemInfo.statusBarHeight this.globalData.navBarHeight = (capsule.top - systemInfo.statusBarHeight) * 2 + capsule.height逻辑说明:statusBarHeight是状态栏高度,不同手机上有差异,iPhone X 之后普遍在 44px 左右,Android 普遍是 20-30px。胶囊信息用wx.getMenuButtonBoundingClientRect()获取,navBarHeight的计算方式是胶囊到状态栏的距离乘 2 再加胶囊自身高度。自定义导航栏时,页面顶部要留出statusBarHeight + navBarHeight的高度占位,否则内容会被状态栏盖住。
真机调试时最容易发现一个现象:iPhone 和 Android 上同样的wx.getSystemInfoSync()返回的状态栏高度不同,导致导航栏顶到状态栏或离得太远。公式本身不会变,但一定要在多个模拟器模式下跑一遍,或者用wx.getWindowInfo()替代已经废弃的wx.getSystemInfoSync()。
5.4 下单与支付:毕设里的“模拟支付”怎么做才像真的
微信支付是小程序下单流程的最后一环。个人开发者的毕设项目申请不到微信支付商户号,这是硬性门槛。我建议做“模拟支付”:用户点击“立即支付”后,前端弹一个确认框,点击确认就把订单状态从待支付改成已支付,并跳转到订单详情页。
实现方式也很直接:
wx.showModal({ title: '模拟支付', content: '演示环境下点击确认即视为支付完成', success: res => { if (res.confirm) { wx.request({ url: 'http://localhost:8080/api/order/pay', method: 'POST', data: { orderId: this.data.orderId }, success: res => { wx.showToast({ title: '支付成功' }) wx.navigateTo({ url: '/pages/order/detail?id=' + this.data.orderId }) } }) } } })逻辑说明:后端/api/order/pay接口把订单状态从 UNPAID 更新为 PENDING(待派单),并记录支付时间。模拟支付和真实微信支付的差别只在“支付动作”这一步,后面的派单、服务、完成状态流转是一致的。答辩时可以明确说这是演示环境的模拟支付,也可以加一句“换成真实微信支付时只需把 showModal 换成 wx.requestPayment,并把支付结果回调到后端验签”。
5.5 本地调试:关闭域名校验、真机与开发者工具的差异
本地联调时,小程序开发者工具默认对wx.request的域名有校验,HTTP 本地地址会被拦截。解决办法是在开发者工具的“详情”设置里勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。这一步不做,你会一直看到request:fail url not in domain list的报错。
另一个容易忽略的点是:在开发者工具里跑后端地址用http://localhost:8080没问题,但真机预览时必须把地址改成电脑在局域网里的 IP,比如http://192.168.1.100:8080。否则真机上localhost指向的是手机自己,请求必然失败。每次真机调试前,在app.js里维护一个baseUrl变量,开发者工具用 localhost,真机用局域网 IP,不要全部写死。
还要注意后端防火墙没有拦截局域网访问。Windows 上跑 SSM 时,如果真机请求一直超时,先试一下手机浏览器能不能打开http://电脑IP:8080/api/order/list,打不开就检查防火墙的入站规则和 Tomcat 的端口监听范围。
6. 从源码到视频演示:部署验证与两个实用扩展方向
6.1 三端闭环验证:拿一个订单走完全流程
复现这个项目最后的目标是:答辩当天打开小程序和管理端,能清晰地展示“用户下单 → 管理员派单 → 用户查看状态 → 评价”这条路径。我建议在演示前做一次完整的旅程验证,用一个固定测试账号走一遍,而不是现场临场操作。
验证清单包括:小程序端能否浏览服务项目列表;未登录时点下单是否正确跳转登录;登录后能否创建订单;管理端能否看到待派单订单并成功指派员工;小程序端刷新后订单状态是否从待派单变成已派单;服务完成后能否提交评价并在管理端看到评分。任何一步断掉,用开发者工具的 Network 面板看后端请求的返回,优先查接口而不是查页面代码。
后端接口验证我习惯用 Postman 或直接写单元测试,但这篇不展开。核心思路是:每个接口先用工具调通,再从小程序里验证。两层都通过,才算接口真正可用。很多项目里的“前端点了没反应”,其实是后端接口抛了异常,但前端没有处理异常提示,看起来像前端坏了。
6.2 两个高性价比的扩展方向:地图选人和真实定时任务
如果时间富裕,我建议做两个扩展中的一个,它们都能在答辩时制造亮点。
第一个是“地图选人”。把服务人员的服务范围用经纬度表示,用户下单时选择自家位置,后端计算距离,推荐最近的服务人员。小程序端用wx.chooseLocation选位置,管理端在列表页展示服务人员所在区域。这个功能实操性强,代码量不大,却能体现你对“位置服务”场景的理解。
第二个是“定时任务自动派单”。用 Spring 的@Scheduled实现一个每天凌晨的定时任务,把超过 12 小时未派单的订单自动提醒或标记为待重指派。这个功能的价值在于展示了 SSM 里 Timer 机制的用法,也让订单状态机多了一个自动化触发入口。
这两个方向都比加一个简单的“积分功能”有区分度,因为它们触及了家政业务的核心痛点:人和服务的时间配对。
6.3 最后的收尾:从复现到内化
做完这个项目,我建议你把关键的点写进自己的笔记里。SSM 的 XML 配置、Vue 的代理设置、小程序的 statusBar 计算、MySQL 的 utf8mb4,这些内容不写成笔记,三个月后基本都会忘干净。技术的价值不在于“当时会了”,而在于下次遇到类似项目时能快速想起来怎么绕开。
这类毕业设计项目的完整形态从来不是源码本身,而是你一整套的交付:能跑通的代码、有业务逻辑的数据库、写清楚设计决策的论文、以及能说清“为什么这么设计”的口头表达能力。源码可以下载,论文可以改写,但这三项能力只能自己动手练出来。希望这篇拆解能帮你在复现这套方案时少走几条弯路,把省下来的时间真正花在理解系统为什么这么运作上。
本文还有配套的精品资源,点击获取