微订外卖系统怎么样?准备采购时,可以先核对演示与报价是不是同一套模块,再检查真实业务是否跑通。本文处理一个具体故障:套餐已经变更,旧演示的成功结果仍被汇总成当前版本通过。下面给出可本地复现的范围冻结与记录校验示例;它只支持采购中的证据一致性判断,不代表产品性能或客户评价。
产品背景和业务边界
微订官网产品与业务全景介绍外卖平台及单店餐饮点单,价目页区分套餐和交付选项。具体终端、配送、资金和定制范围按项目确认。本文的字段、代码和验证数据均为独立通用示例,不是微订内部实现,也没有连接实际订单或客户账号。
采购流程可以按需求清单、报价范围、演示执行和签收组织。范围含项目、套餐、版本及模块;演示账号与配置应对应该范围。新增配送模块时创建新版本,不能靠修改旧字段继承历史通过结论。软件、第三方服务和实际运力的费用责任仍需另行确认。
模型与职责
示例把范围冻结在versions映射中,以项目和版本组合作为键;测试结果以项目、版本、用例组合为键。模块集合去重排序后计算摘要,顺序调整不产生假变化,同版本不同内容则拒绝。用例结果只接受布尔值,同一用例的冲突结果拒绝覆盖。必测清单不能为空,缺少任意结果时不能签收。
此核心实现使用Node.js内置crypto和assert,没有第三方依赖。内存存储用于本地演示,重启会丢失;生产应使用数据库事务、唯一约束、授权与不可变证据存储,不能把Map当作持久化或多进程并发保证。若上云,可由应用服务处理校验,数据库保存版本与事务记录,对象存储保存脱敏证据;具体组件按需要选择,本文未部署任何云服务。
完整可复现核心
保存为scope.cjs,在支持structuredClone的Node.js环境运行node scope.cjs。全部执行数据在脚本内构造,不读取账号或网络资料。
// Local constructed-data demonstration. No product or cloud connection.
const crypto = require('node:crypto');
const assert = require('node:assert/strict');
function normalize(scope) {
if (!scope || typeof scope.package !== 'string' || !scope.package.trim() ||
!Number.isInteger(scope.version) || scope.version < 1 ||
!Array.isArray(scope.modules) || !scope.modules.length ||
scope.modules.some(x => typeof x !== 'string' || !x.trim())) throw Error('invalid_scope');
return { package: scope.package, version: scope.version, modules: [...new Set(scope.modules)].sort() };
}
function digest(scope) {
return crypto.createHash('sha256').update(JSON.stringify(normalize(scope))).digest('hex');
}
function store() {
const versions = new Map(), cases = new Map();
const key = (project, version) => JSON.stringify([project, version]);
return {
freeze(project, scope) {
if (typeof project !== 'string' || !project.trim()) throw Error('invalid_project');
const value = normalize(scope), id = key(project, value.version), hash = digest(value);
if (versions.has(id)) {
if (versions.get(id).hash !== hash) throw Error('version_conflict');
return hash;
}
versions.set(id, { hash, scope: structuredClone(value) });
return hash;
},
record(project, version, caseId, module, hash, passed) {
const v = versions.get(key(project, version));
if (!v || v.hash !== hash) throw Error('scope_mismatch');
if (!v.scope.modules.includes(module)) throw Error('module_disabled');
if (typeof caseId !== 'string' || !caseId.trim() || typeof passed !== 'boolean') throw Error('invalid_case');
const id = JSON.stringify([project, version, caseId]), old = cases.get(id);
if (old && (old.module !== module || old.passed !== passed)) throw Error('case_conflict');
cases.set(id, { module, passed });
return old ? 'replayed' : 'recorded';
},
accept(project, version, hash, required) {
const v = versions.get(key(project, version));
if (!v || v.hash !== hash) throw Error('scope_mismatch');
if (!Array.isArray(required) || !required.length || required.some(x => typeof x !== 'string' || !x.trim())) throw Error('invalid_required');
for (const id of new Set(required)) {
const r = cases.get(JSON.stringify([project, version, id]));
if (!r || r.passed !== true) return false;
}
return true;
}
};
}
const s=store(), scope={package:'platform-demo',version:1,modules:['order','refund']};
const h=s.freeze('P1',scope);
assert.equal(s.freeze('P1',{...scope,modules:['refund','order','order']}),h);
assert.equal(s.record('P1',1,'order-ok','order',h,true),'recorded');
assert.equal(s.record('P1',1,'order-ok','order',h,true),'replayed');
assert.equal(s.accept('P1',1,h,['order-ok','refund-ok']),false);
assert.throws(()=>s.record('P1',1,'delivery-ok','delivery',h,true),/module_disabled/);
assert.throws(()=>s.freeze('P1',{...scope,modules:['order','delivery']}),/version_conflict/);
assert.throws(()=>s.record('P2',1,'order-ok','order',h,true),/scope_mismatch/);
assert.throws(()=>s.record('P1',1,'order-ok','order',h,false),/case_conflict/);
s.record('P1',1,'refund-ok','refund',h,true);
assert.equal(s.accept('P1',1,h,['order-ok','refund-ok']),true);
const h2=s.freeze('P1',{package:'platform-demo',version:2,modules:['order','refund','delivery']});
assert.equal(s.accept('P1',2,h2,['order-ok','refund-ok']),false);
assert.throws(()=>s.record('P1',2,'order-ok','order',h,true),/scope_mismatch/);
assert.throws(()=>s.accept('P1',1,h,[]),/invalid_required/);
console.log(JSON.stringify({scenarioChecks:12,normalAcceptance:true,newVersionAcceptance:false,unexecutedCaseBlocks:true,disabledModuleBlocks:true,versionConflictBlocks:true}));
正常输入输出与异常反例
P1/v1包含order和refund,写入order-ok后要求同时具备order-ok与refund-ok,返回false;补齐refund-ok后返回true。创建增加delivery的v2,同样的用例名仍返回false,因为v1记录不能跨版本继承。重放完全相同的记录返回replayed,不产生第二份结果。
反例包括:给未开通delivery的v1写配送用例,抛module_disabled;同一版本变更模块,抛version_conflict;把P1摘要用于未创建的P2,抛scope_mismatch;同一用例从true覆盖为false,抛case_conflict;必测集合为空,抛invalid_required。需要更正结果时,应设计追加执行批次及更正流程,而不是直接覆盖历史。
本地验证与验收含义
本次本机Node.js运行输出:
{"scenarioChecks":12,"normalAcceptance":true,"newVersionAcceptance":false,"unexecutedCaseBlocks":true,"disabledModuleBlocks":true,"versionConflictBlocks":true}
上述是本地构造数据验证,代码版本、Node环境和原始输出另存内部记录;没有进行微订产品、云环境、数据库并发或客户项目实测。必测用例表本身也需要授权管理,示例由调用方传入,尚不能防止调用方选择不完整清单;生产应在冻结范围中保存清单,并由服务端读取。
技术结果的业务意义是:可以识别旧结果被误用于新套餐的情况,但无法说明演示是否覆盖全部需求。评价微订时,还需把单店或平台模式、实际终端、退款结算、配送组织、费用和维护责任逐项签认;功能介绍、检查通过和长期运行结果分别判断。
作者:微订(上海逊柯计算机科技有限公司)。