feat(ov): windowed dedup visit counting with retention cleanup
Task 8 (P2): POST /public/albums/{token}/visits with visit_sessions
window dedup (VISIT_WINDOW_MINUTES), requestId idempotent replay
(echoes stored outcome; expired beyond VISIT_REQUEST_TTL_MINUTES
rejected), album_stats viewCount/statVersion authority + outbox_stat_events
views snapshots, WS stats.updated broadcast, visitorId+IP fixed-window
rate limit (VISIT_WINDOW_REQUESTS), and ov CleanupModule scheduler
(setInterval, CLEANUP_INTERVAL_MINUTES) deleting like_batches /
visit_requests / visit_sessions past their windows — totals never
touched. Contract prose aligned to replay semantics; constants
VISIT_WINDOW_REQUESTS / CLEANUP_INTERVAL_MINUTES added.
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1202,12 +1202,14 @@ paths:
|
||||
加载成功后上报一次(列表/图片/WS 等接口调用一律不触发计数)。
|
||||
|
||||
- requestId 为上报幂等键(建议 UUID),counted 确认前跨刷新/重试持久复用;
|
||||
重复 requestId 返回 counted=false 与权威 viewCount,不重复计数。
|
||||
重复 requestId 幂等重放:counted 回显该 requestId 首次处理的结局
|
||||
(确认响应丢失后重试仍见 true),viewCount 恒为当前权威值,不重复计数。
|
||||
- 去重窗口(VISIT_WINDOW_MINUTES,默认 30 分钟,D07)内同一 visitorId
|
||||
不重复计数;窗口过后再上报按新访次计数。
|
||||
- 过期 requestId(VISIT_REQUEST_TTL_MINUTES,默认 35 分钟)拒绝重放
|
||||
(counted=false),不得重新加计。
|
||||
- 基础限速:visitorId 滑窗,超限 429(RATE_LIMITED);去重本身为主缓解。
|
||||
(counted=false,无论原结局),不得重新加计。
|
||||
- 基础限速:visitorId+来源 IP 固定窗(VISIT_WINDOW_REQUESTS,默认
|
||||
60/窗),超限 429(RATE_LIMITED);去重本身为主缓解。
|
||||
- 相册非公开/紧急停用 404(ALBUM_NOT_PUBLIC)。
|
||||
security: []
|
||||
requestBody:
|
||||
@@ -2666,8 +2668,9 @@ components:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
description: |
|
||||
本次是否实际计数:true=新访次;false=去重窗口内重复、重复
|
||||
requestId 或过期 requestId 重放(均不重复计数)。
|
||||
本次是否计入:true=新访次,或幂等重放已确认计入的 requestId
|
||||
(回显原结局);false=去重窗口内重复或过期 requestId 重放
|
||||
(均不重复计数)。
|
||||
|
||||
# ── 内部同步 ──
|
||||
|
||||
|
||||
@@ -28,3 +28,7 @@ export const LIKE_IP_WINDOW_DELTA = 2000;
|
||||
export const LIKE_BURST_MERGE_MS = 500;
|
||||
/** 浏览上报 requestId 保留分钟:过期 requestId 拒绝重放(≥ 去重窗口)。 */
|
||||
export const VISIT_REQUEST_TTL_MINUTES = 35;
|
||||
/** 浏览上报基础限速:visitorId+IP 固定窗(=去重窗口)内请求数上限,超限 429。 */
|
||||
export const VISIT_WINDOW_REQUESTS = 60;
|
||||
/** ov 互动流水清理调度间隔分钟(like_batches/visit_requests/visit_sessions)。 */
|
||||
export const CLEANUP_INTERVAL_MINUTES = 60;
|
||||
|
||||
+8
-5
@@ -554,12 +554,14 @@ export interface paths {
|
||||
* 加载成功后上报一次(列表/图片/WS 等接口调用一律不触发计数)。
|
||||
*
|
||||
* - requestId 为上报幂等键(建议 UUID),counted 确认前跨刷新/重试持久复用;
|
||||
* 重复 requestId 返回 counted=false 与权威 viewCount,不重复计数。
|
||||
* 重复 requestId 幂等重放:counted 回显该 requestId 首次处理的结局
|
||||
* (确认响应丢失后重试仍见 true),viewCount 恒为当前权威值,不重复计数。
|
||||
* - 去重窗口(VISIT_WINDOW_MINUTES,默认 30 分钟,D07)内同一 visitorId
|
||||
* 不重复计数;窗口过后再上报按新访次计数。
|
||||
* - 过期 requestId(VISIT_REQUEST_TTL_MINUTES,默认 35 分钟)拒绝重放
|
||||
* (counted=false),不得重新加计。
|
||||
* - 基础限速:visitorId 滑窗,超限 429(RATE_LIMITED);去重本身为主缓解。
|
||||
* (counted=false,无论原结局),不得重新加计。
|
||||
* - 基础限速:visitorId+来源 IP 固定窗(VISIT_WINDOW_REQUESTS,默认
|
||||
* 60/窗),超限 429(RATE_LIMITED);去重本身为主缓解。
|
||||
* - 相册非公开/紧急停用 404(ALBUM_NOT_PUBLIC)。
|
||||
*/
|
||||
post: operations["reportAlbumVisit"];
|
||||
@@ -1334,8 +1336,9 @@ export interface components {
|
||||
*/
|
||||
readonly viewCount: number;
|
||||
/**
|
||||
* @description 本次是否实际计数:true=新访次;false=去重窗口内重复、重复
|
||||
* requestId 或过期 requestId 重放(均不重复计数)。
|
||||
* @description 本次是否计入:true=新访次,或幂等重放已确认计入的 requestId
|
||||
* (回显原结局);false=去重窗口内重复或过期 requestId 重放
|
||||
* (均不重复计数)。
|
||||
*/
|
||||
readonly counted: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user