SEC_Warehouse/src/pages/login/wxLoginFlow.ts

93 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Taro from '@tarojs/taro'
import { wxMiniappLogin, type WxMiniappLoginResponse } from '@/api/auth'
import { storage } from '@/utils/storage'
import { WX_OPENID_STORAGE_KEY, FALLBACK_WX_APP_ID } from '@/constants/auth'
// 取小程序 appId:正常环境走 accountInfo,异常(部分基座不支持)回退常量
function resolveAppId(): string {
try {
return Taro.getAccountInfoSync().miniProgram.appId || FALLBACK_WX_APP_ID
} catch {
return FALLBACK_WX_APP_ID
}
}
// 登录步骤子状态(http 层把 200 内 Message 附加到返回值 message 字段)
export type LoginSubState = 'needUserInfo' | 'noMobile' | 'complete'
export function getLoginSubState(res: WxMiniappLoginResponse): LoginSubState {
if (res.message === 'needUserInfo') return 'needUserInfo'
if (res.message === 'noMobile') return 'noMobile'
return 'complete'
}
// 微信一键登录主流程:
// Taro.login 换一次性 code → 组装 appId/openId → 调 miniapp/login → 缓存响应中的 openId
// 首次登录后端可能分步返回 needUserInfo / noMobile(仅绑定凭证,无 token),逐步授权后重调补全
export async function performWxLogin(): Promise<WxMiniappLoginResponse> {
// Taro.login reject 时返回 { errMsg } 形状对象而非 Error,此处归一化为带 message 的 Error
let code: string
try {
const r = await Taro.login()
code = r.code
} catch (e) {
throw new Error(e instanceof Error ? e.message : '微信登录失败')
}
if (!code) throw new Error('获取微信登录凭证失败')
const openId = storage.get<string>(WX_OPENID_STORAGE_KEY) ?? undefined
console.log('[login] performWxLogin 请求', { appId: resolveAppId(), code, openId })
const result = await wxMiniappLogin({ appId: resolveAppId(), code, openId })
if (result.openId) storage.set(WX_OPENID_STORAGE_KEY, result.openId)
console.log('[login] performWxLogin 结果', getLoginSubState(result), result)
return result
}
// 重调登录接口的公共尾部:换新 code(一次性)→ 组装参数 → 调用 → 缓存 openId
async function reloginWith(
openId: string,
extra: { iv?: string; encryptedData?: string; phoneCode?: string }
): Promise<WxMiniappLoginResponse> {
let code: string
try {
const r = await Taro.login()
code = r.code
} catch {
throw new Error('获取微信登录凭证失败,请重试')
}
const params = { appId: resolveAppId(), code, openId, ...extra }
console.log('[login] 重调登录接口', params)
const result = await wxMiniappLogin(params)
if (result.openId) storage.set(WX_OPENID_STORAGE_KEY, result.openId)
console.log('[login] 重调结果', getLoginSubState(result), result)
return result
}
// needUserInfo 二次授权:getUserProfile 拿 iv/encryptedData → 重调登录接口补用户资料
export async function completeUserInfoLogin(openId: string): Promise<WxMiniappLoginResponse> {
let profile: { iv: string; encryptedData: string }
try {
const r = await Taro.getUserProfile({ desc: '用于完善会员资料' })
profile = { iv: r.iv, encryptedData: r.encryptedData }
} catch (e) {
throw new Error(e instanceof Error ? e.message : (e as { errMsg?: string })?.errMsg || '授权失败')
}
return reloginWith(openId, profile)
}
// noMobile 三次授权:手机号授权按钮回调(open-type=getPhoneNumber)→ 重调登录接口
// detail 两种形态:新版含 code(动态令牌,后端可直连微信换取手机号);
// 旧版含 iv/encryptedData(需与 session_key 解密,依赖同一 code 会话)
export async function completeMobileLogin(
openId: string,
detail: { iv: string; encryptedData: string; code?: string; errMsg?: string }
): Promise<WxMiniappLoginResponse> {
if (!detail.iv && !detail.code) {
throw new Error(detail.errMsg || '手机号授权失败')
}
return reloginWith(openId, {
iv: detail.iv,
encryptedData: detail.encryptedData,
phoneCode: detail.code
})
}