feat: 小程序客服话术表与自动回复接口
- 新增 MiniappScriptScene / MiniappScriptAutoReply 模型与迁移 - 新增 /peizhi/huashuhq、/peizhi/huashu_match 接口及 Admin 配置 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
111
config/views.py
111
config/views.py
@@ -35,7 +35,8 @@ from utils.chat_utils import subscribe_merchant_link_chat
|
||||
|
||||
from .models import (
|
||||
Gonggao, Lunbo, Tupianpeizhi, Qunpeizhi,
|
||||
ShangjiaMoban, ShangjiaLianjie, PopupPage, PopupConfig, WithdrawConfig
|
||||
ShangjiaMoban, ShangjiaLianjie, PopupPage, PopupConfig, WithdrawConfig,
|
||||
MiniappScriptScene, MiniappScriptAutoReply,
|
||||
)
|
||||
|
||||
from users.models import (
|
||||
@@ -2842,8 +2843,8 @@ class GuanshiQRCodeView(APIView):
|
||||
logger.info(f"获取到 access_token (前10位): {access_token[:10]}... club={club_id}")
|
||||
|
||||
encoded_invite = urllib.parse.quote(invite_code, safe='')
|
||||
# 打手端已合并至 fighter(TabBar);与旧版 dashouduan 同为扫码入口,fighter.js 已处理 inviteCode 自动注册
|
||||
page_path = f'pages/fighter/fighter?inviteCode={encoded_invite}'
|
||||
# 兼容页 dashouduan 跳转打手中心;fighter.js 处理 inviteCode 自动注册
|
||||
page_path = f'pages/dashouduan/dashouduan?inviteCode={encoded_invite}'
|
||||
wx_url = f'https://api.weixin.qq.com/wxa/getwxacode?access_token={access_token}'
|
||||
post_data = {
|
||||
'path': page_path,
|
||||
@@ -3078,8 +3079,8 @@ class ZuzhangHaibaoView(APIView):
|
||||
|
||||
# ===== 修改点:使用 A 接口(getwxacode) =====
|
||||
encoded_invite = urllib.parse.quote(invite_code, safe='')
|
||||
# 管事注册:打手中心 fighter.js 已支持 registerType=guanshi + inviteCode 自动注册
|
||||
page_path = f'pages/fighter/fighter?registerType=guanshi&inviteCode={encoded_invite}'
|
||||
# 兼容页 guanshiduan 跳转打手中心(管事 registerType)
|
||||
page_path = f'pages/guanshiduan/guanshiduan?inviteCode={encoded_invite}'
|
||||
|
||||
wx_resp, wx_error = self._request_wx_qrcode(page_path, access_token)
|
||||
if wx_error and is_weixin_token_invalid(wx_error.get('errcode'), wx_error.get('errmsg', '')):
|
||||
@@ -3377,4 +3378,102 @@ class CheckPhoneAuthView(APIView):
|
||||
boss = UserBoss.query.get(user=user)
|
||||
except UserBoss.DoesNotExist:
|
||||
return False
|
||||
return (boss.alldingdan or 0) > 0
|
||||
return (boss.alldingdan or 0) > 0
|
||||
|
||||
|
||||
# ---------- 小程序客服话术 / 自动回复 ----------
|
||||
|
||||
MINIAPP_SCRIPT_DEFAULTS = {
|
||||
'cs_welcome': {
|
||||
'title': '',
|
||||
'content': '你好,请问有什么可以帮到您的?',
|
||||
'confirm_text': '',
|
||||
'cancel_text': '',
|
||||
},
|
||||
'chat_image_confirm': {
|
||||
'title': '温馨提示',
|
||||
'content': '平台禁止任何违法违规行为,聊天内容仅限于商家与接单员正常业务对接。严禁赌博、诈骗及其他非法行为,违者后果自负。',
|
||||
'confirm_text': '确认',
|
||||
'cancel_text': '取消',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _resolve_script_club_id(request):
|
||||
from jituan.services.club_context import resolve_club_id_from_request
|
||||
from jituan.services.club_user import get_user_club_id
|
||||
user = getattr(request, 'user', None)
|
||||
club_id = get_user_club_id(user) if user and getattr(user, 'is_authenticated', False) else None
|
||||
if not club_id:
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
return club_id or 'xq'
|
||||
|
||||
|
||||
def _merge_script_scene(scene_key, row):
|
||||
base = dict(MINIAPP_SCRIPT_DEFAULTS.get(scene_key) or {})
|
||||
if not row:
|
||||
return base
|
||||
if row.title:
|
||||
base['title'] = row.title
|
||||
if row.content:
|
||||
base['content'] = row.content
|
||||
if row.confirm_text:
|
||||
base['confirm_text'] = row.confirm_text
|
||||
if row.cancel_text:
|
||||
base['cancel_text'] = row.cancel_text
|
||||
return base
|
||||
|
||||
|
||||
class HuashuHqView(APIView):
|
||||
"""POST /peizhi/huashuhq 批量获取场景话术"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
scene_keys = request.data.get('scene_keys') or []
|
||||
if not isinstance(scene_keys, list) or not scene_keys:
|
||||
return Response({'code': 400, 'msg': 'scene_keys 不能为空', 'data': None}, status=400)
|
||||
club_id = _resolve_script_club_id(request)
|
||||
rows = MiniappScriptScene.query.filter(
|
||||
club_id=club_id, scene_key__in=scene_keys, enabled=True,
|
||||
)
|
||||
row_map = {r.scene_key: r for r in rows}
|
||||
data = {}
|
||||
for key in scene_keys:
|
||||
if not isinstance(key, str) or not key.strip():
|
||||
continue
|
||||
data[key.strip()] = _merge_script_scene(key.strip(), row_map.get(key.strip()))
|
||||
return Response({'code': 200, 'msg': 'success', 'data': data})
|
||||
|
||||
|
||||
class HuashuMatchView(APIView):
|
||||
"""POST /peizhi/huashu_match 客服关键词自动回复匹配"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
scene_key = (request.data.get('scene_key') or '').strip()
|
||||
user_text = (request.data.get('user_text') or '').strip()
|
||||
if scene_key != 'cs_auto_reply':
|
||||
return Response({'code': 400, 'msg': '不支持的 scene_key', 'data': {'matched': False, 'content': ''}})
|
||||
if not user_text:
|
||||
return Response({'code': 200, 'msg': 'success', 'data': {'matched': False, 'content': ''}})
|
||||
club_id = _resolve_script_club_id(request)
|
||||
rules = MiniappScriptAutoReply.query.filter(
|
||||
club_id=club_id, enabled=True,
|
||||
).order_by('-sort_order', 'id')
|
||||
text_lower = user_text.lower()
|
||||
for rule in rules:
|
||||
kw = (rule.keyword or '').strip()
|
||||
if not kw:
|
||||
continue
|
||||
if kw.lower() in text_lower or kw in user_text:
|
||||
reply_type = rule.reply_type or MiniappScriptAutoReply.REPLY_TEXT
|
||||
return Response({
|
||||
'code': 200,
|
||||
'msg': 'success',
|
||||
'data': {
|
||||
'matched': True,
|
||||
'content': rule.reply_content or '',
|
||||
'reply_type': reply_type,
|
||||
},
|
||||
})
|
||||
return Response({'code': 200, 'msg': 'success', 'data': {'matched': False, 'content': ''}})
|
||||
Reference in New Issue
Block a user