Просмотр исходного кода

fix: 优化用户认证流程与微信 openid 绑定

- 完善手机号登录时 openid 绑定逻辑
- 优化前端 store 中的用户信息处理
- 增强 auth.controller.ts 参数验证

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MyFramework User 3 месяцев назад
Родитель
Сommit
3dfc1cea27

+ 31 - 1
my-uniapp-vue3/src/App.vue

@@ -27,9 +27,39 @@ async function handleWechatOAuthCallback() {
     const result = await post<{ openid: string }>('/payment/wechat/openid', { code });
 
     if (result.openid) {
-      localStorage.setItem('wx_openid', result.openid);
       console.log('[App] OpenID 获取成功');
 
+      // 先尝试根据 openid 自动登录
+      try {
+        const userStore = useUserStore();
+        await userStore.loginByOpenid(result.openid);
+        console.log('[App] OpenID 已绑定,自动登录成功');
+
+        // 清除 URL 参数并跳转首页
+        searchParams.delete('code');
+        searchParams.delete('state');
+        const newSearch = searchParams.toString();
+        const newUrl = window.location.origin + window.location.pathname
+          + (newSearch ? '?' + newSearch : '');
+        window.history.replaceState({}, '', newUrl);
+        uni.hideLoading();
+        uni.showToast({ title: '自动登录成功', icon: 'success' });
+        setTimeout(() => {
+          uni.switchTab({ url: '/pages/index/index' });
+        }, 500);
+        return;
+      } catch (e: any) {
+        if (e?.message?.includes('未绑定') || e?.code === -1) {
+          // 自动登录失败(未绑定账号),继续正常流程
+          console.log('[App] OpenID 未绑定账号,将存储待登录');
+        } else {
+          console.error('[App] 自动登录异常:', e);
+        }
+      }
+
+      // 未绑定账号或自动登录失败,存储 openid 等待手机号登录绑定
+      localStorage.setItem('wx_openid', result.openid);
+
       // 🔧 标记刚从 OAuth 回来,页面组件可据此自动触发支付
       try { sessionStorage.setItem('wx_oauth_just_done', '1'); } catch (_) {}
 

+ 17 - 0
my-uniapp-vue3/src/store/user.ts

@@ -101,6 +101,22 @@ export const useUserStore = defineStore('user', () => {
     }
   }
 
+  // 根据 openid 自动登录
+  async function loginByOpenid(openid: string) {
+    const result = await post<{
+      token: string;
+      user: UserInfo;
+    }>('/auth/login-by-openid', { openid });
+
+    token.value = result.token;
+    userInfo.value = result.user;
+    setToken(result.token);
+    setUserInfo(result.user);
+
+    await fetchMemberStatus();
+    return result;
+  }
+
   return {
     // 状态
     token,
@@ -112,6 +128,7 @@ export const useUserStore = defineStore('user', () => {
     // 方法
     initUser,
     login,
+    loginByOpenid,
     sendCode,
     fetchUserInfo,
     fetchMemberStatus,

+ 26 - 0
server/src/modules/auth/auth.controller.ts

@@ -52,6 +52,32 @@ router.post('/login', async (ctx: Context) => {
   };
 });
 
+// 根据 openid 自动登录(公众号场景)
+router.post('/login-by-openid', async (ctx: Context) => {
+  const { openid } = ctx.request.body as { openid: string };
+
+  if (!openid) {
+    throw new BadRequestError('openid 不能为空');
+  }
+
+  const result = await AuthService.loginWithOpenid(openid);
+
+  if (!result) {
+    ctx.body = {
+      code: -1,
+      message: '该微信未绑定账号,请先登录绑定',
+      data: null,
+    };
+    return;
+  }
+
+  ctx.body = {
+    code: 0,
+    message: '自动登录成功',
+    data: result,
+  };
+});
+
 // 获取用户信息
 router.get('/user-info', authMiddleware, async (ctx: Context) => {
   const userId = ctx.state.user.userId;

+ 34 - 0
server/src/modules/auth/auth.service.ts

@@ -114,6 +114,40 @@ export async function loginWithPhone(phone: string, code?: string, inviteCode?:
   };
 }
 
+// 根据 openid 自动登录(公众号场景)
+export async function loginWithOpenid(openid: string): Promise<{
+  token: string;
+  user: {
+    id: string;
+    phone: string;
+    nickname: string;
+    avatar: string;
+    memberLevel: number;
+    isNewUser: boolean;
+  };
+} | null> {
+  if (!openid) return null;
+
+  const user = await prisma.user.findFirst({ where: { openid } });
+  if (!user) {
+    return null; // openid 未绑定任何账号
+  }
+
+  const token = generateToken(user.id.toString(), user.phone || undefined);
+
+  return {
+    token,
+    user: {
+      id: user.id.toString(),
+      phone: user.phone || '',
+      nickname: user.nickname,
+      avatar: user.avatar,
+      memberLevel: user.memberLevel,
+      isNewUser: false,
+    },
+  };
+}
+
 // 获取用户信息
 export async function getUserInfo(userId: string) {
   const user = await prisma.user.findUnique({ where: { id: parseInt(userId) } });