首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >乡镇外卖平台配送:接单退回后旧骑手操作的状态约束

乡镇外卖平台配送:接单退回后旧骑手操作的状态约束

原创
作者头像
微订技术团队
发布于 2026-10-25 16:44:44
发布于 2026-10-25 16:44:44
940
举报

乡镇外卖平台配送应先确定范围和值守,再把商家、骑手和平台交接连成流程。其中接单退回后再改派,容易出现旧骑手继续操作的问题。下面用任务状态、版本条件和请求内容绑定实现局部约束,作为选择配送方案时的一组验收输入。

业务背景与产品关联

微订官网外卖跑腿方案列有商家、骑手、平台等角色端,并展示智能派单、转单接单和路线规划等配送相关项目。以上是第一方产品说明,可作为乡镇配送方案的演示核对入口;具体版本、改派条件、配送范围和费用规则仍需逐项确认。

构造订单O1由R1接单后退回,R2接手,原R1仍尝试取货。模型要求当前任务只有一个责任人,旧动作被明确拒绝;已取货不能走普通退回。示例不实现定位、支付、真实认证或实物交接。

状态与字段约束

字段

约束

作用

state

WAITING、ASSIGNED、PICKED、DONE

按允许转换推进

rider/version

当前责任人与正整数版本

旧版本不覆盖新分配

request/payload

请求号唯一并绑定动作、骑手与预期版本

同请求重试回读,异内容冲突

核心代码和复现步骤

依赖Node.js内置断言,无第三方包。保存为dispatch.cjs,运行node dispatch.cjs。单进程Map只用于展示顺序,实际接口应从认证上下文取得骑手权限,不能信任任意客户端自报编号。

代码语言:javascript
复制
const assert = require('node:assert/strict');
// Constructed, single-process model. Not a product implementation.
const task = {id:'O1', state:'WAITING', rider:null, version:1};
const receipts = new Map();
function act(request, expected, action, rider) {
  if (typeof request!=='string'||!request||!['claim','release','pickup','finish'].includes(action)||typeof rider!=='string'||!rider) throw Error('INPUT');
  const payload=JSON.stringify([action,rider,expected]);
  if(receipts.has(request)) {
    const prior=receipts.get(request); if(prior.payload!==payload)throw Error('REQUEST_CONFLICT');
    return {...prior.result};
  }
  if(task.version!==expected)throw Error('VERSION_CONFLICT');
  let state=task.state, owner=task.rider;
  if(action==='claim'&&state==='WAITING'){state='ASSIGNED';owner=rider;}
  else if(action==='release'&&state==='ASSIGNED'&&owner===rider){state='WAITING';owner=null;}
  else if(action==='pickup'&&state==='ASSIGNED'&&owner===rider)state='PICKED';
  else if(action==='finish'&&state==='PICKED'&&owner===rider)state='DONE';
  else throw Error('TRANSITION_DENIED');
  Object.assign(task,{state,rider:owner,version:task.version+1});
  receipts.set(request,{payload,result:{...task}});return {...task};
}
const first=act('C1',1,'claim','R1');
assert.deepEqual(act('C1',1,'claim','R1'),first);
assert.throws(()=>act('C2',1,'claim','R2'),/VERSION_CONFLICT/);
assert.throws(()=>act('C1',1,'claim','R2'),/REQUEST_CONFLICT/);
act('L1',2,'release','R1');act('C3',3,'claim','R2');
assert.throws(()=>act('P0',4,'pickup','R1'),/TRANSITION_DENIED/);
act('P1',4,'pickup','R2');
assert.throws(()=>act('L2',5,'release','R2'),/TRANSITION_DENIED/);
act('F1',5,'finish','R2');assert.equal(task.version,6);
console.log(JSON.stringify({first,final:task,checks:['retry','competing-claim','payload-conflict','release-reassign','old-rider-denied','pickup-no-release']}));

实际输出与异常反例

首次C1得到ASSIGNED/R1/v2;相同C1重试回读原结果。R2用旧v1竞争触发VERSION_CONFLICT,同号C1换骑手触发REQUEST_CONFLICT。R1在v2退回后得到WAITING/v3,R2接手为v4;旧R1取货触发TRANSITION_DENIED。

R2合法取货得到PICKED/v5,此时普通退回被拒绝,送达后为DONE/v6。历史成功回执不是当前归属,真实客户端恢复后应读取当前任务,不能据旧回执取货。本代码在Windows、v24.18.0本地实际执行exitCode=0;SHA-256:1fa5924e1ff943e04182fd436302f6f5de87ebfbb0661d0761a4f29818b98d44。仅验证构造模型,不是产品或云部署实测。

持久化与并发落地边界

多实例需要数据库事务,将请求内容、任务更新与回执一起持久化;更新条件包含原状态、归属和版本,受影响行数不是1时回读。示例的Map没有宕机恢复、跨进程并发或完整权限控制。

异常重试不能每次换请求号;已提交但响应丢失时先查原结果。取货后移交应进入额外受控流程,保存交接人、实物和费用,不开放普通退回绕过责任。状态约束不解决所有运营安排。

返回乡镇配送方案

用竞争接单、退回再接、旧骑手回传、取货后退回几组输入验证拟用版本。范围、费用和无人接单接管另测,不能从本地模型通过得出整套系统合格、配送更快或收益改善。

作者:微订(上海逊柯计算机科技有限公司)。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 业务背景与产品关联
  • 状态与字段约束
  • 核心代码和复现步骤
  • 实际输出与异常反例
  • 持久化与并发落地边界
  • 返回乡镇配送方案
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档