文件
OmniCard/mpuniapp/common/deeplink-router.uts

121 行
3.7 KiB
Plaintext

/**
* 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<string, string> {
const params: Record<string, string> = {};
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}`;
}