fix: 扫码注册统一用户查找与俱乐部校验,修复wdlyhdl与自动注册失败
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,84 @@
|
|||||||
"""用户所属俱乐部解析。"""
|
"""用户所属俱乐部解析。"""
|
||||||
|
from django.db import IntegrityError
|
||||||
|
|
||||||
from jituan.constants import CLUB_ID_DEFAULT
|
from jituan.constants import CLUB_ID_DEFAULT
|
||||||
from jituan.models import UserWxOpenid
|
from jituan.models import UserWxOpenid
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_invite_party_club(user, request_club_id=None):
|
||||||
|
"""
|
||||||
|
邀请链校验用:只返回已落库的俱乐部归属。
|
||||||
|
未归属任何俱乐部时返回 None(勿默认 xq,避免星之界扫码误拦)。
|
||||||
|
"""
|
||||||
|
if not user:
|
||||||
|
return None
|
||||||
|
|
||||||
|
explicit = (getattr(user, 'ClubID', None) or '').strip()
|
||||||
|
if explicit:
|
||||||
|
return explicit
|
||||||
|
|
||||||
|
uid = getattr(user, 'UserUID', None) or getattr(user, 'yonghuid', None)
|
||||||
|
if not uid:
|
||||||
|
return None
|
||||||
|
|
||||||
|
req = (request_club_id or '').strip()
|
||||||
|
if req:
|
||||||
|
hit = UserWxOpenid.query.filter(yonghuid=uid, club_id=req).first()
|
||||||
|
if hit:
|
||||||
|
return req
|
||||||
|
|
||||||
|
binding = UserWxOpenid.query.filter(yonghuid=uid).order_by('id').first()
|
||||||
|
if binding:
|
||||||
|
return binding.club_id
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_user_by_wx_openid(club_id, openid):
|
||||||
|
"""按俱乐部 openid 绑定或 legacy OpenID/UserName 定位用户。"""
|
||||||
|
from users.business_models import User
|
||||||
|
|
||||||
|
if club_id and openid:
|
||||||
|
binding = UserWxOpenid.query.filter(club_id=club_id, openid=openid).first()
|
||||||
|
if binding:
|
||||||
|
user = User.query.filter(UserUID=binding.yonghuid).first()
|
||||||
|
if not user:
|
||||||
|
user = User.objects.filter(UserUID=binding.yonghuid).first()
|
||||||
|
if user:
|
||||||
|
return user
|
||||||
|
|
||||||
|
user = User.query.filter(OpenID=openid).first()
|
||||||
|
if not user:
|
||||||
|
user = User.objects.filter(OpenID=openid).first()
|
||||||
|
if not user:
|
||||||
|
user = User.query.filter(UserName=openid).first()
|
||||||
|
if not user:
|
||||||
|
user = User.objects.filter(UserName=openid).first()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_wx_openid_binding(club_id, openid, user, unionid=''):
|
||||||
|
if not club_id or not openid or not user:
|
||||||
|
return None
|
||||||
|
uid = getattr(user, 'UserUID', None)
|
||||||
|
if not uid:
|
||||||
|
return None
|
||||||
|
|
||||||
|
binding = UserWxOpenid.query.filter(club_id=club_id, openid=openid).first()
|
||||||
|
if binding:
|
||||||
|
return binding
|
||||||
|
|
||||||
|
try:
|
||||||
|
return UserWxOpenid.query.create(
|
||||||
|
club_id=club_id,
|
||||||
|
openid=openid,
|
||||||
|
yonghuid=uid,
|
||||||
|
unionid=unionid or getattr(user, 'UnionID', None) or '',
|
||||||
|
)
|
||||||
|
except IntegrityError:
|
||||||
|
return UserWxOpenid.query.filter(club_id=club_id, openid=openid).first()
|
||||||
|
|
||||||
|
|
||||||
def get_user_club_id(user):
|
def get_user_club_id(user):
|
||||||
"""
|
"""
|
||||||
解析用户归属 club(注册/邀请链隔离用)。
|
解析用户归属 club(注册/邀请链隔离用)。
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
"""邀请码 / 邀请链俱乐部隔离。"""
|
"""邀请码 / 邀请链俱乐部隔离。"""
|
||||||
|
from jituan.constants import CLUB_ID_DEFAULT
|
||||||
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
||||||
from jituan.services.club_user import get_user_club_id
|
from jituan.services.club_user import resolve_invite_party_club
|
||||||
|
|
||||||
|
|
||||||
def assert_invite_same_club(request, inviter_user, invitee_user=None):
|
def assert_invite_same_club(request, inviter_user, invitee_user=None):
|
||||||
"""
|
"""
|
||||||
校验邀请人与被邀请人属于同一俱乐部,且与请求 club 一致。
|
校验邀请人与被邀请人属于同一俱乐部,且与请求 club 一致。
|
||||||
现网全部 xq 时行为与改造前一致。
|
未归属俱乐部的被邀请人允许在本小程序完成首次注册。
|
||||||
返回 (ok, message)
|
返回 (ok, message)
|
||||||
"""
|
"""
|
||||||
club_id = resolve_club_id_for_wechat_request(request)
|
club_id = resolve_club_id_for_wechat_request(request)
|
||||||
inviter_club = get_user_club_id(inviter_user)
|
inviter_club = resolve_invite_party_club(inviter_user, club_id)
|
||||||
|
if inviter_club is None and club_id == CLUB_ID_DEFAULT:
|
||||||
|
inviter_club = CLUB_ID_DEFAULT
|
||||||
if inviter_club != club_id:
|
if inviter_club != club_id:
|
||||||
return False, '邀请码不属于当前小程序俱乐部'
|
return False, '邀请码不属于当前小程序俱乐部'
|
||||||
|
|
||||||
if invitee_user is not None:
|
if invitee_user is not None:
|
||||||
invitee_club = get_user_club_id(invitee_user)
|
invitee_club = resolve_invite_party_club(invitee_user, club_id)
|
||||||
if invitee_club != club_id:
|
if invitee_club is not None and invitee_club != club_id:
|
||||||
return False, '您的账号归属与当前小程序不一致,请使用对应俱乐部小程序'
|
return False, '您的账号归属与当前小程序不一致,请使用对应俱乐部小程序'
|
||||||
if invitee_club != inviter_club:
|
if invitee_club is not None and invitee_club != inviter_club:
|
||||||
return False, '邀请码与您的俱乐部不匹配'
|
return False, '邀请码与您的俱乐部不匹配'
|
||||||
|
|
||||||
return True, ''
|
return True, ''
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ import random
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rest_framework_simplejwt.tokens import RefreshToken
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
|
|
||||||
from jituan.constants import CLUB_ID_DEFAULT
|
from jituan.constants import CLUB_ID_DEFAULT
|
||||||
from jituan.models import UserWxOpenid
|
from jituan.models import UserWxOpenid
|
||||||
from jituan.services.club_resolver import jscode2session, resolve_club_by_appid
|
from jituan.services.club_resolver import jscode2session, resolve_club_by_appid
|
||||||
|
from jituan.services.club_user import (
|
||||||
|
ensure_user_club_id,
|
||||||
|
ensure_wx_openid_binding,
|
||||||
|
find_user_by_wx_openid,
|
||||||
|
)
|
||||||
from users.business_models import User
|
from users.business_models import User
|
||||||
from users.models import UserBoss, UserDashou, UserGuanshi, UserShangjia, UserZuzhang, UserShenheguan
|
from users.models import UserBoss, UserDashou, UserGuanshi, UserShangjia, UserZuzhang, UserShenheguan
|
||||||
from gvsdsdk.models import Role
|
from gvsdsdk.models import Role
|
||||||
@@ -105,37 +110,6 @@ def _collect_role_status(user):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _find_user_by_openid_legacy(openid):
|
|
||||||
"""兼容旧登录/扫码注册:User.OpenID 或 UserName 存的是 openid。"""
|
|
||||||
user = User.query.filter(OpenID=openid).first()
|
|
||||||
if not user:
|
|
||||||
user = User.objects.filter(OpenID=openid).first()
|
|
||||||
if not user:
|
|
||||||
user = User.query.filter(UserName=openid).first()
|
|
||||||
if not user:
|
|
||||||
user = User.objects.filter(UserName=openid).first()
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
def _ensure_wx_binding(resolved_club_id, openid, user, unionid=''):
|
|
||||||
binding = UserWxOpenid.query.filter(
|
|
||||||
club_id=resolved_club_id, openid=openid,
|
|
||||||
).first()
|
|
||||||
if binding:
|
|
||||||
return binding
|
|
||||||
try:
|
|
||||||
return UserWxOpenid.query.create(
|
|
||||||
club_id=resolved_club_id,
|
|
||||||
openid=openid,
|
|
||||||
yonghuid=user.UserUID,
|
|
||||||
unionid=unionid or user.UnionID or '',
|
|
||||||
)
|
|
||||||
except IntegrityError:
|
|
||||||
return UserWxOpenid.query.filter(
|
|
||||||
club_id=resolved_club_id, openid=openid,
|
|
||||||
).first()
|
|
||||||
|
|
||||||
|
|
||||||
def login_or_register_by_club(code, club_id=None, app_id=None, client_ip=''):
|
def login_or_register_by_club(code, club_id=None, app_id=None, client_ip=''):
|
||||||
"""
|
"""
|
||||||
俱乐部微信登录核心逻辑。
|
俱乐部微信登录核心逻辑。
|
||||||
@@ -164,9 +138,9 @@ def login_or_register_by_club(code, club_id=None, app_id=None, client_ip=''):
|
|||||||
user = User.objects.filter(UserUID=binding.yonghuid).first()
|
user = User.objects.filter(UserUID=binding.yonghuid).first()
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
user = _find_user_by_openid_legacy(openid)
|
user = find_user_by_wx_openid(resolved_club_id, openid)
|
||||||
if user:
|
if user:
|
||||||
_ensure_wx_binding(resolved_club_id, openid, user, unionid)
|
ensure_wx_openid_binding(resolved_club_id, openid, user, unionid)
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
user_uuid = uuid.uuid4().bytes
|
user_uuid = uuid.uuid4().bytes
|
||||||
@@ -179,17 +153,18 @@ def login_or_register_by_club(code, club_id=None, app_id=None, client_ip=''):
|
|||||||
UnionID=unionid or None,
|
UnionID=unionid or None,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
|
from django.db import IntegrityError
|
||||||
user.save()
|
user.save()
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
user = _find_user_by_openid_legacy(openid)
|
user = find_user_by_wx_openid(resolved_club_id, openid)
|
||||||
if not user:
|
if not user:
|
||||||
raise
|
raise
|
||||||
_ensure_wx_binding(resolved_club_id, openid, user, unionid)
|
ensure_wx_openid_binding(resolved_club_id, openid, user, unionid)
|
||||||
else:
|
else:
|
||||||
created = True
|
created = True
|
||||||
_ensure_boss_profile(user)
|
_ensure_boss_profile(user)
|
||||||
_ensure_consumer_role(user)
|
_ensure_consumer_role(user)
|
||||||
_ensure_wx_binding(resolved_club_id, openid, user, unionid)
|
ensure_wx_openid_binding(resolved_club_id, openid, user, unionid)
|
||||||
|
|
||||||
if unionid and unionid != (user.UnionID or ''):
|
if unionid and unionid != (user.UnionID or ''):
|
||||||
user.UnionID = unionid
|
user.UnionID = unionid
|
||||||
@@ -198,7 +173,6 @@ def login_or_register_by_club(code, club_id=None, app_id=None, client_ip=''):
|
|||||||
user.UserLastLoginDate = timezone.now()
|
user.UserLastLoginDate = timezone.now()
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
from jituan.services.club_user import ensure_user_club_id
|
|
||||||
ensure_user_club_id(user, resolved_club_id)
|
ensure_user_club_id(user, resolved_club_id)
|
||||||
|
|
||||||
roles = _collect_role_status(user)
|
roles = _collect_role_status(user)
|
||||||
|
|||||||
@@ -550,6 +550,9 @@ class DashouZhuceView(APIView):
|
|||||||
}, status=status.HTTP_404_NOT_FOUND)
|
}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
from jituan.services.invite_guard import assert_invite_same_club
|
from jituan.services.invite_guard import assert_invite_same_club
|
||||||
|
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
||||||
|
from jituan.services.club_user import ensure_user_club_id
|
||||||
|
|
||||||
ok, msg = assert_invite_same_club(request, guanshi_profile.user, current_user)
|
ok, msg = assert_invite_same_club(request, guanshi_profile.user, current_user)
|
||||||
if not ok:
|
if not ok:
|
||||||
return Response({
|
return Response({
|
||||||
@@ -558,6 +561,8 @@ class DashouZhuceView(APIView):
|
|||||||
'data': None
|
'data': None
|
||||||
}, status=status.HTTP_403_FORBIDDEN)
|
}, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
club_id = resolve_club_id_for_wechat_request(request)
|
||||||
|
|
||||||
# 4. 验证管事状态
|
# 4. 验证管事状态
|
||||||
|
|
||||||
# 5. 核心:在数据库事务中创建用户的所有身份
|
# 5. 核心:在数据库事务中创建用户的所有身份
|
||||||
@@ -617,6 +622,8 @@ class DashouZhuceView(APIView):
|
|||||||
'data': None
|
'data': None
|
||||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
ensure_user_club_id(current_user, club_id)
|
||||||
|
|
||||||
# 6. 注册成功,返回信息 (新注册用户没有会员记录,clumber返回空列表)
|
# 6. 注册成功,返回信息 (新注册用户没有会员记录,clumber返回空列表)
|
||||||
fanhui_data = self.zhuangbeiFanhuiShuju(dashou_profile, current_user, is_new=True)
|
fanhui_data = self.zhuangbeiFanhuiShuju(dashou_profile, current_user, is_new=True)
|
||||||
return Response({
|
return Response({
|
||||||
@@ -851,31 +858,51 @@ class WechatLoginAndDashouRegisterView(APIView):
|
|||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
openid = wechat_data['openid']
|
openid = wechat_data['openid']
|
||||||
|
unionid = wechat_data.get('unionid', '')
|
||||||
|
|
||||||
# 3. 获取客户端真实IP
|
# 3. 获取客户端真实IP
|
||||||
kehuduan_ip = self.huoquKehuduanIP(request)
|
kehuduan_ip = self.huoquKehuduanIP(request)
|
||||||
|
|
||||||
|
from jituan.constants import CLUB_ID_DEFAULT
|
||||||
|
from jituan.services.club_user import (
|
||||||
|
ensure_user_club_id,
|
||||||
|
ensure_wx_openid_binding,
|
||||||
|
find_user_by_wx_openid,
|
||||||
|
)
|
||||||
|
|
||||||
# 4. 开始数据库事务(确保登录和注册的原子性)
|
# 4. 开始数据库事务(确保登录和注册的原子性)
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
# 4.1 创建或获取用户(登录逻辑)
|
# 4.1 定位或创建用户(兼容俱乐部登录建立的 user_wx_openid)
|
||||||
user_main, created = User.objects.select_for_update().get_or_create(
|
user_main = find_user_by_wx_openid(club_id, openid)
|
||||||
OpenID=openid,
|
created = False
|
||||||
defaults={
|
|
||||||
'UserUID': self.shengchengYonghuID(),
|
if not user_main:
|
||||||
'UserName': openid,
|
try:
|
||||||
}
|
user_main = User(
|
||||||
)
|
UserUUID=uuid.uuid4().bytes,
|
||||||
|
UserUID=self.shengchengYonghuID(),
|
||||||
|
UserName=openid,
|
||||||
|
OpenID=openid if club_id == CLUB_ID_DEFAULT else None,
|
||||||
|
UnionID=unionid or None,
|
||||||
|
)
|
||||||
|
user_main.save()
|
||||||
|
created = True
|
||||||
|
UserBoss.query.create(user=user_main, nickname='微信用户')
|
||||||
|
except IntegrityError:
|
||||||
|
user_main = find_user_by_wx_openid(club_id, openid)
|
||||||
|
if not user_main:
|
||||||
|
raise
|
||||||
|
|
||||||
|
ensure_wx_openid_binding(club_id, openid, user_main, unionid)
|
||||||
|
|
||||||
# 更新用户IP和最后登录时间
|
# 更新用户IP和最后登录时间
|
||||||
cunchu_ip = kehuduan_ip
|
cunchu_ip = kehuduan_ip
|
||||||
user_main.IP = cunchu_ip
|
user_main.IP = cunchu_ip
|
||||||
user_main.UserLastLoginDate = timezone.now()
|
user_main.UserLastLoginDate = timezone.now()
|
||||||
|
if unionid and unionid != (user_main.UnionID or ''):
|
||||||
|
user_main.UnionID = unionid
|
||||||
user_main.save()
|
user_main.save()
|
||||||
|
|
||||||
if created:
|
|
||||||
# 新用户:创建老板扩展表
|
|
||||||
UserBoss.query.create(user=user_main, nickname='微信用户')
|
|
||||||
|
|
||||||
# 4.2 验证邀请码对应的管事(注册逻辑)
|
# 4.2 验证邀请码对应的管事(注册逻辑)
|
||||||
try:
|
try:
|
||||||
# 使用select_related获取管事及其关联的主表用户信息
|
# 使用select_related获取管事及其关联的主表用户信息
|
||||||
@@ -886,7 +913,6 @@ class WechatLoginAndDashouRegisterView(APIView):
|
|||||||
return self.fanhuiZhihouDengluXinxi(user_main, yaoqingma_wuxiao=True)
|
return self.fanhuiZhihouDengluXinxi(user_main, yaoqingma_wuxiao=True)
|
||||||
|
|
||||||
from jituan.services.invite_guard import assert_invite_same_club
|
from jituan.services.invite_guard import assert_invite_same_club
|
||||||
from jituan.services.club_user import ensure_user_club_id
|
|
||||||
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
from jituan.services.club_resolver import resolve_club_id_for_wechat_request
|
||||||
|
|
||||||
ok, invite_msg = assert_invite_same_club(
|
ok, invite_msg = assert_invite_same_club(
|
||||||
@@ -1204,6 +1230,7 @@ class WechatLoginAndDashouRegisterView(APIView):
|
|||||||
'shangjiastatus': shangjia_status,
|
'shangjiastatus': shangjia_status,
|
||||||
'dashoustatus': dashou_status,
|
'dashoustatus': dashou_status,
|
||||||
'guanshistatus': guanshi_status,
|
'guanshistatus': guanshi_status,
|
||||||
|
'club_id': getattr(user_main, 'ClubID', None) or '',
|
||||||
'dashouqun': group_info.get('dashouqun', ''),
|
'dashouqun': group_info.get('dashouqun', ''),
|
||||||
'dashouqunid': group_info.get('dashouqunid', ''),
|
'dashouqunid': group_info.get('dashouqunid', ''),
|
||||||
'guanshiqun': group_info.get('guanshiqun', ''),
|
'guanshiqun': group_info.get('guanshiqun', ''),
|
||||||
|
|||||||
Reference in New Issue
Block a user