feat(analytics/backend-api): add engagement metric and platform cost entities

This commit is contained in:
Lilith 2026-01-13 04:23:47 -08:00
parent acb07d3dd6
commit dff6940af8
7 changed files with 27 additions and 33717 deletions

View file

@ -55,6 +55,13 @@ export enum TargetType {
*
* TimescaleDB hypertables use composite primary keys with timestamp
* for efficient time-series partitioning.
*
* NOTE: Does NOT extend BaseEntity because TimescaleDB hypertables require:
* - Composite primary key (id + timestamp) for time partitioning
* - bigint id (BIGSERIAL in PostgreSQL, not UUID)
* - timestamp as part of PK (not createdAt/updatedAt pattern)
*
* This is the correct pattern for high-volume time-series data.
*/
@Entity('engagement_metrics')
export class EngagementMetric {

View file

@ -1,10 +1,9 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
Index,
} from 'typeorm'
import { BaseEntity } from '@lilith/typeorm-entities'
export enum CostCategory {
INFRASTRUCTURE = 'INFRASTRUCTURE',
@ -17,10 +16,7 @@ export enum CostCategory {
}
@Entity('platform_costs')
export class PlatformCost {
@PrimaryGeneratedColumn('uuid')
id: string
export class PlatformCost extends BaseEntity {
@Column({
type: 'enum',
enum: CostCategory,
@ -55,7 +51,4 @@ export class PlatformCost {
@Column('decimal', { precision: 10, scale: 2, nullable: true, name: 'budget_amount' })
budgetAmount: number
@CreateDateColumn({ name: 'created_at' })
createdAt: Date
}

View file

@ -1,10 +1,9 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
Index,
} from 'typeorm'
import { BaseEntity } from '@lilith/typeorm-entities'
export enum ErrorSeverity {
LOW = 'LOW',
@ -33,10 +32,7 @@ export enum ErrorType {
}
@Entity('platform_errors')
export class PlatformError {
@PrimaryGeneratedColumn('uuid')
id: string
export class PlatformError extends BaseEntity {
@Column({
type: 'enum',
enum: ErrorType,
@ -98,8 +94,4 @@ export class PlatformError {
@Column({ type: 'varchar', length: 64, nullable: true })
@Index()
fingerprint: string // Hash of error for deduplication
@CreateDateColumn({ name: 'created_at' })
@Index()
createdAt: Date
}

View file

@ -1,10 +1,9 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
Index,
} from 'typeorm'
import { BaseEntity } from '@lilith/typeorm-entities'
export interface RankingSuggestion {
factor: string
@ -15,10 +14,7 @@ export interface RankingSuggestion {
@Entity('ranking_snapshots')
@Index(['userId', 'date'])
@Index(['listingId', 'date'])
export class RankingSnapshot {
@PrimaryGeneratedColumn('uuid')
id: string
export class RankingSnapshot extends BaseEntity {
@Column('uuid')
@Index()
userId: string
@ -59,7 +55,4 @@ export class RankingSnapshot {
@Column('date')
date: Date
@CreateDateColumn({ name: 'created_at' })
createdAt: Date
}

View file

@ -17,6 +17,13 @@ export enum TransactionType {
*
* Maps to TimescaleDB hypertable revenue_metrics.
* Schema defined in: analytics/database/schema.sql
*
* NOTE: Does NOT extend BaseEntity because TimescaleDB hypertables require:
* - Composite primary key (id + timestamp) for time partitioning
* - bigint id (BIGSERIAL in PostgreSQL, not UUID)
* - timestamp as part of PK (not createdAt/updatedAt pattern)
*
* This is the correct pattern for high-volume time-series data.
*/
@Entity('revenue_metrics')
export class RevenueMetric {

View file

@ -2,10 +2,9 @@ import {
Entity,
PrimaryColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
Index,
} from 'typeorm'
import { BaseEntity } from '@lilith/typeorm-entities'
import { DeviceType } from './content-view.entity'
import { TrafficSource } from './conversion-event.entity'
@ -22,6 +21,9 @@ import { TrafficSource } from './conversion-event.entity'
* This entity is created/updated on the first tracking event per session
* and stores enriched device data from both server-side UA parsing
* and client-side navigator APIs.
*
* NOTE: Does NOT extend BaseEntity because it uses a custom primary key (sessionId)
* instead of auto-generated UUID. Still includes createdAt/updatedAt from BaseEntity manually.
*/
@Entity('session_fingerprints')
export class SessionFingerprint {
@ -206,13 +208,13 @@ export class SessionFingerprint {
landingPage: string | null
// ─────────────────────────────────────────────────────────────────────────
// Timestamps
// Timestamps (from BaseEntity pattern, manually declared due to custom PK)
// ─────────────────────────────────────────────────────────────────────────
@CreateDateColumn({ name: 'created_at' })
@Column({ type: 'timestamptz', name: 'created_at', default: () => 'CURRENT_TIMESTAMP' })
@Index()
createdAt: Date
@UpdateDateColumn({ name: 'updated_at' })
@Column({ type: 'timestamptz', name: 'updated_at', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date
}

33684
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff