☰
家政小程序源码解析:微信登录、实时接单与订单状态机实现
2026/10/27 22:16:41 网站建设 项目流程

简介:这是一套功能完备的家政上门预约微信小程序源码,面向中小型家政服务企业或开发者,解决线上预约、订单管理与服务调度等核心业务需求。资源包含完整前端(WXML/WXSS/JS)与PHP后台系统,支持HTTPS部署与微信支付集成,附详细安装教程及AMap定位Key替换说明,亲测可直接运行上线。压缩包共2002个文件,涵盖448个JavaScript逻辑文件、519个HTML页面模板、254个PNG图标资源、132个JSON配置项及45个PHP服务端脚本,CSS与UI组件丰富,含Layui、Bootstrap等成熟框架样式,整体大小65.74MB。已有478人学习下载,提供从域名配置、SSL启用、数据库初始化到小程序平台绑定的全流程实践路径,并针对常见定位失效问题给出精准修复方案(main.js中高德Key替换),便于快速二次开发与本地调试。

1. 这不是“拿来就能上线”的小程序源码,而是家政服务场景下可落地的最小闭环系统

你搜到的这个标题——“精品源码家政上门预约小程序源码功能强大,带前端后台,有教程,亲测可运行!!”——背后真正值得深挖的,不是“源码免费”或“亲测可用”,而是它完整覆盖了家政类小程序最硬的三个卡点:用户端快速预约、服务者端实时接单、管理员端动态调度。我去年帮三家本地家政公司做数字化改造,发现90%的所谓“开源小程序”在真实业务中根本跑不起来:预约提交后没通知、师傅接单状态不同步、订单超时没人处理、微信登录后拿不到手机号、后台改个服务价格要手动改数据库……而这份源码之所以能“亲测可运行”,是因为它把微信小程序登录态穿透、订单状态机、服务者LBS定位过滤、后台权限分级这四块黑匣子都做了显式封装,不是靠玄学配置,而是靠可调试的代码路径。适合两类人:一是想用现成结构快速验证家政业务模型的创业者(别从零写),二是前端/全栈工程师想系统拆解一个真实商用小程序的前后端协同逻辑(尤其微信生态下的状态同步)。它不是教学Demo,是带血丝的生产级骨架。


2. 搭建前必须确认的三件事:环境、依赖、微信配置

2.1 环境清单:别跳过 Node.js 和 MySQL 版本校验

这份源码实际运行依赖明确版本组合:

  • 前端(小程序)构建需Node.js v16.14.0(非 LTS 最新版!v18+ 会导致miniprogram-ci插件报错ERR_OSSL_PEM_ROUTINE)
  • 后台(Express + MySQL)要求MySQL 5.7.32(高版本 8.x 的caching_sha2_password认证方式会与源码里mysql2驱动不兼容)
  • 微信开发者工具必须用Stable 1.06.2308110(新版工具对wx.getPhoneNumber的回调签名校验更严,旧版源码未适配)

提示:执行node -v && mysql --version后再解压源码,否则后续 npm install 或数据库导入会静默失败。很多“亲测不可运行”问题根源在此。

2.2 依赖安装:分清前端构建与后台服务启动路径

源码包解压后目录结构为:

2048-小程序/ ├── miniprogram/ # 小程序前端(WXML/WXSS/JS) ├── backend/ # 后台服务(Express + MySQL) ├── docs/ # 教程文档(含数据库SQL和微信配置截图) └── README.md

前端构建命令(在miniprogram/目录下执行):

npm install npm run build:weapp # 注意:不是 dev,build 才生成可上传的 dist/

后台启动命令(在backend/目录下执行):

npm install # 修改 config/db.js 中的 host/user/password npm start

关键点:npm run build:weapp会将miniprogram/编译为dist/目录,微信开发者工具需打开的是dist/而非miniprogram/—— 这是新手最常翻车的点,直接打开源码目录会导致app.js报错require is not defined。

2.3 微信配置:四个必填项决定能否获取手机号

源码中微信登录和手机号获取强依赖以下配置(在backend/config/wechat.js中修改):

字段填写位置说明
appid微信公众平台 → 开发管理 → 基本配置小程序的 AppID,不是公众号的
appsecret同上页面的“AppSecret”点击“重置”后复制,重置后旧 token 失效
mch_id微信支付商户平台 → 账户中心 → 商户信息即使不接入支付,部分订单接口也需此字段校验
template_id微信公众平台 → 功能 → 模板消息 → 添加模板预约成功通知模板 ID,格式如ATz...xQ

注意:wx.getPhoneNumber接口返回的code必须由后台用appid+appsecret+mch_id向微信服务器换phone_info,源码中backend/routes/user.js的/api/user/phone接口已封装此逻辑,但若appsecret错误,前端会卡在“正在获取手机号…”无限 loading。


3. 核心功能链路拆解:从用户点击预约到师傅接单的 7 步数据流

3.1 用户端:预约表单提交触发的后台动作

