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: ['deletedAt'] })
@Index({ properties: ['createdAt'] }) @Index({ properties: ['createdAt'] })
export abstract class BaseEntity { export abstract class BaseEntity {
[OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'deletedAt'|'orderNumber'; [OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'deletedAt'|'orderNumber'|'children';
@PrimaryKey({ type: 'string', columnType: 'char(26)' }) @PrimaryKey({ type: 'string', columnType: 'char(26)' })
id: string = ulid(); 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'; import { Payment } from 'src/modules/payments/entities/payment.entity';
@Entity({ tableName: 'orders' }) @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 { export class Order extends BaseEntity {
@ManyToOne(() => User) @ManyToOne(() => User)
user!: User; user!: User;
@@ -56,7 +51,7 @@ export class Order extends BaseEntity {
@ManyToOne(() => PaymentMethod) @ManyToOne(() => PaymentMethod)
paymentMethod: PaymentMethod; paymentMethod: PaymentMethod;
@Property({ type: 'int', defaultRaw: `nextval('order_number_seq')` }) @Property({ type: 'int', autoincrement: true })
orderNumber!: number; orderNumber!: number;
@Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) @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 { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
@@ -24,4 +24,9 @@ export class CreateCategoryDto {
@Type(() => Number) @Type(() => Number)
@ApiPropertyOptional({ example: 1 }) @ApiPropertyOptional({ example: 1 })
order?: number; 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 { Product } from './product.entity';
import { BaseEntity } from '../../../common/entities/base.entity'; import { BaseEntity } from '../../../common/entities/base.entity';
import { Shop } from '../../shops/entities/shop.entity'; import { Shop } from '../../shops/entities/shop.entity';
@@ -10,6 +10,12 @@ export class Category extends BaseEntity {
@ManyToOne(() => Category, { nullable: true }) @ManyToOne(() => Category, { nullable: true })
parent?: Category parent?: Category
@OneToMany(() => Category, (cat) => cat.parent, {
cascade: [Cascade.ALL],
orphanRemoval: true
})
children: Category[]
@Property() @Property()
title!: string; title!: string;
@@ -20,11 +20,23 @@ export class CategoryService {
if (!shop) { if (!shop) {
throw new NotFoundException(RestMessage.NOT_FOUND); 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> = { const data: RequiredEntityData<Category> = {
title: dto.title, title: dto.title,
isActive: dto.isActive ?? true, isActive: dto.isActive ?? true,
shop: shop, shop: shop,
avatarUrl: dto.avatarUrl, avatarUrl: dto.avatarUrl,
order: dto.order,
parent: parent,
}; };
const category = this.categoryRepository.create(data); const category = this.categoryRepository.create(data);
@@ -32,23 +44,20 @@ export class CategoryService {
return category; return category;
} }
async findAllByShop(slug: string): Promise<Category[]> { async findAllByShop(shopSlug: string): Promise<Category[]> {
const shop = await this.em.findOne(Shop, { slug });
if (!shop || !shop.id) {
throw new NotFoundException(RestMessage.NOT_FOUND);
}
return this.categoryRepository.find( 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[]> { async findAllByShopId(shopId: string): Promise<Category[]> {
return this.categoryRepository.find( return this.categoryRepository.find(
{ shop: { id: shopId } }, { shop: { id: shopId } },
{ orderBy: { order: 'asc' } }); { orderBy: { order: 'asc' }, populate: ['parent'] });
} }
async findOne(shopId: string, id: string): Promise<Category> { 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); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND);
return { return {
@@ -57,17 +66,32 @@ export class CategoryService {
isActive: cat.isActive, isActive: cat.isActive,
shop: cat.shop, shop: cat.shop,
avatarUrl: cat.avatarUrl, avatarUrl: cat.avatarUrl,
order: cat.order,
parent: cat.parent,
createdAt: cat.createdAt, createdAt: cat.createdAt,
updatedAt: cat.updatedAt, updatedAt: cat.updatedAt,
products: cat.products.getItems().map(f => ({ id: f.id, title: f.title })), products: cat.products.getItems().map(f => ({ id: f.id, title: f.title })),
} as unknown as Category; } as unknown as Category;
} }
async update(shopId: string, id: string, dto: UpdateCategoryDto): Promise<Category> { async update(shopId: string, id: string, dto: UpdateCategoryDto): Promise<Category> {
const cat = await this.categoryRepository.findOne({ id, shop: { id: shopId } }); const cat = await this.categoryRepository.findOne({ id, shop: { id: shopId } });
if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); 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); await this.em.persistAndFlush(cat);
return cat; return cat;
} }