fix(login): 恢复手机号快捷登录功能,避免带旧Token导致失败

- 将 login.tsx 和 register.tsx 中登录请求从封装的 request 工具改回 Taro.request
- 取消登录请求中自动注入 Authorization 头,避免携带过期/旧 token 导致认证失败
- 调整登录接口响应数据解析,适配 Taro.request 的数据结构
- fetchUserInfo 改回使用 Taro.request,手动携带 Authorization 头部
- 添加全局 getPhoneNumber 调用冷却机制,防止频繁调用导致微信报错
- 登录页新增手机授权冷却
This commit is contained in:
2026-07-16 17:01:17 +08:00
parent ce1e2bd4dd
commit 46298c011f
9 changed files with 322 additions and 45 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import Taro from '@tarojs/taro'
import { View, Image, Text, Button } from '@tarojs/components'
import { TenantId } from '@/config/app'
@@ -7,6 +7,11 @@ import { getUserInfo } from '@/api/layout'
import { saveStorageByLoginUser, SERVER_API_URL } from '@/utils/server'
import { isUserDisabled } from '@/utils/auth'
import request from '@/utils/request'
import {
getPhoneAuthCooldownRemaining,
isPhoneAuthCoolingDown,
markPhoneAuthCalled,
} from '@/utils/phoneAuth'
import {
checkAndHandleInviteRelation,
hasPendingInvite,
@@ -73,21 +78,22 @@ async function ensureWxOpenIdSaved() {
}
}
/** 获取用户信息(使用临时 token避免本地 storage 尚未写入) */
/** 获取用户信息 */
async function fetchUserInfo(token: string) {
try {
const res: any = await request.get(
`${SERVER_API_URL}/auth/user`,
{},
{
header: {
Authorization: `Bearer ${token}`,
},
showError: false,
}
)
if (res?.code === 0 && res?.data) {
return res.data
const res: any = await Taro.request({
url: `${SERVER_API_URL}/auth/user`,
method: 'GET',
header: {
Authorization: token,
'content-type': 'application/json',
TenantId: String(TenantId),
},
timeout: 15000,
})
if (res.data?.code === 0 && res.data?.data) {
return res.data.data
}
return null
} catch (e) {
@@ -99,16 +105,39 @@ async function fetchUserInfo(token: string) {
const Login = () => {
const [isAgree, setIsAgree] = useState(false)
const [loading, setLoading] = useState(false)
const [phoneAuthCooling, setPhoneAuthCooling] = useState(isPhoneAuthCoolingDown())
const [showContent, setShowContent] = useState(false)
const phoneAuthInFlightRef = useRef(false)
const phoneAuthTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const router = Taro.getCurrentInstance().router
const isWeapp = IS_WEAPP
const canUsePhoneAuth = isAgree && !loading && !phoneAuthCooling
/** 页面加载动画 */
useEffect(() => {
setTimeout(() => setShowContent(true), 100)
}, [])
useEffect(() => {
return () => {
if (phoneAuthTimerRef.current) {
clearTimeout(phoneAuthTimerRef.current)
phoneAuthTimerRef.current = null
}
}
}, [])
const startPhoneAuthCooldown = () => {
const remaining = Math.max(getPhoneAuthCooldownRemaining(), 300)
setPhoneAuthCooling(true)
if (phoneAuthTimerRef.current) clearTimeout(phoneAuthTimerRef.current)
phoneAuthTimerRef.current = setTimeout(() => {
phoneAuthTimerRef.current = null
setPhoneAuthCooling(false)
}, remaining)
}
/** 解析 redirect 参数 */
const redirectUrl = (() => {
const raw = (router?.params as Record<string, string> | undefined)?.redirect
@@ -201,10 +230,19 @@ const Login = () => {
Taro.showToast({ title: '请先勾选同意协议', icon: 'none' })
return
}
if (loading) return
if (loading || phoneAuthInFlightRef.current || isPhoneAuthCoolingDown()) {
startPhoneAuthCooldown()
Taro.showToast({ title: '请稍后再试', icon: 'none' })
return
}
const { code: phoneCode, errMsg } = detail || {}
phoneAuthInFlightRef.current = true
markPhoneAuthCalled()
startPhoneAuthCooldown()
const { code: phoneCode, encryptedData, iv, errMsg } = detail || {}
if (!phoneCode || (errMsg && errMsg.includes('fail'))) {
phoneAuthInFlightRef.current = false
showPhoneAuthFailedModal(errMsg)
return
}
@@ -219,12 +257,19 @@ const Login = () => {
`${SERVER_API_URL}/wx-login/loginByMpWxPhone`,
{
code: phoneCode,
encryptedData,
iv,
notVerifyPhone: true,
refereeId,
sceneType: 'save_referee',
tenantId: Number(TenantId),
},
{ showError: false }
{
timeout: 20000,
retry: 0,
showError: false,
returnRaw: true,
}
)
if (res?.code === 0 && res?.data?.access_token) {
@@ -266,6 +311,7 @@ const Login = () => {
console.error('微信登录失败:', e)
Taro.showToast({ title: e?.message || '登录失败', icon: 'none' })
} finally {
phoneAuthInFlightRef.current = false
setLoading(false)
}
}
@@ -309,9 +355,9 @@ const Login = () => {
margin: 0,
padding: 0,
}}
openType='getPhoneNumber'
openType={canUsePhoneAuth ? 'getPhoneNumber' : undefined}
onGetPhoneNumber={handleGetPhoneNumber}
disabled={!isAgree || loading}
disabled={!canUsePhoneAuth}
loading={loading}
>

View File

@@ -4,9 +4,8 @@ import { View, Text } from '@tarojs/components'
import { Button, Checkbox } from '@nutui/nutui-react-taro'
import { TenantId } from '@/config/app'
import { getUserInfo, getWxOpenId } from '@/api/layout'
import { saveStorageByLoginUser, SERVER_API_URL } from '@/utils/server'
import { saveStorageByLoginUser } from '@/utils/server'
import { isUserDisabled } from '@/utils/auth'
import request from '@/utils/request'
import {
getStoredInviteParams,
parseInviteParams,
@@ -26,6 +25,17 @@ interface GetPhoneNumberEvent {
detail: GetPhoneNumberDetail
}
interface LoginResponse {
data: {
code?: number
message?: string
data?: {
access_token: string
user: any
}
}
}
async function getWeappLoginCode(): Promise<string | undefined> {
try {
const res = await new Promise<{ code?: string }>((resolve, reject) => {
@@ -181,9 +191,10 @@ const Register = () => {
// 获取小程序登录 code用于后续绑定 openid
const wxLoginCode = await getWeappLoginCode()
const res: any = await request.post(
`${SERVER_API_URL}/wx-login/loginByMpWxPhone`,
{
const res = (await Taro.request({
url: 'https://shop-api.websoft.top/api/wx-login/loginByMpWxPhone',
method: 'POST',
data: {
code: phoneCode,
encryptedData,
iv,
@@ -192,16 +203,19 @@ const Register = () => {
sceneType: 'save_referee',
tenantId: TenantId,
},
{ showError: false }
)
header: {
'content-type': 'application/json',
TenantId,
},
})) as unknown as LoginResponse
if (res?.code === 1) {
Taro.showToast({ title: res.message || '登录失败', icon: 'none' })
if ((res as any)?.data?.code === 1) {
Taro.showToast({ title: res.data.message || '登录失败', icon: 'none' })
return
}
const token = res?.data?.access_token
const user = res?.data?.user
const token = res?.data?.data?.access_token
const user = res?.data?.data?.user
if (!token || !user?.userId) {
Taro.showToast({ title: '登录失败,请重试', icon: 'none' })
return