update
This commit is contained in:
parent
5aa0fe4303
commit
7afd47655d
@ -22,10 +22,22 @@ async def create_model_apikey(ns={}):
|
|||||||
|
|
||||||
already_sync_user_key = records[0]['opc_apikey']
|
already_sync_user_key = records[0]['opc_apikey']
|
||||||
already_sync_user_appid = records[0]['appid']
|
already_sync_user_appid = records[0]['appid']
|
||||||
|
|
||||||
|
# domain 从数据库params表中获取到pname=cntoai_domain的pvalue值
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('kboss') as sor:
|
||||||
|
domain = await sor.R('params', {'pname': 'cntoai_domain'})
|
||||||
|
if domain:
|
||||||
|
domain = domain[0]['pvalue']
|
||||||
|
else:
|
||||||
|
debug(f"create_model_apikey未找到域名")
|
||||||
|
return {
|
||||||
|
'status': False,
|
||||||
|
'msg': '未找到域名'
|
||||||
|
}
|
||||||
|
|
||||||
# 目标URL
|
# 目标URL
|
||||||
base_url = 'https://ai.atvoe.com'
|
url = f"{domain}/dapi/apply_apikey.dspy"
|
||||||
url = f"{base_url}/dapi/apply_apikey.dspy"
|
|
||||||
|
|
||||||
# 请求头
|
# 请求头
|
||||||
headers = {
|
headers = {
|
||||||
@ -35,10 +47,11 @@ async def create_model_apikey(ns={}):
|
|||||||
|
|
||||||
# 请求体数据
|
# 请求体数据
|
||||||
payload = {
|
payload = {
|
||||||
"appname": "cn_ai_user",
|
"appname": ns.get('appname'),
|
||||||
"description": "",
|
"description": ns.get('description'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 正常返回的是 {'status': 'ok', 'data': {'id': 'HlEQmcbCA1dX0qjhffA_K', 'name': 'cn_ai_user', 'description': '', 'secretkey': 'QUZVcXg5V1p1STMybG5Ia4r9NHBpkeRw558aATmohvZ7GYptvg==', 'allowedips': None, 'orgid': 'KHtWKY2LENTU4hYYim1Ks'}}
|
||||||
try:
|
try:
|
||||||
# 创建一个异步会话
|
# 创建一个异步会话
|
||||||
result_sysnc = None
|
result_sysnc = None
|
||||||
@ -49,7 +62,7 @@ async def create_model_apikey(ns={}):
|
|||||||
debug(f"create_model_apikey状态码: {response.status}")
|
debug(f"create_model_apikey状态码: {response.status}")
|
||||||
result_sysnc = await response.json()
|
result_sysnc = await response.json()
|
||||||
|
|
||||||
if not result_sysnc.get('status') == 'success':
|
if not result_sysnc.get('status') == 'ok':
|
||||||
debug(f"create_model_apikey创建模型apikey失败: {result_sysnc}")
|
debug(f"create_model_apikey创建模型apikey失败: {result_sysnc}")
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': False,
|
||||||
@ -60,24 +73,22 @@ async def create_model_apikey(ns={}):
|
|||||||
async with db.sqlorContext('kboss') as sor:
|
async with db.sqlorContext('kboss') as sor:
|
||||||
# user_api_keys表格 userid/opc_apikey
|
# user_api_keys表格 userid/opc_apikey
|
||||||
# 首先判断apikey是否存在
|
# 首先判断apikey是否存在
|
||||||
apikey = result_sysnc['data'][0].get('apikey')
|
remote_table_id = result_sysnc['data'].get('id')
|
||||||
appid = result_sysnc['data'][0].get('appid')
|
name = result_sysnc['data'].get('name')
|
||||||
secretkey = result_sysnc['data'][0].get('secretkey')
|
secretkey = result_sysnc['data'].get('secretkey')
|
||||||
|
|
||||||
await sor.C('user_api_keys', {
|
await sor.C('user_api_keys', {
|
||||||
'userid': userid,
|
'userid': ns['userid'],
|
||||||
'opc_apikey': apikey,
|
'remote_table_id': remote_table_id,
|
||||||
'appid': appid,
|
'name': name,
|
||||||
|
'opc_apikey': 1,
|
||||||
'secretkey': secretkey,
|
'secretkey': secretkey,
|
||||||
'action': 'user_self_create',
|
'action': 'user_self_create',
|
||||||
'expire_time': None,
|
|
||||||
})
|
})
|
||||||
|
return {
|
||||||
debug(f"sync_cn_ai_user用户{payload['user']['id']}同步成功")
|
'status': True,
|
||||||
return {
|
'msg': '创建模型apikey成功'
|
||||||
'status': True,
|
}
|
||||||
'msg': '用户同步成功'
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug(f"sync_cn_ai_user{userid}同步用户失败: {e}")
|
debug(f"sync_cn_ai_user{userid}同步用户失败: {e}")
|
||||||
|
|||||||
@ -23,9 +23,21 @@ async def get_model_apikey(ns={}):
|
|||||||
already_sync_user_key = records[0]['opc_apikey']
|
already_sync_user_key = records[0]['opc_apikey']
|
||||||
already_sync_user_appid = records[0]['appid']
|
already_sync_user_appid = records[0]['appid']
|
||||||
|
|
||||||
|
# domain 从数据库params表中获取到pname=cntoai_domain的pvalue值
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('kboss') as sor:
|
||||||
|
domain = await sor.R('params', {'pname': 'cntoai_domain'})
|
||||||
|
if domain:
|
||||||
|
domain = domain[0]['pvalue']
|
||||||
|
else:
|
||||||
|
debug(f"get_model_apikey未找到域名")
|
||||||
|
return {
|
||||||
|
'status': False,
|
||||||
|
'msg': '未找到域名'
|
||||||
|
}
|
||||||
|
|
||||||
# 目标URL
|
# 目标URL
|
||||||
base_url = 'https://ai.atvoe.com'
|
url = f"{domain}/dapi/downapps.dspy"
|
||||||
url = f"{base_url}/dapi/apply_apikey.dspy"
|
|
||||||
|
|
||||||
# 请求头
|
# 请求头
|
||||||
headers = {
|
headers = {
|
||||||
@ -43,7 +55,7 @@ async def get_model_apikey(ns={}):
|
|||||||
debug(f"get_model_apikey状态码: {response.status}")
|
debug(f"get_model_apikey状态码: {response.status}")
|
||||||
result_sysnc = await response.json()
|
result_sysnc = await response.json()
|
||||||
|
|
||||||
if not result_sysnc.get('status') == 'success':
|
if not result_sysnc.get('status') == 'ok':
|
||||||
debug(f"get_model_apikey获取模型apikey失败: {result_sysnc}")
|
debug(f"get_model_apikey获取模型apikey失败: {result_sysnc}")
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': False,
|
||||||
@ -54,30 +66,33 @@ async def get_model_apikey(ns={}):
|
|||||||
async with db.sqlorContext('kboss') as sor:
|
async with db.sqlorContext('kboss') as sor:
|
||||||
# user_api_keys表格 userid/opc_apikey
|
# user_api_keys表格 userid/opc_apikey
|
||||||
# 首先判断apikey是否存在
|
# 首先判断apikey是否存在
|
||||||
apikey = result_sysnc['data'][0].get('apikey')
|
apikeys = result_sysnc['data']['apikeys']
|
||||||
appid = result_sysnc['data'][0].get('appid')
|
# 遍历apikeys,如果apikey不存在,则创建, 如果存在则做更新 根据userid和remote_table_id判断
|
||||||
secretkey = result_sysnc['data'][0].get('secretkey')
|
for apikey_item in apikeys:
|
||||||
|
remote_table_id = apikey_item.get('id')
|
||||||
|
name = '' if not apikey_item.get('name') else apikey_item.get('name')
|
||||||
|
apikeyid = apikey_item.get('apikeyid')
|
||||||
|
exist_record = await sor.R('user_api_keys', {'userid': ns['userid'], 'remote_table_id': remote_table_id})
|
||||||
|
if exist_record:
|
||||||
|
update_sql = f"UPDATE user_api_keys SET name = '{name}', opc_apikey = '{apikeyid}' WHERE userid = '{ns['userid']}' AND remote_table_id = '{remote_table_id}'"
|
||||||
|
await sor.sqlExe(update_sql, {})
|
||||||
|
else:
|
||||||
|
await sor.C('user_api_keys', {
|
||||||
|
'userid': ns['userid'],
|
||||||
|
'remote_table_id': remote_table_id,
|
||||||
|
'name': name,
|
||||||
|
'opc_apikey': apikeyid,
|
||||||
|
'action': 'user_self_create',
|
||||||
|
})
|
||||||
|
|
||||||
await sor.C('user_api_keys', {
|
result_sysnc['status'] = True
|
||||||
'userid': userid,
|
return result_sysnc
|
||||||
'opc_apikey': apikey,
|
|
||||||
'appid': appid,
|
|
||||||
'secretkey': secretkey,
|
|
||||||
'action': 'user_self_create',
|
|
||||||
'expire_time': None,
|
|
||||||
})
|
|
||||||
|
|
||||||
debug(f"sync_cn_ai_user用户{payload['user']['id']}同步成功")
|
|
||||||
return {
|
|
||||||
'status': True,
|
|
||||||
'msg': '用户同步成功'
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug(f"sync_cn_ai_user{userid}同步用户失败: {e}")
|
debug(f"get_model_apikey获取模型apikey失败: {e}")
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': False,
|
||||||
'msg': f"sync_cn_ai_user{userid}同步用户失败: {e}"
|
'msg': f"get_model_apikey获取模型apikey失败: {e}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -5,32 +5,33 @@ async def get_user_balance(ns={}):
|
|||||||
:param userid: 用户 ID
|
:param userid: 用户 ID
|
||||||
:return: 账户余额(与 getCustomerBalance 返回值一致)
|
:return: 账户余额(与 getCustomerBalance 返回值一致)
|
||||||
"""
|
"""
|
||||||
|
debug(ns)
|
||||||
apikey = ns.get('apikey')
|
apikey = ns.get('apikey')
|
||||||
userid = ns.get('userid')
|
userid = ns.get('userid')
|
||||||
db = DBPools()
|
db = DBPools()
|
||||||
async with db.sqlorContext('kboss') as sor:
|
async with db.sqlorContext('kboss') as sor:
|
||||||
if not apikey:
|
if not apikey:
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': 'error',
|
||||||
'msg': 'apikey is required'
|
'msg': 'apikey is required'
|
||||||
}
|
}
|
||||||
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
|
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
|
||||||
if not userid_li:
|
if not userid_li:
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': 'error',
|
||||||
'msg': 'apikey无效,请联系管理员'
|
'msg': 'apikey无效,请联系管理员'
|
||||||
}
|
}
|
||||||
# userid = userid_li[0]['userid']
|
# userid = userid_li[0]['userid']
|
||||||
user = await sor.R('users', {'id': userid})
|
user = await sor.R('users', {'id': userid})
|
||||||
if not user:
|
if not user:
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': 'error',
|
||||||
'msg': '用户不存在'
|
'msg': '用户不存在'
|
||||||
}
|
}
|
||||||
orgid = await sor.R('organization', {'id': user[0]['orgid']})
|
orgid = await sor.R('organization', {'id': user[0]['orgid']})
|
||||||
balance = await getCustomerBalance(sor, orgid[0]['id'])
|
balance = await getCustomerBalance(sor, orgid[0]['id'])
|
||||||
return {
|
return {
|
||||||
'status': True,
|
'status': 'ok',
|
||||||
'balance': balance
|
'balance': balance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ _MODEL_FIELDS = (
|
|||||||
'context_length', 'input_token_price', 'output_token_price',
|
'context_length', 'input_token_price', 'output_token_price',
|
||||||
'cache_hit_input_price', 'billing_method', 'billing_unit',
|
'cache_hit_input_price', 'billing_method', 'billing_unit',
|
||||||
'capabilities', 'limitations', 'highlights', 'is_active',
|
'capabilities', 'limitations', 'highlights', 'is_active',
|
||||||
'description', 'listing_status',
|
'description', 'listing_status',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
94
b/cntoai/model_management_customer_search.dspy
Normal file
94
b/cntoai/model_management_customer_search.dspy
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
def _escape(value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return str(value).replace("'", "''")
|
||||||
|
|
||||||
|
# 客户侧可见字段(不含 listing_status、is_active 等运营字段)
|
||||||
|
_CUSTOMER_MODEL_COLUMNS = """
|
||||||
|
id, llmid, provider, model_name, display_name, model_type,
|
||||||
|
context_length, input_token_price, output_token_price,
|
||||||
|
cache_hit_input_price, billing_method, billing_unit,
|
||||||
|
capabilities, limitations, highlights, description, sort_order
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _customer_listed_conditions(ns):
|
||||||
|
"""已上架且启用的模型;支持按厂商、模型类别筛选"""
|
||||||
|
conditions = ["listing_status = 1", "is_active = 1"]
|
||||||
|
if ns.get('provider'):
|
||||||
|
conditions.append("provider = '%s'" % _escape(ns.get('provider')))
|
||||||
|
if ns.get('model_type'):
|
||||||
|
conditions.append("model_type = '%s'" % _escape(ns.get('model_type')))
|
||||||
|
return ' AND '.join(conditions)
|
||||||
|
|
||||||
|
async def model_management_customer_search(ns={}):
|
||||||
|
"""
|
||||||
|
客户查看模型列表:仅已上架且启用的模型。
|
||||||
|
|
||||||
|
可选参数:
|
||||||
|
provider (str) 厂商,精确匹配筛选
|
||||||
|
model_type (str) 模型类别,精确匹配筛选
|
||||||
|
current_page (int) 页码,默认 1
|
||||||
|
page_size (int) 每页条数,默认 10
|
||||||
|
|
||||||
|
返回 data:
|
||||||
|
provider_list 当前可见模型中的厂商列表(去重)
|
||||||
|
model_type_list 当前可见模型中的模型类别列表(去重)
|
||||||
|
filter_total 当前筛选条件下的模型数量
|
||||||
|
model_list 模型列表
|
||||||
|
page_size, current_page
|
||||||
|
|
||||||
|
调用示例见 model_management_customer_search.dspy
|
||||||
|
"""
|
||||||
|
page_size = int(ns.get('page_size', 1000))
|
||||||
|
current_page = int(ns.get('current_page', 1))
|
||||||
|
offset = (current_page - 1) * page_size
|
||||||
|
where_clause = _customer_listed_conditions(ns)
|
||||||
|
listed_base = "listing_status = 1 AND is_active = 1"
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('kboss') as sor:
|
||||||
|
try:
|
||||||
|
provider_sql = """
|
||||||
|
SELECT DISTINCT provider FROM model_management
|
||||||
|
WHERE %s AND provider IS NOT NULL AND provider != ''
|
||||||
|
ORDER BY provider;
|
||||||
|
""" % listed_base
|
||||||
|
model_type_sql = """
|
||||||
|
SELECT DISTINCT model_type FROM model_management
|
||||||
|
WHERE %s AND model_type IS NOT NULL AND model_type != ''
|
||||||
|
ORDER BY model_type;
|
||||||
|
""" % listed_base
|
||||||
|
|
||||||
|
count_sql = """
|
||||||
|
SELECT COUNT(*) AS total_count FROM model_management WHERE %s;
|
||||||
|
""" % where_clause
|
||||||
|
find_sql = """
|
||||||
|
SELECT %s FROM model_management
|
||||||
|
WHERE %s
|
||||||
|
ORDER BY sort_order ASC
|
||||||
|
LIMIT %s OFFSET %s;
|
||||||
|
""" % (_CUSTOMER_MODEL_COLUMNS, where_clause, page_size, offset)
|
||||||
|
|
||||||
|
provider_rows = await sor.sqlExe(provider_sql, {})
|
||||||
|
model_type_rows = await sor.sqlExe(model_type_sql, {})
|
||||||
|
filter_total = (await sor.sqlExe(count_sql, {}))[0]['total_count']
|
||||||
|
model_list = await sor.sqlExe(find_sql, {})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'status': True,
|
||||||
|
'msg': 'customer model search success',
|
||||||
|
'data': {
|
||||||
|
'provider_list': [r['provider'] for r in provider_rows],
|
||||||
|
'model_type_list': [r['model_type'] for r in model_type_rows],
|
||||||
|
'filter_total': filter_total,
|
||||||
|
'page_size': page_size,
|
||||||
|
'current_page': current_page,
|
||||||
|
'model_list': model_list,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {'status': False, 'msg': 'customer model search failed, %s' % str(e)}
|
||||||
|
|
||||||
|
ret = await model_management_customer_search(params_kw)
|
||||||
|
return ret
|
||||||
67
b/cntoai/model_management_move_down.dspy
Normal file
67
b/cntoai/model_management_move_down.dspy
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
def _escape(value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return str(value).replace("'", "''")
|
||||||
|
|
||||||
|
async def model_management_move_down(ns={}):
|
||||||
|
"""
|
||||||
|
下移:与排序上的下一条记录交换 sort_order(已在最后则提示)
|
||||||
|
|
||||||
|
必填参数:
|
||||||
|
id (int|str) 模型主键
|
||||||
|
"""
|
||||||
|
model_id = ns.get('id')
|
||||||
|
if not model_id:
|
||||||
|
return {'status': False, 'msg': 'id is required'}
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('kboss') as sor:
|
||||||
|
try:
|
||||||
|
current_sql = """
|
||||||
|
SELECT id, sort_order FROM model_management WHERE id = '%s' LIMIT 1;
|
||||||
|
""" % _escape(model_id)
|
||||||
|
current = await sor.sqlExe(current_sql, {})
|
||||||
|
if not current:
|
||||||
|
return {'status': False, 'msg': 'model not found'}
|
||||||
|
|
||||||
|
cur = current[0]
|
||||||
|
cur_order = int(cur.get('sort_order') or 0)
|
||||||
|
cur_id = int(cur.get('id'))
|
||||||
|
|
||||||
|
next_sql = """
|
||||||
|
SELECT id, sort_order FROM model_management
|
||||||
|
WHERE (sort_order > %s) OR (sort_order = %s AND id > %s)
|
||||||
|
ORDER BY sort_order ASC, id ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""" % (cur_order, cur_order, cur_id)
|
||||||
|
next_row = await sor.sqlExe(next_sql, {})
|
||||||
|
if not next_row:
|
||||||
|
return {'status': True, 'msg': 'already at bottom', 'data': {'sort_order': cur_order}}
|
||||||
|
|
||||||
|
nxt = next_row[0]
|
||||||
|
nxt_order = int(nxt.get('sort_order') or 0)
|
||||||
|
nxt_id = _escape(nxt.get('id'))
|
||||||
|
|
||||||
|
swap_cur_sql = """
|
||||||
|
UPDATE model_management SET sort_order = %s WHERE id = '%s';
|
||||||
|
""" % (nxt_order, _escape(model_id))
|
||||||
|
swap_nxt_sql = """
|
||||||
|
UPDATE model_management SET sort_order = %s WHERE id = '%s';
|
||||||
|
""" % (cur_order, nxt_id)
|
||||||
|
await sor.sqlExe(swap_cur_sql, {})
|
||||||
|
await sor.sqlExe(swap_nxt_sql, {})
|
||||||
|
return {
|
||||||
|
'status': True,
|
||||||
|
'msg': 'move down success',
|
||||||
|
'data': {
|
||||||
|
'id': model_id,
|
||||||
|
'sort_order': nxt_order,
|
||||||
|
'swapped_with_id': nxt.get('id'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
await sor.rollback()
|
||||||
|
return {'status': False, 'msg': 'move down failed, %s' % str(e)}
|
||||||
|
|
||||||
|
ret = await model_management_move_down(params_kw)
|
||||||
|
return ret
|
||||||
49
b/cntoai/model_management_pin_top.dspy
Normal file
49
b/cntoai/model_management_pin_top.dspy
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
def _escape(value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return str(value).replace("'", "''")
|
||||||
|
|
||||||
|
async def model_management_pin_top(ns={}):
|
||||||
|
"""
|
||||||
|
置顶:将模型排到全局列表最前(sort_order 设为当前最小值 - 1)
|
||||||
|
|
||||||
|
必填参数:
|
||||||
|
id (int|str) 模型主键
|
||||||
|
"""
|
||||||
|
model_id = ns.get('id')
|
||||||
|
if not model_id:
|
||||||
|
return {'status': False, 'msg': 'id is required'}
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('kboss') as sor:
|
||||||
|
try:
|
||||||
|
current_sql = """
|
||||||
|
SELECT id, sort_order FROM model_management WHERE id = '%s' LIMIT 1;
|
||||||
|
""" % _escape(model_id)
|
||||||
|
current = await sor.sqlExe(current_sql, {})
|
||||||
|
if not current:
|
||||||
|
return {'status': False, 'msg': 'model not found'}
|
||||||
|
|
||||||
|
min_sql = "SELECT MIN(sort_order) AS min_order FROM model_management;"
|
||||||
|
min_order = int((await sor.sqlExe(min_sql, {}))[0].get('min_order') or 0)
|
||||||
|
current_order = int(current[0].get('sort_order') or 0)
|
||||||
|
|
||||||
|
if current_order <= min_order:
|
||||||
|
return {'status': True, 'msg': 'already at top', 'data': {'sort_order': current_order}}
|
||||||
|
|
||||||
|
new_order = min_order - 1
|
||||||
|
update_sql = """
|
||||||
|
UPDATE model_management SET sort_order = %s WHERE id = '%s';
|
||||||
|
""" % (new_order, _escape(model_id))
|
||||||
|
await sor.sqlExe(update_sql, {})
|
||||||
|
return {
|
||||||
|
'status': True,
|
||||||
|
'msg': 'pin to top success',
|
||||||
|
'data': {'id': model_id, 'sort_order': new_order},
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
await sor.rollback()
|
||||||
|
return {'status': False, 'msg': 'pin to top failed, %s' % str(e)}
|
||||||
|
|
||||||
|
ret = await model_management_pin_top(params_kw)
|
||||||
|
return ret
|
||||||
@ -4,7 +4,7 @@ _MODEL_FIELDS = (
|
|||||||
'context_length', 'input_token_price', 'output_token_price',
|
'context_length', 'input_token_price', 'output_token_price',
|
||||||
'cache_hit_input_price', 'billing_method', 'billing_unit',
|
'cache_hit_input_price', 'billing_method', 'billing_unit',
|
||||||
'capabilities', 'limitations', 'highlights', 'is_active',
|
'capabilities', 'limitations', 'highlights', 'is_active',
|
||||||
'description', 'listing_status',
|
'description', 'listing_status', 'sort_order',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -30,14 +30,15 @@ async def model_management_search(ns={}):
|
|||||||
"""
|
"""
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
page_size = int(ns.get('page_size', 100))
|
page_size = int(ns.get('page_size', 1000))
|
||||||
current_page = int(ns.get('current_page', 1))
|
current_page = int(ns.get('current_page', 1))
|
||||||
offset = (current_page - 1) * page_size
|
offset = (current_page - 1) * page_size
|
||||||
|
|
||||||
conditions = ['1=1']
|
conditions = ['1=1']
|
||||||
if ns.get('model_name'):
|
if ns.get('model_name'):
|
||||||
model_name = ns.get('model_name')
|
model_name = ns.get('model_name')
|
||||||
conditions.append("model_name LIKE '%s'" % model_name)
|
# 模糊查询
|
||||||
|
conditions.append(f"model_name LIKE '%%%%{model_name}%%%%'")
|
||||||
if ns.get('model_type'):
|
if ns.get('model_type'):
|
||||||
conditions.append("model_type = '%s'" % _escape(ns.get('model_type')))
|
conditions.append("model_type = '%s'" % _escape(ns.get('model_type')))
|
||||||
if ns.get('provider'):
|
if ns.get('provider'):
|
||||||
@ -57,7 +58,7 @@ async def model_management_search(ns={}):
|
|||||||
count_sql = """SELECT COUNT(*) AS total_count FROM model_management WHERE %s;""" % where_clause
|
count_sql = """SELECT COUNT(*) AS total_count FROM model_management WHERE %s;""" % where_clause
|
||||||
filter_total = (await sor.sqlExe(count_sql, {}))[0]['total_count']
|
filter_total = (await sor.sqlExe(count_sql, {}))[0]['total_count']
|
||||||
|
|
||||||
find_sql = """SELECT * FROM model_management WHERE %s ORDER BY updated_at DESC LIMIT %s OFFSET %s;""" % (where_clause, page_size, offset)
|
find_sql = """SELECT * FROM model_management WHERE %s ORDER BY sort_order ASC LIMIT %s OFFSET %s;""" % (where_clause, page_size, offset)
|
||||||
model_list = await sor.sqlExe(find_sql, {})
|
model_list = await sor.sqlExe(find_sql, {})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -26,7 +26,7 @@ async def _charge_order(sor, orderid, order_type='NEW'):
|
|||||||
"""
|
"""
|
||||||
order_rows = await sor.R('bz_order', {'id': orderid})
|
order_rows = await sor.R('bz_order', {'id': orderid})
|
||||||
if not order_rows:
|
if not order_rows:
|
||||||
return {'status': False, 'msg': '订单不存在'}
|
return {'status': 'error', 'msg': '订单不存在'}
|
||||||
|
|
||||||
order_row = order_rows[0]
|
order_row = order_rows[0]
|
||||||
product_url = None
|
product_url = None
|
||||||
@ -39,7 +39,7 @@ async def _charge_order(sor, orderid, order_type='NEW'):
|
|||||||
if count - float(order_row['amount']) < 0:
|
if count - float(order_row['amount']) < 0:
|
||||||
pricedifference = count - round(order_row['amount'], 2)
|
pricedifference = count - round(order_row['amount'], 2)
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': 'error',
|
||||||
'msg': '账户余额不足',
|
'msg': '账户余额不足',
|
||||||
'pricedifference': round(pricedifference, 2),
|
'pricedifference': round(pricedifference, 2),
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ async def _charge_order(sor, orderid, order_type='NEW'):
|
|||||||
# await sor.C('customer_goods', nss)
|
# await sor.C('customer_goods', nss)
|
||||||
return {'status': True, 'msg': '支付成功'}
|
return {'status': True, 'msg': '支付成功'}
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
return {'status': False, 'msg': str(error)}
|
return {'status': 'error', 'msg': str(error)}
|
||||||
|
|
||||||
async def calc_price_by_saleprotocol(sor, org, product_id, supply_price, quantity=1):
|
async def calc_price_by_saleprotocol(sor, org, product_id, supply_price, quantity=1):
|
||||||
"""
|
"""
|
||||||
@ -136,18 +136,18 @@ async def calc_price_by_saleprotocol(sor, org, product_id, supply_price, quantit
|
|||||||
:param quantity: 数量,默认 1
|
:param quantity: 数量,默认 1
|
||||||
:return: dict
|
:return: dict
|
||||||
成功: status=True, amount(行总金额), price(折后单价), list_price, discount
|
成功: status=True, amount(行总金额), price(折后单价), list_price, discount
|
||||||
失败: status=False, msg
|
失败: status='error', msg
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
supply_price = abs(float(supply_price))
|
supply_price = abs(float(supply_price))
|
||||||
quantity = int(quantity)
|
quantity = int(quantity)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return {'status': False, 'msg': 'supply_price / quantity 必须为有效数字'}
|
return {'status': 'error', 'msg': 'supply_price / quantity 必须为有效数字'}
|
||||||
|
|
||||||
if supply_price <= 0:
|
if supply_price <= 0:
|
||||||
return {'status': False, 'msg': 'supply_price 必须大于 0'}
|
return {'status': 'error', 'msg': 'supply_price 必须大于 0'}
|
||||||
if quantity <= 0:
|
if quantity <= 0:
|
||||||
return {'status': False, 'msg': 'quantity 必须大于 0'}
|
return {'status': 'error', 'msg': 'quantity 必须大于 0'}
|
||||||
|
|
||||||
saleprotocol_to_person = await sor.R(
|
saleprotocol_to_person = await sor.R(
|
||||||
'saleprotocol',
|
'saleprotocol',
|
||||||
@ -193,7 +193,7 @@ async def calc_price_by_saleprotocol(sor, org, product_id, supply_price, quantit
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not product_salemode:
|
if not product_salemode:
|
||||||
return {'status': False, 'msg': '还未上线这个产品的协议配置'}
|
return {'status': 'error', 'msg': '还未上线这个产品的协议配置'}
|
||||||
|
|
||||||
discount = product_salemode[0]['discount']
|
discount = product_salemode[0]['discount']
|
||||||
list_price = supply_price
|
list_price = supply_price
|
||||||
@ -217,12 +217,25 @@ async def process_user_billing(ns={}):
|
|||||||
:param userid: 用户 ID
|
:param userid: 用户 ID
|
||||||
:param providername: 厂商名称(写入 bz_order.source,并用于查 product)
|
:param providername: 厂商名称(写入 bz_order.source,并用于查 product)
|
||||||
:param productname: 产品名称(写入 servicename,并用于查 product)
|
:param productname: 产品名称(写入 servicename,并用于查 product)
|
||||||
:param amount: 扣费金额;use_saleprotocol=False 时为最终扣费额;
|
:param amount: 扣费金额;use_saleprotocol='error' 时为最终扣费额;
|
||||||
use_saleprotocol=True 时为供应价/目录价(折扣前单价),走协议算价
|
use_saleprotocol=True 时为供应价/目录价(折扣前单价),走协议算价
|
||||||
:param use_saleprotocol: 是否启用 saleprotocol_pricing 协议折扣算价,默认 False 直接按 amount 扣费
|
:param use_saleprotocol: 是否启用 saleprotocol_pricing 协议折扣算价,默认 'error' 直接按 amount 扣费
|
||||||
:param quantity: 仅 use_saleprotocol=True 时生效,数量默认 1
|
:param quantity: 仅 use_saleprotocol=True 时生效,数量默认 1
|
||||||
:return: dict,含 status、msg;成功时含 orderid、amount
|
:return: dict,含 status、msg;成功时含 orderid、amount
|
||||||
"""
|
"""
|
||||||
|
# 存储输入值到usage表
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('kboss') as sor:
|
||||||
|
usage_ns = {
|
||||||
|
'id': uuid(),
|
||||||
|
'userid': ns.get('userid'),
|
||||||
|
'apikey': ns.get('apikey'),
|
||||||
|
'llmid': ns.get('llmid'),
|
||||||
|
'original_price': ns.get('amount'),
|
||||||
|
'usage_content': json.dumps(ns.get('usage')) if isinstance(ns.get('usage'), dict) else ns.get('usage')
|
||||||
|
}
|
||||||
|
await sor.C('model_usage', usage_ns)
|
||||||
|
|
||||||
apikey = ns.get('apikey')
|
apikey = ns.get('apikey')
|
||||||
userid = ns.get('userid')
|
userid = ns.get('userid')
|
||||||
providername = ns.get('providername')
|
providername = ns.get('providername')
|
||||||
@ -234,142 +247,151 @@ async def process_user_billing(ns={}):
|
|||||||
llmid = ns.get('llmid')
|
llmid = ns.get('llmid')
|
||||||
if not llmid:
|
if not llmid:
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': 'error',
|
||||||
'msg': 'llmid必传'
|
'msg': 'llmid必传'
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
amount = round(float(amount), 2)
|
amount = round(float(amount), 2)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return {'status': False, 'msg': 'amount 必须为有效数字'}
|
return {'status': 'error', 'msg': 'amount 必须为有效数字'}
|
||||||
|
|
||||||
if amount <= 0:
|
if amount <= 0:
|
||||||
return {'status': False, 'msg': 'amount 必须大于 0'}
|
return {'status': 'error', 'msg': 'amount 必须大于 0'}
|
||||||
|
|
||||||
db = DBPools()
|
db = DBPools()
|
||||||
async with db.sqlorContext('kboss') as sor:
|
async with db.sqlorContext('kboss') as sor:
|
||||||
product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'})
|
try:
|
||||||
if not product_li:
|
product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'})
|
||||||
return {
|
if not product_li:
|
||||||
'status': False,
|
return {
|
||||||
'msg': '未找到对应产品,请确认'
|
'status': 'error',
|
||||||
|
'msg': '未找到对应产品,请确认'
|
||||||
|
}
|
||||||
|
product = product_li[0]
|
||||||
|
productname = product['name']
|
||||||
|
providerid = product['providerid']
|
||||||
|
providername_list = await sor.R('organization', {'id': providerid})
|
||||||
|
if not providername_list:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'msg': '厂商不存在 %s' % providername
|
||||||
|
}
|
||||||
|
providername = providername_list[0]['orgname']
|
||||||
|
|
||||||
|
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
|
||||||
|
if not userid_li:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'msg': 'apikey无效,请联系管理员'
|
||||||
|
}
|
||||||
|
# userid = userid_li[0]['userid']
|
||||||
|
|
||||||
|
user_list = await sor.R('users', {'id': userid})
|
||||||
|
if not user_list:
|
||||||
|
return {'status': 'error', 'msg': '用户不存在 %s' % userid}
|
||||||
|
|
||||||
|
org_list = await sor.R('organization', {'id': user_list[0]['orgid']})
|
||||||
|
if not org_list:
|
||||||
|
return {'status': 'error', 'msg': '用户所属机构不存在'}
|
||||||
|
|
||||||
|
customerid = org_list[0]['id']
|
||||||
|
# product = await _lookup_product(sor, providername, productname)
|
||||||
|
# if not product:
|
||||||
|
# return {
|
||||||
|
# 'status': 'error',
|
||||||
|
# 'msg': '未找到对应产品,请确认 providername/productname 与库中 provider、product 配置一致',
|
||||||
|
# }
|
||||||
|
|
||||||
|
list_price = amount
|
||||||
|
unit_price = amount
|
||||||
|
discount = 1
|
||||||
|
originalprice = amount
|
||||||
|
|
||||||
|
if use_saleprotocol:
|
||||||
|
price_res = await calc_price_by_saleprotocol(
|
||||||
|
sor, org_list[0], product['id'], amount, quantity=quantity,
|
||||||
|
)
|
||||||
|
if not price_res['status']:
|
||||||
|
return price_res
|
||||||
|
debug(price_res)
|
||||||
|
debug('list_price %s' % list_price)
|
||||||
|
amount = price_res['amount']
|
||||||
|
list_price = price_res['list_price']
|
||||||
|
unit_price = price_res['price']
|
||||||
|
discount = price_res['discount']
|
||||||
|
originalprice = list_price * quantity
|
||||||
|
|
||||||
|
balance = await getCustomerBalance(sor, customerid)
|
||||||
|
if balance is None:
|
||||||
|
balance = 0
|
||||||
|
if amount > balance:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'msg': '账户余额不足',
|
||||||
|
'pricedifference': round(balance - amount, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
order_id = uuid()
|
||||||
|
now_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
bz_ns = {
|
||||||
|
'id': order_id,
|
||||||
|
'order_status': '0',
|
||||||
|
'business_op': 'BUY',
|
||||||
|
'userid': userid,
|
||||||
|
'customerid': customerid,
|
||||||
|
'order_date': now_str,
|
||||||
|
'source': providername,
|
||||||
|
'amount': amount,
|
||||||
|
'originalprice': round(originalprice, 2),
|
||||||
|
'ordertype': 'prepay',
|
||||||
|
'servicename': productname,
|
||||||
}
|
}
|
||||||
product = product_li[0]
|
await sor.C('bz_order', bz_ns)
|
||||||
productname = product['name']
|
|
||||||
providerid = product['providerid']
|
goods_ns = {
|
||||||
providername_list = await sor.R('organization', {'id': providerid})
|
'id': uuid(),
|
||||||
if not providername_list:
|
'orderid': order_id,
|
||||||
return {
|
'productid': product['id'],
|
||||||
'status': False,
|
'providerid': product['providerid'],
|
||||||
'msg': '厂商不存在 %s' % providername
|
'list_price': list_price,
|
||||||
|
'discount': discount,
|
||||||
|
'quantity': quantity if use_saleprotocol else 1,
|
||||||
|
'price': unit_price,
|
||||||
|
'amount': amount,
|
||||||
|
'chargemode': 'prepay',
|
||||||
|
'servicename': productname,
|
||||||
|
'resourceids': '',
|
||||||
|
'resourcestarttime': now_str,
|
||||||
|
'resourceendtime': None,
|
||||||
}
|
}
|
||||||
providername = providername_list[0]['orgname']
|
await sor.C('order_goods', goods_ns)
|
||||||
|
|
||||||
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
|
charge_res = await _charge_order(sor, order_id, order_type='NEW')
|
||||||
if not userid_li:
|
if not charge_res['status']:
|
||||||
return {
|
await sor.rollback()
|
||||||
'status': False,
|
return charge_res
|
||||||
'msg': 'apikey无效,请联系管理员'
|
|
||||||
|
await sor.U('model_usage', {'id': usage_ns['id'], 'orderid': order_id, 'bill_status': 1})
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'status': 'ok',
|
||||||
|
'msg': '扣费成功',
|
||||||
|
'orderid': order_id,
|
||||||
|
'amount': amount,
|
||||||
|
'productid': product['id'],
|
||||||
}
|
}
|
||||||
# userid = userid_li[0]['userid']
|
if use_saleprotocol:
|
||||||
|
result['discount'] = discount
|
||||||
user_list = await sor.R('users', {'id': userid})
|
result['list_price'] = list_price
|
||||||
if not user_list:
|
result['price'] = unit_price
|
||||||
return {'status': False, 'msg': '用户不存在 %s' % userid}
|
return result
|
||||||
|
except Exception as e:
|
||||||
org_list = await sor.R('organization', {'id': user_list[0]['orgid']})
|
sor.rollback()
|
||||||
if not org_list:
|
|
||||||
return {'status': False, 'msg': '用户所属机构不存在'}
|
|
||||||
|
|
||||||
customerid = org_list[0]['id']
|
|
||||||
# product = await _lookup_product(sor, providername, productname)
|
|
||||||
# if not product:
|
|
||||||
# return {
|
|
||||||
# 'status': False,
|
|
||||||
# 'msg': '未找到对应产品,请确认 providername/productname 与库中 provider、product 配置一致',
|
|
||||||
# }
|
|
||||||
|
|
||||||
list_price = amount
|
|
||||||
unit_price = amount
|
|
||||||
discount = 1
|
|
||||||
originalprice = amount
|
|
||||||
|
|
||||||
if use_saleprotocol:
|
|
||||||
price_res = await calc_price_by_saleprotocol(
|
|
||||||
sor, org_list[0], product['id'], amount, quantity=quantity,
|
|
||||||
)
|
|
||||||
if not price_res['status']:
|
|
||||||
return price_res
|
|
||||||
debug(price_res)
|
|
||||||
debug('list_price %s' % list_price)
|
|
||||||
amount = price_res['amount']
|
|
||||||
list_price = price_res['list_price']
|
|
||||||
unit_price = price_res['price']
|
|
||||||
discount = price_res['discount']
|
|
||||||
originalprice = list_price * quantity
|
|
||||||
|
|
||||||
balance = await getCustomerBalance(sor, customerid)
|
|
||||||
if balance is None:
|
|
||||||
balance = 0
|
|
||||||
if amount > balance:
|
|
||||||
return {
|
return {
|
||||||
'status': False,
|
'status': 'error',
|
||||||
'msg': '账户余额不足',
|
'msg': str(e)
|
||||||
'pricedifference': round(balance - amount, 2),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
order_id = uuid()
|
|
||||||
now_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
bz_ns = {
|
|
||||||
'id': order_id,
|
|
||||||
'order_status': '0',
|
|
||||||
'business_op': 'BUY',
|
|
||||||
'userid': userid,
|
|
||||||
'customerid': customerid,
|
|
||||||
'order_date': now_str,
|
|
||||||
'source': providername,
|
|
||||||
'amount': amount,
|
|
||||||
'originalprice': round(originalprice, 2),
|
|
||||||
'ordertype': 'prepay',
|
|
||||||
'servicename': productname,
|
|
||||||
}
|
|
||||||
await sor.C('bz_order', bz_ns)
|
|
||||||
|
|
||||||
goods_ns = {
|
|
||||||
'id': uuid(),
|
|
||||||
'orderid': order_id,
|
|
||||||
'productid': product['id'],
|
|
||||||
'providerid': product['providerid'],
|
|
||||||
'list_price': list_price,
|
|
||||||
'discount': discount,
|
|
||||||
'quantity': quantity if use_saleprotocol else 1,
|
|
||||||
'price': unit_price,
|
|
||||||
'amount': amount,
|
|
||||||
'chargemode': 'prepay',
|
|
||||||
'servicename': productname,
|
|
||||||
'resourceids': '',
|
|
||||||
'resourcestarttime': now_str,
|
|
||||||
'resourceendtime': None,
|
|
||||||
}
|
|
||||||
await sor.C('order_goods', goods_ns)
|
|
||||||
|
|
||||||
charge_res = await _charge_order(sor, order_id, order_type='NEW')
|
|
||||||
if not charge_res['status']:
|
|
||||||
await sor.rollback()
|
|
||||||
return charge_res
|
|
||||||
|
|
||||||
result = {
|
|
||||||
'status': True,
|
|
||||||
'msg': '扣费成功',
|
|
||||||
'orderid': order_id,
|
|
||||||
'amount': amount,
|
|
||||||
'productid': product['id'],
|
|
||||||
}
|
|
||||||
if use_saleprotocol:
|
|
||||||
result['discount'] = discount
|
|
||||||
result['list_price'] = list_price
|
|
||||||
result['price'] = unit_price
|
|
||||||
return result
|
|
||||||
|
|
||||||
ret = await process_user_billing(params_kw)
|
ret = await process_user_billing(params_kw)
|
||||||
return ret
|
return ret
|
||||||
@ -23,8 +23,21 @@ async def sync_cn_ai_user(ns={}):
|
|||||||
already_sync_user_dappid = 'cndemo'
|
already_sync_user_dappid = 'cndemo'
|
||||||
|
|
||||||
# 目标URL
|
# 目标URL
|
||||||
url = "https://ai.atvoe.com/rbac/usersync"
|
# domain 从数据库params表中获取到pname=cntoai_domain的pvalue值
|
||||||
# url = 'https://ai.atvoe.com/tmp/env.dspy'
|
domain = None
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('kboss') as sor:
|
||||||
|
domain = await sor.R('params', {'pname': 'cntoai_domain'})
|
||||||
|
if domain:
|
||||||
|
domain = domain[0]['pvalue']
|
||||||
|
else:
|
||||||
|
debug(f"sync_cn_ai_user未找到域名")
|
||||||
|
return {
|
||||||
|
'status': False,
|
||||||
|
'msg': '未找到域名'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = f"{domain}/rbac/usersync"
|
||||||
|
|
||||||
# 请求头
|
# 请求头
|
||||||
headers = {
|
headers = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user