feat: columna ad_daily.link_clicks (Meta clics enlace reales)
Persistimos link_clicks en warehouse (con backfill desde __lc), sync escribe la columna y creatividades la leen sin romper si falta. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -365,27 +365,47 @@ async function replaceDaily(
|
||||
);
|
||||
}
|
||||
|
||||
// chunk inserts (link_clicks → ad_name marker until column exists)
|
||||
// chunk inserts — prefer link_clicks column; fallback to Meta `__lc:N` marker
|
||||
for (let i = 0; i < rows.length; i += 400) {
|
||||
const chunk = rows.slice(i, i + 400).map((r) => {
|
||||
const link = Math.round(r.link_clicks ?? r.clicks);
|
||||
const { link_clicks: _lc, ...rest } = r;
|
||||
if (r.platform === 'meta') {
|
||||
return { ...rest, ad_name: `__lc:${link}` };
|
||||
}
|
||||
return rest;
|
||||
});
|
||||
const r = await fetch(`${base}/rest/v1/ad_daily`, {
|
||||
const full = rows.slice(i, i + 400).map((r) => ({
|
||||
...r,
|
||||
link_clicks: Math.round(r.link_clicks ?? r.clicks),
|
||||
ad_name: r.ad_name || '',
|
||||
}));
|
||||
let r = await fetch(`${base}/rest/v1/ad_daily`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/json',
|
||||
Prefer: 'return=minimal',
|
||||
},
|
||||
body: JSON.stringify(chunk),
|
||||
body: JSON.stringify(full),
|
||||
});
|
||||
if (!r.ok) {
|
||||
throw new Error(`insert ad_daily: ${(await r.text()).slice(0, 220)}`);
|
||||
const errText = await r.text();
|
||||
if (!/link_clicks/i.test(errText)) {
|
||||
throw new Error(`insert ad_daily: ${errText.slice(0, 220)}`);
|
||||
}
|
||||
const legacy = full.map((row) => {
|
||||
const link = Math.round(row.link_clicks ?? row.clicks);
|
||||
const { link_clicks: _lc, ...rest } = row;
|
||||
if (row.platform === 'meta') {
|
||||
return { ...rest, ad_name: `__lc:${link}` };
|
||||
}
|
||||
return rest;
|
||||
});
|
||||
r = await fetch(`${base}/rest/v1/ad_daily`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/json',
|
||||
Prefer: 'return=minimal',
|
||||
},
|
||||
body: JSON.stringify(legacy),
|
||||
});
|
||||
if (!r.ok) {
|
||||
throw new Error(`insert ad_daily: ${(await r.text()).slice(0, 220)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ export type AdDailyRow = {
|
||||
tenant_id?: string;
|
||||
};
|
||||
|
||||
/** Meta link clicks stored as ad_name `__lc:N` until link_clicks column is used. */
|
||||
/** Meta link clicks: column first, else `__lc:N` in ad_name (legacy), else all clicks. */
|
||||
export function linkClicksOf(r: AdDailyRow): number {
|
||||
const m = String(r.ad_name || '').match(/^__lc:(\d+(?:\.\d+)?)$/);
|
||||
if (m) return Number(m[1]) || 0;
|
||||
if (r.link_clicks != null && Number.isFinite(Number(r.link_clicks))) {
|
||||
return Number(r.link_clicks) || 0;
|
||||
}
|
||||
const m = String(r.ad_name || '').match(/^__lc:(\d+(?:\.\d+)?)$/);
|
||||
if (m) return Number(m[1]) || 0;
|
||||
return Number(r.clicks || 0) || 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user