feat(llm): 供应商账号改为供应商CRUD子表(llm_account_by_vendor)——弹窗进入只显示/处理选定供应商账号;账号归属服务端守卫(vendor_id不可改/跨供应商禁删);删独立账号列表页(index.ui卡片同步移除)
This commit is contained in:
parent
c16bdb4a28
commit
34ef256fe6
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,6 +8,7 @@ dist/
|
|||||||
# CRUD generated directories (auto-generated by xls2ui at build time, NOT in repo)
|
# CRUD generated directories (auto-generated by xls2ui at build time, NOT in repo)
|
||||||
wwwroot/llm_vendor/
|
wwwroot/llm_vendor/
|
||||||
wwwroot/llm_account/
|
wwwroot/llm_account/
|
||||||
|
wwwroot/llm_account_by_vendor/
|
||||||
wwwroot/llm_model/
|
wwwroot/llm_model/
|
||||||
wwwroot/llm_api_profile/
|
wwwroot/llm_api_profile/
|
||||||
wwwroot/llm_org_policy/
|
wwwroot/llm_org_policy/
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"tblname": "llm_account",
|
"tblname": "llm_account",
|
||||||
|
"alias": "llm_account_by_vendor",
|
||||||
"title": "供应商账号",
|
"title": "供应商账号",
|
||||||
"params": {
|
"params": {
|
||||||
"sortby": "name",
|
"sortby": "name",
|
||||||
@ -7,15 +8,10 @@
|
|||||||
"browserfields": {
|
"browserfields": {
|
||||||
"exclouded": [
|
"exclouded": [
|
||||||
"id",
|
"id",
|
||||||
"api_key"
|
"api_key",
|
||||||
|
"vendor_id"
|
||||||
],
|
],
|
||||||
"alters": {
|
"alters": {
|
||||||
"vendor_id": {
|
|
||||||
"uitype": "code",
|
|
||||||
"dataurl": "{{entire_url('../api/get_llm_vendor_options.dspy')}}",
|
|
||||||
"valueField": "value",
|
|
||||||
"textField": "text"
|
|
||||||
},
|
|
||||||
"status": {
|
"status": {
|
||||||
"uitype": "code",
|
"uitype": "code",
|
||||||
"dataurl": "{{entire_url('../api/get_llm_status_options.dspy')}}",
|
"dataurl": "{{entire_url('../api/get_llm_status_options.dspy')}}",
|
||||||
@ -34,6 +30,7 @@
|
|||||||
"editexclouded": [
|
"editexclouded": [
|
||||||
"id",
|
"id",
|
||||||
"org_id",
|
"org_id",
|
||||||
|
"vendor_id",
|
||||||
"balance",
|
"balance",
|
||||||
"total_recharge",
|
"total_recharge",
|
||||||
"created_at",
|
"created_at",
|
||||||
@ -30,6 +30,14 @@
|
|||||||
"new_data_url": "{{entire_url('../api/add_llm_vendor.dspy')}}",
|
"new_data_url": "{{entire_url('../api/add_llm_vendor.dspy')}}",
|
||||||
"update_data_url": "{{entire_url('../api/update_llm_vendor.dspy')}}",
|
"update_data_url": "{{entire_url('../api/update_llm_vendor.dspy')}}",
|
||||||
"delete_data_url": "{{entire_url('../api/delete_llm_vendor.dspy')}}"
|
"delete_data_url": "{{entire_url('../api/delete_llm_vendor.dspy')}}"
|
||||||
}
|
},
|
||||||
|
"subtables": [
|
||||||
|
{
|
||||||
|
"field": "vendor_id",
|
||||||
|
"title": "供应商账号",
|
||||||
|
"subtable": "llm_account_by_vendor",
|
||||||
|
"url": "{{entire_url('/pipeline-llm/llm_account_by_vendor')}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -222,10 +222,18 @@ async def create_llm_account(params_kw):
|
|||||||
|
|
||||||
|
|
||||||
async def update_llm_account(params_kw):
|
async def update_llm_account(params_kw):
|
||||||
"""账号更新:api_key 非空才重新加密(空=不改)。"""
|
"""账号更新:api_key 非空才重新加密(空=不改)。
|
||||||
|
|
||||||
|
vendor_id 归属不可改:账号在哪个供应商下创建就固定在哪个供应商下,
|
||||||
|
防止子表(按供应商过滤)入口下篡改表单把账号挪到别的供应商。
|
||||||
|
"""
|
||||||
result = {'success': False, 'message': ''}
|
result = {'success': False, 'message': ''}
|
||||||
try:
|
try:
|
||||||
data = _clean(params_kw)
|
data = _clean(params_kw)
|
||||||
|
aid = data.get('id', '')
|
||||||
|
if not aid:
|
||||||
|
raise ValueError('缺少 id')
|
||||||
|
data.pop('vendor_id', None)
|
||||||
if data.get('api_key') and data['api_key'] != MASK:
|
if data.get('api_key') and data['api_key'] != MASK:
|
||||||
data['api_key'] = encrypt_api_key(data['api_key'])
|
data['api_key'] = encrypt_api_key(data['api_key'])
|
||||||
else:
|
else:
|
||||||
@ -243,17 +251,28 @@ async def update_llm_account(params_kw):
|
|||||||
|
|
||||||
|
|
||||||
async def delete_llm_account(params_kw):
|
async def delete_llm_account(params_kw):
|
||||||
|
"""账号删除:被模型引用时禁止;带 vendor_id 时校验归属(子表范围隔离)。"""
|
||||||
result = {'success': False, 'message': ''}
|
result = {'success': False, 'message': ''}
|
||||||
try:
|
try:
|
||||||
aid = (params_kw or {}).get('id', '')
|
pk = params_kw or {}
|
||||||
|
aid = pk.get('id', '')
|
||||||
if not aid:
|
if not aid:
|
||||||
raise ValueError('缺少 id')
|
raise ValueError('缺少 id')
|
||||||
db, dbname = _get_sor()
|
db, dbname = _get_sor()
|
||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
recs = await sor.sqlExe(
|
recs = await sor.sqlExe(
|
||||||
|
"SELECT vendor_id FROM llm_account WHERE id=${a}$", {"a": aid})
|
||||||
|
await sor.sqlExe("COMMIT", {})
|
||||||
|
if not recs:
|
||||||
|
raise ValueError('账号不存在')
|
||||||
|
# 子表入口会带 vendor_id:校验账号确实属于该供应商,防跨供应商误删/越权删
|
||||||
|
expect_vid = pk.get('vendor_id', '')
|
||||||
|
if expect_vid and getattr(recs[0], 'vendor_id', '') != expect_vid:
|
||||||
|
raise ValueError('账号不属于该供应商,禁止删除')
|
||||||
|
cnt_recs = await sor.sqlExe(
|
||||||
"SELECT COUNT(*) AS c FROM llm_model WHERE account_id=${a}$", {"a": aid})
|
"SELECT COUNT(*) AS c FROM llm_model WHERE account_id=${a}$", {"a": aid})
|
||||||
await sor.sqlExe("COMMIT", {})
|
await sor.sqlExe("COMMIT", {})
|
||||||
cnt = int(getattr(recs[0], 'c', 0)) if recs else 0
|
cnt = int(getattr(cnt_recs[0], 'c', 0)) if cnt_recs else 0
|
||||||
if cnt > 0:
|
if cnt > 0:
|
||||||
raise ValueError('该账号被 %d 个模型设为默认账号,请先解除后再删除' % cnt)
|
raise ValueError('该账号被 %d 个模型设为默认账号,请先解除后再删除' % cnt)
|
||||||
await sor.sqlExe("DELETE FROM llm_account WHERE id=${a}$", {"a": aid})
|
await sor.sqlExe("DELETE FROM llm_account WHERE id=${a}$", {"a": aid})
|
||||||
|
|||||||
@ -82,31 +82,6 @@
|
|||||||
{"widgettype": "Text", "options": {"text": "供应商协议 + 全球端点目录(国内/国际,配置一次全账号共用)", "cfontsize": 1.2}}
|
{"widgettype": "Text", "options": {"text": "供应商协议 + 全球端点目录(国内/国际,配置一次全账号共用)", "cfontsize": 1.2}}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"widgettype": "VBox",
|
|
||||||
"options": {
|
|
||||||
"css": "card",
|
|
||||||
"cwidth": 25,
|
|
||||||
"padding": "16px",
|
|
||||||
"cursor": "pointer"
|
|
||||||
},
|
|
||||||
"binds": [
|
|
||||||
{
|
|
||||||
"wid": "self",
|
|
||||||
"event": "click",
|
|
||||||
"actiontype": "urlwidget",
|
|
||||||
"target": "app.llm_content",
|
|
||||||
"options": {
|
|
||||||
"url": "{{entire_url('/pipeline-llm/llm_account')}}"
|
|
||||||
},
|
|
||||||
"mode": "replace"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"subwidgets": [
|
|
||||||
{"widgettype": "Title4", "options": {"text": "供应商账号(钱包)", "marginBottom": "8px"}},
|
|
||||||
{"widgettype": "Text", "options": {"text": "多账号独立充值记账、选用端点;轮转突破供应商限流", "cfontsize": 1.2}}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"widgettype": "VBox",
|
"widgettype": "VBox",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user