From 4f4cfcd6f44a7de3d5484d00a44b13cb6b3c331f Mon Sep 17 00:00:00 2001 From: jeremygan2021 Date: Fri, 27 Feb 2026 16:33:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/shop/utils.py | 9 +++-- backend/shop/views.py | 55 ++++++++++++++++++++++++++-- miniprogram/src/pages/user/index.tsx | 20 +++++----- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/backend/shop/utils.py b/backend/shop/utils.py index c0e9b69..8f8dfc9 100644 --- a/backend/shop/utils.py +++ b/backend/shop/utils.py @@ -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 diff --git a/backend/shop/views.py b/backend/shop/views.py index 7bd1d99..90d71cb 100644 --- a/backend/shop/views.py +++ b/backend/shop/views.py @@ -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() diff --git a/miniprogram/src/pages/user/index.tsx b/miniprogram/src/pages/user/index.tsx index a0f272e..20c2de6 100644 --- a/miniprogram/src/pages/user/index.tsx +++ b/miniprogram/src/pages/user/index.tsx @@ -318,14 +318,6 @@ export default function UserIndex() { {userInfo && ( - {/* 明星技术用户 */} - {userInfo.is_star && ( - - 🌟 - 技术专家 - - )} - {/* 管理员 */} {userInfo.is_admin && ( @@ -333,9 +325,17 @@ export default function UserIndex() { 管理员 )} + + {/* 明星技术用户/专家 */} + {userInfo.is_star && ( + + 🌟 + {userInfo.title || '技术专家'} + + )} - {/* 网页用户徽章 - 仅在 has_web_badge 为 true 时显示 */} - {userInfo.has_web_badge && ( + {/* 网页用户徽章 */} + {(userInfo.has_web_badge || userInfo.has_web_account) && ( 🌐 网页用户