跑腿系统稳定性要看恢复后是否还能解释原受理结果。微订官网跑腿和多角色说明是第一方产品背景。本文选择“系统重启后汇总相同,但一漏一重”的故障,给出截止批次明细核对模型,帮助采购方检查恢复证据,不声称微订采用本模型。
用一个具体故障回答稳定性问题
数据边界与云上职责
设计以SQLite语法说明,关系数据库保存测试批次、原受理明细与恢复快照。若选用RDS,可承担关系事务。对象存储可保留脱敏证据,消息服务只能通知核查,不代替明细一致性结论。是否采用这些云组件依实际方案决定。
批次从open进入sealed后固定截止与原受理集合,核查完成才进reviewed。恢复记录以snapshot_id和task_id唯一,保留tenant_id、金额分及状态。同一task_id但内容不符也要报差异。已受理未完成单允许waiting或assigned,不能统记丢失。未决结果保持在报告中。
数据模型与完整核心SQL
PRAGMA foreign_keys=ON;
CREATE TABLE test_batch(
batch_id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL,
cutoff TEXT NOT NULL, state TEXT NOT NULL
CHECK(state IN ('open','sealed','reviewed')),
UNIQUE(batch_id,tenant_id)
);
CREATE TABLE accepted_task(
batch_id TEXT NOT NULL, tenant_id TEXT NOT NULL,
task_id TEXT NOT NULL, cents INTEGER NOT NULL CHECK(cents>=0),
PRIMARY KEY(batch_id,task_id),
FOREIGN KEY(batch_id,tenant_id) REFERENCES test_batch(batch_id,tenant_id)
);
CREATE TRIGGER accept_open BEFORE INSERT ON accepted_task
WHEN NOT EXISTS(SELECT 1 FROM test_batch WHERE batch_id=NEW.batch_id
AND tenant_id=NEW.tenant_id AND state='open')
BEGIN SELECT RAISE(ABORT,'batch_not_open'); END;
CREATE TRIGGER accept_no_update BEFORE UPDATE ON accepted_task
BEGIN SELECT RAISE(ABORT,'immutable_acceptance'); END;
CREATE TRIGGER accept_no_delete BEFORE DELETE ON accepted_task
BEGIN SELECT RAISE(ABORT,'immutable_acceptance'); END;
CREATE TABLE restored_task(
snapshot_id TEXT NOT NULL, task_id TEXT NOT NULL,
tenant_id TEXT NOT NULL, cents INTEGER NOT NULL CHECK(cents>=0),
state TEXT NOT NULL CHECK(state IN ('waiting','assigned','done','cancelled')),
PRIMARY KEY(snapshot_id,task_id)
);
-- 同批受理登记和封存均由受控服务入口执行。
BEGIN IMMEDIATE;
UPDATE test_batch SET state='sealed'
WHERE batch_id=:batch AND state='open';
COMMIT;
-- 恢复快照仅导入该封存测试输入所对应的业务范围。
-- task_id按本示例假设全局唯一;快照不是生产全库的任意截取。
SELECT a.task_id,'missing_or_mismatch' AS issue
FROM accepted_task a JOIN test_batch b ON b.batch_id=a.batch_id
LEFT JOIN restored_task r ON r.snapshot_id=:snapshot AND r.task_id=a.task_id
WHERE a.batch_id=:batch AND b.state='sealed'
AND (r.task_id IS NULL OR r.tenant_id<>a.tenant_id OR r.cents<>a.cents)
UNION ALL
SELECT r.task_id,'unexpected' FROM restored_task r
WHERE r.snapshot_id=:snapshot AND NOT EXISTS(
SELECT 1 FROM accepted_task a WHERE a.batch_id=:batch AND a.task_id=r.task_id);
-- reviewed由授权复核人在保存差异及处理结论后标记;不自动修单。
UPDATE test_batch SET state='reviewed'
WHERE batch_id=:batch AND state='sealed';
完整步骤与反例
第一步登记批次B、租户A和截止时刻,open时受理T1/1000分与T2/2000分。第二步封存B,再导入同一测试范围的恢复快照S。若S只有T1和T3,两边数量都是二,查询仍预期列T2缺失、T3意外出现。T1金额变900分或租户变B,同样报mismatch。
第三步把差异与原受理证据、支付或退款回执逐项对应,指定复核人,不直接把差异单改成完成。核查记录保存后才转reviewed。封存后新增受理、改写或删除原明细应拒绝。重复快照任务键冲突应使导入事务回滚。快照缺行可能来自导出范围或采集失败,不能未经核对就定性为产品丢单。
约束范围和复现条件
这是一份未执行的SQLite设计。复现时需启用外键、在隔离数据库建表、按步骤导入固定样例,导入异常需回滚。实际快照必须固定截止和范围,封存数据与快照在服务层只读保护。示例SQL未证明并发压测、外部支付、快照可信性或云服务已通过。
test_batch和恢复表的写权限只给受控测试入口。封存后禁止旁路修改批次、截止、租户或快照。若要覆盖这些写入威胁,需补数据库权限、审计和不可变证据保存。采购结论应回到原单是否可找、差异是否解释、未决责任是否交班,不以一张汇总表宣称稳定。
恢复快照必须交代客户端和附件采集范围
采购方还应要求恢复报告分别给出业务任务、定位数据和凭证附件的覆盖范围。原模型只核对受理任务、租户和金额。若恢复快照没有照片字段,查询返回零差异只支持这些已列字段相同,照片恢复结果继续待验。
沿B批次增加一个有界验收步骤。封存时登记T1有待传附件P1、客户端构建版本C1,恢复采集开始和结束时分别记录服务端任务版本。若两次版本不同,把该任务列为采集中变化并重新取得一致证据,不能混用两个时间点拼成一个“恢复成功”记录。
预期正常输入是同一封存范围、同一任务版本,T1业务状态可查,P1按原任务补传后有可核对回执。异常输入为任务已取消但旧客户端仍试图提交送达,或P1内容摘要与原文件不符。前者按当前业务规则拒绝或复核,后者保留原附件并查明来源,均不通过修改原受理明细来消除差异。
复现设计时用隔离数据库执行前述建表与样例SQL,再由受控测试入口采集任务版本和附件回执。RDS等关系存储只承担所选方案内的事务职责,采集器仍需校验授权、范围和版本。本文新增步骤尚未执行,恢复快照、对象存储和真实微订版本的测试结果须分别留证。
作者:微订(上海逊柯计算机科技有限公司)。