feat: 商家子客服 merchant_ops 模块与专用 API
This commit is contained in:
211
merchant_ops/services/dispatch.py
Normal file
211
merchant_ops/services/dispatch.py
Normal file
@@ -0,0 +1,211 @@
|
||||
"""子客服派单(独立实现,不修改商家派单接口)。"""
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
|
||||
from backend.utils import update_shangjia_daily
|
||||
from merchant_ops.constants import ACTOR_STAFF, STAT_ACTION_DISPATCH
|
||||
from merchant_ops.models import MerchantOrderDispatch
|
||||
from merchant_ops.services.stats import update_staff_daily
|
||||
from merchant_ops.services.wallet import check_wallet_available, confirm_dispatch_deduct
|
||||
from orders.models import MerchantOrderExt, Order
|
||||
from orders.notice_tasks import dingdan_guangbo
|
||||
from orders.utils import calc_shangjia_order_fencheng
|
||||
from products.models import ShangpinLeixing
|
||||
from rank.models import Chenghao, DingdanBiaoqian
|
||||
from users.models import UserShangjia, UserDashou
|
||||
from users.business_models import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _generate_dingdan_id():
|
||||
ts = int(time.time() * 1000)
|
||||
pid = os.getpid() % 1000
|
||||
rnd = random.randint(1000, 9999)
|
||||
return f"SJ{ts}{pid:03d}{rnd}"
|
||||
|
||||
|
||||
def staff_dispatch_order(member, staff_user, data):
|
||||
"""
|
||||
子客服发单。返回 (success, payload_dict)
|
||||
payload_dict: code/msg/data 或 dingdan
|
||||
"""
|
||||
shangpin_type_id = data.get('shangpinTypeId')
|
||||
huiyuan_id = data.get('huiyuanId')
|
||||
dingdan_jieshao = str(data.get('dingdanJieshao', '')).strip()
|
||||
dingdan_beizhu = str(data.get('dingdanBeizhu', '')).strip()
|
||||
laoban_name = str(data.get('laobanName', '')).strip()
|
||||
zhiding_uid = str(data.get('zhidingUid', '')).strip()
|
||||
jiage_str = data.get('jiage')
|
||||
label_id = data.get('labelId')
|
||||
commission_enabled = data.get('commissionEnabled', False)
|
||||
commission_value_raw = data.get('commissionValue')
|
||||
commission_value_str = str(commission_value_raw).strip() if commission_value_raw is not None else ''
|
||||
|
||||
if not all([shangpin_type_id, dingdan_jieshao, laoban_name, jiage_str]):
|
||||
return False, {'code': 400, 'msg': '缺少必填参数', 'data': None}
|
||||
|
||||
try:
|
||||
jiage = float(jiage_str)
|
||||
if jiage < 1 or jiage > 10000:
|
||||
return False, {'code': 400, 'msg': '价格需在1-10000元之间', 'data': None}
|
||||
except ValueError:
|
||||
return False, {'code': 400, 'msg': '价格格式错误', 'data': None}
|
||||
|
||||
try:
|
||||
shangpin_type = ShangpinLeixing.query.get(id=shangpin_type_id)
|
||||
except ShangpinLeixing.DoesNotExist:
|
||||
return False, {'code': 400, 'msg': '商品类型不存在', 'data': None}
|
||||
|
||||
if commission_enabled and commission_value_str:
|
||||
try:
|
||||
cv = float(commission_value_str)
|
||||
if cv > 0:
|
||||
final_yaoqiu_type = 2
|
||||
final_yongjin = Decimal(str(cv))
|
||||
final_huiyuan_id = ''
|
||||
else:
|
||||
final_yaoqiu_type = shangpin_type.yaoqiuleixing or 1
|
||||
final_huiyuan_id = str(huiyuan_id) if huiyuan_id else ''
|
||||
except ValueError:
|
||||
final_yaoqiu_type = shangpin_type.yaoqiuleixing or 1
|
||||
final_huiyuan_id = str(huiyuan_id) if huiyuan_id else ''
|
||||
else:
|
||||
final_yaoqiu_type = shangpin_type.yaoqiuleixing or 1
|
||||
final_huiyuan_id = str(huiyuan_id) if huiyuan_id else ''
|
||||
|
||||
chenghao_obj = None
|
||||
label_id_str = str(label_id).strip() if label_id is not None else ''
|
||||
if label_id_str and label_id_str not in ('0', 'null', 'undefined'):
|
||||
try:
|
||||
lid = int(label_id_str)
|
||||
if lid > 0:
|
||||
chenghao_obj = Chenghao.query.filter(id=lid).first()
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
merchant_id = member.merchant_id
|
||||
try:
|
||||
shangjia = UserShangjia.query.get(user__UserUID=merchant_id)
|
||||
except UserShangjia.DoesNotExist:
|
||||
return False, {'code': 403, 'msg': '所属商家不存在', 'data': None}
|
||||
|
||||
if shangjia.zhuangtai != 1:
|
||||
return False, {'code': 403, 'msg': '商家状态异常', 'data': None}
|
||||
|
||||
if jiage > float(shangjia.yue):
|
||||
return False, {'code': 400, 'msg': f'商家余额不足,当前{shangjia.yue}元', 'data': None}
|
||||
|
||||
zhiding_dashou = None
|
||||
if zhiding_uid:
|
||||
try:
|
||||
zhiding_user = User.query.get(UserUID=zhiding_uid)
|
||||
zhiding_dashou = zhiding_user.DashouProfile
|
||||
if zhiding_dashou.zhanghaozhuangtai != 1:
|
||||
return False, {'code': 400, 'msg': '指定打手状态异常', 'data': None}
|
||||
except User.DoesNotExist:
|
||||
return False, {'code': 400, 'msg': '指定用户不存在', 'data': None}
|
||||
except UserDashou.DoesNotExist:
|
||||
return False, {'code': 400, 'msg': '指定用户不是打手', 'data': None}
|
||||
|
||||
dashou_fencheng, guanshi_fencheng = calc_shangjia_order_fencheng(jiage)
|
||||
merchant_user = User.query.get(UserUID=merchant_id)
|
||||
|
||||
dingdan = None
|
||||
try:
|
||||
with transaction.atomic():
|
||||
check_wallet_available(member, jiage)
|
||||
|
||||
dingdan = Order.query.create(
|
||||
OrderID=_generate_dingdan_id(),
|
||||
Status=7 if zhiding_dashou else 1,
|
||||
Platform=2,
|
||||
Amount=jiage,
|
||||
PlayerCommission=dashou_fencheng,
|
||||
ManagerCommission=guanshi_fencheng,
|
||||
AssignedID=zhiding_uid if zhiding_dashou else '',
|
||||
ProductID=0,
|
||||
ProductTypeID=int(shangpin_type_id),
|
||||
GrabRequirement=final_yaoqiu_type,
|
||||
CommissionReq=final_yongjin,
|
||||
MembershipID=final_huiyuan_id,
|
||||
Description=dingdan_jieshao[:2000],
|
||||
Remark=dingdan_beizhu[:1000],
|
||||
Nickname=laoban_name[:50],
|
||||
ImageURL=merchant_user.Avatar if merchant_user else '',
|
||||
User1ID=f"Sj{merchant_id}",
|
||||
)
|
||||
|
||||
MerchantOrderExt.query.create(
|
||||
Order=dingdan,
|
||||
MerchantID=merchant_id,
|
||||
MerchantNickname=shangjia.nicheng,
|
||||
)
|
||||
|
||||
if chenghao_obj:
|
||||
DingdanBiaoqian.query.create(dingdan=dingdan, chenghao=chenghao_obj)
|
||||
|
||||
UserShangjia.query.filter(id=shangjia.id).update(
|
||||
fabu=F('fabu') + 1,
|
||||
yue=F('yue') - Decimal(str(jiage)),
|
||||
jinridingdan=F('jinridingdan') + 1,
|
||||
jinriliushui=F('jinriliushui') + Decimal(str(jiage)),
|
||||
jinyuedingdan=F('jinyuedingdan') + 1,
|
||||
jinyueliushui=F('jinyueliushui') + Decimal(str(jiage)),
|
||||
)
|
||||
|
||||
update_shangjia_daily(merchant_id, Decimal(str(jiage)), STAT_ACTION_DISPATCH)
|
||||
update_staff_daily(
|
||||
merchant_id, member.staff_user_id, member.id, jiage, STAT_ACTION_DISPATCH,
|
||||
)
|
||||
|
||||
MerchantOrderDispatch.objects.create(
|
||||
order_id=dingdan.OrderID,
|
||||
merchant_id=merchant_id,
|
||||
dispatch_actor_type=ACTOR_STAFF,
|
||||
dispatch_actor_id=member.staff_user_id,
|
||||
staff_member_id=member.id,
|
||||
)
|
||||
|
||||
confirm_dispatch_deduct(member, jiage, dingdan.OrderID, member.staff_user_id)
|
||||
|
||||
from merchant_ops.models import MerchantStaffMember
|
||||
MerchantStaffMember.query.filter(id=member.id).update(
|
||||
today_dispatch_count=F('today_dispatch_count') + 1,
|
||||
today_dispatch_amount=F('today_dispatch_amount') + Decimal(str(jiage)),
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
return False, {'code': 400, 'msg': str(e), 'data': None}
|
||||
except Exception as e:
|
||||
logger.error(f'子客服派单失败: {e}', exc_info=True)
|
||||
return False, {'code': 500, 'msg': '订单创建失败', 'data': None}
|
||||
|
||||
try:
|
||||
from orders.views import ShangjiaPaifaView
|
||||
view = ShangjiaPaifaView()
|
||||
game_type = view._get_game_type_name(dingdan.ProductTypeID)
|
||||
except Exception:
|
||||
game_type = '游戏订单'
|
||||
|
||||
try:
|
||||
dingdan_guangbo.delay({
|
||||
'OrderID': dingdan.OrderID,
|
||||
'game_type': game_type,
|
||||
'amount': str(dingdan.Amount),
|
||||
'order_desc': (dingdan.Description or '')[:50],
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f'广播任务失败: {e}')
|
||||
|
||||
return True, {
|
||||
'code': 200,
|
||||
'msg': '派单成功',
|
||||
'data': {'dingdan_id': dingdan.OrderID, 'jine': str(dingdan.Amount)},
|
||||
}
|
||||
Reference in New Issue
Block a user