This commit is contained in:
ping 2026-05-21 20:55:47 +08:00
parent 5aa0fe4303
commit 7afd47655d
10 changed files with 454 additions and 181 deletions

View File

@ -22,10 +22,22 @@ async def create_model_apikey(ns={}):
already_sync_user_key = records[0]['opc_apikey']
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
base_url = 'https://ai.atvoe.com'
url = f"{base_url}/dapi/apply_apikey.dspy"
url = f"{domain}/dapi/apply_apikey.dspy"
# 请求头
headers = {
@ -35,10 +47,11 @@ async def create_model_apikey(ns={}):
# 请求体数据
payload = {
"appname": "cn_ai_user",
"description": "",
"appname": ns.get('appname'),
"description": ns.get('description'),
}
# 正常返回的是 {'status': 'ok', 'data': {'id': 'HlEQmcbCA1dX0qjhffA_K', 'name': 'cn_ai_user', 'description': '', 'secretkey': 'QUZVcXg5V1p1STMybG5Ia4r9NHBpkeRw558aATmohvZ7GYptvg==', 'allowedips': None, 'orgid': 'KHtWKY2LENTU4hYYim1Ks'}}
try:
# 创建一个异步会话
result_sysnc = None
@ -49,7 +62,7 @@ async def create_model_apikey(ns={}):
debug(f"create_model_apikey状态码: {response.status}")
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}")
return {
'status': False,
@ -60,24 +73,22 @@ async def create_model_apikey(ns={}):
async with db.sqlorContext('kboss') as sor:
# user_api_keys表格 userid/opc_apikey
# 首先判断apikey是否存在
apikey = result_sysnc['data'][0].get('apikey')
appid = result_sysnc['data'][0].get('appid')
secretkey = result_sysnc['data'][0].get('secretkey')
remote_table_id = result_sysnc['data'].get('id')
name = result_sysnc['data'].get('name')
secretkey = result_sysnc['data'].get('secretkey')
await sor.C('user_api_keys', {
'userid': userid,
'opc_apikey': apikey,
'appid': appid,
'userid': ns['userid'],
'remote_table_id': remote_table_id,
'name': name,
'opc_apikey': 1,
'secretkey': secretkey,
'action': 'user_self_create',
'expire_time': None,
})
debug(f"sync_cn_ai_user用户{payload['user']['id']}同步成功")
return {
'status': True,
'msg': '用户同步成功'
}
return {
'status': True,
'msg': '创建模型apikey成功'
}
except Exception as e:
debug(f"sync_cn_ai_user{userid}同步用户失败: {e}")

View File

@ -23,9 +23,21 @@ async def get_model_apikey(ns={}):
already_sync_user_key = records[0]['opc_apikey']
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
base_url = 'https://ai.atvoe.com'
url = f"{base_url}/dapi/apply_apikey.dspy"
url = f"{domain}/dapi/downapps.dspy"
# 请求头
headers = {
@ -43,7 +55,7 @@ async def get_model_apikey(ns={}):
debug(f"get_model_apikey状态码: {response.status}")
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}")
return {
'status': False,
@ -54,30 +66,33 @@ async def get_model_apikey(ns={}):
async with db.sqlorContext('kboss') as sor:
# user_api_keys表格 userid/opc_apikey
# 首先判断apikey是否存在
apikey = result_sysnc['data'][0].get('apikey')
appid = result_sysnc['data'][0].get('appid')
secretkey = result_sysnc['data'][0].get('secretkey')
apikeys = result_sysnc['data']['apikeys']
# 遍历apikeys如果apikey不存在则创建 如果存在则做更新 根据userid和remote_table_id判断
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', {
'userid': userid,
'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': '用户同步成功'
}
result_sysnc['status'] = True
return result_sysnc
except Exception as e:
debug(f"sync_cn_ai_user{userid}同步用户失败: {e}")
debug(f"get_model_apikey获取模型apikey失败: {e}")
return {
'status': False,
'msg': f"sync_cn_ai_user{userid}同步用户失败: {e}"
'msg': f"get_model_apikey获取模型apikey失败: {e}"
}

View File

@ -5,32 +5,33 @@ async def get_user_balance(ns={}):
:param userid: 用户 ID
:return: 账户余额(与 getCustomerBalance 返回值一致)
"""
debug(ns)
apikey = ns.get('apikey')
userid = ns.get('userid')
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if not apikey:
return {
'status': False,
'status': 'error',
'msg': 'apikey is required'
}
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
if not userid_li:
return {
'status': False,
'status': 'error',
'msg': 'apikey无效请联系管理员'
}
# userid = userid_li[0]['userid']
user = await sor.R('users', {'id': userid})
if not user:
return {
'status': False,
'status': 'error',
'msg': '用户不存在'
}
orgid = await sor.R('organization', {'id': user[0]['orgid']})
balance = await getCustomerBalance(sor, orgid[0]['id'])
return {
'status': True,
'status': 'ok',
'balance': balance
}

View File

@ -4,7 +4,7 @@ _MODEL_FIELDS = (
'context_length', 'input_token_price', 'output_token_price',
'cache_hit_input_price', 'billing_method', 'billing_unit',
'capabilities', 'limitations', 'highlights', 'is_active',
'description', 'listing_status',
'description', 'listing_status',
)

View 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

View 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

View 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

View File

@ -4,7 +4,7 @@ _MODEL_FIELDS = (
'context_length', 'input_token_price', 'output_token_price',
'cache_hit_input_price', 'billing_method', 'billing_unit',
'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
page_size = int(ns.get('page_size', 100))
page_size = int(ns.get('page_size', 1000))
current_page = int(ns.get('current_page', 1))
offset = (current_page - 1) * page_size
conditions = ['1=1']
if 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'):
conditions.append("model_type = '%s'" % _escape(ns.get('model_type')))
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
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, {})
return {

View File

@ -26,7 +26,7 @@ async def _charge_order(sor, orderid, order_type='NEW'):
"""
order_rows = await sor.R('bz_order', {'id': orderid})
if not order_rows:
return {'status': False, 'msg': '订单不存在'}
return {'status': 'error', 'msg': '订单不存在'}
order_row = order_rows[0]
product_url = None
@ -39,7 +39,7 @@ async def _charge_order(sor, orderid, order_type='NEW'):
if count - float(order_row['amount']) < 0:
pricedifference = count - round(order_row['amount'], 2)
return {
'status': False,
'status': 'error',
'msg': '账户余额不足',
'pricedifference': round(pricedifference, 2),
}
@ -123,7 +123,7 @@ async def _charge_order(sor, orderid, order_type='NEW'):
# await sor.C('customer_goods', nss)
return {'status': True, 'msg': '支付成功'}
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):
"""
@ -136,18 +136,18 @@ async def calc_price_by_saleprotocol(sor, org, product_id, supply_price, quantit
:param quantity: 数量,默认 1
:return: dict
成功: status=True, amount(行总金额), price(折后单价), list_price, discount
失败: status=False, msg
失败: status='error', msg
"""
try:
supply_price = abs(float(supply_price))
quantity = int(quantity)
except (TypeError, ValueError):
return {'status': False, 'msg': 'supply_price / quantity 必须为有效数字'}
return {'status': 'error', 'msg': 'supply_price / quantity 必须为有效数字'}
if supply_price <= 0:
return {'status': False, 'msg': 'supply_price 必须大于 0'}
return {'status': 'error', 'msg': 'supply_price 必须大于 0'}
if quantity <= 0:
return {'status': False, 'msg': 'quantity 必须大于 0'}
return {'status': 'error', 'msg': 'quantity 必须大于 0'}
saleprotocol_to_person = await sor.R(
'saleprotocol',
@ -193,7 +193,7 @@ async def calc_price_by_saleprotocol(sor, org, product_id, supply_price, quantit
)
if not product_salemode:
return {'status': False, 'msg': '还未上线这个产品的协议配置'}
return {'status': 'error', 'msg': '还未上线这个产品的协议配置'}
discount = product_salemode[0]['discount']
list_price = supply_price
@ -217,12 +217,25 @@ async def process_user_billing(ns={}):
:param userid: 用户 ID
:param providername: 厂商名称(写入 bz_order.source并用于查 product
:param productname: 产品名称(写入 servicename并用于查 product
:param amount: 扣费金额use_saleprotocol=False 时为最终扣费额;
:param amount: 扣费金额use_saleprotocol='error' 时为最终扣费额;
use_saleprotocol=True 时为供应价/目录价(折扣前单价),走协议算价
:param use_saleprotocol: 是否启用 saleprotocol_pricing 协议折扣算价,默认 False 直接按 amount 扣费
:param use_saleprotocol: 是否启用 saleprotocol_pricing 协议折扣算价,默认 'error' 直接按 amount 扣费
:param quantity: 仅 use_saleprotocol=True 时生效,数量默认 1
: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')
userid = ns.get('userid')
providername = ns.get('providername')
@ -234,142 +247,151 @@ async def process_user_billing(ns={}):
llmid = ns.get('llmid')
if not llmid:
return {
'status': False,
'status': 'error',
'msg': 'llmid必传'
}
try:
amount = round(float(amount), 2)
except (TypeError, ValueError):
return {'status': False, 'msg': 'amount 必须为有效数字'}
return {'status': 'error', 'msg': 'amount 必须为有效数字'}
if amount <= 0:
return {'status': False, 'msg': 'amount 必须大于 0'}
return {'status': 'error', 'msg': 'amount 必须大于 0'}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'})
if not product_li:
return {
'status': False,
'msg': '未找到对应产品,请确认'
try:
product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'})
if not product_li:
return {
'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]
productname = product['name']
providerid = product['providerid']
providername_list = await sor.R('organization', {'id': providerid})
if not providername_list:
return {
'status': False,
'msg': '厂商不存在 %s' % providername
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,
}
providername = providername_list[0]['orgname']
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
if not userid_li:
return {
'status': False,
'msg': 'apikey无效请联系管理员'
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
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']
user_list = await sor.R('users', {'id': userid})
if not user_list:
return {'status': False, 'msg': '用户不存在 %s' % userid}
org_list = await sor.R('organization', {'id': user_list[0]['orgid']})
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:
if use_saleprotocol:
result['discount'] = discount
result['list_price'] = list_price
result['price'] = unit_price
return result
except Exception as e:
sor.rollback()
return {
'status': False,
'msg': '账户余额不足',
'pricedifference': round(balance - amount, 2),
'status': 'error',
'msg': str(e)
}
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)
return ret

View File

@ -23,8 +23,21 @@ async def sync_cn_ai_user(ns={}):
already_sync_user_dappid = 'cndemo'
# 目标URL
url = "https://ai.atvoe.com/rbac/usersync"
# url = 'https://ai.atvoe.com/tmp/env.dspy'
# domain 从数据库params表中获取到pname=cntoai_domain的pvalue值
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 = {