| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- /* data.js —— Parse REST 读取层。
- * 只带 X-Parse-Application-Id(公开读),不带任何凭据。
- * 不用 count 参数(服务端已知问题),一律取 results 长度。 */
- (function () {
- "use strict";
- const cfg = window.KF_CONFIG;
- const state = {
- loadedAt: null,
- ok: false,
- error: null,
- truncated: [], // 被截断的表名
- counts: {}
- };
- /* Parse 的日期在各字段里是 {"__type":"Date","iso":"…"}(或 {"__type":"Date","__d"…})。
- * 下游到处 new Date(x) 排序、算时长,逐个处理容易漏——在入口一次性换成 ISO 串。
- * 只处理带 __type 的对象本身;数组/普通对象(scores、decision、report)原样递归。 */
- function normalizeDates(v) {
- if (Array.isArray(v)) return v.map(normalizeDates);
- if (v && typeof v === "object") {
- if (v.__type === "Date") {
- window.__DBGD = (window.__DBGD||[]).concat([JSON.stringify(v)]);
- const t = v.iso ? new Date(v.iso).getTime() : v.__d != null ? Number(v.__d) : NaN;
- return Number.isFinite(t) ? new Date(t).toISOString() : null; /* 统一成 UTC ISO,避免各浏览器解析口径不一 */
- }
- const out = {};
- for (const k in v) out[k] = normalizeDates(v[k]);
- return out;
- }
- return v;
- }
- function headers() {
- return { "X-Parse-Application-Id": cfg.appId, Accept: "application/json" };
- }
- async function getJSON(path) {
- const res = await fetch(cfg.parseBase + path, { headers: headers(), cache: "no-store" });
- if (!res.ok) throw new Error(`HTTP ${res.status} · ${path}`);
- return res.json();
- }
- /* 分页拉取:单页 pageLimit 条,用 skip 往后翻,直到不足一页。
- * 线下 40 人两天写下来完全可能超过 1000 条;超过时要能全部取到,而不是悄悄截断。
- * 翻了 50 页仍满页才标记"被截断",交给 UI 明示。 */
- async function fetchClass(cls) {
- const page = Math.max(1, cfg.pageLimit || 1000);
- const rows = [];
- let skip = 0;
- for (let guard = 0; guard < 50; guard++) {
- const q = `?limit=${page}&skip=${skip}&order=createdAt`;
- const json = await getJSON(`/classes/${cls}${q}`);
- const batch = Array.isArray(json.results) ? normalizeDates(json.results) : [];
- rows.push(...batch);
- if (batch.length < page) return rows;
- skip += page;
- }
- state.truncated.push(cls); /* 翻了 50 页还没完:如实标记被截断 */
- return rows;
- }
- /* 拉全部六张表。任何一张失败都整体失败并给出原因,UI 显示错误态而不是空态 */
- async function load() {
- state.truncated = [];
- const c = cfg.classes;
- const names = ["project", "artifact", "insight", "source", "session", "opportunity"];
- try {
- const rows = await Promise.all(names.map((n) => fetchClass(c[n])));
- const out = {};
- names.forEach((n, i) => { out[n] = rows[i]; });
- Object.assign(out, { loadedAt: new Date().toISOString(), truncated: state.truncated.slice() });
- state.ok = true;
- state.error = null;
- state.loadedAt = out.loadedAt;
- state.counts = { project: out.project.length, artifact: out.artifact.length,
- insight: out.insight.length, source: out.source.length, session: out.session.length,
- opportunity: out.opportunity.length };
- return out;
- } catch (e) {
- state.ok = false;
- state.error = e.message || String(e);
- throw e;
- }
- }
- window.KF_DATA = { load, state, normalizeDates };
- })();
|