用户需求(2026-09-09):研发报告按钮点击按项目owner过滤列表;每项点击看
研发场景/输入/输出/成果;成果文件多个列出、逐个点击下载。
1. 列表页 api/opp_reports_list.dspy(替换CRUD裸表页作菜单入口):
- list_visible_reports 单一事实源:挂项目的报告仅项目owner可见
(sd_projects.created_by==uid;agent建的项目降级同机构,对齐
check_project_owner既有语义);未挂项目的平台级调研登录可见;
孤儿报告(项目已删)不可见(LIKE 'agent.%' 写法有 rag_client.py 生产先例)
- 整行点击下钻详情,交互范式照抄 opp_references_items.dspy
2. 详情弹窗 api/opp_report_detail.dspy:
- parse_report_sections 四节解析(## 研发场景/输入/输出/成果),
MdWidget逐节渲染;兼容序号/冒号/加粗标题变体;三级标题不误判;
旧报告无节时全文归输出节;空节如实标注不编造
- check_report_visible 与列表同一可见性规则(语义不漂移)
3. 文件下载 api/opp_report_file_dl.dspy + list_report_files:
- 范围=项目deliverables/全部文件+报告PPT(realpath去重)
- 安全模型对齐 opp_attachment_dl:前端只传report_id+name,
服务端回查清单命中才给文件,文件名不参与路径拼接(防穿越)
4. agent侧四节模板硬要求:OPP_PROMPT新增节+create/update_report工具描述
5. RBAC load_path 补3端点;i18n zh/en 补9词条;README 补改造说明
离线实测11项断言全过:标准四节/标题变体/旧报告归输出/缺节/三级标题不误判/
重复节拼接/空正文/deliverables扫描+PPT去重/项目外PPT/无项目不崩/防穿越
71 lines
2.1 KiB
Python
71 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
||
"""RBAC path registration for pipeline-opportunity module.
|
||
|
||
与平台惯例一致:页面/CRUD/API → logined。
|
||
在宿主应用根目录执行(set_role_perm.py 位于宿主根):
|
||
cd <APP_ROOT> && py3/bin/python pkgs/pipeline-opportunity/scripts/load_path.py
|
||
"""
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
|
||
MOD = "pipeline-opportunity"
|
||
# set_role_perm.py 在宿主应用根目录(本脚本位于 <APP_ROOT>/pkgs/pipeline-opportunity/scripts/)
|
||
APP_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
||
|
||
TABLES = [
|
||
"opp_reports", "opp_approvals",
|
||
]
|
||
|
||
PATHS_ANY = []
|
||
|
||
PATHS_LOGINED = [
|
||
"/%s" % MOD,
|
||
"/%s/index.ui" % MOD,
|
||
"/%s/agent" % MOD,
|
||
"/%s/agent/index.ui" % MOD,
|
||
"/%s/api/opp_references_popup.dspy" % MOD,
|
||
"/%s/api/opp_references_items.dspy" % MOD,
|
||
"/%s/api/opp_demand_items.dspy" % MOD,
|
||
"/%s/api/opp_overseas_items.dspy" % MOD,
|
||
"/%s/api/opp_item_detail.dspy" % MOD,
|
||
"/%s/api/opp_attachment_dl.dspy" % MOD,
|
||
"/%s/api/opp_confirm_report.dspy" % MOD,
|
||
"/%s/api/opp_report_ppt.dspy" % MOD,
|
||
"/%s/api/opp_reports_list.dspy" % MOD,
|
||
"/%s/api/opp_report_detail.dspy" % MOD,
|
||
"/%s/api/opp_report_file_dl.dspy" % MOD,
|
||
]
|
||
for t in TABLES:
|
||
PATHS_LOGINED += [
|
||
"/%s/%s/index.ui" % (MOD, t),
|
||
"/%s/%s/get_%s.dspy" % (MOD, t, t),
|
||
"/%s/%s/add_%s.dspy" % (MOD, t, t),
|
||
"/%s/%s/update_%s.dspy" % (MOD, t, t),
|
||
"/%s/%s/delete_%s.dspy" % (MOD, t, t),
|
||
]
|
||
|
||
|
||
def _run(role, path):
|
||
r = subprocess.run([sys.executable, os.path.join(APP_ROOT, "set_role_perm.py"), role, path],
|
||
capture_output=True, text=True, cwd=APP_ROOT)
|
||
if r.returncode != 0:
|
||
print(" FAIL [%s] %s: %s" % (role, path, (r.stderr or "").strip()[:120]))
|
||
return r.returncode == 0
|
||
|
||
|
||
def main():
|
||
print("=== %s RBAC registration ===" % MOD)
|
||
n = 0
|
||
for p in PATHS_ANY:
|
||
n += _run("any", p)
|
||
print(" any: %s" % p)
|
||
for p in PATHS_LOGINED:
|
||
n += _run("logined", p)
|
||
print(" logined: %s" % p)
|
||
print("Done. %d/%d paths registered" % (n, len(PATHS_ANY) + len(PATHS_LOGINED)))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|