Files
Django/jituan/services/wechat_login.py
XingQue ce86b488d9 fix: 微信登录失败时返回明确错误,附属信息异常不阻断登录
避免 staff/订单统计异常导致整次 wechat-login 500;缺俱乐部配置时提示更清晰。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-22 21:00:09 +08:00

224 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""俱乐部微信登录(多 openid 绑定,不修改旧 wechatlogin 逻辑)。"""
import logging
import random
import time
import uuid
from django.db import transaction
from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
from jituan.constants import CLUB_ID_DEFAULT
from jituan.models import UserWxOpenid
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,
resolve_or_create_wx_user,
)
from users.business_models import User
from users.models import UserBoss, UserDashou, UserGuanshi, UserShangjia, UserZuzhang, UserShenheguan
from gvsdsdk.models import Role
logger = logging.getLogger(__name__)
def _generate_user_uid():
for _ in range(10):
timestamp_part = str(int(time.time()))[-5:].zfill(5)
random_part = str(random.randint(0, 99)).zfill(2)
user_id = timestamp_part + random_part
if len(user_id) == 7 and user_id.isdigit():
if not User.query.filter(UserUID=user_id).exists():
return user_id
raise RuntimeError('生成用户ID失败')
def _ensure_boss_profile(user):
try:
UserBoss.query.get(user=user)
except UserBoss.DoesNotExist:
UserBoss.query.create(user=user, nickname='板板大人')
def _ensure_consumer_role(user):
from users.business_models import UserRole as BizUserRole
normal_role = Role.objects.filter(RoleName='消费者').first()
if not normal_role:
return
exists = BizUserRole.objects.filter(UserUUID=user.UserUUID, RoleUUID=normal_role.RoleUUID).exists()
if not exists:
BizUserRole.objects.create(
UserRoleUUID=uuid.uuid4().bytes,
UserUUID=user.UserUUID,
RoleUUID=normal_role.RoleUUID,
CompanyUUID=uuid.UUID('b0000000-0000-0000-0000-000000000001').bytes,
)
def _collect_role_status(user):
dashou_status = None
shangjia_status = None
guanshi_status = None
zuzhang_status = None
kaoheguan_status = 0
boss_nickname = '微信用户'
try:
boss = UserBoss.query.get(user=user)
boss_nickname = boss.nickname or '微信用户'
except UserBoss.DoesNotExist:
pass
try:
UserDashou.query.get(user=user)
dashou_status = 1
except UserDashou.DoesNotExist:
dashou_status = None
try:
UserShangjia.query.get(user=user)
shangjia_status = 1
except UserShangjia.DoesNotExist:
shangjia_status = None
try:
UserGuanshi.query.get(user=user)
guanshi_status = 1
except UserGuanshi.DoesNotExist:
guanshi_status = None
try:
UserZuzhang.query.get(user=user)
zuzhang_status = 1
except UserZuzhang.DoesNotExist:
zuzhang_status = None
try:
kg = UserShenheguan.query.get(user=user)
kaoheguan_status = 1 if kg.zhuangtai == 1 else 0
except UserShenheguan.DoesNotExist:
kaoheguan_status = 0
return {
'dashou_status': dashou_status,
'shangjia_status': shangjia_status,
'guanshi_status': guanshi_status,
'zuzhang_status': zuzhang_status,
'kaoheguan_status': kaoheguan_status,
'boss_nickname': boss_nickname,
}
def login_or_register_by_club(code, club_id=None, app_id=None, client_ip=''):
"""
俱乐部微信登录核心逻辑。
- 通过 user_wx_openid(club_id, openid) 定位用户
- xq 俱乐部兼容:若绑定不存在但 User.OpenID 已存在,则自动建绑定(不新建用户)
"""
wx_data = jscode2session(code, club_id=club_id, app_id=app_id)
if not wx_data or 'openid' not in wx_data:
return None, wx_data.get('errmsg', '微信授权失败')
openid = wx_data['openid']
unionid = wx_data.get('unionid', '')
resolved_club_id = wx_data.get('club_id') or club_id or CLUB_ID_DEFAULT
with transaction.atomic():
binding = UserWxOpenid.query.filter(
club_id=resolved_club_id, openid=openid
).first()
user = None
created = False
if binding:
user = User.query.filter(UserUID=binding.yonghuid).first()
if not user:
user = User.objects.filter(UserUID=binding.yonghuid).first()
if not user:
user, created = resolve_or_create_wx_user(
resolved_club_id, openid, _generate_user_uid, unionid=unionid,
)
if created:
_ensure_boss_profile(user)
_ensure_consumer_role(user)
else:
ensure_wx_openid_binding(resolved_club_id, openid, user, unionid)
if unionid and unionid != (user.UnionID or ''):
user.UnionID = unionid
if client_ip:
user.IP = client_ip
user.UserLastLoginDate = timezone.now()
user.save()
ensure_user_club_id(user, resolved_club_id)
roles = _collect_role_status(user)
refresh = RefreshToken.for_user(user)
token = str(refresh.access_token)
staff_fields = {}
order_counts = {}
group_info = {}
try:
from merchant_ops.services.authz import staff_fields_for_login
staff_fields = staff_fields_for_login(user) or {}
except Exception:
logger.exception('staff_fields_for_login 失败 uid=%s', getattr(user, 'UserUID', ''))
try:
from users.views import WechatMiniProgramLoginView
login_view = WechatMiniProgramLoginView()
order_counts = login_view.jisuanOrderShuliang(user.UserUID) or {}
group_info = login_view.huoquQunPeizhi() or {}
except Exception:
logger.exception('登录附属信息失败 uid=%s', getattr(user, 'UserUID', ''))
data = {
'token': token,
'nicheng': roles['boss_nickname'],
'uid': user.UserUID,
'touxiang': user.Avatar or '',
'shangjiastatus': roles['shangjia_status'],
'dashoustatus': roles['dashou_status'],
'guanshistatus': roles['guanshi_status'],
'zuzhangstatus': roles['zuzhang_status'],
'kaoheguanstatus': roles['kaoheguan_status'],
'dingdantiaoshu': order_counts,
'dashouqun': group_info.get('dashouqun', ''),
'dashouqunid': group_info.get('dashouqunid', ''),
'guanshiqun': group_info.get('guanshiqun', ''),
'guanshiqunid': group_info.get('guanshiqunid', ''),
'club_id': resolved_club_id,
'is_new_user': created,
**staff_fields,
}
return data, None
def refresh_wx_openid_binding(code, user, club_id=None, app_id=None):
"""已登录用户:用当前小程序 code 刷新 user_wx_openid不换 JWT"""
wx_data = jscode2session(code, club_id=club_id, app_id=app_id)
if not wx_data or 'openid' not in wx_data:
return None, wx_data.get('errmsg', '微信授权失败')
openid = wx_data['openid']
unionid = wx_data.get('unionid', '')
resolved_club_id = wx_data.get('club_id') or club_id or CLUB_ID_DEFAULT
ensure_wx_openid_binding(resolved_club_id, openid, user, unionid)
ensure_user_club_id(user, resolved_club_id)
if unionid and unionid != (getattr(user, 'UnionID', None) or ''):
user.UnionID = unionid
user.save(update_fields=['UnionID'])
return {
'club_id': resolved_club_id,
'openid_preview': openid[:6] + '...' + openid[-4:] if len(openid) > 10 else openid,
'wx_appid': wx_data.get('wx_appid', ''),
}, None