freshyo/apps/backend/src/stores/slot-store.ts
2026-05-11 20:57:13 +05:30

338 lines
9.7 KiB
TypeScript

// import redisClient from '@/src/lib/redis-client'
import {
getAllSlotsWithProductsForCache,
type SlotWithProductsData,
} from '@/src/dbService'
import { scaffoldAssetUrl } from '@/src/lib/s3-client'
import dayjs from 'dayjs'
// Define the structure for slot with products
interface SlotWithProducts {
id: number
deliveryTime: Date
freezeTime: Date
isActive: boolean
isCapacityFull: boolean
products: Array<{
id: number
name: string
shortDescription: string | null
productQuantity: number
price: string
marketPrice: string | null
unit: string | null
images: string[]
isOutOfStock: boolean
storeId: number | null
nextDeliveryDate: Date
}>
}
interface SlotInfo {
id: number
deliveryTime: Date
freezeTime: Date
isCapacityFull: boolean
}
async function transformSlotToStoreSlot(slot: SlotWithProductsData): Promise<SlotWithProducts> {
return {
id: slot.id,
deliveryTime: slot.deliveryTime,
freezeTime: slot.freezeTime,
isActive: slot.isActive,
isCapacityFull: slot.isCapacityFull,
products: slot.products.map((product) => ({
id: product.id,
name: product.name,
productQuantity: product.productQuantity,
shortDescription: product.shortDescription,
price: product.price.toString(),
marketPrice: product.marketPrice?.toString() || null,
unit: product.unit?.shortNotation || null,
images: scaffoldAssetUrl(
(product.images as string[]) || []
),
isOutOfStock: product.isOutOfStock,
storeId: product.storeId,
nextDeliveryDate: slot.deliveryTime,
})),
}
}
function extractSlotInfo(slot: SlotWithProductsData): SlotInfo {
return {
id: slot.id,
deliveryTime: slot.deliveryTime,
freezeTime: slot.freezeTime,
isCapacityFull: slot.isCapacityFull,
}
}
async function fetchAllTransformedSlots(): Promise<SlotWithProducts[]> {
const slots = await getAllSlotsWithProductsForCache()
return Promise.all(slots.map(transformSlotToStoreSlot))
}
export async function initializeSlotStore(): Promise<void> {
try {
console.log('Initializing slot store in Redis...')
// Fetch active delivery slots with future delivery times
const slots = await getAllSlotsWithProductsForCache()
/*
// Old implementation - direct DB queries:
import { db } from '@/src/db/db_index'
import { deliverySlotInfo } from '@/src/db/schema'
import { eq, gt, and, asc } from 'drizzle-orm'
const now = new Date();
const slots = await db.query.deliverySlotInfo.findMany({
where: and(
eq(deliverySlotInfo.isActive, true),
gt(deliverySlotInfo.deliveryTime, now),
),
with: {
productSlots: {
with: {
product: {
with: {
unit: true,
store: true,
},
},
},
},
},
orderBy: asc(deliverySlotInfo.deliveryTime),
});
*/
// Transform data for storage
const slotsWithProducts = await Promise.all(
slots.map(async (slot) => ({
id: slot.id,
deliveryTime: slot.deliveryTime,
freezeTime: slot.freezeTime,
isActive: slot.isActive,
isCapacityFull: slot.isCapacityFull,
products: await Promise.all(
slot.products.map(async (product) => ({
id: product.id,
name: product.name,
productQuantity: product.productQuantity,
shortDescription: product.shortDescription,
price: product.price.toString(),
marketPrice: product.marketPrice?.toString() || null,
unit: product.unit?.shortNotation || null,
images: scaffoldAssetUrl(
(product.images as string[]) || []
),
isOutOfStock: product.isOutOfStock,
storeId: product.storeId,
nextDeliveryDate: slot.deliveryTime,
}))
),
}))
)
// Store each slot in Redis with key pattern "slot:{id}"
// for (const slot of slotsWithProducts) {
// await redisClient.set(`slot:${slot.id}`, JSON.stringify(slot))
// }
// Build and store product-slots map
// Group slots by productId
const productSlotsMap: Record<number, SlotInfo[]> = {}
for (const slot of slotsWithProducts) {
for (const product of slot.products) {
if (!productSlotsMap[product.id]) {
productSlotsMap[product.id] = []
}
productSlotsMap[product.id].push({
id: slot.id,
deliveryTime: slot.deliveryTime,
freezeTime: slot.freezeTime,
isCapacityFull: slot.isCapacityFull,
})
}
}
// Store each product's slots in Redis with key pattern "product:{id}:slots"
// for (const [productId, slotInfos] of Object.entries(productSlotsMap)) {
// await redisClient.set(
// `product:${productId}:slots`,
// JSON.stringify(slotInfos)
// )
// }
console.log('Slot store initialized successfully')
} catch (error) {
console.error('Error initializing slot store:', error)
}
}
export async function getSlotById(slotId: number): Promise<SlotWithProducts | null> {
try {
// const key = `slot:${slotId}`
// const data = await redisClient.get(key)
// if (!data) return null
// return JSON.parse(data) as SlotWithProducts
const slots = await getAllSlotsWithProductsForCache()
const slot = slots.find(s => s.id === slotId)
if (!slot) return null
return transformSlotToStoreSlot(slot)
} catch (error) {
console.error(`Error getting slot ${slotId}:`, error)
return null
}
}
export async function getAllSlots(): Promise<SlotWithProducts[]> {
try {
// Get all keys matching the pattern "slot:*"
// const keys = await redisClient.KEYS('slot:*')
//
// if (keys.length === 0) return []
//
// // Get all slots using MGET for better performance
// const slotsData = await redisClient.MGET(keys)
//
// const slots: SlotWithProducts[] = []
// for (const slotData of slotsData) {
// if (slotData) {
// slots.push(JSON.parse(slotData) as SlotWithProducts)
// }
// }
//
// return slots
return fetchAllTransformedSlots()
} catch (error) {
console.error('Error getting all slots:', error)
return []
}
}
export async function getProductSlots(productId: number): Promise<SlotInfo[]> {
try {
// const key = `product:${productId}:slots`
// const data = await redisClient.get(key)
// if (!data) return []
// return JSON.parse(data) as SlotInfo[]
const slots = await getAllSlotsWithProductsForCache()
const productSlots: SlotInfo[] = []
for (const slot of slots) {
const hasProduct = slot.products.some(p => p.id === productId)
if (hasProduct) {
productSlots.push(extractSlotInfo(slot))
}
}
return productSlots
} catch (error) {
console.error(`Error getting slots for product ${productId}:`, error)
return []
}
}
export async function getAllProductsSlots(): Promise<Record<number, SlotInfo[]>> {
try {
// Get all keys matching the pattern "product:*:slots"
// const keys = await redisClient.KEYS('product:*:slots')
//
// if (keys.length === 0) return {}
//
// // Get all product slots using MGET for better performance
// const productsData = await redisClient.MGET(keys)
//
// const result: Record<number, SlotInfo[]> = {}
// for (const key of keys) {
// // Extract productId from key "product:{id}:slots"
// const match = key.match(/product:(\d+):slots/)
// if (match) {
// const productId = parseInt(match[1], 10)
// const dataIndex = keys.indexOf(key)
// if (productsData[dataIndex]) {
// result[productId] = JSON.parse(productsData[dataIndex]) as SlotInfo[]
// }
// }
// }
//
// return result
const slots = await getAllSlotsWithProductsForCache()
const result: Record<number, SlotInfo[]> = {}
for (const slot of slots) {
const slotInfo = extractSlotInfo(slot)
for (const product of slot.products) {
const productId = product.id
if (!result[productId]) {
result[productId] = []
}
result[productId].push(slotInfo)
}
}
return result
} catch (error) {
console.error('Error getting all products slots:', error)
return {}
}
}
export async function getMultipleProductsSlots(
productIds: number[]
): Promise<Record<number, SlotInfo[]>> {
try {
if (productIds.length === 0) return {}
// Build keys for all productIds
// const keys = productIds.map((id) => `product:${id}:slots`)
//
// // Use MGET for batch retrieval
// const productsData = await redisClient.MGET(keys)
//
// const result: Record<number, SlotInfo[]> = {}
// for (let i = 0; i < productIds.length; i++) {
// const data = productsData[i]
// if (data) {
// const slots = JSON.parse(data) as SlotInfo[]
// // Filter out slots that are at full capacity
// result[productIds[i]] = slots.filter((slot) => !slot.isCapacityFull)
// }
// }
//
// return result
const slots = await getAllSlotsWithProductsForCache()
const productIdSet = new Set(productIds)
const result: Record<number, SlotInfo[]> = {}
for (const productId of productIds) {
result[productId] = []
}
for (const slot of slots) {
const slotInfo = extractSlotInfo(slot)
for (const product of slot.products) {
const pid = product.id
if (productIdSet.has(pid) && !slot.isCapacityFull) {
result[pid].push(slotInfo)
}
}
}
return result
} catch (error) {
console.error('Error getting products slots:', error)
return {}
}
}