import Taro from '@tarojs/taro'; import { request, isLoggedIn, toast } from './api'; const PROMO_ID_KEY = 'dukang_promo_id'; const ASSOC_SCENE_KEY = 'dukang_partner_assoc_scene'; /** 同一次进入只 touch 一次扫码计数,避免首页反复 onShow 刷量 */ let lastScanTouchKey = ''; function safeDecode(raw: string): string { try { return decodeURIComponent(raw); } catch { return raw; } } function normalizePromoId(raw: unknown): string | null { if (raw == null || raw === '') return null; const s = safeDecode(String(raw)).trim(); // 小程序码 scene 写入的是推广活动数字 ID if (!/^\d+$/.test(s)) return null; return s; } type EnterOptionsLike = { scene?: string | number; query?: Record; path?: string; }; function normalizeAssocScene(raw: unknown): string | null { if (raw == null || raw === '') return null; const s = safeDecode(String(raw)).trim(); return /^pa_\d+$/.test(s) ? s : null; } function extractAssocSceneFromEnterOptions(opts?: EnterOptionsLike | null): string | null { if (!opts) return null; const q = opts.query ?? {}; return ( normalizeAssocScene(q.scene) || normalizeAssocScene(opts.scene) || normalizeAssocScene(q.partnerId) || null ); } export function getStoredAssocScene(): string | null { try { return normalizeAssocScene(Taro.getStorageSync(ASSOC_SCENE_KEY)); } catch { return null; } } export function setStoredAssocScene(scene: string) { const id = normalizeAssocScene(scene); if (!id) return; try { Taro.setStorageSync(ASSOC_SCENE_KEY, id); } catch { /* ignore */ } } export function clearStoredAssocScene() { try { Taro.removeStorageSync(ASSOC_SCENE_KEY); } catch { /* ignore */ } } /** 从启动/进入参数解析推广活动 ID(优先 query.scene,与 getwxacodeunlimit 一致) */ export function extractPromoIdFromEnterOptions(opts?: EnterOptionsLike | null): string | null { if (!opts) return null; const q = opts.query ?? {}; if (extractAssocSceneFromEnterOptions(opts)) return null; return ( normalizePromoId(q.scene) || normalizePromoId(q.promoId) || normalizePromoId(q.pid) || null ); } export function getStoredPromoId(): string | null { try { const v = Taro.getStorageSync(PROMO_ID_KEY); return normalizePromoId(v); } catch { return null; } } export function setStoredPromoId(promoId: string) { const id = normalizePromoId(promoId); if (!id) return; try { Taro.setStorageSync(PROMO_ID_KEY, id); } catch { /* ignore */ } } function readEnterOptions(): EnterOptionsLike | null { try { if (typeof Taro.getEnterOptionsSync === 'function') { return Taro.getEnterOptionsSync() as EnterOptionsLike; } } catch { /* ignore */ } try { if (typeof Taro.getLaunchOptionsSync === 'function') { return Taro.getLaunchOptionsSync() as EnterOptionsLike; } } catch { /* ignore */ } // H5:从 URL query 读取 if (process.env.TARO_ENV === 'h5' && typeof window !== 'undefined') { const params = new URLSearchParams(window.location.search); return { query: { scene: params.get('scene') || undefined, promoId: params.get('promoId') || undefined, pid: params.get('pid') || undefined, }, }; } return null; } /** * 主页面进入时:取出 scene(活动 ID)本地缓存,并回传 /promo/touch 累加扫码次数。 * 同一进入会话只计一次扫码。 */ async function bindStoredAssocIfLoggedIn(): Promise { const scene = getStoredAssocScene(); if (!scene || !isLoggedIn()) return; try { const result = await request<{ alreadyBound?: boolean; bound?: boolean }>('/user/partner-assoc/bind', { method: 'POST', data: { scene }, }); clearStoredAssocScene(); if (result.alreadyBound) toast('已关联'); else if (result.bound) toast('关联成功', 'success'); } catch (e) { const msg = e instanceof Error ? e.message : ''; if (msg.includes('已关联')) { clearStoredAssocScene(); toast('已关联'); } } } export async function capturePromoSceneAndTouchScan(): Promise { const opts = readEnterOptions(); const assocScene = extractAssocSceneFromEnterOptions(opts); if (assocScene) { setStoredAssocScene(assocScene); const touchKey = `assoc|${assocScene}|${opts?.path || ''}|${JSON.stringify(opts?.query || {})}|${String(opts?.scene ?? '')}`; if (touchKey !== lastScanTouchKey) { lastScanTouchKey = touchKey; await touchAssoc({ scene: assocScene, countScan: true }); } await bindStoredAssocIfLoggedIn(); return; } const fromEnter = extractPromoIdFromEnterOptions(opts); if (fromEnter) { setStoredPromoId(fromEnter); const touchKey = `${fromEnter}|${opts?.path || ''}|${JSON.stringify(opts?.query || {})}|${String(opts?.scene ?? '')}`; if (touchKey === lastScanTouchKey) return; lastScanTouchKey = touchKey; await touchPromo({ promoId: fromEnter, countScan: true }); return; } // 无新 scene 时不重复扫码计数 } async function touchAssoc(input: { scene: string; countScan: boolean }): Promise { try { await request('/user/partner-assoc/touch', { method: 'POST', data: { scene: input.scene, countScan: input.countScan, }, }); } catch { /* 静默失败,不阻断浏览 */ } } /** 登录成功后:用已缓存的活动 ID 做归因(不重复加扫码次数) */ export async function touchStoredPromoAfterLogin(): Promise { await bindStoredAssocIfLoggedIn(); const promoId = getStoredPromoId(); if (!promoId) return; await touchPromo({ promoId, countScan: false }); } async function touchPromo(input: { promoId: string; countScan: boolean }): Promise { try { await request('/promo/touch', { method: 'POST', data: { promoId: input.promoId, countScan: input.countScan, }, }); } catch { /* 静默失败,不阻断浏览 */ } }