This commit is contained in:
shafi54 2026-02-20 11:40:59 +05:30
parent 40a98e38f5
commit d4afa75eaf
5 changed files with 565 additions and 22 deletions

View file

@ -0,0 +1,405 @@
import { db } from '../../db/db_index'
import {
orders,
orderItems,
orderStatus,
addresses,
productInfo,
paymentInfoTable,
coupons,
couponUsage,
payments,
cartItems,
refunds,
units,
userDetails,
} from '../../db/schema'
import { eq, and, inArray, desc, gte } from 'drizzle-orm'
// ============ User/Auth Queries ============
/**
* Get user details by user ID
*/
export async function getUserDetails(userId: number) {
return db.query.userDetails.findFirst({
where: eq(userDetails.userId, userId),
})
}
// ============ Address Queries ============
/**
* Get user address by ID
*/
export async function getUserAddress(userId: number, addressId: number) {
return db.query.addresses.findFirst({
where: and(eq(addresses.userId, userId), eq(addresses.id, addressId)),
})
}
// ============ Product Queries ============
/**
* Get product by ID
*/
export async function getProductById(productId: number) {
return db.query.productInfo.findFirst({
where: eq(productInfo.id, productId),
})
}
/**
* Get multiple products by IDs with unit info
*/
export async function getProductsByIdsWithUnits(productIds: number[]) {
return db
.select({
id: productInfo.id,
name: productInfo.name,
shortDescription: productInfo.shortDescription,
price: productInfo.price,
images: productInfo.images,
isOutOfStock: productInfo.isOutOfStock,
unitShortNotation: units.shortNotation,
incrementStep: productInfo.incrementStep,
})
.from(productInfo)
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(and(inArray(productInfo.id, productIds), eq(productInfo.isSuspended, false)))
.orderBy(desc(productInfo.createdAt))
}
// ============ Coupon Queries ============
/**
* Get coupon with usages for user
*/
export async function getCouponWithUsages(couponId: number, userId: number) {
return db.query.coupons.findFirst({
where: eq(coupons.id, couponId),
with: {
usages: { where: eq(couponUsage.userId, userId) },
},
})
}
/**
* Insert coupon usage
*/
export async function insertCouponUsage(data: {
userId: number
couponId: number
orderId: number
orderItemId: number | null
usedAt: Date
}) {
return db.insert(couponUsage).values(data)
}
/**
* Get coupon usages for order
*/
export async function getCouponUsagesForOrder(orderId: number) {
return db.query.couponUsage.findMany({
where: eq(couponUsage.orderId, orderId),
with: {
coupon: true,
},
})
}
// ============ Cart Queries ============
/**
* Delete cart items for user by product IDs
*/
export async function deleteCartItems(userId: number, productIds: number[]) {
return db.delete(cartItems).where(
and(
eq(cartItems.userId, userId),
inArray(cartItems.productId, productIds)
)
)
}
// ============ Payment Info Queries ============
/**
* Create payment info
*/
export async function createPaymentInfo(data: {
status: string
gateway: string
merchantOrderId: string
}) {
return db.insert(paymentInfoTable).values(data).returning()
}
// ============ Order Queries ============
/**
* Insert multiple orders
*/
export async function insertOrders(ordersData: any[]) {
return db.insert(orders).values(ordersData).returning()
}
/**
* Insert multiple order items
*/
export async function insertOrderItems(itemsData: any[]) {
return db.insert(orderItems).values(itemsData)
}
/**
* Insert multiple order statuses
*/
export async function insertOrderStatuses(statusesData: any[]) {
return db.insert(orderStatus).values(statusesData)
}
/**
* Get user orders with all relations
*/
export async function getUserOrdersWithRelations(userId: number, limit: number, offset: number) {
return db.query.orders.findMany({
where: eq(orders.userId, userId),
with: {
orderItems: {
with: {
product: true,
},
},
slot: true,
paymentInfo: true,
orderStatus: true,
refunds: true,
},
orderBy: (orders, { desc }) => [desc(orders.createdAt)],
limit: limit,
offset: offset,
})
}
/**
* Count user orders
*/
export async function countUserOrders(userId: number) {
return db.$count(orders, eq(orders.userId, userId))
}
/**
* Get order by ID with all relations
*/
export async function getOrderByIdWithRelations(orderId: number) {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
orderItems: {
with: {
product: true,
},
},
slot: true,
paymentInfo: true,
orderStatus: {
with: {
refundCoupon: true,
},
},
refunds: true,
},
})
}
/**
* Get order by ID with order status
*/
export async function getOrderWithStatus(orderId: number) {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
orderStatus: true,
},
})
}
/**
* Update order status to cancelled
*/
export async function updateOrderStatusToCancelled(
statusId: number,
data: {
isCancelled: boolean
cancelReason: string
cancellationUserNotes: string
cancellationReviewed: boolean
}
) {
return db
.update(orderStatus)
.set(data)
.where(eq(orderStatus.id, statusId))
}
/**
* Insert refund record
*/
export async function insertRefund(data: { orderId: number; refundStatus: string }) {
return db.insert(refunds).values(data)
}
/**
* Update order notes
*/
export async function updateOrderNotes(orderId: number, userNotes: string | null) {
return db
.update(orders)
.set({ userNotes })
.where(eq(orders.id, orderId))
}
/**
* Get recent delivered orders for user
*/
export async function getRecentDeliveredOrders(
userId: number,
since: Date,
limit: number
) {
return db
.select({ id: orders.id })
.from(orders)
.innerJoin(orderStatus, eq(orders.id, orderStatus.orderId))
.where(
and(
eq(orders.userId, userId),
eq(orderStatus.isDelivered, true),
gte(orders.createdAt, since)
)
)
.orderBy(desc(orders.createdAt))
.limit(limit)
}
/**
* Get order items by order IDs
*/
export async function getOrderItemsByOrderIds(orderIds: number[]) {
return db
.select({ productId: orderItems.productId })
.from(orderItems)
.where(inArray(orderItems.orderId, orderIds))
}
// ============ Transaction Helper ============
/**
* Execute function within a database transaction
*/
export async function withTransaction<T>(fn: (tx: any) => Promise<T>): Promise<T> {
return db.transaction(fn)
}
/**
* Cancel order with refund record in a transaction
*/
export async function cancelOrderWithRefund(
statusId: number,
orderId: number,
isCod: boolean,
reason: string
): Promise<{ orderId: number }> {
return db.transaction(async (tx) => {
// Update order status
await tx
.update(orderStatus)
.set({
isCancelled: true,
cancelReason: reason,
cancellationUserNotes: reason,
cancellationReviewed: false,
})
.where(eq(orderStatus.id, statusId))
// Insert refund record
const refundStatus = isCod ? "na" : "pending"
await tx.insert(refunds).values({
orderId,
refundStatus,
})
return { orderId }
})
}
type Tx = Parameters<Parameters<typeof db.transaction>[0]>[0]
/**
* Create orders with payment info in a transaction
*/
export async function createOrdersWithPayment(
ordersData: any[],
paymentMethod: "online" | "cod",
totalWithDelivery: number,
razorpayOrderCreator?: (paymentInfoId: number, amount: string) => Promise<any>,
paymentRecordInserter?: (paymentInfoId: number, razorpayOrder: any, tx: Tx) => Promise<any>
): Promise<typeof orders.$inferSelect[]> {
return db.transaction(async (tx) => {
let sharedPaymentInfoId: number | null = null
if (paymentMethod === "online") {
const [paymentInfo] = await tx
.insert(paymentInfoTable)
.values({
status: "pending",
gateway: "razorpay",
merchantOrderId: `multi_order_${Date.now()}`,
})
.returning()
sharedPaymentInfoId = paymentInfo.id
}
const ordersToInsert: Omit<typeof orders.$inferInsert, "id">[] = ordersData.map(
(od) => ({
...od.order,
paymentInfoId: sharedPaymentInfoId,
})
)
const insertedOrders = await tx.insert(orders).values(ordersToInsert).returning()
const allOrderItems: Omit<typeof orderItems.$inferInsert, "id">[] = []
const allOrderStatuses: Omit<typeof orderStatus.$inferInsert, "id">[] = []
insertedOrders.forEach((order: typeof orders.$inferSelect, index: number) => {
const od = ordersData[index]
od.orderItems.forEach((item: any) => {
allOrderItems.push({ ...item, orderId: order.id as number })
})
allOrderStatuses.push({
...od.orderStatus,
orderId: order.id as number,
})
})
await tx.insert(orderItems).values(allOrderItems)
await tx.insert(orderStatus).values(allOrderStatuses)
if (paymentMethod === "online" && sharedPaymentInfoId && razorpayOrderCreator && paymentRecordInserter) {
const razorpayOrder = await razorpayOrderCreator(
sharedPaymentInfoId,
totalWithDelivery.toString()
)
await paymentRecordInserter(
sharedPaymentInfoId,
razorpayOrder,
tx
)
}
return insertedOrders
})
}

