From 2f71d851ef8428ce974edcf83185310f45a011c8 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 9 Sep 2026 15:18:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(opp):=20LIKE=20=E6=A8=A1=E5=BC=8F=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=8C=96=E2=80=94=E2=80=94SQL=E5=AD=97=E9=9D=A2?= =?UTF-8?q?=E9=87=8F=E8=A3=B8%=E8=A2=AB=E9=A9=B1=E5=8A=A8=E5=BD=93?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E7=AC=A6=E7=82=B8ValueError(?= =?UTF-8?q?=E5=AE=9E=E6=B5=8B500),=20agent.%=20=E6=94=B9=E8=B5=B0=20${agen?= =?UTF-8?q?t=5Fpat}$=20=E4=BC=A0=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- pipeline_opportunity/opp_report_capability.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 51bf32b..4448037 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ slash 命令:`/hot` `/ai` `/reports` `/oppdiag`。 1. **按项目 owner 过滤**(`list_visible_reports` 单一事实源): - 挂项目的报告:当前用户是项目 owner(`sd_projects.created_by==uid`)才可见; - agent 创建的项目(`created_by LIKE 'agent.%'`)降级为同机构成员可见 + agent 创建的项目(`created_by LIKE 'agent.%'`,模式经 `${kw}$` 参数化—— + SQL 字面量裸 `%` 会被驱动当格式化符炸 ValueError,实测踩过)降级为同机构成员可见 (对齐 `check_project_owner` 既有降级语义,不重复发明) - 未挂项目的报告(平台级调研,存量数据大多是这类):登录用户可见 - 项目已删的孤儿报告:owner 无法验证 → 不可见(宁缺勿漏,LEFT JOIN 自然排除) diff --git a/pipeline_opportunity/opp_report_capability.py b/pipeline_opportunity/opp_report_capability.py index 3c735e6..3f25460 100644 --- a/pipeline_opportunity/opp_report_capability.py +++ b/pipeline_opportunity/opp_report_capability.py @@ -110,6 +110,9 @@ async def list_visible_reports(sor, user_id): if not user_id: return [] uorg = await _user_org(sor, user_id) + # ⚠️ LIKE 模式必须走 ${kw}$ 参数化——SQL 字面量里的裸 % 会被驱动当格式化符 + # (实测 ValueError: unsupported format character;rag_client.py 的 + # NOT LIKE 'agent.%' 是同一颗潜伏雷)。参数值里的 % 安全。 sql = ( "SELECT r.id, r.project_id, r.software, r.title, r.status, r.ppt_path, " "r.created_by, r.created_at, r.updated_at, " @@ -117,10 +120,11 @@ async def list_visible_reports(sor, user_id): "FROM opp_reports r LEFT JOIN sd_projects p ON r.project_id = p.id " "WHERE (r.project_id IS NULL OR r.project_id = '') " " OR p.created_by = ${uid}$ " - + (" OR (p.created_by LIKE 'agent.%' AND p.org_id = ${uorg}$ AND ${uorg}$ <> '') " - if uorg else "") - + "ORDER BY r.created_at DESC LIMIT 200") - recs = await sor.sqlExe(sql, {"uid": user_id, "uorg": uorg}) + " OR (p.created_by LIKE ${agent_pat}$ " + " AND p.org_id = ${uorg}$ AND ${uorg}$ <> '') " + "ORDER BY r.created_at DESC LIMIT 200") + recs = await sor.sqlExe(sql, {"uid": user_id, "uorg": uorg, + "agent_pat": "agent.%"}) await sor.sqlExe("COMMIT", {}) return rows_to_dicts(recs)