From e5ae2f1555c9b57b95c17fb3a8c1c8d628234d23 Mon Sep 17 00:00:00 2001 From: ymq Date: Mon, 24 Aug 2026 12:25:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20check=5Ftask=5Fowner/check=5Ftenant=5Fo?= =?UTF-8?q?wner=20=E4=BB=BB=E5=8A=A1=E6=93=8D=E4=BD=9C=20owner=20=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C(=E9=80=9A=E7=94=A8=E5=BC=95=E6=93=8E=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E6=94=BE=E8=A1=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pipeline_service/init.py | 4 +- pipeline_service/project_capability.py | 65 +++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/pipeline_service/init.py b/pipeline_service/init.py index fa0712f..7614045 100644 --- a/pipeline_service/init.py +++ b/pipeline_service/init.py @@ -513,7 +513,7 @@ def load_pipeline_service(): from .human_task_capability import ( create_human_task, complete_human_task, qc_human_task, list_project_human_tasks, count_my_human_tasks, list_my_human_todos, bug_accept) - from .project_capability import check_project_owner + from .project_capability import check_project_owner, check_task_owner, check_tenant_owner env.create_human_task = create_human_task env.complete_human_task = complete_human_task env.qc_human_task = qc_human_task @@ -522,6 +522,8 @@ def load_pipeline_service(): env.list_my_human_todos = list_my_human_todos env.bug_accept = bug_accept env.check_project_owner = check_project_owner + env.check_task_owner = check_task_owner + env.check_tenant_owner = check_tenant_owner # Register default handler register_default_handler() diff --git a/pipeline_service/project_capability.py b/pipeline_service/project_capability.py index cd427a6..5255c0e 100644 --- a/pipeline_service/project_capability.py +++ b/pipeline_service/project_capability.py @@ -104,22 +104,23 @@ async def create_project(name, project_type="web_app", description="", return True, pid -async def check_project_owner(project_id, user_id): +async def check_project_owner(project_id, user_id, sor=None): """校验 user_id 是否为项目 owner(sd_projects.created_by)。 - 独立开 context(供 pipeline-task 等 dspy 直接调用)。 + 独立开 context(供 pipeline-task 等 dspy 直接调用);传入 sor 时复用它(供 + pipeline-sdlc dspy 在已有 get_sor_context 内调用,避免嵌套 context 的 MDL 锁)。 返回 (True, '') 或 (False, 错误信息)。 """ if not project_id: return False, "缺少 project_id" if not user_id: return False, "未登录" - db, dbname = _get_db() - async with db.sqlorContext(dbname) as sor: - recs = await sor.sqlExe( + + async def _check(_sor): + recs = await _sor.sqlExe( "SELECT created_by, name FROM sd_projects WHERE id=${pid}$", {"pid": project_id}) - await sor.sqlExe("COMMIT", {}) + await _sor.sqlExe("COMMIT", {}) if not recs: return False, "项目不存在" owner = getattr(recs[0], 'created_by', '') or '' @@ -127,6 +128,58 @@ async def check_project_owner(project_id, user_id): return True, '' return False, "仅项目 owner 可执行此操作" + if sor is not None: + return await _check(sor) + db, dbname = _get_db() + async with db.sqlorContext(dbname) as _s: + return await _check(_s) + + +async def check_task_owner(task_id, user_id): + """校验 user_id 是否为 task 所属 SDLC 项目的 owner。 + + SDLC 任务 tenant_id=project_id,须校验 owner;通用引擎任务 tenant_id=org_id(非 + 项目 id),放行返回 (True, '')。独立开 context(供 pipeline-task dspy 调用)。 + """ + if not task_id: + return False, "缺少 task_id" + if not user_id: + return False, "未登录" + db, dbname = _get_db() + async with db.sqlorContext(dbname) as sor: + recs = await sor.sqlExe( + "SELECT tenant_id FROM pipeline_tasks WHERE id=${tid}$", {"tid": task_id}) + await sor.sqlExe("COMMIT", {}) + if not recs: + return False, "任务不存在" + tenant = getattr(recs[0], 'tenant_id', '') or '' + p = await sor.sqlExe( + "SELECT id FROM sd_projects WHERE id=${pid}$", {"pid": tenant}) + await sor.sqlExe("COMMIT", {}) + if not p: + # 通用引擎任务(tenant_id 非 SDLC 项目),无 owner 概念,放行 + return True, '' + return await check_project_owner(tenant, user_id, sor) + + +async def check_tenant_owner(tenant_id, user_id): + """校验 user_id 是否为 tenant_id 所属 SDLC 项目的 owner。非项目 tenant 放行。 + + 独立开 context(供 pipeline-task 的 task_submit 等 dspy 调用)。 + """ + if not user_id: + return False, "未登录" + if not tenant_id: + return True, '' + db, dbname = _get_db() + async with db.sqlorContext(dbname) as sor: + p = await sor.sqlExe( + "SELECT id FROM sd_projects WHERE id=${pid}$", {"pid": tenant_id}) + await sor.sqlExe("COMMIT", {}) + if not p: + return True, '' + return await check_project_owner(tenant_id, user_id, sor) + async def start_project(project_id, who=None, agent_id=None): """启动项目:draft → active(也兼容 archived → active 重新激活)。"""