From bec73658d582789f74fab752fabc71fe927386a2 Mon Sep 17 00:00:00 2001 From: mahyargdz Date: Mon, 14 Apr 2025 16:26:37 +0330 Subject: [PATCH] chore: add referral module --- src/app.module.ts | 2 + src/common/enums/message.enum.ts | 11 ++ src/configs/typeorm.config.ts | 2 +- .../blogs/controllers/blogs.controller.ts | 1 + .../discounts/entities/discount.entity.ts | 2 +- .../referrals/DTO/use-referral-code.dto.ts | 11 ++ .../entities/referral-code.entity.ts | 28 ++++ .../entities/referral-reward.entity.ts | 29 ++++ .../referrals/entities/referral.entity.ts | 28 ++++ .../enums/referral-reward-status.enum.ts | 5 + .../referrals/enums/referral-status.enum.ts | 5 + .../referrals/providers/referrals.service.ts | 128 ++++++++++++++++++ src/modules/referrals/referrals.controller.ts | 31 +++++ src/modules/referrals/referrals.module.ts | 20 +++ .../repositories/referral-codes.repository.ts | 12 ++ .../referral-rewards.repository.ts | 12 ++ .../repositories/referrals.repository.ts | 12 ++ src/modules/users/entities/user.entity.ts | 19 ++- src/modules/users/providers/users.service.ts | 5 +- src/modules/users/users.module.ts | 2 + .../wallets/providers/wallets.service.ts | 21 +++ 21 files changed, 378 insertions(+), 8 deletions(-) create mode 100644 src/modules/referrals/DTO/use-referral-code.dto.ts create mode 100644 src/modules/referrals/entities/referral-code.entity.ts create mode 100644 src/modules/referrals/entities/referral-reward.entity.ts create mode 100644 src/modules/referrals/entities/referral.entity.ts create mode 100644 src/modules/referrals/enums/referral-reward-status.enum.ts create mode 100644 src/modules/referrals/enums/referral-status.enum.ts create mode 100644 src/modules/referrals/providers/referrals.service.ts create mode 100644 src/modules/referrals/referrals.controller.ts create mode 100644 src/modules/referrals/referrals.module.ts create mode 100644 src/modules/referrals/repositories/referral-codes.repository.ts create mode 100644 src/modules/referrals/repositories/referral-rewards.repository.ts create mode 100644 src/modules/referrals/repositories/referrals.repository.ts diff --git a/src/app.module.ts b/src/app.module.ts index bc6f031..157a186 100755 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -31,6 +31,7 @@ import { LearningModule } from "./modules/learnings/learning.module"; // import { LoggerModule } from "./modules/logger/logger.module"; import { NotificationModule } from "./modules/notifications/notifications.module"; import { PaymentsModule } from "./modules/payments/payments.module"; +import { ReferralsModule } from "./modules/referrals/referrals.module"; import { SettingModule } from "./modules/settings/settings.module"; import { SubscriptionsModule } from "./modules/subscriptions/subscriptions.module"; import { TicketsModule } from "./modules/tickets/tickets.module"; @@ -70,6 +71,7 @@ import { WalletsModule } from "./modules/wallets/wallets.module"; BlogsModule, // LoggerModule, LandingModule, + ReferralsModule, ], controllers: [], }) diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 2698177..5064bbf 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -292,6 +292,7 @@ export const enum WalletMessage { SUBSCRIPTION_WALLET_TRANSFER = "پرداخت اشتراک از طریق کیف پول", INVOICE_WALLET_TRANSFER = "پرداخت صورت حساب از طریق کیف پول", TRANSACTION_NOT_FOUND = "تراکنش پیدا نشد", + REFERRAL_REWARD_WALLET_TRANSFER = "پرداخت پاداش ارجاع از طریق کیف پول", } export const enum PaymentMessage { @@ -689,3 +690,13 @@ export const enum SliderMessage { IS_ACTIVE_SHOULD_BE_A_BOOLEAN = "وضعیت فعال بودن اسلایدر باید یک بولین باشد", SLIDER_NOT_FOUND = "اسلایدر مورد نظر یافت نشد", } + +export const enum ReferralMessage { + CODE_EXIST_FOR_USER = "کد ارجاع برای این کاربر قبلاً وجود دارد", + CODE_GENERATION_FAILED = "تولید کد ارجاع منحصر به فرد پس از چندین تلاش ناموفق بود", + INVALID_REFERRAL_CODE = "کد ارجاع نامعتبر است", + REFERRAL_ALREADY_USED = "کد ارجاع قبلاً استفاده شده است", + REFERRAL_SUCCESS = "کد ارجاع با موفقیت اعمال شد", + INVALID_OR_INACTIVE_REFERRAL_CODE = "کد ارجاع نامعتبر یا غیرفعال است", + USER_ALREADY_REFERRED = "کاربر قبلاً ارجاع داده شده است", +} diff --git a/src/configs/typeorm.config.ts b/src/configs/typeorm.config.ts index 69adcce..8d8378f 100755 --- a/src/configs/typeorm.config.ts +++ b/src/configs/typeorm.config.ts @@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions { username: configService.getOrThrow("DB_USER"), password: configService.getOrThrow("DB_PASS"), autoLoadEntities: true, - synchronize: true, + synchronize: configService.getOrThrow("NODE_ENV") == "production" ? false : true, logging: configService.getOrThrow("NODE_ENV") == "production" ? false : true, migrationsTableName: "typeorm_migrations", migrationsRun: false, diff --git a/src/modules/blogs/controllers/blogs.controller.ts b/src/modules/blogs/controllers/blogs.controller.ts index 4dea3d5..edf8b62 100644 --- a/src/modules/blogs/controllers/blogs.controller.ts +++ b/src/modules/blogs/controllers/blogs.controller.ts @@ -149,6 +149,7 @@ export class BlogsController { } @ApiOperation({ summary: "Get all active categories (user side)" }) + @SkipAuth() @Get("categories/public") getCategoriesUserSide() { return this.blogsService.getCategoriesUserSide(); diff --git a/src/modules/discounts/entities/discount.entity.ts b/src/modules/discounts/entities/discount.entity.ts index ea982c1..d6cd924 100755 --- a/src/modules/discounts/entities/discount.entity.ts +++ b/src/modules/discounts/entities/discount.entity.ts @@ -21,7 +21,7 @@ export class Discount extends BaseEntity { @Column({ type: "enum", enum: DiscountCalculationType }) calculationType: DiscountCalculationType; - @Column({ type: "decimal", precision: 10, scale: 2, nullable: false, transformer: new DecimalTransformer() }) + @Column({ type: "decimal", precision: 16, scale: 2, nullable: false, transformer: new DecimalTransformer() }) amount: Decimal; @Column({ type: "boolean", default: true }) diff --git a/src/modules/referrals/DTO/use-referral-code.dto.ts b/src/modules/referrals/DTO/use-referral-code.dto.ts new file mode 100644 index 0000000..6ab2c45 --- /dev/null +++ b/src/modules/referrals/DTO/use-referral-code.dto.ts @@ -0,0 +1,11 @@ +import { IsNotEmpty, IsString } from "class-validator"; + +export class UseReferralCodeDto { + @IsNotEmpty() + @IsString() + referralCode: string; + + @IsNotEmpty() + @IsString() + userId: string; +} diff --git a/src/modules/referrals/entities/referral-code.entity.ts b/src/modules/referrals/entities/referral-code.entity.ts new file mode 100644 index 0000000..0fe8a6c --- /dev/null +++ b/src/modules/referrals/entities/referral-code.entity.ts @@ -0,0 +1,28 @@ +import { Column, Entity, JoinColumn, OneToMany, OneToOne } from "typeorm"; + +import { Referral } from "./referral.entity"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { User } from "../../users/entities/user.entity"; + +@Entity() +export class ReferralCode extends BaseEntity { + @Column({ type: "varchar", length: 6, unique: true }) + code: string; + + @Column({ type: "boolean", default: true }) + isActive: boolean; + + @Column({ type: "integer", default: 0 }) + usedCount: number; + + @Column({ type: "timestamptz", nullable: true }) + expiresAt: Date; + + //*********************************** */ + @OneToOne(() => User, (user) => user.referralCode) + @JoinColumn() + user: User; + + @OneToMany(() => Referral, (referral) => referral.referralCode) + referrals: Referral[]; +} diff --git a/src/modules/referrals/entities/referral-reward.entity.ts b/src/modules/referrals/entities/referral-reward.entity.ts new file mode 100644 index 0000000..21be6c0 --- /dev/null +++ b/src/modules/referrals/entities/referral-reward.entity.ts @@ -0,0 +1,29 @@ +import Decimal from "decimal.js"; +import { Column, Entity, ManyToOne } from "typeorm"; + +import { Referral } from "./referral.entity"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { DecimalTransformer } from "../../../common/transformers/decimal.transformer"; +import { User } from "../../users/entities/user.entity"; +import { ReferralRewardStatus } from "../enums/referral-reward-status.enum"; + +@Entity() +export class ReferralReward extends BaseEntity { + @ManyToOne(() => User, (user) => user.referralRewards) + user: User; + + @ManyToOne(() => Referral, (referral) => referral.rewards) + referral: Referral; + + @Column({ type: "decimal", precision: 10, scale: 2, nullable: false, transformer: new DecimalTransformer() }) + amount: Decimal; + + @Column({ type: "enum", enum: ReferralRewardStatus, default: ReferralRewardStatus.PENDING }) + status: ReferralRewardStatus; + + @Column({ type: "timestamp", nullable: true }) + paidAt: Date; + + @Column({ type: "varchar", length: 255, nullable: true }) + transactionId: string; +} diff --git a/src/modules/referrals/entities/referral.entity.ts b/src/modules/referrals/entities/referral.entity.ts new file mode 100644 index 0000000..480220c --- /dev/null +++ b/src/modules/referrals/entities/referral.entity.ts @@ -0,0 +1,28 @@ +import { Column, Entity, ManyToOne, OneToMany } from "typeorm"; + +import { ReferralCode } from "./referral-code.entity"; +import { ReferralReward } from "./referral-reward.entity"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { User } from "../../users/entities/user.entity"; +import { ReferralStatus } from "../enums/referral-status.enum"; + +@Entity() +export class Referral extends BaseEntity { + @ManyToOne(() => User, (user) => user.referrals) + referrer: User; + + @ManyToOne(() => User, (user) => user.referredBy) + referredUser: User; + + @ManyToOne(() => ReferralCode, (code) => code.referrals) + referralCode: ReferralCode; + + @OneToMany(() => ReferralReward, (reward) => reward.referral) + rewards: ReferralReward[]; + + @Column({ type: "enum", enum: ReferralStatus, default: ReferralStatus.PENDING }) + status: ReferralStatus; + + @Column({ type: "timestamptz", nullable: true }) + completedAt?: Date; +} diff --git a/src/modules/referrals/enums/referral-reward-status.enum.ts b/src/modules/referrals/enums/referral-reward-status.enum.ts new file mode 100644 index 0000000..f9bb646 --- /dev/null +++ b/src/modules/referrals/enums/referral-reward-status.enum.ts @@ -0,0 +1,5 @@ +export enum ReferralRewardStatus { + PENDING = "PENDING", + PAID = "PAID", + CANCELLED = "CANCELLED", +} diff --git a/src/modules/referrals/enums/referral-status.enum.ts b/src/modules/referrals/enums/referral-status.enum.ts new file mode 100644 index 0000000..4b972ec --- /dev/null +++ b/src/modules/referrals/enums/referral-status.enum.ts @@ -0,0 +1,5 @@ +export enum ReferralStatus { + PENDING = "PENDING", + COMPLETED = "COMPLETED", + CANCELLED = "CANCELLED", +} diff --git a/src/modules/referrals/providers/referrals.service.ts b/src/modules/referrals/providers/referrals.service.ts new file mode 100644 index 0000000..9edbcb7 --- /dev/null +++ b/src/modules/referrals/providers/referrals.service.ts @@ -0,0 +1,128 @@ +import { randomBytes } from "node:crypto"; + +import { BadRequestException, Injectable } from "@nestjs/common"; +import Decimal from "decimal.js"; +import { DataSource, QueryRunner } from "typeorm"; + +import { ReferralMessage, UserMessage } from "../../../common/enums/message.enum"; +import { User } from "../../users/entities/user.entity"; +import { WalletsService } from "../../wallets/providers/wallets.service"; +import { UseReferralCodeDto } from "../DTO/use-referral-code.dto"; +import { ReferralRewardStatus } from "../enums/referral-reward-status.enum"; +import { ReferralStatus } from "../enums/referral-status.enum"; +import { ReferralCodesRepository } from "../repositories/referral-codes.repository"; +import { ReferralRewardsRepository } from "../repositories/referral-rewards.repository"; +import { ReferralsRepository } from "../repositories/referrals.repository"; +@Injectable() +export class ReferralsService { + constructor( + private readonly referralsRepository: ReferralsRepository, + private readonly referralCodesRepository: ReferralCodesRepository, + private readonly referralRewardsRepository: ReferralRewardsRepository, + private readonly walletsService: WalletsService, + private readonly dataSource: DataSource, + ) {} + + async generateReferralCode(userId: string, queryRunner: QueryRunner) { + const existReferralCode = await queryRunner.manager.findOneBy(this.referralCodesRepository.target, { user: { id: userId } }); + + if (existReferralCode) throw new BadRequestException(ReferralMessage.CODE_EXIST_FOR_USER); + + const MAX_ATTEMPTS = 10; + let attempts = 0; + let code: string; + let existCode; + + do { + if (attempts >= MAX_ATTEMPTS) { + throw new BadRequestException(ReferralMessage.CODE_GENERATION_FAILED); + } + + code = this.generateRandomCode(); + existCode = await queryRunner.manager.findOneBy(this.referralCodesRepository.target, { code }); + attempts++; + } while (existCode); + + const referralCode = queryRunner.manager.create(this.referralCodesRepository.target, { + code, + user: { id: userId }, + }); + + await queryRunner.manager.save(referralCode); + return code; + } + + //*********************************** */ + async useReferralCode(dto: UseReferralCodeDto) { + const queryRunner = this.dataSource.createQueryRunner(); + + try { + await queryRunner.connect(); + await queryRunner.startTransaction(); + + const referredUser = await queryRunner.manager.findOne(User, { where: { id: dto.userId } }); + if (!referredUser) throw new BadRequestException(UserMessage.USER_NOT_FOUND); + + const referralCode = await queryRunner.manager.findOne(this.referralCodesRepository.target, { + where: { code: dto.referralCode, isActive: true }, + relations: { user: true }, + }); + if (!referralCode || !referralCode.user) throw new BadRequestException(ReferralMessage.INVALID_OR_INACTIVE_REFERRAL_CODE); + + // Check if user has already been referred + const existingReferral = await queryRunner.manager.findOne(this.referralsRepository.target, { + where: { referredUser: { id: referredUser.id } }, + }); + if (existingReferral) throw new BadRequestException(ReferralMessage.USER_ALREADY_REFERRED); + + // Create referral record + const referral = queryRunner.manager.create(this.referralsRepository.target, { + referrer: { id: referralCode.user.id }, + referredUser: { id: referredUser.id }, + referralCode: { id: referralCode.id }, + status: ReferralStatus.PENDING, + }); + + await queryRunner.manager.save(referral); + + // Increment usedCount + referralCode.usedCount += 1; + await queryRunner.manager.save(referralCode); + + // Create pending reward + const reward = queryRunner.manager.create(this.referralRewardsRepository.target, { + user: { id: referralCode.user.id }, + referral: { id: referral.id }, + amount: 100, // Set your reward amount here + status: ReferralRewardStatus.PENDING, + }); + + await queryRunner.manager.save(reward); + + // Charge the referred user's wallet + const chargeAmount = new Decimal(100); + await this.walletsService.createReferralRewardTransaction(referredUser.id, chargeAmount, queryRunner); + + // Update referral status to COMPLETED + referral.status = ReferralStatus.COMPLETED; + await queryRunner.manager.save(referral); + + // Update reward status to PAID + reward.status = ReferralRewardStatus.PAID; + reward.paidAt = new Date(); + await queryRunner.manager.save(reward); + + await queryRunner.commitTransaction(); + return { referral, reward }; + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } + } + + private generateRandomCode(): string { + return randomBytes(6).toString("hex"); + } +} diff --git a/src/modules/referrals/referrals.controller.ts b/src/modules/referrals/referrals.controller.ts new file mode 100644 index 0000000..fe75b1a --- /dev/null +++ b/src/modules/referrals/referrals.controller.ts @@ -0,0 +1,31 @@ +import { Body, Controller, Post } from "@nestjs/common"; +import { ApiOperation } from "@nestjs/swagger"; + +import { UseReferralCodeDto } from "./DTO/use-referral-code.dto"; +import { ReferralsService } from "./providers/referrals.service"; +import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; +// import { UserDec } from "../../common/decorators/user.decorator"; + +@Controller("referrals") +@AuthGuards() +export class ReferralsController { + constructor(private readonly referralsService: ReferralsService) {} + + @ApiOperation({ summary: "Use a referral code" }) + @Post("use-code") + useReferralCode(@Body() dto: UseReferralCodeDto) { + return this.referralsService.useReferralCode(dto); + } + + // @ApiOperation({ summary: "Get referral statistics for a user" }) + // @Get("stats/:userId") + // getReferralStats(@Param("userId") userId: string) { + // return this.referralsService.getReferralStats(userId); + // } + + // @Get("my-stats") + // @ApiOperation({ summary: "Get current user's referral statistics" }) + // getMyReferralStats(@UserDec("id") userId: string) { + // return this.referralsService.getReferralStats(userId); + // } +} diff --git a/src/modules/referrals/referrals.module.ts b/src/modules/referrals/referrals.module.ts new file mode 100644 index 0000000..711a94a --- /dev/null +++ b/src/modules/referrals/referrals.module.ts @@ -0,0 +1,20 @@ +import { Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; + +import { ReferralCode } from "./entities/referral-code.entity"; +import { ReferralReward } from "./entities/referral-reward.entity"; +import { Referral } from "./entities/referral.entity"; +import { ReferralsService } from "./providers/referrals.service"; +import { ReferralsController } from "./referrals.controller"; +import { WalletsModule } from "../wallets/wallets.module"; +import { ReferralCodesRepository } from "./repositories/referral-codes.repository"; +import { ReferralRewardsRepository } from "./repositories/referral-rewards.repository"; +import { ReferralsRepository } from "./repositories/referrals.repository"; + +@Module({ + imports: [TypeOrmModule.forFeature([Referral, ReferralReward, ReferralCode]), WalletsModule], + providers: [ReferralsService, ReferralsRepository, ReferralCodesRepository, ReferralRewardsRepository], + controllers: [ReferralsController], + exports: [ReferralsService], +}) +export class ReferralsModule {} diff --git a/src/modules/referrals/repositories/referral-codes.repository.ts b/src/modules/referrals/repositories/referral-codes.repository.ts new file mode 100644 index 0000000..7847a77 --- /dev/null +++ b/src/modules/referrals/repositories/referral-codes.repository.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; + +import { ReferralCode } from "../entities/referral-code.entity"; + +@Injectable() +export class ReferralCodesRepository extends Repository { + constructor(@InjectRepository(ReferralCode) repository: Repository) { + super(repository.target, repository.manager, repository.queryRunner); + } +} diff --git a/src/modules/referrals/repositories/referral-rewards.repository.ts b/src/modules/referrals/repositories/referral-rewards.repository.ts new file mode 100644 index 0000000..26f8171 --- /dev/null +++ b/src/modules/referrals/repositories/referral-rewards.repository.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; + +import { ReferralReward } from "../entities/referral-reward.entity"; + +@Injectable() +export class ReferralRewardsRepository extends Repository { + constructor(@InjectRepository(ReferralReward) repository: Repository) { + super(repository.target, repository.manager, repository.queryRunner); + } +} diff --git a/src/modules/referrals/repositories/referrals.repository.ts b/src/modules/referrals/repositories/referrals.repository.ts new file mode 100644 index 0000000..215e8b5 --- /dev/null +++ b/src/modules/referrals/repositories/referrals.repository.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; + +import { Referral } from "../entities/referral.entity"; + +@Injectable() +export class ReferralsRepository extends Repository { + constructor(@InjectRepository(Referral) repository: Repository) { + super(repository.target, repository.manager, repository.queryRunner); + } +} diff --git a/src/modules/users/entities/user.entity.ts b/src/modules/users/entities/user.entity.ts index f477f0a..4d84c2b 100755 --- a/src/modules/users/entities/user.entity.ts +++ b/src/modules/users/entities/user.entity.ts @@ -18,6 +18,9 @@ import { Invoice } from "../../invoices/entities/invoice.entity"; import { LearningProgress } from "../../learnings/entities/learning-progress.entity"; import { Notification } from "../../notifications/entities/notification.entity"; import { Payment } from "../../payments/entities/payment.entity"; +import { ReferralCode } from "../../referrals/entities/referral-code.entity"; +import { ReferralReward } from "../../referrals/entities/referral-reward.entity"; +import { Referral } from "../../referrals/entities/referral.entity"; import { UserSetting } from "../../settings/entities/user-setting.entity"; import { UserQuickAccess } from "../../subscriptions/entities/user-quick-access.entity"; import { UserSubscription } from "../../subscriptions/entities/user-subscription.entity"; @@ -132,8 +135,16 @@ export class User extends BaseEntity { @OneToMany(() => RefreshToken, (refreshToken) => refreshToken.user, { cascade: true }) refreshTokens: RefreshToken[]; -} -// @ManyToMany(() => DanakService, (danakService) => danakService.users) -// @JoinTable() -// danakServices: DanakService[]; + @OneToOne(() => ReferralCode, (code) => code.user) + referralCode: ReferralCode; + + @OneToMany(() => Referral, (referral) => referral.referrer) + referrals: Referral[]; + + @OneToMany(() => Referral, (referral) => referral.referredUser) + referredBy: Referral[]; + + @OneToMany(() => ReferralReward, (reward) => reward.user) + referralRewards: ReferralReward[]; +} diff --git a/src/modules/users/providers/users.service.ts b/src/modules/users/providers/users.service.ts index ba2ed9f..8a3849c 100755 --- a/src/modules/users/providers/users.service.ts +++ b/src/modules/users/providers/users.service.ts @@ -11,6 +11,7 @@ import { AddressService } from "../../address/providers/address.service"; import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto"; import { RequestOtpDto } from "../../auth/DTO/request-otp.dto"; import { VerifyOtpDto } from "../../auth/DTO/verify-otp.dto"; +import { ReferralsService } from "../../referrals/providers/referrals.service"; import { UserSettingsService } from "../../settings/providers/user-settings.service"; import { CacheService } from "../../utils/providers/cache.service"; import { EmailService } from "../../utils/providers/email.service"; @@ -30,7 +31,6 @@ import { LegalUserRepository } from "../repositories/legal-user.repository"; import { RealUserRepository } from "../repositories/real-user.repository"; import { UserGroupRepository } from "../repositories/user-group.repository"; import { UserRepository } from "../repositories/users.repository"; - @Injectable() export class UsersService { private readonly logger = new Logger(UsersService.name); @@ -47,6 +47,7 @@ export class UsersService { private readonly otpService: OTPService, private readonly smsService: SmsService, private readonly cacheService: CacheService, + private readonly referralsService: ReferralsService, ) {} /************************************************************ */ @@ -254,8 +255,8 @@ export class UsersService { await queryRunner.manager.save(user); await this.userSettingsService.createUserSettings(user.id, queryRunner); - await this.walletsService.createUserWallet(user.id, queryRunner); + await this.referralsService.generateReferralCode(user.id, queryRunner); return user; } diff --git a/src/modules/users/users.module.ts b/src/modules/users/users.module.ts index e370441..0be3cde 100755 --- a/src/modules/users/users.module.ts +++ b/src/modules/users/users.module.ts @@ -26,6 +26,7 @@ import { UtilsModule } from "../utils/utils.module"; import { AdminsService } from "./providers/admins.service"; import { CustomersService } from "./providers/customers.service"; import { RefreshTokensRepository } from "./repositories/refresh-token.repository"; +import { ReferralsModule } from "../referrals/referrals.module"; @Module({ imports: [ @@ -33,6 +34,7 @@ import { RefreshTokensRepository } from "./repositories/refresh-token.repository WalletsModule, UtilsModule, AddressModule, + ReferralsModule, ], providers: [ UsersService, diff --git a/src/modules/wallets/providers/wallets.service.ts b/src/modules/wallets/providers/wallets.service.ts index 2ff28a6..293b85f 100755 --- a/src/modules/wallets/providers/wallets.service.ts +++ b/src/modules/wallets/providers/wallets.service.ts @@ -111,6 +111,27 @@ export class WalletsService { return transaction; } + + //*********************************** */ + + async createReferralRewardTransaction(userId: string, amount: Decimal, queryRunner: QueryRunner) { + const wallet = await queryRunner.manager.findOne(Wallet, { where: { user: { id: userId } }, lock: { mode: "pessimistic_write" } }); + if (!wallet) throw new BadRequestException(WalletMessage.WALLET_NOT_FOUND); + + const transaction = queryRunner.manager.create(WalletTransaction, { + amount, + wallet: { id: wallet.id }, + type: TransactionType.DEPOSIT, + description: WalletMessage.REFERRAL_REWARD_WALLET_TRANSFER, + }); + + wallet.balance = new Decimal(wallet.balance).add(transaction.amount); + + await queryRunner.manager.save(Wallet, wallet); + await queryRunner.manager.save(WalletTransaction, transaction); + + return transaction; + } //*********************************** */ async getTransaction(userId: string, paginationDto: PaginationDto) {