update
This commit is contained in:
parent
d8968afe83
commit
314aaa9d85
@ -11,7 +11,7 @@ async def get_deerer_header(ns={}):
|
||||
}
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('kboss') as sor:
|
||||
records = await sor.R('user_api_keys', {'userid': userid})
|
||||
records = await sor.R('user_api_keys', {'userid': userid, 'action': 'sync'})
|
||||
if not records:
|
||||
return {
|
||||
'status': False,
|
||||
|
||||
33
b/cntoai/get_user_balance.dspy
Normal file
33
b/cntoai/get_user_balance.dspy
Normal file
@ -0,0 +1,33 @@
|
||||
async def get_user_balance(ns={}):
|
||||
"""
|
||||
根据 userid 查询对应机构的客户余额。
|
||||
|
||||
:param userid: 用户 ID
|
||||
:return: 账户余额(与 getCustomerBalance 返回值一致)
|
||||
"""
|
||||
apikey = ns.get('apikey')
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('kboss') as sor:
|
||||
if not apikey:
|
||||
return {
|
||||
'status': False,
|
||||
'msg': 'apikey is required'
|
||||
}
|
||||
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
|
||||
if not userid_li:
|
||||
return {
|
||||
'status': False,
|
||||
'msg': '用户未同步到系统'
|
||||
}
|
||||
userid = userid_li[0]['userid']
|
||||
user = await sor.R('users', {'id': userid})
|
||||
orgid = await sor.R('organization', {'id': user[0]['orgid']})
|
||||
balance = await getCustomerBalance(sor, orgid[0]['id'])
|
||||
return {
|
||||
'status': True,
|
||||
'balance': balance
|
||||
}
|
||||
|
||||
|
||||
ret = await get_user_balance(params_kw)
|
||||
return ret
|
||||
354
b/cntoai/process_user_billing.dspy
Normal file
354
b/cntoai/process_user_billing.dspy
Normal file
@ -0,0 +1,354 @@
|
||||
async def _lookup_product(sor, providername, productname):
|
||||
"""
|
||||
按厂商名 + 产品名解析 product 记录。
|
||||
依次尝试:provider.name + product.name → providerpid → 仅 product.name
|
||||
"""
|
||||
provider_list = await sor.R('provider', {'name': providername, 'del_flg': '0'})
|
||||
if provider_list:
|
||||
product_list = await sor.R(
|
||||
'product',
|
||||
{'name': productname, 'providerid': provider_list[0]['orgid'], 'del_flg': '0'},
|
||||
)
|
||||
if product_list:
|
||||
return product_list[0]
|
||||
|
||||
product_list = await sor.R('product', {'name': productname, 'providerid': provider_list[0]['orgid'], 'del_flg': '0'})
|
||||
if product_list:
|
||||
return product_list[0]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def _charge_order(sor, orderid, order_type='NEW'):
|
||||
"""
|
||||
确认支付:校验余额 → order2bill → BillAccounting → 更新订单/账单 → customer_goods。
|
||||
逻辑来自 get_baidu_orderlist.dspy 的 affirmbz_order。
|
||||
"""
|
||||
order_rows = await sor.R('bz_order', {'id': orderid})
|
||||
if not order_rows:
|
||||
return {'status': False, 'msg': '订单不存在'}
|
||||
|
||||
order_row = order_rows[0]
|
||||
product_url = None
|
||||
|
||||
await get_business_date(sor=None)
|
||||
|
||||
count = await getCustomerBalance(sor, order_row['customerid'])
|
||||
if count is None:
|
||||
count = 0
|
||||
if count - float(order_row['amount']) < 0:
|
||||
pricedifference = count - round(order_row['amount'], 2)
|
||||
return {
|
||||
'status': False,
|
||||
'msg': '账户余额不足',
|
||||
'pricedifference': round(pricedifference, 2),
|
||||
}
|
||||
|
||||
await order2bill(orderid, sor)
|
||||
bills = await sor.R('bill', {'orderid': orderid, 'del_flg': '0'})
|
||||
try:
|
||||
for bill in bills:
|
||||
ba = BillAccounting(bill)
|
||||
await ba.accounting(sor)
|
||||
dates = datetime.datetime.now()
|
||||
await sor.U('bz_order', {'id': orderid, 'order_status': '1', 'create_at': dates})
|
||||
await sor.U('bill', {'id': orderid, 'bill_state': '1'})
|
||||
|
||||
# 暂时不处理customer_goods
|
||||
# order_goods = await sor.R('order_goods', {'orderid': orderid})
|
||||
# for item in order_goods:
|
||||
# if order_type == 'REFUND':
|
||||
# resource_find_sql = (
|
||||
# "select id from customer_goods where resourceid = '%s';"
|
||||
# % item['resourceids']
|
||||
# )
|
||||
# resource_find_li = await sor.sqlExe(resource_find_sql, {})
|
||||
# resource_find_id = resource_find_li[0]['id']
|
||||
# await sor.U('customer_goods', {'id': resource_find_id, 'del_flg': '1'})
|
||||
# elif order_type == 'RENEW':
|
||||
# resource_find_sql = (
|
||||
# "select id from customer_goods where FIND_IN_SET('%s', resourceid) and del_flg = '0';"
|
||||
# % item['resourceids']
|
||||
# )
|
||||
# resource_find_li = await sor.sqlExe(resource_find_sql, {})
|
||||
# resource_find_id = resource_find_li[0]['id']
|
||||
# await sor.U(
|
||||
# 'customer_goods',
|
||||
# {
|
||||
# 'id': resource_find_id,
|
||||
# 'start_date': item['resourcestarttime'],
|
||||
# 'expire_date': item['resourceendtime'],
|
||||
# },
|
||||
# )
|
||||
# else:
|
||||
# if item.get('chargemode') == 'postpay' and item.get('orderkey') == 'snapshot':
|
||||
# continue
|
||||
|
||||
# product = await sor.R('product', {'id': item['productid']})
|
||||
# now_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
# nss = {
|
||||
# 'id': uuid(),
|
||||
# 'providerrid': product[0]['providerid'],
|
||||
# 'productname': product[0]['name'],
|
||||
# 'productdesc': product[0]['description'],
|
||||
# 'customerid': order_row['customerid'],
|
||||
# 'productid': product[0]['id'],
|
||||
# 'specdataid': item.get('spec_id'),
|
||||
# 'orderid': order_row['id'],
|
||||
# 'start_date': item.get('resourcestarttime') or now_str,
|
||||
# 'expire_date': item.get('resourceendtime'),
|
||||
# 'resourceid': item.get('resourceids') or '',
|
||||
# 'orderkey': item.get('orderkey'),
|
||||
# }
|
||||
|
||||
# if product_url:
|
||||
# nss['product_url'] = product_url
|
||||
# else:
|
||||
# spec = (
|
||||
# json.loads(product[0]['spec_note'])
|
||||
# if isinstance(product[0]['spec_note'], str)
|
||||
# else product[0]['spec_note']
|
||||
# )
|
||||
# spec_list_url = [
|
||||
# spec_item['value']
|
||||
# for spec_item in (spec or [])
|
||||
# if spec_item.get('configName') == 'listUrl'
|
||||
# ]
|
||||
# nss['product_url'] = (
|
||||
# spec_list_url[0]
|
||||
# if spec_list_url
|
||||
# else 'https://console.vcp.baidu.com/bcc/#/bcc/instance/list'
|
||||
# )
|
||||
|
||||
# await sor.C('customer_goods', nss)
|
||||
return {'status': True, 'msg': '支付成功'}
|
||||
except Exception as error:
|
||||
return {'status': False, 'msg': str(error)}
|
||||
|
||||
async def calc_price_by_saleprotocol(sor, org, product_id, supply_price, quantity=1):
|
||||
"""
|
||||
查 saleprotocol、product_salemode,按折扣计算应付金额。
|
||||
|
||||
:param sor: sqlor 上下文(kboss)
|
||||
:param org: organization 记录,须含 id、parentid
|
||||
:param product_id: product 表主键
|
||||
:param supply_price: 供应价/目录价(折扣前单价,与百度脚本 catalogPrice / itemFee.price 同义)
|
||||
:param quantity: 数量,默认 1
|
||||
:return: dict
|
||||
成功: status=True, amount(行总金额), price(折后单价), list_price, discount
|
||||
失败: status=False, msg
|
||||
"""
|
||||
try:
|
||||
supply_price = abs(float(supply_price))
|
||||
quantity = int(quantity)
|
||||
except (TypeError, ValueError):
|
||||
return {'status': False, 'msg': 'supply_price / quantity 必须为有效数字'}
|
||||
|
||||
if supply_price <= 0:
|
||||
return {'status': False, 'msg': 'supply_price 必须大于 0'}
|
||||
if quantity <= 0:
|
||||
return {'status': False, 'msg': 'quantity 必须大于 0'}
|
||||
|
||||
saleprotocol_to_person = await sor.R(
|
||||
'saleprotocol',
|
||||
{'bid_orgid': org['id'], 'offer_orgid': org['parentid'], 'del_flg': '0'},
|
||||
)
|
||||
saleprotocol_to_all = await sor.R(
|
||||
'saleprotocol',
|
||||
{
|
||||
'bid_orgid': '*',
|
||||
'offer_orgid': org['parentid'],
|
||||
'del_flg': '0',
|
||||
'salemode': '0',
|
||||
},
|
||||
)
|
||||
|
||||
product_salemode = None
|
||||
if saleprotocol_to_person:
|
||||
product_salemode = await sor.R(
|
||||
'product_salemode',
|
||||
{
|
||||
'protocolid': saleprotocol_to_person[0]['id'],
|
||||
'productid': product_id,
|
||||
'del_flg': '0',
|
||||
},
|
||||
)
|
||||
if not product_salemode and saleprotocol_to_all:
|
||||
product_salemode = await sor.R(
|
||||
'product_salemode',
|
||||
{
|
||||
'protocolid': saleprotocol_to_all[0]['id'],
|
||||
'productid': product_id,
|
||||
'del_flg': '0',
|
||||
},
|
||||
)
|
||||
elif saleprotocol_to_all:
|
||||
product_salemode = await sor.R(
|
||||
'product_salemode',
|
||||
{
|
||||
'protocolid': saleprotocol_to_all[0]['id'],
|
||||
'productid': product_id,
|
||||
'del_flg': '0',
|
||||
},
|
||||
)
|
||||
|
||||
if not product_salemode:
|
||||
return {'status': False, 'msg': '还未上线这个产品的协议配置'}
|
||||
|
||||
discount = product_salemode[0]['discount']
|
||||
list_price = supply_price
|
||||
price = abs(round(list_price * discount, 2))
|
||||
amount = abs(round(price * quantity, 2))
|
||||
|
||||
return {
|
||||
'status': True,
|
||||
'amount': amount,
|
||||
'price': price,
|
||||
'list_price': list_price,
|
||||
'discount': discount,
|
||||
'protocolid': product_salemode[0]['protocolid'],
|
||||
'product_salemode_id': product_salemode[0].get('id'),
|
||||
}
|
||||
|
||||
async def process_user_billing(ns={}):
|
||||
"""
|
||||
通用记账扣费:创建本地订单 → 校验余额 → 出账记账。
|
||||
|
||||
:param userid: 用户 ID
|
||||
:param providername: 厂商名称(写入 bz_order.source,并用于查 product)
|
||||
:param productname: 产品名称(写入 servicename,并用于查 product)
|
||||
:param amount: 扣费金额;use_saleprotocol=False 时为最终扣费额;
|
||||
use_saleprotocol=True 时为供应价/目录价(折扣前单价),走协议算价
|
||||
:param use_saleprotocol: 是否启用 saleprotocol_pricing 协议折扣算价,默认 False 直接按 amount 扣费
|
||||
:param quantity: 仅 use_saleprotocol=True 时生效,数量默认 1
|
||||
:return: dict,含 status、msg;成功时含 orderid、amount
|
||||
"""
|
||||
apikey = ns.get('apikey')
|
||||
providername = ns.get('providername')
|
||||
productname = ns.get('productname')
|
||||
amount = ns.get('amount')
|
||||
use_saleprotocol = ns.get('use_saleprotocol', False)
|
||||
quantity = int(ns.get('quantity', 1))
|
||||
|
||||
userid_li = await sor.R('user_api_keys', {'opc_apikey': apikey})
|
||||
if not userid_li:
|
||||
return {
|
||||
'status': False,
|
||||
'msg': '用户未同步到系统'
|
||||
}
|
||||
userid = userid_li[0]['userid']
|
||||
|
||||
try:
|
||||
amount = round(float(amount), 2)
|
||||
except (TypeError, ValueError):
|
||||
return {'status': False, 'msg': 'amount 必须为有效数字'}
|
||||
|
||||
if amount <= 0:
|
||||
return {'status': False, 'msg': 'amount 必须大于 0'}
|
||||
|
||||
db = DBPools()
|
||||
async with db.sqlorContext('kboss') as sor:
|
||||
provider_list = await sor.R('provider', {'name': providername})
|
||||
if not provider_list:
|
||||
return {'status': False, 'msg': '厂商不存在 %s' % providername}
|
||||
|
||||
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:
|
||||
return {
|
||||
'status': False,
|
||||
'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,
|
||||
}
|
||||
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
|
||||
@ -65,6 +65,7 @@ async def sync_cn_ai_user(userid=None, orgid=None, username=None, name=None, ema
|
||||
'opc_apikey': apikey,
|
||||
'appid': appid,
|
||||
'secretkey': secretkey,
|
||||
'action': 'sync',
|
||||
'expire_time': None,
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user