diff --git a/jituan/services/club_user.py b/jituan/services/club_user.py index c57ec92..fb0f210 100644 --- a/jituan/services/club_user.py +++ b/jituan/services/club_user.py @@ -86,6 +86,40 @@ def ensure_wx_openid_binding(club_id, openid, user, unionid=''): return UserWxOpenid.query.filter(club_id=club_id, openid=openid).first() +def resolve_or_create_wx_user(club_id, openid, generate_uid, unionid=''): + """ + 微信登录/注册统一找用户,避免 UserName(openid) 唯一键冲突。 + 星之界等俱乐部用户常 UserName=openid 且 OpenID 为空,不可 get_or_create(OpenID=...)。 + 返回 (user, created)。 + """ + import uuid + from users.business_models import User + + user = find_user_by_wx_openid(club_id, openid) + if user: + ensure_wx_openid_binding(club_id, openid, user, unionid) + return user, False + + try: + user = User( + UserUUID=uuid.uuid4().bytes, + UserUID=generate_uid(), + UserName=openid, + OpenID=openid if club_id == CLUB_ID_DEFAULT else None, + UnionID=unionid or None, + ClubID='', + ) + user.save() + ensure_wx_openid_binding(club_id, openid, user, unionid) + return user, True + except IntegrityError: + user = find_user_by_wx_openid(club_id, openid) + if not user: + raise + ensure_wx_openid_binding(club_id, openid, user, unionid) + return user, False + + def get_user_club_id(user): """ 解析用户归属 club(注册/邀请链隔离用)。 diff --git a/jituan/services/wechat_login.py b/jituan/services/wechat_login.py index c3cfe2b..5bf1f52 100644 --- a/jituan/services/wechat_login.py +++ b/jituan/services/wechat_login.py @@ -15,6 +15,7 @@ from jituan.services.club_user import ( ensure_user_club_id, ensure_wx_openid_binding, find_user_by_wx_openid, + resolve_or_create_wx_user, ) from users.business_models import User from users.models import UserBoss, UserDashou, UserGuanshi, UserShangjia, UserZuzhang, UserShenheguan @@ -138,34 +139,14 @@ def login_or_register_by_club(code, club_id=None, app_id=None, client_ip=''): user = User.objects.filter(UserUID=binding.yonghuid).first() if not user: - user = find_user_by_wx_openid(resolved_club_id, openid) - if user: - ensure_wx_openid_binding(resolved_club_id, openid, user, unionid) - - if not user: - user_uuid = uuid.uuid4().bytes - new_uid = _generate_user_uid() - user = User( - UserUUID=user_uuid, - UserUID=new_uid, - UserName=openid, - OpenID=openid if resolved_club_id == CLUB_ID_DEFAULT else None, - UnionID=unionid or None, - ClubID='', + user, created = resolve_or_create_wx_user( + resolved_club_id, openid, _generate_user_uid, unionid=unionid, ) - try: - from django.db import IntegrityError - user.save() - except IntegrityError: - user = find_user_by_wx_openid(resolved_club_id, openid) - if not user: - raise - ensure_wx_openid_binding(resolved_club_id, openid, user, unionid) - else: - created = True + if created: _ensure_boss_profile(user) _ensure_consumer_role(user) - ensure_wx_openid_binding(resolved_club_id, openid, user, unionid) + else: + ensure_wx_openid_binding(resolved_club_id, openid, user, unionid) if unionid and unionid != (user.UnionID or ''): user.UnionID = unionid diff --git a/users/views/auth.py b/users/views/auth.py index 83b2cff..1adf3b9 100644 --- a/users/views/auth.py +++ b/users/views/auth.py @@ -123,20 +123,21 @@ class WechatMiniProgramLoginView(APIView): openid = wechat_data['openid'] session_key = wechat_data.get('session_key', '') - unionid = wechat_data.get('unionid', '') # 🆕【新增】获取unionid + unionid = wechat_data.get('unionid', '') - # 获取客户端真实IP + from jituan.services.club_resolver import resolve_club_id_for_wechat_request + from jituan.services.club_user import ensure_user_club_id, resolve_or_create_wx_user + + club_id = resolve_club_id_for_wechat_request(request) kehuduan_ip = self.huoquKehuduanIP(request) with transaction.atomic(): - # 创建或获取用户 - user_main, created = User.objects.select_for_update().get_or_create( - OpenID=openid, - defaults={'UserUID': self.shengchengYonghuID(), 'UserName': openid} + user_main, created = resolve_or_create_wx_user( + club_id, openid, self.shengchengYonghuID, unionid=unionid, ) + ensure_user_club_id(user_main, club_id) - # 保存unionid - if unionid and unionid != user_main.UnionID: + if unionid and unionid != (user_main.UnionID or ''): user_main.UnionID = unionid user_main.IP = kehuduan_ip @@ -842,35 +843,16 @@ class WechatLoginAndDashouRegisterView(APIView): 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, + resolve_or_create_wx_user, ) # 4. 开始数据库事务(确保登录和注册的原子性) with transaction.atomic(): - # 4.1 定位或创建用户(兼容俱乐部登录建立的 user_wx_openid) - user_main = find_user_by_wx_openid(club_id, openid) - created = False - - if not user_main: - 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, - ClubID='', # 勿用模型历史默认 xq,注册成功后再写 - ) - 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) + user_main, created = resolve_or_create_wx_user( + club_id, openid, self.shengchengYonghuID, unionid=unionid, + ) + if created: + UserBoss.query.create(user=user_main, nickname='微信用户') # 更新用户IP和最后登录时间 cunchu_ip = kehuduan_ip @@ -1282,14 +1264,16 @@ class WechatLoginAndGuanshiRegisterView(APIView): openid = wechat_data['openid'] kehuduan_ip = self._huoqu_kehuduan_ip(request) + from jituan.services.club_resolver import resolve_club_id_for_wechat_request + from jituan.services.club_user import ensure_user_club_id, resolve_or_create_wx_user + + club_id = resolve_club_id_for_wechat_request(request) + with transaction.atomic(): - user_main, created = User.objects.select_for_update().get_or_create( - OpenID=openid, - defaults={ - 'UserUID': self._shengcheng_yonghu_id(), - 'UserName': openid, - } + user_main, created = resolve_or_create_wx_user( + club_id, openid, self._shengcheng_yonghu_id, ) + ensure_user_club_id(user_main, club_id) user_main.IP = kehuduan_ip user_main.UserLastLoginDate = timezone.now() user_main.save()