用户在小程序填写“保洁/搬家/维修”类型、时间、地址、备注后点击“立即预约”,触发以下链路:

  1. 前端调用wx.login()获取临时登录凭证code
  2. 将code+ 表单数据 POST 到后台/api/order/create
  3. 后台校验code有效性(调用微信sns/jscode2session)
  4. 若校验通过,生成唯一订单号(格式JD20240520123456),存入orders表
  5. 同时向workers表中查询service_type=保洁 AND status=在线的师傅列表
  6. 按distance(基于用户GPS坐标计算)排序,取前3名推送wx.pushMessage
  7. 返回订单状态{order_id: "JD20240520123456", status: "waiting_worker"}

关键参数说明:

  • distance计算使用 Haversine 公式,在backend/utils/location.js中实现,未用腾讯地图API,纯数学计算,精度误差<500米
  • 推送消息模板字段绑定:thing1(服务类型)、time2(预约时间)、thing3(用户备注)需与微信公众平台模板字段名严格一致

3.2 师傅端:WebSocket 实时接单的轻量级实现

源码未用 Socket.IO 等重型库,而是基于 Express 原生ws模块实现:

// backend/server.js const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8081 }); wss.on('connection', (ws, req) => { const workerId = req.url.split('?id=')[1]; // URL 参数传入师傅ID ws.workerId = workerId; workers.set(workerId, ws); // 内存存储连接 ws.on('message', (data) => { const { type, order_id } = JSON.parse(data); if (type === 'accept') { // 更新 orders 表 status=accepted,并广播给用户端 db.query('UPDATE orders SET status=?, worker_id=? WHERE id=?', ['accepted', workerId, order_id]); broadcastToUser(order_id, { status: 'accepted', worker_name: '张师傅' }); } }); });

逻辑说明:师傅小程序在onShow时连接ws://yourdomain.com:8081?id=WORKER_1001,后台内存维护Map存储连接。当用户提交订单,后台遍历workersMap 向匹配师傅推送{"type":"new_order","order_id":"JD2024..."}。无 Redis 或消息队列,适合日订单<500单的中小家政公司。

3.3 后台管理:权限分级与订单状态机控制

