feat: 点单端邀请绑定与只看审核商品开关

复用 ClubMiniappIcon 增加点单端海报背景;新增 C 端邀请码/换绑店铺接口;hqsp 支持 zhi_kan_shenhe,不影响支付抢单分红。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-21 02:37:31 +08:00
parent d5b8ff5b8a
commit 60d8b52693
14 changed files with 615 additions and 7 deletions

View File

@@ -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',

View File

@@ -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):