update
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module, forwardRef } from '@nestjs/common';
|
||||||
import { AdminService } from './providers/admin.service';
|
import { AdminService } from './providers/admin.service';
|
||||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||||
import { Admin } from './entities/admin.entity';
|
import { Admin } from './entities/admin.entity';
|
||||||
@@ -19,7 +19,7 @@ import { AdminRole } from './entities/adminRole.entity';
|
|||||||
MikroOrmModule.forFeature([Admin, Role, Permission, RolePermission, AdminRole]),
|
MikroOrmModule.forFeature([Admin, Role, Permission, RolePermission, AdminRole]),
|
||||||
JwtModule,
|
JwtModule,
|
||||||
UtilsModule,
|
UtilsModule,
|
||||||
RestaurantsModule,
|
forwardRef(() => RestaurantsModule),
|
||||||
],
|
],
|
||||||
exports: [AdminService, AdminRepository],
|
exports: [AdminService, AdminRepository],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
|||||||
server!: Server;
|
server!: Server;
|
||||||
|
|
||||||
private readonly logger = new Logger(NotificationsGateway.name);
|
private readonly logger = new Logger(NotificationsGateway.name);
|
||||||
|
private readonly pingIntervals = new Map<string, NodeJS.Timeout>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => NotificationService))
|
@Inject(forwardRef(() => NotificationService))
|
||||||
@@ -41,6 +42,13 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
|||||||
const authenticatedClient = client as AuthenticatedSocket;
|
const authenticatedClient = client as AuthenticatedSocket;
|
||||||
this.logger.log(`Admin connected: ${authenticatedClient.adminId}`);
|
this.logger.log(`Admin connected: ${authenticatedClient.adminId}`);
|
||||||
this.handleJoinRoom(authenticatedClient);
|
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) {
|
} catch (error) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`Connection authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
`Connection authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||||
@@ -53,6 +61,13 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
|||||||
handleDisconnect(client: Socket) {
|
handleDisconnect(client: Socket) {
|
||||||
this.logger.log(`Client disconnected: ${client.id}`);
|
this.logger.log(`Client disconnected: ${client.id}`);
|
||||||
this.handleLeaveRoom(client);
|
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) {
|
sendInAppNotification(repipient: { adminId: string; restaurantId: string }, payload: IInAppNotificationPayload) {
|
||||||
@@ -65,7 +80,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
|||||||
private getRoom(adminId: string, restaurantId: string) {
|
private getRoom(adminId: string, restaurantId: string) {
|
||||||
return `restaurant:${restaurantId}-admin:${adminId}`;
|
return `restaurant:${restaurantId}-admin:${adminId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeaveRoom(client: AuthenticatedSocket) {
|
handleLeaveRoom(client: AuthenticatedSocket) {
|
||||||
const room = this.getRoom(client.adminId!, client.restId!);
|
const room = this.getRoom(client.adminId!, client.restId!);
|
||||||
void client.leave(room);
|
void client.leave(room);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { UserId } from 'src/common/decorators/user-id.decorator';
|
|||||||
import { RestId } from 'src/common/decorators';
|
import { RestId } from 'src/common/decorators';
|
||||||
import { FindUsersDto } from '../dto/find-user.dto';
|
import { FindUsersDto } from '../dto/find-user.dto';
|
||||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||||
|
import { ConvertScoreToWalletDto } from '../dto/convert-score-to-wallet.dto';
|
||||||
|
|
||||||
@ApiTags('User')
|
@ApiTags('User')
|
||||||
@Controller()
|
@Controller()
|
||||||
@@ -117,4 +118,19 @@ export class UsersController {
|
|||||||
) {
|
) {
|
||||||
return this.userService.findAll(restId, query);
|
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;
|
||||||
|
}
|
||||||
@@ -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 { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { User } from '../entities/user.entity';
|
import { User } from '../entities/user.entity';
|
||||||
import { UserAddress } from '../entities/user-address.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 { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
import { UserRepository } from '../repositories/user.repository';
|
import { UserRepository } from '../repositories/user.repository';
|
||||||
import { normalizePhone } from '../../utils/phone.util';
|
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()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly userRepository: UserRepository,
|
private readonly userRepository: UserRepository,
|
||||||
|
private readonly restaurantRepository: RestRepository,
|
||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -283,4 +286,48 @@ export class UserService {
|
|||||||
|
|
||||||
return address;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import { User } from './entities/user.entity';
|
|||||||
import { UserAddress } from './entities/user-address.entity';
|
import { UserAddress } from './entities/user-address.entity';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { UserRepository } from './repositories/user.repository';
|
import { UserRepository } from './repositories/user.repository';
|
||||||
|
import { RestaurantsModule } from '../restaurants/restaurants.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [UserService, UserRepository],
|
providers: [UserService, UserRepository],
|
||||||
controllers: [UsersController],
|
controllers: [UsersController],
|
||||||
imports: [MikroOrmModule.forFeature([User, UserAddress]), JwtModule],
|
imports: [MikroOrmModule.forFeature([User, UserAddress]), JwtModule, RestaurantsModule],
|
||||||
exports: [UserService, UserRepository],
|
exports: [UserService, UserRepository],
|
||||||
})
|
})
|
||||||
export class UserModule {}
|
export class UserModule {}
|
||||||
|
|||||||
Reference in New Issue
Block a user