feat(assoc): v4.0.1 合伙人关联码、分佣账单与 H5 用户管理
订单佣金只认关联用户;合伙人备注写入独立表;H5 增加用户管理与首页统计。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import Taro from '@tarojs/taro';
|
||||
import { request } from './api';
|
||||
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 = '';
|
||||
@@ -28,10 +29,54 @@ type EnterOptionsLike = {
|
||||
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) ||
|
||||
@@ -92,8 +137,35 @@ function readEnterOptions(): EnterOptionsLike | null {
|
||||
* 主页面进入时:取出 scene(活动 ID)本地缓存,并回传 /promo/touch 累加扫码次数。
|
||||
* 同一进入会话只计一次扫码。
|
||||
*/
|
||||
async function bindStoredAssocIfLoggedIn(): Promise<void> {
|
||||
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<void> {
|
||||
const opts = readEnterOptions();
|
||||
const assocScene = extractAssocSceneFromEnterOptions(opts);
|
||||
if (assocScene) {
|
||||
setStoredAssocScene(assocScene);
|
||||
await bindStoredAssocIfLoggedIn();
|
||||
return;
|
||||
}
|
||||
|
||||
const fromEnter = extractPromoIdFromEnterOptions(opts);
|
||||
if (fromEnter) {
|
||||
setStoredPromoId(fromEnter);
|
||||
@@ -109,6 +181,7 @@ export async function capturePromoSceneAndTouchScan(): Promise<void> {
|
||||
|
||||
/** 登录成功后:用已缓存的活动 ID 做归因(不重复加扫码次数) */
|
||||
export async function touchStoredPromoAfterLogin(): Promise<void> {
|
||||
await bindStoredAssocIfLoggedIn();
|
||||
const promoId = getStoredPromoId();
|
||||
if (!promoId) return;
|
||||
await touchPromo({ promoId, countScan: false });
|
||||
|
||||
Reference in New Issue
Block a user