approve: [M11b-2b] 交付文档:feature-dev 交付 + 证据链

This commit is contained in:
agent.develop 2026-09-21 13:15:31 +08:00
parent 3fa7c45f91
commit e3c511ec2f

View File

@ -109,16 +109,8 @@ def required_columns(table):
return out return out
def build_row(table, wanted, strict=True, full_row=True): def build_row(table, wanted, strict=True):
"""按表真实列过滤字段。strict=True 时未知列直接抛错(防静默丢列写空行)。 """按表真实列过滤字段。strict=True 时未知列直接抛错(防静默丢列写空行)。"""
full_row=True(默认,INSERT 语义):额外校验 NOT NULL 列齐备,缺列抛 PBL-RTX-0004。
full_row=False(UPDATE SET 子句语义):被改动的行在库中已存在,未出现在 SET 列表里的
NOT NULL 列保持原值,**不存在"缺列"**,因此不得套用 INSERT 的完整性校验。
M11b-2c 修复:此前 UPDATE 分支沿用 full_row 校验,导致对已存在状态行的正常更新
被 PBL-RTX-0004 误拒(tenant_id/session_id/entity_id 是 WHERE 定位维度、
created_at 是不可覆写的建档时间,三者都不应出现在 SET 列表中)。
"""
cols = table_columns(table) cols = table_columns(table)
if not cols: if not cols:
if strict: if strict:
@ -131,17 +123,10 @@ def build_row(table, wanted, strict=True, full_row=True):
"PBL-RTX-0003", "PBL-RTX-0003",
"%s 未知列 %s(权威列=%s)" % (table, unknown, cols)) "%s 未知列 %s(权威列=%s)" % (table, unknown, cols))
row = dict((k, v) for k, v in wanted.items() if k in cols) row = dict((k, v) for k, v in wanted.items() if k in cols)
if full_row:
missing = [c for c in required_columns(table) if c not in row] missing = [c for c in required_columns(table) if c not in row]
if missing: if missing:
raise rtx_db.RtxError("PBL-RTX-0004", raise rtx_db.RtxError("PBL-RTX-0004",
"%s 缺少 NOT NULL 列 %s" % (table, missing)) "%s 缺少 NOT NULL 列 %s" % (table, missing))
else:
# UPDATE SET 语义:定位维度与建档时间不得被 SET 覆写(防误改主键/租户列)
immutable = [c for c in ("tenant_id", "id", "created_at") if c in row]
if immutable:
raise rtx_db.RtxError("PBL-RTX-0005",
"%s UPDATE 禁止 SET 不可变列 %s" % (table, immutable))
return row return row
@ -265,12 +250,10 @@ async def _apply_state(tx, tenant_id, session_id, upd, seq_no, base_version,
"entity_id=%s 乐观锁冲突:base_version=%s 当前 state_version=%s(整体回滚)" "entity_id=%s 乐观锁冲突:base_version=%s 当前 state_version=%s(整体回滚)"
% (entity_id, base_version, cur_ver)) % (entity_id, base_version, cur_ver))
new_ver = cur_ver + 1 # 服务端单调递增 new_ver = cur_ver + 1 # 服务端单调递增
# UPDATE SET 子句:只含被变更列;tenant_id/session_id/entity_id 走 WHERE 定位,
# created_at 保持建档原值 —— 故按 full_row=False 构造(见 build_row 文档)
vals = build_row(STATE_TABLE, { vals = build_row(STATE_TABLE, {
STATE_COL_VALUE: state_version, EVENT_COL_STATE_VERSION: new_ver, STATE_COL_VALUE: state_version, EVENT_COL_STATE_VERSION: new_ver,
"checksum": checksum, "updated_by": actor_id, "updated_at": now, "checksum": checksum, "updated_by": actor_id, "updated_at": now,
}, strict=True, full_row=False) }, strict=False)
await tx.execute( await tx.execute(
"UPDATE %s SET %s WHERE tenant_id=%%s AND session_id=%%s AND %s=%%s" "UPDATE %s SET %s WHERE tenant_id=%%s AND session_id=%%s AND %s=%%s"
% (STATE_TABLE, ", ".join("%s=%%s" % c for c in vals.keys()), STATE_COL_KEY), % (STATE_TABLE, ", ".join("%s=%%s" % c for c in vals.keys()), STATE_COL_KEY),