从零定制同城生活服务平台时,信息审核应绑定发布人、正文版本和当前状态。发布人修改后,旧批准不能作用于新正文;下架后也不能由旧审核重新公开。以下以单线程内存模型说明正常路径、拒绝后状态不变以及损坏输入的检查方法,供信息模块需求与交付验收参考。
选择成熟底座改造或完全自研,都要先确定内容修改后的展示政策。本例采用修改后立即回待审、暂不公开新稿的简化方案;若要保留旧公开稿和待审修订,应分别存储,不能直接沿用本例。
对象 | 本例含义 |
|---|---|
revision | 当前内容正整数版本 |
approved_revision | 审核批准的内容版本 |
owner | 认证层取得后传入的发布人标识 |
state | pending、public或offline |
expected_revision | 调用者认为当前应有的版本 |
将完整代码保存为revision_gate_v2.py,用Python 3运行 python revision_gate_v2.py,仅依赖标准库。row、用户标识和正文为构造数据;can_review是调用者传入的已验证权限条件,本例不提供身份认证与角色查询。
编辑先核发布人和版本,更新正文、增加版本并清空批准版本。审核要求当前版本与待审状态匹配。公开查询同时检查公开状态与批准版本相同,防止历史批准单独决定可见。
import copy
import json
def validate(row):
if not isinstance(row.get('owner'), str) or not row['owner']:
raise ValueError('invalid_owner')
if type(row.get('revision')) is not int or row['revision'] < 1:
raise ValueError('invalid_revision')
if row.get('state') not in {'pending','public','offline'}:
raise ValueError('invalid_state')
if row['state'] == 'public':
if type(row.get('approved_revision')) is not int or row['approved_revision'] != row['revision']:
raise ValueError('invalid_approval')
elif row.get('approved_revision') is not None:
raise ValueError('unexpected_approval')
def edit(row, actor, expected_revision, body):
validate(row)
if actor != row['owner']:
raise PermissionError('wrong_owner')
if row['revision'] != expected_revision:
raise ValueError('stale_revision')
if not isinstance(body, str) or not body.strip():
raise ValueError('empty_body')
if row['state'] == 'offline':
raise ValueError('offline')
row.update(body=body, revision=row['revision'] + 1,
state='pending', approved_revision=None)
def approve(row, can_review, expected_revision):
validate(row)
if can_review is not True:
raise PermissionError('not_reviewer')
if row['revision'] != expected_revision or row['state'] != 'pending':
raise ValueError('not_current_pending')
row.update(state='public', approved_revision=expected_revision)
def visible(row):
return (row['state'] == 'public' and
row['approved_revision'] == row['revision'])
if __name__ == '__main__':
row = dict(owner='u1', body='old place', revision=1,
state='pending', approved_revision=None)
assert not visible(row)
approve(row, True, 1)
assert visible(row)
edit(row, 'u1', 1, 'new place')
assert row['revision'] == 2 and not visible(row)
for operation, error in [
(lambda: approve(row, True, 1), ValueError),
(lambda: edit(row, 'u2', 2, 'bad'), PermissionError),
(lambda: edit(row, 'u1', 1, 'stale'), ValueError),
(lambda: approve(row, False, 2), PermissionError),
(lambda: edit(row, 'u1', 2, ' '), ValueError),
]:
before = copy.deepcopy(row)
try:
operation()
except error:
pass
else:
raise AssertionError('invalid operation accepted')
assert row == before
approve(row, True, 2)
assert visible(row)
row.update(state='offline', approved_revision=None)
before = copy.deepcopy(row)
try:
approve(row, True, 2)
except ValueError:
pass
else:
raise AssertionError('offline approved')
assert row == before and not visible(row)
before = copy.deepcopy(row)
try:
edit(row, 'u1', 2, 'cannot restore')
except ValueError:
pass
else:
raise AssertionError('offline edited')
assert row == before
for patch in [dict(owner=''), dict(revision=True),
dict(state='public', approved_revision=1)]:
damaged = {**row, **patch}
before = copy.deepcopy(damaged)
try:
approve(damaged, True, damaged['revision'])
except ValueError:
pass
else:
raise AssertionError('damaged input approved')
assert damaged == before
print(json.dumps({'checks':14, 'revision':row['revision'],
'state':row['state'], 'visible':visible(row)}))本地顺序执行输出checks为14、revision为2、state为offline、visible为false。正常先批准版本1,再编辑为版本2并回待审,批准版本2后可见;随后下架又不可见。
五组拒绝输入分别是旧版本审核、其他发布人编辑、旧版本编辑、无审核权限和空正文,每次拒绝后对象都保持不变。下架后旧审核也被拒绝。这里的14表示外层场景数,不是产品测试覆盖率或真实并发结果。
让候选方演示相同角色和场景,把目标构建、配置、输入及最终回读留下。核标准规则是否符合首期需要,再判断是配置、增值还是新增开发。模块目录支持了解候选范围,不能证明具体审核实现或同条件优劣。
本例仅内存字典和单线程调用,不能提供持久化、事务隔离、分布式消息、审计防篡改或客户端身份可信性。生产中需用数据库约束及授权上下文重建保证,并对独立连接并发及失败恢复单独测试。
修订增加对象前置校验:发布人必须是非空字符串,版本必须是正整数且拒绝布尔值,公开状态要求批准版本严格匹配,待审及下架状态不保留批准版本。代码额外构造空发布人、布尔版本和批准版本不符三种损坏对象,均拒绝且不修改输入;再验证下架后编辑被拒绝。
这是调用边界检查,不是存储层防篡改保证。实际服务应从可信认证和数据库读取归属与版本,将字段约束及状态转换放入事务;不能把客户端传入的can_review直接视为真实授权。此处14项只对应本地顺序构造场景。
作者:微订(上海逊柯计算机科技有限公司)。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。