chore: add referral module
This commit is contained in:
@@ -31,6 +31,7 @@ import { LearningModule } from "./modules/learnings/learning.module";
|
|||||||
// import { LoggerModule } from "./modules/logger/logger.module";
|
// import { LoggerModule } from "./modules/logger/logger.module";
|
||||||
import { NotificationModule } from "./modules/notifications/notifications.module";
|
import { NotificationModule } from "./modules/notifications/notifications.module";
|
||||||
import { PaymentsModule } from "./modules/payments/payments.module";
|
import { PaymentsModule } from "./modules/payments/payments.module";
|
||||||
|
import { ReferralsModule } from "./modules/referrals/referrals.module";
|
||||||
import { SettingModule } from "./modules/settings/settings.module";
|
import { SettingModule } from "./modules/settings/settings.module";
|
||||||
import { SubscriptionsModule } from "./modules/subscriptions/subscriptions.module";
|
import { SubscriptionsModule } from "./modules/subscriptions/subscriptions.module";
|
||||||
import { TicketsModule } from "./modules/tickets/tickets.module";
|
import { TicketsModule } from "./modules/tickets/tickets.module";
|
||||||
@@ -70,6 +71,7 @@ import { WalletsModule } from "./modules/wallets/wallets.module";
|
|||||||
BlogsModule,
|
BlogsModule,
|
||||||
// LoggerModule,
|
// LoggerModule,
|
||||||
LandingModule,
|
LandingModule,
|
||||||
|
ReferralsModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ export const enum WalletMessage {
|
|||||||
SUBSCRIPTION_WALLET_TRANSFER = "پرداخت اشتراک از طریق کیف پول",
|
SUBSCRIPTION_WALLET_TRANSFER = "پرداخت اشتراک از طریق کیف پول",
|
||||||
INVOICE_WALLET_TRANSFER = "پرداخت صورت حساب از طریق کیف پول",
|
INVOICE_WALLET_TRANSFER = "پرداخت صورت حساب از طریق کیف پول",
|
||||||
TRANSACTION_NOT_FOUND = "تراکنش پیدا نشد",
|
TRANSACTION_NOT_FOUND = "تراکنش پیدا نشد",
|
||||||
|
REFERRAL_REWARD_WALLET_TRANSFER = "پرداخت پاداش ارجاع از طریق کیف پول",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum PaymentMessage {
|
export const enum PaymentMessage {
|
||||||
@@ -689,3 +690,13 @@ export const enum SliderMessage {
|
|||||||
IS_ACTIVE_SHOULD_BE_A_BOOLEAN = "وضعیت فعال بودن اسلایدر باید یک بولین باشد",
|
IS_ACTIVE_SHOULD_BE_A_BOOLEAN = "وضعیت فعال بودن اسلایدر باید یک بولین باشد",
|
||||||
SLIDER_NOT_FOUND = "اسلایدر مورد نظر یافت نشد",
|
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 = "کاربر قبلاً ارجاع داده شده است",
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions {
|
|||||||
username: configService.getOrThrow<string>("DB_USER"),
|
username: configService.getOrThrow<string>("DB_USER"),
|
||||||
password: configService.getOrThrow<string>("DB_PASS"),
|
password: configService.getOrThrow<string>("DB_PASS"),
|
||||||
autoLoadEntities: true,
|
autoLoadEntities: true,
|
||||||
synchronize: true,
|
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||||
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||||
migrationsTableName: "typeorm_migrations",
|
migrationsTableName: "typeorm_migrations",
|
||||||
migrationsRun: false,
|
migrationsRun: false,
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ export class BlogsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get all active categories (user side)" })
|
@ApiOperation({ summary: "Get all active categories (user side)" })
|
||||||
|
@SkipAuth()
|
||||||
@Get("categories/public")
|
@Get("categories/public")
|
||||||
getCategoriesUserSide() {
|
getCategoriesUserSide() {
|
||||||
return this.blogsService.getCategoriesUserSide();
|
return this.blogsService.getCategoriesUserSide();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class Discount extends BaseEntity {
|
|||||||
@Column({ type: "enum", enum: DiscountCalculationType })
|
@Column({ type: "enum", enum: DiscountCalculationType })
|
||||||
calculationType: 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;
|
amount: Decimal;
|
||||||
|
|
||||||
@Column({ type: "boolean", default: true })
|
@Column({ type: "boolean", default: true })
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { IsNotEmpty, IsString } from "class-validator";
|
||||||
|
|
||||||
|
export class UseReferralCodeDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
referralCode: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
@@ -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[];
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum ReferralRewardStatus {
|
||||||
|
PENDING = "PENDING",
|
||||||
|
PAID = "PAID",
|
||||||
|
CANCELLED = "CANCELLED",
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum ReferralStatus {
|
||||||
|
PENDING = "PENDING",
|
||||||
|
COMPLETED = "COMPLETED",
|
||||||
|
CANCELLED = "CANCELLED",
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -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 {}
|
||||||
@@ -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<ReferralCode> {
|
||||||
|
constructor(@InjectRepository(ReferralCode) repository: Repository<ReferralCode>) {
|
||||||
|
super(repository.target, repository.manager, repository.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ReferralReward> {
|
||||||
|
constructor(@InjectRepository(ReferralReward) repository: Repository<ReferralReward>) {
|
||||||
|
super(repository.target, repository.manager, repository.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Referral> {
|
||||||
|
constructor(@InjectRepository(Referral) repository: Repository<Referral>) {
|
||||||
|
super(repository.target, repository.manager, repository.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ import { Invoice } from "../../invoices/entities/invoice.entity";
|
|||||||
import { LearningProgress } from "../../learnings/entities/learning-progress.entity";
|
import { LearningProgress } from "../../learnings/entities/learning-progress.entity";
|
||||||
import { Notification } from "../../notifications/entities/notification.entity";
|
import { Notification } from "../../notifications/entities/notification.entity";
|
||||||
import { Payment } from "../../payments/entities/payment.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 { UserSetting } from "../../settings/entities/user-setting.entity";
|
||||||
import { UserQuickAccess } from "../../subscriptions/entities/user-quick-access.entity";
|
import { UserQuickAccess } from "../../subscriptions/entities/user-quick-access.entity";
|
||||||
import { UserSubscription } from "../../subscriptions/entities/user-subscription.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 })
|
@OneToMany(() => RefreshToken, (refreshToken) => refreshToken.user, { cascade: true })
|
||||||
refreshTokens: RefreshToken[];
|
refreshTokens: RefreshToken[];
|
||||||
}
|
|
||||||
|
|
||||||
// @ManyToMany(() => DanakService, (danakService) => danakService.users)
|
@OneToOne(() => ReferralCode, (code) => code.user)
|
||||||
// @JoinTable()
|
referralCode: ReferralCode;
|
||||||
// danakServices: DanakService[];
|
|
||||||
|
@OneToMany(() => Referral, (referral) => referral.referrer)
|
||||||
|
referrals: Referral[];
|
||||||
|
|
||||||
|
@OneToMany(() => Referral, (referral) => referral.referredUser)
|
||||||
|
referredBy: Referral[];
|
||||||
|
|
||||||
|
@OneToMany(() => ReferralReward, (reward) => reward.user)
|
||||||
|
referralRewards: ReferralReward[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { AddressService } from "../../address/providers/address.service";
|
|||||||
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
|
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
|
||||||
import { RequestOtpDto } from "../../auth/DTO/request-otp.dto";
|
import { RequestOtpDto } from "../../auth/DTO/request-otp.dto";
|
||||||
import { VerifyOtpDto } from "../../auth/DTO/verify-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 { UserSettingsService } from "../../settings/providers/user-settings.service";
|
||||||
import { CacheService } from "../../utils/providers/cache.service";
|
import { CacheService } from "../../utils/providers/cache.service";
|
||||||
import { EmailService } from "../../utils/providers/email.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 { RealUserRepository } from "../repositories/real-user.repository";
|
||||||
import { UserGroupRepository } from "../repositories/user-group.repository";
|
import { UserGroupRepository } from "../repositories/user-group.repository";
|
||||||
import { UserRepository } from "../repositories/users.repository";
|
import { UserRepository } from "../repositories/users.repository";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
private readonly logger = new Logger(UsersService.name);
|
private readonly logger = new Logger(UsersService.name);
|
||||||
@@ -47,6 +47,7 @@ export class UsersService {
|
|||||||
private readonly otpService: OTPService,
|
private readonly otpService: OTPService,
|
||||||
private readonly smsService: SmsService,
|
private readonly smsService: SmsService,
|
||||||
private readonly cacheService: CacheService,
|
private readonly cacheService: CacheService,
|
||||||
|
private readonly referralsService: ReferralsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/************************************************************ */
|
/************************************************************ */
|
||||||
@@ -254,8 +255,8 @@ export class UsersService {
|
|||||||
await queryRunner.manager.save(user);
|
await queryRunner.manager.save(user);
|
||||||
|
|
||||||
await this.userSettingsService.createUserSettings(user.id, queryRunner);
|
await this.userSettingsService.createUserSettings(user.id, queryRunner);
|
||||||
|
|
||||||
await this.walletsService.createUserWallet(user.id, queryRunner);
|
await this.walletsService.createUserWallet(user.id, queryRunner);
|
||||||
|
await this.referralsService.generateReferralCode(user.id, queryRunner);
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { UtilsModule } from "../utils/utils.module";
|
|||||||
import { AdminsService } from "./providers/admins.service";
|
import { AdminsService } from "./providers/admins.service";
|
||||||
import { CustomersService } from "./providers/customers.service";
|
import { CustomersService } from "./providers/customers.service";
|
||||||
import { RefreshTokensRepository } from "./repositories/refresh-token.repository";
|
import { RefreshTokensRepository } from "./repositories/refresh-token.repository";
|
||||||
|
import { ReferralsModule } from "../referrals/referrals.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -33,6 +34,7 @@ import { RefreshTokensRepository } from "./repositories/refresh-token.repository
|
|||||||
WalletsModule,
|
WalletsModule,
|
||||||
UtilsModule,
|
UtilsModule,
|
||||||
AddressModule,
|
AddressModule,
|
||||||
|
ReferralsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
UsersService,
|
UsersService,
|
||||||
|
|||||||
@@ -111,6 +111,27 @@ export class WalletsService {
|
|||||||
|
|
||||||
return transaction;
|
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) {
|
async getTransaction(userId: string, paginationDto: PaginationDto) {
|
||||||
|
|||||||
Reference in New Issue
Block a user