# my_pipelines.dspy - 当前用户可用产线列表 → ChipBar 数据(手机端「产线」Tab 胶囊条) # 口径(2026-09-07 用户定夺):客户只显示购买过且未到期的产线; # 平台侧(owner./reseller. 角色)显示全部 published 产线。 # 数据链:product_subscription(status='1', end_date>=今天) × product(product_type='pipeline', # resource_ref_id=pipelines.id) × pipelines(status='published')。 import json as _json uid = await get_user() if not uid: return _json.dumps({"items": [], "error": "未登录"}, ensure_ascii=False) orgid = await get_userorgid() dbname = get_module_dbname('pipeline_core') # mode=full(桌面侧边栏/管理场景):不过滤订阅,返回全部 published 产线; # 默认(手机端 ChipBar):客户只见已购未到期产线(2026-09-07 用户定夺) _mode = ((params_kw or {}).get('mode') or '').strip() # fmt=more(底部 TabBar「更多」菜单):返回 MobileSheet 项格式 # [{name,label,icon,desc,url}],url 直达该产线会话页(2026-09-08) _fmt = ((params_kw or {}).get('fmt') or '').strip() items = [] try: # 平台侧角色(owner./reseller.)看全部 published 产线;客户只看已购未到期。 # get_user_roles 由 rbac.init 注册到 ServerEnv,dspy run_ns 会带入; # 探测失败/不可用时默认按客户处理(只显示已购,安全侧,不放大可见面)。 is_ops = _mode == 'full' if not is_ops: try: roles = await get_user_roles(uid) for r in (roles or []): rs = str(r) if rs.startswith('owner.') or rs.startswith('reseller.'): is_ops = True break except Exception: is_ops = False async with DBPools().sqlorContext(dbname) as sor: if is_ops: recs = await sor.sqlExe( "SELECT id, name FROM pipelines WHERE status='published' ORDER BY id", {}) await sor.sqlExe("COMMIT", {}) ids = [(getattr(r, 'id', ''), getattr(r, 'name', '')) for r in (recs or [])] else: srecs = await sor.sqlExe( "SELECT DISTINCT pr.resource_ref_id AS rid FROM product_subscription ps" " JOIN product pr ON ps.product_id = pr.id" " WHERE ps.user_org_id=${org}$ AND ps.status='1'" " AND ps.end_date >= CURDATE()" " AND pr.product_type='pipeline'", {"org": orgid}) await sor.sqlExe("COMMIT", {}) rids = [] for r in (srecs or []): v = getattr(r, 'rid', '') if v and v not in rids: rids.append(v) ids = [] if rids: # sqlor IN 列表必须展开占位符(传 list 会崩) ph = {} marks = [] for i, rid in enumerate(rids): k = 'k%d' % i ph[k] = rid marks.append('${%s}$' % k) prec = await sor.sqlExe( "SELECT id, name FROM pipelines WHERE status='published'" " AND id IN (" + ','.join(marks) + ") ORDER BY id", ph) await sor.sqlExe("COMMIT", {}) ids = [(getattr(r, 'id', ''), getattr(r, 'name', '')) for r in (prec or [])] for pid, pname in ids: if _fmt == 'more': # 底部 TabBar「更多」菜单项(2026-09-08):点击 → 内容区直达该产线 # 会话页(embed=1:隐藏胶囊条/门禁,锁定产线,会话区最大化)。 # url 不预 urlencode(前端 url_parse 会再编码一次,双重编码坑) items.append({"name": "m_pl_" + pid, "label": pname or pid, "icon": "⚡", "desc": "已购产线", "url": entire_url("/pipeline_core/mobile_agent.ui") + "?pipeline_id=" + pid + "&embed=1"}) else: items.append({"id": pid, "pipeline_id": pid, "name": pname or pid, "session_id": "m_" + pid}) except Exception: items = [] return _json.dumps({"items": items}, ensure_ascii=False)