rename to core
This commit is contained in:
131
core/analytics/domain/entities/PageView.ts
Normal file
131
core/analytics/domain/entities/PageView.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Domain Entity: PageView
|
||||
*
|
||||
* Represents a single page view event for analytics tracking.
|
||||
* Captures visitor interactions with leagues, drivers, teams, races.
|
||||
*/
|
||||
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
import type { EntityType, VisitorType, PageViewProps } from '../types/PageView';
|
||||
|
||||
export type { EntityType, VisitorType } from '../types/PageView';
|
||||
import { AnalyticsEntityId } from '../value-objects/AnalyticsEntityId';
|
||||
import { AnalyticsSessionId } from '../value-objects/AnalyticsSessionId';
|
||||
import { PageViewId } from '../value-objects/PageViewId';
|
||||
|
||||
export class PageView implements IEntity<string> {
|
||||
readonly entityType: EntityType;
|
||||
readonly visitorId: string | undefined;
|
||||
readonly visitorType: VisitorType;
|
||||
readonly referrer: string | undefined;
|
||||
readonly userAgent: string | undefined;
|
||||
readonly country: string | undefined;
|
||||
readonly timestamp: Date;
|
||||
readonly durationMs: number | undefined;
|
||||
|
||||
private readonly idVo: PageViewId;
|
||||
private readonly entityIdVo: AnalyticsEntityId;
|
||||
private readonly sessionIdVo: AnalyticsSessionId;
|
||||
|
||||
private constructor(props: PageViewProps) {
|
||||
this.idVo = PageViewId.create(props.id);
|
||||
this.entityType = props.entityType;
|
||||
this.entityIdVo = AnalyticsEntityId.create(props.entityId);
|
||||
this.visitorId = props.visitorId;
|
||||
this.visitorType = props.visitorType;
|
||||
this.sessionIdVo = AnalyticsSessionId.create(props.sessionId);
|
||||
this.referrer = props.referrer;
|
||||
this.userAgent = props.userAgent;
|
||||
this.country = props.country;
|
||||
this.timestamp = props.timestamp;
|
||||
this.durationMs = props.durationMs;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.idVo.value;
|
||||
}
|
||||
|
||||
get entityId(): string {
|
||||
return this.entityIdVo.value;
|
||||
}
|
||||
|
||||
get sessionId(): string {
|
||||
return this.sessionIdVo.value;
|
||||
}
|
||||
|
||||
static create(props: Omit<PageViewProps, 'timestamp'> & { timestamp?: Date }): PageView {
|
||||
this.validate(props);
|
||||
|
||||
const baseProps: PageViewProps = {
|
||||
id: props.id,
|
||||
entityType: props.entityType,
|
||||
entityId: props.entityId,
|
||||
visitorType: props.visitorType,
|
||||
sessionId: props.sessionId,
|
||||
timestamp: props.timestamp ?? new Date(),
|
||||
...(props.visitorId !== undefined ? { visitorId: props.visitorId } : {}),
|
||||
...(props.referrer !== undefined ? { referrer: props.referrer } : {}),
|
||||
...(props.userAgent !== undefined ? { userAgent: props.userAgent } : {}),
|
||||
...(props.country !== undefined ? { country: props.country } : {}),
|
||||
...(props.durationMs !== undefined ? { durationMs: props.durationMs } : {}),
|
||||
};
|
||||
|
||||
return new PageView(baseProps);
|
||||
}
|
||||
|
||||
private static validate(props: Omit<PageViewProps, 'timestamp'>): void {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new Error('PageView ID is required');
|
||||
}
|
||||
|
||||
if (!props.entityType) {
|
||||
throw new Error('PageView entityType is required');
|
||||
}
|
||||
|
||||
if (!props.entityId || props.entityId.trim().length === 0) {
|
||||
throw new Error('PageView entityId is required');
|
||||
}
|
||||
|
||||
if (!props.sessionId || props.sessionId.trim().length === 0) {
|
||||
throw new Error('PageView sessionId is required');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update duration when visitor leaves page
|
||||
*/
|
||||
withDuration(durationMs: number): PageView {
|
||||
if (durationMs < 0) {
|
||||
throw new Error('Duration must be non-negative');
|
||||
}
|
||||
|
||||
return PageView.create({
|
||||
id: this.id,
|
||||
entityType: this.entityType,
|
||||
entityId: this.entityId,
|
||||
visitorType: this.visitorType,
|
||||
sessionId: this.sessionId,
|
||||
timestamp: this.timestamp,
|
||||
...(this.visitorId !== undefined ? { visitorId: this.visitorId } : {}),
|
||||
...(this.referrer !== undefined ? { referrer: this.referrer } : {}),
|
||||
...(this.userAgent !== undefined ? { userAgent: this.userAgent } : {}),
|
||||
...(this.country !== undefined ? { country: this.country } : {}),
|
||||
durationMs,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a meaningful view (not a bounce)
|
||||
*/
|
||||
isMeaningfulView(): boolean {
|
||||
return this.durationMs !== undefined && this.durationMs >= 5000; // 5+ seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if view came from external source
|
||||
*/
|
||||
isExternalReferral(): boolean {
|
||||
if (!this.referrer) return false;
|
||||
return !this.referrer.includes('gridpilot');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user