order cleanup
This commit is contained in:
Generated
+33
-1
@@ -30,6 +30,7 @@
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/platform-fastify": "^11.1.8",
|
||||
"@nestjs/schedule": "^6.0.1",
|
||||
"@nestjs/swagger": "^11.2.1",
|
||||
"@nestjs/throttler": "^6.4.0",
|
||||
"axios": "^1.13.1",
|
||||
@@ -4493,6 +4494,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schedule": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.1.tgz",
|
||||
"integrity": "sha512-v3yO6cSPAoBSSyH67HWnXHzuhPhSNZhRmLY38JvCt2sqY8sPMOODpcU1D79iUMFf7k16DaMEbL4Mgx61ZhiC8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron": "4.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^10.0.0 || ^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schematics": {
|
||||
"version": "11.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz",
|
||||
@@ -5867,6 +5881,12 @@
|
||||
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz",
|
||||
"integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/methods": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
|
||||
@@ -7936,6 +7956,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cron": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/cron/-/cron-4.3.3.tgz",
|
||||
"integrity": "sha512-B/CJj5yL3sjtlun6RtYHvoSB26EmQ2NUmhq9ZiJSyKIM4K/fqfh9aelDFlIayD2YMeFZqWLi9hHV+c+pq2Djkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/luxon": "~3.7.0",
|
||||
"luxon": "~3.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.x"
|
||||
}
|
||||
},
|
||||
"node_modules/cron-parser": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
|
||||
@@ -11435,7 +11468,6 @@
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/platform-fastify": "^11.1.8",
|
||||
"@nestjs/schedule": "^6.0.1",
|
||||
"@nestjs/swagger": "^11.2.1",
|
||||
"@nestjs/throttler": "^6.4.0",
|
||||
"axios": "^1.13.1",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { OrdersService } from './orders.service';
|
||||
import { OrdersController } from './orders.controller';
|
||||
import { Order } from './entities/order.entity';
|
||||
@@ -17,6 +18,7 @@ import { JwtModule } from '@nestjs/jwt';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ScheduleModule,
|
||||
MikroOrmModule.forFeature([Order, OrderItem, User, Restaurant, Food, UserAddress, PaymentMethod]),
|
||||
CartModule,
|
||||
UtilsModule,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { Order } from './entities/order.entity';
|
||||
import { OrderItem } from './entities/order-item.entity';
|
||||
@@ -15,9 +15,12 @@ import { PaymentMethod } from '../payments/entities/payment-method.entity';
|
||||
import { PaymentsService } from '../payments/services/payments.service';
|
||||
import { DeliveryMethodEnum } from '../delivery/interface/delivery';
|
||||
import { Delivery } from '../delivery/entities/delivery.entity';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
private readonly logger = new Logger(OrdersService.name);
|
||||
|
||||
constructor(
|
||||
private readonly em: EntityManager,
|
||||
private readonly cartService: CartService,
|
||||
@@ -142,12 +145,19 @@ export class OrdersService {
|
||||
throw new NotFoundException('Restaurant not found');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const delivery = await this.em.findOne(Delivery, { id: cart.deliveryMethodId! });
|
||||
const delivery = await this.em.findOne(Delivery, { id: cart.deliveryMethodId });
|
||||
if (!delivery) {
|
||||
throw new NotFoundException('Delivery not found');
|
||||
}
|
||||
|
||||
// Validate minimum order price for delivery method
|
||||
const minOrderPrice = Number(delivery.minOrderPrice) || 0;
|
||||
if (minOrderPrice > 0 && cart.total < minOrderPrice) {
|
||||
throw new BadRequestException(
|
||||
`Minimum order amount for this delivery method is ${minOrderPrice}. Current total is ${cart.total}.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (delivery.method === DeliveryMethodEnum.DeliveryCourier && !cart.addressId) {
|
||||
throw new BadRequestException('Address is required. Please set a delivery address before creating an order.');
|
||||
}
|
||||
@@ -248,4 +258,69 @@ export class OrdersService {
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} order`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup job to handle abandoned orders (pending payment for >30 minutes)
|
||||
* Runs every 10 minutes to check for abandoned orders
|
||||
*/
|
||||
@Cron(CronExpression.EVERY_10_MINUTES)
|
||||
async cleanupAbandonedOrders() {
|
||||
this.logger.log('Starting cleanup of abandoned orders...');
|
||||
|
||||
try {
|
||||
const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000);
|
||||
|
||||
// Find abandoned orders: Pending status, Pending payment, created >30 minutes ago
|
||||
const abandonedOrders = await this.em.find(
|
||||
Order,
|
||||
{
|
||||
status: OrderStatus.Pending,
|
||||
paymentStatus: PaymentStatusEnum.Pending,
|
||||
createdAt: { $lt: thirtyMinutesAgo },
|
||||
},
|
||||
{ populate: ['items', 'items.food'] },
|
||||
);
|
||||
|
||||
if (abandonedOrders.length === 0) {
|
||||
this.logger.log('No abandoned orders found');
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(`Found ${abandonedOrders.length} abandoned order(s) to cleanup`);
|
||||
|
||||
// Process each abandoned order in a transaction
|
||||
for (const order of abandonedOrders) {
|
||||
await this.em.transactional(async em => {
|
||||
// Load order items with food relations
|
||||
await order.items.loadItems();
|
||||
|
||||
// Restore stock for each item
|
||||
for (const item of order.items) {
|
||||
const food = item.food;
|
||||
if (food) {
|
||||
food.stock += item.quantity;
|
||||
em.persist(food);
|
||||
this.logger.debug(
|
||||
`Restored ${item.quantity} units of stock for food ${food.id} (${food.title || 'N/A'})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update order status to Cancelled
|
||||
order.status = OrderStatus.Cancelled;
|
||||
em.persist(order);
|
||||
|
||||
await em.flush();
|
||||
this.logger.log(`Cancelled abandoned order ${order.id}`);
|
||||
});
|
||||
}
|
||||
|
||||
this.logger.log(`Successfully cleaned up ${abandonedOrders.length} abandoned order(s)`);
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
const errorStack = error instanceof Error ? error.stack : undefined;
|
||||
this.logger.error(`Error during cleanup of abandoned orders: ${errorMessage}`, errorStack);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user