fix: 老小程序不改端,后端兼容扫码注册(dashouzhuce/wdlyhdl)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
18
jituan/services/api_compat.py
Normal file
18
jituan/services/api_compat.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""老小程序兼容:统一 JSON 错误格式(勿改前端时靠后端适配)。"""
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
def legacy_miniapp_error(code, message, http_status=status.HTTP_200_OK):
|
||||
"""
|
||||
业务错误返回 HTTP 200 + body.code。
|
||||
老端 request.js 会把 HTTP 403 当成「请先登录」,导致只显示「注册失败」。
|
||||
同时写 message 与 msg,兼容 dashouzhuce / wdlyhdl 两种字段名。
|
||||
"""
|
||||
text = message or '操作失败'
|
||||
return Response({
|
||||
'code': code,
|
||||
'message': text,
|
||||
'msg': text,
|
||||
'data': None,
|
||||
}, status=http_status)
|
||||
@@ -42,11 +42,38 @@ def get_club_wx_credentials(club_id):
|
||||
return '', '', club
|
||||
|
||||
|
||||
def _invite_code_from_request(request):
|
||||
data = getattr(request, 'data', None) or {}
|
||||
if not isinstance(data, dict):
|
||||
return ''
|
||||
return (data.get('inviteCode') or data.get('invite_code') or '').strip()
|
||||
|
||||
|
||||
def resolve_club_from_invite_code(invite_code):
|
||||
"""老端无 Header/Referer 时,从管事邀请码反查俱乐部。"""
|
||||
invite_code = (invite_code or '').strip()
|
||||
if not invite_code:
|
||||
return None
|
||||
try:
|
||||
from users.models import UserGuanshi
|
||||
from jituan.services.club_user import resolve_invite_party_club
|
||||
|
||||
guanshi = UserGuanshi.query.select_related('user').get(yaoqingma=invite_code)
|
||||
user = guanshi.user
|
||||
club = resolve_invite_party_club(user, None)
|
||||
if club:
|
||||
return club
|
||||
explicit = (getattr(user, 'ClubID', None) or '').strip()
|
||||
return explicit or None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def resolve_club_id_for_wechat_request(request):
|
||||
"""
|
||||
小程序请求解析 club_id(登录/扫码注册)。
|
||||
优先级:X-Club-Id > body.appId > Referer 内 wx appid > 默认 xq。
|
||||
老端 fighter.js 的 wdlyhdl 未带头时,靠 Referer 识别星之界等俱乐部。
|
||||
优先级:X-Club-Id > body.appId > Referer > body.inviteCode 反查管事 > JWT 用户归属 > 默认 xq。
|
||||
老端 fighter.js 的 wdlyhdl 未带头时,靠 Referer 或邀请码识别星之界。
|
||||
"""
|
||||
from jituan.services.club_context import resolve_club_id_from_request
|
||||
|
||||
@@ -72,6 +99,18 @@ def resolve_club_id_for_wechat_request(request):
|
||||
if club:
|
||||
return club.club_id
|
||||
|
||||
invite_club = resolve_club_from_invite_code(_invite_code_from_request(request))
|
||||
if invite_club:
|
||||
return invite_club
|
||||
|
||||
# 已登录走 dashouzhuce 时往往无 Referer,用 JWT 用户归属俱乐部
|
||||
user = getattr(request, 'user', None)
|
||||
if user is not None and getattr(user, 'is_authenticated', False):
|
||||
from jituan.services.club_user import get_user_club_id
|
||||
user_club = get_user_club_id(user)
|
||||
if user_club:
|
||||
return user_club
|
||||
|
||||
return resolve_club_id_from_request(request)
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
"""邀请码 / 邀请链俱乐部隔离。"""
|
||||
import logging
|
||||
|
||||
from jituan.models import UserWxOpenid
|
||||
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
||||
from jituan.services.club_user import resolve_invite_party_club
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _invitee_has_club_binding(user, club_id):
|
||||
uid = getattr(user, 'UserUID', None)
|
||||
if not uid or not club_id:
|
||||
return False
|
||||
return UserWxOpenid.query.filter(yonghuid=uid, club_id=club_id).exists()
|
||||
|
||||
|
||||
def assert_invite_same_club(request, inviter_user, invitee_user=None):
|
||||
"""
|
||||
校验邀请人与被邀请人属于同一俱乐部,且与请求 club 一致。
|
||||
@@ -28,11 +36,13 @@ def assert_invite_same_club(request, inviter_user, invitee_user=None):
|
||||
if invitee_user is not None:
|
||||
invitee_club = resolve_invite_party_club(invitee_user, club_id)
|
||||
if invitee_club is not None and invitee_club != club_id:
|
||||
logger.warning(
|
||||
'invite_guard 被邀请人俱乐部不符 invitee=%s invitee_club=%s req_club=%s',
|
||||
getattr(invitee_user, 'UserUID', '?'), invitee_club, club_id,
|
||||
)
|
||||
return False, '您的账号归属与当前小程序不一致,请使用对应俱乐部小程序'
|
||||
# 老用户 ClubID 可能是 xq,但扫的是本俱乐部管事码:允许注册并回写 ClubID
|
||||
if not _invitee_has_club_binding(invitee_user, club_id) and inviter_club != club_id:
|
||||
logger.warning(
|
||||
'invite_guard 被邀请人俱乐部不符 invitee=%s invitee_club=%s req_club=%s',
|
||||
getattr(invitee_user, 'UserUID', '?'), invitee_club, club_id,
|
||||
)
|
||||
return False, '您的账号归属与当前小程序不一致,请使用对应俱乐部小程序'
|
||||
if invitee_club is not None and invitee_club != inviter_club:
|
||||
return False, '邀请码与您的俱乐部不匹配'
|
||||
|
||||
|
||||
@@ -509,22 +509,16 @@ class DashouZhuceView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
from jituan.services.api_compat import legacy_miniapp_error
|
||||
|
||||
# 1. 获取并验证邀请码参数
|
||||
yaoqingma = request.data.get('inviteCode')
|
||||
if not yaoqingma:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '邀请码不能为空',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return legacy_miniapp_error(400, '邀请码不能为空')
|
||||
|
||||
yaoqingma = yaoqingma.strip()
|
||||
if len(yaoqingma) > 100:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'message': '邀请码长度超过限制',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return legacy_miniapp_error(400, '邀请码长度超过限制')
|
||||
|
||||
current_user = request.user
|
||||
# 注意:这里request.user是User实例
|
||||
@@ -543,11 +537,7 @@ class DashouZhuceView(APIView):
|
||||
# 使用select_related一次性获取管事及其关联的主表用户信息,性能最高
|
||||
guanshi_profile = UserGuanshi.query.select_related('user').get(yaoqingma=yaoqingma)
|
||||
except UserGuanshi.DoesNotExist:
|
||||
return Response({
|
||||
'code': 404,
|
||||
'message': '邀请码无效或不存在',
|
||||
'data': None
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
return legacy_miniapp_error(404, '邀请码无效或不存在')
|
||||
|
||||
from jituan.services.invite_guard import assert_invite_same_club
|
||||
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
||||
@@ -559,12 +549,7 @@ class DashouZhuceView(APIView):
|
||||
'dashouzhuce 俱乐部校验失败 uid=%s club=%s msg=%s',
|
||||
current_user.UserUID, resolve_club_id_for_wechat_request(request), msg,
|
||||
)
|
||||
return Response({
|
||||
'code': 403,
|
||||
'message': msg,
|
||||
'msg': msg,
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
return legacy_miniapp_error(403, msg)
|
||||
|
||||
club_id = resolve_club_id_for_wechat_request(request)
|
||||
|
||||
@@ -620,12 +605,8 @@ class DashouZhuceView(APIView):
|
||||
except Exception as e:
|
||||
|
||||
# 事务会自动回滚
|
||||
# 这里可以记录更详细的日志
|
||||
return Response({
|
||||
'code': 500,
|
||||
'message': f'注册过程中发生系统错误: {str(e)}',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
logger.exception('dashouzhuce 注册异常 uid=%s', current_user.UserUID)
|
||||
return legacy_miniapp_error(500, f'注册过程中发生系统错误: {str(e)}')
|
||||
|
||||
ensure_user_club_id(current_user, club_id)
|
||||
|
||||
@@ -634,6 +615,7 @@ class DashouZhuceView(APIView):
|
||||
return Response({
|
||||
'code': 200,
|
||||
'message': '注册成功!已开通打手、管事身份。',
|
||||
'msg': '注册成功!已开通打手、管事身份。',
|
||||
'data': fanhui_data
|
||||
})
|
||||
|
||||
@@ -824,30 +806,20 @@ class WechatLoginAndDashouRegisterView(APIView):
|
||||
处理微信登录并注册打手请求
|
||||
"""
|
||||
try:
|
||||
from jituan.services.api_compat import legacy_miniapp_error
|
||||
|
||||
# 1. 获取并验证参数
|
||||
code = request.data.get('code', '').strip()
|
||||
yaoqingma = request.data.get('inviteCode', '').strip()
|
||||
|
||||
if not code:
|
||||
return Response({
|
||||
'code': 1,
|
||||
'msg': '微信授权码不能为空',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return legacy_miniapp_error(1, '微信授权码不能为空')
|
||||
|
||||
if not yaoqingma:
|
||||
return Response({
|
||||
'code': 2,
|
||||
'msg': '邀请码不能为空',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return legacy_miniapp_error(2, '邀请码不能为空')
|
||||
|
||||
if len(yaoqingma) > 100:
|
||||
return Response({
|
||||
'code': 3,
|
||||
'msg': '邀请码长度超过限制',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return legacy_miniapp_error(3, '邀请码长度超过限制')
|
||||
|
||||
# 2. 获取微信openid(按俱乐部小程序 appid,勿用全局星阙配置)
|
||||
from jituan.services.club_resolver import (
|
||||
@@ -859,11 +831,7 @@ class WechatLoginAndDashouRegisterView(APIView):
|
||||
if not wechat_data or 'openid' not in wechat_data:
|
||||
error_msg = wechat_data.get('errmsg', '微信授权失败')
|
||||
logger.warning('wdlyhdl 微信登录失败 club=%s err=%s', club_id, error_msg)
|
||||
return Response({
|
||||
'code': 4,
|
||||
'msg': f'微信登录失败: {error_msg}',
|
||||
'data': None
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return legacy_miniapp_error(4, f'微信登录失败: {error_msg}')
|
||||
|
||||
openid = wechat_data['openid']
|
||||
unionid = wechat_data.get('unionid', '')
|
||||
@@ -916,9 +884,8 @@ class WechatLoginAndDashouRegisterView(APIView):
|
||||
# 使用select_related获取管事及其关联的主表用户信息
|
||||
guanshi_profile = UserGuanshi.query.select_related('user').get(yaoqingma=yaoqingma)
|
||||
except UserGuanshi.DoesNotExist:
|
||||
# 邀请码无效,但用户已登录,可以返回登录成功但注册失败
|
||||
# 这里我们继续执行,但只返回登录信息
|
||||
return self.fanhuiZhihouDengluXinxi(user_main, yaoqingma_wuxiao=True)
|
||||
ensure_user_club_id(user_main, club_id)
|
||||
return legacy_miniapp_error(404, '邀请码无效或不存在')
|
||||
|
||||
from jituan.services.invite_guard import assert_invite_same_club
|
||||
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
||||
@@ -931,12 +898,7 @@ class WechatLoginAndDashouRegisterView(APIView):
|
||||
'wdlyhdl 俱乐部校验失败 uid=%s club=%s msg=%s invite=%s',
|
||||
user_main.UserUID, club_id, invite_msg, yaoqingma[:8],
|
||||
)
|
||||
return Response({
|
||||
'code': 403,
|
||||
'msg': invite_msg,
|
||||
'message': invite_msg,
|
||||
'data': None
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
return legacy_miniapp_error(403, invite_msg)
|
||||
|
||||
ensure_user_club_id(user_main, resolve_club_id_for_wechat_request(request))
|
||||
|
||||
@@ -1003,12 +965,9 @@ class WechatLoginAndDashouRegisterView(APIView):
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
# 返回友好错误信息
|
||||
return Response({
|
||||
'code': 99,
|
||||
'msg': '系统繁忙,请稍后重试',
|
||||
'data': None
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
# 返回友好错误信息(HTTP 200 以便老端读出 msg)
|
||||
from jituan.services.api_compat import legacy_miniapp_error
|
||||
return legacy_miniapp_error(99, '系统繁忙,请稍后重试')
|
||||
|
||||
def get_wechat_openid(self, code):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user