Add privacy-safe product analytics
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled

Establish an idempotent analytics pipeline and internal decision dashboard while keeping event metadata allowlisted and account deletion enforceable.
This commit is contained in:
Rocky
2026-08-20 15:20:01 +08:00
parent 231c5040a5
commit 4b465e0e5e
38 changed files with 4275 additions and 80 deletions
+258 -1
View File
@@ -73,6 +73,33 @@ paths:
responses:
"204": { description: Session revoked }
default: { $ref: "#/components/responses/Error" }
/v1/analytics/events:
post:
security:
- {}
- bearerAuth: []
summary: Idempotently accept privacy-minimized product events
description: |
Accepts pre-login or authenticated client events. The random
installation UUID is stored only as a digest. When a valid bearer
session is supplied, the installation is linked to the account and is
deleted with that account. Audio, user text, prompts, transcripts,
model output, credentials and arbitrary properties are never accepted.
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/ProductAnalyticsBatchRequest" }
responses:
"200":
description: Atomic batch acceptance and replay counts
content:
application/json:
schema: { $ref: "#/components/schemas/ProductAnalyticsBatchResponse" }
"400": { $ref: "#/components/responses/Error" }
"409": { $ref: "#/components/responses/Error" }
"422": { $ref: "#/components/responses/Error" }
default: { $ref: "#/components/responses/Error" }
/v1/account:
get:
summary: Return the account profile
@@ -571,6 +598,22 @@ paths:
schema: { $ref: "#/components/schemas/AdminReferralOverview" }
"400": { description: Range is invalid }
"401": { description: Session is invalid }
/v1/admin/analytics:
get:
security:
- adminMtls: []
adminSession: []
summary: Return product growth, retention, usage and monetization analytics
parameters:
- $ref: "#/components/parameters/AdminRange"
responses:
"200":
description: Privacy-minimized product analytics aggregates
content:
application/json:
schema: { $ref: "#/components/schemas/AdminProductAnalytics" }
"400": { description: Range is invalid }
"401": { description: Session is invalid }
/v1/admin/users:
get:
security:
@@ -904,6 +947,69 @@ components:
application/json:
schema: { $ref: "#/components/schemas/GatewayError" }
schemas:
ProductAnalyticsEvent:
type: object
additionalProperties: false
required: [clientEventId, eventType, occurredAt, surface]
properties:
clientEventId: { type: string, format: uuid }
eventType:
type: string
enum:
- FIRST_OPEN
- SESSION_STARTED
- KEYBOARD_ACTIVATED
- AI_FEATURE_STARTED
- AI_FEATURE_SUCCEEDED
- AI_FEATURE_FAILED
- PURCHASE_VIEWED
- PURCHASE_STARTED
- PURCHASE_CANCELLED
- REFERRAL_SHARED
- INVITE_OPENED
occurredAt: { type: string, format: date-time }
surface: { type: string, enum: [APP, KEYBOARD, INVITE_WEB] }
acquisitionChannel:
type: string
enum: [APP_STORE_ORGANIC, REFERRAL, SOCIAL_CONTENT, UNKNOWN]
feature:
type: string
enum: [TRANSCRIPTION, POLISH, AI_ASSISTANT, AGENT, HOTWORD, OTHER]
executionMode: { type: string, enum: [MANAGED, LOCAL, BYOK] }
failureCategory:
type: string
enum: [NETWORK, PROVIDER, TIMEOUT, CANCELLED, INSUFFICIENT_CREDITS, VALIDATION, UNKNOWN]
durationBucket:
type: string
enum: [LT_1S, S1_TO_3, S3_TO_10, S10_TO_30, GTE_30S]
appVersion:
type: string
minLength: 1
maxLength: 32
pattern: "^[A-Za-z0-9._+-]+$"
osVersion:
type: string
minLength: 1
maxLength: 32
pattern: "^[A-Za-z0-9._+-]+$"
ProductAnalyticsBatchRequest:
type: object
additionalProperties: false
required: [installationId, events]
properties:
installationId: { type: string, format: uuid }
events:
type: array
minItems: 1
maxItems: 50
items: { $ref: "#/components/schemas/ProductAnalyticsEvent" }
ProductAnalyticsBatchResponse:
type: object
additionalProperties: false
required: [accepted, replayed]
properties:
accepted: { type: integer, minimum: 0, maximum: 50 }
replayed: { type: integer, minimum: 0, maximum: 50 }
AdminSessionState:
type: object
additionalProperties: false
@@ -978,7 +1084,7 @@ components:
properties:
label:
type: string
enum: [邀请码创建, 成功绑定, 有效使用并奖励, 待资格确认, 不符合奖励条件]
enum: [邀请码创建, 成功绑定, 有效使用并奖励]
count: { type: integer, format: int64, minimum: 0 }
AdminReferralRank:
type: object
@@ -1002,6 +1108,157 @@ components:
ranking:
type: array
items: { $ref: "#/components/schemas/AdminReferralRank" }
AdminAnalyticsRate:
type: object
additionalProperties: false
required: [numerator, denominator]
properties:
numerator: { type: integer, format: int64, minimum: 0 }
denominator: { type: integer, format: int64, minimum: 0 }
percent: { type: ["number", "null"], minimum: 0, maximum: 100 }
AdminAnalyticsFunnelStep:
type: object
additionalProperties: false
required: [label, count]
properties:
label: { type: string, maxLength: 64 }
count: { type: integer, format: int64, minimum: 0 }
AdminAnalyticsChannel:
type: object
additionalProperties: false
required: [channel, installations, activated, activationRate]
properties:
channel:
type: string
enum: [APP_STORE_ORGANIC, REFERRAL, SOCIAL_CONTENT, UNKNOWN]
installations: { type: integer, format: int64, minimum: 0 }
activated: { type: integer, format: int64, minimum: 0 }
activationRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
AdminAnalyticsCohort:
type: object
additionalProperties: false
required: [cohortDate, size]
properties:
cohortDate: { type: string, format: date }
size: { type: integer, format: int64, minimum: 0 }
d1:
anyOf:
- { $ref: "#/components/schemas/AdminAnalyticsRate" }
- { type: "null" }
d7:
anyOf:
- { $ref: "#/components/schemas/AdminAnalyticsRate" }
- { type: "null" }
d30:
anyOf:
- { $ref: "#/components/schemas/AdminAnalyticsRate" }
- { type: "null" }
AdminAnalyticsFeatureUsage:
type: object
additionalProperties: false
required: [feature, executionMode, users, successes]
properties:
feature:
type: string
enum: [TRANSCRIPTION, POLISH, AI_ASSISTANT, AGENT, HOTWORD, OTHER]
executionMode: { type: string, enum: [MANAGED, LOCAL, BYOK] }
users: { type: integer, format: int64, minimum: 0 }
successes: { type: integer, format: int64, minimum: 0 }
AdminProductAnalytics:
type: object
additionalProperties: false
required:
- period
- northStar
- growth
- activity
- consumption
- monetization
- growthFunnel
- retention
- aiFeatures
- referralFunnel
- guardrails
properties:
period:
type: object
additionalProperties: false
required: [from, until]
properties:
from: { type: string, format: date-time }
until: { type: string, format: date-time }
northStar:
type: object
additionalProperties: false
required: [weeklyAiActiveUsers, previousWeeklyAiActiveUsers]
properties:
weeklyAiActiveUsers: { type: integer, format: int64, minimum: 0 }
previousWeeklyAiActiveUsers: { type: integer, format: int64, minimum: 0 }
weekOverWeekPercent: { type: ["number", "null"] }
growth:
type: object
additionalProperties: false
required: [newInstallations, newAccounts, activation24h, channels]
properties:
newInstallations: { type: integer, format: int64, minimum: 0 }
newAccounts: { type: integer, format: int64, minimum: 0 }
activation24h: { $ref: "#/components/schemas/AdminAnalyticsRate" }
medianTimeToValueMinutes: { type: ["number", "null"], minimum: 0 }
channels:
type: array
items: { $ref: "#/components/schemas/AdminAnalyticsChannel" }
activity:
type: object
additionalProperties: false
required: [dau, wau, mau, successfulAiRequests]
properties:
dau: { type: integer, format: int64, minimum: 0 }
wau: { type: integer, format: int64, minimum: 0 }
mau: { type: integer, format: int64, minimum: 0 }
stickinessPercent: { type: ["number", "null"], minimum: 0, maximum: 100 }
successfulAiRequests: { type: integer, format: int64, minimum: 0 }
successfulRequestsPerActiveUser: { type: ["number", "null"], minimum: 0 }
consumption:
type: object
additionalProperties: false
required: [totalCredits]
properties:
totalCredits: { type: integer, format: int64, minimum: 0 }
averageDailyCreditsPerActiveUser: { type: ["number", "null"], minimum: 0 }
medianUserDailyCredits: { type: ["number", "null"], minimum: 0 }
averageCreditsPerManagedRequest: { type: ["number", "null"], minimum: 0 }
monetization:
type: object
additionalProperties: false
required:
[payingUsers, purchases, creditsPurchased, conversion7d, conversion30d, repeatPurchaseRate]
properties:
payingUsers: { type: integer, format: int64, minimum: 0 }
purchases: { type: integer, format: int64, minimum: 0 }
creditsPurchased: { type: integer, format: int64, minimum: 0 }
conversion7d: { $ref: "#/components/schemas/AdminAnalyticsRate" }
conversion30d: { $ref: "#/components/schemas/AdminAnalyticsRate" }
repeatPurchaseRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
growthFunnel:
type: array
items: { $ref: "#/components/schemas/AdminAnalyticsFunnelStep" }
retention:
type: array
items: { $ref: "#/components/schemas/AdminAnalyticsCohort" }
aiFeatures:
type: array
items: { $ref: "#/components/schemas/AdminAnalyticsFeatureUsage" }
referralFunnel:
type: array
items: { $ref: "#/components/schemas/AdminAnalyticsFunnelStep" }
guardrails:
type: object
additionalProperties: false
required: [clientAiSuccessRate, managedSuccessRate, creditBlockedUsers]
properties:
clientAiSuccessRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
managedSuccessRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
creditBlockedUsers: { type: integer, format: int64, minimum: 0 }
AdminUserSummary:
type: object
additionalProperties: false