feat(h5): 首页「上次比价」横幅接真实 /api/v1/compare/records(4 分钟新鲜窗口)
删写死 mock(DEFAULT_LAST_COMPARE_RECORD + localStorage 读),改带 Bearer token 拉真实比价记录;取最近一条 status=success 且 created_at 在 4 分钟内才显示(对齐原生 HomeViewModel.loadLastCompare),否则/未登录/401 隐藏横幅;进页 + 回前台 visibilitychange 各拉一次;点击仍走 SGBridge.navigate('compareResult')。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+45
-19
@@ -4931,7 +4931,7 @@ TODO: 补充 @owner / @from / @to / @notes(参照 FLOW.md)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="last-compare-banner" id="lastCompareBanner" onclick="go('compresult')" aria-live="polite">
|
||||
<div class="last-compare-banner" id="lastCompareBanner" onclick="go('compresult')" aria-live="polite" style="display:none">
|
||||
<div class="lcb-head">
|
||||
<span class="lcb-kicker" id="lastCompareAge">上次比价</span>
|
||||
<span class="lcb-status"></span>
|
||||
@@ -8997,7 +8997,9 @@ function goAuthLocation() {
|
||||
openLocSysPopup();
|
||||
}
|
||||
// 回前台(定位授权窗 / 设置返回)重核授权态并刷新 feed 区(纯 H5,不依赖原生事件)。
|
||||
document.addEventListener('visibilitychange', function(){ if (document.visibilityState === 'visible') { try { applyLocationState(); } catch(e){} } });
|
||||
document.addEventListener('visibilitychange', function(){ if (document.visibilityState === 'visible') { try { applyLocationState(); } catch(e){} try { renderLastCompareBanner(); } catch(e){} } });
|
||||
// 「上次比价」横幅:进页拉一次 + 回前台(上面 visibilitychange)再拉(4 分钟窗口随时间过期;在外卖 App 比完价回来即出)
|
||||
document.addEventListener('DOMContentLoaded', function(){ try { renderLastCompareBanner(); } catch(e){} });
|
||||
function isHomeNearbyEmpty(feed) {
|
||||
try {
|
||||
var params = new URLSearchParams(location.search);
|
||||
@@ -9089,13 +9091,7 @@ function renderTriClaim() {
|
||||
if (entry) entry.classList.toggle('coupon-claimed', homeCouponClaimed);
|
||||
}
|
||||
|
||||
const LAST_COMPARE_RECORD_KEY = 'proto_last_compare_record';
|
||||
const DEFAULT_LAST_COMPARE_RECORD = {
|
||||
shop: '窑鸡王(康营店)',
|
||||
items: ['招牌窑鸡整只-原味', '窑鸡饭套餐'],
|
||||
bestPlatform: '淘宝',
|
||||
bestPrice: 57.9
|
||||
};
|
||||
// 「上次比价」数据改由后端 /api/v1/compare/records 实拉(见 renderLastCompareBanner);原写死 mock 已移除。
|
||||
const LAST_COMPARE_VARIANTS = ['reference', 'tagright', 'topbar', 'pricebadge', 'mutedright'];
|
||||
const LAST_COMPARE_PLATFORM_LOGOS = {
|
||||
'淘宝': 'assets/shared/logos/taobao.png',
|
||||
@@ -9112,20 +9108,50 @@ function getLastCompareVariant() {
|
||||
return LAST_COMPARE_VARIANTS.includes(value) ? value : 'reference';
|
||||
}
|
||||
|
||||
function readLastCompareRecord() {
|
||||
try {
|
||||
const raw = localStorage.getItem(LAST_COMPARE_RECORD_KEY);
|
||||
return raw ? JSON.parse(raw) : null;
|
||||
} catch(e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function renderLastCompareBanner() {
|
||||
const banner = document.getElementById('lastCompareBanner');
|
||||
if (!banner) return;
|
||||
const token = (window.SGBridge && SGBridge.getToken && SGBridge.getToken()) || '';
|
||||
if (!token) { banner.style.display = 'none'; return; } // 未登录 → 隐藏
|
||||
fetch('/api/v1/compare/records?limit=5', { headers: { 'Authorization': 'Bearer ' + token } })
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (d) {
|
||||
const rec = pickFreshLastCompare(d && d.items);
|
||||
if (!rec) { banner.style.display = 'none'; return; } // 无成功 / 超 4 分钟窗口 / 401 → 隐藏
|
||||
banner.style.display = '';
|
||||
fillLastCompareBanner(lastCompareToSummary(rec));
|
||||
})
|
||||
.catch(function () { banner.style.display = 'none'; });
|
||||
}
|
||||
|
||||
const record = readLastCompareRecord() || DEFAULT_LAST_COMPARE_RECORD;
|
||||
// 取最近一条成功比价并卡 4 分钟新鲜窗口(records 按 created_at 倒序,首条成功即最新成功)。
|
||||
function pickFreshLastCompare(items) {
|
||||
if (!Array.isArray(items)) return null;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const it = items[i];
|
||||
if (it && it.status === 'success') {
|
||||
const t = Date.parse(it.created_at);
|
||||
if (isNaN(t)) return null;
|
||||
const age = Date.now() - t; // 容 1 分钟时钟偏差(同原生 toFreshSummary)
|
||||
return (age >= -60000 && age <= 4 * 60 * 1000) ? it : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 后端 ComparisonRecordOut → 横幅摘要(对齐原生 LastCompareSummary)。
|
||||
function lastCompareToSummary(rec) {
|
||||
return {
|
||||
shop: rec.store_name || '',
|
||||
items: (rec.items || []).map(function (it) { return it && it.name; }).filter(Boolean),
|
||||
bestPlatform: rec.best_platform_name || '',
|
||||
bestPrice: rec.best_price_cents != null ? rec.best_price_cents / 100 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
function fillLastCompareBanner(record) {
|
||||
const banner = document.getElementById('lastCompareBanner');
|
||||
if (!banner) return;
|
||||
const shopEl = document.getElementById('lastCompareShop');
|
||||
const dishEl = document.getElementById('lastCompareDish');
|
||||
const ageEl = document.getElementById('lastCompareAge');
|
||||
|
||||
@@ -70,3 +70,19 @@ def test_h5_home_feed_wired(client) -> None:
|
||||
assert "茶百道" not in body # 写死的 mock 卡(店名)已移除
|
||||
assert "chabaidao.png" not in body # 写死的 mock 头图已移除
|
||||
assert "比团购省 5.5 元" not in body # 随原生:mock 卡的「比团购省」标签已删(渲染只用 price_label/rank_label)
|
||||
|
||||
|
||||
def test_h5_home_last_compare_wired(client) -> None:
|
||||
# 「上次比价」横幅改接真实 /api/v1/compare/records + 4 分钟新鲜窗口;原写死 mock 默认值已移除
|
||||
resp = client.get("/h5/home/index.html")
|
||||
assert resp.status_code == 200
|
||||
body = resp.text
|
||||
assert "/api/v1/compare/records" in body # 接真实比价记录端点
|
||||
assert "pickFreshLastCompare" in body # 4 分钟新鲜窗口逻辑
|
||||
assert "DEFAULT_LAST_COMPARE_RECORD" not in body # 写死 mock 默认已移除
|
||||
|
||||
|
||||
def test_compare_records_requires_auth(client) -> None:
|
||||
# 横幅靠 Bearer token 才拿得到数据;无鉴权必须 401(无桥/未登录时横幅隐藏)
|
||||
resp = client.get("/api/v1/compare/records?limit=5")
|
||||
assert resp.status_code == 401
|
||||
|
||||
Reference in New Issue
Block a user