ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

家政预约系统二次开发实战:订单状态机与佣金结算核心设计

家政预约系统二次开发实战:订单状态机与佣金结算核心设计 简介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_refundorder_id、refund_sn、refund_amount、refund_status退款申请与处理记录ls_technicianreal_name、mobile、service_scope、status技师档案与技术工种标签ls_merchant_settlementorder_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_itemsJSON 数组。这里有一个关键设计下单时并不直接锁定技师而是等用户付款后进入派单队列。也就是说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 215.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 默认时区是 UTCMySQL 默认为系统时区一般为 Asia/Shanghai数据入库时被按 UTC 解析。解决统一时区。定位config/app.php确认default_timezone Asia/Shanghai,MySQL 连接串中保持timezone8参数同时把前端预约时间做成标准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流水是否只有一条。这样的自测相当于给二开改动买了一份后悔药——因为结算出问题往往不是当下报错而是月结对账时对不上那时候再去翻旧账已经晚了。从那以后我每次动完跟订单、金额相关的代码都会强制走一遍这个脚本顺便让现场同事也能跑。希望帮到你。本文还有配套的精品资源点击获取
返回列表