feat(site): 方案 C - 「我常访问」个人榜(匿名 ID + localStorage 合并)

- NavClick 加 anon_id 字段(已为历史表自动迁移)
- 后端静默下发 wk_anon cookie(uuid4,1 年,HttpOnly)
- 点击埋点写入 anon_id;新增 GET /site/api/my-top 与 POST /site/api/my-clear
- 首页服务端渲染「我常访问」区(时间衰减加权分),与「热门网址」共显
- 前端 localStorage 累计 + 与服务端榜取 max 合并;冷启动阈值 2 个网址
- 一键清除:清 localStorage + 服务端删该匿名 ID 的全部记录
- 移动端抽屉、顶部导航同步加「我常访问」入口
- 底部隐私说明更新
This commit is contained in:
bwstudio
2026-09-06 19:16:45 +08:00
parent a47ad991d0
commit 34e8010830
7 changed files with 521 additions and 62 deletions
+48
View File
@@ -0,0 +1,48 @@
"""一次性脚本:给已有的 site_nav_click 表加 anon_id 列(幂等,可重复执行)。
用法: PYTHONPATH=. python scripts/ensure_nav_click_anon_id.py
"""
import os
import sqlite3
from app import app
from applications.extensions import db
with app.app_context():
uri = str(db.engine.url)
if not uri.startswith('sqlite'):
print('非 SQLite,跳过(MySQL 请用 flask db migrate/upgrade 或手工 ALTER TABLE')
raise SystemExit(0)
# 注意:用 engine.url.database,而不是配置里的相对路径(配置可能是 sqlite:///../pear.db
path = db.engine.url.database or ''
path = os.path.abspath(os.path.normpath(path))
print('DB:', path)
conn = sqlite3.connect(path)
cur = conn.cursor()
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='site_nav_click'")
if not cur.fetchone():
print('表不存在,交给 db.create_all() 处理')
conn.close()
from applications.extensions import db
from applications.models import NavClick # noqa: F401
db.create_all()
print('OK: 已建表(含 anon_id')
raise SystemExit(0)
cur.execute('PRAGMA table_info(site_nav_click)')
cols = [r[1] for r in cur.fetchall()]
print('现有列:', cols)
if 'anon_id' in cols:
print('OK: anon_id 已存在,无需变更')
else:
cur.execute("ALTER TABLE site_nav_click ADD COLUMN anon_id VARCHAR(36) DEFAULT ''")
cur.execute('CREATE INDEX IF NOT EXISTS ix_site_nav_click_anon_id ON site_nav_click (anon_id)')
conn.commit()
print('OK: 已添加 anon_id 列 + 索引')
cur.execute('PRAGMA table_info(site_nav_click)')
print('变更后列:', [r[1] for r in cur.fetchall()])
conn.close()