资讯动态

PHP网约车H5源码解析:乘客端与司机端模块复用与订单状态机设计

发布时间:2026/10/27 12:24:54 来源:尧图企业网站定制
简介这份PHP网约车H5打车系统源码面向想研究在线出行平台架构的开发者与学习者采用PHP后端搭配H5前端完整划分乘客端与司机端两条业务线。乘客端支持目的地输入、费用预估、车型选择、在线支付与常用地址保存司机端涵盖接单、订单详情、导航与个人资料管理并内置消息通知机制便于理解订单状态流转与实时推送的实现思路。资源包共约2000个文件以591个md说明文档、442个js脚本、207个html页面、169个json配置、75个css样式及36个sql建表脚本为主另有少量sh、c、h等文件压缩包约147.04MB目录结构清晰便于按模块检索。目前已有292人学习下载。对于想研究高并发订单处理、数据库存储设计、SSL加密与防SQL注入等安全策略的读者这份源码可作为完整的参考方案帮助快速理解网约车平台从叫车到支付的核心链路。1. PHP网约车H5打车系统源码乘客端与司机端拆开看哪些模块能直接复用打开一份 PHP 网约车 H5 打车系统源码最先要判断的不是代码写得好不好而是乘客端和司机端到底拆成了几个入口、订单状态机跑在哪一侧。这个标题对应的场景很具体一套基于 PHP 的 H5 打车系统前端跑在手机浏览器里后端用 PHP 处理订单、计价、位置上报乘客端负责下单和支付司机端负责接单和行程状态推进。它适合想快速搭一套同城出行 Demo 的开发者也适合需要把派单、计价、状态流转逻辑抽出来复用到其他调度类项目的人。热搜里常出现「网约车app开发」「网约车大数据综合项目」但落到源码层面真正决定能不能跑通的是订单表结构和两端接口的职责划分而不是界面数量。下面按「先看清结构、再动手跑通、最后避坑」的顺序拆开讲。2. 先拆清乘客端与司机端的接口边界订单状态机是核心2.1 两端共用的订单表与状态字段怎么设计一份能跑的 PHP 网约车 H5 源码数据库里通常有一张order表有的写成orders或wx_order乘客端和司机端都读写它。判断源码是否可复用先看这张表的状态字段。常见做法是用一个status整型字段表示订单生命周期取值大致是0 待接单、1 已接单、2 司机已到达、3 行程中、4 已完成、5 已取消。乘客端只负责把 0 推进到 1 之前的下单动作以及 4 之后的支付和评价司机端负责 1 到 4 之间的状态推进。两端接口如果都直接改status就会出现乘客端误改行程中状态的问题。-- 订单表核心字段常见命名按源码实际调整 CREATE TABLE order ( id int NOT NULL AUTO_INCREMENT, passenger_id int NOT NULL COMMENT 乘客用户ID, driver_id int DEFAULT NULL COMMENT 接单司机ID未接单为NULL, start_addr varchar(255) NOT NULL COMMENT 上车点, end_addr varchar(255) NOT NULL COMMENT 目的地, start_lat decimal(10,6) DEFAULT NULL, start_lng decimal(10,6) DEFAULT NULL, status tinyint NOT NULL DEFAULT 0 COMMENT 0待接单 1已接单 2已到达 3行程中 4已完成 5已取消, amount decimal(10,2) DEFAULT NULL COMMENT 订单金额, create_time int NOT NULL COMMENT 下单时间戳, accept_time int DEFAULT NULL COMMENT 接单时间戳, PRIMARY KEY (id), KEY idx_driver_status (driver_id,status), KEY idx_passenger (passenger_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段建表语句里idx_driver_status联合索引是给司机端「查我的进行中订单」用的idx_passenger给乘客端「查我的历史订单」用。如果源码里没有这两个索引订单量一上来司机端列表就会明显变慢。status用 tinyint 而不是字符串是为了让状态判断在 PHP 里可以直接用整数比较减少类型转换。accept_time单独存时间戳而不是靠status变化时间推算是因为派单超时逻辑需要精确到秒。2.2 乘客端接口该做什么、不该做什么乘客端在 H5 里通常只有四个核心接口下单、查订单详情、取消订单、支付回调。下单接口接收上车点、目的地、经纬度写入order表并返回订单号查详情接口按passenger_id和订单号返回状态取消订单只允许在status0时执行支付回调由支付渠道异步通知改status为已完成并写支付流水。乘客端不应该出现「修改司机位置」「推进行程状态」这类接口如果源码里有说明两端边界没拆干净后期司机端逻辑一改就会互相影响。// 乘客端取消订单只允许待接单状态取消 public function cancelOrder($passengerId, $orderId) { $order Db::name(order) -where([id $orderId, passenger_id $passengerId]) -find(); if (!$order) { return [code 404, msg 订单不存在]; } // 关键判断只有 status0 才允许乘客取消 if ($order[status] ! 0) { return [code 400, msg 司机已接单请与司机协商]; } Db::name(order)-where(id, $orderId)-update([ status 5, cancel_time time(), ]); return [code 0, msg 已取消]; }这段代码的逻辑说明先按订单号和乘客 ID 双条件查询防止越权取消别人的订单再判断status只有 0 才允许取消。参数说明$passengerId从登录态取不能从请求参数取否则可以伪造$orderId需要做整型过滤。失败时看返回码404 是订单不属于当前乘客400 是状态不允许。常见误用是直接update不查状态导致司机已接单的订单被乘客单方面取消司机端还在跑行程。2.3 司机端接口的接单与状态推进司机端接口比乘客端多一层「抢单」竞争。接单接口要在高并发下保证一个订单只被一个司机接到常见做法是用update ... where status0的受影响行数判断而不是先查再改。行程推进接口按status逐级校验比如从 1 到 2 必须司机已到达从 2 到 3 必须乘客已上车。位置上报接口单独走一张driver_location表或缓存不要频繁写order表。// 司机接单用受影响行数保证原子性 public function acceptOrder($driverId, $orderId) { $affected Db::name(order) -where([id $orderId, status 0]) -update([ driver_id $driverId, status 1, accept_time time(), ]); if ($affected 0) { return [code 409, msg 手慢了订单已被接走]; } return [code 0, msg 接单成功]; }逻辑说明where里带status0只有仍处于待接单的订单才会被更新update返回受影响行数0 表示没抢到。参数说明$driverId从司机登录态取$orderId做整型过滤。失败时看 409说明订单已被其他司机接走或已取消。这个写法比「先 select 再 update」可靠后者在并发下两个司机可能都查到 status0然后都执行更新。3. 把源码在本地跑起来PHP 环境、数据库与 H5 入口配置3.1 PHP 版本与扩展检查这类源码多数基于 ThinkPHP 或 Laravel 的早期版本PHP 版本选 7.4 或 8.0 比较稳。PHP 8.1 以上对部分老框架的动态属性有兼容问题容易出现Deprecated警告甚至报错。需要确认的扩展pdo_mysql、mbstring、curl、json、openssl。如果源码里有地图逆地理编码还会用到bcmath做距离计算。检查命令如下。php -v php -m | grep -E pdo_mysql|mbstring|curl|openssl|bcmath逻辑说明第一条看版本第二条过滤出关键扩展。如果pdo_mysql没出现数据库连接会直接失败。参数说明grep -E后面是扩展名列表按源码实际依赖增减。失败时看 PHP 错误日志常见的是could not find driver说明 pdo_mysql 没装。3.2 数据库导入与配置文件修改源码包里一般有install.sql或database.sql先建库再导入。配置文件通常在config/database.php或.env。需要改的是主机、库名、用户名、密码、端口。导入前确认字符集是 utf8mb4否则 emoji 地址会乱码。mysql -u root -p -e CREATE DATABASE ride_hailing DEFAULT CHARSET utf8mb4; mysql -u root -p ride_hailing install.sql逻辑说明第一条建库并指定字符集第二条把 SQL 文件导入。参数说明ride_hailing是库名按源码配置改install.sql是源码里的 SQL 文件名不同包可能叫db.sql。失败时看报错行号常见的是外键依赖顺序问题需要先导入基础表再导入关联表。3.3 H5 入口与跨域配置H5 前端一般放在public/h5或public/static下用浏览器直接访问http://localhost/h5/index.html。如果前端请求后端接口报跨域需要在 PHP 入口加响应头或在 Nginx 里配add_header。热搜里「php跨域jsonp」提到的 jsonp 是老方案现在更推荐在 PHP 入口统一加 CORS 头。// 在公共入口文件顶部加跨域头 header(Access-Control-Allow-Origin: *); header(Access-Control-Allow-Methods: GET, POST, OPTIONS); header(Access-Control-Allow-Headers: Content-Type, Authorization); if ($_SERVER[REQUEST_METHOD] OPTIONS) { exit; }逻辑说明前三行允许跨域方法和头第四行处理预检请求直接返回。参数说明*在生产环境应改成具体域名避免任意站点调用。失败时看浏览器控制台如果 OPTIONS 请求返回 404说明入口没拦截预检。4. 计价与派单逻辑源码里最容易和实际业务脱节的两块4.1 计价规则表与里程费计算计价通常拆成起步价、里程费、时长费、夜间费。源码里常见一张price_rule表按城市或车型配置。计算时用两点经纬度算直线距离再乘系数或者调地图 API 算驾车距离。直线距离实现简单但和实际费用偏差大适合 Demo要接近真实得用地图路径规划接口。// 直线距离计价Demo 可用生产建议换地图API function calcDistance($lat1, $lng1, $lat2, $lng2) { $earthRadius 6371; // 地球半径 km $dLat deg2rad($lat2 - $lat1); $dLng deg2rad($lng2 - $lng1); $a sin($dLat / 2) * sin($dLat / 2) cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng / 2) * sin($dLng / 2); $c 2 * atan2(sqrt($a), sqrt(1 - $a)); return $earthRadius * $c; }逻辑说明这是 Haversine 公式返回公里数。参数说明四个参数是起终点纬经度单位是度。失败时看返回值是否为 0可能是经纬度传反了。这个结果乘 1.3 到 1.5 的系数可以粗略模拟道路距离但只适合演示。4.2 派单是推还是拉源码里的两种实现派单有两种常见实现推模式是乘客下单后服务端主动找附近司机推送拉模式是司机端轮询待接单列表。源码里拉模式居多实现简单司机端定时请求status0的订单列表。推模式需要长连接或消息队列复杂度高。判断源码用哪种看司机端有没有定时器以及后端有没有push相关接口。// 司机端拉模式轮询H5 常见写法 setInterval(function () { fetch(/api/driver/pendingOrders?lat lat lng lng) .then(res res.json()) .then(data { if (data.code 0 data.list.length 0) { renderOrderList(data.list); } }); }, 5000); // 5秒轮询一次逻辑说明每 5 秒请求一次待接单列表有数据就渲染。参数说明lat、lng是司机当前位置后端按距离排序。失败时看网络面板如果请求堆积说明上一次没返回就发了下一次需要加锁或改长轮询。轮询间隔太短会增加服务器压力太长司机接单体验差5 到 10 秒是常见折中。5. 避坑与排查PHP 网约车 H5 源码落地时的 5 个血泪教训5.1 订单状态被两端同时修改现象乘客端显示已完成司机端还显示行程中。原因两端各自 updatestatus没有统一的状态机校验。解决把状态推进收敛到一个服务层所有改状态的入口都走同一个方法方法内校验当前状态是否允许目标状态。5.2 司机位置上报把数据库写满现象order表或driver表体积暴涨查询变慢。原因位置上报直接 update 主表每秒一次。解决位置单独存 Redis 或driver_location表只保留最新一条历史轨迹按需落盘。主表只存接单时的位置快照。5.3 PHP 8 下老框架报动态属性警告现象页面顶部大量Deprecated: Creation of dynamic property。原因源码基于 PHP 7 写法PHP 8.2 收紧了动态属性。解决降级到 PHP 7.4 或 8.0或在模型类里显式声明属性。不要直接关错误报告会掩盖真正的问题。5.4 支付回调没做验签导致订单被伪造完成现象没有支付却显示已完成。原因回调接口只接收订单号就改状态没验证签名和金额。解决回调里校验渠道签名、订单金额、订单状态三者一致才改status4并且做幂等同一笔回调重复到达只处理一次。5.5 H5 在部分手机浏览器定位失败现象乘客端拿不到经纬度下单报错。原因H5 定位需要 HTTPS 或 localhostHTTP 下部分浏览器直接拒绝。解决本地开发用 localhost线上必须配 HTTPS。同时加降级方案定位失败时允许手动输入地址。6. 进阶把派单从轮询改成基于地理位置的筛选并验证效果轮询跑通之后下一步通常是让派单更准。拉模式默认按时间倒序返回待接单列表司机看到的单可能离自己很远。改进做法是在order表存上车点经纬度司机端请求时带上自己的经纬度后端用边界框先粗筛再精算距离。边界框的思路是先按纬度差和经度差圈出一个矩形范围用索引快速过滤再对少量结果算精确距离。-- 边界框粗筛1公里约等于纬度0.009度经度按cos(纬度)修正 SELECT id, start_addr, start_lat, start_lng, (6371 * ACOS(COS(RADIANS(?)) * COS(RADIANS(start_lat)) * COS(RADIANS(start_lng) - RADIANS(?)) SIN(RADIANS(?)) * SIN(RADIANS(start_lat)))) AS distance FROM order WHERE status 0 AND start_lat BETWEEN ? - 0.009 AND ? 0.009 AND start_lng BETWEEN ? - 0.012 AND ? 0.012 HAVING distance 1 ORDER BY distance ASC LIMIT 20;逻辑说明WHERE里的 BETWEEN 用索引快速缩小范围HAVING再算精确距离过滤。参数说明四个?分别是司机纬度、司机经度、司机纬度、司机纬度后面几个是边界框的上下界。经度差用 0.012 是因为在中纬度地区经度一度对应的距离比纬度短。失败时看EXPLAIN如果没走idx_driver_status或没有经纬度索引需要补索引。验证方法造 100 条不同位置的待接单订单司机端分别用轮询和边界框筛选请求对比返回列表里最近订单的距离和响应时间。我一般会看两个指标最近订单距离是否小于 1 公里以及接口 P99 是否在 200ms 以内。如果距离还是很大检查经纬度是不是存成了字符串导致计算失效如果响应慢看是不是HAVING前的结果集太大需要缩小边界框。这套源码值不值得投入取决于你是要快速演示还是要做真实调度。演示的话把订单状态机和两端接口边界理清就能跑要做真实业务计价和派单这两块必须重写位置存储和支付验签也不能省。我自己踩过最深的坑是早期图省事让两端直接改状态后来加一个「司机已到达」状态就改了三个地方漏了一处导致乘客端显示错乱。从那以后凡是状态流转我都先画状态图再写代码。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