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:
parent
66966dbc88
commit
e76bb3db60
@ -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()
|
||||
|
||||
61
wwwroot/api/ops_dashboard_data.dspy
Normal file
61
wwwroot/api/ops_dashboard_data.dspy
Normal 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)
|
||||
}
|
||||
116
wwwroot/index.ui
116
wwwroot/index.ui
@ -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"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user