74 lines
3.8 KiB
TypeScript
74 lines
3.8 KiB
TypeScript
import { createHash } from "crypto";
|
|
|
|
export const TEST_APP_ID = "40e3d5c5f2af49a4a074205564ce5dbb";
|
|
export const PRODUCTION_APP_ID = "95993f9ab6f1402a87abe5147827e5e0";
|
|
const RECEIVER = "https://data.nika4fun.com/sync_data";
|
|
|
|
/** Only newly committed transitions generate events; polling never replays historical stages. */
|
|
export function collect(before: any, run: any, isDebug: any, now: number): any[] {
|
|
if (!run) return [];
|
|
// Existing Utils.POST uses isDebug=true for RELEASE, false for develop/trial.
|
|
const appId = isDebug === true || isDebug === "true" ? PRODUCTION_APP_ID : TEST_APP_ID;
|
|
const events: any[] = [];
|
|
for (const stage of run.stages) {
|
|
const previous = before?.id === run.id && before.stages.find((s: any) =>
|
|
s.stage === stage.stage && s.startedAt === stage.startedAt && s.matchId === stage.matchId);
|
|
const add = (event: string, properties: any, attempt = "") => {
|
|
const id = createHash("sha256").update(JSON.stringify([
|
|
run.id, stage.stage, stage.startedAt, stage.matchId, event, attempt,
|
|
])).digest("hex");
|
|
events.push({ id, event, properties, appId, time: now });
|
|
};
|
|
if (!previous) add("cloud_rise_start", {
|
|
stage: stage.stage, end_count: stage.opponents.filter((o: any) => o.success_num >= stage.target).length,
|
|
});
|
|
const oldResults = new Set((previous?.results || []).map((r: any) => r.id));
|
|
stage.results.forEach((result: any, index: number) => {
|
|
if (!oldResults.has(result.id)) add("cloud_rise_step", {
|
|
stage: stage.stage, step: index + 1, result: result.outcome === "win" ? "success" : "failure",
|
|
}, result.id);
|
|
});
|
|
if ((stage.status === "lost" || stage.status === "expired") && previous?.status !== stage.status) {
|
|
add("cloud_rise_fail", { stage: stage.stage, step: Math.min(stage.target, stage.success_num + 1) });
|
|
}
|
|
// The exact client-calculated award becomes authoritative only when its receipt is saved.
|
|
if (stage.status === "won" && stage.rewardSaved && !previous?.rewardSaved) {
|
|
add("cloud_rise_succeed", { stage: stage.stage, coin_amount: stage.reward });
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
|
|
/** Flush before returning; failed/uncertain deliveries remain in the durable queue for retry. */
|
|
export async function send(events: any[], user: any, sdkModule?: any): Promise<string[]> {
|
|
if (!events.length) return [];
|
|
if (!user.openid && !user.distinctId) return [];
|
|
const ThinkingData = sdkModule || require("thinkingdata-node");
|
|
const groups = [TEST_APP_ID, PRODUCTION_APP_ID].map(appId => events.filter(e => e.appId === appId));
|
|
const delivered = await Promise.all(groups.filter(group => group.length).map(async group => {
|
|
const sdk = ThinkingData.initWithBatchMode(group[0].appId, RECEIVER, { batchSize: group.length + 1 });
|
|
try {
|
|
await new Promise<void>((resolve, reject) => {
|
|
const timer = setTimeout(() => reject(new Error("数数上报超时")), 1000);
|
|
const finish = (error?: any) => { clearTimeout(timer); error ? reject(error) : resolve(); };
|
|
try {
|
|
for (const item of group) sdk.trackFirst({
|
|
...(user.openid ? { accountId: user.openid } : {}),
|
|
...(user.distinctId ? { distinctId: user.distinctId } : {}),
|
|
event: item.event, firstCheckId: item.id,
|
|
// Align with the existing server SDK's China-local event timestamps.
|
|
time: new Date(item.time + 8 * 3600000), properties: item.properties,
|
|
callback(error: any) { if (error) finish(error); },
|
|
});
|
|
sdk.flush(finish);
|
|
} catch (error) { finish(error); }
|
|
});
|
|
return group.map(item => item.id);
|
|
} catch (error) {
|
|
console.error("cloudRise analytics pending", error instanceof Error ? error.message : error);
|
|
return [];
|
|
} finally { sdk.close(); }
|
|
}));
|
|
return delivered.flat();
|
|
}
|