This commit is contained in:
2025-12-13 10:04:13 +03:30
parent c5c71491a4
commit 85b4e5bb6a
6 changed files with 101 additions and 5 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common';
import { Module, forwardRef } from '@nestjs/common';
import { AdminService } from './providers/admin.service';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { Admin } from './entities/admin.entity';
@@ -19,7 +19,7 @@ import { AdminRole } from './entities/adminRole.entity';
MikroOrmModule.forFeature([Admin, Role, Permission, RolePermission, AdminRole]),
JwtModule,
UtilsModule,
RestaurantsModule,
forwardRef(() => RestaurantsModule),
],
exports: [AdminService, AdminRepository],
})
@@ -28,6 +28,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
server!: Server;
private readonly logger = new Logger(NotificationsGateway.name);
private readonly pingIntervals = new Map<string, NodeJS.Timeout>();
constructor(
@Inject(forwardRef(() => NotificationService))
@@ -41,6 +42,13 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
const authenticatedClient = client as AuthenticatedSocket;
this.logger.log(`Admin connected: ${authenticatedClient.adminId}`);
this.handleJoinRoom(authenticatedClient);
// Start ping interval for this client
// const intervalId = setInterval(() => {
// void authenticatedClient.emit('ping', { message: 'ping', timestamp: new Date().toISOString() });
// }, 2000);
// this.pingIntervals.set(client.id, intervalId);
} catch (error) {
this.logger.error(
`Connection authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
@@ -53,6 +61,13 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
handleDisconnect(client: Socket) {
this.logger.log(`Client disconnected: ${client.id}`);
this.handleLeaveRoom(client);
// Clear ping interval for this client
// const intervalId = this.pingIntervals.get(client.id);
// if (intervalId) {
// clearInterval(intervalId);
// this.pingIntervals.delete(client.id);
// }
}
sendInAppNotification(repipient: { adminId: string; restaurantId: string }, payload: IInAppNotificationPayload) {
@@ -65,7 +80,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
private getRoom(adminId: string, restaurantId: string) {
return `restaurant:${restaurantId}-admin:${adminId}`;
}
handleLeaveRoom(client: AuthenticatedSocket) {
const room = this.getRoom(client.adminId!, client.restId!);
void client.leave(room);
@@ -9,6 +9,7 @@ import { UserId } from 'src/common/decorators/user-id.decorator';
import { RestId } from 'src/common/decorators';
import { FindUsersDto } from '../dto/find-user.dto';
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
import { ConvertScoreToWalletDto } from '../dto/convert-score-to-wallet.dto';
@ApiTags('User')
@Controller()
@@ -117,4 +118,19 @@ export class UsersController {
) {
return this.userService.findAll(restId, query);
}
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Convert user score (points) to wallet balance' })
@ApiBody({ type: ConvertScoreToWalletDto })
@Post('public/user/convert-score-to-wallet')
async convertScoreToWallet(@UserId() userId: string, @Body() dto: ConvertScoreToWalletDto, @RestId() restId: string) {
const user = await this.userService.convertScoreToWallet(userId, restId, dto);
return {
id: user.id,
wallet: user.wallet,
points: user.points,
message: 'Score converted to wallet successfully',
};
}
}
@@ -0,0 +1,17 @@
import { IsNumber, IsOptional, Min } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
/**
* DTO for converting user score (points) to wallet balance.
*/
export class ConvertScoreToWalletDto {
@ApiPropertyOptional({
description: 'Amount of points to convert. If not provided, converts all available points',
example: 100,
minimum: 1,
})
@IsOptional()
@IsNumber()
@Min(1)
points?: number;
}
+48 -1
View File
@@ -1,4 +1,4 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
import { User } from '../entities/user.entity';
import { UserAddress } from '../entities/user-address.entity';
@@ -10,11 +10,14 @@ import { UpdateUserAddressDto } from '../dto/update-user-address.dto';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
import { UserRepository } from '../repositories/user.repository';
import { normalizePhone } from '../../utils/phone.util';
import { ConvertScoreToWalletDto } from '../dto/convert-score-to-wallet.dto';
import { RestRepository } from 'src/modules/restaurants/repositories/rest.repository';
@Injectable()
export class UserService {
constructor(
private readonly userRepository: UserRepository,
private readonly restaurantRepository: RestRepository,
private readonly em: EntityManager,
) {}
@@ -283,4 +286,48 @@ export class UserService {
return address;
}
async convertScoreToWallet(userId: string, restId: string, dto: ConvertScoreToWalletDto): Promise<User> {
const user = await this.userRepository.findOne({ id: userId });
if (!user) {
throw new NotFoundException(`User with ID ${userId} not found.`);
}
// Determine how many points to convert
const pointsToConvert = dto.points ?? user.points;
// Validate points to convert
if (pointsToConvert <= 0) {
throw new BadRequestException('Points to convert must be greater than 0.');
}
if (user.points < pointsToConvert) {
throw new BadRequestException(`Insufficient points. Available: ${user.points}, Requested: ${pointsToConvert}.`);
}
const restaurant = await this.restaurantRepository.findOne({ id: restId });
if (!restaurant) {
throw new NotFoundException(`Restaurant with ID ${restId} not found.`);
}
if (!restaurant.score) {
throw new BadRequestException('Restaurant score not found.');
}
if (!restaurant.score.purchaseScore) {
throw new BadRequestException('Restaurant purchase score not found.');
}
if (!restaurant.score.purchaseAmount) {
throw new BadRequestException('Restaurant purchase amount not found.');
}
const walletIncreaseAmount =
(Number(pointsToConvert) * Number(restaurant.score.purchaseAmount)) / Number(restaurant.score.purchaseScore);
// Convert points to wallet (1:1 ratio - can be made configurable)
// Update user's wallet and points
user.wallet = (user.wallet || 0) + walletIncreaseAmount;
user.points = user.points - pointsToConvert;
await this.em.flush();
return user;
}
}
+2 -1
View File
@@ -6,11 +6,12 @@ import { User } from './entities/user.entity';
import { UserAddress } from './entities/user-address.entity';
import { JwtModule } from '@nestjs/jwt';
import { UserRepository } from './repositories/user.repository';
import { RestaurantsModule } from '../restaurants/restaurants.module';
@Module({
providers: [UserService, UserRepository],
controllers: [UsersController],
imports: [MikroOrmModule.forFeature([User, UserAddress]), JwtModule],
imports: [MikroOrmModule.forFeature([User, UserAddress]), JwtModule, RestaurantsModule],
exports: [UserService, UserRepository],
})
export class UserModule {}