forked from syntaxbullet/AuroraBot-discord
feat: Introduce new modules for class, inventory, leveling, and quests with expanded schema, refactor user service, and add verification scripts.
This commit is contained in:
124
src/modules/inventory/inventory.service.ts
Normal file
124
src/modules/inventory/inventory.service.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
import { inventory, items, users } from "@/db/schema";
|
||||
import { eq, and, sql } from "drizzle-orm";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import { economyService } from "@/modules/economy/economy.service";
|
||||
|
||||
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 [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;
|
||||
} else {
|
||||
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}`, txFn);
|
||||
|
||||
// Add Item (using local logic to keep in same tx, or could refactor addItem to take tx too and call it)
|
||||
// Let's refactor addItem below to accept tx, then call it here?
|
||||
// Since we are modifying buyItem, we can just inline the item addition or call addItem if we update it.
|
||||
// Let's assume we update addItem next. For now, inline the add logic but cleaner.
|
||||
|
||||
const existingInv = await txFn.query.inventory.findFirst({
|
||||
where: and(eq(inventory.userId, BigInt(userId)), eq(inventory.itemId, itemId)),
|
||||
});
|
||||
|
||||
if (existingInv) {
|
||||
await txFn.update(inventory).set({ quantity: sql`${inventory.quantity} + ${quantity}` })
|
||||
.where(and(eq(inventory.userId, BigInt(userId)), eq(inventory.itemId, itemId)));
|
||||
} else {
|
||||
await txFn.insert(inventory).values({ userId: BigInt(userId), itemId, quantity });
|
||||
}
|
||||
|
||||
return { success: true, item, totalPrice };
|
||||
};
|
||||
|
||||
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user