feat(mobile): 手机端产线工作界面——mobile_agent.ui(ChipBar已购产线胶囊+AgentIO全屏,mobile_opts压margin)+my_pipelines.dspy(客户按product_subscription未到期×pipelines published过滤,平台角色看全部,get_user_roles失败降级按客户安全侧)(2026-09-07手机端适配:只有购买过的产线可见)
This commit is contained in:
parent
01bfd01619
commit
c7b766e1c1
72
wwwroot/api/my_pipelines.dspy
Normal file
72
wwwroot/api/my_pipelines.dspy
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# 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')
|
||||||
|
|
||||||
|
items = []
|
||||||
|
try:
|
||||||
|
# 平台侧角色(owner./reseller.)看全部 published 产线;客户只看已购未到期。
|
||||||
|
# get_user_roles 由 rbac.init 注册到 ServerEnv,dspy run_ns 会带入;
|
||||||
|
# 探测失败/不可用时默认按客户处理(只显示已购,安全侧,不放大可见面)。
|
||||||
|
is_ops = False
|
||||||
|
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:
|
||||||
|
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)
|
||||||
52
wwwroot/mobile_agent.ui
Normal file
52
wwwroot/mobile_agent.ui
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {"width": "100%", "height": "100%", "padding": "0"},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "ChipBar",
|
||||||
|
"id": "mobile_pipeline_chips",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "auto",
|
||||||
|
"dataurl": "/pipeline_core/api/my_pipelines.dspy",
|
||||||
|
"valueField": "id",
|
||||||
|
"textField": "name",
|
||||||
|
"empty_text": "暂无已购产线 · 请到「我的 → 产品商城」购买"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "select",
|
||||||
|
"actiontype": "method",
|
||||||
|
"target": "app.mobile_agent_io",
|
||||||
|
"method": "set_pipeline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "select",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.mobile_menus_wrap",
|
||||||
|
"options": {"url": "/pipeline_core/api/agent_menus.dspy", "method": "GET"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "mobile_menus_wrap",
|
||||||
|
"options": {"width": "100%", "height": "auto"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "AgentIO",
|
||||||
|
"id": "mobile_agent_io",
|
||||||
|
"options": {
|
||||||
|
"css": "filler",
|
||||||
|
"margin": "0 24px 24px 24px",
|
||||||
|
"mobile_opts": {"margin": "0 8px 8px 8px"},
|
||||||
|
"url": "/pipeline_core/api/agent_chat.dspy",
|
||||||
|
"model_dataurl": "/pipeline_core/api/agent_model_options.dspy?capabilities=chat",
|
||||||
|
"model_cwidth": 14,
|
||||||
|
"placeholder": "输入你的需求..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user