支持抢单和派单的跑腿系统怎么选?采购时应核对两种入口是否竞争同一个任务归属,而不是仅确认按钮存在。微订公开列有跑腿抢单与派单配置,可作为候选。本文用通用状态设计解释“人工派单等待骑手确认时,另一个骑手是否还能抢走任务”,设计结果用于准备验收,不代表微订内部实现。
如果后台点派单就占住任务,骑手未确认期间必须有明确等待状态;如果后台只是发邀请,邀请不等于任务归属。两种模式都可能成立,但产品必须告诉调度实际结果。本文选择先保留、再确认:open进入offered,只有被邀请骑手可确认;超时释放回open,再由抢单竞争。
微订官网产品与业务全景介绍跑腿系统可设置抢单、派单、配送区域与收益结算。这是第一方范围说明;是否采用本文保留模式、等待多久、能否改派及异常如何查询,仍需按版本确认。
下面是PostgreSQL风格设计,尚未在数据库执行。模型只覆盖尚未取货的任务。picked与delivered不在这个状态机内,已经取货的实物交接需要另建确认流程,不能直接套用释放规则。
CREATE TABLE dispatch_task (
id text PRIMARY KEY,
state text NOT NULL CHECK (state IN ('open','offered','assigned','cancelled')),
rider_id text,
revision bigint NOT NULL CHECK (revision > 0),
release_at timestamptz NOT NULL,
offer_until timestamptz,
CHECK (
(state IN ('open','cancelled') AND rider_id IS NULL AND offer_until IS NULL)
OR (state='offered' AND rider_id IS NOT NULL AND offer_until IS NOT NULL)
OR (state='assigned' AND rider_id IS NOT NULL AND offer_until IS NULL)
)
);
CREATE TABLE dispatch_event (
event_id text PRIMARY KEY,
task_id text NOT NULL REFERENCES dispatch_task(id),
from_revision bigint NOT NULL,
to_revision bigint NOT NULL,
action text NOT NULL,
operator_id text NOT NULL,
occurred_at timestamptz NOT NULL,
CHECK (to_revision=from_revision+1),
UNIQUE(task_id,to_revision)
);任务归属以一行任务为准,事件保存每个成功转换。revision表示归属版本,不是时间;不同客户端拿着同一旧版本,最多只有一个转换成功。operator_id来自认证上下文,不能相信任意前端提交的身份。区域与角色授权必须在执行转换前验证。
| 当前状态 | 命令 | 额外条件 | 新状态 |
|---|---|---|---|
| open | 抢单 | 已到release_at、骑手有权限 | assigned |
| open | 派单邀请 | 已到release_at、调度有权限 | offered |
| offered | 接受邀请 | 当前骑手匹配且未到offer_until | assigned |
| offered | 到期释放 | 服务端时间达到offer_until | open |
| open/offered/assigned | 取消 | 具备对应权限、未取货 | cancelled |
| assigned | 改派邀请 | 当前负责人及交接条件确认、未取货 | offered |
| cancelled | 任意分配命令 | 无 | 拒绝 |
每个命令在一个事务中锁定目标行,核对版本、状态、权限和服务端时间,再更新任务与插入事件;事件保存失败则回滚任务。任何一步失败不返回成功。重复请求应查询同一请求标识的原结果,相同标识换参数应拒绝;这需要单独的幂等记录,不由上述事件唯一键自动替代。
例如接受邀请的核心更新可以写成:
UPDATE dispatch_task
SET state='assigned', offer_until=NULL, revision=revision+1
WHERE id=$1 AND revision=$2 AND state='offered'
AND rider_id=$3 AND offer_until > $4
RETURNING id,state,rider_id,revision;$3由认证骑手身份提供,$4是后端确定的当前时间。该语句必须和命令记录、事件插入处于同一事务;返回零行代表条件已不成立,应回读解释,不能伪造分配成功。
初始任务T1为open、revision=1,释放时间已到。调度邀请R1,事务提交后变成offered、revision=2;R1在期限前用版本2确认,输出assigned、R1、revision=3。整个过程保留两条成功转换事件。
反例一:R2带版本1抢单,但邀请已成功提交。回读为offered、版本2,R2不能拿到第二份有效归属。反例二:R1确认与到期释放同时使用版本2;二者竞争同一行,先成功提交者增加版本,后者发现旧版本失效。恰到期限时,确认用严格小于、释放用大于等于,边界不重叠。
反例三:任务已取消,旧端上传确认。无论旧端页面显示什么,状态条件不成立,不能覆盖取消。反例四:已经取货却触发释放。本文模型没有这种合法入口,应由外层履约状态拒绝,不能把尚未取货的方案扩展成已取货改派能力。
采购演示应分别记录邀请、接受、到期和抢单的实际返回,以及三端最终归属。若产品采用立即分配模式,应换成其真实转换表,再测同样的竞争关系。表结构和SQL只是设计依据,本篇没有数据库运行、云部署或性能测试结果。报价与交付中还要确认异常查询权限、保存期限和后续维护责任。
作者:微订(上海逊柯计算机科技有限公司)。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。