diff --git a/backend/db/schema/dailyAnalytics.schema.ts b/backend/db/schema/dailyAnalytics.schema.ts index e1eaa98..49b97c2 100644 --- a/backend/db/schema/dailyAnalytics.schema.ts +++ b/backend/db/schema/dailyAnalytics.schema.ts @@ -4,30 +4,42 @@ import { pgTable, text, timestamp, + unique, uuid, } from "drizzle-orm/pg-core"; -export const analyticsDaily = pgTable("analytics_daily", { - id: uuid("id").defaultRandom().primaryKey(), +export const analyticsDaily = pgTable( + "analytics_daily", + { + id: uuid("id").defaultRandom().primaryKey(), - businessDate: date("business_date").notNull(), + businessDate: date("business_date", { mode: "string" }).notNull(), - method: text("method").notNull(), - routePattern: text("route_pattern").notNull(), - module: text("module").notNull(), + method: text("method").notNull(), + routePattern: text("route_pattern").notNull(), + module: text("module").notNull(), - totalHits: integer("total_hits").notNull(), - uniqueUsers: integer("unique_users").notNull(), + totalHits: integer("total_hits").notNull(), + uniqueUsers: integer("unique_users").notNull(), - successCount: integer("success_count").notNull(), - errorCount: integer("error_count").notNull(), + successCount: integer("success_count").notNull(), + errorCount: integer("error_count").notNull(), - avgDurationMs: integer("avg_duration_ms").notNull(), - maxDurationMs: integer("max_duration_ms").notNull(), + avgDurationMs: integer("avg_duration_ms").notNull(), + maxDurationMs: integer("max_duration_ms").notNull(), - firstHitAt: timestamp("first_hit_at").notNull(), - lastHitAt: timestamp("last_hit_at").notNull(), + firstHitAt: timestamp("first_hit_at").notNull(), + lastHitAt: timestamp("last_hit_at").notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().notNull(), -}); + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), + }, + (table) => [ + unique("analytics_daily_business_route_unique").on( + table.businessDate, + table.method, + table.routePattern, + table.module, + ), + ], +); diff --git a/backend/utils/analyticRouteHits.utils.ts b/backend/utils/analyticRouteHits.utils.ts index 1aa1c1a..360680c 100644 --- a/backend/utils/analyticRouteHits.utils.ts +++ b/backend/utils/analyticRouteHits.utils.ts @@ -61,7 +61,7 @@ export async function aggregateRouteHitsForBusinessDay() { const rows = await db .select({ - businessDate: sql`${businessDate}`, + businessDate: sql`CAST(${businessDate} AS date)`, method: analytics.method, routePattern: analytics.routePattern, module: sql`COALESCE(${analytics.module}, 'unknown')`, @@ -105,9 +105,16 @@ export async function aggregateRouteHitsForBusinessDay() { }; } + const values = rows.map((row) => ({ + ...row, + businessDate: row.businessDate, + firstHitAt: new Date(row.firstHitAt), + lastHitAt: new Date(row.lastHitAt), + })); + await db .insert(analyticsDaily) - .values(rows) + .values(values) .onConflictDoUpdate({ target: [ analyticsDaily.businessDate,