首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >外卖跑腿系统选型:报价变更的版本条件更新与确认约束

外卖跑腿系统选型:报价变更的版本条件更新与确认约束

原创
作者头像
微订技术团队
修改于 2026-11-12 17:01:35
修改于 2026-11-12 17:01:35
110
举报

外卖跑腿系统选型中的报价变更,应核对版本与确认的绑定:改址或加购物品形成新报价后,旧页面只能得到版本冲突;当前报价确认与费用快照应在同一事务保存。下面用SQLite条件更新实现这条约束,并用两个数据库连接顺序复现旧版本写入被拒绝的情况。

把外卖与帮买的费用约定落实到记录

外卖商品款与配送服务费、帮买代购款与服务费应分别保留。示例假设B1为帮买单,goods是代购预算,service是服务费,金额使用整数最小货币单位。消费者改址后形成v2报价,确认时需对应v2的内容,不能仅凭总金额判断是否同一次约定。

这是通用业务实现示例。实际采购还需在目标版本中核对报价展示、身份权限、费用项和异常结果;本文代码没有接入任何产品或支付渠道,也不构成软件已通过验收的结论。

数据模型和事务边界

quote保存当前报价及offered、confirmed、cancelled状态;confirmation保存确认时的商品或代购款、服务费与版本快照,以订单和版本为复合主键。SQL约束拒绝非整数或负费用,更新时同时匹配订单、可信客户、revision和offered。

confirm的principal来自服务端认证中间件。客户端只提交订单和版本;示例使用固定身份替代认证系统。找不到订单返回404,他人订单返回403,过期版本或取消状态返回409,非法版本返回422。同一已确认版本返回原处理结果,不重复写快照。

完整可运行代码

代码语言:python
复制
import sqlite3,tempfile,json
from pathlib import Path
with tempfile.TemporaryDirectory() as folder:
    path=Path(folder)/'quote.db'
    a=sqlite3.connect(path); b=sqlite3.connect(path)
    a.executescript('''
    CREATE TABLE quote(id TEXT PRIMARY KEY, customer TEXT NOT NULL,
      revision INTEGER NOT NULL CHECK(typeof(revision)='integer' AND revision>0),
      goods INTEGER NOT NULL CHECK(typeof(goods)='integer' AND goods>=0),
      service INTEGER NOT NULL CHECK(typeof(service)='integer' AND service>=0),
      state TEXT NOT NULL CHECK(state IN ('offered','confirmed','cancelled')));
    CREATE TABLE confirmation(order_id TEXT NOT NULL, revision INTEGER NOT NULL,
      customer TEXT NOT NULL, goods INTEGER NOT NULL, service INTEGER NOT NULL,
      PRIMARY KEY(order_id,revision));
    INSERT INTO quote VALUES('B1','U1',1,3200,800,'offered');
    INSERT INTO quote VALUES('B2','U1',1,0,600,'cancelled');
    ''')
    def confirm(db,principal,order_id,revision):
        # principal is supplied by authenticated server middleware.
        if type(revision) is not int or revision<=0:return {'status':422}
        with db:
            row=db.execute('SELECT customer,revision,goods,service,state FROM quote WHERE id=?',
                           (order_id,)).fetchone()
            if row is None:return {'status':404}
            customer,current,goods,service,state=row
            if customer!=principal:return {'status':403}
            if current!=revision or state=='cancelled':return {'status':409}
            if state=='confirmed':return {'status':200,'duplicate':True}
            changed=db.execute('''UPDATE quote SET state='confirmed'
              WHERE id=? AND customer=? AND revision=? AND state='offered' ''',
              (order_id,principal,revision)).rowcount
            if changed!=1:return {'status':409}
            db.execute('INSERT INTO confirmation VALUES(?,?,?,?,?)',
                       (order_id,revision,principal,goods,service))
            return {'status':200,'duplicate':False}
    checks=0
    def check(actual,expected):
        global checks
        assert actual==expected,(actual,expected)
        checks+=1
    check(confirm(a,'U2','B1',1),{'status':403})
    check(confirm(a,'U1','missing',1),{'status':404})
    check(confirm(a,'U1','B1',True),{'status':422})
    check(confirm(a,'U1','B1',0),{'status':422})
    # Both clients read v1; a controlled proposal then publishes v2.
    old_version=b.execute("SELECT revision FROM quote WHERE id='B1'").fetchone()[0]
    with a:
        a.execute("UPDATE quote SET revision=2,service=1000 WHERE id='B1' AND revision=1")
    check(confirm(b,'U1','B1',old_version),{'status':409})
    check(a.execute('SELECT count(*) FROM confirmation').fetchone()[0],0)
    check(confirm(a,'U1','B1',2),{'status':200,'duplicate':False})
    check(confirm(b,'U1','B1',2),{'status':200,'duplicate':True})
    check(a.execute('SELECT count(*) FROM confirmation').fetchone()[0],1)
    check(a.execute('SELECT goods,service FROM confirmation').fetchone(),(3200,1000))
    check(confirm(a,'U1','B2',1),{'status':409})
    check(confirm(b,'U2','B1',2),{'status':403})
    try:
        with a:a.execute("UPDATE quote SET service=-1 WHERE id='B1'")
    except sqlite3.IntegrityError:constraint=True
    else:constraint=False
    check(constraint,True)
    check(a.execute("SELECT service FROM quote WHERE id='B1'").fetchone()[0],1000)
    a.close();b.close()
    print(json.dumps({'checks':checks,'staleWriter':409,'confirmations':1,
      'scope':'local sequential two-connection example; no production concurrency or payment'}))