View file

@ -0,0 +1,138 @@
import { db } from '../../db/db_index'
import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productReviews, users } from '../../db/schema'
import { eq, and, gt, sql, desc } from 'drizzle-orm'
/**
* Get product basic info with unit
*/
export async function getProductWithUnit(productId: number) {
return db
.select({
id: productInfo.id,
name: productInfo.name,
shortDescription: productInfo.shortDescription,
longDescription: productInfo.longDescription,
price: productInfo.price,
marketPrice: productInfo.marketPrice,
images: productInfo.images,
isOutOfStock: productInfo.isOutOfStock,
storeId: productInfo.storeId,
unitShortNotation: units.shortNotation,
incrementStep: productInfo.incrementStep,
productQuantity: productInfo.productQuantity,
isFlashAvailable: productInfo.isFlashAvailable,
flashPrice: productInfo.flashPrice,
})
.from(productInfo)
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(eq(productInfo.id, productId))
.limit(1)
}
/**
* Get store info by ID
*/
export async function getStoreById(storeId: number) {
return db.query.storeInfo.findFirst({
where: eq(storeInfo.id, storeId),
columns: { id: true, name: true, description: true },
})
}
/**
* Get delivery slots for product
*/
export async function getProductDeliverySlots(productId: number) {
return db
.select({
id: deliverySlotInfo.id,
deliveryTime: deliverySlotInfo.deliveryTime,
freezeTime: deliverySlotInfo.freezeTime,
})
.from(productSlots)
.innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id))
.where(
and(
eq(productSlots.productId, productId),
eq(deliverySlotInfo.isActive, true),
gt(deliverySlotInfo.deliveryTime, sql`NOW()`),
gt(deliverySlotInfo.freezeTime, sql`NOW()`)
)
)
.orderBy(deliverySlotInfo.deliveryTime)
}
/**
* Get special deals for product
*/
export async function getProductSpecialDeals(productId: number) {
return db
.select({
quantity: specialDeals.quantity,
price: specialDeals.price,
validTill: specialDeals.validTill,
})
.from(specialDeals)
.where(
and(
eq(specialDeals.productId, productId),
gt(specialDeals.validTill, sql`NOW()`)
)
)
.orderBy(specialDeals.quantity)
}
/**
* Get product reviews with user info
*/
export async function getProductReviews(productId: number, limit: number, offset: number) {
return db
.select({
id: productReviews.id,
reviewBody: productReviews.reviewBody,
ratings: productReviews.ratings,
imageUrls: productReviews.imageUrls,
reviewTime: productReviews.reviewTime,
userName: users.name,
})
.from(productReviews)
.innerJoin(users, eq(productReviews.userId, users.id))
.where(eq(productReviews.productId, productId))
.orderBy(desc(productReviews.reviewTime))
.limit(limit)
.offset(offset)
}
/**
* Count reviews for product
*/
export async function countProductReviews(productId: number) {
const result = await db
.select({ count: sql`count(*)` })
.from(productReviews)
.where(eq(productReviews.productId, productId))
return Number(result[0].count)
}
/**
* Check if product exists
*/
export async function checkProductExists(productId: number) {
return db.query.productInfo.findFirst({
where: eq(productInfo.id, productId),
})
}
/**
* Insert new review
*/
export async function insertReview(data: {
userId: number
productId: number
reviewBody: string
ratings: number
imageUrls: string[]
}) {
return db.insert(productReviews).values(data).returning()
}

View file

@ -47,9 +47,9 @@ export const slotsRouter = router({
getSlotsWithProducts: publicProcedure.query(async () => {
const allSlots = await getAllSlotsFromCache();
const currentTime = new Date();
const validSlots = allSlots.filter((slot) =>
dayjs(slot.freezeTime).isAfter(currentTime)
);
const validSlots = allSlots
.filter((slot) => dayjs(slot.freezeTime).isAfter(currentTime))
.sort((a, b) => dayjs(a.deliveryTime).valueOf() - dayjs(b.deliveryTime).valueOf());
return {
slots: validSlots,

View file

@ -63,8 +63,8 @@ const isDevMode = Constants.executionEnvironment !== "standalone";
// const BASE_API_URL = API_URL;
// const BASE_API_URL = 'http://10.0.2.2:4000';
// const BASE_API_URL = 'http://192.168.100.101:4000';
// const BASE_API_URL = 'http://192.168.1.3:4000';
let BASE_API_URL = "https://mf.freshyo.in";
const BASE_API_URL = 'http://192.168.1.3:4000';
// let BASE_API_URL = "https://mf.freshyo.in";
// let BASE_API_URL = 'http://192.168.100.104:4000';
// let BASE_API_URL = 'http://192.168.29.176:4000';