chore: snapshot production code before Apple Design UI refinement
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const baseUrl = process.env.GB32960_BASE_URL || "http://115.29.187.205:20200";
|
||||
const vin = process.env.GB32960_VIN || "LB9A32A24R0LS1720";
|
||||
const plate = process.env.GB32960_PLATE || "粤AGP4377";
|
||||
const exportDate = process.env.GB32960_DATE || "2026-08-17";
|
||||
const dateFrom = `${exportDate} 00:00:00`;
|
||||
const dateTo = `${exportDate} 23:59:59`;
|
||||
const limit = 500;
|
||||
const outputPath = process.env.GB32960_DATA_PATH || new URL("./raw-frames.json", import.meta.url).pathname;
|
||||
|
||||
async function fetchPage(offset, includeTotal = false) {
|
||||
const params = new URLSearchParams({
|
||||
protocol: "GB32960",
|
||||
vin,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
orderBy: "eventTime",
|
||||
limit: String(limit),
|
||||
offset: String(offset),
|
||||
includeFields: "true",
|
||||
includePayload: "true",
|
||||
includeTotal: includeTotal ? "true" : "false",
|
||||
});
|
||||
const response = await fetch(`${baseUrl}/api/history/raw-frames?${params}`, {
|
||||
headers: { "Accept-Encoding": "gzip" },
|
||||
signal: AbortSignal.timeout(60000),
|
||||
});
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
const first = await fetchPage(0, true);
|
||||
const total = Number(first.total ?? 0);
|
||||
const offsets = [];
|
||||
for (let offset = limit; offset < total; offset += limit) offsets.push(offset);
|
||||
const remaining = await Promise.all(offsets.map((offset) => fetchPage(offset)));
|
||||
const items = [...(first.items ?? []), ...remaining.flatMap((page) => page.items ?? [])];
|
||||
|
||||
const frameIds = new Set(items.map((item) => item.frame_id));
|
||||
const eventIds = new Set(items.map((item) => item.event_id));
|
||||
const fieldKeys = [...new Set(items.flatMap((item) => Object.keys(item.parsed_fields ?? {})))].sort();
|
||||
const messageCounts = Object.fromEntries(
|
||||
[...items.reduce((map, item) => map.set(item.message_id_hex, (map.get(item.message_id_hex) ?? 0) + 1), new Map())]
|
||||
.sort(([a], [b]) => a.localeCompare(b)),
|
||||
);
|
||||
|
||||
if (items.length !== total) throw new Error(`row count mismatch: fetched=${items.length} total=${total}`);
|
||||
if (frameIds.size !== items.length) throw new Error(`duplicate frame_id: unique=${frameIds.size} rows=${items.length}`);
|
||||
if (eventIds.size !== items.length) throw new Error(`duplicate event_id: unique=${eventIds.size} rows=${items.length}`);
|
||||
for (const item of items) {
|
||||
if (item.protocol !== "GB32960" || item.vin !== vin) throw new Error(`scope mismatch at ${item.frame_id}`);
|
||||
if (item.ts < dateFrom || item.ts > dateTo) throw new Error(`time out of range at ${item.frame_id}: ${item.ts}`);
|
||||
}
|
||||
|
||||
const output = {
|
||||
exportedAt: new Date().toISOString(),
|
||||
source: `${baseUrl}/api/history/raw-frames`,
|
||||
query: { plate, vin, protocol: "GB32960", dateFrom, dateTo, orderBy: "eventTime" },
|
||||
verification: {
|
||||
apiTotal: total,
|
||||
fetchedRows: items.length,
|
||||
uniqueFrameIds: frameIds.size,
|
||||
uniqueEventIds: eventIds.size,
|
||||
earliestTs: items.reduce((min, item) => !min || item.ts < min ? item.ts : min, ""),
|
||||
latestTs: items.reduce((max, item) => !max || item.ts > max ? item.ts : max, ""),
|
||||
parsedFieldCount: fieldKeys.length,
|
||||
messageCounts,
|
||||
},
|
||||
fieldKeys,
|
||||
items,
|
||||
};
|
||||
|
||||
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
||||
await fs.writeFile(outputPath, JSON.stringify(output), "utf8");
|
||||
console.log(JSON.stringify({ outputPath, ...output.verification }, null, 2));
|
||||
Reference in New Issue
Block a user