import { randomBytes } from "node:crypto"; import { BadRequestException, Injectable } from "@nestjs/common"; import Decimal from "decimal.js"; import { QueryRunner } from "typeorm"; import { ReferralMessage, UserMessage } from "../../../common/enums/message.enum"; import { User } from "../../users/entities/user.entity"; import { UseReferralCodeDto } from "../DTO/use-referral-code.dto"; 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 { private MAX_ATTEMPTS = 10; constructor( private readonly referralsRepository: ReferralsRepository, private readonly referralCodesRepository: ReferralCodesRepository, private readonly referralRewardsRepository: ReferralRewardsRepository, ) {} 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); let attempts = 0; let code: string; let existCode; do { if (attempts >= this.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, queryRunner: QueryRunner) { 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); const existingReferral = await queryRunner.manager.findOne(this.referralsRepository.target, { where: { referredUser: { id: referredUser.id } }, }); if (existingReferral) throw new BadRequestException(ReferralMessage.USER_ALREADY_REFERRED); const referral = queryRunner.manager.create(this.referralsRepository.target, { referrer: { id: referralCode.user.id }, referredUser: { id: referredUser.id }, referralCode: { id: referralCode.id }, }); await queryRunner.manager.save(referral); referralCode.usedCount += 1; await queryRunner.manager.save(referralCode); // const reward = queryRunner.manager.create(this.referralRewardsRepository.target, { // user: { id: referralCode.user.id }, // referral: { id: referral.id }, // }); // await queryRunner.manager.save(reward); return referral; } //*********************************** */ async getPendingReferral(userId: string, queryRunner: QueryRunner) { return queryRunner.manager.findOne(this.referralsRepository.target, { where: { referredUser: { id: userId }, status: ReferralStatus.PENDING, }, relations: { referrer: true }, }); } //*********************************** */ async createReferral(userId: string, queryRunner: QueryRunner) { const referral = queryRunner.manager.create(this.referralsRepository.target, { referrer: { id: userId }, referredUser: { id: userId }, }); await queryRunner.manager.save(referral); } //*********************************** */ async createReferralReward(userId: string, referralId: string, amount: Decimal, queryRunner: QueryRunner) { const reward = queryRunner.manager.create(this.referralRewardsRepository.target, { user: { id: userId }, referral: { id: referralId }, amount, }); await queryRunner.manager.save(reward); return reward; } //*********************************** */ private generateRandomCode(): string { return randomBytes(6).toString("hex"); } }