From 7a6a0350dcdb6b06bb47412171d9dabee4e4fb2e Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 2 Sep 2026 12:00:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20resolve=5Fproject=5Fid=E9=94=AE=E6=BC=82?= =?UTF-8?q?=E7=A7=BB=E9=98=B2=E6=8A=A4=E2=80=94=E2=80=94=E4=BC=A0=E5=85=A5?= =?UTF-8?q?=E5=80=BC=E5=85=88=E6=8C=89id=E6=A0=A1=E9=AA=8C,=E6=9F=A5?= =?UTF-8?q?=E4=B8=8D=E5=88=B0=E6=8C=89name=E5=8F=8D=E6=9F=A5=E5=BD=92?= =?UTF-8?q?=E4=B8=80=E4=B8=BA=E7=9C=9F=E5=AE=9Eid,=E5=90=8D=E5=AD=97?= =?UTF-8?q?=E6=AD=A7=E4=B9=89=E6=8B=92=E7=BB=9D;=E6=A0=B9=E6=B2=BBbid=5Fch?= =?UTF-8?q?apters=E4=BB=A5=E9=A1=B9=E7=9B=AE=E5=90=8D=E8=90=BD=E5=BA=93(?= =?UTF-8?q?=E9=99=95=E8=A5=BF=E9=A1=B9=E7=9B=AE26=E6=9D=A1=E5=90=8D?= =?UTF-8?q?=E5=AD=97=E9=94=AE=E5=AD=A4=E5=84=BF=E8=A1=8C,=E4=B8=8Eid?= =?UTF-8?q?=E9=94=AE=E9=AA=A8=E6=9E=B6=E5=B9=B6=E5=AD=98=E5=BD=A2=E6=88=90?= =?UTF-8?q?=E5=8F=8C=E9=AA=A8=E6=9E=B6)(2026-09-02)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pipeline_bidding/bid_common.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/pipeline_bidding/bid_common.py b/pipeline_bidding/bid_common.py index 56200f2..5383d96 100644 --- a/pipeline_bidding/bid_common.py +++ b/pipeline_bidding/bid_common.py @@ -158,13 +158,36 @@ async def get_param(sor, key, default=""): async def resolve_project_id(project_id="", **entity_ids): - """补齐 project_id:调用方(会话上下文)已给则原样返回;否则从实体反查项目。 + """补齐/校验 project_id:必须是 sd_projects 真实存在的项目。 角色工具经引擎分发时默认注入会话当前项目;为空时(无会话上下文)按实体反查, 避免「项目不明」导致跨项目读写。全部反查失败抛 ValueError。 + + 键漂移防护(2026-09-02):调用方可能把项目「名字」误当 project_id 传入 + (曾致 bid_chapters 26 条记录以名字为键落库,与正确 id 键骨架并存形成双骨架)。 + 此处统一校验:传入值先按 id 查;查不到再按 name 反查并归一为真实 id; + 名字命中多个项目则拒绝(歧义),彻底堵住「名字当 id」写库。 """ if project_id: - return project_id + db, dbname = get_db() + async with db.sqlorContext(dbname) as sor: + recs = await sor.sqlExe( + "SELECT id FROM sd_projects WHERE id=${p}$ LIMIT 1", {"p": project_id}) + await sor.sqlExe("COMMIT", {}) + if recs: + return project_id + # 按名字反查(键漂移容错:误传项目名 → 归一为真实 id) + recs = await sor.sqlExe( + "SELECT id FROM sd_projects WHERE name=${p}$", {"p": project_id}) + await sor.sqlExe("COMMIT", {}) + if len(recs) == 1: + real_id = getattr(recs[0], "id", "") + logger.warning("resolve_project_id: 项目名「%s」→ 归一为 id %s(键漂移防护)", + project_id, real_id) + return real_id + if len(recs) > 1: + raise ValueError("项目名「%s」匹配到多个项目,请改用项目 ID 指定" % project_id) + raise ValueError("项目不存在:%s" % project_id) db, dbname = get_db() async with db.sqlorContext(dbname) as sor: for tbl, col, val in (("bid_chapters", "chapter_id", entity_ids.get("chapter_id")),