diff --git a/apps/h5-partner/src/App.tsx b/apps/h5-partner/src/App.tsx index d9d6182..af7de6e 100644 --- a/apps/h5-partner/src/App.tsx +++ b/apps/h5-partner/src/App.tsx @@ -1,13 +1,32 @@ -import { Routes, Route, Navigate } from 'react-router-dom'; +import { Routes, Route, Navigate, useLocation } from 'react-router-dom'; import LoginPage from './pages/LoginPage'; import PartnerAppRoutes from './PartnerAppRoutes'; import { isLoggedIn } from './lib/api'; +import { usePartnerSession } from './contexts/PartnerSessionContext'; +import { partnerHomePath } from './lib/partnerAccess'; + +function LoginRoute() { + const { account } = usePartnerSession(); + if (isLoggedIn()) { + return ; + } + return ; +} + +function AppRoutes() { + const location = useLocation(); -export default function App() { return ( - } /> - : } /> + } /> + : } + /> ); } + +export default function App() { + return ; +} diff --git a/apps/h5-partner/src/PartnerAppRoutes.tsx b/apps/h5-partner/src/PartnerAppRoutes.tsx index 89f1b55..71f60f3 100644 --- a/apps/h5-partner/src/PartnerAppRoutes.tsx +++ b/apps/h5-partner/src/PartnerAppRoutes.tsx @@ -61,12 +61,12 @@ function SubAccountRoutes() { } export default function PartnerAppRoutes() { - const { account, loading, loggedIn } = usePartnerSession(); + const { account, loading } = usePartnerSession(); - if (!loggedIn && !isLoggedIn()) { + if (!isLoggedIn()) { return ; } - if (loading) { + if (loading && !account) { return ; } if (isSubAccount(account)) { diff --git a/apps/h5-partner/src/contexts/PartnerSessionContext.tsx b/apps/h5-partner/src/contexts/PartnerSessionContext.tsx index b772723..a64faaa 100644 --- a/apps/h5-partner/src/contexts/PartnerSessionContext.tsx +++ b/apps/h5-partner/src/contexts/PartnerSessionContext.tsx @@ -1,6 +1,6 @@ import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react'; import { toAppPath } from '@dukang/weixin-sdk'; -import { clearAuth, isLoggedIn, request } from '../lib/api'; +import { clearAuth, isLoggedIn, request, saveAuth, type PartnerAuthPayload } from '../lib/api'; import type { PartnerMe, PartnerStaffRole } from '@dukang/shared-types'; @@ -12,6 +12,7 @@ type PartnerSessionValue = { account: PartnerAccount | null; loading: boolean; loggedIn: boolean; + applySession: (session: PartnerAuthPayload) => void; refresh: () => Promise; logout: () => void; }; @@ -23,6 +24,15 @@ export function PartnerSessionProvider({ children }: { children: ReactNode }) { const [loading, setLoading] = useState(true); const [loggedIn, setLoggedIn] = useState(() => isLoggedIn()); + const applySession = useCallback((session: PartnerAuthPayload) => { + saveAuth(session); + setLoggedIn(true); + setLoading(false); + if (session.partner) { + setAccount(session.partner as PartnerAccount); + } + }, []); + const refresh = useCallback(async (): Promise => { if (!isLoggedIn()) { setAccount(null); @@ -38,6 +48,9 @@ export function PartnerSessionProvider({ children }: { children: ReactNode }) { return data; } catch { setAccount(null); + if (!isLoggedIn()) { + setLoggedIn(false); + } return null; } finally { setLoading(false); @@ -57,7 +70,7 @@ export function PartnerSessionProvider({ children }: { children: ReactNode }) { return ( {children} diff --git a/apps/h5-partner/src/lib/api.ts b/apps/h5-partner/src/lib/api.ts index b1f18d4..5bc722b 100644 --- a/apps/h5-partner/src/lib/api.ts +++ b/apps/h5-partner/src/lib/api.ts @@ -1,4 +1,4 @@ -import type { PartnerMe } from '@dukang/shared-types'; +import type { PartnerMe, PartnerStaffRole } from '@dukang/shared-types'; import { isOnAppPath, toAppPath } from '@dukang/weixin-sdk'; export const apiBase = '/api/v1'; @@ -6,7 +6,10 @@ export const apiBase = '/api/v1'; const LAST_PHONE = 'partnerLastPhone'; const PARTNER_PROFILE = 'partnerProfile'; -export type PartnerSessionProfile = Pick; +export type PartnerSessionProfile = Pick & { + isPrimary?: PartnerMe['isPrimary']; + staffRole?: PartnerStaffRole; +}; export type PartnerAuthPayload = { accessToken: string; diff --git a/apps/h5-partner/src/lib/wechat-auth.ts b/apps/h5-partner/src/lib/wechat-auth.ts index ab434ab..c32cd01 100644 --- a/apps/h5-partner/src/lib/wechat-auth.ts +++ b/apps/h5-partner/src/lib/wechat-auth.ts @@ -2,7 +2,7 @@ import type { ClientRuntimeConfig, WechatLoginResult } from '@dukang/shared-type import { isWxAuthorizeEnabled } from '@dukang/shared-types'; import { WECHAT_INAPP_REQUIRED_MSG } from '@dukang/weixin-sdk'; import { isWechatEnv, weixinSdk } from './weixin'; -import { request, saveAuth } from './api'; +import { request, saveAuth, type PartnerSessionProfile } from './api'; export type PartnerProfile = { id: string; @@ -37,7 +37,10 @@ export async function checkNeedsWechatAuth(profile: PartnerProfile | null): Prom /** 处理微信登录/绑定结果,返回是否已拿到 token 可进入首页 */ export function handlePartnerWechatLoginResult(result: WechatLoginResult): boolean { if (!result.accessToken) return false; - saveAuth({ accessToken: result.accessToken }); + saveAuth({ + accessToken: result.accessToken, + partner: result.partner as PartnerSessionProfile | undefined, + }); return true; } diff --git a/apps/h5-partner/src/pages/LoginPage.tsx b/apps/h5-partner/src/pages/LoginPage.tsx index fce59d0..bcc662d 100644 --- a/apps/h5-partner/src/pages/LoginPage.tsx +++ b/apps/h5-partner/src/pages/LoginPage.tsx @@ -1,13 +1,13 @@ import { useEffect, useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; -import { request, getLastPhone, getPartnerProfile, saveAuth } from '../lib/api'; +import { request, getLastPhone, getPartnerProfile, type PartnerAuthPayload } from '../lib/api'; import { usePartnerSession } from '../contexts/PartnerSessionContext'; +import type { PartnerAccount } from '../contexts/PartnerSessionContext'; import { partnerHomePath } from '../lib/partnerAccess'; import type { PartnerMe } from '@dukang/shared-types'; import { fetchClientConfig, handlePartnerWechatCallback, - handlePartnerWechatLoginResult, loginPartnerWithWechat, } from '../lib/wechat-auth'; import { isWechatEnv } from '../lib/weixin'; @@ -43,9 +43,21 @@ function formatPartnerError(e: unknown): string { return text; } +function toLoginSession(data: { accessToken: string; partner?: PartnerMe }): PartnerAuthPayload { + return { + accessToken: data.accessToken, + partner: data.partner, + }; +} + +function toPartnerAccount(partner?: Record): PartnerAccount | undefined { + if (!partner || typeof partner.id !== 'string') return undefined; + return partner as unknown as PartnerAccount; +} + export default function LoginPage() { const navigate = useNavigate(); - const { refresh } = usePartnerSession(); + const { applySession } = usePartnerSession(); const [params] = useSearchParams(); const quick = params.get('quick') === '1'; const savedProfile = getPartnerProfile(); @@ -73,14 +85,17 @@ export default function LoginPage() { useEffect(() => { if (!isWechatEnv() || !wxAuthorize || !params.get('code')) return; void handlePartnerWechatCallback() - .then(async (result) => { - if (!result) return; - if (!handlePartnerWechatLoginResult(result)) return; - const account = await refresh(); - navigate(partnerHomePath(account ?? undefined)); + .then((result) => { + if (!result?.accessToken) return; + const session: PartnerAuthPayload = { + accessToken: result.accessToken, + partner: toPartnerAccount(result.partner), + }; + applySession(session); + navigate(partnerHomePath(session.partner), { replace: true }); }) .catch((e) => setMsg(formatWechatError(e))); - }, [navigate, refresh, wxAuthorize, params]); + }, [applySession, navigate, wxAuthorize, params]); function formatWechatError(e: unknown): string { const text = e instanceof Error ? e.message : '微信登录失败'; @@ -155,10 +170,10 @@ export default function LoginPage() { body: JSON.stringify({ phone: loginPhone, code }), silent: true, }); - saveAuth(data); + const session = toLoginSession(data); + applySession(session); persistRememberAccount(loginPhone); - const account = await refresh(); - navigate(partnerHomePath(account ?? data.partner)); + navigate(partnerHomePath(session.partner), { replace: true }); } catch (e) { setMsg(formatPartnerError(e)); } finally { @@ -177,8 +192,11 @@ export default function LoginPage() { try { const ok = await loginPartnerWithWechat(); if (ok) { - const account = await refresh(); - navigate(partnerHomePath(account ?? undefined)); + applySession({ + accessToken: localStorage.getItem('accessToken') ?? '', + partner: getPartnerProfile() ?? undefined, + }); + navigate(partnerHomePath(getPartnerProfile()), { replace: true }); } } catch (e) { setMsg(formatWechatError(e)); diff --git a/scripts/test-partner-auth.mjs b/scripts/test-partner-auth.mjs index 25ba9ed..94c394e 100644 --- a/scripts/test-partner-auth.mjs +++ b/scripts/test-partner-auth.mjs @@ -55,7 +55,11 @@ async function main() { ); if (!login.accessToken) throw new Error('login missing token'); - console.log('4. Admin partner logs list'); + console.log('4. PARTNER /partner/me after login'); + const me = await req('PARTNER_H5', '/partner/me', { token: login.accessToken }); + if (!me?.id || !me?.phone) throw new Error('partner/me missing profile'); + + console.log('5. Admin partner logs list'); const admin = await req('HQ_WEB', '/admin/auth/login/password', { method: 'POST', body: JSON.stringify({