This commit is contained in:
2026-01-17 13:16:43 +03:30
parent 2ddf892793
commit 1c45abcede
11 changed files with 161 additions and 43 deletions
@@ -9,6 +9,7 @@ import { OrderStatus } 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';
import { CreateOrderDto } from '../dto/create-order.dto';
@ApiTags('orders') @ApiTags('orders')
@ApiBearerAuth() @ApiBearerAuth()
@@ -16,13 +17,13 @@ import { Permission } from 'src/common/enums/permission.enum';
export class OrdersController { export class OrdersController {
constructor(private readonly ordersService: OrdersService) { } constructor(private readonly ordersService: OrdersService) { }
@Post('public/checkout')
// @UseGuards(AuthGuard) // @UseGuards(AuthGuard)
// @Post('public/checkout') @ApiOperation({ summary: 'Checkout : create order ' })
@ApiBody({ type: CreateOrderDto })
// @ApiOperation({ summary: 'Checkout : create order and payment record' }) checkout(@UserId() userId: string, @Body() body: CreateOrderDto) {
// checkout(@UserId() userId: string,) { return this.ordersService.createOrder(userId, body);
// return this.ordersService.checkout(userId); }
// }
// @UseGuards(AuthGuard) // @UseGuards(AuthGuard)
// @Get('public/orders') // @Get('public/orders')
+29
View File
@@ -0,0 +1,29 @@
import { IsString, IsOptional, IsBoolean, IsInt, Min, IsArray, IsNumber } from 'class-validator';
import { ApiPropertyOptional, ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
export class CreateOrderDto {
@IsArray()
@ApiProperty({ isArray: true })
items: CreateOrderItemDto[];
@IsString()
content: string
@IsArray()
@IsString({ each: true })
attachments: string[]
}
export class CreateOrderItemDto {
@IsInt()
@ApiProperty()
productId: bigint;
@ApiProperty()
quantity: number
@IsArray()
attributesValues: string[]
}
@@ -1,4 +1,4 @@
import { Entity, Enum, Index, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'; import { Entity, Enum, Index, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity'; import { BaseEntity } from '../../../common/entities/base.entity';
import { Order } from './order.entity'; import { Order } from './order.entity';
import { Product } from 'src/modules/product/entities/product.entity'; import { Product } from 'src/modules/product/entities/product.entity';
@@ -17,6 +17,9 @@ export class OrderItem extends BaseEntity {
@ManyToOne(() => Product) @ManyToOne(() => Product)
product!: Product; product!: Product;
@Property({ type: 'json' })
attributesValues: string[]
@Property({ type: 'int' }) @Property({ type: 'int' })
quantity!: number; quantity!: number;
+6 -7
View File
@@ -38,8 +38,8 @@ export class Order extends BaseEntity {
}) })
items = new Collection<OrderItem>(this); items = new Collection<OrderItem>(this);
@Property({ nullable: true }) // @Property({ nullable: true })
userAddress?: string | null; // userAddress?: string | null;
@Property({ type: 'bigint', autoincrement: true }) @Property({ type: 'bigint', autoincrement: true })
orderNumber: number; orderNumber: number;
@@ -52,8 +52,8 @@ export class Order extends BaseEntity {
subTotal!: number; subTotal!: number;
@Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) // @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
deliveryFee: number = 0; // deliveryFee: number = 0;
@Property({ type: 'decimal', precision: 10, scale: 0 }) @Property({ type: 'decimal', precision: 10, scale: 0 })
total!: number; total!: number;
@@ -64,12 +64,11 @@ export class Order extends BaseEntity {
@Property({ type: 'decimal', precision: 10, scale: 0 }) @Property({ type: 'decimal', precision: 10, scale: 0 })
balance!: number; balance!: number;
@Property({ type: 'text', nullable: true }) // @Property({ type: 'text', nullable: true })
description?: string; // description?: string;
@Enum(() => OrderStatus) @Enum(() => OrderStatus)
status!: OrderStatus; status!: OrderStatus;
} }
@@ -9,5 +9,5 @@ export enum OrderStatus {
export enum OrderItemStatus { export enum OrderItemStatus {
PENDING = 'pending', PENDING = 'pending',
VERIFIED = 'verified', CONFIRMED = 'confirmed',
} }
+74 -3
View File
@@ -17,6 +17,15 @@ 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 { UserService } from 'src/modules/user/providers/user.service';
import { OrderItemStatus, OrderStatus } from '../interface/order.interface';
import { OrderItemRepository } from '../repositories/order-item.repository';
import { ProductService } from 'src/modules/product/providers/product.service';
import { ProductRepository } from 'src/modules/product/repositories/product.repository';
import { TicketService } from 'src/modules/ticket/providers/tickets.service';
import { TicketRepository } from 'src/modules/ticket/repositories/tickets.repository';
import { TicketStatus } from 'src/modules/ticket/enums/ticket-status.enum';
@Injectable() @Injectable()
@@ -24,14 +33,76 @@ export class OrdersService {
private readonly logger = new Logger(OrdersService.name); private readonly logger = new Logger(OrdersService.name);
constructor( constructor(
private readonly em: EntityManager, private readonly em: EntityManager,
private readonly orderRepository: OrderRepository, private readonly orderRepository: OrderRepository,
private readonly orderItemRepository: OrderItemRepository,
private readonly paymentsService: PaymentsService, private readonly paymentsService: PaymentsService,
private readonly userService: UserService,
private readonly productService: ProductService,
private readonly productRepository: ProductRepository,
private readonly ticketRepository: TicketRepository,
private readonly ticketService: TicketService,
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
) { } ) { }
async createOrder(userId: string, dto: CreateOrderDto) {
const { attachments, content, items } = dto
this.em.transactional(async (em) => {
const user = await this.userService.findById(userId)
if (!user) {
throw new BadRequestException("user not found")
}
const order = this.orderRepository.create({
user,
discount: 0,
subTotal: 0,
orderNumber: 1,
total: 0,
paidAmount: 0,
balance: 0,
status: OrderStatus.NEW
})
em.persist(order)
const productIds = items.map(item => item.productId)
const products = await this.productRepository.find({
id: { $in: productIds }
})
if (productIds.length !== products.length) {
throw new BadRequestException("some products not found")
}
items.forEach(item => {
const product = products.find(p => p.id == item.productId)
if (!product) {
throw new BadRequestException(`product ${product} not found`)
}
const orderItem = this.orderItemRepository.create({
order,
attributesValues: item.attributesValues,
discount: 0,
product,
quantity: item.quantity,
status: OrderItemStatus.PENDING,
unitPrice: 0,
totalPrice: 0,
})
em.persist(orderItem)
});
const ticket = this.ticketRepository.create({
content,
user,
attachments,
status: TicketStatus.PENDING
})
em.persist(ticket)
em.flush()
})
}
} }
@@ -0,0 +1,11 @@
import { Injectable } from '@nestjs/common';
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
import { OrderItem } from '../entities/order-item.entity';
@Injectable()
export class OrderItemRepository extends EntityRepository<OrderItem> {
constructor(readonly em: EntityManager) {
super(em, OrderItem);
}
}
+10 -6
View File
@@ -7,24 +7,25 @@ import {
Collection, Collection,
Cascade, Cascade,
Enum, Enum,
PrimaryKey,
} from '@mikro-orm/core'; } from '@mikro-orm/core';
import { BaseEntity } from "../../../common/entities/base.entity"; import { BaseEntity } from "../../../common/entities/base.entity";
import { User } from "../../user/entities/user.entity"; import { User } from "../../user/entities/user.entity";
import { TicketStatus } from "../enums/ticket-status.enum"; import { TicketStatus } from "../enums/ticket-status.enum";
import { Product } from 'src/modules/product/entities/product.entity'; // import { Product } from 'src/modules/product/entities/product.entity';
import { Admin } from 'src/modules/admin/entities/admin.entity'; import { Admin } from 'src/modules/admin/entities/admin.entity';
@Entity() @Entity()
export class Ticket extends BaseEntity { export class Ticket extends BaseEntity {
@Property({ type: "bigint", primary: true }) @PrimaryKey({ type: "bigint", autoincrement: true })
id: number; id: bigint;
@Enum(() => TicketStatus) @Enum(() => TicketStatus)
status: TicketStatus; status: TicketStatus;
// TODO : create relation
@Property({ type: 'json' }) @Property({ type: 'text' })
products: Product | null; content: string;
@ManyToOne(() => Admin) @ManyToOne(() => Admin)
admin: Admin | null; admin: Admin | null;
@@ -35,6 +36,9 @@ export class Ticket extends BaseEntity {
@ManyToOne(() => Ticket) @ManyToOne(() => Ticket)
parent: Ticket | null; parent: Ticket | null;
@OneToMany(()=>Ticket,(ticket)=>ticket.id)
children=new Collection<Ticket>(this)
@Property({ type: 'json' }) @Property({ type: 'json' })
attachments: string[] attachments: string[]
} }
@@ -35,8 +35,8 @@ import dayjs from "dayjs";
// import { TicketsRepository } from "../repositories/tickets.repository"; // import { TicketsRepository } from "../repositories/tickets.repository";
@Injectable() @Injectable()
export class TicketsService { export class TicketService {
private readonly logger = new Logger(TicketsService.name); private readonly logger = new Logger(TicketService.name);
// constructor( // constructor(
// @InjectQueue(TICKET.QUEUE_NAME) private readonly ticketQueue: Queue, // @InjectQueue(TICKET.QUEUE_NAME) private readonly ticketQueue: Queue,
+2 -2
View File
@@ -6,7 +6,7 @@ import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto";
import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto"; import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto";
import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto"; import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto";
import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto"; import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto";
import { TicketsService } from "./providers/tickets.service"; import { TicketService } from "./providers/tickets.service";
import { ParamDto } from "../../common/DTO/param.dto"; import { ParamDto } from "../../common/DTO/param.dto";
@@ -15,7 +15,7 @@ import { ParamDto } from "../../common/DTO/param.dto";
@ApiTags("Tickets") @ApiTags("Tickets")
export class TicketsController { export class TicketsController {
constructor(private readonly ticketsService: TicketsService) {} constructor(private readonly ticketsService: TicketService) { }
//----------------- ticket categories ------------------- //----------------- ticket categories -------------------
+3 -3
View File
@@ -4,7 +4,7 @@ import { Module } from "@nestjs/common";
import { TICKET } from "./constant"; import { TICKET } from "./constant";
import { Ticket } from "./entities/ticket.entity"; import { Ticket } from "./entities/ticket.entity";
import { TicketsService } from "./providers/tickets.service"; import { TicketService } from "./providers/tickets.service";
import { TicketRepository } from "./repositories/tickets.repository"; import { TicketRepository } from "./repositories/tickets.repository";
import { TicketsController } from "./tickets.controller"; import { TicketsController } from "./tickets.controller";
@@ -17,8 +17,8 @@ import { MikroOrmModule } from "@mikro-orm/nestjs";
MikroOrmModule.forFeature([Ticket]), MikroOrmModule.forFeature([Ticket]),
BullModule.registerQueue({ name: TICKET.QUEUE_NAME }), BullModule.registerQueue({ name: TICKET.QUEUE_NAME }),
], ],
providers: [TicketsService, TicketRepository], providers: [TicketService, TicketRepository],
controllers: [TicketsController], controllers: [TicketsController],
exports: [TicketsService], exports: [TicketService],
}) })
export class TicketsModule { } export class TicketsModule { }