小程序登录
All checks were successful
Deploy to Server / deploy (push) Successful in 26s

This commit is contained in:
jeremygan2021
2026-02-27 16:33:26 +08:00
parent 53bf74893b
commit 4f4cfcd6f4
3 changed files with 66 additions and 18 deletions

View File

@@ -45,7 +45,7 @@ def get_current_wechat_user(request):
except (BadSignature, SignatureExpired):
return None
def get_access_token(config=None):
def get_access_token(config=None, force_refresh=False):
"""
获取微信接口调用凭证 (client_credential)
"""
@@ -54,9 +54,10 @@ def get_access_token(config=None):
if config:
cache_key = f'wechat_access_token_{config.app_id}'
token = cache.get(cache_key)
if token:
return token
if not force_refresh:
token = cache.get(cache_key)
if token:
return token
if not config:
# 优先查找指定 AppID

View File

@@ -1016,6 +1016,15 @@ def wechat_login(request):
phone_res = requests.post(phone_url, json={'code': phone_code}, timeout=5)
phone_data = phone_res.json()
# Retry if access token is invalid or expired
if phone_data.get('errcode') in [40001, 40014, 42001]:
print(f"Access token invalid/expired ({phone_data.get('errcode')}), refreshing...")
access_token = get_access_token(config, force_refresh=True)
if access_token:
phone_url = f"https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={access_token}"
phone_res = requests.post(phone_url, json={'code': phone_code}, timeout=5)
phone_data = phone_res.json()
if phone_data.get('errcode') == 0:
phone_info = phone_data.get('phone_info')
phone_number = phone_info.get('purePhoneNumber')
@@ -1066,14 +1075,21 @@ def wechat_login(request):
dist.user = mp_user
dist.save()
# 4. 迁移用户信息(如果小程序用户没有昵称/头像继承Web用户的
if not mp_user.nickname and phone_user.nickname:
# 4. 迁移用户信息(优先使用Web用户的信息覆盖小程序的信息保持体验一致
if phone_user.nickname:
mp_user.nickname = phone_user.nickname
if not mp_user.avatar_url and phone_user.avatar_url:
if phone_user.avatar_url:
mp_user.avatar_url = phone_user.avatar_url
if mp_user.gender == 0 and phone_user.gender != 0:
if phone_user.gender != 0:
mp_user.gender = phone_user.gender
# 迁移关联的系统用户 (用于管理员权限等)
if phone_user.user and not mp_user.user:
mp_user.user = phone_user.user
# 清除旧对象的关联,防止唯一约束冲突(虽然即将删除,但为了安全)
phone_user.user = None
phone_user.save()
# 标记拥有Web徽章
mp_user.has_web_badge = True
mp_user.save()
@@ -1096,6 +1112,8 @@ def wechat_login(request):
# 只有当它是 Web 虚拟用户时,才覆盖 OpenID
if user.openid.startswith('web_') or not user.openid:
user.openid = openid
# 确保标记为拥有 Web 徽章 (虽然它本来就是 Web 用户转过来的)
user.has_web_badge = True
user.save()
elif user.openid != openid:
# 冲突: 手机号已被另一个真实 OpenID 绑定,但当前登录的是新的 OpenID
@@ -1134,8 +1152,20 @@ def wechat_login(request):
# 更新用户基本信息 (如果有传入)
if nickname:
user.nickname = nickname
elif not user.nickname:
# 默认昵称逻辑 (与 Web 端保持一致)
if user.phone_number:
user.nickname = f"User_{user.phone_number[-4:]}"
else:
user.nickname = f"WeChat_User_{user.openid[-4:]}"
if avatar_url:
user.avatar_url = avatar_url
elif not user.avatar_url:
# 默认头像逻辑
seed = user.phone_number or user.openid
user.avatar_url = f"https://api.dicebear.com/7.x/avataaars/svg?seed={seed}"
if gender is not None:
user.gender = gender
if country:
@@ -1338,6 +1368,23 @@ def bind_phone(request):
dist.user = current_user
dist.save()
# 6. 迁移用户信息 (优先使用 Web 用户信息)
if existing_user.nickname:
current_user.nickname = existing_user.nickname
if existing_user.avatar_url:
current_user.avatar_url = existing_user.avatar_url
if existing_user.gender != 0:
current_user.gender = existing_user.gender
# 7. 迁移系统用户关联
if existing_user.user and not current_user.user:
current_user.user = existing_user.user
existing_user.user = None
existing_user.save()
# 8. 标记 Web 徽章
current_user.has_web_badge = True
# 删除旧 Web 用户
existing_user.delete()

View File

@@ -318,14 +318,6 @@ export default function UserIndex() {
{userInfo && (
<View className='badges-row'>
{/* 明星技术用户 */}
{userInfo.is_star && (
<View className='badge star'>
<Text className='badge-icon'>🌟</Text>
<Text className='badge-text'></Text>
</View>
)}
{/* 管理员 */}
{userInfo.is_admin && (
<View className='badge admin'>
@@ -333,9 +325,17 @@ export default function UserIndex() {
<Text className='badge-text'></Text>
</View>
)}
{/* 明星技术用户/专家 */}
{userInfo.is_star && (
<View className='badge star'>
<Text className='badge-icon'>🌟</Text>
<Text className='badge-text'>{userInfo.title || '技术专家'}</Text>
</View>
)}
{/* 网页用户徽章 - 仅在 has_web_badge 为 true 时显示 */}
{userInfo.has_web_badge && (
{/* 网页用户徽章 */}
{(userInfo.has_web_badge || userInfo.has_web_account) && (
<View className='badge web active'>
<Text className='badge-icon'>🌐</Text>
<Text className='badge-text'></Text>