|
|
@@ -109,6 +109,51 @@ function buildMatrix(rows, dates) {
|
|
|
return groups;
|
|
|
}
|
|
|
|
|
|
+function numOrNull(value) {
|
|
|
+ if (value === null || value === undefined || value === '') return null;
|
|
|
+ const n = Number(value);
|
|
|
+ return Number.isFinite(n) ? n : null;
|
|
|
+}
|
|
|
+
|
|
|
+/** 平台全字段行 → 矩阵行(只取 4 个可展示指标,其余字段留在 raw) */
|
|
|
+function platformRowToMatrix(row) {
|
|
|
+ const d = row.data || {};
|
|
|
+ return {
|
|
|
+ date: row.date,
|
|
|
+ shopKey: String(row.shopId || ''),
|
|
|
+ shopId: String(row.shopId || ''),
|
|
|
+ shopName: row.shopName || '',
|
|
|
+ school: row.school || '',
|
|
|
+ status: '',
|
|
|
+ order_cnt: numOrNull(d['有效订单']),
|
|
|
+ revenue: numOrNull(d['营业额']),
|
|
|
+ exposure: numOrNull(d['曝光人数']),
|
|
|
+ click_rate: numOrNull(d['进店转化率']),
|
|
|
+ raw: d,
|
|
|
+ quality: [],
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/** 人工表 + 平台明细合并:同一天同一店,人工优先,平台补位 */
|
|
|
+async function loadMergedRows(store, { from, to, schools, shops } = {}) {
|
|
|
+ const manual = await store.queryRows({ from, to, schools, shops });
|
|
|
+ let platform = [];
|
|
|
+ try {
|
|
|
+ platform = await store.queryPlatformRows({ from, to });
|
|
|
+ } catch { /* 平台表不可用不影响人工数据 */ }
|
|
|
+ const map = new Map();
|
|
|
+ for (const row of manual) map.set(`${row.date}|${row.shopKey}`, row);
|
|
|
+ for (const row of platform) {
|
|
|
+ const key = `${row.date}|${row.shopId}`;
|
|
|
+ if (map.has(key)) continue;
|
|
|
+ map.set(key, platformRowToMatrix(row));
|
|
|
+ }
|
|
|
+ let rows = [...map.values()];
|
|
|
+ if (schools && schools.length) rows = rows.filter((r) => schools.includes(String(r.school || '')));
|
|
|
+ if (shops && shops.length) rows = rows.filter((r) => shops.includes(String(r.shopId || r.shopKey || '')));
|
|
|
+ return rows;
|
|
|
+}
|
|
|
+
|
|
|
function csvCell(value) {
|
|
|
const text = value == null ? '' : String(value);
|
|
|
return /[",\n\r]/.test(text) ? '"' + text.replace(/"/g, '""') + '"' : text;
|
|
|
@@ -349,7 +394,7 @@ function register(app, ctx) {
|
|
|
const metrics = parseMetricsParam(query.metrics);
|
|
|
let rows;
|
|
|
try {
|
|
|
- rows = await store.queryRows({ from: from || undefined, to: to || undefined, schools, shops });
|
|
|
+ rows = await loadMergedRows(store, { from: from || undefined, to: to || undefined, schools, shops });
|
|
|
} catch (e) {
|
|
|
return sendJson(res, 503, { error: '查询失败', detail: e.message || String(e) });
|
|
|
}
|
|
|
@@ -477,7 +522,7 @@ function register(app, ctx) {
|
|
|
const metrics = parseMetricsParam(query.metrics);
|
|
|
let rows;
|
|
|
try {
|
|
|
- rows = await store.queryRows({ from: from || undefined, to: to || undefined, schools, shops });
|
|
|
+ rows = await loadMergedRows(store, { from: from || undefined, to: to || undefined, schools, shops });
|
|
|
} catch (e) {
|
|
|
return sendJson(res, 503, { error: '查询失败', detail: e.message || String(e) });
|
|
|
}
|