feat: 点单端邀请绑定与只看审核商品开关
复用 ClubMiniappIcon 增加点单端海报背景;新增 C 端邀请码/换绑店铺接口;hqsp 支持 zhi_kan_shenhe,不影响支付抢单分红。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,7 +38,7 @@ from .merchant_links import (
|
||||
IPUtils, OrderLinkThrottle,
|
||||
)
|
||||
from .qrcode_poster import (
|
||||
GuanZhuAListView, GuanshiQRCodeView, ZuzhangHaibaoView,
|
||||
GuanZhuAListView, GuanshiQRCodeView, ZuzhangHaibaoView, CYaoqingQRCodeView,
|
||||
)
|
||||
from .scripts import (
|
||||
HuashuHqView, HuashuMatchView,
|
||||
@@ -65,7 +65,7 @@ __all__ = [
|
||||
'KehuGetDingdanLianjieView', 'KehuTianxieDingdanView',
|
||||
'IPUtils', 'OrderLinkThrottle',
|
||||
# qrcode_poster
|
||||
'GuanZhuAListView', 'GuanshiQRCodeView', 'ZuzhangHaibaoView',
|
||||
'GuanZhuAListView', 'GuanshiQRCodeView', 'ZuzhangHaibaoView', 'CYaoqingQRCodeView',
|
||||
# scripts
|
||||
'HuashuHqView', 'HuashuMatchView',
|
||||
'MiniappScriptHoutaiListView', 'MiniappScriptHoutaiModifyView',
|
||||
|
||||
@@ -351,9 +351,130 @@ class GuanshiQRCodeView(APIView):
|
||||
return None
|
||||
|
||||
|
||||
class CYaoqingQRCodeView(APIView):
|
||||
"""
|
||||
点单端用户邀请小程序码。
|
||||
POST /peizhi/cyaoqingewm
|
||||
"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
current_user = request.user
|
||||
try:
|
||||
from jituan.services.club_context import resolve_effective_club_id
|
||||
from utils.weixin_token import get_club_mini_access_token, is_weixin_token_invalid
|
||||
from users.services.c_invite import ensure_c_invite_code
|
||||
from users.models import YonghuCYaoqing
|
||||
|
||||
club_id = resolve_effective_club_id(request, current_user)
|
||||
profile, invite_code, _ = ensure_c_invite_code(current_user)
|
||||
|
||||
access_token = get_club_mini_access_token(club_id)
|
||||
if not access_token:
|
||||
return Response(
|
||||
{'code': 500, 'message': '获取微信access_token失败', 'data': None},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
encoded_invite = urllib.parse.quote(invite_code, safe='')
|
||||
page_path = f'pages/index/index?inviteCode={encoded_invite}'
|
||||
post_data = {
|
||||
'path': page_path,
|
||||
'width': 430,
|
||||
'auto_color': False,
|
||||
'line_color': {'r': 0, 'g': 0, 'b': 0},
|
||||
}
|
||||
|
||||
def _request_qr(token):
|
||||
try:
|
||||
return requests.post(
|
||||
f'https://api.weixin.qq.com/wxa/getwxacode?access_token={token}',
|
||||
json=post_data,
|
||||
timeout=10,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error('请求微信接口异常: %s', e, exc_info=True)
|
||||
return None
|
||||
|
||||
wx_resp = _request_qr(access_token)
|
||||
if wx_resp is None:
|
||||
return Response(
|
||||
{'code': 500, 'message': '调用微信服务失败', 'data': None},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
if wx_resp.headers.get('content-type', '').startswith('application/json'):
|
||||
try:
|
||||
err = wx_resp.json()
|
||||
if is_weixin_token_invalid(err.get('errcode'), err.get('errmsg', '')):
|
||||
access_token = get_club_mini_access_token(club_id, force_refresh=True)
|
||||
if access_token:
|
||||
wx_resp = _request_qr(access_token)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if wx_resp.status_code != 200:
|
||||
errmsg = '未知错误'
|
||||
try:
|
||||
errmsg = wx_resp.json().get('errmsg', errmsg)
|
||||
except Exception:
|
||||
pass
|
||||
return Response(
|
||||
{'code': 500, 'message': f'生成二维码失败: {errmsg}', 'data': None},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
content_type = wx_resp.headers.get('content-type', '')
|
||||
if 'image' not in content_type:
|
||||
try:
|
||||
errmsg = wx_resp.json().get('errmsg', '未知错误')
|
||||
except Exception:
|
||||
errmsg = '生成二维码失败'
|
||||
return Response(
|
||||
{'code': 500, 'message': f'生成二维码失败: {errmsg}', 'data': None},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
timestamp = int(time.time())
|
||||
filename = f'{current_user.UserUID}_{timestamp}.png'
|
||||
oss_path = f'erweima/cuser/{club_id}/{filename}'
|
||||
file_obj = io.BytesIO(wx_resp.content)
|
||||
full_url = upload_to_oss(file_obj, oss_path)
|
||||
if not full_url:
|
||||
return Response(
|
||||
{'code': 500, 'message': '图片上传失败', 'data': None},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
cos_domain = getattr(settings, 'COS_DOMAIN', '')
|
||||
if cos_domain and full_url.startswith(cos_domain):
|
||||
relative_path = full_url[len(cos_domain):].lstrip('/')
|
||||
else:
|
||||
relative_path = oss_path
|
||||
|
||||
old_url = profile.invite_qrcode_url
|
||||
if old_url:
|
||||
try:
|
||||
delete_from_oss(old_url)
|
||||
except Exception as e:
|
||||
logger.warning('删除旧 C 端二维码失败: %s', e)
|
||||
|
||||
with transaction.atomic():
|
||||
locked = YonghuCYaoqing.objects.select_for_update().get(pk=profile.pk)
|
||||
locked.invite_qrcode_url = relative_path
|
||||
locked.save(update_fields=['invite_qrcode_url', 'UpdateTime'])
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'message': 'success',
|
||||
'data': {'url': relative_path, 'yaoqingma': invite_code},
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error('生成点单端邀请二维码失败: %s', e, exc_info=True)
|
||||
return Response(
|
||||
{'code': 500, 'message': '服务器内部错误', 'data': None},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
|
||||
class ZuzhangHaibaoView(APIView):
|
||||
|
||||
Reference in New Issue
Block a user