复现步骤与真实本地输出

使用Python 3标准库sqlite3、tempfile、json和pathlib。保存代码为quote_version_sql.py,执行python quote_version_sql.py,无第三方依赖。程序在临时目录创建数据库,关闭两个连接后自动清理该示例数据库。

本轮运行通过14项检查,输出staleWriter=409、confirmations=1。连接b先读到v1,连接a将报价推进到v2,b再提交旧版被拒绝,确认记录仍为0。v2首次确认成功后,另一连接重试返回duplicate=true,确认快照只保留一份,费用为构造的3200与1000。

异常检查覆盖他人身份、缺失订单、布尔或零版本、取消状态和数据库负费用约束。负费用更新失败后事务回滚,原服务费仍为1000。所有金额与编号都是构造输入,不是产品售价、项目订单或真实支付结果。

条件更新解决什么,还需要补什么

先读再无条件写会留下版本竞争窗口。这里的UPDATE把revision和状态放进WHERE,只有影响1行才插入确认快照,二者共用事务。如果条件不成立返回冲突,调用方重新读取当前报价,不自动接受新版本。

生产中的报价提案也必须采用受控版本更新,并保存地址、物品要求及费用的不可变快照。示例仅复制费用字段,真实系统应把确认绑定到完整服务内容。订单状态与报价状态也应按业务合同区分,确认报价不自动等于已付款或已接单。

两个连接按明确顺序执行,只验证过期写入与重复确认,不声称已经通过同时竞争、分布式负载或线上数据库验证。SQLite用于本地复现,迁移到其他数据库需重新验证隔离级别、锁冲突、唯一键竞争和失败重试。

签名验证、支付到账、追加收款、退款和配送任务均不在此函数内。接入支付前应从持久化确认快照生成对应的收款请求,保留唯一交易关联。超时先查原交易,避免重复收费;取消后到账应另行对账,不以支付通知直接恢复任务。

用这组结果核对选型方案

在目标系统先提交一笔外卖与一笔帮买,再保留旧页面、改址生成新报价,分别记录旧确认拒绝、当前确认成功和重复请求的结果。要求能查到确认身份、报价版本和费用快照,对未通过项说明是配置、接口还是开发范围。

本地14项检查只支持本文实现和构造输入。采购方案的账号权限、跨端展示、异常处理人、在途账单、数据导出与后续维护仍需独立验证,不能把示例输出写成某一产品的生产测试。

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

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

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

目录
  • 把外卖与帮买的费用约定落实到记录
  • 数据模型和事务边界
  • 完整可运行代码
  • 复现步骤与真实本地输出
  • 条件更新解决什么,还需要补什么
  • 用这组结果核对选型方案
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档