ARTICLE DETAIL

资讯详情

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

Java+微信小程序跑腿平台实战:订单状态机与并发更新避坑指南

Java+微信小程序跑腿平台实战:订单状态机与并发更新避坑指南 简介这是一份结合 Java 后端与微信小程序前端的跑腿平台项目源码包面向具备一定 Java、微信开发基础希望参与实际业务系统设计的开发者可用于毕业设计或项目实战参考。代码覆盖用户端小程序、后台管理端与服务器端核心逻辑涉及订单处理、支付接口、跑腿任务分派等常见流程。压缩包共 2020 个文件大小 9.81MB包括 48 个 Java 源文件、21 个 XML 配置、7 个 WXML 小程序页面文件、1 个 SQL 数据库脚本以及大量 CSS、PNG、SVG、JS 等前端资源与页面素材基本可还原出完整项目结构。目前已有 200 人学习下载。相较于单纯教程这份资源提供了可直接阅读的代码与页面文件便于对照学习微信 API 调用、数据库表设计、后端接口分层等知识点也能帮助快速搭建同类跑腿服务的原型。1. 一份“需求代码.rar”里的跑腿平台到底值不值得接跑腿平台不是新概念但用 Java 做后端、微信小程序做前端的这套组合仍然是外包接单和毕业设计里出现频率最高的题目。原因很直接订单状态流转、用户与骑手两套角色权限、支付与定位这几个点恰好把 Web 开发的核心技能都串起来了而且小程序端天然适合这种“附近的人下单、骑手抢单”的场景。你拿到的 .rar 里往往只有一份需求文档加残缺的代码骨架真正要交付的是一个能演示下单、接单、送达闭环的系统。这篇笔记就按我在实际项目里的做法从需求拆解到后端接口、小程序对接、联调避坑、最后上线验证把整个链路讲透。新手照着做能跑通熟手可以直接拿走状态机和并发更新的设计。2. 把需求拆成能落地的模块订单、骑手、用户三端闭环2.1 跑腿平台的四个核心角色与状态机跑腿平台表面上只有用户和骑手但实际落地时后台管理员是绕不开的第三类角色。用户发布跑腿需求骑手接单管理员处理投诉和审核骑手资质再加上微信支付充当支付渠道整个系统最少要支撑四种身份。最常见的翻车点是只在代码里写死“user”和“rider”两个角色结果后台审核功能无处安放最后硬塞进用户表里用 type 字段区分权限校验一团糟。正确的做法是角色独立成字段但鉴权统一走拦截器。我的习惯是用一个role字段放在用户表里取值 0 用户、1 骑手、2 管理员登录后把角色写进 JWT 的 claims 里。接口层用自定义注解RequireRole做拦截比在业务代码里写if (user.getRole() ! 1)干净得多。订单状态机是另一个容易想简单的地方。一个小小的跑腿订单状态往往有十来个待支付、已支付待接单、已接单、取件中、配送中、已送达、已取消、退款中、已退款。如果只用一个 status 整数保存而不管流转规则就会出现“已送达的订单还能被取消”这种事故。我一般用两张表来管状态订单主表存当前状态订单状态流水表存每一次变更记录。主表的 status 用枚举类OrderStatusEnum管理不允许在业务代码里直接写魔法数字。状态流转的合法性统一在一个OrderStateMachine类里判断比如只有WAIT_RECEIVE状态下的订单才能被骑手抢单DELIVERING状态才能触发“送达”操作。这么设计之后接口层只需要调用状态机的方法不用关心“什么状态能做什么”排查脏数据时也只用看流水表。以下是订单状态枚举的示意实际项目里建议把状态码和描述一起维护public enum OrderStatusEnum { UNPAID(0, 待支付), PAID(1, 已支付待接单), ACCEPTED(2, 已接单), PICKING(3, 取件中), DELIVERING(4, 配送中), FINISHED(5, 已送达), CANCELED(6, 已取消), REFUNDING(7, 退款中), REFUNDED(8, 已退款); }为什么要用枚举而不是直接存 int因为枚举可以在getStatus()之外提供getDesc()和canTransferTo(target)方法前端展示、后端判断、日志排查都用同一份定义。后面要加“异常上报”状态时只需要改这个枚举和状态机不用满项目搜if (status 4)。记住一个原则状态字段绝不裸露在业务代码里。2.2 用 Spring Boot 搭出订单核心表六张表和一个枚举后端框架的选择上Spring Boot MyBatis-Plus 是目前 Java 后端接这类项目的主流不是因为它性能最好而是因为 CRUD 代码量最少、资料最多遇到问题能搜到答案。数据库用 MySQL缓存和分布式锁先别急着上 Redis——跑腿平台的核心矛盾是订单状态并发更新用数据库行锁和条件更新就能解决没必要为了演示引入额外组件。核心表通常需要六张用户表、骑手表也可以合并进用户表、订单表、订单状态流水表、地址表、支付单表。其中订单表是主战场字段设计直接决定后续接口好不好写。订单表不能只存一个“起止地址”字符串要把经纬度、联系人、电话都拆出来因为后面要做“附近骑手”查询。以下是订单表的最小字段设计CREATE TABLE order_info ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单号业务展示用, user_id bigint NOT NULL COMMENT 下单用户ID, rider_id bigint DEFAULT NULL COMMENT 接单骑手ID未接单时为NULL, status tinyint NOT NULL DEFAULT 0 COMMENT 订单状态对应OrderStatusEnum, pickup_lng decimal(10,7) NOT NULL COMMENT 取件经度, pickup_lat decimal(10,7) NOT NULL COMMENT 取件纬度, pickup_address varchar(255) NOT NULL, delivery_lng decimal(10,7) NOT NULL, delivery_lat decimal(10,7) NOT NULL, delivery_address varchar(255) NOT NULL, receiver_name varchar(50) NOT NULL, receiver_phone varchar(20) NOT NULL, goods_desc varchar(255) DEFAULT NULL COMMENT 物品描述, estimate_distance decimal(10,2) DEFAULT NULL COMMENT 预估距离单位公里, fee decimal(10,2) NOT NULL COMMENT 跑腿费单位元, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_status_create (status, create_time), KEY idx_rider_id (rider_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意几个容易被忽略的点rider_id允许为空这正好表达“尚未被接单”的语义status用 tinyint 配合后端枚举不用 varchar 存中文经纬度用 decimal(10,7)float 的精度在距离计算时不够索引上要覆盖“用户查自己的订单”和“骑手查可抢订单”这两个高频查询。很多新手在status上不加索引导致订单量上来后列表查询全表扫描。订单状态流水表更简单记录order_id、from_status、to_status、operator_type、operator_id、create_time每次状态变更插一条。这张表的作用一是问题回溯二是后面做“骑手轨迹”或“订单超时自动取消”时能拿到精确时间点。2.3 关键流程下单、抢单、送达的接口设计三个核心接口分别是用户下单、骑手抢单、骑手送达。下单接口要注意的是不能在事务里直接计算“附近骑手”然后推送因为下单动作本身只需要落订单数据找骑手是异步流程。常见做法是下单后把订单 ID 丢进一个延迟队列等用户完成了支付再进入可抢单池。抢单接口是整个系统并发压力最大的点多个骑手同时抢一个订单数据库层面必须保证只有一个骑手成功。使用UPDATE ... WHERE status 待接单 AND rider_id IS NULL这种条件更新是正确姿势配合影响行数判断是否抢到。伪代码如下Transactional public boolean grabOrder(Long orderId, Long riderId) { // 直接把状态从待接单改为已接单如果影响行数为0说明已被抢走 int rows orderInfoMapper.updateStatusWithCondition( orderId, OrderStatusEnum.PAID.getStatus(), // 期望当前状态 OrderStatusEnum.ACCEPTED.getStatus(), // 目标状态 riderId ); if (rows 0) { // 可能是订单不存在、状态不对或者被别人抢了 return false; } // 插入状态流水 orderFlowMapper.insert(orderId, PAID, ACCEPTED, RIDER, riderId); return true; }对应的 SQL 是UPDATE order_info SET rider_id #{riderId}, status #{toStatus}, update_time NOW() WHERE id #{orderId} AND status #{fromStatus} AND rider_id IS NULLupdateStatusWithCondition里最关键的是把status 待接单写进 WHERE 子句而不是先 SELECT 再 UPDATE。有些人写代码是先查订单状态判断是待接单再更新这在单机演示没问题一旦并发稍微上来就两个骑手都查询到“待接单”然后先后更新成功订单被抢两次。MySQL 的 InnoDB 在 UPDATE 时会锁定匹配的行所以条件更新天然解决了这个问题。当然前提是事务隔离级别不是允许脏读的级别默认的 REPEATABLE READ 没问题。送达接口则要加一个位置校验骑手点击“送达”时前端上报当前位置后端计算与收货点的距离大于某个阈值比如 500 米就拒绝。这个校验能挡住大部分“刷单”行为也是答辩和演示时的一个亮点。距离计算用高德或腾讯地图 API 没问题但本地开发时用Haversine公式也能算出球面距离精度在几百米内完全够用。3. 微信小程序端从 page 到 API 的桥接实现3.1 小程序登录态wx.login 换 openid 的完整链路微信小程序登录不能直接拿wx.login返回的 code 当作登录凭证存下来。正确流程是小程序端把 code 发给后端后端调用微信的jscode2session接口换取 openid 和 session_key然后后端自己生成一个业务登录态比如 JWT再返回给小程序。小程序后续请求都带这个 JWT后端不再关心微信那边的 code 是否过期。这个链路里有三个坑。第一code 只能使用一次而且有效期只有 5 分钟所以拿到 code 要立刻发后端。第二后端不能把 openid 直接返回给前端否则任何人都能拿 openid 伪装用户。第三session_key 涉及用户手机号解密等敏感操作只能在服务端保存绝不能写进小程序缓存。后端接口示例PostMapping(/wx/login) public Result login(RequestBody WxLoginRequest req) { // 1. 用 code 向微信服务器换 openid String url https://api.weixin.qq.com/sns/jscode2session ?appid appid secret secret js_code req.getCode() grant_typeauthorization_code; String resp restTemplate.getForObject(url, String.class); // 2. 解析返回的 openid不存在则注册新用户 // 3. 生成 JWT把 role 和 userId 放进 claims }参数说明appid和secret在微信公众平台的“开发设置”里拿secret绝不能出现在小程序源码里也不能作为前端请求参数传进来必须在后端配置。这里用restTemplate是偷懒做法实际项目建议用带连接池的 HTTP 客户端避免频繁创建连接。jscode2session接口有每日调用量限制所以用户登录后不能每次都调微信而是后端靠 JWT 有效期维持会话JWT 过期了才重新走wx.login。3.2 订单列表的“加载更多”分页参数与触底刷新小程序页面里最常见的交互就是“页面列表加载更多”。很多新手把全部订单一次性查出来渲染订单一多页面直接卡死而且不符合微信分页加载的体验规范。正确做法是后端分页前端触底加载下一页。后端分页接口我习惯返回一个统一的分页响应结构records列表、total总数、current当前页、size每页条数、hasMore是否还有更多。前端用onReachBottom生命周期触发加载。这里有个容易犯的错误onReachBottom只有在页面配置了onReachBottomDistance且内容超过一屏时才会触发测试时如果列表太短永远等不到触底事件。前端关键代码片段Page({ data: { orders: [], page: 1, size: 10, hasMore: true }, loadMore() { if (!this.data.hasMore) return; wx.request({ url: https://api.exampledomain.com/order/list, data: { page: this.data.page, size: this.data.size, status: this.data.status }, success: (res) { const newList this.data.orders.concat(res.data.data.records); this.setData({ orders: newList, page: this.data.page 1, hasMore: res.data.data.hasMore }); } }); }, onReachBottom() { this.loadMore(); } });这里的data.page初始为 1每次加载成功后自增。concat能确保已有数据不被覆盖。要注意wx.request默认超时时间较短后端如果查询慢需要在wx.request里设置timeout: 10000。另外如果有筛选状态切换筛选时要重置page为 1、orders为空数组、hasMore为 true否则会出现“旧状态的数据混进新列表”的问题。3.3 用 uni-app 还是原生两种写法的取舍这个项目如果用原生微信小程序开发好处是 API 直出没有中间层的兼容问题坏处是如果将来要发布到支付宝小程序或抖音小程序代码基本重写。用 uni-app 的好处是一套代码多端发布但是要额外承担编译层的黑匣子风险比如这个项目标题里提到的“uniapp 微信小程序打包”问题很大概率会遇到。我的观点是如果这是给客户交付的跑腿平台优先用原生或 Taro 这类和微信贴合紧的方案如果是给自己练手或者演示可以用 uni-app因为它的 Vue 语法上手更快页面和组件的组织也更符合前端直觉。这里不站队只指出几个真实差异。原生小程序里wx.request的success回调在编译后仍然是原生回调而 uni-app 里是 Promise 包装两者在错误处理上要注意检查statusCode。另外原生小程序的 wxml 语法是“标签 双花括号”uni-app 的 template 部分几乎等同于 vue 语法如果团队里没有 Vue 基础就不要强行上 uni-app。最后uni-app 打包到微信小程序后分包和自定义组件的路径会有变化需要在manifest.json里配置mp-weixin的appid和setting。无论选哪种最关键的都是把登录态和请求封装独立出来避免每个页面都重复写wx.request。4. 前后端联调的四个必踩坑从 10002 到重复推送4.1 请求 10002域名校验与调试模式的玄学用微信开发者工具调试时最常遇到的是request:fail或url not in domain list。这类问题的根源是小程序平台要求所有请求域名必须是 HTTPS 域名并且已经在微信公众平台后台配置为 request 合法域名。本地开发时后端跑在http://localhost:8080显然不满足。解决方法是分两步开发者工具右上角点击“详情”勾选“不校验合法域名”选项这一步只对开发者工具有效手机预览时必须关掉。然后在真机调试时把后端接口部署到一个有 HTTPS 证书的测试域名上或者在微信公众平台后台把测试域名加进白名单。但这个标题里出现过的“微信小程序 10002”又是另一回事。10002 错误码通常出现在使用 web-view 组件或页面跳转时提示“页面不存在”或“无权限访问”。具体表现是从公众号文章或二维码进入小程序页面时页面路径没有配置在业务域名里。如果你在小程序里嵌了 web-view 加载 H5此时 H5 的域名必须和业务域名一致且要在后台配置业务域名证书校验文件。调试时开发者工具的“合法域名”校验不会校验 web-view 里的业务域名所以经常出现开发者工具里正常、真机打开就白屏的情况。解决这类问题的顺序是先看报错码10002 优先检查页面路径和业务域名url not in domain list优先检查 request 合法域名两者都指向“域名配置”本质是一样的——把前端用的全部域名都列进后台的对应配置里不要漏了uploadFile和downloadFile域名它们是独立配置项。4.2 订单状态并发更新WHERE status 条件更新保住一致性前文抢单接口已经提过条件更新这里再展开一个更隐蔽的场景用户取消订单和骑手抢单同时发生。用户点“取消”骑手点“抢单”如果后端代码没有做状态条件判断最终状态就取决于两个请求谁先执行完会造成逻辑错误用户已经取消的订单被骑手抢走并配送。正常设计是取消接口也要带条件UPDATE order_info SET status已取消 WHERE id? AND status IN (待支付, 待接单)并且被取消的订单要释放骑手。抢单接口同理。两个操作都依赖数据库的行锁谁先获取到行锁谁就赢后执行的会因为 WHERE 条件不满足而影响 0 行业务上再提示“订单已取消”或“订单已被抢”。在代码层面还可以用乐观锁做二次保障。给订单表加一个version字段每次更新带上version ?影响行数为 0 就重试或提示。实际项目中条件更新已经够用version适合像“修改运费金额”这类没有状态约束的更新。不要在每次抢单时先SELECT ... FOR UPDATE因为锁的持有时间更长更容易死锁。4.3 定位权限与距离计算WGS84 与 GCJ02 的坑微信小程序获取地理位置是通过wx.getLocation得到经纬度这个经纬度是 GCJ02 坐标国测局加密坐标不是 GPS 原始坐标 WGS84。如果后端拿了这两套坐标混用算出来的距离会偏移几十米到几百米严重时会把“同楼取件”判断成“跨街配送”。最常见的坑是高德地图、腾讯地图使用 GCJ02Google Earth 和部分 GPS 设备使用 WGS84。小程序内置的wx.getLocation返回的latitude/longitude是 GCJ02但wx.chooseLocation返回的也是 GCJ02。如果你把这种坐标直接存到数据库再调用高德的“地理编码”接口是没问题的但如果你想用 Haversine 公式精确计算距离就必须先把 GCJ02 转 WGS84 再算或者直接用高德/腾讯的距离计算 API。另一个坑是用户拒绝授权定位。小程序里wx.getLocation需要用户主动点击授权如果用户拒绝需要引导到设置页重新打开。判断方式是检查res.errMsg是否包含auth deny。后端在接收坐标时也应该校验经纬度是否在合理范围纬度 -90 到 90经度 -180 到 180防止脏数据入库。如果你给跑腿平台加“附近骑手”查询最省事的方式是直接用腾讯地图或者高德的小程序 SDK它能直接在wx环境里调qqmap-wx-jssdk不需要后端自己计算球面距离。后端如果需要显示“距离多少米”则建议用官方 API 返回的距离字段不要自己用坐标公式因为坐标偏移会直接影响用户判断。4.4 支付回调重复通知幂等表怎么建微信支付下单成功后支付结果通知会异步 POST 到你的服务器回调地址。这个回调可能重复发送多次微信官方文档明确说明“通知可能多次到达”。如果你在回调里直接改订单状态一个订单会被重复处理两遍可能导致库存扣减两次、积分发放两次、或者状态从“已支付”被错误改成“已支付”。解决方法是幂等处理。最简单的方式是建一张支付回调记录表以transaction_id微信支付单号作为唯一索引。回调进来先尝试插入记录插入成功才继续业务处理插入报唯一键冲突就直接返回“SUCCESS”不再处理。伪代码如下PostMapping(/wx/pay/notify) public String payNotify(RequestBody String xmlMsg) { // 1. 解析 XML验签 // 2. 取 transactionId先查回调表 int inserted payNotifyMapper.insertIgnore(transactionId); if (inserted 0) { return xmlreturn_codeSUCCESS/return_code/xml; } // 3. 更新订单状态待支付 - 已支付 // 4. 推送订单给小程序的“附近骑手” }这里使用insertIgnore是 MySQL 特性遇到唯一键冲突返回影响行数 0。在这个基础上订单状态更新也要加上条件UPDATE order_info SET status已支付 WHERE id? AND status待支付。这两层加在一起就算回调重复十次也只有第一次能真正改状态。5. 把“需求代码”升级成可运行工程的三个关键改造5.1 用枚举代替魔法数字订单状态不再翻车拿到一份“需求代码.rar”最让人头疼的不是缺功能而是代码里到处是status 2、type 1这种魔法数字。改一个状态含义得全项目搜索替换漏一处就出隐蔽 bug。验收标准很简单除枚举类和状态机外代码里不允许出现无法解释的整数常量。改造过程分三小步。第一步定义枚举类包含状态码、描述、可转移目标集合第二步改造数据库查询返回的status字段在实体类里用枚举类型接收MyBatis-Plus 支持默认枚举映射第三步把业务逻辑里所有判断都换成OrderStatusEnum.ACCEPTED.getStatus()或直接比较枚举对象。这一步改造完成后新增状态只要改枚举和状态机不用动核心接口。顺手还要干一件事所有接口的返回值里状态字段不要只给数字要同时给描述文案。小程序端直接展示“等待接单”而不是1前端不用再做映射。做法是在 VO 里加一个statusDesc字段由枚举的getDesc()填充。5.2 给骑手分配订单简单贪心还是延迟队列跑腿平台有一个关键决策用户下单后系统是主动“指派”给某一个骑手还是把订单放到池子里让骑手“抢单”。两者业务逻辑不一样代码复杂度也差很多。小单量项目用抢单模式最简单订单支付完成后 status 置为待接单骑手端刷新列表看到所有待接单订单点“抢单”走前面说的条件更新。如果需求文档里写的是“系统自动分配”那就要考虑策略了。最简单的实现是“最近骑手优先”查询订单取件点附近 3 公里内所有在线的骑手优先推给距离最近的那个如果对方 30 秒内不响应再推给第二近的。这个“超时未响应转派”可以用 Java 的DelayQueue或者 Redis 的 key 过期回调实现但生产环境直接用 RabbitMQ 延迟队列更可控。这里要提醒一点很多跑腿平台实际上同时支持“指派”和“抢单”但需求文档往往含糊。开发前一定先和需求方确认否则做出来用户能下单但骑手接不到单演示时直接翻车。我一般建议 MVP 阶段统一用抢单模式砍掉自动分配的逻辑因为自动分配涉及在线状态维护、消息推送、超时回收工作量大且很容易出 bug。如果客户坚持要有那就加一个“仅推荐”功能后台把订单置顶推荐给附近骑手但是否接受仍是骑手决定。5.3 本地跑通的最小配置application.yml 与数据库脚本很多人拿到代码压缩包第一次mvn spring-boot:run就抛一堆错原因不是代码问题而是配置缺项。一个可运行的 Java 跑腿平台最少要有这几个文件application.yml数据源、Redis、微信配置、schema.sql建表脚本、data.sql初始化管理员账号。最小配置示例spring: datasource: url: jdbc:mysql://localhost:3306/paotui?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 timeout: 3000ms wx: appid: wx1234567890abcdef secret: abcdefghijklmnopqrstuvwxyz123456 mch-id: 1900001234 callback-url: https://api.example.com/wx/pay/notify注意serverTimezone必须设置否则 MySQL 8 与 Java 的时间处理会出现 8 小时偏差。Redis 如果只是用于缓存登录态没有它系统也能跑但项目里如果有“在线骑手状态”功能就依赖 Redis 的过期时间。微信支付相关配置没有正式商户号时可以先留空支付功能用“模拟支付”接口顶替演示时不走微信真实转账。数据库脚本建议用schema.sql加data.sql并在application.yml里配置spring.sql.init.mode为 always 或 never不要用默认的嵌入式数据库行为。因为 MySQL 的建表语句有ENGINEInnoDB等特性直接用 Spring Boot 的自动建表在 MySQL 下没问题但如果脚本里带了外键约束执行顺序要注意先建父表再建子表。6. 验证方案的最后一公里压测、埋点与模拟器抓包6.1 用 Mock 数据快速验证状态流转服务跑起来第一件事不是写页面而是先验证订单状态机。我习惯写一个StateMachineTest单元测试把从“待支付”到“已退款”的每一条合法路径和非法路径都断言一遍。跑腿平台的状态分支多人工点击测试很难覆盖所有组合单元测试是最便宜的后悔药。例如测试“已送达状态不可被取消”Test void finishedOrderCannotBeCanceled() { OrderInfo order new OrderInfo(); order.setStatus(OrderStatusEnum.FINISHED.getStatus()); assertFalse(orderStateMachine.canTransferTo(order, OrderStatusEnum.CANCELED)); }然后把订单服务里所有改写状态的方法都在这类测试里跑一遍。更重要的是验证并发用 JUnit 开两个线程同时抢同一个订单断言只有一次成功。这是判断条件更新写没写对的黄金标准。做完状态机测试再联调小程序能减少至少一半的接口调试时间。6.2 模拟器抓包Charles 配置与 HTTPS 证书小程序开发中最实用的调试手段是抓包。Windows 下首选 CharlesmacOS 也可以用。抓微信小程序的包和抓网页不一样开发者工具里的请求可以在 Network 面板直接看但真机调试时的请求必须走代理。Charles 配置 HTTPS 抓包需要两步安装 Charles 根证书到电脑并信任再把手机 Wi-Fi 代理指向电脑 IP 和 8888 端口同时在手机上安装并信任同一个证书。之后在小程序里发请求Charles 就能看到完整的 URL、请求头和响应体。使用 Charles 的断点功能能模拟接口异常。比如把订单列表接口的响应改成空数组看小程序页面是否出现“暂无订单”的兜底或者把抢单接口的返回码改成“已抢光”验证前端是否展示错误提示。这里有个经验抓包时如果发现 HTTPS 请求显示为 CONNECT 隧道先检查证书有没有安装到系统根证书而不是用户证书Android 7 以上默认不信任用户证书。证书没问题但请求仍失败再检查小程序的合法域名配置抓包代理和域名校验是两层不同的机制不要混在一起排查。抓包还有个大用途对比前后端接口的字段名。常见翻车是后端返回orderNo小程序里却写order_number页面渲染空白。用 Charles 看一次响应体哪里不一致一目了然比对着代码逐行找快得多。6.3 上线前检查清单从数据库索引到微信审核跑腿平台上线的检查清单里最容易被忽略的是数据库索引。订单表一定要有status create_time联合索引否则骑手刷新抢单列表时一次全表扫描会让接口耗时从 20ms 变成 200ms用户划几次页面就被限流。地址表如果有“常用地址”功能需要加user_id索引。订单流水表则要加order_id索引支撑按订单流水查询。微信审核方面最常被退回的原因是“没有完整的业务流程”和“涉及虚拟支付”。跑腿平台涉及真实交易必须在审核时提供测试账号并且让审核员能看到下单到支付完成的完整闭环。如果不想接微信支付可以把支付功能设计成“模拟支付”但审核时要在小程序描述里注明。另外小程序的隐私协议里必须写明会收集位置信息、手机号、微信昵称否则审核会以“违规收集用户信息”驳回。位置权限只申请一次不要每次进入页面都弹授权框弹多了用户拒绝率高审核员体验差也会被拒。最后说一个我的习惯上线前用开发者工具的“自动化测试”模块跑一遍核心路径再加一轮手动回归重点测订单异常分支——支付超时、取消订单、骑手取消、送达超时这些分支平时演示很少走却是跑腿平台最容易出投诉的环节。把这些跑顺了再谈上线也不迟。希望帮到你。本文还有配套的精品资源点击获取
返回列表