"""俱乐部微信配置与 openid 解析。""" import logging import re import requests from django.conf import settings from jituan.constants import CLUB_ID_DEFAULT from jituan.models import Club logger = logging.getLogger(__name__) _REFERER_APPID_RE = re.compile(r'servicewechat\.com/(wx[0-9a-fA-F]+)/', re.I) def get_club_miniapp_appid(club): """小程序 AppID:登录 jscode2session 与 JSAPI 支付须一致。""" if not club: return '' return (club.wx_appid or club.pay_app_id or '').strip() def resolve_club_by_appid(app_id): if not app_id: return None app_id = app_id.strip() try: club = Club.query.filter(wx_appid=app_id, status=1).first() if club: return club return Club.query.filter(pay_app_id=app_id, status=1).first() except Exception: club = Club.objects.filter(wx_appid=app_id, status=1).first() if club: return club return Club.objects.filter(pay_app_id=app_id, status=1).first() def get_club_wx_credentials(club_id): """优先读 club 表;xq 缺省时回退 settings 全局配置(兼容现网)。""" club_id = club_id or CLUB_ID_DEFAULT try: club = Club.query.get(club_id=club_id) except Club.DoesNotExist: club = None appid = get_club_miniapp_appid(club) secret = (club.wx_secret or '').strip() if club else '' if appid and secret: return appid, secret, club if club_id == CLUB_ID_DEFAULT: return ( getattr(settings, 'WEIXIN_APPID', ''), getattr(settings, 'WEIXIN_SECRET', ''), club, ) 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 > body.inviteCode 反查管事 > JWT 用户归属 > 默认 xq。 老端 fighter.js 的 wdlyhdl 未带头时,靠 Referer 或邀请码识别星之界。 """ from jituan.services.club_context import resolve_club_id_from_request header = (request.META.get('HTTP_X_CLUB_ID') or '').strip() if not header and hasattr(request, 'headers'): header = (request.headers.get('X-Club-Id') or '').strip() if header: return header app_id = '' data = getattr(request, 'data', None) or {} if isinstance(data, dict): app_id = (data.get('appId') or data.get('app_id') or '').strip() if not app_id: referer = request.META.get('HTTP_REFERER', '') or '' matched = _REFERER_APPID_RE.search(referer) if matched: app_id = matched.group(1) if app_id: club = resolve_club_by_appid(app_id) 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) def wechat_jscode2session_for_request(request, code): """按请求上下文换取 openid(多俱乐部小程序)。""" club_id = resolve_club_id_for_wechat_request(request) return jscode2session(code, club_id=club_id) def jscode2session(code, club_id=None, app_id=None): """ 按俱乐部微信配置换取 openid。 club_id 与 app_id 二选一;app_id 用于从小程序 appId 反查 club。 """ club = None if app_id: club = resolve_club_by_appid(app_id) club_id = club.club_id if club else club_id appid, secret, club = get_club_wx_credentials(club_id) if not appid or not secret: return {'errmsg': '俱乐部微信配置未设置'} url = 'https://api.weixin.qq.com/sns/jscode2session' params = { 'appid': appid, 'secret': secret, 'js_code': code, 'grant_type': 'authorization_code', } try: response = requests.get(url, params=params, timeout=10) result = response.json() if 'openid' in result: return { 'openid': result['openid'], 'session_key': result.get('session_key', ''), 'unionid': result.get('unionid', ''), 'club_id': club_id or CLUB_ID_DEFAULT, 'wx_appid': appid, } errcode = result.get('errcode', 'unknown') errmsg = result.get('errmsg', '未知错误') return {'errmsg': f'[{errcode}]{errmsg}'} except requests.exceptions.Timeout: return {'errmsg': '请求微信接口超时'} except Exception as e: logger.exception('jscode2session 异常 club_id=%s', club_id) return {'errmsg': f'请求微信接口异常: {str(e)}'}