/** * OmniCard AI (CRMmp) - 移动端 App / H5 智能数字名片 Universal Link 深度链接路由分发解析器 * 严格遵循 AGENTS.md 与 uts 强类型跨端开发规范 */ export interface DeepLinkRouteInfo { isValid: boolean; cardId?: number; cardSlug?: string; sceneMode: 'business' | 'social' | 'exhibition'; locale: string; focusTab?: string; source: string; rawUrl?: string; } /** * 解析 URL 参数为 Key-Value 映射 */ function parseQueryParams(queryString: string): Record { const params: Record = {}; if (!queryString) return params; const pairs = queryString.replace(/^\?/, '').split('&'); for (const pair of pairs) { if (!pair) continue; const [key, value] = pair.split('='); if (key) { params[decodeURIComponent(key)] = value ? decodeURIComponent(value) : ''; } } return params; } /** * 解析 Universal Link 或 App Custom Scheme */ export function parseDeepLink(url?: string): DeepLinkRouteInfo { const fallback: DeepLinkRouteInfo = { isValid: false, sceneMode: 'business', locale: 'en', source: 'direct', }; if (!url || typeof url !== 'string' || !url.trim()) { return fallback; } const clean = url.trim(); // 1. 检查是否为 Universal Link (如 https://omnicard.ai/p/xxx 或 /c/xxx) if (clean.startsWith('http://') || clean.startsWith('https://')) { const isDomainMatch = clean.includes('omnicard.ai') || clean.includes('omni.me') || clean.includes('/p/'); if (!isDomainMatch) { return fallback; } const questionIdx = clean.indexOf('?'); const pathPart = questionIdx !== -1 ? clean.slice(0, questionIdx) : clean; const queryPart = questionIdx !== -1 ? clean.slice(questionIdx + 1) : ''; const query = parseQueryParams(queryPart); // 提取 slug (例如 /p/alex-zheng -> alex-zheng) const pMatch = pathPart.match(/\/p\/([^/?#]+)/); const slug = pMatch ? pMatch[1] : undefined; return { isValid: true, cardSlug: slug, cardId: query.id ? Number(query.id) : undefined, sceneMode: (query.scene === 'social' || query.scene === 'exhibition') ? query.scene : 'exhibition', locale: query.lang || 'en', focusTab: query.tab, source: query.from || 'direct', rawUrl: clean, }; } // 2. 检查是否为 App Scheme (omnicard://card/view?id=...) if (clean.startsWith('omnicard://')) { const questionIdx = clean.indexOf('?'); const queryPart = questionIdx !== -1 ? clean.slice(questionIdx + 1) : ''; const query = parseQueryParams(queryPart); return { isValid: true, cardId: query.id ? Number(query.id) : undefined, cardSlug: query.slug, sceneMode: (query.scene === 'social' || query.scene === 'exhibition') ? query.scene : 'business', locale: query.lang || 'en', focusTab: query.tab, source: query.from || 'direct', rawUrl: clean, }; } return fallback; } /** * 将深度链接解析结果构建为应用内路由路径 */ export function buildAppPageRoute(info: DeepLinkRouteInfo): string { if (!info || !info.isValid) { return '/pages/owner/hall/index'; } const queryParts: string[] = []; if (info.cardSlug) queryParts.push(`slug=${encodeURIComponent(info.cardSlug)}`); if (info.cardId) queryParts.push(`id=${info.cardId}`); if (info.sceneMode) queryParts.push(`scene=${info.sceneMode}`); if (info.locale) queryParts.push(`lang=${info.locale}`); if (info.focusTab) queryParts.push(`tab=${info.focusTab}`); if (info.source) queryParts.push(`source=${info.source}`); const queryString = queryParts.length > 0 ? `?${queryParts.join('&')}` : ''; return `/pages/portal/index${queryString}`; }