ARTICLE DETAIL

资讯详情

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

Python+微信小程序代驾系统实战:从订单派单到轨迹追踪

Python+微信小程序代驾系统实战:从订单派单到轨迹追踪 先说结论这篇毕设/实战项目核心不只是把代驾系统写出来而是怎么把用户下单、司机接单、费用计算、轨迹追踪这条完整业务链路用 Python 后端和微信小程序前端配合落地。代驾这个品类在所有出行类业务中算是逻辑中等偏简单、但细节格外多的一个没有复杂的拼车调度但实时性和费用准确性要求很高。如果你正在准备毕设答辩或者想做一个能写进简历的完整项目这篇文章会按我的实际开发经验把从需求拆解到联调上线的全过程给你捋一遍包括一些教材里不会写、但真正动手时必踩的坑。1. 代驾系统到底在做什么业务模式与需求边界很多同学拿到代驾系统设计与实现这个题目第一反应是用户下单司机接单完事。但真把需求落到数据库表和接口层面你会发现代驾业务的复杂度和外卖、跑腿完全不同。写代码之前务必先把业务角色的数据权限和状态机梳理清楚。代驾系统涉及三类角色乘客、代驾司机、平台管理员。乘客侧的核心诉求是一键呼叫代驾、查看司机位置、支付费用司机侧的核心诉求是听单、抢单/派单、开始服务、结束服务、提现管理员侧的核心诉求是审核司机资质、处理订单纠纷、查看平台流水。单看这些好像和滴滴顺风车、货拉拉都差不多但代驾有一个非常独特的业务特点订单时效性极强且服务起点是动态的。用户喝多了在餐厅门口下单司机的接单响应必须在几十秒内完成超过时间用户很可能就自己打车走了。这就决定了系统设计上必须优先保证派单实时性和司机在线状态的心跳维护。另外代驾的费用是典型的动态计价不单纯是里程费。常规代驾计费模型是起步价 超里程费 等待费 夜间服务费起步价通常覆盖 10 公里以内不同城市不同超出部分按每公里计费晚上 22:00 到次日 6:00 要加收夜间费如果司机到了之后用户 5 分钟还没上车开始按分钟计等待费。这四类费用规则必须做成可配置的否则每次调价你都要改代码这在毕设答辩里也会被老师重点追问你的计价是否灵活扩展。需求边界也要提前划清楚明确哪些做、哪些不做。比如用户余额支付、优惠券、分享邀请得奖励这类营销功能和核心业务无关建议作为扩展点写在论文里但不实现而司机刷脸认证、实时轨迹上传、订单录音这类功能涉及第三方服务对接和硬件成本毕设阶段用 Mock 或简化方案更合理。别贪大把主链路做扎实比堆砌半成品功能有用得多。2. 技术选型心路为什么是 Python 写后端、微信小程序做前端你题目的限定是Python 微信小程序这本身已经把技术栈框死了但框死并不代表不用选型。Python 后端用什么 Web 框架数据库用 MySQL 还是 PostgreSQL消息推送用 WebSocket 还是轮询这些才是真正的设计空间。2.1 Web 框架选型Django 还是 FlaskPython 后端主流就三个方向Django、Flask、FastAPI。代驾这个项目我最终建议用 Flask 扩展组件或者 Django DRF。理由分两种场景场景一如果目的是快速完成毕设、工期两到三个月选Flask Flask-SQLAlchemy Flask-SocketIO。Flask 灵活你可以在答辩时清楚说出每个模块是自己手动拼起来的老师问起来你能答得出细节。缺点是很多能力要靠扩展包组装目录结构需要自己规范。场景二如果你的项目偏管理系统方向后台页面有不少表格、表单、权限管理选Django会舒服很多。Django 自带 Admin 后台司机审核、订单查询这类管理功能几乎白送。用 DRF 写 API 也很规范。我自己做这类出行项目更偏向 Flask代驾后端本质上是高实时 I/O 场景Flask 的轻量特性配合 Socket.IO 实现实时推送更顺手。Django 的重量级功能在这里有一半用不上。// 一个合理的 Flask 项目目录结构 dadai-server/ ├── app/ │ ├── __init__.py # 应用工厂注册蓝图和扩展 │ ├── models/ # 数据库模型 │ │ ├── user.py # 乘客、司机可共用用户表 角色字段 │ │ ├── order.py # 订单模型 │ │ └── wallet.py # 司机钱包和流水 │ ├── api/ # 蓝图路由 │ │ ├── auth.py # 登录、注册、token 管理 │ │ ├── passenger.py # 乘客端接口 │ │ ├── driver.py # 司机端接口 │ │ └── admin.py # 管理端接口 │ ├── services/ # 业务逻辑层 │ │ ├── dispatch.py # 派单策略 │ │ ├── pricing.py # 计费引擎 │ │ └── push.py # WebSocket 推送封装 │ └── utils/ ├── config.py └── run.py2.2 前后端通信选型REST WebSocket 双通道微信小程序和 Python 后端通信主要有两种通道两个都要用RESTful API 负责常规请求登录、下单、查询订单列表、司机资料审核、金额结算。这类请求的实时性要求不高HTTP JSON 足够。WebSocket 负责实时事件推送新订单广播、司机抢单结果、司机实时位置更新。这是代驾业务和其他管理系统最不一样的地方没有 WebSocket你的系统只能靠轮询体验会差一个档次。可能有同学问小程序能用 WebSocket 吗可以。微信小程序天然支持wx.connectSocketAPI。但要注意小程序在前台时可以维持长连接切到后台超过一定时间连接会被微信主动断掉所以你的小程序端必须做断线重连 应用回到前台时重新建链的逻辑。Python 端实现 WebSocket 我建议用 Flask-SocketIO配合 eventlet 或 gevent 跑异步。选它的原因是和 Flask 生态融合好且自带房间room机制做推送指定司机这种操作非常方便。核心思想是每个司机登录后Socket.IO 连接保存自己的driver_id服务端根据派单算法把司机 ID 对应到 room然后向指定 room 推单。2.3 数据存储方案与 Redis 的角色业务数据存 MySQL 没问题这属于共识不展开。但 Redis 在这个项目里承担一个关键任务司机地理位置和在线状态的实时存储。为什么不用 MySQL 存坐标因为司机端是每隔 5 到 10 秒上报一次 GPS 坐标订单高峰期一天就是几万条坐标记录。如果你直接写 MySQL一段时间后整个订单详情接口都会被拖慢而且找附近 3 公里内在线司机这种查询如果用 MySQL 做经纬度范围扫描表数据量大之后性能会很难看。Redis 在这个项目里有三个场景很合适用GEO系列命令GEOADD、GEORADIUS维护司机坐标天然支持围绕用户经纬度找半径 N 公里内的司机。用 Redis 的 Key 过期机制维护司机在线状态司机心跳上报就EXPIRE续期60 秒没上报就认为掉线。用 Redis 的 List 或 Stream 做待派单队列避免并发抢单时订单被多个司机同时看到、重复接单。在毕设里如果老师问为什么不用 MySQL 存坐标你答MySQL 的经纬度范围检索走不上索引高并发下性能差Redis GEO 底层是跳表读写都是微秒级而且有现成的距离排序能力这个回答基本能拿满分。3. 数据库设计的核心表与关键字段细节数据库设计是代驾系统最容易翻车的地方。很多同学一上来建一张 order 表、一张 user 表就开始写接口后面做到计费、派单时才哭着改表结构。按我的习惯核心表至少要保证下面这些字段维度缺一个后面都难受。3.1 用户表乘客和司机分开还是合一我建议一张用户主表 一张司机扩展表。乘客和司机本质上都是用户共用一套手机号密码登录体系、共用一套微信 OpenID 绑定逻辑。但司机有额外的资质信息驾驶证照片、从业资格证编号、车牌号、车辆品牌型号、服务城市、接单状态、评分、接单总量。这样做的好处是登录鉴权逻辑只需写一遍订单表里的外键也统一指向 user 表 id后续做用户画像、平台整体数据统计时可以直接在主表上聚合不用 JOIN 两张用户表。// 用户表关键字段简化 CREATE TABLE user ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, phone VARCHAR(20) UNIQUE NOT NULL COMMENT 手机号登录唯一凭证, password_hash VARCHAR(255) NOT NULL COMMENT 密码哈希, nickname VARCHAR(50), avatar_url VARCHAR(255), role TINYINT NOT NULL DEFAULT 0 COMMENT 0-乘客 1-司机 2-管理员, wx_openid VARCHAR(64) DEFAULT NULL, status TINYINT DEFAULT 1 COMMENT 账号状态 1-正常 0-禁用, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); // 司机扩展表关键字段 CREATE TABLE driver_profile ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL COMMENT 关联用户主表, real_name VARCHAR(20) NOT NULL COMMENT 真实姓名, id_card_no VARCHAR(30) NOT NULL COMMENT 身份证号, driver_license_no VARCHAR(30) NOT NULL COMMENT 驾驶证号, license_first_get_date DATE COMMENT 初次领证日期, car_brand VARCHAR(30), car_plate VARCHAR(10), car_color VARCHAR(10), service_city VARCHAR(50) COMMENT 服务城市用于派单过滤, auth_status TINYINT DEFAULT 0 COMMENT 0-待审核 1-通过 2-拒绝, online_status TINYINT DEFAULT 0 COMMENT 0-离线 1-空闲 2-服务中, rating DECIMAL(3,2) DEFAULT 5.00 COMMENT 综合评分, total_orders INT DEFAULT 0 COMMENT 累计完单数, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );3.2 订单表状态机设计必须前置订单表是整张数据库设计的灵魂。除了用户、司机、起终点经纬度、地址文本、价格这些常规字段重点设计订单状态字段。代驾订单我建议划分为以下状态流转链PENDING(待接单) → ACCEPTED(已接单/司机赶往) → PICKED_UP(已上车/服务进行中) → COMPLETED(已完成/待支付) → PAID(已支付) → CLOSED(已关闭)异常分支也要提前准备用户取消、司机取消、订单超时未接单自动取消、支付超时自动关闭。每种分支对应一个 status 值或一个 cancel_type 字段。// 订单表关键字段核心部分 CREATE TABLE order ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) UNIQUE NOT NULL COMMENT 业务订单号规则如 yyyyMMddHHmmss随机数, passenger_id BIGINT UNSIGNED NOT NULL, driver_id BIGINT UNSIGNED DEFAULT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 见状态流转定义, pickup_lat DECIMAL(10,6) NOT NULL COMMENT 起点纬度, pickup_lng DECIMAL(10,6) NOT NULL COMMENT 起点经度, pickup_address VARCHAR(255) NOT NULL, dest_lat DECIMAL(10,6), dest_lng DECIMAL(10,6), dest_address VARCHAR(255), estimate_amount DECIMAL(10,2) COMMENT 预估价格, real_amount DECIMAL(10,2) COMMENT 实际价格, start_distance_km DECIMAL(5,2) COMMENT 司机到起点距离, service_distance_km DECIMAL(5,2) COMMENT 实际服务里程, service_duration_min INT COMMENT 服务时长, wait_duration_min INT COMMENT 等待时长, night_fee DECIMAL(8,2) COMMENT 夜间服务费, start_time DATETIME COMMENT 司机出发时间, pickup_time DATETIME COMMENT 用户上车时间, end_time DATETIME COMMENT 服务结束时间, pay_type TINYINT COMMENT 1-微信支付 2-余额, pay_time DATETIME, cancel_reason VARCHAR(255), created_at DATETIME DEFAULT CURRENT_TIMESTAMP );这里特别注意一个细节金额字段必须用 DECIMAL不能 FLOAT/DOUBLE。Python 的 float 做加减有精度问题1.1 2.2 这种你懂得直接存浮点后果就是用户账单对不上这在答辩演示时会很尴尬。后端 Python 里用decimal.Decimal处理金额API 返回前再转字符串或整数分。3.3 计费配置表规则不写死在代码里计费引擎是代驾系统的核心亮点也是答辩时最好展示的设计感所在。把计费规则做成配置表而不是硬编码在 Python 代码里这在架构层面是一个明显的加分项。// 计费规则表 CREATE TABLE pricing_rule ( id INT AUTO_INCREMENT PRIMARY KEY, city_code VARCHAR(20) COMMENT 城市编码NULL 表示默认全国, rule_type TINYINT COMMENT 1-起步价 2-超里程单价 3-等待费单价 4-夜间服务费, base_km DECIMAL(5,2) COMMENT 起步价包含里程, base_price DECIMAL(8,2) COMMENT 起步价金额, per_km_price DECIMAL(6,2) COMMENT 超出部分每公里单价, per_min_price DECIMAL(6,2) COMMENT 每分钟等待费单价, night_start_time TIME COMMENT 夜间时段开始, night_end_time TIME COMMENT 夜间时段结束, night_extra_price DECIMAL(6,2) COMMENT 夜间每单加收金额, effective_date DATE COMMENT 生效日期支持改价不回溯 );后端写一个pricing.py模块从配置表读取当天生效的规则组合计算价格。提前把启动价规则、超里程、等待时长、夜间时段做成配置后面调价只需要改数据库记录代码零改动。这个点写进论文的系统可维护性章节比堆砌十个接口更出彩。4. 派单策略就近派单和抢单的取舍派单是代驾系统的业务难点。主流策略有抢单和派单两种两者互有优劣实际系统往往是混合模式。我建议核心逻辑做成就近派单为主超时转抢单兜底理由后面细说。4.1 就近派单的实现Redis GEO 距离检索用户点击呼叫代驾后后端拿用户的经纬度去 Redis 里查附近 3 公里内、在线状态为空闲的司机按距离从近到远排序取前 5 名作为候选司机。用 Redis 命令描述这个过程特别简洁。客户端上报坐标GEOADD driver:location {driver_id} {lng} {lat} EXPIRE driver:location {driver_id} 120查附近司机GEORADIUS driver:location {user_lng} {user_lat} 3 km WITHDIST ASC COUNT 10返回结果自带距离直接按距离排序取前几个就行。坐标上报和查询操作都是微秒级完全扛得住大量司机并发心跳。但要特别注意多个候选司机同时收到订单谁能确认如果 A、B、C 三个司机同时点了接单这个订单只能归属一个司机否则会出现多人实际去同一地点服务直接造成严重事故。这就是并发一致性要考虑的核心问题。我的方案是后端生成一个待派单令牌存 Redis 并设置过期时间如 60 秒SET order:dispatch:{order_no} 0 NX EX 60司机点击接单时用 Python 的 SETNX 操作把令牌置 1。只有一个司机能成功从 0 改为 1其余司机返回手慢了订单已被接。这个思路本质是 Redis 分布式锁简单可靠秒杀场景也是同一个套路。4.2 从派单到抢单的降级链路也别把所有希望寄托在就近派单上。现实中经常出现附近 3 公里内没有空闲司机或者司机在忙但 GPS 漂移导致明明很近却没被搜到。如果派单失败用户干等两分钟毫无反馈体验非常差。更稳妥的做法是分两层第一层就近派单附近 3 公里有候选司机时按距离推荐前 5 名推送新订单提醒。超时 30 秒无人接单系统自动向更大范围5 公里的在线司机广播。第二层大厅抢单所有到达司机端首页的可抢订单列表展示起步价、里程方向、用户出发地。司机自行点抢靠手速和网络延迟决定归属。大厅抢单的好处不仅是兜底它还给司机一种平台有单可做的自主感派单失败的订单转化率也会提升。实际运营中的代驾平台基本都是这两种模式混合。4.3 防作弊司机刷单检测代驾行业的噩梦是司机和乘客串通刷单。司机自己下单给自己或者找朋友下一单开出去几百米结束平台却要垫付给司机报酬。毕设里不要求做完整的风控体系但可以做一个简单的规则引擎同一司机连续多单起点相近、单均里程过短、同乘客多次打赏触发人工审核标记。这个在答辩时展示做一个异常订单列表页一下就能体现你的系统不是玩具。5. 微信小程序端核心页面与关键交互细节小程序端主要承担乘客侧交互司机端通常用独立的小程序或 App 实现。毕设里可以做一个乘客端小程序 司机端小程序分别放在两个小程序或者只做乘客端加管理后台。分两个小程序会让工作量大一倍但答辩时角色分离是很直观的亮点。下面只讲乘客端最关键的几个页面和交互逻辑。5.1 一键下单页地图选点和费用预估乘客打开小程序首页是地图自动定位当前地点作为代驾起点。页面要素包括地图上显示用户当前位置Marker 标注起点地址详情反向地理编码微信自带wx.getLocation返回的经纬度通过腾讯地图 WebService API 转地址文本终点输入框支持搜索地点联想调腾讯地图关键词输入提示接口预估费用展示起点地址一旦确定即可调后端POST /api/pricing/estimate拿预估价格呼叫代驾按钮起终点选择这里有个容易忽略的细节代驾起点通常就是当前位置终点才是用户输入的正好和打车软件相反。所以页面交互上起点默认锁定为当前定位不允许手动修改到太远的地方除非用户手动拖动地图拾取。地图组件这方面小程序用微信内置map组件 腾讯地图 SDK 就够。不建议再用第三方地图插件包内嵌原生 map 组件在性能和包体积上都更合适。5.2 订单状态实时追踪页WebSocket 推送的落地订单创建成功后乘客端自动跳转到订单详情页呈现的是一张长在地图上的状态卡片。这个页面要实时监听以下事件订单被司机接单推送司机昵称、车牌号、车辆品牌、司机距你多远司机已出发司机已到达推送通知用户可以上车了司机开始服务开始计费司机结束服务展示账单详情事件推送用 WebSocket。小程序端在进入订单页时建立连接。后端用 Flask-SocketIO 定义事件回调大致逻辑如下# 伪代码向指定用户推送订单状态 from flask_socketio import emit, send, join_room def send_order_event(order_id, event_type, data): room forder_{order_id} emit(event_type, data, roomroom, namespace/orders)前端小程序 Socket 监听收到driver_accepted等事件后更新页面状态。这里有一个真实的坑小程序的 WebSocket 地址要求用 wss线上配置或 ws开发者工具本地调试如果你在真机上调试后端跑在 localhost需要把调试域名校验关掉或者把后端挂到局域网 IP。实践上建议一开始就使用局域网 IP 或内网穿透方案验证不要等全部写完才联调不然排查起来工作量巨大。5.3 支付模块微信支付的简化与合规处理代驾订单结算一定涉及支付。微信小程序支付的标准流程是后端调用微信支付统一下单接口拿到 prepay_id返回给小程序小程序端唤醒wx.requestPayment拉起支付面板。这个链路本身不复杂但有两个特别麻烦的点一是资质个人主体的微信小程序无法开通微信支付必须企业主体才能申请。毕设阶段如果你没有企业资质通常的做法是模拟支付做一个确认支付按钮直接调用后端POST /api/order/pay把订单状态从待支付改为已支付。论文里说明这是为演示方便做的 Mock 处理并附上真实微信支付的对接代码和流程说明。二是支付回调真实环境中支付完成后微信服务器会向你的后端回调通知结果你的后端必须应答成功收到。这个回调 URL 必须是公网可达的 HTTPS 地址。毕设本地开发很难具备这个条件用模拟支付绕开是合理的。这里要记一个安全红线模拟支付接口如果不上签名加密任何人都能恶意调用把你的订单改成已支付演示时被老师发现会被打上无安全意识标签。正确的做法是模拟支付接口仅在开发调试环境开启配置文件中加一个MOCK_PAYMENT True/False总开关管理后台或数据库标识订单允许模拟支付并且模拟支付也要在你的业务层保留一条支付流水记录。6. 司机端核心功能与状态机约束多数毕设项目对司机端着墨较少但代驾系统的核心业务其实是司机端驱动的乘客端的体验完全依赖司机端的可靠上报。司机端要围绕司机全生命周期状态机来设计。司机状态机离线 → 空闲上线听单 → 接单赶往用户起点 → 已上车服务中 → 结束服务重新变为空闲这个状态机的流转必须由服务端驱动不能由前端自行切换否则司机关掉 App 再打开服务端可能完全不知道司机去了哪里。所以设计上司机点上线调用POST /api/driver/online。司机位置每 5 秒上报一次POST /api/driver/location。后端收到位置上报后更新 Redis GEO 和 TTL。后端在订单状态变更时同步修改司机的order_status和online_status。另一个关键实现是司机接单后的距离和到达判定。司机点击接单后服务端要根据司机当前坐标计算到起点的距离判断是否在合理范围内。如果司机在 10 公里外接了一个单要么是外挂刷单要么是 GPS 漂移系统要能识别并拒绝或标记。关于到达和服务的开始与结束有一个逻辑陷阱要提醒用户上车状态的触发不能只靠司机端点击按钮要结合起点到达判定。实践中更稳妥的做法是司机到达起点周围 100 米内App 允许点击我已到达用户端同步收到通知。服务结束同理司机到达终点 100 米内才允许点击结束服务否则 GPS 记录和里程单价会对不上。7. 后端接口设计一次完整的订单生命周期这一节从接口维度把订单主链路串一遍你写代码时可以对照这个清单查漏补缺。接口路径按实际业务自行约定关键是状态和参数的完整性。7.1 从登录到下单POST /api/auth/wxlogin小程序调用wx.login拿 code后端用 code 换 openid判断用户是否首次登录首次则自动注册。返回自定义 token后续请求头带Authorization: Bearer token。POST /api/order/estimate传入起点经纬度、终点经纬度返回预估价格和费用明细起步价、里程费、夜间费等。前端在下单页提前展示给用户。POST /api/order/create传入起点和终点的经纬度、地址文本。后端创建订单订单号生成规则要有业务可读性例如D2026022015001234D 表示代驾 年月日时分秒 随机数。POST /api/dispatch/search下单后后端立刻执行派单匹配。注意派单匹配是异步的不能让下单请求一直挂着等司机响应。实际项目里下单接口和派单动作通常是并行或异步的先快速创建订单返回用户正在为你派单派单结果通过 WebSocket 异步推送给用户端。7.2 司机接单到服务结束POST /api/driver/accept司机点击接单传入 order_no。后端执行 Redis SETNX 抢单锁成功后更新订单状态为已接单同时把司机状态改为服务中并推送事件给乘客端。POST /api/trip/start司机到达起点、乘客上车后司机端点击开始。后端记录服务开始时间。POST /api/driver/location服务过程中司机 GPS 持续上报后端记录轨迹。轨迹除了入库供后续里程校验、申诉调取也可以实时推送给乘客端显示司机位置。POST /api/trip/finish司机到达终点点击结束服务。后端根据实际里程、时长、夜间时段结算实付金额生成账单。此时订单状态变为待支付。这里有一个精度问题要想清楚实际里程以什么为准是司机上报 GPS 轨迹累加还是地图 API 算路还是司机仪表盘公里数司机的仪表盘是不准的轮胎气压不同都影响公里数地图 API 算路依赖道路网络和实时路况交通拥堵时绕路场景误差大GPS 轨迹累加容易受到漂移影响高架桥下定位跳几千上万米。实践中常采用地图 API 算路 GPS 轨迹参照的混合策略起点终点确定时先用地图 API 算出一个标准里程服务结束后再对比司机轨迹里程如果两个数值差异超过 20%系统标记该订单为异常订单进入人工审核。这个逻辑做进毕设里又能加点特色分。POST /api/order/pay乘客确认支付模拟或真实订单状态变为已支付。司机侧增加一笔本期可结算收入流水写入营收表。7.3 管理端接口与数据统计管理端不追求华丽页面但至少要有司机入驻申请列表查看认证资料、审核通过/拒绝、订单列表按时间/司机/用户筛选、订单详情含轨迹回放、营收统计按日/周/月汇总平台流水和司机分成。管理后台界面用 Bootstrap 或 Laravel-Admin 风格快速搭也可以用 Django Admin 快速实现。Python 后端提供一个/api/admin/stats/summary返回总订单数、总流水、平均客单价、完单率等统计指标管理页面用 ECharts 渲染图表即可。8. 位置服务和轨迹回放最容易翻车的两个技术点8.1 GPS 精度与坐标系千万别把 WGS-84 当 GCJ-02国内地图腾讯、高德、百度使用 GCJ-02 火星坐标系而手机 GPS 芯片原始定位输出的是 WGS-84 坐标系。两者在大陆区域有 100 到 700 米不等的偏移如果你直接拿原始 GPS 坐标传到小程序地图上标注用户位置会偏到隔壁小区派单也会完全不靠谱。这是个常见基础问题正确流程是小程序端用wx.getLocation拿到坐标后可以设置isHighAccuracy: true提高精度并通过type: gcj02让微信直接把坐标转成 GCJ-02 坐标系。后端统一按 GCJ-02 坐标存储和计算。Redis GEO 里存的也是 GCJ-02 坐标这样和地图渲染、距离计算用同一套坐标系避免二次转换误差。如果从第三方渠道拿到的坐标是 WGS-84 或其他坐标系后端存储前必须先做坐标转换。这个点如果在答辩现场被老师问起你能够答出GCJ-02 是火星坐标、WGS-84 是国际标准坐标要做偏移转换基本能确认你真的跑过真机而不是只看过理论。8.2 轨迹回放的实现思路轨迹回放是订单详情页的一个展示功能也可以在管理端看板中用来核查异常订单。实现思路不算复杂订单服务过程中司机上报的每个坐标点按时序存入轨迹表。轨迹回放时加载订单全部轨迹点按时间顺序在地图上逐个 Marker 或 Polyline 展示。CREATE TABLE order_track ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL, lng DECIMAL(10,6) NOT NULL, lat DECIMAL(10,6) NOT NULL, speed_kmh DECIMAL(5,1) COMMENT 瞬时速度, create_time DATETIME NOT NULL, KEY idx_order_time (order_no, create_time) );轨迹点采集不要盲存每一条 GPS建议按距离如每 10 米一个点或时间每 5 秒一个点降采样。否则一个 20 公里的订单可能要存上千个点管理端加载轨迹接口会卡顿。降采样后整个轨迹几十 KB 内能加载完配合地图 Polyline 平滑显示体验好得多。9. 后端接口安全与权限被忽略但答辩必问的部分跳开业务功能专门说说安全设计。我发现毕设项目普遍不重视权限和安全但老师偏偏爱追问这些。代驾系统至少有四道安全设计要做9.1 基于 Token 的登录鉴权微信登录成功后后端签发一个自定义 token建议使用 JWT 或简单的uuid Redis 存储。所有涉及身份的接口都要校验 token。推荐 JWT 的优势是后端无需存会话状态天然适合多实例部署。但要注意 JWT 一旦签发在过期前无法主动吊销所以建议有效期设短一些如 24 小时并支持 refresh_token 刷新机制。9.2 越权防护用户只能操作自己的订单订单接口必须校验order.passenger_id与当前登录用户一致。典型的安全漏洞是用户只需要遍历订单号就能查看或操作别人的订单。一个简单校验order Order.query.filter_by(idorder_id).first() if not order or order.passenger_id ! current_user.id: return error(无权操作该订单)这个逻辑每写一个接口都要过一遍不能只靠前端隐藏按钮。9.3 请求参数校验与异常捕获后端接口收到任何外部参数都要做校验。特别是经纬度范围lat 在 -90 到 90lng 在 -180 到 180、地址长度限制、价格字段类型校验。Python 里可以用 Marshmallow 或 Pydantic 做请求模型校验。使用 Pydantic 的好处是还能自动生成 API 文档配合 FastAPI 或插件答辩时一亮相技术深度立刻不一样。9.4 敏感信息脱敏手机号、身份证、驾驶证信息司机认证信息包含手机号、身份证号、驾驶证号这些在管理端展示时要做部分脱敏比如手机号只显示前三位后四位身份证只显示前六后四。数据库本身不落明文也可以考虑加密存储。虽然毕设演示可能不会用到但答辩时提到隐私数据加密存储、展示脱敏这个设计是讲究且有加分的。10. 联调与部署开发到演示的最后一公里10.1 开发环境与真机联调开发时最常见的卡点是微信开发者工具打开的是电脑模拟器环境和真机差别很大。建议整个开发周期都用真机预览调试模拟器只用来写页面样式。具体步骤小程序后台配置服务器域名请求域名、Socket 域名。开发者工具可以勾选不校验合法域名但真机预览必须配。如果你没有备案域名可以用内网穿透方案把本地 Flask 服务映射到公网临时域名再用小程序真机对接。内网穿透本质上就是把本机服务暴露到公网实践中很常用它只是工具我们要关注的是联调效率但注意用这类工具的同时服务端口不要裸奔加上 token 校验否则任何人都能访问你的本地接口。WebSocket 域名和 HTTP 域名是分开配置的容易漏配联调报错时先检查后台域名配置。10.2 部署方案建议毕设系统的部署可以是一台 2 核 4G 的云服务器也可以直接局域网部署在实验室机器。部署栈配置Web 服务器Nginx静态资源 反向代理 FlaskPython 进程管理Gunicorn 或 uWSGI配 3 个 worker 进程数据库MySQL 8.0缓存Redis 6.xWebSocketGunicorn 不适合跑 WebSocket需要用daphne或gunicorn gevent-websocket组合。更省心的方案是把 Socket.IO 服务单独跑在另一个端口。需要注意Flask-SocketIO 需要和 Gunicorn 的事件模型匹配直接用gunicorn -k eventlet或geventworker 跑不然长连接很容易断开。这一步卡住过很多人提前搜索对应版本配置别等到演示前夜才折腾。10.3 演示环境准备清单答辩现场演示代码库容易出意外。根据经验你至少准备一套离线演示方案本地起好服务把 MySQL、Redis 数据准备好预置几个模拟司机和订单。真机小程序和模拟器都跑通一遍完整流程下单→派单→接单→服务→支付。提前录制一条演示视频作为兜底。现场没网、没信号、蓝牙干扰都可能导致 GPS 漂移或接口连不上视频不丢人现场翻车才丢人。11. 避坑清单我实际踩过的一些坑最后给一份我在类似项目里实际操作中踩过的坑建议按这个清单提前排雷。微信小程序合法域名配置在开发者工具里一切正常真机一预览接口全挂大概率是后台没配 request 合法域名或者用了 IP 端口形式。小程序对域名有备案要求本地调试建议直接勾选不校验。WebSocket 连接不稳定小程序切后台超过一定时间Socket 会自动断开。一定要监听wx.onSocketClose并在回到前台时主动重连否则订单状态推送会静默丢失。后端 Flask-SocketIO 也要设置ping_interval和ping_timeout避免长连接假死。GPS 坐标偏差小程序的wx.getLocation默认返回 WGS-84 原始坐标必须确认你传给后端的是什么坐标系。从数据库里看到的坐标和地图上标的 Marker 不一致通常是坐标系混用了。并发抢单重复处理没有用 Redis SETNX 或数据库唯一索引承接多个司机同时接收同一单导致超卖。这类并发逻辑必须用锁不能在 Python 层用 if 判断。金额精度问题用 FLOAT 存金额涉及多次加减乘后会出现 0.30000000000000004 这类结果传给小程序展示时用户会看到一长串小数。用 DecimalAPI 序列化前转字符串。订单号生成策略时间戳简单随机数很容易在并发下重复尤其是用 datetime 毫秒字符串时。建议用当前时间 用户ID后四位 随机数配合数据库唯一索引兜底确保任何并发情况都不会出现重复订单号。管理端和乘客端同时登录同一账号如果登录态只存单点 token管理端登录会把乘客端挤下线。需要在用户会话表里存多端隔离标识区分login_type这个细节不做演示时会直接翻车。数据库连接池配置Flask 默认每请求开一个 MySQL 连接本地没事并发一高就报 Too many connections。用 SQLAlchemy 连接池配置pool_size和pool_recycle这是生产化必须过的一关。从我的实际开发体会来说代驾系统最适合作为 Python 微信小程序的综合实战项目它有一套完整且有辨识度的业务链路允许你在派单、计价、轨迹、支付等关键点上做出有深度的技术设计又不像电商系统那样臃肿。你沉浸做完一遍对后端接口设计、状态机建模、实时通信、缓存使用、权限安全这些核心能力的掌握会比做十个 CRUD 管理页面扎实得多。做完之后还可以加一个白板需求——把调度算法换成更复杂的多目标派单项目又能往上走一个层次。
返回列表