feat: update order and user repositories to use instance methods for creation and improve session handling; enhance order service with transaction management for payment and cancellation

This commit is contained in:
2026-01-13 00:52:29 +03:00
parent f8aebd53df
commit f13a83c992
5 changed files with 87 additions and 11 deletions
+5 -3
View File
@@ -4,13 +4,15 @@ import { OrderModel, type OrderDocument } from "../models/order.model.js";
export class OrderRepository {
static create(
data: Omit<OrderDocument, "_id" | "createdAt" | "updatedAt">,
session: ClientSession,
session?: ClientSession,
): Promise<OrderDocument> {
return OrderModel.create(data, { session });
const order = new OrderModel(data);
return order.save({ session: session ?? undefined });
}
static findById(id: string, session?: ClientSession) {
return OrderModel.findById(id).session(session ?? null);
const query = OrderModel.findById(id);
return session ? query.session(session) : query;
}
static findAll() {
+2 -1
View File
@@ -15,6 +15,7 @@ export class UserRepository {
data: Pick<UserDocument, "email" | "passwordHash" | "role">,
session?: ClientSession,
): Promise<UserDocument> {
return UserModel.create([data], { session }).then((res) => res[0]);
const user = new UserModel(data);
return user.save({ session: session ?? undefined });
}
}
+78 -5
View File
@@ -4,13 +4,86 @@ import { OrderRepository } from "../repositories/order.repo.js";
export class OrderService {
static listOrders(userId: string, role: string) {
throw new Error("Method not implemented.");
if (String(role).toLowerCase() === "admin") {
return OrderRepository.findAll();
}
return OrderRepository.findByUser(userId);
}
static payOrder(id: string | string[] | undefined) {
throw new Error("Method not implemented.");
static async payOrder(id: string | string[] | undefined) {
if (!id || Array.isArray(id)) {
throw { statusCode: 400, message: "Invalid order id" };
}
const session = await mongoose.startSession();
session.startTransaction();
try {
const order = await OrderRepository.findById(id, session);
if (!order) {
throw { statusCode: 404, message: "Order not found" };
}
if (order.status === "paid") {
throw { statusCode: 409, message: "Order is already paid" };
}
if (order.status === "cancelled") {
throw { statusCode: 409, message: "Cannot pay a cancelled order" };
}
order.status = "paid";
await OrderRepository.save(order, session);
await session.commitTransaction();
return order;
} catch (err) {
await session.abortTransaction();
throw err;
} finally {
session.endSession();
}
}
static cancelOrder(id: string | string[] | undefined) {
throw new Error("Method not implemented.");
static async cancelOrder(id: string | string[] | undefined) {
if (!id || Array.isArray(id)) {
throw { statusCode: 400, message: "Invalid order id" };
}
const session = await mongoose.startSession();
session.startTransaction();
try {
const order = await OrderRepository.findById(id, session);
if (!order) {
throw { statusCode: 404, message: "Order not found" };
}
if (order.status === "cancelled") {
throw { statusCode: 409, message: "Order is already cancelled" };
}
// restore stock for each item
for (const item of order.items) {
await ProductRepository.incrementStock(
item.productId,
item.quantity,
session,
);
}
order.status = "cancelled";
await OrderRepository.save(order, session);
await session.commitTransaction();
return order;
} catch (err) {
await session.abortTransaction();
throw err;
} finally {
session.endSession();
}
}
static async createOrder(
userId: string,
+1 -1
View File
@@ -1,4 +1,4 @@
import { JwtPayload } from "../utils/jwt.js";
import { JwtPayload } from "../utils/jwt.ts";
declare global {
namespace Express {
+1 -1
View File
@@ -3,7 +3,7 @@
"compilerOptions": {
// File Layout
"rootDir": "./src",
// "outDir": "./dist",
"outDir": "./dist",
// Environment Settings
// See also https://aka.ms/tsconfig/module