const BASE = "http://localhost:8090"; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function jsonFetch(path, opts = {}, retries = 8) { let lastErr; for (let i = 0; i < retries; i++) { try { const res = await fetch(BASE + path, opts); if (res.ok) return { status: res.status, data: await res.json() }; return { status: res.status, data: await res.json().catch(() => null) }; } catch (e) { lastErr = e; await sleep(800); } } throw lastErr; } // 1) 登录 root const login = await jsonFetch("/api/v1/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ account: "root", password: "root" }), }); const tok = login?.data?.access_token; if (!tok) { console.log("LOGIN_FAIL", JSON.stringify(login)); process.exit(1); } console.log("LOGIN_OK uid=", login.data.user?.id); const auth = (m, p, b) => jsonFetch(p, { method: m, headers: { "Content-Type": "application/json", Authorization: "Bearer " + tok }, body: b ? JSON.stringify(b) : undefined, }); // 2) 取一篇真实走秀文章的对外编码 id const list = await jsonFetch("/api/v1/public/runway-looks?page=1&size=1&locale=en"); const art = list?.data?.data?.[0]; if (!art?.id) { console.log("NO_ARTICLE", JSON.stringify(list).slice(0, 300)); process.exit(1); } const articleId = String(art.id); console.log("ARTICLE id=", articleId, "title=", art.title?.slice(0, 30)); // 3) 记录一次浏览(文章级) const post1 = await auth("POST", "/api/v1/auth/history", { target_uid: articleId }); console.log("POST1 status=", post1.status, JSON.stringify(post1.data)); // 4) 列表应含该文章 const get1 = await auth("GET", "/api/v1/auth/history?page=1&per_page=24"); const g1 = get1.data?.data; console.log("GET1 total=", g1?.total, "items=", JSON.stringify(g1?.items?.map((x) => x.id))); // 5) 再次记录同一篇(应 upsert,total 不增) await auth("POST", "/api/v1/auth/history", { target_uid: articleId }); const get2 = await auth("GET", "/api/v1/auth/history?page=1&per_page=24"); console.log("GET2(after dup POST) total=", get2.data?.data?.total, "(应为 1,upsert 生效)"); // 6) 回查详情接口(账户页同款)——确认能取到封面/标题 const detail = await jsonFetch("/api/v1/public/runway-looks/" + articleId + "?locale=en"); console.log("DETAIL_OK title=", detail.data?.data?.title?.slice(0, 30), "cover=", detail.data?.data?.cover?.slice(0, 40)); process.exit(0);