简介本资源是一套基于Java开发的网约车平台完整设计源码面向Java后端开发者、毕业设计学生及微服务架构学习者旨在解决网约车系统核心业务模块如用户管理、订单调度、司机接单、实时定位与支付对接的工程化实现问题。压缩包共447个文件包含309个Java业务逻辑与实体类文件、49个XML配置与SQL映射文件、14个YAML微服务配置、14个CMD启动脚本及14个Maven Wrapper脚本整体体积仅1.94MB轻量但结构完整便于快速导入IDE运行调试。已有616人下载学习适合用于课程设计、毕设参考或Spring BootMyBatis技术栈的实战演练。源码采用模块化分层设计涵盖用户端、司机端与管理后台三端交互逻辑附带基础数据库脚本3个.db文件与可执行构建环境mvnw系列脚本开箱即用显著降低网约车系统原型开发门槛。 这两年Java面试题里网约车平台几乎是出现频率最高的实战项目之一。究其原因它表面是打车实际却把高并发、实时通信、分布式事务、LBS检索、状态机设计这些后端硬骨头全占了。一个基于Java的网约车平台设计源码如果只是把CRUD拼一拼跑起来也就是个演示Demo离真正能用的系统差着十万八千里。这篇文章我不讲虚的直接把整个项目的核心业务模型、技术选型逻辑、源码模块拆解和调试运行中会踩的坑全部摊开给想做此类项目或者正在准备的各位一个可复用的设计参考。我按自己实际做过的一套网约车平台源码来梳理。这套项目以Spring Cloud Alibaba为微服务底座覆盖乘客端、司机端、管理后台三条业务线核心模块包括订单服务、司机服务、计价服务、支付服务、位置服务、消息推送服务六大块。你可以用它做课程设计、面试项目、或者作为商用系统的起点。下面这些内容全部来自我实际开发、调试、压测过程中的记录不是从文档里抄来的概念。1. 网约车平台到底在做什么核心业务与系统边界1.1 表面是打车实际上是一整套状态流转机器很多人第一次接触网约车项目以为核心就是用户发单、司机接单这么简单。真把需求拆开看你会发现整个业务链远比想象复杂乘客呼叫前要匹配车型和计价规则发单后要经过待接单、已接单、司机到达、行程中、待支付、已完成这一整套状态机司机端要处理接单、拒单、开始服务、结束服务、账单确认平台后台还要管司机入驻审核、车辆信息、行程审计、异常订单申诉。这些环节单独看都不难难的是它们之间通过事件驱动互相耦合。一个订单状态变更是简单的update语句但一个订单状态变更同时要触发司机推送、乘客通知、计价服务启动计费、支付服务冻结预付款、位置服务开始轨迹记录这就不简单了。我在设计源码时把订单状态变更做成了事件总线模式每个服务发布领域事件其他服务通过MQ订阅而不是互相直接Feign调用。这个决策在后来的压测里被证明是必要的单体服务直接调用在高峰期追不上状态变更的吞吐量。1.2 乘客端、司机端、管理后台三个入口决定模块划分网约车项目的系统边界可以从入口来划分这一点决定了源码的包结构和服务边界。乘客端最核心的诉求是快速打到车、价格透明、支付简单所以乘客服务要处理的业务包括注册登录、常用地址管理、一键叫车、费用预估、行程评价、投诉申诉。司机端则完全不同司机在意的除了接单量还有路线规划、实时导航、收益统计、提现操作所以司机服务要单独拆涉及司机入驻、接单状态管理、行程开始结束、账单汇总、钱包提现。管理后台是最容易被忽略但工作量最大的一块它需要做司机资质审核、车辆信息管理、订单监控、计价规则配置、优惠券发放、风控标记。我在项目源码里把这三个端对应的服务和数据表都做了物理隔离绝不混用一个用户表。乘客和司机虽然都是人但属性差异太大乘客关注常用地址和支付方式司机关注车辆信息和接单偏好硬合一张表后期改字段会非常痛苦。1.3 与普通电商项目的本质差异很多有电商经验的人上手网约车项目会在两个地方栽跟头一个是把订单当电商订单处理另一个是把司机当普通商品库存。电商订单的核心特征是用户主动发起、状态可以逆向流转退款退货、物流信息由外部系统驱动。网约车订单的核心特征是平台主动匹配供需、服务过程强实时、司机和乘客的位置信息贯穿全程。在电商里你下单后商品不会跑掉在网约车里乘客发单后司机可能正在路上堵着也可能接单后接到乘客电话说换地方上车。所以网约车订单的状态机必须支持正在前往这个中间态必须有倒计时自动取消机制必须处理司机到达后乘客未上车的超时费用。我建议在设计数据库和状态机之前先把业务时序图画清楚理清各个角色在每个时间点能做什么操作。这一步做扎实了后面写代码会顺利很多不然就会出现乘客取消订单后司机还在计费这种低级却致命的Bug。2. 核心业务域设计订单状态机、计价引擎与派单策略2.1 订单状态机从呼叫到支付的生命周期订单状态机是整个网约车平台的心脏状态设计的好坏直接决定业务逻辑的清晰度。我使用的状态定义是BOOKING待接单、ARRIVING司机前往接驾、ARRIVED司机已到达、ON_TRIP行程中、WAIT_PAY待支付、COMPLETED已完成、CANCELLED已取消。七个状态每种状态转换都要校验合法性。Java代码里我用枚举加状态流转表来实现核心代码如下public enum OrderStatus { BOOKING(0, 待接单), ARRIVING(1, 司机前往), ARRIVED(2, 司机已到达), ON_TRIP(3, 行程中), WAIT_PAY(4, 待支付), COMPLETED(5, 已完成), CANCELLED(6, 已取消); private final int code; private final String desc; // 合法的状态流转表当前状态 - 事件 - 目标状态 private static final MapOrderStatus, MapOrderEvent, OrderStatus TRANSITIONS new EnumMap(OrderStatus.class); static { MapOrderEvent, OrderStatus bookingMap new EnumMap(OrderEvent.class); bookingMap.put(OrderEvent.DRIVER_ACCEPT, ARRIVING); bookingMap.put(OrderEvent.USER_CANCEL, CANCELLED); bookingMap.put(OrderEvent.TIMEOUT_CANCEL, CANCELLED); TRANSITIONS.put(BOOKING, bookingMap); MapOrderEvent, OrderStatus arrivingMap new EnumMap(OrderEvent.class); arrivingMap.put(OrderEvent.DRIVER_ARRIVED, ARRIVED); arrivingMap.put(OrderEvent.USER_CANCEL, CANCELLED); TRANSITIONS.put(ARRIVING, arrivingMap); // 其余状态转换略核心限制是每个事件都显式声明 } public static boolean canTransit(OrderStatus from, OrderEvent event) { MapOrderEvent, OrderStatus map TRANSITIONS.get(from); return map ! null map.containsKey(event); } }这个设计的核心价值在于把状态校验集中在一个类里任何调用方想做非法转换都会直接失败不用在service层到处写if判断。我接手过的一些项目里订单状态判断散落在十几个方法中每次改动都如履薄冰那种痛苦经历过的人都懂。建议在订单表里加一个version乐观锁字段状态更新走CAS防止并发双重操作把状态搞乱。2.2 计价引擎一个独立服务而不是订单服务的一个方法计价逻辑是网约车平台里被严重低估复杂度的模块。我见过很多演示项目把计价写成订单服务里的一个方法看似省事实际上把自己坑死了。计价规则的种类太多了实时订单按里程加时长计费预约订单有预约服务费跨城订单有返程费还有夜间服务费、雨天溢价、高峰期动态调价、优惠券抵扣。把计价拆成独立服务最大的收益是规则变更可以独立上线不影响订单主流程。我使用的计价模型是public class PriceCalculator { // 基础计费起步价 里程费 时长费 public Fare calculate(CityRule rule, TripInfo trip) { BigDecimal baseFare rule.getBaseFare(); BigDecimal distanceFare trip.getDistanceKm().multiply(rule.getPerKmPrice()); BigDecimal timeFare trip.getDurationMin().multiply(rule.getPerMinutePrice()); BigDecimal subtotal baseFare.add(distanceFare).add(timeFare); // 动态调价系数高峰期或特殊天气生效 if (trip.isPeakTime()) { subtotal subtotal.multiply(rule.getPeakFactor()); } // 夜间服务费 if (trip.isNightTime()) { subtotal subtotal.add(rule.getNightSurcharge()); } // 优惠券抵扣必须小于订单金额 BigDecimal discount trip.getDiscount().min(subtotal); return Fare.builder() .subtotal(subtotal) .discount(discount) .payable(subtotal.subtract(discount)) .build(); } }计费结果要用BigDecimal计算绝不用double这是基本的金融安全底线。每次行程结束生成费用后还要生成一张费用明细表存下来包含基础费、里程费、时长费、附加费、优惠券明细这样乘客申诉、对账审计都能快速查到账目来源。2.3 派单策略就近派单与抢单模式的取舍派单策略是网约车平台最有技术含量的部分之一。在小规模项目里最简单可靠的是抢单模式乘客发单后平台把订单推送给附近的司机司机手动抢单先抢到先得。抢单模式的好处是调度逻辑简单不需要复杂的运筹优化算法坏处是可能造成远单司机抢到近单乘客等待体验差。我在这套源码中实现的是基于Redis GEO的主动派单策略乘客发单后从Redis GEO中查询订单起点3公里范围内的空闲司机按距离和评分加权打分后依次推送订单若第一个司机15秒内未响应自动流转给下一个候选司机。这个方案在中小数据量下性能非常好GEO查询是毫秒级的。核心逻辑public ListDriverLocation findNearbyDrivers(double lon, double lat, double radiusKm) { // Redis GEO 查询半径内司机位置 GeoResultsRedisGeoCommands.GeoLocationString results redisTemplate.opsForGeo() .radius(DRIVER_GEO_KEY, new Point(lon, lat), new Distance(radiusKm, RedisGeoCommands.DistanceUnit.KILOMETERS)); // 再过滤掉已处于订单状态的司机 return results.getContent().stream() .map(result - driverLocationService.getDriver(result.getContent().getName())) .filter(DriverLocation::isIdle) .sorted(Comparator.comparing(DriverLocation::getScore).reversed()) .collect(Collectors.toList()); }注意单纯的距离最近并不等于最优派单一个司机距离近但如果正在完成上一单的收尾响应也不及时。所以我在候选司机打分时要综合考虑距离、完单率、服务分、是否顺路。这个打分规则可以在管理后台配置派单引擎只负责执行规则这样运营同学可以根据城市运力情况灵活调整。3. 架构选型与数据库设计这套技术栈为什么能撑住业务3.1 服务拆分粒度不是越细越好网约车平台的服务拆分要找一个平衡点。拆分过细比如把一个订单服务拆成订单创建服务、订单状态服务、订单查询服务开发调试和部署的成本会成倍上升拆分过粗比如把订单、计价、支付合成一个大单体后续任何模块的改动都互相牵制。我使用的拆分方案是按业务域划分为六个服务用户服务管理乘客端账号、常用地址、优惠券司机服务管理司机入驻、车辆信息、接单状态、钱包提现订单服务管理订单生命周期和派单流程计价服务管理计价规则和费用计算支付服务对接支付渠道、处理账户流水和退款位置服务接收司机位置上报、存储轨迹、提供LBS检索。各服务之间通过OpenFeign做同步查询通过RocketMQ做异步通知。同步与异步的使用原则是需要即时返回结果的用Feign不需要调用方等待结果的用MQ。比如乘客发单后必须同步知道有没有司机接单所以发单后的派单尝试是同步的但支付完成的后续动作比如发送电子发票、更新司机收益汇总、发送优惠券这些完全可以用MQ异步处理。3.2 关键选型每个组件解决什么问题技术栈的选择不追求最潮追求的是在问题场景下最稳、生态最成熟的组合。场景选型理由微服务框架Spring Cloud Alibaba服务注册发现、配置中心、限流降级一站式解决社区活跃业务数据库MySQL 8.0订单、用户等核心数据强一致要求关系型数据库最合适缓存与LBSRedis 6.0缓存热数据之外GEO命令天然适合附近的司机检索消息队列RocketMQ事务消息能力适合订单与支付的对账场景延迟消息适合订单超时取消实时推送Netty WebSocket兼顾高并发长连接和消息实时性司机端位置推送用这个分布式事务Seata AT模式订单创建和预支付扣款等强一致场景用其他用最终一致性定时调度XXL-Job订单超时扫描、司机每日账单汇总、异常订单巡检有不少人问我为什么不用Kafka而用RocketMQ。Kafka的核心优势在超高吞吐量的日志类数据但网约车订单支付场景需要可靠的事务消息和精确的延迟消息RocketMQ在这两点上更顺手。技术选型要结合业务特征别人用的明星组件放到你的场景里可能就是个负累。3.3 核心数据表的设计表结构本身就是业务逻辑的映射数据库表结构是网约车项目中最见功力的一层设计得好不好后面写SQL时体验天差地别。订单主表是整个系统的核心我设计的字段包括order_id、order_no业务订单号对外展示用、user_id、driver_id、car_type、start_longitude、start_latitude、end_longitude、end_latitude、start_address、end_address、distance_km、duration_min、status、fare_amount、pay_status、create_time、accept_time、arrive_time、start_time、finish_time。所有时间字段都要建因为每个时间点都是后续统计司机效率、乘客等待时长的依据。订单号一定单独设置一个业务订单号字段用雪花算法生成不要用数据库自增主键对外暴露。司机位置表这里有个设计细节值得单独说不要每上报一次位置就update一次数据库那会形成巨大的写压力。我的做法是实时位置放入Redis GEO3秒一条写入消息队列位置服务消费后批量写入轨迹表数据库只保留最近的轨迹点用于审计追溯。真正的附近司机检索永远查Redis不查MySQL。计价规则表本质上是一张JSON配置表city_code、car_type、start_time、end_time、base_fare、per_km_price、per_min_price、night_surcharge、peak_factor。把规则做成可配置而不是写死在代码里这是计价服务能够独立上线的关键。4. 源码结构拆解一单完整流程的代码追踪4.1 多模块Maven工程结构拿到源码第一件事是理解工程结构。我用的是Maven多模块项目父POM统一管理依赖版本子模块各司其职ride-platform/ ├── ride-common/ // 通用工具、公共返回、异常处理 ├── ride-gateway/ // 网关服务 ├── ride-modules/ │ ├── user-service/ // 用户服务 │ ├── driver-service/ // 司机服务 │ ├── order-service/ // 订单服务 │ ├── price-service/ // 计价服务 │ ├── payment-service/ // 支付服务 │ └── location-service/ // 位置服务这种结构的最大好处是模块边界清晰编译和打包都可以按模块独立进行。如果只想部署订单服务只需要单独打出order-service的jar包即可不用把整个项目重新打包。面试时如果被问到项目结构这个设计能展示出你对工程化的理解。4.2 核心服务的关键类地图源码里每个服务都遵循标准的Controller-Service-Mapper三层结构但关键业务类值得压箱底去读订单服务里最核心的是OrderServiceImpl它负责创建订单、触发派单、接收司机操作、处理取消。这个类里我严格控制事务边界状态变更和派单动作必须在同一个本地事务内因为状态从BOOKING变到ARRIVING的同时必须马上锁定司机否则就会出现两个订单派给同一个司机的情况。计价服务里最核心的是PriceCalculator和PriceRuleService前者是纯计算逻辑没有状态可以独立单元测试后者负责从数据库加载规则并做缓存避免每次计费都查库。位置服务里最关键的是LocationConsumer和GeoService前者消费司机位置消息批量写入轨迹后者封装了Redis GEO的添加、查询、删除操作。消息推送服务里是WebSocketServer维护了每个司机连接对应的Channel支持按driverId精准推送。4.3 一个订单从发起到完成代码是这么走的我建议拿到源码后按下面的链路去读一遍完整流程这比零散看代码高效得多乘客在乘客端点击一键叫车请求先走网关网关做token校验后路由到订单服务的createOrder接口。OrderServiceImpl创建订单状态为BOOKING同时调用司机服务的Feign接口获取附近候选司机这里的位置来源是Redis GEO。拿到候选司机列表后将订单消息推送到消息服务向候选司机逐个推送订单。司机端收到推送弹窗点击接单司机服务接收请求后调用订单服务的acceptOrder接口此时数据库做一次乐观锁更新状态从BOOKING变成ARRIVING订单服务发送MQ消息通知乘客端司机已接单。司机到达上车点后点击开始行程状态从ARRIVED变成ON_TRIP。行程中位置服务持续接收司机端上报的GPS坐标轨迹表里累加轨迹点行程结束后计算总里程和总时长。司机点击结束行程后订单服务调用计价服务的calculate接口生成费用订单状态变为WAIT_PAY。乘客支付成功后支付服务回调订单服务状态变为COMPLETED同时MQ广播支付成功事件用户服务更新用户消费记录司机服务更新司机收益。读源码时你会发现一个特点所有状态的变更都只由一个服务发起其他服务通过事件感知。这是微服务架构里保持数据一致性的核心原则如果每个服务都能改订单状态那么这个系统的状态迟早会乱。5. 真正调通整个系统你会在这几个地方卡住5.1 地图API的坐标体系与编码问题这是绝大多数人会在第一步就踩的坑。高德地图用的是GCJ-02坐标系百度地图用的是BD-09坐标系而GPS原始坐标是WGS-84。三者的经纬度差异在城市范围内可能达到几百米如果乘客端用高德传入坐标司机端用百度地图导航两边的位置对不上司机找不到乘客那是必然的。解决方案是在位置服务里统一做坐标转换乘客端、司机端在请求头带上来源标记网关层统一把坐标转换为项目内部标准坐标系下游服务只认标准坐标。这样调度、计价、轨迹存储用的都是同一套坐标体系展示层再按需转换。我建议源码里内置高德坐标系和百度坐标系的互转工具类实测转换误差控制在10米以内。另外要注意逆地理编码的调用量。一个订单从发起到结束至少需要3次以上的逆地理编码来获取起终点地址如果完全依赖高德API每天上百万订单的调用量费用非常可观。我的做法是建立本地地址缓存短时间内的重复坐标查询直接命中缓存。5.2 司机位置上报的实时性频率、批量、削峰司机端位置上报是典型的高频写场景。假设一个城市有1万名在线司机每3秒上报一次每秒就有约3300条位置消息进入系统这只是单城市单车型的量。如果把位置直接写入关系型数据库数据库扛不住。我的实际做法是司机端按3秒间隔批量上报一次上报携带10个左右的轨迹点位置服务接收后先把最新位置更新到Redis GEO用于实时派单检索同时把轨迹数据批量写入消息队列由异步消费者攒批后批量插入轨迹表。这里攒批插入是关键优化——用MyBatis的批量insert一次插入几十条记录比逐条插入性能提升一个量级。还有一个细节是位置上报的时效性校验。如果某条位置消息的时间戳比当前时间早太多说明是网络延迟后补发的直接丢弃否则会污染实时位置数据。5.3 并发场景下的重复派单与超卖问题网约车场景下有两个并发问题特别容易踩第一个是乘客重复发单第二个是同一司机被多单派中。乘客端连续点击一键叫车如果后端没有做防重就会创建多个订单乘客可能要同时为多笔订单付费。我的解决方案是在创建订单前先查询Redis中该用户的进行中订单标记存在则直接返回当前订单同时在订单表上建(user_id, status)的联合唯一索引兜底数据库层面拦截重复未完成订单。司机被多单派中的问题本质上和电商超卖是同构的。我用乐观锁解决派单前先执行update driver set status BUSY where id #{driverId} and status IDLE如果影响行数为0说明司机已经被其他订单占用当前订单自动流转到下一个候选司机。数据库的原子性在这里比任何分布式锁都靠谱实际压测中200并发下重复派单率为0。5.4 分布式事务的取舍强一致和最终一致当订单服务和支付服务在不同服务中乘客发起支付、订单状态更新、司机收益增加这三件事如何保持一致是网约车项目里最考验功底的部分。我的原则是一切以订单状态为锚点尽量缩小强一致事务的范围扩大最终一致的场景。乘客支付时只对订单状态为WAIT_PAY到状态为COMPLETED这步使用Seata的AT模式强一致事务保证不出现资金已扣但订单未完成的情况。而支付成功后的司机收益增加、优惠券核销、发票生成、消息推送等全部通过RocketMQ事务消息异步处理消费失败则通过定时任务扫描对账表做补偿。这里有一个血的教训不要为了省事把所有操作都包进一个大事务。跨服务的大事务会把数据库锁持有时间拉到秒级并发一高系统就雪崩。分布式系统的正确姿势是宁可靠最终一致性不用大事务。6. 从能跑到能上线这中间还缺什么把源码跑起来、能下单、能接单这只是完成了20%的工作。一个真正可以被生产环境使用的网约车平台至少还缺这几块硬功夫第一是全链路监控。线上环境一个订单出问题你能不能通过traceId把从乘客发单到订单完成的所有日志串起来我建议引入SkyWalking做全链路追踪每个接口都埋点日志里必须输出traceId。第二是压测和容量评估。用JMeter模拟高峰期的派单流量找出系统的瓶颈点是数据库连接池不够还是Redis GEO查询太慢还是MQ消费跟不上压测完之后你才知道自己的系统到底能扛多少并发。第三是熔断降级预案。地图API挂了怎么办支付通道超时怎么办消息队列积压怎么办这些在生产环境都是会真实发生的故障源码里至少要有关键接口的降级开关和兜底逻辑。就我个人做这套项目的体验而言网约车平台源码最大的价值不是能跑通而是它把后端开发的几乎所有核心知识点串在了一起。状态机设计锻炼业务抽象能力分布式事务锻炼一致性思维高并发场景锻炼性能优化意识LBS检索锻炼数据结构与中间件的活用能力。做完这个项目你对Java生态、微服务架构、分布式系统的理解会上一个明显的台阶。就算不是要入职网约车行业这套设计思路和排障经验在其他业务系统里一样用得上。本文还有配套的精品资源点击获取