This commit is contained in:
2026-02-09 12:17:11 +03:30
parent e5f448fa24
commit ef53604d91
5 changed files with 49 additions and 19 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ import { ulid } from 'ulid';
@Index({ properties: ['deletedAt'] })
@Index({ properties: ['createdAt'] })
export abstract class BaseEntity {
[OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'deletedAt'|'orderNumber';
[OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'deletedAt'|'orderNumber'|'children';
@PrimaryKey({ type: 'string', columnType: 'char(26)' })
id: string = ulid();
+1 -6
View File
@@ -20,11 +20,6 @@ import { OrderUserAddress, OrderCarAddress } from '../interface/order.interface'
import { Payment } from 'src/modules/payments/entities/payment.entity';
@Entity({ tableName: 'orders' })
@Unique({ properties: ['shop', 'orderNumber'] })
@Index({ properties: ['shop', 'status'] })
@Index({ properties: ['user', 'status'] })
@Index({ properties: ['shop', 'orderNumber'] })
@Index({ properties: ['status'] })
export class Order extends BaseEntity {
@ManyToOne(() => User)
user!: User;
@@ -56,7 +51,7 @@ export class Order extends BaseEntity {
@ManyToOne(() => PaymentMethod)
paymentMethod: PaymentMethod;
@Property({ type: 'int', defaultRaw: `nextval('order_number_seq')` })
@Property({ type: 'int', autoincrement: true })
orderNumber!: number;
@Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
@@ -1,4 +1,4 @@
import { IsString, IsOptional, IsBoolean, IsInt, Min } from 'class-validator';
import { IsString, IsOptional, IsBoolean, IsInt, Min, IsUUID } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
@@ -24,4 +24,9 @@ export class CreateCategoryDto {
@Type(() => Number)
@ApiPropertyOptional({ example: 1 })
order?: number;
@IsOptional()
@IsUUID()
@ApiPropertyOptional({ example: '123e4567-e89b-12d3-a456-426614174000' })
parentId?: string;
}
@@ -1,4 +1,4 @@
import { Entity, Index, Property, Collection, OneToMany, ManyToOne } from '@mikro-orm/core';
import { Entity, Index, Property, Collection, OneToMany, ManyToOne, Cascade } from '@mikro-orm/core';
import { Product } from './product.entity';
import { BaseEntity } from '../../../common/entities/base.entity';
import { Shop } from '../../shops/entities/shop.entity';
@@ -10,6 +10,12 @@ export class Category extends BaseEntity {
@ManyToOne(() => Category, { nullable: true })
parent?: Category
@OneToMany(() => Category, (cat) => cat.parent, {
cascade: [Cascade.ALL],
orphanRemoval: true
})
children: Category[]
@Property()
title!: string;
@@ -20,11 +20,23 @@ export class CategoryService {
if (!shop) {
throw new NotFoundException(RestMessage.NOT_FOUND);
}
let parent: Category | undefined;
if (dto.parentId) {
const foundParent = await this.categoryRepository.findOne({ id: dto.parentId, shop: { id: shopId } });
if (!foundParent) {
throw new NotFoundException(CategoryMessage.NOT_FOUND);
}
parent = foundParent;
}
const data: RequiredEntityData<Category> = {
title: dto.title,
isActive: dto.isActive ?? true,
shop: shop,
avatarUrl: dto.avatarUrl,
order: dto.order,
parent: parent,
};
const category = this.categoryRepository.create(data);
@@ -32,23 +44,20 @@ export class CategoryService {
return category;
}
async findAllByShop(slug: string): Promise<Category[]> {
const shop = await this.em.findOne(Shop, { slug });
if (!shop || !shop.id) {
throw new NotFoundException(RestMessage.NOT_FOUND);
}
async findAllByShop(shopSlug: string): Promise<Category[]> {
return this.categoryRepository.find(
{ shop: shop, isActive: true }, { orderBy: { order: 'ASC' } });
{ shop: { slug: shopSlug }, isActive: true, parent: null },
{ orderBy: { order: 'ASC' }, populate: ['parent'] });
}
async findAllByShopId(shopId: string): Promise<Category[]> {
return this.categoryRepository.find(
{ shop: { id: shopId } },
{ orderBy: { order: 'asc' } });
{ orderBy: { order: 'asc' }, populate: ['parent'] });
}
async findOne(shopId: string, id: string): Promise<Category> {
const cat = await this.categoryRepository.findOne({ id, shop: { id: shopId } }, { populate: ['products'] });
const cat = await this.categoryRepository.findOne({ id, shop: { id: shopId } }, { populate: ['products', 'parent'] });
if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND);
return {
@@ -57,17 +66,32 @@ export class CategoryService {
isActive: cat.isActive,
shop: cat.shop,
avatarUrl: cat.avatarUrl,
order: cat.order,
parent: cat.parent,
createdAt: cat.createdAt,
updatedAt: cat.updatedAt,
products: cat.products.getItems().map(f => ({ id: f.id, title: f.title })),
} as unknown as Category;
}
async update(shopId: string, id: string, dto: UpdateCategoryDto): Promise<Category> {
const cat = await this.categoryRepository.findOne({ id, shop: { id: shopId } });
if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND);
this.em.assign(cat, dto);
// Handle parent relationship update
if (dto.parentId !== undefined) {
if (dto.parentId) {
const parent = await this.categoryRepository.findOne({ id: dto.parentId, shop: { id: shopId } });
if (!parent) {
throw new NotFoundException(CategoryMessage.NOT_FOUND);
}
cat.parent = parent;
} else {
cat.parent = undefined;
}
}
this.em.assign(cat, { ...dto, parentId: undefined }); // Exclude parentId from direct assignment
await this.em.persistAndFlush(cat);
return cat;
}