一、同意与退出type Consent = { optIn: boolean; categories: string[] }
function canCollect(c: Consent, cat: string): boolean { return c.optIn && c.categories.includes(cat) }
二、采样与速率function sample(p: number): boolean { return Math.random() < p }
class RateGate { windowMs: number; max: number; hits = new Map<string, number[]>(); constructor(windowMs: number, max: number) { this.windowMs = windowMs; this.max = max } allow(key: string): boolean { const now = Date.now(); const arr = (this.hits.get(key) || []).filter(t => now - t < this.windowMs); if (arr.length >= this.max) return false; arr.push(now); this.hits.set(key, arr); return true } }
三、事件Schema与去标识type Event = { id: string; category: string; action: string; label?: string; value?: number; userId?: string; email?: string; ts: string }
function nowIso(): string { return new Date().toISOString() }
function hash(input: string, salt: string): string { const data = new TextEncoder().encode(input + salt); let h = 0; for (const b of data) h = (h * 31 + b) >>> 0; return h.toString(16) }
function redactPII(e: Event, salt: string): Event {
const out: Event = { ...e }
if (out.email) { const at = out.email.indexOf('@'); out.email = at > 0 ? '***' + out.email.slice(at) : '***' }
if (out.userId) out.userId = hash(out.userId, salt)
return out
}
四、保留期与合规type Store = { push: (e: Event) => void; expireDays: number }
function withinRetention(createdAt: Date, days: number): boolean { const diff = (Date.now() - createdAt.getTime()) / (1000 * 60 * 60 * 24); return diff <= days }
五、采集中间件type Ctx = { consent: Consent; salt: string; rate: RateGate }
function collect(ctx: Ctx, e: Event, store: Store, p: number): boolean {
if (!canCollect(ctx.consent, e.category)) return false
if (!ctx.rate.allow(e.category)) return false
if (!sample(p)) return false
const red = redactPII({ ...e, ts: nowIso() }, ctx.salt)
store.push(red)
return true
}

发表评论 取消回复