This commit is contained in:
@@ -45,7 +45,7 @@ def get_current_wechat_user(request):
|
|||||||
except (BadSignature, SignatureExpired):
|
except (BadSignature, SignatureExpired):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_access_token(config=None):
|
def get_access_token(config=None, force_refresh=False):
|
||||||
"""
|
"""
|
||||||
获取微信接口调用凭证 (client_credential)
|
获取微信接口调用凭证 (client_credential)
|
||||||
"""
|
"""
|
||||||
@@ -54,9 +54,10 @@ def get_access_token(config=None):
|
|||||||
if config:
|
if config:
|
||||||
cache_key = f'wechat_access_token_{config.app_id}'
|
cache_key = f'wechat_access_token_{config.app_id}'
|
||||||
|
|
||||||
token = cache.get(cache_key)
|
if not force_refresh:
|
||||||
if token:
|
token = cache.get(cache_key)
|
||||||
return token
|
if token:
|
||||||
|
return token
|
||||||
|
|
||||||
if not config:
|
if not config:
|
||||||
# 优先查找指定 AppID
|
# 优先查找指定 AppID
|
||||||
|
|||||||
@@ -1016,6 +1016,15 @@ def wechat_login(request):
|
|||||||
phone_res = requests.post(phone_url, json={'code': phone_code}, timeout=5)
|
phone_res = requests.post(phone_url, json={'code': phone_code}, timeout=5)
|
||||||
phone_data = phone_res.json()
|
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:
|
if phone_data.get('errcode') == 0:
|
||||||
phone_info = phone_data.get('phone_info')
|
phone_info = phone_data.get('phone_info')
|
||||||
phone_number = phone_info.get('purePhoneNumber')
|
phone_number = phone_info.get('purePhoneNumber')
|
||||||
@@ -1066,14 +1075,21 @@ def wechat_login(request):
|
|||||||
dist.user = mp_user
|
dist.user = mp_user
|
||||||
dist.save()
|
dist.save()
|
||||||
|
|
||||||
# 4. 迁移用户信息(如果小程序用户没有昵称/头像,继承Web用户的)
|
# 4. 迁移用户信息(优先使用Web用户的信息,覆盖小程序的信息,保持体验一致)
|
||||||
if not mp_user.nickname and phone_user.nickname:
|
if phone_user.nickname:
|
||||||
mp_user.nickname = 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
|
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
|
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徽章
|
# 标记拥有Web徽章
|
||||||
mp_user.has_web_badge = True
|
mp_user.has_web_badge = True
|
||||||
mp_user.save()
|
mp_user.save()
|
||||||
@@ -1096,6 +1112,8 @@ def wechat_login(request):
|
|||||||
# 只有当它是 Web 虚拟用户时,才覆盖 OpenID
|
# 只有当它是 Web 虚拟用户时,才覆盖 OpenID
|
||||||
if user.openid.startswith('web_') or not user.openid:
|
if user.openid.startswith('web_') or not user.openid:
|
||||||
user.openid = openid
|
user.openid = openid
|
||||||
|
# 确保标记为拥有 Web 徽章 (虽然它本来就是 Web 用户转过来的)
|
||||||
|
user.has_web_badge = True
|
||||||
user.save()
|
user.save()
|
||||||
elif user.openid != openid:
|
elif user.openid != openid:
|
||||||
# 冲突: 手机号已被另一个真实 OpenID 绑定,但当前登录的是新的 OpenID
|
# 冲突: 手机号已被另一个真实 OpenID 绑定,但当前登录的是新的 OpenID
|
||||||
@@ -1134,8 +1152,20 @@ def wechat_login(request):
|
|||||||
# 更新用户基本信息 (如果有传入)
|
# 更新用户基本信息 (如果有传入)
|
||||||
if nickname:
|
if nickname:
|
||||||
user.nickname = 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:
|
if avatar_url:
|
||||||
user.avatar_url = 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:
|
if gender is not None:
|
||||||
user.gender = gender
|
user.gender = gender
|
||||||
if country:
|
if country:
|
||||||
@@ -1338,6 +1368,23 @@ def bind_phone(request):
|
|||||||
dist.user = current_user
|
dist.user = current_user
|
||||||
dist.save()
|
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 用户
|
# 删除旧 Web 用户
|
||||||
existing_user.delete()
|
existing_user.delete()
|
||||||
|
|
||||||
|
|||||||
@@ -318,14 +318,6 @@ export default function UserIndex() {
|
|||||||
|
|
||||||
{userInfo && (
|
{userInfo && (
|
||||||
<View className='badges-row'>
|
<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 && (
|
{userInfo.is_admin && (
|
||||||
<View className='badge admin'>
|
<View className='badge admin'>
|
||||||
@@ -333,9 +325,17 @@ export default function UserIndex() {
|
|||||||
<Text className='badge-text'>管理员</Text>
|
<Text className='badge-text'>管理员</Text>
|
||||||
</View>
|
</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'>
|
<View className='badge web active'>
|
||||||
<Text className='badge-icon'>🌐</Text>
|
<Text className='badge-icon'>🌐</Text>
|
||||||
<Text className='badge-text'>网页用户</Text>
|
<Text className='badge-text'>网页用户</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user