feat: 项目角色模型配置 CRUD 界面(模型配置设置界面)
- sd_project_role_models CRUD(index.ui + get/add/update/delete dspy) - 下拉选项:get_model_options(模型)/get_role_options(角色)/get_project_select_options(项目) - load_path 注册权限路径 + models 表模型
This commit is contained in:
parent
87d1848e2d
commit
3785622bac
25
models/sd_project_role_models.json
Normal file
25
models/sd_project_role_models.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "sd_project_role_models",
|
||||||
|
"title": "项目角色模型配置",
|
||||||
|
"primary": ["id"],
|
||||||
|
"catelog": "entity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||||
|
{"name": "project_id", "title": "项目ID", "type": "str", "length": 32, "nullable": "no"},
|
||||||
|
{"name": "role", "title": "角色名", "type": "str", "length": 64, "nullable": "no"},
|
||||||
|
{"name": "model_name", "title": "模型名(llm.name,空=用项目缺省模型)", "type": "str", "length": 128},
|
||||||
|
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"},
|
||||||
|
{"name": "updated_at", "title": "更新时间", "type": "timestamp"}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
{"name": "uk_sd_project_role_models_pr", "idxtype": "unique", "idxfields": ["project_id", "role"]}
|
||||||
|
],
|
||||||
|
"codes": [
|
||||||
|
{"field": "role", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='sd_role'"},
|
||||||
|
{"field": "model_name", "table": "llm", "valuefield": "name", "textfield": "name", "cond": ""}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -60,6 +60,12 @@ PATHS_LOGINED = [
|
|||||||
f"/{MOD}/sd_deploy_envs/update_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_envs/delete_sd_deploy_envs.dspy",
|
||||||
f"/{MOD}/sd_deploy_env_list/index.ui",
|
f"/{MOD}/sd_deploy_env_list/index.ui",
|
||||||
|
# Project Role Models(角色模型配置)
|
||||||
|
f"/{MOD}/sd_project_role_models/index.ui",
|
||||||
|
f"/{MOD}/sd_project_role_models/get_sd_project_role_models.dspy",
|
||||||
|
f"/{MOD}/sd_project_role_models/add_sd_project_role_models.dspy",
|
||||||
|
f"/{MOD}/sd_project_role_models/update_sd_project_role_models.dspy",
|
||||||
|
f"/{MOD}/sd_project_role_models/delete_sd_project_role_models.dspy",
|
||||||
# Review
|
# Review
|
||||||
f"/{MOD}/sd_review/index.ui",
|
f"/{MOD}/sd_review/index.ui",
|
||||||
# API
|
# API
|
||||||
@ -69,6 +75,9 @@ PATHS_LOGINED = [
|
|||||||
f"/{MOD}/api/env_verify.dspy",
|
f"/{MOD}/api/env_verify.dspy",
|
||||||
f"/{MOD}/api/get_iteration_options.dspy",
|
f"/{MOD}/api/get_iteration_options.dspy",
|
||||||
f"/{MOD}/api/get_project_options.dspy",
|
f"/{MOD}/api/get_project_options.dspy",
|
||||||
|
f"/{MOD}/api/get_project_select_options.dspy",
|
||||||
|
f"/{MOD}/api/get_role_options.dspy",
|
||||||
|
f"/{MOD}/api/get_model_options.dspy",
|
||||||
f"/{MOD}/api/get_test_plan_options.dspy",
|
f"/{MOD}/api/get_test_plan_options.dspy",
|
||||||
f"/{MOD}/api/submit_bug.dspy",
|
f"/{MOD}/api/submit_bug.dspy",
|
||||||
f"/{MOD}/api/sd_bug_create.dspy",
|
f"/{MOD}/api/sd_bug_create.dspy",
|
||||||
|
|||||||
10
wwwroot/api/get_model_options.dspy
Normal file
10
wwwroot/api/get_model_options.dspy
Normal file
@ -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 name as value, name as text from llm where status='active' order by name")
|
||||||
|
r = [{'value': '', 'text': '缺省(用项目默认模型)'}] + list(rows)
|
||||||
|
return json.dumps(r, ensure_ascii=False)
|
||||||
|
except Exception as e:
|
||||||
|
debug(f'get_model_options error: {e}')
|
||||||
|
return json.dumps(result, ensure_ascii=False)
|
||||||
10
wwwroot/api/get_project_select_options.dspy
Normal file
10
wwwroot/api/get_project_select_options.dspy
Normal file
@ -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 sd_projects order by created_at desc")
|
||||||
|
r = [{'value': '', 'text': '请选择'}] + list(rows)
|
||||||
|
return json.dumps(r, ensure_ascii=False)
|
||||||
|
except Exception as e:
|
||||||
|
debug(f'get_project_select_options error: {e}')
|
||||||
|
return json.dumps(result, ensure_ascii=False)
|
||||||
12
wwwroot/api/get_role_options.dspy
Normal file
12
wwwroot/api/get_role_options.dspy
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import json
|
||||||
|
roles = [
|
||||||
|
{'value': 'agent.pm', 'text': 'PM 项目经理'},
|
||||||
|
{'value': 'agent.requirement', 'text': '需求分析师'},
|
||||||
|
{'value': 'agent.design', 'text': '系统设计师'},
|
||||||
|
{'value': 'agent.develop', 'text': '开发工程师'},
|
||||||
|
{'value': 'agent.deploy_test', 'text': '测试部署工程师'},
|
||||||
|
{'value': 'agent.test', 'text': '测试工程师'},
|
||||||
|
{'value': 'agent.deploy_prod', 'text': '生产部署工程师'},
|
||||||
|
{'value': 'agent.qc', 'text': 'QC 质量控制'},
|
||||||
|
]
|
||||||
|
return json.dumps(roles, ensure_ascii=False)
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
ns = params_kw.copy()
|
||||||
|
for k,v in ns.items():
|
||||||
|
if v == 'NaN' or v == 'null' or v == '':
|
||||||
|
ns[k] = None
|
||||||
|
id = params_kw.id
|
||||||
|
if not id or len(id) > 32:
|
||||||
|
id = uuid()
|
||||||
|
ns['id'] = id
|
||||||
|
|
||||||
|
if not ns.get('project_id'):
|
||||||
|
return {"widgettype":"Error","options":{"title":"Error","timeout":3,"cwidth":16,"cheight":9,"message":"需要 project_id"}}
|
||||||
|
if not ns.get('role'):
|
||||||
|
return {"widgettype":"Error","options":{"title":"Error","timeout":3,"cwidth":16,"cheight":9,"message":"需要 role"}}
|
||||||
|
|
||||||
|
userorgid = await get_userorgid()
|
||||||
|
if not userorgid:
|
||||||
|
return {
|
||||||
|
"widgettype":"Error",
|
||||||
|
"options":{
|
||||||
|
"title":"Authorization Error",
|
||||||
|
"timeout":3,
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"message":"Please login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
dbname = get_module_dbname('pipeline_sdlc')
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
# 已存在则更新(upsert by project_id+role)
|
||||||
|
r = await sor.sqlExe("select id from sd_project_role_models where project_id=${pid}$ and role=${role}$", {"pid": ns['project_id'], "role": ns['role']})
|
||||||
|
if r:
|
||||||
|
await sor.sqlExe("update sd_project_role_models set model_name=${m}$ where id=${id}$", {"m": ns.get('model_name'), "id": r[0].id})
|
||||||
|
else:
|
||||||
|
await sor.C('sd_project_role_models', ns.copy())
|
||||||
|
return {
|
||||||
|
"widgettype":"Message",
|
||||||
|
"options":{
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"title":"Add Success",
|
||||||
|
"timeout":3,
|
||||||
|
"message":"ok"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"widgettype":"Error",
|
||||||
|
"options":{
|
||||||
|
"title":"Add Error",
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"timeout":3,
|
||||||
|
"message":"failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
ns = params_kw.copy()
|
||||||
|
id = params_kw.id
|
||||||
|
if not id:
|
||||||
|
return {"widgettype":"Error","options":{"title":"Error","timeout":3,"cwidth":16,"cheight":9,"message":"需要 id"}}
|
||||||
|
|
||||||
|
userorgid = await get_userorgid()
|
||||||
|
if not userorgid:
|
||||||
|
return {
|
||||||
|
"widgettype":"Error",
|
||||||
|
"options":{
|
||||||
|
"title":"Authorization Error",
|
||||||
|
"timeout":3,
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"message":"Please login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
dbname = get_module_dbname('pipeline_sdlc')
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
await sor.D('sd_project_role_models', {'id': id})
|
||||||
|
return {
|
||||||
|
"widgettype":"Message",
|
||||||
|
"options":{
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"title":"Delete Success",
|
||||||
|
"timeout":3,
|
||||||
|
"message":"ok"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"widgettype":"Error",
|
||||||
|
"options":{
|
||||||
|
"title":"Delete Error",
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"timeout":3,
|
||||||
|
"message":"failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
ns = params_kw.copy()
|
||||||
|
|
||||||
|
userorgid = await get_userorgid()
|
||||||
|
if not userorgid:
|
||||||
|
return {
|
||||||
|
"widgettype":"Error",
|
||||||
|
"options":{
|
||||||
|
"title":"Authorization Error",
|
||||||
|
"timeout":3,
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"message":"Please login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(f'get_sd_project_role_models.dspy:{ns=}')
|
||||||
|
if not ns.get('page'):
|
||||||
|
ns['page'] = 1
|
||||||
|
if not ns.get('sort'):
|
||||||
|
ns['sort'] = ["role asc"]
|
||||||
|
|
||||||
|
sql = '''select a.*, b.name as project_name
|
||||||
|
from (select * from sd_project_role_models where 1=1 [[filterstr]]) a
|
||||||
|
left join sd_projects b on a.project_id = b.id'''
|
||||||
|
|
||||||
|
filterjson = params_kw.get('data_filter')
|
||||||
|
if filterjson and isinstance(filterjson, str):
|
||||||
|
try:
|
||||||
|
filterjson = json.loads(filterjson)
|
||||||
|
except (json.JSONDecodeError, TypeError):
|
||||||
|
filterjson = None
|
||||||
|
|
||||||
|
if filterjson:
|
||||||
|
filterdic = ns.copy()
|
||||||
|
filterdic['filterstr'] = ''
|
||||||
|
dbf = DBFilter(filterjson)
|
||||||
|
conds = dbf.gen(ns)
|
||||||
|
if conds:
|
||||||
|
ns.update(dbf.consts)
|
||||||
|
conds = f' and {conds}'
|
||||||
|
filterdic['filterstr'] = conds
|
||||||
|
ac = ArgsConvert('[[', ']]')
|
||||||
|
vars = ac.findAllVariables(sql)
|
||||||
|
NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr'}
|
||||||
|
filterdic.update(NameSpace)
|
||||||
|
sql = ac.convert(sql, filterdic)
|
||||||
|
else:
|
||||||
|
ac = ArgsConvert('[[', ']]')
|
||||||
|
sql = ac.convert(sql, {'filterstr': ''})
|
||||||
|
|
||||||
|
debug(f'{sql=}')
|
||||||
|
db = DBPools()
|
||||||
|
dbname = get_module_dbname('pipeline_sdlc')
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
r = await sor.sqlPaging(sql, ns)
|
||||||
|
return r
|
||||||
|
return {
|
||||||
|
"total":0,
|
||||||
|
"rows":[]
|
||||||
|
}
|
||||||
65
wwwroot/sd_project_role_models/index.ui
Normal file
65
wwwroot/sd_project_role_models/index.ui
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
{
|
||||||
|
"widgettype":"VBox",
|
||||||
|
"options":{"cheight":40,"width":"100%"},
|
||||||
|
"subwidgets":[{
|
||||||
|
"id":"sd_project_role_models_tbl",
|
||||||
|
"widgettype":"Tabular",
|
||||||
|
"options":{
|
||||||
|
"width":"100%",
|
||||||
|
"height":"100%",
|
||||||
|
"title":"项目角色模型配置",
|
||||||
|
"css":"card",
|
||||||
|
"editable":{
|
||||||
|
"new_data_url":"{{entire_url('add_sd_project_role_models.dspy')}}",
|
||||||
|
"delete_data_url":"{{entire_url('delete_sd_project_role_models.dspy')}}",
|
||||||
|
"update_data_url":"{{entire_url('update_sd_project_role_models.dspy')}}"
|
||||||
|
},
|
||||||
|
"data_url":"{{entire_url('./get_sd_project_role_models.dspy')}}",
|
||||||
|
"data_method":"GET",
|
||||||
|
"data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}},
|
||||||
|
"row_options":{
|
||||||
|
"browserfields":{
|
||||||
|
"alters":{
|
||||||
|
"project_id":{
|
||||||
|
"uitype":"code",
|
||||||
|
"dataurl":"{{entire_url('../api/get_project_select_options.dspy')}}",
|
||||||
|
"valueField":"value",
|
||||||
|
"textField":"text"
|
||||||
|
},
|
||||||
|
"role":{
|
||||||
|
"uitype":"code",
|
||||||
|
"dataurl":"{{entire_url('../api/get_role_options.dspy')}}",
|
||||||
|
"valueField":"value",
|
||||||
|
"textField":"text"
|
||||||
|
},
|
||||||
|
"model_name":{
|
||||||
|
"uitype":"code",
|
||||||
|
"dataurl":"{{entire_url('../api/get_model_options.dspy')}}",
|
||||||
|
"valueField":"value",
|
||||||
|
"textField":"text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editexclouded":["id","created_at","updated_at","project_name"],
|
||||||
|
"fields":[
|
||||||
|
{"name":"id","title":"主键ID","type":"str","length":32,"cwidth":18,"uitype":"str","datatype":"str","label":"主键ID"},
|
||||||
|
{"name":"project_name","title":"项目","type":"str","length":200,"cwidth":22,"uitype":"str","datatype":"str","label":"项目"},
|
||||||
|
{"name":"project_id","title":"项目ID","type":"str","length":32,"cwidth":0,"uitype":"code","valueField":"value","textField":"text","dataurl":"{{entire_url('../api/get_project_select_options.dspy')}}","label":"项目ID"},
|
||||||
|
{"name":"role","title":"角色","type":"str","length":64,"cwidth":20,"uitype":"code","valueField":"value","textField":"text","dataurl":"{{entire_url('../api/get_role_options.dspy')}}","label":"角色"},
|
||||||
|
{"name":"model_name","title":"模型","type":"str","length":128,"cwidth":20,"uitype":"code","valueField":"value","textField":"text","dataurl":"{{entire_url('../api/get_model_options.dspy')}}","label":"模型(空=缺省)"},
|
||||||
|
{"name":"created_at","title":"创建时间","type":"timestamp","uitype":"str","datatype":"timestamp","label":"创建时间"},
|
||||||
|
{"name":"updated_at","title":"更新时间","type":"timestamp","uitype":"str","datatype":"timestamp","label":"更新时间"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"data_filter":{
|
||||||
|
"AND":[
|
||||||
|
{"field":"role","op":"LIKE","var":"role_input"},
|
||||||
|
{"field":"project_id","op":"=","var":"project_id_input"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"page_rows":100,
|
||||||
|
"cache_limit":5
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
ns = params_kw.copy()
|
||||||
|
for k,v in ns.items():
|
||||||
|
if v == 'NaN' or v == 'null' or v == '':
|
||||||
|
ns[k] = None
|
||||||
|
id = params_kw.id
|
||||||
|
if not id:
|
||||||
|
return {"widgettype":"Error","options":{"title":"Error","timeout":3,"cwidth":16,"cheight":9,"message":"需要 id"}}
|
||||||
|
|
||||||
|
userorgid = await get_userorgid()
|
||||||
|
if not userorgid:
|
||||||
|
return {
|
||||||
|
"widgettype":"Error",
|
||||||
|
"options":{
|
||||||
|
"title":"Authorization Error",
|
||||||
|
"timeout":3,
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"message":"Please login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
dbname = get_module_dbname('pipeline_sdlc')
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
await sor.U('sd_project_role_models', ns.copy())
|
||||||
|
return {
|
||||||
|
"widgettype":"Message",
|
||||||
|
"options":{
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"title":"Update Success",
|
||||||
|
"timeout":3,
|
||||||
|
"message":"ok"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"widgettype":"Error",
|
||||||
|
"options":{
|
||||||
|
"title":"Update Error",
|
||||||
|
"cwidth":16,
|
||||||
|
"cheight":9,
|
||||||
|
"timeout":3,
|
||||||
|
"message":"failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user