From cde7293d5e743fc60c80bf65094eda1598932e25 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sat, 22 Aug 2026 18:55:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(build):=20=E7=94=9F=E6=88=90=20get=20dspy?= =?UTF-8?q?=20=E5=90=8E=E6=B3=A8=E5=85=A5=E4=BC=9A=E8=AF=9D=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E9=A1=B9=E7=9B=AE=E8=BF=87=E6=BB=A4=EF=BC=88=E8=BF=AD?= =?UTF-8?q?=E4=BB=A3/Bug/=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20=E6=97=A0?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=8A=A5=E9=94=99=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 5 ++ scripts/inject_project_filter.py | 92 ++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 scripts/inject_project_filter.py diff --git a/build.sh b/build.sh index 30d8fb7..7b3d16d 100644 --- a/build.sh +++ b/build.sh @@ -175,6 +175,11 @@ for d in sd_projects sd_iterations; do done done +# 6b. Inject session-current-project filter into generated get dspys (迭代/Bug/测试用例 用会话当前项目,无项目报错) +if [ -f "$cdir/scripts/inject_project_filter.py" ]; then + "$cdir/py3/bin/python" "$cdir/scripts/inject_project_filter.py" "$cdir/wwwroot/pipeline-sdlc" || echo " WARN: inject_project_filter.py failed" +fi + # 10.6 Create module tables (models -> DDL -> execute) echo "=== Module table creation ===" diff --git a/scripts/inject_project_filter.py b/scripts/inject_project_filter.py new file mode 100644 index 0000000..cf53bf0 --- /dev/null +++ b/scripts/inject_project_filter.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""给生成的 get_*.dspy 注入「会话当前项目强制过滤」(xls2ui 生成后 post-processing)。 + +背景:迭代/Bug/测试用例 三个功能页面要「用会话当前项目,不选项目,没项目报错」。 +这些页面是 xls2ui 从 json/ + models/ 生成的(不入 git),无法在 json 源里表达 +「后端强制按 current_project_id 过滤」这类自定义逻辑,故在生成后注入: + +1. 在 ns = params_kw.copy() 之后注入「读 current_project_id + 无项目报错」代码; +2. SQL 里 where 1=1 [[filterstr]] 之前插入项目过滤条件(直接字段或子查询)。 + +幂等:已含 _cur_pid 的文件跳过;找不到锚点(ns 初始化 / where 1=1)跳过并告警。 + +用法:py3/bin/python scripts/inject_project_filter.py +""" +import sys +import os + +# 表 -> 项目过滤 SQL 片段(插在 where 1=1 [[filterstr]] 的 1=1 之后) +PROJECT_FILTERS = { + "sd_iterations": "project_id = ${_cur_pid}$", + "sd_bugs": "iteration_id IN (SELECT id FROM sd_iterations WHERE project_id = ${_cur_pid}$)", + "sd_test_cases": ( + "plan_id IN (SELECT id FROM sd_test_plans WHERE iteration_id IN " + "(SELECT id FROM sd_iterations WHERE project_id = ${_cur_pid}$))" + ), +} + +INJECT_CODE = ''' +# 会话当前项目强制过滤(auto-injected):读 current_project_id,无项目报错 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_dbname = get_module_dbname('pipeline_sdlc') +async with DBPools().sqlorContext(_dbname) as _sor: + _prec = await _sor.sqlExe("SELECT current_project_id FROM pipeline_agent_settings WHERE user_id=${u}$", {"u": _uid}) + await _sor.sqlExe("COMMIT", {}) + _cur_pid = getattr(_prec[0], 'current_project_id', '') if _prec else '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":20,"cheight":9,"message":"请先在会话中切换项目"}} +ns['_cur_pid'] = _cur_pid +''' + + +def process_file(path, filter_sql): + with open(path, "r", encoding="utf-8") as f: + content = f.read() + + changed = False + + # 1. 注入读 current_project_id(幂等:已注入则跳过) + if "_cur_pid" in content: + print(f" skip {os.path.basename(path)}: 已注入") + return False + if "ns = params_kw.copy()" not in content: + print(f" WARN {path}: 未找到 ns = params_kw.copy(),跳过") + return False + content = content.replace("ns = params_kw.copy()", + "ns = params_kw.copy()" + INJECT_CODE, 1) + changed = True + + # 2. SQL 加项目过滤 + if "where 1=1 [[filterstr]]" not in content: + print(f" WARN {path}: 未找到 where 1=1 [[filterstr]],跳过 SQL 过滤") + else: + content = content.replace("where 1=1 [[filterstr]]", + "where " + filter_sql + " [[filterstr]]", 1) + changed = True + + with open(path, "w", encoding="utf-8") as f: + f.write(content) + print(f" injected {os.path.basename(path)}") + return True + + +def main(): + if len(sys.argv) < 2: + print(__doc__) + sys.exit(1) + wwwroot = sys.argv[1] + ok = 0 + for tbl, fs in PROJECT_FILTERS.items(): + path = os.path.join(wwwroot, tbl, "get_" + tbl + ".dspy") + if not os.path.isfile(path): + print(f" missing {path}") + continue + if process_file(path, fs): + ok += 1 + print(f"inject_project_filter: {ok} 个文件已处理") + + +if __name__ == "__main__": + main()