|
@@ -422,9 +422,9 @@ const syncState = {
|
|
|
|
|
|
|
|
function syncSnapshot() {
|
|
function syncSnapshot() {
|
|
|
const { running, cancelRequested, startedAt, finishedAt, error, entity, from, to,
|
|
const { running, cancelRequested, startedAt, finishedAt, error, entity, from, to,
|
|
|
- planTotal, done, current, added, updated, failed } = syncState;
|
|
|
|
|
|
|
+ planTotal, done, current, added, updated, failed, variantHits } = syncState;
|
|
|
return { running, cancelRequested, startedAt, finishedAt, error,
|
|
return { running, cancelRequested, startedAt, finishedAt, error,
|
|
|
- entity, from, to, planTotal, done, current, added, updated, failed };
|
|
|
|
|
|
|
+ entity, from, to, planTotal, done, current, added, updated, failed, variantHits: variantHits || 0 };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function dayShift(ymd, delta) {
|
|
function dayShift(ymd, delta) {
|
|
@@ -454,7 +454,7 @@ async function runSync({ from, to, scope = 'all', chainId, shopIds, port } = {})
|
|
|
Object.assign(syncState, {
|
|
Object.assign(syncState, {
|
|
|
running: true, cancelRequested: false, startedAt: new Date().toISOString(), finishedAt: null,
|
|
running: true, cancelRequested: false, startedAt: new Date().toISOString(), finishedAt: null,
|
|
|
error: null, entity: null, from: null, to: null,
|
|
error: null, entity: null, from: null, to: null,
|
|
|
- planTotal: 0, done: 0, current: '准备中', added: 0, updated: 0, failed: 0, steps: [],
|
|
|
|
|
|
|
+ planTotal: 0, done: 0, current: '准备中', added: 0, updated: 0, failed: 0, steps: [], variantHits: 0,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -490,10 +490,7 @@ async function runSync({ from, to, scope = 'all', chainId, shopIds, port } = {})
|
|
|
for (const s of shops) {
|
|
for (const s of shops) {
|
|
|
jobs.push({
|
|
jobs.push({
|
|
|
kind: 'shop', label: `门店·${s.shopName}`, params: { chainId: '-1', shopListIn: String(s.shopId) },
|
|
kind: 'shop', label: `门店·${s.shopName}`, params: { chainId: '-1', shopListIn: String(s.shopId) },
|
|
|
- fn: () => callHistory(
|
|
|
|
|
- { chainId: '-1', shopListIn: String(s.shopId) },
|
|
|
|
|
- { scope: 'shop', shopId: s.shopId, shopName: s.shopName, chainId: s.chainId || '', chainName: s.chainName || '' },
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ fn: () => callShopHistory(s, usedPort),
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -537,6 +534,7 @@ async function runSync({ from, to, scope = 'all', chainId, shopIds, port } = {})
|
|
|
cancelled: syncState.cancelRequested,
|
|
cancelled: syncState.cancelRequested,
|
|
|
added: syncState.added, updated: syncState.updated, failed: syncState.failed,
|
|
added: syncState.added, updated: syncState.updated, failed: syncState.failed,
|
|
|
done: syncState.done, total: syncState.planTotal,
|
|
done: syncState.done, total: syncState.planTotal,
|
|
|
|
|
+ variantHits: syncState.variantHits || 0,
|
|
|
steps: syncState.steps,
|
|
steps: syncState.steps,
|
|
|
};
|
|
};
|
|
|
store.appendSyncLog(entity.key, run);
|
|
store.appendSyncLog(entity.key, run);
|
|
@@ -583,6 +581,35 @@ async function callHistory(scopeKey, ctx, port = DEFAULT_CDP_PORT) {
|
|
|
return daily;
|
|
return daily;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/** 门店行是否有真实数据(全零 = 取数异常形态或确实无营业) */
|
|
|
|
|
+function shopRowsHaveData(rows) {
|
|
|
|
|
+ return (rows || []).some((r) => (Number(r.validOrderCnt) || 0) > 0 || (Number(r.grossAmt) || 0) > 0
|
|
|
|
|
+ || (Number(r.incomeAmt) || 0) > 0 || (Number(r.expUv) || 0) > 0);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 门店级取数(参数自愈):现行 chainId=-1+shopListIn 在部分账号(尤其新开单校区账号)
|
|
|
|
|
+ * 返回全零而接口侧数据确实存在。把取数诊断的 A/B/D 对照自动化进同步:
|
|
|
|
|
+ * A 现行(-1) → B 该店真实连锁ID → D 空连锁ID,哪个变体取到真实数据就用哪个;
|
|
|
|
|
+ * 三种都全零则按合法无营业返回首个结果(多花的两次请求量级可接受)。 */
|
|
|
|
|
+async function callShopHistory(s, port = DEFAULT_CDP_PORT) {
|
|
|
|
|
+ const ctx = { scope: 'shop', shopId: s.shopId, shopName: s.shopName, chainId: s.chainId || '', chainName: s.chainName || '' };
|
|
|
|
|
+ const variants = [
|
|
|
|
|
+ { tag: 'A', scopeKey: { chainId: '-1', shopListIn: String(s.shopId) } },
|
|
|
|
|
+ ...(s.chainId ? [{ tag: 'B', scopeKey: { chainId: String(s.chainId), shopListIn: String(s.shopId) } }] : []),
|
|
|
|
|
+ { tag: 'D', scopeKey: { chainId: '', shopListIn: String(s.shopId) } },
|
|
|
|
|
+ ];
|
|
|
|
|
+ let fallback;
|
|
|
|
|
+ for (const v of variants) {
|
|
|
|
|
+ const rows = await callHistory(v.scopeKey, ctx, port);
|
|
|
|
|
+ if (shopRowsHaveData(rows)) {
|
|
|
|
|
+ if (v.tag !== 'A') syncState.variantHits = (syncState.variantHits || 0) + 1;
|
|
|
|
|
+ return rows;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!fallback) fallback = rows;
|
|
|
|
|
+ }
|
|
|
|
|
+ return fallback;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function requestSyncCancel() {
|
|
function requestSyncCancel() {
|
|
|
if (syncState.running) syncState.cancelRequested = true;
|
|
if (syncState.running) syncState.cancelRequested = true;
|
|
|
return syncSnapshot();
|
|
return syncSnapshot();
|