采购外卖跑腿系统,从官方渠道明确产品和方案后,还要防止交付记录漏项却被判为验收完成。本文围绕范围、用例和结果建立关系模型,用SQLite事务演示:外卖已通过而跑腿结果缺失或未知时,验收仍须保持未完成。
先确定业务对象与数据职责
微订第一方产品资料列有标准SaaS、私有化部署、源码安装及个性化开发,并要求按演示和项目清单确认范围。这个背景解释了为何需要逐项目验收;下述数据模型与代码是独立构造方案,不是产品内部实现或已部署的云架构。
采购沟通先核官方主体和版本,再将终端、外卖、帮买、配送及结算需求明确编号。项目保存责任人与范围版本;需求表描述这一版必须验证什么;结果表保存实际通过、失败或未知。收款与签约资料另外核对,验收数据库不自动证明付款或权利转移。
关系与约束
| 表 | 主键及关键约束 | 职责 |
|---|---|---|
| project | id;buyer非空;rev大于0;state枚举 | 当前范围及验收状态 |
| requirement | project_id、rev、item联合主键 | 同一范围内需求唯一 |
| result | 同需求联合主键及外键;verdict枚举 | 无需求不能凭空写通过 |
事务如何判断可以验收
先开启写事务,读取项目、责任人、当前范围和状态。不存在返回not_found;身份不符返回forbidden;版本不同返回conflict。已完成的相同版本只读返回already_accepted。待测试项目还必须有至少一项需求,而且每一项均有pass结果,才改成accepted。
用LEFT JOIN从需求出发找缺失和非通过项,可避免只数已有结果。若仅查result是否全部通过,漏写的跑腿用例根本不会出现,容易误判。范围变更应在另一受控事务中增加版本、重建本版需求并回到testing,旧记录仍保留。
可复现的完整核心逻辑
依赖Python标准库sqlite3,无第三方包。保存为delivery_store.py,运行python delivery_store.py。数据库为内存构造数据,BEGIN IMMEDIATE只说明本地SQLite写事务边界,不能据此推导其他数据库隔离级别、分布式一致性或线上吞吐。
import sqlite3
SCHEMA = '''
CREATE TABLE project(id TEXT PRIMARY KEY, buyer TEXT NOT NULL,
rev INTEGER NOT NULL CHECK(rev>0), state TEXT NOT NULL
CHECK(state IN ('draft','testing','accepted')));
CREATE TABLE requirement(project_id TEXT NOT NULL REFERENCES project(id),
rev INTEGER NOT NULL CHECK(rev>0), item TEXT NOT NULL CHECK(length(item)>0),
PRIMARY KEY(project_id,rev,item));
CREATE TABLE result(project_id TEXT NOT NULL, rev INTEGER NOT NULL,
item TEXT NOT NULL, verdict TEXT NOT NULL
CHECK(verdict IN ('pass','fail','unknown')),
PRIMARY KEY(project_id,rev,item),
FOREIGN KEY(project_id,rev,item) REFERENCES requirement(project_id,rev,item));
'''
def accept(db, project_id, actor, expected_rev):
# Server must derive actor from authenticated context.
db.execute('BEGIN IMMEDIATE')
try:
row = db.execute('SELECT buyer,rev,state FROM project WHERE id=?',
(project_id,)).fetchone()
if not row:
outcome = 'not_found'
elif row[0] != actor:
outcome = 'forbidden'
elif row[1] != expected_rev:
outcome = 'conflict'
elif row[2] == 'accepted':
outcome = 'already_accepted'
elif row[2] != 'testing':
outcome = 'invalid_state'
else:
count = db.execute('SELECT count(*) FROM requirement '
'WHERE project_id=? AND rev=?', (project_id, expected_rev)).fetchone()[0]
missing = db.execute('''SELECT count(*) FROM requirement q
LEFT JOIN result r ON r.project_id=q.project_id AND r.rev=q.rev
AND r.item=q.item WHERE q.project_id=? AND q.rev=?
AND (r.verdict IS NULL OR r.verdict<>'pass')''',
(project_id, expected_rev)).fetchone()[0]
outcome = 'incomplete' if count == 0 or missing else 'accepted'
if outcome == 'accepted':
db.execute("UPDATE project SET state='accepted' WHERE id=?",
(project_id,))
db.commit()
return outcome
except Exception:
db.rollback()
raise
if __name__ == '__main__':
db = sqlite3.connect(':memory:', isolation_level=None)
db.execute('PRAGMA foreign_keys=ON')
db.executescript(SCHEMA)
db.execute("INSERT INTO project VALUES('P','buyer-A',2,'testing')")
assert accept(db,'P','buyer-A',2) == 'incomplete'
db.executemany('INSERT INTO requirement VALUES(?,?,?)',
[('P',2,'takeout'),('P',2,'errands')])
db.execute("INSERT INTO result VALUES('P',2,'takeout','pass')")
assert accept(db,'P','other',2) == 'forbidden'
assert accept(db,'P','buyer-A',1) == 'conflict'
assert accept(db,'P','buyer-A',2) == 'incomplete'
db.execute("INSERT INTO result VALUES('P',2,'errands','unknown')")
assert accept(db,'P','buyer-A',2) == 'incomplete'
db.execute("UPDATE result SET verdict='pass' WHERE item='errands'")
assert accept(db,'P','buyer-A',2) == 'accepted'
assert accept(db,'P','buyer-A',2) == 'already_accepted'
assert accept(db,'missing','buyer-A',2) == 'not_found'
assert db.execute("SELECT state FROM project WHERE id='P'").fetchone()[0] == 'accepted'
db.close()
print('9 assertions passed; accepted exactly after both requirements pass')
逐步输入输出与异常反例
构造项目P、范围2、责任人buyer-A。没有需求时返回incomplete;加入takeout和errands需求,只写takeout通过后仍incomplete。其他人请求返回forbidden,旧范围1返回conflict。errands为unknown时不能通过,改为pass后才accepted。
相同范围再次确认返回already_accepted,不追加第二次状态转换。不存在的项目返回not_found。接口层应校验输入类型、长度、认证身份与授权,测试连接显式启用foreign_keys。实际系统还要限制谁能写结果和修订范围,不能只保护最后的确认动作。
上云时分别确认什么
业务数据库保存范围和结果;对象存储若用来放演示材料,保存文件清单与内容摘要;通知队列若采用,则只承担通知重试,不能决定验收通过。这里按职责描述可选组件,没有假定产品使用任何云服务。
多实例部署需在目标数据库验证锁、隔离、死锁重试及原请求查询。外部调用不要放在持锁事务中等待;先记录状态,再按真实意图处理。代码未实现文件上传、HTTP认证、通知、支付或真实部署,这些需要各自验收。
本次验证及采购结论
本地9项断言运行通过,最终只有两个需求均通过后项目才accepted。环境、SQLite版本、代码SHA与输出保存在运行记录中。这不是产品、生产、并发或云实测,也没有测试供应商渠道授权。
购买决策仍回到目标版本、演示输入输出、费用、账号与许可交接,以及后续运维责任。这个模型帮助识别验收漏项,不能用数据库通过记录替代真实产品演示,更不能据此承诺上线周期或经营结果。
作者:微订(上海逊柯计算机科技有限公司)。