1f2a0c658f
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>
39 lines
1.4 KiB
TypeScript
39 lines
1.4 KiB
TypeScript
// services/api/src/interactions/dto.ts —— 互动上报请求体(P2 Task 7 点赞批次 /
|
||
// Task 8 浏览上报)。校验边界与契约 LikeBatchRequest / VisitReportRequest
|
||
// 逐字段对齐(sessionId/batchId 1..64、delta 1..20 整数;visitorId/requestId
|
||
// 8..128);越界一律 400 VALIDATION_FAILED(全局 ValidationPipe whitelist
|
||
// 剥离未声明字段)。delta 必须是 JSON number 整数(管道未开 transform,
|
||
// 字符串数字不静默转换——公开匿名端点从严)。
|
||
import { IsInt, IsString, Length, Max, Min } from 'class-validator';
|
||
|
||
export class LikeBatchDto {
|
||
/** 会话标识(客户端随机生成、会话期间复用;限速滑窗的键之一)。 */
|
||
@IsString()
|
||
@Length(1, 64)
|
||
sessionId!: string;
|
||
|
||
/** 批次幂等键:同批失败重试必须复用,跨批次不得复用。 */
|
||
@IsString()
|
||
@Length(1, 64)
|
||
batchId!: string;
|
||
|
||
/** 本批新增点赞数(客户端短窗合并后的累计点击数)。 */
|
||
@IsInt()
|
||
@Min(1)
|
||
@Max(20)
|
||
delta!: number;
|
||
}
|
||
|
||
/** 浏览上报请求体(P2 Task 8;契约 VisitReportRequest)。 */
|
||
export class VisitReportDto {
|
||
/** 访客标识(客户端持久化并在标签页间共享;去重窗口与限速的键)。 */
|
||
@IsString()
|
||
@Length(8, 128)
|
||
visitorId!: string;
|
||
|
||
/** 上报幂等键:counted 确认前跨刷新/重试持久复用(建议 UUID)。 */
|
||
@IsString()
|
||
@Length(8, 128)
|
||
requestId!: string;
|
||
}
|