import { Injectable } from '@nestjs/common'; import { CacheService } from 'src/modules/utils/cache.service'; import { roundQuantity } from 'src/modules/products/utils/quantity.utils'; import { Cart } from '../interfaces/cart.interface'; @Injectable() export class CartRepository { private readonly CART_TTL = 3600; // 1 hour in seconds private readonly CART_KEY_PREFIX = 'cart'; constructor(private readonly cacheService: CacheService) { } /** * Get cart by user and shop */ async findByUserAndShop(userId: string, shopId: string): Promise { const cacheKey = this.getCacheKey(userId, shopId); const cachedCart = await this.cacheService.get(cacheKey); if (cachedCart) { try { const parsed: unknown = JSON.parse(cachedCart); if (this.isCart(parsed) && parsed.userId === userId && parsed.shopId === shopId) { return this.normalizeCartQuantities(parsed); } } catch { // If parsing fails, return null } } return null; } /** * Save cart to cache */ async save(cart: Cart): Promise { const cacheKey = this.getCacheKey(cart.userId, cart.shopId); await this.cacheService.set(cacheKey, JSON.stringify(cart), this.CART_TTL); } /** * Delete cart from cache */ async delete(userId: string, shopId: string): Promise { const cacheKey = this.getCacheKey(userId, shopId); await this.cacheService.del(cacheKey); } /** * Generate cache key for cart */ private getCacheKey(userId: string, shopId: string): string { return `${this.CART_KEY_PREFIX}:${userId}:${shopId}`; } /** * Coerce cached item quantities to numbers (guards against string concat bugs). */ private normalizeCartQuantities(cart: Cart): Cart { for (const item of cart.items) { item.quantity = roundQuantity(item.quantity); } cart.totalItems = roundQuantity(cart.totalItems); return cart; } /** * Type guard to check if an object is a Cart */ private isCart(obj: unknown): obj is Cart { if (typeof obj !== 'object' || obj === null) { return false; } const cart = obj as Record; return ( typeof cart.userId === 'string' && typeof cart.shopId === 'string' && Array.isArray(cart.items) && typeof cart.subTotal === 'number' && typeof cart.itemsDiscount === 'number' && typeof cart.couponDiscount === 'number' && typeof cart.totalDiscount === 'number' && typeof cart.tax === 'number' && typeof cart.deliveryFee === 'number' && typeof cart.total === 'number' && typeof cart.totalItems === 'number' && typeof cart.createdAt === 'string' && typeof cart.updatedAt === 'string' ); } }