order list for user

This commit is contained in:
2026-01-17 18:37:04 +03:30
parent e07075a7c5
commit 02ab8fbc6a
9 changed files with 84 additions and 91 deletions
@@ -1,11 +1,11 @@
import { Controller, Get, Post, Param, UseGuards, Patch, Query, Body } from '@nestjs/common'; import { Controller, Get, Post, Param, UseGuards, Patch, Query, Body } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiHeader, ApiBody } from '@nestjs/swagger'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiHeader, ApiBody, ApiQuery } from '@nestjs/swagger';
import { OrdersService } from '../providers/orders.service'; import { OrdersService } from '../providers/orders.service';
import { AuthGuard } from '../../auth/guards/auth.guard'; import { AuthGuard } from '../../auth/guards/auth.guard';
import { UserId } from '../../../common/decorators/user-id.decorator'; import { UserId } from '../../../common/decorators/user-id.decorator';
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard'; import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
import { FindOrdersDto } from '../dto/find-orders.dto'; import { FindOrdersDto } from '../dto/find-orders.dto';
import { OrderStatus } from '../interface/order.interface'; import { OrderStatusEnum } from '../interface/order.interface';
import { UpdateOrderStatusDto } from '../dto/update-order-status.dto'; import { UpdateOrderStatusDto } from '../dto/update-order-status.dto';
import { Permissions } from 'src/common/decorators/permissions.decorator'; import { Permissions } from 'src/common/decorators/permissions.decorator';
import { Permission } from 'src/common/enums/permission.enum'; import { Permission } from 'src/common/enums/permission.enum';
@@ -25,13 +25,21 @@ export class OrdersController {
return this.ordersService.createOrder(userId, body); return this.ordersService.createOrder(userId, body);
} }
// @UseGuards(AuthGuard) @Get('public/orders')
// @Get('public/orders') @UseGuards(AuthGuard)
@ApiQuery({ name: 'page', required: false, type: Number })
// @ApiOperation({ summary: 'Get all orders with pagination and filters' }) @ApiQuery({ name: 'limit', required: false, type: Number })
// findAll(@Query() dto: FindOrdersDto, @UserId() userId: string) { @ApiQuery({ name: 'search', required: false, type: String })
// return this.ordersService.findAllForUser(dto, userId); @ApiQuery({ name: 'orderBy', required: false, type: String })
// } @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] })
@ApiQuery({ name: 'statuses', required: false, enum: OrderStatusEnum })
@ApiQuery({ name: 'from', required: false, type: Date })
@ApiQuery({ name: 'to', required: false, type: Date })
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
async findAll(@Query() dto: FindOrdersDto, @UserId() userId: string) {
const orders=await this.ordersService.findAllForUser(userId, dto);
return orders
}
// @UseGuards(AuthGuard) // @UseGuards(AuthGuard)
// @ApiOperation({ summary: 'Get an order By id for User' }) // @ApiOperation({ summary: 'Get an order By id for User' })
+11 -14
View File
@@ -10,7 +10,7 @@ import {
} from 'class-validator'; } from 'class-validator';
import { Type, Transform } from 'class-transformer'; import { Type, Transform } from 'class-transformer';
import { ApiPropertyOptional } from '@nestjs/swagger'; import { ApiPropertyOptional } from '@nestjs/swagger';
import { OrderStatus } from '../interface/order.interface'; import { OrderStatusEnum } from '../interface/order.interface';
import { PaymentStatusEnum } from '../../payment/interface/payment'; import { PaymentStatusEnum } from '../../payment/interface/payment';
const sortOrderOptions = ['asc', 'desc'] as const; const sortOrderOptions = ['asc', 'desc'] as const;
@@ -31,13 +31,10 @@ export class FindOrdersDto {
@Min(1) @Min(1)
limit: number = 10; limit: number = 10;
/**
* ?statuses=paid,confirmed
* ?statuses=paid&statuses=confirmed
*/
@ApiPropertyOptional({ @ApiPropertyOptional({
description: 'Filter by order statuses', description: 'Filter by order statuses',
enum: OrderStatus, enum: OrderStatusEnum,
isArray: true, isArray: true,
}) })
@IsOptional() @IsOptional()
@@ -45,13 +42,13 @@ export class FindOrdersDto {
Array.isArray(value) ? value : value?.split(',') Array.isArray(value) ? value : value?.split(',')
) )
@IsArray() @IsArray()
@IsEnum(OrderStatus, { each: true }) @IsEnum(OrderStatusEnum, { each: true })
statuses?: OrderStatus[]; statuses?: OrderStatusEnum[];
@ApiPropertyOptional({ enum: PaymentStatusEnum }) // @ApiPropertyOptional({ enum: PaymentStatusEnum })
@IsOptional() // @IsOptional()
@IsEnum(PaymentStatusEnum) // @IsEnum(PaymentStatusEnum)
paymentStatus?: PaymentStatusEnum; // paymentStatus?: PaymentStatusEnum;
@ApiPropertyOptional() @ApiPropertyOptional()
@IsOptional() @IsOptional()
@@ -61,12 +58,12 @@ export class FindOrdersDto {
@ApiPropertyOptional({ format: 'date-time' }) @ApiPropertyOptional({ format: 'date-time' })
@IsOptional() @IsOptional()
@IsDateString() @IsDateString()
startDate?: string; from?: string;
@ApiPropertyOptional({ format: 'date-time' }) @ApiPropertyOptional({ format: 'date-time' })
@IsOptional() @IsOptional()
@IsDateString() @IsDateString()
endDate?: string; to?: string;
@ApiPropertyOptional({ default: 'createdAt' }) @ApiPropertyOptional({ default: 'createdAt' })
@IsOptional() @IsOptional()
+3 -3
View File
@@ -10,7 +10,7 @@ import {
PrimaryKey, PrimaryKey,
} from '@mikro-orm/core'; } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity'; import { BaseEntity } from '../../../common/entities/base.entity';
import { OrderStatus } from '../interface/order.interface'; import { OrderStatusEnum } from '../interface/order.interface';
import { User } from '../../user/entities/user.entity'; import { User } from '../../user/entities/user.entity';
import { OrderItem } from './order-item.entity'; import { OrderItem } from './order-item.entity';
import { Payment } from 'src/modules/payment/entities/payment.entity'; import { Payment } from 'src/modules/payment/entities/payment.entity';
@@ -68,7 +68,7 @@ export class Order extends BaseEntity {
// description?: string; // description?: string;
@Enum(() => OrderStatus) @Enum(() => OrderStatusEnum)
status!: OrderStatus; status!: OrderStatusEnum;
} }
+3 -3
View File
@@ -1,4 +1,4 @@
import type { OrderStatus } from '../interface/order.interface'; import type { OrderStatusEnum } from '../interface/order.interface';
export class OrderCreatedEvent { export class OrderCreatedEvent {
constructor( constructor(
@@ -15,7 +15,7 @@ export class OrderStatusChangedEvent {
public readonly userId: string, public readonly userId: string,
public readonly orderNumber: string, public readonly orderNumber: string,
public readonly: string, public readonly: string,
public readonly previousStatus: OrderStatus, public readonly previousStatus: OrderStatusEnum,
public readonly newStatus: OrderStatus, public readonly newStatus: OrderStatusEnum,
) { } ) { }
} }
@@ -1,5 +1,6 @@
export enum OrderStatus { export enum OrderStatusEnum {
NEW = 'new', NEW = 'new',
INVOICE = 'invoice',
PREPARING = 'preparing', PREPARING = 'preparing',
COMPLETED = 'completed', COMPLETED = 'completed',
CANCELED = 'canceled', CANCELED = 'canceled',
@@ -7,7 +7,7 @@ import { NotifTitleEnum } from 'src/modules/notification/interfaces/notification
import { NotificationService } from 'src/modules/notification/services/notification.service'; import { NotificationService } from 'src/modules/notification/services/notification.service';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { OrderRepository } from '../repositories/order.repository'; import { OrderRepository } from '../repositories/order.repository';
import { OrderStatus } from '../interface/order.interface'; import { OrderStatusEnum } from '../interface/order.interface';
import { PaymentMethodEnum } from 'src/modules/payment/interface/payment'; import { PaymentMethodEnum } from 'src/modules/payment/interface/payment';
import { UserService } from 'src/modules/user/providers/user.service'; import { UserService } from 'src/modules/user/providers/user.service';
@@ -3,23 +3,18 @@ import { EntityManager } from '@mikro-orm/postgresql';
import { Order } from '../entities/order.entity'; import { Order } from '../entities/order.entity';
import { OrderItem } from '../entities/order-item.entity'; import { OrderItem } from '../entities/order-item.entity';
import { User } from '../../user/entities/user.entity'; import { User } from '../../user/entities/user.entity';
import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment'; import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment';
import { PaymentsService } from '../../payment/services/payments.service'; import { PaymentsService } from '../../payment/services/payments.service';
import { OrderRepository } from '../repositories/order.repository'; import { OrderRepository } from '../repositories/order.repository';
import { FindOrdersDto } from '../dto/find-orders.dto'; import { FindOrdersDto } from '../dto/find-orders.dto';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
import { Payment } from 'src/modules/payment/entities/payment.entity'; import { Payment } from 'src/modules/payment/entities/payment.entity';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events'; import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
import { OrderMessage } from 'src/common/enums/message.enum'; import { OrderMessage } from 'src/common/enums/message.enum';
import { CreateOrderDto } from '../dto/create-order.dto'; import { CreateOrderDto } from '../dto/create-order.dto';
import { UserService } from 'src/modules/user/providers/user.service'; import { UserService } from 'src/modules/user/providers/user.service';
import { OrderItemStatus, OrderStatus } from '../interface/order.interface'; import { OrderItemStatus, OrderStatusEnum } from '../interface/order.interface';
import { OrderItemRepository } from '../repositories/order-item.repository'; import { OrderItemRepository } from '../repositories/order-item.repository';
import { ProductService } from 'src/modules/product/providers/product.service'; import { ProductService } from 'src/modules/product/providers/product.service';
import { ProductRepository } from 'src/modules/product/repositories/product.repository'; import { ProductRepository } from 'src/modules/product/repositories/product.repository';
@@ -62,7 +57,7 @@ export class OrdersService {
total: 0, total: 0,
paidAmount: 0, paidAmount: 0,
balance: 0, balance: 0,
status: OrderStatus.NEW status: OrderStatusEnum.NEW
}) })
em.persist(order) em.persist(order)
@@ -109,4 +104,8 @@ export class OrdersService {
return order return order
} }
async findAllForUser(userId: string, dto: FindOrdersDto) {
const orders = await this.orderRepository.findAllPaginated({ userId, ...dto })
return orders
}
} }
@@ -3,22 +3,13 @@ import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
import { FilterQuery } from '@mikro-orm/core'; import { FilterQuery } from '@mikro-orm/core';
import { Order } from '../entities/order.entity'; import { Order } from '../entities/order.entity';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
import { OrderStatus } from '../interface/order.interface'; import { OrderStatusEnum } from '../interface/order.interface';
import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment'; import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment';
import { FindOrdersDto } from '../dto/find-orders.dto';
type FindOrdersOpts = { class FindOrdersOpts extends FindOrdersDto {
page?: number;
limit?: number;
statuses?: OrderStatus[];
paymentStatus?: PaymentStatusEnum;
search?: string;
startDate?: string;
endDate?: string;
orderBy?: string;
order?: 'asc' | 'desc';
userId?: string; userId?: string;
excludeOnlinePendingPayment?: boolean;
}; };
@Injectable() @Injectable()
@@ -31,30 +22,29 @@ export class OrderRepository extends EntityRepository<Order> {
* Find orders with pagination and optional filters. * Find orders with pagination and optional filters.
* Supports: statuses, paymentStatus, search (orderNumber), date range, ordering. * Supports: statuses, paymentStatus, search (orderNumber), date range, ordering.
*/ */
async findAllPaginated( opts: FindOrdersOpts = {}): Promise<PaginatedResult<Order>> { async findAllPaginated(opts: FindOrdersOpts): Promise<PaginatedResult<Order>> {
const { const {
page = 1, page = 1,
limit = 10, limit = 10,
statuses, statuses,
search, search,
startDate,
endDate,
orderBy = 'createdAt', orderBy = 'createdAt',
order = 'desc', order = 'desc',
userId, userId,
excludeOnlinePendingPayment = false, from,
to
} = opts; } = opts;
const offset = (page - 1) * limit; const offset = (page - 1) * limit;
const where: FilterQuery<Order> = { }; const where: FilterQuery<Order> = {};
// Filter by statuses // Filter by statuses
if (statuses) { if (statuses) {
// Ensure statuses is always an array and normalize it // Ensure statuses is always an array and normalize it
const normalizedStatuses = Array.isArray(statuses) ? statuses : [statuses]; const normalizedStatuses = Array.isArray(statuses) ? statuses : [statuses];
// Filter out any empty values // Filter out any empty values
const validStatuses = normalizedStatuses.filter((s): s is OrderStatus => !!s); const validStatuses = normalizedStatuses.filter((s): s is OrderStatusEnum => !!s);
if (validStatuses.length > 0) { if (validStatuses.length > 0) {
// Use direct equality for single value, $in for multiple values to avoid SQL generation issues // Use direct equality for single value, $in for multiple values to avoid SQL generation issues
@@ -71,13 +61,13 @@ export class OrderRepository extends EntityRepository<Order> {
} }
// Filter by date range // Filter by date range
if (startDate || endDate) { if (from || to) {
where.createdAt = {}; where.createdAt = {};
if (startDate) { if (from) {
where.createdAt.$gte = new Date(startDate); where.createdAt.$gte = new Date(from);
} }
if (endDate) { if (to) {
where.createdAt.$lte = new Date(endDate); where.createdAt.$lte = new Date(to);
} }
} }
@@ -99,40 +89,38 @@ export class OrderRepository extends EntityRepository<Order> {
where.$or = searchConditions; where.$or = searchConditions;
} }
// First, fetch orders without reviews // First, fetch orders without reviews
const [data, total] = await this.findAndCount(where, { const [data, total] = await this.findAndCount(where, {
limit, limit,
offset, offset,
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
populate: ['user', 'restaurant', 'deliveryMethod', 'paymentMethod', 'items', 'items.product'] as never, populate: ['items','items.product'],
}); });
// Collect all (orderId, productId) pairs for efficient review lookup // Collect all (orderId, productId) pairs for efficient review lookup
const orderproductPairs: Array<{ orderId: string; productId: bigint }> = []; // const orderproductPairs: Array<{ orderId: string; productId: bigint }> = [];
for (const order of data) { // for (const order of data) {
for (const item of order.items.getItems()) { // for (const item of order.items.getItems()) {
if (item.product?.id) { // if (item.product?.id) {
orderproductPairs.push({ orderId: order.id, productId: item.product.id }); // orderproductPairs.push({ orderId: order.id, productId: item.product.id });
} // }
} // }
} // }
// Map reviewIds to products // Map reviewIds to products
for (const order of data) { // for (const order of data) {
for (const item of order.items.getItems()) { // for (const item of order.items.getItems()) {
if (item.product) { // if (item.product) {
const key = `${order.id}-${item.product.id}`; // const key = `${order.id}-${item.product.id}`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const product = item.product as any; // const product = item.product as any;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
} // }
} // }
} // }
const totalPages = Math.ceil(total / limit); const totalPages = Math.ceil(total / limit);
@@ -5,8 +5,8 @@ import { EntityManager } from '@mikro-orm/postgresql';
import { Order } from '../../order/entities/order.entity'; import { Order } from '../../order/entities/order.entity';
import { Logger } from '@nestjs/common'; import { Logger } from '@nestjs/common';
import { GatewayManager } from '../gateways/gateway.manager'; import { GatewayManager } from '../gateways/gateway.manager';
import { OrderPaymentContext } from '../interface/payment'; import { OrderPaymentContext } from '../interface/payment';
import { OrderStatus } from 'src/modules/order/interface/order.interface'; import { OrderStatusEnum } from 'src/modules/order/interface/order.interface';
import { ChartPeriodEnum, PaymentChartDto } from '../dto/payment-chart.dto'; import { ChartPeriodEnum, PaymentChartDto } from '../dto/payment-chart.dto';
import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum'; import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';