简介:likeshop上门家政系统开源版源码是一套基于likeadmin-php框架开发的上门预约系统,面向需要搭建家政服务平台的开发者与本地生活运营商。系统将用户端与师傅端深度融合,覆盖地图定位、在线预约、自动派单、后台派单、下单支付、核销订单等完整业务闭环,支持服务价格多规格设置、首页DIY装修、预约时间段自定义,并接入微信与支付宝官方支付、腾讯地图以及阿里云和腾讯云短信,全方位满足日常预约、接单与结算场景。资源共包含两千个文件,压缩包约96.09MB,为全部前后台无加密源代码,主要文件类型包括Vue、JavaScript、CSS等前端交互代码,以及Java、JSON、Markdown等后端逻辑与配置文档,目录结构清晰,便于按模块阅读和二次开发。目前已有138人学习下载,适合希望快速部署本地家政预约平台或有深度定制需求的技术人员。
1. 为什么家政需要一套独立系统,而不是拿通用商城硬改
likeshop 上门家政系统开源版(下文简称“家政版”)是把通用商城订单改造成预约制服务订单的行业发行版。通用商城解决的是“把商品卖出去”,家政解决的是“把服务排出去”——预约时间、指派技师、上门核销、按次结算,这四个动作通用商城模板一个都覆盖不了。这套系统保留了 likeshop 原生的支付、会员、营销、分销能力,再把订单主干换成了服务单模型,适合做区域家政平台的个人或小团队直接拿来做二开起点。整套系统包含管理后台、商户端、用户端小程序/H5,技术栈为 PHP + MySQL + Redis,部署形态与 likeshop 同源,对熟悉 thinkphp6 生态的开发者非常友好。
2. 从下单到结算的完整链路:预约、派单与核销的模块拆解
2.1 业务模块与数据库表结构
家政版的核心不在页面,而在底层表结构。订单相关表是这套系统差异最大的地方。先看几张关键业务表的设计逻辑。
| 表名称 | 核心字段 | 说明 |
|---|---|---|
ls_hotel_order(家政订单表) | order_sn、reserve_time、service_address、service_items、technician_id、pay_status、order_status | 服务单主体,非实物商品订单 |
ls_order_refund | order_id、refund_sn、refund_amount、refund_status | 退款申请与处理记录 |
ls_technician | real_name、mobile、service_scope、status | 技师档案与技术工种标签 |
ls_merchant_settlement | order_amount、commission_amount、settlement_amount、settlement_time | 平台与商户间的结算流水 |
如果你打开数据库看,会发现ls_hotel_order比通用的ls_order多了reserve_time和technician_id两个字段。前者决定服务派单的排期依据,后者关联服务人员。技术工种标签service_scope在派单环节扮演筛选器角色,例如用户下单“空调清洗”,系统自动过滤出具备该标签的技师列表。
2.2 预约订单的状态流转:从待支付到已完成的五个节点
订单状态机是整个二次开发的基石。家政版核心状态定义在likeshop\common\enum\OrderStatusEnum中,与普通电商不同,服务单多了“待服务”和“服务中”两个业务态。一条订单的正常流转路径是:
// 核心状态枚举 const WAIT_PAY = 0; // 待支付 const WAIT_SERVICE = 1; // 已支付,待预约上门 const SERVING = 2; // 服务进行中 const WAIT_COMMENT = 3; // 待评价 const FINISHED = 4; // 已完成 const CANCELLED = 5; // 已取消逻辑上每次状态变更都伴随动作约束:WAIT_SERVICE下才有“开始服务”按钮,SERVING下才有“完成服务”按钮,评价必须等WAIT_COMMENT才能提交。你在二开时不要越过这些状态节点直接改库,否则结算逻辑和统计报表都会错位。比如某天运营手动把一单从WAIT_SERVICE改成FINISHED,平台佣金照样会结算,但派单记录和服务时长统计全丢了。
2.3 页面到接口的映射关系
前端页面并不多,真正容易绕晕的是页面与接口的对应关系:
| 页面 | 接口路径 | 作用 |
|---|---|---|
| 预约服务页 | /api/goods/detail | 展示服务项和价格规格 |
| 确认订单页 | /api/order/add | 提交服务时间、地址、技师偏好 |
| 订单列表 | /api/order/lists | 用户端查看全部服务单 |
| 服务核销页 | /api/verify/verify | 商户端核销服务码 |
| 商户结算页 | /admin/merchant/lists | 后台查看商户结算状态 |
确认订单接口order/add是核心入口,前端会同时传入reserve_time、service_address、service_items(JSON 数组)。这里有一个关键设计:下单时并不直接锁定技师,而是等用户付款后进入派单队列。也就是说technician_id首次落库可能为空,由运营在后台手工指派或通过派单规则自动分配。这个设计对后文佣金中间件的改造非常关键——支付成功回调触发的是“订单状态变更 + 佣金计算”,跟技师是否存在没有强耦合。
3. 本地部署与系统初始化:从环境准备到后台配置
3.1 部署环境要求与项目目录结构
这套系统的运行环境要求与 likeshop 官方一致。参考常见部署实践,生产环境使用 Linux + Nginx + PHP 8.0 以上 + MySQL 5.7 以上 + Redis 的组合,PHP 需要启用 fileinfo、redis、bcmath 扩展。项目解压后的目录结构如下:
project/ ├── server/ # PHP 后端(thinkphp6 框架) │ ├── app/ # 应用目录 │ │ ├── api/ # 用户端接口模块 │ │ ├── admin/ # 管理后台接口模块 │ │ ├── platform/ # 平台端接口模块(家政版) │ │ └── common/ # 公共模型、枚举、服务层 │ ├── route/ # 路由定义 │ └── extend/ # 第三方扩展 ├── pc/ # PC 端管理后台 ├── uni-app/ # 用户端 H5/小程序 ├── merchant/ # 商户端 H5/小程序 ├── sql/ # 数据库脚本 └── README.md注意server/app/platform这个模块,这就是家政版区别于通用 likeshop 的核心——平台对商户抽佣和资金结算相关的业务代码都在这里面。如果你拿到的是完整源码包,启动前先检查server/.env是否存在,不存在则从.env.example复制一份,并核对数据库连接和 Redis 配置。
3.2 数据库导入与后台初始化的完整步骤
第一步把sql目录下的数据脚本导入 MySQL。脚本文件一般按日期+版本号命名,例如20240101_v1.sql。导入前先建库,字符集建议utf8mb4。命令行导入:
mysql -uroot -p123456 < sql/20240101_v1.sql导入完成后进入后端配置。管理后台首次打开会进入安装向导,需要按页面引导填入数据库信息、创建管理员账号、配置 Redis 地址。安装向导的主要作用是生成.env文件并写入基础配置。如果部署后打不开后台,先检查.env中APP_DEBUG是否已设为true,开启后页面会直接输出具体报错。
3.3 关键 system 配置项
后台上线前至少要求检查六个配置项:支付方式、短信服务、地图密钥、分账比例、预约时间配置、商城基础设置。其中分账比例是家政版特有项,位置在“平台端-结算设置-佣金比例”,默认值通常为 15%,有上下浮动区间。
支付接口在“商城-支付方式”里配置,常用的有微信支付与支付宝。家政这种服务类场景,预约时段配置尤其重要,在“系统-预约设置”里可配置“最早可预约时间”和“最晚可预约时间”,比如设置最早提前 2 小时、最晚提前 7 天可约。这直接决定前端日期选择器能选的范围,如果运营发现用户下单可选时间不对,先来这里检查,不要急着改前端组件。
4. 二次开发实战:给平台加一个佣金结算中间件
4.1 佣金结算的通用实现思路
家政平台的商业模式通常有两种:一种是自己养技师直接服务,另一种是招募商户入驻、平台抽佣。这套开源版本地面向的是第二种——平台提供订单撮合,商户接单并服务,平台按比例抽佣。佣金计算的通用做法是:在订单变为“已完成”时,按“实付金额 - 退款金额”的净额乘以佣金比例,写入商户结算流水,同时生成平台收入记录。
// 计算可结算净额 $netAmount = $order['order_amount'] - $order['refund_amount']; $commission = bcmul($netAmount, $config['commission_rate'], 2); $settlementAmount = bcsub($netAmount, $commission, 2);采用两点理由:其一,订单存在退款场景,如果按下单时的总金额算佣金,退款后平台退佣金再重新抽一遍很麻烦,净额法一次算清;其二,bcmath扩展保证金额精确到小数点后两位,避免浮点误差——财务数据出现 0.1 元对不上的情况,多半就是没走高精度计算。使用bcmath前必须确认 PHP 已启用 bcmath 扩展。
4.2 在完成支付回调中加入平台抽佣逻辑
支付回调在likeshop\common\logic\PayNotifyLogic中处理。支付成功后会调用order_pay_success方法。在回调中嵌入佣金计算逻辑,核心思路是把“用户付款成功”与“平台抽佣”解耦——先更新订单为已支付,再异步写结算流水:
// 支付成功回调入口 public function orderPaySuccess($order) { // 1. 更新订单状态为已支付 $order->pay_status = 1; $order->order_status = OrderStatusEnum::WAIT_SERVICE; $order->save(); // 2. 触发佣金结算事件(异步执行) event('Settlement', ['order_sn' => $order->order_sn]); } // 监听器内逻辑 public function handle($data) { $order = Order::where('order_sn', $data['order_sn'])->find(); // 防止重复结算,按 order_sn + 结算日做唯一判断 $exists = MerchantSettlement::where('order_sn', $order->order_sn)->find(); if ($exists) { return; } // 计算佣金并写入结算表 $this->createSettlement($order); }这里必须引入“结算事件”这一层抽耦。如果你直接把抽佣写进支付回调同步执行,万一商户号配置错误导致结算写失败,支付宝或微信的回调会因没有返回成功标识而重复推送,订单状态会被多次覆盖,最终账目混乱。通过消息队列异步处理结算,是物理隔离“支付确认”和“资金计算”两个动作的常见做法。
4.3 账单核对脚本:验证结算是否闭环
写个 PHP 命令行脚本,用来核对一段时间内订单流水和结算流水是否对得上,这是上线前必须走的一步:
// 脚本:核对近 7 天订单与结算流水 $start = date('Y-m-d 00:00:00', strtotime('-7 days')); $end = date('Y-m-d 23:59:59'); // 从订单维度汇总实付金额 $orders = Db::name('hotel_order') ->where('pay_status', 1) ->where('order_status', 4) ->whereBetween('create_time', [$start, $end]) ->field('SUM(order_amount) as amount, COUNT(*) as cnt') ->find(); // 从结算流水维度汇总已结算金额 $settlement = Db::name('merchant_settlement') ->whereBetween('create_time', [$start, $end]) ->field('SUM(settlement_amount) as amount, COUNT(*) as cnt') ->find(); echo "订单总金额: {$orders['amount']}, 结算总金额: {$settlement['amount']}" . PHP_EOL; echo "订单总数: {$orders['cnt']}, 结算流水数: {$settlement['cnt']}" . PHP_EOL;跑完后核对两个数:订单总数是否等于结算流水总数,结算总金额是否等于订单总金额减去平台佣金。这脚本对账的意义在于用两个独立的数据源互相印证,如果订单数和结算数对不上,说明有订单漏结算或重复结算。我曾遇到过一个商户的订单全部漏结算,查了半天发现是中间件监听事件没有注册成功——订单流走完了,但事件消费者没起来,数据就静默丢了。
5. 踩坑与排查记录:上线初期最容易翻车的五个环节
5.1 订单超时未支付,积压却没有自动关闭
现象:用户下单选了服务时间但一直不支付,订单池里大量待支付订单占着服务时段,预约时间到了还在等支付。
原因:系统依赖计划任务来关闭超时订单,但部署时crontab没有挂上 thinkphp6 的定时调度命令,超时关闭逻辑未被触发。这类问题多半是安装时只配置了 web 服务,忽略了计划任务配置。
解决:在 crontab 中增加每分钟执行一次调度器的任务:
* * * * * php /www/wwwroot/likeshop/server/think schedule:run >> /dev/null 2>&15.2 支付回调二次入库,订单状态错乱
现象:用户反馈支付成功后订单还是“待支付”,刷新多次后突然变成“已完成”,且佣金结算出现双份。
原因:支付平台回调重试机制触发,回调日志出现重复调用,而回调处理逻辑没有做幂等校验,同一个transaction_id被处理了两次。
解决:回调处理前先查支付流水表是否存在该transaction_id:
$log = Db::name('pay_log')->where('transaction_id', $transactionId)->find(); if ($log) { return 'SUCCESS'; // 防止重复处理 }核心原则是“回调用transaction_id做唯一键,重复回调直接返回成功,不重复改单”。这也是支付宝和微信官方文档里最强调的一点。
5.3 核销码提示非法,无法完成服务
现象:核销时提示核销码无效,但订单明明是成功状态。通常店家拿手机对用户的核销码扫了几次都失败。
原因:核销码生成时包含了下单用户信息作为校验因子,如果同一订单在小程序端生成和商户端读取的上下文不一致,校验就失败。更深层的原因是部署后缓存了旧的校验规则代码,新代码未生效。
解决:清缓存后重试。项目目录下运行:
php think clear同时核对server/runtime目录下是否有多余的缓存文件,手工删除后重启 PHP-FPM。
5.4 预约时间错乱差 8 小时
现象:用户在前端选的下午 3 点预约,后台看到的排期是早上 7 点,差了 8 小时整。
原因:前端传的时间格式是时间戳,后端存储时转为 datetime 字符串,两者在处理时区上不一致。PHP 默认时区是 UTC,MySQL 默认为系统时区(一般为 Asia/Shanghai),数据入库时被按 UTC 解析。
解决:统一时区。定位config/app.php,确认:
'default_timezone' => 'Asia/Shanghai',MySQL 连接串中保持timezone=+8参数,同时把前端预约时间做成标准Y-m-d H:i:s字符串提交,不在接口层做时间戳转换。
5.5 优惠叠加时佣金金额不对
现象:用户用优惠券加参与满减活动后实付 80 元,平台却按订单原价 100 元抽了佣金,商户实际到账明显减少,引起商户投诉。
原因:佣金计算使用的order_amount是原始商品总价,没有减去优惠金额,而实际可结算金额应以用户真实支付净额为准。
解决:佣金计算一律基于实付金额:
// 实付金额 = 订单总额 - 优惠金额 - 退款金额 $actualPay = $order['order_amount'] - $order['coupon_amount'] - $order['refund_amount']; $commission = bcmul($actualPay, $config['commission_rate'], 2);这条规则必须写进平台结算规则文档,避免后续二开人员沿用旧的order_amount导致财务偏差。
6. 给二开后的订单状态机做一轮自测:脚本化验证结算闭环
6.1 用命令行驱动一条完整订单的生命周期
手工点页面验证二开功能很费时间,测试来回五六次至少半小时。我一般写一段命令行脚本来模拟一条家政订单的完整生命周期,把状态流转、佣金计算、结算写入一次性跑通。脚本直接调用服务层方法,不经过 HTTP 层:
// testSettlement.php // 模拟一条家政订单从创建到结算完成 use think\facade\Db; // 1. 构造测试订单数据 $orderSn = 'TEST' . date('YmdHis'); $orderId = Db::name('hotel_order')->insertGetId([ 'order_sn' => $orderSn, 'order_amount' => 100.00, 'coupon_amount' => 20.00, 'refund_amount' => 0.00, 'pay_status' => 1, 'order_status' => 1, 'create_time' => time(), ]); // 2. 模拟支付成功回调里的佣金计算中间件 $commissionRate = 0.15; // 平台抽佣比例 15% $order = Db::name('hotel_order')->find($orderId); $actualPay = $order['order_amount'] - $order['coupon_amount'] - $order['refund_amount']; $commission = bcmul($actualPay, $commissionRate, 2); $settlementAmount = bcsub($actualPay, $commission, 2); // 3. 写入结算流水 Db::name('merchant_settlement')->insert([ 'order_sn' => $orderSn, 'order_amount' => $actualPay, 'commission_amount' => $commission, 'settlement_amount' => $settlementAmount, 'create_time' => time(), ]); // 4. 验证账目 $total = Db::name('merchant_settlement') ->where('order_sn', $orderSn) ->summary('settlement_amount'); echo "测试订单 {$orderSn} 结算成功,到账金额:{$total}" . PHP_EOL;脚本的用处不止一次性的验证,每次改动佣金公式或结算逻辑,我会把脚本跑一遍,然后人工计算一次预期值做比对。比如上面这个测试用例,订单 100 元、优惠 20 元、佣金 15%,那么实际到账应该是 68 元。脚本输出这个值就是对的,输出别的值说明逻辑有偏差。
6.2 验证结果怎么读
跑完脚本不要只看输出,还要查两条记录:查hotel_order订单状态是否到了“已完成”,查merchant_settlement流水是否只有一条。这样的自测相当于给二开改动买了一份后悔药——因为结算出问题往往不是当下报错,而是月结对账时对不上,那时候再去翻旧账已经晚了。从那以后我每次动完跟订单、金额相关的代码,都会强制走一遍这个脚本,顺便让现场同事也能跑。希望帮到你。
本文还有配套的精品资源,点击获取