From 144b3afbc8f1b722ca3959a1af232f73eb03878f Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Tue, 21 Jul 2026 11:19:09 +0800
Subject: [PATCH 01/15] update
---
b/cntoai/get_user_balance.dspy | 2 ++
1 file changed, 2 insertions(+)
diff --git a/b/cntoai/get_user_balance.dspy b/b/cntoai/get_user_balance.dspy
index 53c6ce8..1a646d8 100644
--- a/b/cntoai/get_user_balance.dspy
+++ b/b/cntoai/get_user_balance.dspy
@@ -35,6 +35,8 @@ async def get_user_balance(ns={}):
}
orgid = await sor.R('organization', {'id': user[0]['orgid']})
balance = await getCustomerBalance(sor, orgid[0]['id'])
+ ns['balance'] = balance
+ debug(ns)
return {
'status': 'ok',
'balance': balance
From d7e2875375bc9f273f841979e1eec7bd060111e5 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Tue, 21 Jul 2026 15:23:56 +0800
Subject: [PATCH 02/15] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BE=9B=E5=BA=94?=
=?UTF-8?q?=E5=95=86=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
b/provider/providerAdd.dspy | 41 +++++++++++++++++++++++++++++--------
1 file changed, 32 insertions(+), 9 deletions(-)
diff --git a/b/provider/providerAdd.dspy b/b/provider/providerAdd.dspy
index b522eaa..ae3d96e 100644
--- a/b/provider/providerAdd.dspy
+++ b/b/provider/providerAdd.dspy
@@ -82,14 +82,6 @@ async def providerAdd(ns={}):
'status': False,
'msg': 'provider add failed, orgcode is empty'
}
- # filter by orgcode
- res = await sor.R('organization', {})
- orgcodes = [orgc.get('orgcode') for orgc in res]
- if org_code in orgcodes:
- return {
- 'status': False,
- 'msg': 'provider add failed, orgcode has already exist'
- }
if not ns.get('orgid'):
orgid_li = await sor.R('users', {'id': await get_user()})
orgid = orgid_li[0].get('orgid') if orgid_li else ''
@@ -101,8 +93,33 @@ async def providerAdd(ns={}):
else:
orgid = ns.get('orgid')
+ # filter by orgcode; existing provider returns ids for idempotent callers
+ exist_org_li = await sor.R('organization', {'orgcode': org_code, 'del_flg': '0'})
+ if len(exist_org_li) >= 1:
+ exist_org = exist_org_li[0]
+ exist_provider_li = await sor.R('provider', {'orgid': exist_org.get('id'), 'del_flg': '0'})
+ if not exist_provider_li:
+ return {
+ 'status': False,
+ 'msg': 'provider add failed, orgcode has already exist but provider not found'
+ }
+ exist_protocol_li = await sor.R('saleprotocol', {'offer_orgid': exist_org.get('id'), 'bid_orgid': orgid, 'del_flg': '0'})
+ if not exist_protocol_li:
+ exist_protocol_li = await sor.R('saleprotocol', {'offer_orgid': exist_org.get('id'), 'del_flg': '0'})
+ return {
+ 'status': True,
+ 'msg': 'provider already exist',
+ 'data': {
+ 'orgid': exist_org.get('id'),
+ 'provider_orgid': exist_org.get('id'),
+ 'provider_id': exist_provider_li[0].get('id'),
+ 'protocolid': exist_protocol_li[0].get('id') if exist_protocol_li else None
+ }
+ }
+
# 供应商parentid 和 运营机构id相同
ns_org['parentid'] = orgid
+ ns_sale_mode['bid_orgid'] = orgid
if not ns_provider.get('start_date'):
ns_provider['start_date'] = time.strftime('%Y-%m-%d %H:%M:%S')
@@ -115,7 +132,13 @@ async def providerAdd(ns={}):
await openProviderAccounts(sor, orgid, ns_org['id'])
return {
"status": True,
- "msg": "provider add success"
+ "msg": "provider add success",
+ "data": {
+ "orgid": ns_org['id'],
+ "provider_orgid": ns_org['id'],
+ "provider_id": ns_provider['id'],
+ "protocolid": ns_sale_mode['id']
+ }
}
except Exception as e:
raise e
From 35d7fa08eadcb77ef8bc98e0e431d81a0ad6a9f4 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Tue, 21 Jul 2026 16:06:04 +0800
Subject: [PATCH 03/15] =?UTF-8?q?llm=E8=A1=A8=E6=A0=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
b/llm.sql | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 145 insertions(+)
create mode 100644 b/llm.sql
diff --git a/b/llm.sql b/b/llm.sql
new file mode 100644
index 0000000..7bd517c
--- /dev/null
+++ b/b/llm.sql
@@ -0,0 +1,145 @@
+# 创建表格命令
+CREATE TABLE llm (
+ id VARCHAR(64) NOT NULL COMMENT '唯一标识',
+ name VARCHAR(50) NOT NULL COMMENT '模型名称',
+ model VARCHAR(50) NOT NULL COMMENT '模型代号',
+ description TEXT COMMENT '模型描述',
+ iconid VARCHAR(50) COMMENT '图标标识',
+ upappid VARCHAR(50) COMMENT '上层应用ID',
+ providerid VARCHAR(64) COMMENT '供应商ID',
+ ownerid VARCHAR(20) DEFAULT '0' COMMENT '所有者ID',
+ enabled_date DATE COMMENT '启用日期',
+ expired_date DATE COMMENT '过期日期',
+ min_balance INT DEFAULT 0 COMMENT '最低余额要求(元)',
+ status VARCHAR(20) COMMENT '状态(如published)',
+ catelogname VARCHAR(50) COMMENT '目录名称(如文生视频)',
+ catelog_id VARCHAR(20) COMMENT '目录ID',
+ apiname VARCHAR(50) COMMENT 'API名称',
+ query_apiname VARCHAR(50) COMMENT '查询API名称',
+ query_period INT COMMENT '查询周期(秒)',
+ ppid VARCHAR(64) COMMENT '定价计划ID',
+ provider_name VARCHAR(100) COMMENT '供应商名称',
+ pricing_display TEXT COMMENT '定价显示文本',
+ provider_iconurl VARCHAR(255) COMMENT '供应商图标URL',
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='LLM模型信息表';
+
+
+# 厂商返回的部分数据示例
+[
+ {
+ "catelogid": "2i2v",
+ "catelogname": "首尾帧生视频",
+ "llms": [
+ {
+ "id": "r0ill7L0s6PtBS8mkmg-7",
+ "name": "viduq3-pro",
+ "model": "viduq3-pro",
+ "description": "vidu文生视频,viduq3-pro模型",
+ "iconid": "vidu",
+ "upappid": "vidu",
+ "providerid": "Bu3Z6OXANehbkD6OK5NJb",
+ "ownerid": "0",
+ "enabled_date": "2026-01-01",
+ "expired_date": "9999-12-31",
+ "min_balance": 10,
+ "status": "published",
+ "catelogname": "首尾帧生视频",
+ "catelog_id": "2i2v",
+ "apiname": "2i2v",
+ "query_apiname": "taskStatus",
+ "query_period": 30,
+ "ppid": "sF2gcl7UeANKtnZv8hfvL",
+ "provider_name": "北京生数科技有限公司",
+ "pricing_display": " - duration: 0.56 元/秒 ",
+ "provider_iconurl": "https://token.opencomputing.cn/appbase/show_icon.dspy?id=vidu"
+ }
+ ]
+ },
+ {
+ "catelogid": "asr",
+ "catelogname": "语音识别",
+ "llms": [
+ {
+ "id": "9glv3S-JSzxholxAblU14",
+ "name": "qwen3-asr-flash",
+ "model": "qwen3-asr-flash",
+ "description": "通义千问系列的录音文件识",
+ "iconid": "qwen",
+ "upappid": "tongyi-wan",
+ "providerid": "6fadgewjraOyvxC_EkHou",
+ "ownerid": "0",
+ "enabled_date": "2025-12-01",
+ "expired_date": "9999-12-31",
+ "min_balance": 10,
+ "status": "published",
+ "catelogname": "语音识别",
+ "catelog_id": "asr",
+ "apiname": "short_asr",
+ "query_apiname": null,
+ "query_period": null,
+ "ppid": "16pwEsQeHiOrxC0O6A2j6",
+ "provider_name": "阿里云",
+ "provider_iconurl": "https://token.opencomputing.cn/appbase/show_icon.dspy?id=qwen"
+ }
+ ]
+ },
+ {
+ "catelogid": "digital_human",
+ "catelogname": "数字人",
+ "llms": [
+ {
+ "id": "KOsAo6YjTdU0KG7_sKUJF",
+ "name": "wan2.2-s2v",
+ "model": "wan2.2-s2v",
+ "description": "模型:wan2.2-s2v,输入音频和照片,用照片中的人生成数字人",
+ "iconid": "qwen",
+ "upappid": "tongyi-wan",
+ "providerid": "6fadgewjraOyvxC_EkHou",
+ "ownerid": "0",
+ "enabled_date": "2025-12-19",
+ "expired_date": "9999-12-31",
+ "min_balance": 10,
+ "status": "published",
+ "catelogname": "数字人",
+ "catelog_id": "digital_human",
+ "apiname": "avator",
+ "query_apiname": "avatorstatus",
+ "query_period": 10,
+ "ppid": "0B6aldoAej1PpZ4ydtrEZ",
+ "provider_name": "阿里云",
+ "pricing_display": " - duration: 0.5 元/秒 [分辨率=480P]\n - duration: 0.9 元/秒 [分辨率=720P]",
+ "provider_iconurl": "https://token.opencomputing.cn/appbase/show_icon.dspy?id=qwen"
+ }
+ ]
+ },
+ {
+ "catelogid": "i2v",
+ "catelogname": "图生视频",
+ "llms": [
+ {
+ "id": "7y7kfYM0Xasizyi37WvAZ",
+ "name": "happyhorse-1.0-i2v",
+ "model": "happyhorse-1.0-i2v",
+ "description": "HappyHorse图生视频模型,以首帧图片为基础,支持通过文本描述进行引导,生成物理真实、运动流畅的视频。",
+ "iconid": "qwen",
+ "upappid": "tongyi-wan",
+ "providerid": "6fadgewjraOyvxC_EkHou",
+ "ownerid": "0",
+ "enabled_date": "2026-04-28",
+ "expired_date": "9999-12-31",
+ "min_balance": 10,
+ "status": "published",
+ "catelogname": "图生视频",
+ "catelog_id": "i2v",
+ "apiname": "i2v-happyhorse",
+ "query_apiname": "t2vstatus",
+ "query_period": 10,
+ "ppid": "knEwDP35DEz5oMAXyvwN3",
+ "provider_name": "阿里云",
+ "pricing_display": " - duration: 0.9 元/秒 [SR=720]\n - duration: 1.6 元/秒 [SR=1080]",
+ "provider_iconurl": "https://token.opencomputing.cn/appbase/show_icon.dspy?id=qwen"
+ },
+ ]
+ }
+]
\ No newline at end of file
From 4af3ff5000e0dd37f2ee5cad49c8581eac2f6447 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Tue, 21 Jul 2026 16:13:44 +0800
Subject: [PATCH 04/15] update
---
b/cntoai/sync_model_to_llm.dspy | 79 +++++++++++++++++++++++++++++++--
1 file changed, 76 insertions(+), 3 deletions(-)
diff --git a/b/cntoai/sync_model_to_llm.dspy b/b/cntoai/sync_model_to_llm.dspy
index ac187af..278d721 100644
--- a/b/cntoai/sync_model_to_llm.dspy
+++ b/b/cntoai/sync_model_to_llm.dspy
@@ -1,5 +1,11 @@
async def sync_model_to_llm(ns={}):
import aiohttp
+
+ async def get_sync_orgid(sor):
+ if ns.get('orgid'):
+ return ns.get('orgid')
+ user_li = await sor.R('users', {'id': await get_user()})
+ return user_li[0].get('orgid') if user_li else ''
# 从数据库读取domain和Bearer token
db = DBPools()
@@ -13,6 +19,12 @@ async def sync_model_to_llm(ns={}):
}
domain = domain_li[0]['pvalue']
bearer_token = user_key[0]['pvalue']
+ sync_orgid = await get_sync_orgid(sor)
+ if not sync_orgid:
+ return {
+ 'status': False,
+ 'msg': '未找到当前用户所属机构'
+ }
url = f"{domain}/llmage/list_llms"
header = {
@@ -33,9 +45,70 @@ async def sync_model_to_llm(ns={}):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
new_llms_count = 0
+ sync_products_count = 0
new_llms_list = []
for category_list in result:
for item in category_list.get('llms', []):
+ provider_orgcode = item.get('providerid')
+ provider_name = item.get('provider_name') or item.get('providerid') or '模型供应商'
+ if not provider_orgcode:
+ continue
+
+ provider_ns = {
+ 'orgid': sync_orgid,
+ 'orgcode': provider_orgcode,
+ 'orgname': provider_name,
+ 'name': provider_name,
+ 'description': provider_name,
+ 'appid': item.get('upappid') or '',
+ 'providerkey': provider_orgcode,
+ 'state': '1',
+ 'start_date': item.get('enabled_date'),
+ 'end_date': item.get('expired_date') or '9999-12-31',
+ 'salemode': ns.get('salemode') or '0',
+ 'settle_mode': ns.get('settle_mode') or '0',
+ 'settle_datep': ns.get('settle_datep')
+ }
+ provider_res = await path_call('../provider/providerAdd.dspy', provider_ns)
+ if not provider_res.get('status'):
+ return {
+ 'status': False,
+ 'msg': '供应商同步失败, %s, %s' % (provider_name, provider_res.get('msg'))
+ }
+ provider_data = provider_res.get('data') or {}
+ provider_orgid = provider_data.get('provider_orgid') or provider_data.get('orgid')
+ protocolid = provider_data.get('protocolid')
+ if not provider_orgid or not protocolid:
+ return {
+ 'status': False,
+ 'msg': '供应商同步失败, 未返回provider_orgid或protocolid, %s' % provider_name
+ }
+
+ product_ns = {
+ 'providerid': provider_orgid,
+ 'providerpid': item.get('id'),
+ 'protocolid': protocolid,
+ 'name': item.get('name'),
+ 'description': item.get('description'),
+ 'ptype': item.get('catelogname') or category_list.get('catelogname') or '',
+ 'state': '1',
+ 'product_code': item.get('model'),
+ 'reseller_orgid': sync_orgid,
+ 'salemode': ns.get('salemode') or '0',
+ 'productgroup': item.get('catelog_id') or item.get('catelogid') or category_list.get('catelogid'),
+ 'label': item.get('catelogname') or category_list.get('catelogname'),
+ 'unit': ns.get('unit'),
+ 'discount': ns.get('discount'),
+ 'floorprice': ns.get('floorprice')
+ }
+ product_res = await path_call('../product/productAdd.dspy', product_ns)
+ if not product_res.get('status'):
+ return {
+ 'status': False,
+ 'msg': '产品同步失败, %s, %s' % (item.get('name'), product_res.get('msg'))
+ }
+ sync_products_count += 1
+
# 查找数据库中是否已经存在,不存在就插入
exist_llm = await sor.R('llm', {'model': item.get('model')})
if exist_llm:
@@ -49,13 +122,13 @@ async def sync_model_to_llm(ns={}):
'iconid': item.get('iconid'),
'upappid': item.get('upappid'),
'apiname': item.get('apiname'),
- 'providerid': item.get('providerid'),
+ 'providerid': provider_orgid,
'ownerid': item.get('ownerid') or '0',
'enabled_date': item.get('enabled_date'),
'expired_date': item.get('expired_date'),
'query_apiname': item.get('query_apiname') or '',
'query_period': item.get('query_period'),
- 'ppid': item.get('ppid'),
+ 'ppid': protocolid,
}
new_llms_count += 1
new_llms_list.append(new_llms.get('model'))
@@ -63,7 +136,7 @@ async def sync_model_to_llm(ns={}):
return {
'status': True,
- 'msg': f"sync_llm_list同步模型成功,共插入{new_llms_count}个模型,模型列表: {new_llms_list}"
+ 'msg': f"sync_llm_list同步模型成功,共同步{sync_products_count}个产品,共插入{new_llms_count}个模型,模型列表: {new_llms_list}"
}
except Exception as e:
From f2b432df51adf44a931408c41da8a55ff34e04c5 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Tue, 21 Jul 2026 17:15:53 +0800
Subject: [PATCH 05/15] updte
---
b/cntoai/sync_model_to_llm.dspy | 54 +++++++++++++++++++++------------
1 file changed, 34 insertions(+), 20 deletions(-)
diff --git a/b/cntoai/sync_model_to_llm.dspy b/b/cntoai/sync_model_to_llm.dspy
index 278d721..c119057 100644
--- a/b/cntoai/sync_model_to_llm.dspy
+++ b/b/cntoai/sync_model_to_llm.dspy
@@ -19,7 +19,8 @@ async def sync_model_to_llm(ns={}):
}
domain = domain_li[0]['pvalue']
bearer_token = user_key[0]['pvalue']
- sync_orgid = await get_sync_orgid(sor)
+ # sync_orgid = await get_sync_orgid(sor)
+ sync_orgid = "mIWUHBeeDM8mwAFPIQ8pS" # 固定写
if not sync_orgid:
return {
'status': False,
@@ -63,11 +64,17 @@ async def sync_model_to_llm(ns={}):
'appid': item.get('upappid') or '',
'providerkey': provider_orgcode,
'state': '1',
- 'start_date': item.get('enabled_date'),
- 'end_date': item.get('expired_date') or '9999-12-31',
- 'salemode': ns.get('salemode') or '0',
- 'settle_mode': ns.get('settle_mode') or '0',
- 'settle_datep': ns.get('settle_datep')
+ 'start_date': '2026-07-01',
+ 'end_date': '9999-12-31',
+ 'settle_flowid': '0',
+ 'salemode': '0',
+ 'settle_mode': '3',
+ 'settle_datep': 'M00',
+ 'contactor': '连战',
+ 'contactor_phone': '13800138000',
+ 'emailaddress': 'lianzhan@cntoai.com',
+ 'address': '北京市海淀区',
+ 'org_type': '4'
}
provider_res = await path_call('../provider/providerAdd.dspy', provider_ns)
if not provider_res.get('status'):
@@ -89,17 +96,14 @@ async def sync_model_to_llm(ns={}):
'providerpid': item.get('id'),
'protocolid': protocolid,
'name': item.get('name'),
- 'description': item.get('description'),
- 'ptype': item.get('catelogname') or category_list.get('catelogname') or '',
- 'state': '1',
'product_code': item.get('model'),
'reseller_orgid': sync_orgid,
- 'salemode': ns.get('salemode') or '0',
- 'productgroup': item.get('catelog_id') or item.get('catelogid') or category_list.get('catelogid'),
'label': item.get('catelogname') or category_list.get('catelogname'),
- 'unit': ns.get('unit'),
- 'discount': ns.get('discount'),
- 'floorprice': ns.get('floorprice')
+ 'classify': 'A',
+ 'ptype': '21',
+ 'unit': 'hour',
+ 'discount': '1',
+ 'product_area': '北京'
}
product_res = await path_call('../product/productAdd.dspy', product_ns)
if not product_res.get('status'):
@@ -112,23 +116,33 @@ async def sync_model_to_llm(ns={}):
# 查找数据库中是否已经存在,不存在就插入
exist_llm = await sor.R('llm', {'model': item.get('model')})
if exist_llm:
+ # 更新
+ exits_id = exist_llm[0].get('id')
+ await sor.U('llm', {'id': exits_id}, item)
continue
+
new_llms = {
'id': item.get('id'),
'name': item.get('name'),
'model': item.get('model'),
'description': item.get('description'),
- 'llmcatelogid': item.get('catelog_id') or item.get('catelogid'),
'iconid': item.get('iconid'),
'upappid': item.get('upappid'),
- 'apiname': item.get('apiname'),
- 'providerid': provider_orgid,
- 'ownerid': item.get('ownerid') or '0',
+ 'providerid': item.get('providerid'),
+ 'ownerid': item.get('ownerid'),
'enabled_date': item.get('enabled_date'),
'expired_date': item.get('expired_date'),
- 'query_apiname': item.get('query_apiname') or '',
+ 'min_balance': item.get('min_balance'),
+ 'status': item.get('status'),
+ 'catelogname': item.get('catelogname'),
+ 'catelog_id': item.get('catelog_id'),
+ 'apiname': item.get('apiname'),
+ 'query_apiname': item.get('query_apiname'),
'query_period': item.get('query_period'),
- 'ppid': protocolid,
+ 'ppid': item.get('ppid'),
+ 'provider_name': item.get('provider_name'),
+ 'pricing_display': item.get('pricing_display'),
+ 'provider_iconurl': item.get('provider_iconurl')
}
new_llms_count += 1
new_llms_list.append(new_llms.get('model'))
From 52568b33520c4b479a499883117d276d955959fe Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Wed, 22 Jul 2026 10:11:18 +0800
Subject: [PATCH 06/15] update
---
b/user/get_user_and_role.dspy | 83 +++++++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 b/user/get_user_and_role.dspy
diff --git a/b/user/get_user_and_role.dspy b/b/user/get_user_and_role.dspy
new file mode 100644
index 0000000..6c555a2
--- /dev/null
+++ b/b/user/get_user_and_role.dspy
@@ -0,0 +1,83 @@
+def _parse_page(ns, default_page=1, default_size=20, max_size=200):
+ try:
+ current_page = int(ns.get('current_page') or ns.get('page') or default_page)
+ except (TypeError, ValueError):
+ current_page = default_page
+ try:
+ page_size = int(ns.get('page_size') or default_size)
+ except (TypeError, ValueError):
+ page_size = default_size
+ current_page = max(1, current_page)
+ page_size = max(1, min(page_size, max_size))
+ return current_page, page_size, (current_page - 1) * page_size
+
+
+async def get_user_and_role(ns={}):
+ """获取机构下所有用户及用户对应的所有角色"""
+ orgid = ns.get('orgid')
+ if not orgid:
+ return {'status': False, 'msg': '缺少 orgid'}
+
+ current_page, page_size, offset = _parse_page(ns)
+ db = DBPools()
+ async with db.sqlorContext('kboss') as sor:
+ try:
+ where_sql = """
+ u.del_flg='0'
+ AND (
+ u.orgid=${orgid}$
+ OR o.parentid=${orgid}$
+ )
+ """
+ if ns.get('username'):
+ where_sql += " AND u.username LIKE '%%%%%s%%%%'" % ns.get('username')
+ if ns.get('mobile'):
+ where_sql += " AND u.mobile LIKE '%%%%%s%%%%'" % ns.get('mobile')
+ count_rows = await sor.sqlExe(
+ """
+ SELECT COUNT(*) AS total_count
+ FROM users u
+ LEFT JOIN organization o ON u.orgid = o.id AND o.del_flg='0'
+ WHERE %s
+ """ % where_sql,
+ {'orgid': orgid},
+ )
+ total_count = count_rows[0]['total_count'] if count_rows else 0
+ sql = """
+ SELECT u.*
+ FROM users u
+ LEFT JOIN organization o ON u.orgid = o.id AND o.del_flg='0'
+ WHERE %s
+ ORDER BY u.create_at DESC
+ LIMIT %d OFFSET %d
+ """ % (where_sql, page_size, offset)
+ users = await sor.sqlExe(sql, {'orgid': orgid})
+ for user in users:
+ user['roles'] = []
+ userroles = await sor.R('userrole', {'userid': user['id']})
+ for userrole in userroles:
+ if userrole.get('del_flg') == '1' or not userrole.get('roleid'):
+ continue
+ roles = await sor.R('role', {'id': userrole['roleid']})
+ if len(roles) >= 1 and roles[0].get('del_flg') != '1':
+ user['roles'].append({
+ 'id': roles[0].get('id'),
+ 'name': roles[0].get('role'),
+ })
+
+ return {
+ 'status': True,
+ 'msg': 'ok',
+ 'data': {
+ 'total_count': total_count,
+ 'current_page': current_page,
+ 'page_size': page_size,
+ 'rows': users,
+ },
+ }
+ except Exception as e:
+ return {'status': False, 'msg': '获取用户角色失败, %s' % str(e)}
+
+
+ret = await get_user_and_role(params_kw)
+return ret
From faedbb8edfb1a22bd3beb4b566ce69bb5fa26842 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Wed, 22 Jul 2026 10:18:48 +0800
Subject: [PATCH 07/15] update
---
b/user/get_user_and_role.dspy | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/b/user/get_user_and_role.dspy b/b/user/get_user_and_role.dspy
index 6c555a2..f209a11 100644
--- a/b/user/get_user_and_role.dspy
+++ b/b/user/get_user_and_role.dspy
@@ -47,14 +47,21 @@ async def get_user_and_role(ns={}):
SELECT u.*
FROM users u
LEFT JOIN organization o ON u.orgid = o.id AND o.del_flg='0'
+ LEFT JOIN (
+ SELECT userid, MAX(create_at) AS userrole_create_at
+ FROM userrole
+ WHERE del_flg='0'
+ GROUP BY userid
+ ) ur_sort ON u.id = ur_sort.userid
WHERE %s
- ORDER BY u.create_at DESC
+ ORDER BY ur_sort.userrole_create_at DESC, u.create_at DESC
LIMIT %d OFFSET %d
""" % (where_sql, page_size, offset)
users = await sor.sqlExe(sql, {'orgid': orgid})
for user in users:
user['roles'] = []
userroles = await sor.R('userrole', {'userid': user['id']})
+ userroles = sorted(userroles, key=lambda item: item.get('create_at') or '', reverse=True)
for userrole in userroles:
if userrole.get('del_flg') == '1' or not userrole.get('roleid'):
continue
From cbf68a3d5e9b089e1e54c1759a8e6662cfd2ff25 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Wed, 22 Jul 2026 11:30:05 +0800
Subject: [PATCH 08/15] update
---
b/user/add_user_role.dspy | 55 +++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 b/user/add_user_role.dspy
diff --git a/b/user/add_user_role.dspy b/b/user/add_user_role.dspy
new file mode 100644
index 0000000..06988e3
--- /dev/null
+++ b/b/user/add_user_role.dspy
@@ -0,0 +1,55 @@
+import json
+
+
+def _parse_roleids(roleid):
+ if roleid is None:
+ return []
+ if isinstance(roleid, list):
+ return [i for i in roleid if i]
+ if isinstance(roleid, str):
+ roleid = roleid.strip()
+ if not roleid:
+ return []
+ try:
+ roleids = json.loads(roleid)
+ except Exception:
+ roleids = [roleid]
+ if isinstance(roleids, list):
+ return [i for i in roleids if i]
+ return [roleids] if roleids else []
+ return [roleid]
+
+
+async def add_user_role(ns):
+ """
+ 用户分配角色
+ :param userid: 用户id
+ :param roleid: 角色id列表
+ :return:
+ """
+ if not ns or not ns.get('userid') or 'roleid' not in ns:
+ return {'status': False, 'msg': '参数不正确'}
+
+ userid = ns.get('userid')
+ roleids = _parse_roleids(ns.get('roleid'))
+ db = DBPools()
+ async with db.sqlorContext('kboss') as sor:
+ userroles = await sor.R('userrole', {'userid': userid})
+ for userrole in userroles:
+ if userrole.get('roleid') not in roleids:
+ await sor.U('userrole', {'del_flg': '1', 'id': userrole['id']})
+
+ for roleid in roleids:
+ roles = {'userid': userid, 'roleid': roleid}
+ reacs = await sor.R('userrole', roles)
+ if len(reacs) >= 1:
+ await sor.U('userrole', {'id': reacs[0]['id'], 'del_flg': '0'})
+ continue
+ await sor.C('userrole', {'id': uuid(), 'userid': userid, 'roleid': roleid})
+ return {'status': True, 'msg': '添加成功'}
+
+ return {'status': False, 'msg': '添加失败'}
+
+
+ret = await add_user_role(params_kw)
+return ret
From 2faa82f4bef54c6693f946754f0ac81cd6edef1f Mon Sep 17 00:00:00 2001
From: hrx <18603305412@163.com>
Date: Wed, 22 Jul 2026 11:34:07 +0800
Subject: [PATCH 09/15] =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
f/web-kboss/src/api/roleManagement.js | 33 +
f/web-kboss/src/router/index.js | 7 +
f/web-kboss/src/store/modules/permission.js | 35 +
.../src/views/LoginDialog/LoginDialog.vue | 5 +
.../homePage/components/topBox/index.vue | 1 +
.../homePage/news/ArticleEditorDialog.vue | 2 +-
.../src/views/modelManagement/ApiDocument.vue | 338 ++++++----
.../roleManagement/index.vue | 619 ++++++++++++++++++
8 files changed, 910 insertions(+), 130 deletions(-)
create mode 100644 f/web-kboss/src/api/roleManagement.js
create mode 100644 f/web-kboss/src/views/superAdministrator/roleManagement/index.vue
diff --git a/f/web-kboss/src/api/roleManagement.js b/f/web-kboss/src/api/roleManagement.js
new file mode 100644
index 0000000..cca6343
--- /dev/null
+++ b/f/web-kboss/src/api/roleManagement.js
@@ -0,0 +1,33 @@
+import request from '@/utils/request'
+// 获取角色
+export function getRoleAPI(data) {
+ return request({
+ url: `/role/getRole.dspy`,
+ method: 'get',
+ params: data
+ })
+}
+// 添加角色
+export function addRoleAPI(data) {
+ return request({
+ url: `/reseller/reseller_add_user.dspy`,
+ method: 'post',
+ data: data
+ })
+}
+// 列表
+export function getListAPI(data) {
+ return request({
+ url: `/user/get_user_and_role.dspy`,
+ method: 'get',
+ params: data
+ })
+}
+// 分配角色 参数为userid roleid
+export function assignRoleAPI(data) {
+ return request({
+ url: `/user/add_user_role.dspy`,
+ method: 'post',
+ data: data
+ })
+}
\ No newline at end of file
diff --git a/f/web-kboss/src/router/index.js b/f/web-kboss/src/router/index.js
index 81298f0..1dca6fa 100644
--- a/f/web-kboss/src/router/index.js
+++ b/f/web-kboss/src/router/index.js
@@ -2305,6 +2305,13 @@ export const asyncRoutes = [
path: "/superAdministrator", component: Layout, redirect: "/superAdministrator/index", meta: {
title: "超级管理员", icon: "el-icon-user-solid", noCache: true, fullPath: "/superAdministrator",
}, children: [{
+ path: "roleManagement",
+ component: () => import("@/views/superAdministrator/roleManagement"),
+ name: "RoleManagement",
+ meta: {
+ title: "角色管理", fullPath: "/superAdministrator/roleManagement",
+ },
+ }, {
path: "addAdmin", component: () => import("@/views/superAdministrator/addAdmin"), name: "addAdmin", meta: {
title: "添加业主机构管理员", fullPath: "/superAdministrator/addAdmin",
},
diff --git a/f/web-kboss/src/store/modules/permission.js b/f/web-kboss/src/store/modules/permission.js
index d773d45..08d5880 100644
--- a/f/web-kboss/src/store/modules/permission.js
+++ b/f/web-kboss/src/store/modules/permission.js
@@ -7,6 +7,7 @@ const MOBILE_UA_REGEXP = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Ope
const CUSTOMER_ROLE = '客户';
const OPERATION_ROLE = '运营';
const FINANCE_ROLE = '财务';
+const ADMINISTRATOR_ROLE = '管理员';
// 这个用户能看到订单管理里的特殊子菜单,比如历史订单和订单详情。
const SPECIAL_ORDER_USER = 'ZhipuHZ';
@@ -23,6 +24,8 @@ const OPERATION_EXTRA_ROUTE_PATHS = ['/modelManagement', '/modelInfoConfig', '/o
// 财务角色需要额外补出来的菜单。
const FINANCE_EXTRA_ROUTE_PATHS = ['/financialOverview'];
+const ADMINISTRATOR_ROUTE_FULL_PATH = '/superAdministrator/roleManagement';
+
// 普通客户账号默认要补出来的基础菜单。
const BASE_USER_ROUTE_PATHS = ['/orderManagement', '/resourceManagement'];
@@ -350,6 +353,30 @@ function addFinanceRoutes(accessedRoutes, routes, userRoles = [], deviceType = '
return appendMissingRoutes(accessedRoutes, getRoutesByPath(routes, FINANCE_EXTRA_ROUTE_PATHS));
}
+function getAdministratorRoutes(routes, deviceType = 'pc') {
+ if (deviceType !== 'pc') {
+ return [];
+ }
+
+ const superAdminRoute = findRouteByPath(routes, SUPER_ADMIN_ROUTE_PATH);
+ if (!superAdminRoute) {
+ return [];
+ }
+
+ const clonedRoute = cloneRoute(superAdminRoute);
+ clonedRoute.redirect = ADMINISTRATOR_ROUTE_FULL_PATH;
+ clonedRoute.children = (clonedRoute.children || []).filter(child => child.meta?.fullPath === ADMINISTRATOR_ROUTE_FULL_PATH);
+ return clonedRoute.children.length ? [clonedRoute] : [];
+}
+
+function addAdministratorRoutes(accessedRoutes, routes, userRoles = [], deviceType = 'pc') {
+ if (!userRoles.includes(ADMINISTRATOR_ROLE)) {
+ return accessedRoutes;
+ }
+
+ return appendMissingRoutes(accessedRoutes, getAdministratorRoutes(routes, deviceType));
+}
+
// token市集是公共菜单,所有登录用户都要能看到。
function addCommonRoutes(accessedRoutes, routes, deviceType = 'pc') {
const commonRoutes = getRoutesByPath(routes, COMMON_ROUTE_PATHS)
@@ -495,6 +522,13 @@ const actions = {
console.log("用户类型:", userType, "orgType:", orgType, "设备类型:", deviceType);
console.log("ACTION generateRoutes - auths:", auths);
+ if (!isSuperAdmin && userRoles.includes(ADMINISTRATOR_ROLE)) {
+ const administratorRoutes = getAdministratorRoutes(asyncRoutes, deviceType);
+ commit("SET_ROUTES", administratorRoutes);
+ resolve(administratorRoutes);
+ return;
+ }
+
// 3. 先生成第一版菜单:超级管理员只拿超管菜单,普通用户按后端 auths 过滤。
let accessedRoutes = isSuperAdmin
? getSuperAdminRoutes(deviceType)
@@ -502,6 +536,7 @@ const actions = {
// 4. token市集是公共入口,所有登录用户都补上。
accessedRoutes = addCommonRoutes(accessedRoutes, asyncRoutes, deviceType);
+ accessedRoutes = addAdministratorRoutes(accessedRoutes, asyncRoutes, userRoles, deviceType);
if (!isSuperAdmin) {
// 5. 普通用户再补一些固定入口,比如订单、资源、客户专属菜单。
diff --git a/f/web-kboss/src/views/LoginDialog/LoginDialog.vue b/f/web-kboss/src/views/LoginDialog/LoginDialog.vue
index 263a61f..298bfc8 100644
--- a/f/web-kboss/src/views/LoginDialog/LoginDialog.vue
+++ b/f/web-kboss/src/views/LoginDialog/LoginDialog.vue
@@ -382,6 +382,11 @@ export default {
})
},
redirectAfterLogin(res) {
+ if (res.roles && res.roles.includes('管理员')) {
+ sessionStorage.removeItem('loginRedirectPath')
+ this.$router.push('/superAdministrator/roleManagement').catch(() => {})
+ return
+ }
const redirectPath = sessionStorage.getItem('loginRedirectPath')
if (redirectPath && redirectPath.startsWith('/') && !redirectPath.includes('/login')) {
sessionStorage.removeItem('loginRedirectPath')
diff --git a/f/web-kboss/src/views/homePage/components/topBox/index.vue b/f/web-kboss/src/views/homePage/components/topBox/index.vue
index 91f25ee..24c8b71 100644
--- a/f/web-kboss/src/views/homePage/components/topBox/index.vue
+++ b/f/web-kboss/src/views/homePage/components/topBox/index.vue
@@ -544,6 +544,7 @@ export default Vue.extend({
else if (role.includes('运维')) this.$router.push('/operationAndMaintenance/workOrderProcessing')
else if (role.includes('销售')) this.$router.push('/sales/distributorManagement')
else if (role.includes('财务')) this.$router.push('/finance/supplierSettlementStatistics')
+ else if (role.includes('管理员')) this.$router.push('/superAdministrator/roleManagement')
else if (role.includes('admin')) this.$router.push('/superAdministrator/addAdmin')
},
async logout() {
diff --git a/f/web-kboss/src/views/homePage/news/ArticleEditorDialog.vue b/f/web-kboss/src/views/homePage/news/ArticleEditorDialog.vue
index 4f04029..792d8a1 100644
--- a/f/web-kboss/src/views/homePage/news/ArticleEditorDialog.vue
+++ b/f/web-kboss/src/views/homePage/news/ArticleEditorDialog.vue
@@ -243,7 +243,7 @@ export default {
this.stopObserveEditorFullscreen()
this.editorFullscreen = false
},
- methods: {
+ methods: {
createEmptyForm() {
// 创建新增文章时使用的默认表单结构。
return {
diff --git a/f/web-kboss/src/views/modelManagement/ApiDocument.vue b/f/web-kboss/src/views/modelManagement/ApiDocument.vue
index 61c629c..8ed6cc9 100644
--- a/f/web-kboss/src/views/modelManagement/ApiDocument.vue
+++ b/f/web-kboss/src/views/modelManagement/ApiDocument.vue
@@ -5,7 +5,7 @@
返回
- API文档
+ 元境 API 文档
@@ -19,40 +19,12 @@
>
- {{ apiDoc.model_name }} API 文档
- {{ heroDescription }}
-
+ 元境 API 文档
+ 统一展示大模型、视频、图像、音频、任务查询等接口说明。
-
- 1. 接口地址
- 统一使用 HTTPS 请求,所有接口都需要携带平台签发的 API Key。
- {{ apiUrlText }}
-
-
-
-
-
-
-
- 2. 请求示例
- {{ apiDoc.curl_code || requestExample }}
-
-
-
- 3. Python 示例
- {{ apiDoc.python_code || pythonExample }}
-
-
-
@@ -61,124 +33,137 @@
+
+
From 0baf4fb9ad1ed3def78a225f1455941b15dd70c0 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Wed, 22 Jul 2026 15:12:11 +0800
Subject: [PATCH 10/15] update
---
b/reseller/reseller_add_user.dspy | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/b/reseller/reseller_add_user.dspy b/b/reseller/reseller_add_user.dspy
index 53736dc..2d77f31 100644
--- a/b/reseller/reseller_add_user.dspy
+++ b/b/reseller/reseller_add_user.dspy
@@ -27,15 +27,16 @@ async def reseller_add_user(ns):
try:
ns['id'] = uuid()
ns['password'] = password_encode(ns['password'])
- tenant_orgid = await get_reseller_user_tenant_orgid(sor, ns.get('orgid'))
+ # tenant_orgid = await get_reseller_user_tenant_orgid(sor, ns.get('orgid'))
+ tenant_orgid = ns.get('orgid')
if not tenant_orgid:
return {'status': False, 'msg': '未找到用户所属租户'}
user_sql = """select id from users where username = '%s' and tenant_orgid = '%s' and del_flg = '0' limit 1;""" % (ns['username'], tenant_orgid)
user = await sor.sqlExe(user_sql, {})
if len(user) >= 1:
return {'status': False, 'msg': '用户名重复'}
- rolereacs = await sor.R('role', {'role': '管理员', 'del_flg': '0', 'org_type': '1'})
- ns['user_reseller'] = '1'
+ rolereacs = await sor.R('role', {'role': '管理员', 'del_flg': '0', 'org_type': ns.get('org_type')})
+ ns['user_reseller'] = ns.get('org_type')
ns['tenant_orgid'] = tenant_orgid
await sor.C('users', ns)
await sor.C('userrole', {'userid': ns['id'], 'roleid': rolereacs[0]['id'], 'id': uuid()})
From cbbdac9adc91b400fc7fa3932eb6a1be167835c1 Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Wed, 22 Jul 2026 15:37:50 +0800
Subject: [PATCH 11/15] update
---
b/cntoai/sync_model_to_llm.dspy | 344 +++++++++++++++++++++++---------
1 file changed, 248 insertions(+), 96 deletions(-)
diff --git a/b/cntoai/sync_model_to_llm.dspy b/b/cntoai/sync_model_to_llm.dspy
index c119057..ee87c1d 100644
--- a/b/cntoai/sync_model_to_llm.dspy
+++ b/b/cntoai/sync_model_to_llm.dspy
@@ -6,6 +6,177 @@ async def sync_model_to_llm(ns={}):
return ns.get('orgid')
user_li = await sor.R('users', {'id': await get_user()})
return user_li[0].get('orgid') if user_li else ''
+
+ def build_llm_data(item):
+ return {
+ 'id': item.get('id') or uuid(),
+ 'name': item.get('name'),
+ 'model': item.get('model'),
+ 'description': item.get('description'),
+ 'iconid': item.get('iconid'),
+ 'upappid': item.get('upappid'),
+ 'providerid': item.get('providerid'),
+ 'ownerid': item.get('ownerid') or '0',
+ 'enabled_date': item.get('enabled_date'),
+ 'expired_date': item.get('expired_date'),
+ 'min_balance': item.get('min_balance'),
+ 'status': item.get('status') or 'published',
+ 'catelogname': item.get('catelogname'),
+ 'catelog_id': item.get('catelog_id') or item.get('catelogid'),
+ 'llmcatelogid': item.get('catelog_id') or item.get('catelogid'),
+ 'apiname': item.get('apiname'),
+ 'query_apiname': item.get('query_apiname'),
+ 'query_period': item.get('query_period'),
+ 'ppid': item.get('ppid'),
+ 'provider_name': item.get('provider_name'),
+ 'pricing_display': item.get('pricing_display'),
+ 'provider_iconurl': item.get('provider_iconurl')
+ }
+
+ async def ensure_provider(sor, provider_orgcode, provider_name, sync_orgid, item):
+ exist_org_li = await sor.R('organization', {'orgcode': provider_orgcode, 'del_flg': '0'})
+ if exist_org_li:
+ provider_orgid = exist_org_li[0].get('id')
+ await sor.U('organization', {'id': provider_orgid}, {
+ 'id': provider_orgid,
+ 'orgname': provider_name,
+ 'orgcode': provider_orgcode,
+ 'parentid': sync_orgid,
+ 'org_type': '4'
+ })
+ else:
+ provider_orgid = uuid()
+ await sor.C('organization', {
+ 'id': provider_orgid,
+ 'orgname': provider_name,
+ 'contactor': '连战',
+ 'contactor_phone': '13800138000',
+ 'address': '北京市海淀区',
+ 'main_business': provider_name,
+ 'orgcode': provider_orgcode,
+ 'emailaddress': 'lianzhan@cntoai.com',
+ 'org_type': '4',
+ 'parentid': sync_orgid
+ })
+ await openProviderAccounts(sor, sync_orgid, provider_orgid)
+
+ provider_li = await sor.R('provider', {'orgid': provider_orgid, 'del_flg': '0'})
+ provider_data = {
+ 'name': provider_name,
+ 'description': provider_name,
+ 'appid': item.get('upappid') or '',
+ 'providerkey': provider_orgcode,
+ 'state': '1',
+ 'start_date': '2026-07-01',
+ 'end_date': '9999-12-31',
+ 'settle_mode': '3',
+ 'settle_flowid': '0',
+ 'settle_datep': 'M00',
+ 'discount_mode': 1,
+ 'orgid': provider_orgid
+ }
+ if provider_li:
+ provider_id = provider_li[0].get('id')
+ provider_data['id'] = provider_id
+ await sor.U('provider', {'id': provider_id}, provider_data)
+ else:
+ provider_id = uuid()
+ provider_data['id'] = provider_id
+ await sor.C('provider', provider_data)
+
+ protocol_li = await sor.R('saleprotocol', {'offer_orgid': provider_orgid, 'bid_orgid': sync_orgid, 'del_flg': '0'})
+ if protocol_li:
+ protocolid = protocol_li[0].get('id')
+ await sor.U('saleprotocol', {'id': protocolid}, {
+ 'id': protocolid,
+ 'offer_orgid': provider_orgid,
+ 'bid_orgid': sync_orgid,
+ 'salemode': '0',
+ 'settle_mode': '3',
+ 'settle_dp': 'M00',
+ 'start_date': '2026-07-01',
+ 'end_date': '9999-12-31'
+ })
+ else:
+ protocolid = uuid()
+ await sor.C('saleprotocol', {
+ 'id': protocolid,
+ 'offer_orgid': provider_orgid,
+ 'bid_orgid': sync_orgid,
+ 'salemode': '0',
+ 'settle_mode': '3',
+ 'settle_dp': 'M00',
+ 'start_date': '2026-07-01',
+ 'end_date': '9999-12-31'
+ })
+
+ return {
+ 'provider_orgid': provider_orgid,
+ 'provider_id': provider_id,
+ 'protocolid': protocolid
+ }
+
+ async def sync_product(sor, item, category_list, provider_orgid, protocolid, sync_orgid):
+ product_data = {
+ 'providerid': provider_orgid,
+ 'providerpid': item.get('id'),
+ 'name': item.get('name'),
+ 'description': item.get('description') or '',
+ 'ptype': '21',
+ 'state': '1',
+ 'salemode': '0',
+ 'product_code': item.get('model'),
+ 'reseller_orgid': sync_orgid,
+ 'classify': 'A',
+ 'label': item.get('catelogname') or category_list.get('catelogname'),
+ 'productgroup': item.get('catelog_id') or item.get('catelogid') or category_list.get('catelogid'),
+ 'product_area': '北京',
+ 'effect_date': '2026-07-01',
+ 'expire_date': '9999-12-31'
+ }
+ product_li = await sor.R('product', {'providerid': provider_orgid, 'product_code': item.get('model'), 'del_flg': '0'})
+ if product_li:
+ product_id = product_li[0].get('id')
+ product_data['id'] = product_id
+ await sor.U('product', {'id': product_id}, product_data)
+ product_action = 'update'
+ else:
+ product_id = uuid()
+ product_data['id'] = product_id
+ await sor.C('product', product_data)
+ product_action = 'insert'
+
+ salemode_data = {
+ 'protocolid': protocolid,
+ 'providerid': provider_orgid,
+ 'productid': product_id,
+ 'discount': 1,
+ 'unit': 'hour'
+ }
+ product_salemode_li = await sor.R('product_salemode', {
+ 'protocolid': protocolid,
+ 'providerid': provider_orgid,
+ 'productid': product_id,
+ 'del_flg': '0'
+ })
+ if product_salemode_li:
+ salemode_data['id'] = product_salemode_li[0].get('id')
+ await sor.U('product_salemode', {'id': salemode_data['id']}, salemode_data)
+ else:
+ salemode_data['id'] = uuid()
+ await sor.C('product_salemode', salemode_data)
+
+ return product_action
+
+ async def upsert_llm(sor, item):
+ llm_data = build_llm_data(item)
+ exist_llm = await sor.R('llm', {'model': item.get('model')})
+ if exist_llm:
+ llm_data['id'] = exist_llm[0].get('id')
+ await sor.U('llm', {'id': llm_data['id']}, llm_data)
+ return 'update'
+ await sor.C('llm', llm_data)
+ return 'insert'
# 从数据库读取domain和Bearer token
db = DBPools()
@@ -45,112 +216,93 @@ async def sync_model_to_llm(ns={}):
# 插入数据库
db = DBPools()
async with db.sqlorContext('kboss') as sor:
- new_llms_count = 0
- sync_products_count = 0
- new_llms_list = []
+ provider_groups = {}
+ current_model_set = set()
for category_list in result:
for item in category_list.get('llms', []):
provider_orgcode = item.get('providerid')
- provider_name = item.get('provider_name') or item.get('providerid') or '模型供应商'
- if not provider_orgcode:
+ model = item.get('model')
+ if not provider_orgcode or not model:
continue
-
- provider_ns = {
- 'orgid': sync_orgid,
- 'orgcode': provider_orgcode,
- 'orgname': provider_name,
- 'name': provider_name,
- 'description': provider_name,
- 'appid': item.get('upappid') or '',
- 'providerkey': provider_orgcode,
- 'state': '1',
- 'start_date': '2026-07-01',
- 'end_date': '9999-12-31',
- 'settle_flowid': '0',
- 'salemode': '0',
- 'settle_mode': '3',
- 'settle_datep': 'M00',
- 'contactor': '连战',
- 'contactor_phone': '13800138000',
- 'emailaddress': 'lianzhan@cntoai.com',
- 'address': '北京市海淀区',
- 'org_type': '4'
- }
- provider_res = await path_call('../provider/providerAdd.dspy', provider_ns)
- if not provider_res.get('status'):
- return {
- 'status': False,
- 'msg': '供应商同步失败, %s, %s' % (provider_name, provider_res.get('msg'))
- }
- provider_data = provider_res.get('data') or {}
- provider_orgid = provider_data.get('provider_orgid') or provider_data.get('orgid')
- protocolid = provider_data.get('protocolid')
- if not provider_orgid or not protocolid:
- return {
- 'status': False,
- 'msg': '供应商同步失败, 未返回provider_orgid或protocolid, %s' % provider_name
+ item['catelogname'] = item.get('catelogname') or category_list.get('catelogname')
+ item['catelog_id'] = item.get('catelog_id') or item.get('catelogid') or category_list.get('catelogid')
+ current_model_set.add(model)
+ if provider_orgcode not in provider_groups:
+ provider_groups[provider_orgcode] = {
+ 'provider_name': item.get('provider_name') or provider_orgcode or '模型供应商',
+ 'items': []
}
+ elif item.get('provider_name'):
+ provider_groups[provider_orgcode]['provider_name'] = item.get('provider_name')
+ provider_groups[provider_orgcode]['items'].append({
+ 'item': item,
+ 'category': category_list
+ })
- product_ns = {
- 'providerid': provider_orgid,
- 'providerpid': item.get('id'),
- 'protocolid': protocolid,
- 'name': item.get('name'),
- 'product_code': item.get('model'),
- 'reseller_orgid': sync_orgid,
- 'label': item.get('catelogname') or category_list.get('catelogname'),
- 'classify': 'A',
- 'ptype': '21',
- 'unit': 'hour',
- 'discount': '1',
- 'product_area': '北京'
- }
- product_res = await path_call('../product/productAdd.dspy', product_ns)
- if not product_res.get('status'):
- return {
- 'status': False,
- 'msg': '产品同步失败, %s, %s' % (item.get('name'), product_res.get('msg'))
- }
- sync_products_count += 1
+ provider_count = 0
+ product_insert_count = 0
+ product_update_count = 0
+ llm_insert_count = 0
+ llm_update_count = 0
+ synced_llms_list = []
- # 查找数据库中是否已经存在,不存在就插入
- exist_llm = await sor.R('llm', {'model': item.get('model')})
- if exist_llm:
- # 更新
- exits_id = exist_llm[0].get('id')
- await sor.U('llm', {'id': exits_id}, item)
- continue
-
- new_llms = {
- 'id': item.get('id'),
- 'name': item.get('name'),
- 'model': item.get('model'),
- 'description': item.get('description'),
- 'iconid': item.get('iconid'),
- 'upappid': item.get('upappid'),
- 'providerid': item.get('providerid'),
- 'ownerid': item.get('ownerid'),
- 'enabled_date': item.get('enabled_date'),
- 'expired_date': item.get('expired_date'),
- 'min_balance': item.get('min_balance'),
- 'status': item.get('status'),
- 'catelogname': item.get('catelogname'),
- 'catelog_id': item.get('catelog_id'),
- 'apiname': item.get('apiname'),
- 'query_apiname': item.get('query_apiname'),
- 'query_period': item.get('query_period'),
- 'ppid': item.get('ppid'),
- 'provider_name': item.get('provider_name'),
- 'pricing_display': item.get('pricing_display'),
- 'provider_iconurl': item.get('provider_iconurl')
- }
- new_llms_count += 1
- new_llms_list.append(new_llms.get('model'))
- await sor.C('llm', new_llms)
+ for provider_orgcode, provider_group in provider_groups.items():
+ provider_name = provider_group.get('provider_name') or provider_orgcode or '模型供应商'
+ provider_data = await ensure_provider(
+ sor,
+ provider_orgcode,
+ provider_name,
+ sync_orgid,
+ provider_group['items'][0]['item']
+ )
+ provider_count += 1
+
+ for model_info in provider_group['items']:
+ item = model_info['item']
+ category_list = model_info['category']
+ product_action = await sync_product(
+ sor,
+ item,
+ category_list,
+ provider_data['provider_orgid'],
+ provider_data['protocolid'],
+ sync_orgid
+ )
+ if product_action == 'insert':
+ product_insert_count += 1
+ else:
+ product_update_count += 1
+
+ llm_action = await upsert_llm(sor, item)
+ if llm_action == 'insert':
+ llm_insert_count += 1
+ else:
+ llm_update_count += 1
+ synced_llms_list.append(item.get('model'))
+
+ llm_unpublished_count = 0
+ exist_llm_li = await sor.R('llm', {})
+ for exist_llm in exist_llm_li:
+ exist_model = exist_llm.get('model')
+ if exist_model and exist_model not in current_model_set and exist_llm.get('status') != 'unpublished':
+ await sor.U('llm', {'id': exist_llm.get('id')}, {
+ 'id': exist_llm.get('id'),
+ 'status': 'unpublished'
+ })
+ llm_unpublished_count += 1
return {
'status': True,
- 'msg': f"sync_llm_list同步模型成功,共同步{sync_products_count}个产品,共插入{new_llms_count}个模型,模型列表: {new_llms_list}"
+ 'msg': 'sync_llm_list同步模型成功',
+ 'data': {
+ 'provider_count': provider_count,
+ 'product_insert_count': product_insert_count,
+ 'product_update_count': product_update_count,
+ 'llm_insert_count': llm_insert_count,
+ 'llm_update_count': llm_update_count,
+ 'llm_unpublished_count': llm_unpublished_count,
+ 'model_list': synced_llms_list
+ }
}
except Exception as e:
From f0f32b9239fb299d60c54c0523fa53060d0b2b4b Mon Sep 17 00:00:00 2001
From: ping <1017253325@qq.com>
Date: Wed, 22 Jul 2026 16:20:06 +0800
Subject: [PATCH 12/15] update
---
b/cntoai/sync_model_to_llm.dspy | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/b/cntoai/sync_model_to_llm.dspy b/b/cntoai/sync_model_to_llm.dspy
index ee87c1d..917f0cf 100644
--- a/b/cntoai/sync_model_to_llm.dspy
+++ b/b/cntoai/sync_model_to_llm.dspy
@@ -37,7 +37,7 @@ async def sync_model_to_llm(ns={}):
exist_org_li = await sor.R('organization', {'orgcode': provider_orgcode, 'del_flg': '0'})
if exist_org_li:
provider_orgid = exist_org_li[0].get('id')
- await sor.U('organization', {'id': provider_orgid}, {
+ await sor.U('organization', {
'id': provider_orgid,
'orgname': provider_name,
'orgcode': provider_orgcode,
@@ -78,7 +78,7 @@ async def sync_model_to_llm(ns={}):
if provider_li:
provider_id = provider_li[0].get('id')
provider_data['id'] = provider_id
- await sor.U('provider', {'id': provider_id}, provider_data)
+ await sor.U('provider', provider_data)
else:
provider_id = uuid()
provider_data['id'] = provider_id
@@ -87,7 +87,7 @@ async def sync_model_to_llm(ns={}):
protocol_li = await sor.R('saleprotocol', {'offer_orgid': provider_orgid, 'bid_orgid': sync_orgid, 'del_flg': '0'})
if protocol_li:
protocolid = protocol_li[0].get('id')
- await sor.U('saleprotocol', {'id': protocolid}, {
+ await sor.U('saleprotocol', {
'id': protocolid,
'offer_orgid': provider_orgid,
'bid_orgid': sync_orgid,
@@ -121,7 +121,7 @@ async def sync_model_to_llm(ns={}):
'providerid': provider_orgid,
'providerpid': item.get('id'),
'name': item.get('name'),
- 'description': item.get('description') or '',
+ # 'description': item.get('description') or '',
'ptype': '21',
'state': '1',
'salemode': '0',
@@ -129,7 +129,7 @@ async def sync_model_to_llm(ns={}):
'reseller_orgid': sync_orgid,
'classify': 'A',
'label': item.get('catelogname') or category_list.get('catelogname'),
- 'productgroup': item.get('catelog_id') or item.get('catelogid') or category_list.get('catelogid'),
+ # 'productgroup': item.get('catelog_id') or item.get('catelogid') or category_list.get('catelogid'),
'product_area': '北京',
'effect_date': '2026-07-01',
'expire_date': '9999-12-31'
@@ -138,7 +138,7 @@ async def sync_model_to_llm(ns={}):
if product_li:
product_id = product_li[0].get('id')
product_data['id'] = product_id
- await sor.U('product', {'id': product_id}, product_data)
+ await sor.U('product', product_data)
product_action = 'update'
else:
product_id = uuid()
@@ -161,7 +161,7 @@ async def sync_model_to_llm(ns={}):
})
if product_salemode_li:
salemode_data['id'] = product_salemode_li[0].get('id')
- await sor.U('product_salemode', {'id': salemode_data['id']}, salemode_data)
+ await sor.U('product_salemode', salemode_data)
else:
salemode_data['id'] = uuid()
await sor.C('product_salemode', salemode_data)
@@ -173,7 +173,7 @@ async def sync_model_to_llm(ns={}):
exist_llm = await sor.R('llm', {'model': item.get('model')})
if exist_llm:
llm_data['id'] = exist_llm[0].get('id')
- await sor.U('llm', {'id': llm_data['id']}, llm_data)
+ await sor.U('llm', llm_data)
return 'update'
await sor.C('llm', llm_data)
return 'insert'
@@ -285,7 +285,7 @@ async def sync_model_to_llm(ns={}):
for exist_llm in exist_llm_li:
exist_model = exist_llm.get('model')
if exist_model and exist_model not in current_model_set and exist_llm.get('status') != 'unpublished':
- await sor.U('llm', {'id': exist_llm.get('id')}, {
+ await sor.U('llm', {
'id': exist_llm.get('id'),
'status': 'unpublished'
})
From 9465a005baf48bad0672ac6b5bc3321322b8c871 Mon Sep 17 00:00:00 2001
From: hrx <18603305412@163.com>
Date: Wed, 22 Jul 2026 17:20:34 +0800
Subject: [PATCH 13/15] =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
f/web-kboss/src/App.vue | 23 +++-
f/web-kboss/src/utils/request.js | 12 +-
.../src/views/product/allProduct/index.vue | 112 +++++++++++++-----
3 files changed, 106 insertions(+), 41 deletions(-)
diff --git a/f/web-kboss/src/App.vue b/f/web-kboss/src/App.vue
index 6e799fe..d1ac44a 100644
--- a/f/web-kboss/src/App.vue
+++ b/f/web-kboss/src/App.vue
@@ -4,6 +4,10 @@
+