backup: Native扫码支付设计前 django 代码快照

保留当前后端工作区已改代码,便于扫码支付开发前回滚对照。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQueandCursor committed 2026-08-08 17:00:53 +08:00
1 parent fb2cfbcfc9
commit 2643321801
4 files changed
+385 -406

No files matched your search

+365 -365
View File
@@ -1,365 +1,365 @@
import io
import os
import re
import time
import random
import secrets
import hashlib
import threading
import urllib.parse
from decimal import Decimal, InvalidOperation
from django.conf import settings
from django.db import transaction, connection
from django.db.models import Q, F, Max, Prefetch
from gvsdsdk.fluent import db, func, FQ
from django.core.cache import cache
from django.core.paginator import Paginator
from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, permissions
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from rest_framework.throttling import AnonRateThrottle, SimpleRateThrottle
from utils.oss_utils import upload_to_oss, delete_from_oss, validate_image
from utils.weixin_broadcast import WeixinBroadcastSender
from utils.weixin_token import get_weixin_mini_access_token, is_weixin_token_invalid
# from utils.ip_security import *
from utils.invitationcode_utils import CreateInvitationCode, VerifyInvitationCode
from backend.utils import update_shangjia_daily
from utils.chat_utils import subscribe_merchant_link_chat
from utils.pdd_order_validator import validate_pdd_order_id, validate_cn_mobile
from ..models import (
Gonggao, Lunbo, Tupianpeizhi, Qunpeizhi,
ShangjiaMoban, ShangjiaLianjie, PopupPage, PopupConfig, WithdrawConfig,
MiniappScriptScene, MiniappScriptAutoReply,
)
from users.models import (
AdminProfile, UserDashou, UserBoss,
UserShangjia, UserGuanshi, UserZuzhang
)
from users.business_models import User
from orders.models import CommissionRate, Order, MerchantOrderExt
from orders.notice_tasks import dingdan_guangbo
from rank.models import Chenghao, DingdanBiaoqian
from products.models import ShangpinLeixing, Huiyuangoumai
from ..serializers import PopupConfigSerializer
import traceback
import requests
import logging
logger = logging.getLogger(__name__)
# 小程序客服话术 / 自动回复默认值(原位于 miniapp_config.py,按功能组归类到话术模块)
MINIAPP_SCRIPT_DEFAULTS = {
'cs_welcome': {
'title': '',
'content': '你好,请问有什么可以帮到您的?',
'confirm_text': '',
'cancel_text': '',
},
'chat_image_confirm': {
'title': '温馨提示',
'content': '平台禁止任何违法违规行为,聊天内容仅限于商家与接单员正常业务对接。严禁赌博、诈骗及其他非法行为,违者后果自负。',
'confirm_text': '确认',
'cancel_text': '取消',
},
'merchant_dispatch_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_raw = (rule.keyword or '').strip()
if not kw_raw:
continue
keywords = [k.strip() for k in kw_raw.split(',') if k.strip()]
for kw in keywords:
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': ''}})
# ---------- 后台:客服话术配置(/houtai/hthqhs、/houtai/htxghs) ----------
SCRIPT_CONFIG_PERMS = ('kfhs666', '8080a')
SCRIPT_SCENE_KEYS = ('chat_image_confirm', 'cs_welcome', 'merchant_dispatch_confirm')
def _has_script_config_perm(permissions):
return any(code in permissions for code in SCRIPT_CONFIG_PERMS)
def _script_scene_item_type(scene_key):
return 'text_display' if scene_key == 'cs_welcome' else 'confirm_modal'
def _serialize_script_scene(row):
return {
'id': row.id,
'scene_key': row.scene_key,
'item_type': _script_scene_item_type(row.scene_key),
'title': row.title or '',
'content': row.content or '',
'confirm_text': row.confirm_text or '',
'cancel_text': row.cancel_text or '',
'is_active': bool(row.enabled),
}
def _serialize_script_auto_reply(row):
keywords = [k.strip() for k in (row.keyword or '').split(',') if k.strip()]
return {
'id': row.id,
'scene_key': 'cs_auto_reply',
'item_type': 'auto_reply',
'title': '',
'keywords': keywords,
'match_mode': 'contains',
'priority': row.sort_order or 0,
'content': row.reply_content or '',
'is_active': bool(row.enabled),
}
class MiniappScriptHoutaiListView(APIView):
"""POST /houtai/hthqhs 后台获取话术与自动回复配置"""
permission_classes = [IsAuthenticated]
def post(self, request):
from backend.utils import verify_kefu_permission
username = request.data.get('username')
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return Response({'code': 403, 'msg': '身份验证失败,请检查登录状态'})
if not _has_script_config_perm(permissions):
return Response({'code': 403, 'msg': '您没有权限访问客服聊天配置'})
club_id = _resolve_script_club_id(request)
scenes = MiniappScriptScene.query.filter(
club_id=club_id, scene_key__in=SCRIPT_SCENE_KEYS,
).order_by('scene_key')
auto_rules = MiniappScriptAutoReply.query.filter(club_id=club_id).order_by('-sort_order', 'id')
items = [_serialize_script_scene(r) for r in scenes]
items.extend(_serialize_script_auto_reply(r) for r in auto_rules)
return Response({'code': 0, 'data': {'items': items, 'club_id': club_id}})
class MiniappScriptHoutaiModifyView(APIView):
"""POST /houtai/htxghs 后台增删改话术与自动回复"""
permission_classes = [IsAuthenticated]
def post(self, request):
from backend.utils import verify_kefu_permission
from jituan.services.display_config import forbid_display_write_in_all_scope
username = request.data.get('username')
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return Response({'code': 403, 'msg': '身份验证失败,请检查登录状态'})
if not _has_script_config_perm(permissions):
return Response({'code': 403, 'msg': '您没有权限操作客服聊天配置'})
action = (request.data.get('action') or '').strip()
if not action:
return Response({'code': 400, 'msg': '缺少 action 参数'})
club_id = _resolve_script_club_id(request)
if action == 'delete':
return self._delete_item(request, club_id)
deny = forbid_display_write_in_all_scope(request)
if deny:
return deny
if action in ('create', 'update'):
item_type = (request.data.get('item_type') or '').strip()
if item_type == 'auto_reply':
return self._save_auto_reply(request, club_id, action)
return self._save_scene(request, club_id, action)
return Response({'code': 400, 'msg': f'未知的 action: {action}'})
def _save_scene(self, request, club_id, action):
scene_key = (request.data.get('scene_key') or '').strip()
if scene_key not in SCRIPT_SCENE_KEYS:
return Response({'code': 400, 'msg': '不支持的 scene_key'})
content = (request.data.get('content') or '').strip()
if not content:
return Response({'code': 400, 'msg': '请填写正文内容'})
fields = {
'title': (request.data.get('title') or '').strip(),
'content': content,
'confirm_text': (request.data.get('confirm_text') or '').strip(),
'cancel_text': (request.data.get('cancel_text') or '').strip(),
'enabled': bool(request.data.get('is_active', True)),
}
if action == 'update':
row_id = request.data.get('id')
if not row_id:
return Response({'code': 400, 'msg': '缺少 id'})
row = MiniappScriptScene.query.filter(id=row_id, club_id=club_id).first()
if not row:
return Response({'code': 404, 'msg': '配置不存在'})
for k, v in fields.items():
setattr(row, k, v)
row.save()
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_script_scene(row)})
existing = MiniappScriptScene.query.filter(club_id=club_id, scene_key=scene_key).first()
if existing:
for k, v in fields.items():
setattr(existing, k, v)
existing.save()
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_script_scene(existing)})
row = MiniappScriptScene.query.create(club_id=club_id, scene_key=scene_key, **fields)
return Response({'code': 0, 'msg': '创建成功', 'data': _serialize_script_scene(row)})
def _save_auto_reply(self, request, club_id, action):
keywords = request.data.get('keywords') or []
if not isinstance(keywords, list) or not keywords:
return Response({'code': 400, 'msg': '请至少添加一个触发关键词'})
kw_list = [str(k).strip() for k in keywords if str(k).strip()]
if not kw_list:
return Response({'code': 400, 'msg': '请至少添加一个触发关键词'})
content = (request.data.get('content') or '').strip()
if not content:
return Response({'code': 400, 'msg': '请填写回复内容'})
fields = {
'keyword': ','.join(kw_list),
'reply_content': content,
'sort_order': int(request.data.get('priority') or 0),
'enabled': bool(request.data.get('is_active', True)),
'reply_type': MiniappScriptAutoReply.REPLY_TEXT,
}
if action == 'update':
row_id = request.data.get('id')
if not row_id:
return Response({'code': 400, 'msg': '缺少 id'})
row = MiniappScriptAutoReply.query.filter(id=row_id, club_id=club_id).first()
if not row:
return Response({'code': 404, 'msg': '规则不存在'})
for k, v in fields.items():
setattr(row, k, v)
row.save()
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_script_auto_reply(row)})
row = MiniappScriptAutoReply.query.create(club_id=club_id, **fields)
return Response({'code': 0, 'msg': '创建成功', 'data': _serialize_script_auto_reply(row)})
def _delete_item(self, request, club_id):
from jituan.services.display_config import forbid_display_write_in_all_scope
deny = forbid_display_write_in_all_scope(request)
if deny:
return deny
row_id = request.data.get('id')
if not row_id:
return Response({'code': 400, 'msg': '缺少 id'})
row = MiniappScriptAutoReply.query.filter(id=row_id, club_id=club_id).first()
if row:
row.delete()
return Response({'code': 0, 'msg': '删除成功'})
row = MiniappScriptScene.query.filter(id=row_id, club_id=club_id).first()
if row:
return Response({'code': 400, 'msg': '场景话术不支持删除,请改为禁用'})
return Response({'code': 404, 'msg': '记录不存在'})
import io
import os
import re
import time
import random
import secrets
import hashlib
import threading
import urllib.parse
from decimal import Decimal, InvalidOperation
from django.conf import settings
from django.db import transaction, connection
from django.db.models import Q, F, Max, Prefetch
from gvsdsdk.fluent import db, func, FQ
from django.core.cache import cache
from django.core.paginator import Paginator
from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, permissions
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from rest_framework.throttling import AnonRateThrottle, SimpleRateThrottle
from utils.oss_utils import upload_to_oss, delete_from_oss, validate_image
from utils.weixin_broadcast import WeixinBroadcastSender
from utils.weixin_token import get_weixin_mini_access_token, is_weixin_token_invalid
# from utils.ip_security import *
from utils.invitationcode_utils import CreateInvitationCode, VerifyInvitationCode
from backend.utils import update_shangjia_daily
from utils.chat_utils import subscribe_merchant_link_chat
from utils.pdd_order_validator import validate_pdd_order_id, validate_cn_mobile
from ..models import (
Gonggao, Lunbo, Tupianpeizhi, Qunpeizhi,
ShangjiaMoban, ShangjiaLianjie, PopupPage, PopupConfig, WithdrawConfig,
MiniappScriptScene, MiniappScriptAutoReply,
)
from users.models import (
AdminProfile, UserDashou, UserBoss,
UserShangjia, UserGuanshi, UserZuzhang
)
from users.business_models import User
from orders.models import CommissionRate, Order, MerchantOrderExt
from orders.notice_tasks import dingdan_guangbo
from rank.models import Chenghao, DingdanBiaoqian
from products.models import ShangpinLeixing, Huiyuangoumai
from ..serializers import PopupConfigSerializer
import traceback
import requests
import logging
logger = logging.getLogger(__name__)
# 小程序客服话术 / 自动回复默认值(原位于 miniapp_config.py,按功能组归类到话术模块)
MINIAPP_SCRIPT_DEFAULTS = {
'cs_welcome': {
'title': '',
'content': '你好,请问有什么可以帮到您的?',
'confirm_text': '',
'cancel_text': '',
},
'chat_image_confirm': {
'title': '温馨提示',
'content': '平台禁止任何违法违规行为,聊天内容仅限于商家与接单员正常业务对接。严禁赌博、诈骗及其他非法行为,违者后果自负。',
'confirm_text': '确认',
'cancel_text': '取消',
},
'merchant_dispatch_confirm': {
'title': '确认派单',
'content': '确认向该打手派发此订单?',
'confirm_text': '确认',
'cancel_text': '取消',
},
}
def _resolve_script_club_id(request):
"""
话术/自动回复按俱乐部隔离。
优先顶栏/小程序 Header X-Club-Id,再用户归属店,最后默认 xq。
(旧逻辑先取 User.ClubID,后台切俱乐部后仍读错店,界面空得像「未加载」。)
"""
from jituan.services.club_context import resolve_effective_club_id
return resolve_effective_club_id(request, getattr(request, 'user', None)) 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_raw = (rule.keyword or '').strip()
if not kw_raw:
continue
keywords = [k.strip() for k in kw_raw.split(',') if k.strip()]
for kw in keywords:
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': ''}})
# ---------- 后台:客服话术配置(/houtai/hthqhs、/houtai/htxghs) ----------
SCRIPT_CONFIG_PERMS = ('kfhs666', '8080a')
SCRIPT_SCENE_KEYS = ('chat_image_confirm', 'cs_welcome', 'merchant_dispatch_confirm')
def _has_script_config_perm(permissions):
return any(code in permissions for code in SCRIPT_CONFIG_PERMS)
def _script_scene_item_type(scene_key):
return 'text_display' if scene_key == 'cs_welcome' else 'confirm_modal'
def _serialize_script_scene(row):
return {
'id': row.id,
'scene_key': row.scene_key,
'item_type': _script_scene_item_type(row.scene_key),
'title': row.title or '',
'content': row.content or '',
'confirm_text': row.confirm_text or '',
'cancel_text': row.cancel_text or '',
'is_active': bool(row.enabled),
}
def _serialize_script_auto_reply(row):
keywords = [k.strip() for k in (row.keyword or '').split(',') if k.strip()]
return {
'id': row.id,
'scene_key': 'cs_auto_reply',
'item_type': 'auto_reply',
'title': '',
'keywords': keywords,
'match_mode': 'contains',
'priority': row.sort_order or 0,
'content': row.reply_content or '',
'is_active': bool(row.enabled),
}
class MiniappScriptHoutaiListView(APIView):
"""POST /houtai/hthqhs 后台获取话术与自动回复配置"""
permission_classes = [IsAuthenticated]
def post(self, request):
from backend.utils import verify_kefu_permission
username = request.data.get('username')
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return Response({'code': 403, 'msg': '身份验证失败,请检查登录状态'})
if not _has_script_config_perm(permissions):
return Response({'code': 403, 'msg': '您没有权限访问客服聊天配置'})
club_id = _resolve_script_club_id(request)
scenes = MiniappScriptScene.query.filter(
club_id=club_id, scene_key__in=SCRIPT_SCENE_KEYS,
).order_by('scene_key')
auto_rules = MiniappScriptAutoReply.query.filter(club_id=club_id).order_by('-sort_order', 'id')
items = [_serialize_script_scene(r) for r in scenes]
items.extend(_serialize_script_auto_reply(r) for r in auto_rules)
return Response({'code': 0, 'data': {'items': items, 'club_id': club_id}})
class MiniappScriptHoutaiModifyView(APIView):
"""POST /houtai/htxghs 后台增删改话术与自动回复"""
permission_classes = [IsAuthenticated]
def post(self, request):
from backend.utils import verify_kefu_permission
from jituan.services.display_config import forbid_display_write_in_all_scope
username = request.data.get('username')
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return Response({'code': 403, 'msg': '身份验证失败,请检查登录状态'})
if not _has_script_config_perm(permissions):
return Response({'code': 403, 'msg': '您没有权限操作客服聊天配置'})
action = (request.data.get('action') or '').strip()
if not action:
return Response({'code': 400, 'msg': '缺少 action 参数'})
club_id = _resolve_script_club_id(request)
if action == 'delete':
return self._delete_item(request, club_id)
deny = forbid_display_write_in_all_scope(request)
if deny:
return deny
if action in ('create', 'update'):
item_type = (request.data.get('item_type') or '').strip()
if item_type == 'auto_reply':
return self._save_auto_reply(request, club_id, action)
return self._save_scene(request, club_id, action)
return Response({'code': 400, 'msg': f'未知的 action: {action}'})
def _save_scene(self, request, club_id, action):
scene_key = (request.data.get('scene_key') or '').strip()
if scene_key not in SCRIPT_SCENE_KEYS:
return Response({'code': 400, 'msg': '不支持的 scene_key'})
content = (request.data.get('content') or '').strip()
if not content:
return Response({'code': 400, 'msg': '请填写正文内容'})
fields = {
'title': (request.data.get('title') or '').strip(),
'content': content,
'confirm_text': (request.data.get('confirm_text') or '').strip(),
'cancel_text': (request.data.get('cancel_text') or '').strip(),
'enabled': bool(request.data.get('is_active', True)),
}
if action == 'update':
row_id = request.data.get('id')
if not row_id:
return Response({'code': 400, 'msg': '缺少 id'})
row = MiniappScriptScene.query.filter(id=row_id, club_id=club_id).first()
if not row:
return Response({'code': 404, 'msg': '配置不存在'})
for k, v in fields.items():
setattr(row, k, v)
row.save()
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_script_scene(row)})
existing = MiniappScriptScene.query.filter(club_id=club_id, scene_key=scene_key).first()
if existing:
for k, v in fields.items():
setattr(existing, k, v)
existing.save()
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_script_scene(existing)})
row = MiniappScriptScene.query.create(club_id=club_id, scene_key=scene_key, **fields)
return Response({'code': 0, 'msg': '创建成功', 'data': _serialize_script_scene(row)})
def _save_auto_reply(self, request, club_id, action):
keywords = request.data.get('keywords') or []
if not isinstance(keywords, list) or not keywords:
return Response({'code': 400, 'msg': '请至少添加一个触发关键词'})
kw_list = [str(k).strip() for k in keywords if str(k).strip()]
if not kw_list:
return Response({'code': 400, 'msg': '请至少添加一个触发关键词'})
content = (request.data.get('content') or '').strip()
if not content:
return Response({'code': 400, 'msg': '请填写回复内容'})
fields = {
'keyword': ','.join(kw_list),
'reply_content': content,
'sort_order': int(request.data.get('priority') or 0),
'enabled': bool(request.data.get('is_active', True)),
'reply_type': MiniappScriptAutoReply.REPLY_TEXT,
}
if action == 'update':
row_id = request.data.get('id')
if not row_id:
return Response({'code': 400, 'msg': '缺少 id'})
row = MiniappScriptAutoReply.query.filter(id=row_id, club_id=club_id).first()
if not row:
return Response({'code': 404, 'msg': '规则不存在'})
for k, v in fields.items():
setattr(row, k, v)
row.save()
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_script_auto_reply(row)})
row = MiniappScriptAutoReply.query.create(club_id=club_id, **fields)
return Response({'code': 0, 'msg': '创建成功', 'data': _serialize_script_auto_reply(row)})
def _delete_item(self, request, club_id):
from jituan.services.display_config import forbid_display_write_in_all_scope
deny = forbid_display_write_in_all_scope(request)
if deny:
return deny
row_id = request.data.get('id')
if not row_id:
return Response({'code': 400, 'msg': '缺少 id'})
row = MiniappScriptAutoReply.query.filter(id=row_id, club_id=club_id).first()
if row:
row.delete()
return Response({'code': 0, 'msg': '删除成功'})
row = MiniappScriptScene.query.filter(id=row_id, club_id=club_id).first()
if row:
return Response({'code': 400, 'msg': '场景话术不支持删除,请改为禁用'})
return Response({'code': 404, 'msg': '记录不存在'})
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
"""
把龙先生(lxs)审核版商品目录复制到小溪(xx)。
+18 -40
View File
@@ -12,21 +12,24 @@ _FALLBACK_DEFAULT_INVITE = {
}
def get_club_default_dashou_invite_raw(club) -> str:
"""只读 config_json 里真正保存的值(后台表单用,不含回落)。"""
if not club:
return ''
cfg = getattr(club, 'config_json', None) or {}
if not isinstance(cfg, dict):
return ''
return (cfg.get(CONFIG_KEY) or '').strip()
def get_club_fallback_dashou_invite(club_id: str) -> str:
"""系统内置回落(仅小程序运行时;后台不应把它当已保存值展示)。"""
def get_club_default_dashou_invite(club_id: str) -> str:
"""
优先读 Club.config_json.default_dashou_invite,
否则回落内置各店默认管事邀请码。
"""
cid = (club_id or '').strip()
if not cid:
return ''
try:
from jituan.models import Club
club = Club.query.filter(club_id=cid).first()
if club:
cfg = club.config_json or {}
code = (cfg.get(CONFIG_KEY) or '').strip()
if code:
return code
except Exception:
logger.exception('read default_dashou_invite failed club=%s', cid)
try:
from jituan.services.club_migrate import CLUB_MIGRATE_DEFAULT_GUANSHI_INVITE
code = (CLUB_MIGRATE_DEFAULT_GUANSHI_INVITE.get(cid) or '').strip()
@@ -37,33 +40,8 @@ def get_club_fallback_dashou_invite(club_id: str) -> str:
return (_FALLBACK_DEFAULT_INVITE.get(cid) or '').strip()
def get_club_default_dashou_invite(club_id: str) -> str:
"""
小程序用:优先 config_json.default_dashou_invite,否则回落内置码。
"""
cid = (club_id or '').strip()
if not cid:
return ''
try:
from jituan.models import Club
club = Club.query.filter(club_id=cid).first()
if club:
code = get_club_default_dashou_invite_raw(club)
if code:
return code
except Exception:
logger.exception('read default_dashou_invite failed club=%s', cid)
return get_club_fallback_dashou_invite(cid)
def set_club_default_dashou_invite(club, invite_code: str) -> None:
"""写入 club.config_json,调用方负责 save。空字符串表示清空已保存值。"""
"""写入 club.config_json,调用方负责 save。"""
cfg = dict(getattr(club, 'config_json', None) or {})
code = (invite_code or '').strip()
if code:
cfg[CONFIG_KEY] = code
else:
# 清空:删掉键,避免再次读到旧固定值
cfg.pop(CONFIG_KEY, None)
# 强制新 dict,避免 JSONField 变更检测失败
club.config_json = dict(cfg)
cfg[CONFIG_KEY] = (invite_code or '').strip()
club.config_json = cfg
+1
View File
@@ -360,6 +360,7 @@ class ZhuanpanGoumai(APIView):
pkg = f'prepay_id={prepay_id}'
pay_sign_src = f'appId={APPID}&nonceStr={pay_nonce}&package={pkg}&signType=MD5&timeStamp={ts}&key={KEY}'
return {
'appId': APPID,
'timeStamp': ts,
'nonceStr': pay_nonce,
'package': pkg,