HotTRDealsBackend/db/dealAnalytics.db.js
2026-02-07 22:42:02 +00:00

135 lines
3.8 KiB
JavaScript

const prisma = require("./client")
function normalizeIds(ids = []) {
return Array.from(
new Set(
(Array.isArray(ids) ? ids : [])
.map((id) => Number(id))
.filter((id) => Number.isInteger(id) && id > 0)
)
)
}
async function ensureTotalsForDealIds(dealIds = []) {
const ids = normalizeIds(dealIds)
if (!ids.length) return 0
const existing = await prisma.deal.findMany({
where: { id: { in: ids } },
select: { id: true },
})
const existingIds = new Set(existing.map((d) => d.id))
if (!existingIds.size) return 0
const data = ids.filter((id) => existingIds.has(id)).map((dealId) => ({ dealId }))
const result = await prisma.dealAnalyticsTotal.createMany({
data,
skipDuplicates: true,
})
return result?.count ?? 0
}
async function getTotalsByDealIds(dealIds = []) {
const ids = normalizeIds(dealIds)
if (!ids.length) return []
return prisma.dealAnalyticsTotal.findMany({
where: { dealId: { in: ids } },
select: {
dealId: true,
impressions: true,
views: true,
clicks: true,
},
})
}
function aggregateEventIncrements(events = []) {
const byDeal = new Map()
for (const event of events) {
const dealId = Number(event.dealId)
if (!Number.isInteger(dealId) || dealId <= 0) continue
const type = String(event.type || "").toUpperCase()
const entry = byDeal.get(dealId) || { dealId, impressions: 0, views: 0, clicks: 0 }
if (type === "IMPRESSION") entry.impressions += 1
else if (type === "VIEW") entry.views += 1
else if (type === "CLICK") entry.clicks += 1
byDeal.set(dealId, entry)
}
return Array.from(byDeal.values())
}
async function applyDealEventBatch(events = []) {
const filtered = (Array.isArray(events) ? events : []).filter(
(e) => e && e.dealId && (e.userId || e.ip)
)
if (!filtered.length) return { inserted: 0, increments: [] }
const data = filtered.map((event) => ({
dealId: Number(event.dealId),
type: String(event.type || "IMPRESSION").toUpperCase(),
userId: event.userId ? Number(event.userId) : null,
ip: event.ip ? String(event.ip) : null,
createdAt: event.createdAt ? new Date(event.createdAt) : new Date(),
}))
const increments = aggregateEventIncrements(data)
await prisma.$transaction(async (tx) => {
await tx.dealEvent.createMany({ data })
for (const inc of increments) {
await tx.dealAnalyticsTotal.upsert({
where: { dealId: inc.dealId },
create: {
dealId: inc.dealId,
impressions: inc.impressions,
views: inc.views,
clicks: inc.clicks,
},
update: {
impressions: { increment: inc.impressions },
views: { increment: inc.views },
clicks: { increment: inc.clicks },
},
})
}
})
return { inserted: data.length, increments }
}
async function applyDealTotalsBatch(increments = []) {
const data = (Array.isArray(increments) ? increments : []).filter(
(item) => item && Number.isInteger(Number(item.dealId))
)
if (!data.length) return { updated: 0 }
await prisma.$transaction(async (tx) => {
for (const inc of data) {
const dealId = Number(inc.dealId)
if (!Number.isInteger(dealId) || dealId <= 0) continue
await tx.dealAnalyticsTotal.upsert({
where: { dealId },
create: {
dealId,
impressions: Number(inc.impressions || 0),
views: Number(inc.views || 0),
clicks: Number(inc.clicks || 0),
},
update: {
impressions: { increment: Number(inc.impressions || 0) },
views: { increment: Number(inc.views || 0) },
clicks: { increment: Number(inc.clicks || 0) },
},
})
}
})
return { updated: data.length }
}
module.exports = {
ensureTotalsForDealIds,
getTotalsByDealIds,
applyDealEventBatch,
applyDealTotalsBatch,
}