This commit is contained in:
toom1996
2026-09-14 00:42:45 +08:00
parent d6cb616337
commit 548155625d
22 changed files with 707 additions and 454 deletions

View File

@ -1,17 +1,25 @@
// src/lib/history.ts — 浏览历史(服务端持久化 + 本地缓存兜底)
// src/lib/history.ts — 浏览历史(本地缓存兜底 + 服务端持久化)
//
// 2026-09-02 语义收敛(用户拍板):
// 职责:历史的本地缓存与去重、以及「服务端调用」的编排。
// 真正的 HTTP 端点(/me/history*)与单篇回查(getHistoryMeta)在 api.ts。
//
// 语义(2026-09-02 用户拍板):
// - 浏览历史 = 「点开过哪篇文章」,触发时机在 item 详情页(打开某篇走秀/街拍即记一条)。
// 列表页(Runway Looks / Street Snaps)进入不再记。
// - 后端 histories 表只存 (user_id, target_uid, viewed_at):target_uid 为文章对外编码串
// (首字符区分类型:s 开头=街拍 / r 开头=走秀,编码串自带类型,故不再有 kind/target_type 概念),
// 表已去掉 title/cover/brand 快照列。
// - 账户页 My History 展示:先取服务端 id 列表,再逐篇回查公开详情接口拿 title/cover/brand
// (用户选「只存 id、展示时回查」),历史卡片点击跳回文章详情。
// 列表页进入不记。
// - 服务端只存 (user_id, target_uid, viewed_at):target_uid 为文章对外编码串
// (首字符区分类型:s 开头=街拍 / r 开头=走秀)。
// - 账户页 My History 展示:先取服务端 id 列表,再逐篇回查公开详情接口拿 title/cover/brand。
// - 未登录:纯本地 localStorage(按用户名隔离);登录态写本地 + 后台 POST(失败不回滚本地)。
import { getHistoryMeta } from "./api"
import { getUser, getAccessToken, authJson } from "./auth"
import { getUser, getAccessToken } from "./auth"
import {
getHistoryMeta,
addHistory,
fetchHistory,
deleteHistory,
clearAllHistory,
type ServerHistory,
} from "./api"
export interface HistoryData {
title: string
@ -36,12 +44,6 @@ function dedupeKey(id: string): string {
return /^[rsij]/.test(s) ? s.slice(1) : s
}
// 后端 HistoryItem 响应结构({data:{items:[...]}} 解包后,仅 id + viewed_at)
interface ServerHistory {
id: string
viewed_at?: number
}
// ---------- 本地存储(兜底 / 离线) ----------
function storageKey(): string | null {
const u = getUser()
@ -90,7 +92,7 @@ export function recordHistory(id: string): void {
setLocal(list)
if (serverMode()) {
authJson("POST", "/me/history", { target_uid: id }).catch(() => {
addHistory(id).catch(() => {
/* 失败不回滚本地 */
})
}
@ -125,12 +127,8 @@ export async function fetchHistoryPage(page = 1, perPage = 24): Promise<HistPage
return { items: local, total: local.length, lastPage: 1 }
}
try {
const qs = `page=${page}&per_page=${perPage}`
const r = await authJson<{ items: ServerHistory[]; total: number; last_page: number }>(
"GET",
`/me/history?${qs}`,
)
const base: HistoryEntry[] = (r?.items ?? []).map((it) => ({
const r = await fetchHistory(page, perPage)
const base: HistoryEntry[] = (r?.items ?? []).map((it: ServerHistory) => ({
id: String(it.id),
viewedAt: Number(it.viewed_at) || 0,
}))
@ -163,7 +161,7 @@ export function removeHistory(id: string): void {
const key = dedupeKey(id)
setLocal(getLocal().filter((h) => dedupeKey(h.id) !== key))
if (serverMode()) {
authJson("DELETE", `/me/history/${encodeURIComponent(id)}`).catch(() => {
deleteHistory(id).catch(() => {
/* 失败不回滚本地 */
})
}
@ -173,7 +171,7 @@ export function removeHistory(id: string): void {
export function clearHistory(): void {
setLocal([])
if (serverMode()) {
authJson("DELETE", "/me/history").catch(() => {
clearAllHistory().catch(() => {
/* 失败不回滚本地 */
})
}