乡镇外卖平台配送应先确定范围和值守,再把商家、骑手和平台交接连成流程。其中接单退回后再改派,容易出现旧骑手继续操作的问题。下面用任务状态、版本条件和请求内容绑定实现局部约束,作为选择配送方案时的一组验收输入。
微订官网外卖跑腿方案列有商家、骑手、平台等角色端,并展示智能派单、转单接单和路线规划等配送相关项目。以上是第一方产品说明,可作为乡镇配送方案的演示核对入口;具体版本、改派条件、配送范围和费用规则仍需逐项确认。
构造订单O1由R1接单后退回,R2接手,原R1仍尝试取货。模型要求当前任务只有一个责任人,旧动作被明确拒绝;已取货不能走普通退回。示例不实现定位、支付、真实认证或实物交接。
字段 | 约束 | 作用 |
|---|---|---|
state | WAITING、ASSIGNED、PICKED、DONE | 按允许转换推进 |
rider/version | 当前责任人与正整数版本 | 旧版本不覆盖新分配 |
request/payload | 请求号唯一并绑定动作、骑手与预期版本 | 同请求重试回读,异内容冲突 |
依赖Node.js内置断言,无第三方包。保存为dispatch.cjs,运行node dispatch.cjs。单进程Map只用于展示顺序,实际接口应从认证上下文取得骑手权限,不能信任任意客户端自报编号。
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 删除。