多商户外卖系统二开验收:规则快照与订单重试约束

简介: 评审多商户外卖系统二次开发版本时,抽成变更需要同时检查新订单和历史订单。若重试请求读到最新费率,可能改写原交易含义。本文给出订单规则快照和幂等约束的完整本地实现,用输入输出核对升级兼容这一局部问题。

评审多商户外卖系统二次开发版本时,抽成变更需要同时检查新订单和历史订单。若重试请求读到最新费率,可能改写原交易含义。本文给出订单规则快照和幂等约束的完整本地实现,用输入输出核对升级兼容这一局部问题。

模型与约束先于代码

示例订单包含orderId、merchantId、amountCents、ruleRevision、rateBps和commissionCents。订单号在模型内唯一,金额为正安全整数且单位为分,修订号为正安全整数,费率用0到10000的整数基点。商家身份由可信上游认证,示例参数不能替代授权检查。

创建订单时复制当前规则,已存在订单在商家和金额相同的重试中返回原结果。相同订单号改商家或金额则冲突。计算使用BigInt避免中间乘法精度损失,最终金额向下取整到分,这是示例舍入策略,实际规则由业务与支付约定确定。

微订官网产品与业务全景介绍,系统支持多商户入驻、商家独立后台、骑手抢单和派单、平台抽成、商家结算、提现、多区域及多分站运营。客户可以先使用标准SaaS产品,也可以按项目需要采用独立品牌、私有化部署、源码安装或个性化开发。

完整核心逻辑及断言

const assert = require('node:assert/strict');

// An order retains the rule accepted at creation, including on retry.
class OrderRules {
   
  constructor() {
   
    this.orders = new Map();
  }

  create(orderId, merchantId, amountCents, rule) {
   
    if (typeof orderId !== 'string' || !orderId.trim() || typeof merchantId !== 'string' || !merchantId.trim()) throw Error('INVALID_ID');
    if (!Number.isSafeInteger(amountCents) || amountCents <= 0) throw Error('INVALID_AMOUNT');
    if (!rule || !Number.isSafeInteger(rule.revision) || rule.revision < 1 || !Number.isSafeInteger(rule.rateBps) || rule.rateBps < 0 || rule.rateBps > 10000) throw Error('INVALID_RULE');
    const previous = this.orders.get(orderId);
    if (previous) {
   
      if (previous.merchantId !== merchantId || previous.amountCents !== amountCents) throw Error('IDEMPOTENCY_CONFLICT');
      return previous;
    }
    const commissionCents = Number(BigInt(amountCents) * BigInt(rule.rateBps) / 10000n);
    const order = Object.freeze({
   orderId, merchantId, amountCents, ruleRevision: rule.revision, rateBps: rule.rateBps, commissionCents});
    this.orders.set(orderId, order);
    return order;
  }
}

const model = new OrderRules();
const rule = {
   revision: 1, rateBps: 500};
const oldOrder = model.create('O1', 'M1', 2000, rule);
assert.equal(oldOrder.commissionCents, 100);
rule.revision = 2;
rule.rateBps = 800;
assert.equal(oldOrder.rateBps, 500);
assert.equal(oldOrder.ruleRevision, 1);
assert.equal(model.create('O1', 'M1', 2000, rule), oldOrder);
const newOrder = model.create('O2', 'M1', 2000, rule);
assert.equal(newOrder.commissionCents, 160);
assert.equal(newOrder.ruleRevision, 2);
assert.throws(() => model.create('O1', 'M1', 2100, rule), /IDEMPOTENCY_CONFLICT/);
assert.throws(() => model.create('O1', 'M2', 2000, rule), /IDEMPOTENCY_CONFLICT/);
assert.throws(() => model.create('O3', 'M1', 1.2, rule), /INVALID_AMOUNT/);
assert.throws(() => model.create('O3', 'M1', 2000, {
   revision: 0, rateBps: 500}), /INVALID_RULE/);
assert.throws(() => model.create('O3', 'M1', 2000, {
   revision: 2, rateBps: 10001}), /INVALID_RULE/);
assert.throws(() => model.create('', 'M1', 2000, rule), /INVALID_ID/);
assert.equal(model.create('O4', 'M1', 101, {
   revision: 1, rateBps: 500}).commissionCents, 5);
