From 7a5b3f0bea1144f10bfc6d854e99f646c02a6348 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 28 Jun 2026 20:08:27 +0330 Subject: [PATCH] module customers --- src/app.module.ts | 2 + src/customers/customers.controller.ts | 46 ++++++++++++ src/customers/customers.module.ts | 14 ++++ src/customers/customers.service.ts | 86 +++++++++++++++++++++++ src/customers/dto/create-customer.dto.ts | 30 ++++++++ src/customers/dto/update-customer.dto.ts | 32 +++++++++ src/customers/entities/customer.entity.ts | 35 +++++++++ src/users/dto/update-user.dto.ts | 6 +- src/users/users.service.ts | 3 + 9 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 src/customers/customers.controller.ts create mode 100644 src/customers/customers.module.ts create mode 100644 src/customers/customers.service.ts create mode 100644 src/customers/dto/create-customer.dto.ts create mode 100644 src/customers/dto/update-customer.dto.ts create mode 100644 src/customers/entities/customer.entity.ts diff --git a/src/app.module.ts b/src/app.module.ts index f6b95d5..18caba8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { CustomersModule } from './customers/customers.module'; import { SalonsModule } from './salons/salons.module'; import { UsersModule } from './users/users.module'; @@ -19,6 +20,7 @@ import { UsersModule } from './users/users.module'; }), UsersModule, SalonsModule, + CustomersModule, ], controllers: [], providers: [], diff --git a/src/customers/customers.controller.ts b/src/customers/customers.controller.ts new file mode 100644 index 0000000..e688d6c --- /dev/null +++ b/src/customers/customers.controller.ts @@ -0,0 +1,46 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseUUIDPipe, + Patch, + Post, +} from '@nestjs/common'; +import { CustomersService } from './customers.service'; +import { CreateCustomerDto } from './dto/create-customer.dto'; +import { UpdateCustomerDto } from './dto/update-customer.dto'; + +@Controller('customers') +export class CustomersController { + constructor(private readonly customersService: CustomersService) {} + + @Post() + create(@Body() createCustomerDto: CreateCustomerDto) { + return this.customersService.create(createCustomerDto); + } + + @Get() + findAll() { + return this.customersService.findAll(); + } + + @Get(':id') + findOne(@Param('id', ParseUUIDPipe) id: string) { + return this.customersService.findOne(id); + } + + @Patch(':id') + update( + @Param('id', ParseUUIDPipe) id: string, + @Body() updateCustomerDto: UpdateCustomerDto, + ) { + return this.customersService.update(id, updateCustomerDto); + } + + @Delete(':id') + remove(@Param('id', ParseUUIDPipe) id: string) { + return this.customersService.remove(id); + } +} diff --git a/src/customers/customers.module.ts b/src/customers/customers.module.ts new file mode 100644 index 0000000..646b819 --- /dev/null +++ b/src/customers/customers.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UsersModule } from '../users/users.module'; +import { CustomersController } from './customers.controller'; +import { CustomersService } from './customers.service'; +import { Customer } from './entities/customer.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([Customer]), UsersModule], + controllers: [CustomersController], + providers: [CustomersService], + exports: [CustomersService], +}) +export class CustomersModule {} diff --git a/src/customers/customers.service.ts b/src/customers/customers.service.ts new file mode 100644 index 0000000..440d41c --- /dev/null +++ b/src/customers/customers.service.ts @@ -0,0 +1,86 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { UserRole } from '../users/enums/user-role.enum'; +import { UsersService } from '../users/users.service'; +import { CreateCustomerDto } from './dto/create-customer.dto'; +import { UpdateCustomerDto } from './dto/update-customer.dto'; +import { Customer } from './entities/customer.entity'; + +@Injectable() +export class CustomersService { + constructor( + @InjectRepository(Customer) + private readonly customersRepository: Repository, + private readonly usersService: UsersService, + ) {} + + async create(createCustomerDto: CreateCustomerDto): Promise { + const user = await this.usersService.create({ + firstName: createCustomerDto.firstName, + lastName: createCustomerDto.lastName, + mobile: createCustomerDto.mobile, + role: UserRole.CUSTOMER, + }); + + const customer = this.customersRepository.create({ + userId: user.id, + dateOfBirth: createCustomerDto.dateOfBirth, + address: createCustomerDto.address, + }); + + const saved = await this.customersRepository.save(customer); + return this.findOne(saved.id); + } + + findAll(): Promise { + return this.customersRepository.find({ relations: { user: true } }); + } + + async findOne(id: string): Promise { + const customer = await this.customersRepository.findOne({ + where: { id }, + relations: { user: true }, + }); + if (!customer) { + throw new NotFoundException(`Customer #${id} not found`); + } + return customer; + } + + async update( + id: string, + updateCustomerDto: UpdateCustomerDto, + ): Promise { + const customer = await this.findOne(id); + const { firstName, lastName, mobile, dateOfBirth, address } = + updateCustomerDto; + + if ( + firstName !== undefined || + lastName !== undefined || + mobile !== undefined + ) { + await this.usersService.update(customer.userId, { + firstName, + lastName, + mobile, + }); + } + + if (dateOfBirth !== undefined) { + customer.dateOfBirth = dateOfBirth; + } + if (address !== undefined) { + customer.address = address; + } + + await this.customersRepository.save(customer); + return this.findOne(customer.id); + } + + async remove(id: string): Promise { + const customer = await this.findOne(id); + await this.usersService.remove(customer.userId); + } +} diff --git a/src/customers/dto/create-customer.dto.ts b/src/customers/dto/create-customer.dto.ts new file mode 100644 index 0000000..f690c08 --- /dev/null +++ b/src/customers/dto/create-customer.dto.ts @@ -0,0 +1,30 @@ +import { + IsDateString, + IsNotEmpty, + IsString, + Length, + Matches, +} from 'class-validator'; + +export class CreateCustomerDto { + @IsString() + @Length(1, 100) + @IsNotEmpty() + firstName: string; + + @IsString() + @Length(1, 100) + @IsNotEmpty() + lastName: string; + + @Matches(/^09\d{9}$/) + mobile: string; + + @IsDateString() + dateOfBirth: string; + + @IsString() + @Length(1, 500) + @IsNotEmpty() + address: string; +} diff --git a/src/customers/dto/update-customer.dto.ts b/src/customers/dto/update-customer.dto.ts new file mode 100644 index 0000000..de9bf9b --- /dev/null +++ b/src/customers/dto/update-customer.dto.ts @@ -0,0 +1,32 @@ +import { + IsDateString, + IsOptional, + IsString, + Length, + Matches, +} from 'class-validator'; + +export class UpdateCustomerDto { + @IsOptional() + @IsString() + @Length(1, 100) + firstName?: string; + + @IsOptional() + @IsString() + @Length(1, 100) + lastName?: string; + + @IsOptional() + @Matches(/^09\d{9}$/) + mobile?: string; + + @IsOptional() + @IsDateString() + dateOfBirth?: string; + + @IsOptional() + @IsString() + @Length(1, 500) + address?: string; +} diff --git a/src/customers/entities/customer.entity.ts b/src/customers/entities/customer.entity.ts new file mode 100644 index 0000000..6564c3d --- /dev/null +++ b/src/customers/entities/customer.entity.ts @@ -0,0 +1,35 @@ +import { + Column, + CreateDateColumn, + Entity, + JoinColumn, + OneToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { User } from '../../users/entities/user.entity'; + +@Entity('customers') +export class Customer { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ unique: true }) + userId: string; + + @OneToOne(() => User, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'userId' }) + user: User; + + @Column({ type: 'date' }) + dateOfBirth: string; + + @Column({ length: 500 }) + address: string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/users/dto/update-user.dto.ts b/src/users/dto/update-user.dto.ts index 357b929..339c385 100644 --- a/src/users/dto/update-user.dto.ts +++ b/src/users/dto/update-user.dto.ts @@ -1,4 +1,4 @@ -import { IsOptional, IsString, Length } from 'class-validator'; +import { IsOptional, IsString, Length, Matches } from 'class-validator'; export class UpdateUserDto { @IsOptional() @@ -10,4 +10,8 @@ export class UpdateUserDto { @IsString() @Length(1, 100) lastName?: string; + + @IsOptional() + @Matches(/^09\d{9}$/) + mobile?: string; } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index dbe4908..17ca5fa 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -36,6 +36,9 @@ export class UsersService { async update(id: string, updateUserDto: UpdateUserDto): Promise { const user = await this.findOne(id); + if (updateUserDto.mobile) { + await this.ensureMobileIsUnique(updateUserDto.mobile, id); + } Object.assign(user, updateUserDto); return this.usersRepository.save(user); }