From 736a2dd57940cbe8bf1a564b15bde750f1e5d392 Mon Sep 17 00:00:00 2001 From: ymq Date: Thu, 10 Sep 2026 16:14:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(human=5Ftask):=20=E5=BE=85=E5=8A=9E?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E8=80=85=E6=B3=A8=E5=86=8C=E8=A1=A8register?= =?UTF-8?q?=5Ftodo=5Fprovider=E2=80=94=E2=80=94=E5=B9=B3=E5=8F=B0=E5=BE=85?= =?UTF-8?q?=E5=8A=9E=E5=BC=80=E6=94=BE=E7=BB=99=E5=A4=96=E9=83=A8=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E6=B3=A8=E5=85=A5(=E5=B7=A5=E5=8D=95=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E9=A6=96=E7=94=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _TODO_PROVIDERS注册表: provider签名async fn(user_id, roles, limit)->[todo dict], dict须含source - list_my_human_todos: db context外遍历providers union(逐个try/except,坏provider不拖垮列表) - count_my_human_tasks: 仅全局角标(project_id空)计入providers——工单不属于产线项目 - 架构: 待办是平台级机制,各部分(产线任务/冒泡问题/工单)产生待办,本模块聚合; 外部模块注册钩子注入,不跨模块读表(sage-module-scaffolding 7.3) --- pipeline_service/human_task_capability.py | 46 +++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/pipeline_service/human_task_capability.py b/pipeline_service/human_task_capability.py index 22d957f..8e66f44 100644 --- a/pipeline_service/human_task_capability.py +++ b/pipeline_service/human_task_capability.py @@ -32,6 +32,20 @@ T_BUG_ACCEPT = "bug_acceptance" T_REQ_CONFIRM = "requirement_confirmation" T_DESIGN_CONFIRM = "design_confirmation" +# ══════════════ 待办提供者注册表(2026-09-10 工单模块接入) ══════════════ +# 平台待办是平台级机制:各部分(产线任务/冒泡问题/工单/…)产生待办, +# 本模块聚合。外部模块通过 register_todo_provider 注入,不跨模块读表。 +# provider 签名: async fn(user_id, roles, limit) -> [todo dict] +# todo dict 须含 source(弹窗按它分流详情端点)+ title/description/created_at。 +_TODO_PROVIDERS = [] + + +def register_todo_provider(fn): + """注册待办提供者(幂等:同函数不重复注册)。""" + if callable(fn) and fn not in _TODO_PROVIDERS: + _TODO_PROVIDERS.append(fn) + logger.info("todo provider registered: %s", getattr(fn, '__module__', '?')) + def _get_db(): db = DBPools() @@ -401,7 +415,20 @@ async def count_my_human_tasks(user_id, project_id=None): n_q = getattr(q[0], 'c', 0) if q else 0 n_ht = getattr(ht[0], 'c', 0) if ht else 0 - return int(n_q) + int(n_ht) + _roles_cache = roles + total = int(n_q) + int(n_ht) + + # 外部待办提供者(工单等,2026-09-10):仅全局角标(project_id 空)计入—— + # 工单不属于任何产线项目,项目内角标不混入。坏 provider 不拖垮计数。 + if not project_id: + for fn in list(_TODO_PROVIDERS): + try: + extra = await fn(user_id, _roles_cache, 100) + total += len(extra or []) + except Exception as e: + logger.warning("todo provider count %s failed: %s", + getattr(fn, '__module__', '?'), str(e)[:160]) + return total async def list_my_human_todos(user_id, limit=100): @@ -442,8 +469,21 @@ async def list_my_human_todos(user_id, limit=100): d = _rec_to_dict(r) d['source'] = 'question' todos.append(d) - todos.sort(key=lambda x: str(x.get('created_at') or ''), reverse=True) - return todos[:limit] + _roles_cache = roles + + # 外部待办提供者(工单等,2026-09-10):在 db context 外调用,provider 自开连接。 + # 逐个 try/except——坏 provider 不拖垮整个待办列表(平台级机制必须健壮)。 + for fn in list(_TODO_PROVIDERS): + try: + extra = await fn(user_id, _roles_cache, limit) + for d in (extra or []): + todos.append(d) + except Exception as e: + logger.warning("todo provider %s failed: %s", + getattr(fn, '__module__', '?'), str(e)[:160]) + + todos.sort(key=lambda x: str(x.get('created_at') or ''), reverse=True) + return todos[:limit] async def bug_accept(bug_id, accept, user_id, comment=None):