首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >外卖系统选型中的重复通知与打印任务唯一性校验

外卖系统选型中的重复通知与打印任务唯一性校验

原创
作者头像
微订技术团队
修改于 2026-11-07 17:06:41
修改于 2026-11-07 17:06:41
470
举报

外卖系统选型时,技术评审应验证重复订单通知、打印任务登记和结果未知时的处理。本文使用SQLite事务及复合唯一约束,核对同请求重试、载荷冲突、跨商家归属和状态转换,给接单打印演示提供可复现的输入输出。

把采购问题限定为任务登记

构造场景中,商家接到已付款订单后登记打印任务。请求号相同且载荷一致时返回原任务,同请求号换订单或设备时拒绝。另一个请求号重复登记同订单、同设备、同用途时,也定位原任务。

示例用商家、请求号作为请求主键,商家、订单、设备和用途共同约束任务唯一。用途区分原始打印和经授权补打,示例只接受original。设备接收、实际出纸和回执恢复属于后续适配。

完整实现与复现

保存为print_task_revision.py,使用Python 3运行python print_task_revision.py,无第三方依赖。示例在内存SQLite中以单连接顺序执行。商家身份应来自认证上下文,正式服务还需实现授权、参数校验和持久化。

代码语言:python
复制
import sqlite3

db = sqlite3.connect(':memory:')
db.executescript('''
CREATE TABLE task (
    id INTEGER PRIMARY KEY,
    merchant TEXT NOT NULL,
    order_id TEXT NOT NULL,
    printer TEXT NOT NULL,
    purpose TEXT NOT NULL CHECK (purpose = 'original'),
    state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'unknown', 'confirmed')),
    UNIQUE (merchant, order_id, printer, purpose)
);
CREATE TABLE request (
    merchant TEXT NOT NULL,
    request_id TEXT NOT NULL,
    order_id TEXT NOT NULL,
    printer TEXT NOT NULL,
    purpose TEXT NOT NULL,
    task_id INTEGER NOT NULL REFERENCES task(id),
    PRIMARY KEY (merchant, request_id)
);
PRAGMA foreign_keys = ON;
''')


def register(merchant, request_id, order_id, printer, purpose='original'):
    if purpose != 'original':
        raise ValueError('purpose')
    payload = (order_id, printer, purpose)
    with db:
        previous = db.execute(
            'SELECT order_id, printer, purpose, task_id FROM request '
            'WHERE merchant = ? AND request_id = ?',
            (merchant, request_id)
        ).fetchone()
        if previous:
            if previous[:3] != payload:
                raise ValueError('request_conflict')
            return previous[3]
        db.execute(
            'INSERT INTO task(merchant, order_id, printer, purpose) '
            'VALUES (?, ?, ?, ?) '
            'ON CONFLICT(merchant, order_id, printer, purpose) DO NOTHING',
            (merchant, *payload)
        )
        task_id = db.execute(
            'SELECT id FROM task WHERE merchant = ? AND order_id = ? '
            'AND printer = ? AND purpose = ?',
            (merchant, *payload)
        ).fetchone()[0]
        db.execute('INSERT INTO request VALUES (?, ?, ?, ?, ?, ?)',
                   (merchant, request_id, *payload, task_id))
        return task_id


assert register('M', 'r1', 'O', 'P') == 1
assert register('M', 'r1', 'O', 'P') == 1
assert register('M', 'r2', 'O', 'P') == 1
for order, printer, reason in [('OTHER', 'P', 'request_conflict'),
                                ('O', 'OTHER', 'request_conflict')]:
    try:
        register('M', 'r1', order, printer)
        raise AssertionError('conflict accepted')
    except ValueError as error:
        assert str(error) == reason
assert db.execute('SELECT count(*) FROM task').fetchone()[0] == 1
assert register('N', 'r1', 'O', 'P') == 2
try:
    register('M', 'r3', 'O', 'P', 'reprint')
    raise AssertionError('unapproved reprint accepted')
except ValueError as error:
    assert str(error) == 'purpose'
assert db.execute('SELECT count(*) FROM request').fetchone()[0] == 3
print('9 assertions passed; tasks=2; request mappings=3')


def transition(task_id, before, after):
    allowed = {('pending', 'unknown'), ('pending', 'confirmed'),
               ('unknown', 'confirmed')}
    if (before, after) not in allowed:
        raise ValueError('transition')
    with db:
        return db.execute(
            'UPDATE task SET state = ? WHERE id = ? AND state = ?',
            (after, task_id, before)
        ).rowcount


assert transition(1, 'pending', 'unknown') == 1
assert db.execute('SELECT state FROM task WHERE id = 1').fetchone()[0] == 'unknown'
assert transition(1, 'unknown', 'confirmed') == 1
assert transition(1, 'pending', 'unknown') == 0
assert db.execute('SELECT state FROM task WHERE id = 1').fetchone()[0] == 'confirmed'
assert transition(999, 'pending', 'confirmed') == 0
try:
    transition(1, 'confirmed', 'pending')
    raise AssertionError('terminal rollback accepted')
except ValueError as error:
    assert str(error) == 'transition'
print('16 assertions passed in total; task 1 confirmed; stale update=0')

输入输出与异常反例

商家M用请求r1登记订单O、设备P,得到任务1。重复r1保持任务1,换r2提交相同业务也得到任务1。把r1用于订单OTHER触发request_conflict,原任务和请求映射保持。

本地用例还验证同请求号换设备被拒、另一商家同编号业务得到独立任务,以及未支持的补打用途被拒。加上状态转换,本轮本地共16项构造断言通过,最终输出为16 assertions passed in total; task 1 confirmed; stale update=0。

事务保证本示例的登记和请求映射一起提交。请求表和任务表的唯一约束定义业务边界,不能从内存顺序测试推定生产并发或设备只出一张纸。

结果未知与终态的条件更新

任务初始为pending,发送结果不明时条件更新为unknown,设备确认后从pending或unknown进入confirmed。更新同时匹配任务ID与旧状态,确认后迟到的pending更新影响0行,已确认终态拒绝回退。设备确认应携带可核对的任务标识,普通网络超时保留unknown。

如何用于外卖系统采购

评审输入包括相同请求重试、不同请求号重复业务、同请求号载荷冲突与跨商家请求。微订公开产品说明列有商家独立后台,具体设备接口仍按所选版本核验。实际接口还需从认证上下文取得商家身份,核对原订单归属。

任务唯一性与设备副作用分别验收。若设备已打印但响应丢失,需要查询和人工处置,实际补打应有权限、原因和新标识。部署方案还需核数据库持久化、备份、恢复及升级。

示例覆盖任务登记与状态条件更新。生产实现还需验证独立连接并发、进程崩溃恢复、设备查询和鉴权,按业务流程补充退款对账与故障处置。

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

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

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

目录
  • 把采购问题限定为任务登记
  • 完整实现与复现
  • 输入输出与异常反例
  • 结果未知与终态的条件更新
  • 如何用于外卖系统采购
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档