Parcourir la source

支付与登录接口合并完成

Zhangbw il y a 2 mois
Parent
commit
2847ff1835
3 fichiers modifiés avec 219 ajouts et 33 suppressions
  1. 10 0
      src/components/UserInfoPopup.vue
  2. 75 22
      src/utils/api.js
  3. 134 11
      src/utils/auth.js

+ 10 - 0
src/components/UserInfoPopup.vue

@@ -61,6 +61,16 @@ export default {
   },
 
   methods: {
+    /**
+     * 获取图片URL(兼容方法)
+     */
+    getImageUrl(url) {
+      if (typeof getImageUrl === 'function') {
+        return getImageUrl(url)
+      }
+      // 降级处理:如果函数不可用,直接返回URL
+      return url || '/static/images/head.png'
+    },
     /**
      * 打开弹窗
      * @param userData 可选,后端返回的用户数据(新流程中不再需要)

+ 75 - 22
src/utils/api.js

@@ -143,6 +143,7 @@ const request = (options) => {
 
 // ==================== H5公众号认证API ====================
 
+// #ifdef H5
 /**
  * 获取微信授权URL
  * @param {string} redirectUrl - 授权后的回调地址
@@ -197,6 +198,59 @@ export const h5PhoneLogin = (params) => {
     data: params
   })
 }
+// #endif
+
+// ==================== 微信小程序认证API ====================
+
+// #ifdef MP-WEIXIN
+/**
+ * 第一步:微信静默登录接口(老用户)
+ * @param {object} params - { loginCode }
+ * @returns {Promise} 返回 { isSign, token?, code? }
+ */
+export const wxSilentLoginApi = (params) => {
+  return request({
+    url: '/v1/auth/wx/silent-login',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: params
+  })
+}
+
+/**
+ * 第二步:手机号授权登录接口(新用户验证手机号)
+ * @param {object} params - { loginCode, phoneCode, encryptedData, iv }
+ * @returns {Promise} 返回 { isSign, token?, openid?, unionid?, phoneNumber? }
+ */
+export const wxPhoneLoginApi = (params) => {
+  return request({
+    url: '/v1/auth/wx/phone-verify',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: params
+  })
+}
+
+/**
+ * 第三步:完善用户信息接口(首次登录)
+ * @param {object} params - { openid, unionid, phoneNumber, nickname, avatarUrl }
+ * @returns {Promise} 返回 { token }
+ */
+export const wxCompleteUserInfoApi = (params) => {
+  return request({
+    url: '/v1/auth/wx/register',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: params
+  })
+}
+// #endif
 
 /**
  * 获取用户完整信息接口
@@ -439,27 +493,8 @@ export const wxPay = (payParams) => {
   }
 
   return new Promise((resolve, reject) => {
-    // 运行时判断环境
-    if (typeof WeixinJSBridge !== 'undefined') {
-      // H5环境使用WeixinJSBridge调起支付
-      console.log('使用 WeixinJSBridge 调起支付')
-      WeixinJSBridge.invoke('getBrandWCPayRequest', {
-        appId: payParams.appId || '',
-        timeStamp: payParams.timeStamp,
-        nonceStr: payParams.nonceStr,
-        package: payParams.packageValue,
-        signType: payParams.signType,
-        paySign: payParams.paySign
-      }, (res) => {
-        if (res.err_msg === 'get_brand_wcpay_request:ok') {
-          resolve(res)
-        } else if (res.err_msg === 'get_brand_wcpay_request:cancel') {
-          reject(new Error('用户取消支付'))
-        } else {
-          reject(new Error('支付失败:' + res.err_msg))
-        }
-      })
-    } else if (typeof uni !== 'undefined' && uni.requestPayment) {
+    // 优先判断小程序环境(避免在小程序中误用 WeixinJSBridge)
+    if (typeof uni !== 'undefined' && uni.requestPayment) {
       // 小程序环境使用uni.requestPayment
       console.log('使用 uni.requestPayment 调起支付')
       uni.requestPayment({
@@ -469,7 +504,6 @@ export const wxPay = (payParams) => {
         package: payParams.packageValue,
         signType: payParams.signType,
         paySign: payParams.paySign,
-        totalFee: totalFee,
         success: (res) => {
           resolve(res)
         },
@@ -482,6 +516,25 @@ export const wxPay = (payParams) => {
           }
         }
       })
+    } else if (typeof WeixinJSBridge !== 'undefined') {
+      // H5环境使用WeixinJSBridge调起支付
+      console.log('使用 WeixinJSBridge 调起支付')
+      WeixinJSBridge.invoke('getBrandWCPayRequest', {
+        appId: payParams.appId || '',
+        timeStamp: payParams.timeStamp,
+        nonceStr: payParams.nonceStr,
+        package: payParams.packageValue,
+        signType: payParams.signType,
+        paySign: payParams.paySign
+      }, (res) => {
+        if (res.err_msg === 'get_brand_wcpay_request:ok') {
+          resolve(res)
+        } else if (res.err_msg === 'get_brand_wcpay_request:cancel') {
+          reject(new Error('用户取消支付'))
+        } else {
+          reject(new Error('支付失败:' + res.err_msg))
+        }
+      })
     } else {
       reject(new Error('当前环境不支持支付'))
     }

+ 134 - 11
src/utils/auth.js

@@ -1,23 +1,25 @@
 /**
- * H5公众号认证工具类
+ * 认证工具类
  * 用于管理用户登录状态、token存储和登录检查
- *
- * H5公众号登录注册流程:
- * 1. 用户选择"登录"或"注册"
- * 2. 获取微信授权URL并重定向
- * 3. 用户授权后微信回调,获取code
- * 4. 通过code获取用户信息(openid、昵称、头像)
- * 5. 前端检查openid是否已注册
- * 6. 用户输入手机号和密码
- * 7. 登录:验证openid+手机号+密码
- * 8. 注册:保存openid+手机号+密码+用户信息
+ * 支持H5公众号和微信小程序双端
  */
 
+// #ifdef H5
 import {
   getH5AuthUrl,
   h5AuthCallback,
   getUserInfoApi
 } from './api.js'
+// #endif
+
+// #ifdef MP-WEIXIN
+import {
+  wxSilentLoginApi,
+  wxPhoneLoginApi,
+  wxCompleteUserInfoApi,
+  getUserInfoApi
+} from './api.js'
+// #endif
 
 const TOKEN_KEY = 'user_token'
 const USER_INFO_KEY = 'user_info'
@@ -292,3 +294,124 @@ export const checkLogin = () => {
 
   return false
 }
+
+// #ifdef MP-WEIXIN
+/**
+ * 第一步:微信静默登录(老用户)
+ * 调用 wx.login() 获取 code,发送到后端检查是否为老用户
+ *
+ * @param {string} loginCode - 微信登录code
+ * @returns {Promise<object>} 返回 { isSign, token?, code? }
+ */
+export const wxSilentLogin = async (loginCode) => {
+  try {
+    console.log('[静默登录] 开始检查用户状态, code:', loginCode)
+
+    const result = await wxSilentLoginApi({ loginCode })
+
+    console.log('[静默登录] 后端响应:', result)
+
+    // 老用户直接登录成功
+    if (result.code === 200 && result.data.isSign === 'true' && result.data.token) {
+      setToken(result.data.token)
+      console.log('[静默登录] 老用户登录成功')
+
+      // 获取用户信息
+      await fetchAndSaveUserInfo()
+
+      // 启动状态检查
+      startStatusCheck()
+    }
+
+    return result.data
+  } catch (error) {
+    console.error('[静默登录] 失败:', error)
+    throw error
+  }
+}
+
+/**
+ * 第二步:手机号授权登录(新用户)
+ * 获取手机号授权后,发送到后端验证
+ *
+ * @param {object} params - 包含 loginCode, phoneCode, encryptedData, iv
+ * @returns {Promise<object>} 返回 { isSign, token?, openid?, unionid?, phoneNumber? }
+ */
+export const wxPhoneLogin = async (params) => {
+  try {
+    console.log('[手机号登录] 开始验证手机号')
+
+    const result = await wxPhoneLoginApi(params)
+
+    console.log('[手机号登录] 后端响应:', result)
+
+    // 已注册用户直接登录成功
+    if (result.code === 200 && result.data.isSign === 'true' && result.data.token) {
+      setToken(result.data.token)
+      console.log('[手机号登录] 已注册用户登录成功')
+
+      // 获取用户信息
+      await fetchAndSaveUserInfo()
+
+      // 启动状态检查
+      startStatusCheck()
+    }
+
+    return result.data
+  } catch (error) {
+    console.error('[手机号登录] 失败:', error)
+    throw error
+  }
+}
+
+/**
+ * 第三步:完善用户信息(首次登录)
+ * 提交完整的用户信息完成注册
+ *
+ * @param {object} userInfo - 包含 openid, unionid, phoneNumber, nickname, avatarUrl
+ * @returns {Promise<boolean>} 注册是否成功
+ */
+export const wxCompleteUserInfo = async (userInfo) => {
+  try {
+    console.log('[完善信息] 提交用户信息')
+
+    const result = await wxCompleteUserInfoApi(userInfo)
+
+    console.log('[完善信息] 后端响应:', result)
+
+    if (result.code === 200 && result.data.token) {
+      setToken(result.data.token)
+      console.log('[完善信息] 注册成功')
+
+      // 获取用户信息
+      await fetchAndSaveUserInfo()
+
+      // 启动状态检查
+      startStatusCheck()
+
+      return true
+    } else {
+      throw new Error(result.message || '注册失败')
+    }
+  } catch (error) {
+    console.error('[完善信息] 失败:', error)
+    throw error
+  }
+}
+
+/**
+ * 获取并保存用户信息
+ * 使用 token 调用后端接口获取完整用户信息
+ */
+const fetchAndSaveUserInfo = async () => {
+  try {
+    const result = await getUserInfoApi()
+    if (result.code === 200 && result.data) {
+      setUserInfo(result.data)
+      console.log('[用户信息] 保存成功')
+    }
+  } catch (error) {
+    console.warn('[用户信息] 获取失败:', error)
+  }
+}
+// #endif