This commit is contained in:
@@ -4,6 +4,7 @@ import { FileInterceptor, type File } from '@nest-lab/fastify-multer';
|
||||
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
|
||||
import { UserService } from '../providers/user.service';
|
||||
import { UpdateUserDto } from '../dto/update-user.dto';
|
||||
import { CreateUserDto } from '../dto/create-user.dto';
|
||||
import { CreateUserAddressDto } from '../dto/create-user-address.dto';
|
||||
import { UpdateUserAddressDto } from '../dto/update-user-address.dto';
|
||||
import { UserId } from 'src/common/decorators/user-id.decorator';
|
||||
@@ -184,6 +185,36 @@ export class UsersController {
|
||||
return this.userService.findPaginated(restId, query);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Permissions(Permission.MANAGE_USERS)
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiOperation({ summary: 'Create a new user for the restaurant' })
|
||||
@ApiBody({ type: CreateUserDto })
|
||||
@Post('admin/users')
|
||||
async createUser(
|
||||
@RestId() restId: string,
|
||||
@Body(new ValidationPipe({ transform: true, whitelist: true })) dto: CreateUserDto,
|
||||
) {
|
||||
return this.userService.createUserForRestaurant(restId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Permissions(Permission.MANAGE_USERS)
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiOperation({ summary: 'Update a user (admin)' })
|
||||
@ApiParam({ name: 'userId', description: 'User ID' })
|
||||
@ApiBody({ type: UpdateUserDto })
|
||||
@Patch('admin/users/:userId')
|
||||
async adminUpdateUser(
|
||||
@Param('userId') userId: string,
|
||||
@RestId() restId: string,
|
||||
@Body(new ValidationPipe({ transform: true, whitelist: true })) dto: UpdateUserDto,
|
||||
) {
|
||||
return this.userService.adminUpdateUser(userId, restId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Permissions(Permission.MANAGE_USERS)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsMobilePhone, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@ApiProperty({ description: "User's phone number", example: '09121234567' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@IsMobilePhone('fa-IR')
|
||||
phone!: string;
|
||||
|
||||
@ApiProperty({ description: "User's first name", example: 'John' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
firstName!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: "User's last name", example: 'Doe' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
lastName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: "User's birth date (ISO)", example: '1990-01-01' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
birthDate?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: "User's marriage date (ISO)", example: '2015-06-01' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
marriageDate?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Gender flag (boolean)', example: true })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
gender?: boolean;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Avatar URL' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
avatarUrl?: string;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException, BadRequestException, ConflictException } from '@nestjs/common';
|
||||
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
||||
import type { File } from '@nest-lab/fastify-multer';
|
||||
import { User } from '../entities/user.entity';
|
||||
@@ -6,6 +6,7 @@ import { UserAddress } from '../entities/user-address.entity';
|
||||
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { UpdateUserDto } from '../dto/update-user.dto';
|
||||
import { CreateUserDto } from '../dto/create-user.dto';
|
||||
import { FindUsersDto } from '../dto/find-user.dto';
|
||||
import { FindWalletTransactionsDto } from '../dto/find-wallet-transactions.dto';
|
||||
import { CreateWalletTransactionDto } from '../dto/create-wallet-transaction.dto';
|
||||
@@ -23,7 +24,7 @@ import { UserRestaurantRepository } from '../repositories/user-restuarant.reposi
|
||||
import { UserRestaurant } from '../entities/user-restuarant.entity';
|
||||
import { ImportUsersResult } from '../interfaces/import-users.interface';
|
||||
import { assertExcelMimeType, parseUsersExcel } from '../utils/import-users-excel.util';
|
||||
import { UserImportMessage } from 'src/common/enums/message.enum';
|
||||
import { AuthMessage, UserImportMessage } from 'src/common/enums/message.enum';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
@@ -119,6 +120,91 @@ export class UserService {
|
||||
return user;
|
||||
}
|
||||
|
||||
async createUserForRestaurant(restId: string, dto: CreateUserDto): Promise<UserRestaurant> {
|
||||
const restaurant = await this.restaurantRepository.findOne({ id: restId });
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException(`Restaurant not found.`);
|
||||
}
|
||||
|
||||
const normalizedPhone = normalizePhone(dto.phone);
|
||||
let user = await this.userRepository.findOne({ phone: normalizedPhone });
|
||||
|
||||
if (user) {
|
||||
const existingLink = await this.userRestaurantRepository.findOne({ user, restaurant });
|
||||
if (existingLink) {
|
||||
throw new ConflictException(AuthMessage.PHONE_EXISTS);
|
||||
}
|
||||
|
||||
const updates: Partial<User> = { firstName: dto.firstName };
|
||||
if (dto.lastName !== undefined) {
|
||||
updates.lastName = dto.lastName;
|
||||
}
|
||||
if (dto.gender !== undefined) {
|
||||
updates.gender = dto.gender;
|
||||
}
|
||||
if (dto.avatarUrl !== undefined) {
|
||||
updates.avatarUrl = dto.avatarUrl;
|
||||
}
|
||||
if (dto.birthDate) {
|
||||
updates.birthDate = new Date(dto.birthDate);
|
||||
}
|
||||
if (dto.marriageDate) {
|
||||
updates.marriageDate = new Date(dto.marriageDate);
|
||||
}
|
||||
this.em.assign(user, updates);
|
||||
} else {
|
||||
const createData = {
|
||||
phone: normalizedPhone,
|
||||
firstName: dto.firstName,
|
||||
lastName: dto.lastName,
|
||||
gender: dto.gender,
|
||||
avatarUrl: dto.avatarUrl,
|
||||
birthDate: dto.birthDate ? new Date(dto.birthDate) : undefined,
|
||||
marriageDate: dto.marriageDate ? new Date(dto.marriageDate) : undefined,
|
||||
} as unknown as RequiredEntityData<User>;
|
||||
|
||||
user = this.userRepository.create(createData);
|
||||
|
||||
const points = Number(restaurant.score?.registerScore || 0);
|
||||
if (points > 0) {
|
||||
const newWalletTransaction = this.walletTransactionRepository.create({
|
||||
user,
|
||||
restaurant,
|
||||
balance: 0,
|
||||
amount: points,
|
||||
type: WalletTransactionType.CREDIT,
|
||||
reason: WalletTransactionReason.DEPOSIT,
|
||||
});
|
||||
this.em.persist(newWalletTransaction);
|
||||
}
|
||||
this.em.persist(user);
|
||||
}
|
||||
|
||||
const userRestaurant = this.userRestaurantRepository.create({
|
||||
user,
|
||||
restaurant,
|
||||
orderCount: 0,
|
||||
totalOrderAmount: 0,
|
||||
});
|
||||
this.em.persist(userRestaurant);
|
||||
await this.em.flush();
|
||||
|
||||
await this.em.populate(userRestaurant, ['user']);
|
||||
return userRestaurant;
|
||||
}
|
||||
|
||||
async adminUpdateUser(userId: string, restId: string, dto: UpdateUserDto): Promise<User> {
|
||||
const userRestaurant = await this.userRestaurantRepository.findOne({
|
||||
user: { id: userId },
|
||||
restaurant: { id: restId },
|
||||
});
|
||||
if (!userRestaurant) {
|
||||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||
}
|
||||
|
||||
return this.updateUser(userId, restId, dto);
|
||||
}
|
||||
|
||||
async updateUser(userId: string, restId: string, dto: UpdateUserDto): Promise<User> {
|
||||
const user = await this.userRepository.findOne({ id: userId });
|
||||
if (!user) {
|
||||
|
||||
Reference in New Issue
Block a user