130 lines
3.5 KiB
TypeScript
130 lines
3.5 KiB
TypeScript
import Taro from '@tarojs/taro';
|
|
import { request } from './api';
|
|
|
|
const PROMO_ID_KEY = 'dukang_promo_id';
|
|
|
|
/** 同一次进入只 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<string, string | undefined>;
|
|
path?: string;
|
|
};
|
|
|
|
/** 从启动/进入参数解析推广活动 ID(优先 query.scene,与 getwxacodeunlimit 一致) */
|
|
export function extractPromoIdFromEnterOptions(opts?: EnterOptionsLike | null): string | null {
|
|
if (!opts) return null;
|
|
const q = opts.query ?? {};
|
|
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 累加扫码次数。
|
|
* 同一进入会话只计一次扫码。
|
|
*/
|
|
export async function capturePromoSceneAndTouchScan(): Promise<void> {
|
|
const opts = readEnterOptions();
|
|
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 时不重复扫码计数
|
|
}
|
|
|
|
/** 登录成功后:用已缓存的活动 ID 做归因(不重复加扫码次数) */
|
|
export async function touchStoredPromoAfterLogin(): Promise<void> {
|
|
const promoId = getStoredPromoId();
|
|
if (!promoId) return;
|
|
await touchPromo({ promoId, countScan: false });
|
|
}
|
|
|
|
async function touchPromo(input: { promoId: string; countScan: boolean }): Promise<void> {
|
|
try {
|
|
await request('/promo/touch', {
|
|
method: 'POST',
|
|
data: {
|
|
promoId: input.promoId,
|
|
countScan: input.countScan,
|
|
},
|
|
});
|
|
} catch {
|
|
/* 静默失败,不阻断浏览 */
|
|
}
|
|
}
|