From d0e2ab89a8ffd08cb9ce04b67730e03704511131 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 11 Sep 2026 18:29:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(bidding):=206=E5=BC=A0CRUD=E8=A1=A8?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E9=A1=B9=E7=9B=AE=E5=BC=BA=E5=88=B6=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E2=80=94=E2=80=94get=5F*.dspy=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E7=BA=A7=E8=A7=A3=E6=9E=90(get=5Fsession=5Fp?= =?UTF-8?q?roject=5Fid=E5=90=AB=E4=BA=A7=E7=BA=BF=E9=9A=94=E7=A6=BB),?= =?UTF-8?q?=E5=BC=BA=E5=88=B6=E8=A6=86=E7=9B=96project=5Fid=E4=BC=A0?= =?UTF-8?q?=E5=80=BC,=E6=97=A0=E9=A1=B9=E7=9B=AE=E6=8A=A5=E9=94=99?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E9=80=89=E6=8B=A9=E6=88=96=E6=96=B0=E5=BB=BA?= =?UTF-8?q?;bid=5Ftask=5Ftree=E5=88=A0=E3=80=8C=E6=9C=80=E8=BF=91=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E4=B8=AD=E9=A1=B9=E7=9B=AE=E3=80=8D=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E7=BA=A7=E5=85=9C=E5=BA=95(=E4=BC=9A=E6=8A=8A=E5=88=AB?= =?UTF-8?q?=E4=BA=BA=E9=A1=B9=E7=9B=AE=E5=BD=93=E5=BD=93=E5=89=8D=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE),=E7=BB=9F=E4=B8=80=E8=B5=B0=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E7=BA=A7=E8=A7=A3=E6=9E=90;=E7=9F=A5=E8=AF=86=E5=BA=93?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E5=8A=A0require=5Fproject=E9=97=A8=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pipeline_bidding/bid_ability.py | 2 +- wwwroot/api/bid_task_tree.dspy | 31 ++++++------------- wwwroot/bid_chapters/get_bid_chapters.dspy | 22 ++++++++++++- wwwroot/bid_documents/get_bid_documents.dspy | 22 ++++++++++++- .../bid_qc_reviews/get_bid_qc_reviews.dspy | 22 ++++++++++++- .../get_bid_qualifications.dspy | 22 ++++++++++++- .../get_bid_scoring_items.dspy | 22 ++++++++++++- .../get_bid_tender_files.dspy | 22 ++++++++++++- 8 files changed, 137 insertions(+), 28 deletions(-) diff --git a/pipeline_bidding/bid_ability.py b/pipeline_bidding/bid_ability.py index a69d566..235e382 100644 --- a/pipeline_bidding/bid_ability.py +++ b/pipeline_bidding/bid_ability.py @@ -517,7 +517,7 @@ def register_bid_ability(): {"label": "🎯 资质", "icon": "", "url": "/pipeline-bidding/bid_qualifications/index.ui", "type": "popup", "width": "88%", "height": "82%", "require_project": True}, {"label": "📚 投标知识库", "icon": "", "url": "/pipeline-bidding/bid_kb_docs/index.ui", - "type": "popup", "width": "88%", "height": "82%"}, + "type": "popup", "width": "88%", "height": "82%", "require_project": True}, {"label": "📦 标书", "icon": "", "url": "/pipeline-bidding/bid_documents/index.ui", "type": "popup", "width": "88%", "height": "82%", "require_project": True}, {"label": "📋 任务树", "icon": "", "url": "/pipeline-bidding/bid_task_tree_popup.ui", diff --git a/wwwroot/api/bid_task_tree.dspy b/wwwroot/api/bid_task_tree.dspy index f1dede8..36606ac 100644 --- a/wwwroot/api/bid_task_tree.dspy +++ b/wwwroot/api/bid_task_tree.dspy @@ -42,31 +42,20 @@ _ROLE_GROUPS = [ _ROLE_SET = set(r for r, _ in _ROLE_GROUPS) async with DBPools().sqlorContext(dbname) as sor: - # ── 解析当前投标项目(三级兜底) ── + # ── 解析当前投标项目(2026-09-11 收敛:统一走 get_session_project_id, + # 含悬空自愈+产线隔离;删掉「最近一个进行中的投标项目」第三级兜底—— + # 把别人的项目当当前项目违反「没项目要提示选择或新建」)── + _sid = (params_kw or {}).get('session_id', '') or '' pid = '' - # 1) 投标产线默认会话的项目绑定 - recs = await sor.sqlExe( - "SELECT current_project_id FROM pipeline_session_settings " - "WHERE user_id=${u}$ AND session_id='default_bidding_general' LIMIT 1", {"u": uid}) - if recs: - pid = getattr(recs[0], 'current_project_id', '') or '' - # 2) agent 全局设置 - if not pid: - recs = await sor.sqlExe( - "SELECT current_project_id FROM pipeline_agent_settings WHERE user_id=${u}$", {"u": uid}) - if recs: - pid = getattr(recs[0], 'current_project_id', '') or '' - # 3) 最近一个进行中的投标项目 - if not pid: - recs = await sor.sqlExe( - "SELECT id FROM sd_projects WHERE pipeline_id='bidding_general' " - "AND status='in_progress' ORDER BY updated_at DESC LIMIT 1") - if recs: - pid = getattr(recs[0], 'id', '') or '' + try: + from pipeline_service.workspace import get_session_project_id + pid = await get_session_project_id(sor, uid, _sid, 'bidding_general') or '' + except Exception: + pid = '' if not pid: if not node_id: - return json.dumps([{"id": "__root__", "label": "请先在会话中切换/创建投标项目", "is_leaf": True}], ensure_ascii=False) + return json.dumps([{"id": "__root__", "label": "请先选择或新建投标项目", "is_leaf": True}], ensure_ascii=False) return json.dumps([], ensure_ascii=False) pname = '' diff --git a/wwwroot/bid_chapters/get_bid_chapters.dspy b/wwwroot/bid_chapters/get_bid_chapters.dspy index c56135c..d4fbd87 100644 --- a/wwwroot/bid_chapters/get_bid_chapters.dspy +++ b/wwwroot/bid_chapters/get_bid_chapters.dspy @@ -1,6 +1,26 @@ ns = params_kw.copy() +# 会话当前项目强制过滤(2026-09-11 用户要求):会话级解析(session→全局兜底+产线隔离+悬空自愈), +# 无项目报错提示;强制覆盖前端 project_id 传值(含搜索框),列表只显示当前项目数据。 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_sid = (params_kw or {}).get('session_id', '') or '' +_pl = (params_kw or {}).get('pipeline_id', '') or 'bidding_general' +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(get_module_dbname('pipeline_bidding')) as _sor: + _cur_pid = await get_session_project_id(_sor, _uid, _sid, _pl) or '' +except Exception: + _cur_pid = '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":22,"cheight":9,"message":"当前会话没有项目,请先选择或新建项目"}} +ns['_cur_pid'] = _cur_pid +ns['project_id'] = _cur_pid + + debug(f'get_bid_chapters.dspy:{ns=}') if not ns.get('page'): @@ -13,7 +33,7 @@ if not ns.get('sort'): sql = '''select a.*, b.status_text, c.section_text -from (select * from bid_chapters where 1=1 [[filterstr]]) a left join (select k as status, +from (select * from bid_chapters where project_id = ${_cur_pid}$ [[filterstr]]) a left join (select k as status, v as status_text from appcodes_kv where parentid='bid_chapter_status') b on a.status = b.status left join (select k as section, v as section_text from appcodes_kv where parentid='bid_scoring_section') c on a.section = c.section''' diff --git a/wwwroot/bid_documents/get_bid_documents.dspy b/wwwroot/bid_documents/get_bid_documents.dspy index 8f710c0..8763593 100644 --- a/wwwroot/bid_documents/get_bid_documents.dspy +++ b/wwwroot/bid_documents/get_bid_documents.dspy @@ -1,6 +1,26 @@ ns = params_kw.copy() +# 会话当前项目强制过滤(2026-09-11 用户要求):会话级解析(session→全局兜底+产线隔离+悬空自愈), +# 无项目报错提示;强制覆盖前端 project_id 传值(含搜索框),列表只显示当前项目数据。 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_sid = (params_kw or {}).get('session_id', '') or '' +_pl = (params_kw or {}).get('pipeline_id', '') or 'bidding_general' +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(get_module_dbname('pipeline_bidding')) as _sor: + _cur_pid = await get_session_project_id(_sor, _uid, _sid, _pl) or '' +except Exception: + _cur_pid = '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":22,"cheight":9,"message":"当前会话没有项目,请先选择或新建项目"}} +ns['_cur_pid'] = _cur_pid +ns['project_id'] = _cur_pid + + debug(f'get_bid_documents.dspy:{ns=}') if not ns.get('page'): @@ -13,7 +33,7 @@ if not ns.get('sort'): sql = '''select a.*, b.status_text -from (select * from bid_documents where 1=1 [[filterstr]]) a left join (select k as status, +from (select * from bid_documents where project_id = ${_cur_pid}$ [[filterstr]]) a left join (select k as status, v as status_text from appcodes_kv where parentid='bid_doc_status') b on a.status = b.status''' diff --git a/wwwroot/bid_qc_reviews/get_bid_qc_reviews.dspy b/wwwroot/bid_qc_reviews/get_bid_qc_reviews.dspy index bbb7a8b..22d3a74 100644 --- a/wwwroot/bid_qc_reviews/get_bid_qc_reviews.dspy +++ b/wwwroot/bid_qc_reviews/get_bid_qc_reviews.dspy @@ -1,6 +1,26 @@ ns = params_kw.copy() +# 会话当前项目强制过滤(2026-09-11 用户要求):会话级解析(session→全局兜底+产线隔离+悬空自愈), +# 无项目报错提示;强制覆盖前端 project_id 传值(含搜索框),列表只显示当前项目数据。 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_sid = (params_kw or {}).get('session_id', '') or '' +_pl = (params_kw or {}).get('pipeline_id', '') or 'bidding_general' +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(get_module_dbname('pipeline_bidding')) as _sor: + _cur_pid = await get_session_project_id(_sor, _uid, _sid, _pl) or '' +except Exception: + _cur_pid = '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":22,"cheight":9,"message":"当前会话没有项目,请先选择或新建项目"}} +ns['_cur_pid'] = _cur_pid +ns['project_id'] = _cur_pid + + debug(f'get_bid_qc_reviews.dspy:{ns=}') if not ns.get('page'): @@ -13,7 +33,7 @@ if not ns.get('sort'): sql = '''select a.*, b.qc_type_text -from (select * from bid_qc_reviews where 1=1 [[filterstr]]) a left join (select k as qc_type, +from (select * from bid_qc_reviews where project_id = ${_cur_pid}$ [[filterstr]]) a left join (select k as qc_type, v as qc_type_text from appcodes_kv where parentid='bid_qc_type') b on a.qc_type = b.qc_type''' filterjson = params_kw.get('data_filter') diff --git a/wwwroot/bid_qualifications/get_bid_qualifications.dspy b/wwwroot/bid_qualifications/get_bid_qualifications.dspy index 2e76103..48c12ab 100644 --- a/wwwroot/bid_qualifications/get_bid_qualifications.dspy +++ b/wwwroot/bid_qualifications/get_bid_qualifications.dspy @@ -1,6 +1,26 @@ ns = params_kw.copy() +# 会话当前项目强制过滤(2026-09-11 用户要求):会话级解析(session→全局兜底+产线隔离+悬空自愈), +# 无项目报错提示;强制覆盖前端 project_id 传值(含搜索框),列表只显示当前项目数据。 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_sid = (params_kw or {}).get('session_id', '') or '' +_pl = (params_kw or {}).get('pipeline_id', '') or 'bidding_general' +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(get_module_dbname('pipeline_bidding')) as _sor: + _cur_pid = await get_session_project_id(_sor, _uid, _sid, _pl) or '' +except Exception: + _cur_pid = '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":22,"cheight":9,"message":"当前会话没有项目,请先选择或新建项目"}} +ns['_cur_pid'] = _cur_pid +ns['project_id'] = _cur_pid + + debug(f'get_bid_qualifications.dspy:{ns=}') if not ns.get('page'): @@ -13,7 +33,7 @@ if not ns.get('sort'): sql = '''select a.*, b.match_status_text, c.owner_text -from (select * from bid_qualifications where 1=1 [[filterstr]]) a left join (select k as match_status, +from (select * from bid_qualifications where project_id = ${_cur_pid}$ [[filterstr]]) a left join (select k as match_status, v as match_status_text from appcodes_kv where parentid='bid_qual_match_status') b on a.match_status = b.match_status left join (select k as owner, v as owner_text from appcodes_kv where parentid='bid_qual_owner') c on a.owner = c.owner''' diff --git a/wwwroot/bid_scoring_items/get_bid_scoring_items.dspy b/wwwroot/bid_scoring_items/get_bid_scoring_items.dspy index 64c776b..033bbdf 100644 --- a/wwwroot/bid_scoring_items/get_bid_scoring_items.dspy +++ b/wwwroot/bid_scoring_items/get_bid_scoring_items.dspy @@ -1,6 +1,26 @@ ns = params_kw.copy() +# 会话当前项目强制过滤(2026-09-11 用户要求):会话级解析(session→全局兜底+产线隔离+悬空自愈), +# 无项目报错提示;强制覆盖前端 project_id 传值(含搜索框),列表只显示当前项目数据。 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_sid = (params_kw or {}).get('session_id', '') or '' +_pl = (params_kw or {}).get('pipeline_id', '') or 'bidding_general' +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(get_module_dbname('pipeline_bidding')) as _sor: + _cur_pid = await get_session_project_id(_sor, _uid, _sid, _pl) or '' +except Exception: + _cur_pid = '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":22,"cheight":9,"message":"当前会话没有项目,请先选择或新建项目"}} +ns['_cur_pid'] = _cur_pid +ns['project_id'] = _cur_pid + + debug(f'get_bid_scoring_items.dspy:{ns=}') if not ns.get('page'): @@ -13,7 +33,7 @@ if not ns.get('sort'): sql = '''select a.*, b.section_text -from (select * from bid_scoring_items where 1=1 [[filterstr]]) a left join (select k as section, +from (select * from bid_scoring_items where project_id = ${_cur_pid}$ [[filterstr]]) a left join (select k as section, v as section_text from appcodes_kv where parentid='bid_scoring_section') b on a.section = b.section''' diff --git a/wwwroot/bid_tender_files/get_bid_tender_files.dspy b/wwwroot/bid_tender_files/get_bid_tender_files.dspy index 2c69b56..f20958e 100644 --- a/wwwroot/bid_tender_files/get_bid_tender_files.dspy +++ b/wwwroot/bid_tender_files/get_bid_tender_files.dspy @@ -1,6 +1,26 @@ ns = params_kw.copy() +# 会话当前项目强制过滤(2026-09-11 用户要求):会话级解析(session→全局兜底+产线隔离+悬空自愈), +# 无项目报错提示;强制覆盖前端 project_id 传值(含搜索框),列表只显示当前项目数据。 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_sid = (params_kw or {}).get('session_id', '') or '' +_pl = (params_kw or {}).get('pipeline_id', '') or 'bidding_general' +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(get_module_dbname('pipeline_bidding')) as _sor: + _cur_pid = await get_session_project_id(_sor, _uid, _sid, _pl) or '' +except Exception: + _cur_pid = '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":22,"cheight":9,"message":"当前会话没有项目,请先选择或新建项目"}} +ns['_cur_pid'] = _cur_pid +ns['project_id'] = _cur_pid + + debug(f'get_bid_tender_files.dspy:{ns=}') if not ns.get('page'): @@ -13,7 +33,7 @@ if not ns.get('sort'): sql = '''select a.*, b.file_type_text -from (select * from bid_tender_files where 1=1 [[filterstr]]) a left join (select k as file_type, +from (select * from bid_tender_files where project_id = ${_cur_pid}$ [[filterstr]]) a left join (select k as file_type, v as file_type_text from appcodes_kv where parentid='bid_file_type') b on a.file_type = b.file_type'''