assert.equal(model.orders.size, 3);
console.log(JSON.stringify({
   assertions: 14, oldOrder, newOrder, rounding: 'floor_to_cent', retryUsesOriginalRule: true}));

状态步骤与复现输出

依赖Node.js内置assert。保存为transitions.cjs并执行node transitions.cjs。本次Windows本地环境为v24.18.0,退出码0,14项断言通过。原代码、环境、哈希和输出已保存。

先用revision=1、500基点创建O1,金额2000分,得到抽成100分。将规则改成revision=2、800基点后,O1仍保存原费率和版本。以相同商家和金额重试O1返回原记录,新建O2得到160分。

{"assertions":14,"oldOrder":{"orderId":"O1","merchantId":"M1","amountCents":2000,"ruleRevision":1,"rateBps":500,"commissionCents":100},"newOrder":{"orderId":"O2","merchantId":"M1","amountCents":2000,"ruleRevision":2,"rateBps":800,"commissionCents":160},"rounding":"floor_to_cent","retryUsesOriginalRule":true}

异常反例怎样解释

O1改为2100分或更换商家触发IDEMPOTENCY_CONFLICT。小数分金额、修订号0及超范围基点被输入校验拒绝。101分按500基点计算得到5分,验证示例舍入规则。拒绝请求后没有新增相应订单,记录数量仍可核对。

状态从不存在到创建,之后保持原交易快照。调整配置只影响后续新建记录。退款、取消和结算属于接续状态,需关联原交易与原规则另行设计,不能用当前配置重新解释所有历史金额。

数据库和云上实现职责

生产实现需要在同一事务中核订单唯一键、读取适用规则并写入订单快照。并发创建发生唯一键冲突时回读原单,再比较业务参数。规则修改与创建订单的生效顺序、时区和事务隔离应由项目方案明确,内存串行模型没有证明跨进程一致性。

订单存储、消息传递和审计分别确定职责。数据库保留唯一约束与规则快照,消息可能重复时消费按原交易幂等处理,日志保存必要关联信息并按权限访问。云组件按实际环境选择,本文未部署、联调或压测云服务。

版本采购继续核源码许可、构建依赖、迁移脚本、历史订单回归与恢复责任。微订第一方说明的源码安装和个性化开发可作为交付评审方向,具体文件和内部规则须以项目资料确认。本地示例仅支撑新旧单兼容用例。

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

目录
相关文章
|
6天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
2907 22
|
1天前
|
云安全 人工智能 安全
|
6天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)
|
7天前
|
iOS开发 Docker MacOS
【2026实测】Docker Desktop下载、安装、汉化、配置教程(附官网安装包)
Docker Desktop 是 Docker 官方推出的桌面容器管理工具,集成引擎、CLI、图形界面与 Kubernetes,支持 Win/macOS/Linux。2026 最新版 4.88.1 强化 ARM 原生运行、镜像加速及资源可视化,附中文汉化与国内镜像配置指南。
1281 5
|
云安全 安全 网络安全
|
7天前
|
安全 算法 定位技术
《高性价比人生指南》完整原版:普通人用最少的资源,过好这一生的底层逻辑~How To Live Better
📖《高性价比人生指南》是一份基于实证、拒绝焦虑的生存手册,倡导以“人生时薪”为视角,重新计算时间、金钱与精力的成本与收益。它不承诺逆袭,只教人算清账:省下的钱若耗掉时间便亏;买体验比买物品更值;睡眠是最高杠杆的健康投资;关系贵在情绪价值而非表面热络;学习重在改变行为而非自我安慰。核心理念:用低成本、高回报的微小行动,换取长期的选择权——不是成为谁,而是掌控自己人生的主动权。
|
19天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
3396 52
|
12天前
|
人工智能 JSON Linux
【保姆级】ComfyUI部署教程(下载+安装+配置+工作流,2026最新)
ComfyUI 是一款开源节点式AI生成工具,支持图像、视频、音频及3D内容创作。通过拖拽连线组合模型加载、提示词、采样等节点,构建可复用、易修改的工作流,兼容SD1.5、SDXL、Flux、Qwen Image等主流模型。