文件
OmniCard/mpuniapp/common/deeplink-router.uts
wikiboss 6c3d5d323a refactor(arch): 落地平台架构演进第一二阶段整改,消除OOM与越权风险并支持高并发缓冲与AI多租户BYOK
- [P0安全底座] 多租户数据闭环:公开留资与雷达足迹自动逆向追溯绑定名片 tenantId 与归属销售 userId
- [P0性能稳定] 消除内存全量加载:公海池分批带条件扫描回收,雷达国家分布改用 SQL 原生 GROUP BY 聚合查询彻底消除 OOM 隐患
- [P0商业变现] 商业化配额门禁切面:在 Cool-Admin modifyBefore 生命周期织入 assertQuotaAvailable 校验,阻断超额名片创建
- [P0环境解耦] 客户端解耦本地硬编码:request.uts 支持 api_base_url 本地覆盖,生产数据库名修正为 crmmpdata
- [P1高并发削峰] 雷达足迹批量缓冲引擎:引入 CrmRadarBufferService 环形缓冲队列与阈值/定时批量 Bulk Insert 落盘
- [P2中枢租户化] AI 智能中枢多租户 BYOK 与 Token 成本风控:支持企业自带 Key 隔离、Token 消耗账本、用量配额门禁与名片人设个性化联动
- [跨端规范] 对齐 Uni-app x 移动微官网原生首屏与底部原生 tabBar 规范
- [测试驱动] 全工程 116 个测试套件,1,175 个单元与集成测试用例全部 100% 绿色通过,管理后台 Vite 打包无错误
2026-09-21 11:14:10 +08:00

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}`;
}