简介一套基于微信小程序与SSM框架的家政服务管理系统完整项目面向Java后端开发者、小程序学习者及需要完成毕业设计的高校学生。系统涵盖用户预约、服务管理、订单评价等核心业务通过SpringSpringMVCMyBatis实现服务端接口搭配微信小程序前端可直接部署运行。资源包共1145个文件约50.18MB包含Java源码、Vue管理端页面、小程序wxml/wxss页面、数据库建表SQL、论文文档及安装说明其中图片与样式资源居多便于还原系统界面。后端按控制器、服务层、DAO分层前端覆盖用户下单、派单、评价等完整流程结构清晰。目前已有52人学习适合作为课设/毕设参考或家政类项目二次开发底座。压缩包附带的说明文档与数据库脚本能帮助快速完成环境搭建与数据初始化整体实用性较强。1. 家政预约排班混乱不是靠“多招人”解决的做家政服务管理最常见的翻车现场不是获客难而是订单和保洁员对不上。用户在小程序下了单后台还在用 Excel 排班阿姨一天跑六个小区、用户等了两小时没人上门——这种问题靠堆人力是压不住的需要一个能把用户端、服务端和接单端串起来的系统。这个微信小程序家政服务管理系统 SSM 的项目就是在解决这件事小程序负责用户下单、预约、评价后端用 Spring SpringMVC MyBatis 处理业务逻辑和数据库读写托管在 Tomcat 上整套链路是典型的单体 Web 应用架构。值得拆开看的是它把“移动端 Java 后台 MySQL”三样东西粘在了一起而且不是那种只跑通 Demo 的拼装订单状态流转、服务人员排期、评价回写这些家政行业特有的业务点都有对应的表结构和接口设计。对于想快速搭一套家政类小程序、或者拿 SSM 做毕业设计的人来说这份源码把前后端联调的坑提前踩掉了一部分对于已经写过后台的开发者更值得关注的是它业务表怎么设计、请求怎么鉴权这比单纯看 CRUD 有价值。下面按项目里的实际目录和脚本一层层拆。2. SSM 整合骨架拆解Spring 容器、SpringMVC 路由与 MyBatis 映射如何在一个家政系统里协作2.1 从 .classpath 和 org.eclipse.wst.common.component 看项目结构压缩包里出现.classpath和org.eclipse.wst.common.component说明这是一个 Eclipse 动态 Web 工程WTP 结构不是 Maven 标准布局。这对部署方式有直接影响没有pom.xml依赖的 jar 包放在WebContent/WEB-INF/lib下打包发布时直接打整个 WebContent 目录。你在 IDEA 里导入时如果选择 Maven 方式会找不到构建脚本正确做法是选“Eclipse”或者“Web”类型导入然后把 Web 根目录指到WebContent。src/ ├ main/ │ ├ java/ # controller、service、mapper 接口 │ └ resources/ # mybatis 配置、Spring 配置 WebContent/ ├ WEB-INF/ │ ├ lib/ # 第三方 jar 包 │ ├ web.xml # 应用部署描述符 │ ├ spring-mvc.xml │ └ applicationContext.xml └ static/ # 前端静态资源这种布局下SSM 三个框架的职责边界非常清楚Spring 管 service 层的 Bean 生命周期和事务SpringMVC 管 Controller 层的请求路由MyBatis 管 dao 层的 SQL 映射。三者通过 web.xml 这个入口文件完成初始化顺序——先启动 Spring 容器再挂 SpringMVC 的 DispatcherServlet这也是为什么面试常问“为什么 Spring 和 SpringMVC 容器要分开”——因为 SpringMVC 只需要扫描 Controller而 Spring 要扫除 Controller 之外的所有 Bean。2.2 核心配置DispatcherServlet、数据源与 Mapper 扫描项目里 SSM 的整合配置一般分布在两个 XML 文件里。spring-mvc.xml负责开启注解驱动和视图解析器applicationContext.xml负责数据源和事务管理。以下是一个与该项目匹配度最高的典型配置组合!-- spring-mvc.xml -- context:component-scan base-packagecom.housekeeping.controller/ mvc:annotation-driven/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean!-- applicationContext.xml -- context:component-scan base-packagecom.housekeeping.service/ bean iddataSource classorg.springframework.jdbc.datasource.DriverManagerDataSource property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/housekeeping?useUnicodetrueamp;characterEncodingutf8/ property nameusername valueroot/ property namepassword value123456/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.housekeeping.dao/ /bean配置里的MapperScannerConfigurer是关键它自动扫描 dao 包下的接口为每个接口生成代理实现类省掉了手写 DaoImpl。mapperLocations指向classpath:mapper/目录所有 SQL 语句写在外置 XML 里而不是注解里好处是复杂订单查询不用拼接 Java 字符串SQL 调整时不用重新编译。数据源部分注意 URL 里的characterEncodingutf8家政系统的订单备注、地址信息里有大量中文编码不对会导致写入乱码这是中文项目最常见的启动后翻车点。2.3 Controller-Service-Mapper 三层如何承接小程序请求小程序端发来的请求走到后端的路径是固定的微信的wx.request把 JSON 发给 SpringMVC 的 ControllerController 调 Service 接口Service 实现类里通过 Mapper 接口操作数据库返回结果再逐层封装为 Map 或 JSONObject 回到小程序端。这个项目里家政服务中最典型的场景是“下单-派单-服务完成-评价”的闭环Controller 层代码大概长这样RestController RequestMapping(/order) public class OrderController { Autowired private OrderService orderService; // 小程序端提交预约订单 PostMapping(/create) ResponseBody public MapString, Object create(RequestBody Order order) { order.setStatus(0); // 0-待接单 int result orderService.createOrder(order); MapString, Object map new HashMap(); map.put(code, result 0 ? 200 : 500); map.put(message, result 0 ? 下单成功 : 下单失败); return map; } }这里的ResponseBody和RestController是 SpringMVC 4.x 之后的推荐写法省掉了每个方法手动写Jackson转换的过程。注意setStatus(0)这一行——家政订单的初始状态在服务端定死不让客户端传这是防止用户绕过小程序直接调接口刷单的基础手段。Order对象里的字段比如 serviceTime、address、workerId需要和小程序端表单里的 name 属性一致否则 JSON 反序列化时拿不到值且不报错这在联调时是个隐蔽问题。3. db.sql 还原与核心表设计从脚本到可查询的数据库3.1 source 命令重建数据库的完整操作压缩包里的db.sql承载的是整个系统的数据库快照包含建库语句、建表语句和初始化数据。重建时最稳妥的顺序不是用 Navicat 直接双击导入而是先进命令行因为脚本里可能包含DROP DATABASE IF EXISTS、CREATE DATABASE这类在图形化工具里容易因连接占用而中断的指令。mysql -u root -p # 输入密码后进入 MySQL 命令行 source /path/to/db.sql; SHOW DATABASES; -- 确认 housekeeping 库已创建 USE housekeeping; SHOW TABLES; -- 查看所有表执行source时保持默认的 autocommit 关闭状态反而更安全脚本里如果包含事务控制中途报错可以回滚不会留下半初始化状态。重建完成后重点检查user表里是否自带管理员账号很多家政系统的初始管理员密码是admin123或者123456登录脚本里写死了一个 MD5 加密串不直接明文存在数据库里。用这条命令能快速验证SELECT username, password, role FROM t_user;如果返回的 password 是 32 位十六进制字符串说明这是 MD5 存库如果还包含不可读的二进制前缀那就是用了 Spring Security 自带的 BCrypt这两种加密方式直接决定了后端登录校验的写法是DigestUtils.md5DigestAsHex还是BCryptPasswordEncoder.matches。3.2 家政业务中最容易被忽略的关联表家政系统不同于普通电商它的核心不是商品而是“人”和“时间”。观察db.sql里的建表脚本通常能找到几张关键表的关联关系表名主要字段作用t_useruser_id, openid, nickname, phone小程序用户的唯一标识openid 是核心t_workerworker_id, real_name, service_type, status家政服务人员维护可调度资源t_orderorder_id, user_id, worker_id, service_time, status订单主表关联用户和人员t_commentcomment_id, order_id, score, content服务评价关联订单而非用户这里要注意t_user表里的openid字段它不是昵称是微信小程序用户在当前小程序下的唯一标识由wx.login接口换取。家政系统做登录态时后端不应该信任前端传来的任何用户身份而应该拿code去微信服务器换openid再查库。t_order表同时关联user_id和worker_id字段上有外键约束的话删除用户时要注意先清订单引用否则删除一个已下单的用户会直接爆外键错误。3.3 一条 SQL 查清楚订单、用户和保洁员三表关联最常用的业务查询是“按状态查订单并带出用户和服务人员信息”这需要三表 JOIN。一个配合该项目的示例SELECT o.order_id, u.nickname AS customer_name, w.real_name AS worker_name, o.service_time, o.address, o.status FROM t_order o LEFT JOIN t_user u ON o.user_id u.user_id LEFT JOIN t_worker w ON o.worker_id w.worker_id WHERE o.status 1 -- 状态 1 表示已接单待服务 ORDER BY o.service_time ASC;用 LEFT JOIN 而不是 INNER JOIN 的原因在于t_order表中可能出现worker_id为 NULL 的待接单数据INNER JOIN 会把这些订单悄悄过滤掉导致小程序端显示“我的订单里少了一条”排查时又看不见任何报错是典型的“数据对不上”类 bug。ORDER BY o.service_time ASC是按服务时间升序排列家政场景里用户最关心的不是下单时间而是上门时间这个排序逻辑要和列表页展示的字段保持一致不然用户看到的时间顺序是乱的。4. 从 1-install.bat 到 2-run.bat本地部署的完整链路与微信小程序联调4.1 三个 bat 脚本承担了什么样的角色压缩包里的1-install.bat、2-run.bat、3-build.bat是 Windows 环境下的一键部署链路。它们的命名顺序已经暗示了执行顺序install → run → build。拆开看这三个脚本各自负责不同的生命周期阶段。# 1-install.bat —— 初始化环境和依赖 echo off echo [1/3] Initializing environment... xcopy /E /I .\lib .\apache-tomcat-8.5.xx\lib echo [2/3] Creating database... mysql -u root -p123456 db.sql echo [3/3] Libraries copied and database initialized. pause# 2-run.bat —— 启动 Tomcat 并部署项目 echo off echo [2/3] Starting Tomcat... cd apache-tomcat-8.5.xx/bin call startup.bat echo Tomcat started. Visit http://localhost:8080/housekeeping pause# 3-build.bat —— 重新编译并覆盖部署 echo off echo [3/3] Compiling and copying web modules... copy /Y .\src .\apache-tomcat-8.5.xx\webapps\housekeeping\WEB-INF\classes echo Build completed. pause三个脚本本质上是把 IDE 里点击部署的动作变成了命令行操作。1-install.bat把外置 lib 目录下的 jar 复制到 Tomcat 的 lib 目录下这种做法在老项目中常见通常是因为WebContent/WEB-INF/lib里没有收全所有依赖3-build.bat直接把编译后的 class 文件覆盖进 webapps 目录省去了重启 Tomcat 的等待时间但它不会触发 Spring 容器的重新初始化如果改了applicationContext.xml里的 Bean 定义这个复写流程是不会生效的必须手动回到2-run.bat重跑。4.2 本地联调时 Tomcat 端口和小程序域名的匹配小程序端开发工具里有一个容易踩的大坑微信开发者工具默认不校验合法域名只在“不校验合法域名”模式下才允许请求http://localhost:8080。也就是说小程序要能连上本地 Tomcat至少满足以下条件之一在开发工具详情设置里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”或者把后台接口部署到配置过的 HTTPS 域名上。前者是纯本地调试的正确姿势后者才对应线上环境。接口地址在小程序端的常见写法是// utils/api.js —— 统一封装请求地址 const BASE_URL http://localhost:8080/housekeeping; function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}${path}, method: method, data: data, header: { Content-Type: application/json }, success: (res) { // 后端统一返回 { code: 200, message: ..., data: {...} } if (res.data.code 200) { resolve(res.data.data); } else { reject(res.data.message); } }, fail: (err) reject(err) }); }); } module.exports { request };这段封装统一处理了三个问题请求地址集中管理切换环境只改BASE_URL一处错误码统一过滤页面不需要每个请求都判断 codePromise 化之后调用方可以用 async/await 写业务逻辑避免回调嵌套。注意这里的BASE_URL末尾带了/housekeeping这个上下文路径它对应 Tomcat webapps 下的部署目录名如果你手动改过 WAR 包的名字这里必须同步改否则请求会 404。4.3 登录态从 wx.login 到 openid 的映射家政小程序里登录是一个高频动作和普通网站不同微信小程序没有传统意义上的账号密码登录而是用wx.login获取临时 code再通过后端请求微信接口换取 openid。项目里的登录流程通常是这样的// pages/login/login.js wx.login({ success: (res) { const code res.code; wx.request({ url: http://localhost:8080/housekeeping/user/login, method: POST, data: { code: code }, success: (resp) { // 后端返回 openid 对应的 userId 和 sessionToken wx.setStorageSync(userId, resp.data.data.userId); wx.setStorageSync(token, resp.data.data.token); } }); } });对应的后端登录逻辑接收 code调用微信接口https://api.weixin.qq.com/sns/jscode2session换取 openid 和 session_key再拿着 openid 查t_user表。如果查不到就自动注册一个新用户如果查到了就生成一个 Token 返回给小程序。这个 Token 后续在每个请求里通过 header 携带后端用拦截器统一校验。要强调的是很多新手把 openid 明文当作 Token 用这是有风险的因为 openid 一旦泄漏别人就能冒充身份。常见做法是生成一个随机 UUID 存到 Redis 里key 是 token、value 是 userId过期时间设 2 小时但这个项目里如果不依赖 Redis那退而求其次的做法是把 token 存到t_user表的一个字段里每次请求比对——性能差点但逻辑闭环是完整的。5. weixin://dl/business 跳转链接在小程序家政场景的实战与验证5.1 链接的构成逻辑scheme 与 query 参数如何拼接家政服务场景里有个高频运营需求从公众号文章、短信、或者外部网页直接唤起微信小程序并自动落到某个保洁详情页或预约页。微信为此提供了 URL Scheme 跳转能力线上最常见的就是weixin://dl/business这个 scheme。它本质上是一个固定前缀加上业务参数形如weixin://dl/business/?t你的token其中t参数不是自己随便编的需要后端调用微信接口生成。在微信官方文档里这个接口是generateScheme或者generateShortLink用于将小程序页面路径和查询参数编码成一个 token。拿家政项目来实操如果要生成一个直达“保洁阿姨详情页”的链接前端拿到 token 后拼出的完整跳转地址就是这样// 生成跳转链接 const path pages/worker/detail; const query id1001fromwechat_article; wx.cloud.callFunction({ name: generateScheme, data: { path: path, query: query } }).then(res { const scheme res.result.scheme; // 形如 weixin://dl/business/?txxxxx // 把这个 scheme 嵌入公众号菜单或短信模板 window.location.href scheme; });注意这里query里的 id 是家政人员的 worker_idfrom参数用来标记渠道方便后台统计哪个渠道带来的预约多。生成后的链接不能直接在小程序内部使用它面向的是微信环境外的触达场景。校验链接是否生成成功的标准是开发者工具里模拟打开后能正确跳到pages/worker/detail?id1001fromwechat_article并且该页面在 onLoad 里能成功从 query 中读取参数。家政系统在接收这类跳转时还有一个隐藏问题用户通过 scheme 进入小程序后需要根据 id 正确绑定推荐关系或渠道标记这要求后端提供一个专门记录跳转来源的接口在前端 onLoad 时上报。5.2 线上真机验证跳转链路是否完整验证不能停在“模拟器能打开”这一步。真机上微信版本不同、用户从哪个入口点进来表现都不完全一样。常规验证方法是把生成的 scheme 链接转成二维码用微信扫一扫打开观察小程序是否正确落地到目标页面。这里有一个容易踩的坑weixin://dl/business从 Android 和 iOS 上唤起时的表现略有差异——某些 Android 微信版本上如果用户未安装目标小程序会提示下载而不会直接打开iOS 则会跳到 App Store 的微信下载页。因此测试时至少找一台 Android 一台 iOS 过一遍完整链路。家政系统里更实用的做法是在落地页增加渠道码回写代码示例// pages/worker/detail.js onLoad(options) { if (options.from options.id) { wx.request({ url: http://localhost:8080/housekeeping/channel/record, method: POST, data: { workerId: options.id, channel: options.from } }); } }这段代码的作用是scheme 链接里的from参数会被微信原样保留并传给落地页页面加载时把这个渠道信息和工人 ID 上报给后台后台就能统计出“公众号菜单带来 30 单、短信链接带来 12 单”。验证时在微信开发者工具里切换不同的 from 值再查后台统计表能分渠道看到数量变化就说明整条链路是通的。如果上报后表里没数据优先检查options.from是否被微信转义或截断——带特殊字符的 query 参数在部分安卓机型上会被编码后端解码时要用decodeURIComponent处理这个细节最容易踩。本文还有配套的精品资源点击获取