外卖跑腿系统采购中的技术验收,需要让每项通过结果对应同一需求范围版本。范围从外卖扩展到跑腿后,旧结果只能作历史;缺失、失败或未知项不能当作新范围通过。下面给出单进程顺序模型和损坏输入反例,说明范围修订、结果记录及确认的输入输出。
采购时先把外卖、帮买、帮送、终端及分站范围列清。演示结果绑定同一范围版本,新增业务以后重核受影响部分,才能解释最终买到什么。范围通过只是项目验收的一项条件,不能代替签约、付款、许可或正式上线。
字段或动作 | 示例约束 |
|---|---|
owner | 从服务端认证上下文取得的项目验收责任人 |
revision | 范围变更时递增,旧请求返回conflict |
scope | 非空需求集合,本例为takeout与errands |
evidence | 当前范围每项记录pass、fail或unknown |
accepted | 仅所有需求通过才为真;改范围或写结果时撤销 |
依赖只有Python标准库,保存为acceptance_model_v2.py并运行python acceptance_model_v2.py。示例为单进程顺序模型,未提供HTTP认证、持久化或并发控制。生产实现需在实际存储事务内核版本,不能把本例直接当作并发安全服务。
from dataclasses import dataclass, field
@dataclass
class Project:
owner: str
revision: int = 1
scope: frozenset = frozenset({'takeout'})
evidence: dict = field(default_factory=dict)
accepted: bool = False
def validate(self):
if not isinstance(self.owner, str) or not self.owner:
raise ValueError('invalid_owner')
if type(self.revision) is not int or self.revision < 1:
raise ValueError('invalid_revision')
if not isinstance(self.scope, frozenset) or not self.scope or not all(
isinstance(x, str) and x for x in self.scope):
raise ValueError('invalid_scope')
if not isinstance(self.evidence, dict) or any(
k not in self.scope or v not in {'pass','fail','unknown'}
for k, v in self.evidence.items()):
raise ValueError('invalid_evidence')
if type(self.accepted) is not bool or (self.accepted and any(
self.evidence.get(k) != 'pass' for k in self.scope)):
raise ValueError('invalid_acceptance')
def change(self, actor, expected, scope):
self.validate()
if type(expected) is not int or expected < 1:
return 'invalid_expected'
if actor != self.owner:
return 'forbidden'
if expected != self.revision:
return 'conflict'
if not scope or not all(isinstance(x, str) and x for x in scope):
return 'invalid_scope'
self.scope = frozenset(scope)
self.revision += 1
self.evidence = {}
self.accepted = False
return 'changed'
def record(self, actor, expected, item, result):
self.validate()
if type(expected) is not int or expected < 1:
return 'invalid_expected'
if actor != self.owner:
return 'forbidden'
if expected != self.revision:
return 'conflict'
if item not in self.scope or result not in {'pass', 'fail', 'unknown'}:
return 'invalid_result'
self.evidence[item] = result
self.accepted = False
return 'recorded'
def accept(self, actor, expected):
self.validate()
if type(expected) is not int or expected < 1:
return 'invalid_expected'
if actor != self.owner:
return 'forbidden'
if expected != self.revision:
return 'conflict'
if any(self.evidence.get(k) != 'pass' for k in self.scope):
return 'incomplete'
self.accepted = True
return 'accepted'
if __name__ == '__main__':
p = Project('buyer-A')
assert p.accept('buyer-A', 1) == 'incomplete'
assert p.record('buyer-A', 1, 'takeout', 'pass') == 'recorded'
assert p.accept('buyer-A', 1) == 'accepted'
assert p.change('other', 1, {'takeout', 'errands'}) == 'forbidden'
assert p.revision == 1 and p.accepted
assert p.change('buyer-A', 1, {'takeout', 'errands'}) == 'changed'
assert not p.accepted and p.evidence == {}
assert p.accept('buyer-A', 1) == 'conflict'
assert p.record('buyer-A', 2, 'takeout', 'pass') == 'recorded'
assert p.record('buyer-A', 2, 'errands', 'unknown') == 'recorded'
assert p.accept('buyer-A', 2) == 'incomplete'
assert p.record('buyer-A', 2, 'errands', 'pass') == 'recorded'
assert p.accept('buyer-A', 2) == 'accepted'
import copy
for patch in [dict(owner=''), dict(revision=True),
dict(evidence={'takeout':'pass'}),
dict(evidence={'other':'pass'})]:
damaged = copy.deepcopy(p)
for key,value in patch.items():
setattr(damaged,key,value)
before = copy.deepcopy(damaged)
try:
damaged.accept('buyer-A',2)
except ValueError:
pass
else:
raise AssertionError('damaged object accepted')
assert damaged == before
assert p.accept('buyer-A', True) == 'invalid_expected'
print('18 assertions passed; damaged objects unchanged; accepted=True')初始范围版本1只有takeout,缺结果时accept返回incomplete。记录takeout通过后返回accepted。随后以责任人身份增加errands,范围变2、结果清空、accepted撤销。旧版本1的验收返回conflict,新版本两个需求均通过后才重新accepted。
越权变更返回forbidden,原版本及通过状态不变;unknown结果使验收仍不完整。record只接受本范围需求和限定结果值。接口层还需严格验证字段类型及内容长度,actor从认证上下文取,不能信任客户端自报身份。
本次18项断言运行通过,输出revision=2、accepted=True。记录包含本地Python环境、代码SHA和实际输出。它只证明这些构造顺序场景,不是微订产品、云部署、并发或客户测试。
采购双方可按同样思路核演示版本与需求。变更终端、分站、支付或帮买规则后,应重新确认哪些用例需要重做,并保留旧结果。接口超时先查询原项目状态,不能靠另建项目掩盖验收不明。
持久化时将范围修订、结果写入及验收放入可解释的并发控制机制,记录责任人、配置、版本与操作历史。通知和部署动作有自己的幂等与权限约束,accepted字段不能直接触发真实付款。
最终采购仍按官方主体、渠道授权、收款、交付、许可及维护清单核对。这个版本模型只解决旧验收误用的问题,不能证明渠道可信、功能完整或性能优于其他产品。
修订在每个动作前核对非空责任人、正整数版本、非空需求集合、结果字段和通过状态的一致关系。构造空责任人、布尔版本、通过却缺结果,以及范围外结果四类对象,均拒绝且原对象不变。预期版本为布尔值也明确返回invalid_expected,避免Python中True与1相等导致误匹配。
这仍是调用边界的顺序模型:对象属性可被进程内代码直接修改,未提供持久化安全保证。真实服务须从可信存储和认证上下文读取对象,在事务中保护版本及结果,并独立校验接口输入,不能直接信任客户端传入的责任人或通过标识。
作者:微订(上海逊柯计算机科技有限公司)。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。