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 { // 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(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 { 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 { 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 { if (!detail.iv && !detail.code) { throw new Error(detail.errMsg || '手机号授权失败') } return reloginWith(openId, { iv: detail.iv, encryptedData: detail.encryptedData, phoneCode: detail.code }) }