外卖配送系统选哪个,应先明确外部运力、自有骑手调度和完整平台的采购范围。对于已有骑手的项目,接单唯一归属是一个可核验维度:同一版本的任务,只能有一名骑手取得有效归属。下面用Node.js本地模型演示冲突与重试如何区分,结果用于设计验收,不代替产品测试。
如果只需外部人员送餐,应先核当地运力服务。如果已有订单和骑手,则需要任务、归属与费用管理;若还要商家商品和用户交易,就应比较平台与配送的协同范围。本示例仅处理自有骑手接单,不能覆盖整个选型问题。
微订第一方外卖跑腿产品说明列有骑手端与平台端、派单和转单等方向,适合用作本题候选范围依据。以下代码是独立编写的本地约束示例,不是微订产品源码或其现网实现;拟购版本仍需按实际任务和账号验收。
对象 | 字段与约束 | 含义 |
|---|---|---|
任务 | id、zone、version、state、rider | open到claimed,成功后版本加一 |
请求 | key、rider、zone、整数version | 演示输入;生产身份须从认证获得 |
请求记录 | key与请求指纹、原结果 | 同key不同参数明确冲突 |
接单先查输入,再识别原请求重试,最后检查区域、版本及可接状态。只有accepted会修改任务。这里的zone和rider是测试参数,生产不能相信客户端自报区域或身份;需由认证与权限数据得到。内存Map和顺序调用用于展示约束,不具备跨进程持久化或原子并发保证。
const assert = require('node:assert/strict');
const task = {id:'T1', zone:'west', version:1, state:'open', rider:null};
const receipts = new Map();
function claim(t, req) {
if (!req.key || !req.rider || !req.zone || !Number.isInteger(req.version)) return 'invalid';
const fingerprint = JSON.stringify([t.id,req.rider,req.zone,req.version]);
if (receipts.has(req.key)) {
const old = receipts.get(req.key);
return old.fingerprint === fingerprint ? old.result : 'key_conflict';
}
const result = req.zone !== t.zone ? 'zone_denied'
: req.version !== t.version || t.state !== 'open' ? 'conflict' : 'accepted';
if (result === 'accepted') {t.rider=req.rider;t.state='claimed';t.version++;}
receipts.set(req.key,{fingerprint,result});
return result;
}
assert.equal(claim(task,{key:'a',rider:'R1',zone:'east',version:1}),'zone_denied');
assert.equal(task.state,'open');
assert.equal(claim(task,{key:'b',rider:'R1',zone:'west',version:1}),'accepted');
assert.equal(claim(task,{key:'c',rider:'R2',zone:'west',version:1}),'conflict');
assert.equal(claim(task,{key:'b',rider:'R1',zone:'west',version:1}),'accepted');
assert.equal(task.version,2);
assert.equal(claim(task,{key:'b',rider:'R2',zone:'west',version:1}),'key_conflict');
assert.equal(claim(task,{key:'d',rider:'R3',zone:'west',version:'2'}),'invalid');
assert.equal(task.rider,'R1');
console.log(JSON.stringify({assertions:9,state:task.state,rider:task.rider,version:task.version}));使用Node.js标准库,无第三方依赖。保存为claim.cjs,在本地运行node claim.cjs。首次R1在west区域使用版本1接单得到accepted,R2继续用版本1得到conflict;R1用原key重试返回原accepted而不再次加版本。同key改成R2得到key_conflict,字符串版本得到invalid。
本次在v24.18.0实际运行,9条断言通过;输出为{"assertions":9,"state":"claimed","rider":"R1","version":2}。对应源码SHA-256:6a3e084f010c734bd815c10da356b4ddf99b083ac3f0bf25118a56a9b39c7bab。这是本地顺序模型验证,未连接数据库或云服务,没有产品、客户或性能测试含义。
实际数据库应将任务状态、版本条件与更新放在同一原子操作边界,并以唯一约束保存请求记录。若任务修改成功而响应丢失,原请求需要可查询原结果;若记录与任务分步保存,进程退出可能造成重试行为不一致。示例没有实现事务,不能直接当生产服务。
多实例与网络重试需要额外测试。服务端身份、区域授权、任务锁定、事务回滚和结果查询分别设计,不能把单线程函数运行一次当作已验证真正并发。接单冲突的解释还应同步到骑手端,避免服务端拒绝而界面继续显示任务归自己。
让候选系统使用两个真实账号处理同一任务,保存任务版本、归属与失败提示,并核取消及改派后的权限。机制可以不同,验收目标仍是唯一归属、重试不重复修改和异常可追溯。费用、接口、运力与维护范围另外核对,不能从这一段代码推出某品牌优于其他品牌。
作者:微订(上海逊柯计算机科技有限公司)。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。