forked from syntaxbullet/AuroraBot-discord
137 lines
5.1 KiB
TypeScript
137 lines
5.1 KiB
TypeScript
|
|
import { inventory, items, users } from "@/db/schema";
|
|
import { eq, and, sql, count } from "drizzle-orm";
|
|
import { DrizzleClient } from "@/lib/DrizzleClient";
|
|
import { economyService } from "@/modules/economy/economy.service";
|
|
import { config } from "@/lib/config";
|
|
|
|
export const inventoryService = {
|
|
addItem: async (userId: string, itemId: number, quantity: bigint = 1n, tx?: any) => {
|
|
const execute = async (txFn: any) => {
|
|
// Check if item exists in inventory
|
|
const existing = await txFn.query.inventory.findFirst({
|
|
where: and(
|
|
eq(inventory.userId, BigInt(userId)),
|
|
eq(inventory.itemId, itemId)
|
|
),
|
|
});
|
|
|
|
if (existing) {
|
|
const newQuantity = (existing.quantity ?? 0n) + quantity;
|
|
if (newQuantity > config.inventory.maxStackSize) {
|
|
throw new Error(`Cannot exceed max stack size of ${config.inventory.maxStackSize}`);
|
|
}
|
|
|
|
const [entry] = await txFn.update(inventory)
|
|
.set({
|
|
quantity: newQuantity,
|
|
})
|
|
.where(and(
|
|
eq(inventory.userId, BigInt(userId)),
|
|
eq(inventory.itemId, itemId)
|
|
))
|
|
.returning();
|
|
return entry;
|
|
} else {
|
|
// Check Slot Limit
|
|
const [inventoryCount] = await txFn
|
|
.select({ count: count() })
|
|
.from(inventory)
|
|
.where(eq(inventory.userId, BigInt(userId)));
|
|
|
|
if (inventoryCount.count >= config.inventory.maxSlots) {
|
|
throw new Error(`Inventory full (Max ${config.inventory.maxSlots} slots)`);
|
|
}
|
|
|
|
if (quantity > config.inventory.maxStackSize) {
|
|
throw new Error(`Cannot exceed max stack size of ${config.inventory.maxStackSize}`);
|
|
}
|
|
|
|
const [entry] = await txFn.insert(inventory)
|
|
.values({
|
|
userId: BigInt(userId),
|
|
itemId: itemId,
|
|
quantity: quantity,
|
|
})
|
|
.returning();
|
|
return entry;
|
|
}
|
|
};
|
|
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
},
|
|
|
|
removeItem: async (userId: string, itemId: number, quantity: bigint = 1n, tx?: any) => {
|
|
const execute = async (txFn: any) => {
|
|
const existing = await txFn.query.inventory.findFirst({
|
|
where: and(
|
|
eq(inventory.userId, BigInt(userId)),
|
|
eq(inventory.itemId, itemId)
|
|
),
|
|
});
|
|
|
|
if (!existing || (existing.quantity ?? 0n) < quantity) {
|
|
throw new Error("Insufficient item quantity");
|
|
}
|
|
|
|
if ((existing.quantity ?? 0n) === quantity) {
|
|
// Delete if quantity becomes 0
|
|
await txFn.delete(inventory)
|
|
.where(and(
|
|
eq(inventory.userId, BigInt(userId)),
|
|
eq(inventory.itemId, itemId)
|
|
));
|
|
return { itemId, quantity: 0n, userId: BigInt(userId) };
|
|
} else {
|
|
const [entry] = await txFn.update(inventory)
|
|
.set({
|
|
quantity: sql`${inventory.quantity} - ${quantity}`,
|
|
})
|
|
.where(and(
|
|
eq(inventory.userId, BigInt(userId)),
|
|
eq(inventory.itemId, itemId)
|
|
))
|
|
.returning();
|
|
return entry;
|
|
}
|
|
};
|
|
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
},
|
|
|
|
getInventory: async (userId: string) => {
|
|
return await DrizzleClient.query.inventory.findMany({
|
|
where: eq(inventory.userId, BigInt(userId)),
|
|
with: {
|
|
item: true,
|
|
},
|
|
});
|
|
},
|
|
|
|
buyItem: async (userId: string, itemId: number, quantity: bigint = 1n, tx?: any) => {
|
|
const execute = async (txFn: any) => {
|
|
const item = await txFn.query.items.findFirst({
|
|
where: eq(items.id, itemId),
|
|
});
|
|
|
|
if (!item) throw new Error("Item not found");
|
|
if (!item.price) throw new Error("Item is not for sale");
|
|
|
|
const totalPrice = item.price * quantity;
|
|
|
|
// Deduct Balance using economy service (passing tx ensures atomicity)
|
|
await economyService.modifyUserBalance(userId, -totalPrice, 'PURCHASE', `Bought ${quantity}x ${item.name}`, null, txFn);
|
|
|
|
await inventoryService.addItem(userId, itemId, quantity, txFn);
|
|
|
|
return { success: true, item, totalPrice };
|
|
};
|
|
|
|
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
},
|
|
|
|
getItem: async (itemId: number) => {
|
|
return await DrizzleClient.query.items.findFirst({
|
|
where: eq(items.id, itemId),
|
|
});
|
|
},
|
|
};
|