feat: UI pages, build.sh, deployment scripts, model uitype fixes

- Add SDLC dashboard, pipeline editor, ops center UI
- build.sh: clone business modules to pkgs/, xls2ui CRUD, fix created_by
- global_func.py: password_encode None-safe wrapper
- pipeline_app.py: permission cache warmup removed
- load_path.py: RBAC permission registration for all modules
- scripts/merge_i18n.py: i18n merge tool
- bin/init_perms.py, bin/init_data.py: init scripts
- set_role_perm.py: single permission registration
- Model uitype fields set for form editing
- pipeline_core/pipeline_ops load_path.py scripts
This commit is contained in:
yumoqing 2026-07-18 22:44:34 +08:00
parent 66966dbc88
commit e76bb3db60
3 changed files with 216 additions and 109 deletions

View File

@ -1,90 +1,62 @@
"""Register all RBAC paths for pipeline_ops module"""
#!/usr/bin/env python3
"""RBAC path registration for pipeline_ops module."""
import os, sys, subprocess
MOD = "pipeline_ops"
PATHS_ANY = [
f"/{MOD}/index.ui",
]
PATHS_LOGINED = [
f"/{MOD}",
f"/{MOD}/api/ops_dashboard_data.dspy",
# Pricing
f"/{MOD}/pipeline_pricing/index.ui",
f"/{MOD}/pipeline_pricing/get_pipeline_pricing.dspy",
f"/{MOD}/pipeline_pricing/add_pipeline_pricing.dspy",
f"/{MOD}/pipeline_pricing/update_pipeline_pricing.dspy",
f"/{MOD}/pipeline_pricing/delete_pipeline_pricing.dspy",
# Capacity
f"/{MOD}/pipeline_capacity/index.ui",
f"/{MOD}/pipeline_capacity/get_pipeline_capacity.dspy",
f"/{MOD}/pipeline_capacity/add_pipeline_capacity.dspy",
f"/{MOD}/pipeline_capacity/update_pipeline_capacity.dspy",
f"/{MOD}/pipeline_capacity/delete_pipeline_capacity.dspy",
# Usage Log
f"/{MOD}/pipeline_usage_log/index.ui",
f"/{MOD}/pipeline_usage_log/get_pipeline_usage_log.dspy",
f"/{MOD}/pipeline_usage_log/add_pipeline_usage_log.dspy",
f"/{MOD}/pipeline_usage_log/update_pipeline_usage_log.dspy",
f"/{MOD}/pipeline_usage_log/delete_pipeline_usage_log.dspy",
# API
f"/{MOD}/api/pipeline_pricing_create.dspy",
f"/{MOD}/api/pipeline_pricing_update.dspy",
f"/{MOD}/api/pipeline_pricing_delete.dspy",
f"/{MOD}/api/pipeline_capacity_create.dspy",
f"/{MOD}/api/pipeline_capacity_update.dspy",
f"/{MOD}/api/pipeline_capacity_delete.dspy",
f"/{MOD}/api/pipeline_usage_log_create.dspy",
f"/{MOD}/api/pipeline_usage_log_update.dspy",
f"/{MOD}/api/pipeline_usage_log_delete.dspy",
f"/{MOD}/api/get_search_pipeline_id.dspy",
]
def load_paths():
"""Return list of RBAC paths for this module"""
paths = [
{
"path": "/pipeline_ops/",
"name": "产线运营",
"icon": "settings",
"parent": "",
"sort": 30
},
{
"path": "/pipeline_ops/pipeline_pricing",
"name": "定价管理",
"icon": "price-tag",
"parent": "/pipeline_ops/",
"sort": 31
},
{
"path": "/pipeline_ops/api/pipeline_pricing_create.dspy",
"name": "新增定价",
"parent": "/pipeline_ops/pipeline_pricing",
"sort": 32
},
{
"path": "/pipeline_ops/api/pipeline_pricing_update.dspy",
"name": "修改定价",
"parent": "/pipeline_ops/pipeline_pricing",
"sort": 33
},
{
"path": "/pipeline_ops/api/pipeline_pricing_delete.dspy",
"name": "删除定价",
"parent": "/pipeline_ops/pipeline_pricing",
"sort": 34
},
{
"path": "/pipeline_ops/pipeline_capacity",
"name": "供应量管理",
"icon": "gauge",
"parent": "/pipeline_ops/",
"sort": 40
},
{
"path": "/pipeline_ops/api/pipeline_capacity_create.dspy",
"name": "新增供应量配置",
"parent": "/pipeline_ops/pipeline_capacity",
"sort": 41
},
{
"path": "/pipeline_ops/api/pipeline_capacity_update.dspy",
"name": "修改供应量配置",
"parent": "/pipeline_ops/pipeline_capacity",
"sort": 42
},
{
"path": "/pipeline_ops/api/pipeline_capacity_delete.dspy",
"name": "删除供应量配置",
"parent": "/pipeline_ops/pipeline_capacity",
"sort": 43
},
{
"path": "/pipeline_ops/pipeline_usage_log",
"name": "使用记录",
"icon": "list",
"parent": "/pipeline_ops/",
"sort": 50
},
{
"path": "/pipeline_ops/api/pipeline_usage_log_create.dspy",
"name": "新增使用记录",
"parent": "/pipeline_ops/pipeline_usage_log",
"sort": 51
},
{
"path": "/pipeline_ops/api/pipeline_usage_log_update.dspy",
"name": "修改使用记录",
"parent": "/pipeline_ops/pipeline_usage_log",
"sort": 52
},
{
"path": "/pipeline_ops/api/pipeline_usage_log_delete.dspy",
"name": "删除使用记录",
"parent": "/pipeline_ops/pipeline_usage_log",
"sort": 53
}
]
return paths
def main():
root = os.path.expanduser("~/pipeline")
set_perm = os.path.join(root, "set_role_perm.py")
if not os.path.isfile(set_perm):
print("ERROR: pipeline root not found"); sys.exit(1)
py = sys.executable
count = 0
for role, paths in [("any", PATHS_ANY), ("logined", PATHS_LOGINED)]:
for path in paths:
r = subprocess.run([py, set_perm, role, path], capture_output=True, text=True, cwd=root)
if r.returncode == 0: count += 1
else: print(f" WARN: {path}")
print(f"Registered {count}/{len(PATHS_ANY)+len(PATHS_LOGINED)} paths for {MOD}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,61 @@
"""
Operations Dashboard Data: task queue, human tasks, stats
"""
from sqlor.dbpools import DBPools
async def main():
dbname = get_module_dbname('pipeline_core')
async with DBPools().sqlorContext(dbname) as sor:
# Task stats by state
task_stats = await sor.sqlExe("""
SELECT state, COUNT(*) as cnt FROM pipeline_tasks
GROUP BY state ORDER BY cnt DESC
""", {})
stats = {'submitted':0, 'running':0, 'completed':0, 'failed':0, 'cancelled':0}
for r in task_stats:
stats[r.state] = r.cnt
# Recent tasks (last 20)
tasks = await sor.sqlExe("""
SELECT t.id, t.title, t.state, t.pipeline_id, t.created_at
FROM pipeline_tasks t
ORDER BY t.created_at DESC LIMIT 20
""", {})
task_list = []
for r in tasks:
task_list.append({
'id': r.id, 'title': r.title, 'state': r.state,
'pipeline_id': r.pipeline_id, 'created_at': str(r.created_at)[:19]
})
# Pending human tasks
human_tasks = await sor.sqlExe("""
SELECT h.id, h.task_id, h.task_type, h.step_name, h.status,
h.assignee_role, h.created_at, h.expired_at,
t.title as task_title
FROM pipeline_human_tasks h
LEFT JOIN pipeline_tasks t ON h.task_id = t.id
WHERE h.status IN ('pending', 'in_progress')
ORDER BY h.created_at DESC LIMIT 20
""", {})
human_list = []
for r in human_tasks:
human_list.append({
'id': r.id, 'task_id': r.task_id, 'task_type': r.task_type,
'step_name': r.step_name, 'status': r.status,
'assignee_role': r.assignee_role, 'task_title': r.task_title,
'created_at': str(r.created_at)[:19],
'expired_at': str(r.expired_at)[:19] if r.expired_at else None
})
return {
'stats': stats,
'tasks': task_list,
'human_tasks': human_list,
'total_tasks': sum(stats.values()),
'active_tasks': stats['running'],
'pending_reviews': len(human_list)
}

View File

@ -1,46 +1,120 @@
{
"widgettype": "VBox",
"options": {"width": "100%", "height": "100%", "padding": "24px", "spacing": "20px"},
"options": {"width": "100%", "height": "100%", "padding": "24px", "gap": "20px"},
"subwidgets": [
{"widgettype": "Title2", "options": {"text": "产线运营"}},
{
"widgettype": "HBox",
"options": {"alignItems": "center", "gap": "12px"},
"subwidgets": [
{"widgettype": "Title2", "options": {"text": "产线运营中心"}},
{"widgettype": "Text", "options": {"text": "任务队列、审批管理、执行监控", "cfontsize": 1.1, "color": "#888"}}
]
},
{
"widgettype": "ResponsableBox",
"options": {"gap": "24px", "minWidth": "260px"},
"options": {"gap": "16px", "minWidth": "150px"},
"subwidgets": [
{
"widgettype": "VBox",
"options": {"css": "card", "cwidth": 23, "cheight": 11, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.main_content", "options": {"url": "{{entire_url('pipeline_pricing/')}}"}, "mode": "replace"}],
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "4px", "border": "1px solid #334155", "cwidth": 16, "alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2, "color": "#60a5fa", "fontWeight": "bold"}},
{"widgettype": "Text", "options": {"text": "已提交", "cfontsize": 0.9, "color": "#94a3b8"}}
]
},
{
"widgettype": "VBox",
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "4px", "border": "1px solid #334155", "cwidth": 16, "alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2, "color": "#fbbf24", "fontWeight": "bold"}},
{"widgettype": "Text", "options": {"text": "运行中", "cfontsize": 0.9, "color": "#94a3b8"}}
]
},
{
"widgettype": "VBox",
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "4px", "border": "1px solid #334155", "cwidth": 16, "alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2, "color": "#4ade80", "fontWeight": "bold"}},
{"widgettype": "Text", "options": {"text": "已完成", "cfontsize": 0.9, "color": "#94a3b8"}}
]
},
{
"widgettype": "VBox",
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "4px", "border": "1px solid #334155", "cwidth": 16, "alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2, "color": "#f87171", "fontWeight": "bold"}},
{"widgettype": "Text", "options": {"text": "失败", "cfontsize": 0.9, "color": "#94a3b8"}}
]
},
{
"widgettype": "VBox",
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "4px", "border": "1px solid #334155", "cwidth": 16, "alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2, "color": "#a78bfa", "fontWeight": "bold"}},
{"widgettype": "Text", "options": {"text": "待审批", "cfontsize": 0.9, "color": "#94a3b8"}}
]
},
{
"widgettype": "VBox",
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "4px", "border": "1px solid #334155", "cwidth": 16, "alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2, "color": "#e2e8f0", "fontWeight": "bold"}},
{"widgettype": "Text", "options": {"text": "总任务", "cfontsize": 0.9, "color": "#94a3b8"}}
]
}
]
},
{
"widgettype": "ResponsableBox",
"options": {"gap": "16px", "minWidth": "400px"},
"subwidgets": [
{
"widgettype": "VBox",
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "12px", "border": "1px solid #334155", "cwidth": 60},
"subwidgets": [
{"widgettype": "Title3", "options": {"text": "近期任务"}},
{"widgettype": "Text", "options": {"text": "暂无任务,提交产线任务后将在此显示", "cfontsize": 0.9, "color": "#64748b", "padding": "20px 0"}}
]
},
{
"widgettype": "VBox",
"options": {"bgcolor": "#1e293b", "padding": "20px", "borderRadius": "12px", "gap": "12px", "border": "1px solid #334155", "cwidth": 40},
"subwidgets": [
{"widgettype": "Title3", "options": {"text": "待审批队列"}},
{"widgettype": "Text", "options": {"text": "暂无审批任务", "cfontsize": 0.9, "color": "#64748b", "padding": "20px 0"}}
]
}
]
},
{
"widgettype": "ResponsableBox",
"options": {"gap": "16px", "minWidth": "180px"},
"subwidgets": [
{
"widgettype": "VBox",
"options": {"css": "card", "cwidth": 23, "cheight": 10, "padding": "24px", "cursor": "pointer", "bgcolor": "#1e293b", "border": "1px solid #334155"},
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.main_content", "options": {"url": "{{entire_url('/pipeline_ops/pipeline_pricing/')}}"}, "mode": "replace"}],
"subwidgets": [
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#FEF2F2", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#EF4444\" stroke-width=\"2\"><path d=\"M12 2a10 10 0 100 20 10 10 0 000-20z\"/><path d=\"M12 6v6l4 2\"/></svg>", "width": "24px", "height": "24px"}}
]},
{"widgettype": "Title3", "options": {"text": "定价管理", "marginBottom": "4px"}},
{"widgettype": "Text", "options": {"text": "管理产线计费方式和价格", "cfontsize": 0.9}}
{"widgettype": "Text", "options": {"text": "管理产线计费方式和价格", "cfontsize": 0.9, "color": "#94a3b8"}}
]
},
{
"widgettype": "VBox",
"options": {"css": "card", "cwidth": 23, "cheight": 11, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.main_content", "options": {"url": "{{entire_url('pipeline_capacity/')}}"}, "mode": "replace"}],
"options": {"css": "card", "cwidth": 23, "cheight": 10, "padding": "24px", "cursor": "pointer", "bgcolor": "#1e293b", "border": "1px solid #334155"},
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.main_content", "options": {"url": "{{entire_url('/pipeline_ops/pipeline_capacity/')}}"}, "mode": "replace"}],
"subwidgets": [
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#F0F9FF", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#0EA5E9\" stroke-width=\"2\"><path d=\"M13 10V3L4 14h7v7l9-11h-7z\"/></svg>", "width": "24px", "height": "24px"}}
]},
{"widgettype": "Title3", "options": {"text": "供应量管理", "marginBottom": "4px"}},
{"widgettype": "Text", "options": {"text": "配置产线并发和调用限额", "cfontsize": 0.9}}
{"widgettype": "Text", "options": {"text": "配置产线并发和调用限额", "cfontsize": 0.9, "color": "#94a3b8"}}
]
},
{
"widgettype": "VBox",
"options": {"css": "card", "cwidth": 23, "cheight": 11, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.main_content", "options": {"url": "{{entire_url('pipeline_usage_log/')}}"}, "mode": "replace"}],
"options": {"css": "card", "cwidth": 23, "cheight": 10, "padding": "24px", "cursor": "pointer", "bgcolor": "#1e293b", "border": "1px solid #334155"},
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.main_content", "options": {"url": "{{entire_url('/pipeline_ops/pipeline_usage_log/')}}"}, "mode": "replace"}],
"subwidgets": [
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#F5F3FF", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#8B5CF6\" stroke-width=\"2\"><path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/></svg>", "width": "24px", "height": "24px"}}
]},
{"widgettype": "Title3", "options": {"text": "使用记录", "marginBottom": "4px"}},
{"widgettype": "Text", "options": {"text": "查看产线调用和消费记录", "cfontsize": 0.9}}
{"widgettype": "Text", "options": {"text": "查看产线调用和消费记录", "cfontsize": 0.9, "color": "#94a3b8"}}
]
}
]