forked from syntaxbullet/AuroraBot-discord
refactor: standardize transaction pattern in class.service.ts
Replace manual transaction handling with withTransaction helper pattern for consistency with other services (economy, inventory, quest, leveling). Also fix validation bug in modifyClassBalance: - Before: if (balance < amount) - After: if (balance + amount < 0n) This correctly validates negative amounts (debits) to prevent balances going below zero.
This commit is contained in:
@@ -2,14 +2,16 @@ import { classes, users } from "@/db/schema";
|
|||||||
import { eq, sql } from "drizzle-orm";
|
import { eq, sql } from "drizzle-orm";
|
||||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@/lib/errors";
|
||||||
|
import { withTransaction } from "@/lib/db";
|
||||||
|
import type { Transaction } from "@/lib/types";
|
||||||
|
|
||||||
export const classService = {
|
export const classService = {
|
||||||
getAllClasses: async () => {
|
getAllClasses: async () => {
|
||||||
return await DrizzleClient.query.classes.findMany();
|
return await DrizzleClient.query.classes.findMany();
|
||||||
},
|
},
|
||||||
|
|
||||||
assignClass: async (userId: string, classId: bigint, tx?: any) => {
|
assignClass: async (userId: string, classId: bigint, tx?: Transaction) => {
|
||||||
const execute = async (txFn: any) => {
|
return await withTransaction(async (txFn) => {
|
||||||
const cls = await txFn.query.classes.findFirst({
|
const cls = await txFn.query.classes.findFirst({
|
||||||
where: eq(classes.id, classId),
|
where: eq(classes.id, classId),
|
||||||
});
|
});
|
||||||
@@ -22,8 +24,7 @@ export const classService = {
|
|||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
};
|
}, tx);
|
||||||
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
||||||
},
|
},
|
||||||
getClassBalance: async (classId: bigint) => {
|
getClassBalance: async (classId: bigint) => {
|
||||||
const cls = await DrizzleClient.query.classes.findFirst({
|
const cls = await DrizzleClient.query.classes.findFirst({
|
||||||
@@ -31,15 +32,15 @@ export const classService = {
|
|||||||
});
|
});
|
||||||
return cls?.balance || 0n;
|
return cls?.balance || 0n;
|
||||||
},
|
},
|
||||||
modifyClassBalance: async (classId: bigint, amount: bigint, tx?: any) => {
|
modifyClassBalance: async (classId: bigint, amount: bigint, tx?: Transaction) => {
|
||||||
const execute = async (txFn: any) => {
|
return await withTransaction(async (txFn) => {
|
||||||
const cls = await txFn.query.classes.findFirst({
|
const cls = await txFn.query.classes.findFirst({
|
||||||
where: eq(classes.id, classId),
|
where: eq(classes.id, classId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!cls) throw new UserError("Class not found");
|
if (!cls) throw new UserError("Class not found");
|
||||||
|
|
||||||
if ((cls.balance ?? 0n) < amount) {
|
if ((cls.balance ?? 0n) + amount < 0n) {
|
||||||
throw new UserError("Insufficient class funds");
|
throw new UserError("Insufficient class funds");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,35 +52,31 @@ export const classService = {
|
|||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return updatedClass;
|
return updatedClass;
|
||||||
};
|
}, tx);
|
||||||
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateClass: async (id: bigint, data: Partial<typeof classes.$inferInsert>, tx?: any) => {
|
updateClass: async (id: bigint, data: Partial<typeof classes.$inferInsert>, tx?: Transaction) => {
|
||||||
const execute = async (txFn: any) => {
|
return await withTransaction(async (txFn) => {
|
||||||
const [updatedClass] = await txFn.update(classes)
|
const [updatedClass] = await txFn.update(classes)
|
||||||
.set(data)
|
.set(data)
|
||||||
.where(eq(classes.id, id))
|
.where(eq(classes.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
return updatedClass;
|
return updatedClass;
|
||||||
};
|
}, tx);
|
||||||
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
createClass: async (data: typeof classes.$inferInsert, tx?: any) => {
|
createClass: async (data: typeof classes.$inferInsert, tx?: Transaction) => {
|
||||||
const execute = async (txFn: any) => {
|
return await withTransaction(async (txFn) => {
|
||||||
const [newClass] = await txFn.insert(classes)
|
const [newClass] = await txFn.insert(classes)
|
||||||
.values(data)
|
.values(data)
|
||||||
.returning();
|
.returning();
|
||||||
return newClass;
|
return newClass;
|
||||||
};
|
}, tx);
|
||||||
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteClass: async (id: bigint, tx?: any) => {
|
deleteClass: async (id: bigint, tx?: Transaction) => {
|
||||||
const execute = async (txFn: any) => {
|
return await withTransaction(async (txFn) => {
|
||||||
await txFn.delete(classes).where(eq(classes.id, id));
|
await txFn.delete(classes).where(eq(classes.id, id));
|
||||||
};
|
}, tx);
|
||||||
return tx ? await execute(tx) : await DrizzleClient.transaction(execute);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user