diff --git a/json/sd_bug_list.json b/json/sd_bug_list.json index aaa1a2e..9038a8d 100644 --- a/json/sd_bug_list.json +++ b/json/sd_bug_list.json @@ -1,70 +1,103 @@ { - "tblname": "sd_bugs", - "title": "Bug管理", - "params": { - "sortby": ["created_at desc"], - "confidential_fields": [], - "browserfields": { - "alters": { - "iteration_id": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}", - "valueField": "iteration_id", - "textField": "iteration_id_text" - }, - "severity": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_severity.dspy')}}", - "valueField": "severity", - "textField": "severity_text" - }, - "priority": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", - "valueField": "priority", - "textField": "priority_text" - }, - "status": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}", - "valueField": "status", - "textField": "status_text" - }, - "reporter_type": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}", - "valueField": "reporter_type", - "textField": "reporter_type_text" - } - } + "tblname": "sd_bugs", + "title": "Bug管理", + "params": { + "sortby": [ + "created_at desc" + ], + "confidential_fields": [], + "browserfields": { + "alters": { + "iteration_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}", + "valueField": "value", + "textField": "text" }, - "editexclouded": ["id", "created_at", "updated_at", "closed_at", "verified_by", "fix_commit"], - "editable": { - "new_data_url": "{{entire_url('../api/sd_bug_create.dspy')}}", - "update_data_url": "{{entire_url('../api/sd_bug_update.dspy')}}", - "delete_data_url": "{{entire_url('../api/sd_bug_delete.dspy')}}" + "severity": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_severity.dspy')}}", + "valueField": "value", + "textField": "text" }, - "data_filter": { - "AND": [ - {"field": "iteration_id", "op": "=", "var": "iteration_input"}, - {"field": "severity", "op": "=", "var": "severity_input"}, - {"field": "status", "op": "=", "var": "status_input"}, - {"field": "title", "op": "LIKE", "var": "title_input"} - ] + "priority": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", + "valueField": "value", + "textField": "text" }, - "record_toolbar": [ - { - "label": "确认", - "actiontype": "dspy", - "url": "{{entire_url('../api/bug_confirm.dspy')}}", - "options": {"icon": "check", "cwidth": 16, "cheight": 9} - }, - { - "label": "关闭", - "actiontype": "dspy", - "url": "{{entire_url('../api/bug_close.dspy')}}", - "options": {"icon": "block", "cwidth": 16, "cheight": 9} - } - ] - } -} + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}", + "valueField": "value", + "textField": "text" + }, + "reporter_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}", + "valueField": "value", + "textField": "text" + } + } + }, + "editexclouded": [ + "id", + "created_at", + "updated_at", + "closed_at", + "verified_by", + "fix_commit" + ], + "editable": { + "new_data_url": "{{entire_url('../api/sd_bug_create.dspy')}}", + "update_data_url": "{{entire_url('../api/sd_bug_update.dspy')}}", + "delete_data_url": "{{entire_url('../api/sd_bug_delete.dspy')}}" + }, + "data_filter": { + "AND": [ + { + "field": "iteration_id", + "op": "=", + "var": "iteration_input" + }, + { + "field": "severity", + "op": "=", + "var": "severity_input" + }, + { + "field": "status", + "op": "=", + "var": "status_input" + }, + { + "field": "title", + "op": "LIKE", + "var": "title_input" + } + ] + }, + "record_toolbar": [ + { + "label": "确认", + "actiontype": "dspy", + "url": "{{entire_url('../api/bug_confirm.dspy')}}", + "options": { + "icon": "check", + "cwidth": 16, + "cheight": 9 + } + }, + { + "label": "关闭", + "actiontype": "dspy", + "url": "{{entire_url('../api/bug_close.dspy')}}", + "options": { + "icon": "block", + "cwidth": 16, + "cheight": 9 + } + } + ] + } +} \ No newline at end of file diff --git a/json/sd_deploy_env_list.json b/json/sd_deploy_env_list.json index 2b0aecf..ee0272a 100644 --- a/json/sd_deploy_env_list.json +++ b/json/sd_deploy_env_list.json @@ -1,45 +1,62 @@ { - "tblname": "sd_deploy_envs", - "title": "部署环境", - "params": { - "sortby": ["env_type"], - "confidential_fields": ["db_password", "ssh_key_path"], - "browserfields": { - "exclouded": ["db_password", "ssh_key_path"], - "alters": { - "project_id": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_project_options.dspy')}}", - "valueField": "project_id", - "textField": "project_id_text" - }, - "env_type": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}", - "valueField": "env_type", - "textField": "env_type_text" - }, - "status": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}", - "valueField": "status", - "textField": "status_text" - } - } + "tblname": "sd_deploy_envs", + "title": "部署环境", + "params": { + "sortby": [ + "env_type" + ], + "confidential_fields": [ + "db_password", + "ssh_key_path" + ], + "browserfields": { + "exclouded": [ + "db_password", + "ssh_key_path" + ], + "alters": { + "project_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_project_options.dspy')}}", + "valueField": "value", + "textField": "text" }, - "editexclouded": ["id", "verified_at", "created_at", "updated_at"], - "editable": { - "new_data_url": "{{entire_url('../api/sd_deploy_env_create.dspy')}}", - "update_data_url": "{{entire_url('../api/sd_deploy_env_update.dspy')}}", - "delete_data_url": "{{entire_url('../api/sd_deploy_env_delete.dspy')}}" + "env_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}", + "valueField": "value", + "textField": "text" }, - "record_toolbar": [ - { - "label": "验证连接", - "actiontype": "dspy", - "url": "{{entire_url('../api/env_verify.dspy')}}", - "options": {"icon": "check", "cwidth": 16, "cheight": 9} - } - ] - } -} + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}", + "valueField": "value", + "textField": "text" + } + } + }, + "editexclouded": [ + "id", + "verified_at", + "created_at", + "updated_at" + ], + "editable": { + "new_data_url": "{{entire_url('../api/sd_deploy_env_create.dspy')}}", + "update_data_url": "{{entire_url('../api/sd_deploy_env_update.dspy')}}", + "delete_data_url": "{{entire_url('../api/sd_deploy_env_delete.dspy')}}" + }, + "record_toolbar": [ + { + "label": "验证连接", + "actiontype": "dspy", + "url": "{{entire_url('../api/env_verify.dspy')}}", + "options": { + "icon": "check", + "cwidth": 16, + "cheight": 9 + } + } + ] + } +} \ No newline at end of file diff --git a/json/sd_iteration_list.json b/json/sd_iteration_list.json index 7dacb61..256a7e7 100644 --- a/json/sd_iteration_list.json +++ b/json/sd_iteration_list.json @@ -1,58 +1,81 @@ { - "tblname": "sd_iterations", - "title": "迭代管理", - "params": { - "sortby": ["created_at desc"], - "confidential_fields": [], - "browserfields": { - "exclouded": ["scope"], - "alters": { - "project_id": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_project_options.dspy')}}", - "valueField": "project_id", - "textField": "project_id_text" - }, - "iteration_type": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}", - "valueField": "iteration_type", - "textField": "iteration_type_text" - }, - "status": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}", - "valueField": "status", - "textField": "status_text" - } - } + "tblname": "sd_iterations", + "title": "迭代管理", + "params": { + "sortby": [ + "created_at desc" + ], + "confidential_fields": [], + "browserfields": { + "exclouded": [ + "scope" + ], + "alters": { + "project_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_project_options.dspy')}}", + "valueField": "value", + "textField": "text" }, - "editexclouded": ["id", "task_id", "started_at", "completed_at", "created_at", "updated_at"], - "editable": { - "new_data_url": "{{entire_url('../api/sd_iteration_create.dspy')}}", - "update_data_url": "{{entire_url('../api/sd_iteration_update.dspy')}}", - "delete_data_url": "{{entire_url('../api/sd_iteration_delete.dspy')}}" + "iteration_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}", + "valueField": "value", + "textField": "text" }, - "data_filter": { - "AND": [ - {"field": "project_id", "op": "=", "var": "project_input"}, - {"field": "iteration_name", "op": "LIKE", "var": "name_input"}, - {"field": "status", "op": "=", "var": "status_input"} - ] + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}", + "valueField": "value", + "textField": "text" + } + } + }, + "editexclouded": [ + "id", + "task_id", + "started_at", + "completed_at", + "created_at", + "updated_at" + ], + "editable": { + "new_data_url": "{{entire_url('../api/sd_iteration_create.dspy')}}", + "update_data_url": "{{entire_url('../api/sd_iteration_update.dspy')}}", + "delete_data_url": "{{entire_url('../api/sd_iteration_delete.dspy')}}" + }, + "data_filter": { + "AND": [ + { + "field": "project_id", + "op": "=", + "var": "project_input" }, - "subtables": [ - { - "field": "iteration_id", - "title": "Bug列表", - "url": "{{entire_url('../sd_bug_list?iteration_id=${id}')}}", - "subtable": "sd_bugs" - }, - { - "field": "iteration_id", - "title": "测试方案", - "url": "{{entire_url('../sd_test_plan_list?iteration_id=${id}')}}", - "subtable": "sd_test_plans" - } - ] - } -} + { + "field": "iteration_name", + "op": "LIKE", + "var": "name_input" + }, + { + "field": "status", + "op": "=", + "var": "status_input" + } + ] + }, + "subtables": [ + { + "field": "iteration_id", + "title": "Bug列表", + "url": "{{entire_url('../sd_bug_list?iteration_id=${id}')}}", + "subtable": "sd_bugs" + }, + { + "field": "iteration_id", + "title": "测试方案", + "url": "{{entire_url('../sd_test_plan_list?iteration_id=${id}')}}", + "subtable": "sd_test_plans" + } + ] + } +} \ No newline at end of file diff --git a/json/sd_project_list.json b/json/sd_project_list.json index 3de8b73..d29acee 100644 --- a/json/sd_project_list.json +++ b/json/sd_project_list.json @@ -1,47 +1,74 @@ { - "tblname": "sd_projects", - "title": "项目管理", - "params": { - "sortby": ["created_at desc"], - "logined_userorgid": "org_id", - "confidential_fields": [], - "browserfields": { - "exclouded": ["tech_stack"], - "alters": { - "status": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_status.dspy')}}", - "valueField": "status", - "textField": "status_text" - }, - "project_type": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}", - "valueField": "project_type", - "textField": "project_type_text" - } - } + "tblname": "sd_projects", + "title": "项目管理", + "params": { + "sortby": [ + "created_at desc" + ], + "logined_userorgid": "org_id", + "confidential_fields": [], + "browserfields": { + "exclouded": [ + "tech_stack" + ], + "alters": { + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_status.dspy')}}", + "valueField": "value", + "textField": "text" }, - "editexclouded": ["id", "created_at", "updated_at", "org_id"], - "editable": { - "new_data_url": "{{entire_url('../api/sd_project_create.dspy')}}", - "update_data_url": "{{entire_url('../api/sd_project_update.dspy')}}", - "delete_data_url": "{{entire_url('../api/sd_project_delete.dspy')}}" + "project_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}", + "valueField": "value", + "textField": "text" }, - "data_filter": { - "AND": [ - {"field": "name", "op": "LIKE", "var": "name_input"}, - {"field": "status", "op": "=", "var": "status_input"}, - {"field": "project_type", "op": "=", "var": "type_input"} - ] + "pipeline_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_pipeline_options.dspy')}}", + "valueField": "value", + "textField": "text" + } + } + }, + "editexclouded": [ + "id", + "created_at", + "updated_at", + "org_id" + ], + "editable": { + "new_data_url": "{{entire_url('../api/sd_project_create.dspy')}}", + "update_data_url": "{{entire_url('../api/sd_project_update.dspy')}}", + "delete_data_url": "{{entire_url('../api/sd_project_delete.dspy')}}" + }, + "data_filter": { + "AND": [ + { + "field": "name", + "op": "LIKE", + "var": "name_input" }, - "subtables": [ - { - "field": "project_id", - "title": "迭代列表", - "url": "{{entire_url('../sd_iteration_list?project_id=${id}')}}", - "subtable": "sd_iterations" - } - ] - } -} + { + "field": "status", + "op": "=", + "var": "status_input" + }, + { + "field": "project_type", + "op": "=", + "var": "type_input" + } + ] + }, + "subtables": [ + { + "field": "project_id", + "title": "迭代列表", + "url": "{{entire_url('../sd_iteration_list?project_id=${id}')}}", + "subtable": "sd_iterations" + } + ] + } +} \ No newline at end of file diff --git a/json/sd_test_case_list.json b/json/sd_test_case_list.json index d3698c7..9485094 100644 --- a/json/sd_test_case_list.json +++ b/json/sd_test_case_list.json @@ -1,50 +1,76 @@ { - "tblname": "sd_test_cases", - "title": "测试用例", - "params": { - "sortby": ["priority desc", "created_at desc"], - "confidential_fields": [], - "browserfields": { - "alters": { - "plan_id": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_test_plan_options.dspy')}}", - "valueField": "plan_id", - "textField": "plan_id_text" - }, - "case_type": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}", - "valueField": "case_type", - "textField": "case_type_text" - }, - "status": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}", - "valueField": "status", - "textField": "status_text" - }, - "priority": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", - "valueField": "priority", - "textField": "priority_text" - } - } + "tblname": "sd_test_cases", + "title": "测试用例", + "params": { + "sortby": [ + "priority desc", + "created_at desc" + ], + "confidential_fields": [], + "browserfields": { + "alters": { + "plan_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_test_plan_options.dspy')}}", + "valueField": "value", + "textField": "text" }, - "editexclouded": ["id", "actual_result", "executed_by", "executed_at", "duration_ms", "created_at"], - "editable": { - "new_data_url": "{{entire_url('../api/sd_test_case_create.dspy')}}", - "update_data_url": "{{entire_url('../api/sd_test_case_update.dspy')}}", - "delete_data_url": "{{entire_url('../api/sd_test_case_delete.dspy')}}" + "case_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}", + "valueField": "value", + "textField": "text" }, - "data_filter": { - "AND": [ - {"field": "plan_id", "op": "=", "var": "plan_input"}, - {"field": "case_type", "op": "=", "var": "type_input"}, - {"field": "status", "op": "=", "var": "status_input"}, - {"field": "priority", "op": "=", "var": "priority_input"} - ] + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}", + "valueField": "value", + "textField": "text" + }, + "priority": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", + "valueField": "value", + "textField": "text" } + } + }, + "editexclouded": [ + "id", + "actual_result", + "executed_by", + "executed_at", + "duration_ms", + "created_at" + ], + "editable": { + "new_data_url": "{{entire_url('../api/sd_test_case_create.dspy')}}", + "update_data_url": "{{entire_url('../api/sd_test_case_update.dspy')}}", + "delete_data_url": "{{entire_url('../api/sd_test_case_delete.dspy')}}" + }, + "data_filter": { + "AND": [ + { + "field": "plan_id", + "op": "=", + "var": "plan_input" + }, + { + "field": "case_type", + "op": "=", + "var": "type_input" + }, + { + "field": "status", + "op": "=", + "var": "status_input" + }, + { + "field": "priority", + "op": "=", + "var": "priority_input" + } + ] } -} + } +} \ No newline at end of file diff --git a/json/sd_test_plan_list.json b/json/sd_test_plan_list.json index b71825a..2ccc35e 100644 --- a/json/sd_test_plan_list.json +++ b/json/sd_test_plan_list.json @@ -1,44 +1,50 @@ { - "tblname": "sd_test_plans", - "title": "测试方案", - "params": { - "sortby": ["created_at desc"], - "confidential_fields": [], - "browserfields": { - "alters": { - "iteration_id": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}", - "valueField": "iteration_id", - "textField": "iteration_id_text" - }, - "plan_type": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}", - "valueField": "plan_type", - "textField": "plan_type_text" - }, - "status": { - "uitype": "code", - "dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}", - "valueField": "status", - "textField": "status_text" - } - } + "tblname": "sd_test_plans", + "title": "测试方案", + "params": { + "sortby": [ + "created_at desc" + ], + "confidential_fields": [], + "browserfields": { + "alters": { + "iteration_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}", + "valueField": "value", + "textField": "text" }, - "editexclouded": ["id", "created_at", "updated_at"], - "editable": { - "new_data_url": "{{entire_url('../api/sd_test_plan_create.dspy')}}", - "update_data_url": "{{entire_url('../api/sd_test_plan_update.dspy')}}", - "delete_data_url": "{{entire_url('../api/sd_test_plan_delete.dspy')}}" + "plan_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}", + "valueField": "value", + "textField": "text" }, - "subtables": [ - { - "field": "plan_id", - "title": "测试用例", - "url": "{{entire_url('../sd_test_case_list?plan_id=${id}')}}", - "subtable": "sd_test_cases" - } - ] - } -} + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}", + "valueField": "value", + "textField": "text" + } + } + }, + "editexclouded": [ + "id", + "created_at", + "updated_at" + ], + "editable": { + "new_data_url": "{{entire_url('../api/sd_test_plan_create.dspy')}}", + "update_data_url": "{{entire_url('../api/sd_test_plan_update.dspy')}}", + "delete_data_url": "{{entire_url('../api/sd_test_plan_delete.dspy')}}" + }, + "subtables": [ + { + "field": "plan_id", + "title": "测试用例", + "url": "{{entire_url('../sd_test_case_list?plan_id=${id}')}}", + "subtable": "sd_test_cases" + } + ] + } +} \ No newline at end of file diff --git a/scripts/load_path.py b/scripts/load_path.py index be22515..77f1cab 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -1,117 +1,132 @@ #!/usr/bin/env python3 -""" -RBAC path registration for pipeline-sdlc module. -Run: cd ~/repos/sage && ./py3/bin/python ~/repos/pipeline-sdlc/scripts/load_path.py -""" -import os -import sys -import subprocess +"""RBAC path registration for pipeline-sdlc module.""" +import os, sys, subprocess -MOD = "pipeline_sdlc" +MOD = "pipeline-sdlc" PATHS_ANY = [ f"/{MOD}/index.ui", - f"/{MOD}/styles/sdlc.css", ] PATHS_LOGINED = [ f"/{MOD}", - f"/{MOD}/sd_project", - f"/{MOD}/sd_project/index.ui", - f"/{MOD}/sd_project/get_sd_project_list.dspy", - f"/{MOD}/sd_project/add_sd_project_list.dspy", - f"/{MOD}/sd_project/update_sd_project_list.dspy", - f"/{MOD}/sd_project/delete_sd_project_list.dspy", - f"/{MOD}/sd_iteration", - f"/{MOD}/sd_iteration/index.ui", - f"/{MOD}/sd_iteration/get_sd_iteration_list.dspy", - f"/{MOD}/sd_iteration/add_sd_iteration_list.dspy", - f"/{MOD}/sd_iteration/update_sd_iteration_list.dspy", - f"/{MOD}/sd_iteration/delete_sd_iteration_list.dspy", - f"/{MOD}/sd_test_plan", - f"/{MOD}/sd_test_plan/index.ui", - f"/{MOD}/sd_test_plan/get_sd_test_plan_list.dspy", - f"/{MOD}/sd_test_plan/add_sd_test_plan_list.dspy", - f"/{MOD}/sd_test_plan/update_sd_test_plan_list.dspy", - f"/{MOD}/sd_test_plan/delete_sd_test_plan_list.dspy", - f"/{MOD}/sd_test_case", - f"/{MOD}/sd_test_case/index.ui", - f"/{MOD}/sd_test_case/get_sd_test_case_list.dspy", - f"/{MOD}/sd_test_case/add_sd_test_case_list.dspy", - f"/{MOD}/sd_test_case/update_sd_test_case_list.dspy", - f"/{MOD}/sd_test_case/delete_sd_test_case_list.dspy", - f"/{MOD}/sd_bug", - f"/{MOD}/sd_bug/index.ui", - f"/{MOD}/sd_bug/get_sd_bug_list.dspy", - f"/{MOD}/sd_bug/add_sd_bug_list.dspy", - f"/{MOD}/sd_bug/update_sd_bug_list.dspy", - f"/{MOD}/sd_bug/delete_sd_bug_list.dspy", - f"/{MOD}/sd_deploy_env", - f"/{MOD}/sd_deploy_env/index.ui", - f"/{MOD}/sd_deploy_env/get_sd_deploy_env_list.dspy", - f"/{MOD}/sd_deploy_env/add_sd_deploy_env_list.dspy", - f"/{MOD}/sd_deploy_env/update_sd_deploy_env_list.dspy", - f"/{MOD}/sd_deploy_env/delete_sd_deploy_env_list.dspy", - # API endpoints - f"/{MOD}/api/create_sd_project.dspy", - f"/{MOD}/api/update_sd_project.dspy", - f"/{MOD}/api/delete_sd_project.dspy", - f"/{MOD}/api/create_sd_iteration.dspy", - f"/{MOD}/api/update_sd_iteration.dspy", - f"/{MOD}/api/delete_sd_iteration.dspy", - f"/{MOD}/api/create_sd_bug.dspy", - f"/{MOD}/api/update_sd_bug.dspy", - f"/{MOD}/api/close_sd_bug.dspy", - f"/{MOD}/api/save_sd_deploy_env.dspy", - f"/{MOD}/api/get_project_options.dspy", - f"/{MOD}/api/get_iteration_options.dspy", - f"/{MOD}/api/check_iteration_bugs.dspy", - f"/{MOD}/api/submit_bug.dspy", # Dashboard - f"/{MOD}/sd_dashboard/dashboard.ui", + f"/{MOD}/sd_dashboard/index.ui", + f"/{MOD}/sd_dashboard/dashboard_data.dspy", + # Projects + f"/{MOD}/sd_projects/index.ui", + f"/{MOD}/sd_projects/get_sd_projects.dspy", + f"/{MOD}/sd_projects/add_sd_projects.dspy", + f"/{MOD}/sd_projects/update_sd_projects.dspy", + f"/{MOD}/sd_projects/delete_sd_projects.dspy", + f"/{MOD}/sd_project_list/index.ui", + # Iterations + f"/{MOD}/sd_iterations/index.ui", + f"/{MOD}/sd_iterations/get_sd_iterations.dspy", + f"/{MOD}/sd_iterations/add_sd_iterations.dspy", + f"/{MOD}/sd_iterations/update_sd_iterations.dspy", + f"/{MOD}/sd_iterations/delete_sd_iterations.dspy", + f"/{MOD}/sd_iteration_list/index.ui", + # Test Plans + f"/{MOD}/sd_test_plans/index.ui", + f"/{MOD}/sd_test_plans/get_sd_test_plans.dspy", + f"/{MOD}/sd_test_plans/add_sd_test_plans.dspy", + f"/{MOD}/sd_test_plans/update_sd_test_plans.dspy", + f"/{MOD}/sd_test_plans/delete_sd_test_plans.dspy", + f"/{MOD}/sd_test_plan_list/index.ui", + # Test Cases + f"/{MOD}/sd_test_cases/index.ui", + f"/{MOD}/sd_test_cases/get_sd_test_cases.dspy", + f"/{MOD}/sd_test_cases/add_sd_test_cases.dspy", + f"/{MOD}/sd_test_cases/update_sd_test_cases.dspy", + f"/{MOD}/sd_test_cases/delete_sd_test_cases.dspy", + f"/{MOD}/sd_test_case_list/index.ui", + # Bugs + f"/{MOD}/sd_bugs/index.ui", + f"/{MOD}/sd_bugs/get_sd_bugs.dspy", + f"/{MOD}/sd_bugs/add_sd_bugs.dspy", + f"/{MOD}/sd_bugs/update_sd_bugs.dspy", + f"/{MOD}/sd_bugs/delete_sd_bugs.dspy", + f"/{MOD}/sd_bug_list/index.ui", + # Deploy Envs + f"/{MOD}/sd_deploy_envs/index.ui", + f"/{MOD}/sd_deploy_envs/get_sd_deploy_envs.dspy", + f"/{MOD}/sd_deploy_envs/add_sd_deploy_envs.dspy", + f"/{MOD}/sd_deploy_envs/update_sd_deploy_envs.dspy", + f"/{MOD}/sd_deploy_envs/delete_sd_deploy_envs.dspy", + f"/{MOD}/sd_deploy_env_list/index.ui", + # Review + f"/{MOD}/sd_review/index.ui", + # API + f"/{MOD}/api/bug_close.dspy", + f"/{MOD}/api/bug_confirm.dspy", + f"/{MOD}/api/check_iteration_bugs.dspy", + f"/{MOD}/api/env_verify.dspy", + f"/{MOD}/api/get_iteration_options.dspy", + f"/{MOD}/api/get_project_options.dspy", + f"/{MOD}/api/get_test_plan_options.dspy", + f"/{MOD}/api/submit_bug.dspy", + f"/{MOD}/api/sd_bug_create.dspy", + f"/{MOD}/api/sd_bug_update.dspy", + f"/{MOD}/api/sd_bug_delete.dspy", + f"/{MOD}/api/sd_case_create.dspy", + f"/{MOD}/api/sd_case_update.dspy", + f"/{MOD}/api/sd_case_delete.dspy", + f"/{MOD}/api/sd_env_create.dspy", + f"/{MOD}/api/sd_env_update.dspy", + f"/{MOD}/api/sd_env_delete.dspy", + f"/{MOD}/api/sd_iter_create.dspy", + f"/{MOD}/api/sd_iter_update.dspy", + f"/{MOD}/api/sd_iter_delete.dspy", + f"/{MOD}/api/sd_plan_create.dspy", + f"/{MOD}/api/sd_plan_update.dspy", + f"/{MOD}/api/sd_plan_delete.dspy", + f"/{MOD}/api/sd_proj_create.dspy", + f"/{MOD}/api/sd_proj_update.dspy", + f"/{MOD}/api/sd_proj_delete.dspy", + # Search data sources + f"/{MOD}/api/get_search_bug_status.dspy", + f"/{MOD}/api/get_search_case_status.dspy", + f"/{MOD}/api/get_search_case_type.dspy", + f"/{MOD}/api/get_search_env_status.dspy", + f"/{MOD}/api/get_search_env_type.dspy", + f"/{MOD}/api/get_search_iteration_status.dspy", + f"/{MOD}/api/get_search_iteration_type.dspy", + f"/{MOD}/api/get_search_plan_status.dspy", + f"/{MOD}/api/get_search_plan_type.dspy", + f"/{MOD}/api/get_search_priority.dspy", + f"/{MOD}/api/get_search_project_type.dspy", + f"/{MOD}/api/get_search_reporter_type.dspy", + f"/{MOD}/api/get_search_severity.dspy", + f"/{MOD}/api/get_search_status.dspy", ] -def find_sage_root(): - """Find Sage root directory.""" - for candidate in [ - os.path.expanduser("~/repos/sage"), - os.path.expanduser("~/sage"), - ]: - if os.path.isdir(os.path.join(candidate, "wwwroot")): - return candidate +def find_app_root(): + for c in [os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + os.path.expanduser("~/pipeline")]: + if os.path.isfile(os.path.join(c, "set_role_perm.py")): + return c return None def main(): - sage_root = find_sage_root() - if not sage_root: - print("ERROR: Sage root not found") + root = find_app_root() + if not root: + print("ERROR: pipeline root not found") sys.exit(1) - - set_perm = os.path.join(sage_root, "scripts", "set_role_perm.py") - if not os.path.exists(set_perm): - set_perm = os.path.join(sage_root, "set_role_perm.py") - - if not os.path.exists(set_perm): - print(f"ERROR: set_role_perm.py not found in {sage_root}") - sys.exit(1) - - python = os.path.join(sage_root, "py3", "bin", "python") - if not os.path.exists(python): - python = sys.executable - + set_perm = os.path.join(root, "set_role_perm.py") + py = sys.executable count = 0 for role, paths in [("any", PATHS_ANY), ("logined", PATHS_LOGINED)]: for path in paths: - cmd = [python, set_perm, role, path] - result = subprocess.run(cmd, capture_output=True, text=True, cwd=sage_root) - if result.returncode == 0: + cmd = [py, set_perm, role, path] + r = subprocess.run(cmd, capture_output=True, text=True, cwd=root) + if r.returncode == 0: count += 1 else: - print(f"WARN: {path} ({role}): {result.stderr.strip()}") - - print(f"Registered {count} paths for module '{MOD}'") + print(f" WARN: {path} -> {r.stderr.strip()[-80:]}") + print(f"Registered {count}/{len(PATHS_ANY)+len(PATHS_LOGINED)} paths for {MOD}") if __name__ == "__main__": diff --git a/wwwroot/api/cockpit_chat.dspy b/wwwroot/api/cockpit_chat.dspy index 5c1d21e..afc790f 100644 --- a/wwwroot/api/cockpit_chat.dspy +++ b/wwwroot/api/cockpit_chat.dspy @@ -1,58 +1,241 @@ -# cockpit_chat.dspy - Send message or list conversation -# POST: action=send_message, iteration_id, message_text -# GET: action=list_messages, iteration_id (optional), task_id (optional) +# cockpit_chat.dspy - Product-grade LLM conversation with context +# POST: action=send_message, iteration_id, message_text, model_id, file_paths +# GET: action=list_messages, iteration_id, task_id + +import aiohttp action = (params_kw or {}).get('action', 'list_messages') dbname = get_module_dbname('pipeline-sdlc') +DEFAULT_SYSTEM_PROMPT = """你是一个专业的软件开发 Agent,名为「开发产线驾驶舱」。你的职责是帮助用户完成软件开发生命周期的各个环节:需求分析、设计、编码、测试、部署。 + +对话规则: +1. 简洁专业,用中文回复 +2. 当用户描述需求时,先理解并复述确认,然后给出分析和建议 +3. 如果用户提到了项目/迭代,主动关联上下文 +4. 可以建议启动开发产线来推进工作 +5. 对于代码相关问题,给出具体的代码示例 +6. 记住对话历史,保持上下文连贯 + +当前你可以帮助用户完成: +- 创建和管理项目、迭代 +- 数据表设计、CRUD 设计、API 设计 +- 代码生成、规范检查、自动修复 +- 测试用例生成、功能测试、Bug 管理 +- 环境部署和验证""" + + +async def _load_agent_settings(sor, uid): + """Load user's agent settings, return defaults if not set.""" + recs = await sor.sqlExe( + "SELECT default_llm_id, system_prompt, temperature, max_context_messages FROM sd_agent_settings WHERE user_id=${uid}$", + {"uid": uid} + ) + if recs: + r = recs[0] + return { + 'llm_id': getattr(r, 'default_llm_id', None), + 'system_prompt': getattr(r, 'system_prompt', None) or DEFAULT_SYSTEM_PROMPT, + 'temperature': float(getattr(r, 'temperature', 0.7) or 0.7), + 'max_context': int(getattr(r, 'max_context_messages', 30) or 30), + } + return { + 'llm_id': None, + 'system_prompt': DEFAULT_SYSTEM_PROMPT, + 'temperature': 0.7, + 'max_context': 30, + } + + +async def _select_model(sor, preferred_llm_id, has_files): + """Select best model: prefer user choice, then multimodal if files, else first active text.""" + # If user has preferred model, use it + if preferred_llm_id: + recs = await sor.sqlExe( + "SELECT id, name, provider, model_id, api_base, api_key, capabilities FROM llm WHERE id=${lid}$ AND status='active'", + {"lid": preferred_llm_id} + ) + if recs: + return recs[0] + + # Auto-select based on file presence + if has_files: + recs = await sor.sqlExe( + "SELECT id, name, provider, model_id, api_base, api_key, capabilities FROM llm WHERE status='active' AND capabilities LIKE '%multimodal%' LIMIT 1", + {} + ) + if recs: + return recs[0] + + # Fallback: first active model + recs = await sor.sqlExe( + "SELECT id, name, provider, model_id, api_base, api_key, capabilities FROM llm WHERE status='active' LIMIT 1", + {} + ) + if recs: + return recs[0] + return None + + +async def _build_context(sor, iteration_id, task_id, max_msgs, system_prompt): + """Build LLM messages array with Hermes-style context.""" + messages = [{"role": "system", "content": system_prompt}] + + # Project/iteration context + if iteration_id: + iters = await sor.sqlExe( + "SELECT i.iteration_name, i.iteration_type, i.status, i.scope, p.name as project_name, p.description, p.tech_stack " + "FROM sd_iterations i LEFT JOIN sd_projects p ON i.project_id=p.id WHERE i.id=${iid}$", + {"iid": iteration_id} + ) + if iters: + it = iters[0] + ctx_parts = ["## 当前上下文"] + ctx_parts.append(f"项目: {getattr(it, 'project_name', '未知')}") + ctx_parts.append(f"迭代: {getattr(it, 'iteration_name', '未知')}") + ctx_parts.append(f"类型: {getattr(it, 'iteration_type', '')}") + ctx_parts.append(f"状态: {getattr(it, 'status', '')}") + desc = getattr(it, 'description', '') + if desc: + ctx_parts.append(f"项目描述: {desc[:500]}") + stack = getattr(it, 'tech_stack', '') + if stack: + ctx_parts.append(f"技术栈: {stack[:300]}") + scope = getattr(it, 'scope', '') + if scope: + ctx_parts.append(f"迭代范围: {scope[:500]}") + messages.append({"role": "system", "content": "\n".join(ctx_parts)}) + + # Task context + if task_id: + tasks = await sor.sqlExe( + "SELECT id, status, pipeline_id FROM pipeline_tasks WHERE id=${tid}$", + {"tid": task_id} + ) + if tasks: + t = tasks[0] + messages.append({"role": "system", "content": f"关联 Pipeline 任务: {t.id}, 状态: {getattr(t, 'status', 'unknown')}"}) + + # Conversation history + where = [] + params = {} + if task_id: + where.append("task_id=${tid}$") + params["tid"] = task_id + if iteration_id: + where.append("iteration_id=${iid}$") + params["iid"] = iteration_id + + if where: + sql = f"SELECT role, content FROM sd_conversations WHERE {' OR '.join(where)} ORDER BY created_at DESC LIMIT ${max_msgs}$" + params["max_msgs"] = max_msgs + history = await sor.sqlExe(sql, params) + # Reverse to chronological order + for h in reversed(history): + role = getattr(h, 'role', 'user') + content = getattr(h, 'content', '') + if role in ('user', 'agent'): + messages.append({"role": "user" if role == "user" else "assistant", "content": content}) + + return messages + + +async def _call_llm(model_info, messages, temperature): + """Call LLM API directly using model config from llm table.""" + api_base = model_info.api_base.rstrip('/') + api_key = model_info.api_key + model_id = model_info.model_id + + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + } + payload = { + "model": model_id, + "messages": messages, + "temperature": temperature, + } + + timeout = aiohttp.ClientTimeout(total=120) + async with aiohttp.ClientSession(timeout=timeout) as session: + async with session.post(f"{api_base}/chat/completions", headers=headers, json=payload) as resp: + if resp.status != 200: + text = await resp.text() + raise ValueError(f"LLM API error {resp.status}: {text[:300]}") + data = await resp.json() + content = data["choices"][0]["message"]["content"] + # Truncate overly long responses + if len(content) > 8000: + content = content[:8000] + "\n\n...(内容过长已截断)" + return content + + +# ==================== ACTION HANDLERS ==================== + if action == 'send_message': iteration_id = (params_kw or {}).get('iteration_id', '') task_id = (params_kw or {}).get('task_id', '') message_text = (params_kw or {}).get('message_text', '').strip() + model_id = (params_kw or {}).get('model_id', '') + file_paths_raw = (params_kw or {}).get('file_paths', '[]') - if not iteration_id or not message_text: - return json.dumps({"error": "Missing iteration_id or message_text"}, ensure_ascii=False) + if not message_text: + return json.dumps({"error": "message_text is required"}, ensure_ascii=False) + + # Parse file paths + has_files = False + try: + fps = json.loads(file_paths_raw) + has_files = len(fps) > 0 + except Exception: + pass uid = await get_user() async with DBPools().sqlorContext(dbname) as sor: - # Save user message + # 1. Save user message msg_id = getID() await sor.C('sd_conversations', { 'id': msg_id, - 'iteration_id': iteration_id, + 'iteration_id': iteration_id or '', 'task_id': task_id or '', 'step_name': '', 'role': 'user', 'content': message_text, - 'attachments': '[]', + 'attachments': file_paths_raw, 'msg_type': 'text', 'org_id': '0', 'created_by': uid }) - # Generate agent response - # 1. Check if there's an active pipeline task for this iteration - if task_id: - agent_reply = f"收到你的消息。当前任务 {task_id} 正在执行中,你的输入已记录。" - else: - # Check for existing iterations - iters = await sor.sqlExe( - "SELECT id, task_id, status FROM sd_iterations WHERE id=${iid}$", {"iid": iteration_id}) - if iters: - it = iters[0] - if hasattr(it, 'task_id') and it.task_id: - agent_reply = f"收到。迭代任务 {it.task_id} 状态: {it.status}。" - else: - agent_reply = f"收到你的需求:「{message_text[:50]}...」。请确认是否要为此迭代启动开发产线?" - else: - agent_reply = f"收到你的需求:「{message_text[:50]}...」。建议先在项目管理中创建项目和迭代。" + # 2. Load agent settings + settings = await _load_agent_settings(sor, uid) - # Save agent message + # 3. Select model (preferred → auto by file type → first active) + selected_llm_id = model_id or settings['llm_id'] + model_info = await _select_model(sor, selected_llm_id, has_files) + if not model_info: + return json.dumps({"error": "No active LLM model configured"}, ensure_ascii=False) + + # 4. Build context + messages = await _build_context( + sor, iteration_id, task_id, + settings['max_context'], settings['system_prompt'] + ) + # Append current user message + messages.append({"role": "user", "content": message_text}) + + # 5. Call LLM + try: + agent_reply = await _call_llm(model_info, messages, settings['temperature']) + except Exception as e: + agent_reply = f"抱歉,模型调用失败: {str(e)[:200]}" + + # 6. Save agent response agent_msg_id = getID() await sor.C('sd_conversations', { 'id': agent_msg_id, - 'iteration_id': iteration_id, + 'iteration_id': iteration_id or '', 'task_id': task_id or '', 'step_name': '', 'role': 'agent', @@ -63,10 +246,16 @@ if action == 'send_message': 'created_by': 'system' }) - return json.dumps({"success": True, "message_id": msg_id}, ensure_ascii=False) + return json.dumps({ + "success": True, + "message_id": msg_id, + "agent_reply": agent_reply, + "model_used": model_info.name, + }, ensure_ascii=False) + else: - # list_messages - return messages as Bricks widget JSON + # list_messages - return conversation as Bricks widget JSON iteration_id = (params_kw or {}).get('iteration_id', '') task_id = (params_kw or {}).get('task_id', '') @@ -85,7 +274,6 @@ else: sql = f"SELECT role, content, msg_type, created_at FROM sd_conversations WHERE {' OR '.join(where)} ORDER BY created_at ASC LIMIT 50" msgs = await sor.sqlExe(sql, params) - # Build chat messages widget msg_widgets = [] for m in msgs: role = m.role if hasattr(m, 'role') else '' @@ -99,12 +287,12 @@ else: elif role == 'user': bg = '#dbeafe' align = 'flex-end' - label = '你' + label = '\u4f60' label_color = '#2563eb' else: bg = '#f1f5f9' align = 'center' - label = '系统' + label = '\u7cfb\u7edf' label_color = '#94a3b8' msg_widgets.append({ @@ -134,7 +322,7 @@ else: msg_widgets.append({ "widgettype": "Text", "options": { - "text": "暂无对话记录。选择一个迭代后,在下方输入框中开始对话。", + "text": "\u6682\u65e0\u5bf9\u8bdd\u8bb0\u5f55\u3002\u9009\u62e9\u4e00\u4e2a\u8fed\u4ee3\u540e\uff0c\u5728\u4e0b\u65b9\u8f93\u5165\u6846\u4e2d\u5f00\u59cb\u5bf9\u8bdd\u3002", "cfontsize": 0.9, "color": "#94a3b8", "padding": "20px" } }) diff --git a/wwwroot/api/cockpit_model_options.dspy b/wwwroot/api/cockpit_model_options.dspy new file mode 100644 index 0000000..8bb29d5 --- /dev/null +++ b/wwwroot/api/cockpit_model_options.dspy @@ -0,0 +1,22 @@ +# cockpit_model_options.dspy - Returns active models with capabilities for dropdown +# Used by cockpit model selector + +dbname = get_module_dbname('pipeline-sdlc') + +async with DBPools().sqlorContext(dbname) as sor: + recs = await sor.sqlExe( + "SELECT id, name, provider, model_id, capabilities FROM llm WHERE status='active' ORDER BY name", + {} + ) + +rows = [] +for r in recs: + rows.append({ + 'value': r.id, + 'text': f"{r.name} ({r.provider})", + 'provider': r.provider, + 'model_id': r.model_id, + 'capabilities': r.capabilities or 'text', + }) + +return rows diff --git a/wwwroot/api/get_pipeline_options.dspy b/wwwroot/api/get_pipeline_options.dspy new file mode 100644 index 0000000..a6b85c8 --- /dev/null +++ b/wwwroot/api/get_pipeline_options.dspy @@ -0,0 +1,10 @@ +import json +result = json.dumps([{'value': '', 'text': '请选择'}], ensure_ascii=False) +try: + async with get_sor_context(request._run_ns, 'pipeline') as sor: + rows = await sor.sqlExe("select id as value, name as text from pipelines order by name") + r = [{'value': '', 'text': '请选择'}] + list(rows) + return json.dumps(r, ensure_ascii=False) +except Exception as e: + debug(f'get_pipeline_options error: {e}') +return json.dumps(result, ensure_ascii=False) diff --git a/wwwroot/index.ui b/wwwroot/index.ui index 54cda1e..e59517f 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -16,12 +16,12 @@ "gap": "12px" }, "subwidgets": [ - { "widgettype": "Title2", "options": { "text": "开发产线驾驶舱" } }, - { "widgettype": "Text", "options": { "text": "AI驱动的对话式开发", "cfontsize": 0.9, "color": "#94a3b8" } }, + { "widgettype": "Title2", "options": { "text": "\u5f00\u53d1\u4ea7\u7ebf\u9a7e\u9a76\u8231" } }, + { "widgettype": "Text", "options": { "text": "AI\u9a71\u52a8\u7684\u5bf9\u8bdd\u5f0f\u5f00\u53d1", "cfontsize": 0.9, "color": "#94a3b8" } }, { "widgettype": "Filler" }, { "widgettype": "Button", - "options": { "name": "refresh", "label": "刷新", "css": "small" }, + "options": { "name": "refresh", "label": "\u5237\u65b0", "css": "small" }, "binds": [{ "wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": "location.reload();" @@ -76,8 +76,8 @@ "widgettype": "VBox", "options": { "alignItems": "center", "padding": "40px 0", "gap": "12px" }, "subwidgets": [ - { "widgettype": "Text", "options": { "text": "选择一个迭代后,在下方输入你的需求。", "cfontsize": 1, "color": "#64748b" } }, - { "widgettype": "Text", "options": { "text": "Agent 将自动分析需求、生成设计、驱动产线执行。", "cfontsize": 0.85, "color": "#94a3b8" } } + { "widgettype": "Text", "options": { "text": "\u9009\u62e9\u4e00\u4e2a\u8fed\u4ee3\u540e\uff0c\u5728\u4e0b\u65b9\u8f93\u5165\u4f60\u7684\u9700\u6c42\u3002", "cfontsize": 1, "color": "#64748b" } }, + { "widgettype": "Text", "options": { "text": "Agent \u5c06\u81ea\u52a8\u5206\u6790\u9700\u6c42\u3001\u751f\u6210\u8bbe\u8ba1\u3001\u9a71\u52a8\u4ea7\u7ebf\u6267\u884c\u3002", "cfontsize": 0.85, "color": "#94a3b8" } } ] } ] @@ -98,7 +98,7 @@ "event": "inputed", "actiontype": "script", "target": "self", - "script": "var prompt=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:prompt,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));var at=new bricks.Text({text:'\u6b63\u5728\u5206\u6790\u9700\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);}var body='message_text='+encodeURIComponent(prompt)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){location.reload();}else{if(at)at.set_text('\u9519\u8bef: '+(r.error||'\u672a\u77e5\u9519\u8bef'));}});" + "script": "var p=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);var at=null;if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:p,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));at=new bricks.Text({text:'\\u6b63\\u5728\\u5206\\u6790\\u9700\\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);}var body='message_text='+encodeURIComponent(p)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){if(at)at.set_text(r.agent_reply||'\\u5df2\\u5904\\u7406');var s=bricks.getWidgetById('stats_row',bricks.app);if(s)s.render({});}else{if(at)at.set_text('\\u9519\\u8bef: '+(r.error||'\\u672a\\u77e5\\u9519\\u8bef'));}});" } ] } diff --git a/wwwroot/pipeline_sdlc b/wwwroot/pipeline_sdlc deleted file mode 120000 index 945c9b4..0000000 --- a/wwwroot/pipeline_sdlc +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/wwwroot/sd_bugs/index.ui b/wwwroot/sd_bugs/index.ui index fd7cc27..b14ba7d 100644 --- a/wwwroot/sd_bugs/index.ui +++ b/wwwroot/sd_bugs/index.ui @@ -50,26 +50,26 @@ "severity": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_severity.dspy')}}", - "valueField": "severity", - "textField": "severity_text" + "valueField": "value", + "textField": "text" }, "priority": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", - "valueField": "priority", - "textField": "priority_text" + "valueField": "value", + "textField": "text" }, "status": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}", - "valueField": "status", - "textField": "status_text" + "valueField": "value", + "textField": "text" }, "reporter_type": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}", - "valueField": "reporter_type", - "textField": "reporter_type_text" + "valueField": "value", + "textField": "text" } } }, @@ -107,7 +107,7 @@ "valueField": "iteration_id", "textField": "iteration_id_text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "sd_iterations", "tblvalue": "id", "tbltext": "iteration_name", @@ -165,15 +165,15 @@ "default": "'major'", "label": "严重程度", "uitype": "code", - "valueField": "severity", - "textField": "severity_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "severity", - "textField": "severity_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_bug_severity'" }, "dataurl": "{{entire_url('../api/get_search_severity.dspy')}}" @@ -187,15 +187,15 @@ "default": "'P1'", "label": "优先级", "uitype": "code", - "valueField": "priority", - "textField": "priority_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "priority", - "textField": "priority_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_priority'" }, "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}" @@ -209,15 +209,15 @@ "default": "'open'", "label": "Bug状态", "uitype": "code", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_bug_status'" }, "dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}" @@ -230,15 +230,15 @@ "nullable": "no", "label": "提交人类型", "uitype": "code", - "valueField": "reporter_type", - "textField": "reporter_type_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "reporter_type", - "textField": "reporter_type_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_reporter_type'" }, "dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}" diff --git a/wwwroot/sd_cockpit/index.ui b/wwwroot/sd_cockpit/index.ui index 5bbc96a..e59517f 100644 --- a/wwwroot/sd_cockpit/index.ui +++ b/wwwroot/sd_cockpit/index.ui @@ -16,12 +16,12 @@ "gap": "12px" }, "subwidgets": [ - { "widgettype": "Title2", "options": { "text": "开发产线驾驶舱" } }, - { "widgettype": "Text", "options": { "text": "AI驱动的对话式开发", "cfontsize": 0.9, "color": "#94a3b8" } }, + { "widgettype": "Title2", "options": { "text": "\u5f00\u53d1\u4ea7\u7ebf\u9a7e\u9a76\u8231" } }, + { "widgettype": "Text", "options": { "text": "AI\u9a71\u52a8\u7684\u5bf9\u8bdd\u5f0f\u5f00\u53d1", "cfontsize": 0.9, "color": "#94a3b8" } }, { "widgettype": "Filler" }, { "widgettype": "Button", - "options": { "name": "refresh", "label": "刷新", "css": "small" }, + "options": { "name": "refresh", "label": "\u5237\u65b0", "css": "small" }, "binds": [{ "wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": "location.reload();" @@ -76,8 +76,8 @@ "widgettype": "VBox", "options": { "alignItems": "center", "padding": "40px 0", "gap": "12px" }, "subwidgets": [ - { "widgettype": "Text", "options": { "text": "选择一个迭代后,在下方输入你的需求。", "cfontsize": 1, "color": "#64748b" } }, - { "widgettype": "Text", "options": { "text": "Agent 将自动分析需求、生成设计、驱动产线执行。", "cfontsize": 0.85, "color": "#94a3b8" } } + { "widgettype": "Text", "options": { "text": "\u9009\u62e9\u4e00\u4e2a\u8fed\u4ee3\u540e\uff0c\u5728\u4e0b\u65b9\u8f93\u5165\u4f60\u7684\u9700\u6c42\u3002", "cfontsize": 1, "color": "#64748b" } }, + { "widgettype": "Text", "options": { "text": "Agent \u5c06\u81ea\u52a8\u5206\u6790\u9700\u6c42\u3001\u751f\u6210\u8bbe\u8ba1\u3001\u9a71\u52a8\u4ea7\u7ebf\u6267\u884c\u3002", "cfontsize": 0.85, "color": "#94a3b8" } } ] } ] @@ -98,7 +98,7 @@ "event": "inputed", "actiontype": "script", "target": "self", - "script": "var prompt=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:prompt,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));var at=new bricks.Text({text:'\u6b63\u5728\u5206\u6790\u9700\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);chat.scrollTo(0,chat.dom_element.scrollHeight);}var body='message_text='+encodeURIComponent(prompt)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){location.reload();}else{if(at)at.set_text('\u9519\u8bef: '+(r.error||'\u672a\u77e5\u9519\u8bef'));}});" + "script": "var p=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);var at=null;if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:p,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));at=new bricks.Text({text:'\\u6b63\\u5728\\u5206\\u6790\\u9700\\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);}var body='message_text='+encodeURIComponent(p)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){if(at)at.set_text(r.agent_reply||'\\u5df2\\u5904\\u7406');var s=bricks.getWidgetById('stats_row',bricks.app);if(s)s.render({});}else{if(at)at.set_text('\\u9519\\u8bef: '+(r.error||'\\u672a\\u77e5\\u9519\\u8bef'));}});" } ] } diff --git a/wwwroot/sd_dashboard/dashboard_data.dspy b/wwwroot/sd_dashboard/dashboard_data.dspy new file mode 100644 index 0000000..eab4588 --- /dev/null +++ b/wwwroot/sd_dashboard/dashboard_data.dspy @@ -0,0 +1,84 @@ +""" +SDLC Dashboard Data API +Returns real-time stats for the dashboard widgets +""" +from sqlor.dbpools import DBPools + +async def main(): + dbname = get_module_dbname('pipeline-sdlc') + + async with DBPools().sqlorContext(dbname) as sor: + # 1) Project stats + projects = await sor.sqlExe( + "SELECT COUNT(*) as total, SUM(CASE WHEN status='active' THEN 1 ELSE 0 END) as active FROM sd_projects", {}) + p = projects[0] if projects else None + + # 2) Active iterations + iters = await sor.sqlExe( + "SELECT COUNT(*) as cnt FROM sd_iterations WHERE status='in_progress'", {}) + active_iters = iters[0].cnt if iters else 0 + + # 3) Open bugs + bugs = await sor.sqlExe( + "SELECT COUNT(*) as cnt FROM sd_bugs WHERE status!='closed' AND status!='resolved'", {}) + open_bugs = bugs[0].cnt if bugs else 0 + + # 4) Pending reviews + reviews = await sor.sqlExe( + "SELECT COUNT(*) as cnt FROM pipeline_human_tasks WHERE status='pending' AND task_type='review'", {}) + pending_reviews = reviews[0].cnt if reviews else 0 + + # 5) Recent iterations with progress + recent_iters = await sor.sqlExe(""" + SELECT i.name, i.status, i.start_date, i.end_date, + COALESCE(tc.total,0) as total_tasks, + COALESCE(tc.completed,0) as completed_tasks + FROM sd_iterations i + LEFT JOIN ( + SELECT iteration_id, COUNT(*) as total, + SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as completed + FROM sd_test_plans GROUP BY iteration_id + ) tc ON tc.iteration_id = i.id + WHERE i.status != 'closed' + ORDER BY i.start_date DESC LIMIT 5 + """, {}) + + iter_list = [] + for r in recent_iters: + pct = round(r.completed_tasks / r.total_tasks * 100) if r.total_tasks > 0 else 0 + iter_list.append({ + 'name': r.name, + 'status': r.status, + 'progress': pct, + 'total': r.total_tasks, + 'completed': r.completed_tasks + }) + + # 6) Bug by severity + bug_by_sev = await sor.sqlExe(""" + SELECT COALESCE(severity,'unset') as severity, COUNT(*) as cnt + FROM sd_bugs WHERE status!='closed' + GROUP BY severity ORDER BY cnt DESC + """, {}) + + severity_map = { + 'critical': {'label': '严重', 'color': '#FF4444'}, + 'major': {'label': '主要', 'color': '#FF8800'}, + 'minor': {'label': '次要', 'color': '#FFAA00'}, + 'trivial': {'label': '轻微', 'color': '#888888'}, + 'unset': {'label': '未分类', 'color': '#666666'} + } + bugs_data = [] + for r in bug_by_sev: + sev = severity_map.get(r.severity, {'label': r.severity, 'color': '#888'}) + bugs_data.append({'label': sev['label'], 'count': r.cnt, 'color': sev['color']}) + + return { + 'projects_total': p.total if p else 0, + 'projects_active': p.active if p else 0, + 'active_iterations': active_iters, + 'open_bugs': open_bugs, + 'pending_reviews': pending_reviews, + 'recent_iterations': iter_list, + 'bugs_by_severity': bugs_data + } diff --git a/wwwroot/sd_dashboard/index.ui b/wwwroot/sd_dashboard/index.ui index a2b4baa..134eb39 100644 --- a/wwwroot/sd_dashboard/index.ui +++ b/wwwroot/sd_dashboard/index.ui @@ -1,53 +1,53 @@ { "widgettype": "VBox", - "options": {"width": "100%", "height": "100%", "padding": "20px", "gap": "20px"}, + "options": {"width": "100%", "height": "100%", "padding": "24px", "gap": "24px"}, "subwidgets": [ { - "widgettype": "Text", - "options": {"text": "📊 项目概览 Dashboard", "cfontsize": 24, "color": "#E0E0E0", "fontWeight": "bold"} - }, - { - "widgettype": "Text", - "options": {"text": "软件开发生命周期全局统计", "cfontsize": 14, "color": "#888888"} + "widgettype": "HBox", + "options": {"alignItems": "center", "gap": "12px"}, + "subwidgets": [ + {"widgettype": "Title2", "options": {"text": "开发产线仪表盘"}}, + {"widgettype": "Text", "options": {"text": "项目/迭代/Bug实时概览", "cfontsize": 1.1, "color": "#888"}} + ] }, { "widgettype": "ResponsableBox", - "options": {"gap": "16px", "minWidth": "180px"}, + "options": {"gap": "16px", "minWidth": "200px"}, "subwidgets": [ { "widgettype": "VBox", - "options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"}, + "options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "12", "cfontsize": 36, "color": "#4A90D9", "fontWeight": "bold"}}, - {"widgettype": "Text", "options": {"text": "活跃项目", "cfontsize": 14, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "Active Projects", "cfontsize": 11, "color": "#888888"}} + {"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#60a5fa", "fontWeight": "bold"}}, + {"widgettype": "Text", "options": {"text": "活跃项目", "cfontsize": 1, "color": "#e2e8f0"}}, + {"widgettype": "Text", "options": {"text": "Active Projects", "cfontsize": 0.8, "color": "#94a3b8"}} ] }, { "widgettype": "VBox", - "options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"}, + "options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "5", "cfontsize": 36, "color": "#4AD97A", "fontWeight": "bold"}}, - {"widgettype": "Text", "options": {"text": "进行中迭代", "cfontsize": 14, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "Active Iterations", "cfontsize": 11, "color": "#888888"}} + {"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#4ade80", "fontWeight": "bold"}}, + {"widgettype": "Text", "options": {"text": "进行中迭代", "cfontsize": 1, "color": "#e2e8f0"}}, + {"widgettype": "Text", "options": {"text": "Active Sprints", "cfontsize": 0.8, "color": "#94a3b8"}} ] }, { "widgettype": "VBox", - "options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"}, + "options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "23", "cfontsize": 36, "color": "#D9A04A", "fontWeight": "bold"}}, - {"widgettype": "Text", "options": {"text": "待处理Bug", "cfontsize": 14, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "Open Bugs", "cfontsize": 11, "color": "#888888"}} + {"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#fbbf24", "fontWeight": "bold"}}, + {"widgettype": "Text", "options": {"text": "待处理Bug", "cfontsize": 1, "color": "#e2e8f0"}}, + {"widgettype": "Text", "options": {"text": "Open Bugs", "cfontsize": 0.8, "color": "#94a3b8"}} ] }, { "widgettype": "VBox", - "options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"}, + "options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "3", "cfontsize": 36, "color": "#9A4AD9", "fontWeight": "bold"}}, - {"widgettype": "Text", "options": {"text": "待审批", "cfontsize": 14, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "Pending Reviews", "cfontsize": 11, "color": "#888888"}} + {"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#a78bfa", "fontWeight": "bold"}}, + {"widgettype": "Text", "options": {"text": "待审批", "cfontsize": 1, "color": "#e2e8f0"}}, + {"widgettype": "Text", "options": {"text": "Pending Reviews", "cfontsize": 0.8, "color": "#94a3b8"}} ] } ] @@ -58,116 +58,18 @@ "subwidgets": [ { "widgettype": "VBox", - "options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "12px", "border": "1px solid #2A2A3E", "cwidth": 50}, + "options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "16px", "border": "1px solid #334155", "cwidth": 60}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "📈 迭代进度", "cfontsize": 16, "color": "#E0E0E0", "fontWeight": "bold"}}, - { - "widgettype": "VBox", - "options": {"gap": "8px"}, - "subwidgets": [ - { - "widgettype": "HBox", - "options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "Sprint 23 - API重构", "cfontsize": 13, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "72%", "cfontsize": 13, "color": "#4AD97A", "fontWeight": "bold"}} - ] - }, - { - "widgettype": "HBox", - "options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "Sprint 23b - 测试覆盖", "cfontsize": 13, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "45%", "cfontsize": 13, "color": "#D9A04A", "fontWeight": "bold"}} - ] - }, - { - "widgettype": "HBox", - "options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "Sprint 24 - 功能开发", "cfontsize": 13, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "规划中", "cfontsize": 13, "color": "#4A90D9", "fontWeight": "bold"}} - ] - } - ] - } + {"widgettype": "Title3", "options": {"text": "迭代进度"}}, + {"widgettype": "Text", "options": {"text": "暂无数据,请先创建项目和迭代", "cfontsize": 0.9, "color": "#64748b", "padding": "20px 0"}} ] }, { "widgettype": "VBox", - "options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "12px", "border": "1px solid #2A2A3E", "cwidth": 50}, + "options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "16px", "border": "1px solid #334155", "cwidth": 40}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "🐛 Bug趋势", "cfontsize": 16, "color": "#E0E0E0", "fontWeight": "bold"}}, - { - "widgettype": "VBox", - "options": {"gap": "8px"}, - "subwidgets": [ - { - "widgettype": "HBox", - "options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "严重 Critical", "cfontsize": 13, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "3", "cfontsize": 13, "color": "#D94A4A", "fontWeight": "bold"}} - ] - }, - { - "widgettype": "HBox", - "options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "高 High", "cfontsize": 13, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "8", "cfontsize": 13, "color": "#D9A04A", "fontWeight": "bold"}} - ] - }, - { - "widgettype": "HBox", - "options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "中 Medium", "cfontsize": 13, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "12", "cfontsize": 13, "color": "#4A90D9", "fontWeight": "bold"}} - ] - } - ] - } - ] - } - ] - }, - { - "widgettype": "VBox", - "options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "12px", "border": "1px solid #2A2A3E"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "🚀 最近部署 Recent Deployments", "cfontsize": 16, "color": "#E0E0E0", "fontWeight": "bold"}}, - { - "widgettype": "ResponsableBox", - "options": {"gap": "12px", "minWidth": "250px"}, - "subwidgets": [ - { - "widgettype": "VBox", - "options": {"backgroundColor": "#121212", "padding": "12px", "borderRadius": "6px", "gap": "4px", "cwidth": 33}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "✅ Production", "cfontsize": 13, "color": "#4AD97A", "fontWeight": "bold"}}, - {"widgettype": "Text", "options": {"text": "v2.2.5 → v2.2.6", "cfontsize": 12, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "2026-06-15 18:30", "cfontsize": 11, "color": "#888888"}} - ] - }, - { - "widgettype": "VBox", - "options": {"backgroundColor": "#121212", "padding": "12px", "borderRadius": "6px", "gap": "4px", "cwidth": 33}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "🔄 Staging", "cfontsize": 13, "color": "#D9A04A", "fontWeight": "bold"}}, - {"widgettype": "Text", "options": {"text": "v2.3.0-rc1", "cfontsize": 12, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "2026-06-16 10:00", "cfontsize": 11, "color": "#888888"}} - ] - }, - { - "widgettype": "VBox", - "options": {"backgroundColor": "#121212", "padding": "12px", "borderRadius": "6px", "gap": "4px", "cwidth": 33}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "🧪 Dev", "cfontsize": 13, "color": "#4A90D9", "fontWeight": "bold"}}, - {"widgettype": "Text", "options": {"text": "v2.3.0-dev.47", "cfontsize": 12, "color": "#E0E0E0"}}, - {"widgettype": "Text", "options": {"text": "2026-06-16 14:22", "cfontsize": 11, "color": "#888888"}} - ] - } + {"widgettype": "Title3", "options": {"text": "Bug 分布"}}, + {"widgettype": "Text", "options": {"text": "暂无Bug数据", "cfontsize": 0.9, "color": "#64748b", "padding": "20px 0"}} ] } ] diff --git a/wwwroot/sd_deploy_envs/index.ui b/wwwroot/sd_deploy_envs/index.ui index 700ac56..0b48546 100644 --- a/wwwroot/sd_deploy_envs/index.ui +++ b/wwwroot/sd_deploy_envs/index.ui @@ -54,14 +54,14 @@ "env_type": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}", - "valueField": "env_type", - "textField": "env_type_text" + "valueField": "value", + "textField": "text" }, "status": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}", - "valueField": "status", - "textField": "status_text" + "valueField": "value", + "textField": "text" } } }, @@ -97,7 +97,7 @@ "valueField": "project_id", "textField": "project_id_text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "sd_projects", "tblvalue": "id", "tbltext": "name", @@ -114,15 +114,15 @@ "nullable": "no", "label": "环境类型", "uitype": "code", - "valueField": "env_type", - "textField": "env_type_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "env_type", - "textField": "env_type_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_env_type'" }, "dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}" @@ -262,15 +262,15 @@ "default": "'configured'", "label": "环境状态", "uitype": "code", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_env_status'" }, "dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}" diff --git a/wwwroot/sd_iterations/index.ui b/wwwroot/sd_iterations/index.ui index bcd8610..9f69dce 100644 --- a/wwwroot/sd_iterations/index.ui +++ b/wwwroot/sd_iterations/index.ui @@ -70,14 +70,14 @@ "iteration_type": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}", - "valueField": "iteration_type", - "textField": "iteration_type_text" + "valueField": "value", + "textField": "text" }, "status": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}", - "valueField": "status", - "textField": "status_text" + "valueField": "value", + "textField": "text" } } }, @@ -115,7 +115,7 @@ "valueField": "project_id", "textField": "project_id_text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "sd_projects", "tblvalue": "id", "tbltext": "name", @@ -144,15 +144,15 @@ "default": "'new_feature'", "label": "迭代类型", "uitype": "code", - "valueField": "iteration_type", - "textField": "iteration_type_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "iteration_type", - "textField": "iteration_type_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_iteration_type'" }, "dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}" @@ -185,15 +185,15 @@ "default": "'planning'", "label": "迭代状态", "uitype": "code", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_iteration_status'" }, "dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}" diff --git a/wwwroot/sd_projects/add_sd_projects.dspy b/wwwroot/sd_projects/add_sd_projects.dspy index d9f3e85..4ea969b 100644 --- a/wwwroot/sd_projects/add_sd_projects.dspy +++ b/wwwroot/sd_projects/add_sd_projects.dspy @@ -23,6 +23,7 @@ if not userorgid: } } ns['org_id'] = userorgid +ns['created_by'] = userorgid db = DBPools() dbname = get_module_dbname('pipeline_sdlc') diff --git a/wwwroot/sd_projects/index.ui b/wwwroot/sd_projects/index.ui index 1b039f2..91a62a4 100644 --- a/wwwroot/sd_projects/index.ui +++ b/wwwroot/sd_projects/index.ui @@ -58,19 +58,26 @@ "status": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_status.dspy')}}", - "valueField": "status", - "textField": "status_text" + "valueField": "value", + "textField": "text" }, "project_type": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}", - "valueField": "project_type", - "textField": "project_type_text" + "valueField": "value", + "textField": "text" } } }, + , + "pipeline_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_pipeline_options.dspy')}}", + "valueField": "value", + "textField": "text" + } "editexclouded":[ "id", "created_at", @@ -119,15 +126,15 @@ "default": "'web_app'", "label": "项目类型", "uitype": "code", - "valueField": "project_type", - "textField": "project_type_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "project_type", - "textField": "project_type_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_project_type'" }, "dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}" @@ -170,15 +177,15 @@ "default": "'draft'", "label": "项目状态", "uitype": "code", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_project_status'" }, "dataurl": "{{entire_url('../api/get_search_status.dspy')}}" diff --git a/wwwroot/sd_projects/update_sd_projects.dspy b/wwwroot/sd_projects/update_sd_projects.dspy index 54f1c2f..93372fd 100644 --- a/wwwroot/sd_projects/update_sd_projects.dspy +++ b/wwwroot/sd_projects/update_sd_projects.dspy @@ -18,6 +18,7 @@ if not userorgid: } } ns['org_id'] = userorgid +ns['created_by'] = userorgid diff --git a/wwwroot/sd_test_cases/index.ui b/wwwroot/sd_test_cases/index.ui index 118e8aa..47a505e 100644 --- a/wwwroot/sd_test_cases/index.ui +++ b/wwwroot/sd_test_cases/index.ui @@ -50,20 +50,20 @@ "case_type": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}", - "valueField": "case_type", - "textField": "case_type_text" + "valueField": "value", + "textField": "text" }, "status": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}", - "valueField": "status", - "textField": "status_text" + "valueField": "value", + "textField": "text" }, "priority": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", - "valueField": "priority", - "textField": "priority_text" + "valueField": "value", + "textField": "text" } } }, @@ -101,7 +101,7 @@ "valueField": "plan_id", "textField": "plan_id_text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "sd_test_plans", "tblvalue": "id", "tbltext": "plan_name", @@ -129,15 +129,15 @@ "nullable": "no", "label": "用例类型", "uitype": "code", - "valueField": "case_type", - "textField": "case_type_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "case_type", - "textField": "case_type_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_test_case_type'" }, "dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}" @@ -151,15 +151,15 @@ "default": "'P2'", "label": "优先级", "uitype": "code", - "valueField": "priority", - "textField": "priority_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "priority", - "textField": "priority_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_priority'" }, "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}" @@ -209,15 +209,15 @@ "default": "'pending'", "label": "用例状态", "uitype": "code", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_test_case_status'" }, "dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}" diff --git a/wwwroot/sd_test_plans/index.ui b/wwwroot/sd_test_plans/index.ui index e47778d..79a0af2 100644 --- a/wwwroot/sd_test_plans/index.ui +++ b/wwwroot/sd_test_plans/index.ui @@ -61,14 +61,14 @@ "plan_type": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}", - "valueField": "plan_type", - "textField": "plan_type_text" + "valueField": "value", + "textField": "text" }, "status": { "uitype": "code", "dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}", - "valueField": "status", - "textField": "status_text" + "valueField": "value", + "textField": "text" } } }, @@ -103,7 +103,7 @@ "valueField": "iteration_id", "textField": "iteration_id_text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "sd_iterations", "tblvalue": "id", "tbltext": "iteration_name", @@ -131,15 +131,15 @@ "nullable": "no", "label": "方案类型", "uitype": "code", - "valueField": "plan_type", - "textField": "plan_type_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "plan_type", - "textField": "plan_type_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_test_plan_type'" }, "dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}" @@ -189,15 +189,15 @@ "default": "'draft'", "label": "方案状态", "uitype": "code", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "params": { - "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "dbname": "{{'sage'}}", "table": "appcodes_kv", "tblvalue": "k", "tbltext": "v", - "valueField": "status", - "textField": "status_text", + "valueField": "value", + "textField": "text", "cond": "parentid='sd_test_plan_status'" }, "dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}"