API-INFO-010 P0 | 事件校验、去重、activeSecondsDelta 处理 【status:todo】 #110

Closed
opened 2026-06-07 11:03:39 +08:00 by wangdl · 2 comments
Owner

目标

统一事件校验和幂等处理。

规则

  • activeSecondsDelta=0 合法,<0 failed,>300 cap 为 300 + warning
  • userId+eventId 唯一去重
  • 非法 position 事件仍保存,但不更新 progress + POSITION_IGNORED warning
  • 重复事件不写库不聚合

详见设计文档 API-INFO-000。

## 目标 统一事件校验和幂等处理。 ### 规则 - activeSecondsDelta=0 合法,<0 failed,>300 cap 为 300 + warning - userId+eventId 唯一去重 - 非法 position 事件仍保存,但不更新 progress + POSITION_IGNORED warning - 重复事件不写库不聚合 详见设计文档 API-INFO-000。
wangdl added this to the M8:学习信息收集与基础分析闭环 milestone 2026-06-07 11:03:39 +08:00
wangdl changed title from API-INFO-005 P0 | 实现 ReadingEvent 去重与校验 to API-INFO-010 P0 | 实现事件校验、去重、activeSecondsDelta 处理 2026-06-07 11:22:16 +08:00
wangdl changed title from API-INFO-010 P0 | 实现事件校验、去重、activeSecondsDelta 处理 to API-INFO-010 P0 | 事件校验、去重、activeSecondsDelta 处理 【status:todo】 2026-06-07 19:04:12 +08:00
Author
Owner

审查结论:当前 API 项目学习信息收集体系基本为全新建设。可复用:JWT Guard、LearningSession 基础表/CRUD、DailyLearningActivity 基础表、ActivityController 部分接口、LearningRecord schema。其余 ReadingEvent/TemporaryMaterial/Progress/批量上报/Processor/聚合/查询接口/错误码/去重/权限/测试/文档均不存在或仅部分存在。

本 Issue: 代码不存在。activeSecondsDelta=0合法、>300截断、<0拒绝。

标签: audit:reviewed audit:api-info status:todo work:api

## 审查结论:当前 API 项目学习信息收集体系基本为全新建设。可复用:JWT Guard、LearningSession 基础表/CRUD、DailyLearningActivity 基础表、ActivityController 部分接口、LearningRecord schema。其余 ReadingEvent/TemporaryMaterial/Progress/批量上报/Processor/聚合/查询接口/错误码/去重/权限/测试/文档均不存在或仅部分存在。 **本 Issue**: 代码不存在。activeSecondsDelta=0合法、>300截断、<0拒绝。 **标签**: audit:reviewed audit:api-info status:todo work:api
Author
Owner

完成报告

交付

reading-event.service.tsprocessBatch() 完整 7 步校验管线:

1. Required fields — eventId / clientSessionId / materialId 缺失 → failed
2. Type validation — readingTargetType 必须为 knowledge_source | temporary_file,eventType 必须为 5 种之一
3. Delta validation — =0 / <0 INVALID_ACTIVE_SECONDS / >300 截断 + ACTIVE_SECONDS_CAPPED warning
4. Timestamp validation — 无效/负数 INVALID_TIMESTAMP / >5min 未来 ⚠️ CLIENT_TIMESTAMP_SKEWED
5. Dedup check — userId+eventId 查重,已存在 → DUPLICATE_EVENT + 跳过写入 + 跳过聚合
6. Position validation — 检查 type 字段属于 6 种 ReadingPosition variant,无效 → POSITION_IGNORED + 不更新 progress
7. Persist — create(非 upsert!先查重再写入),status=processed

代码证据

// Delta 处理
let delta = Number(e.activeSecondsDelta ?? 0);
if (isNaN(delta) || delta < 0)  failed, INVALID_ACTIVE_SECONDS
if (delta > 300) { delta = 300; deltaCapped = true; }  warning

// 去重(查重 + 跳过聚合,不写库)
const existing = await prisma.readingEvent.findUnique({ userId_eventId });
if (existing) { result.duplicate++; continue; }

// 非法 position 仍保存事件,但不更新 progress
if (!positionValid) warnings.push(POSITION_IGNORED);
## 完成报告 ### 交付 `reading-event.service.ts` — `processBatch()` 完整 7 步校验管线: **1. Required fields** — eventId / clientSessionId / materialId 缺失 → failed **2. Type validation** — readingTargetType 必须为 knowledge_source | temporary_file,eventType 必须为 5 种之一 **3. Delta validation** — =0 ✅ / <0 ❌ INVALID_ACTIVE_SECONDS / >300 ✅ 截断 + ACTIVE_SECONDS_CAPPED warning **4. Timestamp validation** — 无效/负数 ❌ INVALID_TIMESTAMP / >5min 未来 ⚠️ CLIENT_TIMESTAMP_SKEWED **5. Dedup check** — userId+eventId 查重,已存在 → DUPLICATE_EVENT + 跳过写入 + 跳过聚合 **6. Position validation** — 检查 type 字段属于 6 种 ReadingPosition variant,无效 → POSITION_IGNORED + 不更新 progress **7. Persist** — create(非 upsert!先查重再写入),status=processed ### 代码证据 ```typescript // Delta 处理 let delta = Number(e.activeSecondsDelta ?? 0); if (isNaN(delta) || delta < 0) → failed, INVALID_ACTIVE_SECONDS if (delta > 300) { delta = 300; deltaCapped = true; } → warning // 去重(查重 + 跳过聚合,不写库) const existing = await prisma.readingEvent.findUnique({ userId_eventId }); if (existing) { result.duplicate++; continue; } // 非法 position 仍保存事件,但不更新 progress if (!positionValid) warnings.push(POSITION_IGNORED); ```
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangdl/api-server#110
No description provided.