import supa from '@/components/supadb/aksupainstance.uts' import { HOME_REDIRECT } from '@/ak/config.uts' import { AkReq } from '@/uni_modules/ak-req/index.uts' import { getCurrentUser, logout, state } from '@/utils/store.uts' import type { UserProfile } from '@/types/mall-types.uts' export type MerchantInfo = { id: string merchant_id: string shop_name: string shop_logo: string shop_banner: string description: string contact_name: string contact_phone: string status: number | null } export type MerchantAuthOptions = { redirectOnFail?: boolean toastOnFail?: boolean redirect?: string message?: string } export type MerchantAuthResult = { ok: boolean message: string userInfo: UserProfile | null merchantInfo: MerchantInfo | null } const MERCHANT_LOGIN_PATH = '/pages/user/login?mode=merchant' const MERCHANT_HOME_PATH = HOME_REDIRECT !== '' ? HOME_REDIRECT : '/pages/mall/merchant/index' let redirectingToMerchantLogin = false function hasText(value: string | null): boolean { return value != null && value !== '' } function getCurrentRoute(): string { try { const pages = getCurrentPages() as Array if (pages.length === 0) { return '' } const currentPage = pages[pages.length - 1] const route = currentPage['route'] if (route != null) { return '/' + route } } catch (e) {} return '' } function isMerchantRoute(route: string): boolean { return route.startsWith('/pages/mall/merchant/') } function shouldShowToast(enabled: boolean | null): boolean { return enabled == null || enabled === true } function shouldRedirect(enabled: boolean | null): boolean { return enabled == null || enabled === true } function buildMerchantInfo(raw: UTSJSONObject): MerchantInfo { return { id: raw.getString('id') ?? '', merchant_id: raw.getString('merchant_id') ?? '', shop_name: raw.getString('shop_name') ?? '', shop_logo: raw.getString('shop_logo') ?? '', shop_banner: raw.getString('shop_banner') ?? '', description: raw.getString('description') ?? '', contact_name: raw.getString('contact_name') ?? '', contact_phone: raw.getString('contact_phone') ?? '', status: raw.getNumber('status') } } function persistMerchantInfo(info: MerchantInfo): void { const payload = new UTSJSONObject() payload.set('id', info.id) payload.set('merchant_id', info.merchant_id) payload.set('shop_name', info.shop_name) payload.set('shop_logo', info.shop_logo) payload.set('shop_banner', info.shop_banner) payload.set('description', info.description) payload.set('contact_name', info.contact_name) payload.set('contact_phone', info.contact_phone) if (info.status != null) { payload.set('status', info.status) } uni.setStorageSync('merchant_info', JSON.stringify(payload)) if (hasText(info.merchant_id)) { uni.setStorageSync('merchant_id', info.merchant_id) } else { uni.removeStorageSync('merchant_id') } } function buildFailureResult(message: string, userInfo: UserProfile | null = null): MerchantAuthResult { return { ok: false, message, userInfo, merchantInfo: null } } async function fetchMerchantInfoByUserId(userId: string): Promise { let response = await supa .from('ml_shops') .select('id, merchant_id, shop_name, shop_logo, shop_banner, description, contact_name, contact_phone, status') .eq('merchant_id', userId) .limit(1) .execute() if (response.error == null && response.data != null) { const rows = response.data as Array if (rows.length > 0) { return buildMerchantInfo(rows[0]) } } response = await supa .from('ml_shops') .select('id, merchant_id, shop_name, shop_logo, shop_banner, description, contact_name, contact_phone, status') .eq('id', userId) .limit(1) .execute() if (response.error == null && response.data != null) { const rows = response.data as Array if (rows.length > 0) { return buildMerchantInfo(rows[0]) } } return null } function getRedirectTarget(explicitRedirect: string | null): string { if (hasText(explicitRedirect)) { return explicitRedirect as string } const currentRoute = getCurrentRoute() if (hasText(currentRoute) && currentRoute !== '/pages/user/login') { return currentRoute } return MERCHANT_HOME_PATH } function handleFailure(message: string, options?: MerchantAuthOptions, userInfo: UserProfile | null = null): MerchantAuthResult { clearAuth() if (shouldShowToast(options?.toastOnFail ?? null) && hasText(message)) { uni.showToast({ title: message, icon: 'none' }) } if (shouldRedirect(options?.redirectOnFail ?? null)) { redirectToMerchantLogin(getRedirectTarget(options?.redirect ?? null)) } return buildFailureResult(message, userInfo) } export function getToken(): string { const token = AkReq.getToken() return token != null ? token : '' } export async function getUserInfo(): Promise { try { const profile = state.userProfile if (profile != null && hasText(profile.id ?? null)) { return profile as UserProfile } } catch (e) {} return await getCurrentUser() } export function getMerchantInfo(): MerchantInfo | null { try { const raw = uni.getStorageSync('merchant_info') as string | null if (hasText(raw)) { const parsed = JSON.parse(raw as string) as UTSJSONObject return buildMerchantInfo(parsed) } } catch (e) { console.error('[merchantAuth] 读取 merchant_info 失败:', e) } return null } export function setMerchantInfo(info: MerchantInfo): void { persistMerchantInfo(info) } export function isLoggedIn(): boolean { try { const session = supa.getSession() const hasSession = session != null && session.user != null const storedId = uni.getStorageSync('user_id') as string | null const hasStoredId = hasText(storedId) return hasSession || hasStoredId } catch (e) { return false } } export function isMerchantRole(userInfo: UserProfile | null): boolean { if (userInfo == null) { return false } const role = userInfo.role != null ? userInfo.role : '' return role === 'merchant' } export async function checkMerchantAccount(userInfo: UserProfile): Promise { const userId = userInfo.id != null ? userInfo.id : '' if (!hasText(userId)) { return null } try { return await fetchMerchantInfoByUserId(userId) } catch (e) { console.error('[merchantAuth] 查询商家信息失败:', e) return null } } export function clearAuth(): void { try { AkReq.clearToken() } catch (e) {} uni.removeStorageSync('user_id') uni.removeStorageSync('merchant_id') uni.removeStorageSync('merchant_info') uni.removeStorageSync('merchant_idx_cache') try { logout() } catch (e) {} } export function redirectToMerchantLogin(redirect?: string): void { const currentRoute = getCurrentRoute() if (currentRoute === '/pages/user/login') { return } if (redirectingToMerchantLogin) { return } redirectingToMerchantLogin = true let targetUrl = MERCHANT_LOGIN_PATH const redirectTarget = getRedirectTarget(redirect != null ? redirect : null) if (hasText(redirectTarget)) { targetUrl = `${MERCHANT_LOGIN_PATH}&redirect=${encodeURIComponent(redirectTarget)}` } uni.reLaunch({ url: targetUrl, complete: () => { setTimeout(() => { redirectingToMerchantLogin = false }, 300) } }) } export function goMerchantHome(): void { uni.reLaunch({ url: MERCHANT_HOME_PATH }) } export async function requireMerchantAuth(options?: MerchantAuthOptions): Promise { const currentRoute = getCurrentRoute() if (currentRoute === '/pages/user/login') { return { ok: true, message: '', userInfo: null, merchantInfo: null } } if (!isLoggedIn()) { return handleFailure(options?.message ?? '请先登录商家账号', options) } const userInfo = await getUserInfo() if (userInfo == null || !hasText(userInfo.id ?? null)) { return handleFailure('登录状态已失效,请重新登录', options) } if (!isMerchantRole(userInfo)) { const message = options?.message != null && options.message !== '' ? options.message : '当前账号不是商家账号' return handleFailure(message, options, userInfo) } const merchantInfo = await checkMerchantAccount(userInfo) if (merchantInfo == null) { return handleFailure('当前账号未绑定商家信息', options, userInfo) } if (merchantInfo.status != null) { if (merchantInfo.status === 0) { return handleFailure('当前商家信息待审核', options, userInfo) } if (merchantInfo.status !== 1) { return handleFailure('当前商家状态异常,暂不可登录', options, userInfo) } } persistMerchantInfo(merchantInfo) return { ok: true, message: '', userInfo, merchantInfo } } export function isCurrentMerchantPage(): boolean { return isMerchantRoute(getCurrentRoute()) }