Files
mama-box/js/pv.js

204 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 兜爸心选 · 页面访问记录(2026-11-07)
推送效果看板要看「推送打开后用户看了哪些页面」,用户同意做。后端 box_app:POST /api/mp/public/mama-box/pv(公开上报),
看板走 POST /api/mp/public/mama-box/pageviews/query(X-Api-Key)。说明见 box_app services/mama_box_pageview_serv.py。
一次「看一页」= 一个 visitId:进来 0.8 秒后报一条(停留 0),之后每 15 秒、切到后台、离开时报一次停留时长(只算页面在前台的时间)。
同一页里换内容也算新的一页:广场切笔记 / 话题 / 分类(pushState)、首页开关星空页(replaceState ?wh=1)—— 这里接管 history 自动识别,页面不用改。
首页 + 孕妈广场 + 测一测 + 专题页 / 计算器都引本文件(和 js/last-sub.js 放一起);新子页面记得加 script。
预览站 / 正式站由后端按路径分(/mama-box-preview/ → preview),这里不管。
11-07 同时报一份给数胎动后台(taidong.douba.top/admin「当天用户 / 全部用户」看每人逛了哪些页,375797cf 接的,app_id=mama-box):
openid-login 拿令牌(存 sessionStorage)→ events/open 报一次进站(visit_id 同一会话共用,30 分钟没动静算新一次)
→ 每离开一页 / 切到后台报一条 events(path /mama-box/…、中文 title、这段前台停留);每次离开页面再报一条不带 title 的
「整次停留」(累计值,对方取最新)。只在有 openid、没退出登录时报;预览站不报(不混进正式数据)。 */
(function () {
'use strict';
if (window.__mbPV) return;
window.__mbPV = 1;
var API = '/api/mp/public/mama-box/pv';
var BEAT = 15000;
var CAT_NAME = { family: '家庭婚姻', work: '职场方向', baby: '孕育经验' };
function param(k) {
var m = ((location.hash || '') + '&' + (location.search || '')).match(new RegExp('[#?&]' + k + '=([^&]*)'));
try { return m ? decodeURIComponent(m[1]).split('?')[0] : ''; } catch (e) { return ''; }
}
function who() {
if (window.mbGate && window.mbGate.out) return { openid: '', unionid: '' }; /* 11-07 退出登录 = 访客 */
var o = param('openid'), u = param('unionid');
if (!o) {
try {
var c = JSON.parse(sessionStorage.getItem('mmbox.identity') || 'null');
if (c && c.openid && (!c.at || Date.now() - c.at < 432e5)) { o = c.openid; u = u || c.unionid || ''; }
} catch (e) {}
}
return { openid: o, unionid: u };
}
/* 当前地址 → 是哪一页。base = 站点根(/mama-box-v2/ 或 /mama-box-preview/),rel = 之后的部分 */
function where() {
var p = location.pathname.replace(/index\.html$/, '');
var m = p.match(/^(\/[^/]+\/)(.*)$/) || [0, '/', ''];
var rel = m[2], q = new URLSearchParams(location.search), r = { group: 'other', page: rel || 'home', title: document.title || '' };
var T = function (v) { return String(v || '').replace(/\s*·\s*(测一测|孕妈广场)$/, ''); };
if (!rel) {
if (q.get('wh') === '1') { r.group = 'whisper'; r.page = 'whisper'; r.title = '孕妈悄悄话·星空页'; }
else { r.group = 'home'; r.page = 'home'; r.title = '妈妈盒子首页'; }
} else if (rel === 'guangchang/ceshi/') {
var id = q.get('id') || '', t = (window.GC_TESTS || []).filter(function (x) { return x.id === id; })[0];
r.group = 'ceshi'; r.page = id ? 'ceshi/test' : 'ceshi/list'; r.testId = id;
r.testTitle = t ? t.title : (id ? T(document.title) : '');
r.title = id ? '测一测·' + (r.testTitle || id) : '测一测·列表';
} else if (rel === 'guangchang/mine/') {
r.group = 'plaza'; r.page = 'plaza/mine'; r.title = '孕妈广场·我的投稿';
} else if (rel === 'guangchang/') {
var n = q.get('n') || '', tp = q.get('t') || '', c = q.get('c') || '';
var tid = tp || (n ? n.replace(/-\d+$/, '') : '');
var topic = (window.GC_TOPICS || []).filter(function (x) { return x.id === tid; })[0];
r.group = 'plaza';
if (tid) { r.topicId = tid; r.topicTitle = topic ? topic.name : ''; }
if (n) {
var d = window.GC_DATA && GC_DATA[tid], i = +(n.match(/-(\d+)$/) || [0, 0])[1];
r.page = 'plaza/note'; r.noteId = n; r.noteTitle = d && d.notes && d.notes[i - 1] ? d.notes[i - 1].title : '';
r.title = '孕妈广场·笔记详情';
} else if (tp) { r.page = 'plaza/topic'; r.title = '孕妈广场·话题'; }
else if (c === 'ceshi') { r.group = 'ceshi'; r.page = 'ceshi/list'; r.title = '测一测·列表'; }
else if (c) { r.page = 'plaza/cat'; r.title = '孕妈广场·' + (CAT_NAME[c] || c); }
else if (q.get('write') === '1') { r.page = 'plaza/write'; r.title = '孕妈广场·写笔记'; }
else if (q.get('fav') === '1') { r.page = 'plaza/fav'; r.title = '孕妈广场·我的收藏'; }
else if (q.get('mine') === '1') { r.page = 'plaza/mine'; r.title = '孕妈广场·我的投稿'; }
else { r.page = 'plaza/home'; r.title = '孕妈广场'; }
} else if (/^topic\/[a-z0-9_-]+\/$/.test(rel)) {
r.group = 'topic'; r.page = rel.replace(/\/$/, ''); r.title = T(document.title) || r.page;
}
r.key = r.page + '|' + (r.noteId || '') + '|' + (r.topicId || '') + '|' + (r.testId || '');
/* 净地址:不带身份(openid/unionid 在 hash,不记) */
r.path = location.pathname + (location.search || '').replace(/([?&])(openid|unionid)=[^&]*/g, '$1').replace(/[?&]+$/, '');
return r;
}
function send(o) {
var s = JSON.stringify(o);
try { if (navigator.sendBeacon && navigator.sendBeacon(API, new Blob([s], { type: 'text/plain' }))) return; } catch (e) {}
try { fetch(API, { method: 'POST', body: s, keepalive: true, headers: { 'Content-Type': 'text/plain' } }).catch(function () {}); } catch (e) {}
}
function vid() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 10) + Math.random().toString(36).slice(2, 6); }
/* ---- 数胎动后台 ---- */
var TD = '/taidong/api/', TD_ON = /^\/mama-box-v2\//.test(location.pathname) || (function () { try { return localStorage.getItem('mmbox.tdtest') === '1'; } catch (e) { return false; } })() /* 预览自测:本机手动置 mmbox.tdtest=1 才报 */, TDK = 'mmbox.td.v1';
function tdState() {
var st = null;
try { st = JSON.parse(sessionStorage.getItem(TDK) || 'null'); } catch (e) {}
var now = Date.now();
if (!st || now - (st.seen || 0) > 30 * 60000) st = { vid: 'mb-' + now + '-' + Math.random().toString(36).slice(2, 8), start: now, acc: 0, opened: 0, tok: st && st.tok, oid: st && st.oid };
st.seen = now;
return st;
}
function tdSave(st) { try { sessionStorage.setItem(TDK, JSON.stringify(st)); } catch (e) {} }
var tdTok = null;
function tdToken() {
if (!TD_ON) return Promise.resolve('');
var w = who();
if (!w.openid) return Promise.resolve('');
var st = tdState();
if (st.tok && st.oid === w.openid) return Promise.resolve(st.tok);
if (tdTok) return tdTok;
tdTok = fetch(TD + 'auth/openid-login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ app_id: 'mama-box', openid: w.openid, unionid: w.unionid || undefined }) })
.then(function (r) { return r.json(); }).then(function (j) {
var t = j && (j.token || (j.data && j.data.token)) || '';
var s2 = tdState(); s2.tok = t; s2.oid = w.openid; tdSave(s2);
return t;
}).catch(function () { return ''; }).then(function (t) { tdTok = null; return t; });
return tdTok;
}
function tdPost(path, body, tok) {
if (!tok) return;
try { fetch(TD + path, { method: 'POST', keepalive: true, headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + tok }, body: JSON.stringify(body) }).catch(function () {}); } catch (e) {}
}
function tdTitle(r) {
var t = r.page === 'home' ? '首页' : r.page === 'whisper' ? '孕妈悄悄话 · 星空页'
: r.page === 'plaza/note' ? '帖子详情 · ' + (r.noteTitle || r.noteId)
: r.page === 'plaza/topic' ? '话题 · ' + (r.topicTitle || r.topicId)
: r.page === 'ceshi/test' ? '测一测 · ' + (r.testTitle || r.testId)
: r.group === 'topic' ? '专题 · ' + r.title
: String(r.title || '').replace('·', ' · ');
return t.slice(0, 40);
}
function tdOpen() {
tdToken().then(function (tok) {
var st = tdState();
if (!tok || st.opened) { tdSave(st); return; }
st.opened = 1; tdSave(st);
tdPost('events/open', { visit_id: st.vid, page: '/mama-box/', src: param('src') }, tok);
});
}
/* 一段前台停留结束(离开这一页 / 切到后台):报这一页 + 整次累计 */
function tdSeg(c, ms, leaving) {
if (!TD_ON || !c || ms < 300) return;
var st = tdState(); st.acc += ms; tdSave(st);
var r = c.full || c.r, path = '/mama-box/' + (r.page === 'home' ? '' : r.page === 'whisper' ? 'whisper' : location.pathname.replace(/^\/[^/]+\//, '').replace(/index\.html$/, '').replace(/\/$/, ''));
var ev = { visit_id: st.vid, path: path.replace(/\/$/, '') || '/mama-box/', title: tdTitle(r), duration_ms: ms, entered_at: c.segAt };
if (ev.path === '/mama-box') ev.path = '/mama-box/';
var site = { visit_id: st.vid, path: '/mama-box/', duration_ms: st.acc };
tdToken().then(function (tok) { tdPost('events', ev, tok); if (leaving) tdPost('events', site, tok); });
}
var cur = null, timer = 0;
function dur() {
if (!cur) return 0;
return cur.acc + (cur.shownAt ? Date.now() - cur.shownAt : 0);
}
function beat() {
if (!cur || !cur.sent) return;
var d = dur();
if (d - cur.lastDur < 1000) return;
cur.lastDur = d;
var w = who();
send({ visitId: cur.id, durationMs: d, openid: w.openid, unionid: w.unionid });
}
function first(c) {
if (c !== cur || c.sent) return;
var r = where(); /* 晚 0.8 秒再取:专题页 / 测试页的标题、广场数据那时才就绪 */
if (r.key !== c.r.key) r = c.r;
var w = who();
c.sent = true; c.full = r;
send({
visitId: c.id, openid: w.openid, unionid: w.unionid, page: r.page, pageGroup: r.group, pageTitle: r.title,
noteId: r.noteId || '', noteTitle: r.noteTitle || '', topicId: r.topicId || '', topicTitle: r.topicTitle || '',
testId: r.testId || '', testTitle: r.testTitle || '', path: c.r.path, enteredAt: c.at, durationMs: dur()
});
}
function close() {
if (!cur) return;
if (cur.shownAt) tdSeg(cur, Date.now() - cur.segAt, true);
if (cur.sent) beat(); else if (dur() >= 800) first(cur);
cur = null;
}
function open() {
var r = where();
if (cur && cur.r.key === r.key) return;
close();
var c = cur = { id: vid(), r: r, at: Date.now(), segAt: Date.now(), acc: 0, shownAt: document.hidden ? 0 : Date.now(), sent: false, lastDur: 0 };
setTimeout(function () { first(c); }, 800);
}
['pushState', 'replaceState'].forEach(function (k) {
var f = history[k];
if (typeof f !== 'function') return;
history[k] = function () { var x = f.apply(this, arguments); try { setTimeout(open, 0); } catch (e) {} return x; };
});
window.addEventListener('popstate', function () { setTimeout(open, 0); });
document.addEventListener('visibilitychange', function () {
if (!cur) return;
if (document.hidden) { if (cur.shownAt) { cur.acc += Date.now() - cur.shownAt; tdSeg(cur, Date.now() - cur.segAt, true); cur.shownAt = 0; } beat(); }
else if (!cur.shownAt) { cur.shownAt = cur.segAt = Date.now(); tdOpen(); }
});
window.addEventListener('pagehide', close);
window.addEventListener('pageshow', function (e) { if (e.persisted) open(); }); /* 从后退缓存回来 = 又看一次 */
timer = setInterval(function () { if (!document.hidden) beat(); }, BEAT);
open();
if (TD_ON) tdOpen();
})();