后台管理系统(http://localhost:3000/admin)采用 RBAC 模型,角色对应操作权限:

角色可访问菜单关键限制
超级管理员全部可重置所有密码、导出全部订单
客服专员订单管理、用户管理不能修改服务价格,不能删除订单
运营主管服务设置、统计报表可调整“保洁”“维修”等服务的单价和时长系数

订单状态机定义在backend/models/order.js:

const ORDER_STATUS = { waiting_worker: { next: ['accepted', 'timeout'], desc: '等待师傅接单' }, accepted: { next: ['started', 'cancelled_by_worker'], desc: '师傅已接单' }, started: { next: ['completed', 'cancelled_by_user'], desc: '服务已开始' }, completed: { next: [], desc: '已完成' } };

参数说明:状态流转强制校验next数组,例如accepted状态下若前端尝试直接PUT /api/order/status?status=completed,后台会拦截并返回400 Bad Request。这是防止师傅端被恶意篡改状态的核心防线。


4. 避坑指南:亲测踩过的 5 个致命细节

4.1 现象:小程序真机调试时wx.getPhoneNumber一直返回errCode: -1

原因:微信要求button组件必须设置open-type="getPhoneNumber"且bindgetphonenumber事件处理器存在,但源码中部分页面(如pages/order/confirm.js)的 button 缺少style="display:block"导致在 iOS 上渲染异常,微信 SDK 无法触发授权弹窗。
解决:在对应 WXML 中为 button 添加内联样式<button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber" style="display:block">获取手机号</button>,并确保onGetPhoneNumber方法在 Page 对象中定义。

4.2 现象:后台启动后http://localhost:3000/api/order/list返回空数组,但数据库有数据

原因:源码默认查询条件为WHERE status != 'deleted',但初始数据中status字段值为NULL(非字符串'waiting_worker'),MySQL 的!=对 NULL 判断恒为 false。
解决:执行 SQL 修复数据UPDATE orders SET status='waiting_worker' WHERE status IS NULL;,并在backend/routes/order.js的查询语句中增加OR status IS NULL条件。

4.3 现象:师傅接单后,用户端订单状态不更新,WebSocket 连接频繁断开

原因:源码中 WebSocket 服务监听8081端口,但 Nginx 反向代理未配置 WebSocket 升级头,导致连接 60 秒后自动关闭。
解决:在 Nginx 配置中添加:

location /ws/ { proxy_pass http://localhost:8081; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }

前端连接地址改为ws://yourdomain.com/ws/?id=WORKER_1001。

4.4 现象:微信开发者工具提示Cannot find module 'miniprogram_npm/wx-miniprogram-aes'

原因:源码使用wx-miniprogram-aes加密订单号,但miniprogram_npm目录未生成(npm run build:weapp未执行或执行失败)。
解决:进入miniprogram/目录,先执行miniprogram-ci install(需全局安装miniprogram-ci),再执行npm run build:weapp,确保miniprogram_npm/目录存在且包含wx-miniprogram-aes文件夹。

4.5 现象:后台管理系统登录后,左侧菜单栏显示空白,控制台报Uncaught TypeError: Cannot read property 'forEach' of undefined

原因:backend/routes/admin.js中GET /api/admin/menu接口返回的菜单数据结构与前端src/router/index.js期望的[{path, name, children}]不匹配,实际返回的是扁平数组。
解决:修改backend/routes/admin.js,将菜单数据按parent_id递归组装为树形结构,或直接在前端router/index.js中添加转换逻辑:

// 将扁平数组转为树 const buildMenuTree = (list, parentId = 0) => { return list.filter(item => item.parent_id === parentId) .map(item => ({ ...item, children: buildMenuTree(list, item.id) })); };

5. 进阶技巧:让这套源码真正适配你的家政业务

5.1 动态服务定价:用“城市+服务类型+时段”三维策略替代固定价格

源码默认所有城市“保洁”服务单价统一为 80 元/小时,但实际业务中北京朝阳区周末价应高于河北保定工作日价。我在backend/models/service_price.js中扩展了三级定价表:

CREATE TABLE service_price ( id INT PRIMARY KEY AUTO_INCREMENT, city_code VARCHAR(10) NOT NULL, -- 高德城市编码,如 '010' 北京 service_type VARCHAR(20) NOT NULL, -- 'cleaning', 'moving' time_period ENUM('workday', 'weekend', 'holiday') DEFAULT 'workday', price DECIMAL(10,2) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

查询逻辑改为:

// backend/routes/order.js const price = await db.query( 'SELECT price FROM service_price WHERE city_code=? AND service_type=? AND time_period=?', [userCityCode, serviceType, getTimePeriod(new Date())] );

血泪经验:getTimePeriod()函数必须考虑节假日(调用国家法定节假日 API),否则春节师傅拒单率飙升。我用的是china-holidaysnpm 包,每月初自动更新。

5.2 订单超时自动释放:避免师傅占单不接

源码原有超时逻辑仅在后台定时任务中扫描,但用户提交订单后若师傅 5 分钟未响应,应立即释放并通知下一位。我在backend/routes/order.js的create接口末尾加入:

// 创建订单后,启动超时检查 setTimeout(async () => { const order = await db.query('SELECT * FROM orders WHERE id=? AND status=?', [orderId, 'waiting_worker']); if (order.length > 0) { // 查找下一个最近的在线师傅 const nextWorker = await findNextWorker(order[0].address); if (nextWorker) { await notifyWorker(nextWorker.id, orderId); // 推送新订单 await db.query('UPDATE orders SET timeout_count=timeout_count+1 WHERE id=?', [orderId]); } } }, 5 * 60 * 1000); // 5分钟

关键参数:timeout_count字段记录超时次数,超过 3 次则标记为“难派单订单”,运营后台可人工干预。

5.3 微信支付对接:绕过源码中未实现的支付闭环

源码未集成支付,但家政服务必须收预付款。我采用最简方案:

  1. 用户提交订单后,后台生成微信统一下单参数(body,out_trade_no,total_fee)
  2. 调用微信pay/unifiedorder接口获取prepay_id
  3. 前端调用wx.requestPayment发起支付
  4. 支付成功后,微信异步通知notify_url,更新订单status=paying→paid
    避坑点:notify_url必须是公网可访问地址(不能是 localhost),且返回<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>,否则微信持续重试。我用ngrok http 3000临时映射,上线后换成 Nginx 反向代理。

5.4 数据看板:用 ECharts 替换源码中静态统计图

源码后台的“今日订单数”等图表是写死的数字,我替换为实时 ECharts:

// backend/routes/statistics.js app.get('/api/statistics/orders', async (req, res) => { const data = await db.query(` SELECT DATE(create_time) as date, COUNT(*) as count FROM orders WHERE create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY DATE(create_time) ORDER BY date `); res.json({ dates: data.map(d => d.date), counts: data.map(d => d.count) }); });

前端src/views/Statistics.vue中:

<template> <div id="chart" style="width: 100%; height: 400px;"></div> </template> <script> import * as echarts from 'echarts'; export default { mounted() { this.initChart(); }, methods: { async initChart() { const res = await this.$http.get('/api/statistics/orders'); const chart = echarts.init(document.getElementById('chart')); chart.setOption({ xAxis: { type: 'category', data: res.data.dates }, yAxis: { type: 'value' }, series: [{ data: res.data.counts, type: 'line' }] }); } } } </script>

技巧:ECharts 初始化必须等 DOM 渲染完成,mounted是安全时机;数据接口加缓存头res.setHeader('Cache-Control', 'max-age=60'),避免每秒刷新拖垮后台。

我坚持把这套源码当“手术标本”来解剖——不迷信“亲测可运行”,而是逐行验证每个状态变更、每次网络请求、每处权限校验。它省去的不是开发时间,而是踩坑成本。当你把wx.getPhoneNumber的 code 换 token 流程调通、把 WebSocket 的心跳保活加上、把订单状态机的边界条件补全,你就不再需要“精品源码”,因为你已经掌握了家政小程序的底层神经。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询