91 lines
3.2 KiB
Python
91 lines
3.2 KiB
Python
"""迁移码:按目标俱乐部 AppID 生成无限小程序码并上传 COS。"""
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
import logging
|
|
import time
|
|
from typing import Optional, Tuple
|
|
|
|
import requests
|
|
from django.conf import settings
|
|
|
|
from jituan.services.club_migrate import SCENE_PREFIX, scene_for_token
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
RECEIVE_PAGE = 'pages/migrate/receive'
|
|
|
|
|
|
def generate_migrate_qrcode(*, to_club_id: str, token: str) -> Tuple[str, str]:
|
|
"""返回 (relative_url, err)。成功时 err 为空。"""
|
|
from utils.weixin_token import get_club_mini_access_token, is_weixin_token_invalid
|
|
|
|
club_id = (to_club_id or '').strip()
|
|
if not club_id:
|
|
return '', '缺少目标俱乐部'
|
|
scene = scene_for_token(token)
|
|
if len(scene) > 32:
|
|
return '', 'scene 超长'
|
|
if not scene.startswith(SCENE_PREFIX):
|
|
return '', 'scene 非法'
|
|
|
|
access_token = get_club_mini_access_token(club_id)
|
|
if not access_token:
|
|
return '', f'俱乐部 {club_id} 获取微信 token 失败'
|
|
|
|
post_data = {
|
|
'scene': scene,
|
|
'page': RECEIVE_PAGE,
|
|
'width': 430,
|
|
'auto_color': False,
|
|
'line_color': {'r': 0, 'g': 0, 'b': 0},
|
|
'check_path': False,
|
|
}
|
|
|
|
def _req(tok: str):
|
|
url = f'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={tok}'
|
|
return requests.post(url, json=post_data, timeout=15)
|
|
|
|
wx_resp = _req(access_token)
|
|
if wx_resp.headers.get('content-type', '').startswith('application/json'):
|
|
try:
|
|
err = wx_resp.json()
|
|
if is_weixin_token_invalid(err.get('errcode'), err.get('errmsg', '')):
|
|
access_token = get_club_mini_access_token(club_id, force_refresh=True)
|
|
if access_token:
|
|
wx_resp = _req(access_token)
|
|
except Exception:
|
|
pass
|
|
|
|
content_type = wx_resp.headers.get('content-type', '')
|
|
if wx_resp.status_code != 200 or 'image' not in content_type:
|
|
errmsg = '未知错误'
|
|
try:
|
|
error_info = wx_resp.json()
|
|
errmsg = error_info.get('errmsg', errmsg)
|
|
logger.error('migrate qr fail club=%s err=%s', club_id, error_info)
|
|
except Exception:
|
|
logger.error('migrate qr fail club=%s status=%s', club_id, wx_resp.status_code)
|
|
return '', f'生成小程序码失败: {errmsg}'
|
|
|
|
from utils.oss_utils import upload_to_oss
|
|
|
|
filename = f"migrate_{club_id}_{token}_{int(time.time())}.png"
|
|
oss_path = f"club_migrate/{club_id}/{filename}"
|
|
file_obj = io.BytesIO(wx_resp.content)
|
|
try:
|
|
full_url = upload_to_oss(file_obj, oss_path, content_type='image/png')
|
|
except Exception as e:
|
|
logger.exception('migrate qr upload fail: %s', e)
|
|
return '', f'上传小程序码失败: {e}'
|
|
if not full_url:
|
|
return '', '上传小程序码失败'
|
|
|
|
# 前端保存相册必须用 https 完整链;相对路径在 oss 未就绪时会保存失败
|
|
if str(full_url).startswith('http'):
|
|
return str(full_url), ''
|
|
cos_domain = getattr(settings, 'COS_DOMAIN', '').rstrip('/')
|
|
if cos_domain:
|
|
return f'{cos_domain}/{str(full_url).lstrip("/")}', ''
|
|
return str(full_url), ''
|